mirror of
https://github.com/voideditor/void
synced 2026-05-24 01:48:25 +00:00
add accept|reject on edit tool!
This commit is contained in:
parent
42391a33c8
commit
d00c478c5b
7 changed files with 179 additions and 81 deletions
|
|
@ -1124,8 +1124,8 @@ class EditCodeService extends Disposable implements IEditCodeService {
|
|||
return
|
||||
}
|
||||
|
||||
public async callBeforeStartApplying(opts: CallBeforeStartApplyingOpts) {
|
||||
const uri = this._getURIBeforeStartApplying(opts)
|
||||
public async callBeforeApplyOrEdit(givenURI: URI | 'current') {
|
||||
const uri = this._uriOfGivenURI(givenURI)
|
||||
if (!uri) return
|
||||
await this._voidModelService.initializeModel(uri)
|
||||
await this._voidModelService.saveModel(uri) // save the URI
|
||||
|
|
@ -1200,7 +1200,7 @@ class EditCodeService extends Disposable implements IEditCodeService {
|
|||
}
|
||||
|
||||
|
||||
public instantlyApplyNewContent({ uri, newContent }: { uri: URI, newContent: string }) {
|
||||
public instantlyRewriteFile({ uri, newContent }: { uri: URI, newContent: string }) {
|
||||
// start diffzone
|
||||
const res = this._startStreamingDiffZone({
|
||||
uri,
|
||||
|
|
|
|||
|
|
@ -44,10 +44,10 @@ export interface IEditCodeService {
|
|||
|
||||
processRawKeybindingText(keybindingStr: string): string;
|
||||
|
||||
callBeforeStartApplying(opts: CallBeforeStartApplyingOpts): Promise<void>;
|
||||
callBeforeApplyOrEdit(uri: URI | 'current'): Promise<void>;
|
||||
startApplying(opts: StartApplyingOpts): [URI, Promise<void>] | null;
|
||||
instantlyApplySearchReplaceBlocks(opts: { uri: URI; searchReplaceBlocks: string }): void;
|
||||
instantlyApplyNewContent(opts: { uri: URI; newContent: string }): void;
|
||||
instantlyRewriteFile(opts: { uri: URI; newContent: string }): void;
|
||||
addCtrlKZone(opts: AddCtrlKOpts): number | undefined;
|
||||
removeCtrlKZone(opts: { diffareaid: number }): void;
|
||||
|
||||
|
|
|
|||
|
|
@ -3,7 +3,7 @@
|
|||
* Licensed under the Apache License, Version 2.0. See LICENSE.txt for more information.
|
||||
*--------------------------------------------------------------------------------------*/
|
||||
|
||||
import { useState, useEffect, useCallback } from 'react'
|
||||
import { useState, useEffect, useCallback, useRef, Fragment } from 'react'
|
||||
import { useAccessor, useCommandBarState, useCommandBarURIListener, useSettingsState } from '../util/services.js'
|
||||
import { usePromise, useRefState } from '../util/helpers.js'
|
||||
import { isFeatureNameDisabled } from '../../../../common/voidSettingsTypes.js'
|
||||
|
|
@ -24,8 +24,9 @@ type IconButtonProps = {
|
|||
Icon: LucideIcon
|
||||
}
|
||||
|
||||
export const IconShell1 = ({ onClick, Icon, disabled, className, ...props }: IconButtonProps & React.ButtonHTMLAttributes<HTMLButtonElement>) => (
|
||||
<button
|
||||
export const IconShell1 = ({ onClick, Icon, disabled, className, ...props }: IconButtonProps & React.ButtonHTMLAttributes<HTMLButtonElement>) => {
|
||||
|
||||
return <button
|
||||
disabled={disabled}
|
||||
onClick={(e) => {
|
||||
e.preventDefault();
|
||||
|
|
@ -34,19 +35,19 @@ export const IconShell1 = ({ onClick, Icon, disabled, className, ...props }: Ico
|
|||
}}
|
||||
// border border-void-border-1 rounded
|
||||
className={`
|
||||
size-[18px]
|
||||
p-[2px]
|
||||
flex items-center justify-center
|
||||
text-sm text-void-fg-3
|
||||
hover:brightness-110
|
||||
disabled:opacity-50 disabled:cursor-not-allowed
|
||||
${className}
|
||||
size-[18px]
|
||||
p-[2px]
|
||||
flex items-center justify-center
|
||||
text-sm text-void-fg-3
|
||||
hover:brightness-110
|
||||
disabled:opacity-50 disabled:cursor-not-allowed
|
||||
${className}
|
||||
`}
|
||||
{...props}
|
||||
>
|
||||
<Icon />
|
||||
</button>
|
||||
)
|
||||
}
|
||||
|
||||
|
||||
// export const IconShell2 = ({ onClick, title, Icon, disabled, className }: IconButtonProps) => (
|
||||
|
|
@ -131,48 +132,41 @@ export const JumpToTerminalButton = ({ onClick }: { onClick: () => void }) => {
|
|||
|
||||
// state persisted for duration of react only
|
||||
// TODO change this to use type `ChatThreads.applyBoxState[applyBoxId]`
|
||||
const applyingURIOfApplyBoxIdRef: { current: { [applyBoxId: string]: URI | undefined } } = { current: {} }
|
||||
const _applyingURIOfApplyBoxIdRef: { current: { [applyBoxId: string]: URI | undefined } } = { current: {} }
|
||||
|
||||
const getUriBeingApplied = (applyBoxId: string) => {
|
||||
return applyingURIOfApplyBoxIdRef.current[applyBoxId] ?? null
|
||||
return _applyingURIOfApplyBoxIdRef.current[applyBoxId] ?? null
|
||||
}
|
||||
|
||||
|
||||
export const useApplyButtonState = ({ applyBoxId, uri }: { applyBoxId: string, uri: URI | 'current' }) => {
|
||||
|
||||
const settingsState = useSettingsState()
|
||||
const isDisabled = !!isFeatureNameDisabled('Apply', settingsState) || !applyBoxId
|
||||
|
||||
export const useApplyStreamState = ({ applyBoxId }: { applyBoxId: string }) => {
|
||||
const accessor = useAccessor()
|
||||
const voidCommandBarService = accessor.get('IVoidCommandBarService')
|
||||
|
||||
const [_, rerender] = useState(0)
|
||||
|
||||
const getStreamState = useCallback(() => {
|
||||
const uri = getUriBeingApplied(applyBoxId)
|
||||
if (!uri) return 'idle-no-changes'
|
||||
return voidCommandBarService.getStreamState(uri)
|
||||
}, [voidCommandBarService, applyBoxId])
|
||||
|
||||
|
||||
const [currStreamStateRef, setStreamState] = useRefState(getStreamState())
|
||||
|
||||
const setApplying = useCallback((uri: URI | undefined) => {
|
||||
_applyingURIOfApplyBoxIdRef.current[applyBoxId] = uri ?? undefined
|
||||
setStreamState(getStreamState())
|
||||
}, [setStreamState, getStreamState, applyBoxId])
|
||||
|
||||
// listen for stream updates on this box
|
||||
useCommandBarURIListener(useCallback((uri_) => {
|
||||
const shouldUpdate = (
|
||||
getUriBeingApplied(applyBoxId)?.fsPath === uri_.fsPath
|
||||
|| (uri !== 'current' && uri.fsPath === uri_.fsPath)
|
||||
)
|
||||
if (shouldUpdate) {
|
||||
rerender(c => c + 1)
|
||||
const uri = getUriBeingApplied(applyBoxId)
|
||||
if (uri?.fsPath === uri_.fsPath) {
|
||||
setStreamState(getStreamState())
|
||||
}
|
||||
}, [applyBoxId, uri]))
|
||||
|
||||
const currStreamState = getStreamState()
|
||||
}, [setStreamState, applyBoxId, getStreamState]))
|
||||
|
||||
|
||||
return {
|
||||
getStreamState,
|
||||
isDisabled,
|
||||
currStreamState,
|
||||
}
|
||||
return { currStreamStateRef, setApplying }
|
||||
}
|
||||
|
||||
|
||||
|
|
@ -202,10 +196,24 @@ const tooltipPropsForApplyBlock = ({ tooltipName, color = undefined, position =
|
|||
'data-tooltip-offset': offset,
|
||||
})
|
||||
|
||||
export const useEditToolStreamState = ({ applyBoxId, uri }: { applyBoxId: string, uri: URI }) => {
|
||||
const accessor = useAccessor()
|
||||
const voidCommandBarService = accessor.get('IVoidCommandBarService')
|
||||
const [streamState, setStreamState] = useState(voidCommandBarService.getStreamState(uri))
|
||||
// listen for stream updates on this box
|
||||
useCommandBarURIListener(useCallback((uri_) => {
|
||||
const shouldUpdate = uri.fsPath === uri_.fsPath
|
||||
if (shouldUpdate) { setStreamState(voidCommandBarService.getStreamState(uri)) }
|
||||
}, [voidCommandBarService, applyBoxId, uri]))
|
||||
|
||||
return { streamState, }
|
||||
}
|
||||
|
||||
export const StatusIndicatorForApplyButton = ({ applyBoxId, uri }: { applyBoxId: string, uri: URI | 'current' } & React.HTMLAttributes<HTMLDivElement>) => {
|
||||
|
||||
const { currStreamState } = useApplyButtonState({ applyBoxId, uri })
|
||||
const { currStreamStateRef } = useApplyStreamState({ applyBoxId })
|
||||
const currStreamState = currStreamStateRef.current
|
||||
|
||||
|
||||
const color = (
|
||||
currStreamState === 'idle-no-changes' ? 'dark' :
|
||||
|
|
@ -231,74 +239,88 @@ export const StatusIndicatorForApplyButton = ({ applyBoxId, uri }: { applyBoxId:
|
|||
}
|
||||
|
||||
|
||||
export const ApplyButtonsHTML = ({ codeStr, applyBoxId, uri }: { codeStr: string, applyBoxId: string, uri: URI | 'current' }) => {
|
||||
export const ApplyButtonsHTML = ({
|
||||
codeStr,
|
||||
applyBoxId,
|
||||
uri,
|
||||
}: {
|
||||
codeStr: string,
|
||||
applyBoxId: string,
|
||||
} & ({
|
||||
uri: URI | 'current';
|
||||
})
|
||||
) => {
|
||||
const accessor = useAccessor()
|
||||
const editCodeService = accessor.get('IEditCodeService')
|
||||
const metricsService = accessor.get('IMetricsService')
|
||||
|
||||
const {
|
||||
currStreamState,
|
||||
isDisabled,
|
||||
getStreamState,
|
||||
} = useApplyButtonState({ applyBoxId, uri })
|
||||
const settingsState = useSettingsState()
|
||||
const isDisabled = !!isFeatureNameDisabled('Apply', settingsState) || !applyBoxId
|
||||
|
||||
const { currStreamStateRef, setApplying } = useApplyStreamState({ applyBoxId })
|
||||
|
||||
|
||||
const onClickSubmit = useCallback(async () => {
|
||||
if (isDisabled) return
|
||||
if (getStreamState() === 'streaming') return
|
||||
const opts = {
|
||||
if (currStreamStateRef.current === 'streaming') return
|
||||
|
||||
await editCodeService.callBeforeApplyOrEdit(uri)
|
||||
|
||||
const [newApplyingUri, applyDonePromise] = editCodeService.startApplying({
|
||||
from: 'ClickApply',
|
||||
applyStr: codeStr,
|
||||
uri: uri,
|
||||
startBehavior: 'reject-conflicts',
|
||||
} as const
|
||||
|
||||
await editCodeService.callBeforeStartApplying(opts)
|
||||
const [newApplyingUri, applyDonePromise] = editCodeService.startApplying(opts) ?? []
|
||||
}) ?? []
|
||||
console.log('setting!!!', newApplyingUri)
|
||||
setApplying(newApplyingUri)
|
||||
|
||||
// catch any errors by interrupting the stream
|
||||
applyDonePromise?.catch(e => {
|
||||
const uri = getUriBeingApplied(applyBoxId)
|
||||
if (uri) editCodeService.interruptURIStreaming({ uri: uri })
|
||||
})
|
||||
|
||||
applyingURIOfApplyBoxIdRef.current[applyBoxId] = newApplyingUri ?? undefined
|
||||
|
||||
// rerender(c => c + 1)
|
||||
metricsService.capture('Apply Code', { length: codeStr.length }) // capture the length only
|
||||
}, [isDisabled, getStreamState, editCodeService, codeStr, uri, applyBoxId, metricsService])
|
||||
|
||||
}, [setApplying, currStreamStateRef, editCodeService, codeStr, uri, applyBoxId, metricsService])
|
||||
|
||||
|
||||
const onInterrupt = useCallback(() => {
|
||||
if (getStreamState() !== 'streaming') return
|
||||
const onClickStop = useCallback(() => {
|
||||
if (currStreamStateRef.current !== 'streaming') return
|
||||
const uri = getUriBeingApplied(applyBoxId)
|
||||
if (!uri) return
|
||||
|
||||
editCodeService.interruptURIStreaming({ uri })
|
||||
metricsService.capture('Stop Apply', {})
|
||||
}, [getStreamState, applyBoxId, editCodeService, metricsService])
|
||||
}, [currStreamStateRef, applyBoxId, editCodeService, metricsService])
|
||||
|
||||
const onAccept = useCallback(() => {
|
||||
const uri = getUriBeingApplied(applyBoxId)
|
||||
if (uri) editCodeService.acceptOrRejectAllDiffAreas({ uri, behavior: 'accept', removeCtrlKs: false })
|
||||
}, [applyBoxId, editCodeService])
|
||||
if (uri) editCodeService.acceptOrRejectAllDiffAreas({ uri: uri, behavior: 'accept', removeCtrlKs: false })
|
||||
}, [uri, applyBoxId, editCodeService])
|
||||
|
||||
const onReject = useCallback(() => {
|
||||
const uri = getUriBeingApplied(applyBoxId)
|
||||
if (uri) editCodeService.acceptOrRejectAllDiffAreas({ uri, behavior: 'reject', removeCtrlKs: false })
|
||||
}, [applyBoxId, editCodeService])
|
||||
if (uri) editCodeService.acceptOrRejectAllDiffAreas({ uri: uri, behavior: 'reject', removeCtrlKs: false })
|
||||
}, [uri, applyBoxId, editCodeService])
|
||||
|
||||
|
||||
const currStreamState = currStreamStateRef.current
|
||||
console.log('currStreamState...', currStreamState)
|
||||
|
||||
if (currStreamState === 'streaming') {
|
||||
return <IconShell1
|
||||
|
||||
Icon={Square}
|
||||
onClick={onInterrupt}
|
||||
|
||||
onClick={onClickStop}
|
||||
{...tooltipPropsForApplyBlock({ tooltipName: 'Stop' })}
|
||||
/>
|
||||
}
|
||||
|
||||
if (currStreamState === 'idle-no-changes') {
|
||||
if (isDisabled) {
|
||||
return null
|
||||
}
|
||||
|
||||
|
||||
if (currStreamState === 'idle-no-changes') {
|
||||
return <IconShell1
|
||||
Icon={Play}
|
||||
onClick={onClickSubmit}
|
||||
|
|
@ -307,6 +329,76 @@ export const ApplyButtonsHTML = ({ codeStr, applyBoxId, uri }: { codeStr: string
|
|||
}
|
||||
|
||||
if (currStreamState === 'idle-has-changes') {
|
||||
return <Fragment>
|
||||
<IconShell1
|
||||
Icon={X}
|
||||
onClick={onReject}
|
||||
{...tooltipPropsForApplyBlock({ tooltipName: 'Remove' })}
|
||||
/>
|
||||
<IconShell1
|
||||
Icon={Check}
|
||||
onClick={onAccept}
|
||||
{...tooltipPropsForApplyBlock({ tooltipName: 'Keep' })}
|
||||
/>
|
||||
</Fragment>
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
export const EditToolButtonsHTML = ({
|
||||
codeStr,
|
||||
applyBoxId,
|
||||
uri,
|
||||
type,
|
||||
}: {
|
||||
codeStr: string,
|
||||
applyBoxId: string,
|
||||
} & ({
|
||||
uri: URI,
|
||||
type: 'edit_file' | 'rewrite_file'
|
||||
})
|
||||
) => {
|
||||
const accessor = useAccessor()
|
||||
const editCodeService = accessor.get('IEditCodeService')
|
||||
const metricsService = accessor.get('IMetricsService')
|
||||
|
||||
const { streamState } = useEditToolStreamState({ applyBoxId, uri })
|
||||
const settingsState = useSettingsState()
|
||||
|
||||
const isDisabled = !!isFeatureNameDisabled('Chat', settingsState) || !applyBoxId
|
||||
|
||||
const onClickSubmit = useCallback(async () => {
|
||||
await editCodeService.callBeforeApplyOrEdit(uri)
|
||||
if (type === 'edit_file') {
|
||||
editCodeService.instantlyApplySearchReplaceBlocks({ uri, searchReplaceBlocks: codeStr })
|
||||
}
|
||||
else if (type === 'rewrite_file') {
|
||||
editCodeService.instantlyRewriteFile({ uri, newContent: codeStr })
|
||||
}
|
||||
}, [type, editCodeService, codeStr, uri, applyBoxId, metricsService])
|
||||
|
||||
const onAccept = useCallback(() => {
|
||||
editCodeService.acceptOrRejectAllDiffAreas({ uri, behavior: 'accept', removeCtrlKs: false })
|
||||
}, [uri, applyBoxId, editCodeService])
|
||||
|
||||
const onReject = useCallback(() => {
|
||||
editCodeService.acceptOrRejectAllDiffAreas({ uri, behavior: 'reject', removeCtrlKs: false })
|
||||
}, [uri, applyBoxId, editCodeService])
|
||||
|
||||
if (isDisabled) return null
|
||||
|
||||
if (streamState === 'idle-no-changes') {
|
||||
return <IconShell1
|
||||
Icon={RotateCw}
|
||||
onClick={onClickSubmit}
|
||||
{...tooltipPropsForApplyBlock({ tooltipName: 'Reapply' })}
|
||||
/>
|
||||
}
|
||||
|
||||
if (streamState === 'idle-has-changes') {
|
||||
return <>
|
||||
<IconShell1
|
||||
Icon={X}
|
||||
|
|
@ -340,7 +432,8 @@ export const BlockCodeApplyWrapper = ({
|
|||
}) => {
|
||||
const accessor = useAccessor()
|
||||
const commandService = accessor.get('ICommandService')
|
||||
const { currStreamState } = useApplyButtonState({ applyBoxId, uri })
|
||||
const { currStreamStateRef } = useApplyStreamState({ applyBoxId })
|
||||
const currStreamState = currStreamStateRef.current
|
||||
|
||||
|
||||
const name = uri !== 'current' ?
|
||||
|
|
|
|||
|
|
@ -71,7 +71,7 @@ export const QuickEditChat = ({
|
|||
startBehavior: 'keep-conflicts',
|
||||
} as const
|
||||
|
||||
await editCodeService.callBeforeStartApplying(opts)
|
||||
await editCodeService.callBeforeApplyOrEdit(opts)
|
||||
const [newApplyingUri, applyDonePromise] = editCodeService.startApplying(opts) ?? []
|
||||
// catch any errors by interrupting the stream
|
||||
applyDonePromise?.catch(e => { if (newApplyingUri) editCodeService.interruptCtrlKStreaming({ diffareaid }) })
|
||||
|
|
|
|||
|
|
@ -23,7 +23,7 @@ import { getModelCapabilities, getIsReasoningEnabledState } from '../../../../co
|
|||
import { AlertTriangle, File, Ban, Check, ChevronRight, Dot, FileIcon, Pencil, Undo, Undo2, X, Flag, Copy as CopyIcon, Info, CirclePlus, Ellipsis, CircleEllipsis, Folder, ALargeSmall, TypeOutline, Text } from 'lucide-react';
|
||||
import { ChatMessage, CheckpointEntry, StagingSelectionItem, ToolMessage } from '../../../../common/chatThreadServiceTypes.js';
|
||||
import { approvalTypeOfToolName, LintErrorItem, ToolApprovalType, toolApprovalTypes, ToolCallParams } from '../../../../common/toolsServiceTypes.js';
|
||||
import { ApplyButtonsHTML, CopyButton, IconShell1, JumpToFileButton, JumpToTerminalButton, StatusIndicator, StatusIndicatorForApplyButton, useApplyButtonState } from '../markdown/ApplyBlockHoverButtons.js';
|
||||
import { CopyButton, EditToolButtonsHTML, IconShell1, JumpToFileButton, JumpToTerminalButton, StatusIndicator, StatusIndicatorForApplyButton, useApplyStreamState, useEditToolStreamState } from '../markdown/ApplyBlockHoverButtons.js';
|
||||
import { IsRunningType } from '../../../chatThreadService.js';
|
||||
import { acceptAllBg, acceptBorder, buttonFontSize, buttonTextColor, rejectAllBg, rejectBg, rejectBorder } from '../../../../common/helpers/colors.js';
|
||||
import { MAX_FILE_CHARS_PAGE, MAX_TERMINAL_INACTIVE_TIME, ToolName, toolNames } from '../../../../common/prompt/prompts.js';
|
||||
|
|
@ -849,7 +849,7 @@ const EditTool = ({ toolMessage, threadId, messageIdx, content }: Parameters<Res
|
|||
const { desc1, desc1Info } = toolNameToDesc(toolMessage.name, toolMessage.params, accessor)
|
||||
const icon = null
|
||||
|
||||
const { rawParams, params } = toolMessage
|
||||
const { rawParams, params, name } = toolMessage
|
||||
const componentParams: ToolHeaderParams = { title, desc1, desc1Info, isError, icon, isRejected, }
|
||||
|
||||
if (toolMessage.type === 'running_now' || toolMessage.type === 'tool_request') {
|
||||
|
|
@ -872,6 +872,7 @@ const EditTool = ({ toolMessage, threadId, messageIdx, content }: Parameters<Res
|
|||
applyBoxId={applyBoxId}
|
||||
uri={params.uri}
|
||||
codeStr={content}
|
||||
toolName={name}
|
||||
/>
|
||||
|
||||
// add children
|
||||
|
|
@ -1620,14 +1621,13 @@ const BottomChildren = ({ children, title }: { children: React.ReactNode, title:
|
|||
}
|
||||
|
||||
|
||||
const EditToolHeaderButtons = ({ applyBoxId, uri, codeStr }: { applyBoxId: string, uri: URI, codeStr: string }) => {
|
||||
const { currStreamState } = useApplyButtonState({ applyBoxId, uri })
|
||||
const EditToolHeaderButtons = ({ applyBoxId, uri, codeStr, toolName }: { applyBoxId: string, uri: URI, codeStr: string, toolName: 'edit_file' | 'rewrite_file' }) => {
|
||||
const { streamState } = useEditToolStreamState({ applyBoxId, uri })
|
||||
return <div className='flex items-center gap-1'>
|
||||
|
||||
|
||||
<StatusIndicatorForApplyButton applyBoxId={applyBoxId} uri={uri} />
|
||||
<JumpToFileButton uri={uri} />
|
||||
{currStreamState === 'idle-no-changes' && <CopyButton codeStr={codeStr} toolTipName='Copy' />}
|
||||
{streamState === 'idle-no-changes' && <CopyButton codeStr={codeStr} toolTipName='Copy' />}
|
||||
<EditToolButtonsHTML type={toolName} codeStr={codeStr} applyBoxId={applyBoxId} uri={uri} />
|
||||
</div>
|
||||
}
|
||||
|
||||
|
|
|
|||
|
|
@ -9,10 +9,13 @@ type ReturnType<T> = [
|
|||
|
||||
// use this if state might be too slow to catch
|
||||
export const useRefState = <T,>(initVal: T): ReturnType<T> => {
|
||||
const [_, _setState] = useState(false)
|
||||
// this actually makes a difference being an int, not a boolean.
|
||||
// if it's a boolean and changes happen to fast, it goes with old values and leads to *very* weird bugs (like returning JSX, but not actually rendering it)
|
||||
const [_s, _setState] = useState(0)
|
||||
|
||||
const ref = useRef<T>(initVal)
|
||||
const setState = useCallback((newVal: T) => {
|
||||
_setState(n => !n) // call rerender
|
||||
_setState(n => n + 1) // call rerender
|
||||
ref.current = newVal
|
||||
}, [])
|
||||
return [ref, setState]
|
||||
|
|
|
|||
|
|
@ -400,7 +400,8 @@ export class ToolsService implements IToolsService {
|
|||
if (this.commandBarService.getStreamState(uri) === 'streaming') {
|
||||
throw new Error(`Another LLM is currently making changes to this file. Please stop streaming for now and ask the user to resume later.`)
|
||||
}
|
||||
editCodeService.instantlyApplyNewContent({ uri, newContent })
|
||||
await editCodeService.callBeforeApplyOrEdit(uri)
|
||||
editCodeService.instantlyRewriteFile({ uri, newContent })
|
||||
// at end, get lint errors
|
||||
const lintErrorsPromise = Promise.resolve().then(async () => {
|
||||
await timeout(2000)
|
||||
|
|
@ -415,6 +416,7 @@ export class ToolsService implements IToolsService {
|
|||
if (this.commandBarService.getStreamState(uri) === 'streaming') {
|
||||
throw new Error(`Another LLM is currently making changes to this file. Please stop streaming for now and ask the user to resume later.`)
|
||||
}
|
||||
await editCodeService.callBeforeApplyOrEdit(uri)
|
||||
editCodeService.instantlyApplySearchReplaceBlocks({ uri, searchReplaceBlocks })
|
||||
|
||||
// at end, get lint errors
|
||||
|
|
|
|||
Loading…
Reference in a new issue