Skip to content

Commit

Permalink
Merge branch '#76-os-specific-commands' into #78-split-modal-to-tabs
Browse files Browse the repository at this point in the history
# Conflicts:
#	src/settings/ShellCommandExtraOptionsModal.ts
  • Loading branch information
Taitava committed Oct 29, 2021
2 parents 3b26edd + b2bfdc4 commit d144aa7
Show file tree
Hide file tree
Showing 14 changed files with 395 additions and 197 deletions.
12 changes: 9 additions & 3 deletions src/Common.ts
Original file line number Diff line number Diff line change
@@ -1,6 +1,7 @@
import {App, Editor, FileSystemAdapter, MarkdownView, normalizePath} from "obsidian";
import {OperatingSystemName} from "./settings/ShellCommandsPluginSettings";
import {PlatformId} from "./settings/ShellCommandsPluginSettings";
import {platform} from "os";
import * as path from "path";

export function getVaultAbsolutePath(app: App) {
// Original code was copied 2021-08-22 from https://github.com/phibr0/obsidian-open-with/blob/84f0e25ba8e8355ff83b22f4050adde4cc6763ea/main.ts#L66-L67
Expand All @@ -20,9 +21,10 @@ export function isWindows() {
}

/**
* This is just a wrapper around platform() in order to cast the type to OperatingSystemName.
* This is just a wrapper around platform() in order to cast the type to PlatformId.
* TODO: Consider renaming this to getPlatformId().
*/
export function getOperatingSystem(): OperatingSystemName {
export function getOperatingSystem(): PlatformId {
// @ts-ignore In theory, platform() can return an OS name not included in OperatingSystemName. But as Obsidian
// currently does not support anything else than Windows, Mac and Linux (except mobile platforms, but they are
// ruled out by the manifest of this plugin), it should be safe to assume that the current OS is one of those
Expand Down Expand Up @@ -99,6 +101,10 @@ export function normalizePath2(path: string) {
return path;
}

export function extractFileName(file_path: string) {
return path.parse(file_path).base;
}

export function joinObjectProperties(object: {}, glue: string) {
let result = "";
for (let property_name in object) {
Expand Down
4 changes: 2 additions & 2 deletions src/Migrations.ts
Original file line number Diff line number Diff line change
Expand Up @@ -86,9 +86,9 @@ function MigrateShellCommandToPlatforms(plugin: ShellCommandsPlugin) {
let shell_command_configuration: ShellCommandConfiguration = plugin.settings.shell_commands[shell_command_id];
if (undefined !== shell_command_configuration.shell_command) {
// The shell command should be migrated.
if (undefined === shell_command_configuration.platforms || shell_command_configuration.platforms.default === "") {
if (undefined === shell_command_configuration.platform_specific_commands || shell_command_configuration.platform_specific_commands.default === "") {
console.log("Migrating shell command #" + shell_command_id + ": shell_command string will be moved to platforms.default: " + shell_command_configuration.shell_command);
shell_command_configuration.platforms = {
shell_command_configuration.platform_specific_commands = {
default: shell_command_configuration.shell_command,
};
delete shell_command_configuration.shell_command;
Expand Down
15 changes: 14 additions & 1 deletion src/Shell.ts
Original file line number Diff line number Diff line change
@@ -1,9 +1,22 @@
import {isWindows} from "./Common";
import {extractFileName, getOperatingSystem, isWindows} from "./Common";
import {PlatformShells} from "./settings/ShellCommandsPluginSettings";

export function getUsersDefaultShell(): string {
if (isWindows()) {
return process.env.ComSpec;
} else {
return process.env.SHELL;
}
}

export function isShellSupported(shell: string) {
const shell_file_name = extractFileName(shell);
const supported_shells = PlatformShells[getOperatingSystem()];
for (let supported_shell_path in supported_shells) {
if (supported_shell_path.substr(-shell_file_name.length, shell_file_name.length).toLowerCase() === shell_file_name.toLowerCase()) {
// 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.
return true;
}
}
return false;
}
16 changes: 12 additions & 4 deletions src/TShellCommand.ts
Original file line number Diff line number Diff line change
Expand Up @@ -47,6 +47,10 @@ export class TShellCommand {
}
}

public getShells() {
return this.configuration.shells;
}

/**
* Returns a shell command string specific for the current operating system, or a generic shell command if this shell
* command does not have an explicit version for the current OS.
Expand All @@ -55,13 +59,13 @@ export class TShellCommand {
let operating_system = getOperatingSystem();

// Check if the shell command has defined a specific command for this operating system.
if (undefined === this.configuration.platforms[operating_system]) {
if (undefined === this.configuration.platform_specific_commands[operating_system]) {
// No command is defined specifically for this operating system.
// Return an "OS agnostic" command.
return this.configuration.platforms.default;
return this.configuration.platform_specific_commands.default;
} else {
// The shell command has defined a specific command for this operating system.
return this.configuration.platforms[operating_system];
return this.configuration.platform_specific_commands[operating_system];
}
}

Expand All @@ -71,7 +75,11 @@ export class TShellCommand {
* current platform into account.
*/
public getDefaultShellCommand() {
return this.configuration.platforms.default;
return this.configuration.platform_specific_commands.default;
}

public getPlatformSpecificShellCommands() {
return this.configuration.platform_specific_commands;
}

public getAlias() {
Expand Down
19 changes: 15 additions & 4 deletions src/main.ts
Original file line number Diff line number Diff line change
Expand Up @@ -16,7 +16,7 @@ import {ConfirmExecutionModal} from "./ConfirmExecutionModal";
import {handleShellCommandOutput} from "./output_channels/OutputChannelDriverFunctions";
import {BaseEncodingOptions} from "fs";
import {TShellCommand, TShellCommandContainer} from "./TShellCommand";
import {getUsersDefaultShell} from "./Shell";
import {getUsersDefaultShell, isShellSupported} from "./Shell";
import {TShellCommandTemporary} from "./TShellCommandTemporary";

export default class ShellCommandsPlugin extends Plugin {
Expand Down Expand Up @@ -111,7 +111,7 @@ export default class ShellCommandsPlugin extends Plugin {
} else {
// Variable parsing succeeded.
// Use the parsed values.
preparsed_t_shell_command.getConfiguration().platforms = {default: parsed_shell_command}; // Overrides all possible OS specific shell command versions.
preparsed_t_shell_command.getConfiguration().platform_specific_commands = {default: parsed_shell_command}; // Overrides all possible OS specific shell command versions.
}

// Also parse variables in an alias, in case the command has one. Variables in aliases do not do anything practical, but they can reveal the user what variables are used in the command.
Expand Down Expand Up @@ -244,6 +244,17 @@ export default class ShellCommandsPlugin extends Plugin {
return;
}

// Check that the currently defined shell is supported by this plugin. If using system default shell, it's possible
// that the shell is something that is not supported. Also, the settings file can be edited manually, and incorrect
// shell can be written there.
const shell = t_shell_command.getShell();
if (!isShellSupported(shell)) {
console.log("Shell is not supported: " + shell);
this.newError("This plugin does not support the following shell: " + shell);
return;
}


// Check that the working directory exists and is a folder
if (!fs.existsSync(working_directory)) {
// Working directory does not exist
Expand All @@ -261,7 +272,7 @@ export default class ShellCommandsPlugin extends Plugin {
// Prepare execution options
let options: BaseEncodingOptions & ExecOptions = {
"cwd": working_directory,
"shell": t_shell_command.getShell(),
"shell": shell,
};

// Execute the shell command
Expand Down Expand Up @@ -368,7 +379,7 @@ export default class ShellCommandsPlugin extends Plugin {

public getDefaultShell(): string {
let operating_system = getOperatingSystem()
let shell_name = this.settings.default_shell[operating_system]; // Can also be undefined.
let shell_name = this.settings.default_shells[operating_system]; // Can also be undefined.
if (undefined === shell_name) {
shell_name = getUsersDefaultShell();
}
Expand Down
4 changes: 2 additions & 2 deletions src/settings/ShellCommandConfiguration.ts
Original file line number Diff line number Diff line change
Expand Up @@ -11,7 +11,7 @@ export interface ShellCommandConfiguration {
* - key: platform (= OS) name
* - value: shell command
*/
platforms: IPlatformSpecificStringWithDefault;
platform_specific_commands: IPlatformSpecificStringWithDefault;
shells: IPlatformSpecificString;
alias: string;
confirm_execution: boolean;
Expand All @@ -29,7 +29,7 @@ export interface ShellCommandConfiguration {

export function newShellCommandConfiguration(shell_command: string = ""): ShellCommandConfiguration {
return {
platforms: {
platform_specific_commands: {
default: shell_command,
},
shells: {},
Expand Down
23 changes: 21 additions & 2 deletions src/settings/ShellCommandExtraOptionsModal.ts
Original file line number Diff line number Diff line change
Expand Up @@ -4,6 +4,13 @@ import {ShellCommandSettingGroup, ShellCommandsSettingsTab} from "./ShellCommand
import {getOutputChannelDriversOptionList} from "../output_channels/OutputChannelDriverFunctions";
import {OutputChannel, OutputChannelOrder, OutputStream} from "../output_channels/OutputChannel";
import {TShellCommand} from "../TShellCommand";
import {PlatformId, PlatformNames} from "./ShellCommandsPluginSettings";
import {createShellSelectionField} from "./setting_elements/CreateShellSelectionField";
import {
generateIgnoredErrorCodesIconTitle,
generateShellCommandFieldName
} from "./setting_elements/CreateShellCommandField";
import {createPlatformSpecificShellCommandField} from "./setting_elements/CreatePlatformSpecificShellCommandField";

export class ShellCommandExtraOptionsModal extends Modal {
static OPTIONS_SUMMARY = "Alias, Output, Confirmation, Ignore errors";
Expand All @@ -26,6 +33,9 @@ export class ShellCommandExtraOptionsModal extends Modal {
onOpen() {
this.modalEl.createEl("h2", {text: this.t_shell_command.getDefaultShellCommand()});

// Make the modal scrollable if it has more content than what fits in the screen.
this.modalEl.addClass("SC-scrollable");

// Tab headers
let tab_header = this.modalEl.createEl("div", {attr: {class: "SC-tab-header"}});
tab_header.createEl("button", {text: "General"}).onclick = () => {
Expand Down Expand Up @@ -53,7 +63,7 @@ export class ShellCommandExtraOptionsModal extends Modal {
this.plugin.obsidian_commands[this.shell_command_id].name = this.plugin.generateObsidianCommandName(this.t_shell_command);

// UpdateShell commands settings panel
this.name_setting.setName(this.setting_tab.generateCommandFieldName(this.shell_command_id, this.t_shell_command));
this.name_setting.setName(generateShellCommandFieldName(this.shell_command_id, this.t_shell_command));

// Save
await this.plugin.saveSettings();
Expand Down Expand Up @@ -132,7 +142,7 @@ export class ShellCommandExtraOptionsModal extends Modal {
let icon_container = this.name_setting.nameEl.find("span.shell-commands-ignored-error-codes-icon-container");
if (this.t_shell_command.getIgnoreErrorCodes().length) {
// Show icon
icon_container.setAttr("aria-label", this.setting_tab.generateIgnoredErrorCodesIconTitle(this.t_shell_command.getIgnoreErrorCodes()));
icon_container.setAttr("aria-label", generateIgnoredErrorCodesIconTitle(this.t_shell_command.getIgnoreErrorCodes()));
icon_container.removeClass("shell-commands-hide");
} else {
// Hide icon
Expand All @@ -141,6 +151,15 @@ export class ShellCommandExtraOptionsModal extends Modal {
})
)
;

// Platform specific shell selection
createShellSelectionField(this.plugin, container_element, this.t_shell_command.getShells(), false);

// Platform specific shell commands
let platform_id: PlatformId;
for (platform_id in PlatformNames) {
createPlatformSpecificShellCommandField(this.plugin, container_element, this.t_shell_command, platform_id);
}
}

private newOutputChannelSetting(title: string, output_stream_name: OutputStream, description: string = "") {
Expand Down
28 changes: 25 additions & 3 deletions src/settings/ShellCommandsPluginSettings.ts
Original file line number Diff line number Diff line change
Expand Up @@ -2,7 +2,7 @@
import {ShellCommandsConfiguration} from "./ShellCommandConfiguration";

export interface ShellCommandsPluginSettings {
default_shell: IPlatformSpecificString;
default_shells: IPlatformSpecificString;
working_directory: string;
preview_variables_in_command_palette: boolean;
shell_commands: ShellCommandsConfiguration;
Expand All @@ -15,7 +15,7 @@ export interface ShellCommandsPluginSettings {
}

export const DEFAULT_SETTINGS: ShellCommandsPluginSettings = {
default_shell: {},
default_shells: {},
working_directory: "",
preview_variables_in_command_palette: true,
shell_commands: {},
Expand All @@ -35,7 +35,13 @@ export const DEFAULT_SETTINGS: ShellCommandsPluginSettings = {
*
* @see NodeJS.Platform
*/
export type OperatingSystemName = "darwin" | "linux" | "win32";
export type PlatformId = "darwin" | "linux" | "win32";

export const PlatformNames: IPlatformSpecificString = {
darwin: "Macintosh",
linux: "Linux",
win32: "Windows",
};

/**
* All OSes supported by the Shell commands plugin.
Expand All @@ -55,3 +61,19 @@ export interface IPlatformSpecificString {
export interface IPlatformSpecificStringWithDefault extends IPlatformSpecificString{
default: string,
}

export const PlatformShells = {
darwin: {
"/bin/bash": "Bash",
"/bin/zsh": "Zsh (Z shell)"
},
linux: {
"/bin/bash": "Bash",
"/bin/zsh": "Zsh (Z shell)"
},
win32: {
"pwsh.exe": "PowerShell Core",
"PowerShell.exe": "PowerShell 5",
"CMD.EXE": "cmd.exe",
}
}
Loading

0 comments on commit d144aa7

Please sign in to comment.