Skip to content

Commit 3320807

Browse files
committed
Add support for referencing commit messages only via a new CommitMessage symbol option
- implement symbol 'Commit message', that gets replaced by divided with "---" block including the selected commit's message (without eventual ascii tree) - remove item 'Commit message', instead show nested quick pick once user selects 'Commit' that shows 2 options: Diff with commit message, Commit message only - 'commit message' should use the same icon as 'commit' - remember last used 'diff with commit message' or 'Commit message only' for initial highlight ├── apps │ └── editor │ └── src │ ├── constants │ │ └── state-keys.ts │ ├── i18n │ │ └── translations │ │ └── views │ │ └── prompt │ │ └── handlers │ │ ├── hash-sign.ts │ │ └── index.ts │ ├── utils │ │ └── git-repository-utils.ts │ └── views │ └── prompt │ └── backend │ ├── message-handlers │ │ └── handle-hash-sign-quick-pick │ │ ├── handle-hash-sign-quick-pick.ts │ │ ├── index.ts │ │ └── symbols │ │ ├── changes-symbol.ts │ │ ├── commit-symbol.ts │ │ ├── index.ts │ │ ├── saved-context-symbol.ts │ │ ├── selection-symbol.ts │ │ └── skill-symbol.ts │ └── utils │ └── symbols │ ├── fragment │ │ └── replace-fragment-symbol.ts │ ├── git │ │ └── replace-git-symbols.ts │ ├── image │ │ └── replace-image-symbol.ts │ ├── pasted-text │ │ └── replace-pasted-text-symbol.ts │ ├── replace-symbols.ts │ ├── saved-context │ │ └── replace-saved-context-symbol.ts │ ├── selection │ │ └── replace-selection-symbol.ts │ ├── skill │ │ └── replace-skill-symbol.ts │ └── website │ └── replace-website-symbol.ts └── packages └── ui └── src └── components └── editor └── common └── prompts ├── PromptField │ ├── PromptField.module.scss │ ├── PromptField.stories.tsx │ ├── PromptField.tsx │ ├── components │ │ ├── Tooltip.module.scss │ │ ├── Tooltip.tsx │ │ └── index.ts │ ├── hooks │ │ ├── use-drag-drop.ts │ │ ├── use-dropdown.ts │ │ ├── use-ghost-text.ts │ │ ├── use-handlers │ │ │ ├── handle-copy.ts │ │ │ ├── handle-cut.ts │ │ │ ├── handle-input-change.ts │ │ │ ├── handle-input-click.ts │ │ │ ├── handle-key-down.ts │ │ │ ├── handle-paste.ts │ │ │ ├── handle-submit.ts │ │ │ ├── index.ts │ │ │ ├── types.ts │ │ │ ├── use-handlers.ts │ │ │ └── utils.ts │ │ └── use-keyboard-shortcuts.ts │ └── index.ts ├── Template │ ├── Template.module.scss │ ├── Template.tsx │ └── index.ts └── shared └── symbols ├── caret.ts ├── get-highlighted-text.tsx ├── index.ts ├── position-mapping.ts ├── prompt-symbols.module.scss ├── reconstruct-raw-value.ts └── use-symbol-deletion.ts
1 parent 7b173b6 commit 3320807

11 files changed

Lines changed: 216 additions & 96 deletions

File tree

‎apps/editor/src/constants/state-keys.ts‎

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -22,6 +22,8 @@ export const LAST_SELECTED_CONTEXT_SOURCE_IN_SYMBOLS_QUICK_PICK_STATE_KEY =
2222
'last-selected-context-source-in-symbols-quick-pick'
2323
export const LAST_SELECTED_REPOSITORY_IN_SYMBOLS_QUCK_PICK_STATE_KEY =
2424
'last-selected-repository-in-symbols-quick-pick'
25+
export const LAST_SELECTED_COMMIT_REFERENCE_ACTION_STATE_KEY =
26+
'last-selected-commit-reference-action'
2527

2628
export const CHECKPOINTS_STATE_KEY = 'checkpoints'
2729
export const TEMPORARY_CHECKPOINT_STATE_KEY = 'temporary-checkpoint'

‎apps/editor/src/views/prompt/backend/message-handlers/handle-hash-sign-quick-pick/handle-hash-sign-quick-pick.ts‎

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -30,7 +30,7 @@ const hash_sign_quick_pick = async (params: {
3030
},
3131
{
3232
label: commit_label,
33-
description: 'Diff from a specific commit'
33+
description: 'Changes from a specific commit'
3434
},
3535
{
3636
label: saved_context_label,
@@ -118,7 +118,7 @@ const hash_sign_quick_pick = async (params: {
118118
result = await handle_changes_item()
119119
break
120120
case commit_label:
121-
result = await handle_commit_item(params.extension_context, 'Commit')
121+
result = await handle_commit_item(params.extension_context)
122122
break
123123
case saved_context_label:
124124
result = await handle_saved_context_item(params.extension_context)

‎apps/editor/src/views/prompt/backend/message-handlers/handle-hash-sign-quick-pick/symbols/commit-symbol.ts‎

Lines changed: 122 additions & 43 deletions
Original file line numberDiff line numberDiff line change
@@ -1,12 +1,14 @@
11
import * as vscode from 'vscode'
22
import { execSync } from 'child_process'
33
import { dictionary } from '@shared/constants/dictionary'
4-
import { LAST_SELECTED_REPOSITORY_IN_SYMBOLS_QUCK_PICK_STATE_KEY } from '@/constants/state-keys'
4+
import {
5+
LAST_SELECTED_REPOSITORY_IN_SYMBOLS_QUCK_PICK_STATE_KEY,
6+
LAST_SELECTED_COMMIT_REFERENCE_ACTION_STATE_KEY
7+
} from '@/constants/state-keys'
58
import { GIT_LOG_SINCE_DURATION } from '@/constants/values'
69

710
export const handle_commit_item = async (
8-
extension_context: vscode.ExtensionContext,
9-
symbol: 'Commit'
11+
extension_context: vscode.ExtensionContext
1012
): Promise<string | 'continue' | undefined> => {
1113
try {
1214
const workspace_folders = vscode.workspace.workspaceFolders
@@ -136,56 +138,133 @@ export const handle_commit_item = async (
136138
}
137139
}
138140

139-
const quick_pick = vscode.window.createQuickPick()
140-
quick_pick.items = commit_items
141-
quick_pick.placeholder = 'Select a commit to reference'
142-
quick_pick.title = 'Commits'
143-
quick_pick.buttons = [vscode.QuickInputButtons.Back]
144-
quick_pick.matchOnDetail = true
145-
146-
const selected_commit = await new Promise<
147-
vscode.QuickPickItem | 'back' | undefined
148-
>((resolve) => {
149-
let is_accepted = false
150-
let did_trigger_back = false
151-
const disposables: vscode.Disposable[] = []
152-
153-
disposables.push(
154-
quick_pick.onDidTriggerButton((button) => {
155-
if (button === vscode.QuickInputButtons.Back) {
156-
did_trigger_back = true
141+
let go_back_to_folders = false
142+
143+
while (true) {
144+
const quick_pick = vscode.window.createQuickPick()
145+
quick_pick.items = commit_items
146+
quick_pick.placeholder = 'Select a commit to reference'
147+
quick_pick.title = 'Commits'
148+
quick_pick.buttons = [vscode.QuickInputButtons.Back]
149+
quick_pick.matchOnDetail = true
150+
151+
const selected_commit = await new Promise<
152+
vscode.QuickPickItem | 'back' | undefined
153+
>((resolve) => {
154+
let is_accepted = false
155+
let did_trigger_back = false
156+
const disposables: vscode.Disposable[] = []
157+
158+
disposables.push(
159+
quick_pick.onDidTriggerButton((button) => {
160+
if (button === vscode.QuickInputButtons.Back) {
161+
did_trigger_back = true
162+
quick_pick.hide()
163+
resolve('back')
164+
}
165+
}),
166+
quick_pick.onDidAccept(() => {
167+
is_accepted = true
168+
resolve(quick_pick.selectedItems[0])
157169
quick_pick.hide()
158-
resolve('back')
159-
}
160-
}),
161-
quick_pick.onDidAccept(() => {
162-
is_accepted = true
163-
resolve(quick_pick.selectedItems[0])
164-
quick_pick.hide()
165-
}),
166-
quick_pick.onDidHide(() => {
167-
if (!is_accepted && !did_trigger_back) {
168-
resolve(undefined)
169-
}
170-
disposables.forEach((d) => d.dispose())
171-
quick_pick.dispose()
172-
})
170+
}),
171+
quick_pick.onDidHide(() => {
172+
if (!is_accepted && !did_trigger_back) {
173+
resolve(undefined)
174+
}
175+
disposables.forEach((d) => d.dispose())
176+
quick_pick.dispose()
177+
})
178+
)
179+
quick_pick.show()
180+
})
181+
182+
if (!selected_commit || selected_commit == 'back') {
183+
if (git_folders.length > 1) {
184+
go_back_to_folders = true
185+
break
186+
}
187+
return 'continue'
188+
}
189+
190+
const action_quick_pick = vscode.window.createQuickPick()
191+
const action_items = [
192+
{
193+
label: 'Diff with commit message'
194+
},
195+
{
196+
label: 'Commit message only'
197+
}
198+
]
199+
action_quick_pick.items = action_items
200+
action_quick_pick.placeholder = 'Select what to include'
201+
action_quick_pick.title = 'Commit Reference Options'
202+
action_quick_pick.buttons = [vscode.QuickInputButtons.Back]
203+
204+
const last_action = extension_context.workspaceState.get<string>(
205+
LAST_SELECTED_COMMIT_REFERENCE_ACTION_STATE_KEY
173206
)
174-
quick_pick.show()
175-
})
207+
if (last_action) {
208+
const active = action_items.find((item) => item.label === last_action)
209+
if (active) action_quick_pick.activeItems = [active]
210+
}
176211

177-
if (!selected_commit || selected_commit == 'back') {
178-
if (git_folders.length > 1) {
212+
const selected_action = await new Promise<
213+
vscode.QuickPickItem | 'back' | undefined
214+
>((resolve) => {
215+
let is_accepted = false
216+
let did_trigger_back = false
217+
const disposables: vscode.Disposable[] = []
218+
219+
disposables.push(
220+
action_quick_pick.onDidTriggerButton((button) => {
221+
if (button === vscode.QuickInputButtons.Back) {
222+
did_trigger_back = true
223+
action_quick_pick.hide()
224+
resolve('back')
225+
}
226+
}),
227+
action_quick_pick.onDidAccept(() => {
228+
is_accepted = true
229+
resolve(action_quick_pick.selectedItems[0])
230+
action_quick_pick.hide()
231+
}),
232+
action_quick_pick.onDidHide(() => {
233+
if (!is_accepted && !did_trigger_back) {
234+
resolve(undefined)
235+
}
236+
disposables.forEach((d) => d.dispose())
237+
action_quick_pick.dispose()
238+
})
239+
)
240+
action_quick_pick.show()
241+
})
242+
243+
if (selected_action == 'back') {
179244
continue
180245
}
181-
return 'continue'
182-
}
183246

184-
if (selected_commit) {
247+
if (!selected_action) {
248+
return 'continue'
249+
}
250+
251+
await extension_context.workspaceState.update(
252+
LAST_SELECTED_COMMIT_REFERENCE_ACTION_STATE_KEY,
253+
selected_action.label
254+
)
255+
256+
const symbol =
257+
selected_action.label === 'Commit message only'
258+
? 'CommitMessage'
259+
: 'Commit'
185260
const message = (selected_commit.detail || '').replace(/"/g, '\\"')
186261
return `#${symbol}(${selected_folder.name}:${selected_commit.label} "${message}")`
187262
}
188263

264+
if (go_back_to_folders) {
265+
continue
266+
}
267+
189268
return 'continue'
190269
}
191270
} catch (error) {

‎apps/editor/src/views/prompt/backend/utils/symbols/git/replace-git-symbols.ts‎

Lines changed: 53 additions & 34 deletions
Original file line numberDiff line numberDiff line change
@@ -428,7 +428,8 @@ const build_commit_changes_markdown = (
428428
export const replace_commit_symbol = async (params: {
429429
instruction: string
430430
}): Promise<{ instruction: string; commit_definitions: string }> => {
431-
const regex = /#Commit\(([^:]+):([a-fA-F0-9]+)\s*(?:"((?:[^"\\]|\\.)*)")?\)/g
431+
const regex =
432+
/#(Commit|CommitMessage)\(([^:]+):([a-fA-F0-9]+)\s*(?:"((?:[^"\\]|\\.)*)")?\)/g
432433

433434
let result_instruction = params.instruction
434435
let commit_definitions = ''
@@ -444,9 +445,10 @@ export const replace_commit_symbol = async (params: {
444445

445446
for (const match of matches) {
446447
const full_match = match[0]
447-
const folder_name = match[1]
448-
const commit_hash = match[2]
449-
const commit_message = match[3]?.replace(/\\(.)/g, '$1')
448+
const symbol_type = match[1]
449+
const folder_name = match[2]
450+
const commit_hash = match[3]
451+
const commit_message = match[4]?.replace(/\\(.)/g, '$1')
450452

451453
const target_folder = workspace_folders.find(
452454
(folder) => folder.name === folder_name
@@ -460,21 +462,7 @@ export const replace_commit_symbol = async (params: {
460462
}
461463

462464
try {
463-
const diff = execSync(`git show ${commit_hash}`, {
464-
cwd: target_folder.uri.fsPath,
465-
encoding: 'utf-8'
466-
}).toString()
467-
468-
if (!diff || diff.length == 0) {
469-
vscode.window.showInformationMessage(
470-
t(
471-
'views.prompt.handlers.utils.symbols.git.replace-git-symbols.commit-seems-empty',
472-
{ commit_hash }
473-
)
474-
)
475-
result_instruction = result_instruction.replace(full_match, '')
476-
continue
477-
}
465+
let replacement_text = ''
478466

479467
let commit_message_body = ''
480468
try {
@@ -491,15 +479,6 @@ export const replace_commit_symbol = async (params: {
491479
})
492480
}
493481

494-
const replacement_text = build_commit_changes_markdown(
495-
diff,
496-
target_folder.uri.fsPath,
497-
commit_hash,
498-
workspace_folders.length > 1 ? folder_name : undefined,
499-
commit_message_body
500-
)
501-
commit_definitions += replacement_text
502-
503482
const short_hash = commit_hash.substring(0, 7)
504483
const header_text = `Commit ${short_hash}`
505484
const title_text = commit_message
@@ -510,12 +489,52 @@ export const replace_commit_symbol = async (params: {
510489
.replace(/[^a-z0-9]+/g, '-')
511490
.replace(/^-|-$/g, '')
512491

513-
result_instruction = result_instruction.replace(
514-
new RegExp(
515-
`\\s*${full_match.replace(/[.*+?^${}()|[\]\\]/g, '\\$&')}\\s*`
516-
),
517-
` [${title_text}](#${link_hash}) `
518-
)
492+
if (symbol_type === 'Commit') {
493+
const diff = execSync(`git show ${commit_hash}`, {
494+
cwd: target_folder.uri.fsPath,
495+
encoding: 'utf-8'
496+
}).toString()
497+
498+
if (!diff || diff.length == 0) {
499+
vscode.window.showInformationMessage(
500+
t(
501+
'views.prompt.handlers.utils.symbols.git.replace-git-symbols.commit-seems-empty',
502+
{ commit_hash }
503+
)
504+
)
505+
result_instruction = result_instruction.replace(full_match, '')
506+
continue
507+
}
508+
509+
replacement_text = build_commit_changes_markdown(
510+
diff,
511+
target_folder.uri.fsPath,
512+
commit_hash,
513+
workspace_folders.length > 1 ? folder_name : undefined,
514+
commit_message_body
515+
)
516+
} else {
517+
if (commit_message_body) {
518+
replacement_text = `---\n\n${commit_message_body}\n\n---\n\n`
519+
}
520+
}
521+
522+
if (symbol_type === 'Commit') {
523+
commit_definitions += replacement_text
524+
result_instruction = result_instruction.replace(
525+
new RegExp(
526+
`\\s*${full_match.replace(/[.*+?^${}()|[\]\\]/g, '\\$&')}\\s*`
527+
),
528+
` [${title_text}](#${link_hash}) `
529+
)
530+
} else {
531+
result_instruction = result_instruction.replace(
532+
new RegExp(
533+
`\\s*${full_match.replace(/[.*+?^${}()|[\]\\]/g, '\\$&')}\\s*`
534+
),
535+
`\n\n${replacement_text}`
536+
)
537+
}
519538
} catch (error) {
520539
vscode.window.showErrorMessage(
521540
dictionary.error_message.FAILED_TO_GET_DIFF_FOR_COMMIT(commit_hash)

‎apps/editor/src/views/prompt/backend/utils/symbols/replace-symbols.ts‎

Lines changed: 4 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -37,7 +37,10 @@ export const replace_symbols = async (
3737
skill_definitions += result.changes_definitions
3838
}
3939

40-
if (processed_instructions.includes('#Commit(')) {
40+
if (
41+
processed_instructions.includes('#Commit(') ||
42+
processed_instructions.includes('#CommitMessage(')
43+
) {
4144
const result = await replace_commit_symbol({
4245
instruction: processed_instructions
4346
})

‎packages/ui/src/components/editor/common/prompts/PromptField/hooks/use-handlers/utils.ts‎

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -10,7 +10,7 @@ export const get_symbol_ranges = (params: {
1010
}): { start: number; end: number }[] => {
1111
const ranges: { start: number; end: number }[] = []
1212
const regex =
13-
/`([^`]+)`|(#Changes\([^)]+\))|(#Selection)|(#SavedContext\((?:WorkspaceState|JSON) "(?:\\.|[^"\\])*"\))|(#Commit\([^:]+:[^\s"]+ "(?:\\.|[^"\\])*"\))|(<fragment path="[^"]+"(?: [^>]+)?>[\s\S]*?<\/fragment>)|(#Skill\([^)]+\))|(#Image\([a-fA-F0-9]+\))|(#PastedText\([a-fA-F0-9]+:\d+\))|(#Website\([^)]+\))/g
13+
/`([^`]+)`|(#Changes\([^)]+\))|(#Selection)|(#SavedContext\((?:WorkspaceState|JSON) "(?:\\.|[^"\\])*"\))|(#(?:Commit|CommitMessage)\([^:]+:[^\s"]+ "(?:\\.|[^"\\])*"\))|(<fragment path="[^"]+"(?: [^>]+)?>[\s\S]*?<\/fragment>)|(#Skill\([^)]+\))|(#Image\([a-fA-F0-9]+\))|(#PastedText\([a-fA-F0-9]+:\d+\))|(#Website\([^)]+\))/g
1414

1515
let match
1616
while ((match = regex.exec(params.text)) !== null) {

‎packages/ui/src/components/editor/common/prompts/shared/symbols/get-highlighted-text.tsx‎

Lines changed: 7 additions & 6 deletions
Original file line numberDiff line numberDiff line change
@@ -58,7 +58,8 @@ export const get_highlighted_text = (params: {
5858
const saved_context_regex_part =
5959
'#SavedContext\\((?:WorkspaceState|JSON) "(?:\\\\.|[^"\\\\])*"\\)'
6060

61-
const commit_regex_part = '#Commit\\([^:]+:[^\\s"]+ "(?:\\\\.|[^"\\\\])*"\\)'
61+
const commit_regex_part =
62+
'#(?:Commit|CommitMessage)\\([^:]+:[^\\s"]+ "(?:\\\\.|[^"\\\\])*"\\)'
6263

6364
const fragment_regex_part =
6465
'<fragment path="[^"]+"(?: [^>]+)?>[\\s\\S]*?<\\/fragment>'
@@ -176,13 +177,13 @@ export const get_highlighted_text = (params: {
176177
)}"</span></span>`
177178
}
178179
const commit_match = part.match(
179-
/^#Commit\(([^:]+):([^\s"]+) "((?:\\.|[^"\\])*)"\)$/
180+
/^#(Commit|CommitMessage)\(([^:]+):([^\s"]+) "((?:\\.|[^"\\])*)"\)$/
180181
)
181182
if (part && commit_match) {
182-
const symbol = 'Commit'
183-
const repo_name = commit_match[1]
184-
const commit_hash = commit_match[2]
185-
const commit_message = commit_match[3].replace(/\\"/g, '"')
183+
const symbol = commit_match[1]
184+
const repo_name = commit_match[2]
185+
const commit_hash = commit_match[3]
186+
const commit_message = commit_match[4].replace(/\\"/g, '"')
186187
const short_hash = commit_hash.substring(0, 7)
187188
return `<span class="${cn(
188189
styles['symbol'],

0 commit comments

Comments
 (0)