improved get_node(), connect(), etc code completion.

-properly completes text arguments
-includes the "/root" autoloads
This commit is contained in:
Juan Linietsky 2015-06-26 01:14:31 -03:00
parent 71cc2561c6
commit a67486a39e
10 changed files with 211 additions and 26 deletions

View File

@ -136,6 +136,14 @@ ScriptInstance::~ScriptInstance() {
}
ScriptCodeCompletionCache *ScriptCodeCompletionCache::singleton=NULL;
ScriptCodeCompletionCache::ScriptCodeCompletionCache() {
singleton=this;
}
void ScriptLanguage::frame() {

View File

@ -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:

View File

@ -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;

View File

@ -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);

View File

@ -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;

View File

@ -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;
}
}

View File

@ -3323,9 +3323,32 @@ void TextEdit::_update_completion_candidates() {
//look for keywords first
bool pre_keyword=false;
bool inquote=false;
int first_quote=-1;
if (cofs>0 && l[cofs-1]==' ') {
int c=cofs-1;
while(c>=0) {
if (l[c]=='"' || l[c]=='\'') {
inquote=!inquote;
if (first_quote==-1)
first_quote=c;
}
c--;
}
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");
}

View File

@ -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) {

View File

@ -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>())

View File

@ -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 {