Skip to content

Commit

Permalink
update: remove description requirement from custom command (#3025)
Browse files Browse the repository at this point in the history
Remove the requirement to add a description for a custom command as the
prompt of the command is descriptive enough.

This eliminates one unnecessary step to simplify the custom command
creation process.

Based on screenshot shared by our users, it looks like users often use
the name of the command to fill in the description field, e.g.
https://sourcegraph.slack.com/archives/C04MSD3DP5L/p1706661347779839?thread_ts=1706656724.966049&cid=C04MSD3DP5L,
https://discord.com/channels/969688426372825169/1202562648340701257/1202562648340701257,
https://sourcegraph.slack.com/archives/C05MW2TMYAV/p1703011617179849

Feedback from a power user:

> One piece of feedback when defining your custom commands is to get rid
of the command description step.

## Test plan

<!-- Required. See
https://sourcegraph.com/docs/dev/background-information/testing_principles.
-->

We will still show the description in the command menu when provided,
but it is no longer a required field when creating a command from the
command palette. You can try to create a custom command with and without
the "description" field to confirm.

I've updated the current e2e tests and unit tests for custom commands to
reflect this change.
  • Loading branch information
abeatrix authored Feb 7, 2024
1 parent 94327ca commit a1afc07
Show file tree
Hide file tree
Showing 6 changed files with 50 additions and 45 deletions.
2 changes: 2 additions & 0 deletions vscode/CHANGELOG.md
Original file line number Diff line number Diff line change
Expand Up @@ -10,6 +10,8 @@ This is a log of all notable changes to Cody for VS Code. [Unreleased] changes a

### Changed

- Custom Command: The `description` field is now optional and will default to use the command prompt. [pull/3025](https://github.com/sourcegraph/cody/pull/3025)

## [1.4.0]

### Added
Expand Down
33 changes: 5 additions & 28 deletions vscode/src/commands/menus/command-builder.ts
Original file line number Diff line number Diff line change
Expand Up @@ -19,11 +19,6 @@ export class CustomCommandsBuilderMenu {
return null
}

const description = await this.makeDescription()
if (!description) {
return null
}

const prompt = await this.makePrompt()
if (!prompt) {
return null
Expand All @@ -34,15 +29,15 @@ export class CustomCommandsBuilderMenu {
return null
}

return { slashCommand, prompt: { ...prompt, description, slashCommand }, type }
return { slashCommand, prompt: { ...prompt, slashCommand }, type }
}

private async makeSlashCommand(commands: string[]): Promise<string | undefined> {
const commandSet = new Set(commands)
let value = await window.showInputBox({
title: 'New Custom Cody Command: Slash Name',
prompt: 'Enter the slash name of the custom command',
placeHolder: 'e.g. /my-custom-command',
placeHolder: 'e.g. /name',
ignoreFocusOut: true,
validateInput: (input: string) => {
if (!input) {
Expand All @@ -63,22 +58,6 @@ export class CustomCommandsBuilderMenu {
return value
}

private async makeDescription(): Promise<string | undefined> {
const description = await window.showInputBox({
title: 'New Custom Cody Command: Description',
prompt: 'Enter a description for the command in sentence case.',
placeHolder: 'e.g. Scan for vulnerabilities',
ignoreFocusOut: true,
validateInput: (input: string) => {
if (!input) {
return 'Command description cannot be empty.'
}
return
},
})
return description
}

private async makePrompt(): Promise<Omit<CodyCommand, 'slashCommand'> | null> {
const prompt = await window.showInputBox({
title: 'New Custom Cody Command: Prompt',
Expand All @@ -98,9 +77,7 @@ export class CustomCommandsBuilderMenu {
return this.addContext({ prompt })
}

private async addContext(
newPrompt?: Omit<CodyCommand, 'slashCommand'>
): Promise<Omit<CodyCommand, 'slashCommand'> | null> {
private async addContext(newPrompt?: Partial<CodyCommand>): Promise<CodyCommand | null> {
if (!newPrompt) {
return null
}
Expand All @@ -117,7 +94,7 @@ export class CustomCommandsBuilderMenu {
})

if (!promptContext?.length) {
return newPrompt
return newPrompt as CodyCommand
}

for (const context of promptContext) {
Expand All @@ -135,7 +112,7 @@ export class CustomCommandsBuilderMenu {
}
}

return newPrompt
return newPrompt as CodyCommand
}

private async makeType(): Promise<CustomCommandType> {
Expand Down
4 changes: 2 additions & 2 deletions vscode/src/commands/menus/index.ts
Original file line number Diff line number Diff line change
Expand Up @@ -26,7 +26,7 @@ export async function showCommandMenu(
items.push(CommandMenuSeperator.commands)
for (const [_name, _command] of vscodeDefaultCommands) {
const label = _command.slashCommand
const description = _command.description
const description = _command.description ?? _command.prompt
const command = _command.slashCommand
items.push({ label, description, command })
}
Expand All @@ -36,7 +36,7 @@ export async function showCommandMenu(
items.push(CommandMenuSeperator.custom)
for (const customCommand of customCommands) {
const label = customCommand.slashCommand
const description = customCommand.description
const description = customCommand.description ?? customCommand.prompt
const command = customCommand.slashCommand
items.push({ label, description, command })
}
Expand Down
42 changes: 33 additions & 9 deletions vscode/src/commands/services/custom-commands.ts
Original file line number Diff line number Diff line change
Expand Up @@ -14,6 +14,9 @@ import { buildCodyCommandMap } from '../utils/get-commands'
import { CustomCommandType } from '@sourcegraph/cody-shared/src/commands/types'
import { fromSlashCommand } from '../utils/common'

const isTesting = process.env.CODY_TESTING === 'true'
const isMac = os.platform() === 'darwin'

/**
* Handles loading, building, and maintaining Custom Commands retrieved from cody.json files
*/
Expand Down Expand Up @@ -174,22 +177,23 @@ export class CustomCommandsManager implements vscode.Disposable {
*/
private async save(
id: string,
prompt: CodyCommand,
command: CodyCommand,
type: CustomCommandType = CustomCommandType.User
): Promise<void> {
this.customCommandsMap.set(id, prompt)
this.customCommandsMap.set(id, command)
let updated: Omit<CodyCommand, 'slashCommand'> | undefined = omit(command, 'slashCommand')

// Filter map to remove commands with non-match type
const filtered = new Map<string, Omit<CodyCommand, 'slashCommand'>>()
for (const [key, command] of this.customCommandsMap) {
if (command.type === type) {
command.type = undefined
filtered.set(fromSlashCommand(key), omit(command, 'slashCommand'))
for (const [key, _command] of this.customCommandsMap) {
if (_command.type === type) {
updated = omit(updated, 'type')
filtered.set(fromSlashCommand(key), updated)
}
}

// Add the new command to the filtered map
filtered.set(fromSlashCommand(id), omit(prompt, 'slashCommand'))
filtered.set(fromSlashCommand(id), updated)

// turn map into json
const jsonContext = { ...this.userJSON }
Expand Down Expand Up @@ -217,9 +221,29 @@ export class CustomCommandsManager implements vscode.Disposable {
case 'open':
void vscode.commands.executeCommand('vscode.open', uri)
break
case 'delete':
void vscode.workspace.fs.delete(uri)
case 'delete': {
let fileType = 'user settings file (~/.vscode/cody.json)'
if (type === CustomCommandType.Workspace) {
fileType = 'workspace settings file (.vscode/cody.json)'
}
const bin = isMac ? 'Trash' : 'Recycle Bin'
const confirmationKey = `Move to ${bin}`
// Playwright cannot capture and interact with pop-up modal in VS Code,
// so we need to turn off modal mode for the display message during tests.
const modal = !isTesting
vscode.window
.showInformationMessage(
`Are you sure you want to delete your Cody ${fileType}?`,
{ detail: `You can restore this file from the ${bin}.`, modal },
confirmationKey
)
.then(async choice => {
if (choice === confirmationKey) {
void vscode.workspace.fs.delete(uri)
}
})
break
}
case 'create':
await createJSONFile(uri)
.then(() => {
Expand Down
2 changes: 2 additions & 0 deletions vscode/src/commands/services/provider.ts
Original file line number Diff line number Diff line change
Expand Up @@ -13,12 +13,14 @@ const editorCommands: CodyCommand[] = [
prompt: ASK_QUESTION_COMMAND.slashCommand,
slashCommand: ASK_QUESTION_COMMAND.slashCommand,
mode: 'ask',
type: 'default',
},
{
description: EDIT_COMMAND.description,
prompt: EDIT_COMMAND.slashCommand,
slashCommand: EDIT_COMMAND.slashCommand,
mode: 'edit',
type: 'default',
},
]

Expand Down
12 changes: 6 additions & 6 deletions vscode/test/e2e/custom-command.test.ts
Original file line number Diff line number Diff line change
@@ -1,5 +1,4 @@
import { expect } from '@playwright/test'

import * as mockServer from '../fixtures/mock-server'

import { sidebarExplorer, sidebarSignin } from './common'
Expand Down Expand Up @@ -38,7 +37,6 @@ test('create a new user command via the custom commands menu', async ({ page, si
await page.getByText('Custom commands').click()

const commandName = 'ATestCommand'
const description = 'A test command added via menu'
const prompt = 'The test command has been created'

// Create a new command via menu
Expand All @@ -48,10 +46,6 @@ test('create a new user command via the custom commands menu', async ({ page, si
await expect(page.getByText('New Custom Cody Command: Slash Name')).toBeVisible()
await page.keyboard.type(commandName)
await page.keyboard.press('Enter')
// Enter description
await expect(page.getByText('New Custom Cody Command: Description')).toBeVisible()
await page.keyboard.type(description)
await page.keyboard.press('Enter')
// Enter prompt
await expect(page.getByText('New Custom Cody Command: Prompt')).toBeVisible()
await page.keyboard.type(prompt)
Expand Down Expand Up @@ -214,6 +208,12 @@ test('open and delete cody.json from the custom command menu', async ({ page, si
await page.locator('a').filter({ hasText: 'Open Workspace Settings (JSON)' }).hover()
await page.getByRole('button', { name: 'Delete Settings File' }).hover()
await page.getByRole('button', { name: 'Delete Settings File' }).click()

// Because we have turned off notification, we will need to check the notification center
// for the confirmation message.
await page.getByRole('button', { name: 'Do Not Disturb' }).click()
await page.getByRole('button', { name: /^Move to / }).click()

// The opened cody.json file should be shown as "Deleted"
await expect(page.getByRole('list').getByLabel(/cody.json(.*)Deleted$/)).toBeVisible()
})

0 comments on commit a1afc07

Please sign in to comment.