degesch: implement autocompletion for /set
It was super annoying to just slightly modify strings and string arrays, now you can have existing values filled in. complete_word() looks a bit cleaner now as well.
This commit is contained in:
		
							
								
								
									
										110
									
								
								degesch.c
									
									
									
									
									
								
							
							
						
						
									
										110
									
								
								degesch.c
									
									
									
									
									
								
							@@ -12358,15 +12358,12 @@ completion_locate (struct completion *self, size_t offset)
 | 
				
			|||||||
	self->location = i - 1;
 | 
						self->location = i - 1;
 | 
				
			||||||
}
 | 
					}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
static bool
 | 
					static char *
 | 
				
			||||||
completion_matches (struct completion *self, int word, const char *pattern)
 | 
					completion_word (struct completion *self, int word)
 | 
				
			||||||
{
 | 
					{
 | 
				
			||||||
	hard_assert (word >= 0 && word < (int) self->words_len);
 | 
						hard_assert (word >= 0 && word < (int) self->words_len);
 | 
				
			||||||
	char *text = xstrndup (self->line + self->words[word].start,
 | 
						return xstrndup (self->line + self->words[word].start,
 | 
				
			||||||
		self->words[word].end - self->words[word].start);
 | 
							self->words[word].end - self->words[word].start);
 | 
				
			||||||
	bool result = !fnmatch (pattern, text, 0);
 | 
					 | 
				
			||||||
	free (text);
 | 
					 | 
				
			||||||
	return result;
 | 
					 | 
				
			||||||
}
 | 
					}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
// - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -
 | 
					// - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -
 | 
				
			||||||
@@ -12461,6 +12458,66 @@ complete_option (struct app_context *ctx, struct completion *data,
 | 
				
			|||||||
	strv_free (&options);
 | 
						strv_free (&options);
 | 
				
			||||||
}
 | 
					}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					static void
 | 
				
			||||||
 | 
					complete_set_value (struct config_item *item, const char *word,
 | 
				
			||||||
 | 
						struct strv *output)
 | 
				
			||||||
 | 
					{
 | 
				
			||||||
 | 
						struct str serialized = str_make ();
 | 
				
			||||||
 | 
						config_item_write (item, false, &serialized);
 | 
				
			||||||
 | 
						if (!strncmp (serialized.str, word, strlen (word)))
 | 
				
			||||||
 | 
							strv_append_owned (output, str_steal (&serialized));
 | 
				
			||||||
 | 
						else
 | 
				
			||||||
 | 
							str_free (&serialized);
 | 
				
			||||||
 | 
					}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					static void
 | 
				
			||||||
 | 
					complete_set_value_array (struct config_item *item, const char *word,
 | 
				
			||||||
 | 
						struct strv *output)
 | 
				
			||||||
 | 
					{
 | 
				
			||||||
 | 
						if (!item->schema || item->schema->type != CONFIG_ITEM_STRING_ARRAY)
 | 
				
			||||||
 | 
							return;
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
						struct strv items = strv_make ();
 | 
				
			||||||
 | 
						cstr_split (item->value.string.str, ",", false, &items);
 | 
				
			||||||
 | 
						for (size_t i = 0; i < items.len; i++)
 | 
				
			||||||
 | 
						{
 | 
				
			||||||
 | 
							struct str wrapped = str_make (), serialized = str_make ();
 | 
				
			||||||
 | 
							str_append (&wrapped, items.vector[i]);
 | 
				
			||||||
 | 
							config_item_write_string (&serialized, &wrapped);
 | 
				
			||||||
 | 
							str_free (&wrapped);
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
							if (!strncmp (serialized.str, word, strlen (word)))
 | 
				
			||||||
 | 
								strv_append_owned (output, str_steal (&serialized));
 | 
				
			||||||
 | 
							else
 | 
				
			||||||
 | 
								str_free (&serialized);
 | 
				
			||||||
 | 
						}
 | 
				
			||||||
 | 
						strv_free (&items);
 | 
				
			||||||
 | 
					}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					static void
 | 
				
			||||||
 | 
					complete_set (struct app_context *ctx, struct completion *data,
 | 
				
			||||||
 | 
						const char *word, struct strv *output)
 | 
				
			||||||
 | 
					{
 | 
				
			||||||
 | 
						if (data->location == 1)
 | 
				
			||||||
 | 
						{
 | 
				
			||||||
 | 
							complete_option (ctx, data, word, output);
 | 
				
			||||||
 | 
							return;
 | 
				
			||||||
 | 
						}
 | 
				
			||||||
 | 
						if (data->location != 3)
 | 
				
			||||||
 | 
							return;
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
						char *key = completion_word (data, 1);
 | 
				
			||||||
 | 
						struct config_item *item = config_item_get (ctx->config.root, key, NULL);
 | 
				
			||||||
 | 
						if (item)
 | 
				
			||||||
 | 
						{
 | 
				
			||||||
 | 
							char *op = completion_word (data, 2);
 | 
				
			||||||
 | 
							if (!strcmp (op, "-=")) complete_set_value_array (item, word, output);
 | 
				
			||||||
 | 
							if (!strcmp (op,  "=")) complete_set_value       (item, word, output);
 | 
				
			||||||
 | 
							free (op);
 | 
				
			||||||
 | 
						}
 | 
				
			||||||
 | 
						free (key);
 | 
				
			||||||
 | 
					}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
static void
 | 
					static void
 | 
				
			||||||
complete_topic (struct app_context *ctx, struct completion *data,
 | 
					complete_topic (struct app_context *ctx, struct completion *data,
 | 
				
			||||||
	const char *word, struct strv *output)
 | 
						const char *word, struct strv *output)
 | 
				
			||||||
@@ -12514,33 +12571,30 @@ static char **
 | 
				
			|||||||
complete_word (struct app_context *ctx, struct completion *data,
 | 
					complete_word (struct app_context *ctx, struct completion *data,
 | 
				
			||||||
	const char *word)
 | 
						const char *word)
 | 
				
			||||||
{
 | 
					{
 | 
				
			||||||
	// First figure out what exactly we need to complete
 | 
						char *initial = completion_word (data, 0);
 | 
				
			||||||
	bool try_commands = false;
 | 
					 | 
				
			||||||
	bool try_options = false;
 | 
					 | 
				
			||||||
	bool try_topic = false;
 | 
					 | 
				
			||||||
	bool try_nicknames = false;
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
	if (data->location == 0 && completion_matches (data, 0, "/*"))
 | 
					 | 
				
			||||||
		try_commands = true;
 | 
					 | 
				
			||||||
	else if (data->location == 1 && completion_matches (data, 0, "/set"))
 | 
					 | 
				
			||||||
		try_options = true;
 | 
					 | 
				
			||||||
	else if (data->location == 1 && completion_matches (data, 0, "/help"))
 | 
					 | 
				
			||||||
		try_commands = try_options = true;
 | 
					 | 
				
			||||||
	else if (data->location == 1 && completion_matches (data, 0, "/topic"))
 | 
					 | 
				
			||||||
		try_topic = try_nicknames = true;
 | 
					 | 
				
			||||||
	else
 | 
					 | 
				
			||||||
		try_nicknames = true;
 | 
					 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
						// Start with a placeholder for the longest common prefix
 | 
				
			||||||
	struct strv words = strv_make ();
 | 
						struct strv words = strv_make ();
 | 
				
			||||||
 | 
					 | 
				
			||||||
	// Add placeholder
 | 
					 | 
				
			||||||
	strv_append_owned (&words, NULL);
 | 
						strv_append_owned (&words, NULL);
 | 
				
			||||||
 | 
					
 | 
				
			||||||
	if (try_commands)   complete_command (ctx, data, word, &words);
 | 
						if      (data->location == 0 && *initial == '/')
 | 
				
			||||||
	if (try_options)    complete_option (ctx, data, word, &words);
 | 
							complete_command   (ctx, data, word, &words);
 | 
				
			||||||
	if (try_topic)      complete_topic (ctx, data, word, &words);
 | 
						else if (data->location >= 1 && !strcmp (initial, "/set"))
 | 
				
			||||||
	if (try_nicknames)  complete_nicknames (ctx, data, word, &words);
 | 
							complete_set       (ctx, data, word, &words);
 | 
				
			||||||
 | 
						else if (data->location == 1 && !strcmp (initial, "/help"))
 | 
				
			||||||
 | 
						{
 | 
				
			||||||
 | 
							complete_command   (ctx, data, word, &words);
 | 
				
			||||||
 | 
							complete_option    (ctx, data, word, &words);
 | 
				
			||||||
 | 
						}
 | 
				
			||||||
 | 
						else if (data->location == 1 && !strcmp (initial, "/topic"))
 | 
				
			||||||
 | 
						{
 | 
				
			||||||
 | 
							complete_topic     (ctx, data, word, &words);
 | 
				
			||||||
 | 
							complete_nicknames (ctx, data, word, &words);
 | 
				
			||||||
 | 
						}
 | 
				
			||||||
 | 
						else
 | 
				
			||||||
 | 
							complete_nicknames (ctx, data, word, &words);
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
						cstr_set (&initial, NULL);
 | 
				
			||||||
	LIST_FOR_EACH (struct hook, iter, ctx->completion_hooks)
 | 
						LIST_FOR_EACH (struct hook, iter, ctx->completion_hooks)
 | 
				
			||||||
	{
 | 
						{
 | 
				
			||||||
		struct completion_hook *hook = (struct completion_hook *) iter;
 | 
							struct completion_hook *hook = (struct completion_hook *) iter;
 | 
				
			||||||
 
 | 
				
			|||||||
		Reference in New Issue
	
	Block a user