mirror of
https://github.com/voideditor/void
synced 2026-05-23 09:28:23 +00:00
Search and replace?
This commit is contained in:
parent
d504daffa5
commit
0dfa81f637
5 changed files with 55 additions and 89 deletions
|
|
@ -1084,56 +1084,6 @@ class InlineDiffsService extends Disposable implements IInlineDiffsService {
|
|||
|
||||
|
||||
|
||||
// // if streaming, use diffs to figure out where to write new code
|
||||
// // these are two different coordinate systems - new and old line number
|
||||
// let newFileEndLine: number // get new[0...newStoppingPoint] with line=newStoppingPoint highlighted
|
||||
// let originalCodeStartLine: number // get original[oldStartingPoint...]
|
||||
|
||||
// const lastDiff = computedDiffs.pop()
|
||||
|
||||
// if (!lastDiff) {
|
||||
// // if the writing is identical so far, display no changes
|
||||
// newFileEndLine = diffZone.startLine
|
||||
// originalCodeStartLine = 1
|
||||
// }
|
||||
// else {
|
||||
// if (lastDiff.type === 'insertion') {
|
||||
// newFileEndLine = lastDiff.endLine
|
||||
// originalCodeStartLine = lastDiff.originalStartLine
|
||||
// }
|
||||
// else if (lastDiff.type === 'deletion') {
|
||||
// newFileEndLine = lastDiff.startLine
|
||||
// originalCodeStartLine = lastDiff.originalStartLine
|
||||
// }
|
||||
// else if (lastDiff.type === 'edit') {
|
||||
// newFileEndLine = lastDiff.endLine
|
||||
// originalCodeStartLine = lastDiff.originalStartLine
|
||||
// }
|
||||
// else {
|
||||
// throw new Error(`Void: diff.type not recognized on: ${lastDiff}`)
|
||||
// }
|
||||
// }
|
||||
|
||||
// diffZone._streamState.line = newFileEndLine
|
||||
|
||||
// // lines are 1-indexed
|
||||
// const newFileTop = llmText.split('\n').slice(diffZone.startLine, (newFileEndLine - 1)).join('\n')
|
||||
// const oldFileBottom = diffZone.originalCode.split('\n').slice((originalCodeStartLine - 1), Infinity).join('\n')
|
||||
|
||||
// const newCode = `${newFileTop}\n${oldFileBottom}`
|
||||
|
||||
// this._writeText(uri, newCode,
|
||||
// { startLineNumber: diffZone.startLine, startColumn: 1, endLineNumber: diffZone.endLine, endColumn: Number.MAX_SAFE_INTEGER, }, // 1-indexed
|
||||
// { shouldRealignDiffAreas: true }
|
||||
// )
|
||||
|
||||
|
||||
// return computedDiffs
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
// called first, then call startApplying
|
||||
public addCtrlKZone({ startLine, endLine, editor }: AddCtrlKOpts) {
|
||||
|
|
@ -1191,8 +1141,19 @@ class InlineDiffsService extends Disposable implements IInlineDiffsService {
|
|||
|
||||
|
||||
public startApplying(opts: StartApplyingOpts) {
|
||||
const addedDiffZone = this._initializeRewriteStream(opts)
|
||||
return addedDiffZone?.diffareaid
|
||||
|
||||
if (opts.type === 'rewrite') {
|
||||
const addedDiffZone = this._initializeRewriteStream(opts)
|
||||
return addedDiffZone?.diffareaid
|
||||
}
|
||||
|
||||
else if (opts.type === 'searchReplace') {
|
||||
this._initializeSearchAndReplaceStream(opts)
|
||||
return undefined
|
||||
}
|
||||
|
||||
else return undefined
|
||||
|
||||
}
|
||||
|
||||
|
||||
|
|
@ -1213,7 +1174,7 @@ class InlineDiffsService extends Disposable implements IInlineDiffsService {
|
|||
}
|
||||
|
||||
|
||||
private async _generateSearchAndReplaceBlocks({ uri, applyStr }: { uri: URI, applyStr: string }): Promise<ExtractedCodeBlock[] | undefined> {
|
||||
private async _initializeSearchAndReplaceStream({ applyStr }: { applyStr: string }) {
|
||||
const ORIGINAL = `<<<<<<< ORIGINAL`
|
||||
const DIVIDER = `=======`
|
||||
const FINAL = `>>>>>>> UPDATED`
|
||||
|
|
@ -1268,6 +1229,14 @@ ${FINAL}
|
|||
${tripleTick[1]}
|
||||
`
|
||||
|
||||
const uri_ = this._getActiveEditorURI()
|
||||
if (!uri_) return
|
||||
const uri = uri_
|
||||
|
||||
// generate search/replace block text
|
||||
const fileContents = await VSReadFile(this._modelService, uri)
|
||||
if (fileContents === null) return
|
||||
|
||||
|
||||
const searchReplaceUserMessage = ({ originalCode, applyStr }: { originalCode: string, applyStr: string }) => `\
|
||||
ORIGINAL_FILE
|
||||
|
|
@ -1280,7 +1249,7 @@ INSTRUCTIONS
|
|||
Please output SEARCH/REPLACE blocks to make the change. Return ONLY your suggested SEARCH/REPLACE blocks, without any explanation.
|
||||
`
|
||||
|
||||
function endsWithAnyPrefixOf(str: string, anyPrefix: string) {
|
||||
const endsWithAnyPrefixOf = (str: string, anyPrefix: string) => {
|
||||
// for each prefix
|
||||
for (let i = anyPrefix.length; i >= 0; i--) {
|
||||
const prefix = anyPrefix.slice(0, i)
|
||||
|
|
@ -1344,9 +1313,6 @@ Please output SEARCH/REPLACE blocks to make the change. Return ONLY your suggest
|
|||
}
|
||||
}
|
||||
|
||||
// generate search/replace block text
|
||||
const fileContents = await VSReadFile(this._modelService, uri)
|
||||
if (fileContents === null) return
|
||||
|
||||
// reject all diffZones on this URI, adding to history (there can't possibly be overlap after this)
|
||||
this.removeDiffAreas({ uri, behavior: 'reject', removeCtrlKs: true })
|
||||
|
|
@ -1364,26 +1330,24 @@ Please output SEARCH/REPLACE blocks to make the change. Return ONLY your suggest
|
|||
const blocks = extractBlocks(fullText)
|
||||
|
||||
// find block.orig in fileContents and return its range in file
|
||||
const findBlock = (block: { orig: string }, fileContents: string) => {
|
||||
const origText = block.orig
|
||||
const idx = fileContents.indexOf(origText)
|
||||
const findTextInCode = (text: string, fileContents: string) => {
|
||||
const idx = fileContents.indexOf(text)
|
||||
if (idx === -1) return 'Not found' as const
|
||||
const lastIdx = fileContents.lastIndexOf(origText)
|
||||
const lastIdx = fileContents.lastIndexOf(text)
|
||||
if (lastIdx !== idx) return 'Not unique' as const
|
||||
|
||||
const startLine = fileContents.substring(0, idx).split('\n').length
|
||||
const numLines = origText.split('\n').length
|
||||
const numLines = text.split('\n').length
|
||||
const endLine = startLine + numLines - 1
|
||||
|
||||
return [startLine, endLine]
|
||||
}
|
||||
|
||||
let latestStreamInfoMutable: any = {}
|
||||
|
||||
|
||||
for (let blockNum = 0; blockNum < blocks.length; blockNum += 1) {
|
||||
const block = blocks[blockNum]
|
||||
const foundInCode = findBlock(block, fileContents)
|
||||
if (block.state === 'writingOriginal') continue
|
||||
|
||||
const foundInCode = findTextInCode(block.orig, fileContents)
|
||||
if (typeof foundInCode === 'string') {
|
||||
console.log('ERROR!!!!', foundInCode)
|
||||
continue
|
||||
|
|
@ -1391,8 +1355,6 @@ Please output SEARCH/REPLACE blocks to make the change. Return ONLY your suggest
|
|||
|
||||
const [startLine, endLine] = foundInCode
|
||||
|
||||
if (block.state === 'writingOriginal') continue
|
||||
|
||||
// if should add new diffarea
|
||||
if (blockNum > diffareaidOfBlockNum.length) {
|
||||
const adding: Omit<DiffZone, 'diffareaid'> = {
|
||||
|
|
@ -1443,13 +1405,11 @@ Please output SEARCH/REPLACE blocks to make the change. Return ONLY your suggest
|
|||
|
||||
})
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
}
|
||||
|
||||
|
||||
|
||||
|
||||
private _initializeRewriteStream(opts: StartApplyingOpts): DiffZone | undefined {
|
||||
|
||||
const { from } = opts
|
||||
|
|
|
|||
|
|
@ -7,7 +7,7 @@ import React, { JSX, useCallback, useEffect, useState } from 'react'
|
|||
import { marked, MarkedToken, Token } from 'marked'
|
||||
import { BlockCode } from './BlockCode.js'
|
||||
import { useAccessor, useChatThreadsState, useChatThreadsStreamState } from '../util/services.js'
|
||||
import { ChatLocation, getApplyBoxId, } from '../../../searchAndReplaceService.js'
|
||||
import { ChatMessageLocation, } from '../../../searchAndReplaceService.js'
|
||||
import { nameToVscodeLanguage } from '../../../helpers/detectLanguage.js'
|
||||
|
||||
|
||||
|
|
@ -19,6 +19,16 @@ enum CopyButtonState {
|
|||
|
||||
const COPY_FEEDBACK_TIMEOUT = 1000 // amount of time to say 'Copied!'
|
||||
|
||||
|
||||
|
||||
type ApplyBoxLocation = ChatMessageLocation & { tokenIdx: number }
|
||||
|
||||
const getApplyBoxId = ({ threadId, messageIdx, tokenIdx }: ApplyBoxLocation) => {
|
||||
return `${threadId}-${messageIdx}-${tokenIdx}`
|
||||
}
|
||||
|
||||
|
||||
|
||||
const ApplyButtonsOnHover = ({ applyStr, applyBoxId }: { applyStr: string, applyBoxId: string }) => {
|
||||
const accessor = useAccessor()
|
||||
|
||||
|
|
@ -47,9 +57,9 @@ const ApplyButtonsOnHover = ({ applyStr, applyBoxId }: { applyStr: string, apply
|
|||
const onApply = useCallback(() => {
|
||||
|
||||
inlineDiffService.startApplying({
|
||||
from: 'Chat',
|
||||
from: 'ClickApply',
|
||||
type: 'searchReplace',
|
||||
applyStr,
|
||||
applyBoxId,
|
||||
})
|
||||
metricsService.capture('Apply Code', { length: applyStr.length }) // capture the length only
|
||||
}, [metricsService, inlineDiffService, applyStr])
|
||||
|
|
@ -87,7 +97,7 @@ export const CodeSpan = ({ children, className }: { children: React.ReactNode, c
|
|||
</code>
|
||||
}
|
||||
|
||||
const RenderToken = ({ token, nested = false, noSpace = false, chatLocation, tokenId = '', }: { token: Token | string, nested?: boolean, noSpace?: boolean, chatLocation?: ChatLocation, tokenId?: string, }): JSX.Element => {
|
||||
const RenderToken = ({ token, nested = false, noSpace = false, chatMessageLocation: chatLocation, tokenIdx }: { token: Token | string, nested?: boolean, noSpace?: boolean, chatMessageLocation?: ChatMessageLocation, tokenIdx: number }): JSX.Element => {
|
||||
|
||||
|
||||
// deal with built-in tokens first (assume marked token)
|
||||
|
|
@ -104,7 +114,7 @@ const RenderToken = ({ token, nested = false, noSpace = false, chatLocation, tok
|
|||
const applyBoxId = getApplyBoxId({
|
||||
threadId: chatLocation!.threadId,
|
||||
messageIdx: chatLocation!.messageIdx,
|
||||
codeblockId: tokenId,
|
||||
tokenIdx: tokenIdx,
|
||||
})
|
||||
|
||||
return <BlockCode
|
||||
|
|
@ -196,7 +206,7 @@ const RenderToken = ({ token, nested = false, noSpace = false, chatLocation, tok
|
|||
if (t.type === "paragraph") {
|
||||
const contents = <>
|
||||
{t.tokens.map((token, index) => (
|
||||
<RenderToken key={index} token={token} tokenId={`${tokenId}-${index}`} /> // assign a unique tokenId to nested components
|
||||
<RenderToken key={index} token={token} tokenIdx={index} /> // assign a unique tokenId to nested components
|
||||
))}
|
||||
</>
|
||||
if (nested) return contents
|
||||
|
|
@ -279,12 +289,12 @@ const RenderToken = ({ token, nested = false, noSpace = false, chatLocation, tok
|
|||
)
|
||||
}
|
||||
|
||||
export const ChatMarkdownRender = ({ string, nested = false, noSpace, chatLocation }: { string: string, nested?: boolean, noSpace?: boolean, chatLocation?: ChatLocation }) => {
|
||||
export const ChatMarkdownRender = ({ string, nested = false, noSpace, chatMessageLocation }: { string: string, nested?: boolean, noSpace?: boolean, chatMessageLocation?: ChatMessageLocation }) => {
|
||||
const tokens = marked.lexer(string); // https://marked.js.org/using_pro#renderer
|
||||
return (
|
||||
<>
|
||||
{tokens.map((token, index) => (
|
||||
<RenderToken key={index} token={token} nested={nested} noSpace={noSpace} chatLocation={chatLocation} />
|
||||
<RenderToken key={index} token={token} nested={nested} noSpace={noSpace} chatMessageLocation={chatMessageLocation} tokenIdx={index} />
|
||||
))}
|
||||
</>
|
||||
)
|
||||
|
|
|
|||
|
|
@ -59,6 +59,7 @@ export const QuickEditChat = ({
|
|||
|
||||
const id = inlineDiffsService.startApplying({
|
||||
from: 'QuickEdit',
|
||||
type:'rewrite',
|
||||
diffareaid: diffareaid,
|
||||
})
|
||||
setCurrentlyStreamingDiffZone(id ?? null)
|
||||
|
|
|
|||
|
|
@ -24,7 +24,7 @@ import { VOID_OPEN_SETTINGS_ACTION_ID } from '../../../voidSettingsPane.js';
|
|||
import { Pencil, X } from 'lucide-react';
|
||||
import { FeatureName, isFeatureNameDisabled } from '../../../../../../../workbench/contrib/void/common/voidSettingsTypes.js';
|
||||
import { WarningBox } from '../void-settings-tsx/WarningBox.js';
|
||||
import { ChatLocation } from '../../../searchAndReplaceService.js';
|
||||
import { ChatMessageLocation } from '../../../searchAndReplaceService.js';
|
||||
|
||||
|
||||
|
||||
|
|
@ -675,12 +675,12 @@ const ChatBubble = ({ chatMessage, isLoading, messageIdx }: { chatMessage: ChatM
|
|||
else if (role === 'assistant') {
|
||||
const thread = chatThreadsService.getCurrentThread()
|
||||
|
||||
const chatLocation: ChatLocation = {
|
||||
const chatMessageLocation: ChatMessageLocation = {
|
||||
threadId: thread.id,
|
||||
messageIdx: messageIdx!,
|
||||
}
|
||||
|
||||
chatbubbleContents = <ChatMarkdownRender string={chatMessage.displayContent ?? ''} chatLocation={chatLocation} />
|
||||
chatbubbleContents = <ChatMarkdownRender string={chatMessage.displayContent ?? ''} chatMessageLocation={chatMessageLocation} />
|
||||
}
|
||||
|
||||
return <div
|
||||
|
|
|
|||
|
|
@ -10,16 +10,11 @@ import { createDecorator } from '../../../../platform/instantiation/common/insta
|
|||
|
||||
|
||||
|
||||
export type ChatLocation = {
|
||||
export type ChatMessageLocation = {
|
||||
threadId: string;
|
||||
messageIdx: number;
|
||||
}
|
||||
|
||||
export type ApplyBoxLocation = ChatLocation & { codeblockId: string }
|
||||
|
||||
export const getApplyBoxId = ({ threadId, messageIdx, codeblockId }: ApplyBoxLocation) => {
|
||||
return `${threadId}-${messageIdx}-${codeblockId}}`
|
||||
}
|
||||
|
||||
export type SearchAndReplaceBlock = {
|
||||
search: string;
|
||||
|
|
|
|||
Loading…
Reference in a new issue