1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60 61 62 63 64 65 66 67 68 69 70 71 72 73 74 75 76 77 78 79 80 81 82 83 84 85 86 87 88 89 90 91 92 93 94 95 96 97 98 99 100 101 102 103 104 105 106 107 108 109 110 111 112 113 114 115 116 117 118 119 120 121 122 123 124 125 126 127 128 129 130 131 132 133 134 135 136 137 138 139 140 141 142
|
use common;
use common::{location};
use io;
use sort;
use strings;
export type editgroupfunc = fn(eg: *editgroup, user: nullable *opaque) void;
// A group of related edits.
export type editgroup = struct {
rule: const *rule,
edits: []edit,
onmerge: nullable *editgroupfunc,
user: nullable *opaque,
};
// Merges a group of edits into a [[context]], to be applied later. Consumes the
// [[editgroup]] object.
export fn merge_edits(ctx: *context, group: *editgroup) void = {
match (group.onmerge) {
case let func: *editgroupfunc =>
func(group, group.user);
case null => void;
};
append(ctx.edits, group.edits...)!;
};
// Frees resources associated with an [[editgroup]].
export fn editgroup_finish(group: *editgroup) void = {
free(group.edits);
free(group.user);
};
// An edit to a text file.
export type edit = struct {
off: io::off,
rem: size,
ins: str,
};
// Frees resources associated with an [[edit]].
export fn edit_finish(e: *edit) void = {
free(e.ins);
};
// Sort edits in order from smallest to largest offset.
export fn edits_sort(edits: []edit) void = {
sort::sort(edits, size(edit), &edit_cmp)!;
};
fn edit_cmp(a: const *opaque, b: const *opaque) int = {
const a = a: const *edit;
const b = b: const *edit;
if (a.off < b.off) {
return -1;
} else if (a.off > b.off) {
return 1;
} else {
return 0;
};
};
// Adds a callback which is run when an edit group is merged.
//
// Important: the user object is freed when the edit group is finished.
export fn edit_onmerge(
group: *editgroup,
func: *editgroupfunc,
user: nullable *opaque = null,
) void = {
assert(group.onmerge == null);
group.onmerge = func;
group.user = user;
};
// Removes text at the given location.
export fn edit_delete(
group: *editgroup,
start: location,
end: location,
) void = {
assert(end.off > start.off);
append(group.edits, edit {
off = start.off,
rem = (end.off - start.off): size,
ins = "",
})!;
};
// Prepends text just before the given location.
export fn edit_prepend(
group: *editgroup,
loc: location,
text: str,
) void = {
append(group.edits, edit {
off = loc.off - 1,
rem = 0,
ins = strings::dup(text)!,
})!;
};
// Insert text at the given location.
export fn edit_insert(
group: *editgroup,
loc: location,
text: str,
) void = {
append(group.edits, edit {
off = loc.off,
rem = 0,
ins = strings::dup(text)!,
})!;
};
// Appends text at the given location.
export fn edit_append(
group: *editgroup,
loc: location,
text: str,
) void = {
append(group.edits, edit {
off = loc.off + 1,
rem = 0,
ins = strings::dup(text)!,
})!;
};
// Replace the text at the given location.
export fn edit_replace(
group: *editgroup,
start: location,
end: location,
text: str,
) void = {
assert(end.off > start.off);
append(group.edits, edit {
off = start.off,
rem = (end.off - start.off): size,
ins = strings::dup(text)!,
})!;
};
|