Skip to content

Commit d6639c6

Browse files
authored
Merge pull request #1 from KebanFiru/feat/implement_basic_usage
Feat/implement basic usage
2 parents 0f71438 + 92f0331 commit d6639c6

10 files changed

Lines changed: 1176 additions & 10 deletions

File tree

.vscodeignore

Lines changed: 11 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,11 @@
1+
.vscode/**
2+
.vscode-test/**
3+
src/**
4+
.gitignore
5+
.yarnrc
6+
vsc-extension-quickstart.md
7+
**/tsconfig.json
8+
**/eslint.config.mjs
9+
**/*.map
10+
**/*.ts
11+
**/.vscode-test.*

eslint.config.mjs

Lines changed: 27 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,27 @@
1+
import typescriptEslint from "typescript-eslint";
2+
3+
export default [{
4+
files: ["**/*.ts"],
5+
}, {
6+
plugins: {
7+
"@typescript-eslint": typescriptEslint.plugin,
8+
},
9+
10+
languageOptions: {
11+
parser: typescriptEslint.parser,
12+
ecmaVersion: 2022,
13+
sourceType: "module",
14+
},
15+
16+
rules: {
17+
"@typescript-eslint/naming-convention": ["warn", {
18+
selector: "import",
19+
format: ["camelCase", "PascalCase"],
20+
}],
21+
22+
curly: "warn",
23+
eqeqeq: "warn",
24+
"no-throw-literal": "warn",
25+
semi: "warn",
26+
},
27+
}];

package.json

Lines changed: 39 additions & 6 deletions
Original file line numberDiff line numberDiff line change
@@ -12,15 +12,31 @@
1212
"activationEvents": [
1313
"onCommand:codecount.countLines",
1414
"onCommand:codecount.countFilesLines",
15-
"onCommand:codecount.countLinesByExtension"
15+
"onCommand:codecount.countLinesByExtension",
16+
"onCommand:codecount.refreshStats",
17+
"onCommand:codecount.gitRepoMissing",
18+
"onView:codecount.stats"
1619
],
1720
"main": "./out/extension.js",
1821
"contributes": {
22+
"viewsContainers": {
23+
"activitybar": [
24+
{
25+
"id": "codecount",
26+
"title": "CodeCount",
27+
"icon": "resources/codecount.svg"
28+
}
29+
]
30+
},
31+
"views": {
32+
"codecount": [
33+
{
34+
"id": "codecount.stats",
35+
"name": "CodeCount"
36+
}
37+
]
38+
},
1939
"commands": [
20-
{
21-
"command": "codecount.helloWorld",
22-
"title": "Hello World"
23-
},
2440
{
2541
"command": "codecount.countLines",
2642
"title": "Count Lines of Code"
@@ -32,8 +48,25 @@
3248
{
3349
"command": "codecount.countLinesByExtension",
3450
"title": "Count Lines of Code by Extension"
51+
},
52+
{
53+
"command": "codecount.refreshStats",
54+
"title": "Refresh CodeCount Stats"
55+
},
56+
{
57+
"command": "codecount.gitRepoMissing",
58+
"title": "Git Repo Missing"
3559
}
36-
]
60+
],
61+
"menus": {
62+
"view/title": [
63+
{
64+
"command": "codecount.refreshStats",
65+
"when": "view == codecount.stats",
66+
"group": "navigation"
67+
}
68+
]
69+
}
3770
},
3871
"scripts": {
3972
"vscode:prepublish": "pnpm run compile",

src/extension.ts

Lines changed: 83 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -1,14 +1,19 @@
1+
import * as path from 'path';
12
import * as vscode from 'vscode';
23
import { LineCounter } from './services/LineCounter';
34
import { GitignoreService } from './services/GitignoreService';
45
import { WorkspaceLineCounter } from './services/WorkspaceLineCounter';
56
import { LineCounterByFileFormat } from './services/LineCounterByFileFormat';
7+
import { StatsTreeProvider } from './views/StatsTreeProvider';
8+
import { StatsService } from './services/StatsService';
69

710
export function activate(context: vscode.ExtensionContext) {
811
const lineCounter = new LineCounter();
912
const gitignoreService = new GitignoreService();
1013
const workspaceLineCounter = new WorkspaceLineCounter(lineCounter, gitignoreService);
1114
const lineCounterByFileFormat = new LineCounterByFileFormat(workspaceLineCounter);
15+
const statsService = new StatsService(lineCounter, gitignoreService);
16+
const statsProvider = new StatsTreeProvider(statsService);
1217

1318
const fileline = vscode.commands.registerCommand('codecount.countLines', () => {
1419
const editor = vscode.window.activeTextEditor;
@@ -66,10 +71,87 @@ export function activate(context: vscode.ExtensionContext) {
6671
);
6772
});
6873

74+
const refreshStats = vscode.commands.registerCommand('codecount.refreshStats', () => {
75+
statsProvider.refresh();
76+
});
77+
78+
const gitRepoMissing = vscode.commands.registerCommand('codecount.gitRepoMissing', () => {
79+
vscode.window.showWarningMessage('Git repo hasn’t been defined.');
80+
});
81+
82+
const openContributorFile = vscode.commands.registerCommand(
83+
'codecount.openContributorFile',
84+
async (args?: { author?: string; filePath?: string }) => {
85+
const author = args?.author;
86+
const filePath = args?.filePath;
87+
if (!author || !filePath) {
88+
return;
89+
}
90+
const rootPath = await statsService.getGitRootPath();
91+
if (!rootPath) {
92+
vscode.window.showWarningMessage('Git repository not found.');
93+
return;
94+
}
95+
96+
const absolutePath = vscode.Uri.file(path.join(rootPath, filePath));
97+
try {
98+
await vscode.workspace.fs.stat(absolutePath);
99+
const document = await vscode.workspace.openTextDocument(absolutePath);
100+
await vscode.window.showTextDocument(document, { preview: false });
101+
} catch {
102+
vscode.window.showWarningMessage('File not found in workspace.');
103+
return;
104+
}
105+
106+
const history = await statsService.getContributorFileHistory(author, filePath);
107+
if (!history.available) {
108+
vscode.window.showWarningMessage('Git repository not found.');
109+
return;
110+
}
111+
if (history.entries.length === 0) {
112+
vscode.window.showInformationMessage('No history found for this contributor.');
113+
return;
114+
}
115+
116+
const pick = await vscode.window.showQuickPick(
117+
history.entries.map((entry) => ({
118+
label: `${entry.date} ${entry.hash}`,
119+
hash: entry.hash
120+
})),
121+
{
122+
placeHolder: 'Select a commit to view changes'
123+
}
124+
);
125+
if (!pick) {
126+
return;
127+
}
128+
129+
const gitPath = path.join(rootPath, filePath);
130+
const encode = (ref: string) =>
131+
vscode.Uri.parse(
132+
`git:${gitPath}?${encodeURIComponent(JSON.stringify({ path: gitPath, ref }))}`
133+
);
134+
135+
const left = encode(`${pick.hash}^`);
136+
const right = encode(pick.hash);
137+
await vscode.commands.executeCommand(
138+
'vscode.diff',
139+
left,
140+
right,
141+
`Changes by ${author}: ${filePath} (${pick.hash})`
142+
);
143+
}
144+
);
145+
69146
context.subscriptions.push(fileline);
70147
context.subscriptions.push(fileslines);
71148
context.subscriptions.push(fileslinesByExtension);
149+
context.subscriptions.push(refreshStats);
150+
context.subscriptions.push(gitRepoMissing);
151+
context.subscriptions.push(openContributorFile);
152+
context.subscriptions.push(
153+
vscode.window.registerTreeDataProvider('codecount.stats', statsProvider)
154+
);
72155
}
73156

74-
// This method is called when your extension is deactivated
75157
export function deactivate() {}

src/services/GitignoreService.ts

Lines changed: 10 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -22,8 +22,9 @@ export class GitignoreService {
2222
try {
2323
const data = await vscode.workspace.fs.readFile(gitignoreUri);
2424
content = data.toString();
25-
} catch {
26-
// .gitignore not found or not readable; ignore silently
25+
}
26+
catch {
27+
// .gitignore not found
2728
}
2829

2930
const rules = this.parseGitignore(content);
@@ -33,18 +34,22 @@ export class GitignoreService {
3334
}
3435

3536
private parseGitignore(content: string): GitignoreRule[] {
37+
3638
const rules: GitignoreRule[] = [];
3739
const lines = content.split(/\r?\n/);
40+
3841
for (const rawLine of lines) {
3942
if (!rawLine || rawLine.trim().length === 0) {
4043
continue;
4144
}
4245

4346
let line = rawLine;
4447
let negate = false;
48+
4549
if (line.startsWith('\\#')) {
4650
line = line.slice(1);
47-
} else if (line.startsWith('#')) {
51+
}
52+
else if (line.startsWith('#')) {
4853
continue;
4954
}
5055

@@ -67,8 +72,10 @@ export class GitignoreService {
6772
}
6873

6974
private matchGitignore(rules: GitignoreRule[], relativePath: string): boolean {
75+
7076
const normalized = relativePath.replace(/\\/g, '/');
7177
let ignored = false;
78+
7279
for (const rule of rules) {
7380
const matches = rule.isDirectory
7481
? rule.regex.test(normalized + '/')

0 commit comments

Comments
 (0)