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
|
use crate::{Range, Rope, Selection, Tendril, Transaction};
use smallvec::SmallVec;
const PAIRS: &[(char, char)] = &[
('(', ')'),
('{', '}'),
('[', ']'),
('\'', '\''),
('"', '"'),
('`', '`'),
];
const CLOSE_BEFORE: &str = ")]}'\":;> \n"; // includes space and newline
// insert hook:
// Fn(doc, selection, char) => Option<Transaction>
// problem is, we want to do this per range, so we can call default handler for some ranges
// so maybe ret Vec<Option<Change>>
// but we also need to be able to return transactions...
//
// to simplify, maybe return Option<Transaction> and just reimplement the default
// TODO: delete implementation where it erases the whole bracket (|) -> |
pub fn hook(doc: &Rope, selection: &Selection, ch: char) -> Option<Transaction> {
for &(open, close) in PAIRS {
if open == ch {
let t = if open == close {
return None;
// handle_same()
} else {
handle_open(doc, selection, open, close, CLOSE_BEFORE)
};
return Some(t);
}
if close == ch {
// && char_at pos == close
return Some(handle_close(doc, selection, open, close));
}
}
None
}
// TODO: special handling for lifetimes in rust: if preceeded by & or < don't auto close '
// for example "&'a mut", or "fn<'a>"
fn next_char(doc: &Rope, pos: usize) -> Option<char> {
if pos >= doc.len_chars() {
return None;
}
Some(doc.char(pos))
}
// TODO: if not cursor but selection, wrap on both sides of selection (surround)
fn handle_open(
doc: &Rope,
selection: &Selection,
open: char,
close: char,
close_before: &str,
) -> Transaction {
let mut ranges = SmallVec::with_capacity(selection.len());
let mut transaction = Transaction::change_by_selection(doc, selection, |range| {
let pos = range.head;
let next = next_char(doc, pos);
ranges.push(Range::new(range.anchor, pos + 1)); // pos + open
match next {
Some(ch) if !close_before.contains(ch) => {
// TODO: else return (use default handler that inserts open)
(pos, pos, Some(Tendril::from_char(open)))
}
// None | Some(ch) if close_before.contains(ch) => {}
_ => {
// insert open & close
let mut pair = Tendril::with_capacity(2);
pair.push_char(open);
pair.push_char(close);
(pos, pos, Some(pair))
}
}
});
transaction.with_selection(Selection::new(ranges, selection.primary_index()))
}
fn handle_close(doc: &Rope, selection: &Selection, _open: char, close: char) -> Transaction {
let mut ranges = SmallVec::with_capacity(selection.len());
let mut transaction = Transaction::change_by_selection(doc, selection, |range| {
let pos = range.head;
let next = next_char(doc, pos);
ranges.push(Range::new(range.anchor, pos + 1)); // pos + close
if next == Some(close) {
// return transaction that moves past close
(pos, pos, None) // no-op
} else {
// TODO: else return (use default handler that inserts close)
(pos, pos, Some(Tendril::from_char(close)))
}
});
transaction.with_selection(Selection::new(ranges, selection.primary_index()))
}
// handle cases where open and close is the same, or in triples ("""docstring""")
fn handle_same() {
// if not cursor but selection, wrap
// let next = next char
}
|