Skip to content

Commit

Permalink
feat(search-output-formatter): initialize search output formatter (#3258
Browse files Browse the repository at this point in the history
)
  • Loading branch information
BackupMiles authored Jun 25, 2024
1 parent 519e316 commit 50c880e
Show file tree
Hide file tree
Showing 7 changed files with 170 additions and 19 deletions.
4 changes: 4 additions & 0 deletions crates/biome_cli/src/execute/diagnostics.rs
Original file line number Diff line number Diff line change
Expand Up @@ -103,6 +103,10 @@ pub(crate) struct UnhandledDiagnostic;
#[diagnostic(category = "parse", message = "Skipped file with syntax errors")]
pub(crate) struct SkippedDiagnostic;

#[derive(Debug, Diagnostic)]
#[diagnostic(category = "search", severity = Information)]
pub(crate) struct SearchDiagnostic;

/// Extension trait for turning [Display]-able error types into [TraversalError]
pub(crate) trait ResultExt {
type Result;
Expand Down
4 changes: 4 additions & 0 deletions crates/biome_cli/src/execute/mod.rs
Original file line number Diff line number Diff line change
Expand Up @@ -312,6 +312,10 @@ impl Execution {
matches!(self.traversal_mode, TraversalMode::CI { .. })
}

pub(crate) const fn is_search(&self) -> bool {
matches!(self.traversal_mode, TraversalMode::Search { .. })
}

pub(crate) const fn is_check(&self) -> bool {
matches!(self.traversal_mode, TraversalMode::Check { .. })
}
Expand Down
24 changes: 18 additions & 6 deletions crates/biome_cli/src/execute/process_file/search.rs
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};
use biome_service::workspace::PatternId;
use std::path::Path;

Expand All @@ -21,16 +21,28 @@ 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"),
)?;

// FIXME: We need to report some real results here...
Ok(FileStatus::Unchanged)
let input = workspace_file.input()?;
let file_name = workspace_file.path.display().to_string();

let search_results = Message::Diagnostics {
name: file_name,
content: input,
diagnostics: result
.matches
.into_iter()
.map(|mat| SearchDiagnostic.with_file_span(mat))
.collect(),
skipped_diagnostics: 0,
};
Ok(FileStatus::Message(search_results))
},
)
}
21 changes: 15 additions & 6 deletions crates/biome_cli/src/reporter/terminal.rs
Original file line number Diff line number Diff line change
Expand Up @@ -51,10 +51,15 @@ impl<'a> ReporterVisitor for ConsoleReporterVisitor<'a> {

fn report_diagnostics(
&mut self,
_execution: &Execution,
execution: &Execution,
diagnostics_payload: DiagnosticsPayload,
) -> io::Result<()> {
for diagnostic in &diagnostics_payload.diagnostics {
if execution.is_search() {
self.0.log(markup! {{PrintDiagnostic::search(diagnostic)}});
continue;
}

if diagnostic.severity() >= diagnostics_payload.diagnostic_level {
if diagnostic.tags().is_verbose() && diagnostics_payload.verbose {
self.0
Expand Down Expand Up @@ -83,13 +88,17 @@ impl fmt::Display for Files {
}
}

struct SummaryDetail(usize);
struct SummaryDetail<'a>(pub(crate) &'a TraversalMode, usize);

impl fmt::Display for SummaryDetail {
impl<'a> fmt::Display for SummaryDetail<'a> {
fn fmt(&self, fmt: &mut Formatter) -> io::Result<()> {
if self.0 > 0 {
if let TraversalMode::Search { .. } = self.0 {
return Ok(());
}

if self.1 > 0 {
fmt.write_markup(markup! {
" Fixed "{Files(self.0)}"."
" Fixed "{Files(self.1)}"."
})
} else {
fmt.write_markup(markup! {
Expand Down Expand Up @@ -147,7 +156,7 @@ pub(crate) struct ConsoleTraversalSummary<'a>(
impl<'a> fmt::Display for ConsoleTraversalSummary<'a> {
fn fmt(&self, fmt: &mut Formatter) -> io::Result<()> {
let summary = SummaryTotal(self.0, self.1.changed + self.1.unchanged, &self.1.duration);
let detail = SummaryDetail(self.1.changed);
let detail = SummaryDetail(self.0, self.1.changed);
fmt.write_markup(markup!(<Info>{summary}{detail}</Info>))?;

if self.1.errors > 0 {
Expand Down
30 changes: 27 additions & 3 deletions crates/biome_diagnostics/src/display.rs
Original file line number Diff line number Diff line change
Expand Up @@ -37,20 +37,31 @@ impl<D: AsDiagnostic + ?Sized> std::fmt::Display for PrintDescription<'_, D> {
pub struct PrintDiagnostic<'fmt, D: ?Sized> {
diag: &'fmt D,
verbose: bool,
search: bool,
}

impl<'fmt, D: AsDiagnostic + ?Sized> PrintDiagnostic<'fmt, D> {
pub fn simple(diag: &'fmt D) -> Self {
Self {
diag,
verbose: false,
search: false,
}
}

pub fn verbose(diag: &'fmt D) -> Self {
Self {
diag,
verbose: true,
search: false,
}
}

pub fn search(diag: &'fmt D) -> Self {
Self {
diag,
verbose: false,
search: true,
}
}
}
Expand All @@ -67,14 +78,19 @@ impl<D: AsDiagnostic + ?Sized> fmt::Display for PrintDiagnostic<'_, D> {
// Wrap the formatter with an indentation level and print the advices
let mut slot = None;
let mut fmt = IndentWriter::wrap(fmt, &mut slot, true, " ");
let mut visitor = PrintAdvices(&mut fmt);

print_advices(&mut visitor, diagnostic, self.verbose)
if self.search {
let mut visitor = PrintSearch(&mut fmt);
print_advices(&mut visitor, diagnostic, self.verbose)
} else {
let mut visitor = PrintAdvices(&mut fmt);
print_advices(&mut visitor, diagnostic, self.verbose)
}
}
}

/// Display struct implementing the formatting of a diagnostic header.
struct PrintHeader<'fmt, D: ?Sized>(&'fmt D);
pub(crate) struct PrintHeader<'fmt, D: ?Sized>(pub(crate) &'fmt D);

impl<D: Diagnostic + ?Sized> fmt::Display for PrintHeader<'_, D> {
fn fmt(&self, f: &mut fmt::Formatter<'_>) -> io::Result<()> {
Expand Down Expand Up @@ -347,6 +363,14 @@ impl<D: Diagnostic + ?Sized> fmt::Display for PrintCauseChain<'_, D> {
}
}

struct PrintSearch<'a, 'b>(&'a mut fmt::Formatter<'b>);

impl Visit for PrintSearch<'_, '_> {
fn record_frame(&mut self, location: Location<'_>) -> io::Result<()> {
frame::print_highlighted_frame(self.0, location)
}
}

/// Implementation of [Visitor] that prints the advices for a diagnostic.
struct PrintAdvices<'a, 'b>(&'a mut fmt::Formatter<'b>);

Expand Down
69 changes: 69 additions & 0 deletions crates/biome_diagnostics/src/display/frame.rs
Original file line number Diff line number Diff line change
Expand Up @@ -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())?;

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 = &current_text[0..marker.start().into()];
let highlighted_range = &current_text[marker.start().into()..marker.end().into()];
let end_range = &current_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
Expand Down
37 changes: 33 additions & 4 deletions crates/biome_service/src/workspace/server.rs
Original file line number Diff line number Diff line change
Expand Up @@ -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;
Expand Down Expand Up @@ -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]
///
Expand Down Expand Up @@ -783,10 +801,21 @@ impl Workspace for WorkspaceServer {
}

fn search_pattern(&self, params: SearchPatternParams) -> Result<SearchResults, WorkspaceError> {
// FIXME: Let's implement some real matching here...
let SearchPatternParams { path, .. } = params;

// TODO: Let's implement some real matching here...
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,
})
}

Expand Down

0 comments on commit 50c880e

Please sign in to comment.