Skip to content

Commit b776fa8

Browse files
authored
Merge pull request #11 from martinRenou/add_support_opening_subdirs
Add support opening subdirs
2 parents 94eeab0 + f16d5ab commit b776fa8

File tree

2 files changed

+108
-67
lines changed

2 files changed

+108
-67
lines changed

src/drive.ts

Lines changed: 99 additions & 63 deletions
Original file line numberDiff line numberDiff line change
@@ -1,7 +1,21 @@
11
import { Contents, ServerConnection } from '@jupyterlab/services';
22

3+
import { PathExt } from '@jupyterlab/coreutils';
4+
35
import { ISignal, Signal } from '@lumino/signaling';
46

7+
async function toArray<T>(
8+
asyncIterator: AsyncIterableIterator<T>
9+
): Promise<T[]> {
10+
const arr = [];
11+
12+
for await (const i of asyncIterator) {
13+
arr.push(i);
14+
}
15+
16+
return arr;
17+
}
18+
519
export class FileSystemDrive implements Contents.IDrive {
620
get isDisposed(): boolean {
721
return this._isDisposed;
@@ -36,7 +50,7 @@ export class FileSystemDrive implements Contents.IDrive {
3650
}
3751

3852
async get(
39-
localPath: string,
53+
path: string,
4054
options?: Contents.IFetchOptions
4155
): Promise<Contents.IModel> {
4256
const root = this._rootHandle;
@@ -55,12 +69,33 @@ export class FileSystemDrive implements Contents.IDrive {
5569
};
5670
}
5771

72+
let parentHandle = root;
73+
// If requesting a file/directory that is not under root, we need the right directory handle
74+
for (const subPath of path.split('/').slice(0, -1)) {
75+
parentHandle = await parentHandle.getDirectoryHandle(subPath);
76+
}
77+
78+
const parentPath = PathExt.dirname(path);
79+
const localPath = PathExt.basename(path);
80+
81+
let localHandle: FileSystemDirectoryHandle | FileSystemFileHandle;
82+
83+
const currentContent = await toArray(parentHandle.values());
84+
5885
if (localPath) {
59-
const handle = await root.getFileHandle(localPath);
60-
const file = await handle.getFile();
86+
localHandle = currentContent.filter(
87+
element => element.name === localPath
88+
)[0];
89+
} else {
90+
localHandle = parentHandle;
91+
}
92+
93+
if (localHandle.kind === 'file') {
94+
const file = await localHandle.getFile();
95+
6196
return {
6297
name: file.name,
63-
path: file.name,
98+
path: PathExt.join(parentPath, localPath),
6499
created: new Date(file.lastModified).toISOString(),
65100
last_modified: new Date(file.lastModified).toISOString(),
66101
format: 'text',
@@ -69,72 +104,71 @@ export class FileSystemDrive implements Contents.IDrive {
69104
type: 'file',
70105
mimetype: 'text/plain'
71106
};
72-
}
73-
74-
const content: Contents.IModel[] = [];
75-
76-
for await (const value of root.values()) {
77-
if (value.kind === 'file') {
78-
const file = await value.getFile();
79-
content.push({
80-
name: file.name,
81-
path: file.name,
82-
created: new Date(file.lastModified).toISOString(),
83-
last_modified: new Date(file.lastModified).toISOString(),
84-
format: 'text',
85-
content: await file.text(),
86-
writable: true,
87-
type: 'file',
88-
mimetype: 'text/plain'
89-
});
90-
} else {
91-
content.push({
92-
name: value.name,
93-
path: value.name,
94-
created: new Date().toISOString(),
95-
last_modified: new Date().toISOString(),
96-
format: 'json',
97-
content: null,
98-
writable: true,
99-
type: 'directory',
100-
mimetype: 'application/json'
101-
});
107+
} else {
108+
const content: Contents.IModel[] = [];
109+
110+
for await (const value of localHandle.values()) {
111+
if (value.kind === 'file') {
112+
const file = await value.getFile();
113+
content.push({
114+
name: file.name,
115+
path: PathExt.join(parentPath, localPath, file.name),
116+
created: new Date(file.lastModified).toISOString(),
117+
last_modified: new Date(file.lastModified).toISOString(),
118+
format: 'text',
119+
content: await file.text(),
120+
writable: true,
121+
type: 'file',
122+
mimetype: 'text/plain'
123+
});
124+
} else {
125+
content.push({
126+
name: value.name,
127+
path: PathExt.join(parentPath, localPath, value.name),
128+
created: '',
129+
last_modified: '',
130+
format: 'json',
131+
content: null,
132+
writable: true,
133+
type: 'directory',
134+
mimetype: 'application/json'
135+
});
136+
}
102137
}
103-
}
104138

105-
const date = new Date();
106-
return {
107-
name: 'root',
108-
path: '',
109-
last_modified: date.toISOString(),
110-
created: date.toISOString(),
111-
format: 'json',
112-
mimetype: 'application/json',
113-
content,
114-
size: undefined,
115-
writable: true,
116-
type: 'directory'
117-
};
139+
return {
140+
name: PathExt.basename(parentPath),
141+
path: PathExt.join(parentPath, localPath),
142+
last_modified: '',
143+
created: '',
144+
format: 'json',
145+
mimetype: 'application/json',
146+
content,
147+
size: undefined,
148+
writable: true,
149+
type: 'directory'
150+
};
151+
}
118152
}
119153

120-
getDownloadUrl(localPath: string): Promise<string> {
154+
getDownloadUrl(path: string): Promise<string> {
121155
throw new Error('Method not implemented.');
122156
}
123157

124158
newUntitled(options?: Contents.ICreateOptions): Promise<Contents.IModel> {
125159
throw new Error('Method not implemented.');
126160
}
127161

128-
delete(localPath: string): Promise<void> {
162+
delete(path: string): Promise<void> {
129163
throw new Error('Method not implemented.');
130164
}
131165

132-
rename(oldLocalPath: string, newLocalPath: string): Promise<Contents.IModel> {
166+
rename(oldPath: string, newPath: string): Promise<Contents.IModel> {
133167
throw new Error('Method not implemented.');
134168
}
135169

136170
async save(
137-
localPath: string,
171+
path: string,
138172
options?: Partial<Contents.IModel>
139173
): Promise<Contents.IModel> {
140174
const root = this._rootHandle;
@@ -143,7 +177,13 @@ export class FileSystemDrive implements Contents.IDrive {
143177
throw new Error('No root file handle');
144178
}
145179

146-
const handle = await root.getFileHandle(localPath);
180+
let parentHandle = root;
181+
// If saving a file that is not under root, we need the right directory handle
182+
for (const subPath of path.split('/').slice(0, -1)) {
183+
parentHandle = await parentHandle.getDirectoryHandle(subPath);
184+
}
185+
186+
const handle = await parentHandle.getFileHandle(PathExt.basename(path));
147187
const writable = await handle.createWritable({});
148188

149189
const format = options?.format;
@@ -155,25 +195,21 @@ export class FileSystemDrive implements Contents.IDrive {
155195
await writable.write(content);
156196
}
157197
await writable.close();
158-
return this.get(localPath);
198+
return this.get(path);
159199
}
160200

161-
copy(localPath: string, toLocalDir: string): Promise<Contents.IModel> {
201+
copy(path: string, toLocalDir: string): Promise<Contents.IModel> {
162202
throw new Error('Method not implemented.');
163203
}
164204

165-
async createCheckpoint(
166-
localPath: string
167-
): Promise<Contents.ICheckpointModel> {
205+
async createCheckpoint(path: string): Promise<Contents.ICheckpointModel> {
168206
return {
169207
id: 'test',
170208
last_modified: new Date().toISOString()
171209
};
172210
}
173211

174-
async listCheckpoints(
175-
localPath: string
176-
): Promise<Contents.ICheckpointModel[]> {
212+
async listCheckpoints(path: string): Promise<Contents.ICheckpointModel[]> {
177213
return [
178214
{
179215
id: 'test',
@@ -182,11 +218,11 @@ export class FileSystemDrive implements Contents.IDrive {
182218
];
183219
}
184220

185-
restoreCheckpoint(localPath: string, checkpointID: string): Promise<void> {
221+
restoreCheckpoint(path: string, checkpointID: string): Promise<void> {
186222
return Promise.resolve(void 0);
187223
}
188224

189-
deleteCheckpoint(localPath: string, checkpointID: string): Promise<void> {
225+
deleteCheckpoint(path: string, checkpointID: string): Promise<void> {
190226
return Promise.resolve(void 0);
191227
}
192228

src/index.ts

Lines changed: 9 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -42,18 +42,23 @@ const plugin: JupyterFrontEndPlugin<void> = {
4242
serviceManager.contents.addDrive(drive);
4343

4444
const widget = createFileBrowser('jp-filesystem-browser', {
45-
driveName: drive.name
45+
driveName: drive.name,
46+
// We don't want to restore old state, we don't have a drive handle ready
47+
restore: false
4648
});
4749
widget.title.caption = trans.__('Local File System');
4850
widget.title.icon = listIcon;
4951

5052
const openDirectoryButton = new ToolbarButton({
5153
icon: folderIcon,
5254
onClick: async () => {
53-
const fileHandle = await window.showDirectoryPicker();
55+
const directoryHandle = await window.showDirectoryPicker();
5456

55-
if (fileHandle) {
56-
drive.rootHandle = fileHandle;
57+
if (directoryHandle) {
58+
drive.rootHandle = directoryHandle;
59+
60+
// Go to root directory
61+
widget.model.cd('/');
5762
}
5863
},
5964
tooltip: trans.__('Open a new folder')

0 commit comments

Comments
 (0)