summaryrefslogtreecommitdiffstats
path: root/src/command
diff options
context:
space:
mode:
authorCanop <cano.petrole@gmail.com>2021-04-08 17:22:30 +0200
committerCanop <cano.petrole@gmail.com>2021-04-15 13:00:23 +0200
commit831c10d6c5ccf74d0109b6bc3ffd708a9da61cab (patch)
tree1435f23e6f19549e49f01e52f4cbfc702ec3a06b /src/command
parentacade5051d04d14509b5526f48b1bb9de2ee4f70 (diff)
staging area [WIP]
The staging area will be the way to operate on multiple files. See issue #266 (it's far from ready)
Diffstat (limited to 'src/command')
-rw-r--r--src/command/completion.rs124
-rw-r--r--src/command/panel_input.rs11
2 files changed, 88 insertions, 47 deletions
diff --git a/src/command/completion.rs b/src/command/completion.rs
index 862395c..82a3eb0 100644
--- a/src/command/completion.rs
+++ b/src/command/completion.rs
@@ -1,15 +1,18 @@
-
use {
super::CommandParts,
crate::{
app::{
AppContext,
- Selection,
+ SelectionType,
+ SelInfo,
},
path::{self, PathAnchor},
verb::PrefixSearchResult,
},
- std::io,
+ std::{
+ io,
+ path::Path,
+ },
};
/// find the longest common start of a and b
@@ -38,6 +41,9 @@ pub enum Completions {
impl Completions {
fn from_list(completions: Vec<String>) -> Self {
+ if completions.is_empty() {
+ return Self::None;
+ }
let mut iter = completions.iter();
let mut common: &str = match iter.next() {
Some(s) => &s,
@@ -77,9 +83,9 @@ impl Completions {
fn for_verb(
start: &str,
con: &AppContext,
- sel: Selection<'_>,
+ sel_info: SelInfo<'_>,
) -> Self {
- match con.verb_store.search(start, Some(sel.stype)) {
+ match con.verb_store.search(start, sel_info.common_stype()) {
PrefixSearchResult::NoMatch => Self::None,
PrefixSearchResult::Match(name, _) => {
if start.len() >= name.len() {
@@ -96,61 +102,95 @@ impl Completions {
}
}
- fn for_path(
- anchor: PathAnchor,
+ fn list_for_path(
+ verb_name: &str,
arg: &str,
- _con: &AppContext,
- sel: Selection<'_>,
- ) -> io::Result<Self> {
+ path: &Path,
+ stype: SelectionType,
+ con: &AppContext,
+ ) -> io::Result<Vec<String>> {
+ let anchor = match con.verb_store.search(verb_name, Some(stype)) {
+ PrefixSearchResult::Match(_, verb) => verb.get_arg_anchor(),
+ _ => PathAnchor::Unspecified,
+ };
let c = regex!(r"^(.*?)([^/]*)$").captures(arg).unwrap();
let parent_part = &c[1];
let child_part = &c[2];
- let parent = path::path_from(sel.path, anchor, parent_part);
+ let parent = path::path_from(path, anchor, parent_part);
+ let mut children = Vec::new();
if !parent.exists() {
debug!("no path completion possible because {:?} doesn't exist", &parent);
- return Ok(Self::None);
- }
- let mut children = Vec::new();
- for entry in parent.read_dir()? {
- let entry = entry?;
- let mut name = entry.file_name().to_string_lossy().to_string();
- if !child_part.is_empty() {
- if !name.starts_with(child_part) {
- continue;
- }
- if name == child_part && entry.file_type()?.is_dir() {
- name = "/".to_string();
- } else {
- name.drain(0..child_part.len());
+ } else {
+ for entry in parent.read_dir()? {
+ let entry = entry?;
+ let mut name = entry.file_name().to_string_lossy().to_string();
+ if !child_part.is_empty() {
+ if !name.starts_with(child_part) {
+ continue;
+ }
+ if name == child_part && entry.file_type()?.is_dir() {
+ name = "/".to_string();
+ } else {
+ name.drain(0..child_part.len());
+ }
}
+ children.push(name);
}
- children.push(name);
}
- Ok(Self::from_list(children))
+ Ok(children)
}
fn for_arg(
verb_name: &str,
arg: &str,
con: &AppContext,
- sel: Selection<'_>,
+ sel_info: SelInfo<'_>,
) -> Self {
// in the future we might offer completion of other types
// of arguments, maybe user supplied, but there's no use case
// now so we'll just assume the user wants to complete a path.
if arg.contains(' ') {
- Self::None
- } else {
- let anchor = match con.verb_store.search(verb_name, Some(sel.stype)) {
- PrefixSearchResult::Match(_, verb) => verb.get_arg_anchor(),
- _ => PathAnchor::Unspecified,
- };
- match Self::for_path(anchor, arg, con, sel) {
- Ok(c) => c,
- Err(e) => {
- warn!("Error while trying to complete path: {:?}", e);
- Self::None
+ return Self::None;
+ }
+ match sel_info {
+ SelInfo::None => Self::None,
+ SelInfo::One(sel) => {
+ match Self::list_for_path(verb_name, arg, sel.path, sel.stype, con) {
+ Ok(list) => Self::from_list(list),
+ Err(e) => {
+ warn!("Error while trying to complete path: {:?}", e);
+ Self::None
+ }
+ }
+ }
+ SelInfo::More(stage) => {
+ // We're looking for the possible completions which
+ // are valid for all elements of the stage
+ let mut lists = stage.paths.iter()
+ .filter_map(|path| {
+ Self::list_for_path(
+ verb_name,
+ arg,
+ path,
+ SelectionType::from(path),
+ con
+ ).ok()
+ });
+ let mut list = match lists.next() {
+ Some(list) => list,
+ None => {
+ // can happen if there were IO errors on paths in stage, for example
+ // on removals
+ return Self::None;
+ }
+ };
+ for ol in lists.next() {
+ list = list.iter().filter(|c| ol.contains(c)).cloned().collect();
+ if list.is_empty() {
+ break;
+ }
}
+ Self::from_list(list)
}
}
}
@@ -158,18 +198,18 @@ impl Completions {
pub fn for_input(
parts: &CommandParts,
con: &AppContext,
- sel: Selection<'_>,
+ sel_info: SelInfo<'_>,
) -> Self {
match &parts.verb_invocation {
Some(invocation) if !invocation.is_empty() => {
match &invocation.args {
None => {
// looking into verb completion
- Self::for_verb(&invocation.name, con, sel)
+ Self::for_verb(&invocation.name, con, sel_info)
}
Some(args) if !args.is_empty() => {
// looking into arg completion
- Self::for_arg(&invocation.name, args, con, sel)
+ Self::for_arg(&invocation.name, args, con, sel_info)
}
_ => {
// nothing possible
diff --git a/src/command/panel_input.rs b/src/command/panel_input.rs
index f4fe8a1..b2f6c7d 100644
--- a/src/command/panel_input.rs
+++ b/src/command/panel_input.rs
@@ -72,10 +72,10 @@ impl PanelInput {
w: &mut W,
event: Event,
con: &AppContext,
- sel: Selection<'_>,
+ sel_info: SelInfo<'_>,
mode: Mode,
) -> Result<Command, ProgramError> {
- let cmd = self.get_command(event, con, sel, mode);
+ let cmd = self.get_command(event, con, sel_info, mode);
self.input_field.display_on(w)?;
Ok(cmd)
}
@@ -152,7 +152,7 @@ impl PanelInput {
&mut self,
event: Event,
con: &AppContext,
- sel: Selection<'_>,
+ sel_info: SelInfo<'_>,
mode: Mode,
) -> Command {
match event {
@@ -209,7 +209,8 @@ impl PanelInput {
} else {
&parts
};
- let completions = Completions::for_input(completable_parts, con, sel);
+ let completions = Completions::for_input(completable_parts, con, sel_info);
+ info!(" -> completions: {:?}", &completions);
let added = match completions {
Completions::None => {
debug!("nothing to complete!");
@@ -268,7 +269,7 @@ impl PanelInput {
if self.handle_input_related_verb(verb, con) {
return Command::from_raw(self.input_field.get_content(), false);
}
- if sel.stype.respects(verb.selection_condition) {
+ if verb.selection_condition.is_respected_by(sel_info.common_stype()) {
if verb.is_internal(Internal::mode_input) {
self.enter_input_mode_with_key(key, &parts);
}