mirror of
https://github.com/microsoft/vscode.git
synced 2026-07-12 22:08:04 -05:00
separate prompt and instructions
This commit is contained in:
committed by
Oleg Solomko
parent
46ff50ed05
commit
1631f03483
@@ -25,10 +25,10 @@ import { ISelectPromptOptions, askToSelectPrompt } from './dialogs/askToSelectPr
|
||||
/**
|
||||
* Action ID for the `Attach Prompt` action.
|
||||
*/
|
||||
const ATTACH_PROMPT_ACTION_ID = 'workbench.action.chat.attach.prompt';
|
||||
const ATTACH_INSTRUCTIONS_ACTION_ID = 'workbench.action.chat.attach.instructions';
|
||||
|
||||
/**
|
||||
* Options for the {@link AttachPromptAction} action.
|
||||
* Options for the {@link AttachInstructionsAction} action.
|
||||
*/
|
||||
export interface IChatAttachPromptActionOptions extends Pick<
|
||||
ISelectPromptOptions, 'resource' | 'widget'
|
||||
@@ -51,11 +51,11 @@ export interface IChatAttachPromptActionOptions extends Pick<
|
||||
/**
|
||||
* Action to attach a prompt to a chat widget input.
|
||||
*/
|
||||
class AttachPromptAction extends Action2 {
|
||||
class AttachInstructionsAction extends Action2 {
|
||||
constructor() {
|
||||
super({
|
||||
id: ATTACH_PROMPT_ACTION_ID,
|
||||
title: localize2('workbench.action.chat.attach.prompt.label', "Use Prompt"),
|
||||
id: ATTACH_INSTRUCTIONS_ACTION_ID,
|
||||
title: localize2('workbench.action.chat.attach.instructions.label', "Attach Instructions"),
|
||||
f1: false,
|
||||
precondition: ContextKeyExpr.and(PromptsConfig.enabledCtx, ChatContextKeys.enabled),
|
||||
category: CHAT_CATEGORY,
|
||||
@@ -100,10 +100,11 @@ class AttachPromptAction extends Action2 {
|
||||
}
|
||||
|
||||
// find all prompt files in the user workspace
|
||||
const promptFiles = await promptsService.listPromptFiles();
|
||||
const promptFiles = await promptsService.listPromptFiles('instructions');
|
||||
|
||||
await askToSelectPrompt({
|
||||
...options,
|
||||
type: 'instructions',
|
||||
promptFiles,
|
||||
fileService,
|
||||
viewsService,
|
||||
@@ -118,7 +119,7 @@ class AttachPromptAction extends Action2 {
|
||||
|
||||
/**
|
||||
* Runs the `Attach Prompt` action with provided options. We export this
|
||||
* function instead of {@link ATTACH_PROMPT_ACTION_ID} directly to
|
||||
* function instead of {@link ATTACH_INSTRUCTIONS_ACTION_ID} directly to
|
||||
* encapsulate/enforce the correct options to be passed to the action.
|
||||
*/
|
||||
export const runAttachPromptAction = async (
|
||||
@@ -126,7 +127,7 @@ export const runAttachPromptAction = async (
|
||||
commandService: ICommandService,
|
||||
): Promise<void> => {
|
||||
return await commandService.executeCommand(
|
||||
ATTACH_PROMPT_ACTION_ID,
|
||||
ATTACH_INSTRUCTIONS_ACTION_ID,
|
||||
options,
|
||||
);
|
||||
};
|
||||
@@ -135,5 +136,5 @@ export const runAttachPromptAction = async (
|
||||
* Helper to register the `Attach Prompt` action.
|
||||
*/
|
||||
export const registerAttachPromptActions = () => {
|
||||
registerAction2(AttachPromptAction);
|
||||
registerAction2(AttachInstructionsAction);
|
||||
};
|
||||
|
||||
@@ -3,7 +3,7 @@
|
||||
* Licensed under the MIT License. See License.txt in the project root for license information.
|
||||
*--------------------------------------------------------------------------------------------*/
|
||||
|
||||
import { DOCS_OPTION } from './constants.js';
|
||||
import { PROMPT_DOCS_OPTION } from './constants.js';
|
||||
import { IChatWidget } from '../../../../chat.js';
|
||||
import { attachPrompt } from './utils/attachPrompt.js';
|
||||
import { handleButtonClick } from './utils/handleButtonClick.js';
|
||||
@@ -13,7 +13,7 @@ import { createPromptPickItem } from './utils/createPromptPickItem.js';
|
||||
import { createPlaceholderText } from './utils/createPlaceholderText.js';
|
||||
import { extUri } from '../../../../../../../../base/common/resources.js';
|
||||
import { WithUriValue } from '../../../../../../../../base/common/types.js';
|
||||
import { IPromptPath } from '../../../../../common/promptSyntax/service/types.js';
|
||||
import { IPromptPath, TPromptsType } from '../../../../../common/promptSyntax/service/types.js';
|
||||
import { DisposableStore } from '../../../../../../../../base/common/lifecycle.js';
|
||||
import { IFileService } from '../../../../../../../../platform/files/common/files.js';
|
||||
import { ILabelService } from '../../../../../../../../platform/label/common/label.js';
|
||||
@@ -48,6 +48,11 @@ export interface ISelectPromptOptions {
|
||||
*/
|
||||
readonly promptFiles: readonly IPromptPath[];
|
||||
|
||||
/**
|
||||
* Type of the prompt files to select.
|
||||
*/
|
||||
readonly type: TPromptsType;
|
||||
|
||||
readonly fileService: IFileService;
|
||||
readonly labelService: ILabelService;
|
||||
readonly viewsService: IViewsService;
|
||||
@@ -66,7 +71,7 @@ export interface ISelectPromptOptions {
|
||||
export const askToSelectPrompt = async (
|
||||
options: ISelectPromptOptions,
|
||||
): Promise<void> => {
|
||||
const { promptFiles, resource, quickInputService, labelService } = options;
|
||||
const { promptFiles, resource, quickInputService, labelService, type } = options;
|
||||
|
||||
const fileOptions = promptFiles.map((promptFile) => {
|
||||
return createPromptPickItem(promptFile, labelService);
|
||||
@@ -75,7 +80,7 @@ export const askToSelectPrompt = async (
|
||||
/**
|
||||
* Add a link to the documentation to the end of prompts list.
|
||||
*/
|
||||
fileOptions.push(DOCS_OPTION);
|
||||
fileOptions.push(PROMPT_DOCS_OPTION);
|
||||
|
||||
// if a resource is provided, create an `activeItem` for it to pre-select
|
||||
// it in the UI, and sort the list so the active item appears at the top
|
||||
@@ -94,7 +99,8 @@ export const askToSelectPrompt = async (
|
||||
uri: resource,
|
||||
// "user" prompts are always registered in the prompts list, hence it
|
||||
// should be safe to assume that `resource` is not "user" prompt here
|
||||
type: 'local',
|
||||
storage: 'local',
|
||||
type,
|
||||
}, labelService);
|
||||
fileOptions.push(activeItem);
|
||||
}
|
||||
@@ -164,7 +170,7 @@ export const askToSelectPrompt = async (
|
||||
const selectedOption = selectedItems[0];
|
||||
|
||||
// whether user selected the docs link option
|
||||
const docsSelected = (selectedOption === DOCS_OPTION);
|
||||
const docsSelected = (selectedOption === PROMPT_DOCS_OPTION);
|
||||
|
||||
// if documentation item was selected, open its link in a browser
|
||||
if (docsSelected) {
|
||||
|
||||
@@ -8,7 +8,7 @@ import { URI } from '../../../../../../../../base/common/uri.js';
|
||||
import { Codicon } from '../../../../../../../../base/common/codicons.js';
|
||||
import { WithUriValue } from '../../../../../../../../base/common/types.js';
|
||||
import { ThemeIcon } from '../../../../../../../../base/common/themables.js';
|
||||
import { DOCUMENTATION_URL } from '../../../../../common/promptSyntax/constants.js';
|
||||
import { INSTRUCTIONS_DOCUMENTATION_URL, PROMPT_DOCUMENTATION_URL } from '../../../../../common/promptSyntax/constants.js';
|
||||
import { isLinux, isWindows } from '../../../../../../../../base/common/platform.js';
|
||||
import { IQuickInputButton, IQuickPickItem } from '../../../../../../../../platform/quickinput/common/quickInput.js';
|
||||
|
||||
@@ -25,15 +25,29 @@ export const ALT_KEY_NAME = (isWindows || isLinux) ? 'Alt' : '⌥';
|
||||
/**
|
||||
* A special quick pick item that links to the documentation.
|
||||
*/
|
||||
export const DOCS_OPTION: WithUriValue<IQuickPickItem> = Object.freeze({
|
||||
export const PROMPT_DOCS_OPTION: WithUriValue<IQuickPickItem> = Object.freeze({
|
||||
type: 'item',
|
||||
label: localize(
|
||||
'commands.prompts.use.select-dialog.docs-label',
|
||||
'Learn how to create reusable prompts',
|
||||
),
|
||||
description: DOCUMENTATION_URL,
|
||||
tooltip: DOCUMENTATION_URL,
|
||||
value: URI.parse(DOCUMENTATION_URL),
|
||||
description: PROMPT_DOCUMENTATION_URL,
|
||||
tooltip: PROMPT_DOCUMENTATION_URL,
|
||||
value: URI.parse(PROMPT_DOCUMENTATION_URL),
|
||||
});
|
||||
|
||||
/**
|
||||
* A special quick pick item that links to the documentation.
|
||||
*/
|
||||
export const INSTRUCTIONS_DOCS_OPTION: WithUriValue<IQuickPickItem> = Object.freeze({
|
||||
type: 'item',
|
||||
label: localize(
|
||||
'commands.instructions.use.select-dialog.docs-label',
|
||||
'Learn how to create custom instructions',
|
||||
),
|
||||
description: INSTRUCTIONS_DOCUMENTATION_URL,
|
||||
tooltip: INSTRUCTIONS_DOCUMENTATION_URL,
|
||||
value: URI.parse(INSTRUCTIONS_DOCUMENTATION_URL),
|
||||
});
|
||||
|
||||
/**
|
||||
|
||||
@@ -19,19 +19,19 @@ export const createPromptPickItem = (
|
||||
promptFile: IPromptPath,
|
||||
labelService: ILabelService,
|
||||
): WithUriValue<IQuickPickItem> => {
|
||||
const { uri, type } = promptFile;
|
||||
const { uri, storage } = promptFile;
|
||||
const fileWithoutExtension = getCleanPromptName(uri);
|
||||
|
||||
// if a "user" prompt, don't show its filesystem path in
|
||||
// the user interface, but do that for all the "local" ones
|
||||
const description = (type === 'user')
|
||||
const description = (storage === 'user')
|
||||
? localize(
|
||||
'user-prompt.capitalized',
|
||||
'User prompt',
|
||||
)
|
||||
: labelService.getUriLabel(dirname(uri), { relative: true });
|
||||
|
||||
const tooltip = (type === 'user')
|
||||
const tooltip = (storage === 'user')
|
||||
? description
|
||||
: uri.fsPath;
|
||||
|
||||
|
||||
@@ -48,7 +48,7 @@ import { ILanguageModelIgnoredFilesService, LanguageModelIgnoredFilesService } f
|
||||
import { ILanguageModelsService, LanguageModelsService } from '../common/languageModels.js';
|
||||
import { ILanguageModelStatsService, LanguageModelStatsService } from '../common/languageModelStats.js';
|
||||
import { ILanguageModelToolsService } from '../common/languageModelToolsService.js';
|
||||
import { DOCUMENTATION_URL } from '../common/promptSyntax/constants.js';
|
||||
import { PROMPT_DOCUMENTATION_URL } from '../common/promptSyntax/constants.js';
|
||||
import { registerReusablePromptLanguageFeatures } from '../common/promptSyntax/languageFeatures/providers/index.js';
|
||||
import { PromptsService } from '../common/promptSyntax/service/promptsService.js';
|
||||
import { IPromptsService } from '../common/promptSyntax/service/types.js';
|
||||
@@ -282,7 +282,7 @@ configurationRegistry.registerConfiguration({
|
||||
'chat.reusablePrompts.config.enabled.description',
|
||||
"Enable reusable prompt files (`*{0}`) in Chat, Edits, and Inline Chat sessions. [Learn More]({1}).",
|
||||
PROMPT_FILE_EXTENSION,
|
||||
DOCUMENTATION_URL,
|
||||
PROMPT_DOCUMENTATION_URL,
|
||||
),
|
||||
default: true,
|
||||
restricted: true,
|
||||
@@ -306,7 +306,7 @@ configurationRegistry.registerConfiguration({
|
||||
'chat.reusablePrompts.config.locations.description',
|
||||
"Specify location(s) of reusable prompt files (`*{0}`) that can be attached in Chat, Edits, and Inline Chat sessions. [Learn More]({1}).\n\nRelative paths are resolved from the root folder(s) of your workspace.",
|
||||
PROMPT_FILE_EXTENSION,
|
||||
DOCUMENTATION_URL,
|
||||
PROMPT_DOCUMENTATION_URL,
|
||||
),
|
||||
default: {
|
||||
[PROMPT_FILES_DEFAULT_SOURCE_FOLDER]: true,
|
||||
|
||||
@@ -17,7 +17,7 @@ import { PromptsConfig } from '../../../../../../../platform/prompts/common/conf
|
||||
import { ICommandService } from '../../../../../../../platform/commands/common/commands.js';
|
||||
import { ContextKeyExpr } from '../../../../../../../platform/contextkey/common/contextkey.js';
|
||||
import { MenuId, MenuRegistry } from '../../../../../../../platform/actions/common/actions.js';
|
||||
import { IPromptPath, IPromptsService } from '../../../../common/promptSyntax/service/types.js';
|
||||
import { IPromptsService, TPromptsStorage, TPromptsType } from '../../../../common/promptSyntax/service/types.js';
|
||||
import { IQuickInputService } from '../../../../../../../platform/quickinput/common/quickInput.js';
|
||||
import { ServicesAccessor } from '../../../../../../../platform/instantiation/common/instantiation.js';
|
||||
import { IWorkspaceContextService } from '../../../../../../../platform/workspace/common/workspace.js';
|
||||
@@ -26,37 +26,13 @@ import { IUserDataSyncEnablementService, SyncResource } from '../../../../../../
|
||||
import { KeybindingsRegistry, KeybindingWeight } from '../../../../../../../platform/keybinding/common/keybindingsRegistry.js';
|
||||
import { INotificationService, NeverShowAgainScope, Severity } from '../../../../../../../platform/notification/common/notification.js';
|
||||
|
||||
/**
|
||||
* Base command ID prefix.
|
||||
*/
|
||||
const BASE_COMMAND_ID = 'workbench.command.prompts.create';
|
||||
|
||||
/**
|
||||
* Command ID for creating a 'local' prompt.
|
||||
*/
|
||||
const LOCAL_COMMAND_ID = `${BASE_COMMAND_ID}.local`;
|
||||
|
||||
/**
|
||||
* Command ID for creating a 'user' prompt.
|
||||
*/
|
||||
const USER_COMMAND_ID = `${BASE_COMMAND_ID}.user`;
|
||||
|
||||
/**
|
||||
* Title of the 'create local prompt' command.
|
||||
*/
|
||||
const LOCAL_COMMAND_TITLE = localize('commands.prompts.create.title.local', "Create Prompt");
|
||||
|
||||
/**
|
||||
* Title of the 'create user prompt' command.
|
||||
*/
|
||||
const USER_COMMAND_TITLE = localize('commands.prompts.create.title.user', "Create User Prompt");
|
||||
|
||||
/**
|
||||
* The command implementation.
|
||||
*/
|
||||
const command = async (
|
||||
accessor: ServicesAccessor,
|
||||
type: IPromptPath['type'],
|
||||
type: TPromptsType,
|
||||
storage: TPromptsStorage
|
||||
): Promise<void> => {
|
||||
const logService = accessor.get(ILogService);
|
||||
const fileService = accessor.get(IFileService);
|
||||
@@ -69,13 +45,14 @@ const command = async (
|
||||
const workspaceService = accessor.get(IWorkspaceContextService);
|
||||
const userDataSyncEnablementService = accessor.get(IUserDataSyncEnablementService);
|
||||
|
||||
const fileName = await askForPromptName(type, quickInputService);
|
||||
const fileName = await askForPromptName(storage, quickInputService);
|
||||
if (!fileName) {
|
||||
return;
|
||||
}
|
||||
|
||||
const selectedFolder = await askForPromptSourceFolder({
|
||||
type: type,
|
||||
storage: storage,
|
||||
labelService,
|
||||
openerService,
|
||||
promptsService,
|
||||
@@ -101,7 +78,7 @@ const command = async (
|
||||
|
||||
await openerService.open(promptUri);
|
||||
|
||||
if (type !== 'user') {
|
||||
if (storage !== 'user') {
|
||||
return;
|
||||
}
|
||||
|
||||
@@ -148,55 +125,33 @@ const command = async (
|
||||
);
|
||||
};
|
||||
|
||||
/**
|
||||
* Factory for creating the command handler with specific prompt `type`.
|
||||
*/
|
||||
const commandFactory = (type: 'local' | 'user') => {
|
||||
return async (accessor: ServicesAccessor): Promise<void> => {
|
||||
return command(accessor, type);
|
||||
};
|
||||
};
|
||||
function register(type: TPromptsType, storage: TPromptsStorage, id: string, title: string) {
|
||||
/**
|
||||
* Register the command.
|
||||
*/
|
||||
KeybindingsRegistry.registerCommandAndKeybindingRule({
|
||||
id,
|
||||
weight: KeybindingWeight.WorkbenchContrib,
|
||||
handler: async (accessor: ServicesAccessor): Promise<void> => {
|
||||
return command(accessor, type, storage);
|
||||
},
|
||||
when: ContextKeyExpr.and(PromptsConfig.enabledCtx, ChatContextKeys.enabled),
|
||||
});
|
||||
|
||||
/**
|
||||
* Register the "Create Prompt" command.
|
||||
*/
|
||||
KeybindingsRegistry.registerCommandAndKeybindingRule({
|
||||
id: LOCAL_COMMAND_ID,
|
||||
weight: KeybindingWeight.WorkbenchContrib,
|
||||
handler: commandFactory('local'),
|
||||
when: ContextKeyExpr.and(PromptsConfig.enabledCtx, ChatContextKeys.enabled),
|
||||
});
|
||||
/**
|
||||
* Register the command in the command palette.
|
||||
*/
|
||||
MenuRegistry.appendMenuItem(MenuId.CommandPalette, {
|
||||
command: {
|
||||
id,
|
||||
title,
|
||||
category: CHAT_CATEGORY
|
||||
},
|
||||
when: ContextKeyExpr.and(PromptsConfig.enabledCtx, ChatContextKeys.enabled)
|
||||
});
|
||||
}
|
||||
|
||||
/**
|
||||
* Register the "Create User Prompt" command.
|
||||
*/
|
||||
KeybindingsRegistry.registerCommandAndKeybindingRule({
|
||||
id: USER_COMMAND_ID,
|
||||
weight: KeybindingWeight.WorkbenchContrib,
|
||||
handler: commandFactory('user'),
|
||||
when: ContextKeyExpr.and(PromptsConfig.enabledCtx, ChatContextKeys.enabled),
|
||||
});
|
||||
|
||||
/**
|
||||
* Register the "Create Prompt" command in the command palette.
|
||||
*/
|
||||
MenuRegistry.appendMenuItem(MenuId.CommandPalette, {
|
||||
command: {
|
||||
id: LOCAL_COMMAND_ID,
|
||||
title: LOCAL_COMMAND_TITLE,
|
||||
category: CHAT_CATEGORY
|
||||
},
|
||||
when: ContextKeyExpr.and(PromptsConfig.enabledCtx, ChatContextKeys.enabled)
|
||||
});
|
||||
|
||||
/**
|
||||
* Register the "Create User Prompt" command in the command palette.
|
||||
*/
|
||||
MenuRegistry.appendMenuItem(MenuId.CommandPalette, {
|
||||
command: {
|
||||
id: USER_COMMAND_ID,
|
||||
title: USER_COMMAND_TITLE,
|
||||
category: CHAT_CATEGORY,
|
||||
},
|
||||
when: ContextKeyExpr.and(PromptsConfig.enabledCtx, ChatContextKeys.enabled)
|
||||
});
|
||||
register('instructions', 'local', 'workbench.command.instructions.create.local', localize('commands.instructions.create.title.local', "Create Instructions File"));
|
||||
register('instructions', 'user', 'workbench.command.instructions.create.user', localize('commands.instructions.create.title.user', "Create User Instructions File"));
|
||||
register('prompt', 'local', 'workbench.command.prompts.create.local', localize('commands.prompts.create.title.local', "Create Prompt File"));
|
||||
register('prompt', 'user', 'workbench.command.prompts.create.user', localize('commands.prompts.create.title.user', "Create User Prompt File"));
|
||||
|
||||
@@ -6,9 +6,9 @@
|
||||
import { localize } from '../../../../../../../../nls.js';
|
||||
import { URI } from '../../../../../../../../base/common/uri.js';
|
||||
import { WithUriValue } from '../../../../../../../../base/common/types.js';
|
||||
import { DOCUMENTATION_URL } from '../../../../../common/promptSyntax/constants.js';
|
||||
import { PROMPT_DOCUMENTATION_URL } from '../../../../../common/promptSyntax/constants.js';
|
||||
import { basename, extUri } from '../../../../../../../../base/common/resources.js';
|
||||
import { IPromptsService } from '../../../../../common/promptSyntax/service/types.js';
|
||||
import { IPromptsService, TPromptsStorage, TPromptsType } from '../../../../../common/promptSyntax/service/types.js';
|
||||
import { ILabelService } from '../../../../../../../../platform/label/common/label.js';
|
||||
import { IOpenerService } from '../../../../../../../../platform/opener/common/opener.js';
|
||||
import { IWorkspaceContextService } from '../../../../../../../../platform/workspace/common/workspace.js';
|
||||
@@ -18,10 +18,9 @@ import { IPickOptions, IQuickInputService, IQuickPickItem } from '../../../../..
|
||||
* Options for {@link askForPromptSourceFolder} dialog.
|
||||
*/
|
||||
interface IAskForFolderOptions {
|
||||
/**
|
||||
* Prompt type.
|
||||
*/
|
||||
readonly type: 'local' | 'user';
|
||||
|
||||
readonly type: TPromptsType;
|
||||
readonly storage: TPromptsStorage;
|
||||
|
||||
readonly labelService: ILabelService;
|
||||
readonly openerService: IOpenerService;
|
||||
@@ -37,10 +36,10 @@ interface IAskForFolderOptions {
|
||||
export const askForPromptSourceFolder = async (
|
||||
options: IAskForFolderOptions,
|
||||
): Promise<URI | undefined> => {
|
||||
const { type, promptsService, quickInputService, labelService, openerService, workspaceService } = options;
|
||||
const { storage, type, promptsService, quickInputService, labelService, openerService, workspaceService } = options;
|
||||
|
||||
// get prompts source folders based on the prompt type
|
||||
const folders = promptsService.getSourceFolders(type);
|
||||
const folders = promptsService.getSourceFolders(type, storage);
|
||||
|
||||
// if no source folders found, show 'learn more' dialog
|
||||
// note! this is a temporary solution and must be replaced with a dialog to select
|
||||
@@ -122,9 +121,9 @@ const showNoFoldersDialog = async (
|
||||
'commands.prompts.create.ask-folder.empty.docs-label',
|
||||
'Learn how to configure reusable prompts',
|
||||
),
|
||||
description: DOCUMENTATION_URL,
|
||||
tooltip: DOCUMENTATION_URL,
|
||||
value: URI.parse(DOCUMENTATION_URL),
|
||||
description: PROMPT_DOCUMENTATION_URL,
|
||||
tooltip: PROMPT_DOCUMENTATION_URL,
|
||||
value: URI.parse(PROMPT_DOCUMENTATION_URL),
|
||||
};
|
||||
|
||||
const result = await quickInputService.pick(
|
||||
|
||||
@@ -22,19 +22,19 @@ import { IActiveCodeEditor, isCodeEditor, isDiffEditor } from '../../../../../..
|
||||
import { KeybindingsRegistry, KeybindingWeight } from '../../../../../../platform/keybinding/common/keybindingsRegistry.js';
|
||||
|
||||
/**
|
||||
* Command ID of the "Use Prompt" command.
|
||||
* Command ID of the "Use Instructions" command.
|
||||
*/
|
||||
export const COMMAND_ID = 'workbench.command.prompts.use';
|
||||
export const COMMAND_ID = 'workbench.command.instructions.use';
|
||||
|
||||
/**
|
||||
* Keybinding of the "Use Prompt" command.
|
||||
* Keybinding of the "Use Instructions" command.
|
||||
* The `cmd + /` is the current keybinding for 'attachment', so we use
|
||||
* the `alt` key modifier to convey the "prompt attachment" action.
|
||||
* the `alt` key modifier to convey the "instructions attachment" action.
|
||||
*/
|
||||
const COMMAND_KEY_BINDING = KeyMod.CtrlCmd | KeyCode.Slash | KeyMod.Alt;
|
||||
|
||||
/**
|
||||
* Implementation of the "Use Prompt" command. The command works in the following way.
|
||||
* Implementation of the "Use Instructions" command. The command works in the following way.
|
||||
*
|
||||
* When executed, it tries to see if a `prompt file` was open in the active code editor
|
||||
* (see {@link IChatAttachPromptActionOptions.resource resource}), and if a chat input
|
||||
@@ -139,12 +139,12 @@ KeybindingsRegistry.registerCommandAndKeybindingRule({
|
||||
});
|
||||
|
||||
/**
|
||||
* Register the "Use Prompt" command in the `command palette`.
|
||||
* Register the "Use Instructions" command in the `command palette`.
|
||||
*/
|
||||
MenuRegistry.appendMenuItem(MenuId.CommandPalette, {
|
||||
command: {
|
||||
id: COMMAND_ID,
|
||||
title: localize('commands.prompts.use.title', "Use Prompt"),
|
||||
title: localize('commands.prompts.use.title', "Use Instructions"),
|
||||
category: CHAT_CATEGORY
|
||||
},
|
||||
when: ContextKeyExpr.and(PromptsConfig.enabledCtx, ChatContextKeys.enabled)
|
||||
|
||||
@@ -8,7 +8,8 @@ import { LanguageFilter } from '../../../../../editor/common/languageSelector.js
|
||||
/**
|
||||
* Documentation link for the reusable prompts feature.
|
||||
*/
|
||||
export const DOCUMENTATION_URL = 'https://aka.ms/vscode-ghcp-prompt-snippets';
|
||||
export const PROMPT_DOCUMENTATION_URL = 'https://aka.ms/vscode-ghcp-prompt-snippets';
|
||||
export const INSTRUCTIONS_DOCUMENTATION_URL = PROMPT_DOCUMENTATION_URL; // TODO: instructions specific link
|
||||
|
||||
/**
|
||||
* Language ID for the reusable prompt syntax.
|
||||
|
||||
@@ -3,7 +3,7 @@
|
||||
* Licensed under the MIT License. See License.txt in the project root for license information.
|
||||
*--------------------------------------------------------------------------------------------*/
|
||||
|
||||
import { IPromptPath, IPromptsService } from './types.js';
|
||||
import { IPromptPath, IPromptsService, TPromptsStorage, TPromptsType } from './types.js';
|
||||
import { URI } from '../../../../../../base/common/uri.js';
|
||||
import { assert } from '../../../../../../base/common/assert.js';
|
||||
import { PromptFilesLocator } from '../utils/promptFilesLocator.js';
|
||||
@@ -83,34 +83,35 @@ export class PromptsService extends Disposable implements IPromptsService {
|
||||
return this.cache.get(model);
|
||||
}
|
||||
|
||||
public async listPromptFiles(): Promise<readonly IPromptPath[]> {
|
||||
public async listPromptFiles(type: TPromptsType): Promise<readonly IPromptPath[]> {
|
||||
const userLocations = [this.userDataService.currentProfile.promptsHome];
|
||||
|
||||
const prompts = await Promise.all([
|
||||
this.fileLocator.listFilesIn(userLocations)
|
||||
.then(withType('user')),
|
||||
.then(withType('user', type)),
|
||||
this.fileLocator.listFiles()
|
||||
.then(withType('local')),
|
||||
.then(withType('local', type)),
|
||||
]);
|
||||
|
||||
return prompts.flat();
|
||||
}
|
||||
|
||||
public getSourceFolders(
|
||||
type: IPromptPath['type'],
|
||||
type: TPromptsType,
|
||||
storage: TPromptsStorage,
|
||||
): readonly IPromptPath[] {
|
||||
// sanity check to make sure we don't miss a new
|
||||
// prompt type that could be added in the future
|
||||
assert(
|
||||
type === 'local' || type === 'user',
|
||||
`Unknown prompt type '${type}'.`,
|
||||
storage === 'local' || storage === 'user',
|
||||
`Unknown prompt storage '${storage}'.`,
|
||||
);
|
||||
|
||||
const prompts = (type === 'user')
|
||||
const prompts = (storage === 'user')
|
||||
? [this.userDataService.currentProfile.promptsHome]
|
||||
: this.fileLocator.getConfigBasedSourceFolders();
|
||||
|
||||
return prompts.map(addType(type));
|
||||
return prompts.map(addType(storage, type));
|
||||
}
|
||||
}
|
||||
|
||||
@@ -118,10 +119,11 @@ export class PromptsService extends Disposable implements IPromptsService {
|
||||
* Utility to add a provided prompt `type` to a prompt URI.
|
||||
*/
|
||||
const addType = (
|
||||
type: 'local' | 'user',
|
||||
storage: TPromptsStorage,
|
||||
type: TPromptsType,
|
||||
): (uri: URI) => IPromptPath => {
|
||||
return (uri) => {
|
||||
return { uri, type: type };
|
||||
return { uri, storage, type };
|
||||
};
|
||||
};
|
||||
|
||||
@@ -129,10 +131,11 @@ const addType = (
|
||||
* Utility to add a provided prompt `type` to a list of prompt URIs.
|
||||
*/
|
||||
const withType = (
|
||||
type: 'local' | 'user',
|
||||
storage: TPromptsStorage,
|
||||
type: TPromptsType,
|
||||
): (uris: readonly URI[]) => (readonly IPromptPath[]) => {
|
||||
return (uris) => {
|
||||
return uris
|
||||
.map(addType(type));
|
||||
.map(addType(storage, type));
|
||||
};
|
||||
};
|
||||
|
||||
@@ -15,11 +15,14 @@ import { createDecorator } from '../../../../../../platform/instantiation/common
|
||||
export const IPromptsService = createDecorator<IPromptsService>('IPromptsService');
|
||||
|
||||
/**
|
||||
* Supported prompt types.
|
||||
* - `local` means the prompt is a local file.
|
||||
* - `user` means a "roam-able" prompt file (similar to snippets).
|
||||
*/
|
||||
type TPromptsType = 'local' | 'user';
|
||||
* Where the prompt is stored.
|
||||
*/
|
||||
export type TPromptsStorage = 'local' | 'user';
|
||||
|
||||
/**
|
||||
* What the prompt is used for.
|
||||
*/
|
||||
export type TPromptsType = 'instructions' | 'prompt';
|
||||
|
||||
/**
|
||||
* Represents a prompt path with its type.
|
||||
@@ -32,11 +35,17 @@ export interface IPromptPath {
|
||||
readonly uri: URI;
|
||||
|
||||
/**
|
||||
* Type of the prompt.
|
||||
* Storage of the prompt.
|
||||
*/
|
||||
readonly storage: TPromptsStorage;
|
||||
|
||||
/**
|
||||
* Type
|
||||
*/
|
||||
readonly type: TPromptsType;
|
||||
}
|
||||
|
||||
|
||||
/**
|
||||
* Provides prompt services.
|
||||
*/
|
||||
@@ -54,12 +63,12 @@ export interface IPromptsService extends IDisposable {
|
||||
/**
|
||||
* List all available prompt files.
|
||||
*/
|
||||
listPromptFiles(): Promise<readonly IPromptPath[]>;
|
||||
listPromptFiles(type: TPromptsType): Promise<readonly IPromptPath[]>;
|
||||
|
||||
/**
|
||||
* Get a list of prompt source folders based on the provided prompt type.
|
||||
* Get a list of prompt source folders based on the provided prompt type and storage.
|
||||
*/
|
||||
getSourceFolders(type: TPromptsType): readonly IPromptPath[];
|
||||
getSourceFolders(type: TPromptsType, storage: TPromptsStorage): readonly IPromptPath[];
|
||||
}
|
||||
|
||||
/**
|
||||
|
||||
Reference in New Issue
Block a user