Compare commits

...

4 Commits

Author SHA1 Message Date
Akos Kitta
fab2f93530 fix(i18n): regenerate translation file
Signed-off-by: Akos Kitta <a.kitta@arduino.cc>
2024-02-23 15:51:08 +01:00
Akos Kitta
fb3c0cab7f feat: can create new folder
Signed-off-by: Akos Kitta <a.kitta@arduino.cc>
2024-02-23 08:51:58 +01:00
Akos Kitta
67e863d9ea fix: order the resources to delete
longest path comes first to remove to most nested resources, then their
container if any.

Signed-off-by: Akos Kitta <a.kitta@arduino.cc>
2024-02-23 08:51:58 +01:00
Akos Kitta
478c36c5bd fix: nested cloud sketch folder sync
Signed-off-by: Akos Kitta <a.kitta@arduino.cc>
2024-02-23 08:51:58 +01:00
6 changed files with 137 additions and 44 deletions

View File

@@ -1,35 +1,36 @@
import { inject, injectable } from '@theia/core/shared/inversify';
import URI from '@theia/core/lib/common/uri';
import { Event } from '@theia/core/lib/common/event';
import { FrontendApplicationContribution } from '@theia/core/lib/browser/frontend-application';
import {
Disposable,
DisposableCollection,
} from '@theia/core/lib/common/disposable';
import { FrontendApplicationContribution } from '@theia/core/lib/browser/frontend-application';
import {
Stat,
FileType,
FileChange,
FileWriteOptions,
FileDeleteOptions,
FileOverwriteOptions,
FileSystemProvider,
FileSystemProviderError,
FileSystemProviderErrorCode,
FileSystemProviderCapabilities,
WatchOptions,
} from '@theia/filesystem/lib/common/files';
import { Event } from '@theia/core/lib/common/event';
import URI from '@theia/core/lib/common/uri';
import { inject, injectable } from '@theia/core/shared/inversify';
import {
FileService,
FileServiceContribution,
} from '@theia/filesystem/lib/browser/file-service';
import {
FileChange,
FileDeleteOptions,
FileOverwriteOptions,
FileSystemProvider,
FileSystemProviderCapabilities,
FileSystemProviderError,
FileSystemProviderErrorCode,
FileType,
FileWriteOptions,
Stat,
WatchOptions,
createFileSystemProviderError,
} from '@theia/filesystem/lib/common/files';
import { SketchesService } from '../../common/protocol';
import { stringToUint8Array } from '../../common/utils';
import { ArduinoPreferences } from '../arduino-preferences';
import { AuthenticationClientService } from '../auth/authentication-client-service';
import { CreateApi } from './create-api';
import { CreateUri } from './create-uri';
import { SketchesService } from '../../common/protocol';
import { ArduinoPreferences } from '../arduino-preferences';
import { Create } from './typings';
import { stringToUint8Array } from '../../common/utils';
import { Create, isNotFound } from './typings';
@injectable()
export class CreateFsProvider
@@ -90,14 +91,27 @@ export class CreateFsProvider
size: 0,
};
}
const resource = await this.getCreateApi.stat(uri.path.toString());
const mtime = Date.parse(resource.modified_at);
return {
type: this.toFileType(resource.type),
ctime: mtime,
mtime,
size: 0,
};
try {
const resource = await this.getCreateApi.stat(uri.path.toString());
const mtime = Date.parse(resource.modified_at);
return {
type: this.toFileType(resource.type),
ctime: mtime,
mtime,
size: 0,
};
} catch (err) {
let errToRethrow = err;
// Not Found (Create API) errors must be remapped to VS Code filesystem provider specific errors
// https://code.visualstudio.com/api/references/vscode-api#FileSystemError
if (isNotFound(errToRethrow)) {
errToRethrow = createFileSystemProviderError(
errToRethrow,
FileSystemProviderErrorCode.FileNotFound
);
}
throw errToRethrow;
}
}
async mkdir(uri: URI): Promise<void> {

View File

@@ -58,6 +58,13 @@ export class WorkspaceCommandContribution extends TheiaWorkspaceCommandContribut
execute: (uri) => this.newFile(uri),
})
);
registry.unregisterCommand(WorkspaceCommands.NEW_FOLDER);
registry.registerCommand(
WorkspaceCommands.NEW_FOLDER,
this.newWorkspaceRootUriAwareCommandHandler({
execute: (uri) => this.newFolder(uri),
})
);
registry.unregisterCommand(WorkspaceCommands.FILE_RENAME);
registry.registerCommand(
WorkspaceCommands.FILE_RENAME,
@@ -72,6 +79,37 @@ export class WorkspaceCommandContribution extends TheiaWorkspaceCommandContribut
);
}
private async newFolder(uri: URI | undefined): Promise<void> {
if (!uri) {
return;
}
const parent = await this.getDirectory(uri);
if (!parent) {
return;
}
const dialog = new WorkspaceInputDialog(
{
title: nls.localizeByDefault('New Folder...'),
parentUri: uri,
placeholder: nls.localize(
'theia/workspace/newFolderPlaceholder',
'Folder Name'
),
validate: (name) => this.validateFileName(name, parent, true),
},
this.labelProvider
);
const name = await this.openDialog(dialog, uri);
if (!name) {
return;
}
const folderUri = uri.resolve(name);
await this.fileService.createFolder(folderUri);
this.fireCreateNewFile({ parent: uri, uri: folderUri });
}
private async newFile(uri: URI | undefined): Promise<void> {
if (!uri) {
return;

View File

@@ -389,21 +389,28 @@ export class CloudSketchbookTree extends SketchbookTree {
private async sync(source: URI, dest: URI): Promise<void> {
const { filesToWrite, filesToDelete } = await this.treeDiff(source, dest);
await Promise.all(
filesToWrite.map(async ({ source, dest }) => {
if ((await this.fileService.resolve(source)).isFile) {
const content = await this.fileService.read(source);
return this.fileService.write(dest, content.value);
}
return this.fileService.createFolder(dest);
})
// Sort by the URIs. The shortest comes first. It's to ensure creating the parent folder for nested resources, for example.
// When sorting the URIs, it does not matter whether on source or dest, only the URI path and its length matters; they're the same for a source+dest pair
const uriPathLengthComparator = (left: URI, right: URI) =>
left.path.toString().length - right.path.toString().length;
filesToWrite.sort((left, right) =>
uriPathLengthComparator(left.source, right.source)
);
for (const { source, dest } of filesToWrite) {
const stat = await this.fileService.resolve(source);
if (stat.isFile) {
const content = await this.fileService.read(source);
await this.fileService.write(dest, content.value);
} else {
await this.fileService.createFolder(dest);
}
}
await Promise.all(
filesToDelete.map((file) =>
this.fileService.delete(file, { recursive: true })
)
);
// Longes URI paths come first to delete the most nested ones first.
filesToDelete.sort(uriPathLengthComparator).reverse();
for (const resource of filesToDelete) {
await this.fileService.delete(resource, { recursive: true });
}
}
override async resolveChildren(

View File

@@ -25,6 +25,14 @@ export namespace SketchbookCommands {
'arduino/sketch/openFolder'
);
export const NEW_FOLDER = Command.toLocalizedCommand(
{
id: 'arduino-sketchbook--new-folder',
label: 'New Folder',
},
'arduino/sketch/newFolder'
);
export const OPEN_SKETCHBOOK_CONTEXT_MENU: Command = {
id: 'arduino-sketchbook--open-sketch-context-menu',
iconClass: 'sketchbook-tree__opts',

View File

@@ -28,7 +28,10 @@ import {
} from '../../sketches-service-client-impl';
import { FileService } from '@theia/filesystem/lib/browser/file-service';
import { URI } from '../../contributions/contribution';
import { WorkspaceInput } from '@theia/workspace/lib/browser';
import {
WorkspaceCommands,
WorkspaceInput,
} from '@theia/workspace/lib/browser';
export const SKETCHBOOK__CONTEXT = ['arduino-sketchbook--context'];
@@ -130,6 +133,21 @@ export class SketchbookWidgetContribution
!!arg && 'node' in arg && SketchbookTree.SketchDirNode.is(arg.node),
});
registry.registerCommand(SketchbookCommands.NEW_FOLDER, {
execute: async (arg) => {
if (arg.node.uri) {
return registry.executeCommand(
WorkspaceCommands.NEW_FOLDER.id,
arg.node.uri
);
}
},
isEnabled: (arg) =>
!!arg && 'node' in arg && SketchbookTree.SketchDirNode.is(arg.node),
isVisible: (arg) =>
!!arg && 'node' in arg && SketchbookTree.SketchDirNode.is(arg.node),
});
registry.registerCommand(SketchbookCommands.OPEN_SKETCHBOOK_CONTEXT_MENU, {
isEnabled: (arg) =>
!!arg && 'node' in arg && SketchbookTree.SketchDirNode.is(arg.node),
@@ -206,6 +224,12 @@ export class SketchbookWidgetContribution
label: SketchbookCommands.REVEAL_IN_FINDER.label,
order: '0',
});
registry.registerMenuAction(SKETCHBOOK__CONTEXT__MAIN_GROUP, {
commandId: SketchbookCommands.NEW_FOLDER.id,
label: SketchbookCommands.NEW_FOLDER.label,
order: '1',
});
}
private openNewWindow(

View File

@@ -455,6 +455,7 @@
"moving": "Moving",
"movingMsg": "The file \"{0}\" needs to be inside a sketch folder named \"{1}\".\nCreate this folder, move the file, and continue?",
"new": "New Sketch",
"newFolder": "New Folder",
"noTrailingPeriod": "A filename cannot end with a dot",
"openFolder": "Open Folder",
"openRecent": "Open Recent",
@@ -545,7 +546,8 @@
"deleteCurrentSketch": "The sketch '{0}' will be permanently deleted. This action is irreversible. Do you want to delete the current sketch?",
"fileNewName": "Name for new file",
"invalidExtension": ".{0} is not a valid extension",
"newFileName": "New name for file"
"newFileName": "New name for file",
"newFolderPlaceholder": "Folder Name"
}
}
}