make everything revolve around model, not editor

This commit is contained in:
Andrew Pareles 2024-11-13 23:58:28 -08:00
parent 1cf371fa8b
commit 1fe12d2bfd
2 changed files with 180 additions and 637 deletions

View file

@ -1,443 +0,0 @@
import * as vscode from 'vscode';
import { findDiffs } from './findDiffs';
import { DiffArea, Diff } from '../common/shared_types';
// TODO in theory this should be disposed
const lightGrayDecoration = vscode.window.createTextEditorDecorationType({
backgroundColor: 'rgba(218 218 218 / .2)',
isWholeLine: true,
})
const darkGrayDecoration = vscode.window.createTextEditorDecorationType({
backgroundColor: 'rgb(148 148 148 / .2)',
isWholeLine: true,
})
// responsible for displaying diffs and showing accept/reject buttons
export class DiffProvider implements vscode.CodeLensProvider {
private _originalFileOfDocument: { [docUriStr: string]: string } = {}
private _diffAreasOfDocument: { [docUriStr: string]: DiffArea[] } = {}
private _diffsOfDocument: { [docUriStr: string]: Diff[] } = {}
private _diffareaidPool = 0
private _diffidPool = 0
private _extensionUri: vscode.Uri
// used internally by vscode
private _onDidChangeCodeLenses: vscode.EventEmitter<void> = new vscode.EventEmitter<void>(); // signals a UI refresh on .fire() events
public readonly onDidChangeCodeLenses: vscode.Event<void> = this._onDidChangeCodeLenses.event;
// used internally by vscode
public provideCodeLenses(document: vscode.TextDocument, token: vscode.CancellationToken): vscode.ProviderResult<vscode.CodeLens[]> {
const docUriStr = document.uri.toString()
return this._diffsOfDocument[docUriStr]?.flatMap(diff => diff.lenses) ?? []
}
// declared by us, registered with vscode.languages.registerCodeLensProvider()
constructor(context: vscode.ExtensionContext) {
this._extensionUri = context.extensionUri
console.log('Creating DisplayChangesProvider')
// // this acts as a useEffect every time text changes
// vscode.workspace.onDidChangeTextDocument((e) => {
// const editor = vscode.window.activeTextEditor
// if (!editor) return
// const docUriStr = editor.document.uri.toString()
// const changes = e.contentChanges.map(c => ({ startLine: c.range.start.line, endLine: c.range.end.line, text: c.text, }))
// // on user change, grow/shrink/merge/delete diff areas
// this.resizeDiffAreas(docUriStr, changes, 'currentFile')
// // refresh the diffAreas
// this.refreshStylesAndDiffs(docUriStr)
// })
}
// used by us only
// public createDiffArea(uri: vscode.Uri, partialDiffArea: Omit<DiffArea, 'diffareaid'>, originalFile: string) {
// const uriStr = uri.toString()
// this._originalFileOfDocument[uriStr] = originalFile
// // make sure array is defined
// if (!this._diffAreasOfDocument[uriStr]) this._diffAreasOfDocument[uriStr] = []
// // remove all diffAreas that the new `diffArea` is overlapping with
// this._diffAreasOfDocument[uriStr] = this._diffAreasOfDocument[uriStr].filter(da => {
// const noOverlap = da.startLine > partialDiffArea.endLine || da.endLine < partialDiffArea.startLine
// if (!noOverlap) return false
// return true
// })
// // add `diffArea` to storage
// const diffArea = {
// ...partialDiffArea,
// diffareaid: this._diffareaidPool
// }
// this._diffAreasOfDocument[uriStr].push(diffArea)
// this._diffareaidPool += 1
// return diffArea
// }
// used by us only
// changes the start/line locations based on the changes that were recently made. does not change any of the diffs in the diff areas
// changes tells us how many lines were inserted/deleted so we can grow/shrink the diffAreas accordingly
// public resizeDiffAreas(docUriStr: string, changes: { text: string, startLine: number, endLine: number }[], changesTo: 'originalFile' | 'currentFile') {
// const diffAreas = this._diffAreasOfDocument[docUriStr] || []
// let endLine: 'originalEndLine' | 'endLine'
// let startLine: 'originalStartLine' | 'startLine'
// if (changesTo === 'originalFile') {
// endLine = 'originalEndLine' as const
// startLine = 'originalStartLine' as const
// } else {
// endLine = 'endLine' as const
// startLine = 'startLine' as const
// }
// for (const change of changes) {
// // here, `change.range` is the range of the original file that gets replaced with `change.text`
// // compute net number of newlines lines that were added/removed
// const numNewLines = (change.text.match(/\n/g) || []).length
// const numLineDeletions = change.endLine - change.startLine
// const deltaNewlines = numNewLines - numLineDeletions
// // compute overlap with each diffArea and shrink/elongate the diffArea accordingly
// for (const diffArea of diffAreas) {
// // if the change is fully within the diffArea, elongate it by the delta amount of newlines
// if (change.startLine >= diffArea[startLine] && change.endLine <= diffArea[endLine]) {
// diffArea[endLine] += deltaNewlines
// }
// // check if the `diffArea` was fully deleted and remove it if so
// if (diffArea[startLine] > diffArea[endLine]) {
// //remove it
// const index = diffAreas.findIndex(da => da === diffArea)
// diffAreas.splice(index, 1)
// }
// // TODO handle other cases where eg. the change overlaps many diffAreas
// }
// // if a diffArea is below the last character of the change, shift the diffArea up/down by the delta amount of newlines
// for (const diffArea of diffAreas) {
// if (diffArea[startLine] > change.endLine) {
// diffArea[startLine] += deltaNewlines
// diffArea[endLine] += deltaNewlines
// }
// }
// // TODO merge any diffAreas if they overlap with each other as a result from the shift
// }
// }
// // used by us only
// // refreshes all the diffs inside each diff area, and refreshes the styles
// public refreshStylesAndDiffs(docUriStr: string) {
// const editor = vscode.window.activeTextEditor // TODO the editor should be that of `docUri` and not necessarily the current editor
// if (!editor) {
// console.log('Error: No active editor!')
// return;
// }
// const originalFile = this._originalFileOfDocument[docUriStr]
// if (!originalFile) {
// console.log('Error: No original file!')
// return;
// }
// const diffAreas = this._diffAreasOfDocument[docUriStr] || []
// // reset all diffs (we update them below)
// this._diffsOfDocument[docUriStr] = []
// // TODO!!!!
// // vscode.languages.clearInlineDiffs(editor)
// // for each diffArea
// for (const diffArea of diffAreas) {
// // get code inside of diffArea
// const originalCode = originalFile.split('\n').slice(diffArea.originalStartLine, diffArea.originalEndLine + 1).join('\n')
// const currentCode = editor.document.getText(new vscode.Range(diffArea.startLine, 0, diffArea.endLine, Number.MAX_SAFE_INTEGER)).replace(/\r\n/g, '\n')
// // compute the diffs
// const diffs = findDiffs(originalCode, currentCode)
// // add the diffs to `this._diffsOfDocument[docUriStr]`
// // if no diffs, set diffs to []
// if (!this._diffsOfDocument[docUriStr])
// this._diffsOfDocument[docUriStr] = []
// // add each diff and its codelens to the document
// for (let i = diffs.length - 1; i > -1; i -= 1) {
// let suggestedDiff = diffs[i]
// this._diffsOfDocument[docUriStr].push({
// ...suggestedDiff,
// diffid: this._diffidPool,
// // originalCode: suggestedDiff.deletedText,
// lenses: [
// new vscode.CodeLens(suggestedDiff.range, { title: 'Accept', command: 'void.acceptDiff', arguments: [{ diffid: this._diffidPool, diffareaid: diffArea.diffareaid }] }),
// new vscode.CodeLens(suggestedDiff.range, { title: 'Reject', command: 'void.rejectDiff', arguments: [{ diffid: this._diffidPool, diffareaid: diffArea.diffareaid }] })
// ]
// });
// vscode.languages.addInlineDiff(editor, suggestedDiff.originalCode, suggestedDiff.range)
// this._diffidPool += 1
// }
// }
// // for each diffArea, highlight its sweepIndex in dark gray
// editor.setDecorations(
// darkGrayDecoration,
// (this._diffAreasOfDocument[docUriStr]
// .filter(diffArea => diffArea.sweepIndex !== null)
// .map(diffArea => {
// let s = diffArea.sweepIndex!
// return new vscode.Range(s, 0, s, 0)
// })
// )
// )
// // for each diffArea, highlight sweepIndex+1...end in light gray
// editor.setDecorations(
// lightGrayDecoration,
// (this._diffAreasOfDocument[docUriStr]
// .filter(diffArea => diffArea.sweepIndex !== null)
// .map(diffArea => {
// return new vscode.Range(diffArea.sweepIndex! + 1, 0, diffArea.endLine, 0)
// })
// )
// )
// // update code lenses
// this._onDidChangeCodeLenses.fire()
// }
// // called on void.acceptDiff
// public async acceptDiff({ diffid, diffareaid }: { diffid: number, diffareaid: number }) {
// const editor = vscode.window.activeTextEditor
// if (!editor)
// return
// const docUriStr = editor.document.uri.toString()
// const diffIdx = this._diffsOfDocument[docUriStr].findIndex(diff => diff.diffid === diffid);
// if (diffIdx === -1) { console.error('Error: DiffID could not be found: ', diffid, diffareaid, this._diffsOfDocument[docUriStr], this._diffAreasOfDocument[docUriStr]); return; }
// const diffareaIdx = this._diffAreasOfDocument[docUriStr].findIndex(diff => diff.diffareaid === diffareaid);
// if (diffareaIdx === -1) { console.error('Error: DiffAreaID could not be found: ', diffid, diffareaid, this._diffsOfDocument[docUriStr], this._diffAreasOfDocument[docUriStr]); return; }
// const diff = this._diffsOfDocument[docUriStr][diffIdx]
// const originalFile = this._originalFileOfDocument[docUriStr]
// const currentFile = await readFileContentOfUri(editor.document.uri)
// // Fixed: Handle newlines properly by splitting into lines and joining with proper newlines
// const originalLines = originalFile.split('\n');
// const currentLines = currentFile.split('\n');
// // Get the changed lines from current file
// const changedLines = currentLines.slice(diff.range.start.line, diff.range.end.line + 1);
// // Create new original file content by replacing the affected lines
// const newOriginalLines = [
// ...originalLines.slice(0, diff.originalRange.start.line),
// ...changedLines,
// ...originalLines.slice(diff.originalRange.end.line + 1)
// ];
// this._originalFileOfDocument[docUriStr] = newOriginalLines.join('\n');
// // Update diff areas based on the change
// this.resizeDiffAreas(docUriStr, [{
// text: changedLines.join('\n'),
// startLine: diff.originalRange.start.line,
// endLine: diff.originalRange.end.line
// }], 'originalFile')
// // Check if diffArea should be removed
// const diffArea = this._diffAreasOfDocument[docUriStr][diffareaIdx]
// const currentArea = currentLines.slice(diffArea.startLine, diffArea.endLine + 1).join('\n')
// const originalArea = newOriginalLines.slice(diffArea.originalStartLine, diffArea.originalEndLine + 1).join('\n')
// if (originalArea === currentArea) {
// const index = this._diffAreasOfDocument[docUriStr].findIndex(da => da.diffareaid === diffArea.diffareaid)
// this._diffAreasOfDocument[docUriStr].splice(index, 1)
// }
// this.refreshStylesAndDiffs(docUriStr)
// }
// // called on void.rejectDiff
// public async rejectDiff({ diffid, diffareaid }: { diffid: number, diffareaid: number }) {
// const editor = vscode.window.activeTextEditor
// if (!editor)
// return
// const docUriStr = editor.document.uri.toString()
// const diffIdx = this._diffsOfDocument[docUriStr].findIndex(diff => diff.diffid === diffid);
// if (diffIdx === -1) { console.error('Error: DiffID could not be found: ', diffid, diffareaid, this._diffsOfDocument[docUriStr], this._diffAreasOfDocument[docUriStr]); return; }
// const diffareaIdx = this._diffAreasOfDocument[docUriStr].findIndex(diff => diff.diffareaid === diffareaid);
// if (diffareaIdx === -1) { console.error('Error: DiffAreaID could not be found: ', diffid, diffareaid, this._diffsOfDocument[docUriStr], this._diffAreasOfDocument[docUriStr]); return; }
// const diff = this._diffsOfDocument[docUriStr][diffIdx]
// // Apply the rejection by replacing with original code
// // we don't have to edit the original or final file; just do a workspace edit so the code equals the original code
// const workspaceEdit = new vscode.WorkspaceEdit();
// workspaceEdit.replace(editor.document.uri, diff.range, diff.originalCode)
// await vscode.workspace.applyEdit(workspaceEdit)
// // Check if diffArea should be removed
// const originalFile = this._originalFileOfDocument[docUriStr]
// const currentFile = await readFileContentOfUri(editor.document.uri)
// const diffArea = this._diffAreasOfDocument[docUriStr][diffareaIdx]
// const currentLines = currentFile.split('\n');
// const originalLines = originalFile.split('\n');
// const currentArea = currentLines.slice(diffArea.startLine, diffArea.endLine + 1).join('\n')
// const originalArea = originalLines.slice(diffArea.originalStartLine, diffArea.originalEndLine + 1).join('\n')
// if (originalArea === currentArea) {
// const index = this._diffAreasOfDocument[docUriStr].findIndex(da => da.diffareaid === diffArea.diffareaid)
// this._diffAreasOfDocument[docUriStr].splice(index, 1)
// }
// this.refreshStylesAndDiffs(docUriStr)
// }
// async startStreamingInDiffArea({ docUri, oldFileStr, diffRepr, diffArea, voidConfig, abortRef }: { docUri: vscode.Uri, oldFileStr: string, diffRepr: string, voidConfig: VoidConfig, diffArea: DiffArea, abortRef: AbortRef }) {
// const promptContent = `\
// ORIGINAL_FILE
// \`\`\`
// ${oldFileStr}
// \`\`\`
// DIFF
// \`\`\`
// ${diffRepr}
// \`\`\`
// INSTRUCTIONS
// Please finish writing the new file by applying the diff to the original file. Return ONLY the completion of the file, without any explanation.
// `
// // make LLM complete the file to include the diff
// await new Promise<void>((resolve, reject) => {
// sendLLMMessage({
// logging: { loggingName: 'streamChunk' },
// messages: [
// { role: 'system', content: writeFileWithDiffInstructions, },
// // TODO include more context too
// { role: 'user', content: promptContent, }
// ],
// onText: (newText, fullText) => {
// this._updateStream(docUri.toString(), diffArea, fullText)
// },
// onFinalMessage: (fullText) => {
// this._updateStream(docUri.toString(), diffArea, fullText)
// resolve();
// },
// onError: (e) => {
// console.error('Error rewriting file with diff', e);
// resolve();
// },
// voidConfig,
// abortRef,
// })
// })
// }
// // used by us only
// private _updateStream = throttle(async (docUriStr: string, diffArea: DiffArea, newDiffAreaCode: string) => {
// const editor = vscode.window.activeTextEditor // TODO the editor should be that of `docUri` and not necessarily the current editor
// if (!editor) {
// console.log('Error: No active editor!')
// return;
// }
// // original code all diffs are based on in the code
// const originalDiffAreaCode = (this._originalFileOfDocument[docUriStr] || '').split('\n').slice(diffArea.originalStartLine, diffArea.originalEndLine + 1).join('\n')
// // figure out where to highlight based on where the AI is in the stream right now, use the last diff in findDiffs to figure that out
// const diffs = findDiffs(originalDiffAreaCode, newDiffAreaCode)
// const lastDiff = diffs?.[diffs.length - 1] ?? null
// // these are two different coordinate systems - new and old line number
// let newFileEndLine: number // get new[0...newStoppingPoint] with line=newStoppingPoint highlighted
// let oldFileStartLine: number // get original[oldStartingPoint...]
// if (!lastDiff) {
// // if the writing is identical so far, display no changes
// newFileEndLine = 0
// oldFileStartLine = 0
// }
// else {
// if (lastDiff.type === 'insertion') {
// newFileEndLine = lastDiff.range.end.line
// oldFileStartLine = lastDiff.originalRange.start.line
// }
// else if (lastDiff.type === 'deletion') {
// newFileEndLine = lastDiff.range.start.line
// oldFileStartLine = lastDiff.originalRange.start.line
// }
// else if (lastDiff.type === 'edit') {
// newFileEndLine = lastDiff.range.end.line
// oldFileStartLine = lastDiff.originalRange.start.line
// }
// else {
// throw new Error(`updateStream: diff.type not recognized: ${lastDiff.type}`)
// }
// }
// // display
// const newFileTop = newDiffAreaCode.split('\n').slice(0, newFileEndLine + 1).join('\n')
// const oldFileBottom = originalDiffAreaCode.split('\n').slice(oldFileStartLine + 1, Infinity).join('\n')
// let newCode = `${newFileTop}\n${oldFileBottom}`
// diffArea.sweepIndex = newFileEndLine
// // replace oldDACode with newDACode with a vscode edit
// const workspaceEdit = new vscode.WorkspaceEdit();
// const diffareaRange = new vscode.Range(diffArea.startLine, 0, diffArea.endLine, Number.MAX_SAFE_INTEGER)
// workspaceEdit.replace(editor.document.uri, diffareaRange, newCode)
// await vscode.workspace.applyEdit(workspaceEdit)
// }, THROTTLE_TIME)
}

View file

@ -14,10 +14,10 @@ import { URI } from '../../../../base/common/uri.js';
import { IVoidConfigStateService } from './registerConfig.js';
import { writeFileWithDiffInstructions } from './prompt/systemPrompts.js';
import { findDiffs } from './findDiffs.js';
import { EndOfLinePreference, IModelDeltaDecoration } from '../../../../editor/common/model.js';
import { EndOfLinePreference, IModelDeltaDecoration, ITextModel } from '../../../../editor/common/model.js';
import { IRange } from '../../../../editor/common/core/range.js';
import { EditorOption } from '../../../../editor/common/config/editorOptions.js';
import { IModelService } from '../../../../editor/common/services/model.js';
// read files from VSCode
@ -33,8 +33,6 @@ export const VSReadFile = async (fileService: IFileService, uri: URI): Promise<s
}
export type Diff = {
diffid: number,
diffareaid: number, // the diff area this diff belongs to, "computed"
@ -66,8 +64,7 @@ type DiffArea = {
endLine: number,
_diffs: Diff[],
_modelid: string, // model id (document where this diffarea lives)
_modeluri: URI,
_model: ITextModel, // the model or "document" this diffarea lives on
_generationid: number,
_sweepLine: number | null,
_sweepCol: number | null,
@ -132,7 +129,6 @@ class InlineDiffsService extends Disposable implements IInlineDiffsService {
@IUndoRedoService private readonly _undoRedoService: IUndoRedoService, // undoRedo service is the history of pressing ctrl+z
@IBulkEditService private readonly _bulkEditService: IBulkEditService,
@IFileService private readonly _fileService: IFileService,
@IModelService private readonly _modelService: IModelService,
) {
super();
@ -141,84 +137,94 @@ class InlineDiffsService extends Disposable implements IInlineDiffsService {
private _addInlineDiffZone = (editor: ICodeEditor, originalText: string, greenRange: IRange) => {
private _addInlineDiffZone = (model: ITextModel, originalText: string, greenRange: IRange) => {
// green decoration and gutter decoration
const greenDecoration: IModelDeltaDecoration[] = [{
range: greenRange,
options: {
className: 'line-insert', // .monaco-editor .line-insert
description: 'line-insert',
isWholeLine: true,
minimap: {
color: { id: 'minimapGutter.addedBackground' },
position: 2
},
overviewRuler: {
color: { id: 'editorOverviewRuler.addedForeground' },
position: 7
const _addInlineDiffZoneToEditor = (editor: ICodeEditor) => {
// green decoration and gutter decoration
const greenDecoration: IModelDeltaDecoration[] = [{
range: greenRange,
options: {
className: 'line-insert', // .monaco-editor .line-insert
description: 'line-insert',
isWholeLine: true,
minimap: {
color: { id: 'minimapGutter.addedBackground' },
position: 2
},
overviewRuler: {
color: { id: 'editorOverviewRuler.addedForeground' },
position: 7
}
}
}
}];
const decorationIds = editor.deltaDecorations([], greenDecoration)
}];
const decorationIds = editor.deltaDecorations([], greenDecoration)
// red in a view zone
let zoneId: string | null = null
editor.changeViewZones(accessor => {
// Get the editor's font info
const fontInfo = editor.getOption(EditorOption.fontInfo);
const domNode = document.createElement('div');
domNode.className = 'monaco-editor view-zones line-delete monaco-mouse-cursor-text';
domNode.style.fontSize = `${fontInfo.fontSize}px`;
domNode.style.fontFamily = fontInfo.fontFamily;
domNode.style.lineHeight = `${fontInfo.lineHeight}px`;
// div
const lineContent = document.createElement('div');
lineContent.className = 'view-line'; // .monaco-editor .inline-deleted-text
// span
const contentSpan = document.createElement('span');
// span
const codeSpan = document.createElement('span');
codeSpan.className = 'mtk1'; // char-delete
codeSpan.textContent = originalText;
// Mount
contentSpan.appendChild(codeSpan);
lineContent.appendChild(contentSpan);
domNode.appendChild(lineContent);
// Gutter (thing to the left)
const gutterDiv = document.createElement('div');
gutterDiv.className = 'inline-diff-gutter';
const minusDiv = document.createElement('div');
minusDiv.className = 'inline-diff-deleted-gutter';
// minusDiv.textContent = '-';
gutterDiv.appendChild(minusDiv);
const viewZone: IViewZone = {
afterLineNumber: greenRange.startLineNumber - 1,
heightInLines: originalText.split('\n').length + 1,
domNode: domNode,
suppressMouseDown: true,
marginDomNode: gutterDiv
};
zoneId = accessor.addZone(viewZone);
// editor.layout();
// this._diffZones.set(editor, [zoneId]);
});
const dispose = () => {
editor.deltaDecorations(decorationIds, []);
// red in a view zone
let zoneId: string | null = null
editor.changeViewZones(accessor => {
if (zoneId) accessor.removeZone(zoneId);
// Get the editor's font info
const fontInfo = editor.getOption(EditorOption.fontInfo);
const domNode = document.createElement('div');
domNode.className = 'monaco-editor view-zones line-delete monaco-mouse-cursor-text';
domNode.style.fontSize = `${fontInfo.fontSize}px`;
domNode.style.fontFamily = fontInfo.fontFamily;
domNode.style.lineHeight = `${fontInfo.lineHeight}px`;
// div
const lineContent = document.createElement('div');
lineContent.className = 'view-line'; // .monaco-editor .inline-deleted-text
// span
const contentSpan = document.createElement('span');
// span
const codeSpan = document.createElement('span');
codeSpan.className = 'mtk1'; // char-delete
codeSpan.textContent = originalText;
// Mount
contentSpan.appendChild(codeSpan);
lineContent.appendChild(contentSpan);
domNode.appendChild(lineContent);
// Gutter (thing to the left)
const gutterDiv = document.createElement('div');
gutterDiv.className = 'inline-diff-gutter';
const minusDiv = document.createElement('div');
minusDiv.className = 'inline-diff-deleted-gutter';
// minusDiv.textContent = '-';
gutterDiv.appendChild(minusDiv);
const viewZone: IViewZone = {
afterLineNumber: greenRange.startLineNumber - 1,
heightInLines: originalText.split('\n').length + 1,
domNode: domNode,
suppressMouseDown: true,
marginDomNode: gutterDiv
};
zoneId = accessor.addZone(viewZone);
// editor.layout();
// this._diffZones.set(editor, [zoneId]);
});
const dispose = () => {
editor.deltaDecorations(decorationIds, []);
editor.changeViewZones(accessor => {
if (zoneId) accessor.removeZone(zoneId);
});
}
return dispose
}
const editors = this._editorService.listCodeEditors().filter(editor => editor.getModel()?.id === model.id)
const disposeFns = editors.map(editor => _addInlineDiffZoneToEditor(editor))
const dispose = () => {
disposeFns.forEach(fn => fn())
}
return dispose
@ -287,16 +293,15 @@ class InlineDiffsService extends Disposable implements IInlineDiffsService {
private _deleteDiffArea(diffArea: DiffArea) {
this._deleteDiffs(diffArea)
delete this.diffAreaOfId[diffArea.diffareaid]
this.diffAreasOfModelId[diffArea._modelid].delete(diffArea.diffareaid.toString())
this.diffAreasOfModelId[diffArea._model.id].delete(diffArea.diffareaid.toString())
}
// for every diffarea in this document, recompute its diffs and restyle it (the two are coupled)
private _refreshAllDiffsAndStyles(editor: ICodeEditor) {
const model = editor.getModel()
if (!model) return
private _refreshAllDiffsAndStyles(model: ITextModel) {
const modelid = model.id
@ -322,7 +327,7 @@ class InlineDiffsService extends Disposable implements IInlineDiffsService {
for (let computedDiff of computedDiffs) {
// add the view zone
const greenRange: IRange = { startLineNumber: diffArea.startLine, startColumn: 0, endLineNumber: diffArea.endLine, endColumn: Number.MAX_SAFE_INTEGER, }
const dispose = this._addInlineDiffZone(editor, computedDiff.originalCode, greenRange)
const dispose = this._addInlineDiffZone(model, computedDiff.originalCode, greenRange)
// create a Diff of it
const diffid = this._diffidPool++
@ -339,34 +344,45 @@ class InlineDiffsService extends Disposable implements IInlineDiffsService {
}
}
private _refreshSweepStyles(editor: ICodeEditor) {
private _refreshSweepStyles(model: ITextModel) {
const model = editor.getModel()
if (!model) return
const modelid = model.id
// const model = editor.getModel()
// if (!model) return
// const modelid = model.id
// for each diffArea, highlight its sweepIndex in dark gray
editor.setDecorations(
darkGrayDecoration,
(this._diffAreasOfDocument[modelid]
.filter(diffArea => diffArea.sweepIndex !== null)
.map(diffArea => {
let s = diffArea.sweepIndex!
return new vscode.Range(s, 0, s, 0)
})
)
)
// const lightGrayDecoration = vscode.window.createTextEditorDecorationType({
// backgroundColor: 'rgba(218 218 218 / .2)',
// isWholeLine: true,
// })
// const darkGrayDecoration = vscode.window.createTextEditorDecorationType({
// backgroundColor: 'rgb(148 148 148 / .2)',
// isWholeLine: true,
// })
// for each diffArea, highlight sweepIndex+1...end in light gray
editor.setDecorations(
lightGrayDecoration,
(this._diffAreasOfDocument[modelid]
.filter(diffArea => diffArea.sweepIndex !== null)
.map(diffArea => {
return new vscode.Range(diffArea.sweepIndex! + 1, 0, diffArea.endLine, 0)
})
)
)
// // for each diffArea, highlight its sweepIndex in dark gray
// editor.setDecorations(
// darkGrayDecoration,
// (this._diffAreasOfDocument[modelid]
// .filter(diffArea => diffArea.sweepIndex !== null)
// .map(diffArea => {
// let s = diffArea.sweepIndex!
// return new vscode.Range(s, 0, s, 0)
// })
// )
// )
// // for each diffArea, highlight sweepIndex+1...end in light gray
// editor.setDecorations(
// lightGrayDecoration,
// (this._diffAreasOfDocument[modelid]
// .filter(diffArea => diffArea.sweepIndex !== null)
// .map(diffArea => {
// return new vscode.Range(diffArea.sweepIndex! + 1, 0, diffArea.endLine, 0)
// })
// )
// )
}
@ -427,10 +443,7 @@ class InlineDiffsService extends Disposable implements IInlineDiffsService {
private _registeredListeners = new Set<string>() // set of model IDs
private _registerTextChangeListener(editor: ICodeEditor) {
const model = editor.getModel()
if (!model) return
private _registerTextChangeListener(model: ITextModel) {
const modelid = model.id
if (this._registeredListeners.has(modelid)) return
@ -441,7 +454,7 @@ class InlineDiffsService extends Disposable implements IInlineDiffsService {
model.onDidChangeContent(e => {
const changes = e.changes.map(c => ({ startLine: c.range.startLineNumber, endLine: c.range.endLineNumber, text: c.text, }))
this._resizeOnTextChange(modelid, changes, 'currentFile')
this._refreshAllDiffsAndStyles(editor)
this._refreshAllDiffsAndStyles(model)
})
)
@ -450,29 +463,14 @@ class InlineDiffsService extends Disposable implements IInlineDiffsService {
this._registeredListeners.delete(modelid)
})
)
// listen for document changes
// // // this acts as a useEffect every time text changes
// vscode.workspace.onDidChangeTextDocument((e) => {
// const editor = vscode.window.activeTextEditor
// if (!editor) return
// const modelid = editor.document.uri.toString()
// const changes = e.contentChanges.map(c => ({ startLine: c.range.start.line, endLine: c.range.end.line, text: c.text, }))
// // on user change, grow/shrink/merge/delete diff areas
// // refresh the diffAreas
// this._refreshStylesAndDiffs(modelid)
// })
}
@throttle(100)
private async _updateDiffAreaText(diffArea: DiffArea, llmCodeSoFar: string, streamingGroup: UndoRedoGroup) {
private async _updateDiffAreaText(diffArea: DiffArea, llmCodeSoFar: string) {
// clear all diffs in this diffarea and recompute them
const uri = diffArea._modeluri
const modelid = diffArea._modelid
const modelid = diffArea._model.id
if (this.streamingStateOfModelId[modelid].type !== 'streaming')
return
@ -518,18 +516,14 @@ class InlineDiffsService extends Disposable implements IInlineDiffsService {
let newCode = `${newFileTop}\n${oldFileBottom}`
diffArea._sweepLine = newFileEndLine
this._bulkEditService.apply(
[new ResourceTextEdit(uri, {
range: {
startLineNumber: diffArea.startLine,
startColumn: 0,
endLineNumber: diffArea.endLine,
endColumn: Number.MAX_SAFE_INTEGER,
},
// applies edits without adding them to undo/redo stack
const model = diffArea._model
if (!model.isDisposed())
model.applyEdits([{
range: { startLineNumber: diffArea.startLine, startColumn: 0, endLineNumber: diffArea.endLine, endColumn: Number.MAX_SAFE_INTEGER, },
text: newCode
})],
// count all changes towards the group
{ undoRedoGroupId: streamingGroup.id });
}])
// TODO resize diffAreas?? Or is this handled already by the listener?
}
@ -537,11 +531,7 @@ class InlineDiffsService extends Disposable implements IInlineDiffsService {
private async _initializeStream(editor: ICodeEditor, diffRepr: string, streamingGroup: UndoRedoGroup) {
// do all the checks first
const model = editor.getModel()
if (!model) return
private async _initializeStream(model: ITextModel, diffRepr: string, streamingGroup: UndoRedoGroup) {
const uri = model.uri
const modelid = uri.toString()
@ -566,7 +556,7 @@ class InlineDiffsService extends Disposable implements IInlineDiffsService {
}
// start listening for text changes
this._registerTextChangeListener(editor)
this._registerTextChangeListener(model)
// add to history
const { finishHistorySnapshot } = this._addToHistory(model.uri, streamingGroup)
@ -582,8 +572,7 @@ class InlineDiffsService extends Disposable implements IInlineDiffsService {
originalEndLine: endLine,
startLine: beginLine,
endLine: endLine, // starts out the same as the current file
_modelid: model.id,
_modeluri: model.uri,
_model: model,
_sweepLine: null,
_sweepCol: null,
_generationid: generationid,
@ -625,14 +614,14 @@ Please finish writing the new file by applying the diff to the original file. Re
{ role: 'user', content: promptContent, }
],
onText: (newText, fullText) => {
this._updateDiffAreaText(diffArea, fullText, streamingGroup)
this._refreshAllDiffsAndStyles(editor)
this._refreshSweepStyles(editor)
this._updateDiffAreaText(diffArea, fullText)
this._refreshAllDiffsAndStyles(model)
this._refreshSweepStyles(model)
},
onFinalMessage: (fullText) => {
this._updateDiffAreaText(diffArea, fullText, streamingGroup)
this._refreshAllDiffsAndStyles(editor)
this._refreshSweepStyles(editor)
this._updateDiffAreaText(diffArea, fullText)
this._refreshAllDiffsAndStyles(model)
this._refreshSweepStyles(model)
resolve();
},
onError: (e) => {
@ -666,7 +655,7 @@ Please finish writing the new file by applying the diff to the original file. Re
this.streamingStateOfModelId[model.id] = streamingState
// initialize stream
this._initializeStream(editor, userMessage, streamingGroup)
this._initializeStream(model, userMessage, streamingGroup)
}
@ -691,8 +680,8 @@ Please finish writing the new file by applying the diff to the original file. Re
const diffArea = this.diffAreaOfId[diffareaid]
if (!diffArea) return
const modelid = diffArea._modelid
const uri = diffArea._modeluri
const model = diffArea._model
const { id: modelid, uri } = model
const originalFile = this.originalFileStrOfModelId[modelid]
const currentFile = await VSReadFile(this._fileService, uri)
@ -730,10 +719,7 @@ Please finish writing the new file by applying the diff to the original file. Re
const shouldDeleteDiffArea = originalArea === currentArea
if (shouldDeleteDiffArea) {
this._deleteDiffArea(diffArea)
// TODO if editor is visible we should update it too, or focus it first
const editor = this._editorService.getActiveCodeEditor()
if (editor?.getModel()?.id === modelid)
this._refreshAllDiffsAndStyles(editor)
this._refreshAllDiffsAndStyles(model)
}
finishHistorySnapshot()
@ -744,51 +730,51 @@ Please finish writing the new file by applying the diff to the original file. Re
// called on void.rejectDiff
public async rejectDiff({ diffid, diffareaid }: { diffid: number, diffareaid: number }) {
const editor = vscode.window.activeTextEditor
if (!editor)
return
public async rejectDiff({ diffid }: { diffid: number }) {
const modelid = editor.document.uri.toString()
const diff = this.diffOfId[diffid]
if (!diff) return
const diffIdx = this._diffsOfDocument[modelid].findIndex(diff => diff.diffid === diffid);
if (diffIdx === -1) { console.error('Error: DiffID could not be found: ', diffid, diffareaid, this._diffsOfDocument[modelid], this._diffAreasOfDocument[modelid]); return; }
const { diffareaid } = diff
const diffArea = this.diffAreaOfId[diffareaid]
if (!diffArea) return
const diffareaIdx = this._diffAreasOfDocument[modelid].findIndex(diff => diff.diffareaid === diffareaid);
if (diffareaIdx === -1) { console.error('Error: DiffAreaID could not be found: ', diffid, diffareaid, this._diffsOfDocument[modelid], this._diffAreasOfDocument[modelid]); return; }
const model = diffArea._model
const { id: modelid, uri } = model
const diff = this._diffsOfDocument[modelid][diffIdx]
const originalFile = this.originalFileStrOfModelId[modelid]
const currentFile = await VSReadFile(this._fileService, uri)
if (currentFile === null) return
// Apply the rejection by replacing with original code
// we don't have to edit the original or final file; just do a workspace edit so the code equals the original code
const workspaceEdit = new vscode.WorkspaceEdit();
workspaceEdit.replace(editor.document.uri, diff.range, diff.originalCode)
await vscode.workspace.applyEdit(workspaceEdit)
// add to history
const { finishHistorySnapshot } = this._addToHistory(uri)
// Apply the rejection by replacing with original code (without putting it on the undo/redo stack, this is OK because we put it on the stack ourselves)
if (!model.isDisposed())
model.applyEdits([{
range: { startLineNumber: diffArea.startLine, startColumn: 0, endLineNumber: diffArea.endLine, endColumn: Number.MAX_SAFE_INTEGER, },
text: diff.originalCode
}])
// Check if diffArea should be removed
const originalFile = this._originalFileOfDocument[modelid]
const currentFile = await readFileContentOfUri(editor.document.uri)
const diffArea = this._diffAreasOfDocument[modelid][diffareaIdx]
const currentLines = currentFile.split('\n');
const originalLines = originalFile.split('\n');
const currentArea = currentLines.slice(diffArea.startLine, diffArea.endLine + 1).join('\n')
const originalArea = originalLines.slice(diffArea.originalStartLine, diffArea.originalEndLine + 1).join('\n')
if (originalArea === currentArea) {
const index = this._diffAreasOfDocument[modelid].findIndex(da => da.diffareaid === diffArea.diffareaid)
this._diffAreasOfDocument[modelid].splice(index, 1)
const shouldDeleteDiffArea = originalArea === currentArea
if (shouldDeleteDiffArea) {
this._deleteDiffArea(diffArea)
}
const editor = this._editorService.getActiveCodeEditor()
if (editor?.getModel()?.id === modelid)
this._refreshAllDiffsAndStyles(model)
finishHistorySnapshot()
this.refreshStylesAndDiffs(modelid)
}
}
registerSingleton(IInlineDiffsService, InlineDiffsService, InstantiationType.Eager);