Compare commits

...
Author SHA1 Message Date
Xinyu Sui 6717ddd809 Revert the min version, and add a check around deploysteps 2022-07-06 15:54:35 -07:00
Xinyu Sui 56b5728512 Merge branch 'main' into dev/xisui/deploy 2022-07-06 12:28:39 -07:00
Colen Garoutte-Carson 996453b3a8 Fix issue in which duplicate compiler args might be used in compiler query (#9532) 2022-07-01 11:44:44 -07:00
Colen Garoutte-Carson 0eb68d22a0 Update changelog to reflect reverted change (#9523) 2022-06-29 12:39:40 -07:00
Colen Garoutte-Carson a2a8d77445 Revert "Reduce default C++ inlay hint font size 12 to match VS and potentially reduce confusion (#9480)" (#9522)
This reverts commit a2683630e0.
2022-06-29 12:35:05 -07:00
Sean McManus 032d9a288e Update changelog for 1.11.1 (#9517) 2022-06-28 21:19:40 -07:00
Sean McManus 59ea146b6e Change C_Cpp.debugShortcut to resource scope (#9514)
Make C_Cpp.debugShortcut resource scope.
2022-06-28 20:57:31 -07:00
Sean McManus 09a1ce1e8a Update ThirdPartyNotices.txt (#9516)
* Update ThirdPartyNotices.txt
2022-06-28 19:18:23 -07:00
Colen Garoutte-Carson 58a9ec846f Fix issue preventing building with the latest nodejs (#9515) 2022-06-28 16:49:19 -07:00
Sean McManus 6f3ae65833 Add setting to hide leading underscores for inlay hints. (#9500)
* Add setting to hide leading underscores for inlay hints.
2022-06-28 16:25:03 -07:00
Sean McManus a8e6695b55 Add setting to show inlay hints on the left. (#9506) 2022-06-28 14:48:06 -07:00
Chuck Ries c7edc9981f Contribute breakpoints for the "rust" language (#9484)
* Contribute breakpoints for the "rust" language

Since rust code can be debugged with this extension, contribute breakpoints for the "rust" language such that the workaround of "allow breakpoints in all files" is no longer required.
2022-06-28 14:12:20 -07:00
Sean McManus 4d0912eb9d Update yarn.lock. (#9513) 2022-06-28 12:15:52 -07:00
Andrew Wang 7747ac6226 Update VSCode Telemetry Package to v0.6.2 (#9512)
* Update VSCode Telemetry Package to v0.6.2
2022-06-28 11:34:09 -07:00
Elaheh Rashedi 13155149ea rename "success" to "succeeded" in telemetry (#9509) 2022-06-28 10:56:55 -07:00
Sean McManus d3fffbad7b Add setting for & space in inlay hints. (#9502)
* Add setting for & space in inlay hints.
2022-06-24 12:06:36 -07:00
Sean McManus 6fe98d2f54 Add left padding to "auto" inlay hints with no spaces on the left (#9499)
* Add left padding to inlay hint.
2022-06-24 11:13:33 -07:00
Sean McManus a2683630e0 Reduce default C++ inlay hint font size 12 to match VS and potentially reduce confusion (#9480)
* Reduce default inlay hint font size to 12 match VS and reduce confusion.
2022-06-23 17:33:37 -07:00
Sean McManus 590923ca99 Fix remove diagnostics for the disable case. (#9495) 2022-06-23 13:42:44 -07:00
Xinyu Sui d252813b70 SSH local forward 2022-06-22 13:41:25 -07:00
Xinyu Sui b8364875ff Allow string as port to support var replacement 2022-06-22 11:06:12 -07:00
Xinyu Sui c035c5d5af Fix typo 2022-06-21 10:39:21 -07:00
Xinyu Sui 47cbaf0772 Merge branch 'main' into dev/xisui/deploy 2022-06-21 10:37:21 -07:00
Xinyu Sui 1246e9100c Merge branch 'main' into dev/xisui/deploy 2022-06-17 14:58:49 -07:00
Xinyu Sui c8d465f9f6 Allow overwriting scp and ssh paths 2022-06-17 14:51:04 -07:00
Xinyu Sui a64d4dccdf Merge branch 'dev/xisui/deploy' of https://github.com/xisui-MSFT/vscode-cpptools into dev/xisui/deploy 2022-06-14 15:04:57 -07:00
Xinyu Sui f739e61a3e Bump min vs code version 2022-06-14 15:04:45 -07:00
Xinyu Sui 0f84114193 Merge branch 'main' into dev/xisui/deploy 2022-06-13 15:56:35 -07:00
Xinyu Sui 2c65b9292f Resolve comments 2022-06-13 15:55:41 -07:00
Xinyu Sui b94c1ad783 Fix preset task 2022-06-07 18:05:34 -07:00
Xinyu Sui 6c9d61babb Merge branch 'main' into dev/xisui/deploy 2022-06-07 17:57:39 -07:00
Xinyu Sui 1bdc8ea304 Typos and cleanup 2022-06-07 17:40:11 -07:00
Xinyu Sui f8c8736172 Fix lint errors 2022-06-07 14:48:32 -07:00
Xinyu Sui 974654d31c Add deploySteps and variables to cppdbg 2022-06-07 10:57:19 -07:00
22 changed files with 5928 additions and 2963 deletions
+1
View File
@@ -33,3 +33,4 @@ localized_string_ids.h
# ignore generate files. It will be generated when building
src/nativeStrings.ts
vscode*.d.ts
+19 -1
View File
@@ -1,6 +1,24 @@
# C/C++ for Visual Studio Code Change Log
## Version 1.11.0: June 20, 2022
## Version 1.11.1 (pre-release): June 29, 2022
### Enhancements
* Move "auto" inlay hints to the right by default and add `C_Cpp.inlayHints.autoDeclarationTypes.showOnLeft`. [#9478](https://github.com/microsoft/vscode-cpptools/issues/9478)
* Allow breakpoints for Rust debugging. [PR #9484](https://github.com/microsoft/vscode-cpptools/pull/9484)
* Remove `_` from the start of parameter inlay hints and add `C_Cpp.inlayHints.parameterNames.hideLeadingUnderscores`. [#9485](https://github.com/microsoft/vscode-cpptools/issues/9485)
* Add `C_Cpp.inlayHints.referenceOperator.showSpace` setting. [#9486](https://github.com/microsoft/vscode-cpptools/issues/9486)
* Make `C_Cpp.debugShortcut` settable per-workspace folder. [PR #9514](https://github.com/microsoft/vscode-cpptools/pull/9514)
### Bug Fixes
* Fix "unknown register name" IntelliSense error. [#4382](https://github.com/microsoft/vscode-cpptools/issues/4382)
* Fix the code analysis "disable" option not automatically clearing the disabled diagnostics. [#9364](https://github.com/microsoft/vscode-cpptools/issues/9364)
* Fix `-isystem` not being used for system headers with code analysis. [#9366](https://github.com/microsoft/vscode-cpptools/issues/9366)
* Fix compiler querying for EDG-based compilers. [#9410](https://github.com/microsoft/vscode-cpptools/issues/9410)
* Fix bug with the environment being incorrect when compiler querying. [#9472](https://github.com/microsoft/vscode-cpptools/issues/9472)
* Fix inlay hints not having padding on the left if there is no existing space. [#9479](https://github.com/microsoft/vscode-cpptools/issues/9479)
* Fix duplicate inlay hints appearing sometimes. [#9487](https://github.com/microsoft/vscode-cpptools/issues/9487)
* Fix process launching concurrency issues on Windows.
## Version 1.11.0 (pre-release): June 20, 2022
### New Features
* Add inlay hints for parameters and auto types. [#5845](https://github.com/microsoft/vscode-cpptools/issues/5845)
File diff suppressed because it is too large Load Diff
+906 -34
View File
File diff suppressed because it is too large Load Diff
+34 -2
View File
@@ -166,10 +166,13 @@
"c_cpp.configuration.inactiveRegionOpacity.markdownDescription": { "message": "Controls the opacity of inactive preprocessor blocks. Scales between `0.1` and `1.0`. This setting only applies when inactive region dimming is enabled.", "comment": [ "Markdown text between `` should not be translated or localized (they represent literal text) and the capitalization, spacing, and punctuation (including the ``) should not be altered." ] },
"c_cpp.configuration.inactiveRegionForegroundColor.description": "Controls the font coloring of inactive preprocessor blocks. Input is in the form a hexadecimal color code or a valid Theme Color. If not set, this defaults to the syntax coloring scheme of the editor. This setting only applies when inactive region dimming is enabled.",
"c_cpp.configuration.inactiveRegionBackgroundColor.description": "Controls the background coloring of inactive preprocessor blocks. Input is in the form a hexadecimal color code or a valid Theme Color. If not set, this defaults to transparent. This setting only applies when inactive region dimming is enabled.",
"c_cpp.configuration.inlayHints.autoDeclarationTypes.enabled.markdownDescription": { "message": "Display inlay hints for deduced type when `auto` is used in a declaration:\n```cpp \n\n auto /* int */ index = 0;\n```", "comment": [ "Markdown text between `` and the text inside ``` block is code and should not be localized." ] },
"c_cpp.configuration.inlayHints.autoDeclarationTypes.enabled.markdownDescription": { "message": "Display inlay hints for deduced type when `auto` is used in a declaration:\n```cpp \n\n auto index /* : int */ = 0;\n```", "comment": [ "Markdown text between `` and the text inside ``` block is code and should not be localized." ] },
"c_cpp.configuration.inlayHints.autoDeclarationTypes.showOnLeft.markdownDescription": { "message": "Display inlay hints for deduced type when `auto` is used in a declaration on the left of the identifier:\n```cpp \n\n auto /* int */ index = 0;\n```", "comment": [ "Markdown text between `` and the text inside ``` block is code and should not be localized." ] },
"c_cpp.configuration.inlayHints.parameterNames.enabled.markdownDescription": { "message": "Display inlay hints for parameter names:\n```cpp \n\n int a = getArea(/* width: */ x, /* height: */ y);\n```", "comment": [ "Markdown text between `` and the text inside ``` block is code and should not be localized." ] },
"c_cpp.configuration.inlayHints.parameterNames.hideLeadingUnderscores.markdownDescription": { "message": "Hide leading `_` in parameter name hints.", "comment": [ "Markdown text between `` is code and should not be localized." ] },
"c_cpp.configuration.inlayHints.parameterNames.suppressWhenArgumentContainsName.markdownDescription": { "message": "Suppress parameter name hints when the argument text or inline comment contains the parameter name:\n```cpp \n\n int a = getArea(width, /* height: */ y);\n```", "comment": [ "Markdown text between `` and the text inside ``` block is code and should not be localized." ] },
"c_cpp.configuration.inlayHints.referenceOperator.enabled.markdownDescription": { "message": "Display the inlay hint reference operator `&` for parameters passed by non-const reference:\n```cpp \n\n swap(/* & first: */ str1, /* & last: */ str2);\n```", "comment": [ "Markdown text between `` and the text inside ``` block is code and should not be localized." ] },
"c_cpp.configuration.inlayHints.referenceOperator.enabled.markdownDescription": { "message": "Display the inlay hint reference operator `&` for parameters passed by non-const reference:\n```cpp \n\n swap(/* &first: */ str1, /* &last: */ str2);\n```", "comment": [ "Markdown text between `` and the text inside ``` block is code and should not be localized." ] },
"c_cpp.configuration.inlayHints.referenceOperator.showSpace.markdownDescription": { "message": "Controls whether a space is shown after `&` for parameters passed by non-const reference:\n```cpp \n\n swap(/* & first: */ str1, /* & last: */ str2);\n```", "comment": [ "Markdown text between `` and the text inside ``` block is code and should not be localized." ] },
"c_cpp.configuration.loggingLevel.markdownDescription": { "message": "The verbosity of logging in the Output Panel. The order of levels from least verbose to most verbose is: `None` < `Error` < `Warning` < `Information` < `Debug`.", "comment": [ "Markdown text between `` should not be translated or localized (they represent literal text) and the capitalization, spacing, and punctuation (including the ``) should not be altered." ] },
"c_cpp.configuration.autoAddFileAssociations.markdownDescription": { "message": "Controls whether files are automatically added to `#files.associations#` when they are the target of a navigation operation from a C/C++ file.", "comment": [ "Markdown text between `` should not be translated or localized (they represent literal text) and the capitalization, spacing, and punctuation (including the ``) should not be altered." ] },
"c_cpp.configuration.workspaceParsingPriority.markdownDescription": { "message": "Controls whether parsing of the non-active workspace files uses sleeps to avoid using 100% CPU. The values `highest`/`high`/`medium`/`low` correspond to approximately 100/75/50/25% CPU usage.", "comment": [ "Markdown text between `` should not be translated or localized (they represent literal text) and the capitalization, spacing, and punctuation (including the ``) should not be altered." ] },
@@ -298,6 +301,35 @@
"c_cpp.debuggers.hardwareBreakpoints.description": "Explicit control of hardware breakpoint behavior for remote targets.",
"c_cpp.debuggers.hardwareBreakpoints.require.description": "If true, always use hardware breakpoints. Defaults to false.",
"c_cpp.debuggers.hardwareBreakpoints.limit.description": "Optional limit on the number of available hardware breakpoints to use. Only enforced when \"require\" is true and \"limit\" is greater than 0. Defaults to 0.",
"c_cpp.debuggers.variables.description": "Variables for recursive substitution in this launch configuration. Each variable may refer to others.",
"c_cpp.debuggers.variables.properties.description": "Variable for recursive substitution in this launch configuration. The value may refer to other variables.",
"c_cpp.debuggers.host.description": "Host information.",
"c_cpp.debuggers.host.user.description": "User logging into the host.",
"c_cpp.debuggers.host.hostName.description": "Host name.",
"c_cpp.debuggers.host.port.description": "SSH port on the host. Default is 22.",
"c_cpp.debuggers.host.jumpHost.description": "Connect to the target host by first making a connection to the jump hosts.",
"c_cpp.debuggers.host.localForward.description": "Forward connections to the given TCP port or Unix socket on the local (client) host to the given host and port, or Unix socket, on the remote side",
"c_cpp.debuggers.host.localForward.bindAddress.description": "Local address",
"c_cpp.debuggers.host.localForward.port.description": "Local port",
"c_cpp.debuggers.host.localForward.host.description": "Host name",
"c_cpp.debuggers.host.localForward.hostPort.description": "Host port",
"c_cpp.debuggers.host.localForward.localSocket.description": "Local socket",
"c_cpp.debuggers.host.localForward.remoteSocket.description": "Remote socket",
"c_cpp.debuggers.deploySteps.description": "Steps needed to deploy the application. Order matters.",
"c_cpp.debuggers.deploySteps.scp.description": "Copy files using SCP.",
"c_cpp.debuggers.deploySteps.scp.files.description": "Files to be copied via SCP. Supports path pattern.",
"c_cpp.debuggers.deploySteps.scp.targetDir.description": "Target directory.",
"c_cpp.debuggers.deploySteps.scp.scpPath.description": "Optional full path to SCP. Assumes SCP is on PATH if not specified",
"c_cpp.debuggers.deploySteps.debug": "If true, skip when starting without debugging. If false, skip when starting debugging. If undefined, never skip.",
"c_cpp.debuggers.deploySteps.ssh.description": "SSH command step.",
"c_cpp.debuggers.deploySteps.ssh.command.description": "Command to be executed via SSH. The command after '-c' in SSH command.",
"c_cpp.debuggers.deploySteps.ssh.sshPath.description": "Optional full path to SSH. Assumes SSH is on PATH if not specified",
"c_cpp.debuggers.deploySteps.continueOn.description": "An optional finish pattern in output. When this pattern is seen in the output, continue the deploy procedures regardless of whether this step returns.",
"c_cpp.debuggers.deploySteps.shell.description": "Shell command step.",
"c_cpp.debuggers.deploySteps.shell.command.description": "Shell command to be executed.",
"c_cpp.debuggers.vsCodeCommand.description": "VS Code command to be invoked. Can be a command in VS Code or an active extension.",
"c_cpp.debuggers.vsCodeCommand.command.description": "VS Code command to be invoked.",
"c_cpp.debuggers.vsCodeCommand.args.description": "Arguments to the VS Code command.",
"c_cpp.taskDefinitions.name.description": "The name of the task.",
"c_cpp.taskDefinitions.command.description": "The path to either a compiler or script that performs compilation.",
"c_cpp.taskDefinitions.args.description": "Additional arguments to pass to the compiler or compilation script.",
+157 -18
View File
@@ -23,10 +23,24 @@ import { PlatformInformation } from '../platform';
import { Environment, ParsedEnvironmentFile } from './ParsedEnvironmentFile';
import { CppSettings, OtherSettings } from '../LanguageServer/settings';
import { configPrefix } from '../LanguageServer/extension';
import { expandAllStrings, ExpansionOptions, ExpansionVars } from '../expand';
import { scp, ssh } from '../SSH/commands';
import * as glob from 'glob';
import { promisify } from 'util';
nls.config({ messageFormat: nls.MessageFormat.bundle, bundleFormat: nls.BundleFormat.standalone })();
const localize: nls.LocalizeFunc = nls.loadMessageBundle();
enum StepType {
scp = 'scp',
ssh = 'ssh',
shell = 'shell',
remoteShell = 'remoteShell',
command = 'command'
}
const globAsync: (pattern: string, options?: glob.IOptions | undefined) => Promise<string[]> = promisify(glob);
/*
* Retrieves configurations from a provider and displays them in a quickpick menu to be selected.
* Ensures that the selected configuration's preLaunchTask (if existent) is populated in the user's task.json.
@@ -95,7 +109,7 @@ export class DebugConfigurationProvider implements vscode.DebugConfigurationProv
const selection: ConfigMenu | undefined = await vscode.window.showQuickPick(this.localizeConfigDetail(items), {placeHolder: localize("select.configuration", "Select a configuration")});
if (!selection) {
Telemetry.logDebuggerEvent(DebuggerEvent.debugPanel, { "debugType": "debug", "configSource": ConfigSource.unknown, "configMode": ConfigMode.unknown, "cancelled": "true", "success": "true" });
Telemetry.logDebuggerEvent(DebuggerEvent.debugPanel, { "debugType": "debug", "configSource": ConfigSource.unknown, "configMode": ConfigMode.unknown, "cancelled": "true", "succeeded": "true" });
return []; // User canceled it.
}
@@ -119,7 +133,7 @@ export class DebugConfigurationProvider implements vscode.DebugConfigurationProv
// resolveDebugConfiguration will be automatically called after calling provideDebugConfigurations.
const configs: CppDebugConfiguration[]= await this.provideDebugConfigurations(folder);
if (!configs || configs.length === 0) {
Telemetry.logDebuggerEvent(DebuggerEvent.debugPanel, { "debugType": DebugType.debug, "configSource": folder ? ConfigSource.workspaceFolder : ConfigSource.singleFile, "configMode": ConfigMode.noLaunchConfig, "cancelled": "true", "success": "true" });
Telemetry.logDebuggerEvent(DebuggerEvent.debugPanel, { "debugType": DebugType.debug, "configSource": folder ? ConfigSource.workspaceFolder : ConfigSource.singleFile, "configMode": ConfigMode.noLaunchConfig, "cancelled": "true", "succeeded": "true" });
return undefined; // aborts debugging silently
} else {
// Currently, we expect only one debug config to be selected.
@@ -153,9 +167,9 @@ export class DebugConfigurationProvider implements vscode.DebugConfigurationProv
try {
await cppBuildTaskProvider.runBuildTask(config.preLaunchTask);
config.preLaunchTask = undefined;
Telemetry.logDebuggerEvent(DebuggerEvent.debugPanel, { "debugType": DebugType.debug, "configSource": config.configSource || ConfigSource.unknown, "configMode": ConfigMode.launchConfig, "cancelled": "false", "success": "true" });
Telemetry.logDebuggerEvent(DebuggerEvent.debugPanel, { "debugType": DebugType.debug, "configSource": config.configSource || ConfigSource.unknown, "configMode": ConfigMode.launchConfig, "cancelled": "false", "succeeded": "true" });
} catch (err) {
Telemetry.logDebuggerEvent(DebuggerEvent.debugPanel, { "debugType": DebugType.debug, "configSource": config.configSource || ConfigSource.unknown, "configMode": ConfigMode.launchConfig, "cancelled": "false", "success": "false" });
Telemetry.logDebuggerEvent(DebuggerEvent.debugPanel, { "debugType": DebugType.debug, "configSource": config.configSource || ConfigSource.unknown, "configMode": ConfigMode.launchConfig, "cancelled": "false", "succeeded": "false" });
}
return config;
}
@@ -180,7 +194,7 @@ export class DebugConfigurationProvider implements vscode.DebugConfigurationProv
config.debugType = config.debugType ? config.debugType : DebugType.debug;
const configMode: ConfigMode = isExistingConfig ? ConfigMode.launchConfig : ConfigMode.noLaunchConfig;
// if configuration.debuggerEvent === undefined, it means this configuration is already defined in launch.json and is shown in debugPanel.
Telemetry.logDebuggerEvent(config.debuggerEvent || DebuggerEvent.debugPanel, { "debugType": config.debugType || DebugType.debug, "configSource": config.configSource || ConfigSource.unknown, "configMode": configMode, "cancelled": "false", "success": "true" });
Telemetry.logDebuggerEvent(config.debuggerEvent || DebuggerEvent.debugPanel, { "debugType": config.debugType || DebugType.debug, "configSource": config.configSource || ConfigSource.unknown, "configMode": configMode, "cancelled": "false", "succeeded": "true" });
if (!resolveByVsCode) {
if ((singleFile || (isDebugPanel && !folder && isExistingTask))) {
@@ -203,11 +217,11 @@ export class DebugConfigurationProvider implements vscode.DebugConfigurationProv
* This hook is directly called after 'resolveDebugConfiguration' but with all variables substituted.
* This is also ran after the tasks.json has completed.
*
* Try to add all missing attributes to the debug configuration being launched.
* Try to add all missing attributes to the debug configuration being launched.
* If return "undefined", the debugging will be aborted silently.
* If return "null", the debugging will be aborted and launch.json will be opened.
*/
resolveDebugConfigurationWithSubstitutedVariables(folder: vscode.WorkspaceFolder | undefined, config: CppDebugConfiguration, token?: vscode.CancellationToken): vscode.ProviderResult<CppDebugConfiguration> {
*/
async resolveDebugConfigurationWithSubstitutedVariables(folder: vscode.WorkspaceFolder | undefined, config: CppDebugConfiguration, token?: vscode.CancellationToken): Promise<CppDebugConfiguration | null | undefined> {
if (!config || !config.type) {
return undefined; // Abort debugging silently.
}
@@ -230,7 +244,7 @@ export class DebugConfigurationProvider implements vscode.DebugConfigurationProv
// Disable debug heap by default, enable if 'enableDebugHeap' is set.
if (!config.enableDebugHeap) {
const disableDebugHeapEnvSetting: Environment = {"name" : "_NO_DEBUG_HEAP", "value" : "1"};
const disableDebugHeapEnvSetting: Environment = { "name": "_NO_DEBUG_HEAP", "value": "1" };
if (config.environment && util.isArray(config.environment)) {
config.environment.push(disableDebugHeapEnvSetting);
@@ -243,6 +257,8 @@ export class DebugConfigurationProvider implements vscode.DebugConfigurationProv
// Add environment variables from .env file
this.resolveEnvFile(config, folder);
await this.expand(config, folder);
this.resolveSourceFileMapVariables(config);
// Modify WSL config for OpenDebugAD7
@@ -305,6 +321,19 @@ export class DebugConfigurationProvider implements vscode.DebugConfigurationProv
// logger.showOutputChannel();
}
// Run deploy steps
if (config.deploySteps && config.deploySteps.length !== 0) {
const codeVersion: number[] = vscode.version.split('.').map(num => parseInt(num, undefined));
if ((util.isNumber(codeVersion[0]) && codeVersion[0] < 1) || (util.isNumber(codeVersion[0]) && codeVersion[0] === 1 && util.isNumber(codeVersion[1]) && codeVersion[1] < 69)) {
logger.getOutputChannelLogger().showErrorMessage(localize("vs.code.1.69+.required", "'deploySteps' require VS Code 1.69+."));
return undefined;
}
const deploySucceeded: boolean = await this.deploySteps(config, token);
if (!deploySucceeded || token?.isCancellationRequested) {
return undefined;
}
}
return config;
}
@@ -557,17 +586,17 @@ export class DebugConfigurationProvider implements vscode.DebugConfigurationProv
const newSourceFileMapTarget: string = util.resolveVariables(sourceFileMapTarget, undefined);
if (sourceFileMapTarget !== newSourceFileMapTarget) {
// Add a space if source was changed, else just tab the target message.
message += (message ? ' ' : '\t');
message += (message ? ' ' : '\t');
message += localize("replacing.targetpath", "Replacing {0} '{1}' with '{2}'.", "targetPath", sourceFileMapTarget, newSourceFileMapTarget);
target = newSourceFileMapTarget;
}
} else if (util.isObject(sourceFileMapTarget)) {
const newSourceFileMapTarget: {"editorPath": string; "useForBreakpoints": boolean } = sourceFileMapTarget;
const newSourceFileMapTarget: { "editorPath": string; "useForBreakpoints": boolean } = sourceFileMapTarget;
newSourceFileMapTarget["editorPath"] = util.resolveVariables(sourceFileMapTarget["editorPath"], undefined);
if (sourceFileMapTarget !== newSourceFileMapTarget) {
// Add a space if source was changed, else just tab the target message.
message += (message ? ' ' : '\t');
message += (message ? ' ' : '\t');
message += localize("replacing.editorPath", "Replacing {0} '{1}' with '{2}'.", "editorPath", sourceFileMapTarget, newSourceFileMapTarget["editorPath"]);
target = newSourceFileMapTarget;
}
@@ -763,7 +792,7 @@ export class DebugConfigurationProvider implements vscode.DebugConfigurationProv
}
const selectedConfig: vscode.DebugConfiguration | undefined = await this.selectConfiguration(textEditor, false, true);
if (!selectedConfig) {
Telemetry.logDebuggerEvent(DebuggerEvent.addConfigGear, { "configSource": ConfigSource.workspaceFolder, "configMode": ConfigMode.launchConfig, "cancelled": "true", "success": "true" });
Telemetry.logDebuggerEvent(DebuggerEvent.addConfigGear, { "configSource": ConfigSource.workspaceFolder, "configMode": ConfigMode.launchConfig, "cancelled": "true", "succeeded": "true" });
return; // User canceled it.
}
@@ -780,7 +809,7 @@ export class DebugConfigurationProvider implements vscode.DebugConfigurationProv
selectedConfig.debuggerEvent = undefined;
// Write debug configuration in launch.json file.
await this.writeDebugConfig(selectedConfig, isExistingConfig, folder);
Telemetry.logDebuggerEvent(DebuggerEvent.addConfigGear, { "configSource": ConfigSource.workspaceFolder, "configMode": ConfigMode.launchConfig, "cancelled": "false", "success": "true" });
Telemetry.logDebuggerEvent(DebuggerEvent.addConfigGear, { "configSource": ConfigSource.workspaceFolder, "configMode": ConfigMode.launchConfig, "cancelled": "false", "succeeded": "true" });
}
public async buildAndRun(textEditor: vscode.TextEditor): Promise<void> {
@@ -792,7 +821,7 @@ export class DebugConfigurationProvider implements vscode.DebugConfigurationProv
let folder: vscode.WorkspaceFolder | undefined = vscode.workspace.getWorkspaceFolder(textEditor.document.uri);
const selectedConfig: CppDebugConfiguration | undefined = await this.selectConfiguration(textEditor);
if (!selectedConfig) {
Telemetry.logDebuggerEvent(DebuggerEvent.playButton, { "debugType": debugModeOn ? DebugType.debug : DebugType.run, "configSource": ConfigSource.unknown, "cancelled": "true", "success": "true" });
Telemetry.logDebuggerEvent(DebuggerEvent.playButton, { "debugType": debugModeOn ? DebugType.debug : DebugType.run, "configSource": ConfigSource.unknown, "cancelled": "true", "succeeded": "true" });
return; // User canceled it.
}
@@ -806,7 +835,7 @@ export class DebugConfigurationProvider implements vscode.DebugConfigurationProv
}
selectedConfig.debugType = debugModeOn ? DebugType.debug : DebugType.run;
// startDebugging will trigger a call to resolveDebugConfiguration.
await vscode.debug.startDebugging(folder, selectedConfig, {noDebug: !debugModeOn});
await vscode.debug.startDebugging(folder, selectedConfig, { noDebug: !debugModeOn });
}
private async selectConfiguration(textEditor: vscode.TextEditor, pickDefault: boolean = true, onlyWorkspaceFolder: boolean = false): Promise<CppDebugConfiguration | undefined> {
@@ -870,10 +899,120 @@ export class DebugConfigurationProvider implements vscode.DebugConfigurationProv
if (e && e.message === util.failedToParseJson) {
vscode.window.showErrorMessage(util.failedToParseJson);
}
Telemetry.logDebuggerEvent(config.debuggerEvent || DebuggerEvent.debugPanel, { "debugType": config.debugType || DebugType.debug, "configSource": config.configSource || ConfigSource.unknown, "configMode": configMode, "cancelled": "false", "success": "false" });
Telemetry.logDebuggerEvent(config.debuggerEvent || DebuggerEvent.debugPanel, { "debugType": config.debugType || DebugType.debug, "configSource": config.configSource || ConfigSource.unknown, "configMode": configMode, "cancelled": "false", "succeeded": "false" });
}
}
}
private async expand(config: vscode.DebugConfiguration, folder: vscode.WorkspaceFolder | undefined): Promise<void> {
const folderPath: string | undefined = folder?.uri.fsPath || vscode.workspace.workspaceFolders?.[0].uri.fsPath;
const vars: ExpansionVars = config.variables ? config.variables : {};
vars.workspaceFolder = folderPath || '{workspaceFolder}';
vars.workspaceFolderBasename = folderPath ? path.basename(folderPath) : '{workspaceFolderBasename}';
const expansionOptions: ExpansionOptions = { vars, recursive: true };
return expandAllStrings(config, expansionOptions);
}
// Returns true when ALL steps succeed; stop all subsequent steps if one fails
private async deploySteps(config: vscode.DebugConfiguration, cancellationToken?: vscode.CancellationToken): Promise<boolean> {
let succeeded: boolean = true;
const deployStart: number = new Date().getTime();
for (const step of config.deploySteps) {
succeeded = await this.singleDeployStep(config, step, cancellationToken);
if (!succeeded) {
break;
}
}
const deployEnd: number = new Date().getTime();
const telemetryProperties: { [key: string]: string } = {
Succeeded: `${succeeded}`,
IsDebugging: `${!config.noDebug || false}`
};
const telemetryMetrics: { [key: string]: number } = {
NumSteps: config.deploySteps.length,
Duration: deployEnd - deployStart
};
Telemetry.logDebuggerEvent('deploy', telemetryProperties, telemetryMetrics);
return succeeded;
}
private async singleDeployStep(config: vscode.DebugConfiguration, step: any, cancellationToken?: vscode.CancellationToken): Promise<boolean> {
if ((config.noDebug && step.debug === true) || (!config.noDebug && step.debug === false)) {
// Skip steps that doesn't match current launch mode. Explicit true/false check, since a step is always run when debug is undefined.
return true;
}
switch (step.type) {
case StepType.command: {
// VS Code commands are the same regardless of which extension invokes them, so just invoke them here.
if (step.args && !Array.isArray(step.args)) {
logger.getOutputChannelLogger().showErrorMessage(localize('command.args.must.be.array', '"args" in command deploy step must be an array.'));
return false;
}
const returnCode: unknown = await vscode.commands.executeCommand(step.command, ...step.args);
return !returnCode;
}
case StepType.scp: {
if (!step.files || !step.targetDir || !step.host) {
logger.getOutputChannelLogger().showErrorMessage(localize('missing.properties.scp', '"host", "files", and "targetDir" are required in scp steps.'));
return false;
}
const host: util.ISshHostInfo = { hostName: step.host.hostName, user: step.host.user, port: step.host.port };
const jumpHosts: util.ISshHostInfo[] = step.host.jumpHosts;
let files: vscode.Uri[] = [];
if (util.isString(step.files)) {
files = files.concat((await globAsync(step.files)).map(file => vscode.Uri.file(file)));
} else if (util.isArrayOfString(step.files)) {
for (const fileGlob of (step.files as string[])) {
files = files.concat((await globAsync(fileGlob)).map(file => vscode.Uri.file(file)));
}
} else {
logger.getOutputChannelLogger().showErrorMessage(localize('incorrect.files.type.scp', '"files" must be a string or an array of strings in scp steps.'));
return false;
}
const scpResult: util.ProcessReturnType = await scp(files, host, step.targetDir, config.scpPath, jumpHosts, cancellationToken);
if (!scpResult.succeeded || cancellationToken?.isCancellationRequested) {
return false;
}
break;
}
case StepType.ssh: {
if (!step.host || !step.command) {
logger.getOutputChannelLogger().showErrorMessage(localize('missing.properties.ssh', '"host" and "command" are required for ssh steps.'));
return false;
}
const host: util.ISshHostInfo = { hostName: step.host.hostName, user: step.host.user, port: step.host.port };
const jumpHosts: util.ISshHostInfo[] = step.host.jumpHosts;
const localForwards: util.ISshLocalForwardInfo[] = step.host.localForwards;
const continueOn: string = step.continueOn;
const sshResult: util.ProcessReturnType = await ssh(host, step.command, config.sshPath, jumpHosts, localForwards, continueOn, cancellationToken);
if (!sshResult.succeeded || cancellationToken?.isCancellationRequested) {
return false;
}
break;
}
case StepType.shell: {
if (!step.command) {
logger.getOutputChannelLogger().showErrorMessage(localize('missing.properties.shell', '"command" is required for shell steps.'));
return false;
}
const taskResult: util.ProcessReturnType = await util.spawnChildProcess(step.command, undefined, step.continueOn);
if (!taskResult.succeeded || cancellationToken?.isCancellationRequested) {
logger.getOutputChannelLogger().showErrorMessage(taskResult.output);
return false;
}
break;
}
default: {
logger.getOutputChannelLogger().appendLine(localize('deploy.step.type.not.supported', 'Deploy step type {0} is not supported.', step.type));
return false;
}
}
return true;
}
}
export interface IConfigurationAssetProvider {
@@ -1026,7 +1165,7 @@ export class ConfigurationSnippetProvider implements vscode.CompletionItemProvid
items = [];
// Make a copy of each snippet since we are adding a comma to the end of the insertText.
this.snippets.forEach((item) => items.push({...item}));
this.snippets.forEach((item) => items.push({ ...item }));
items.map((item) => {
item.insertText = item.insertText + ','; // Add comma
@@ -22,6 +22,9 @@ interface CppInlayHint {
inlayHintKind: InlayHintKind;
isValueRef: boolean;
hasParamName: boolean;
leftPadding: boolean;
rightPadding: boolean;
identifierLength: number;
}
interface GetInlayHintsResult {
@@ -101,10 +104,22 @@ export class InlayHintsProvider implements vscode.InlayHintsProvider {
let paramHintLabel: string = "";
if (settings.inlayHintsParameterNames) {
paramHintLabel = (settings.inlayHintsParameterNamesSuppressName && hint.hasParamName) ? "" : hint.label;
if (paramHintLabel !== "" && settings.inlayHintsParameterNamesHideLeadingUnderscores) {
let nonUnderscoreIndex: number = 0;
for (let i: number = 0; i < paramHintLabel.length; ++i) {
if (paramHintLabel[i] !== '_') {
nonUnderscoreIndex = i;
break;
}
}
if (nonUnderscoreIndex > 0) {
paramHintLabel = paramHintLabel.substring(nonUnderscoreIndex);
}
}
}
let refOperatorString: string = "";
if (settings.inlayHintsReferenceOperator && hint.isValueRef) {
refOperatorString = (paramHintLabel.length > 0) ? "& " : "&";
refOperatorString = (paramHintLabel.length > 0 && settings.inlayHintsReferenceOperatorShowSpace) ? "& " : "&";
}
let label: string = "";
if (paramHintLabel.length > 0 || refOperatorString.length > 0) {
@@ -123,13 +138,17 @@ export class InlayHintsProvider implements vscode.InlayHintsProvider {
private createCacheEntry(inlayHintsResults: GetInlayHintsResult): InlayHintsCacheEntry {
const typeHints: vscode.InlayHint[] = [];
const settings: CppSettings = new CppSettings();
for (const h of inlayHintsResults.inlayHints) {
if (h.inlayHintKind === InlayHintKind.Type) {
const showOnLeft: boolean = settings.inlayHintsAutoDeclarationTypesShowOnLeft && h.identifierLength > 0;
const inlayHint: vscode.InlayHint = new vscode.InlayHint(
new vscode.Position(h.position.line, h.position.character),
h.label,
new vscode.Position(h.position.line, h.position.character +
(showOnLeft ? 0 : h.identifierLength)),
(showOnLeft ? h.label : ": " + h.label),
vscode.InlayHintKind.Type);
inlayHint.paddingRight = true;
inlayHint.paddingRight = showOnLeft || h.rightPadding;
inlayHint.paddingLeft = showOnLeft && h.leftPadding;
typeHints.push(inlayHint);
}
}
+2
View File
@@ -2793,6 +2793,7 @@ export class DefaultClient implements Client {
itemConfig.compilerPath = compilerPathAndArgs.compilerPath;
if (providerVersion < Version.v6) {
itemConfig.compilerArgsLegacy = compilerPathAndArgs.allCompilerArgs;
itemConfig.compilerArgs = undefined;
} else {
itemConfig.compilerArgs = compilerPathAndArgs.allCompilerArgs;
}
@@ -2884,6 +2885,7 @@ export class DefaultClient implements Client {
sanitized.compilerPath = compilerPathAndArgs.compilerPath;
if (providerVersion < Version.v6) {
sanitized.compilerArgsLegacy = compilerPathAndArgs.allCompilerArgs;
sanitized.compilerArgs = undefined;
} else {
sanitized.compilerArgs = compilerPathAndArgs.allCompilerArgs;
}
+1 -1
View File
@@ -278,9 +278,9 @@ export function publishCodeAnalysisDiagnostics(params: PublishCodeAnalysisDiagno
const relatedCodeActions: CodeActionDiagnosticInfo[] = [];
const rootAndRelatedWorkspaceEdits: CodeActionWorkspaceEdit[] = [];
const rootAndRelatedIdentifiersAndUris: CodeAnalysisDiagnosticIdentifiersAndUri[] = [];
rootAndRelatedIdentifiersAndUris.push(identifiersAndUri);
if (workspaceEdit.workspaceEdit !== undefined) {
rootAndRelatedWorkspaceEdits.push(workspaceEdit);
rootAndRelatedIdentifiersAndUris.push(identifiersAndUri);
}
if (d.relatedInformation) {
diagnostic.relatedInformation = [];
+12
View File
@@ -256,6 +256,10 @@ export class CppSettings extends Settings {
return super.Section.get<boolean>("inlayHints.autoDeclarationTypes.enabled") === true;
}
public get inlayHintsAutoDeclarationTypesShowOnLeft(): boolean {
return super.Section.get<boolean>("inlayHints.autoDeclarationTypes.showOnLeft") === true;
}
public get inlayHintsParameterNames(): boolean {
return super.Section.get<boolean>("inlayHints.parameterNames.enabled") === true;
}
@@ -264,10 +268,18 @@ export class CppSettings extends Settings {
return super.Section.get<boolean>("inlayHints.parameterNames.suppressWhenArgumentContainsName") === true;
}
public get inlayHintsParameterNamesHideLeadingUnderscores(): boolean {
return super.Section.get<boolean>("inlayHints.parameterNames.hideLeadingUnderscores") === true;
}
public get inlayHintsReferenceOperator(): boolean {
return super.Section.get<boolean>("inlayHints.referenceOperator.enabled") === true;
}
public get inlayHintsReferenceOperatorShowSpace(): boolean {
return super.Section.get<boolean>("inlayHints.referenceOperator.showSpace") === true;
}
public get enhancedColorization(): boolean {
return super.Section.get<string>("enhancedColorization") === "Enabled"
&& super.Section.get<string>("intelliSenseEngine") === "Default"
+340
View File
@@ -0,0 +1,340 @@
/* --------------------------------------------------------------------------------------------
* Copyright (c) Microsoft Corporation. All Rights Reserved.
* See 'LICENSE' in the project root for license information.
* ------------------------------------------------------------------------------------------ */
import * as vscode from 'vscode';
import { stripEscapeSequences, isWindows, escapeStringForRegex } from '../common';
export type IDifferingHostConfirmationProvider =
(message: string, cancelToken?: vscode.CancellationToken) => Promise<string | undefined>;
export type IFingerprintConfirmationProvider =
(host: string, fingerprint: string, cancelToken?: vscode.CancellationToken) => Promise<string | undefined>;
export interface IInteraction {
canceled?: boolean;
postAction?: 'consume' | 'keep';
response?: string;
isPassword?: boolean;
continue?: boolean; // Continue without waiting for the program to finish or pause
}
export interface IInteractorDataDetails {
detectedServerKey?: string;
detail?: string;
}
export interface IInteractor {
id: string;
onData(data: string, cancelToken?: vscode.CancellationToken, extraDetails?: IInteractorDataDetails): Promise<IInteraction>;
}
export class MitmInteractor implements IInteractor {
static ID = 'mitm';
constructor() { }
get id(): string {
return MitmInteractor.ID;
}
async onData(data: string): Promise<IInteraction> {
if (!!data.match('Port forwarding is disabled to avoid man-in-the-middle attacks.')) {
throw Error('Port forwarding is disabled to avoid man-in-the-middle attacks.');
}
return {};
}
}
export class FingerprintInteractor implements IInteractor {
static ID = 'fingerprint';
constructor(private readonly hostName: string, private readonly confirmationProvider: IFingerprintConfirmationProvider) { }
get id(): string {
return FingerprintInteractor.ID;
}
async onData(data: string, cancelToken?: vscode.CancellationToken, extraDetails?: IInteractorDataDetails): Promise<IInteraction> {
const fingerprintMatcher: RegExp = /fingerprint\sis\s(.+)\./;
const result: IInteraction = { postAction: 'keep' };
data = data.trim();
let fingerprintMatch: RegExpMatchArray | null;
if (
data.includes('Are you sure you want to continue connecting') &&
(fingerprintMatch = data.match(fingerprintMatcher))
) {
result.postAction = 'consume';
const confirmation: string | undefined = await this.confirmationProvider(
this.hostName,
fingerprintMatch[1],
cancelToken
);
if (confirmation) {
result.response = confirmation;
} else {
result.canceled = true;
}
} else if (
isWindows() &&
(data.includes('The authenticity of host ') || (data === '' && extraDetails?.detectedServerKey))
) {
// hack for #1195
// First line local server case (git ssh only gives the first line over ssh_askpass)
const key: string = extraDetails?.detectedServerKey || '(unknown)';
result.postAction = 'consume';
const confirmation: string | undefined = await this.confirmationProvider(this.hostName, key, cancelToken);
if (confirmation) {
result.response = confirmation;
} else {
result.canceled = true;
}
}
return result;
}
}
export class DifferingHostKeyInteractor implements IInteractor {
static ID = 'differing host key';
constructor(private readonly confirmationProvider: IDifferingHostConfirmationProvider) { }
get id(): string {
return DifferingHostKeyInteractor.ID;
}
async onData(data: string, cancelToken?: vscode.CancellationToken, _extraDetails?: IInteractorDataDetails): Promise<IInteraction> {
const result: IInteraction = { postAction: 'keep' };
data = data.trim();
if (
data.includes('Are you sure you want to continue connecting') &&
data.includes('Offending key for IP in') &&
data.includes('Matching host key in')
) {
result.postAction = 'consume';
const message: string = data.substring(data.indexOf('Warning'), data.indexOf('Are')).trim();
const confirmation: string | undefined = await this.confirmationProvider(message, cancelToken);
if (confirmation) {
result.response = confirmation;
} else {
result.canceled = true;
}
}
return result;
}
}
export type IStringProvider =
(key?: string, detail?: string, cancelToken?: vscode.CancellationToken) => Promise<string | undefined>;
export class PassphraseInteractor implements IInteractor {
static ID = 'passphrase';
constructor(private readonly passphraseProvider: IStringProvider) { }
get id(): string {
return PassphraseInteractor.ID;
}
async onData(data: string, cancelToken?: vscode.CancellationToken): Promise<IInteraction> {
const result: IInteraction = { postAction: 'keep' };
const lines: string[] = data.trim().split('\n');
if (lines.some(l => l.indexOf('Enter passphrase for') >= 0)) {
result.postAction = 'consume';
const passphrase: string | undefined = await this.passphraseProvider(undefined, undefined, cancelToken); // TODO keep track of the key name
if (typeof passphrase === 'string') {
result.response = passphrase;
result.isPassword = true;
} else {
result.canceled = true;
}
} else if (lines.some(l => l.indexOf('Identity added:') >= 0)) {
result.postAction = 'consume';
}
return result;
}
}
export function getExitCode(output: string, marker: string): number | undefined {
const regex: RegExp = new RegExp(`${marker}##([0-9]*)##`);
const match: RegExpExecArray | null = regex.exec(output);
if (match) {
try {
const num: number = parseInt(match[1]);
return Number.isNaN(num) ? undefined : num;
} catch (err) {
return undefined;
}
}
return 0;
}
/**
* Matches SSH password prompt of format:
* 's password:
* or
* Password:
* not
* 's old password:
* 's new password:
*/
function getPasswordPrompt(data: string, details?: IInteractorDataDetails): { user?: string; message?: string } | undefined {
if (data.includes('Password:')) {
// Password prompt for unspecified user
return { user: '' };
}
// Got \r\r\n as a line ending here
const match: RegExpMatchArray | null = stripEscapeSequences(data).match(/([a-zA-Z0-9\-_@\.]*)'s password:/);
if (match) {
return {
user: match[1],
message: details ? details.detail : undefined
};
}
return undefined;
}
export class PasswordInteractor implements IInteractor {
static ID = 'password';
constructor(private readonly passwordProvider: IStringProvider) { }
get id(): string {
return PasswordInteractor.ID;
}
async onData(data: string, cancelToken?: vscode.CancellationToken, extraDetails?: IInteractorDataDetails): Promise<IInteraction> {
const result: IInteraction = { postAction: 'keep' };
const pwPrompt: { user?: string; message?: string } | undefined = getPasswordPrompt(data, extraDetails);
if (pwPrompt && typeof pwPrompt.user === 'string') {
result.postAction = 'consume';
const password: string | undefined = await this.passwordProvider(pwPrompt.user, pwPrompt.message, cancelToken);
if (typeof password === 'string') {
result.response = password;
result.isPassword = true;
} else {
result.canceled = true;
}
}
return result;
}
}
export type IVerificationCodeProvider =
(msg: string, cancelToken?: vscode.CancellationToken) => Promise<string | undefined>;
export class TwoFacInteractor implements IInteractor {
static ID = '2fa';
constructor(private readonly verificationCodeProvider: IVerificationCodeProvider) { }
get id(): string {
return TwoFacInteractor.ID;
}
async onData(data: string, cancelToken?: vscode.CancellationToken): Promise<IInteraction> {
const result: IInteraction = { postAction: 'keep' };
if (data.includes('Verification code:')) {
result.postAction = 'consume';
const verificationCode: string | undefined = await this.verificationCodeProvider('Enter verification code', cancelToken);
if (typeof verificationCode === 'string') {
result.response = verificationCode;
result.isPassword = true;
} else {
result.canceled = true;
}
}
return result;
}
}
// https://github.com/microsoft/vscode-remote-release/issues/2170
export class DuoTwoFacInteractor implements IInteractor {
static ID = 'duo-2fa';
constructor(private readonly verificationCodeProvider: IVerificationCodeProvider) { }
get id(): string {
return DuoTwoFacInteractor.ID;
}
async onData(data: string, cancelToken?: vscode.CancellationToken): Promise<IInteraction> {
const result: IInteraction = { postAction: 'keep' };
if (data.includes('Passcode:')) {
result.postAction = 'consume';
const verificationCode: string | undefined = await this.verificationCodeProvider('Enter passcode', cancelToken);
if (typeof verificationCode === 'string') {
result.response = verificationCode;
result.isPassword = true;
} else {
result.canceled = true;
}
}
return result;
}
}
export class ContinueOnInteractor implements IInteractor {
static ID = 'continueOn';
constructor(private readonly continueOn: string) { }
get id(): string {
return ContinueOnInteractor.ID;
}
async onData(data: string, _cancelToken?: vscode.CancellationToken): Promise<IInteraction> {
const result: IInteraction = { postAction: 'keep' };
const pattern: string = escapeStringForRegex(this.continueOn);
const re: RegExp = new RegExp(pattern, 'g');
if (data.match(re)) {
result.continue = true;
}
return result;
}
}
export class ComposedInteractor implements IInteractor {
constructor(private readonly interactors: IInteractor[]) { }
get id(): string {
return 'composed';
}
async onData(data: string): Promise<IInteraction> {
for (const interactor of this.interactors) {
const result: IInteraction = await interactor.onData(data);
if (result.postAction === 'consume') {
return result;
}
}
return { postAction: 'keep' };
}
}
export interface ISystemInteractor {
createTerminal(options: vscode.TerminalOptions): vscode.Terminal;
onDidCloseTerminal: typeof vscode.window.onDidCloseTerminal;
onDidWriteTerminalData: typeof vscode.window.onDidWriteTerminalData;
}
export const defaultSystemInteractor: ISystemInteractor = {
createTerminal: vscode.window.createTerminal,
onDidCloseTerminal: vscode.window.onDidCloseTerminal,
onDidWriteTerminalData: vscode.window.onDidWriteTerminalData
};
+98
View File
@@ -0,0 +1,98 @@
/* --------------------------------------------------------------------------------------------
* Copyright (c) Microsoft Corporation. All Rights Reserved.
* See 'LICENSE' in the project root for license information.
* ------------------------------------------------------------------------------------------ */
import * as vscode from 'vscode';
import * as nls from 'vscode-nls';
import { ISshHostInfo, ISshLocalForwardInfo, ProcessReturnType } from '../common';
import { defaultSystemInteractor } from './commandInteractors';
import { runSshTerminalCommandWithLogin } from './sshCommandRunner';
nls.config({ messageFormat: nls.MessageFormat.bundle, bundleFormat: nls.BundleFormat.standalone })();
const localize: nls.LocalizeFunc = nls.loadMessageBundle();
export async function scp(files: vscode.Uri[], host: ISshHostInfo, targetDir: string, scpPath?: string, jumpHosts?: ISshHostInfo[], cancellationToken?: vscode.CancellationToken): Promise<ProcessReturnType> {
const args: string[] = [];
if (jumpHosts && jumpHosts.length > 0) {
args.push('-J', jumpHosts.map(getFullHostAddress).join(','));
}
if (host.port) {
// upper case P
args.push('-P', `${host.port}`);
}
args.push(files.map(uri => `"${uri.fsPath}"`).join(' '), `${getFullHostAddressNoPort(host)}:${targetDir}`);
return runSshTerminalCommandWithLogin(host, { systemInteractor: defaultSystemInteractor, nickname: 'scp', command: `"${scpPath || 'scp'}" ${args.join(' ')}`, token: cancellationToken });
}
export function ssh(host: ISshHostInfo, command: string, sshPath?: string, jumpHosts?: ISshHostInfo[], localForwards?: ISshLocalForwardInfo[], continueOn?: string, cancellationToken?: vscode.CancellationToken): Promise<ProcessReturnType> {
const args: string[] = [];
if (jumpHosts && jumpHosts.length > 0) {
args.push('-J', jumpHosts.map(getFullHostAddress).join(','));
}
if (host.port) {
// lower case p
args.push('-p', `${host.port}`);
}
if (localForwards) {
localForwards.forEach(info => args.push(...localForwardToArgs(info)));
}
args.push(getFullHostAddressNoPort(host), `"${command}"`);
return runSshTerminalCommandWithLogin(host, {
systemInteractor: defaultSystemInteractor,
command: `"${sshPath || 'ssh'}" ${args.join(' ')}`,
nickname: 'ssh',
continueOn,
token: cancellationToken
});
}
/**
* Takes one local forward info and convert it to '-L' args in ssh.
*/
function localForwardToArgs(localForward: ISshLocalForwardInfo): string[] {
// Do not combine error checking and arg conversion for clarity.
if (localForward.localSocket && (localForward.bindAddress || localForward.port)) {
throw Error(localize('local.forward.local.conflict', '"localSocket" cannot be specifed at the same time with "bindAddress" or "port" in localForwards'));
}
if (!localForward.localSocket && !localForward.port) {
throw Error(localize('local.forward.local.missing', '"port" or "localSocket" required in localForwards'));
}
if (localForward.remoteSocket && (localForward.host || localForward.hostPort)) {
throw Error(localize('local.forward.remote.conflict', '"remoteSocket" cannot be specifed at the same time with "host" or "hostPort" in localForwards'));
}
if (!localForward.remoteSocket && (!localForward.host || !localForward.hostPort)) {
throw Error(localize('local.forward.remote.missing', '"host" and "hostPort", or "remoteSocket" required in localForwards'));
}
let arg: string = '';
if (localForward.localSocket) {
arg += `${localForward.localSocket}:`;
}
if (localForward.bindAddress) {
arg += `${localForward.bindAddress}:`;
}
if (localForward.port) {
arg += `${localForward.port}:`;
}
if (localForward.remoteSocket) {
arg += `${localForward.remoteSocket}`;
}
if (localForward.host && localForward.hostPort) {
arg += `${localForward.host}:${localForward.hostPort}`;
}
return ['-L', arg];
}
/** user@host */
function getFullHostAddressNoPort(host: ISshHostInfo): string {
return host.user ? `${host.user}@${host.hostName}` : `${host.hostName}`;
}
function getFullHostAddress(host: ISshHostInfo): string {
const fullHostName: string = getFullHostAddressNoPort(host);
return host.port ? `${fullHostName}:${host.port}` : fullHostName;
}
+518
View File
@@ -0,0 +1,518 @@
/* --------------------------------------------------------------------------------------------
* Copyright (c) Microsoft Corporation. All Rights Reserved.
* See 'LICENSE' in the project root for license information.
* ------------------------------------------------------------------------------------------ */
import * as os from 'os';
import * as path from 'path';
import * as vscode from 'vscode';
import * as nls from 'vscode-nls';
import {
DifferingHostKeyInteractor,
DuoTwoFacInteractor,
FingerprintInteractor,
IInteractor,
MitmInteractor,
PassphraseInteractor,
PasswordInteractor,
TwoFacInteractor,
ContinueOnInteractor,
ISystemInteractor,
IInteraction
} from './commandInteractors';
import { isWindows, ISshHostInfo, splitLines, stripEscapeSequences, ProcessReturnType } from '../common';
import { getOutputChannelLogger } from '../logger';
nls.config({ messageFormat: nls.MessageFormat.bundle, bundleFormat: nls.BundleFormat.standalone })();
const localize: nls.LocalizeFunc = nls.loadMessageBundle();
export class CanceledError extends Error {
constructor() {
super(localize('ssh.canceled', 'SSH command canceled'));
}
}
export interface ICommandResult {
stdout: string;
stderr: string;
}
export function showPassphraseInputBox(
keyName?: string,
prompt?: string,
cancelToken?: vscode.CancellationToken
): Promise<string | undefined> {
const keyStr: string = keyName ? `"${keyName}"` : '';
const msg: string = localize('ssh.passphrase.input.box', 'Enter passphrase for ssh key {0}', keyStr);
return showInputBox(msg, prompt, cancelToken);
}
export function showPasswordInputBox(
user: string | undefined,
prompt?: string,
cancelToken?: vscode.CancellationToken
): Promise<string | undefined> {
const msg: string = user ? localize('ssh.enter.password.for.user', 'Enter password for user "{0}"', user) : localize('ssh_message_enterPassword', 'Enter password');
return showInputBox(msg, prompt, cancelToken);
}
export function showVerificationCodeInputBox(
msg: string,
cancelToken?: vscode.CancellationToken
): Promise<string | undefined> {
return showInputBox(msg, undefined, cancelToken);
}
export async function showInputBox(
msg: string,
prompt?: string,
cancelToken?: vscode.CancellationToken
): Promise<string | undefined> {
return new Promise((resolve, reject) => {
const quickPick: vscode.InputBox = vscode.window.createInputBox();
quickPick.title = msg;
quickPick.prompt = prompt;
quickPick.password = true;
quickPick.ignoreFocusOut = true;
let isAccepted: boolean = false;
quickPick.onDidAccept(() => {
isAccepted = true;
const passphrase: string = quickPick.value;
quickPick.dispose();
resolve(passphrase);
});
quickPick.onDidHide(() => {
if (!isAccepted) {
resolve(undefined);
}
});
quickPick.show();
if (cancelToken) {
cancelToken.onCancellationRequested(() => {
reject(new CanceledError());
quickPick.dispose();
});
}
});
}
class ConfirmationItem implements vscode.QuickPickItem, vscode.MessageItem {
title: string;
isCloseAffordance: boolean = true;
constructor(public label: string, public value: string) {
this.title = label;
}
}
const continueConfirmationPlaceholder: string = localize('ssh.continue.confirmation.placeholder', 'Are you sure you want to continue?');
export async function showHostKeyConfirmation(
host: string,
fingerprint: string,
cancelToken?: vscode.CancellationToken
): Promise<string | undefined> {
return showConfirmationPicker(
localize('ssh.host.key.confirmation.title', '"{0}" has fingerprint "{1}".', host, fingerprint),
continueConfirmationPlaceholder,
cancelToken
);
}
export async function showDifferingHostConfirmation(
message: string,
cancelToken?: vscode.CancellationToken
): Promise<string | undefined> {
return showConfirmationPicker(message, continueConfirmationPlaceholder, cancelToken);
}
async function showConfirmationPicker(
title: string,
placeholder: string,
cancelToken?: vscode.CancellationToken
): Promise<string | undefined> {
return new Promise((resolve, reject) => {
const quickPick: vscode.QuickPick<ConfirmationItem> = vscode.window.createQuickPick<ConfirmationItem>();
quickPick.canSelectMany = false;
quickPick.items = [new ConfirmationItem(localize('continue', 'Continue'), 'yes'), new ConfirmationItem(localize('cancel', 'Cancel'), 'no')];
quickPick.title = title;
quickPick.placeholder = placeholder;
let isAccepted: boolean = false;
quickPick.onDidAccept(async () => {
isAccepted = true;
const value: string = quickPick.selectedItems[0].value;
quickPick.dispose();
resolve(value);
});
quickPick.onDidHide(() => {
if (!isAccepted) {
resolve(undefined);
}
});
quickPick.show();
if (cancelToken) {
cancelToken.onCancellationRequested(() => {
quickPick.hide();
reject(new CanceledError());
});
}
});
}
export interface ITerminalCommandWithLoginArgs {
systemInteractor: ISystemInteractor;
command: string;
nickname: string;
marker?: string;
usedInteractors?: Set<string>;
interactor?: IInteractor;
cwd?: string;
token?: vscode.CancellationToken;
continueOn?: string;
revealTerminal?: vscode.Event<void>;
}
export async function runSshTerminalCommandWithLogin(
host: ISshHostInfo,
terminalArgs: ITerminalCommandWithLoginArgs,
showLoginTerminal = false
): Promise<ProcessReturnType> {
const interactors: IInteractor[] = [];
if (terminalArgs.interactor) {
interactors.push(terminalArgs.interactor);
}
if (!showLoginTerminal) {
interactors.push(
new MitmInteractor(),
new FingerprintInteractor(host.hostName, showHostKeyConfirmation),
new PassphraseInteractor(showPassphraseInputBox),
new DifferingHostKeyInteractor(showDifferingHostConfirmation),
new PasswordInteractor(showPasswordInputBox),
new TwoFacInteractor(showVerificationCodeInputBox),
new DuoTwoFacInteractor(showVerificationCodeInputBox)
);
}
if (terminalArgs.continueOn) {
interactors.push(new ContinueOnInteractor(terminalArgs.continueOn));
}
// This terminal is always local
const result: ProcessReturnType = await runInteractiveSshTerminalCommand({
systemInteractor: terminalArgs.systemInteractor,
command: terminalArgs.command,
interactors,
usedInteractors: terminalArgs.usedInteractors,
nickname: terminalArgs.nickname,
token: terminalArgs.token,
marker: terminalArgs.marker,
revealTerminal: terminalArgs.revealTerminal,
showLoginTerminal,
cwd: terminalArgs.cwd ? vscode.Uri.file(terminalArgs.cwd) : undefined
});
return result;
}
export interface ITerminalCommandArgs {
systemInteractor: ISystemInteractor;
command: string;
interactors?: IInteractor[];
nickname: string;
usedInteractors?: Set<string>;
sendText?: string;
cwd?: vscode.Uri;
terminalIsWindows?: boolean;
token?: vscode.CancellationToken;
marker?: string;
revealTerminal?: vscode.Event<void>;
showLoginTerminal?: boolean; // If true, respect the showLoginTerminal setting
}
export function getPauseLogMarker(uuid: string): string {
return `${uuid}: pauseLog`;
}
export function getResumeLogMarker(uuid: string): string {
return `${uuid}: resumeLog`;
}
export function runInteractiveSshTerminalCommand(args: ITerminalCommandArgs): Promise<ProcessReturnType> {
const disposables: vscode.Disposable[] = [];
const { systemInteractor, command, interactors, nickname, token } = args;
let logIsPaused: boolean = false;
return new Promise(async (resolve, reject) => {
let stdout: string = '';
let windowListener: vscode.Disposable | undefined;
let terminalListener: vscode.Disposable | undefined;
let terminal: vscode.Terminal | undefined;
let tokenListener: vscode.Disposable;
let continueWithoutExiting: boolean = false;
const clean = () => {
if (terminalListener) {
terminalListener.dispose();
terminalListener = undefined;
}
if (terminal) {
terminal.dispose();
terminal = undefined;
}
if (windowListener) {
windowListener.dispose();
windowListener = undefined;
}
if (tokenListener) {
tokenListener.dispose();
}
disposables.forEach(disposable => disposable.dispose());
};
const done = (cancel: boolean = false, noClean: boolean = false, exitCode?: number) => {
if (!noClean) {
clean();
}
getOutputChannelLogger().appendLine(cancel ? localize('ssh.terminal.command.canceled', '"{0}" terminal command canceled.', nickname) : localize('ssh.terminal.command.done', '"{0}" terminal command done.', nickname));
if (cancel) {
if (continueWithoutExiting) {
getOutputChannelLogger().showWarningMessage(localize('ssh.continuing.command.canceled', 'Task \'{0}\' is canceled, but the underlying command may not be terminated. Please check manually.', command));
}
return reject(new CanceledError());
}
// When using showLoginTerminal, stdout include the passphrase prompt, etc. Try to get just the command output on the last line.
const actualOutput: string | undefined = cancel ? '' : lastNonemptyLine(stdout);
resolve({ succeeded: !exitCode, exitCode, output: actualOutput || '' });
};
const failed = (error?: any) => {
clean();
getOutputChannelLogger().showErrorMessage(localize('ssh.process.failed', '"{0}" process failed: {1}', nickname, error));
reject(error);
};
const handleOutputLogging = (data: string): void => {
let nextPauseState: boolean | undefined;
if (args.marker) {
const pauseMarker: string = getPauseLogMarker(args.marker);
const pauseIdx: number = data.lastIndexOf(pauseMarker);
if (pauseIdx >= 0) {
data = data.substring(0, pauseIdx + pauseMarker.length);
nextPauseState = true;
}
const resumeIdx: number = data.lastIndexOf(getResumeLogMarker(args.marker));
if (resumeIdx >= 0) {
data = data.substring(resumeIdx);
nextPauseState = false;
}
}
// Log the chunk of data that includes the pause/resume markers,
// so unpause first and pause after logging
if (!logIsPaused) {
logReceivedData(data, nickname);
}
if (typeof nextPauseState === 'boolean') {
logIsPaused = nextPauseState;
}
};
const handleTerminalOutput = async (dataWrite: vscode.TerminalDataWriteEvent): Promise<void> => {
handleOutputLogging(dataWrite.data);
stdout += dataWrite.data;
if (interactors) {
for (const interactor of interactors) {
try {
const interaction: IInteraction = await interactor.onData(stdout);
if (interaction.postAction === 'consume') {
if (args.usedInteractors) {
args.usedInteractors.add(interactor.id);
}
stdout = '';
}
if (interaction.canceled) {
if (args.usedInteractors) {
args.usedInteractors.add(interactor.id);
}
done(true);
return;
}
if (interaction.continue) {
if (args.usedInteractors) {
args.usedInteractors.add(interactor.id);
}
continueWithoutExiting = true;
done(false, true);
return;
}
if (typeof interaction.response === 'string') {
if (args.usedInteractors) {
args.usedInteractors.add(interactor.id);
}
if (terminal) {
terminal.sendText(`${interaction.response}\n`);
const logOutput: string = interaction.isPassword
? interaction.response.replace(/./g, '*')
: interaction.response;
getOutputChannelLogger().appendLine(localize('ssh.wrote.data.to.terminal', '"{0}" wrote data to terminal: "{1}".', nickname, logOutput));
}
}
} catch (e) {
failed(e);
}
}
}
};
if (token) {
tokenListener = token.onCancellationRequested(() => {
done(true);
});
}
const terminalIsWindows: boolean = typeof args.terminalIsWindows === 'boolean' ? args.terminalIsWindows : isWindows();
try {
// the terminal process should not fail, but exit cleanly
let shellArgs: string | string[];
if (args.sendText) {
shellArgs = '';
} else {
shellArgs = terminalIsWindows ? `/c (${command})\nexit /b %ErrorLevel%` : ['-c', `${command}\nexit $?`];
}
const options: vscode.TerminalOptions = {
cwd:
args.cwd ||
(terminalIsWindows
? vscode.Uri.file(os.homedir() || 'c:\\')
: vscode.Uri.file(os.homedir() || '/')),
name: nickname,
shellPath: getShellPath(terminalIsWindows),
shellArgs,
hideFromUser: true
};
let terminalDataHandlingQueue: Promise<void> = Promise.resolve();
terminalListener = systemInteractor.onDidWriteTerminalData(async e => {
if (e.terminal !== terminal) {
return;
}
terminalDataHandlingQueue = terminalDataHandlingQueue.finally(() => handleTerminalOutput(e));
});
terminal = systemInteractor.createTerminal(options);
if (args.revealTerminal) {
disposables.push(
args.revealTerminal(() => {
if (terminal) {
terminal.show();
}
})
);
}
if (args.sendText) {
const sendText: string = terminalIsWindows ? `(${args.sendText})\nexit /b %ErrorLevel%` : `${args.sendText}\nexit $?`;
terminal.sendText(sendText);
getOutputChannelLogger().appendLine(localize('ssh.wrote.data.to.terminal', '"{0}" wrote data to terminal: "{1}".', nickname, args.sendText));
}
if (args.showLoginTerminal) {
terminal.show();
}
windowListener = systemInteractor.onDidCloseTerminal(t => {
if (t === terminal) {
terminal = undefined; // Is already disposed
done(false, false, t.exitStatus?.code);
}
});
} catch (error) {
failed(error);
}
});
}
function getShellPath(_isWindows: boolean): string {
if (_isWindows) {
// Some users don't have cmd.exe on the path...
if (process.env.SystemRoot) {
// This var should always exist but be paranoid
const cmdPath: string = path.join(process.env.SystemRoot, 'System32', 'cmd.exe');
return cmdPath;
} else {
return 'cmd.exe';
}
} else {
// Note - can't rely on having sh in path (#590), and can't check the disk (bc remote terminals)
return '/bin/sh';
}
}
function logReceivedData(data: string, nickname: string): void {
const logData: string = data.replace(/\r?\n$/, ''); // Trim single trailing newline for nicer log
if (logData === ' ') {
// From the sleep command that must periodically echo ' '
return;
}
const markedLines: string = logData
.split(/\n/)
.map(line => `${nickname}> ${line}`)
.join('\n');
getOutputChannelLogger().appendLine(markedLines);
}
function lastNonemptyLine(str: string): string | undefined {
const lines: string[] = splitLines(str);
if (isWindows()) {
let outputContainingPipeError: string = '';
for (let i: number = lines.length - 1; i >= 0; i--) {
const strippedLine: string = stripEscapeSequences(lines[i]);
if (strippedLine.match(/The process tried to write to a nonexistent pipe/)) {
outputContainingPipeError = strippedLine;
continue;
}
if (!!strippedLine) {
return strippedLine;
}
}
if (outputContainingPipeError) {
return outputContainingPipeError;
}
}
const nonEmptyLines: string[] = lines.filter(l => !!l);
return nonEmptyLines[nonEmptyLines.length - 1];
}
+115 -27
View File
@@ -19,9 +19,9 @@ import * as tmp from 'tmp';
import { ClientRequest, OutgoingHttpHeaders } from 'http';
import { lookupString } from './nativeStrings';
import * as nls from 'vscode-nls';
import { Readable } from 'stream';
import * as jsonc from 'comment-json';
import { TargetPopulation } from 'vscode-tas-client';
import { CppSettings } from './LanguageServer/settings';
nls.config({ messageFormat: nls.MessageFormat.bundle, bundleFormat: nls.BundleFormat.standalone })();
const localize: nls.LocalizeFunc = nls.loadMessageBundle();
@@ -321,10 +321,13 @@ export function resolveCachePath(input: string | undefined, additionalEnvironmen
return resolvedPath;
}
export function isWindows(): boolean {
return os.platform() === 'win32';
}
export function defaultExePath(): string {
const isWindows: boolean = os.platform() === 'win32';
const exePath: string = path.join('${fileDirname}', '${fileBasenameNoExtension}');
return isWindows ? exePath + '.exe' : exePath;
return isWindows() ? exePath + '.exe' : exePath;
}
export function findExePathInArgs(args: string[]): string | undefined {
@@ -570,9 +573,9 @@ export function checkDirectoryExistsSync(dirPath: string): boolean {
}
/** Test whether a relative path exists */
export function checkPathExistsSync(path: string, relativePath: string, isWindows: boolean, isWSL: boolean, isCompilerPath: boolean): { pathExists: boolean; path: string } {
export function checkPathExistsSync(path: string, relativePath: string, _isWindows: boolean, isWSL: boolean, isCompilerPath: boolean): { pathExists: boolean; path: string } {
let pathExists: boolean = true;
const existsWithExeAdded: (path: string) => boolean = (path: string) => isCompilerPath && isWindows && !isWSL && fs.existsSync(path + ".exe");
const existsWithExeAdded: (path: string) => boolean = (path: string) => isCompilerPath && _isWindows && !isWSL && fs.existsSync(path + ".exe");
if (!fs.existsSync(path)) {
if (existsWithExeAdded(path)) {
path += ".exe";
@@ -711,32 +714,83 @@ export function execChildProcess(process: string, workingDirectory?: string, cha
});
}
export function spawnChildProcess(process: string, args: string[], workingDirectory: string,
dataCallback: (stdout: string) => void, errorCallback: (stderr: string) => void): Promise<void> {
export interface ProcessReturnType {
succeeded: boolean;
exitCode?: number | NodeJS.Signals;
output: string;
}
return new Promise<void>(function (resolve, reject): void {
const child: child_process.ChildProcess = child_process.spawn(process, args, { cwd: workingDirectory });
export async function spawnChildProcess(program: string, args: string[] = [], continueOn?: string, cancellationToken?: vscode.CancellationToken): Promise<ProcessReturnType> {
const programOutput: ProcessOutput = await spawnChildProcessImpl(program, args, continueOn, cancellationToken);
const exitCode: number | NodeJS.Signals | undefined = programOutput.exitCode;
const settings: CppSettings = new CppSettings();
if (settings.loggingLevel === "Information" || settings.loggingLevel === "Debug") {
getOutputChannelLogger().appendLine(`$ ${program} ${args.join(' ')}\n${programOutput.stderr || programOutput.stdout}\n`);
}
if (programOutput.exitCode) {
return { succeeded: false, exitCode, output: programOutput.stderr || programOutput.stdout || localize('process.exited', 'Process exited with code {0}', exitCode) };
} else {
let stdout: string;
if (programOutput.stdout.length) {
// Type system doesn't work very well here, so we need call toString
stdout = programOutput.stdout;
} else {
stdout = localize('process.succeeded', 'Process executed successfully.');
}
return { succeeded: true, exitCode, output: stdout };
}
}
const stdout: Readable | null = child.stdout;
if (stdout) {
stdout.on('data', (data) => {
dataCallback(`${data}`);
});
interface ProcessOutput {
exitCode?: number | NodeJS.Signals;
stdout: string;
stderr: string;
}
async function spawnChildProcessImpl(program: string, args: string[], continueOn?: string, cancellationToken?: vscode.CancellationToken): Promise<ProcessOutput> {
return new Promise(async (resolve, reject) => {
let proc: child_process.ChildProcess;
if (await isExecutable(program)) {
proc = child_process.spawn(`.${isWindows() ? '\\' : '/'}${path.basename(program)}`, args, { shell: true, cwd: path.dirname(program) });
} else {
proc = child_process.spawn(program, args, { shell: true });
}
const stderr: Readable | null = child.stderr;
if (stderr) {
stderr.on('data', (data) => {
errorCallback(`${data}`);
});
}
const cancellationTokenListener: vscode.Disposable | undefined = cancellationToken?.onCancellationRequested(() => {
getOutputChannelLogger().appendLine(localize('killing.process', 'Killing process {0}', program));
proc.kill();
});
child.on('exit', (code: number) => {
if (code !== 0) {
reject(new Error(localize("process.exited.with.code", "{0} exited with error code {1}", process, code)));
} else {
resolve();
const clean = () => {
proc.removeAllListeners();
if (cancellationTokenListener) {
cancellationTokenListener.dispose();
}
};
let stdout: string = '';
let stderr: string = '';
if (proc.stdout) {
proc.stdout.on('data', data => {
stdout += data.toString();
if (continueOn) {
const continueOnReg: string = escapeStringForRegex(continueOn);
if (stdout.search(continueOnReg)) {
resolve({ stdout: stdout.trim(), stderr: stderr.trim() });
}
}
});
}
if (proc.stderr) {
proc.stderr.on('data', data => stderr += data.toString());
}
proc.on('close', (code, signal) => {
clean();
resolve({ exitCode: code || signal, stdout: stdout.trim(), stderr: stderr.trim() });
});
proc.on('error', error => {
clean();
reject(error);
});
});
}
@@ -927,7 +981,6 @@ function resolveWindowsEnvironmentVariables(str: string): string {
}
function legacyExtractArgs(argsString: string): string[] {
const isWindows: boolean = os.platform() === 'win32';
const result: string[] = [];
let currentArg: string = "";
let isWithinDoubleQuote: boolean = false;
@@ -951,7 +1004,7 @@ function legacyExtractArgs(argsString: string): string[] {
}
} else if (c === '\'') {
// On Windows, a single quote string is not allowed to join multiple args into a single arg
if (!isWindows) {
if (!isWindows()) {
if (!isWithinDoubleQuote) {
isWithinSingleQuote = !isWithinSingleQuote;
}
@@ -1471,3 +1524,38 @@ export function isVsCodeInsiders(): boolean {
extensionPath.includes(".vscode-server-exploration");
}
export function stripEscapeSequences(str: string): string {
return str
.replace(/\x1b\[\??[0-9]{0,3}(;[0-9]{1,3})?[a-zA-Z]/g, '')
.replace(/\u0008/g, '')
.replace(/\r/g, '');
}
export function splitLines(data: string): string[] {
return data.split(/\r?\n/g);
}
export function escapeStringForRegex(str: string): string {
return str.replace(/([.*+?^=!:${}()|\[\]\/\\])/g, '\\$1');
}
export function replaceAll(str: string, searchValue: string, replaceValue: string): string {
const pattern: string = escapeStringForRegex(searchValue);
const re: RegExp = new RegExp(pattern, 'g');
return str.replace(re, replaceValue);
}
export interface ISshHostInfo {
hostName: string;
user?: string;
port?: number | string;
}
export interface ISshLocalForwardInfo {
bindAddress?: string;
port?: number | string;
host?: string;
hostPort?: number | string;
localSocket?: string;
remoteSocket?: string;
}
+133
View File
@@ -0,0 +1,133 @@
/* --------------------------------------------------------------------------------------------
* Copyright (c) Microsoft Corporation. All Rights Reserved.
* See 'LICENSE' in the project root for license information.
* ------------------------------------------------------------------------------------------ */
import * as vscode from 'vscode';
import * as nls from 'vscode-nls';
import { isString, replaceAll } from './common';
import { getOutputChannelLogger } from './logger';
/**
* Support ExpansionVars (${var}), env (${env:var}), and optionally VS CODE commands (${command:commandID}).
* Supported format follows https://code.visualstudio.com/docs/editor/variables-reference
* Expand options and functions are mofidifed from https://github.com/microsoft/vscode-cmake-tools/blob/main/src/expand.ts
*/
nls.config({ messageFormat: nls.MessageFormat.bundle, bundleFormat: nls.BundleFormat.standalone })();
const localize: nls.LocalizeFunc = nls.loadMessageBundle();
export interface ExpansionVars {
[key: string]: string;
workspaceFolder: string;
workspaceFolderBasename: string;
}
export interface ExpansionOptions {
vars: ExpansionVars;
doNotSupportCommands?: boolean;
recursive?: boolean;
}
export async function expandAllStrings(obj: any, options: ExpansionOptions): Promise<void> {
if (Array.isArray(obj) || (obj !== null && typeof obj === 'object')) {
for (const key of Object.keys(obj)) {
if (isString(obj[key])) {
obj[key] = await expandString(obj[key], options);
} else {
await expandAllStrings(obj[key], options);
}
}
}
}
export async function expandString(input: string, options: ExpansionOptions): Promise<string> {
const MAX_RECURSION: number = 10;
let result: string = input;
let didReplacement: boolean = false;
let i: number = 0;
do {
// TODO: consider a full circular reference check?
[result, didReplacement] = await expandStringImpl(result, options);
i++;
} while (i < MAX_RECURSION && options.recursive && didReplacement);
if (i === MAX_RECURSION && didReplacement) {
getOutputChannelLogger().showErrorMessage(localize('max.recursion.reached', 'Reached max string expansion recursion. Possible circular reference.'));
}
return replaceAll(result, '${dollar}', '$');
}
/** Returns [expandedString, didReplacement] */
async function expandStringImpl(input: string, options: ExpansionOptions): Promise<[string, boolean]> {
if (!input) {
return [input, false];
}
// We accumulate a list of substitutions that we need to make, preventing
// recursively expanding or looping forever on bad replacements
const subs: Map<string, string> = new Map<string, string>();
const var_re: RegExp = /\$\{(\w+)\}/g;
let match: RegExpMatchArray | null = null;
while ((match = var_re.exec(input))) {
const full: string = match[0];
const key: string = match[1];
if (key !== 'dollar') {
// Replace dollar sign at the very end of the expanding process
const repl: string = options.vars[key];
if (!repl) {
getOutputChannelLogger().showWarningMessage(localize('invalid.var.reference', 'Invalid variable reference {0} in string: {1}.', full, input));
} else {
subs.set(full, repl);
}
}
}
// Regular expression for variable value (between the variable suffix and the next ending curly bracket):
// .+? matches any character (except line terminators) between one and unlimited times,
// as few times as possible, expanding as needed (lazy)
const varValueRegexp: string = ".+?";
const env_re: RegExp = RegExp(`\\$\\{env:(${varValueRegexp})\\}`, "g");
while ((match = env_re.exec(input))) {
const full: string = match[0];
const varname: string = match[1];
if (process.env[varname] === undefined) {
getOutputChannelLogger().showWarningMessage(localize('env.var.not.found', 'Environment variable {0} not found', varname));
}
const repl: string = process.env[varname] || '';
subs.set(full, repl);
}
const command_re: RegExp = RegExp(`\\$\\{command:(${varValueRegexp})\\}`, "g");
while ((match = command_re.exec(input))) {
if (options.doNotSupportCommands) {
getOutputChannelLogger().showWarningMessage(localize('commands.not.supported', 'Commands are not supported for string: {0}.', input));
break;
}
const full: string = match[0];
const command: string = match[1];
if (subs.has(full)) {
continue; // Don't execute commands more than once per string
}
try {
const command_ret: unknown = await vscode.commands.executeCommand(command, options.vars.workspaceFolder);
subs.set(full, `${command_ret}`);
} catch (e: any) {
getOutputChannelLogger().showWarningMessage(localize('exception.executing.command', 'Exception while executing command {0} for string: {1} {2}.', command, input, e));
}
}
let result: string = input;
let didReplacement: boolean = false;
subs.forEach((value, key) => {
if (value !== key) {
result = replaceAll(result, key, value);
didReplacement = true;
}
});
return [result, didReplacement];
}
+3 -3
View File
@@ -4,7 +4,7 @@
* ------------------------------------------------------------------------------------------ */
'use strict';
import TelemetryReporter from 'vscode-extension-telemetry';
import TelemetryReporter from '@vscode/extension-telemetry';
import { getExperimentationServiceAsync, IExperimentationService, IExperimentationTelemetry, TargetPopulation } from 'vscode-tas-client';
import * as util from './common';
@@ -89,11 +89,11 @@ export async function deactivate(): Promise<void> {
}
}
export function logDebuggerEvent(eventName: string, properties?: { [key: string]: string }): void {
export function logDebuggerEvent(eventName: string, properties?: { [key: string]: string }, metrics?: { [key: string]: number }): void {
const sendTelemetry = () => {
if (experimentationTelemetry) {
const eventNamePrefix: string = "cppdbg/VS/Diagnostics/Debugger/";
experimentationTelemetry.sendTelemetryEvent(eventNamePrefix + eventName, properties);
experimentationTelemetry.sendTelemetryEvent(eventNamePrefix + eventName, properties, metrics);
}
};
+2 -1
View File
@@ -14,6 +14,7 @@
"include": [
"test/**/*.ts",
"tools/**/*.ts",
"ui/**/*.ts"
"ui/**/*.ts",
"./vscode*.d.ts"
]
}
+46
View File
@@ -0,0 +1,46 @@
/* --------------------------------------------------------------------------------------------
* Copyright (c) Microsoft Corporation. All Rights Reserved.
* See 'LICENSE' in the project root for license information.
* ------------------------------------------------------------------------------------------ */
import * as assert from "assert";
import { ExpansionOptions, expandAllStrings, expandString } from "../../src/expand";
suite('Var expansion validation', () => {
test('Expand all strings', async () => {
const input: object = {
in1: "${test2}",
in2: "${test4}",
in3: [
"${test3}",
{
in4: "${test3}"
}
]
};
const variables: object = {
test1: "t1",
test2: "${dollar}{${test1}}",
test3: "test${test2}test:${env:envtest}:${dollar}test",
test4: "${test4}"
};
const expansionOptions: ExpansionOptions = {
vars: {
workspaceFolder: '{workspaceFolder}',
workspaceFolderBasename: '{workspaceFolderBasename}',
...variables
},
recursive: true
};
await expandAllStrings(input, expansionOptions);
assert.deepStrictEqual(input, {
in1: "${t1}",
in2: "${test4}",
in3: [
"test${t1}test::$test",
{
in4: "test${t1}test::$test"
}
]
});
});
});
+8 -7
View File
@@ -53,7 +53,7 @@ function updateDefaults(object: any, defaults: any): any {
}
function refReplace(definitions: any, ref: any): any {
// $ref is formatted as "#/definitions/ObjectName"
// $ref is formatted as "#/definitions/ObjectName"
const referenceStringArray: string[] = ref['$ref'].split('/');
// Getting "ObjectName"
@@ -82,11 +82,7 @@ function replaceReferences(definitions: any, objects: any): any {
// Handle 'anyOf' with references
if (objects[key].hasOwnProperty('anyOf')) {
for (const index in objects[key].anyOf) {
if (objects[key].anyOf[index].hasOwnProperty('$ref')) {
objects[key].anyOf[index] = refReplace(definitions, objects[key].anyOf[index]);
}
}
objects[key].anyOf = replaceReferences(definitions, objects[key].anyOf);
}
// Recursively replace references if this object has properties.
@@ -95,10 +91,15 @@ function replaceReferences(definitions: any, objects: any): any {
objects[key].properties = updateDefaults(objects[key].properties, objects[key].default);
}
// Recursively replace references if the array has objects in items.
// Items in array contains ref
if (objects[key].hasOwnProperty('type') && objects[key].type === "array" && objects[key].items !== null && objects[key].items.hasOwnProperty('$ref')) {
objects[key].items = refReplace(definitions, objects[key].items);
}
// Recursively replace references if the array has objects that contains ref in items.
if (objects[key].hasOwnProperty('type') && objects[key].type === "array" && objects[key].items !== null) {
objects[key].items = replaceReferences(definitions, objects[key].items);
}
}
return objects;
+321 -2
View File
@@ -193,6 +193,313 @@
}
}
},
"Variables": {
"type": "object",
"description": "%c_cpp.debuggers.variables.description%",
"default": {
"<variable-name>": "<variable-value>"
},
"properties": {},
"additionalProperties": {
"type": "string",
"description": "%c_cpp.debuggers.variables.properties.description%",
"default": ""
}
},
"VsCodeCommand": {
"type": "object",
"description": "%c_cpp.debuggers.vsCodeCommand.description%",
"default": {},
"required": [
"type",
"command"
],
"properties": {
"type": {
"type": "string",
"description": "%c_cpp.debuggers.vsCodeCommand.description%",
"default": "",
"enum": [
"command"
]
},
"command": {
"type": "string",
"description": "%c_cpp.debuggers.vsCodeCommand.command.description%",
"default": ""
},
"args": {
"type": "array",
"description": "%c_cpp.debuggers.vsCodeCommand.args.description%",
"items": {
"type": "string"
}
}
}
},
"Host": {
"type": "object",
"description": "%c_cpp.debuggers.host.description%",
"default": {},
"required": [
"hostName"
],
"properties": {
"user": {
"type": "string",
"description": "%c_cpp.debuggers.host.user.description%",
"default": ""
},
"hostName": {
"type": "string",
"description": "%c_cpp.debuggers.host.hostName.description%",
"default": ""
},
"port": {
"anyOf": [
{
"type": "number"
},
{
"type": "string",
"pattern": "^\\d+$|^\\${.+}$"
}
],
"description": "%c_cpp.debuggers.host.port.description%",
"default": 22
},
"jumpHosts": {
"type": "array",
"description": "%c_cpp.debuggers.host.jumpHost.description%",
"items": {
"type": "object",
"default": {},
"required": [
"hostName"
],
"properties": {
"user": {
"type": "string",
"description": "%c_cpp.debuggers.host.user.description%",
"default": ""
},
"hostName": {
"type": "string",
"description": "%c_cpp.debuggers.host.hostName.description%",
"default": ""
},
"port": {
"anyOf": [
{
"type": "number"
},
{
"type": "string",
"pattern": "^\\d+$|^\\${.+}$"
}
],
"description": "%c_cpp.debuggers.host.port.description%",
"default": 22
}
}
}
},
"localForwards": {
"type": "array",
"description": "%c_cpp.debuggers.host.localForward.description%",
"items": {
"type": "object",
"default": {},
"properties": {
"bindAddress": {
"type": "string",
"description": "%c_cpp.debuggers.host.localForward.bindAddress.description%",
"default": ""
},
"port": {
"anyOf": [
{
"type": "number"
},
{
"type": "string",
"pattern": "^\\d+$|^\\${.+}$"
}
],
"description": "%c_cpp.debuggers.host.localForward.port.description%"
},
"host": {
"type": "string",
"description": "%c_cpp.debuggers.host.localForward.host.description%",
"default": ""
},
"hostPort": {
"anyOf": [
{
"type": "number"
},
{
"type": "string",
"pattern": "^\\d+$|^\\${.+}$"
}
],
"description": "%c_cpp.debuggers.host.localForward.hostPort.description%"
},
"localSocket": {
"type": "string",
"description": "%c_cpp.debuggers.host.localForward.localSocket.description%",
"default": ""
},
"remoteSocket": {
"type": "string",
"description": "%c_cpp.debuggers.host.localForward.remoteSocket.description%",
"default": ""
}
}
}
}
}
},
"DeploySteps": {
"type": "array",
"description": "%c_cpp.debuggers.deploySteps.description%",
"items": {
"anyOf": [
{
"type": "object",
"description": "%c_cpp.debuggers.deploySteps.scp.description%",
"default": {},
"required": [
"type",
"files",
"host",
"targetDir"
],
"properties": {
"type": {
"type": "string",
"description": "%c_cpp.debuggers.deploySteps.scp.description%",
"default": "",
"enum": [
"scp"
]
},
"files": {
"anyOf": [
{
"type": "string"
},
{
"type": "array",
"items": {
"type": "string"
}
}
],
"description": "%c_cpp.debuggers.deploySteps.scp.files.description%",
"default": ""
},
"host": {
"$ref": "#/definitions/Host"
},
"targetDir": {
"type": "string",
"description": "%c_cpp.debuggers.deploySteps.scp.targetDir.description%",
"default": ""
},
"scpPath": {
"type": "string",
"description": "%c_cpp.debuggers.deploySteps.scp.scpPath.description%",
"default": ""
},
"debug": {
"type": "boolean",
"description": "%c_cpp.debuggers.deploySteps.debug%"
}
}
},
{
"type": "object",
"description": "%c_cpp.debuggers.deploySteps.ssh.description%",
"default": {},
"required": [
"type",
"host",
"command"
],
"properties": {
"type": {
"type": "string",
"description": "%c_cpp.debuggers.deploySteps.ssh.description%",
"default": "",
"enum": [
"ssh"
]
},
"host": {
"$ref": "#/definitions/Host"
},
"command": {
"type": "string",
"description": "%c_cpp.debuggers.deploySteps.ssh.command.description%",
"default": ""
},
"sshPath": {
"type": "string",
"description": "%c_cpp.debuggers.deploySteps.ssh.sshPath.description%",
"default": ""
},
"continueOn": {
"type": "string",
"description": "%c_cpp.debuggers.deploySteps.continueOn.description%",
"default": ""
},
"debug": {
"type": "boolean",
"description": "%c_cpp.debuggers.deploySteps.debug%"
}
}
},
{
"type": "object",
"description": "%c_cpp.debuggers.deploySteps.shell.description%",
"default": {},
"required": [
"type",
"command"
],
"properties": {
"type": {
"type": "string",
"description": "%c_cpp.debuggers.deploySteps.shell.description%",
"default": "",
"enum": [
"shell"
]
},
"command": {
"type": "string",
"description": "%c_cpp.debuggers.deploySteps.shell.command.description%",
"default": ""
},
"continueOn": {
"type": "string",
"description": "%c_cpp.debuggers.deploySteps.continueOn.description%",
"default": ""
},
"debug": {
"type": "boolean",
"description": "%c_cpp.debuggers.deploySteps.debug%"
}
}
},
{
"$ref": "#/definitions/VsCodeCommand"
}
]
},
"default": []
},
"CppdbgLaunchOptions": {
"type": "object",
"required": [
@@ -253,8 +560,8 @@
},
"launchCompleteCommand": {
"enum": [
"exec-run",
"exec-continue",
"exec-run",
"exec-continue",
"None"
],
"description": "%c_cpp.debuggers.launchCompleteCommand.description%",
@@ -399,6 +706,12 @@
"$ref": "#/definitions/HardwareBreakpoints",
"description": "%c_cpp.debuggers.hardwareBreakpoints.description%",
"default": {}
},
"variables": {
"$ref": "#/definitions/Variables"
},
"deploySteps": {
"$ref": "#/definitions/DeploySteps"
}
}
},
@@ -513,6 +826,12 @@
"symbolLoadInfo": {
"$ref": "#/definitions/SymbolLoadInfo",
"description": "%c_cpp.debuggers.symbolLoadInfo.description%"
},
"variables": {
"$ref": "#/definitions/Variables"
},
"deploySteps": {
"$ref": "#/definitions/DeploySteps"
}
}
},
+1 -1
View File
@@ -15,7 +15,7 @@
},
"include": [
"src/**/*.ts",
"./node_modules/vscode/vscode.d.ts",
"./vscode*.d.ts",
"./node_modules/vscode/lib/*"
]
}
+121 -40
View File
@@ -44,14 +44,14 @@
resolved "https://registry.yarnpkg.com/@discoveryjs/json-ext/-/json-ext-0.5.2.tgz#8f03a22a04de437254e8ce8cc84ba39689288752"
integrity sha512-HyYEUDeIj5rRQU2Hk5HTB2uHsbRQpF70nvMhVzi+VJR0X+xNEhjPui4/kBf3VeH/wqD28PT4sVOm8qqLjBrSZg==
"@es-joy/jsdoccomment@0.10.8":
version "0.10.8"
resolved "https://registry.yarnpkg.com/@es-joy/jsdoccomment/-/jsdoccomment-0.10.8.tgz#b3152887e25246410ed4ea569a55926ec13b2b05"
integrity sha512-3P1JiGL4xaR9PoTKUHa2N/LKwa2/eUdRqGwijMWWgBqbFEqJUVpmaOi2TcjcemrsRMgFLBzQCK4ToPhrSVDiFQ==
"@es-joy/jsdoccomment@~0.31.0":
version "0.31.0"
resolved "https://registry.yarnpkg.com/@es-joy/jsdoccomment/-/jsdoccomment-0.31.0.tgz#dbc342cc38eb6878c12727985e693eaef34302bc"
integrity sha512-tc1/iuQcnaiSIUVad72PBierDFpsxdUHtEF/OrfqvM1CBAsIoMP51j52jTMb3dXriwhieTo289InzZj72jL3EQ==
dependencies:
comment-parser "1.2.4"
comment-parser "1.3.1"
esquery "^1.4.0"
jsdoc-type-pratt-parser "1.1.1"
jsdoc-type-pratt-parser "~3.1.0"
"@eslint/eslintrc@^0.4.3":
version "0.4.3"
@@ -101,6 +101,42 @@
resolved "https://registry.yarnpkg.com/@humanwhocodes/object-schema/-/object-schema-1.2.1.tgz#b520529ec21d8e5945a1851dfd1c32e94e39ff45"
integrity sha512-ZnQMnLV4e7hDlUvw8H+U8ASL02SS2Gn6+9Ac3wGGLIe7+je2AeAOxPY+izIPJDfFDb7eDjev0Us8MO1iFRN8hA==
"@microsoft/[email protected]", "@microsoft/1ds-core-js@^3.2.3":
version "3.2.3"
resolved "https://registry.yarnpkg.com/@microsoft/1ds-core-js/-/1ds-core-js-3.2.3.tgz#2217d92ec8b073caa4577a13f40ea3a5c4c4d4e7"
integrity sha512-796A8fd90oUKDRO7UXUT9BwZ3G+a9XzJj5v012FcCN/2qRhEsIV3x/0wkx2S08T4FiQEUPkB2uoYHpEjEneM7g==
dependencies:
"@microsoft/applicationinsights-core-js" "2.8.4"
"@microsoft/applicationinsights-shims" "^2.0.1"
"@microsoft/dynamicproto-js" "^1.1.6"
"@microsoft/1ds-post-js@^3.2.3":
version "3.2.3"
resolved "https://registry.yarnpkg.com/@microsoft/1ds-post-js/-/1ds-post-js-3.2.3.tgz#1fa7d51615a44f289632ae8c588007ba943db216"
integrity sha512-tcGJQXXr2LYoBbIXPoUVe1KCF3OtBsuKDFL7BXfmNtuSGtWF0yejm6H83DrR8/cUIGMRMUP9lqNlqFGwDYiwAQ==
dependencies:
"@microsoft/1ds-core-js" "3.2.3"
"@microsoft/applicationinsights-shims" "^2.0.1"
"@microsoft/dynamicproto-js" "^1.1.6"
"@microsoft/[email protected]":
version "2.8.4"
resolved "https://registry.yarnpkg.com/@microsoft/applicationinsights-core-js/-/applicationinsights-core-js-2.8.4.tgz#607e531bb241a8920d43960f68a7c76a6f9af596"
integrity sha512-FoA0FNOsFbJnLyTyQlYs6+HR7HMEa6nAOE6WOm9WVejBHMHQ/Bdb+hfVFi6slxwCimr/ner90jchi4/sIYdnyQ==
dependencies:
"@microsoft/applicationinsights-shims" "2.0.1"
"@microsoft/dynamicproto-js" "^1.1.6"
"@microsoft/[email protected]", "@microsoft/applicationinsights-shims@^2.0.1":
version "2.0.1"
resolved "https://registry.yarnpkg.com/@microsoft/applicationinsights-shims/-/applicationinsights-shims-2.0.1.tgz#5d72fb7aaf4056c4fda54f9d7c93ccf8ca9bcbfd"
integrity sha512-G0MXf6R6HndRbDy9BbEj0zrLeuhwt2nsXk2zKtF0TnYo39KgYqhYC2ayIzKPTm2KAE+xzD7rgyLdZnrcRvt9WQ==
"@microsoft/dynamicproto-js@^1.1.6":
version "1.1.6"
resolved "https://registry.yarnpkg.com/@microsoft/dynamicproto-js/-/dynamicproto-js-1.1.6.tgz#6fe03468862861f5f88ac4c3959a652b3797f1bc"
integrity sha512-D1Oivw1A4bIXhzBIy3/BBPn3p2On+kpO2NiYt9shICDK7L/w+cR6FFBUsBZ05l6iqzTeL+Jm8lAYn0g6G7DmDg==
"@nodelib/[email protected]":
version "2.1.4"
resolved "https://registry.yarnpkg.com/@nodelib/fs.scandir/-/fs.scandir-2.1.4.tgz#d4b3549a5db5de2683e0c1071ab4f140904bbf69"
@@ -254,6 +290,14 @@
resolved "https://registry.yarnpkg.com/@types/estree/-/estree-0.0.46.tgz#0fb6bfbbeabd7a30880504993369c4bf1deab1fe"
integrity sha512-laIjwTQaD+5DukBZaygQ79K1Z0jb1bPEMRrkXSLjtCcZm+abyp5YbrqpSLzD42FwWW6gK/aS4NYpJ804nG2brg==
"@types/glob@^7.1.3":
version "7.2.0"
resolved "https://registry.yarnpkg.com/@types/glob/-/glob-7.2.0.tgz#bc1b5bf3aa92f25bd5dd39f35c57361bdce5b2eb"
integrity sha512-ZUxbzKl0IfJILTS6t7ip5fQQM/J3TJYubDm3nMbgubNNYS62eXeUpoLUC8/7fJNiFYHTrGPQn7hspDUzIHX3UA==
dependencies:
"@types/minimatch" "*"
"@types/node" "*"
"@types/json-schema@*", "@types/json-schema@^7.0.6":
version "7.0.7"
resolved "https://registry.yarnpkg.com/@types/json-schema/-/json-schema-7.0.7.tgz#98a993516c859eb0d5c4c8f098317a9ea68db9ad"
@@ -269,6 +313,11 @@
resolved "https://registry.yarnpkg.com/@types/json5/-/json5-0.0.29.tgz#ee28707ae94e11d2b827bcbe5270bcea7f3e71ee"
integrity sha1-7ihweulOEdK4J7y+UnC86n8+ce4=
"@types/minimatch@*":
version "3.0.5"
resolved "https://registry.yarnpkg.com/@types/minimatch/-/minimatch-3.0.5.tgz#1001cc5e6a3704b83c236027e77f2f58ea010f40"
integrity sha512-Klz949h02Gz2uZCMGwDUSDS1YBlTdDDgbWHi+81l29tQALUtvz4rAYi5uoVhE5Lagoq6DeqAUlbrHvW/mXDgdQ==
"@types/minimatch@^3.0.3":
version "3.0.3"
resolved "https://registry.yarnpkg.com/@types/minimatch/-/minimatch-3.0.3.tgz#3dca0e3f33b200fc7d1139c0cd96c1268cadfd9d"
@@ -316,11 +365,6 @@
resolved "https://registry.yarnpkg.com/@types/tmp/-/tmp-0.1.0.tgz#19cf73a7bcf641965485119726397a096f0049bd"
integrity sha512-6IwZ9HzWbCq6XoQWhxLpDjuADodH/MKXRUIDFudvgjcVdjFknvmR+DNsoUeer4XPrEnrZs04Jj+kfV9pFsrhmA==
"@types/[email protected]":
version "1.65.0"
resolved "https://registry.yarnpkg.com/@types/vscode/-/vscode-1.65.0.tgz#042dd8d93c32ac62cb826cd0fa12376069d1f448"
integrity sha512-wQhExnh2nEzpjDMSKhUvnNmz3ucpd3E+R7wJkOhBNK3No6fG3VUdmVmMOKD0A8NDZDDDiQcLNxe3oGmX5SjJ5w==
"@types/which@^1.3.2":
version "1.3.2"
resolved "https://registry.yarnpkg.com/@types/which/-/which-1.3.2.tgz#9c246fc0c93ded311c8512df2891fb41f6227fdf"
@@ -415,6 +459,14 @@
resolved "https://registry.yarnpkg.com/@ungap/promise-all-settled/-/promise-all-settled-1.1.2.tgz#aa58042711d6e3275dd37dc597e5d31e8c290a44"
integrity sha512-sL/cEvJWAnClXw0wHk85/2L0G6Sj8UB0Ctc1TEMbKSsmpRosqhwj9gWgFRZSrBr2f9tiXISwNhCPmlfqUqyb9Q==
"@vscode/extension-telemetry@^0.6.2":
version "0.6.2"
resolved "https://registry.yarnpkg.com/@vscode/extension-telemetry/-/extension-telemetry-0.6.2.tgz#b86814ee680615730da94220c2b03ea9c3c14a8e"
integrity sha512-yb/wxLuaaCRcBAZtDCjNYSisAXz3FWsSqAha5nhHcYxx2ZPdQdWuZqVXGKq0ZpHVndBWWtK6XqtpCN2/HB4S1w==
dependencies:
"@microsoft/1ds-core-js" "^3.2.3"
"@microsoft/1ds-post-js" "^3.2.3"
"@vscode/test-electron@^1.6.1":
version "1.6.1"
resolved "https://registry.yarnpkg.com/@vscode/test-electron/-/test-electron-1.6.1.tgz#2b282154097e250ee9b94b6a284eb5804e53a3d7"
@@ -1332,10 +1384,10 @@ comment-json@^4.1.1:
has-own-prop "^2.0.0"
repeat-string "^1.6.1"
comment-parser@1.2.4:
version "1.2.4"
resolved "https://registry.yarnpkg.com/comment-parser/-/comment-parser-1.2.4.tgz#489f3ee55dfd184a6e4bffb31baba284453cb760"
integrity sha512-pm0b+qv+CkWNriSTMsfnjChF9kH0kxz55y44Wo5le9qLxMj5xDQAaEd9ZN1ovSuk9CsrncWaFwgpOMg7ClJwkw==
comment-parser@1.3.1:
version "1.3.1"
resolved "https://registry.yarnpkg.com/comment-parser/-/comment-parser-1.3.1.tgz#3d7ea3adaf9345594aedee6563f422348f165c1b"
integrity sha512-B52sN2VNghyq5ofvUsqZjmk6YkihBX5vMSChmSK9v4ShjKf3Vk5Xcmgpw4o+iIgtrnM/u5FiMpz9VKb8lpBveA==
component-emitter@^1.2.1:
version "1.3.0"
@@ -1453,7 +1505,7 @@ [email protected]:
dependencies:
ms "^2.1.1"
debug@4, debug@^4.3.2:
debug@4:
version "4.3.2"
resolved "https://registry.yarnpkg.com/debug/-/debug-4.3.2.tgz#f0a49c18ac8779e31d4a0c6029dfb76873c7428b"
integrity sha512-mOp8wKcvj7XxC78zLgw/ZA+6TSgkoE2C/ienthhRD298T7UNwAg9diBpLRxC0mOezLl4B0xV7M0cCO6P/O0Xhw==
@@ -1488,6 +1540,13 @@ debug@^4.0.1, debug@^4.1.1:
dependencies:
ms "^2.1.1"
debug@^4.3.4:
version "4.3.4"
resolved "https://registry.yarnpkg.com/debug/-/debug-4.3.4.tgz#1319f6579357f2338d3337d2cdd4914bb5dcc865"
integrity sha512-PRWFHuSU3eDtQJPvnNY7Jcket1j0t5OuOsFzPPzsekD52Zl8qUfFIPEiswXqIvHWGVHOgX+7G/vCNNhehwxfkQ==
dependencies:
ms "2.1.2"
decamelize@^1.1.1, decamelize@^1.2.0:
version "1.2.0"
resolved "https://registry.yarnpkg.com/decamelize/-/decamelize-1.2.0.tgz#f6534d15148269b20352e7bee26f501f9a191290"
@@ -1870,19 +1929,17 @@ eslint-plugin-import@^2.24.2:
resolve "^1.20.0"
tsconfig-paths "^3.11.0"
eslint-plugin-jsdoc@^36.1.0:
version "36.1.0"
resolved "https://registry.yarnpkg.com/eslint-plugin-jsdoc/-/eslint-plugin-jsdoc-36.1.0.tgz#8dfe5f27edfb6aa3812e6d86ccaea849ddc86b03"
integrity sha512-Qpied2AJCQcScxfzTObLKRiP5QgLXjMU/ITjBagEV5p2Q/HpumD1EQtazdRYdjDSwPmXhwOl2yquwOGQ4HOJNw==
eslint-plugin-jsdoc@^39.3.3:
version "39.3.3"
resolved "https://registry.yarnpkg.com/eslint-plugin-jsdoc/-/eslint-plugin-jsdoc-39.3.3.tgz#75dd67ce581e7527a69f27800138cc0f9c388236"
integrity sha512-K/DAjKRUNaUTf0KQhI9PvsF+Y3mGDx/j0pofXsJCQe/tmRsRweBIXR353c8nAro0lytZYEf7l0PluBpzKDiHxw==
dependencies:
"@es-joy/jsdoccomment" "0.10.8"
comment-parser "1.2.4"
debug "^4.3.2"
"@es-joy/jsdoccomment" "~0.31.0"
comment-parser "1.3.1"
debug "^4.3.4"
escape-string-regexp "^4.0.0"
esquery "^1.4.0"
jsdoc-type-pratt-parser "^1.1.1"
lodash "^4.17.21"
regextras "^0.8.0"
semver "^7.3.5"
semver "^7.3.7"
spdx-expression-parse "^3.0.1"
eslint-scope@^5.0.0:
@@ -3407,10 +3464,10 @@ js-yaml@^3.13.1:
argparse "^1.0.7"
esprima "^4.0.0"
jsdoc-type-pratt-parser@1.1.1, jsdoc-type-pratt-parser@^1.1.1:
version "1.1.1"
resolved "https://registry.yarnpkg.com/jsdoc-type-pratt-parser/-/jsdoc-type-pratt-parser-1.1.1.tgz#10fe5e409ba38de22a48b555598955a26ff0160f"
integrity sha512-uelRmpghNwPBuZScwgBG/OzodaFk5RbO5xaivBdsAY70icWfShwZ7PCMO0x1zSkOa8T1FzHThmrdoyg/0AwV5g==
jsdoc-type-pratt-parser@~3.1.0:
version "3.1.0"
resolved "https://registry.yarnpkg.com/jsdoc-type-pratt-parser/-/jsdoc-type-pratt-parser-3.1.0.tgz#a4a56bdc6e82e5865ffd9febc5b1a227ff28e67e"
integrity sha512-MgtD0ZiCDk9B+eI73BextfRrVQl0oyzRG8B2BjORts6jbunj4ScKPcyXGTbB6eXL4y9TzxCm6hyeLq/2ASzNdw==
json-parse-better-errors@^1.0.1, json-parse-better-errors@^1.0.2:
version "1.0.2"
@@ -3477,6 +3534,11 @@ kind-of@^6.0.0, kind-of@^6.0.2:
resolved "https://registry.yarnpkg.com/kind-of/-/kind-of-6.0.3.tgz#07c05034a6c349fa06e24fa35aa76db4580ce4dd"
integrity sha512-dcS1ul+9tmeD95T+x28/ehLgd9mENa3LsvDTtzm3vyBEO7RPptvAD+t44WVXaUjTBRcrpFeFlC8WCruUR456hw==
kleur@^3.0.3:
version "3.0.3"
resolved "https://registry.yarnpkg.com/kleur/-/kleur-3.0.3.tgz#a79c9ecc86ee1ce3fa6206d1216c501f147fc07e"
integrity sha512-eTIzlVOSUR+JxdDFepEYcBMtZ9Qqdef+rnzWdRZuMbOywu5tO2w2N7rqjoANZ5k9vywhL6Br1VRjUIgTQx4E8w==
last-run@^1.1.0:
version "1.1.1"
resolved "https://registry.yarnpkg.com/last-run/-/last-run-1.1.1.tgz#45b96942c17b1c79c772198259ba943bebf8ca5b"
@@ -4432,6 +4494,14 @@ progress@^2.0.0:
resolved "https://registry.yarnpkg.com/progress/-/progress-2.0.3.tgz#7e8cf8d8f5b8f239c1bc68beb4eb78567d572ef8"
integrity sha512-7PiHtLll5LdnKIMw100I+8xJXR5gW2QwWYkT6iJva0bXitZKa/XMrSbdmg3r2Xnaidz9Qumd0VPaMrZlF9V9sA==
prompts@^2.1.0:
version "2.4.2"
resolved "https://registry.yarnpkg.com/prompts/-/prompts-2.4.2.tgz#7b57e73b3a48029ad10ebd44f74b01722a4cb069"
integrity sha512-NxNv/kLguCA7p3jE8oL2aEBsrJWgAakBpgmgK6lpPWV+WuOmY6r2/zbAVnP+T8bQlA0nzHXSJSJW0Hq7ylaD2Q==
dependencies:
kleur "^3.0.3"
sisteransi "^1.0.5"
prr@~1.0.1:
version "1.0.1"
resolved "https://registry.yarnpkg.com/prr/-/prr-1.0.1.tgz#d3fc114ba06995a45ec6893f484ceb1d78f5f476"
@@ -4585,11 +4655,6 @@ regexpp@^3.1.0:
resolved "https://registry.yarnpkg.com/regexpp/-/regexpp-3.1.0.tgz#206d0ad0a5648cffbdb8ae46438f3dc51c9f78e2"
integrity sha512-ZOIzd8yVsQQA7j8GCSlPGXwg5PfmA1mrq0JP4nGhh54LaKN3xdai/vHUDu74pKwV8OxseMS65u2NImosQcSD0Q==
regextras@^0.8.0:
version "0.8.0"
resolved "https://registry.yarnpkg.com/regextras/-/regextras-0.8.0.tgz#ec0f99853d4912839321172f608b544814b02217"
integrity sha512-k519uI04Z3SaY0fLX843MRXnDeG2+vHOFsyhiPZvNLe7r8rD2YNRjq4BQLZZ0oAr2NrtvZlICsXysGNFPGa3CQ==
remove-bom-buffer@^3.0.0:
version "3.0.0"
resolved "https://registry.yarnpkg.com/remove-bom-buffer/-/remove-bom-buffer-3.0.0.tgz#c2bf1e377520d324f623892e33c10cac2c252b53"
@@ -4818,6 +4883,13 @@ semver@^7.2.1, semver@^7.3.4, semver@^7.3.5:
dependencies:
lru-cache "^6.0.0"
semver@^7.3.7:
version "7.3.7"
resolved "https://registry.yarnpkg.com/semver/-/semver-7.3.7.tgz#12c5b649afdbf9049707796e22a4028814ce523f"
integrity sha512-QlYTucUYOews+WeEujDoEGziz4K6c47V/Bd+LjSSYcA94p+DmINdf7ncaUinThfvZyu13lN9OY1XDxt8C0Tw0g==
dependencies:
lru-cache "^6.0.0"
[email protected], serialize-javascript@^5.0.1:
version "5.0.1"
resolved "https://registry.yarnpkg.com/serialize-javascript/-/serialize-javascript-5.0.1.tgz#7886ec848049a462467a97d3d918ebb2aaf934f4"
@@ -4898,6 +4970,11 @@ signal-exit@^3.0.3:
resolved "https://registry.yarnpkg.com/signal-exit/-/signal-exit-3.0.3.tgz#a1410c2edd8f077b08b4e253c8eacfcaf057461c"
integrity sha512-VUJ49FC8U1OxwZLxIbTTrDvLnf/6TDgxZcK8wxR8zs13xpx7xbG60ndBlhNrFi2EMuFRoeDoJO7wthSLq42EjA==
sisteransi@^1.0.5:
version "1.0.5"
resolved "https://registry.yarnpkg.com/sisteransi/-/sisteransi-1.0.5.tgz#134d681297756437cc05ca01370d3a7a571075ed"
integrity sha512-bLGGlR1QxBcynn2d5YmDX4MGjlZvy2MRBDRNHLJ8VI6l6+9FUiyTFNJ0IveOSP0bcXgVDPRcfGqA0pjaqUpfVg==
slash@^3.0.0:
version "3.0.0"
resolved "https://registry.yarnpkg.com/slash/-/slash-3.0.0.tgz#6539be870c165adbd5240220dbe361f1bc4d4634"
@@ -5790,10 +5867,14 @@ [email protected], vscode-debugprotocol@^1.35.0:
resolved "https://registry.yarnpkg.com/vscode-debugprotocol/-/vscode-debugprotocol-1.38.0.tgz#7a9bcd457e6642f48fabef114c0fa1c25a2fb1e7"
integrity sha512-oam9iSjNfXSn71a8bmNsXv8k/rIKSOcllIPrFnNgxd1EMBpfnum+gb7lmRpcH0zSjGb+OH8Ncn8B5tv8srWbNQ==
vscode-extension-telemetry@^0.4.4:
version "0.4.4"
resolved "https://registry.yarnpkg.com/vscode-extension-telemetry/-/vscode-extension-telemetry-0.4.4.tgz#e5d5ac90ea7d07ec054f5310893d95d43d5984b0"
integrity sha512-LzirNf2GnykXCAqqWrvj+snYqgkPVyjwM72tYOHgcZiG/ZRuNjmqlgvs+SomEJdmD8cutduitPmhoyIuzOrVfA==
vscode-dts@^0.3.2:
version "0.3.3"
resolved "https://registry.yarnpkg.com/vscode-dts/-/vscode-dts-0.3.3.tgz#e5ef3afe76182875b252cca7f449938e4a0bf28a"
integrity sha512-JfOsWL0NvfVw0UF9bcTjlv1Onz3Ted7cgpPWKWMnHGB+72t/tn8WFDeKLZO42l2k9KJq/NGS9rFC5gZbyI4FTg==
dependencies:
minimist "^1.2.0"
prompts "^2.1.0"
rimraf "^3.0.0"
vscode-jsonrpc@^4.0.0:
version "4.0.0"