Skip to content

Commit

Permalink
app support for switching contexts
Browse files Browse the repository at this point in the history
  • Loading branch information
alisonelizabeth committed Oct 27, 2020
1 parent b5db29e commit 00efc3c
Show file tree
Hide file tree
Showing 15 changed files with 107 additions and 30 deletions.
2 changes: 1 addition & 1 deletion packages/kbn-monaco/src/index.ts
Original file line number Diff line number Diff line change
Expand Up @@ -22,7 +22,7 @@ import './register_globals';

export { monaco } from './monaco_imports';
export { XJsonLang } from './xjson';
export { PainlessLang } from './painless';
export { PainlessLang, PainlessContext } from './painless';

/* eslint-disable-next-line @kbn/eslint/module_migration */
import * as BarePluginApi from 'monaco-editor/esm/vs/editor/editor.api';
Expand Down
32 changes: 32 additions & 0 deletions packages/kbn-monaco/src/painless/context_service.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,32 @@
/*
* Licensed to Elasticsearch B.V. under one or more contributor
* license agreements. See the NOTICE file distributed with
* this work for additional information regarding copyright
* ownership. Elasticsearch B.V. licenses this file to you under
* the Apache License, Version 2.0 (the "License"); you may
* not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing,
* software distributed under the License is distributed on an
* "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
* KIND, either express or implied. See the License for the
* specific language governing permissions and limitations
* under the License.
*/

import { PainlessContext } from './types';

export class ContextService {
context: PainlessContext = 'painless_test';

public get workerContext() {
return this.context;
}

public set workerContext(newContext: PainlessContext) {
this.context = newContext;
}
}
2 changes: 2 additions & 0 deletions packages/kbn-monaco/src/painless/index.ts
Original file line number Diff line number Diff line change
Expand Up @@ -22,3 +22,5 @@ import { lexerRules } from './lexer_rules';
import { getSuggestionProvider } from './language';

export const PainlessLang = { ID, getSuggestionProvider, lexerRules };

export { PainlessContext } from './types';
19 changes: 13 additions & 6 deletions packages/kbn-monaco/src/painless/language.ts
Original file line number Diff line number Diff line change
Expand Up @@ -21,18 +21,25 @@ import { monaco } from '../monaco_imports';

import { PainlessCompletionAdapter } from './painless_completion';
import { WorkerProxyService } from './worker_proxy_service';
import { ContextService } from './context_service';
import { ID } from './constants';
import { PainlessContext } from './types';
import { PainlessWorker } from './worker';

const wps = new WorkerProxyService();
const workerProxyService = new WorkerProxyService();
const contextService = new ContextService();

const worker = (...uris: any[]) => {
return wps.getWorker(uris);
type WorkerAccessor = (...uris: monaco.Uri[]) => Promise<PainlessWorker>;

const worker: WorkerAccessor = (...uris: monaco.Uri[]): Promise<PainlessWorker> => {
return workerProxyService.getWorker(uris);
};

monaco.languages.onLanguage(ID, async () => {
wps.setup();
workerProxyService.setup();
});

export const getSuggestionProvider = (context: PainlessContext) =>
new PainlessCompletionAdapter(worker, context);
export const getSuggestionProvider = (context: PainlessContext) => {
contextService.workerContext = context;
return new PainlessCompletionAdapter(worker, contextService);
};
20 changes: 15 additions & 5 deletions packages/kbn-monaco/src/painless/painless_completion.ts
Original file line number Diff line number Diff line change
Expand Up @@ -18,7 +18,9 @@
*/

import { monaco } from '../monaco_imports';
import { ContextService } from './context_service';
import { PainlessCompletionResult, PainlessCompletionKind } from './types';
import { PainlessWorker } from './worker';

const getCompletionKind = (kind: PainlessCompletionKind): monaco.languages.CompletionItemKind => {
const monacoItemKind = monaco.languages.CompletionItemKind;
Expand All @@ -39,8 +41,13 @@ const getCompletionKind = (kind: PainlessCompletionKind): monaco.languages.Compl
};

export class PainlessCompletionAdapter implements monaco.languages.CompletionItemProvider {
// @ts-ignore
constructor(private _worker, private _painlessContext) {}
constructor(
private _worker: {
(...uris: monaco.Uri[]): Promise<PainlessWorker>;
(arg0: monaco.Uri): Promise<PainlessWorker>;
},
private _contextService: ContextService
) {}

public get triggerCharacters(): string[] {
return ['.'];
Expand All @@ -59,9 +66,12 @@ export class PainlessCompletionAdapter implements monaco.languages.CompletionIte
endLineNumber: position.lineNumber,
endColumn: position.column,
});
return this._worker()
.then((worker: any) => {
return worker.provideAutocompleteSuggestions(currentLineChars, this._painlessContext);
return this._worker(model.uri)
.then((worker: PainlessWorker) => {
return worker.provideAutocompleteSuggestions(
currentLineChars,
this._contextService.workerContext
);
})
.then((completionInfo: PainlessCompletionResult) => {
const wordInfo = model.getWordUntilPosition(position);
Expand Down
20 changes: 9 additions & 11 deletions packages/kbn-monaco/src/painless/worker/painless_worker.ts
Original file line number Diff line number Diff line change
Expand Up @@ -19,14 +19,15 @@

import { PainlessCompletionResult, PainlessContext } from '../types';

import { PainlessCompletionManager } from './completion_manager';
import { PainlessCompletionService } from './services';

export class PainlessWorker {
async provideAutocompleteSuggestions(
currentLineChars: string,
context: PainlessContext
context: PainlessContext,
index?: string
): Promise<PainlessCompletionResult> {
const completionManager = new PainlessCompletionManager(context);
const completionService = new PainlessCompletionService(context);
// Array of the active line words, e.g., [boolean, isInCircle]
const words = currentLineChars.replace('\t', '').split(' ');
// What the user is currently typing
Expand All @@ -35,7 +36,7 @@ export class PainlessWorker {
// If the active typing contains dot notation, we assume we need to access the object's properties
const isProperty = activeTyping.split('.').length === 2;
// If the preceding word is a type, e.g., "boolean", we assume the user is declaring a variable and skip autocomplete
const hasDeclaredType = words.length === 2 && completionManager.getTypes().includes(words[0]);
const hasDeclaredType = words.length === 2 && completionService.getTypes().includes(words[0]);
// If the preceding word contains the "new" keyword, we only provide constructor autcompletion
const isConstructor = words[words.length - 2] === 'new';

Expand All @@ -45,15 +46,12 @@ export class PainlessWorker {
};

if (isConstructor) {
autocompleteSuggestions = completionManager.getPainlessConstructorsToAutocomplete();
autocompleteSuggestions = completionService.getPainlessConstructorsToAutocomplete();
} else if (isProperty) {
const className = activeTyping.substring(0, activeTyping.length - 1).split('.')[0];

autocompleteSuggestions = completionManager.getPainlessClassToAutocomplete(className);
} else {
if (!hasDeclaredType) {
autocompleteSuggestions = completionManager.getPainlessClassesToAutocomplete();
}
autocompleteSuggestions = completionService.getPainlessClassToAutocomplete(className);
} else if (!hasDeclaredType) {
autocompleteSuggestions = completionService.getPainlessClassesToAutocomplete();
}

return Promise.resolve(autocompleteSuggestions);
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -17,7 +17,7 @@
* under the License.
*/

import { PainlessCompletionResult, PainlessCompletionItem, PainlessContext } from '../types';
import { PainlessCompletionResult, PainlessCompletionItem, PainlessContext } from '../../types';
import { painlessTestContext, scoreContext, filterContext } from './context';

interface Field {
Expand Down Expand Up @@ -96,7 +96,7 @@ const getMethodDescription = (
return `${methodName}(${parameterDescription}): ${returnValue}`;
};

export class PainlessCompletionManager {
export class PainlessCompletionService {
context: Context;
constructor(private _painlessContext: PainlessContext) {
this.context = mapContextToData[this._painlessContext] as Context;
Expand Down
20 changes: 20 additions & 0 deletions packages/kbn-monaco/src/painless/worker/services/index.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,20 @@
/*
* Licensed to Elasticsearch B.V. under one or more contributor
* license agreements. See the NOTICE file distributed with
* this work for additional information regarding copyright
* ownership. Elasticsearch B.V. licenses this file to you under
* the Apache License, Version 2.0 (the "License"); you may
* not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing,
* software distributed under the License is distributed on an
* "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
* KIND, either express or implied. See the License for the
* specific language governing permissions and limitations
* under the License.
*/

export { PainlessCompletionService } from './completion';
3 changes: 2 additions & 1 deletion packages/kbn-monaco/src/painless/worker_proxy_service.ts
Original file line number Diff line number Diff line change
Expand Up @@ -23,10 +23,11 @@ import { PainlessWorker } from './worker';
export class WorkerProxyService {
private worker: monaco.editor.MonacoWebWorker<PainlessWorker> | undefined;

public async getWorker(resources: any[]) {
public async getWorker(resources: monaco.Uri[]) {
if (!this.worker) {
throw new Error('Worker Proxy Service has not been setup!');
}

await this.worker.withSyncedResources(resources);
const proxy = await this.worker.getProxy();
return proxy;
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -4,16 +4,19 @@
* you may not use this file except in compliance with the Elastic License.
*/
import React from 'react';
import { PainlessLang } from '@kbn/monaco';
import { PainlessLang, PainlessContext } from '@kbn/monaco';

import { CodeEditor } from '../../../../../../src/plugins/kibana_react/public';

interface Props {
code: string;
onChange: (code: string) => void;
context: PainlessContext;
}

export function Editor({ code, onChange }: Props) {
export function Editor({ code, onChange, context }: Props) {
const suggestionProvider = PainlessLang.getSuggestionProvider(context);

return (
<CodeEditor
languageId={PainlessLang.ID}
Expand All @@ -22,7 +25,7 @@ export function Editor({ code, onChange }: Props) {
height="100%"
value={code}
onChange={onChange}
suggestionProvider={PainlessLang.getSuggestionProvider('painless_test')}
suggestionProvider={suggestionProvider}
options={{
fontSize: 12,
minimap: {
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -51,7 +51,11 @@ export const Main: React.FunctionComponent = () => {
</h1>
</EuiTitle>

<Editor code={payload.code} onChange={(nextCode) => updatePayload({ code: nextCode })} />
<Editor
context={payload.context}
code={payload.code}
onChange={(nextCode) => updatePayload({ code: nextCode })}
/>
</EuiFlexItem>

<EuiFlexItem>
Expand Down

0 comments on commit 00efc3c

Please sign in to comment.