@statiolake/coc-explorer 0.1.0 → 0.2.0
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/README.md +3 -3
- package/lib/index.js +139 -0
- package/package.json +13 -1
package/README.md
CHANGED
|
@@ -9,6 +9,6 @@ single click; right click opens the item's context action menu through
|
|
|
9
9
|
files intentionally have no type-specific icons.
|
|
10
10
|
|
|
11
11
|
View actions define both the right-click context menu and NvimTree-style local
|
|
12
|
-
keys: `o`, `<C-x>`, `<C-v>`, `<C-t>`, `a`, `
|
|
13
|
-
`+`, `<BS>`, and `P`. Coc's native
|
|
14
|
-
bindings remain available.
|
|
12
|
+
keys: `o`, `<C-x>`, `<C-v>`, `<C-t>`, `a`, `x` (cut), `y` (copy), `p` (paste),
|
|
13
|
+
`d` (delete), `r`, `R`, `gy`, `.`, `s`, `-`, `+`, `<BS>`, and `P`. Coc's native
|
|
14
|
+
`<CR>`, `<Tab>`, `f`, `t`, and `M` TreeView bindings remain available.
|
package/lib/index.js
CHANGED
|
@@ -212,6 +212,18 @@ var Explorer = class {
|
|
|
212
212
|
"coc-explorer.delete",
|
|
213
213
|
(node) => this.delete(node)
|
|
214
214
|
),
|
|
215
|
+
import_coc.commands.registerCommand(
|
|
216
|
+
"coc-explorer.cut",
|
|
217
|
+
(node) => this.stage(node, "cut")
|
|
218
|
+
),
|
|
219
|
+
import_coc.commands.registerCommand(
|
|
220
|
+
"coc-explorer.copy",
|
|
221
|
+
(node) => this.stage(node, "copy")
|
|
222
|
+
),
|
|
223
|
+
import_coc.commands.registerCommand(
|
|
224
|
+
"coc-explorer.paste",
|
|
225
|
+
(node) => this.paste(node)
|
|
226
|
+
),
|
|
215
227
|
import_coc.commands.registerCommand(
|
|
216
228
|
"coc-explorer.copyPath",
|
|
217
229
|
(node) => this.copyPath(node)
|
|
@@ -222,6 +234,7 @@ var Explorer = class {
|
|
|
222
234
|
}
|
|
223
235
|
provider = new ExplorerProvider();
|
|
224
236
|
tree;
|
|
237
|
+
clipboard;
|
|
225
238
|
refreshTimer;
|
|
226
239
|
async show() {
|
|
227
240
|
await this.ui.showContainer("explorer");
|
|
@@ -314,6 +327,7 @@ var Explorer = class {
|
|
|
314
327
|
const target = import_node_path.default.isAbsolute(name) ? name : import_node_path.default.join(import_node_path.default.dirname(node.path), name);
|
|
315
328
|
if (target === node.path) return;
|
|
316
329
|
await import_node_fs.promises.rename(node.path, target);
|
|
330
|
+
this.clearClipboardWithin(node.path);
|
|
317
331
|
this.provider.refresh(node.parent);
|
|
318
332
|
}
|
|
319
333
|
async delete(node) {
|
|
@@ -324,8 +338,71 @@ var Explorer = class {
|
|
|
324
338
|
);
|
|
325
339
|
if (answer !== "Delete") return;
|
|
326
340
|
await import_node_fs.promises.rm(node.path, { recursive: node.directory });
|
|
341
|
+
this.clearClipboardWithin(node.path);
|
|
327
342
|
this.provider.refresh(node.parent);
|
|
328
343
|
}
|
|
344
|
+
stage(node, operation) {
|
|
345
|
+
if (!node) return;
|
|
346
|
+
this.clipboard = {
|
|
347
|
+
operation,
|
|
348
|
+
source: node.path,
|
|
349
|
+
directory: node.directory
|
|
350
|
+
};
|
|
351
|
+
import_coc.window.showInformationMessage(
|
|
352
|
+
`${operation === "copy" ? "Copied" : "Cut"} ${node.path}`
|
|
353
|
+
);
|
|
354
|
+
}
|
|
355
|
+
async paste(node) {
|
|
356
|
+
if (!node) return;
|
|
357
|
+
const entry = this.clipboard;
|
|
358
|
+
if (!entry) {
|
|
359
|
+
import_coc.window.showWarningMessage("Explorer clipboard is empty");
|
|
360
|
+
return;
|
|
361
|
+
}
|
|
362
|
+
const destination = node.directory ? node.path : import_node_path.default.dirname(node.path);
|
|
363
|
+
const target = import_node_path.default.join(destination, import_node_path.default.basename(entry.source));
|
|
364
|
+
if (target === entry.source) {
|
|
365
|
+
import_coc.window.showWarningMessage("Source and destination are the same");
|
|
366
|
+
return;
|
|
367
|
+
}
|
|
368
|
+
if (entry.directory && isWithin(target, entry.source)) {
|
|
369
|
+
import_coc.window.showWarningMessage("Cannot paste a directory inside itself");
|
|
370
|
+
return;
|
|
371
|
+
}
|
|
372
|
+
try {
|
|
373
|
+
await import_node_fs.promises.lstat(entry.source);
|
|
374
|
+
} catch {
|
|
375
|
+
this.clipboard = void 0;
|
|
376
|
+
import_coc.window.showWarningMessage(`Source no longer exists: ${entry.source}`);
|
|
377
|
+
return;
|
|
378
|
+
}
|
|
379
|
+
const replace = await pathExists(target);
|
|
380
|
+
if (replace) {
|
|
381
|
+
const answer = await import_coc.window.showWarningMessage(
|
|
382
|
+
`Replace ${target}?`,
|
|
383
|
+
"Replace"
|
|
384
|
+
);
|
|
385
|
+
if (answer !== "Replace") return;
|
|
386
|
+
}
|
|
387
|
+
await writeReplacing(target, replace, async () => {
|
|
388
|
+
if (entry.operation === "copy") {
|
|
389
|
+
await import_node_fs.promises.cp(entry.source, target, {
|
|
390
|
+
recursive: entry.directory,
|
|
391
|
+
errorOnExist: true,
|
|
392
|
+
force: false
|
|
393
|
+
});
|
|
394
|
+
} else {
|
|
395
|
+
await move(entry.source, target, entry.directory);
|
|
396
|
+
}
|
|
397
|
+
});
|
|
398
|
+
if (entry.operation === "cut") this.clipboard = void 0;
|
|
399
|
+
this.provider.refresh();
|
|
400
|
+
}
|
|
401
|
+
clearClipboardWithin(parent) {
|
|
402
|
+
if (this.clipboard && isWithin(this.clipboard.source, parent)) {
|
|
403
|
+
this.clipboard = void 0;
|
|
404
|
+
}
|
|
405
|
+
}
|
|
329
406
|
async copyPath(node) {
|
|
330
407
|
if (!node) return;
|
|
331
408
|
await import_coc.workspace.nvim.call("setreg", ["+", node.path]);
|
|
@@ -423,6 +500,24 @@ var Explorer = class {
|
|
|
423
500
|
keys: ["r"],
|
|
424
501
|
handler: (node) => this.rename(node)
|
|
425
502
|
},
|
|
503
|
+
{
|
|
504
|
+
id: "coc-explorer.cut",
|
|
505
|
+
title: "Cut",
|
|
506
|
+
keys: ["x"],
|
|
507
|
+
handler: (node) => this.stage(node, "cut")
|
|
508
|
+
},
|
|
509
|
+
{
|
|
510
|
+
id: "coc-explorer.copy",
|
|
511
|
+
title: "Copy",
|
|
512
|
+
keys: ["y"],
|
|
513
|
+
handler: (node) => this.stage(node, "copy")
|
|
514
|
+
},
|
|
515
|
+
{
|
|
516
|
+
id: "coc-explorer.paste",
|
|
517
|
+
title: "Paste",
|
|
518
|
+
keys: ["p"],
|
|
519
|
+
handler: (node) => this.paste(node)
|
|
520
|
+
},
|
|
426
521
|
{
|
|
427
522
|
id: "coc-explorer.delete",
|
|
428
523
|
title: "Delete",
|
|
@@ -466,6 +561,50 @@ var Explorer = class {
|
|
|
466
561
|
function fnameescape(filename) {
|
|
467
562
|
return filename.replace(/([\\\s|"'])/g, "\\$1");
|
|
468
563
|
}
|
|
564
|
+
function isWithin(filename, parent) {
|
|
565
|
+
const relative = import_node_path.default.relative(parent, filename);
|
|
566
|
+
return relative === "" || relative !== ".." && !relative.startsWith(`..${import_node_path.default.sep}`) && !import_node_path.default.isAbsolute(relative);
|
|
567
|
+
}
|
|
568
|
+
async function pathExists(filename) {
|
|
569
|
+
try {
|
|
570
|
+
await import_node_fs.promises.lstat(filename);
|
|
571
|
+
return true;
|
|
572
|
+
} catch {
|
|
573
|
+
return false;
|
|
574
|
+
}
|
|
575
|
+
}
|
|
576
|
+
async function writeReplacing(target, replace, write) {
|
|
577
|
+
if (!replace) {
|
|
578
|
+
await write();
|
|
579
|
+
return;
|
|
580
|
+
}
|
|
581
|
+
const backup = import_node_path.default.join(
|
|
582
|
+
import_node_path.default.dirname(target),
|
|
583
|
+
`.coc-explorer-${import_node_path.default.basename(target)}-${process.pid}-${Date.now()}`
|
|
584
|
+
);
|
|
585
|
+
await import_node_fs.promises.rename(target, backup);
|
|
586
|
+
try {
|
|
587
|
+
await write();
|
|
588
|
+
} catch (error) {
|
|
589
|
+
await import_node_fs.promises.rm(target, { recursive: true, force: true });
|
|
590
|
+
await import_node_fs.promises.rename(backup, target);
|
|
591
|
+
throw error;
|
|
592
|
+
}
|
|
593
|
+
await import_node_fs.promises.rm(backup, { recursive: true, force: true });
|
|
594
|
+
}
|
|
595
|
+
async function move(source, target, directory) {
|
|
596
|
+
try {
|
|
597
|
+
await import_node_fs.promises.rename(source, target);
|
|
598
|
+
} catch (error) {
|
|
599
|
+
if (error.code !== "EXDEV") throw error;
|
|
600
|
+
await import_node_fs.promises.cp(source, target, {
|
|
601
|
+
recursive: directory,
|
|
602
|
+
errorOnExist: true,
|
|
603
|
+
force: false
|
|
604
|
+
});
|
|
605
|
+
await import_node_fs.promises.rm(source, { recursive: directory });
|
|
606
|
+
}
|
|
607
|
+
}
|
|
469
608
|
async function activate(context) {
|
|
470
609
|
const extension = import_coc.extensions.getExtensionById("@statiolake/coc-ui");
|
|
471
610
|
if (!extension?.exports) {
|
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "@statiolake/coc-explorer",
|
|
3
|
-
"version": "0.
|
|
3
|
+
"version": "0.2.0",
|
|
4
4
|
"description": "A lean filesystem explorer for coc.nvim view containers",
|
|
5
5
|
"author": "statiolake",
|
|
6
6
|
"license": "MIT",
|
|
@@ -85,6 +85,18 @@
|
|
|
85
85
|
"command": "coc-explorer.delete",
|
|
86
86
|
"title": "Delete Explorer Item"
|
|
87
87
|
},
|
|
88
|
+
{
|
|
89
|
+
"command": "coc-explorer.cut",
|
|
90
|
+
"title": "Cut Explorer Item"
|
|
91
|
+
},
|
|
92
|
+
{
|
|
93
|
+
"command": "coc-explorer.copy",
|
|
94
|
+
"title": "Copy Explorer Item"
|
|
95
|
+
},
|
|
96
|
+
{
|
|
97
|
+
"command": "coc-explorer.paste",
|
|
98
|
+
"title": "Paste Explorer Item"
|
|
99
|
+
},
|
|
88
100
|
{
|
|
89
101
|
"command": "coc-explorer.copyPath",
|
|
90
102
|
"title": "Copy Explorer Item Path"
|