mirror of https://github.com/helix-editor/helix
Merge f252e179a2
into 1315b7e2b1
commit
43853e96a6
|
@ -1,3 +1,4 @@
|
|||
//! Functions for working with the host environment.
|
||||
use std::{
|
||||
borrow::Cow,
|
||||
ffi::{OsStr, OsString},
|
||||
|
@ -10,9 +11,9 @@ use once_cell::sync::Lazy;
|
|||
// We keep the CWD as a static so that we can access it in places where we don't have access to the Editor
|
||||
static CWD: RwLock<Option<PathBuf>> = RwLock::new(None);
|
||||
|
||||
// Get the current working directory.
|
||||
// This information is managed internally as the call to std::env::current_dir
|
||||
// might fail if the cwd has been deleted.
|
||||
/// Get the current working directory.
|
||||
/// This information is managed internally as the call to std::env::current_dir
|
||||
/// might fail if the cwd has been deleted.
|
||||
pub fn current_working_dir() -> PathBuf {
|
||||
if let Some(path) = &*CWD.read().unwrap() {
|
||||
return path.clone();
|
||||
|
@ -37,6 +38,7 @@ pub fn current_working_dir() -> PathBuf {
|
|||
cwd
|
||||
}
|
||||
|
||||
/// Update the current working directory.
|
||||
pub fn set_current_working_dir(path: impl AsRef<Path>) -> std::io::Result<Option<PathBuf>> {
|
||||
let path = crate::path::canonicalize(path);
|
||||
std::env::set_current_dir(&path)?;
|
||||
|
@ -45,14 +47,17 @@ pub fn set_current_working_dir(path: impl AsRef<Path>) -> std::io::Result<Option
|
|||
Ok(cwd.replace(path))
|
||||
}
|
||||
|
||||
/// Checks if the given environment variable is set.
|
||||
pub fn env_var_is_set(env_var_name: &str) -> bool {
|
||||
std::env::var_os(env_var_name).is_some()
|
||||
}
|
||||
|
||||
/// Checks if a binary with the given name exists.
|
||||
pub fn binary_exists<T: AsRef<OsStr>>(binary_name: T) -> bool {
|
||||
which::which(binary_name).is_ok()
|
||||
}
|
||||
|
||||
/// Attempts to find a binary of the given name. See [which](https://linux.die.net/man/1/which).
|
||||
pub fn which<T: AsRef<OsStr>>(
|
||||
binary_name: T,
|
||||
) -> Result<std::path::PathBuf, ExecutableNotFoundError> {
|
||||
|
|
|
@ -1,3 +1,4 @@
|
|||
//! Functions for managine file metadata.
|
||||
//! From <https://github.com/Freaky/faccess>
|
||||
|
||||
use std::io;
|
||||
|
|
|
@ -1,3 +1,6 @@
|
|||
//! Extensions to the standard library. A collection of helper functions
|
||||
//! used throughout helix.
|
||||
|
||||
pub mod env;
|
||||
pub mod faccess;
|
||||
pub mod path;
|
||||
|
|
|
@ -1,3 +1,5 @@
|
|||
//! Functions for working with [Path].
|
||||
|
||||
pub use etcetera::home_dir;
|
||||
use once_cell::sync::Lazy;
|
||||
use regex_cursor::{engines::meta::Regex, Input};
|
||||
|
@ -140,6 +142,7 @@ pub fn canonicalize(path: impl AsRef<Path>) -> PathBuf {
|
|||
normalize(path)
|
||||
}
|
||||
|
||||
/// Convert path into a relative path
|
||||
pub fn get_relative_path<'a, P>(path: P) -> Cow<'a, Path>
|
||||
where
|
||||
P: Into<Cow<'a, Path>>,
|
||||
|
|
|
@ -1,3 +1,5 @@
|
|||
//! Provides [Range] type expanding on [RangeBounds].
|
||||
|
||||
use std::ops::{self, RangeBounds};
|
||||
|
||||
/// A range of `char`s within the text.
|
||||
|
@ -66,6 +68,7 @@ pub fn is_subset<const ALLOW_EMPTY: bool>(
|
|||
}
|
||||
}
|
||||
|
||||
/// Similar to is_subset but requires each element of `super_set` to be matched
|
||||
pub fn is_exact_subset(
|
||||
mut super_set: impl Iterator<Item = Range>,
|
||||
mut sub_set: impl Iterator<Item = Range>,
|
||||
|
|
|
@ -1,3 +1,4 @@
|
|||
//! Functions and types for working with [RopeSlice]
|
||||
use std::fmt;
|
||||
use std::ops::{Bound, RangeBounds};
|
||||
|
||||
|
@ -8,6 +9,7 @@ use ropey::iter::Chunks;
|
|||
use ropey::RopeSlice;
|
||||
use unicode_segmentation::{GraphemeCursor, GraphemeIncomplete};
|
||||
|
||||
/// Additional utility functions for [RopeSlice]
|
||||
pub trait RopeSliceExt<'a>: Sized {
|
||||
fn ends_with(self, text: &str) -> bool;
|
||||
fn starts_with(self, text: &str) -> bool;
|
||||
|
|
|
@ -95,6 +95,7 @@ impl Capabilities {
|
|||
}
|
||||
}
|
||||
|
||||
/// Terminal backend supporting a wide variety of terminals
|
||||
pub struct CrosstermBackend<W: Write> {
|
||||
buffer: W,
|
||||
capabilities: Capabilities,
|
||||
|
|
|
@ -1,3 +1,5 @@
|
|||
//! Provides interface for controlling the terminal
|
||||
|
||||
use std::io;
|
||||
|
||||
use crate::{buffer::Cell, terminal::Config};
|
||||
|
@ -12,19 +14,32 @@ pub use self::crossterm::CrosstermBackend;
|
|||
mod test;
|
||||
pub use self::test::TestBackend;
|
||||
|
||||
/// Representation of a terminal backend.
|
||||
pub trait Backend {
|
||||
/// Claims the terminal for TUI use.
|
||||
fn claim(&mut self, config: Config) -> Result<(), io::Error>;
|
||||
/// Update terminal configuration.
|
||||
fn reconfigure(&mut self, config: Config) -> Result<(), io::Error>;
|
||||
/// Restores the terminal to a normal state, undoes `claim`
|
||||
fn restore(&mut self, config: Config) -> Result<(), io::Error>;
|
||||
/// Forcibly resets the terminal, ignoring errors and configuration
|
||||
fn force_restore() -> Result<(), io::Error>;
|
||||
/// Draws styled text to the terminal
|
||||
fn draw<'a, I>(&mut self, content: I) -> Result<(), io::Error>
|
||||
where
|
||||
I: Iterator<Item = (u16, u16, &'a Cell)>;
|
||||
/// Hides the cursor
|
||||
fn hide_cursor(&mut self) -> Result<(), io::Error>;
|
||||
/// Sets the cursor to the given shape
|
||||
fn show_cursor(&mut self, kind: CursorKind) -> Result<(), io::Error>;
|
||||
/// Gets the current position of the cursor
|
||||
fn get_cursor(&mut self) -> Result<(u16, u16), io::Error>;
|
||||
/// Sets the cursor to the given position
|
||||
fn set_cursor(&mut self, x: u16, y: u16) -> Result<(), io::Error>;
|
||||
/// Clears the terminal
|
||||
fn clear(&mut self) -> Result<(), io::Error>;
|
||||
/// Gets the size of the terminal in cells
|
||||
fn size(&self) -> Result<Rect, io::Error>;
|
||||
/// Flushes the terminal buffer
|
||||
fn flush(&mut self) -> Result<(), io::Error>;
|
||||
}
|
||||
|
|
|
@ -1,3 +1,4 @@
|
|||
//! Contents of a terminal screen. A [Buffer] is made up of [Cell]s.
|
||||
use crate::text::{Span, Spans};
|
||||
use helix_core::unicode::width::UnicodeWidthStr;
|
||||
use std::cmp::min;
|
||||
|
@ -5,7 +6,7 @@ use unicode_segmentation::UnicodeSegmentation;
|
|||
|
||||
use helix_view::graphics::{Color, Modifier, Rect, Style, UnderlineStyle};
|
||||
|
||||
/// A buffer cell
|
||||
/// One cell of the terminal. Contains one stylized grapheme.
|
||||
#[derive(Debug, Clone, PartialEq, Eq)]
|
||||
pub struct Cell {
|
||||
pub symbol: String,
|
||||
|
@ -17,28 +18,33 @@ pub struct Cell {
|
|||
}
|
||||
|
||||
impl Cell {
|
||||
/// Set the cell's grapheme
|
||||
pub fn set_symbol(&mut self, symbol: &str) -> &mut Cell {
|
||||
self.symbol.clear();
|
||||
self.symbol.push_str(symbol);
|
||||
self
|
||||
}
|
||||
|
||||
/// Set the cell's grapheme to a [char]
|
||||
pub fn set_char(&mut self, ch: char) -> &mut Cell {
|
||||
self.symbol.clear();
|
||||
self.symbol.push(ch);
|
||||
self
|
||||
}
|
||||
|
||||
/// Set the foreground [Color]
|
||||
pub fn set_fg(&mut self, color: Color) -> &mut Cell {
|
||||
self.fg = color;
|
||||
self
|
||||
}
|
||||
|
||||
/// Set the background [Color]
|
||||
pub fn set_bg(&mut self, color: Color) -> &mut Cell {
|
||||
self.bg = color;
|
||||
self
|
||||
}
|
||||
|
||||
/// Set the [Style] of the cell
|
||||
pub fn set_style(&mut self, style: Style) -> &mut Cell {
|
||||
if let Some(c) = style.fg {
|
||||
self.fg = c;
|
||||
|
@ -58,6 +64,7 @@ impl Cell {
|
|||
self
|
||||
}
|
||||
|
||||
/// Returns the current style of the cell
|
||||
pub fn style(&self) -> Style {
|
||||
Style::default()
|
||||
.fg(self.fg)
|
||||
|
@ -67,6 +74,7 @@ impl Cell {
|
|||
.add_modifier(self.modifier)
|
||||
}
|
||||
|
||||
/// Resets the cell to a default blank state
|
||||
pub fn reset(&mut self) {
|
||||
self.symbol.clear();
|
||||
self.symbol.push(' ');
|
||||
|
@ -494,6 +502,8 @@ impl Buffer {
|
|||
(x_offset as u16, y)
|
||||
}
|
||||
|
||||
/// Print at most the first `width` characters of a [Spans] if enough space is available
|
||||
/// until the end of the line. Appends a `…` at the end of truncated lines.
|
||||
pub fn set_spans_truncated(&mut self, x: u16, y: u16, spans: &Spans, width: u16) -> (u16, u16) {
|
||||
// prevent panic if out of range
|
||||
if !self.in_bounds(x, y) || width == 0 {
|
||||
|
@ -535,6 +545,8 @@ impl Buffer {
|
|||
(x_offset as u16, y)
|
||||
}
|
||||
|
||||
/// Print at most the first `width` characters of a [Spans] if enough space is available
|
||||
/// until the end of the line
|
||||
pub fn set_spans(&mut self, x: u16, y: u16, spans: &Spans, width: u16) -> (u16, u16) {
|
||||
let mut remaining_width = width;
|
||||
let mut x = x;
|
||||
|
@ -556,6 +568,8 @@ impl Buffer {
|
|||
(x, y)
|
||||
}
|
||||
|
||||
/// Print at most the first `width` characters of a [Span] if enough space is available
|
||||
/// until the end of the line
|
||||
pub fn set_span(&mut self, x: u16, y: u16, span: &Span, width: u16) -> (u16, u16) {
|
||||
self.set_stringn(x, y, span.content.as_ref(), width as usize, span.style)
|
||||
}
|
||||
|
@ -572,6 +586,7 @@ impl Buffer {
|
|||
}
|
||||
}
|
||||
|
||||
/// Set all cells in the [area](Rect) to the given [Style]
|
||||
pub fn set_style(&mut self, area: Rect, style: Style) {
|
||||
for y in area.top()..area.bottom() {
|
||||
for x in area.left()..area.right() {
|
||||
|
|
|
@ -1,3 +1,5 @@
|
|||
//! Layout engine for terminal
|
||||
|
||||
use std::cell::RefCell;
|
||||
use std::collections::HashMap;
|
||||
|
||||
|
@ -7,6 +9,7 @@ use cassowary::{Constraint as CassowaryConstraint, Expression, Solver, Variable}
|
|||
|
||||
use helix_view::graphics::{Margin, Rect};
|
||||
|
||||
/// Enum of all corners
|
||||
#[derive(Debug, Hash, Clone, Copy, PartialEq, Eq)]
|
||||
pub enum Corner {
|
||||
TopLeft,
|
||||
|
@ -15,12 +18,14 @@ pub enum Corner {
|
|||
BottomLeft,
|
||||
}
|
||||
|
||||
/// Direction a [Rect] should be split
|
||||
#[derive(Debug, Hash, Clone, PartialEq, Eq)]
|
||||
pub enum Direction {
|
||||
Horizontal,
|
||||
Vertical,
|
||||
}
|
||||
|
||||
/// Describes requirements of a [Rect] to be split
|
||||
#[derive(Debug, Clone, Copy, PartialEq, Eq, Hash)]
|
||||
pub enum Constraint {
|
||||
// TODO: enforce range 0 - 100
|
||||
|
@ -46,6 +51,7 @@ impl Constraint {
|
|||
}
|
||||
}
|
||||
|
||||
/// How content should be aligned
|
||||
#[derive(Debug, Clone, Copy, PartialEq, Eq)]
|
||||
pub enum Alignment {
|
||||
Left,
|
||||
|
@ -53,6 +59,7 @@ pub enum Alignment {
|
|||
Right,
|
||||
}
|
||||
|
||||
/// Description of a how a [Rect] should be split
|
||||
#[derive(Debug, Clone, PartialEq, Eq, Hash)]
|
||||
pub struct Layout {
|
||||
direction: Direction,
|
||||
|
@ -75,6 +82,7 @@ impl Default for Layout {
|
|||
}
|
||||
|
||||
impl Layout {
|
||||
/// Returns a layout with the given [Constraint]s.
|
||||
pub fn constraints<C>(mut self, constraints: C) -> Layout
|
||||
where
|
||||
C: Into<Vec<Constraint>>,
|
||||
|
@ -83,21 +91,25 @@ impl Layout {
|
|||
self
|
||||
}
|
||||
|
||||
/// Returns a layout wit the given margins on all sides.
|
||||
pub const fn margin(mut self, margin: u16) -> Layout {
|
||||
self.margin = Margin::all(margin);
|
||||
self
|
||||
}
|
||||
|
||||
/// Returns a layout with the given horizontal margins.
|
||||
pub const fn horizontal_margin(mut self, horizontal: u16) -> Layout {
|
||||
self.margin.horizontal = horizontal;
|
||||
self
|
||||
}
|
||||
|
||||
/// Returns a layout with the given vertical margins.
|
||||
pub const fn vertical_margin(mut self, vertical: u16) -> Layout {
|
||||
self.margin.vertical = vertical;
|
||||
self
|
||||
}
|
||||
|
||||
/// Returns a layout with the given [Direction].
|
||||
pub const fn direction(mut self, direction: Direction) -> Layout {
|
||||
self.direction = direction;
|
||||
self
|
||||
|
|
|
@ -1,3 +1,5 @@
|
|||
//! Common TUI symbols including blocks, bars, braille, lines
|
||||
|
||||
pub mod block {
|
||||
pub const FULL: &str = "█";
|
||||
pub const SEVEN_EIGHTHS: &str = "▉";
|
||||
|
|
|
@ -1,3 +1,6 @@
|
|||
//! Terminal interface provided through the [Terminal] type.
|
||||
//! Frontend for [Backend]
|
||||
|
||||
use crate::{backend::Backend, buffer::Buffer};
|
||||
use helix_view::editor::Config as EditorConfig;
|
||||
use helix_view::graphics::{CursorKind, Rect};
|
||||
|
@ -17,6 +20,7 @@ pub struct Viewport {
|
|||
resize_behavior: ResizeBehavior,
|
||||
}
|
||||
|
||||
/// Terminal configuration
|
||||
#[derive(Debug)]
|
||||
pub struct Config {
|
||||
pub enable_mouse_capture: bool,
|
||||
|
@ -47,7 +51,7 @@ pub struct TerminalOptions {
|
|||
pub viewport: Viewport,
|
||||
}
|
||||
|
||||
/// Interface to the terminal backed by Termion
|
||||
/// Interface to the terminal backed by crossterm
|
||||
#[derive(Debug)]
|
||||
pub struct Terminal<B>
|
||||
where
|
||||
|
|
|
@ -331,6 +331,7 @@ impl<'a> Table<'a> {
|
|||
}
|
||||
}
|
||||
|
||||
/// Track [Table] scroll offset and selection
|
||||
#[derive(Debug, Default, Clone)]
|
||||
pub struct TableState {
|
||||
pub offset: usize,
|
||||
|
|
|
@ -9,8 +9,6 @@ categories.workspace = true
|
|||
repository.workspace = true
|
||||
homepage.workspace = true
|
||||
|
||||
# See more keys and their definitions at https://doc.rust-lang.org/cargo/reference/manifest.html
|
||||
|
||||
[dependencies]
|
||||
helix-core = { path = "../helix-core" }
|
||||
helix-event = { path = "../helix-event" }
|
||||
|
|
|
@ -34,6 +34,7 @@ struct DiffInner {
|
|||
hunks: Vec<Hunk>,
|
||||
}
|
||||
|
||||
/// Representation of a diff that can be updated.
|
||||
#[derive(Clone, Debug)]
|
||||
pub struct DiffHandle {
|
||||
channel: UnboundedSender<Event>,
|
||||
|
@ -64,10 +65,12 @@ impl DiffHandle {
|
|||
(differ, handle)
|
||||
}
|
||||
|
||||
/// Switch base and modified texts' roles
|
||||
pub fn invert(&mut self) {
|
||||
self.inverted = !self.inverted;
|
||||
}
|
||||
|
||||
/// Load the actual diff
|
||||
pub fn load(&self) -> Diff {
|
||||
Diff {
|
||||
diff: self.diff.read(),
|
||||
|
@ -88,6 +91,7 @@ impl DiffHandle {
|
|||
self.update_document_impl(doc, self.inverted, Some(RenderLock { lock, timeout }))
|
||||
}
|
||||
|
||||
/// Updates the base text of the diff. Returns if the update was successful.
|
||||
pub fn update_diff_base(&self, diff_base: Rope) -> bool {
|
||||
self.update_document_impl(diff_base, !self.inverted, None)
|
||||
}
|
||||
|
@ -143,7 +147,7 @@ impl Hunk {
|
|||
after: u32::MAX..u32::MAX,
|
||||
};
|
||||
|
||||
/// Inverts a change so that `before`
|
||||
/// Inverts a change so that `before` and `after` are swapped.
|
||||
pub fn invert(&self) -> Hunk {
|
||||
Hunk {
|
||||
before: self.after.clone(),
|
||||
|
@ -151,10 +155,12 @@ impl Hunk {
|
|||
}
|
||||
}
|
||||
|
||||
/// Is this hunk only adding to the document
|
||||
pub fn is_pure_insertion(&self) -> bool {
|
||||
self.before.is_empty()
|
||||
}
|
||||
|
||||
/// Is this hunk only removing from the document
|
||||
pub fn is_pure_removal(&self) -> bool {
|
||||
self.after.is_empty()
|
||||
}
|
||||
|
|
|
@ -1,3 +1,7 @@
|
|||
//! `helix_vcs` provides types for working with diffs from a Version Control System (VCS).
|
||||
//! Currently `git` is the only supported provider for diffs, but this architecture allows
|
||||
//! for other providers to be added in the future.
|
||||
|
||||
use anyhow::{anyhow, bail, Result};
|
||||
use arc_swap::ArcSwap;
|
||||
use std::{
|
||||
|
@ -16,12 +20,16 @@ mod status;
|
|||
|
||||
pub use status::FileChange;
|
||||
|
||||
/// Contains all active diff providers. Diff providers are compiled in via features. Currently
|
||||
/// only `git` is supported.
|
||||
#[derive(Clone)]
|
||||
pub struct DiffProviderRegistry {
|
||||
providers: Vec<DiffProvider>,
|
||||
}
|
||||
|
||||
impl DiffProviderRegistry {
|
||||
/// Get the given file from the VCS. This provides the unedited document as a "base"
|
||||
/// for a diff to be created.
|
||||
pub fn get_diff_base(&self, file: &Path) -> Option<Vec<u8>> {
|
||||
self.providers
|
||||
.iter()
|
||||
|
@ -35,6 +43,7 @@ impl DiffProviderRegistry {
|
|||
})
|
||||
}
|
||||
|
||||
/// Get the current name of the current [HEAD](https://stackoverflow.com/questions/2304087/what-is-head-in-git).
|
||||
pub fn get_current_head_name(&self, file: &Path) -> Option<Arc<ArcSwap<Box<str>>>> {
|
||||
self.providers
|
||||
.iter()
|
||||
|
@ -75,6 +84,7 @@ impl Default for DiffProviderRegistry {
|
|||
let providers = vec![
|
||||
#[cfg(feature = "git")]
|
||||
DiffProvider::Git,
|
||||
DiffProvider::None,
|
||||
];
|
||||
DiffProviderRegistry { providers }
|
||||
}
|
||||
|
@ -85,7 +95,7 @@ impl Default for DiffProviderRegistry {
|
|||
///
|
||||
/// `Copy` is simply to ensure the `clone()` call is the simplest it can be.
|
||||
#[derive(Copy, Clone)]
|
||||
pub enum DiffProvider {
|
||||
enum DiffProvider {
|
||||
#[cfg(feature = "git")]
|
||||
Git,
|
||||
None,
|
||||
|
|
|
@ -1,18 +1,16 @@
|
|||
use std::path::{Path, PathBuf};
|
||||
|
||||
/// States for a file having been changed.
|
||||
pub enum FileChange {
|
||||
Untracked {
|
||||
path: PathBuf,
|
||||
},
|
||||
Modified {
|
||||
path: PathBuf,
|
||||
},
|
||||
Conflict {
|
||||
path: PathBuf,
|
||||
},
|
||||
Deleted {
|
||||
path: PathBuf,
|
||||
},
|
||||
/// Not tracked by the VCS.
|
||||
Untracked { path: PathBuf },
|
||||
/// File has been modified.
|
||||
Modified { path: PathBuf },
|
||||
/// File modification is in conflict with a different update.
|
||||
Conflict { path: PathBuf },
|
||||
/// File has been deleted.
|
||||
Deleted { path: PathBuf },
|
||||
/// File has been renamed.
|
||||
Renamed {
|
||||
from_path: PathBuf,
|
||||
to_path: PathBuf,
|
||||
|
|
Loading…
Reference in New Issue