Merge branch 'dev' into 'main'
Dev See merge request Onyad/clippy-terminal!1
This commit is contained in:
commit
fc53f9bfb1
|
@ -2,11 +2,27 @@ cmake_minimum_required(VERSION 3.14)
|
||||||
|
|
||||||
project(clippy_terminal)
|
project(clippy_terminal)
|
||||||
|
|
||||||
set(CMAKE_CXX_STANDARD 17)
|
set(CMAKE_CXX_STANDARD 20)
|
||||||
|
|
||||||
file(GLOB_RECURSE SOURCES_FILES src/*)
|
file(GLOB_RECURSE SOURCES_FILES src/*)
|
||||||
|
|
||||||
add_executable(clippy_terminal ${SOURCES_FILES})
|
add_executable(clippy_terminal ${SOURCES_FILES})
|
||||||
target_include_directories(clippy_terminal PRIVATE ${CMAKE_CURRENT_SOURCE_DIR}/src)
|
target_include_directories(clippy_terminal PRIVATE ${CMAKE_CURRENT_SOURCE_DIR}/src)
|
||||||
|
target_link_libraries(clippy_terminal cppshell rang)
|
||||||
|
|
||||||
install(TARGETS clippy_terminal DESTINATION bin)
|
install(TARGETS clippy_terminal DESTINATION bin)
|
||||||
|
|
||||||
|
include(FetchContent)
|
||||||
|
FetchContent_Declare(
|
||||||
|
cppshell
|
||||||
|
GIT_REPOSITORY https://gitlab.com/Onyad/cppshell
|
||||||
|
GIT_TAG origin/main
|
||||||
|
)
|
||||||
|
FetchContent_MakeAvailable(cppshell)
|
||||||
|
|
||||||
|
FetchContent_Declare(
|
||||||
|
rang
|
||||||
|
GIT_REPOSITORY https://github.com/agauniyal/rang.git
|
||||||
|
GIT_TAG origin/master
|
||||||
|
)
|
||||||
|
FetchContent_MakeAvailable(rang)
|
||||||
|
|
91
src/clippy/clippy.cpp
Normal file
91
src/clippy/clippy.cpp
Normal file
|
@ -0,0 +1,91 @@
|
||||||
|
#include <clippy/clippy.hpp>
|
||||||
|
#include <clippy/target.hpp>
|
||||||
|
|
||||||
|
#include <utils/parametres.hpp>
|
||||||
|
#include <utils/config_path.hpp>
|
||||||
|
|
||||||
|
#include <iostream>
|
||||||
|
#include <memory>
|
||||||
|
#include <vector>
|
||||||
|
#include <filesystem>
|
||||||
|
|
||||||
|
#include <rang.hpp>
|
||||||
|
|
||||||
|
void Clippy::Run(const std::vector<std::string>& args) {
|
||||||
|
if (auto result = TryExecuteClippyCommand(args); result) {
|
||||||
|
result->Execute();
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
if (auto result = GetScriptTarget(args); result) {
|
||||||
|
result->Execute();
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
std::cout << rang::bg::red << rang::style::bold << "Unsupported parameters { ";
|
||||||
|
for (size_t i = 1; i < args.size(); ++i) {
|
||||||
|
std::cout << args[i] << (i + 1 == args.size() ? "" : ",") << " ";
|
||||||
|
}
|
||||||
|
std::cout << "}" << std::endl << rang::bg::reset << rang::style::reset;
|
||||||
|
}
|
||||||
|
|
||||||
|
Clippy::TargetPtr Clippy::TryExecuteClippyCommand(
|
||||||
|
const std::vector<std::string>& args) {
|
||||||
|
using namespace utils::parametres;
|
||||||
|
using namespace clippy::targets;
|
||||||
|
if (CheckPatternParametres(args, Parameter::Skip, "help",
|
||||||
|
Parameter::Anything)) {
|
||||||
|
std::cout << "Hello I'm clippy" << std::endl;
|
||||||
|
std::cout << "Parametres: { ";
|
||||||
|
for (size_t i = 0; i < args.size(); ++i) {
|
||||||
|
std::cout << args[i] << (i + 1 == args.size() ? "" : ",") << " ";
|
||||||
|
}
|
||||||
|
std::cout << "}" << std::endl;
|
||||||
|
|
||||||
|
return std::make_unique<EmptyTarget>();
|
||||||
|
}
|
||||||
|
|
||||||
|
if (CheckPatternParametres(args, Parameter::Skip, "cfg",
|
||||||
|
Parameter::Anything)) {
|
||||||
|
LoadProjects();
|
||||||
|
auto p = projects_->GetCurrentProject();
|
||||||
|
|
||||||
|
if (p.has_value()) {
|
||||||
|
return std::make_unique<OpenProjectConfig>(p->GetConfig());
|
||||||
|
} else {
|
||||||
|
return std::make_unique<CreateProjectConfig>(projects_.value());
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
if (CheckPatternParametres(args, Parameter::Skip, "crt",
|
||||||
|
Parameter::Anything)) {
|
||||||
|
LoadProjects();
|
||||||
|
return std::make_unique<CreateProjectConfig>(projects_.value());
|
||||||
|
}
|
||||||
|
|
||||||
|
return nullptr;
|
||||||
|
}
|
||||||
|
|
||||||
|
Clippy::TargetPtr Clippy::GetScriptTarget(
|
||||||
|
const std::vector<std::string>& args) {
|
||||||
|
LoadProjects();
|
||||||
|
auto p = projects_->GetCurrentProject();
|
||||||
|
|
||||||
|
if (!p.has_value() || args.size() < 2) {
|
||||||
|
return nullptr;
|
||||||
|
}
|
||||||
|
|
||||||
|
auto cfg = p->GetConfig();
|
||||||
|
auto result = cfg.GetTarget(args[1]);
|
||||||
|
|
||||||
|
if (result && enable_aliases_) {
|
||||||
|
result->PushFront("shopt -s expand_aliases");
|
||||||
|
}
|
||||||
|
|
||||||
|
return result;
|
||||||
|
}
|
||||||
|
|
||||||
|
void Clippy::LoadProjects() {
|
||||||
|
if (!projects_.has_value()) {
|
||||||
|
projects_.emplace(utils::GetProjectDirectory() / "projects");
|
||||||
|
}
|
||||||
|
}
|
25
src/clippy/clippy.hpp
Normal file
25
src/clippy/clippy.hpp
Normal file
|
@ -0,0 +1,25 @@
|
||||||
|
#pragma once
|
||||||
|
|
||||||
|
#include <clippy/project_list.hpp>
|
||||||
|
#include <clippy/target.hpp>
|
||||||
|
|
||||||
|
#include <vector>
|
||||||
|
#include <string>
|
||||||
|
#include <memory>
|
||||||
|
|
||||||
|
class Clippy {
|
||||||
|
public:
|
||||||
|
Clippy(bool enable_aliases = true) : enable_aliases_(enable_aliases) {}
|
||||||
|
|
||||||
|
void Run(const std::vector<std::string>& args);
|
||||||
|
|
||||||
|
using TargetPtr = std::unique_ptr<clippy::targets::Target>;
|
||||||
|
private:
|
||||||
|
TargetPtr TryExecuteClippyCommand(const std::vector<std::string>& args);
|
||||||
|
TargetPtr GetScriptTarget(const std::vector<std::string>& args);
|
||||||
|
|
||||||
|
void LoadProjects();
|
||||||
|
|
||||||
|
bool enable_aliases_;
|
||||||
|
std::optional<ProjectList> projects_;
|
||||||
|
};
|
56
src/clippy/config.cpp
Normal file
56
src/clippy/config.cpp
Normal file
|
@ -0,0 +1,56 @@
|
||||||
|
#include <clippy/config.hpp>
|
||||||
|
#include <clippy/target.hpp>
|
||||||
|
|
||||||
|
#include <fstream>
|
||||||
|
#include <algorithm>
|
||||||
|
|
||||||
|
std::string Strip(std::string s) {
|
||||||
|
while (!s.empty() && std::isspace(s.back())) {
|
||||||
|
s.pop_back();
|
||||||
|
}
|
||||||
|
std::reverse(s.begin(), s.end());
|
||||||
|
while (!s.empty() && std::isspace(s.back())) {
|
||||||
|
s.pop_back();
|
||||||
|
}
|
||||||
|
std::reverse(s.begin(), s.end());
|
||||||
|
|
||||||
|
return s;
|
||||||
|
}
|
||||||
|
|
||||||
|
std::unique_ptr<clippy::targets::RunShellScript> Config::GetTarget(
|
||||||
|
const std::string& target) {
|
||||||
|
std::ifstream in(path_);
|
||||||
|
|
||||||
|
std::string current;
|
||||||
|
std::vector<std::string> target_commands;
|
||||||
|
target_commands.emplace_back("cd " + initial_directory_);
|
||||||
|
|
||||||
|
bool target_begin = false;
|
||||||
|
|
||||||
|
while (std::getline(in, current)) {
|
||||||
|
if (current == target + ":") {
|
||||||
|
target_begin = true;
|
||||||
|
continue;
|
||||||
|
}
|
||||||
|
|
||||||
|
if (current.empty()) {
|
||||||
|
continue;
|
||||||
|
}
|
||||||
|
|
||||||
|
if (!std::isspace(current[0]) && target_begin) {
|
||||||
|
target_begin = false;
|
||||||
|
}
|
||||||
|
|
||||||
|
if (target_begin) {
|
||||||
|
target_commands.emplace_back(Strip(std::move(current)));
|
||||||
|
} else if (current[0] == '!') {
|
||||||
|
target_commands.emplace_back(current.substr(1, current.size() - 1));
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
if (!target_begin) {
|
||||||
|
return nullptr;
|
||||||
|
}
|
||||||
|
|
||||||
|
return std::make_unique<clippy::targets::RunShellScript>(target_commands);
|
||||||
|
}
|
25
src/clippy/config.hpp
Normal file
25
src/clippy/config.hpp
Normal file
|
@ -0,0 +1,25 @@
|
||||||
|
#pragma once
|
||||||
|
|
||||||
|
#include <utils/editor.hpp>
|
||||||
|
|
||||||
|
#include <string>
|
||||||
|
#include <memory>
|
||||||
|
|
||||||
|
namespace clippy::targets {
|
||||||
|
class Target;
|
||||||
|
class RunShellScript;
|
||||||
|
}
|
||||||
|
|
||||||
|
class Config {
|
||||||
|
public:
|
||||||
|
Config(std::string path, std::string initial_directory)
|
||||||
|
: path_(std::move(path)), initial_directory_(initial_directory) {}
|
||||||
|
|
||||||
|
void Edit() { utils::OpenEditor(path_); }
|
||||||
|
|
||||||
|
std::unique_ptr<clippy::targets::RunShellScript> GetTarget(const std::string& target);
|
||||||
|
|
||||||
|
private:
|
||||||
|
std::string path_;
|
||||||
|
std::string initial_directory_;
|
||||||
|
};
|
110
src/clippy/project_list.cpp
Normal file
110
src/clippy/project_list.cpp
Normal file
|
@ -0,0 +1,110 @@
|
||||||
|
#include <clippy/project_list.hpp>
|
||||||
|
#include <filesystem>
|
||||||
|
#include <utils/lock_file.hpp>
|
||||||
|
|
||||||
|
#include <fstream>
|
||||||
|
#include <mutex>
|
||||||
|
#include <random>
|
||||||
|
#include <chrono>
|
||||||
|
|
||||||
|
Config ProjectList::GetNewConfig(
|
||||||
|
const std::filesystem::path& config_directory) {
|
||||||
|
std::lock_guard guard(lock_file_);
|
||||||
|
|
||||||
|
LoadWithouLock();
|
||||||
|
|
||||||
|
std::mt19937 rnd(std::chrono::system_clock::now().time_since_epoch().count());
|
||||||
|
|
||||||
|
auto RandomSymbol = [&rnd]() {
|
||||||
|
int n = rnd() % (26 + 26 + 10);
|
||||||
|
if (n < 26) {
|
||||||
|
return 'a' + n;
|
||||||
|
} else if (n < 26 + 26) {
|
||||||
|
return 'A' + n - 26;
|
||||||
|
} else {
|
||||||
|
return '0' + n - 26 - 26;
|
||||||
|
}
|
||||||
|
};
|
||||||
|
|
||||||
|
auto GenerateFilename = [&rnd, &RandomSymbol]() {
|
||||||
|
std::string filename;
|
||||||
|
for (size_t i = 0; i < 6; ++i) {
|
||||||
|
filename += RandomSymbol();
|
||||||
|
}
|
||||||
|
|
||||||
|
return filename;
|
||||||
|
};
|
||||||
|
|
||||||
|
auto filename = GenerateFilename();
|
||||||
|
while (true) {
|
||||||
|
bool exist_config = false;
|
||||||
|
for (auto& project : projects_) {
|
||||||
|
if (filename == project.configuration_file.filename()) {
|
||||||
|
exist_config = true;
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
if (!exist_config) {
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
|
||||||
|
filename = GenerateFilename();
|
||||||
|
}
|
||||||
|
|
||||||
|
auto path_to_config = config_directory / filename;
|
||||||
|
|
||||||
|
std::ofstream out(path_, std::ios::app);
|
||||||
|
out << std::filesystem::current_path() << " " << path_to_config << std::endl;
|
||||||
|
out.close();
|
||||||
|
|
||||||
|
projects_.emplace_back(std::filesystem::current_path(), path_to_config);
|
||||||
|
|
||||||
|
return {path_to_config, std::filesystem::current_path()};
|
||||||
|
}
|
||||||
|
|
||||||
|
void ProjectList::Load() {
|
||||||
|
std::lock_guard guard(lock_file_);
|
||||||
|
|
||||||
|
LoadWithouLock();
|
||||||
|
}
|
||||||
|
|
||||||
|
void ProjectList::LoadWithouLock() {
|
||||||
|
std::ifstream in(path_);
|
||||||
|
|
||||||
|
Project current;
|
||||||
|
|
||||||
|
while (in >> current.root_project >> current.configuration_file) {
|
||||||
|
projects_.push_back(current);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
std::optional<Project> ProjectList::GetCurrentProject() {
|
||||||
|
auto current_path = std::filesystem::current_path();
|
||||||
|
|
||||||
|
std::optional<Project> result;
|
||||||
|
auto UpdateResult = [&result](Project p) {
|
||||||
|
if (!result.has_value()) {
|
||||||
|
result = p;
|
||||||
|
} else {
|
||||||
|
result = std::max(p, result.value());
|
||||||
|
}
|
||||||
|
};
|
||||||
|
|
||||||
|
for (auto& project : projects_) {
|
||||||
|
auto prefix_current_path = current_path;
|
||||||
|
|
||||||
|
if (project.root_project == "/") {
|
||||||
|
UpdateResult(project);
|
||||||
|
continue;
|
||||||
|
}
|
||||||
|
|
||||||
|
while (prefix_current_path != "/") {
|
||||||
|
if (prefix_current_path == project.root_project) {
|
||||||
|
UpdateResult(project);
|
||||||
|
}
|
||||||
|
prefix_current_path = prefix_current_path.parent_path();
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
return result;
|
||||||
|
}
|
45
src/clippy/project_list.hpp
Normal file
45
src/clippy/project_list.hpp
Normal file
|
@ -0,0 +1,45 @@
|
||||||
|
#pragma once
|
||||||
|
#include <clippy/config.hpp>
|
||||||
|
|
||||||
|
#include <utils/lock_file.hpp>
|
||||||
|
|
||||||
|
#include <vector>
|
||||||
|
#include <filesystem>
|
||||||
|
#include <optional>
|
||||||
|
|
||||||
|
struct Project {
|
||||||
|
Project() {}
|
||||||
|
|
||||||
|
Project(std::filesystem::path root_project, std::filesystem::path configuration_file)
|
||||||
|
: root_project(root_project), configuration_file(configuration_file) {}
|
||||||
|
|
||||||
|
std::strong_ordering operator<=>(const Project&) const = default;
|
||||||
|
|
||||||
|
Config GetConfig() { return Config{configuration_file, root_project}; }
|
||||||
|
|
||||||
|
std::filesystem::path root_project;
|
||||||
|
std::filesystem::path configuration_file;
|
||||||
|
};
|
||||||
|
|
||||||
|
class ProjectList {
|
||||||
|
public:
|
||||||
|
ProjectList(std::filesystem::path path) : path_(std::move(path)), lock_file_(path_) {
|
||||||
|
Load();
|
||||||
|
}
|
||||||
|
|
||||||
|
Config GetNewConfig(const std::filesystem::path& config_directory);
|
||||||
|
|
||||||
|
const std::vector<Project>& GetProjects() const { return projects_; }
|
||||||
|
|
||||||
|
std::optional<Project> GetCurrentProject();
|
||||||
|
|
||||||
|
private:
|
||||||
|
void Load();
|
||||||
|
void LoadWithouLock();
|
||||||
|
|
||||||
|
private:
|
||||||
|
std::filesystem::path path_;
|
||||||
|
utils::filesystem::LockFile lock_file_;
|
||||||
|
|
||||||
|
std::vector<Project> projects_;
|
||||||
|
};
|
105
src/clippy/target.hpp
Normal file
105
src/clippy/target.hpp
Normal file
|
@ -0,0 +1,105 @@
|
||||||
|
#pragma once
|
||||||
|
|
||||||
|
#include <clippy/project_list.hpp>
|
||||||
|
#include <clippy/config.hpp>
|
||||||
|
|
||||||
|
#include <utils/editor.hpp>
|
||||||
|
#include <utils/config_path.hpp>
|
||||||
|
|
||||||
|
#include <cppshell/shell.hpp>
|
||||||
|
|
||||||
|
#include <rang.hpp>
|
||||||
|
|
||||||
|
#include <iostream>
|
||||||
|
#include <ranges>
|
||||||
|
#include <functional>
|
||||||
|
|
||||||
|
namespace clippy::targets {
|
||||||
|
class Target {
|
||||||
|
public:
|
||||||
|
virtual void Execute() = 0;
|
||||||
|
virtual ~Target() = default;
|
||||||
|
};
|
||||||
|
|
||||||
|
class EmptyTarget : public Target {
|
||||||
|
public:
|
||||||
|
void Execute() override {}
|
||||||
|
~EmptyTarget() override {}
|
||||||
|
};
|
||||||
|
|
||||||
|
class OpenProjectConfig : public Target {
|
||||||
|
public:
|
||||||
|
OpenProjectConfig(Config config) : config_(config) {}
|
||||||
|
|
||||||
|
void Execute() override { config_.Edit(); }
|
||||||
|
~OpenProjectConfig() override {}
|
||||||
|
|
||||||
|
private:
|
||||||
|
Config config_;
|
||||||
|
};
|
||||||
|
|
||||||
|
class CreateProjectConfig : public Target {
|
||||||
|
public:
|
||||||
|
CreateProjectConfig(ProjectList& projects) : projects_(projects) {}
|
||||||
|
|
||||||
|
void Execute() override {
|
||||||
|
auto scripts_path = utils::GetProjectDirectory() / "scripts";
|
||||||
|
std::filesystem::create_directories(scripts_path);
|
||||||
|
|
||||||
|
auto config = projects_.GetNewConfig(scripts_path);
|
||||||
|
config.Edit();
|
||||||
|
}
|
||||||
|
~CreateProjectConfig() override {}
|
||||||
|
|
||||||
|
private:
|
||||||
|
ProjectList& projects_;
|
||||||
|
};
|
||||||
|
|
||||||
|
class RunShellScript : public Target {
|
||||||
|
public:
|
||||||
|
template <template <typename, typename...> class C, typename... Params>
|
||||||
|
RunShellScript(const C<std::string, Params...>& commands) : commands_(commands.begin(), commands.end()) {}
|
||||||
|
|
||||||
|
void PushBack(const std::string& command) {
|
||||||
|
commands_.push_back(command);
|
||||||
|
}
|
||||||
|
|
||||||
|
void PushFront(const std::string& command) {
|
||||||
|
commands_.push_front(command);
|
||||||
|
}
|
||||||
|
|
||||||
|
void Execute() override {
|
||||||
|
auto tmp_path = utils::GetProjectDirectory() / "tmp";
|
||||||
|
std::filesystem::create_directories(tmp_path);
|
||||||
|
|
||||||
|
cppshell::Shell s("bash", tmp_path);
|
||||||
|
|
||||||
|
for (auto& command : commands_) {
|
||||||
|
std::cout << rang::fg::green << rang::style::bold << rang::bgB::blue << "->" << rang::fg::reset
|
||||||
|
<< rang::bg::reset << " " << command << rang::style::reset << std::endl;
|
||||||
|
s.Execute(command);
|
||||||
|
if (int err = s.GetExitCodeLastCommand(); err != 0) {
|
||||||
|
std::cout << rang::fg::red << rang::style::bold
|
||||||
|
<< "Command exit with code " << err << rang::fg::reset
|
||||||
|
<< rang::style::reset << std::endl;
|
||||||
|
std::cout << rang::fg::blue << rang::style::bold
|
||||||
|
<< "Continue execution? [Y/n] " << rang::fg::reset
|
||||||
|
<< rang::style::reset;
|
||||||
|
|
||||||
|
std::string result;
|
||||||
|
std::getline(std::cin, result);
|
||||||
|
if (result == "" || result == "y") {
|
||||||
|
continue;
|
||||||
|
} else {
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
~RunShellScript() override {}
|
||||||
|
|
||||||
|
private:
|
||||||
|
std::deque<std::string> commands_;
|
||||||
|
};
|
||||||
|
} // namespace clippy::targets
|
18
src/main.cpp
18
src/main.cpp
|
@ -1,5 +1,17 @@
|
||||||
#include <iostream>
|
#include <clippy/clippy.hpp>
|
||||||
|
#include <utils/parametres.hpp>
|
||||||
|
|
||||||
int main() {
|
#include <vector>
|
||||||
std::cout << "Hello I'm clippy" << std::endl;
|
#include <string>
|
||||||
|
|
||||||
|
#include <csignal>
|
||||||
|
|
||||||
|
int main(int argc, char* argv[]) {
|
||||||
|
std::signal(SIGPIPE, SIG_IGN);
|
||||||
|
|
||||||
|
using namespace utils::parametres;
|
||||||
|
auto params = ParseInputParameters(argc, argv);
|
||||||
|
|
||||||
|
Clippy clippy;
|
||||||
|
clippy.Run(params);
|
||||||
}
|
}
|
||||||
|
|
20
src/utils/config_path.hpp
Normal file
20
src/utils/config_path.hpp
Normal file
|
@ -0,0 +1,20 @@
|
||||||
|
#pragma once
|
||||||
|
|
||||||
|
#include <string>
|
||||||
|
#include <vector>
|
||||||
|
#include <ranges>
|
||||||
|
#include <filesystem>
|
||||||
|
|
||||||
|
namespace utils {
|
||||||
|
inline std::filesystem::path GetProjectDirectory() {
|
||||||
|
namespace fs = std::filesystem;
|
||||||
|
using std::filesystem::path;
|
||||||
|
|
||||||
|
path config_path =
|
||||||
|
std::string(std::getenv("HOME")) + "/.local/share/clippy-terminal/";
|
||||||
|
|
||||||
|
fs::create_directories(config_path);
|
||||||
|
|
||||||
|
return config_path;
|
||||||
|
}
|
||||||
|
} // namespace utils
|
22
src/utils/editor.cpp
Normal file
22
src/utils/editor.cpp
Normal file
|
@ -0,0 +1,22 @@
|
||||||
|
#include <utils/editor.hpp>
|
||||||
|
#include <utils/config_path.hpp>
|
||||||
|
|
||||||
|
#include <cppshell/shell.hpp>
|
||||||
|
|
||||||
|
namespace utils {
|
||||||
|
void OpenEditor(const std::string& file) {
|
||||||
|
std::string editors[] = {"nvim", "vim", "vi"};
|
||||||
|
|
||||||
|
auto tmp_path = utils::GetProjectDirectory() / "tmp";
|
||||||
|
std::filesystem::create_directories(tmp_path);
|
||||||
|
|
||||||
|
cppshell::Shell shell("bash", tmp_path);
|
||||||
|
|
||||||
|
for (auto& editor : editors) {
|
||||||
|
shell.Execute(editor + " " + file);
|
||||||
|
if (shell.GetExitCodeLastCommand() == 0) {
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
} // namespace utils
|
7
src/utils/editor.hpp
Normal file
7
src/utils/editor.hpp
Normal file
|
@ -0,0 +1,7 @@
|
||||||
|
#pragma once
|
||||||
|
|
||||||
|
#include <string>
|
||||||
|
|
||||||
|
namespace utils {
|
||||||
|
void OpenEditor(const std::string& file);
|
||||||
|
}
|
18
src/utils/lock_file.cpp
Normal file
18
src/utils/lock_file.cpp
Normal file
|
@ -0,0 +1,18 @@
|
||||||
|
#include <utils/lock_file.hpp>
|
||||||
|
|
||||||
|
#include <unistd.h>
|
||||||
|
#include <sys/file.h>
|
||||||
|
|
||||||
|
namespace utils::filesystem {
|
||||||
|
void LockFile::Lock() {
|
||||||
|
fd_ = open(path_.data(), O_RDWR | O_CREAT, 0666);
|
||||||
|
while (flock(fd_, LOCK_EX) != 0) {
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
void LockFile::Unlock() {
|
||||||
|
while (flock(fd_, LOCK_UN) != 0) {
|
||||||
|
}
|
||||||
|
close(fd_);
|
||||||
|
}
|
||||||
|
} // namespace utils::filesystem
|
19
src/utils/lock_file.hpp
Normal file
19
src/utils/lock_file.hpp
Normal file
|
@ -0,0 +1,19 @@
|
||||||
|
#pragma once
|
||||||
|
#include <string>
|
||||||
|
|
||||||
|
namespace utils::filesystem {
|
||||||
|
class LockFile {
|
||||||
|
public:
|
||||||
|
LockFile(std::string path) : path_(std::move(path) + ".lock") {}
|
||||||
|
|
||||||
|
void Lock();
|
||||||
|
void lock() { Lock(); }
|
||||||
|
|
||||||
|
void Unlock();
|
||||||
|
void unlock() { Unlock(); }
|
||||||
|
|
||||||
|
private:
|
||||||
|
std::string path_;
|
||||||
|
int fd_ = -1;
|
||||||
|
};
|
||||||
|
} // namespace utils::filesystem
|
14
src/utils/parametres.cpp
Normal file
14
src/utils/parametres.cpp
Normal file
|
@ -0,0 +1,14 @@
|
||||||
|
#include <utils/parametres.hpp>
|
||||||
|
|
||||||
|
namespace utils::parametres {
|
||||||
|
std::vector<std::string> ParseInputParameters(int argc, char* argv[]) {
|
||||||
|
std::vector<std::string> result;
|
||||||
|
result.reserve(argc);
|
||||||
|
|
||||||
|
for (size_t i = 0; i < argc; ++i) {
|
||||||
|
result.emplace_back(argv[i]);
|
||||||
|
}
|
||||||
|
|
||||||
|
return result;
|
||||||
|
}
|
||||||
|
} // namespace utils
|
66
src/utils/parametres.hpp
Normal file
66
src/utils/parametres.hpp
Normal file
|
@ -0,0 +1,66 @@
|
||||||
|
#pragma once
|
||||||
|
#include <vector>
|
||||||
|
#include <string>
|
||||||
|
#include <stdexcept>
|
||||||
|
|
||||||
|
namespace utils::parametres {
|
||||||
|
enum class Parameter { Skip, Nothing, Anything };
|
||||||
|
|
||||||
|
namespace detail {
|
||||||
|
template <size_t index, typename... Args>
|
||||||
|
bool CheckPatternParametresImpl(const std::vector<std::string>& args,
|
||||||
|
Parameter t) {
|
||||||
|
if (t == Parameter::Nothing) {
|
||||||
|
return index == args.size();
|
||||||
|
} else if (t == Parameter::Anything) {
|
||||||
|
return true;
|
||||||
|
}
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
|
||||||
|
template <size_t index, typename T, typename... Args,
|
||||||
|
std::enable_if_t<!std::is_same_v<T, Parameter>, bool> = true>
|
||||||
|
bool CheckPatternParametresImpl(const std::vector<std::string>& args, T&& head,
|
||||||
|
Args&&... pattern);
|
||||||
|
|
||||||
|
template <size_t index, typename T, typename... Args,
|
||||||
|
std::enable_if_t<std::is_same_v<T, Parameter>, bool> = true>
|
||||||
|
bool CheckPatternParametresImpl(const std::vector<std::string>& args, T&& p,
|
||||||
|
Args&&... pattern) {
|
||||||
|
if (index >= args.size()) {
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
|
||||||
|
if (p != Parameter::Skip) {
|
||||||
|
throw std::logic_error("Unsupported parameter");
|
||||||
|
}
|
||||||
|
|
||||||
|
return CheckPatternParametresImpl<index + 1, Args...>(
|
||||||
|
args, std::forward<Args>(pattern)...);
|
||||||
|
}
|
||||||
|
|
||||||
|
template <size_t index, typename T, typename... Args,
|
||||||
|
std::enable_if_t<!std::is_same_v<T, Parameter>, bool>>
|
||||||
|
bool CheckPatternParametresImpl(const std::vector<std::string>& args, T&& head,
|
||||||
|
Args&&... pattern) {
|
||||||
|
if (index >= args.size()) {
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
if (args[index] != head) {
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
return CheckPatternParametresImpl<index + 1, Args...>(
|
||||||
|
args, std::forward<Args>(pattern)...);
|
||||||
|
}
|
||||||
|
} // namespace detail
|
||||||
|
|
||||||
|
std::vector<std::string> ParseInputParameters(int argc, char* argv[]);
|
||||||
|
|
||||||
|
template <typename... Args>
|
||||||
|
bool CheckPatternParametres(const std::vector<std::string>& args,
|
||||||
|
Args&&... pattern) {
|
||||||
|
return detail::CheckPatternParametresImpl<0, Args...>(
|
||||||
|
args, std::forward<Args>(pattern)...);
|
||||||
|
}
|
||||||
|
|
||||||
|
} // namespace utils
|
Loading…
Reference in a new issue