implemented support for the 'sentence' text object in the line editor
This commit is contained in:
@@ -235,6 +235,9 @@ impl ClampedUsize {
|
||||
pub fn get(self) -> usize {
|
||||
self.value
|
||||
}
|
||||
pub fn cap(&self) -> usize {
|
||||
self.max
|
||||
}
|
||||
pub fn upper_bound(&self) -> usize {
|
||||
if self.exclusive {
|
||||
self.max.saturating_sub(1)
|
||||
@@ -935,29 +938,15 @@ impl LineBuf {
|
||||
let mut closer_count: u32 = 0;
|
||||
while let Some(idx) = backward_indices.next() {
|
||||
let gr = self.grapheme_at(idx)?.to_string();
|
||||
if gr != closer && gr != opener { continue }
|
||||
if (gr != closer && gr != opener) || self.grapheme_is_escaped(idx) { continue }
|
||||
|
||||
let mut escaped = false;
|
||||
while let Some(idx) = backward_indices.next() {
|
||||
// Keep consuming indices as long as they refer to a backslash
|
||||
let Some("\\") = self.grapheme_at(idx) else {
|
||||
break
|
||||
};
|
||||
// On each backslash, flip this boolean
|
||||
escaped = !escaped
|
||||
}
|
||||
|
||||
// If there are an even number of backslashes (or none), we are not escaped
|
||||
// Therefore, we have found the start position
|
||||
if !escaped {
|
||||
if gr == closer {
|
||||
closer_count += 1;
|
||||
} else if closer_count == 0 {
|
||||
start_pos = Some(idx);
|
||||
break
|
||||
} else {
|
||||
closer_count = closer_count.saturating_sub(1)
|
||||
}
|
||||
if gr == closer {
|
||||
closer_count += 1;
|
||||
} else if closer_count == 0 {
|
||||
start_pos = Some(idx);
|
||||
break
|
||||
} else {
|
||||
closer_count = closer_count.saturating_sub(1)
|
||||
}
|
||||
}
|
||||
|
||||
@@ -968,8 +957,8 @@ impl LineBuf {
|
||||
let mut opener_count: u32 = 0;
|
||||
|
||||
while let Some(idx) = forward_indices.next() {
|
||||
if self.grapheme_is_escaped(idx) { continue }
|
||||
match self.grapheme_at(idx)? {
|
||||
"\\" => { forward_indices.next(); }
|
||||
gr if gr == opener => opener_count += 1,
|
||||
gr if gr == closer => {
|
||||
if opener_count == 0 {
|
||||
@@ -991,8 +980,8 @@ impl LineBuf {
|
||||
let mut opener_count: u32 = 0;
|
||||
|
||||
while let Some(idx) = forward_indices.next() {
|
||||
if self.grapheme_is_escaped(idx) { continue }
|
||||
match self.grapheme_at(idx)? {
|
||||
"\\" => { forward_indices.next(); }
|
||||
gr if gr == opener => {
|
||||
if opener_count == 0 {
|
||||
start = Some(idx);
|
||||
@@ -1022,6 +1011,19 @@ impl LineBuf {
|
||||
Bound::Around => {
|
||||
// End excludes the quote, so push it forward
|
||||
end += 1;
|
||||
|
||||
// We also need to include any trailing whitespace
|
||||
let end_of_line = self.end_of_line();
|
||||
let remainder = end..end_of_line;
|
||||
for idx in remainder {
|
||||
let Some(gr) = self.grapheme_at(idx) else { break };
|
||||
flog!(DEBUG, gr);
|
||||
if is_whitespace(gr) {
|
||||
end += 1;
|
||||
} else {
|
||||
break
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@@ -1673,41 +1675,53 @@ impl LineBuf {
|
||||
};
|
||||
match text_obj {
|
||||
TextObj::Sentence(dir) |
|
||||
TextObj::Paragraph(dir) => {
|
||||
match dir {
|
||||
Direction::Forward => MotionKind::On(end),
|
||||
Direction::Backward => {
|
||||
let cur_sentence_start = start;
|
||||
let mut start_pos = self.cursor.get();
|
||||
for _ in 0..count {
|
||||
if self.is_sentence_start(start_pos) {
|
||||
// We know there is some punctuation before us now
|
||||
// Let's find it
|
||||
let mut bkwd_indices = (0..start_pos).rev();
|
||||
let punct_pos = bkwd_indices
|
||||
.find(|idx| self.grapheme_at(*idx).is_some_and(|gr| PUNCTUATION.contains(&gr)))
|
||||
.unwrap();
|
||||
if self.grapheme_before(punct_pos).is_some() {
|
||||
let Some((new_start,_)) = self.text_obj_sentence(punct_pos - 1, count, Bound::Inside) else {
|
||||
TextObj::Paragraph(dir) => {
|
||||
match dir {
|
||||
Direction::Forward => MotionKind::On(end),
|
||||
Direction::Backward => {
|
||||
let cur_sentence_start = start;
|
||||
let mut start_pos = self.cursor.get();
|
||||
for _ in 0..count {
|
||||
if self.is_sentence_start(start_pos) {
|
||||
// We know there is some punctuation before us now
|
||||
// Let's find it
|
||||
let mut bkwd_indices = (0..start_pos).rev();
|
||||
let punct_pos = bkwd_indices
|
||||
.find(|idx| self.grapheme_at(*idx).is_some_and(|gr| PUNCTUATION.contains(&gr)))
|
||||
.unwrap();
|
||||
if self.grapheme_before(punct_pos).is_some() {
|
||||
let Some((new_start,_)) = self.text_obj_sentence(punct_pos - 1, count, Bound::Inside) else {
|
||||
return MotionKind::Null
|
||||
};
|
||||
start_pos = new_start;
|
||||
continue
|
||||
} else {
|
||||
return MotionKind::Null
|
||||
};
|
||||
start_pos = new_start;
|
||||
continue
|
||||
}
|
||||
} else {
|
||||
return MotionKind::Null
|
||||
start_pos = cur_sentence_start;
|
||||
}
|
||||
} else {
|
||||
start_pos = cur_sentence_start;
|
||||
}
|
||||
MotionKind::On(start_pos)
|
||||
}
|
||||
MotionKind::On(start_pos)
|
||||
}
|
||||
}
|
||||
TextObj::Word(_, bound) |
|
||||
TextObj::WholeSentence(bound) |
|
||||
TextObj::WholeParagraph(bound) => {
|
||||
match bound {
|
||||
Bound::Inside => MotionKind::Inclusive((start,end)),
|
||||
Bound::Around => MotionKind::Exclusive((start,end)),
|
||||
}
|
||||
}
|
||||
_ => {
|
||||
|
||||
MotionKind::Inclusive((start,end))
|
||||
}
|
||||
TextObj::DoubleQuote(_) |
|
||||
TextObj::SingleQuote(_) |
|
||||
TextObj::BacktickQuote(_) |
|
||||
TextObj::Paren(_) |
|
||||
TextObj::Bracket(_) |
|
||||
TextObj::Brace(_) |
|
||||
TextObj::Angle(_) => MotionKind::Exclusive((start,end)),
|
||||
_ => todo!()
|
||||
}
|
||||
}
|
||||
MotionCmd(_,Motion::ToDelimMatch) => {
|
||||
@@ -1927,7 +1941,7 @@ impl LineBuf {
|
||||
}
|
||||
|
||||
final_end = final_end.min(self.cursor.max);
|
||||
MotionKind::Inclusive((start,final_end))
|
||||
MotionKind::Exclusive((start,final_end))
|
||||
}
|
||||
MotionCmd(count,Motion::RepeatMotion) => todo!(),
|
||||
MotionCmd(count,Motion::RepeatMotionRev) => todo!(),
|
||||
@@ -2041,7 +2055,15 @@ impl LineBuf {
|
||||
let end = end.min(col);
|
||||
self.cursor.set(start + end)
|
||||
}
|
||||
MotionKind::Inclusive((start,end)) |
|
||||
MotionKind::Inclusive((start,end)) => {
|
||||
if self.select_range().is_none() {
|
||||
self.cursor.set(start)
|
||||
} else {
|
||||
self.cursor.set(end);
|
||||
self.select_mode = Some(SelectMode::Char(SelectAnchor::End));
|
||||
self.select_range = Some((start,end));
|
||||
}
|
||||
}
|
||||
MotionKind::Exclusive((start,end)) => {
|
||||
if self.select_range().is_none() {
|
||||
self.cursor.set(start)
|
||||
@@ -2081,11 +2103,11 @@ impl LineBuf {
|
||||
ordered(self.cursor.get(), pos)
|
||||
}
|
||||
MotionKind::InclusiveWithTargetCol((start,end),_) |
|
||||
MotionKind::Inclusive((start,end)) => ordered(*start, *end),
|
||||
MotionKind::Exclusive((start,end)) => ordered(*start, *end),
|
||||
MotionKind::ExclusiveWithTargetCol((start,end),_) |
|
||||
MotionKind::Exclusive((start,end)) => {
|
||||
MotionKind::Inclusive((start,end)) => {
|
||||
let (start, mut end) = ordered(*start, *end);
|
||||
end = end.saturating_sub(1);
|
||||
end = ClampedUsize::new(end,self.cursor.max,false).ret_add(1);
|
||||
(start,end)
|
||||
}
|
||||
MotionKind::Null => return None
|
||||
@@ -2463,7 +2485,7 @@ impl LineBuf {
|
||||
.map(|m| self.eval_motion(verb_ref.as_ref(), m))
|
||||
.unwrap_or({
|
||||
self.select_range
|
||||
.map(MotionKind::Inclusive)
|
||||
.map(MotionKind::Exclusive)
|
||||
.unwrap_or(MotionKind::Null)
|
||||
})
|
||||
};
|
||||
|
||||
@@ -941,6 +941,8 @@ impl ViNormal {
|
||||
let obj = match chars_clone.next().unwrap() {
|
||||
'w' => TextObj::Word(Word::Normal,bound),
|
||||
'W' => TextObj::Word(Word::Big,bound),
|
||||
's' => TextObj::WholeSentence(bound),
|
||||
'p' => TextObj::WholeParagraph(bound),
|
||||
'"' => TextObj::DoubleQuote(bound),
|
||||
'\'' => TextObj::SingleQuote(bound),
|
||||
'`' => TextObj::BacktickQuote(bound),
|
||||
@@ -1602,6 +1604,8 @@ impl ViVisual {
|
||||
let obj = match chars_clone.next().unwrap() {
|
||||
'w' => TextObj::Word(Word::Normal,bound),
|
||||
'W' => TextObj::Word(Word::Big,bound),
|
||||
's' => TextObj::WholeSentence(bound),
|
||||
'p' => TextObj::WholeParagraph(bound),
|
||||
'"' => TextObj::DoubleQuote(bound),
|
||||
'\'' => TextObj::SingleQuote(bound),
|
||||
'`' => TextObj::BacktickQuote(bound),
|
||||
|
||||
18
thing.md
18
thing.md
@@ -1,18 +0,0 @@
|
||||
Welcome to the vicut wiki!
|
||||
|
||||
Table of contents:
|
||||
- [🚀 Advanced Usage](https://github.com/km-clay/vicut/wiki/Advanced-Usage)
|
||||
- [🌀 Nested Repeats](https://github.com/km-clay/vicut/wiki/Advanced-Usage#-nested-repeats)
|
||||
- [🏷️ Naming Fields](https://github.com/km-clay/vicut/wiki/Advanced-Usage#%EF%B8%8F-naming-fields)
|
||||
- [🧹 Editing the Input](https://github.com/km-clay/vicut/wiki/Advanced-Usage#-editing-the-input)
|
||||
- [🎛️ Switching Modes](https://github.com/km-clay/vicut/wiki/Advanced-Usage#%EF%B8%8F-switching-modes)
|
||||
- [✍️ Insert Mode](https://github.com/km-clay/vicut/wiki/Advanced-Usage#%EF%B8%8F-insert-mode)
|
||||
- [👁️ Visual Mode](https://github.com/km-clay/vicut/wiki/Advanced-Usage#%EF%B8%8F-visual-mode)
|
||||
- [🪄 Control Sequence Aliases](https://github.com/km-clay/vicut/wiki/Control-Sequence-Aliases)
|
||||
- [🔑 Special Key Aliases](https://github.com/km-clay/vicut/wiki/Control-Sequence-Aliases#-special-key-aliases)
|
||||
- [🧩 Modifier Keys](https://github.com/km-clay/vicut/wiki/Control-Sequence-Aliases#-modifier-keys)
|
||||
- [💡 Usage Examples](https://github.com/km-clay/vicut/wiki/Usage-Examples)
|
||||
- [📡 Exhibit A: `speedtest-cli --list`](https://github.com/km-clay/vicut/wiki/Usage-Examples#-exhibit-a-speedtest-cli---list)
|
||||
- [📊 Tool Comparison](https://github.com/km-clay/vicut/wiki/Usage-Examples#-tool-comparison)
|
||||
- [🌐 Exhibit B: `nmcli dev`](https://github.com/km-clay/vicut/wiki/Usage-Examples#-exhibit-b-nmcli-dev)
|
||||
- [📊 Tool Comparison](https://github.com/km-clay/vicut/wiki/Usage-Examples#-tool-comparison-1)
|
||||
Reference in New Issue
Block a user