Modify structured suggestion output
* On suggestions that include deletions, use a diff inspired output format * When suggesting addition, use `+` as underline * Color highlight modified span
This commit is contained in:
committed by
Esteban Kuber
parent
d488de82f3
commit
99f2977031
@@ -160,32 +160,77 @@ pub struct SubstitutionPart {
|
||||
pub snippet: String,
|
||||
}
|
||||
|
||||
/// Used to translate between `Span`s and byte positions within a single output line in highlighted
|
||||
/// code of structured suggestions.
|
||||
#[derive(Debug, Clone, Copy)]
|
||||
pub struct SubstitutionHighlight {
|
||||
start: usize,
|
||||
end: usize,
|
||||
}
|
||||
|
||||
impl SubstitutionPart {
|
||||
pub fn is_addition(&self, sm: &SourceMap) -> bool {
|
||||
!self.snippet.is_empty()
|
||||
&& sm
|
||||
.span_to_snippet(self.span)
|
||||
.map_or(self.span.is_empty(), |snippet| snippet.trim().is_empty())
|
||||
}
|
||||
|
||||
pub fn is_deletion(&self) -> bool {
|
||||
self.snippet.trim().is_empty()
|
||||
}
|
||||
|
||||
pub fn is_replacement(&self, sm: &SourceMap) -> bool {
|
||||
!self.snippet.is_empty()
|
||||
&& sm
|
||||
.span_to_snippet(self.span)
|
||||
.map_or(!self.span.is_empty(), |snippet| !snippet.trim().is_empty())
|
||||
}
|
||||
}
|
||||
|
||||
impl CodeSuggestion {
|
||||
/// Returns the assembled code suggestions, whether they should be shown with an underline
|
||||
/// and whether the substitution only differs in capitalization.
|
||||
pub fn splice_lines(&self, sm: &SourceMap) -> Vec<(String, Vec<SubstitutionPart>, bool)> {
|
||||
pub fn splice_lines(
|
||||
&self,
|
||||
sm: &SourceMap,
|
||||
) -> Vec<(String, Vec<SubstitutionPart>, Vec<Vec<SubstitutionHighlight>>, bool)> {
|
||||
// For the `Vec<Vec<SubstitutionHighlight>>` value, the first level of the vector
|
||||
// corresponds to the output snippet's lines, while the second level corresponds to the
|
||||
// substrings within that line that should be highlighted.
|
||||
|
||||
use rustc_span::{CharPos, Pos};
|
||||
|
||||
/// Append to a buffer the remainder of the line of existing source code, and return the
|
||||
/// count of lines that have been added for accurate highlighting.
|
||||
fn push_trailing(
|
||||
buf: &mut String,
|
||||
line_opt: Option<&Cow<'_, str>>,
|
||||
lo: &Loc,
|
||||
hi_opt: Option<&Loc>,
|
||||
) {
|
||||
) -> usize {
|
||||
let mut line_count = 0;
|
||||
let (lo, hi_opt) = (lo.col.to_usize(), hi_opt.map(|hi| hi.col.to_usize()));
|
||||
if let Some(line) = line_opt {
|
||||
if let Some(lo) = line.char_indices().map(|(i, _)| i).nth(lo) {
|
||||
let hi_opt = hi_opt.and_then(|hi| line.char_indices().map(|(i, _)| i).nth(hi));
|
||||
match hi_opt {
|
||||
Some(hi) if hi > lo => buf.push_str(&line[lo..hi]),
|
||||
Some(hi) if hi > lo => {
|
||||
line_count = line[lo..hi].matches('\n').count();
|
||||
buf.push_str(&line[lo..hi])
|
||||
}
|
||||
Some(_) => (),
|
||||
None => buf.push_str(&line[lo..]),
|
||||
None => {
|
||||
line_count = line[lo..].matches('\n').count();
|
||||
buf.push_str(&line[lo..])
|
||||
}
|
||||
}
|
||||
}
|
||||
if hi_opt.is_none() {
|
||||
buf.push('\n');
|
||||
}
|
||||
}
|
||||
line_count
|
||||
}
|
||||
|
||||
assert!(!self.substitutions.is_empty());
|
||||
@@ -220,6 +265,7 @@ impl CodeSuggestion {
|
||||
return None;
|
||||
}
|
||||
|
||||
let mut highlights = vec![];
|
||||
// To build up the result, we do this for each span:
|
||||
// - push the line segment trailing the previous span
|
||||
// (at the beginning a "phantom" span pointing at the start of the line)
|
||||
@@ -236,17 +282,29 @@ impl CodeSuggestion {
|
||||
lines.lines.get(0).and_then(|line0| sf.get_line(line0.line_index));
|
||||
let mut buf = String::new();
|
||||
|
||||
let mut line_highlight = vec![];
|
||||
for part in &substitution.parts {
|
||||
let cur_lo = sm.lookup_char_pos(part.span.lo());
|
||||
if prev_hi.line == cur_lo.line {
|
||||
push_trailing(&mut buf, prev_line.as_ref(), &prev_hi, Some(&cur_lo));
|
||||
let mut count =
|
||||
push_trailing(&mut buf, prev_line.as_ref(), &prev_hi, Some(&cur_lo));
|
||||
while count > 0 {
|
||||
highlights.push(std::mem::take(&mut line_highlight));
|
||||
count -= 1;
|
||||
}
|
||||
} else {
|
||||
push_trailing(&mut buf, prev_line.as_ref(), &prev_hi, None);
|
||||
highlights.push(std::mem::take(&mut line_highlight));
|
||||
let mut count = push_trailing(&mut buf, prev_line.as_ref(), &prev_hi, None);
|
||||
while count > 0 {
|
||||
highlights.push(std::mem::take(&mut line_highlight));
|
||||
count -= 1;
|
||||
}
|
||||
// push lines between the previous and current span (if any)
|
||||
for idx in prev_hi.line..(cur_lo.line - 1) {
|
||||
if let Some(line) = sf.get_line(idx) {
|
||||
buf.push_str(line.as_ref());
|
||||
buf.push('\n');
|
||||
highlights.push(std::mem::take(&mut line_highlight));
|
||||
}
|
||||
}
|
||||
if let Some(cur_line) = sf.get_line(cur_lo.line - 1) {
|
||||
@@ -257,10 +315,21 @@ impl CodeSuggestion {
|
||||
buf.push_str(&cur_line[..end]);
|
||||
}
|
||||
}
|
||||
// Add a whole line highlight per line in the snippet.
|
||||
line_highlight.push(SubstitutionHighlight {
|
||||
start: cur_lo.col.0,
|
||||
end: cur_lo.col.0
|
||||
+ part.snippet.split('\n').next().unwrap_or(&part.snippet).len(),
|
||||
});
|
||||
for line in part.snippet.split('\n').skip(1) {
|
||||
highlights.push(std::mem::take(&mut line_highlight));
|
||||
line_highlight.push(SubstitutionHighlight { start: 0, end: line.len() });
|
||||
}
|
||||
buf.push_str(&part.snippet);
|
||||
prev_hi = sm.lookup_char_pos(part.span.hi());
|
||||
prev_line = sf.get_line(prev_hi.line - 1);
|
||||
}
|
||||
highlights.push(std::mem::take(&mut line_highlight));
|
||||
let only_capitalization = is_case_difference(sm, &buf, bounding_span);
|
||||
// if the replacement already ends with a newline, don't print the next line
|
||||
if !buf.ends_with('\n') {
|
||||
@@ -270,7 +339,7 @@ impl CodeSuggestion {
|
||||
while buf.ends_with('\n') {
|
||||
buf.pop();
|
||||
}
|
||||
Some((buf, substitution.parts, only_capitalization))
|
||||
Some((buf, substitution.parts, highlights, only_capitalization))
|
||||
})
|
||||
.collect()
|
||||
}
|
||||
|
||||
Reference in New Issue
Block a user