[Bf-blender-cvs] SVN commit: /data/svn/bf-blender [15343] branches/soc-2008-quorn: Text plugin basis with plugin for suggestions/completions.

Ian Thompson quornian at googlemail.com
Tue Jun 24 17:25:32 CEST 2008


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




More information about the Bf-blender-cvs mailing list