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, '', '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 . + +"""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 . + +# Original copyright (see docstring): +#***************************************************************************** +# Copyright (C) 2001-2006 Fernando Perez +# +# 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 . + +"""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 . + +"""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' + __space_type__ = 'CONSOLE' - def draw(self, context): - sc = context.space_data - # text = sc.text - layout = self.layout + def draw(self, context): + sc = context.space_data + # text = sc.text + layout = self.layout - row= layout.row(align=True) - row.template_header() + row = layout.row(align=True) + row.template_header() - if context.area.show_menus: - sub = row.row(align=True) + 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") + 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) + 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") - 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) -