Skip to content

feat: implement Editor Context Link feature to enhance chat prompts w…#42

Open
maurice30120 wants to merge 3 commits into
formulahendry:mainfrom
maurice30120:feature/editor-context-link
Open

feat: implement Editor Context Link feature to enhance chat prompts w…#42
maurice30120 wants to merge 3 commits into
formulahendry:mainfrom
maurice30120:feature/editor-context-link

Conversation

@maurice30120

Copy link
Copy Markdown

Editor Context Link

Summary

Editor Context Link lets users include the current VS Code editor context in ACP chat prompts.

When enabled, the extension captures the context at send time and prefixes the user prompt with:

  • the active file path;
  • the cursor position;
  • the selected text, when there is a selection;
  • otherwise, the current line;
  • the list of open file tabs.

Only paths are listed for open editors. Their contents are not sent.

User Experience

The Chat view title bar shows a $(link) icon next to the existing $(attach) file attachment icon.

  • Disabled by default.
  • Clicking the icon toggles editor context linking on or off.
  • The state is persisted in VS Code workspace state.
  • Context is injected only when a prompt is sent.

The chat input stays unchanged. Users type a normal message; the extension enriches the prompt before sending it to the ACP agent.

Prompt Format

With Selected Text

VS Code context:
File: /absolute/path/to/src/example.ts
Cursor: 42:7
Selection: 40:1-43:12

```ts
const value = computeTotal(items);
return value;
```

Open editors:
- /absolute/path/to/src/example.ts
- /absolute/path/to/src/other.ts
- /absolute/path/to/package.json

User prompt:
Explain this code.

Without Selected Text

VS Code context:
File: /absolute/path/to/src/example.ts
Cursor: 42:7
Line: 42

```ts
const value = computeTotal(items);
```

Open editors:
- /absolute/path/to/src/example.ts
- /absolute/path/to/src/other.ts
- /absolute/path/to/package.json

User prompt:
Explain this line.

No Active Editor

If linking is enabled but there is no active text editor, the prompt is sent unchanged and the chat shows a non-blocking error:

No active VS Code editor context.

Implementation

src/ui/EditorContext.ts contains the context logic:

  • captureEditorContext(editor, openEditors) captures the active editor context;
  • captureOpenEditorPaths(tabGroups) extracts local file paths from open VS Code tabs;
  • normalizeOpenEditorPaths(paths) removes duplicates while preserving order;
  • buildEditorContextSection(context) formats the context block;
  • buildPromptWithEditorContext(prompt, context) prefixes the user prompt.

src/ui/ChatWebviewProvider.ts sends the final prompt. It receives a getEditorContext callback from extension.ts, so the webview provider does not directly read VS Code global editor state.

src/extension.ts owns the toggle command:

  • command: acp.toggleEditorContextLink;
  • state key: acp.editorContextLinked;
  • storage: context.workspaceState;
  • toolbar placement: view/title for acp-chat.

Tests

src/test/EditorContext.test.ts covers:

  • formatting selected text context;
  • formatting current-line context;
  • formatting the Open editors section;
  • deduplicating open editor paths while preserving order;
  • leaving prompts unchanged when context is null;
  • using the file extension, such as .ts, as the Markdown code block language.

Copilot AI review requested due to automatic review settings June 8, 2026 07:39

Copilot AI left a comment

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Pull request overview

Note

Copilot was unable to run its full agentic suite in this review.

Adds an “Editor Context Link” feature that can optionally prefix ACP chat prompts with the active VS Code editor context (file, cursor/selection/current line, open tabs), controlled via a toggle command with persisted workspace state.

Changes:

  • Introduces EditorContext capture + prompt/section formatting helpers.
  • Updates chat prompt sending to conditionally inject editor context and adds a toggle command/state plumbing.
  • Adds tests and documentation for the new editor-context behavior.

Reviewed changes

Copilot reviewed 7 out of 7 changed files in this pull request and generated 4 comments.

Show a summary per file
File Description
src/ui/EditorContext.ts New utilities/types to capture editor + open tabs context and format it into a prompt section.
src/ui/ChatWebviewProvider.ts Adds optional editor-context injection into sendPrompt, plus toggleable linked state.
src/extension.ts Wires editor-context provider callback into chat provider; registers toggle command + persists state.
package.json Adds command + view title button for toggling editor context link.
docs/editor-context-link.md Documents UX, prompt format, and implementation details.
src/test/extension.test.ts Cleans up test suite formatting (removes informational message).
src/test/EditorContext.test.ts Adds unit coverage for context formatting/deduplication and language selection.

💡 Add Copilot custom instructions for smarter, more guided reviews. Learn how to get started.

Comment thread src/ui/EditorContext.ts Outdated
Comment on lines +101 to +109
const lines = [
'VS Code context:',
`File: ${context.filePath}`,
`Cursor: ${context.cursorLine}:${context.cursorCharacter}`,
locationLine,
'',
`\`\`\`${context.language}`,
contextText,
'```',
Comment thread src/ui/EditorContext.ts Outdated
Comment on lines +75 to +89
export function normalizeOpenEditorPaths(paths: readonly string[]): string[] {
const seen = new Set<string>();
const normalized: string[] = [];

for (const filePath of paths) {
if (!filePath || seen.has(filePath)) {
continue;
}

seen.add(filePath);
normalized.push(filePath);
}

return normalized;
}
Comment thread src/ui/ChatWebviewProvider.ts Outdated
Comment on lines +177 to +197
const editorContext = this.editorContextLinked ? this.getEditorContext() : null;
const finalText = this.editorContextLinked && editorContext
? buildPromptWithEditorContext(text, editorContext)
: text;

if (this.editorContextLinked && !editorContext) {
this.postMessage({
type: 'error',
message: 'No active VS Code editor context.',
});
}

sendEvent('chat/messageSent', {
agentName: this.sessionManager.getActiveAgentName() ?? '',
}, {
messageLength: finalText.length,
});

// Record the first prompt for the history store (used as a label
// fallback when no title is supplied by the agent).
this.sessionManager.recordFirstPrompt(activeId, finalText);
Comment thread package.json Outdated
Comment on lines +210 to +224
{
"command": "acp.attachFile",
"when": "view == acp-chat",
"group": "navigation@2"
},
{
"command": "acp.toggleEditorContextLink",
"when": "view == acp-chat",
"group": "navigation@3"
},
{
"command": "acp.cancelTurn",
"when": "view == acp-chat && acp.turnInProgress",
"group": "navigation@4"
},
@maurice30120 maurice30120 force-pushed the feature/editor-context-link branch from 824ed1d to b71c692 Compare June 8, 2026 07:45
Copilot AI review requested due to automatic review settings June 8, 2026 08:11
@maurice30120 maurice30120 force-pushed the feature/editor-context-link branch from b71c692 to 2ffda7a Compare June 8, 2026 08:11

Copilot AI left a comment

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Pull request overview

Copilot reviewed 6 out of 6 changed files in this pull request and generated 5 comments.

Comment thread src/ui/ChatWebviewProvider.ts Outdated
Comment on lines +177 to +187
const editorContext = this.editorContextLinked ? this.getEditorContext() : null;
const finalText = this.editorContextLinked && editorContext
? buildPromptWithEditorContext(text, editorContext)
: text;

if (this.editorContextLinked && !editorContext) {
this.postMessage({
type: 'error',
message: 'No active VS Code editor context.',
});
}
Comment thread src/ui/ChatWebviewProvider.ts Outdated
Comment on lines +195 to +197
// Record the first prompt for the history store (used as a label
// fallback when no title is supplied by the agent).
this.sessionManager.recordFirstPrompt(activeId, finalText);
Comment thread src/ui/EditorContext.ts Outdated
Comment on lines +101 to +109
const lines = [
'VS Code context:',
`File: ${context.filePath}`,
`Cursor: ${context.cursorLine}:${context.cursorCharacter}`,
locationLine,
'',
`\`\`\`${context.language}`,
contextText,
'```',
Comment thread src/ui/EditorContext.ts Outdated
Comment on lines +75 to +86
export function normalizeOpenEditorPaths(paths: readonly string[]): string[] {
const seen = new Set<string>();
const normalized: string[] = [];

for (const filePath of paths) {
if (!filePath || seen.has(filePath)) {
continue;
}

seen.add(filePath);
normalized.push(filePath);
}
Comment thread src/ui/EditorContext.ts
Copilot AI review requested due to automatic review settings June 8, 2026 09:33
@maurice30120 maurice30120 force-pushed the feature/editor-context-link branch from 97b6acf to a47e49f Compare June 8, 2026 09:33

Copilot AI left a comment

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Pull request overview

Copilot reviewed 6 out of 6 changed files in this pull request and generated 5 comments.

Comment thread src/ui/EditorContext.ts Outdated
Comment on lines +39 to +51
export function initializeRecentFilesTracker() {
vscode.workspace.onDidOpenTextDocument(doc => {
if (doc.uri.scheme === 'file') {
recentFilesTracker.set(doc.uri.fsPath, Date.now());
}
});

vscode.workspace.onDidCloseTextDocument(doc => {
if (doc.uri.scheme === 'file') {
recentFilesTracker.delete(doc.uri.fsPath);
}
});
}
Comment thread src/ui/EditorContext.ts Outdated
Comment thread src/ui/ChatWebviewProvider.ts Outdated
Comment on lines +182 to +187
if (this.editorContextLinked && !editorContext) {
this.postMessage({
type: 'info',
message: 'No editor context available — sending prompt without context.',
});
}
Comment on lines +177 to +180
const editorContext = this.editorContextLinked ? this.getEditorContext() : null;
const finalText = this.editorContextLinked && editorContext
? buildPromptWithEditorContext(text, editorContext)
: text;
Comment thread src/ui/EditorContext.ts
@maurice30120 maurice30120 force-pushed the feature/editor-context-link branch from dca98f0 to 2f2a5a5 Compare June 8, 2026 10:04
Copilot AI review requested due to automatic review settings June 8, 2026 10:38

Copilot AI left a comment

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Pull request overview

Copilot reviewed 7 out of 7 changed files in this pull request and generated 8 comments.

Comment thread src/ui/EditorContext.ts Outdated
Comment on lines +107 to +130
export function normalizeRecentEditorPaths(files: readonly TrackedFile[]): TrackedFile[] {
const seen = new Set<string>();
const normalized: TrackedFile[] = [];

for (const file of files) {
if (!file.path) {
continue;
}

// Create canonical key: normalize path separators only
const canonicalKey = path.normalize(file.path);
if (seen.has(canonicalKey)) {
continue;
}

seen.add(canonicalKey);
normalized.push(file);
}

// Sort by openedAt DESC and limit to MAX_RECENT_FILES
return normalized
.sort((a, b) => b.openedAt - a.openedAt)
.slice(0, MAX_RECENT_FILES);
}
Comment thread src/ui/EditorContext.ts Outdated
Comment thread src/ui/EditorContext.ts Outdated
Comment on lines +39 to +51
export function initializeRecentFilesTracker() {
vscode.workspace.onDidOpenTextDocument(doc => {
if (doc.uri.scheme === 'file') {
recentFilesTracker.set(doc.uri.fsPath, Date.now());
}
});

vscode.workspace.onDidCloseTextDocument(doc => {
if (doc.uri.scheme === 'file') {
recentFilesTracker.delete(doc.uri.fsPath);
}
});
}
Comment thread src/extension.ts Outdated
Comment on lines +26 to +27
// --- Core services ---
initializeRecentFilesTracker();
Comment thread src/ui/EditorContext.ts
Comment thread .gitattributes Outdated
Comment on lines +1 to +5
* text=auto eol=lf
*.{ts,js,json,md,html,css,yml,yaml,sh} text eol=lf
*.{png,jpg,jpeg,gif,ico,woff,woff2,ttf,eot,svg} binary

src/ui/ChatWebviewProvider.ts -text
Comment thread src/test/EditorContext.test.ts
Comment thread src/ui/EditorContext.ts Outdated
Comment on lines +132 to +133
function getSafeFenceMarker(contextText: string): string {
let fence = '```';
Copilot AI review requested due to automatic review settings June 8, 2026 11:00

Copilot AI left a comment

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Pull request overview

Copilot reviewed 7 out of 8 changed files in this pull request and generated 5 comments.

Comment thread src/ui/EditorContext.ts Outdated
Comment thread src/ui/EditorContext.ts
Comment thread src/ui/ChatWebviewProvider.ts Outdated
Comment on lines +182 to +187
if (this.editorContextLinked && !editorContext) {
this.postMessage({
type: 'info',
message: 'No editor context available — sending prompt without context.',
});
}
Comment thread src/test/EditorContext.test.ts Outdated
Comment on lines +7 to +8
(vscode.workspace as any).workspaceFolders = undefined;
(vscode.workspace as any).asRelativePath = (p: string) => p;
Comment thread .gitattributes Outdated
Comment on lines +1 to +3
* text=auto eol=lf
*.{ts,js,json,md,html,css,yml,yaml,sh} text eol=lf
*.{png,jpg,jpeg,gif,ico,woff,woff2,ttf,eot,svg} binary
Copilot AI review requested due to automatic review settings June 8, 2026 11:29

Copilot AI left a comment

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Pull request overview

Copilot reviewed 7 out of 8 changed files in this pull request and generated 7 comments.

Comment thread src/ui/ChatWebviewProvider.ts Outdated
Comment on lines +182 to +187
if (this.editorContextLinked && !editorContext) {
this.postMessage({
type: 'info',
message: 'No editor context available — sending prompt without context.',
});
}
Comment on lines +177 to +180
const editorContext = this.editorContextLinked ? this.getEditorContext() : null;
const finalText = this.editorContextLinked && editorContext
? buildPromptWithEditorContext(text, editorContext)
: text;
Comment thread src/ui/ChatWebviewProvider.ts Outdated
Comment on lines +195 to +198
// Record the original user text for the history store (used as a label
// fallback when no title is supplied by the agent).
// This avoids persisting sensitive context (file paths, etc.) in history.
this.sessionManager.recordFirstPrompt(activeId, text);
Comment thread src/ui/ChatWebviewProvider.ts Outdated
this.postMessage({ type: 'promptStart' });

try {
const response = await this.sessionManager.sendPrompt(activeId, finalText);
Comment thread src/ui/ChatWebviewProvider.ts Outdated
Comment on lines +1948 to +1952
function attachScrollHide(dropdownEl) {
if (!dropdownEl || dropdownEl._tooltipScrollAttached) return;
dropdownEl._tooltipScrollAttached = true;
dropdownEl.addEventListener('scroll', hidePickerTooltip);
}
Comment thread src/test/EditorContext.test.ts
Comment thread src/ui/EditorContext.ts Outdated
Comment on lines +134 to +144
const canonicalKey = path.normalize(file.path);
const existing = fileMap.get(canonicalKey);

if (!existing || file.openedAt > existing.openedAt) {
fileMap.set(canonicalKey, file);
}
}

return Array.from(fileMap.values())
.sort((a, b) => b.openedAt - a.openedAt)
.slice(0, MAX_RECENT_FILES);
Copilot AI review requested due to automatic review settings June 8, 2026 11:34

Copilot AI left a comment

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Pull request overview

Copilot reviewed 7 out of 8 changed files in this pull request and generated 5 comments.

Comment thread src/ui/ChatWebviewProvider.ts Outdated
Comment on lines +182 to +187
if (this.editorContextLinked && !editorContext) {
this.postMessage({
type: 'info',
message: 'No editor context available — sending prompt without context.',
});
}
Comment thread src/ui/EditorContext.ts Outdated
Comment on lines +32 to +33
// Global tracker for recently opened files
const recentFilesTracker = new Map<string, number>();
Comment thread src/ui/EditorContext.ts Outdated
Comment on lines +53 to +65
// Initialize tracker with VS Code document events
export function initializeRecentFilesTracker(): vscode.Disposable[] {
const openDocDisposable = vscode.workspace.onDidOpenTextDocument(doc => {
if (doc.uri.scheme === 'file') {
recentFilesTracker.set(doc.uri.fsPath, Date.now());
}
});

const closeDocDisposable = vscode.workspace.onDidCloseTextDocument(doc => {
if (doc.uri.scheme === 'file') {
recentFilesTracker.delete(doc.uri.fsPath);
}
});
Comment thread src/ui/EditorContext.ts Outdated
Comment on lines +134 to +135
const canonicalKey = path.normalize(file.path);
const existing = fileMap.get(canonicalKey);
Comment thread src/test/EditorContext.test.ts Outdated
Comment on lines +27 to +38
Object.defineProperty(vscode.workspace, 'workspaceFolders', {
value: [],
configurable: true,
enumerable: true,
writable: true,
});
Object.defineProperty(vscode.workspace, 'asRelativePath', {
value: (p: string) => p,
configurable: true,
enumerable: true,
writable: true,
});
Copilot AI review requested due to automatic review settings June 8, 2026 11:46

Copilot AI left a comment

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Pull request overview

Copilot reviewed 6 out of 7 changed files in this pull request and generated 4 comments.

Comment thread package.json
Comment thread src/ui/EditorContext.ts Outdated
Comment on lines +32 to +33
// Global tracker for recently opened files
const recentFilesTracker = new Map<string, number>();
Comment thread src/ui/EditorContext.ts Outdated
Comment on lines +104 to +124
export function captureOpenEditorPaths(tabGroups: readonly vscode.TabGroup[]): TrackedFile[] {
const files: TrackedFile[] = [];

for (const group of tabGroups) {
for (const tab of group.tabs) {
if (tab.input instanceof vscode.TabInputText && tab.input.uri.scheme === 'file') {
const fsPath = tab.input.uri.fsPath;
const openedAt = recentFilesTracker.get(fsPath) ?? Date.now();
if (!recentFilesTracker.has(fsPath)) {
recentFilesTracker.set(fsPath, openedAt);
}
files.push({
path: fsPath,
openedAt
});
}
}
}

return normalizeRecentEditorPaths(files);
}
Comment thread src/ui/EditorContext.ts Outdated
Comment on lines +134 to +139
const canonicalKey = path.normalize(file.path);
const existing = fileMap.get(canonicalKey);

if (!existing || file.openedAt > existing.openedAt) {
fileMap.set(canonicalKey, file);
}
Copilot AI review requested due to automatic review settings June 8, 2026 12:11

Copilot AI left a comment

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Pull request overview

Copilot reviewed 6 out of 7 changed files in this pull request and generated 6 comments.

Comment thread src/ui/EditorContext.ts
Comment thread src/ui/EditorContext.ts
Comment thread src/ui/EditorContext.ts
Comment thread src/test/EditorContext.test.ts
Comment thread src/test/EditorContext.test.ts Outdated
Comment thread src/test/EditorContext.test.ts Outdated
Copilot AI review requested due to automatic review settings June 8, 2026 12:16
@maurice30120 maurice30120 force-pushed the feature/editor-context-link branch from 351402c to 986ac07 Compare June 8, 2026 12:16

Copilot AI left a comment

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Pull request overview

Copilot reviewed 6 out of 7 changed files in this pull request and generated 3 comments.

Comment thread src/test/EditorContext.test.ts
Comment thread src/ui/EditorContext.ts
Comment thread src/ui/EditorContext.ts
@maurice30120 maurice30120 requested a review from Copilot June 9, 2026 05:46

Copilot AI left a comment

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Pull request overview

Copilot reviewed 6 out of 7 changed files in this pull request and generated 4 comments.

Comment thread src/ui/EditorContext.ts
Comment on lines +34 to +35
// Global tracker for currently open editor files.
const openEditorOpenedAtByPath = new Map<string, number>();
Comment thread src/ui/EditorContext.ts Outdated
Comment on lines +134 to +145
export function captureOpenEditorPaths(tabGroups: readonly vscode.TabGroup[]): OpenEditorFile[] {
const files: OpenEditorFile[] = [];

for (const group of tabGroups) {
for (const tab of group.tabs) {
if (tab.input instanceof vscode.TabInputText && tab.input.uri.scheme === 'file') {
const fsPath = tab.input.uri.fsPath;
let openedAt = openEditorOpenedAtByPath.get(fsPath);
if (openedAt === undefined) {
openedAt = Date.now();
openEditorOpenedAtByPath.set(fsPath, openedAt);
}
Comment thread src/ui/EditorContext.ts Outdated
Comment on lines +139 to +140
if (tab.input instanceof vscode.TabInputText && tab.input.uri.scheme === 'file') {
const fsPath = tab.input.uri.fsPath;
Comment thread src/ui/EditorContext.ts
Comment on lines +193 to +197
const workspacePathFormatter: EditorContextPathFormatter = filePath => {
return vscode.workspace.workspaceFolders?.length
? vscode.workspace.asRelativePath(filePath)
: filePath;
};
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

3 participants