diff --git a/src/commands/markFile.ts b/src/commands/markFile.ts index ad9c9ae..071f27e 100644 --- a/src/commands/markFile.ts +++ b/src/commands/markFile.ts @@ -23,7 +23,7 @@ export const markFile = { action: () => void, message: string, markedFilesProvider: MarkedFilesProvider, - ) { + ): Promise { action(); showMessage.info(message); await markedFilesProvider.refresh(); @@ -61,30 +61,43 @@ export const markFile = { return; } + if (markedFiles.has(filePath)) { + this.unmarkFile(filePath, markedFilesProvider); + } else { + await this.handleSingleFile(filePath, workspacePath, markedFilesProvider); + } + }, + + async handleSingleFile( + filePath: string, + workspacePath: string, + markedFilesProvider: MarkedFilesProvider, + ): Promise { const relPath = getRelativePath(workspacePath, filePath); + if (isIgnored(relPath)) { showMessage.warning( - `Cannot mark "${getBasename(filePath)}": File is excluded\nThis file matches patterns in your ignore files (.gitignore, .dockerignore, etc.)\nPath: ${relPath}`, + `Note: "${getBasename(filePath)}" matches patterns in your ignore files but will be marked anyway since it was specifically selected.`, ); - return; } if (!this.isFileTypeSupported(filePath)) { showMessage.warning( `Cannot mark "${getBasename(filePath)}": Unsupported file type (.${getExtension(filePath)})\nSupported types: ${getConfig().detectedFileExtensions.join(', ')}\nYou can add more file types in Settings > LLM Context Generator > Detected File Extensions`, ); - return; + return false; } - if (markedFiles.has(filePath)) { - this.unmarkFile(filePath, markedFilesProvider); - } else { + if (!markedFiles.has(filePath)) { this.updateMarkedFiles( () => markedFiles.add(filePath), `Marked: ${getBasename(filePath)}`, markedFilesProvider, ); + } else { + showMessage.info('Selected file is already marked.'); } + return true; }, async unmarkFromTreeView( @@ -113,6 +126,16 @@ export const markFile = { return; } + // Special case: single non-directory file selected + if (uris.length === 1 && !isDirectory(uris[0].fsPath)) { + await this.handleSingleFile( + uris[0].fsPath, + workspacePath, + markedFilesProvider, + ); + return; + } + const newFiles = new Set(); const ignoredFiles = new Set(); const unsupportedFiles = new Set(); @@ -131,7 +154,13 @@ export const markFile = { return; } } - await this.processPath(filePath, newFiles, workspacePath); + await this.processPath( + filePath, + newFiles, + ignoredFiles, + unsupportedFiles, + workspacePath, + ); }), ); @@ -175,15 +204,20 @@ export const markFile = { async processPath( path: string, newFiles: Set, + ignoredFiles: Set, + unsupportedFiles: Set, workspacePath: string, ): Promise { if (!isDirectory(path)) { const relPath = getRelativePath(workspacePath, path); + // Only check ignore status for files that are part of bulk operations if (isIgnored(relPath)) { - return; // Skip warning as it's handled at higher level + ignoredFiles.add(path); + return; } if (!this.isFileTypeSupported(path)) { - return; // Skip warning as it's handled at higher level + unsupportedFiles.add(path); + return; } if (!markedFiles.has(path)) { newFiles.add(path); @@ -199,10 +233,17 @@ export const markFile = { const relPath = getRelativePath(workspacePath, filePath); if (isIgnored(relPath)) { + ignoredFiles.add(filePath); return; } - await this.processPath(filePath, newFiles, workspacePath); + await this.processPath( + filePath, + newFiles, + ignoredFiles, + unsupportedFiles, + workspacePath, + ); }), ); } catch (error) { diff --git a/src/generators/contextGenerator.ts b/src/generators/contextGenerator.ts index bd2298f..2082bf2 100644 --- a/src/generators/contextGenerator.ts +++ b/src/generators/contextGenerator.ts @@ -8,6 +8,7 @@ import { getExtension, resolvePath, getDirname, + getBasename, } from '../utils/fileUtils'; import { formatFileComment } from '../utils/markdownUtils'; import { estimateTokenCount } from '../utils/tokenUtils'; @@ -94,12 +95,28 @@ export class ContextGenerator { }; } + private async handleSingleFile( + filePath: string, + relPath: string, + contextParts: string[], + ): Promise { + if (isIgnored(relPath)) { + showMessage.warning( + `Note: "${getBasename(filePath)}" matches patterns in your ignore files but will be included anyway since it was specifically selected.`, + ); + } + + if (!isDirectory(filePath)) { + await this.processFile(filePath, relPath, contextParts); + } + } + private async processOpenFile( filePath: string, contextParts: string[], ): Promise { const relPath = getRelativePath(this.workspacePath, filePath); - await this.processFile(filePath, relPath, contextParts); + await this.handleSingleFile(filePath, relPath, contextParts); const content = readFileContent(filePath); await this.processImports(filePath, content, contextParts); @@ -115,6 +132,7 @@ export class ContextGenerator { const resolvedPath = resolvePath(getDirname(filePath), importPath); const relPath = getRelativePath(this.workspacePath, resolvedPath); + // For imports, we do respect ignore patterns if (isIgnored(relPath)) { continue; } @@ -156,6 +174,7 @@ export class ContextGenerator { const filePath = resolvePath(dir, file); const relPath = getRelativePath(this.workspacePath, filePath); + // For workspace-wide scanning, respect ignore patterns if (isIgnored(relPath)) { continue; } @@ -172,9 +191,18 @@ export class ContextGenerator { files: string[], contextParts: string[], ): Promise { + // Special case for single marked file + if (files.length === 1) { + const filePath = files[0]; + const relPath = getRelativePath(this.workspacePath, filePath); + await this.handleSingleFile(filePath, relPath, contextParts); + return; + } + + // For multiple files, trust the marking process's decisions for (const filePath of files) { const relPath = getRelativePath(this.workspacePath, filePath); - if (!isIgnored(relPath) && !isDirectory(filePath)) { + if (!isDirectory(filePath)) { await this.processFile(filePath, relPath, contextParts); } }