finally bg edits seem to actually work

This commit is contained in:
Andrew Pareles 2025-03-17 01:06:37 -07:00
parent 9350c0dcdf
commit 35eb93d829
5 changed files with 69 additions and 58 deletions

View file

@ -594,6 +594,7 @@ class ChatThreadService extends Disposable implements IChatThreadService {
const errorMessage = this.errMsgs.rejected
this._addMessageToThread(threadId, { role: 'tool', name: name, paramsStr: paramsStr, id, content: errorMessage, result: { type: 'rejected', params: params }, })
this._setStreamState(threadId, {}, 'set')
}
stopRunning(threadId: string) {
const thread = this.state.allThreads[threadId]

View file

@ -44,6 +44,8 @@ import { IEditCodeService, URIStreamState, AddCtrlKOpts, StartApplyingOpts } fro
import { IVoidSettingsService } from '../common/voidSettingsService.js';
import { FeatureName } from '../common/voidSettingsTypes.js';
import { IVoidModelService } from '../common/voidModelService.js';
import { ITextFileService } from '../../../services/textfile/common/textfiles.js';
import { deepClone } from '../../../../base/common/objects.js';
const configOfBG = (color: Color) => {
return { dark: color, light: color, hcDark: color, hcLight: color, }
@ -277,6 +279,7 @@ class EditCodeService extends Disposable implements IEditCodeService {
@IVoidSettingsService private readonly _settingsService: IVoidSettingsService,
// @IFileService private readonly _fileService: IFileService,
@IVoidModelService private readonly _voidModelService: IVoidModelService,
@ITextFileService private readonly _textFileService: ITextFileService,
) {
super();
@ -784,7 +787,10 @@ class EditCodeService extends Disposable implements IEditCodeService {
weAreWriting = false
private _writeURIText(uri: URI, text: string, range_: IRange | 'wholeFileRange', { shouldRealignDiffAreas, }: { shouldRealignDiffAreas: boolean, }) {
const { model } = this._voidModelService.getModel(uri)
if (!model) return // TODO!!!! make sure this works
if (!model) {
this._refreshStylesAndDiffsInURI(uri) // at the end of a write, we still expect to refresh all styles. e.g. sometimes we expect to restore all the decorations even if no edits were made when _writeText is used
return
}
const range: IRange = range_ === 'wholeFileRange' ?
{ startLineNumber: 1, startColumn: 1, endLineNumber: model.getLineCount(), endColumn: Number.MAX_SAFE_INTEGER } // whole file
@ -817,8 +823,8 @@ class EditCodeService extends Disposable implements IEditCodeService {
private _addToHistory(uri: URI, opts?: { onUndo?: () => void }) {
const getCurrentSnapshot = async (): Promise<HistorySnapshot> => {
await this._voidModelService.initializeModel(uri)
const getCurrentSnapshot = (): HistorySnapshot => {
const { model } = this._voidModelService.getModel(uri)
const snapshottedDiffAreaOfId: Record<string, DiffAreaSnapshot> = {}
@ -827,7 +833,7 @@ class EditCodeService extends Disposable implements IEditCodeService {
if (diffArea._URI.fsPath !== uri.fsPath) continue
snapshottedDiffAreaOfId[diffareaid] = structuredClone( // a structured clone must be on a JSON object
snapshottedDiffAreaOfId[diffareaid] = deepClone(
Object.fromEntries(diffAreaSnapshotKeys.map(key => [key, diffArea[key]]))
) as DiffAreaSnapshot
}
@ -841,8 +847,7 @@ class EditCodeService extends Disposable implements IEditCodeService {
}
}
const restoreDiffAreas = async (snapshotPromise: Promise<HistorySnapshot>) => {
const snapshot = await snapshotPromise
const restoreDiffAreas = async (snapshot: HistorySnapshot) => {
// for each diffarea in this uri, stop streaming if currently streaming
for (const diffareaid in this.diffAreaOfId) {
@ -855,7 +860,7 @@ class EditCodeService extends Disposable implements IEditCodeService {
this._deleteAllDiffAreas(uri)
this.diffAreasOfURI[uri.fsPath]?.clear()
const { snapshottedDiffAreaOfId, entireFileCode: entireModelCode } = structuredClone(snapshot) // don't want to destroy the snapshot
const { snapshottedDiffAreaOfId, entireFileCode: entireModelCode } = deepClone(snapshot) // don't want to destroy the snapshot
// restore diffAreaOfId and diffAreasOfModelId
for (const diffareaid in snapshottedDiffAreaOfId) {
@ -885,7 +890,6 @@ class EditCodeService extends Disposable implements IEditCodeService {
}
this._onDidAddOrDeleteDiffZones.fire({ uri })
await this._voidModelService.initializeModel(uri)
// restore file content
this._writeURIText(uri, entireModelCode,
'wholeFileRange',
@ -894,8 +898,8 @@ class EditCodeService extends Disposable implements IEditCodeService {
// this._noLongerNeedModelReference(uri)
}
const beforeSnapshot: Promise<HistorySnapshot> = getCurrentSnapshot()
let afterSnapshot: Promise<HistorySnapshot> | null = null
const beforeSnapshot: HistorySnapshot = getCurrentSnapshot()
let afterSnapshot: HistorySnapshot | null = null
const elt: IUndoRedoElement = {
type: UndoRedoElementType.Resource,
@ -907,7 +911,12 @@ class EditCodeService extends Disposable implements IEditCodeService {
}
this._undoRedoService.pushElement(elt)
const onFinishEdit = () => { afterSnapshot = getCurrentSnapshot() }
const onFinishEdit = async () => {
afterSnapshot = getCurrentSnapshot()
await this._textFileService.save(uri, { // we want [our change] -> [save] so it's all treated as one change.
skipSaveParticipants: true // avoid triggering extensions etc (if they reformat the page, it will add another item to the undo stack)
})
}
return { onFinishEdit }
}
@ -1298,7 +1307,6 @@ class EditCodeService extends Disposable implements IEditCodeService {
let currentFileStr: string
if (from === 'ClickApply') {
const uri_ = this._getActiveEditorURI()
if (!uri_) return
uri = uri_
@ -1492,8 +1500,6 @@ class EditCodeService extends Disposable implements IEditCodeService {
{ shouldRealignDiffAreas: true }
)
const { editorModel } = this._voidModelService.getModel(uri)
editorModel?.save() // save the file
onDone()
resMessageDonePromise()
},
@ -1534,6 +1540,9 @@ class EditCodeService extends Disposable implements IEditCodeService {
else {
uri = givenURI
}
await this._voidModelService.initializeModel(uri)
const { model } = this._voidModelService.getModel(uri)
if (!model) return
@ -1823,8 +1832,6 @@ class EditCodeService extends Disposable implements IEditCodeService {
{ shouldRealignDiffAreas: true }
)
const { editorModel } = this._voidModelService.getModel(uri)
editorModel?.save() // save the file // TODO!!! make sure this works
onDone()
resMessageDonePromise()
},

View file

@ -138,6 +138,9 @@ export const useApplyButtonHTML = ({ codeStr, applyBoxId, uri }: { codeStr: stri
uri: uri,
startBehavior: 'reject-conflicts',
}) ?? []
if (!newApplyingUri) console.log('NOT new applying')
applyingURIOfApplyBoxIdRef.current[applyBoxId] = newApplyingUri ?? undefined
rerender(c => c + 1)
@ -291,7 +294,6 @@ export const BlockCodeApplyWrapper = ({
uri: URI | 'current',
}) => {
const { statusIndicatorHTML, buttonsHTML } = useApplyButtonHTML({ codeStr: initValue, applyBoxId, uri })
const accessor = useAccessor()
const commandService = accessor.get('ICommandService')
@ -301,7 +303,6 @@ export const BlockCodeApplyWrapper = ({
name={<span className='not-italic'>{getBasename(uri.fsPath)}</span>}
isSmall={true}
showDot={false}
// TODO!!! this uri is not correct, it is not recognized as an actual file for some stupid reason
onClick={() => { commandService.executeCommand('vscode.open', uri, { preview: true }) }}
/>
: <span>{language}</span>

View file

@ -44,7 +44,6 @@ import './chatThreadService.js'
import './metricsPollService.js'
// ---------- common (unclear if these actually need to be imported, because they're already imported wherever they're used) ----------
// llmMessage
@ -62,3 +61,5 @@ import '../common/metricsService.js'
// updates
import '../common/voidUpdateService.js'
// model service
import '../common/voidModelService.js'

View file

@ -1,68 +1,69 @@
/*--------------------------------------------------------------------------------------
* Copyright 2025 Glass Devtools, Inc. All rights reserved.
* Licensed under the Apache License, Version 2.0. See LICENSE.txt for more information.
*--------------------------------------------------------------------------------------*/
import { Disposable } from '../../../../base/common/lifecycle.js';
import { Disposable, IReference } from '../../../../base/common/lifecycle.js';
import { URI } from '../../../../base/common/uri.js';
import { ITextModel } from '../../../../editor/common/model.js';
import { IResolvedTextEditorModel, ITextModelService } from '../../../../editor/common/services/resolverService.js';
import { registerSingleton, InstantiationType } from '../../../../platform/instantiation/common/extensions.js';
import { createDecorator } from '../../../../platform/instantiation/common/instantiation.js';
import { ITextFileEditorModel, ITextFileService } from '../../../services/textfile/common/textfiles.js';
type VoidModelType = {
model: ITextModel | null;
editorModel: IResolvedTextEditorModel | null;
};
type VoidModelType = { model: ITextModel | null, editorModel: ITextFileEditorModel | null }
export interface IVoidModelService {
readonly _serviceBrand: undefined;
initializeModel(uri: URI): Promise<void>
getModel(uri: URI): VoidModelType
getModelSafe(uri: URI): Promise<VoidModelType>
initializeModel(uri: URI): Promise<void>;
getModel(uri: URI): VoidModelType;
getModelSafe(uri: URI): Promise<VoidModelType>;
}
export const IVoidModelService = createDecorator<IVoidModelService>('voidVoidModelService');
class VoidModelService extends Disposable implements IVoidModelService {
_serviceBrand: undefined;
static readonly ID = 'voidVoidModelService';
private readonly _modelRefOfURI: Record<string, ITextFileEditorModel> = {}
private readonly _modelRefOfURI: Record<string, IReference<IResolvedTextEditorModel>> = {};
constructor(
@ITextFileService private readonly _textFileService: ITextFileService,
@ITextModelService private readonly _textModelService: ITextModelService,
) {
super()
super();
}
initializeModel = async (uri: URI) => {
if (uri.fsPath in this._modelRefOfURI) return
if (uri.scheme !== 'file') return
const model = await this._textFileService.files.resolve(uri)
if (uri.fsPath in this._modelRefOfURI) return;
const editorModelRef = await this._textModelService.createModelReference(uri);
// Keep a strong reference to prevent disposal
this._modelRefOfURI[uri.fsPath] = editorModelRef;
};
this._modelRefOfURI[uri.fsPath] = model
}
getModel = (uri: URI) => {
const editorModel = this._modelRefOfURI[uri.fsPath]
if (!editorModel) return { model: null, editorModel: null }
const model = editorModel.textEditorModel
if (!model)
return { model: null, editorModel }
return { model, editorModel }
}
getModel = (uri: URI): VoidModelType => {
const editorModelRef = this._modelRefOfURI[uri.fsPath];
if (!editorModelRef) {
return { model: null, editorModel: null };
}
getModelSafe = async (uri: URI) => {
if (!(uri.fsPath in this._modelRefOfURI)) await this.initializeModel(uri)
return this.getModel(uri)
}
const model = editorModelRef.object.textEditorModel;
if (!model) {
return { model: null, editorModel: editorModelRef.object };
}
return { model, editorModel: editorModelRef.object };
};
getModelSafe = async (uri: URI): Promise<VoidModelType> => {
if (!(uri.fsPath in this._modelRefOfURI)) await this.initializeModel(uri);
return this.getModel(uri);
};
override dispose() {
super.dispose()
for (const [_, reference] of Object.entries(this._modelRefOfURI)) {
reference?.dispose()
super.dispose();
for (const ref of Object.values(this._modelRefOfURI)) {
ref.dispose(); // release reference to allow disposal
}
}
}
registerSingleton(IVoidModelService, VoidModelService, InstantiationType.Eager);