From 4bc295fe613d1aa5536f11f08818e2e4f5ee11d7 Mon Sep 17 00:00:00 2001 From: Slackadays Date: Fri, 11 Oct 2024 11:56:55 -0400 Subject: [PATCH] Add more for Script, add skeleton Share, remove excess whitespace --- src/cb/CMakeLists.txt | 2 ++ src/cb/src/actions/config.cpp | 3 ++ src/cb/src/actions/history.cpp | 3 +- src/cb/src/actions/script.cpp | 22 ++++++++++++++- src/cb/src/actions/search.cpp | 1 + src/cb/src/actions/share.cpp | 21 ++++++++++++++ src/cb/src/actions/status.cpp | 3 +- src/cb/src/clipboard.cpp | 1 - src/cb/src/clipboard.hpp | 18 +++++++----- src/cb/src/locales/en_us.cpp | 27 +++++++++--------- src/cb/src/utils/editors.cpp | 4 +-- src/cb/src/utils/formatting.cpp | 20 ++++++++++++++ src/cb/src/utils/runners.cpp | 49 +++++++++++++++++++++++++++++++++ src/cb/src/utils/utils.cpp | 10 +++++-- 14 files changed, 156 insertions(+), 28 deletions(-) create mode 100644 src/cb/src/actions/share.cpp create mode 100644 src/cb/src/utils/runners.cpp diff --git a/src/cb/CMakeLists.txt b/src/cb/CMakeLists.txt index 5a6515e3f..1d1392fbe 100644 --- a/src/cb/CMakeLists.txt +++ b/src/cb/CMakeLists.txt @@ -26,6 +26,7 @@ add_executable(cb src/actions/undo.cpp src/actions/redo.cpp src/actions/script.cpp + src/actions/share.cpp src/locales/en_us.cpp src/locales/es_co.cpp src/locales/es_do.cpp @@ -34,6 +35,7 @@ add_executable(cb src/locales/de_de.cpp src/locales/fr_fr.cpp src/utils/editors.cpp + src/utils/runners.cpp src/utils/utils.cpp src/utils/formatting.cpp src/utils/files.cpp diff --git a/src/cb/src/actions/config.cpp b/src/cb/src/actions/config.cpp index 304713ac2..afc61194e 100644 --- a/src/cb/src/actions/config.cpp +++ b/src/cb/src/actions/config.cpp @@ -31,6 +31,9 @@ void config() { // Clipbord editor fprintf(stderr, formatColors("[info]%s┃ Content editor: [help]%s[blank]\n").data(), generatedEndbar().data(), findUsableEditor() ? findUsableEditor().value().data() : "None"); + // Clipboard script runner + fprintf(stderr, formatColors("[info]%s┃ Script runner: [help]%s[blank]\n").data(), generatedEndbar().data(), getenv("CLIPBOARD_SCRIPT_RUNNER") ? getenv("CLIPBOARD_SCRIPT_RUNNER") : "default"); + // Max history size fprintf(stderr, formatColors("[info]%s┃ Max history size: [help]%s[blank]\n").data(), generatedEndbar().data(), !maximumHistorySize.empty() ? maximumHistorySize.data() : "unlimited"); diff --git a/src/cb/src/actions/history.cpp b/src/cb/src/actions/history.cpp index 1257204be..ea1c2a210 100644 --- a/src/cb/src/actions/history.cpp +++ b/src/cb/src/actions/history.cpp @@ -208,7 +208,8 @@ void history() { if (auto MIMEtype = inferMIMEType(content); MIMEtype.has_value()) content = "\033[7m\033[1m " + std::string(MIMEtype.value()) + ", " + formatBytes(content.length()) + " \033[22m\033[27m"; else - content = makeControlCharactersVisible(content, available.columns); + content = removeExcessWhitespace(content, available.columns * 2); + content = makeControlCharactersVisible(content, available.columns); batchedMessage += content.substr(0, widthRemaining); continue; } diff --git a/src/cb/src/actions/script.cpp b/src/cb/src/actions/script.cpp index 6731e71aa..1f2adf323 100644 --- a/src/cb/src/actions/script.cpp +++ b/src/cb/src/actions/script.cpp @@ -17,7 +17,27 @@ namespace PerformAction { void script() { - + if (io_type == IOType::File) { + if (copying.items.size() > 1) { + error_exit("%s", formatColors("[error][inverse] ✘ [noinverse] You can only set one script file to run. [help]⬤ Try providing a single script file instead.[blank]\n")); + } + if (copying.items.empty()) { + error_exit("%s", formatColors("[error][inverse] ✘ [noinverse] You need to provide a script file to run. [help]⬤ Try providing a script file instead.[blank]\n")); + } + if (copying.items.at(0).string() == "") { + fs::remove(path.metadata.script); + if (output_silent || confirmation_silent) return; + stopIndicator(); + fprintf(stderr, "%s", formatColors("[success][inverse] ✔ [noinverse] Removed script[blank]\n").data()); + } else { + fs::remove(path.metadata.script); + fs::copy(copying.items.at(0), path.metadata.script); + if (output_silent || confirmation_silent) return; + stopIndicator(); + fprintf(stderr, formatColors("[success][inverse] ✔ [noinverse] Saved script \"%s\"[blank]\n").data(), fileContents(path.metadata.script).value().data()); + } + } else if (io_type == IOType::Text) { + } } } // namespace PerformAction \ No newline at end of file diff --git a/src/cb/src/actions/search.cpp b/src/cb/src/actions/search.cpp index 3e52cc4c1..dcdf83857 100644 --- a/src/cb/src/actions/search.cpp +++ b/src/cb/src/actions/search.cpp @@ -54,6 +54,7 @@ void displaySearchResults(const std::vector& results) { "", result.entry); std::string preview = result.preview; + preview = removeExcessWhitespace(preview, available.columns * 2); preview = makeControlCharactersVisible(preview, available.columns); auto widthRemaining = available.columns - (longestClipboardLength + longestEntryLength + 7); if (preview.length() > widthRemaining) { diff --git a/src/cb/src/actions/share.cpp b/src/cb/src/actions/share.cpp new file mode 100644 index 000000000..322505019 --- /dev/null +++ b/src/cb/src/actions/share.cpp @@ -0,0 +1,21 @@ +/* The Clipboard Project - Cut, copy, and paste anything, anytime, anywhere, all from the terminal. + Copyright (C) 2024 Jackson Huff and other contributors on GitHub.com + SPDX-License-Identifier: GPL-3.0-or-later + This program is free software: you can redistribute it and/or modify + it under the terms of the GNU General Public License as published by + the Free Software Foundation, either version 3 of the License, or + (at your option) any later version. + + This program is distributed in the hope that it will be useful, + but WITHOUT ANY WARRANTY; without even the implied warranty of + MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + GNU General Public License for more details. + + You should have received a copy of the GNU General Public License + along with this program. If not, see .*/ +#include "../clipboard.hpp" + +namespace PerformAction { +void share() {} + +} // namespace PerformAction \ No newline at end of file diff --git a/src/cb/src/actions/status.cpp b/src/cb/src/actions/status.cpp index 5961e6e81..ac5eba553 100644 --- a/src/cb/src/actions/status.cpp +++ b/src/cb/src/actions/status.cpp @@ -59,7 +59,8 @@ void status() { if (auto type = inferMIMEType(content); type.has_value()) content = "\033[7m\033[1m " + std::string(type.value()) + ", " + formatBytes(content.length()) + " \033[22m\033[27m"; else - content = makeControlCharactersVisible(content, available.columns); + content = removeExcessWhitespace(content, available.columns * 2); + content = makeControlCharactersVisible(content, available.columns); fprintf(stderr, formatColors("[help]%s[blank]\n").data(), content.substr(0, widthRemaining).data()); clipboard.releaseLock(); continue; diff --git a/src/cb/src/clipboard.cpp b/src/cb/src/clipboard.cpp index a7f7c2452..48529f692 100644 --- a/src/cb/src/clipboard.cpp +++ b/src/cb/src/clipboard.cpp @@ -47,7 +47,6 @@ Clipboard::Clipboard(const std::string& clipboard_name, const unsigned long& cli metadata.notes = metadata / constants.notes_name; metadata.originals = metadata / constants.original_files_name; metadata.script = metadata / constants.script_name; - fs::create_directories(data); fs::create_directories(metadata); diff --git a/src/cb/src/clipboard.hpp b/src/cb/src/clipboard.hpp index 0df74f994..7eb5b49a0 100644 --- a/src/cb/src/clipboard.hpp +++ b/src/cb/src/clipboard.hpp @@ -215,7 +215,7 @@ struct IsTTY { }; extern IsTTY is_tty; -enum class Action : unsigned int { Cut, Copy, Paste, Clear, Show, Edit, Add, Remove, Note, Swap, Status, Info, Load, Import, Export, History, Ignore, Search, Undo, Redo, Config, Script }; +enum class Action : unsigned int { Cut, Copy, Paste, Clear, Show, Edit, Add, Remove, Note, Swap, Status, Info, Load, Import, Export, History, Ignore, Search, Undo, Redo, Config, Script, Share }; extern Action action; @@ -236,11 +236,11 @@ class EnumArray : public std::array { T& original(const Action& index) { return internal_original.value()[static_cast(index)]; } }; -extern EnumArray actions; -extern EnumArray action_shortcuts; -extern EnumArray doing_action; -extern EnumArray did_action; -extern EnumArray action_descriptions; +extern EnumArray actions; +extern EnumArray action_shortcuts; +extern EnumArray doing_action; +extern EnumArray did_action; +extern EnumArray action_descriptions; extern std::array, 10> colors; @@ -288,7 +288,7 @@ class Clipboard { fs::path lock; fs::path notes; fs::path originals; - fs::path scripts; + fs::path script; operator fs::path() { return root; } operator fs::path() const { return root; } auto operator=(const auto& other) { return root = other; } @@ -375,6 +375,7 @@ size_t columnLength(const std::string_view& message); std::string generatedEndbar(); std::string repeatString(const std::string_view& character, const size_t& length); std::string makeControlCharactersVisible(const std::string_view& oldStr, size_t len = 0); +std::string removeExcessWhitespace(const std::string_view& str, size_t len = 0); unsigned long levenshteinDistance(const std::string_view& one, const std::string_view& two); void setLanguagePT(); void setLanguageTR(); @@ -475,6 +476,7 @@ extern void writeToGUIClipboard(const ClipboardContent& clipboard); extern const bool GUIClipboardSupportsCut; extern bool playAsyncSoundEffect(const std::valarray& samples); extern std::optional findUsableEditor(); +extern std::optional findUsableScriptRunner(); namespace PerformAction { void copyItem(const fs::path& f, const bool use_regular_copy = copying.use_safe_copy); @@ -511,4 +513,6 @@ void historyJSON(); void search(); void searchJSON(); void config(); +void script(); +void share(); } // namespace PerformAction \ No newline at end of file diff --git a/src/cb/src/locales/en_us.cpp b/src/cb/src/locales/en_us.cpp index 9b721371e..1bcae160e 100644 --- a/src/cb/src/locales/en_us.cpp +++ b/src/cb/src/locales/en_us.cpp @@ -15,22 +15,22 @@ along with this program. If not, see .*/ #include "../clipboard.hpp" -EnumArray actions = {"cut", "copy", "paste", "clear", "show", "edit", "add", "remove", "note", "swap", "status", - "info", "load", "import", "export", "history", "ignore", "search", "undo", "redo", "config", "script"}; +EnumArray actions = {"cut", "copy", "paste", "clear", "show", "edit", "add", "remove", "note", "swap", "status", "info", + "load", "import", "export", "history", "ignore", "search", "undo", "redo", "config", "script", "share"}; -EnumArray action_shortcuts = {"ct", "cp", "p", "clr", "sh", "ed", "ad", "rm", "nt", "sw", "st", "in", "ld", "imp", "ex", "hs", "ig", "sr", "u", "r", "cfg", "sc"}; +EnumArray action_shortcuts = {"ct", "cp", "p", "clr", "sh", "ed", "ad", "rm", "nt", "sw", "st", "in", "ld", "imp", "ex", "hs", "ig", "sr", "u", "r", "cfg", "sc", "sh"}; -EnumArray doing_action = {"Cutting", "Copying", "Pasting", "Clearing", "Showing", "Editing", "Adding", - "Removing", "Noting", "Swapping", "Checking status", "Showing info", "Loading", "Importing", - "Exporting", "Getting history", "Ignoring", "Searching", "Undoing", "Redoing", "Checking Configuration", - "Setting script"}; +EnumArray doing_action = {"Cutting", "Copying", "Pasting", "Clearing", "Showing", "Editing", "Adding", + "Removing", "Noting", "Swapping", "Checking status", "Showing info", "Loading", "Importing", + "Exporting", "Getting history", "Ignoring", "Searching", "Undoing", "Redoing", "Checking Configuration", + "Setting script", "Sharing clipboard"}; -EnumArray did_action = {"Cut", "Copied", "Pasted", "Cleared", "Showed", "Edited", "Added", - "Removed", "Noted", "Swapped", "Checked status", "Showed info", "Loaded", "Imported", - "Exported", "Got history", "Ignored", "Searched", "Undid", "Redid", "Checked Configuration", - "Set script"}; +EnumArray did_action = {"Cut", "Copied", "Pasted", "Cleared", "Showed", "Edited", "Added", + "Removed", "Noted", "Swapped", "Checked status", "Showed info", "Loaded", "Imported", + "Exported", "Got history", "Ignored", "Searched", "Undid", "Redid", "Checked Configuration", + "Set script", "Shared clipboard"}; -EnumArray action_descriptions = { +EnumArray action_descriptions = { "Cut items into a clipboard.", "Copy items into a clipboard.", "Paste items from a clipboard.", @@ -52,7 +52,8 @@ EnumArray action_descriptions = { "Placeholder: Not implemented yet", "Placeholder: Not implemented yet", "Show the configuration of CB.", - "Set a script to run for this clipboard."}; + "Set a script to run for this clipboard.", + "Share a clipboard with others."}; Message help_message = "[info]┃ This is the Clipboard Project %s (commit %s), the cut, copy, and paste system for the command line.[blank]\n" "[info][bold]┃ Examples[blank]\n" diff --git a/src/cb/src/utils/editors.cpp b/src/cb/src/utils/editors.cpp index 2dad064a9..a457c50d9 100644 --- a/src/cb/src/utils/editors.cpp +++ b/src/cb/src/utils/editors.cpp @@ -25,8 +25,8 @@ std::optional findUsableEditor() { }; auto fallbackEditor = []() -> std::optional { - constexpr std::array fallbacks {"nano", "vim", "nvim", "micro", "code", "gedit", "vi", "emacs", "subl", "sublime", "atom", "gedit", "kate", "mousepad", "leafpad", "pluma", "geany", - "notepad.exe", "notepad++.exe", "wordpad.exe", "word.exe"}; + constexpr std::array fallbacks {"nano", "vim", "nvim", "micro", "code", "gedit", "vi", "emacs", "subl", "sublime", "atom", + "gedit", "kate", "mousepad", "leafpad", "pluma", "geany", "notepad.exe", "notepad++.exe", "wordpad.exe", "word.exe"}; std::string pathContent(getenv("PATH")); std::vector paths; diff --git a/src/cb/src/utils/formatting.cpp b/src/cb/src/utils/formatting.cpp index 6a448097f..9b67979c2 100644 --- a/src/cb/src/utils/formatting.cpp +++ b/src/cb/src/utils/formatting.cpp @@ -87,6 +87,26 @@ std::string makeControlCharactersVisible(const std::string_view& oldStr, size_t return newStr; } +std::string removeExcessWhitespace(const std::string_view& oldStr, size_t len) { + std::string newStr; + newStr.reserve(oldStr.size()); + + // Remove all whitespace that isn't a single space (2+ spaces, and tabs) + for (size_t i = 0; i < len && i < oldStr.size(); i++) { + if (oldStr[i] == ' ') { + newStr += ' '; + while (i + 1 < len && i + 1 < oldStr.size() && oldStr[i + 1] == ' ') + i++; + } else if (oldStr[i] == '\t') { + newStr += ' '; + } else { + newStr += oldStr[i]; + } + } + + return newStr; +} + std::string JSONescape(const std::string_view& input) { std::string temp(input); diff --git a/src/cb/src/utils/runners.cpp b/src/cb/src/utils/runners.cpp new file mode 100644 index 000000000..523627a09 --- /dev/null +++ b/src/cb/src/utils/runners.cpp @@ -0,0 +1,49 @@ +/* The Clipboard Project - Cut, copy, and paste anything, anytime, anywhere, all from the terminal. + Copyright (C) 2024 Jackson Huff and other contributors on GitHub.com + SPDX-License-Identifier: GPL-3.0-or-later + This program is free software: you can redistribute it and/or modify + it under the terms of the GNU General Public License as published by + the Free Software Foundation, either version 3 of the License, or + (at your option) any later version. + + This program is distributed in the hope that it will be useful, + but WITHOUT ANY WARRANTY; without even the implied warranty of + MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + GNU General Public License for more details. + + You should have received a copy of the GNU General Public License + along with this program. If not, see .*/ +#include "../clipboard.hpp" + +std::optional findUsableScriptRunner() { + auto preferredScriptRunner = []() -> std::optional { + if (auto runner = getenv("CLIPBOARD_SCRIPT_RUNNER"); runner != nullptr) return runner; + return std::nullopt; + }; + + auto fallbackScriptRunner = []() -> std::optional { +#if defined(_WIN32) || defined(_WIN64) + constexpr std::array fallbacks {"cmd.exe", "powershell.exe", "wsl.exe", "bash.exe", "sh.exe", "zsh.exe", "fish.exe", "pwsh.exe"}; +#else + constexpr std::array fallbacks {"bash", "sh", "zsh", "ksh", "csh", "tcsh", "dash", "fish"}; +#endif + std::string pathContent(getenv("PATH")); + std::vector paths; + + // split paths by : or ; (: for posix, ; for windows) + auto strings = regexSplit(pathContent, std::regex("[:;]")); + std::transform(strings.begin(), strings.end(), std::back_inserter(paths), [](const std::string& path) { return fs::path(path); }); + + for (const auto& path : paths) + for (const auto& fallback : fallbacks) + if (fs::exists(path / fallback)) return fallback; + + return std::nullopt; + }; + + auto runner = preferredScriptRunner(); + + if (!runner) runner = fallbackScriptRunner(); + + return runner; +} \ No newline at end of file diff --git a/src/cb/src/utils/utils.cpp b/src/cb/src/utils/utils.cpp index c01ee406a..032694c96 100644 --- a/src/cb/src/utils/utils.cpp +++ b/src/cb/src/utils/utils.cpp @@ -450,10 +450,10 @@ Action getAction() { IOType getIOType() { using enum Action; using enum IOType; - if (action_is_one_of(Cut, Copy, Add)) { + if (action_is_one_of(Cut, Copy, Add, Script)) { if (copying.items.size() >= 1 && std::all_of(copying.items.begin(), copying.items.end(), [](const auto& item) { return !fs::exists(item); })) return Text; if (!is_tty.in && copying.items.empty()) return Pipe; - } else if (action_is_one_of(Paste, Show, Clear, Edit, Status, Info, History, Search, Config)) { + } else if (action_is_one_of(Paste, Show, Clear, Edit, Status, Info, History, Search, Config, Share)) { if (!is_tty.out) return Pipe; return Text; } else if (action_is_one_of(Remove, Note, Ignore, Swap, Load, Import, Export)) { @@ -641,6 +641,8 @@ void performAction() { copy(); else if (action == Add) addFiles(); + else if (action == Script) + script(); else complainAboutMissingAction("file"); } else if (io_type == Pipe) { @@ -668,6 +670,8 @@ void performAction() { historyJSON(); else if (action == Search) searchJSON(); + else if (action == Script) + script(); else complainAboutMissingAction("pipe"); } else if (io_type == Text) { @@ -707,6 +711,8 @@ void performAction() { search(); else if (action == Config) config(); + else if (action == Script) + script(); else complainAboutMissingAction("text"); }