fix SEARCH/REPLACE detection

This commit is contained in:
Andrew Pareles 2025-03-04 01:18:10 -08:00
parent 5c5a44e4ac
commit 7f278aafcb
4 changed files with 20 additions and 33 deletions

View file

@ -18,9 +18,14 @@ import { IModelService } from '../../../../editor/common/services/model.js';
import { extractCodeFromRegular } from './helpers/extractCodeFromResult.js';
import { registerWorkbenchContribution2, WorkbenchPhase } from '../../../common/contributions.js';
import { ILLMMessageService } from '../common/llmMessageService.js';
import { _ln, allLinebreakSymbols } from '../common/voidFileService.js';
import { isWindows } from '../../../../base/common/platform.js';
// import { IContextGatheringService } from './contextGatheringService.js';
const allLinebreakSymbols = ['\r\n', '\n']
const _ln = isWindows ? allLinebreakSymbols[0] : allLinebreakSymbols[1]
// The extension this was called from is here - https://github.com/voideditor/void/blob/autocomplete/extensions/void/src/extension/extension.ts

View file

@ -1588,6 +1588,7 @@ class EditCodeService extends Disposable implements IEditCodeService {
// if error
if (typeof originalBounds === 'string') {
console.error('Error in originalBounds, retrying.', originalBounds)
messages.push(
{ role: 'assistant', content: fullText }, // latest output
{ role: 'user', content: errMsgOfInvalidStr(originalBounds) } // user explanation of what's wrong
@ -1663,6 +1664,8 @@ class EditCodeService extends Disposable implements IEditCodeService {
this._notificationService.info(`Void: We ran Apply, but the LLM didn't output any changes.`)
}
await new Promise(resolve => setTimeout(resolve, 500))
// writeover the whole file
let newCode = originalFileCode
for (let blockNum = addedTrackingZoneOfBlockNum.length - 1; blockNum >= 0; blockNum -= 1) {

View file

@ -182,10 +182,9 @@ const endsWithAnyPrefixOf = (str: string, anyPrefix: string) => {
// guarantees if you keep adding text, array length will strictly grow and state will progress without going back
export const extractSearchReplaceBlocks = (str: string) => {
const ORIGINAL_ = ORIGINAL + `\n`
const DIVIDER_ = '\n' + DIVIDER + `\n`
// logic for FINAL_ is slightly more complicated - should be '\n' + FINAL, but that ignores if the final output is empty
const DIVIDER_ = DIVIDER + `\n`
// we only check FINAL, we don't care about FINAL + '\n' because the string is over
const blocks: ExtractedSearchReplaceBlock[] = []
@ -196,7 +195,7 @@ export const extractSearchReplaceBlocks = (str: string) => {
if (origStart === -1) { return blocks }
origStart += ORIGINAL_.length
i = origStart
// wrote <<<< ORIGINAL
// wrote <<<< ORIGINAL\n
let dividerStart = str.indexOf(DIVIDER_, i)
if (dividerStart === -1) { // if didnt find DIVIDER_, either writing originalStr or DIVIDER_ right now
@ -211,17 +210,13 @@ export const extractSearchReplaceBlocks = (str: string) => {
const origStrDone = str.substring(origStart, dividerStart)
dividerStart += DIVIDER_.length
i = dividerStart
// wrote =====
// wrote \n=====\n
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_)
const finalStart = str.indexOf(FINAL, i)
if (finalStart === -1) { // if didnt find FINAL, either writing finalStr or FINAL right now
const isWritingFINAL = endsWithAnyPrefixOf(str, FINAL)
blocks.push({
orig: origStrDone,
final: str.substring(dividerStart, str.length - (isWritingFINAL?.length ?? 0)),
@ -230,8 +225,7 @@ export const extractSearchReplaceBlocks = (str: string) => {
return blocks
}
const finalStrDone = str.substring(dividerStart, finalStart)
finalStart += FINAL_.length
i = finalStart
i = finalStart + FINAL.length
// wrote >>>>> FINAL
blocks.push({

View file

@ -3,7 +3,6 @@
* Licensed under the Apache License, Version 2.0. See LICENSE.txt for more information.
*--------------------------------------------------------------------------------------*/
import { isWindows } from '../../../../base/common/platform.js';
import { URI } from '../../../../base/common/uri.js';
import { EndOfLinePreference } from '../../../../editor/common/model.js';
import { IModelService } from '../../../../editor/common/services/model.js';
@ -11,11 +10,6 @@ import { IFileService } from '../../../../platform/files/common/files.js';
import { registerSingleton, InstantiationType } from '../../../../platform/instantiation/common/extensions.js';
import { createDecorator } from '../../../../platform/instantiation/common/instantiation.js';
// linebreak symbols
export const allLinebreakSymbols = ['\r\n', '\n']
export const _ln = isWindows ? allLinebreakSymbols[0] : allLinebreakSymbols[1]
export interface IVoidFileService {
readonly _serviceBrand: undefined;
@ -52,19 +46,10 @@ export class VoidFileService implements IVoidFileService {
_readFileRaw = async (uri: URI, range?: { startLineNumber: number, endLineNumber: number }): Promise<string | null> => {
try { // this throws an error if no file exists (eg it was deleted)
const res = await this.fileService.readFile(uri);
if (range) {
return res.value.toString()
.split(_ln)
.slice(range.startLineNumber - 1, range.endLineNumber)
.join(_ln)
}
return res.value.toString();
const str = res.value.toString().replace(/\r\n/g, '\n'); // even if not on Windows, might read a file with \r\n
if (range) return str.split('\n').slice(range.startLineNumber - 1, range.endLineNumber).join('\n')
return str;
} catch (e) {
return null;
}