diff --git a/config/menus/vars.cfg b/config/menus/vars.cfg index 66bcf8dae..218760c3c 100644 --- a/config/menus/vars.cfg +++ b/config/menus/vars.cfg @@ -83,7 +83,7 @@ menu_variables = [ q = (+ $variable_index $i) if (< $q $numvars) [ curvar = (getvarinfo $q $variable_types $variable_no_types $variable_flags $variable_no_flags $variable_search_text) - hilvar = (stringreplace $curvar $variable_search_text (format "^fs^fy%1^fS" $variable_search_text)) + hilvar = (string_highlight_list $curvar $variable_search_text "^fs^fy" "^fS") ui_list [ ui_radiobutton $hilvar variable_number $q ] ] [ ui_strut 1 ] ] diff --git a/config/usage.cfg b/config/usage.cfg index afcc7f6cb..7c92d29a1 100644 --- a/config/usage.cfg +++ b/config/usage.cfg @@ -715,6 +715,7 @@ setdesc "stringlen" "returns the length of string;^nexample: stringlen ^"four^"" setdesc "stringncasecmp" "returns true when the first count characters of string1 and string2 are equal ignoring case;^nexample: stringncasecmp ^"str^" ^"StRiNg^" 3" "string1 string2 count" setdesc "stringncmp" "returns true when the first count characters of string1 and string2 are equal;^nexample: stringncmp ^"str^" ^"string^" 3" "string1 string2 count" setdesc "stringreplace" "returns string with all occurrences of search replaced with replace;^nexample: stringreplace ^"misspelled sring^" ^"sring^" ^"string^"" "string search replace" +setdesc "string_highlight_list" "returns string with all occurrences of words in string surrounded by before and after;^nexample: string_highlight_list ^"abcdefghi^" ^"b efg gh^" ^"<^" ^">^" // this returns ^"acdi^"" "string words before after" setdesc "stringstr" "returns the index of search in string;^nexample: stringstr ^"long string^" ^"str^"" "string search" setdesc "substring" "returns a substringing of string starting at start and continuing to the end or for count characters;^nexample: substring ^"string^" 2 3" "string start [count]" setdesc "concat" "returns all values concatenated with a space between each argument;^nexample: concat hello (getname)" "value..." diff --git a/src/engine/command.cpp b/src/engine/command.cpp index 705e9577c..b3df12ac6 100644 --- a/src/engine/command.cpp +++ b/src/engine/command.cpp @@ -3475,6 +3475,63 @@ char *strreplace(const char *s, const char *oldval, const char *newval) ICOMMAND(0, stringreplace, "sss", (char *s, char *o, char *n), commandret->setstr(strreplace(s, o, n))); +// Surrounds all occurrences in `s` of the words in `words` with `before` and `after`. +// Words that overlap will be merged and count as one big word. +// Example: string_highlight_list("abcdefghi", "b efg gh", "<", ">") +// returns "acdi". +char *string_highlight_list(const char *s, const char *words, const char *before, const char *after) +{ + if(!s || !*s) return newstring(""); + + std::vector word_list; + explodelist(words, word_list); + size_t s_len = strlen(s); + char *highlight_mask = new char[s_len]; // This is a byte array, not string + memset(highlight_mask, 0, s_len); + + // First mark the characters that will be highlighted. + for(const std::string &word : word_list) + { + for(size_t s_i = 0; s_i < s_len; s_i++) + { + char *mp = &highlight_mask[s_i]; + const char *cp = &s[s_i]; + if(strncmp(word.c_str(), cp, word.length()) == 0) + { + memset(mp, 1, word.length()); + } + } + } + + std::string replaced; + replaced.reserve(s_len); + char prev_m = 0; + // Now do the replacing using highlight_mask to see where the highlights start and end. + for(size_t s_i = 0; s_i < s_len; s_i++) + { + char m = highlight_mask[s_i]; + char c = s[s_i]; + // Start highlighting + if(m && !prev_m) + { + replaced += before; + } + // End highlighting + else if(!m && prev_m) + { + replaced += after; + } + replaced += c; + prev_m = m; + } + if(highlight_mask[s_len - 1]) replaced += after; + + delete[] highlight_mask; + return newstring(replaced.c_str(), replaced.length()); +} + +ICOMMAND(0, string_highlight_list, "ssss", (char *s, char *words, char *before, char *after), commandret->setstr(string_highlight_list(s, words, before, after))); + void stringsplice(const char *s, const char *vals, int *skip, int *count) { int slen = strlen(s), vlen = strlen(vals), @@ -3579,11 +3636,24 @@ void getvarinfo(int n, int types, int notypes, int flags, int noflags, char *str } if(str && *str) { + std::vector words; + explodelist(str, words); static char *laststr = NULL; if(ids[1].empty() || !laststr || strcmp(str, laststr)) { ids[1].setsize(0); - loopv(ids[0]) if(rigcasestr(ids[0][i]->name, str)) ids[1].add(ids[0][i]); + loopv(ids[0]) + { + bool matches = true; + for (const std::string &word : words) + { + if(!rigcasestr(ids[0][i]->name, word.c_str())) + { + matches = false; + } + } + if(matches) ids[1].add(ids[0][i]); + } if(laststr) DELETEA(laststr); laststr = newstring(str); }