Skip to content

Commit

Permalink
feat: support for finding symbol references with souregraph (#329)
Browse files Browse the repository at this point in the history
  • Loading branch information
conwnet authored Jun 23, 2021
1 parent 722a7b6 commit 028cbd1
Show file tree
Hide file tree
Showing 6 changed files with 333 additions and 105 deletions.
95 changes: 4 additions & 91 deletions extensions/github1s/src/interfaces/sourcegraph/definition.ts
Original file line number Diff line number Diff line change
Expand Up @@ -4,11 +4,8 @@
*/

import { gql } from '@apollo/client/core';
import {
escapeRegexp,
sourcegraphClient,
getRepoRefQueryString,
} from './common';
import { sourcegraphClient } from './common';
import { getSymbolPositions } from './position';

export interface SymbolDefinition {
precise: boolean;
Expand Down Expand Up @@ -108,85 +105,6 @@ const getLSIFDefinitions = async (
});
};

const searchDefinitionsQuery = gql`
query($query: String!) {
search(query: $query) {
results {
results {
... on FileMatch {
symbols {
name
kind
location {
resource {
path
repository {
name
}
commit {
oid
}
}
range {
start {
line
character
}
end {
line
character
}
}
}
}
}
}
}
}
}
`;

// get symbol definitions base on search
export const getSearchDefinitions = async (
owner: string,
repo: string,
ref: string,
symbol: string
): Promise<SymbolDefinition[]> => {
const repoRefString = getRepoRefQueryString(owner, repo, ref);
const optionsString = [
'context:global',
'type:symbol',
'patternType:regexp',
'case:yes',
].join(' ');
const patternString = `^${escapeRegexp(symbol)}$`;
const query = [repoRefString, optionsString, patternString].join(' ');
const response = await sourcegraphClient.query({
query: searchDefinitionsQuery,
variables: { query },
});

const resultSymbols = response?.data?.search?.results?.results?.flatMap(
(item) => item.symbols
);
return (resultSymbols || []).map((symbol) => {
const { resource, range } = symbol.location;
const [owner, repo] = resource.repository.name
.split('/')
.filter(Boolean)
.slice(-2);
return {
precise: false,
owner,
repo,
ref: resource.commit.oid,
path: `/${resource.path}`,
range,
};
});
};

export const getSymbolDefinitions = (
owner: string,
repo: string,
Expand All @@ -208,17 +126,12 @@ export const getSymbolDefinitions = (
line,
character
);
const searchDefinitionsPromise = getSearchDefinitions(
owner,
repo,
ref,
symbol
);
const searchDefinitionsPromise = getSymbolPositions(owner, repo, ref, symbol);

return LSIFDefinitionsPromise.then((LSIFDefinitions) => {
if (LSIFDefinitions.length) {
return LSIFDefinitions;
}
return searchDefinitionsPromise;
return searchDefinitionsPromise as Promise<SymbolDefinition[]>;
});
};
109 changes: 109 additions & 0 deletions extensions/github1s/src/interfaces/sourcegraph/position.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,109 @@
/**
* @file Sourcegraph api for searching symbol positions
* @author netcon
*/

import { gql } from '@apollo/client/core';
import {
escapeRegexp,
sourcegraphClient,
getRepoRefQueryString,
} from './common';

export interface SymbolPosition {
precise: boolean;
owner: string;
repo: string;
ref: string;
path: string;
range: {
start: {
line: number;
character: number;
};
end: {
line: number;
character: number;
};
};
}

const searchPositionsQuery = gql`
query($query: String!) {
search(query: $query) {
results {
results {
... on FileMatch {
symbols {
name
kind
location {
resource {
path
repository {
name
}
commit {
oid
}
}
range {
start {
line
character
}
end {
line
character
}
}
}
}
}
}
}
}
}
`;

// get symbol position information base on search,
// used by definition, reference and hover
export const getSymbolPositions = async (
owner: string,
repo: string,
ref: string,
symbol: string
): Promise<SymbolPosition[]> => {
const repoRefString = getRepoRefQueryString(owner, repo, ref);
const optionsString = [
'context:global',
'type:symbol',
'patternType:regexp',
'case:yes',
].join(' ');
const patternString = `^${escapeRegexp(symbol)}$`;
const query = [repoRefString, optionsString, patternString].join(' ');
const response = await sourcegraphClient.query({
query: searchPositionsQuery,
variables: { query },
});

const resultSymbols = response?.data?.search?.results?.results?.flatMap(
(item) => item.symbols
);
return (resultSymbols || []).map((symbol) => {
const { resource, range } = symbol.location;
const [owner, repo] = resource.repository.name
.split('/')
.filter(Boolean)
.slice(-2);
return {
precise: false,
owner,
repo,
ref: resource.commit.oid,
path: `/${resource.path}`,
range,
};
});
};
137 changes: 137 additions & 0 deletions extensions/github1s/src/interfaces/sourcegraph/reference.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,137 @@
/**
* @file Sourcegraph reference api
* @author netcon
*/

import { gql } from '@apollo/client/core';
import { sourcegraphClient } from './common';
import { getSymbolPositions } from './position';

export interface SymbolReference {
precise: boolean;
owner: string;
repo: string;
ref: string;
path: string;
range: {
start: {
line: number;
character: number;
};
end: {
line: number;
character: number;
};
};
}

const LSIFReferencesQuery = gql`
query(
$repository: String!
$ref: String!
$path: String!
$line: Int!
$character: Int!
) {
repository(name: $repository) {
commit(rev: $ref) {
blob(path: $path) {
lsif {
references(line: $line, character: $character) {
nodes {
resource {
path
repository {
name
}
commit {
oid
}
}
range {
start {
line
character
}
end {
line
character
}
}
}
}
}
}
}
}
}
`;

// find references with Sourcegraph LSIF
// https://docs.sourcegraph.com/code_intelligence/explanations/precise_code_intelligence
const getLSIFReferences = async (
owner: string,
repo: string,
ref: string,
path: string,
line: number,
character: number
): Promise<SymbolReference[]> => {
const response = await sourcegraphClient.query({
query: LSIFReferencesQuery,
variables: {
repository: `github.com/${owner}/${repo}`,
ref,
path: path.slice(1),
line,
character,
},
});
const referenceNodes =
response?.data?.repository?.commit?.blob?.lsif?.references?.nodes;
return (referenceNodes || []).map(({ resource, range }) => {
const [owner, repo] = resource.repository.name
.split('/')
.filter(Boolean)
.slice(-2);
return {
precise: true,
owner,
repo,
ref: resource.commit.oid,
path: `/${resource.path}`,
range,
};
});
};

export const getSymbolReferences = (
owner: string,
repo: string,
ref: string,
path: string,
line: number,
character: number,
symbol: string
): Promise<SymbolReference[]> => {
// if failed to find references from LSIF,
// fallback to search-based references, using
// two promise instead of `await` to request in
// parallel for getting result as soon as possible
const LSIFReferencesPromise = getLSIFReferences(
owner,
repo,
ref,
path,
line,
character
);
const searchReferencesPromise = getSymbolPositions(owner, repo, ref, symbol);

return LSIFReferencesPromise.then((LSIFReferences) => {
if (LSIFReferences.length) {
return LSIFReferences;
}
return searchReferencesPromise as Promise<SymbolReference[]>;
});
};
Loading

0 comments on commit 028cbd1

Please sign in to comment.