mirror of
https://github.com/helix-editor/helix.git
synced 2024-11-22 01:16:18 +04:00
Add a version-control statusline element (#5682)
This commit is contained in:
parent
98415f288f
commit
1661e4b5e1
1
Cargo.lock
generated
1
Cargo.lock
generated
@ -1212,6 +1212,7 @@ dependencies = [
|
||||
name = "helix-vcs"
|
||||
version = "0.6.0"
|
||||
dependencies = [
|
||||
"arc-swap",
|
||||
"gix",
|
||||
"helix-core",
|
||||
"imara-diff",
|
||||
|
@ -111,6 +111,7 @@ ### `[editor.statusline]` Section
|
||||
| `position-percentage` | The cursor position as a percentage of the total number of lines |
|
||||
| `separator` | The string defined in `editor.statusline.separator` (defaults to `"│"`) |
|
||||
| `spacer` | Inserts a space between elements (multiple/contiguous spacers may be specified) |
|
||||
| `version-control` | The current branch name or detached commit hash of the opened workspace |
|
||||
|
||||
### `[editor.lsp]` Section
|
||||
|
||||
|
@ -159,6 +159,7 @@ fn get_render_function<F>(element_id: StatusLineElementID) -> impl Fn(&mut Rende
|
||||
helix_view::editor::StatusLineElement::TotalLineNumbers => render_total_line_numbers,
|
||||
helix_view::editor::StatusLineElement::Separator => render_separator,
|
||||
helix_view::editor::StatusLineElement::Spacer => render_spacer,
|
||||
helix_view::editor::StatusLineElement::VersionControl => render_version_control,
|
||||
}
|
||||
}
|
||||
|
||||
@ -476,3 +477,16 @@ fn render_spacer<F>(context: &mut RenderContext, write: F)
|
||||
{
|
||||
write(context, String::from(" "), None);
|
||||
}
|
||||
|
||||
fn render_version_control<F>(context: &mut RenderContext, write: F)
|
||||
where
|
||||
F: Fn(&mut RenderContext, String, Option<Style>) + Copy,
|
||||
{
|
||||
let head = context
|
||||
.doc
|
||||
.version_control_head()
|
||||
.unwrap_or_default()
|
||||
.to_string();
|
||||
|
||||
write(context, head, None);
|
||||
}
|
||||
|
@ -15,6 +15,7 @@ helix-core = { version = "0.6", path = "../helix-core" }
|
||||
|
||||
tokio = { version = "1", features = ["rt", "rt-multi-thread", "time", "sync", "parking_lot", "macros"] }
|
||||
parking_lot = "0.12"
|
||||
arc-swap = { version = "1.6.0" }
|
||||
|
||||
gix = { version = "0.39.0", default-features = false , optional = true }
|
||||
imara-diff = "0.1.5"
|
||||
|
@ -1,4 +1,6 @@
|
||||
use arc_swap::ArcSwap;
|
||||
use std::path::Path;
|
||||
use std::sync::Arc;
|
||||
|
||||
use gix::objs::tree::EntryMode;
|
||||
use gix::sec::trust::DefaultForLevel;
|
||||
@ -87,6 +89,21 @@ fn get_diff_base(&self, file: &Path) -> Option<Vec<u8>> {
|
||||
}
|
||||
Some(data)
|
||||
}
|
||||
|
||||
fn get_current_head_name(&self, file: &Path) -> Option<Arc<ArcSwap<Box<str>>>> {
|
||||
debug_assert!(!file.exists() || file.is_file());
|
||||
debug_assert!(file.is_absolute());
|
||||
let repo = Git::open_repo(file.parent()?, None)?.to_thread_local();
|
||||
let head_ref = repo.head_ref().ok()?;
|
||||
let head_commit = repo.head_commit().ok()?;
|
||||
|
||||
let name = match head_ref {
|
||||
Some(reference) => reference.name().shorten().to_string(),
|
||||
None => head_commit.id.to_hex_with_len(8).to_string(),
|
||||
};
|
||||
|
||||
Some(Arc::new(ArcSwap::from_pointee(name.into_boxed_str())))
|
||||
}
|
||||
}
|
||||
|
||||
/// Finds the object that contains the contents of a file at a specific commit.
|
||||
|
@ -1,4 +1,5 @@
|
||||
use std::path::Path;
|
||||
use arc_swap::ArcSwap;
|
||||
use std::{path::Path, sync::Arc};
|
||||
|
||||
#[cfg(feature = "git")]
|
||||
pub use git::Git;
|
||||
@ -18,6 +19,7 @@ pub trait DiffProvider {
|
||||
/// The data is returned as raw byte without any decoding or encoding performed
|
||||
/// to ensure all file encodings are handled correctly.
|
||||
fn get_diff_base(&self, file: &Path) -> Option<Vec<u8>>;
|
||||
fn get_current_head_name(&self, file: &Path) -> Option<Arc<ArcSwap<Box<str>>>>;
|
||||
}
|
||||
|
||||
#[doc(hidden)]
|
||||
@ -26,6 +28,10 @@ impl DiffProvider for Dummy {
|
||||
fn get_diff_base(&self, _file: &Path) -> Option<Vec<u8>> {
|
||||
None
|
||||
}
|
||||
|
||||
fn get_current_head_name(&self, _file: &Path) -> Option<Arc<ArcSwap<Box<str>>>> {
|
||||
None
|
||||
}
|
||||
}
|
||||
|
||||
pub struct DiffProviderRegistry {
|
||||
@ -38,6 +44,12 @@ pub fn get_diff_base(&self, file: &Path) -> Option<Vec<u8>> {
|
||||
.iter()
|
||||
.find_map(|provider| provider.get_diff_base(file))
|
||||
}
|
||||
|
||||
pub fn get_current_head_name(&self, file: &Path) -> Option<Arc<ArcSwap<Box<str>>>> {
|
||||
self.providers
|
||||
.iter()
|
||||
.find_map(|provider| provider.get_current_head_name(file))
|
||||
}
|
||||
}
|
||||
|
||||
impl Default for DiffProviderRegistry {
|
||||
|
@ -1,5 +1,6 @@
|
||||
use anyhow::{anyhow, bail, Context, Error};
|
||||
use arc_swap::access::DynAccess;
|
||||
use arc_swap::ArcSwap;
|
||||
use futures_util::future::BoxFuture;
|
||||
use futures_util::FutureExt;
|
||||
use helix_core::auto_pairs::AutoPairs;
|
||||
@ -158,6 +159,7 @@ pub struct Document {
|
||||
language_server: Option<Arc<helix_lsp::Client>>,
|
||||
|
||||
diff_handle: Option<DiffHandle>,
|
||||
version_control_head: Option<Arc<ArcSwap<Box<str>>>>,
|
||||
}
|
||||
|
||||
use std::{fmt, mem};
|
||||
@ -404,6 +406,7 @@ pub fn from(
|
||||
language_server: None,
|
||||
diff_handle: None,
|
||||
config,
|
||||
version_control_head: None,
|
||||
}
|
||||
}
|
||||
pub fn default(config: Arc<dyn DynAccess<Config>>) -> Self {
|
||||
@ -707,6 +710,8 @@ pub fn reload(
|
||||
None => self.diff_handle = None,
|
||||
}
|
||||
|
||||
self.version_control_head = provider_registry.get_current_head_name(&path);
|
||||
|
||||
Ok(())
|
||||
}
|
||||
|
||||
@ -1158,6 +1163,17 @@ pub fn set_diff_base(&mut self, diff_base: Vec<u8>, redraw_handle: RedrawHandle)
|
||||
}
|
||||
}
|
||||
|
||||
pub fn version_control_head(&self) -> Option<Arc<Box<str>>> {
|
||||
self.version_control_head.as_ref().map(|a| a.load_full())
|
||||
}
|
||||
|
||||
pub fn set_version_control_head(
|
||||
&mut self,
|
||||
version_control_head: Option<Arc<ArcSwap<Box<str>>>>,
|
||||
) {
|
||||
self.version_control_head = version_control_head;
|
||||
}
|
||||
|
||||
#[inline]
|
||||
/// Tree-sitter AST tree
|
||||
pub fn syntax(&self) -> Option<&Syntax> {
|
||||
|
@ -467,6 +467,9 @@ pub enum StatusLineElement {
|
||||
|
||||
/// A single space
|
||||
Spacer,
|
||||
|
||||
/// Current version control information
|
||||
VersionControl,
|
||||
}
|
||||
|
||||
// Cursor shape is read and used on every rendered frame and so needs
|
||||
@ -1297,6 +1300,7 @@ pub fn open(&mut self, path: &Path, action: Action) -> Result<DocumentId, Error>
|
||||
if let Some(diff_base) = self.diff_providers.get_diff_base(&path) {
|
||||
doc.set_diff_base(diff_base, self.redraw_handle.clone());
|
||||
}
|
||||
doc.set_version_control_head(self.diff_providers.get_current_head_name(&path));
|
||||
|
||||
let id = self.new_document(doc);
|
||||
let _ = self.launch_language_server(id);
|
||||
|
Loading…
Reference in New Issue
Block a user