Skip to content
Open
Show file tree
Hide file tree
Changes from 34 commits
Commits
Show all changes
35 commits
Select commit Hold shift + click to select a range
898ba52
feat(command): Add Create Source/Header Pair command
anonchihaya0908 Jul 16, 2025
b8dfda3
Refactor and modularize source/header pair creation
anonchihaya0908 Jul 16, 2025
e82a3ad
This refactors the "Create Source/Header Pair" command into a unified,
anonchihaya0908 Jul 16, 2025
605aab9
Update create-source-header-pair.ts
anonchihaya0908 Jul 16, 2025
e84f24d
Refactor file pair creation logic and templates
anonchihaya0908 Jul 16, 2025
1c0cfd9
Update create-source-header-pair.ts
anonchihaya0908 Jul 16, 2025
9663d80
Update create-source-header-pair.ts
anonchihaya0908 Jul 16, 2025
76e3d19
Update create-source-header-pair.ts
anonchihaya0908 Jul 16, 2025
769cbba
Rename and enhance source/header pair commands
anonchihaya0908 Jul 17, 2025
438f11a
npm run format
anonchihaya0908 Jul 18, 2025
da3e288
fix
anonchihaya0908 Jul 18, 2025
85adf58
fix
anonchihaya0908 Jul 18, 2025
475839e
fix register.
anonchihaya0908 Jul 18, 2025
4345afc
npm run format
anonchihaya0908 Jul 18, 2025
c463be2
Refactor create-source-header-pair module structure
anonchihaya0908 Jul 18, 2025
2a60d7c
Refactor C++ pair creation workflow and improve UX
anonchihaya0908 Jul 18, 2025
e6b97fe
npm run format
anonchihaya0908 Jul 18, 2025
6b4a6c1
Refactor coordinator and UI for dependency injection
anonchihaya0908 Jul 18, 2025
aad7b7c
Update ui.ts
anonchihaya0908 Jul 18, 2025
6ac7352
Update ui.ts
anonchihaya0908 Jul 18, 2025
0d3e9dd
Update ui.ts
anonchihaya0908 Jul 18, 2025
9c6135a
fuck
anonchihaya0908 Jul 18, 2025
a820052
Add icon and explorer context menu for new source/header pair
anonchihaya0908 Jul 18, 2025
c31a851
Remove clangd.newSourcePair command from package.json
anonchihaya0908 Jul 18, 2025
21eeb6b
Update service.ts
anonchihaya0908 Jul 19, 2025
1ccc7d9
Update package.json
anonchihaya0908 Jul 19, 2025
2f7cb17
Update coordinator.ts
anonchihaya0908 Jul 19, 2025
173b431
fix
anonchihaya0908 Jul 19, 2025
7b9d7c0
Create vscode-clangd-fancy-0.2.1.vsix
anonchihaya0908 Jul 19, 2025
149d7c3
Update service.ts
anonchihaya0908 Jul 19, 2025
55cffa9
Update service.ts
anonchihaya0908 Jul 19, 2025
21eb998
format
anonchihaya0908 Jul 19, 2025
fc34485
Update package.json
anonchihaya0908 Jul 20, 2025
838165e
Update package.json
anonchihaya0908 Jul 20, 2025
aac5797
Merge branch 'clangd:master' into master
anonchihaya0908 Jul 22, 2025
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
22 changes: 17 additions & 5 deletions package-lock.json
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Some generated files are not rendered by default. Learn more about how customized files appear on GitHub.

90 changes: 83 additions & 7 deletions package.json
Copy link
Contributor

Choose a reason for hiding this comment

The 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.

Copy link
Contributor

Choose a reason for hiding this comment

The 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
Expand Up @@ -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/",
Expand All @@ -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",
Expand Down Expand Up @@ -194,6 +195,60 @@
"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 this rule"
},
"label": {
"type": "string",
"description": "Human-readable name shown in UI"
},
"description": {
"type": "string",
"description": "Detailed description of what this rule creates"
},
"language": {
"type": "string",
"enum": [
"c",
"cpp"
],
"description": "Target programming language"
},
"headerExt": {
"type": "string",
"description": "File extension for header file (e.g., '.h', '.hh')"
},
"sourceExt": {
"type": "string",
"description": "File extension for source file (e.g., '.cpp', '.cc', '.c')"
},
"isClass": {
"type": "boolean",
"description": "Whether this rule creates a class template"
},
"isStruct": {
"type": "boolean",
"description": "Whether this rule creates a struct template"
}
},
"required": [
"key",
"label",
"description",
"language",
"headerExt",
"sourceExt"
]
},
"description": "Custom pairing rules for creating source/header file pairs with different extensions"
}
}
},
Expand All @@ -212,7 +267,18 @@
{
"command": "clangd.switchheadersource",
"category": "clangd",
"title": "Switch Between Source/Header"
"title": "Switch Between Source/Header Pair"
},
{
"command": "clangd.newSourcePair",
"category": "clangd",
"title": "New Source/Header Pair",
"icon": "$(new-file)"
},
{
"command": "clangd.createPair.configureRules",
"category": "clangd",
"title": "Configure Source/Header Pairing Rules"
},
{
"command": "clangd.install",
Expand Down Expand Up @@ -368,6 +434,13 @@
"group": "navigation"
}
],
"explorer/context": [
{
"command": "clangd.newSourcePair",
"when": "explorerResourceIsFolder",
"group": "2_workspace@1"
}
],
"commandPalette": [
{
"command": "clangd.typeHierarchy.viewParents",
Expand All @@ -388,19 +461,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)"
}
]
}
}
}
}
1 change: 1 addition & 0 deletions src/ast.ts
Original file line number Diff line number Diff line change
Expand Up @@ -4,6 +4,7 @@ import * as vscode from 'vscode';
import * as vscodelc from 'vscode-languageclient/node';

import {ClangdContext} from './clangd-context';

Copy link
Contributor

Choose a reason for hiding this comment

The 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';
Expand Down
107 changes: 107 additions & 0 deletions src/create-source-header-pair/coordinator.ts
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();
}
}
56 changes: 56 additions & 0 deletions src/create-source-header-pair/index.ts
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';
Loading