upd
This commit is contained in:
parent
a2186366b8
commit
75eb05bd1a
|
@ -8,7 +8,7 @@ file(GLOB_RECURSE SOURCES_FILES src/*)
|
|||
|
||||
add_executable(clippy_terminal ${SOURCES_FILES})
|
||||
target_include_directories(clippy_terminal PRIVATE ${CMAKE_CURRENT_SOURCE_DIR}/src)
|
||||
target_link_libraries(clippy_terminal cppshell rang jsoncons)
|
||||
target_link_libraries(clippy_terminal cppshell tmuxub rang jsoncons)
|
||||
|
||||
install(TARGETS clippy_terminal DESTINATION bin)
|
||||
|
||||
|
@ -20,6 +20,13 @@ FetchContent_Declare(
|
|||
)
|
||||
FetchContent_MakeAvailable(cppshell)
|
||||
|
||||
FetchContent_Declare(
|
||||
tmuxub
|
||||
GIT_REPOSITORY https://gitlab.com/Onyad/tmuxub
|
||||
GIT_TAG origin/main
|
||||
)
|
||||
FetchContent_MakeAvailable(tmuxub)
|
||||
|
||||
FetchContent_Declare(
|
||||
rang
|
||||
GIT_REPOSITORY https://github.com/agauniyal/rang.git
|
||||
|
@ -27,10 +34,10 @@ FetchContent_Declare(
|
|||
)
|
||||
FetchContent_MakeAvailable(rang)
|
||||
|
||||
set(JSONCONS_BUILD_TESTS OFF CACHE INTERNAL "Turn off tests")
|
||||
FetchContent_Declare(
|
||||
jsoncons
|
||||
GIT_REPOSITORY https://github.com/danielaparker/jsoncons
|
||||
GIT_TAG origin/master
|
||||
)
|
||||
set(JSONCONS_BUILD_TESTS OFF)
|
||||
FetchContent_MakeAvailable(jsoncons)
|
||||
|
|
|
@ -39,6 +39,8 @@ Clippy::TargetPtr Clippy::TryExecuteClippyCommand(const std::vector<std::string>
|
|||
}
|
||||
std::cout << "}" << std::endl;
|
||||
|
||||
std::cout << "You can use:\n cfg\n crt\n prj\n list\n setname\n loadcfg\n op" << std::endl;
|
||||
|
||||
return std::make_unique<EmptyTarget>();
|
||||
}
|
||||
|
||||
|
@ -59,19 +61,9 @@ Clippy::TargetPtr Clippy::TryExecuteClippyCommand(const std::vector<std::string>
|
|||
}
|
||||
|
||||
if (CheckPatternParametres(args, Parameter::Skip, "prj", Parameter::Anything)) {
|
||||
if (args.size() != 3) {
|
||||
// TODO description current project or project by name
|
||||
return nullptr;
|
||||
}
|
||||
std::string name_project = args[2];
|
||||
|
||||
LoadProjects();
|
||||
|
||||
for (auto& project : projects_->GetProjects()) {
|
||||
if (project.name == name_project) {
|
||||
std::cout << "Find: " << project.root_project << " " << project.configuration_file << std::endl;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
if (CheckPatternParametres(args, Parameter::Skip, "list", Parameter::Nothing)) {
|
||||
LoadProjects();
|
||||
|
@ -85,6 +77,18 @@ Clippy::TargetPtr Clippy::TryExecuteClippyCommand(const std::vector<std::string>
|
|||
return std::make_unique<SetNameProject>(projects_.value(), args[2]);
|
||||
}
|
||||
|
||||
if (CheckPatternParametres(args, Parameter::Skip, "loadcfg", Parameter::Nothing)) {
|
||||
LoadProjects();
|
||||
|
||||
return std::make_unique<OpenLoadConfig>(projects_.value());
|
||||
}
|
||||
|
||||
if (CheckPatternParametres(args, Parameter::Skip, "op", Parameter::Anything)) {
|
||||
LoadProjects();
|
||||
|
||||
return std::make_unique<OpenProject>(projects_.value(), args[2]);
|
||||
}
|
||||
|
||||
return nullptr;
|
||||
}
|
||||
|
||||
|
|
|
@ -10,6 +10,8 @@
|
|||
#include <random>
|
||||
#include <chrono>
|
||||
|
||||
#include <utils/filesystem.hpp>
|
||||
|
||||
#include <jsoncons/json.hpp>
|
||||
#include <jsoncons_ext/cbor/cbor.hpp>
|
||||
|
||||
|
@ -18,45 +20,8 @@ Config ProjectList::GetNewConfig(const std::filesystem::path& config_directory)
|
|||
|
||||
LoadWithoutLock();
|
||||
|
||||
std::mt19937 rnd(std::chrono::system_clock::now().time_since_epoch().count());
|
||||
auto path_to_config = utils::filesystem::GenerateFile(config_directory);
|
||||
|
||||
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;
|
||||
projects_.emplace_back(std::filesystem::current_path(), path_to_config);
|
||||
|
||||
SaveConfig();
|
||||
|
@ -80,6 +45,13 @@ void ProjectList::OldLoadConfig(const std::string& data) {
|
|||
}
|
||||
}
|
||||
|
||||
template <typename U, typename T>
|
||||
void UpdateField(jsoncons::json& data, const std::string& field, std::optional<T> member) {
|
||||
if (member) {
|
||||
data[field] = static_cast<U>(member.value());
|
||||
}
|
||||
}
|
||||
|
||||
void ProjectList::SaveConfig() {
|
||||
jsoncons::json result;
|
||||
|
||||
|
@ -91,10 +63,8 @@ void ProjectList::SaveConfig() {
|
|||
current["path_to_config"] = std::string(project.configuration_file);
|
||||
current["path_root_project"] = std::string(project.root_project);
|
||||
|
||||
std::cout << (bool)project.name << std::endl;
|
||||
if (project.name && !project.name.value().empty()) {
|
||||
current["name"] = project.name.value();
|
||||
}
|
||||
UpdateField<std::string>(current, "name", project.name);
|
||||
UpdateField<std::string>(current, "open_script", project.open_script);
|
||||
|
||||
result["projects"].emplace_back(current);
|
||||
}
|
||||
|
@ -103,6 +73,11 @@ void ProjectList::SaveConfig() {
|
|||
out << result.to_string();
|
||||
}
|
||||
|
||||
template <typename T>
|
||||
std::optional<T> GetOptionalField(const jsoncons::json& data, std::string field) {
|
||||
return data.contains(field) ? std::make_optional(data[field].as<std::string>()) : std::nullopt;
|
||||
}
|
||||
|
||||
void ProjectList::LoadConfig(const jsoncons::json& data) {
|
||||
if (data["version"] != "0.1") {
|
||||
throw std::logic_error("unsupported version config");
|
||||
|
@ -112,18 +87,17 @@ void ProjectList::LoadConfig(const jsoncons::json& data) {
|
|||
for (auto& project : data["projects"].array_range()) {
|
||||
current.configuration_file = project["path_to_config"].as<std::string>();
|
||||
current.root_project = project["path_root_project"].as<std::string>();
|
||||
if (project.contains("name")) {
|
||||
current.name = project["name"].as<std::string>();
|
||||
} else {
|
||||
current.name = std::nullopt;
|
||||
}
|
||||
|
||||
current.name = GetOptionalField<std::string>(project, "name");
|
||||
current.open_script = GetOptionalField<std::string>(project, "open_script");
|
||||
|
||||
projects_.emplace_back(current);
|
||||
}
|
||||
}
|
||||
|
||||
void ProjectList::LoadWithoutLock() {
|
||||
auto data = utils::LoadFile(path_);
|
||||
projects_.clear();
|
||||
auto data = utils::filesystem::LoadFile(path_);
|
||||
|
||||
try {
|
||||
LoadConfig(jsoncons::json::parse(data));
|
||||
|
@ -172,3 +146,16 @@ Project* ProjectList::GetCurrentProject_() {
|
|||
|
||||
return result;
|
||||
}
|
||||
|
||||
Project* ProjectList::GetProjectByName_(const std::string& name) {
|
||||
Project* result = nullptr;
|
||||
for (auto& project : projects_) {
|
||||
if (!project.name) {
|
||||
continue;
|
||||
}
|
||||
if (project.name == name) {
|
||||
result = &project;
|
||||
}
|
||||
}
|
||||
return result;
|
||||
}
|
||||
|
|
|
@ -1,6 +1,7 @@
|
|||
#pragma once
|
||||
#include <clippy/config.hpp>
|
||||
|
||||
#include <mutex>
|
||||
#include <stdexcept>
|
||||
#include <utils/lock_file.hpp>
|
||||
|
||||
|
@ -9,6 +10,9 @@
|
|||
#include <optional>
|
||||
|
||||
#include <jsoncons/json.hpp>
|
||||
#include "utils/config_path.hpp"
|
||||
#include "utils/editor.hpp"
|
||||
#include "utils/filesystem.hpp"
|
||||
|
||||
struct Project {
|
||||
Project() {}
|
||||
|
@ -25,6 +29,7 @@ struct Project {
|
|||
std::filesystem::path root_project;
|
||||
std::filesystem::path configuration_file;
|
||||
std::optional<std::string> name;
|
||||
std::optional<std::filesystem::path> open_script;
|
||||
};
|
||||
|
||||
class ProjectList {
|
||||
|
@ -58,8 +63,37 @@ class ProjectList {
|
|||
}
|
||||
}
|
||||
|
||||
std::filesystem::path GetCurrentLoadConfig() {
|
||||
auto* p = GetCurrentProject_();
|
||||
if (p) {
|
||||
if (p->open_script) {
|
||||
return p->open_script.value();
|
||||
} else {
|
||||
auto scripts_path = utils::GetProjectDirectory() / "scripts";
|
||||
{
|
||||
std::lock_guard lock(lock_file_);
|
||||
p->open_script = utils::filesystem::GenerateFile(scripts_path);
|
||||
SaveConfig();
|
||||
}
|
||||
return p->open_script.value();
|
||||
}
|
||||
} else {
|
||||
throw std::logic_error("Not exists current project");
|
||||
}
|
||||
}
|
||||
|
||||
std::optional<Project> GetProjectByName(const std::string& name) {
|
||||
auto* p = GetProjectByName_(name);
|
||||
if (p) {
|
||||
return *p;
|
||||
} else {
|
||||
return std::nullopt;
|
||||
}
|
||||
}
|
||||
|
||||
private:
|
||||
Project* GetCurrentProject_();
|
||||
Project* GetProjectByName_(const std::string&);
|
||||
|
||||
void OldLoadConfig(const std::string&);
|
||||
void SaveConfig();
|
||||
|
|
|
@ -14,6 +14,9 @@
|
|||
#include <deque>
|
||||
#include <ranges>
|
||||
#include <functional>
|
||||
#include "utils/filesystem.hpp"
|
||||
|
||||
#include <tmuxub/tmuxub.hpp>
|
||||
|
||||
namespace clippy::targets {
|
||||
class Target {
|
||||
|
@ -61,7 +64,8 @@ class CreateProjectConfig : public Target {
|
|||
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()) {}
|
||||
RunShellScript(const C<std::string, Params...>& commands)
|
||||
: commands_(commands.begin(), commands.end()) {}
|
||||
|
||||
void PushBack(const std::string& command) {
|
||||
commands_.push_back(command);
|
||||
|
@ -78,14 +82,15 @@ class RunShellScript : public Target {
|
|||
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;
|
||||
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::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);
|
||||
|
@ -110,8 +115,9 @@ class PrintListProjects : public Target {
|
|||
|
||||
void Execute() override {
|
||||
for (auto& project : projects_.GetProjects()) {
|
||||
std::cout << std::setw(10) << project.name.value_or("-") << " " << std::setw(50) << project.root_project << " "
|
||||
<< std::setw(70) << project.configuration_file << std::endl;
|
||||
std::cout << std::setw(10) << project.name.value_or("-") << " " << std::setw(50)
|
||||
<< project.root_project << " " << std::setw(70) << project.configuration_file
|
||||
<< std::setw(70) << project.open_script.value_or("-") << std::endl;
|
||||
}
|
||||
}
|
||||
|
||||
|
@ -132,4 +138,42 @@ class SetNameProject : public Target {
|
|||
ProjectList& projects_;
|
||||
std::string new_name_;
|
||||
};
|
||||
|
||||
class OpenLoadConfig : public Target {
|
||||
public:
|
||||
OpenLoadConfig(ProjectList& projects) : projects_(projects) {}
|
||||
|
||||
void Execute() override {
|
||||
utils::OpenEditor(projects_.GetCurrentLoadConfig());
|
||||
}
|
||||
|
||||
private:
|
||||
ProjectList& projects_;
|
||||
std::string new_name_;
|
||||
};
|
||||
|
||||
class OpenProject : public Target {
|
||||
public:
|
||||
OpenProject(ProjectList& projects, const std::string& name) : projects_(projects), name_(name) {}
|
||||
|
||||
void Execute() override {
|
||||
auto p = projects_.GetProjectByName(name_);
|
||||
|
||||
tmuxub::Tmux t(utils::GetProjectDirectory() / "tmuxsocket");
|
||||
if (t.HasSession(name_)) {
|
||||
t.ConnectToSession(name_);
|
||||
t.Exec();
|
||||
} else {
|
||||
auto file = utils::filesystem::LoadFile(p->open_script.value());
|
||||
chdir(std::string(p.value().root_project).data());
|
||||
t.CreateSession(name_);
|
||||
t.SplitWindow(file);
|
||||
t.Exec();
|
||||
}
|
||||
}
|
||||
|
||||
private:
|
||||
ProjectList& projects_;
|
||||
std::string name_;
|
||||
};
|
||||
} // namespace clippy::targets
|
||||
|
|
|
@ -1,10 +1,15 @@
|
|||
#pragma once
|
||||
|
||||
#include <iostream>
|
||||
#include <string>
|
||||
#include <filesystem>
|
||||
#include <fstream>
|
||||
#include <random>
|
||||
#include <chrono>
|
||||
|
||||
namespace utils {
|
||||
#include <utils/random.hpp>
|
||||
|
||||
namespace utils::filesystem {
|
||||
inline std::string LoadFile(const std::filesystem::path& file) {
|
||||
std::ifstream in(file);
|
||||
|
||||
|
@ -19,4 +24,44 @@ inline std::string LoadFile(const std::filesystem::path& file) {
|
|||
return result;
|
||||
}
|
||||
|
||||
} // namespace utils
|
||||
inline std::filesystem::path GenerateFile(std::filesystem::path dir) {
|
||||
if (!std::filesystem::is_directory(dir)) {
|
||||
throw std::logic_error(std::string(dir) + " is not directory");
|
||||
}
|
||||
|
||||
static auto RandomSymbol = []() {
|
||||
int n = utils::random::RandInt(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 = [length = 6]() mutable {
|
||||
if (length > 10) {
|
||||
throw std::logic_error("failed to create file");
|
||||
}
|
||||
std::string filename;
|
||||
for (size_t i = 0; i < length; ++i) {
|
||||
filename += RandomSymbol();
|
||||
}
|
||||
length++;
|
||||
|
||||
return filename;
|
||||
};
|
||||
|
||||
auto filename = GenerateFilename();
|
||||
while (true) {
|
||||
if (std::filesystem::status(dir / filename).type() == std::filesystem::file_type::not_found) {
|
||||
break;
|
||||
}
|
||||
|
||||
filename = GenerateFilename();
|
||||
}
|
||||
|
||||
return dir / filename;
|
||||
}
|
||||
} // namespace utils::filesystem
|
||||
|
|
12
src/utils/random.hpp
Normal file
12
src/utils/random.hpp
Normal file
|
@ -0,0 +1,12 @@
|
|||
#pragma once
|
||||
|
||||
#include <random>
|
||||
#include <chrono>
|
||||
|
||||
namespace utils::random {
|
||||
static inline std::mt19937_64 rnd(std::chrono::system_clock::now().time_since_epoch().count());
|
||||
|
||||
inline uint64_t RandInt(uint64_t max_n) {
|
||||
return rnd() % max_n;
|
||||
}
|
||||
}
|
Loading…
Reference in a new issue