mirror of
https://github.com/arduino/arduino-ide.git
synced 2025-12-23 15:37:20 +00:00
Compare commits
4 Commits
lincensed
...
fix-nested
| Author | SHA1 | Date | |
|---|---|---|---|
|
|
fab2f93530 | ||
|
|
fb3c0cab7f | ||
|
|
67e863d9ea | ||
|
|
478c36c5bd |
@@ -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> {
|
||||
|
||||
@@ -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;
|
||||
|
||||
@@ -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(
|
||||
|
||||
@@ -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',
|
||||
|
||||
@@ -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(
|
||||
|
||||
@@ -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"
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
Reference in New Issue
Block a user