Skip to content

Commit

Permalink
Merge branch 'devel' into CB-4573-CE-update-PostgreSQL
Browse files Browse the repository at this point in the history
  • Loading branch information
dariamarutkina authored Jan 25, 2024
2 parents 94ad2c4 + 3e93752 commit 9da64e2
Show file tree
Hide file tree
Showing 6 changed files with 68 additions and 28 deletions.
15 changes: 13 additions & 2 deletions webapp/packages/core-blocks/src/FormControls/Textarea.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -6,7 +6,7 @@
* you may not use this file except in compliance with the License.
*/
import { observer } from 'mobx-react-lite';
import { useCallback, useContext } from 'react';
import { useCallback, useContext, useLayoutEffect, useRef } from 'react';

import { filterLayoutFakeProps, getLayoutProps } from '../Containers/filterLayoutFakeProps';
import type { ILayoutSizeProps } from '../Containers/ILayoutSizeProps';
Expand All @@ -23,6 +23,7 @@ type BaseProps = Omit<React.TextareaHTMLAttributes<HTMLTextAreaElement>, 'onChan
description?: string;
labelTooltip?: string;
embedded?: boolean;
cursorInitiallyAtEnd?: boolean;
};

type ControlledProps = BaseProps & {
Expand Down Expand Up @@ -54,9 +55,11 @@ export const Textarea: TextareaType = observer(function Textarea({
description,
labelTooltip,
embedded,
cursorInitiallyAtEnd,
onChange = () => {},
...rest
}: ControlledProps | ObjectProps<any, any>) {
const textareaRef = useRef<HTMLTextAreaElement | null>(null);
const layoutProps = getLayoutProps(rest);
rest = filterLayoutFakeProps(rest);
const styles = useS(textareaStyle);
Expand All @@ -79,13 +82,21 @@ export const Textarea: TextareaType = observer(function Textarea({

const value = state ? state[name] : controlledValue;

useLayoutEffect(() => {
if (cursorInitiallyAtEnd && typeof value === 'string') {
const position = value.length;
textareaRef.current?.setSelectionRange(position, position);
}
}, [cursorInitiallyAtEnd]);

return (
<Field {...layoutProps} className={s(styles, { field: true, embedded }, className)}>
<FieldLabel className={s(styles, { fieldLabel: true })} title={labelTooltip || rest.title} required={required}>
<FieldLabel className={s(styles, { fieldLabel: true })} title={labelTooltip || rest.title} required={required}>
{children}
</FieldLabel>
<textarea
{...rest}
ref={textareaRef}
required={required}
className={s(styles, { textarea: true })}
value={value ?? ''}
Expand Down
10 changes: 9 additions & 1 deletion webapp/packages/plugin-codemirror6/src/IReactCodemirrorProps.ts
Original file line number Diff line number Diff line change
Expand Up @@ -5,17 +5,25 @@
* Licensed under the Apache License, Version 2.0.
* you may not use this file except in compliance with the License.
*/
import type { Compartment, Extension } from '@codemirror/state';
import type { Compartment, Extension, SelectionRange } from '@codemirror/state';
import type { ViewUpdate } from '@codemirror/view';

/** Currently we support only main selection range */
interface ISelection {
anchor: number;
head?: number;
}

export interface IReactCodeMirrorProps extends React.PropsWithChildren {
/** in case of using editor in editing mode its better for performance to use getValue instead */
value?: string;
cursor?: ISelection;
incomingValue?: string;
getValue?: () => string;
extensions?: Map<Compartment, Extension>;
readonly?: boolean;
autoFocus?: boolean;
onChange?: (value: string, update: ViewUpdate) => void;
onCursorChange?: (selection: SelectionRange, update: ViewUpdate) => void;
onUpdate?: (update: ViewUpdate) => void;
}
44 changes: 35 additions & 9 deletions webapp/packages/plugin-codemirror6/src/ReactCodemirror.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -6,7 +6,7 @@
* you may not use this file except in compliance with the License.
*/
import { MergeView } from '@codemirror/merge';
import { Annotation, Compartment, Extension, StateEffect } from '@codemirror/state';
import { Annotation, Compartment, Extension, StateEffect, TransactionSpec } from '@codemirror/state';
import { EditorView, ViewUpdate } from '@codemirror/view';
import { observer } from 'mobx-react-lite';
import { forwardRef, useImperativeHandle, useLayoutEffect, useMemo, useRef, useState } from 'react';
Expand All @@ -22,7 +22,19 @@ const External = Annotation.define<boolean>();

export const ReactCodemirror = observer<IReactCodeMirrorProps, IEditorRef>(
forwardRef(function ReactCodemirror(
{ children, getValue, value, incomingValue, extensions = new Map<Compartment, Extension>(), readonly, autoFocus, onChange, onUpdate },
{
children,
getValue,
value,
cursor,
incomingValue,
extensions = new Map<Compartment, Extension>(),
readonly,
autoFocus,
onChange,
onCursorChange,
onUpdate,
},
ref,
) {
value = value ?? getValue?.();
Expand All @@ -32,7 +44,7 @@ export const ReactCodemirror = observer<IReactCodeMirrorProps, IEditorRef>(
const [container, setContainer] = useState<HTMLDivElement | null>(null);
const [view, setView] = useState<EditorView | null>(null);
const [incomingView, setIncomingView] = useState<EditorView | null>(null);
const callbackRef = useObjectRef({ onChange, onUpdate });
const callbackRef = useObjectRef({ onChange, onCursorChange, onUpdate });
const [selection, setSelection] = useState(view?.state.selection.main ?? null);

useLayoutEffect(() => {
Expand All @@ -50,6 +62,11 @@ export const ReactCodemirror = observer<IReactCodeMirrorProps, IEditorRef>(
callbackRef.onChange?.(value, update);
}

if (update.selectionSet && !remote) {
const selection = update.state.selection.main;
callbackRef.onCursorChange?.(selection, update);
}

callbackRef.onUpdate?.(update);
});

Expand Down Expand Up @@ -136,13 +153,22 @@ export const ReactCodemirror = observer<IReactCodeMirrorProps, IEditorRef>(
});

useLayoutEffect(() => {
if (value !== undefined && view && value !== view.state.doc.toString()) {
view.dispatch({
changes: { from: 0, to: view.state.doc.length, insert: value },
annotations: [External.of(true)],
});
if (view) {
const transaction: TransactionSpec = { annotations: [External.of(true)] };

if (value !== undefined && value !== view.state.doc.toString()) {
transaction.changes = { from: 0, to: view.state.doc.length, insert: value };
}

if (cursor && (view.state.selection.main.anchor !== cursor.anchor || view.state.selection.main.head !== cursor.head)) {
transaction.selection = cursor;
}

if (transaction.changes || transaction.selection) {
view.dispatch(transaction);
}
}
}, [value, view]);
});

useLayoutEffect(() => {
if (incomingValue !== undefined && incomingView && incomingValue !== incomingView.state.doc.toString()) {
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -125,13 +125,17 @@ export const SQLCodeEditorPanel: TabContainerPanelComponent<ISqlEditorModeProps>
<SQLCodeEditorLoader
ref={setEditorRef}
getValue={() => data.value}
cursor={{
anchor: data.cursor.begin,
head: data.cursor.end,
}}
incomingValue={data.incomingValue}
extensions={extensions}
readonly={data.readonly}
autoFocus
lineNumbers
onChange={panel.onQueryChange}
onUpdate={panel.onUpdate}
onCursorChange={selection => panel.onCursorChange(selection.from, selection.to)}
>
{data.isIncomingChanges && (
<>
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -5,6 +5,7 @@
* Licensed under the Apache License, Version 2.0.
* you may not use this file except in compliance with the License.
*/
import { action } from 'mobx';
import { useCallback } from 'react';

import { useExecutor, useObservableRef } from '@cloudbeaver/core-blocks';
Expand All @@ -17,7 +18,7 @@ import type { IEditor } from '../SQLCodeEditor/useSQLCodeEditor';
interface State {
highlightActiveQuery: () => void;
onQueryChange: (query: string) => void;
onUpdate: (update: ViewUpdate) => void;
onCursorChange: (begin: number, end?: number) => void;
}

export function useSQLCodeEditorPanel(data: ISQLEditorData, editor: IEditor) {
Expand All @@ -35,21 +36,12 @@ export function useSQLCodeEditorPanel(data: ISQLEditorData, editor: IEditor) {
onQueryChange(query: string) {
this.data.setScript(query);
},
onUpdate(update: ViewUpdate) {
const transactions = update.transactions.filter(t => t.selection !== undefined);
const lastTransaction = transactions[transactions.length - 1] as Transaction | undefined;

if (lastTransaction) {
const from = lastTransaction.selection?.main.from ?? update.state.selection.main.from;
const to = lastTransaction.selection?.main.to ?? update.state.selection.main.to;

this.data.setCursor(from, to);
}
onCursorChange(begin: number, end?: number) {
this.data.setCursor(begin, end);
},
}),
{},
{ onQueryChange: action.bound, onCursorChange: action.bound },
{ editor, data },
['onQueryChange', 'onUpdate'],
);

const updateHighlight = useCallback(
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -138,7 +138,6 @@ export function useSqlEditor(state: ISqlEditorTabState): ISQLEditorData {
onExecute: new SyncExecutor(),
onSegmentExecute: new SyncExecutor(),
onUpdate: new SyncExecutor(),
onFormat: new SyncExecutor(),
parser: new SQLParser(),

cursor: { begin: 0, end: 0 },
Expand Down Expand Up @@ -225,8 +224,8 @@ export function useSqlEditor(state: ISqlEditorTabState): ISQLEditorData {
this.readonlyState = true;
const formatted = await this.sqlDialectInfoService.formatScript(this.dataSource.executionContext, script.query);

this.onFormat.execute([script, formatted]);
this.setScript(query.substring(0, script.begin) + formatted + query.substring(script.end));
this.setCursor(script.begin + formatted.length);
} finally {
this.readonlyState = false;
}
Expand Down

0 comments on commit 9da64e2

Please sign in to comment.