mirror of
https://github.com/elastic/kibana.git
synced 2025-04-24 09:48:58 -04:00
[Code] be able to fallback to generic lang server if dedicated lang s… (#42317)
* [Code] be able to fallback to generic lang server if dedicated lang server is disabled or not installed
This commit is contained in:
parent
7e46de5a02
commit
4c85ada320
10 changed files with 78 additions and 51 deletions
|
@ -38,4 +38,6 @@ export const CTAGS_SUPPORT_LANGS = [
|
|||
'shell',
|
||||
'sql',
|
||||
'tcl',
|
||||
'java',
|
||||
'javascript',
|
||||
];
|
||||
|
|
|
@ -64,7 +64,6 @@ export class StatusIndicatorComponent extends React.Component<Props, State> {
|
|||
const { statusReport } = this.props;
|
||||
let severity = Severity.NONE;
|
||||
const children: any[] = [];
|
||||
|
||||
const addError = (error: RepoFileStatus | LangServerType) => {
|
||||
// @ts-ignore
|
||||
const s: any = REPO_FILE_STATUS_SEVERITY[error];
|
||||
|
|
|
@ -200,14 +200,14 @@ describe('lsp_incremental_indexer unit tests', () => {
|
|||
|
||||
// There are 5 MODIFIED items and 1 ADDED item. Only 1 file is in supported
|
||||
// language. Each file with supported language has 1 file + 1 symbol + 1 reference.
|
||||
// Total doc indexed should be 8 * 3 = 15,
|
||||
// Total doc indexed should be 6 * 2 + 4 = 16,
|
||||
// which can be fitted into a single batch index.
|
||||
assert.strictEqual(bulkSpy.callCount, 2);
|
||||
let total = 0;
|
||||
for (let i = 0; i < bulkSpy.callCount; i++) {
|
||||
total += bulkSpy.getCall(i).args[0].body.length;
|
||||
}
|
||||
assert.strictEqual(total, 8 * 2);
|
||||
assert.strictEqual(total, 16);
|
||||
|
||||
// @ts-ignore
|
||||
}).timeout(20000);
|
||||
|
|
|
@ -211,7 +211,7 @@ describe('lsp_service tests', () => {
|
|||
assert.ok(workspaceFolderExists);
|
||||
const controller = lspservice.controller;
|
||||
// @ts-ignore
|
||||
const languageServer = controller.languageServerMap.typescript;
|
||||
const languageServer = controller.languageServerMap.typescript[0];
|
||||
const realWorkspacePath = fs.realpathSync(workspacePath);
|
||||
|
||||
// @ts-ignore
|
||||
|
@ -268,7 +268,7 @@ describe('lsp_service tests', () => {
|
|||
await lspservice.shutdown();
|
||||
}
|
||||
// @ts-ignore
|
||||
}).timeout(10000);
|
||||
}).timeout(20000);
|
||||
|
||||
it('should update if a worktree is not the newest', async () => {
|
||||
const lspservice = mockLspService();
|
||||
|
|
|
@ -19,10 +19,10 @@ export const LspServiceDefinition = {
|
|||
},
|
||||
languageSeverDef: {
|
||||
request: {} as { lang: string },
|
||||
response: {} as LanguageServerDefinition | null,
|
||||
response: {} as LanguageServerDefinition[],
|
||||
},
|
||||
languageServerStatus: {
|
||||
request: {} as { lang: string },
|
||||
request: {} as { langName: string },
|
||||
response: {} as LanguageServerStatus,
|
||||
},
|
||||
initializeState: {
|
||||
|
@ -40,8 +40,8 @@ export const getLspServiceHandler = (
|
|||
async languageSeverDef({ lang }) {
|
||||
return lspService.getLanguageSeverDef(lang);
|
||||
},
|
||||
async languageServerStatus({ lang }) {
|
||||
return await lspService.languageServerStatus(lang);
|
||||
async languageServerStatus({ langName }) {
|
||||
return lspService.languageServerStatus(langName);
|
||||
},
|
||||
async initializeState({ repoUri, revision }) {
|
||||
return await lspService.initializeState(repoUri, revision);
|
||||
|
|
|
@ -46,7 +46,7 @@ export class LanguageServerController implements ILanguageServerHandler {
|
|||
// a list of support language servers
|
||||
private readonly languageServers: LanguageServerData[];
|
||||
// a { lang -> server } map from above list
|
||||
private readonly languageServerMap: { [lang: string]: LanguageServerData };
|
||||
private readonly languageServerMap: { [lang: string]: LanguageServerData[] };
|
||||
private log: Logger;
|
||||
|
||||
constructor(
|
||||
|
@ -64,13 +64,22 @@ export class LanguageServerController implements ILanguageServerHandler {
|
|||
maxWorkspace: options.maxWorkspace,
|
||||
launcher: new def.launcher(this.targetHost, options, loggerFactory),
|
||||
}));
|
||||
const add2map = (
|
||||
map: { [lang: string]: LanguageServerData[] },
|
||||
lang: string,
|
||||
ls: LanguageServerData
|
||||
) => {
|
||||
const arr = map[lang] || [];
|
||||
arr.push(ls);
|
||||
map[lang] = arr.sort((a, b) => b.definition.priority - a.definition.priority);
|
||||
};
|
||||
this.languageServerMap = this.languageServers.reduce(
|
||||
(map, ls) => {
|
||||
ls.languages.forEach(lang => (map[lang] = ls));
|
||||
map[ls.definition.name] = ls;
|
||||
ls.languages.forEach(lang => add2map(map, lang, ls));
|
||||
map[ls.definition.name] = [ls];
|
||||
return map;
|
||||
},
|
||||
{} as { [lang: string]: LanguageServerData }
|
||||
{} as { [lang: string]: LanguageServerData[] }
|
||||
);
|
||||
}
|
||||
|
||||
|
@ -183,28 +192,24 @@ export class LanguageServerController implements ILanguageServerHandler {
|
|||
}
|
||||
}
|
||||
|
||||
public status(lang: string): LanguageServerStatus {
|
||||
const ls = this.languageServerMap[lang];
|
||||
if (ls) {
|
||||
const status = this.installManager.status(ls.definition);
|
||||
// installed, but is it running?
|
||||
if (status === LanguageServerStatus.READY) {
|
||||
if (ls.launcher.running) {
|
||||
return LanguageServerStatus.RUNNING;
|
||||
}
|
||||
public status(def: LanguageServerDefinition): LanguageServerStatus {
|
||||
const status = this.installManager.status(def);
|
||||
// installed, but is it running?
|
||||
if (status === LanguageServerStatus.READY) {
|
||||
const ls = this.languageServers.find(d => d.definition === def);
|
||||
if (ls && ls.launcher.running) {
|
||||
return LanguageServerStatus.RUNNING;
|
||||
}
|
||||
return status;
|
||||
} else {
|
||||
return LanguageServerStatus.NOT_INSTALLED;
|
||||
}
|
||||
return status;
|
||||
}
|
||||
|
||||
public getLanguageServerDef(lang: string): LanguageServerDefinition | null {
|
||||
public getLanguageServerDef(lang: string): LanguageServerDefinition[] {
|
||||
const data = this.languageServerMap[lang];
|
||||
if (data) {
|
||||
return data.definition;
|
||||
return data.map(d => d.definition);
|
||||
}
|
||||
return null;
|
||||
return [];
|
||||
}
|
||||
|
||||
private async findOrCreateHandler(
|
||||
|
@ -254,18 +259,18 @@ export class LanguageServerController implements ILanguageServerHandler {
|
|||
}
|
||||
|
||||
private findLanguageServer(lang: string) {
|
||||
const ls = this.languageServerMap[lang];
|
||||
if (ls) {
|
||||
if (
|
||||
!this.options.lsp.detach &&
|
||||
this.installManager.status(ls.definition) !== LanguageServerStatus.READY
|
||||
) {
|
||||
const lsArr = this.languageServerMap[lang];
|
||||
if (lsArr) {
|
||||
const ls = lsArr.find(
|
||||
d => this.installManager.status(d.definition) !== LanguageServerStatus.NOT_INSTALLED
|
||||
);
|
||||
if (!this.options.lsp.detach && ls === undefined) {
|
||||
throw new ResponseError(
|
||||
LanguageServerNotInstalled,
|
||||
`language server ${lang} not installed`
|
||||
);
|
||||
} else {
|
||||
return ls;
|
||||
return ls!;
|
||||
}
|
||||
} else {
|
||||
throw new ResponseError(UnknownFileLanguage, `unsupported language ${lang}`);
|
||||
|
|
|
@ -21,6 +21,7 @@ export interface LanguageServerDefinition extends LanguageServer {
|
|||
downloadUrl?: (version: string, devMode?: boolean) => string;
|
||||
embedPath?: string;
|
||||
installationPluginName?: string;
|
||||
priority: number;
|
||||
}
|
||||
|
||||
export const TYPESCRIPT: LanguageServerDefinition = {
|
||||
|
@ -30,6 +31,7 @@ export const TYPESCRIPT: LanguageServerDefinition = {
|
|||
launcher: TypescriptServerLauncher,
|
||||
installationType: InstallationType.Embed,
|
||||
embedPath: require.resolve('@elastic/javascript-typescript-langserver/lib/language-server.js'),
|
||||
priority: 2,
|
||||
};
|
||||
export const JAVA: LanguageServerDefinition = {
|
||||
name: 'Java',
|
||||
|
@ -39,6 +41,7 @@ export const JAVA: LanguageServerDefinition = {
|
|||
installationType: InstallationType.Plugin,
|
||||
installationPluginName: 'java-langserver',
|
||||
installationFolderName: 'jdt',
|
||||
priority: 2,
|
||||
downloadUrl: (version: string, devMode?: boolean) =>
|
||||
devMode!
|
||||
? `https://snapshots.elastic.co/downloads/java-langserver-plugins/java-langserver/java-langserver-${version}-SNAPSHOT-$OS.zip`
|
||||
|
@ -51,6 +54,7 @@ export const GO: LanguageServerDefinition = {
|
|||
launcher: GoServerLauncher,
|
||||
installationType: InstallationType.Plugin,
|
||||
installationPluginName: 'goLanguageServer',
|
||||
priority: 2,
|
||||
};
|
||||
export const CTAGS: LanguageServerDefinition = {
|
||||
name: 'Ctags',
|
||||
|
@ -59,6 +63,7 @@ export const CTAGS: LanguageServerDefinition = {
|
|||
launcher: CtagsLauncher,
|
||||
installationType: InstallationType.Embed,
|
||||
embedPath: require.resolve('@elastic/ctags-langserver/lib/cli.js'),
|
||||
priority: 1,
|
||||
};
|
||||
export const LanguageServers: LanguageServerDefinition[] = [TYPESCRIPT, JAVA, CTAGS];
|
||||
export const LanguageServersDeveloping: LanguageServerDefinition[] = [GO];
|
||||
|
|
|
@ -79,15 +79,21 @@ export class LspService {
|
|||
}
|
||||
|
||||
public supportLanguage(lang: string) {
|
||||
return this.controller.getLanguageServerDef(lang) !== null;
|
||||
return this.controller.getLanguageServerDef(lang).length > 0;
|
||||
}
|
||||
|
||||
public getLanguageSeverDef(lang: string) {
|
||||
return this.controller.getLanguageServerDef(lang);
|
||||
}
|
||||
|
||||
public languageServerStatus(lang: string): LanguageServerStatus {
|
||||
return this.controller.status(lang);
|
||||
public languageServerStatus(name: string): LanguageServerStatus {
|
||||
const defs = this.controller.getLanguageServerDef(name);
|
||||
if (defs.length > 0) {
|
||||
const def = defs[0];
|
||||
return this.controller.status(def);
|
||||
} else {
|
||||
return LanguageServerStatus.NOT_INSTALLED;
|
||||
}
|
||||
}
|
||||
|
||||
public async initializeState(repoUri: string, revision: string) {
|
||||
|
|
|
@ -18,7 +18,7 @@ export function installRoute(router: CodeServerRouter, codeServices: CodeService
|
|||
const kibanaVersion = router.server.config().get('pkg.version') as string;
|
||||
const status = (endpoint: Endpoint, def: LanguageServerDefinition) => ({
|
||||
name: def.name,
|
||||
status: lspService.languageServerStatus(endpoint, { lang: def.name }),
|
||||
status: lspService.languageServerStatus(endpoint, { langName: def.name }),
|
||||
version: def.version,
|
||||
build: def.build,
|
||||
languages: def.languages,
|
||||
|
|
|
@ -55,38 +55,48 @@ export function statusRoute(router: CodeServerRouter, codeServices: CodeServices
|
|||
) {
|
||||
if (blob.content) {
|
||||
const lang: string = blob.lang!;
|
||||
const def = await lspService.languageSeverDef(endpoint, { lang });
|
||||
if (def === null) {
|
||||
const defs = await lspService.languageSeverDef(endpoint, { lang });
|
||||
if (defs.length === 0) {
|
||||
report.fileStatus = RepoFileStatus.FILE_NOT_SUPPORTED;
|
||||
} else {
|
||||
return def;
|
||||
return defs;
|
||||
}
|
||||
} else {
|
||||
report.fileStatus = RepoFileStatus.FILE_IS_TOO_BIG;
|
||||
}
|
||||
return [];
|
||||
}
|
||||
|
||||
async function handleLspStatus(
|
||||
endpoint: Endpoint,
|
||||
report: StatusReport,
|
||||
def: LanguageServerDefinition,
|
||||
defs: LanguageServerDefinition[],
|
||||
repoUri: string,
|
||||
revision: string
|
||||
) {
|
||||
report.langServerType = def === CTAGS ? LangServerType.GENERIC : LangServerType.DEDICATED;
|
||||
const status = await lspService.languageServerStatus(endpoint, { lang: def.languages[0] });
|
||||
if (status === LanguageServerStatus.NOT_INSTALLED) {
|
||||
const dedicated = defs.find(d => d !== CTAGS);
|
||||
const generic = defs.find(d => d === CTAGS);
|
||||
report.langServerType = dedicated ? LangServerType.DEDICATED : LangServerType.GENERIC;
|
||||
if (
|
||||
dedicated &&
|
||||
(await lspService.languageServerStatus(endpoint, { langName: dedicated.name })) ===
|
||||
LanguageServerStatus.NOT_INSTALLED
|
||||
) {
|
||||
report.langServerStatus = RepoFileStatus.LANG_SERVER_NOT_INSTALLED;
|
||||
if (generic) {
|
||||
// dedicated lang server not installed, fallback to generic
|
||||
report.langServerType = LangServerType.GENERIC;
|
||||
}
|
||||
} else {
|
||||
const def = dedicated || generic;
|
||||
const state = await lspService.initializeState(endpoint, { repoUri, revision });
|
||||
const initState = state[def.name];
|
||||
const initState = state[def!.name];
|
||||
report.langServerStatus =
|
||||
initState === WorkspaceStatus.Initialized
|
||||
? RepoFileStatus.LANG_SERVER_INITIALIZED
|
||||
: RepoFileStatus.LANG_SERVER_IS_INITIALIZING;
|
||||
}
|
||||
}
|
||||
|
||||
router.route({
|
||||
path: '/api/code/repo/{uri*3}/status/{ref}/{path*}',
|
||||
method: 'GET',
|
||||
|
@ -113,9 +123,9 @@ export function statusRoute(router: CodeServerRouter, codeServices: CodeServices
|
|||
});
|
||||
// text file
|
||||
if (!blob.isBinary) {
|
||||
const def = await handleFileStatus(endpoint, report, blob);
|
||||
if (def) {
|
||||
await handleLspStatus(endpoint, report, def, uri, ref);
|
||||
const defs = await handleFileStatus(endpoint, report, blob);
|
||||
if (defs.length > 0) {
|
||||
await handleLspStatus(endpoint, report, defs, uri, ref);
|
||||
}
|
||||
}
|
||||
} catch (e) {
|
||||
|
|
Loading…
Add table
Add a link
Reference in a new issue