[Bf-blender-cvs] SVN commit: /data/svn/bf-blender [15351] branches/soc-2008-quorn: Added UI for suggestions list.

Ian Thompson quornian at googlemail.com
Wed Jun 25 15:52:47 CEST 2008


Revision: 15351
          http://projects.blender.org/plugins/scmsvn/viewcvs.php?view=rev&root=bf-blender&revision=15351
Author:   quorn
Date:     2008-06-25 15:51:54 +0200 (Wed, 25 Jun 2008)

Log Message:
-----------
Added UI for suggestions list. Works with arrow-keys and mouse wheel, accept with Enter, reject with Esc or click elsewhere. Mouse selection not yet supported. The script is called from the File->Text Plugins menu.

Tidied python script, the C suggestions functions and fixed some bugs including suggestions not being freed properly.

Modified Paths:
--------------
    branches/soc-2008-quorn/release/scripts/textplugin_suggest.py
    branches/soc-2008-quorn/source/blender/blenkernel/BKE_suggestions.h
    branches/soc-2008-quorn/source/blender/blenkernel/intern/suggestions.c
    branches/soc-2008-quorn/source/blender/python/api2_2x/Text.c
    branches/soc-2008-quorn/source/blender/src/drawtext.c

Modified: branches/soc-2008-quorn/release/scripts/textplugin_suggest.py
===================================================================
--- branches/soc-2008-quorn/release/scripts/textplugin_suggest.py	2008-06-25 13:21:38 UTC (rev 15350)
+++ branches/soc-2008-quorn/release/scripts/textplugin_suggest.py	2008-06-25 13:51:54 UTC (rev 15351)
@@ -6,12 +6,11 @@
 Tooltip: 'Suggests completions for the word at the cursor in a python script'
 """
 
-import bpy
+import bpy, __builtin__, token
 from Blender  import Text
 from StringIO import StringIO
 from inspect  import *
 from tokenize import generate_tokens
-import token
 
 TK_TYPE  = 0
 TK_TOKEN = 1
@@ -21,32 +20,48 @@
 TK_ROW = 0
 TK_COL = 1
 
+execs = [] # Used to establish the same import context across defs
+
 keywords = ['and', 'del', 'from', 'not', 'while', 'as', 'elif', 'global',
 			'or', 'with', 'assert', 'else', 'if', 'pass', 'yield',
 			'break', 'except', 'import', 'print', 'class', 'exec', 'in',
 			'raise', 'continue', 'finally', 'is', 'return', 'def', 'for',
 			'lambda', 'try' ]
 
-execs = [] # Used to establish the same import context across defs (import is scope sensitive)
 
+def getBuiltins():
+	builtins = []
+	bi = dir(__builtin__)
+	for k in bi:
+		v = eval(k)
+		if ismodule(v): t='m'
+		elif callable(v): t='f'
+		else: t='v'
+		builtins.append((k, t))
+	return builtins
+
+
+def getKeywords():
+	global keywords
+	return [(k, 'k') for k in keywords]
+
+
 def getTokens(txt):
-	global tokens_cached
-	if tokens_cached==None:
-		lines = txt.asLines()
-		str = '\n'.join(lines)
-		readline = StringIO(str).readline
-		g = generate_tokens(readline)
-		tokens = []
-		for t in g: tokens.append(t)
-		tokens_cached = tokens
-	return tokens_cached
-tokens_cached = None
+	lines = txt.asLines()
+	str = '\n'.join(lines)
+	readline = StringIO(str).readline
+	g = generate_tokens(readline)
+	tokens = []
+	for t in g: tokens.append(t)
+	return tokens
 
+
 def isNameChar(s):
-	return s.isalnum() or s in ['_']
+	return (s.isalnum() or s == '_')
 
-# Returns words preceding the cursor that are separated by periods as a list in the
-# same order
+
+# Returns words preceding the cursor that are separated by periods as a list in
+# the same order
 def getCompletionSymbols(txt):
 	(l, c)= txt.getCursorPos()
 	lines = txt.asLines()
@@ -68,7 +83,14 @@
 def getGlobals(txt):
 	global execs
 	
-	tokens = getTokens(txt)
+	# Unfortunately, tokenize may fail if the script leaves brackets or strings
+	# open. For now we return an empty list, leaving builtins and keywords as
+	# the only globals. (on the TODO list)
+	try:
+		tokens = getTokens(txt)
+	except:
+		return []
+	
 	globals = dict()
 	for i in range(len(tokens)):
 		
@@ -92,6 +114,7 @@
 						x = tokens[i][TK_LINE].strip()
 						k = tokens[i][TK_TOKEN]
 						execs.append(x)
+						exec 'try: '+x+'\nexcept: pass'
 						
 						# Add the symbol name to the return list
 						globals[k] = 'm'
@@ -105,16 +128,17 @@
 				# Add the import to the execs list
 				x = tokens[i][TK_LINE].strip()
 				execs.append(x)
+				exec 'try: '+x+'\nexcept: pass'
 				
 				# Import parent module so we can process it for sub modules
 				parent = ''.join([t[TK_TOKEN] for t in tokens[fr+1:i-1]])
-				exec "import "+parent
+				exec 'try: import '+parent+'\nexcept: pass'
 				
 				# All submodules, functions, etc.
 				if tokens[i][TK_TOKEN]=='*':
 					
 					# Add each symbol name to the return list
-					exec "d="+parent+".__dict__.items()"
+					d = eval(parent).__dict__.items()
 					for k,v in d:
 						if not globals.has_key(k) or not globals[k]:
 							t='v'
@@ -130,7 +154,7 @@
 							if not globals.has_key(k) or not globals[k]:
 								t='v'
 								try:
-									exec 'v='+parent+'.'+k
+									v = eval(parent+'.'+k)
 									if ismodule(v): t='m'
 									elif callable(v): t='f'
 								except: pass
@@ -149,36 +173,14 @@
 					t='v'
 				globals[k] = t
 	
-	return globals
+	return globals.items()
 
-def cmpi0(x, y):
-	return cmp(x[0].lower(), y[0].lower())
 
 def globalSuggest(txt, cs):
-	global execs
-	
-	suggestions = dict()
-	(row, col) = txt.getCursorPos()
 	globals = getGlobals(txt)
-	
-	# Sometimes we have conditional includes which will fail if the module
-	# cannot be found. So we protect outselves in a try block
-	for x in execs:
-		exec 'try: '+x+'\nexcept: pass'
-	
-	if len(cs)==0:
-		sub = ''
-	else:
-		sub = cs[0].lower()
-	print 'Search:', sub
-	
-	for k,t in globals.items():
-		if k.lower().startswith(sub):
-			suggestions[k] = t
-	
-	l = list(suggestions.items())
-	return sorted (l, cmp=cmpi0)
+	return globals
 
+
 # Only works for 'static' members (eg. Text.Get)
 def memberSuggest(txt, cs):
 	global execs
@@ -194,29 +196,29 @@
 	suggestions = dict()
 	(row, col) = txt.getCursorPos()
 	
-	sub = cs[len(cs)-1].lower()
-	print 'Search:', sub
+	sub = cs[len(cs)-1]
 	
-	t=None
+	m=None
 	pre='.'.join(cs[:-1])
 	try:
-		exec "t="+pre
+		m = eval(pre)
 	except:
-		print 'Failed to assign '+pre
-		print execs
-		print cs
+		print pre+ ' not found or not imported.'
 	
-	if t!=None:
-		for k,v in t.__dict__.items():
+	if m!=None:
+		for k,v in m.__dict__.items():
 			if ismodule(v): t='m'
 			elif callable(v): t='f'
 			else: t='v'
-			if k.lower().startswith(sub):
-				suggestions[k] = t
+			suggestions[k] = t
 	
-	l = list(suggestions.items())
-	return sorted (l, cmp=cmpi0)
+	return suggestions.items()
 
+
+def cmp0(x, y):
+	return cmp(x[0], y[0])
+
+
 def main():
 	txt = bpy.data.texts.active
 	if txt==None: return
@@ -225,10 +227,12 @@
 	
 	if len(cs)<=1:
 		l = globalSuggest(txt, cs)
-		txt.suggest(l, cs[len(cs)-1])
-		
+		l.extend(getBuiltins())
+		l.extend(getKeywords())
 	else:
 		l = memberSuggest(txt, cs)
-		txt.suggest(l, cs[len(cs)-1])
+	
+	l.sort(cmp=cmp0)
+	txt.suggest(l, cs[len(cs)-1])
 
 main()

Modified: branches/soc-2008-quorn/source/blender/blenkernel/BKE_suggestions.h
===================================================================
--- branches/soc-2008-quorn/source/blender/blenkernel/BKE_suggestions.h	2008-06-25 13:21:38 UTC (rev 15350)
+++ branches/soc-2008-quorn/source/blender/blenkernel/BKE_suggestions.h	2008-06-25 13:51:54 UTC (rev 15351)
@@ -57,19 +57,23 @@
 typedef struct SuggList {
 	SuggItem *first, *last;
 	SuggItem *firstmatch, *lastmatch;
+	SuggItem *selected;
 } SuggList;
 
 void free_suggestions();
 
-void add_suggestion(const char *name, char type);
-void update_suggestions(const char *prefix);
+void suggest_add(const char *name, char type);
+void suggest_prefix(const char *prefix);
 SuggItem *suggest_first();
 SuggItem *suggest_last();
 
-void set_suggest_text(Text *text);
-void clear_suggest_text();
-short is_suggest_active(Text *text);
+void suggest_set_text(Text *text);
+void suggest_clear_text();
+short suggest_is_active(Text *text);
 
+void suggest_set_selected(SuggItem *sel);
+SuggItem *suggest_get_selected();
+
 #ifdef __cplusplus
 }
 #endif

Modified: branches/soc-2008-quorn/source/blender/blenkernel/intern/suggestions.c
===================================================================
--- branches/soc-2008-quorn/source/blender/blenkernel/intern/suggestions.c	2008-06-25 13:21:38 UTC (rev 15350)
+++ branches/soc-2008-quorn/source/blender/blenkernel/intern/suggestions.c	2008-06-25 13:51:54 UTC (rev 15351)
@@ -40,14 +40,17 @@
 static Text *suggText = NULL;
 
 void free_suggestions() {
-	SuggItem *item;
-	for (item = suggestions.last; item; item=item->prev)
+	SuggItem *item, *prev;
+	for (item = suggestions.last; item; item=prev) {
+		prev = item->prev;
 		MEM_freeN(item);
+	}
 	suggestions.first = suggestions.last = NULL;
 	suggestions.firstmatch = suggestions.lastmatch = NULL;
+	suggestions.selected = NULL;
 }
 
-void add_suggestion(const char *name, char type) {
+void suggest_add(const char *name, char type) {
 	SuggItem *newitem;
 
 	newitem = MEM_mallocN(sizeof(SuggItem) + strlen(name) + 1, "SuggestionItem");
@@ -63,6 +66,7 @@
 
 	if (!suggestions.first) {
 		suggestions.first = suggestions.last = newitem;
+		suggestions.selected = newitem;
 	} else {
 		newitem->prev = suggestions.last;
 		suggestions.last->next = newitem;
@@ -70,13 +74,13 @@
 	}
 }
 
-void update_suggestions(const char *prefix) {
+void suggest_prefix(const char *prefix) {
 	SuggItem *match, *first, *last;
 	int cmp, len = strlen(prefix);
 
 	if (!suggestions.first) return;
 	if (len==0) {
-		suggestions.firstmatch = suggestions.first;
+		suggestions.selected = suggestions.firstmatch = suggestions.first;
 		suggestions.lastmatch = suggestions.last;
 		return;
 	}
@@ -96,10 +100,10 @@
 	}
 	if (first) {
 		if (!last) last = suggestions.last;
-		suggestions.firstmatch = first;
+		suggestions.selected = suggestions.firstmatch = first;
 		suggestions.lastmatch = last;
 	} else {
-		suggestions.firstmatch = suggestions.lastmatch = NULL;
+		suggestions.selected = suggestions.firstmatch = suggestions.lastmatch = NULL;
 	}
 }
 
@@ -111,15 +115,23 @@
 	return suggestions.lastmatch;
 }
 
-void set_suggest_text(Text *text) {
+void suggest_set_text(Text *text) {
 	suggText = text;
 }
 
-void clear_suggest_text() {
+void suggest_clear_text() {
 	free_suggestions();
 	suggText = NULL;
 }
 
-short is_suggest_active(Text *text) {
+short suggest_is_active(Text *text) {
 	return suggText==text ? 1 : 0;
 }
+
+void suggest_set_selected(SuggItem *sel) {
+	suggestions.selected = sel;
+}
+
+SuggItem *suggest_get_selected() {
+	return suggestions.selected;
+}

Modified: branches/soc-2008-quorn/source/blender/python/api2_2x/Text.c
===================================================================
--- branches/soc-2008-quorn/source/blender/python/api2_2x/Text.c	2008-06-25 13:21:38 UTC (rev 15350)
+++ branches/soc-2008-quorn/source/blender/python/api2_2x/Text.c	2008-06-25 13:51:54 UTC (rev 15351)
@@ -129,7 +129,7 @@
 	{"setCursorPos", ( PyCFunction ) Text_setCursorPos, METH_VARARGS,
 	 "(row, col) - Set the cursor position to (row, col)"},
 	{"suggest", ( PyCFunction ) Text_suggest, METH_VARARGS,
-	 "(list) - List of tuples of the form (name, type) where type is one of 'm', 'v', 'f' for module, variable and function respectively"},
+	 "(list) - List of tuples of the form (name, type) where type is one of 'm', 'v', 'f', 'k' for module, variable, function and keyword respectively"},
 	{NULL, NULL, 0, NULL}
 };
 
@@ -544,7 +544,7 @@
 				"Active text area has no Text object");
 	
 	list_len = PyList_Size(list);
-	clear_suggest_text();
+	suggest_clear_text();
 	
 	for (i = 0; i < list_len; i++) {
 		item = PyList_GetItem(list, i);
@@ -555,14 +555,14 @@
 		name = PyString_AsString(PyTuple_GetItem(item, 0));

@@ Diff output truncated at 10240 characters. @@




More information about the Bf-blender-cvs mailing list