Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Viewer pane pops out to editor tab #4808

Merged
merged 17 commits into from
Nov 21, 2024
Merged
Show file tree
Hide file tree
Changes from 5 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
4 changes: 4 additions & 0 deletions build/lib/i18n.resources.json
Original file line number Diff line number Diff line change
Expand Up @@ -206,6 +206,10 @@
"name": "vs/workbench/contrib/positronPreview",
"project": "vscode-workbench"
},
{
"name": "vs/workbench/contrib/positronPreviewEditor",
"project": "vscode-workbench"
},
{
"name": "vs/workbench/contrib/positronPlots",
"project": "vscode-workbench"
Expand Down
1 change: 1 addition & 0 deletions src/vs/base/common/network.ts
Original file line number Diff line number Diff line change
Expand Up @@ -128,6 +128,7 @@ export namespace Schemas {
export const positronDataExplorer = 'positron-data-explorer';
export const positronNotebook = 'positron-notebook';
export const positronPlotsEditor = 'positron-plots-editor';
export const positronPreviewEditor = 'positron-preview-editor'

// --- End Positron ---

Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -19,6 +19,7 @@ import { DisposableStore } from 'vs/base/common/lifecycle';
const reload = localize('positron.preview.html.reload', "Reload the content");
const clear = localize('positron.preview.html.clear', "Clear the content");
const openInBrowser = localize('positron.preview.html.openInBrowser', "Open the content in the default browser");
const openInEditor = localize('positron.preview.html.openInEditor', "Open the content in an editor tab.");

/**
* HtmlActionBarsProps interface.
Expand Down Expand Up @@ -52,6 +53,11 @@ export const HtmlActionBars = (props: PropsWithChildren<HtmlActionBarsProps>) =>
{ openExternal: true, fromUserGesture: true });
};

// Handler for open in editor button
const openInEditorHandler = () => {
props.positronPreviewService.openEditor();
};

// Main use effect.
useEffect(() => {
// Create the disposable store for cleanup.
Expand Down Expand Up @@ -89,6 +95,13 @@ export const HtmlActionBars = (props: PropsWithChildren<HtmlActionBarsProps>) =>
ariaLabel={openInBrowser}
onPressed={openInBrowserHandler} />
<ActionBarSeparator />
<ActionBarButton
iconId='go-to-file'
align='right'
tooltip={openInEditor}
ariaLabel={openInEditor}
onPressed={openInEditorHandler} />
<ActionBarSeparator />
<ActionBarButton
iconId='clear-all'
align='right'
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -9,6 +9,7 @@ import { useEffect } from 'react'; // eslint-disable-line no-duplicate-imports
import { PreviewWebview } from 'vs/workbench/contrib/positronPreview/browser/previewWebview';
import * as DOM from 'vs/base/browser/dom';


/**
* PreviewContainerProps interface.
*/
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -36,6 +36,8 @@ const reload = localize('positron.preview.reload', "Reload the current URL");
const clear = localize('positron.preview.clear', "Clear the current URL");
const openInBrowser = localize('positron.preview.openInBrowser', "Open the current URL in the default browser");
const currentUrl = localize('positron.preview.currentUrl', "The current URL");
// TODO: do these just get copied between html/url action bars?
const openInEditor = localize('positron.preview.html.openInEditor', "Open the content in an editor tab.");

/**
* UrlActionBars component.
Expand Down Expand Up @@ -72,6 +74,10 @@ export const UrlActionBars = (props: PropsWithChildren<UrlActionBarsProps>) => {
});
};

const openInEditorHandler = () => {
props.positronPreviewService.openEditor();
};

// Handler for the clear button.
const clearHandler = () => {
props.positronPreviewService.clearAllPreviews();
Expand Down Expand Up @@ -192,6 +198,13 @@ export const UrlActionBars = (props: PropsWithChildren<UrlActionBarsProps>) => {
ariaLabel={openInBrowser}
onPressed={openInBrowserHandler} />
<ActionBarSeparator />
<ActionBarButton
iconId='go-to-file'
align='right'
tooltip={openInEditor}
ariaLabel={openInEditor}
onPressed={openInEditorHandler} />
<ActionBarSeparator />
<ActionBarButton
iconId='clear-all'
align='right'
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -18,7 +18,7 @@ import { IPositronPreviewService, POSITRON_PREVIEW_VIEW_ID } from 'vs/workbench/
import { IWorkbenchContributionsRegistry, Extensions as WorkbenchExtensions, IWorkbenchContribution } from 'vs/workbench/common/contributions';
import { ViewContainer, IViewContainersRegistry, ViewContainerLocation, Extensions as ViewContainerExtensions, IViewsRegistry } from 'vs/workbench/common/views';
import { registerAction2 } from 'vs/platform/actions/common/actions';
import { PositronOpenUrlInViewerAction } from 'vs/workbench/contrib/positronPreview/browser/positronPreviewActions';
import { PositronOpenUrlInViewerAction, PreviewEditorAction } from 'vs/workbench/contrib/positronPreview/browser/positronPreviewActions';
import { IConfigurationRegistry, Extensions as ConfigurationExtensions, ConfigurationScope, } from 'vs/platform/configuration/common/configurationRegistry';

// The Positron preview view icon.
Expand Down Expand Up @@ -71,6 +71,7 @@ class PositronPreviewContribution extends Disposable implements IWorkbenchContri

private registerActions(): void {
registerAction2(PositronOpenUrlInViewerAction);
registerAction2(PreviewEditorAction);
}
}

Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -73,3 +73,30 @@ export class PositronOpenUrlInViewerAction extends Action2 {
previewService.openUri(previewId, undefined, uri);
}
}

export class PreviewEditorAction extends Action2 {
static ID = 'workbench.action.positronPreview.openEditor';

constructor() {
super({
id: PreviewEditorAction.ID,
title: nls.localize2('positronPreview.openEditor', 'Open Preview in Editor Tab'),
category,
f1: true,
});
}
/**
* Runs the action and opens the selected plot in the editor.
*
* @param accessor The service accessor.
*/
async run(accessor: ServicesAccessor) {
const previewService = accessor.get(IPositronPreviewService);
if (previewService.activePreviewWebviewId) {
previewService.openEditor();
} else {
console.log('no preview service');
}
}

}
Original file line number Diff line number Diff line change
Expand Up @@ -26,6 +26,8 @@ import { PreviewHtml } from 'vs/workbench/contrib/positronPreview/browser/previe
import { ICommandService } from 'vs/platform/commands/common/commands';
import { basename } from 'vs/base/common/path';
import { IExtensionService } from 'vs/workbench/services/extensions/common/extensions';
import { IEditorService } from 'vs/workbench/services/editor/common/editorService';
import { Schemas } from 'vs/base/common/network';

/**
* Positron preview service; keeps track of the set of active previews and
Expand All @@ -35,7 +37,7 @@ export class PositronPreviewService extends Disposable implements IPositronPrevi

declare readonly _serviceBrand: undefined;

private _items: Map<string, PreviewWebview> = new Map();
private _items: Map<string, { preview: PreviewWebview; isEditor: boolean }> = new Map();

private static _previewIdCounter = 0;

Expand All @@ -45,6 +47,8 @@ export class PositronPreviewService extends Disposable implements IPositronPrevi

private _onDidChangeActivePreviewWebview = new Emitter<string>;

//private _editorPreview = new Map<string, IPositronPreviewService>();

constructor(
@ICommandService private readonly _commandService: ICommandService,
@IWebviewService private readonly _webviewService: IWebviewService,
Expand All @@ -53,7 +57,8 @@ export class PositronPreviewService extends Disposable implements IPositronPrevi
@ILogService private readonly _logService: ILogService,
@IOpenerService private readonly _openerService: IOpenerService,
@IPositronNotebookOutputWebviewService private readonly _notebookOutputWebviewService: IPositronNotebookOutputWebviewService,
@IExtensionService private readonly _extensionService: IExtensionService
@IExtensionService private readonly _extensionService: IExtensionService,
@IEditorService private readonly _editorService: IEditorService
) {
super();
this.onDidCreatePreviewWebview = this._onDidCreatePreviewWebviewEmitter.event;
Expand Down Expand Up @@ -95,9 +100,9 @@ export class PositronPreviewService extends Disposable implements IPositronPrevi
// use HTML proxies, since these proxies live in the extension host.
this._register(this._extensionService.onWillStop((e) => {
for (const preview of this._items.values()) {
if (preview instanceof PreviewHtml) {
preview.webview.dispose();
this._items.delete(preview.previewId);
if (preview.preview instanceof PreviewHtml) {
preview.preview.webview.dispose();
this._items.delete(preview.preview.previewId);
}
}
}));
Expand All @@ -111,7 +116,7 @@ export class PositronPreviewService extends Disposable implements IPositronPrevi
}

get previewWebviews(): PreviewWebview[] {
return Array.from(this._items.values());
return Array.from(this._items.values()).map(item => item.preview);
}

get activePreviewWebviewId(): string {
Expand All @@ -122,7 +127,7 @@ export class PositronPreviewService extends Disposable implements IPositronPrevi
if (!this._selectedItemId) {
return undefined;
}
return this._items.get(this._selectedItemId);
return this._items.get(this._selectedItemId)?.preview;
}

clearAllPreviews(): void {
Expand All @@ -132,7 +137,7 @@ export class PositronPreviewService extends Disposable implements IPositronPrevi

// Dispose all active webviews
for (const item of this._items.values()) {
item.webview.dispose();
item.preview.webview.dispose();
}

// Clear the map
Expand All @@ -158,7 +163,7 @@ export class PositronPreviewService extends Disposable implements IPositronPrevi

// Notify previous preview that it is no longer active
if (this._items.has(this._selectedItemId)) {
this._items.get(this._selectedItemId)!.active = false;
this._items.get(this._selectedItemId)!.preview.active = false;
}

// Swap to new preview
Expand All @@ -167,7 +172,7 @@ export class PositronPreviewService extends Disposable implements IPositronPrevi

// Notify new preview that it is active
if (id) {
this._items.get(id)!.active = true;
this._items.get(id)!.preview.active = true;
}
}

Expand All @@ -184,7 +189,7 @@ export class PositronPreviewService extends Disposable implements IPositronPrevi
const webview = this._webviewService.createWebviewOverlay(webviewInitInfo);
const overlay = this.createOverlayWebview(webview);
const preview = new PreviewWebview(viewType, previewId, title, overlay);
this._items.set(previewId, preview);
this._items.set(previewId, { preview, isEditor: false });

this.openPreviewWebview(preview, preserveFocus);

Expand Down Expand Up @@ -271,11 +276,14 @@ export class PositronPreviewService extends Disposable implements IPositronPrevi
private makeActivePreview(preview: PreviewWebview) {
// Remove any other previews from the item list; they can be expensive
// to keep around.
this._items.forEach((value) => {
value.dispose();
this._items.forEach((value, key) => {
if (!value.isEditor) {
value.preview.dispose();
this._items.delete(key);
}
});
this._items.clear();
this._items.set(preview.previewId, preview);

this._items.set(preview.previewId, { preview, isEditor: false });

// Open the preview
this.openPreviewWebview(preview);
Expand Down Expand Up @@ -349,7 +357,7 @@ export class PositronPreviewService extends Disposable implements IPositronPrevi

openPreviewWebview(
preview: PreviewWebview,
preserveFocus?: boolean | undefined
preserveFocus?: boolean | undefined,
) {
this._onDidCreatePreviewWebviewEmitter.fire(preview);
this.activePreviewWebviewId = preview.previewId;
Expand All @@ -359,14 +367,18 @@ export class PositronPreviewService extends Disposable implements IPositronPrevi

const wasActive = this.activePreviewWebviewId === preview.previewId;

this._items.delete(preview.previewId);
if (!this._items.get(preview.previewId)?.isEditor) {

this._items.delete(preview.previewId);

}

// Select a new preview webview if the closed one was active
if (wasActive) {
const items = this._items.values().next();
if (items.value) {
// If we have other items to show, select one
this.activePreviewWebviewId = items.value.previewId;
this.activePreviewWebviewId = items.value.preview.previewId;
} else {
// Nothing else to show; set the the active preview to undefined
this.activePreviewWebviewId = '';
Expand Down Expand Up @@ -402,7 +414,7 @@ export class PositronPreviewService extends Disposable implements IPositronPrevi
'notebookRenderer',
e.id, session.metadata.sessionName,
overlay);
this._items.set(e.id, preview);
this._items.set(e.id, { preview, isEditor: false });
this.openPreviewWebview(preview, false);
}
}
Expand Down Expand Up @@ -496,4 +508,50 @@ export class PositronPreviewService extends Disposable implements IPositronPrevi
// It's a localhost http or https URL; we can handle it in the viewer.
return true;
}

public async openEditor(): Promise<void> {
// Create a unique ID for this preview.
const previewId = `editorPreview.${PositronPreviewService._previewIdCounter++}`;
const oldPreviewId = this.activePreviewWebviewId;

if (!this.activePreviewWebview) {
return;
}

// if (this.activePreviewWebview.viewType === 'positron.previewHtml') {
// preview = this.createPreviewHtml('sessionId', previewId, extension, 'uri', );
// }

const oldPreview = this._items.get(oldPreviewId);
if (!oldPreview) {
return;
}

const overlay = this.createOverlayWebview(this.activePreviewWebview.webview.webview);
const preview = new PreviewWebview(this.activePreviewWebview.viewType, previewId, '', overlay);

this._items.set(previewId, { preview: preview, isEditor: true });

// this._onDidCreatePreviewWebviewEmitter.fire(preview);
this.activePreviewWebviewId = previewId;

const editorPane = await this._editorService.openEditor({
resource: URI.from({
scheme: Schemas.positronPreviewEditor,
path: previewId
})
});

if (!editorPane) {
throw new Error('Failed to open editor');
}

}

public disposePreview(previewId: string): void {
this._items.get(previewId)?.preview.webview.dispose();
// Remove the preview
this._items.delete(previewId);
}

}
Original file line number Diff line number Diff line change
Expand Up @@ -116,4 +116,8 @@ export interface IPositronPreviewService {
get activePreviewWebviewId(): string;

set activePreviewWebviewId(id: string);

openEditor(): Promise<void>;

disposePreview(previewId: string): void;
}
Loading