Skip to content

Commit df3e0d9

Browse files
committed
added auto exclude
1 parent ae6c5f4 commit df3e0d9

3 files changed

Lines changed: 219 additions & 20 deletions

File tree

README.md

Lines changed: 35 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -43,6 +43,41 @@ Generate comprehensive reports that include:
4343
- **Toggle AI Style** (zap/settings icon): Switch between analysis modes
4444
- **Refresh** (refresh icon): Update the file tree view
4545

46+
## Settings
47+
48+
The extension can be configured through VS Code settings:
49+
50+
### Auto-Exclude Settings
51+
- `repotxt.autoExcludeEnabled`: Enable/disable automatic file exclusion
52+
- Default: `true`
53+
- When enabled, automatically excludes common development files and folders
54+
55+
- `repotxt.autoExcludePatterns`: Patterns of files and folders to automatically exclude
56+
- Default patterns:
57+
```
58+
node_modules
59+
.git
60+
dist
61+
build
62+
out
63+
coverage
64+
.env
65+
*.log
66+
package-lock.json
67+
yarn.lock
68+
```
69+
- Supports glob patterns like `*.log`
70+
71+
### Git Integration
72+
- `repotxt.respectGitignore`: Automatically exclude files listed in .gitignore
73+
- Default: `true`
74+
- When enabled, reads and respects your repository's .gitignore file
75+
76+
You can modify these settings in VS Code:
77+
1. Open Settings (Ctrl/Cmd + ,)
78+
2. Search for "Repository Analyzer"
79+
3. Adjust settings as needed
80+
4681
## Requirements
4782
4883
- Visual Studio Code version 1.96.0 or higher

package.json

Lines changed: 35 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -4,7 +4,7 @@
44
"description": "Analyze and generate reports of repository content",
55
"repository": "https://github.com/Vasysik/repotxt",
66

7-
"version": "0.0.1",
7+
"version": "0.0.2",
88
"engines": {
99
"vscode": "^1.96.0"
1010
},
@@ -82,6 +82,40 @@
8282
"group": "navigation@3"
8383
}
8484
]
85+
},
86+
"configuration": {
87+
"title": "Repository Analyzer",
88+
"properties": {
89+
"repotxt.autoExcludeEnabled": {
90+
"type": "boolean",
91+
"default": true,
92+
"description": "Enable/disable automatic file exclusion"
93+
},
94+
"repotxt.autoExcludePatterns": {
95+
"type": "array",
96+
"default": [
97+
"node_modules",
98+
".git",
99+
"dist",
100+
"build",
101+
"out",
102+
"coverage",
103+
".env",
104+
"*.log",
105+
"package-lock.json",
106+
"yarn.lock"
107+
],
108+
"items": {
109+
"type": "string"
110+
},
111+
"description": "Patterns to automatically exclude from the repository analysis"
112+
},
113+
"repotxt.respectGitignore": {
114+
"type": "boolean",
115+
"default": true,
116+
"description": "Automatically exclude files and folders listed in .gitignore"
117+
}
118+
}
85119
}
86120
},
87121
"scripts": {

src/extension.ts

Lines changed: 149 additions & 19 deletions
Original file line numberDiff line numberDiff line change
@@ -1,13 +1,20 @@
11
import * as vscode from 'vscode';
22
import * as fs from 'fs';
33
import * as path from 'path';
4+
import * as readline from 'readline';
45

56
interface FileTreeItem extends vscode.TreeItem {
67
children?: FileTreeItem[];
78
fullPath: string;
89
excluded: boolean;
910
}
1011

12+
interface ExclusionSettings {
13+
autoExcludePatterns: string[];
14+
respectGitignore: boolean;
15+
autoExcludeEnabled: boolean;
16+
}
17+
1118
class RepoAnalyzerProvider implements vscode.TreeDataProvider<FileTreeItem> {
1219
private _onDidChangeTreeData = new vscode.EventEmitter<FileTreeItem | undefined | void>();
1320
readonly onDidChangeTreeData = this._onDidChangeTreeData.event;
@@ -16,11 +23,13 @@ class RepoAnalyzerProvider implements vscode.TreeDataProvider<FileTreeItem> {
1623
private workspaceRoot: string | undefined;
1724
private _useAIStyle: boolean = false;
1825
private fileWatcher: vscode.FileSystemWatcher | undefined;
26+
private gitignorePatterns: string[] = [];
1927

2028
constructor(context: vscode.ExtensionContext) {
2129
if (vscode.workspace.workspaceFolders) {
2230
this.workspaceRoot = vscode.workspace.workspaceFolders[0].uri.fsPath;
2331
this.setupFileWatcher();
32+
this.initializeExclusions().then(() => { this.refresh(); });
2433
}
2534

2635
this._useAIStyle = context.globalState.get('repotxt.useAIStyle', false);
@@ -29,13 +38,139 @@ class RepoAnalyzerProvider implements vscode.TreeDataProvider<FileTreeItem> {
2938
if (vscode.workspace.workspaceFolders) {
3039
this.workspaceRoot = vscode.workspace.workspaceFolders[0].uri.fsPath;
3140
this.setupFileWatcher();
32-
this.refresh();
41+
this.initializeExclusions().then(() => { this.refresh(); });
42+
}
43+
});
44+
45+
vscode.workspace.onDidChangeConfiguration(e => {
46+
if (e.affectsConfiguration('repotxt')) {
47+
this.initializeExclusions().then(() => { this.refresh(); });
48+
}
49+
});
50+
}
51+
52+
private async checkForAutoExclusion(filePath: string) {
53+
const config = vscode.workspace.getConfiguration('repotxt');
54+
const autoExcludeEnabled = config.get('autoExcludeEnabled', true);
55+
56+
if (!autoExcludeEnabled) {
57+
return;
58+
}
59+
60+
const patterns = config.get<string[]>('autoExcludePatterns', []);
61+
const fileName = path.basename(filePath);
62+
const relativePath = path.relative(this.workspaceRoot!, filePath);
63+
64+
const shouldExclude = patterns.some(pattern => {
65+
if (pattern === fileName || pattern === relativePath) {
66+
return true;
3367
}
68+
69+
if (pattern.includes('*')) {
70+
const regex = new RegExp('^' + pattern.replace(/\*/g, '.*') + '$');
71+
return regex.test(fileName) || regex.test(relativePath);
72+
}
73+
74+
return false;
3475
});
76+
77+
if (shouldExclude) {
78+
this.excludedPaths.add(filePath);
79+
if (fs.statSync(filePath).isDirectory()) {
80+
this.excludeDirectory(filePath);
81+
}
82+
}
83+
}
84+
85+
private async initializeExclusions() {
86+
if (!this.workspaceRoot) return;
87+
88+
const config = vscode.workspace.getConfiguration('repotxt');
89+
const settings: ExclusionSettings = {
90+
autoExcludeEnabled: config.get('autoExcludeEnabled', true),
91+
autoExcludePatterns: config.get('autoExcludePatterns', [
92+
'node_modules',
93+
'.git',
94+
'dist',
95+
'build',
96+
'out',
97+
'coverage',
98+
'.env',
99+
'*.log',
100+
'package-lock.json',
101+
'yarn.lock'
102+
]),
103+
respectGitignore: config.get('respectGitignore', true)
104+
};
105+
106+
this.excludedPaths.clear();
107+
108+
if (!settings.autoExcludeEnabled) {
109+
return;
110+
}
111+
112+
if (settings.autoExcludePatterns.length > 0) {
113+
await this.processExcludePatterns(settings.autoExcludePatterns);
114+
}
115+
116+
if (settings.respectGitignore) {
117+
await this.processGitignore();
118+
}
119+
}
120+
121+
private async processGitignore() {
122+
if (!this.workspaceRoot) return;
123+
124+
const gitignorePath = path.join(this.workspaceRoot, '.gitignore');
125+
if (!fs.existsSync(gitignorePath)) return;
126+
127+
try {
128+
const fileStream = fs.createReadStream(gitignorePath);
129+
const rl = readline.createInterface({
130+
input: fileStream,
131+
crlfDelay: Infinity
132+
});
133+
134+
this.gitignorePatterns = [];
135+
for await (const line of rl) {
136+
if (line && !line.startsWith('#') && line.trim()) {
137+
this.gitignorePatterns.push(line.trim());
138+
}
139+
}
140+
141+
await this.processExcludePatterns(this.gitignorePatterns);
142+
} catch (error) {
143+
console.error('Error processing .gitignore:', error);
144+
}
145+
}
146+
147+
private async processExcludePatterns(patterns: string[]) {
148+
if (!this.workspaceRoot) return;
149+
150+
for (const pattern of patterns) {
151+
const fullPattern = path.join(this.workspaceRoot, pattern);
152+
153+
try {
154+
// Handle glob patterns
155+
const matches = await vscode.workspace.findFiles(pattern);
156+
matches.forEach(uri => {
157+
this.excludedPaths.add(uri.fsPath);
158+
});
159+
160+
// Handle direct paths
161+
if (fs.existsSync(fullPattern)) {
162+
this.excludedPaths.add(fullPattern);
163+
if (fs.statSync(fullPattern).isDirectory()) {
164+
this.excludeDirectory(fullPattern);
165+
}
166+
}
167+
} catch (error) {
168+
console.error(`Error processing pattern ${pattern}:`, error);
169+
}
170+
}
35171
}
36172

37173
private setupFileWatcher() {
38-
// Очищаем предыдущий watcher если он был
39174
if (this.fileWatcher) {
40175
this.fileWatcher.dispose();
41176
}
@@ -45,19 +180,20 @@ class RepoAnalyzerProvider implements vscode.TreeDataProvider<FileTreeItem> {
45180
new vscode.RelativePattern(this.workspaceRoot, '**/*')
46181
);
47182

48-
this.fileWatcher.onDidCreate(() => this.refresh());
183+
this.fileWatcher.onDidCreate(async (uri) => {
184+
await this.checkForAutoExclusion(uri.fsPath);
185+
this.refresh();
186+
});
49187
this.fileWatcher.onDidDelete(() => this.refresh());
50188
this.fileWatcher.onDidChange(() => this.refresh());
51189
}
52190
}
53191

54192
private isPathExcluded(fullPath: string): boolean {
55-
// Проверяем сам путь
56193
if (this.excludedPaths.has(fullPath)) {
57194
return true;
58195
}
59196

60-
// Проверяем, находится ли путь в исключенной папке
61197
for (const excludedPath of this.excludedPaths) {
62198
if (fullPath.startsWith(excludedPath + path.sep)) {
63199
return true;
@@ -93,7 +229,6 @@ class RepoAnalyzerProvider implements vscode.TreeDataProvider<FileTreeItem> {
93229
}
94230
}
95231

96-
// Добавляем геттер для useAIStyle
97232
get useAIStyle(): boolean {
98233
return this._useAIStyle;
99234
}
@@ -115,7 +250,6 @@ class RepoAnalyzerProvider implements vscode.TreeDataProvider<FileTreeItem> {
115250
treeItem.iconPath = new vscode.ThemeIcon('eye-closed');
116251
treeItem.description = '(excluded)';
117252
treeItem.tooltip = 'Excluded from report';
118-
treeItem.resourceUri = vscode.Uri.parse(`excluded:${element.fullPath}`);
119253
} else {
120254
treeItem.iconPath = element.collapsibleState === vscode.TreeItemCollapsibleState.None ?
121255
new vscode.ThemeIcon('file') :
@@ -307,12 +441,17 @@ export function activate(context: vscode.ExtensionContext) {
307441
showCollapseAll: true
308442
});
309443

310-
// Create status bar item for AI Style toggle
311444
aiStatusBarItem = vscode.window.createStatusBarItem(vscode.StatusBarAlignment.Right);
312445
aiStatusBarItem.command = 'repotxt.toggleAIStyle';
446+
context.subscriptions.push(aiStatusBarItem);
447+
448+
function updateAIStatusBarItem(useAIStyle: boolean) {
449+
aiStatusBarItem.text = useAIStyle ? '$(sparkle) AI Style' : '$(symbol-boolean) Regular Style';
450+
aiStatusBarItem.tooltip = useAIStyle ? 'Click to disable AI Style' : 'Click to enable AI Style';
451+
}
452+
313453
updateAIStatusBarItem(repoAnalyzerProvider.useAIStyle);
314454
aiStatusBarItem.show();
315-
context.subscriptions.push(aiStatusBarItem);
316455

317456
context.subscriptions.push(
318457
vscode.commands.registerCommand('repotxt.refresh', () => {
@@ -351,13 +490,4 @@ export function activate(context: vscode.ExtensionContext) {
351490
);
352491
}
353492

354-
function updateAIStatusBarItem(useAIStyle: boolean) {
355-
aiStatusBarItem.text = useAIStyle ? '$(sparkle) AI Style' : '$(symbol-boolean) Regular Style';
356-
aiStatusBarItem.tooltip = useAIStyle ? 'Click to disable AI Style' : 'Click to enable AI Style';
357-
}
358-
359-
export function deactivate() {
360-
if (aiStatusBarItem) {
361-
aiStatusBarItem.dispose();
362-
}
363-
}
493+
export function deactivate() {}

0 commit comments

Comments
 (0)