mirror of
https://github.com/voideditor/void
synced 2026-05-24 09:58:23 +00:00
commandbar draft
This commit is contained in:
parent
47e93d1cf3
commit
3f7f2fb40c
4 changed files with 244 additions and 7 deletions
|
|
@ -177,7 +177,7 @@ type CtrlKZone = {
|
|||
} & CommonZoneProps
|
||||
|
||||
|
||||
type DiffZone = {
|
||||
export type DiffZone = {
|
||||
type: 'DiffZone',
|
||||
originalCode: string;
|
||||
_diffOfId: Record<string, Diff>; // diffid -> diff in this DiffArea
|
||||
|
|
@ -207,7 +207,7 @@ type TrackingZone<T> = {
|
|||
|
||||
|
||||
// called DiffArea for historical purposes, we can rename to something like TextRegion if we want
|
||||
type DiffArea = CtrlKZone | DiffZone | TrackingZone<any>
|
||||
export type DiffArea = CtrlKZone | DiffZone | TrackingZone<any>
|
||||
|
||||
const diffAreaSnapshotKeys = [
|
||||
'type',
|
||||
|
|
@ -240,16 +240,22 @@ class EditCodeService extends Disposable implements IEditCodeService {
|
|||
|
||||
|
||||
// URI <--> model
|
||||
diffAreasOfURI: Record<string, Set<string> | undefined> = {}
|
||||
diffAreasOfURI: Record<string, Set<string> | undefined> = {}; // uri -> diffareaId
|
||||
|
||||
diffAreaOfId: Record<string, DiffArea> = {};
|
||||
diffOfId: Record<string, Diff> = {}; // redundant with diffArea._diffs
|
||||
diffAreaOfId: Record<string, DiffArea> = {}; // diffareaId -> diffArea
|
||||
diffOfId: Record<string, Diff> = {}; // diffid -> diff (redundant with diffArea._diffOfId)
|
||||
|
||||
_sortedUrisWithDiffs: URI[] = [] // derivative of diffAreaOfId
|
||||
_sortedDiffsOfFspath: { [uriString: string]: Diff[] | undefined } = {} // derivative of diffAreaOfId
|
||||
|
||||
// only applies to diffZones
|
||||
// streamingDiffZones: Set<number> = new Set()
|
||||
private readonly _onDidChangeDiffZoneStreaming = new Emitter<{ uri: URI; diffareaid: number }>();
|
||||
private readonly _onDidAddOrDeleteDiffZones = new Emitter<{ uri: URI }>();
|
||||
onDidAddOrDeleteDiffZones = this._onDidAddOrDeleteDiffZones.event;
|
||||
|
||||
private readonly _onDidFinishAddOrDeleteDiffInDiffZone = new Emitter<{ uri: URI }>();
|
||||
onDidAddOrDeleteDiffInDiffZone = this._onDidFinishAddOrDeleteDiffInDiffZone.event;
|
||||
|
||||
private readonly _onDidChangeCtrlKZoneStreaming = new Emitter<{ uri: URI; diffareaid: number }>();
|
||||
onDidChangeCtrlKZoneStreaming = this._onDidChangeCtrlKZoneStreaming.event
|
||||
|
|
@ -344,6 +350,46 @@ class EditCodeService extends Disposable implements IEditCodeService {
|
|||
for (let editor of this._codeEditorService.listCodeEditors()) { initializeEditor(editor) }
|
||||
this._register(this._codeEditorService.onCodeEditorAdd(editor => { initializeEditor(editor) }))
|
||||
|
||||
|
||||
// update `_sortedUrisWithDiffs` and `_sortedDiffsOfUri` on all changes to diffzones
|
||||
this._register(this._onDidFinishAddOrDeleteDiffInDiffZone.event(({ uri }) => {
|
||||
|
||||
|
||||
// 1. Update _sortedUrisWithDiffs
|
||||
const hasDiffZones = Array.from(this.diffAreasOfURI[uri.fsPath] || [])
|
||||
.some(diffAreaId => this.diffAreaOfId[diffAreaId]?.type === 'DiffZone');
|
||||
|
||||
// Add or remove this URI from _sortedUrisWithDiffs
|
||||
const currentIndex = this._sortedUrisWithDiffs.findIndex(u => u.fsPath === uri.fsPath);
|
||||
if (hasDiffZones && currentIndex === -1) {
|
||||
// Add URI and maintain sort
|
||||
this._sortedUrisWithDiffs.push(uri);
|
||||
this._sortedUrisWithDiffs.sort((a, b) => a.fsPath.localeCompare(b.fsPath));
|
||||
} else if (!hasDiffZones && currentIndex !== -1) {
|
||||
// Remove URI
|
||||
this._sortedUrisWithDiffs.splice(currentIndex, 1);
|
||||
}
|
||||
|
||||
// 2. Update _sortedDiffsOfUri only for this URI
|
||||
const diffsInUri: Diff[] = [];
|
||||
|
||||
// Collect all diffs from DiffZones in this URI
|
||||
for (const diffAreaId of this.diffAreasOfURI[uri.fsPath] || []) {
|
||||
const diffArea = this.diffAreaOfId[diffAreaId];
|
||||
if (diffArea?.type === 'DiffZone') {
|
||||
diffsInUri.push(...Object.values(diffArea._diffOfId));
|
||||
}
|
||||
}
|
||||
|
||||
// Update or remove the entry for this URI
|
||||
if (diffsInUri.length > 0) {
|
||||
this._sortedDiffsOfFspath[uri.fsPath] = diffsInUri.sort((a, b) => a.startLine - b.startLine);
|
||||
console.log('_sortedDiffsOfFspath', this._sortedDiffsOfFspath)
|
||||
} else {
|
||||
delete this._sortedDiffsOfFspath[uri.fsPath];
|
||||
}
|
||||
}));
|
||||
|
||||
}
|
||||
|
||||
|
||||
|
|
@ -901,6 +947,10 @@ class EditCodeService extends Disposable implements IEditCodeService {
|
|||
if (diffArea.type !== 'DiffZone') return
|
||||
delete diffArea._diffOfId[diff.diffid]
|
||||
delete this.diffOfId[diff.diffid]
|
||||
|
||||
if (!diffArea._streamState.isStreaming) {
|
||||
this._onDidFinishAddOrDeleteDiffInDiffZone.fire({ uri: diffArea._URI })
|
||||
}
|
||||
}
|
||||
|
||||
private _deleteDiffs(diffZone: DiffZone) {
|
||||
|
|
@ -993,6 +1043,10 @@ class EditCodeService extends Disposable implements IEditCodeService {
|
|||
this.diffOfId[diffid] = newDiff
|
||||
diffZone._diffOfId[diffid] = newDiff
|
||||
|
||||
if (!diffZone._streamState.isStreaming) {
|
||||
this._onDidFinishAddOrDeleteDiffInDiffZone.fire({ uri })
|
||||
}
|
||||
|
||||
return newDiff
|
||||
}
|
||||
|
||||
|
|
|
|||
|
|
@ -7,6 +7,7 @@ import { Event } from '../../../../base/common/event.js';
|
|||
import { URI } from '../../../../base/common/uri.js';
|
||||
import { ICodeEditor } from '../../../../editor/browser/editorBrowser.js';
|
||||
import { createDecorator } from '../../../../platform/instantiation/common/instantiation.js';
|
||||
import { Diff, DiffArea } from './editCodeService.js';
|
||||
|
||||
|
||||
|
||||
|
|
@ -36,13 +37,24 @@ export const IEditCodeService = createDecorator<IEditCodeService>('editCodeServi
|
|||
|
||||
export interface IEditCodeService {
|
||||
readonly _serviceBrand: undefined;
|
||||
|
||||
// main entrypoints (initialize things for the functions below to be called):
|
||||
startApplying(opts: StartApplyingOpts): Promise<[URI, Promise<void>] | null>;
|
||||
_sortedUrisWithDiffs: URI[];
|
||||
_sortedDiffsOfFspath: { [fsPath: string]: Diff[] | undefined };
|
||||
|
||||
diffAreaOfId: Record<string, DiffArea>;
|
||||
diffOfId: Record<string, Diff>;
|
||||
|
||||
|
||||
addCtrlKZone(opts: AddCtrlKOpts): number | undefined;
|
||||
|
||||
removeCtrlKZone(opts: { diffareaid: number }): void;
|
||||
removeDiffAreas(opts: { uri: URI, removeCtrlKs: boolean, behavior: 'reject' | 'accept' }): void;
|
||||
|
||||
onDidAddOrDeleteDiffZones: Event<{ uri: URI }>;
|
||||
onDidAddOrDeleteDiffInDiffZone: Event<{ uri: URI }>;
|
||||
|
||||
// CtrlKZone streaming state
|
||||
isCtrlKZoneStreaming(opts: { diffareaid: number }): boolean;
|
||||
interruptCtrlKStreaming(opts: { diffareaid: number }): void;
|
||||
|
|
|
|||
|
|
@ -41,7 +41,7 @@ const CodespanWithLink = ({ text, rawText, chatMessageLocation }: { text: string
|
|||
const accessor = useAccessor()
|
||||
|
||||
const chatThreadService = accessor.get('IChatThreadService')
|
||||
const commandSerivce = accessor.get('ICommandService')
|
||||
const commandService = accessor.get('ICommandService')
|
||||
const editorService = accessor.get('ICodeEditorService')
|
||||
|
||||
const { messageIdx, threadId } = chatMessageLocation
|
||||
|
|
@ -73,7 +73,7 @@ const CodespanWithLink = ({ text, rawText, chatMessageLocation }: { text: string
|
|||
const selection = link.selection
|
||||
|
||||
// open the file
|
||||
commandSerivce.executeCommand('vscode.open', link.uri).then(() => {
|
||||
commandService.executeCommand('vscode.open', link.uri).then(() => {
|
||||
|
||||
// select the text
|
||||
setTimeout(() => {
|
||||
|
|
|
|||
|
|
@ -32,6 +32,8 @@ import { ResolveReason, ToolCallParams, ToolName, ToolNameWithApproval } from '.
|
|||
import { getLanguageFromModel } from '../../../../common/helpers/getLanguage.js';
|
||||
import { dirname } from '../../../../../../../base/common/resources.js';
|
||||
import { useApplyButtonHTML } from '../markdown/ApplyBlockHoverButtons.js';
|
||||
import { DiffZone } from '../../../editCodeService.js';
|
||||
import { ScrollType } from '../../../../../../../editor/common/editorCommon.js';
|
||||
|
||||
|
||||
|
||||
|
|
@ -1609,6 +1611,173 @@ const ChatBubble = ({ chatMessage, isLoading, messageIdx, isLast }: ChatBubblePr
|
|||
}
|
||||
|
||||
|
||||
const VoidCommandBar = () => {
|
||||
const accessor = useAccessor()
|
||||
const editCodeService = accessor.get('IEditCodeService')
|
||||
const editorService = accessor.get('ICodeEditorService')
|
||||
const commandService = accessor.get('ICommandService')
|
||||
|
||||
const [_, rerender] = useState(0)
|
||||
console.log('rerender count: ', _)
|
||||
|
||||
// state for what the user is currently focused on (both URI and diff)
|
||||
const [diffIdxOfFspath, setDiffIdxOfFspath] = useState<Record<string, number | undefined>>({})
|
||||
// const [currentUriIdx, setCurrentUriIdx] = useState(-1) // we are doing O(n) search for this
|
||||
|
||||
const getCurrentUri = useCallback(() => {
|
||||
const editor = editorService.getActiveCodeEditor()
|
||||
if (!editor) return null
|
||||
const uri = editor.getModel()?.uri
|
||||
if (!uri) return null
|
||||
return uri
|
||||
}, [editorService])
|
||||
|
||||
const diffZones: DiffZone[] = []
|
||||
|
||||
|
||||
// trigger rerender when diffzone is created (TODO need to also update when diff is accepted/rejected)
|
||||
useEffect(() => {
|
||||
const disposable = editCodeService.onDidAddOrDeleteDiffInDiffZone(() => {
|
||||
rerender(c => c + 1) // rerender
|
||||
})
|
||||
return () => disposable.dispose()
|
||||
}, [editCodeService, rerender])
|
||||
|
||||
|
||||
const getNextDiff = useCallback(({ step }: { step: 1 | -1 }) => {
|
||||
|
||||
const currentUri = getCurrentUri()
|
||||
|
||||
if (!currentUri) {
|
||||
return;
|
||||
}
|
||||
|
||||
const sortedDiffs = editCodeService._sortedDiffsOfFspath[currentUri.fsPath]
|
||||
|
||||
if (!sortedDiffs || sortedDiffs.length === 0) {
|
||||
return;
|
||||
}
|
||||
|
||||
const currentDiffIdx = diffIdxOfFspath[currentUri.fsPath] || 0
|
||||
const nextDiffIdx = (currentDiffIdx + step) % sortedDiffs.length
|
||||
|
||||
const nextDiff = sortedDiffs[nextDiffIdx]
|
||||
|
||||
return { nextDiff, nextDiffIdx, }
|
||||
|
||||
}, [getCurrentUri, editCodeService._sortedDiffsOfFspath, diffIdxOfFspath])
|
||||
|
||||
const getNextUri = useCallback(({ step }: { step: 1 | -1 }) => {
|
||||
|
||||
const sortedUris = editCodeService._sortedUrisWithDiffs
|
||||
if (sortedUris.length === 0) {
|
||||
return;
|
||||
}
|
||||
|
||||
const currentUri = getCurrentUri()
|
||||
|
||||
const defaultUriIdx = step === 1 ? -1 : 0 // defaults: if next, currentIdx = -1; if prev, currentIdx = 0
|
||||
let currentUriIdx = -1
|
||||
if (currentUri) {
|
||||
currentUriIdx = sortedUris.findIndex(u => u.fsPath === currentUri.fsPath)
|
||||
}
|
||||
|
||||
if (currentUriIdx === -1) { // not found
|
||||
currentUriIdx = defaultUriIdx // set to default
|
||||
}
|
||||
|
||||
const nextUriIdx = (currentUriIdx + step) % sortedUris.length
|
||||
const nextUri = sortedUris[nextUriIdx]
|
||||
|
||||
return { nextUri, nextUriIdx, }
|
||||
|
||||
}, [getCurrentUri, editCodeService._sortedUrisWithDiffs])
|
||||
|
||||
|
||||
|
||||
return <div className='bg-red-500 min-h-4 min-w-4 flex gap-4'>
|
||||
|
||||
<button
|
||||
className='bg-[var(--vscode-button-secondaryBackground)] text-[var(--vscode-button-secondaryForeground)] hover:bg-[var(--vscode-button-secondaryHoverBackground)] px-4 py-1.5 rounded text-sm font-medium'
|
||||
onClick={() => {
|
||||
rerender(c => c + 1)
|
||||
}}
|
||||
>
|
||||
rerender
|
||||
</button>
|
||||
|
||||
<button
|
||||
onClick={() => {
|
||||
|
||||
// get the next diff
|
||||
const res = getNextDiff({ step: 1 })
|
||||
if (!res) return;
|
||||
|
||||
// scroll to the next diff
|
||||
const { nextDiff, nextDiffIdx } = res;
|
||||
const editor = editorService.getActiveCodeEditor()
|
||||
if (!editor) return;
|
||||
|
||||
const range = { startLineNumber: nextDiff.startLine, endLineNumber: nextDiff.startLine, startColumn: 1, endColumn: 1 };
|
||||
editor.revealRange(range, ScrollType.Immediate)
|
||||
|
||||
// update state
|
||||
const diffArea = editCodeService.diffAreaOfId[nextDiff.diffareaid]
|
||||
setDiffIdxOfFspath(v => ({ ...v, [diffArea._URI.fsPath]: nextDiffIdx }))
|
||||
|
||||
}}
|
||||
>
|
||||
next diff:
|
||||
</button>
|
||||
<button
|
||||
onClick={() => {
|
||||
|
||||
// get the next uri
|
||||
const res = getNextUri({ step: 1 })
|
||||
if (!res) return;
|
||||
|
||||
const { nextUri, nextUriIdx } = res;
|
||||
|
||||
// open the uri and scroll to diff
|
||||
const sortedDiffs = editCodeService._sortedDiffsOfFspath[nextUri.fsPath]
|
||||
if (!sortedDiffs) return;
|
||||
|
||||
const diffIdx = diffIdxOfFspath[nextUri.fsPath] || 0
|
||||
const diff = sortedDiffs[diffIdx]
|
||||
|
||||
const range = { startLineNumber: diff.startLine, endLineNumber: diff.startLine, startColumn: 1, endColumn: 1 };
|
||||
|
||||
commandService.executeCommand('vscode.open', nextUri).then(() => {
|
||||
|
||||
// select the text
|
||||
setTimeout(() => {
|
||||
|
||||
const editor = editorService.getActiveCodeEditor()
|
||||
if (!editor) return;
|
||||
|
||||
editor.revealRange(range, ScrollType.Immediate)
|
||||
|
||||
}, 50)
|
||||
|
||||
})
|
||||
}}
|
||||
>
|
||||
next diff area
|
||||
</button>
|
||||
<div>
|
||||
<div className='gap-2 text-[var(--vscode-editor-foreground)] flex'>
|
||||
<div>numUris: {Object.keys(editCodeService._sortedDiffsOfFspath).length}</div>
|
||||
<div>numDiffs: {Object.values(editCodeService._sortedDiffsOfFspath).reduce((acc, diffs) => acc + (diffs?.length || 0), 0)}</div>
|
||||
</div>
|
||||
</div>
|
||||
{diffZones.map((area, index) => (
|
||||
<>
|
||||
<div key={index} className='bg-red-500 p-2 rounded-lg m-2 text-white'>{getBasename(area?._URI?.toString())}</div>
|
||||
</>
|
||||
))}
|
||||
</div>
|
||||
}
|
||||
|
||||
export const SidebarChat = () => {
|
||||
|
||||
const textAreaRef = useRef<HTMLTextAreaElement | null>(null)
|
||||
|
|
@ -1805,6 +1974,8 @@ export const SidebarChat = () => {
|
|||
fnsRef={textAreaFnsRef}
|
||||
multiline={true}
|
||||
/>
|
||||
<VoidCommandBar />
|
||||
|
||||
</VoidChatArea>
|
||||
</div>
|
||||
|
||||
|
|
|
|||
Loading…
Reference in a new issue