-
Notifications
You must be signed in to change notification settings - Fork 1.2k
Handling Save as
of notebooks when using new VSC Notebook API
#11876
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
Changes from all commits
2c0daee
518df1a
48b58d9
0987fa9
0f24741
c75ae77
7770c43
c4e289a
6bf730b
07b93ec
0804fe3
5906c31
64e8085
File filter
Filter by extension
Conversations
Jump to
Diff view
Diff view
There are no files selected for viewing
Original file line number | Diff line number | Diff line change |
---|---|---|
|
@@ -4,20 +4,29 @@ | |
'use strict'; | ||
|
||
import { inject, injectable } from 'inversify'; | ||
import { CancellationToken, CancellationTokenSource, Uri, WorkspaceConfiguration } from 'vscode'; | ||
import { CancellationToken, CancellationTokenSource, EventEmitter, Uri, WorkspaceConfiguration } from 'vscode'; | ||
import { IWorkspaceService } from '../../common/application/types'; | ||
import { IDisposable, IDisposableRegistry } from '../../common/types'; | ||
import { DataScience } from '../../common/utils/localize'; | ||
import { noop } from '../../common/utils/misc'; | ||
import { INotebookModel, INotebookStorage } from '../types'; | ||
import { isUntitled } from './nativeEditorStorage'; | ||
|
||
// tslint:disable-next-line:no-require-imports no-var-requires | ||
const debounce = require('lodash/debounce') as typeof import('lodash/debounce'); | ||
|
||
export const INotebookStorageProvider = Symbol.for('INotebookStorageProvider'); | ||
export interface INotebookStorageProvider extends INotebookStorage {} | ||
export interface INotebookStorageProvider extends INotebookStorage { | ||
createNew(contents?: string): Promise<INotebookModel>; | ||
} | ||
@injectable() | ||
export class NotebookStorageProvider implements INotebookStorageProvider { | ||
public get onSavedAs() { | ||
return this._savedAs.event; | ||
} | ||
private readonly _savedAs = new EventEmitter<{ new: Uri; old: Uri }>(); | ||
private readonly storageAndModels = new Map<string, Promise<INotebookModel>>(); | ||
private models = new Set<INotebookModel>(); | ||
private readonly disposables: IDisposable[] = []; | ||
private readonly _autoSaveNotebookInHotExitFile = new WeakMap<INotebookModel, Function>(); | ||
constructor( | ||
|
@@ -26,13 +35,16 @@ export class NotebookStorageProvider implements INotebookStorageProvider { | |
@inject(IWorkspaceService) private readonly workspaceService: IWorkspaceService | ||
) { | ||
disposables.push(this); | ||
disposables.push(storage.onSavedAs((e) => this._savedAs.fire(e))); | ||
} | ||
public save(model: INotebookModel, cancellation: CancellationToken) { | ||
return this.storage.save(model, cancellation); | ||
} | ||
public async saveAs(model: INotebookModel, targetResource: Uri) { | ||
const oldUri = model.file; | ||
await this.storage.saveAs(model, targetResource); | ||
this.trackModel(model); | ||
this.storageAndModels.delete(oldUri.toString()); | ||
this.storageAndModels.set(targetResource.toString(), Promise.resolve(model)); | ||
} | ||
public backup(model: INotebookModel, cancellation: CancellationToken) { | ||
|
@@ -52,11 +64,33 @@ export class NotebookStorageProvider implements INotebookStorageProvider { | |
this.disposables.shift()?.dispose(); // NOSONAR | ||
} | ||
} | ||
|
||
public async createNew(contents?: string): Promise<INotebookModel> { | ||
// Create a new URI for the dummy file using our root workspace path | ||
const uri = await this.getNextNewNotebookUri(); | ||
return this.load(uri, contents); | ||
} | ||
|
||
private async getNextNewNotebookUri(): Promise<Uri> { | ||
// tslint:disable-next-line: no-suspicious-comment | ||
// TODO: This will not work, if we close an untitled document. | ||
// See if we have any untitled storage already | ||
const untitledStorage = Array.from(this.models.values()).filter(isUntitled); | ||
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. Why not use a counter? Aren't you mimicing the existing bug with this? There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more.
Yes, didn't want to fix it here. Just want to leave the bug and fix it correctly with tests. Doing that means more work at this stage. |
||
// Just use the length (don't bother trying to fill in holes). We never remove storage objects from | ||
// our map, so we'll keep creating new untitled notebooks. | ||
const fileName = `${DataScience.untitledNotebookFileName()}-${untitledStorage.length + 1}.ipynb`; | ||
const fileUri = Uri.file(fileName); | ||
// Turn this back into an untitled | ||
return fileUri.with({ scheme: 'untitled', path: fileName }); | ||
} | ||
|
||
private trackModel(model: INotebookModel) { | ||
this.disposables.push(model); | ||
this.models.add(model); | ||
// When a model is no longer used, ensure we remove it from the cache. | ||
model.onDidDispose( | ||
() => { | ||
this.models.delete(model); | ||
this.storageAndModels.delete(model.file.toString()); | ||
this._autoSaveNotebookInHotExitFile.delete(model); | ||
}, | ||
|
Uh oh!
There was an error while loading. Please reload this page.