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, ®ex); |
198 resolve(&base_bytes, &local_bytes, &other_bytes, ®ex); |
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 { |