Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
31 changes: 26 additions & 5 deletions docs/developer_portal/extensions/contribution-types.md
Original file line number Diff line number Diff line change
Expand Up @@ -34,7 +34,7 @@ Frontend contribution types allow extensions to extend Superset's user interface

Extensions can add new views or panels to the host application, such as custom SQL Lab panels, dashboards, or other UI components. Each view is registered with a unique ID and can be activated or deactivated as needed. Contribution areas are uniquely identified (e.g., `sqllab.panels` for SQL Lab panels), enabling seamless integration into specific parts of the application.

``` json
```json
"frontend": {
"contributions": {
"views": {
Expand All @@ -53,7 +53,7 @@ Extensions can add new views or panels to the host application, such as custom S

Extensions can define custom commands that can be executed within the host application, such as context-aware actions or menu options. Each command can specify properties like a unique command identifier, an icon, a title, and a description. These commands can be invoked by users through menus, keyboard shortcuts, or other UI elements, enabling extensions to add rich, interactive functionality to Superset.

``` json
```json
"frontend": {
"contributions": {
"commands": [
Expand All @@ -72,7 +72,7 @@ Extensions can define custom commands that can be executed within the host appli

Extensions can contribute new menu items or context menus to the host application, providing users with additional actions and options. Each menu item can specify properties such as the target view, the command to execute, its placement (primary, secondary, or context), and conditions for when it should be displayed. Menu contribution areas are uniquely identified (e.g., `sqllab.editor` for the SQL Lab editor), allowing extensions to seamlessly integrate their functionality into specific menus and workflows within Superset.

``` json
```json
"frontend": {
"contributions": {
"menus": {
Expand Down Expand Up @@ -101,6 +101,27 @@ Extensions can contribute new menu items or context menus to the host applicatio
}
```

### Editors

Extensions can replace Superset's default text editors with custom implementations. This enables enhanced editing experiences using alternative editor frameworks like Monaco, CodeMirror, or custom solutions. When an extension registers an editor for a language, it replaces the default Ace editor in all locations that use that language (SQL Lab, Dashboard Properties, CSS editors, etc.).

```json
"frontend": {
"contributions": {
"editors": [
{
"id": "my_extension.monaco_sql",
"name": "Monaco SQL Editor",
"languages": ["sql"],
"description": "Monaco-based SQL editor with IntelliSense"
}
]
}
}
```

See [Editors Extension Point](./extension-points/editors) for implementation details.

## Backend

Backend contribution types allow extensions to extend Superset's server-side capabilities with new API endpoints, MCP tools, and MCP prompts.
Expand All @@ -109,7 +130,7 @@ Backend contribution types allow extensions to extend Superset's server-side cap

Extensions can register custom REST API endpoints under the `/api/v1/extensions/` namespace. This dedicated namespace prevents conflicts with built-in endpoints and provides a clear separation between core and extension functionality.

``` json
```json
"backend": {
"entryPoints": ["my_extension.entrypoint"],
"files": ["backend/src/my_extension/**/*.py"]
Expand All @@ -118,7 +139,7 @@ Extensions can register custom REST API endpoints under the `/api/v1/extensions/

The entry point module registers the API with Superset:

``` python
```python
from superset_core.api.rest_api import add_extension_api
from .api import MyExtensionAPI

Expand Down
245 changes: 245 additions & 0 deletions docs/developer_portal/extensions/extension-points/editors.md
Original file line number Diff line number Diff line change
@@ -0,0 +1,245 @@
---
title: Editors
sidebar_position: 2
---

<!--
Licensed to the Apache Software Foundation (ASF) under one
or more contributor license agreements. See the NOTICE file
distributed with this work for additional information
regarding copyright ownership. The ASF licenses this file
to you under the Apache License, Version 2.0 (the
"License"); you may not use this file except in compliance
with the License. You may obtain a copy of the License at

http://www.apache.org/licenses/LICENSE-2.0

Unless required by applicable law or agreed to in writing,
software distributed under the License is distributed on an
"AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
KIND, either express or implied. See the License for the
specific language governing permissions and limitations
under the License.
-->

# Editor Contributions

Extensions can replace Superset's default text editors with custom implementations. This allows you to provide enhanced editing experiences using alternative editor frameworks like Monaco, CodeMirror, or custom solutions.

## Overview

Superset uses text editors in various places throughout the application:

| Language | Locations |
|----------|-----------|
| `sql` | SQL Lab, Metric/Filter Popovers |
| `json` | Dashboard Properties, Annotation Modal, Theme Modal |
| `css` | Dashboard Properties, CSS Template Modal |
| `markdown` | Dashboard Markdown component |
| `yaml` | Template Params Editor |

By registering an editor provider for a language, your extension replaces the default Ace editor in **all** locations that use that language.

## Manifest Configuration

Declare editor contributions in your `extension.json` manifest:

```json
{
"name": "monaco-editor",
"version": "1.0.0",
"frontend": {
"contributions": {
"editors": [
{
"id": "monaco-editor.sql",
"name": "Monaco SQL Editor",
"languages": ["sql"],
"description": "Monaco-based SQL editor with IntelliSense"
}
]
}
}
}
```

## Implementing an Editor

Your editor component must implement the `EditorProps` interface and expose an `EditorHandle` via `forwardRef`. For the complete interface definitions, see `@apache-superset/core/api/editors.ts`.

### Key EditorProps

```typescript
interface EditorProps {
/** Controlled value */
value: string;
/** Content change handler */
onChange: (value: string) => void;
/** Language mode for syntax highlighting */
language: EditorLanguage;
/** Keyboard shortcuts to register */
hotkeys?: EditorHotkey[];
/** Callback when editor is ready with imperative handle */
onReady?: (handle: EditorHandle) => void;
/** Host-specific context (e.g., database info from SQL Lab) */
metadata?: Record<string, unknown>;
// ... additional props for styling, annotations, etc.
}
```

### Key EditorHandle Methods

```typescript
interface EditorHandle {
/** Focus the editor */
focus(): void;
/** Get the current editor content */
getValue(): string;
/** Get the current cursor position */
getCursorPosition(): Position;
/** Move the cursor to a specific position */
moveCursorToPosition(position: Position): void;
/** Set the selection range */
setSelection(selection: Range): void;
/** Scroll to a specific line */
scrollToLine(line: number): void;
// ... additional methods for text manipulation, annotations, etc.
}
```

## Example Implementation

Here's an example of a Monaco-based SQL editor implementing the key interfaces shown above:

### MonacoSQLEditor.tsx

```typescript
import { forwardRef, useRef, useImperativeHandle, useEffect } from 'react';
import * as monaco from 'monaco-editor';
import type { editors } from '@apache-superset/core';

const MonacoSQLEditor = forwardRef<editors.EditorHandle, editors.EditorProps>(
(props, ref) => {
const { value, onChange, hotkeys, onReady } = props;
const containerRef = useRef<HTMLDivElement>(null);
const editorRef = useRef<monaco.editor.IStandaloneCodeEditor | null>(null);

// Implement EditorHandle interface
const handle: editors.EditorHandle = {
focus: () => editorRef.current?.focus(),
getValue: () => editorRef.current?.getValue() ?? '',
getCursorPosition: () => {
const pos = editorRef.current?.getPosition();
return { line: (pos?.lineNumber ?? 1) - 1, column: (pos?.column ?? 1) - 1 };
},
// ... implement remaining methods
};

useImperativeHandle(ref, () => handle, []);

useEffect(() => {
if (!containerRef.current) return;

const editor = monaco.editor.create(containerRef.current, { value, language: 'sql' });
editorRef.current = editor;

editor.onDidChangeModelContent(() => onChange(editor.getValue()));

// Register hotkeys
hotkeys?.forEach(hotkey => {
editor.addAction({
id: hotkey.name,
label: hotkey.name,
run: () => hotkey.exec(handle),
});
});

onReady?.(handle);
return () => editor.dispose();
}, []);

return <div ref={containerRef} style={{ height: '100%', width: '100%' }} />;
},
);

export default MonacoSQLEditor;
```

### activate.ts

```typescript
import { editors } from '@apache-superset/core';
import MonacoSQLEditor from './MonacoSQLEditor';

export function activate(context) {
// Register the Monaco editor for SQL
const disposable = editors.registerEditorProvider(
{
id: 'monaco-sql-editor.sql',
name: 'Monaco SQL Editor',
languages: ['sql'],
},
MonacoSQLEditor,
);

context.subscriptions.push(disposable);
}
```

## Handling Hotkeys

Superset passes keyboard shortcuts via the `hotkeys` prop. Each hotkey includes an `exec` function that receives the `EditorHandle`:

```typescript
interface EditorHotkey {
name: string;
key: string; // e.g., "Ctrl-Enter", "Alt-Shift-F"
description?: string;
exec: (handle: EditorHandle) => void;
}
```

Your editor must register these hotkeys with your editor framework and call `exec(handle)` when triggered.

## Keywords

Superset passes static autocomplete suggestions via the `keywords` prop. These include table names, column names, and SQL functions based on the current database context:

```typescript
interface EditorKeyword {
name: string;
value?: string; // Text to insert (defaults to name)
meta?: string; // Category like "table", "column", "function"
score?: number; // Sorting priority
}
```

Your editor should convert these to your framework's completion format and register them for autocomplete.

## Completion Providers

For dynamic autocomplete (e.g., fetching suggestions as the user types), implement and register a `CompletionProvider` via the `EditorHandle`:

```typescript
const provider: CompletionProvider = {
id: 'my-sql-completions',
triggerCharacters: ['.', ' '],
provideCompletions: async (content, position, context) => {
// Use context.metadata for database info
// Return array of CompletionItem
return [
{ label: 'SELECT', insertText: 'SELECT', kind: 'keyword' },
// ...
];
},
};

// Register during editor initialization
const disposable = handle.registerCompletionProvider(provider);
```

## Next Steps

- **[SQL Lab Extension Points](./sqllab)** - Learn about other SQL Lab customizations
- **[Contribution Types](../contribution-types)** - Explore other contribution types
- **[Development](../development)** - Set up your development environment
1 change: 1 addition & 0 deletions docs/developer_portal/extensions/registry.md
Original file line number Diff line number Diff line change
Expand Up @@ -38,6 +38,7 @@ This page serves as a registry of community-created Superset extensions. These e
| [SQL Lab Result Stats](https://github.com/michael-s-molina/superset-extensions/tree/main/result_stats) | A SQL Lab extension that automatically computes statistics for query results, providing type-aware analysis including numeric metrics (min, max, mean, median, std dev), string analysis (length, empty counts), and date range information. | Michael S. Molina | <a href="/img/extensions/result-stats.png" target="_blank"><img src="/img/extensions/result-stats.png" alt="Result Stats" width="120" /></a> |
| [SQL Snippets](https://github.com/michael-s-molina/superset-extensions/tree/main/sql_snippets) | A SQL Lab extension that provides reusable SQL code snippets, enabling quick insertion of commonly used code blocks such as license headers, author information, and frequently used SQL patterns. | Michael S. Molina | <a href="/img/extensions/sql-snippets.png" target="_blank"><img src="/img/extensions/sql-snippets.png" alt="SQL Snippets" width="120" /></a> |
| [SQL Lab Query Estimator](https://github.com/michael-s-molina/superset-extensions/tree/main/query_estimator) | A SQL Lab panel that analyzes query execution plans to estimate resource impact, detect performance issues like Cartesian products and high-cost operations, and visualize the query plan tree. | Michael S. Molina | <a href="/img/extensions/query-estimator.png" target="_blank"><img src="/img/extensions/query-estimator.png" alt="Query Estimator" width="120" /></a> |
| [Editors Bundle](https://github.com/michael-s-molina/superset-extensions/tree/main/editors_bundle) | A Superset extension that demonstrates how to provide custom code editors for different languages. This extension showcases the editor contribution system by registering alternative editors that can replace Superset's default Ace editor. | Michael S. Molina | <a href="/img/extensions/editors-bundle.png" target="_blank"><img src="/img/extensions/editors-bundle.png" alt="Editors Bundle" width="120" /></a> |

## How to Add Your Extension

Expand Down
Binary file added docs/static/img/extensions/editors-bundle.png
Loading
Sorry, something went wrong. Reload?
Sorry, we cannot display this file.
Sorry, this file is invalid so it cannot be displayed.
5 changes: 5 additions & 0 deletions scripts/check-type.js
Original file line number Diff line number Diff line change
Expand Up @@ -27,6 +27,11 @@ const { existsSync } = require("node:fs");
const { chdir, cwd } = require("node:process");
const { createRequire } = require("node:module");

// Increase memory limit for TypeScript compiler
if (!process.env.NODE_OPTIONS?.includes("--max-old-space-size")) {
process.env.NODE_OPTIONS = `${process.env.NODE_OPTIONS || ""} --max-old-space-size=8192`.trim();
}

const SUPERSET_ROOT = dirname(__dirname);
const PACKAGE_ARG_REGEX = /^package=/;
const EXCLUDE_DECLARATION_DIR_REGEX = /^excludeDeclarationDir=/;
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -61,8 +61,8 @@ describe('SqlLab query tabs', () => {
});

it('opens a new tab by a button and a shortcut', () => {
const editorContent = '#ace-editor .ace_content';
const editorInput = '#ace-editor textarea';
const editorContent = '.ace_editor .ace_content';
const editorInput = '.ace_editor textarea';
const queryLimitSelector = '#js-sql-toolbar .limitDropdown';
cy.get(tabSelector).then(tabs => {
const initialTabCount = tabs.length;
Expand Down
2 changes: 1 addition & 1 deletion superset-frontend/package.json
Original file line number Diff line number Diff line change
Expand Up @@ -84,7 +84,7 @@
"tdd": "cross-env NODE_ENV=test NODE_OPTIONS=\"--max-old-space-size=8192\" jest --watch",
"test": "cross-env NODE_ENV=test NODE_OPTIONS=\"--max-old-space-size=8192\" jest --max-workers=80% --silent",
"test-loud": "cross-env NODE_ENV=test NODE_OPTIONS=\"--max-old-space-size=8192\" jest --max-workers=80%",
"type": "tsc --noEmit",
"type": "cross-env NODE_OPTIONS=\"--max-old-space-size=8192\" tsc --noEmit",
"update-maps": "cd plugins/legacy-plugin-chart-country-map/scripts && jupyter nbconvert --to notebook --execute --inplace --allow-errors --ExecutePreprocessor.timeout=1200 'Country Map GeoJSON Generator.ipynb'",
"validate-release": "../RELEASING/validate_this_release.sh"
},
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -74,7 +74,27 @@ export interface ViewContribution {
}

/**
* Aggregates all contributions (commands, menus, and views) provided by an extension or module.
* Describes an editor that can be contributed to the application.
* Extensions declare editor contributions in their extension.json manifest.
*/
export interface EditorContribution {
/** Unique identifier for the editor (e.g., "acme.monaco-sql") */
id: string;
/** Display name of the editor */
name: string;
/** Languages this editor supports */
languages: EditorLanguage[];
/** Optional description of the editor */
description?: string;
}

/**
* Supported editor languages.
*/
export type EditorLanguage = 'sql' | 'json' | 'yaml' | 'markdown' | 'css';

/**
* Aggregates all contributions (commands, menus, views, and editors) provided by an extension or module.
*/
export interface Contributions {
/** List of command contributions. */
Expand All @@ -87,4 +107,6 @@ export interface Contributions {
views: {
[key: string]: ViewContribution[];
};
/** List of editor contributions. */
editors?: EditorContribution[];
}
Loading
Loading