diff --git a/x-pack/plugins/code/public/actions/structure.ts b/x-pack/plugins/code/public/actions/structure.ts index 5b7639d95619f..98d83bb589982 100644 --- a/x-pack/plugins/code/public/actions/structure.ts +++ b/x-pack/plugins/code/public/actions/structure.ts @@ -10,6 +10,7 @@ import { SymbolInformation } from 'vscode-languageserver-types/lib/esm/main'; export interface SymbolsPayload { path: string; data: SymbolInformation[]; + structureTree: any; } export const loadStructure = createAction('LOAD STRUCTURE'); diff --git a/x-pack/plugins/code/public/reducers/symbol.ts b/x-pack/plugins/code/public/reducers/symbol.ts index b85b7ba36d0f5..c89c03e54f0f4 100644 --- a/x-pack/plugins/code/public/reducers/symbol.ts +++ b/x-pack/plugins/code/public/reducers/symbol.ts @@ -19,16 +19,11 @@ import { } from '../actions'; import { languageServerInitializing } from '../actions/language_server'; -const SPECIAL_SYMBOL_NAME = '{...}'; -const SPECIAL_CONTAINER_NAME = ''; - export interface SymbolWithMembers extends SymbolInformation { members?: SymbolWithMembers[]; path?: string; } -type Container = SymbolWithMembers | undefined; - export interface SymbolState { symbols: { [key: string]: SymbolInformation[] }; structureTree: { [key: string]: SymbolWithMembers[] }; @@ -47,77 +42,6 @@ const initialState: SymbolState = { languageServerInitializing: false, }; -const sortSymbol = (a: SymbolWithMembers, b: SymbolWithMembers) => { - const lineDiff = a.location.range.start.line - b.location.range.start.line; - if (lineDiff === 0) { - return a.location.range.start.character - b.location.range.start.character; - } else { - return lineDiff; - } -}; - -const generateStructureTree: (symbols: SymbolInformation[]) => any = symbols => { - const structureTree: SymbolWithMembers[] = []; - - function findContainer( - tree: SymbolWithMembers[], - containerName?: string - ): SymbolInformation | undefined { - if (containerName === undefined) { - return undefined; - } - const regex = new RegExp(`^${containerName}[<(]?.*[>)]?$`); - const result = tree.find((s: SymbolInformation) => { - return regex.test(s.name); - }); - if (result) { - return result; - } else { - const subTree = tree - .filter(s => s.members) - .map(s => s.members) - .flat(); - if (subTree.length > 0) { - return findContainer(subTree, containerName); - } else { - return undefined; - } - } - } - - symbols - .sort(sortSymbol) - .forEach((s: SymbolInformation, index: number, arr: SymbolInformation[]) => { - let container: Container; - /** - * For Enum class in Java, the container name and symbol name that LSP gives are special. - * For more information, see https://github.com/elastic/codesearch/issues/580 - */ - if (s.containerName === SPECIAL_CONTAINER_NAME) { - container = _.findLast( - arr.slice(0, index), - (sy: SymbolInformation) => sy.name === SPECIAL_SYMBOL_NAME - ); - } else { - container = findContainer(structureTree, s.containerName); - } - if (container) { - if (!container.path) { - container.path = container.name; - } - if (container.members) { - container.members.push({ ...s, path: `${container.path}/${s.name}` }); - } else { - container.members = [{ ...s, path: `${container.path}/${s.name}` }]; - } - } else { - structureTree.push({ ...s, path: s.name }); - } - }); - - return structureTree; -}; - export const symbol = handleActions( { [String(loadStructure)]: (state: SymbolState, action: Action) => @@ -128,8 +52,8 @@ export const symbol = handleActions( [String(loadStructureSuccess)]: (state: SymbolState, action: Action) => produce(state, (draft: SymbolState) => { draft.loading = false; - const { path, data } = action.payload!; - draft.structureTree[path] = generateStructureTree(data); + const { path, data, structureTree } = action.payload!; + draft.structureTree[path] = structureTree; draft.symbols = { ...state.symbols, [path]: data, diff --git a/x-pack/plugins/code/public/sagas/structure.ts b/x-pack/plugins/code/public/sagas/structure.ts index e8e26bca18c27..ccc788b029008 100644 --- a/x-pack/plugins/code/public/sagas/structure.ts +++ b/x-pack/plugins/code/public/sagas/structure.ts @@ -7,13 +7,92 @@ import { Action } from 'redux-actions'; import { delay } from 'redux-saga'; import { call, put, takeEvery, cancel, take, fork } from 'redux-saga/effects'; +import { SymbolInformation } from 'vscode-languageserver-types/lib/esm/main'; import { LspRestClient, TextDocumentMethods } from '../../common/lsp_client'; import { loadStructure, loadStructureFailed, loadStructureSuccess } from '../actions'; import { ServerNotInitialized } from '../../common/lsp_error_codes'; import { languageServerInitializing } from '../actions/language_server'; +import { SymbolWithMembers } from '../reducers/symbol'; const STRUCTURE_TREE_POLLING_INTERVAL_SEC = 3; +type Container = SymbolWithMembers | undefined; + +const SPECIAL_SYMBOL_NAME = '{...}'; +const SPECIAL_CONTAINER_NAME = ''; + +const sortSymbol = (a: SymbolWithMembers, b: SymbolWithMembers) => { + const lineDiff = a.location.range.start.line - b.location.range.start.line; + if (lineDiff === 0) { + return a.location.range.start.character - b.location.range.start.character; + } else { + return lineDiff; + } +}; + +const generateStructureTree: (symbols: SymbolInformation[]) => any = symbols => { + const structureTree: SymbolWithMembers[] = []; + + function findContainer( + tree: SymbolWithMembers[], + containerName?: string + ): SymbolInformation | undefined { + if (containerName === undefined) { + return undefined; + } + const regex = new RegExp(`^${containerName}[<(]?.*[>)]?$`); + const result = tree.find((s: SymbolInformation) => { + return regex.test(s.name); + }); + if (result) { + return result; + } else { + // TODO: Use Array.flat once supported + const subTree = tree.reduce( + (s, t) => (t.members ? s.concat(t.members) : s), + [] as SymbolWithMembers[] + ); + if (subTree.length > 0) { + return findContainer(subTree, containerName); + } else { + return undefined; + } + } + } + + symbols + .sort(sortSymbol) + .forEach((s: SymbolInformation, index: number, arr: SymbolInformation[]) => { + let container: Container; + /** + * For Enum class in Java, the container name and symbol name that LSP gives are special. + * For more information, see https://github.com/elastic/codesearch/issues/580 + */ + if (s.containerName === SPECIAL_CONTAINER_NAME) { + container = _.findLast( + arr.slice(0, index), + (sy: SymbolInformation) => sy.name === SPECIAL_SYMBOL_NAME + ); + } else { + container = findContainer(structureTree, s.containerName); + } + if (container) { + if (!container.path) { + container.path = container.name; + } + if (container.members) { + container.members.push({ ...s, path: `${container.path}/${s.name}` }); + } else { + container.members = [{ ...s, path: `${container.path}/${s.name}` }]; + } + } else { + structureTree.push({ ...s, path: s.name }); + } + }); + + return structureTree; +}; + function requestStructure(uri?: string) { const lspClient = new LspRestClient('/api/code/lsp'); const lspMethods = new TextDocumentMethods(lspClient); @@ -42,7 +121,8 @@ function* pollingSaga(action: Action) { while (true) { try { const data = yield call(requestStructure, `git:/${action.payload}`); - yield put(loadStructureSuccess({ path: action.payload!, data })); + const structureTree = generateStructureTree(data); + yield put(loadStructureSuccess({ path: action.payload!, data, structureTree })); } catch (e) { if (e.code && e.code === ServerNotInitialized) { yield put(languageServerInitializing());