VS Code Extension API: Embedding Native Editor with WebView in One Tab or Adding Clickable Gutter Icons
Question 1: Native Editor + WebView in the Same Tab
I’m developing a VS Code extension and want to create a custom view that combines:
- A code editor (for .ts/.js files) with full workspace support, including editing, syntax highlighting, IntelliSense, go-to definition, and diagnostics
- A WebView UI panel for visualization and controls
- Both appearing together as a single tab/editor view, not as separate editors or split panes
Does the VS Code extension API allow embedding the native editor view inside a tab together with a WebView while preserving full language support? If this isn’t possible, is there any official documentation or issue indicating this limitation?
Question 2: Clickable Gutter/Margin Icons as Fallback
If embedding isn’t feasible, I need an alternative approach:
- Display small icons/buttons in the editor’s left margin/gutter beside line numbers (e.g., “Run test”)
- Allow users to click these icons to trigger extension commands
Specific questions:
- Does the VS Code extension API support clickable gutter/margin icons?
- If yes, which API (decorations, margin, commands, etc.) is used, and are there restrictions (e.g., icon display without clickability)?
- If not supported, what are common workarounds (e.g., listening to editor mouse events, computing click regions, using CodeLens, overlay UI, etc.)?
I appreciate any pointers, API references, or example implementations.
VS Code Extension API: Embedding Native Editor with WebView in One Tab or Adding Clickable Gutter Icons
Brief Answer
The VS Code Extension API doesn’t currently support embedding a native editor view with a WebView in the same tab while preserving full language support. Official documentation indicates this is a limitation, with no direct API method available for this specific use case. However, you can add clickable icons to the editor’s gutter margin using the TextEditorDecorationType API combined with command callbacks, though with some limitations on icon placement and interaction.
Contents
- Can You Embed a Native Editor with WebView in the Same Tab?
- Adding Clickable Gutter Icons in VS Code Extensions
- Code Examples and Practical Implementation
- Conclusion and Best Practices
Can You Embed a Native Editor with WebView in the Same Tab?
Understanding the Limitations of VS Code Editor API
The VS Code extension API doesn’t currently provide a direct way to embed a native editor view (with full IntelliSense, syntax highlighting, etc.) together with a WebView in the same tab. The editor and WebView APIs are designed to operate separately:
- Editor API: Provides access to the built-in text editor but doesn’t allow embedding it within other UI elements
- WebView API: Creates a separate panel or view that can display HTML content but doesn’t include native editor capabilities
The fundamental limitation is that VS Code’s text editor is a complex, native component that handles rendering, language services, and user interactions in a self-contained manner. The extension API exposes ways to interact with existing editors or create new ones, but not to embed them within custom UI elements.
Existing Workarounds and Their Limitations
While there’s no direct solution, developers have explored several workarounds:
-
Split View Approach: Using the
vscode.window.createTextEditor
API to open the editor in one pane and WebView in another, then programmatically managing their layout. However, this creates separate tabs rather than a unified experience. -
Custom Editor Provider: Implementing a custom editor using
vscode.window.registerCustomEditorProvider
. This allows creating a completely custom editor view, but it requires implementing all editor features (syntax highlighting, IntelliSense, etc.) from scratch, which is complex and doesn’t leverage the native editor’s capabilities. -
WebView with Monaco Editor: Embedding Monaco Editor (VS Code’s underlying editor) within the WebView. While this provides editor functionality, it doesn’t integrate with VS Code’s language services, workspace features, or existing file system in the same way as the native editor.
Official Documentation and Community Solutions
The VS Code extension documentation doesn’t explicitly address this limitation, but related discussions in the GitHub repository indicate this is a known constraint:
- Issue #85721 on VS Code GitHub discusses embedding WebView in editor, with no official solution provided
- Issue #90463 mentions limitations of editor embedding
- Community solutions generally involve one of the workarounds mentioned above
For official documentation, the relevant sections are:
Adding Clickable Gutter Icons in VS Code Extensions
TextEditorDecorationType API for Gutter Icons
VS Code Extension API provides a way to add decorations to the editor, including icons in the gutter margin. The TextEditorDecorationType
API allows you to:
- Define decoration options that include gutter icons
- Apply these decorations to specific lines or ranges in the editor
- Attach commands to decorations for interactivity
Key components involved:
vscode.window.createTextEditorDecorationType()
: Creates a decoration type with specific stylingTextEditorDecorationOptions
: Configuration for decorations, including gutter icon pathsonDidClickDecoration
event: Handles clicks on decorations
Implementation Steps for Clickable Gutter Icons
Here’s how to implement clickable gutter icons:
- Create a Decoration Type:
const decorationType = vscode.window.createTextEditorDecorationType({
gutterIconPath: vscode.Uri.file(path.join(context.extensionPath, 'images', 'gutter-icon.png')),
gutterIconSize: 'contain'
});
- Apply Decorations to Editor:
const decorationOptions: vscode.DecorationOptions[] = [];
decorationOptions.push({
range: new vscode.Range(lineNumber, 0, lineNumber, 0),
hoverMessage: 'Click to run test'
});
editor.setDecorations(decorationType, decorationOptions);
- Handle Decoration Clicks:
vscode.window.onDidClickDecoration(e => {
if (e.decorationOptions?.hoverMessage === 'Click to run test') {
vscode.commands.executeCommand('your.command', e.uri);
}
});
Limitations and Considerations
While the decoration API provides gutter icons with click handling, there are several limitations:
-
Limited Position Control: Icons are aligned to the left side of the gutter and can’t be precisely positioned between line numbers.
-
No Native Context Menu: Clicking on gutter icons doesn’t trigger the editor’s context menu.
-
Performance: Too many decorations can impact editor performance, especially in large files.
-
Dynamic Updates: Updating decorations frequently can cause flickering or performance issues.
-
Icon Size Constraints: Gutter icons are limited to small sizes (typically 16x16 or 32x32 pixels).
Alternative Approaches
If gutter decorations don’t meet your needs, consider these alternatives:
- CodeLens:
vscode.languages.registerCodeLensProvider(documentSelector, {
provideCodeLenses(document) {
return [new vscode.CodeLens(new vscode.Range(line, 0, line, 0), {
command: 'your.command',
title: 'Run Test'
})];
}
});
-
Editor Mouse Events: Listen to editor mouse events and calculate click regions.
-
TreeView Integration: Create a custom TreeView that shows clickable items related to code lines.
-
Status Bar Items: Add clickable items to the status bar that can be context-aware.
Code Examples and Practical Implementation
Example: Adding Clickable Gutter Icons
Here’s a complete example of an extension that adds clickable gutter icons:
import * as vscode from 'vscode';
export function activate(context: vscode.ExtensionContext) {
// Create decoration type for gutter icon
const decorationType = vscode.window.createTextEditorDecorationType({
gutterIconPath: context.asUri('images/gutter-icon.png'),
gutterIconSize: 'contain'
});
// Store decoration options
let decorationOptions: vscode.DecorationOptions[] = [];
// Update decorations when active editor changes
function updateDecorations() {
const editor = vscode.window.activeTextEditor;
if (!editor) return;
// Clear existing decorations
decorationOptions = [];
// Add decoration to line 1 (for example)
decorationOptions.push({
range: new vscode.Range(0, 0, 0, 0),
hoverMessage: 'Click to run test'
});
editor.setDecorations(decorationType, decorationOptions);
}
// Initial update
updateDecorations();
// Register command to run when gutter icon is clicked
const clickDecorationHandler = vscode.window.onDidClickDecoration(e => {
if (e.decorationOptions?.hoverMessage === 'Click to run test') {
vscode.commands.executeCommand('your.extension.runTest', e.uri);
}
});
// Update decorations when editor changes
const editorChangeHandler = vscode.window.onDidChangeActiveTextEditor(updateDecorations);
// Register command
const runTestCommand = vscode.commands.registerCommand('your.extension.runTest', (uri: vscode.Uri) => {
vscode.window.showInformationMessage('Test executed for ' + uri.fsPath);
});
// Add disposables
context.subscriptions.push(decorationType, clickDecorationHandler, editorChangeHandler, runTestCommand);
}
Example: WebView Integration as Alternative
While you can’t embed a native editor with WebView in the same tab, you can create a WebView that integrates with the current editor:
import * as vscode from 'vscode';
export function activate(context: vscode.ExtensionContext) {
// Create WebView panel
const panel = vscode.window.createWebviewPanel(
'customEditorWebView',
'Custom Editor View',
vscode.ViewColumn.Beside,
{}
);
// Set WebView content (with Monaco Editor embedded)
panel.webview.html = getWebViewContent();
// Handle messages from WebView
panel.webview.onDidReceiveMessage(message => {
switch (message.command) {
case 'getEditorContent':
const editor = vscode.window.activeTextEditor;
if (editor) {
panel.webview.postMessage({
command: 'editorContent',
content: editor.document.getText()
});
}
break;
}
});
}
function getWebViewContent() {
return `
<!DOCTYPE html>
<html>
<head>
<meta charset="UTF-8">
<title>Custom Editor View</title>
<script src="https://unpkg.com/monaco-editor@0.34.1/min/vs/loader.js"></script>
<style>
body, html { height: 100%; margin: 0; padding: 0; }
#editor { width: 100%; height: 100%; }
</style>
</head>
<body>
<div id="editor" data-do-not-activate="true"></div>
<script>
require.config({ paths: { 'vs': 'https://unpkg.com/monaco-editor@0.34.1/min/vs' }});
require(['vs/editor/editor.main'], function () {
const editor = monaco.editor.create(document.getElementById('editor'), {
value: '',
language: 'javascript',
theme: 'vs-dark'
});
// Handle content from VS Code extension
window.addEventListener('message', event => {
if (event.data.command === 'editorContent') {
editor.setValue(event.data.content);
}
});
// Send content to VS Code extension when changed
editor.onDidChangeModelContent(() => {
vscode.postMessage({
command: 'updateEditorContent',
content: editor.getValue()
});
});
});
</script>
</body>
</html>
`;
}
Conclusion and Best Practices
Key Takeaways
-
Native Editor + WebView in Same Tab: VS Code Extension API doesn’t currently support embedding a native editor with WebView in the same tab while preserving full language support. The closest alternatives are split views or custom editors with Monaco Editor.
-
Clickable Gutter Icons: VS Code Extension API does support adding clickable icons to the editor’s gutter through the TextEditorDecorationType API, with some limitations on positioning and performance.
-
API Selection: For gutter icons, use TextEditorDecorationType with onDidClickDecoration events. For more complex UI elements, consider CodeLens or TreeView alternatives.
Practical Recommendations
-
For gutter icons:
- Use the decoration API for simple, line-specific actions
- Consider performance implications when adding many decorations
- Provide visual feedback when icons are clicked
-
For editor + WebView integration:
- Consider the split view approach if separate panes are acceptable
- Use Monaco Editor in WebView if you need embedded editor functionality
- Implement proper message passing between WebView and extension
-
When choosing between approaches:
- Evaluate the trade-offs between native features and customization
- Consider your users’ workflow and expectations
- Prototype different solutions to find the best fit for your use case
For more information, refer to the VS Code Extension API documentation, WebView guide, Custom Editor API, and Decoration API documentation available on the VS Code website.