Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
Original file line number Diff line number Diff line change
Expand Up @@ -8,7 +8,6 @@ import { Emitter, Event } from '../../../../../base/common/event.js';
import { IMarkdownString, isMarkdownString } from '../../../../../base/common/htmlContent.js';
import { stripIcons } from '../../../../../base/common/iconLabels.js';
import { Disposable, DisposableStore } from '../../../../../base/common/lifecycle.js';
import { basename } from '../../../../../base/common/resources.js';
import { URI } from '../../../../../base/common/uri.js';
import { localize } from '../../../../../nls.js';
import { AccessibleViewProviderId, AccessibleViewType, IAccessibleViewContentProvider } from '../../../../../platform/accessibility/browser/accessibleView.js';
Expand All @@ -22,7 +21,7 @@ import { IChatExtensionsContent, IChatModifiedFilesConfirmationData, IChatPullRe
import { isResponseVM } from '../../common/model/chatViewModel.js';
import { IToolResultInputOutputDetails, IToolResultOutputDetails, isToolResultInputOutputDetails, isToolResultOutputDetails, toolContentToA11yString } from '../../common/tools/languageModelToolsService.js';
import { ChatTreeItem, IChatWidget, IChatWidgetService } from '../chat.js';
import { isLocation, Location } from '../../../../../editor/common/languages.js';
import { Location } from '../../../../../editor/common/languages.js';

export class ChatResponseAccessibleView implements IAccessibleViewImplementation {
readonly priority = 100;
Expand Down Expand Up @@ -300,29 +299,6 @@ class ChatResponseAccessibleProvider extends Disposable implements IAccessibleVi
}
break;
}
case 'inlineReference': {
const ref = part.inlineReference;
let text: string;
if (URI.isUri(ref)) {
const name = part.name || basename(ref);
const isFileUri = ref.scheme === 'file';
const path = isFileUri ? (ref.fsPath || ref.path) : ref.toString(true);
text = name !== path ? `${name} (${path})` : path;
} else if (isLocation(ref)) {
const name = part.name || basename(ref.uri);
const isFileUri = ref.uri.scheme === 'file';
const basePath = isFileUri ? (ref.uri.fsPath || ref.uri.path) : ref.uri.toString(true);
const location = `${basePath}:${ref.range.startLineNumber}`;
text = `${name} (${location})`;
} else {
// IWorkspaceSymbol
const isFileUri = ref.location.uri.scheme === 'file';
const basePath = isFileUri ? (ref.location.uri.fsPath || ref.location.uri.path) : ref.location.uri.toString(true);
text = `${ref.name} (${basePath}:${ref.location.range.startLineNumber})`;
}
contentParts.push(text);
break;
}
case 'elicitation2':
case 'elicitationSerialized': {
const title = part.title;
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -486,164 +486,5 @@ suite('ChatResponseAccessibleView', () => {
assert.ok(content.includes('Response content'));
assert.ok(content.includes('Thinking: Reasoning'));
});

test('includes file path for URI inline references', () => {
const instantiationService = store.add(new TestInstantiationService());
const storageService = store.add(new TestStorageService());

const inlineReferenceUri = URI.file('/path/to/index.ts');
const responseItem = {
response: {
value: [
{ kind: 'markdownContent', content: new MarkdownString('See file ') },
{ kind: 'inlineReference', inlineReference: inlineReferenceUri, name: 'index.ts' },
{ kind: 'markdownContent', content: new MarkdownString(' for details') }
]
},
model: { onDidChange: Event.None },
setVote: () => undefined
};
const items = [responseItem];
let focusedItem: unknown = responseItem;

const widget = {
hasInputFocus: () => false,
focusResponseItem: () => { focusedItem = responseItem; },
getFocus: () => focusedItem,
focus: (item: unknown) => { focusedItem = item; },
viewModel: { getItems: () => items }
} as unknown as IChatWidget;

const widgetService = {
_serviceBrand: undefined,
lastFocusedWidget: widget,
onDidAddWidget: Event.None,
onDidBackgroundSession: Event.None,
reveal: async () => true,
revealWidget: async () => widget,
getAllWidgets: () => [widget],
getWidgetByInputUri: () => widget,
openSession: async () => widget,
getWidgetBySessionResource: () => widget
} as unknown as IChatWidgetService;

instantiationService.stub(IChatWidgetService, widgetService);
instantiationService.stub(IStorageService, storageService);

const accessibleView = new ChatResponseAccessibleView();
const provider = instantiationService.invokeFunction(accessor => accessibleView.getProvider(accessor));
assert.ok(provider);
store.add(provider);
const content = provider.provideContent();
const expectedPath = inlineReferenceUri.fsPath || inlineReferenceUri.path;
assert.ok(content.includes('index.ts'));
assert.ok(content.includes(expectedPath));
assert.ok(content.includes('See file'));
assert.ok(content.includes('for details'));
});

test('includes file path and line number for Location inline references', () => {
const instantiationService = store.add(new TestInstantiationService());
const storageService = store.add(new TestStorageService());

const fileLocation: Location = {
uri: URI.file('/src/app/main.ts'),
range: new Range(42, 1, 42, 20)
};

const responseItem = {
response: {
value: [
{ kind: 'markdownContent', content: new MarkdownString('Error at ') },
{ kind: 'inlineReference', inlineReference: fileLocation, name: 'main.ts' }
]
},
model: { onDidChange: Event.None },
setVote: () => undefined
};
const items = [responseItem];
let focusedItem: unknown = responseItem;

const widget = {
hasInputFocus: () => false,
focusResponseItem: () => { focusedItem = responseItem; },
getFocus: () => focusedItem,
focus: (item: unknown) => { focusedItem = item; },
viewModel: { getItems: () => items }
} as unknown as IChatWidget;

const widgetService = {
_serviceBrand: undefined,
lastFocusedWidget: widget,
onDidAddWidget: Event.None,
onDidBackgroundSession: Event.None,
reveal: async () => true,
revealWidget: async () => widget,
getAllWidgets: () => [widget],
getWidgetByInputUri: () => widget,
openSession: async () => widget,
getWidgetBySessionResource: () => widget
} as unknown as IChatWidgetService;

instantiationService.stub(IChatWidgetService, widgetService);
instantiationService.stub(IStorageService, storageService);

const accessibleView = new ChatResponseAccessibleView();
const provider = instantiationService.invokeFunction(accessor => accessibleView.getProvider(accessor));
assert.ok(provider);
store.add(provider);
const content = provider.provideContent();
assert.ok(content.includes('main.ts'));
assert.ok(content.includes('/src/app/main.ts:42'));
});

test('uses basename as name for URI inline references without explicit name', () => {
const instantiationService = store.add(new TestInstantiationService());
const storageService = store.add(new TestStorageService());

const responseItem = {
response: {
value: [
{ kind: 'inlineReference', inlineReference: URI.file('/workspace/src/utils.ts') }
]
},
model: { onDidChange: Event.None },
setVote: () => undefined
};
const items = [responseItem];
let focusedItem: unknown = responseItem;

const widget = {
hasInputFocus: () => false,
focusResponseItem: () => { focusedItem = responseItem; },
getFocus: () => focusedItem,
focus: (item: unknown) => { focusedItem = item; },
viewModel: { getItems: () => items }
} as unknown as IChatWidget;

const widgetService = {
_serviceBrand: undefined,
lastFocusedWidget: widget,
onDidAddWidget: Event.None,
onDidBackgroundSession: Event.None,
reveal: async () => true,
revealWidget: async () => widget,
getAllWidgets: () => [widget],
getWidgetByInputUri: () => widget,
openSession: async () => widget,
getWidgetBySessionResource: () => widget
} as unknown as IChatWidgetService;

instantiationService.stub(IChatWidgetService, widgetService);
instantiationService.stub(IStorageService, storageService);

const accessibleView = new ChatResponseAccessibleView();
const provider = instantiationService.invokeFunction(accessor => accessibleView.getProvider(accessor));
assert.ok(provider);
store.add(provider);
const content = provider.provideContent();
assert.ok(content.includes('utils.ts'));
assert.ok(content.includes('/workspace/src/utils.ts'));
});
});
});
Loading