diff --git a/Extension/CHANGELOG.md b/Extension/CHANGELOG.md index 8891897fa2..d00d6843ef 100644 --- a/Extension/CHANGELOG.md +++ b/Extension/CHANGELOG.md @@ -1,11 +1,29 @@ # C/C++ for Visual Studio Code Changelog +## Version 1.23.0: October 29, 2024 +### Enhancements +* Update to clang-format and clang-tidy 19.1.2. [#12824](https://github.com/microsoft/vscode-cpptools/issues/12824) +* Enable `#cpp` with GitHub Copilot chat without `C_Cpp.experimentalFeatures` enabled. [PR #12898](https://github.com/microsoft/vscode-cpptools/pull/12898) + +### Bug Fixes +* Fix some translation issues. [#7824](https://github.com/microsoft/vscode-cpptools/issues/7824), [#12439](https://github.com/microsoft/vscode-cpptools/issues/12439), [#12440](https://github.com/microsoft/vscode-cpptools/issues/12440), [#12441](https://github.com/microsoft/vscode-cpptools/issues/12441) +* Fix a bug with 'Select IntelliSense Configuration'. [#12705](https://github.com/microsoft/vscode-cpptools/issues/12705) +* Fix newlines being removed from hover markdown code blocks. [#12794](https://github.com/microsoft/vscode-cpptools/issues/12794) +* Fix clang-format using `-` instead of `--` args. [#12819](https://github.com/microsoft/vscode-cpptools/issues/12819) +* Fix processing of `compile_commands.json` generated by the clang `-MJ` option. [#12837](https://github.com/microsoft/vscode-cpptools/issues/12837) +* Fix handling of `-I` and `-isystem` with the same path. [#12842](https://github.com/microsoft/vscode-cpptools/issues/12842) +* Fix stale colorization due to delays in updating the open file version. [PR #12851](https://github.com/microsoft/vscode-cpptools/pull/12851) +* Fix redundant progressive squiggle updates. [PR #12876](https://github.com/microsoft/vscode-cpptools/pull/12876) +* Fix inactive regions with multi-byte UTF-8 characters. [#12879](https://github.com/microsoft/vscode-cpptools/issues/12879) +* Fix some duplicate requests potentially not getting discarded. +* Fix a random crash in `start_process_and_wait_for_exit`. + ## Version 1.22.10: October 21, 2024 ### Bug Fixes * Fix the 'Extract to Function' feature not working. * Fix the 'Go to Next/Prev Preprocessor Conditional' feature not working. -## Version 1.22.9: October 10, 2024 +## Version 1.22.9: October 14, 2024 ### Performance Improvements * Initialization performance improvements. [#12030](https://github.com/microsoft/vscode-cpptools/issues/12030) - Some processing is parallelized and started earlier (populating the filename cache, discovering files). [#11954](https://github.com/microsoft/vscode-cpptools/issues/11954), [#12169](https://github.com/microsoft/vscode-cpptools/issues/12169) diff --git a/Extension/package.json b/Extension/package.json index 182e2ccfc6..f183a39f5c 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.22.9-main", + "version": "1.23.0-main", "publisher": "ms-vscode", "icon": "LanguageCCPP_color_128x.png", "readme": "README.md", @@ -38,8 +38,7 @@ "Snippets" ], "enabledApiProposals": [ - "terminalDataWriteEvent", - "lmTools" + "terminalDataWriteEvent" ], "capabilities": { "untrustedWorkspaces": { @@ -6512,7 +6511,7 @@ "translations-generate": "set NODE_OPTIONS=--no-experimental-fetch && gulp translations-generate", "translations-import": "gulp translations-import", "import-edge-strings": "ts-node -T ./.scripts/import_edge_strings.ts", - "prep:dts": "yarn verify dts --quiet || (npx vscode-dts dev && npx vscode-dts main)", + "prep:dts": "yarn verify dts --quiet || (npx @vscode/dts dev && npx @vscode/dts main)", "build": "yarn prep:dts && echo [Building TypeScript code] && tsc --build tsconfig.json" }, "devDependencies": { diff --git a/Extension/src/Debugger/configurationProvider.ts b/Extension/src/Debugger/configurationProvider.ts index 2b9ce38ac2..067983feac 100644 --- a/Extension/src/Debugger/configurationProvider.ts +++ b/Extension/src/Debugger/configurationProvider.ts @@ -331,7 +331,7 @@ export class DebugConfigurationProvider implements vscode.DebugConfigurationProv // Run deploy steps if (config.deploySteps && config.deploySteps.length !== 0) { - const codeVersion: number[] = vscode.version.split('.').map(num => parseInt(num, undefined)); + const codeVersion: number[] = util.getVsCodeVersion(); if ((util.isNumber(codeVersion[0]) && codeVersion[0] < 1) || (util.isNumber(codeVersion[0]) && codeVersion[0] === 1 && util.isNumber(codeVersion[1]) && codeVersion[1] < 69)) { void logger.getOutputChannelLogger().showErrorMessage(localize("vs.code.1.69+.required", "'deploySteps' require VS Code 1.69+.")); return undefined; diff --git a/Extension/src/LanguageServer/client.ts b/Extension/src/LanguageServer/client.ts index 04dcaba701..e46743815e 100644 --- a/Extension/src/LanguageServer/client.ts +++ b/Extension/src/LanguageServer/client.ts @@ -1046,7 +1046,7 @@ export class DefaultClient implements Client { if (index === paths.length - 1) { action = "disable"; settings.defaultCompilerPath = ""; - await this.configuration.updateCompilerPathIfSet(settings.defaultCompilerPath); + await this.configuration.updateCompilerPathIfSet(""); configurationSelected = true; await this.showPrompt(sender); return ui.ShowConfigureIntelliSenseButton(false, this, ConfigurationType.CompilerPath, "disablePrompt"); @@ -1066,7 +1066,7 @@ export class DefaultClient implements Client { configurationSelected = true; action = "compiler browsed"; settings.defaultCompilerPath = result[0].fsPath; - await this.configuration.updateCompilerPathIfSet(settings.defaultCompilerPath); + await this.configuration.updateCompilerPathIfSet(result[0].fsPath); void SessionState.trustedCompilerFound.set(true); } else { configurationSelected = true; @@ -1084,8 +1084,9 @@ export class DefaultClient implements Client { return ui.ShowConfigureIntelliSenseButton(false, this, ConfigurationType.CompileCommands, showButtonSender); } else { action = "select compiler"; - settings.defaultCompilerPath = util.isCl(paths[index]) ? "cl.exe" : paths[index]; - await this.configuration.updateCompilerPathIfSet(settings.defaultCompilerPath); + const newCompiler: string = util.isCl(paths[index]) ? "cl.exe" : paths[index]; + settings.defaultCompilerPath = newCompiler; + await this.configuration.updateCompilerPathIfSet(newCompiler); void SessionState.trustedCompilerFound.set(true); } } @@ -2387,7 +2388,9 @@ export class DefaultClient implements Client { } this.updateInactiveRegions(intelliSenseResult.uri, intelliSenseResult.inactiveRegions, intelliSenseResult.clearExistingInactiveRegions, intelliSenseResult.isCompletePass); - this.updateSquiggles(intelliSenseResult.uri, intelliSenseResult.diagnostics, intelliSenseResult.clearExistingDiagnostics); + if (intelliSenseResult.clearExistingDiagnostics || intelliSenseResult.diagnostics.length > 0) { + this.updateSquiggles(intelliSenseResult.uri, intelliSenseResult.diagnostics, intelliSenseResult.clearExistingDiagnostics); + } } private updateSquiggles(uriString: string, diagnostics: IntelliSenseDiagnostic[], startNewSet: boolean): void { diff --git a/Extension/src/LanguageServer/configurations.ts b/Extension/src/LanguageServer/configurations.ts index 3a02622b0a..3339514cc2 100644 --- a/Extension/src/LanguageServer/configurations.ts +++ b/Extension/src/LanguageServer/configurations.ts @@ -1948,7 +1948,7 @@ export class CppProperties { compilerPath = checkPathExists.path; } if (!compilerPathExists) { - compilerMessage = localize('cannot.find2', "Cannot find \"{0}\".", compilerPath); + compilerMessage = localize('cannot.find', "Cannot find: {0}", compilerPath); newSquiggleMetrics.PathNonExistent++; } if (compilerMessage) { @@ -1975,7 +1975,7 @@ export class CppProperties { dotConfigPath = checkPathExists.path; } if (!dotConfigPathExists) { - dotConfigMessage = localize('cannot.find2', "Cannot find \"{0}\".", dotConfigPath); + dotConfigMessage = localize('cannot.find', "Cannot find: {0}", dotConfigPath); newSquiggleMetrics.PathNonExistent++; } else if (dotConfigPath && !util.checkFileExistsSync(dotConfigPath)) { dotConfigMessage = localize("path.is.not.a.file", "Path is not a file: {0}", dotConfigPath); @@ -2083,7 +2083,7 @@ export class CppProperties { } else { badPath = `"${expandedPaths[0]}"`; } - message = localize('cannot.find2', "Cannot find {0}", badPath); + message = localize('cannot.find', "Cannot find: {0}", badPath); newSquiggleMetrics.PathNonExistent++; } else { // Check for file versus path mismatches. @@ -2141,7 +2141,7 @@ export class CppProperties { endOffset = curOffset + curMatch.length; let message: string; if (!pathExists) { - message = localize('cannot.find2', "Cannot find \"{0}\".", expandedPaths[0]); + message = localize('cannot.find', "Cannot find: {0}", expandedPaths[0]); newSquiggleMetrics.PathNonExistent++; const diagnostic: vscode.Diagnostic = new vscode.Diagnostic( new vscode.Range(document.positionAt(envTextStartOffSet + curOffset), diff --git a/Extension/src/LanguageServer/editorConfig.ts b/Extension/src/LanguageServer/editorConfig.ts index 1ac8452b46..21a73673c6 100644 --- a/Extension/src/LanguageServer/editorConfig.ts +++ b/Extension/src/LanguageServer/editorConfig.ts @@ -92,7 +92,17 @@ function parseEditorConfigContent(content: string): Record { const [key, ...values] = line.split('='); if (key && values.length > 0) { const trimmedKey = key.trim(); - const value = values.join('=').trim(); + let value: any = values.join('=').trim(); + + // Convert boolean-like and numeric values. + if (value.toLowerCase() === 'true') { + value = true; + } else if (value.toLowerCase() === 'false') { + value = false; + } else if (!isNaN(Number(value))) { + value = Number(value); + } + if (currentSection) { // Ensure the current section is initialized. if (!config[currentSection]) { @@ -114,7 +124,7 @@ function getEditorConfig(filePath: string): any { const rootDir: string = path.parse(currentDir).root; // Traverse from the file's directory to the root directory. - for (;;) { + for (; ;) { const editorConfigPath: string = path.join(currentDir, '.editorconfig'); if (fs.existsSync(editorConfigPath)) { const configFileContent: string = fs.readFileSync(editorConfigPath, 'utf-8'); @@ -139,7 +149,7 @@ function getEditorConfig(filePath: string): any { }); // Check if the current .editorconfig is the root. - if (configData['*']?.root?.toLowerCase() === 'true') { + if (configData['*']?.root) { break; // Stop searching after processing the root = true file. } } diff --git a/Extension/src/LanguageServer/extension.ts b/Extension/src/LanguageServer/extension.ts index c70abfb26b..02dd3e8861 100644 --- a/Extension/src/LanguageServer/extension.ts +++ b/Extension/src/LanguageServer/extension.ts @@ -252,9 +252,15 @@ export async function activate(): Promise { activeDocument = activeEditor.document; } - if (util.extensionContext && new CppSettings().experimentalFeatures) { - const tool = vscode.lm.registerTool('cpptools-lmtool-configuration', new CppConfigurationLanguageModelTool()); - disposables.push(tool); + if (util.extensionContext) { + // lmTools wasn't stabilized until 1.95, but (as of October 2024) + // cpptools can be installed on older versions of VS Code. See + // https://github.com/microsoft/vscode-cpptools/blob/main/Extension/package.json#L14 + const version = util.getVsCodeVersion(); + if (version[0] > 1 || (version[0] === 1 && version[1] >= 95)) { + const tool = vscode.lm.registerTool('cpptools-lmtool-configuration', new CppConfigurationLanguageModelTool()); + disposables.push(tool); + } } await registerRelatedFilesProvider(); diff --git a/Extension/src/LanguageServer/lmTool.ts b/Extension/src/LanguageServer/lmTool.ts index ed5be61a00..c3fad8b6eb 100644 --- a/Extension/src/LanguageServer/lmTool.ts +++ b/Extension/src/LanguageServer/lmTool.ts @@ -51,6 +51,7 @@ export class CppConfigurationLanguageModelTool implements vscode.LanguageModelTo } private async getContext(token: vscode.CancellationToken): Promise { + const telemetryProperties: Record = {}; try { const currentDoc = vscode.window.activeTextEditor?.document; if (!currentDoc || (!util.isCpp(currentDoc) && !util.isHeaderFile(currentDoc.uri))) { @@ -62,28 +63,44 @@ export class CppConfigurationLanguageModelTool implements vscode.LanguageModelTo return 'No configuration information is available for the active document.'; } - telemetry.logLanguageModelToolEvent( - 'cpp', - { - "language": chatContext.language, - "compiler": chatContext.compiler, - "standardVersion": chatContext.standardVersion, - "targetPlatform": chatContext.targetPlatform, - "targetArchitecture": chatContext.targetArchitecture - }); - for (const key in knownValues) { const knownKey = key as keyof ChatContextResult; if (knownValues[knownKey] && chatContext[knownKey]) { - chatContext[knownKey] = knownValues[knownKey][chatContext[knownKey]] || chatContext[knownKey]; + // Clear the value if it's not in the known values. + chatContext[knownKey] = knownValues[knownKey][chatContext[knownKey]] || ""; } } - return `The user is working on a ${chatContext.language} project. The project uses language version ${chatContext.standardVersion}, compiles using the ${chatContext.compiler} compiler, targets the ${chatContext.targetPlatform} platform, and targets the ${chatContext.targetArchitecture} architecture.`; + let contextString = ""; + if (chatContext.language) { + contextString += `The user is working on a ${chatContext.language} project. `; + telemetryProperties["language"] = chatContext.language; + } + if (chatContext.standardVersion) { + contextString += `The project uses language version ${chatContext.standardVersion}. `; + telemetryProperties["standardVersion"] = chatContext.standardVersion; + } + if (chatContext.compiler) { + contextString += `The project compiles using the ${chatContext.compiler} compiler. `; + telemetryProperties["compiler"] = chatContext.compiler; + } + if (chatContext.targetPlatform) { + contextString += `The project targets the ${chatContext.targetPlatform} platform. `; + telemetryProperties["targetPlatform"] = chatContext.targetPlatform; + } + if (chatContext.targetArchitecture) { + contextString += `The project targets the ${chatContext.targetArchitecture} architecture. `; + telemetryProperties["targetArchitecture"] = chatContext.targetArchitecture; + } + + return contextString; } catch { await this.reportError(); + telemetryProperties["error"] = "true"; return ""; + } finally { + telemetry.logLanguageModelToolEvent('cpp', telemetryProperties); } } diff --git a/Extension/src/LanguageServer/protocolFilter.ts b/Extension/src/LanguageServer/protocolFilter.ts index 4dddbbcc22..b292c67b16 100644 --- a/Extension/src/LanguageServer/protocolFilter.ts +++ b/Extension/src/LanguageServer/protocolFilter.ts @@ -8,6 +8,7 @@ import * as path from 'path'; import * as vscode from 'vscode'; import { Middleware } from 'vscode-languageclient'; import * as util from '../common'; +import { logAndReturn } from '../Utility/Async/returns'; import { Client } from './client'; import { clients } from './extension'; import { shouldChangeFromCToCpp } from './utils'; @@ -18,14 +19,8 @@ export const ServerCancelled: number = -32802; let anyFileOpened: boolean = false; export function createProtocolFilter(): Middleware { - // Disabling lint for invoke handlers - const invoke1 = (a: any, next: (a: any) => any): any => clients.ActiveClient.enqueue(() => next(a)); - const invoke2 = (a: any, b: any, next: (a: any, b: any) => any): any => clients.ActiveClient.enqueue(() => next(a, b)); - const invoke3 = (a: any, b: any, c: any, next: (a: any, b: any, c: any) => any): any => clients.ActiveClient.enqueue(() => next(a, b, c)); - const invoke4 = (a: any, b: any, c: any, d: any, next: (a: any, b: any, c: any, d: any) => any): any => clients.ActiveClient.enqueue(() => next(a, b, c, d)); - return { - didOpen: async (document, sendMessage) => clients.ActiveClient.enqueue(async () => { + didOpen: async (document, sendMessage) => { if (!util.isCpp(document)) { return; } @@ -41,62 +36,41 @@ export function createProtocolFilter(): Middleware { const mappingString: string = baseFileName + "@" + document.fileName; client.addFileAssociations(mappingString, "cpp"); client.sendDidChangeSettings(); - document = await vscode.languages.setTextDocumentLanguage(document, "cpp"); + // This will cause the file to be closed and reopened. + void vscode.languages.setTextDocumentLanguage(document, "cpp"); + return; } // client.takeOwnership() will call client.TrackedDocuments.add() again, but that's ok. It's a Set. client.onDidOpenTextDocument(document); client.takeOwnership(document); - await sendMessage(document); - - // For a file already open when we activate, sometimes we don't get any notifications about visible - // or active text editors, visible ranges, or text selection. As a workaround, we trigger - // onDidChangeVisibleTextEditors here, only for the first file opened. - if (!anyFileOpened) { - anyFileOpened = true; - const cppEditors: vscode.TextEditor[] = vscode.window.visibleTextEditors.filter(e => util.isCpp(e.document)); - await client.onDidChangeVisibleTextEditors(cppEditors); - } + void sendMessage(document).then(() => { + // For a file already open when we activate, sometimes we don't get any notifications about visible + // or active text editors, visible ranges, or text selection. As a workaround, we trigger + // onDidChangeVisibleTextEditors here, only for the first file opened. + if (!anyFileOpened) { + anyFileOpened = true; + const cppEditors: vscode.TextEditor[] = vscode.window.visibleTextEditors.filter(e => util.isCpp(e.document)); + client.onDidChangeVisibleTextEditors(cppEditors).catch(logAndReturn.undefined); + } + }); } } - }), - didChange: invoke1, - willSave: invoke1, + }, willSaveWaitUntil: async (event, sendMessage) => { - // await clients.ActiveClient.ready; - // Don't use awaitUntilLanguageClientReady. - // Otherwise, the message can be delayed too long. const me: Client = clients.getClientFor(event.document.uri); if (me.TrackedDocuments.has(event.document.uri.toString())) { return sendMessage(event); } return []; }, - didSave: invoke1, - didClose: async (document, sendMessage) => clients.ActiveClient.enqueue(async () => { + didClose: async (document, sendMessage) => { const me: Client = clients.getClientFor(document.uri); const uriString: string = document.uri.toString(); if (me.TrackedDocuments.has(uriString)) { me.onDidCloseTextDocument(document); me.TrackedDocuments.delete(uriString); - await sendMessage(document); - } - }), - provideCompletionItem: invoke4, - resolveCompletionItem: invoke2, - provideHover: async (document, position, token, next: (document: any, position: any, token: any) => any) => clients.ActiveClient.enqueue(async () => { - const me: Client = clients.getClientFor(document.uri); - if (me.TrackedDocuments.has(document.uri.toString())) { - return next(document, position, token); + void sendMessage(document); } - return null; - }), - provideSignatureHelp: invoke4, - provideDefinition: invoke3, - provideReferences: invoke4, - provideDocumentHighlights: invoke3, - provideDeclaration: invoke3, - workspace: { - didChangeConfiguration: invoke1 } }; } diff --git a/Extension/src/common.ts b/Extension/src/common.ts index c962a719cd..dbd0bcdd63 100644 --- a/Extension/src/common.ts +++ b/Extension/src/common.ts @@ -1814,3 +1814,7 @@ export function findExePathInArgs(args: CommandString[]): string | undefined { return undefined; } + +export function getVsCodeVersion(): number[] { + return vscode.version.split('.').map(num => parseInt(num, undefined)); +}