Welcome to mirror list, hosted at ThFree Co, Russian Federation.

git.blender.org/blender.git - Unnamed repository; edit this file 'description' to name the repository.
summaryrefslogtreecommitdiff
diff options
context:
space:
mode:
-rw-r--r--release/scripts/modules/autocomplete.py211
-rw-r--r--release/scripts/modules/console/__init__.py16
-rw-r--r--release/scripts/modules/console/complete_import.py174
-rw-r--r--release/scripts/modules/console/complete_namespace.py67
-rw-r--r--release/scripts/modules/console/intellisense.py100
-rw-r--r--release/scripts/ui/space_console.py424
6 files changed, 569 insertions, 423 deletions
diff --git a/release/scripts/modules/autocomplete.py b/release/scripts/modules/autocomplete.py
deleted file mode 100644
index 9dd489a178e..00000000000
--- a/release/scripts/modules/autocomplete.py
+++ /dev/null
@@ -1,211 +0,0 @@
-
-
-def execute(bcon):
- '''
- This function has been taken from a BGE console autocomp I wrote a while ago
- the dictionaty bcon is not needed but it means I can copy and paste from the old func
- which works ok for now.
-
- 'bcon' dictionary keys, set by the caller
- * 'cursor' - index of the editing character (int)
- * 'edit_text' - text string for editing (string)
- * 'scrollback' - text to add to the scrollback, options are added here. (text)
- * 'namespace' - namespace, (dictionary)
-
- '''
-
-
- def is_delimiter(ch):
- '''
- For skipping words
- '''
- if ch == '_':
- return False
- if ch.isalnum():
- return False
-
- return True
-
- def is_delimiter_autocomp(ch):
- '''
- When autocompleteing will earch back and
- '''
- if ch in '._[] "\'':
- return False
- if ch.isalnum():
- return False
-
- return True
-
-
- def do_autocomp(autocomp_prefix, autocomp_members):
- '''
- return text to insert and a list of options
- '''
- autocomp_members = [v for v in autocomp_members if v.startswith(autocomp_prefix)]
-
- print("AUTO: '%s'" % autocomp_prefix)
- print("MEMBERS: '%s'" % str(autocomp_members))
-
- if not autocomp_prefix:
- return '', autocomp_members
- elif len(autocomp_members) > 1:
- # find a common string between all members after the prefix
- # 'ge' [getA, getB, getC] --> 'get'
-
- # get the shortest member
- min_len = min([len(v) for v in autocomp_members])
-
- autocomp_prefix_ret = ''
-
- for i in range(len(autocomp_prefix), min_len):
- char_soup = set()
- for v in autocomp_members:
- char_soup.add(v[i])
-
- if len(char_soup) > 1:
- break
- else:
- autocomp_prefix_ret += char_soup.pop()
-
- return autocomp_prefix_ret, autocomp_members
- elif len(autocomp_members) == 1:
- if autocomp_prefix == autocomp_members[0]:
- # the variable matched the prefix exactly
- # add a '.' so you can quickly continue.
- # Could try add [] or other possible extensions rather then '.' too if we had the variable.
- return '.', []
- else:
- # finish off the of the word word
- return autocomp_members[0][len(autocomp_prefix):], []
- else:
- return '', []
-
-
- def BCon_PrevChar(bcon):
- cursor = bcon['cursor']-1
- if cursor<0:
- return None
-
- try:
- return bcon['edit_text'][cursor]
- except:
- return None
-
-
- def BCon_NextChar(bcon):
- try:
- return bcon['edit_text'][bcon['cursor']]
- except:
- return None
-
- def BCon_cursorLeft(bcon):
- bcon['cursor'] -= 1
- if bcon['cursor'] < 0:
- bcon['cursor'] = 0
-
- def BCon_cursorRight(bcon):
- bcon['cursor'] += 1
- if bcon['cursor'] > len(bcon['edit_text']):
- bcon['cursor'] = len(bcon['edit_text'])
-
- def BCon_AddScrollback(bcon, text):
-
- bcon['scrollback'] = bcon['scrollback'] + text
-
-
- def BCon_cursorInsertChar(bcon, ch):
- if bcon['cursor']==0:
- bcon['edit_text'] = ch + bcon['edit_text']
- elif bcon['cursor']==len(bcon['edit_text']):
- bcon['edit_text'] = bcon['edit_text'] + ch
- else:
- bcon['edit_text'] = bcon['edit_text'][:bcon['cursor']] + ch + bcon['edit_text'][bcon['cursor']:]
-
- bcon['cursor']
- if bcon['cursor'] > len(bcon['edit_text']):
- bcon['cursor'] = len(bcon['edit_text'])
- BCon_cursorRight(bcon)
-
-
- TEMP_NAME = '___tempname___'
-
- cursor_orig = bcon['cursor']
-
- ch = BCon_PrevChar(bcon)
- while ch != None and (not is_delimiter(ch)):
- ch = BCon_PrevChar(bcon)
- BCon_cursorLeft(bcon)
-
- if ch != None:
- BCon_cursorRight(bcon)
-
- #print (cursor_orig, bcon['cursor'])
-
- cursor_base = bcon['cursor']
-
- autocomp_prefix = bcon['edit_text'][cursor_base:cursor_orig]
-
- print("PREFIX:'%s'" % autocomp_prefix)
-
- # Get the previous word
- if BCon_PrevChar(bcon)=='.':
- BCon_cursorLeft(bcon)
- ch = BCon_PrevChar(bcon)
- while ch != None and is_delimiter_autocomp(ch)==False:
- ch = BCon_PrevChar(bcon)
- BCon_cursorLeft(bcon)
-
- cursor_new = bcon['cursor']
-
- if ch != None:
- cursor_new+=1
-
- pytxt = bcon['edit_text'][cursor_new:cursor_base-1].strip()
- print("AUTOCOMP EVAL: '%s'" % pytxt)
- #try:
- if pytxt:
- bcon['console'].runsource(TEMP_NAME + '=' + pytxt, '<input>', 'single')
- # print val
- else: ##except:
- val = None
-
- try:
- val = bcon['namespace'][TEMP_NAME]
- del bcon['namespace'][TEMP_NAME]
- except:
- val = None
-
- if val:
- autocomp_members = dir(val)
-
- autocomp_prefix_ret, autocomp_members = do_autocomp(autocomp_prefix, autocomp_members)
-
- bcon['cursor'] = cursor_orig
- for v in autocomp_prefix_ret:
- BCon_cursorInsertChar(bcon, v)
- cursor_orig = bcon['cursor']
-
- if autocomp_members:
- BCon_AddScrollback(bcon, ', '.join(autocomp_members))
-
- del val
-
- else:
- # Autocomp global namespace
- autocomp_members = bcon['namespace'].keys()
-
- if autocomp_prefix:
- autocomp_members = [v for v in autocomp_members if v.startswith(autocomp_prefix)]
-
- autocomp_prefix_ret, autocomp_members = do_autocomp(autocomp_prefix, autocomp_members)
-
- bcon['cursor'] = cursor_orig
- for v in autocomp_prefix_ret:
- BCon_cursorInsertChar(bcon, v)
- cursor_orig = bcon['cursor']
-
- if autocomp_members:
- BCon_AddScrollback(bcon, ', '.join(autocomp_members))
-
- bcon['cursor'] = cursor_orig \ No newline at end of file
diff --git a/release/scripts/modules/console/__init__.py b/release/scripts/modules/console/__init__.py
new file mode 100644
index 00000000000..eb32d78b1ef
--- /dev/null
+++ b/release/scripts/modules/console/__init__.py
@@ -0,0 +1,16 @@
+# Copyright (c) 2009 www.stani.be (GPL license)
+
+# This program is free software: you can redistribute it and/or modify
+# it under the terms of the GNU Lesser General Public License as published by
+# the Free Software Foundation, either version 3 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 Lesser General Public License for more details.
+#
+# You should have received a copy of the GNU Lesser General Public License
+# along with this program. If not, see <http://www.gnu.org/licenses/>.
+
+"""Package for console specific modules."""
diff --git a/release/scripts/modules/console/complete_import.py b/release/scripts/modules/console/complete_import.py
new file mode 100644
index 00000000000..02ded3eef6d
--- /dev/null
+++ b/release/scripts/modules/console/complete_import.py
@@ -0,0 +1,174 @@
+# Copyright (c) 2009 Fernando Perez, www.stani.be (GPL license)
+
+# This program is free software: you can redistribute it and/or modify
+# it under the terms of the GNU Lesser General Public License as published by
+# the Free Software Foundation, either version 3 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 Lesser General Public License for more details.
+#
+# You should have received a copy of the GNU Lesser General Public License
+# along with this program. If not, see <http://www.gnu.org/licenses/>.
+
+# Original copyright (see docstring):
+#*****************************************************************************
+# Copyright (C) 2001-2006 Fernando Perez <fperez@colorado.edu>
+#
+# Distributed under the terms of the BSD License. The full license is in
+# the file COPYING, distributed as part of this software.
+#*****************************************************************************
+
+"""Completer for import statements
+
+Original code was from IPython/Extensions/ipy_completers.py. The following
+changes have been made:
+- ported to python3
+- pep8 polishing
+- limit list of modules to prefix in case of "from w"
+- sorted modules
+- added sphinx documentation
+"""
+
+
+import os
+import sys
+
+TIMEOUT_STORAGE = 3 # Time in secs after which the rootmodules will be stored
+TIMEOUT_GIVEUP = 20 # Time in secs after which we give up
+
+ROOT_MODULES = None
+
+
+def get_root_modules():
+ """
+ Returns a list containing the names of all the modules available in the
+ folders of the pythonpath.
+
+ :returns: modules
+ :rtype: list
+ """
+ global ROOT_MODULES
+ modules = []
+ if not(ROOT_MODULES is None):
+ return ROOT_MODULES
+ from time import time
+ t = time()
+ store = False
+ for path in sys.path:
+ modules += module_list(path)
+ if time() - t >= TIMEOUT_STORAGE and not store:
+ # Caching the list of root modules, please wait!
+ store = True
+ if time() - t > TIMEOUT_GIVEUP:
+ # This is taking too long, we give up.
+ ROOT_MODULES = []
+ return []
+
+ modules += sys.builtin_module_names
+
+ modules = list(set(modules))
+ if '__init__' in modules:
+ modules.remove('__init__')
+ modules = sorted(set(modules))
+ if store:
+ ROOT_MODULES = modules
+ return modules
+
+
+def module_list(path):
+ """
+ Return the list containing the names of the modules available in
+ the given folder.
+
+ :param path: folder path
+ :type path: str
+ :returns: modules
+ :rtype: list
+ """
+
+ if os.path.isdir(path):
+ folder_list = os.listdir(path)
+ elif path.endswith('.egg'):
+ from zipimport import zipimporter
+ try:
+ folder_list = [f for f in zipimporter(path)._files]
+ except:
+ folder_list = []
+ else:
+ folder_list = []
+ #folder_list = glob.glob(os.path.join(path,'*'))
+ folder_list = [p for p in folder_list \
+ if os.path.exists(os.path.join(path, p, '__init__.py'))\
+ or p[-3:] in ('.py', '.so')\
+ or p[-4:] in ('.pyc', '.pyo', '.pyd')]
+
+ folder_list = [os.path.basename(p).split('.')[0] for p in folder_list]
+ return folder_list
+
+
+def complete(line):
+ """
+ Returns a list containing the completion possibilities for an import line.
+
+ :param line:
+
+ incomplete line which contains an import statement::
+
+ import xml.d
+ from xml.dom import
+
+ :type line: str
+ :returns: list of completion possibilities
+ :rtype: list
+
+ >>> complete('import weak')
+ ['weakref']
+ """
+ import inspect
+
+ def try_import(mod, only_modules=False):
+
+ def is_importable(module, attr):
+ if only_modules:
+ return inspect.ismodule(getattr(module, attr))
+ else:
+ return not(attr[:2] == '__' and attr[-2:] == '__')
+
+ try:
+ m = __import__(mod)
+ except:
+ return []
+ mods = mod.split('.')
+ for module in mods[1:]:
+ m = getattr(m, module)
+ if (not hasattr(m, '__file__')) or (not only_modules) or\
+ (hasattr(m, '__file__') and '__init__' in m.__file__):
+ completion_list = [attr for attr in dir(m)
+ if is_importable(m, attr)]
+ completion_list.extend(getattr(m, '__all__', []))
+ if hasattr(m, '__file__') and '__init__' in m.__file__:
+ completion_list.extend(module_list(os.path.dirname(m.__file__)))
+ completion_list = list(set(completion_list))
+ if '__init__' in completion_list:
+ completion_list.remove('__init__')
+ return completion_list
+
+ words = line.split(' ')
+ if len(words) == 3 and words[0] == 'from':
+ return ['import ']
+ if len(words) < 3 and (words[0] in ['import', 'from']):
+ if len(words) == 1:
+ return get_root_modules()
+ mod = words[1].split('.')
+ if len(mod) < 2:
+ mod0 = mod[0]
+ return [m for m in get_root_modules() if m.startswith(mod0)]
+ completion_list = try_import('.'.join(mod[:-1]), True)
+ completion_list = ['.'.join(mod[:-1] + [el]) for el in completion_list]
+ return completion_list
+ if len(words) >= 3 and words[0] == 'from':
+ mod = words[1]
+ return try_import(mod)
diff --git a/release/scripts/modules/console/complete_namespace.py b/release/scripts/modules/console/complete_namespace.py
new file mode 100644
index 00000000000..a2836a60b29
--- /dev/null
+++ b/release/scripts/modules/console/complete_namespace.py
@@ -0,0 +1,67 @@
+# Copyright (c) 2009 www.stani.be (GPL license)
+
+# This program is free software: you can redistribute it and/or modify
+# it under the terms of the GNU Lesser General Public License as published by
+# the Free Software Foundation, either version 3 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 Lesser General Public License for more details.
+#
+# You should have received a copy of the GNU Lesser General Public License
+# along with this program. If not, see <http://www.gnu.org/licenses/>.
+
+"""Autocomplete with the standard library"""
+
+import rlcompleter
+
+TEMP = '__tEmP__' # only \w characters are allowed!
+TEMP_N = len(TEMP)
+
+
+def complete(word, namespace, private=True):
+ """Complete word within a namespace with the standard rlcompleter
+ module. Also supports index or key access [].
+
+ :param word: word to be completed
+ :type word: str
+ :param namespace: namespace
+ :type namespace: dict
+ :param private: whether private attribute/methods should be returned
+ :type private: bool
+
+ >>> complete('fo', {'foo': 'bar'})
+ ['foo']
+ """
+ completer = rlcompleter.Completer(namespace)
+
+ # brackets are normally not allowed -> work around (only in this case)
+ if '[' in word:
+ obj, attr = word.rsplit('.', 1)
+ try:
+ # do not run the obj expression in the console
+ namespace[TEMP] = eval(obj, namespace)
+ except Exception:
+ return []
+ _word = TEMP + '.' + attr
+ else:
+ _word = word
+
+ # find matches with stdlibrary (don't try to implement this yourself)
+ completer.complete(_word, 0)
+ matches = completer.matches
+
+ # brackets are normally not allowed -> clean up
+ if '[' in word:
+ matches = [obj + match[TEMP_N:] for match in matches]
+ del namespace[TEMP]
+
+ # separate public from private
+ public_matches = [match for match in matches if not('._' in match)]
+ if private:
+ private_matches = [match for match in matches if '._' in match]
+ return public_matches + private_matches
+ else:
+ return public_matches
diff --git a/release/scripts/modules/console/intellisense.py b/release/scripts/modules/console/intellisense.py
new file mode 100644
index 00000000000..2658f79a4cc
--- /dev/null
+++ b/release/scripts/modules/console/intellisense.py
@@ -0,0 +1,100 @@
+# Copyright (c) 2009 www.stani.be (GPL license)
+
+# This program is free software: you can redistribute it and/or modify
+# it under the terms of the GNU Lesser General Public License as published by
+# the Free Software Foundation, either version 3 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 Lesser General Public License for more details.
+#
+# You should have received a copy of the GNU Lesser General Public License
+# along with this program. If not, see <http://www.gnu.org/licenses/>.
+
+"""This module provides intellisense features such as:
+
+* autocompletion
+* calltips (not yet implemented)
+
+It unifies all completion plugins and only loads them on demand.
+"""
+# TODO: file complete if startswith quotes
+import os
+import re
+
+# regular expressions to find out which completer we need
+
+# line which starts with an import statement
+RE_MODULE = re.compile('^import|from.+')
+
+# The following regular expression means a word which:
+# - doesn't start with a quote (quoted words are not py objects)
+# - starts with a [a-zA-Z0-9_]
+# - afterwards dots are allowed as well
+# - square bracket pairs [] are allowed (should be closed)
+RE_UNQUOTED_WORD = re.compile(
+ '''(?:^|[^"'])((?:\w+(?:\w|[.]|\[.+?\])*|))$''', re.UNICODE)
+
+
+def complete(line, cursor, namespace, private=True):
+ """Returns a list of possible completions.
+
+ :param line: incomplete text line
+ :type line: str
+ :param cursor: current character position
+ :type cursor: int
+ :param namespace: namespace
+ :type namespace: dict
+ :param private: whether private variables should be listed
+ :type private: bool
+ :returns: list of completions, word
+ :rtype: list, str
+ """
+ re_unquoted_word = RE_UNQUOTED_WORD.search(line[:cursor])
+ if re_unquoted_word:
+ # unquoted word -> module or attribute completion
+ word = re_unquoted_word.group(1)
+ if RE_MODULE.match(line):
+ import complete_import
+ matches = complete_import.complete(line)
+ else:
+ import complete_namespace
+ matches = complete_namespace.complete(word, namespace, private)
+ else:
+ # for now we don't have completers for strings
+ # TODO: add file auto completer for strings
+ word = ''
+ matches = []
+ return matches, word
+
+
+def expand(line, cursor, namespace, private=True):
+ """This method is invoked when the user asks autocompletion,
+ e.g. when Ctrl+Space is clicked.
+
+ :param line: incomplete text line
+ :type line: str
+ :param cursor: current character position
+ :type cursor: int
+ :param namespace: namespace
+ :type namespace: dict
+ :param private: whether private variables should be listed
+ :type private: bool
+ :returns:
+
+ current expanded line, updated cursor position and scrollback
+
+ :rtype: str, int, str
+ """
+ matches, word = complete(line, cursor, namespace, private)
+ prefix = os.path.commonprefix(matches)[len(word):]
+ if prefix:
+ line = line[:cursor] + prefix + line[cursor:]
+ cursor += len(prefix)
+ if len(matches) == 1:
+ scrollback = ''
+ else:
+ scrollback = ' '.join([m.split('.')[-1] for m in matches])
+ return line, cursor, scrollback
diff --git a/release/scripts/ui/space_console.py b/release/scripts/ui/space_console.py
index 19024977992..d02577d93e6 100644
--- a/release/scripts/ui/space_console.py
+++ b/release/scripts/ui/space_console.py
@@ -1,235 +1,236 @@
+import sys
import bpy
+
class CONSOLE_HT_header(bpy.types.Header):
- __space_type__ = 'CONSOLE'
-
- def draw(self, context):
- sc = context.space_data
- # text = sc.text
- layout = self.layout
-
- row= layout.row(align=True)
- row.template_header()
-
- if context.area.show_menus:
- sub = row.row(align=True)
-
- if sc.console_type == 'REPORT':
- sub.itemM("CONSOLE_MT_report")
- else:
- sub.itemM("CONSOLE_MT_console")
-
- layout.itemS()
- layout.itemR(sc, "console_type", expand=True)
-
- if sc.console_type == 'REPORT':
- row = layout.row(align=True)
- row.itemR(sc, "show_report_debug", text="Debug")
- row.itemR(sc, "show_report_info", text="Info")
- row.itemR(sc, "show_report_operator", text="Operators")
- row.itemR(sc, "show_report_warn", text="Warnings")
- row.itemR(sc, "show_report_error", text="Errors")
-
- row = layout.row()
- row.enabled = sc.show_report_operator
- row.itemO("console.report_replay")
- else:
- row = layout.row(align=True)
- row.itemO("console.autocomplete", text="Autocomplete")
+ __space_type__ = 'CONSOLE'
+
+ def draw(self, context):
+ sc = context.space_data
+ # text = sc.text
+ layout = self.layout
+
+ row = layout.row(align=True)
+ row.template_header()
+
+ if context.area.show_menus:
+ sub = row.row(align=True)
+
+ if sc.console_type == 'REPORT':
+ sub.itemM("CONSOLE_MT_report")
+ else:
+ sub.itemM("CONSOLE_MT_console")
+
+ layout.itemS()
+ layout.itemR(sc, "console_type", expand=True)
+
+ if sc.console_type == 'REPORT':
+ row = layout.row(align=True)
+ row.itemR(sc, "show_report_debug", text="Debug")
+ row.itemR(sc, "show_report_info", text="Info")
+ row.itemR(sc, "show_report_operator", text="Operators")
+ row.itemR(sc, "show_report_warn", text="Warnings")
+ row.itemR(sc, "show_report_error", text="Errors")
+
+ row = layout.row()
+ row.enabled = sc.show_report_operator
+ row.itemO("console.report_replay")
+ else:
+ row = layout.row(align=True)
+ row.itemO("console.autocomplete", text="Autocomplete")
+
class CONSOLE_MT_console(bpy.types.Menu):
- __label__ = "Console"
+ __label__ = "Console"
- def draw(self, context):
- layout = self.layout
- sc = context.space_data
+ def draw(self, context):
+ layout = self.layout
+ layout.column()
+ layout.itemO("console.clear")
+ layout.itemO("console.copy")
+ layout.itemO("console.paste")
- layout.column()
- layout.itemO("console.clear")
- layout.itemO("console.copy")
- layout.itemO("console.paste")
class CONSOLE_MT_report(bpy.types.Menu):
- __label__ = "Report"
+ __label__ = "Report"
- def draw(self, context):
- layout = self.layout
- sc = context.space_data
+ def draw(self, context):
+ layout = self.layout
+ layout.column()
+ layout.itemO("console.select_all_toggle")
+ layout.itemO("console.select_border")
+ layout.itemO("console.report_delete")
+ layout.itemO("console.report_copy")
- layout.column()
- layout.itemO("console.select_all_toggle")
- layout.itemO("console.select_border")
- layout.itemO("console.report_delete")
- layout.itemO("console.report_copy")
def add_scrollback(text, text_type):
- for l in text.split('\n'):
- bpy.ops.console.scrollback_append(text=l.replace('\t', ' '), type=text_type)
+ for l in text.split('\n'):
+ bpy.ops.console.scrollback_append(text=l.replace('\t', ' '),
+ type=text_type)
+
def get_console(console_id):
- '''
- helper function for console operators
- currently each text datablock gets its own console - code.InteractiveConsole()
- ...which is stored in this function.
-
- console_id can be any hashable type
- '''
- import sys, code
-
- try: consoles = get_console.consoles
- except:consoles = get_console.consoles = {}
-
- # clear all dead consoles, use text names as IDs
- # TODO, find a way to clear IDs
- '''
- for console_id in list(consoles.keys()):
- if console_id not in bpy.data.texts:
- del consoles[id]
- '''
-
- try:
- namespace, console, stdout, stderr = consoles[console_id]
- except:
- namespace = {'__builtins__':__builtins__} # locals()
- namespace['bpy'] = bpy
-
- console = code.InteractiveConsole(namespace)
-
- import io
- stdout = io.StringIO()
- stderr = io.StringIO()
-
- consoles[console_id]= namespace, console, stdout, stderr
-
- return namespace, console, stdout, stderr
+ '''
+ helper function for console operators
+ currently each text datablock gets its own
+ console - bpython_code.InteractiveConsole()
+ ...which is stored in this function.
+
+ console_id can be any hashable type
+ '''
+ from code import InteractiveConsole
+
+ try:
+ consoles = get_console.consoles
+ except:
+ consoles = get_console.consoles = {}
+
+ # clear all dead consoles, use text names as IDs
+ # TODO, find a way to clear IDs
+ '''
+ for console_id in list(consoles.keys()):
+ if console_id not in bpy.data.texts:
+ del consoles[id]
+ '''
+
+ try:
+ console, stdout, stderr = consoles[console_id]
+ except:
+ namespace = {'__builtins__': __builtins__, 'bpy': bpy}
+ console = InteractiveConsole(namespace)
+
+ import io
+ stdout = io.StringIO()
+ stderr = io.StringIO()
+
+ consoles[console_id] = console, stdout, stderr
+
+ return console, stdout, stderr
+
class CONSOLE_OT_exec(bpy.types.Operator):
- '''Execute the current console line as a python expression.'''
- __idname__ = "console.execute"
- __label__ = "Console Execute"
- __register__ = False
-
- # Both prompts must be the same length
- PROMPT = '>>> '
- PROMPT_MULTI = '... '
-
- # is this working???
- '''
- def poll(self, context):
- return (context.space_data.type == 'PYTHON')
- ''' # its not :|
-
- def execute(self, context):
- import sys
-
- sc = context.space_data
-
- try:
- line = sc.history[-1].line
- except:
- return ('CANCELLED',)
-
- if sc.console_type != 'PYTHON':
- return ('CANCELLED',)
-
- namespace, console, stdout, stderr = get_console(hash(context.region))
- namespace['C'] = context
-
- # redirect output
- sys.stdout = stdout
- sys.stderr = stderr
-
- # run the console
- if not line.strip():
- line_exec = '\n' # executes a multiline statement
- else:
- line_exec = line
-
- is_multiline = console.push(line_exec)
-
- stdout.seek(0)
- stderr.seek(0)
-
- output = stdout.read()
- output_err = stderr.read()
-
- # cleanup
- sys.stdout = sys.__stdout__
- sys.stderr = sys.__stderr__
- sys.last_traceback = None
-
- # So we can reuse, clear all data
- stdout.truncate(0)
- stderr.truncate(0)
-
- bpy.ops.console.scrollback_append(text = sc.prompt+line, type='INPUT')
-
- if is_multiline: sc.prompt = self.PROMPT_MULTI
- else: sc.prompt = self.PROMPT
-
- # insert a new blank line
- bpy.ops.console.history_append(text="", current_character=0, remove_duplicates= True)
-
- # Insert the output into the editor
- # not quite correct because the order might have changed, but ok 99% of the time.
- if output: add_scrollback(output, 'OUTPUT')
- if output_err: add_scrollback(output_err, 'ERROR')
-
-
- return ('FINISHED',)
+ '''Execute the current console line as a python expression.'''
+ __idname__ = "console.execute"
+ __label__ = "Console Execute"
+ __register__ = False
+
+ # Both prompts must be the same length
+ PROMPT = '>>> '
+ PROMPT_MULTI = '... '
+
+ # is this working???
+ '''
+ def poll(self, context):
+ return (context.space_data.type == 'PYTHON')
+ '''
+ # its not :|
+
+ def execute(self, context):
+ sc = context.space_data
+
+ try:
+ line = sc.history[-1].line
+ except:
+ return ('CANCELLED',)
+
+ if sc.console_type != 'PYTHON':
+ return ('CANCELLED',)
+
+ console, stdout, stderr = get_console(hash(context.region))
+
+ # redirect output
+ sys.stdout = stdout
+ sys.stderr = stderr
+
+ # run the console
+ if not line.strip():
+ line_exec = '\n' # executes a multiline statement
+ else:
+ line_exec = line
+
+ is_multiline = console.push(line_exec)
+
+ stdout.seek(0)
+ stderr.seek(0)
+
+ output = stdout.read()
+ output_err = stderr.read()
+
+ # cleanup
+ sys.stdout = sys.__stdout__
+ sys.stderr = sys.__stderr__
+ sys.last_traceback = None
+
+ # So we can reuse, clear all data
+ stdout.truncate(0)
+ stderr.truncate(0)
+
+ bpy.ops.console.scrollback_append(text=sc.prompt + line, type='INPUT')
+
+ if is_multiline:
+ sc.prompt = self.PROMPT_MULTI
+ else:
+ sc.prompt = self.PROMPT
+
+ # insert a new blank line
+ bpy.ops.console.history_append(text="", current_character=0,
+ remove_duplicates=True)
+
+ # Insert the output into the editor
+ # not quite correct because the order might have changed,
+ # but ok 99% of the time.
+ if output:
+ add_scrollback(output, 'OUTPUT')
+ if output_err:
+ add_scrollback(output_err, 'ERROR')
+
+ return ('FINISHED',)
class CONSOLE_OT_autocomplete(bpy.types.Operator):
- '''Evaluate the namespace up until the cursor and give a list of options or complete the name if there is only one.'''
- __idname__ = "console.autocomplete"
- __label__ = "Console Autocomplete"
- __register__ = False
-
- def poll(self, context):
- return context.space_data.console_type == 'PYTHON'
-
- def execute(self, context):
-
- sc = context.space_data
-
- namespace, console, stdout, stderr = get_console(hash(context.region))
-
- current_line = sc.history[-1]
- line = current_line.line
-
- if not console:
- return ('CANCELLED',)
-
- if sc.console_type != 'PYTHON':
- return ('CANCELLED',)
-
- # fake cursor, use for autocomp func.
- bcon = {}
- bcon['cursor'] = current_line.current_character
- bcon['console'] = console
- bcon['edit_text'] = line
- bcon['namespace'] = namespace
- bcon['scrollback'] = '' # nor from the BGE console
-
-
- # This function isnt aware of the text editor or being an operator
- # just does the autocomp then copy its results back
- import autocomplete
- autocomplete.execute(bcon)
-
- # Now we need to copy back the line from blender back into the text editor.
- # This will change when we dont use the text editor anymore
- if bcon['scrollback']:
- add_scrollback(bcon['scrollback'], 'INFO')
-
- # copy back
- current_line.line = bcon['edit_text']
- current_line.current_character = bcon['cursor']
-
- context.area.tag_redraw()
-
- return ('FINISHED',)
+ '''Evaluate the namespace up until the cursor and give a list of
+ options or complete the name if there is only one.'''
+ __idname__ = "console.autocomplete"
+ __label__ = "Console Autocomplete"
+ __register__ = False
+ def poll(self, context):
+ return context.space_data.console_type == 'PYTHON'
+
+ def execute(self, context):
+ from console import intellisense
+
+ sc = context.space_data
+
+ console = get_console(hash(context.region))[0]
+
+ current_line = sc.history[-1]
+ line = current_line.line
+
+ if not console:
+ return ('CANCELLED',)
+
+ if sc.console_type != 'PYTHON':
+ return ('CANCELLED',)
+
+ # This function isnt aware of the text editor or being an operator
+ # just does the autocomp then copy its results back
+ current_line.line, current_line.current_character, scrollback = \
+ intellisense.expand(
+ line=current_line.line,
+ cursor=current_line.current_character,
+ namespace=console.locals,
+ private='-d' in sys.argv)
+
+ # Now we need to copy back the line from blender back into the
+ # text editor. This will change when we dont use the text editor
+ # anymore
+ if scrollback:
+ add_scrollback(scrollback, 'INFO')
+
+ context.area.tag_redraw()
+
+ return ('FINISHED',)
bpy.types.register(CONSOLE_HT_header)
@@ -238,4 +239,3 @@ bpy.types.register(CONSOLE_MT_report)
bpy.ops.add(CONSOLE_OT_exec)
bpy.ops.add(CONSOLE_OT_autocomplete)
-