Skip to content
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

Merged

Conversation

BackupMiles
Copy link
Contributor

@BackupMiles BackupMiles commented Jun 22, 2024

Summary

This PR aims to provide the infrastructure for the future search command output.

  • Added the capability to highlight a given TextRange inside a file
  • Added a dummy data provider to generate the text matches temporarily

Aims to resolve #2462

Test Plan

Since this PR only introduces an infrastructure for a command output, I have not provided any tests yet -- I would be glad to add them if deemed necessary.
Running cargo test still yields a positive result.

@github-actions github-actions bot added A-CLI Area: CLI A-Project Area: project A-Diagnostic Area: diagnostocis labels Jun 22, 2024
@Conaclos Conaclos requested a review from arendjr June 22, 2024 17:13
@github-actions github-actions bot added the A-Changelog Area: changelog label Jun 22, 2024
Copy link
Contributor

@arendjr arendjr left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Nice work!

let source = SourceFile::new(source_code);

let start = source.location(span.start())?;
let end = source.location(span.end())?;
Copy link
Contributor

Choose a reason for hiding this comment

The 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.

Copy link
Contributor Author

Choose a reason for hiding this comment

The 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?

Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Would be appreciated!

} else if is_last_line {
let start_index = current_text
.text_len()
.checked_sub(current_text.trim_start().text_len())
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I think I’d just use saturated_sub() for the extra safety. It shouldn’t happen either way.

Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Sure! Quick note, with that function being available on u32 I would need to add a bunch of into()-s, and later transform it back to TextSize. Is this ok?

Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I have just pushed the changes which show what I am referring to, I am happy to revert them in case 👍

for (i, char) in iter {
let should_highlight = i >= marker.start().into() && i < marker.end().into();
if should_highlight {
fmt.write_markup(markup! { <Emphasis><Info>{char}</Info></Emphasis> })?;
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Can we create subslices of adjacent characters that need highlighting, instead of adding the markup per character?

Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Absolutely! I have just pushed the changes, let me know if you have any feedback :)

@@ -783,10 +801,25 @@ impl Workspace for WorkspaceServer {
}

fn search_pattern(&self, params: SearchPatternParams) -> Result<SearchResults, WorkspaceError> {
let SearchPatternParams {
path,
pattern: _pattern,
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

You can use .. if you want to ignore additional fields.

@ematipico ematipico self-requested a review June 23, 2024 16:14
Copy link
Member

@ematipico ematipico left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Since this PR doesn't add anything to the user (there's no command search yet), I think the changelog change should be reverted.

I also left some feedback and questions

@@ -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", tags(SEARCH))]
Copy link
Member

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 see

Copy link
Contributor

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 :)

Copy link
Contributor Author

@BackupMiles BackupMiles Jun 24, 2024

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 see

Yes, this is the reason why I went with it:

  • I think a SearchDiagnostic should have a Hint severity
    • The DiagnosticPrinter will deem a diagnostic skippable if its severity is below its diagnostic_level -- in our case, anything below Information is deemed skippable
  • For fn should_skip_diagnostic to print a search diagnostic despite its low severity, I figured I needed to have a SEARCH tag attached to the diagnostic_tags (see traverse.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 to

Copy link
Contributor Author

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 see

Yes, this is the reason why I went with it:

  • I think a SearchDiagnostic should have a Hint severity

    • The DiagnosticPrinter will deem a diagnostic skippable if its severity is below its diagnostic_level -- in our case, anything below Information is deemed skippable
  • For fn should_skip_diagnostic to print a search diagnostic despite its low severity, I figured I needed to have a SEARCH tag attached to the diagnostic_tags (see traverse.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 to

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 👍

.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...
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Is the FIXME still relevant after this PR?

Copy link
Contributor

Choose a reason for hiding this comment

The 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 :)

Copy link
Contributor Author

Choose a reason for hiding this comment

The 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 :)

Sorry for my last out-of-sync reply: I actually went ahead and removed this one, in favor of keeping the other TODO inside the function which is actually supposed to return the matches

.map(|mat| {
SearchDiagnostic
.with_file_span(mat)
.with_severity(Severity::Hint)
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

If the severity of SearchDiagnostic doesn't depend on external factors (for example, the configuration), then the severity can be set directly in the definition of SearchDiagnostic:

#[derive(Debug, Diagnostic)]
#[diagnostic(category = "search", tags(SEARCH), severity = Hint)]
struct SearchDiagnostic;

Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Maybe Severity::Information is also more appropriate?

Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I think so too!

Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Sure! I will go ahead and change the severity to Information then

crates/biome_cli/src/reporter/terminal.rs Show resolved Hide resolved
@@ -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...
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Is the FIXME still applicable? I see there's some new code, maybe the FIXME should become a TODO or something else?

Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

A TODO definitely sounds better, thanks! 👍

@github-actions github-actions bot removed the A-Changelog Area: changelog label Jun 24, 2024
@arendjr arendjr merged commit 50c880e into biomejs:main Jun 25, 2024
10 checks passed
@arendjr
Copy link
Contributor

arendjr commented Jun 25, 2024

Thanks @BackupMiles !

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Labels
A-CLI Area: CLI A-Diagnostic Area: diagnostocis A-Project Area: project
Projects
None yet
Development

Successfully merging this pull request may close these issues.

☂️ Implement biome search output formatting
3 participants