From e9bbb97acccc08ae03fde41e4cc6d2dc6722021a Mon Sep 17 00:00:00 2001 From: Juan Linietsky Date: Mon, 22 Jun 2015 00:03:19 -0300 Subject: Multiple scene editing *POTENTIALLY UNSTABLE* -ability to edit multiple scenes at the same time -resource internal IDs are now persistent, this makes multiple scene editing possible but maaaaay result in file corruption bugs (tested and could not find anything but possibility exists because core code changed, report immediately if you find this). -properly save settings, layout, etc when edited -script editing is independent from scene editing now -show a yellow box when a script belongs to the scene --- modules/gdscript/gd_editor.cpp | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) (limited to 'modules/gdscript/gd_editor.cpp') diff --git a/modules/gdscript/gd_editor.cpp b/modules/gdscript/gd_editor.cpp index f59dbd91d..0edfd6b9a 100644 --- a/modules/gdscript/gd_editor.cpp +++ b/modules/gdscript/gd_editor.cpp @@ -1195,7 +1195,7 @@ static void _find_identifiers(GDCompletionContext& context,int p_line,bool p_onl } static const char*_type_names[Variant::VARIANT_MAX]={ - "null","bool","int","float","String","Vector2","Rect2","Vector3","Matrix32","Plane","Quat","AABB","Matrix3","Trasnform", + "null","bool","int","float","String","Vector2","Rect2","Vector3","Matrix32","Plane","Quat","AABB","Matrix3","Transform", "Color","Image","NodePath","RID","Object","InputEvent","Dictionary","Array","RawArray","IntArray","FloatArray","StringArray", "Vector2Array","Vector3Array","ColorArray"}; -- cgit v1.2.3-70-g09d2 From a67486a39ee629acac068a6d014015944cf83bb3 Mon Sep 17 00:00:00 2001 From: Juan Linietsky Date: Fri, 26 Jun 2015 01:14:31 -0300 Subject: improved get_node(), connect(), etc code completion. -properly completes text arguments -includes the "/root" autoloads --- core/script_language.cpp | 8 +++ core/script_language.h | 13 +++++ modules/gdscript/gd_editor.cpp | 26 ++++++++- modules/gdscript/gd_parser.cpp | 21 ++++++- modules/gdscript/gd_parser.h | 3 +- modules/gdscript/gd_tokenizer.cpp | 33 ++++++----- scene/gui/text_edit.cpp | 42 ++++++++++++-- tools/editor/editor_node.cpp | 4 +- tools/editor/plugins/script_editor_plugin.cpp | 83 +++++++++++++++++++++++++++ tools/editor/plugins/script_editor_plugin.h | 4 ++ 10 files changed, 211 insertions(+), 26 deletions(-) (limited to 'modules/gdscript/gd_editor.cpp') diff --git a/core/script_language.cpp b/core/script_language.cpp index 68ac7d0ae..35c50b102 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 c1b906e25..7104fe454 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 0edfd6b9a..c374f509c 100644 --- a/modules/gdscript/gd_editor.cpp +++ b/modules/gdscript/gd_editor.cpp @@ -381,7 +381,12 @@ static Ref _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 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 props; + Globals::get_singleton()->get_property_list(&props); + + for(List::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::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),"")); 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 options; diff --git a/modules/gdscript/gd_parser.cpp b/modules/gdscript/gd_parser.cpp index 71af9ab10..afe8c9aa7 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 T* GDParser::alloc_node() { @@ -116,6 +117,14 @@ bool GDParser::_parse_arguments(Node* p_parent,Vector& 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 &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 &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 fd5c0802e..fd8a2576f 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 &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 656a015c6..2844edfdd 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 c497bc536..44e97b588 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 9f3c57b0d..7b44f2cad 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 sdata = ResourceLoader::load(lpath); + Ref 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 afde354e2..b15abf809 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 cached; + + +public: + + uint64_t max_time_cache; + int max_cache_size; + + void cleanup() { + + List< Map::Element * > to_clean; + + + Map::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::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::Element *O=cached.front(); + older=O->get().time_loaded; + Map::Element *I=O; + while(I) { + if (I->get().time_loadedget().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& p_functions, bool p_dontclear) { @@ -1694,6 +1771,7 @@ void ScriptEditor::get_window_layout(Ref 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