This commit is contained in:
Timofey Khoruzhii 2023-01-09 11:42:28 +03:00
parent a2186366b8
commit 75eb05bd1a
7 changed files with 206 additions and 73 deletions

View file

@ -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)

View file

@ -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;
}

View file

@ -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;
}

View file

@ -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();

View file

@ -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

View file

@ -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
View 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;
}
}