Repository: kbknapp/clap-rs
Pull request #1225 (2018-03-21 03:03:55)

V3 dev
Implements Indices on v3

<!-- Reviewable:start -->
---
This change is [<img src="https://reviewable.io/review_button.svg" height="34" align="absmiddle" alt="Reviewable"/>](https://reviewable.io/reviews/kbknapp/clap-rs/1225)
<!-- Reviewable:end -->



Patch:
diff --git a/src/app/parser.rs b/src/app/parser.rs
index c20a131e..95ed3fd2 100644
--- a/src/app/parser.rs
+++ b/src/app/parser.rs
@@ -6,6 +6,7 @@ use std::os::unix::ffi::OsStrExt;
 use std::slice::Iter;
 use std::iter::Peekable;
 use std::mem;
+use std::cell::Cell;
 
 // Third party
 use map::{self, VecMap};
@@ -55,6 +56,7 @@ where
     num_flags: usize,
     pub positionals: VecMap<&'a str>,
     seen: Vec<&'a str>,
+    cur_idx: Cell<usize>,
 }
 
 // Standalone split borrow functions
@@ -106,6 +108,7 @@ where
             num_flags: 0,
             positionals: VecMap::new(),
             seen: Vec::new(),
+            cur_idx: Cell::new(0),
         }
     }
 
@@ -487,7 +490,7 @@ where
             let low_index_mults = self.is_set(AS::LowIndexMultiplePositional)
                 && pos_counter == (self.positionals.len() - 1);
             let missing_pos = self.is_set(AS::AllowMissingPositional)
-                && (pos_counter == (self.positionals.len() - 1) 
+                && (pos_counter == (self.positionals.len() - 1)
                     && !self.is_set(AS::TrailingValues));
             debugln!(
                 "Parser::get_matches_with: Positional counter...{}",
@@ -930,6 +933,10 @@ where
     ) -> ClapResult<ParseResult<'a>> {
         // maybe here lifetime should be 'a
         debugln!("Parser::parse_long_arg;");
+
+        // Update the curent index
+        self.cur_idx.set(self.cur_idx.get() + 1);
+
         let mut val = None;
         debug!("Parser::parse_long_arg: Does it contain '='...");
         let arg = if full_arg.contains_byte(b'=') {
@@ -1013,6 +1020,10 @@ where
         let mut ret = ParseResult::NotFound;
         for c in arg.chars() {
             debugln!("Parser::parse_short_arg:iter:{}", c);
+
+            // update each index because `-abcd` is four indices to clap
+            self.cur_idx.set(self.cur_idx.get() + 1);
+
             // Check for matching short options, and return the name if there is no trailing
             // concatenated value: -oval
             // Option: -o
@@ -1183,12 +1194,19 @@ where
     ) -> ClapResult<ParseResult<'a>> {
         debugln!("Parser::add_single_val_to_arg;");
         debugln!("Parser::add_single_val_to_arg: adding val...{:?}", v);
+
+        // update the current index because each value is a distinct index to clap
+        self.cur_idx.set(self.cur_idx.get() + 1);
+
+        // @TODO @docs @p4 docs should probably note that terminator doesn't get an index
         if let Some(t) = arg.terminator {
             if t == v {
                 return Ok(ParseResult::ValuesDone);
             }
         }
+
         matcher.add_val_to(arg.name, v);
+        matcher.add_index_to(arg.name, self.cur_idx.get());
 
         // Increment or create the group "args"
         if let Some(grps) = self.groups_for_arg(arg.name) {
@@ -1211,6 +1229,8 @@ where
         debugln!("Parser::parse_flag;");
 
         matcher.inc_occurrence_of(flag.name);
+        matcher.add_index_to(flag.name, self.cur_idx.get());
+
         // Increment or create the group "args"
         self.groups_for_arg(flag.name)
             .and_then(|vec| Some(matcher.inc_occurrences_of(&*vec)));
diff --git a/src/args/arg_matcher.rs b/src/args/arg_matcher.rs
index 57a9db86..e2969f34 100644
--- a/src/args/arg_matcher.rs
+++ b/src/args/arg_matcher.rs
@@ -122,12 +122,22 @@ impl<'a> ArgMatcher<'a> {
     pub fn add_val_to(&mut self, arg: &'a str, val: &OsStr) {
         let ma = self.entry(arg).or_insert(MatchedArg {
             occurs: 0,
+            indices: Vec::with_capacity(1),
             vals: Vec::with_capacity(1),
         });
         // let len = ma.vals.len() + 1;
         ma.vals.push(val.to_owned());
     }
 
+    pub fn add_index_to(&mut self, arg: &'a str, idx: usize) {
+      let ma = self.entry(arg).or_insert(MatchedArg {
+        occurs: 0,
+        indices: Vec::with_capacity(1),
+        vals: Vec::new(),
+      });
+      ma.indices.push(idx);
+    }
+    
     pub fn needs_more_vals<'b>(&self, o: &Arg) -> bool {
         debugln!("ArgMatcher::needs_more_vals: o={}", o.name);
         if let Some(ma) = self.get(o.name) {
diff --git a/src/args/arg_matches.rs b/src/args/arg_matches.rs
index 074311e1..29912267 100644
--- a/src/args/arg_matches.rs
+++ b/src/args/arg_matches.rs
@@ -369,6 +369,229 @@ impl<'a> ArgMatches<'a> {
         self.args.get(name.as_ref()).map_or(0, |a| a.occurs)
     }
 
+    /// Gets the starting index of the argument in respect to all other arguments. Indices are
+    /// similar to argv indices, but are not exactly 1:1.
+    ///
+    /// For flags (i.e. those arguments which don't have an associated value), indices refer
+    /// to occurrence of the switch, such as `-f`, or `--flag`. However, for options the indices
+    /// refer to the *values* `-o val` would therefore not represent two distinct indices, only the
+    /// index for `val` would be recorded. This is by design.
+    ///
+    /// Besides the flag/option descrepancy, the primary difference between an argv index and clap
+    /// index, is that clap continues counting once all arguments have properly seperated, whereas
+    /// an argv index does not.
+    ///
+    /// The examples should clear this up.
+    ///
+    /// *NOTE:* If an argument is allowed multiple times, this method will only give the *first*
+    /// index.
+    ///
+    /// # Examples
+    ///
+    /// The argv indices are listed in the comments below. See how they correspond to the clap
+    /// indices. Note that if it's not listed in a clap index, this is becuase it's not saved in
+    /// in an `ArgMatches` struct for querying.
+    ///
+    /// ```rust
+    /// # use clap::{App, Arg};
+    /// let m = App::new("myapp")
+    ///     .arg(Arg::with_name("flag")
+    ///         .short("f"))
+    ///     .arg(Arg::with_name("option")
+    ///         .short("o")
+    ///         .takes_value(true))
+    ///     .get_matches_from(vec!["myapp", "-f", "-o", "val"]);
+    ///             // ARGV idices: ^0       ^1    ^2    ^3
+    ///             // clap idices:          ^1          ^3
+    ///
+    /// assert_eq!(m.index_of("flag"), Some(1));
+    /// assert_eq!(m.index_of("option"), Some(3));
+    /// ```
+    ///
+    /// Now notice, if we use one of the other styles of options:
+    ///
+    /// ```rust
+    /// # use clap::{App, Arg};
+    /// let m = App::new("myapp")
+    ///     .arg(Arg::with_name("flag")
+    ///         .short("f"))
+    ///     .arg(Arg::with_name("option")
+    ///         .short("o")
+    ///         .takes_value(true))
+    ///     .get_matches_from(vec!["myapp", "-f", "-o=val"]);
+    ///             // ARGV idices: ^0       ^1    ^2
+    ///             // clap idices:          ^1       ^3
+    ///
+    /// assert_eq!(m.index_of("flag"), Some(1));
+    /// assert_eq!(m.index_of("option"), Some(3));
+    /// ```
+    ///
+    /// Things become much more complicated, or clear if we look at a more complex combination of
+    /// flags. Let's also throw in the final option style for good measure.
+    ///
+    /// ```rust
+    /// # use clap::{App, Arg};
+    /// let m = App::new("myapp")
+    ///     .arg(Arg::with_name("flag")
+    ///         .short("f"))
+    ///     .arg(Arg::with_name("flag2")
+    ///         .short("F"))
+    ///     .arg(Arg::with_name("flag3")
+    ///         .short("z"))
+    ///     .arg(Arg::with_name("option")
+    ///         .short("o")
+    ///         .takes_value(true))
+    ///     .get_matches_from(vec!["myapp", "-fzF", "-oval"]);
+    ///             // ARGV idices: ^0      ^1       ^2
+    ///             // clap idices:         ^1,2,3    ^5
+    ///             //
+    ///             // clap sees the above as 'myapp -f -z -F -o val'
+    ///             //                         ^0    ^1 ^2 ^3 ^4 ^5
+    /// assert_eq!(m.index_of("flag"), Some(1));
+    /// assert_eq!(m.index_of("flag2"), Some(3));
+    /// assert_eq!(m.index_of("flag3"), Some(2));
+    /// assert_eq!(m.index_of("option"), Some(5));
+    /// ```
+    ///
+    /// One final combination of flags/options to see how they combine:
+    ///
+    /// ```rust
+    /// # use clap::{App, Arg};
+    /// let m = App::new("myapp")
+    ///     .arg(Arg::with_name("flag")
+    ///         .short("f"))
+    ///     .arg(Arg::with_name("flag2")
+    ///         .short("F"))
+    ///     .arg(Arg::with_name("flag3")
+    ///         .short("z"))
+    ///     .arg(Arg::with_name("option")
+    ///         .short("o")
+    ///         .takes_value(true)
+    ///         .multiple(true))
+    ///     .get_matches_from(vec!["myapp", "-fzFoval"]);
+    ///             // ARGV idices: ^0       ^1
+    ///             // clap idices:          ^1,2,3^5
+    ///             //
+    ///             // clap sees the above as 'myapp -f -z -F -o val'
+    ///             //                         ^0    ^1 ^2 ^3 ^4 ^5
+    /// assert_eq!(m.index_of("flag"), Some(1));
+    /// assert_eq!(m.index_of("flag2"), Some(3));
+    /// assert_eq!(m.index_of("flag3"), Some(2));
+    /// assert_eq!(m.index_of("option"), Some(5));
+    /// ```
+    ///
+    /// The last part to mention is when values are sent in multiple groups with a [delimiter].
+    ///
+    /// ```rust
+    /// # use clap::{App, Arg};
+    /// let m = App::new("myapp")
+    ///     .arg(Arg::with_name("option")
+    ///         .short("o")
+    ///         .takes_value(true)
+    ///         .multiple(true))
+    ///     .get_matches_from(vec!["myapp", "-o=val1,val2,val3"]);
+    ///             // ARGV idices: ^0       ^1
+    ///             // clap idices:             ^2   ^3   ^4
+    ///             //
+    ///             // clap sees the above as 'myapp -o val1 val2 val3'
+    ///             //                         ^0    ^1 ^2   ^3   ^4
+    /// assert_eq!(m.index_of("option"), Some(2));
+    /// ```
+    /// [`ArgMatches`]: ./struct.ArgMatches.html
+    /// [delimiter]: ./struct.Arg.html#method.value_delimiter
+    pub fn index_of<S: AsRef<str>>(&self, name: S) -> Option<usize> {
+        if let Some(arg) = self.args.get(name.as_ref()) {
+            if let Some(i) = arg.indices.get(0) {
+                return Some(*i);
+            }
+        }
+        None
+    }
+
+    /// Gets all indices of the argument in respect to all other arguments. Indices are
+    /// similar to argv indices, but are not exactly 1:1.
+    ///
+    /// For flags (i.e. those arguments which don't have an associated value), indices refer
+    /// to occurrence of the switch, such as `-f`, or `--flag`. However, for options the indices
+    /// refer to the *values* `-o val` would therefore not represent two distinct indices, only the
+    /// index for `val` would be recorded. This is by design.
+    ///
+    /// *NOTE:* For more information about how clap indices compare to argv indices, see
+    /// [`ArgMatches::index_of`]
+    ///
+    /// # Examples
+    ///
+    /// ```rust
+    /// # use clap::{App, Arg};
+    /// let m = App::new("myapp")
+    ///     .arg(Arg::with_name("option")
+    ///         .short("o")
+    ///         .takes_value(true)
+    ///         .use_delimiter(true)
+    ///         .multiple(true))
+    ///     .get_matches_from(vec!["myapp", "-o=val1,val2,val3"]);
+    ///             // ARGV idices: ^0       ^1
+    ///             // clap idices:             ^2   ^3   ^4
+    ///             //
+    ///             // clap sees the above as 'myapp -o val1 val2 val3'
+    ///             //                         ^0    ^1 ^2   ^3   ^4
+    /// assert_eq!(m.indices_of("option").unwrap().collect::<Vec<_>>(), &[2, 3, 4]);
+    /// ```
+    ///
+    /// Another quick example is when flags and options are used together
+    ///
+    /// ```rust
+    /// # use clap::{App, Arg};
+    /// let m = App::new("myapp")
+    ///     .arg(Arg::with_name("option")
+    ///         .short("o")
+    ///         .takes_value(true)
+    ///         .multiple(true))
+    ///     .arg(Arg::with_name("flag")
+    ///         .short("f")
+    ///         .multiple_occurrences(true))
+    ///     .get_matches_from(vec!["myapp", "-o", "val1", "-f", "-o", "val2", "-f"]);
+    ///             // ARGV idices: ^0       ^1    ^2      ^3    ^4    ^5      ^6
+    ///             // clap idices:                ^2      ^3          ^5      ^6
+    ///
+    /// assert_eq!(m.indices_of("option").unwrap().collect::<Vec<_>>(), &[2, 5]);
+    /// assert_eq!(m.indices_of("flag").unwrap().collect::<Vec<_>>(), &[3, 6]);
+    /// ```
+    ///
+    /// One final example, which is an odd case; if we *don't* use  value delimiter as we did with
+    /// the first example above instead of `val1`, `val2` and `val3` all being distinc values, they
+    /// would all be a single value of `val1,val2,val3`, in which case case they'd only receive a
+    /// single index.
+    ///
+    /// ```rust
+    /// # use clap::{App, Arg};
+    /// let m = App::new("myapp")
+    ///     .arg(Arg::with_name("option")
+    ///         .short("o")
+    ///         .takes_value(true)
+    ///         .multiple(true))
+    ///     .get_matches_from(vec!["myapp", "-o=val1,val2,val3"]);
+    ///             // ARGV idices: ^0       ^1
+    ///             // clap idices:             ^2
+    ///             //
+    ///             // clap sees the above as 'myapp -o "val1,val2,val3"'
+    ///             //                         ^0    ^1  ^2
+    /// assert_eq!(m.indices_of("option").unwrap().collect::<Vec<_>>(), &[2]);
+    /// ```
+    /// [`ArgMatches`]: ./struct.ArgMatches.html
+    /// [`ArgMatches::index_of`]: ./struct.ArgMatches.html#method.index_of
+    /// [delimiter]: ./struct.Arg.html#method.value_delimiter
+    pub fn indices_of<S: AsRef<str>>(&'a self, name: S) -> Option<Indices<'a>> {
+        if let Some(arg) = self.args.get(name.as_ref()) {
+            fn to_usize(i: &usize) -> usize { *i }
+            let to_usize: fn(&usize) -> usize = to_usize; // coerce to fn pointer
+            return Some(Indices {
+                iter: arg.indices.iter().map(to_usize),
+            });
+        }
+        None
+    }
+
     /// Because [`Subcommand`]s are essentially "sub-[`App`]s" they have their own [`ArgMatches`]
     /// as well. This method returns the [`ArgMatches`] for a particular subcommand or `None` if
     /// the subcommand wasn't present at runtime.
@@ -555,10 +778,16 @@ impl<'a> ArgMatches<'a> {
 /// # use clap::{App, Arg};
 /// let m = App::new("myapp")
 ///     .arg(Arg::with_name("output")
+///         .short("o")
+///         .multiple(true)
 ///         .takes_value(true))
-///     .get_matches_from(vec!["myapp", "something"]);
+///     .get_matches_from(vec!["myapp", "-o", "val1", "val2"]);
+///
+/// let mut values = m.values_of("output").unwrap();
 ///
-/// assert_eq!(m.value_of("output"), Some("something"));
+/// assert_eq!(values.next(), Some("val1"));
+/// assert_eq!(values.next(), Some("val2"));
+/// assert_eq!(values.next(), None);
 /// ```
 /// [`ArgMatches::values_of`]: ./struct.ArgMatches.html#method.values_of
 #[derive(Clone)]
@@ -592,19 +821,6 @@ impl<'a> Default for Values<'a> {
     }
 }
 
-#[test]
-fn test_default_values() {
-    let mut values: Values = Values::default();
-    assert_eq!(values.next(), None);
-}
-
-#[test]
-fn test_default_values_with_shorter_lifetime() {
-    let matches = ArgMatches::new();
-    let mut values = matches.values_of("").unwrap_or_default();
-    assert_eq!(values.next(), None);
-}
-
 /// An iterator for getting multiple values out of an argument via the [`ArgMatches::values_of_os`]
 /// method. Usage of this iterator allows values which contain invalid UTF-8 code points unlike
 /// [`Values`].
@@ -655,15 +871,98 @@ impl<'a> Default for OsValues<'a> {
     }
 }
 
-#[test]
-fn test_default_osvalues() {
-    let mut values: OsValues = OsValues::default();
-    assert_eq!(values.next(), None);
+/// An iterator for getting multiple indices out of an argument via the [`ArgMatches::indices_of`]
+/// method.
+///
+/// # Examples
+///
+/// ```rust
+/// # use clap::{App, Arg};
+/// let m = App::new("myapp")
+///     .arg(Arg::with_name("output")
+///         .short("o")
+///         .multiple(true)
+///         .takes_value(true))
+///     .get_matches_from(vec!["myapp", "-o", "val1", "val2"]);
+///
+/// let mut indices = m.indices_of("output").unwrap();
+///
+/// assert_eq!(indices.next(), Some(2));
+/// assert_eq!(indices.next(), Some(3));
+/// assert_eq!(indices.next(), None);
+/// ```
+/// [`ArgMatches::indices_of`]: ./struct.ArgMatches.html#method.indices_of
+#[derive(Clone)]
+#[allow(missing_debug_implementations)]
+pub struct Indices<'a> { // would rather use '_, but: https://github.com/rust-lang/rust/issues/48469
+    iter: Map<Iter<'a, usize>, fn(&'a usize) -> usize>,
+}
+
+impl<'a> Iterator for Indices<'a> {
+    type Item = usize;
+
+    fn next(&mut self) -> Option<usize> { self.iter.next() }
+    fn size_hint(&self) -> (usize, Option<usize>) { self.iter.size_hint() }
+}
+
+impl<'a> DoubleEndedIterator for Indices<'a> {
+    fn next_back(&mut self) -> Option<usize> { self.iter.next_back() }
+}
+
+impl<'a> ExactSizeIterator for Indices<'a> {}
+
+/// Creates an empty iterator.
+impl<'a> Default for Indices<'a> {
+    fn default() -> Self {
+        static EMPTY: [usize; 0] = [];
+        // This is never called because the iterator is empty:
+        fn to_usize(_: &usize) -> usize { unreachable!() };
+        Indices {
+            iter: EMPTY[..].iter().map(to_usize),
+        }
+    }
 }
 
-#[test]
-fn test_default_osvalues_with_shorter_lifetime() {
-    let matches = ArgMatches::new();
-    let mut values = matches.values_of_os("").unwrap_or_default();
-    assert_eq!(values.next(), None);
+#[cfg(test)]
+mod tests {
+    use super::*;
+
+    #[test]
+    fn test_default_values() {
+        let mut values: Values = Values::default();
+        assert_eq!(values.next(), None);
+    }
+
+    #[test]
+    fn test_default_values_with_shorter_lifetime() {
+        let matches = ArgMatches::new();
+        let mut values = matches.values_of("").unwrap_or_default();
+        assert_eq!(values.next(), None);
+    }
+
+    #[test]
+    fn test_default_osvalues() {
+        let mut values: OsValues = OsValues::default();
+        assert_eq!(values.next(), None);
+    }
+
+    #[test]
+    fn test_default_osvalues_with_shorter_lifetime() {
+        let matches = ArgMatches::new();
+        let mut values = matches.values_of_os("").unwrap_or_default();
+        assert_eq!(values.next(), None);
+    }
+
+    #[test]
+    fn test_default_indices() {
+        let mut indices: Indices = Indices::default();
+        assert_eq!(indices.next(), None);
+    }
+
+    #[test]
+    fn test_default_indices_with_shorter_lifetime() {
+        let matches = ArgMatches::new();
+        let mut indices = matches.indices_of("").unwrap_or_default();
+        assert_eq!(indices.next(), None);
+    }
 }
diff --git a/src/args/matched_arg.rs b/src/args/matched_arg.rs
index 9a73af9d..25a942e5 100644
--- a/src/args/matched_arg.rs
+++ b/src/args/matched_arg.rs
@@ -7,6 +7,8 @@ pub struct MatchedArg {
     #[doc(hidden)]
     pub occurs: u64,
     #[doc(hidden)]
+    pub indices: Vec<usize>,
+    #[doc(hidden)]
     pub vals: Vec<OsString>,
 }
 
@@ -14,7 +16,8 @@ impl Default for MatchedArg {
     fn default() -> Self {
         MatchedArg {
             occurs: 1,
-            vals: Vec::with_capacity(1),
+            indices: Vec::new(),
+            vals: Vec::new(),
         }
     }
 }


Test patch:
diff --git a/tests/indices.rs b/tests/indices.rs
new file mode 100644
index 00000000..60c29182
--- /dev/null
+++ b/tests/indices.rs
@@ -0,0 +1,175 @@
+extern crate clap;
+extern crate regex;
+
+include!("../clap-test.rs");
+
+use clap::{App, Arg};
+
+#[test]
+fn indices_mult_opts() {
+	let m = App::new("ind")
+		.arg(Arg::with_name("exclude")
+			.short("e")
+			.takes_value(true)
+			.multiple(true))
+		.arg(Arg::with_name("include")
+			.short("i")
+			.takes_value(true)
+			.multiple(true))
+		.get_matches_from(vec!["ind", "-e", "A", "B", "-i", "B", "C", "-e", "C"]);
+
+		assert_eq!(m.indices_of("exclude").unwrap().collect::<Vec<_>>(), &[2, 3, 8]);
+		assert_eq!(m.indices_of("include").unwrap().collect::<Vec<_>>(), &[5, 6]);
+}
+
+#[test]
+fn index_mult_opts() {
+	let m = App::new("ind")
+		.arg(Arg::with_name("exclude")
+			.short("e")
+			.takes_value(true)
+			.multiple(true))
+		.arg(Arg::with_name("include")
+			.short("i")
+			.takes_value(true)
+			.multiple(true))
+		.get_matches_from(vec!["ind", "-e", "A", "B", "-i", "B", "C", "-e", "C"]);
+
+		assert_eq!(m.index_of("exclude"), Some(2));
+		assert_eq!(m.index_of("include"), Some(5));
+}
+
+#[test]
+fn index_flag() {
+	let m = App::new("ind")
+		.arg(Arg::with_name("exclude")
+			.short("e"))
+		.arg(Arg::with_name("include")
+			.short("i"))
+		.get_matches_from(vec!["ind", "-e", "-i"]);
+
+		assert_eq!(m.index_of("exclude"), Some(1));
+		assert_eq!(m.index_of("include"), Some(2));
+}
+
+#[test]
+fn index_flags() {
+	let m = App::new("ind")
+		.arg(Arg::with_name("exclude")
+			.short("e")
+			.multiple_occurrences(true))
+		.arg(Arg::with_name("include")
+			.short("i")
+			.multiple_occurrences(true))
+		.get_matches_from(vec!["ind", "-e", "-i", "-e", "-e", "-i"]);
+
+		assert_eq!(m.index_of("exclude"), Some(1));
+		assert_eq!(m.index_of("include"), Some(2));
+}
+
+#[test]
+fn indices_mult_flags() {
+	let m = App::new("ind")
+		.arg(Arg::with_name("exclude")
+			.short("e")
+			.multiple_occurrences(true))
+		.arg(Arg::with_name("include")
+			.short("i")
+			.multiple_occurrences(true))
+		.get_matches_from(vec!["ind", "-e", "-i", "-e", "-e", "-i"]);
+
+		assert_eq!(m.indices_of("exclude").unwrap().collect::<Vec<_>>(), &[1, 3, 4]);
+		assert_eq!(m.indices_of("include").unwrap().collect::<Vec<_>>(), &[2, 5]);
+}
+
+#[test]
+fn indices_mult_flags_combined() {
+	let m = App::new("ind")
+		.arg(Arg::with_name("exclude")
+			.short("e")
+			.multiple_occurrences(true))
+		.arg(Arg::with_name("include")
+			.short("i")
+			.multiple_occurrences(true))
+		.get_matches_from(vec!["ind", "-eieei"]);
+
+		assert_eq!(m.indices_of("exclude").unwrap().collect::<Vec<_>>(), &[1, 3, 4]);
+		assert_eq!(m.indices_of("include").unwrap().collect::<Vec<_>>(), &[2, 5]);
+}
+
+#[test]
+fn indices_mult_flags_opt_combined() {
+	let m = App::new("ind")
+		.arg(Arg::with_name("exclude")
+			.short("e")
+			.multiple_occurrences(true))
+		.arg(Arg::with_name("include")
+			.short("i")
+			.multiple_occurrences(true))
+		.arg(Arg::with_name("option")
+			.short("o")
+			.takes_value(true))
+		.get_matches_from(vec!["ind", "-eieeio", "val"]);
+
+		assert_eq!(m.indices_of("exclude").unwrap().collect::<Vec<_>>(), &[1, 3, 4]);
+		assert_eq!(m.indices_of("include").unwrap().collect::<Vec<_>>(), &[2, 5]);
+		assert_eq!(m.indices_of("option").unwrap().collect::<Vec<_>>(), &[7]);
+}
+
+#[test]
+fn indices_mult_flags_opt_combined_eq() {
+	let m = App::new("ind")
+		.arg(Arg::with_name("exclude")
+			.short("e")
+			.multiple_occurrences(true))
+		.arg(Arg::with_name("include")
+			.short("i")
+			.multiple_occurrences(true))
+		.arg(Arg::with_name("option")
+			.short("o")
+			.takes_value(true))
+		.get_matches_from(vec!["ind", "-eieeio=val"]);
+
+		assert_eq!(m.indices_of("exclude").unwrap().collect::<Vec<_>>(), &[1, 3, 4]);
+		assert_eq!(m.indices_of("include").unwrap().collect::<Vec<_>>(), &[2, 5]);
+		assert_eq!(m.indices_of("option").unwrap().collect::<Vec<_>>(), &[7]);
+}
+
+#[test]
+fn indices_mult_opt_value_delim_eq() {
+    let m = App::new("myapp")
+	    .arg(Arg::with_name("option")
+		    .short("o")
+		    .takes_value(true)
+		    .use_delimiter(true)
+		    .multiple(true))
+	    .get_matches_from(vec!["myapp", "-o=val1,val2,val3"]);
+    assert_eq!(m.indices_of("option").unwrap().collect::<Vec<_>>(), &[2, 3, 4]);
+}
+
+#[test]
+fn indices_mult_opt_value_no_delim_eq() {
+    let m = App::new("myapp")
+	    .arg(Arg::with_name("option")
+		    .short("o")
+		    .takes_value(true)
+		    .multiple(true))
+	    .get_matches_from(vec!["myapp", "-o=val1,val2,val3"]);
+    assert_eq!(m.indices_of("option").unwrap().collect::<Vec<_>>(), &[2]);
+}
+
+#[test]
+fn indices_mult_opt_mult_flag() {
+    let m = App::new("myapp")
+    .arg(Arg::with_name("option")
+	    .short("o")
+	    .takes_value(true)
+	    .multiple_occurrences(true))
+    .arg(Arg::with_name("flag")
+	    .short("f")
+	    .multiple_occurrences(true))
+    .get_matches_from(vec!["myapp", "-o", "val1", "-f", "-o", "val2", "-f"]);
+
+    assert_eq!(m.indices_of("option").unwrap().collect::<Vec<_>>(), &[2, 5]);
+    assert_eq!(m.indices_of("flag").unwrap().collect::<Vec<_>>(), &[3, 6]);
+}