Skip to content
This repository has been archived by the owner on Sep 2, 2020. It is now read-only.

lsp: textDocument/documentSymbol support #195

Open
wants to merge 4 commits into
base: master
Choose a base branch
from
Open
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
3 changes: 3 additions & 0 deletions .vscode/settings.json
Original file line number Diff line number Diff line change
@@ -0,0 +1,3 @@
{
"javascript.validate.enable": false
}
2 changes: 1 addition & 1 deletion README.md
Original file line number Diff line number Diff line change
Expand Up @@ -116,6 +116,6 @@ For each transport, there is a slight difference in JSON message format, especia
| -------------------:|------------------------------|-----------------------------------|
| Diagnostics | `getDiagnostics` | `textDocument/publishDiagnostics` |
| Autocompletion | `getAutocompleteSuggestions` | `textDocument/completion` |
| Outline | `getOutline` | Not supported yet |
| Outline | `getOutline` | `textDocument/documentSymbol` |
| Go-to definition | `getDefinition` | Not supported yet |
| File Events | Not supported yet | `didOpen/didClose/didSave/didChange` events |
6 changes: 6 additions & 0 deletions packages/interface/src/GraphQLLanguageService.js
Original file line number Diff line number Diff line change
Expand Up @@ -21,6 +21,7 @@ import type {
GraphQLCache,
GraphQLConfig,
GraphQLProjectConfig,
Outline,
Uri,
} from 'graphql-language-service-types';
import type {Position} from 'graphql-language-service-utils';
Expand All @@ -46,6 +47,7 @@ import {
getDefinitionQueryResultForFragmentSpread,
getDefinitionQueryResultForDefinitionNode,
} from './getDefinition';
import {getOutline} from './getOutline';
import {getASTNodeAtPosition} from 'graphql-language-service-utils';

export class GraphQLLanguageService {
Expand Down Expand Up @@ -244,4 +246,8 @@ export class GraphQLLanguageService {

return result;
}

async getOutline(query: string): Promise<?Outline> {
return getOutline(query);
}
}
1 change: 1 addition & 0 deletions packages/interface/src/getOutline.js
Original file line number Diff line number Diff line change
Expand Up @@ -56,6 +56,7 @@ function outlineTreeConverter(docText: string): OutlineTreeConverterType {
representativeName: node.name,
startPosition: offsetToPosition(docText, node.loc.start),
endPosition: offsetToPosition(docText, node.loc.end),
kind: node.kind,
children: node.selectionSet || [],
});
return {
Expand Down
57 changes: 57 additions & 0 deletions packages/server/src/MessageProcessor.js
Original file line number Diff line number Diff line change
Expand Up @@ -35,6 +35,9 @@ import {
InitializeResult,
Location,
PublishDiagnosticsParams,
DocumentSymbolParams,
SymbolInformation,
SymbolKind,
} from 'vscode-languageserver';

import {getGraphQLCache} from './GraphQLCache';
Expand All @@ -48,6 +51,13 @@ type CachedDocumentType = {
contents: Array<CachedContent>,
};

const KIND_TO_SYMBOL_KIND = {
Field: SymbolKind.Field,
OperationDefinition: SymbolKind.Class,
FragmentDefinition: SymbolKind.Class,
FragmentSpread: SymbolKind.Struct,
Copy link
Contributor

Choose a reason for hiding this comment

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

Should we look to add more symbols in the future, e.g. ObjectTypeExtension and such?

Copy link
Contributor Author

Choose a reason for hiding this comment

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

Sounds reasonable to me - I haven't worked very much with the SDL, so I only implemented what the outline code already does here: https://github.com/stephen/graphql-language-service/blob/90c4c5899f3f14216794636749210386fa749427/packages/interface/src/getOutline.js#L21-L30

};

export class MessageProcessor {
_graphQLCache: GraphQLCache;
_languageService: GraphQLLanguageService;
Expand Down Expand Up @@ -79,6 +89,7 @@ export class MessageProcessor {
const serverCapabilities: ServerCapabilities = {
capabilities: {
completionProvider: {resolveProvider: true},
documentSymbolProvider: true,
definitionProvider: true,
textDocumentSync: 1,
},
Expand Down Expand Up @@ -416,6 +427,52 @@ export class MessageProcessor {
return formatted;
}

async handleDocumentSymbolRequest(
params: DocumentSymbolParams.type,
): Promise<Array<SymbolInformation>> {
if (!this._isInitialized) {
return [];
}

if (!params || !params.textDocument) {
throw new Error('`textDocument` argument is required.');
}

const textDocument = params.textDocument;
const cachedDocument = this._getCachedDocument(textDocument.uri);
if (!cachedDocument) {
throw new Error('A cached document cannot be found.');
}

const outline = await this._languageService.getOutline(
cachedDocument.contents[0].query,
);
if (!outline) {
return [];
}

const output: Array<SymbolInformation> = [];
const input = outline.outlineTrees.map(tree => [null, tree]);
while (input.length > 0) {
const [parent, tree] = input.pop();
output.push({
name: tree.representativeName,
kind: KIND_TO_SYMBOL_KIND[tree.kind],
location: {
uri: textDocument.uri,
range: {
start: tree.startPosition,
end: tree.endPosition,
},
},
containerName: parent ? parent.representativeName : undefined,
});
input.push(...tree.children.map(child => [tree, child]));
}

return output;
}

_isRelayCompatMode(query: string): boolean {
return (
query.indexOf('RelayCompat') !== -1 ||
Expand Down
49 changes: 49 additions & 0 deletions packages/server/src/__tests__/MessageProcessor-test.js
Original file line number Diff line number Diff line change
Expand Up @@ -10,6 +10,7 @@

import {expect} from 'chai';
import {Position, Range} from 'graphql-language-service-utils';
import {SymbolKind} from 'vscode-languageserver-types';
import {beforeEach, describe, it} from 'mocha';

import {MessageProcessor} from '../MessageProcessor';
Expand Down Expand Up @@ -51,6 +52,19 @@ describe('MessageProcessor', () => {
getDiagnostics: (query, uri) => {
return [];
},
getOutline: query => {
return {
outlineTrees: [
{
representativeName: 'item',
kind: 'Field',
startPosition: {line: 1, character: 2},
endPosition: {line: 1, character: 4},
children: [],
},
],
};
},
};
});
messageProcessor._isInitialized = true;
Expand Down Expand Up @@ -164,4 +178,39 @@ describe('MessageProcessor', () => {
const result = await messageProcessor.handleDefinitionRequest(test);
expect(result[0].uri).to.equal(`file://${queryDir}/testFragment.graphql`);
});

it('runs document symbol requests', async () => {
const validQuery = `
{
hero(episode: EMPIRE){
...testFragment
}
}
`;

const newDocument = {
textDocument: {
text: validQuery,
uri: `${queryDir}/test3.graphql`,
version: 0,
},
};

await messageProcessor.handleDidOpenOrSaveNotification(newDocument);

const test = {
textDocument: newDocument.textDocument,
};

const result = await messageProcessor.handleDocumentSymbolRequest(test);

expect(result).to.not.be.undefined;
expect(result.length).to.equal(1);
expect(result[0].name).to.equal('item');
expect(result[0].kind).to.equal(SymbolKind.Field);
expect(result[0].location.range).to.deep.equal({
start: {line: 1, character: 2},
end: {line: 1, character: 4},
});
});
});
4 changes: 4 additions & 0 deletions packages/server/src/startServer.js
Original file line number Diff line number Diff line change
Expand Up @@ -34,6 +34,7 @@ import {
ExitNotification,
InitializeRequest,
PublishDiagnosticsNotification,
DocumentSymbolRequest,
ShutdownRequest,
} from 'vscode-languageserver';

Expand Down Expand Up @@ -168,4 +169,7 @@ function addHandlers(
connection.onRequest(DefinitionRequest.type, params =>
messageProcessor.handleDefinitionRequest(params),
);
connection.onRequest(DocumentSymbolRequest.type, (params, token) =>
messageProcessor.handleDocumentSymbolRequest(params),
);
}
1 change: 1 addition & 0 deletions packages/types/src/index.js
Original file line number Diff line number Diff line change
Expand Up @@ -259,6 +259,7 @@ export type OutlineTree = {
tokenizedText?: TokenizedText,
representativeName?: string,

kind: string,
startPosition: Position,
endPosition?: Position,
children: Array<OutlineTree>,
Expand Down