Show More
@@ -419,6 +419,59 b' impl Config {' | |||||
419 | .any(|layer| layer.has_non_empty_section(section)) |
|
419 | .any(|layer| layer.has_non_empty_section(section)) | |
420 | } |
|
420 | } | |
421 |
|
421 | |||
|
422 | /// Yields (key, value) pairs for everything in the given section | |||
|
423 | pub fn iter_section<'a>( | |||
|
424 | &'a self, | |||
|
425 | section: &'a [u8], | |||
|
426 | ) -> impl Iterator<Item = (&[u8], &[u8])> + 'a { | |||
|
427 | // TODO: Use `Iterator`’s `.peekable()` when its `peek_mut` is | |||
|
428 | // available: | |||
|
429 | // https://doc.rust-lang.org/nightly/std/iter/struct.Peekable.html#method.peek_mut | |||
|
430 | struct Peekable<I: Iterator> { | |||
|
431 | iter: I, | |||
|
432 | /// Remember a peeked value, even if it was None. | |||
|
433 | peeked: Option<Option<I::Item>>, | |||
|
434 | } | |||
|
435 | ||||
|
436 | impl<I: Iterator> Peekable<I> { | |||
|
437 | fn new(iter: I) -> Self { | |||
|
438 | Self { iter, peeked: None } | |||
|
439 | } | |||
|
440 | ||||
|
441 | fn next(&mut self) { | |||
|
442 | self.peeked = None | |||
|
443 | } | |||
|
444 | ||||
|
445 | fn peek_mut(&mut self) -> Option<&mut I::Item> { | |||
|
446 | let iter = &mut self.iter; | |||
|
447 | self.peeked.get_or_insert_with(|| iter.next()).as_mut() | |||
|
448 | } | |||
|
449 | } | |||
|
450 | ||||
|
451 | // Deduplicate keys redefined in multiple layers | |||
|
452 | let mut keys_already_seen = HashSet::new(); | |||
|
453 | let mut key_is_new = | |||
|
454 | move |&(key, _value): &(&'a [u8], &'a [u8])| -> bool { | |||
|
455 | keys_already_seen.insert(key) | |||
|
456 | }; | |||
|
457 | // This is similar to `flat_map` + `filter_map`, except with a single | |||
|
458 | // closure that owns `key_is_new` (and therefore the | |||
|
459 | // `keys_already_seen` set): | |||
|
460 | let mut layer_iters = Peekable::new( | |||
|
461 | self.layers | |||
|
462 | .iter() | |||
|
463 | .rev() | |||
|
464 | .map(move |layer| layer.iter_section(section)), | |||
|
465 | ); | |||
|
466 | std::iter::from_fn(move || loop { | |||
|
467 | if let Some(pair) = layer_iters.peek_mut()?.find(&mut key_is_new) { | |||
|
468 | return Some(pair); | |||
|
469 | } else { | |||
|
470 | layer_iters.next(); | |||
|
471 | } | |||
|
472 | }) | |||
|
473 | } | |||
|
474 | ||||
422 | /// Get raw values bytes from all layers (even untrusted ones) in order |
|
475 | /// Get raw values bytes from all layers (even untrusted ones) in order | |
423 | /// of precedence. |
|
476 | /// of precedence. | |
424 | #[cfg(test)] |
|
477 | #[cfg(test)] |
@@ -127,6 +127,17 b' impl ConfigLayer {' | |||||
127 | .flat_map(|section| section.keys().map(|vec| &**vec)) |
|
127 | .flat_map(|section| section.keys().map(|vec| &**vec)) | |
128 | } |
|
128 | } | |
129 |
|
129 | |||
|
130 | /// Returns the (key, value) pairs defined in the given section | |||
|
131 | pub fn iter_section<'layer>( | |||
|
132 | &'layer self, | |||
|
133 | section: &[u8], | |||
|
134 | ) -> impl Iterator<Item = (&'layer [u8], &'layer [u8])> { | |||
|
135 | self.sections | |||
|
136 | .get(section) | |||
|
137 | .into_iter() | |||
|
138 | .flat_map(|section| section.iter().map(|(k, v)| (&**k, &*v.bytes))) | |||
|
139 | } | |||
|
140 | ||||
130 | /// Returns whether any key is defined in the given section |
|
141 | /// Returns whether any key is defined in the given section | |
131 | pub fn has_non_empty_section(&self, section: &[u8]) -> bool { |
|
142 | pub fn has_non_empty_section(&self, section: &[u8]) -> bool { | |
132 | self.sections |
|
143 | self.sections |
@@ -21,10 +21,12 b' use hg::manifest::Manifest;' | |||||
21 | use hg::matchers::AlwaysMatcher; |
|
21 | use hg::matchers::AlwaysMatcher; | |
22 | use hg::repo::Repo; |
|
22 | use hg::repo::Repo; | |
23 | use hg::utils::files::get_bytes_from_os_string; |
|
23 | use hg::utils::files::get_bytes_from_os_string; | |
|
24 | use hg::utils::files::get_path_from_bytes; | |||
24 | use hg::utils::hg_path::{hg_path_to_path_buf, HgPath}; |
|
25 | use hg::utils::hg_path::{hg_path_to_path_buf, HgPath}; | |
25 | use hg::{HgPathCow, StatusOptions}; |
|
26 | use hg::{HgPathCow, StatusOptions}; | |
26 | use log::{info, warn}; |
|
27 | use log::{info, warn}; | |
27 | use std::io; |
|
28 | use std::io; | |
|
29 | use std::path::PathBuf; | |||
28 |
|
30 | |||
29 | pub const HELP_TEXT: &str = " |
|
31 | pub const HELP_TEXT: &str = " | |
30 | Show changed files in the working directory |
|
32 | Show changed files in the working directory | |
@@ -213,11 +215,10 b' pub fn run(invocation: &crate::CliInvoca' | |||||
213 | list_ignored: display_states.ignored, |
|
215 | list_ignored: display_states.ignored, | |
214 | collect_traversed_dirs: false, |
|
216 | collect_traversed_dirs: false, | |
215 | }; |
|
217 | }; | |
216 | let ignore_file = repo.working_directory_vfs().join(".hgignore"); // TODO hardcoded |
|
|||
217 | let (mut ds_status, pattern_warnings) = dmap.status( |
|
218 | let (mut ds_status, pattern_warnings) = dmap.status( | |
218 | &AlwaysMatcher, |
|
219 | &AlwaysMatcher, | |
219 | repo.working_directory_path().to_owned(), |
|
220 | repo.working_directory_path().to_owned(), | |
220 |
|
|
221 | ignore_files(repo, config), | |
221 | options, |
|
222 | options, | |
222 | )?; |
|
223 | )?; | |
223 | if !pattern_warnings.is_empty() { |
|
224 | if !pattern_warnings.is_empty() { | |
@@ -396,6 +397,25 b' pub fn run(invocation: &crate::CliInvoca' | |||||
396 | Ok(()) |
|
397 | Ok(()) | |
397 | } |
|
398 | } | |
398 |
|
399 | |||
|
400 | fn ignore_files(repo: &Repo, config: &Config) -> Vec<PathBuf> { | |||
|
401 | let mut ignore_files = Vec::new(); | |||
|
402 | let repo_ignore = repo.working_directory_vfs().join(".hgignore"); | |||
|
403 | if repo_ignore.exists() { | |||
|
404 | ignore_files.push(repo_ignore) | |||
|
405 | } | |||
|
406 | for (key, value) in config.iter_section(b"ui") { | |||
|
407 | if key == b"ignore" || key.starts_with(b"ignore.") { | |||
|
408 | let path = get_path_from_bytes(value); | |||
|
409 | // TODO: expand "~/" and environment variable here, like Python | |||
|
410 | // does with `os.path.expanduser` and `os.path.expandvars` | |||
|
411 | ||||
|
412 | let joined = repo.working_directory_path().join(path); | |||
|
413 | ignore_files.push(joined); | |||
|
414 | } | |||
|
415 | } | |||
|
416 | ignore_files | |||
|
417 | } | |||
|
418 | ||||
399 | // Probably more elegant to use a Deref or Borrow trait rather than |
|
419 | // Probably more elegant to use a Deref or Borrow trait rather than | |
400 | // harcode HgPathBuf, but probably not really useful at this point |
|
420 | // harcode HgPathBuf, but probably not really useful at this point | |
401 | fn display_status_paths( |
|
421 | fn display_status_paths( |
General Comments 0
You need to be logged in to leave comments.
Login now