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 {
|
pub fn get(self) -> usize {
|
||||||
self.value
|
self.value
|
||||||
}
|
}
|
||||||
|
pub fn cap(&self) -> usize {
|
||||||
|
self.max
|
||||||
|
}
|
||||||
pub fn upper_bound(&self) -> usize {
|
pub fn upper_bound(&self) -> usize {
|
||||||
if self.exclusive {
|
if self.exclusive {
|
||||||
self.max.saturating_sub(1)
|
self.max.saturating_sub(1)
|
||||||
@@ -935,29 +938,15 @@ impl LineBuf {
|
|||||||
let mut closer_count: u32 = 0;
|
let mut closer_count: u32 = 0;
|
||||||
while let Some(idx) = backward_indices.next() {
|
while let Some(idx) = backward_indices.next() {
|
||||||
let gr = self.grapheme_at(idx)?.to_string();
|
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;
|
if gr == closer {
|
||||||
while let Some(idx) = backward_indices.next() {
|
closer_count += 1;
|
||||||
// Keep consuming indices as long as they refer to a backslash
|
} else if closer_count == 0 {
|
||||||
let Some("\\") = self.grapheme_at(idx) else {
|
start_pos = Some(idx);
|
||||||
break
|
break
|
||||||
};
|
} else {
|
||||||
// On each backslash, flip this boolean
|
closer_count = closer_count.saturating_sub(1)
|
||||||
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)
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -968,8 +957,8 @@ impl LineBuf {
|
|||||||
let mut opener_count: u32 = 0;
|
let mut opener_count: u32 = 0;
|
||||||
|
|
||||||
while let Some(idx) = forward_indices.next() {
|
while let Some(idx) = forward_indices.next() {
|
||||||
|
if self.grapheme_is_escaped(idx) { continue }
|
||||||
match self.grapheme_at(idx)? {
|
match self.grapheme_at(idx)? {
|
||||||
"\\" => { forward_indices.next(); }
|
|
||||||
gr if gr == opener => opener_count += 1,
|
gr if gr == opener => opener_count += 1,
|
||||||
gr if gr == closer => {
|
gr if gr == closer => {
|
||||||
if opener_count == 0 {
|
if opener_count == 0 {
|
||||||
@@ -991,8 +980,8 @@ impl LineBuf {
|
|||||||
let mut opener_count: u32 = 0;
|
let mut opener_count: u32 = 0;
|
||||||
|
|
||||||
while let Some(idx) = forward_indices.next() {
|
while let Some(idx) = forward_indices.next() {
|
||||||
|
if self.grapheme_is_escaped(idx) { continue }
|
||||||
match self.grapheme_at(idx)? {
|
match self.grapheme_at(idx)? {
|
||||||
"\\" => { forward_indices.next(); }
|
|
||||||
gr if gr == opener => {
|
gr if gr == opener => {
|
||||||
if opener_count == 0 {
|
if opener_count == 0 {
|
||||||
start = Some(idx);
|
start = Some(idx);
|
||||||
@@ -1022,6 +1011,19 @@ impl LineBuf {
|
|||||||
Bound::Around => {
|
Bound::Around => {
|
||||||
// End excludes the quote, so push it forward
|
// End excludes the quote, so push it forward
|
||||||
end += 1;
|
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 {
|
match text_obj {
|
||||||
TextObj::Sentence(dir) |
|
TextObj::Sentence(dir) |
|
||||||
TextObj::Paragraph(dir) => {
|
TextObj::Paragraph(dir) => {
|
||||||
match dir {
|
match dir {
|
||||||
Direction::Forward => MotionKind::On(end),
|
Direction::Forward => MotionKind::On(end),
|
||||||
Direction::Backward => {
|
Direction::Backward => {
|
||||||
let cur_sentence_start = start;
|
let cur_sentence_start = start;
|
||||||
let mut start_pos = self.cursor.get();
|
let mut start_pos = self.cursor.get();
|
||||||
for _ in 0..count {
|
for _ in 0..count {
|
||||||
if self.is_sentence_start(start_pos) {
|
if self.is_sentence_start(start_pos) {
|
||||||
// We know there is some punctuation before us now
|
// We know there is some punctuation before us now
|
||||||
// Let's find it
|
// Let's find it
|
||||||
let mut bkwd_indices = (0..start_pos).rev();
|
let mut bkwd_indices = (0..start_pos).rev();
|
||||||
let punct_pos = bkwd_indices
|
let punct_pos = bkwd_indices
|
||||||
.find(|idx| self.grapheme_at(*idx).is_some_and(|gr| PUNCTUATION.contains(&gr)))
|
.find(|idx| self.grapheme_at(*idx).is_some_and(|gr| PUNCTUATION.contains(&gr)))
|
||||||
.unwrap();
|
.unwrap();
|
||||||
if self.grapheme_before(punct_pos).is_some() {
|
if self.grapheme_before(punct_pos).is_some() {
|
||||||
let Some((new_start,_)) = self.text_obj_sentence(punct_pos - 1, count, Bound::Inside) else {
|
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
|
return MotionKind::Null
|
||||||
};
|
}
|
||||||
start_pos = new_start;
|
|
||||||
continue
|
|
||||||
} else {
|
} 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)),
|
||||||
|
}
|
||||||
}
|
}
|
||||||
_ => {
|
TextObj::DoubleQuote(_) |
|
||||||
|
TextObj::SingleQuote(_) |
|
||||||
MotionKind::Inclusive((start,end))
|
TextObj::BacktickQuote(_) |
|
||||||
}
|
TextObj::Paren(_) |
|
||||||
|
TextObj::Bracket(_) |
|
||||||
|
TextObj::Brace(_) |
|
||||||
|
TextObj::Angle(_) => MotionKind::Exclusive((start,end)),
|
||||||
|
_ => todo!()
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
MotionCmd(_,Motion::ToDelimMatch) => {
|
MotionCmd(_,Motion::ToDelimMatch) => {
|
||||||
@@ -1927,7 +1941,7 @@ impl LineBuf {
|
|||||||
}
|
}
|
||||||
|
|
||||||
final_end = final_end.min(self.cursor.max);
|
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::RepeatMotion) => todo!(),
|
||||||
MotionCmd(count,Motion::RepeatMotionRev) => todo!(),
|
MotionCmd(count,Motion::RepeatMotionRev) => todo!(),
|
||||||
@@ -2041,7 +2055,15 @@ impl LineBuf {
|
|||||||
let end = end.min(col);
|
let end = end.min(col);
|
||||||
self.cursor.set(start + end)
|
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)) => {
|
MotionKind::Exclusive((start,end)) => {
|
||||||
if self.select_range().is_none() {
|
if self.select_range().is_none() {
|
||||||
self.cursor.set(start)
|
self.cursor.set(start)
|
||||||
@@ -2081,11 +2103,11 @@ impl LineBuf {
|
|||||||
ordered(self.cursor.get(), pos)
|
ordered(self.cursor.get(), pos)
|
||||||
}
|
}
|
||||||
MotionKind::InclusiveWithTargetCol((start,end),_) |
|
MotionKind::InclusiveWithTargetCol((start,end),_) |
|
||||||
MotionKind::Inclusive((start,end)) => ordered(*start, *end),
|
MotionKind::Exclusive((start,end)) => ordered(*start, *end),
|
||||||
MotionKind::ExclusiveWithTargetCol((start,end),_) |
|
MotionKind::ExclusiveWithTargetCol((start,end),_) |
|
||||||
MotionKind::Exclusive((start,end)) => {
|
MotionKind::Inclusive((start,end)) => {
|
||||||
let (start, mut end) = ordered(*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)
|
(start,end)
|
||||||
}
|
}
|
||||||
MotionKind::Null => return None
|
MotionKind::Null => return None
|
||||||
@@ -2463,7 +2485,7 @@ impl LineBuf {
|
|||||||
.map(|m| self.eval_motion(verb_ref.as_ref(), m))
|
.map(|m| self.eval_motion(verb_ref.as_ref(), m))
|
||||||
.unwrap_or({
|
.unwrap_or({
|
||||||
self.select_range
|
self.select_range
|
||||||
.map(MotionKind::Inclusive)
|
.map(MotionKind::Exclusive)
|
||||||
.unwrap_or(MotionKind::Null)
|
.unwrap_or(MotionKind::Null)
|
||||||
})
|
})
|
||||||
};
|
};
|
||||||
|
|||||||
@@ -941,6 +941,8 @@ impl ViNormal {
|
|||||||
let obj = match chars_clone.next().unwrap() {
|
let obj = match chars_clone.next().unwrap() {
|
||||||
'w' => TextObj::Word(Word::Normal,bound),
|
'w' => TextObj::Word(Word::Normal,bound),
|
||||||
'W' => TextObj::Word(Word::Big,bound),
|
'W' => TextObj::Word(Word::Big,bound),
|
||||||
|
's' => TextObj::WholeSentence(bound),
|
||||||
|
'p' => TextObj::WholeParagraph(bound),
|
||||||
'"' => TextObj::DoubleQuote(bound),
|
'"' => TextObj::DoubleQuote(bound),
|
||||||
'\'' => TextObj::SingleQuote(bound),
|
'\'' => TextObj::SingleQuote(bound),
|
||||||
'`' => TextObj::BacktickQuote(bound),
|
'`' => TextObj::BacktickQuote(bound),
|
||||||
@@ -1602,6 +1604,8 @@ impl ViVisual {
|
|||||||
let obj = match chars_clone.next().unwrap() {
|
let obj = match chars_clone.next().unwrap() {
|
||||||
'w' => TextObj::Word(Word::Normal,bound),
|
'w' => TextObj::Word(Word::Normal,bound),
|
||||||
'W' => TextObj::Word(Word::Big,bound),
|
'W' => TextObj::Word(Word::Big,bound),
|
||||||
|
's' => TextObj::WholeSentence(bound),
|
||||||
|
'p' => TextObj::WholeParagraph(bound),
|
||||||
'"' => TextObj::DoubleQuote(bound),
|
'"' => TextObj::DoubleQuote(bound),
|
||||||
'\'' => TextObj::SingleQuote(bound),
|
'\'' => TextObj::SingleQuote(bound),
|
||||||
'`' => TextObj::BacktickQuote(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