mirror of
https://github.com/voideditor/void
synced 2026-05-24 09:58:23 +00:00
update code selection code
This commit is contained in:
parent
878ec1e782
commit
620b335dc5
5 changed files with 141 additions and 133 deletions
|
|
@ -1,55 +1,25 @@
|
||||||
|
|
||||||
import { URI } from '../../../../../base/common/uri.js';
|
import { CodeSelection } from '../registerThreads.js';
|
||||||
|
|
||||||
|
export const filesStr = (selections: CodeSelection[]) => {
|
||||||
|
|
||||||
export type LLMCodeSelection = { selectionStr: string; filePath: URI }
|
return selections.map(({ fileURI, content, selectionStr }) =>
|
||||||
export type LLMFile = { content: string, filepath: URI }
|
`\
|
||||||
|
File: ${fileURI.fsPath}
|
||||||
export const filesStr = (fullFiles: LLMFile[]) => {
|
|
||||||
return fullFiles.map(({ filepath, content }) =>
|
|
||||||
`
|
|
||||||
${filepath.fsPath}
|
|
||||||
\`\`\`
|
\`\`\`
|
||||||
${content}
|
${content}
|
||||||
\`\`\``).join('\n')
|
\`\`\`${selectionStr === null ? '' : `
|
||||||
|
Selection: ${selectionStr}`}
|
||||||
|
`).join('\n')
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
export const userInstructionsStr = (instructions: string, files: LLMFile[], selection: LLMCodeSelection | null) => {
|
export const userInstructionsStr = (instructions: string, selections: CodeSelection[]) => {
|
||||||
let str = '';
|
let str = '';
|
||||||
|
if (selections.length > 0) {
|
||||||
if (files.length > 0) {
|
str += filesStr(selections);
|
||||||
str += filesStr(files);
|
str += `Please edit the selected code following these instructions:\n`
|
||||||
}
|
|
||||||
|
|
||||||
if (selection) {
|
|
||||||
str += `
|
|
||||||
I am currently selecting this code:
|
|
||||||
\t\`\`\`${selection.selectionStr}\`\`\`
|
|
||||||
`;
|
|
||||||
}
|
|
||||||
|
|
||||||
if (files.length > 0 && selection) {
|
|
||||||
str += `
|
|
||||||
Please edit the selected code or the entire file following these instructions:
|
|
||||||
`;
|
|
||||||
} else if (files.length > 0) {
|
|
||||||
str += `
|
|
||||||
Please edit the file following these instructions:
|
|
||||||
`;
|
|
||||||
} else if (selection) {
|
|
||||||
str += `
|
|
||||||
Please edit the selected code following these instructions:
|
|
||||||
`;
|
|
||||||
}
|
|
||||||
|
|
||||||
str += `
|
|
||||||
\t${instructions}
|
|
||||||
`;
|
|
||||||
if (files.length > 0) {
|
|
||||||
str += `
|
|
||||||
\tIf you make a change, rewrite the entire file.
|
|
||||||
`; // TODO don't rewrite the whole file on prompt, instead rewrite it when click Apply
|
|
||||||
}
|
}
|
||||||
|
str += `${instructions}`;
|
||||||
return str;
|
return str;
|
||||||
};
|
};
|
||||||
|
|
|
||||||
|
|
@ -1,12 +1,12 @@
|
||||||
import React, { FormEvent, useCallback, useRef, useState } from 'react';
|
import React, { FormEvent, Fragment, useCallback, useRef, useState } from 'react';
|
||||||
|
|
||||||
|
|
||||||
import { useConfigState, useService, useThreadsState } from '../util/services.js';
|
import { useConfigState, useService, useThreadsState } from '../util/services.js';
|
||||||
import { URI } from '../../../../../../../base/common/uri.js';
|
|
||||||
import { VSReadFile } from '../../../registerInlineDiffs.js';
|
import { VSReadFile } from '../../../registerInlineDiffs.js';
|
||||||
import { sendLLMMessage } from '../util/sendLLMMessage.js';
|
import { sendLLMMessage } from '../util/sendLLMMessage.js';
|
||||||
import { generateDiffInstructions } from '../../../prompt/systemPrompts.js';
|
import { generateDiffInstructions } from '../../../prompt/systemPrompts.js';
|
||||||
import { LLMCodeSelection, userInstructionsStr } from '../../../prompt/stringifyFiles.js';
|
import { userInstructionsStr } from '../../../prompt/stringifyFiles.js';
|
||||||
|
import { CodeSelection, CodeStagingSelection } from '../../../registerThreads.js';
|
||||||
|
|
||||||
import { BlockCode } from '../markdown/BlockCode.js';
|
import { BlockCode } from '../markdown/BlockCode.js';
|
||||||
import { MarkdownRender } from '../markdown/MarkdownRender.js';
|
import { MarkdownRender } from '../markdown/MarkdownRender.js';
|
||||||
|
|
@ -17,8 +17,7 @@ export type ChatMessage =
|
||||||
role: 'user';
|
role: 'user';
|
||||||
content: string; // content sent to the llm
|
content: string; // content sent to the llm
|
||||||
displayContent: string; // content displayed to user
|
displayContent: string; // content displayed to user
|
||||||
selection: LLMCodeSelection | null; // the user's selection
|
selections: CodeSelection[] | null; // the user's selection
|
||||||
files: URI[]; // the files sent in the message
|
|
||||||
}
|
}
|
||||||
| {
|
| {
|
||||||
role: 'assistant';
|
role: 'assistant';
|
||||||
|
|
@ -40,37 +39,51 @@ const getBasename = (pathStr: string) => {
|
||||||
return parts[parts.length - 1]
|
return parts[parts.length - 1]
|
||||||
}
|
}
|
||||||
|
|
||||||
export const SelectedFiles = ({ files, setFiles, }: { files: URI[]; setFiles: null | ((files: URI[]) => void) }) => {
|
export const SelectedFiles = ({ type, selections, setStagingSelns, }:
|
||||||
|
| { type: 'past', selections: CodeSelection[]; setStagingSelns?: undefined }
|
||||||
|
| { type: 'staging', selections: CodeStagingSelection[]; setStagingSelns: ((files: CodeStagingSelection[]) => void) }
|
||||||
|
) => {
|
||||||
return (
|
return (
|
||||||
files.length !== 0 && (
|
selections.length !== 0 && (
|
||||||
<div className='flex flex-wrap -mx-1 -mb-1'>
|
<div className='flex flex-wrap -mx-1 -mb-1'>
|
||||||
{files.map((filename, i) => (
|
{selections.map((selection, i) => (
|
||||||
<button
|
<Fragment key={i}>
|
||||||
key={filename.path}
|
|
||||||
disabled={!setFiles}
|
|
||||||
className={`btn btn-secondary btn-sm border border-vscode-input-border rounded flex items-center space-x-2 mx-1 mb-1 disabled:cursor-default`}
|
|
||||||
type='button'
|
|
||||||
onClick={() => setFiles?.([...files.slice(0, i), ...files.slice(i + 1, Infinity)])}
|
|
||||||
>
|
|
||||||
<span>{getBasename(filename.fsPath)}</span>
|
|
||||||
|
|
||||||
{/* X button */}
|
<button
|
||||||
{!!setFiles && <span className=''>
|
disabled={!setStagingSelns}
|
||||||
<svg
|
className={`btn btn-secondary btn-sm border border-vscode-input-border rounded flex items-center space-x-2 mx-1 mb-1 disabled:cursor-default`}
|
||||||
xmlns='http://www.w3.org/2000/svg'
|
type='button'
|
||||||
fill='none'
|
onClick={type === 'staging' ? () => setStagingSelns([...selections.slice(0, i), ...selections.slice(i + 1, Infinity)]) : undefined}
|
||||||
viewBox='0 0 24 24'
|
>
|
||||||
stroke='currentColor'
|
<span>{getBasename(selection.fileURI.fsPath)}</span>
|
||||||
className='size-4'
|
|
||||||
>
|
{/* X button */}
|
||||||
<path
|
{!!setStagingSelns && <span className=''>
|
||||||
strokeLinecap='round'
|
<svg
|
||||||
strokeLinejoin='round'
|
xmlns='http://www.w3.org/2000/svg'
|
||||||
d='M6 18 18 6M6 6l12 12'
|
fill='none'
|
||||||
/>
|
viewBox='0 0 24 24'
|
||||||
</svg>
|
stroke='currentColor'
|
||||||
</span>}
|
className='size-4'
|
||||||
</button>
|
>
|
||||||
|
<path
|
||||||
|
strokeLinecap='round'
|
||||||
|
strokeLinejoin='round'
|
||||||
|
d='M6 18 18 6M6 6l12 12'
|
||||||
|
/>
|
||||||
|
</svg>
|
||||||
|
</span>}
|
||||||
|
</button>
|
||||||
|
{selection.selectionStr && <BlockCode text={selection.selectionStr}
|
||||||
|
buttonsOnHover={(
|
||||||
|
<button
|
||||||
|
onClick={() => setStagingSelns?.([...selections.slice(0, i), { ...selection, selectionStr: null }, ...selections.slice(i + 1, Infinity)])}
|
||||||
|
className="btn btn-secondary btn-sm border border-vscode-input-border rounded"
|
||||||
|
>
|
||||||
|
Remove
|
||||||
|
</button>
|
||||||
|
)} />}
|
||||||
|
</Fragment>
|
||||||
))}
|
))}
|
||||||
</div>
|
</div>
|
||||||
)
|
)
|
||||||
|
|
@ -90,11 +103,7 @@ const ChatBubble = ({ chatMessage }: { chatMessage: ChatMessage }) => {
|
||||||
|
|
||||||
if (role === 'user') {
|
if (role === 'user') {
|
||||||
chatbubbleContents = <>
|
chatbubbleContents = <>
|
||||||
<SelectedFiles files={chatMessage.files} setFiles={null} />
|
<SelectedFiles type='past' selections={chatMessage.selections} />
|
||||||
{chatMessage.selection?.selectionStr && <BlockCode
|
|
||||||
text={chatMessage.selection.selectionStr}
|
|
||||||
buttonsOnHover={null}
|
|
||||||
/>}
|
|
||||||
{children}
|
{children}
|
||||||
</>
|
</>
|
||||||
}
|
}
|
||||||
|
|
@ -133,8 +142,6 @@ export const SidebarChat = () => {
|
||||||
|
|
||||||
// ----- SIDEBAR CHAT state (local) -----
|
// ----- SIDEBAR CHAT state (local) -----
|
||||||
// state of current message
|
// state of current message
|
||||||
const [selection, setSelection] = useState<LLMCodeSelection | null>(null) // the code the user is selecting
|
|
||||||
const [files, setFiles] = useState<URI[]>([]) // the names of the files in the chat
|
|
||||||
const [instructions, setInstructions] = useState('') // the user's instructions
|
const [instructions, setInstructions] = useState('') // the user's instructions
|
||||||
|
|
||||||
// state of chat
|
// state of chat
|
||||||
|
|
@ -146,8 +153,6 @@ export const SidebarChat = () => {
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
const isDisabled = !instructions
|
const isDisabled = !instructions
|
||||||
|
|
||||||
const formRef = useRef<HTMLFormElement | null>(null)
|
const formRef = useRef<HTMLFormElement | null>(null)
|
||||||
|
|
@ -160,16 +165,15 @@ export const SidebarChat = () => {
|
||||||
setIsLoading(true)
|
setIsLoading(true)
|
||||||
setInstructions('');
|
setInstructions('');
|
||||||
formRef.current?.reset(); // reset the form's text when clear instructions or unexpected behavior happens
|
formRef.current?.reset(); // reset the form's text when clear instructions or unexpected behavior happens
|
||||||
setSelection(null)
|
threadsStateService.setStaging([]) // clear staging
|
||||||
setFiles([])
|
|
||||||
setLatestError('')
|
setLatestError('')
|
||||||
|
|
||||||
|
const stagingSelections = threadsStateService.state._currentStagingSelections
|
||||||
|
|
||||||
|
const selections = await Promise.all(
|
||||||
const relevantFiles = await Promise.all(
|
stagingSelections.map(async (sel) => ({ ...sel, content: await VSReadFile(fileService, sel.fileURI) }))
|
||||||
files.map(async (filepath) => ({ content: await VSReadFile(fileService, filepath), filepath }))
|
|
||||||
).then(
|
).then(
|
||||||
(files) => files.filter(file => file.content !== null) as {content:string, filepath:URI}[]
|
(files) => files.filter(file => file.content !== null) as CodeSelection[]
|
||||||
)
|
)
|
||||||
|
|
||||||
// add system message to chat history
|
// add system message to chat history
|
||||||
|
|
@ -177,8 +181,8 @@ export const SidebarChat = () => {
|
||||||
|
|
||||||
threadsStateService.addMessageToCurrentThread(systemPromptElt)
|
threadsStateService.addMessageToCurrentThread(systemPromptElt)
|
||||||
|
|
||||||
const userContent = userInstructionsStr(instructions, relevantFiles, selection)
|
const userContent = userInstructionsStr(instructions, selections)
|
||||||
const newHistoryElt: ChatMessage = { role: 'user', content: userContent, displayContent: instructions, selection, files }
|
const newHistoryElt: ChatMessage = { role: 'user', content: userContent, displayContent: instructions, selections }
|
||||||
threadsStateService.addMessageToCurrentThread(newHistoryElt)
|
threadsStateService.addMessageToCurrentThread(newHistoryElt)
|
||||||
|
|
||||||
const currentThread = threadsStateService.getCurrentThread(threadsStateService.state) // the the instant state right now, don't wait for the React state
|
const currentThread = threadsStateService.getCurrentThread(threadsStateService.state) // the the instant state right now, don't wait for the React state
|
||||||
|
|
@ -231,6 +235,9 @@ export const SidebarChat = () => {
|
||||||
|
|
||||||
|
|
||||||
const currentThread = threadsStateService.getCurrentThread(threadsState)
|
const currentThread = threadsStateService.getCurrentThread(threadsState)
|
||||||
|
|
||||||
|
const selections = threadsState._currentStagingSelections ?? []
|
||||||
|
|
||||||
return <>
|
return <>
|
||||||
<div className="overflow-x-hidden space-y-4">
|
<div className="overflow-x-hidden space-y-4">
|
||||||
{/* previous messages */}
|
{/* previous messages */}
|
||||||
|
|
@ -246,22 +253,9 @@ export const SidebarChat = () => {
|
||||||
<div className="text-left">
|
<div className="text-left">
|
||||||
<div className="relative">
|
<div className="relative">
|
||||||
<div className="input">
|
<div className="input">
|
||||||
{/* selection */}
|
{/* selections */}
|
||||||
{(files.length || selection?.selectionStr) && <div className="p-2 pb-0 space-y-2">
|
{(selections.length || selections) && <div className="p-2 pb-0 space-y-2">
|
||||||
{/* selected files */}
|
<SelectedFiles type='staging' selections={selections} setStagingSelns={threadsStateService.setStaging} />
|
||||||
<SelectedFiles files={files} setFiles={setFiles} />
|
|
||||||
{/* selected code */}
|
|
||||||
{!!selection?.selectionStr && (
|
|
||||||
<BlockCode text={selection.selectionStr}
|
|
||||||
buttonsOnHover={(
|
|
||||||
<button
|
|
||||||
onClick={() => setSelection(null)}
|
|
||||||
className="btn btn-secondary btn-sm border border-vscode-input-border rounded"
|
|
||||||
>
|
|
||||||
Remove
|
|
||||||
</button>
|
|
||||||
)} />
|
|
||||||
)}
|
|
||||||
</div>}
|
</div>}
|
||||||
|
|
||||||
<form
|
<form
|
||||||
|
|
|
||||||
|
|
@ -336,7 +336,7 @@ class InlineDiffsService extends Disposable implements IInlineDiffsService {
|
||||||
|
|
||||||
for (let computedDiff of computedDiffs) {
|
for (let computedDiff of computedDiffs) {
|
||||||
// add the view zone
|
// add the view zone
|
||||||
const greenRange: IRange = { startLineNumber: computedDiff.startLine, startColumn: 0, endLineNumber: computedDiff.endLine, endColumn: Number.MAX_SAFE_INTEGER, }
|
const greenRange: IRange = { startLineNumber: computedDiff.startLine + 1, startColumn: 0, endLineNumber: computedDiff.endLine + 1, endColumn: Number.MAX_SAFE_INTEGER, }
|
||||||
const dispose = this._addInlineDiffZone(model, computedDiff.originalCode, greenRange)
|
const dispose = this._addInlineDiffZone(model, computedDiff.originalCode, greenRange)
|
||||||
|
|
||||||
// create a Diff of it
|
// create a Diff of it
|
||||||
|
|
@ -772,7 +772,7 @@ Please finish writing the new file by applying the diff to the original file. Re
|
||||||
this._writeToModel(
|
this._writeToModel(
|
||||||
model,
|
model,
|
||||||
diff.originalCode,
|
diff.originalCode,
|
||||||
{ startLineNumber: diffArea.startLine, startColumn: 0, endLineNumber: diffArea.endLine, endColumn: Number.MAX_SAFE_INTEGER, },
|
{ startLineNumber: diffArea.startLine + 1, startColumn: 0, endLineNumber: diffArea.endLine + 1, endColumn: Number.MAX_SAFE_INTEGER, },
|
||||||
editGroup
|
editGroup
|
||||||
)
|
)
|
||||||
|
|
||||||
|
|
|
||||||
|
|
@ -28,7 +28,7 @@ import { Disposable } from '../../../../base/common/lifecycle.js';
|
||||||
import { Emitter, Event } from '../../../../base/common/event.js';
|
import { Emitter, Event } from '../../../../base/common/event.js';
|
||||||
import { InstantiationType, registerSingleton } from '../../../../platform/instantiation/common/extensions.js';
|
import { InstantiationType, registerSingleton } from '../../../../platform/instantiation/common/extensions.js';
|
||||||
import { IViewsService } from '../../../services/views/common/viewsService.js';
|
import { IViewsService } from '../../../services/views/common/viewsService.js';
|
||||||
import { IThreadHistoryService } from './registerThreads.js';
|
import { CodeStagingSelection, IThreadHistoryService } from './registerThreads.js';
|
||||||
import { IConfigurationService } from '../../../../platform/configuration/common/configuration.js';
|
import { IConfigurationService } from '../../../../platform/configuration/common/configuration.js';
|
||||||
import { IThemeService } from '../../../../platform/theme/common/themeService.js';
|
import { IThemeService } from '../../../../platform/theme/common/themeService.js';
|
||||||
import { IContextMenuService } from '../../../../platform/contextview/browser/contextView.js';
|
import { IContextMenuService } from '../../../../platform/contextview/browser/contextView.js';
|
||||||
|
|
@ -44,6 +44,8 @@ import mountFn from './react/out/sidebar-tsx/Sidebar.js';
|
||||||
import { IVoidConfigStateService } from './registerConfig.js';
|
import { IVoidConfigStateService } from './registerConfig.js';
|
||||||
import { IFileService } from '../../../../platform/files/common/files.js';
|
import { IFileService } from '../../../../platform/files/common/files.js';
|
||||||
import { IInlineDiffsService } from './registerInlineDiffs.js';
|
import { IInlineDiffsService } from './registerInlineDiffs.js';
|
||||||
|
import { IEditorService } from '../../../services/editor/common/editorService.js';
|
||||||
|
import { ICodeEditorService } from '../../../../editor/browser/services/codeEditorService.js';
|
||||||
// import { IClipboardService } from '../../../../platform/clipboard/common/clipboardService.js';
|
// import { IClipboardService } from '../../../../platform/clipboard/common/clipboardService.js';
|
||||||
|
|
||||||
|
|
||||||
|
|
@ -169,6 +171,8 @@ export interface IVoidSidebarStateService {
|
||||||
onDidBlurChat: Event<void>;
|
onDidBlurChat: Event<void>;
|
||||||
fireFocusChat(): void;
|
fireFocusChat(): void;
|
||||||
fireBlurChat(): void;
|
fireBlurChat(): void;
|
||||||
|
|
||||||
|
openView(): void;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
|
|
@ -192,8 +196,9 @@ class VoidSidebarStateService extends Disposable implements IVoidSidebarStateSer
|
||||||
|
|
||||||
setState(newState: Partial<VoidSidebarState>) {
|
setState(newState: Partial<VoidSidebarState>) {
|
||||||
// make sure view is open if the tab changes
|
// make sure view is open if the tab changes
|
||||||
if ('currentTab' in newState)
|
if ('currentTab' in newState) {
|
||||||
this._viewsService.openView(SIDEBAR_VIEW_ID);
|
this.openView()
|
||||||
|
}
|
||||||
|
|
||||||
this.state = { ...this.state, ...newState }
|
this.state = { ...this.state, ...newState }
|
||||||
this._onDidChangeState.fire()
|
this._onDidChangeState.fire()
|
||||||
|
|
@ -207,12 +212,18 @@ class VoidSidebarStateService extends Disposable implements IVoidSidebarStateSer
|
||||||
this._onBlurChat.fire()
|
this._onBlurChat.fire()
|
||||||
}
|
}
|
||||||
|
|
||||||
|
openView() {
|
||||||
|
this._viewsService.openViewContainer(VOID_VIEW_CONTAINER_ID);
|
||||||
|
this._viewsService.openView(SIDEBAR_VIEW_ID);
|
||||||
|
}
|
||||||
|
|
||||||
constructor(
|
constructor(
|
||||||
@IViewsService private readonly _viewsService: IViewsService,
|
@IViewsService private readonly _viewsService: IViewsService,
|
||||||
|
// @IThreadHistoryService private readonly _threadHistoryService: IThreadHistoryService,
|
||||||
) {
|
) {
|
||||||
super()
|
super()
|
||||||
// auto open the view on mount (if it bothers you this is here, this is technically just initializing the state of the view)
|
// auto open the view on mount (if it bothers you this is here, this is technically just initializing the state of the view)
|
||||||
this._viewsService.openView(SIDEBAR_VIEW_ID);
|
this.openView()
|
||||||
|
|
||||||
// initial state
|
// initial state
|
||||||
this.state = {
|
this.state = {
|
||||||
|
|
@ -233,27 +244,41 @@ registerSingleton(IVoidSidebarStateService, VoidSidebarStateService, Instantiati
|
||||||
// Action: when press ctrl+L, show the sidebar chat and add to the selection
|
// Action: when press ctrl+L, show the sidebar chat and add to the selection
|
||||||
registerAction2(class extends Action2 {
|
registerAction2(class extends Action2 {
|
||||||
constructor() {
|
constructor() {
|
||||||
super({ id: 'void.ctrl+l', title: 'Show Sidebar', keybinding: { primary: KeyMod.CtrlCmd | KeyCode.KeyL, weight: KeybindingWeight.WorkbenchContrib } });
|
super({ id: 'void.ctrl+l', title: 'Show Sidebar', keybinding: { primary: KeyMod.CtrlCmd | KeyCode.KeyL, weight: KeybindingWeight.BuiltinExtension } });
|
||||||
}
|
}
|
||||||
async run(accessor: ServicesAccessor): Promise<void> {
|
async run(accessor: ServicesAccessor): Promise<void> {
|
||||||
|
|
||||||
|
const model = accessor.get(ICodeEditorService).getActiveCodeEditor()?.getModel()
|
||||||
|
if (!model)
|
||||||
|
return
|
||||||
|
|
||||||
|
|
||||||
const stateService = accessor.get(IVoidSidebarStateService)
|
const stateService = accessor.get(IVoidSidebarStateService)
|
||||||
stateService.setState({ isHistoryOpen: false, currentTab: 'chat' })
|
stateService.setState({ isHistoryOpen: false, currentTab: 'chat' })
|
||||||
stateService.fireFocusChat()
|
stateService.fireFocusChat()
|
||||||
|
|
||||||
// const selection = accessor.get(IEditorService).activeTextEditorControl?.getSelection()
|
// add selection
|
||||||
|
const threadHistoryService = accessor.get(IThreadHistoryService)
|
||||||
|
const currentStaging = threadHistoryService.state._currentStagingSelections
|
||||||
|
const currentStagingEltIdx = currentStaging?.findIndex(s => s.fileURI.fsPath === model.uri.fsPath)
|
||||||
|
|
||||||
|
// if there exists a selection with this URI, replace it
|
||||||
|
const selectionRange = accessor.get(IEditorService).activeTextEditorControl?.getSelection()
|
||||||
|
|
||||||
// chat state:
|
if (selectionRange) {
|
||||||
// // if user pressed ctrl+l, add their selection to the sidebar
|
const selection: CodeStagingSelection = { selectionStr: model.getValueInRange(selectionRange), fileURI: model.uri }
|
||||||
// useOnVSCodeMessage('ctrl+l', (m) => {
|
|
||||||
// setSelection(m.selection)
|
|
||||||
// const filepath = m.selection.filePath
|
|
||||||
|
|
||||||
// // add current file to the context if it's not already in the files array
|
|
||||||
// if (!files.find(f => f.fsPath === filepath.fsPath))
|
|
||||||
// setFiles(files => [...files, filepath])
|
|
||||||
// })
|
|
||||||
|
|
||||||
|
if (currentStagingEltIdx !== undefined && currentStagingEltIdx !== -1) {
|
||||||
|
threadHistoryService.setStaging([
|
||||||
|
...currentStaging!.slice(0, currentStagingEltIdx),
|
||||||
|
selection,
|
||||||
|
...currentStaging!.slice(currentStagingEltIdx + 1, Infinity)
|
||||||
|
])
|
||||||
|
}
|
||||||
|
else {
|
||||||
|
threadHistoryService.setStaging([...(currentStaging ?? []), selection])
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
}
|
}
|
||||||
});
|
});
|
||||||
|
|
|
||||||
|
|
@ -6,15 +6,24 @@ import { IStorageService, StorageScope, StorageTarget } from '../../../../platfo
|
||||||
import { URI } from '../../../../base/common/uri.js';
|
import { URI } from '../../../../base/common/uri.js';
|
||||||
import { Emitter, Event } from '../../../../base/common/event.js';
|
import { Emitter, Event } from '../../../../base/common/event.js';
|
||||||
|
|
||||||
export type CodeSelection = { selectionStr: string; filePath: URI }
|
// if selectionStr is null, it means just send the whole file
|
||||||
|
export type CodeSelection = {
|
||||||
|
selectionStr: string | null;
|
||||||
|
fileURI: URI,
|
||||||
|
content: string;
|
||||||
|
}
|
||||||
|
|
||||||
|
export type CodeStagingSelection = {
|
||||||
|
selectionStr: string | null;
|
||||||
|
fileURI: URI;
|
||||||
|
}
|
||||||
|
|
||||||
export type ChatMessage =
|
export type ChatMessage =
|
||||||
| {
|
| {
|
||||||
role: 'user';
|
role: 'user';
|
||||||
content: string; // content sent to the llm
|
content: string; // content sent to the llm
|
||||||
displayContent: string; // content displayed to user
|
displayContent: string; // content displayed to user
|
||||||
selection: CodeSelection | null; // the user's selection
|
selections: CodeSelection[] | null; // the user's selection
|
||||||
files: URI[]; // the files sent in the message
|
|
||||||
}
|
}
|
||||||
| {
|
| {
|
||||||
role: 'assistant';
|
role: 'assistant';
|
||||||
|
|
@ -40,6 +49,7 @@ export type ChatThreads = {
|
||||||
export type ThreadsState = {
|
export type ThreadsState = {
|
||||||
allThreads: ChatThreads;
|
allThreads: ChatThreads;
|
||||||
_currentThreadId: string | null; // intended for internal use only
|
_currentThreadId: string | null; // intended for internal use only
|
||||||
|
_currentStagingSelections: CodeStagingSelection[] | null;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
|
|
@ -64,8 +74,10 @@ export interface IThreadHistoryService {
|
||||||
getCurrentThread(state: ThreadsState): ChatThreads[string] | null;
|
getCurrentThread(state: ThreadsState): ChatThreads[string] | null;
|
||||||
startNewThread(): void;
|
startNewThread(): void;
|
||||||
switchToThread(threadId: string): void;
|
switchToThread(threadId: string): void;
|
||||||
startNewThread(): void;
|
|
||||||
addMessageToCurrentThread(message: ChatMessage): void;
|
addMessageToCurrentThread(message: ChatMessage): void;
|
||||||
|
|
||||||
|
setStaging(stagingSelection: CodeStagingSelection[] | null): void;
|
||||||
|
|
||||||
}
|
}
|
||||||
|
|
||||||
export const IThreadHistoryService = createDecorator<IThreadHistoryService>('voidThreadHistoryService');
|
export const IThreadHistoryService = createDecorator<IThreadHistoryService>('voidThreadHistoryService');
|
||||||
|
|
@ -84,8 +96,9 @@ class ThreadHistoryService extends Disposable implements IThreadHistoryService {
|
||||||
super()
|
super()
|
||||||
|
|
||||||
this.state = {
|
this.state = {
|
||||||
|
allThreads: this._readAllThreads(),
|
||||||
_currentThreadId: null,
|
_currentThreadId: null,
|
||||||
allThreads: this._readAllThreads()
|
_currentStagingSelections: null,
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
@ -105,7 +118,8 @@ class ThreadHistoryService extends Disposable implements IThreadHistoryService {
|
||||||
...this.state,
|
...this.state,
|
||||||
...state
|
...state
|
||||||
}
|
}
|
||||||
if (affectsCurrent) this._onDidChangeCurrentThread.fire()
|
if (affectsCurrent)
|
||||||
|
this._onDidChangeCurrentThread.fire()
|
||||||
}
|
}
|
||||||
|
|
||||||
// must "prove" that you have access to the current state by providing it
|
// must "prove" that you have access to the current state by providing it
|
||||||
|
|
@ -166,6 +180,11 @@ class ThreadHistoryService extends Disposable implements IThreadHistoryService {
|
||||||
this._setState({ allThreads: newThreads }, true) // the current thread just changed (it had a message added to it)
|
this._setState({ allThreads: newThreads }, true) // the current thread just changed (it had a message added to it)
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
|
setStaging(stagingSelection: CodeStagingSelection[] | null): void {
|
||||||
|
this._setState({ _currentStagingSelections: stagingSelection }, true) // this is a hack for now
|
||||||
|
}
|
||||||
|
|
||||||
}
|
}
|
||||||
|
|
||||||
registerSingleton(IThreadHistoryService, ThreadHistoryService, InstantiationType.Eager);
|
registerSingleton(IThreadHistoryService, ThreadHistoryService, InstantiationType.Eager);
|
||||||
|
|
|
||||||
Loading…
Reference in a new issue