/*
* Copyright (C) 2012-2022 Robin Haberkorn
*
* This program is free software: you can redistribute it and/or modify
* it under the terms of the GNU General Public License as published by
* the Free Software Foundation, either version 3 of the License, or
* (at your option) any later version.
*
* This program is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
* GNU General Public License for more details.
*
* You should have received a copy of the GNU General Public License
* along with this program. If not, see .
*/
#ifdef HAVE_CONFIG_H
#include "config.h"
#endif
#include
/*
* NOTE: Must be included only once.
*/
//#include
#include "sciteco.h"
#include "interface.h"
#include "string-utils.h"
#include "rb3str.h"
static gint
teco_rb3str_cmp(const teco_rb3str_head_t *head, const teco_string_t *data)
{
return teco_string_cmp(&head->key, data->data, data->len);
}
static gint
teco_rb3str_casecmp(const teco_rb3str_head_t *head, const teco_string_t *data)
{
return teco_string_casecmp(&head->key, data->data, data->len);
}
/** @memberof teco_rb3str_tree_t */
teco_rb3str_head_t *
teco_rb3str_insert(teco_rb3str_tree_t *tree, gboolean case_sensitive, teco_rb3str_head_t *head)
{
rb3_cmp *cmp = case_sensitive ? (rb3_cmp *)teco_rb3str_cmp : (rb3_cmp *)teco_rb3str_casecmp;
return (teco_rb3str_head_t *)rb3_insert(tree, &head->head, cmp, &head->key);
}
/** @memberof teco_rb3str_tree_t */
teco_rb3str_head_t *
teco_rb3str_find(teco_rb3str_tree_t *tree, gboolean case_sensitive, const gchar *str, gsize len)
{
rb3_cmp *cmp = case_sensitive ? (rb3_cmp *)teco_rb3str_cmp : (rb3_cmp *)teco_rb3str_casecmp;
teco_string_t data = {(gchar *)str, len};
return (teco_rb3str_head_t *)rb3_find(tree, cmp, &data);
}
/** @memberof teco_rb3str_tree_t */
teco_rb3str_head_t *
teco_rb3str_nfind(teco_rb3str_tree_t *tree, gboolean case_sensitive, const gchar *str, gsize len)
{
rb3_cmp *cmp = case_sensitive ? (rb3_cmp *)teco_rb3str_cmp : (rb3_cmp *)teco_rb3str_casecmp;
teco_string_t data = {(gchar *)str, len};
/*
* This is based on rb3_INLINE_find() in rb3ptr.h.
* Alternatively, we might adapt/wrap teco_rb3str_cmp() in order to store
* the last element > data.
*/
struct rb3_head *parent = rb3_get_base(tree);
struct rb3_head *res = NULL;
int dir = RB3_LEFT;
while (rb3_has_child(parent, dir)) {
parent = rb3_get_child(parent, dir);
int r = cmp(parent, &data);
if (r == 0)
return (teco_rb3str_head_t *)parent;
dir = (r < 0) ? RB3_RIGHT : RB3_LEFT;
if (dir == RB3_LEFT)
res = parent;
}
return (teco_rb3str_head_t *)res;
}
/**
* Auto-complete string given the entries of a RB tree.
*
* @param tree The RB tree (root).
* @param case_sensitive Whether to match case-sensitive.
* @param str String to complete (not necessarily null-terminated).
* @param str_len Length of characters in `str`.
* @param restrict_len Limit completions to this size.
* @param insert String to set with characters that can be autocompleted.
* @return TRUE if the completion was unambiguous, else FALSE.
*
* @memberof teco_rb3str_tree_t
*/
gboolean
teco_rb3str_auto_complete(teco_rb3str_tree_t *tree, gboolean case_sensitive,
const gchar *str, gsize str_len, gsize restrict_len, teco_string_t *insert)
{
memset(insert, 0, sizeof(*insert));
teco_string_diff_t diff = case_sensitive ? teco_string_diff : teco_string_casediff;
teco_rb3str_head_t *first = NULL;
gsize prefix_len = 0;
guint prefixed_entries = 0;
for (teco_rb3str_head_t *cur = teco_rb3str_nfind(tree, case_sensitive, str, str_len);
cur && cur->key.len >= str_len && diff(&cur->key, str, str_len) == str_len;
cur = teco_rb3str_get_next(cur)) {
if (restrict_len && cur->key.len != restrict_len)
continue;
if (G_UNLIKELY(!first)) {
first = cur;
prefix_len = cur->key.len - str_len;
} else {
gsize len = diff(&cur->key, first->key.data, first->key.len) - str_len;
if (len < prefix_len)
prefix_len = len;
}
prefixed_entries++;
}
if (prefix_len > 0) {
teco_string_init(insert, first->key.data + str_len, prefix_len);
} else if (prefixed_entries > 1) {
for (teco_rb3str_head_t *cur = first;
cur && cur->key.len >= str_len && diff(&cur->key, str, str_len) == str_len;
cur = teco_rb3str_get_next(cur)) {
if (restrict_len && cur->key.len != restrict_len)
continue;
teco_interface_popup_add(TECO_POPUP_PLAIN,
cur->key.data, cur->key.len, FALSE);
}
teco_interface_popup_show();
}
return prefixed_entries == 1;
}