mirror of
https://github.com/helix-editor/helix.git
synced 2024-11-26 11:23:31 +04:00
Document more of helix-core (#904)
This commit is contained in:
parent
acc5ac5e73
commit
92c2d5d3bf
@ -1,3 +1,6 @@
|
|||||||
|
//! When typing the opening character of one of the possible pairs defined below,
|
||||||
|
//! this module provides the functionality to insert the paired closing character.
|
||||||
|
|
||||||
use crate::{Range, Rope, Selection, Tendril, Transaction};
|
use crate::{Range, Rope, Selection, Tendril, Transaction};
|
||||||
use smallvec::SmallVec;
|
use smallvec::SmallVec;
|
||||||
|
|
||||||
|
@ -1,3 +1,5 @@
|
|||||||
|
//! Utility functions to categorize a `char`.
|
||||||
|
|
||||||
use crate::LineEnding;
|
use crate::LineEnding;
|
||||||
|
|
||||||
#[derive(Debug, Eq, PartialEq)]
|
#[derive(Debug, Eq, PartialEq)]
|
||||||
|
@ -1,3 +1,6 @@
|
|||||||
|
//! This module contains the functionality toggle comments on lines over the selection
|
||||||
|
//! using the comment character defined in the user's `languages.toml`
|
||||||
|
|
||||||
use crate::{
|
use crate::{
|
||||||
find_first_non_whitespace_char, Change, Rope, RopeSlice, Selection, Tendril, Transaction,
|
find_first_non_whitespace_char, Change, Rope, RopeSlice, Selection, Tendril, Transaction,
|
||||||
};
|
};
|
||||||
|
@ -1,3 +1,6 @@
|
|||||||
|
//! LSP diagnostic utility types.
|
||||||
|
|
||||||
|
/// Describes the severity level of a [`Diagnostic`].
|
||||||
#[derive(Debug, Eq, PartialEq)]
|
#[derive(Debug, Eq, PartialEq)]
|
||||||
pub enum Severity {
|
pub enum Severity {
|
||||||
Error,
|
Error,
|
||||||
@ -6,12 +9,14 @@ pub enum Severity {
|
|||||||
Hint,
|
Hint,
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/// A range of `char`s within the text.
|
||||||
#[derive(Debug)]
|
#[derive(Debug)]
|
||||||
pub struct Range {
|
pub struct Range {
|
||||||
pub start: usize,
|
pub start: usize,
|
||||||
pub end: usize,
|
pub end: usize,
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/// Corresponds to [`lsp_types::Diagnostic`](https://docs.rs/lsp-types/0.91.0/lsp_types/struct.Diagnostic.html)
|
||||||
#[derive(Debug)]
|
#[derive(Debug)]
|
||||||
pub struct Diagnostic {
|
pub struct Diagnostic {
|
||||||
pub range: Range,
|
pub range: Range,
|
||||||
|
@ -1,4 +1,6 @@
|
|||||||
// Based on https://github.com/cessen/led/blob/c4fa72405f510b7fd16052f90a598c429b3104a6/src/graphemes.rs
|
//! Utility functions to traverse the unicode graphemes of a `Rope`'s text contents.
|
||||||
|
//!
|
||||||
|
//! Based on https://github.com/cessen/led/blob/c4fa72405f510b7fd16052f90a598c429b3104a6/src/graphemes.rs
|
||||||
use ropey::{iter::Chunks, str_utils::byte_to_char_idx, RopeSlice};
|
use ropey::{iter::Chunks, str_utils::byte_to_char_idx, RopeSlice};
|
||||||
use unicode_segmentation::{GraphemeCursor, GraphemeIncomplete};
|
use unicode_segmentation::{GraphemeCursor, GraphemeIncomplete};
|
||||||
use unicode_width::UnicodeWidthStr;
|
use unicode_width::UnicodeWidthStr;
|
||||||
|
@ -4,48 +4,50 @@
|
|||||||
use std::num::NonZeroUsize;
|
use std::num::NonZeroUsize;
|
||||||
use std::time::{Duration, Instant};
|
use std::time::{Duration, Instant};
|
||||||
|
|
||||||
// Stores the history of changes to a buffer.
|
/// Stores the history of changes to a buffer.
|
||||||
//
|
///
|
||||||
// Currently the history is represented as a vector of revisions. The vector
|
/// Currently the history is represented as a vector of revisions. The vector
|
||||||
// always has at least one element: the empty root revision. Each revision
|
/// always has at least one element: the empty root revision. Each revision
|
||||||
// with the exception of the root has a parent revision, a [Transaction]
|
/// with the exception of the root has a parent revision, a [Transaction]
|
||||||
// that can be applied to its parent to transition from the parent to itself,
|
/// that can be applied to its parent to transition from the parent to itself,
|
||||||
// and an inversion of that transaction to transition from the parent to its
|
/// and an inversion of that transaction to transition from the parent to its
|
||||||
// latest child.
|
/// latest child.
|
||||||
//
|
///
|
||||||
// When using `u` to undo a change, an inverse of the stored transaction will
|
/// When using `u` to undo a change, an inverse of the stored transaction will
|
||||||
// be applied which will transition the buffer to the parent state.
|
/// be applied which will transition the buffer to the parent state.
|
||||||
//
|
///
|
||||||
// Each revision with the exception of the last in the vector also has a
|
/// Each revision with the exception of the last in the vector also has a
|
||||||
// last child revision. When using `U` to redo a change, the last child transaction
|
/// last child revision. When using `U` to redo a change, the last child transaction
|
||||||
// will be applied to the current state of the buffer.
|
/// will be applied to the current state of the buffer.
|
||||||
//
|
///
|
||||||
// The current revision is the one currently displayed in the buffer.
|
/// The current revision is the one currently displayed in the buffer.
|
||||||
//
|
///
|
||||||
// Commiting a new revision to the history will update the last child of the
|
/// Commiting a new revision to the history will update the last child of the
|
||||||
// current revision, and push a new revision to the end of the vector.
|
/// current revision, and push a new revision to the end of the vector.
|
||||||
//
|
///
|
||||||
// Revisions are commited with a timestamp. :earlier and :later can be used
|
/// Revisions are commited with a timestamp. :earlier and :later can be used
|
||||||
// to jump to the closest revision to a moment in time relative to the timestamp
|
/// to jump to the closest revision to a moment in time relative to the timestamp
|
||||||
// of the current revision plus (:later) or minus (:earlier) the duration
|
/// of the current revision plus (:later) or minus (:earlier) the duration
|
||||||
// given to the command. If a single integer is given, the editor will instead
|
/// given to the command. If a single integer is given, the editor will instead
|
||||||
// jump the given number of revisions in the vector.
|
/// jump the given number of revisions in the vector.
|
||||||
//
|
///
|
||||||
// Limitations:
|
/// Limitations:
|
||||||
// * Changes in selections currently don't commit history changes. The selection
|
/// * Changes in selections currently don't commit history changes. The selection
|
||||||
// will only be updated to the state after a commited buffer change.
|
/// will only be updated to the state after a commited buffer change.
|
||||||
// * The vector of history revisions is currently unbounded. This might
|
/// * The vector of history revisions is currently unbounded. This might
|
||||||
// cause the memory consumption to grow significantly large during long
|
/// cause the memory consumption to grow significantly large during long
|
||||||
// editing sessions.
|
/// editing sessions.
|
||||||
// * Because delete transactions currently don't store the text that they
|
/// * Because delete transactions currently don't store the text that they
|
||||||
// delete, we also store an inversion of the transaction.
|
/// delete, we also store an inversion of the transaction.
|
||||||
|
///
|
||||||
|
/// Using time to navigate the history: https://github.com/helix-editor/helix/pull/194
|
||||||
#[derive(Debug)]
|
#[derive(Debug)]
|
||||||
pub struct History {
|
pub struct History {
|
||||||
revisions: Vec<Revision>,
|
revisions: Vec<Revision>,
|
||||||
current: usize,
|
current: usize,
|
||||||
}
|
}
|
||||||
|
|
||||||
// A single point in history. See [History] for more information.
|
/// A single point in history. See [History] for more information.
|
||||||
#[derive(Debug)]
|
#[derive(Debug)]
|
||||||
struct Revision {
|
struct Revision {
|
||||||
parent: usize,
|
parent: usize,
|
||||||
@ -111,6 +113,7 @@ pub const fn at_root(&self) -> bool {
|
|||||||
self.current == 0
|
self.current == 0
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/// Undo the last edit.
|
||||||
pub fn undo(&mut self) -> Option<&Transaction> {
|
pub fn undo(&mut self) -> Option<&Transaction> {
|
||||||
if self.at_root() {
|
if self.at_root() {
|
||||||
return None;
|
return None;
|
||||||
@ -121,6 +124,7 @@ pub fn undo(&mut self) -> Option<&Transaction> {
|
|||||||
Some(¤t_revision.inversion)
|
Some(¤t_revision.inversion)
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/// Redo the last edit.
|
||||||
pub fn redo(&mut self) -> Option<&Transaction> {
|
pub fn redo(&mut self) -> Option<&Transaction> {
|
||||||
let current_revision = &self.revisions[self.current];
|
let current_revision = &self.revisions[self.current];
|
||||||
let last_child = current_revision.last_child?;
|
let last_child = current_revision.last_child?;
|
||||||
@ -147,8 +151,8 @@ fn lowest_common_ancestor(&self, mut a: usize, mut b: usize) -> usize {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
// List of nodes on the way from `n` to 'a`. Doesn`t include `a`.
|
/// List of nodes on the way from `n` to 'a`. Doesn`t include `a`.
|
||||||
// Includes `n` unless `a == n`. `a` must be an ancestor of `n`.
|
/// Includes `n` unless `a == n`. `a` must be an ancestor of `n`.
|
||||||
fn path_up(&self, mut n: usize, a: usize) -> Vec<usize> {
|
fn path_up(&self, mut n: usize, a: usize) -> Vec<usize> {
|
||||||
let mut path = Vec::new();
|
let mut path = Vec::new();
|
||||||
while n != a {
|
while n != a {
|
||||||
@ -158,6 +162,7 @@ fn path_up(&self, mut n: usize, a: usize) -> Vec<usize> {
|
|||||||
path
|
path
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/// Create a [`Transaction`] that will jump to a specific revision in the history.
|
||||||
fn jump_to(&mut self, to: usize) -> Vec<Transaction> {
|
fn jump_to(&mut self, to: usize) -> Vec<Transaction> {
|
||||||
let lca = self.lowest_common_ancestor(self.current, to);
|
let lca = self.lowest_common_ancestor(self.current, to);
|
||||||
let up = self.path_up(self.current, lca);
|
let up = self.path_up(self.current, lca);
|
||||||
@ -171,10 +176,12 @@ fn jump_to(&mut self, to: usize) -> Vec<Transaction> {
|
|||||||
up_txns.chain(down_txns).collect()
|
up_txns.chain(down_txns).collect()
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/// Creates a [`Transaction`] that will undo `delta` revisions.
|
||||||
fn jump_backward(&mut self, delta: usize) -> Vec<Transaction> {
|
fn jump_backward(&mut self, delta: usize) -> Vec<Transaction> {
|
||||||
self.jump_to(self.current.saturating_sub(delta))
|
self.jump_to(self.current.saturating_sub(delta))
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/// Creates a [`Transaction`] that will redo `delta` revisions.
|
||||||
fn jump_forward(&mut self, delta: usize) -> Vec<Transaction> {
|
fn jump_forward(&mut self, delta: usize) -> Vec<Transaction> {
|
||||||
self.jump_to(
|
self.jump_to(
|
||||||
self.current
|
self.current
|
||||||
@ -183,7 +190,7 @@ fn jump_forward(&mut self, delta: usize) -> Vec<Transaction> {
|
|||||||
)
|
)
|
||||||
}
|
}
|
||||||
|
|
||||||
// Helper for a binary search case below.
|
/// Helper for a binary search case below.
|
||||||
fn revision_closer_to_instant(&self, i: usize, instant: Instant) -> usize {
|
fn revision_closer_to_instant(&self, i: usize, instant: Instant) -> usize {
|
||||||
let dur_im1 = instant.duration_since(self.revisions[i - 1].timestamp);
|
let dur_im1 = instant.duration_since(self.revisions[i - 1].timestamp);
|
||||||
let dur_i = self.revisions[i].timestamp.duration_since(instant);
|
let dur_i = self.revisions[i].timestamp.duration_since(instant);
|
||||||
@ -194,6 +201,8 @@ fn revision_closer_to_instant(&self, i: usize, instant: Instant) -> usize {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/// Creates a [`Transaction`] that will match a revision created at around
|
||||||
|
/// `instant`.
|
||||||
fn jump_instant(&mut self, instant: Instant) -> Vec<Transaction> {
|
fn jump_instant(&mut self, instant: Instant) -> Vec<Transaction> {
|
||||||
let search_result = self
|
let search_result = self
|
||||||
.revisions
|
.revisions
|
||||||
@ -209,6 +218,8 @@ fn jump_instant(&mut self, instant: Instant) -> Vec<Transaction> {
|
|||||||
self.jump_to(revision)
|
self.jump_to(revision)
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/// Creates a [`Transaction`] that will match a revision created `duration` ago
|
||||||
|
/// from the timestamp of current revision.
|
||||||
fn jump_duration_backward(&mut self, duration: Duration) -> Vec<Transaction> {
|
fn jump_duration_backward(&mut self, duration: Duration) -> Vec<Transaction> {
|
||||||
match self.revisions[self.current].timestamp.checked_sub(duration) {
|
match self.revisions[self.current].timestamp.checked_sub(duration) {
|
||||||
Some(instant) => self.jump_instant(instant),
|
Some(instant) => self.jump_instant(instant),
|
||||||
@ -216,6 +227,8 @@ fn jump_duration_backward(&mut self, duration: Duration) -> Vec<Transaction> {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/// Creates a [`Transaction`] that will match a revision created `duration` in
|
||||||
|
/// the future from the timestamp of the current revision.
|
||||||
fn jump_duration_forward(&mut self, duration: Duration) -> Vec<Transaction> {
|
fn jump_duration_forward(&mut self, duration: Duration) -> Vec<Transaction> {
|
||||||
match self.revisions[self.current].timestamp.checked_add(duration) {
|
match self.revisions[self.current].timestamp.checked_add(duration) {
|
||||||
Some(instant) => self.jump_instant(instant),
|
Some(instant) => self.jump_instant(instant),
|
||||||
@ -223,6 +236,7 @@ fn jump_duration_forward(&mut self, duration: Duration) -> Vec<Transaction> {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/// Creates an undo [`Transaction`].
|
||||||
pub fn earlier(&mut self, uk: UndoKind) -> Vec<Transaction> {
|
pub fn earlier(&mut self, uk: UndoKind) -> Vec<Transaction> {
|
||||||
use UndoKind::*;
|
use UndoKind::*;
|
||||||
match uk {
|
match uk {
|
||||||
@ -231,6 +245,7 @@ pub fn earlier(&mut self, uk: UndoKind) -> Vec<Transaction> {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/// Creates a redo [`Transaction`].
|
||||||
pub fn later(&mut self, uk: UndoKind) -> Vec<Transaction> {
|
pub fn later(&mut self, uk: UndoKind) -> Vec<Transaction> {
|
||||||
use UndoKind::*;
|
use UndoKind::*;
|
||||||
match uk {
|
match uk {
|
||||||
@ -240,13 +255,14 @@ pub fn later(&mut self, uk: UndoKind) -> Vec<Transaction> {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/// Whether to undo by a number of edits or a duration of time.
|
||||||
#[derive(Debug, PartialEq)]
|
#[derive(Debug, PartialEq)]
|
||||||
pub enum UndoKind {
|
pub enum UndoKind {
|
||||||
Steps(usize),
|
Steps(usize),
|
||||||
TimePeriod(std::time::Duration),
|
TimePeriod(std::time::Duration),
|
||||||
}
|
}
|
||||||
|
|
||||||
// A subset of sytemd.time time span syntax units.
|
/// A subset of sytemd.time time span syntax units.
|
||||||
const TIME_UNITS: &[(&[&str], &str, u64)] = &[
|
const TIME_UNITS: &[(&[&str], &str, u64)] = &[
|
||||||
(&["seconds", "second", "sec", "s"], "seconds", 1),
|
(&["seconds", "second", "sec", "s"], "seconds", 1),
|
||||||
(&["minutes", "minute", "min", "m"], "minutes", 60),
|
(&["minutes", "minute", "min", "m"], "minutes", 60),
|
||||||
@ -254,11 +270,20 @@ pub enum UndoKind {
|
|||||||
(&["days", "day", "d"], "days", 24 * 60 * 60),
|
(&["days", "day", "d"], "days", 24 * 60 * 60),
|
||||||
];
|
];
|
||||||
|
|
||||||
|
/// Checks if the duration input can be turned into a valid duration. It must be a
|
||||||
|
/// positive integer and denote the [unit of time.](`TIME_UNITS`)
|
||||||
|
/// Examples of valid durations:
|
||||||
|
/// * `5 sec`
|
||||||
|
/// * `5 min`
|
||||||
|
/// * `5 hr`
|
||||||
|
/// * `5 days`
|
||||||
static DURATION_VALIDATION_REGEX: Lazy<Regex> =
|
static DURATION_VALIDATION_REGEX: Lazy<Regex> =
|
||||||
Lazy::new(|| Regex::new(r"^(?:\d+\s*[a-z]+\s*)+$").unwrap());
|
Lazy::new(|| Regex::new(r"^(?:\d+\s*[a-z]+\s*)+$").unwrap());
|
||||||
|
|
||||||
|
/// Captures both the number and unit as separate capture groups.
|
||||||
static NUMBER_UNIT_REGEX: Lazy<Regex> = Lazy::new(|| Regex::new(r"(\d+)\s*([a-z]+)").unwrap());
|
static NUMBER_UNIT_REGEX: Lazy<Regex> = Lazy::new(|| Regex::new(r"(\d+)\s*([a-z]+)").unwrap());
|
||||||
|
|
||||||
|
/// Parse a string (e.g. "5 sec") and try to convert it into a [`Duration`].
|
||||||
fn parse_human_duration(s: &str) -> Result<Duration, String> {
|
fn parse_human_duration(s: &str) -> Result<Duration, String> {
|
||||||
if !DURATION_VALIDATION_REGEX.is_match(s) {
|
if !DURATION_VALIDATION_REGEX.is_match(s) {
|
||||||
return Err("duration should be composed \
|
return Err("duration should be composed \
|
||||||
|
Loading…
Reference in New Issue
Block a user