aboutsummaryrefslogtreecommitdiff
diff options
context:
space:
mode:
authorBlaž Hrastnik2021-03-18 06:07:02 +0000
committerBlaž Hrastnik2021-03-18 06:07:02 +0000
commitc331721565a00850849e0a99fd325ae281bfb800 (patch)
tree9645238dc5900e77426e44ecbd4d9e47250d3a1d
parent8ba1e15d296fd3350332d8fa372c4c7703024a67 (diff)
Finish hiding doc.state / State as an implementation detail.
-rw-r--r--helix-core/src/movement.rs81
-rw-r--r--helix-core/src/state.rs113
-rw-r--r--helix-term/src/commands.rs112
-rw-r--r--helix-term/src/ui/mod.rs10
-rw-r--r--helix-view/src/document.rs3
5 files changed, 174 insertions, 145 deletions
diff --git a/helix-core/src/movement.rs b/helix-core/src/movement.rs
index 57abc498..88d6df0a 100644
--- a/helix-core/src/movement.rs
+++ b/helix-core/src/movement.rs
@@ -1,6 +1,66 @@
use crate::graphemes::{nth_next_grapheme_boundary, nth_prev_grapheme_boundary, RopeGraphemes};
use crate::{coords_at_pos, pos_at_coords, ChangeSet, Position, Range, Rope, RopeSlice, Selection};
+#[derive(Copy, Clone, PartialEq, Eq)]
+pub enum Direction {
+ Forward,
+ Backward,
+}
+
+pub fn move_horizontally(
+ text: RopeSlice,
+ range: Range,
+ dir: Direction,
+ count: usize,
+ extend: bool,
+) -> Range {
+ let pos = range.head;
+ let line = text.char_to_line(pos);
+ // TODO: we can optimize clamping by passing in RopeSlice limited to current line. that way
+ // we stop calculating past start/end of line.
+ let pos = match dir {
+ Direction::Backward => {
+ let start = text.line_to_char(line);
+ nth_prev_grapheme_boundary(text, pos, count).max(start)
+ }
+ Direction::Forward => {
+ // Line end is pos at the start of next line - 1
+ // subtract another 1 because the line ends with \n
+ let end = text.line_to_char(line + 1).saturating_sub(2);
+ nth_next_grapheme_boundary(text, pos, count).min(end)
+ }
+ };
+ Range::new(if extend { range.anchor } else { pos }, pos)
+}
+
+pub fn move_vertically(
+ text: RopeSlice,
+ range: Range,
+ dir: Direction,
+ count: usize,
+ extend: bool,
+) -> Range {
+ let Position { row, col } = coords_at_pos(text, range.head);
+
+ let horiz = range.horiz.unwrap_or(col as u32);
+
+ let new_line = match dir {
+ Direction::Backward => row.saturating_sub(count),
+ Direction::Forward => std::cmp::min(row.saturating_add(count), text.len_lines() - 1),
+ };
+
+ // convert to 0-indexed, subtract another 1 because len_chars() counts \n
+ let new_line_len = text.line(new_line).len_chars().saturating_sub(2);
+
+ let new_col = std::cmp::min(horiz as usize, new_line_len);
+
+ let pos = pos_at_coords(text, Position::new(new_line, new_col));
+
+ let mut range = Range::new(if extend { range.anchor } else { pos }, pos);
+ range.horiz = Some(horiz);
+ range
+}
+
pub fn move_next_word_start(slice: RopeSlice, mut pos: usize, count: usize) -> usize {
for _ in 0..count {
if pos + 1 == slice.len_chars() {
@@ -155,3 +215,24 @@ where
*pos = pos.saturating_sub(1);
}
}
+
+#[cfg(test)]
+mod test {
+ use super::*;
+
+ #[test]
+ fn test_vertical_move() {
+ let text = Rope::from("abcd\nefg\nwrs");
+ let slice = text.slice(..);
+ let pos = pos_at_coords(slice, (0, 4).into());
+
+ let range = Range::new(pos, pos);
+ assert_eq!(
+ coords_at_pos(
+ slice,
+ move_vertically(slice, range, Direction::Forward, 1, false).head
+ ),
+ (1, 2).into()
+ );
+ }
+}
diff --git a/helix-core/src/state.rs b/helix-core/src/state.rs
index 9dcd5548..ab475cab 100644
--- a/helix-core/src/state.rs
+++ b/helix-core/src/state.rs
@@ -1,25 +1,12 @@
-use crate::graphemes::{nth_next_grapheme_boundary, nth_prev_grapheme_boundary, RopeGraphemes};
-use crate::{coords_at_pos, pos_at_coords, ChangeSet, Position, Range, Rope, RopeSlice, Selection};
+use crate::{Rope, Selection};
/// A state represents the current editor state of a single buffer.
#[derive(Clone)]
pub struct State {
- // TODO: fields should be private but we need to refactor commands.rs first
pub doc: Rope,
pub selection: Selection,
}
-#[derive(Copy, Clone, PartialEq, Eq)]
-pub enum Direction {
- Forward,
- Backward,
-}
-#[derive(Copy, Clone, PartialEq, Eq)]
-pub enum Granularity {
- Character,
- Line,
-}
-
impl State {
#[must_use]
pub fn new(doc: Rope) -> Self {
@@ -51,102 +38,4 @@ impl State {
// syntax
// foldable
// changeFilter/transactionFilter
-
- pub fn move_range(
- &self,
- range: Range,
- dir: Direction,
- granularity: Granularity,
- count: usize,
- extend: bool,
- ) -> Range {
- let text = self.doc.slice(..);
- let pos = range.head;
- let line = text.char_to_line(pos);
- // TODO: we can optimize clamping by passing in RopeSlice limited to current line. that way
- // we stop calculating past start/end of line.
- let pos = match (dir, granularity) {
- (Direction::Backward, Granularity::Character) => {
- let start = text.line_to_char(line);
- nth_prev_grapheme_boundary(text, pos, count).max(start)
- }
- (Direction::Forward, Granularity::Character) => {
- // Line end is pos at the start of next line - 1
- // subtract another 1 because the line ends with \n
- let end = text.line_to_char(line + 1).saturating_sub(2);
- nth_next_grapheme_boundary(text, pos, count).min(end)
- }
- (_, Granularity::Line) => return move_vertically(text, dir, range, count, extend),
- };
- Range::new(if extend { range.anchor } else { pos }, pos)
- }
-
- pub fn move_selection(
- &self,
- dir: Direction,
- granularity: Granularity,
- count: usize,
- ) -> Selection {
- self.selection
- .transform(|range| self.move_range(range, dir, granularity, count, false))
- }
-
- pub fn extend_selection(
- &self,
- dir: Direction,
- granularity: Granularity,
- count: usize,
- ) -> Selection {
- self.selection
- .transform(|range| self.move_range(range, dir, granularity, count, true))
- }
-}
-
-fn move_vertically(
- text: RopeSlice,
- dir: Direction,
- range: Range,
- count: usize,
- extend: bool,
-) -> Range {
- let Position { row, col } = coords_at_pos(text, range.head);
-
- let horiz = range.horiz.unwrap_or(col as u32);
-
- let new_line = match dir {
- Direction::Backward => row.saturating_sub(count),
- Direction::Forward => std::cmp::min(row.saturating_add(count), text.len_lines() - 1),
- };
-
- // convert to 0-indexed, subtract another 1 because len_chars() counts \n
- let new_line_len = text.line(new_line).len_chars().saturating_sub(2);
-
- let new_col = std::cmp::min(horiz as usize, new_line_len);
-
- let pos = pos_at_coords(text, Position::new(new_line, new_col));
-
- let mut range = Range::new(if extend { range.anchor } else { pos }, pos);
- range.horiz = Some(horiz);
- range
-}
-
-#[cfg(test)]
-mod test {
- use super::*;
-
- #[test]
- fn test_vertical_move() {
- let text = Rope::from("abcd\nefg\nwrs");
- let slice = text.slice(..);
- let pos = pos_at_coords(slice, (0, 4).into());
-
- let range = Range::new(pos, pos);
- assert_eq!(
- coords_at_pos(
- slice,
- move_vertically(slice, Direction::Forward, range, 1, false).head
- ),
- (1, 2).into()
- );
- }
}
diff --git a/helix-term/src/commands.rs b/helix-term/src/commands.rs
index 5b742e43..6165c4c1 100644
--- a/helix-term/src/commands.rs
+++ b/helix-term/src/commands.rs
@@ -1,11 +1,11 @@
use helix_core::{
comment, coords_at_pos, graphemes,
indent::TAB_WIDTH,
- movement, object, pos_at_coords,
+ movement::{self, Direction},
+ object, pos_at_coords,
regex::{self, Regex},
- register, search, selection,
- state::{Direction, Granularity},
- Change, ChangeSet, Position, Range, Rope, RopeSlice, Selection, Tendril, Transaction,
+ register, search, selection, Change, ChangeSet, Position, Range, Rope, RopeSlice, Selection,
+ Tendril, Transaction,
};
use once_cell::sync::Lazy;
@@ -72,36 +72,64 @@ pub type Command = fn(cx: &mut Context);
pub fn move_char_left(cx: &mut Context) {
let count = cx.count;
let doc = cx.doc();
- let selection = doc
- .state
- .move_selection(Direction::Backward, Granularity::Character, count);
+ let text = doc.text().slice(..);
+ let selection = doc.selection().transform(|range| {
+ movement::move_horizontally(
+ text,
+ range,
+ Direction::Backward,
+ count,
+ false, /* extend */
+ )
+ });
doc.set_selection(selection);
}
pub fn move_char_right(cx: &mut Context) {
let count = cx.count;
let doc = cx.doc();
- let selection = doc
- .state
- .move_selection(Direction::Forward, Granularity::Character, count);
+ let text = doc.text().slice(..);
+ let selection = doc.selection().transform(|range| {
+ movement::move_horizontally(
+ text,
+ range,
+ Direction::Forward,
+ count,
+ false, /* extend */
+ )
+ });
doc.set_selection(selection);
}
pub fn move_line_up(cx: &mut Context) {
let count = cx.count;
let doc = cx.doc();
- let selection = doc
- .state
- .move_selection(Direction::Backward, Granularity::Line, count);
+ let text = doc.text().slice(..);
+ let selection = doc.selection().transform(|range| {
+ movement::move_vertically(
+ text,
+ range,
+ Direction::Backward,
+ count,
+ false, /* extend */
+ )
+ });
doc.set_selection(selection);
}
pub fn move_line_down(cx: &mut Context) {
let count = cx.count;
let doc = cx.doc();
- let selection = doc
- .state
- .move_selection(Direction::Forward, Granularity::Line, count);
+ let text = doc.text().slice(..);
+ let selection = doc.selection().transform(|range| {
+ movement::move_vertically(
+ text,
+ range,
+ Direction::Forward,
+ count,
+ false, /* extend */
+ )
+ });
doc.set_selection(selection);
}
@@ -409,36 +437,64 @@ pub fn half_page_down(cx: &mut Context) {
pub fn extend_char_left(cx: &mut Context) {
let count = cx.count;
let doc = cx.doc();
- let selection = doc
- .state
- .extend_selection(Direction::Backward, Granularity::Character, count);
+ let text = doc.text().slice(..);
+ let selection = doc.selection().transform(|range| {
+ movement::move_horizontally(
+ text,
+ range,
+ Direction::Backward,
+ count,
+ true, /* extend */
+ )
+ });
doc.set_selection(selection);
}
pub fn extend_char_right(cx: &mut Context) {
let count = cx.count;
let doc = cx.doc();
- let selection = doc
- .state
- .extend_selection(Direction::Forward, Granularity::Character, count);
+ let text = doc.text().slice(..);
+ let selection = doc.selection().transform(|range| {
+ movement::move_horizontally(
+ text,
+ range,
+ Direction::Forward,
+ count,
+ true, /* extend */
+ )
+ });
doc.set_selection(selection);
}
pub fn extend_line_up(cx: &mut Context) {
let count = cx.count;
let doc = cx.doc();
- let selection = doc
- .state
- .extend_selection(Direction::Backward, Granularity::Line, count);
+ let text = doc.text().slice(..);
+ let selection = doc.selection().transform(|range| {
+ movement::move_vertically(
+ text,
+ range,
+ Direction::Backward,
+ count,
+ true, /* extend */
+ )
+ });
doc.set_selection(selection);
}
pub fn extend_line_down(cx: &mut Context) {
let count = cx.count;
let doc = cx.doc();
- let selection = doc
- .state
- .extend_selection(Direction::Forward, Granularity::Line, count);
+ let text = doc.text().slice(..);
+ let selection = doc.selection().transform(|range| {
+ movement::move_vertically(
+ text,
+ range,
+ Direction::Forward,
+ count,
+ true, /* extend */
+ )
+ });
doc.set_selection(selection);
}
diff --git a/helix-term/src/ui/mod.rs b/helix-term/src/ui/mod.rs
index f2376504..f7ba59cd 100644
--- a/helix-term/src/ui/mod.rs
+++ b/helix-term/src/ui/mod.rs
@@ -31,7 +31,7 @@ pub fn regex_prompt(
prompt: String,
fun: impl Fn(&mut Document, Regex) + 'static,
) -> Prompt {
- let snapshot = cx.doc().state.clone();
+ let snapshot = cx.doc().selection().clone();
Prompt::new(
prompt,
@@ -39,9 +39,10 @@ pub fn regex_prompt(
move |editor: &mut Editor, input: &str, event: PromptEvent| {
match event {
PromptEvent::Abort => {
- // revert state
+ // TODO: also revert doc
+ // TODO: also revert text
let doc = &mut editor.view_mut().doc;
- doc.state = snapshot.clone();
+ doc.set_selection(snapshot.clone());
}
PromptEvent::Validate => {
//
@@ -58,7 +59,8 @@ pub fn regex_prompt(
let doc = &mut view.doc;
// revert state to what it was before the last update
- doc.state = snapshot.clone();
+ // TODO: also revert text
+ doc.set_selection(snapshot.clone());
fun(doc, regex);
diff --git a/helix-view/src/document.rs b/helix-view/src/document.rs
index 76a3b8e9..e606ec3c 100644
--- a/helix-view/src/document.rs
+++ b/helix-view/src/document.rs
@@ -17,7 +17,8 @@ pub enum Mode {
}
pub struct Document {
- pub state: State, // rope + selection
+ // rope + selection
+ state: State,
path: Option<PathBuf>,
/// Current editing mode.