From c7e6cf9d951582e5f8067438bdd572c5bb3f6b9e Mon Sep 17 00:00:00 2001 From: Dmitriy Osipov <75555523+teunlao@users.noreply.github.com> Date: Wed, 26 Jul 2023 12:38:30 +0200 Subject: [PATCH] CB-3476 feat: save text selection and cursor position for each tab (#1846) Co-authored-by: EvgeniaBzzz <139753579+EvgeniaBzzz@users.noreply.github.com> --- .../plugin-codemirror6/src/IEditorRef.ts | 2 ++ .../src/ReactCodemirror.tsx | 7 +++++- .../SQLCodeEditorPanel/SQLCodeEditorPanel.tsx | 25 +++++++++++++++++-- 3 files changed, 31 insertions(+), 3 deletions(-) diff --git a/webapp/packages/plugin-codemirror6/src/IEditorRef.ts b/webapp/packages/plugin-codemirror6/src/IEditorRef.ts index d079d6fe67..4b9a54938e 100644 --- a/webapp/packages/plugin-codemirror6/src/IEditorRef.ts +++ b/webapp/packages/plugin-codemirror6/src/IEditorRef.ts @@ -6,8 +6,10 @@ * you may not use this file except in compliance with the License. */ import type { EditorView } from '@codemirror/view'; +import type { SelectionRange } from '@codemirror/state'; export interface IEditorRef { container: HTMLDivElement | null; view: EditorView | null; + selection: SelectionRange | null; } diff --git a/webapp/packages/plugin-codemirror6/src/ReactCodemirror.tsx b/webapp/packages/plugin-codemirror6/src/ReactCodemirror.tsx index 8541a5b9b2..f511c9e0e8 100644 --- a/webapp/packages/plugin-codemirror6/src/ReactCodemirror.tsx +++ b/webapp/packages/plugin-codemirror6/src/ReactCodemirror.tsx @@ -33,11 +33,15 @@ export const ReactCodemirror = observer( const [view, setView] = useState(null); const [incomingView, setIncomingView] = useState(null); const callbackRef = useObjectRef({ onChange, onUpdate }); + const [selection, setSelection] = useState(view?.state.selection.main ?? null); useLayoutEffect(() => { if (container) { const updateListener = EditorView.updateListener.of((update: ViewUpdate) => { const remote = update.transactions.some(tr => tr.annotation(External)); + if (update.selectionSet) { + setSelection(update.state.selection.main); + } if (update.docChanged && !remote) { const doc = update.state.doc; @@ -159,8 +163,9 @@ export const ReactCodemirror = observer( () => ({ container, view, + selection, }), - [container, view], + [container, view, selection], ); const context = useMemo( diff --git a/webapp/packages/plugin-sql-editor-new/src/SQLEditor/SQLCodeEditorPanel/SQLCodeEditorPanel.tsx b/webapp/packages/plugin-sql-editor-new/src/SQLEditor/SQLCodeEditorPanel/SQLCodeEditorPanel.tsx index ccc1468ae1..ca5a31ba61 100644 --- a/webapp/packages/plugin-sql-editor-new/src/SQLEditor/SQLCodeEditorPanel/SQLCodeEditorPanel.tsx +++ b/webapp/packages/plugin-sql-editor-new/src/SQLEditor/SQLCodeEditorPanel/SQLCodeEditorPanel.tsx @@ -6,13 +6,13 @@ * you may not use this file except in compliance with the License. */ import { observer } from 'mobx-react-lite'; -import { useState } from 'react'; +import { useEffect, useState } from 'react'; import { MenuBarSmallItem, useExecutor, useS, useTranslate } from '@cloudbeaver/core-blocks'; import { useService } from '@cloudbeaver/core-di'; import { NotificationService } from '@cloudbeaver/core-events'; import { DATA_CONTEXT_NAV_NODE, getNodesFromContext, NavNodeManagerService } from '@cloudbeaver/core-navigation-tree'; -import { TabContainerPanelComponent, useDNDBox } from '@cloudbeaver/core-ui'; +import { TabContainerPanelComponent, useDNDBox, useTabLocalState } from '@cloudbeaver/core-ui'; import { closeCompletion, IEditorRef, Prec, ReactCodemirrorPanel, useCodemirrorExtensions } from '@cloudbeaver/plugin-codemirror6'; import type { ISqlEditorModeProps } from '@cloudbeaver/plugin-sql-editor'; @@ -25,15 +25,36 @@ import { useSqlDialectExtension } from '../useSqlDialectExtension'; import style from './SQLCodeEditorPanel.m.css'; import { useSQLCodeEditorPanel } from './useSQLCodeEditorPanel'; +interface ILocalSQLCodeEditorPanelState { + selection: { from: number; to: number }; +}; export const SQLCodeEditorPanel: TabContainerPanelComponent = observer(function SQLCodeEditorPanel({ data }) { const notificationService = useService(NotificationService); const navNodeManagerService = useService(NavNodeManagerService); const translate = useTranslate(); + const localState = useTabLocalState(() => ({ selection: { from: 0, to: 0 } })); const styles = useS(style); const [editorRef, setEditorRef] = useState(null); const editor = useSQLCodeEditor(editorRef); + + useEffect(() => { + + editorRef?.view?.dispatch({ + selection: { anchor: localState.selection.from, head: localState.selection.to }, + scrollIntoView: true, + }); + }, [editorRef?.view, localState]); + + useEffect(() => { + if (!editorRef?.selection) { + return; + } + + localState.selection = { ...editorRef?.selection }; + }, [editorRef?.selection]); + const panel = useSQLCodeEditorPanel(data, editor); const extensions = useCodemirrorExtensions(undefined, [ACTIVE_QUERY_EXTENSION, Prec.lowest(QUERY_STATUS_GUTTER_EXTENSION)]); const autocompletion = useSqlDialectAutocompletion(data);