Skip to content

Commit ed7225b

Browse files
committed
Add layout and table styles, refactor styles import structure, and modularize template rendering functions
- Introduced new layout styles in `layout.ts` for better container and header styling. - Added table styles in `tables.ts` for improved presentation of data. - Refactored `styles.ts` to import styles from separate fragments, enhancing maintainability. - Modularized template rendering functions in `templates.ts` for better organization and reusability.
1 parent 0d775bc commit ed7225b

20 files changed

Lines changed: 1081 additions & 1228 deletions

README.md

Lines changed: 51 additions & 78 deletions
Original file line numberDiff line numberDiff line change
@@ -3,28 +3,39 @@
33
<!-- Badges -->
44
[![License: MIT](https://img.shields.io/badge/License-MIT-yellow.svg)](https://opensource.org/licenses/MIT)
55
[![VS Code](https://img.shields.io/badge/VS%20Code-1.108.2+-blue)](https://code.visualstudio.com/)
6+
[![VS Marketplace](https://img.shields.io/visual-studio-marketplace/v/KebanFiru.CodeLineCounter?label=VS%20Marketplace&color=0078d7)](https://marketplace.visualstudio.com/items?itemName=KebanFiru.CodeLineCounter)
67
[![TypeScript](https://img.shields.io/badge/TypeScript-5.9+-blue)](https://www.typescriptlang.org/)
78

8-
> Quickly analyze your project's codebase by counting lines of code, comments, and get insights organized by file types.
9+
> Quickly analyze your project's codebase with comprehensive metrics, Git analytics, and interactive dashboards.
910
1011
## Overview
1112

12-
**CodeCount** is a powerful VS Code extension that provides comprehensive code metrics for your projects. Instantly see how many lines of code, comments, and blank lines your workspace contains. Get detailed breakdowns by file type, understand code distribution, and track your project's scale.
13-
14-
Perfect for developers, project managers, and teams who need quick insights into codebase size and complexity.
13+
**CodeCount** is a powerful VS Code extension that provides deep insights into your codebase. Get instant metrics on lines of code, understand contributor activity, track commit trends, and visualize language distribution. Perfect for developers, project managers, and teams who need comprehensive codebase analytics.
1514

1615
## Features
1716

18-
- **Workspace Analysis** - Get total line counts for your entire project
19-
- **File-by-File Breakdown** - See statistics for individual files
20-
- **Extension-Based Statistics** - Analyze code distribution across file types (TypeScript, JavaScript, Python, etc.)
21-
- **Comment Detection** - Identify and count comment lines separately
22-
- **Real-Time Refresh** - Update stats anytime with a single click
23-
- **Sidebar Integration** - Dedicated panel in VS Code's Activity Bar
24-
- **Gitignore Support** - Respects your `.gitignore` patterns automatically
25-
- **⏱Performance Optimized** - Fast analysis even for large projects
26-
27-
## 📋 Commands
17+
### Code Metrics
18+
- **Workspace Analysis** - Total line counts for entire projects
19+
- **File-by-File Breakdown** - Statistics for individual files
20+
- **Language Distribution** - Analyze code across file types with expandable lists
21+
- **Comment Detection** - Separate counts for comment and blank lines
22+
- **Gitignore Support** - Automatically respects `.gitignore` patterns
23+
24+
### Git Analytics (Repository View)
25+
- **Contributor Statistics** - Track contributions with added/deleted line counts
26+
- **Commit Trends** - Visualize commits, additions, and deletions over time
27+
- **Monthly Activity** - Monitor development activity by month
28+
- **Work Patterns** - Understand coding patterns by weekday and hour
29+
- **Repository Metrics** - Total contributors, commits, and lines changed
30+
31+
### Interactive Dashboard
32+
- **Real-Time Statistics Panel** - View metrics in VS Code sidebar
33+
- **Expandable Lists** - Top 3 preview with "Show all" toggle for contributors and languages
34+
- **Responsive Charts** - Full-width visualizations using Chart.js v4.4.0
35+
- **Dark Mode Support** - Optimized for VS Code themes with white text on dark backgrounds
36+
- **Performance Optimized** - Instant analysis even for large projects
37+
38+
## Commands
2839

2940
Access CodeCount commands via the Command Palette (`Ctrl+Shift+P` / `Cmd+Shift+P`):
3041

@@ -35,81 +46,49 @@ Access CodeCount commands via the Command Palette (`Ctrl+Shift+P` / `Cmd+Shift+P
3546
| `CodeCount: Count Lines of Code by Extension` | Get breakdown by file type |
3647
| `CodeCount: Refresh CodeCount Stats` | Refresh sidebar statistics |
3748

38-
## 📦 Requirements
49+
## Requirements
3950

4051
- **VS Code:** 1.108.2 or newer
41-
- **Git:** Required for repository recognition (optional, but recommended)
42-
43-
## 🚀 Quick Start
44-
45-
### Installation
46-
47-
1. Open VS Code
48-
2. Go to Extensions (`Ctrl+Shift+X` / `Cmd+Shift+X`)
49-
3. Search for "CodeCount"
50-
4. Click **Install**
51-
5. Reload VS Code if prompted
52+
- **Git:** Required for repository analytics and contributor tracking
5253

5354
### Usage
5455

55-
#### Method 1: Using Commands
56+
#### Method 1: Using Sidebar
57+
- Click the **CodeCount** icon in the Activity Bar (left sidebar)
58+
- View comprehensive statistics including:
59+
- Stats overview grid
60+
- Language distribution with breakdown
61+
- Top contributors and their contributions
62+
- Repository analytics and commit history
63+
64+
#### Method 2: Using Commands
5665
- Open Command Palette (`Ctrl+Shift+P` / `Cmd+Shift+P`)
5766
- Type "CodeCount" to see available commands
5867
- Select the command you want to run
5968

60-
#### Method 2: Using Sidebar
61-
- Click the **CodeCount** icon in the Activity Bar (left sidebar)
62-
- View current statistics
63-
- Click the refresh button to update
64-
65-
#### Example Output
66-
```
67-
Total lines in workspace (145 files): 25,847
68-
Comment lines: 3,421
69-
```
69+
#### Dashboard Features
70+
- **Expandable Lists** - Click "Show all" to expand contributors or languages
71+
- **Interactive Charts** - Hover over visualizations for detailed information
72+
- **Full-Width Analytics** - View commit trends, monthly activity, and work patterns
73+
- **Real-Time Updates** - Click refresh to update statistics
7074

71-
## 🛠️ Development
75+
## Architecture
7276

73-
### Prerequisites
74-
- Node.js 18+
75-
- pnpm (recommended) or npm
77+
### Technology Stack
78+
- **Frontend:** HTML/CSS/JavaScript in VS Code Webview
79+
- **Charts:** Chart.js v4.4.0
80+
- **Language:** TypeScript with strict mode
81+
- **Styling:** CSS Grid layout with responsive design
82+
- **Git Integration:** Child process execution for git commands
7683

77-
### Setup
78-
79-
## 📚 Configuration
84+
## Configuration
8085

8186
CodeCount respects your project's `.gitignore` file, automatically excluding:
8287
- `node_modules/`
8388
- `dist/` and `build/`
8489
- `.git/` directories
8590
- And any patterns defined in `.gitignore`
8691

87-
## 🤝 Contributing
88-
89-
We welcome contributions! Here's how to help:
90-
91-
### Getting Started
92-
1. **Fork** the repository
93-
2. **Create** a feature branch: `git checkout -b feature/amazing-feature`
94-
3. **Make** your changes
95-
4. **Test** thoroughly
96-
5. **Commit** with clear messages: `git commit -m 'Add amazing feature'`
97-
6. **Push** to your branch: `git push origin feature/amazing-feature`
98-
7. **Open** a Pull Request
99-
100-
### Development Guidelines
101-
- Follow TypeScript best practices
102-
- Keep code clean and well-documented
103-
- Run `pnpm run lint` before committing
104-
- Write meaningful commit messages
105-
- Test changes in the Extension Development Host
106-
107-
### Code Style
108-
- Use TypeScript with strict mode enabled
109-
- Follow ESLint rules (run `pnpm run lint`)
110-
- Use descriptive variable and function names
111-
- Add comments for complex logic
112-
11392
## License
11493

11594
This project is licensed under the **MIT License** - see the [LICENSE](LICENSE) file for details.
@@ -126,15 +105,9 @@ Please include:
126105
- Steps to reproduce (for bugs)
127106
- Expected vs actual behavior
128107
- VS Code version and OS
108+
- Extension version
129109

130-
## 📊Project Statistics
131-
132-
- **Language:** TypeScript
133-
- **VS Code API:** 1.108.2+
134-
- **License:** MIT
135-
- **Status:** Active Development
136-
137-
## 💬 Questions?
110+
## Questions?
138111

139112
Have questions? Feel free to:
140113
- Open a [Discussion](https://github.com/KebanFiru/CodeCount/discussions)

package.json

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -2,7 +2,7 @@
22
"name": "CodeLineCounter",
33
"displayName": "CodeLineCounter",
44
"description": "A tool for learn how much line of code your project contains",
5-
"version": "1.0.2",
5+
"version": "2.0.0",
66
"publisher": "KebanFiru",
77
"license": "MIT",
88
"homepage": "https://github.com/KebanFiru/CodeCount#readme",

src/extension.ts

Lines changed: 39 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -16,6 +16,45 @@ export function activate(context: vscode.ExtensionContext) {
1616
const statsService = new StatsService(lineCounter, gitignoreService);
1717
const statsProvider = new StatsTreeProvider(statsService);
1818

19+
let branchRefreshTimer: ReturnType<typeof setTimeout> | undefined;
20+
const scheduleBranchRefresh = () => {
21+
if (branchRefreshTimer) {
22+
clearTimeout(branchRefreshTimer);
23+
}
24+
branchRefreshTimer = setTimeout(() => {
25+
gitignoreService.clearCache();
26+
statsProvider.refresh();
27+
StatsWebviewPanel.refreshIfOpen();
28+
branchRefreshTimer = undefined;
29+
}, 150);
30+
};
31+
32+
void (async () => {
33+
const gitDirPath = await statsService.getGitDirPath();
34+
if (!gitDirPath) {
35+
return;
36+
}
37+
38+
const gitRoot = await statsService.getGitRootPath();
39+
if (!gitRoot) {
40+
return;
41+
}
42+
43+
const watchers = [
44+
vscode.workspace.createFileSystemWatcher(new vscode.RelativePattern(gitRoot, '.git/HEAD')),
45+
vscode.workspace.createFileSystemWatcher(new vscode.RelativePattern(gitRoot, '.git/refs/heads/**')),
46+
vscode.workspace.createFileSystemWatcher(new vscode.RelativePattern(gitRoot, '.git/packed-refs')),
47+
vscode.workspace.createFileSystemWatcher('**/.gitignore')
48+
];
49+
50+
for (const watcher of watchers) {
51+
watcher.onDidChange(scheduleBranchRefresh, undefined, context.subscriptions);
52+
watcher.onDidCreate(scheduleBranchRefresh, undefined, context.subscriptions);
53+
watcher.onDidDelete(scheduleBranchRefresh, undefined, context.subscriptions);
54+
context.subscriptions.push(watcher);
55+
}
56+
})();
57+
1958
const fileline = vscode.commands.registerCommand('codecount.countLines', () => {
2059
const editor = vscode.window.activeTextEditor;
2160
if (!editor) {

src/services/GitignoreService.ts

Lines changed: 75 additions & 19 deletions
Original file line numberDiff line numberDiff line change
@@ -2,9 +2,18 @@ import * as path from 'path';
22
import * as vscode from 'vscode';
33
import type { GitignoreMatcher, GitignoreRule } from '../types/gitignore';
44

5+
type GitignoreScope = {
6+
rootRelativeDir: string;
7+
rules: GitignoreRule[];
8+
};
9+
510
export class GitignoreService {
611
private readonly cache = new Map<string, GitignoreMatcher>();
712

13+
clearCache(): void {
14+
this.cache.clear();
15+
}
16+
817
async getMatcher(rootPath: string): Promise<GitignoreMatcher> {
918
const cached = this.cache.get(rootPath);
1019
if (cached) {
@@ -17,22 +26,45 @@ export class GitignoreService {
1726
}
1827

1928
private async loadGitignore(rootPath: string): Promise<GitignoreMatcher> {
20-
const gitignoreUri = vscode.Uri.file(path.join(rootPath, '.gitignore'));
21-
let content = '';
22-
try {
23-
const data = await vscode.workspace.fs.readFile(gitignoreUri);
24-
content = data.toString();
25-
}
26-
catch {
27-
// .gitignore not found
28-
}
29-
30-
const rules = this.parseGitignore(content);
29+
const scopes = await this.loadGitignoreScopes(rootPath);
3130
return {
32-
ignores: (relativePath: string) => this.matchGitignore(rules, relativePath)
31+
ignores: (relativePath: string) => this.matchGitignore(scopes, relativePath)
3332
};
3433
}
3534

35+
private async loadGitignoreScopes(rootPath: string): Promise<GitignoreScope[]> {
36+
const gitignoreFiles = await vscode.workspace.findFiles(
37+
'**/.gitignore',
38+
'**/{node_modules,out,dist,.git}/**'
39+
);
40+
41+
const scopes: GitignoreScope[] = [];
42+
for (const uri of gitignoreFiles) {
43+
const relativePath = path.relative(rootPath, uri.fsPath);
44+
if (relativePath.startsWith('..') || path.isAbsolute(relativePath)) {
45+
continue;
46+
}
47+
48+
const relativeGitignorePath = relativePath.replace(/\\/g, '/');
49+
const rootRelativeDir = path.dirname(relativeGitignorePath).replace(/\\/g, '/');
50+
const content = await this.readGitignoreFile(uri);
51+
const rules = this.parseGitignore(content);
52+
scopes.push({ rootRelativeDir, rules });
53+
}
54+
55+
scopes.sort((a, b) => a.rootRelativeDir.length - b.rootRelativeDir.length);
56+
return scopes;
57+
}
58+
59+
private async readGitignoreFile(uri: vscode.Uri): Promise<string> {
60+
try {
61+
const data = await vscode.workspace.fs.readFile(uri);
62+
return Buffer.from(data).toString('utf8').replace(/^\uFEFF/, '');
63+
} catch {
64+
return '';
65+
}
66+
}
67+
3668
private parseGitignore(content: string): GitignoreRule[] {
3769

3870
const rules: GitignoreRule[] = [];
@@ -71,23 +103,47 @@ export class GitignoreService {
71103
return rules;
72104
}
73105

74-
private matchGitignore(rules: GitignoreRule[], relativePath: string): boolean {
106+
private matchGitignore(scopes: GitignoreScope[], relativePath: string): boolean {
75107

76108
const normalized = relativePath.replace(/\\/g, '/');
77109
let ignored = false;
110+
const pathSegments = normalized.split('/');
111+
112+
for (const scope of scopes) {
113+
if (!this.scopeApplies(scope.rootRelativeDir, pathSegments)) {
114+
continue;
115+
}
116+
117+
const scopedRelativePath = scope.rootRelativeDir === '.'
118+
? normalized
119+
: normalized.slice(scope.rootRelativeDir.length + 1);
78120

79-
for (const rule of rules) {
80-
const matches = rule.isDirectory
81-
? rule.regex.test(normalized + '/')
82-
: rule.regex.test(normalized);
83-
if (matches) {
84-
ignored = !rule.negate;
121+
for (const rule of scope.rules) {
122+
const matches = rule.isDirectory
123+
? rule.regex.test(scopedRelativePath + '/')
124+
: rule.regex.test(scopedRelativePath);
125+
if (matches) {
126+
ignored = !rule.negate;
127+
}
85128
}
86129
}
87130

88131
return ignored;
89132
}
90133

134+
private scopeApplies(rootRelativeDir: string, pathSegments: string[]): boolean {
135+
if (rootRelativeDir === '.' || rootRelativeDir.length === 0) {
136+
return true;
137+
}
138+
139+
const scopeSegments = rootRelativeDir.split('/').filter(Boolean);
140+
if (scopeSegments.length === 0 || scopeSegments.length > pathSegments.length) {
141+
return false;
142+
}
143+
144+
return scopeSegments.every((segment, index) => pathSegments[index] === segment);
145+
}
146+
91147
private gitignorePatternToRegex(pattern: string): RegExp {
92148
let pat = pattern.replace(/\\/g, '/');
93149
let anchored = false;

0 commit comments

Comments
 (0)