Skip to content

Commit 1867336

Browse files
fix(editor): implement missing methods, fix cursor position clearing (apache#38603)
1 parent f538326 commit 1867336

4 files changed

Lines changed: 103 additions & 4 deletions

File tree

superset-frontend/packages/superset-core/src/editors/index.ts

Lines changed: 43 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -369,6 +369,28 @@ export interface EditorProps {
369369
theme?: SupersetTheme;
370370
}
371371

372+
/**
373+
* A single text change expressed as an offset-based replacement.
374+
*/
375+
export interface ContentChange {
376+
/** Character offset in the document where the replaced range starts */
377+
rangeOffset: number;
378+
/** Length in characters of the replaced range (0 for pure insertions) */
379+
rangeLength: number;
380+
/** Text inserted at rangeOffset (empty string for pure deletions) */
381+
text: string;
382+
}
383+
384+
/**
385+
* Payload delivered to `onDidChangeContent` listeners.
386+
*/
387+
export interface ContentChangeEvent {
388+
/** Returns the full current content of the editor */
389+
getValue(): string;
390+
/** The individual changes that occurred in this event */
391+
changes: ReadonlyArray<ContentChange>;
392+
}
393+
372394
/**
373395
* Imperative API for controlling the editor programmatically.
374396
*
@@ -492,6 +514,27 @@ export interface EditorHandle {
492514
* - CodeMirror: editor.requestMeasure()
493515
*/
494516
resize(): void;
517+
518+
/**
519+
* Subscribe to content changes in the editor.
520+
*
521+
* The listener receives a {@link ContentChangeEvent} with:
522+
* - `getValue()` — lazy accessor for the full content (call only when needed
523+
* to avoid unnecessary O(n) string allocation on every keystroke)
524+
* - `changes` — the individual edits that occurred, as offset-based replacements
525+
*
526+
* @param listener Called with a ContentChangeEvent on every change
527+
* @param thisArgs Optional `this` context for the listener
528+
* @returns A Disposable that unsubscribes the listener when disposed
529+
*
530+
* @example
531+
* const disposable = editor.onDidChangeContent(e => {
532+
* setStatements(parseStatements(e.getValue()));
533+
* });
534+
* // Later, to unsubscribe:
535+
* disposable.dispose();
536+
*/
537+
onDidChangeContent: Event<ContentChangeEvent>;
495538
}
496539

497540
/**

superset-frontend/packages/superset-core/src/sqlLab/index.ts

Lines changed: 16 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -252,6 +252,22 @@ export interface QueryResult {
252252
*/
253253
export declare const getActivePanel: () => Panel;
254254

255+
/**
256+
* Switches the active panel in the SQL Lab south pane.
257+
* Built-in panel IDs are 'Results' and 'History'.
258+
* Pinned table panels use the table's ID as their panel ID.
259+
*
260+
* @param panelId The ID of the panel to activate
261+
* @returns Promise that resolves when the panel is activated
262+
*
263+
* @example
264+
* ```typescript
265+
* // Focus the Results panel after running a query
266+
* await setActivePanel('Results');
267+
* ```
268+
*/
269+
export declare function setActivePanel(panelId: string): Promise<void>;
270+
255271
/**
256272
* Gets the currently active tab in SQL Lab.
257273
*

superset-frontend/src/core/editors/AceEditorProvider.tsx

Lines changed: 37 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -55,6 +55,8 @@ type Range = editors.Range;
5555
type Selection = editors.Selection;
5656
type EditorAnnotation = editors.EditorAnnotation;
5757
type CompletionProvider = editors.CompletionProvider;
58+
type ContentChange = editors.ContentChange;
59+
type ContentChangeEvent = editors.ContentChangeEvent;
5860

5961
/**
6062
* Maps EditorLanguage to the corresponding Ace editor component.
@@ -117,10 +119,14 @@ const createAceEditorHandle = (
117119
},
118120

119121
moveCursorToPosition: (position: Position) => {
120-
aceEditorRef.current?.editor?.moveCursorToPosition({
121-
row: position.line,
122-
column: position.column,
123-
});
122+
const editor = aceEditorRef.current?.editor;
123+
if (editor) {
124+
editor.clearSelection();
125+
editor.moveCursorToPosition({
126+
row: position.line,
127+
column: position.column,
128+
});
129+
}
124130
},
125131

126132
getSelections: (): Selection[] => {
@@ -186,6 +192,33 @@ const createAceEditorHandle = (
186192
resize: () => {
187193
aceEditorRef.current?.editor?.resize();
188194
},
195+
196+
onDidChangeContent: (listener, thisArgs?) => {
197+
const editor = aceEditorRef.current?.editor;
198+
if (!editor) return new Disposable(() => {});
199+
const bound = (thisArgs ? listener.bind(thisArgs) : listener) as (
200+
e: ContentChangeEvent,
201+
) => void;
202+
const handler = (delta: {
203+
action: 'insert' | 'remove';
204+
start: { row: number; column: number };
205+
lines: string[];
206+
}) => {
207+
const rangeOffset = editor.session.doc.positionToIndex(delta.start);
208+
const changeText = delta.lines.join(
209+
editor.session.doc.getNewLineCharacter(),
210+
);
211+
const change: ContentChange =
212+
delta.action === 'insert'
213+
? { rangeOffset, rangeLength: 0, text: changeText }
214+
: { rangeOffset, rangeLength: changeText.length, text: '' };
215+
bound({ getValue: () => editor.getValue(), changes: [change] });
216+
};
217+
editor.session.on('change', handler);
218+
return new Disposable(() => {
219+
editor.session.off('change', handler);
220+
});
221+
},
189222
});
190223

191224
/**

superset-frontend/src/core/sqlLab/index.ts

Lines changed: 7 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -694,6 +694,12 @@ const setSchema: typeof sqlLabApi.setSchema = async (schema: string | null) => {
694694
store.dispatch(queryEditorSetSchema(queryEditor ?? null, schema));
695695
};
696696

697+
const setActivePanel: typeof sqlLabApi.setActivePanel = async (
698+
panelId: string,
699+
) => {
700+
store.dispatch({ type: SET_ACTIVE_SOUTHPANE_TAB, tabId: panelId });
701+
};
702+
697703
export const sqlLab: typeof sqlLabApi = {
698704
CTASMethod,
699705
getActivePanel,
@@ -719,6 +725,7 @@ export const sqlLab: typeof sqlLabApi = {
719725
setDatabase,
720726
setCatalog,
721727
setSchema,
728+
setActivePanel,
722729
};
723730

724731
// Export all models

0 commit comments

Comments
 (0)