From 1c992d1b60d056992054171c6337ba128fa90ef8 Mon Sep 17 00:00:00 2001 From: Wroud Date: Tue, 5 Apr 2022 14:04:32 +0300 Subject: [PATCH] fix(plugin-sql-editor): reduce server calls --- .../plugin-sql-editor/src/SQLParser.ts | 5 ++ .../useSQLCodeEditorPanel.ts | 82 +++++++++++++++---- .../src/SqlEditor/useSqlEditor.ts | 12 +-- 3 files changed, 80 insertions(+), 19 deletions(-) diff --git a/webapp/packages/plugin-sql-editor/src/SQLParser.ts b/webapp/packages/plugin-sql-editor/src/SQLParser.ts index f55ea044ba..aedaec6142 100644 --- a/webapp/packages/plugin-sql-editor/src/SQLParser.ts +++ b/webapp/packages/plugin-sql-editor/src/SQLParser.ts @@ -316,6 +316,11 @@ export class SQLParser { return this; } + isEndsWithDelimiter(): boolean { + this.update(); + return this.scriptDelimiters.some(delimiter => this.parsedScript?.endsWith(delimiter)); + } + private update() { if (this.parsedScript !== this.script) { this.parse(this.script); diff --git a/webapp/packages/plugin-sql-editor/src/SqlEditor/SQLCodeEditorPanel/useSQLCodeEditorPanel.ts b/webapp/packages/plugin-sql-editor/src/SqlEditor/SQLCodeEditorPanel/useSQLCodeEditorPanel.ts index e7bdb04710..cafd3db322 100644 --- a/webapp/packages/plugin-sql-editor/src/SqlEditor/SQLCodeEditorPanel/useSQLCodeEditorPanel.ts +++ b/webapp/packages/plugin-sql-editor/src/SqlEditor/SQLCodeEditorPanel/useSQLCodeEditorPanel.ts @@ -18,14 +18,19 @@ import { action } from 'mobx'; import type { IControlledCodeMirror } from 'react-codemirror2'; import { useExecutor, useObservableRef } from '@cloudbeaver/core-blocks'; -import { throttleAsync } from '@cloudbeaver/core-utils'; +import type { SqlCompletionProposal } from '@cloudbeaver/core-sdk'; import type { SQLCodeEditorController } from '../SQLCodeEditor/SQLCodeEditorController'; import type { ISQLEditorData } from '../useSqlEditor'; interface ISQLCodeEditorPanelData { + proposalsWordFrom: Position | null; + proposalsWord: string | null; + proposalsMode: boolean | null; + proposals: SqlCompletionProposal[] | null; readonly activeSuggest: boolean; readonly bindings: Omit; + closeHint(): void; } interface ISQLCodeEditorPanelDataPrivate extends ISQLCodeEditorPanelData { @@ -46,6 +51,7 @@ export function useSQLCodeEditorPanel( controller: SQLCodeEditorController | null ): ISQLCodeEditorPanelData { const editorPanelData = useObservableRef(() => ({ + proposals: null, activeSuggest: true, options: { theme: 'material', @@ -121,7 +127,7 @@ export function useSQLCodeEditorPanel( const nextCursor = editor.getCursor('from'); if (nextCursor.line !== lastChange.from.line) { - editor.closeHint(); + this.closeHint(); return; } @@ -155,7 +161,7 @@ export function useSQLCodeEditorPanel( cursor = from; if (closeCharacters.test(ch) || from.line !== cursor.line) { - editor.closeHint(); + this.closeHint(); } else { editor.state.completionActive.update(); } @@ -167,6 +173,16 @@ export function useSQLCodeEditorPanel( this.highlightActiveQuery(); }, + closeHint() { + const editor = this.controller?.getEditor(); + + if (!editor) { + return; + } + + editor.closeHint(); + }, + async showHint(activeSuggest: boolean) { const editor = this.controller?.getEditor(); @@ -191,24 +207,62 @@ export function useSQLCodeEditorPanel( }, - getHandleAutocomplete: throttleAsync(async function getHandleAutocomplete( + async getHandleAutocomplete( editor: Editor, options: ShowHintOptions ): Promise { const cursor = editor.getCursor('from'); - const cursorPosition = getAbsolutePosition(editor, cursor); const [from, to, word] = getWordRange(editor, cursor); - - let proposals = await editorPanelData.data.getHintProposals(cursorPosition, !options.completeSingle); - - proposals = proposals.filter( - ({ displayString }) => displayString.toLocaleLowerCase() !== word.toLocaleLowerCase() + const cursorPosition = getAbsolutePosition( + editor, word.length > 0 + ? { ...from, ch: from.ch + 1 } + : from ); - if (proposals.length === 0) { - return; + const proposalWord = word.slice(0, 1); + let proposals = editorPanelData.proposals; + + if ( + proposals === null + || editorPanelData.proposalsWord !== proposalWord + || editorPanelData.proposalsMode !== options.completeSingle + || editorPanelData.proposalsWordFrom?.ch !== from.ch + || editorPanelData.proposalsWordFrom.line !== from.line + || !word.startsWith(editorPanelData.proposalsWord) + ) { + const proposalsMode = options.completeSingle ?? false; + editorPanelData.proposalsMode = proposalsMode; + editorPanelData.proposalsWord = proposalWord; + editorPanelData.proposalsWordFrom = from; + editorPanelData.proposals = []; + + proposals = await editorPanelData.data.getHintProposals(cursorPosition, !options.completeSingle); + + if ( + editorPanelData.proposalsWord === proposalWord + && editorPanelData.proposalsWordFrom === from + && editorPanelData.proposalsMode === proposalsMode + ) { + editorPanelData.proposals = proposals; + + if (editor.state.completionActive && proposals.length > 0) { + editor.state.completionActive.update(); + return; + } + } } + proposals = proposals.filter( + ({ displayString }) => ( + displayString.toLocaleLowerCase() !== word.toLocaleLowerCase() + && displayString.toLocaleLowerCase().startsWith(word.toLocaleLowerCase()) + ) + ); + + // if (proposals.length === 0) { + // return; + // } + const hints: Hints = { from, to, @@ -219,7 +273,7 @@ export function useSQLCodeEditorPanel( }; return hints; - }, 1000 / 3), + }, highlightActiveQuery() { this.controller?.highlightSegment(true); @@ -249,7 +303,7 @@ export function useSQLCodeEditorPanel( useExecutor({ executor: data.onExecute, handlers: [function updateHighlight() { - controller?.getEditor()?.closeHint(); + editorPanelData.closeHint(); controller?.resetLineStateHighlight(); }], }); diff --git a/webapp/packages/plugin-sql-editor/src/SqlEditor/useSqlEditor.ts b/webapp/packages/plugin-sql-editor/src/SqlEditor/useSqlEditor.ts index 2fa842a7a1..4cc6e00b58 100644 --- a/webapp/packages/plugin-sql-editor/src/SqlEditor/useSqlEditor.ts +++ b/webapp/packages/plugin-sql-editor/src/SqlEditor/useSqlEditor.ts @@ -188,7 +188,7 @@ export function useSqlEditor(state: ISqlEditorTabState): ISQLEditorData { this.onUpdate.execute(); }, - async getHintProposals(position, simple) { + getHintProposals: throttleAsync(async function getHintProposals(this: ISQLEditorDataPrivate, position, simple) { if (!this.state.executionContext) { return []; } @@ -204,7 +204,7 @@ export function useSqlEditor(state: ISqlEditorTabState): ISQLEditorData { ); return proposals; - }, + }, 1000 / 3), async formatScript(): Promise { if (this.isDisabled || this.isScriptEmpty || !this.state.executionContext) { @@ -225,8 +225,8 @@ export function useSqlEditor(state: ISqlEditorTabState): ISQLEditorData { this.setQuery( query.substring(0, script.begin) - + formatted - + query.substring(script.end) + + formatted + + query.substring(script.end) ); } finally { this.readonlyState = false; @@ -333,7 +333,9 @@ export function useSqlEditor(state: ISqlEditorTabState): ISQLEditorData { setQuery(query: string): void { this.state.query = query; this.parser.setScript(query); - this.updateParserScriptsThrottle(); + if (this.parser.isEndsWithDelimiter()) { + this.updateParserScriptsThrottle(); + } this.onUpdate.execute(); },