Skip to content

Commit a7d253e

Browse files
committed
feat(config): config compiler plugins that port legacy features to the new YAML syntax
1 parent 4ecae44 commit a7d253e

9 files changed

+248
-4
lines changed
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,42 @@
1+
//
2+
// Copyright RIME Developers
3+
// Distributed under the BSD License
4+
//
5+
#include <boost/algorithm/string.hpp>
6+
#include <rime/config/config_compiler_impl.h>
7+
#include <rime/config/plugins.h>
8+
9+
namespace rime {
10+
11+
static string remove_suffix(const string& input, const string& suffix) {
12+
return boost::ends_with(input, suffix) ?
13+
input.substr(0, input.length() - suffix.length()) : input;
14+
}
15+
16+
// auto-patch applies to all loaded config resources, including dependencies.
17+
// therefore it's done at the end of Compile phase.
18+
bool AutoPatchConfigPlugin::ReviewCompileOutput(ConfigCompiler* compiler,
19+
an<ConfigResource> resource) {
20+
if (boost::ends_with(resource->resource_id, ".custom"))
21+
return true;
22+
// skip auto-patch if there is already an explicit `__patch` at the root node
23+
auto root_deps = compiler->GetDependencies(resource->resource_id + ":");
24+
if (!root_deps.empty() && root_deps.back()->priority() >= kPatch)
25+
return true;
26+
auto patch_resource_id =
27+
remove_suffix(resource->resource_id, ".schema") + ".custom";
28+
LOG(INFO) << "auto-patch " << resource->resource_id << ":/__patch: "
29+
<< patch_resource_id << ":/patch?";
30+
compiler->Push(resource);
31+
compiler->AddDependency(
32+
New<PatchReference>(Reference{patch_resource_id, "patch", true}));
33+
compiler->Pop();
34+
return true;
35+
}
36+
37+
bool AutoPatchConfigPlugin::ReviewLinkOutput(ConfigCompiler* compiler,
38+
an<ConfigResource> resource) {
39+
return true;
40+
}
41+
42+
} // namespace rime

src/rime/config/config_compiler.cc

+8-2
Original file line numberDiff line numberDiff line change
@@ -5,6 +5,7 @@
55
#include <rime/config/config_compiler_impl.h>
66
#include <rime/config/config_data.h>
77
#include <rime/config/config_types.h>
8+
#include <rime/config/plugins.h>
89

910
namespace rime {
1011

@@ -243,8 +244,10 @@ void ConfigDependencyGraph::Add(an<Dependency> dependency) {
243244
}
244245
}
245246

246-
ConfigCompiler::ConfigCompiler(ResourceResolver* resource_resolver)
247+
ConfigCompiler::ConfigCompiler(ResourceResolver* resource_resolver,
248+
ConfigCompilerPlugin* plugin)
247249
: resource_resolver_(resource_resolver),
250+
plugin_(plugin),
248251
graph_(new ConfigDependencyGraph) {
249252
}
250253

@@ -303,6 +306,8 @@ an<ConfigResource> ConfigCompiler::Compile(const string& file_name) {
303306
resource->loaded = resource->data->LoadFromFile(
304307
resource_resolver_->ResolvePath(resource_id).string(), this);
305308
Pop();
309+
if (plugin_)
310+
plugin_->ReviewCompileOutput(this, resource);
306311
return resource;
307312
}
308313

@@ -483,7 +488,8 @@ bool ConfigCompiler::Link(an<ConfigResource> target) {
483488
LOG(ERROR) << "resource not found: " << target->resource_id;
484489
return false;
485490
}
486-
return ResolveDependencies(found->first + ":");
491+
return ResolveDependencies(found->first + ":") &&
492+
(plugin_ ? plugin_->ReviewLinkOutput(this, target) : true);
487493
}
488494

489495
bool ConfigCompiler::ResolveDependencies(const string& path) {

src/rime/config/config_compiler.h

+4-1
Original file line numberDiff line numberDiff line change
@@ -35,6 +35,7 @@ struct Reference {
3535
string repr() const;
3636
};
3737

38+
class ConfigCompilerPlugin;
3839
class ResourceResolver;
3940
struct Dependency;
4041
struct ConfigDependencyGraph;
@@ -46,7 +47,8 @@ class ConfigCompiler {
4647
static constexpr const char* APPEND_DIRECTIVE = "__append";
4748
static constexpr const char* MERGE_DIRECTIVE = "__merge";
4849

49-
explicit ConfigCompiler(ResourceResolver* resource_resolver);
50+
ConfigCompiler(ResourceResolver* resource_resolver,
51+
ConfigCompilerPlugin* plugin);
5052
virtual ~ConfigCompiler();
5153

5254
Reference CreateReference(const string& qualified_path);
@@ -69,6 +71,7 @@ class ConfigCompiler {
6971

7072
private:
7173
ResourceResolver* resource_resolver_;
74+
ConfigCompilerPlugin* plugin_;
7275
the<ConfigDependencyGraph> graph_;
7376
};
7477

src/rime/config/config_component.cc

+43-1
Original file line numberDiff line numberDiff line change
@@ -11,6 +11,7 @@
1111
#include <rime/config/config_compiler.h>
1212
#include <rime/config/config_data.h>
1313
#include <rime/config/config_types.h>
14+
#include <rime/config/plugins.h>
1415

1516
namespace rime {
1617

@@ -160,12 +161,53 @@ Config* ConfigComponent::Create(const string& file_name) {
160161
return new Config(GetConfigData(file_name));
161162
}
162163

164+
void ConfigComponent::InstallPlugin(ConfigCompilerPlugin* plugin) {
165+
plugins_.push_back(the<ConfigCompilerPlugin>(plugin));
166+
}
167+
168+
template <class Container>
169+
struct MultiplePlugins : ConfigCompilerPlugin {
170+
Container& plugins;
171+
172+
MultiplePlugins(Container& _plugins)
173+
: plugins(_plugins) {
174+
}
175+
bool ReviewCompileOutput(ConfigCompiler* compiler,
176+
an<ConfigResource> resource) override {
177+
return ReviewedByAll(&ConfigCompilerPlugin::ReviewCompileOutput,
178+
compiler, resource);
179+
}
180+
bool ReviewLinkOutput(ConfigCompiler* compiler,
181+
an<ConfigResource> resource) override {
182+
return ReviewedByAll(&ConfigCompilerPlugin::ReviewLinkOutput,
183+
compiler, resource);
184+
}
185+
186+
typedef bool (ConfigCompilerPlugin::*Reviewer)(ConfigCompiler* compiler,
187+
an<ConfigResource> resource);
188+
bool ReviewedByAll(Reviewer reviewer,
189+
ConfigCompiler* compiler,
190+
an<ConfigResource> resource);
191+
};
192+
193+
template <class Container>
194+
bool MultiplePlugins<Container>::ReviewedByAll(Reviewer reviewer,
195+
ConfigCompiler* compiler,
196+
an<ConfigResource> resource) {
197+
for (const auto& plugin : plugins) {
198+
if(!((*plugin).*reviewer)(compiler, resource))
199+
return false;
200+
}
201+
return true;
202+
}
203+
163204
an<ConfigData> ConfigComponent::GetConfigData(const string& file_name) {
164205
auto config_id = resource_resolver_->ToResourceId(file_name);
165206
// keep a weak reference to the shared config data in the component
166207
weak<ConfigData>& wp(cache_[config_id]);
167208
if (wp.expired()) { // create a new copy and load it
168-
ConfigCompiler compiler(resource_resolver_.get());
209+
MultiplePlugins<decltype(plugins_)> multiple_plugins(plugins_);
210+
ConfigCompiler compiler(resource_resolver_.get(), &multiple_plugins);
169211
auto resource = compiler.Compile(file_name);
170212
if (resource->loaded && !compiler.Link(resource)) {
171213
LOG(ERROR) << "error loading config from: " << file_name;

src/rime/config/config_component.h

+6
Original file line numberDiff line numberDiff line change
@@ -63,18 +63,24 @@ class Config : public Class<Config, const string&>, public ConfigItemRef {
6363
void SetItem(an<ConfigItem> item);
6464
};
6565

66+
class ConfigCompiler;
67+
class ConfigCompilerPlugin;
6668
class ResourceResolver;
69+
struct ConfigResource;
6770

6871
class ConfigComponent : public Config::Component {
6972
public:
7073
ConfigComponent();
7174
~ConfigComponent();
7275
Config* Create(const string& file_name);
76+
void InstallPlugin(ConfigCompilerPlugin *plugin);
77+
bool ApplyPlugins(ConfigCompiler* compiler, an<ConfigResource> resource);
7378

7479
private:
7580
an<ConfigData> GetConfigData(const string& file_name);
7681
map<string, weak<ConfigData>> cache_;
7782
the<ResourceResolver> resource_resolver_;
83+
vector<the<ConfigCompilerPlugin>> plugins_;
7884
};
7985

8086
} // namespace rime
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,22 @@
1+
//
2+
// Copyright RIME Developers
3+
// Distributed under the BSD License
4+
//
5+
#include <rime/config/config_compiler_impl.h>
6+
#include <rime/config/plugins.h>
7+
8+
namespace rime {
9+
10+
bool LegacyDictionaryConfigPlugin::ReviewCompileOutput(
11+
ConfigCompiler* compiler, an<ConfigResource> resource) {
12+
// TODO: unimplemented
13+
return true;
14+
}
15+
16+
bool LegacyDictionaryConfigPlugin::ReviewLinkOutput(
17+
ConfigCompiler* compiler, an<ConfigResource> resource) {
18+
// TODO: unimplemented
19+
return true;
20+
}
21+
22+
} // namespace rime
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,75 @@
1+
//
2+
// Copyright RIME Developers
3+
// Distributed under the BSD License
4+
//
5+
#include <boost/algorithm/string.hpp>
6+
#include <rime/config/config_compiler_impl.h>
7+
#include <rime/config/config_cow_ref.h>
8+
#include <rime/config/plugins.h>
9+
10+
namespace rime {
11+
12+
bool LegacyPresetConfigPlugin::ReviewCompileOutput(
13+
ConfigCompiler* compiler, an<ConfigResource> resource) {
14+
return true;
15+
}
16+
17+
bool LegacyPresetConfigPlugin::ReviewLinkOutput(
18+
ConfigCompiler* compiler, an<ConfigResource> resource) {
19+
if (!boost::ends_with(resource->resource_id, ".schema"))
20+
return true;
21+
if (auto preset = resource->data->Traverse("key_binder/import_preset")) {
22+
if (!Is<ConfigValue>(preset))
23+
return false;
24+
auto preset_config_id = As<ConfigValue>(preset)->str();
25+
LOG(INFO) << "interpreting key_binder/import_preset: " << preset_config_id;
26+
auto target = Cow(resource, "key_binder");
27+
auto map = As<ConfigMap>(**target);
28+
if (map && map->HasKey("bindings")) {
29+
// append to included list `key_binder/bindings/+` instead of overwriting
30+
auto appended = map->Get("bindings");
31+
*Cow(target, "bindings/+") = appended;
32+
// `*target` is already referencing a copied map, safe to edit directly
33+
(*target)["bindings"] = nullptr;
34+
}
35+
Reference reference{preset_config_id, "key_binder", false};
36+
if (!IncludeReference{reference}
37+
.TargetedAt(target).Resolve(compiler)) {
38+
LOG(ERROR) << "failed to include section " << reference;
39+
return false;
40+
}
41+
}
42+
// NOTE: in the following cases, Cow() is not strictly necessary because
43+
// we know for sure that no other ConfigResource is going to reference the
44+
// root map node that will be modified. But other than the root node of the
45+
// resource being linked, it's possbile a map or list has multiple references
46+
// in the node tree, therefore Cow() is recommended to make sure the
47+
// modifications only happen to one place.
48+
if (auto preset = resource->data->Traverse("punctuator/import_preset")) {
49+
if (!Is<ConfigValue>(preset))
50+
return false;
51+
auto preset_config_id = As<ConfigValue>(preset)->str();
52+
LOG(INFO) << "interpreting punctuator/import_preset: " << preset_config_id;
53+
Reference reference{preset_config_id, "punctuator", false};
54+
if (!IncludeReference{reference}
55+
.TargetedAt(Cow(resource, "punctuator")).Resolve(compiler)) {
56+
LOG(ERROR) << "failed to include section " << reference;
57+
return false;
58+
}
59+
}
60+
if (auto preset = resource->data->Traverse("recognizer/import_preset")) {
61+
if (!Is<ConfigValue>(preset))
62+
return false;
63+
auto preset_config_id = As<ConfigValue>(preset)->str();
64+
LOG(INFO) << "interpreting recognizer/import_preset: " << preset_config_id;
65+
Reference reference{preset_config_id, "recognizer", false};
66+
if (!IncludeReference{reference}
67+
.TargetedAt(Cow(resource, "recognizer")).Resolve(compiler)) {
68+
LOG(ERROR) << "failed to include section " << reference;
69+
return false;
70+
}
71+
}
72+
return true;
73+
}
74+
75+
} // namespace rime

src/rime/config/plugins.h

+44
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,44 @@
1+
//
2+
// Copyright RIME Developers
3+
// Distributed under the BSD License
4+
//
5+
#ifndef RIME_CONFIG_PLUGINS_H_
6+
#define RIME_CONFIG_PLUGINS_H_
7+
8+
#include <rime/common.h>
9+
10+
namespace rime {
11+
12+
class ConfigCompiler;
13+
struct ConfigResource;
14+
15+
class ConfigCompilerPlugin {
16+
public:
17+
typedef bool Review(ConfigCompiler* compiler,
18+
an<ConfigResource> resource);
19+
20+
virtual Review ReviewCompileOutput = 0;
21+
virtual Review ReviewLinkOutput = 0;
22+
};
23+
24+
class AutoPatchConfigPlugin : public ConfigCompilerPlugin {
25+
public:
26+
Review ReviewCompileOutput;
27+
Review ReviewLinkOutput;
28+
};
29+
30+
class LegacyPresetConfigPlugin : public ConfigCompilerPlugin {
31+
public:
32+
Review ReviewCompileOutput;
33+
Review ReviewLinkOutput;
34+
};
35+
36+
class LegacyDictionaryConfigPlugin : public ConfigCompilerPlugin {
37+
public:
38+
Review ReviewCompileOutput;
39+
Review ReviewLinkOutput;
40+
};
41+
42+
} // namespace rime
43+
44+
#endif // RIME_CONFIG_PLUGINS_H_

src/rime/core_module.cc

+4
Original file line numberDiff line numberDiff line change
@@ -11,6 +11,7 @@
1111

1212
// built-in components
1313
#include <rime/config.h>
14+
#include <rime/config/plugins.h>
1415
#include <rime/schema.h>
1516

1617
using namespace rime;
@@ -20,6 +21,9 @@ static void rime_core_initialize() {
2021
Registry& r = Registry::instance();
2122

2223
auto config = new ConfigComponent;
24+
config->InstallPlugin(new AutoPatchConfigPlugin);
25+
config->InstallPlugin(new LegacyPresetConfigPlugin);
26+
config->InstallPlugin(new LegacyDictionaryConfigPlugin);
2327
r.Register("config", config);
2428
r.Register("schema", new SchemaComponent(config));
2529
}

0 commit comments

Comments
 (0)