Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Changed file picker #5645

Merged
merged 15 commits into from Mar 31, 2024
Merged
Show file tree
Hide file tree
Changes from 6 commits
Commits
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Jump to
Jump to file
Failed to load files.
Diff view
Diff view
67 changes: 67 additions & 0 deletions Cargo.lock

Some generated files are not rendered by default. Learn more about how customized files appear on GitHub.

6 changes: 6 additions & 0 deletions helix-term/src/commands.rs
Expand Up @@ -324,6 +324,7 @@ impl MappableCommand {
buffer_picker, "Open buffer picker",
jumplist_picker, "Open jumplist picker",
symbol_picker, "Open symbol picker",
changed_file_picker, "Open changed file picker",
select_references_to_symbol_under_cursor, "Select symbol references",
workspace_symbol_picker, "Open workspace symbol picker",
diagnostics_picker, "Open diagnostic picker",
Expand Down Expand Up @@ -2994,6 +2995,11 @@ fn jumplist_picker(cx: &mut Context) {
cx.push_layer(Box::new(overlaid(picker)));
}

fn changed_file_picker(cx: &mut Context) {
let picker = ui::changed_file_picker(cx.editor);
xJonathanLEI marked this conversation as resolved.
Show resolved Hide resolved
cx.push_layer(Box::new(overlaid(picker)));
}

impl ui::menu::Item for MappableCommand {
type Data = ReverseKeymap;

Expand Down
3 changes: 2 additions & 1 deletion helix-term/src/keymap/default.rs
Expand Up @@ -223,9 +223,10 @@ pub fn default() -> HashMap<Mode, KeyTrie> {
"S" => workspace_symbol_picker,
"d" => diagnostics_picker,
"D" => workspace_diagnostics_picker,
"g" => changed_file_picker,
"a" => code_action,
"'" => last_picker,
"g" => { "Debug (experimental)" sticky=true
"G" => { "Debug (experimental)" sticky=true
"l" => dap_launch,
"r" => dap_restart,
"b" => dap_toggle_breakpoint,
Expand Down
36 changes: 36 additions & 0 deletions helix-term/src/ui/menu.rs
Expand Up @@ -14,9 +14,11 @@ use tui::{

pub use tui::widgets::{Cell, Row};

use helix_vcs::FileChange;
use helix_view::{
editor::SmartTabConfig,
graphics::{Margin, Rect},
theme::Style,
Editor,
};
use tui::layout::Constraint;
Expand Down Expand Up @@ -52,6 +54,40 @@ impl Item for PathBuf {

pub type MenuCallback<T> = Box<dyn Fn(&mut Editor, Option<&T>, MenuEvent)>;

pub struct FileChangeData {
xJonathanLEI marked this conversation as resolved.
Show resolved Hide resolved
pub cwd: PathBuf,
pub style_untracked: Style,
pub style_modified: Style,
pub style_deleted: Style,
pub style_renamed: Style,
}

impl Item for FileChange {
type Data = FileChangeData;

fn format(&self, data: &Self::Data) -> Row {
let process_path = |path: &PathBuf| {
path.strip_prefix(&data.cwd)
.unwrap_or(path)
.display()
.to_string()
};

let (sign, style, content) = match self {
Self::Untracked { path } => ("[+]", data.style_untracked, process_path(path)),
Self::Modified { path } => ("[~]", data.style_modified, process_path(path)),
Self::Deleted { path } => ("[-]", data.style_deleted, process_path(path)),
Self::Renamed { from_path, to_path } => (
"[>]",
data.style_modified,
format!("{} -> {}", process_path(from_path), process_path(to_path)),
),
};

Row::new(vec![sign.to_owned(), content]).style(style)
xJonathanLEI marked this conversation as resolved.
Show resolved Hide resolved
}
}

pub struct Menu<T: Item> {
options: Vec<T>,
editor_data: T::Data,
Expand Down
44 changes: 43 additions & 1 deletion helix-term/src/ui/mod.rs
Expand Up @@ -19,8 +19,9 @@ use crate::job::{self, Callback};
pub use completion::{Completion, CompletionItem};
pub use editor::EditorView;
use helix_stdx::rope;
use helix_vcs::FileChange;
pub use markdown::Markdown;
pub use menu::Menu;
pub use menu::{FileChangeData, Menu};
pub use picker::{DynamicPicker, FileLocation, Picker};
pub use popup::Popup;
pub use prompt::{Prompt, PromptEvent};
Expand Down Expand Up @@ -255,6 +256,47 @@ pub fn file_picker(root: PathBuf, config: &helix_view::editor::Config) -> Picker
picker
}

pub fn changed_file_picker(editor: &helix_view::Editor) -> Picker<FileChange> {
let cwd = std::env::current_dir().unwrap_or_else(|_| PathBuf::from("./"));

let added = editor.theme.get("diff.plus");
let deleted = editor.theme.get("diff.minus");
let modified = editor.theme.get("diff.delta");

let picker = Picker::new(
Vec::new(),
menu::FileChangeData {
cwd: cwd.clone(),
style_untracked: added,
style_modified: modified,
style_deleted: deleted,
style_renamed: modified,
},
|cx, meta: &FileChange, action| {
let path_to_open = meta.path();
if let Err(e) = cx.editor.open(path_to_open, action) {
let err = if let Some(err) = e.source() {
format!("{}", err)
} else {
format!("unable to open \"{}\"", path_to_open.display())
};
cx.editor.set_error(err);
}
},
)
.with_preview(|_editor, meta| Some((meta.path().to_path_buf().into(), None)));
let injector = picker.injector();

editor.diff_providers.clone().for_each_changed_file(
cwd,
move |change| injector.push(change).is_ok(),
|err| {
helix_event::status::report_blocking(err);
},
);
picker
}

pub mod completers {
use crate::ui::prompt::Completion;
use helix_core::fuzzy::fuzzy_match;
Expand Down
2 changes: 1 addition & 1 deletion helix-vcs/Cargo.toml
Expand Up @@ -19,7 +19,7 @@ tokio = { version = "1", features = ["rt", "rt-multi-thread", "time", "sync", "p
parking_lot = "0.12"
arc-swap = { version = "1.7.0" }

gix = { version = "0.61.0", features = ["attributes"], default-features = false, optional = true }
gix = { version = "0.61.0", features = ["attributes", "status"], default-features = false, optional = true }
imara-diff = "0.1.5"
anyhow = "1"

Expand Down
28 changes: 28 additions & 0 deletions helix-vcs/src/diff.rs
@@ -1,4 +1,5 @@
use std::ops::Range;
use std::path::{Path, PathBuf};
use std::sync::Arc;

use helix_core::Rope;
Expand Down Expand Up @@ -290,3 +291,30 @@ impl Diff<'_> {
}
}
}

pub enum FileChange {
xJonathanLEI marked this conversation as resolved.
Show resolved Hide resolved
Untracked {
path: PathBuf,
},
Modified {
path: PathBuf,
},
Deleted {
path: PathBuf,
},
Renamed {
from_path: PathBuf,
to_path: PathBuf,
},
}

impl FileChange {
pub fn path(&self) -> &Path {
match self {
Self::Untracked { path } => path,
Self::Modified { path } => path,
Self::Deleted { path } => path,
Self::Renamed { to_path, .. } => to_path,
}
}
}