-
-
Notifications
You must be signed in to change notification settings - Fork 35
New issue
Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.
By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.
Already on GitHub? Sign in to your account
cmark files and multiline fragments #74
Changes from 15 commits
2e727e5
aef15df
e535f38
1e548ab
0a8715a
bdcb4b9
dd594af
d1c1eb5
abc591d
74ddae5
e4b9115
110f81b
15ae31e
b8012a3
27c8c0f
c5dd04c
ca1a9d8
147dcdd
28bf82e
9194444
736554f
f27ea72
bfc516c
c974458
a6b6a71
01a3387
51b1360
22e304f
342eb39
ca8d386
File filter
Filter by extension
Conversations
Jump to
Diff view
Diff view
There are no files selected for viewing
Original file line number | Diff line number | Diff line change |
---|---|---|
|
@@ -384,7 +384,7 @@ impl UserPicked { | |
} | ||
|
||
loop { | ||
let mut guard = ScopedRaw::new(); | ||
let guard = ScopedRaw::new(); | ||
|
||
self.print_replacements_list(state)?; | ||
|
||
|
@@ -403,11 +403,14 @@ impl UserPicked { | |
} | ||
|
||
let event = match crossterm::event::read() | ||
.map(move |event| { | ||
drop(guard); | ||
event | ||
}) | ||
.map_err(|e| anyhow::anyhow!("Something unexpected happened on the CLI: {}", e))? | ||
{ | ||
Event::Key(event) => event, | ||
Event::Resize(..) => { | ||
drop(guard); | ||
continue; | ||
} | ||
sth => { | ||
|
@@ -417,9 +420,8 @@ impl UserPicked { | |
}; | ||
|
||
if state.is_custom_entry() { | ||
drop(guard); | ||
info!("Custom entry mode"); | ||
guard = ScopedRaw::new(); | ||
let _guard = ScopedRaw::new(); | ||
|
||
let pick = self.enter_custom_replacement(state, event)?; | ||
|
||
|
@@ -435,7 +437,6 @@ impl UserPicked { | |
} | ||
} | ||
|
||
drop(guard); | ||
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. That There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. I am not sure if the current approach is sane, there seem to be a few artificats in the cli, cursor showing and lines being off by one, just because we leave raw mode, so this might have to be reverted eventually. |
||
// print normally again | ||
trace!("registered event: {:?}", &event); | ||
|
||
|
Original file line number | Diff line number | Diff line change |
---|---|---|
|
@@ -107,80 +107,85 @@ impl CheckableChunk { | |
&range | ||
); | ||
|
||
let Range { start, end } = range.clone(); | ||
let mut active = false; | ||
let Range { start, end } = range; | ||
self.source_mapping | ||
.iter() | ||
.skip_while(|(sub, _span)| sub.end <= start) | ||
.take_while(|(sub, _span)| end <= sub.end) | ||
.skip_while(|(fragment_range, _span)| fragment_range.end <= start) | ||
.take_while(|(fragment_range, _span)| end <= fragment_range.end) | ||
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. I like the new variable names 👍 There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. what does There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. I think it's best explained in the module docs https://github.com/drahnr/cargo-spellcheck/pull/74/files/27c8c0f661e02c310dfa14a9d6475b73f1312cd2#diff-5fe541b5263f70cb56752ad21183cdddR1-R13 if not that must be expanded. |
||
.inspect(|x| { | ||
trace!(">>> item {:?} ∈ {:?}", &range, x.0); | ||
}) | ||
.filter(|(sub, _)| { | ||
.filter(|(fragment_range, _)| { | ||
// could possibly happen on empty documentation lines with `///` | ||
sub.len() > 0 | ||
fragment_range.len() > 0 | ||
}) | ||
.filter_map(|(sub, span)| { | ||
if span.start.line == span.end.line { | ||
debug_assert!(span.start.column <= span.end.column); | ||
if span.start.column > span.end.column { | ||
return None; | ||
} | ||
.filter_map(|(fragment_range, fragment_span)| { | ||
// trim range so we only capture the relevant part | ||
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. what is considered an irrelevant part? There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. It's not about irrelevant, it's a coverage problem of the given |
||
let sub_fragment_range = std::cmp::max(fragment_range.start, range.start) | ||
..std::cmp::min(fragment_range.end, range.end); | ||
trace!( | ||
">> fragment: span:{:?} => range:{:?} | sub:{:?} -> sub_fragment{:?}", | ||
&fragment_span, | ||
&fragment_range, | ||
range, | ||
&sub_fragment_range, | ||
); | ||
|
||
if sub_fragment_range.len() == 0 { | ||
log::debug!("sub fragment is zero, dropping!"); | ||
return None; | ||
} | ||
|
||
// assured by filter | ||
assert!(end > 0); | ||
|
||
if sub.contains(&start) { | ||
let offset = start - sub.start; | ||
if sub.contains(&(end - 1)) { | ||
// complete start to end | ||
active = false; | ||
Some((start..end, offset)) | ||
} else { | ||
// only start, continue taking until end | ||
active = true; | ||
Some((start..sub.end, offset)) | ||
if fragment_span.start.line == fragment_span.end.line { | ||
assert!(fragment_span.start.column <= fragment_span.end.column); | ||
assert_eq!( | ||
fragment_span.end.column + 1 - fragment_span.start.column, | ||
fragment_range.len() | ||
); | ||
} | ||
// take the full fragment string, we need to count newlines before and after | ||
let s = &self.as_str()[fragment_range.clone()]; | ||
// relative to the range given / offset | ||
let shift = sub_fragment_range.start - fragment_range.start; | ||
let mut sub_fragment_span = fragment_span.clone(); | ||
let state: LineColumn = fragment_span.start; | ||
for (idx, c, cursor) in s.chars().enumerate().scan(state, |state, (idx, c)| { | ||
let x: (usize, char, LineColumn) = (idx, c, state.clone()); | ||
drahnr marked this conversation as resolved.
Show resolved
Hide resolved
|
||
match c { | ||
'\n' => { | ||
state.line += 1; | ||
state.column = 0; | ||
} | ||
_ => state.column += 1, | ||
} | ||
Some(x) | ||
}) { | ||
trace!("char[{}]: {}", idx, c); | ||
if idx == shift { | ||
sub_fragment_span.start = cursor; | ||
} | ||
} else if active { | ||
if sub.contains(&(end - 1)) { | ||
active = false; | ||
Some((sub.start..end, 0usize)) // @todo double check this | ||
} else { | ||
Some((sub.clone(), 0usize)) | ||
sub_fragment_span.end = cursor; // always set, even if we never reach the end of fragment | ||
if idx >= (sub_fragment_range.len() + shift - 1) { | ||
break; | ||
} | ||
} else { | ||
None | ||
} | ||
.map(|(fragment_range, offset)| { | ||
// @todo handle multiline here | ||
// @todo requires knowledge of how many items are remaining in the line | ||
// @todo which needs to be extracted from chunk | ||
assert_eq!(span.start.line, span.end.line); | ||
// |<--------range----------->| | ||
// |<-d1->|<-fragment->|<-.+->| | ||
let d1 = fragment_range | ||
.start | ||
.checked_sub(start) | ||
.expect("d1 must be positive"); | ||
|
||
assert!(range.end >= fragment_range.end); | ||
|
||
trace!( | ||
">> offset={} fragment={:?} range={:?}", | ||
offset, | ||
&fragment_range, | ||
&range | ||
|
||
// let _ = dbg!(&sub_fragment_span); | ||
// let _ = dbg!(&sub_fragment_range); | ||
drahnr marked this conversation as resolved.
Show resolved
Hide resolved
|
||
if sub_fragment_span.start.line == sub_fragment_span.end.line { | ||
assert!(sub_fragment_span.start.column <= sub_fragment_span.end.column); | ||
assert_eq!( | ||
sub_fragment_span.end.column + 1 - sub_fragment_span.start.column, | ||
sub_fragment_range.len() | ||
); | ||
trace!(">> {:?}", &span); | ||
// @todo count line wraps | ||
let mut span = span.clone(); | ||
span.start.column += offset + d1; | ||
span.end.column = span.start.column + fragment_range.len() - 1; | ||
assert!(span.start.column <= span.end.column); | ||
|
||
(fragment_range, span) | ||
}) | ||
} | ||
log::warn!( | ||
drahnr marked this conversation as resolved.
Show resolved
Hide resolved
drahnr marked this conversation as resolved.
Show resolved
Hide resolved
|
||
">> sub_fragment range={:?} span={:?} => {}", | ||
&sub_fragment_range, | ||
&sub_fragment_span, | ||
self.display(sub_fragment_range.clone()), | ||
); | ||
|
||
Some((sub_fragment_range, sub_fragment_span)) | ||
}) | ||
.collect::<IndexMap<_, _>>() | ||
} | ||
|
@@ -196,6 +201,10 @@ impl CheckableChunk { | |
pub fn iter(&self) -> indexmap::map::Iter<Range, Span> { | ||
self.source_mapping.iter() | ||
} | ||
|
||
pub fn fragment_count(&self) -> usize { | ||
self.source_mapping.len() | ||
} | ||
} | ||
|
||
/// Convert the clusters of one file into a source description as well | ||
|
@@ -210,6 +219,23 @@ impl From<Clusters> for Vec<CheckableChunk> { | |
} | ||
} | ||
|
||
/// Extract lines together with associated `Range`s relative to str `s` | ||
/// | ||
/// Easily collectable into a `HashMap`. | ||
fn lines_with_ranges<'a>(s: &'a str) -> impl Iterator<Item = (Range, &'a str)> + Clone { | ||
// @todo line consumes \r\n and \n so the ranges could off by 1 on windows | ||
drahnr marked this conversation as resolved.
Show resolved
Hide resolved
|
||
// @todo requires a custom impl of `lines()` iterator | ||
s.lines().scan( | ||
0usize, | ||
|offset: &mut usize, line: &'_ str| -> Option<(Range, &'_ str)> { | ||
let n = line.chars().count(); | ||
let range = *offset..*offset + n + 1; | ||
*offset += n + 1; // for newline, see @todo above | ||
Some((range, line)) | ||
}, | ||
) | ||
} | ||
|
||
use std::fmt; | ||
|
||
/// A display style wrapper for a trimmed literal. | ||
|
@@ -240,8 +266,6 @@ where | |
type Error = Error; | ||
fn try_from(tuple: (R, Span)) -> Result<Self> { | ||
let chunk = tuple.0.into(); | ||
let _first = chunk.source_mapping.iter().next().unwrap().1; // @todo | ||
let _last = chunk.source_mapping.iter().rev().next().unwrap().1; // @todo | ||
let span = tuple.1; | ||
let range = span.to_content_range(chunk)?; | ||
Ok(Self(chunk, range)) | ||
|
@@ -309,6 +333,8 @@ mod test { | |
|
||
#[test] | ||
fn find_spans_simple() { | ||
let _ = env_logger::builder().is_test(true).try_init(); | ||
|
||
// generate `///<space>...` | ||
const SOURCE: &'static str = fluff_up!(["xyz"]); | ||
let set = gen_literal_set(SOURCE); | ||
|
@@ -344,6 +370,8 @@ mod test { | |
|
||
#[test] | ||
fn find_spans_multiline() { | ||
let _ = env_logger::builder().is_test(true).try_init(); | ||
|
||
const SOURCE: &'static str = fluff_up!(["xyz", "second", "third", "fourth"]); | ||
let set = gen_literal_set(SOURCE); | ||
let chunk = dbg!(CheckableChunk::from_literalset(set)); | ||
|
@@ -380,4 +408,53 @@ mod test { | |
assert_eq!(span, expected_span); | ||
} | ||
} | ||
|
||
#[test] | ||
fn find_spans_chyrp() { | ||
let _ = env_logger::builder().is_test(true).try_init(); | ||
|
||
const SOURCE: &'static str = chyrp_up!(["Amsel", "Wacholderdrossel", "Buchfink"]); | ||
let set = gen_literal_set(SOURCE); | ||
let chunk = dbg!(CheckableChunk::from_literalset(set)); | ||
const CHUNK_RANGES: &[Range] = &[0..(5 + 1 + 16 + 1 + 8)]; | ||
const EXPECTED_SPANS: &[Span] = &[Span { | ||
start: LineColumn { | ||
line: 1, | ||
column: 0 + 9, | ||
}, // prefix is #[doc=r#" | ||
end: LineColumn { line: 3, column: 7 }, // suffix is pointeless | ||
}]; | ||
|
||
assert_eq!( | ||
dbg!(&EXPECTED_SPANS[0] | ||
.to_content_range(&chunk) | ||
.expect("Must be ok to extract span from chunk")), | ||
dbg!(&CHUNK_RANGES[0]) | ||
); | ||
|
||
const EXPECTED_STR: &[&'static str] = &[r#"Amsel | ||
Wacholderdrossel | ||
Buchfink"#]; | ||
|
||
assert_eq!(EXPECTED_STR[0], chunk.as_str()); | ||
|
||
for (query_range, expected_span, expected_str) in itertools::cons_tuples( | ||
CHUNK_RANGES | ||
.iter() | ||
.zip(EXPECTED_SPANS.iter()) | ||
.zip(EXPECTED_STR.iter()), | ||
) { | ||
let range2span = chunk.find_spans(query_range.clone()); | ||
// test deals only with a single line, so we know it only is a single entry | ||
assert_eq!(range2span.len(), 1); | ||
let (range, span) = dbg!(range2span.iter().next().unwrap()); | ||
assert!(query_range.contains(&(range.start))); | ||
assert!(query_range.contains(&(range.end - 1))); | ||
assert_eq!( | ||
load_span_from(SOURCE.as_bytes(), *span).expect("Span extraction must work"), | ||
expected_str.to_owned() | ||
); | ||
assert_eq!(span, expected_span); | ||
} | ||
} | ||
} |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
👍