on master help sign in
Add a version-control statusline element (#5682)
commit1661e4b5e1d8ebfef28f798fcb86ba2656373ba0 authorDimitar Gyurov <dgyurov@gmail.com> dateFri 10 Mar 2023, 23:42 (4 years ago) parent98415f2
8 files changed+67−1
Cargo.lock+1 book/src/configuration.md+1 helix-term/src/ui/statusline.rs+14 helix-vcs/Cargo.toml+1 helix-vcs/src/git.rs+17 helix-vcs/src/lib.rs+13−1 helix-view/src/document.rs+16 helix-view/src/editor.rs+4
Cargo.lock+1
@@ -1212,6 +1212,7 @@ dependencies = [
12121212name = "helix-vcs"
12131213version = "0.6.0"
12141214dependencies = [
1215+ "arc-swap",
12151216 "gix",
12161217 "helix-core",
12171218 "imara-diff",
book/src/configuration.md+1
@@ -111,6 +111,7 @@ The following statusline elements can be configured:
111111| `position-percentage` | The cursor position as a percentage of the total number of lines |
112112| `separator` | The string defined in `editor.statusline.separator` (defaults to `"│"`) |
113113| `spacer` | Inserts a space between elements (multiple/contiguous spacers may be specified) |
114+| `version-control` | The current branch name or detached commit hash of the opened workspace |
114115
115116### `[editor.lsp]` Section
116117
helix-term/src/ui/statusline.rs+14
@@ -159,6 +159,7 @@ where
159159 helix_view::editor::StatusLineElement::TotalLineNumbers => render_total_line_numbers,
160160 helix_view::editor::StatusLineElement::Separator => render_separator,
161161 helix_view::editor::StatusLineElement::Spacer => render_spacer,
162+ helix_view::editor::StatusLineElement::VersionControl => render_version_control,
162163 }
163164}
164165
@@ -476,3 +477,16 @@ where
476477{
477478 write(context, String::from(" "), None);
478479}
480+
481+fn render_version_control<F>(context: &mut RenderContext, write: F)
482+where
483+ F: Fn(&mut RenderContext, String, Option<Style>) + Copy,
484+{
485+ let head = context
486+ .doc
487+ .version_control_head()
488+ .unwrap_or_default()
489+ .to_string();
490+
491+ write(context, head, None);
492+}
helix-vcs/Cargo.toml+1
@@ -15,6 +15,7 @@ helix-core = { version = "0.6", path = "../helix-core" }
1515
1616tokio = { version = "1", features = ["rt", "rt-multi-thread", "time", "sync", "parking_lot", "macros"] }
1717parking_lot = "0.12"
18+arc-swap = { version = "1.6.0" }
1819
1920gix = { version = "0.39.0", default-features = false , optional = true }
2021imara-diff = "0.1.5"
helix-vcs/src/git.rs+17
@@ -1,4 +1,6 @@
1+use arc_swap::ArcSwap;
12use std::path::Path;
3+use std::sync::Arc;
24
35use gix::objs::tree::EntryMode;
46use gix::sec::trust::DefaultForLevel;
@@ -87,6 +89,21 @@ impl DiffProvider for Git {
8789 }
8890 Some(data)
8991 }
92+
93+ fn get_current_head_name(&self, file: &Path) -> Option<Arc<ArcSwap<Box<str>>>> {
94+ debug_assert!(!file.exists() || file.is_file());
95+ debug_assert!(file.is_absolute());
96+ let repo = Git::open_repo(file.parent()?, None)?.to_thread_local();
97+ let head_ref = repo.head_ref().ok()?;
98+ let head_commit = repo.head_commit().ok()?;
99+
100+ let name = match head_ref {
101+ Some(reference) => reference.name().shorten().to_string(),
102+ None => head_commit.id.to_hex_with_len(8).to_string(),
103+ };
104+
105+ Some(Arc::new(ArcSwap::from_pointee(name.into_boxed_str())))
106+ }
90107}
91108
92109/// Finds the object that contains the contents of a file at a specific commit.
helix-vcs/src/lib.rs+13−1
@@ -1,4 +1,5 @@
1−use std::path::Path;
1+use arc_swap::ArcSwap;
2+use std::{path::Path, sync::Arc};
23
34#[cfg(feature = "git")]
45pub use git::Git;
@@ -18,6 +19,7 @@ pub trait DiffProvider {
1819 /// The data is returned as raw byte without any decoding or encoding performed
1920 /// to ensure all file encodings are handled correctly.
2021 fn get_diff_base(&self, file: &Path) -> Option<Vec<u8>>;
22+ fn get_current_head_name(&self, file: &Path) -> Option<Arc<ArcSwap<Box<str>>>>;
2123}
2224
2325#[doc(hidden)]
@@ -26,6 +28,10 @@ impl DiffProvider for Dummy {
2628 fn get_diff_base(&self, _file: &Path) -> Option<Vec<u8>> {
2729 None
2830 }
31+
32+ fn get_current_head_name(&self, _file: &Path) -> Option<Arc<ArcSwap<Box<str>>>> {
33+ None
34+ }
2935}
3036
3137pub struct DiffProviderRegistry {
@@ -38,6 +44,12 @@ impl DiffProviderRegistry {
3844 .iter()
3945 .find_map(|provider| provider.get_diff_base(file))
4046 }
47+
48+ pub fn get_current_head_name(&self, file: &Path) -> Option<Arc<ArcSwap<Box<str>>>> {
49+ self.providers
50+ .iter()
51+ .find_map(|provider| provider.get_current_head_name(file))
52+ }
4153}
4254
4355impl Default for DiffProviderRegistry {
helix-view/src/document.rs+16
@@ -1,5 +1,6 @@
11use anyhow::{anyhow, bail, Context, Error};
22use arc_swap::access::DynAccess;
3+use arc_swap::ArcSwap;
34use futures_util::future::BoxFuture;
45use futures_util::FutureExt;
56use helix_core::auto_pairs::AutoPairs;
@@ -158,6 +159,7 @@ pub struct Document {
158159 language_server: Option<Arc<helix_lsp::Client>>,
159160
160161 diff_handle: Option<DiffHandle>,
162+ version_control_head: Option<Arc<ArcSwap<Box<str>>>>,
161163}
162164
163165use std::{fmt, mem};
@@ -404,6 +406,7 @@ impl Document {
404406 language_server: None,
405407 diff_handle: None,
406408 config,
409+ version_control_head: None,
407410 }
408411 }
409412 pub fn default(config: Arc<dyn DynAccess<Config>>) -> Self {
@@ -707,6 +710,8 @@ impl Document {
707710 None => self.diff_handle = None,
708711 }
709712
713+ self.version_control_head = provider_registry.get_current_head_name(&path);
714+
710715 Ok(())
711716 }
712717
@@ -1158,6 +1163,17 @@ impl Document {
11581163 }
11591164 }
11601165
1166+ pub fn version_control_head(&self) -> Option<Arc<Box<str>>> {
1167+ self.version_control_head.as_ref().map(|a| a.load_full())
1168+ }
1169+
1170+ pub fn set_version_control_head(
1171+ &mut self,
1172+ version_control_head: Option<Arc<ArcSwap<Box<str>>>>,
1173+ ) {
1174+ self.version_control_head = version_control_head;
1175+ }
1176+
11611177 #[inline]
11621178 /// Tree-sitter AST tree
11631179 pub fn syntax(&self) -> Option<&Syntax> {
helix-view/src/editor.rs+4
@@ -467,6 +467,9 @@ pub enum StatusLineElement {
467467
468468 /// A single space
469469 Spacer,
470+
471+ /// Current version control information
472+ VersionControl,
470473}
471474
472475// Cursor shape is read and used on every rendered frame and so needs
@@ -1297,6 +1300,7 @@ impl Editor {
12971300 if let Some(diff_base) = self.diff_providers.get_diff_base(&path) {
12981301 doc.set_diff_base(diff_base, self.redraw_handle.clone());
12991302 }
1303+ doc.set_version_control_head(self.diff_providers.get_current_head_name(&path));
13001304
13011305 let id = self.new_document(doc);
13021306 let _ = self.launch_language_server(id);