You are viewing a plain text version of this content. The canonical link for it is here.
Posted to commits@superset.apache.org by mi...@apache.org on 2026/03/13 12:07:03 UTC
(superset) branch master updated: fix(editor): implement missing methods, fix cursor position clearing (#38603)
This is an automated email from the ASF dual-hosted git repository.
michaelsmolina pushed a commit to branch master
in repository https://gitbox.apache.org/repos/asf/superset.git
The following commit(s) were added to refs/heads/master by this push:
new 18673369076 fix(editor): implement missing methods, fix cursor position clearing (#38603)
18673369076 is described below
commit 186733690761b16d4856e0a7fca09c6f5204b758
Author: Michael S. Molina <70...@users.noreply.github.com>
AuthorDate: Fri Mar 13 09:06:55 2026 -0300
fix(editor): implement missing methods, fix cursor position clearing (#38603)
---
.../packages/superset-core/src/editors/index.ts | 43 ++++++++++++++++++++++
.../packages/superset-core/src/sqlLab/index.ts | 16 ++++++++
.../src/core/editors/AceEditorProvider.tsx | 41 +++++++++++++++++++--
superset-frontend/src/core/sqlLab/index.ts | 7 ++++
4 files changed, 103 insertions(+), 4 deletions(-)
diff --git a/superset-frontend/packages/superset-core/src/editors/index.ts b/superset-frontend/packages/superset-core/src/editors/index.ts
index a1ed3ed9653..0fea7c7fe71 100644
--- a/superset-frontend/packages/superset-core/src/editors/index.ts
+++ b/superset-frontend/packages/superset-core/src/editors/index.ts
@@ -369,6 +369,28 @@ export interface EditorProps {
theme?: SupersetTheme;
}
+/**
+ * A single text change expressed as an offset-based replacement.
+ */
+export interface ContentChange {
+ /** Character offset in the document where the replaced range starts */
+ rangeOffset: number;
+ /** Length in characters of the replaced range (0 for pure insertions) */
+ rangeLength: number;
+ /** Text inserted at rangeOffset (empty string for pure deletions) */
+ text: string;
+}
+
+/**
+ * Payload delivered to `onDidChangeContent` listeners.
+ */
+export interface ContentChangeEvent {
+ /** Returns the full current content of the editor */
+ getValue(): string;
+ /** The individual changes that occurred in this event */
+ changes: ReadonlyArray<ContentChange>;
+}
+
/**
* Imperative API for controlling the editor programmatically.
*
@@ -492,6 +514,27 @@ export interface EditorHandle {
* - CodeMirror: editor.requestMeasure()
*/
resize(): void;
+
+ /**
+ * Subscribe to content changes in the editor.
+ *
+ * The listener receives a {@link ContentChangeEvent} with:
+ * - `getValue()` — lazy accessor for the full content (call only when needed
+ * to avoid unnecessary O(n) string allocation on every keystroke)
+ * - `changes` — the individual edits that occurred, as offset-based replacements
+ *
+ * @param listener Called with a ContentChangeEvent on every change
+ * @param thisArgs Optional `this` context for the listener
+ * @returns A Disposable that unsubscribes the listener when disposed
+ *
+ * @example
+ * const disposable = editor.onDidChangeContent(e => {
+ * setStatements(parseStatements(e.getValue()));
+ * });
+ * // Later, to unsubscribe:
+ * disposable.dispose();
+ */
+ onDidChangeContent: Event<ContentChangeEvent>;
}
/**
diff --git a/superset-frontend/packages/superset-core/src/sqlLab/index.ts b/superset-frontend/packages/superset-core/src/sqlLab/index.ts
index cae3bc2d4e5..844c25f9ea6 100644
--- a/superset-frontend/packages/superset-core/src/sqlLab/index.ts
+++ b/superset-frontend/packages/superset-core/src/sqlLab/index.ts
@@ -252,6 +252,22 @@ export interface QueryResult {
*/
export declare const getActivePanel: () => Panel;
+/**
+ * Switches the active panel in the SQL Lab south pane.
+ * Built-in panel IDs are 'Results' and 'History'.
+ * Pinned table panels use the table's ID as their panel ID.
+ *
+ * @param panelId The ID of the panel to activate
+ * @returns Promise that resolves when the panel is activated
+ *
+ * @example
+ * ```typescript
+ * // Focus the Results panel after running a query
+ * await setActivePanel('Results');
+ * ```
+ */
+export declare function setActivePanel(panelId: string): Promise<void>;
+
/**
* Gets the currently active tab in SQL Lab.
*
diff --git a/superset-frontend/src/core/editors/AceEditorProvider.tsx b/superset-frontend/src/core/editors/AceEditorProvider.tsx
index abdf76d14f1..3deb345a217 100644
--- a/superset-frontend/src/core/editors/AceEditorProvider.tsx
+++ b/superset-frontend/src/core/editors/AceEditorProvider.tsx
@@ -55,6 +55,8 @@ type Range = editors.Range;
type Selection = editors.Selection;
type EditorAnnotation = editors.EditorAnnotation;
type CompletionProvider = editors.CompletionProvider;
+type ContentChange = editors.ContentChange;
+type ContentChangeEvent = editors.ContentChangeEvent;
/**
* Maps EditorLanguage to the corresponding Ace editor component.
@@ -117,10 +119,14 @@ const createAceEditorHandle = (
},
moveCursorToPosition: (position: Position) => {
- aceEditorRef.current?.editor?.moveCursorToPosition({
- row: position.line,
- column: position.column,
- });
+ const editor = aceEditorRef.current?.editor;
+ if (editor) {
+ editor.clearSelection();
+ editor.moveCursorToPosition({
+ row: position.line,
+ column: position.column,
+ });
+ }
},
getSelections: (): Selection[] => {
@@ -186,6 +192,33 @@ const createAceEditorHandle = (
resize: () => {
aceEditorRef.current?.editor?.resize();
},
+
+ onDidChangeContent: (listener, thisArgs?) => {
+ const editor = aceEditorRef.current?.editor;
+ if (!editor) return new Disposable(() => {});
+ const bound = (thisArgs ? listener.bind(thisArgs) : listener) as (
+ e: ContentChangeEvent,
+ ) => void;
+ const handler = (delta: {
+ action: 'insert' | 'remove';
+ start: { row: number; column: number };
+ lines: string[];
+ }) => {
+ const rangeOffset = editor.session.doc.positionToIndex(delta.start);
+ const changeText = delta.lines.join(
+ editor.session.doc.getNewLineCharacter(),
+ );
+ const change: ContentChange =
+ delta.action === 'insert'
+ ? { rangeOffset, rangeLength: 0, text: changeText }
+ : { rangeOffset, rangeLength: changeText.length, text: '' };
+ bound({ getValue: () => editor.getValue(), changes: [change] });
+ };
+ editor.session.on('change', handler);
+ return new Disposable(() => {
+ editor.session.off('change', handler);
+ });
+ },
});
/**
diff --git a/superset-frontend/src/core/sqlLab/index.ts b/superset-frontend/src/core/sqlLab/index.ts
index 8e16a41613a..b14be7efd07 100644
--- a/superset-frontend/src/core/sqlLab/index.ts
+++ b/superset-frontend/src/core/sqlLab/index.ts
@@ -694,6 +694,12 @@ const setSchema: typeof sqlLabApi.setSchema = async (schema: string | null) => {
store.dispatch(queryEditorSetSchema(queryEditor ?? null, schema));
};
+const setActivePanel: typeof sqlLabApi.setActivePanel = async (
+ panelId: string,
+) => {
+ store.dispatch({ type: SET_ACTIVE_SOUTHPANE_TAB, tabId: panelId });
+};
+
export const sqlLab: typeof sqlLabApi = {
CTASMethod,
getActivePanel,
@@ -719,6 +725,7 @@ export const sqlLab: typeof sqlLabApi = {
setDatabase,
setCatalog,
setSchema,
+ setActivePanel,
};
// Export all models