#include "config.hpp" #include "files.hpp" #include "filesystem.hpp" #include "terminal.hpp" #include #include #include Config::Config() { const auto home_path = filesystem::get_home_path(); if(std::getenv("XDG_RUNTIME_DIR") != nullptr) { if(auto ptr = std::getenv("XDG_CONFIG_HOME")) { boost::filesystem::path config_dir(ptr); home_juci_path = config_dir / "juci"; } else { home_juci_path = home_path / ".config" / "juci"; } juci_config_file = home_juci_path / "config.json"; return; } if(home_path.empty()) throw std::runtime_error("Could not find home path"); home_juci_path = home_path / ".juci"; juci_config_file = home_juci_path / "config.json"; } void Config::load() { try { const auto old_config_file_path = filesystem::get_home_path() / ".juci" / "config" / "config.json"; if(juci_config_file != old_config_file_path && boost::filesystem::exists(old_config_file_path)) { boost::filesystem::rename(old_config_file_path, old_config_file_path.parent_path().parent_path() / "config.json"); const auto old_config_directory_path = old_config_file_path.parent_path(); if(boost::filesystem::is_empty(old_config_directory_path)) { boost::filesystem::remove_all(old_config_directory_path); } const std::string command("mv " + old_config_directory_path.parent_path().string() + " " + juci_config_file.parent_path().string()); if(TinyProcessLib::Process(command).get_exit_status() == 0) { boost::filesystem::remove_all(old_config_directory_path); } } boost::filesystem::create_directories(home_juci_path); if(!boost::filesystem::exists(juci_config_file)) filesystem::write(juci_config_file, default_config_file); auto juci_style_path = home_juci_path / "styles"; boost::filesystem::create_directories(juci_style_path); 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); boost::property_tree::ptree cfg; boost::property_tree::json_parser::read_json(juci_config_file.string(), cfg); update(cfg); read(cfg); } catch(const std::exception &e) { dispatcher.post([config_json = juci_config_file, e_what = std::string(e.what())] { ::Terminal::get().print("\e[31mError\e[m: could not parse " + config_json.string() + ": " + e_what + "\n", true); }); std::stringstream ss; ss << default_config_file; boost::property_tree::ptree cfg; boost::property_tree::read_json(ss, cfg); read(cfg); } } void Config::update(boost::property_tree::ptree &cfg) { boost::property_tree::ptree default_cfg; bool cfg_ok = true; if(cfg.get("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(juci_config_file.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() == "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(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(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(); } auto source_json = cfg.get_child("source"); source.style = source_json.get("style"); source.font = source_json.get("font"); source.cleanup_whitespace_characters = source_json.get("cleanup_whitespace_characters"); source.show_whitespace_characters = source_json.get("show_whitespace_characters"); source.format_style_on_save = source_json.get("format_style_on_save"); source.format_style_on_save_if_style_file_found = source_json.get("format_style_on_save_if_style_file_found"); source.smart_brackets = source_json.get("smart_brackets"); source.smart_inserts = source_json.get("smart_inserts"); if(source.smart_inserts) source.smart_brackets = true; source.show_map = source_json.get("show_map"); source.map_font_size = source_json.get("map_font_size"); source.show_git_diff = source_json.get("show_git_diff"); source.show_background_pattern = source_json.get("show_background_pattern"); source.show_right_margin = source_json.get("show_right_margin"); source.right_margin_position = source_json.get("right_margin_position"); source.spellcheck_language = source_json.get("spellcheck_language"); source.default_tab_char = source_json.get("default_tab_char"); source.default_tab_size = source_json.get("default_tab_size"); source.auto_tab_char_and_size = source_json.get("auto_tab_char_and_size"); source.tab_indents_line = source_json.get("tab_indents_line"); source.word_wrap = source_json.get("word_wrap"); source.highlight_current_line = source_json.get("highlight_current_line"); source.show_line_numbers = source_json.get("show_line_numbers"); source.enable_multiple_cursors = source_json.get("enable_multiple_cursors"); source.auto_reload_changed_files = source_json.get("auto_reload_changed_files"); source.search_for_selection = source_json.get("search_for_selection"); source.clang_format_style = source_json.get("clang_format_style"); source.clang_usages_threads = static_cast(source_json.get("clang_usages_threads")); source.clang_tidy_enable = source_json.get("clang_tidy_enable"); source.clang_tidy_checks = source_json.get("clang_tidy_checks"); source.clang_detailed_preprocessing_record = source_json.get("clang_detailed_preprocessing_record"); source.debug_place_cursor_at_stop = source_json.get("debug_place_cursor_at_stop"); 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("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(); } } version = cfg.get("version"); theme.name = cfg.get("gtk_theme.name"); theme.variant = cfg.get("gtk_theme.variant"); theme.font = cfg.get("gtk_theme.font"); project.default_build_path = cfg.get("project.default_build_path"); project.debug_build_path = cfg.get("project.debug_build_path"); project.cmake.command = cfg.get("project.cmake.command"); project.cmake.compile_command = cfg.get("project.cmake.compile_command"); project.meson.command = cfg.get("project.meson.command"); project.meson.compile_command = cfg.get("project.meson.compile_command"); project.default_build_management_system = cfg.get("project.default_build_management_system"); project.save_on_compile_or_run = cfg.get("project.save_on_compile_or_run"); project.ctags_command = cfg.get("project.ctags_command"); project.grep_command = cfg.get("project.grep_command"); project.cargo_command = cfg.get("project.cargo_command"); project.python_command = cfg.get("project.python_command"); project.markdown_command = cfg.get("project.markdown_command"); terminal.history_size = cfg.get("terminal.history_size"); terminal.font = cfg.get("terminal.font"); terminal.clear_on_compile = cfg.get("terminal.clear_on_compile"); terminal.clear_on_run_command = cfg.get("terminal.clear_on_run_command"); terminal.hide_entry_on_run_command = cfg.get("terminal.hide_entry_on_run_command"); log.libclang = cfg.get("log.libclang"); log.language_server = cfg.get("log.language_server"); }