add support for recursive selection and deselection of files in the tree view

This commit is contained in:
2025-03-12 07:25:26 +00:00
parent 65bf168e1f
commit 2d28f71e9b
5 changed files with 473 additions and 39 deletions

View File

@@ -3,6 +3,21 @@ import * as fs from 'fs';
import * as path from 'path';
import { PrompterSettings } from '../models/settings';
import { FileReader } from './fileReader';
// Using require for the ignore package due to its module export style
let ignore: any;
try {
ignore = require('ignore');
console.log('Successfully loaded ignore package');
} catch (error) {
console.error('Error loading ignore package:', error);
// Fallback implementation if the package fails to load
ignore = {
default: () => ({
add: () => {},
ignores: () => false
})
};
}
import { TokenEstimator } from './tokenEstimator';
/**
@@ -168,47 +183,117 @@ export class PromptGenerator {
* @returns String representation of the file tree
*/
private static generateFileTree(files: Map<string, any>, rootPath: string): string {
// Create a simple tree representation
// Create a tree representation
const treeLines: string[] = [];
// Group files by directory
const dirMap = new Map<string, string[]>();
// Create a tree structure
interface TreeNode {
name: string;
isDirectory: boolean;
children: Map<string, TreeNode>;
}
// Create root node
const root: TreeNode = {
name: path.basename(rootPath),
isDirectory: true,
children: new Map<string, TreeNode>()
};
// Initialize the ignore instance
let ig;
try {
ig = typeof ignore === 'function' ? ignore() : ignore.default();
console.log('Successfully initialized ignore instance');
} catch (error) {
console.error('Error initializing ignore instance:', error);
// Fallback implementation
ig = {
add: () => {},
ignores: () => false
};
}
// Read .gitignore patterns if available
try {
const gitignorePath = path.join(rootPath, '.gitignore');
if (fs.existsSync(gitignorePath)) {
const gitignoreContent = fs.readFileSync(gitignorePath, 'utf8');
// Add patterns from .gitignore
ig.add(gitignoreContent);
// Always include .gitignore itself
ig.add('!.gitignore');
}
} catch (error) {
console.error('Error reading .gitignore:', error);
}
// Build the tree structure
for (const filePath of files.keys()) {
const relativePath = this.getRelativePath(filePath, rootPath);
const dir = path.dirname(relativePath);
if (!dirMap.has(dir)) {
dirMap.set(dir, []);
// Skip ignored files using the ignore package
// Use forward slashes for paths to ensure consistent matching across platforms
const normalizedPath = relativePath.split(path.sep).join('/');
if (ig.ignores(normalizedPath)) {
continue;
}
dirMap.get(dir)?.push(path.basename(filePath));
}
// Sort directories
const sortedDirs = Array.from(dirMap.keys()).sort();
// Build the tree
for (let i = 0; i < sortedDirs.length; i++) {
const dir = sortedDirs[i];
const isLast = i === sortedDirs.length - 1;
const prefix = isLast ? '└── ' : '├── ';
// Split the path into parts
const parts = relativePath.split('/');
// Skip root directory
if (dir !== '.') {
treeLines.push(`${prefix}${dir}`);
}
// Start from the root
let currentNode = root;
// Add files
const files = dirMap.get(dir)?.sort() || [];
for (let j = 0; j < files.length; j++) {
const file = files[j];
const isLastFile = j === files.length - 1;
const filePrefix = dir === '.' ? (isLastFile ? '└── ' : '├── ') : ' ' + (isLastFile ? '└── ' : '├── ');
treeLines.push(`${filePrefix}${file}`);
// Build the path in the tree
for (let i = 0; i < parts.length; i++) {
const part = parts[i];
const isDirectory = i < parts.length - 1;
if (!currentNode.children.has(part)) {
currentNode.children.set(part, {
name: part,
isDirectory,
children: new Map<string, TreeNode>()
});
} else if (isDirectory) {
// Ensure it's marked as a directory if we encounter it again
currentNode.children.get(part)!.isDirectory = true;
}
currentNode = currentNode.children.get(part)!;
}
}
// Function to recursively build the tree lines
const buildTreeLines = (node: TreeNode, prefix: string = '', isLast: boolean = true, parentPrefix: string = ''): void => {
// Skip the root node in the output
if (node !== root) {
const linePrefix = parentPrefix + (isLast ? '└── ' : '├── ');
treeLines.push(`${linePrefix}${node.name}${node.isDirectory ? '' : ''}`);
}
// Sort children: directories first, then files, both alphabetically
const sortedChildren = Array.from(node.children.values())
.sort((a, b) => {
if (a.isDirectory === b.isDirectory) {
return a.name.localeCompare(b.name);
}
return a.isDirectory ? -1 : 1;
});
// Process children
sortedChildren.forEach((child, index) => {
const isChildLast = index === sortedChildren.length - 1;
const childParentPrefix = node === root ? '' : parentPrefix + (isLast ? ' ' : '│ ');
buildTreeLines(child, prefix, isChildLast, childParentPrefix);
});
};
// Build the tree lines
buildTreeLines(root);
return treeLines.join('\n');
}