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

Add highlighting of matches to Quick Bar items #22019

Open
wants to merge 3 commits into
base: dev
Choose a base branch
from

Conversation

chammp
Copy link
Contributor

@chammp chammp commented Sep 18, 2024

Proposed change

This MR adds highlighting of (partial) matches of the filter/search text to the items display in HA's Quick Bar:

qbhighlight

I think the same mechanism can be applied to other places with find-as-you-type-queries based on the fuzzy sequence matching as well (area picker for instance); that'd be part of later MRs, though. If this one is accepted, that is.

A couple notes:

  • The actual scoring method already returns information about where the matching happened.
  • This information is condensed and attached to each searchable item.
  • As in at least one instance the structure of data that's passed to the filter function differs from the structure it is displayed in, the embedding component is responsible to convert this back into appropriate tokens.
  • The target structure is an array of tokens, each with a text part and an indicator if this token matched the filter.
  • Concatenation of all tokens yields the original string that was searched in.
  • The actual styling of the highlights is just something that I came up with super spontaneously and that probably should be adapted somehow; I'm ofc open for suggestions.
  • My FE dev experience is, let's say limited and suuuuper dated; so if there's anything that itches a seasoned FE dev, I am happy to learn what and why.

There seems to be a bug in Chromium based browsers (screencast above is done in Firefox) where they erroneously apply word capitalization on highlighty boundaries in a very inconsistend and apparently random way. I don't know if there are CSS or DOM hacks that could be applied, perhaps anyone here as an idea? See screenshots of that super inconsistent behavior (Edge 128.0.2739.79, however a freshly installed Chrome shows the same behavior):

Search for "n":
grafik

Add an "a":
grafik

Remove the "a" again:
grafik

Supposedly the HTML is the same in step 1 and 3, but Chromium renders it differently. Also, no crazy capitalization after "Na" in step 2. 🤷

Type of change

  • Dependency upgrade
  • Bugfix (non-breaking change which fixes an issue)
  • New feature (thank you!)
  • Breaking change (fix/feature causing existing functionality to break)
  • Code quality improvements to existing code or addition of tests

Example configuration

Additional information

  • This PR fixes or closes issue: fixes #
  • This PR is related to issue or discussion:
  • Link to documentation pull request:

Checklist

  • The code change is tested and works locally.
  • There is no commented out code in this PR.
  • Tests have been added to verify that the new code works.

If user exposed functionality or configuration variables are added/changed:

Summary by CodeRabbit

  • New Features

    • Enhanced fuzzy matching functionality to provide detailed match information, including best match index and character segments.
    • Introduced a custom web component HaMatchSegment for visually displaying matched text segments.
    • Improved QuickBar item filtering with clearer visual feedback on matches.
  • Bug Fixes

    • Refined testing for fuzzy matching functionalities to ensure accuracy in scoring and tokenization.
  • Documentation

    • Updated documentation for FuzzyScore to clarify the variable nature of match positions.

Copy link
Contributor

coderabbitai bot commented Sep 18, 2024

Walkthrough

Walkthrough

The changes encompass modifications to documentation, enhancements to the fuzzy matching functionality, the introduction of a new web component for displaying match segments, and improvements to testing coverage. Key updates include a refined fuzzySequentialMatch function that returns detailed match information, the addition of a HaMatchSegment component for visual representation, and enhanced tests for validating the fuzzy matching logic and tokenization processes.

Changes

Files Change Summary
src/common/string/filter/filter.ts Updated documentation comments for FuzzyScore type to indicate potential additional match positions.
src/common/string/filter/sequence-matching.ts Enhanced fuzzySequentialMatch to return match score and details; added helper functions for match analysis; modified ScorableTextItem interface to include matchInfo.
src/components/ha-match-segment.ts Introduced HaMatchSegment web component for rendering matched segments with visual differentiation; defined styles and global interface extension for custom element recognition.
src/dialogs/quick-bar/ha-quick-bar.ts Improved QuickBar class to reset match information based on filters; added private methods for generating match tokens for command and entity items; updated rendering methods to utilize new match token structures.
test/common/string/sequence_matching.test.ts Enhanced tests for fuzzySequentialMatch, tokenizeMatchInfo, and tokenizeConcatenatedMatchInfo to ensure robustness and coverage, including checks for score presence and handling of undefined segments.

Recent review details

Configuration used: CodeRabbit UI
Review profile: CHILL

Commits

Files that changed from the base of the PR and between 049ec8d and f9556d3.

Files selected for processing (1)
  • src/components/ha-match-segment.ts (1 hunks)
Files skipped from review as they are similar to previous changes (1)
  • src/components/ha-match-segment.ts

Thank you for using CodeRabbit. We offer it for free to the OSS community and would appreciate your support in helping us grow. If you find it useful, would you consider giving us a shout-out on your favorite social media?

Share
Tips

Chat

There are 3 ways to chat with CodeRabbit:

  • Review comments: Directly reply to a review comment made by CodeRabbit. Example:
    -- I pushed a fix in commit <commit_id>, please review it.
    -- Generate unit testing code for this file.
    • Open a follow-up GitHub issue for this discussion.
  • Files and specific lines of code (under the "Files changed" tab): Tag @coderabbitai in a new review comment at the desired location with your query. Examples:
    -- @coderabbitai generate unit testing code for this file.
    -- @coderabbitai modularize this function.
  • PR comments: Tag @coderabbitai in a new PR comment to ask questions about the PR branch. For the best results, please provide a very specific query, as very limited context is provided in this mode. Examples:
    -- @coderabbitai generate interesting stats about this repository and render them as a table.
    -- @coderabbitai read src/utils.ts and generate unit testing code.
    -- @coderabbitai read the files in the src/scheduler package and generate a class diagram using mermaid and a README in the markdown format.
    -- @coderabbitai help me debug CodeRabbit configuration file.

Note: Be mindful of the bot's finite context window. It's strongly recommended to break down tasks such as reading entire modules into smaller chunks. For a focused discussion, use review comments to chat about specific files and their changes, instead of using the PR comments.

CodeRabbit Commands (Invoked using PR comments)

  • @coderabbitai pause to pause the reviews on a PR.
  • @coderabbitai resume to resume the paused reviews.
  • @coderabbitai review to trigger an incremental review. This is useful when automatic reviews are disabled for the repository.
  • @coderabbitai full review to do a full review from scratch and review all the files again.
  • @coderabbitai summary to regenerate the summary of the PR.
  • @coderabbitai resolve resolve all the CodeRabbit review comments.
  • @coderabbitai configuration to show the current CodeRabbit configuration for the repository.
  • @coderabbitai help to get help.

Other keywords and placeholders

  • Add @coderabbitai ignore anywhere in the PR description to prevent this PR from being reviewed.
  • Add @coderabbitai summary to generate the high-level summary at a specific location in the PR description.
  • Add @coderabbitai or @coderabbitai title anywhere in the PR title to generate the title automatically.

CodeRabbit Configuration File (.coderabbit.yaml)

  • You can programmatically configure CodeRabbit by adding a .coderabbit.yaml file to the root of your repository.
  • Please see the configuration documentation for more information.
  • If your editor has YAML language server enabled, you can add the path at the top of this file to enable auto-completion and validation: # yaml-language-server: $schema=https://coderabbit.ai/integrations/schema.v2.json

Documentation and Community

  • Visit our Documentation for detailed information on how to use CodeRabbit.
  • Join our Discord Community to get help, request features, and share feedback.
  • Follow us on X/Twitter for updates and announcements.

Copy link
Contributor

@coderabbitai coderabbitai bot left a comment

Choose a reason for hiding this comment

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

Actionable comments posted: 0

Outside diff range and nitpick comments (1)
src/dialogs/quick-bar/ha-quick-bar.ts (1)

378-416: LGTM! Consider adding unit tests.

The new _applyCommandItemMatchingInfo and _applyEntityItemMatchingInfo functions encapsulate the logic for generating match tokens nicely. This promotes code reusability and maintainability.

The functions also handle cases where matchInfo may be undefined, preventing potential runtime errors.

Consider adding unit tests for these functions to ensure they handle various scenarios correctly, such as:

  • When matchInfo is undefined
  • When matchInfo has different index values
  • When matchInfo has no match segments
Review details

Configuration used: CodeRabbit UI
Review profile: CHILL

Commits

Files that changed from the base of the PR and between 858a00e and 049ec8d.

Files selected for processing (5)
  • src/common/string/filter/filter.ts (1 hunks)
  • src/common/string/filter/sequence-matching.ts (3 hunks)
  • src/components/ha-match-segment.ts (1 hunks)
  • src/dialogs/quick-bar/ha-quick-bar.ts (6 hunks)
  • test/common/string/sequence_matching.test.ts (4 hunks)
Files skipped from review due to trivial changes (1)
  • src/common/string/filter/filter.ts
Additional comments not posted (16)
src/components/ha-match-segment.ts (5)

1-3: LGTM!

The imports are relevant and required for the component implementation. The code segment looks good.


5-7: LGTM!

The custom element definition and property declaration follow the LitElement conventions correctly. The code segment looks good.


9-19: LGTM!

The rendering logic in the render method is implemented correctly. It handles the case when segments is empty or undefined, and it conditionally renders each segment with highlighting for matched segments. The code segment looks good.


21-29: LGTM!

The static styles method correctly defines the CSS styles for the component using the css tagged template literal. The "highlight" class styles are appropriate for visually distinguishing matched segments. The code segment looks good.


31-35: LGTM!

The global interface extension for HTMLElementTagNameMap is correctly declared, associating the custom element name ha-match-segment with the HaMatchSegment class. This ensures that the custom element can be recognized as a valid HTML tag in the DOM. The code segment looks good.

test/common/string/sequence_matching.test.ts (4)

66-67: LGTM!

The additional assertion to check if the result is defined before accessing its score property improves the test's robustness and prevents potential false positives.


112-154: LGTM!

The updated test case enhances the coverage by validating the score and matchInfo properties of the filtered and sorted items. This ensures that the fuzzyFilterSort function calculates the scores and match information accurately in addition to filtering and sorting the items correctly.


166-246: LGTM!

The test case for tokenizeMatchInfo provides comprehensive coverage by validating the function's behavior for various input patterns, including fully matched, partially matched, and unmatched segments. It ensures that the function correctly tokenizes the item string based on the match information.

The additional test case for undefined segments is a nice touch, verifying that the function handles gracefully when no match information is provided.

Overall, the test case is well-structured and helps catch any potential issues or regressions in the tokenization logic.


248-324: LGTM!

The test case for tokenizeConcatenatedMatchInfo provides comprehensive coverage by validating the function's behavior for various input patterns, including fully matched, partially matched, and unmatched segments for both the type and target. It ensures that the function correctly tokenizes the concatenated string based on the match information.

The additional test case for undefined segments is a nice touch, verifying that the function handles gracefully when no match information is provided.

Overall, the test case is well-structured and helps catch any potential issues or regressions in the tokenization logic for concatenated strings.

src/common/string/filter/sequence-matching.ts (6)

63-123: Approve the addition of _getContinuousRuns function.

The _getContinuousRuns function is a helpful addition that analyzes and returns continuous segments of matches and gaps. The implementation looks correct and well-documented.


125-153: Approve the addition of tokenizeMatchInfo function.

The tokenizeMatchInfo function is a useful addition that tokenizes a matched string against match segments. It provides a way to generate match tokens indicating which parts of the string matched the original query. The implementation looks good.


155-197: Approve the addition of tokenizeConcatenatedMatchInfo function.

The tokenizeConcatenatedMatchInfo function is a valuable addition that tokenizes the segments of a concatenated string against match segments. It provides a way to handle cases where distinct string parts have been concatenated for a fuzzy sequential search, but need to be tokenized separately. The implementation looks correct and well-documented.


Line range hint 335-374: Approve the changes to fuzzyFilterSort function.

The updates to the fuzzyFilterSort function ensure that the score and matchInfo properties of each item are properly set based on the best match found using fuzzySequentialMatch. The changes look good and the updated JSDoc comments provide clearer documentation on the structure of the returned items.


321-321: Approve the changes to ScorableTextItem interface.

The addition of the matchInfo property to the ScorableTextItem interface, along with the clear JSDoc comment, enhances the interface by allowing detailed match information to be stored alongside the score. This enables more advanced processing and display of matched results. The changes look good.


Line range hint 6-61: Approve the changes to fuzzySequentialMatch function.

The updates to the fuzzySequentialMatch function enhance its functionality by returning more detailed match information and handling multiple strings in item.strings. The changes look good.

Verify that the code consuming the fuzzySequentialMatch function has been updated to handle the new returned object structure containing the score and match information.

Run the following script to find usages of fuzzySequentialMatch:

Verification successful

Approve the changes to fuzzySequentialMatch function. Usage has been properly updated.

The modifications to the fuzzySequentialMatch function have been implemented correctly, and its usage throughout the codebase has been updated to handle the new returned object structure. Key observations:

  • In test files (e.g., 'test/common/string/sequence_matching.test.ts'), both the score and matchInfo properties are accessed from the returned object.
  • In the implementation file ('src/common/string/filter/sequence-matching.ts'), the fuzzyFilterSort function correctly destructures the returned object to get score and matchInfo.

These findings confirm that the changes have been consistently applied and the function is being used correctly with its new return type.

Scripts executed

The following scripts were executed for the analysis:

Script:

#!/bin/bash
# Description: Find usages of `fuzzySequentialMatch` function.

# Test: Search for the function usage. Expect: Occurrences of the function call.
rg --type typescript -A 5 $'fuzzySequentialMatch'

Length of output: 83


Script:

#!/bin/bash
# Description: Find usages of `fuzzySequentialMatch` function in TypeScript files.

# Search for the function usage in .ts files
echo "Searching in .ts files:"
rg --type-add 'ts:*.ts' --type ts -A 5 'fuzzySequentialMatch'

# Search for the function usage in .tsx files (in case of React with TypeScript)
echo "Searching in .tsx files:"
rg --type-add 'tsx:*.tsx' --type tsx -A 5 'fuzzySequentialMatch'

Length of output: 7260

src/dialogs/quick-bar/ha-quick-bar.ts (1)

133-150: Verify the function signature change in the codebase.

The updated logic to reset matchInfo when no filter is applied looks good. However, ensure that all calls to _getItems have been updated to match the new signature.

Run the following script to verify the function usage:

Verification successful

Verification successful: _getItems function usage is correct

The updated _getItems function in src/dialogs/quick-bar/ha-quick-bar.ts is being called with the correct new signature (4 parameters). Other occurrences of _getItems in the codebase are separate implementations within their respective components and are not affected by this change.

Scripts executed

The following scripts were executed for the analysis:

Script:

#!/bin/bash
# Description: Verify all calls to `_getItems` match the new signature.

# Test: Search for the function usage. Expect: Only occurrences of the new signature.
rg --type typescript -A 5 $'_getItems\\('

Length of output: 76


Script:

#!/bin/bash
# Description: Verify all calls to `_getItems` match the new signature.

# Find TypeScript files and search for _getItems function calls
fd -e ts -e tsx | xargs rg -n -C 5 '_getItems\('

Length of output: 3870

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Projects
None yet
Development

Successfully merging this pull request may close these issues.

1 participant