comparison contrib/merge-lists/src/main.rs @ 49011:b999edb15f8c

merge-lists: make it possible to specify pattern to match The `merge-lists` tool doesn't know anything about Python other than its regex that attempts to match import lines. Let's make it possible to pass in a custom regex so it's easy to use the tool for e.g. C/C++ `#include` lines or Rust `use` lines (given the limited). Differential Revision: https://phab.mercurial-scm.org/D12392
author Martin von Zweigbergk <martinvonz@google.com>
date Fri, 18 Mar 2022 12:23:47 -0700
parents 681b25ea579e
children
comparison
equal deleted inserted replaced
49010:681b25ea579e 49011:b999edb15f8c
1 use clap::Parser; 1 use clap::{ArgGroup, Parser};
2 use itertools::Itertools; 2 use itertools::Itertools;
3 use regex::bytes::Regex; 3 use regex::bytes::Regex;
4 use similar::ChangeTag; 4 use similar::ChangeTag;
5 use std::cmp::{max, min, Ordering}; 5 use std::cmp::{max, min, Ordering};
6 use std::collections::HashSet; 6 use std::collections::HashSet;
148 /// A tool that performs a 3-way merge, resolving conflicts in sorted lists and 148 /// A tool that performs a 3-way merge, resolving conflicts in sorted lists and
149 /// leaving other conflicts unchanged. This is useful with Mercurial's support 149 /// leaving other conflicts unchanged. This is useful with Mercurial's support
150 /// for partial merge tools (configured in `[partial-merge-tools]`). 150 /// for partial merge tools (configured in `[partial-merge-tools]`).
151 #[derive(Parser, Debug)] 151 #[derive(Parser, Debug)]
152 #[clap(version, about, long_about = None)] 152 #[clap(version, about, long_about = None)]
153 #[clap(group(ArgGroup::new("match").required(true).args(&["pattern", "python-imports"])))]
153 struct Args { 154 struct Args {
154 /// Path to the file's content in the "local" side 155 /// Path to the file's content in the "local" side
155 local: OsString, 156 local: OsString,
156 157
157 /// Path to the file's content in the base 158 /// Path to the file's content in the base
158 base: OsString, 159 base: OsString,
159 160
160 /// Path to the file's content in the "other" side 161 /// Path to the file's content in the "other" side
161 other: OsString, 162 other: OsString,
163
164 /// Regular expression to use
165 #[clap(long, short)]
166 pattern: Option<String>,
167
168 /// Use built-in regular expression for Python imports
169 #[clap(long)]
170 python_imports: bool,
171 }
172
173 fn get_regex(args: &Args) -> Regex {
174 let pattern = if args.python_imports {
175 r"import \w+(\.\w+)*( +#.*)?\n|from (\w+(\.\w+)* import \w+( as \w+)?(, \w+( as \w+)?)*( +#.*)?)"
176 } else if let Some(pattern) = &args.pattern {
177 pattern
178 } else {
179 ".*"
180 };
181 let pattern = format!(r"{}\r?\n?", pattern);
182 regex::bytes::Regex::new(&pattern).unwrap()
162 } 183 }
163 184
164 fn main() { 185 fn main() {
165 let args: Args = Args::parse(); 186 let args: Args = Args::parse();
166 187
170 191
171 let base_bytes = std::fs::read(&base_path).unwrap(); 192 let base_bytes = std::fs::read(&base_path).unwrap();
172 let local_bytes = std::fs::read(&local_path).unwrap(); 193 let local_bytes = std::fs::read(&local_path).unwrap();
173 let other_bytes = std::fs::read(&other_path).unwrap(); 194 let other_bytes = std::fs::read(&other_path).unwrap();
174 195
175 let regex = 196 let regex = get_regex(&args);
176 regex::bytes::Regex::new(r"import \w+(\.\w+)*( +#.*)?\n|from (\w+(\.\w+)* import \w+( as \w+)?(, \w+( as \w+)?)*( +#.*)?)\r?\n?").unwrap();
177 let (new_base_bytes, new_local_bytes, new_other_bytes) = 197 let (new_base_bytes, new_local_bytes, new_other_bytes) =
178 resolve(&base_bytes, &local_bytes, &other_bytes, &regex); 198 resolve(&base_bytes, &local_bytes, &other_bytes, &regex);
179 199
180 // Write out the result if anything changed 200 // Write out the result if anything changed
181 if new_base_bytes != base_bytes { 201 if new_base_bytes != base_bytes {