Skip to content

Commit a394a62

Browse files
committed
Merge branch '#76-os-specific-commands' into 0.7.0
2 parents 1abc7fb + 11276d7 commit a394a62

18 files changed

+759
-280
lines changed

src/Common.ts

Lines changed: 19 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -1,4 +1,7 @@
11
import {App, Editor, FileSystemAdapter, MarkdownView, normalizePath} from "obsidian";
2+
import {PlatformId} from "./settings/ShellCommandsPluginSettings";
3+
import {platform} from "os";
4+
import * as path from "path";
25

36
export function getVaultAbsolutePath(app: App) {
47
// Original code was copied 2021-08-22 from https://github.com/phibr0/obsidian-open-with/blob/84f0e25ba8e8355ff83b22f4050adde4cc6763ea/main.ts#L66-L67
@@ -17,6 +20,18 @@ export function isWindows() {
1720
return process.platform === "win32";
1821
}
1922

23+
/**
24+
* This is just a wrapper around platform() in order to cast the type to PlatformId.
25+
* TODO: Consider renaming this to getPlatformId().
26+
*/
27+
export function getOperatingSystem(): PlatformId {
28+
// @ts-ignore In theory, platform() can return an OS name not included in OperatingSystemName. But as Obsidian
29+
// currently does not support anything else than Windows, Mac and Linux (except mobile platforms, but they are
30+
// ruled out by the manifest of this plugin), it should be safe to assume that the current OS is one of those
31+
// three.
32+
return platform();
33+
}
34+
2035
export function getView(app: App) {
2136
let view = app.workspace.getActiveViewOfType(MarkdownView);
2237
if (!view) {
@@ -86,6 +101,10 @@ export function normalizePath2(path: string) {
86101
return path;
87102
}
88103

104+
export function extractFileName(file_path: string) {
105+
return path.parse(file_path).base;
106+
}
107+
89108
export function joinObjectProperties(object: {}, glue: string) {
90109
let result = "";
91110
for (let property_name in object) {

src/ConfirmExecutionModal.ts

Lines changed: 7 additions & 7 deletions
Original file line numberDiff line numberDiff line change
@@ -1,26 +1,26 @@
11
import {Modal, Setting} from "obsidian";
22
import ShellCommandsPlugin from "./main";
3-
import {ShellCommandConfiguration} from "./settings/ShellCommandConfiguration";
3+
import {TShellCommand} from "./TShellCommand";
44

55
export class ConfirmExecutionModal extends Modal {
66
private plugin: ShellCommandsPlugin;
77
private readonly shell_command: string;
8-
private shell_command_configuration: ShellCommandConfiguration;
8+
private readonly t_shell_command: TShellCommand;
99

10-
constructor(plugin: ShellCommandsPlugin, shell_command: string, shell_command_configuration: ShellCommandConfiguration) {
10+
constructor(plugin: ShellCommandsPlugin, shell_command: string, t_shell_command: TShellCommand) {
1111
super(plugin.app);
1212
this.plugin = plugin;
1313
this.shell_command = shell_command;
14-
this.shell_command_configuration = shell_command_configuration;
14+
this.t_shell_command = t_shell_command;
1515
}
1616

1717
open() {
1818
super.open();
1919

2020
// Information about the shell command
2121
this.modalEl.createEl("h2", {text: this.shell_command, attr: {style: "margin-bottom: 0;"}});
22-
if (this.shell_command_configuration.alias) {
23-
let paragraph = this.modalEl.createEl("p", {text: "Alias: " + this.shell_command_configuration.alias, attr: {style: "margin-top: 0;"}});
22+
if (this.t_shell_command.getAlias()) {
23+
let paragraph = this.modalEl.createEl("p", {text: "Alias: " + this.t_shell_command.getAlias(), attr: {style: "margin-top: 0;"}});
2424
}
2525
this.modalEl.createEl("p", {text: "Execute this shell command?"});
2626

@@ -30,7 +30,7 @@ export class ConfirmExecutionModal extends Modal {
3030
.setButtonText("Yes, execute!")
3131
.onClick(() => {
3232
console.log("User confirmed execution of shell command: " + this.shell_command);
33-
this.plugin.executeShellCommand(this.shell_command, this.shell_command_configuration);
33+
this.plugin.executeShellCommand(this.shell_command, this.t_shell_command);
3434
this.close();
3535
})
3636
)

src/Migrations.ts

Lines changed: 45 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -1,9 +1,11 @@
11
import ShellCommandsPlugin from "./main";
2-
import {newShellCommandConfiguration} from "./settings/ShellCommandConfiguration";
2+
import {newShellCommandConfiguration, ShellCommandConfiguration} from "./settings/ShellCommandConfiguration";
33

44
export async function RunMigrations(plugin: ShellCommandsPlugin) {
55
let save = MigrateCommandsToShellCommands(plugin);
6+
save ||= MigrateShellCommandToPlatforms(plugin);
67
save ||= EnsureShellCommandsHaveAllFields(plugin);
8+
save ||= DeleteEmptyCommandsField(plugin);
79
if (save) {
810
// Only save if there were changes to configuration.
911
console.log("Saving migrations...")
@@ -13,6 +15,9 @@ export async function RunMigrations(plugin: ShellCommandsPlugin) {
1315
}
1416

1517
function MigrateCommandsToShellCommands(plugin: ShellCommandsPlugin) {
18+
if (undefined === plugin.settings.commands) {
19+
return false;
20+
}
1621
let count_shell_commands = plugin.settings.commands.length;
1722
let save = false;
1823
if (0 < count_shell_commands) {
@@ -59,7 +64,7 @@ function EnsureShellCommandsHaveAllFields(plugin: ShellCommandsPlugin) {
5964
let save = false;
6065
let shell_command_default_configuration = newShellCommandConfiguration();
6166
let shell_command_id: string;
62-
let shell_command_configurations = plugin.getShellCommands();
67+
let shell_command_configurations = plugin.settings.shell_commands;
6368
for (shell_command_id in shell_command_configurations) {
6469
let shell_command_configuration = shell_command_configurations[shell_command_id];
6570
for (let property_name in shell_command_default_configuration) {
@@ -77,4 +82,42 @@ function EnsureShellCommandsHaveAllFields(plugin: ShellCommandsPlugin) {
7782
}
7883
}
7984
return save;
85+
}
86+
87+
function MigrateShellCommandToPlatforms(plugin: ShellCommandsPlugin) {
88+
let save = false;
89+
for (let shell_command_id in plugin.settings.shell_commands) {
90+
let shell_command_configuration: ShellCommandConfiguration = plugin.settings.shell_commands[shell_command_id];
91+
if (undefined !== shell_command_configuration.shell_command) {
92+
// The shell command should be migrated.
93+
if (undefined === shell_command_configuration.platform_specific_commands || shell_command_configuration.platform_specific_commands.default === "") {
94+
console.log("Migrating shell command #" + shell_command_id + ": shell_command string will be moved to platforms.default: " + shell_command_configuration.shell_command);
95+
shell_command_configuration.platform_specific_commands = {
96+
default: shell_command_configuration.shell_command,
97+
};
98+
delete shell_command_configuration.shell_command;
99+
save = true;
100+
} else {
101+
console.log("Migration failure for shell command #" + shell_command_id + ": platforms exists already.");
102+
}
103+
}
104+
}
105+
return save;
106+
}
107+
108+
/**
109+
* Can be removed in 1.0.0.
110+
*
111+
* @param plugin
112+
* @constructor
113+
*/
114+
function DeleteEmptyCommandsField(plugin: ShellCommandsPlugin) {
115+
let save = false;
116+
if (undefined !== plugin.settings.commands) {
117+
if (plugin.settings.commands.length === 0) {
118+
delete plugin.settings.commands;
119+
save = true;
120+
}
121+
}
122+
return save;
80123
}

src/Shell.ts

Lines changed: 22 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,22 @@
1+
import {extractFileName, getOperatingSystem, isWindows} from "./Common";
2+
import {PlatformShells} from "./settings/ShellCommandsPluginSettings";
3+
4+
export function getUsersDefaultShell(): string {
5+
if (isWindows()) {
6+
return process.env.ComSpec;
7+
} else {
8+
return process.env.SHELL;
9+
}
10+
}
11+
12+
export function isShellSupported(shell: string) {
13+
const shell_file_name = extractFileName(shell);
14+
const supported_shells = PlatformShells[getOperatingSystem()];
15+
for (let supported_shell_path in supported_shells) {
16+
if (supported_shell_path.substr(-shell_file_name.length, shell_file_name.length).toLowerCase() === shell_file_name.toLowerCase()) {
17+
// If supported_shell_path (e.g. /bin/bash or CMD.EXE) ends with shell_file_name (e.g. bash, derived from /bin/bash or CMD.EXE, derived from C:\System32\CMD.EXE), then the shell can be considered to be supported.
18+
return true;
19+
}
20+
}
21+
return false;
22+
}

src/TShellCommand.ts

Lines changed: 104 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,104 @@
1+
import {ShellCommandConfiguration} from "./settings/ShellCommandConfiguration";
2+
import ShellCommandsPlugin from "./main";
3+
import {getOperatingSystem} from "./Common";
4+
5+
export interface TShellCommandContainer {
6+
[key: string]: TShellCommand,
7+
}
8+
9+
export class TShellCommand {
10+
11+
private readonly id: string;
12+
private plugin: ShellCommandsPlugin;
13+
private configuration: ShellCommandConfiguration;
14+
15+
constructor (plugin: ShellCommandsPlugin, shell_command_id: string, configuration: ShellCommandConfiguration) {
16+
this.plugin = plugin;
17+
this.id = shell_command_id;
18+
this.configuration = configuration;
19+
}
20+
21+
public getPlugin() {
22+
return this.plugin;
23+
}
24+
/**
25+
* Use this when you need to alter the configuration values. if you only need to read configuration values, use get*()
26+
* methods instead.
27+
*/
28+
public getConfiguration() {
29+
return this.configuration;
30+
}
31+
32+
public getId() {
33+
return this.id;
34+
}
35+
36+
public getShell(): string {
37+
let operating_system = getOperatingSystem();
38+
39+
// Check if the shell command has defined a specific shell.
40+
if (undefined === this.configuration.shells[operating_system]) {
41+
// The shell command does not define an explicit shell.
42+
// Use a default shell from the plugin's settings.
43+
return this.plugin.getDefaultShell();
44+
} else {
45+
// The shell command has an explicit shell defined.
46+
return this.configuration.shells[operating_system];
47+
}
48+
}
49+
50+
public getShells() {
51+
return this.configuration.shells;
52+
}
53+
54+
/**
55+
* Returns a shell command string specific for the current operating system, or a generic shell command if this shell
56+
* command does not have an explicit version for the current OS.
57+
*/
58+
public getShellCommand(): string {
59+
let operating_system = getOperatingSystem();
60+
61+
// Check if the shell command has defined a specific command for this operating system.
62+
if (undefined === this.configuration.platform_specific_commands[operating_system]) {
63+
// No command is defined specifically for this operating system.
64+
// Return an "OS agnostic" command.
65+
return this.configuration.platform_specific_commands.default;
66+
} else {
67+
// The shell command has defined a specific command for this operating system.
68+
return this.configuration.platform_specific_commands[operating_system];
69+
}
70+
}
71+
72+
/**
73+
* Returns a version of the shell command that should be used if no platform specific command is defined for the
74+
* current platform. If you plan to use this for execution, consider using getShellCommand() instead, as it takes the
75+
* current platform into account.
76+
*/
77+
public getDefaultShellCommand() {
78+
return this.configuration.platform_specific_commands.default;
79+
}
80+
81+
public getPlatformSpecificShellCommands() {
82+
return this.configuration.platform_specific_commands;
83+
}
84+
85+
public getAlias() {
86+
return this.configuration.alias;
87+
}
88+
89+
public getConfirmExecution() {
90+
return this.configuration.confirm_execution;
91+
}
92+
93+
public getIgnoreErrorCodes() {
94+
return this.configuration.ignore_error_codes;
95+
}
96+
97+
public getOutputChannelOrder() {
98+
return this.configuration.output_channel_order;
99+
}
100+
101+
public getOutputChannels() {
102+
return this.configuration.output_channels;
103+
}
104+
}

src/TShellCommandTemporary.ts

Lines changed: 32 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,32 @@
1+
import {TShellCommand} from "./TShellCommand";
2+
import ShellCommandsPlugin from "./main";
3+
import {ShellCommandConfiguration} from "./settings/ShellCommandConfiguration";
4+
import {cloneObject} from "./Common";
5+
6+
export class TShellCommandTemporary extends TShellCommand {
7+
8+
/**
9+
* @private Do not create new objects directly, use fromTShellCommand() instead.
10+
* @param plugin
11+
* @param shell_command_configuration
12+
*/
13+
constructor(plugin: ShellCommandsPlugin, shell_command_configuration: ShellCommandConfiguration) {
14+
super(plugin, null, shell_command_configuration);
15+
}
16+
17+
public getId(): string {
18+
throw Error("TShellCommandTemporary does not have an ID, because it is a clone of a real TShellCommand that should not be saved.");
19+
}
20+
21+
/**
22+
* Returns a TShellCommandTemporary instance, which contains configuration that is copied from the given TShellCommand.
23+
* The clone can be used for altering the configuration temporarily. The clone cannot be saved, and it's ID cannot be
24+
* accessed.
25+
*/
26+
public static fromTShellCommand(t_shell_command: TShellCommand) {
27+
return new TShellCommandTemporary(
28+
t_shell_command.getPlugin(),
29+
cloneObject(t_shell_command.getConfiguration()),
30+
);
31+
}
32+
}

0 commit comments

Comments
 (0)