Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
4 changes: 2 additions & 2 deletions cli/src/commands/config/subcommands/edit.rs
Original file line number Diff line number Diff line change
Expand Up @@ -11,7 +11,7 @@ use crate::extensions::{LocationSelection, WithLocations};
/// Implements the `edit` subcommand for Meva configuration management.
///
/// Opens the chosen configuration file in the user's preferred editor,
/// respecting any `core.editor` override in config or falling back to OS defaults.
/// respecting any `editor.default` override in config or falling back to OS defaults.
#[derive(Default)]
pub struct ConfigEditCommand;

Expand Down Expand Up @@ -45,7 +45,7 @@ impl MevaCommand for ConfigEditCommand {
_container: &Self::Container,
) -> miette::Result<()> {
let loader = MevaConfigLoader::default();
let override_cmd = loader.get("core.editor", None).ok();
let override_cmd = loader.get("editor.default", None).ok();
let location = matches
.get_config_location()
.get_default_path()
Expand Down
4 changes: 2 additions & 2 deletions cli/src/commands/ignore/subcommands/edit.rs
Original file line number Diff line number Diff line change
Expand Up @@ -14,7 +14,7 @@ use crate::{commands::MevaCommand, extensions::WithFile};
/// Implements the `edit` subcommand for Meva ignored files management.
///
/// Opens the chosen configuration file in the user's preferred editor,
/// respecting any `core.editor` override in config or falling back to OS defaults.
/// respecting any `editor.default` override in config or falling back to OS defaults.
#[derive(Default)]
pub struct IgnoreEditCommand;

Expand Down Expand Up @@ -50,7 +50,7 @@ impl MevaCommand for IgnoreEditCommand {
let ignore_service = IgnoreService::new(layout.ignore_file_name());

let loader = MevaConfigLoader::default();
let override_cmd = loader.get("core.editor", None).ok();
let override_cmd = loader.get("editor.default", None).ok();

let ignore_file = match file {
Some(p) => p.to_path_buf(),
Expand Down
2 changes: 1 addition & 1 deletion cli/src/commands/plugins/subcommands/edit.rs
Original file line number Diff line number Diff line change
Expand Up @@ -94,7 +94,7 @@ impl MevaCommand for PluginsEditCommand {

if enabled.is_none() {
let loader = MevaConfigLoader::default();
let override_cmd = loader.get("core.editor", None).ok();
let override_cmd = loader.get("editor.default", None).ok();
response
.source_file
.open_in_editor(override_cmd)
Expand Down
1 change: 1 addition & 0 deletions engine/src/handlers/status/handlers.rs
Original file line number Diff line number Diff line change
Expand Up @@ -160,6 +160,7 @@ impl StatusHandler {
ahead: 0,
behind: 0,
},
// TODO: Fetch real upstream/ahead/behind info
HeadMode::Symbolic => BranchInfo {
head: head.extract_branch_name(),
is_detached: false,
Expand Down
2 changes: 1 addition & 1 deletion gui/src/main.rs
Original file line number Diff line number Diff line change
Expand Up @@ -19,7 +19,7 @@ fn configure_frame_options() -> eframe::NativeOptions {

eframe::NativeOptions {
viewport: egui::ViewportBuilder::default()
.with_inner_size([800.0, 600.0])
.with_inner_size([1200.0, 800.0])
.with_icon(icon),
centered: true,
..Default::default()
Expand Down
14 changes: 12 additions & 2 deletions gui/src/meva_gui.rs
Original file line number Diff line number Diff line change
Expand Up @@ -221,8 +221,18 @@ impl MevaGui {

if let Some(status) = &self.repository_status {
ui.horizontal(|ui| {
RefreshStatusButton::new(&mut self.async_worker, &self.container)
.show(ui, ctx);
let has_unmerged = status
.unmerged
.as_ref()
.map(|v| !v.is_empty())
.unwrap_or(false);

MoreActionsButton::new(
&mut self.async_worker,
&self.container,
has_unmerged,
)
.show(ui, ctx);
ui.with_layout(
egui::Layout::top_down_justified(egui::Align::Center),
|ui| {
Expand Down
2 changes: 2 additions & 0 deletions gui/src/ui/components/buttons.rs
Original file line number Diff line number Diff line change
@@ -1,5 +1,6 @@
mod commit_button;
mod icon_button;
mod more_actions_button;
mod navigation_button;
mod open_repository_button;
mod refresh_status_button;
Expand All @@ -8,6 +9,7 @@ mod theme_button;

pub use commit_button::CommitButton;
pub use icon_button::IconButton;
pub use more_actions_button::MoreActionsButton;
pub use navigation_button::NavigationButton;
pub use open_repository_button::OpenRepositoryButton;
pub use refresh_status_button::RefreshStatusButton;
Expand Down
170 changes: 170 additions & 0 deletions gui/src/ui/components/buttons/more_actions_button.rs
Original file line number Diff line number Diff line change
@@ -0,0 +1,170 @@
use std::{sync::Arc, thread};

use egui::{RichText, Ui};
use egui_phosphor::regular as icons;

use engine::{
EngineContainer,
engine_container::MevaContainer,
handlers::{
// merge::Request as MergeRequest,
branch::{BranchOperations, ListRequest, Request as BranchRequest},
status::Request as StatusRequest,
},
};

use crate::events::{AsyncWorker, EventError, WorkerEvent, WorkerResult};

/// A UI component providing a dropdown menu for secondary repository actions.
///
/// Contains operations like:
/// - **Abort Merge**: Available only during a merge conflict.
/// - **Refresh Status**: Forces a re-scan of the working directory.
pub struct MoreActionsButton<'a> {
worker: &'a mut AsyncWorker,
container: &'a Arc<MevaContainer>,
has_unmerged_changes: bool,
}

impl<'a> MoreActionsButton<'a> {
/// Creates a new [`MoreActionsButton`].
pub fn new(
worker: &'a mut AsyncWorker,
container: &'a Arc<MevaContainer>,
has_unmerged_changes: bool,
) -> Self {
Self {
worker,
container,
has_unmerged_changes,
}
}

/// Renders the button (three vertical dots) and the dropdown menu.
pub fn show(&mut self, ui: &mut Ui, ctx: &egui::Context) {
ui.menu_button(icons::DOTS_THREE_OUTLINE_VERTICAL, |ui| {
ui.style_mut().visuals.button_frame = true;
ui.set_min_width(150.0);

if ui
.button(format!("{} Refresh Status", icons::ARROWS_CLOCKWISE))
.on_hover_text("Force reload of file status and branches")
.clicked()
{
ui.close_kind(egui::UiKind::Menu);
self.handle_refresh(ctx);
}

// TODO: remove the "true" condition when the merge detection is implemented
// if self.has_unmerged_changes || true {
if self.has_unmerged_changes {
ui.separator();
if ui
.button(
RichText::new(format!("{} Abort Merge", icons::PROHIBIT))
.color(ui.visuals().error_fg_color),
)
.on_hover_text("Abort the current merge and restore HEAD")
.clicked()
{
ui.close_kind(egui::UiKind::Menu);
self.handle_abort_merge(ctx);
}
}
});
}

/// Initiates background status refresh.
fn handle_refresh(&mut self, ctx: &egui::Context) {
let container = self.container.clone();
let ctx_clone = ctx.clone();

self.worker.spawn(ctx.clone(), move |tx| {
let mut report = |msg: String| {
let _ = tx.send(WorkerEvent::Progress(msg));
ctx_clone.request_repaint();
};

match Self::execute_refresh_logic(container, &mut report) {
Ok(result) => {
let _ = tx.send(WorkerEvent::Success(result));
}
Err(err) => {
let _ = tx.send(WorkerEvent::Error(EventError::new(
"Refresh Failed".to_string(),
"Could not refresh status".to_string(),
err,
)));
}
}
ctx_clone.request_repaint();
});
}

/// Initiates the background task to abort the merge.
fn handle_abort_merge(&mut self, ctx: &egui::Context) {
let container = self.container.clone();
let ctx_clone = ctx.clone();

self.worker.spawn(ctx.clone(), move |tx| {
let mut report = |msg: String| {
let _ = tx.send(WorkerEvent::Progress(msg));
ctx_clone.request_repaint();
};

match Self::execute_abort_merge(container, &mut report) {
Ok(result) => {
let _ = tx.send(WorkerEvent::Success(result));
}
Err(err_msg) => {
let _ = tx.send(WorkerEvent::Error(EventError::new(
"Abort Failed".to_string(),
"Could not abort merge".to_string(),
err_msg,
)));
}
}
ctx_clone.request_repaint();
});
}

/// Logic for Abort Merge.
fn execute_abort_merge(
container: Arc<MevaContainer>,
report: &mut dyn FnMut(String),
) -> Result<WorkerResult, String> {
report("Aborting merge...".to_string());
thread::sleep(std::time::Duration::from_millis(500));

// TODO: Implement actual merge abort logic
// let request = MergeRequest {};
// let merge_handler = container.merge_handler().map_err(|e| e.to_string())?;
// merge_handler.handle_merge(request).map_err(|e| e.to_string())?;

Self::execute_refresh_logic(container, report)
}

/// Shared logic for reloading status and branches.
fn execute_refresh_logic(
container: Arc<MevaContainer>,
report: &mut dyn FnMut(String),
) -> Result<WorkerResult, String> {
report("Refreshing status...".to_string());
thread::sleep(std::time::Duration::from_millis(500));

let status_handler = container.status_handler().map_err(|e| e.to_string())?;
let status = status_handler
.handle_status(StatusRequest::with_branch())
.map_err(|e| e.to_string())?;

report("Loading branches...".into());
thread::sleep(std::time::Duration::from_millis(500));

let branch_handler = container.branch_handler().map_err(|e| e.to_string())?;
let branch = branch_handler
.branch(BranchRequest::List(ListRequest::local_only(false)))
.map_err(|e| e.to_string())?;

Ok(WorkerResult::RepositoryOpened { branch, status })
}
}
1 change: 1 addition & 0 deletions gui/src/ui/components/buttons/refresh_status_button.rs
Original file line number Diff line number Diff line change
Expand Up @@ -104,6 +104,7 @@ impl<'a> RefreshStatusButton<'a> {
.map_err(|e| e.to_string())?;

report("Loading branches...".into());
thread::sleep(std::time::Duration::from_millis(500));

let branch_handler = container.branch_handler().map_err(|e| e.to_string())?;
let branch = branch_handler
Expand Down
Loading