summaryrefslogtreecommitdiff
diff options
context:
space:
mode:
-rw-r--r--core/script_language.cpp8
-rw-r--r--core/script_language.h13
-rw-r--r--modules/gdscript/gd_editor.cpp26
-rw-r--r--modules/gdscript/gd_parser.cpp21
-rw-r--r--modules/gdscript/gd_parser.h3
-rw-r--r--modules/gdscript/gd_tokenizer.cpp33
-rw-r--r--scene/gui/text_edit.cpp42
-rw-r--r--tools/editor/editor_node.cpp4
-rw-r--r--tools/editor/plugins/script_editor_plugin.cpp83
-rw-r--r--tools/editor/plugins/script_editor_plugin.h4
10 files changed, 211 insertions, 26 deletions
diff --git a/core/script_language.cpp b/core/script_language.cpp
index 68ac7d0ae7..35c50b1022 100644
--- a/core/script_language.cpp
+++ b/core/script_language.cpp
@@ -136,6 +136,14 @@ ScriptInstance::~ScriptInstance() {
}
+
+ScriptCodeCompletionCache *ScriptCodeCompletionCache::singleton=NULL;
+ScriptCodeCompletionCache::ScriptCodeCompletionCache() {
+ singleton=this;
+}
+
+
+
void ScriptLanguage::frame() {
diff --git a/core/script_language.h b/core/script_language.h
index c1b906e251..7104fe4547 100644
--- a/core/script_language.h
+++ b/core/script_language.h
@@ -126,6 +126,19 @@ public:
virtual ~ScriptInstance();
};
+class ScriptCodeCompletionCache {
+
+ static ScriptCodeCompletionCache *singleton;
+public:
+
+ virtual RES get_cached_resource(const String& p_path)=0;
+
+ static ScriptCodeCompletionCache* get_sigleton() { return singleton; }
+
+ ScriptCodeCompletionCache();
+
+};
+
class ScriptLanguage {
public:
diff --git a/modules/gdscript/gd_editor.cpp b/modules/gdscript/gd_editor.cpp
index 0edfd6b9a4..c374f509c0 100644
--- a/modules/gdscript/gd_editor.cpp
+++ b/modules/gdscript/gd_editor.cpp
@@ -381,7 +381,12 @@ static Ref<Reference> _get_parent_class(GDCompletionContext& context) {
path=context.base_path.plus_file(path);
}
- script = ResourceLoader::load(path);
+
+ if (ScriptCodeCompletionCache::get_sigleton())
+ script = ScriptCodeCompletionCache::get_sigleton()->get_cached_resource(path);
+ else
+ script = ResourceLoader::load(path);
+
if (script.is_null()) {
return REF();
}
@@ -1322,6 +1327,21 @@ static void _find_type_arguments(const GDParser::Node*p_node,int p_line,const St
if (obj) {
List<String> options;
obj->get_argument_options(p_method,p_argidx,&options);
+ if (obj->is_type("Node") && p_argidx==0 && (String(p_method)=="get_node" || String(p_method)=="has_node")) {
+
+ List<PropertyInfo> props;
+ Globals::get_singleton()->get_property_list(&props);
+
+ for(List<PropertyInfo>::Element *E=props.front();E;E=E->next()) {
+
+ String s = E->get().name;
+ if (!s.begins_with("autoload/"))
+ continue;
+ // print_line("found "+s);
+ String name = s.get_slice("/",1);
+ options.push_back("\"/root/"+name+"\"");
+ }
+ }
for(List<String>::Element *E=options.front();E;E=E->next()) {
result.insert(E->get());
@@ -1661,7 +1681,9 @@ Error GDScriptLanguage::complete_code(const String& p_code, const String& p_base
//print_line( p_code.replace(String::chr(0xFFFF),"<cursor>"));
GDParser p;
- Error err = p.parse(p_code,p_base_path,true);
+ //Error parse(const String& p_code, const String& p_base_path="", bool p_just_validate=false,const String& p_self_path="",bool p_for_completion=false);
+
+ Error err = p.parse(p_code,p_base_path,false,"",true);
bool isfunction=false;
Set<String> options;
diff --git a/modules/gdscript/gd_parser.cpp b/modules/gdscript/gd_parser.cpp
index 71af9ab10d..afe8c9aa71 100644
--- a/modules/gdscript/gd_parser.cpp
+++ b/modules/gdscript/gd_parser.cpp
@@ -30,6 +30,7 @@
#include "print_string.h"
#include "io/resource_loader.h"
#include "os/file_access.h"
+#include "script_language.h"
template<class T>
T* GDParser::alloc_node() {
@@ -116,6 +117,14 @@ bool GDParser::_parse_arguments(Node* p_parent,Vector<Node*>& p_args,bool p_stat
if (tokenizer->get_token()==GDTokenizer::TK_CURSOR) {
_make_completable_call(argidx);
completion_node=p_parent;
+ } else if (tokenizer->get_token()==GDTokenizer::TK_CONSTANT && tokenizer->get_token_constant().get_type()==Variant::STRING && tokenizer->get_token(1)==GDTokenizer::TK_CURSOR) {
+ //completing a string argument..
+ completion_cursor=tokenizer->get_token_constant();
+
+ _make_completable_call(argidx);
+ completion_node=p_parent;
+ tokenizer->advance(1);
+ return false;
}
Node*arg = _parse_expression(p_parent,p_static);
@@ -277,7 +286,11 @@ GDParser::Node* GDParser::_parse_expression(Node *p_parent,bool p_static,bool p_
if (!validating) {
//this can be too slow for just validating code
- res = ResourceLoader::load(path);
+ if (for_completion && ScriptCodeCompletionCache::get_sigleton()) {
+ res = ScriptCodeCompletionCache::get_sigleton()->get_cached_resource(path);
+ } else {
+ res = ResourceLoader::load(path);
+ }
if (!res.is_valid()) {
_set_error("Can't preload resource at path: "+path);
return NULL;
@@ -2814,6 +2827,8 @@ Error GDParser::_parse(const String& p_base_path) {
Error GDParser::parse_bytecode(const Vector<uint8_t> &p_bytecode,const String& p_base_path, const String &p_self_path) {
+ for_completion=false;
+ validating=false;
completion_type=COMPLETION_NONE;
completion_node=NULL;
completion_class=NULL;
@@ -2834,7 +2849,7 @@ Error GDParser::parse_bytecode(const Vector<uint8_t> &p_bytecode,const String& p
}
-Error GDParser::parse(const String& p_code, const String& p_base_path, bool p_just_validate, const String &p_self_path) {
+Error GDParser::parse(const String& p_code, const String& p_base_path, bool p_just_validate, const String &p_self_path,bool p_for_completion) {
completion_type=COMPLETION_NONE;
completion_node=NULL;
@@ -2851,6 +2866,7 @@ Error GDParser::parse(const String& p_code, const String& p_base_path, bool p_ju
tt->set_code(p_code);
validating=p_just_validate;
+ for_completion=p_for_completion;
tokenizer=tt;
Error ret = _parse(p_base_path);
memdelete(tt);
@@ -2886,6 +2902,7 @@ void GDParser::clear() {
current_function=NULL;
validating=false;
+ for_completion=false;
error_set=false;
tab_level.clear();
tab_level.push_back(0);
diff --git a/modules/gdscript/gd_parser.h b/modules/gdscript/gd_parser.h
index fd5c0802ea..fd8a2576fa 100644
--- a/modules/gdscript/gd_parser.h
+++ b/modules/gdscript/gd_parser.h
@@ -387,6 +387,7 @@ private:
T* alloc_node();
bool validating;
+ bool for_completion;
int parenthesis;
bool error_set;
String error;
@@ -443,7 +444,7 @@ public:
String get_error() const;
int get_error_line() const;
int get_error_column() const;
- Error parse(const String& p_code, const String& p_base_path="", bool p_just_validate=false,const String& p_self_path="");
+ Error parse(const String& p_code, const String& p_base_path="", bool p_just_validate=false,const String& p_self_path="",bool p_for_completion=false);
Error parse_bytecode(const Vector<uint8_t> &p_bytecode,const String& p_base_path="",const String& p_self_path="");
const Node *get_parse_tree() const;
diff --git a/modules/gdscript/gd_tokenizer.cpp b/modules/gdscript/gd_tokenizer.cpp
index 656a015c66..2844edfddc 100644
--- a/modules/gdscript/gd_tokenizer.cpp
+++ b/modules/gdscript/gd_tokenizer.cpp
@@ -568,7 +568,10 @@ void GDTokenizerText::_advance() {
} else if( string_mode!=STRING_MULTILINE && CharType(GETCHAR(i))=='\n') {
_make_error("Unexpected EOL at String.");
return;
-
+ } else if( CharType(GETCHAR(i))==0xFFFF) {
+ //string ends here, next will be TK
+ i--;
+ break;
} else if (CharType(GETCHAR(i))=='\\') {
//escaped characters...
i++;
@@ -670,19 +673,19 @@ void GDTokenizerText::_advance() {
while(true) {
if (GETCHAR(i)=='.') {
if (period_found || exponent_found) {
- _make_error("Invalid numeric constant at '.'");
+ _make_error("Invalid numeric constant at '.'");
return;
}
period_found=true;
} else if (GETCHAR(i)=='x') {
- if (hexa_found || str.length()!=1 || !( (i==1 && str[0]=='0') || (i==2 && str[1]=='0' && str[0]=='-') ) ) {
- _make_error("Invalid numeric constant at 'x'");
+ if (hexa_found || str.length()!=1 || !( (i==1 && str[0]=='0') || (i==2 && str[1]=='0' && str[0]=='-') ) ) {
+ _make_error("Invalid numeric constant at 'x'");
return;
}
hexa_found=true;
- } else if (!hexa_found && GETCHAR(i)=='e') {
+ } else if (!hexa_found && GETCHAR(i)=='e') {
if (hexa_found || exponent_found) {
- _make_error("Invalid numeric constant at 'e'");
+ _make_error("Invalid numeric constant at 'e'");
return;
}
exponent_found=true;
@@ -692,7 +695,7 @@ void GDTokenizerText::_advance() {
} else if ((GETCHAR(i)=='-' || GETCHAR(i)=='+') && exponent_found) {
if (sign_found) {
- _make_error("Invalid numeric constant at '-'");
+ _make_error("Invalid numeric constant at '-'");
return;
}
sign_found=true;
@@ -703,20 +706,20 @@ void GDTokenizerText::_advance() {
i++;
}
- if (!( _is_number(str[str.length()-1]) || (hexa_found && _is_hex(str[str.length()-1])))) {
- _make_error("Invalid numeric constant: "+str);
+ if (!( _is_number(str[str.length()-1]) || (hexa_found && _is_hex(str[str.length()-1])))) {
+ _make_error("Invalid numeric constant: "+str);
return;
}
INCPOS(str.length());
- if (hexa_found) {
- int val = str.hex_to_int();
- _make_constant(val);
- } else if (period_found) {
+ if (hexa_found) {
+ int val = str.hex_to_int();
+ _make_constant(val);
+ } else if (period_found) {
real_t val = str.to_double();
//print_line("*%*%*%*% to convert: "+str+" result: "+rtos(val));
_make_constant(val);
- } else {
+ } else {
int val = str.to_int();
_make_constant(val);
@@ -825,7 +828,7 @@ void GDTokenizerText::_advance() {
_make_built_in_func(GDFunctions::Function(i));
found=true;
- break;
+ break;
}
}
diff --git a/scene/gui/text_edit.cpp b/scene/gui/text_edit.cpp
index c497bc5363..44e97b5889 100644
--- a/scene/gui/text_edit.cpp
+++ b/scene/gui/text_edit.cpp
@@ -3323,9 +3323,32 @@ void TextEdit::_update_completion_candidates() {
//look for keywords first
- bool pre_keyword=false;
+ bool inquote=false;
+ int first_quote=-1;
+
+ int c=cofs-1;
+ while(c>=0) {
+ if (l[c]=='"' || l[c]=='\'') {
+ inquote=!inquote;
+ if (first_quote==-1)
+ first_quote=c;
+ }
+ c--;
+ }
- if (cofs>0 && l[cofs-1]==' ') {
+ bool pre_keyword=false;
+ bool cancel=false;
+
+ //print_line("inquote: "+itos(inquote)+"first quote "+itos(first_quote)+" cofs-1 "+itos(cofs-1));
+ if (!inquote && first_quote==cofs-1) {
+ //no completion here
+ //print_line("cancel!");
+ cancel=true;
+ } if (inquote && first_quote!=-1) {
+
+ s=l.substr(first_quote,cofs-first_quote);
+ //print_line("s: 1"+s);
+ } else if (cofs>0 && l[cofs-1]==' ') {
int kofs=cofs-1;
String kw;
while (kofs>=0 && l[kofs]==' ')
@@ -3337,7 +3360,7 @@ void TextEdit::_update_completion_candidates() {
}
pre_keyword=keywords.has(kw);
- print_line("KW "+kw+"? "+itos(pre_keyword));
+ //print_line("KW "+kw+"? "+itos(pre_keyword));
} else {
@@ -3354,7 +3377,7 @@ void TextEdit::_update_completion_candidates() {
update();
- if (!pre_keyword && s=="" && (cofs==0 || !completion_prefixes.has(String::chr(l[cofs-1])))) {
+ if (cancel || (!pre_keyword && s=="" && (cofs==0 || !completion_prefixes.has(String::chr(l[cofs-1]))))) {
//none to complete, cancel
_cancel_completion();
return;
@@ -3421,7 +3444,16 @@ void TextEdit::query_code_comple() {
String l = text[cursor.line];
int ofs = CLAMP(cursor.column,0,l.length());
- if (ofs>0 && (_is_completable(l[ofs-1]) || completion_prefixes.has(String::chr(l[ofs-1]))))
+ bool inquote=false;
+
+ int c=ofs-1;
+ while(c>=0) {
+ if (l[c]=='"' || l[c]=='\'')
+ inquote=!inquote;
+ c--;
+ }
+
+ if (ofs>0 && (inquote || _is_completable(l[ofs-1]) || completion_prefixes.has(String::chr(l[ofs-1]))))
emit_signal("request_completion");
}
diff --git a/tools/editor/editor_node.cpp b/tools/editor/editor_node.cpp
index 9f3c57b0df..7b44f2cad9 100644
--- a/tools/editor/editor_node.cpp
+++ b/tools/editor/editor_node.cpp
@@ -3047,7 +3047,7 @@ Error EditorNode::load_scene(const String& p_scene) {
//_cleanup_scene(); // i'm sorry but this MUST happen to avoid modified resources to not be reloaded.
- Ref<PackedScene> sdata = ResourceLoader::load(lpath);
+ Ref<PackedScene> sdata = ResourceLoader::load(lpath,"",true);
if (!sdata.is_valid()) {
current_option=-1;
@@ -3064,6 +3064,8 @@ Error EditorNode::load_scene(const String& p_scene) {
return ERR_FILE_NOT_FOUND;
}
+ sdata->set_path(lpath,true); //take over path
+
Node*new_scene=sdata->instance(true);
if (!new_scene) {
diff --git a/tools/editor/plugins/script_editor_plugin.cpp b/tools/editor/plugins/script_editor_plugin.cpp
index afde354e2d..b15abf8096 100644
--- a/tools/editor/plugins/script_editor_plugin.cpp
+++ b/tools/editor/plugins/script_editor_plugin.cpp
@@ -43,6 +43,83 @@
/*** SCRIPT EDITOR ****/
+class EditorScriptCodeCompletionCache : public ScriptCodeCompletionCache {
+
+
+ struct Cache {
+ uint64_t time_loaded;
+ RES cache;
+ };
+
+ Map<String,Cache> cached;
+
+
+public:
+
+ uint64_t max_time_cache;
+ int max_cache_size;
+
+ void cleanup() {
+
+ List< Map<String,Cache>::Element * > to_clean;
+
+
+ Map<String,Cache>::Element *I=cached.front();
+ while(I) {
+ if ((OS::get_singleton()->get_ticks_msec()-I->get().time_loaded)>max_time_cache) {
+ to_clean.push_back(I);
+ }
+ I=I->next();
+ }
+
+ while(to_clean.front()) {
+ cached.erase(to_clean.front()->get());
+ to_clean.pop_front();
+ }
+ }
+
+ RES get_cached_resource(const String& p_path) {
+
+ Map<String,Cache>::Element *E=cached.find(p_path);
+ if (!E) {
+
+ Cache c;
+ c.cache=ResourceLoader::load(p_path);
+ E=cached.insert(p_path,c);
+ }
+
+ E->get().time_loaded=OS::get_singleton()->get_ticks_msec();
+
+ if (cached.size()>max_cache_size) {
+ uint64_t older;
+ Map<String,Cache>::Element *O=cached.front();
+ older=O->get().time_loaded;
+ Map<String,Cache>::Element *I=O;
+ while(I) {
+ if (I->get().time_loaded<older) {
+ older = I->get().time_loaded;
+ O=I;
+ }
+ I=I->next();
+ }
+
+ if (O!=E) {//should never heppane..
+ cached.erase(O);
+ }
+ }
+
+ return E->get().cache;
+ }
+
+
+ EditorScriptCodeCompletionCache() {
+
+ max_cache_size=128;
+ max_time_cache=5*60*1000; //minutes, five
+ }
+
+};
+
#define SORT_SCRIPT_LIST
void ScriptEditorQuickOpen::popup(const Vector<String>& p_functions, bool p_dontclear) {
@@ -1694,6 +1771,7 @@ void ScriptEditor::get_window_layout(Ref<ConfigFile> p_layout) {
ScriptEditor::ScriptEditor(EditorNode *p_editor) {
+ completion_cache = memnew( EditorScriptCodeCompletionCache );
restoring_layout=false;
waiting_update_names=false;
editor=p_editor;
@@ -1874,6 +1952,11 @@ ScriptEditor::ScriptEditor(EditorNode *p_editor) {
}
+ScriptEditor::~ScriptEditor() {
+
+ memdelete(completion_cache);
+}
+
void ScriptEditorPlugin::edit(Object *p_object) {
if (!p_object->cast_to<Script>())
diff --git a/tools/editor/plugins/script_editor_plugin.h b/tools/editor/plugins/script_editor_plugin.h
index 74a7d7e3e5..0dd152cb25 100644
--- a/tools/editor/plugins/script_editor_plugin.h
+++ b/tools/editor/plugins/script_editor_plugin.h
@@ -106,6 +106,8 @@ public:
};
+class EditorScriptCodeCompletionCache;
+
class ScriptEditor : public VBoxContainer {
OBJ_TYPE(ScriptEditor, VBoxContainer );
@@ -191,6 +193,7 @@ class ScriptEditor : public VBoxContainer {
ScriptEditorQuickOpen *quick_open;
+ EditorScriptCodeCompletionCache *completion_cache;
void _editor_play();
void _editor_pause();
@@ -248,6 +251,7 @@ public:
void get_window_layout(Ref<ConfigFile> p_layout);
ScriptEditor(EditorNode *p_editor);
+ ~ScriptEditor();
};
class ScriptEditorPlugin : public EditorPlugin {