Skip to content

Commit bb812f5

Browse files
authored
Save variable explorer height persistently (#12086)
* started working * redux setup done * mostly working * clean code * small refactors * communication done * finished linking * formatted * changed to local storage * some small bugs - mostly working * some small bugs * working! * clean up * cleaned up code * fixed linting bugs * fixed broken tests * removed duplicated comment * addressed comments * added sourceUri to store old filename * fixed untitled persistence save * made untitled notebooks NOT restore previous height * fixed comments * started working * redux setup done * mostly working * clean code * small refactors * communication done * finished linking * formatted * changed to local storage * some small bugs - mostly working * some small bugs * working! * clean up * cleaned up code * fixed linting bugs * fixed broken tests * removed duplicated comment * addressed comments * added sourceUri to store old filename * fixed untitled persistence save * made untitled notebooks NOT restore previous height * fixed comments * added export * fixed
1 parent 39d95ae commit bb812f5

File tree

18 files changed

+217
-32
lines changed

18 files changed

+217
-32
lines changed

src/client/datascience/interactive-common/interactiveBase.ts

Lines changed: 54 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -62,8 +62,10 @@ import {
6262
IRemoteReexecuteCode,
6363
IShowDataViewer,
6464
ISubmitNewCell,
65-
SysInfoReason
65+
SysInfoReason,
66+
VariableExplorerStateKeys
6667
} from '../interactive-common/interactiveWindowTypes';
68+
import { isUntitledFile } from '../interactive-ipynb/nativeEditorStorage';
6769
import { JupyterInvalidKernelError } from '../jupyter/jupyterInvalidKernelError';
6870
import { JupyterKernelPromiseFailedError } from '../jupyter/kernels/jupyterKernelPromiseFailedError';
6971
import { KernelSwitcher } from '../jupyter/kernels/kernelSwitcher';
@@ -144,6 +146,7 @@ export abstract class InteractiveBase extends WebViewHost<IInteractiveWindowMapp
144146
@unmanaged() protected errorHandler: IDataScienceErrorHandler,
145147
@unmanaged() protected readonly commandManager: ICommandManager,
146148
@unmanaged() protected globalStorage: Memento,
149+
@unmanaged() protected workspaceStorage: Memento,
147150
@unmanaged() rootPath: string,
148151
@unmanaged() scripts: string[],
149152
@unmanaged() title: string,
@@ -228,6 +231,14 @@ export abstract class InteractiveBase extends WebViewHost<IInteractiveWindowMapp
228231
// Send the loc strings (skip during testing as it takes up a lot of memory)
229232
const locStrings = isTestExecution() ? '{}' : localize.getCollectionJSON();
230233
this.postMessageInternal(SharedMessages.LocInit, locStrings).ignoreErrors();
234+
this.variableExplorerHeightRequest()
235+
.then((data) =>
236+
this.postMessageInternal(
237+
InteractiveWindowMessages.VariableExplorerHeightResponse,
238+
data
239+
).ignoreErrors()
240+
)
241+
.catch(); // do nothing
231242
break;
232243

233244
case InteractiveWindowMessages.GotoCodeCell:
@@ -278,6 +289,10 @@ export abstract class InteractiveBase extends WebViewHost<IInteractiveWindowMapp
278289
this.variableExplorerToggle(payload);
279290
break;
280291

292+
case InteractiveWindowMessages.SetVariableExplorerHeight:
293+
this.setVariableExplorerHeight(payload).ignoreErrors();
294+
break;
295+
281296
case InteractiveWindowMessages.AddedSysInfo:
282297
this.handleMessage(message, payload, this.onAddedSysInfo);
283298
break;
@@ -1388,6 +1403,44 @@ export abstract class InteractiveBase extends WebViewHost<IInteractiveWindowMapp
13881403
}
13891404
};
13901405

1406+
// tslint:disable-next-line: no-any
1407+
private async setVariableExplorerHeight(payload?: any) {
1408+
// Store variable explorer height based on file name in workspace storage
1409+
if (payload !== undefined) {
1410+
const updatedHeights = payload as { containerHeight: number; gridHeight: number };
1411+
const uri = await this.getOwningResource(); // Get file name
1412+
1413+
if (!uri) {
1414+
return;
1415+
}
1416+
// Storing an object that looks like
1417+
// { "fully qualified Path to 1.ipynb": 1234,
1418+
// "fully qualifieid path to 2.ipynb": 1234 }
1419+
1420+
// tslint:disable-next-line: no-any
1421+
const value = this.workspaceStorage.get(VariableExplorerStateKeys.height, {} as any);
1422+
value[uri.toString()] = updatedHeights;
1423+
this.workspaceStorage.update(VariableExplorerStateKeys.height, value);
1424+
}
1425+
}
1426+
1427+
private async variableExplorerHeightRequest(): Promise<
1428+
{ containerHeight: number; gridHeight: number } | undefined
1429+
> {
1430+
const uri = await this.getOwningResource(); // Get file name
1431+
1432+
if (!uri || isUntitledFile(uri)) {
1433+
return; // don't resotre height of untitled notebooks
1434+
}
1435+
1436+
// tslint:disable-next-line: no-any
1437+
const value = this.workspaceStorage.get(VariableExplorerStateKeys.height, {} as any);
1438+
const uriString = uri.toString();
1439+
if (uriString in value) {
1440+
return value[uriString];
1441+
}
1442+
}
1443+
13911444
private async requestTmLanguage(languageId: string) {
13921445
// Get the contents of the appropriate tmLanguage file.
13931446
traceInfo('Request for tmlanguage file.');

src/client/datascience/interactive-common/interactiveWindowTypes.ts

Lines changed: 10 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -11,6 +11,7 @@ import {
1111
CommonActionType,
1212
IAddCellAction,
1313
ILoadIPyWidgetClassFailureAction,
14+
IVariableExplorerHeight,
1415
LoadIPyWidgetClassLoadAction,
1516
NotifyIPyWidgeWidgetVersionNotSupportedAction
1617
} from '../../../datascience-ui/interactive-common/redux/reducers/types';
@@ -68,6 +69,8 @@ export enum InteractiveWindowMessages {
6869
GetVariablesRequest = 'get_variables_request',
6970
GetVariablesResponse = 'get_variables_response',
7071
VariableExplorerToggle = 'variable_explorer_toggle',
72+
SetVariableExplorerHeight = 'set_variable_explorer_height',
73+
VariableExplorerHeightResponse = 'variable_explorer_height_response',
7174
ForceVariableRefresh = 'force_variable_refresh',
7275
ProvideCompletionItemsRequest = 'provide_completion_items_request',
7376
CancelCompletionItemsRequest = 'cancel_completion_items_request',
@@ -177,6 +180,10 @@ export interface ICopyCode {
177180
source: string;
178181
}
179182

183+
export enum VariableExplorerStateKeys {
184+
height = 'NBVariableHeights'
185+
}
186+
180187
export enum SysInfoReason {
181188
Start,
182189
Restart,
@@ -351,6 +358,7 @@ export interface INotebookModelSaved extends INotebookModelChange {
351358
export interface INotebookModelSavedAs extends INotebookModelChange {
352359
kind: 'saveAs';
353360
target: Uri;
361+
sourceUri: Uri;
354362
}
355363

356364
export interface INotebookModelRemoveAllChange extends INotebookModelChange {
@@ -561,6 +569,8 @@ export class IInteractiveWindowMapping {
561569
public [InteractiveWindowMessages.GetVariablesRequest]: IJupyterVariablesRequest;
562570
public [InteractiveWindowMessages.GetVariablesResponse]: IJupyterVariablesResponse;
563571
public [InteractiveWindowMessages.VariableExplorerToggle]: boolean;
572+
public [InteractiveWindowMessages.SetVariableExplorerHeight]: IVariableExplorerHeight;
573+
public [InteractiveWindowMessages.VariableExplorerHeightResponse]: IVariableExplorerHeight;
564574
public [CssMessages.GetCssRequest]: IGetCssRequest;
565575
public [CssMessages.GetCssResponse]: IGetCssResponse;
566576
public [CssMessages.GetMonacoThemeRequest]: IGetMonacoThemeRequest;

src/client/datascience/interactive-common/synchronization.ts

Lines changed: 3 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -84,6 +84,7 @@ const messageWithMessageTypes: MessageMapping<IInteractiveWindowMapping> & Messa
8484
[CommonActionType.TOGGLE_LINE_NUMBERS]: MessageType.syncWithLiveShare,
8585
[CommonActionType.TOGGLE_OUTPUT]: MessageType.syncWithLiveShare,
8686
[CommonActionType.TOGGLE_VARIABLE_EXPLORER]: MessageType.syncWithLiveShare,
87+
[CommonActionType.SET_VARIABLE_EXPLORER_HEIGHT]: MessageType.other,
8788
[CommonActionType.UNFOCUS_CELL]: MessageType.syncWithLiveShare,
8889
[CommonActionType.UNMOUNT]: MessageType.other,
8990
[CommonActionType.PostOutgoingMessage]: MessageType.other,
@@ -193,6 +194,8 @@ const messageWithMessageTypes: MessageMapping<IInteractiveWindowMapping> & Messa
193194
[InteractiveWindowMessages.UpdateKernel]: MessageType.syncAcrossSameNotebooks | MessageType.syncWithLiveShare,
194195
[InteractiveWindowMessages.UpdateDisplayData]: MessageType.syncWithLiveShare,
195196
[InteractiveWindowMessages.VariableExplorerToggle]: MessageType.other,
197+
[InteractiveWindowMessages.SetVariableExplorerHeight]: MessageType.other,
198+
[InteractiveWindowMessages.VariableExplorerHeightResponse]: MessageType.other,
196199
[InteractiveWindowMessages.VariablesComplete]: MessageType.other,
197200
[InteractiveWindowMessages.ConvertUriForUseInWebViewRequest]: MessageType.other,
198201
[InteractiveWindowMessages.ConvertUriForUseInWebViewResponse]: MessageType.other,

src/client/datascience/interactive-ipynb/nativeEditor.ts

Lines changed: 30 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -36,7 +36,8 @@ import {
3636
IDisposableRegistry,
3737
IExperimentsManager,
3838
IMemento,
39-
Resource
39+
Resource,
40+
WORKSPACE_MEMENTO
4041
} from '../../common/types';
4142
import { createDeferred, Deferred } from '../../common/utils/async';
4243
import * as localize from '../../common/utils/localize';
@@ -54,7 +55,8 @@ import {
5455
IRunByLine,
5556
ISubmitNewCell,
5657
NotebookModelChange,
57-
SysInfoReason
58+
SysInfoReason,
59+
VariableExplorerStateKeys
5860
} from '../interactive-common/interactiveWindowTypes';
5961
import {
6062
CellState,
@@ -174,6 +176,7 @@ export class NativeEditor extends InteractiveBase implements INotebookEditor {
174176
@inject(INotebookImporter) protected readonly importer: INotebookImporter,
175177
@inject(IDataScienceErrorHandler) errorHandler: IDataScienceErrorHandler,
176178
@inject(IMemento) @named(GLOBAL_MEMENTO) globalStorage: Memento,
179+
@inject(IMemento) @named(WORKSPACE_MEMENTO) workspaceStorage: Memento,
177180
@inject(IExperimentsManager) experimentsManager: IExperimentsManager,
178181
@inject(IAsyncDisposableRegistry) asyncRegistry: IAsyncDisposableRegistry,
179182
@inject(KernelSwitcher) switcher: KernelSwitcher,
@@ -202,6 +205,7 @@ export class NativeEditor extends InteractiveBase implements INotebookEditor {
202205
errorHandler,
203206
commandManager,
204207
globalStorage,
208+
workspaceStorage,
205209
nativeEditorDir,
206210
[
207211
path.join(nativeEditorDir, 'require.js'),
@@ -560,6 +564,15 @@ export class NativeEditor extends InteractiveBase implements INotebookEditor {
560564
// VS code is telling us to broadcast this to our UI. Tell the UI about the new change
561565
await this.postMessage(InteractiveWindowMessages.UpdateModel, change);
562566
}
567+
if (change.kind === 'saveAs' && change.model) {
568+
const newFileName = change.model.file.toString();
569+
const oldFileName = change.sourceUri.toString();
570+
571+
if (newFileName !== oldFileName) {
572+
// If the filename has changed
573+
this.renameVariableExplorerHeights(oldFileName, newFileName);
574+
}
575+
}
563576

564577
// Use the current state of the model to indicate dirty (not the message itself)
565578
if (this.model && change.newDirty !== change.oldDirty) {
@@ -572,6 +585,21 @@ export class NativeEditor extends InteractiveBase implements INotebookEditor {
572585
}
573586
}
574587
}
588+
589+
private renameVariableExplorerHeights(name: string, updatedName: string) {
590+
// Updates the workspace storage to reflect the updated name of the notebook
591+
// should be called if the name of the notebook changes
592+
// tslint:disable-next-line: no-any
593+
const value = this.workspaceStorage.get(VariableExplorerStateKeys.height, {} as any);
594+
if (!(name in value)) {
595+
return; // Nothing to update
596+
}
597+
598+
value[updatedName] = value[name];
599+
delete value[name];
600+
this.workspaceStorage.update(VariableExplorerStateKeys.height, value);
601+
}
602+
575603
private interruptExecution() {
576604
this.executeCancelTokens.forEach((t) => t.cancel());
577605
}

src/client/datascience/interactive-ipynb/nativeEditorOldWebView.ts

Lines changed: 4 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -24,7 +24,8 @@ import {
2424
IConfigurationService,
2525
IDisposableRegistry,
2626
IExperimentsManager,
27-
IMemento
27+
IMemento,
28+
WORKSPACE_MEMENTO
2829
} from '../../common/types';
2930
import * as localize from '../../common/utils/localize';
3031
import { noop } from '../../common/utils/misc';
@@ -96,6 +97,7 @@ export class NativeEditorOldWebView extends NativeEditor {
9697
@inject(INotebookImporter) importer: INotebookImporter,
9798
@inject(IDataScienceErrorHandler) errorHandler: IDataScienceErrorHandler,
9899
@inject(IMemento) @named(GLOBAL_MEMENTO) globalStorage: Memento,
100+
@inject(IMemento) @named(WORKSPACE_MEMENTO) workspaceStorage: Memento,
99101
@inject(IExperimentsManager) experimentsManager: IExperimentsManager,
100102
@inject(IAsyncDisposableRegistry) asyncRegistry: IAsyncDisposableRegistry,
101103
@inject(KernelSwitcher) switcher: KernelSwitcher,
@@ -128,6 +130,7 @@ export class NativeEditorOldWebView extends NativeEditor {
128130
importer,
129131
errorHandler,
130132
globalStorage,
133+
workspaceStorage,
131134
experimentsManager,
132135
asyncRegistry,
133136
switcher,

src/client/datascience/interactive-ipynb/nativeEditorStorage.ts

Lines changed: 3 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -35,7 +35,7 @@ interface INativeEditorStorageState {
3535
notebookJson: Partial<nbformat.INotebookContent>;
3636
}
3737

38-
function isUntitledFile(file?: Uri) {
38+
export function isUntitledFile(file?: Uri) {
3939
return file?.scheme === 'untitled';
4040
}
4141
export function isUntitled(model?: INotebookModel): boolean {
@@ -528,7 +528,8 @@ export class NativeEditorStorage implements INotebookStorage {
528528
kind: 'saveAs',
529529
oldDirty: model.isDirty,
530530
newDirty: false,
531-
target: file
531+
target: file,
532+
sourceUri: model.file
532533
});
533534
this.savedAs.fire({ new: file, old });
534535
this.clearHotExit(old).ignoreErrors();

src/client/datascience/interactive-window/interactiveWindow.ts

Lines changed: 4 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -25,7 +25,8 @@ import {
2525
IExperimentsManager,
2626
IMemento,
2727
IPersistentStateFactory,
28-
Resource
28+
Resource,
29+
WORKSPACE_MEMENTO
2930
} from '../../common/types';
3031
import * as localize from '../../common/utils/localize';
3132
import { EXTENSION_ROOT_DIR } from '../../constants';
@@ -110,6 +111,7 @@ export class InteractiveWindow extends InteractiveBase implements IInteractiveWi
110111
@inject(IDataScienceErrorHandler) errorHandler: IDataScienceErrorHandler,
111112
@inject(IPersistentStateFactory) private readonly stateFactory: IPersistentStateFactory,
112113
@inject(IMemento) @named(GLOBAL_MEMENTO) globalStorage: Memento,
114+
@inject(IMemento) @named(WORKSPACE_MEMENTO) workspaceStorage: Memento,
113115
@inject(IExperimentsManager) experimentsManager: IExperimentsManager,
114116
@inject(KernelSwitcher) switcher: KernelSwitcher,
115117
@inject(INotebookProvider) notebookProvider: INotebookProvider,
@@ -137,6 +139,7 @@ export class InteractiveWindow extends InteractiveBase implements IInteractiveWi
137139
errorHandler,
138140
commandManager,
139141
globalStorage,
142+
workspaceStorage,
140143
historyReactDir,
141144
[
142145
path.join(historyReactDir, 'require.js'),

src/datascience-ui/history-react/interactivePanel.tsx

Lines changed: 3 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -307,13 +307,16 @@ ${buildSettingsCss(this.props.settings)}`}</style>
307307
toolbarHeight = this.mainPanelToolbarRef.current.offsetHeight;
308308
}
309309
return {
310+
gridHeight: this.props.variableState.gridHeight,
311+
containerHeight: this.props.variableState.containerHeight,
310312
variables: this.props.variableState.variables,
311313
debugging: this.props.debugging,
312314
busy: this.props.busy,
313315
showDataExplorer: this.props.showDataViewer,
314316
skipDefault: this.props.skipDefault,
315317
testMode: this.props.testMode,
316318
closeVariableExplorer: this.props.toggleVariableExplorer,
319+
setVariableExplorerHeight: this.props.setVariableExplorerHeight,
317320
baseTheme: baseTheme,
318321
pageIn: this.pageInVariableData,
319322
fontSize: this.props.font.size,

src/datascience-ui/history-react/redux/actions.ts

Lines changed: 4 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -18,7 +18,8 @@ import {
1818
ILinkClickAction,
1919
IOpenSettingsAction,
2020
IScrollAction,
21-
IShowDataViewerAction
21+
IShowDataViewerAction,
22+
IVariableExplorerHeight
2223
} from '../../interactive-common/redux/reducers/types';
2324
import { IMonacoModelContentChangeEvent } from '../../react-common/monacoHelpers';
2425

@@ -73,6 +74,8 @@ export const actionCreators = {
7374
submitInput: (code: string, cellId: string): CommonAction<ICodeAction> =>
7475
createIncomingActionWithPayload(CommonActionType.SUBMIT_INPUT, { code, cellId }),
7576
toggleVariableExplorer: (): CommonAction => createIncomingAction(CommonActionType.TOGGLE_VARIABLE_EXPLORER),
77+
setVariableExplorerHeight: (containerHeight: number, gridHeight: number): CommonAction<IVariableExplorerHeight> =>
78+
createIncomingActionWithPayload(CommonActionType.SET_VARIABLE_EXPLORER_HEIGHT, { containerHeight, gridHeight }),
7679
expandAll: (): CommonAction => createIncomingAction(InteractiveWindowMessages.ExpandAll),
7780
collapseAll: (): CommonAction => createIncomingAction(InteractiveWindowMessages.CollapseAll),
7881
export: (): CommonAction => createIncomingAction(CommonActionType.EXPORT),

src/datascience-ui/interactive-common/redux/reducers/types.ts

Lines changed: 8 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -24,7 +24,6 @@ import { CursorPos, IMainState } from '../../mainState';
2424
* 5) Add a new handler for the action under the 'reducer's folder. Handle the expected state change
2525
* 6) Add the handler to the main reducer map in reducers\index.ts
2626
*/
27-
2827
export enum CommonActionType {
2928
ADD_AND_FOCUS_NEW_CELL = 'action.add_new_cell_and_focus_cell',
3029
INSERT_ABOVE_AND_FOCUS_NEW_CELL = 'action.insert_above_and_focus_cell',
@@ -74,6 +73,7 @@ export enum CommonActionType {
7473
SCROLL = 'action.scroll',
7574
SELECT_CELL = 'action.select_cell',
7675
SELECT_SERVER = 'action.select_server',
76+
SET_VARIABLE_EXPLORER_HEIGHT = 'action.set_variable_explorer_height',
7777
SEND_COMMAND = 'action.send_command',
7878
SHOW_DATA_VIEWER = 'action.show_data_viewer',
7979
STEP = 'action.step',
@@ -134,6 +134,7 @@ export type CommonActionTypeMapping = {
134134
[CommonActionType.CODE_CREATED]: ICodeCreatedAction;
135135
[CommonActionType.GET_VARIABLE_DATA]: IJupyterVariablesRequest;
136136
[CommonActionType.TOGGLE_VARIABLE_EXPLORER]: never | undefined;
137+
[CommonActionType.SET_VARIABLE_EXPLORER_HEIGHT]: IVariableExplorerHeight;
137138
[CommonActionType.PostOutgoingMessage]: never | undefined;
138139
[CommonActionType.REFRESH_VARIABLES]: never | undefined;
139140
[CommonActionType.OPEN_SETTINGS]: IOpenSettingsAction;
@@ -230,6 +231,12 @@ export interface ILoadIPyWidgetClassFailureAction {
230231
error: any;
231232
timedout: boolean;
232233
}
234+
235+
export interface IVariableExplorerHeight {
236+
containerHeight: number;
237+
gridHeight: number;
238+
}
239+
233240
export type LoadIPyWidgetClassDisabledAction = {
234241
className: string;
235242
moduleName: string;

0 commit comments

Comments
 (0)