You can not select more than 25 topics Topics must start with a letter or number, can include dashes ('-') and can be up to 35 characters long.
 
 

207 lines
8.8 KiB

#include "config.h"
#include "files.h"
#include "filesystem.h"
#include "terminal.h"
#include <algorithm>
#include <exception>
#include <iostream>
Config::Config() {
home_path = filesystem::get_home_path();
if(home_path.empty())
throw std::runtime_error("Could not find home path");
home_juci_path = home_path / ".juci";
}
void Config::load() {
auto config_json = (home_juci_path / "config" / "config.json").string(); // This causes some redundant copies, but assures windows support
boost::property_tree::ptree cfg;
try {
find_or_create_config_files();
boost::property_tree::json_parser::read_json(config_json, cfg);
update(cfg);
read(cfg);
}
catch(const std::exception &e) {
dispatcher.post([config_json, e_what = std::string(e.what())] {
::Terminal::get().print("Error: could not parse " + config_json + ": " + e_what + "\n", true);
});
std::stringstream ss;
ss << default_config_file;
boost::property_tree::read_json(ss, cfg);
read(cfg);
}
}
void Config::find_or_create_config_files() {
auto config_dir = home_juci_path / "config";
auto config_json = config_dir / "config.json";
boost::filesystem::create_directories(config_dir); // io exp captured by calling method
if(!boost::filesystem::exists(config_json))
filesystem::write(config_json, default_config_file);
auto juci_style_path = home_juci_path / "styles";
boost::filesystem::create_directories(juci_style_path); // io exp captured by calling method
juci_style_path /= "juci-light.xml";
if(!boost::filesystem::exists(juci_style_path))
filesystem::write(juci_style_path, juci_light_style);
juci_style_path = juci_style_path.parent_path();
juci_style_path /= "juci-dark.xml";
if(!boost::filesystem::exists(juci_style_path))
filesystem::write(juci_style_path, juci_dark_style);
juci_style_path = juci_style_path.parent_path();
juci_style_path /= "juci-dark-blue.xml";
if(!boost::filesystem::exists(juci_style_path))
filesystem::write(juci_style_path, juci_dark_blue_style);
}
void Config::update(boost::property_tree::ptree &cfg) {
boost::property_tree::ptree default_cfg;
bool cfg_ok = true;
if(cfg.get<std::string>("version") != JUCI_VERSION) {
std::stringstream ss;
ss << default_config_file;
boost::property_tree::read_json(ss, default_cfg);
cfg_ok = false;
auto it_version = cfg.find("version");
if(it_version != cfg.not_found()) {
make_version_dependent_corrections(cfg, default_cfg, it_version->second.data());
it_version->second.data() = JUCI_VERSION;
}
auto style_path = home_juci_path / "styles";
filesystem::write(style_path / "juci-light.xml", juci_light_style);
filesystem::write(style_path / "juci-dark.xml", juci_dark_style);
filesystem::write(style_path / "juci-dark-blue.xml", juci_dark_blue_style);
}
else
return;
cfg_ok &= add_missing_nodes(cfg, default_cfg);
cfg_ok &= remove_deprecated_nodes(cfg, default_cfg);
if(!cfg_ok)
boost::property_tree::write_json((home_juci_path / "config" / "config.json").string(), cfg);
}
void Config::make_version_dependent_corrections(boost::property_tree::ptree &cfg, const boost::property_tree::ptree &default_cfg, const std::string &version) {
auto &keybindings_cfg = cfg.get_child("keybindings");
try {
if(version <= "1.2.4") {
auto it_file_print = keybindings_cfg.find("print");
if(it_file_print != keybindings_cfg.not_found() && it_file_print->second.data() == "<primary>p") {
dispatcher.post([] {
::Terminal::get().print("Preference change: keybindings.print set to \"\"\n");
});
it_file_print->second.data() = "";
}
}
}
catch(const std::exception &e) {
std::cerr << "Error correcting preferences: " << e.what() << std::endl;
}
}
bool Config::add_missing_nodes(boost::property_tree::ptree &cfg, const boost::property_tree::ptree &default_cfg, std::string parent_path) {
if(parent_path.size() > 0)
parent_path += ".";
bool unchanged = true;
for(auto &node : default_cfg) {
auto path = parent_path + node.first;
try {
cfg.get<std::string>(path);
}
catch(const std::exception &e) {
cfg.add(path, node.second.data());
unchanged = false;
}
unchanged &= add_missing_nodes(cfg, node.second, path);
}
return unchanged;
}
bool Config::remove_deprecated_nodes(boost::property_tree::ptree &cfg, const boost::property_tree::ptree &default_cfg, std::string parent_path) {
if(parent_path.size() > 0)
parent_path += ".";
bool unchanged = true;
for(auto it = cfg.begin(); it != cfg.end();) {
auto path = parent_path + it->first;
try {
default_cfg.get<std::string>(path);
unchanged &= remove_deprecated_nodes(it->second, default_cfg, path);
++it;
}
catch(const std::exception &e) {
it = cfg.erase(it);
unchanged = false;
}
}
return unchanged;
}
void Config::read(const boost::property_tree::ptree &cfg) {
auto keybindings_pt = cfg.get_child("keybindings");
for(auto &i : keybindings_pt) {
menu.keys[i.first] = i.second.get_value<std::string>();
}
auto source_json = cfg.get_child("source");
source.style = source_json.get<std::string>("style");
source.font = source_json.get<std::string>("font");
source.cleanup_whitespace_characters = source_json.get<bool>("cleanup_whitespace_characters");
source.show_whitespace_characters = source_json.get<std::string>("show_whitespace_characters");
source.format_style_on_save = source_json.get<bool>("format_style_on_save");
source.format_style_on_save_if_style_file_found = source_json.get<bool>("format_style_on_save_if_style_file_found");
source.smart_brackets = source_json.get<bool>("smart_brackets");
source.smart_inserts = source_json.get<bool>("smart_inserts");
if(source.smart_inserts)
source.smart_brackets = true;
source.show_map = source_json.get<bool>("show_map");
source.map_font_size = source_json.get<std::string>("map_font_size");
source.show_git_diff = source_json.get<bool>("show_git_diff");
source.show_background_pattern = source_json.get<bool>("show_background_pattern");
source.show_right_margin = source_json.get<bool>("show_right_margin");
source.right_margin_position = source_json.get<unsigned>("right_margin_position");
source.spellcheck_language = source_json.get<std::string>("spellcheck_language");
source.default_tab_char = source_json.get<char>("default_tab_char");
source.default_tab_size = source_json.get<unsigned>("default_tab_size");
source.auto_tab_char_and_size = source_json.get<bool>("auto_tab_char_and_size");
source.tab_indents_line = source_json.get<bool>("tab_indents_line");
source.wrap_lines = source_json.get<bool>("wrap_lines");
source.highlight_current_line = source_json.get<bool>("highlight_current_line");
source.show_line_numbers = source_json.get<bool>("show_line_numbers");
source.enable_multiple_cursors = source_json.get<bool>("enable_multiple_cursors");
source.auto_reload_changed_files = source_json.get<bool>("auto_reload_changed_files");
source.clang_format_style = source_json.get<std::string>("clang_format_style");
source.clang_usages_threads = static_cast<unsigned>(source_json.get<int>("clang_usages_threads"));
auto pt_doc_search = cfg.get_child("documentation_searches");
for(auto &pt_doc_search_lang : pt_doc_search) {
source.documentation_searches[pt_doc_search_lang.first].separator = pt_doc_search_lang.second.get<std::string>("separator");
auto &queries = source.documentation_searches.find(pt_doc_search_lang.first)->second.queries;
for(auto &i : pt_doc_search_lang.second.get_child("queries")) {
queries[i.first] = i.second.get_value<std::string>();
}
}
window.theme_name = cfg.get<std::string>("gtk_theme.name");
window.theme_variant = cfg.get<std::string>("gtk_theme.variant");
window.version = cfg.get<std::string>("version");
project.default_build_path = cfg.get<std::string>("project.default_build_path");
project.debug_build_path = cfg.get<std::string>("project.debug_build_path");
project.cmake.command = cfg.get<std::string>("project.cmake.command");
project.cmake.compile_command = cfg.get<std::string>("project.cmake.compile_command");
project.meson.command = cfg.get<std::string>("project.meson.command");
project.meson.compile_command = cfg.get<std::string>("project.meson.compile_command");
project.save_on_compile_or_run = cfg.get<bool>("project.save_on_compile_or_run");
project.clear_terminal_on_compile = cfg.get<bool>("project.clear_terminal_on_compile");
project.ctags_command = cfg.get<std::string>("project.ctags_command");
project.python_command = cfg.get<std::string>("project.python_command");
terminal.history_size = cfg.get<int>("terminal.history_size");
terminal.font = cfg.get<std::string>("terminal.font");
log.libclang = cfg.get<bool>("log.libclang");
log.language_server = cfg.get<bool>("log.language_server");
}