file and folder copy uniform

This commit is contained in:
Andrew Pareles 2025-05-12 18:20:44 -07:00
parent 08274637e1
commit 7066b4285b
2 changed files with 93 additions and 81 deletions

View file

@ -6,47 +6,11 @@ import { INotificationService } from '../../../../platform/notification/common/n
import { IFileService } from '../../../../platform/files/common/files.js';
import { IClipboardService } from '../../../../platform/clipboard/common/clipboardService.js';
import { IDirectoryStrService } from '../common/directoryStrService.js';
import { readFile, DEFAULT_FILE_SIZE_LIMIT } from '../common/prompt/prompts.js';
import { messageOfSelection } from '../common/prompt/prompts.js';
import { IVoidModelService } from '../common/voidModelService.js';
async function filePrompt(fileService: IFileService, uri: URI, clipboardService: IClipboardService): Promise<string> {
const { val } = await readFile(fileService, uri, DEFAULT_FILE_SIZE_LIMIT)
const fileName = uri.fsPath.split('/').pop() ?? ''
if (!val) {
throw new Error('Failed to copy prompt')
}
const prompt = `
${fileName}:
\`\`\`
${val}
\`\`\``.trim()
return prompt
}
/**
* Add a menu item to the explorer context menu that copies a prompt for the selected file or directory.
*
* Example file prompt:
*
* ```
* index.js:
* \`\`\`
* console.log('Hello World!');
* \`\`\`
*
* Example directory prompt:
* ```
* Directory of /path/to/src:
* src/
* index.ts
* src.ts
* latest/
* index.ts
* src.ts
* types.ts
* util.ts
* ```
*/
class FilePromptActionService extends Action2 {
private static readonly VOID_COPY_FILE_PROMPT_ID = 'void.copyfileprompt'
@ -67,22 +31,48 @@ class FilePromptActionService extends Action2 {
const fileService = accessor.get(IFileService);
const clipboardService = accessor.get(IClipboardService)
const directoryStrService = accessor.get(IDirectoryStrService)
const stat = await fileService.resolve(uri)
const prompt = stat.isFile
? await filePrompt(fileService, uri, clipboardService)
: await directoryStrService.getDirectoryStrTool(uri)
await clipboardService.writeText(prompt)
const voidModelService = accessor.get(IVoidModelService)
const stat = await fileService.stat(uri)
const folderOpts = {
maxChildren: 1000,
maxCharsPerFile: 2_000_000,
} as const
let m: string = 'No contents detected'
if (stat.isFile) {
m = await messageOfSelection({
type: 'File',
uri,
language: (await voidModelService.getModelSafe(uri)).model?.getLanguageId() || '',
state: { wasAddedAsCurrentFile: false, },
}, {
folderOpts,
directoryStrService,
fileService,
})
}
if (stat.isDirectory) {
m = await messageOfSelection({
type: 'Folder',
uri,
}, {
folderOpts,
fileService,
directoryStrService,
})
}
await clipboardService.writeText(m)
} catch (error) {
const notificationService = accessor.get(INotificationService)
FilePromptActionService._onError(notificationService, error)
notificationService.error(error + '')
}
}
private static _onError(notificationService: INotificationService, error: Error) {
const errorMessage = localize2('voidCopyPromptError', 'Failed to copy prompt')
notificationService.error(errorMessage.value)
throw error
}
}
registerAction2(FilePromptActionService)

View file

@ -551,46 +551,68 @@ export const readFile = async (fileService: IFileService, uri: URI, fileSizeLimi
export const messageOfSelection = async (
s: StagingSelectionItem,
opts: {
directoryStrService: IDirectoryStrService,
fileService: IFileService,
folderOpts: {
maxChildren: number,
maxCharsPerFile: number,
}
}
) => {
const lineNumAddition = (range: [number, number]) => ` (lines ${range[0]}:${range[1]})`
if (s.type === 'File' || s.type === 'CodeSelection') {
const { val } = await readFile(opts.fileService, s.uri, DEFAULT_FILE_SIZE_LIMIT)
const lineNumAdd = s.type === 'CodeSelection' ? lineNumAddition(s.range) : ''
const content = val === null ? 'null' : `${tripleTick[0]}${s.language}\n${val}\n${tripleTick[1]}`
const str = `${s.uri.fsPath}${lineNumAdd}:\n${content}`
return str
}
else if (s.type === 'Folder') {
const dirStr: string = await opts.directoryStrService.getDirectoryStrTool(s.uri)
const folderStructure = `${s.uri.fsPath} folder structure:${tripleTick[0]}\n${dirStr}\n${tripleTick[1]}`
const uris = await opts.directoryStrService.getAllURIsInDirectory(s.uri, { maxResults: opts.folderOpts.maxChildren })
const strOfFiles = await Promise.all(uris.map(async uri => {
const { val, truncated } = await readFile(opts.fileService, uri, opts.folderOpts.maxCharsPerFile)
const truncationStr = truncated ? `\n... file truncated ...` : ''
const content = val === null ? 'null' : `${tripleTick[0]}\n${val}${truncationStr}\n${tripleTick[1]}`
const str = `${uri.fsPath}:\n${content}`
return str
}))
const contentStr = [folderStructure, ...strOfFiles].join('\n\n')
return contentStr
}
else
return ''
}
export const chat_userMessageContent = async (instructions: string, currSelns: StagingSelectionItem[] | null,
opts: { directoryStrService: IDirectoryStrService, fileService: IFileService }
export const chat_userMessageContent = async (
instructions: string,
currSelns: StagingSelectionItem[] | null,
opts: {
directoryStrService: IDirectoryStrService,
fileService: IFileService
},
) => {
const lineNumAddition = (range: [number, number]) => ` (lines ${range[0]}:${range[1]})`
let selnsStrs: string[] = []
selnsStrs = await Promise.all(currSelns?.map(async (s) => {
const selnsStrs = await Promise.all((currSelns ?? []).map(async (s) =>
messageOfSelection(s, {
...opts,
folderOpts: { maxChildren: 100, maxCharsPerFile: 100_000, }
})
))
if (s.type === 'File' || s.type === 'CodeSelection') {
const { val } = await readFile(opts.fileService, s.uri, DEFAULT_FILE_SIZE_LIMIT)
const lineNumAdd = s.type === 'CodeSelection' ? lineNumAddition(s.range) : ''
const content = val === null ? 'null' : `${tripleTick[0]}${s.language}\n${val}\n${tripleTick[1]}`
const str = `${s.uri.fsPath}${lineNumAdd}:\n${content}`
return str
}
else if (s.type === 'Folder') {
const dirStr: string = await opts.directoryStrService.getDirectoryStrTool(s.uri)
const folderStructure = `${s.uri.fsPath} folder structure:${tripleTick[0]}\n${dirStr}\n${tripleTick[1]}`
const uris = await opts.directoryStrService.getAllURIsInDirectory(s.uri, { maxResults: 100 })
const strOfFiles = await Promise.all(uris.map(async uri => {
const { val, truncated } = await readFile(opts.fileService, uri, 100_000)
const truncationStr = truncated ? `\n... file truncated ...` : ''
const content = val === null ? 'null' : `${tripleTick[0]}\n${val}${truncationStr}\n${tripleTick[1]}`
const str = `${uri.fsPath}:\n${content}`
return str
}))
const contentStr = [folderStructure, ...strOfFiles].join('\n\n')
return contentStr
}
else
return ''
}) ?? [])
const selnsStr = selnsStrs.join('\n') ?? ''
let str = ''
str += `${instructions}`
const selnsStr = selnsStrs.join('\n\n') ?? ''
if (selnsStr) str += `\n---\nSELECTIONS\n${selnsStr}`
return str;
}