forked from bartvdbraak/blender
replacement for my own autocomplete module by stani
--- from his patch All the functionality is in the console folder: - intellisense.py: the central module which loads others on demand - complete_namespace: more or less a replacement for the old autocomplete.py - complete_import: module completion (I find this very handy, not just luxury) These complete_* modules work very simple and should also work outside blender. You give some input and it returns a list with possible completions. autocomplete.py is now deprecated.
This commit is contained in:
parent
8c707b2a5f
commit
4b3fd4a8e0
@ -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
|
16
release/scripts/modules/console/__init__.py
Normal file
16
release/scripts/modules/console/__init__.py
Normal file
@ -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."""
|
174
release/scripts/modules/console/complete_import.py
Normal file
174
release/scripts/modules/console/complete_import.py
Normal file
@ -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)
|
67
release/scripts/modules/console/complete_namespace.py
Normal file
67
release/scripts/modules/console/complete_namespace.py
Normal file
@ -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
|
100
release/scripts/modules/console/intellisense.py
Normal file
100
release/scripts/modules/console/intellisense.py
Normal file
@ -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
|
@ -1,5 +1,7 @@
|
||||
import sys
|
||||
import bpy
|
||||
|
||||
|
||||
class CONSOLE_HT_header(bpy.types.Header):
|
||||
__space_type__ = 'CONSOLE'
|
||||
|
||||
@ -37,47 +39,51 @@ class CONSOLE_HT_header(bpy.types.Header):
|
||||
row = layout.row(align=True)
|
||||
row.itemO("console.autocomplete", text="Autocomplete")
|
||||
|
||||
|
||||
class CONSOLE_MT_console(bpy.types.Menu):
|
||||
__label__ = "Console"
|
||||
|
||||
def draw(self, context):
|
||||
layout = self.layout
|
||||
sc = context.space_data
|
||||
|
||||
layout.column()
|
||||
layout.itemO("console.clear")
|
||||
layout.itemO("console.copy")
|
||||
layout.itemO("console.paste")
|
||||
|
||||
|
||||
class CONSOLE_MT_report(bpy.types.Menu):
|
||||
__label__ = "Report"
|
||||
|
||||
def draw(self, context):
|
||||
layout = self.layout
|
||||
sc = context.space_data
|
||||
|
||||
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)
|
||||
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()
|
||||
currently each text datablock gets its own
|
||||
console - bpython_code.InteractiveConsole()
|
||||
...which is stored in this function.
|
||||
|
||||
console_id can be any hashable type
|
||||
'''
|
||||
import sys, code
|
||||
from code import InteractiveConsole
|
||||
|
||||
try: consoles = get_console.consoles
|
||||
except:consoles = get_console.consoles = {}
|
||||
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
|
||||
@ -88,20 +94,19 @@ def get_console(console_id):
|
||||
'''
|
||||
|
||||
try:
|
||||
namespace, console, stdout, stderr = consoles[console_id]
|
||||
console, stdout, stderr = consoles[console_id]
|
||||
except:
|
||||
namespace = {'__builtins__':__builtins__} # locals()
|
||||
namespace['bpy'] = bpy
|
||||
|
||||
console = code.InteractiveConsole(namespace)
|
||||
namespace = {'__builtins__': __builtins__, 'bpy': bpy}
|
||||
console = InteractiveConsole(namespace)
|
||||
|
||||
import io
|
||||
stdout = io.StringIO()
|
||||
stderr = io.StringIO()
|
||||
|
||||
consoles[console_id]= namespace, console, stdout, stderr
|
||||
consoles[console_id] = console, stdout, stderr
|
||||
|
||||
return console, stdout, stderr
|
||||
|
||||
return namespace, console, stdout, stderr
|
||||
|
||||
class CONSOLE_OT_exec(bpy.types.Operator):
|
||||
'''Execute the current console line as a python expression.'''
|
||||
@ -117,11 +122,10 @@ class CONSOLE_OT_exec(bpy.types.Operator):
|
||||
'''
|
||||
def poll(self, context):
|
||||
return (context.space_data.type == 'PYTHON')
|
||||
''' # its not :|
|
||||
'''
|
||||
# its not :|
|
||||
|
||||
def execute(self, context):
|
||||
import sys
|
||||
|
||||
sc = context.space_data
|
||||
|
||||
try:
|
||||
@ -132,8 +136,7 @@ class CONSOLE_OT_exec(bpy.types.Operator):
|
||||
if sc.console_type != 'PYTHON':
|
||||
return ('CANCELLED',)
|
||||
|
||||
namespace, console, stdout, stderr = get_console(hash(context.region))
|
||||
namespace['C'] = context
|
||||
console, stdout, stderr = get_console(hash(context.region))
|
||||
|
||||
# redirect output
|
||||
sys.stdout = stdout
|
||||
@ -164,23 +167,29 @@ class CONSOLE_OT_exec(bpy.types.Operator):
|
||||
|
||||
bpy.ops.console.scrollback_append(text=sc.prompt + line, type='INPUT')
|
||||
|
||||
if is_multiline: sc.prompt = self.PROMPT_MULTI
|
||||
else: sc.prompt = self.PROMPT
|
||||
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)
|
||||
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')
|
||||
|
||||
# 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.'''
|
||||
'''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
|
||||
@ -189,10 +198,11 @@ class CONSOLE_OT_autocomplete(bpy.types.Operator):
|
||||
return context.space_data.console_type == 'PYTHON'
|
||||
|
||||
def execute(self, context):
|
||||
from console import intellisense
|
||||
|
||||
sc = context.space_data
|
||||
|
||||
namespace, console, stdout, stderr = get_console(hash(context.region))
|
||||
console = get_console(hash(context.region))[0]
|
||||
|
||||
current_line = sc.history[-1]
|
||||
line = current_line.line
|
||||
@ -203,39 +213,29 @@ class CONSOLE_OT_autocomplete(bpy.types.Operator):
|
||||
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)
|
||||
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 bcon['scrollback']:
|
||||
add_scrollback(bcon['scrollback'], 'INFO')
|
||||
|
||||
# copy back
|
||||
current_line.line = bcon['edit_text']
|
||||
current_line.current_character = bcon['cursor']
|
||||
# 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)
|
||||
bpy.types.register(CONSOLE_MT_console)
|
||||
bpy.types.register(CONSOLE_MT_report)
|
||||
|
||||
bpy.ops.add(CONSOLE_OT_exec)
|
||||
bpy.ops.add(CONSOLE_OT_autocomplete)
|
||||
|
||||
|
Loading…
Reference in New Issue
Block a user