Path: blob/main/extensions/copilot/src/platform/inlineEdits/common/statelessNextEditProviders.ts
13400 views
/*---------------------------------------------------------------------------------------------1* Copyright (c) Microsoft Corporation. All rights reserved.2* Licensed under the MIT License. See License.txt in the project root for license information.3*--------------------------------------------------------------------------------------------*/45import { LineEdit, LineReplacement } from '../../../util/vs/editor/common/core/edits/lineEdit';6import { StringEdit } from '../../../util/vs/editor/common/core/edits/stringEdit';7import { StatelessNextEditDocument } from './statelessNextEditProvider';89export class IgnoreEmptyLineAndLeadingTrailingWhitespaceChanges {10public static filterEdit(resultDocument: StatelessNextEditDocument, singleEdits: readonly LineReplacement[]): readonly LineReplacement[] {11const filteredEdits = singleEdits.filter(e => !IgnoreEmptyLineAndLeadingTrailingWhitespaceChanges._isWhitespaceOnlyChange(e, resultDocument.documentAfterEditsLines));12return filteredEdits;13}1415private static _isWhitespaceOnlyChange(edit: LineReplacement, baseLines: string[]): boolean {16const originalLines = edit.lineRange.toOffsetRange().slice(baseLines);17const newLines = edit.newLines;1819const isRemoval = newLines.length === 0;2021// is removing empty lines22if (isRemoval && originalLines.every(line => line.trim() === '')) {23return true;24}2526// is adding empty lines27if (!isRemoval && newLines.every(line => line.trim() === '')) {28return true;29}3031if (originalLines.length !== newLines.length) {32return false;33}3435for (let i = 0; i < originalLines.length; i++) {36const originalLine = originalLines[i];37const newLine = newLines[i];38if (originalLine.trim() !== newLine.trim()) {39return false;40}41}42return true;43}44}4546export class IgnoreWhitespaceOnlyChanges {47public static filterEdit(resultDocument: StatelessNextEditDocument, singleEdits: readonly LineReplacement[]): readonly LineReplacement[] {48return singleEdits.filter(e => !IgnoreWhitespaceOnlyChanges._isFormattingOnlyChange(resultDocument.documentAfterEditsLines, e));49}5051/**52* @remarks public only for testing53*/54public static _isFormattingOnlyChange(baseLines: string[], singleEdit: LineReplacement): boolean {55const originalLines = singleEdit.lineRange.toOffsetRange().slice(baseLines).join('').replace(/\s/g, '');56const newLines = singleEdit.newLines.join('').replace(/\s/g, '');57return originalLines === newLines;58}59}6061export function editWouldDeleteWhatWasJustInserted(activeDocument: StatelessNextEditDocument, lineEdit: LineEdit) {62let edit = lineEdit.toEdit(activeDocument.documentAfterEdits);63// ! important: reduce it to the minimal set of changes64edit = edit.normalizeOnSource(activeDocument.documentAfterEdits.value);65if (!editIsDeletion(edit)) {66return false;67}68// We are deleting something. Is it what was just inserted?69for (let i = activeDocument.recentEdits.edits.length - 1; i >= 0; i--) {70const recentEdit = activeDocument.recentEdits.edits[i];71const rebaseResult = edit.tryRebase(recentEdit);72if (!rebaseResult) {73// the edit we want to do cannot be rebased, which indicates that it would interfere with a recent edit74return true;75}76edit = rebaseResult;77}78return false;79}80export function editIsDeletion(edit: StringEdit): boolean {81const deletedChars = edit.replacements.reduce((acc, singleEdit) => acc + singleEdit.replaceRange.length, 0);82const insertedChars = edit.replacements.reduce((acc, singleEdit) => acc + singleEdit.newText.length, 0);83return insertedChars === 0 && deletedChars > 0;84}8586export function editWouldDeleteWhatWasJustInserted2(activeDocument: StatelessNextEditDocument, lineEdit: LineEdit) {87let edit = lineEdit.toEdit(activeDocument.documentAfterEdits);88// ! important: reduce it to the minimal set of changes89edit = edit.normalizeOnSource(activeDocument.documentAfterEdits.value);90if (!editIsDeletion(edit)) {91return false;92}9394let documentContents = activeDocument.documentAfterEdits.value;9596for (let i = activeDocument.recentEdits.edits.length - 1; i >= 0; i--) {97const recentEdit = activeDocument.recentEdits.edits[i];98const recentEditInverse = recentEdit.inverse(documentContents);99100if (recentEditInverse.equals(edit)) {101return true;102}103104documentContents = recentEditInverse.apply(documentContents);105}106107return false;108}109110111