-
Notifications
You must be signed in to change notification settings - Fork 37.9k
@ can be used to reference context, just like # #294777
New issue
Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.
By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.
Already on GitHub? Sign in to your account
base: main
Are you sure you want to change the base?
Conversation
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Pull request overview
Extends chat input completions so @ can trigger the same “context reference” completions as #.
Changes:
- Broadened completion word patterns and trigger characters to include
@in addition to#. - Adjusted filtering/matching to consider the typed leader and (in some cases) match against both basename and URI label.
- Added pattern-based filtering for tool completions when a leader-prefixed prefix is present.
| return { | ||
| label: { label: basename, description: labelDescription }, | ||
| filterText: `${chatVariableLeader}${basename}`, | ||
| filterText: `${basename} ${typedLeader}${basename} ${uriLabel}`, |
Copilot
AI
Feb 12, 2026
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
uriLabel is referenced in filterText but is not defined in this scope, which will cause a TypeScript compile error. Use labelDescription (already computed) or introduce a const uriLabel = this.labelService.getUriLabel(resource, { relative: true }); before constructing the completion item.
| filterText: `${basename} ${typedLeader}${basename} ${uriLabel}`, | |
| filterText: `${basename} ${typedLeader}${basename} ${labelDescription}`, |
| const typedLeader = range.varWord?.word?.charAt(0) === chatAgentLeader ? chatAgentLeader : chatVariableLeader; | ||
| const basename = this.labelService.getUriBasenameLabel(currentResource); | ||
| const text = `${chatVariableLeader}file:${basename}:${currentSelection.startLineNumber}-${currentSelection.endLineNumber}`; |
Copilot
AI
Feb 12, 2026
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
When the user types @, filterText is adjusted to @… but the inserted text (text) and the displayed label still hardcode chatVariableLeader (#). This makes @ behave differently from # (and contradicts the PR intent of “just like #”). Consider building text and label with typedLeader so the completion both matches and inserts using the leader the user typed.
See below for a potential fix:
const text = `${typedLeader}file:${basename}:${currentSelection.startLineNumber}-${currentSelection.endLineNumber}`;
const fullRangeText = `:${currentSelection.startLineNumber}:${currentSelection.startColumn}-${currentSelection.endLineNumber}:${currentSelection.endColumn}`;
const description = this.labelService.getUriLabel(currentResource, { relative: true }) + fullRangeText;
const result: CompletionList = { suggestions: [] };
result.suggestions.push({
label: { label: `${typedLeader}selection`, description },
| label: { label: `${chatVariableLeader}selection`, description }, | ||
| filterText: `${chatVariableLeader}selection`, | ||
| filterText: `${typedLeader}selection`, | ||
| insertText: range.varWord?.endColumn === range.replace.endColumn ? `${text} ` : text, |
Copilot
AI
Feb 12, 2026
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
When the user types @, filterText is adjusted to @… but the inserted text (text) and the displayed label still hardcode chatVariableLeader (#). This makes @ behave differently from # (and contradicts the PR intent of “just like #”). Consider building text and label with typedLeader so the completion both matches and inserts using the leader the user typed.
See below for a potential fix:
const text = `${typedLeader}file:${basename}:${currentSelection.startLineNumber}-${currentSelection.endLineNumber}`;
const fullRangeText = `:${currentSelection.startLineNumber}:${currentSelection.startColumn}-${currentSelection.endLineNumber}:${currentSelection.endColumn}`;
const description = this.labelService.getUriLabel(currentResource, { relative: true }) + fullRangeText;
const result: CompletionList = { suggestions: [] };
result.suggestions.push({
label: { label: `${typedLeader}selection`, description },
| const typedLeader = info.varWord?.word?.charAt(0) === chatAgentLeader ? chatAgentLeader : chatVariableLeader; | ||
|
|
||
| const makeSymbolCompletionItem = (symbolItem: { name: string; location: Location; kind: SymbolKind }, pattern: string): CompletionItem => { | ||
| const text = `${chatVariableLeader}sym:${symbolItem.name}`; |
Copilot
AI
Feb 12, 2026
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Similar to the selection completion: filterText uses typedLeader, but the inserted text always uses chatVariableLeader (#). If @sym:… is intended to be a first-class equivalent to #sym:…, text should be derived from typedLeader (or, alternatively, keep filterText aligned with the inserted leader).
| const text = `${chatVariableLeader}sym:${symbolItem.name}`; | |
| const text = `${typedLeader}sym:${symbolItem.name}`; |
| label: { label: symbolItem.name, description: uriLabel }, | ||
| filterText: `${chatVariableLeader}${symbolItem.name}`, | ||
| filterText: `${typedLeader}${symbolItem.name}`, | ||
| insertText: info.varWord?.endColumn === info.replace.endColumn ? `${text} ` : text, |
Copilot
AI
Feb 12, 2026
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Similar to the selection completion: filterText uses typedLeader, but the inserted text always uses chatVariableLeader (#). If @sym:… is intended to be a first-class equivalent to #sym:…, text should be derived from typedLeader (or, alternatively, keep filterText aligned with the inserted leader).
| const withLeader = `${chatVariableLeader}${name}`; | ||
| suggestions.push({ | ||
| label: withLeader, | ||
| range, | ||
| detail, | ||
| documentation, | ||
| filterText: `${typedLeader}${name}`, | ||
| insertText: withLeader + ' ', | ||
| kind: CompletionItemKind.Tool, |
Copilot
AI
Feb 12, 2026
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Tool completions will still insert #toolName even when triggered by @, because withLeader is always built from chatVariableLeader. To make @ behave “just like #”, compute withLeader using typedLeader (and consider also updating label so the UI matches what will be inserted).
| class BuiltinDynamicCompletions extends Disposable { | ||
| private static readonly addReferenceCommand = '_addReferenceCmd'; | ||
| private static readonly VariableNameDef = new RegExp(`${chatVariableLeader}[\\w:-]*`, 'g'); // MUST be using `g`-flag | ||
| private static readonly VariableNameDef = new RegExp(`[${chatVariableLeader}${chatAgentLeader}][\\w:-]*`, 'g'); // MUST be using `g`-flag |
Copilot
AI
Feb 12, 2026
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Building a character class via interpolation can become fragile if either leader ever changes to a regex-special character inside [] (e.g. -, ], ^, \). Consider escaping the leaders before interpolating them into the regex to keep this robust.
See below for a potential fix:
function escapeForCharClass(text: string): string {
return text.replace(/[-\\\]^]/g, '\\$&');
}
class BuiltinDynamicCompletions extends Disposable {
private static readonly addReferenceCommand = '_addReferenceCmd';
private static readonly VariableNameDef = new RegExp(`[${escapeForCharClass(chatVariableLeader)}${escapeForCharClass(chatAgentLeader)}][\\w:-]*`, 'g'); // MUST be using `g`-flag
@.mov