From 5f9b346e286e4f14d0c1ac243d826dd86c6018d3 Mon Sep 17 00:00:00 2001 From: Garrett Campbell Date: Mon, 24 Mar 2025 13:21:07 -0400 Subject: [PATCH 1/3] add setting to enable/disable language services, enabled by default, still needs testing --- package.json | 6 ++ package.nls.json | 1 + src/config.ts | 6 ++ src/extension.ts | 146 +++++++++++++++++++++++++++++++---------------- 4 files changed, 111 insertions(+), 48 deletions(-) diff --git a/package.json b/package.json index cb57208189..a999d5ced6 100644 --- a/package.json +++ b/package.json @@ -3624,6 +3624,12 @@ "description": "%cmake-tools.configuration.cmake.enableAutomaticKitScan.description%", "scope": "resource" }, + "cmake.enableLanguageServices": { + "type": "boolean", + "default": true, + "description": "%cmake-tools.configuration.cmake.enableLanguageServices.description%", + "scope": "machine" + }, "cmake.preRunCoverageTarget": { "type": "string", "default": null, diff --git a/package.nls.json b/package.nls.json index a2c2c9ab93..7f491e10ec 100644 --- a/package.nls.json +++ b/package.nls.json @@ -274,6 +274,7 @@ "cmake-tools.configuration.cmake.automaticReconfigure.description": "Automatically configure CMake project directories when the kit or the configuration preset is changed.", "cmake-tools.configuration.cmake.pinnedCommands.description":"List of CMake commands to always pin by default. These will appear in the CMake Tools sidebar 'Pinned Commands' section.", "cmake-tools.configuration.cmake.enableAutomaticKitScan.description": "Enable automatic scanning for kits when a kit isn't selected. This will only take affect when CMake Presets aren't being used.", + "cmake-tools.configuration.cmake.enableLanguageServices.description": "Enable language services for CMake files. This will enable syntax highlighting, code completion, and other features.", "cmake-tools.configuration.cmake.preRunCoverageTarget.description": "Target to build before running tests with coverage using the test explorer", "cmake-tools.configuration.cmake.postRunCoverageTarget.description": "Target to build after running tests with coverage using the test explorer", "cmake-tools.configuration.cmake.coverageInfoFiles.description": "LCOV coverage info files to be processed after running tests with coverage using the test explorer.", diff --git a/src/config.ts b/src/config.ts index 224d4652f3..e92fbba9ba 100644 --- a/src/config.ts +++ b/src/config.ts @@ -217,6 +217,7 @@ export interface ExtensionConfigurationSettings { automaticReconfigure: boolean; pinnedCommands: string[]; enableAutomaticKitScan: boolean; + enableLanguageServices: boolean; preRunCoverageTarget: string | null; postRunCoverageTarget: string | null; coverageInfoFiles: string[]; @@ -571,6 +572,10 @@ export class ConfigurationReader implements vscode.Disposable { return this.configData.enableAutomaticKitScan; } + get enableLanguageServices(): boolean { + return this.configData.enableLanguageServices; + } + get preRunCoverageTarget(): string | null { return this.configData.preRunCoverageTarget; } @@ -648,6 +653,7 @@ export class ConfigurationReader implements vscode.Disposable { automaticReconfigure: new vscode.EventEmitter(), pinnedCommands: new vscode.EventEmitter(), enableAutomaticKitScan: new vscode.EventEmitter(), + enableLanguageServices: new vscode.EventEmitter(), preRunCoverageTarget: new vscode.EventEmitter(), postRunCoverageTarget: new vscode.EventEmitter(), coverageInfoFiles: new vscode.EventEmitter() diff --git a/src/extension.ts b/src/extension.ts index 196158195f..f2205e5549 100644 --- a/src/extension.ts +++ b/src/extension.ts @@ -106,6 +106,12 @@ export class ExtensionManager implements vscode.Disposable { private onDidChangeActiveTextEditorSub: vscode.Disposable = new DummyDisposable(); private readonly extensionActiveCommandsEmitter = new vscode.EventEmitter(); private readonly workspaceConfig: ConfigurationReader = ConfigurationReader.create(); + private readonly CMAKE_LANGUAGE = "cmake"; + private readonly CMAKE_SELECTOR: vscode.DocumentSelector = [ + { language: this.CMAKE_LANGUAGE, scheme: "file" }, + { language: this.CMAKE_LANGUAGE, scheme: "untitled" } + ]; + private languageServicesDisposables: vscode.Disposable[] = []; private updateTouchBarVisibility(config: TouchBarConfig) { const touchBarVisible = config.visibility === "default"; @@ -119,6 +125,17 @@ export class ExtensionManager implements vscode.Disposable { * Second-phase async init */ public async init() { + if (this.workspaceConfig.enableLanguageServices) { + await this.enableLanguageServices(); + this.workspaceConfig.onChange('enableLanguageServices', async (value) => { + if (value) { + await this.enableLanguageServices(); + } else { + this.disposeLanguageServices(); + } + }); + } + this.updateTouchBarVisibility(this.workspaceConfig.touchbar); this.workspaceConfig.onChange('touchbar', config => this.updateTouchBarVisibility(config)); @@ -357,6 +374,7 @@ export class ExtensionManager implements vscode.Disposable { private activeTestPresetSub: vscode.Disposable = new DummyDisposable(); private activePackagePresetSub: vscode.Disposable = new DummyDisposable(); private activeWorkflowPresetSub: vscode.Disposable = new DummyDisposable(); + private enableLanguageServicesSub: vscode.Disposable = new DummyDisposable(); // Watch the code model so that we may update the tree view // @@ -379,6 +397,83 @@ export class ExtensionManager implements vscode.Disposable { private cppToolsAPI?: cpt.CppToolsApi; private configProviderRegistered?: boolean = false; + private async enableLanguageServices() { + try { + const languageServices = await LanguageServiceData.create(); + this.languageServicesDisposables.push(vscode.languages.registerHoverProvider( + this.CMAKE_SELECTOR, + languageServices + )); + this.languageServicesDisposables.push(vscode.languages.registerCompletionItemProvider( + this.CMAKE_SELECTOR, + languageServices + )); + } catch { + log.error( + localize( + "language.service.failed", + "Failed to initialize language services" + ) + ); + } + + this.languageServicesDisposables.push(vscode.languages.setLanguageConfiguration( + this.CMAKE_LANGUAGE, + { + indentationRules: { + // ^(.*\*/)?\s*\}.*$ + decreaseIndentPattern: /^(.*\*\/)?\s*\}.*$/, + // ^.*\{[^}"']*$ + increaseIndentPattern: /^.*\{[^}"']*$/ + }, + wordPattern: + /(-?\d*\.\d\w*)|([^\`\~\!\@\#\%\^\&\*\(\)\-\=\+\[\{\]\}\\\|\;\:\'\"\,\.\<\>\/\?\s]+)/g, + comments: { + lineComment: "#" + }, + brackets: [ + ["{", "}"], + ["(", ")"] + ], + + __electricCharacterSupport: { + brackets: [ + { + tokenType: "delimiter.curly.ts", + open: "{", + close: "}", + isElectric: true + }, + { + tokenType: "delimiter.square.ts", + open: "[", + close: "]", + isElectric: true + }, + { + tokenType: "delimiter.paren.ts", + open: "(", + close: ")", + isElectric: true + } + ] + }, + + __characterPairSupport: { + autoClosingPairs: [ + { open: "{", close: "}" }, + { open: "(", close: ")" }, + { open: '"', close: '"', notIn: ["string"] } + ] + } + } + )); + } + + private disposeLanguageServices() { + this.languageServicesDisposables.forEach(sub => sub.dispose()); + } + private getProjectsForWorkspaceFolder(folder?: vscode.WorkspaceFolder): CMakeProject[] | undefined { folder = this.getWorkspaceFolder(folder); return this.projectController.getProjectsForWorkspaceFolder(folder); @@ -554,6 +649,7 @@ export class ExtensionManager implements vscode.Disposable { */ async asyncDispose() { this.disposeSubs(); + this.disposeLanguageServices(); this.codeModelUpdateSubs.forEach( subs => subs.forEach( sub => sub.dispose() @@ -736,7 +832,7 @@ export class ExtensionManager implements vscode.Disposable { private disposeSubs() { util.disposeAll(this.projectSubscriptions); - for (const sub of [this.statusMessageSub, this.targetNameSub, this.buildTypeSub, this.launchTargetSub, this.ctestEnabledSub, this.isBusySub, this.activeConfigurePresetSub, this.activeBuildPresetSub, this.activeTestPresetSub, this.activePackagePresetSub, this.activeWorkflowPresetSub]) { + for (const sub of [this.statusMessageSub, this.targetNameSub, this.buildTypeSub, this.launchTargetSub, this.ctestEnabledSub, this.isBusySub, this.activeConfigurePresetSub, this.activeBuildPresetSub, this.activeTestPresetSub, this.activePackagePresetSub, this.activeWorkflowPresetSub, this.enableLanguageServicesSub]) { sub.dispose(); } } @@ -834,6 +930,7 @@ export class ExtensionManager implements vscode.Disposable { this.activeTestPresetSub = new DummyDisposable(); this.activePackagePresetSub = new DummyDisposable(); this.activeWorkflowPresetSub = new DummyDisposable(); + this.enableLanguageServicesSub = new DummyDisposable(); this.statusBar.setActiveKitName(''); this.statusBar.setConfigurePresetName(''); this.statusBar.setBuildPresetName(''); @@ -2400,53 +2497,6 @@ export async function activate(context: vscode.ExtensionContext): Promise\/\?\s]+)/g, - comments: { - lineComment: '#' - }, - brackets: [ - ['{', '}'], - ['(', ')'] - ], - - __electricCharacterSupport: { - brackets: [ - { tokenType: 'delimiter.curly.ts', open: '{', close: '}', isElectric: true }, - { tokenType: 'delimiter.square.ts', open: '[', close: ']', isElectric: true }, - { tokenType: 'delimiter.paren.ts', open: '(', close: ')', isElectric: true } - ] - }, - - __characterPairSupport: { - autoClosingPairs: [ - { open: '{', close: '}' }, - { open: '(', close: ')' }, - { open: '"', close: '"', notIn: ['string'] } - ] - } - }); - if (vscode.workspace.getConfiguration('cmake').get('showOptionsMovedNotification')) { void vscode.window.showInformationMessage( localize('options.moved.notification.body', "Some status bar options in CMake Tools have now moved to the Project Status View in the CMake Tools sidebar. You can customize your view with the 'cmake.options' property in settings."), From eda768117c21b61f301ce38a1cd74f83b11d89e2 Mon Sep 17 00:00:00 2001 From: Garrett Campbell Date: Mon, 24 Mar 2025 13:23:20 -0400 Subject: [PATCH 2/3] update test --- test/unit-tests/config.test.ts | 1 + 1 file changed, 1 insertion(+) diff --git a/test/unit-tests/config.test.ts b/test/unit-tests/config.test.ts index 28d2911ff1..a67337b274 100644 --- a/test/unit-tests/config.test.ts +++ b/test/unit-tests/config.test.ts @@ -78,6 +78,7 @@ function createConfig(conf: Partial): Configurat ignoreCMakeListsMissing: false, automaticReconfigure: false, enableAutomaticKitScan: true, + enableLanguageServices: true, preRunCoverageTarget: null, postRunCoverageTarget: null, coverageInfoFiles: [] From bbfaf3340e470615e2af3d52b3beb08c7612248d Mon Sep 17 00:00:00 2001 From: Garrett Campbell Date: Mon, 24 Mar 2025 13:30:45 -0400 Subject: [PATCH 3/3] add changelog entry --- CHANGELOG.md | 1 + 1 file changed, 1 insertion(+) diff --git a/CHANGELOG.md b/CHANGELOG.md index c724ee6c75..a5efc65109 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -5,6 +5,7 @@ Features: - Add an option to specify the launch target for debugging CTest tests. [#4273](https://github.com/microsoft/vscode-cmake-tools/pull/4273) +- Add a setting to enable/disable our built-in language services. [#4290](https://github.com/microsoft/vscode-cmake-tools/issues/4290) Improvements: