more progress (still broken) + fix weird _useThread() + rm initStreamingDiffZoneId

This commit is contained in:
Andrew Pareles 2025-02-21 20:10:02 -08:00
parent b005b1e95a
commit 86dfc5521d
8 changed files with 150 additions and 81 deletions

View file

@ -162,9 +162,12 @@ export interface IChatThreadService {
isFocusingMessage(): boolean;
setFocusedMessageIdx(messageIdx: number | undefined): void;
// _useFocusedStagingState(messageIdx?: number | undefined): readonly [StagingInfo, (stagingInfo: StagingInfo) => void];
_useCurrentThreadState(): readonly [ThreadType['state'], (newState: Partial<ThreadType['state']>) => void];
_useCurrentMessageState(messageIdx: number): readonly [UserMessageState, (newState: Partial<UserMessageState>) => void];
// exposed getters/setters
getCurrentMessageState: (messageIdx: number) => UserMessageState
setCurrentMessageState: (messageIdx: number, newState: Partial<UserMessageState>) => void
getCurrentThreadStagingSelections: () => StagingSelectionItem[]
setCurrentThreadStagingSelections: (stagingSelections: StagingSelectionItem[]) => void
// call to edit a message
editUserMessageAndStreamResponse({ userMessage, chatMode, messageIdx }: { userMessage: string, chatMode: ChatMode, messageIdx: number }): Promise<void>;
@ -622,33 +625,27 @@ class ChatThreadService extends Disposable implements IChatThreadService {
}
getCurrentThreadStagingSelections = () => {
return this.getCurrentThread().state.stagingSelections
}
setCurrentThreadStagingSelections = (stagingSelections: StagingSelectionItem[]) => {
this._setCurrentThreadState({ stagingSelections })
}
// gets `staging` and `setStaging` of the currently focused element, given the index of the currently selected message (or undefined if no message is selected)
_useCurrentMessageState(messageIdx: number) {
const thread = this.getCurrentThread()
const messages = thread.messages
const currMessage = messages[messageIdx]
if (currMessage.role !== 'user') {
return [defaultMessageState, (s: any) => { }] as const
}
const state = currMessage.state
const setState = (newState: Partial<UserMessageState>) => this._setCurrentMessageState(newState, messageIdx)
return [state, setState] as const
getCurrentMessageState(messageIdx: number): UserMessageState {
const currMessage = this.getCurrentThread()?.messages?.[messageIdx]
if (!currMessage || currMessage.role !== 'user') return defaultMessageState
return currMessage.state
}
setCurrentMessageState(messageIdx: number, newState: Partial<UserMessageState>) {
const currMessage = this.getCurrentThread()?.messages?.[messageIdx]
if (!currMessage || currMessage.role !== 'user') return
this._setCurrentMessageState(newState, messageIdx)
}
_useCurrentThreadState() {
const thread = this.getCurrentThread()
const state = thread.state
const setState = this._setCurrentThreadState.bind(this)
return [state, setState] as const
}
}

View file

@ -248,10 +248,17 @@ type StreamLocationMutable = { line: number, col: number, addedSplitYet: boolean
export interface IEditCodeService {
readonly _serviceBrand: undefined;
startApplying(opts: StartApplyingOpts): number | void;
interruptStreaming(diffareaid: number): void;
startApplying(opts: StartApplyingOpts): URI | null;
addCtrlKZone(opts: AddCtrlKOpts): number | undefined;
removeCtrlKZone(opts: { diffareaid: number }): void;
isDiffZoneStreaming(opts: { diffareaid: number }): boolean;
isCtrlKZoneStreaming(opts: { diffareaid: number }): boolean;
interruptDiffZoneStreaming(opts: { diffareaid: number }): void;
interruptCtrlKStreaming(opts: { diffareaid: number }): void;
// testDiffs(): void;
}
@ -274,6 +281,7 @@ class EditCodeService extends Disposable implements IEditCodeService {
private readonly _onDidAddOrDeleteDiffZones = new Emitter<{ uri: URI }>();
constructor(
// @IHistoryService private readonly _historyService: IHistoryService, // history service is the history of pressing alt left/right
@ICodeEditorService private readonly _editorService: ICodeEditorService,
@ -309,7 +317,7 @@ class EditCodeService extends Disposable implements IEditCodeService {
// when a stream starts or ends
let removeAcceptRejectAllUI: (() => void) | null = null
const onChangeUriState = () => {
const changeUriState = () => {
const uri = model.uri
const diffZones = [...this.diffAreasOfURI[uri.fsPath].values()]
.map(diffareaid => this.diffAreaOfId[diffareaid])
@ -322,8 +330,8 @@ class EditCodeService extends Disposable implements IEditCodeService {
removeAcceptRejectAllUI = null
}
}
this._register(this._onDidAddOrDeleteDiffZones.event(({ uri: uri_ }) => { if (uri_.fsPath === model.uri.fsPath) onChangeUriState() }))
this._register(this._onDidChangeStreaming.event(({ uri: uri_ }) => { if (uri_.fsPath === model.uri.fsPath) onChangeUriState() }))
this._register(this._onDidAddOrDeleteDiffZones.event(({ uri: uri_ }) => { if (uri_.fsPath === model.uri.fsPath) changeUriState() }))
this._register(this._onDidChangeStreaming.event(({ uri: uri_ }) => { if (uri_.fsPath === model.uri.fsPath) changeUriState() }))
}
// initialize all existing models + initialize when a new model mounts
for (let model of this._modelService.getModels()) { initializeModel(model) }
@ -526,7 +534,6 @@ class EditCodeService extends Disposable implements IEditCodeService {
mountCtrlK(domNode, accessor, {
diffareaid: ctrlKZone.diffareaid,
initStreamingDiffZoneId: ctrlKZone._linkedStreamingDiffZone,
textAreaRef: (r) => {
textAreaRef.current = r
@ -1198,19 +1205,15 @@ class EditCodeService extends Disposable implements IEditCodeService {
public startApplying(opts: StartApplyingOpts) {
if (opts.type === 'rewrite') {
const addedDiffZone = this._initializeWriteoverStream(opts)
return addedDiffZone?.diffareaid
const addedDiffArea = this._initializeWriteoverStream(opts)
return addedDiffArea?._URI ?? null
}
else if (opts.type === 'searchReplace') {
const addedDiffZone = this._initializeSearchAndReplaceStream(opts)
return addedDiffZone?.diffareaid
const addedDiffArea = this._initializeSearchAndReplaceStream(opts)
return addedDiffArea?._URI ?? null
}
return undefined
return null
}
@ -1708,18 +1711,46 @@ class EditCodeService extends Disposable implements IEditCodeService {
this._undoRedoService.undo(uri)
}
// call this outside undo/redo (it calls undo). this is only for aborting a diffzone stream
interruptStreaming(diffareaid: number) {
const diffArea = this.diffAreaOfId[diffareaid]
if (!diffArea) return
if (diffArea.type !== 'DiffZone') return
if (!diffArea._streamState.isStreaming) return
this._stopIfStreaming(diffArea)
this._undoHistory(diffArea._URI)
isCtrlKZoneStreaming({ diffareaid }: { diffareaid: number }) {
const ctrlKZone = this.diffAreaOfId[diffareaid]
if (!ctrlKZone) return false
if (ctrlKZone.type !== 'CtrlKZone') return false
return !!ctrlKZone._linkedStreamingDiffZone
}
isDiffZoneStreaming({ diffareaid }: { diffareaid: number }) {
const diffZone = this.diffAreaOfId[diffareaid]
if (diffZone?.type !== 'DiffZone') return false
return diffZone._streamState.isStreaming
}
// call this outside undo/redo (it calls undo). this is only for aborting a diffzone stream
interruptDiffZoneStreaming({ diffareaid }: { diffareaid: number }) {
const diffZone = this.diffAreaOfId[diffareaid]
if (diffZone?.type !== 'DiffZone') return
if (!diffZone._streamState.isStreaming) return
this._stopIfStreaming(diffZone)
this._undoHistory(diffZone._URI)
}
interruptCtrlKStreaming({ diffareaid }: { diffareaid: number }) {
const ctrlKZone = this.diffAreaOfId[diffareaid]
if (ctrlKZone?.type !== 'CtrlKZone') return
if (!ctrlKZone._linkedStreamingDiffZone) return
const linkedStreamingDiffZone = this.diffAreaOfId[ctrlKZone._linkedStreamingDiffZone]
if (!linkedStreamingDiffZone) return
if (linkedStreamingDiffZone.type !== 'DiffZone') return
this.interruptDiffZoneStreaming({ diffareaid: linkedStreamingDiffZone.diffareaid })
}

View file

@ -17,7 +17,6 @@ import { IMetricsService } from '../common/metricsService.js';
export type QuickEditPropsType = {
diffareaid: number,
initStreamingDiffZoneId: number | null,
textAreaRef: (ref: HTMLTextAreaElement | null) => void;
onChangeHeight: (height: number) => void;
onChangeText: (text: string) => void;

View file

@ -1,5 +1,7 @@
import { useState, useEffect, useCallback } from 'react'
import { useAccessor } from '../util/services.js'
import { useAccessor, useIsURIStreaming } from '../util/services.js'
import { useRefState } from '../util/helpers.js'
import { isFeatureNameDisabled } from '../../../../common/voidSettingsTypes.js'
enum CopyButtonText {
Idle = 'Copy',
@ -50,7 +52,15 @@ const ApplyButton = ({ codeStr, codeBoxId }: { codeStr: string, codeBoxId: strin
const metricsService = accessor.get('IMetricsService')
const onApply = useCallback(() => {
const [currStreamingDiffZoneRef, setCurrentlyStreamingDiffZone] = useRefState<number | null>(initStreamingDiffZoneId)
const isStreaming = currStreamingDiffZoneRef.current !== null
const isDisabled = !!isFeatureNameDisabled('Ctrl+K', settingsState)
useIsDiffZoneStreaming(isDiffAreaStreaming)
const onSubmit = useCallback(() => {
const diffareaid = editCodeService.startApplying({
from: 'ClickApply',
type: 'searchReplace',
@ -60,7 +70,31 @@ const ApplyButton = ({ codeStr, codeBoxId }: { codeStr: string, codeBoxId: strin
metricsService.capture('Apply Code', { length: codeStr.length }) // capture the length only
}, [metricsService, editCodeService, codeStr])
if (isDisabled) return
if (currStreamingDiffZoneRef.current !== null) return
textAreaFnsRef.current?.disable()
const id = editCodeService.startApplying({
from: 'QuickEdit',
type: 'rewrite',
diffareaid: diffareaid,
})
setCurrentlyStreamingDiffZone(id ?? null)
}, [currStreamingDiffZoneRef, setCurrentlyStreamingDiffZone, isDisabled, editCodeService, diffareaid])
const onInterrupt = useCallback(() => {
if (currStreamingDiffZoneRef.current === null) return
editCodeService.interruptStreaming(currStreamingDiffZoneRef.current)
setCurrentlyStreamingDiffZone(null)
textAreaFnsRef.current?.enable()
}, [currStreamingDiffZoneRef, setCurrentlyStreamingDiffZone, editCodeService])
const isSingleLine = !codeStr.includes('\n')

View file

@ -4,7 +4,7 @@
*--------------------------------------------------------------------------------------*/
import React, { FormEvent, useCallback, useEffect, useRef, useState } from 'react';
import { useSettingsState, useSidebarState, useChatThreadsState, useQuickEditState, useAccessor } from '../util/services.js';
import { useSettingsState, useSidebarState, useChatThreadsState, useQuickEditState, useAccessor, useIsCtrlKZoneStreaming } from '../util/services.js';
import { TextAreaFns, VoidInputBox2 } from '../util/inputs.js';
import { QuickEditPropsType } from '../../../quickEditActions.js';
import { ButtonStop, ButtonSubmit, IconX, VoidChatArea } from '../sidebar-tsx/SidebarChat.js';
@ -16,7 +16,6 @@ import { isFeatureNameDisabled } from '../../../../../../../workbench/contrib/vo
export const QuickEditChat = ({
diffareaid,
initStreamingDiffZoneId,
onChangeHeight,
onChangeText: onChangeText_,
textAreaRef: textAreaRef_,
@ -49,28 +48,25 @@ export const QuickEditChat = ({
const [instructionsAreEmpty, setInstructionsAreEmpty] = useState(!(initText ?? '')) // the user's instructions
const isDisabled = instructionsAreEmpty || !!isFeatureNameDisabled('Ctrl+K', settingsState)
const [currStreamingDiffZoneRef, setCurrentlyStreamingDiffZone] = useRefState<number | null>(initStreamingDiffZoneId)
const isStreaming = currStreamingDiffZoneRef.current !== null
const isStreamingRefState = useIsCtrlKZoneStreaming(diffareaid)
const onSubmit = useCallback(() => {
if (isDisabled) return
if (currStreamingDiffZoneRef.current !== null) return
if (isStreamingRefState.current) return
textAreaFnsRef.current?.disable()
const id = editCodeService.startApplying({
editCodeService.startApplying({
from: 'QuickEdit',
type:'rewrite',
diffareaid: diffareaid,
type: 'rewrite',
diffareaid,
})
setCurrentlyStreamingDiffZone(id ?? null)
}, [currStreamingDiffZoneRef, setCurrentlyStreamingDiffZone, isDisabled, editCodeService, diffareaid])
}, [isStreamingRefState, isDisabled, editCodeService, diffareaid])
const onInterrupt = useCallback(() => {
if (currStreamingDiffZoneRef.current === null) return
editCodeService.interruptStreaming(currStreamingDiffZoneRef.current)
setCurrentlyStreamingDiffZone(null)
if (!isStreamingRefState.current ) return
editCodeService.interruptCtrlKStreaming({ diffareaid })
textAreaFnsRef.current?.enable()
}, [currStreamingDiffZoneRef, setCurrentlyStreamingDiffZone, editCodeService])
}, [isStreamingRefState, editCodeService])
const onX = useCallback(() => {
@ -89,7 +85,7 @@ export const QuickEditChat = ({
onSubmit={onSubmit}
onAbort={onInterrupt}
onClose={onX}
isStreaming={isStreaming}
isStreaming={isStreamingRefState.current}
isDisabled={isDisabled}
featureName="Ctrl+K"
className="py-2 w-full"

View file

@ -557,11 +557,11 @@ const ChatBubble = ({ chatMessage, isLoading, messageIdx }: { chatMessage: ChatM
let setStagingSelections = (s: StagingSelectionItem[]) => { }
if (messageIdx !== undefined) {
const [_state, _setState] = chatThreadsService._useCurrentMessageState(messageIdx)
const _state = chatThreadsService.getCurrentMessageState(messageIdx)
isBeingEdited = _state.isBeingEdited
setIsBeingEdited = (v) => _setState({ isBeingEdited: v })
stagingSelections = _state.stagingSelections
setStagingSelections = (s) => { _setState({ stagingSelections: s }) }
setIsBeingEdited = (v) => chatThreadsService.setCurrentMessageState(messageIdx, { isBeingEdited: v })
setStagingSelections = (s) => chatThreadsService.setCurrentMessageState(messageIdx, { stagingSelections: s })
}
@ -780,9 +780,8 @@ export const SidebarChat = () => {
const currentThread = chatThreadsService.getCurrentThread()
const previousMessages = currentThread?.messages ?? []
const [_state, _setState] = chatThreadsService._useCurrentThreadState()
const selections = _state.stagingSelections
const setSelections = (s: StagingSelectionItem[]) => { _setState({ stagingSelections: s }) }
const selections = chatThreadsService.getCurrentThread().state.stagingSelections
const setSelections = (s: StagingSelectionItem[]) => { chatThreadsService.setCurrentThreadStagingSelections(s) }
// stream state
const currThreadStreamState = useChatThreadsStreamState(chatThreadsState.currentThreadId)
@ -818,7 +817,7 @@ export const SidebarChat = () => {
textAreaFnsRef.current?.setValue('')
textAreaRef.current?.focus() // focus input after submit
}, [chatThreadsService, isDisabled, isStreaming, textAreaRef, textAreaFnsRef, selections, setSelections])
}, [chatThreadsService, isDisabled, isStreaming, textAreaRef, textAreaFnsRef, setSelections])
const onAbort = () => {
const threadId = currentThread.id

View file

@ -47,6 +47,7 @@ import { IEnvironmentService } from '../../../../../../../platform/environment/c
import { IConfigurationService } from '../../../../../../../platform/configuration/common/configuration.js'
import { IPathService } from '../../../../../../../workbench/services/path/common/pathService.js'
import { IMetricsService } from '../../../../../../../workbench/contrib/void/common/metricsService.js'
import { URI } from '../../../../../../../base/common/uri.js'
@ -353,3 +354,17 @@ export const useIsDark = () => {
return isDark
}
export const useIsCtrlKZoneStreaming = (diffareaid: number) => {
return { current: true }
}
export const useIsDiffZoneStreaming = (uri: URI) => {
}

View file

@ -141,13 +141,11 @@ registerAction2(class extends Action2 {
let setSelections = (s: StagingSelectionItem[]) => { }
if (focusedMessageIdx === undefined) {
const [state, setState] = chatThreadService._useCurrentThreadState()
selections = state.stagingSelections
setSelections = (s) => setState({ stagingSelections: s })
selections = chatThreadService.getCurrentThreadStagingSelections()
setSelections = (s: StagingSelectionItem[]) => chatThreadService.setCurrentThreadStagingSelections(s)
} else {
const [state, setState] = chatThreadService._useCurrentMessageState(focusedMessageIdx)
selections = state.stagingSelections
setSelections = (s) => setState({ stagingSelections: s })
selections = chatThreadService.getCurrentMessageState(focusedMessageIdx).stagingSelections
setSelections = (s) => chatThreadService.setCurrentMessageState(focusedMessageIdx, { stagingSelections: s })
}
// if matches with existing selection, overwrite (since text may change)