Skip to content
This repository was archived by the owner on Mar 18, 2024. It is now read-only.
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
117 changes: 45 additions & 72 deletions package/src/handler.test.ts
Original file line number Diff line number Diff line change
@@ -1,103 +1,76 @@
import * as assert from 'assert'
import { Handler } from './handler'
import { Result } from './api'
import { Position } from 'sourcegraph'

interface SearchTest {
crossRepo?: boolean
doc: {
uri: string
text: string
}
expSearchQueries: string[]
}
import { referencesQueries, definitionQueries } from './handler'
import { TextDocument } from 'sourcegraph'

describe('search requests', () => {
it('makes correct search requests for goto definition', async () => {
const tests: SearchTest[] = [
{
crossRepo: undefined,
doc: {
uri: 'git://github.com/foo/bar?rev#file.c',
text: 'token',
},
expSearchQueries: [
'\\btoken\\b case:yes file:.(h|c|hpp|cpp|m|cc)$ type:file',
],
},
{
crossRepo: true,
doc: {
uri: 'git://github.com/foo/bar?rev#file.c',
text: 'token',
},
expSearchQueries: [
'\\btoken\\b case:yes file:.(h|c|hpp|cpp|m|cc)$ type:symbol',
'\\btoken\\b case:yes file:.(h|c|hpp|cpp|m|cc)$ type:file',
],
},
interface DefinitionTest {
doc: TextDocument
definitionPatterns?: string[]
expectedSearchQueries: string[]
}
const tests: DefinitionTest[] = [
{
crossRepo: false,
doc: {
uri: 'git://github.com/foo/bar?rev#file.c',
uri: 'git://github.com/foo/bar?rev#file.cpp',
languageId: 'cpp',
text: 'token',
},
expSearchQueries: [
'\\btoken\\b case:yes file:.(h|c|hpp|cpp|m|cc)$ type:symbol repo:^github.com/foo/bar$@rev',
'\\btoken\\b case:yes file:.(h|c|hpp|cpp|m|cc)$ type:file',
definitionPatterns: ['const\\s%s\\s='],
expectedSearchQueries: [
// current file
'const\\stoken\\s= case:yes file:.(cpp)$ type:file repo:^github.com/foo/bar$@rev file:^file.cpp$',
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 the dots in the file: query should be escaped with a backslash

Copy link
Contributor Author

Choose a reason for hiding this comment

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

Ah, yup, and in general the string needs to be run through something like escapeRegexChars() (I made that up).

// current repo symbols
'^token$ case:yes file:.(cpp)$ type:symbol repo:^github.com/foo/bar$@rev',
// current repo definition patterns
'const\\stoken\\s= case:yes file:.(cpp)$ type:file repo:^github.com/foo/bar$@rev',
// all repos definition patterns
'const\\stoken\\s= case:yes file:.(cpp)$ type:file',
],
},
]

for (const test of tests) {
const h = new Handler({ languageID: 'l' })
const searchQueries: string[] = []
h.api.search = (searchQuery: string): Promise<Result[]> => {
searchQueries.push(searchQuery)
return Promise.resolve([])
}
await h.definition(
{
uri: test.doc.uri,
languageId: 'l',
text: test.doc.text,
},
{ line: 0, character: 0 } as Position
assert.deepStrictEqual(
definitionQueries({
searchToken: 'token',
doc: test.doc,
fileExts: ['cpp'],
definitionPatterns: test.definitionPatterns || [],
}),
test.expectedSearchQueries
)
assert.deepStrictEqual(test.expSearchQueries, searchQueries)
}
})

it('makes correct search requests for references', async () => {
const tests: SearchTest[] = [
interface ReferencesTest {
doc: TextDocument
expectedSearchQueries: string[]
}
const tests: ReferencesTest[] = [
{
doc: {
uri: 'git://github.com/foo/bar?rev#file.c',
uri: 'git://github.com/foo/bar?rev#file.cpp',
languageId: 'cpp',
text: 'token',
},
expSearchQueries: [
'\\btoken\\b case:yes file:.(h|c|hpp|cpp|m|cc)$ type:file repo:^github.com/foo/bar$@rev',
'\\btoken\\b case:yes file:.(h|c|hpp|cpp|m|cc)$ type:file -repo:^github.com/foo/bar$',
expectedSearchQueries: [
'\\btoken\\b case:yes file:.(cpp)$ type:file repo:^github.com/foo/bar$@rev',
'\\btoken\\b case:yes file:.(cpp)$ type:file -repo:^github.com/foo/bar$',
],
},
]

for (const test of tests) {
const h = new Handler({ languageID: 'l' })
const searchQueries: string[] = []
h.api.search = (searchQuery: string): Promise<Result[]> => {
searchQueries.push(searchQuery)
return Promise.resolve([])
}
await h.references(
{
uri: test.doc.uri,
languageId: 'l',
text: test.doc.text,
},
{ line: 0, character: 0 } as Position
assert.deepStrictEqual(
referencesQueries({
searchToken: 'token',
doc: test.doc,
fileExts: ['cpp'],
}),
test.expectedSearchQueries
)
assert.deepStrictEqual(test.expSearchQueries, searchQueries)
}
})
})
135 changes: 84 additions & 51 deletions package/src/handler.ts
Original file line number Diff line number Diff line change
@@ -1,6 +1,6 @@
import { API, Result, parseUri } from './api'
import * as sprintf from 'sprintf-js'
import { takeWhile, dropWhile, sortBy } from 'lodash'
import { takeWhile, dropWhile, sortBy, flatten } from 'lodash'
import {
DocumentSelector,
Location,
Expand Down Expand Up @@ -263,6 +263,72 @@ function sortByProximity({
})
}

export function definitionQueries({
searchToken,
doc,
fileExts,
definitionPatterns,
}: {
searchToken: string
doc: TextDocument
fileExts: string[]
definitionPatterns: string[]
}): string[] {
const patternQuery = (
scope: Scope,
patterns: string[]
): string | undefined => {
return patterns.length === 0
? undefined
: makeQuery({
searchToken: patterns
.map(pattern =>
sprintf.sprintf(`${pattern}`, searchToken)
)
.join('|'),
searchType: 'file',
currentFileUri: doc.uri,
scope,
fileExts,
})
}

return [
patternQuery('current file', definitionPatterns),
makeQuery({
searchToken: `^${searchToken}$`,
searchType: 'symbol',
currentFileUri: doc.uri,
scope: 'current repository',
fileExts,
}),
patternQuery('current repository', definitionPatterns),
patternQuery('all repositories', definitionPatterns),
].filter((query): query is string => Boolean(query))
}

export function referencesQueries({
searchToken,
doc,
fileExts,
}: {
searchToken: string
doc: TextDocument
fileExts: string[]
}): string[] {
const from = (scope: Scope): string =>
makeQuery({
searchToken: `\\b${searchToken}\\b`,
searchType: 'file',
currentFileUri: doc.uri,
scope,
fileExts,
})
// ⚠️ This CANNOT be simplified to `[from('all repositories')]` because
// searches that span all repositories always fail on Sourcegraph.com.
return [from('current repository'), from('other repositories')]
}

/**
* @see package.json contributes.configuration section for the configuration schema.
*/
Expand Down Expand Up @@ -582,39 +648,12 @@ export class Handler {
}
const searchToken = tokenResult.searchToken

const patternQuery = (
scope: Scope,
patterns: string[]
): string | undefined => {
return patterns.length === 0
? undefined
: makeQuery({
searchToken: patterns
.map(pattern =>
sprintf.sprintf(`${pattern}`, searchToken)
)
.join('|'),
searchType: 'file',
currentFileUri: doc.uri,
scope,
fileExts: this.fileExts,
})
}

const queries = [
patternQuery('current file', this.definitionPatterns),
makeQuery({
searchToken: `^${searchToken}$`,
searchType: 'symbol',
currentFileUri: doc.uri,
scope: 'current repository',
fileExts: this.fileExts,
}),
patternQuery('current repository', this.definitionPatterns),
patternQuery('all repositories', this.definitionPatterns),
].filter((query): query is string => Boolean(query))

for (const query of queries) {
for (const query of definitionQueries({
searchToken,
doc,
fileExts: this.fileExts,
definitionPatterns: this.definitionPatterns,
})) {
const symbolResults = (await this.api.search(query)).map(result =>
resultToLocation({ result, sourcegraph: this.sourcegraph })
)
Expand Down Expand Up @@ -647,25 +686,19 @@ export class Handler {
}
const searchToken = tokenResult.searchToken

const referencesFrom = async (scope: Scope): Promise<Location[]> =>
(await this.api.search(
makeQuery({
searchToken: `\\b${searchToken}\\b`,
searchType: 'file',
currentFileUri: doc.uri,
scope,
fileExts: this.fileExts,
})
)).map(result =>
resultToLocation({ result, sourcegraph: this.sourcegraph })
)

return sortByProximity({
currentLocation: doc.uri,
locations: [
...(await referencesFrom('current repository')),
...(await referencesFrom('other repositories')),
],
locations: flatten(
await Promise.all(
referencesQueries({
searchToken,
doc,
fileExts: this.fileExts,
}).map(query => this.api.search(query))
)
).map(result =>
resultToLocation({ result, sourcegraph: this.sourcegraph })
),
})
}
}