summaryrefslogtreecommitdiff
diff options
context:
space:
mode:
-rw-r--r--core/ustring.cpp44
-rw-r--r--core/ustring.h2
-rw-r--r--core/variant_call.cpp4
-rw-r--r--doc/base/classes.xml16
-rw-r--r--scene/gui/text_edit.cpp81
5 files changed, 104 insertions, 43 deletions
diff --git a/core/ustring.cpp b/core/ustring.cpp
index 485f7f1b62..ea9a9d903e 100644
--- a/core/ustring.cpp
+++ b/core/ustring.cpp
@@ -2810,6 +2810,50 @@ bool String::_base_is_subsequence_of(const String& p_string, bool case_insensiti
return false;
}
+Vector<String> String::bigrams() const {
+ int n_pairs = length() - 1;
+ Vector<String> b;
+ if(n_pairs <= 0) {
+ return b;
+ }
+ b.resize(n_pairs);
+ for(int i = 0; i < n_pairs; i++) {
+ b[i] = substr(i,2);
+ }
+ return b;
+}
+
+// Similarity according to Sorensen-Dice coefficient
+float String::similarity(const String& p_string) const {
+ if(operator==(p_string)) {
+ // Equal strings are totally similar
+ return 1.0f;
+ }
+ if (length() < 2 || p_string.length() < 2) {
+ // No way to calculate similarity without a single bigram
+ return 0.0f;
+ }
+
+ Vector<String> src_bigrams = bigrams();
+ Vector<String> tgt_bigrams = p_string.bigrams();
+
+ int src_size = src_bigrams.size();
+ int tgt_size = tgt_bigrams.size();
+
+ float sum = src_size + tgt_size;
+ float inter = 0;
+ for (int i = 0; i < src_size; i++) {
+ for (int j = 0; j < tgt_size; j++) {
+ if (src_bigrams[i] == tgt_bigrams[j]) {
+ inter++;
+ break;
+ }
+ }
+ }
+
+ return (2.0f * inter)/sum;
+}
+
static bool _wildcard_match(const CharType* p_pattern, const CharType* p_string,bool p_case_sensitive) {
switch (*p_pattern) {
case '\0':
diff --git a/core/ustring.h b/core/ustring.h
index 8aceb0748c..692cb4e37d 100644
--- a/core/ustring.h
+++ b/core/ustring.h
@@ -123,6 +123,8 @@ public:
bool ends_with(const String& p_string) const;
bool is_subsequence_of(const String& p_string) const;
bool is_subsequence_ofi(const String& p_string) const;
+ Vector<String> bigrams() const;
+ float similarity(const String& p_string) const;
String replace_first(String p_key,String p_with) const;
String replace(String p_key,String p_with) const;
String replacen(String p_key,String p_with) const;
diff --git a/core/variant_call.cpp b/core/variant_call.cpp
index 29a92febbd..84015d2b04 100644
--- a/core/variant_call.cpp
+++ b/core/variant_call.cpp
@@ -249,6 +249,8 @@ static void _call_##m_type##_##m_method(Variant& r_ret,Variant& p_self,const Var
VCALL_LOCALMEM1R(String,ends_with);
VCALL_LOCALMEM1R(String,is_subsequence_of);
VCALL_LOCALMEM1R(String,is_subsequence_ofi);
+ VCALL_LOCALMEM0R(String,bigrams);
+ VCALL_LOCALMEM1R(String,similarity);
VCALL_LOCALMEM2R(String,replace);
VCALL_LOCALMEM2R(String,replacen);
VCALL_LOCALMEM2R(String,insert);
@@ -1281,6 +1283,8 @@ _VariantCall::addfunc(Variant::m_vtype,Variant::m_ret,_SCS(#m_method),VCALL(m_cl
ADDFUNC1(STRING,BOOL,String,ends_with,STRING,"text",varray());
ADDFUNC1(STRING,BOOL,String,is_subsequence_of,STRING,"text",varray());
ADDFUNC1(STRING,BOOL,String,is_subsequence_ofi,STRING,"text",varray());
+ ADDFUNC0(STRING,STRING_ARRAY,String,bigrams,varray());
+ ADDFUNC1(STRING,REAL,String,similarity,STRING,"text",varray());
ADDFUNC2(STRING,STRING,String,replace,STRING,"what",STRING,"forwhat",varray());
ADDFUNC2(STRING,STRING,String,replacen,STRING,"what",STRING,"forwhat",varray());
diff --git a/doc/base/classes.xml b/doc/base/classes.xml
index b773d85b02..e687fb5ccf 100644
--- a/doc/base/classes.xml
+++ b/doc/base/classes.xml
@@ -37268,6 +37268,13 @@ This method controls whether the position between two cached points is interpola
Return true if the strings begins with the given string.
</description>
</method>
+ <method name="bigrams">
+ <return type="StringArray">
+ </return>
+ <description>
+ Return the bigrams (pairs of consecutive letters) of this string.
+ </description>
+ </method>
<method name="c_escape">
<return type="String">
</return>
@@ -37627,6 +37634,15 @@ This method controls whether the position between two cached points is interpola
<description>
</description>
</method>
+ <method name="similarity">
+ <return type="float">
+ </return>
+ <argument index="0" name="text" type="String">
+ </argument>
+ <description>
+ Return the similarity index of the text compared to this string. 1 means totally similar and 0 means totally dissimilar.
+ </description>
+ </method>
<method name="split">
<return type="StringArray">
</return>
diff --git a/scene/gui/text_edit.cpp b/scene/gui/text_edit.cpp
index 8c928513f3..7fbd412de8 100644
--- a/scene/gui/text_edit.cpp
+++ b/scene/gui/text_edit.cpp
@@ -3851,8 +3851,14 @@ void TextEdit::undo() {
}
}
- cursor_set_line(undo_stack_pos->get().from_line);
- cursor_set_column(undo_stack_pos->get().from_column);
+ if (undo_stack_pos->get().type == TextOperation::TYPE_REMOVE) {
+ cursor_set_line(undo_stack_pos->get().to_line);
+ cursor_set_column(undo_stack_pos->get().to_column);
+ _cancel_code_hint();
+ } else {
+ cursor_set_line(undo_stack_pos->get().from_line);
+ cursor_set_column(undo_stack_pos->get().from_column);
+ }
update();
}
@@ -3995,28 +4001,19 @@ void TextEdit::set_completion(bool p_enabled,const Vector<String>& p_prefixes) {
void TextEdit::_confirm_completion() {
- String remaining=completion_current.substr(completion_base.length(),completion_current.length()-completion_base.length());
- String l = text[cursor.line];
- bool same=true;
- //if what is going to be inserted is the same as what it is, don't change it
- for(int i=0;i<remaining.length();i++) {
- int c=i+cursor.column;
- if (c>=l.length() || l[c]!=remaining[i]) {
- same=false;
- break;
- }
- }
+ begin_complex_operation();
- if (same)
- cursor_set_column(cursor.column+remaining.length());
- else {
- insert_text_at_cursor(remaining);
- if (remaining.ends_with("(") && auto_brace_completion_enabled) {
- insert_text_at_cursor(")");
- cursor.column--;
- }
+ _remove_text(cursor.line, cursor.column - completion_base.length(), cursor.line, cursor.column);
+ cursor_set_column(cursor.column - completion_base.length(), false);
+ insert_text_at_cursor(completion_current);
+
+ if (completion_current.ends_with("(") && auto_brace_completion_enabled) {
+ insert_text_at_cursor(")");
+ cursor.column--;
}
+ end_complex_operation();
+
_cancel_completion();
}
@@ -4119,30 +4116,29 @@ void TextEdit::_update_completion_candidates() {
completion_index=0;
completion_base=s;
int ci_match=0;
+ Vector<float> sim_cache;
for(int i=0;i<completion_strings.size();i++) {
- if (completion_strings[i].begins_with(s)) {
+ if (s.is_subsequence_ofi(completion_strings[i])) {
// don't remove duplicates if no input is provided
- if (completion_options.find(completion_strings[i]) != -1 && s != "") {
+ if (s != "" && completion_options.find(completion_strings[i]) != -1) {
continue;
}
- completion_options.push_back(completion_strings[i]);
- int m=0;
- int max=MIN(completion_current.length(),completion_strings[i].length());
- if (max<ci_match)
- continue;
- for(int j=0;j<max;j++) {
-
- if (j>=completion_strings[i].length())
- break;
- if (completion_current[j]!=completion_strings[i][j])
- break;
- m++;
- }
- if (m>ci_match) {
- ci_match=m;
- completion_index=completion_options.size()-1;
+ // Calculate the similarity to keep completions in good order
+ float similarity = s.similarity(completion_strings[i]);
+ int comp_size = completion_options.size();
+ if (comp_size == 0) {
+ completion_options.push_back(completion_strings[i]);
+ sim_cache.push_back(similarity);
+ } else {
+ float comp_sim;
+ int pos = 0;
+ do {
+ comp_sim = sim_cache[pos++];
+ } while(pos < comp_size && similarity <= comp_sim);
+ pos--; // Pos will be off by one
+ completion_options.insert(pos, completion_strings[i]);
+ sim_cache.insert(pos, similarity);
}
-
}
}
@@ -4155,7 +4151,8 @@ void TextEdit::_update_completion_candidates() {
}
- completion_current=completion_options[completion_index];
+ // The top of the list is the best match
+ completion_current=completion_options[0];
#if 0 // even there's only one option, user still get the chance to choose using it or not
if (completion_options.size()==1) {
@@ -4167,8 +4164,6 @@ void TextEdit::_update_completion_candidates() {
}
#endif
- if (completion_options.size()==1 && s==completion_options[0])
- _cancel_completion();
completion_enabled=true;
}