Skip to content

Commit 4f77dfa

Browse files
feat(workspace): copy as command (#3544)
* add json format for copy as command * add test * added copy as for markdpwn format * address Pr comments * add getPodsDir method in ExtensionProvider * add keybinding utils for copy as format * add test * update keybindings
1 parent 5c95add commit 4f77dfa

File tree

13 files changed

+344
-30
lines changed

13 files changed

+344
-30
lines changed

packages/plugin-core/package.json

Lines changed: 14 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -244,6 +244,10 @@
244244
"command": "dendron.copyCodespaceURL",
245245
"title": "Dendron: Copy Codespace URL"
246246
},
247+
{
248+
"command": "dendron.copyAs",
249+
"title": "Dendron: Copy As"
250+
},
247251
{
248252
"command": "dendron.delete",
249253
"title": "Dendron: Delete"
@@ -660,6 +664,10 @@
660664
"command": "dendron.copyCodespaceURL",
661665
"when": "dendron:pluginActive && shellExecutionSupported"
662666
},
667+
{
668+
"command": "dendron.copyAs",
669+
"when": "dendron:pluginActive && shellExecutionSupported"
670+
},
663671
{
664672
"command": "dendron.delete",
665673
"when": "dendron:pluginActive && shellExecutionSupported"
@@ -1324,6 +1332,12 @@
13241332
"mac": "cmd+shift+r",
13251333
"when": "editorFocus && dendron:pluginActive"
13261334
},
1335+
{
1336+
"command": "dendron.copyAs",
1337+
"key": "ctrl+k ctrl+c",
1338+
"mac": "cmd+k cmd+c",
1339+
"when": "dendron:pluginActive"
1340+
},
13271341
{
13281342
"command": "dendron.delete",
13291343
"key": "ctrl+shift+d",

packages/plugin-core/src/ExtensionProvider.ts

Lines changed: 9 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -1,4 +1,6 @@
11
import { DendronError } from "@dendronhq/common-all";
2+
import { PodUtils } from "@dendronhq/pods-core";
3+
import { ensureDirSync } from "fs-extra";
24
import _ from "lodash";
35
import { IDendronExtension } from "./dendronExtensionInterface";
46
import { IWSUtilsV2 } from "./WSUtilsV2Interface";
@@ -51,4 +53,11 @@ export class ExtensionProvider {
5153
static register(extension: IDendronExtension) {
5254
ExtensionProvider.extension = extension;
5355
}
56+
57+
static getPodsDir() {
58+
const { wsRoot } = ExtensionProvider.getDWorkspace();
59+
const podsDir = PodUtils.getPodDir({ wsRoot });
60+
ensureDirSync(podsDir);
61+
return podsDir;
62+
}
5463
}

packages/plugin-core/src/KeybindingUtils.ts

Lines changed: 35 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -344,12 +344,44 @@ export class KeybindingUtils {
344344
return result[0].key;
345345
} else if (result.length > 1) {
346346
throw new DendronError({
347-
message: KeybindingUtils.MULTIPLE_KEYBINDINGS_MSG_FMT,
347+
message: this.getMultipleKeybindingsMsgFormat("pod"),
348348
});
349349
}
350350
return undefined;
351351
}
352352

353-
static MULTIPLE_KEYBINDINGS_MSG_FMT =
354-
"Multiple keybindings found for pod command shortcut.";
353+
static getKeybindingsForCopyAsIfExists(format: string): string | undefined {
354+
const { keybindingConfigPath } = this.getKeybindingConfigPath();
355+
356+
if (!fs.existsSync(keybindingConfigPath)) {
357+
return undefined;
358+
}
359+
360+
const keybindings = readJSONWithCommentsSync(keybindingConfigPath);
361+
362+
if (!KeybindingUtils.checkKeybindingsExist(keybindings)) {
363+
return undefined;
364+
}
365+
366+
const result = keybindings.filter((item) => {
367+
return (
368+
item.command &&
369+
item.command === DENDRON_COMMANDS.COPY_AS.key &&
370+
item.args === format
371+
);
372+
});
373+
374+
if (result.length === 1 && result[0].key) {
375+
return result[0].key;
376+
} else if (result.length > 1) {
377+
throw new DendronError({
378+
message: this.getMultipleKeybindingsMsgFormat("copy as"),
379+
});
380+
}
381+
return undefined;
382+
}
383+
384+
static getMultipleKeybindingsMsgFormat(cmd: string) {
385+
return `Multiple keybindings found for ${cmd} command shortcut.`;
386+
}
355387
}
Lines changed: 80 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,80 @@
1+
import { DendronError } from "@dendronhq/common-all";
2+
import {
3+
CopyAsFormat,
4+
getAllCopyAsFormat,
5+
JSONV2PodConfig,
6+
MarkdownV2PodConfig,
7+
PodExportScope,
8+
PodV2Types,
9+
} from "@dendronhq/pods-core";
10+
import _ from "lodash";
11+
import { PodCommandFactory } from "../components/pods/PodCommandFactory";
12+
import { PodUIControls } from "../components/pods/PodControls";
13+
import { DENDRON_COMMANDS } from "../constants";
14+
import { VSCodeUtils } from "../vsCodeUtils";
15+
import { BasicCommand, CodeCommandInstance } from "./base";
16+
17+
type CommandOutput = void;
18+
type CommandOpts = CodeCommandInstance;
19+
20+
/**
21+
* Command that will find the appropriate export command to run, and then run
22+
* it. This is the UI entry point for all export pod functionality.
23+
*/
24+
export class CopyAsCommand extends BasicCommand<
25+
CommandOpts,
26+
CommandOutput,
27+
CopyAsFormat
28+
> {
29+
public format: CopyAsFormat[];
30+
key = DENDRON_COMMANDS.COPY_AS.key;
31+
32+
constructor(_name?: string) {
33+
super(_name);
34+
this.format = getAllCopyAsFormat();
35+
}
36+
37+
async sanityCheck() {
38+
if (_.isUndefined(VSCodeUtils.getActiveTextEditor())) {
39+
return "you must have a note open to execute this command";
40+
}
41+
return;
42+
}
43+
44+
async gatherInputs(copyAsFormat?: CopyAsFormat) {
45+
const format =
46+
copyAsFormat || (await PodUIControls.promptToSelectCopyAsFormat());
47+
48+
if (!format) {
49+
return;
50+
}
51+
switch (format) {
52+
case CopyAsFormat.JSON: {
53+
const config: JSONV2PodConfig = {
54+
destination: "clipboard",
55+
exportScope: PodExportScope.Note,
56+
podType: PodV2Types.JSONExportV2,
57+
podId: "copyAs.json", // dummy value, required property
58+
};
59+
return PodCommandFactory.createPodCommandForStoredConfig({ config });
60+
}
61+
case CopyAsFormat.MARKDOWN: {
62+
const config: MarkdownV2PodConfig = {
63+
destination: "clipboard",
64+
exportScope: PodExportScope.Note,
65+
podType: PodV2Types.MarkdownExportV2,
66+
podId: "copyAs.markdown", // dummy value, required property
67+
};
68+
return PodCommandFactory.createPodCommandForStoredConfig({ config });
69+
}
70+
default:
71+
throw new DendronError({
72+
message: `${format} is not a valid copy as format. If you are using a keybinding, make sure the argument is one of the following values: ${getAllCopyAsFormat()}`,
73+
});
74+
}
75+
}
76+
77+
async execute(opts: CommandOpts) {
78+
opts.run();
79+
}
80+
}

packages/plugin-core/src/commands/index.ts

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -87,6 +87,7 @@ import { CreateNoteCommand } from "./CreateNoteCommand";
8787
import { MergeNoteCommand } from "./MergeNoteCommand";
8888
import { CopyCodespaceURL } from "./CopyCodespaceURL";
8989
import { MoveSelectionToCommand } from "./MoveSelectionToCommand";
90+
import { CopyAsCommand } from "./CopyAsCommand";
9091

9192
/**
9293
* Note: this does not contain commands that have parametered constructors, as
@@ -181,6 +182,7 @@ const ALL_COMMANDS = [
181182
MergeNoteCommand,
182183
CreateNoteCommand,
183184
CopyCodespaceURL,
185+
CopyAsCommand,
184186
] as CodeCommandConstructor[];
185187

186188
export { ALL_COMMANDS };

packages/plugin-core/src/commands/pods/ExportPodV2Command.ts

Lines changed: 7 additions & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -48,10 +48,10 @@ export class ExportPodV2Command extends BaseCommand<
4848

4949
// If a podId is passed in, use this instead of prompting the user
5050
if (args?.podId) {
51-
return PodCommandFactory.createPodCommandForStoredConfig(
52-
{ podId: args.podId },
53-
args.exportScope
54-
);
51+
return PodCommandFactory.createPodCommandForStoredConfig({
52+
configId: { podId: args.podId },
53+
exportScope: args.exportScope,
54+
});
5555
}
5656

5757
const exportChoice = await PodUIControls.promptForExportConfigOrNewExport();
@@ -66,7 +66,9 @@ export class ExportPodV2Command extends BaseCommand<
6666
}
6767
return PodCommandFactory.createPodCommandForPodType(podType);
6868
} else {
69-
return PodCommandFactory.createPodCommandForStoredConfig(exportChoice);
69+
return PodCommandFactory.createPodCommandForStoredConfig({
70+
configId: exportChoice,
71+
});
7072
}
7173
}
7274

packages/plugin-core/src/commands/pods/JSONExportPodCommand.ts

Lines changed: 3 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -110,17 +110,19 @@ export class JSONExportPodCommand extends BaseExportPodCommand<
110110
exportReturnValue: JSONExportReturnType;
111111
config: RunnableJSONV2PodConfig;
112112
}) {
113+
let successMessage = "Finished running JSON export pod.";
113114
const data = exportReturnValue.data?.exportedNotes;
114115
if (_.isString(data) && config.destination === "clipboard") {
115116
vscode.env.clipboard.writeText(data);
117+
successMessage += " Content is copied to the clipboard";
116118
}
117119
if (ResponseUtil.hasError(exportReturnValue)) {
118120
const errorMsg = `Finished JSON Export. Error encountered: ${ErrorFactory.safeStringify(
119121
exportReturnValue.error
120122
)}`;
121123
this.L.error(errorMsg);
122124
} else {
123-
vscode.window.showInformationMessage("Finished running JSON export pod.");
125+
vscode.window.showInformationMessage(successMessage);
124126
}
125127
}
126128

packages/plugin-core/src/commands/pods/MarkdownExportPodCommand.ts

Lines changed: 3 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -123,8 +123,10 @@ export class MarkdownExportPodCommand extends BaseExportPodCommand<
123123
config: RunnableMarkdownV2PodConfig;
124124
}) {
125125
const data = exportReturnValue.data?.exportedNotes;
126+
let successMessage = "Finished running Markdown export pod.";
126127
if (_.isString(data) && config.destination === "clipboard") {
127128
vscode.env.clipboard.writeText(data);
129+
successMessage += " Content is copied to the clipboard";
128130
}
129131
const count = data?.length ?? 0;
130132
if (ResponseUtil.hasError(exportReturnValue)) {
@@ -133,9 +135,7 @@ export class MarkdownExportPodCommand extends BaseExportPodCommand<
133135
)}`;
134136
this.L.error(errorMsg);
135137
} else {
136-
vscode.window.showInformationMessage(
137-
"Finished running Markdown export pod."
138-
);
138+
vscode.window.showInformationMessage(successMessage);
139139
}
140140
}
141141

packages/plugin-core/src/components/pods/PodCommandFactory.ts

Lines changed: 40 additions & 16 deletions
Original file line numberDiff line numberDiff line change
@@ -13,7 +13,6 @@ import { JSONExportPodCommand } from "../../commands/pods/JSONExportPodCommand";
1313
import { MarkdownExportPodCommand } from "../../commands/pods/MarkdownExportPodCommand";
1414
import { NotionExportPodCommand } from "../../commands/pods/NotionExportPodCommand";
1515
import { ExtensionProvider } from "../../ExtensionProvider";
16-
import { getExtension } from "../../workspace";
1716

1817
export class PodCommandFactory {
1918
/**
@@ -22,27 +21,52 @@ export class PodCommandFactory {
2221
* @param configId
2322
* @returns A pod command configured with the found configuration
2423
*/
25-
public static createPodCommandForStoredConfig(
26-
configId: Pick<ExportPodConfigurationV2, "podId">,
27-
exportScope?: PodExportScope
28-
): CodeCommandInstance {
29-
const storedConfig = PodV2ConfigManager.getPodConfigById({
30-
podsDir: path.join(getExtension().podsDir, "custom"),
31-
opts: configId,
32-
});
33-
34-
if (!storedConfig) {
24+
public static createPodCommandForStoredConfig({
25+
configId,
26+
exportScope,
27+
config,
28+
}: {
29+
configId?: Pick<ExportPodConfigurationV2, "podId">;
30+
exportScope?: PodExportScope;
31+
config?: ExportPodConfigurationV2 & { destination?: string };
32+
}): CodeCommandInstance {
33+
// configId is a required param for all cases except when called from CopyAsCommand. It sends a predefined config
34+
if (!configId && !config) {
3535
throw new DendronError({
36-
message: `No pod config with id ${configId.podId} found.`,
36+
message: `Please provide a config id to continue.`,
3737
});
3838
}
39-
// overrides the exportScope of stored config with the exportScope passed in args
40-
if (exportScope) {
41-
storedConfig.exportScope = exportScope;
39+
let podType: PodV2Types;
40+
let storedConfig: ExportPodConfigurationV2 | undefined;
41+
if (config) {
42+
podType = config.podType;
43+
storedConfig = config;
44+
} else {
45+
if (!configId) {
46+
throw new DendronError({
47+
message: `Please provide a config id`,
48+
});
49+
}
50+
51+
storedConfig = PodV2ConfigManager.getPodConfigById({
52+
podsDir: path.join(ExtensionProvider.getPodsDir(), "custom"),
53+
opts: configId,
54+
});
55+
56+
if (!storedConfig) {
57+
throw new DendronError({
58+
message: `No pod config with id ${configId.podId} found.`,
59+
});
60+
}
61+
// overrides the exportScope of stored config with the exportScope passed in args
62+
if (exportScope) {
63+
storedConfig.exportScope = exportScope;
64+
}
65+
podType = storedConfig.podType;
4266
}
4367
let cmdWithArgs: CodeCommandInstance;
4468
const extension = ExtensionProvider.getExtension();
45-
switch (storedConfig.podType) {
69+
switch (podType) {
4670
case PodV2Types.AirtableExportV2: {
4771
const airtableCmd = new AirtableExportPodCommand(extension);
4872
cmdWithArgs = {

0 commit comments

Comments
 (0)