@@ -0,0 +1,117 @@
1+use crate::{Range, Rope, Selection, Tendril, Transaction};
2+use smallvec::SmallVec;
4+const PAIRS: &[(char, char)] = &[
13+const CLOSE_BEFORE: &str = ")]}'\":;> \n"; // includes space and newline
16+// Fn(doc, selection, char) => Option<Transaction>
17+// problem is, we want to do this per range, so we can call default handler for some ranges
18+// so maybe ret Vec<Option<Change>>
19+// but we also need to be able to return transactions...
21+// to simplify, maybe return Option<Transaction> and just reimplement the default
23+// TODO: delete implementation where it erases the whole bracket (|) -> |
25+pub fn hook(doc: &Rope, selection: &Selection, ch: char) -> Option<Transaction> {
26+ for &(open, close) in PAIRS {
28+ let t = if open == close {
32+ handle_open(doc, selection, open, close, CLOSE_BEFORE)
38+ // && char_at pos == close
39+ return Some(handle_close(doc, selection, open, close));
46+// TODO: special handling for lifetimes in rust: if preceeded by & or < don't auto close '
47+// for example "&'a mut", or "fn<'a>"
49+fn next_char(doc: &Rope, pos: usize) -> Option<char> {
50+ if pos >= doc.len_chars() {
56+// TODO: if not cursor but selection, wrap on both sides of selection (surround)
59+ selection: &Selection,
64+ let mut ranges = SmallVec::with_capacity(selection.len());
66+ let mut transaction = Transaction::change_by_selection(doc, selection, |range| {
67+ let pos = range.head;
68+ let next = next_char(doc, pos);
70+ ranges.push(Range::new(range.anchor, pos + 1)); // pos + open
73+ Some(ch) if !close_before.contains(ch) => {
74+ // TODO: else return (use default handler that inserts open)
75+ (pos, pos, Some(Tendril::from_char(open)))
77+ // None | Some(ch) if close_before.contains(ch) => {}
79+ // insert open & close
80+ let mut pair = Tendril::with_capacity(2);
81+ pair.push_char(open);
82+ pair.push_char(close);
84+ (pos, pos, Some(pair))
89+ transaction.with_selection(Selection::new(ranges, selection.primary_index()))
92+fn handle_close(doc: &Rope, selection: &Selection, _open: char, close: char) -> Transaction {
93+ let mut ranges = SmallVec::with_capacity(selection.len());
95+ let mut transaction = Transaction::change_by_selection(doc, selection, |range| {
96+ let pos = range.head;
97+ let next = next_char(doc, pos);
99+ ranges.push(Range::new(range.anchor, pos + 1)); // pos + close
101+ if next == Some(close) {
102+ // return transaction that moves past close
103+ (pos, pos, None) // no-op
105+ // TODO: else return (use default handler that inserts close)
106+ (pos, pos, Some(Tendril::from_char(close)))
110+ transaction.with_selection(Selection::new(ranges, selection.primary_index()))
113+// handle cases where open and close is the same, or in triples ("""docstring""")
115+ // if not cursor but selection, wrap
116+ // let next = next char