This repository was archived by the owner on Nov 6, 2018. It is now read-only.
-
Notifications
You must be signed in to change notification settings - Fork 1
Add search features API #95
Merged
Merged
Changes from all commits
Commits
Show all changes
17 commits
Select commit
Hold shift + click to select a range
efa285d
feat: search provider foo
attfarhan 5e4fe5b
feat: can registerSearchProvider
attfarhan de15667
fix: clean up
attfarhan f5fd2be
feat: return original query if no providers
attfarhan e10a9fc
docs: update docs
attfarhan 25ca64b
chore: rename SearchProvider to QueryTransformProvider
attfarhan c76d18c
fix: improve naming
attfarhan 8a1c7aa
chore: add tests for queryTransformer
attfarhan 39590b9
fix: remove commented chaining attempts
attfarhan 4847f2b
fix: more renames and cleanup
attfarhan 13e53ba
chore: add integration test
attfarhan 4bbef15
fix: address comments
attfarhan c112fa7
fix: remove unneeded internal tsdoc directives
attfarhan 61756bc
fix: add string as transform query return type
attfarhan f1326e3
Revert "fix: add string as transform query return type"
attfarhan 480071b
fix: update docs
attfarhan 4c57cae
fix: docs
attfarhan File filter
Filter by extension
Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
There are no files selected for viewing
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
| Original file line number | Diff line number | Diff line change |
|---|---|---|
| @@ -0,0 +1,47 @@ | ||
| import { from, Observable, Subscription } from 'rxjs' | ||
| import { ExtSearch } from 'src/extension/api/search' | ||
| import { Connection } from 'src/protocol/jsonrpc2/connection' | ||
| import { createProxyAndHandleRequests } from '../../common/proxy' | ||
| import { TransformQuerySignature } from '../providers/queryTransformer' | ||
| import { FeatureProviderRegistry } from '../providers/registry' | ||
| import { SubscriptionMap } from './common' | ||
|
|
||
| /** @internal */ | ||
| export interface SearchAPI { | ||
| $registerQueryTransformer(id: number): void | ||
| $unregister(id: number): void | ||
| } | ||
|
|
||
| /** @internal */ | ||
| export class Search implements SearchAPI { | ||
| private subscriptions = new Subscription() | ||
| private registrations = new SubscriptionMap() | ||
| private proxy: ExtSearch | ||
|
|
||
| constructor( | ||
| connection: Connection, | ||
| private queryTransformerRegistry: FeatureProviderRegistry<{}, TransformQuerySignature> | ||
| ) { | ||
| this.subscriptions.add(this.registrations) | ||
|
|
||
| this.proxy = createProxyAndHandleRequests('search', connection, this) | ||
| } | ||
|
|
||
| public $registerQueryTransformer(id: number): void { | ||
| this.registrations.add( | ||
| id, | ||
| this.queryTransformerRegistry.registerProvider( | ||
| {}, | ||
| (query: string): Observable<string> => from(this.proxy.$transformQuery(id, query)) | ||
| ) | ||
| ) | ||
| } | ||
|
|
||
| public $unregister(id: number): void { | ||
| this.registrations.remove(id) | ||
| } | ||
|
|
||
| public unsubscribe(): void { | ||
| this.subscriptions.unsubscribe() | ||
| } | ||
| } | ||
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
| Original file line number | Diff line number | Diff line change |
|---|---|---|
| @@ -0,0 +1,70 @@ | ||
| import * as assert from 'assert' | ||
| import { of } from 'rxjs' | ||
| import { TestScheduler } from 'rxjs/testing' | ||
| import { transformQuery, TransformQuerySignature } from './queryTransformer' | ||
|
|
||
| const scheduler = () => new TestScheduler((a, b) => assert.deepStrictEqual(a, b)) | ||
|
|
||
| const FIXTURE_INPUT = 'foo' | ||
| const FIXTURE_RESULT = 'bar' | ||
| const FIXTURE_RESULT_TWO = 'qux' | ||
| const FIXTURE_RESULT_MERGED = 'foo bar qux' | ||
|
|
||
| describe('transformQuery', () => { | ||
| describe('0 providers', () => { | ||
| it('returns original query', () => | ||
| scheduler().run(({ cold, expectObservable }) => | ||
| expectObservable( | ||
| transformQuery(cold<TransformQuerySignature[]>('-a-|', { a: [] }), FIXTURE_INPUT) | ||
| ).toBe('-a-|', { | ||
| a: FIXTURE_INPUT, | ||
| }) | ||
| )) | ||
| }) | ||
|
|
||
| describe('1 provider', () => { | ||
| it('returns result from provider', () => | ||
| scheduler().run(({ cold, expectObservable }) => | ||
| expectObservable( | ||
| transformQuery( | ||
| cold<TransformQuerySignature[]>('-a-|', { | ||
| a: [q => of(FIXTURE_RESULT)], | ||
| }), | ||
| FIXTURE_INPUT | ||
| ) | ||
| ).toBe('-a-|', { a: FIXTURE_RESULT }) | ||
| )) | ||
| }) | ||
|
|
||
| describe('2 providers', () => { | ||
| it('returns a single query transformed by both providers', () => | ||
| scheduler().run(({ cold, expectObservable }) => | ||
| expectObservable( | ||
| transformQuery( | ||
| cold<TransformQuerySignature[]>('-a-|', { | ||
| a: [q => of(`${q} ${FIXTURE_RESULT}`), q => of(`${q} ${FIXTURE_RESULT_TWO}`)], | ||
| }), | ||
| FIXTURE_INPUT | ||
| ) | ||
| ).toBe('-a-|', { a: FIXTURE_RESULT_MERGED }) | ||
| )) | ||
| }) | ||
|
|
||
| describe('Multiple emissions', () => { | ||
| it('returns stream of results', () => | ||
| scheduler().run(({ cold, expectObservable }) => | ||
| expectObservable( | ||
| transformQuery( | ||
| cold<TransformQuerySignature[]>('-a-b-|', { | ||
| a: [q => of(`${q} ${FIXTURE_RESULT}`)], | ||
| b: [q => of(`${q} ${FIXTURE_RESULT_TWO}`)], | ||
| }), | ||
| FIXTURE_INPUT | ||
| ) | ||
| ).toBe('-a-b-|', { | ||
| a: `${FIXTURE_INPUT} ${FIXTURE_RESULT}`, | ||
| b: `${FIXTURE_INPUT} ${FIXTURE_RESULT_TWO}`, | ||
| }) | ||
| )) | ||
| }) | ||
| }) |
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
| Original file line number | Diff line number | Diff line change |
|---|---|---|
| @@ -0,0 +1,34 @@ | ||
| import { Observable, of } from 'rxjs' | ||
| import { flatMap, map, switchMap } from 'rxjs/operators' | ||
| import { FeatureProviderRegistry } from './registry' | ||
|
|
||
| export type TransformQuerySignature = (query: string) => Observable<string> | ||
|
|
||
| /** Transforms search queries using registered query transformers from extensions. */ | ||
| export class QueryTransformerRegistry extends FeatureProviderRegistry<{}, TransformQuerySignature> { | ||
| public transformQuery(query: string): Observable<string> { | ||
| return transformQuery(this.providers, query) | ||
| } | ||
| } | ||
|
|
||
| /** | ||
| * 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 query transformers | ||
| * | ||
| */ | ||
| export function transformQuery(providers: Observable<TransformQuerySignature[]>, query: string): Observable<string> { | ||
| return providers.pipe( | ||
| switchMap(providers => { | ||
| if (providers.length === 0) { | ||
| return [query] | ||
| } | ||
| return providers.reduce( | ||
| (currentQuery, transformQuery) => | ||
| currentQuery.pipe(flatMap(q => transformQuery(q).pipe(map(transformedQuery => transformedQuery)))), | ||
| of(query) | ||
| ) | ||
| }) | ||
| ) | ||
| } |
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
| Original file line number | Diff line number | Diff line change |
|---|---|---|
| @@ -0,0 +1,24 @@ | ||
| import { Unsubscribable } from 'rxjs' | ||
| import { QueryTransformer } from 'sourcegraph' | ||
| import { SearchAPI } from 'src/client/api/search' | ||
| import { ProviderMap } from './common' | ||
|
|
||
| export interface ExtSearchAPI { | ||
| $transformQuery: (id: number, query: string) => Promise<string> | ||
| } | ||
|
|
||
| export class ExtSearch implements ExtSearchAPI { | ||
| private registrations = new ProviderMap<QueryTransformer>(id => this.proxy.$unregister(id)) | ||
| constructor(private proxy: SearchAPI) {} | ||
|
|
||
| public registerQueryTransformer(provider: QueryTransformer): Unsubscribable { | ||
| const { id, subscription } = this.registrations.add(provider) | ||
| this.proxy.$registerQueryTransformer(id) | ||
| return subscription | ||
| } | ||
|
|
||
| public $transformQuery(id: number, query: string): Promise<string> { | ||
| const provider = this.registrations.get<QueryTransformer>(id) | ||
| return Promise.resolve(provider.transformQuery(query)) | ||
| } | ||
| } |
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
| Original file line number | Diff line number | Diff line change |
|---|---|---|
| @@ -0,0 +1,46 @@ | ||
| import * as assert from 'assert' | ||
| import { take } from 'rxjs/operators' | ||
| import { integrationTestContext } from './helpers.test' | ||
|
|
||
| describe('search (integration)', () => { | ||
| it('registers a query transformer', async () => { | ||
| const { clientController, extensionHost, ready } = await integrationTestContext() | ||
|
|
||
| // Register the provider and call it | ||
| const unsubscribe = extensionHost.search.registerQueryTransformer({ transformQuery: () => 'bar' }) | ||
| await ready | ||
| assert.deepStrictEqual( | ||
| await clientController.registries.queryTransformer | ||
| .transformQuery('foo') | ||
| .pipe(take(1)) | ||
| .toPromise(), | ||
| 'bar' | ||
| ) | ||
|
|
||
| // Unregister the provider and ensure it's removed. | ||
| unsubscribe.unsubscribe() | ||
| assert.deepStrictEqual( | ||
| await clientController.registries.queryTransformer | ||
| .transformQuery('foo') | ||
| .pipe(take(1)) | ||
| .toPromise(), | ||
| 'foo' | ||
| ) | ||
| }) | ||
|
|
||
| it('supports multiple query transformers', async () => { | ||
| const { clientController, extensionHost, ready } = await integrationTestContext() | ||
|
|
||
| // Register the provider and call it | ||
| extensionHost.search.registerQueryTransformer({ transformQuery: q => `${q} bar` }) | ||
|
Member
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. Minor, but |
||
| extensionHost.search.registerQueryTransformer({ transformQuery: q => `${q} qux` }) | ||
| await ready | ||
| assert.deepStrictEqual( | ||
| await clientController.registries.queryTransformer | ||
| .transformQuery('foo') | ||
| .pipe(take(1)) | ||
| .toPromise(), | ||
| 'foo bar qux' | ||
| ) | ||
| }) | ||
| }) | ||
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
| Original file line number | Diff line number | Diff line change |
|---|---|---|
|
|
@@ -785,6 +785,38 @@ declare module 'sourcegraph' { | |
| ): Unsubscribable | ||
| } | ||
|
|
||
| /** | ||
| * A query transformer alters a user's search query before executing a search. | ||
| * | ||
| * Query transformers allow extensions to define new search query operators and syntax, for example, | ||
| * by matching strings in a query (e.g. `go.imports:`) and replacing them with a regular expression or string. | ||
| */ | ||
| export interface QueryTransformer { | ||
| /** | ||
| * Transforms a search query into another, valid query. If there are no transformations to be made | ||
|
Member
There was a problem hiding this comment. Choose a reason for hiding this commentThe 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" |
||
| * the original query is returned. | ||
| * | ||
| * @param query A search query. | ||
| */ | ||
| transformQuery(query: string): string | Promise<string> | ||
| } | ||
|
|
||
| /** | ||
| * API for extensions to augment search functionality. | ||
| */ | ||
| export namespace search { | ||
| /** | ||
| * Registers a query transformer. | ||
| * | ||
| * Multiple transformers can be registered. In that case, all transformations will be applied | ||
| * and the result is a single query that has been altered by all transformers. The order in | ||
| * which transfomers are applied is not defined. | ||
| * | ||
| * @param provider A query transformer. | ||
| */ | ||
| export function registerQueryTransformer(provider: QueryTransformer): Unsubscribable | ||
| } | ||
|
|
||
| /** | ||
| * Commands are functions that are implemented and registered by extensions. Extensions can invoke any command | ||
| * (including commands registered by other extensions). The extension can also define contributions (in | ||
|
|
||
Add this suggestion to a batch that can be applied as a single commit.
This suggestion is invalid because no changes were made to the code.
Suggestions cannot be applied while the pull request is closed.
Suggestions cannot be applied while viewing a subset of changes.
Only one suggestion per line can be applied in a batch.
Add this suggestion to a batch that can be applied as a single commit.
Applying suggestions on deleted lines is not supported.
You must change the existing code in this line in order to create a valid suggestion.
Outdated suggestions cannot be applied.
This suggestion has been applied or marked resolved.
Suggestions cannot be applied from pending reviews.
Suggestions cannot be applied on multi-line comments.
Suggestions cannot be applied while the pull request is queued to merge.
Suggestion cannot be applied right now. Please check back later.
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).