CB-3476 feat: save text selection and cursor position for each tab (#1846)

Co-authored-by: EvgeniaBzzz <139753579+EvgeniaBzzz@users.noreply.github.com>
This commit is contained in:
Dmitriy Osipov
2023-07-26 13:38:30 +03:00
committed by GitHub
co-authored by EvgeniaBzzz
parent 27b0180107
commit c7e6cf9d95
3 changed files with 31 additions and 3 deletions
@@ -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;
}
@@ -33,11 +33,15 @@ export const ReactCodemirror = observer<IReactCodeMirrorProps, IEditorRef>(
const [view, setView] = useState<EditorView | null>(null);
const [incomingView, setIncomingView] = useState<EditorView | null>(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<IReactCodeMirrorProps, IEditorRef>(
() => ({
container,
view,
selection,
}),
[container, view],
[container, view, selection],
);
const context = useMemo<IReactCodemirrorContext>(
@@ -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<ISqlEditorModeProps> = observer(function SQLCodeEditorPanel({ data }) {
const notificationService = useService(NotificationService);
const navNodeManagerService = useService(NavNodeManagerService);
const translate = useTranslate();
const localState = useTabLocalState<ILocalSQLCodeEditorPanelState>(() => ({ selection: { from: 0, to: 0 } }));
const styles = useS(style);
const [editorRef, setEditorRef] = useState<IEditorRef | null>(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);