mirror of
https://github.com/voideditor/void
synced 2026-05-24 09:58:23 +00:00
seems to work! some annoying react state stuff..
This commit is contained in:
parent
63b71dec24
commit
19ebf4a1a2
5 changed files with 59 additions and 55 deletions
|
|
@ -17,6 +17,7 @@ import { InternalToolInfo, IToolsService, ToolCallReturnType, ToolFns, ToolName,
|
|||
import { toLLMChatMessage } from '../common/llmMessageTypes.js';
|
||||
import { IWorkspaceContextService } from '../../../../platform/workspace/common/workspace.js';
|
||||
import { IVoidFileService } from '../common/voidFileService.js';
|
||||
import { generateUuid } from '../../../../base/common/uuid.js';
|
||||
|
||||
|
||||
const findLastIndex = <T>(arr: T[], condition: (t: T) => boolean): number => {
|
||||
|
|
@ -123,7 +124,7 @@ export type ThreadStreamState = {
|
|||
const newThreadObject = () => {
|
||||
const now = new Date().toISOString()
|
||||
return {
|
||||
id: new Date().getTime().toString(),
|
||||
id: generateUuid(),
|
||||
createdAt: now,
|
||||
lastModified: now,
|
||||
messages: [],
|
||||
|
|
|
|||
|
|
@ -338,23 +338,24 @@ class EditCodeService extends Disposable implements IEditCodeService {
|
|||
let prevStreamState = this.getURIStreamState({ uri: model.uri })
|
||||
const updateAcceptRejectAllUI = () => {
|
||||
const state = this.getURIStreamState({ uri: model.uri })
|
||||
if (prevStreamState === state) return
|
||||
let prevStateActual = prevStreamState
|
||||
prevStreamState = state
|
||||
if (state === prevStateActual) return
|
||||
this._onDidChangeURIStreamState.fire({ uri: model.uri, state })
|
||||
}
|
||||
|
||||
// add/remove the accept|reject UI
|
||||
|
||||
let _removeAcceptRejectAllUI: (() => void) | null = null
|
||||
this._register(this._onDidChangeURIStreamState.event(({ uri: uri_ }) => {
|
||||
if (uri_.fsPath !== model.uri.fsPath) return
|
||||
const state = this.getURIStreamState({ uri: model.uri })
|
||||
if (state === 'acceptRejectAll' && !_removeAcceptRejectAllUI) {
|
||||
_removeAcceptRejectAllUI = this._addAcceptRejectAllUI(model.uri) ?? null
|
||||
this._register(this._onDidChangeURIStreamState.event(({ uri, state }) => {
|
||||
if (uri.fsPath !== model.uri.fsPath) return
|
||||
if (state === 'acceptRejectAll') {
|
||||
if (!_removeAcceptRejectAllUI)
|
||||
_removeAcceptRejectAllUI = this._addAcceptRejectAllUI(model.uri) ?? null
|
||||
} else {
|
||||
_removeAcceptRejectAllUI?.()
|
||||
_removeAcceptRejectAllUI = null
|
||||
}
|
||||
}))
|
||||
|
||||
this._register(this._onDidChangeDiffZoneStreaming.event(({ uri: uri_ }) => { if (uri_.fsPath === model.uri.fsPath) updateAcceptRejectAllUI() }))
|
||||
this._register(this._onDidAddOrDeleteDiffZones.event(({ uri: uri_ }) => { if (uri_.fsPath === model.uri.fsPath) updateAcceptRejectAllUI() }))
|
||||
|
||||
|
|
@ -1666,8 +1667,10 @@ class EditCodeService extends Disposable implements IEditCodeService {
|
|||
this._writeStreamedDiffZoneLLMText(uri, block.orig, block.final, deltaFinalText, latestStreamLocationMutable)
|
||||
oldBlocks = blocks // oldblocks is only used if writingFinal
|
||||
|
||||
const { endLine: currentEndLine } = addedTrackingZoneOfBlockNum[blockNum]
|
||||
diffZone._streamState.line = currentEndLine
|
||||
// const { endLine: currentEndLine } = addedTrackingZoneOfBlockNum[blockNum] // would be bad to do this because a lot of the bottom lines might be the same. more accurate to go with latestStreamLocationMutable
|
||||
// diffZone._streamState.line = currentEndLine
|
||||
diffZone._streamState.line = latestStreamLocationMutable.line
|
||||
|
||||
|
||||
|
||||
} // end for
|
||||
|
|
@ -1682,7 +1685,7 @@ class EditCodeService extends Disposable implements IEditCodeService {
|
|||
const blocks = extractSearchReplaceBlocks(fullText)
|
||||
|
||||
if (blocks.length === 0) {
|
||||
this._notificationService.info(`Void: When running Apply, your model didn't output any changes that Void recognized. You might need to use a smarter model for Apply.`)
|
||||
this._notificationService.info(`Void: We ran Apply, but the LLM didn't output any changes.`)
|
||||
}
|
||||
|
||||
// writeover the whole file
|
||||
|
|
|
|||
|
|
@ -201,7 +201,7 @@ export const extractSearchReplaceBlocks = (str: string) => {
|
|||
|
||||
const ORIGINAL_ = ORIGINAL + `\n`
|
||||
const DIVIDER_ = '\n' + DIVIDER + `\n`
|
||||
const FINAL_ = '\n' + FINAL
|
||||
// logic for FINAL_ is slightly more complicated - should be '\n' + FINAL, but that ignores if the final output is empty
|
||||
|
||||
|
||||
const blocks: ExtractedSearchReplaceBlock[] = []
|
||||
|
|
@ -229,7 +229,13 @@ export const extractSearchReplaceBlocks = (str: string) => {
|
|||
i = dividerStart
|
||||
// wrote =====
|
||||
|
||||
let finalStart = str.indexOf(FINAL_, i)
|
||||
|
||||
|
||||
const finalStartA = str.indexOf(FINAL, i)
|
||||
const finalStartB = str.indexOf('\n' + FINAL, i) // go with B if possible, else fallback to A, it's more permissive
|
||||
const FINAL_ = finalStartB !== -1 ? '\n' + FINAL : FINAL
|
||||
let finalStart = finalStartB !== -1 ? finalStartB : finalStartA
|
||||
|
||||
if (finalStart === -1) { // if didnt find FINAL_, either writing finalStr or FINAL_ right now
|
||||
const isWritingFINAL = endsWithAnyPrefixOf(str, FINAL_)
|
||||
blocks.push({
|
||||
|
|
|
|||
|
|
@ -32,7 +32,7 @@ const CopyButton = ({ codeStr }: { codeStr: string }) => {
|
|||
.then(() => { setCopyButtonText(CopyButtonText.Copied) })
|
||||
.catch(() => { setCopyButtonText(CopyButtonText.Error) })
|
||||
metricsService.capture('Copy Code', { length: codeStr.length }) // capture the length only
|
||||
}, [metricsService, clipboardService, codeStr])
|
||||
}, [metricsService, clipboardService, codeStr, setCopyButtonText])
|
||||
|
||||
const isSingleLine = !codeStr.includes('\n')
|
||||
|
||||
|
|
@ -49,67 +49,60 @@ const CopyButton = ({ codeStr }: { codeStr: string }) => {
|
|||
|
||||
|
||||
// state persisted for duration of react only
|
||||
const streamingURIOfApplyBoxIdRef: { current: { [applyBoxId: string]: URI | undefined } } = { current: {} }
|
||||
const useStreamingURIOfApplyBoxId = (applyBoxId: string | null) => {
|
||||
const [_, ss] = useState(0)
|
||||
const uri = applyBoxId === null ? null : streamingURIOfApplyBoxIdRef.current[applyBoxId]
|
||||
const setUri = useCallback((uri: URI | null) => {
|
||||
if (applyBoxId === null) return
|
||||
ss(c => c + 1)
|
||||
if (uri === null) {
|
||||
delete streamingURIOfApplyBoxIdRef.current[applyBoxId]
|
||||
}
|
||||
else {
|
||||
streamingURIOfApplyBoxIdRef.current = {
|
||||
...streamingURIOfApplyBoxIdRef.current,
|
||||
[applyBoxId]: uri,
|
||||
}
|
||||
}
|
||||
}, [applyBoxId])
|
||||
return [uri, setUri] as const
|
||||
}
|
||||
const applyingURIOfApplyBoxIdRef: { current: { [applyBoxId: string]: URI | undefined } } = { current: {} }
|
||||
|
||||
|
||||
export const ApplyBlockHoverButtons = ({ codeStr, applyBoxId }: { codeStr: string, applyBoxId: string | null }) => {
|
||||
export const ApplyBlockHoverButtons = ({ codeStr, applyBoxId }: { codeStr: string, applyBoxId: string }) => {
|
||||
|
||||
console.log('applyboxid', applyBoxId, applyingURIOfApplyBoxIdRef)
|
||||
|
||||
const settingsState = useSettingsState()
|
||||
|
||||
const isDisabled = !!isFeatureNameDisabled('Apply', settingsState) || applyBoxId === null
|
||||
const isDisabled = !!isFeatureNameDisabled('Apply', settingsState) || !applyBoxId
|
||||
|
||||
const accessor = useAccessor()
|
||||
const editCodeService = accessor.get('IEditCodeService')
|
||||
const metricsService = accessor.get('IMetricsService')
|
||||
|
||||
// get streaming URI of this applyBlockId (cached in react)
|
||||
const [appliedURI, setAppliedURI] = useStreamingURIOfApplyBoxId(applyBoxId)
|
||||
const [applyingUriRef, setApplyingUri_] = useRefState(applyingURIOfApplyBoxIdRef.current[applyBoxId] ?? null)
|
||||
const [streamStateRef, setStreamState_] = useRefState(editCodeService.getURIStreamState({ uri: applyingUriRef.current ?? null }))
|
||||
|
||||
// get stream state of this URI
|
||||
const [streamStateRef, setStreamState] = useRefState(editCodeService.getURIStreamState({ uri: appliedURI ?? null }))
|
||||
useURIStreamState(useCallback((uri, streamState) => {
|
||||
if (appliedURI?.fsPath !== uri.fsPath) return
|
||||
setStreamState(streamState)
|
||||
}, [appliedURI, setStreamState]))
|
||||
const setApplyingUri = useCallback((uri: URI | null) => { // switched the box's URI to whatever they clicked on most recently
|
||||
setApplyingUri_(uri)
|
||||
const newStreamState = editCodeService.getURIStreamState({ uri })
|
||||
if (uri) applyingURIOfApplyBoxIdRef.current[applyBoxId] = uri
|
||||
setStreamState_(newStreamState)
|
||||
}, [applyBoxId, setApplyingUri_, editCodeService, setStreamState_])
|
||||
|
||||
// listen for stream updates
|
||||
useURIStreamState(
|
||||
useCallback((uri, streamState) => {
|
||||
const shouldUpdate = applyingUriRef.current?.fsPath === uri.fsPath
|
||||
if (!shouldUpdate) return
|
||||
setStreamState_(streamState) // editCodeService.getURIStreamState({ uri: applyingUriRef.current ?? null })
|
||||
}, [applyingUriRef, setStreamState_])
|
||||
)
|
||||
|
||||
const onSubmit = useCallback(() => {
|
||||
if (isDisabled) return
|
||||
if (streamStateRef.current === 'streaming') return
|
||||
const uri = editCodeService.startApplying({
|
||||
from: 'ClickApply',
|
||||
type: 'searchReplace',
|
||||
applyStr: codeStr,
|
||||
chatApplyBoxId: applyBoxId,
|
||||
})
|
||||
setAppliedURI(uri)
|
||||
setApplyingUri(uri)
|
||||
metricsService.capture('Apply Code', { length: codeStr.length }) // capture the length only
|
||||
}, [streamStateRef, setAppliedURI, editCodeService, applyBoxId, codeStr, metricsService])
|
||||
}, [editCodeService, applyBoxId, codeStr, metricsService, isDisabled, streamStateRef, setApplyingUri])
|
||||
|
||||
|
||||
const onInterrupt = useCallback(() => {
|
||||
if (!appliedURI) return
|
||||
editCodeService.interruptURIStreaming({ uri: appliedURI, })
|
||||
if (streamStateRef.current !== 'streaming') return
|
||||
if (!applyingUriRef.current) return
|
||||
|
||||
editCodeService.interruptURIStreaming({ uri: applyingUriRef.current, })
|
||||
metricsService.capture('Stop Apply', {})
|
||||
}, [streamStateRef, editCodeService, appliedURI, metricsService])
|
||||
}, [editCodeService, metricsService, streamStateRef, applyingUriRef])
|
||||
|
||||
|
||||
const isSingleLine = !codeStr.includes('\n')
|
||||
|
|
@ -135,8 +128,8 @@ export const ApplyBlockHoverButtons = ({ codeStr, applyBoxId }: { codeStr: strin
|
|||
// btn btn-secondary btn-sm border text-sm border-vscode-input-border rounded
|
||||
className={`${isSingleLine ? '' : 'px-1 py-0.5'} text-sm bg-void-bg-1 text-void-fg-1 hover:brightness-110 border border-vscode-input-border rounded`}
|
||||
onClick={() => {
|
||||
if (!appliedURI) return
|
||||
editCodeService.removeDiffAreas({ uri: appliedURI, behavior: 'accept', removeCtrlKs: false })
|
||||
if (!applyingUriRef.current) return
|
||||
editCodeService.removeDiffAreas({ uri: applyingUriRef.current, behavior: 'accept', removeCtrlKs: false })
|
||||
}}
|
||||
>
|
||||
Accept
|
||||
|
|
@ -145,14 +138,15 @@ export const ApplyBlockHoverButtons = ({ codeStr, applyBoxId }: { codeStr: strin
|
|||
// btn btn-secondary btn-sm border text-sm border-vscode-input-border rounded
|
||||
className={`${isSingleLine ? '' : 'px-1 py-0.5'} text-sm bg-void-bg-1 text-void-fg-1 hover:brightness-110 border border-vscode-input-border rounded`}
|
||||
onClick={() => {
|
||||
if (!appliedURI) return
|
||||
editCodeService.removeDiffAreas({ uri: appliedURI, behavior: 'reject', removeCtrlKs: false })
|
||||
if (!applyingUriRef.current) return
|
||||
editCodeService.removeDiffAreas({ uri: applyingUriRef.current, behavior: 'reject', removeCtrlKs: false })
|
||||
}}
|
||||
>
|
||||
Reject
|
||||
</button>
|
||||
</>
|
||||
|
||||
console.log('streamStateRef.current', streamStateRef.current)
|
||||
|
||||
return <>
|
||||
{streamStateRef.current !== 'streaming' && <CopyButton codeStr={codeStr} />}
|
||||
|
|
|
|||
|
|
@ -55,7 +55,7 @@ const RenderToken = ({ token, nested, noSpace, chatMessageLocation, tokenIdx }:
|
|||
return <BlockCode
|
||||
initValue={t.text}
|
||||
language={t.lang === undefined ? undefined : nameToVscodeLanguage[t.lang]}
|
||||
buttonsOnHover={<ApplyBlockHoverButtons applyBoxId={applyBoxId} codeStr={t.text} />}
|
||||
buttonsOnHover={applyBoxId && <ApplyBlockHoverButtons applyBoxId={applyBoxId} codeStr={t.text} />}
|
||||
/>
|
||||
}
|
||||
|
||||
|
|
|
|||
Loading…
Reference in a new issue