Skip to content

Commit 2971ac5

Browse files
authored
Port dataviewer feature (#14435)
* Ensure debug adapter request/response sequence numbers tally (#14336) * Make data viewer openable from the variables window while debugging (#14406)
1 parent 7f66dc0 commit 2971ac5

27 files changed

+385
-200
lines changed

build/ci/templates/globals.yml

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -9,5 +9,5 @@ variables:
99
VSC_PYTHON_WEBVIEW_LOG_FILE: '$(Build.ArtifactStagingDirectory)/pvsc_webview.log'
1010
CI_BRANCH_NAME: ${Build.SourceBranchName}
1111
npm_config_cache: $(Pipeline.Workspace)/.npm
12-
vmImageMacOS: 'macOS-10.15'
12+
vmImageMacOS: 'macOS-latest'
1313
TS_NODE_FILES: true # Temporarily enabled to allow using types from vscode.proposed.d.ts from ts-node (for tests).

news/1 Enhancements/14406.md

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1 @@
1+
Make data viewer openable from the variables window context menu while debugging.

package.json

Lines changed: 17 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -927,6 +927,11 @@
927927
"title": "DataScience.latestExtension",
928928
"category": "Python"
929929
},
930+
{
931+
"command": "python.datascience.showDataViewer",
932+
"title": "%python.command.python.datascience.showDataViewer.title%",
933+
"category": "Python"
934+
},
930935
{
931936
"command": "python.analysis.restartLanguageServer",
932937
"title": "%python.command.python.analysis.restartLanguageServer.title%",
@@ -1550,6 +1555,11 @@
15501555
"category": "Python",
15511556
"when": "false"
15521557
},
1558+
{
1559+
"command": "python.datascience.showDataViewer",
1560+
"category": "Python",
1561+
"when": "false"
1562+
},
15531563
{
15541564
"command": "python.datascience.export",
15551565
"title": "%DataScience.notebookExportAs%",
@@ -1655,6 +1665,13 @@
16551665
"when": "view == python_tests && viewItem == suite && !busyTests",
16561666
"group": "inline@0"
16571667
}
1668+
],
1669+
"debug/variables/context": [
1670+
{
1671+
"command": "python.datascience.showDataViewer",
1672+
"group": "1_view",
1673+
"when": "debugProtocolVariableMenuContext == 'viewableInDataViewer'"
1674+
}
16581675
]
16591676
},
16601677
"breakpoints": [

package.nls.json

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -120,6 +120,7 @@
120120
"Datascience.currentlySelectedJupyterInterpreterForPlaceholder": "current: {0}",
121121
"python.command.python.analysis.clearCache.title": "Clear Module Analysis Cache",
122122
"python.command.python.analysis.restartLanguageServer.title": "Restart Language Server",
123+
"python.command.python.datascience.showDataViewer.title": "View Value in Data Viewer",
123124
"python.snippet.launch.standard.label": "Python: Current File",
124125
"python.snippet.launch.module.label": "Python: Module",
125126
"python.snippet.launch.module.default": "enter-your-module-name",

src/client/common/application/commands.ts

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -6,6 +6,7 @@
66
import { CancellationToken, Position, TextDocument, Uri } from 'vscode';
77
import { Commands as LSCommands } from '../../activation/commands';
88
import { Commands as DSCommands } from '../../datascience/constants';
9+
import { IShowDataViewerFromVariablePanel } from '../../datascience/interactive-common/interactiveWindowTypes';
910
import { KernelConnectionMetadata } from '../../datascience/jupyter/kernels/types';
1011
import { INotebookModel, ISwitchKernelOptions } from '../../datascience/types';
1112
import { PythonEnvironment } from '../../pythonEnvironments/info';
@@ -205,4 +206,5 @@ export interface ICommandNameArgumentTypeMapping extends ICommandNameWithoutArgu
205206
[DSCommands.TrustNotebook]: [undefined | never | Uri];
206207
[DSCommands.NotebookEditorExpandAllCells]: [];
207208
[DSCommands.NotebookEditorCollapseAllCells]: [];
209+
[DSCommands.ShowDataViewer]: [IShowDataViewerFromVariablePanel];
208210
}

src/client/datascience/commands/commandRegistry.ts

Lines changed: 38 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -5,21 +5,28 @@
55

66
import { inject, injectable, multiInject, named, optional } from 'inversify';
77
import { CodeLens, ConfigurationTarget, env, Range, Uri } from 'vscode';
8+
import { DebugProtocol } from 'vscode-debugprotocol';
89
import { ICommandNameArgumentTypeMapping } from '../../common/application/commands';
910
import { IApplicationShell, ICommandManager, IDebugService, IDocumentManager } from '../../common/application/types';
1011
import { Commands as coreCommands } from '../../common/constants';
12+
import { traceError } from '../../common/logger';
1113

1214
import { IStartPage } from '../../common/startPage/types';
1315
import { IConfigurationService, IDisposable, IOutputChannel } from '../../common/types';
1416
import { DataScience } from '../../common/utils/localize';
1517
import { noop } from '../../common/utils/misc';
1618
import { captureTelemetry, sendTelemetryEvent } from '../../telemetry';
1719
import { Commands, JUPYTER_OUTPUT_CHANNEL, Telemetry } from '../constants';
20+
import { IDataViewerFactory } from '../data-viewing/types';
21+
import { DataViewerChecker } from '../interactive-common/dataViewerChecker';
22+
import { IShowDataViewerFromVariablePanel } from '../interactive-common/interactiveWindowTypes';
23+
import { convertDebugProtocolVariableToIJupyterVariable } from '../jupyter/debuggerVariables';
1824
import {
1925
ICodeWatcher,
2026
IDataScienceCodeLensProvider,
2127
IDataScienceCommandListener,
2228
IDataScienceFileSystem,
29+
IJupyterVariableDataProviderFactory,
2330
INotebookEditorProvider
2431
} from '../types';
2532
import { JupyterCommandLineSelectorCommand } from './commandLineSelector';
@@ -30,6 +37,7 @@ import { JupyterServerSelectorCommand } from './serverSelector';
3037
@injectable()
3138
export class CommandRegistry implements IDisposable {
3239
private readonly disposables: IDisposable[] = [];
40+
private dataViewerChecker: DataViewerChecker;
3341
constructor(
3442
@inject(IDocumentManager) private documentManager: IDocumentManager,
3543
@inject(IDataScienceCodeLensProvider) private dataScienceCodeLensProvider: IDataScienceCodeLensProvider,
@@ -48,10 +56,14 @@ export class CommandRegistry implements IDisposable {
4856
@inject(IOutputChannel) @named(JUPYTER_OUTPUT_CHANNEL) private jupyterOutput: IOutputChannel,
4957
@inject(IStartPage) private startPage: IStartPage,
5058
@inject(ExportCommands) private readonly exportCommand: ExportCommands,
59+
@inject(IJupyterVariableDataProviderFactory)
60+
private readonly jupyterVariableDataProviderFactory: IJupyterVariableDataProviderFactory,
61+
@inject(IDataViewerFactory) private readonly dataViewerFactory: IDataViewerFactory,
5162
@inject(IDataScienceFileSystem) private readonly fs: IDataScienceFileSystem
5263
) {
5364
this.disposables.push(this.serverSelectedCommand);
5465
this.disposables.push(this.notebookCommands);
66+
this.dataViewerChecker = new DataViewerChecker(configService, appShell);
5567
}
5668
public register() {
5769
this.commandLineCommand.register();
@@ -96,6 +108,7 @@ export class CommandRegistry implements IDisposable {
96108
this.registerCommand(Commands.ViewJupyterOutput, this.viewJupyterOutput);
97109
this.registerCommand(Commands.GatherQuality, this.reportGatherQuality);
98110
this.registerCommand(Commands.LatestExtension, this.openPythonExtensionPage);
111+
this.registerCommand(Commands.ShowDataViewer, this.onVariablePanelShowDataViewerRequest);
99112
this.registerCommand(
100113
Commands.EnableLoadingWidgetsFrom3rdPartySource,
101114
this.enableLoadingWidgetScriptsFromThirdParty
@@ -466,4 +479,29 @@ export class CommandRegistry implements IDisposable {
466479
private openPythonExtensionPage() {
467480
env.openExternal(Uri.parse(`https://marketplace.visualstudio.com/items?itemName=ms-python.python`));
468481
}
482+
483+
private async onVariablePanelShowDataViewerRequest(request: IShowDataViewerFromVariablePanel) {
484+
sendTelemetryEvent(Telemetry.OpenDataViewerFromVariableWindowRequest);
485+
if (this.debugService.activeDebugSession) {
486+
const jupyterVariable = convertDebugProtocolVariableToIJupyterVariable(
487+
request.variable as DebugProtocol.Variable
488+
);
489+
try {
490+
const jupyterVariableDataProvider = await this.jupyterVariableDataProviderFactory.create(
491+
jupyterVariable
492+
);
493+
const dataFrameInfo = await jupyterVariableDataProvider.getDataFrameInfo();
494+
const columnSize = dataFrameInfo?.columns?.length;
495+
if (columnSize && (await this.dataViewerChecker.isRequestedColumnSizeAllowed(columnSize))) {
496+
const title: string = `${DataScience.dataExplorerTitle()} - ${jupyterVariable.name}`;
497+
await this.dataViewerFactory.create(jupyterVariableDataProvider, title);
498+
sendTelemetryEvent(Telemetry.OpenDataViewerFromVariableWindowSuccess);
499+
}
500+
} catch (e) {
501+
sendTelemetryEvent(Telemetry.OpenDataViewerFromVariableWindowError);
502+
traceError(e);
503+
this.appShell.showErrorMessage(e.toString());
504+
}
505+
}
506+
}
469507
}

src/client/datascience/constants.ts

Lines changed: 5 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -111,6 +111,7 @@ export namespace Commands {
111111
'python.datascience.enableLoadingWidgetScriptsFromThirdPartySource';
112112
export const NotebookEditorExpandAllCells = 'python.datascience.notebookeditor.expandallcells';
113113
export const NotebookEditorCollapseAllCells = 'python.datascience.notebookeditor.collapseallcells';
114+
export const ShowDataViewer = 'python.datascience.showDataViewer';
114115
}
115116

116117
export namespace CodeLensCommands {
@@ -409,7 +410,10 @@ export enum Telemetry {
409410
TrustAllNotebooks = 'DATASCIENCE.TRUST_ALL_NOTEBOOKS',
410411
TrustNotebook = 'DATASCIENCE.TRUST_NOTEBOOK',
411412
DoNotTrustNotebook = 'DATASCIENCE.DO_NOT_TRUST_NOTEBOOK',
412-
NotebookTrustPromptShown = 'DATASCIENCE.NOTEBOOK_TRUST_PROMPT_SHOWN'
413+
NotebookTrustPromptShown = 'DATASCIENCE.NOTEBOOK_TRUST_PROMPT_SHOWN',
414+
OpenDataViewerFromVariableWindowRequest = 'DATASCIENCE.OPEN_DATAVIEWER_FROM_VARIABLE_WINDOW_REQUEST',
415+
OpenDataViewerFromVariableWindowError = 'DATASCIENCE.OPEN_DATAVIEWER_FROM_VARIABLE_WINDOW_ERROR',
416+
OpenDataViewerFromVariableWindowSuccess = 'DATASCIENCE.OPEN_DATAVIEWER_FROM_VARIABLE_WINDOW_SUCCESS'
413417
}
414418

415419
export enum NativeKeyboardCommandTelemetry {

src/client/datascience/data-viewing/jupyterVariableDataProvider.ts

Lines changed: 9 additions & 9 deletions
Original file line numberDiff line numberDiff line change
@@ -57,15 +57,15 @@ export class JupyterVariableDataProvider implements IJupyterVariableDataProvider
5757
return;
5858
}
5959

60-
public setDependencies(variable: IJupyterVariable, notebook: INotebook): void {
60+
public setDependencies(variable: IJupyterVariable, notebook?: INotebook): void {
6161
this.notebook = notebook;
6262
this.variable = variable;
6363
}
6464

6565
public async getDataFrameInfo(): Promise<IDataFrameInfo> {
6666
let dataFrameInfo: IDataFrameInfo = {};
6767
await this.ensureInitialized();
68-
if (this.variable && this.notebook) {
68+
if (this.variable) {
6969
dataFrameInfo = {
7070
columns: this.variable.columns
7171
? JupyterVariableDataProvider.getNormalizedColumns(this.variable.columns)
@@ -80,12 +80,12 @@ export class JupyterVariableDataProvider implements IJupyterVariableDataProvider
8080
public async getAllRows() {
8181
let allRows: IRowsResponse = [];
8282
await this.ensureInitialized();
83-
if (this.variable && this.variable.rowCount && this.notebook) {
83+
if (this.variable && this.variable.rowCount) {
8484
const dataFrameRows = await this.variableManager.getDataFrameRows(
8585
this.variable,
86-
this.notebook,
8786
0,
88-
this.variable.rowCount
87+
this.variable.rowCount,
88+
this.notebook
8989
);
9090
allRows = dataFrameRows && dataFrameRows.data ? (dataFrameRows.data as IRowsResponse) : [];
9191
}
@@ -95,18 +95,18 @@ export class JupyterVariableDataProvider implements IJupyterVariableDataProvider
9595
public async getRows(start: number, end: number) {
9696
let rows: IRowsResponse = [];
9797
await this.ensureInitialized();
98-
if (this.variable && this.variable.rowCount && this.notebook) {
99-
const dataFrameRows = await this.variableManager.getDataFrameRows(this.variable, this.notebook, start, end);
98+
if (this.variable && this.variable.rowCount) {
99+
const dataFrameRows = await this.variableManager.getDataFrameRows(this.variable, start, end, this.notebook);
100100
rows = dataFrameRows && dataFrameRows.data ? (dataFrameRows.data as IRowsResponse) : [];
101101
}
102102
return rows;
103103
}
104104

105105
private async ensureInitialized(): Promise<void> {
106106
// Postpone pre-req and variable initialization until data is requested.
107-
if (!this.initialized && this.variable && this.notebook) {
107+
if (!this.initialized && this.variable) {
108108
this.initialized = true;
109-
await this.dependencyService.checkAndInstallMissingDependencies(this.notebook.getMatchingInterpreter());
109+
await this.dependencyService.checkAndInstallMissingDependencies(this.notebook?.getMatchingInterpreter());
110110
this.variable = await this.variableManager.getDataFrameInfo(this.variable, this.notebook);
111111
}
112112
}

src/client/datascience/data-viewing/jupyterVariableDataProviderFactory.ts

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -17,7 +17,7 @@ import {
1717
export class JupyterVariableDataProviderFactory implements IJupyterVariableDataProviderFactory {
1818
constructor(@inject(IServiceContainer) private serviceContainer: IServiceContainer) {}
1919

20-
public async create(variable: IJupyterVariable, notebook: INotebook): Promise<IJupyterVariableDataProvider> {
20+
public async create(variable: IJupyterVariable, notebook?: INotebook): Promise<IJupyterVariableDataProvider> {
2121
const jupyterVariableDataProvider = this.serviceContainer.get<IJupyterVariableDataProvider>(
2222
IJupyterVariableDataProvider
2323
);

0 commit comments

Comments
 (0)