-
Couldn't load subscription status.
- Fork 145
feat(command): Add Create Source/Header Pair command #840
New issue
Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.
By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.
Already on GitHub? Sign in to your account
base: master
Are you sure you want to change the base?
Changes from 34 commits
898ba52
b8dfda3
e82a3ad
605aab9
e84f24d
1c0cfd9
9663d80
76e3d19
769cbba
438f11a
da3e288
85adf58
475839e
4345afc
c463be2
2a60d7c
e6b97fe
6b4a6c1
aad7b7c
6ac7352
0d3e9dd
9c6135a
a820052
c31a851
21eeb6b
1ccc7d9
2f7cb17
173b431
7b9d7c0
149d7c3
55cffa9
21eb998
fc34485
838165e
aac5797
File filter
Filter by extension
Conversations
Jump to
Diff view
Diff view
There are no files selected for viewing
Some generated files are not rendered by default. Learn more about how customized files appear on GitHub.
|
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. See comment in bug, though I'd be more inclined to ask for a snippet ID rather than having fields to indicate a class/struct. There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. I like your addition of relevant icons, though can you do this in a separate PR, such that it's easier to track the relevant changes and/or land these changes separately? |
| Original file line number | Diff line number | Diff line change |
|---|---|---|
|
|
@@ -4,6 +4,7 @@ import * as vscode from 'vscode'; | |
| import * as vscodelc from 'vscode-languageclient/node'; | ||
|
|
||
| import {ClangdContext} from './clangd-context'; | ||
|
|
||
|
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. File is touched without any significant changes. Can you undo this newline? (Unless required by formatting rules) |
||
| import type {ASTParams, ASTNode} from '../api/vscode-clangd'; | ||
|
|
||
| const ASTRequestMethod = 'textDocument/ast'; | ||
|
|
||
| Original file line number | Diff line number | Diff line change |
|---|---|---|
| @@ -0,0 +1,107 @@ | ||
| // | ||
| // PAIR COORDINATOR | ||
| // ================ | ||
| // | ||
| // Lean coordinator that orchestrates the source/header pair creation workflow. | ||
| // 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'; | ||
|
|
||
| // 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); | ||
| } | ||
|
|
||
| // Dispose method for cleanup when extension is deactivated | ||
| dispose() { | ||
| this.newPairCommand.dispose(); | ||
| this.configureRulesCommand.dispose(); | ||
| } | ||
|
|
||
| // Main workflow orchestration | ||
| public async create(): Promise<void> { | ||
| 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); | ||
| } | ||
| } | ||
|
|
||
| // Determine target directory with fallback to workspace picker | ||
| private async getTargetDirectory(): Promise<vscode.Uri|undefined> { | ||
| 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<void> { | ||
| await showConfigurationWizard(); | ||
| } | ||
| } |
| Original file line number | Diff line number | Diff line change |
|---|---|---|
| @@ -0,0 +1,56 @@ | ||
| // | ||
| // 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'; | ||
| 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); | ||
|
|
||
| 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'; |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
vscode-nls seems to be deprecated: https://github.com/microsoft/vscode-nls