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
24 changes: 17 additions & 7 deletions src/vs/workbench/contrib/chat/browser/tools/renameTool.ts
Original file line number Diff line number Diff line change
Expand Up @@ -15,6 +15,7 @@ import { TextEdit } from '../../../../../editor/common/languages.js';
import { IBulkEditService, ResourceTextEdit } from '../../../../../editor/browser/services/bulkEditService.js';
import { ILanguageFeaturesService } from '../../../../../editor/common/services/languageFeatures.js';
import { ITextModelService } from '../../../../../editor/common/services/resolverService.js';
import { ILanguageService } from '../../../../../editor/common/languages/language.js';
import { rename } from '../../../../../editor/contrib/rename/browser/rename.js';
import { localize } from '../../../../../nls.js';
import { ContextKeyExpr } from '../../../../../platform/contextkey/common/contextkey.js';
Expand Down Expand Up @@ -53,6 +54,7 @@ export class RenameTool extends Disposable implements IToolImpl {

constructor(
@ILanguageFeaturesService private readonly _languageFeaturesService: ILanguageFeaturesService,
@ILanguageService private readonly _languageService: ILanguageService,
@ITextModelService private readonly _textModelService: ITextModelService,
@IWorkspaceContextService private readonly _workspaceContextService: IWorkspaceContextService,
@IChatService private readonly _chatService: IChatService,
Expand All @@ -67,26 +69,31 @@ export class RenameTool extends Disposable implements IToolImpl {
)((() => this._onDidUpdateToolData.fire())));
}

getToolData(): IToolData {
getToolData(): IToolData | undefined {
const languageIds = this._languageFeaturesService.renameProvider.registeredLanguageIds;

if (languageIds.size === 0) {
return undefined;
}

let modelDescription = BaseModelDescription;
let userDescription: string;
if (languageIds.has('*')) {
modelDescription += '\n\nSupported for all languages.';
} else if (languageIds.size > 0) {
userDescription = localize('tool.rename.userDescription', 'Rename a symbol across the workspace');
} else {
const sorted = [...languageIds].sort();
modelDescription += `\n\nCurrently supported for: ${sorted.join(', ')}.`;
} else {
modelDescription += '\n\nNo languages currently have rename providers registered.';
const niceNames = sorted.map(id => this._languageService.getLanguageName(id) ?? id);
userDescription = localize('tool.rename.userDescriptionWithLanguages', 'Rename a symbol across the workspace ({0})', niceNames.join(', '));
}

return {
id: RenameToolId,
toolReferenceName: 'rename',
canBeReferencedInPrompt: false,
icon: ThemeIcon.fromId(Codicon.rename.id),
displayName: localize('tool.rename.displayName', 'Rename Symbol'),
userDescription: localize('tool.rename.userDescription', 'Rename a symbol across the workspace'),
userDescription,
modelDescription,
source: ToolDataSource.Internal,
when: ContextKeyExpr.has('config.chat.tools.renameTool.enabled'),
Expand Down Expand Up @@ -251,9 +258,12 @@ export class RenameToolContribution extends Disposable implements IWorkbenchCont
let registration: IDisposable | undefined;
const registerRenameTool = () => {
registration?.dispose();
registration = undefined;
toolsService.flushToolUpdates();
const toolData = renameTool.getToolData();
registration = toolsService.registerTool(toolData, renameTool);
if (toolData) {
registration = toolsService.registerTool(toolData, renameTool);
}
};
registerRenameTool();
this._store.add(renameTool.onDidUpdateToolData(registerRenameTool));
Expand Down
23 changes: 17 additions & 6 deletions src/vs/workbench/contrib/chat/browser/tools/usagesTool.ts
Original file line number Diff line number Diff line change
Expand Up @@ -18,6 +18,7 @@ import { Location, LocationLink } from '../../../../../editor/common/languages.j
import { IModelService } from '../../../../../editor/common/services/model.js';
import { ILanguageFeaturesService } from '../../../../../editor/common/services/languageFeatures.js';
import { ITextModelService } from '../../../../../editor/common/services/resolverService.js';
import { ILanguageService } from '../../../../../editor/common/languages/language.js';
import { getDefinitionsAtPosition, getImplementationsAtPosition, getReferencesAtPosition } from '../../../../../editor/contrib/gotoSymbol/browser/goToSymbol.js';
import { localize } from '../../../../../nls.js';
import { ContextKeyExpr } from '../../../../../platform/contextkey/common/contextkey.js';
Expand Down Expand Up @@ -50,6 +51,7 @@ export class UsagesTool extends Disposable implements IToolImpl {

constructor(
@ILanguageFeaturesService private readonly _languageFeaturesService: ILanguageFeaturesService,
@ILanguageService private readonly _languageService: ILanguageService,
@IModelService private readonly _modelService: IModelService,
@ISearchService private readonly _searchService: ISearchService,
@ITextModelService private readonly _textModelService: ITextModelService,
Expand All @@ -64,17 +66,23 @@ export class UsagesTool extends Disposable implements IToolImpl {
)((() => this._onDidUpdateToolData.fire())));
}

getToolData(): IToolData {
getToolData(): IToolData | undefined {
const languageIds = this._languageFeaturesService.referenceProvider.registeredLanguageIds;

if (languageIds.size === 0) {
return undefined;
}

let modelDescription = BaseModelDescription;
let userDescription: string;
if (languageIds.has('*')) {
modelDescription += '\n\nSupported for all languages.';
} else if (languageIds.size > 0) {
userDescription = localize('tool.usages.userDescription', 'Find references, definitions, and implementations of a symbol');
} else {
const sorted = [...languageIds].sort();
modelDescription += `\n\nCurrently supported for: ${sorted.join(', ')}.`;
} else {
modelDescription += '\n\nNo languages currently have reference providers registered.';
const niceNames = sorted.map(id => this._languageService.getLanguageName(id) ?? id);
userDescription = localize('tool.usages.userDescriptionWithLanguages', 'Find references, definitions, and implementations of a symbol ({0})', niceNames.join(', '));
}

return {
Expand All @@ -83,7 +91,7 @@ export class UsagesTool extends Disposable implements IToolImpl {
canBeReferencedInPrompt: false,
icon: ThemeIcon.fromId(Codicon.references.id),
displayName: localize('tool.usages.displayName', 'List Code Usages'),
userDescription: localize('tool.usages.userDescription', 'Find references, definitions, and implementations of a symbol'),
userDescription,
modelDescription,
source: ToolDataSource.Internal,
when: ContextKeyExpr.has('config.chat.tools.usagesTool.enabled'),
Expand Down Expand Up @@ -320,9 +328,12 @@ export class UsagesToolContribution extends Disposable implements IWorkbenchCont
let registration: IDisposable | undefined;
const registerUsagesTool = () => {
registration?.dispose();
registration = undefined;
toolsService.flushToolUpdates();
const toolData = usagesTool.getToolData();
registration = toolsService.registerTool(toolData, usagesTool);
if (toolData) {
registration = toolsService.registerTool(toolData, usagesTool);
}
};
registerUsagesTool();
this._store.add(usagesTool.onDidUpdateToolData(registerUsagesTool));
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -12,10 +12,11 @@ import { RenameProvider, WorkspaceEdit, Rejection } from '../../../../../../edit
import { IMarkdownString } from '../../../../../../base/common/htmlContent.js';
import { LanguageFeaturesService } from '../../../../../../editor/common/services/languageFeaturesService.js';
import { ITextModelService } from '../../../../../../editor/common/services/resolverService.js';
import { ILanguageService } from '../../../../../../editor/common/languages/language.js';
import { createTextModel } from '../../../../../../editor/test/common/testTextModel.js';
import { IWorkspaceContextService, IWorkspaceFolder } from '../../../../../../platform/workspace/common/workspace.js';
import { IBulkEditService, IBulkEditResult } from '../../../../../../editor/browser/services/bulkEditService.js';
import { RenameTool, RenameToolId } from '../../../browser/tools/renameTool.js';
import { RenameTool } from '../../../browser/tools/renameTool.js';
import { IChatService } from '../../../common/chatService/chatService.js';
import { IToolInvocation, IToolResult, IToolResultTextPart, ToolProgress } from '../../../common/tools/languageModelToolsService.js';
import { ensureNoDisposablesAreLeakedInTestSuite } from '../../../../../../base/test/common/utils.js';
Expand Down Expand Up @@ -100,9 +101,14 @@ suite('RenameTool', () => {
const noopCountTokens = async () => 0;
const noopProgress: ToolProgress = { report() { } };

function createMockLanguageService(): ILanguageService {
return { getLanguageName: (id: string) => id } as unknown as ILanguageService;
}

function createTool(textModelService: ITextModelService, options?: { bulkEditService?: IBulkEditService }): RenameTool {
return new RenameTool(
langFeatures,
createMockLanguageService(),
textModelService,
createMockWorkspaceService(),
createMockChatService(),
Expand All @@ -124,9 +130,7 @@ suite('RenameTool', () => {

test('reports no providers when none registered', () => {
const tool = disposables.add(createTool(createMockTextModelService(null!)));
const data = tool.getToolData();
assert.strictEqual(data.id, RenameToolId);
assert.ok(data.modelDescription.includes('No languages currently have rename providers'));
assert.strictEqual(tool.getToolData(), undefined);
});

test('lists registered language ids', () => {
Expand All @@ -136,7 +140,7 @@ suite('RenameTool', () => {
provideRenameEdits: () => ({ edits: [] }),
}));
const data = tool.getToolData();
assert.ok(data.modelDescription.includes('typescript'));
assert.ok(data?.modelDescription.includes('typescript'));
});

test('reports all languages for wildcard', () => {
Expand All @@ -145,7 +149,7 @@ suite('RenameTool', () => {
provideRenameEdits: () => ({ edits: [] }),
}));
const data = tool.getToolData();
assert.ok(data.modelDescription.includes('all languages'));
assert.ok(data?.modelDescription.includes('all languages'));
});
});

Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -13,10 +13,11 @@ import { ITextModel } from '../../../../../../editor/common/model.js';
import { LanguageFeaturesService } from '../../../../../../editor/common/services/languageFeaturesService.js';
import { IModelService } from '../../../../../../editor/common/services/model.js';
import { ITextModelService } from '../../../../../../editor/common/services/resolverService.js';
import { ILanguageService } from '../../../../../../editor/common/languages/language.js';
import { createTextModel } from '../../../../../../editor/test/common/testTextModel.js';
import { IWorkspaceContextService, IWorkspaceFolder } from '../../../../../../platform/workspace/common/workspace.js';
import { FileMatch, ISearchComplete, ISearchService, ITextQuery, OneLineRange, TextSearchMatch } from '../../../../../services/search/common/search.js';
import { UsagesTool, UsagesToolId } from '../../../browser/tools/usagesTool.js';
import { UsagesTool } from '../../../browser/tools/usagesTool.js';
import { IToolInvocation, IToolResult, IToolResultTextPart, ToolProgress } from '../../../common/tools/languageModelToolsService.js';
import { ensureNoDisposablesAreLeakedInTestSuite } from '../../../../../../base/test/common/utils.js';

Expand Down Expand Up @@ -91,8 +92,12 @@ suite('UsagesTool', () => {
const noopCountTokens = async () => 0;
const noopProgress: ToolProgress = { report() { } };

function createMockLanguageService(): ILanguageService {
return { getLanguageName: (id: string) => id } as unknown as ILanguageService;
}

function createTool(textModelService: ITextModelService, workspaceService: IWorkspaceContextService, options?: { modelService?: IModelService; searchService?: ISearchService }): UsagesTool {
return new UsagesTool(langFeatures, options?.modelService ?? createMockModelService(), options?.searchService ?? createMockSearchService(), textModelService, workspaceService);
return new UsagesTool(langFeatures, createMockLanguageService(), options?.modelService ?? createMockModelService(), options?.searchService ?? createMockSearchService(), textModelService, workspaceService);
}

setup(() => {
Expand All @@ -109,24 +114,22 @@ suite('UsagesTool', () => {

test('reports no providers when none registered', () => {
const tool = disposables.add(createTool(createMockTextModelService(null!), createMockWorkspaceService()));
const data = tool.getToolData();
assert.strictEqual(data.id, UsagesToolId);
assert.ok(data.modelDescription.includes('No languages currently have reference providers'));
assert.strictEqual(tool.getToolData(), undefined);
});

test('lists registered language ids', () => {
const model = disposables.add(createTextModel('', 'typescript', undefined, testUri));
const tool = disposables.add(createTool(createMockTextModelService(model), createMockWorkspaceService()));
disposables.add(langFeatures.referenceProvider.register('typescript', { provideReferences: () => [] }));
const data = tool.getToolData();
assert.ok(data.modelDescription.includes('typescript'));
assert.ok(data?.modelDescription.includes('typescript'));
});

test('reports all languages for wildcard', () => {
const tool = disposables.add(createTool(createMockTextModelService(null!), createMockWorkspaceService()));
disposables.add(langFeatures.referenceProvider.register('*', { provideReferences: () => [] }));
const data = tool.getToolData();
assert.ok(data.modelDescription.includes('all languages'));
assert.ok(data?.modelDescription.includes('all languages'));
});
});

Expand Down
Loading