From 142a7075b9fcaf0632addbf9a38db72bf1ec6a2b Mon Sep 17 00:00:00 2001 From: "opensearch-trigger-bot[bot]" <98922864+opensearch-trigger-bot[bot]@users.noreply.github.com> Date: Mon, 9 Sep 2024 11:16:56 -0700 Subject: [PATCH] [Auto Suggest] Look and Feel changes (#7991) (#8062) * put in trigger suggest on focus and on type space * modify dql suggestions to keep suggestion chaining fluid * Changeset file for PR #7991 created/updated * user hints * specify dql operators in details and icon * Changeset file for PR #7991 created/updated * Changeset file for PR #7991 created/updated * update testing constants * modify scss * Changeset file for PR #7991 created/updated --------- (cherry picked from commit 4776fcd9e0a875ce4b5ac97a73b8952c846afdf2) Signed-off-by: Paul Sebastian Signed-off-by: github-actions[bot] Co-authored-by: github-actions[bot] Co-authored-by: opensearch-changeset-bot[bot] <154024398+opensearch-changeset-bot[bot]@users.noreply.github.com> --- changelogs/fragments/7991.yml | 2 ++ .../public/antlr/dql/code_completion.test.ts | 20 +++++++++++-------- .../data/public/antlr/dql/code_completion.ts | 17 +++++++++++++--- .../data/public/antlr/shared/constants.ts | 1 + .../public/ui/query_editor/_query_editor.scss | 20 +++++++++++++++++++ .../editors/default_editor/index.tsx | 2 ++ .../public/ui/query_editor/editors/shared.tsx | 2 ++ .../public/ui/query_editor/query_editor.tsx | 1 + .../public/code_editor/code_editor.tsx | 11 ++++++++++ 9 files changed, 65 insertions(+), 11 deletions(-) create mode 100644 changelogs/fragments/7991.yml diff --git a/changelogs/fragments/7991.yml b/changelogs/fragments/7991.yml new file mode 100644 index 00000000000..f60b36710c5 --- /dev/null +++ b/changelogs/fragments/7991.yml @@ -0,0 +1,2 @@ +feat: +- Keep Autocomplete suggestion window open and put user hints below the suggestion window ([#7991](https://github.com/opensearch-project/OpenSearch-Dashboards/pull/7991)) \ No newline at end of file diff --git a/src/plugins/data/public/antlr/dql/code_completion.test.ts b/src/plugins/data/public/antlr/dql/code_completion.test.ts index 7a4efe719cc..547ccc615e3 100644 --- a/src/plugins/data/public/antlr/dql/code_completion.test.ts +++ b/src/plugins/data/public/antlr/dql/code_completion.test.ts @@ -174,11 +174,11 @@ const testingIndex = ({ } as unknown) as IndexPattern; const booleanOperatorSuggestions = [ - { text: 'or', type: 17, detail: 'Keyword' }, - { text: 'and', type: 17, detail: 'Keyword' }, + { text: 'or', type: 11, detail: 'Operator', insertText: 'or ' }, + { text: 'and', type: 11, detail: 'Operator', insertText: 'and ' }, ]; -const notOperatorSuggestion = { text: 'not', type: 17, detail: 'Keyword' }; +const notOperatorSuggestion = { text: 'not', type: 11, detail: 'Operator', insertText: 'not ' }; const fieldNameSuggestions: Array<{ text: string; @@ -211,27 +211,31 @@ const carrierValues = [ ]; const allCarrierValueSuggestions = [ - { text: 'Logstash Airways', type: 13, detail: 'Value' }, - { text: 'BeatsWest', type: 13, detail: 'Value' }, + { text: 'Logstash Airways', type: 13, detail: 'Value', insertText: 'Logstash Airways ' }, + { text: 'BeatsWest', type: 13, detail: 'Value', insertText: 'BeatsWest ' }, { text: 'OpenSearch Dashboards Airlines', type: 13, detail: 'Value', + insertText: 'OpenSearch Dashboards Airlines ', }, - { text: 'OpenSearch-Air', type: 13, detail: 'Value' }, + { text: 'OpenSearch-Air', type: 13, detail: 'Value', insertText: 'OpenSearch-Air ' }, ]; const carrierWithNotSuggestions = allCarrierValueSuggestions.concat(notOperatorSuggestion); -const logCarrierValueSuggestion = [{ text: 'Logstash Airways', type: 13, detail: 'Value' }]; +const logCarrierValueSuggestion = [ + { text: 'Logstash Airways', type: 13, detail: 'Value', insertText: 'Logstash Airways ' }, +]; const openCarrierValueSuggestion = [ { text: 'OpenSearch Dashboards Airlines', type: 13, detail: 'Value', + insertText: 'OpenSearch Dashboards Airlines ', }, - { text: 'OpenSearch-Air', type: 13, detail: 'Value' }, + { text: 'OpenSearch-Air', type: 13, detail: 'Value', insertText: 'OpenSearch-Air ' }, ]; const addPositionToValue = (vals: any, start: number, end: number) => diff --git a/src/plugins/data/public/antlr/dql/code_completion.ts b/src/plugins/data/public/antlr/dql/code_completion.ts index a14310f5697..5968d2408c1 100644 --- a/src/plugins/data/public/antlr/dql/code_completion.ts +++ b/src/plugins/data/public/antlr/dql/code_completion.ts @@ -135,7 +135,7 @@ export const getSuggestions = async ({ // check to see if field rule is a candidate. if so, suggest field names if (candidates.rules.has(DQLParser.RULE_field)) { - completions.push(...fetchFieldSuggestions(indexPattern, (f) => `${f}: `)); + completions.push(...fetchFieldSuggestions(indexPattern, (f: any) => `${f}: `)); } interface FoundLastValue { @@ -247,12 +247,15 @@ export const getSuggestions = async ({ cursorLine, cursorColumn + 1 ), + insertText: `${val} `, }; }) ); } } + const dqlOperators = new Set([DQLParser.AND, DQLParser.OR, DQLParser.NOT]); + // suggest other candidates, mainly keywords [...candidates.tokens.keys()].forEach((token: number) => { // ignore identifier, already handled with field rule @@ -261,11 +264,19 @@ export const getSuggestions = async ({ } const tokenSymbolName = parser.vocabulary.getSymbolicName(token)?.toLowerCase(); + if (tokenSymbolName) { + let type = monaco.languages.CompletionItemKind.Keyword; + let detail = SuggestionItemDetailsTags.Keyword; + if (dqlOperators.has(token)) { + type = monaco.languages.CompletionItemKind.Operator; + detail = SuggestionItemDetailsTags.Operator; + } completions.push({ text: tokenSymbolName, - type: monaco.languages.CompletionItemKind.Keyword, - detail: SuggestionItemDetailsTags.Keyword, + type, + detail, + insertText: `${tokenSymbolName} `, }); } }); diff --git a/src/plugins/data/public/antlr/shared/constants.ts b/src/plugins/data/public/antlr/shared/constants.ts index bd38cedc382..16fe97b737a 100644 --- a/src/plugins/data/public/antlr/shared/constants.ts +++ b/src/plugins/data/public/antlr/shared/constants.ts @@ -8,5 +8,6 @@ export const enum SuggestionItemDetailsTags { Keyword = 'Keyword', AggregateFunction = 'Aggregate Function', Value = 'Value', + Operator = 'Operator', } export const quotesRegex = /^'(.*)'$/; diff --git a/src/plugins/data/public/ui/query_editor/_query_editor.scss b/src/plugins/data/public/ui/query_editor/_query_editor.scss index 770160de8c1..3e9a6ac61c7 100644 --- a/src/plugins/data/public/ui/query_editor/_query_editor.scss +++ b/src/plugins/data/public/ui/query_editor/_query_editor.scss @@ -199,3 +199,23 @@ border: none; } } + +.suggest-widget { + position: relative; + + &.visible::after { + position: absolute; + height: auto; + bottom: auto; + left: 0; + width: 100%; + background-color: $euiColorLightestShade; + border: $euiBorderThin; + padding: $euiSizeXS; + text-align: left; + color: $euiColorDarkShade; + font-size: $euiFontSizeXS; + content: "Tab to insert, ESC to close window"; + display: block; + } +} diff --git a/src/plugins/data/public/ui/query_editor/editors/default_editor/index.tsx b/src/plugins/data/public/ui/query_editor/editors/default_editor/index.tsx index b7bb11c18a8..764b25d4b9a 100644 --- a/src/plugins/data/public/ui/query_editor/editors/default_editor/index.tsx +++ b/src/plugins/data/public/ui/query_editor/editors/default_editor/index.tsx @@ -55,6 +55,7 @@ export const DefaultInput: React.FC = ({ }} suggestionProvider={{ provideCompletionItems, + triggerCharacters: [' '], }} languageConfiguration={{ autoClosingPairs: [ @@ -65,6 +66,7 @@ export const DefaultInput: React.FC = ({ { open: "'", close: "'" }, ], }} + triggerSuggestOnFocus={true} />
{footerItems && ( diff --git a/src/plugins/data/public/ui/query_editor/editors/shared.tsx b/src/plugins/data/public/ui/query_editor/editors/shared.tsx index 6608b95042d..32cb0aa18a5 100644 --- a/src/plugins/data/public/ui/query_editor/editors/shared.tsx +++ b/src/plugins/data/public/ui/query_editor/editors/shared.tsx @@ -95,6 +95,7 @@ export const SingleLineInput: React.FC = ({ }} suggestionProvider={{ provideCompletionItems, + triggerCharacters: [' '], }} languageConfiguration={{ autoClosingPairs: [ @@ -108,6 +109,7 @@ export const SingleLineInput: React.FC = ({ }, ], }} + triggerSuggestOnFocus={true} />
diff --git a/src/plugins/data/public/ui/query_editor/query_editor.tsx b/src/plugins/data/public/ui/query_editor/query_editor.tsx index dc75d0be3d7..0abe9796bbf 100644 --- a/src/plugins/data/public/ui/query_editor/query_editor.tsx +++ b/src/plugins/data/public/ui/query_editor/query_editor.tsx @@ -309,6 +309,7 @@ export default class QueryEditorUI extends Component { insertText: s.insertText ?? s.text, range: s.replacePosition ?? defaultRange, detail: s.detail, + command: { id: 'editor.action.triggerSuggest', title: 'Trigger Next Suggestion' }, }; }) : [], diff --git a/src/plugins/opensearch_dashboards_react/public/code_editor/code_editor.tsx b/src/plugins/opensearch_dashboards_react/public/code_editor/code_editor.tsx index 8996297775c..137f369c9b9 100644 --- a/src/plugins/opensearch_dashboards_react/public/code_editor/code_editor.tsx +++ b/src/plugins/opensearch_dashboards_react/public/code_editor/code_editor.tsx @@ -108,6 +108,11 @@ export interface Props { * Should the editor use the dark theme */ useDarkTheme?: boolean; + + /** + * Whether the suggestion widget/window will be triggered upon clicking into the editor + */ + triggerSuggestOnFocus?: boolean; } export class CodeEditor extends React.Component { @@ -141,6 +146,12 @@ export class CodeEditor extends React.Component { if (this.props.editorDidMount) { this.props.editorDidMount(editor); } + + if (this.props.triggerSuggestOnFocus) { + editor.onDidFocusEditorWidget(() => { + editor.trigger('keyboard', 'editor.action.triggerSuggest', {}); + }); + } }; render() {