Skip to content

Commit

Permalink
add a diff source for the diff gutter based on the on-disk file contents
Browse files Browse the repository at this point in the history
  • Loading branch information
doy committed Sep 1, 2023
1 parent d5e93c6 commit 38b21b3
Show file tree
Hide file tree
Showing 4 changed files with 109 additions and 50 deletions.
19 changes: 18 additions & 1 deletion helix-term/src/application.rs
Original file line number Diff line number Diff line change
Expand Up @@ -13,7 +13,7 @@ use helix_lsp::{
use helix_view::{
align_view,
document::DocumentSavedEventResult,
editor::{ConfigEvent, EditorEvent},
editor::{ConfigEvent, DiffSource, EditorEvent},
graphics::Rect,
theme,
tree::Layout,
Expand Down Expand Up @@ -351,11 +351,15 @@ impl Application {
// the Application can apply it.
ConfigEvent::Update(editor_config) => {
let mut app_config = (*self.config.load().clone()).clone();
let update_diff_base = app_config.editor.diff_source != editor_config.diff_source;
app_config.editor = *editor_config;
if let Err(err) = self.terminal.reconfigure(app_config.editor.clone().into()) {
self.editor.set_error(err.to_string());
};
self.config.store(Arc::new(app_config));
if update_diff_base {
self.editor.update_diff_base();
}
}
}

Expand Down Expand Up @@ -557,6 +561,19 @@ impl Application {
self.editor.refresh_language_servers(id);
}

let diff_source = self.editor.config().diff_source;
let doc = doc_mut!(self.editor, &doc_save_event.doc_id);
if diff_source == DiffSource::File {
if let Some(path) = doc.path().cloned() {
doc.update_diff_base(
&path,
&self.editor.diff_provider,
diff_source,
Some(doc.text().clone()),
);
}
}

// TODO: fix being overwritten by lsp
self.editor.set_status(format!(
"'{}' written, {}L {}B",
Expand Down
11 changes: 7 additions & 4 deletions helix-term/src/commands/typed.rs
Original file line number Diff line number Diff line change
Expand Up @@ -1266,10 +1266,12 @@ fn reload(
}

let scrolloff = cx.editor.config().scrolloff;
let diff_source = cx.editor.config().diff_source;
let (view, doc) = current!(cx.editor);
doc.reload(view, &cx.editor.diff_provider).map(|_| {
view.ensure_cursor_in_view(doc, scrolloff);
})?;
doc.reload(view, &cx.editor.diff_provider, diff_source)
.map(|_| {
view.ensure_cursor_in_view(doc, scrolloff);
})?;
if let Some(path) = doc.path() {
cx.editor
.language_servers
Expand Down Expand Up @@ -1307,6 +1309,7 @@ fn reload_all(
.collect();

for (doc_id, view_ids) in docs_view_ids {
let diff_source = cx.editor.config().diff_source;
let doc = doc_mut!(cx.editor, &doc_id);

// Every doc is guaranteed to have at least 1 view at this point.
Expand All @@ -1315,7 +1318,7 @@ fn reload_all(
// Ensure that the view is synced with the document's history.
view.sync_changes(doc);

doc.reload(view, &cx.editor.diff_provider)?;
doc.reload(view, &cx.editor.diff_provider, diff_source)?;
if let Some(path) = doc.path() {
cx.editor
.language_servers
Expand Down
86 changes: 57 additions & 29 deletions helix-view/src/document.rs
Original file line number Diff line number Diff line change
Expand Up @@ -33,7 +33,7 @@ use helix_core::{
ChangeSet, Diagnostic, LineEnding, Range, Rope, RopeBuilder, Selection, Syntax, Transaction,
};

use crate::editor::Config;
use crate::editor::{Config, DiffSource};
use crate::{DocumentId, Editor, Theme, View, ViewId};

/// 8kB of buffer space for encoding and decoding `Rope`s.
Expand Down Expand Up @@ -991,7 +991,12 @@ impl Document {
}

/// Reload the document from its path.
pub fn reload(&mut self, view: &mut View, diff_provider: &Git) -> Result<(), Error> {
pub fn reload(
&mut self,
view: &mut View,
diff_provider: &Git,
diff_source: DiffSource,
) -> Result<(), Error> {
let encoding = self.encoding;
let path = self
.path()
Expand All @@ -1017,23 +1022,7 @@ impl Document {

self.detect_indent_and_line_ending();

match diff_provider.get_diff_base(&path) {
Ok(diff_base) => self.set_diff_base(diff_base),
Err(err) => {
log::info!("{err:#?}");
log::info!("failed to open diff base for for {}", path.display());
self.diff_handle = None;
}
}

self.version_control_head = match diff_provider.get_current_head_name(&path) {
Ok(res) => Some(res),
Err(err) => {
log::info!("{err:#?}");
log::info!("failed to obtain current head name for {}", path.display());
None
}
};
self.update_diff_base(&path, diff_provider, diff_source, Some(rope));

Ok(())
}
Expand Down Expand Up @@ -1591,25 +1580,64 @@ impl Document {
/// Intialize/updates the differ for this document with a new base.
pub fn set_diff_base(&mut self, diff_base: Vec<u8>) {
if let Ok((diff_base, ..)) = from_reader(&mut diff_base.as_slice(), Some(self.encoding)) {
if let Some(differ) = &self.diff_handle {
differ.update_diff_base(diff_base);
return;
}
self.diff_handle = Some(DiffHandle::new(diff_base, self.text.clone()))
self.set_diff_base_raw(diff_base);
} else {
self.diff_handle = None;
}
}

pub fn version_control_head(&self) -> Option<Arc<Box<str>>> {
self.version_control_head.as_ref().map(|a| a.load_full())
pub fn set_diff_base_raw(&mut self, diff_base: Rope) {
if let Some(differ) = &self.diff_handle {
differ.update_diff_base(diff_base);
return;
}
self.diff_handle = Some(DiffHandle::new(diff_base, self.text.clone()))
}

pub fn set_version_control_head(
pub fn update_diff_base(
&mut self,
version_control_head: Option<Arc<ArcSwap<Box<str>>>>,
path: &Path,
diff_provider: &Git,
diff_source: DiffSource,
text: Option<Rope>,
) {
self.version_control_head = version_control_head;
match diff_source {
DiffSource::Git => match diff_provider.get_diff_base(path) {
Ok(diff_base) => self.set_diff_base(diff_base),
Err(err) => {
log::info!("{err:#?}");
log::info!("failed to open diff base for for {}", path.display());
self.diff_handle = None;
}
},
DiffSource::File => {
if let Some(text) = text {
self.set_diff_base_raw(text);
} else {
match std::fs::read(path) {
Ok(bytes) => self.set_diff_base(bytes),
Err(err) => {
log::info!("{err:#?}");
log::info!("failed to open diff base for for {}", path.display());
self.diff_handle = None;
}
}
}
}
}

self.version_control_head = match diff_provider.get_current_head_name(path) {
Ok(res) => Some(res),
Err(err) => {
log::info!("{err:#?}");
log::info!("failed to obtain current head name for {}", path.display());
None
}
};
}

pub fn version_control_head(&self) -> Option<Arc<Box<str>>> {
self.version_control_head.as_ref().map(|a| a.load_full())
}

#[inline]
Expand Down
43 changes: 27 additions & 16 deletions helix-view/src/editor.rs
Original file line number Diff line number Diff line change
Expand Up @@ -73,6 +73,14 @@ where
)
}

#[derive(Debug, Default, Clone, Copy, PartialEq, Eq, Serialize, Deserialize)]
#[serde(rename_all = "kebab-case")]
pub enum DiffSource {
#[default]
Git,
File,
}

#[derive(Debug, Clone, PartialEq, Eq, Serialize, Deserialize)]
#[serde(rename_all = "kebab-case", default, deny_unknown_fields)]
pub struct GutterConfig {
Expand Down Expand Up @@ -289,6 +297,9 @@ pub struct Config {
pub default_line_ending: LineEndingConfig,
/// Enables smart tab
pub smart_tab: Option<SmartTabConfig>,
#[serde(default)]
/// What the diff gutter should diff against
pub diff_source: DiffSource,
}

#[derive(Debug, Clone, PartialEq, Deserialize, Serialize, Eq, PartialOrd, Ord)]
Expand Down Expand Up @@ -843,6 +854,7 @@ impl Default for Config {
workspace_lsp_roots: Vec::new(),
default_line_ending: LineEndingConfig::default(),
smart_tab: Some(SmartTabConfig::default()),
diff_source: DiffSource::default(),
}
}
}
Expand Down Expand Up @@ -1445,22 +1457,12 @@ impl Editor {
self.config.clone(),
)?;

match self.diff_provider.get_diff_base(&path) {
Ok(diff_base) => doc.set_diff_base(diff_base),
Err(err) => {
log::info!("{err:#?}");
log::info!("failed to open diff base for for {}", path.display());
}
}

doc.set_version_control_head(match self.diff_provider.get_current_head_name(&path) {
Ok(res) => Some(res),
Err(err) => {
log::info!("{err:#?}");
log::info!("failed to obtain current head name for {}", path.display());
None
}
});
doc.update_diff_base(
&path,
&self.diff_provider,
self.config().diff_source,
Some(doc.text().clone()),
);

let id = self.new_document(doc);
let _ = self.launch_language_servers(id);
Expand Down Expand Up @@ -1832,6 +1834,15 @@ impl Editor {
.as_ref()
.and_then(|debugger| debugger.current_stack_frame())
}

pub fn update_diff_base(&mut self) {
let diff_source = self.config().diff_source;
for doc in self.documents.values_mut() {
if let Some(path) = doc.path().cloned() {
doc.update_diff_base(&path, &self.diff_provider, diff_source, None);
}
}
}
}

fn try_restore_indent(doc: &mut Document, view: &mut View) {
Expand Down

0 comments on commit 38b21b3

Please sign in to comment.