From 898ba52cb2b76ee5977bd6a5cf51d6c99670132c Mon Sep 17 00:00:00 2001 From: anonchihaya0908 Date: Wed, 16 Jul 2025 13:56:26 +0800 Subject: [PATCH 01/34] feat(command): Add Create Source/Header Pair command Improves the C++ development workflow by providing a new command to create corresponding source and header files simultaneously, mirroring common IDE functionality. This new command, `clangd.createSourceHeaderPair`, provides the following features: - Prompts the user for a valid C++ class name with robust validation. - Intelligently detects the target directory, preferring the active file's location. - Generates professional-grade file templates for both the header and source file. - Handles cross-platform line endings (EOL) correctly by respecting user settings. - Prevents accidental file overwrites by checking for existing files before creation. - Is accessible from the Command Palette and the editor context menu. Resolves #[Your-Issue-Number-Here] --- package.json | 21 +++- src/create-source-header-pair.ts | 160 +++++++++++++++++++++++++++++++ src/extension.ts | 15 ++- 3 files changed, 189 insertions(+), 7 deletions(-) create mode 100644 src/create-source-header-pair.ts diff --git a/package.json b/package.json index e91f771b..773a119a 100644 --- a/package.json +++ b/package.json @@ -214,6 +214,11 @@ "category": "clangd", "title": "Switch Between Source/Header" }, + { + "command": "clangd.createSourceHeaderPair", + "category": "clangd", + "title": "Create Source/Header Pair" + }, { "command": "clangd.install", "category": "clangd", @@ -331,6 +336,11 @@ "when": "resourceLangId == c || resourceLangId == cpp || resourceLangId == cuda-cpp || resourceLangId == objective-c || resourceLangId == objective-cpp", "group": "0_navigation@5" }, + { + "command": "clangd.createSourceHeaderPair", + "when": "resourceLangId == c || resourceLangId == cpp || resourceLangId == cuda-cpp || resourceLangId == objective-c || resourceLangId == objective-cpp", + "group": "1_modification@1" + }, { "command": "clangd.ast", "when": "(resourceLangId == c || resourceLangId == cpp || resourceLangId == cuda-cpp || resourceLangId == objective-c || resourceLangId == objective-cpp) && clangd.ast.supported" @@ -388,19 +398,22 @@ { "id": "clangd.typeHierarchyView", "name": "Type Hierarchy", - "when": "clangd.typeHierarchyVisible" + "when": "clangd.typeHierarchyVisible", + "icon": "$(type-hierarchy)" }, { "id": "clangd.memoryUsage", "name": "clangd Memory Usage", - "when": "clangd.memoryUsage.hasData" + "when": "clangd.memoryUsage.hasData", + "icon": "$(dashboard)" }, { "id": "clangd.ast", "name": "AST", - "when": "clangd.ast.hasData" + "when": "clangd.ast.hasData", + "icon": "$(list-tree)" } ] } } -} +} \ No newline at end of file diff --git a/src/create-source-header-pair.ts b/src/create-source-header-pair.ts new file mode 100644 index 00000000..1478569d --- /dev/null +++ b/src/create-source-header-pair.ts @@ -0,0 +1,160 @@ +import * as path from 'path'; +import * as vscode from 'vscode'; +import * as os from 'os'; + +import { ClangdContext } from './clangd-context'; + +// Handles the "Create Source/Header Pair" command. +class PairCreator implements vscode.Disposable { + private command: vscode.Disposable; + + constructor() { + this.command = vscode.commands.registerCommand( + 'clangd.createSourceHeaderPair', this.create, this); + } + + // Implements vscode.Disposable to clean up resources. + dispose() { this.command.dispose(); } + + // Helper method to get platform-appropriate line ending + private getLineEnding(): string { + const eolSetting = vscode.workspace.getConfiguration('files').get('eol'); + if (eolSetting === '\n' || eolSetting === '\r\n') { + return eolSetting; + + } + return os.EOL; + } + + // The core implementation of the command. + // It handles user input, file creation, and opening the new file. + public async create() { + const targetDirectory = await this.getTargetDirectory(); + if (!targetDirectory) { + vscode.window.showErrorMessage( + 'Could not determine a target directory. Please open a folder or a file first.'); + return; + } + + const className = await vscode.window.showInputBox({ + prompt: 'Please enter the name for the new C++ class.', + placeHolder: 'MyClass', + validateInput: text => { + if (!text || text.trim().length === 0) { + return 'Class name cannot be empty.'; + } + if (!text.match(/^[a-zA-Z_][a-zA-Z0-9_]*$/)) { + return 'Name must start with a letter or underscore and contain only letters, numbers, and underscores.'; + } + return null; + } + }); + + if (!className) { + return; // User canceled the input + } + + const headerPath = + vscode.Uri.file(path.join(targetDirectory.fsPath, `${className}.h`)); + const sourcePath = + vscode.Uri.file(path.join(targetDirectory.fsPath, `${className}.cpp`)); + + // Prevent overwriting existing files + const [headerExists, sourceExists] = await Promise.allSettled([ + vscode.workspace.fs.stat(headerPath), + vscode.workspace.fs.stat(sourcePath) + ]); + + if (headerExists.status === 'fulfilled') { + vscode.window.showErrorMessage( + `File already exists: ${headerPath.fsPath}`); + return; + } + if (sourceExists.status === 'fulfilled') { + vscode.window.showErrorMessage( + `File already exists: ${sourcePath.fsPath}`); + return; + } + + + // Get the appropriate line ending for this platform/user setting + const eol = this.getLineEnding(); + + const headerGuard = `${className.toUpperCase()}_H_`; + + // Define the content using clean template literals. + const headerTemplate = `#ifndef ${headerGuard} +#define ${headerGuard} + +class ${className} { +public: + ${className}(); + ~${className}(); + +private: + // Add private members here +}; + +#endif // ${headerGuard} +`; + + const sourceTemplate = `#include "${className}.h" + +${className}::${className}() { + // Constructor implementation +} + +${className}::~${className}() { + // Destructor implementation +} +`; + + // Replace all LF (\n) characters in the templates with the correct EOL sequence. + const headerContent = headerTemplate.replace(/\n/g, eol); + const sourceContent = sourceTemplate.replace(/\n/g, eol); + + + try { + await Promise.all([ + vscode.workspace.fs.writeFile(headerPath, Buffer.from(headerContent, 'utf8')), + vscode.workspace.fs.writeFile(sourcePath, Buffer.from(sourceContent, 'utf8')) + ]); + } catch (error: any) { + vscode.window.showErrorMessage( + `Failed to create files: ${error.message}`); + return; + } + + await vscode.window.showTextDocument( + await vscode.workspace.openTextDocument(headerPath)); + await vscode.window.showInformationMessage( + `Successfully created ${className}.h and ${className}.cpp`); + } + + // Helper method to intelligently determine the directory for new files. + private async getTargetDirectory(): Promise { + if (vscode.window.activeTextEditor && !vscode.window.activeTextEditor.document.isUntitled) { + return vscode.Uri.file( + path.dirname(vscode.window.activeTextEditor.document.uri.fsPath)); + } + + if (vscode.workspace.workspaceFolders && + vscode.workspace.workspaceFolders.length > 0) { + if (vscode.workspace.workspaceFolders.length === 1) { + return vscode.workspace.workspaceFolders[0].uri; + } + const picked = await vscode.window.showWorkspaceFolderPick({ + placeHolder: 'Please select a workspace folder to create the files in' + }); + return picked?.uri; + } + + return undefined; + } +} + +// Registers the command and lifecycle handler for the create pair feature. +export function registerCreateSourceHeaderPairCommand(context: ClangdContext) { + context.subscriptions.push(new PairCreator()); + +} \ No newline at end of file diff --git a/src/extension.ts b/src/extension.ts index 815866fc..642fcb32 100644 --- a/src/extension.ts +++ b/src/extension.ts @@ -5,6 +5,9 @@ import {ClangdExtension} from '../api/vscode-clangd'; import {ClangdExtensionImpl} from './api'; import {ClangdContext} from './clangd-context'; import {get, update} from './config'; +import { + registerCreateSourceHeaderPairCommand +} from './create-source-header-pair'; let apiInstance: ClangdExtensionImpl|undefined; @@ -52,8 +55,11 @@ export async function activate(context: vscode.ExtensionContext): clangdContext.dispose(); clangdContext = await ClangdContext.create(context.globalStoragePath, outputChannel); - if (clangdContext) + if (clangdContext) { context.subscriptions.push(clangdContext); + + registerCreateSourceHeaderPairCommand(clangdContext); + } if (apiInstance) { apiInstance.client = clangdContext?.client; } @@ -64,9 +70,12 @@ export async function activate(context: vscode.ExtensionContext): if (vscode.workspace.getConfiguration('clangd').get('enable')) { clangdContext = await ClangdContext.create(context.globalStoragePath, outputChannel); - if (clangdContext) + if (clangdContext) { context.subscriptions.push(clangdContext); + registerCreateSourceHeaderPairCommand(clangdContext); + } + shouldCheck = vscode.workspace.getConfiguration('clangd').get( 'detectExtensionConflicts') ?? false; @@ -106,4 +115,4 @@ export async function activate(context: vscode.ExtensionContext): apiInstance = new ClangdExtensionImpl(clangdContext?.client); return apiInstance; -} +} \ No newline at end of file From b8dfda380bd7a16f817287543ed5f1c64954f64d Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?=E9=95=BF=E6=9C=9F=E7=B4=A0=E9=A3=9F?= Date: Wed, 16 Jul 2025 18:03:47 +0800 Subject: [PATCH 02/34] Refactor and modularize source/header pair creation Refactored the PairCreator class to modularize logic for file name validation, language detection, template generation, file existence checks, and file writing. Added interfaces and constants for better type safety and maintainability. Improved user prompts and error handling, and made the code more extensible for both C and C++ file generation. --- src/create-source-header-pair.ts | 298 +++++++++++++++++++++++-------- 1 file changed, 225 insertions(+), 73 deletions(-) diff --git a/src/create-source-header-pair.ts b/src/create-source-header-pair.ts index 1478569d..e93988e8 100644 --- a/src/create-source-header-pair.ts +++ b/src/create-source-header-pair.ts @@ -4,6 +4,38 @@ import * as os from 'os'; import { ClangdContext } from './clangd-context'; +// Constants for file extensions and validation +const FILE_EXTENSIONS = { + HEADER: '.h', + C_SOURCE: '.c', + CPP_SOURCE: '.cpp' +} as const; + +const LANGUAGE_TYPES = { + C: 'c', + CPP: 'cpp' +} as const; + +const VALIDATION_PATTERNS = { + IDENTIFIER: /^[a-zA-Z_][a-zA-Z0-9_]*$/ +} as const; + +// Interface for template configuration +interface TemplateConfig { + fileName: string; + headerGuard: string; + eol: string; + isC: boolean; +} + +// Interface for file generation result +interface FileGenerationResult { + headerPath: vscode.Uri; + sourcePath: vscode.Uri; + headerContent: string; + sourceContent: string; +} + // Handles the "Create Source/Header Pair" command. class PairCreator implements vscode.Disposable { private command: vscode.Disposable; @@ -16,145 +48,265 @@ class PairCreator implements vscode.Disposable { // Implements vscode.Disposable to clean up resources. dispose() { this.command.dispose(); } + // Helper method to get platform-appropriate line ending private getLineEnding(): string { const eolSetting = vscode.workspace.getConfiguration('files').get('eol'); if (eolSetting === '\n' || eolSetting === '\r\n') { return eolSetting; - } return os.EOL; } - // The core implementation of the command. - // It handles user input, file creation, and opening the new file. - public async create() { - const targetDirectory = await this.getTargetDirectory(); - if (!targetDirectory) { - vscode.window.showErrorMessage( - 'Could not determine a target directory. Please open a folder or a file first.'); - return; + // Detects the language type based on the active editor + private detectLanguage(): 'c' | 'cpp' { + const activeEditor = vscode.window.activeTextEditor; + if (activeEditor && !activeEditor.document.isUntitled) { + const langId = activeEditor.document.languageId; + return langId === LANGUAGE_TYPES.C ? LANGUAGE_TYPES.C : LANGUAGE_TYPES.CPP; } + return LANGUAGE_TYPES.CPP; // Default to C++ + } - const className = await vscode.window.showInputBox({ - prompt: 'Please enter the name for the new C++ class.', - placeHolder: 'MyClass', - validateInput: text => { - if (!text || text.trim().length === 0) { - return 'Class name cannot be empty.'; - } - if (!text.match(/^[a-zA-Z_][a-zA-Z0-9_]*$/)) { - return 'Name must start with a letter or underscore and contain only letters, numbers, and underscores.'; - } - return null; - } - }); - - if (!className) { - return; // User canceled the input + // Validates user input for file name + private validateFileName(text: string): string | null { + if (!text || text.trim().length === 0) { + return 'Name cannot be empty.'; + } + if (!VALIDATION_PATTERNS.IDENTIFIER.test(text)) { + return 'Name must start with a letter or underscore and contain only letters, numbers, and underscores.'; } + return null; + } - const headerPath = - vscode.Uri.file(path.join(targetDirectory.fsPath, `${className}.h`)); - const sourcePath = - vscode.Uri.file(path.join(targetDirectory.fsPath, `${className}.cpp`)); + // Prompts user for file name input + private async promptForFileName(isC: boolean): Promise { + return vscode.window.showInputBox({ + prompt: isC + ? 'Please enter the name for the new C files.' + : 'Please enter the name for the new C++ class.', + placeHolder: isC ? 'my_c_functions' : 'MyClass', + validateInput: this.validateFileName + }); + } - // Prevent overwriting existing files + // Checks if files already exist + private async checkFileExistence(headerPath: vscode.Uri, sourcePath: vscode.Uri): Promise { const [headerExists, sourceExists] = await Promise.allSettled([ vscode.workspace.fs.stat(headerPath), vscode.workspace.fs.stat(sourcePath) ]); if (headerExists.status === 'fulfilled') { - vscode.window.showErrorMessage( - `File already exists: ${headerPath.fsPath}`); - return; + vscode.window.showErrorMessage(`File already exists: ${headerPath.fsPath}`); + return true; } + if (sourceExists.status === 'fulfilled') { - vscode.window.showErrorMessage( - `File already exists: ${sourcePath.fsPath}`); - return; + vscode.window.showErrorMessage(`File already exists: ${sourcePath.fsPath}`); + return true; } + return false; + } + + // Generates C language templates + private generateCTemplates(config: TemplateConfig): { header: string; source: string } { + const headerTemplate = `#ifndef ${config.headerGuard} +#define ${config.headerGuard} - // Get the appropriate line ending for this platform/user setting - const eol = this.getLineEnding(); +// Function declarations for ${config.fileName}.c - const headerGuard = `${className.toUpperCase()}_H_`; +#endif // ${config.headerGuard} +`; - // Define the content using clean template literals. - const headerTemplate = `#ifndef ${headerGuard} -#define ${headerGuard} + const sourceTemplate = `#include "${config.fileName}.h" -class ${className} { +// Function implementations for ${config.fileName}.c +`; + + return { + header: headerTemplate.replace(/\n/g, config.eol), + source: sourceTemplate.replace(/\n/g, config.eol) + }; + } + + // Generates C++ language templates + private generateCppTemplates(config: TemplateConfig): { header: string; source: string } { + const headerTemplate = `#ifndef ${config.headerGuard} +#define ${config.headerGuard} + +class ${config.fileName} { public: - ${className}(); - ~${className}(); + ${config.fileName}(); + ~${config.fileName}(); private: // Add private members here }; -#endif // ${headerGuard} +#endif // ${config.headerGuard} `; - const sourceTemplate = `#include "${className}.h" + const sourceTemplate = `#include "${config.fileName}.h" -${className}::${className}() { +${config.fileName}::${config.fileName}() { // Constructor implementation } -${className}::~${className}() { +${config.fileName}::~${config.fileName}() { // Destructor implementation } `; - // Replace all LF (\n) characters in the templates with the correct EOL sequence. - const headerContent = headerTemplate.replace(/\n/g, eol); - const sourceContent = sourceTemplate.replace(/\n/g, eol); + return { + header: headerTemplate.replace(/\n/g, config.eol), + source: sourceTemplate.replace(/\n/g, config.eol) + }; + } + + // Generates file templates based on language type + private generateTemplates(config: TemplateConfig): { header: string; source: string } { + return config.isC + ? this.generateCTemplates(config) + : this.generateCppTemplates(config); + } + // Prepares file generation data + private prepareFileGeneration( + fileName: string, + targetDirectory: vscode.Uri, + isC: boolean + ): FileGenerationResult { + const sourceExt = isC ? FILE_EXTENSIONS.C_SOURCE : FILE_EXTENSIONS.CPP_SOURCE; + const headerPath = vscode.Uri.file( + path.join(targetDirectory.fsPath, `${fileName}${FILE_EXTENSIONS.HEADER}`) + ); + const sourcePath = vscode.Uri.file( + path.join(targetDirectory.fsPath, `${fileName}${sourceExt}`) + ); + const config: TemplateConfig = { + fileName, + headerGuard: `${fileName.toUpperCase()}_H_`, + eol: this.getLineEnding(), + isC + }; + + const templates = this.generateTemplates(config); + + return { + headerPath, + sourcePath, + headerContent: templates.header, + sourceContent: templates.source + }; + } + + // Writes files to the filesystem + private async writeFiles(generation: FileGenerationResult): Promise { try { await Promise.all([ - vscode.workspace.fs.writeFile(headerPath, Buffer.from(headerContent, 'utf8')), - vscode.workspace.fs.writeFile(sourcePath, Buffer.from(sourceContent, 'utf8')) + vscode.workspace.fs.writeFile( + generation.headerPath, + Buffer.from(generation.headerContent, 'utf8') + ), + vscode.workspace.fs.writeFile( + generation.sourcePath, + Buffer.from(generation.sourceContent, 'utf8') + ) ]); } catch (error: any) { - vscode.window.showErrorMessage( - `Failed to create files: ${error.message}`); - return; + throw new Error(`Failed to create files: ${error.message}`); } + } + + // Opens the created header file and shows success message + private async finalizeCreation(generation: FileGenerationResult): Promise { + const fileName = path.basename(generation.headerPath.fsPath, FILE_EXTENSIONS.HEADER); + const sourceExt = path.extname(generation.sourcePath.fsPath); await vscode.window.showTextDocument( - await vscode.workspace.openTextDocument(headerPath)); + await vscode.workspace.openTextDocument(generation.headerPath) + ); + await vscode.window.showInformationMessage( - `Successfully created ${className}.h and ${className}.cpp`); + `Successfully created ${fileName}${FILE_EXTENSIONS.HEADER} and ${fileName}${sourceExt}` + ); + } + + // The core implementation of the command. + // It handles user input, file creation, and opening the new file. + public async create(): Promise { + try { + // Step 1: Get target directory + const targetDirectory = await this.getTargetDirectory(); + if (!targetDirectory) { + vscode.window.showErrorMessage( + 'Could not determine a target directory. Please open a folder or a file first.' + ); + return; + } + + // Step 2: Detect language + const language = this.detectLanguage(); + const isC = language === LANGUAGE_TYPES.C; + + // Step 3: Get file name from user + const fileName = await this.promptForFileName(isC); + if (!fileName) { + return; // User canceled the input + } + + // Step 4: Prepare file generation data + const generation = this.prepareFileGeneration(fileName, targetDirectory, isC); + + // Step 5: Check if files already exist + const filesExist = await this.checkFileExistence(generation.headerPath, generation.sourcePath); + if (filesExist) { + return; + } + + // Step 6: Write files + await this.writeFiles(generation); + + // Step 7: Finalize creation (open file and show message) + await this.finalizeCreation(generation); + + } catch (error: any) { + vscode.window.showErrorMessage(error.message || 'An unexpected error occurred.'); + } } // Helper method to intelligently determine the directory for new files. private async getTargetDirectory(): Promise { - if (vscode.window.activeTextEditor && !vscode.window.activeTextEditor.document.isUntitled) { - return vscode.Uri.file( - path.dirname(vscode.window.activeTextEditor.document.uri.fsPath)); + // Try to use the directory of the currently active file + const activeEditor = vscode.window.activeTextEditor; + if (activeEditor && !activeEditor.document.isUntitled) { + return vscode.Uri.file(path.dirname(activeEditor.document.uri.fsPath)); } - if (vscode.workspace.workspaceFolders && - vscode.workspace.workspaceFolders.length > 0) { - if (vscode.workspace.workspaceFolders.length === 1) { - return vscode.workspace.workspaceFolders[0].uri; - } - const picked = await vscode.window.showWorkspaceFolderPick({ - placeHolder: 'Please select a workspace folder to create the files in' - }); - return picked?.uri; + // Fall back to workspace folders + const workspaceFolders = vscode.workspace.workspaceFolders; + if (!workspaceFolders || workspaceFolders.length === 0) { + return undefined; + } + + // If there's only one workspace folder, use it + if (workspaceFolders.length === 1) { + return workspaceFolders[0].uri; } - return undefined; + // If there are multiple workspace folders, let the user choose + const picked = await vscode.window.showWorkspaceFolderPick({ + placeHolder: 'Please select a workspace folder to create the files in' + }); + + return picked?.uri; } } // Registers the command and lifecycle handler for the create pair feature. export function registerCreateSourceHeaderPairCommand(context: ClangdContext) { context.subscriptions.push(new PairCreator()); - } \ No newline at end of file From e82a3ad64e0efad6d59286382da52f45e47843bc Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?=E9=95=BF=E6=9C=9F=E7=B4=A0=E9=A3=9F?= Date: Wed, 16 Jul 2025 19:33:26 +0800 Subject: [PATCH 03/34] This refactors the "Create Source/Header Pair" command into a unified, context-aware wizard, significantly improving user experience and flexibility. Key improvements include: - A single, multi-step input flow for all creation logic. - Dynamically sorts template choices based on the active language context (C vs. C++), prioritizing the most likely user intention. - Disambiguates between C and C++ structs, providing correct templates and file extensions for each. - Introduces intelligent, dynamic placeholders in the file name input box based on the current file and selected template. - The underlying code has been significantly refactored for clarity, maintainability, and type safety, breaking down complex logic into single-responsibility helper methods. --- src/create-source-header-pair.ts | 395 +++++++++++++------------------ 1 file changed, 166 insertions(+), 229 deletions(-) diff --git a/src/create-source-header-pair.ts b/src/create-source-header-pair.ts index e93988e8..0f7be72a 100644 --- a/src/create-source-header-pair.ts +++ b/src/create-source-header-pair.ts @@ -4,309 +4,246 @@ import * as os from 'os'; import { ClangdContext } from './clangd-context'; -// Constants for file extensions and validation +// --- Constants and Types --- + const FILE_EXTENSIONS = { HEADER: '.h', C_SOURCE: '.c', CPP_SOURCE: '.cpp' } as const; -const LANGUAGE_TYPES = { - C: 'c', - CPP: 'cpp' -} as const; - const VALIDATION_PATTERNS = { IDENTIFIER: /^[a-zA-Z_][a-zA-Z0-9_]*$/ } as const; -// Interface for template configuration -interface TemplateConfig { - fileName: string; - headerGuard: string; - eol: string; - isC: boolean; -} +const DEFAULT_PLACEHOLDERS = { + C_FILES: 'my_c_functions', + C_STRUCT: 'MyStruct', + CPP_FILES: 'utils', + CPP_CLASS: 'MyClass', + CPP_STRUCT: 'MyStruct' +} as const; -// Interface for file generation result -interface FileGenerationResult { - headerPath: vscode.Uri; - sourcePath: vscode.Uri; - headerContent: string; - sourceContent: string; -} +const TEMPLATE_KEYS = { + CPP_FILES: 'cpp_files', + CPP_CLASS: 'cpp_class', + CPP_STRUCT: 'cpp_struct', + C_FILES: 'c_files', + C_STRUCT: 'c_struct' +} as const; +type TemplateType = typeof TEMPLATE_KEYS[keyof typeof TEMPLATE_KEYS]; + +const TEMPLATE_CHOICES: ReadonlyArray = [ + { label: '$(new-file) C++ Files', description: 'Create an empty C++ header/source pair.', key: TEMPLATE_KEYS.CPP_FILES }, + { label: '$(symbol-class) C++ Class', description: 'Create a C++ header/source pair with a class definition.', key: TEMPLATE_KEYS.CPP_CLASS }, + { label: '$(symbol-struct) C++ Struct', description: 'Create a C++ header/source pair with a struct definition.', key: TEMPLATE_KEYS.CPP_STRUCT }, + { label: '$(file-code) C Files', description: 'Create a standard C header/source pair for functions.', key: TEMPLATE_KEYS.C_FILES }, + { label: '$(symbol-struct) C Struct', description: 'Create a standard C header/source pair with a struct definition.', key: TEMPLATE_KEYS.C_STRUCT } +]; + +// --- Main Class --- -// Handles the "Create Source/Header Pair" command. class PairCreator implements vscode.Disposable { private command: vscode.Disposable; constructor() { - this.command = vscode.commands.registerCommand( - 'clangd.createSourceHeaderPair', this.create, this); + this.command = vscode.commands.registerCommand('clangd.createSourceHeaderPair', this.create, this); } - // Implements vscode.Disposable to clean up resources. dispose() { this.command.dispose(); } + public async create(): Promise { + try { + const targetDirectory = await this.getTargetDirectory(); + if (!targetDirectory) { + vscode.window.showErrorMessage('Could not determine a target directory. Please open a folder or a file first.'); + return; + } - // Helper method to get platform-appropriate line ending - private getLineEnding(): string { - const eolSetting = vscode.workspace.getConfiguration('files').get('eol'); - if (eolSetting === '\n' || eolSetting === '\r\n') { - return eolSetting; - } - return os.EOL; - } + const language = this.detectLanguage(); + const templateType = await this.promptForTemplateType(language); + if (!templateType) return; - // Detects the language type based on the active editor - private detectLanguage(): 'c' | 'cpp' { - const activeEditor = vscode.window.activeTextEditor; - if (activeEditor && !activeEditor.document.isUntitled) { - const langId = activeEditor.document.languageId; - return langId === LANGUAGE_TYPES.C ? LANGUAGE_TYPES.C : LANGUAGE_TYPES.CPP; - } - return LANGUAGE_TYPES.CPP; // Default to C++ - } + const fileName = await this.promptForFileName(templateType); + if (!fileName) return; - // Validates user input for file name - private validateFileName(text: string): string | null { - if (!text || text.trim().length === 0) { - return 'Name cannot be empty.'; - } - if (!VALIDATION_PATTERNS.IDENTIFIER.test(text)) { - return 'Name must start with a letter or underscore and contain only letters, numbers, and underscores.'; - } - return null; - } + const isC = templateType === TEMPLATE_KEYS.C_FILES || templateType === TEMPLATE_KEYS.C_STRUCT; + const sourceExt = isC ? FILE_EXTENSIONS.C_SOURCE : FILE_EXTENSIONS.CPP_SOURCE; + const headerPath = vscode.Uri.file(path.join(targetDirectory.fsPath, `${fileName}${FILE_EXTENSIONS.HEADER}`)); + const sourcePath = vscode.Uri.file(path.join(targetDirectory.fsPath, `${fileName}${sourceExt}`)); - // Prompts user for file name input - private async promptForFileName(isC: boolean): Promise { - return vscode.window.showInputBox({ - prompt: isC - ? 'Please enter the name for the new C files.' - : 'Please enter the name for the new C++ class.', - placeHolder: isC ? 'my_c_functions' : 'MyClass', - validateInput: this.validateFileName - }); - } + const existingFilePath = await this.checkFileExistence(headerPath, sourcePath); + if (existingFilePath) { + vscode.window.showErrorMessage(`File already exists: ${existingFilePath}`); + return; + } - // Checks if files already exist - private async checkFileExistence(headerPath: vscode.Uri, sourcePath: vscode.Uri): Promise { - const [headerExists, sourceExists] = await Promise.allSettled([ - vscode.workspace.fs.stat(headerPath), - vscode.workspace.fs.stat(sourcePath) - ]); + const eol = this.getLineEnding(); + const { headerContent, sourceContent } = this.generateFileContent(fileName, eol, templateType); - if (headerExists.status === 'fulfilled') { - vscode.window.showErrorMessage(`File already exists: ${headerPath.fsPath}`); - return true; - } + await this.writeFiles(headerPath, sourcePath, headerContent, sourceContent); + await this.finalizeCreation(headerPath, sourcePath); - if (sourceExists.status === 'fulfilled') { - vscode.window.showErrorMessage(`File already exists: ${sourcePath.fsPath}`); - return true; + } catch (error: any) { + vscode.window.showErrorMessage(error.message || 'An unexpected error occurred.'); } - - return false; } - // Generates C language templates - private generateCTemplates(config: TemplateConfig): { header: string; source: string } { - const headerTemplate = `#ifndef ${config.headerGuard} -#define ${config.headerGuard} + // --- Helper Methods --- -// Function declarations for ${config.fileName}.c + private getLineEnding(): string { + const eolSetting = vscode.workspace.getConfiguration('files').get('eol'); + return (eolSetting === '\n' || eolSetting === '\r\n') ? eolSetting : os.EOL; + } -#endif // ${config.headerGuard} -`; + private detectLanguage(): 'c' | 'cpp' { + const activeEditor = vscode.window.activeTextEditor; + return (activeEditor && !activeEditor.document.isUntitled && activeEditor.document.languageId === 'c') ? 'c' : 'cpp'; + } - const sourceTemplate = `#include "${config.fileName}.h" + private toPascalCase(input: string): string { + return input.split(/[-_]/).map(word => word.charAt(0).toUpperCase() + word.slice(1)).join(''); + } -// Function implementations for ${config.fileName}.c -`; + private getPlaceholder(templateType: TemplateType): string { + const activeEditor = vscode.window.activeTextEditor; + if (activeEditor && !activeEditor.document.isUntitled) { + const currentFileName = path.basename(activeEditor.document.fileName, path.extname(activeEditor.document.fileName)); + if (templateType === TEMPLATE_KEYS.C_FILES || templateType === TEMPLATE_KEYS.C_STRUCT) { + return currentFileName; + } + return this.toPascalCase(currentFileName); + } - return { - header: headerTemplate.replace(/\n/g, config.eol), - source: sourceTemplate.replace(/\n/g, config.eol) - }; + switch (templateType) { + case TEMPLATE_KEYS.C_FILES: return DEFAULT_PLACEHOLDERS.C_FILES; + case TEMPLATE_KEYS.C_STRUCT: return DEFAULT_PLACEHOLDERS.C_STRUCT; + case TEMPLATE_KEYS.CPP_CLASS: return DEFAULT_PLACEHOLDERS.CPP_CLASS; + case TEMPLATE_KEYS.CPP_STRUCT: return DEFAULT_PLACEHOLDERS.CPP_STRUCT; + default: return DEFAULT_PLACEHOLDERS.CPP_FILES; + } } - // Generates C++ language templates - private generateCppTemplates(config: TemplateConfig): { header: string; source: string } { - const headerTemplate = `#ifndef ${config.headerGuard} -#define ${config.headerGuard} + private async promptForTemplateType(language: 'c' | 'cpp'): Promise { + const cppOrder: TemplateType[] = [TEMPLATE_KEYS.CPP_FILES, TEMPLATE_KEYS.CPP_CLASS, TEMPLATE_KEYS.CPP_STRUCT, TEMPLATE_KEYS.C_FILES, TEMPLATE_KEYS.C_STRUCT]; + const cOrder: TemplateType[] = [TEMPLATE_KEYS.C_FILES, TEMPLATE_KEYS.C_STRUCT, TEMPLATE_KEYS.CPP_FILES, TEMPLATE_KEYS.CPP_CLASS, TEMPLATE_KEYS.CPP_STRUCT]; + const desiredOrder = language === 'c' ? cOrder : cppOrder; -class ${config.fileName} { -public: - ${config.fileName}(); - ~${config.fileName}(); + const orderedChoices = [...TEMPLATE_CHOICES].sort((a, b) => desiredOrder.indexOf(a.key) - desiredOrder.indexOf(b.key)); + orderedChoices.forEach((choice, index) => choice.picked = index === 0); -private: - // Add private members here -}; + const result = await vscode.window.showQuickPick(orderedChoices, { + placeHolder: 'Please select the type of files to create.', + title: 'Create Pair: Step 1 of 2' + }); + return result?.key; + } -#endif // ${config.headerGuard} -`; + private validateIdentifier(text: string): string | null { + if (!text?.trim()) return 'Name cannot be empty.'; + if (!VALIDATION_PATTERNS.IDENTIFIER.test(text)) return 'Invalid C/C++ identifier.'; + return null; + } - const sourceTemplate = `#include "${config.fileName}.h" + private async promptForFileName(templateType: TemplateType): Promise { + let prompt: string; + switch (templateType) { + case TEMPLATE_KEYS.C_FILES: prompt = 'Please enter the name for the new C files.'; break; + case TEMPLATE_KEYS.C_STRUCT: prompt = 'Please enter the struct name for the new C files.'; break; + case TEMPLATE_KEYS.CPP_CLASS: prompt = 'Please enter the class name.'; break; + case TEMPLATE_KEYS.CPP_STRUCT: prompt = 'Please enter the struct name.'; break; + default: prompt = 'Please enter the base name for the new C++ files.'; break; + } -${config.fileName}::${config.fileName}() { - // Constructor implementation -} + return vscode.window.showInputBox({ + prompt, + placeHolder: this.getPlaceholder(templateType), + validateInput: this.validateIdentifier, + title: 'Create Pair: Step 2 of 2' + }); + } -${config.fileName}::~${config.fileName}() { - // Destructor implementation -} -`; + private generateFileContent(fileName: string, eol: string, templateType: TemplateType): { headerContent: string, sourceContent: string } { + const headerGuard = `${fileName.toUpperCase()}_H_`; + const includeLine = `#include "${fileName}.h"`; + const headerGuardBlock = `#ifndef ${headerGuard}\n#define ${headerGuard}`; + const endifLine = `\n#endif // ${headerGuard}\n`; + + let headerTemplate: string, sourceTemplate: string; + + switch (templateType) { + case TEMPLATE_KEYS.C_FILES: + headerTemplate = `${headerGuardBlock}\n\n// Function declarations for ${fileName}.c\n${endifLine}`; + sourceTemplate = `${includeLine}\n\n// Function implementations for ${fileName}.c\n`; + break; + case TEMPLATE_KEYS.C_STRUCT: + case TEMPLATE_KEYS.CPP_STRUCT: + headerTemplate = `${headerGuardBlock}\n\nstruct ${fileName} {\n // Struct members\n};\n${endifLine}`; + sourceTemplate = includeLine; + break; + case TEMPLATE_KEYS.CPP_CLASS: + headerTemplate = `${headerGuardBlock}\n\nclass ${fileName} {\npublic:\n ${fileName}();\n ~${fileName}();\n\nprivate:\n // Add private members here\n};\n${endifLine}`; + sourceTemplate = `${includeLine}\n\n${fileName}::${fileName}() {\n // Constructor implementation\n}\n\n${fileName}::~${fileName}() {\n // Destructor implementation\n}\n`; + break; + default: // CPP_FILES + headerTemplate = `${headerGuardBlock}\n\n// Declarations for ${fileName}\n${endifLine}`; + sourceTemplate = includeLine; + break; + } return { - header: headerTemplate.replace(/\n/g, config.eol), - source: sourceTemplate.replace(/\n/g, config.eol) + headerContent: headerTemplate.replace(/\n/g, eol), + sourceContent: sourceTemplate.replace(/\n/g, eol) }; } - // Generates file templates based on language type - private generateTemplates(config: TemplateConfig): { header: string; source: string } { - return config.isC - ? this.generateCTemplates(config) - : this.generateCppTemplates(config); - } - - // Prepares file generation data - private prepareFileGeneration( - fileName: string, - targetDirectory: vscode.Uri, - isC: boolean - ): FileGenerationResult { - const sourceExt = isC ? FILE_EXTENSIONS.C_SOURCE : FILE_EXTENSIONS.CPP_SOURCE; - const headerPath = vscode.Uri.file( - path.join(targetDirectory.fsPath, `${fileName}${FILE_EXTENSIONS.HEADER}`) - ); - const sourcePath = vscode.Uri.file( - path.join(targetDirectory.fsPath, `${fileName}${sourceExt}`) - ); - - const config: TemplateConfig = { - fileName, - headerGuard: `${fileName.toUpperCase()}_H_`, - eol: this.getLineEnding(), - isC - }; - - const templates = this.generateTemplates(config); + private async checkFileExistence(headerPath: vscode.Uri, sourcePath: vscode.Uri): Promise { + const checks = await Promise.allSettled([ + vscode.workspace.fs.stat(headerPath), + vscode.workspace.fs.stat(sourcePath) + ]); - return { - headerPath, - sourcePath, - headerContent: templates.header, - sourceContent: templates.source - }; + if (checks[0].status === 'fulfilled') return headerPath.fsPath; + if (checks[1].status === 'fulfilled') return sourcePath.fsPath; + return null; } - // Writes files to the filesystem - private async writeFiles(generation: FileGenerationResult): Promise { + private async writeFiles(headerPath: vscode.Uri, sourcePath: vscode.Uri, headerContent: string, sourceContent: string): Promise { try { await Promise.all([ - vscode.workspace.fs.writeFile( - generation.headerPath, - Buffer.from(generation.headerContent, 'utf8') - ), - vscode.workspace.fs.writeFile( - generation.sourcePath, - Buffer.from(generation.sourceContent, 'utf8') - ) + vscode.workspace.fs.writeFile(headerPath, Buffer.from(headerContent, 'utf8')), + vscode.workspace.fs.writeFile(sourcePath, Buffer.from(sourceContent, 'utf8')) ]); } catch (error: any) { - throw new Error(`Failed to create files: ${error.message}`); + throw new Error(`Failed to create files: ${error.message}.`); } } - // Opens the created header file and shows success message - private async finalizeCreation(generation: FileGenerationResult): Promise { - const fileName = path.basename(generation.headerPath.fsPath, FILE_EXTENSIONS.HEADER); - const sourceExt = path.extname(generation.sourcePath.fsPath); - - await vscode.window.showTextDocument( - await vscode.workspace.openTextDocument(generation.headerPath) - ); - - await vscode.window.showInformationMessage( - `Successfully created ${fileName}${FILE_EXTENSIONS.HEADER} and ${fileName}${sourceExt}` - ); - } - - // The core implementation of the command. - // It handles user input, file creation, and opening the new file. - public async create(): Promise { - try { - // Step 1: Get target directory - const targetDirectory = await this.getTargetDirectory(); - if (!targetDirectory) { - vscode.window.showErrorMessage( - 'Could not determine a target directory. Please open a folder or a file first.' - ); - return; - } - - // Step 2: Detect language - const language = this.detectLanguage(); - const isC = language === LANGUAGE_TYPES.C; - - // Step 3: Get file name from user - const fileName = await this.promptForFileName(isC); - if (!fileName) { - return; // User canceled the input - } - - // Step 4: Prepare file generation data - const generation = this.prepareFileGeneration(fileName, targetDirectory, isC); - - // Step 5: Check if files already exist - const filesExist = await this.checkFileExistence(generation.headerPath, generation.sourcePath); - if (filesExist) { - return; - } - - // Step 6: Write files - await this.writeFiles(generation); - - // Step 7: Finalize creation (open file and show message) - await this.finalizeCreation(generation); - - } catch (error: any) { - vscode.window.showErrorMessage(error.message || 'An unexpected error occurred.'); - } + private async finalizeCreation(headerPath: vscode.Uri, sourcePath: vscode.Uri): Promise { + await vscode.window.showTextDocument(await vscode.workspace.openTextDocument(headerPath)); + await vscode.window.showInformationMessage(`Successfully created ${path.basename(headerPath.fsPath)} and ${path.basename(sourcePath.fsPath)}.`); } - // Helper method to intelligently determine the directory for new files. private async getTargetDirectory(): Promise { - // Try to use the directory of the currently active file const activeEditor = vscode.window.activeTextEditor; if (activeEditor && !activeEditor.document.isUntitled) { return vscode.Uri.file(path.dirname(activeEditor.document.uri.fsPath)); } - // Fall back to workspace folders const workspaceFolders = vscode.workspace.workspaceFolders; - if (!workspaceFolders || workspaceFolders.length === 0) { - return undefined; - } - - // If there's only one workspace folder, use it - if (workspaceFolders.length === 1) { + if (workspaceFolders?.length === 1) { return workspaceFolders[0].uri; } - // If there are multiple workspace folders, let the user choose - const picked = await vscode.window.showWorkspaceFolderPick({ - placeHolder: 'Please select a workspace folder to create the files in' - }); + if (workspaceFolders && workspaceFolders.length > 1) { + const picked = await vscode.window.showWorkspaceFolderPick({ placeHolder: 'Please select a workspace folder to create the files in.' }); + return picked?.uri; + } - return picked?.uri; + return undefined; } } -// Registers the command and lifecycle handler for the create pair feature. export function registerCreateSourceHeaderPairCommand(context: ClangdContext) { context.subscriptions.push(new PairCreator()); } \ No newline at end of file From 605aab994ccd8b214ab51ca6eb053d2c62199473 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?=E9=95=BF=E6=9C=9F=E7=B4=A0=E9=A3=9F?= Date: Wed, 16 Jul 2025 20:54:04 +0800 Subject: [PATCH 04/34] Update create-source-header-pair.ts --- src/create-source-header-pair.ts | 10 +++++----- 1 file changed, 5 insertions(+), 5 deletions(-) diff --git a/src/create-source-header-pair.ts b/src/create-source-header-pair.ts index 0f7be72a..969ad053 100644 --- a/src/create-source-header-pair.ts +++ b/src/create-source-header-pair.ts @@ -34,11 +34,11 @@ const TEMPLATE_KEYS = { type TemplateType = typeof TEMPLATE_KEYS[keyof typeof TEMPLATE_KEYS]; const TEMPLATE_CHOICES: ReadonlyArray = [ - { label: '$(new-file) C++ Files', description: 'Create an empty C++ header/source pair.', key: TEMPLATE_KEYS.CPP_FILES }, - { label: '$(symbol-class) C++ Class', description: 'Create a C++ header/source pair with a class definition.', key: TEMPLATE_KEYS.CPP_CLASS }, - { label: '$(symbol-struct) C++ Struct', description: 'Create a C++ header/source pair with a struct definition.', key: TEMPLATE_KEYS.CPP_STRUCT }, - { label: '$(file-code) C Files', description: 'Create a standard C header/source pair for functions.', key: TEMPLATE_KEYS.C_FILES }, - { label: '$(symbol-struct) C Struct', description: 'Create a standard C header/source pair with a struct definition.', key: TEMPLATE_KEYS.C_STRUCT } + { label: '$(new-file) C++ Source Pair', description: 'Creates a C++ header/source pair with header guards.', key: TEMPLATE_KEYS.CPP_FILES }, + { label: '$(symbol-class) C++ Class Pair', description: 'Creates a C++ header/source pair with a class definition.', key: TEMPLATE_KEYS.CPP_CLASS }, + { label: '$(symbol-struct) C++ Struct Pair', description: 'Creates a C++ header/source pair with a struct definition.', key: TEMPLATE_KEYS.CPP_STRUCT }, + { label: '$(file-code) C Files Pair', description: 'Creates a standard C header/source pair for functions.', key: TEMPLATE_KEYS.C_FILES }, + { label: '$(symbol-struct) C Struct Pair', description: 'Creates a standard C header/source pair with a struct definition.', key: TEMPLATE_KEYS.C_STRUCT } ]; // --- Main Class --- From e84f24da0baf448645952e7bb2df7414cb2a3db2 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?=E9=95=BF=E6=9C=9F=E7=B4=A0=E9=A3=9F?= Date: Wed, 16 Jul 2025 23:27:47 +0800 Subject: [PATCH 05/34] Refactor file pair creation logic and templates Replaces the old template and file extension logic with a unified PairingRule structure, simplifying template selection and file generation for C and C++ header/source pairs. Improves language detection, user prompts, and file content generation, making the code more maintainable and user-friendly. --- src/create-source-header-pair.ts | 246 ++++++++++++++++++------------- 1 file changed, 140 insertions(+), 106 deletions(-) diff --git a/src/create-source-header-pair.ts b/src/create-source-header-pair.ts index 969ad053..f71ddf59 100644 --- a/src/create-source-header-pair.ts +++ b/src/create-source-header-pair.ts @@ -6,39 +6,35 @@ import { ClangdContext } from './clangd-context'; // --- Constants and Types --- -const FILE_EXTENSIONS = { - HEADER: '.h', - C_SOURCE: '.c', - CPP_SOURCE: '.cpp' -} as const; - const VALIDATION_PATTERNS = { IDENTIFIER: /^[a-zA-Z_][a-zA-Z0-9_]*$/ } as const; const DEFAULT_PLACEHOLDERS = { - C_FILES: 'my_c_functions', + C_EMPTY: 'my_c_functions', C_STRUCT: 'MyStruct', - CPP_FILES: 'utils', + CPP_EMPTY: 'utils', CPP_CLASS: 'MyClass', CPP_STRUCT: 'MyStruct' } as const; -const TEMPLATE_KEYS = { - CPP_FILES: 'cpp_files', - CPP_CLASS: 'cpp_class', - CPP_STRUCT: 'cpp_struct', - C_FILES: 'c_files', - C_STRUCT: 'c_struct' -} as const; -type TemplateType = typeof TEMPLATE_KEYS[keyof typeof TEMPLATE_KEYS]; - -const TEMPLATE_CHOICES: ReadonlyArray = [ - { label: '$(new-file) C++ Source Pair', description: 'Creates a C++ header/source pair with header guards.', key: TEMPLATE_KEYS.CPP_FILES }, - { label: '$(symbol-class) C++ Class Pair', description: 'Creates a C++ header/source pair with a class definition.', key: TEMPLATE_KEYS.CPP_CLASS }, - { label: '$(symbol-struct) C++ Struct Pair', description: 'Creates a C++ header/source pair with a struct definition.', key: TEMPLATE_KEYS.CPP_STRUCT }, - { label: '$(file-code) C Files Pair', description: 'Creates a standard C header/source pair for functions.', key: TEMPLATE_KEYS.C_FILES }, - { label: '$(symbol-struct) C Struct Pair', description: 'Creates a standard C header/source pair with a struct definition.', key: TEMPLATE_KEYS.C_STRUCT } +interface PairingRule { + key: string; + label: string; + description: string; + language: 'c' | 'cpp'; + headerExt: string; + sourceExt: string; + isClass?: boolean; + isStruct?: boolean; +} + +const TEMPLATE_RULES: ReadonlyArray = [ + { key: 'cpp_empty', label: 'New Empty C++ Pair', description: 'Creates .h/.cpp files with header guards.', language: 'cpp', headerExt: '.h', sourceExt: '.cpp' }, + { key: 'cpp_class', label: 'New C++ Class', description: 'Creates .h/.cpp files for a C++ class.', language: 'cpp', headerExt: '.h', sourceExt: '.cpp', isClass: true }, + { key: 'cpp_struct', label: 'New C++ Struct', description: 'Creates .h/.cpp files for a C++ struct.', language: 'cpp', headerExt: '.h', sourceExt: '.cpp', isStruct: true }, + { key: 'c_empty', label: 'New Empty C Pair', description: 'Creates .h/.c files for C functions.', language: 'c', headerExt: '.h', sourceExt: '.c' }, + { key: 'c_struct', label: 'New C Struct', description: 'Creates .h/.c files for a C struct (using typedef).', language: 'c', headerExt: '.h', sourceExt: '.c', isStruct: true } ]; // --- Main Class --- @@ -56,21 +52,22 @@ class PairCreator implements vscode.Disposable { try { const targetDirectory = await this.getTargetDirectory(); if (!targetDirectory) { - vscode.window.showErrorMessage('Could not determine a target directory. Please open a folder or a file first.'); + vscode.window.showErrorMessage('Cannot determine target directory. Please open a folder or a file first.'); return; } - const language = this.detectLanguage(); - const templateType = await this.promptForTemplateType(language); - if (!templateType) return; + // Step 2: Perform language detection once to get all context. + const { language, uncertain } = await this.detectLanguage(); + + // Step 3: Prompt for the rule, passing all context. + const rule = await this.promptForPairingRule(language, uncertain); + if (!rule) return; - const fileName = await this.promptForFileName(templateType); + const fileName = await this.promptForFileName(rule); if (!fileName) return; - const isC = templateType === TEMPLATE_KEYS.C_FILES || templateType === TEMPLATE_KEYS.C_STRUCT; - const sourceExt = isC ? FILE_EXTENSIONS.C_SOURCE : FILE_EXTENSIONS.CPP_SOURCE; - const headerPath = vscode.Uri.file(path.join(targetDirectory.fsPath, `${fileName}${FILE_EXTENSIONS.HEADER}`)); - const sourcePath = vscode.Uri.file(path.join(targetDirectory.fsPath, `${fileName}${sourceExt}`)); + const headerPath = vscode.Uri.file(path.join(targetDirectory.fsPath, `${fileName}${rule.headerExt}`)); + const sourcePath = vscode.Uri.file(path.join(targetDirectory.fsPath, `${fileName}${rule.sourceExt}`)); const existingFilePath = await this.checkFileExistence(headerPath, sourcePath); if (existingFilePath) { @@ -79,7 +76,7 @@ class PairCreator implements vscode.Disposable { } const eol = this.getLineEnding(); - const { headerContent, sourceContent } = this.generateFileContent(fileName, eol, templateType); + const { headerContent, sourceContent } = this.generateFileContent(fileName, eol, rule); await this.writeFiles(headerPath, sourcePath, headerContent, sourceContent); await this.finalizeCreation(headerPath, sourcePath); @@ -96,99 +93,141 @@ class PairCreator implements vscode.Disposable { return (eolSetting === '\n' || eolSetting === '\r\n') ? eolSetting : os.EOL; } - private detectLanguage(): 'c' | 'cpp' { + // --- START: The Refined, Unified detectLanguage Method --- + private async detectLanguage(): Promise<{ language: 'c' | 'cpp', uncertain: boolean }> { const activeEditor = vscode.window.activeTextEditor; - return (activeEditor && !activeEditor.document.isUntitled && activeEditor.document.languageId === 'c') ? 'c' : 'cpp'; + if (!activeEditor || activeEditor.document.isUntitled) { + return { language: 'cpp', uncertain: true }; + } + + const document = activeEditor.document; + const langId = document.languageId; + const filePath = document.uri.fsPath; + const ext = path.extname(filePath); + + // Strategy 1: Trust definitive source file language IDs + if (ext === '.c') return { language: 'c', uncertain: false }; + if (ext === '.cpp' || ext === '.cc' || ext === '.cxx') return { language: 'cpp', uncertain: false }; + + // Strategy 2: For header files, infer from companion files + if (ext === '.h') { + const baseName = path.basename(filePath, '.h'); + const dirPath = path.dirname(filePath); + + const companionChecks = [ + path.join(dirPath, `${baseName}.c`), + path.join(dirPath, `${baseName}.cpp`), + path.join(dirPath, `${baseName}.cc`), + path.join(dirPath, `${baseName}.cxx`) + ]; + + const results = await Promise.allSettled( + companionChecks.map(file => vscode.workspace.fs.stat(vscode.Uri.file(file))) + ); + + if (results[0].status === 'fulfilled') return { language: 'c', uncertain: false }; + if (results.slice(1).some(r => r.status === 'fulfilled')) return { language: 'cpp', uncertain: false }; + + // If we are in a header file with no companion, the context is uncertain. + return { language: 'cpp', uncertain: true }; + } + + // Strategy 3: Fallback using the potentially unreliable language ID + return { language: (langId === 'c' ? 'c' : 'cpp'), uncertain: true }; } + // --- END: The Refined, Unified detectLanguage Method --- private toPascalCase(input: string): string { return input.split(/[-_]/).map(word => word.charAt(0).toUpperCase() + word.slice(1)).join(''); } - private getPlaceholder(templateType: TemplateType): string { + private getPlaceholder(rule: PairingRule): string { const activeEditor = vscode.window.activeTextEditor; if (activeEditor && !activeEditor.document.isUntitled) { const currentFileName = path.basename(activeEditor.document.fileName, path.extname(activeEditor.document.fileName)); - if (templateType === TEMPLATE_KEYS.C_FILES || templateType === TEMPLATE_KEYS.C_STRUCT) { - return currentFileName; - } + if (rule.language === 'c') return currentFileName; return this.toPascalCase(currentFileName); } - switch (templateType) { - case TEMPLATE_KEYS.C_FILES: return DEFAULT_PLACEHOLDERS.C_FILES; - case TEMPLATE_KEYS.C_STRUCT: return DEFAULT_PLACEHOLDERS.C_STRUCT; - case TEMPLATE_KEYS.CPP_CLASS: return DEFAULT_PLACEHOLDERS.CPP_CLASS; - case TEMPLATE_KEYS.CPP_STRUCT: return DEFAULT_PLACEHOLDERS.CPP_STRUCT; - default: return DEFAULT_PLACEHOLDERS.CPP_FILES; - } + if (rule.isClass) return DEFAULT_PLACEHOLDERS.CPP_CLASS; + if (rule.isStruct && rule.language === 'cpp') return DEFAULT_PLACEHOLDERS.CPP_STRUCT; + if (rule.isStruct && rule.language === 'c') return DEFAULT_PLACEHOLDERS.C_STRUCT; + if (rule.language === 'c') return DEFAULT_PLACEHOLDERS.C_EMPTY; + return DEFAULT_PLACEHOLDERS.CPP_EMPTY; } - private async promptForTemplateType(language: 'c' | 'cpp'): Promise { - const cppOrder: TemplateType[] = [TEMPLATE_KEYS.CPP_FILES, TEMPLATE_KEYS.CPP_CLASS, TEMPLATE_KEYS.CPP_STRUCT, TEMPLATE_KEYS.C_FILES, TEMPLATE_KEYS.C_STRUCT]; - const cOrder: TemplateType[] = [TEMPLATE_KEYS.C_FILES, TEMPLATE_KEYS.C_STRUCT, TEMPLATE_KEYS.CPP_FILES, TEMPLATE_KEYS.CPP_CLASS, TEMPLATE_KEYS.CPP_STRUCT]; - const desiredOrder = language === 'c' ? cOrder : cppOrder; + // --- START: The Refined, Simplified promptForPairingRule Method --- + private async promptForPairingRule(language: 'c' | 'cpp', uncertain: boolean): Promise { + let desiredOrder: string[]; + + if (uncertain) { + // For orphan headers, present a neutral, user-friendly order. + desiredOrder = ['cpp_empty', 'c_empty', 'cpp_class', 'cpp_struct', 'c_struct']; + } else if (language === 'c') { + // For C context, prioritize C options. + desiredOrder = ['c_empty', 'c_struct', 'cpp_empty', 'cpp_class', 'cpp_struct']; + } else { + // For C++ context, prioritize C++ options. + desiredOrder = ['cpp_empty', 'cpp_class', 'cpp_struct', 'c_empty', 'c_struct']; + } + + const choices = [...TEMPLATE_RULES].sort((a, b) => desiredOrder.indexOf(a.key) - desiredOrder.indexOf(b.key)); - const orderedChoices = [...TEMPLATE_CHOICES].sort((a, b) => desiredOrder.indexOf(a.key) - desiredOrder.indexOf(b.key)); - orderedChoices.forEach((choice, index) => choice.picked = index === 0); + // Attach the QuickPickItem properties directly to our rule objects + const quickPickItems = choices.map(rule => ({ ...rule, label: `$(file-code) ${rule.label}` })); // Example to ensure it's a QuickPickItem - const result = await vscode.window.showQuickPick(orderedChoices, { - placeHolder: 'Please select the type of files to create.', + // Find the default choice and mark it as picked + if (quickPickItems.length > 0) { + (quickPickItems[0] as any).picked = true; + } + + const result = await vscode.window.showQuickPick(quickPickItems, { + placeHolder: 'Please select the type of file pair to create.', title: 'Create Pair: Step 1 of 2' }); - return result?.key; - } - private validateIdentifier(text: string): string | null { - if (!text?.trim()) return 'Name cannot be empty.'; - if (!VALIDATION_PATTERNS.IDENTIFIER.test(text)) return 'Invalid C/C++ identifier.'; - return null; + // The result itself is one of our enriched QuickPickItem objects, which is a PairingRule. + return result as PairingRule | undefined; } + // --- END: The Refined, Simplified promptForPairingRule Method --- - private async promptForFileName(templateType: TemplateType): Promise { - let prompt: string; - switch (templateType) { - case TEMPLATE_KEYS.C_FILES: prompt = 'Please enter the name for the new C files.'; break; - case TEMPLATE_KEYS.C_STRUCT: prompt = 'Please enter the struct name for the new C files.'; break; - case TEMPLATE_KEYS.CPP_CLASS: prompt = 'Please enter the class name.'; break; - case TEMPLATE_KEYS.CPP_STRUCT: prompt = 'Please enter the struct name.'; break; - default: prompt = 'Please enter the base name for the new C++ files.'; break; - } + private async promptForFileName(rule: PairingRule): Promise { + let prompt = 'Please enter a name.'; + if (rule.isClass) prompt = 'Please enter the name for the new C++ class.'; + else if (rule.isStruct) prompt = `Please enter the name for the new ${rule.language.toUpperCase()} struct.`; + else prompt = `Please enter the base name for the new ${rule.language.toUpperCase()} file pair.`; return vscode.window.showInputBox({ prompt, - placeHolder: this.getPlaceholder(templateType), - validateInput: this.validateIdentifier, + placeHolder: this.getPlaceholder(rule), + validateInput: (text) => VALIDATION_PATTERNS.IDENTIFIER.test(text?.trim() || '') ? null : 'Invalid C/C++ identifier.', title: 'Create Pair: Step 2 of 2' }); } - private generateFileContent(fileName: string, eol: string, templateType: TemplateType): { headerContent: string, sourceContent: string } { + private generateFileContent(fileName: string, eol: string, rule: PairingRule): { headerContent: string, sourceContent: string } { const headerGuard = `${fileName.toUpperCase()}_H_`; - const includeLine = `#include "${fileName}.h"`; + const includeLine = `#include "${fileName}${rule.headerExt}"`; const headerGuardBlock = `#ifndef ${headerGuard}\n#define ${headerGuard}`; const endifLine = `\n#endif // ${headerGuard}\n`; let headerTemplate: string, sourceTemplate: string; - switch (templateType) { - case TEMPLATE_KEYS.C_FILES: - headerTemplate = `${headerGuardBlock}\n\n// Function declarations for ${fileName}.c\n${endifLine}`; - sourceTemplate = `${includeLine}\n\n// Function implementations for ${fileName}.c\n`; - break; - case TEMPLATE_KEYS.C_STRUCT: - case TEMPLATE_KEYS.CPP_STRUCT: - headerTemplate = `${headerGuardBlock}\n\nstruct ${fileName} {\n // Struct members\n};\n${endifLine}`; - sourceTemplate = includeLine; - break; - case TEMPLATE_KEYS.CPP_CLASS: - headerTemplate = `${headerGuardBlock}\n\nclass ${fileName} {\npublic:\n ${fileName}();\n ~${fileName}();\n\nprivate:\n // Add private members here\n};\n${endifLine}`; - sourceTemplate = `${includeLine}\n\n${fileName}::${fileName}() {\n // Constructor implementation\n}\n\n${fileName}::~${fileName}() {\n // Destructor implementation\n}\n`; - break; - default: // CPP_FILES - headerTemplate = `${headerGuardBlock}\n\n// Declarations for ${fileName}\n${endifLine}`; - sourceTemplate = includeLine; - break; + if (rule.isClass) { + headerTemplate = `${headerGuardBlock}\n\nclass ${fileName} {\npublic:\n ${fileName}();\n ~${fileName}();\n\nprivate:\n // Add private members here\n};\n${endifLine}`; + sourceTemplate = `${includeLine}\n\n${fileName}::${fileName}() {\n // Constructor implementation\n}\n\n${fileName}::~${fileName}() {\n // Destructor implementation\n}\n`; + } else if (rule.isStruct && rule.language === 'cpp') { + headerTemplate = `${headerGuardBlock}\n\nstruct ${fileName} {\n // Struct members\n};\n${endifLine}`; + sourceTemplate = includeLine; + } else if (rule.isStruct && rule.language === 'c') { + headerTemplate = `${headerGuardBlock}\n\ntypedef struct {\n // Struct members\n} ${fileName};\n${endifLine}`; + sourceTemplate = includeLine; + } else if (rule.language === 'c') { + headerTemplate = `${headerGuardBlock}\n\n// Declarations for ${fileName}.c\n${endifLine}`; + sourceTemplate = `${includeLine}\n\n// Implementations for ${fileName}.c\n`; + } else { // C++ Empty + headerTemplate = `${headerGuardBlock}\n\n// Declarations for ${fileName}.cpp\n${endifLine}`; + sourceTemplate = includeLine; } return { @@ -198,13 +237,13 @@ class PairCreator implements vscode.Disposable { } private async checkFileExistence(headerPath: vscode.Uri, sourcePath: vscode.Uri): Promise { - const checks = await Promise.allSettled([ - vscode.workspace.fs.stat(headerPath), - vscode.workspace.fs.stat(sourcePath) - ]); - - if (checks[0].status === 'fulfilled') return headerPath.fsPath; - if (checks[1].status === 'fulfilled') return sourcePath.fsPath; + const pathsToCheck = [headerPath, sourcePath]; + for (const filePath of pathsToCheck) { + try { + await vscode.workspace.fs.stat(filePath); + return filePath.fsPath; + } catch { } + } return null; } @@ -229,17 +268,12 @@ class PairCreator implements vscode.Disposable { if (activeEditor && !activeEditor.document.isUntitled) { return vscode.Uri.file(path.dirname(activeEditor.document.uri.fsPath)); } - const workspaceFolders = vscode.workspace.workspaceFolders; - if (workspaceFolders?.length === 1) { - return workspaceFolders[0].uri; - } - + if (workspaceFolders?.length === 1) return workspaceFolders[0].uri; if (workspaceFolders && workspaceFolders.length > 1) { - const picked = await vscode.window.showWorkspaceFolderPick({ placeHolder: 'Please select a workspace folder to create the files in.' }); - return picked?.uri; + const selectedFolder = await vscode.window.showWorkspaceFolderPick({ placeHolder: 'Please select a workspace folder for the new files.' }); + return selectedFolder?.uri; } - return undefined; } } From 1c0cfd9080c5d1a87b48fedc53424f1befaabf0c Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?=E9=95=BF=E6=9C=9F=E7=B4=A0=E9=A3=9F?= Date: Wed, 16 Jul 2025 23:51:11 +0800 Subject: [PATCH 06/34] Update create-source-header-pair.ts --- src/create-source-header-pair.ts | 144 +++++++++++++++++++++++-------- 1 file changed, 109 insertions(+), 35 deletions(-) diff --git a/src/create-source-header-pair.ts b/src/create-source-header-pair.ts index f71ddf59..2157dba9 100644 --- a/src/create-source-header-pair.ts +++ b/src/create-source-header-pair.ts @@ -171,23 +171,16 @@ class PairCreator implements vscode.Disposable { desiredOrder = ['cpp_empty', 'cpp_class', 'cpp_struct', 'c_empty', 'c_struct']; } - const choices = [...TEMPLATE_RULES].sort((a, b) => desiredOrder.indexOf(a.key) - desiredOrder.indexOf(b.key)); + const choices = [...TEMPLATE_RULES] + .sort((a, b) => desiredOrder.indexOf(a.key) - desiredOrder.indexOf(b.key)) + .map(rule => ({ ...rule, label: `$(file-code) ${rule.label}` })); - // Attach the QuickPickItem properties directly to our rule objects - const quickPickItems = choices.map(rule => ({ ...rule, label: `$(file-code) ${rule.label}` })); // Example to ensure it's a QuickPickItem - - // Find the default choice and mark it as picked - if (quickPickItems.length > 0) { - (quickPickItems[0] as any).picked = true; - } - - const result = await vscode.window.showQuickPick(quickPickItems, { + const result = await vscode.window.showQuickPick(choices, { placeHolder: 'Please select the type of file pair to create.', - title: 'Create Pair: Step 1 of 2' + title: 'Create Pair - Step 1 of 2' }); - // The result itself is one of our enriched QuickPickItem objects, which is a PairingRule. - return result as PairingRule | undefined; + return result; } // --- END: The Refined, Simplified promptForPairingRule Method --- @@ -201,41 +194,122 @@ class PairCreator implements vscode.Disposable { prompt, placeHolder: this.getPlaceholder(rule), validateInput: (text) => VALIDATION_PATTERNS.IDENTIFIER.test(text?.trim() || '') ? null : 'Invalid C/C++ identifier.', - title: 'Create Pair: Step 2 of 2' + title: 'Create Pair - Step 2 of 2' }); } private generateFileContent(fileName: string, eol: string, rule: PairingRule): { headerContent: string, sourceContent: string } { - const headerGuard = `${fileName.toUpperCase()}_H_`; - const includeLine = `#include "${fileName}${rule.headerExt}"`; - const headerGuardBlock = `#ifndef ${headerGuard}\n#define ${headerGuard}`; - const endifLine = `\n#endif // ${headerGuard}\n`; + const templates = this.getTemplatesByRule(rule); + const context = { + fileName, + headerGuard: `${fileName.toUpperCase()}_H_`, + includeLine: `#include "${fileName}${rule.headerExt}"` + }; - let headerTemplate: string, sourceTemplate: string; + const headerContent = this.applyTemplate(templates.header, context); + const sourceContent = this.applyTemplate(templates.source, context); + return { + headerContent: headerContent.replace(/\n/g, eol), + sourceContent: sourceContent.replace(/\n/g, eol) + }; + } + + private getTemplatesByRule(rule: PairingRule): { header: string, source: string } { if (rule.isClass) { - headerTemplate = `${headerGuardBlock}\n\nclass ${fileName} {\npublic:\n ${fileName}();\n ~${fileName}();\n\nprivate:\n // Add private members here\n};\n${endifLine}`; - sourceTemplate = `${includeLine}\n\n${fileName}::${fileName}() {\n // Constructor implementation\n}\n\n${fileName}::~${fileName}() {\n // Destructor implementation\n}\n`; - } else if (rule.isStruct && rule.language === 'cpp') { - headerTemplate = `${headerGuardBlock}\n\nstruct ${fileName} {\n // Struct members\n};\n${endifLine}`; - sourceTemplate = includeLine; - } else if (rule.isStruct && rule.language === 'c') { - headerTemplate = `${headerGuardBlock}\n\ntypedef struct {\n // Struct members\n} ${fileName};\n${endifLine}`; - sourceTemplate = includeLine; - } else if (rule.language === 'c') { - headerTemplate = `${headerGuardBlock}\n\n// Declarations for ${fileName}.c\n${endifLine}`; - sourceTemplate = `${includeLine}\n\n// Implementations for ${fileName}.c\n`; - } else { // C++ Empty - headerTemplate = `${headerGuardBlock}\n\n// Declarations for ${fileName}.cpp\n${endifLine}`; - sourceTemplate = includeLine; + return { + header: `#ifndef {{headerGuard}} +#define {{headerGuard}} + +class {{fileName}} { +public: + {{fileName}}(); + ~{{fileName}}(); + +private: + // Add private members here +}; + +#endif // {{headerGuard}} +`, + source: `{{includeLine}} + +{{fileName}}::{{fileName}}() { + // Constructor implementation +} + +{{fileName}}::~{{fileName}}() { + // Destructor implementation +} +` + }; } + if (rule.isStruct && rule.language === 'cpp') { + return { + header: `#ifndef {{headerGuard}} +#define {{headerGuard}} + +struct {{fileName}} { + // Struct members +}; + +#endif // {{headerGuard}} +`, + source: '{{includeLine}}' + }; + } + + if (rule.isStruct && rule.language === 'c') { + return { + header: `#ifndef {{headerGuard}} +#define {{headerGuard}} + +typedef struct { + // Struct members +} {{fileName}}; + +#endif // {{headerGuard}} +`, + source: '{{includeLine}}' + }; + } + + if (rule.language === 'c') { + return { + header: `#ifndef {{headerGuard}} +#define {{headerGuard}} + +// Declarations for {{fileName}}.c + +#endif // {{headerGuard}} +`, + source: `{{includeLine}} + +// Implementations for {{fileName}}.c +` + }; + } + + // C++ Empty (default) return { - headerContent: headerTemplate.replace(/\n/g, eol), - sourceContent: sourceTemplate.replace(/\n/g, eol) + header: `#ifndef {{headerGuard}} +#define {{headerGuard}} + +// Declarations for {{fileName}}.cpp + +#endif // {{headerGuard}} +`, + source: '{{includeLine}}' }; } + private applyTemplate(template: string, context: Record): string { + return template.replace(/\{\{(\w+)\}\}/g, (match, key) => { + return context[key] || match; // If key exists in context, replace it. Otherwise, keep the original placeholder. + }); + } + private async checkFileExistence(headerPath: vscode.Uri, sourcePath: vscode.Uri): Promise { const pathsToCheck = [headerPath, sourcePath]; for (const filePath of pathsToCheck) { From 9663d80c2a64002cb34f1b7a77a6a66faadbe618 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?=E9=95=BF=E6=9C=9F=E7=B4=A0=E9=A3=9F?= Date: Thu, 17 Jul 2025 00:06:47 +0800 Subject: [PATCH 07/34] Update create-source-header-pair.ts --- src/create-source-header-pair.ts | 84 ++++++++++++++++++++------------ 1 file changed, 52 insertions(+), 32 deletions(-) diff --git a/src/create-source-header-pair.ts b/src/create-source-header-pair.ts index 2157dba9..69d7330a 100644 --- a/src/create-source-header-pair.ts +++ b/src/create-source-header-pair.ts @@ -6,10 +6,12 @@ import { ClangdContext } from './clangd-context'; // --- Constants and Types --- +// Regular expressions for validating user input const VALIDATION_PATTERNS = { - IDENTIFIER: /^[a-zA-Z_][a-zA-Z0-9_]*$/ + IDENTIFIER: /^[a-zA-Z_][a-zA-Z0-9_]*$/ // Valid C/C++ identifier pattern } as const; +// Default placeholder names for different template types const DEFAULT_PLACEHOLDERS = { C_EMPTY: 'my_c_functions', C_STRUCT: 'MyStruct', @@ -18,17 +20,19 @@ const DEFAULT_PLACEHOLDERS = { CPP_STRUCT: 'MyStruct' } as const; +// Defines the structure of a file pair template rule interface PairingRule { - key: string; - label: string; - description: string; - language: 'c' | 'cpp'; - headerExt: string; - sourceExt: string; - isClass?: boolean; - isStruct?: boolean; + key: string; // Unique identifier for this rule + label: string; // Human-readable name shown in UI + description: string; // Detailed description of what this rule creates + language: 'c' | 'cpp'; // Target programming language + headerExt: string; // File extension for header file (e.g., '.h') + sourceExt: string; // File extension for source file (e.g., '.cpp', '.c') + isClass?: boolean; // Whether this rule creates a class template + isStruct?: boolean; // Whether this rule creates a struct template } +// Available template rules for creating different types of file pairs const TEMPLATE_RULES: ReadonlyArray = [ { key: 'cpp_empty', label: 'New Empty C++ Pair', description: 'Creates .h/.cpp files with header guards.', language: 'cpp', headerExt: '.h', sourceExt: '.cpp' }, { key: 'cpp_class', label: 'New C++ Class', description: 'Creates .h/.cpp files for a C++ class.', language: 'cpp', headerExt: '.h', sourceExt: '.cpp', isClass: true }, @@ -39,6 +43,7 @@ const TEMPLATE_RULES: ReadonlyArray = [ // --- Main Class --- +// Main class responsible for creating header/source file pairs class PairCreator implements vscode.Disposable { private command: vscode.Disposable; @@ -48,6 +53,7 @@ class PairCreator implements vscode.Disposable { dispose() { this.command.dispose(); } + // Main entry point for creating a source/header file pair public async create(): Promise { try { const targetDirectory = await this.getTargetDirectory(); @@ -56,10 +62,8 @@ class PairCreator implements vscode.Disposable { return; } - // Step 2: Perform language detection once to get all context. const { language, uncertain } = await this.detectLanguage(); - // Step 3: Prompt for the rule, passing all context. const rule = await this.promptForPairingRule(language, uncertain); if (!rule) return; @@ -93,7 +97,7 @@ class PairCreator implements vscode.Disposable { return (eolSetting === '\n' || eolSetting === '\r\n') ? eolSetting : os.EOL; } - // --- START: The Refined, Unified detectLanguage Method --- + // Detects the programming language context (C or C++) based on the current active file private async detectLanguage(): Promise<{ language: 'c' | 'cpp', uncertain: boolean }> { const activeEditor = vscode.window.activeTextEditor; if (!activeEditor || activeEditor.document.isUntitled) { @@ -105,20 +109,20 @@ class PairCreator implements vscode.Disposable { const filePath = document.uri.fsPath; const ext = path.extname(filePath); - // Strategy 1: Trust definitive source file language IDs + // Strategy 1: Trust definitive source file extensions if (ext === '.c') return { language: 'c', uncertain: false }; if (ext === '.cpp' || ext === '.cc' || ext === '.cxx') return { language: 'cpp', uncertain: false }; - // Strategy 2: For header files, infer from companion files + // Strategy 2: For header files (.h), infer language from companion source files if (ext === '.h') { const baseName = path.basename(filePath, '.h'); const dirPath = path.dirname(filePath); const companionChecks = [ - path.join(dirPath, `${baseName}.c`), - path.join(dirPath, `${baseName}.cpp`), - path.join(dirPath, `${baseName}.cc`), - path.join(dirPath, `${baseName}.cxx`) + path.join(dirPath, `${baseName}.c`), // C source file + path.join(dirPath, `${baseName}.cpp`), // C++ source file + path.join(dirPath, `${baseName}.cc`), // Alternative C++ extension + path.join(dirPath, `${baseName}.cxx`) // Alternative C++ extension ]; const results = await Promise.allSettled( @@ -128,23 +132,24 @@ class PairCreator implements vscode.Disposable { if (results[0].status === 'fulfilled') return { language: 'c', uncertain: false }; if (results.slice(1).some(r => r.status === 'fulfilled')) return { language: 'cpp', uncertain: false }; - // If we are in a header file with no companion, the context is uncertain. return { language: 'cpp', uncertain: true }; } - // Strategy 3: Fallback using the potentially unreliable language ID + // Strategy 3: Fallback to VS Code's language detection return { language: (langId === 'c' ? 'c' : 'cpp'), uncertain: true }; } - // --- END: The Refined, Unified detectLanguage Method --- + // Converts a string to PascalCase by splitting on hyphens and underscores private toPascalCase(input: string): string { return input.split(/[-_]/).map(word => word.charAt(0).toUpperCase() + word.slice(1)).join(''); } + // Generates an appropriate placeholder for the file name input private getPlaceholder(rule: PairingRule): string { const activeEditor = vscode.window.activeTextEditor; if (activeEditor && !activeEditor.document.isUntitled) { const currentFileName = path.basename(activeEditor.document.fileName, path.extname(activeEditor.document.fileName)); + // C uses snake_case by convention, C++ typically uses PascalCase for classes if (rule.language === 'c') return currentFileName; return this.toPascalCase(currentFileName); } @@ -156,18 +161,15 @@ class PairCreator implements vscode.Disposable { return DEFAULT_PLACEHOLDERS.CPP_EMPTY; } - // --- START: The Refined, Simplified promptForPairingRule Method --- + // Prompts the user to select a file pair template type from available options private async promptForPairingRule(language: 'c' | 'cpp', uncertain: boolean): Promise { let desiredOrder: string[]; if (uncertain) { - // For orphan headers, present a neutral, user-friendly order. desiredOrder = ['cpp_empty', 'c_empty', 'cpp_class', 'cpp_struct', 'c_struct']; } else if (language === 'c') { - // For C context, prioritize C options. desiredOrder = ['c_empty', 'c_struct', 'cpp_empty', 'cpp_class', 'cpp_struct']; } else { - // For C++ context, prioritize C++ options. desiredOrder = ['cpp_empty', 'cpp_class', 'cpp_struct', 'c_empty', 'c_struct']; } @@ -182,8 +184,8 @@ class PairCreator implements vscode.Disposable { return result; } - // --- END: The Refined, Simplified promptForPairingRule Method --- + // Prompts the user to enter a name for the new file pair private async promptForFileName(rule: PairingRule): Promise { let prompt = 'Please enter a name.'; if (rule.isClass) prompt = 'Please enter the name for the new C++ class.'; @@ -197,9 +199,10 @@ class PairCreator implements vscode.Disposable { title: 'Create Pair - Step 2 of 2' }); } - + // Generates the content for both header and source files based on the selected template rule private generateFileContent(fileName: string, eol: string, rule: PairingRule): { headerContent: string, sourceContent: string } { const templates = this.getTemplatesByRule(rule); + const context = { fileName, headerGuard: `${fileName.toUpperCase()}_H_`, @@ -215,7 +218,9 @@ class PairCreator implements vscode.Disposable { }; } + // Retrieves the appropriate file templates based on the selected pairing rule private getTemplatesByRule(rule: PairingRule): { header: string, source: string } { + // C++ Class template with constructor/destructor if (rule.isClass) { return { header: `#ifndef {{headerGuard}} @@ -245,6 +250,7 @@ private: }; } + // C++ Struct template if (rule.isStruct && rule.language === 'cpp') { return { header: `#ifndef {{headerGuard}} @@ -260,6 +266,7 @@ struct {{fileName}} { }; } + // C Struct template with typedef if (rule.isStruct && rule.language === 'c') { return { header: `#ifndef {{headerGuard}} @@ -275,6 +282,7 @@ typedef struct { }; } + // C Empty file template if (rule.language === 'c') { return { header: `#ifndef {{headerGuard}} @@ -291,7 +299,7 @@ typedef struct { }; } - // C++ Empty (default) + // C++ Empty file template (default) return { header: `#ifndef {{headerGuard}} #define {{headerGuard}} @@ -304,9 +312,10 @@ typedef struct { }; } + // Applies template variable substitution using {{variable}} placeholders private applyTemplate(template: string, context: Record): string { return template.replace(/\{\{(\w+)\}\}/g, (match, key) => { - return context[key] || match; // If key exists in context, replace it. Otherwise, keep the original placeholder. + return context[key] || match; }); } @@ -316,7 +325,9 @@ typedef struct { try { await vscode.workspace.fs.stat(filePath); return filePath.fsPath; - } catch { } + } catch { + // File doesn't exist, continue checking + } } return null; } @@ -334,24 +345,33 @@ typedef struct { private async finalizeCreation(headerPath: vscode.Uri, sourcePath: vscode.Uri): Promise { await vscode.window.showTextDocument(await vscode.workspace.openTextDocument(headerPath)); - await vscode.window.showInformationMessage(`Successfully created ${path.basename(headerPath.fsPath)} and ${path.basename(sourcePath.fsPath)}.`); + await vscode.window.showInformationMessage( + `Successfully created ${path.basename(headerPath.fsPath)} and ${path.basename(sourcePath.fsPath)}.` + ); } private async getTargetDirectory(): Promise { const activeEditor = vscode.window.activeTextEditor; + if (activeEditor && !activeEditor.document.isUntitled) { return vscode.Uri.file(path.dirname(activeEditor.document.uri.fsPath)); } + const workspaceFolders = vscode.workspace.workspaceFolders; if (workspaceFolders?.length === 1) return workspaceFolders[0].uri; + if (workspaceFolders && workspaceFolders.length > 1) { - const selectedFolder = await vscode.window.showWorkspaceFolderPick({ placeHolder: 'Please select a workspace folder for the new files.' }); + const selectedFolder = await vscode.window.showWorkspaceFolderPick({ + placeHolder: 'Please select a workspace folder for the new files.' + }); return selectedFolder?.uri; } + return undefined; } } +// Registers the create source/header pair command with the VS Code extension context export function registerCreateSourceHeaderPairCommand(context: ClangdContext) { context.subscriptions.push(new PairCreator()); } \ No newline at end of file From 76e3d19229cbc0abc728b4b31c35b592f8ceabe9 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?=E9=95=BF=E6=9C=9F=E7=B4=A0=E9=A3=9F?= Date: Thu, 17 Jul 2025 00:48:59 +0800 Subject: [PATCH 08/34] Update create-source-header-pair.ts --- src/create-source-header-pair.ts | 10 +++++----- 1 file changed, 5 insertions(+), 5 deletions(-) diff --git a/src/create-source-header-pair.ts b/src/create-source-header-pair.ts index 69d7330a..6c0ad12c 100644 --- a/src/create-source-header-pair.ts +++ b/src/create-source-header-pair.ts @@ -34,11 +34,11 @@ interface PairingRule { // Available template rules for creating different types of file pairs const TEMPLATE_RULES: ReadonlyArray = [ - { key: 'cpp_empty', label: 'New Empty C++ Pair', description: 'Creates .h/.cpp files with header guards.', language: 'cpp', headerExt: '.h', sourceExt: '.cpp' }, - { key: 'cpp_class', label: 'New C++ Class', description: 'Creates .h/.cpp files for a C++ class.', language: 'cpp', headerExt: '.h', sourceExt: '.cpp', isClass: true }, - { key: 'cpp_struct', label: 'New C++ Struct', description: 'Creates .h/.cpp files for a C++ struct.', language: 'cpp', headerExt: '.h', sourceExt: '.cpp', isStruct: true }, - { key: 'c_empty', label: 'New Empty C Pair', description: 'Creates .h/.c files for C functions.', language: 'c', headerExt: '.h', sourceExt: '.c' }, - { key: 'c_struct', label: 'New C Struct', description: 'Creates .h/.c files for a C struct (using typedef).', language: 'c', headerExt: '.h', sourceExt: '.c', isStruct: true } + { key: 'cpp_empty', label: 'New Empty C++ Pair', description: 'Creates .cpp/.h files with header guards.', language: 'cpp', headerExt: '.h', sourceExt: '.cpp' }, + { key: 'cpp_class', label: 'New C++ Class', description: 'Creates .cpp/.h files for a C++ class.', language: 'cpp', headerExt: '.h', sourceExt: '.cpp', isClass: true }, + { key: 'cpp_struct', label: 'New C++ Struct', description: 'Creates .cpp/.h files for a C++ struct.', language: 'cpp', headerExt: '.h', sourceExt: '.cpp', isStruct: true }, + { key: 'c_empty', label: 'New Empty C Pair', description: 'Creates .c/.h files for C functions.', language: 'c', headerExt: '.h', sourceExt: '.c' }, + { key: 'c_struct', label: 'New C Struct', description: 'Creates .c/.h files for a C struct (using typedef).', language: 'c', headerExt: '.h', sourceExt: '.c', isStruct: true } ]; // --- Main Class --- From 769cbba9c26608de3eaf0e9cdf04166c0a594ca0 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?=E9=95=BF=E6=9C=9F=E7=B4=A0=E9=A3=9F?= Date: Thu, 17 Jul 2025 16:06:33 +0800 Subject: [PATCH 09/34] Rename and enhance source/header pair commands Renamed 'createSourceHeaderPair' to 'newSourcePair' throughout the codebase and updated command titles for clarity. Added a new command for configuring source/header pairing rules. Improved template rule labels with icon syntax for better UI presentation. Created a placeholder for pairing rule service. --- package.json | 13 +++++++++---- src/create-source-header-pair.ts | 15 +++++++-------- src/pairing-rule-service.ts | 0 3 files changed, 16 insertions(+), 12 deletions(-) create mode 100644 src/pairing-rule-service.ts diff --git a/package.json b/package.json index 773a119a..bc19f5bc 100644 --- a/package.json +++ b/package.json @@ -212,12 +212,17 @@ { "command": "clangd.switchheadersource", "category": "clangd", - "title": "Switch Between Source/Header" + "title": "Switch Between Source/Header Pair" }, { - "command": "clangd.createSourceHeaderPair", + "command": "clangd.newSourcePair", "category": "clangd", - "title": "Create Source/Header Pair" + "title": "New Source/Header Pair" + }, + { + "command": "clangd.newSourcePair.configureRules", + "category": "clangd", + "title": "Configure Source/Header Pairing Rules" }, { "command": "clangd.install", @@ -337,7 +342,7 @@ "group": "0_navigation@5" }, { - "command": "clangd.createSourceHeaderPair", + "command": "clangd.newSourcePair", "when": "resourceLangId == c || resourceLangId == cpp || resourceLangId == cuda-cpp || resourceLangId == objective-c || resourceLangId == objective-cpp", "group": "1_modification@1" }, diff --git a/src/create-source-header-pair.ts b/src/create-source-header-pair.ts index 6c0ad12c..301d84b5 100644 --- a/src/create-source-header-pair.ts +++ b/src/create-source-header-pair.ts @@ -34,11 +34,11 @@ interface PairingRule { // Available template rules for creating different types of file pairs const TEMPLATE_RULES: ReadonlyArray = [ - { key: 'cpp_empty', label: 'New Empty C++ Pair', description: 'Creates .cpp/.h files with header guards.', language: 'cpp', headerExt: '.h', sourceExt: '.cpp' }, - { key: 'cpp_class', label: 'New C++ Class', description: 'Creates .cpp/.h files for a C++ class.', language: 'cpp', headerExt: '.h', sourceExt: '.cpp', isClass: true }, - { key: 'cpp_struct', label: 'New C++ Struct', description: 'Creates .cpp/.h files for a C++ struct.', language: 'cpp', headerExt: '.h', sourceExt: '.cpp', isStruct: true }, - { key: 'c_empty', label: 'New Empty C Pair', description: 'Creates .c/.h files for C functions.', language: 'c', headerExt: '.h', sourceExt: '.c' }, - { key: 'c_struct', label: 'New C Struct', description: 'Creates .c/.h files for a C struct (using typedef).', language: 'c', headerExt: '.h', sourceExt: '.c', isStruct: true } + { key: 'cpp_empty', label: '$(new-file) Empty C++ Pair', description: 'Creates a basic .h/.cpp file pair with header guards.', language: 'cpp', headerExt: '.h', sourceExt: '.cpp' }, + { key: 'cpp_class', label: '$(symbol-class) C++ Class', description: 'Creates a .h/.cpp pair with a boilerplate class definition.', language: 'cpp', headerExt: '.h', sourceExt: '.cpp', isClass: true }, + { key: 'cpp_struct', label: '$(symbol-struct) C++ Struct', description: 'Creates a .h/.cpp pair with a boilerplate struct definition.', language: 'cpp', headerExt: '.h', sourceExt: '.cpp', isStruct: true }, + { key: 'c_empty', label: '$(file-code) Empty C Pair', description: 'Creates a basic .h/.c file pair for function declarations.', language: 'c', headerExt: '.h', sourceExt: '.c' }, + { key: 'c_struct', label: '$(symbol-struct) C Struct', description: 'Creates a .h/.c pair with a boilerplate typedef struct.', language: 'c', headerExt: '.h', sourceExt: '.c', isStruct: true } ]; // --- Main Class --- @@ -48,7 +48,7 @@ class PairCreator implements vscode.Disposable { private command: vscode.Disposable; constructor() { - this.command = vscode.commands.registerCommand('clangd.createSourceHeaderPair', this.create, this); + this.command = vscode.commands.registerCommand('clangd.newSourcePair', this.create, this); } dispose() { this.command.dispose(); } @@ -174,8 +174,7 @@ class PairCreator implements vscode.Disposable { } const choices = [...TEMPLATE_RULES] - .sort((a, b) => desiredOrder.indexOf(a.key) - desiredOrder.indexOf(b.key)) - .map(rule => ({ ...rule, label: `$(file-code) ${rule.label}` })); + .sort((a, b) => desiredOrder.indexOf(a.key) - desiredOrder.indexOf(b.key)); const result = await vscode.window.showQuickPick(choices, { placeHolder: 'Please select the type of file pair to create.', diff --git a/src/pairing-rule-service.ts b/src/pairing-rule-service.ts new file mode 100644 index 00000000..e69de29b From 438f11a220fa031cc5ba903434628df96560c55b Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?=E9=95=BF=E6=9C=9F=E7=B4=A0=E9=A3=9F?= Date: Fri, 18 Jul 2025 15:49:04 +0800 Subject: [PATCH 10/34] npm run format --- src/ast.ts | 1 + src/create-source-header-pair.ts | 263 +++++++++++++++++++++---------- 2 files changed, 178 insertions(+), 86 deletions(-) diff --git a/src/ast.ts b/src/ast.ts index c136f982..69e0639c 100644 --- a/src/ast.ts +++ b/src/ast.ts @@ -4,6 +4,7 @@ import * as vscode from 'vscode'; import * as vscodelc from 'vscode-languageclient/node'; import {ClangdContext} from './clangd-context'; + import type {ASTParams, ASTNode} from '../api/vscode-clangd'; const ASTRequestMethod = 'textDocument/ast'; diff --git a/src/create-source-header-pair.ts b/src/create-source-header-pair.ts index 301d84b5..cace2e70 100644 --- a/src/create-source-header-pair.ts +++ b/src/create-source-header-pair.ts @@ -1,14 +1,14 @@ +import * as os from 'os'; import * as path from 'path'; import * as vscode from 'vscode'; -import * as os from 'os'; -import { ClangdContext } from './clangd-context'; +import {ClangdContext} from './clangd-context'; // --- Constants and Types --- // Regular expressions for validating user input const VALIDATION_PATTERNS = { - IDENTIFIER: /^[a-zA-Z_][a-zA-Z0-9_]*$/ // Valid C/C++ identifier pattern + IDENTIFIER: /^[a-zA-Z_][a-zA-Z0-9_]*$/ // Valid C/C++ identifier pattern } as const; // Default placeholder names for different template types @@ -22,23 +22,61 @@ const DEFAULT_PLACEHOLDERS = { // Defines the structure of a file pair template rule interface PairingRule { - key: string; // Unique identifier for this rule - label: string; // Human-readable name shown in UI - description: string; // Detailed description of what this rule creates - language: 'c' | 'cpp'; // Target programming language - headerExt: string; // File extension for header file (e.g., '.h') - sourceExt: string; // File extension for source file (e.g., '.cpp', '.c') - isClass?: boolean; // Whether this rule creates a class template - isStruct?: boolean; // Whether this rule creates a struct template + key: string; // Unique identifier for this rule + label: string; // Human-readable name shown in UI + description: string; // Detailed description of what this rule creates + language: 'c'|'cpp'; // Target programming language + headerExt: string; // File extension for header file (e.g., '.h') + sourceExt: string; // File extension for source file (e.g., '.cpp', '.c') + isClass?: boolean; // Whether this rule creates a class template + isStruct?: boolean; // Whether this rule creates a struct template } // Available template rules for creating different types of file pairs const TEMPLATE_RULES: ReadonlyArray = [ - { key: 'cpp_empty', label: '$(new-file) Empty C++ Pair', description: 'Creates a basic .h/.cpp file pair with header guards.', language: 'cpp', headerExt: '.h', sourceExt: '.cpp' }, - { key: 'cpp_class', label: '$(symbol-class) C++ Class', description: 'Creates a .h/.cpp pair with a boilerplate class definition.', language: 'cpp', headerExt: '.h', sourceExt: '.cpp', isClass: true }, - { key: 'cpp_struct', label: '$(symbol-struct) C++ Struct', description: 'Creates a .h/.cpp pair with a boilerplate struct definition.', language: 'cpp', headerExt: '.h', sourceExt: '.cpp', isStruct: true }, - { key: 'c_empty', label: '$(file-code) Empty C Pair', description: 'Creates a basic .h/.c file pair for function declarations.', language: 'c', headerExt: '.h', sourceExt: '.c' }, - { key: 'c_struct', label: '$(symbol-struct) C Struct', description: 'Creates a .h/.c pair with a boilerplate typedef struct.', language: 'c', headerExt: '.h', sourceExt: '.c', isStruct: true } + { + key: 'cpp_empty', + label: '$(new-file) Empty C++ Pair', + description: 'Creates a basic .h/.cpp file pair with header guards.', + language: 'cpp', + headerExt: '.h', + sourceExt: '.cpp' + }, + { + key: 'cpp_class', + label: '$(symbol-class) C++ Class', + description: 'Creates a .h/.cpp pair with a boilerplate class definition.', + language: 'cpp', + headerExt: '.h', + sourceExt: '.cpp', + isClass: true + }, + { + key: 'cpp_struct', + label: '$(symbol-struct) C++ Struct', + description: 'Creates a .h/.cpp pair with a boilerplate struct definition.', + language: 'cpp', + headerExt: '.h', + sourceExt: '.cpp', + isStruct: true + }, + { + key: 'c_empty', + label: '$(file-code) Empty C Pair', + description: 'Creates a basic .h/.c file pair for function declarations.', + language: 'c', + headerExt: '.h', + sourceExt: '.c' + }, + { + key: 'c_struct', + label: '$(symbol-struct) C Struct', + description: 'Creates a .h/.c pair with a boilerplate typedef struct.', + language: 'c', + headerExt: '.h', + sourceExt: '.c', + isStruct: true + } ]; // --- Main Class --- @@ -48,7 +86,8 @@ class PairCreator implements vscode.Disposable { private command: vscode.Disposable; constructor() { - this.command = vscode.commands.registerCommand('clangd.newSourcePair', this.create, this); + this.command = vscode.commands.registerCommand('clangd.newSourcePair', + this.create, this); } dispose() { this.command.dispose(); } @@ -58,50 +97,63 @@ class PairCreator implements vscode.Disposable { try { const targetDirectory = await this.getTargetDirectory(); if (!targetDirectory) { - vscode.window.showErrorMessage('Cannot determine target directory. Please open a folder or a file first.'); + vscode.window.showErrorMessage( + 'Cannot determine target directory. Please open a folder or a file first.'); return; } - const { language, uncertain } = await this.detectLanguage(); + const {language, uncertain} = await this.detectLanguage(); const rule = await this.promptForPairingRule(language, uncertain); - if (!rule) return; + if (!rule) + return; const fileName = await this.promptForFileName(rule); - if (!fileName) return; + if (!fileName) + return; - const headerPath = vscode.Uri.file(path.join(targetDirectory.fsPath, `${fileName}${rule.headerExt}`)); - const sourcePath = vscode.Uri.file(path.join(targetDirectory.fsPath, `${fileName}${rule.sourceExt}`)); + const headerPath = vscode.Uri.file( + path.join(targetDirectory.fsPath, `${fileName}${rule.headerExt}`)); + const sourcePath = vscode.Uri.file( + path.join(targetDirectory.fsPath, `${fileName}${rule.sourceExt}`)); - const existingFilePath = await this.checkFileExistence(headerPath, sourcePath); + const existingFilePath = + await this.checkFileExistence(headerPath, sourcePath); if (existingFilePath) { - vscode.window.showErrorMessage(`File already exists: ${existingFilePath}`); + vscode.window.showErrorMessage( + `File already exists: ${existingFilePath}`); return; } const eol = this.getLineEnding(); - const { headerContent, sourceContent } = this.generateFileContent(fileName, eol, rule); + const {headerContent, sourceContent} = + this.generateFileContent(fileName, eol, rule); - await this.writeFiles(headerPath, sourcePath, headerContent, sourceContent); + await this.writeFiles(headerPath, sourcePath, headerContent, + sourceContent); await this.finalizeCreation(headerPath, sourcePath); } catch (error: any) { - vscode.window.showErrorMessage(error.message || 'An unexpected error occurred.'); + vscode.window.showErrorMessage(error.message || + 'An unexpected error occurred.'); } } // --- Helper Methods --- private getLineEnding(): string { - const eolSetting = vscode.workspace.getConfiguration('files').get('eol'); + const eolSetting = + vscode.workspace.getConfiguration('files').get('eol'); return (eolSetting === '\n' || eolSetting === '\r\n') ? eolSetting : os.EOL; } - // Detects the programming language context (C or C++) based on the current active file - private async detectLanguage(): Promise<{ language: 'c' | 'cpp', uncertain: boolean }> { + // Detects the programming language context (C or C++) based on the current + // active file + private async detectLanguage(): + Promise<{language: 'c' | 'cpp', uncertain: boolean}> { const activeEditor = vscode.window.activeTextEditor; if (!activeEditor || activeEditor.document.isUntitled) { - return { language: 'cpp', uncertain: true }; + return {language: 'cpp', uncertain: true}; } const document = activeEditor.document; @@ -110,71 +162,89 @@ class PairCreator implements vscode.Disposable { const ext = path.extname(filePath); // Strategy 1: Trust definitive source file extensions - if (ext === '.c') return { language: 'c', uncertain: false }; - if (ext === '.cpp' || ext === '.cc' || ext === '.cxx') return { language: 'cpp', uncertain: false }; + if (ext === '.c') + return {language: 'c', uncertain: false}; + if (ext === '.cpp' || ext === '.cc' || ext === '.cxx') + return {language: 'cpp', uncertain: false}; - // Strategy 2: For header files (.h), infer language from companion source files + // Strategy 2: For header files (.h), infer language from companion source + // files if (ext === '.h') { const baseName = path.basename(filePath, '.h'); const dirPath = path.dirname(filePath); const companionChecks = [ - path.join(dirPath, `${baseName}.c`), // C source file - path.join(dirPath, `${baseName}.cpp`), // C++ source file - path.join(dirPath, `${baseName}.cc`), // Alternative C++ extension - path.join(dirPath, `${baseName}.cxx`) // Alternative C++ extension + path.join(dirPath, `${baseName}.c`), // C source file + path.join(dirPath, `${baseName}.cpp`), // C++ source file + path.join(dirPath, `${baseName}.cc`), // Alternative C++ extension + path.join(dirPath, `${baseName}.cxx`) // Alternative C++ extension ]; - const results = await Promise.allSettled( - companionChecks.map(file => vscode.workspace.fs.stat(vscode.Uri.file(file))) - ); + const results = await Promise.allSettled(companionChecks.map( + file => vscode.workspace.fs.stat(vscode.Uri.file(file)))); - if (results[0].status === 'fulfilled') return { language: 'c', uncertain: false }; - if (results.slice(1).some(r => r.status === 'fulfilled')) return { language: 'cpp', uncertain: false }; + if (results[0].status === 'fulfilled') + return {language: 'c', uncertain: false}; + if (results.slice(1).some(r => r.status === 'fulfilled')) + return {language: 'cpp', uncertain: false}; - return { language: 'cpp', uncertain: true }; + return {language: 'cpp', uncertain: true}; } // Strategy 3: Fallback to VS Code's language detection - return { language: (langId === 'c' ? 'c' : 'cpp'), uncertain: true }; + return {language: (langId === 'c' ? 'c' : 'cpp'), uncertain: true}; } // Converts a string to PascalCase by splitting on hyphens and underscores private toPascalCase(input: string): string { - return input.split(/[-_]/).map(word => word.charAt(0).toUpperCase() + word.slice(1)).join(''); + return input.split(/[-_]/) + .map(word => word.charAt(0).toUpperCase() + word.slice(1)) + .join(''); } // Generates an appropriate placeholder for the file name input private getPlaceholder(rule: PairingRule): string { const activeEditor = vscode.window.activeTextEditor; if (activeEditor && !activeEditor.document.isUntitled) { - const currentFileName = path.basename(activeEditor.document.fileName, path.extname(activeEditor.document.fileName)); - // C uses snake_case by convention, C++ typically uses PascalCase for classes - if (rule.language === 'c') return currentFileName; + const currentFileName = + path.basename(activeEditor.document.fileName, + path.extname(activeEditor.document.fileName)); + // C uses snake_case by convention, C++ typically uses PascalCase for + // classes + if (rule.language === 'c') + return currentFileName; return this.toPascalCase(currentFileName); } - if (rule.isClass) return DEFAULT_PLACEHOLDERS.CPP_CLASS; - if (rule.isStruct && rule.language === 'cpp') return DEFAULT_PLACEHOLDERS.CPP_STRUCT; - if (rule.isStruct && rule.language === 'c') return DEFAULT_PLACEHOLDERS.C_STRUCT; - if (rule.language === 'c') return DEFAULT_PLACEHOLDERS.C_EMPTY; + if (rule.isClass) + return DEFAULT_PLACEHOLDERS.CPP_CLASS; + if (rule.isStruct && rule.language === 'cpp') + return DEFAULT_PLACEHOLDERS.CPP_STRUCT; + if (rule.isStruct && rule.language === 'c') + return DEFAULT_PLACEHOLDERS.C_STRUCT; + if (rule.language === 'c') + return DEFAULT_PLACEHOLDERS.C_EMPTY; return DEFAULT_PLACEHOLDERS.CPP_EMPTY; } // Prompts the user to select a file pair template type from available options - private async promptForPairingRule(language: 'c' | 'cpp', uncertain: boolean): Promise { + private async promptForPairingRule(language: 'c'|'cpp', uncertain: boolean): + Promise { let desiredOrder: string[]; if (uncertain) { - desiredOrder = ['cpp_empty', 'c_empty', 'cpp_class', 'cpp_struct', 'c_struct']; + desiredOrder = + ['cpp_empty', 'c_empty', 'cpp_class', 'cpp_struct', 'c_struct']; } else if (language === 'c') { - desiredOrder = ['c_empty', 'c_struct', 'cpp_empty', 'cpp_class', 'cpp_struct']; + desiredOrder = + ['c_empty', 'c_struct', 'cpp_empty', 'cpp_class', 'cpp_struct']; } else { - desiredOrder = ['cpp_empty', 'cpp_class', 'cpp_struct', 'c_empty', 'c_struct']; + desiredOrder = + ['cpp_empty', 'cpp_class', 'cpp_struct', 'c_empty', 'c_struct']; } - const choices = [...TEMPLATE_RULES] - .sort((a, b) => desiredOrder.indexOf(a.key) - desiredOrder.indexOf(b.key)); + const choices = [...TEMPLATE_RULES].sort( + (a, b) => desiredOrder.indexOf(a.key) - desiredOrder.indexOf(b.key)); const result = await vscode.window.showQuickPick(choices, { placeHolder: 'Please select the type of file pair to create.', @@ -185,21 +255,32 @@ class PairCreator implements vscode.Disposable { } // Prompts the user to enter a name for the new file pair - private async promptForFileName(rule: PairingRule): Promise { + private async promptForFileName(rule: PairingRule): + Promise { let prompt = 'Please enter a name.'; - if (rule.isClass) prompt = 'Please enter the name for the new C++ class.'; - else if (rule.isStruct) prompt = `Please enter the name for the new ${rule.language.toUpperCase()} struct.`; - else prompt = `Please enter the base name for the new ${rule.language.toUpperCase()} file pair.`; + if (rule.isClass) + prompt = 'Please enter the name for the new C++ class.'; + else if (rule.isStruct) + prompt = `Please enter the name for the new ${ + rule.language.toUpperCase()} struct.`; + else + prompt = `Please enter the base name for the new ${ + rule.language.toUpperCase()} file pair.`; return vscode.window.showInputBox({ prompt, placeHolder: this.getPlaceholder(rule), - validateInput: (text) => VALIDATION_PATTERNS.IDENTIFIER.test(text?.trim() || '') ? null : 'Invalid C/C++ identifier.', + validateInput: (text) => + VALIDATION_PATTERNS.IDENTIFIER.test(text?.trim() || '') + ? null + : 'Invalid C/C++ identifier.', title: 'Create Pair - Step 2 of 2' }); } - // Generates the content for both header and source files based on the selected template rule - private generateFileContent(fileName: string, eol: string, rule: PairingRule): { headerContent: string, sourceContent: string } { + // Generates the content for both header and source files based on the + // selected template rule + private generateFileContent(fileName: string, eol: string, rule: PairingRule): + {headerContent: string, sourceContent: string} { const templates = this.getTemplatesByRule(rule); const context = { @@ -218,7 +299,8 @@ class PairCreator implements vscode.Disposable { } // Retrieves the appropriate file templates based on the selected pairing rule - private getTemplatesByRule(rule: PairingRule): { header: string, source: string } { + private getTemplatesByRule(rule: PairingRule): + {header: string, source: string} { // C++ Class template with constructor/destructor if (rule.isClass) { return { @@ -312,13 +394,15 @@ typedef struct { } // Applies template variable substitution using {{variable}} placeholders - private applyTemplate(template: string, context: Record): string { - return template.replace(/\{\{(\w+)\}\}/g, (match, key) => { - return context[key] || match; - }); + private applyTemplate(template: string, + context: Record): string { + return template.replace(/\{\{(\w+)\}\}/g, + (match, key) => { return context[key] || match; }); } - private async checkFileExistence(headerPath: vscode.Uri, sourcePath: vscode.Uri): Promise { + private async checkFileExistence(headerPath: vscode.Uri, + sourcePath: vscode.Uri): + Promise { const pathsToCheck = [headerPath, sourcePath]; for (const filePath of pathsToCheck) { try { @@ -331,25 +415,31 @@ typedef struct { return null; } - private async writeFiles(headerPath: vscode.Uri, sourcePath: vscode.Uri, headerContent: string, sourceContent: string): Promise { + private async writeFiles(headerPath: vscode.Uri, sourcePath: vscode.Uri, + headerContent: string, + sourceContent: string): Promise { try { await Promise.all([ - vscode.workspace.fs.writeFile(headerPath, Buffer.from(headerContent, 'utf8')), - vscode.workspace.fs.writeFile(sourcePath, Buffer.from(sourceContent, 'utf8')) + vscode.workspace.fs.writeFile(headerPath, + Buffer.from(headerContent, 'utf8')), + vscode.workspace.fs.writeFile(sourcePath, + Buffer.from(sourceContent, 'utf8')) ]); } catch (error: any) { throw new Error(`Failed to create files: ${error.message}.`); } } - private async finalizeCreation(headerPath: vscode.Uri, sourcePath: vscode.Uri): Promise { - await vscode.window.showTextDocument(await vscode.workspace.openTextDocument(headerPath)); + private async finalizeCreation(headerPath: vscode.Uri, + sourcePath: vscode.Uri): Promise { + await vscode.window.showTextDocument( + await vscode.workspace.openTextDocument(headerPath)); await vscode.window.showInformationMessage( - `Successfully created ${path.basename(headerPath.fsPath)} and ${path.basename(sourcePath.fsPath)}.` - ); + `Successfully created ${path.basename(headerPath.fsPath)} and ${ + path.basename(sourcePath.fsPath)}.`); } - private async getTargetDirectory(): Promise { + private async getTargetDirectory(): Promise { const activeEditor = vscode.window.activeTextEditor; if (activeEditor && !activeEditor.document.isUntitled) { @@ -357,12 +447,12 @@ typedef struct { } const workspaceFolders = vscode.workspace.workspaceFolders; - if (workspaceFolders?.length === 1) return workspaceFolders[0].uri; + if (workspaceFolders?.length === 1) + return workspaceFolders[0].uri; if (workspaceFolders && workspaceFolders.length > 1) { - const selectedFolder = await vscode.window.showWorkspaceFolderPick({ - placeHolder: 'Please select a workspace folder for the new files.' - }); + const selectedFolder = await vscode.window.showWorkspaceFolderPick( + {placeHolder: 'Please select a workspace folder for the new files.'}); return selectedFolder?.uri; } @@ -370,7 +460,8 @@ typedef struct { } } -// Registers the create source/header pair command with the VS Code extension context +// Registers the create source/header pair command with the VS Code extension +// context export function registerCreateSourceHeaderPairCommand(context: ClangdContext) { context.subscriptions.push(new PairCreator()); } \ No newline at end of file From da3e2884097199688eaad1dfa11da59c660269f5 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?=E9=95=BF=E6=9C=9F=E7=B4=A0=E9=A3=9F?= Date: Fri, 18 Jul 2025 19:09:13 +0800 Subject: [PATCH 11/34] fix --- src/create-source-header-pair.ts | 1185 ++++++++++++++++++++++-------- src/pairing-rule-manager.ts | 329 +++++++++ src/pairing-rule-service.ts | 0 3 files changed, 1202 insertions(+), 312 deletions(-) create mode 100644 src/pairing-rule-manager.ts delete mode 100644 src/pairing-rule-service.ts diff --git a/src/create-source-header-pair.ts b/src/create-source-header-pair.ts index cace2e70..75a93b71 100644 --- a/src/create-source-header-pair.ts +++ b/src/create-source-header-pair.ts @@ -1,52 +1,90 @@ -import * as os from 'os'; +// +// CREATE SOURCE HEADER PAIR +// ========================= +// +// PURPOSE: +// This module provides functionality to create matching header/source file pairs +// for C/C++ development. It intelligently detects language context, offers +// appropriate templates, and handles custom file extensions. +// +// ARCHITECTURE: +// 1. PairCreatorService (Business Logic Layer) +// - Language detection from file context +// - File existence checking with caching +// - Template content generation +// - File system operations (read/write) +// - Custom extension handling +// +// 2. PairCreatorUI (User Interface Layer) +// - User prompts and input validation +// - Template selection dialogs +// - Custom rule management integration +// - Error handling and user feedback +// +// 3. SourceHeaderPairCoordinator (Main Coordinator) +// - Orchestrates the entire workflow +// - Registers VS Code command +// - Manages component lifecycle +// +// WORKFLOW: +// Command triggered → Detect target directory → Analyze language context → +// Check for custom rules → Present template choices → Get file name → +// Validate uniqueness → Generate content → Write files → Open in editor +// +// FEATURES: +// - Smart language detection (C vs C++) +// - Multiple template types (class, struct, empty) +// - Custom file extension support +// - Header guard generation +// - Cross-language template options +// - Workspace-aware directory selection +// - Input validation for C/C++ identifiers +// +// INTEGRATION: +// Uses PairingRuleManager for custom extension configurations +// Integrates with VS Code file system and editor APIs +// + import * as path from 'path'; import * as vscode from 'vscode'; -import {ClangdContext} from './clangd-context'; +import { ClangdContext } from './clangd-context'; +import { PairingRule, PairingRuleService } from './pairing-rule-manager'; -// --- Constants and Types --- +// Types for better type safety +type Language = 'c' | 'cpp'; +type TemplateKey = 'CPP_CLASS' | 'CPP_STRUCT' | 'C_STRUCT' | 'C_EMPTY' | 'CPP_EMPTY'; -// Regular expressions for validating user input +// Regular expression patterns to validate C/C++ identifiers const VALIDATION_PATTERNS = { - IDENTIFIER: /^[a-zA-Z_][a-zA-Z0-9_]*$/ // Valid C/C++ identifier pattern -} as const; + IDENTIFIER: /^[a-zA-Z_][a-zA-Z0-9_]*$/ +}; -// Default placeholder names for different template types +// Default placeholder names for different file types const DEFAULT_PLACEHOLDERS = { C_EMPTY: 'my_c_functions', C_STRUCT: 'MyStruct', CPP_EMPTY: 'utils', CPP_CLASS: 'MyClass', CPP_STRUCT: 'MyStruct' -} as const; - -// Defines the structure of a file pair template rule -interface PairingRule { - key: string; // Unique identifier for this rule - label: string; // Human-readable name shown in UI - description: string; // Detailed description of what this rule creates - language: 'c'|'cpp'; // Target programming language - headerExt: string; // File extension for header file (e.g., '.h') - sourceExt: string; // File extension for source file (e.g., '.cpp', '.c') - isClass?: boolean; // Whether this rule creates a class template - isStruct?: boolean; // Whether this rule creates a struct template -} +}; -// Available template rules for creating different types of file pairs -const TEMPLATE_RULES: ReadonlyArray = [ +// Template rules for available file pair types +const TEMPLATE_RULES: PairingRule[] = [ { key: 'cpp_empty', - label: '$(new-file) Empty C++ Pair', - description: 'Creates a basic .h/.cpp file pair with header guards.', - language: 'cpp', + label: '$(new-file) C++ Pair', + description: 'Creates a basic Header/Source file pair with header guards.', + language: 'cpp' as const, headerExt: '.h', sourceExt: '.cpp' }, { key: 'cpp_class', label: '$(symbol-class) C++ Class', - description: 'Creates a .h/.cpp pair with a boilerplate class definition.', - language: 'cpp', + description: + 'Creates a Header/Source file pair with a boilerplate class definition.', + language: 'cpp' as const, headerExt: '.h', sourceExt: '.cpp', isClass: true @@ -54,235 +92,273 @@ const TEMPLATE_RULES: ReadonlyArray = [ { key: 'cpp_struct', label: '$(symbol-struct) C++ Struct', - description: 'Creates a .h/.cpp pair with a boilerplate struct definition.', - language: 'cpp', + description: + 'Creates a Header/Source file pair with a boilerplate struct definition.', + language: 'cpp' as const, headerExt: '.h', sourceExt: '.cpp', isStruct: true }, { key: 'c_empty', - label: '$(file-code) Empty C Pair', + label: '$(file-code) C Pair', description: 'Creates a basic .h/.c file pair for function declarations.', - language: 'c', + language: 'c' as const, headerExt: '.h', sourceExt: '.c' }, { key: 'c_struct', label: '$(symbol-struct) C Struct', - description: 'Creates a .h/.c pair with a boilerplate typedef struct.', - language: 'c', + description: 'Creates a .h/.c file pair with a boilerplate typedef struct.', + language: 'c' as const, headerExt: '.h', sourceExt: '.c', isStruct: true } ]; -// --- Main Class --- +// File templates with immutable structure +const FILE_TEMPLATES = { + CPP_CLASS: { + header: `#ifndef {{headerGuard}} +#define {{headerGuard}} -// Main class responsible for creating header/source file pairs -class PairCreator implements vscode.Disposable { - private command: vscode.Disposable; +class {{fileName}} { +public: + {{fileName}}(); + ~{{fileName}}(); - constructor() { - this.command = vscode.commands.registerCommand('clangd.newSourcePair', - this.create, this); - } +private: + // Add private members here +}; - dispose() { this.command.dispose(); } +#endif // {{headerGuard}} +`, + source: `{{includeLine}} - // Main entry point for creating a source/header file pair - public async create(): Promise { - try { - const targetDirectory = await this.getTargetDirectory(); - if (!targetDirectory) { - vscode.window.showErrorMessage( - 'Cannot determine target directory. Please open a folder or a file first.'); - return; - } +{{fileName}}::{{fileName}}() { + // Constructor implementation +} - const {language, uncertain} = await this.detectLanguage(); +{{fileName}}::~{{fileName}}() { + // Destructor implementation +} +` + }, + CPP_STRUCT: { + header: `#ifndef {{headerGuard}} +#define {{headerGuard}} - const rule = await this.promptForPairingRule(language, uncertain); - if (!rule) - return; +struct {{fileName}} { + // Struct members +}; - const fileName = await this.promptForFileName(rule); - if (!fileName) - return; +#endif // {{headerGuard}} +`, + source: '{{includeLine}}' + }, + C_STRUCT: { + header: `#ifndef {{headerGuard}} +#define {{headerGuard}} - const headerPath = vscode.Uri.file( - path.join(targetDirectory.fsPath, `${fileName}${rule.headerExt}`)); - const sourcePath = vscode.Uri.file( - path.join(targetDirectory.fsPath, `${fileName}${rule.sourceExt}`)); +typedef struct { + // Struct members +} {{fileName}}; - const existingFilePath = - await this.checkFileExistence(headerPath, sourcePath); - if (existingFilePath) { - vscode.window.showErrorMessage( - `File already exists: ${existingFilePath}`); - return; - } +#endif // {{headerGuard}} +`, + source: '{{includeLine}}' + }, + C_EMPTY: { + header: `#ifndef {{headerGuard}} +#define {{headerGuard}} - const eol = this.getLineEnding(); - const {headerContent, sourceContent} = - this.generateFileContent(fileName, eol, rule); +// Declarations for {{fileName}}.c - await this.writeFiles(headerPath, sourcePath, headerContent, - sourceContent); - await this.finalizeCreation(headerPath, sourcePath); +#endif // {{headerGuard}} +`, + source: `{{includeLine}} - } catch (error: any) { - vscode.window.showErrorMessage(error.message || - 'An unexpected error occurred.'); - } +// Implementations for {{fileName}}.c +` + }, + CPP_EMPTY: { + header: `#ifndef {{headerGuard}} +#define {{headerGuard}} + +// Declarations for {{fileName}}.cpp + +#endif // {{headerGuard}} +`, + source: '{{includeLine}}' } +}; - // --- Helper Methods --- +// Service Layer - Core business logic +class PairCreatorService { + // Cache for expensive file system operations + private static readonly fileStatCache = new Map>(); - private getLineEnding(): string { - const eolSetting = - vscode.workspace.getConfiguration('files').get('eol'); - return (eolSetting === '\n' || eolSetting === '\r\n') ? eolSetting : os.EOL; + // Definitive file extensions for fast lookup + private static readonly DEFINITIVE_EXTENSIONS = { + c: new Set(['.c']), + cpp: new Set(['.cpp', '.cc', '.cxx', '.hh', '.hpp', '.hxx']) + }; + + + // Optimized file existence check with caching to improve performance + private static async fileExists(filePath: string): Promise { + if (this.fileStatCache.has(filePath)) { + return this.fileStatCache.get(filePath)!; + } + + const promise = + Promise.resolve(vscode.workspace.fs.stat(vscode.Uri.file(filePath)) + .then(() => true, () => false)); + + this.fileStatCache.set(filePath, promise); + + // Auto-clear cache entry after 5 seconds to prevent memory leaks + setTimeout(() => this.fileStatCache.delete(filePath), 5000); + + return promise; } - // Detects the programming language context (C or C++) based on the current - // active file - private async detectLanguage(): - Promise<{language: 'c' | 'cpp', uncertain: boolean}> { - const activeEditor = vscode.window.activeTextEditor; - if (!activeEditor || activeEditor.document.isUntitled) { - return {language: 'cpp', uncertain: true}; + // Detects programming language from file info (pure business logic) + // DETECTION STRATEGY: + // 1. Fast path: Check file extension against definitive lists + // - .c files are definitely C + // - .cpp/.cc/.cxx files are definitely C++ + // 2. Special case: .h files are ambiguous + // - Look for companion files in same directory + // - .h + .c companion → C language + // - .h + .cpp/.cc/.cxx companion → C++ language + // - No companion found → default to C++ (uncertain) + // 3. Fallback: Use VS Code's language ID detection + // Returns: language type and uncertainty flag for UI decisions + public async detectLanguage(languageId?: string, filePath?: string): + Promise<{ language: Language, uncertain: boolean }> { + if (!languageId || !filePath) { + return { language: 'cpp', uncertain: true }; } - const document = activeEditor.document; - const langId = document.languageId; - const filePath = document.uri.fsPath; const ext = path.extname(filePath); - // Strategy 1: Trust definitive source file extensions - if (ext === '.c') - return {language: 'c', uncertain: false}; - if (ext === '.cpp' || ext === '.cc' || ext === '.cxx') - return {language: 'cpp', uncertain: false}; + // Fast path for definitive extensions + if (PairCreatorService.DEFINITIVE_EXTENSIONS.c.has(ext)) { + return { language: 'c', uncertain: false }; + } + if (PairCreatorService.DEFINITIVE_EXTENSIONS.cpp.has(ext)) { + return { language: 'cpp', uncertain: false }; + } - // Strategy 2: For header files (.h), infer language from companion source - // files + // Special handling for .h files with companion file detection if (ext === '.h') { - const baseName = path.basename(filePath, '.h'); - const dirPath = path.dirname(filePath); + const result = await this.detectLanguageForHeaderFile(filePath); + if (result) + return result; + } - const companionChecks = [ - path.join(dirPath, `${baseName}.c`), // C source file - path.join(dirPath, `${baseName}.cpp`), // C++ source file - path.join(dirPath, `${baseName}.cc`), // Alternative C++ extension - path.join(dirPath, `${baseName}.cxx`) // Alternative C++ extension - ]; + // Fallback to language ID + return { language: languageId === 'c' ? 'c' : 'cpp', uncertain: true }; + } - const results = await Promise.allSettled(companionChecks.map( - file => vscode.workspace.fs.stat(vscode.Uri.file(file)))); + // Optimized header file language detection by checking companion files + // COMPANION FILE DETECTION STRATEGY: + // 1. Extract base name from header file (remove .h extension) + // 2. Check for C companion first (.c) - less common, early exit optimization + // - If found: definitely C language + // 3. Check for C++ companions in parallel (.cpp, .cc, .cxx) + // - If any found: definitely C++ language + // 4. No companion found: default to C++ but mark as uncertain + // This helps determine appropriate templates and reduces user confusion + private async detectLanguageForHeaderFile(filePath: string): + Promise<{ language: Language, uncertain: boolean } | null> { + const baseName = path.basename(filePath, '.h'); + const dirPath = path.dirname(filePath); + + // Check for C companion file first (less common, check first for early + // exit) + const cFile = path.join(dirPath, `${baseName}.c`); + if (await PairCreatorService.fileExists(cFile)) { + return { language: 'c', uncertain: false }; + } - if (results[0].status === 'fulfilled') - return {language: 'c', uncertain: false}; - if (results.slice(1).some(r => r.status === 'fulfilled')) - return {language: 'cpp', uncertain: false}; + // Check for C++ companion files in parallel + const cppExtensions = ['.cpp', '.cc', '.cxx']; + const cppChecks = + cppExtensions.map(ext => PairCreatorService.fileExists( + path.join(dirPath, `${baseName}${ext}`))); - return {language: 'cpp', uncertain: true}; + const results = await Promise.all(cppChecks); + if (results.some((exists: boolean) => exists)) { + return { language: 'cpp', uncertain: false }; } - // Strategy 3: Fallback to VS Code's language detection - return {language: (langId === 'c' ? 'c' : 'cpp'), uncertain: true}; + return { language: 'cpp', uncertain: true }; } - // Converts a string to PascalCase by splitting on hyphens and underscores - private toPascalCase(input: string): string { - return input.split(/[-_]/) - .map(word => word.charAt(0).toUpperCase() + word.slice(1)) - .join(''); + // Gets all available pairing rules (custom + workspace + user) + public getAllPairingRules(): PairingRule[] { + return [ + ...(PairingRuleService.getRules('workspace') ?? []), + ...(PairingRuleService.getRules('user') ?? []) + ]; } - // Generates an appropriate placeholder for the file name input - private getPlaceholder(rule: PairingRule): string { - const activeEditor = vscode.window.activeTextEditor; - if (activeEditor && !activeEditor.document.isUntitled) { - const currentFileName = - path.basename(activeEditor.document.fileName, - path.extname(activeEditor.document.fileName)); - // C uses snake_case by convention, C++ typically uses PascalCase for - // classes - if (rule.language === 'c') - return currentFileName; - return this.toPascalCase(currentFileName); + // Gets custom C++ extensions if available from configuration + public getCustomCppExtensions(): { headerExt: string, sourceExt: string } | null { + const allRules = this.getAllPairingRules(); + const cppCustomRule = allRules.find((rule: PairingRule) => rule.language === 'cpp'); + return cppCustomRule ? { + headerExt: cppCustomRule.headerExt, + sourceExt: cppCustomRule.sourceExt } + : null; + } - if (rule.isClass) + // Converts string to PascalCase efficiently (pure function) + public toPascalCase(input: string): string { + return input.split(/[-_]+/) + .map(word => word.charAt(0).toUpperCase() + word.slice(1)) + .join(''); + } + + // Gets default placeholder based on rule type (pure function) + public getDefaultPlaceholder(rule: PairingRule): string { + if (rule.isClass) { return DEFAULT_PLACEHOLDERS.CPP_CLASS; - if (rule.isStruct && rule.language === 'cpp') - return DEFAULT_PLACEHOLDERS.CPP_STRUCT; - if (rule.isStruct && rule.language === 'c') - return DEFAULT_PLACEHOLDERS.C_STRUCT; - if (rule.language === 'c') - return DEFAULT_PLACEHOLDERS.C_EMPTY; - return DEFAULT_PLACEHOLDERS.CPP_EMPTY; - } - - // Prompts the user to select a file pair template type from available options - private async promptForPairingRule(language: 'c'|'cpp', uncertain: boolean): - Promise { - let desiredOrder: string[]; - - if (uncertain) { - desiredOrder = - ['cpp_empty', 'c_empty', 'cpp_class', 'cpp_struct', 'c_struct']; - } else if (language === 'c') { - desiredOrder = - ['c_empty', 'c_struct', 'cpp_empty', 'cpp_class', 'cpp_struct']; - } else { - desiredOrder = - ['cpp_empty', 'cpp_class', 'cpp_struct', 'c_empty', 'c_struct']; } - const choices = [...TEMPLATE_RULES].sort( - (a, b) => desiredOrder.indexOf(a.key) - desiredOrder.indexOf(b.key)); - - const result = await vscode.window.showQuickPick(choices, { - placeHolder: 'Please select the type of file pair to create.', - title: 'Create Pair - Step 1 of 2' - }); + if (rule.isStruct) { + return rule.language === 'cpp' ? DEFAULT_PLACEHOLDERS.CPP_STRUCT + : DEFAULT_PLACEHOLDERS.C_STRUCT; + } - return result; + return rule.language === 'c' ? DEFAULT_PLACEHOLDERS.C_EMPTY + : DEFAULT_PLACEHOLDERS.CPP_EMPTY; } - // Prompts the user to enter a name for the new file pair - private async promptForFileName(rule: PairingRule): - Promise { - let prompt = 'Please enter a name.'; - if (rule.isClass) - prompt = 'Please enter the name for the new C++ class.'; - else if (rule.isStruct) - prompt = `Please enter the name for the new ${ - rule.language.toUpperCase()} struct.`; - else - prompt = `Please enter the base name for the new ${ - rule.language.toUpperCase()} file pair.`; + // Optimized line ending detection based on VS Code settings and platform + public getLineEnding(): string { + const eolSetting = + vscode.workspace.getConfiguration('files').get('eol'); - return vscode.window.showInputBox({ - prompt, - placeHolder: this.getPlaceholder(rule), - validateInput: (text) => - VALIDATION_PATTERNS.IDENTIFIER.test(text?.trim() || '') - ? null - : 'Invalid C/C++ identifier.', - title: 'Create Pair - Step 2 of 2' - }); + return eolSetting === '\n' || eolSetting === '\r\n' ? eolSetting + : process.platform === 'win32' ? '\r\n' + : '\n'; } - // Generates the content for both header and source files based on the - // selected template rule - private generateFileContent(fileName: string, eol: string, rule: PairingRule): - {headerContent: string, sourceContent: string} { - const templates = this.getTemplatesByRule(rule); + // Generates file content with improved template selection + public generateFileContent(fileName: string, eol: string, rule: PairingRule): { headerContent: string; sourceContent: string; } { + const templateKey: TemplateKey = + rule.isClass ? 'CPP_CLASS' + : rule.isStruct ? (rule.language === 'cpp' ? 'CPP_STRUCT' : 'C_STRUCT') + : rule.language === 'c' ? 'C_EMPTY' + : 'CPP_EMPTY'; + + const templates = FILE_TEMPLATES[templateKey]; const context = { fileName, headerGuard: `${fileName.toUpperCase()}_H_`, @@ -298,170 +374,655 @@ class PairCreator implements vscode.Disposable { }; } - // Retrieves the appropriate file templates based on the selected pairing rule - private getTemplatesByRule(rule: PairingRule): - {header: string, source: string} { - // C++ Class template with constructor/destructor - if (rule.isClass) { - return { - header: `#ifndef {{headerGuard}} -#define {{headerGuard}} + // Optimized template variable substitution using regex replacement + private applyTemplate(template: string, + context: Record): string { + // Pre-compile regex for better performance if used frequently + return template.replace(/\{\{(\w+)\}\}/g, (_, key) => context[key] ?? ''); + } -class {{fileName}} { -public: - {{fileName}}(); - ~{{fileName}}(); + // File existence check with parallel processing for multiple files + public async checkFileExistence(headerPath: vscode.Uri, + sourcePath: vscode.Uri): + Promise { + const checks = [headerPath, sourcePath].map(async (uri) => { + try { + await vscode.workspace.fs.stat(uri); + return uri.fsPath; + } catch { + return null; + } + }); -private: - // Add private members here -}; + const results = await Promise.all(checks); + return results.find(path => path !== null) ?? null; + } -#endif // {{headerGuard}} -`, - source: `{{includeLine}} + // Optimized file writing with error handling and parallel writes + public async writeFiles(headerPath: vscode.Uri, sourcePath: vscode.Uri, + headerContent: string, + sourceContent: string): Promise { + try { + await Promise.all([ + vscode.workspace.fs.writeFile(headerPath, + Buffer.from(headerContent, 'utf8')), + vscode.workspace.fs.writeFile(sourcePath, + Buffer.from(sourceContent, 'utf8')) + ]); + } catch (error) { + throw new Error(`Failed to create files: ${error instanceof Error ? error.message : 'Unknown error'}`); + } + } -{{fileName}}::{{fileName}}() { - // Constructor implementation -} + // Smart target directory detection (pure business logic) + public async getTargetDirectory(activeDocumentPath?: string, + workspaceFolders + ?: readonly vscode.WorkspaceFolder[]): + Promise { + // Prefer current file's directory + if (activeDocumentPath) { + return vscode.Uri.file(path.dirname(activeDocumentPath)); + } -{{fileName}}::~{{fileName}}() { - // Destructor implementation -} -` - }; + // Return single workspace folder directly + if (workspaceFolders?.length === 1) { + return workspaceFolders[0].uri; } - // C++ Struct template - if (rule.isStruct && rule.language === 'cpp') { - return { - header: `#ifndef {{headerGuard}} -#define {{headerGuard}} + // Multiple workspace folders require UI selection + return undefined; + } -struct {{fileName}} { - // Struct members -}; + // Language mismatch warning logic (pure business logic) + public async shouldShowLanguageMismatchWarning(language: Language, + result: PairingRule, + currentDir?: string, + activeFilePath + ?: string): Promise { + if (!currentDir || !activeFilePath) { + return true; + } -#endif // {{headerGuard}} -`, - source: '{{includeLine}}' - }; + if (language === 'c' && result.language === 'cpp') { + return this.checkForCppFilesInDirectory(currentDir); } - // C Struct template with typedef - if (rule.isStruct && rule.language === 'c') { - return { - header: `#ifndef {{headerGuard}} -#define {{headerGuard}} + return this.checkForCorrespondingSourceFiles(currentDir, activeFilePath, + language); + } -typedef struct { - // Struct members -} {{fileName}}; + // Check for C++ files in directory to inform language mismatch warnings + private async checkForCppFilesInDirectory(dirPath: string): Promise { + try { + const entries = + await vscode.workspace.fs.readDirectory(vscode.Uri.file(dirPath)); + const hasCppFiles = + entries.some(([fileName, fileType]) => + fileType === vscode.FileType.File && + PairCreatorService.DEFINITIVE_EXTENSIONS.cpp.has( + path.extname(fileName))); + return !hasCppFiles; // Show warning if NO C++ files found + } catch { + return true; // Show warning if can't check + } + } -#endif // {{headerGuard}} -`, - source: '{{includeLine}}' - }; + // Check for corresponding source files to inform language mismatch warnings + private async checkForCorrespondingSourceFiles( + dirPath: string, filePath: string, language: Language): Promise { + const baseName = path.basename(filePath, path.extname(filePath)); + const extensions = language === 'c' ? ['.c'] : ['.cpp', '.cc', '.cxx']; + + const checks = extensions.map(ext => PairCreatorService.fileExists( + path.join(dirPath, `${baseName}${ext}`))); + + try { + const results = await Promise.all(checks); + return !results.some( + (exists: boolean) => exists); // Show warning if NO corresponding files found + } catch { + return true; // Show warning if can't check } + } +} - // C Empty file template - if (rule.language === 'c') { - return { - header: `#ifndef {{headerGuard}} -#define {{headerGuard}} +// UI Layer -// Declarations for {{fileName}}.c +// PairCreatorUI handles all user interface interactions for file pair creation. +// It manages dialogs, input validation, and user choices. +class PairCreatorUI { + private service: PairCreatorService; -#endif // {{headerGuard}} -`, - source: `{{includeLine}} + constructor(service: PairCreatorService) { this.service = service; } -// Implementations for {{fileName}}.c -` - }; + // Gets placeholder name for input dialog, considering active file context + private getPlaceholder(rule: PairingRule): string { + const activeEditor = vscode.window.activeTextEditor; + + if (activeEditor?.document && !activeEditor.document.isUntitled) { + const fileName = + path.basename(activeEditor.document.fileName, + path.extname(activeEditor.document.fileName)); + return rule.language === 'c' ? fileName + : this.service.toPascalCase(fileName); } - // C++ Empty file template (default) - return { - header: `#ifndef {{headerGuard}} -#define {{headerGuard}} + return this.service.getDefaultPlaceholder(rule); + } -// Declarations for {{fileName}}.cpp + // Gets target directory with UI fallback for multiple workspace folders + public async getTargetDirectory(): Promise { + const activeEditor = vscode.window.activeTextEditor; + const activeDocumentPath = + activeEditor?.document && !activeEditor.document.isUntitled + ? activeEditor.document.uri.fsPath + : undefined; + const workspaceFolders = vscode.workspace.workspaceFolders; -#endif // {{headerGuard}} -`, - source: '{{includeLine}}' - }; + // Try service layer first + const result = await this.service.getTargetDirectory(activeDocumentPath, + workspaceFolders); + if (result) { + return result; + } + + // Handle multiple workspace folders with UI + if (workspaceFolders && workspaceFolders.length > 1) { + const selected = await vscode.window.showWorkspaceFolderPick( + { placeHolder: 'Select workspace folder for new files' }); + return selected?.uri; + } + + return undefined; } - // Applies template variable substitution using {{variable}} placeholders - private applyTemplate(template: string, - context: Record): string { - return template.replace(/\{\{(\w+)\}\}/g, - (match, key) => { return context[key] || match; }); + // Checks if language mismatch warning should be shown with UI context + private async shouldShowLanguageMismatchWarning(language: Language, + result: PairingRule): + Promise { + const activeEditor = vscode.window.activeTextEditor; + const currentDir = + activeEditor?.document && !activeEditor.document.isUntitled + ? path.dirname(activeEditor.document.uri.fsPath) + : undefined; + const activeFilePath = + activeEditor?.document && !activeEditor.document.isUntitled + ? activeEditor.document.uri.fsPath + : undefined; + + return this.service.shouldShowLanguageMismatchWarning( + language, result, currentDir, activeFilePath); } - private async checkFileExistence(headerPath: vscode.Uri, - sourcePath: vscode.Uri): - Promise { - const pathsToCheck = [headerPath, sourcePath]; - for (const filePath of pathsToCheck) { - try { - await vscode.workspace.fs.stat(filePath); - return filePath.fsPath; - } catch { - // File doesn't exist, continue checking + // Detects programming language from active editor context + public async detectLanguage(): + Promise<{ language: Language, uncertain: boolean }> { + const activeEditor = vscode.window.activeTextEditor; + const languageId = activeEditor?.document?.languageId; + const filePath = activeEditor?.document && !activeEditor.document.isUntitled + ? activeEditor.document.uri.fsPath + : undefined; + + return this.service.detectLanguage(languageId, filePath); + } + + // Adapts template rules for display in UI based on custom extensions + private adaptRuleForDisplay(rule: PairingRule): PairingRule { + if (rule.language !== 'cpp') { + return rule; + } + + const customExtensions = this.service.getCustomCppExtensions(); + if (!customExtensions) { + return rule; + } + + const { headerExt, sourceExt } = customExtensions; + + // Adapt description for display + const replacementPattern = + /Header\/Source|\.h(?:h|pp|xx)?\/\.c(?:pp|c|xx)?/g; + const newDescription = rule.description.replace( + replacementPattern, `${headerExt}/${sourceExt}`); + + return { ...rule, description: newDescription, headerExt, sourceExt }; + } + + // Prepares template choices for UI display with proper ordering and adaptation + private prepareTemplateChoices(language: 'c' | 'cpp', + uncertain: boolean): PairingRule[] { + const desiredOrder = + uncertain + ? ['cpp_empty', 'c_empty', 'cpp_class', 'cpp_struct', 'c_struct'] + : language === 'c' + ? ['c_empty', 'c_struct', 'cpp_empty', 'cpp_class', 'cpp_struct'] + : ['cpp_empty', 'cpp_class', 'cpp_struct', 'c_empty', 'c_struct']; + + return [...TEMPLATE_RULES] + .sort((a, b) => + desiredOrder.indexOf(a.key) - desiredOrder.indexOf(b.key)) + .map(rule => this.adaptRuleForDisplay(rule)); + } + + // Filters custom rules by language and prepares them for display + private prepareCustomRulesChoices(allRules: PairingRule[], + language: 'c' | 'cpp'): { + languageRules: PairingRule[], + adaptedDefaultTemplates: PairingRule[], + otherLanguageTemplates: PairingRule[], + cleanedCustomRules: PairingRule[] + } { + const languageRules = allRules.filter((rule: PairingRule) => rule.language === language); + const customExt = languageRules.length > 0 ? languageRules[0] : null; + + let adaptedDefaultTemplates: PairingRule[] = []; + + if (customExt && language === 'cpp') { + // For C++, adapt default templates with custom extensions + adaptedDefaultTemplates = + TEMPLATE_RULES + .filter( + template => + template.language === 'cpp' && + !languageRules.some( + customRule => + customRule.isClass === template.isClass && + customRule.isStruct === template.isStruct && + (customRule.isClass || customRule.isStruct || + (!customRule.isClass && !customRule.isStruct && + !template.isClass && !template.isStruct)))) + .map( + template => ({ + ...template, + key: `${template.key}_adapted`, + headerExt: customExt.headerExt, + sourceExt: customExt.sourceExt, + description: + template.description + .replace( + /Header\/Source/g, + `${customExt.headerExt}/${customExt.sourceExt}`) + .replace(/\.h\/\.cpp/g, `${customExt.headerExt}/${customExt.sourceExt}`) + .replace(/basic \.h\/\.cpp/g, + `basic ${customExt.headerExt}/${customExt.sourceExt}`) + .replace(/Creates a \.h\/\.cpp/g, + `Creates a ${customExt.headerExt}/${customExt.sourceExt}`) + })); + } else { + // Standard adaptation for non-custom or C language + adaptedDefaultTemplates = + TEMPLATE_RULES + .filter(template => + template.language === language && + !languageRules.some( + customRule => + customRule.headerExt === template.headerExt && + customRule.sourceExt === template.sourceExt && + customRule.isClass === template.isClass && + customRule.isStruct === template.isStruct)) + .map(template => this.adaptRuleForDisplay(template)); + } + + const otherLanguageTemplates = + TEMPLATE_RULES.filter(template => template.language !== language) + .map(template => this.adaptRuleForDisplay(template)); + + const cleanedCustomRules = allRules.map( + (rule: PairingRule) => ({ + ...rule, + label: rule.label.includes('$(') + ? rule.label + : `$(new-file) ${rule.language === 'cpp' ? 'C++' : 'C'} Pair (${rule.headerExt}/${rule.sourceExt})`, + description: rule.description.startsWith('Creates a') + ? rule.description + : `Creates a ${rule.headerExt}/${rule.sourceExt} file pair with header guards.` + })); + + return { + languageRules, + adaptedDefaultTemplates, + otherLanguageTemplates, + cleanedCustomRules + }; + } + + // Checks for existing custom pairing rules and offers to create them if not found + // For C++, presents options to use custom rules or create new ones. + // For C, always uses default templates. + // Returns selected rule, null if cancelled, undefined for defaults, or 'use_default' flag + public async checkAndOfferCustomRules(language: 'c' | 'cpp', + uncertain: boolean): + Promise { + if (language === 'c') + return undefined; // Always use default C templates + + const allRules = this.service.getAllPairingRules(); + const languageRules = allRules.filter((rule: PairingRule) => rule.language === language); + + if (languageRules.length > 0) { + const result = await this.selectFromCustomRules(allRules, language); + return result === undefined ? null : result; + } + + if (!uncertain) { + const shouldCreateRules = await this.offerToCreateCustomRules(language); + if (shouldCreateRules === null) + return null; + if (shouldCreateRules) { + const result = await this.createCustomRules(language); + return result === undefined ? null : result; } } - return null; + + return undefined; } - private async writeFiles(headerPath: vscode.Uri, sourcePath: vscode.Uri, - headerContent: string, - sourceContent: string): Promise { + // Presents a selection dialog for custom pairing rules + // Combines custom rules with adapted default templates and cross-language options + // Returns selected rule, undefined if cancelled, or 'use_default' flag + public async selectFromCustomRules(allRules: PairingRule[], + language: 'c' | 'cpp'): + Promise { + + const { + cleanedCustomRules, + adaptedDefaultTemplates, + otherLanguageTemplates + } = this.prepareCustomRulesChoices(allRules, language); + + const choices = [ + ...cleanedCustomRules, ...adaptedDefaultTemplates, + ...otherLanguageTemplates, { + key: 'use_default', + label: '$(list-unordered) Use Default Templates', + description: + 'Use the built-in default pairing rules instead of custom rules', + isSpecial: true + } + ]; + + const result = await vscode.window.showQuickPick(choices, { + placeHolder: `Select a ${language.toUpperCase()} pairing rule`, + title: 'Custom Pairing Rules Available', + }); + + if (!result) + return undefined; + if ('isSpecial' in result && result.isSpecial && + result.key === 'use_default') + return 'use_default'; + return result as PairingRule; + } + + // Shows a dialog offering to create custom pairing rules for C++ + // Only applicable for C++ since C uses standard .c/.h extensions + // Returns true to create rules, false to dismiss, null if cancelled + public async offerToCreateCustomRules(language: 'c' | + 'cpp'): Promise { + if (language === 'c') + return false; + + const result = await vscode.window.showInformationMessage( + `No custom pairing rules found for C++. Would you like to create custom rules to use different file extensions (e.g., .cc/.hh instead of .cpp/.h)?`, + { modal: false }, 'Create Custom Rules', 'Dismiss'); + + return result === 'Create Custom Rules' ? true + : result === 'Dismiss' ? false + : null; + } + + // Guides the user through creating custom pairing rules for C++ + // Offers common extension combinations or allows custom input + // Saves the rule to workspace or global settings + // Returns the created custom rule or undefined if cancelled + public async createCustomRules(language: 'c' | + 'cpp'): Promise { + if (language === 'c') + return undefined; + + const commonExtensions = [ + { label: '.h / .cpp (Default)', headerExt: '.h', sourceExt: '.cpp' }, + { label: '.hh / .cc (Alternative)', headerExt: '.hh', sourceExt: '.cc' }, { + label: '.hpp / .cpp (Header Plus Plus)', + headerExt: '.hpp', + sourceExt: '.cpp' + }, + { label: '.hxx / .cxx (Extended)', headerExt: '.hxx', sourceExt: '.cxx' }, + { label: 'Custom Extensions', headerExt: '', sourceExt: '' } + ]; + + const selectedExtension = + await vscode.window.showQuickPick(commonExtensions, { + placeHolder: `Select file extensions for C++ files`, + title: 'Choose File Extensions' + }); + + if (!selectedExtension) + return undefined; + + let { headerExt, sourceExt } = selectedExtension; + + if (!headerExt || !sourceExt) { + const validateExt = (text: string) => + (!text || !text.startsWith('.') || text.length < 2) + ? 'Please enter a valid file extension starting with a dot (e.g., .h)' + : null; + + headerExt = await vscode.window.showInputBox({ + prompt: 'Enter header file extension (e.g., .h, .hh, .hpp)', + placeHolder: '.h', + validateInput: validateExt + }) || ''; + + if (!headerExt) + return undefined; + + sourceExt = await vscode.window.showInputBox({ + prompt: `Enter source file extension for C++ (e.g., .cpp, .cc, .cxx)`, + placeHolder: '.cpp', + validateInput: validateExt + }) || ''; + + if (!sourceExt) + return undefined; + } + + const customRule: PairingRule = { + key: `custom_cpp_${Date.now()}`, + label: `$(new-file) C++ Pair (${headerExt}/${sourceExt})`, + description: + `Creates a ${headerExt}/${sourceExt} file pair with header guards.`, + language: 'cpp', + headerExt, + sourceExt + }; + + const saveLocation = await vscode.window.showQuickPick( + [ + { + label: 'Workspace Settings', + description: 'Save to current workspace only', + value: 'workspace' + }, + { + label: 'Global Settings', + description: 'Save to user settings (available in all workspaces)', + value: 'user' + } + ], + { + placeHolder: 'Where would you like to save this custom rule?', + title: 'Save Location' + }); + + if (!saveLocation) + return undefined; + try { - await Promise.all([ - vscode.workspace.fs.writeFile(headerPath, - Buffer.from(headerContent, 'utf8')), - vscode.workspace.fs.writeFile(sourcePath, - Buffer.from(sourceContent, 'utf8')) - ]); + const existingRules = PairingRuleService.getRules( + saveLocation.value as 'workspace' | 'user') || + []; + await PairingRuleService.writeRules([...existingRules, customRule], + saveLocation.value as 'workspace' | + 'user'); + + const locationText = + saveLocation.value === 'workspace' ? 'workspace' : 'global'; + vscode.window.showInformationMessage( + `Custom pairing rule saved to ${locationText} settings.`); + + return customRule; } catch (error: any) { - throw new Error(`Failed to create files: ${error.message}.`); + vscode.window.showErrorMessage( + `Failed to save custom rule: ${error.message}`); + return undefined; + } + } + + // Prompts the user to select a pairing rule from available options + // First checks for custom rules, then falls back to default templates + // Returns selected pairing rule or undefined if cancelled + public async promptForPairingRule(language: 'c' | 'cpp', uncertain: boolean): + Promise { + const customRulesResult = + await this.checkAndOfferCustomRules(language, uncertain); + + if (customRulesResult === null) + return undefined; + if (customRulesResult === 'use_default') { + // Continue to default template selection + } else if (customRulesResult) { + return customRulesResult; + } + + const choices = this.prepareTemplateChoices(language, uncertain); + + const result = await vscode.window.showQuickPick(choices, { + placeHolder: 'Please select the type of file pair to create.', + title: 'Create Source/Header Pair' + }); + + if (result && !uncertain && language !== result.language) { + const shouldShowWarning = + await this.shouldShowLanguageMismatchWarning(language, result); + + if (shouldShowWarning) { + const detectedLangName = language === 'c' ? 'C' : 'C++'; + const selectedLangName = result.language === 'c' ? 'C' : 'C++'; + + const shouldContinue = await vscode.window.showWarningMessage( + `You're working in a ${detectedLangName} file but selected a ${selectedLangName} template. This may create files with incompatible extensions or content.`, + 'Continue', 'Cancel'); + + if (shouldContinue !== 'Continue') + return undefined; + } } + + return result; } - private async finalizeCreation(headerPath: vscode.Uri, - sourcePath: vscode.Uri): Promise { + // Prompts the user to enter a name for the new file pair + // Validates input as a valid C/C++ identifier and provides context-appropriate prompts + // Returns the entered file name or undefined if cancelled + public async promptForFileName(rule: PairingRule): Promise { + const prompt = rule.isClass ? 'Please enter the name for the new C++ class.' + : rule.isStruct + ? `Please enter the name for the new ${rule.language.toUpperCase()} struct.` + : `Please enter the base name for the new ${rule.language.toUpperCase()} file pair.`; + + return vscode.window.showInputBox({ + prompt, + placeHolder: this.getPlaceholder(rule), + validateInput: (text) => + VALIDATION_PATTERNS.IDENTIFIER.test(text?.trim() || '') + ? null + : 'Invalid C/C++ identifier.', + title: 'Create Source/Header Pair' + }); + } + + // Shows success message and opens the newly created header file + public async showSuccessAndOpenFile(headerPath: vscode.Uri, + sourcePath: vscode.Uri): Promise { await vscode.window.showTextDocument( - await vscode.workspace.openTextDocument(headerPath)); + await vscode.workspace.openTextDocument(headerPath)); await vscode.window.showInformationMessage( - `Successfully created ${path.basename(headerPath.fsPath)} and ${ - path.basename(sourcePath.fsPath)}.`); + `Successfully created ${path.basename(headerPath.fsPath)} and ${path.basename(sourcePath.fsPath)}.`); } +} - private async getTargetDirectory(): Promise { - const activeEditor = vscode.window.activeTextEditor; +// Main Coordinator Class - if (activeEditor && !activeEditor.document.isUntitled) { - return vscode.Uri.file(path.dirname(activeEditor.document.uri.fsPath)); - } +// PairCreator coordinates the UI and Service layers to handle the complete file +// pair creation workflow. It serves as the main entry point and orchestrates +// the entire process. +class PairCreator implements vscode.Disposable { + private command: vscode.Disposable; + private service: PairCreatorService; + private ui: PairCreatorUI; - const workspaceFolders = vscode.workspace.workspaceFolders; - if (workspaceFolders?.length === 1) - return workspaceFolders[0].uri; + // Constructor registers the VS Code command for creating source/header pairs + constructor() { + this.service = new PairCreatorService(); + this.ui = new PairCreatorUI(this.service); + this.command = vscode.commands.registerCommand( + 'clangd.createSourceHeaderPair', this.create, this); + } - if (workspaceFolders && workspaceFolders.length > 1) { - const selectedFolder = await vscode.window.showWorkspaceFolderPick( - {placeHolder: 'Please select a workspace folder for the new files.'}); - return selectedFolder?.uri; - } + // Dispose method for cleanup when extension is deactivated + dispose() { this.command.dispose(); } - return undefined; + // Main entry point for the file pair creation process + // Orchestrates the entire workflow using the service and UI layers + public async create(): Promise { + try { + const targetDirectory = await this.ui.getTargetDirectory(); + if (!targetDirectory) { + vscode.window.showErrorMessage( + 'Cannot determine target directory. Please open a folder or a file first.'); + return; + } + + const { language, uncertain } = await this.ui.detectLanguage(); + const rule = await this.ui.promptForPairingRule(language, uncertain); + if (!rule) + return; + + const fileName = await this.ui.promptForFileName(rule); + if (!fileName) + return; + + const headerPath = vscode.Uri.file( + path.join(targetDirectory.fsPath, `${fileName}${rule.headerExt}`)); + const sourcePath = vscode.Uri.file( + path.join(targetDirectory.fsPath, `${fileName}${rule.sourceExt}`)); + + const existingFilePath = + await this.service.checkFileExistence(headerPath, sourcePath); + if (existingFilePath) { + vscode.window.showErrorMessage( + `File already exists: ${existingFilePath}`); + return; + } + + const eol = this.service.getLineEnding(); + const { headerContent, sourceContent } = + this.service.generateFileContent(fileName, eol, rule); + + await this.service.writeFiles(headerPath, sourcePath, headerContent, + sourceContent); + await this.ui.showSuccessAndOpenFile(headerPath, sourcePath); + + } catch (error: any) { + vscode.window.showErrorMessage(error.message || + 'An unexpected error occurred.'); + } } } -// Registers the create source/header pair command with the VS Code extension -// context +// Registers the create source/header pair command with the VS Code extension context +// This function should be called during extension activation to make the command available export function registerCreateSourceHeaderPairCommand(context: ClangdContext) { context.subscriptions.push(new PairCreator()); -} \ No newline at end of file +} diff --git a/src/pairing-rule-manager.ts b/src/pairing-rule-manager.ts new file mode 100644 index 00000000..b3fa1d24 --- /dev/null +++ b/src/pairing-rule-manager.ts @@ -0,0 +1,329 @@ +/** + * PAIRING RULE MANAGER + * ==================== + * + * PURPOSE: + * This module manages custom file extension pairing rules for C/C++ header/source files. + * It allows users to configure custom extensions (e.g., .hh/.cc instead of .h/.cpp) + * and provides UI for managing these configurations. + * + * ARCHITECTURE: + * 1. PairingRuleService - Core configuration management + * - Reads/writes VS Code workspace/user settings + * - Validates pairing rules + * - Handles configuration scopes (workspace vs global) + * + * 2. PairingRuleUI - User interface for configuration + * - Quick setup wizard with predefined options + * - Advanced management for editing/resetting rules + * - Scope selection (workspace vs global settings) + * + * WORKFLOW: + * User calls showConfigurationWizard() → + * Presents predefined extension combinations (.h/.cpp, .hh/.cc, etc.) → + * User selects option or goes to advanced management → + * Rule is saved to workspace or global settings → + * Other parts of extension use these rules for file creation + * + * CONFIGURATION STORAGE: + * - Workspace: .vscode/settings.json (clangd.createPair.rules) + * - Global: User settings.json (clangd.createPair.rules) + */ + +import * as vscode from 'vscode'; + +// Public interface for pairing rules +export interface PairingRule { + key: string; + label: string; + description: string; + language: 'c' | 'cpp'; + headerExt: string; + sourceExt: string; + isClass?: boolean; + isStruct?: boolean; +} + +// Type aliases for QuickPick items +type RuleQuickPickItem = vscode.QuickPickItem & { rule: PairingRule }; +type ActionQuickPickItem = vscode.QuickPickItem & { key: string }; + +// Configuration management service class +export class PairingRuleService { + private static readonly CONFIG_KEY = 'createPair.rules'; + + // Validate a single pairing rule to ensure it has all required fields + private static validateRule(rule: PairingRule): void { + if (!rule.key || !rule.language || !rule.headerExt || !rule.sourceExt) { + throw new Error(`Invalid rule: ${JSON.stringify(rule)}`); + } + } + + // Show error message and re-throw the error for proper error handling + private static handleError(error: unknown, operation: string, + scope: string): never { + const message = `Failed to ${operation} pairing rules for ${scope}: ${error instanceof Error ? error.message : 'Unknown error'}`; + vscode.window.showErrorMessage(message); + throw error; + } + + // Get the currently active pairing rules from configuration + public static getActiveRules(): ReadonlyArray { + return vscode.workspace.getConfiguration('clangd').get( + PairingRuleService.CONFIG_KEY, []); + } + + // Check if custom rules exist for the specified scope (workspace or user) + public static hasCustomRules(scope: 'workspace' | 'user'): boolean { + const inspection = + vscode.workspace.getConfiguration('clangd').inspect( + PairingRuleService.CONFIG_KEY); + const value = scope === 'workspace' ? inspection?.workspaceValue + : inspection?.globalValue; + return Array.isArray(value); + } + + // Get pairing rules for a specific scope (workspace or user) + public static getRules(scope: 'workspace' | 'user'): PairingRule[] | undefined { + const inspection = + vscode.workspace.getConfiguration('clangd').inspect( + PairingRuleService.CONFIG_KEY); + return scope === 'workspace' ? inspection?.workspaceValue + : inspection?.globalValue; + } + + // Write pairing rules to the specified scope (workspace or user) + public static async writeRules(rules: PairingRule[], + scope: 'workspace' | 'user'): Promise { + try { + if (!Array.isArray(rules)) + throw new Error('Rules must be an array'); + rules.forEach(PairingRuleService.validateRule); + + const target = scope === 'workspace' + ? vscode.ConfigurationTarget.Workspace + : vscode.ConfigurationTarget.Global; + await vscode.workspace.getConfiguration('clangd').update( + PairingRuleService.CONFIG_KEY, rules, target); + } catch (error) { + PairingRuleService.handleError(error, 'save', scope); + } + } + + // Reset pairing rules for the specified scope (remove custom rules) + public static async resetRules(scope: 'workspace' | 'user'): Promise { + try { + const target = scope === 'workspace' + ? vscode.ConfigurationTarget.Workspace + : vscode.ConfigurationTarget.Global; + await vscode.workspace.getConfiguration('clangd').update( + PairingRuleService.CONFIG_KEY, undefined, target); + } catch (error) { + PairingRuleService.handleError(error, 'reset', scope); + } + } +} + +// User interface management class +export class PairingRuleUI { + // Predefined extension combinations for C++ file pairs + private static readonly EXTENSION_OPTIONS = [ + { + label: '.h / .cpp', + description: 'Standard C++ extensions', + headerExt: '.h', + sourceExt: '.cpp', + language: 'cpp' as const, + }, + { + label: '.hh / .cc', + description: 'Alternative C++ extensions', + headerExt: '.hh', + sourceExt: '.cc', + language: 'cpp' as const, + }, + { + label: '.hpp / .cpp', + description: 'Header Plus Plus style', + headerExt: '.hpp', + sourceExt: '.cpp', + language: 'cpp' as const, + }, + { + label: '.hxx / .cxx', + description: 'Extended C++ extensions', + headerExt: '.hxx', + sourceExt: '.cxx', + language: 'cpp' as const, + }, + ]; + + // Create rule choices from extension options for QuickPick display + private static createRuleChoices(): RuleQuickPickItem[] { + return PairingRuleUI.EXTENSION_OPTIONS.map( + (option, index) => ({ + label: `$(file-code) ${option.label}`, + description: option.description, + rule: { + key: `custom_${option.language}_${index}`, + label: `${option.language.toUpperCase()} Pair (${option.headerExt}/${option.sourceExt})`, + description: `Creates a ${option.headerExt}/${option.sourceExt} file pair with header guards.`, + language: option.language, + headerExt: option.headerExt, + sourceExt: option.sourceExt, + }, + })); + } + + // Create advanced options separator and menu item for advanced management + private static createAdvancedOptions(): ActionQuickPickItem[] { + return [ + { + label: 'Advanced Management', + kind: vscode.QuickPickItemKind.Separator, + key: 'separator', + }, + { + label: '$(settings-gear) Advanced Options...', + description: 'Edit or reset rules manually', + key: 'advanced_options', + }, + ]; + } + + // Create advanced menu items based on current settings and available actions + private static createAdvancedMenuItems(): ActionQuickPickItem[] { + const items: ActionQuickPickItem[] = [ + { + label: '$(edit) Edit Workspace Rules...', + description: 'Opens .vscode/settings.json', + key: 'edit_workspace', + }, + ]; + + if (PairingRuleService.hasCustomRules('workspace')) { + items.push({ + label: '$(clear-all) Reset Workspace Rules', + description: 'Use global or default rules instead', + key: 'reset_workspace', + }); + } + + items.push({ + label: 'Global (User) Settings', + kind: vscode.QuickPickItemKind.Separator, + key: 'separator_global', + }, + { + label: '$(edit) Edit Global Rules...', + description: 'Opens your global settings.json', + key: 'edit_global', + }); + + if (PairingRuleService.hasCustomRules('user')) { + items.push({ + label: '$(clear-all) Reset Global Rules', + description: 'Use the extension default rules instead', + key: 'reset_global', + }); + } + + return items; + } + + // Handle rule selection and ask for save scope (workspace or global) + private static async handleRuleSelection(rule: PairingRule): Promise { + const selection = await vscode.window.showQuickPick( + [ + { + label: 'Save for this Workspace', + description: 'Recommended. Creates a .vscode/settings.json file.', + scope: 'workspace', + }, + { + label: 'Save for all my Projects (Global)', + description: 'Modifies your global user settings.', + scope: 'user', + }, + ], + { + placeHolder: 'Where would you like to save this rule?', + title: 'Save Configuration Scope', + }); + + if (!selection) + return; + + await PairingRuleService.writeRules([rule], selection.scope as 'workspace' | + 'user'); + vscode.window.showInformationMessage(`Successfully set '${rule.label}' as the default extension for the ${selection.scope}.`); + } + + // Handle advanced menu selection and execute the corresponding action + private static async handleAdvancedMenuSelection(key: string): Promise { + const actions = { + edit_workspace: () => vscode.commands.executeCommand( + 'workbench.action.openWorkspaceSettingsFile'), + edit_global: () => + vscode.commands.executeCommand('workbench.action.openSettingsJson'), + reset_workspace: async () => { + await PairingRuleService.resetRules('workspace'); + vscode.window.showInformationMessage( + 'Workspace pairing rules have been reset.'); + }, + reset_global: async () => { + await PairingRuleService.resetRules('user'); + vscode.window.showInformationMessage( + 'Global pairing rules have been reset.'); + }, + }; + + const action = actions[key as keyof typeof actions]; + if (action) + await action(); + } + + // Main configuration wizard - entry point for setting up pairing rules + public static async showConfigurationWizard(): Promise { + const quickPick = + vscode.window.createQuickPick(); + quickPick.title = 'Quick Setup: Choose File Extensions'; + quickPick.placeholder = + 'Choose file extension combination for this workspace, or go to advanced options.'; + quickPick.items = [ + ...PairingRuleUI.createRuleChoices(), + ...PairingRuleUI.createAdvancedOptions() + ]; + + quickPick.onDidChangeSelection(async (selection) => { + if (!selection[0]) + return; + quickPick.hide(); + + const item = selection[0]; + if ('rule' in item) { + await PairingRuleUI.handleRuleSelection(item.rule); + } else if (item.key === 'advanced_options') { + await PairingRuleUI.showAdvancedManagementMenu(); + } + }); + + quickPick.onDidHide(() => quickPick.dispose()); + quickPick.show(); + } + + // Advanced management menu for editing and resetting pairing rules + public static async showAdvancedManagementMenu(): Promise { + const selection = await vscode.window.showQuickPick( + PairingRuleUI.createAdvancedMenuItems(), { + title: 'Advanced Rule Management', + }); + + if (selection?.key) { + await PairingRuleUI.handleAdvancedMenuSelection(selection.key); + } + } +} + +// Backward compatibility export for the main configuration wizard +export const showConfigurationWizard = PairingRuleUI.showConfigurationWizard; \ No newline at end of file diff --git a/src/pairing-rule-service.ts b/src/pairing-rule-service.ts deleted file mode 100644 index e69de29b..00000000 From 85adf58c3260e3f40b5439e93bed0edaa4814cd4 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?=E9=95=BF=E6=9C=9F=E7=B4=A0=E9=A3=9F?= Date: Fri, 18 Jul 2025 19:13:23 +0800 Subject: [PATCH 12/34] fix --- src/create-source-header-pair.ts | 46 ++++++++++++++---------- src/pairing-rule-manager.ts | 62 ++++++++++++++++---------------- 2 files changed, 58 insertions(+), 50 deletions(-) diff --git a/src/create-source-header-pair.ts b/src/create-source-header-pair.ts index 75a93b71..61520162 100644 --- a/src/create-source-header-pair.ts +++ b/src/create-source-header-pair.ts @@ -227,16 +227,16 @@ class PairCreatorService { // Detects programming language from file info (pure business logic) // DETECTION STRATEGY: - // 1. Fast path: Check file extension against definitive lists + // 1. Fast path: Check file extension against definitive extension sets // - .c files are definitely C - // - .cpp/.cc/.cxx files are definitely C++ - // 2. Special case: .h files are ambiguous - // - Look for companion files in same directory - // - .h + .c companion → C language - // - .h + .cpp/.cc/.cxx companion → C++ language - // - No companion found → default to C++ (uncertain) + // - .cpp/.cc/.cxx/.hh/.hpp/.hxx are definitely C++ + // 2. Special case: .h files are ambiguous (could be C or C++) + // - Look for companion files in same directory to determine context + // - If MyClass.h exists with MyClass.cpp -> C++ + // - If utils.h exists with utils.c -> C // 3. Fallback: Use VS Code's language ID detection - // Returns: language type and uncertainty flag for UI decisions + // - Based on file content analysis or user settings + // 4. Default: When all else fails, assume C++ (more common in modern development) public async detectLanguage(languageId?: string, filePath?: string): Promise<{ language: Language, uncertain: boolean }> { if (!languageId || !filePath) { @@ -265,14 +265,22 @@ class PairCreatorService { } // Optimized header file language detection by checking companion files - // COMPANION FILE DETECTION STRATEGY: - // 1. Extract base name from header file (remove .h extension) - // 2. Check for C companion first (.c) - less common, early exit optimization - // - If found: definitely C language - // 3. Check for C++ companions in parallel (.cpp, .cc, .cxx) - // - If any found: definitely C++ language - // 4. No companion found: default to C++ but mark as uncertain - // This helps determine appropriate templates and reduces user confusion + // HEADER FILE DETECTION STRATEGY: + // Problem: .h files are used by both C and C++, making language detection ambiguous + // Solution: Look for companion source files in the same directory + // + // Algorithm: + // 1. Extract base name from header file (e.g., "utils" from "utils.h") + // 2. Check for C companion first (utils.c) - early exit optimization + // - C projects are less common, so checking first allows quick determination + // 3. Check for C++ companions in parallel (utils.cpp, utils.cc, utils.cxx) + // - Use Promise.all for concurrent file existence checks + // 4. Return definitive result if companion found, otherwise default to C++ + // + // Examples: + // - math.h + math.c exists → Detected as C language + // - Vector.h + Vector.cpp exists → Detected as C++ language + // - standalone.h (no companions) → Default to C++ (uncertain=true) private async detectLanguageForHeaderFile(filePath: string): Promise<{ language: Language, uncertain: boolean } | null> { const baseName = path.basename(filePath, '.h'); @@ -954,10 +962,10 @@ class PairCreatorUI { // Main Coordinator Class -// PairCreator coordinates the UI and Service layers to handle the complete file +// SourceHeaderPairCoordinator coordinates the UI and Service layers to handle the complete file // pair creation workflow. It serves as the main entry point and orchestrates // the entire process. -class PairCreator implements vscode.Disposable { +class SourceHeaderPairCoordinator implements vscode.Disposable { private command: vscode.Disposable; private service: PairCreatorService; private ui: PairCreatorUI; @@ -1024,5 +1032,5 @@ class PairCreator implements vscode.Disposable { // Registers the create source/header pair command with the VS Code extension context // This function should be called during extension activation to make the command available export function registerCreateSourceHeaderPairCommand(context: ClangdContext) { - context.subscriptions.push(new PairCreator()); + context.subscriptions.push(new SourceHeaderPairCoordinator()); } diff --git a/src/pairing-rule-manager.ts b/src/pairing-rule-manager.ts index b3fa1d24..0893a1b2 100644 --- a/src/pairing-rule-manager.ts +++ b/src/pairing-rule-manager.ts @@ -1,34 +1,34 @@ -/** - * PAIRING RULE MANAGER - * ==================== - * - * PURPOSE: - * This module manages custom file extension pairing rules for C/C++ header/source files. - * It allows users to configure custom extensions (e.g., .hh/.cc instead of .h/.cpp) - * and provides UI for managing these configurations. - * - * ARCHITECTURE: - * 1. PairingRuleService - Core configuration management - * - Reads/writes VS Code workspace/user settings - * - Validates pairing rules - * - Handles configuration scopes (workspace vs global) - * - * 2. PairingRuleUI - User interface for configuration - * - Quick setup wizard with predefined options - * - Advanced management for editing/resetting rules - * - Scope selection (workspace vs global settings) - * - * WORKFLOW: - * User calls showConfigurationWizard() → - * Presents predefined extension combinations (.h/.cpp, .hh/.cc, etc.) → - * User selects option or goes to advanced management → - * Rule is saved to workspace or global settings → - * Other parts of extension use these rules for file creation - * - * CONFIGURATION STORAGE: - * - Workspace: .vscode/settings.json (clangd.createPair.rules) - * - Global: User settings.json (clangd.createPair.rules) - */ +// +// PAIRING RULE MANAGER +// ==================== +// +// PURPOSE: +// This module manages custom file extension pairing rules for C/C++ header/source files. +// It allows users to configure custom extensions (e.g., .hh/.cc instead of .h/.cpp) +// and provides UI for managing these configurations. +// +// ARCHITECTURE: +// 1. PairingRuleService - Core configuration management +// - Reads/writes VS Code workspace/user settings +// - Validates pairing rules +// - Handles configuration scopes (workspace vs global) +// +// 2. PairingRuleUI - User interface for configuration +// - Quick setup wizard with predefined options +// - Advanced management for editing/resetting rules +// - Scope selection (workspace vs global settings) +// +// WORKFLOW: +// User calls showConfigurationWizard() → +// Presents predefined extension combinations (.h/.cpp, .hh/.cc, etc.) → +// User selects option or goes to advanced management → +// Rule is saved to workspace or global settings → +// Other parts of extension use these rules for file creation +// +// CONFIGURATION STORAGE: +// - Workspace: .vscode/settings.json (clangd.createPair.rules) +// - Global: User settings.json (clangd.createPair.rules) +// import * as vscode from 'vscode'; From 475839e72dffe29f8033de0883304cfe28a410ee Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?=E9=95=BF=E6=9C=9F=E7=B4=A0=E9=A3=9F?= Date: Fri, 18 Jul 2025 19:24:11 +0800 Subject: [PATCH 13/34] fix register. --- package.json | 53 ++++++++++++++++++++++++++++++++ src/create-source-header-pair.ts | 36 ++++++++++++++++------ 2 files changed, 80 insertions(+), 9 deletions(-) diff --git a/package.json b/package.json index bc19f5bc..31cca103 100644 --- a/package.json +++ b/package.json @@ -194,6 +194,59 @@ "type": "boolean", "default": true, "description": "Enable clangd language server features" + }, + "clangd.createPair.rules": { + "type": "array", + "default": [], + "items": { + "type": "object", + "properties": { + "key": { + "type": "string", + "description": "Unique identifier for the pairing rule" + }, + "label": { + "type": "string", + "description": "Display label for the pairing rule" + }, + "description": { + "type": "string", + "description": "Description of what the pairing rule does" + }, + "language": { + "type": "string", + "enum": [ + "c", + "cpp" + ], + "description": "Programming language for this rule" + }, + "headerExt": { + "type": "string", + "description": "File extension for header files (e.g., .h, .hpp)" + }, + "sourceExt": { + "type": "string", + "description": "File extension for source files (e.g., .c, .cpp)" + }, + "isClass": { + "type": "boolean", + "description": "Whether this rule is for creating classes" + }, + "isStruct": { + "type": "boolean", + "description": "Whether this rule is for creating structs" + } + }, + "required": [ + "key", + "label", + "language", + "headerExt", + "sourceExt" + ] + }, + "description": "Custom pairing rules for source/header file creation" } } }, diff --git a/src/create-source-header-pair.ts b/src/create-source-header-pair.ts index 61520162..810335f2 100644 --- a/src/create-source-header-pair.ts +++ b/src/create-source-header-pair.ts @@ -21,7 +21,7 @@ // - Custom rule management integration // - Error handling and user feedback // -// 3. SourceHeaderPairCoordinator (Main Coordinator) +// 3. PairCoordinator (Main Coordinator) // - Orchestrates the entire workflow // - Registers VS Code command // - Manages component lifecycle @@ -48,6 +48,8 @@ import * as path from 'path'; import * as vscode from 'vscode'; +import { showConfigurationWizard } from './pairing-rule-manager'; + import { ClangdContext } from './clangd-context'; import { PairingRule, PairingRuleService } from './pairing-rule-manager'; @@ -962,24 +964,34 @@ class PairCreatorUI { // Main Coordinator Class -// SourceHeaderPairCoordinator coordinates the UI and Service layers to handle the complete file +// PairCoordinator coordinates the UI and Service layers to handle the complete file // pair creation workflow. It serves as the main entry point and orchestrates // the entire process. -class SourceHeaderPairCoordinator implements vscode.Disposable { - private command: vscode.Disposable; +class PairCoordinator implements vscode.Disposable { + private newPairCommand: vscode.Disposable; + private configureRulesCommand: vscode.Disposable; private service: PairCreatorService; private ui: PairCreatorUI; - // Constructor registers the VS Code command for creating source/header pairs + // Constructor registers the VS Code commands for creating source/header pairs and configuring rules constructor() { this.service = new PairCreatorService(); this.ui = new PairCreatorUI(this.service); - this.command = vscode.commands.registerCommand( - 'clangd.createSourceHeaderPair', this.create, this); + + // Register the main command for creating new source/header pairs + this.newPairCommand = vscode.commands.registerCommand( + 'clangd.newSourcePair', this.create, this); + + // Register the command for configuring pairing rules + this.configureRulesCommand = vscode.commands.registerCommand( + 'clangd.newSourcePair.configureRules', this.configureRules, this); } // Dispose method for cleanup when extension is deactivated - dispose() { this.command.dispose(); } + dispose() { + this.newPairCommand.dispose(); + this.configureRulesCommand.dispose(); + } // Main entry point for the file pair creation process // Orchestrates the entire workflow using the service and UI layers @@ -1027,10 +1039,16 @@ class SourceHeaderPairCoordinator implements vscode.Disposable { 'An unexpected error occurred.'); } } + + // Opens the configuration wizard for source/header pairing rules + // Delegates to the PairingRuleManager's configuration interface + public async configureRules(): Promise { + await showConfigurationWizard(); + } } // Registers the create source/header pair command with the VS Code extension context // This function should be called during extension activation to make the command available export function registerCreateSourceHeaderPairCommand(context: ClangdContext) { - context.subscriptions.push(new SourceHeaderPairCoordinator()); + context.subscriptions.push(new PairCoordinator()); } From 4345afce772e7492dfcb3e859beaf09f8506a059 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?=E9=95=BF=E6=9C=9F=E7=B4=A0=E9=A3=9F?= Date: Fri, 18 Jul 2025 19:45:18 +0800 Subject: [PATCH 14/34] npm run format --- src/create-source-header-pair.ts | 546 ++++++++++++++++--------------- src/pairing-rule-manager.ts | 151 ++++----- 2 files changed, 363 insertions(+), 334 deletions(-) diff --git a/src/create-source-header-pair.ts b/src/create-source-header-pair.ts index 810335f2..34ac1ec0 100644 --- a/src/create-source-header-pair.ts +++ b/src/create-source-header-pair.ts @@ -1,12 +1,12 @@ // // CREATE SOURCE HEADER PAIR // ========================= -// +// // PURPOSE: -// This module provides functionality to create matching header/source file pairs -// for C/C++ development. It intelligently detects language context, offers -// appropriate templates, and handles custom file extensions. -// +// This module provides functionality to create matching header/source file +// pairs for C/C++ development. It intelligently detects language context, +// offers appropriate templates, and handles custom file extensions. +// // ARCHITECTURE: // 1. PairCreatorService (Business Logic Layer) // - Language detection from file context @@ -14,23 +14,23 @@ // - Template content generation // - File system operations (read/write) // - Custom extension handling -// +// // 2. PairCreatorUI (User Interface Layer) // - User prompts and input validation // - Template selection dialogs // - Custom rule management integration // - Error handling and user feedback -// +// // 3. PairCoordinator (Main Coordinator) // - Orchestrates the entire workflow // - Registers VS Code command // - Manages component lifecycle -// +// // WORKFLOW: // Command triggered → Detect target directory → Analyze language context → // Check for custom rules → Present template choices → Get file name → // Validate uniqueness → Generate content → Write files → Open in editor -// +// // FEATURES: // - Smart language detection (C vs C++) // - Multiple template types (class, struct, empty) @@ -39,7 +39,7 @@ // - Cross-language template options // - Workspace-aware directory selection // - Input validation for C/C++ identifiers -// +// // INTEGRATION: // Uses PairingRuleManager for custom extension configurations // Integrates with VS Code file system and editor APIs @@ -48,14 +48,16 @@ import * as path from 'path'; import * as vscode from 'vscode'; -import { showConfigurationWizard } from './pairing-rule-manager'; - -import { ClangdContext } from './clangd-context'; -import { PairingRule, PairingRuleService } from './pairing-rule-manager'; +import {ClangdContext} from './clangd-context'; +import { + PairingRule, + PairingRuleService, + showConfigurationWizard +} from './pairing-rule-manager'; // Types for better type safety -type Language = 'c' | 'cpp'; -type TemplateKey = 'CPP_CLASS' | 'CPP_STRUCT' | 'C_STRUCT' | 'C_EMPTY' | 'CPP_EMPTY'; +type Language = 'c'|'cpp'; +type TemplateKey = 'CPP_CLASS'|'CPP_STRUCT'|'C_STRUCT'|'C_EMPTY'|'CPP_EMPTY'; // Regular expression patterns to validate C/C++ identifiers const VALIDATION_PATTERNS = { @@ -85,7 +87,7 @@ const TEMPLATE_RULES: PairingRule[] = [ key: 'cpp_class', label: '$(symbol-class) C++ Class', description: - 'Creates a Header/Source file pair with a boilerplate class definition.', + 'Creates a Header/Source file pair with a boilerplate class definition.', language: 'cpp' as const, headerExt: '.h', sourceExt: '.cpp', @@ -95,7 +97,7 @@ const TEMPLATE_RULES: PairingRule[] = [ key: 'cpp_struct', label: '$(symbol-struct) C++ Struct', description: - 'Creates a Header/Source file pair with a boilerplate struct definition.', + 'Creates a Header/Source file pair with a boilerplate struct definition.', language: 'cpp' as const, headerExt: '.h', sourceExt: '.cpp', @@ -208,7 +210,6 @@ class PairCreatorService { cpp: new Set(['.cpp', '.cc', '.cxx', '.hh', '.hpp', '.hxx']) }; - // Optimized file existence check with caching to improve performance private static async fileExists(filePath: string): Promise { if (this.fileStatCache.has(filePath)) { @@ -216,8 +217,8 @@ class PairCreatorService { } const promise = - Promise.resolve(vscode.workspace.fs.stat(vscode.Uri.file(filePath)) - .then(() => true, () => false)); + Promise.resolve(vscode.workspace.fs.stat(vscode.Uri.file(filePath)) + .then(() => true, () => false)); this.fileStatCache.set(filePath, promise); @@ -238,21 +239,22 @@ class PairCreatorService { // - If utils.h exists with utils.c -> C // 3. Fallback: Use VS Code's language ID detection // - Based on file content analysis or user settings - // 4. Default: When all else fails, assume C++ (more common in modern development) + // 4. Default: When all else fails, assume C++ (more common in modern + // development) public async detectLanguage(languageId?: string, filePath?: string): - Promise<{ language: Language, uncertain: boolean }> { + Promise<{language: Language, uncertain: boolean}> { if (!languageId || !filePath) { - return { language: 'cpp', uncertain: true }; + return {language: 'cpp', uncertain: true}; } const ext = path.extname(filePath); // Fast path for definitive extensions if (PairCreatorService.DEFINITIVE_EXTENSIONS.c.has(ext)) { - return { language: 'c', uncertain: false }; + return {language: 'c', uncertain: false}; } if (PairCreatorService.DEFINITIVE_EXTENSIONS.cpp.has(ext)) { - return { language: 'cpp', uncertain: false }; + return {language: 'cpp', uncertain: false}; } // Special handling for .h files with companion file detection @@ -263,28 +265,29 @@ class PairCreatorService { } // Fallback to language ID - return { language: languageId === 'c' ? 'c' : 'cpp', uncertain: true }; + return {language: languageId === 'c' ? 'c' : 'cpp', uncertain: true}; } // Optimized header file language detection by checking companion files // HEADER FILE DETECTION STRATEGY: - // Problem: .h files are used by both C and C++, making language detection ambiguous - // Solution: Look for companion source files in the same directory - // + // Problem: .h files are used by both C and C++, making language detection + // ambiguous Solution: Look for companion source files in the same directory + // // Algorithm: // 1. Extract base name from header file (e.g., "utils" from "utils.h") // 2. Check for C companion first (utils.c) - early exit optimization - // - C projects are less common, so checking first allows quick determination + // - C projects are less common, so checking first allows quick + // determination // 3. Check for C++ companions in parallel (utils.cpp, utils.cc, utils.cxx) // - Use Promise.all for concurrent file existence checks // 4. Return definitive result if companion found, otherwise default to C++ - // + // // Examples: // - math.h + math.c exists → Detected as C language - // - Vector.h + Vector.cpp exists → Detected as C++ language + // - Vector.h + Vector.cpp exists → Detected as C++ language // - standalone.h (no companions) → Default to C++ (uncertain=true) private async detectLanguageForHeaderFile(filePath: string): - Promise<{ language: Language, uncertain: boolean } | null> { + Promise<{language: Language, uncertain: boolean}|null> { const baseName = path.basename(filePath, '.h'); const dirPath = path.dirname(filePath); @@ -292,21 +295,21 @@ class PairCreatorService { // exit) const cFile = path.join(dirPath, `${baseName}.c`); if (await PairCreatorService.fileExists(cFile)) { - return { language: 'c', uncertain: false }; + return {language: 'c', uncertain: false}; } // Check for C++ companion files in parallel const cppExtensions = ['.cpp', '.cc', '.cxx']; const cppChecks = - cppExtensions.map(ext => PairCreatorService.fileExists( - path.join(dirPath, `${baseName}${ext}`))); + cppExtensions.map(ext => PairCreatorService.fileExists( + path.join(dirPath, `${baseName}${ext}`))); const results = await Promise.all(cppChecks); if (results.some((exists: boolean) => exists)) { - return { language: 'cpp', uncertain: false }; + return {language: 'cpp', uncertain: false}; } - return { language: 'cpp', uncertain: true }; + return {language: 'cpp', uncertain: true}; } // Gets all available pairing rules (custom + workspace + user) @@ -318,21 +321,22 @@ class PairCreatorService { } // Gets custom C++ extensions if available from configuration - public getCustomCppExtensions(): { headerExt: string, sourceExt: string } | null { + public getCustomCppExtensions(): {headerExt: string, sourceExt: string}|null { const allRules = this.getAllPairingRules(); - const cppCustomRule = allRules.find((rule: PairingRule) => rule.language === 'cpp'); + const cppCustomRule = + allRules.find((rule: PairingRule) => rule.language === 'cpp'); return cppCustomRule ? { headerExt: cppCustomRule.headerExt, sourceExt: cppCustomRule.sourceExt } - : null; + : null; } // Converts string to PascalCase efficiently (pure function) public toPascalCase(input: string): string { return input.split(/[-_]+/) - .map(word => word.charAt(0).toUpperCase() + word.slice(1)) - .join(''); + .map(word => word.charAt(0).toUpperCase() + word.slice(1)) + .join(''); } // Gets default placeholder based on rule type (pure function) @@ -343,30 +347,31 @@ class PairCreatorService { if (rule.isStruct) { return rule.language === 'cpp' ? DEFAULT_PLACEHOLDERS.CPP_STRUCT - : DEFAULT_PLACEHOLDERS.C_STRUCT; + : DEFAULT_PLACEHOLDERS.C_STRUCT; } return rule.language === 'c' ? DEFAULT_PLACEHOLDERS.C_EMPTY - : DEFAULT_PLACEHOLDERS.CPP_EMPTY; + : DEFAULT_PLACEHOLDERS.CPP_EMPTY; } // Optimized line ending detection based on VS Code settings and platform public getLineEnding(): string { const eolSetting = - vscode.workspace.getConfiguration('files').get('eol'); + vscode.workspace.getConfiguration('files').get('eol'); return eolSetting === '\n' || eolSetting === '\r\n' ? eolSetting - : process.platform === 'win32' ? '\r\n' - : '\n'; + : process.platform === 'win32' ? '\r\n' + : '\n'; } // Generates file content with improved template selection - public generateFileContent(fileName: string, eol: string, rule: PairingRule): { headerContent: string; sourceContent: string; } { + public generateFileContent(fileName: string, eol: string, rule: PairingRule): + {headerContent: string; sourceContent: string;} { const templateKey: TemplateKey = - rule.isClass ? 'CPP_CLASS' + rule.isClass ? 'CPP_CLASS' : rule.isStruct ? (rule.language === 'cpp' ? 'CPP_STRUCT' : 'C_STRUCT') - : rule.language === 'c' ? 'C_EMPTY' - : 'CPP_EMPTY'; + : rule.language === 'c' ? 'C_EMPTY' + : 'CPP_EMPTY'; const templates = FILE_TEMPLATES[templateKey]; const context = { @@ -386,15 +391,15 @@ class PairCreatorService { // Optimized template variable substitution using regex replacement private applyTemplate(template: string, - context: Record): string { + context: Record): string { // Pre-compile regex for better performance if used frequently return template.replace(/\{\{(\w+)\}\}/g, (_, key) => context[key] ?? ''); } // File existence check with parallel processing for multiple files public async checkFileExistence(headerPath: vscode.Uri, - sourcePath: vscode.Uri): - Promise { + sourcePath: vscode.Uri): + Promise { const checks = [headerPath, sourcePath].map(async (uri) => { try { await vscode.workspace.fs.stat(uri); @@ -410,25 +415,26 @@ class PairCreatorService { // Optimized file writing with error handling and parallel writes public async writeFiles(headerPath: vscode.Uri, sourcePath: vscode.Uri, - headerContent: string, - sourceContent: string): Promise { + headerContent: string, + sourceContent: string): Promise { try { await Promise.all([ vscode.workspace.fs.writeFile(headerPath, - Buffer.from(headerContent, 'utf8')), + Buffer.from(headerContent, 'utf8')), vscode.workspace.fs.writeFile(sourcePath, - Buffer.from(sourceContent, 'utf8')) + Buffer.from(sourceContent, 'utf8')) ]); } catch (error) { - throw new Error(`Failed to create files: ${error instanceof Error ? error.message : 'Unknown error'}`); + throw new Error(`Failed to create files: ${ + error instanceof Error ? error.message : 'Unknown error'}`); } } // Smart target directory detection (pure business logic) public async getTargetDirectory(activeDocumentPath?: string, - workspaceFolders - ?: readonly vscode.WorkspaceFolder[]): - Promise { + workspaceFolders + ?: readonly vscode.WorkspaceFolder[]): + Promise { // Prefer current file's directory if (activeDocumentPath) { return vscode.Uri.file(path.dirname(activeDocumentPath)); @@ -445,10 +451,10 @@ class PairCreatorService { // Language mismatch warning logic (pure business logic) public async shouldShowLanguageMismatchWarning(language: Language, - result: PairingRule, - currentDir?: string, - activeFilePath - ?: string): Promise { + result: PairingRule, + currentDir?: string, + activeFilePath + ?: string): Promise { if (!currentDir || !activeFilePath) { return true; } @@ -458,19 +464,19 @@ class PairCreatorService { } return this.checkForCorrespondingSourceFiles(currentDir, activeFilePath, - language); + language); } // Check for C++ files in directory to inform language mismatch warnings private async checkForCppFilesInDirectory(dirPath: string): Promise { try { const entries = - await vscode.workspace.fs.readDirectory(vscode.Uri.file(dirPath)); + await vscode.workspace.fs.readDirectory(vscode.Uri.file(dirPath)); const hasCppFiles = - entries.some(([fileName, fileType]) => - fileType === vscode.FileType.File && - PairCreatorService.DEFINITIVE_EXTENSIONS.cpp.has( - path.extname(fileName))); + entries.some(([fileName, fileType]) => + fileType === vscode.FileType.File && + PairCreatorService.DEFINITIVE_EXTENSIONS.cpp.has( + path.extname(fileName))); return !hasCppFiles; // Show warning if NO C++ files found } catch { return true; // Show warning if can't check @@ -479,17 +485,18 @@ class PairCreatorService { // Check for corresponding source files to inform language mismatch warnings private async checkForCorrespondingSourceFiles( - dirPath: string, filePath: string, language: Language): Promise { + dirPath: string, filePath: string, language: Language): Promise { const baseName = path.basename(filePath, path.extname(filePath)); const extensions = language === 'c' ? ['.c'] : ['.cpp', '.cc', '.cxx']; const checks = extensions.map(ext => PairCreatorService.fileExists( - path.join(dirPath, `${baseName}${ext}`))); + path.join(dirPath, `${baseName}${ext}`))); try { const results = await Promise.all(checks); return !results.some( - (exists: boolean) => exists); // Show warning if NO corresponding files found + (exists: boolean) => + exists); // Show warning if NO corresponding files found } catch { return true; // Show warning if can't check } @@ -511,27 +518,27 @@ class PairCreatorUI { if (activeEditor?.document && !activeEditor.document.isUntitled) { const fileName = - path.basename(activeEditor.document.fileName, - path.extname(activeEditor.document.fileName)); + path.basename(activeEditor.document.fileName, + path.extname(activeEditor.document.fileName)); return rule.language === 'c' ? fileName - : this.service.toPascalCase(fileName); + : this.service.toPascalCase(fileName); } return this.service.getDefaultPlaceholder(rule); } // Gets target directory with UI fallback for multiple workspace folders - public async getTargetDirectory(): Promise { + public async getTargetDirectory(): Promise { const activeEditor = vscode.window.activeTextEditor; const activeDocumentPath = - activeEditor?.document && !activeEditor.document.isUntitled - ? activeEditor.document.uri.fsPath - : undefined; + activeEditor?.document && !activeEditor.document.isUntitled + ? activeEditor.document.uri.fsPath + : undefined; const workspaceFolders = vscode.workspace.workspaceFolders; // Try service layer first const result = await this.service.getTargetDirectory(activeDocumentPath, - workspaceFolders); + workspaceFolders); if (result) { return result; } @@ -539,7 +546,7 @@ class PairCreatorUI { // Handle multiple workspace folders with UI if (workspaceFolders && workspaceFolders.length > 1) { const selected = await vscode.window.showWorkspaceFolderPick( - { placeHolder: 'Select workspace folder for new files' }); + {placeHolder: 'Select workspace folder for new files'}); return selected?.uri; } @@ -548,30 +555,30 @@ class PairCreatorUI { // Checks if language mismatch warning should be shown with UI context private async shouldShowLanguageMismatchWarning(language: Language, - result: PairingRule): - Promise { + result: PairingRule): + Promise { const activeEditor = vscode.window.activeTextEditor; const currentDir = - activeEditor?.document && !activeEditor.document.isUntitled - ? path.dirname(activeEditor.document.uri.fsPath) - : undefined; + activeEditor?.document && !activeEditor.document.isUntitled + ? path.dirname(activeEditor.document.uri.fsPath) + : undefined; const activeFilePath = - activeEditor?.document && !activeEditor.document.isUntitled - ? activeEditor.document.uri.fsPath - : undefined; + activeEditor?.document && !activeEditor.document.isUntitled + ? activeEditor.document.uri.fsPath + : undefined; return this.service.shouldShowLanguageMismatchWarning( - language, result, currentDir, activeFilePath); + language, result, currentDir, activeFilePath); } // Detects programming language from active editor context public async detectLanguage(): - Promise<{ language: Language, uncertain: boolean }> { + Promise<{language: Language, uncertain: boolean}> { const activeEditor = vscode.window.activeTextEditor; const languageId = activeEditor?.document?.languageId; const filePath = activeEditor?.document && !activeEditor.document.isUntitled - ? activeEditor.document.uri.fsPath - : undefined; + ? activeEditor.document.uri.fsPath + : undefined; return this.service.detectLanguage(languageId, filePath); } @@ -587,42 +594,44 @@ class PairCreatorUI { return rule; } - const { headerExt, sourceExt } = customExtensions; + const {headerExt, sourceExt} = customExtensions; // Adapt description for display const replacementPattern = - /Header\/Source|\.h(?:h|pp|xx)?\/\.c(?:pp|c|xx)?/g; + /Header\/Source|\.h(?:h|pp|xx)?\/\.c(?:pp|c|xx)?/g; const newDescription = rule.description.replace( - replacementPattern, `${headerExt}/${sourceExt}`); + replacementPattern, `${headerExt}/${sourceExt}`); - return { ...rule, description: newDescription, headerExt, sourceExt }; + return {...rule, description: newDescription, headerExt, sourceExt}; } - // Prepares template choices for UI display with proper ordering and adaptation - private prepareTemplateChoices(language: 'c' | 'cpp', - uncertain: boolean): PairingRule[] { + // Prepares template choices for UI display with proper ordering and + // adaptation + private prepareTemplateChoices(language: 'c'|'cpp', + uncertain: boolean): PairingRule[] { const desiredOrder = - uncertain - ? ['cpp_empty', 'c_empty', 'cpp_class', 'cpp_struct', 'c_struct'] + uncertain + ? ['cpp_empty', 'c_empty', 'cpp_class', 'cpp_struct', 'c_struct'] : language === 'c' - ? ['c_empty', 'c_struct', 'cpp_empty', 'cpp_class', 'cpp_struct'] - : ['cpp_empty', 'cpp_class', 'cpp_struct', 'c_empty', 'c_struct']; + ? ['c_empty', 'c_struct', 'cpp_empty', 'cpp_class', 'cpp_struct'] + : ['cpp_empty', 'cpp_class', 'cpp_struct', 'c_empty', 'c_struct']; return [...TEMPLATE_RULES] - .sort((a, b) => - desiredOrder.indexOf(a.key) - desiredOrder.indexOf(b.key)) - .map(rule => this.adaptRuleForDisplay(rule)); + .sort((a, b) => + desiredOrder.indexOf(a.key) - desiredOrder.indexOf(b.key)) + .map(rule => this.adaptRuleForDisplay(rule)); } // Filters custom rules by language and prepares them for display private prepareCustomRulesChoices(allRules: PairingRule[], - language: 'c' | 'cpp'): { - languageRules: PairingRule[], - adaptedDefaultTemplates: PairingRule[], - otherLanguageTemplates: PairingRule[], - cleanedCustomRules: PairingRule[] - } { - const languageRules = allRules.filter((rule: PairingRule) => rule.language === language); + language: 'c'|'cpp'): { + languageRules: PairingRule[], + adaptedDefaultTemplates: PairingRule[], + otherLanguageTemplates: PairingRule[], + cleanedCustomRules: PairingRule[] + } { + const languageRules = + allRules.filter((rule: PairingRule) => rule.language === language); const customExt = languageRules.length > 0 ? languageRules[0] : null; let adaptedDefaultTemplates: PairingRule[] = []; @@ -630,63 +639,69 @@ class PairCreatorUI { if (customExt && language === 'cpp') { // For C++, adapt default templates with custom extensions adaptedDefaultTemplates = - TEMPLATE_RULES - .filter( - template => - template.language === 'cpp' && - !languageRules.some( - customRule => - customRule.isClass === template.isClass && - customRule.isStruct === template.isStruct && - (customRule.isClass || customRule.isStruct || - (!customRule.isClass && !customRule.isStruct && - !template.isClass && !template.isStruct)))) - .map( - template => ({ - ...template, - key: `${template.key}_adapted`, - headerExt: customExt.headerExt, - sourceExt: customExt.sourceExt, - description: - template.description - .replace( - /Header\/Source/g, - `${customExt.headerExt}/${customExt.sourceExt}`) - .replace(/\.h\/\.cpp/g, `${customExt.headerExt}/${customExt.sourceExt}`) - .replace(/basic \.h\/\.cpp/g, - `basic ${customExt.headerExt}/${customExt.sourceExt}`) - .replace(/Creates a \.h\/\.cpp/g, - `Creates a ${customExt.headerExt}/${customExt.sourceExt}`) - })); + TEMPLATE_RULES + .filter( + template => + template.language === 'cpp' && + !languageRules.some( + customRule => + customRule.isClass === template.isClass && + customRule.isStruct === template.isStruct && + (customRule.isClass || customRule.isStruct || + (!customRule.isClass && !customRule.isStruct && + !template.isClass && !template.isStruct)))) + .map( + template => ({ + ...template, + key: `${template.key}_adapted`, + headerExt: customExt.headerExt, + sourceExt: customExt.sourceExt, + description: + template.description + .replace( + /Header\/Source/g, + `${customExt.headerExt}/${customExt.sourceExt}`) + .replace(/\.h\/\.cpp/g, `${customExt.headerExt}/${ + customExt.sourceExt}`) + .replace(/basic \.h\/\.cpp/g, + `basic ${customExt.headerExt}/${ + customExt.sourceExt}`) + .replace(/Creates a \.h\/\.cpp/g, + `Creates a ${customExt.headerExt}/${ + customExt.sourceExt}`) + })); } else { // Standard adaptation for non-custom or C language adaptedDefaultTemplates = - TEMPLATE_RULES - .filter(template => - template.language === language && - !languageRules.some( - customRule => - customRule.headerExt === template.headerExt && - customRule.sourceExt === template.sourceExt && - customRule.isClass === template.isClass && - customRule.isStruct === template.isStruct)) - .map(template => this.adaptRuleForDisplay(template)); + TEMPLATE_RULES + .filter(template => + template.language === language && + !languageRules.some( + customRule => + customRule.headerExt === template.headerExt && + customRule.sourceExt === template.sourceExt && + customRule.isClass === template.isClass && + customRule.isStruct === template.isStruct)) + .map(template => this.adaptRuleForDisplay(template)); } const otherLanguageTemplates = - TEMPLATE_RULES.filter(template => template.language !== language) - .map(template => this.adaptRuleForDisplay(template)); + TEMPLATE_RULES.filter(template => template.language !== language) + .map(template => this.adaptRuleForDisplay(template)); const cleanedCustomRules = allRules.map( - (rule: PairingRule) => ({ - ...rule, - label: rule.label.includes('$(') - ? rule.label - : `$(new-file) ${rule.language === 'cpp' ? 'C++' : 'C'} Pair (${rule.headerExt}/${rule.sourceExt})`, - description: rule.description.startsWith('Creates a') - ? rule.description - : `Creates a ${rule.headerExt}/${rule.sourceExt} file pair with header guards.` - })); + (rule: PairingRule) => ({ + ...rule, + label: rule.label.includes('$(') + ? rule.label + : `$(new-file) ${ + rule.language === 'cpp' ? 'C++' : 'C'} Pair (${ + rule.headerExt}/${rule.sourceExt})`, + description: rule.description.startsWith('Creates a') + ? rule.description + : `Creates a ${rule.headerExt}/${ + rule.sourceExt} file pair with header guards.` + })); return { languageRules, @@ -696,18 +711,19 @@ class PairCreatorUI { }; } - // Checks for existing custom pairing rules and offers to create them if not found - // For C++, presents options to use custom rules or create new ones. - // For C, always uses default templates. - // Returns selected rule, null if cancelled, undefined for defaults, or 'use_default' flag - public async checkAndOfferCustomRules(language: 'c' | 'cpp', - uncertain: boolean): - Promise { + // Checks for existing custom pairing rules and offers to create them if not + // found For C++, presents options to use custom rules or create new ones. For + // C, always uses default templates. Returns selected rule, null if cancelled, + // undefined for defaults, or 'use_default' flag + public async checkAndOfferCustomRules(language: 'c'|'cpp', + uncertain: boolean): + Promise { if (language === 'c') return undefined; // Always use default C templates const allRules = this.service.getAllPairingRules(); - const languageRules = allRules.filter((rule: PairingRule) => rule.language === language); + const languageRules = + allRules.filter((rule: PairingRule) => rule.language === language); if (languageRules.length > 0) { const result = await this.selectFromCustomRules(allRules, language); @@ -728,11 +744,12 @@ class PairCreatorUI { } // Presents a selection dialog for custom pairing rules - // Combines custom rules with adapted default templates and cross-language options - // Returns selected rule, undefined if cancelled, or 'use_default' flag + // Combines custom rules with adapted default templates and cross-language + // options Returns selected rule, undefined if cancelled, or 'use_default' + // flag public async selectFromCustomRules(allRules: PairingRule[], - language: 'c' | 'cpp'): - Promise { + language: 'c'|'cpp'): + Promise { const { cleanedCustomRules, @@ -746,7 +763,7 @@ class PairCreatorUI { key: 'use_default', label: '$(list-unordered) Use Default Templates', description: - 'Use the built-in default pairing rules instead of custom rules', + 'Use the built-in default pairing rules instead of custom rules', isSpecial: true } ]; @@ -759,7 +776,7 @@ class PairCreatorUI { if (!result) return undefined; if ('isSpecial' in result && result.isSpecial && - result.key === 'use_default') + result.key === 'use_default') return 'use_default'; return result as PairingRule; } @@ -767,56 +784,56 @@ class PairCreatorUI { // Shows a dialog offering to create custom pairing rules for C++ // Only applicable for C++ since C uses standard .c/.h extensions // Returns true to create rules, false to dismiss, null if cancelled - public async offerToCreateCustomRules(language: 'c' | - 'cpp'): Promise { + public async offerToCreateCustomRules(language: 'c'| + 'cpp'): Promise { if (language === 'c') return false; const result = await vscode.window.showInformationMessage( - `No custom pairing rules found for C++. Would you like to create custom rules to use different file extensions (e.g., .cc/.hh instead of .cpp/.h)?`, - { modal: false }, 'Create Custom Rules', 'Dismiss'); + `No custom pairing rules found for C++. Would you like to create custom rules to use different file extensions (e.g., .cc/.hh instead of .cpp/.h)?`, + {modal: false}, 'Create Custom Rules', 'Dismiss'); return result === 'Create Custom Rules' ? true - : result === 'Dismiss' ? false - : null; + : result === 'Dismiss' ? false + : null; } // Guides the user through creating custom pairing rules for C++ // Offers common extension combinations or allows custom input // Saves the rule to workspace or global settings // Returns the created custom rule or undefined if cancelled - public async createCustomRules(language: 'c' | - 'cpp'): Promise { + public async createCustomRules(language: 'c'| + 'cpp'): Promise { if (language === 'c') return undefined; const commonExtensions = [ - { label: '.h / .cpp (Default)', headerExt: '.h', sourceExt: '.cpp' }, - { label: '.hh / .cc (Alternative)', headerExt: '.hh', sourceExt: '.cc' }, { + {label: '.h / .cpp (Default)', headerExt: '.h', sourceExt: '.cpp'}, + {label: '.hh / .cc (Alternative)', headerExt: '.hh', sourceExt: '.cc'}, { label: '.hpp / .cpp (Header Plus Plus)', headerExt: '.hpp', sourceExt: '.cpp' }, - { label: '.hxx / .cxx (Extended)', headerExt: '.hxx', sourceExt: '.cxx' }, - { label: 'Custom Extensions', headerExt: '', sourceExt: '' } + {label: '.hxx / .cxx (Extended)', headerExt: '.hxx', sourceExt: '.cxx'}, + {label: 'Custom Extensions', headerExt: '', sourceExt: ''} ]; const selectedExtension = - await vscode.window.showQuickPick(commonExtensions, { - placeHolder: `Select file extensions for C++ files`, - title: 'Choose File Extensions' - }); + await vscode.window.showQuickPick(commonExtensions, { + placeHolder: `Select file extensions for C++ files`, + title: 'Choose File Extensions' + }); if (!selectedExtension) return undefined; - let { headerExt, sourceExt } = selectedExtension; + let {headerExt, sourceExt} = selectedExtension; if (!headerExt || !sourceExt) { const validateExt = (text: string) => - (!text || !text.startsWith('.') || text.length < 2) - ? 'Please enter a valid file extension starting with a dot (e.g., .h)' - : null; + (!text || !text.startsWith('.') || text.length < 2) + ? 'Please enter a valid file extension starting with a dot (e.g., .h)' + : null; headerExt = await vscode.window.showInputBox({ prompt: 'Enter header file extension (e.g., .h, .hh, .hpp)', @@ -841,50 +858,50 @@ class PairCreatorUI { key: `custom_cpp_${Date.now()}`, label: `$(new-file) C++ Pair (${headerExt}/${sourceExt})`, description: - `Creates a ${headerExt}/${sourceExt} file pair with header guards.`, + `Creates a ${headerExt}/${sourceExt} file pair with header guards.`, language: 'cpp', headerExt, sourceExt }; const saveLocation = await vscode.window.showQuickPick( - [ - { - label: 'Workspace Settings', - description: 'Save to current workspace only', - value: 'workspace' - }, + [ + { + label: 'Workspace Settings', + description: 'Save to current workspace only', + value: 'workspace' + }, + { + label: 'Global Settings', + description: 'Save to user settings (available in all workspaces)', + value: 'user' + } + ], { - label: 'Global Settings', - description: 'Save to user settings (available in all workspaces)', - value: 'user' - } - ], - { - placeHolder: 'Where would you like to save this custom rule?', - title: 'Save Location' - }); + placeHolder: 'Where would you like to save this custom rule?', + title: 'Save Location' + }); if (!saveLocation) return undefined; try { const existingRules = PairingRuleService.getRules( - saveLocation.value as 'workspace' | 'user') || - []; + saveLocation.value as 'workspace' | 'user') || + []; await PairingRuleService.writeRules([...existingRules, customRule], - saveLocation.value as 'workspace' | - 'user'); + saveLocation.value as 'workspace' | + 'user'); const locationText = - saveLocation.value === 'workspace' ? 'workspace' : 'global'; + saveLocation.value === 'workspace' ? 'workspace' : 'global'; vscode.window.showInformationMessage( - `Custom pairing rule saved to ${locationText} settings.`); + `Custom pairing rule saved to ${locationText} settings.`); return customRule; } catch (error: any) { vscode.window.showErrorMessage( - `Failed to save custom rule: ${error.message}`); + `Failed to save custom rule: ${error.message}`); return undefined; } } @@ -892,10 +909,10 @@ class PairCreatorUI { // Prompts the user to select a pairing rule from available options // First checks for custom rules, then falls back to default templates // Returns selected pairing rule or undefined if cancelled - public async promptForPairingRule(language: 'c' | 'cpp', uncertain: boolean): - Promise { + public async promptForPairingRule(language: 'c'|'cpp', uncertain: boolean): + Promise { const customRulesResult = - await this.checkAndOfferCustomRules(language, uncertain); + await this.checkAndOfferCustomRules(language, uncertain); if (customRulesResult === null) return undefined; @@ -914,15 +931,16 @@ class PairCreatorUI { if (result && !uncertain && language !== result.language) { const shouldShowWarning = - await this.shouldShowLanguageMismatchWarning(language, result); + await this.shouldShowLanguageMismatchWarning(language, result); if (shouldShowWarning) { const detectedLangName = language === 'c' ? 'C' : 'C++'; const selectedLangName = result.language === 'c' ? 'C' : 'C++'; const shouldContinue = await vscode.window.showWarningMessage( - `You're working in a ${detectedLangName} file but selected a ${selectedLangName} template. This may create files with incompatible extensions or content.`, - 'Continue', 'Cancel'); + `You're working in a ${detectedLangName} file but selected a ${ + selectedLangName} template. This may create files with incompatible extensions or content.`, + 'Continue', 'Cancel'); if (shouldContinue !== 'Continue') return undefined; @@ -933,58 +951,63 @@ class PairCreatorUI { } // Prompts the user to enter a name for the new file pair - // Validates input as a valid C/C++ identifier and provides context-appropriate prompts - // Returns the entered file name or undefined if cancelled - public async promptForFileName(rule: PairingRule): Promise { + // Validates input as a valid C/C++ identifier and provides + // context-appropriate prompts Returns the entered file name or undefined if + // cancelled + public async promptForFileName(rule: PairingRule): Promise { const prompt = rule.isClass ? 'Please enter the name for the new C++ class.' - : rule.isStruct - ? `Please enter the name for the new ${rule.language.toUpperCase()} struct.` - : `Please enter the base name for the new ${rule.language.toUpperCase()} file pair.`; + : rule.isStruct + ? `Please enter the name for the new ${ + rule.language.toUpperCase()} struct.` + : `Please enter the base name for the new ${ + rule.language.toUpperCase()} file pair.`; return vscode.window.showInputBox({ prompt, placeHolder: this.getPlaceholder(rule), validateInput: (text) => - VALIDATION_PATTERNS.IDENTIFIER.test(text?.trim() || '') - ? null - : 'Invalid C/C++ identifier.', + VALIDATION_PATTERNS.IDENTIFIER.test(text?.trim() || '') + ? null + : 'Invalid C/C++ identifier.', title: 'Create Source/Header Pair' }); } // Shows success message and opens the newly created header file public async showSuccessAndOpenFile(headerPath: vscode.Uri, - sourcePath: vscode.Uri): Promise { + sourcePath: vscode.Uri): Promise { await vscode.window.showTextDocument( - await vscode.workspace.openTextDocument(headerPath)); + await vscode.workspace.openTextDocument(headerPath)); await vscode.window.showInformationMessage( - `Successfully created ${path.basename(headerPath.fsPath)} and ${path.basename(sourcePath.fsPath)}.`); + `Successfully created ${path.basename(headerPath.fsPath)} and ${ + path.basename(sourcePath.fsPath)}.`); } } // Main Coordinator Class -// PairCoordinator coordinates the UI and Service layers to handle the complete file -// pair creation workflow. It serves as the main entry point and orchestrates -// the entire process. +// PairCoordinator coordinates the UI and Service layers to handle the complete +// file pair creation workflow. It serves as the main entry point and +// orchestrates the entire process. class PairCoordinator implements vscode.Disposable { private newPairCommand: vscode.Disposable; private configureRulesCommand: vscode.Disposable; private service: PairCreatorService; private ui: PairCreatorUI; - // Constructor registers the VS Code commands for creating source/header pairs and configuring rules + // Constructor registers the VS Code commands for creating source/header pairs + // and configuring rules constructor() { this.service = new PairCreatorService(); this.ui = new PairCreatorUI(this.service); // Register the main command for creating new source/header pairs this.newPairCommand = vscode.commands.registerCommand( - 'clangd.newSourcePair', this.create, this); + 'clangd.newSourcePair', this.create, this); // Register the command for configuring pairing rules this.configureRulesCommand = vscode.commands.registerCommand( - 'clangd.newSourcePair.configureRules', this.configureRules, this); + 'clangd.newSourcePair.configureRules', this.configureRules, this); } // Dispose method for cleanup when extension is deactivated @@ -1000,11 +1023,11 @@ class PairCoordinator implements vscode.Disposable { const targetDirectory = await this.ui.getTargetDirectory(); if (!targetDirectory) { vscode.window.showErrorMessage( - 'Cannot determine target directory. Please open a folder or a file first.'); + 'Cannot determine target directory. Please open a folder or a file first.'); return; } - const { language, uncertain } = await this.ui.detectLanguage(); + const {language, uncertain} = await this.ui.detectLanguage(); const rule = await this.ui.promptForPairingRule(language, uncertain); if (!rule) return; @@ -1014,29 +1037,29 @@ class PairCoordinator implements vscode.Disposable { return; const headerPath = vscode.Uri.file( - path.join(targetDirectory.fsPath, `${fileName}${rule.headerExt}`)); + path.join(targetDirectory.fsPath, `${fileName}${rule.headerExt}`)); const sourcePath = vscode.Uri.file( - path.join(targetDirectory.fsPath, `${fileName}${rule.sourceExt}`)); + path.join(targetDirectory.fsPath, `${fileName}${rule.sourceExt}`)); const existingFilePath = - await this.service.checkFileExistence(headerPath, sourcePath); + await this.service.checkFileExistence(headerPath, sourcePath); if (existingFilePath) { vscode.window.showErrorMessage( - `File already exists: ${existingFilePath}`); + `File already exists: ${existingFilePath}`); return; } const eol = this.service.getLineEnding(); - const { headerContent, sourceContent } = - this.service.generateFileContent(fileName, eol, rule); + const {headerContent, sourceContent} = + this.service.generateFileContent(fileName, eol, rule); await this.service.writeFiles(headerPath, sourcePath, headerContent, - sourceContent); + sourceContent); await this.ui.showSuccessAndOpenFile(headerPath, sourcePath); } catch (error: any) { vscode.window.showErrorMessage(error.message || - 'An unexpected error occurred.'); + 'An unexpected error occurred.'); } } @@ -1047,8 +1070,9 @@ class PairCoordinator implements vscode.Disposable { } } -// Registers the create source/header pair command with the VS Code extension context -// This function should be called during extension activation to make the command available +// Registers the create source/header pair command with the VS Code extension +// context This function should be called during extension activation to make +// the command available export function registerCreateSourceHeaderPairCommand(context: ClangdContext) { context.subscriptions.push(new PairCoordinator()); } diff --git a/src/pairing-rule-manager.ts b/src/pairing-rule-manager.ts index 0893a1b2..2d0eb4d0 100644 --- a/src/pairing-rule-manager.ts +++ b/src/pairing-rule-manager.ts @@ -1,30 +1,31 @@ // // PAIRING RULE MANAGER // ==================== -// +// // PURPOSE: -// This module manages custom file extension pairing rules for C/C++ header/source files. -// It allows users to configure custom extensions (e.g., .hh/.cc instead of .h/.cpp) -// and provides UI for managing these configurations. -// +// This module manages custom file extension pairing rules for C/C++ +// header/source files. It allows users to configure custom extensions (e.g., +// .hh/.cc instead of .h/.cpp) and provides UI for managing these +// configurations. +// // ARCHITECTURE: // 1. PairingRuleService - Core configuration management // - Reads/writes VS Code workspace/user settings // - Validates pairing rules // - Handles configuration scopes (workspace vs global) -// +// // 2. PairingRuleUI - User interface for configuration // - Quick setup wizard with predefined options // - Advanced management for editing/resetting rules // - Scope selection (workspace vs global settings) -// +// // WORKFLOW: // User calls showConfigurationWizard() → // Presents predefined extension combinations (.h/.cpp, .hh/.cc, etc.) → // User selects option or goes to advanced management → // Rule is saved to workspace or global settings → // Other parts of extension use these rules for file creation -// +// // CONFIGURATION STORAGE: // - Workspace: .vscode/settings.json (clangd.createPair.rules) // - Global: User settings.json (clangd.createPair.rules) @@ -37,7 +38,7 @@ export interface PairingRule { key: string; label: string; description: string; - language: 'c' | 'cpp'; + language: 'c'|'cpp'; headerExt: string; sourceExt: string; isClass?: boolean; @@ -45,8 +46,8 @@ export interface PairingRule { } // Type aliases for QuickPick items -type RuleQuickPickItem = vscode.QuickPickItem & { rule: PairingRule }; -type ActionQuickPickItem = vscode.QuickPickItem & { key: string }; +type RuleQuickPickItem = vscode.QuickPickItem&{rule: PairingRule}; +type ActionQuickPickItem = vscode.QuickPickItem&{key: string}; // Configuration management service class export class PairingRuleService { @@ -61,8 +62,9 @@ export class PairingRuleService { // Show error message and re-throw the error for proper error handling private static handleError(error: unknown, operation: string, - scope: string): never { - const message = `Failed to ${operation} pairing rules for ${scope}: ${error instanceof Error ? error.message : 'Unknown error'}`; + scope: string): never { + const message = `Failed to ${operation} pairing rules for ${scope}: ${ + error instanceof Error ? error.message : 'Unknown error'}`; vscode.window.showErrorMessage(message); throw error; } @@ -70,54 +72,54 @@ export class PairingRuleService { // Get the currently active pairing rules from configuration public static getActiveRules(): ReadonlyArray { return vscode.workspace.getConfiguration('clangd').get( - PairingRuleService.CONFIG_KEY, []); + PairingRuleService.CONFIG_KEY, []); } // Check if custom rules exist for the specified scope (workspace or user) - public static hasCustomRules(scope: 'workspace' | 'user'): boolean { + public static hasCustomRules(scope: 'workspace'|'user'): boolean { const inspection = - vscode.workspace.getConfiguration('clangd').inspect( - PairingRuleService.CONFIG_KEY); + vscode.workspace.getConfiguration('clangd').inspect( + PairingRuleService.CONFIG_KEY); const value = scope === 'workspace' ? inspection?.workspaceValue - : inspection?.globalValue; + : inspection?.globalValue; return Array.isArray(value); } // Get pairing rules for a specific scope (workspace or user) - public static getRules(scope: 'workspace' | 'user'): PairingRule[] | undefined { + public static getRules(scope: 'workspace'|'user'): PairingRule[]|undefined { const inspection = - vscode.workspace.getConfiguration('clangd').inspect( - PairingRuleService.CONFIG_KEY); + vscode.workspace.getConfiguration('clangd').inspect( + PairingRuleService.CONFIG_KEY); return scope === 'workspace' ? inspection?.workspaceValue - : inspection?.globalValue; + : inspection?.globalValue; } // Write pairing rules to the specified scope (workspace or user) public static async writeRules(rules: PairingRule[], - scope: 'workspace' | 'user'): Promise { + scope: 'workspace'|'user'): Promise { try { if (!Array.isArray(rules)) throw new Error('Rules must be an array'); rules.forEach(PairingRuleService.validateRule); const target = scope === 'workspace' - ? vscode.ConfigurationTarget.Workspace - : vscode.ConfigurationTarget.Global; + ? vscode.ConfigurationTarget.Workspace + : vscode.ConfigurationTarget.Global; await vscode.workspace.getConfiguration('clangd').update( - PairingRuleService.CONFIG_KEY, rules, target); + PairingRuleService.CONFIG_KEY, rules, target); } catch (error) { PairingRuleService.handleError(error, 'save', scope); } } // Reset pairing rules for the specified scope (remove custom rules) - public static async resetRules(scope: 'workspace' | 'user'): Promise { + public static async resetRules(scope: 'workspace'|'user'): Promise { try { const target = scope === 'workspace' - ? vscode.ConfigurationTarget.Workspace - : vscode.ConfigurationTarget.Global; + ? vscode.ConfigurationTarget.Workspace + : vscode.ConfigurationTarget.Global; await vscode.workspace.getConfiguration('clangd').update( - PairingRuleService.CONFIG_KEY, undefined, target); + PairingRuleService.CONFIG_KEY, undefined, target); } catch (error) { PairingRuleService.handleError(error, 'reset', scope); } @@ -161,18 +163,20 @@ export class PairingRuleUI { // Create rule choices from extension options for QuickPick display private static createRuleChoices(): RuleQuickPickItem[] { return PairingRuleUI.EXTENSION_OPTIONS.map( - (option, index) => ({ - label: `$(file-code) ${option.label}`, - description: option.description, - rule: { - key: `custom_${option.language}_${index}`, - label: `${option.language.toUpperCase()} Pair (${option.headerExt}/${option.sourceExt})`, - description: `Creates a ${option.headerExt}/${option.sourceExt} file pair with header guards.`, - language: option.language, - headerExt: option.headerExt, - sourceExt: option.sourceExt, - }, - })); + (option, index) => ({ + label: `$(file-code) ${option.label}`, + description: option.description, + rule: { + key: `custom_${option.language}_${index}`, + label: `${option.language.toUpperCase()} Pair (${ + option.headerExt}/${option.sourceExt})`, + description: `Creates a ${option.headerExt}/${ + option.sourceExt} file pair with header guards.`, + language: option.language, + headerExt: option.headerExt, + sourceExt: option.sourceExt, + }, + })); } // Create advanced options separator and menu item for advanced management @@ -214,11 +218,11 @@ export class PairingRuleUI { kind: vscode.QuickPickItemKind.Separator, key: 'separator_global', }, - { - label: '$(edit) Edit Global Rules...', - description: 'Opens your global settings.json', - key: 'edit_global', - }); + { + label: '$(edit) Edit Global Rules...', + description: 'Opens your global settings.json', + key: 'edit_global', + }); if (PairingRuleService.hasCustomRules('user')) { items.push({ @@ -234,47 +238,48 @@ export class PairingRuleUI { // Handle rule selection and ask for save scope (workspace or global) private static async handleRuleSelection(rule: PairingRule): Promise { const selection = await vscode.window.showQuickPick( - [ - { - label: 'Save for this Workspace', - description: 'Recommended. Creates a .vscode/settings.json file.', - scope: 'workspace', - }, + [ + { + label: 'Save for this Workspace', + description: 'Recommended. Creates a .vscode/settings.json file.', + scope: 'workspace', + }, + { + label: 'Save for all my Projects (Global)', + description: 'Modifies your global user settings.', + scope: 'user', + }, + ], { - label: 'Save for all my Projects (Global)', - description: 'Modifies your global user settings.', - scope: 'user', - }, - ], - { - placeHolder: 'Where would you like to save this rule?', - title: 'Save Configuration Scope', - }); + placeHolder: 'Where would you like to save this rule?', + title: 'Save Configuration Scope', + }); if (!selection) return; await PairingRuleService.writeRules([rule], selection.scope as 'workspace' | - 'user'); - vscode.window.showInformationMessage(`Successfully set '${rule.label}' as the default extension for the ${selection.scope}.`); + 'user'); + vscode.window.showInformationMessage(`Successfully set '${ + rule.label}' as the default extension for the ${selection.scope}.`); } // Handle advanced menu selection and execute the corresponding action private static async handleAdvancedMenuSelection(key: string): Promise { const actions = { edit_workspace: () => vscode.commands.executeCommand( - 'workbench.action.openWorkspaceSettingsFile'), + 'workbench.action.openWorkspaceSettingsFile'), edit_global: () => - vscode.commands.executeCommand('workbench.action.openSettingsJson'), + vscode.commands.executeCommand('workbench.action.openSettingsJson'), reset_workspace: async () => { await PairingRuleService.resetRules('workspace'); vscode.window.showInformationMessage( - 'Workspace pairing rules have been reset.'); + 'Workspace pairing rules have been reset.'); }, reset_global: async () => { await PairingRuleService.resetRules('user'); vscode.window.showInformationMessage( - 'Global pairing rules have been reset.'); + 'Global pairing rules have been reset.'); }, }; @@ -286,10 +291,10 @@ export class PairingRuleUI { // Main configuration wizard - entry point for setting up pairing rules public static async showConfigurationWizard(): Promise { const quickPick = - vscode.window.createQuickPick(); + vscode.window.createQuickPick(); quickPick.title = 'Quick Setup: Choose File Extensions'; quickPick.placeholder = - 'Choose file extension combination for this workspace, or go to advanced options.'; + 'Choose file extension combination for this workspace, or go to advanced options.'; quickPick.items = [ ...PairingRuleUI.createRuleChoices(), ...PairingRuleUI.createAdvancedOptions() @@ -315,9 +320,9 @@ export class PairingRuleUI { // Advanced management menu for editing and resetting pairing rules public static async showAdvancedManagementMenu(): Promise { const selection = await vscode.window.showQuickPick( - PairingRuleUI.createAdvancedMenuItems(), { - title: 'Advanced Rule Management', - }); + PairingRuleUI.createAdvancedMenuItems(), { + title: 'Advanced Rule Management', + }); if (selection?.key) { await PairingRuleUI.handleAdvancedMenuSelection(selection.key); From c463be2a637a263a0fcdd93479190972d25da333 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?=E9=95=BF=E6=9C=9F=E7=B4=A0=E9=A3=9F?= Date: Fri, 18 Jul 2025 21:13:42 +0800 Subject: [PATCH 15/34] Refactor create-source-header-pair module structure Split the monolithic create-source-header-pair.ts into separate files: coordinator.ts, service.ts, ui.ts, and templates.ts, and updated the module index and registration logic. Updated package.json command and configuration descriptions for clarity. Adjusted extension and command registration in extension.ts and updated pairing-rule-manager.ts as needed. --- package.json | 21 +- src/create-source-header-pair.ts | 1078 ------------------ src/create-source-header-pair/coordinator.ts | 99 ++ src/create-source-header-pair/index.ts | 47 + src/create-source-header-pair/service.ts | 332 ++++++ src/create-source-header-pair/templates.ts | 156 +++ src/create-source-header-pair/ui.ts | 476 ++++++++ src/extension.ts | 138 +-- src/pairing-rule-manager.ts | 167 ++- 9 files changed, 1256 insertions(+), 1258 deletions(-) delete mode 100644 src/create-source-header-pair.ts create mode 100644 src/create-source-header-pair/coordinator.ts create mode 100644 src/create-source-header-pair/index.ts create mode 100644 src/create-source-header-pair/service.ts create mode 100644 src/create-source-header-pair/templates.ts create mode 100644 src/create-source-header-pair/ui.ts diff --git a/package.json b/package.json index 31cca103..c1da0989 100644 --- a/package.json +++ b/package.json @@ -203,15 +203,15 @@ "properties": { "key": { "type": "string", - "description": "Unique identifier for the pairing rule" + "description": "Unique identifier for this rule" }, "label": { "type": "string", - "description": "Display label for the pairing rule" + "description": "Human-readable name shown in UI" }, "description": { "type": "string", - "description": "Description of what the pairing rule does" + "description": "Detailed description of what this rule creates" }, "language": { "type": "string", @@ -219,34 +219,35 @@ "c", "cpp" ], - "description": "Programming language for this rule" + "description": "Target programming language" }, "headerExt": { "type": "string", - "description": "File extension for header files (e.g., .h, .hpp)" + "description": "File extension for header file (e.g., '.h', '.hh')" }, "sourceExt": { "type": "string", - "description": "File extension for source files (e.g., .c, .cpp)" + "description": "File extension for source file (e.g., '.cpp', '.cc', '.c')" }, "isClass": { "type": "boolean", - "description": "Whether this rule is for creating classes" + "description": "Whether this rule creates a class template" }, "isStruct": { "type": "boolean", - "description": "Whether this rule is for creating structs" + "description": "Whether this rule creates a struct template" } }, "required": [ "key", "label", + "description", "language", "headerExt", "sourceExt" ] }, - "description": "Custom pairing rules for source/header file creation" + "description": "Custom pairing rules for creating source/header file pairs with different extensions" } } }, @@ -273,7 +274,7 @@ "title": "New Source/Header Pair" }, { - "command": "clangd.newSourcePair.configureRules", + "command": "clangd.createPair.configureRules", "category": "clangd", "title": "Configure Source/Header Pairing Rules" }, diff --git a/src/create-source-header-pair.ts b/src/create-source-header-pair.ts deleted file mode 100644 index 34ac1ec0..00000000 --- a/src/create-source-header-pair.ts +++ /dev/null @@ -1,1078 +0,0 @@ -// -// CREATE SOURCE HEADER PAIR -// ========================= -// -// PURPOSE: -// This module provides functionality to create matching header/source file -// pairs for C/C++ development. It intelligently detects language context, -// offers appropriate templates, and handles custom file extensions. -// -// ARCHITECTURE: -// 1. PairCreatorService (Business Logic Layer) -// - Language detection from file context -// - File existence checking with caching -// - Template content generation -// - File system operations (read/write) -// - Custom extension handling -// -// 2. PairCreatorUI (User Interface Layer) -// - User prompts and input validation -// - Template selection dialogs -// - Custom rule management integration -// - Error handling and user feedback -// -// 3. PairCoordinator (Main Coordinator) -// - Orchestrates the entire workflow -// - Registers VS Code command -// - Manages component lifecycle -// -// WORKFLOW: -// Command triggered → Detect target directory → Analyze language context → -// Check for custom rules → Present template choices → Get file name → -// Validate uniqueness → Generate content → Write files → Open in editor -// -// FEATURES: -// - Smart language detection (C vs C++) -// - Multiple template types (class, struct, empty) -// - Custom file extension support -// - Header guard generation -// - Cross-language template options -// - Workspace-aware directory selection -// - Input validation for C/C++ identifiers -// -// INTEGRATION: -// Uses PairingRuleManager for custom extension configurations -// Integrates with VS Code file system and editor APIs -// - -import * as path from 'path'; -import * as vscode from 'vscode'; - -import {ClangdContext} from './clangd-context'; -import { - PairingRule, - PairingRuleService, - showConfigurationWizard -} from './pairing-rule-manager'; - -// Types for better type safety -type Language = 'c'|'cpp'; -type TemplateKey = 'CPP_CLASS'|'CPP_STRUCT'|'C_STRUCT'|'C_EMPTY'|'CPP_EMPTY'; - -// Regular expression patterns to validate C/C++ identifiers -const VALIDATION_PATTERNS = { - IDENTIFIER: /^[a-zA-Z_][a-zA-Z0-9_]*$/ -}; - -// Default placeholder names for different file types -const DEFAULT_PLACEHOLDERS = { - C_EMPTY: 'my_c_functions', - C_STRUCT: 'MyStruct', - CPP_EMPTY: 'utils', - CPP_CLASS: 'MyClass', - CPP_STRUCT: 'MyStruct' -}; - -// Template rules for available file pair types -const TEMPLATE_RULES: PairingRule[] = [ - { - key: 'cpp_empty', - label: '$(new-file) C++ Pair', - description: 'Creates a basic Header/Source file pair with header guards.', - language: 'cpp' as const, - headerExt: '.h', - sourceExt: '.cpp' - }, - { - key: 'cpp_class', - label: '$(symbol-class) C++ Class', - description: - 'Creates a Header/Source file pair with a boilerplate class definition.', - language: 'cpp' as const, - headerExt: '.h', - sourceExt: '.cpp', - isClass: true - }, - { - key: 'cpp_struct', - label: '$(symbol-struct) C++ Struct', - description: - 'Creates a Header/Source file pair with a boilerplate struct definition.', - language: 'cpp' as const, - headerExt: '.h', - sourceExt: '.cpp', - isStruct: true - }, - { - key: 'c_empty', - label: '$(file-code) C Pair', - description: 'Creates a basic .h/.c file pair for function declarations.', - language: 'c' as const, - headerExt: '.h', - sourceExt: '.c' - }, - { - key: 'c_struct', - label: '$(symbol-struct) C Struct', - description: 'Creates a .h/.c file pair with a boilerplate typedef struct.', - language: 'c' as const, - headerExt: '.h', - sourceExt: '.c', - isStruct: true - } -]; - -// File templates with immutable structure -const FILE_TEMPLATES = { - CPP_CLASS: { - header: `#ifndef {{headerGuard}} -#define {{headerGuard}} - -class {{fileName}} { -public: - {{fileName}}(); - ~{{fileName}}(); - -private: - // Add private members here -}; - -#endif // {{headerGuard}} -`, - source: `{{includeLine}} - -{{fileName}}::{{fileName}}() { - // Constructor implementation -} - -{{fileName}}::~{{fileName}}() { - // Destructor implementation -} -` - }, - CPP_STRUCT: { - header: `#ifndef {{headerGuard}} -#define {{headerGuard}} - -struct {{fileName}} { - // Struct members -}; - -#endif // {{headerGuard}} -`, - source: '{{includeLine}}' - }, - C_STRUCT: { - header: `#ifndef {{headerGuard}} -#define {{headerGuard}} - -typedef struct { - // Struct members -} {{fileName}}; - -#endif // {{headerGuard}} -`, - source: '{{includeLine}}' - }, - C_EMPTY: { - header: `#ifndef {{headerGuard}} -#define {{headerGuard}} - -// Declarations for {{fileName}}.c - -#endif // {{headerGuard}} -`, - source: `{{includeLine}} - -// Implementations for {{fileName}}.c -` - }, - CPP_EMPTY: { - header: `#ifndef {{headerGuard}} -#define {{headerGuard}} - -// Declarations for {{fileName}}.cpp - -#endif // {{headerGuard}} -`, - source: '{{includeLine}}' - } -}; - -// Service Layer - Core business logic -class PairCreatorService { - // Cache for expensive file system operations - private static readonly fileStatCache = new Map>(); - - // Definitive file extensions for fast lookup - private static readonly DEFINITIVE_EXTENSIONS = { - c: new Set(['.c']), - cpp: new Set(['.cpp', '.cc', '.cxx', '.hh', '.hpp', '.hxx']) - }; - - // Optimized file existence check with caching to improve performance - private static async fileExists(filePath: string): Promise { - if (this.fileStatCache.has(filePath)) { - return this.fileStatCache.get(filePath)!; - } - - const promise = - Promise.resolve(vscode.workspace.fs.stat(vscode.Uri.file(filePath)) - .then(() => true, () => false)); - - this.fileStatCache.set(filePath, promise); - - // Auto-clear cache entry after 5 seconds to prevent memory leaks - setTimeout(() => this.fileStatCache.delete(filePath), 5000); - - return promise; - } - - // Detects programming language from file info (pure business logic) - // DETECTION STRATEGY: - // 1. Fast path: Check file extension against definitive extension sets - // - .c files are definitely C - // - .cpp/.cc/.cxx/.hh/.hpp/.hxx are definitely C++ - // 2. Special case: .h files are ambiguous (could be C or C++) - // - Look for companion files in same directory to determine context - // - If MyClass.h exists with MyClass.cpp -> C++ - // - If utils.h exists with utils.c -> C - // 3. Fallback: Use VS Code's language ID detection - // - Based on file content analysis or user settings - // 4. Default: When all else fails, assume C++ (more common in modern - // development) - public async detectLanguage(languageId?: string, filePath?: string): - Promise<{language: Language, uncertain: boolean}> { - if (!languageId || !filePath) { - return {language: 'cpp', uncertain: true}; - } - - const ext = path.extname(filePath); - - // Fast path for definitive extensions - if (PairCreatorService.DEFINITIVE_EXTENSIONS.c.has(ext)) { - return {language: 'c', uncertain: false}; - } - if (PairCreatorService.DEFINITIVE_EXTENSIONS.cpp.has(ext)) { - return {language: 'cpp', uncertain: false}; - } - - // Special handling for .h files with companion file detection - if (ext === '.h') { - const result = await this.detectLanguageForHeaderFile(filePath); - if (result) - return result; - } - - // Fallback to language ID - return {language: languageId === 'c' ? 'c' : 'cpp', uncertain: true}; - } - - // Optimized header file language detection by checking companion files - // HEADER FILE DETECTION STRATEGY: - // Problem: .h files are used by both C and C++, making language detection - // ambiguous Solution: Look for companion source files in the same directory - // - // Algorithm: - // 1. Extract base name from header file (e.g., "utils" from "utils.h") - // 2. Check for C companion first (utils.c) - early exit optimization - // - C projects are less common, so checking first allows quick - // determination - // 3. Check for C++ companions in parallel (utils.cpp, utils.cc, utils.cxx) - // - Use Promise.all for concurrent file existence checks - // 4. Return definitive result if companion found, otherwise default to C++ - // - // Examples: - // - math.h + math.c exists → Detected as C language - // - Vector.h + Vector.cpp exists → Detected as C++ language - // - standalone.h (no companions) → Default to C++ (uncertain=true) - private async detectLanguageForHeaderFile(filePath: string): - Promise<{language: Language, uncertain: boolean}|null> { - const baseName = path.basename(filePath, '.h'); - const dirPath = path.dirname(filePath); - - // Check for C companion file first (less common, check first for early - // exit) - const cFile = path.join(dirPath, `${baseName}.c`); - if (await PairCreatorService.fileExists(cFile)) { - return {language: 'c', uncertain: false}; - } - - // Check for C++ companion files in parallel - const cppExtensions = ['.cpp', '.cc', '.cxx']; - const cppChecks = - cppExtensions.map(ext => PairCreatorService.fileExists( - path.join(dirPath, `${baseName}${ext}`))); - - const results = await Promise.all(cppChecks); - if (results.some((exists: boolean) => exists)) { - return {language: 'cpp', uncertain: false}; - } - - return {language: 'cpp', uncertain: true}; - } - - // Gets all available pairing rules (custom + workspace + user) - public getAllPairingRules(): PairingRule[] { - return [ - ...(PairingRuleService.getRules('workspace') ?? []), - ...(PairingRuleService.getRules('user') ?? []) - ]; - } - - // Gets custom C++ extensions if available from configuration - public getCustomCppExtensions(): {headerExt: string, sourceExt: string}|null { - const allRules = this.getAllPairingRules(); - const cppCustomRule = - allRules.find((rule: PairingRule) => rule.language === 'cpp'); - return cppCustomRule ? { - headerExt: cppCustomRule.headerExt, - sourceExt: cppCustomRule.sourceExt - } - : null; - } - - // Converts string to PascalCase efficiently (pure function) - public toPascalCase(input: string): string { - return input.split(/[-_]+/) - .map(word => word.charAt(0).toUpperCase() + word.slice(1)) - .join(''); - } - - // Gets default placeholder based on rule type (pure function) - public getDefaultPlaceholder(rule: PairingRule): string { - if (rule.isClass) { - return DEFAULT_PLACEHOLDERS.CPP_CLASS; - } - - if (rule.isStruct) { - return rule.language === 'cpp' ? DEFAULT_PLACEHOLDERS.CPP_STRUCT - : DEFAULT_PLACEHOLDERS.C_STRUCT; - } - - return rule.language === 'c' ? DEFAULT_PLACEHOLDERS.C_EMPTY - : DEFAULT_PLACEHOLDERS.CPP_EMPTY; - } - - // Optimized line ending detection based on VS Code settings and platform - public getLineEnding(): string { - const eolSetting = - vscode.workspace.getConfiguration('files').get('eol'); - - return eolSetting === '\n' || eolSetting === '\r\n' ? eolSetting - : process.platform === 'win32' ? '\r\n' - : '\n'; - } - - // Generates file content with improved template selection - public generateFileContent(fileName: string, eol: string, rule: PairingRule): - {headerContent: string; sourceContent: string;} { - const templateKey: TemplateKey = - rule.isClass ? 'CPP_CLASS' - : rule.isStruct ? (rule.language === 'cpp' ? 'CPP_STRUCT' : 'C_STRUCT') - : rule.language === 'c' ? 'C_EMPTY' - : 'CPP_EMPTY'; - - const templates = FILE_TEMPLATES[templateKey]; - const context = { - fileName, - headerGuard: `${fileName.toUpperCase()}_H_`, - includeLine: `#include "${fileName}${rule.headerExt}"` - }; - - const headerContent = this.applyTemplate(templates.header, context); - const sourceContent = this.applyTemplate(templates.source, context); - - return { - headerContent: headerContent.replace(/\n/g, eol), - sourceContent: sourceContent.replace(/\n/g, eol) - }; - } - - // Optimized template variable substitution using regex replacement - private applyTemplate(template: string, - context: Record): string { - // Pre-compile regex for better performance if used frequently - return template.replace(/\{\{(\w+)\}\}/g, (_, key) => context[key] ?? ''); - } - - // File existence check with parallel processing for multiple files - public async checkFileExistence(headerPath: vscode.Uri, - sourcePath: vscode.Uri): - Promise { - const checks = [headerPath, sourcePath].map(async (uri) => { - try { - await vscode.workspace.fs.stat(uri); - return uri.fsPath; - } catch { - return null; - } - }); - - const results = await Promise.all(checks); - return results.find(path => path !== null) ?? null; - } - - // Optimized file writing with error handling and parallel writes - public async writeFiles(headerPath: vscode.Uri, sourcePath: vscode.Uri, - headerContent: string, - sourceContent: string): Promise { - try { - await Promise.all([ - vscode.workspace.fs.writeFile(headerPath, - Buffer.from(headerContent, 'utf8')), - vscode.workspace.fs.writeFile(sourcePath, - Buffer.from(sourceContent, 'utf8')) - ]); - } catch (error) { - throw new Error(`Failed to create files: ${ - error instanceof Error ? error.message : 'Unknown error'}`); - } - } - - // Smart target directory detection (pure business logic) - public async getTargetDirectory(activeDocumentPath?: string, - workspaceFolders - ?: readonly vscode.WorkspaceFolder[]): - Promise { - // Prefer current file's directory - if (activeDocumentPath) { - return vscode.Uri.file(path.dirname(activeDocumentPath)); - } - - // Return single workspace folder directly - if (workspaceFolders?.length === 1) { - return workspaceFolders[0].uri; - } - - // Multiple workspace folders require UI selection - return undefined; - } - - // Language mismatch warning logic (pure business logic) - public async shouldShowLanguageMismatchWarning(language: Language, - result: PairingRule, - currentDir?: string, - activeFilePath - ?: string): Promise { - if (!currentDir || !activeFilePath) { - return true; - } - - if (language === 'c' && result.language === 'cpp') { - return this.checkForCppFilesInDirectory(currentDir); - } - - return this.checkForCorrespondingSourceFiles(currentDir, activeFilePath, - language); - } - - // Check for C++ files in directory to inform language mismatch warnings - private async checkForCppFilesInDirectory(dirPath: string): Promise { - try { - const entries = - await vscode.workspace.fs.readDirectory(vscode.Uri.file(dirPath)); - const hasCppFiles = - entries.some(([fileName, fileType]) => - fileType === vscode.FileType.File && - PairCreatorService.DEFINITIVE_EXTENSIONS.cpp.has( - path.extname(fileName))); - return !hasCppFiles; // Show warning if NO C++ files found - } catch { - return true; // Show warning if can't check - } - } - - // Check for corresponding source files to inform language mismatch warnings - private async checkForCorrespondingSourceFiles( - dirPath: string, filePath: string, language: Language): Promise { - const baseName = path.basename(filePath, path.extname(filePath)); - const extensions = language === 'c' ? ['.c'] : ['.cpp', '.cc', '.cxx']; - - const checks = extensions.map(ext => PairCreatorService.fileExists( - path.join(dirPath, `${baseName}${ext}`))); - - try { - const results = await Promise.all(checks); - return !results.some( - (exists: boolean) => - exists); // Show warning if NO corresponding files found - } catch { - return true; // Show warning if can't check - } - } -} - -// UI Layer - -// PairCreatorUI handles all user interface interactions for file pair creation. -// It manages dialogs, input validation, and user choices. -class PairCreatorUI { - private service: PairCreatorService; - - constructor(service: PairCreatorService) { this.service = service; } - - // Gets placeholder name for input dialog, considering active file context - private getPlaceholder(rule: PairingRule): string { - const activeEditor = vscode.window.activeTextEditor; - - if (activeEditor?.document && !activeEditor.document.isUntitled) { - const fileName = - path.basename(activeEditor.document.fileName, - path.extname(activeEditor.document.fileName)); - return rule.language === 'c' ? fileName - : this.service.toPascalCase(fileName); - } - - return this.service.getDefaultPlaceholder(rule); - } - - // Gets target directory with UI fallback for multiple workspace folders - public async getTargetDirectory(): Promise { - const activeEditor = vscode.window.activeTextEditor; - const activeDocumentPath = - activeEditor?.document && !activeEditor.document.isUntitled - ? activeEditor.document.uri.fsPath - : undefined; - const workspaceFolders = vscode.workspace.workspaceFolders; - - // Try service layer first - const result = await this.service.getTargetDirectory(activeDocumentPath, - workspaceFolders); - if (result) { - return result; - } - - // Handle multiple workspace folders with UI - if (workspaceFolders && workspaceFolders.length > 1) { - const selected = await vscode.window.showWorkspaceFolderPick( - {placeHolder: 'Select workspace folder for new files'}); - return selected?.uri; - } - - return undefined; - } - - // Checks if language mismatch warning should be shown with UI context - private async shouldShowLanguageMismatchWarning(language: Language, - result: PairingRule): - Promise { - const activeEditor = vscode.window.activeTextEditor; - const currentDir = - activeEditor?.document && !activeEditor.document.isUntitled - ? path.dirname(activeEditor.document.uri.fsPath) - : undefined; - const activeFilePath = - activeEditor?.document && !activeEditor.document.isUntitled - ? activeEditor.document.uri.fsPath - : undefined; - - return this.service.shouldShowLanguageMismatchWarning( - language, result, currentDir, activeFilePath); - } - - // Detects programming language from active editor context - public async detectLanguage(): - Promise<{language: Language, uncertain: boolean}> { - const activeEditor = vscode.window.activeTextEditor; - const languageId = activeEditor?.document?.languageId; - const filePath = activeEditor?.document && !activeEditor.document.isUntitled - ? activeEditor.document.uri.fsPath - : undefined; - - return this.service.detectLanguage(languageId, filePath); - } - - // Adapts template rules for display in UI based on custom extensions - private adaptRuleForDisplay(rule: PairingRule): PairingRule { - if (rule.language !== 'cpp') { - return rule; - } - - const customExtensions = this.service.getCustomCppExtensions(); - if (!customExtensions) { - return rule; - } - - const {headerExt, sourceExt} = customExtensions; - - // Adapt description for display - const replacementPattern = - /Header\/Source|\.h(?:h|pp|xx)?\/\.c(?:pp|c|xx)?/g; - const newDescription = rule.description.replace( - replacementPattern, `${headerExt}/${sourceExt}`); - - return {...rule, description: newDescription, headerExt, sourceExt}; - } - - // Prepares template choices for UI display with proper ordering and - // adaptation - private prepareTemplateChoices(language: 'c'|'cpp', - uncertain: boolean): PairingRule[] { - const desiredOrder = - uncertain - ? ['cpp_empty', 'c_empty', 'cpp_class', 'cpp_struct', 'c_struct'] - : language === 'c' - ? ['c_empty', 'c_struct', 'cpp_empty', 'cpp_class', 'cpp_struct'] - : ['cpp_empty', 'cpp_class', 'cpp_struct', 'c_empty', 'c_struct']; - - return [...TEMPLATE_RULES] - .sort((a, b) => - desiredOrder.indexOf(a.key) - desiredOrder.indexOf(b.key)) - .map(rule => this.adaptRuleForDisplay(rule)); - } - - // Filters custom rules by language and prepares them for display - private prepareCustomRulesChoices(allRules: PairingRule[], - language: 'c'|'cpp'): { - languageRules: PairingRule[], - adaptedDefaultTemplates: PairingRule[], - otherLanguageTemplates: PairingRule[], - cleanedCustomRules: PairingRule[] - } { - const languageRules = - allRules.filter((rule: PairingRule) => rule.language === language); - const customExt = languageRules.length > 0 ? languageRules[0] : null; - - let adaptedDefaultTemplates: PairingRule[] = []; - - if (customExt && language === 'cpp') { - // For C++, adapt default templates with custom extensions - adaptedDefaultTemplates = - TEMPLATE_RULES - .filter( - template => - template.language === 'cpp' && - !languageRules.some( - customRule => - customRule.isClass === template.isClass && - customRule.isStruct === template.isStruct && - (customRule.isClass || customRule.isStruct || - (!customRule.isClass && !customRule.isStruct && - !template.isClass && !template.isStruct)))) - .map( - template => ({ - ...template, - key: `${template.key}_adapted`, - headerExt: customExt.headerExt, - sourceExt: customExt.sourceExt, - description: - template.description - .replace( - /Header\/Source/g, - `${customExt.headerExt}/${customExt.sourceExt}`) - .replace(/\.h\/\.cpp/g, `${customExt.headerExt}/${ - customExt.sourceExt}`) - .replace(/basic \.h\/\.cpp/g, - `basic ${customExt.headerExt}/${ - customExt.sourceExt}`) - .replace(/Creates a \.h\/\.cpp/g, - `Creates a ${customExt.headerExt}/${ - customExt.sourceExt}`) - })); - } else { - // Standard adaptation for non-custom or C language - adaptedDefaultTemplates = - TEMPLATE_RULES - .filter(template => - template.language === language && - !languageRules.some( - customRule => - customRule.headerExt === template.headerExt && - customRule.sourceExt === template.sourceExt && - customRule.isClass === template.isClass && - customRule.isStruct === template.isStruct)) - .map(template => this.adaptRuleForDisplay(template)); - } - - const otherLanguageTemplates = - TEMPLATE_RULES.filter(template => template.language !== language) - .map(template => this.adaptRuleForDisplay(template)); - - const cleanedCustomRules = allRules.map( - (rule: PairingRule) => ({ - ...rule, - label: rule.label.includes('$(') - ? rule.label - : `$(new-file) ${ - rule.language === 'cpp' ? 'C++' : 'C'} Pair (${ - rule.headerExt}/${rule.sourceExt})`, - description: rule.description.startsWith('Creates a') - ? rule.description - : `Creates a ${rule.headerExt}/${ - rule.sourceExt} file pair with header guards.` - })); - - return { - languageRules, - adaptedDefaultTemplates, - otherLanguageTemplates, - cleanedCustomRules - }; - } - - // Checks for existing custom pairing rules and offers to create them if not - // found For C++, presents options to use custom rules or create new ones. For - // C, always uses default templates. Returns selected rule, null if cancelled, - // undefined for defaults, or 'use_default' flag - public async checkAndOfferCustomRules(language: 'c'|'cpp', - uncertain: boolean): - Promise { - if (language === 'c') - return undefined; // Always use default C templates - - const allRules = this.service.getAllPairingRules(); - const languageRules = - allRules.filter((rule: PairingRule) => rule.language === language); - - if (languageRules.length > 0) { - const result = await this.selectFromCustomRules(allRules, language); - return result === undefined ? null : result; - } - - if (!uncertain) { - const shouldCreateRules = await this.offerToCreateCustomRules(language); - if (shouldCreateRules === null) - return null; - if (shouldCreateRules) { - const result = await this.createCustomRules(language); - return result === undefined ? null : result; - } - } - - return undefined; - } - - // Presents a selection dialog for custom pairing rules - // Combines custom rules with adapted default templates and cross-language - // options Returns selected rule, undefined if cancelled, or 'use_default' - // flag - public async selectFromCustomRules(allRules: PairingRule[], - language: 'c'|'cpp'): - Promise { - - const { - cleanedCustomRules, - adaptedDefaultTemplates, - otherLanguageTemplates - } = this.prepareCustomRulesChoices(allRules, language); - - const choices = [ - ...cleanedCustomRules, ...adaptedDefaultTemplates, - ...otherLanguageTemplates, { - key: 'use_default', - label: '$(list-unordered) Use Default Templates', - description: - 'Use the built-in default pairing rules instead of custom rules', - isSpecial: true - } - ]; - - const result = await vscode.window.showQuickPick(choices, { - placeHolder: `Select a ${language.toUpperCase()} pairing rule`, - title: 'Custom Pairing Rules Available', - }); - - if (!result) - return undefined; - if ('isSpecial' in result && result.isSpecial && - result.key === 'use_default') - return 'use_default'; - return result as PairingRule; - } - - // Shows a dialog offering to create custom pairing rules for C++ - // Only applicable for C++ since C uses standard .c/.h extensions - // Returns true to create rules, false to dismiss, null if cancelled - public async offerToCreateCustomRules(language: 'c'| - 'cpp'): Promise { - if (language === 'c') - return false; - - const result = await vscode.window.showInformationMessage( - `No custom pairing rules found for C++. Would you like to create custom rules to use different file extensions (e.g., .cc/.hh instead of .cpp/.h)?`, - {modal: false}, 'Create Custom Rules', 'Dismiss'); - - return result === 'Create Custom Rules' ? true - : result === 'Dismiss' ? false - : null; - } - - // Guides the user through creating custom pairing rules for C++ - // Offers common extension combinations or allows custom input - // Saves the rule to workspace or global settings - // Returns the created custom rule or undefined if cancelled - public async createCustomRules(language: 'c'| - 'cpp'): Promise { - if (language === 'c') - return undefined; - - const commonExtensions = [ - {label: '.h / .cpp (Default)', headerExt: '.h', sourceExt: '.cpp'}, - {label: '.hh / .cc (Alternative)', headerExt: '.hh', sourceExt: '.cc'}, { - label: '.hpp / .cpp (Header Plus Plus)', - headerExt: '.hpp', - sourceExt: '.cpp' - }, - {label: '.hxx / .cxx (Extended)', headerExt: '.hxx', sourceExt: '.cxx'}, - {label: 'Custom Extensions', headerExt: '', sourceExt: ''} - ]; - - const selectedExtension = - await vscode.window.showQuickPick(commonExtensions, { - placeHolder: `Select file extensions for C++ files`, - title: 'Choose File Extensions' - }); - - if (!selectedExtension) - return undefined; - - let {headerExt, sourceExt} = selectedExtension; - - if (!headerExt || !sourceExt) { - const validateExt = (text: string) => - (!text || !text.startsWith('.') || text.length < 2) - ? 'Please enter a valid file extension starting with a dot (e.g., .h)' - : null; - - headerExt = await vscode.window.showInputBox({ - prompt: 'Enter header file extension (e.g., .h, .hh, .hpp)', - placeHolder: '.h', - validateInput: validateExt - }) || ''; - - if (!headerExt) - return undefined; - - sourceExt = await vscode.window.showInputBox({ - prompt: `Enter source file extension for C++ (e.g., .cpp, .cc, .cxx)`, - placeHolder: '.cpp', - validateInput: validateExt - }) || ''; - - if (!sourceExt) - return undefined; - } - - const customRule: PairingRule = { - key: `custom_cpp_${Date.now()}`, - label: `$(new-file) C++ Pair (${headerExt}/${sourceExt})`, - description: - `Creates a ${headerExt}/${sourceExt} file pair with header guards.`, - language: 'cpp', - headerExt, - sourceExt - }; - - const saveLocation = await vscode.window.showQuickPick( - [ - { - label: 'Workspace Settings', - description: 'Save to current workspace only', - value: 'workspace' - }, - { - label: 'Global Settings', - description: 'Save to user settings (available in all workspaces)', - value: 'user' - } - ], - { - placeHolder: 'Where would you like to save this custom rule?', - title: 'Save Location' - }); - - if (!saveLocation) - return undefined; - - try { - const existingRules = PairingRuleService.getRules( - saveLocation.value as 'workspace' | 'user') || - []; - await PairingRuleService.writeRules([...existingRules, customRule], - saveLocation.value as 'workspace' | - 'user'); - - const locationText = - saveLocation.value === 'workspace' ? 'workspace' : 'global'; - vscode.window.showInformationMessage( - `Custom pairing rule saved to ${locationText} settings.`); - - return customRule; - } catch (error: any) { - vscode.window.showErrorMessage( - `Failed to save custom rule: ${error.message}`); - return undefined; - } - } - - // Prompts the user to select a pairing rule from available options - // First checks for custom rules, then falls back to default templates - // Returns selected pairing rule or undefined if cancelled - public async promptForPairingRule(language: 'c'|'cpp', uncertain: boolean): - Promise { - const customRulesResult = - await this.checkAndOfferCustomRules(language, uncertain); - - if (customRulesResult === null) - return undefined; - if (customRulesResult === 'use_default') { - // Continue to default template selection - } else if (customRulesResult) { - return customRulesResult; - } - - const choices = this.prepareTemplateChoices(language, uncertain); - - const result = await vscode.window.showQuickPick(choices, { - placeHolder: 'Please select the type of file pair to create.', - title: 'Create Source/Header Pair' - }); - - if (result && !uncertain && language !== result.language) { - const shouldShowWarning = - await this.shouldShowLanguageMismatchWarning(language, result); - - if (shouldShowWarning) { - const detectedLangName = language === 'c' ? 'C' : 'C++'; - const selectedLangName = result.language === 'c' ? 'C' : 'C++'; - - const shouldContinue = await vscode.window.showWarningMessage( - `You're working in a ${detectedLangName} file but selected a ${ - selectedLangName} template. This may create files with incompatible extensions or content.`, - 'Continue', 'Cancel'); - - if (shouldContinue !== 'Continue') - return undefined; - } - } - - return result; - } - - // Prompts the user to enter a name for the new file pair - // Validates input as a valid C/C++ identifier and provides - // context-appropriate prompts Returns the entered file name or undefined if - // cancelled - public async promptForFileName(rule: PairingRule): Promise { - const prompt = rule.isClass ? 'Please enter the name for the new C++ class.' - : rule.isStruct - ? `Please enter the name for the new ${ - rule.language.toUpperCase()} struct.` - : `Please enter the base name for the new ${ - rule.language.toUpperCase()} file pair.`; - - return vscode.window.showInputBox({ - prompt, - placeHolder: this.getPlaceholder(rule), - validateInput: (text) => - VALIDATION_PATTERNS.IDENTIFIER.test(text?.trim() || '') - ? null - : 'Invalid C/C++ identifier.', - title: 'Create Source/Header Pair' - }); - } - - // Shows success message and opens the newly created header file - public async showSuccessAndOpenFile(headerPath: vscode.Uri, - sourcePath: vscode.Uri): Promise { - await vscode.window.showTextDocument( - await vscode.workspace.openTextDocument(headerPath)); - await vscode.window.showInformationMessage( - `Successfully created ${path.basename(headerPath.fsPath)} and ${ - path.basename(sourcePath.fsPath)}.`); - } -} - -// Main Coordinator Class - -// PairCoordinator coordinates the UI and Service layers to handle the complete -// file pair creation workflow. It serves as the main entry point and -// orchestrates the entire process. -class PairCoordinator implements vscode.Disposable { - private newPairCommand: vscode.Disposable; - private configureRulesCommand: vscode.Disposable; - private service: PairCreatorService; - private ui: PairCreatorUI; - - // Constructor registers the VS Code commands for creating source/header pairs - // and configuring rules - constructor() { - this.service = new PairCreatorService(); - this.ui = new PairCreatorUI(this.service); - - // Register the main command for creating new source/header pairs - this.newPairCommand = vscode.commands.registerCommand( - 'clangd.newSourcePair', this.create, this); - - // Register the command for configuring pairing rules - this.configureRulesCommand = vscode.commands.registerCommand( - 'clangd.newSourcePair.configureRules', this.configureRules, this); - } - - // Dispose method for cleanup when extension is deactivated - dispose() { - this.newPairCommand.dispose(); - this.configureRulesCommand.dispose(); - } - - // Main entry point for the file pair creation process - // Orchestrates the entire workflow using the service and UI layers - public async create(): Promise { - try { - const targetDirectory = await this.ui.getTargetDirectory(); - if (!targetDirectory) { - vscode.window.showErrorMessage( - 'Cannot determine target directory. Please open a folder or a file first.'); - return; - } - - const {language, uncertain} = await this.ui.detectLanguage(); - const rule = await this.ui.promptForPairingRule(language, uncertain); - if (!rule) - return; - - const fileName = await this.ui.promptForFileName(rule); - if (!fileName) - return; - - const headerPath = vscode.Uri.file( - path.join(targetDirectory.fsPath, `${fileName}${rule.headerExt}`)); - const sourcePath = vscode.Uri.file( - path.join(targetDirectory.fsPath, `${fileName}${rule.sourceExt}`)); - - const existingFilePath = - await this.service.checkFileExistence(headerPath, sourcePath); - if (existingFilePath) { - vscode.window.showErrorMessage( - `File already exists: ${existingFilePath}`); - return; - } - - const eol = this.service.getLineEnding(); - const {headerContent, sourceContent} = - this.service.generateFileContent(fileName, eol, rule); - - await this.service.writeFiles(headerPath, sourcePath, headerContent, - sourceContent); - await this.ui.showSuccessAndOpenFile(headerPath, sourcePath); - - } catch (error: any) { - vscode.window.showErrorMessage(error.message || - 'An unexpected error occurred.'); - } - } - - // Opens the configuration wizard for source/header pairing rules - // Delegates to the PairingRuleManager's configuration interface - public async configureRules(): Promise { - await showConfigurationWizard(); - } -} - -// Registers the create source/header pair command with the VS Code extension -// context This function should be called during extension activation to make -// the command available -export function registerCreateSourceHeaderPairCommand(context: ClangdContext) { - context.subscriptions.push(new PairCoordinator()); -} diff --git a/src/create-source-header-pair/coordinator.ts b/src/create-source-header-pair/coordinator.ts new file mode 100644 index 00000000..7717d909 --- /dev/null +++ b/src/create-source-header-pair/coordinator.ts @@ -0,0 +1,99 @@ +// +// PAIR COORDINATOR +// ================ +// +// Main coordinator class that orchestrates the entire source/header pair +// creation workflow. Acts as the main entry point and manages the +// interaction between service and UI layers. +// + +import * as path from 'path'; +import * as vscode from 'vscode'; + +import { showConfigurationWizard } from '../pairing-rule-manager'; +import { PairCreatorService } from './service'; +import { PairCreatorUI } from './ui'; + +// PairCoordinator coordinates the UI and Service layers to handle the complete +// file pair creation workflow. It serves as the main entry point and +// orchestrates the entire process. +export class PairCoordinator implements vscode.Disposable { + private newPairCommand: vscode.Disposable; + private configureRulesCommand: vscode.Disposable; + private service: PairCreatorService; + private ui: PairCreatorUI; + + // Constructor registers the VS Code commands for creating source/header pairs + // and configuring rules + constructor() { + this.service = new PairCreatorService(); + this.ui = new PairCreatorUI(this.service); + + // Register the main command for creating new source/header pairs + this.newPairCommand = vscode.commands.registerCommand( + 'clangd.newSourcePair', this.create, this); + + // Register the command for configuring pairing rules + this.configureRulesCommand = vscode.commands.registerCommand( + 'clangd.newSourcePair.configureRules', this.configureRules, this); + } + + // Dispose method for cleanup when extension is deactivated + dispose() { + this.newPairCommand.dispose(); + this.configureRulesCommand.dispose(); + } + + // Main entry point for the file pair creation process + // Orchestrates the entire workflow using the service and UI layers + public async create(): Promise { + try { + const targetDirectory = await this.ui.getTargetDirectory(); + if (!targetDirectory) { + vscode.window.showErrorMessage( + 'Cannot determine target directory. Please open a folder or a file first.'); + return; + } + + const { language, uncertain } = await this.ui.detectLanguage(); + const rule = await this.ui.promptForPairingRule(language, uncertain); + if (!rule) + return; + + const fileName = await this.ui.promptForFileName(rule); + if (!fileName) + return; + + const headerPath = vscode.Uri.file( + path.join(targetDirectory.fsPath, `${fileName}${rule.headerExt}`)); + const sourcePath = vscode.Uri.file( + path.join(targetDirectory.fsPath, `${fileName}${rule.sourceExt}`)); + + const existingFilePath = + await this.service.checkFileExistence(headerPath, sourcePath); + if (existingFilePath) { + vscode.window.showErrorMessage( + `File already exists: ${existingFilePath}`); + return; + } + + const eol = this.service.getLineEnding(); + const { headerContent, sourceContent } = + this.service.generateFileContent(fileName, eol, rule); + + await this.service.writeFiles(headerPath, sourcePath, headerContent, + sourceContent); + await this.ui.showSuccessAndOpenFile(headerPath, sourcePath); + + } catch (error: any) { + vscode.window.showErrorMessage(error.message || + 'An unexpected error occurred.'); + } + } + + // Opens the configuration wizard for source/header pairing rules + // Delegates to the PairingRuleManager's configuration interface + public async configureRules(): Promise { + await showConfigurationWizard(); + } +} diff --git a/src/create-source-header-pair/index.ts b/src/create-source-header-pair/index.ts new file mode 100644 index 00000000..9ad1ba6e --- /dev/null +++ b/src/create-source-header-pair/index.ts @@ -0,0 +1,47 @@ +// +// CREATE SOURCE HEADER PAIR - MODULE INDEX +// ======================================== +// +// This module provides functionality to create matching header/source file pairs +// for C/C++ development. It intelligently detects language context, offers +// appropriate templates, and handles custom file extensions. +// +// ARCHITECTURE: +// - templates.ts: Template rules and file content templates +// - service.ts: Business logic layer (language detection, file operations) +// - ui.ts: User interface layer (dialogs, input validation) +// - coordinator.ts: Main coordinator (orchestrates workflow, registers commands) +// +// WORKFLOW: +// Command triggered → Detect target directory → Analyze language context → +// Check for custom rules → Present template choices → Get file name → +// Validate uniqueness → Generate content → Write files → Open in editor +// +// FEATURES: +// - Smart language detection (C vs C++) +// - Multiple template types (class, struct, empty) +// - Custom file extension support +// - Header guard generation +// - Cross-language template options +// - Workspace-aware directory selection +// - Input validation for C/C++ identifiers +// +// INTEGRATION: +// Uses PairingRuleManager for custom extension configurations +// Integrates with VS Code file system and editor APIs +// + +import { ClangdContext } from '../clangd-context'; +import { PairCoordinator } from './coordinator'; + +// Registers the create source/header pair command with the VS Code extension context +// This function should be called during extension activation to make the command available +export function registerCreateSourceHeaderPairCommand(context: ClangdContext) { + context.subscriptions.push(new PairCoordinator()); +} + +// Re-export main types and classes for external usage +export { PairCoordinator } from './coordinator'; +export { PairCreatorService } from './service'; +export { PairCreatorUI } from './ui'; +export { Language, TemplateKey } from './templates'; diff --git a/src/create-source-header-pair/service.ts b/src/create-source-header-pair/service.ts new file mode 100644 index 00000000..d7cd9e12 --- /dev/null +++ b/src/create-source-header-pair/service.ts @@ -0,0 +1,332 @@ +// +// PAIR CREATOR SERVICE +// =================== +// +// Business logic layer for file pair creation functionality. +// Handles language detection, file operations, template processing, +// and configuration management. +// + +import * as path from 'path'; +import * as vscode from 'vscode'; + +import { PairingRule, PairingRuleService } from '../pairing-rule-manager'; +import { + Language, + TemplateKey, + DEFAULT_PLACEHOLDERS, + FILE_TEMPLATES +} from './templates'; + +// Service Layer - Core business logic +export class PairCreatorService { + // Cache for expensive file system operations + private static readonly fileStatCache = new Map>(); + + // Definitive file extensions for fast lookup + private static readonly DEFINITIVE_EXTENSIONS = { + c: new Set(['.c']), + cpp: new Set(['.cpp', '.cc', '.cxx', '.hh', '.hpp', '.hxx']) + }; + + // Optimized file existence check with caching to improve performance + private static async fileExists(filePath: string): Promise { + if (this.fileStatCache.has(filePath)) { + return this.fileStatCache.get(filePath)!; + } + + const promise = + Promise.resolve(vscode.workspace.fs.stat(vscode.Uri.file(filePath)) + .then(() => true, () => false)); + + this.fileStatCache.set(filePath, promise); + + // Auto-clear cache entry after 5 seconds to prevent memory leaks + setTimeout(() => this.fileStatCache.delete(filePath), 5000); + + return promise; + } + + // Detects programming language from VS Code editor context + // ARCHITECTURE NOTE: This method accesses VS Code APIs but belongs in service layer + // because it contains the business logic for determining language context. + // UI layer should only handle user interactions, not business decisions. + public async detectLanguageFromEditor(): + Promise<{ language: Language, uncertain: boolean }> { + const activeEditor = vscode.window.activeTextEditor; + const languageId = activeEditor?.document?.languageId; + const filePath = activeEditor?.document && !activeEditor.document.isUntitled + ? activeEditor.document.uri.fsPath + : undefined; + + return this.detectLanguage(languageId, filePath); + } + + // Detects programming language from file info (pure business logic) + // DETECTION STRATEGY: + // 1. Fast path: Check file extension against definitive extension sets + // - .c files are definitely C + // - .cpp/.cc/.cxx/.hh/.hpp/.hxx are definitely C++ + // 2. Special case: .h files are ambiguous (could be C or C++) + // - Look for companion files in same directory to determine context + // - If MyClass.h exists with MyClass.cpp -> C++ + // - If utils.h exists with utils.c -> C + // 3. Fallback: Use VS Code's language ID detection + // - Based on file content analysis or user settings + // 4. Default: When all else fails, assume C++ (more common in modern development) + public async detectLanguage(languageId?: string, filePath?: string): + Promise<{ language: Language, uncertain: boolean }> { + if (!languageId || !filePath) { + return { language: 'cpp', uncertain: true }; + } + + const ext = path.extname(filePath); + + // Fast path for definitive extensions + if (PairCreatorService.DEFINITIVE_EXTENSIONS.c.has(ext)) { + return { language: 'c', uncertain: false }; + } + if (PairCreatorService.DEFINITIVE_EXTENSIONS.cpp.has(ext)) { + return { language: 'cpp', uncertain: false }; + } + + // Special handling for .h files with companion file detection + if (ext === '.h') { + const result = await this.detectLanguageForHeaderFile(filePath); + if (result) + return result; + } + + // Fallback to language ID + return { language: languageId === 'c' ? 'c' : 'cpp', uncertain: true }; + } + + // Optimized header file language detection by checking companion files + // HEADER FILE DETECTION STRATEGY: + // Problem: .h files are used by both C and C++, making language detection ambiguous + // Solution: Look for companion source files in the same directory + // + // Algorithm: + // 1. Extract base name from header file (e.g., "utils" from "utils.h") + // 2. Check for C companion first (utils.c) - early exit optimization + // - C projects are less common, so checking first allows quick determination + // 3. Check for C++ companions in parallel (utils.cpp, utils.cc, utils.cxx) + // - Use Promise.all for concurrent file existence checks + // 4. Return definitive result if companion found, otherwise default to C++ + // + // Examples: + // - math.h + math.c exists → Detected as C language + // - Vector.h + Vector.cpp exists → Detected as C++ language + // - standalone.h (no companions) → Default to C++ (uncertain=true) + private async detectLanguageForHeaderFile(filePath: string): + Promise<{ language: Language, uncertain: boolean } | null> { + const baseName = path.basename(filePath, '.h'); + const dirPath = path.dirname(filePath); + + // Check for C companion file first (less common, check first for early exit) + const cFile = path.join(dirPath, `${baseName}.c`); + if (await PairCreatorService.fileExists(cFile)) { + return { language: 'c', uncertain: false }; + } + + // Check for C++ companion files in parallel + const cppExtensions = ['.cpp', '.cc', '.cxx']; + const cppChecks = + cppExtensions.map(ext => PairCreatorService.fileExists( + path.join(dirPath, `${baseName}${ext}`))); + + const results = await Promise.all(cppChecks); + if (results.some((exists: boolean) => exists)) { + return { language: 'cpp', uncertain: false }; + } + + return { language: 'cpp', uncertain: true }; + } + + // Gets all available pairing rules (custom + workspace + user) + public getAllPairingRules(): PairingRule[] { + return [ + ...(PairingRuleService.getRules('workspace') ?? []), + ...(PairingRuleService.getRules('user') ?? []) + ]; + } + + // Gets custom C++ extensions if available from configuration + public getCustomCppExtensions(): { headerExt: string, sourceExt: string } | null { + const allRules = this.getAllPairingRules(); + const cppCustomRule = + allRules.find((rule: PairingRule) => rule.language === 'cpp'); + return cppCustomRule ? { + headerExt: cppCustomRule.headerExt, + sourceExt: cppCustomRule.sourceExt + } + : null; + } + + // Converts string to PascalCase efficiently (pure function) + public toPascalCase(input: string): string { + return input.split(/[-_]+/) + .map(word => word.charAt(0).toUpperCase() + word.slice(1)) + .join(''); + } + + // Gets default placeholder based on rule type (pure function) + public getDefaultPlaceholder(rule: PairingRule): string { + if (rule.isClass) { + return DEFAULT_PLACEHOLDERS.CPP_CLASS; + } + + if (rule.isStruct) { + return rule.language === 'cpp' ? DEFAULT_PLACEHOLDERS.CPP_STRUCT + : DEFAULT_PLACEHOLDERS.C_STRUCT; + } + + return rule.language === 'c' ? DEFAULT_PLACEHOLDERS.C_EMPTY + : DEFAULT_PLACEHOLDERS.CPP_EMPTY; + } + + // Optimized line ending detection based on VS Code settings and platform + public getLineEnding(): string { + const eolSetting = + vscode.workspace.getConfiguration('files').get('eol'); + + return eolSetting === '\n' || eolSetting === '\r\n' ? eolSetting + : process.platform === 'win32' ? '\r\n' + : '\n'; + } + + // Generates file content with improved template selection + public generateFileContent(fileName: string, eol: string, rule: PairingRule): { headerContent: string; sourceContent: string; } { + const templateKey: TemplateKey = + rule.isClass ? 'CPP_CLASS' + : rule.isStruct ? (rule.language === 'cpp' ? 'CPP_STRUCT' : 'C_STRUCT') + : rule.language === 'c' ? 'C_EMPTY' + : 'CPP_EMPTY'; + + const templates = FILE_TEMPLATES[templateKey]; + const context = { + fileName, + headerGuard: `${fileName.toUpperCase()}_H_`, + includeLine: `#include "${fileName}${rule.headerExt}"` + }; + + const headerContent = this.applyTemplate(templates.header, context); + const sourceContent = this.applyTemplate(templates.source, context); + + return { + headerContent: headerContent.replace(/\n/g, eol), + sourceContent: sourceContent.replace(/\n/g, eol) + }; + } + + // Optimized template variable substitution using regex replacement + private applyTemplate(template: string, + context: Record): string { + // Pre-compile regex for better performance if used frequently + return template.replace(/\{\{(\w+)\}\}/g, (_, key) => context[key] ?? ''); + } + + // File existence check with parallel processing for multiple files + public async checkFileExistence(headerPath: vscode.Uri, + sourcePath: vscode.Uri): + Promise { + const checks = [headerPath, sourcePath].map(async (uri) => { + try { + await vscode.workspace.fs.stat(uri); + return uri.fsPath; + } catch { + return null; + } + }); + + const results = await Promise.all(checks); + return results.find(path => path !== null) ?? null; + } + + // Optimized file writing with error handling and parallel writes + public async writeFiles(headerPath: vscode.Uri, sourcePath: vscode.Uri, + headerContent: string, + sourceContent: string): Promise { + try { + await Promise.all([ + vscode.workspace.fs.writeFile(headerPath, + Buffer.from(headerContent, 'utf8')), + vscode.workspace.fs.writeFile(sourcePath, + Buffer.from(sourceContent, 'utf8')) + ]); + } catch (error) { + throw new Error(`Failed to create files: ${error instanceof Error ? error.message : 'Unknown error'}`); + } + } + + // Smart target directory detection (pure business logic) + public async getTargetDirectory(activeDocumentPath?: string, + workspaceFolders + ?: readonly vscode.WorkspaceFolder[]): + Promise { + // Prefer current file's directory + if (activeDocumentPath) { + return vscode.Uri.file(path.dirname(activeDocumentPath)); + } + + // Return single workspace folder directly + if (workspaceFolders?.length === 1) { + return workspaceFolders[0].uri; + } + + // Multiple workspace folders require UI selection + return undefined; + } + + // Language mismatch warning logic (pure business logic) + public async shouldShowLanguageMismatchWarning(language: Language, + result: PairingRule, + currentDir?: string, + activeFilePath + ?: string): Promise { + if (!currentDir || !activeFilePath) { + return true; + } + + if (language === 'c' && result.language === 'cpp') { + return this.checkForCppFilesInDirectory(currentDir); + } + + return this.checkForCorrespondingSourceFiles(currentDir, activeFilePath, + language); + } + + // Check for C++ files in directory to inform language mismatch warnings + private async checkForCppFilesInDirectory(dirPath: string): Promise { + try { + const entries = + await vscode.workspace.fs.readDirectory(vscode.Uri.file(dirPath)); + const hasCppFiles = + entries.some(([fileName, fileType]) => + fileType === vscode.FileType.File && + PairCreatorService.DEFINITIVE_EXTENSIONS.cpp.has( + path.extname(fileName))); + return !hasCppFiles; // Show warning if NO C++ files found + } catch { + return true; // Show warning if can't check + } + } + + // Check for corresponding source files to inform language mismatch warnings + private async checkForCorrespondingSourceFiles( + dirPath: string, filePath: string, language: Language): Promise { + const baseName = path.basename(filePath, path.extname(filePath)); + const extensions = language === 'c' ? ['.c'] : ['.cpp', '.cc', '.cxx']; + + const checks = extensions.map(ext => PairCreatorService.fileExists( + path.join(dirPath, `${baseName}${ext}`))); + + try { + const results = await Promise.all(checks); + return !results.some( + (exists: boolean) => exists); // Show warning if NO corresponding files found + } catch { + return true; // Show warning if can't check + } + } +} diff --git a/src/create-source-header-pair/templates.ts b/src/create-source-header-pair/templates.ts new file mode 100644 index 00000000..dc82cd8c --- /dev/null +++ b/src/create-source-header-pair/templates.ts @@ -0,0 +1,156 @@ +// +// TEMPLATES AND CONSTANTS +// ======================= +// +// This module contains all the hardcoded data for file pair creation: +// - Template rules for different file types +// - File content templates with placeholders +// - Default placeholder names +// - Validation patterns +// + +import { PairingRule } from '../pairing-rule-manager'; + +// Types for better type safety +export type Language = 'c' | 'cpp'; +export type TemplateKey = 'CPP_CLASS' | 'CPP_STRUCT' | 'C_STRUCT' | 'C_EMPTY' | 'CPP_EMPTY'; + +// Regular expression patterns to validate C/C++ identifiers +export const VALIDATION_PATTERNS = { + IDENTIFIER: /^[a-zA-Z_][a-zA-Z0-9_]*$/ +}; + +// Default placeholder names for different file types +export const DEFAULT_PLACEHOLDERS = { + C_EMPTY: 'my_c_functions', + C_STRUCT: 'MyStruct', + CPP_EMPTY: 'utils', + CPP_CLASS: 'MyClass', + CPP_STRUCT: 'MyStruct' +}; + +// Template rules for available file pair types +export const TEMPLATE_RULES: PairingRule[] = [ + { + key: 'cpp_empty', + label: '$(new-file) C++ Pair', + description: 'Creates a basic Header/Source file pair with header guards.', + language: 'cpp' as const, + headerExt: '.h', + sourceExt: '.cpp' + }, + { + key: 'cpp_class', + label: '$(symbol-class) C++ Class', + description: + 'Creates a Header/Source file pair with a boilerplate class definition.', + language: 'cpp' as const, + headerExt: '.h', + sourceExt: '.cpp', + isClass: true + }, + { + key: 'cpp_struct', + label: '$(symbol-struct) C++ Struct', + description: + 'Creates a Header/Source file pair with a boilerplate struct definition.', + language: 'cpp' as const, + headerExt: '.h', + sourceExt: '.cpp', + isStruct: true + }, + { + key: 'c_empty', + label: '$(file-code) C Pair', + description: 'Creates a basic .h/.c file pair for function declarations.', + language: 'c' as const, + headerExt: '.h', + sourceExt: '.c' + }, + { + key: 'c_struct', + label: '$(symbol-struct) C Struct', + description: 'Creates a .h/.c file pair with a boilerplate typedef struct.', + language: 'c' as const, + headerExt: '.h', + sourceExt: '.c', + isStruct: true + } +]; + +// File templates with immutable structure +export const FILE_TEMPLATES = { + CPP_CLASS: { + header: `#ifndef {{headerGuard}} +#define {{headerGuard}} + +class {{fileName}} { +public: + {{fileName}}(); + ~{{fileName}}(); + +private: + // Add private members here +}; + +#endif // {{headerGuard}} +`, + source: `{{includeLine}} + +{{fileName}}::{{fileName}}() { + // Constructor implementation +} + +{{fileName}}::~{{fileName}}() { + // Destructor implementation +} +` + }, + CPP_STRUCT: { + header: `#ifndef {{headerGuard}} +#define {{headerGuard}} + +struct {{fileName}} { + // Struct members +}; + +#endif // {{headerGuard}} +`, + source: '{{includeLine}}' + }, + C_STRUCT: { + header: `#ifndef {{headerGuard}} +#define {{headerGuard}} + +typedef struct { + // Struct members +} {{fileName}}; + +#endif // {{headerGuard}} +`, + source: '{{includeLine}}' + }, + C_EMPTY: { + header: `#ifndef {{headerGuard}} +#define {{headerGuard}} + +// Declarations for {{fileName}}.c + +#endif // {{headerGuard}} +`, + source: `{{includeLine}} + +// Implementations for {{fileName}}.c +` + }, + CPP_EMPTY: { + header: `#ifndef {{headerGuard}} +#define {{headerGuard}} + +// Declarations for {{fileName}}.cpp + +#endif // {{headerGuard}} +`, + source: '{{includeLine}}' + } +}; diff --git a/src/create-source-header-pair/ui.ts b/src/create-source-header-pair/ui.ts new file mode 100644 index 00000000..e8e3dd44 --- /dev/null +++ b/src/create-source-header-pair/ui.ts @@ -0,0 +1,476 @@ +// +// PAIR CREATOR UI +// =============== +// +// User interface layer for file pair creation functionality. +// Handles all user interactions, input validation, dialogs, +// and template selection. +// + +import * as path from 'path'; +import * as vscode from 'vscode'; + +import { PairingRule, PairingRuleService } from '../pairing-rule-manager'; +import { PairCreatorService } from './service'; +import { Language, VALIDATION_PATTERNS, TEMPLATE_RULES } from './templates'; + +// PairCreatorUI handles all user interface interactions for file pair creation. +// It manages dialogs, input validation, and user choices. +export class PairCreatorUI { + private service: PairCreatorService; + + constructor(service: PairCreatorService) { this.service = service; } + + // Gets placeholder name for input dialog, considering active file context + private getPlaceholder(rule: PairingRule): string { + const activeEditor = vscode.window.activeTextEditor; + + if (activeEditor?.document && !activeEditor.document.isUntitled) { + const fileName = + path.basename(activeEditor.document.fileName, + path.extname(activeEditor.document.fileName)); + return rule.language === 'c' ? fileName + : this.service.toPascalCase(fileName); + } + + return this.service.getDefaultPlaceholder(rule); + } + + // Gets target directory with UI fallback for multiple workspace folders + public async getTargetDirectory(): Promise { + const activeEditor = vscode.window.activeTextEditor; + const activeDocumentPath = + activeEditor?.document && !activeEditor.document.isUntitled + ? activeEditor.document.uri.fsPath + : undefined; + const workspaceFolders = vscode.workspace.workspaceFolders; + + // Try service layer first + const result = await this.service.getTargetDirectory(activeDocumentPath, + workspaceFolders); + if (result) { + return result; + } + + // Handle multiple workspace folders with UI + if (workspaceFolders && workspaceFolders.length > 1) { + const selected = await vscode.window.showWorkspaceFolderPick( + { placeHolder: 'Select workspace folder for new files' }); + return selected?.uri; + } + + return undefined; + } + + // Checks if language mismatch warning should be shown with UI context + private async shouldShowLanguageMismatchWarning(language: Language, + result: PairingRule): + Promise { + const activeEditor = vscode.window.activeTextEditor; + const currentDir = + activeEditor?.document && !activeEditor.document.isUntitled + ? path.dirname(activeEditor.document.uri.fsPath) + : undefined; + const activeFilePath = + activeEditor?.document && !activeEditor.document.isUntitled + ? activeEditor.document.uri.fsPath + : undefined; + + return this.service.shouldShowLanguageMismatchWarning( + language, result, currentDir, activeFilePath); + } + + // Detects programming language by delegating to service layer + // UI layer should not contain business logic, only call service methods + public async detectLanguage(): + Promise<{ language: Language, uncertain: boolean }> { + return this.service.detectLanguageFromEditor(); + } + + // Adapts template rules for display in UI based on custom extensions + private adaptRuleForDisplay(rule: PairingRule): PairingRule { + if (rule.language !== 'cpp') { + return rule; + } + + const customExtensions = this.service.getCustomCppExtensions(); + if (!customExtensions) { + return rule; + } + + const { headerExt, sourceExt } = customExtensions; + + // Adapt description for display + const replacementPattern = + /Header\/Source|\.h(?:h|pp|xx)?\/\.c(?:pp|c|xx)?/g; + const newDescription = rule.description.replace( + replacementPattern, `${headerExt}/${sourceExt}`); + + return { ...rule, description: newDescription, headerExt, sourceExt }; + } + + // Prepares template choices for UI display with proper ordering and adaptation + private prepareTemplateChoices(language: 'c' | 'cpp', + uncertain: boolean): PairingRule[] { + const desiredOrder = + uncertain + ? ['cpp_empty', 'c_empty', 'cpp_class', 'cpp_struct', 'c_struct'] + : language === 'c' + ? ['c_empty', 'c_struct', 'cpp_empty', 'cpp_class', 'cpp_struct'] + : ['cpp_empty', 'cpp_class', 'cpp_struct', 'c_empty', 'c_struct']; + + return [...TEMPLATE_RULES] + .sort((a, b) => + desiredOrder.indexOf(a.key) - desiredOrder.indexOf(b.key)) + .map(rule => this.adaptRuleForDisplay(rule)); + } + + // Filters custom rules by language and prepares them for display + private prepareCustomRulesChoices(allRules: PairingRule[], + language: 'c' | 'cpp'): { + languageRules: PairingRule[], + adaptedDefaultTemplates: PairingRule[], + otherLanguageTemplates: PairingRule[], + cleanedCustomRules: PairingRule[] + } { + const languageRules = + allRules.filter((rule: PairingRule) => rule.language === language); + const customExt = languageRules.length > 0 ? languageRules[0] : null; + + let adaptedDefaultTemplates: PairingRule[] = []; + + if (customExt && language === 'cpp') { + // For C++, adapt default templates with custom extensions + adaptedDefaultTemplates = + TEMPLATE_RULES + .filter( + template => + template.language === 'cpp' && + !languageRules.some( + customRule => + customRule.isClass === template.isClass && + customRule.isStruct === template.isStruct && + (customRule.isClass || customRule.isStruct || + (!customRule.isClass && !customRule.isStruct && + !template.isClass && !template.isStruct)))) + .map( + template => ({ + ...template, + key: `${template.key}_adapted`, + headerExt: customExt.headerExt, + sourceExt: customExt.sourceExt, + description: + template.description + .replace( + /Header\/Source/g, + `${customExt.headerExt}/${customExt.sourceExt}`) + .replace(/\.h\/\.cpp/g, `${customExt.headerExt}/${customExt.sourceExt}`) + .replace(/basic \.h\/\.cpp/g, + `basic ${customExt.headerExt}/${customExt.sourceExt}`) + .replace(/Creates a \.h\/\.cpp/g, + `Creates a ${customExt.headerExt}/${customExt.sourceExt}`) + })); + } else { + // Standard adaptation for non-custom or C language + adaptedDefaultTemplates = + TEMPLATE_RULES + .filter(template => + template.language === language && + !languageRules.some( + customRule => + customRule.headerExt === template.headerExt && + customRule.sourceExt === template.sourceExt && + customRule.isClass === template.isClass && + customRule.isStruct === template.isStruct)) + .map(template => this.adaptRuleForDisplay(template)); + } + + const otherLanguageTemplates = + TEMPLATE_RULES.filter(template => template.language !== language) + .map(template => this.adaptRuleForDisplay(template)); + + const cleanedCustomRules = allRules.map( + (rule: PairingRule) => ({ + ...rule, + label: rule.label.includes('$(') + ? rule.label + : `$(new-file) ${rule.language === 'cpp' ? 'C++' : 'C'} Pair (${rule.headerExt}/${rule.sourceExt})`, + description: rule.description.startsWith('Creates a') + ? rule.description + : `Creates a ${rule.headerExt}/${rule.sourceExt} file pair with header guards.` + })); + + return { + languageRules, + adaptedDefaultTemplates, + otherLanguageTemplates, + cleanedCustomRules + }; + } + + // Checks for existing custom pairing rules and offers to create them if not found + // For C++, presents options to use custom rules or create new ones. + // For C, always uses default templates. + // Returns selected rule, null if cancelled, undefined for defaults, or 'use_default' flag + public async checkAndOfferCustomRules(language: 'c' | 'cpp', + uncertain: boolean): + Promise { + if (language === 'c') + return undefined; // Always use default C templates + + const allRules = this.service.getAllPairingRules(); + const languageRules = + allRules.filter((rule: PairingRule) => rule.language === language); + + if (languageRules.length > 0) { + const result = await this.selectFromCustomRules(allRules, language); + return result === undefined ? null : result; + } + + if (!uncertain) { + const shouldCreateRules = await this.offerToCreateCustomRules(language); + if (shouldCreateRules === null) + return null; + if (shouldCreateRules) { + const result = await this.createCustomRules(language); + return result === undefined ? null : result; + } + } + + return undefined; + } + + // Presents a selection dialog for custom pairing rules + // Combines custom rules with adapted default templates and cross-language options + // Returns selected rule, undefined if cancelled, or 'use_default' flag + public async selectFromCustomRules(allRules: PairingRule[], + language: 'c' | 'cpp'): + Promise { + + const { + cleanedCustomRules, + adaptedDefaultTemplates, + otherLanguageTemplates + } = this.prepareCustomRulesChoices(allRules, language); + + const choices = [ + ...cleanedCustomRules, ...adaptedDefaultTemplates, + ...otherLanguageTemplates, { + key: 'use_default', + label: '$(list-unordered) Use Default Templates', + description: + 'Use the built-in default pairing rules instead of custom rules', + isSpecial: true + } + ]; + + const result = await vscode.window.showQuickPick(choices, { + placeHolder: `Select a ${language.toUpperCase()} pairing rule`, + title: 'Custom Pairing Rules Available', + }); + + if (!result) + return undefined; + if ('isSpecial' in result && result.isSpecial && + result.key === 'use_default') + return 'use_default'; + return result as PairingRule; + } + + // Shows a dialog offering to create custom pairing rules for C++ + // Only applicable for C++ since C uses standard .c/.h extensions + // Returns true to create rules, false to dismiss, null if cancelled + public async offerToCreateCustomRules(language: 'c' | + 'cpp'): Promise { + if (language === 'c') + return false; + + const result = await vscode.window.showInformationMessage( + `No custom pairing rules found for C++. Would you like to create custom rules to use different file extensions (e.g., .cc/.hh instead of .cpp/.h)?`, + { modal: false }, 'Create Custom Rules', 'Dismiss'); + + return result === 'Create Custom Rules' ? true + : result === 'Dismiss' ? false + : null; + } + + // Guides the user through creating custom pairing rules for C++ + // Offers common extension combinations or allows custom input + // Saves the rule to workspace or global settings + // Returns the created custom rule or undefined if cancelled + public async createCustomRules(language: 'c' | + 'cpp'): Promise { + if (language === 'c') + return undefined; + + const commonExtensions = [ + { label: '.h / .cpp (Default)', headerExt: '.h', sourceExt: '.cpp' }, + { label: '.hh / .cc (Alternative)', headerExt: '.hh', sourceExt: '.cc' }, { + label: '.hpp / .cpp (Header Plus Plus)', + headerExt: '.hpp', + sourceExt: '.cpp' + }, + { label: '.hxx / .cxx (Extended)', headerExt: '.hxx', sourceExt: '.cxx' }, + { label: 'Custom Extensions', headerExt: '', sourceExt: '' } + ]; + + const selectedExtension = + await vscode.window.showQuickPick(commonExtensions, { + placeHolder: `Select file extensions for C++ files`, + title: 'Choose File Extensions' + }); + + if (!selectedExtension) + return undefined; + + let { headerExt, sourceExt } = selectedExtension; + + if (!headerExt || !sourceExt) { + const validateExt = (text: string) => + (!text || !text.startsWith('.') || text.length < 2) + ? 'Please enter a valid file extension starting with a dot (e.g., .h)' + : null; + + headerExt = await vscode.window.showInputBox({ + prompt: 'Enter header file extension (e.g., .h, .hh, .hpp)', + placeHolder: '.h', + validateInput: validateExt + }) || ''; + + if (!headerExt) + return undefined; + + sourceExt = await vscode.window.showInputBox({ + prompt: `Enter source file extension for C++ (e.g., .cpp, .cc, .cxx)`, + placeHolder: '.cpp', + validateInput: validateExt + }) || ''; + + if (!sourceExt) + return undefined; + } + + const customRule: PairingRule = { + key: `custom_cpp_${Date.now()}`, + label: `$(new-file) C++ Pair (${headerExt}/${sourceExt})`, + description: + `Creates a ${headerExt}/${sourceExt} file pair with header guards.`, + language: 'cpp', + headerExt, + sourceExt + }; + + const saveLocation = await vscode.window.showQuickPick( + [ + { + label: 'Workspace Settings', + description: 'Save to current workspace only', + value: 'workspace' + }, + { + label: 'Global Settings', + description: 'Save to user settings (available in all workspaces)', + value: 'user' + } + ], + { + placeHolder: 'Where would you like to save this custom rule?', + title: 'Save Location' + }); + + if (!saveLocation) + return undefined; + + try { + const existingRules = PairingRuleService.getRules( + saveLocation.value as 'workspace' | 'user') || + []; + await PairingRuleService.writeRules([...existingRules, customRule], + saveLocation.value as 'workspace' | + 'user'); + + const locationText = + saveLocation.value === 'workspace' ? 'workspace' : 'global'; + vscode.window.showInformationMessage( + `Custom pairing rule saved to ${locationText} settings.`); + + return customRule; + } catch (error: any) { + vscode.window.showErrorMessage( + `Failed to save custom rule: ${error.message}`); + return undefined; + } + } + + // Prompts the user to select a pairing rule from available options + // First checks for custom rules, then falls back to default templates + // Returns selected pairing rule or undefined if cancelled + public async promptForPairingRule(language: 'c' | 'cpp', uncertain: boolean): + Promise { + const customRulesResult = + await this.checkAndOfferCustomRules(language, uncertain); + + if (customRulesResult === null) + return undefined; + if (customRulesResult === 'use_default') { + // Continue to default template selection + } else if (customRulesResult) { + return customRulesResult; + } + + const choices = this.prepareTemplateChoices(language, uncertain); + + const result = await vscode.window.showQuickPick(choices, { + placeHolder: 'Please select the type of file pair to create.', + title: 'Create Source/Header Pair' + }); + + if (result && !uncertain && language !== result.language) { + const shouldShowWarning = + await this.shouldShowLanguageMismatchWarning(language, result); + + if (shouldShowWarning) { + const detectedLangName = language === 'c' ? 'C' : 'C++'; + const selectedLangName = result.language === 'c' ? 'C' : 'C++'; + + const shouldContinue = await vscode.window.showWarningMessage( + `You're working in a ${detectedLangName} file but selected a ${selectedLangName} template. This may create files with incompatible extensions or content.`, + 'Continue', 'Cancel'); + + if (shouldContinue !== 'Continue') + return undefined; + } + } + + return result; + } + + // Prompts the user to enter a name for the new file pair + // Validates input as a valid C/C++ identifier and provides context-appropriate prompts + // Returns the entered file name or undefined if cancelled + public async promptForFileName(rule: PairingRule): Promise { + const prompt = rule.isClass ? 'Please enter the name for the new C++ class.' + : rule.isStruct + ? `Please enter the name for the new ${rule.language.toUpperCase()} struct.` + : `Please enter the base name for the new ${rule.language.toUpperCase()} file pair.`; + + return vscode.window.showInputBox({ + prompt, + placeHolder: this.getPlaceholder(rule), + validateInput: (text) => + VALIDATION_PATTERNS.IDENTIFIER.test(text?.trim() || '') + ? null + : 'Invalid C/C++ identifier.', + title: 'Create Source/Header Pair' + }); + } + + // Shows success message and opens the newly created header file + public async showSuccessAndOpenFile(headerPath: vscode.Uri, + sourcePath: vscode.Uri): Promise { + await vscode.window.showTextDocument( + await vscode.workspace.openTextDocument(headerPath)); + await vscode.window.showInformationMessage( + `Successfully created ${path.basename(headerPath.fsPath)} and ${path.basename(sourcePath.fsPath)}.`); + } +} diff --git a/src/extension.ts b/src/extension.ts index 642fcb32..1a7273f8 100644 --- a/src/extension.ts +++ b/src/extension.ts @@ -1,75 +1,76 @@ import * as vscode from 'vscode'; -import {ClangdExtension} from '../api/vscode-clangd'; +import { ClangdExtension } from '../api/vscode-clangd'; -import {ClangdExtensionImpl} from './api'; -import {ClangdContext} from './clangd-context'; -import {get, update} from './config'; +import { ClangdExtensionImpl } from './api'; +import { ClangdContext } from './clangd-context'; +import { get, update } from './config'; import { registerCreateSourceHeaderPairCommand -} from './create-source-header-pair'; +} from './create-source-header-pair/index'; +import { showConfigurationWizard } from './pairing-rule-manager'; -let apiInstance: ClangdExtensionImpl|undefined; +let apiInstance: ClangdExtensionImpl | undefined; /** * This method is called when the extension is activated. The extension is * activated the very first time a command is executed. */ export async function activate(context: vscode.ExtensionContext): - Promise { + Promise { const outputChannel = vscode.window.createOutputChannel('clangd'); context.subscriptions.push(outputChannel); - let clangdContext: ClangdContext|null = null; + let clangdContext: ClangdContext | null = null; // An empty place holder for the activate command, otherwise we'll get an // "command is not registered" error. context.subscriptions.push( - vscode.commands.registerCommand('clangd.activate', async () => {})); + vscode.commands.registerCommand('clangd.activate', async () => { })); context.subscriptions.push( - vscode.commands.registerCommand('clangd.restart', async () => { - if (!get('enable')) { - vscode.window - .showInformationMessage( - 'Language features from Clangd are currently disabled. Would you like to enable them?', - 'Enable', 'Close') - .then(async (choice) => { - if (choice === 'Enable') { - await update('enable', true); - vscode.commands.executeCommand('clangd.restart'); - } - }); - return; - } + vscode.commands.registerCommand('clangd.restart', async () => { + if (!get('enable')) { + vscode.window + .showInformationMessage( + 'Language features from Clangd are currently disabled. Would you like to enable them?', + 'Enable', 'Close') + .then(async (choice) => { + if (choice === 'Enable') { + await update('enable', true); + vscode.commands.executeCommand('clangd.restart'); + } + }); + return; + } - // clangd.restart can be called when the extension is not yet activated. - // In such a case, vscode will activate the extension and then run this - // handler. Detect this situation and bail out (doing an extra - // stop/start cycle in this situation is pointless, and doesn't work - // anyways because the client can't be stop()-ped when it's still in the - // Starting state). - if (clangdContext && clangdContext.clientIsStarting()) { - return; - } - if (clangdContext) - clangdContext.dispose(); - clangdContext = await ClangdContext.create(context.globalStoragePath, - outputChannel); - if (clangdContext) { - context.subscriptions.push(clangdContext); + // clangd.restart can be called when the extension is not yet activated. + // In such a case, vscode will activate the extension and then run this + // handler. Detect this situation and bail out (doing an extra + // stop/start cycle in this situation is pointless, and doesn't work + // anyways because the client can't be stop()-ped when it's still in the + // Starting state). + if (clangdContext && clangdContext.clientIsStarting()) { + return; + } + if (clangdContext) + clangdContext.dispose(); + clangdContext = await ClangdContext.create(context.globalStoragePath, + outputChannel); + if (clangdContext) { + context.subscriptions.push(clangdContext); - registerCreateSourceHeaderPairCommand(clangdContext); - } - if (apiInstance) { - apiInstance.client = clangdContext?.client; - } - })); + registerCreateSourceHeaderPairCommand(clangdContext); + } + if (apiInstance) { + apiInstance.client = clangdContext?.client; + } + })); let shouldCheck = false; if (vscode.workspace.getConfiguration('clangd').get('enable')) { clangdContext = - await ClangdContext.create(context.globalStoragePath, outputChannel); + await ClangdContext.create(context.globalStoragePath, outputChannel); if (clangdContext) { context.subscriptions.push(clangdContext); @@ -77,42 +78,43 @@ export async function activate(context: vscode.ExtensionContext): } shouldCheck = vscode.workspace.getConfiguration('clangd').get( - 'detectExtensionConflicts') ?? - false; + 'detectExtensionConflicts') ?? + false; } if (shouldCheck) { - const interval = setInterval(function() { + const interval = setInterval(function () { const cppTools = vscode.extensions.getExtension('ms-vscode.cpptools'); if (cppTools && cppTools.isActive) { const cppToolsConfiguration = - vscode.workspace.getConfiguration('C_Cpp'); + vscode.workspace.getConfiguration('C_Cpp'); const cppToolsEnabled = - cppToolsConfiguration.get('intelliSenseEngine'); + cppToolsConfiguration.get('intelliSenseEngine'); if (cppToolsEnabled?.toLowerCase() !== 'disabled') { vscode.window - .showWarningMessage( - 'You have both the Microsoft C++ (cpptools) extension and ' + - 'clangd extension enabled. The Microsoft IntelliSense features ' + - 'conflict with clangd\'s code completion, diagnostics etc.', - 'Disable IntelliSense', 'Never show this warning') - .then(selection => { - if (selection == 'Disable IntelliSense') { - cppToolsConfiguration.update( - 'intelliSenseEngine', 'disabled', - vscode.ConfigurationTarget.Global); - } else if (selection == 'Never show this warning') { - vscode.workspace.getConfiguration('clangd').update( - 'detectExtensionConflicts', false, - vscode.ConfigurationTarget.Global); - clearInterval(interval); - } - }); + .showWarningMessage( + 'You have both the Microsoft C++ (cpptools) extension and ' + + 'clangd extension enabled. The Microsoft IntelliSense features ' + + 'conflict with clangd\'s code completion, diagnostics etc.', + 'Disable IntelliSense', 'Never show this warning') + .then(selection => { + if (selection == 'Disable IntelliSense') { + cppToolsConfiguration.update( + 'intelliSenseEngine', 'disabled', + vscode.ConfigurationTarget.Global); + } else if (selection == 'Never show this warning') { + vscode.workspace.getConfiguration('clangd').update( + 'detectExtensionConflicts', false, + vscode.ConfigurationTarget.Global); + clearInterval(interval); + } + }); } } }, 5000); } - + context.subscriptions.push(vscode.commands.registerCommand( + 'clangd.createPair.configureRules', showConfigurationWizard)); apiInstance = new ClangdExtensionImpl(clangdContext?.client); return apiInstance; } \ No newline at end of file diff --git a/src/pairing-rule-manager.ts b/src/pairing-rule-manager.ts index 2d0eb4d0..657e7edd 100644 --- a/src/pairing-rule-manager.ts +++ b/src/pairing-rule-manager.ts @@ -1,36 +1,3 @@ -// -// PAIRING RULE MANAGER -// ==================== -// -// PURPOSE: -// This module manages custom file extension pairing rules for C/C++ -// header/source files. It allows users to configure custom extensions (e.g., -// .hh/.cc instead of .h/.cpp) and provides UI for managing these -// configurations. -// -// ARCHITECTURE: -// 1. PairingRuleService - Core configuration management -// - Reads/writes VS Code workspace/user settings -// - Validates pairing rules -// - Handles configuration scopes (workspace vs global) -// -// 2. PairingRuleUI - User interface for configuration -// - Quick setup wizard with predefined options -// - Advanced management for editing/resetting rules -// - Scope selection (workspace vs global settings) -// -// WORKFLOW: -// User calls showConfigurationWizard() → -// Presents predefined extension combinations (.h/.cpp, .hh/.cc, etc.) → -// User selects option or goes to advanced management → -// Rule is saved to workspace or global settings → -// Other parts of extension use these rules for file creation -// -// CONFIGURATION STORAGE: -// - Workspace: .vscode/settings.json (clangd.createPair.rules) -// - Global: User settings.json (clangd.createPair.rules) -// - import * as vscode from 'vscode'; // Public interface for pairing rules @@ -38,7 +5,7 @@ export interface PairingRule { key: string; label: string; description: string; - language: 'c'|'cpp'; + language: 'c' | 'cpp'; headerExt: string; sourceExt: string; isClass?: boolean; @@ -46,8 +13,8 @@ export interface PairingRule { } // Type aliases for QuickPick items -type RuleQuickPickItem = vscode.QuickPickItem&{rule: PairingRule}; -type ActionQuickPickItem = vscode.QuickPickItem&{key: string}; +type RuleQuickPickItem = vscode.QuickPickItem & { rule: PairingRule }; +type ActionQuickPickItem = vscode.QuickPickItem & { key: string }; // Configuration management service class export class PairingRuleService { @@ -62,9 +29,8 @@ export class PairingRuleService { // Show error message and re-throw the error for proper error handling private static handleError(error: unknown, operation: string, - scope: string): never { - const message = `Failed to ${operation} pairing rules for ${scope}: ${ - error instanceof Error ? error.message : 'Unknown error'}`; + scope: string): never { + const message = `Failed to ${operation} pairing rules for ${scope}: ${error instanceof Error ? error.message : 'Unknown error'}`; vscode.window.showErrorMessage(message); throw error; } @@ -72,54 +38,54 @@ export class PairingRuleService { // Get the currently active pairing rules from configuration public static getActiveRules(): ReadonlyArray { return vscode.workspace.getConfiguration('clangd').get( - PairingRuleService.CONFIG_KEY, []); + PairingRuleService.CONFIG_KEY, []); } // Check if custom rules exist for the specified scope (workspace or user) - public static hasCustomRules(scope: 'workspace'|'user'): boolean { + public static hasCustomRules(scope: 'workspace' | 'user'): boolean { const inspection = - vscode.workspace.getConfiguration('clangd').inspect( - PairingRuleService.CONFIG_KEY); + vscode.workspace.getConfiguration('clangd').inspect( + PairingRuleService.CONFIG_KEY); const value = scope === 'workspace' ? inspection?.workspaceValue - : inspection?.globalValue; + : inspection?.globalValue; return Array.isArray(value); } // Get pairing rules for a specific scope (workspace or user) - public static getRules(scope: 'workspace'|'user'): PairingRule[]|undefined { + public static getRules(scope: 'workspace' | 'user'): PairingRule[] | undefined { const inspection = - vscode.workspace.getConfiguration('clangd').inspect( - PairingRuleService.CONFIG_KEY); + vscode.workspace.getConfiguration('clangd').inspect( + PairingRuleService.CONFIG_KEY); return scope === 'workspace' ? inspection?.workspaceValue - : inspection?.globalValue; + : inspection?.globalValue; } // Write pairing rules to the specified scope (workspace or user) public static async writeRules(rules: PairingRule[], - scope: 'workspace'|'user'): Promise { + scope: 'workspace' | 'user'): Promise { try { if (!Array.isArray(rules)) throw new Error('Rules must be an array'); rules.forEach(PairingRuleService.validateRule); const target = scope === 'workspace' - ? vscode.ConfigurationTarget.Workspace - : vscode.ConfigurationTarget.Global; + ? vscode.ConfigurationTarget.Workspace + : vscode.ConfigurationTarget.Global; await vscode.workspace.getConfiguration('clangd').update( - PairingRuleService.CONFIG_KEY, rules, target); + PairingRuleService.CONFIG_KEY, rules, target); } catch (error) { PairingRuleService.handleError(error, 'save', scope); } } // Reset pairing rules for the specified scope (remove custom rules) - public static async resetRules(scope: 'workspace'|'user'): Promise { + public static async resetRules(scope: 'workspace' | 'user'): Promise { try { const target = scope === 'workspace' - ? vscode.ConfigurationTarget.Workspace - : vscode.ConfigurationTarget.Global; + ? vscode.ConfigurationTarget.Workspace + : vscode.ConfigurationTarget.Global; await vscode.workspace.getConfiguration('clangd').update( - PairingRuleService.CONFIG_KEY, undefined, target); + PairingRuleService.CONFIG_KEY, undefined, target); } catch (error) { PairingRuleService.handleError(error, 'reset', scope); } @@ -163,20 +129,18 @@ export class PairingRuleUI { // Create rule choices from extension options for QuickPick display private static createRuleChoices(): RuleQuickPickItem[] { return PairingRuleUI.EXTENSION_OPTIONS.map( - (option, index) => ({ - label: `$(file-code) ${option.label}`, - description: option.description, - rule: { - key: `custom_${option.language}_${index}`, - label: `${option.language.toUpperCase()} Pair (${ - option.headerExt}/${option.sourceExt})`, - description: `Creates a ${option.headerExt}/${ - option.sourceExt} file pair with header guards.`, - language: option.language, - headerExt: option.headerExt, - sourceExt: option.sourceExt, - }, - })); + (option, index) => ({ + label: `$(file-code) ${option.label}`, + description: option.description, + rule: { + key: `custom_${option.language}_${index}`, + label: `${option.language.toUpperCase()} Pair (${option.headerExt}/${option.sourceExt})`, + description: `Creates a ${option.headerExt}/${option.sourceExt} file pair with header guards.`, + language: option.language, + headerExt: option.headerExt, + sourceExt: option.sourceExt, + }, + })); } // Create advanced options separator and menu item for advanced management @@ -218,11 +182,11 @@ export class PairingRuleUI { kind: vscode.QuickPickItemKind.Separator, key: 'separator_global', }, - { - label: '$(edit) Edit Global Rules...', - description: 'Opens your global settings.json', - key: 'edit_global', - }); + { + label: '$(edit) Edit Global Rules...', + description: 'Opens your global settings.json', + key: 'edit_global', + }); if (PairingRuleService.hasCustomRules('user')) { items.push({ @@ -238,48 +202,47 @@ export class PairingRuleUI { // Handle rule selection and ask for save scope (workspace or global) private static async handleRuleSelection(rule: PairingRule): Promise { const selection = await vscode.window.showQuickPick( - [ - { - label: 'Save for this Workspace', - description: 'Recommended. Creates a .vscode/settings.json file.', - scope: 'workspace', - }, - { - label: 'Save for all my Projects (Global)', - description: 'Modifies your global user settings.', - scope: 'user', - }, - ], + [ + { + label: 'Save for this Workspace', + description: 'Recommended. Creates a .vscode/settings.json file.', + scope: 'workspace', + }, { - placeHolder: 'Where would you like to save this rule?', - title: 'Save Configuration Scope', - }); + label: 'Save for all my Projects (Global)', + description: 'Modifies your global user settings.', + scope: 'user', + }, + ], + { + placeHolder: 'Where would you like to save this rule?', + title: 'Save Configuration Scope', + }); if (!selection) return; await PairingRuleService.writeRules([rule], selection.scope as 'workspace' | - 'user'); - vscode.window.showInformationMessage(`Successfully set '${ - rule.label}' as the default extension for the ${selection.scope}.`); + 'user'); + vscode.window.showInformationMessage(`Successfully set '${rule.label}' as the default extension for the ${selection.scope}.`); } // Handle advanced menu selection and execute the corresponding action private static async handleAdvancedMenuSelection(key: string): Promise { const actions = { edit_workspace: () => vscode.commands.executeCommand( - 'workbench.action.openWorkspaceSettingsFile'), + 'workbench.action.openWorkspaceSettingsFile'), edit_global: () => - vscode.commands.executeCommand('workbench.action.openSettingsJson'), + vscode.commands.executeCommand('workbench.action.openSettingsJson'), reset_workspace: async () => { await PairingRuleService.resetRules('workspace'); vscode.window.showInformationMessage( - 'Workspace pairing rules have been reset.'); + 'Workspace pairing rules have been reset.'); }, reset_global: async () => { await PairingRuleService.resetRules('user'); vscode.window.showInformationMessage( - 'Global pairing rules have been reset.'); + 'Global pairing rules have been reset.'); }, }; @@ -291,10 +254,10 @@ export class PairingRuleUI { // Main configuration wizard - entry point for setting up pairing rules public static async showConfigurationWizard(): Promise { const quickPick = - vscode.window.createQuickPick(); + vscode.window.createQuickPick(); quickPick.title = 'Quick Setup: Choose File Extensions'; quickPick.placeholder = - 'Choose file extension combination for this workspace, or go to advanced options.'; + 'Choose file extension combination for this workspace, or go to advanced options.'; quickPick.items = [ ...PairingRuleUI.createRuleChoices(), ...PairingRuleUI.createAdvancedOptions() @@ -320,9 +283,9 @@ export class PairingRuleUI { // Advanced management menu for editing and resetting pairing rules public static async showAdvancedManagementMenu(): Promise { const selection = await vscode.window.showQuickPick( - PairingRuleUI.createAdvancedMenuItems(), { - title: 'Advanced Rule Management', - }); + PairingRuleUI.createAdvancedMenuItems(), { + title: 'Advanced Rule Management', + }); if (selection?.key) { await PairingRuleUI.handleAdvancedMenuSelection(selection.key); From 2a60d7cfc14646e66fb365e3e1bd5e6ccc35cd45 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?=E9=95=BF=E6=9C=9F=E7=B4=A0=E9=A3=9F?= Date: Fri, 18 Jul 2025 22:52:45 +0800 Subject: [PATCH 16/34] Refactor C++ pair creation workflow and improve UX Refactored the coordinator to move directory and language detection to the service layer, streamlined the file creation flow, and added logic to offer saving custom C++ extension pairs as defaults. The UI now prompts for template type and file extensions in a more intuitive order, and success messages are shown non-blockingly. Service layer now provides file path creation and improved file existence caching. --- src/create-source-header-pair/coordinator.ts | 187 +++++++++++++--- src/create-source-header-pair/service.ts | 35 ++- src/create-source-header-pair/ui.ts | 223 +++++++++++++++---- 3 files changed, 358 insertions(+), 87 deletions(-) diff --git a/src/create-source-header-pair/coordinator.ts b/src/create-source-header-pair/coordinator.ts index 7717d909..6a012c4c 100644 --- a/src/create-source-header-pair/coordinator.ts +++ b/src/create-source-header-pair/coordinator.ts @@ -7,10 +7,9 @@ // interaction between service and UI layers. // -import * as path from 'path'; import * as vscode from 'vscode'; -import { showConfigurationWizard } from '../pairing-rule-manager'; +import { PairingRule, showConfigurationWizard } from '../pairing-rule-manager'; import { PairCreatorService } from './service'; import { PairCreatorUI } from './ui'; @@ -18,6 +17,12 @@ import { PairCreatorUI } from './ui'; // file pair creation workflow. It serves as the main entry point and // orchestrates the entire process. export class PairCoordinator implements vscode.Disposable { + private static readonly ERROR_MESSAGES = { + NO_TARGET_DIRECTORY: 'Cannot determine target directory. Please open a folder or a file first.', + FILE_EXISTS: (filePath: string) => `File already exists: ${filePath}`, + UNEXPECTED_ERROR: 'An unexpected error occurred.' + } as const; + private newPairCommand: vscode.Disposable; private configureRulesCommand: vscode.Disposable; private service: PairCreatorService; @@ -48,47 +53,165 @@ export class PairCoordinator implements vscode.Disposable { // Orchestrates the entire workflow using the service and UI layers public async create(): Promise { try { - const targetDirectory = await this.ui.getTargetDirectory(); + // 1. Get target directory + const targetDirectory = await this.service.getTargetDirectory( + vscode.window.activeTextEditor?.document?.uri.fsPath, + vscode.workspace.workspaceFolders) ?? + await this.showWorkspaceFolderPicker(); + if (!targetDirectory) { - vscode.window.showErrorMessage( - 'Cannot determine target directory. Please open a folder or a file first.'); + vscode.window.showErrorMessage(PairCoordinator.ERROR_MESSAGES.NO_TARGET_DIRECTORY); return; } - const { language, uncertain } = await this.ui.detectLanguage(); - const rule = await this.ui.promptForPairingRule(language, uncertain); - if (!rule) - return; + await this.createPairInDirectory(targetDirectory); - const fileName = await this.ui.promptForFileName(rule); - if (!fileName) - return; + } catch (error: unknown) { + const errorMessage = error instanceof Error ? error.message : 'An unexpected error occurred.'; + vscode.window.showErrorMessage(errorMessage); + } + } - const headerPath = vscode.Uri.file( - path.join(targetDirectory.fsPath, `${fileName}${rule.headerExt}`)); - const sourcePath = vscode.Uri.file( - path.join(targetDirectory.fsPath, `${fileName}${rule.sourceExt}`)); + // Show workspace folder picker when multiple folders are available + private async showWorkspaceFolderPicker(): Promise { + const workspaceFolders = vscode.workspace.workspaceFolders; + if (!workspaceFolders || workspaceFolders.length <= 1) { + return undefined; + } - const existingFilePath = - await this.service.checkFileExistence(headerPath, sourcePath); - if (existingFilePath) { - vscode.window.showErrorMessage( - `File already exists: ${existingFilePath}`); - return; - } + const selected = await vscode.window.showQuickPick( + workspaceFolders.map(folder => ({ + label: folder.name, + description: folder.uri.fsPath, + folder: folder + })), + { placeHolder: 'Select workspace folder for new files' } + ); + + return selected?.folder.uri; + } + + // Create file pair in the specified directory + private async createPairInDirectory(targetDirectory: vscode.Uri): Promise { + // 2. Detect language and get user inputs + const { language, uncertain } = await this.service.detectLanguageFromEditor(); + const rule = await this.ui.promptForPairingRule(language, uncertain); + if (!rule) return; + + const fileName = await this.ui.promptForFileName(rule); + if (!fileName) return; + + // 3. Create and validate file paths + const { headerPath, sourcePath } = this.service.createFilePaths(targetDirectory, fileName, rule); + + const existingFilePath = await this.service.checkFileExistence(headerPath, sourcePath); + if (existingFilePath) { + vscode.window.showErrorMessage( + PairCoordinator.ERROR_MESSAGES.FILE_EXISTS(existingFilePath)); + return; + } + + // 4. Generate and write files + await this.generateAndWriteFiles(fileName, rule, headerPath, sourcePath); - const eol = this.service.getLineEnding(); - const { headerContent, sourceContent } = - this.service.generateFileContent(fileName, eol, rule); + // 5. Show success and open files + await this.ui.showSuccessAndOpenFile(headerPath, sourcePath); - await this.service.writeFiles(headerPath, sourcePath, headerContent, - sourceContent); - await this.ui.showSuccessAndOpenFile(headerPath, sourcePath); + // 6. Offer to save as default if it's a custom rule without existing config + await this.offerToSaveAsDefault(rule, language); + } + + /** + * Offers to save the rule as default configuration after successful file creation + * Only shows this for C++ rules when no existing configuration exists + */ + private async offerToSaveAsDefault(rule: PairingRule, language: 'c' | 'cpp'): Promise { + // Only offer for C++ rules + if (language !== 'cpp') { + return; + } + + // Check if user already has custom C++ rules configured + const customRules = this.service.getAllPairingRules(); + const hasCppCustomRules = customRules.some(r => r.language === 'cpp'); + if (hasCppCustomRules) { + return; // Don't prompt if they already have C++ configuration + } - } catch (error: any) { - vscode.window.showErrorMessage(error.message || - 'An unexpected error occurred.'); + // Check if this is a custom rule (has _custom suffix means user went through extension selection) + const isCustomRule = rule.key.includes('custom'); + if (!isCustomRule) { + return; // Don't prompt for built-in rules } + + const choice = await vscode.window.showInformationMessage( + `Files created successfully! Would you like to save "${rule.headerExt}/${rule.sourceExt}" as your default C++ extensions for this workspace?`, + 'Save as Default', + 'Not Now' + ); + + if (choice === 'Save as Default') { + await this.saveRuleAsDefault(rule); + } + } + + //Saves a rule as the default configuration with user choice of scope + private async saveRuleAsDefault(rule: PairingRule): Promise { + const { PairingRuleService } = await import('../pairing-rule-manager'); + + const scopeChoice = await vscode.window.showQuickPick([ + { + label: 'Save for this Workspace', + description: 'Recommended. Creates a .vscode/settings.json file.', + scope: 'workspace' + }, + { + label: 'Save for all my Projects (Global)', + description: 'Modifies your global user settings.', + scope: 'user' + } + ], { + placeHolder: 'Where would you like to save this configuration?', + title: 'Save Configuration Scope' + }); + + if (!scopeChoice) return; + + try { + // Create a clean rule for saving (remove the 'custom' key suffix) + const cleanRule: PairingRule = { + ...rule, + key: rule.key.replace('_custom', ''), + label: `${rule.language.toUpperCase()} Pair (${rule.headerExt}/${rule.sourceExt})`, + description: `Creates a ${rule.headerExt}/${rule.sourceExt} file pair with header guards.` + }; + + await PairingRuleService.writeRules([cleanRule], scopeChoice.scope as 'workspace' | 'user'); + + vscode.window.showInformationMessage( + `Successfully saved '${rule.headerExt}/${rule.sourceExt}' as the default extension for ${scopeChoice.scope === 'workspace' ? 'this workspace' : 'all projects'}.` + ); + } catch (error) { + vscode.window.showErrorMessage( + `Failed to save configuration: ${error instanceof Error ? error.message : 'Unknown error'}` + ); + } + } + + /** + * Generates file content and writes both header and source files + */ + private async generateAndWriteFiles( + fileName: string, + rule: PairingRule, + headerPath: vscode.Uri, + sourcePath: vscode.Uri + ): Promise { + const eol = this.service.getLineEnding(); + const { headerContent, sourceContent } = + this.service.generateFileContent(fileName, eol, rule); + + await this.service.writeFiles(headerPath, sourcePath, headerContent, sourceContent); } // Opens the configuration wizard for source/header pairing rules diff --git a/src/create-source-header-pair/service.ts b/src/create-source-header-pair/service.ts index d7cd9e12..8a8a7fe3 100644 --- a/src/create-source-header-pair/service.ts +++ b/src/create-source-header-pair/service.ts @@ -20,14 +20,34 @@ import { // Service Layer - Core business logic export class PairCreatorService { - // Cache for expensive file system operations + // Cache for expensive file system operations with TTL private static readonly fileStatCache = new Map>(); + private static readonly CACHE_TTL = 5000; // 5 seconds // Definitive file extensions for fast lookup private static readonly DEFINITIVE_EXTENSIONS = { c: new Set(['.c']), cpp: new Set(['.cpp', '.cc', '.cxx', '.hh', '.hpp', '.hxx']) - }; + } as const; + + /** + * Creates file paths for header and source files + * @param targetDirectory Target directory URI + * @param fileName Base file name without extension + * @param rule Pairing rule with extensions + * @returns Object with headerPath and sourcePath URIs + */ + public createFilePaths(targetDirectory: vscode.Uri, fileName: string, rule: PairingRule): { + headerPath: vscode.Uri; + sourcePath: vscode.Uri; + } { + return { + headerPath: vscode.Uri.file( + path.join(targetDirectory.fsPath, `${fileName}${rule.headerExt}`)), + sourcePath: vscode.Uri.file( + path.join(targetDirectory.fsPath, `${fileName}${rule.sourceExt}`)) + }; + } // Optimized file existence check with caching to improve performance private static async fileExists(filePath: string): Promise { @@ -35,14 +55,15 @@ export class PairCreatorService { return this.fileStatCache.get(filePath)!; } - const promise = - Promise.resolve(vscode.workspace.fs.stat(vscode.Uri.file(filePath)) - .then(() => true, () => false)); + const promise = Promise.resolve( + vscode.workspace.fs.stat(vscode.Uri.file(filePath)) + .then(() => true, () => false) + ); this.fileStatCache.set(filePath, promise); - // Auto-clear cache entry after 5 seconds to prevent memory leaks - setTimeout(() => this.fileStatCache.delete(filePath), 5000); + // Auto-clear cache entry after TTL to prevent memory leaks + setTimeout(() => this.fileStatCache.delete(filePath), this.CACHE_TTL); return promise; } diff --git a/src/create-source-header-pair/ui.ts b/src/create-source-header-pair/ui.ts index e8e3dd44..e6773049 100644 --- a/src/create-source-header-pair/ui.ts +++ b/src/create-source-header-pair/ui.ts @@ -36,32 +36,6 @@ export class PairCreatorUI { return this.service.getDefaultPlaceholder(rule); } - // Gets target directory with UI fallback for multiple workspace folders - public async getTargetDirectory(): Promise { - const activeEditor = vscode.window.activeTextEditor; - const activeDocumentPath = - activeEditor?.document && !activeEditor.document.isUntitled - ? activeEditor.document.uri.fsPath - : undefined; - const workspaceFolders = vscode.workspace.workspaceFolders; - - // Try service layer first - const result = await this.service.getTargetDirectory(activeDocumentPath, - workspaceFolders); - if (result) { - return result; - } - - // Handle multiple workspace folders with UI - if (workspaceFolders && workspaceFolders.length > 1) { - const selected = await vscode.window.showWorkspaceFolderPick( - { placeHolder: 'Select workspace folder for new files' }); - return selected?.uri; - } - - return undefined; - } - // Checks if language mismatch warning should be shown with UI context private async shouldShowLanguageMismatchWarning(language: Language, result: PairingRule): @@ -80,13 +54,6 @@ export class PairCreatorUI { language, result, currentDir, activeFilePath); } - // Detects programming language by delegating to service layer - // UI layer should not contain business logic, only call service methods - public async detectLanguage(): - Promise<{ language: Language, uncertain: boolean }> { - return this.service.detectLanguageFromEditor(); - } - // Adapts template rules for display in UI based on custom extensions private adaptRuleForDisplay(rule: PairingRule): PairingRule { if (rule.language !== 'cpp') { @@ -395,29 +362,75 @@ export class PairCreatorUI { `Custom pairing rule saved to ${locationText} settings.`); return customRule; - } catch (error: any) { + } catch (error: unknown) { + const errorMessage = error instanceof Error ? error.message : 'Unknown error occurred'; vscode.window.showErrorMessage( - `Failed to save custom rule: ${error.message}`); + `Failed to save custom rule: ${errorMessage}`); return undefined; } } // Prompts the user to select a pairing rule from available options - // First checks for custom rules, then falls back to default templates + // IMPROVED FLOW: + // 1. Check for existing custom rules first + // 2. If no custom rules, show template choice (C/C++ types) + // 3. If C++ selected, then choose extensions + // 4. After successful creation, offer to save as default // Returns selected pairing rule or undefined if cancelled public async promptForPairingRule(language: 'c' | 'cpp', uncertain: boolean): Promise { - const customRulesResult = - await this.checkAndOfferCustomRules(language, uncertain); - if (customRulesResult === null) - return undefined; - if (customRulesResult === 'use_default') { - // Continue to default template selection - } else if (customRulesResult) { - return customRulesResult; + // First check if there are existing custom rules + if (language === 'cpp') { + const allRules = this.service.getAllPairingRules(); + const languageRules = allRules.filter((rule: PairingRule) => rule.language === language); + + if (languageRules.length > 0) { + // Use existing flow for custom rules + const result = await this.selectFromCustomRules(allRules, language); + if (result === null || result === undefined) return undefined; // User cancelled + if (result === 'use_default') { + // Continue to default template selection + } else if (result) { + return result; + } + } } + // New improved flow: Choose template type first (C/C++ language and type) + const templateChoice = await this.promptForTemplateTypeFirst(language, uncertain); + if (!templateChoice) return undefined; + + // If C++ template was selected and no custom rules exist, ask for extensions + if (templateChoice.language === 'cpp') { + const allRules = this.service.getAllPairingRules(); + const cppRules = allRules.filter((rule: PairingRule) => rule.language === 'cpp'); + + if (cppRules.length === 0) { + // No custom C++ rules, let user choose extensions + const extensionChoice = await this.promptForFileExtensions(); + if (!extensionChoice) return undefined; + + // Apply the chosen extensions to the template + return { + ...templateChoice, + key: `${templateChoice.key}_custom`, + headerExt: extensionChoice.headerExt, + sourceExt: extensionChoice.sourceExt, + description: templateChoice.description.replace(/\.h\/\.cpp/g, + `${extensionChoice.headerExt}/${extensionChoice.sourceExt}`) + }; + } + } + + return templateChoice; + } + + /** + * First step: Choose template type (language and template kind) + * Shows both C and C++ options regardless of detected language + */ + private async promptForTemplateTypeFirst(language: 'c' | 'cpp', uncertain: boolean): Promise { const choices = this.prepareTemplateChoices(language, uncertain); const result = await vscode.window.showQuickPick(choices, { @@ -445,6 +458,104 @@ export class PairCreatorUI { return result; } + /** + * New improved flow: First choose file extensions, then template type + * This provides better UX by letting users see their choice immediately + */ + private async promptForExtensionsAndTemplate(language: 'cpp'): Promise { + // Step 1: Choose file extensions + const extensionChoice = await this.promptForFileExtensions(); + if (!extensionChoice) return undefined; + + // Step 2: Choose template type with the selected extensions + const templateChoice = await this.promptForTemplateType(extensionChoice); + return templateChoice; + } + + /** + * Let user choose file extensions (.h/.cpp, .hh/.cc, etc.) + */ + private async promptForFileExtensions(): Promise<{ headerExt: string, sourceExt: string } | undefined> { + const extensionOptions = [ + { + label: '$(file-code) .h / .cpp', + description: 'Standard C++ extensions (most common)', + detail: 'Widely used, compatible with most tools and IDEs', + headerExt: '.h', + sourceExt: '.cpp' + }, + { + label: '$(file-code) .hh / .cc', + description: 'Alternative C++ extensions', + detail: 'Used by Google style guide and some projects', + headerExt: '.hh', + sourceExt: '.cc' + }, + { + label: '$(file-code) .hpp / .cpp', + description: 'Header Plus Plus style', + detail: 'Explicitly indicates C++ headers', + headerExt: '.hpp', + sourceExt: '.cpp' + }, + { + label: '$(file-code) .hxx / .cxx', + description: 'Extended C++ extensions', + detail: 'Less common but explicit C++ indicator', + headerExt: '.hxx', + sourceExt: '.cxx' + } + ]; + + const selected = await vscode.window.showQuickPick(extensionOptions, { + placeHolder: 'Choose file extensions for your C++ files', + title: 'Step 1 of 2: Select File Extensions', + matchOnDescription: true, + matchOnDetail: true + }); + + return selected ? { headerExt: selected.headerExt, sourceExt: selected.sourceExt } : undefined; + } + + /** + * Let user choose template type (Class, Struct, Empty) with selected extensions + */ + private async promptForTemplateType(extensions: { headerExt: string, sourceExt: string }): Promise { + const templateOptions: PairingRule[] = [ + { + key: 'cpp_empty_custom', + label: `$(new-file) C++ Pair`, + description: `Creates ${extensions.headerExt}/${extensions.sourceExt} with header guards`, + language: 'cpp', + headerExt: extensions.headerExt, + sourceExt: extensions.sourceExt + }, + { + key: 'cpp_class_custom', + label: `$(symbol-class) C++ Class`, + description: `Creates ${extensions.headerExt}/${extensions.sourceExt} with class template`, + language: 'cpp', + headerExt: extensions.headerExt, + sourceExt: extensions.sourceExt, + isClass: true + }, + { + key: 'cpp_struct_custom', + label: `$(symbol-struct) C++ Struct`, + description: `Creates ${extensions.headerExt}/${extensions.sourceExt} with struct template`, + language: 'cpp', + headerExt: extensions.headerExt, + sourceExt: extensions.sourceExt, + isStruct: true + } + ]; + + return vscode.window.showQuickPick(templateOptions, { + placeHolder: `Choose template type for ${extensions.headerExt}/${extensions.sourceExt} files`, + title: 'Step 2 of 2: Select Template Type' + }); + } + // Prompts the user to enter a name for the new file pair // Validates input as a valid C/C++ identifier and provides context-appropriate prompts // Returns the entered file name or undefined if cancelled @@ -468,9 +579,25 @@ export class PairCreatorUI { // Shows success message and opens the newly created header file public async showSuccessAndOpenFile(headerPath: vscode.Uri, sourcePath: vscode.Uri): Promise { - await vscode.window.showTextDocument( - await vscode.workspace.openTextDocument(headerPath)); - await vscode.window.showInformationMessage( - `Successfully created ${path.basename(headerPath.fsPath)} and ${path.basename(sourcePath.fsPath)}.`); + try { + const document = await vscode.workspace.openTextDocument(headerPath); + + // Use setTimeout to make this non-blocking and avoid hanging + setTimeout(async () => { + try { + await vscode.window.showTextDocument(document); + } catch (error) { + // Silently handle file opening errors + } + }, 100); + + // Make success message non-blocking by not awaiting it + vscode.window.showInformationMessage( + `Successfully created ${path.basename(headerPath.fsPath)} and ${path.basename(sourcePath.fsPath)}.`); + } catch (error) { + // Still show success message even if file opening fails + vscode.window.showInformationMessage( + `Successfully created ${path.basename(headerPath.fsPath)} and ${path.basename(sourcePath.fsPath)}.`); + } } } From e6b97fe206a4d92a369788a66909a89b0bbb1296 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?=E9=95=BF=E6=9C=9F=E7=B4=A0=E9=A3=9F?= Date: Fri, 18 Jul 2025 23:36:51 +0800 Subject: [PATCH 17/34] npm run format --- src/extension.ts | 134 ++++++++++++++++++------------------ src/pairing-rule-manager.ts | 134 +++++++++++++++++++----------------- 2 files changed, 136 insertions(+), 132 deletions(-) diff --git a/src/extension.ts b/src/extension.ts index 1a7273f8..e982659a 100644 --- a/src/extension.ts +++ b/src/extension.ts @@ -1,76 +1,76 @@ import * as vscode from 'vscode'; -import { ClangdExtension } from '../api/vscode-clangd'; +import {ClangdExtension} from '../api/vscode-clangd'; -import { ClangdExtensionImpl } from './api'; -import { ClangdContext } from './clangd-context'; -import { get, update } from './config'; +import {ClangdExtensionImpl} from './api'; +import {ClangdContext} from './clangd-context'; +import {get, update} from './config'; import { registerCreateSourceHeaderPairCommand } from './create-source-header-pair/index'; -import { showConfigurationWizard } from './pairing-rule-manager'; +import {showConfigurationWizard} from './pairing-rule-manager'; -let apiInstance: ClangdExtensionImpl | undefined; +let apiInstance: ClangdExtensionImpl|undefined; /** * This method is called when the extension is activated. The extension is * activated the very first time a command is executed. */ export async function activate(context: vscode.ExtensionContext): - Promise { + Promise { const outputChannel = vscode.window.createOutputChannel('clangd'); context.subscriptions.push(outputChannel); - let clangdContext: ClangdContext | null = null; + let clangdContext: ClangdContext|null = null; // An empty place holder for the activate command, otherwise we'll get an // "command is not registered" error. context.subscriptions.push( - vscode.commands.registerCommand('clangd.activate', async () => { })); + vscode.commands.registerCommand('clangd.activate', async () => {})); context.subscriptions.push( - vscode.commands.registerCommand('clangd.restart', async () => { - if (!get('enable')) { - vscode.window - .showInformationMessage( - 'Language features from Clangd are currently disabled. Would you like to enable them?', - 'Enable', 'Close') - .then(async (choice) => { - if (choice === 'Enable') { - await update('enable', true); - vscode.commands.executeCommand('clangd.restart'); - } - }); - return; - } + vscode.commands.registerCommand('clangd.restart', async () => { + if (!get('enable')) { + vscode.window + .showInformationMessage( + 'Language features from Clangd are currently disabled. Would you like to enable them?', + 'Enable', 'Close') + .then(async (choice) => { + if (choice === 'Enable') { + await update('enable', true); + vscode.commands.executeCommand('clangd.restart'); + } + }); + return; + } - // clangd.restart can be called when the extension is not yet activated. - // In such a case, vscode will activate the extension and then run this - // handler. Detect this situation and bail out (doing an extra - // stop/start cycle in this situation is pointless, and doesn't work - // anyways because the client can't be stop()-ped when it's still in the - // Starting state). - if (clangdContext && clangdContext.clientIsStarting()) { - return; - } - if (clangdContext) - clangdContext.dispose(); - clangdContext = await ClangdContext.create(context.globalStoragePath, - outputChannel); - if (clangdContext) { - context.subscriptions.push(clangdContext); + // clangd.restart can be called when the extension is not yet activated. + // In such a case, vscode will activate the extension and then run this + // handler. Detect this situation and bail out (doing an extra + // stop/start cycle in this situation is pointless, and doesn't work + // anyways because the client can't be stop()-ped when it's still in the + // Starting state). + if (clangdContext && clangdContext.clientIsStarting()) { + return; + } + if (clangdContext) + clangdContext.dispose(); + clangdContext = await ClangdContext.create(context.globalStoragePath, + outputChannel); + if (clangdContext) { + context.subscriptions.push(clangdContext); - registerCreateSourceHeaderPairCommand(clangdContext); - } - if (apiInstance) { - apiInstance.client = clangdContext?.client; - } - })); + registerCreateSourceHeaderPairCommand(clangdContext); + } + if (apiInstance) { + apiInstance.client = clangdContext?.client; + } + })); let shouldCheck = false; if (vscode.workspace.getConfiguration('clangd').get('enable')) { clangdContext = - await ClangdContext.create(context.globalStoragePath, outputChannel); + await ClangdContext.create(context.globalStoragePath, outputChannel); if (clangdContext) { context.subscriptions.push(clangdContext); @@ -78,37 +78,37 @@ export async function activate(context: vscode.ExtensionContext): } shouldCheck = vscode.workspace.getConfiguration('clangd').get( - 'detectExtensionConflicts') ?? - false; + 'detectExtensionConflicts') ?? + false; } if (shouldCheck) { - const interval = setInterval(function () { + const interval = setInterval(function() { const cppTools = vscode.extensions.getExtension('ms-vscode.cpptools'); if (cppTools && cppTools.isActive) { const cppToolsConfiguration = - vscode.workspace.getConfiguration('C_Cpp'); + vscode.workspace.getConfiguration('C_Cpp'); const cppToolsEnabled = - cppToolsConfiguration.get('intelliSenseEngine'); + cppToolsConfiguration.get('intelliSenseEngine'); if (cppToolsEnabled?.toLowerCase() !== 'disabled') { vscode.window - .showWarningMessage( - 'You have both the Microsoft C++ (cpptools) extension and ' + - 'clangd extension enabled. The Microsoft IntelliSense features ' + - 'conflict with clangd\'s code completion, diagnostics etc.', - 'Disable IntelliSense', 'Never show this warning') - .then(selection => { - if (selection == 'Disable IntelliSense') { - cppToolsConfiguration.update( - 'intelliSenseEngine', 'disabled', - vscode.ConfigurationTarget.Global); - } else if (selection == 'Never show this warning') { - vscode.workspace.getConfiguration('clangd').update( - 'detectExtensionConflicts', false, - vscode.ConfigurationTarget.Global); - clearInterval(interval); - } - }); + .showWarningMessage( + 'You have both the Microsoft C++ (cpptools) extension and ' + + 'clangd extension enabled. The Microsoft IntelliSense features ' + + 'conflict with clangd\'s code completion, diagnostics etc.', + 'Disable IntelliSense', 'Never show this warning') + .then(selection => { + if (selection == 'Disable IntelliSense') { + cppToolsConfiguration.update( + 'intelliSenseEngine', 'disabled', + vscode.ConfigurationTarget.Global); + } else if (selection == 'Never show this warning') { + vscode.workspace.getConfiguration('clangd').update( + 'detectExtensionConflicts', false, + vscode.ConfigurationTarget.Global); + clearInterval(interval); + } + }); } } }, 5000); diff --git a/src/pairing-rule-manager.ts b/src/pairing-rule-manager.ts index 657e7edd..93224925 100644 --- a/src/pairing-rule-manager.ts +++ b/src/pairing-rule-manager.ts @@ -5,7 +5,7 @@ export interface PairingRule { key: string; label: string; description: string; - language: 'c' | 'cpp'; + language: 'c'|'cpp'; headerExt: string; sourceExt: string; isClass?: boolean; @@ -13,8 +13,8 @@ export interface PairingRule { } // Type aliases for QuickPick items -type RuleQuickPickItem = vscode.QuickPickItem & { rule: PairingRule }; -type ActionQuickPickItem = vscode.QuickPickItem & { key: string }; +type RuleQuickPickItem = vscode.QuickPickItem&{rule: PairingRule}; +type ActionQuickPickItem = vscode.QuickPickItem&{key: string}; // Configuration management service class export class PairingRuleService { @@ -29,8 +29,9 @@ export class PairingRuleService { // Show error message and re-throw the error for proper error handling private static handleError(error: unknown, operation: string, - scope: string): never { - const message = `Failed to ${operation} pairing rules for ${scope}: ${error instanceof Error ? error.message : 'Unknown error'}`; + scope: string): never { + const message = `Failed to ${operation} pairing rules for ${scope}: ${ + error instanceof Error ? error.message : 'Unknown error'}`; vscode.window.showErrorMessage(message); throw error; } @@ -38,54 +39,54 @@ export class PairingRuleService { // Get the currently active pairing rules from configuration public static getActiveRules(): ReadonlyArray { return vscode.workspace.getConfiguration('clangd').get( - PairingRuleService.CONFIG_KEY, []); + PairingRuleService.CONFIG_KEY, []); } // Check if custom rules exist for the specified scope (workspace or user) - public static hasCustomRules(scope: 'workspace' | 'user'): boolean { + public static hasCustomRules(scope: 'workspace'|'user'): boolean { const inspection = - vscode.workspace.getConfiguration('clangd').inspect( - PairingRuleService.CONFIG_KEY); + vscode.workspace.getConfiguration('clangd').inspect( + PairingRuleService.CONFIG_KEY); const value = scope === 'workspace' ? inspection?.workspaceValue - : inspection?.globalValue; + : inspection?.globalValue; return Array.isArray(value); } // Get pairing rules for a specific scope (workspace or user) - public static getRules(scope: 'workspace' | 'user'): PairingRule[] | undefined { + public static getRules(scope: 'workspace'|'user'): PairingRule[]|undefined { const inspection = - vscode.workspace.getConfiguration('clangd').inspect( - PairingRuleService.CONFIG_KEY); + vscode.workspace.getConfiguration('clangd').inspect( + PairingRuleService.CONFIG_KEY); return scope === 'workspace' ? inspection?.workspaceValue - : inspection?.globalValue; + : inspection?.globalValue; } // Write pairing rules to the specified scope (workspace or user) public static async writeRules(rules: PairingRule[], - scope: 'workspace' | 'user'): Promise { + scope: 'workspace'|'user'): Promise { try { if (!Array.isArray(rules)) throw new Error('Rules must be an array'); rules.forEach(PairingRuleService.validateRule); const target = scope === 'workspace' - ? vscode.ConfigurationTarget.Workspace - : vscode.ConfigurationTarget.Global; + ? vscode.ConfigurationTarget.Workspace + : vscode.ConfigurationTarget.Global; await vscode.workspace.getConfiguration('clangd').update( - PairingRuleService.CONFIG_KEY, rules, target); + PairingRuleService.CONFIG_KEY, rules, target); } catch (error) { PairingRuleService.handleError(error, 'save', scope); } } // Reset pairing rules for the specified scope (remove custom rules) - public static async resetRules(scope: 'workspace' | 'user'): Promise { + public static async resetRules(scope: 'workspace'|'user'): Promise { try { const target = scope === 'workspace' - ? vscode.ConfigurationTarget.Workspace - : vscode.ConfigurationTarget.Global; + ? vscode.ConfigurationTarget.Workspace + : vscode.ConfigurationTarget.Global; await vscode.workspace.getConfiguration('clangd').update( - PairingRuleService.CONFIG_KEY, undefined, target); + PairingRuleService.CONFIG_KEY, undefined, target); } catch (error) { PairingRuleService.handleError(error, 'reset', scope); } @@ -129,18 +130,20 @@ export class PairingRuleUI { // Create rule choices from extension options for QuickPick display private static createRuleChoices(): RuleQuickPickItem[] { return PairingRuleUI.EXTENSION_OPTIONS.map( - (option, index) => ({ - label: `$(file-code) ${option.label}`, - description: option.description, - rule: { - key: `custom_${option.language}_${index}`, - label: `${option.language.toUpperCase()} Pair (${option.headerExt}/${option.sourceExt})`, - description: `Creates a ${option.headerExt}/${option.sourceExt} file pair with header guards.`, - language: option.language, - headerExt: option.headerExt, - sourceExt: option.sourceExt, - }, - })); + (option, index) => ({ + label: `$(file-code) ${option.label}`, + description: option.description, + rule: { + key: `custom_${option.language}_${index}`, + label: `${option.language.toUpperCase()} Pair (${ + option.headerExt}/${option.sourceExt})`, + description: `Creates a ${option.headerExt}/${ + option.sourceExt} file pair with header guards.`, + language: option.language, + headerExt: option.headerExt, + sourceExt: option.sourceExt, + }, + })); } // Create advanced options separator and menu item for advanced management @@ -182,11 +185,11 @@ export class PairingRuleUI { kind: vscode.QuickPickItemKind.Separator, key: 'separator_global', }, - { - label: '$(edit) Edit Global Rules...', - description: 'Opens your global settings.json', - key: 'edit_global', - }); + { + label: '$(edit) Edit Global Rules...', + description: 'Opens your global settings.json', + key: 'edit_global', + }); if (PairingRuleService.hasCustomRules('user')) { items.push({ @@ -202,47 +205,48 @@ export class PairingRuleUI { // Handle rule selection and ask for save scope (workspace or global) private static async handleRuleSelection(rule: PairingRule): Promise { const selection = await vscode.window.showQuickPick( - [ - { - label: 'Save for this Workspace', - description: 'Recommended. Creates a .vscode/settings.json file.', - scope: 'workspace', - }, + [ + { + label: 'Save for this Workspace', + description: 'Recommended. Creates a .vscode/settings.json file.', + scope: 'workspace', + }, + { + label: 'Save for all my Projects (Global)', + description: 'Modifies your global user settings.', + scope: 'user', + }, + ], { - label: 'Save for all my Projects (Global)', - description: 'Modifies your global user settings.', - scope: 'user', - }, - ], - { - placeHolder: 'Where would you like to save this rule?', - title: 'Save Configuration Scope', - }); + placeHolder: 'Where would you like to save this rule?', + title: 'Save Configuration Scope', + }); if (!selection) return; await PairingRuleService.writeRules([rule], selection.scope as 'workspace' | - 'user'); - vscode.window.showInformationMessage(`Successfully set '${rule.label}' as the default extension for the ${selection.scope}.`); + 'user'); + vscode.window.showInformationMessage(`Successfully set '${ + rule.label}' as the default extension for the ${selection.scope}.`); } // Handle advanced menu selection and execute the corresponding action private static async handleAdvancedMenuSelection(key: string): Promise { const actions = { edit_workspace: () => vscode.commands.executeCommand( - 'workbench.action.openWorkspaceSettingsFile'), + 'workbench.action.openWorkspaceSettingsFile'), edit_global: () => - vscode.commands.executeCommand('workbench.action.openSettingsJson'), + vscode.commands.executeCommand('workbench.action.openSettingsJson'), reset_workspace: async () => { await PairingRuleService.resetRules('workspace'); vscode.window.showInformationMessage( - 'Workspace pairing rules have been reset.'); + 'Workspace pairing rules have been reset.'); }, reset_global: async () => { await PairingRuleService.resetRules('user'); vscode.window.showInformationMessage( - 'Global pairing rules have been reset.'); + 'Global pairing rules have been reset.'); }, }; @@ -254,10 +258,10 @@ export class PairingRuleUI { // Main configuration wizard - entry point for setting up pairing rules public static async showConfigurationWizard(): Promise { const quickPick = - vscode.window.createQuickPick(); + vscode.window.createQuickPick(); quickPick.title = 'Quick Setup: Choose File Extensions'; quickPick.placeholder = - 'Choose file extension combination for this workspace, or go to advanced options.'; + 'Choose file extension combination for this workspace, or go to advanced options.'; quickPick.items = [ ...PairingRuleUI.createRuleChoices(), ...PairingRuleUI.createAdvancedOptions() @@ -283,9 +287,9 @@ export class PairingRuleUI { // Advanced management menu for editing and resetting pairing rules public static async showAdvancedManagementMenu(): Promise { const selection = await vscode.window.showQuickPick( - PairingRuleUI.createAdvancedMenuItems(), { - title: 'Advanced Rule Management', - }); + PairingRuleUI.createAdvancedMenuItems(), { + title: 'Advanced Rule Management', + }); if (selection?.key) { await PairingRuleUI.handleAdvancedMenuSelection(selection.key); From 6b4a6c11bac3667815b238d7ee82c70b2f203f49 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?=E9=95=BF=E6=9C=9F=E7=B4=A0=E9=A3=9F?= Date: Sat, 19 Jul 2025 01:22:05 +0800 Subject: [PATCH 18/34] Refactor coordinator and UI for dependency injection Refactors the PairCoordinator to use dependency injection for service and UI layers, simplifying the workflow and improving testability. Moves file creation and rule-saving logic into the service layer, and moves workspace folder picking into the UI layer. Updates the registration logic to construct dependencies explicitly. Also, centralizes file extension selection UI in PairingRuleUI and cleans up related code. --- src/create-source-header-pair/coordinator.ts | 210 ++++--------------- src/create-source-header-pair/index.ts | 11 +- src/create-source-header-pair/service.ts | 94 +++++++++ src/create-source-header-pair/ui.ts | 113 ++-------- src/pairing-rule-manager.ts | 166 ++++++++------- 5 files changed, 255 insertions(+), 339 deletions(-) diff --git a/src/create-source-header-pair/coordinator.ts b/src/create-source-header-pair/coordinator.ts index 6a012c4c..e2d5e80c 100644 --- a/src/create-source-header-pair/coordinator.ts +++ b/src/create-source-header-pair/coordinator.ts @@ -2,20 +2,18 @@ // PAIR COORDINATOR // ================ // -// Main coordinator class that orchestrates the entire source/header pair -// creation workflow. Acts as the main entry point and manages the -// interaction between service and UI layers. -// +// Lean coordinator that orchestrates the source/header pair creation workflow. +// Uses dependency injection and delegates all implementation details to +// Service and UI layers. The create() method reads like a clear story. import * as vscode from 'vscode'; -import { PairingRule, showConfigurationWizard } from '../pairing-rule-manager'; +import { showConfigurationWizard } from '../pairing-rule-manager'; import { PairCreatorService } from './service'; import { PairCreatorUI } from './ui'; -// PairCoordinator coordinates the UI and Service layers to handle the complete -// file pair creation workflow. It serves as the main entry point and -// orchestrates the entire process. +// PairCoordinator orchestrates the workflow between UI and Service layers. +// It follows the single responsibility principle and uses dependency injection. export class PairCoordinator implements vscode.Disposable { private static readonly ERROR_MESSAGES = { NO_TARGET_DIRECTORY: 'Cannot determine target directory. Please open a folder or a file first.', @@ -25,20 +23,15 @@ export class PairCoordinator implements vscode.Disposable { private newPairCommand: vscode.Disposable; private configureRulesCommand: vscode.Disposable; - private service: PairCreatorService; - private ui: PairCreatorUI; - - // Constructor registers the VS Code commands for creating source/header pairs - // and configuring rules - constructor() { - this.service = new PairCreatorService(); - this.ui = new PairCreatorUI(this.service); - // Register the main command for creating new source/header pairs + // Constructor with dependency injection - receives pre-configured instances + constructor( + private readonly service: PairCreatorService, + private readonly ui: PairCreatorUI + ) { + // Register commands this.newPairCommand = vscode.commands.registerCommand( 'clangd.newSourcePair', this.create, this); - - // Register the command for configuring pairing rules this.configureRulesCommand = vscode.commands.registerCommand( 'clangd.newSourcePair.configureRules', this.configureRules, this); } @@ -49,173 +42,54 @@ export class PairCoordinator implements vscode.Disposable { this.configureRulesCommand.dispose(); } - // Main entry point for the file pair creation process - // Orchestrates the entire workflow using the service and UI layers + // Main workflow orchestration - reads like a clear story public async create(): Promise { try { - // 1. Get target directory - const targetDirectory = await this.service.getTargetDirectory( - vscode.window.activeTextEditor?.document?.uri.fsPath, - vscode.workspace.workspaceFolders) ?? - await this.showWorkspaceFolderPicker(); - + // 1. Determine where to create files + const targetDirectory = await this.getTargetDirectory(); if (!targetDirectory) { vscode.window.showErrorMessage(PairCoordinator.ERROR_MESSAGES.NO_TARGET_DIRECTORY); return; } - await this.createPairInDirectory(targetDirectory); - - } catch (error: unknown) { - const errorMessage = error instanceof Error ? error.message : 'An unexpected error occurred.'; - vscode.window.showErrorMessage(errorMessage); - } - } - - // Show workspace folder picker when multiple folders are available - private async showWorkspaceFolderPicker(): Promise { - const workspaceFolders = vscode.workspace.workspaceFolders; - if (!workspaceFolders || workspaceFolders.length <= 1) { - return undefined; - } - - const selected = await vscode.window.showQuickPick( - workspaceFolders.map(folder => ({ - label: folder.name, - description: folder.uri.fsPath, - folder: folder - })), - { placeHolder: 'Select workspace folder for new files' } - ); - - return selected?.folder.uri; - } - - // Create file pair in the specified directory - private async createPairInDirectory(targetDirectory: vscode.Uri): Promise { - // 2. Detect language and get user inputs - const { language, uncertain } = await this.service.detectLanguageFromEditor(); - const rule = await this.ui.promptForPairingRule(language, uncertain); - if (!rule) return; - - const fileName = await this.ui.promptForFileName(rule); - if (!fileName) return; - - // 3. Create and validate file paths - const { headerPath, sourcePath } = this.service.createFilePaths(targetDirectory, fileName, rule); - - const existingFilePath = await this.service.checkFileExistence(headerPath, sourcePath); - if (existingFilePath) { - vscode.window.showErrorMessage( - PairCoordinator.ERROR_MESSAGES.FILE_EXISTS(existingFilePath)); - return; - } - - // 4. Generate and write files - await this.generateAndWriteFiles(fileName, rule, headerPath, sourcePath); + // 2. Get user preferences for what to create + const { language, uncertain } = await this.service.detectLanguageFromEditor(); + const rule = await this.ui.promptForPairingRule(language, uncertain); + if (!rule) return; - // 5. Show success and open files - await this.ui.showSuccessAndOpenFile(headerPath, sourcePath); + const fileName = await this.ui.promptForFileName(rule); + if (!fileName) return; - // 6. Offer to save as default if it's a custom rule without existing config - await this.offerToSaveAsDefault(rule, language); - } - - /** - * Offers to save the rule as default configuration after successful file creation - * Only shows this for C++ rules when no existing configuration exists - */ - private async offerToSaveAsDefault(rule: PairingRule, language: 'c' | 'cpp'): Promise { - // Only offer for C++ rules - if (language !== 'cpp') { - return; - } - - // Check if user already has custom C++ rules configured - const customRules = this.service.getAllPairingRules(); - const hasCppCustomRules = customRules.some(r => r.language === 'cpp'); - if (hasCppCustomRules) { - return; // Don't prompt if they already have C++ configuration - } - - // Check if this is a custom rule (has _custom suffix means user went through extension selection) - const isCustomRule = rule.key.includes('custom'); - if (!isCustomRule) { - return; // Don't prompt for built-in rules - } - - const choice = await vscode.window.showInformationMessage( - `Files created successfully! Would you like to save "${rule.headerExt}/${rule.sourceExt}" as your default C++ extensions for this workspace?`, - 'Save as Default', - 'Not Now' - ); - - if (choice === 'Save as Default') { - await this.saveRuleAsDefault(rule); - } - } - - //Saves a rule as the default configuration with user choice of scope - private async saveRuleAsDefault(rule: PairingRule): Promise { - const { PairingRuleService } = await import('../pairing-rule-manager'); - - const scopeChoice = await vscode.window.showQuickPick([ - { - label: 'Save for this Workspace', - description: 'Recommended. Creates a .vscode/settings.json file.', - scope: 'workspace' - }, - { - label: 'Save for all my Projects (Global)', - description: 'Modifies your global user settings.', - scope: 'user' + // 3. Prepare file paths and check for conflicts + const { headerPath, sourcePath } = this.service.createFilePaths(targetDirectory, fileName, rule); + const existingFilePath = await this.service.checkFileExistence(headerPath, sourcePath); + if (existingFilePath) { + vscode.window.showErrorMessage(PairCoordinator.ERROR_MESSAGES.FILE_EXISTS(existingFilePath)); + return; } - ], { - placeHolder: 'Where would you like to save this configuration?', - title: 'Save Configuration Scope' - }); - if (!scopeChoice) return; + // 4. Create the files + await this.service.generateAndWriteFiles(fileName, rule, headerPath, sourcePath); - try { - // Create a clean rule for saving (remove the 'custom' key suffix) - const cleanRule: PairingRule = { - ...rule, - key: rule.key.replace('_custom', ''), - label: `${rule.language.toUpperCase()} Pair (${rule.headerExt}/${rule.sourceExt})`, - description: `Creates a ${rule.headerExt}/${rule.sourceExt} file pair with header guards.` - }; + // 5. Show success and handle post-creation tasks + await this.ui.showSuccessAndOpenFile(headerPath, sourcePath); + await this.service.handleOfferToSaveAsDefault(rule, language); - await PairingRuleService.writeRules([cleanRule], scopeChoice.scope as 'workspace' | 'user'); - - vscode.window.showInformationMessage( - `Successfully saved '${rule.headerExt}/${rule.sourceExt}' as the default extension for ${scopeChoice.scope === 'workspace' ? 'this workspace' : 'all projects'}.` - ); - } catch (error) { - vscode.window.showErrorMessage( - `Failed to save configuration: ${error instanceof Error ? error.message : 'Unknown error'}` - ); + } catch (error: unknown) { + const errorMessage = error instanceof Error ? error.message : PairCoordinator.ERROR_MESSAGES.UNEXPECTED_ERROR; + vscode.window.showErrorMessage(errorMessage); } } - /** - * Generates file content and writes both header and source files - */ - private async generateAndWriteFiles( - fileName: string, - rule: PairingRule, - headerPath: vscode.Uri, - sourcePath: vscode.Uri - ): Promise { - const eol = this.service.getLineEnding(); - const { headerContent, sourceContent } = - this.service.generateFileContent(fileName, eol, rule); - - await this.service.writeFiles(headerPath, sourcePath, headerContent, sourceContent); + // Determine target directory with fallback to workspace picker + private async getTargetDirectory(): Promise { + return await this.service.getTargetDirectory( + vscode.window.activeTextEditor?.document?.uri.fsPath, + vscode.workspace.workspaceFolders + ) ?? await this.ui.showWorkspaceFolderPicker(); } - // Opens the configuration wizard for source/header pairing rules - // Delegates to the PairingRuleManager's configuration interface + // Opens the configuration wizard for pairing rules public async configureRules(): Promise { await showConfigurationWizard(); } diff --git a/src/create-source-header-pair/index.ts b/src/create-source-header-pair/index.ts index 9ad1ba6e..5da3477d 100644 --- a/src/create-source-header-pair/index.ts +++ b/src/create-source-header-pair/index.ts @@ -33,11 +33,18 @@ import { ClangdContext } from '../clangd-context'; import { PairCoordinator } from './coordinator'; +import { PairCreatorService } from './service'; +import { PairCreatorUI } from './ui'; // Registers the create source/header pair command with the VS Code extension context -// This function should be called during extension activation to make the command available +// Uses dependency injection to create properly configured instances export function registerCreateSourceHeaderPairCommand(context: ClangdContext) { - context.subscriptions.push(new PairCoordinator()); + // Create instances with proper dependencies + const service = new PairCreatorService(); + const ui = new PairCreatorUI(service); + const coordinator = new PairCoordinator(service, ui); + + context.subscriptions.push(coordinator); } // Re-export main types and classes for external usage diff --git a/src/create-source-header-pair/service.ts b/src/create-source-header-pair/service.ts index 8a8a7fe3..5a567ab9 100644 --- a/src/create-source-header-pair/service.ts +++ b/src/create-source-header-pair/service.ts @@ -350,4 +350,98 @@ export class PairCreatorService { return true; // Show warning if can't check } } + + /** + * Checks if should offer to save rule as default and handles the save process + */ + public async handleOfferToSaveAsDefault(rule: PairingRule, language: 'c' | 'cpp'): Promise { + // Only offer for C++ rules + if (language !== 'cpp') { + return; + } + + // Check if user already has custom C++ rules configured + const customRules = this.getAllPairingRules(); + const hasCppCustomRules = customRules.some(r => r.language === 'cpp'); + if (hasCppCustomRules) { + return; // Don't prompt if they already have C++ configuration + } + + // Check if this is a custom rule (has _custom suffix means user went through extension selection) + const isCustomRule = rule.key.includes('custom'); + if (!isCustomRule) { + return; // Don't prompt for built-in rules + } + + const choice = await vscode.window.showInformationMessage( + `Files created successfully! Would you like to save "${rule.headerExt}/${rule.sourceExt}" as your default C++ extensions for this workspace?`, + 'Save as Default', + 'Not Now' + ); + + if (choice === 'Save as Default') { + await this.saveRuleAsDefault(rule); + } + } + + /** + * Saves a rule as the default configuration with user choice of scope + */ + public async saveRuleAsDefault(rule: PairingRule): Promise { + const { PairingRuleService } = await import('../pairing-rule-manager'); + + const scopeChoice = await vscode.window.showQuickPick([ + { + label: 'Save for this Workspace', + description: 'Recommended. Creates a .vscode/settings.json file.', + scope: 'workspace' + }, + { + label: 'Save for all my Projects (Global)', + description: 'Modifies your global user settings.', + scope: 'user' + } + ], { + placeHolder: 'Where would you like to save this configuration?', + title: 'Save Configuration Scope' + }); + + if (!scopeChoice) { + return; + } + + try { + // Create a clean rule for saving (remove the 'custom' key suffix) + const cleanRule: PairingRule = { + ...rule, + key: rule.key.replace('_custom', ''), + label: `${rule.language.toUpperCase()} Pair (${rule.headerExt}/${rule.sourceExt})`, + description: `Creates a ${rule.headerExt}/${rule.sourceExt} file pair with header guards.` + }; + + await PairingRuleService.writeRules([cleanRule], scopeChoice.scope as 'workspace' | 'user'); + + vscode.window.showInformationMessage( + `Successfully saved '${rule.headerExt}/${rule.sourceExt}' as the default extension for ${scopeChoice.scope === 'workspace' ? 'this workspace' : 'all projects'}.` + ); + } catch (error) { + vscode.window.showErrorMessage( + `Failed to save configuration: ${error instanceof Error ? error.message : 'Unknown error'}` + ); + } + } + + /** + * Generates file content and writes both header and source files + */ + public async generateAndWriteFiles( + fileName: string, + rule: PairingRule, + headerPath: vscode.Uri, + sourcePath: vscode.Uri + ): Promise { + const eol = this.getLineEnding(); + const { headerContent, sourceContent } = this.generateFileContent(fileName, eol, rule); + await this.writeFiles(headerPath, sourcePath, headerContent, sourceContent); + } } diff --git a/src/create-source-header-pair/ui.ts b/src/create-source-header-pair/ui.ts index e6773049..38a9ec50 100644 --- a/src/create-source-header-pair/ui.ts +++ b/src/create-source-header-pair/ui.ts @@ -10,7 +10,7 @@ import * as path from 'path'; import * as vscode from 'vscode'; -import { PairingRule, PairingRuleService } from '../pairing-rule-manager'; +import { PairingRule, PairingRuleService, PairingRuleUI } from '../pairing-rule-manager'; import { PairCreatorService } from './service'; import { Language, VALIDATION_PATTERNS, TEMPLATE_RULES } from './templates'; @@ -408,7 +408,7 @@ export class PairCreatorUI { if (cppRules.length === 0) { // No custom C++ rules, let user choose extensions - const extensionChoice = await this.promptForFileExtensions(); + const extensionChoice = await PairingRuleUI.promptForFileExtensions(); if (!extensionChoice) return undefined; // Apply the chosen extensions to the template @@ -458,102 +458,23 @@ export class PairCreatorUI { return result; } - /** - * New improved flow: First choose file extensions, then template type - * This provides better UX by letting users see their choice immediately - */ - private async promptForExtensionsAndTemplate(language: 'cpp'): Promise { - // Step 1: Choose file extensions - const extensionChoice = await this.promptForFileExtensions(); - if (!extensionChoice) return undefined; - - // Step 2: Choose template type with the selected extensions - const templateChoice = await this.promptForTemplateType(extensionChoice); - return templateChoice; - } - - /** - * Let user choose file extensions (.h/.cpp, .hh/.cc, etc.) - */ - private async promptForFileExtensions(): Promise<{ headerExt: string, sourceExt: string } | undefined> { - const extensionOptions = [ - { - label: '$(file-code) .h / .cpp', - description: 'Standard C++ extensions (most common)', - detail: 'Widely used, compatible with most tools and IDEs', - headerExt: '.h', - sourceExt: '.cpp' - }, - { - label: '$(file-code) .hh / .cc', - description: 'Alternative C++ extensions', - detail: 'Used by Google style guide and some projects', - headerExt: '.hh', - sourceExt: '.cc' - }, - { - label: '$(file-code) .hpp / .cpp', - description: 'Header Plus Plus style', - detail: 'Explicitly indicates C++ headers', - headerExt: '.hpp', - sourceExt: '.cpp' - }, - { - label: '$(file-code) .hxx / .cxx', - description: 'Extended C++ extensions', - detail: 'Less common but explicit C++ indicator', - headerExt: '.hxx', - sourceExt: '.cxx' - } - ]; - - const selected = await vscode.window.showQuickPick(extensionOptions, { - placeHolder: 'Choose file extensions for your C++ files', - title: 'Step 1 of 2: Select File Extensions', - matchOnDescription: true, - matchOnDetail: true - }); - - return selected ? { headerExt: selected.headerExt, sourceExt: selected.sourceExt } : undefined; - } + // Shows workspace folder picker when multiple folders are available + public async showWorkspaceFolderPicker(): Promise { + const workspaceFolders = vscode.workspace.workspaceFolders; + if (!workspaceFolders || workspaceFolders.length <= 1) { + return undefined; + } - /** - * Let user choose template type (Class, Struct, Empty) with selected extensions - */ - private async promptForTemplateType(extensions: { headerExt: string, sourceExt: string }): Promise { - const templateOptions: PairingRule[] = [ - { - key: 'cpp_empty_custom', - label: `$(new-file) C++ Pair`, - description: `Creates ${extensions.headerExt}/${extensions.sourceExt} with header guards`, - language: 'cpp', - headerExt: extensions.headerExt, - sourceExt: extensions.sourceExt - }, - { - key: 'cpp_class_custom', - label: `$(symbol-class) C++ Class`, - description: `Creates ${extensions.headerExt}/${extensions.sourceExt} with class template`, - language: 'cpp', - headerExt: extensions.headerExt, - sourceExt: extensions.sourceExt, - isClass: true - }, - { - key: 'cpp_struct_custom', - label: `$(symbol-struct) C++ Struct`, - description: `Creates ${extensions.headerExt}/${extensions.sourceExt} with struct template`, - language: 'cpp', - headerExt: extensions.headerExt, - sourceExt: extensions.sourceExt, - isStruct: true - } - ]; + const selected = await vscode.window.showQuickPick( + workspaceFolders.map(folder => ({ + label: folder.name, + description: folder.uri.fsPath, + folder: folder + })), + { placeHolder: 'Select workspace folder for new files' } + ); - return vscode.window.showQuickPick(templateOptions, { - placeHolder: `Choose template type for ${extensions.headerExt}/${extensions.sourceExt} files`, - title: 'Step 2 of 2: Select Template Type' - }); + return selected?.folder.uri; } // Prompts the user to enter a name for the new file pair diff --git a/src/pairing-rule-manager.ts b/src/pairing-rule-manager.ts index 93224925..c3299441 100644 --- a/src/pairing-rule-manager.ts +++ b/src/pairing-rule-manager.ts @@ -5,7 +5,7 @@ export interface PairingRule { key: string; label: string; description: string; - language: 'c'|'cpp'; + language: 'c' | 'cpp'; headerExt: string; sourceExt: string; isClass?: boolean; @@ -13,8 +13,8 @@ export interface PairingRule { } // Type aliases for QuickPick items -type RuleQuickPickItem = vscode.QuickPickItem&{rule: PairingRule}; -type ActionQuickPickItem = vscode.QuickPickItem&{key: string}; +type RuleQuickPickItem = vscode.QuickPickItem & { rule: PairingRule }; +type ActionQuickPickItem = vscode.QuickPickItem & { key: string }; // Configuration management service class export class PairingRuleService { @@ -29,9 +29,8 @@ export class PairingRuleService { // Show error message and re-throw the error for proper error handling private static handleError(error: unknown, operation: string, - scope: string): never { - const message = `Failed to ${operation} pairing rules for ${scope}: ${ - error instanceof Error ? error.message : 'Unknown error'}`; + scope: string): never { + const message = `Failed to ${operation} pairing rules for ${scope}: ${error instanceof Error ? error.message : 'Unknown error'}`; vscode.window.showErrorMessage(message); throw error; } @@ -39,54 +38,54 @@ export class PairingRuleService { // Get the currently active pairing rules from configuration public static getActiveRules(): ReadonlyArray { return vscode.workspace.getConfiguration('clangd').get( - PairingRuleService.CONFIG_KEY, []); + PairingRuleService.CONFIG_KEY, []); } // Check if custom rules exist for the specified scope (workspace or user) - public static hasCustomRules(scope: 'workspace'|'user'): boolean { + public static hasCustomRules(scope: 'workspace' | 'user'): boolean { const inspection = - vscode.workspace.getConfiguration('clangd').inspect( - PairingRuleService.CONFIG_KEY); + vscode.workspace.getConfiguration('clangd').inspect( + PairingRuleService.CONFIG_KEY); const value = scope === 'workspace' ? inspection?.workspaceValue - : inspection?.globalValue; + : inspection?.globalValue; return Array.isArray(value); } // Get pairing rules for a specific scope (workspace or user) - public static getRules(scope: 'workspace'|'user'): PairingRule[]|undefined { + public static getRules(scope: 'workspace' | 'user'): PairingRule[] | undefined { const inspection = - vscode.workspace.getConfiguration('clangd').inspect( - PairingRuleService.CONFIG_KEY); + vscode.workspace.getConfiguration('clangd').inspect( + PairingRuleService.CONFIG_KEY); return scope === 'workspace' ? inspection?.workspaceValue - : inspection?.globalValue; + : inspection?.globalValue; } // Write pairing rules to the specified scope (workspace or user) public static async writeRules(rules: PairingRule[], - scope: 'workspace'|'user'): Promise { + scope: 'workspace' | 'user'): Promise { try { if (!Array.isArray(rules)) throw new Error('Rules must be an array'); rules.forEach(PairingRuleService.validateRule); const target = scope === 'workspace' - ? vscode.ConfigurationTarget.Workspace - : vscode.ConfigurationTarget.Global; + ? vscode.ConfigurationTarget.Workspace + : vscode.ConfigurationTarget.Global; await vscode.workspace.getConfiguration('clangd').update( - PairingRuleService.CONFIG_KEY, rules, target); + PairingRuleService.CONFIG_KEY, rules, target); } catch (error) { PairingRuleService.handleError(error, 'save', scope); } } // Reset pairing rules for the specified scope (remove custom rules) - public static async resetRules(scope: 'workspace'|'user'): Promise { + public static async resetRules(scope: 'workspace' | 'user'): Promise { try { const target = scope === 'workspace' - ? vscode.ConfigurationTarget.Workspace - : vscode.ConfigurationTarget.Global; + ? vscode.ConfigurationTarget.Workspace + : vscode.ConfigurationTarget.Global; await vscode.workspace.getConfiguration('clangd').update( - PairingRuleService.CONFIG_KEY, undefined, target); + PairingRuleService.CONFIG_KEY, undefined, target); } catch (error) { PairingRuleService.handleError(error, 'reset', scope); } @@ -98,29 +97,33 @@ export class PairingRuleUI { // Predefined extension combinations for C++ file pairs private static readonly EXTENSION_OPTIONS = [ { - label: '.h / .cpp', + label: '$(file-code) .h / .cpp', description: 'Standard C++ extensions', + detail: 'Widely used, compatible with most tools and IDEs.', headerExt: '.h', sourceExt: '.cpp', language: 'cpp' as const, }, { - label: '.hh / .cc', + label: '$(file-code) .hh / .cc', description: 'Alternative C++ extensions', + detail: 'Used by Google style guide and some projects.', headerExt: '.hh', sourceExt: '.cc', language: 'cpp' as const, }, { - label: '.hpp / .cpp', + label: '$(file-code) .hpp / .cpp', description: 'Header Plus Plus style', + detail: 'Explicitly indicates C++ headers.', headerExt: '.hpp', sourceExt: '.cpp', language: 'cpp' as const, }, { - label: '.hxx / .cxx', + label: '$(file-code) .hxx / .cxx', description: 'Extended C++ extensions', + detail: 'Less common but explicit C++ indicator.', headerExt: '.hxx', sourceExt: '.cxx', language: 'cpp' as const, @@ -130,20 +133,19 @@ export class PairingRuleUI { // Create rule choices from extension options for QuickPick display private static createRuleChoices(): RuleQuickPickItem[] { return PairingRuleUI.EXTENSION_OPTIONS.map( - (option, index) => ({ - label: `$(file-code) ${option.label}`, - description: option.description, - rule: { - key: `custom_${option.language}_${index}`, - label: `${option.language.toUpperCase()} Pair (${ - option.headerExt}/${option.sourceExt})`, - description: `Creates a ${option.headerExt}/${ - option.sourceExt} file pair with header guards.`, - language: option.language, - headerExt: option.headerExt, - sourceExt: option.sourceExt, - }, - })); + (option, index) => ({ + label: option.label, + description: option.description, + detail: option.detail, + rule: { + key: `custom_${option.language}_${index}`, + label: `${option.language.toUpperCase()} Pair (${option.headerExt}/${option.sourceExt})`, + description: `Creates a ${option.headerExt}/${option.sourceExt} file pair with header guards.`, + language: option.language, + headerExt: option.headerExt, + sourceExt: option.sourceExt, + }, + })); } // Create advanced options separator and menu item for advanced management @@ -185,11 +187,11 @@ export class PairingRuleUI { kind: vscode.QuickPickItemKind.Separator, key: 'separator_global', }, - { - label: '$(edit) Edit Global Rules...', - description: 'Opens your global settings.json', - key: 'edit_global', - }); + { + label: '$(edit) Edit Global Rules...', + description: 'Opens your global settings.json', + key: 'edit_global', + }); if (PairingRuleService.hasCustomRules('user')) { items.push({ @@ -205,48 +207,47 @@ export class PairingRuleUI { // Handle rule selection and ask for save scope (workspace or global) private static async handleRuleSelection(rule: PairingRule): Promise { const selection = await vscode.window.showQuickPick( - [ - { - label: 'Save for this Workspace', - description: 'Recommended. Creates a .vscode/settings.json file.', - scope: 'workspace', - }, - { - label: 'Save for all my Projects (Global)', - description: 'Modifies your global user settings.', - scope: 'user', - }, - ], + [ + { + label: 'Save for this Workspace', + description: 'Recommended. Creates a .vscode/settings.json file.', + scope: 'workspace', + }, { - placeHolder: 'Where would you like to save this rule?', - title: 'Save Configuration Scope', - }); + label: 'Save for all my Projects (Global)', + description: 'Modifies your global user settings.', + scope: 'user', + }, + ], + { + placeHolder: 'Where would you like to save this rule?', + title: 'Save Configuration Scope', + }); if (!selection) return; await PairingRuleService.writeRules([rule], selection.scope as 'workspace' | - 'user'); - vscode.window.showInformationMessage(`Successfully set '${ - rule.label}' as the default extension for the ${selection.scope}.`); + 'user'); + vscode.window.showInformationMessage(`Successfully set '${rule.label}' as the default extension for the ${selection.scope}.`); } // Handle advanced menu selection and execute the corresponding action private static async handleAdvancedMenuSelection(key: string): Promise { const actions = { edit_workspace: () => vscode.commands.executeCommand( - 'workbench.action.openWorkspaceSettingsFile'), + 'workbench.action.openWorkspaceSettingsFile'), edit_global: () => - vscode.commands.executeCommand('workbench.action.openSettingsJson'), + vscode.commands.executeCommand('workbench.action.openSettingsJson'), reset_workspace: async () => { await PairingRuleService.resetRules('workspace'); vscode.window.showInformationMessage( - 'Workspace pairing rules have been reset.'); + 'Workspace pairing rules have been reset.'); }, reset_global: async () => { await PairingRuleService.resetRules('user'); vscode.window.showInformationMessage( - 'Global pairing rules have been reset.'); + 'Global pairing rules have been reset.'); }, }; @@ -255,13 +256,32 @@ export class PairingRuleUI { await action(); } + // Public method to prompt for file extensions only + public static async promptForFileExtensions(): Promise<{ headerExt: string, sourceExt: string } | undefined> { + const selected = await vscode.window.showQuickPick( + PairingRuleUI.EXTENSION_OPTIONS.map(option => ({ + label: option.label, + description: option.description, + detail: option.detail, + headerExt: option.headerExt, + sourceExt: option.sourceExt + })), { + placeHolder: 'Choose file extensions for your C++ files', + title: 'Step 1 of 2: Select File Extensions', + matchOnDescription: true, + matchOnDetail: true + }); + + return selected ? { headerExt: selected.headerExt, sourceExt: selected.sourceExt } : undefined; + } + // Main configuration wizard - entry point for setting up pairing rules public static async showConfigurationWizard(): Promise { const quickPick = - vscode.window.createQuickPick(); + vscode.window.createQuickPick(); quickPick.title = 'Quick Setup: Choose File Extensions'; quickPick.placeholder = - 'Choose file extension combination for this workspace, or go to advanced options.'; + 'Choose file extension combination for this workspace, or go to advanced options.'; quickPick.items = [ ...PairingRuleUI.createRuleChoices(), ...PairingRuleUI.createAdvancedOptions() @@ -287,9 +307,9 @@ export class PairingRuleUI { // Advanced management menu for editing and resetting pairing rules public static async showAdvancedManagementMenu(): Promise { const selection = await vscode.window.showQuickPick( - PairingRuleUI.createAdvancedMenuItems(), { - title: 'Advanced Rule Management', - }); + PairingRuleUI.createAdvancedMenuItems(), { + title: 'Advanced Rule Management', + }); if (selection?.key) { await PairingRuleUI.handleAdvancedMenuSelection(selection.key); From aad7b7c27bcbd333b86910a214090a017aa34f98 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?=E9=95=BF=E6=9C=9F=E7=B4=A0=E9=A3=9F?= Date: Sat, 19 Jul 2025 01:31:50 +0800 Subject: [PATCH 19/34] Update ui.ts --- src/create-source-header-pair/ui.ts | 14 +++++++++----- 1 file changed, 9 insertions(+), 5 deletions(-) diff --git a/src/create-source-header-pair/ui.ts b/src/create-source-header-pair/ui.ts index 38a9ec50..73e86621 100644 --- a/src/create-source-header-pair/ui.ts +++ b/src/create-source-header-pair/ui.ts @@ -512,13 +512,17 @@ export class PairCreatorUI { } }, 100); - // Make success message non-blocking by not awaiting it - vscode.window.showInformationMessage( - `Successfully created ${path.basename(headerPath.fsPath)} and ${path.basename(sourcePath.fsPath)}.`); + // Show success message with slight delay to allow other dialogs to appear + setTimeout(() => { + vscode.window.showInformationMessage( + `Successfully created ${path.basename(headerPath.fsPath)} and ${path.basename(sourcePath.fsPath)}.`); + }, 50); } catch (error) { // Still show success message even if file opening fails - vscode.window.showInformationMessage( - `Successfully created ${path.basename(headerPath.fsPath)} and ${path.basename(sourcePath.fsPath)}.`); + setTimeout(() => { + vscode.window.showInformationMessage( + `Successfully created ${path.basename(headerPath.fsPath)} and ${path.basename(sourcePath.fsPath)}.`); + }, 50); } } } From 6ac7352a74fa69dd91aed7e4e370fe7dbb243b03 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?=E9=95=BF=E6=9C=9F=E7=B4=A0=E9=A3=9F?= Date: Sat, 19 Jul 2025 02:06:53 +0800 Subject: [PATCH 20/34] Update ui.ts --- src/create-source-header-pair/ui.ts | 44 +++++++++++++++++++++++------ 1 file changed, 36 insertions(+), 8 deletions(-) diff --git a/src/create-source-header-pair/ui.ts b/src/create-source-header-pair/ui.ts index 73e86621..84f16a12 100644 --- a/src/create-source-header-pair/ui.ts +++ b/src/create-source-header-pair/ui.ts @@ -220,20 +220,37 @@ export class PairCreatorUI { otherLanguageTemplates } = this.prepareCustomRulesChoices(allRules, language); - const choices = [ - ...cleanedCustomRules, ...adaptedDefaultTemplates, - ...otherLanguageTemplates, { + // Enhance the choices with better formatting for display + const enhancedChoices = [ + ...cleanedCustomRules.map(rule => ({ + ...rule, + description: `Custom configuration for this workspace`, + detail: rule.description // Move original description to detail + })), + ...adaptedDefaultTemplates.map(rule => ({ + ...rule, + description: `Built-in template with custom extensions`, + detail: rule.description // Move original description to detail + })), + ...otherLanguageTemplates.map(rule => ({ + ...rule, + description: `Alternative ${rule.language.toUpperCase()} template option`, + detail: rule.description // Move original description to detail + })), + { key: 'use_default', label: '$(list-unordered) Use Default Templates', - description: - 'Use the built-in default pairing rules instead of custom rules', + description: 'Ignore custom settings and use built-in defaults', + detail: 'Standard .h/.cpp extensions.', isSpecial: true } ]; - const result = await vscode.window.showQuickPick(choices, { + const result = await vscode.window.showQuickPick(enhancedChoices, { placeHolder: `Select a ${language.toUpperCase()} pairing rule`, title: 'Custom Pairing Rules Available', + matchOnDescription: true, + matchOnDetail: true }); if (!result) @@ -433,9 +450,20 @@ export class PairCreatorUI { private async promptForTemplateTypeFirst(language: 'c' | 'cpp', uncertain: boolean): Promise { const choices = this.prepareTemplateChoices(language, uncertain); - const result = await vscode.window.showQuickPick(choices, { + // Add detail field for better UI consistency + const enhancedChoices = choices.map(rule => ({ + ...rule, + description: rule.isClass ? 'Includes constructor, destructor, and basic structure' + : rule.isStruct ? 'Simple data structure with member variables' + : `Basic ${rule.language.toUpperCase()} file pair with header guards`, + detail: rule.description // Move original description to detail + })); + + const result = await vscode.window.showQuickPick(enhancedChoices, { placeHolder: 'Please select the type of file pair to create.', - title: 'Create Source/Header Pair' + title: 'Create Source/Header Pair', + matchOnDescription: true, + matchOnDetail: true }); if (result && !uncertain && language !== result.language) { From 0d3e9dd42f0314e3d6b4a3245d4355dfb8f4d285 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?=E9=95=BF=E6=9C=9F=E7=B4=A0=E9=A3=9F?= Date: Sat, 19 Jul 2025 02:38:06 +0800 Subject: [PATCH 21/34] Update ui.ts --- src/create-source-header-pair/ui.ts | 276 ++++++++++++++-------------- 1 file changed, 142 insertions(+), 134 deletions(-) diff --git a/src/create-source-header-pair/ui.ts b/src/create-source-header-pair/ui.ts index 84f16a12..1555b97a 100644 --- a/src/create-source-header-pair/ui.ts +++ b/src/create-source-header-pair/ui.ts @@ -21,6 +21,114 @@ export class PairCreatorUI { constructor(service: PairCreatorService) { this.service = service; } + // =============================== + // UI ADAPTERS - Data to View Conversion + // =============================== + + // Converts a PairingRule to a QuickPickItem for template selection + private adaptRuleForTemplateDisplay(rule: PairingRule): vscode.QuickPickItem & PairingRule { + const categoryDesc = rule.isClass ? 'Includes constructor, destructor, and basic structure' + : rule.isStruct ? 'Simple data structure with member variables' + : `Basic ${rule.language.toUpperCase()} file pair with header guards`; + + return { + ...rule, + description: categoryDesc, + detail: rule.description + }; + } + + // Converts a PairingRule to a QuickPickItem for custom rules selection + private adaptRuleForCustomRulesDisplay(rule: PairingRule, category: 'custom' | 'builtin' | 'alternative'): vscode.QuickPickItem & PairingRule { + const categoryMap = { + custom: 'Custom configuration for this workspace', + builtin: 'Built-in template with custom extensions', + alternative: `Alternative ${rule.language.toUpperCase()} template option` + }; + + return { + ...rule, + description: categoryMap[category], + detail: rule.description + }; + } + + // Creates the special "Use Default Templates" option + private createUseDefaultOption(): vscode.QuickPickItem & { key: string, isSpecial: boolean } { + return { + key: 'use_default', + label: '$(list-unordered) Use Default Templates', + description: 'Ignore custom settings and use built-in defaults', + detail: 'Standard .h/.cpp extensions', + isSpecial: true + }; + } + + // =============================== + // DATA PREPARATION - Small, Focused Functions + // =============================== + + // Gets custom rules for the specified language + private getCustomRulesForLanguage(allRules: PairingRule[], language: 'c' | 'cpp'): PairingRule[] { + return allRules.filter((rule: PairingRule) => rule.language === language); + } + + // Gets adapted built-in templates with custom extensions + private getAdaptedBuiltinTemplates(customRules: PairingRule[], language: 'c' | 'cpp'): PairingRule[] { + const customExt = customRules.length > 0 ? customRules[0] : null; + + if (customExt && language === 'cpp') { + return TEMPLATE_RULES + .filter(template => + template.language === 'cpp' && + !customRules.some(customRule => + customRule.isClass === template.isClass && + customRule.isStruct === template.isStruct && + (customRule.isClass || customRule.isStruct || + (!customRule.isClass && !customRule.isStruct && + !template.isClass && !template.isStruct)))) + .map(template => ({ + ...template, + key: `${template.key}_adapted`, + headerExt: customExt.headerExt, + sourceExt: customExt.sourceExt, + description: template.description + .replace(/Header\/Source/g, `${customExt.headerExt}/${customExt.sourceExt}`) + .replace(/\.h\/\.cpp/g, `${customExt.headerExt}/${customExt.sourceExt}`) + .replace(/basic \.h\/\.cpp/g, `basic ${customExt.headerExt}/${customExt.sourceExt}`) + .replace(/Creates a \.h\/\.cpp/g, `Creates a ${customExt.headerExt}/${customExt.sourceExt}`) + })); + } else { + return TEMPLATE_RULES + .filter(template => + template.language === language && + !customRules.some(customRule => + customRule.headerExt === template.headerExt && + customRule.sourceExt === template.sourceExt && + customRule.isClass === template.isClass && + customRule.isStruct === template.isStruct)) + .map(template => this.adaptRuleForDisplay(template)); + } + } + + // Gets cross-language template options + private getCrossLanguageTemplates(language: 'c' | 'cpp'): PairingRule[] { + return TEMPLATE_RULES + .filter(template => template.language !== language) + .map(template => this.adaptRuleForDisplay(template)); + } + + // Cleans up custom rules with proper labels and descriptions + private cleanCustomRules(rules: PairingRule[]): PairingRule[] { + return rules.map(rule => ({ + ...rule, + label: rule.label.includes('$(') ? rule.label + : `$(new-file) ${rule.language === 'cpp' ? 'C++' : 'C'} Pair (${rule.headerExt}/${rule.sourceExt})`, + description: rule.description.startsWith('Creates a') ? rule.description + : `Creates a ${rule.headerExt}/${rule.sourceExt} file pair with header guards.` + })); + } + // Gets placeholder name for input dialog, considering active file context private getPlaceholder(rule: PairingRule): string { const activeEditor = vscode.window.activeTextEditor; @@ -92,80 +200,17 @@ export class PairCreatorUI { .map(rule => this.adaptRuleForDisplay(rule)); } - // Filters custom rules by language and prepares them for display - private prepareCustomRulesChoices(allRules: PairingRule[], - language: 'c' | 'cpp'): { - languageRules: PairingRule[], - adaptedDefaultTemplates: PairingRule[], - otherLanguageTemplates: PairingRule[], - cleanedCustomRules: PairingRule[] - } { - const languageRules = - allRules.filter((rule: PairingRule) => rule.language === language); - const customExt = languageRules.length > 0 ? languageRules[0] : null; - - let adaptedDefaultTemplates: PairingRule[] = []; - - if (customExt && language === 'cpp') { - // For C++, adapt default templates with custom extensions - adaptedDefaultTemplates = - TEMPLATE_RULES - .filter( - template => - template.language === 'cpp' && - !languageRules.some( - customRule => - customRule.isClass === template.isClass && - customRule.isStruct === template.isStruct && - (customRule.isClass || customRule.isStruct || - (!customRule.isClass && !customRule.isStruct && - !template.isClass && !template.isStruct)))) - .map( - template => ({ - ...template, - key: `${template.key}_adapted`, - headerExt: customExt.headerExt, - sourceExt: customExt.sourceExt, - description: - template.description - .replace( - /Header\/Source/g, - `${customExt.headerExt}/${customExt.sourceExt}`) - .replace(/\.h\/\.cpp/g, `${customExt.headerExt}/${customExt.sourceExt}`) - .replace(/basic \.h\/\.cpp/g, - `basic ${customExt.headerExt}/${customExt.sourceExt}`) - .replace(/Creates a \.h\/\.cpp/g, - `Creates a ${customExt.headerExt}/${customExt.sourceExt}`) - })); - } else { - // Standard adaptation for non-custom or C language - adaptedDefaultTemplates = - TEMPLATE_RULES - .filter(template => - template.language === language && - !languageRules.some( - customRule => - customRule.headerExt === template.headerExt && - customRule.sourceExt === template.sourceExt && - customRule.isClass === template.isClass && - customRule.isStruct === template.isStruct)) - .map(template => this.adaptRuleForDisplay(template)); - } - - const otherLanguageTemplates = - TEMPLATE_RULES.filter(template => template.language !== language) - .map(template => this.adaptRuleForDisplay(template)); - - const cleanedCustomRules = allRules.map( - (rule: PairingRule) => ({ - ...rule, - label: rule.label.includes('$(') - ? rule.label - : `$(new-file) ${rule.language === 'cpp' ? 'C++' : 'C'} Pair (${rule.headerExt}/${rule.sourceExt})`, - description: rule.description.startsWith('Creates a') - ? rule.description - : `Creates a ${rule.headerExt}/${rule.sourceExt} file pair with header guards.` - })); + // Simplified function that delegates to smaller, focused functions + private prepareCustomRulesChoices(allRules: PairingRule[], language: 'c' | 'cpp'): { + languageRules: PairingRule[], + adaptedDefaultTemplates: PairingRule[], + otherLanguageTemplates: PairingRule[], + cleanedCustomRules: PairingRule[] + } { + const languageRules = this.getCustomRulesForLanguage(allRules, language); + const adaptedDefaultTemplates = this.getAdaptedBuiltinTemplates(languageRules, language); + const otherLanguageTemplates = this.getCrossLanguageTemplates(language); + const cleanedCustomRules = this.cleanCustomRules(allRules); return { languageRules, @@ -207,57 +252,35 @@ export class PairCreatorUI { return undefined; } - // Presents a selection dialog for custom pairing rules - // Combines custom rules with adapted default templates and cross-language options - // Returns selected rule, undefined if cancelled, or 'use_default' flag - public async selectFromCustomRules(allRules: PairingRule[], - language: 'c' | 'cpp'): - Promise { + // =============================== + // SIMPLIFIED UI CONTROLLERS + // =============================== + // Simplified version with clear intent: either returns a rule or indicates cancellation + public async selectFromCustomRules(allRules: PairingRule[], language: 'c' | 'cpp'): Promise { const { cleanedCustomRules, adaptedDefaultTemplates, otherLanguageTemplates } = this.prepareCustomRulesChoices(allRules, language); - // Enhance the choices with better formatting for display - const enhancedChoices = [ - ...cleanedCustomRules.map(rule => ({ - ...rule, - description: `Custom configuration for this workspace`, - detail: rule.description // Move original description to detail - })), - ...adaptedDefaultTemplates.map(rule => ({ - ...rule, - description: `Built-in template with custom extensions`, - detail: rule.description // Move original description to detail - })), - ...otherLanguageTemplates.map(rule => ({ - ...rule, - description: `Alternative ${rule.language.toUpperCase()} template option`, - detail: rule.description // Move original description to detail - })), - { - key: 'use_default', - label: '$(list-unordered) Use Default Templates', - description: 'Ignore custom settings and use built-in defaults', - detail: 'Standard .h/.cpp extensions.', - isSpecial: true - } + // Use adapters to create consistent UI items + const choices = [ + ...cleanedCustomRules.map(rule => this.adaptRuleForCustomRulesDisplay(rule, 'custom')), + ...adaptedDefaultTemplates.map(rule => this.adaptRuleForCustomRulesDisplay(rule, 'builtin')), + ...otherLanguageTemplates.map(rule => this.adaptRuleForCustomRulesDisplay(rule, 'alternative')), + this.createUseDefaultOption() ]; - const result = await vscode.window.showQuickPick(enhancedChoices, { + const result = await vscode.window.showQuickPick(choices, { placeHolder: `Select a ${language.toUpperCase()} pairing rule`, title: 'Custom Pairing Rules Available', matchOnDescription: true, matchOnDetail: true }); - if (!result) - return undefined; - if ('isSpecial' in result && result.isSpecial && - result.key === 'use_default') - return 'use_default'; + if (!result) return null; // User cancelled + if ('isSpecial' in result && result.isSpecial) return null; // User chose "Use Default Templates" return result as PairingRule; } @@ -405,12 +428,8 @@ export class PairCreatorUI { if (languageRules.length > 0) { // Use existing flow for custom rules const result = await this.selectFromCustomRules(allRules, language); - if (result === null || result === undefined) return undefined; // User cancelled - if (result === 'use_default') { - // Continue to default template selection - } else if (result) { - return result; - } + if (result === null) return undefined; // User cancelled or chose default templates + if (result) return result; // User selected a custom rule } } @@ -443,21 +462,10 @@ export class PairCreatorUI { return templateChoice; } - /** - * First step: Choose template type (language and template kind) - * Shows both C and C++ options regardless of detected language - */ - private async promptForTemplateTypeFirst(language: 'c' | 'cpp', uncertain: boolean): Promise { + // Simplified template selection with adapter pattern + private async promptForTemplateTypeFirst(language: 'c' | 'cpp', uncertain: boolean): Promise { const choices = this.prepareTemplateChoices(language, uncertain); - - // Add detail field for better UI consistency - const enhancedChoices = choices.map(rule => ({ - ...rule, - description: rule.isClass ? 'Includes constructor, destructor, and basic structure' - : rule.isStruct ? 'Simple data structure with member variables' - : `Basic ${rule.language.toUpperCase()} file pair with header guards`, - detail: rule.description // Move original description to detail - })); + const enhancedChoices = choices.map(rule => this.adaptRuleForTemplateDisplay(rule)); const result = await vscode.window.showQuickPick(enhancedChoices, { placeHolder: 'Please select the type of file pair to create.', @@ -466,9 +474,10 @@ export class PairCreatorUI { matchOnDetail: true }); + if (!result) return null; // User cancelled + if (result && !uncertain && language !== result.language) { - const shouldShowWarning = - await this.shouldShowLanguageMismatchWarning(language, result); + const shouldShowWarning = await this.shouldShowLanguageMismatchWarning(language, result); if (shouldShowWarning) { const detectedLangName = language === 'c' ? 'C' : 'C++'; @@ -478,8 +487,7 @@ export class PairCreatorUI { `You're working in a ${detectedLangName} file but selected a ${selectedLangName} template. This may create files with incompatible extensions or content.`, 'Continue', 'Cancel'); - if (shouldContinue !== 'Continue') - return undefined; + if (shouldContinue !== 'Continue') return null; } } From 9c6135affd11a9cf24f52c5e8dc6329fda7177fc Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?=E9=95=BF=E6=9C=9F=E7=B4=A0=E9=A3=9F?= Date: Sat, 19 Jul 2025 02:48:53 +0800 Subject: [PATCH 22/34] fuck --- src/create-source-header-pair/ui.ts | 84 +++++--------- src/pairing-rule-manager.ts | 170 +++++++++++++++------------- 2 files changed, 117 insertions(+), 137 deletions(-) diff --git a/src/create-source-header-pair/ui.ts b/src/create-source-header-pair/ui.ts index 1555b97a..1ec4e36c 100644 --- a/src/create-source-header-pair/ui.ts +++ b/src/create-source-header-pair/ui.ts @@ -14,6 +14,17 @@ import { PairingRule, PairingRuleService, PairingRuleUI } from '../pairing-rule- import { PairCreatorService } from './service'; import { Language, VALIDATION_PATTERNS, TEMPLATE_RULES } from './templates'; +// Type to clearly express user intent when selecting custom rules +type CustomRuleSelection = + | { type: 'rule', rule: PairingRule } // User selected a specific rule + | { type: 'use_default' } // User wants to use default templates + | { type: 'cancelled' }; // User cancelled the operation + +// This type clearly expresses three distinct user intents: +// - 'rule': User selected a specific pairing rule and it should be used +// - 'use_default': User explicitly chose to use default templates and should proceed to the default template selection flow +// - 'cancelled': User pressed ESC to cancel and the entire flow should be terminated + // PairCreatorUI handles all user interface interactions for file pair creation. // It manages dialogs, input validation, and user choices. export class PairCreatorUI { @@ -21,11 +32,6 @@ export class PairCreatorUI { constructor(service: PairCreatorService) { this.service = service; } - // =============================== - // UI ADAPTERS - Data to View Conversion - // =============================== - - // Converts a PairingRule to a QuickPickItem for template selection private adaptRuleForTemplateDisplay(rule: PairingRule): vscode.QuickPickItem & PairingRule { const categoryDesc = rule.isClass ? 'Includes constructor, destructor, and basic structure' : rule.isStruct ? 'Simple data structure with member variables' @@ -64,10 +70,6 @@ export class PairCreatorUI { }; } - // =============================== - // DATA PREPARATION - Small, Focused Functions - // =============================== - // Gets custom rules for the specified language private getCustomRulesForLanguage(allRules: PairingRule[], language: 'c' | 'cpp'): PairingRule[] { return allRules.filter((rule: PairingRule) => rule.language === language); @@ -220,44 +222,10 @@ export class PairCreatorUI { }; } - // Checks for existing custom pairing rules and offers to create them if not found - // For C++, presents options to use custom rules or create new ones. - // For C, always uses default templates. - // Returns selected rule, null if cancelled, undefined for defaults, or 'use_default' flag - public async checkAndOfferCustomRules(language: 'c' | 'cpp', - uncertain: boolean): - Promise { - if (language === 'c') - return undefined; // Always use default C templates - - const allRules = this.service.getAllPairingRules(); - const languageRules = - allRules.filter((rule: PairingRule) => rule.language === language); - - if (languageRules.length > 0) { - const result = await this.selectFromCustomRules(allRules, language); - return result === undefined ? null : result; - } - - if (!uncertain) { - const shouldCreateRules = await this.offerToCreateCustomRules(language); - if (shouldCreateRules === null) - return null; - if (shouldCreateRules) { - const result = await this.createCustomRules(language); - return result === undefined ? null : result; - } - } - - return undefined; - } - - // =============================== - // SIMPLIFIED UI CONTROLLERS - // =============================== - - // Simplified version with clear intent: either returns a rule or indicates cancellation - public async selectFromCustomRules(allRules: PairingRule[], language: 'c' | 'cpp'): Promise { + // - Returns 'cancelled' if the user presses ESC to cancel, and the operation should be terminated + // - Returns 'use_default' if the user selects "Use Default Templates", and should proceed to the default template flow + // - Returns 'rule' if the user selects a specific rule, and that rule should be used directly + public async selectFromCustomRules(allRules: PairingRule[], language: 'c' | 'cpp'): Promise { const { cleanedCustomRules, adaptedDefaultTemplates, @@ -279,14 +247,14 @@ export class PairCreatorUI { matchOnDetail: true }); - if (!result) return null; // User cancelled - if ('isSpecial' in result && result.isSpecial) return null; // User chose "Use Default Templates" - return result as PairingRule; + if (!result) return { type: 'cancelled' }; // User pressed ESC or cancelled + if ('isSpecial' in result && result.isSpecial) return { type: 'use_default' }; // User chose "Use Default Templates" + return { type: 'rule', rule: result as PairingRule }; // User selected a specific rule } - // Shows a dialog offering to create custom pairing rules for C++ - // Only applicable for C++ since C uses standard .c/.h extensions - // Returns true to create rules, false to dismiss, null if cancelled + // Shows a dialog offering to create custom pairing rules for C++. + // Only applicable for C++ since C uses standard .c/.h extensions. + // Returns true to create rules, false to dismiss, or null if cancelled. public async offerToCreateCustomRules(language: 'c' | 'cpp'): Promise { if (language === 'c') @@ -426,10 +394,14 @@ export class PairCreatorUI { const languageRules = allRules.filter((rule: PairingRule) => rule.language === language); if (languageRules.length > 0) { - // Use existing flow for custom rules + // Use existing flow for custom rules with clear intent handling const result = await this.selectFromCustomRules(allRules, language); - if (result === null) return undefined; // User cancelled or chose default templates - if (result) return result; // User selected a custom rule + if (result.type === 'cancelled') return undefined; // 用户明确取消操作 + if (result.type === 'use_default') { + // 用户选择使用默认模板,继续到常规流程(不return,让代码继续执行) + } else if (result.type === 'rule') { + return result.rule; // 用户选择了具体的自定义规则 + } } } diff --git a/src/pairing-rule-manager.ts b/src/pairing-rule-manager.ts index c3299441..d775894a 100644 --- a/src/pairing-rule-manager.ts +++ b/src/pairing-rule-manager.ts @@ -5,7 +5,7 @@ export interface PairingRule { key: string; label: string; description: string; - language: 'c' | 'cpp'; + language: 'c'|'cpp'; headerExt: string; sourceExt: string; isClass?: boolean; @@ -13,8 +13,8 @@ export interface PairingRule { } // Type aliases for QuickPick items -type RuleQuickPickItem = vscode.QuickPickItem & { rule: PairingRule }; -type ActionQuickPickItem = vscode.QuickPickItem & { key: string }; +type RuleQuickPickItem = vscode.QuickPickItem&{rule: PairingRule}; +type ActionQuickPickItem = vscode.QuickPickItem&{key: string}; // Configuration management service class export class PairingRuleService { @@ -29,8 +29,9 @@ export class PairingRuleService { // Show error message and re-throw the error for proper error handling private static handleError(error: unknown, operation: string, - scope: string): never { - const message = `Failed to ${operation} pairing rules for ${scope}: ${error instanceof Error ? error.message : 'Unknown error'}`; + scope: string): never { + const message = `Failed to ${operation} pairing rules for ${scope}: ${ + error instanceof Error ? error.message : 'Unknown error'}`; vscode.window.showErrorMessage(message); throw error; } @@ -38,54 +39,54 @@ export class PairingRuleService { // Get the currently active pairing rules from configuration public static getActiveRules(): ReadonlyArray { return vscode.workspace.getConfiguration('clangd').get( - PairingRuleService.CONFIG_KEY, []); + PairingRuleService.CONFIG_KEY, []); } // Check if custom rules exist for the specified scope (workspace or user) - public static hasCustomRules(scope: 'workspace' | 'user'): boolean { + public static hasCustomRules(scope: 'workspace'|'user'): boolean { const inspection = - vscode.workspace.getConfiguration('clangd').inspect( - PairingRuleService.CONFIG_KEY); + vscode.workspace.getConfiguration('clangd').inspect( + PairingRuleService.CONFIG_KEY); const value = scope === 'workspace' ? inspection?.workspaceValue - : inspection?.globalValue; + : inspection?.globalValue; return Array.isArray(value); } // Get pairing rules for a specific scope (workspace or user) - public static getRules(scope: 'workspace' | 'user'): PairingRule[] | undefined { + public static getRules(scope: 'workspace'|'user'): PairingRule[]|undefined { const inspection = - vscode.workspace.getConfiguration('clangd').inspect( - PairingRuleService.CONFIG_KEY); + vscode.workspace.getConfiguration('clangd').inspect( + PairingRuleService.CONFIG_KEY); return scope === 'workspace' ? inspection?.workspaceValue - : inspection?.globalValue; + : inspection?.globalValue; } // Write pairing rules to the specified scope (workspace or user) public static async writeRules(rules: PairingRule[], - scope: 'workspace' | 'user'): Promise { + scope: 'workspace'|'user'): Promise { try { if (!Array.isArray(rules)) throw new Error('Rules must be an array'); rules.forEach(PairingRuleService.validateRule); const target = scope === 'workspace' - ? vscode.ConfigurationTarget.Workspace - : vscode.ConfigurationTarget.Global; + ? vscode.ConfigurationTarget.Workspace + : vscode.ConfigurationTarget.Global; await vscode.workspace.getConfiguration('clangd').update( - PairingRuleService.CONFIG_KEY, rules, target); + PairingRuleService.CONFIG_KEY, rules, target); } catch (error) { PairingRuleService.handleError(error, 'save', scope); } } // Reset pairing rules for the specified scope (remove custom rules) - public static async resetRules(scope: 'workspace' | 'user'): Promise { + public static async resetRules(scope: 'workspace'|'user'): Promise { try { const target = scope === 'workspace' - ? vscode.ConfigurationTarget.Workspace - : vscode.ConfigurationTarget.Global; + ? vscode.ConfigurationTarget.Workspace + : vscode.ConfigurationTarget.Global; await vscode.workspace.getConfiguration('clangd').update( - PairingRuleService.CONFIG_KEY, undefined, target); + PairingRuleService.CONFIG_KEY, undefined, target); } catch (error) { PairingRuleService.handleError(error, 'reset', scope); } @@ -133,19 +134,21 @@ export class PairingRuleUI { // Create rule choices from extension options for QuickPick display private static createRuleChoices(): RuleQuickPickItem[] { return PairingRuleUI.EXTENSION_OPTIONS.map( - (option, index) => ({ - label: option.label, - description: option.description, - detail: option.detail, - rule: { - key: `custom_${option.language}_${index}`, - label: `${option.language.toUpperCase()} Pair (${option.headerExt}/${option.sourceExt})`, - description: `Creates a ${option.headerExt}/${option.sourceExt} file pair with header guards.`, - language: option.language, - headerExt: option.headerExt, - sourceExt: option.sourceExt, - }, - })); + (option, index) => ({ + label: option.label, + description: option.description, + detail: option.detail, + rule: { + key: `custom_${option.language}_${index}`, + label: `${option.language.toUpperCase()} Pair (${ + option.headerExt}/${option.sourceExt})`, + description: `Creates a ${option.headerExt}/${ + option.sourceExt} file pair with header guards.`, + language: option.language, + headerExt: option.headerExt, + sourceExt: option.sourceExt, + }, + })); } // Create advanced options separator and menu item for advanced management @@ -187,11 +190,11 @@ export class PairingRuleUI { kind: vscode.QuickPickItemKind.Separator, key: 'separator_global', }, - { - label: '$(edit) Edit Global Rules...', - description: 'Opens your global settings.json', - key: 'edit_global', - }); + { + label: '$(edit) Edit Global Rules...', + description: 'Opens your global settings.json', + key: 'edit_global', + }); if (PairingRuleService.hasCustomRules('user')) { items.push({ @@ -207,47 +210,48 @@ export class PairingRuleUI { // Handle rule selection and ask for save scope (workspace or global) private static async handleRuleSelection(rule: PairingRule): Promise { const selection = await vscode.window.showQuickPick( - [ - { - label: 'Save for this Workspace', - description: 'Recommended. Creates a .vscode/settings.json file.', - scope: 'workspace', - }, + [ + { + label: 'Save for this Workspace', + description: 'Recommended. Creates a .vscode/settings.json file.', + scope: 'workspace', + }, + { + label: 'Save for all my Projects (Global)', + description: 'Modifies your global user settings.', + scope: 'user', + }, + ], { - label: 'Save for all my Projects (Global)', - description: 'Modifies your global user settings.', - scope: 'user', - }, - ], - { - placeHolder: 'Where would you like to save this rule?', - title: 'Save Configuration Scope', - }); + placeHolder: 'Where would you like to save this rule?', + title: 'Save Configuration Scope', + }); if (!selection) return; await PairingRuleService.writeRules([rule], selection.scope as 'workspace' | - 'user'); - vscode.window.showInformationMessage(`Successfully set '${rule.label}' as the default extension for the ${selection.scope}.`); + 'user'); + vscode.window.showInformationMessage(`Successfully set '${ + rule.label}' as the default extension for the ${selection.scope}.`); } // Handle advanced menu selection and execute the corresponding action private static async handleAdvancedMenuSelection(key: string): Promise { const actions = { edit_workspace: () => vscode.commands.executeCommand( - 'workbench.action.openWorkspaceSettingsFile'), + 'workbench.action.openWorkspaceSettingsFile'), edit_global: () => - vscode.commands.executeCommand('workbench.action.openSettingsJson'), + vscode.commands.executeCommand('workbench.action.openSettingsJson'), reset_workspace: async () => { await PairingRuleService.resetRules('workspace'); vscode.window.showInformationMessage( - 'Workspace pairing rules have been reset.'); + 'Workspace pairing rules have been reset.'); }, reset_global: async () => { await PairingRuleService.resetRules('user'); vscode.window.showInformationMessage( - 'Global pairing rules have been reset.'); + 'Global pairing rules have been reset.'); }, }; @@ -257,31 +261,35 @@ export class PairingRuleUI { } // Public method to prompt for file extensions only - public static async promptForFileExtensions(): Promise<{ headerExt: string, sourceExt: string } | undefined> { + public static async promptForFileExtensions(): + Promise<{headerExt: string, sourceExt: string}|undefined> { const selected = await vscode.window.showQuickPick( - PairingRuleUI.EXTENSION_OPTIONS.map(option => ({ - label: option.label, - description: option.description, - detail: option.detail, - headerExt: option.headerExt, - sourceExt: option.sourceExt - })), { - placeHolder: 'Choose file extensions for your C++ files', - title: 'Step 1 of 2: Select File Extensions', - matchOnDescription: true, - matchOnDetail: true - }); - - return selected ? { headerExt: selected.headerExt, sourceExt: selected.sourceExt } : undefined; + PairingRuleUI.EXTENSION_OPTIONS.map(option => ({ + label: option.label, + description: option.description, + detail: option.detail, + headerExt: option.headerExt, + sourceExt: option.sourceExt + })), + { + placeHolder: 'Choose file extensions for your C++ files', + title: 'Step 1 of 2: Select File Extensions', + matchOnDescription: true, + matchOnDetail: true + }); + + return selected + ? {headerExt: selected.headerExt, sourceExt: selected.sourceExt} + : undefined; } // Main configuration wizard - entry point for setting up pairing rules public static async showConfigurationWizard(): Promise { const quickPick = - vscode.window.createQuickPick(); + vscode.window.createQuickPick(); quickPick.title = 'Quick Setup: Choose File Extensions'; quickPick.placeholder = - 'Choose file extension combination for this workspace, or go to advanced options.'; + 'Choose file extension combination for this workspace, or go to advanced options.'; quickPick.items = [ ...PairingRuleUI.createRuleChoices(), ...PairingRuleUI.createAdvancedOptions() @@ -307,9 +315,9 @@ export class PairingRuleUI { // Advanced management menu for editing and resetting pairing rules public static async showAdvancedManagementMenu(): Promise { const selection = await vscode.window.showQuickPick( - PairingRuleUI.createAdvancedMenuItems(), { - title: 'Advanced Rule Management', - }); + PairingRuleUI.createAdvancedMenuItems(), { + title: 'Advanced Rule Management', + }); if (selection?.key) { await PairingRuleUI.handleAdvancedMenuSelection(selection.key); From a82005280453f3fdacd09a254b66234cb94dd72f Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?=E9=95=BF=E6=9C=9F=E7=B4=A0=E9=A3=9F?= Date: Sat, 19 Jul 2025 03:00:51 +0800 Subject: [PATCH 23/34] Add icon and explorer context menu for new source/header pair Added an icon to the 'New Source/Header Pair' command and registered it in the explorer context menu for folders. This improves discoverability and usability of the command within the file explorer. --- package.json | 10 +++++++++- 1 file changed, 9 insertions(+), 1 deletion(-) diff --git a/package.json b/package.json index c1da0989..d95c6cc8 100644 --- a/package.json +++ b/package.json @@ -271,7 +271,8 @@ { "command": "clangd.newSourcePair", "category": "clangd", - "title": "New Source/Header Pair" + "title": "New Source/Header Pair", + "icon": "$(new-file)" }, { "command": "clangd.createPair.configureRules", @@ -437,6 +438,13 @@ "group": "navigation" } ], + "explorer/context": [ + { + "command": "clangd.newSourcePair", + "when": "explorerResourceIsFolder", + "group": "2_workspace@1" + } + ], "commandPalette": [ { "command": "clangd.typeHierarchy.viewParents", From c31a851ebe8a74fddfd9ca93e4ea6847ec7e6b7b Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?=E9=95=BF=E6=9C=9F=E7=B4=A0=E9=A3=9F?= Date: Sat, 19 Jul 2025 05:51:44 +0800 Subject: [PATCH 24/34] Remove clangd.newSourcePair command from package.json Deleted the clangd.newSourcePair command configuration from the package.json contributes.commands section. This cleans up unused or deprecated command definitions. --- package.json | 5 ----- 1 file changed, 5 deletions(-) diff --git a/package.json b/package.json index d95c6cc8..528c7ac5 100644 --- a/package.json +++ b/package.json @@ -396,11 +396,6 @@ "when": "resourceLangId == c || resourceLangId == cpp || resourceLangId == cuda-cpp || resourceLangId == objective-c || resourceLangId == objective-cpp", "group": "0_navigation@5" }, - { - "command": "clangd.newSourcePair", - "when": "resourceLangId == c || resourceLangId == cpp || resourceLangId == cuda-cpp || resourceLangId == objective-c || resourceLangId == objective-cpp", - "group": "1_modification@1" - }, { "command": "clangd.ast", "when": "(resourceLangId == c || resourceLangId == cpp || resourceLangId == cuda-cpp || resourceLangId == objective-c || resourceLangId == objective-cpp) && clangd.ast.supported" From 21eeb6b7bd3a79c9b127dd8628aa6a72b46bd3e4 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?=E9=95=BF=E6=9C=9F=E7=B4=A0=E9=A3=9F?= Date: Sat, 19 Jul 2025 15:47:45 +0800 Subject: [PATCH 25/34] Update service.ts --- src/create-source-header-pair/service.ts | 9 +++------ 1 file changed, 3 insertions(+), 6 deletions(-) diff --git a/src/create-source-header-pair/service.ts b/src/create-source-header-pair/service.ts index 5a567ab9..5ba09e91 100644 --- a/src/create-source-header-pair/service.ts +++ b/src/create-source-header-pair/service.ts @@ -384,9 +384,8 @@ export class PairCreatorService { } } - /** - * Saves a rule as the default configuration with user choice of scope - */ + + // Saves a rule as the default configuration with user choice of scope public async saveRuleAsDefault(rule: PairingRule): Promise { const { PairingRuleService } = await import('../pairing-rule-manager'); @@ -431,9 +430,7 @@ export class PairCreatorService { } } - /** - * Generates file content and writes both header and source files - */ + // Generates file content and writes both header and source files public async generateAndWriteFiles( fileName: string, rule: PairingRule, From 1ccc7d921861b25e9f9f714438eb5fab29c384db Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?=E9=95=BF=E6=9C=9F=E7=B4=A0=E9=A3=9F?= Date: Sat, 19 Jul 2025 16:13:25 +0800 Subject: [PATCH 26/34] Update package.json --- package.json | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/package.json b/package.json index 528c7ac5..62466fe2 100644 --- a/package.json +++ b/package.json @@ -1,8 +1,8 @@ { - "name": "vscode-clangd", + "name": "vscode-clangd-fancy", "displayName": "clangd", "description": "C/C++ completion, navigation, and insights", - "version": "0.2.0", + "version": "0.2.1", "publisher": "llvm-vs-code-extensions", "license": "MIT", "homepage": "https://clangd.llvm.org/", From 2f7cb17cffd9632fdcee363d4c4d58af06b14cf6 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?=E9=95=BF=E6=9C=9F=E7=B4=A0=E9=A3=9F?= Date: Sat, 19 Jul 2025 16:15:54 +0800 Subject: [PATCH 27/34] Update coordinator.ts --- src/create-source-header-pair/coordinator.ts | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/src/create-source-header-pair/coordinator.ts b/src/create-source-header-pair/coordinator.ts index e2d5e80c..f80db767 100644 --- a/src/create-source-header-pair/coordinator.ts +++ b/src/create-source-header-pair/coordinator.ts @@ -4,7 +4,7 @@ // // Lean coordinator that orchestrates the source/header pair creation workflow. // Uses dependency injection and delegates all implementation details to -// Service and UI layers. The create() method reads like a clear story. +// service and UI layers. import * as vscode from 'vscode'; @@ -42,7 +42,7 @@ export class PairCoordinator implements vscode.Disposable { this.configureRulesCommand.dispose(); } - // Main workflow orchestration - reads like a clear story + // Main workflow orchestration public async create(): Promise { try { // 1. Determine where to create files From 173b431c051c0b1511e1e0dbdd400ff81b16d4a3 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?=E9=95=BF=E6=9C=9F=E7=B4=A0=E9=A3=9F?= Date: Sat, 19 Jul 2025 16:21:09 +0800 Subject: [PATCH 28/34] fix --- package-lock.json | 22 +++++++++++++++++----- package.json | 5 +++-- 2 files changed, 20 insertions(+), 7 deletions(-) diff --git a/package-lock.json b/package-lock.json index d13209d9..1ca5f7d6 100644 --- a/package-lock.json +++ b/package-lock.json @@ -1,16 +1,17 @@ { - "name": "vscode-clangd", - "version": "0.2.0", + "name": "vscode-clangd-fancy", + "version": "0.2.1", "lockfileVersion": 2, "requires": true, "packages": { "": { - "name": "vscode-clangd", - "version": "0.2.0", + "name": "vscode-clangd-fancy", + "version": "0.2.1", "license": "MIT", "dependencies": { "@clangd/install": "0.1.20", - "vscode-languageclient": "^9.0.1" + "vscode-languageclient": "^9.0.1", + "vscode-nls": "^5.2.0" }, "devDependencies": { "@types/glob": "^8.1.0", @@ -6306,6 +6307,12 @@ "integrity": "sha512-Ld1VelNuX9pdF39h2Hgaeb5hEZM2Z3jUrrMgWQAu82jMtZp7p3vJT3BzToKtZI7NgQssZje5o0zryOrhQvzQAg==", "license": "MIT" }, + "node_modules/vscode-nls": { + "version": "5.2.0", + "resolved": "https://registry.npmjs.org/vscode-nls/-/vscode-nls-5.2.0.tgz", + "integrity": "sha512-RAaHx7B14ZU04EU31pT+rKz2/zSl7xMsfIZuo8pd+KZO6PXtQmpevpq3vxvWNcrGbdmhM/rr5Uw5Mz+NBfhVng==", + "license": "MIT" + }, "node_modules/webidl-conversions": { "version": "3.0.1", "resolved": "https://registry.npmjs.org/webidl-conversions/-/webidl-conversions-3.0.1.tgz", @@ -10727,6 +10734,11 @@ "resolved": "https://registry.npmjs.org/vscode-languageserver-types/-/vscode-languageserver-types-3.17.5.tgz", "integrity": "sha512-Ld1VelNuX9pdF39h2Hgaeb5hEZM2Z3jUrrMgWQAu82jMtZp7p3vJT3BzToKtZI7NgQssZje5o0zryOrhQvzQAg==" }, + "vscode-nls": { + "version": "5.2.0", + "resolved": "https://registry.npmjs.org/vscode-nls/-/vscode-nls-5.2.0.tgz", + "integrity": "sha512-RAaHx7B14ZU04EU31pT+rKz2/zSl7xMsfIZuo8pd+KZO6PXtQmpevpq3vxvWNcrGbdmhM/rr5Uw5Mz+NBfhVng==" + }, "webidl-conversions": { "version": "3.0.1", "resolved": "https://registry.npmjs.org/webidl-conversions/-/webidl-conversions-3.0.1.tgz", diff --git a/package.json b/package.json index 62466fe2..1eb89f0f 100644 --- a/package.json +++ b/package.json @@ -1,6 +1,6 @@ { "name": "vscode-clangd-fancy", - "displayName": "clangd", + "displayName": "Clotho", "description": "C/C++ completion, navigation, and insights", "version": "0.2.1", "publisher": "llvm-vs-code-extensions", @@ -48,7 +48,8 @@ }, "dependencies": { "@clangd/install": "0.1.20", - "vscode-languageclient": "^9.0.1" + "vscode-languageclient": "^9.0.1", + "vscode-nls": "^5.2.0" }, "devDependencies": { "@types/glob": "^8.1.0", From 7b9d7c0fcdb5abfca0e48ac0e81ed60c819dc380 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?=E9=95=BF=E6=9C=9F=E7=B4=A0=E9=A3=9F?= Date: Sat, 19 Jul 2025 16:26:31 +0800 Subject: [PATCH 29/34] Create vscode-clangd-fancy-0.2.1.vsix --- vscode-clangd-fancy-0.2.1.vsix | Bin 0 -> 643312 bytes 1 file changed, 0 insertions(+), 0 deletions(-) create mode 100644 vscode-clangd-fancy-0.2.1.vsix diff --git a/vscode-clangd-fancy-0.2.1.vsix b/vscode-clangd-fancy-0.2.1.vsix new file mode 100644 index 0000000000000000000000000000000000000000..f2aab5205d13efec2b9fb8e666f4647355383313 GIT binary patch literal 643312 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πilh#z~jj`7coy?zC9xjeZzmog=x> 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%0ASi#ELR(vpV*AJCG=F~lME8Q-dc1^NoVhLwsd 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 z zK_>~}k=YHy5Id6k^2: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%0DGvlk_&JocGN@aOC01T?9rIh){8fqd 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 Date: Sat, 19 Jul 2025 21:57:55 +0800 Subject: [PATCH 30/34] Update service.ts --- src/create-source-header-pair/service.ts | 51 ++++++++++++++---------- 1 file changed, 29 insertions(+), 22 deletions(-) diff --git a/src/create-source-header-pair/service.ts b/src/create-source-header-pair/service.ts index 5ba09e91..836d72eb 100644 --- a/src/create-source-header-pair/service.ts +++ b/src/create-source-header-pair/service.ts @@ -191,6 +191,34 @@ export class PairCreatorService { .join(''); } + // Generates header guard macro based on filename and extension + private generateHeaderGuard(fileName: string, headerExt: string): string { + // Remove leading dot from extension and convert to uppercase + const extPart = headerExt.replace(/^\./, '').toUpperCase(); + return `${fileName.toUpperCase()}_${extPart}_`; + } + public generateFileContent(fileName: string, eol: string, rule: PairingRule): { headerContent: string; sourceContent: string; } { + const templateKey: TemplateKey = + rule.isClass ? 'CPP_CLASS' + : rule.isStruct ? (rule.language === 'cpp' ? 'CPP_STRUCT' : 'C_STRUCT') + : rule.language === 'c' ? 'C_EMPTY' + : 'CPP_EMPTY'; + + const templates = FILE_TEMPLATES[templateKey]; + const context = { + fileName, + headerGuard: this.generateHeaderGuard(fileName, rule.headerExt), + includeLine: `#include "${fileName}${rule.headerExt}"` + }; + + const headerContent = this.applyTemplate(templates.header, context); + const sourceContent = this.applyTemplate(templates.source, context); + + return { + headerContent: headerContent.replace(/\n/g, eol), + sourceContent: sourceContent.replace(/\n/g, eol) + }; + } // Gets default placeholder based on rule type (pure function) public getDefaultPlaceholder(rule: PairingRule): string { if (rule.isClass) { @@ -217,28 +245,7 @@ export class PairCreatorService { } // Generates file content with improved template selection - public generateFileContent(fileName: string, eol: string, rule: PairingRule): { headerContent: string; sourceContent: string; } { - const templateKey: TemplateKey = - rule.isClass ? 'CPP_CLASS' - : rule.isStruct ? (rule.language === 'cpp' ? 'CPP_STRUCT' : 'C_STRUCT') - : rule.language === 'c' ? 'C_EMPTY' - : 'CPP_EMPTY'; - const templates = FILE_TEMPLATES[templateKey]; - const context = { - fileName, - headerGuard: `${fileName.toUpperCase()}_H_`, - includeLine: `#include "${fileName}${rule.headerExt}"` - }; - - const headerContent = this.applyTemplate(templates.header, context); - const sourceContent = this.applyTemplate(templates.source, context); - - return { - headerContent: headerContent.replace(/\n/g, eol), - sourceContent: sourceContent.replace(/\n/g, eol) - }; - } // Optimized template variable substitution using regex replacement private applyTemplate(template: string, @@ -385,7 +392,7 @@ export class PairCreatorService { } - // Saves a rule as the default configuration with user choice of scope + // Saves a rule as the default configuration with user choice of scope public async saveRuleAsDefault(rule: PairingRule): Promise { const { PairingRuleService } = await import('../pairing-rule-manager'); From 55cffa97b8225f5c187acfcac48ef38084e540f1 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?=E9=95=BF=E6=9C=9F=E7=B4=A0=E9=A3=9F?= Date: Sat, 19 Jul 2025 22:18:39 +0800 Subject: [PATCH 31/34] Update service.ts --- src/create-source-header-pair/service.ts | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-) diff --git a/src/create-source-header-pair/service.ts b/src/create-source-header-pair/service.ts index 836d72eb..0b2f37cb 100644 --- a/src/create-source-header-pair/service.ts +++ b/src/create-source-header-pair/service.ts @@ -197,6 +197,7 @@ export class PairCreatorService { const extPart = headerExt.replace(/^\./, '').toUpperCase(); return `${fileName.toUpperCase()}_${extPart}_`; } + // Generates file content with improved template selection public generateFileContent(fileName: string, eol: string, rule: PairingRule): { headerContent: string; sourceContent: string; } { const templateKey: TemplateKey = rule.isClass ? 'CPP_CLASS' @@ -244,7 +245,7 @@ export class PairCreatorService { : '\n'; } - // Generates file content with improved template selection + // Optimized template variable substitution using regex replacement From 21eb998c9ad46de19dac6f94d31c43a81d24c4aa Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?=E9=95=BF=E6=9C=9F=E7=B4=A0=E9=A3=9F?= Date: Sat, 19 Jul 2025 22:23:21 +0800 Subject: [PATCH 32/34] format --- package.json | 2 +- src/create-source-header-pair/coordinator.ts | 165 +-- src/create-source-header-pair/index.ts | 40 +- src/create-source-header-pair/service.ts | 795 +++++++------- src/create-source-header-pair/templates.ts | 149 +-- src/create-source-header-pair/ui.ts | 1017 ++++++++++-------- 6 files changed, 1125 insertions(+), 1043 deletions(-) diff --git a/package.json b/package.json index 1eb89f0f..cba8b72a 100644 --- a/package.json +++ b/package.json @@ -35,7 +35,7 @@ "vscode:prepublish": "npm run check-ts && npm run esbuild -- --minify --keep-names", "compile": "npm run esbuild -- --sourcemap", "check-ts": "tsc -noEmit -p ./", - "format": "clang-format -i --glob=\"{src,test}/*.ts\"", + "format": "clang-format -i --glob=\"{src,test}/**/*.ts\"", "test-compile": "tsc -p ./ && npm run compile", "test": "npm run test-compile && node ./out/test/index.js", "package": "vsce package --baseImagesUrl https://raw.githubusercontent.com/clangd/vscode-clangd/master/", diff --git a/src/create-source-header-pair/coordinator.ts b/src/create-source-header-pair/coordinator.ts index f80db767..8ead1f17 100644 --- a/src/create-source-header-pair/coordinator.ts +++ b/src/create-source-header-pair/coordinator.ts @@ -3,94 +3,105 @@ // ================ // // Lean coordinator that orchestrates the source/header pair creation workflow. -// Uses dependency injection and delegates all implementation details to +// Uses dependency injection and delegates all implementation details to // service and UI layers. import * as vscode from 'vscode'; -import { showConfigurationWizard } from '../pairing-rule-manager'; -import { PairCreatorService } from './service'; -import { PairCreatorUI } from './ui'; +import {showConfigurationWizard} from '../pairing-rule-manager'; + +import {PairCreatorService} from './service'; +import {PairCreatorUI} from './ui'; // PairCoordinator orchestrates the workflow between UI and Service layers. // It follows the single responsibility principle and uses dependency injection. export class PairCoordinator implements vscode.Disposable { - private static readonly ERROR_MESSAGES = { - NO_TARGET_DIRECTORY: 'Cannot determine target directory. Please open a folder or a file first.', - FILE_EXISTS: (filePath: string) => `File already exists: ${filePath}`, - UNEXPECTED_ERROR: 'An unexpected error occurred.' - } as const; - - private newPairCommand: vscode.Disposable; - private configureRulesCommand: vscode.Disposable; - - // Constructor with dependency injection - receives pre-configured instances - constructor( - private readonly service: PairCreatorService, - private readonly ui: PairCreatorUI - ) { - // Register commands - this.newPairCommand = vscode.commands.registerCommand( - 'clangd.newSourcePair', this.create, this); - this.configureRulesCommand = vscode.commands.registerCommand( - 'clangd.newSourcePair.configureRules', this.configureRules, this); - } + private static readonly ERROR_MESSAGES = { + NO_TARGET_DIRECTORY: + 'Cannot determine target directory. Please open a folder or a file first.', + FILE_EXISTS: (filePath: string) => `File already exists: ${filePath}`, + UNEXPECTED_ERROR: 'An unexpected error occurred.' + } as const; - // Dispose method for cleanup when extension is deactivated - dispose() { - this.newPairCommand.dispose(); - this.configureRulesCommand.dispose(); - } + private newPairCommand: vscode.Disposable; + private configureRulesCommand: vscode.Disposable; - // Main workflow orchestration - public async create(): Promise { - try { - // 1. Determine where to create files - const targetDirectory = await this.getTargetDirectory(); - if (!targetDirectory) { - vscode.window.showErrorMessage(PairCoordinator.ERROR_MESSAGES.NO_TARGET_DIRECTORY); - return; - } - - // 2. Get user preferences for what to create - const { language, uncertain } = await this.service.detectLanguageFromEditor(); - const rule = await this.ui.promptForPairingRule(language, uncertain); - if (!rule) return; - - const fileName = await this.ui.promptForFileName(rule); - if (!fileName) return; - - // 3. Prepare file paths and check for conflicts - const { headerPath, sourcePath } = this.service.createFilePaths(targetDirectory, fileName, rule); - const existingFilePath = await this.service.checkFileExistence(headerPath, sourcePath); - if (existingFilePath) { - vscode.window.showErrorMessage(PairCoordinator.ERROR_MESSAGES.FILE_EXISTS(existingFilePath)); - return; - } - - // 4. Create the files - await this.service.generateAndWriteFiles(fileName, rule, headerPath, sourcePath); - - // 5. Show success and handle post-creation tasks - await this.ui.showSuccessAndOpenFile(headerPath, sourcePath); - await this.service.handleOfferToSaveAsDefault(rule, language); - - } catch (error: unknown) { - const errorMessage = error instanceof Error ? error.message : PairCoordinator.ERROR_MESSAGES.UNEXPECTED_ERROR; - vscode.window.showErrorMessage(errorMessage); - } - } + // Constructor with dependency injection - receives pre-configured instances + constructor(private readonly service: PairCreatorService, + private readonly ui: PairCreatorUI) { + // Register commands + this.newPairCommand = vscode.commands.registerCommand( + 'clangd.newSourcePair', this.create, this); + this.configureRulesCommand = vscode.commands.registerCommand( + 'clangd.newSourcePair.configureRules', this.configureRules, this); + } - // Determine target directory with fallback to workspace picker - private async getTargetDirectory(): Promise { - return await this.service.getTargetDirectory( - vscode.window.activeTextEditor?.document?.uri.fsPath, - vscode.workspace.workspaceFolders - ) ?? await this.ui.showWorkspaceFolderPicker(); - } + // Dispose method for cleanup when extension is deactivated + dispose() { + this.newPairCommand.dispose(); + this.configureRulesCommand.dispose(); + } + + // Main workflow orchestration + public async create(): Promise { + try { + // 1. Determine where to create files + const targetDirectory = await this.getTargetDirectory(); + if (!targetDirectory) { + vscode.window.showErrorMessage( + PairCoordinator.ERROR_MESSAGES.NO_TARGET_DIRECTORY); + return; + } + + // 2. Get user preferences for what to create + const {language, uncertain} = + await this.service.detectLanguageFromEditor(); + const rule = await this.ui.promptForPairingRule(language, uncertain); + if (!rule) + return; - // Opens the configuration wizard for pairing rules - public async configureRules(): Promise { - await showConfigurationWizard(); + const fileName = await this.ui.promptForFileName(rule); + if (!fileName) + return; + + // 3. Prepare file paths and check for conflicts + const {headerPath, sourcePath} = + this.service.createFilePaths(targetDirectory, fileName, rule); + const existingFilePath = + await this.service.checkFileExistence(headerPath, sourcePath); + if (existingFilePath) { + vscode.window.showErrorMessage( + PairCoordinator.ERROR_MESSAGES.FILE_EXISTS(existingFilePath)); + return; + } + + // 4. Create the files + await this.service.generateAndWriteFiles(fileName, rule, headerPath, + sourcePath); + + // 5. Show success and handle post-creation tasks + await this.ui.showSuccessAndOpenFile(headerPath, sourcePath); + await this.service.handleOfferToSaveAsDefault(rule, language); + + } catch (error: unknown) { + const errorMessage = + error instanceof Error + ? error.message + : PairCoordinator.ERROR_MESSAGES.UNEXPECTED_ERROR; + vscode.window.showErrorMessage(errorMessage); } + } + + // Determine target directory with fallback to workspace picker + private async getTargetDirectory(): Promise { + return await this.service.getTargetDirectory( + vscode.window.activeTextEditor?.document?.uri.fsPath, + vscode.workspace.workspaceFolders) ?? + await this.ui.showWorkspaceFolderPicker(); + } + + // Opens the configuration wizard for pairing rules + public async configureRules(): Promise { + await showConfigurationWizard(); + } } diff --git a/src/create-source-header-pair/index.ts b/src/create-source-header-pair/index.ts index 5da3477d..94322f94 100644 --- a/src/create-source-header-pair/index.ts +++ b/src/create-source-header-pair/index.ts @@ -2,15 +2,16 @@ // CREATE SOURCE HEADER PAIR - MODULE INDEX // ======================================== // -// This module provides functionality to create matching header/source file pairs -// for C/C++ development. It intelligently detects language context, offers -// appropriate templates, and handles custom file extensions. +// This module provides functionality to create matching header/source file +// pairs for C/C++ development. It intelligently detects language context, +// offers appropriate templates, and handles custom file extensions. // // ARCHITECTURE: // - templates.ts: Template rules and file content templates // - service.ts: Business logic layer (language detection, file operations) // - ui.ts: User interface layer (dialogs, input validation) -// - coordinator.ts: Main coordinator (orchestrates workflow, registers commands) +// - coordinator.ts: Main coordinator (orchestrates workflow, registers +// commands) // // WORKFLOW: // Command triggered → Detect target directory → Analyze language context → @@ -31,24 +32,25 @@ // Integrates with VS Code file system and editor APIs // -import { ClangdContext } from '../clangd-context'; -import { PairCoordinator } from './coordinator'; -import { PairCreatorService } from './service'; -import { PairCreatorUI } from './ui'; +import {ClangdContext} from '../clangd-context'; -// Registers the create source/header pair command with the VS Code extension context -// Uses dependency injection to create properly configured instances +import {PairCoordinator} from './coordinator'; +import {PairCreatorService} from './service'; +import {PairCreatorUI} from './ui'; + +// Registers the create source/header pair command with the VS Code extension +// context Uses dependency injection to create properly configured instances export function registerCreateSourceHeaderPairCommand(context: ClangdContext) { - // Create instances with proper dependencies - const service = new PairCreatorService(); - const ui = new PairCreatorUI(service); - const coordinator = new PairCoordinator(service, ui); + // Create instances with proper dependencies + const service = new PairCreatorService(); + const ui = new PairCreatorUI(service); + const coordinator = new PairCoordinator(service, ui); - context.subscriptions.push(coordinator); + context.subscriptions.push(coordinator); } // Re-export main types and classes for external usage -export { PairCoordinator } from './coordinator'; -export { PairCreatorService } from './service'; -export { PairCreatorUI } from './ui'; -export { Language, TemplateKey } from './templates'; +export {PairCoordinator} from './coordinator'; +export {PairCreatorService} from './service'; +export {PairCreatorUI} from './ui'; +export {Language, TemplateKey} from './templates'; diff --git a/src/create-source-header-pair/service.ts b/src/create-source-header-pair/service.ts index 0b2f37cb..89a4f545 100644 --- a/src/create-source-header-pair/service.ts +++ b/src/create-source-header-pair/service.ts @@ -10,443 +10,450 @@ import * as path from 'path'; import * as vscode from 'vscode'; -import { PairingRule, PairingRuleService } from '../pairing-rule-manager'; +import {PairingRule, PairingRuleService} from '../pairing-rule-manager'; + import { - Language, - TemplateKey, - DEFAULT_PLACEHOLDERS, - FILE_TEMPLATES + DEFAULT_PLACEHOLDERS, + FILE_TEMPLATES, + Language, + TemplateKey } from './templates'; // Service Layer - Core business logic export class PairCreatorService { - // Cache for expensive file system operations with TTL - private static readonly fileStatCache = new Map>(); - private static readonly CACHE_TTL = 5000; // 5 seconds - - // Definitive file extensions for fast lookup - private static readonly DEFINITIVE_EXTENSIONS = { - c: new Set(['.c']), - cpp: new Set(['.cpp', '.cc', '.cxx', '.hh', '.hpp', '.hxx']) - } as const; - - /** - * Creates file paths for header and source files - * @param targetDirectory Target directory URI - * @param fileName Base file name without extension - * @param rule Pairing rule with extensions - * @returns Object with headerPath and sourcePath URIs - */ - public createFilePaths(targetDirectory: vscode.Uri, fileName: string, rule: PairingRule): { - headerPath: vscode.Uri; - sourcePath: vscode.Uri; - } { - return { - headerPath: vscode.Uri.file( - path.join(targetDirectory.fsPath, `${fileName}${rule.headerExt}`)), - sourcePath: vscode.Uri.file( - path.join(targetDirectory.fsPath, `${fileName}${rule.sourceExt}`)) - }; + // Cache for expensive file system operations with TTL + private static readonly fileStatCache = new Map>(); + private static readonly CACHE_TTL = 5000; // 5 seconds + + // Definitive file extensions for fast lookup + private static readonly DEFINITIVE_EXTENSIONS = { + c: new Set(['.c']), + cpp: new Set(['.cpp', '.cc', '.cxx', '.hh', '.hpp', '.hxx']) + } as const; + + /** + * Creates file paths for header and source files + * @param targetDirectory Target directory URI + * @param fileName Base file name without extension + * @param rule Pairing rule with extensions + * @returns Object with headerPath and sourcePath URIs + */ + public createFilePaths(targetDirectory: vscode.Uri, fileName: string, + rule: PairingRule): + {headerPath: vscode.Uri; sourcePath: vscode.Uri;} { + return { + headerPath: vscode.Uri.file( + path.join(targetDirectory.fsPath, `${fileName}${rule.headerExt}`)), + sourcePath: vscode.Uri.file( + path.join(targetDirectory.fsPath, `${fileName}${rule.sourceExt}`)) + }; + } + + // Optimized file existence check with caching to improve performance + private static async fileExists(filePath: string): Promise { + if (this.fileStatCache.has(filePath)) { + return this.fileStatCache.get(filePath)!; } - // Optimized file existence check with caching to improve performance - private static async fileExists(filePath: string): Promise { - if (this.fileStatCache.has(filePath)) { - return this.fileStatCache.get(filePath)!; - } - - const promise = Promise.resolve( - vscode.workspace.fs.stat(vscode.Uri.file(filePath)) - .then(() => true, () => false) - ); - - this.fileStatCache.set(filePath, promise); - - // Auto-clear cache entry after TTL to prevent memory leaks - setTimeout(() => this.fileStatCache.delete(filePath), this.CACHE_TTL); - - return promise; + const promise = + Promise.resolve(vscode.workspace.fs.stat(vscode.Uri.file(filePath)) + .then(() => true, () => false)); + + this.fileStatCache.set(filePath, promise); + + // Auto-clear cache entry after TTL to prevent memory leaks + setTimeout(() => this.fileStatCache.delete(filePath), this.CACHE_TTL); + + return promise; + } + + // Detects programming language from VS Code editor context + // ARCHITECTURE NOTE: This method accesses VS Code APIs but belongs in service + // layer because it contains the business logic for determining language + // context. UI layer should only handle user interactions, not business + // decisions. + public async detectLanguageFromEditor(): + Promise<{language: Language, uncertain: boolean}> { + const activeEditor = vscode.window.activeTextEditor; + const languageId = activeEditor?.document?.languageId; + const filePath = activeEditor?.document && !activeEditor.document.isUntitled + ? activeEditor.document.uri.fsPath + : undefined; + + return this.detectLanguage(languageId, filePath); + } + + // Detects programming language from file info (pure business logic) + // DETECTION STRATEGY: + // 1. Fast path: Check file extension against definitive extension sets + // - .c files are definitely C + // - .cpp/.cc/.cxx/.hh/.hpp/.hxx are definitely C++ + // 2. Special case: .h files are ambiguous (could be C or C++) + // - Look for companion files in same directory to determine context + // - If MyClass.h exists with MyClass.cpp -> C++ + // - If utils.h exists with utils.c -> C + // 3. Fallback: Use VS Code's language ID detection + // - Based on file content analysis or user settings + // 4. Default: When all else fails, assume C++ (more common in modern + // development) + public async detectLanguage(languageId?: string, filePath?: string): + Promise<{language: Language, uncertain: boolean}> { + if (!languageId || !filePath) { + return {language: 'cpp', uncertain: true}; } - // Detects programming language from VS Code editor context - // ARCHITECTURE NOTE: This method accesses VS Code APIs but belongs in service layer - // because it contains the business logic for determining language context. - // UI layer should only handle user interactions, not business decisions. - public async detectLanguageFromEditor(): - Promise<{ language: Language, uncertain: boolean }> { - const activeEditor = vscode.window.activeTextEditor; - const languageId = activeEditor?.document?.languageId; - const filePath = activeEditor?.document && !activeEditor.document.isUntitled - ? activeEditor.document.uri.fsPath - : undefined; - - return this.detectLanguage(languageId, filePath); - } + const ext = path.extname(filePath); - // Detects programming language from file info (pure business logic) - // DETECTION STRATEGY: - // 1. Fast path: Check file extension against definitive extension sets - // - .c files are definitely C - // - .cpp/.cc/.cxx/.hh/.hpp/.hxx are definitely C++ - // 2. Special case: .h files are ambiguous (could be C or C++) - // - Look for companion files in same directory to determine context - // - If MyClass.h exists with MyClass.cpp -> C++ - // - If utils.h exists with utils.c -> C - // 3. Fallback: Use VS Code's language ID detection - // - Based on file content analysis or user settings - // 4. Default: When all else fails, assume C++ (more common in modern development) - public async detectLanguage(languageId?: string, filePath?: string): - Promise<{ language: Language, uncertain: boolean }> { - if (!languageId || !filePath) { - return { language: 'cpp', uncertain: true }; - } - - const ext = path.extname(filePath); - - // Fast path for definitive extensions - if (PairCreatorService.DEFINITIVE_EXTENSIONS.c.has(ext)) { - return { language: 'c', uncertain: false }; - } - if (PairCreatorService.DEFINITIVE_EXTENSIONS.cpp.has(ext)) { - return { language: 'cpp', uncertain: false }; - } - - // Special handling for .h files with companion file detection - if (ext === '.h') { - const result = await this.detectLanguageForHeaderFile(filePath); - if (result) - return result; - } - - // Fallback to language ID - return { language: languageId === 'c' ? 'c' : 'cpp', uncertain: true }; + // Fast path for definitive extensions + if (PairCreatorService.DEFINITIVE_EXTENSIONS.c.has(ext)) { + return {language: 'c', uncertain: false}; } - - // Optimized header file language detection by checking companion files - // HEADER FILE DETECTION STRATEGY: - // Problem: .h files are used by both C and C++, making language detection ambiguous - // Solution: Look for companion source files in the same directory - // - // Algorithm: - // 1. Extract base name from header file (e.g., "utils" from "utils.h") - // 2. Check for C companion first (utils.c) - early exit optimization - // - C projects are less common, so checking first allows quick determination - // 3. Check for C++ companions in parallel (utils.cpp, utils.cc, utils.cxx) - // - Use Promise.all for concurrent file existence checks - // 4. Return definitive result if companion found, otherwise default to C++ - // - // Examples: - // - math.h + math.c exists → Detected as C language - // - Vector.h + Vector.cpp exists → Detected as C++ language - // - standalone.h (no companions) → Default to C++ (uncertain=true) - private async detectLanguageForHeaderFile(filePath: string): - Promise<{ language: Language, uncertain: boolean } | null> { - const baseName = path.basename(filePath, '.h'); - const dirPath = path.dirname(filePath); - - // Check for C companion file first (less common, check first for early exit) - const cFile = path.join(dirPath, `${baseName}.c`); - if (await PairCreatorService.fileExists(cFile)) { - return { language: 'c', uncertain: false }; - } - - // Check for C++ companion files in parallel - const cppExtensions = ['.cpp', '.cc', '.cxx']; - const cppChecks = - cppExtensions.map(ext => PairCreatorService.fileExists( - path.join(dirPath, `${baseName}${ext}`))); - - const results = await Promise.all(cppChecks); - if (results.some((exists: boolean) => exists)) { - return { language: 'cpp', uncertain: false }; - } - - return { language: 'cpp', uncertain: true }; + if (PairCreatorService.DEFINITIVE_EXTENSIONS.cpp.has(ext)) { + return {language: 'cpp', uncertain: false}; } - // Gets all available pairing rules (custom + workspace + user) - public getAllPairingRules(): PairingRule[] { - return [ - ...(PairingRuleService.getRules('workspace') ?? []), - ...(PairingRuleService.getRules('user') ?? []) - ]; + // Special handling for .h files with companion file detection + if (ext === '.h') { + const result = await this.detectLanguageForHeaderFile(filePath); + if (result) + return result; } - // Gets custom C++ extensions if available from configuration - public getCustomCppExtensions(): { headerExt: string, sourceExt: string } | null { - const allRules = this.getAllPairingRules(); - const cppCustomRule = - allRules.find((rule: PairingRule) => rule.language === 'cpp'); - return cppCustomRule ? { - headerExt: cppCustomRule.headerExt, - sourceExt: cppCustomRule.sourceExt - } - : null; + // Fallback to language ID + return {language: languageId === 'c' ? 'c' : 'cpp', uncertain: true}; + } + + // Optimized header file language detection by checking companion files + // HEADER FILE DETECTION STRATEGY: + // Problem: .h files are used by both C and C++, making language detection + // ambiguous Solution: Look for companion source files in the same directory + // + // Algorithm: + // 1. Extract base name from header file (e.g., "utils" from "utils.h") + // 2. Check for C companion first (utils.c) - early exit optimization + // - C projects are less common, so checking first allows quick + // determination + // 3. Check for C++ companions in parallel (utils.cpp, utils.cc, utils.cxx) + // - Use Promise.all for concurrent file existence checks + // 4. Return definitive result if companion found, otherwise default to C++ + // + // Examples: + // - math.h + math.c exists → Detected as C language + // - Vector.h + Vector.cpp exists → Detected as C++ language + // - standalone.h (no companions) → Default to C++ (uncertain=true) + private async detectLanguageForHeaderFile(filePath: string): + Promise<{language: Language, uncertain: boolean}|null> { + const baseName = path.basename(filePath, '.h'); + const dirPath = path.dirname(filePath); + + // Check for C companion file first (less common, check first for early + // exit) + const cFile = path.join(dirPath, `${baseName}.c`); + if (await PairCreatorService.fileExists(cFile)) { + return {language: 'c', uncertain: false}; } - // Converts string to PascalCase efficiently (pure function) - public toPascalCase(input: string): string { - return input.split(/[-_]+/) - .map(word => word.charAt(0).toUpperCase() + word.slice(1)) - .join(''); - } + // Check for C++ companion files in parallel + const cppExtensions = ['.cpp', '.cc', '.cxx']; + const cppChecks = + cppExtensions.map(ext => PairCreatorService.fileExists( + path.join(dirPath, `${baseName}${ext}`))); - // Generates header guard macro based on filename and extension - private generateHeaderGuard(fileName: string, headerExt: string): string { - // Remove leading dot from extension and convert to uppercase - const extPart = headerExt.replace(/^\./, '').toUpperCase(); - return `${fileName.toUpperCase()}_${extPart}_`; + const results = await Promise.all(cppChecks); + if (results.some((exists: boolean) => exists)) { + return {language: 'cpp', uncertain: false}; } - // Generates file content with improved template selection - public generateFileContent(fileName: string, eol: string, rule: PairingRule): { headerContent: string; sourceContent: string; } { - const templateKey: TemplateKey = - rule.isClass ? 'CPP_CLASS' - : rule.isStruct ? (rule.language === 'cpp' ? 'CPP_STRUCT' : 'C_STRUCT') - : rule.language === 'c' ? 'C_EMPTY' - : 'CPP_EMPTY'; - - const templates = FILE_TEMPLATES[templateKey]; - const context = { - fileName, - headerGuard: this.generateHeaderGuard(fileName, rule.headerExt), - includeLine: `#include "${fileName}${rule.headerExt}"` - }; - - const headerContent = this.applyTemplate(templates.header, context); - const sourceContent = this.applyTemplate(templates.source, context); - - return { - headerContent: headerContent.replace(/\n/g, eol), - sourceContent: sourceContent.replace(/\n/g, eol) - }; + + return {language: 'cpp', uncertain: true}; + } + + // Gets all available pairing rules (custom + workspace + user) + public getAllPairingRules(): PairingRule[] { + return [ + ...(PairingRuleService.getRules('workspace') ?? []), + ...(PairingRuleService.getRules('user') ?? []) + ]; + } + + // Gets custom C++ extensions if available from configuration + public getCustomCppExtensions(): {headerExt: string, sourceExt: string}|null { + const allRules = this.getAllPairingRules(); + const cppCustomRule = + allRules.find((rule: PairingRule) => rule.language === 'cpp'); + return cppCustomRule ? { + headerExt: cppCustomRule.headerExt, + sourceExt: cppCustomRule.sourceExt } - // Gets default placeholder based on rule type (pure function) - public getDefaultPlaceholder(rule: PairingRule): string { - if (rule.isClass) { - return DEFAULT_PLACEHOLDERS.CPP_CLASS; - } - - if (rule.isStruct) { - return rule.language === 'cpp' ? DEFAULT_PLACEHOLDERS.CPP_STRUCT - : DEFAULT_PLACEHOLDERS.C_STRUCT; - } - - return rule.language === 'c' ? DEFAULT_PLACEHOLDERS.C_EMPTY - : DEFAULT_PLACEHOLDERS.CPP_EMPTY; + : null; + } + + // Converts string to PascalCase efficiently (pure function) + public toPascalCase(input: string): string { + return input.split(/[-_]+/) + .map(word => word.charAt(0).toUpperCase() + word.slice(1)) + .join(''); + } + + // Generates header guard macro based on filename and extension + private generateHeaderGuard(fileName: string, headerExt: string): string { + // Remove leading dot from extension and convert to uppercase + const extPart = headerExt.replace(/^\./, '').toUpperCase(); + return `${fileName.toUpperCase()}_${extPart}_`; + } + // Generates file content with improved template selection + public generateFileContent(fileName: string, eol: string, rule: PairingRule): + {headerContent: string; sourceContent: string;} { + const templateKey: TemplateKey = + rule.isClass ? 'CPP_CLASS' + : rule.isStruct ? (rule.language === 'cpp' ? 'CPP_STRUCT' : 'C_STRUCT') + : rule.language === 'c' ? 'C_EMPTY' + : 'CPP_EMPTY'; + + const templates = FILE_TEMPLATES[templateKey]; + const context = { + fileName, + headerGuard: this.generateHeaderGuard(fileName, rule.headerExt), + includeLine: `#include "${fileName}${rule.headerExt}"` + }; + + const headerContent = this.applyTemplate(templates.header, context); + const sourceContent = this.applyTemplate(templates.source, context); + + return { + headerContent: headerContent.replace(/\n/g, eol), + sourceContent: sourceContent.replace(/\n/g, eol) + }; + } + // Gets default placeholder based on rule type (pure function) + public getDefaultPlaceholder(rule: PairingRule): string { + if (rule.isClass) { + return DEFAULT_PLACEHOLDERS.CPP_CLASS; } - // Optimized line ending detection based on VS Code settings and platform - public getLineEnding(): string { - const eolSetting = - vscode.workspace.getConfiguration('files').get('eol'); - - return eolSetting === '\n' || eolSetting === '\r\n' ? eolSetting - : process.platform === 'win32' ? '\r\n' - : '\n'; + if (rule.isStruct) { + return rule.language === 'cpp' ? DEFAULT_PLACEHOLDERS.CPP_STRUCT + : DEFAULT_PLACEHOLDERS.C_STRUCT; } - - - - // Optimized template variable substitution using regex replacement - private applyTemplate(template: string, - context: Record): string { - // Pre-compile regex for better performance if used frequently - return template.replace(/\{\{(\w+)\}\}/g, (_, key) => context[key] ?? ''); + return rule.language === 'c' ? DEFAULT_PLACEHOLDERS.C_EMPTY + : DEFAULT_PLACEHOLDERS.CPP_EMPTY; + } + + // Optimized line ending detection based on VS Code settings and platform + public getLineEnding(): string { + const eolSetting = + vscode.workspace.getConfiguration('files').get('eol'); + + return eolSetting === '\n' || eolSetting === '\r\n' ? eolSetting + : process.platform === 'win32' ? '\r\n' + : '\n'; + } + + // Optimized template variable substitution using regex replacement + private applyTemplate(template: string, + context: Record): string { + // Pre-compile regex for better performance if used frequently + return template.replace(/\{\{(\w+)\}\}/g, (_, key) => context[key] ?? ''); + } + + // File existence check with parallel processing for multiple files + public async checkFileExistence(headerPath: vscode.Uri, + sourcePath: vscode.Uri): + Promise { + const checks = [headerPath, sourcePath].map(async (uri) => { + try { + await vscode.workspace.fs.stat(uri); + return uri.fsPath; + } catch { + return null; + } + }); + + const results = await Promise.all(checks); + return results.find(path => path !== null) ?? null; + } + + // Optimized file writing with error handling and parallel writes + public async writeFiles(headerPath: vscode.Uri, sourcePath: vscode.Uri, + headerContent: string, + sourceContent: string): Promise { + try { + await Promise.all([ + vscode.workspace.fs.writeFile(headerPath, + Buffer.from(headerContent, 'utf8')), + vscode.workspace.fs.writeFile(sourcePath, + Buffer.from(sourceContent, 'utf8')) + ]); + } catch (error) { + throw new Error(`Failed to create files: ${ + error instanceof Error ? error.message : 'Unknown error'}`); } - - // File existence check with parallel processing for multiple files - public async checkFileExistence(headerPath: vscode.Uri, - sourcePath: vscode.Uri): - Promise { - const checks = [headerPath, sourcePath].map(async (uri) => { - try { - await vscode.workspace.fs.stat(uri); - return uri.fsPath; - } catch { - return null; - } - }); - - const results = await Promise.all(checks); - return results.find(path => path !== null) ?? null; + } + + // Smart target directory detection (pure business logic) + public async getTargetDirectory(activeDocumentPath?: string, + workspaceFolders + ?: readonly vscode.WorkspaceFolder[]): + Promise { + // Prefer current file's directory + if (activeDocumentPath) { + return vscode.Uri.file(path.dirname(activeDocumentPath)); } - // Optimized file writing with error handling and parallel writes - public async writeFiles(headerPath: vscode.Uri, sourcePath: vscode.Uri, - headerContent: string, - sourceContent: string): Promise { - try { - await Promise.all([ - vscode.workspace.fs.writeFile(headerPath, - Buffer.from(headerContent, 'utf8')), - vscode.workspace.fs.writeFile(sourcePath, - Buffer.from(sourceContent, 'utf8')) - ]); - } catch (error) { - throw new Error(`Failed to create files: ${error instanceof Error ? error.message : 'Unknown error'}`); - } + // Return single workspace folder directly + if (workspaceFolders?.length === 1) { + return workspaceFolders[0].uri; } - // Smart target directory detection (pure business logic) - public async getTargetDirectory(activeDocumentPath?: string, - workspaceFolders - ?: readonly vscode.WorkspaceFolder[]): - Promise { - // Prefer current file's directory - if (activeDocumentPath) { - return vscode.Uri.file(path.dirname(activeDocumentPath)); - } - - // Return single workspace folder directly - if (workspaceFolders?.length === 1) { - return workspaceFolders[0].uri; - } - - // Multiple workspace folders require UI selection - return undefined; + // Multiple workspace folders require UI selection + return undefined; + } + + // Language mismatch warning logic (pure business logic) + public async shouldShowLanguageMismatchWarning(language: Language, + result: PairingRule, + currentDir?: string, + activeFilePath + ?: string): Promise { + if (!currentDir || !activeFilePath) { + return true; } - // Language mismatch warning logic (pure business logic) - public async shouldShowLanguageMismatchWarning(language: Language, - result: PairingRule, - currentDir?: string, - activeFilePath - ?: string): Promise { - if (!currentDir || !activeFilePath) { - return true; - } - - if (language === 'c' && result.language === 'cpp') { - return this.checkForCppFilesInDirectory(currentDir); - } - - return this.checkForCorrespondingSourceFiles(currentDir, activeFilePath, - language); + if (language === 'c' && result.language === 'cpp') { + return this.checkForCppFilesInDirectory(currentDir); } - // Check for C++ files in directory to inform language mismatch warnings - private async checkForCppFilesInDirectory(dirPath: string): Promise { - try { - const entries = - await vscode.workspace.fs.readDirectory(vscode.Uri.file(dirPath)); - const hasCppFiles = - entries.some(([fileName, fileType]) => - fileType === vscode.FileType.File && - PairCreatorService.DEFINITIVE_EXTENSIONS.cpp.has( - path.extname(fileName))); - return !hasCppFiles; // Show warning if NO C++ files found - } catch { - return true; // Show warning if can't check - } + return this.checkForCorrespondingSourceFiles(currentDir, activeFilePath, + language); + } + + // Check for C++ files in directory to inform language mismatch warnings + private async checkForCppFilesInDirectory(dirPath: string): Promise { + try { + const entries = + await vscode.workspace.fs.readDirectory(vscode.Uri.file(dirPath)); + const hasCppFiles = + entries.some(([fileName, fileType]) => + fileType === vscode.FileType.File && + PairCreatorService.DEFINITIVE_EXTENSIONS.cpp.has( + path.extname(fileName))); + return !hasCppFiles; // Show warning if NO C++ files found + } catch { + return true; // Show warning if can't check + } + } + + // Check for corresponding source files to inform language mismatch warnings + private async checkForCorrespondingSourceFiles( + dirPath: string, filePath: string, language: Language): Promise { + const baseName = path.basename(filePath, path.extname(filePath)); + const extensions = language === 'c' ? ['.c'] : ['.cpp', '.cc', '.cxx']; + + const checks = extensions.map(ext => PairCreatorService.fileExists( + path.join(dirPath, `${baseName}${ext}`))); + + try { + const results = await Promise.all(checks); + return !results.some( + (exists: boolean) => + exists); // Show warning if NO corresponding files found + } catch { + return true; // Show warning if can't check + } + } + + /** + * Checks if should offer to save rule as default and handles the save process + */ + public async handleOfferToSaveAsDefault(rule: PairingRule, + language: 'c'|'cpp'): Promise { + // Only offer for C++ rules + if (language !== 'cpp') { + return; } - // Check for corresponding source files to inform language mismatch warnings - private async checkForCorrespondingSourceFiles( - dirPath: string, filePath: string, language: Language): Promise { - const baseName = path.basename(filePath, path.extname(filePath)); - const extensions = language === 'c' ? ['.c'] : ['.cpp', '.cc', '.cxx']; - - const checks = extensions.map(ext => PairCreatorService.fileExists( - path.join(dirPath, `${baseName}${ext}`))); - - try { - const results = await Promise.all(checks); - return !results.some( - (exists: boolean) => exists); // Show warning if NO corresponding files found - } catch { - return true; // Show warning if can't check - } + // Check if user already has custom C++ rules configured + const customRules = this.getAllPairingRules(); + const hasCppCustomRules = customRules.some(r => r.language === 'cpp'); + if (hasCppCustomRules) { + return; // Don't prompt if they already have C++ configuration } - /** - * Checks if should offer to save rule as default and handles the save process - */ - public async handleOfferToSaveAsDefault(rule: PairingRule, language: 'c' | 'cpp'): Promise { - // Only offer for C++ rules - if (language !== 'cpp') { - return; - } - - // Check if user already has custom C++ rules configured - const customRules = this.getAllPairingRules(); - const hasCppCustomRules = customRules.some(r => r.language === 'cpp'); - if (hasCppCustomRules) { - return; // Don't prompt if they already have C++ configuration - } - - // Check if this is a custom rule (has _custom suffix means user went through extension selection) - const isCustomRule = rule.key.includes('custom'); - if (!isCustomRule) { - return; // Don't prompt for built-in rules - } - - const choice = await vscode.window.showInformationMessage( - `Files created successfully! Would you like to save "${rule.headerExt}/${rule.sourceExt}" as your default C++ extensions for this workspace?`, - 'Save as Default', - 'Not Now' - ); - - if (choice === 'Save as Default') { - await this.saveRuleAsDefault(rule); - } + // Check if this is a custom rule (has _custom suffix means user went + // through extension selection) + const isCustomRule = rule.key.includes('custom'); + if (!isCustomRule) { + return; // Don't prompt for built-in rules } + const choice = await vscode.window.showInformationMessage( + `Files created successfully! Would you like to save "${ + rule.headerExt}/${ + rule.sourceExt}" as your default C++ extensions for this workspace?`, + 'Save as Default', 'Not Now'); - // Saves a rule as the default configuration with user choice of scope - public async saveRuleAsDefault(rule: PairingRule): Promise { - const { PairingRuleService } = await import('../pairing-rule-manager'); - - const scopeChoice = await vscode.window.showQuickPick([ - { - label: 'Save for this Workspace', - description: 'Recommended. Creates a .vscode/settings.json file.', - scope: 'workspace' - }, - { - label: 'Save for all my Projects (Global)', - description: 'Modifies your global user settings.', - scope: 'user' - } - ], { - placeHolder: 'Where would you like to save this configuration?', - title: 'Save Configuration Scope' + if (choice === 'Save as Default') { + await this.saveRuleAsDefault(rule); + } + } + + // Saves a rule as the default configuration with user choice of scope + public async saveRuleAsDefault(rule: PairingRule): Promise { + const {PairingRuleService} = await import('../pairing-rule-manager'); + + const scopeChoice = await vscode.window.showQuickPick( + [ + { + label: 'Save for this Workspace', + description: 'Recommended. Creates a .vscode/settings.json file.', + scope: 'workspace' + }, + { + label: 'Save for all my Projects (Global)', + description: 'Modifies your global user settings.', + scope: 'user' + } + ], + { + placeHolder: 'Where would you like to save this configuration?', + title: 'Save Configuration Scope' }); - if (!scopeChoice) { - return; - } - - try { - // Create a clean rule for saving (remove the 'custom' key suffix) - const cleanRule: PairingRule = { - ...rule, - key: rule.key.replace('_custom', ''), - label: `${rule.language.toUpperCase()} Pair (${rule.headerExt}/${rule.sourceExt})`, - description: `Creates a ${rule.headerExt}/${rule.sourceExt} file pair with header guards.` - }; - - await PairingRuleService.writeRules([cleanRule], scopeChoice.scope as 'workspace' | 'user'); - - vscode.window.showInformationMessage( - `Successfully saved '${rule.headerExt}/${rule.sourceExt}' as the default extension for ${scopeChoice.scope === 'workspace' ? 'this workspace' : 'all projects'}.` - ); - } catch (error) { - vscode.window.showErrorMessage( - `Failed to save configuration: ${error instanceof Error ? error.message : 'Unknown error'}` - ); - } + if (!scopeChoice) { + return; } - // Generates file content and writes both header and source files - public async generateAndWriteFiles( - fileName: string, - rule: PairingRule, - headerPath: vscode.Uri, - sourcePath: vscode.Uri - ): Promise { - const eol = this.getLineEnding(); - const { headerContent, sourceContent } = this.generateFileContent(fileName, eol, rule); - await this.writeFiles(headerPath, sourcePath, headerContent, sourceContent); + try { + // Create a clean rule for saving (remove the 'custom' key suffix) + const cleanRule: PairingRule = { + ...rule, + key: rule.key.replace('_custom', ''), + label: `${rule.language.toUpperCase()} Pair (${rule.headerExt}/${ + rule.sourceExt})`, + description: `Creates a ${rule.headerExt}/${ + rule.sourceExt} file pair with header guards.` + }; + + await PairingRuleService.writeRules( + [cleanRule], scopeChoice.scope as 'workspace' | 'user'); + + vscode.window.showInformationMessage(`Successfully saved '${ + rule.headerExt}/${rule.sourceExt}' as the default extension for ${ + scopeChoice.scope === 'workspace' ? 'this workspace' + : 'all projects'}.`); + } catch (error) { + vscode.window.showErrorMessage(`Failed to save configuration: ${ + error instanceof Error ? error.message : 'Unknown error'}`); } + } + + // Generates file content and writes both header and source files + public async generateAndWriteFiles(fileName: string, rule: PairingRule, + headerPath: vscode.Uri, + sourcePath: vscode.Uri): Promise { + const eol = this.getLineEnding(); + const {headerContent, sourceContent} = + this.generateFileContent(fileName, eol, rule); + await this.writeFiles(headerPath, sourcePath, headerContent, sourceContent); + } } diff --git a/src/create-source-header-pair/templates.ts b/src/create-source-header-pair/templates.ts index dc82cd8c..826d4e0f 100644 --- a/src/create-source-header-pair/templates.ts +++ b/src/create-source-header-pair/templates.ts @@ -9,79 +9,80 @@ // - Validation patterns // -import { PairingRule } from '../pairing-rule-manager'; +import {PairingRule} from '../pairing-rule-manager'; // Types for better type safety -export type Language = 'c' | 'cpp'; -export type TemplateKey = 'CPP_CLASS' | 'CPP_STRUCT' | 'C_STRUCT' | 'C_EMPTY' | 'CPP_EMPTY'; +export type Language = 'c'|'cpp'; +export type TemplateKey = + 'CPP_CLASS'|'CPP_STRUCT'|'C_STRUCT'|'C_EMPTY'|'CPP_EMPTY'; // Regular expression patterns to validate C/C++ identifiers export const VALIDATION_PATTERNS = { - IDENTIFIER: /^[a-zA-Z_][a-zA-Z0-9_]*$/ + IDENTIFIER: /^[a-zA-Z_][a-zA-Z0-9_]*$/ }; // Default placeholder names for different file types export const DEFAULT_PLACEHOLDERS = { - C_EMPTY: 'my_c_functions', - C_STRUCT: 'MyStruct', - CPP_EMPTY: 'utils', - CPP_CLASS: 'MyClass', - CPP_STRUCT: 'MyStruct' + C_EMPTY: 'my_c_functions', + C_STRUCT: 'MyStruct', + CPP_EMPTY: 'utils', + CPP_CLASS: 'MyClass', + CPP_STRUCT: 'MyStruct' }; // Template rules for available file pair types export const TEMPLATE_RULES: PairingRule[] = [ - { - key: 'cpp_empty', - label: '$(new-file) C++ Pair', - description: 'Creates a basic Header/Source file pair with header guards.', - language: 'cpp' as const, - headerExt: '.h', - sourceExt: '.cpp' - }, - { - key: 'cpp_class', - label: '$(symbol-class) C++ Class', - description: - 'Creates a Header/Source file pair with a boilerplate class definition.', - language: 'cpp' as const, - headerExt: '.h', - sourceExt: '.cpp', - isClass: true - }, - { - key: 'cpp_struct', - label: '$(symbol-struct) C++ Struct', - description: - 'Creates a Header/Source file pair with a boilerplate struct definition.', - language: 'cpp' as const, - headerExt: '.h', - sourceExt: '.cpp', - isStruct: true - }, - { - key: 'c_empty', - label: '$(file-code) C Pair', - description: 'Creates a basic .h/.c file pair for function declarations.', - language: 'c' as const, - headerExt: '.h', - sourceExt: '.c' - }, - { - key: 'c_struct', - label: '$(symbol-struct) C Struct', - description: 'Creates a .h/.c file pair with a boilerplate typedef struct.', - language: 'c' as const, - headerExt: '.h', - sourceExt: '.c', - isStruct: true - } + { + key: 'cpp_empty', + label: '$(new-file) C++ Pair', + description: 'Creates a basic Header/Source file pair with header guards.', + language: 'cpp' as const, + headerExt: '.h', + sourceExt: '.cpp' + }, + { + key: 'cpp_class', + label: '$(symbol-class) C++ Class', + description: + 'Creates a Header/Source file pair with a boilerplate class definition.', + language: 'cpp' as const, + headerExt: '.h', + sourceExt: '.cpp', + isClass: true + }, + { + key: 'cpp_struct', + label: '$(symbol-struct) C++ Struct', + description: + 'Creates a Header/Source file pair with a boilerplate struct definition.', + language: 'cpp' as const, + headerExt: '.h', + sourceExt: '.cpp', + isStruct: true + }, + { + key: 'c_empty', + label: '$(file-code) C Pair', + description: 'Creates a basic .h/.c file pair for function declarations.', + language: 'c' as const, + headerExt: '.h', + sourceExt: '.c' + }, + { + key: 'c_struct', + label: '$(symbol-struct) C Struct', + description: 'Creates a .h/.c file pair with a boilerplate typedef struct.', + language: 'c' as const, + headerExt: '.h', + sourceExt: '.c', + isStruct: true + } ]; // File templates with immutable structure export const FILE_TEMPLATES = { - CPP_CLASS: { - header: `#ifndef {{headerGuard}} + CPP_CLASS: { + header: `#ifndef {{headerGuard}} #define {{headerGuard}} class {{fileName}} { @@ -95,7 +96,7 @@ private: #endif // {{headerGuard}} `, - source: `{{includeLine}} + source: `{{includeLine}} {{fileName}}::{{fileName}}() { // Constructor implementation @@ -105,9 +106,9 @@ private: // Destructor implementation } ` - }, - CPP_STRUCT: { - header: `#ifndef {{headerGuard}} + }, + CPP_STRUCT: { + header: `#ifndef {{headerGuard}} #define {{headerGuard}} struct {{fileName}} { @@ -116,10 +117,10 @@ struct {{fileName}} { #endif // {{headerGuard}} `, - source: '{{includeLine}}' - }, - C_STRUCT: { - header: `#ifndef {{headerGuard}} + source: '{{includeLine}}' + }, + C_STRUCT: { + header: `#ifndef {{headerGuard}} #define {{headerGuard}} typedef struct { @@ -128,29 +129,29 @@ typedef struct { #endif // {{headerGuard}} `, - source: '{{includeLine}}' - }, - C_EMPTY: { - header: `#ifndef {{headerGuard}} + source: '{{includeLine}}' + }, + C_EMPTY: { + header: `#ifndef {{headerGuard}} #define {{headerGuard}} // Declarations for {{fileName}}.c #endif // {{headerGuard}} `, - source: `{{includeLine}} + source: `{{includeLine}} // Implementations for {{fileName}}.c ` - }, - CPP_EMPTY: { - header: `#ifndef {{headerGuard}} + }, + CPP_EMPTY: { + header: `#ifndef {{headerGuard}} #define {{headerGuard}} // Declarations for {{fileName}}.cpp #endif // {{headerGuard}} `, - source: '{{includeLine}}' - } + source: '{{includeLine}}' + } }; diff --git a/src/create-source-header-pair/ui.ts b/src/create-source-header-pair/ui.ts index 1ec4e36c..86dacfb9 100644 --- a/src/create-source-header-pair/ui.ts +++ b/src/create-source-header-pair/ui.ts @@ -10,527 +10,588 @@ import * as path from 'path'; import * as vscode from 'vscode'; -import { PairingRule, PairingRuleService, PairingRuleUI } from '../pairing-rule-manager'; -import { PairCreatorService } from './service'; -import { Language, VALIDATION_PATTERNS, TEMPLATE_RULES } from './templates'; +import { + PairingRule, + PairingRuleService, + PairingRuleUI +} from '../pairing-rule-manager'; + +import {PairCreatorService} from './service'; +import {Language, TEMPLATE_RULES, VALIDATION_PATTERNS} from './templates'; // Type to clearly express user intent when selecting custom rules type CustomRuleSelection = - | { type: 'rule', rule: PairingRule } // User selected a specific rule - | { type: 'use_default' } // User wants to use default templates - | { type: 'cancelled' }; // User cancelled the operation + |{type: 'rule', rule: PairingRule} // User selected a specific rule +|{type: 'use_default'} // User wants to use default templates +|{type: 'cancelled'}; // User cancelled the operation // This type clearly expresses three distinct user intents: // - 'rule': User selected a specific pairing rule and it should be used -// - 'use_default': User explicitly chose to use default templates and should proceed to the default template selection flow -// - 'cancelled': User pressed ESC to cancel and the entire flow should be terminated +// - 'use_default': User explicitly chose to use default templates and should +// proceed to the default template selection flow +// - 'cancelled': User pressed ESC to cancel and the entire flow should be +// terminated // PairCreatorUI handles all user interface interactions for file pair creation. // It manages dialogs, input validation, and user choices. export class PairCreatorUI { - private service: PairCreatorService; - - constructor(service: PairCreatorService) { this.service = service; } - - private adaptRuleForTemplateDisplay(rule: PairingRule): vscode.QuickPickItem & PairingRule { - const categoryDesc = rule.isClass ? 'Includes constructor, destructor, and basic structure' - : rule.isStruct ? 'Simple data structure with member variables' - : `Basic ${rule.language.toUpperCase()} file pair with header guards`; - - return { - ...rule, - description: categoryDesc, - detail: rule.description - }; - } - - // Converts a PairingRule to a QuickPickItem for custom rules selection - private adaptRuleForCustomRulesDisplay(rule: PairingRule, category: 'custom' | 'builtin' | 'alternative'): vscode.QuickPickItem & PairingRule { - const categoryMap = { - custom: 'Custom configuration for this workspace', - builtin: 'Built-in template with custom extensions', - alternative: `Alternative ${rule.language.toUpperCase()} template option` - }; - - return { - ...rule, - description: categoryMap[category], - detail: rule.description - }; - } - - // Creates the special "Use Default Templates" option - private createUseDefaultOption(): vscode.QuickPickItem & { key: string, isSpecial: boolean } { - return { - key: 'use_default', - label: '$(list-unordered) Use Default Templates', - description: 'Ignore custom settings and use built-in defaults', - detail: 'Standard .h/.cpp extensions', - isSpecial: true - }; - } - - // Gets custom rules for the specified language - private getCustomRulesForLanguage(allRules: PairingRule[], language: 'c' | 'cpp'): PairingRule[] { - return allRules.filter((rule: PairingRule) => rule.language === language); - } - - // Gets adapted built-in templates with custom extensions - private getAdaptedBuiltinTemplates(customRules: PairingRule[], language: 'c' | 'cpp'): PairingRule[] { - const customExt = customRules.length > 0 ? customRules[0] : null; - - if (customExt && language === 'cpp') { - return TEMPLATE_RULES - .filter(template => - template.language === 'cpp' && - !customRules.some(customRule => - customRule.isClass === template.isClass && - customRule.isStruct === template.isStruct && - (customRule.isClass || customRule.isStruct || - (!customRule.isClass && !customRule.isStruct && + private service: PairCreatorService; + + constructor(service: PairCreatorService) { this.service = service; } + + private adaptRuleForTemplateDisplay(rule: PairingRule): vscode.QuickPickItem + &PairingRule { + const categoryDesc = + rule.isClass ? 'Includes constructor, destructor, and basic structure' + : rule.isStruct + ? 'Simple data structure with member variables' + : `Basic ${ + rule.language.toUpperCase()} file pair with header guards`; + + return {...rule, description: categoryDesc, detail: rule.description}; + } + + // Converts a PairingRule to a QuickPickItem for custom rules selection + private adaptRuleForCustomRulesDisplay(rule: PairingRule, category: 'custom'| + 'builtin'|'alternative'): + vscode.QuickPickItem&PairingRule { + const categoryMap = { + custom: 'Custom configuration for this workspace', + builtin: 'Built-in template with custom extensions', + alternative: `Alternative ${rule.language.toUpperCase()} template option` + }; + + return { + ...rule, + description: categoryMap[category], + detail: rule.description + }; + } + + // Creates the special "Use Default Templates" option + private createUseDefaultOption(): vscode.QuickPickItem& + {key: string, isSpecial: boolean} { + return { + key: 'use_default', + label: '$(list-unordered) Use Default Templates', + description: 'Ignore custom settings and use built-in defaults', + detail: 'Standard .h/.cpp extensions', + isSpecial: true + }; + } + + // Gets custom rules for the specified language + private getCustomRulesForLanguage(allRules: PairingRule[], + language: 'c'|'cpp'): PairingRule[] { + return allRules.filter((rule: PairingRule) => rule.language === language); + } + + // Gets adapted built-in templates with custom extensions + private getAdaptedBuiltinTemplates(customRules: PairingRule[], + language: 'c'|'cpp'): PairingRule[] { + const customExt = customRules.length > 0 ? customRules[0] : null; + + if (customExt && language === 'cpp') { + return TEMPLATE_RULES + .filter(template => + template.language === 'cpp' && + !customRules.some( + customRule => + customRule.isClass === template.isClass && + customRule.isStruct === template.isStruct && + (customRule.isClass || customRule.isStruct || + (!customRule.isClass && !customRule.isStruct && !template.isClass && !template.isStruct)))) - .map(template => ({ - ...template, - key: `${template.key}_adapted`, - headerExt: customExt.headerExt, - sourceExt: customExt.sourceExt, - description: template.description - .replace(/Header\/Source/g, `${customExt.headerExt}/${customExt.sourceExt}`) - .replace(/\.h\/\.cpp/g, `${customExt.headerExt}/${customExt.sourceExt}`) - .replace(/basic \.h\/\.cpp/g, `basic ${customExt.headerExt}/${customExt.sourceExt}`) - .replace(/Creates a \.h\/\.cpp/g, `Creates a ${customExt.headerExt}/${customExt.sourceExt}`) - })); - } else { - return TEMPLATE_RULES - .filter(template => - template.language === language && - !customRules.some(customRule => - customRule.headerExt === template.headerExt && - customRule.sourceExt === template.sourceExt && - customRule.isClass === template.isClass && - customRule.isStruct === template.isStruct)) - .map(template => this.adaptRuleForDisplay(template)); - } - } - - // Gets cross-language template options - private getCrossLanguageTemplates(language: 'c' | 'cpp'): PairingRule[] { - return TEMPLATE_RULES - .filter(template => template.language !== language) - .map(template => this.adaptRuleForDisplay(template)); + .map(template => ({ + ...template, + key: `${template.key}_adapted`, + headerExt: customExt.headerExt, + sourceExt: customExt.sourceExt, + description: + template.description + .replace(/Header\/Source/g, `${customExt.headerExt}/${ + customExt.sourceExt}`) + .replace(/\.h\/\.cpp/g, `${customExt.headerExt}/${ + customExt.sourceExt}`) + .replace(/basic \.h\/\.cpp/g, + `basic ${customExt.headerExt}/${ + customExt.sourceExt}`) + .replace(/Creates a \.h\/\.cpp/g, + `Creates a ${customExt.headerExt}/${ + customExt.sourceExt}`) + })); + } else { + return TEMPLATE_RULES + .filter(template => + template.language === language && + !customRules.some( + customRule => + customRule.headerExt === template.headerExt && + customRule.sourceExt === template.sourceExt && + customRule.isClass === template.isClass && + customRule.isStruct === template.isStruct)) + .map(template => this.adaptRuleForDisplay(template)); } - - // Cleans up custom rules with proper labels and descriptions - private cleanCustomRules(rules: PairingRule[]): PairingRule[] { - return rules.map(rule => ({ - ...rule, - label: rule.label.includes('$(') ? rule.label - : `$(new-file) ${rule.language === 'cpp' ? 'C++' : 'C'} Pair (${rule.headerExt}/${rule.sourceExt})`, - description: rule.description.startsWith('Creates a') ? rule.description - : `Creates a ${rule.headerExt}/${rule.sourceExt} file pair with header guards.` + } + + // Gets cross-language template options + private getCrossLanguageTemplates(language: 'c'|'cpp'): PairingRule[] { + return TEMPLATE_RULES.filter(template => template.language !== language) + .map(template => this.adaptRuleForDisplay(template)); + } + + // Cleans up custom rules with proper labels and descriptions + private cleanCustomRules(rules: PairingRule[]): PairingRule[] { + return rules.map( + rule => ({ + ...rule, + label: rule.label.includes('$(') + ? rule.label + : `$(new-file) ${ + rule.language === 'cpp' ? 'C++' : 'C'} Pair (${ + rule.headerExt}/${rule.sourceExt})`, + description: rule.description.startsWith('Creates a') + ? rule.description + : `Creates a ${rule.headerExt}/${ + rule.sourceExt} file pair with header guards.` })); + } + + // Gets placeholder name for input dialog, considering active file context + private getPlaceholder(rule: PairingRule): string { + const activeEditor = vscode.window.activeTextEditor; + + if (activeEditor?.document && !activeEditor.document.isUntitled) { + const fileName = + path.basename(activeEditor.document.fileName, + path.extname(activeEditor.document.fileName)); + return rule.language === 'c' ? fileName + : this.service.toPascalCase(fileName); } - // Gets placeholder name for input dialog, considering active file context - private getPlaceholder(rule: PairingRule): string { - const activeEditor = vscode.window.activeTextEditor; - - if (activeEditor?.document && !activeEditor.document.isUntitled) { - const fileName = - path.basename(activeEditor.document.fileName, - path.extname(activeEditor.document.fileName)); - return rule.language === 'c' ? fileName - : this.service.toPascalCase(fileName); - } - - return this.service.getDefaultPlaceholder(rule); + return this.service.getDefaultPlaceholder(rule); + } + + // Checks if language mismatch warning should be shown with UI context + private async shouldShowLanguageMismatchWarning(language: Language, + result: PairingRule): + Promise { + const activeEditor = vscode.window.activeTextEditor; + const currentDir = + activeEditor?.document && !activeEditor.document.isUntitled + ? path.dirname(activeEditor.document.uri.fsPath) + : undefined; + const activeFilePath = + activeEditor?.document && !activeEditor.document.isUntitled + ? activeEditor.document.uri.fsPath + : undefined; + + return this.service.shouldShowLanguageMismatchWarning( + language, result, currentDir, activeFilePath); + } + + // Adapts template rules for display in UI based on custom extensions + private adaptRuleForDisplay(rule: PairingRule): PairingRule { + if (rule.language !== 'cpp') { + return rule; } - // Checks if language mismatch warning should be shown with UI context - private async shouldShowLanguageMismatchWarning(language: Language, - result: PairingRule): - Promise { - const activeEditor = vscode.window.activeTextEditor; - const currentDir = - activeEditor?.document && !activeEditor.document.isUntitled - ? path.dirname(activeEditor.document.uri.fsPath) - : undefined; - const activeFilePath = - activeEditor?.document && !activeEditor.document.isUntitled - ? activeEditor.document.uri.fsPath - : undefined; - - return this.service.shouldShowLanguageMismatchWarning( - language, result, currentDir, activeFilePath); + const customExtensions = this.service.getCustomCppExtensions(); + if (!customExtensions) { + return rule; } - // Adapts template rules for display in UI based on custom extensions - private adaptRuleForDisplay(rule: PairingRule): PairingRule { - if (rule.language !== 'cpp') { - return rule; - } + const {headerExt, sourceExt} = customExtensions; + + // Adapt description for display + const replacementPattern = + /Header\/Source|\.h(?:h|pp|xx)?\/\.c(?:pp|c|xx)?/g; + const newDescription = rule.description.replace( + replacementPattern, `${headerExt}/${sourceExt}`); + + return {...rule, description: newDescription, headerExt, sourceExt}; + } + + // Prepares template choices for UI display with proper ordering and + // adaptation + private prepareTemplateChoices(language: 'c'|'cpp', + uncertain: boolean): PairingRule[] { + const desiredOrder = + uncertain + ? ['cpp_empty', 'c_empty', 'cpp_class', 'cpp_struct', 'c_struct'] + : language === 'c' + ? ['c_empty', 'c_struct', 'cpp_empty', 'cpp_class', 'cpp_struct'] + : ['cpp_empty', 'cpp_class', 'cpp_struct', 'c_empty', 'c_struct']; + + return [...TEMPLATE_RULES] + .sort((a, b) => + desiredOrder.indexOf(a.key) - desiredOrder.indexOf(b.key)) + .map(rule => this.adaptRuleForDisplay(rule)); + } + + // Simplified function that delegates to smaller, focused functions + private prepareCustomRulesChoices(allRules: PairingRule[], + language: 'c'|'cpp'): { + languageRules: PairingRule[], + adaptedDefaultTemplates: PairingRule[], + otherLanguageTemplates: PairingRule[], + cleanedCustomRules: PairingRule[] + } { + const languageRules = this.getCustomRulesForLanguage(allRules, language); + const adaptedDefaultTemplates = + this.getAdaptedBuiltinTemplates(languageRules, language); + const otherLanguageTemplates = this.getCrossLanguageTemplates(language); + const cleanedCustomRules = this.cleanCustomRules(allRules); + + return { + languageRules, + adaptedDefaultTemplates, + otherLanguageTemplates, + cleanedCustomRules + }; + } + + // - Returns 'cancelled' if the user presses ESC to cancel, and the operation + // should be terminated + // - Returns 'use_default' if the user selects "Use Default Templates", and + // should proceed to the default template flow + // - Returns 'rule' if the user selects a specific rule, and that rule should + // be used directly + public async selectFromCustomRules(allRules: PairingRule[], language: 'c'| + 'cpp'): Promise { + const { + cleanedCustomRules, + adaptedDefaultTemplates, + otherLanguageTemplates + } = this.prepareCustomRulesChoices(allRules, language); + + // Use adapters to create consistent UI items + const choices = [ + ...cleanedCustomRules.map( + rule => this.adaptRuleForCustomRulesDisplay(rule, 'custom')), + ...adaptedDefaultTemplates.map( + rule => this.adaptRuleForCustomRulesDisplay(rule, 'builtin')), + ...otherLanguageTemplates.map( + rule => this.adaptRuleForCustomRulesDisplay(rule, 'alternative')), + this.createUseDefaultOption() + ]; + + const result = await vscode.window.showQuickPick(choices, { + placeHolder: `Select a ${language.toUpperCase()} pairing rule`, + title: 'Custom Pairing Rules Available', + matchOnDescription: true, + matchOnDetail: true + }); + + if (!result) + return {type: 'cancelled'}; // User pressed ESC or cancelled + if ('isSpecial' in result && result.isSpecial) + return {type: 'use_default'}; // User chose "Use Default Templates" + return { + type: 'rule', + rule: result as PairingRule + }; // User selected a specific rule + } + + // Shows a dialog offering to create custom pairing rules for C++. + // Only applicable for C++ since C uses standard .c/.h extensions. + // Returns true to create rules, false to dismiss, or null if cancelled. + public async offerToCreateCustomRules(language: 'c'| + 'cpp'): Promise { + if (language === 'c') + return false; + + const result = await vscode.window.showInformationMessage( + `No custom pairing rules found for C++. Would you like to create custom rules to use different file extensions (e.g., .cc/.hh instead of .cpp/.h)?`, + {modal: false}, 'Create Custom Rules', 'Dismiss'); + + return result === 'Create Custom Rules' ? true + : result === 'Dismiss' ? false + : null; + } + + // Guides the user through creating custom pairing rules for C++ + // Offers common extension combinations or allows custom input + // Saves the rule to workspace or global settings + // Returns the created custom rule or undefined if cancelled + public async createCustomRules(language: 'c'| + 'cpp'): Promise { + if (language === 'c') + return undefined; + + const commonExtensions = [ + {label: '.h / .cpp (Default)', headerExt: '.h', sourceExt: '.cpp'}, + {label: '.hh / .cc (Alternative)', headerExt: '.hh', sourceExt: '.cc'}, { + label: '.hpp / .cpp (Header Plus Plus)', + headerExt: '.hpp', + sourceExt: '.cpp' + }, + {label: '.hxx / .cxx (Extended)', headerExt: '.hxx', sourceExt: '.cxx'}, + {label: 'Custom Extensions', headerExt: '', sourceExt: ''} + ]; + + const selectedExtension = + await vscode.window.showQuickPick(commonExtensions, { + placeHolder: `Select file extensions for C++ files`, + title: 'Choose File Extensions' + }); - const customExtensions = this.service.getCustomCppExtensions(); - if (!customExtensions) { - return rule; - } + if (!selectedExtension) + return undefined; - const { headerExt, sourceExt } = customExtensions; + let {headerExt, sourceExt} = selectedExtension; - // Adapt description for display - const replacementPattern = - /Header\/Source|\.h(?:h|pp|xx)?\/\.c(?:pp|c|xx)?/g; - const newDescription = rule.description.replace( - replacementPattern, `${headerExt}/${sourceExt}`); + if (!headerExt || !sourceExt) { + const validateExt = (text: string) => + (!text || !text.startsWith('.') || text.length < 2) + ? 'Please enter a valid file extension starting with a dot (e.g., .h)' + : null; - return { ...rule, description: newDescription, headerExt, sourceExt }; - } + headerExt = await vscode.window.showInputBox({ + prompt: 'Enter header file extension (e.g., .h, .hh, .hpp)', + placeHolder: '.h', + validateInput: validateExt + }) || ''; - // Prepares template choices for UI display with proper ordering and adaptation - private prepareTemplateChoices(language: 'c' | 'cpp', - uncertain: boolean): PairingRule[] { - const desiredOrder = - uncertain - ? ['cpp_empty', 'c_empty', 'cpp_class', 'cpp_struct', 'c_struct'] - : language === 'c' - ? ['c_empty', 'c_struct', 'cpp_empty', 'cpp_class', 'cpp_struct'] - : ['cpp_empty', 'cpp_class', 'cpp_struct', 'c_empty', 'c_struct']; - - return [...TEMPLATE_RULES] - .sort((a, b) => - desiredOrder.indexOf(a.key) - desiredOrder.indexOf(b.key)) - .map(rule => this.adaptRuleForDisplay(rule)); - } + if (!headerExt) + return undefined; - // Simplified function that delegates to smaller, focused functions - private prepareCustomRulesChoices(allRules: PairingRule[], language: 'c' | 'cpp'): { - languageRules: PairingRule[], - adaptedDefaultTemplates: PairingRule[], - otherLanguageTemplates: PairingRule[], - cleanedCustomRules: PairingRule[] - } { - const languageRules = this.getCustomRulesForLanguage(allRules, language); - const adaptedDefaultTemplates = this.getAdaptedBuiltinTemplates(languageRules, language); - const otherLanguageTemplates = this.getCrossLanguageTemplates(language); - const cleanedCustomRules = this.cleanCustomRules(allRules); + sourceExt = await vscode.window.showInputBox({ + prompt: `Enter source file extension for C++ (e.g., .cpp, .cc, .cxx)`, + placeHolder: '.cpp', + validateInput: validateExt + }) || ''; - return { - languageRules, - adaptedDefaultTemplates, - otherLanguageTemplates, - cleanedCustomRules - }; + if (!sourceExt) + return undefined; } - // - Returns 'cancelled' if the user presses ESC to cancel, and the operation should be terminated - // - Returns 'use_default' if the user selects "Use Default Templates", and should proceed to the default template flow - // - Returns 'rule' if the user selects a specific rule, and that rule should be used directly - public async selectFromCustomRules(allRules: PairingRule[], language: 'c' | 'cpp'): Promise { - const { - cleanedCustomRules, - adaptedDefaultTemplates, - otherLanguageTemplates - } = this.prepareCustomRulesChoices(allRules, language); - - // Use adapters to create consistent UI items - const choices = [ - ...cleanedCustomRules.map(rule => this.adaptRuleForCustomRulesDisplay(rule, 'custom')), - ...adaptedDefaultTemplates.map(rule => this.adaptRuleForCustomRulesDisplay(rule, 'builtin')), - ...otherLanguageTemplates.map(rule => this.adaptRuleForCustomRulesDisplay(rule, 'alternative')), - this.createUseDefaultOption() - ]; - - const result = await vscode.window.showQuickPick(choices, { - placeHolder: `Select a ${language.toUpperCase()} pairing rule`, - title: 'Custom Pairing Rules Available', - matchOnDescription: true, - matchOnDetail: true + const customRule: PairingRule = { + key: `custom_cpp_${Date.now()}`, + label: `$(new-file) C++ Pair (${headerExt}/${sourceExt})`, + description: + `Creates a ${headerExt}/${sourceExt} file pair with header guards.`, + language: 'cpp', + headerExt, + sourceExt + }; + + const saveLocation = await vscode.window.showQuickPick( + [ + { + label: 'Workspace Settings', + description: 'Save to current workspace only', + value: 'workspace' + }, + { + label: 'Global Settings', + description: 'Save to user settings (available in all workspaces)', + value: 'user' + } + ], + { + placeHolder: 'Where would you like to save this custom rule?', + title: 'Save Location' }); - if (!result) return { type: 'cancelled' }; // User pressed ESC or cancelled - if ('isSpecial' in result && result.isSpecial) return { type: 'use_default' }; // User chose "Use Default Templates" - return { type: 'rule', rule: result as PairingRule }; // User selected a specific rule - } - - // Shows a dialog offering to create custom pairing rules for C++. - // Only applicable for C++ since C uses standard .c/.h extensions. - // Returns true to create rules, false to dismiss, or null if cancelled. - public async offerToCreateCustomRules(language: 'c' | - 'cpp'): Promise { - if (language === 'c') - return false; - - const result = await vscode.window.showInformationMessage( - `No custom pairing rules found for C++. Would you like to create custom rules to use different file extensions (e.g., .cc/.hh instead of .cpp/.h)?`, - { modal: false }, 'Create Custom Rules', 'Dismiss'); - - return result === 'Create Custom Rules' ? true - : result === 'Dismiss' ? false - : null; + if (!saveLocation) + return undefined; + + try { + const existingRules = PairingRuleService.getRules( + saveLocation.value as 'workspace' | 'user') || + []; + await PairingRuleService.writeRules([...existingRules, customRule], + saveLocation.value as 'workspace' | + 'user'); + + const locationText = + saveLocation.value === 'workspace' ? 'workspace' : 'global'; + vscode.window.showInformationMessage( + `Custom pairing rule saved to ${locationText} settings.`); + + return customRule; + } catch (error: unknown) { + const errorMessage = + error instanceof Error ? error.message : 'Unknown error occurred'; + vscode.window.showErrorMessage( + `Failed to save custom rule: ${errorMessage}`); + return undefined; } - - // Guides the user through creating custom pairing rules for C++ - // Offers common extension combinations or allows custom input - // Saves the rule to workspace or global settings - // Returns the created custom rule or undefined if cancelled - public async createCustomRules(language: 'c' | - 'cpp'): Promise { - if (language === 'c') - return undefined; - - const commonExtensions = [ - { label: '.h / .cpp (Default)', headerExt: '.h', sourceExt: '.cpp' }, - { label: '.hh / .cc (Alternative)', headerExt: '.hh', sourceExt: '.cc' }, { - label: '.hpp / .cpp (Header Plus Plus)', - headerExt: '.hpp', - sourceExt: '.cpp' - }, - { label: '.hxx / .cxx (Extended)', headerExt: '.hxx', sourceExt: '.cxx' }, - { label: 'Custom Extensions', headerExt: '', sourceExt: '' } - ]; - - const selectedExtension = - await vscode.window.showQuickPick(commonExtensions, { - placeHolder: `Select file extensions for C++ files`, - title: 'Choose File Extensions' - }); - - if (!selectedExtension) - return undefined; - - let { headerExt, sourceExt } = selectedExtension; - - if (!headerExt || !sourceExt) { - const validateExt = (text: string) => - (!text || !text.startsWith('.') || text.length < 2) - ? 'Please enter a valid file extension starting with a dot (e.g., .h)' - : null; - - headerExt = await vscode.window.showInputBox({ - prompt: 'Enter header file extension (e.g., .h, .hh, .hpp)', - placeHolder: '.h', - validateInput: validateExt - }) || ''; - - if (!headerExt) - return undefined; - - sourceExt = await vscode.window.showInputBox({ - prompt: `Enter source file extension for C++ (e.g., .cpp, .cc, .cxx)`, - placeHolder: '.cpp', - validateInput: validateExt - }) || ''; - - if (!sourceExt) - return undefined; - } - - const customRule: PairingRule = { - key: `custom_cpp_${Date.now()}`, - label: `$(new-file) C++ Pair (${headerExt}/${sourceExt})`, - description: - `Creates a ${headerExt}/${sourceExt} file pair with header guards.`, - language: 'cpp', - headerExt, - sourceExt - }; - - const saveLocation = await vscode.window.showQuickPick( - [ - { - label: 'Workspace Settings', - description: 'Save to current workspace only', - value: 'workspace' - }, - { - label: 'Global Settings', - description: 'Save to user settings (available in all workspaces)', - value: 'user' - } - ], - { - placeHolder: 'Where would you like to save this custom rule?', - title: 'Save Location' - }); - - if (!saveLocation) - return undefined; - - try { - const existingRules = PairingRuleService.getRules( - saveLocation.value as 'workspace' | 'user') || - []; - await PairingRuleService.writeRules([...existingRules, customRule], - saveLocation.value as 'workspace' | - 'user'); - - const locationText = - saveLocation.value === 'workspace' ? 'workspace' : 'global'; - vscode.window.showInformationMessage( - `Custom pairing rule saved to ${locationText} settings.`); - - return customRule; - } catch (error: unknown) { - const errorMessage = error instanceof Error ? error.message : 'Unknown error occurred'; - vscode.window.showErrorMessage( - `Failed to save custom rule: ${errorMessage}`); - return undefined; + } + + // Prompts the user to select a pairing rule from available options + // IMPROVED FLOW: + // 1. Check for existing custom rules first + // 2. If no custom rules, show template choice (C/C++ types) + // 3. If C++ selected, then choose extensions + // 4. After successful creation, offer to save as default + // Returns selected pairing rule or undefined if cancelled + public async promptForPairingRule(language: 'c'|'cpp', uncertain: boolean): + Promise { + + // First check if there are existing custom rules + if (language === 'cpp') { + const allRules = this.service.getAllPairingRules(); + const languageRules = + allRules.filter((rule: PairingRule) => rule.language === language); + + if (languageRules.length > 0) { + // Use existing flow for custom rules with clear intent handling + const result = await this.selectFromCustomRules(allRules, language); + if (result.type === 'cancelled') + return undefined; // 用户明确取消操作 + if (result.type === 'use_default') { + // 用户选择使用默认模板,继续到常规流程(不return,让代码继续执行) + } else if (result.type === 'rule') { + return result.rule; // 用户选择了具体的自定义规则 } + } } - // Prompts the user to select a pairing rule from available options - // IMPROVED FLOW: - // 1. Check for existing custom rules first - // 2. If no custom rules, show template choice (C/C++ types) - // 3. If C++ selected, then choose extensions - // 4. After successful creation, offer to save as default - // Returns selected pairing rule or undefined if cancelled - public async promptForPairingRule(language: 'c' | 'cpp', uncertain: boolean): - Promise { - - // First check if there are existing custom rules - if (language === 'cpp') { - const allRules = this.service.getAllPairingRules(); - const languageRules = allRules.filter((rule: PairingRule) => rule.language === language); - - if (languageRules.length > 0) { - // Use existing flow for custom rules with clear intent handling - const result = await this.selectFromCustomRules(allRules, language); - if (result.type === 'cancelled') return undefined; // 用户明确取消操作 - if (result.type === 'use_default') { - // 用户选择使用默认模板,继续到常规流程(不return,让代码继续执行) - } else if (result.type === 'rule') { - return result.rule; // 用户选择了具体的自定义规则 - } - } - } - - // New improved flow: Choose template type first (C/C++ language and type) - const templateChoice = await this.promptForTemplateTypeFirst(language, uncertain); - if (!templateChoice) return undefined; - - // If C++ template was selected and no custom rules exist, ask for extensions - if (templateChoice.language === 'cpp') { - const allRules = this.service.getAllPairingRules(); - const cppRules = allRules.filter((rule: PairingRule) => rule.language === 'cpp'); - - if (cppRules.length === 0) { - // No custom C++ rules, let user choose extensions - const extensionChoice = await PairingRuleUI.promptForFileExtensions(); - if (!extensionChoice) return undefined; - - // Apply the chosen extensions to the template - return { - ...templateChoice, - key: `${templateChoice.key}_custom`, - headerExt: extensionChoice.headerExt, - sourceExt: extensionChoice.sourceExt, - description: templateChoice.description.replace(/\.h\/\.cpp/g, - `${extensionChoice.headerExt}/${extensionChoice.sourceExt}`) - }; - } - } - - return templateChoice; + // New improved flow: Choose template type first (C/C++ language and type) + const templateChoice = + await this.promptForTemplateTypeFirst(language, uncertain); + if (!templateChoice) + return undefined; + + // If C++ template was selected and no custom rules exist, ask for + // extensions + if (templateChoice.language === 'cpp') { + const allRules = this.service.getAllPairingRules(); + const cppRules = + allRules.filter((rule: PairingRule) => rule.language === 'cpp'); + + if (cppRules.length === 0) { + // No custom C++ rules, let user choose extensions + const extensionChoice = await PairingRuleUI.promptForFileExtensions(); + if (!extensionChoice) + return undefined; + + // Apply the chosen extensions to the template + return { + ...templateChoice, + key: `${templateChoice.key}_custom`, + headerExt: extensionChoice.headerExt, + sourceExt: extensionChoice.sourceExt, + description: templateChoice.description.replace( + /\.h\/\.cpp/g, + `${extensionChoice.headerExt}/${extensionChoice.sourceExt}`) + }; + } } - // Simplified template selection with adapter pattern - private async promptForTemplateTypeFirst(language: 'c' | 'cpp', uncertain: boolean): Promise { - const choices = this.prepareTemplateChoices(language, uncertain); - const enhancedChoices = choices.map(rule => this.adaptRuleForTemplateDisplay(rule)); - - const result = await vscode.window.showQuickPick(enhancedChoices, { - placeHolder: 'Please select the type of file pair to create.', - title: 'Create Source/Header Pair', - matchOnDescription: true, - matchOnDetail: true - }); - - if (!result) return null; // User cancelled - - if (result && !uncertain && language !== result.language) { - const shouldShowWarning = await this.shouldShowLanguageMismatchWarning(language, result); - - if (shouldShowWarning) { - const detectedLangName = language === 'c' ? 'C' : 'C++'; - const selectedLangName = result.language === 'c' ? 'C' : 'C++'; - - const shouldContinue = await vscode.window.showWarningMessage( - `You're working in a ${detectedLangName} file but selected a ${selectedLangName} template. This may create files with incompatible extensions or content.`, - 'Continue', 'Cancel'); - - if (shouldContinue !== 'Continue') return null; - } - } - - return result; + return templateChoice; + } + + // Simplified template selection with adapter pattern + private async promptForTemplateTypeFirst(language: 'c'|'cpp', + uncertain: boolean): + Promise { + const choices = this.prepareTemplateChoices(language, uncertain); + const enhancedChoices = + choices.map(rule => this.adaptRuleForTemplateDisplay(rule)); + + const result = await vscode.window.showQuickPick(enhancedChoices, { + placeHolder: 'Please select the type of file pair to create.', + title: 'Create Source/Header Pair', + matchOnDescription: true, + matchOnDetail: true + }); + + if (!result) + return null; // User cancelled + + if (result && !uncertain && language !== result.language) { + const shouldShowWarning = + await this.shouldShowLanguageMismatchWarning(language, result); + + if (shouldShowWarning) { + const detectedLangName = language === 'c' ? 'C' : 'C++'; + const selectedLangName = result.language === 'c' ? 'C' : 'C++'; + + const shouldContinue = await vscode.window.showWarningMessage( + `You're working in a ${detectedLangName} file but selected a ${ + selectedLangName} template. This may create files with incompatible extensions or content.`, + 'Continue', 'Cancel'); + + if (shouldContinue !== 'Continue') + return null; + } } - // Shows workspace folder picker when multiple folders are available - public async showWorkspaceFolderPicker(): Promise { - const workspaceFolders = vscode.workspace.workspaceFolders; - if (!workspaceFolders || workspaceFolders.length <= 1) { - return undefined; - } - - const selected = await vscode.window.showQuickPick( - workspaceFolders.map(folder => ({ - label: folder.name, - description: folder.uri.fsPath, - folder: folder - })), - { placeHolder: 'Select workspace folder for new files' } - ); + return result; + } - return selected?.folder.uri; - } - - // Prompts the user to enter a name for the new file pair - // Validates input as a valid C/C++ identifier and provides context-appropriate prompts - // Returns the entered file name or undefined if cancelled - public async promptForFileName(rule: PairingRule): Promise { - const prompt = rule.isClass ? 'Please enter the name for the new C++ class.' - : rule.isStruct - ? `Please enter the name for the new ${rule.language.toUpperCase()} struct.` - : `Please enter the base name for the new ${rule.language.toUpperCase()} file pair.`; - - return vscode.window.showInputBox({ - prompt, - placeHolder: this.getPlaceholder(rule), - validateInput: (text) => - VALIDATION_PATTERNS.IDENTIFIER.test(text?.trim() || '') - ? null - : 'Invalid C/C++ identifier.', - title: 'Create Source/Header Pair' - }); + // Shows workspace folder picker when multiple folders are available + public async showWorkspaceFolderPicker(): Promise { + const workspaceFolders = vscode.workspace.workspaceFolders; + if (!workspaceFolders || workspaceFolders.length <= 1) { + return undefined; } - // Shows success message and opens the newly created header file - public async showSuccessAndOpenFile(headerPath: vscode.Uri, - sourcePath: vscode.Uri): Promise { + const selected = await vscode.window.showQuickPick( + workspaceFolders.map(folder => ({ + label: folder.name, + description: folder.uri.fsPath, + folder: folder + })), + {placeHolder: 'Select workspace folder for new files'}); + + return selected?.folder.uri; + } + + // Prompts the user to enter a name for the new file pair + // Validates input as a valid C/C++ identifier and provides + // context-appropriate prompts Returns the entered file name or undefined if + // cancelled + public async promptForFileName(rule: PairingRule): Promise { + const prompt = rule.isClass ? 'Please enter the name for the new C++ class.' + : rule.isStruct + ? `Please enter the name for the new ${ + rule.language.toUpperCase()} struct.` + : `Please enter the base name for the new ${ + rule.language.toUpperCase()} file pair.`; + + return vscode.window.showInputBox({ + prompt, + placeHolder: this.getPlaceholder(rule), + validateInput: (text) => + VALIDATION_PATTERNS.IDENTIFIER.test(text?.trim() || '') + ? null + : 'Invalid C/C++ identifier.', + title: 'Create Source/Header Pair' + }); + } + + // Shows success message and opens the newly created header file + public async showSuccessAndOpenFile(headerPath: vscode.Uri, + sourcePath: vscode.Uri): Promise { + try { + const document = await vscode.workspace.openTextDocument(headerPath); + + // Use setTimeout to make this non-blocking and avoid hanging + setTimeout(async () => { try { - const document = await vscode.workspace.openTextDocument(headerPath); - - // Use setTimeout to make this non-blocking and avoid hanging - setTimeout(async () => { - try { - await vscode.window.showTextDocument(document); - } catch (error) { - // Silently handle file opening errors - } - }, 100); - - // Show success message with slight delay to allow other dialogs to appear - setTimeout(() => { - vscode.window.showInformationMessage( - `Successfully created ${path.basename(headerPath.fsPath)} and ${path.basename(sourcePath.fsPath)}.`); - }, 50); + await vscode.window.showTextDocument(document); } catch (error) { - // Still show success message even if file opening fails - setTimeout(() => { - vscode.window.showInformationMessage( - `Successfully created ${path.basename(headerPath.fsPath)} and ${path.basename(sourcePath.fsPath)}.`); - }, 50); + // Silently handle file opening errors } + }, 100); + + // Show success message with slight delay to allow other dialogs to appear + setTimeout(() => { + vscode.window.showInformationMessage( + `Successfully created ${path.basename(headerPath.fsPath)} and ${ + path.basename(sourcePath.fsPath)}.`); + }, 50); + } catch (error) { + // Still show success message even if file opening fails + setTimeout(() => { + vscode.window.showInformationMessage( + `Successfully created ${path.basename(headerPath.fsPath)} and ${ + path.basename(sourcePath.fsPath)}.`); + }, 50); } + } } From fc34485bf0a5058d609aa26736bde38ed36c3d71 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?=E9=95=BF=E6=9C=9F=E7=B4=A0=E9=A3=9F?= Date: Sun, 20 Jul 2025 13:05:31 +0800 Subject: [PATCH 33/34] Update package.json --- package.json | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/package.json b/package.json index cba8b72a..8820efa4 100644 --- a/package.json +++ b/package.json @@ -1,8 +1,8 @@ { "name": "vscode-clangd-fancy", - "displayName": "Clotho", + "displayName": "clangd", "description": "C/C++ completion, navigation, and insights", - "version": "0.2.1", + "version": "0.2.0", "publisher": "llvm-vs-code-extensions", "license": "MIT", "homepage": "https://clangd.llvm.org/", From 838165ecf50e3db7c06dead46f3211889405c6eb Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?=E9=95=BF=E6=9C=9F=E7=B4=A0=E9=A3=9F?= Date: Sun, 20 Jul 2025 13:05:48 +0800 Subject: [PATCH 34/34] Update package.json --- package.json | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/package.json b/package.json index 8820efa4..cc4f7306 100644 --- a/package.json +++ b/package.json @@ -1,5 +1,5 @@ { - "name": "vscode-clangd-fancy", + "name": "vscode-clangd", "displayName": "clangd", "description": "C/C++ completion, navigation, and insights", "version": "0.2.0",