[Bf-blender-cvs] SVN commit: /data/svn/bf-blender [16330] trunk/blender: Text Editor ( GSOC 2008)

Ian Thompson quornian at googlemail.com
Mon Sep 1 16:04:22 CEST 2008


Revision: 16330
          http://projects.blender.org/plugins/scmsvn/viewcvs.php?view=rev&root=bf-blender&revision=16330
Author:   quorn
Date:     2008-09-01 16:04:22 +0200 (Mon, 01 Sep 2008)

Log Message:
-----------
Text Editor (GSOC 2008)
=======================
Merge of branch soc-2008-quorn to trunk:
Merged 14970:16308 to trunk at 16307, updated to HEAD.
Merged 16318

Main features from this branch:
 - Python text plugins
 - Suggestions and documentation elements
 - Improved syntax highlighting
 - Word wrap
 - Additional editing tools
 - Various undo and clipboard fixes
 - File header info and modification checks

Modified Paths:
--------------
    trunk/blender/intern/ghost/intern/GHOST_SystemWin32.cpp
    trunk/blender/release/datafiles/blenderbuttons
    trunk/blender/source/blender/blenkernel/BKE_text.h
    trunk/blender/source/blender/blenkernel/intern/text.c
    trunk/blender/source/blender/blenloader/intern/readfile.c
    trunk/blender/source/blender/blenloader/intern/writefile.c
    trunk/blender/source/blender/include/BIF_drawtext.h
    trunk/blender/source/blender/include/BIF_keyval.h
    trunk/blender/source/blender/include/BIF_resources.h
    trunk/blender/source/blender/include/BIF_space.h
    trunk/blender/source/blender/include/blendef.h
    trunk/blender/source/blender/makesdna/DNA_space_types.h
    trunk/blender/source/blender/makesdna/DNA_text_types.h
    trunk/blender/source/blender/python/BPY_extern.h
    trunk/blender/source/blender/python/BPY_interface.c
    trunk/blender/source/blender/python/BPY_menus.c
    trunk/blender/source/blender/python/BPY_menus.h
    trunk/blender/source/blender/python/api2_2x/Text.c
    trunk/blender/source/blender/python/api2_2x/Text.h
    trunk/blender/source/blender/python/api2_2x/doc/Draw.py
    trunk/blender/source/blender/python/api2_2x/doc/Text.py
    trunk/blender/source/blender/src/blenderbuttons.c
    trunk/blender/source/blender/src/drawtext.c
    trunk/blender/source/blender/src/header_text.c
    trunk/blender/source/blender/src/keyval.c
    trunk/blender/source/blender/src/space.c
    trunk/blender/source/blender/src/toolbox.c
    trunk/blender/source/blender/src/usiblender.c

Added Paths:
-----------
    trunk/blender/release/scripts/bpymodules/BPyTextPlugin.py
    trunk/blender/release/scripts/scripttemplate_text_plugin.py
    trunk/blender/release/scripts/textplugin_functiondocs.py
    trunk/blender/release/scripts/textplugin_imports.py
    trunk/blender/release/scripts/textplugin_membersuggest.py
    trunk/blender/release/scripts/textplugin_outliner.py
    trunk/blender/release/scripts/textplugin_suggest.py
    trunk/blender/release/scripts/textplugin_templates.py
    trunk/blender/source/blender/blenkernel/BKE_suggestions.h
    trunk/blender/source/blender/blenkernel/intern/suggestions.c

Modified: trunk/blender/intern/ghost/intern/GHOST_SystemWin32.cpp
===================================================================
--- trunk/blender/intern/ghost/intern/GHOST_SystemWin32.cpp	2008-09-01 13:04:50 UTC (rev 16329)
+++ trunk/blender/intern/ghost/intern/GHOST_SystemWin32.cpp	2008-09-01 14:04:22 UTC (rev 16330)
@@ -917,8 +917,12 @@
 	char *buffer;
 	char *temp_buff;
 	
-	if ( OpenClipboard(NULL) ) {
+	if ( IsClipboardFormatAvailable(CF_TEXT) && OpenClipboard(NULL) ) {
 		HANDLE hData = GetClipboardData( CF_TEXT );
+		if (hData == NULL) {
+			CloseClipboard();
+			return NULL;
+		}
 		buffer = (char*)GlobalLock( hData );
 		
 		temp_buff = (char*) malloc(strlen(buffer)+1);

Modified: trunk/blender/release/datafiles/blenderbuttons
===================================================================
(Binary files differ)

Added: trunk/blender/release/scripts/bpymodules/BPyTextPlugin.py
===================================================================
--- trunk/blender/release/scripts/bpymodules/BPyTextPlugin.py	                        (rev 0)
+++ trunk/blender/release/scripts/bpymodules/BPyTextPlugin.py	2008-09-01 14:04:22 UTC (rev 16330)
@@ -0,0 +1,814 @@
+"""The BPyTextPlugin Module
+
+Use get_cached_descriptor(txt) to retrieve information about the script held in
+the txt Text object.
+
+Use print_cache_for(txt) to print the information to the console.
+
+Use line, cursor = current_line(txt) to get the logical line and cursor position
+
+Use get_targets(line, cursor) to find out what precedes the cursor:
+	aaa.bbb.cc|c.ddd -> ['aaa', 'bbb', 'cc']
+
+Use resolve_targets(txt, targets) to turn a target list into a usable object if
+one is found to match.
+"""
+
+import bpy, sys, os
+import __builtin__, tokenize
+from Blender.sys import time
+from tokenize import generate_tokens, TokenError, \
+		COMMENT, DEDENT, INDENT, NAME, NEWLINE, NL, STRING, NUMBER
+
+class Definition():
+	"""Describes a definition or defined object through its name, line number
+	and docstring. This is the base class for definition based descriptors.
+	"""
+	
+	def __init__(self, name, lineno, doc=''):
+		self.name = name
+		self.lineno = lineno
+		self.doc = doc
+
+class ScriptDesc():
+	"""Describes a script through lists of further descriptor objects (classes,
+	defs, vars) and dictionaries to built-in types (imports). If a script has
+	not been fully parsed, its incomplete flag will be set. The time of the last
+	parse is held by the time field and the name of the text object from which
+	it was parsed, the name field.
+	"""
+	
+	def __init__(self, name, imports, classes, defs, vars, incomplete=False):
+		self.name = name
+		self.imports = imports
+		self.classes = classes
+		self.defs = defs
+		self.vars = vars
+		self.incomplete = incomplete
+		self.parse_due = 0
+	
+	def set_delay(self, delay):
+		self.parse_due = time() + delay
+
+class ClassDesc(Definition):
+	"""Describes a class through lists of further descriptor objects (defs and
+	vars). The name of the class is held by the name field and the line on
+	which it is defined is held in lineno.
+	"""
+	
+	def __init__(self, name, parents, defs, vars, lineno, doc=''):
+		Definition.__init__(self, name, lineno, doc)
+		self.parents = parents
+		self.defs = defs
+		self.vars = vars
+
+class FunctionDesc(Definition):
+	"""Describes a function through its name and list of parameters (name,
+	params) and the line on which it is defined (lineno).
+	"""
+	
+	def __init__(self, name, params, lineno, doc=''):
+		Definition.__init__(self, name, lineno, doc)
+		self.params = params
+
+class VarDesc(Definition):
+	"""Describes a variable through its name and type (if ascertainable) and the
+	line on which it is defined (lineno). If no type can be determined, type
+	will equal None.
+	"""
+	
+	def __init__(self, name, type, lineno):
+		Definition.__init__(self, name, lineno)
+		self.type = type # None for unknown (supports: dict/list/str)
+
+# Context types
+CTX_UNSET = -1
+CTX_NORMAL = 0
+CTX_SINGLE_QUOTE = 1
+CTX_DOUBLE_QUOTE = 2
+CTX_COMMENT = 3
+
+# Python keywords
+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' ]
+
+# Module file extensions
+MODULE_EXTS = ['.py', '.pyc', '.pyo', '.pyw', '.pyd']
+
+ModuleType = type(__builtin__)
+NoneScriptDesc = ScriptDesc('', dict(), dict(), dict(), dict(), True)
+
+_modules = {}
+_modules_updated = 0
+_parse_cache = dict()
+
+def _load_module_names():
+	"""Searches the sys.path for module files and lists them, along with
+	sys.builtin_module_names, in the global dict _modules.
+	"""
+	
+	global _modules
+	
+	for n in sys.builtin_module_names:
+		_modules[n] = None
+	for p in sys.path:
+		if p == '': p = os.curdir
+		if not os.path.isdir(p): continue
+		for f in os.listdir(p):
+			for ext in MODULE_EXTS:
+				if f.endswith(ext):
+					_modules[f[:-len(ext)]] = None
+					break
+
+_load_module_names()
+
+def _trim_doc(doc):
+	"""Trims the quotes from a quoted STRING token (eg. "'''text'''" -> "text")
+	"""
+	
+	l = len(doc)
+	i = 0
+	while i < l/2 and (doc[i] == "'" or doc[i] == '"'):
+		i += 1
+	return doc[i:-i]
+
+def resolve_targets(txt, targets):
+	"""Attempts to return a useful object for the locally or externally defined
+	entity described by targets. If the object is local (defined in txt), a
+	Definition instance is returned. If the object is external (imported or
+	built in), the object itself is returned. If no object can be found, None is
+	returned.
+	"""
+	
+	count = len(targets)
+	if count==0: return None
+	
+	obj = None
+	local = None
+	i = 1
+	
+	desc = get_cached_descriptor(txt)
+	b = targets[0].find('(')
+	if b==-1: b = None # Trick to let us use [:b] and get the whole string
+	
+	if desc.classes.has_key(targets[0][:b]):
+		local = desc.classes[targets[0][:b]]
+	elif desc.defs.has_key(targets[0]):
+		local = desc.defs[targets[0]]
+	elif desc.vars.has_key(targets[0]):
+		obj = desc.vars[targets[0]].type
+	
+	if local:
+		while i < count:
+			b = targets[i].find('(')
+			if b==-1: b = None
+			if hasattr(local, 'classes') and local.classes.has_key(targets[i][:b]):
+				local = local.classes[targets[i][:b]]
+			elif hasattr(local, 'defs') and local.defs.has_key(targets[i]):
+				local = local.defs[targets[i]]
+			elif hasattr(local, 'vars') and local.vars.has_key(targets[i]):
+				obj = local.vars[targets[i]].type
+				local = None
+				i += 1
+				break
+			else:
+				local = None
+				break
+			i += 1
+	
+	if local: return local
+	
+	if not obj:
+		if desc.imports.has_key(targets[0]):
+			obj = desc.imports[targets[0]]
+		else:
+			builtins = get_builtins()
+			if builtins.has_key(targets[0]):
+				obj = builtins[targets[0]]
+	
+	while obj and i < count:
+		if hasattr(obj, targets[i]):
+			obj = getattr(obj, targets[i])
+		else:
+			obj = None
+			break
+		i += 1
+	
+	return obj
+
+def get_cached_descriptor(txt, force_parse=0):
+	"""Returns the cached ScriptDesc for the specified Text object 'txt'. If the
+	script has not been parsed in the last 'period' seconds it will be reparsed
+	to obtain this descriptor.
+	
+	Specifying TP_AUTO for the period (default) will choose a period based on the
+	size of the Text object. Larger texts are parsed less often.
+	"""
+	
+	global _parse_cache
+	
+	parse = True
+	key = hash(txt)
+	if not force_parse and _parse_cache.has_key(key):
+		desc = _parse_cache[key]
+		if desc.parse_due > time():
+			parse = desc.incomplete
+	
+	if parse:
+		desc = parse_text(txt)
+	
+	return desc
+
+def parse_text(txt):
+	"""Parses an entire script's text and returns a ScriptDesc instance
+	containing information about the script.
+	
+	If the text is not a valid Python script (for example if brackets are left
+	open), parsing may fail to complete. However, if this occurs, no exception
+	is thrown. Instead the returned ScriptDesc instance will have its incomplete
+	flag set and information processed up to this point will still be accessible.
+	"""
+	
+	start_time = time()
+	txt.reset()
+	tokens = generate_tokens(txt.readline) # Throws TokenError
+	
+	curl, cursor = txt.getCursorPos()
+	linen = curl + 1 # Token line numbers are one-based
+	
+	imports = dict()
+	imp_step = 0
+	
+	classes = dict()
+	cls_step = 0
+	
+	defs = dict()
+	def_step = 0
+	
+	vars = dict()
+	var1_step = 0
+	var2_step = 0
+	var3_step = 0
+	var_accum = dict()
+	var_forflag = False
+	
+	indent = 0
+	prev_type = -1
+	prev_text = ''
+	incomplete = False
+	
+	while True:
+		try:
+			type, text, start, end, line = tokens.next()
+		except StopIteration:
+			break
+		except (TokenError, IndentationError):
+			incomplete = True
+			break
+		
+		# Skip all comments and line joining characters
+		if type == COMMENT or type == NL:
+			continue
+		
+		#################
+		## Indentation ##
+		#################
+		
+		if type == INDENT:
+			indent += 1
+		elif type == DEDENT:
+			indent -= 1
+		
+		#########################
+		## Module importing... ##
+		#########################
+		
+		imp_store = False
+		
+		# Default, look for 'from' or 'import' to start
+		if imp_step == 0:
+			if text == 'from':
+				imp_tmp = []
+				imp_step = 1
+			elif text == 'import':
+				imp_from = None
+				imp_tmp = []
+				imp_step = 2
+		
+		# Found a 'from', create imp_from in form '???.???...'
+		elif imp_step == 1:
+			if text == 'import':
+				imp_from = '.'.join(imp_tmp)
+				imp_tmp = []
+				imp_step = 2
+			elif type == NAME:
+				imp_tmp.append(text)
+			elif text != '.':
+				imp_step = 0 # Invalid syntax
+		
+		# Found 'import', imp_from is populated or None, create imp_name
+		elif imp_step == 2:
+			if text == 'as':
+				imp_name = '.'.join(imp_tmp)
+				imp_step = 3
+			elif type == NAME or text == '*':
+				imp_tmp.append(text)
+			elif text != '.':
+				imp_name = '.'.join(imp_tmp)
+				imp_symb = imp_name
+				imp_store = True
+		
+		# Found 'as', change imp_symb to this value and go back to step 2
+		elif imp_step == 3:
+			if type == NAME:
+				imp_symb = text
+			else:
+				imp_store = True
+		
+		# Both imp_name and imp_symb have now been populated so we can import
+		if imp_store:
+			
+			# Handle special case of 'import *'
+			if imp_name == '*':
+				parent = get_module(imp_from)
+				imports.update(parent.__dict__)
+				
+			else:
+				# Try importing the name as a module
+				try:
+					if imp_from:
+						module = get_module(imp_from +'.'+ imp_name)
+					else:
+						module = get_module(imp_name)

@@ Diff output truncated at 10240 characters. @@




More information about the Bf-blender-cvs mailing list