-
-
Notifications
You must be signed in to change notification settings - Fork 524
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
feat(search-output-formatter): initialize search output formatter #3258
Changes from 11 commits
4497e61
c178fa7
ce27943
0e6202e
47e9c21
f6363d1
e85c34d
b3c213c
edeaf3a
4eed44d
e603148
2280fb5
7bf0e80
da5dc0e
bfd5ec6
dc1e19c
b0f9948
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 |
---|---|---|
@@ -1,7 +1,7 @@ | ||
use crate::execute::diagnostics::ResultExt; | ||
use crate::execute::diagnostics::{ResultExt, SearchDiagnostic}; | ||
use crate::execute::process_file::workspace_file::WorkspaceFile; | ||
use crate::execute::process_file::{FileResult, FileStatus, SharedTraversalOptions}; | ||
use biome_diagnostics::category; | ||
use crate::execute::process_file::{FileResult, FileStatus, Message, SharedTraversalOptions}; | ||
use biome_diagnostics::{category, DiagnosticExt, Severity}; | ||
use biome_service::workspace::PatternId; | ||
use std::path::Path; | ||
|
||
|
@@ -21,16 +21,33 @@ pub(crate) fn search_with_guard<'ctx>( | |
) -> FileResult { | ||
tracing::info_span!("Processes searching", path =? workspace_file.path.display()).in_scope( | ||
move || { | ||
let _result = workspace_file | ||
let result = workspace_file | ||
.guard() | ||
.search_pattern(pattern) | ||
.with_file_path_and_code( | ||
workspace_file.path.display().to_string(), | ||
category!("search"), | ||
)?; | ||
|
||
let input = workspace_file.input()?; | ||
let file_name = workspace_file.path.display().to_string(); | ||
|
||
// FIXME: We need to report some real results here... | ||
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. Is the 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 guess so, since the formatting is ready for the engine that provides real results is :) 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.
Sorry for my last out-of-sync reply: I actually went ahead and removed this one, in favor of keeping the other |
||
Ok(FileStatus::Unchanged) | ||
let search_results = Message::Diagnostics { | ||
name: file_name, | ||
content: input, | ||
diagnostics: result | ||
.matches | ||
.into_iter() | ||
.map(|mat| { | ||
SearchDiagnostic | ||
.with_file_span(mat) | ||
.with_severity(Severity::Hint) | ||
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. If the severity of #[derive(Debug, Diagnostic)]
#[diagnostic(category = "search", tags(SEARCH), severity = Hint)]
struct SearchDiagnostic; 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. Maybe 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 so too! 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. Sure! I will go ahead and change the severity to |
||
}) | ||
.collect(), | ||
skipped_diagnostics: 0, | ||
}; | ||
Ok(FileStatus::Message(search_results)) | ||
}, | ||
) | ||
} |
Original file line number | Diff line number | Diff line change |
---|---|---|
|
@@ -254,6 +254,75 @@ pub(super) fn print_frame(fmt: &mut fmt::Formatter<'_>, location: Location<'_>) | |
fmt.write_str("\n") | ||
} | ||
|
||
pub(super) fn print_highlighted_frame( | ||
fmt: &mut fmt::Formatter<'_>, | ||
location: Location<'_>, | ||
) -> io::Result<()> { | ||
let Some(span) = location.span else { | ||
return Ok(()); | ||
}; | ||
let Some(source_code) = location.source_code else { | ||
return Ok(()); | ||
}; | ||
|
||
// TODO: instead of calculating lines for every match, | ||
// check if the Grit engine is able to pull them out | ||
let source = SourceFile::new(source_code); | ||
|
||
let start = source.location(span.start())?; | ||
let end = source.location(span.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. Ah, this makes me realize we need to calculate line numbers for every match, which can potentially be a bit expensive. No big deal either way, but a good reminder that I may want to see if the Grit engine can already report the line numbers for us. At least it tracks them for some purposes already (though not everywhere), so I may be able to pull those out. 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. Would you like me to add a TODO to keep track of this possibility? 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. Would be appreciated! |
||
|
||
let match_line_start = start.line_number; | ||
let match_line_end = end.line_number.saturating_add(1); | ||
|
||
for line_index in IntoIter::new(match_line_start..match_line_end) { | ||
let current_range = source.line_range(line_index.to_zero_indexed()); | ||
let current_range = match current_range { | ||
Ok(v) => v, | ||
Err(_) => continue, | ||
}; | ||
|
||
let current_text = source_code.text[current_range].trim_end_matches(['\r', '\n']); | ||
|
||
let is_first_line = line_index == start.line_number; | ||
let is_last_line = line_index == end.line_number; | ||
|
||
let start_index_relative_to_line = | ||
span.start().max(current_range.start()) - current_range.start(); | ||
let end_index_relative_to_line = | ||
span.end().min(current_range.end()) - current_range.start(); | ||
|
||
let marker = if is_first_line && is_last_line { | ||
TextRange::new(start_index_relative_to_line, end_index_relative_to_line) | ||
} else if is_last_line { | ||
let start_index: u32 = current_text.text_len().into(); | ||
|
||
let safe_start_index = | ||
start_index.saturating_sub(current_text.trim_start().text_len().into()); | ||
|
||
TextRange::new(TextSize::from(safe_start_index), end_index_relative_to_line) | ||
} else { | ||
TextRange::new(start_index_relative_to_line, current_text.text_len()) | ||
}; | ||
|
||
fmt.write_markup(markup! { | ||
<Emphasis>{format_args!("{line_index} \u{2502} ")}</Emphasis> | ||
})?; | ||
|
||
let start_range = ¤t_text[0..marker.start().into()]; | ||
let highlighted_range = ¤t_text[marker.start().into()..marker.end().into()]; | ||
let end_range = ¤t_text[marker.end().into()..current_text.text_len().into()]; | ||
|
||
write!(fmt, "{start_range}")?; | ||
fmt.write_markup(markup! { <Emphasis><Info>{highlighted_range}</Info></Emphasis> })?; | ||
write!(fmt, "{end_range}")?; | ||
|
||
writeln!(fmt)?; | ||
} | ||
|
||
Ok(()) | ||
} | ||
|
||
/// Calculate the length of the string representation of `value` | ||
pub(super) fn calculate_print_width(mut value: OneIndexed) -> NonZeroUsize { | ||
// SAFETY: Constant is being initialized with a non-zero value | ||
|
Original file line number | Diff line number | Diff line change |
---|---|---|
|
@@ -29,7 +29,7 @@ use biome_json_parser::{parse_json_with_cache, JsonParserOptions}; | |
use biome_json_syntax::JsonFileSource; | ||
use biome_parser::AnyParse; | ||
use biome_project::NodeJsProject; | ||
use biome_rowan::NodeCache; | ||
use biome_rowan::{NodeCache, TextLen, TextRange, TextSize}; | ||
use dashmap::{mapref::entry::Entry, DashMap}; | ||
use indexmap::IndexSet; | ||
use std::ffi::OsStr; | ||
|
@@ -75,6 +75,24 @@ pub(crate) struct Document { | |
node_cache: NodeCache, | ||
} | ||
|
||
// TODO: remove once an actual implementation for the matches is present | ||
struct DummySearchMatchesProvider; | ||
|
||
impl DummySearchMatchesProvider { | ||
// a match that goes from the first to the last character of the first line, if present | ||
fn get_range(input: &str) -> Vec<TextRange> { | ||
let mut lines = input.lines(); | ||
let first_line = lines.next(); | ||
|
||
let max_size = match first_line { | ||
Some(v) => v.text_len(), | ||
None => return vec![], | ||
}; | ||
|
||
vec![TextRange::new(TextSize::from(0), max_size)] | ||
} | ||
} | ||
|
||
impl WorkspaceServer { | ||
/// Create a new [Workspace] | ||
/// | ||
|
@@ -783,10 +801,22 @@ impl Workspace for WorkspaceServer { | |
} | ||
|
||
fn search_pattern(&self, params: SearchPatternParams) -> Result<SearchResults, WorkspaceError> { | ||
let SearchPatternParams { path, .. } = params; | ||
|
||
// FIXME: Let's implement some real matching here... | ||
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. Is the 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. A |
||
|
||
let document = self | ||
.documents | ||
.get_mut(&path) | ||
.ok_or_else(WorkspaceError::not_found)?; | ||
|
||
let content = document.content.as_str(); | ||
|
||
let match_ranges = DummySearchMatchesProvider::get_range(content); | ||
|
||
Ok(SearchResults { | ||
file: params.path, | ||
matches: Vec::new(), | ||
file: path, | ||
matches: match_ranges, | ||
}) | ||
} | ||
|
||
|
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.
Is there a particular reason why you added a
SEARCH
tag? Tags are meant to add additional information to the current diagnostic, and you can assign multiple tags. I'm not sure this tag fits in here, unless there's a reason I don't seeThere 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.
Just for my understanding, you think the
category
should be enough to right, and no tags would be needed here? If so, I guess that makes sense :)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.
Yes, this is the reason why I went with it:
SearchDiagnostic
should have aHint
severityDiagnosticPrinter
will deem a diagnostic skippable if its severity is below its diagnostic_level -- in our case, anything belowInformation
is deemed skippablefn should_skip_diagnostic
to print a search diagnostic despite its low severity, I figured I needed to have aSEARCH
tag attached to thediagnostic_tags
(seetraverse.rs::should_skip_diagnostic
)With your previous comments, I realized I am able to get this info out of the printer
execution
if needed: so, if there are no better ways, I could change my code to query the execution if you'd want me toThere 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.
I apologize for this confusion: after checking thoroughly, I realized this behavior was not happening the way I was imagining it.
I have now proceeded to remove the
SEARCH
tag and its associated checks altogether 👍