-
Notifications
You must be signed in to change notification settings - Fork 2
Conversation
Codecov Report
@@ Coverage Diff @@
## master #95 +/- ##
==========================================
+ Coverage 78.07% 78.41% +0.33%
==========================================
Files 61 64 +3
Lines 2472 2520 +48
Branches 521 522 +1
==========================================
+ Hits 1930 1976 +46
- Misses 542 544 +2
Continue to review full report at Codecov.
|
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.
Can you provide a small example of what this looks like when used in a Sourcegraph extension?
Make sure the naming here won't clash if/when we add providers for search results like JIRA or GitHub issues.
src/client/providers/search.ts
Outdated
if (providers.length === 0) { | ||
return [query] | ||
} | ||
return providers[0](query) |
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.
Could these be chained? Something like provider[0](query).then(q1 => provider[1](q1)).then(...)
?
@chrismwendt here's an example extension: https://github.com/attfarhan/search-extension Perhaps we could change the naming from |
|
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.
Add tests for all newly added code. There are existing tests for similar things to everything you've done, so you can start with those.
src/client/api/search.ts
Outdated
$unregister(id: number): void | ||
} | ||
|
||
export class SearchFeatures implements SearchFeaturesAPI { |
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.
Can just call it Search, not SearchFeatures. LanguageFeatures is called that because just "Language" alone would be confusing. That problem does not exist for search. (Update all instances of "searchFeatures" too.)
src/client/providers/search.ts
Outdated
export type TransformQuerySignature = (query: string) => Observable<string | null | undefined> | ||
|
||
/** Provides transformed queries from the first search extension. */ | ||
export class SearchTransformProviderRegistry extends FeatureProviderRegistry<{}, TransformQuerySignature> { |
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 it a search transform, a query transform, or what? Be consistent.
I think that "query transformer" is the best term to use. So QueryTransformerRegistry
, registerQueryTransformer
, etc.
@chrismwendt @sqs PTAL |
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.
This looks fantastic! Nice job. I added a lot of comments about very picky details.
import { FeatureProviderRegistry } from '../providers/registry' | ||
import { SubscriptionMap } from './common' | ||
|
||
export interface SearchAPI { |
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.
Add /** @internal */
tsdoc directives as in https://sourcegraph.com/github.com/sourcegraph/sourcegraph-extension-api/-/blob/src/client/api/configuration.ts#L8:1 and other files because this type is part of the internal API (so we want to communicate that to readers and to tools like doc generators).
expectObservable( | ||
transformQuery( | ||
cold<TransformQuerySignature[]>('-a-b-|', { | ||
a: [q => of(q)], |
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.
If the first query transform is just the identity func, then this doesn't really test the behavior of "all query transform funcs are applied". Instead you should test something like:
- 1st query transform func appends
a
to the string - 2nd query transform func appends
b
to the string
and test that the result is the original string with ab
appended.
(I think the current test would pass even if your logic was like this.providers[1](query)
, which would obviously be incorrect and would not be caught by the test.)
|
||
export type TransformQuerySignature = (query: string) => Observable<string | null | undefined> | ||
|
||
/** Provides transformed queries from the first search extension. */ |
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.
Inaccurate now: it is not just the first search extension, it calls all of them.
Also, there is no such concept as a "search extension". An extension can have any number of query transformers. The relevant concept here is "query transformers".
So:
/** Transforms search queries using registered query transformers from extensions. */
* Returns an observable that emits a query transformed by all providers whenever any of the last-emitted set of providers emits | ||
* a query. | ||
* | ||
* Most callers should use QueryTransformerRegistry's transformQuery method, which uses the registered search |
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.
The term "search provider" is inaccurate; there is no such concept (or it is an imprecise way to refer to query transformers). It should be "query transformers".
return providers.reduce( | ||
(currentQuery, transformQuery) => | ||
currentQuery.pipe( | ||
flatMap(q => transformQuery(q).pipe(map(transformedQuery => transformedQuery || q))) |
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 this the desired behavior?
- If a query transformer returns null or the empty string, use the original input string.
- Otherwise, use the original input string.
I suspect you don't intend to consider the empty string as meaning "use the original input string". But the empty string is falsey.
So I would do:
transformedQuery => transformedQuery === null ? q : transformedQuery
Or, really, why special-case null at all? If a query transformer wants to not make any modifications to the string, it can just return it unmodified. That is actually what I'd recommend.
src/integration-test/search.test.ts
Outdated
'bar' | ||
) | ||
|
||
// Unregister the provider and ensure it's removed |
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.
for consistency, add a period after a line comment if it's a full sentence
const { clientController, extensionHost, ready } = await integrationTestContext() | ||
|
||
// Register the provider and call it | ||
extensionHost.search.registerQueryTransformer({ transformQuery: q => `${q} bar` }) |
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.
Minor, but bar
and baz
look almost the same; it's not easy to visually spot that they are distinct, given that everything else on the 2 lines is the same. I would recommend using foo
, qux
, etc., here on one of the lines so it's easier to spot that they are different.
src/sourcegraph.d.ts
Outdated
@@ -717,6 +717,14 @@ declare module 'sourcegraph' { | |||
): ProviderResult<Location[]> | |||
} | |||
|
|||
export interface QueryTransformerProvider { |
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.
Add tsdoc docstrings to:
- interface QueryTransformerProvider
- QueryTransformerProvider#transformQuery
- namespace search
- function registerQueryTransformer
src/sourcegraph.d.ts
Outdated
@@ -717,6 +717,14 @@ declare module 'sourcegraph' { | |||
): ProviderResult<Location[]> | |||
} | |||
|
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.
I think you should just call it QueryTransformer
not QueryTransformerProvider
. I struggled with this in other cases but here's my thinking:
- The goal of naming is to communicate what something is.
- Using the "Provider" suffix is generally good for things like this because it communicates that this fits the "provider" pattern. Not in the strict Erich Gamma Design Patterns sense, but just in the "this is like hover providers, definition providers, etc., in that it can be registered/unregistered and provides data given some parameters".
- But when there is a word that is similar to "provider", is more accurate, is short, and is the term you'd use anywhere else in code describing this, then you should use that word instead.
- In this case, that word is "Transformer". QueryTransformer and QueryTransformerProvider basically mean the same thing; in the latter, "transformer" and "provider" basically mean the same thing, so it's weird to have them both.
- Looking at vscode.d.ts, they have many things that follow the guidelines I laid out instead of always using the "Provider" suffix: registerUriHandler, registerWebviewPanelSerializer, etc. Those are way better than registerUriHandlerProvider or registerUriProvider, or registerWebviewPanelSerializationProvider or registerWebviewPanelSerializerProvider.
src/sourcegraph.d.ts
Outdated
@@ -717,6 +717,14 @@ declare module 'sourcegraph' { | |||
): ProviderResult<Location[]> | |||
} | |||
|
|||
export interface QueryTransformerProvider { | |||
transformQuery(query: string): ProviderResult<string> |
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.
I wrote 2 comments above about:
- not special-casing
null
return values - using
Promise<string>
instead ofPromise<string | null | undefined>
I see why you included null and undefined now, because ProviderResult
adds them. But I think ProviderResult
is only useful for things like hover providers, not for transform funcs. So you should just make this return string | Promise<string>
.
The reason why ProviderResult
is useful for hover/definition/reference/etc. providers is that they need to be able to return null
to say "I have no answer for this query". But in transformQuery
, you can say "I have no transformations to apply to this query" by just returning the original query input argument.
Another related reason you should use string | Promise<string>
instead of ProviderResult<string>
here is that it adds confusion if there are 3 things that all mean the same thing ("do not transform this query"):
query => query
query => null
query => undefined
There is no need for the 2nd and 3rd ways of expressing this.
*/ | ||
export interface QueryTransformer { | ||
/** | ||
* Transforms a search query into another, valid query. If there are no transformations to be made |
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.
I'd replace "the original query is returned" with ", it should return the original query"
src/sourcegraph.d.ts
Outdated
* Transforms a search query into another, valid query. If there are no transformations to be made | ||
* the original query is returned. | ||
* | ||
* @param query A search query provided by a client. |
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.
just "A search query" is fine, no need to say "provided by a client" (not sure what that adds)
src/sourcegraph.d.ts
Outdated
/** | ||
* Registers a query transformer. | ||
* | ||
* Multiple transformers can be registered. In that case, all transformers will be executed |
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.
I think it is more idiomatic to say a transformation is applied, not executed. So replace "executed" with "applied".
Also, the order in which multiple transformers are applied is undefined (i.e., the extension API makes no guarantee). So I'd state that.
🎉 This PR is included in version 18.1.0 🎉 The release is available on: Your semantic-release bot 📦🚀 |
This adds the first part of the new search features API. It currently allows extensions to transform user queries.