grafana/public/app/plugins/datasource/loki/components/monaco-query-field/monaco-completion-provider/index.ts
Matias Chomicki 729ce8bb72
Loki: query editor using Monaco (#55391)
* loki: switch to a monaco-based query field, step 1 (#46291)

* loki: use monaco-logql (#46318)

* loki: use monaco-logql

* updated monaco-logql

* fix all the tests (#46327)

* loki: recommend parser (#46362)

* loki: recommend parser

* additional improvements

* more improvements

* type and lint fixes

* more improvements

* trigger autocomplete on focus

* rename

* loki: more smart features (#46414)

* loki: more smart features

* loki: updated syntax-highlight version

* better explanation (#46443)

* better explanation

* improved help-text

Co-authored-by: Ivana Huckova <30407135+ivanahuckova@users.noreply.github.com>

Co-authored-by: Ivana Huckova <30407135+ivanahuckova@users.noreply.github.com>

* Fix label

* feat(loki-monaco-editor): add monaco-logql as a dependency

* feat(loki-monaco-editor): add back range function removed during merge

* feat(loki-monaco-editor): sync imports with recent changes

* feat(loki-monaco-editor): add missing lang provider functions

* feat(loki-monaco-editor): fix imports

* feat(loki-monaco-editor): display monaco editor by default

Temporarily

* Chore: remove commented code

* Chore: minor refactor to NeverCaseError

* Chore: minor code cleanups

* feat(loki-monaco-editor): add history implementation

Will see how it behaves and base the history slicing on tangible feedback

* feat(loki-monaco-editor): turn completion data provider into a class

* Chore: fix missing imports

* feat(loki-monaco-editor): refactor data provider methods

Move complexity scattered everywhere to the provider

* Chore: clean up redundant code

* Chore: minor comments cleanup

* Chore: simplify override services

* Chore: rename callback

* feat(loki-monaco-editor): use query hints implementation to parse expression

* feat(loki-monaco-editor): improve function name

* Chore: remove superfluous variable in favor of destructuring

* Chore: remove unused imports

* Chore: make method async

* feat(loki-monaco-editor): fix deprecations and errors in situation

* feat(loki-monaco-editor): comment failing test case

* Chore: remove comment from test

* Chore: remove duplicated completion item

* Chore: fix linting issues

* Chore: update language provider test

* Chore: update datasource test

* feat(loki-monaco-editor): create feature flag

* feat(loki-monaco-editor): place the editor under a feature flag

* Chore: add completion unit test

* Chore: add completion data provider test

* Chore: remove unwanted export

* Chore: remove unused export

* Chore(loki-query-field): destructure all props

* chore(loki-completions): remove odd string

* fix(loki-completions): remove rate_interval

Not supported

* fix(loki-completions): remove line filters for after pipe case

We shouldn't offer line filters if we are after first pipe.

* refactor(loki-datasource): update default parameter

* fix(loki-syntax): remove outdated documentation

* Update capitalization in pkg/services/featuremgmt/registry.go

Co-authored-by: Ivana Huckova <30407135+ivanahuckova@users.noreply.github.com>

* refactor(situation): use node types instead of names

* Chore: comment line filters pending implementation

It's breaking the build due to a linting error.

* Chore: update feature flag test after capitalization change

* Revert "fix(loki-completions): remove line filters for after pipe case"

This reverts commit 3d003ca4bc.

* Revert "Chore: comment line filters pending implementation"

This reverts commit 84bfe76a6a.

Co-authored-by: Gábor Farkas <gabor.farkas@gmail.com>
Co-authored-by: Ivana Huckova <30407135+ivanahuckova@users.noreply.github.com>
Co-authored-by: Ivana Huckova <ivana.huckova@gmail.com>
2022-10-06 10:35:30 -04:00

113 lines
4.4 KiB
TypeScript

import type { Monaco, monacoTypes } from '@grafana/ui';
import { CompletionDataProvider } from './CompletionDataProvider';
import { NeverCaseError } from './NeverCaseError';
import { getCompletions, CompletionType } from './completions';
import { getSituation } from './situation';
// from: monacoTypes.languages.CompletionItemInsertTextRule.InsertAsSnippet
const INSERT_AS_SNIPPET_ENUM_VALUE = 4;
export function getSuggestOptions(): monacoTypes.editor.ISuggestOptions {
return {
// monaco-editor sometimes provides suggestions automatically, i am not
// sure based on what, seems to be by analyzing the words already
// written.
// to try it out:
// - enter `go_goroutines{job~`
// - have the cursor at the end of the string
// - press ctrl-enter
// - you will get two suggestions
// those were not provided by grafana, they are offered automatically.
// i want to remove those. the only way i found is:
// - every suggestion-item has a `kind` attribute,
// that controls the icon to the left of the suggestion.
// - items auto-generated by monaco have `kind` set to `text`.
// - we make sure grafana-provided suggestions do not have `kind` set to `text`.
// - and then we tell monaco not to show suggestions of kind `text`
showWords: false,
};
}
function getMonacoCompletionItemKind(type: CompletionType, monaco: Monaco): monacoTypes.languages.CompletionItemKind {
switch (type) {
case 'DURATION':
return monaco.languages.CompletionItemKind.Unit;
case 'FUNCTION':
return monaco.languages.CompletionItemKind.Variable;
case 'HISTORY':
return monaco.languages.CompletionItemKind.Snippet;
case 'LABEL_NAME':
return monaco.languages.CompletionItemKind.Enum;
case 'LABEL_VALUE':
return monaco.languages.CompletionItemKind.EnumMember;
case 'PATTERN':
return monaco.languages.CompletionItemKind.Constructor;
case 'PARSER':
return monaco.languages.CompletionItemKind.Class;
case 'LINE_FILTER':
return monaco.languages.CompletionItemKind.TypeParameter;
case 'LINE_FORMAT':
return monaco.languages.CompletionItemKind.Event;
default:
throw new NeverCaseError(type);
}
}
export function getCompletionProvider(
monaco: Monaco,
dataProvider: CompletionDataProvider
): monacoTypes.languages.CompletionItemProvider {
const provideCompletionItems = (
model: monacoTypes.editor.ITextModel,
position: monacoTypes.Position
): monacoTypes.languages.ProviderResult<monacoTypes.languages.CompletionList> => {
const word = model.getWordAtPosition(position);
const range =
word != null
? monaco.Range.lift({
startLineNumber: position.lineNumber,
endLineNumber: position.lineNumber,
startColumn: word.startColumn,
endColumn: word.endColumn,
})
: monaco.Range.fromPositions(position);
// documentation says `position` will be "adjusted" in `getOffsetAt`
// i don't know what that means, to be sure i clone it
const positionClone = {
column: position.column,
lineNumber: position.lineNumber,
};
const offset = model.getOffsetAt(positionClone);
const situation = getSituation(model.getValue(), offset);
const completionsPromise = situation != null ? getCompletions(situation, dataProvider) : Promise.resolve([]);
return completionsPromise.then((items) => {
// monaco by default alphabetically orders the items.
// to stop it, we use a number-as-string sortkey,
// so that monaco keeps the order we use
const maxIndexDigits = items.length.toString().length;
const suggestions: monacoTypes.languages.CompletionItem[] = items.map((item, index) => ({
kind: getMonacoCompletionItemKind(item.type, monaco),
label: item.label,
insertText: item.insertText,
insertTextRules: item.isSnippet ? INSERT_AS_SNIPPET_ENUM_VALUE : undefined,
detail: item.detail,
documentation: item.documentation,
sortText: index.toString().padStart(maxIndexDigits, '0'), // to force the order we have
range,
command: item.triggerOnInsert
? {
id: 'editor.action.triggerSuggest',
title: '',
}
: undefined,
}));
return { suggestions };
});
};
return {
triggerCharacters: ['{', ',', '[', '(', '=', '~', ' ', '"', '|'],
provideCompletionItems,
};
}