Skip to content
7 changes: 7 additions & 0 deletions src/rime/config/config_component.h
Original file line number Diff line number Diff line change
Expand Up @@ -117,6 +117,13 @@ class ConfigComponent : public ConfigComponentBase {
ResourceProvider::kDefaultResourceType)) {
setup(&loader_);
}
ConfigComponent(function<void(Loader* loader)> setup,
const string& loader_dir)
: ConfigComponentBase(
new ResourceResolver(ResourceProvider::kDefaultResourceType)) {
resource_resolver_->set_root_path(path(loader_dir));
setup(&loader_);
}

private:
an<ConfigData> LoadConfig(const string& config_id) override {
Expand Down
40 changes: 37 additions & 3 deletions src/rime/config/config_data.cc
Original file line number Diff line number Diff line change
Expand Up @@ -86,13 +86,47 @@ bool ConfigData::SaveToFile(const path& file_path) {
file_path_ = file_path;
modified_ = false;
if (file_path.empty()) {
// not really saving
LOG(ERROR) << "SaveToFile: file path is empty";
return false;
}
LOG(INFO) << "saving config file '" << file_path << "'.";
// dump tree

// check if root node exists
if (!root) {
LOG(WARNING) << "SaveToFile: root node is null, creating empty map";
root = New<ConfigMap>();
}

// ensure parent directory exists
auto parent_dir = file_path.parent_path();
if (!parent_dir.empty() && !std::filesystem::exists(parent_dir)) {
LOG(INFO) << "SaveToFile: creating parent directory: " << parent_dir;
try {
std::filesystem::create_directories(parent_dir);
} catch (const std::exception& e) {
LOG(ERROR) << "SaveToFile: failed to create directory " << parent_dir
<< ": " << e.what();
return false;
}
}

// try to open file
std::ofstream out(file_path.c_str());
return SaveToStream(out);
if (!out.good()) {
LOG(ERROR) << "SaveToFile: failed to open file for writing: " << file_path;
return false;
}

bool result = SaveToStream(out);
out.close();

if (result) {
LOG(INFO) << "SaveToFile: successfully saved to " << file_path;
} else {
LOG(ERROR) << "SaveToFile: failed to save to " << file_path;
}

return result;
}

bool ConfigData::IsListItemReference(const string& key) {
Expand Down
1 change: 1 addition & 0 deletions src/rime/config/plugins.h
Original file line number Diff line number Diff line change
Expand Up @@ -58,6 +58,7 @@ struct ResourceType;
class SaveOutputPlugin : public ConfigCompilerPlugin {
public:
SaveOutputPlugin();
SaveOutputPlugin(const string& output_dir);
virtual ~SaveOutputPlugin();

Review ReviewCompileOutput;
Expand Down
33 changes: 32 additions & 1 deletion src/rime/config/save_output_plugin.cc
Original file line number Diff line number Diff line change
Expand Up @@ -2,6 +2,7 @@
// Copyright RIME Developers
// Distributed under the BSD License
//
#include <filesystem>
#include <rime/resource.h>
#include <rime/service.h>
#include <rime/config/config_compiler.h>
Expand All @@ -16,6 +17,11 @@ SaveOutputPlugin::SaveOutputPlugin()
: resource_resolver_(
Service::instance().CreateStagingResourceResolver(kCompiledConfig)) {}

SaveOutputPlugin::SaveOutputPlugin(const string& output_dir)
: resource_resolver_(new ResourceResolver(kCompiledConfig)) {
resource_resolver_->set_root_path(path(output_dir));
}

SaveOutputPlugin::~SaveOutputPlugin() {}

bool SaveOutputPlugin::ReviewCompileOutput(ConfigCompiler* compiler,
Expand All @@ -25,8 +31,33 @@ bool SaveOutputPlugin::ReviewCompileOutput(ConfigCompiler* compiler,

bool SaveOutputPlugin::ReviewLinkOutput(ConfigCompiler* compiler,
an<ConfigResource> resource) {
LOG(INFO) << "SaveOutputPlugin::ReviewLinkOutput for resource: "
<< resource->resource_id;

auto file_path = resource_resolver_->ResolvePath(resource->resource_id);
return resource->data->SaveToFile(file_path);
LOG(INFO) << "Attempting to save to: " << file_path;

// ensure directory exists
auto parent_dir = file_path.parent_path();
if (!std::filesystem::exists(parent_dir)) {
LOG(INFO) << "Creating directory: " << parent_dir;
try {
std::filesystem::create_directories(parent_dir);
} catch (const std::exception& e) {
LOG(ERROR) << "Failed to create directory " << parent_dir << ": "
<< e.what();
return false;
}
}

bool result = resource->data->SaveToFile(file_path);
if (result) {
LOG(INFO) << "Successfully saved config to: " << file_path;
} else {
LOG(ERROR) << "Failed to save config to: " << file_path;
}

return result;
}

} // namespace rime
38 changes: 33 additions & 5 deletions src/rime/resource.cc
Original file line number Diff line number Diff line change
Expand Up @@ -9,13 +9,41 @@

namespace rime {

// Convert a file path to a resource ID by extracting the core name and
// directory structure
string ResourceResolver::ToResourceId(const string& file_path) const {
string string_path = path(file_path).generic_u8string();
bool has_prefix = boost::starts_with(string_path, type_.prefix);
bool has_suffix = boost::ends_with(string_path, type_.suffix);
path p(file_path);
// Get the filename from the path
string filename = p.filename().generic_u8string();

// Check if the filename has the expected prefix and suffix
bool has_prefix = boost::starts_with(filename, type_.prefix);
bool has_suffix = boost::ends_with(filename, type_.suffix);

// Calculate the start and end positions to extract the core name
size_t start = (has_prefix ? type_.prefix.length() : 0);
size_t end = string_path.length() - (has_suffix ? type_.suffix.length() : 0);
return string_path.substr(start, end);
size_t end = filename.length() - (has_suffix ? type_.suffix.length() : 0);

// Handle files with parent directories
if (p.has_parent_path()) {
string parent_dir = p.parent_path().generic_u8string();
string resource_name = filename.substr(start, end);

// Remove leading slash from parent directory if present
if (!parent_dir.empty() && parent_dir[0] == '/') {
parent_dir = parent_dir.substr(1);
}

// Return either just the resource name or include the parent directory
if (parent_dir.empty()) {
return resource_name;
} else {
return parent_dir + "/" + resource_name;
}
} else {
// For files without parent paths, just return the extracted name
return filename.substr(start, end);
}
}

string ResourceResolver::ToFilePath(const string& resource_id) const {
Expand Down
4 changes: 4 additions & 0 deletions src/rime_api.h
Original file line number Diff line number Diff line change
Expand Up @@ -505,6 +505,10 @@ typedef struct RIME_FLAVORED(rime_api_t) {
size_t index);

Bool (*change_page)(RimeSessionId session_id, Bool backward);

Bool (*compile_config_file)(const char* src_path,
const char* dest_path,
const char* file_name);
} RIME_FLAVORED(RimeApi);

//! API entry
Expand Down
44 changes: 44 additions & 0 deletions src/rime_api_impl.h
Original file line number Diff line number Diff line change
Expand Up @@ -17,6 +17,8 @@
#include <rime/setup.h>
#include <rime/signature.h>
#include <rime/switches.h>
#include <rime/config.h>
#include <rime/config/plugins.h>

using namespace rime;

Expand Down Expand Up @@ -135,6 +137,47 @@ RIME_DEPRECATED Bool RimeDeployConfigFile(const char* file_name,
return Bool(deployer.RunTask("config_file_update", args));
}

RIME_DEPRECATED Bool RimeCompileConfigFile(const char* src_path,
const char* dest_path,
const char* file_name) {
// Ensure destination directory exists
std::filesystem::path dest_dir(dest_path);
if (!std::filesystem::exists(dest_dir)) {
std::filesystem::create_directories(dest_dir);
LOG(INFO) << "Created destination directory: " << dest_path;
}

// Create config builder
auto config_builder = new ConfigComponent<ConfigBuilder>(
[&](ConfigBuilder* builder) {
builder->InstallPlugin(new AutoPatchConfigPlugin);
builder->InstallPlugin(new DefaultConfigPlugin);
builder->InstallPlugin(new LegacyPresetConfigPlugin);
builder->InstallPlugin(new LegacyDictionaryConfigPlugin);
builder->InstallPlugin(new BuildInfoPlugin);
builder->InstallPlugin(new SaveOutputPlugin(dest_path));
},
src_path);

// Compile file
LOG(INFO) << "Compiling YAML file: " << file_name;
LOG(INFO) << "Source path: " << src_path;
LOG(INFO) << "Destination path: " << dest_path;

Config* config = config_builder->Create(file_name);
bool result = (config != nullptr);

if (result) {
LOG(INFO) << "✓ Compilation successful!";
} else {
LOG(ERROR) << "✗ Compilation failed!";
}

delete config;
delete config_builder;
return result;
}

RIME_DEPRECATED Bool RimeSyncUserData() {
Service::instance().CleanupAllSessions();
Deployer& deployer(Service::instance().deployer());
Expand Down Expand Up @@ -1228,6 +1271,7 @@ RIME_API RIME_FLAVORED(RimeApi) * RIME_FLAVORED(rime_get_api)() {
s_api.highlight_candidate_on_current_page =
&RimeHighlightCandidateOnCurrentPage;
s_api.change_page = &RimeChangePage;
s_api.compile_config_file = &RimeCompileConfigFile;
}
return &s_api;
}
10 changes: 10 additions & 0 deletions tools/CMakeLists.txt
Original file line number Diff line number Diff line change
Expand Up @@ -37,9 +37,19 @@ target_link_libraries(rime_patch
${rime_library}
${rime_levers_library})

set(rime_yaml_compiler_src "rime_yaml_compiler.cc")
add_executable(rime_yaml_compiler ${rime_yaml_compiler_src})
target_include_directories(rime_yaml_compiler BEFORE PRIVATE
${PROJECT_SOURCE_DIR}/src
${PROJECT_BINARY_DIR}/src)
target_link_libraries(rime_yaml_compiler
${rime_library}
${rime_gears_library})

install(TARGETS rime_deployer DESTINATION ${BIN_INSTALL_DIR})
install(TARGETS rime_dict_manager DESTINATION ${BIN_INSTALL_DIR})
install(TARGETS rime_patch DESTINATION ${BIN_INSTALL_DIR})
install(TARGETS rime_yaml_compiler DESTINATION ${BIN_INSTALL_DIR})

# do not work with Windows DLL; interfaces to dict are missing DLL export.
if(NOT WIN32 OR NOT BUILD_SHARED_LIBS)
Expand Down
112 changes: 112 additions & 0 deletions tools/rime_yaml_compiler.cc
Original file line number Diff line number Diff line change
@@ -0,0 +1,112 @@
/*
* rime_yaml_compiler.cc
* RIME YAML 编译工具
* 用于编译自定义的 YAML 配置文件
*/
#include <iostream>
#include <string>
#include <filesystem>
#include <rime/config.h>
#include <rime/config/plugins.h>
#include <rime/schema.h>
#include "codepage.h"
#include <rime_api.h>

using namespace rime;
using namespace std;

void print_usage(const char* program_name) {
cout << "用法: " << program_name << " [选项] <YAML文件路径>" << endl;
cout << "选项:" << endl;
cout << " -s, --source <路径> 源路径 (默认: 当前目录)" << endl;
cout << " -d, --dest <路径> 目标路径 (默认: 当前目录)" << endl;
cout << " -h, --help 显示此帮助信息" << endl;
cout << endl;
cout << "示例:" << endl;
cout << " " << program_name << " theme/bim.yaml" << endl;
cout << " " << program_name
<< " -s /path/to/source -d /path/to/dest config.yaml" << endl;
}
int main(int argc, char* argv[]) {
unsigned int codepage = SetConsoleOutputCodePage();

string src_path = ".";
string dest_path = ".";
string file_path;

// 解析命令行参数
for (int i = 1; i < argc; i++) {
string arg = argv[i];

if (arg == "-h" || arg == "--help") {
print_usage(argv[0]);
SetConsoleOutputCodePage(codepage);
return 0;
} else if (arg == "-s" || arg == "--source") {
if (i + 1 < argc) {
src_path = argv[++i];
} else {
cerr << "错误: " << arg << " 需要一个参数" << endl;
SetConsoleOutputCodePage(codepage);
return 1;
}
} else if (arg == "-d" || arg == "--dest") {
if (i + 1 < argc) {
dest_path = argv[++i];
} else {
cerr << "错误: " << arg << " 需要一个参数" << endl;
SetConsoleOutputCodePage(codepage);
return 1;
}
} else if (arg[0] != '-') {
// 这是文件路径
if (file_path.empty()) {
file_path = arg;
} else {
cerr << "错误: 指定了多个文件路径" << endl;
SetConsoleOutputCodePage(codepage);
return 1;
}
} else {
cerr << "错误: 未知选项 " << arg << endl;
print_usage(argv[0]);
SetConsoleOutputCodePage(codepage);
return 1;
}
}

// 检查是否指定了文件路径
if (file_path.empty()) {
cerr << "错误: 必须指定 YAML 文件路径" << endl;
print_usage(argv[0]);
SetConsoleOutputCodePage(codepage);
return 1;
}

// 检查文件是否存在
string full_file_path = src_path + "/" + file_path;
if (!filesystem::exists(full_file_path)) {
cerr << "错误: 文件不存在: " << full_file_path << endl;
SetConsoleOutputCodePage(codepage);
return 1;
}

cout << "=== RIME YAML 编译器 ===" << endl;

// 编译文件
// bool success = compile_yaml_file(src_path, dest_path, file_path);
RimeApi* rime = rime_get_api();

// 检查API版本并验证compile_config_file函数是否可用
if (!RIME_API_AVAILABLE(rime, compile_config_file)) {
cerr << "错误: 当前版本的 librime 不支持 compile_config_file 函数" << endl;
SetConsoleOutputCodePage(codepage);
return 1;
}

bool success = rime->compile_config_file(src_path.c_str(), dest_path.c_str(),
file_path.c_str());

SetConsoleOutputCodePage(codepage);
return success ? 0 : 1;
}
Loading