diff --git a/Build/package/jobs_package_vsix.yml b/Build/package/jobs_package_vsix.yml index 88b8e71d6..e5f6f71a8 100644 --- a/Build/package/jobs_package_vsix.yml +++ b/Build/package/jobs_package_vsix.yml @@ -5,6 +5,9 @@ parameters: - name: srcDir type: string default: '' +- name: signType + type: string + default: 'real' jobs: - job: package @@ -12,6 +15,15 @@ jobs: timeoutInMinutes: 30 cancelTimeoutInMinutes: 1 templateContext: + mb: # Enable the MicroBuild Signing toolset + signing: + enabled: true + signType: ${{ parameters.signType }} + zipSources: false + ${{ if eq(parameters.signType, 'real') }}: + signWithProd: true + featureFlags: + autoBaseline: false outputs: - output: pipelineArtifact displayName: '${{ parameters.vsixName }}' diff --git a/Extension/.vscodeignore b/Extension/.vscodeignore index 427193a59..55474d9bf 100644 --- a/Extension/.vscodeignore +++ b/Extension/.vscodeignore @@ -29,25 +29,25 @@ jobs/** cgmanifest.json # ignore development files -tsconfig.json -test.tsconfig.json -ui.tsconfig.json -tslint.json +.eslintignore .eslintrc.js -webpack.config.js -tscCompileList.txt -gulpfile.js .gitattributes .gitignore +gulpfile.js +localized_string_ids.h +readme.developer.md +Reinstalling the Extension.md +test.tsconfig.json +translations_auto_pr.js +tsconfig.json +tslint.json +tscCompileList.txt +ui.tsconfig.json +webpack.config.js CMakeLists.txt debugAdapters/install.lock* typings/** **/*.map -import_edge_strings.js -localized_string_ids.h -translations_auto_pr.js -readme.developer.md -Reinstalling the Extension.md *.d.ts # ignore i18n language files diff --git a/Extension/CHANGELOG.md b/Extension/CHANGELOG.md index a870b73ce..3190ca284 100644 --- a/Extension/CHANGELOG.md +++ b/Extension/CHANGELOG.md @@ -1,5 +1,11 @@ # C/C++ for Visual Studio Code Changelog +## Version 1.28.1: October 6, 2025 +### Bug Fixes +* Fix extension activation getting stuck when certain SSH config files are processed (by the SSH targets view feature). [#13966](https://github.com/microsoft/vscode-cpptools/issues/13966) +* Fix document symbols randomly showing previous versions of symbols after they are modified. [#13967](https://github.com/microsoft/vscode-cpptools/issues/13967) +* A potential fix for a crash. + ## Version 1.28.0: September 25, 2025 ### Enhancements * Add IntelliSense support for c23 `bool`, `true`, and `false`. [#13737](https://github.com/microsoft/vscode-cpptools/issues/13737) diff --git a/Extension/package.json b/Extension/package.json index b9a668062..90c7396b4 100644 --- a/Extension/package.json +++ b/Extension/package.json @@ -2,7 +2,7 @@ "name": "cpptools", "displayName": "C/C++", "description": "C/C++ IntelliSense, debugging, and code browsing.", - "version": "1.28.0-main", + "version": "1.28.1-main", "publisher": "ms-vscode", "icon": "LanguageCCPP_color_128x.png", "readme": "README.md", diff --git a/Extension/src/LanguageServer/client.ts b/Extension/src/LanguageServer/client.ts index 40611c728..246a74c6b 100644 --- a/Extension/src/LanguageServer/client.ts +++ b/Extension/src/LanguageServer/client.ts @@ -1391,6 +1391,7 @@ export class DefaultClient implements Client { } this.copilotCompletionProvider = CopilotCompletionContextProvider.Create(); + util.setProgress(util.getProgressCopilotSuccess()); this.disposables.push(this.copilotCompletionProvider); // Listen for messages from the language server. diff --git a/Extension/src/SSH/sshHosts.ts b/Extension/src/SSH/sshHosts.ts index 97d3b625c..98c4d9308 100644 --- a/Extension/src/SSH/sshHosts.ts +++ b/Extension/src/SSH/sshHosts.ts @@ -99,28 +99,64 @@ export async function getSshConfiguration(configurationPath: string, resolveIncl return config; } -async function resolveConfigIncludes(config: Configuration, configPath: string): Promise { - for (const entry of config) { - if (isDirective(entry) && entry.param === 'Include') { - let includePath: string = resolveHome(entry.value); - if (isWindows && !!includePath.match(/^\/[a-z]:/i)) { - includePath = includePath.substr(1); - } - - if (!path.isAbsolute(includePath)) { - includePath = path.resolve(path.dirname(configPath), includePath); - } - - const pathsToGetFilesFrom: string[] = await globAsync(includePath); +function getProcessedPathKey(filePath: string): string { + const absolutePath: string = path.resolve(filePath); + const normalizedPath: string = path.normalize(absolutePath); + return isWindows ? normalizedPath.toLowerCase() : normalizedPath; +} - for (const filePath of pathsToGetFilesFrom) { - await getIncludedConfigFile(config, filePath); +async function resolveConfigIncludes( + config: Configuration, + configPath: string, + processedIncludePaths?: Set, + processedIncludeEntries?: WeakSet +): Promise { + processedIncludePaths = processedIncludePaths ?? new Set(); + processedIncludeEntries = processedIncludeEntries ?? new WeakSet(); + const configKey: string = getProcessedPathKey(configPath); + if (processedIncludePaths.has(configKey)) { + return; + } + processedIncludePaths.add(configKey); + try { + for (const entry of config) { + if (isDirective(entry) && entry.param === 'Include') { + // Prevent duplicate expansion of the same Include directive within a single resolution pass. + if (processedIncludeEntries.has(entry)) { + continue; + } + processedIncludeEntries.add(entry); + let includePath: string = resolveHome(entry.value); + if (isWindows && !!includePath.match(/^\/[a-z]:/i)) { + includePath = includePath.slice(1); + } + + if (!path.isAbsolute(includePath)) { + includePath = path.resolve(path.dirname(configPath), includePath); + } + + const pathsToGetFilesFrom: string[] = await globAsync(includePath); + + for (const filePath of pathsToGetFilesFrom) { + const includeKey: string = getProcessedPathKey(filePath); + if (processedIncludePaths.has(includeKey)) { + continue; + } + await getIncludedConfigFile(config, filePath, processedIncludePaths, processedIncludeEntries); + } } } + } finally { + processedIncludePaths.delete(configKey); } } -async function getIncludedConfigFile(config: Configuration, includePath: string): Promise { +async function getIncludedConfigFile( + config: Configuration, + includePath: string, + processedIncludePaths: Set, + processedIncludeEntries: WeakSet +): Promise { let includedContents: string; try { includedContents = (await fs.readFile(includePath)).toString(); @@ -136,6 +172,7 @@ async function getIncludedConfigFile(config: Configuration, includePath: string) getSshChannel().appendLine(localize("failed.to.parse.SSH.config", "Failed to parse SSH configuration file {0}: {1}", includePath, (err as Error).message)); return; } + await resolveConfigIncludes(parsedIncludedContents, includePath, processedIncludePaths, processedIncludeEntries); config.push(...parsedIncludedContents); } diff --git a/Extension/src/common.ts b/Extension/src/common.ts index c9cf94954..e726669a6 100644 --- a/Extension/src/common.ts +++ b/Extension/src/common.ts @@ -247,19 +247,22 @@ export function displayExtensionNotReadyPrompt(): void { // Users start with a progress of 0 and it increases as they get further along in using the tool. // This eliminates noise/problems due to re-installs, terminated installs that don't send errors, // errors followed by workarounds that lead to success, etc. -const progressInstallSuccess: number = 100; +const progressDebuggerStarted: number = 50; +const progressDebuggerSuccess: number = 100; const progressExecutableStarted: number = 150; +const progressCopilotSuccess: number = 180; const progressExecutableSuccess: number = 200; const progressParseRootSuccess: number = 300; +const progressLanguageServiceDisabled: number = 400; const progressIntelliSenseNoSquiggles: number = 1000; // Might add more IntelliSense progress measurements later. -// IntelliSense progress is separate from the install progress, because parse root can occur afterwards. +// IntelliSense progress is separate from the activation progress, because parse root can occur afterwards. -const installProgressStr: string = "CPP." + packageJson.version + ".Progress"; +const activationProgressStr: string = "CPP." + packageJson.version + ".Progress"; const intelliSenseProgressStr: string = "CPP." + packageJson.version + ".IntelliSenseProgress"; export function getProgress(): number { - return extensionContext ? extensionContext.globalState.get(installProgressStr, -1) : -1; + return extensionContext ? extensionContext.globalState.get(activationProgressStr, -1) : -1; } export function getIntelliSenseProgress(): number { @@ -268,15 +271,18 @@ export function getIntelliSenseProgress(): number { export function setProgress(progress: number): void { if (extensionContext && getProgress() < progress) { - void extensionContext.globalState.update(installProgressStr, progress); + void extensionContext.globalState.update(activationProgressStr, progress); const telemetryProperties: Record = {}; let progressName: string | undefined; switch (progress) { - case 0: progressName = "install started"; break; - case progressInstallSuccess: progressName = "install succeeded"; break; + case 0: progressName = "activation started"; break; + case progressDebuggerStarted: progressName = "debugger started"; break; + case progressDebuggerSuccess: progressName = "debugger succeeded"; break; case progressExecutableStarted: progressName = "executable started"; break; + case progressCopilotSuccess: progressName = "copilot succeeded"; break; case progressExecutableSuccess: progressName = "executable succeeded"; break; case progressParseRootSuccess: progressName = "parse root succeeded"; break; + case progressLanguageServiceDisabled: progressName = "language service disabled"; break; } if (progressName) { telemetryProperties.progress = progressName; @@ -300,10 +306,13 @@ export function setIntelliSenseProgress(progress: number): void { } } -export function getProgressInstallSuccess(): number { return progressInstallSuccess; } // Download/install was successful (i.e. not blocked by component acquisition). +export function getProgressDebuggerStarted(): number { return progressDebuggerStarted; } // Debugger initialization was started. +export function getProgressDebuggerSuccess(): number { return progressDebuggerSuccess; } // Debugger was successfully initialized. export function getProgressExecutableStarted(): number { return progressExecutableStarted; } // The extension was activated and starting the executable was attempted. +export function getProgressCopilotSuccess(): number { return progressCopilotSuccess; } // Copilot activation was successful. export function getProgressExecutableSuccess(): number { return progressExecutableSuccess; } // Starting the exe was successful (i.e. not blocked by 32-bit or glibc < 2.18 on Linux) export function getProgressParseRootSuccess(): number { return progressParseRootSuccess; } // Parse root was successful (i.e. not blocked by processing taking too long). +export function getProgressLanguageServiceDisabled(): number { return progressLanguageServiceDisabled; } // The user disabled the language service. export function getProgressIntelliSenseNoSquiggles(): number { return progressIntelliSenseNoSquiggles; } // IntelliSense was successful and the user got no squiggles. export function isUri(input: any): input is vscode.Uri { diff --git a/Extension/src/main.ts b/Extension/src/main.ts index be5f02274..ac9030d55 100644 --- a/Extension/src/main.ts +++ b/Extension/src/main.ts @@ -42,8 +42,8 @@ export async function activate(context: vscode.ExtensionContext): Promise