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 Aug 31, 2023
1 parent d5e93c6 commit 05ceea1
Show file tree
Hide file tree
Showing 4 changed files with 101 additions and 45 deletions.
15 changes: 14 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,15 @@ 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);
doc.set_original_text(doc.text().clone());
if diff_source == DiffSource::File {
if let Some(path) = doc.path().cloned() {
doc.update_diff_base(&path, &self.editor.diff_provider, diff_source);
}
}

// 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
82 changes: 58 additions & 24 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 @@ -130,6 +130,7 @@ impl SavePoint {
pub struct Document {
pub(crate) id: DocumentId,
text: Rope,
original_text: Rope,
selections: HashMap<ViewId, Selection>,

/// Inlay hints annotations for the document, by view.
Expand Down Expand Up @@ -652,7 +653,8 @@ impl Document {
path: None,
encoding,
has_bom,
text,
text: text.clone(),
original_text: text,
selections: HashMap::default(),
inlay_hints: HashMap::default(),
inlay_hints_oudated: false,
Expand Down Expand Up @@ -991,7 +993,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 +1024,9 @@ 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.original_text = rope;

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);

Ok(())
}
Expand Down Expand Up @@ -1591,16 +1584,48 @@ 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 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 update_diff_base(&mut self, path: &Path, diff_provider: &Git, diff_source: DiffSource) {
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;
}
}

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
}
};
}
DiffSource::File => {
self.set_diff_base_raw(self.original_text.clone());
self.version_control_head = None;
}
}
}

pub fn version_control_head(&self) -> Option<Arc<Box<str>>> {
self.version_control_head.as_ref().map(|a| a.load_full())
}
Expand Down Expand Up @@ -1650,6 +1675,15 @@ impl Document {
&self.text
}

#[inline]
pub fn original_text(&self) -> &Rope {
&self.original_text
}

pub fn set_original_text(&mut self, text: Rope) {
self.original_text = text;
}

#[inline]
pub fn selection(&self, view_id: ViewId) -> &Selection {
&self.selections[&view_id]
Expand Down
38 changes: 22 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,7 @@ 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);

let id = self.new_document(doc);
let _ = self.launch_language_servers(id);
Expand Down Expand Up @@ -1832,6 +1829,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);
}
}
}
}

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

0 comments on commit 05ceea1

Please sign in to comment.