aboutsummaryrefslogtreecommitdiff
path: root/helix-core
diff options
context:
space:
mode:
Diffstat (limited to 'helix-core')
-rw-r--r--helix-core/src/graphemes.rs14
-rw-r--r--helix-core/src/indent.rs31
-rw-r--r--helix-core/src/lib.rs30
-rw-r--r--helix-core/src/selection.rs16
-rw-r--r--helix-core/src/state.rs50
-rw-r--r--helix-core/src/syntax.rs18
6 files changed, 88 insertions, 71 deletions
diff --git a/helix-core/src/graphemes.rs b/helix-core/src/graphemes.rs
index 42e27b90..e0693769 100644
--- a/helix-core/src/graphemes.rs
+++ b/helix-core/src/graphemes.rs
@@ -26,7 +26,7 @@ pub fn grapheme_width(g: &str) -> usize {
}
}
-pub fn nth_prev_grapheme_boundary(slice: &RopeSlice, char_idx: usize, n: usize) -> usize {
+pub fn nth_prev_grapheme_boundary(slice: RopeSlice, char_idx: usize, n: usize) -> usize {
// TODO: implement this more efficiently. This has to do a lot of
// re-scanning of rope chunks. Probably move the main implementation here,
// and have prev_grapheme_boundary call this instead.
@@ -38,7 +38,7 @@ pub fn nth_prev_grapheme_boundary(slice: &RopeSlice, char_idx: usize, n: usize)
}
/// Finds the previous grapheme boundary before the given char position.
-pub fn prev_grapheme_boundary(slice: &RopeSlice, char_idx: usize) -> usize {
+pub fn prev_grapheme_boundary(slice: RopeSlice, char_idx: usize) -> usize {
// Bounds check
debug_assert!(char_idx <= slice.len_chars());
@@ -74,7 +74,7 @@ pub fn prev_grapheme_boundary(slice: &RopeSlice, char_idx: usize) -> usize {
}
}
-pub fn nth_next_grapheme_boundary(slice: &RopeSlice, char_idx: usize, n: usize) -> usize {
+pub fn nth_next_grapheme_boundary(slice: RopeSlice, char_idx: usize, n: usize) -> usize {
// TODO: implement this more efficiently. This has to do a lot of
// re-scanning of rope chunks. Probably move the main implementation here,
// and have next_grapheme_boundary call this instead.
@@ -86,7 +86,7 @@ pub fn nth_next_grapheme_boundary(slice: &RopeSlice, char_idx: usize, n: usize)
}
/// Finds the next grapheme boundary after the given char position.
-pub fn next_grapheme_boundary(slice: &RopeSlice, char_idx: usize) -> usize {
+pub fn next_grapheme_boundary(slice: RopeSlice, char_idx: usize) -> usize {
// Bounds check
debug_assert!(char_idx <= slice.len_chars());
@@ -123,7 +123,7 @@ pub fn next_grapheme_boundary(slice: &RopeSlice, char_idx: usize) -> usize {
}
/// Returns whether the given char position is a grapheme boundary.
-pub fn is_grapheme_boundary(slice: &RopeSlice, char_idx: usize) -> bool {
+pub fn is_grapheme_boundary(slice: RopeSlice, char_idx: usize) -> bool {
// Bounds check
debug_assert!(char_idx <= slice.len_chars());
@@ -160,11 +160,11 @@ pub struct RopeGraphemes<'a> {
}
impl<'a> RopeGraphemes<'a> {
- pub fn new<'b>(slice: &RopeSlice<'b>) -> RopeGraphemes<'b> {
+ pub fn new<'b>(slice: RopeSlice<'b>) -> RopeGraphemes<'b> {
let mut chunks = slice.chunks();
let first_chunk = chunks.next().unwrap_or("");
RopeGraphemes {
- text: *slice,
+ text: slice,
chunks,
cur_chunk: first_chunk,
cur_chunk_start: 0,
diff --git a/helix-core/src/indent.rs b/helix-core/src/indent.rs
index e708b317..49552b4f 100644
--- a/helix-core/src/indent.rs
+++ b/helix-core/src/indent.rs
@@ -1,4 +1,5 @@
use crate::{
+ find_first_non_whitespace_char,
syntax::Syntax,
tree_sitter::{Node, Tree},
Rope, RopeSlice, State,
@@ -9,7 +10,7 @@ use crate::{
pub const TAB_WIDTH: usize = 4;
-fn indent_level_for_line(line: &RopeSlice) -> usize {
+fn indent_level_for_line(line: RopeSlice) -> usize {
let mut len = 0;
for ch in line.chars() {
match ch {
@@ -159,27 +160,11 @@ fn calculate_indentation(node: Option<Node>, newline: bool) -> usize {
increment as usize
}
-fn find_first_non_whitespace_char(state: &State, line_num: usize) -> Option<usize> {
- let line = state.doc.line(line_num);
- let mut start = state.doc.line_to_char(line_num);
-
- // find first non-whitespace char
- for ch in line.chars() {
- // TODO: could use memchr with chunks?
- if ch != ' ' && ch != '\t' && ch != '\n' {
- return Some(start);
- }
- start += 1;
- }
-
- None
-}
-
fn suggested_indent_for_line(syntax: Option<&Syntax>, state: &State, line_num: usize) -> usize {
let line = state.doc.line(line_num);
- let current = indent_level_for_line(&line);
+ let current = indent_level_for_line(line);
- if let Some(start) = find_first_non_whitespace_char(state, line_num) {
+ if let Some(start) = find_first_non_whitespace_char(state.doc.slice(..), line_num) {
return suggested_indent_for_pos(syntax, state, start, false);
};
@@ -216,12 +201,12 @@ mod test {
#[test]
fn test_indent_level() {
let line = Rope::from(" fn new"); // 8 spaces
- assert_eq!(indent_level_for_line(&line.slice(..)), 2);
+ assert_eq!(indent_level_for_line(line.slice(..)), 2);
let line = Rope::from("\t\t\tfn new"); // 3 tabs
- assert_eq!(indent_level_for_line(&line.slice(..)), 3);
+ assert_eq!(indent_level_for_line(line.slice(..)), 3);
// mixed indentation
let line = Rope::from("\t \tfn new"); // 1 tab, 4 spaces, tab
- assert_eq!(indent_level_for_line(&line.slice(..)), 3);
+ assert_eq!(indent_level_for_line(line.slice(..)), 3);
}
#[test]
@@ -306,7 +291,7 @@ where
for i in 0..state.doc.len_lines() {
let line = text.line(i);
- let indent = indent_level_for_line(&line);
+ let indent = indent_level_for_line(line);
assert_eq!(
suggested_indent_for_line(Some(&syntax), &state, i),
indent,
diff --git a/helix-core/src/lib.rs b/helix-core/src/lib.rs
index ddf1439c..f9fe4bd4 100644
--- a/helix-core/src/lib.rs
+++ b/helix-core/src/lib.rs
@@ -11,6 +11,36 @@ pub mod state;
pub mod syntax;
mod transaction;
+pub(crate) fn find_first_non_whitespace_char2(line: RopeSlice) -> Option<usize> {
+ let mut start = 0;
+
+ // find first non-whitespace char
+ for ch in line.chars() {
+ // TODO: could use memchr with chunks?
+ if ch != ' ' && ch != '\t' && ch != '\n' {
+ return Some(start);
+ }
+ start += 1;
+ }
+
+ None
+}
+pub(crate) fn find_first_non_whitespace_char(text: RopeSlice, line_num: usize) -> Option<usize> {
+ let line = text.line(line_num);
+ let mut start = text.line_to_char(line_num);
+
+ // find first non-whitespace char
+ for ch in line.chars() {
+ // TODO: could use memchr with chunks?
+ if ch != ' ' && ch != '\t' && ch != '\n' {
+ return Some(start);
+ }
+ start += 1;
+ }
+
+ None
+}
+
pub use ropey::{Rope, RopeSlice};
pub use tendril::StrTendril as Tendril;
diff --git a/helix-core/src/selection.rs b/helix-core/src/selection.rs
index 9a93e667..d3ac045f 100644
--- a/helix-core/src/selection.rs
+++ b/helix-core/src/selection.rs
@@ -120,7 +120,7 @@ impl Range {
// groupAt
#[inline]
- pub fn fragment<'a>(&'a self, text: &'a RopeSlice) -> Cow<'a, str> {
+ pub fn fragment<'a>(&'a self, text: RopeSlice<'a>) -> Cow<'a, str> {
Cow::from(text.slice(self.from()..self.to() + 1))
}
}
@@ -267,7 +267,7 @@ impl Selection {
)
}
- pub fn fragments<'a>(&'a self, text: &'a RopeSlice) -> impl Iterator<Item = Cow<str>> + 'a {
+ pub fn fragments<'a>(&'a self, text: RopeSlice<'a>) -> impl Iterator<Item = Cow<str>> + 'a {
self.ranges.iter().map(move |range| range.fragment(text))
}
}
@@ -275,7 +275,7 @@ impl Selection {
// TODO: checkSelection -> check if valid for doc length
pub fn select_on_matches(
- text: &RopeSlice,
+ text: RopeSlice,
selections: &Selection,
regex: &crate::regex::Regex,
) -> Option<Selection> {
@@ -283,7 +283,7 @@ pub fn select_on_matches(
for sel in selections.ranges() {
// TODO: can't avoid occasional allocations since Regex can't operate on chunks yet
- let fragment = sel.fragment(&text);
+ let fragment = sel.fragment(text);
let mut sel_start = sel.from();
let sel_end = sel.to();
@@ -309,7 +309,7 @@ pub fn select_on_matches(
// TODO: support to split on capture #N instead of whole match
pub fn split_on_matches(
- text: &RopeSlice,
+ text: RopeSlice,
selections: &Selection,
regex: &crate::regex::Regex,
) -> Selection {
@@ -317,7 +317,7 @@ pub fn split_on_matches(
for sel in selections.ranges() {
// TODO: can't avoid occasional allocations since Regex can't operate on chunks yet
- let fragment = sel.fragment(&text);
+ let fragment = sel.fragment(text);
let mut sel_start = sel.from();
let sel_end = sel.to();
@@ -420,7 +420,7 @@ mod test {
let selections = Selection::new(smallvec![Range::new(0, 8), Range::new(10, 19),], 0);
- let result = split_on_matches(&text.slice(..), &selections, &Regex::new(r"\s+").unwrap());
+ let result = split_on_matches(text.slice(..), &selections, &Regex::new(r"\s+").unwrap());
assert_eq!(
result.ranges(),
@@ -434,7 +434,7 @@ mod test {
);
assert_eq!(
- result.fragments(&text.slice(..)).collect::<Vec<_>>(),
+ result.fragments(text.slice(..)).collect::<Vec<_>>(),
&["abcd", "efg", "rs", "xyz", "1"]
);
}
diff --git a/helix-core/src/state.rs b/helix-core/src/state.rs
index 1b8ae1eb..65194b38 100644
--- a/helix-core/src/state.rs
+++ b/helix-core/src/state.rs
@@ -101,7 +101,7 @@ impl State {
let line = text.char_to_line(pos);
let start = text.line_to_char(line);
let pos = std::cmp::max(
- nth_prev_grapheme_boundary(&text.slice(..), pos, count),
+ nth_prev_grapheme_boundary(text.slice(..), pos, count),
start,
);
Range::new(if extend { range.anchor } else { pos }, pos)
@@ -113,14 +113,14 @@ impl State {
// subtract another 1 because the line ends with \n
let end = text.line_to_char(line + 1).saturating_sub(2);
let pos =
- std::cmp::min(nth_next_grapheme_boundary(&text.slice(..), pos, count), end);
+ std::cmp::min(nth_next_grapheme_boundary(text.slice(..), pos, count), end);
Range::new(if extend { range.anchor } else { pos }, pos)
}
- (_, Granularity::Line) => move_vertically(&text.slice(..), dir, range, count, extend),
+ (_, Granularity::Line) => move_vertically(text.slice(..), dir, range, count, extend),
}
}
- pub fn move_next_word_start(slice: &RopeSlice, mut pos: usize) -> usize {
+ pub fn move_next_word_start(slice: RopeSlice, mut pos: usize) -> usize {
// TODO: confirm it's fine without using graphemes, I think it should be
let ch = slice.char(pos);
let next = slice.char(pos.saturating_add(1));
@@ -143,7 +143,7 @@ impl State {
pos
}
- pub fn move_prev_word_start(slice: &RopeSlice, mut pos: usize) -> usize {
+ pub fn move_prev_word_start(slice: RopeSlice, mut pos: usize) -> usize {
// TODO: confirm it's fine without using graphemes, I think it should be
let ch = slice.char(pos);
let prev = slice.char(pos.saturating_sub(1)); // TODO: just return original pos if at start
@@ -169,7 +169,7 @@ impl State {
pos.saturating_add(1)
}
- pub fn move_next_word_end(slice: &RopeSlice, mut pos: usize, _count: usize) -> usize {
+ pub fn move_next_word_end(slice: RopeSlice, mut pos: usize, _count: usize) -> usize {
// TODO: confirm it's fine without using graphemes, I think it should be
let ch = slice.char(pos);
let next = slice.char(pos.saturating_add(1));
@@ -217,7 +217,7 @@ impl State {
}
/// Convert a character index to (line, column) coordinates.
-pub fn coords_at_pos(text: &RopeSlice, pos: usize) -> Position {
+pub fn coords_at_pos(text: RopeSlice, pos: usize) -> Position {
let line = text.char_to_line(pos);
let line_start = text.line_to_char(line);
let col = text.slice(line_start..pos).len_chars();
@@ -225,14 +225,14 @@ pub fn coords_at_pos(text: &RopeSlice, pos: usize) -> Position {
}
/// Convert (line, column) coordinates to a character index.
-pub fn pos_at_coords(text: &RopeSlice, coords: Position) -> usize {
+pub fn pos_at_coords(text: RopeSlice, coords: Position) -> usize {
let Position { row, col } = coords;
let line_start = text.line_to_char(row);
nth_next_grapheme_boundary(text, line_start, col)
}
fn move_vertically(
- text: &RopeSlice,
+ text: RopeSlice,
dir: Direction,
range: Range,
count: usize,
@@ -287,7 +287,7 @@ fn categorize(ch: char) -> Category {
}
}
-fn skip_over_next<F>(slice: &RopeSlice, pos: &mut usize, fun: F)
+fn skip_over_next<F>(slice: RopeSlice, pos: &mut usize, fun: F)
where
F: Fn(char) -> bool,
{
@@ -301,7 +301,7 @@ where
}
}
-fn skip_over_prev<F>(slice: &RopeSlice, pos: &mut usize, fun: F)
+fn skip_over_prev<F>(slice: RopeSlice, pos: &mut usize, fun: F)
where
F: Fn(char) -> bool,
{
@@ -323,34 +323,36 @@ mod test {
#[test]
fn test_coords_at_pos() {
let text = Rope::from("ḧëḷḷö\nẅöṛḷḋ");
- assert_eq!(coords_at_pos(&text.slice(..), 0), (0, 0).into());
- assert_eq!(coords_at_pos(&text.slice(..), 5), (0, 5).into()); // position on \n
- assert_eq!(coords_at_pos(&text.slice(..), 6), (1, 0).into()); // position on w
- assert_eq!(coords_at_pos(&text.slice(..), 7), (1, 1).into()); // position on o
- assert_eq!(coords_at_pos(&text.slice(..), 10), (1, 4).into()); // position on d
+ let slice = text.slice(..);
+ assert_eq!(coords_at_pos(slice, 0), (0, 0).into());
+ assert_eq!(coords_at_pos(slice, 5), (0, 5).into()); // position on \n
+ assert_eq!(coords_at_pos(slice, 6), (1, 0).into()); // position on w
+ assert_eq!(coords_at_pos(slice, 7), (1, 1).into()); // position on o
+ assert_eq!(coords_at_pos(slice, 10), (1, 4).into()); // position on d
}
#[test]
fn test_pos_at_coords() {
let text = Rope::from("ḧëḷḷö\nẅöṛḷḋ");
- assert_eq!(pos_at_coords(&text.slice(..), (0, 0).into()), 0);
- assert_eq!(pos_at_coords(&text.slice(..), (0, 5).into()), 5); // position on \n
- assert_eq!(pos_at_coords(&text.slice(..), (1, 0).into()), 6); // position on w
- assert_eq!(pos_at_coords(&text.slice(..), (1, 1).into()), 7); // position on o
- assert_eq!(pos_at_coords(&text.slice(..), (1, 4).into()), 10); // position on d
+ let slice = text.slice(..);
+ assert_eq!(pos_at_coords(slice, (0, 0).into()), 0);
+ assert_eq!(pos_at_coords(slice, (0, 5).into()), 5); // position on \n
+ assert_eq!(pos_at_coords(slice, (1, 0).into()), 6); // position on w
+ assert_eq!(pos_at_coords(slice, (1, 1).into()), 7); // position on o
+ assert_eq!(pos_at_coords(slice, (1, 4).into()), 10); // position on d
}
#[test]
fn test_vertical_move() {
let text = Rope::from("abcd\nefg\nwrs");
- let pos = pos_at_coords(&text.slice(..), (0, 4).into());
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
+ slice,
+ move_vertically(slice, Direction::Forward, range, 1, false).head
),
(1, 2).into()
);
diff --git a/helix-core/src/syntax.rs b/helix-core/src/syntax.rs
index 861cda0c..32974e11 100644
--- a/helix-core/src/syntax.rs
+++ b/helix-core/src/syntax.rs
@@ -406,7 +406,7 @@ impl LanguageLayer {
}
pub(crate) fn generate_edits(
- old_text: &RopeSlice,
+ old_text: RopeSlice,
changeset: &ChangeSet,
) -> Vec<tree_sitter::InputEdit> {
use Operation::*;
@@ -419,7 +419,7 @@ impl LanguageLayer {
// TODO; this is a lot easier with Change instead of Operation.
- fn point_at_pos(text: &RopeSlice, pos: usize) -> (usize, Point) {
+ fn point_at_pos(text: RopeSlice, pos: usize) -> (usize, Point) {
let byte = text.char_to_byte(pos); // <- attempted to index past end
let line = text.char_to_line(pos);
let line_start_byte = text.line_to_byte(line);
@@ -458,8 +458,8 @@ impl LanguageLayer {
new_pos += len;
}
Delete(_) => {
- let (start_byte, start_position) = point_at_pos(&old_text, old_pos);
- let (old_end_byte, old_end_position) = point_at_pos(&old_text, old_end);
+ let (start_byte, start_position) = point_at_pos(old_text, old_pos);
+ let (old_end_byte, old_end_position) = point_at_pos(old_text, old_end);
// TODO: Position also needs to be byte based...
// let byte = char_to_byte(old_pos)
@@ -478,14 +478,14 @@ impl LanguageLayer {
});
}
Insert(s) => {
- let (start_byte, start_position) = point_at_pos(&old_text, old_pos);
+ let (start_byte, start_position) = point_at_pos(old_text, old_pos);
let ins = s.chars().count();
// a subsequent delete means a replace, consume it
if let Some(Delete(len)) = iter.peek() {
old_end = old_pos + len;
- let (old_end_byte, old_end_position) = point_at_pos(&old_text, old_end);
+ let (old_end_byte, old_end_position) = point_at_pos(old_text, old_end);
iter.next();
@@ -530,7 +530,7 @@ impl LanguageLayer {
return Ok(());
}
- let edits = Self::generate_edits(&old_source.slice(..), changeset);
+ let edits = Self::generate_edits(old_source.slice(..), changeset);
// Notify the tree about all the changes
for edit in edits {
@@ -1528,7 +1528,7 @@ fn test_input_edits() {
&state,
vec![(6, 11, Some("test".into())), (12, 17, None)].into_iter(),
);
- let edits = LanguageLayer::generate_edits(&state.doc.slice(..), &transaction.changes);
+ let edits = LanguageLayer::generate_edits(state.doc.slice(..), &transaction.changes);
// transaction.apply(&mut state);
assert_eq!(
@@ -1556,7 +1556,7 @@ fn test_input_edits() {
// Testing with the official example from tree-sitter
let mut state = State::new("fn test() {}".into());
let transaction = Transaction::change(&state, vec![(8, 8, Some("a: u32".into()))].into_iter());
- let edits = LanguageLayer::generate_edits(&state.doc.slice(..), &transaction.changes);
+ let edits = LanguageLayer::generate_edits(state.doc.slice(..), &transaction.changes);
transaction.apply(&mut state);
assert_eq!(state.doc(), "fn test(a: u32) {}");