mirror of
https://github.com/extrawurst/gitui.git
synced 2024-11-22 11:03:25 +03:00
PoC list submodules (#1090)
This commit is contained in:
parent
bcb565788e
commit
ef3ece552d
@ -7,6 +7,13 @@ and this project adheres to [Semantic Versioning](https://semver.org/spec/v2.0.0
|
||||
|
||||
## Unreleased
|
||||
|
||||
**submodules view**
|
||||
|
||||
![submodules](assets/submodules.png)
|
||||
|
||||
### Added
|
||||
* submodules support ([#1087](https://github.com/extrawurst/gitui/issues/1087))
|
||||
|
||||
## [0.21.0] - 2021-08-17
|
||||
|
||||
**popup stacking**
|
||||
|
2
Makefile
2
Makefile
@ -2,7 +2,7 @@
|
||||
.PHONY: debug build-release release-linux-musl test clippy clippy-pedantic install install-debug
|
||||
|
||||
ARGS=-l
|
||||
# ARGS=-l -d ~/code/git-bare-test.git
|
||||
# ARGS=-l -d ~/code/extern/pbrt-v4
|
||||
# ARGS=-l -d ~/code/git-bare-test.git -w ~/code/git-bare-test
|
||||
|
||||
profile:
|
||||
|
BIN
assets/submodules.png
Normal file
BIN
assets/submodules.png
Normal file
Binary file not shown.
After Width: | Height: | Size: 152 KiB |
@ -10,6 +10,12 @@ use unicode_truncate::UnicodeTruncateStr;
|
||||
)]
|
||||
pub struct CommitId(Oid);
|
||||
|
||||
impl Default for CommitId {
|
||||
fn default() -> Self {
|
||||
Self(Oid::zero())
|
||||
}
|
||||
}
|
||||
|
||||
impl CommitId {
|
||||
/// create new `CommitId`
|
||||
pub const fn new(id: Oid) -> Self {
|
||||
|
@ -27,6 +27,7 @@ mod staging;
|
||||
mod stash;
|
||||
mod state;
|
||||
pub mod status;
|
||||
mod submodules;
|
||||
mod tags;
|
||||
mod tree;
|
||||
pub mod utils;
|
||||
@ -80,6 +81,9 @@ pub use stash::{
|
||||
};
|
||||
pub use state::{repo_state, RepoState};
|
||||
pub use status::is_workdir_clean;
|
||||
pub use submodules::{
|
||||
get_submodules, update_submodule, SubmoduleInfo, SubmoduleStatus,
|
||||
};
|
||||
pub use tags::{
|
||||
delete_tag, get_tags, get_tags_with_metadata, CommitTags, Tag,
|
||||
TagWithMetadata, Tags,
|
||||
|
84
asyncgit/src/sync/submodules.rs
Normal file
84
asyncgit/src/sync/submodules.rs
Normal file
@ -0,0 +1,84 @@
|
||||
use std::path::PathBuf;
|
||||
|
||||
use git2::SubmoduleUpdateOptions;
|
||||
use scopetime::scope_time;
|
||||
|
||||
use super::{repo, CommitId, RepoPath};
|
||||
use crate::{error::Result, Error};
|
||||
|
||||
pub use git2::SubmoduleStatus;
|
||||
|
||||
///
|
||||
pub struct SubmoduleInfo {
|
||||
///
|
||||
pub path: PathBuf,
|
||||
///
|
||||
pub url: Option<String>,
|
||||
///
|
||||
pub id: Option<CommitId>,
|
||||
///
|
||||
pub head_id: Option<CommitId>,
|
||||
///
|
||||
pub status: SubmoduleStatus,
|
||||
}
|
||||
|
||||
impl SubmoduleInfo {
|
||||
///
|
||||
pub fn get_repo_path(
|
||||
&self,
|
||||
repo_path: &RepoPath,
|
||||
) -> Result<RepoPath> {
|
||||
let repo = repo(repo_path)?;
|
||||
let wd = repo.workdir().ok_or(Error::NoWorkDir)?;
|
||||
|
||||
Ok(RepoPath::Path(wd.join(self.path.clone())))
|
||||
}
|
||||
}
|
||||
|
||||
///
|
||||
pub fn get_submodules(
|
||||
repo_path: &RepoPath,
|
||||
) -> Result<Vec<SubmoduleInfo>> {
|
||||
scope_time!("get_submodules");
|
||||
|
||||
let (r, repo2) = (repo(repo_path)?, repo(repo_path)?);
|
||||
|
||||
let res = r
|
||||
.submodules()?
|
||||
.iter()
|
||||
.map(|s| {
|
||||
let status = repo2
|
||||
.submodule_status(
|
||||
s.name().unwrap_or_default(),
|
||||
git2::SubmoduleIgnore::None,
|
||||
)
|
||||
.unwrap_or(SubmoduleStatus::empty());
|
||||
|
||||
SubmoduleInfo {
|
||||
path: s.path().to_path_buf(),
|
||||
id: s.workdir_id().map(CommitId::from),
|
||||
head_id: s.head_id().map(CommitId::from),
|
||||
url: s.url().map(String::from),
|
||||
status,
|
||||
}
|
||||
})
|
||||
.collect();
|
||||
|
||||
Ok(res)
|
||||
}
|
||||
|
||||
///
|
||||
pub fn update_submodule(
|
||||
repo_path: &RepoPath,
|
||||
path: &str,
|
||||
) -> Result<()> {
|
||||
let repo = repo(repo_path)?;
|
||||
|
||||
let mut submodule = repo.find_submodule(path)?;
|
||||
|
||||
let mut options = SubmoduleUpdateOptions::new();
|
||||
|
||||
submodule.update(true, Some(&mut options))?;
|
||||
|
||||
Ok(())
|
||||
}
|
14
src/app.rs
14
src/app.rs
@ -11,7 +11,8 @@ use crate::{
|
||||
MsgComponent, OptionsPopupComponent, PullComponent,
|
||||
PushComponent, PushTagsComponent, RenameBranchComponent,
|
||||
RevisionFilesPopup, SharedOptions, StashMsgComponent,
|
||||
TagCommitComponent, TagListComponent,
|
||||
SubmodulesListComponent, TagCommitComponent,
|
||||
TagListComponent,
|
||||
},
|
||||
input::{Input, InputEvent, InputState},
|
||||
keys::{key_match, KeyConfig, SharedKeyConfig},
|
||||
@ -70,6 +71,7 @@ pub struct App {
|
||||
rename_branch_popup: RenameBranchComponent,
|
||||
select_branch_popup: BranchListComponent,
|
||||
options_popup: OptionsPopupComponent,
|
||||
submodule_popup: SubmodulesListComponent,
|
||||
tags_popup: TagListComponent,
|
||||
cmdbar: RefCell<CommandBar>,
|
||||
tab: usize,
|
||||
@ -231,6 +233,11 @@ impl App {
|
||||
key_config.clone(),
|
||||
options.clone(),
|
||||
),
|
||||
submodule_popup: SubmodulesListComponent::new(
|
||||
repo.clone(),
|
||||
theme.clone(),
|
||||
key_config.clone(),
|
||||
),
|
||||
find_file_popup: FileFindPopup::new(
|
||||
&queue,
|
||||
theme.clone(),
|
||||
@ -543,6 +550,7 @@ impl App {
|
||||
rename_branch_popup,
|
||||
select_branch_popup,
|
||||
revision_files_popup,
|
||||
submodule_popup,
|
||||
tags_popup,
|
||||
options_popup,
|
||||
help,
|
||||
@ -567,6 +575,7 @@ impl App {
|
||||
external_editor_popup,
|
||||
tag_commit_popup,
|
||||
select_branch_popup,
|
||||
submodule_popup,
|
||||
tags_popup,
|
||||
create_branch_popup,
|
||||
rename_branch_popup,
|
||||
@ -775,6 +784,9 @@ impl App {
|
||||
InternalEvent::SelectBranch => {
|
||||
self.select_branch_popup.open()?;
|
||||
}
|
||||
InternalEvent::ViewSubmodules => {
|
||||
self.submodule_popup.open()?;
|
||||
}
|
||||
InternalEvent::Tags => {
|
||||
self.tags_popup.open()?;
|
||||
}
|
||||
|
@ -541,9 +541,9 @@ impl BranchListComponent {
|
||||
const HEAD_SYMBOL: char = '*';
|
||||
const EMPTY_SYMBOL: char = ' ';
|
||||
const THREE_DOTS: &str = "...";
|
||||
const THREE_DOTS_LENGTH: usize = THREE_DOTS.len(); // "..."
|
||||
const COMMIT_HASH_LENGTH: usize = 8;
|
||||
const IS_HEAD_STAR_LENGTH: usize = 3; // "* "
|
||||
const THREE_DOTS_LENGTH: usize = THREE_DOTS.len(); // "..."
|
||||
|
||||
let branch_name_length: usize =
|
||||
width_available as usize * 40 / 100;
|
||||
|
@ -26,6 +26,7 @@ mod revision_files;
|
||||
mod revision_files_popup;
|
||||
mod stashmsg;
|
||||
mod status_tree;
|
||||
mod submodules;
|
||||
mod syntax_text;
|
||||
mod tag_commit;
|
||||
mod taglist;
|
||||
@ -61,6 +62,7 @@ pub use reset::ConfirmComponent;
|
||||
pub use revision_files::RevisionFilesComponent;
|
||||
pub use revision_files_popup::{FileTreeOpen, RevisionFilesPopup};
|
||||
pub use stashmsg::StashMsgComponent;
|
||||
pub use submodules::SubmodulesListComponent;
|
||||
pub use syntax_text::SyntaxTextComponent;
|
||||
pub use tag_commit::TagCommitComponent;
|
||||
pub use taglist::TagListComponent;
|
||||
|
402
src/components/submodules.rs
Normal file
402
src/components/submodules.rs
Normal file
@ -0,0 +1,402 @@
|
||||
use super::{
|
||||
utils::scroll_vertical::VerticalScroll, visibility_blocking,
|
||||
CommandBlocking, CommandInfo, Component, DrawableComponent,
|
||||
EventState, ScrollType,
|
||||
};
|
||||
use crate::{
|
||||
keys::{key_match, SharedKeyConfig},
|
||||
strings,
|
||||
ui::{self, Size},
|
||||
};
|
||||
use anyhow::Result;
|
||||
use asyncgit::sync::{get_submodules, RepoPathRef, SubmoduleInfo};
|
||||
use crossterm::event::Event;
|
||||
use std::{cell::Cell, convert::TryInto};
|
||||
use tui::{
|
||||
backend::Backend,
|
||||
layout::{
|
||||
Alignment, Constraint, Direction, Layout, Margin, Rect,
|
||||
},
|
||||
text::{Span, Spans, Text},
|
||||
widgets::{Block, BorderType, Borders, Clear, Paragraph},
|
||||
Frame,
|
||||
};
|
||||
use ui::style::SharedTheme;
|
||||
use unicode_truncate::UnicodeTruncateStr;
|
||||
|
||||
///
|
||||
pub struct SubmodulesListComponent {
|
||||
repo: RepoPathRef,
|
||||
submodules: Vec<SubmoduleInfo>,
|
||||
visible: bool,
|
||||
current_height: Cell<u16>,
|
||||
selection: u16,
|
||||
scroll: VerticalScroll,
|
||||
theme: SharedTheme,
|
||||
key_config: SharedKeyConfig,
|
||||
}
|
||||
|
||||
impl DrawableComponent for SubmodulesListComponent {
|
||||
fn draw<B: Backend>(
|
||||
&self,
|
||||
f: &mut Frame<B>,
|
||||
rect: Rect,
|
||||
) -> Result<()> {
|
||||
if self.is_visible() {
|
||||
const PERCENT_SIZE: Size = Size::new(80, 80);
|
||||
const MIN_SIZE: Size = Size::new(60, 30);
|
||||
|
||||
let area = ui::centered_rect(
|
||||
PERCENT_SIZE.width,
|
||||
PERCENT_SIZE.height,
|
||||
rect,
|
||||
);
|
||||
let area = ui::rect_inside(MIN_SIZE, rect.into(), area);
|
||||
let area = area.intersection(rect);
|
||||
|
||||
f.render_widget(Clear, area);
|
||||
|
||||
f.render_widget(
|
||||
Block::default()
|
||||
.title(strings::POPUP_TITLE_SUBMODULES)
|
||||
.border_type(BorderType::Thick)
|
||||
.borders(Borders::ALL),
|
||||
area,
|
||||
);
|
||||
|
||||
let area = area.inner(&Margin {
|
||||
vertical: 1,
|
||||
horizontal: 1,
|
||||
});
|
||||
|
||||
let chunks = Layout::default()
|
||||
.direction(Direction::Horizontal)
|
||||
.constraints(
|
||||
[Constraint::Min(40), Constraint::Length(40)]
|
||||
.as_ref(),
|
||||
)
|
||||
.split(area);
|
||||
|
||||
self.draw_list(f, chunks[0])?;
|
||||
self.draw_info(f, chunks[1]);
|
||||
}
|
||||
|
||||
Ok(())
|
||||
}
|
||||
}
|
||||
|
||||
impl Component for SubmodulesListComponent {
|
||||
fn commands(
|
||||
&self,
|
||||
out: &mut Vec<CommandInfo>,
|
||||
force_all: bool,
|
||||
) -> CommandBlocking {
|
||||
if self.visible || force_all {
|
||||
if !force_all {
|
||||
out.clear();
|
||||
}
|
||||
|
||||
out.push(CommandInfo::new(
|
||||
strings::commands::scroll(&self.key_config),
|
||||
true,
|
||||
true,
|
||||
));
|
||||
|
||||
out.push(CommandInfo::new(
|
||||
strings::commands::close_popup(&self.key_config),
|
||||
true,
|
||||
true,
|
||||
));
|
||||
}
|
||||
visibility_blocking(self)
|
||||
}
|
||||
|
||||
fn event(&mut self, ev: &Event) -> Result<EventState> {
|
||||
if !self.visible {
|
||||
return Ok(EventState::NotConsumed);
|
||||
}
|
||||
|
||||
if let Event::Key(e) = ev {
|
||||
if key_match(e, self.key_config.keys.exit_popup) {
|
||||
self.hide();
|
||||
} else if key_match(e, self.key_config.keys.move_down) {
|
||||
return self
|
||||
.move_selection(ScrollType::Up)
|
||||
.map(Into::into);
|
||||
} else if key_match(e, self.key_config.keys.move_up) {
|
||||
return self
|
||||
.move_selection(ScrollType::Down)
|
||||
.map(Into::into);
|
||||
} else if key_match(e, self.key_config.keys.page_down) {
|
||||
return self
|
||||
.move_selection(ScrollType::PageDown)
|
||||
.map(Into::into);
|
||||
} else if key_match(e, self.key_config.keys.page_up) {
|
||||
return self
|
||||
.move_selection(ScrollType::PageUp)
|
||||
.map(Into::into);
|
||||
} else if key_match(e, self.key_config.keys.home) {
|
||||
return self
|
||||
.move_selection(ScrollType::Home)
|
||||
.map(Into::into);
|
||||
} else if key_match(e, self.key_config.keys.end) {
|
||||
return self
|
||||
.move_selection(ScrollType::End)
|
||||
.map(Into::into);
|
||||
} else if key_match(
|
||||
e,
|
||||
self.key_config.keys.cmd_bar_toggle,
|
||||
) {
|
||||
//do not consume if its the more key
|
||||
return Ok(EventState::NotConsumed);
|
||||
}
|
||||
}
|
||||
|
||||
Ok(EventState::Consumed)
|
||||
}
|
||||
|
||||
fn is_visible(&self) -> bool {
|
||||
self.visible
|
||||
}
|
||||
|
||||
fn hide(&mut self) {
|
||||
self.visible = false;
|
||||
}
|
||||
|
||||
fn show(&mut self) -> Result<()> {
|
||||
self.visible = true;
|
||||
|
||||
Ok(())
|
||||
}
|
||||
}
|
||||
|
||||
impl SubmodulesListComponent {
|
||||
pub fn new(
|
||||
repo: RepoPathRef,
|
||||
theme: SharedTheme,
|
||||
key_config: SharedKeyConfig,
|
||||
) -> Self {
|
||||
Self {
|
||||
submodules: Vec::new(),
|
||||
scroll: VerticalScroll::new(),
|
||||
selection: 0,
|
||||
visible: false,
|
||||
theme,
|
||||
key_config,
|
||||
current_height: Cell::new(0),
|
||||
repo,
|
||||
}
|
||||
}
|
||||
|
||||
///
|
||||
pub fn open(&mut self) -> Result<()> {
|
||||
self.show()?;
|
||||
self.update_submodules()?;
|
||||
|
||||
Ok(())
|
||||
}
|
||||
|
||||
///
|
||||
pub fn update_submodules(&mut self) -> Result<()> {
|
||||
if self.is_visible() {
|
||||
self.submodules = get_submodules(&self.repo.borrow())?;
|
||||
|
||||
self.set_selection(self.selection)?;
|
||||
}
|
||||
Ok(())
|
||||
}
|
||||
|
||||
fn selected_entry(&self) -> Option<&SubmoduleInfo> {
|
||||
self.submodules.get(self.selection as usize)
|
||||
}
|
||||
|
||||
//TODO: dedup this almost identical with BranchListComponent
|
||||
fn move_selection(&mut self, scroll: ScrollType) -> Result<bool> {
|
||||
let new_selection = match scroll {
|
||||
ScrollType::Up => self.selection.saturating_add(1),
|
||||
ScrollType::Down => self.selection.saturating_sub(1),
|
||||
ScrollType::PageDown => self
|
||||
.selection
|
||||
.saturating_add(self.current_height.get()),
|
||||
ScrollType::PageUp => self
|
||||
.selection
|
||||
.saturating_sub(self.current_height.get()),
|
||||
ScrollType::Home => 0,
|
||||
ScrollType::End => {
|
||||
let count: u16 = self.submodules.len().try_into()?;
|
||||
count.saturating_sub(1)
|
||||
}
|
||||
};
|
||||
|
||||
self.set_selection(new_selection)?;
|
||||
|
||||
Ok(true)
|
||||
}
|
||||
|
||||
fn set_selection(&mut self, selection: u16) -> Result<()> {
|
||||
let num_branches: u16 = self.submodules.len().try_into()?;
|
||||
let num_branches = num_branches.saturating_sub(1);
|
||||
|
||||
let selection = if selection > num_branches {
|
||||
num_branches
|
||||
} else {
|
||||
selection
|
||||
};
|
||||
|
||||
self.selection = selection;
|
||||
|
||||
Ok(())
|
||||
}
|
||||
|
||||
fn get_text(
|
||||
&self,
|
||||
theme: &SharedTheme,
|
||||
width_available: u16,
|
||||
height: usize,
|
||||
) -> Text {
|
||||
const THREE_DOTS: &str = "...";
|
||||
const THREE_DOTS_LENGTH: usize = THREE_DOTS.len(); // "..."
|
||||
const COMMIT_HASH_LENGTH: usize = 8;
|
||||
|
||||
let mut txt = Vec::with_capacity(3);
|
||||
|
||||
let name_length: usize = (width_available as usize)
|
||||
.saturating_sub(COMMIT_HASH_LENGTH)
|
||||
.saturating_sub(THREE_DOTS_LENGTH);
|
||||
|
||||
for (i, submodule) in self
|
||||
.submodules
|
||||
.iter()
|
||||
.skip(self.scroll.get_top())
|
||||
.take(height)
|
||||
.enumerate()
|
||||
{
|
||||
let mut module_path = submodule
|
||||
.path
|
||||
.as_os_str()
|
||||
.to_string_lossy()
|
||||
.to_string();
|
||||
|
||||
if module_path.len() > name_length {
|
||||
module_path.unicode_truncate(
|
||||
name_length.saturating_sub(THREE_DOTS_LENGTH),
|
||||
);
|
||||
module_path += THREE_DOTS;
|
||||
}
|
||||
|
||||
let selected = (self.selection as usize
|
||||
- self.scroll.get_top())
|
||||
== i;
|
||||
|
||||
let span_hash = Span::styled(
|
||||
format!(
|
||||
"{} ",
|
||||
submodule
|
||||
.head_id
|
||||
.unwrap_or_default()
|
||||
.get_short_string()
|
||||
),
|
||||
theme.commit_hash(selected),
|
||||
);
|
||||
|
||||
let span_name = Span::styled(
|
||||
format!("{:w$} ", module_path, w = name_length),
|
||||
theme.text(true, selected),
|
||||
);
|
||||
|
||||
txt.push(Spans::from(vec![span_name, span_hash]));
|
||||
}
|
||||
|
||||
Text::from(txt)
|
||||
}
|
||||
|
||||
fn get_info_text(&self, theme: &SharedTheme) -> Text {
|
||||
self.selected_entry().map_or_else(
|
||||
Text::default,
|
||||
|submodule| {
|
||||
let span_title_path =
|
||||
Span::styled("Path:", theme.text(false, false));
|
||||
let span_path = Span::styled(
|
||||
submodule.path.to_string_lossy(),
|
||||
theme.text(true, false),
|
||||
);
|
||||
|
||||
let span_title_commit =
|
||||
Span::styled("Commit:", theme.text(false, false));
|
||||
let span_commit = Span::styled(
|
||||
submodule.id.unwrap_or_default().to_string(),
|
||||
theme.commit_hash(false),
|
||||
);
|
||||
|
||||
let span_title_url =
|
||||
Span::styled("Url:", theme.text(false, false));
|
||||
let span_url = Span::styled(
|
||||
submodule.url.clone().unwrap_or_default(),
|
||||
theme.text(true, false),
|
||||
);
|
||||
|
||||
let span_title_status =
|
||||
Span::styled("Status:", theme.text(false, false));
|
||||
let span_status = Span::styled(
|
||||
format!("{:?}", submodule.status),
|
||||
theme.text(true, false),
|
||||
);
|
||||
|
||||
Text::from(vec![
|
||||
Spans::from(vec![span_title_path]),
|
||||
Spans::from(vec![span_path]),
|
||||
Spans::from(vec![]),
|
||||
Spans::from(vec![span_title_commit]),
|
||||
Spans::from(vec![span_commit]),
|
||||
Spans::from(vec![]),
|
||||
Spans::from(vec![span_title_url]),
|
||||
Spans::from(vec![span_url]),
|
||||
Spans::from(vec![]),
|
||||
Spans::from(vec![span_title_status]),
|
||||
Spans::from(vec![span_status]),
|
||||
])
|
||||
},
|
||||
)
|
||||
}
|
||||
|
||||
fn draw_list<B: Backend>(
|
||||
&self,
|
||||
f: &mut Frame<B>,
|
||||
r: Rect,
|
||||
) -> Result<()> {
|
||||
let height_in_lines = r.height as usize;
|
||||
self.current_height.set(height_in_lines.try_into()?);
|
||||
|
||||
self.scroll.update(
|
||||
self.selection as usize,
|
||||
self.submodules.len(),
|
||||
height_in_lines,
|
||||
);
|
||||
|
||||
f.render_widget(
|
||||
Paragraph::new(self.get_text(
|
||||
&self.theme,
|
||||
r.width,
|
||||
height_in_lines,
|
||||
))
|
||||
.alignment(Alignment::Left),
|
||||
r,
|
||||
);
|
||||
|
||||
let mut r = r;
|
||||
r.height += 2;
|
||||
r.y = r.y.saturating_sub(1);
|
||||
|
||||
self.scroll.draw(f, r, &self.theme);
|
||||
|
||||
Ok(())
|
||||
}
|
||||
|
||||
fn draw_info<B: Backend>(&self, f: &mut Frame<B>, r: Rect) {
|
||||
f.render_widget(
|
||||
Paragraph::new(self.get_info_text(&self.theme))
|
||||
.alignment(Alignment::Left),
|
||||
r,
|
||||
);
|
||||
}
|
||||
}
|
@ -107,6 +107,7 @@ pub struct KeysList {
|
||||
pub undo_commit: GituiKeyEvent,
|
||||
pub stage_unstage_item: GituiKeyEvent,
|
||||
pub tag_annotate: GituiKeyEvent,
|
||||
pub view_submodules: GituiKeyEvent,
|
||||
}
|
||||
|
||||
#[rustfmt::skip]
|
||||
@ -185,6 +186,8 @@ impl Default for KeysList {
|
||||
file_find: GituiKeyEvent::new(KeyCode::Char('f'), KeyModifiers::empty()),
|
||||
stage_unstage_item: GituiKeyEvent::new(KeyCode::Enter, KeyModifiers::empty()),
|
||||
tag_annotate: GituiKeyEvent::new(KeyCode::Char('a'), KeyModifiers::CONTROL),
|
||||
view_submodules: GituiKeyEvent::new(KeyCode::Char('S'), KeyModifiers::SHIFT),
|
||||
|
||||
}
|
||||
}
|
||||
}
|
||||
|
@ -79,6 +79,7 @@ pub struct KeysListFile {
|
||||
pub undo_commit: Option<GituiKeyEvent>,
|
||||
pub stage_unstage_item: Option<GituiKeyEvent>,
|
||||
pub tag_annotate: Option<GituiKeyEvent>,
|
||||
pub view_submodules: Option<GituiKeyEvent>,
|
||||
}
|
||||
|
||||
impl KeysListFile {
|
||||
@ -166,6 +167,7 @@ impl KeysListFile {
|
||||
undo_commit: self.undo_commit.unwrap_or(default.undo_commit),
|
||||
stage_unstage_item: self.stage_unstage_item.unwrap_or(default.stage_unstage_item),
|
||||
tag_annotate: self.tag_annotate.unwrap_or(default.tag_annotate),
|
||||
view_submodules: self.view_submodules.unwrap_or(default.view_submodules),
|
||||
}
|
||||
}
|
||||
}
|
||||
|
@ -122,6 +122,8 @@ pub enum InternalEvent {
|
||||
PopupStackPop,
|
||||
///
|
||||
PopupStackPush(StackablePopupOpen),
|
||||
///
|
||||
ViewSubmodules,
|
||||
}
|
||||
|
||||
/// single threaded simple queue for components to communicate with each other
|
||||
|
@ -23,6 +23,8 @@ pub static PUSH_TAGS_STATES_FETCHING: &str = "fetching";
|
||||
pub static PUSH_TAGS_STATES_PUSHING: &str = "pushing";
|
||||
pub static PUSH_TAGS_STATES_DONE: &str = "done";
|
||||
|
||||
pub static POPUP_TITLE_SUBMODULES: &str = "Submodules";
|
||||
|
||||
pub mod symbol {
|
||||
pub const WHITESPACE: &str = "\u{00B7}"; //·
|
||||
pub const CHECKMARK: &str = "\u{2713}"; //✓
|
||||
@ -700,6 +702,19 @@ pub mod commands {
|
||||
)
|
||||
}
|
||||
|
||||
pub fn view_submodules(
|
||||
key_config: &SharedKeyConfig,
|
||||
) -> CommandText {
|
||||
CommandText::new(
|
||||
format!(
|
||||
"Submodules [{}]",
|
||||
key_config.get_hint(key_config.keys.view_submodules),
|
||||
),
|
||||
"open submodule view",
|
||||
CMD_GROUP_GENERAL,
|
||||
)
|
||||
}
|
||||
|
||||
pub fn continue_rebase(
|
||||
key_config: &SharedKeyConfig,
|
||||
) -> CommandText {
|
||||
|
@ -781,6 +781,12 @@ impl Component for Status {
|
||||
true,
|
||||
self.pending_revert() || force_all,
|
||||
));
|
||||
|
||||
out.push(CommandInfo::new(
|
||||
strings::commands::view_submodules(&self.key_config),
|
||||
true,
|
||||
true,
|
||||
));
|
||||
}
|
||||
|
||||
{
|
||||
@ -936,6 +942,12 @@ impl Component for Status {
|
||||
NeedsUpdate::ALL,
|
||||
));
|
||||
Ok(EventState::Consumed)
|
||||
} else if key_match(
|
||||
k,
|
||||
self.key_config.keys.view_submodules,
|
||||
) {
|
||||
self.queue.push(InternalEvent::ViewSubmodules);
|
||||
Ok(EventState::Consumed)
|
||||
} else {
|
||||
Ok(EventState::NotConsumed)
|
||||
};
|
||||
|
Loading…
Reference in New Issue
Block a user