mirror of
https://github.com/google-gemini/gemini-cli.git
synced 2026-04-24 03:54:43 -07:00
feat: add folder suggestions to /dir add command (#15724)
This commit is contained in:
@@ -4,10 +4,13 @@
|
||||
* SPDX-License-Identifier: Apache-2.0
|
||||
*/
|
||||
|
||||
import { vi, describe, it, expect, beforeEach } from 'vitest';
|
||||
import { vi, describe, it, expect, beforeEach, afterEach } from 'vitest';
|
||||
import type { Mock } from 'vitest';
|
||||
import { directoryCommand } from './directoryCommand.js';
|
||||
import { expandHomeDir } from '../utils/directoryUtils.js';
|
||||
import {
|
||||
expandHomeDir,
|
||||
getDirectorySuggestions,
|
||||
} from '../utils/directoryUtils.js';
|
||||
import type { Config, WorkspaceContext } from '@google/gemini-cli-core';
|
||||
import type { MultiFolderTrustDialogProps } from '../components/MultiFolderTrustDialog.js';
|
||||
import type { CommandContext, OpenCustomDialogActionReturn } from './types.js';
|
||||
@@ -17,6 +20,15 @@ import * as path from 'node:path';
|
||||
import * as trustedFolders from '../../config/trustedFolders.js';
|
||||
import type { LoadedTrustedFolders } from '../../config/trustedFolders.js';
|
||||
|
||||
vi.mock('../utils/directoryUtils.js', async (importOriginal) => {
|
||||
const actual =
|
||||
await importOriginal<typeof import('../utils/directoryUtils.js')>();
|
||||
return {
|
||||
...actual,
|
||||
getDirectorySuggestions: vi.fn(),
|
||||
};
|
||||
});
|
||||
|
||||
describe('directoryCommand', () => {
|
||||
let mockContext: CommandContext;
|
||||
let mockConfig: Config;
|
||||
@@ -217,6 +229,47 @@ describe('directoryCommand', () => {
|
||||
expect.any(Number),
|
||||
);
|
||||
});
|
||||
|
||||
describe('completion', () => {
|
||||
const completion = addCommand!.completion!;
|
||||
|
||||
it('should return empty suggestions for an empty path', async () => {
|
||||
const results = await completion(mockContext, '');
|
||||
expect(results).toEqual([]);
|
||||
});
|
||||
|
||||
it('should return empty suggestions for whitespace only path', async () => {
|
||||
const results = await completion(mockContext, ' ');
|
||||
expect(results).toEqual([]);
|
||||
});
|
||||
|
||||
it('should return suggestions for a single path', async () => {
|
||||
vi.mocked(getDirectorySuggestions).mockResolvedValue(['docs/', 'src/']);
|
||||
|
||||
const results = await completion(mockContext, 'd');
|
||||
|
||||
expect(getDirectorySuggestions).toHaveBeenCalledWith('d');
|
||||
expect(results).toEqual(['docs/', 'src/']);
|
||||
});
|
||||
|
||||
it('should return suggestions for multiple paths', async () => {
|
||||
vi.mocked(getDirectorySuggestions).mockResolvedValue(['src/']);
|
||||
|
||||
const results = await completion(mockContext, 'docs/,s');
|
||||
|
||||
expect(getDirectorySuggestions).toHaveBeenCalledWith('s');
|
||||
expect(results).toEqual(['docs/,src/']);
|
||||
});
|
||||
|
||||
it('should handle leading whitespace in suggestions', async () => {
|
||||
vi.mocked(getDirectorySuggestions).mockResolvedValue(['src/']);
|
||||
|
||||
const results = await completion(mockContext, 'docs/, s');
|
||||
|
||||
expect(getDirectorySuggestions).toHaveBeenCalledWith('s');
|
||||
expect(results).toEqual(['docs/, src/']);
|
||||
});
|
||||
});
|
||||
});
|
||||
|
||||
describe('add with folder trust enabled', () => {
|
||||
|
||||
@@ -14,7 +14,10 @@ import type { SlashCommand, CommandContext } from './types.js';
|
||||
import { CommandKind } from './types.js';
|
||||
import { MessageType, type HistoryItem } from '../types.js';
|
||||
import { refreshServerHierarchicalMemory } from '@google/gemini-cli-core';
|
||||
import { expandHomeDir } from '../utils/directoryUtils.js';
|
||||
import {
|
||||
expandHomeDir,
|
||||
getDirectorySuggestions,
|
||||
} from '../utils/directoryUtils.js';
|
||||
import type { Config } from '@google/gemini-cli-core';
|
||||
|
||||
async function finishAddingDirectories(
|
||||
@@ -80,6 +83,27 @@ export const directoryCommand: SlashCommand = {
|
||||
'Add directories to the workspace. Use comma to separate multiple paths',
|
||||
kind: CommandKind.BUILT_IN,
|
||||
autoExecute: false,
|
||||
showCompletionLoading: false,
|
||||
completion: async (context: CommandContext, partialArg: string) => {
|
||||
// Support multiple paths separated by commas
|
||||
const parts = partialArg.split(',');
|
||||
const lastPart = parts[parts.length - 1];
|
||||
const leadingWhitespace = lastPart.match(/^\s*/)?.[0] ?? '';
|
||||
const trimmedLastPart = lastPart.trimStart();
|
||||
|
||||
if (trimmedLastPart === '') {
|
||||
return [];
|
||||
}
|
||||
|
||||
const suggestions = await getDirectorySuggestions(trimmedLastPart);
|
||||
|
||||
if (parts.length > 1) {
|
||||
const prefix = parts.slice(0, -1).join(',') + ',';
|
||||
return suggestions.map((s) => prefix + leadingWhitespace + s);
|
||||
}
|
||||
|
||||
return suggestions.map((s) => leadingWhitespace + s);
|
||||
},
|
||||
action: async (context: CommandContext, args: string) => {
|
||||
const {
|
||||
ui: { addItem },
|
||||
|
||||
@@ -201,5 +201,11 @@ export interface SlashCommand {
|
||||
partialArg: string,
|
||||
) => Promise<string[]> | string[];
|
||||
|
||||
/**
|
||||
* Whether to show the loading indicator while fetching completions.
|
||||
* Defaults to true. Set to false for fast completions to avoid flicker.
|
||||
*/
|
||||
showCompletionLoading?: boolean;
|
||||
|
||||
subCommands?: SlashCommand[];
|
||||
}
|
||||
|
||||
Reference in New Issue
Block a user