refactor(hashline-edit): enforce three-op edit model
Unify internal hashline edit handling around replace/append/prepend to remove legacy operation shapes. This keeps normalization, ordering, deduplication, execution, and tests aligned with the new op/pos/end/lines contract.
This commit is contained in:
@@ -6,23 +6,13 @@ function normalizeEditPayload(payload: string | string[]): string {
|
||||
}
|
||||
|
||||
function buildDedupeKey(edit: HashlineEdit): string {
|
||||
switch (edit.type) {
|
||||
case "set_line":
|
||||
return `set_line|${edit.line}|${normalizeEditPayload(edit.text)}`
|
||||
case "replace_lines":
|
||||
return `replace_lines|${edit.start_line}|${edit.end_line}|${normalizeEditPayload(edit.text)}`
|
||||
case "insert_after":
|
||||
return `insert_after|${edit.line}|${normalizeEditPayload(edit.text)}`
|
||||
case "insert_before":
|
||||
return `insert_before|${edit.line}|${normalizeEditPayload(edit.text)}`
|
||||
case "insert_between":
|
||||
return `insert_between|${edit.after_line}|${edit.before_line}|${normalizeEditPayload(edit.text)}`
|
||||
switch (edit.op) {
|
||||
case "replace":
|
||||
return `replace|${edit.old_text}|${normalizeEditPayload(edit.new_text)}`
|
||||
return `replace|${edit.pos}|${edit.end ?? ""}|${normalizeEditPayload(edit.lines)}`
|
||||
case "append":
|
||||
return `append|${normalizeEditPayload(edit.text)}`
|
||||
return `append|${edit.pos ?? ""}|${normalizeEditPayload(edit.lines)}`
|
||||
case "prepend":
|
||||
return `prepend|${normalizeEditPayload(edit.text)}`
|
||||
return `prepend|${edit.pos ?? ""}|${normalizeEditPayload(edit.lines)}`
|
||||
default:
|
||||
return JSON.stringify(edit)
|
||||
}
|
||||
|
||||
@@ -150,11 +150,3 @@ export function applyPrepend(lines: string[], text: string | string[]): string[]
|
||||
}
|
||||
return [...normalized, ...lines]
|
||||
}
|
||||
|
||||
export function applyReplace(content: string, oldText: string, newText: string | string[]): string {
|
||||
if (!content.includes(oldText)) {
|
||||
throw new Error(`Text not found: "${oldText}"`)
|
||||
}
|
||||
const replacement = Array.isArray(newText) ? newText.join("\n") : newText
|
||||
return content.replaceAll(oldText, replacement)
|
||||
}
|
||||
|
||||
@@ -1,6 +1,6 @@
|
||||
import { describe, expect, it } from "bun:test"
|
||||
import { applyHashlineEdits, applyInsertAfter, applyReplace, applyReplaceLines, applySetLine } from "./edit-operations"
|
||||
import { applyAppend, applyPrepend } from "./edit-operation-primitives"
|
||||
import { applyHashlineEdits, applyInsertAfter, applyReplaceLines, applySetLine } from "./edit-operations"
|
||||
import { applyAppend, applyInsertBetween, applyPrepend } from "./edit-operation-primitives"
|
||||
import { computeLineHash } from "./hash-computation"
|
||||
import type { HashlineEdit } from "./types"
|
||||
|
||||
@@ -49,7 +49,7 @@ describe("hashline edit operations", () => {
|
||||
//#when
|
||||
const result = applyHashlineEdits(
|
||||
lines.join("\n"),
|
||||
[{ type: "insert_before", line: anchorFor(lines, 2), text: "before 2" }]
|
||||
[{ op: "prepend", pos: anchorFor(lines, 2), lines: "before 2" }]
|
||||
)
|
||||
|
||||
//#then
|
||||
@@ -61,15 +61,7 @@ describe("hashline edit operations", () => {
|
||||
const lines = ["line 1", "line 2", "line 3"]
|
||||
|
||||
//#when
|
||||
const result = applyHashlineEdits(
|
||||
lines.join("\n"),
|
||||
[{
|
||||
type: "insert_between",
|
||||
after_line: anchorFor(lines, 1),
|
||||
before_line: anchorFor(lines, 2),
|
||||
text: ["between"],
|
||||
}]
|
||||
)
|
||||
const result = applyInsertBetween(lines, anchorFor(lines, 1), anchorFor(lines, 2), ["between"]).join("\n")
|
||||
|
||||
//#then
|
||||
expect(result).toEqual("line 1\nbetween\nline 2\nline 3")
|
||||
@@ -89,7 +81,7 @@ describe("hashline edit operations", () => {
|
||||
|
||||
//#when / #then
|
||||
expect(() =>
|
||||
applyHashlineEdits(lines.join("\n"), [{ type: "insert_before", line: anchorFor(lines, 1), text: [] }])
|
||||
applyHashlineEdits(lines.join("\n"), [{ op: "prepend", pos: anchorFor(lines, 1), lines: [] }])
|
||||
).toThrow(/non-empty/i)
|
||||
})
|
||||
|
||||
@@ -98,28 +90,7 @@ describe("hashline edit operations", () => {
|
||||
const lines = ["line 1", "line 2"]
|
||||
|
||||
//#when / #then
|
||||
expect(() =>
|
||||
applyHashlineEdits(
|
||||
lines.join("\n"),
|
||||
[{
|
||||
type: "insert_between",
|
||||
after_line: anchorFor(lines, 1),
|
||||
before_line: anchorFor(lines, 2),
|
||||
text: [],
|
||||
}]
|
||||
)
|
||||
).toThrow(/non-empty/i)
|
||||
})
|
||||
|
||||
it("applies replace operation", () => {
|
||||
//#given
|
||||
const content = "hello world foo"
|
||||
|
||||
//#when
|
||||
const result = applyReplace(content, "world", "universe")
|
||||
|
||||
//#then
|
||||
expect(result).toEqual("hello universe foo")
|
||||
expect(() => applyInsertBetween(lines, anchorFor(lines, 1), anchorFor(lines, 2), [])).toThrow(/non-empty/i)
|
||||
})
|
||||
|
||||
it("applies mixed edits in one pass", () => {
|
||||
@@ -127,8 +98,8 @@ describe("hashline edit operations", () => {
|
||||
const content = "line 1\nline 2\nline 3"
|
||||
const lines = content.split("\n")
|
||||
const edits: HashlineEdit[] = [
|
||||
{ type: "insert_after", line: anchorFor(lines, 1), text: "inserted" },
|
||||
{ type: "set_line", line: anchorFor(lines, 3), text: "modified" },
|
||||
{ op: "append", pos: anchorFor(lines, 1), lines: "inserted" },
|
||||
{ op: "replace", pos: anchorFor(lines, 3), lines: "modified" },
|
||||
]
|
||||
|
||||
//#when
|
||||
@@ -143,8 +114,8 @@ describe("hashline edit operations", () => {
|
||||
const content = "line 1\nline 2"
|
||||
const lines = content.split("\n")
|
||||
const edits: HashlineEdit[] = [
|
||||
{ type: "insert_after", line: anchorFor(lines, 1), text: "inserted" },
|
||||
{ type: "insert_after", line: anchorFor(lines, 1), text: "inserted" },
|
||||
{ op: "append", pos: anchorFor(lines, 1), lines: "inserted" },
|
||||
{ op: "append", pos: anchorFor(lines, 1), lines: "inserted" },
|
||||
]
|
||||
|
||||
//#when
|
||||
@@ -227,16 +198,9 @@ describe("hashline edit operations", () => {
|
||||
const lines = ["line 1", "line 2", "line 3"]
|
||||
|
||||
//#when / #then
|
||||
expect(() =>
|
||||
applyHashlineEdits(lines.join("\n"), [
|
||||
{
|
||||
type: "insert_between",
|
||||
after_line: anchorFor(lines, 1),
|
||||
before_line: anchorFor(lines, 2),
|
||||
text: ["line 1", "line 2"],
|
||||
},
|
||||
])
|
||||
).toThrow(/non-empty/i)
|
||||
expect(() => applyInsertBetween(lines, anchorFor(lines, 1), anchorFor(lines, 2), ["line 1", "line 2"])).toThrow(
|
||||
/non-empty/i
|
||||
)
|
||||
})
|
||||
|
||||
it("restores indentation for first replace_lines entry", () => {
|
||||
@@ -322,8 +286,8 @@ describe("hashline edit operations", () => {
|
||||
|
||||
//#when
|
||||
const result = applyHashlineEdits(content, [
|
||||
{ type: "append", text: ["line 3"] },
|
||||
{ type: "prepend", text: ["line 0"] },
|
||||
{ op: "append", lines: ["line 3"] },
|
||||
{ op: "prepend", lines: ["line 0"] },
|
||||
])
|
||||
|
||||
//#then
|
||||
|
||||
@@ -5,9 +5,7 @@ import {
|
||||
applyAppend,
|
||||
applyInsertAfter,
|
||||
applyInsertBefore,
|
||||
applyInsertBetween,
|
||||
applyPrepend,
|
||||
applyReplace,
|
||||
applyReplaceLines,
|
||||
applySetLine,
|
||||
} from "./edit-operation-primitives"
|
||||
@@ -33,42 +31,17 @@ export function applyHashlineEditsWithReport(content: string, edits: HashlineEdi
|
||||
|
||||
let noopEdits = 0
|
||||
|
||||
let result = content
|
||||
let lines = result.length === 0 ? [] : result.split("\n")
|
||||
let lines = content.length === 0 ? [] : content.split("\n")
|
||||
|
||||
const refs = collectLineRefs(sortedEdits)
|
||||
validateLineRefs(lines, refs)
|
||||
|
||||
for (const edit of sortedEdits) {
|
||||
switch (edit.type) {
|
||||
case "set_line": {
|
||||
lines = applySetLine(lines, edit.line, edit.text, { skipValidation: true })
|
||||
break
|
||||
}
|
||||
case "replace_lines": {
|
||||
lines = applyReplaceLines(lines, edit.start_line, edit.end_line, edit.text, { skipValidation: true })
|
||||
break
|
||||
}
|
||||
case "insert_after": {
|
||||
const next = applyInsertAfter(lines, edit.line, edit.text, { skipValidation: true })
|
||||
if (next.join("\n") === lines.join("\n")) {
|
||||
noopEdits += 1
|
||||
break
|
||||
}
|
||||
lines = next
|
||||
break
|
||||
}
|
||||
case "insert_before": {
|
||||
const next = applyInsertBefore(lines, edit.line, edit.text, { skipValidation: true })
|
||||
if (next.join("\n") === lines.join("\n")) {
|
||||
noopEdits += 1
|
||||
break
|
||||
}
|
||||
lines = next
|
||||
break
|
||||
}
|
||||
case "insert_between": {
|
||||
const next = applyInsertBetween(lines, edit.after_line, edit.before_line, edit.text, { skipValidation: true })
|
||||
switch (edit.op) {
|
||||
case "replace": {
|
||||
const next = edit.end
|
||||
? applyReplaceLines(lines, edit.pos, edit.end, edit.lines, { skipValidation: true })
|
||||
: applySetLine(lines, edit.pos, edit.lines, { skipValidation: true })
|
||||
if (next.join("\n") === lines.join("\n")) {
|
||||
noopEdits += 1
|
||||
break
|
||||
@@ -77,7 +50,9 @@ export function applyHashlineEditsWithReport(content: string, edits: HashlineEdi
|
||||
break
|
||||
}
|
||||
case "append": {
|
||||
const next = applyAppend(lines, edit.text)
|
||||
const next = edit.pos
|
||||
? applyInsertAfter(lines, edit.pos, edit.lines, { skipValidation: true })
|
||||
: applyAppend(lines, edit.lines)
|
||||
if (next.join("\n") === lines.join("\n")) {
|
||||
noopEdits += 1
|
||||
break
|
||||
@@ -86,7 +61,9 @@ export function applyHashlineEditsWithReport(content: string, edits: HashlineEdi
|
||||
break
|
||||
}
|
||||
case "prepend": {
|
||||
const next = applyPrepend(lines, edit.text)
|
||||
const next = edit.pos
|
||||
? applyInsertBefore(lines, edit.pos, edit.lines, { skipValidation: true })
|
||||
: applyPrepend(lines, edit.lines)
|
||||
if (next.join("\n") === lines.join("\n")) {
|
||||
noopEdits += 1
|
||||
break
|
||||
@@ -94,17 +71,6 @@ export function applyHashlineEditsWithReport(content: string, edits: HashlineEdi
|
||||
lines = next
|
||||
break
|
||||
}
|
||||
case "replace": {
|
||||
result = lines.join("\n")
|
||||
const replaced = applyReplace(result, edit.old_text, edit.new_text)
|
||||
if (replaced === result) {
|
||||
noopEdits += 1
|
||||
break
|
||||
}
|
||||
result = replaced
|
||||
lines = result.split("\n")
|
||||
break
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@@ -124,6 +90,4 @@ export {
|
||||
applyReplaceLines,
|
||||
applyInsertAfter,
|
||||
applyInsertBefore,
|
||||
applyInsertBetween,
|
||||
applyReplace,
|
||||
} from "./edit-operation-primitives"
|
||||
|
||||
@@ -2,23 +2,13 @@ import { parseLineRef } from "./validation"
|
||||
import type { HashlineEdit } from "./types"
|
||||
|
||||
export function getEditLineNumber(edit: HashlineEdit): number {
|
||||
switch (edit.type) {
|
||||
case "set_line":
|
||||
return parseLineRef(edit.line).line
|
||||
case "replace_lines":
|
||||
return parseLineRef(edit.end_line).line
|
||||
case "insert_after":
|
||||
return parseLineRef(edit.line).line
|
||||
case "insert_before":
|
||||
return parseLineRef(edit.line).line
|
||||
case "insert_between":
|
||||
return parseLineRef(edit.before_line).line
|
||||
case "append":
|
||||
return Number.NEGATIVE_INFINITY
|
||||
case "prepend":
|
||||
return Number.NEGATIVE_INFINITY
|
||||
switch (edit.op) {
|
||||
case "replace":
|
||||
return Number.NEGATIVE_INFINITY
|
||||
return parseLineRef(edit.end ?? edit.pos).line
|
||||
case "append":
|
||||
return edit.pos ? parseLineRef(edit.pos).line : Number.NEGATIVE_INFINITY
|
||||
case "prepend":
|
||||
return edit.pos ? parseLineRef(edit.pos).line : Number.NEGATIVE_INFINITY
|
||||
default:
|
||||
return Number.POSITIVE_INFINITY
|
||||
}
|
||||
@@ -26,21 +16,12 @@ export function getEditLineNumber(edit: HashlineEdit): number {
|
||||
|
||||
export function collectLineRefs(edits: HashlineEdit[]): string[] {
|
||||
return edits.flatMap((edit) => {
|
||||
switch (edit.type) {
|
||||
case "set_line":
|
||||
return [edit.line]
|
||||
case "replace_lines":
|
||||
return [edit.start_line, edit.end_line]
|
||||
case "insert_after":
|
||||
return [edit.line]
|
||||
case "insert_before":
|
||||
return [edit.line]
|
||||
case "insert_between":
|
||||
return [edit.after_line, edit.before_line]
|
||||
switch (edit.op) {
|
||||
case "replace":
|
||||
return edit.end ? [edit.pos, edit.end] : [edit.pos]
|
||||
case "append":
|
||||
case "prepend":
|
||||
case "replace":
|
||||
return []
|
||||
return edit.pos ? [edit.pos] : []
|
||||
default:
|
||||
return []
|
||||
}
|
||||
|
||||
@@ -32,7 +32,7 @@ function resolveToolCallID(ctx: ToolContextWithCallID): string | undefined {
|
||||
|
||||
function canCreateFromMissingFile(edits: HashlineEdit[]): boolean {
|
||||
if (edits.length === 0) return false
|
||||
return edits.every((edit) => edit.type === "append" || edit.type === "prepend")
|
||||
return edits.every((edit) => edit.op === "append" || edit.op === "prepend")
|
||||
}
|
||||
|
||||
function buildSuccessMeta(
|
||||
|
||||
@@ -8,14 +8,9 @@ export {
|
||||
export { parseLineRef, validateLineRef } from "./validation"
|
||||
export type { LineRef } from "./validation"
|
||||
export type {
|
||||
SetLine,
|
||||
ReplaceLines,
|
||||
InsertAfter,
|
||||
InsertBefore,
|
||||
InsertBetween,
|
||||
Replace,
|
||||
Append,
|
||||
Prepend,
|
||||
ReplaceEdit,
|
||||
AppendEdit,
|
||||
PrependEdit,
|
||||
HashlineEdit,
|
||||
} from "./types"
|
||||
export { NIBBLE_STR, HASHLINE_DICT, HASHLINE_REF_PATTERN, HASHLINE_OUTPUT_PATTERN } from "./constants"
|
||||
@@ -23,8 +18,6 @@ export {
|
||||
applyHashlineEdits,
|
||||
applyInsertAfter,
|
||||
applyInsertBefore,
|
||||
applyInsertBetween,
|
||||
applyReplace,
|
||||
applyReplaceLines,
|
||||
applySetLine,
|
||||
} from "./edit-operations"
|
||||
|
||||
@@ -2,7 +2,7 @@ import { describe, expect, it } from "bun:test"
|
||||
import { normalizeHashlineEdits, type RawHashlineEdit } from "./normalize-edits"
|
||||
|
||||
describe("normalizeHashlineEdits", () => {
|
||||
it("maps replace with pos to set_line", () => {
|
||||
it("maps replace with pos to replace", () => {
|
||||
//#given
|
||||
const input: RawHashlineEdit[] = [{ op: "replace", pos: "2#VK", lines: "updated" }]
|
||||
|
||||
@@ -10,10 +10,10 @@ describe("normalizeHashlineEdits", () => {
|
||||
const result = normalizeHashlineEdits(input)
|
||||
|
||||
//#then
|
||||
expect(result).toEqual([{ type: "set_line", line: "2#VK", text: "updated" }])
|
||||
expect(result).toEqual([{ op: "replace", pos: "2#VK", lines: "updated" }])
|
||||
})
|
||||
|
||||
it("maps replace with pos and end to replace_lines", () => {
|
||||
it("maps replace with pos and end to replace", () => {
|
||||
//#given
|
||||
const input: RawHashlineEdit[] = [{ op: "replace", pos: "2#VK", end: "4#MB", lines: ["a", "b"] }]
|
||||
|
||||
@@ -21,10 +21,10 @@ describe("normalizeHashlineEdits", () => {
|
||||
const result = normalizeHashlineEdits(input)
|
||||
|
||||
//#then
|
||||
expect(result).toEqual([{ type: "replace_lines", start_line: "2#VK", end_line: "4#MB", text: ["a", "b"] }])
|
||||
expect(result).toEqual([{ op: "replace", pos: "2#VK", end: "4#MB", lines: ["a", "b"] }])
|
||||
})
|
||||
|
||||
it("maps anchored append and prepend to insert operations", () => {
|
||||
it("maps anchored append and prepend preserving op", () => {
|
||||
//#given
|
||||
const input: RawHashlineEdit[] = [
|
||||
{ op: "append", pos: "2#VK", lines: ["after"] },
|
||||
@@ -35,10 +35,7 @@ describe("normalizeHashlineEdits", () => {
|
||||
const result = normalizeHashlineEdits(input)
|
||||
|
||||
//#then
|
||||
expect(result).toEqual([
|
||||
{ type: "insert_after", line: "2#VK", text: ["after"] },
|
||||
{ type: "insert_before", line: "4#MB", text: ["before"] },
|
||||
])
|
||||
expect(result).toEqual([{ op: "append", pos: "2#VK", lines: ["after"] }, { op: "prepend", pos: "4#MB", lines: ["before"] }])
|
||||
})
|
||||
|
||||
it("prefers pos over end for prepend anchors", () => {
|
||||
@@ -49,7 +46,7 @@ describe("normalizeHashlineEdits", () => {
|
||||
const result = normalizeHashlineEdits(input)
|
||||
|
||||
//#then
|
||||
expect(result).toEqual([{ type: "insert_before", line: "3#AA", text: ["before"] }])
|
||||
expect(result).toEqual([{ op: "prepend", pos: "3#AA", lines: ["before"] }])
|
||||
})
|
||||
|
||||
it("rejects legacy payload without op", () => {
|
||||
|
||||
@@ -1,4 +1,4 @@
|
||||
import type { HashlineEdit } from "./types"
|
||||
import type { AppendEdit, HashlineEdit, PrependEdit, ReplaceEdit } from "./types"
|
||||
|
||||
type HashlineToolOp = "replace" | "append" | "prepend"
|
||||
|
||||
@@ -36,62 +36,43 @@ function normalizeReplaceEdit(edit: RawHashlineEdit, index: number): HashlineEdi
|
||||
const pos = normalizeAnchor(edit.pos)
|
||||
const end = normalizeAnchor(edit.end)
|
||||
const anchor = requireLine(pos ?? end, index, "replace")
|
||||
const text = requireLines(edit, index)
|
||||
const lines = requireLines(edit, index)
|
||||
|
||||
if (pos && end) {
|
||||
return {
|
||||
type: "replace_lines",
|
||||
start_line: pos,
|
||||
end_line: end,
|
||||
text,
|
||||
}
|
||||
}
|
||||
|
||||
return {
|
||||
type: "set_line",
|
||||
line: anchor,
|
||||
text,
|
||||
const normalized: ReplaceEdit = {
|
||||
op: "replace",
|
||||
pos: anchor,
|
||||
lines,
|
||||
}
|
||||
if (end) normalized.end = end
|
||||
return normalized
|
||||
}
|
||||
|
||||
function normalizeAppendEdit(edit: RawHashlineEdit, index: number): HashlineEdit {
|
||||
const pos = normalizeAnchor(edit.pos)
|
||||
const end = normalizeAnchor(edit.end)
|
||||
const anchor = pos ?? end
|
||||
const text = requireLines(edit, index)
|
||||
const lines = requireLines(edit, index)
|
||||
|
||||
if (!anchor) {
|
||||
return {
|
||||
type: "append",
|
||||
text,
|
||||
}
|
||||
}
|
||||
|
||||
return {
|
||||
type: "insert_after",
|
||||
line: anchor,
|
||||
text,
|
||||
const normalized: AppendEdit = {
|
||||
op: "append",
|
||||
lines,
|
||||
}
|
||||
if (anchor) normalized.pos = anchor
|
||||
return normalized
|
||||
}
|
||||
|
||||
function normalizePrependEdit(edit: RawHashlineEdit, index: number): HashlineEdit {
|
||||
const pos = normalizeAnchor(edit.pos)
|
||||
const end = normalizeAnchor(edit.end)
|
||||
const anchor = pos ?? end
|
||||
const text = requireLines(edit, index)
|
||||
const lines = requireLines(edit, index)
|
||||
|
||||
if (!anchor) {
|
||||
return {
|
||||
type: "prepend",
|
||||
text,
|
||||
}
|
||||
}
|
||||
|
||||
return {
|
||||
type: "insert_before",
|
||||
line: anchor,
|
||||
text,
|
||||
const normalized: PrependEdit = {
|
||||
op: "prepend",
|
||||
lines,
|
||||
}
|
||||
if (anchor) normalized.pos = anchor
|
||||
return normalized
|
||||
}
|
||||
|
||||
export function normalizeHashlineEdits(rawEdits: RawHashlineEdit[]): HashlineEdit[] {
|
||||
|
||||
@@ -1,57 +1,20 @@
|
||||
export interface SetLine {
|
||||
type: "set_line"
|
||||
line: string
|
||||
text: string | string[]
|
||||
export interface ReplaceEdit {
|
||||
op: "replace"
|
||||
pos: string
|
||||
end?: string
|
||||
lines: string | string[]
|
||||
}
|
||||
|
||||
export interface ReplaceLines {
|
||||
type: "replace_lines"
|
||||
start_line: string
|
||||
end_line: string
|
||||
text: string | string[]
|
||||
export interface AppendEdit {
|
||||
op: "append"
|
||||
pos?: string
|
||||
lines: string | string[]
|
||||
}
|
||||
|
||||
export interface InsertAfter {
|
||||
type: "insert_after"
|
||||
line: string
|
||||
text: string | string[]
|
||||
export interface PrependEdit {
|
||||
op: "prepend"
|
||||
pos?: string
|
||||
lines: string | string[]
|
||||
}
|
||||
|
||||
export interface InsertBefore {
|
||||
type: "insert_before"
|
||||
line: string
|
||||
text: string | string[]
|
||||
}
|
||||
|
||||
export interface InsertBetween {
|
||||
type: "insert_between"
|
||||
after_line: string
|
||||
before_line: string
|
||||
text: string | string[]
|
||||
}
|
||||
|
||||
export interface Replace {
|
||||
type: "replace"
|
||||
old_text: string
|
||||
new_text: string | string[]
|
||||
}
|
||||
|
||||
export interface Append {
|
||||
type: "append"
|
||||
text: string | string[]
|
||||
}
|
||||
|
||||
export interface Prepend {
|
||||
type: "prepend"
|
||||
text: string | string[]
|
||||
}
|
||||
|
||||
export type HashlineEdit =
|
||||
| SetLine
|
||||
| ReplaceLines
|
||||
| InsertAfter
|
||||
| InsertBefore
|
||||
| InsertBetween
|
||||
| Replace
|
||||
| Append
|
||||
| Prepend
|
||||
export type HashlineEdit = ReplaceEdit | AppendEdit | PrependEdit
|
||||
|
||||
Reference in New Issue
Block a user