ソースを参照

feat: implement copy context logic with formatting and status bar

Developer 1 ヶ月 前
コミット
6333567f67
1 ファイル変更176 行追加0 行削除
  1. 176 0
      src/copyContext.ts

+ 176 - 0
src/copyContext.ts

@@ -0,0 +1,176 @@
+import * as vscode from 'vscode';
+
+/**
+ * Infer language identifier from file extension.
+ */
+function getLanguage(filePath: string): string {
+  const ext = filePath.split('.').pop() ?? '';
+  const langMap: Record<string, string> = {
+    ts: 'typescript',
+    tsx: 'typescriptreact',
+    js: 'javascript',
+    jsx: 'javascriptreact',
+    json: 'json',
+    md: 'markdown',
+    css: 'css',
+    html: 'html',
+    py: 'python',
+    rs: 'rust',
+    go: 'go',
+    java: 'java',
+    rb: 'ruby',
+    php: 'php',
+    yml: 'yaml',
+    yaml: 'yaml',
+    sh: 'bash',
+    bash: 'bash',
+    sql: 'sql',
+    vue: 'vue',
+    svelte: 'svelte',
+  };
+  return langMap[ext] ?? ext;
+}
+
+/**
+ * Format a single file's content as AI context.
+ * Output: [path/to/file.ts]\n```lang\ncontent\n```\n
+ */
+export function formatFileContent(filePath: string, content: string): string {
+  const lang = getLanguage(filePath);
+  return [
+    `[${filePath}]`,
+    '```' + lang,
+    content,
+    '```',
+    '',
+  ].join('\n');
+}
+
+/**
+ * Format a selection within a file as AI context.
+ * Output: [path/to/file.ts LnCn-LnCn]\n```lang\ncontent\n```\n
+ */
+export function formatFileSelection(
+  filePath: string,
+  content: string,
+  startLine: number,
+  startCol: number,
+  endLine: number,
+  endCol: number,
+): string {
+  const lang = getLanguage(filePath);
+  const header = `${filePath} L${startLine}C${startCol}-L${endLine}C${endCol}`;
+  return [
+    `[${header}]`,
+    '```' + lang,
+    content,
+    '```',
+    '',
+  ].join('\n');
+}
+
+/**
+ * Format multiple file contents, joining with blank lines.
+ */
+export function formatMultiFile(entries: { filePath: string; content: string }[]): string {
+  return entries.map(e => formatFileContent(e.filePath, e.content)).join('\n');
+}
+
+/**
+ * Copy the current editor selection to clipboard with AI-friendly formatting.
+ */
+export async function copySelection(): Promise<void> {
+  const editor = vscode.window.activeTextEditor;
+  if (!editor) return;
+
+  const selection = editor.selection;
+  if (selection.isEmpty) return;
+
+  const document = editor.document;
+  const filePath = vscode.workspace.asRelativePath(document.uri);
+  const text = document.getText(selection);
+
+  // VS Code selections are 0-indexed for lines, columns are 0-indexed UTF-16 code units
+  const startLine = selection.start.line + 1;
+  const startCol = selection.start.character + 1;
+  const endLine = selection.end.line + 1;
+  const endCol = selection.end.character + 1;
+
+  const result = formatFileSelection(filePath, text, startLine, startCol, endLine, endCol);
+
+  await vscode.env.clipboard.writeText(result);
+  showStatus(`$(clippy) Copied ${filePath}:${startLine}-${endLine}`);
+}
+
+/**
+ * Copy the entire active file to clipboard with AI-friendly formatting.
+ */
+export async function copyActiveFile(): Promise<void> {
+  const editor = vscode.window.activeTextEditor;
+  if (!editor) return;
+
+  const document = editor.document;
+  const filePath = vscode.workspace.asRelativePath(document.uri);
+  const content = document.getText();
+  const result = formatFileContent(filePath, content);
+
+  await vscode.env.clipboard.writeText(result);
+  showStatus(`$(clippy) Copied file: ${filePath}`);
+}
+
+/**
+ * Select files from the workspace and copy their contents.
+ */
+export async function copyWorkspaceFiles(): Promise<void> {
+  const workspaceFolders = vscode.workspace.workspaceFolders;
+  if (!workspaceFolders || workspaceFolders.length === 0) return;
+
+  const uris = await vscode.window.showOpenDialog({
+    canSelectFiles: true,
+    canSelectFolders: false,
+    canSelectMany: true,
+    openLabel: 'Select files to copy as context',
+  });
+
+  if (!uris || uris.length === 0) return;
+
+  await copyFilesFromUris(uris);
+}
+
+/**
+ * Copy files selected in the explorer context menu.
+ */
+export async function copySelectedFiles(selectedUris: vscode.Uri[]): Promise<void> {
+  if (!selectedUris || selectedUris.length === 0) return;
+  await copyFilesFromUris(selectedUris);
+}
+
+/**
+ * Read and format multiple files, then copy to clipboard.
+ */
+async function copyFilesFromUris(uris: vscode.Uri[]): Promise<void> {
+  const entries: { filePath: string; content: string }[] = [];
+
+  for (const uri of uris) {
+    try {
+      const doc = await vscode.workspace.openTextDocument(uri);
+      const filePath = vscode.workspace.asRelativePath(uri);
+      entries.push({ filePath, content: doc.getText() });
+    } catch {
+      // skip binary or unreadable files
+    }
+  }
+
+  if (entries.length === 0) return;
+
+  const result = formatMultiFile(entries);
+  await vscode.env.clipboard.writeText(result);
+  showStatus(`$(clippy) Copied ${entries.length} file(s)`);
+}
+
+/**
+ * Show a temporary status bar message.
+ */
+function showStatus(message: string): void {
+  vscode.window.setStatusBarMessage(message, 3000);
+}