feat(search/svelte): Add context specific suggestions to repo search input#62880
Conversation
|
This is amazing! (And you wanna hide the search bar?) |
b344425 to
9a980d2
Compare
|
Design feedback (not blocking): I find it pretty difficult to see that the text describing the suggestion is not part of the suggestion. My eye scans right past that text because it's not very high contrast compared to the suggestion text. The new designs also don't really fix this, and this scenario (a query snippet that is not just a filter, which is highlighted blue) is not covered in the new designs. @taiyab could we add some examples of "query snippet with a description" to the design? |
| expect(parse('abc content:"with space" def /regex literal/ ghi |')).toMatchInlineSnapshot(` | ||
| abc content:"with space" def /regex literal/ ghi | ||
| ^^^ (pattern) | ||
| ^^^^^^^ (filter.field: literal) | ||
| ^^^^^^^^^^^^^ (filter.value: literal) | ||
| ^^^^^^^^^^^^^^^^^^^^^ (filter) | ||
| ^^^ (pattern) | ||
| ^^^^^^^^^^^^^^^ (pattern) | ||
| ^^^ (pattern) | ||
| `) |
| * Converts a parse node into a sequence of Token's. This function generates | ||
| * new tokens as needed to represent the parse tree in a flat list. Those | ||
| * tokens won't have useful ranges. Range information is only preserved for | ||
| * pattern and parameter nodes. |
There was a problem hiding this comment.
I don't quite understand this. What use is a tokenize function if we can't trust the ranges it yields?
There was a problem hiding this comment.
Agree, plus to this - what ranges we can trust?
There was a problem hiding this comment.
This is all part of getRelevantTokens which takes in a a tree and basically prunes nodes that are irrelevant. E.g. for the query foo OR file:bar lang:JS baz, if we only want to keep all file: and lang: filters, we would convert
OR
/ \
foo AND
/ \
file:bar AND
/ \
lang:file baz
to just
AND
/ \
file:bar lang:JS
Later we "flatten" the tree into a list of tokens again (which is what tokenize is doing).
So far we only had the need to read the list of tokens but now we also want to use them to change them in the original search query. But some of the tokens returned here are basically synthetic, due to the transformation.
Or maybe it makes more sense to think about it this way: We have an input query and transform it into another query. Some tokens exist in both queries and we want to make changes to the original query based on the new query.
So maybe what should be returned is a list of tokens and some kind of source map.
There was a problem hiding this comment.
@vovakulikov, @camdencheek I changed the implementation of this. Please have another and let me know if it makes more sense now.
| { path: '/-/batch-changes', label: 'Batch changes', visibility: 'admin' }, | ||
| { path: '/-/settings', icon: mdiCog, label: 'Settings', visibility: 'admin' }, | ||
| ] | ||
| const repositoryContext = writable<RepositoryPageContext>({}) |
There was a problem hiding this comment.
Q: rather than all the beforeNavigate/afterNavigate hooks, why not populate repositoryContext with the information we already have from data? We should already have the current repo and current revision, and we can add current file in the child layout, right?
There was a problem hiding this comment.
I don't know; I agree with Camden that this feels a bit off when the parent level should know about lower lever child data. This repository context is about to create yet another horizontal stream of data in a world where we already have not trivial data passing through data-loaders.
I think I'm not against this idea about global context but maybe we should make it a bit more global in this case
rather than a specific repositoryContext thing.
There was a problem hiding this comment.
why not populate
repositoryContextwith the information we already have fromdata
data will only return what the loader (and any parent loader) returns, not "child" loaders. We can access all page data via $page.data but that is not strongly typed. We can add optional fields to the PageData type, but that would then apply to all routes.
Would you find it less confusing if we used $page.data and data instead of repositoryContext and data?
The core issue IMO is that we want to propagate data up from pages to the layout. Context solves that but I understand that it's "yet another thing".
There was a problem hiding this comment.
Because I think of this more as page data than component data (which I understand is kinda odd since pages are components), this feels like it belongs in page data loaders. We can make one of the pieces of page data writable so child pages can add their own information. In particular, the before/after hooks feel pretty weird since we're effectively manually implementing page data loading.
I'm having difficulty describing what I mean, so I think it might be easier to just show it. This draft moves all the propagation into the page data loaders so the page itself doesn't need to worry about it. Personally, I think that feels cleaner since it reduces the amount that the pages themselves need to know about the data loading lifecycle.
There was a problem hiding this comment.
(Also, feel free to just ignore me. I do not feel strongly about any of this, but wanted to at least bring it up in case I was missing something about how this needs to work)
There was a problem hiding this comment.
@camdencheek I don't think this would work due to data preloading. It would cause the context to updated if the user hovers over a file without clicking it. Using the page lifecycle ensures that the context is only changed when the page is actually navigated to/away from.
As I said in the PR, it doesn't feel ideal to me either, but I can't think of anything else right now apart from extending the global PageData object.
There was a problem hiding this comment.
Ah, interesting. I was thinking that preloading would create a new data object with a new writable that would be updated by its child data fetchers.
There was a problem hiding this comment.
A data loader is only executed when it's dependencies change (e.g. a URL parameter that is accessed in the loader). So in general you cannot make assumptions about when which loader is executed.
Took me a while to understand what you were saying here as I scanned the image first. Once I completely read it, and looked back at the image, it took me at least 3-5 seconds to even see that a description was been appended after the suggestion :D Yes — I'll fix this in the designs. |
|
@fkling Note: The search suggestions that are queries should probably have the same styling treatment as a keyword search query in the search input itself. (This is provided it was ran as a keyword search query (and not if it wasn't)). |
camdencheek
left a comment
There was a problem hiding this comment.
Played around with it locally -- works great, thank you!
vovakulikov
left a comment
There was a problem hiding this comment.
Left a few minor comments, nothing bit. I think though we should address our data flows as soon as possible while we have not that many data sources and fields. Complexity around contexts and different data types in data loaders look a bit scary every time I have to touch it. (maybe we can simplify it)
|
|
||
| input = input.replaceAll('|', '') | ||
| const result = parseSearchQuery(input) | ||
| const tokens = scanSearchQuery(input, false, SearchPatternType.standard) |
There was a problem hiding this comment.
Minor: it's nothing but IMO would be a bit easier to read if it was with object-like argument with key fields
like this
const tokens = scanSearchQuery({
input,
interpreteComments: false,
pattern: SearchPatternType.standard
})| return getRelevantTokens(result.node, { start: inputPosition, end: inputPosition }, filter) | ||
| } | ||
|
|
||
| function annotateToken(token: Token, prefix?: string): string { |
There was a problem hiding this comment.
I like the idea with annotations, very cool
| * Converts a parse node into a sequence of Token's. This function generates | ||
| * new tokens as needed to represent the parse tree in a flat list. Those | ||
| * tokens won't have useful ranges. Range information is only preserved for | ||
| * pattern and parameter nodes. |
There was a problem hiding this comment.
Agree, plus to this - what ranges we can trust?
| (tokens: Token[], position: number, { repoName, revision }: ScopeInformation): Option[] => { | ||
| const options: Option[] = [] | ||
|
|
||
| { |
There was a problem hiding this comment.
I don't think we need these blocks. Since both have no overlap scoped-variables this should be fine, no?
There was a problem hiding this comment.
I added them to not "pollute" the function scope. The variables used here are only relevant for this specific computation.
| { path: '/-/batch-changes', label: 'Batch changes', visibility: 'admin' }, | ||
| { path: '/-/settings', icon: mdiCog, label: 'Settings', visibility: 'admin' }, | ||
| ] | ||
| const repositoryContext = writable<RepositoryPageContext>({}) |
There was a problem hiding this comment.
I don't know; I agree with Camden that this feels a bit off when the parent level should know about lower lever child data. This repository context is about to create yet another horizontal stream of data in a world where we already have not trivial data passing through data-loaders.
I think I'm not against this idea about global context but maybe we should make it a bit more global in this case
rather than a specific repositoryContext thing.
camdencheek
left a comment
There was a problem hiding this comment.
The new approach makes more sense to me! Thanks for the refactor
This allows us to replace the corresponding tokens in the original query. It's not ideal because the token sequence will contain a mix of generated and preserved tokens but I hope it's good enough.
This commit adds a new suggestions source to the repository search input. It currently offers the following suggestions: - Repository group/org - Current file and/or directory - Language of current file A new context store is introduced to propagate those values to the search input. When on a commit page it adds the revision of the commit to the repo filter.
Co-authored-by: Camden Cheek <camden@ccheek.com>
4e64c59 to
d43d493
Compare

This PR adds a new suggestions source to the repository search input. It currently offers the following suggestions:
When on a commit page it adds the revision of the commit to the repo filter.
A new context store is introduced to propagate those values to the search input. I also had to update our query processing logic to preserve pattern and filter token ranges (first commit) so that we can also replace filters, not just add the.
NOTE: I'm not convinced the way the information is propagated via context is the best way to do, but it's the simplest I could think of now. I can imagine in the future that individual pages create/register suggestions directly.
sg-sk-fuzzy-finder-.3.mp4
Test plan
Manual testing.