Revision: 15343
          
http://projects.blender.org/plugins/scmsvn/viewcvs.php?view=rev&root=bf-blender&revision=15343
Author:   quorn
Date:     2008-06-24 17:25:25 +0200 (Tue, 24 Jun 2008)

Log Message:
-----------
Text plugin basis with plugin for suggestions/completions. The suggest plugin 
works for imported global variables, methods, modules and module members. For 
example typing:

import Blender
from Blender import *
| <- cursor here suggests globals
Blender.Draw.gl| <- cursor here suggests all Draw members starting gl

Currently suggestions are listed in the console when the space is redrawn but 
will be presented as a menu-style list soon. Also to add are 
shortcut/activation keys to allow plugins to respond to certain key strokes.

Modified Paths:
--------------
    branches/soc-2008-quorn/source/blender/python/BPY_interface.c
    branches/soc-2008-quorn/source/blender/python/BPY_menus.c
    branches/soc-2008-quorn/source/blender/python/BPY_menus.h
    branches/soc-2008-quorn/source/blender/python/api2_2x/Text.c
    branches/soc-2008-quorn/source/blender/python/api2_2x/doc/Text.py
    branches/soc-2008-quorn/source/blender/src/drawtext.c
    branches/soc-2008-quorn/source/blender/src/header_text.c
    branches/soc-2008-quorn/source/blender/src/usiblender.c

Added 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

Added: branches/soc-2008-quorn/release/scripts/textplugin_suggest.py
===================================================================
--- branches/soc-2008-quorn/release/scripts/textplugin_suggest.py               
                (rev 0)
+++ branches/soc-2008-quorn/release/scripts/textplugin_suggest.py       
2008-06-24 15:25:25 UTC (rev 15343)
@@ -0,0 +1,234 @@
+#!BPY
+"""
+Name: 'Suggest'
+Blender: 243
+Group: 'TextPlugin'
+Tooltip: 'Suggests completions for the word at the cursor in a python script'
+"""
+
+import bpy
+from Blender  import Text
+from StringIO import StringIO
+from inspect  import *
+from tokenize import generate_tokens
+import token
+
+TK_TYPE  = 0
+TK_TOKEN = 1
+TK_START = 2 #(srow, scol)
+TK_END   = 3 #(erow, ecol)
+TK_LINE  = 4
+TK_ROW = 0
+TK_COL = 1
+
+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 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
+
+def isNameChar(s):
+       return s.isalnum() or s in ['_']
+
+# 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()
+       line = lines[l]
+       a=0
+       for a in range(1, c+1):
+               if not isNameChar(line[c-a]) and line[c-a]!='.':
+                       a -= 1
+                       break
+       return line[c-a:c].split('.')
+
+
+# Returns a list of tuples of symbol names and their types (name, type) where
+# type is one of:
+#   m (module/class)  Has its own members (includes classes)
+#   v (variable)      Has a type which may have its own members
+#   f (function)      Callable and may have a return type (with its own 
members)
+# It also updates the global import context (via execs)
+def getGlobals(txt):
+       global execs
+       
+       tokens = getTokens(txt)
+       globals = dict()
+       for i in range(len(tokens)):
+               
+               # Handle all import statements
+               if i>=1 and tokens[i-1][TK_TOKEN]=='import':
+                       
+                       # Find 'from' if it exists
+                       fr= -1
+                       for a in range(1, i):
+                               if tokens[i-a][TK_TYPE]==token.NEWLINE: break
+                               if tokens[i-a][TK_TOKEN]=='from':
+                                       fr=i-a
+                                       break
+                       
+                       # Handle: import ___[,___]
+                       if fr<0:
+                               
+                               while True:
+                                       if tokens[i][TK_TYPE]==token.NAME:
+                                               # Add the import to the execs 
list
+                                               x = tokens[i][TK_LINE].strip()
+                                               k = tokens[i][TK_TOKEN]
+                                               execs.append(x)
+                                               
+                                               # Add the symbol name to the 
return list
+                                               globals[k] = 'm'
+                                       elif tokens[i][TK_TOKEN]!=',':
+                                               break
+                                       i += 1
+                       
+                       # Handle statement: from ___[.___] import ___[,___]
+                       else: # fr>=0:
+                               
+                               # Add the import to the execs list
+                               x = tokens[i][TK_LINE].strip()
+                               execs.append(x)
+                               
+                               # 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
+                               
+                               # All submodules, functions, etc.
+                               if tokens[i][TK_TOKEN]=='*':
+                                       
+                                       # Add each symbol name to the return 
list
+                                       exec "d="+parent+".__dict__.items()"
+                                       for k,v in d:
+                                               if not globals.has_key(k) or 
not globals[k]:
+                                                       t='v'
+                                                       if ismodule(v): t='m'
+                                                       elif callable(v): t='f'
+                                                       globals[k] = t
+                               
+                               # Specific function, submodule, etc.
+                               else:
+                                       while True:
+                                               if 
tokens[i][TK_TYPE]==token.NAME:
+                                                       k = tokens[i][TK_TOKEN]
+                                                       if not 
globals.has_key(k) or not globals[k]:
+                                                               t='v'
+                                                               try:
+                                                                       exec 
'v='+parent+'.'+k
+                                                                       if 
ismodule(v): t='m'
+                                                                       elif 
callable(v): t='f'
+                                                               except: pass
+                                                               globals[k] = t
+                                               elif tokens[i][TK_TOKEN]!=',':
+                                                       break
+                                               i += 1
+                                       
+               elif tokens[i][TK_TYPE]==token.NAME and tokens[i][TK_TOKEN] not 
in keywords and (i==0 or tokens[i-1][TK_TOKEN]!='.'):
+                       k = tokens[i][TK_TOKEN]
+                       if not globals.has_key(k) or not globals[k]:
+                               t=None
+                               if (i>0 and tokens[i-1][TK_TOKEN]=='def'):
+                                       t='f'
+                               else:
+                                       t='v'
+                               globals[k] = t
+       
+       return globals
+
+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)
+
+# Only works for 'static' members (eg. Text.Get)
+def memberSuggest(txt, cs):
+       global execs
+       
+       # Populate the execs for imports
+       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'
+       
+       suggestions = dict()
+       (row, col) = txt.getCursorPos()
+       
+       sub = cs[len(cs)-1].lower()
+       print 'Search:', sub
+       
+       t=None
+       pre='.'.join(cs[:-1])
+       try:
+               exec "t="+pre
+       except:
+               print 'Failed to assign '+pre
+               print execs
+               print cs
+       
+       if t!=None:
+               for k,v in t.__dict__.items():
+                       if ismodule(v): t='m'
+                       elif callable(v): t='f'
+                       else: t='v'
+                       if k.lower().startswith(sub):
+                               suggestions[k] = t
+       
+       l = list(suggestions.items())
+       return sorted (l, cmp=cmpi0)
+
+def main():
+       txt = bpy.data.texts.active
+       if txt==None: return
+       
+       cs = getCompletionSymbols(txt)
+       
+       if len(cs)<=1:
+               l = globalSuggest(txt, cs)
+               txt.suggest(l, cs[len(cs)-1])
+               
+       else:
+               l = memberSuggest(txt, cs)
+               txt.suggest(l, cs[len(cs)-1])
+
+main()

Added: branches/soc-2008-quorn/source/blender/blenkernel/BKE_suggestions.h
===================================================================
--- branches/soc-2008-quorn/source/blender/blenkernel/BKE_suggestions.h         
                (rev 0)
+++ branches/soc-2008-quorn/source/blender/blenkernel/BKE_suggestions.h 
2008-06-24 15:25:25 UTC (rev 15343)
@@ -0,0 +1,77 @@
+/**    
+ * $Id: $ 
+ *
+ * ***** BEGIN GPL LICENSE BLOCK *****
+ *
+ * This program is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU General Public License
+ * as published by the Free Software Foundation; either version 2
+ * of the License, or (at your option) any later version.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
+ * GNU General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with this program; if not, write to the Free Software Foundation,
+ * Inc., 59 Temple Place - Suite 330, Boston, MA  02111-1307, USA.
+ *
+ * The Original Code is Copyright (C) 2008, Blender Foundation
+ * All rights reserved.
+ *
+ * The Original Code is: all of this file.
+ *
+ * Contributor(s): none yet.
+ *
+ * ***** END GPL LICENSE BLOCK *****
+ */
+#ifndef BKE_SUGGESTIONS_H
+#define BKE_SUGGESTIONS_H
+
+#ifdef __cplusplus
+extern "C" {
+#endif
+
+/* ****************************************************************************
+Suggestions must be added in sorted order (no attempt is made to sort the list)
+The list is then divided up based on the prefix provided by update_suggestions:
+Example:
+  Prefix: ab
+  aaa <-- first
+  aab
+  aba <-- firstmatch
+  abb <-- lastmatch
+  baa
+  bab <-- last
+**************************************************************************** */
+
+struct Text;
+
+typedef struct SuggItem {
+       struct SuggItem *prev, *next;
+       char *name;
+       char type;
+} SuggItem;
+
+typedef struct SuggList {
+       SuggItem *first, *last;
+       SuggItem *firstmatch, *lastmatch;
+} SuggList;
+
+void free_suggestions();
+
+void add_suggestion(const char *name, char type);
+void update_suggestions(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);
+
+#ifdef __cplusplus
+}
+#endif
+
+#endif

Added: branches/soc-2008-quorn/source/blender/blenkernel/intern/suggestions.c
===================================================================
--- branches/soc-2008-quorn/source/blender/blenkernel/intern/suggestions.c      
                        (rev 0)
+++ branches/soc-2008-quorn/source/blender/blenkernel/intern/suggestions.c      
2008-06-24 15:25:25 UTC (rev 15343)
@@ -0,0 +1,125 @@
+/**
+ * $Id: $
+ *
+ * ***** BEGIN GPL LICENSE BLOCK *****
+ *
+ * This program is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU General Public License
+ * as published by the Free Software Foundation; either version 2
+ * of the License, or (at your option) any later version.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
+ * GNU General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with this program; if not, write to the Free Software Foundation,
+ * Inc., 59 Temple Place - Suite 330, Boston, MA  02111-1307, USA.
+ *
+ * The Original Code is Copyright (C) 2008, Blender Foundation
+ * All rights reserved.
+ *
+ * The Original Code is: all of this file.
+ *
+ * Contributor(s): none yet.
+ *
+ * ***** END GPL LICENSE BLOCK *****
+ */
+
+#include <stdlib.h>
+#include <string.h>
+
+#include "MEM_guardedalloc.h"
+#include "BLI_blenlib.h"
+#include "DNA_text_types.h"
+#include "BKE_text.h"
+#include "BKE_suggestions.h"
+
+static SuggList suggestions= {NULL, NULL, NULL, NULL};
+static Text *suggText = NULL;
+
+void free_suggestions() {
+       SuggItem *item;
+       for (item = suggestions.last; item; item=item->prev)
+               MEM_freeN(item);
+       suggestions.first = suggestions.last = NULL;
+       suggestions.firstmatch = suggestions.lastmatch = NULL;
+}
+

@@ Diff output truncated at 10240 characters. @@

_______________________________________________
Bf-blender-cvs mailing list
[email protected]
http://lists.blender.org/mailman/listinfo/bf-blender-cvs

Reply via email to