[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