File: edit.ha

package info (click to toggle)
hare-update 0.25.2.0-1
  • links: PTS, VCS
  • area: main
  • in suites: forky, sid
  • size: 516 kB
  • sloc: makefile: 31; sh: 14
file content (142 lines) | stat: -rw-r--r-- 2,901 bytes parent folder | download
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)!,
	})!;
};