Skip to content

Commit 364574a

Browse files
authored
Merge branch 'main' into dev/carsonradtke/bifurcate-far-requests-by-sender
2 parents 9309198 + ddb5445 commit 364574a

18 files changed

Lines changed: 1117 additions & 14 deletions

.github/workflows/job-compile-and-test.yml

Lines changed: 12 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -66,6 +66,11 @@ jobs:
6666
# run: yarn test --scenario=MultirootDeadlockTest
6767
# working-directory: Extension
6868

69+
# - name: Run E2E IntelliSense features tests
70+
# if: ${{ inputs.platform == 'windows' }}
71+
# run: yarn test --scenario=RunWithoutDebugging
72+
# working-directory: Extension
73+
6974
# NOTE: For mac/linux run the tests with xvfb-action for UI support.
7075
# Another way to start xvfb https://github.com/microsoft/vscode-test/blob/master/sample/azure-pipelines.yml
7176

@@ -83,3 +88,10 @@ jobs:
8388
# run: yarn test --scenario=MultirootDeadlockTest
8489
# working-directory: Extension
8590

91+
# - name: Run E2E IntelliSense features tests (xvfb)
92+
# if: ${{ inputs.platform == 'mac' || inputs.platform == 'linux' }}
93+
# uses: coactions/setup-xvfb@v1
94+
# with:
95+
# run: yarn test --scenario=RunWithoutDebugging
96+
# working-directory: Extension
97+
Lines changed: 113 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,113 @@
1+
/* --------------------------------------------------------------------------------------------
2+
* Copyright (c) Microsoft Corporation. All Rights Reserved.
3+
* See 'LICENSE' in the project root for license information.
4+
* ------------------------------------------------------------------------------------------ */
5+
6+
import { cp, readdir, rm, stat } from 'node:fs/promises';
7+
import { homedir } from 'node:os';
8+
import { join } from 'node:path';
9+
import { $args, $root, green, heading, note } from './common';
10+
11+
const extensionPrefix = 'ms-vscode.cpptools-';
12+
const foldersToCopy = ['bin', 'debugAdapters', 'LLVM'] as const;
13+
14+
type InstalledExtension = {
15+
path: string;
16+
version: number[];
17+
modified: number;
18+
};
19+
20+
function compareVersions(left: number[], right: number[]): number {
21+
const maxLength: number = Math.max(left.length, right.length);
22+
for (let i = 0; i < maxLength; i++) {
23+
const diff: number = (left[i] ?? 0) - (right[i] ?? 0);
24+
if (diff !== 0) {
25+
return diff;
26+
}
27+
}
28+
return 0;
29+
}
30+
31+
function tryParseVersion(folderName: string): number[] | undefined {
32+
if (!folderName.startsWith(extensionPrefix)) {
33+
return undefined;
34+
}
35+
36+
const versionText: string | undefined = folderName.substring(extensionPrefix.length).match(/^\d+\.\d+\.\d+/)?.[0];
37+
return versionText?.split('.').map(each => Number(each));
38+
}
39+
40+
async function getInstalledExtensions(root: string): Promise<InstalledExtension[]> {
41+
try {
42+
const entries = await readdir(root, { withFileTypes: true });
43+
const candidates: Promise<InstalledExtension | undefined>[] = entries.map(async (entry) => {
44+
if (!entry.isDirectory()) {
45+
return undefined;
46+
}
47+
48+
const version: number[] | undefined = tryParseVersion(entry.name);
49+
if (!version) {
50+
return undefined;
51+
}
52+
53+
const extensionPath: string = join(root, entry.name);
54+
for (const folder of foldersToCopy) {
55+
const info = await stat(join(extensionPath, folder)).catch(() => undefined);
56+
if (!info?.isDirectory()) {
57+
return undefined;
58+
}
59+
}
60+
61+
const info = await stat(extensionPath);
62+
return {
63+
path: extensionPath,
64+
version,
65+
modified: info.mtimeMs
66+
};
67+
});
68+
69+
const found = await Promise.all(candidates);
70+
return found.filter((entry): entry is InstalledExtension => entry !== undefined);
71+
} catch {
72+
return [];
73+
}
74+
}
75+
76+
async function findLatestInstalledExtension(providedPath?: string): Promise<string> {
77+
if (providedPath) {
78+
return providedPath;
79+
}
80+
81+
const searchRoots: string[] = [
82+
join(homedir(), '.vscode', 'extensions'),
83+
join(homedir(), '.vscode-insiders', 'extensions'),
84+
join(homedir(), '.vscode-server', 'extensions'),
85+
join(homedir(), '.vscode-server-insiders', 'extensions')
86+
];
87+
88+
const installed: InstalledExtension[] = (await Promise.all(searchRoots.map(each => getInstalledExtensions(each)))).flat();
89+
if (!installed.length) {
90+
throw new Error(`Unable to find an installed C/C++ extension under ${searchRoots.join(' or ')}.`);
91+
}
92+
93+
installed.sort((left, right) => compareVersions(right.version, left.version) || right.modified - left.modified);
94+
return installed[0].path;
95+
}
96+
97+
export async function main(sourcePath = $args[0]) {
98+
console.log(heading('Copy installed extension binaries'));
99+
100+
const installedExtensionPath: string = await findLatestInstalledExtension(sourcePath);
101+
note(`Using installed extension at ${installedExtensionPath}`);
102+
103+
for (const folder of foldersToCopy) {
104+
const source: string = join(installedExtensionPath, folder);
105+
const destination: string = join($root, folder);
106+
107+
console.log(`Copying ${green(folder)} from ${source}`);
108+
await rm(destination, { recursive: true, force: true });
109+
await cp(source, destination, { recursive: true, force: true });
110+
}
111+
112+
note(`Copied installed binaries into ${$root}`);
113+
}

Extension/.vscode/launch.json

Lines changed: 4 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -97,6 +97,10 @@
9797
"label": "MultirootDeadlockTest ",
9898
"value": "${workspaceFolder}/test/scenarios/MultirootDeadlockTest/assets/test.code-workspace"
9999
},
100+
{
101+
"label": "RunWithoutDebugging ",
102+
"value": "${workspaceFolder}/test/scenarios/RunWithoutDebugging/assets/"
103+
},
100104
{
101105
"label": "SimpleCppProject ",
102106
"value": "${workspaceFolder}/test/scenarios/SimpleCppProject/assets/simpleCppProject.code-workspace"

Extension/package.json

Lines changed: 7 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -1703,6 +1703,12 @@
17031703
"description": "%c_cpp.configuration.doxygen.generateOnType.description%",
17041704
"scope": "resource"
17051705
},
1706+
"C_Cpp.doxygen.generateOnCodeAction": {
1707+
"type": "boolean",
1708+
"default": true,
1709+
"description": "%c_cpp.configuration.doxygen.generateOnCodeAction.description%",
1710+
"scope": "resource"
1711+
},
17061712
"C_Cpp.doxygen.generatedStyle": {
17071713
"type": "string",
17081714
"enum": [
@@ -6799,6 +6805,7 @@
67996805
"generate-native-strings": "ts-node -T ./.scripts/generateNativeStrings.ts",
68006806
"generate-options-schema": "ts-node -T ./.scripts/generateOptionsSchema.ts",
68016807
"copy-walkthrough-media": "ts-node -T ./.scripts/copyWalkthruMedia.ts",
6808+
"copy-extension-binaries": "ts-node -T ./.scripts/copyExtensionBinaries.ts",
68026809
"translations-export": "yarn install && yarn prep && yarn generate-native-strings && gulp translations-export",
68036810
"translations-generate": "gulp translations-generate",
68046811
"translations-import": "gulp translations-import",

Extension/package.nls.json

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -580,6 +580,7 @@
580580
]
581581
},
582582
"c_cpp.configuration.doxygen.generateOnType.description": "Controls whether to automatically insert the Doxygen comment after typing the chosen comment style.",
583+
"c_cpp.configuration.doxygen.generateOnCodeAction.description": "Controls whether the code action to generate a Doxygen comment is enabled.",
583584
"c_cpp.configuration.doxygen.generatedStyle.description": "The string of characters used as the starting line of the Doxygen comment.",
584585
"c_cpp.configuration.doxygen.sectionTags.description": "Select the Doxygen section tags that you would like to appear on hover in the tooltip area when the 'Simplify Structured Comments' setting is enabled. ",
585586
"c_cpp.configuration.commentContinuationPatterns.items.anyof.string.markdownDescription": {

Extension/src/Debugger/configurationProvider.ts

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -147,12 +147,14 @@ export class DebugConfigurationProvider implements vscode.DebugConfigurationProv
147147
Telemetry.logDebuggerEvent(DebuggerEvent.debugPanel, { "debugType": DebugType.debug, "configSource": folder ? ConfigSource.workspaceFolder : ConfigSource.singleFile, "configMode": ConfigMode.noLaunchConfig, "cancelled": "true", "succeeded": "true" });
148148
return undefined; // aborts debugging silently
149149
} else {
150+
const noDebug = config.noDebug ?? false; // Preserve the noDebug value from the config if it exists.
150151
// Currently, we expect only one debug config to be selected.
151152
console.assert(configs.length === 1, "More than one debug config is selected.");
152153
config = configs[0];
153154
// Keep track of the entry point where the debug config has been selected, for telemetry purposes.
154155
config.debuggerEvent = DebuggerEvent.debugPanel;
155156
config.configSource = folder ? ConfigSource.workspaceFolder : ConfigSource.singleFile;
157+
config.noDebug = noDebug;
156158
}
157159
}
158160

Extension/src/Debugger/configurations.ts

Lines changed: 3 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -97,7 +97,7 @@ function createLaunchString(name: string, type: string, executable: string): str
9797
"stopAtEntry": false,
9898
"cwd": "$\{fileDirname\}",
9999
"environment": [],
100-
${ type === "cppdbg" ? `"externalConsole": false` : `"console": "externalTerminal"` }
100+
${type === "cppdbg" ? `"externalConsole": false` : `"console": "internalConsole"`}
101101
`;
102102
}
103103

@@ -164,7 +164,7 @@ export class MIConfigurations extends Configuration {
164164
\t${indentJsonString(createLaunchString(name, this.miDebugger, this.executable))},
165165
\t"MIMode": "${this.MIMode}"{0}{1}
166166
}`, [this.miDebugger === "cppdbg" && os.platform() === "win32" ? `,${os.EOL}\t"miDebuggerPath": "/path/to/gdb"` : "",
167-
this.additionalProperties ? `,${os.EOL}\t${indentJsonString(this.additionalProperties)}` : ""]);
167+
this.additionalProperties ? `,${os.EOL}\t${indentJsonString(this.additionalProperties)}` : ""]);
168168

169169
return {
170170
"label": configPrefix + name,
@@ -182,7 +182,7 @@ export class MIConfigurations extends Configuration {
182182
\t${indentJsonString(createAttachString(name, this.miDebugger, this.executable))}
183183
\t"MIMode": "${this.MIMode}"{0}{1}
184184
}`, [this.miDebugger === "cppdbg" && os.platform() === "win32" ? `,${os.EOL}\t"miDebuggerPath": "/path/to/gdb"` : "",
185-
this.additionalProperties ? `,${os.EOL}\t${indentJsonString(this.additionalProperties)}` : ""]);
185+
this.additionalProperties ? `,${os.EOL}\t${indentJsonString(this.additionalProperties)}` : ""]);
186186

187187
return {
188188
"label": configPrefix + name,

Extension/src/Debugger/debugAdapterDescriptorFactory.ts

Lines changed: 44 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -7,11 +7,13 @@ import * as os from 'os';
77
import * as path from 'path';
88
import * as vscode from "vscode";
99
import * as nls from 'vscode-nls';
10+
import { getOutputChannel } from '../logger';
11+
import { RunWithoutDebuggingAdapter } from './runWithoutDebuggingAdapter';
1012

1113
nls.config({ messageFormat: nls.MessageFormat.bundle, bundleFormat: nls.BundleFormat.standalone })();
1214
const localize: nls.LocalizeFunc = nls.loadMessageBundle();
1315

14-
// Registers DebugAdapterDescriptorFactory for `cppdbg` and `cppvsdbg`. If it is not ready, it will prompt a wait for the download dialog.
16+
// Registers DebugAdapterDescriptorFactory for `cppdbg` and `cppvsdbg`.
1517
// NOTE: This file is not automatically tested.
1618

1719
abstract class AbstractDebugAdapterDescriptorFactory implements vscode.DebugAdapterDescriptorFactory {
@@ -26,8 +28,15 @@ abstract class AbstractDebugAdapterDescriptorFactory implements vscode.DebugAdap
2628
}
2729

2830
export class CppdbgDebugAdapterDescriptorFactory extends AbstractDebugAdapterDescriptorFactory {
31+
async createDebugAdapterDescriptor(session: vscode.DebugSession, _executable?: vscode.DebugAdapterExecutable): Promise<vscode.DebugAdapterDescriptor> {
32+
if (session.configuration.noDebug) {
33+
if (noDebugSupported(session.configuration)) {
34+
return new vscode.DebugAdapterInlineImplementation(new RunWithoutDebuggingAdapter());
35+
}
36+
// If the configuration is not supported, gracefully fall back to a regular debug session and log a message to the user.
37+
logReasonForNoDebugNotSupported(session.configuration);
38+
}
2939

30-
async createDebugAdapterDescriptor(_session: vscode.DebugSession, _executable?: vscode.DebugAdapterExecutable): Promise<vscode.DebugAdapterDescriptor> {
3140
const adapter: string = "./debugAdapters/bin/OpenDebugAD7" + (os.platform() === 'win32' ? ".exe" : "");
3241

3342
const command: string = path.join(this.context.extensionPath, adapter);
@@ -37,8 +46,15 @@ export class CppdbgDebugAdapterDescriptorFactory extends AbstractDebugAdapterDes
3746
}
3847

3948
export class CppvsdbgDebugAdapterDescriptorFactory extends AbstractDebugAdapterDescriptorFactory {
49+
async createDebugAdapterDescriptor(session: vscode.DebugSession, _executable?: vscode.DebugAdapterExecutable): Promise<vscode.DebugAdapterDescriptor | null> {
50+
if (session.configuration.noDebug) {
51+
if (noDebugSupported(session.configuration)) {
52+
return new vscode.DebugAdapterInlineImplementation(new RunWithoutDebuggingAdapter());
53+
}
54+
// If the configuration is not supported, gracefully fall back to a regular debug session and log a message to the user.
55+
logReasonForNoDebugNotSupported(session.configuration);
56+
}
4057

41-
async createDebugAdapterDescriptor(_session: vscode.DebugSession, _executable?: vscode.DebugAdapterExecutable): Promise<vscode.DebugAdapterDescriptor | null> {
4258
if (os.platform() !== 'win32') {
4359
void vscode.window.showErrorMessage(localize("debugger.not.available", "Debugger type '{0}' is not available for non-Windows machines.", "cppvsdbg"));
4460
return null;
@@ -50,3 +66,28 @@ export class CppvsdbgDebugAdapterDescriptorFactory extends AbstractDebugAdapterD
5066
}
5167
}
5268
}
69+
70+
function noDebugSupported(configuration: vscode.DebugConfiguration): boolean {
71+
// Don't attempt to start a noDebug session if the configuration has any of these properties, which require a debug adapter to function.
72+
return configuration.request === 'launch' && !configuration.pipeTransport && !configuration.debugServerPath && !configuration.miDebuggerServerAddress && !configuration.coreDumpPath;
73+
}
74+
75+
function logReasonForNoDebugNotSupported(configuration: vscode.DebugConfiguration): void {
76+
const outputChannel = getOutputChannel();
77+
if (configuration.request !== 'launch') {
78+
outputChannel.appendLine(localize("debugger.noDebug.requestType.not.supported", "Run Without Debugging is only supported for launch configurations."));
79+
}
80+
if (configuration.pipeTransport) {
81+
outputChannel.appendLine(localize("debugger.noDebug.pipeTransport.not.supported", "Run Without Debugging is not supported for configurations with 'pipeTransport' set."));
82+
}
83+
if (configuration.debugServerPath) {
84+
outputChannel.appendLine(localize("debugger.noDebug.debugServerPath.not.supported", "Run Without Debugging is not supported for configurations with 'debugServerPath' set."));
85+
}
86+
if (configuration.miDebuggerServerAddress) {
87+
outputChannel.appendLine(localize("debugger.noDebug.miDebuggerServerAddress.not.supported", "Run Without Debugging is not supported for configurations with 'miDebuggerServerAddress' set."));
88+
}
89+
if (configuration.coreDumpPath) {
90+
outputChannel.appendLine(localize("debugger.noDebug.coreDumpPath.not.supported", "Run Without Debugging is not supported for configurations with 'coreDumpPath' set."));
91+
}
92+
outputChannel.show(true);
93+
}

0 commit comments

Comments
 (0)