mirror of
https://github.com/elastic/kibana.git
synced 2025-04-24 09:48:58 -04:00
This commit is contained in:
parent
dc24720f62
commit
1616ba5ffc
10 changed files with 175 additions and 73 deletions
|
@ -6,6 +6,7 @@
|
|||
|
||||
export enum RepoFileStatus {
|
||||
LANG_SERVER_IS_INITIALIZING = 'Language server is initializing.',
|
||||
LANG_SERVER_INITIALIZED = 'Language server initialized.',
|
||||
INDEXING = 'Indexing in progress',
|
||||
FILE_NOT_SUPPORTED = 'Current file is not of a supported language.',
|
||||
REVISION_NOT_INDEXED = 'Current revision is not indexed.',
|
||||
|
@ -60,8 +61,9 @@ export const REPO_FILE_STATUS_SEVERITY = {
|
|||
export interface StatusReport {
|
||||
repoStatus?: RepoFileStatus.INDEXING | RepoFileStatus.REVISION_NOT_INDEXED;
|
||||
fileStatus?: RepoFileStatus.FILE_NOT_SUPPORTED | RepoFileStatus.FILE_IS_TOO_BIG;
|
||||
langServerType: LangServerType;
|
||||
langServerType?: LangServerType;
|
||||
langServerStatus?:
|
||||
| RepoFileStatus.LANG_SERVER_IS_INITIALIZING
|
||||
| RepoFileStatus.LANG_SERVER_NOT_INSTALLED;
|
||||
| RepoFileStatus.LANG_SERVER_NOT_INSTALLED
|
||||
| RepoFileStatus.LANG_SERVER_INITIALIZED;
|
||||
}
|
||||
|
|
|
@ -70,3 +70,8 @@ export const FetchRepoFileStatusSuccess = createAction<{
|
|||
statusReport: StatusReport;
|
||||
}>('FETCH REPO FILE STATUS SUCCESS');
|
||||
export const FetchRepoFileStatusFailed = createAction<any>('FETCH REPO FILE STATUS FAILED');
|
||||
|
||||
export const StatusChanged = createAction<{
|
||||
prevStatus: StatusReport;
|
||||
currentStatus: StatusReport;
|
||||
}>('STATUS CHANGED');
|
||||
|
|
|
@ -30,7 +30,7 @@ import {
|
|||
currentTreeSelector,
|
||||
hasMoreCommitsSelector,
|
||||
repoUriSelector,
|
||||
statusSelector,
|
||||
repoStatusSelector,
|
||||
} from '../../selectors';
|
||||
import { encodeRevisionString } from '../../../common/uri_util';
|
||||
import { history } from '../../utils/url';
|
||||
|
@ -404,7 +404,7 @@ const mapStateToProps = (state: RootState) => ({
|
|||
branches: state.revision.branches,
|
||||
hasMoreCommits: hasMoreCommitsSelector(state),
|
||||
loadingCommits: state.revision.loadingCommits,
|
||||
repoStatus: statusSelector(state, repoUriSelector(state)),
|
||||
repoStatus: repoStatusSelector(state, repoUriSelector(state)),
|
||||
searchOptions: state.search.searchOptions,
|
||||
query: state.search.query,
|
||||
currentRepository: state.repository.repository,
|
||||
|
|
|
@ -75,12 +75,12 @@ export class StatusIndicatorComponent extends React.Component<Props, State> {
|
|||
if (fix !== undefined) {
|
||||
const fixUrl = this.fixUrl(fix);
|
||||
children.push(
|
||||
<p>
|
||||
<p key={`${error}_key`}>
|
||||
{error} You can {fixUrl}.
|
||||
</p>
|
||||
);
|
||||
} else {
|
||||
children.push(<p>{error}</p>);
|
||||
children.push(<p key={`${error}_key`}>{error}</p>);
|
||||
}
|
||||
}
|
||||
};
|
||||
|
@ -99,7 +99,13 @@ export class StatusIndicatorComponent extends React.Component<Props, State> {
|
|||
}
|
||||
}
|
||||
const svg = svgs[severity];
|
||||
const icon = <EuiButtonIcon iconType={svg} onClick={this.openPopover.bind(this)} />;
|
||||
const icon = (
|
||||
<EuiButtonIcon
|
||||
aria-label="status_indicator"
|
||||
iconType={svg}
|
||||
onClick={this.openPopover.bind(this)}
|
||||
/>
|
||||
);
|
||||
if (children.length === 0) {
|
||||
return <div />;
|
||||
}
|
||||
|
|
|
@ -7,6 +7,10 @@
|
|||
import { Hover } from 'vscode-languageserver-types';
|
||||
import { LspRestClient, TextDocumentMethods } from '../../../common/lsp_client';
|
||||
import { AsyncTask, Computer } from '../computer';
|
||||
import { store } from '../../stores/index';
|
||||
import { statusSelector } from '../../selectors';
|
||||
import { LangServerType, RepoFileStatus } from '../../../common/repo_file_status';
|
||||
import { ServerNotInitialized } from '../../../common/lsp_error_codes';
|
||||
|
||||
export const LOADING = 'loading';
|
||||
|
||||
|
@ -26,29 +30,68 @@ export class HoverComputer implements Computer<Hover> {
|
|||
}
|
||||
|
||||
public compute(): AsyncTask<Hover> {
|
||||
return this.lspMethods.hover.asyncTask({
|
||||
position: {
|
||||
line: this.range!.startLineNumber - 1,
|
||||
character: this.range!.startColumn - 1,
|
||||
const status = statusSelector(store.getState());
|
||||
if (
|
||||
status &&
|
||||
status.langServerType !== LangServerType.NONE &&
|
||||
status.fileStatus !== RepoFileStatus.FILE_NOT_SUPPORTED &&
|
||||
status.fileStatus !== RepoFileStatus.FILE_IS_TOO_BIG
|
||||
) {
|
||||
if (status.langServerStatus === RepoFileStatus.LANG_SERVER_IS_INITIALIZING) {
|
||||
return this.initializing();
|
||||
}
|
||||
|
||||
return this.lspMethods.hover.asyncTask({
|
||||
position: {
|
||||
line: this.range!.startLineNumber - 1,
|
||||
character: this.range!.startColumn - 1,
|
||||
},
|
||||
textDocument: {
|
||||
uri: this.uri!,
|
||||
},
|
||||
});
|
||||
}
|
||||
return this.empty();
|
||||
}
|
||||
|
||||
private empty(): AsyncTask<Hover> {
|
||||
const empty = {
|
||||
range: this.lspRange(),
|
||||
contents: [],
|
||||
};
|
||||
return {
|
||||
cancel(): void {},
|
||||
promise(): Promise<Hover> {
|
||||
return Promise.resolve(empty);
|
||||
},
|
||||
textDocument: {
|
||||
uri: this.uri!,
|
||||
};
|
||||
}
|
||||
|
||||
private initializing(): AsyncTask<Hover> {
|
||||
return {
|
||||
cancel(): void {},
|
||||
promise(): Promise<Hover> {
|
||||
return Promise.reject({ code: ServerNotInitialized });
|
||||
},
|
||||
});
|
||||
};
|
||||
}
|
||||
|
||||
private lspRange() {
|
||||
return {
|
||||
start: {
|
||||
line: this.range.startLineNumber - 1,
|
||||
character: this.range.startColumn - 1,
|
||||
},
|
||||
end: {
|
||||
line: this.range.endLineNumber - 1,
|
||||
character: this.range.endColumn - 1,
|
||||
},
|
||||
};
|
||||
}
|
||||
|
||||
public loadingMessage(): Hover {
|
||||
return {
|
||||
range: {
|
||||
start: {
|
||||
line: this.range.startLineNumber - 1,
|
||||
character: this.range.startColumn - 1,
|
||||
},
|
||||
end: {
|
||||
line: this.range.endLineNumber - 1,
|
||||
character: this.range.endColumn - 1,
|
||||
},
|
||||
},
|
||||
range: this.lspRange(),
|
||||
contents: LOADING,
|
||||
};
|
||||
}
|
||||
|
|
|
@ -27,6 +27,7 @@ import {
|
|||
StatusSuccessPayload,
|
||||
RepoStatus,
|
||||
RepoState,
|
||||
FetchRepoFileStatus,
|
||||
} from '../actions';
|
||||
import { StatusReport } from '../../common/repo_file_status';
|
||||
|
||||
|
@ -223,6 +224,10 @@ export const status = handleActions<StatusState, StatusPayload>(
|
|||
draft.repoFileStatus = statusReport;
|
||||
draft.currentStatusPath = path;
|
||||
}),
|
||||
[String(FetchRepoFileStatus)]: (state: StatusState, action: Action<any>) =>
|
||||
produce<StatusState>(state, (draft: StatusState) => {
|
||||
draft.currentStatusPath = action.payload;
|
||||
}),
|
||||
},
|
||||
initialState
|
||||
);
|
||||
|
|
|
@ -8,26 +8,29 @@ import { Action } from 'redux-actions';
|
|||
import { call, put, select, takeEvery, takeLatest } from 'redux-saga/effects';
|
||||
import { kfetch } from 'ui/kfetch';
|
||||
import { isEqual } from 'lodash';
|
||||
import { delay } from 'redux-saga';
|
||||
|
||||
import { RepositoryUri, WorkerReservedProgress } from '../../model';
|
||||
import {
|
||||
deleteRepoFinished,
|
||||
FetchFilePayload,
|
||||
FetchRepoFileStatus,
|
||||
FetchRepoFileStatusFailed,
|
||||
FetchRepoFileStatusSuccess,
|
||||
Match,
|
||||
routeChange,
|
||||
updateCloneProgress,
|
||||
updateDeleteProgress,
|
||||
pollRepoCloneStatusStop,
|
||||
pollRepoDeleteStatusStop,
|
||||
pollRepoIndexStatusStop,
|
||||
FetchRepoFileStatus,
|
||||
FetchRepoFileStatusSuccess,
|
||||
FetchRepoFileStatusFailed,
|
||||
FetchFilePayload,
|
||||
routeChange,
|
||||
StatusChanged,
|
||||
updateCloneProgress,
|
||||
updateDeleteProgress,
|
||||
} from '../actions';
|
||||
import * as ROUTES from '../components/routes';
|
||||
import { RootState } from '../reducers';
|
||||
import { mainRoutePattern } from './patterns';
|
||||
import { StatusReport } from '../../common/repo_file_status';
|
||||
import { RepoFileStatus, StatusReport } from '../../common/repo_file_status';
|
||||
import { statusSelector } from '../selectors';
|
||||
|
||||
const matchSelector = (state: RootState) => state.route.match;
|
||||
|
||||
|
@ -83,20 +86,45 @@ function* handleMainRouteChange(action: Action<Match>) {
|
|||
const newStatusPath: FetchFilePayload = { uri, revision, path };
|
||||
const currentStatusPath = yield select((state: RootState) => state.status.currentStatusPath);
|
||||
if (!isEqual(newStatusPath, currentStatusPath)) {
|
||||
yield call(fetchStatus, newStatusPath);
|
||||
yield call(startPollingStatus, newStatusPath);
|
||||
}
|
||||
}
|
||||
const STATUS_POLLING_FREQ_HIGH_MS = 3000;
|
||||
const STATUS_POLLING_FREQ_LOW_MS = 10000;
|
||||
|
||||
function* startPollingStatus(location: FetchFilePayload) {
|
||||
yield put(FetchRepoFileStatus(location));
|
||||
let currentStatusPath = yield select((state: RootState) => state.status.currentStatusPath);
|
||||
while (isEqual(location, currentStatusPath)) {
|
||||
const previousStatus: StatusReport = yield select(statusSelector);
|
||||
const newStatus: StatusReport = yield call(fetchStatus, location);
|
||||
yield call(compareStatus, previousStatus, newStatus);
|
||||
const delayMs =
|
||||
newStatus.langServerStatus === RepoFileStatus.LANG_SERVER_IS_INITIALIZING
|
||||
? STATUS_POLLING_FREQ_HIGH_MS
|
||||
: STATUS_POLLING_FREQ_LOW_MS;
|
||||
yield call(delay, delayMs);
|
||||
currentStatusPath = yield select((state: RootState) => state.status.currentStatusPath);
|
||||
}
|
||||
}
|
||||
|
||||
function* compareStatus(prevStatus: StatusReport, currentStatus: StatusReport) {
|
||||
if (!isEqual(prevStatus, currentStatus)) {
|
||||
yield put(StatusChanged({ prevStatus, currentStatus }));
|
||||
}
|
||||
}
|
||||
|
||||
function* fetchStatus(location: FetchFilePayload) {
|
||||
yield put(FetchRepoFileStatus(location));
|
||||
try {
|
||||
const newStatus = yield call(requestStatus, location);
|
||||
const newStatus: StatusReport = yield call(requestStatus, location);
|
||||
yield put(
|
||||
FetchRepoFileStatusSuccess({
|
||||
statusReport: newStatus as StatusReport,
|
||||
statusReport: newStatus,
|
||||
path: location,
|
||||
})
|
||||
);
|
||||
return newStatus;
|
||||
} catch (e) {
|
||||
yield put(FetchRepoFileStatusFailed(e));
|
||||
}
|
||||
|
@ -104,8 +132,12 @@ function* fetchStatus(location: FetchFilePayload) {
|
|||
|
||||
function requestStatus(location: FetchFilePayload) {
|
||||
const { uri, revision, path } = location;
|
||||
const pathname = path
|
||||
? `/api/code/repo/${uri}/status/${revision}/${path}`
|
||||
: `/api/code/repo/${uri}/status/${revision}`;
|
||||
|
||||
return kfetch({
|
||||
pathname: `/api/code/repo/${uri}/status/${revision}/${path}`,
|
||||
pathname,
|
||||
method: 'GET',
|
||||
});
|
||||
}
|
||||
|
|
|
@ -5,17 +5,20 @@
|
|||
*/
|
||||
|
||||
import { Action } from 'redux-actions';
|
||||
import { delay } from 'redux-saga';
|
||||
import { call, put, takeEvery, cancel, take, fork } from 'redux-saga/effects';
|
||||
import { call, put, select, takeEvery } 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 {
|
||||
loadStructure,
|
||||
loadStructureFailed,
|
||||
loadStructureSuccess,
|
||||
StatusChanged,
|
||||
} from '../actions';
|
||||
import { SymbolWithMembers } from '../actions/structure';
|
||||
import { matchContainerName } from '../utils/symbol_utils';
|
||||
|
||||
const STRUCTURE_TREE_POLLING_INTERVAL_SEC = 3;
|
||||
import { RepoFileStatus, StatusReport } from '../../common/repo_file_status';
|
||||
import { RootState } from '../reducers';
|
||||
import { toCanonicalUrl } from '../../common/uri_util';
|
||||
|
||||
type Container = SymbolWithMembers | undefined;
|
||||
|
||||
|
@ -103,33 +106,39 @@ function requestStructure(uri?: string) {
|
|||
});
|
||||
}
|
||||
|
||||
function* beginPollingSymbols(action: Action<string>) {
|
||||
try {
|
||||
const pollingTaskId = yield fork(pollingSaga, action);
|
||||
yield take([String(loadStructureSuccess), String(loadStructureFailed)]);
|
||||
yield cancel(pollingTaskId);
|
||||
} catch (err) {
|
||||
yield put(loadStructureFailed(err));
|
||||
function* statusChanged(action: Action<any>) {
|
||||
const {
|
||||
prevStatus,
|
||||
currentStatus,
|
||||
}: { prevStatus: StatusReport; currentStatus: StatusReport } = action.payload;
|
||||
if (
|
||||
prevStatus &&
|
||||
prevStatus.langServerStatus === RepoFileStatus.LANG_SERVER_IS_INITIALIZING &&
|
||||
currentStatus.langServerStatus !== RepoFileStatus.LANG_SERVER_IS_INITIALIZING
|
||||
) {
|
||||
const { revision, uri, path } = yield select(
|
||||
(state: RootState) => state.status.currentStatusPath
|
||||
);
|
||||
const u = toCanonicalUrl({
|
||||
repoUri: uri,
|
||||
file: path,
|
||||
revision,
|
||||
});
|
||||
yield call(fetchSymbols, loadStructure(u));
|
||||
}
|
||||
}
|
||||
|
||||
export function* watchLoadStructure() {
|
||||
yield takeEvery(String(loadStructure), beginPollingSymbols);
|
||||
yield takeEvery(String(loadStructure), fetchSymbols);
|
||||
yield takeEvery(String(StatusChanged), statusChanged);
|
||||
}
|
||||
|
||||
function* pollingSaga(action: Action<string>) {
|
||||
while (true) {
|
||||
try {
|
||||
const data = yield call(requestStructure, `git:/${action.payload}`);
|
||||
const structureTree = generateStructureTree(data);
|
||||
yield put(loadStructureSuccess({ path: action.payload!, data, structureTree }));
|
||||
} catch (e) {
|
||||
if (e.code && e.code === ServerNotInitialized) {
|
||||
yield put(languageServerInitializing());
|
||||
yield delay(STRUCTURE_TREE_POLLING_INTERVAL_SEC * 1000);
|
||||
} else {
|
||||
yield put(loadStructureFailed(e));
|
||||
}
|
||||
}
|
||||
function* fetchSymbols(action: Action<string>) {
|
||||
try {
|
||||
const data = yield call(requestStructure, `git:/${action.payload}`);
|
||||
const structureTree = generateStructureTree(data);
|
||||
yield put(loadStructureSuccess({ path: action.payload!, data, structureTree }));
|
||||
} catch (e) {
|
||||
yield put(loadStructureFailed(e));
|
||||
}
|
||||
}
|
||||
|
|
|
@ -38,7 +38,7 @@ export const repoUriSelector = (state: RootState) => {
|
|||
|
||||
export const routeSelector = (state: RootState) => state.route.match;
|
||||
|
||||
export const statusSelector = (state: RootState, repoUri: RepositoryUri) => {
|
||||
export const repoStatusSelector = (state: RootState, repoUri: RepositoryUri) => {
|
||||
return state.status.status[repoUri];
|
||||
};
|
||||
|
||||
|
@ -101,3 +101,5 @@ export const repoScopeSelector = (state: RootState) => state.search.searchOption
|
|||
export const urlQueryStringSelector = (state: RootState) => state.route.match.location.search;
|
||||
|
||||
export const previousMatchSelector = (state: RootState) => state.route.previousMatch;
|
||||
|
||||
export const statusSelector = (state: RootState) => state.status.repoFileStatus;
|
||||
|
|
|
@ -72,9 +72,10 @@ export function statusRoute(
|
|||
} else {
|
||||
const state = await lspService.initializeState(repoUri, revision);
|
||||
const initState = state[def.name];
|
||||
if (initState !== WorkspaceStatus.Initialized) {
|
||||
report.langServerStatus = RepoFileStatus.LANG_SERVER_IS_INITIALIZING;
|
||||
}
|
||||
report.langServerStatus =
|
||||
initState === WorkspaceStatus.Initialized
|
||||
? RepoFileStatus.LANG_SERVER_INITIALIZED
|
||||
: RepoFileStatus.LANG_SERVER_IS_INITIALIZING;
|
||||
}
|
||||
}
|
||||
|
||||
|
@ -83,9 +84,7 @@ export function statusRoute(
|
|||
method: 'GET',
|
||||
async handler(req: hapi.Request) {
|
||||
const { uri, path, ref } = req.params;
|
||||
const report: StatusReport = {
|
||||
langServerType: LangServerType.NONE,
|
||||
};
|
||||
const report: StatusReport = {};
|
||||
const repoObjectClient = new RepositoryObjectClient(new EsClientWithRequest(req));
|
||||
try {
|
||||
// Check if the repository already exists
|
||||
|
@ -94,7 +93,6 @@ export function statusRoute(
|
|||
return Boom.notFound(`repo ${uri} not found`);
|
||||
}
|
||||
await handleRepoStatus(report, uri, ref, repoObjectClient);
|
||||
|
||||
if (path) {
|
||||
try {
|
||||
try {
|
||||
|
|
Loading…
Add table
Add a link
Reference in a new issue