mirror of
https://github.com/weechat/weechat.git
synced 2026-06-12 14:14:48 +02:00
core: track per-contributor overrides in theme registry
Refactor the theme registry to store one sub-table per contributor
instead of a single merged hashtable. Each registered theme now holds
a linked list of t_theme_contribution entries:
struct t_theme_contribution {
struct t_weechat_plugin *plugin; /* NULL = core */
const void *script; /* NULL for non-script */
struct t_hashtable *overrides;
...
};
Identity of a contributor is the (plugin, script) pair:
- (NULL, NULL) -> core (theme_builtin_init)
- (plugin, NULL) -> plugin-level contribution
- (plugin, script) -> individual script (filled in by next commit)
theme_register is now (plugin, script, name, overrides). It searches
the existing contributions for a matching (plugin, script) and merges
the new overrides into it; otherwise it appends a fresh contribution.
The public macro weechat_theme_register(name, overrides) still takes
two args - it now expands to pass weechat_plugin and NULL for script.
theme_apply iterates contributions in list order, calling
config_file_option_set for each entry; later contributions naturally
win for duplicate keys.
Two new internal helpers prepare for the lifecycle work in the next
two commits:
- theme_unregister_plugin (plugin): drops every contribution owned
by that plugin (with script == NULL).
- theme_unregister_script (plugin, script): drops every contribution
owned by that script.
Neither is called yet; the auto-purge wiring lands in commits 24
(plugin_unloaded signal) and 25 (script API + script-unload hook).
Other touched code:
- core-theme-builtin.c switches to theme_register (NULL, NULL, ...).
- core-command.c /theme info uses theme_overrides_count helper
instead of reaching into theme->overrides (which no longer
exists).
- WEECHAT_PLUGIN_API_VERSION bumped to 20260527-01 (function-pointer
signature change).
Two new tests cover the new semantics:
- UnregisterByOwner: registers four contributions from distinct
(plugin, script) pairs, then prunes by plugin and by script,
asserting per-contribution removal.
- RegisterMergesPerContributor: two successive register calls from
the same (plugin, script) merge into a single contribution with
later keys overriding earlier ones.
Existing tests are updated to use the new theme_register signature,
theme_overrides_count, and theme_get_override (replacing direct
access to theme->overrides->items_count and hashtable_get on
theme->overrides). No plugin or script call sites change - the
public weechat_theme_register macro keeps the same shape.
This commit is contained in:
@@ -7406,8 +7406,7 @@ COMMAND_CALLBACK(theme)
|
||||
? ptr_theme->weechat_version : "");
|
||||
gui_chat_printf (NULL,
|
||||
_(" overrides : %d"),
|
||||
(ptr_theme->overrides)
|
||||
? ptr_theme->overrides->items_count : 0);
|
||||
theme_overrides_count (ptr_theme));
|
||||
free (path);
|
||||
theme_free (file_theme);
|
||||
return WEECHAT_RC_OK;
|
||||
|
||||
@@ -111,7 +111,7 @@ theme_builtin_register_entries (const char *name,
|
||||
for (i = 0; entries[i].option; i++)
|
||||
hashtable_set (overrides, entries[i].option, entries[i].value);
|
||||
|
||||
theme_register (name, overrides);
|
||||
theme_register (NULL, NULL, name, overrides);
|
||||
|
||||
hashtable_free (overrides);
|
||||
}
|
||||
|
||||
+273
-28
@@ -116,25 +116,32 @@ theme_alloc (const char *name)
|
||||
new_theme->description = strdup ("");
|
||||
new_theme->date = theme_format_now ();
|
||||
new_theme->weechat_version = strdup (version_get_version ());
|
||||
new_theme->overrides = hashtable_new (32,
|
||||
WEECHAT_HASHTABLE_STRING,
|
||||
WEECHAT_HASHTABLE_STRING,
|
||||
NULL, NULL);
|
||||
if (!new_theme->name || !new_theme->description
|
||||
|| !new_theme->date || !new_theme->weechat_version
|
||||
|| !new_theme->overrides)
|
||||
|| !new_theme->date || !new_theme->weechat_version)
|
||||
{
|
||||
free (new_theme->name);
|
||||
free (new_theme->description);
|
||||
free (new_theme->date);
|
||||
free (new_theme->weechat_version);
|
||||
hashtable_free (new_theme->overrides);
|
||||
free (new_theme);
|
||||
return NULL;
|
||||
}
|
||||
return new_theme;
|
||||
}
|
||||
|
||||
/*
|
||||
* Frees one contribution (does not unlink from the parent theme list).
|
||||
*/
|
||||
|
||||
void
|
||||
theme_contribution_free (struct t_theme_contribution *contribution)
|
||||
{
|
||||
if (!contribution)
|
||||
return;
|
||||
hashtable_free (contribution->overrides);
|
||||
free (contribution);
|
||||
}
|
||||
|
||||
/*
|
||||
* Frees a theme (does not unlink from registry; caller handles that).
|
||||
*/
|
||||
@@ -142,13 +149,21 @@ theme_alloc (const char *name)
|
||||
void
|
||||
theme_free (struct t_theme *theme)
|
||||
{
|
||||
struct t_theme_contribution *ptr_contribution, *next_contribution;
|
||||
|
||||
if (!theme)
|
||||
return;
|
||||
ptr_contribution = theme->contributions;
|
||||
while (ptr_contribution)
|
||||
{
|
||||
next_contribution = ptr_contribution->next_contribution;
|
||||
theme_contribution_free (ptr_contribution);
|
||||
ptr_contribution = next_contribution;
|
||||
}
|
||||
free (theme->name);
|
||||
free (theme->description);
|
||||
free (theme->date);
|
||||
free (theme->weechat_version);
|
||||
hashtable_free (theme->overrides);
|
||||
free (theme);
|
||||
}
|
||||
|
||||
@@ -171,23 +186,93 @@ theme_merge_overrides_cb (void *data,
|
||||
}
|
||||
|
||||
/*
|
||||
* Registers a theme by name with a set of option overrides.
|
||||
* Searches the contribution belonging to the given (plugin, script) pair
|
||||
* in a theme's contribution list. Returns NULL if not found.
|
||||
*/
|
||||
|
||||
struct t_theme_contribution *
|
||||
theme_search_contribution (struct t_theme *theme,
|
||||
struct t_weechat_plugin *plugin,
|
||||
const void *script)
|
||||
{
|
||||
struct t_theme_contribution *ptr;
|
||||
|
||||
if (!theme)
|
||||
return NULL;
|
||||
for (ptr = theme->contributions; ptr; ptr = ptr->next_contribution)
|
||||
{
|
||||
if ((ptr->plugin == plugin) && (ptr->script == script))
|
||||
return ptr;
|
||||
}
|
||||
return NULL;
|
||||
}
|
||||
|
||||
/*
|
||||
* Allocates a new contribution and appends it to a theme's list.
|
||||
*
|
||||
* If a theme with the given name already exists, the provided overrides
|
||||
* are merged into the existing theme's hashtable (later registrations
|
||||
* override earlier ones for duplicate keys). This lets plugins/scripts
|
||||
* register their per-theme contributions without coordinating with core.
|
||||
* Returns the new contribution, NULL on error.
|
||||
*/
|
||||
|
||||
struct t_theme_contribution *
|
||||
theme_contribution_new (struct t_theme *theme,
|
||||
struct t_weechat_plugin *plugin,
|
||||
const void *script)
|
||||
{
|
||||
struct t_theme_contribution *new_contribution;
|
||||
|
||||
new_contribution = calloc (1, sizeof (*new_contribution));
|
||||
if (!new_contribution)
|
||||
return NULL;
|
||||
new_contribution->plugin = plugin;
|
||||
new_contribution->script = script;
|
||||
new_contribution->overrides = hashtable_new (32,
|
||||
WEECHAT_HASHTABLE_STRING,
|
||||
WEECHAT_HASHTABLE_STRING,
|
||||
NULL, NULL);
|
||||
if (!new_contribution->overrides)
|
||||
{
|
||||
free (new_contribution);
|
||||
return NULL;
|
||||
}
|
||||
new_contribution->prev_contribution = theme->last_contribution;
|
||||
new_contribution->next_contribution = NULL;
|
||||
if (theme->last_contribution)
|
||||
theme->last_contribution->next_contribution = new_contribution;
|
||||
else
|
||||
theme->contributions = new_contribution;
|
||||
theme->last_contribution = new_contribution;
|
||||
return new_contribution;
|
||||
}
|
||||
|
||||
/*
|
||||
* Registers a contribution to a theme.
|
||||
*
|
||||
* The "overrides" hashtable passed in is read-only from this function's
|
||||
* perspective; the caller retains ownership and may free it.
|
||||
* Identity is the (plugin, script) pair:
|
||||
* - plugin == NULL && script == NULL => core
|
||||
* - plugin != NULL && script == NULL => plugin-level
|
||||
* - plugin != NULL && script != NULL => individual script
|
||||
*
|
||||
* Returns pointer to theme (existing or newly created), NULL on error.
|
||||
* If a contribution for the same (plugin, script) already exists under
|
||||
* the named theme, the new overrides are merged into it (later keys
|
||||
* win). Otherwise a new contribution is appended. Across distinct
|
||||
* contributors, contributions are applied in list order at apply-time
|
||||
* (later contributions override earlier ones for duplicate keys).
|
||||
*
|
||||
* The "overrides" hashtable passed in is read-only here; the caller
|
||||
* retains ownership and may free it after the call.
|
||||
*
|
||||
* Returns pointer to the theme (existing or newly created), NULL on
|
||||
* error.
|
||||
*/
|
||||
|
||||
struct t_theme *
|
||||
theme_register (const char *name, struct t_hashtable *overrides)
|
||||
theme_register (struct t_weechat_plugin *plugin,
|
||||
const void *script,
|
||||
const char *name,
|
||||
struct t_hashtable *overrides)
|
||||
{
|
||||
struct t_theme *theme;
|
||||
struct t_theme_contribution *contribution;
|
||||
|
||||
if (!name || !name[0])
|
||||
return NULL;
|
||||
@@ -209,14 +294,158 @@ theme_register (const char *name, struct t_hashtable *overrides)
|
||||
|
||||
if (overrides)
|
||||
{
|
||||
contribution = theme_search_contribution (theme, plugin, script);
|
||||
if (!contribution)
|
||||
{
|
||||
contribution = theme_contribution_new (theme, plugin, script);
|
||||
if (!contribution)
|
||||
return theme; /* theme exists but contribution failed */
|
||||
}
|
||||
hashtable_map (overrides,
|
||||
&theme_merge_overrides_cb,
|
||||
theme->overrides);
|
||||
contribution->overrides);
|
||||
}
|
||||
|
||||
return theme;
|
||||
}
|
||||
|
||||
/*
|
||||
* Drops one contribution from a theme (unlinks and frees it).
|
||||
*/
|
||||
|
||||
void
|
||||
theme_drop_contribution (struct t_theme *theme,
|
||||
struct t_theme_contribution *contribution)
|
||||
{
|
||||
if (!theme || !contribution)
|
||||
return;
|
||||
if (contribution->prev_contribution)
|
||||
{
|
||||
contribution->prev_contribution->next_contribution =
|
||||
contribution->next_contribution;
|
||||
}
|
||||
else
|
||||
{
|
||||
theme->contributions = contribution->next_contribution;
|
||||
}
|
||||
if (contribution->next_contribution)
|
||||
{
|
||||
contribution->next_contribution->prev_contribution =
|
||||
contribution->prev_contribution;
|
||||
}
|
||||
else
|
||||
{
|
||||
theme->last_contribution = contribution->prev_contribution;
|
||||
}
|
||||
theme_contribution_free (contribution);
|
||||
}
|
||||
|
||||
/*
|
||||
* Drops all contributions owned by a given plugin (across every theme).
|
||||
* Called from the plugin-unload lifecycle (next commit).
|
||||
*
|
||||
* Contributions whose script is non-NULL are kept (they belong to
|
||||
* individual scripts and are cleaned up separately on script unload).
|
||||
*/
|
||||
|
||||
void
|
||||
theme_unregister_plugin (struct t_weechat_plugin *plugin)
|
||||
{
|
||||
struct t_theme *ptr_theme;
|
||||
struct t_theme_contribution *ptr_contribution, *next_contribution;
|
||||
|
||||
if (!plugin)
|
||||
return;
|
||||
for (ptr_theme = themes; ptr_theme; ptr_theme = ptr_theme->next_theme)
|
||||
{
|
||||
ptr_contribution = ptr_theme->contributions;
|
||||
while (ptr_contribution)
|
||||
{
|
||||
next_contribution = ptr_contribution->next_contribution;
|
||||
if ((ptr_contribution->plugin == plugin)
|
||||
&& (ptr_contribution->script == NULL))
|
||||
{
|
||||
theme_drop_contribution (ptr_theme, ptr_contribution);
|
||||
}
|
||||
ptr_contribution = next_contribution;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
/*
|
||||
* Drops all contributions owned by a given script (across every theme).
|
||||
* Called from the script-unload lifecycle (a later commit).
|
||||
*/
|
||||
|
||||
void
|
||||
theme_unregister_script (struct t_weechat_plugin *plugin,
|
||||
const void *script)
|
||||
{
|
||||
struct t_theme *ptr_theme;
|
||||
struct t_theme_contribution *ptr_contribution, *next_contribution;
|
||||
|
||||
if (!plugin || !script)
|
||||
return;
|
||||
for (ptr_theme = themes; ptr_theme; ptr_theme = ptr_theme->next_theme)
|
||||
{
|
||||
ptr_contribution = ptr_theme->contributions;
|
||||
while (ptr_contribution)
|
||||
{
|
||||
next_contribution = ptr_contribution->next_contribution;
|
||||
if ((ptr_contribution->plugin == plugin)
|
||||
&& (ptr_contribution->script == script))
|
||||
{
|
||||
theme_drop_contribution (ptr_theme, ptr_contribution);
|
||||
}
|
||||
ptr_contribution = next_contribution;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
/*
|
||||
* Returns the total number of overrides across all contributions of a
|
||||
* theme. Duplicate keys (across contributions) are counted multiple
|
||||
* times; the actual merged-unique count is at most this number.
|
||||
*/
|
||||
|
||||
int
|
||||
theme_overrides_count (struct t_theme *theme)
|
||||
{
|
||||
struct t_theme_contribution *ptr;
|
||||
int n;
|
||||
|
||||
if (!theme)
|
||||
return 0;
|
||||
n = 0;
|
||||
for (ptr = theme->contributions; ptr; ptr = ptr->next_contribution)
|
||||
n += ptr->overrides->items_count;
|
||||
return n;
|
||||
}
|
||||
|
||||
/*
|
||||
* Returns the effective value of an option override across the theme's
|
||||
* contributions (later contributions win). Returns NULL if no
|
||||
* contribution provides the key.
|
||||
*/
|
||||
|
||||
const char *
|
||||
theme_get_override (struct t_theme *theme, const char *option_name)
|
||||
{
|
||||
struct t_theme_contribution *ptr;
|
||||
const char *value, *latest;
|
||||
|
||||
if (!theme || !option_name)
|
||||
return NULL;
|
||||
latest = NULL;
|
||||
for (ptr = theme->contributions; ptr; ptr = ptr->next_contribution)
|
||||
{
|
||||
value = (const char *)hashtable_get (ptr->overrides, option_name);
|
||||
if (value)
|
||||
latest = value;
|
||||
}
|
||||
return latest;
|
||||
}
|
||||
|
||||
/*
|
||||
* Compares two themes by name (callback used by arraylist sort).
|
||||
*
|
||||
@@ -500,6 +729,7 @@ theme_file_parse (const char *path)
|
||||
char line[8192], *ptr, *end, *eq, *key, *value;
|
||||
int line_number, in_options;
|
||||
struct t_theme *theme;
|
||||
struct t_theme_contribution *contribution;
|
||||
|
||||
if (!path)
|
||||
return NULL;
|
||||
@@ -514,6 +744,15 @@ theme_file_parse (const char *path)
|
||||
fclose (file);
|
||||
return NULL;
|
||||
}
|
||||
/* file themes carry a single anonymous (plugin=NULL, script=NULL)
|
||||
contribution holding everything in the [options] section */
|
||||
contribution = theme_contribution_new (theme, NULL, NULL);
|
||||
if (!contribution)
|
||||
{
|
||||
theme_free (theme);
|
||||
fclose (file);
|
||||
return NULL;
|
||||
}
|
||||
/* clear the placeholder name; the file should provide it */
|
||||
free (theme->name);
|
||||
theme->name = NULL;
|
||||
@@ -617,7 +856,7 @@ theme_file_parse (const char *path)
|
||||
|
||||
if (in_options)
|
||||
{
|
||||
hashtable_set (theme->overrides, key, value);
|
||||
hashtable_set (contribution->overrides, key, value);
|
||||
}
|
||||
else
|
||||
{
|
||||
@@ -685,8 +924,8 @@ int
|
||||
theme_apply (const char *name)
|
||||
{
|
||||
struct t_theme *file_theme = NULL;
|
||||
struct t_theme *registry_theme = NULL;
|
||||
struct t_hashtable *overrides = NULL;
|
||||
struct t_theme *theme = NULL;
|
||||
struct t_theme_contribution *ptr_contribution;
|
||||
char *path = NULL;
|
||||
char *backup_name = NULL;
|
||||
|
||||
@@ -709,12 +948,12 @@ theme_apply (const char *name)
|
||||
free (path);
|
||||
return WEECHAT_RC_ERROR;
|
||||
}
|
||||
overrides = file_theme->overrides;
|
||||
theme = file_theme;
|
||||
}
|
||||
else
|
||||
{
|
||||
registry_theme = theme_search (name);
|
||||
if (!registry_theme)
|
||||
theme = theme_search (name);
|
||||
if (!theme)
|
||||
{
|
||||
gui_chat_printf (NULL,
|
||||
_("%sTheme \"%s\" not found"),
|
||||
@@ -723,7 +962,6 @@ theme_apply (const char *name)
|
||||
free (path);
|
||||
return WEECHAT_RC_ERROR;
|
||||
}
|
||||
overrides = registry_theme->overrides;
|
||||
}
|
||||
free (path);
|
||||
|
||||
@@ -744,10 +982,17 @@ theme_apply (const char *name)
|
||||
}
|
||||
}
|
||||
|
||||
/* apply each override; per-option refreshes are suppressed via the
|
||||
theme_applying flag (see config_change_color) */
|
||||
/* Apply each contribution in order; per-option refreshes are
|
||||
suppressed via the theme_applying flag (see config_change_color).
|
||||
Later contributions naturally win for duplicate keys because
|
||||
config_file_option_set is called for each in sequence. */
|
||||
theme_applying = 1;
|
||||
hashtable_map (overrides, &theme_apply_set_option_cb, NULL);
|
||||
for (ptr_contribution = theme->contributions; ptr_contribution;
|
||||
ptr_contribution = ptr_contribution->next_contribution)
|
||||
{
|
||||
hashtable_map (ptr_contribution->overrides,
|
||||
&theme_apply_set_option_cb, NULL);
|
||||
}
|
||||
theme_applying = 0;
|
||||
|
||||
/* file_theme (if any) is transient: discard now */
|
||||
|
||||
+34
-5
@@ -24,16 +24,35 @@
|
||||
|
||||
struct t_hashtable;
|
||||
struct t_arraylist;
|
||||
struct t_weechat_plugin;
|
||||
|
||||
/*
|
||||
* A contribution is one (owner, overrides) pair attached to a theme.
|
||||
* "owner" is identified by a (plugin, script) pair:
|
||||
* - plugin == NULL && script == NULL => core
|
||||
* - plugin != NULL && script == NULL => plugin (e.g. irc, fset)
|
||||
* - plugin != NULL && script != NULL => individual script under that
|
||||
* script-language plugin
|
||||
*/
|
||||
struct t_theme_contribution
|
||||
{
|
||||
struct t_weechat_plugin *plugin;
|
||||
const void *script;
|
||||
struct t_hashtable *overrides; /* full_option_name -> value */
|
||||
struct t_theme_contribution *prev_contribution;
|
||||
struct t_theme_contribution *next_contribution;
|
||||
};
|
||||
|
||||
struct t_theme
|
||||
{
|
||||
char *name; /* "dark", "solarized", ... */
|
||||
char *name; /* "light", "solarized", ... */
|
||||
char *description; /* free-form text */
|
||||
char *date; /* "YYYY-MM-DD HH:MM:SS" */
|
||||
char *weechat_version; /* version at registration time */
|
||||
struct t_hashtable *overrides; /* full_option_name -> value */
|
||||
struct t_theme *prev_theme; /* link to previous theme */
|
||||
struct t_theme *next_theme; /* link to next theme */
|
||||
struct t_theme_contribution *contributions;
|
||||
struct t_theme_contribution *last_contribution;
|
||||
struct t_theme *prev_theme;
|
||||
struct t_theme *next_theme;
|
||||
};
|
||||
|
||||
extern struct t_theme *themes;
|
||||
@@ -41,8 +60,13 @@ extern struct t_theme *last_theme;
|
||||
extern int theme_applying; /* gate for config_change_color */
|
||||
|
||||
extern struct t_theme *theme_search (const char *name);
|
||||
extern struct t_theme *theme_register (const char *name,
|
||||
extern struct t_theme *theme_register (struct t_weechat_plugin *plugin,
|
||||
const void *script,
|
||||
const char *name,
|
||||
struct t_hashtable *overrides);
|
||||
extern int theme_overrides_count (struct t_theme *theme);
|
||||
extern const char *theme_get_override (struct t_theme *theme,
|
||||
const char *option_name);
|
||||
extern struct t_arraylist *theme_list (void);
|
||||
extern int theme_apply (const char *name);
|
||||
extern int theme_save (const char *name, int full);
|
||||
@@ -52,6 +76,11 @@ extern char *theme_user_file_path (const char *name);
|
||||
extern struct t_theme *theme_file_parse (const char *path);
|
||||
extern void theme_free (struct t_theme *theme);
|
||||
|
||||
/* lifecycle: drop all contributions owned by a plugin or script */
|
||||
extern void theme_unregister_plugin (struct t_weechat_plugin *plugin);
|
||||
extern void theme_unregister_script (struct t_weechat_plugin *plugin,
|
||||
const void *script);
|
||||
|
||||
extern void theme_init (void);
|
||||
extern void theme_end (void);
|
||||
|
||||
|
||||
@@ -730,7 +730,9 @@ struct t_weechat_plugin
|
||||
const char *option_name);
|
||||
|
||||
/* themes */
|
||||
struct t_theme *(*theme_register) (const char *name,
|
||||
struct t_theme *(*theme_register) (struct t_weechat_plugin *plugin,
|
||||
const void *script,
|
||||
const char *name,
|
||||
struct t_hashtable *overrides);
|
||||
|
||||
/* key bindings */
|
||||
@@ -1862,7 +1864,8 @@ extern int weechat_plugin_end (struct t_weechat_plugin *plugin);
|
||||
|
||||
/* themes */
|
||||
#define weechat_theme_register(__name, __overrides) \
|
||||
(weechat_plugin->theme_register)(__name, __overrides)
|
||||
(weechat_plugin->theme_register)(weechat_plugin, NULL, \
|
||||
__name, __overrides)
|
||||
|
||||
/* key bindings */
|
||||
#define weechat_key_bind(__context, __keys) \
|
||||
|
||||
@@ -99,7 +99,7 @@ TEST(CoreTheme, Search)
|
||||
POINTERS_EQUAL(NULL, theme_search (NULL));
|
||||
|
||||
overrides = make_overrides ("weechat.color.chat", "default", NULL, NULL);
|
||||
theme_register ("dark", overrides);
|
||||
theme_register (NULL, NULL, "dark", overrides);
|
||||
hashtable_free (overrides);
|
||||
|
||||
/* registered name found */
|
||||
@@ -165,8 +165,9 @@ TEST(CoreTheme, Alloc)
|
||||
LONGS_EQUAL(19, (long)strlen (theme->date));
|
||||
CHECK(theme->weechat_version != NULL);
|
||||
CHECK(theme->weechat_version[0] != '\0');
|
||||
CHECK(theme->overrides != NULL);
|
||||
LONGS_EQUAL(0, theme->overrides->items_count);
|
||||
POINTERS_EQUAL(NULL, theme->contributions);
|
||||
POINTERS_EQUAL(NULL, theme->last_contribution);
|
||||
LONGS_EQUAL(0, theme_overrides_count (theme));
|
||||
POINTERS_EQUAL(NULL, theme->prev_theme);
|
||||
POINTERS_EQUAL(NULL, theme->next_theme);
|
||||
|
||||
@@ -202,40 +203,40 @@ TEST(CoreTheme, Register)
|
||||
struct t_theme *t1, *t2;
|
||||
|
||||
/* NULL / empty name => NULL */
|
||||
POINTERS_EQUAL(NULL, theme_register (NULL, NULL));
|
||||
POINTERS_EQUAL(NULL, theme_register ("", NULL));
|
||||
POINTERS_EQUAL(NULL, theme_register (NULL, NULL, NULL, NULL));
|
||||
POINTERS_EQUAL(NULL, theme_register (NULL, NULL, "", NULL));
|
||||
|
||||
/* register a new theme */
|
||||
o1 = make_overrides ("weechat.color.chat", "default",
|
||||
"weechat.color.separator", "blue");
|
||||
t1 = theme_register ("dark", o1);
|
||||
t1 = theme_register (NULL, NULL, "dark", o1);
|
||||
hashtable_free (o1);
|
||||
CHECK(t1 != NULL);
|
||||
STRCMP_EQUAL("dark", t1->name);
|
||||
LONGS_EQUAL(2, t1->overrides->items_count);
|
||||
STRCMP_EQUAL("default", (const char *)hashtable_get (t1->overrides,
|
||||
LONGS_EQUAL(2, theme_overrides_count (t1));
|
||||
STRCMP_EQUAL("default", theme_get_override (t1,
|
||||
"weechat.color.chat"));
|
||||
STRCMP_EQUAL("blue", (const char *)hashtable_get (t1->overrides,
|
||||
STRCMP_EQUAL("blue", theme_get_override (t1,
|
||||
"weechat.color.separator"));
|
||||
|
||||
/* second call with same name merges into the existing theme */
|
||||
o2 = make_overrides ("irc.color.input_nick", "lightcyan",
|
||||
"weechat.color.separator", "darkgray");
|
||||
t2 = theme_register ("dark", o2);
|
||||
t2 = theme_register (NULL, NULL, "dark", o2);
|
||||
hashtable_free (o2);
|
||||
POINTERS_EQUAL(t1, t2); /* same struct, merged into */
|
||||
LONGS_EQUAL(3, t1->overrides->items_count);
|
||||
LONGS_EQUAL(3, theme_overrides_count (t1));
|
||||
/* new key added */
|
||||
STRCMP_EQUAL("lightcyan", (const char *)hashtable_get (t1->overrides,
|
||||
STRCMP_EQUAL("lightcyan", theme_get_override (t1,
|
||||
"irc.color.input_nick"));
|
||||
/* duplicate key overridden */
|
||||
STRCMP_EQUAL("darkgray", (const char *)hashtable_get (t1->overrides,
|
||||
STRCMP_EQUAL("darkgray", theme_get_override (t1,
|
||||
"weechat.color.separator"));
|
||||
|
||||
/* registering with NULL overrides only creates the theme */
|
||||
t2 = theme_register ("empty", NULL);
|
||||
t2 = theme_register (NULL, NULL, "empty", NULL);
|
||||
CHECK(t2 != NULL);
|
||||
LONGS_EQUAL(0, t2->overrides->items_count);
|
||||
LONGS_EQUAL(0, theme_overrides_count (t2));
|
||||
}
|
||||
|
||||
/*
|
||||
@@ -255,9 +256,9 @@ TEST(CoreTheme, List)
|
||||
arraylist_free (list);
|
||||
|
||||
/* register three themes in non-alphabetical order */
|
||||
theme_register ("solarized", NULL);
|
||||
theme_register ("dark", NULL);
|
||||
theme_register ("nord", NULL);
|
||||
theme_register (NULL, NULL, "solarized", NULL);
|
||||
theme_register (NULL, NULL, "dark", NULL);
|
||||
theme_register (NULL, NULL, "nord", NULL);
|
||||
|
||||
list = theme_list ();
|
||||
CHECK(list != NULL);
|
||||
@@ -471,7 +472,7 @@ TEST(CoreTheme, Apply)
|
||||
/* register a theme that flips one themable option, then apply */
|
||||
overrides = make_overrides ("weechat.look.prefix_error", "TEST!",
|
||||
NULL, NULL);
|
||||
theme_register ("apply_test", overrides);
|
||||
theme_register (NULL, NULL, "apply_test", overrides);
|
||||
hashtable_free (overrides);
|
||||
|
||||
LONGS_EQUAL(WEECHAT_RC_OK, theme_apply ("apply_test"));
|
||||
@@ -584,18 +585,17 @@ TEST(CoreTheme, FileParse)
|
||||
|
||||
/* [options] entries: three known keys, "unknown_info_key" must NOT
|
||||
leak in (it lives under [info]) */
|
||||
LONGS_EQUAL(3, theme->overrides->items_count);
|
||||
LONGS_EQUAL(3, theme_overrides_count (theme));
|
||||
STRCMP_EQUAL("default",
|
||||
(const char *)hashtable_get (theme->overrides,
|
||||
theme_get_override (theme,
|
||||
"weechat.color.chat"));
|
||||
STRCMP_EQUAL("blue",
|
||||
(const char *)hashtable_get (theme->overrides,
|
||||
theme_get_override (theme,
|
||||
"weechat.color.separator"));
|
||||
STRCMP_EQUAL("lightcyan",
|
||||
(const char *)hashtable_get (theme->overrides,
|
||||
theme_get_override (theme,
|
||||
"irc.color.input_nick"));
|
||||
POINTERS_EQUAL(NULL, hashtable_get (theme->overrides,
|
||||
"unknown_info_key"));
|
||||
POINTERS_EQUAL(NULL, theme_get_override (theme, "unknown_info_key"));
|
||||
|
||||
theme_free (theme);
|
||||
unlink (path);
|
||||
@@ -614,7 +614,7 @@ TEST(CoreTheme, FileParse)
|
||||
STRCMP_EQUAL("", theme->description);
|
||||
STRCMP_EQUAL("", theme->date);
|
||||
STRCMP_EQUAL("", theme->weechat_version);
|
||||
LONGS_EQUAL(0, theme->overrides->items_count);
|
||||
LONGS_EQUAL(0, theme_overrides_count (theme));
|
||||
theme_free (theme);
|
||||
unlink (path);
|
||||
|
||||
@@ -634,11 +634,11 @@ TEST(CoreTheme, FileParse)
|
||||
theme = theme_file_parse (path);
|
||||
CHECK(theme != NULL);
|
||||
STRCMP_EQUAL("robust", theme->name);
|
||||
LONGS_EQUAL(1, theme->overrides->items_count);
|
||||
LONGS_EQUAL(1, theme_overrides_count (theme));
|
||||
STRCMP_EQUAL("red",
|
||||
(const char *)hashtable_get (theme->overrides,
|
||||
theme_get_override (theme,
|
||||
"weechat.color.chat"));
|
||||
POINTERS_EQUAL(NULL, hashtable_get (theme->overrides, "ignored"));
|
||||
POINTERS_EQUAL(NULL, theme_get_override (theme, "ignored"));
|
||||
theme_free (theme);
|
||||
unlink (path);
|
||||
}
|
||||
@@ -662,7 +662,7 @@ TEST(CoreTheme, Save)
|
||||
LONGS_EQUAL(WEECHAT_RC_ERROR, theme_save ("backup-anything", 1));
|
||||
|
||||
/* name colliding with a built-in is refused */
|
||||
theme_register ("dark", NULL);
|
||||
theme_register (NULL, NULL, "dark", NULL);
|
||||
LONGS_EQUAL(WEECHAT_RC_ERROR, theme_save ("dark", 0));
|
||||
LONGS_EQUAL(WEECHAT_RC_ERROR, theme_save ("dark", 1));
|
||||
|
||||
@@ -699,7 +699,7 @@ TEST(CoreTheme, Delete)
|
||||
LONGS_EQUAL(WEECHAT_RC_ERROR, theme_delete (""));
|
||||
|
||||
/* refuses to delete a built-in (no file to delete) */
|
||||
theme_register ("dark", NULL);
|
||||
theme_register (NULL, NULL, "dark", NULL);
|
||||
LONGS_EQUAL(WEECHAT_RC_ERROR, theme_delete ("dark"));
|
||||
|
||||
/* missing file => error */
|
||||
@@ -725,7 +725,7 @@ TEST(CoreTheme, Delete)
|
||||
TEST(CoreTheme, Init)
|
||||
{
|
||||
/* register something so we can prove init wipes it */
|
||||
theme_register ("dark", NULL);
|
||||
theme_register (NULL, NULL, "dark", NULL);
|
||||
CHECK(themes != NULL);
|
||||
|
||||
theme_init ();
|
||||
@@ -741,8 +741,8 @@ TEST(CoreTheme, Init)
|
||||
|
||||
TEST(CoreTheme, End)
|
||||
{
|
||||
theme_register ("dark", NULL);
|
||||
theme_register ("light", NULL);
|
||||
theme_register (NULL, NULL, "dark", NULL);
|
||||
theme_register (NULL, NULL, "light", NULL);
|
||||
CHECK(themes != NULL);
|
||||
|
||||
theme_end ();
|
||||
@@ -750,6 +750,93 @@ TEST(CoreTheme, End)
|
||||
POINTERS_EQUAL(NULL, last_theme);
|
||||
}
|
||||
|
||||
/*
|
||||
* Test functions:
|
||||
* theme_unregister_plugin
|
||||
* theme_unregister_script
|
||||
* theme_register (per-contributor identity)
|
||||
*/
|
||||
|
||||
TEST(CoreTheme, UnregisterByOwner)
|
||||
{
|
||||
struct t_weechat_plugin fake_plugin_a, fake_plugin_b;
|
||||
int script_a = 0, script_b = 0;
|
||||
struct t_hashtable *o1, *o2, *o3, *o4;
|
||||
struct t_theme *theme;
|
||||
|
||||
/* four contributors register against the same theme:
|
||||
core (NULL, NULL), plugin_a (no script), plugin_b (no script),
|
||||
and an individual script under plugin_a */
|
||||
o1 = make_overrides ("weechat.color.chat", "default", NULL, NULL);
|
||||
o2 = make_overrides ("irc.color.input_nick", "cyan", NULL, NULL);
|
||||
o3 = make_overrides ("fset.color.title_filter", "18", NULL, NULL);
|
||||
o4 = make_overrides ("weechat.color.separator", "251", NULL, NULL);
|
||||
|
||||
theme_register (NULL, NULL, "light", o1);
|
||||
theme_register (&fake_plugin_a, NULL, "light", o2);
|
||||
theme_register (&fake_plugin_b, NULL, "light", o3);
|
||||
theme_register (&fake_plugin_a, &script_a, "light", o4);
|
||||
|
||||
hashtable_free (o1);
|
||||
hashtable_free (o2);
|
||||
hashtable_free (o3);
|
||||
hashtable_free (o4);
|
||||
|
||||
theme = theme_search ("light");
|
||||
CHECK(theme != NULL);
|
||||
LONGS_EQUAL(4, theme_overrides_count (theme));
|
||||
|
||||
/* dropping plugin_a's plugin-level contribution leaves core,
|
||||
plugin_b, and plugin_a's script contributions intact */
|
||||
theme_unregister_plugin (&fake_plugin_a);
|
||||
LONGS_EQUAL(3, theme_overrides_count (theme));
|
||||
STRCMP_EQUAL("default",
|
||||
theme_get_override (theme, "weechat.color.chat"));
|
||||
POINTERS_EQUAL(NULL,
|
||||
theme_get_override (theme, "irc.color.input_nick"));
|
||||
STRCMP_EQUAL("18",
|
||||
theme_get_override (theme, "fset.color.title_filter"));
|
||||
STRCMP_EQUAL("251",
|
||||
theme_get_override (theme, "weechat.color.separator"));
|
||||
|
||||
/* dropping the script contribution leaves only core and plugin_b */
|
||||
theme_unregister_script (&fake_plugin_a, &script_a);
|
||||
LONGS_EQUAL(2, theme_overrides_count (theme));
|
||||
POINTERS_EQUAL(NULL,
|
||||
theme_get_override (theme, "weechat.color.separator"));
|
||||
|
||||
/* unrelated owners are no-ops */
|
||||
theme_unregister_plugin (&fake_plugin_a); /* already gone */
|
||||
theme_unregister_script (&fake_plugin_b, &script_b); /* never registered */
|
||||
LONGS_EQUAL(2, theme_overrides_count (theme));
|
||||
}
|
||||
|
||||
TEST(CoreTheme, RegisterMergesPerContributor)
|
||||
{
|
||||
struct t_weechat_plugin fake_plugin;
|
||||
struct t_hashtable *a, *b;
|
||||
struct t_theme *theme;
|
||||
|
||||
/* two successive registrations from the same (plugin, script)
|
||||
merge into a single contribution */
|
||||
a = make_overrides ("k1", "v1", "k2", "v2");
|
||||
b = make_overrides ("k2", "newv2", "k3", "v3");
|
||||
|
||||
theme_register (&fake_plugin, NULL, "X", a);
|
||||
theme = theme_register (&fake_plugin, NULL, "X", b);
|
||||
hashtable_free (a);
|
||||
hashtable_free (b);
|
||||
|
||||
CHECK(theme != NULL);
|
||||
/* one contribution, 3 keys (k1, k2, k3) */
|
||||
CHECK(theme->contributions != NULL);
|
||||
POINTERS_EQUAL(NULL, theme->contributions->next_contribution);
|
||||
LONGS_EQUAL(3, theme->contributions->overrides->items_count);
|
||||
STRCMP_EQUAL("v1", theme_get_override (theme, "k1"));
|
||||
STRCMP_EQUAL("newv2", theme_get_override (theme, "k2"));
|
||||
STRCMP_EQUAL("v3", theme_get_override (theme, "k3"));
|
||||
}
|
||||
|
||||
/*
|
||||
* Test functions:
|
||||
* theme_builtin_init
|
||||
@@ -770,24 +857,24 @@ TEST(CoreTheme, BuiltinInit)
|
||||
CHECK(theme != NULL);
|
||||
|
||||
/* sanity check: many core color overrides (>= 30) */
|
||||
CHECK(theme->overrides->items_count >= 30);
|
||||
CHECK(theme_overrides_count (theme) >= 30);
|
||||
|
||||
/* spot-check a few known entries from the core light table */
|
||||
STRCMP_EQUAL("cyan",
|
||||
(const char *)hashtable_get (theme->overrides,
|
||||
theme_get_override (theme,
|
||||
"weechat.color.chat_nick"));
|
||||
STRCMP_EQUAL("251",
|
||||
(const char *)hashtable_get (theme->overrides,
|
||||
theme_get_override (theme,
|
||||
"weechat.color.separator"));
|
||||
STRCMP_EQUAL("254",
|
||||
(const char *)hashtable_get (theme->overrides,
|
||||
theme_get_override (theme,
|
||||
"weechat.bar.status.color_bg"));
|
||||
|
||||
/* idempotency: a second call merges (no duplicate themes, count
|
||||
stays the same because the same keys are re-inserted) */
|
||||
int count_before = theme->overrides->items_count;
|
||||
int count_before = theme_overrides_count (theme);
|
||||
theme_builtin_init ();
|
||||
theme = theme_search ("light");
|
||||
CHECK(theme != NULL);
|
||||
LONGS_EQUAL(count_before, theme->overrides->items_count);
|
||||
LONGS_EQUAL(count_before, theme_overrides_count (theme));
|
||||
}
|
||||
|
||||
Reference in New Issue
Block a user