├── .gitignore
├── multi_script_editor
├── __init__.py
├── helpText.txt
├── hqt.py
├── icons
│ ├── __init__.py
│ ├── clear.png
│ ├── donate.png
│ ├── exec_all.png
│ ├── exec_sel.png
│ ├── help.png
│ ├── pw.png
│ └── pw_logo.png
├── icons_rcs.py
├── jedi
│ ├── __init__.py
│ ├── __main__.py
│ ├── _compatibility.py
│ ├── api
│ │ ├── __init__.py
│ │ ├── classes.py
│ │ ├── helpers.py
│ │ ├── interpreter.py
│ │ ├── keywords.py
│ │ ├── replstartup.py
│ │ └── usages.py
│ ├── cache.py
│ ├── common.py
│ ├── debug.py
│ ├── evaluate
│ │ ├── __init__.py
│ │ ├── analysis.py
│ │ ├── cache.py
│ │ ├── compiled
│ │ │ ├── __init__.py
│ │ │ ├── fake.py
│ │ │ └── fake
│ │ │ │ ├── _functools.pym
│ │ │ │ ├── _sqlite3.pym
│ │ │ │ ├── _sre.pym
│ │ │ │ ├── _weakref.pym
│ │ │ │ ├── builtins.pym
│ │ │ │ ├── datetime.pym
│ │ │ │ ├── io.pym
│ │ │ │ └── posix.pym
│ │ ├── docstrings.py
│ │ ├── dynamic.py
│ │ ├── finder.py
│ │ ├── helpers.py
│ │ ├── imports.py
│ │ ├── iterable.py
│ │ ├── param.py
│ │ ├── precedence.py
│ │ ├── recursion.py
│ │ ├── representation.py
│ │ ├── stdlib.py
│ │ └── sys_path.py
│ ├── parser
│ │ ├── __init__.py
│ │ ├── fast.py
│ │ ├── representation.py
│ │ ├── tokenize.py
│ │ └── user_context.py
│ ├── refactoring.py
│ ├── settings.py
│ └── utils.py
├── managers
│ ├── _3dsmax.py
│ ├── __init__.py
│ ├── _houdini.py
│ ├── _maya.py
│ ├── _nuke.py
│ ├── completeWidget.py
│ ├── houdini
│ │ ├── hou.py
│ │ ├── multi_script_editor_16.pypanel
│ │ ├── soptoolutils.py
│ │ └── toolutils.py
│ ├── nuke
│ │ ├── __init__.py
│ │ ├── callbacks.py
│ │ ├── geo.py
│ │ ├── main.py
│ │ ├── math.py
│ │ ├── nodes.py
│ │ └── rotopaint.py
│ └── run_3dsmax.py
├── run.cmd
├── run.sh
├── scriptEditor.py
├── sessionManager.py
├── settingsManager.py
├── shortcuts.txt
├── style
│ ├── __init__.py
│ ├── completer.qss
│ ├── links.py
│ ├── pw.ico
│ ├── pw.png
│ ├── script_editor.png
│ └── style.css
├── tested.txt
└── widgets
│ ├── __init__.py
│ ├── about.py
│ ├── about.ui
│ ├── about_UIs.py
│ ├── completeWidget.py
│ ├── findWidget.py
│ ├── findWidget.ui
│ ├── findWidget_UIs.py
│ ├── inputWidget.py
│ ├── numBarWidget.py
│ ├── outputWidget.py
│ ├── pythonSyntax
│ ├── __init__.py
│ ├── design.py
│ ├── keywords.py
│ └── syntaxHighLighter.py
│ ├── scriptEditor.ui
│ ├── scriptEditor_UIs.py
│ ├── shortcuts.py
│ ├── shortcuts.ui
│ ├── shortcuts_UIs.py
│ ├── tabWidget.py
│ ├── themeEditor.py
│ ├── themeEditor.ui
│ ├── themeEditor_UI.py
│ └── themeEditor_UIs.py
├── readme.md
├── readme_3dsmax.md
├── readme_houdini.md
├── readme_maya.md
├── readme_nuke.md
├── readme_standalone.md
├── releaseNote.md
└── setup.py
/.gitignore:
--------------------------------------------------------------------------------
1 | # Created by .ignore support plugin (hsz.mobi)
2 | todo
3 | .idea
4 | tmp
5 | _backup
6 | _tmp
7 |
--------------------------------------------------------------------------------
/multi_script_editor/__init__.py:
--------------------------------------------------------------------------------
1 | import os, sys
2 |
3 | root = os.path.dirname(__file__)
4 | if not root in sys.path:
5 | sys.path.append(root)
6 |
7 |
8 | # HOUDINI
9 | def showHoudini(clear=False, ontop=False, name=None, floating=False, position=(), size=(),
10 | pane=None, replacePyPanel=False, hideTitleMenu=True):
11 | """
12 | This method use hqt module. Download it before
13 | """
14 | from .managers import _houdini
15 | reload(_houdini)
16 | _houdini.show(clear=clear, ontop=ontop, name=name, floating=floating, position=position,
17 | size=size, pane=pane, replacePyPanel=replacePyPanel, hideTitleMenu=hideTitleMenu)
18 |
19 | # NUKE
20 | def showNuke(panel=False):
21 | from .managers import _nuke
22 | reload(_nuke)
23 | _nuke.show(panel)
24 |
25 |
26 | # MAYA
27 | def showMaya(dock=False):
28 | from .managers import _maya
29 | reload (_maya)
30 | _maya.show(dock)
31 |
32 | # 3DSMAX PLUS
33 | def show3DSMax():
34 | sys.argv = []
35 | from .managers import _3dsmax
36 | reload (_3dsmax)
37 | _3dsmax.show()
--------------------------------------------------------------------------------
/multi_script_editor/helpText.txt:
--------------------------------------------------------------------------------
1 |
2 |
3 |
4 |
5 |
6 |
7 | Multi Script Editor v%s
8 |
9 | By PaulWinex paulwinex.com
10 |
11 | Editor Variables:
12 |
13 | -
14 | self_main: (QWidget) main widget
15 | -
16 | self_output: (QWidget) output widget
17 | -
18 | self_version: (string) current version
19 | -
20 | self_context: (string or None) current context
21 | -
22 | self_help: (function) show this text
23 |
24 |
25 |
26 |
--------------------------------------------------------------------------------
/multi_script_editor/icons/__init__.py:
--------------------------------------------------------------------------------
1 | import os
2 |
3 | root = os.path.dirname(__file__)
4 |
5 | icons = dict(
6 | pw=os.path.join(root,'pw.png'),
7 | all=os.path.join(root,'exec_all.png'),
8 | sel=os.path.join(root,'exec_sel.png'),
9 | clear=os.path.join(root,'clear.png'),
10 | help=os.path.join(root,'help.png'),
11 | donate=os.path.join(root,'donate.png'),
12 | )
--------------------------------------------------------------------------------
/multi_script_editor/icons/clear.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/paulwinex/pw_MultiScriptEditor/e447e99f87cb07e238baf693b7e124e50efdbc51/multi_script_editor/icons/clear.png
--------------------------------------------------------------------------------
/multi_script_editor/icons/donate.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/paulwinex/pw_MultiScriptEditor/e447e99f87cb07e238baf693b7e124e50efdbc51/multi_script_editor/icons/donate.png
--------------------------------------------------------------------------------
/multi_script_editor/icons/exec_all.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/paulwinex/pw_MultiScriptEditor/e447e99f87cb07e238baf693b7e124e50efdbc51/multi_script_editor/icons/exec_all.png
--------------------------------------------------------------------------------
/multi_script_editor/icons/exec_sel.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/paulwinex/pw_MultiScriptEditor/e447e99f87cb07e238baf693b7e124e50efdbc51/multi_script_editor/icons/exec_sel.png
--------------------------------------------------------------------------------
/multi_script_editor/icons/help.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/paulwinex/pw_MultiScriptEditor/e447e99f87cb07e238baf693b7e124e50efdbc51/multi_script_editor/icons/help.png
--------------------------------------------------------------------------------
/multi_script_editor/icons/pw.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/paulwinex/pw_MultiScriptEditor/e447e99f87cb07e238baf693b7e124e50efdbc51/multi_script_editor/icons/pw.png
--------------------------------------------------------------------------------
/multi_script_editor/icons/pw_logo.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/paulwinex/pw_MultiScriptEditor/e447e99f87cb07e238baf693b7e124e50efdbc51/multi_script_editor/icons/pw_logo.png
--------------------------------------------------------------------------------
/multi_script_editor/jedi/__init__.py:
--------------------------------------------------------------------------------
1 | """
2 | Jedi is an autocompletion tool for Python that can be used in IDEs/editors.
3 | Jedi works. Jedi is fast. It understands all of the basic Python syntax
4 | elements including many builtin functions.
5 |
6 | Additionaly, Jedi suports two different goto functions and has support for
7 | renaming as well as Pydoc support and some other IDE features.
8 |
9 | Jedi uses a very simple API to connect with IDE's. There's a reference
10 | implementation as a `VIM-Plugin `_,
11 | which uses Jedi's autocompletion. I encourage you to use Jedi in your IDEs.
12 | It's really easy. If there are any problems (also with licensing), just contact
13 | me.
14 |
15 | To give you a simple example how you can use the Jedi library, here is an
16 | example for the autocompletion feature:
17 |
18 | >>> import jedi
19 | >>> source = '''
20 | ... import datetime
21 | ... datetime.da'''
22 | >>> script = jedi.Script(source, 3, len('datetime.da'), 'example.py')
23 | >>> script
24 |
25 | >>> completions = script.completions()
26 | >>> completions #doctest: +ELLIPSIS
27 | [, , ...]
28 | >>> print(completions[0].complete)
29 | te
30 | >>> print(completions[0].name)
31 | date
32 |
33 | As you see Jedi is pretty simple and allows you to concentrate on writing a
34 | good text editor, while still having very good IDE features for Python.
35 | """
36 |
37 | __version__ = '0.8.1-final0'
38 |
39 | from jedi.api import Script, Interpreter, NotFoundError, set_debug_function
40 | from jedi.api import preload_module, defined_names
41 | from jedi import settings
42 |
--------------------------------------------------------------------------------
/multi_script_editor/jedi/__main__.py:
--------------------------------------------------------------------------------
1 | from sys import argv
2 | from os.path import join, dirname, abspath, isdir
3 |
4 |
5 | if len(argv) == 2 and argv[1] == 'repl':
6 | # don't want to use __main__ only for repl yet, maybe we want to use it for
7 | # something else. So just use the keyword ``repl`` for now.
8 | print(join(dirname(abspath(__file__)), 'api', 'replstartup.py'))
9 | elif len(argv) > 1 and argv[1] == 'linter':
10 | """
11 | This is a pre-alpha API. You're not supposed to use it at all, except for
12 | testing. It will very likely change.
13 | """
14 | import jedi
15 | import sys
16 |
17 | if '--debug' in sys.argv:
18 | jedi.set_debug_function()
19 |
20 | for path in sys.argv[2:]:
21 | if path.startswith('--'):
22 | continue
23 | if isdir(path):
24 | import fnmatch
25 | import os
26 |
27 | paths = []
28 | for root, dirnames, filenames in os.walk(path):
29 | for filename in fnmatch.filter(filenames, '*.py'):
30 | paths.append(os.path.join(root, filename))
31 | else:
32 | paths = [path]
33 |
34 | try:
35 | for path in paths:
36 | for error in jedi.Script(path=path)._analysis():
37 | print(error)
38 | except Exception:
39 | if '--pdb' in sys.argv:
40 | import pdb
41 | pdb.post_mortem()
42 | else:
43 | raise
44 |
--------------------------------------------------------------------------------
/multi_script_editor/jedi/_compatibility.py:
--------------------------------------------------------------------------------
1 | """
2 | To ensure compatibility from Python ``2.6`` - ``3.3``, a module has been
3 | created. Clearly there is huge need to use conforming syntax.
4 | """
5 | import sys
6 | import imp
7 | import os
8 | import re
9 | try:
10 | import importlib
11 | except ImportError:
12 | pass
13 |
14 | is_py3 = sys.version_info[0] >= 3
15 | is_py33 = is_py3 and sys.version_info.minor >= 3
16 | is_py26 = not is_py3 and sys.version_info[1] < 7
17 |
18 |
19 | def find_module_py33(string, path=None):
20 | loader = importlib.machinery.PathFinder.find_module(string, path)
21 |
22 | if loader is None and path is None: # Fallback to find builtins
23 | loader = importlib.find_loader(string)
24 |
25 | if loader is None:
26 | raise ImportError("Couldn't find a loader for {0}".format(string))
27 |
28 | try:
29 | is_package = loader.is_package(string)
30 | if is_package:
31 | module_path = os.path.dirname(loader.path)
32 | module_file = None
33 | else:
34 | module_path = loader.get_filename(string)
35 | module_file = open(module_path, 'rb')
36 | except AttributeError:
37 | # ExtensionLoader has not attribute get_filename, instead it has a
38 | # path attribute that we can use to retrieve the module path
39 | try:
40 | module_path = loader.path
41 | module_file = open(loader.path, 'rb')
42 | except AttributeError:
43 | module_path = string
44 | module_file = None
45 | finally:
46 | is_package = False
47 |
48 | return module_file, module_path, is_package
49 |
50 |
51 | def find_module_pre_py33(string, path=None):
52 | module_file, module_path, description = imp.find_module(string, path)
53 | module_type = description[2]
54 | return module_file, module_path, module_type is imp.PKG_DIRECTORY
55 |
56 |
57 | find_module = find_module_py33 if is_py33 else find_module_pre_py33
58 | find_module.__doc__ = """
59 | Provides information about a module.
60 |
61 | This function isolates the differences in importing libraries introduced with
62 | python 3.3 on; it gets a module name and optionally a path. It will return a
63 | tuple containin an open file for the module (if not builtin), the filename
64 | or the name of the module if it is a builtin one and a boolean indicating
65 | if the module is contained in a package.
66 | """
67 |
68 | # next was defined in python 2.6, in python 3 obj.next won't be possible
69 | # anymore
70 | try:
71 | next = next
72 | except NameError:
73 | _raiseStopIteration = object()
74 |
75 | def next(iterator, default=_raiseStopIteration):
76 | if not hasattr(iterator, 'next'):
77 | raise TypeError("not an iterator")
78 | try:
79 | return iterator.next()
80 | except StopIteration:
81 | if default is _raiseStopIteration:
82 | raise
83 | else:
84 | return default
85 |
86 | # unicode function
87 | try:
88 | unicode = unicode
89 | except NameError:
90 | unicode = str
91 |
92 | if is_py3:
93 | u = lambda s: s
94 | else:
95 | u = lambda s: s.decode('utf-8')
96 |
97 | u.__doc__ = """
98 | Decode a raw string into unicode object. Do nothing in Python 3.
99 | """
100 |
101 | # exec function
102 | if is_py3:
103 | def exec_function(source, global_map):
104 | exec(source, global_map)
105 | else:
106 | eval(compile("""def exec_function(source, global_map):
107 | exec source in global_map """, 'blub', 'exec'))
108 |
109 | # re-raise function
110 | if is_py3:
111 | def reraise(exception, traceback):
112 | raise exception.with_traceback(traceback)
113 | else:
114 | eval(compile("""
115 | def reraise(exception, traceback):
116 | raise exception, None, traceback
117 | """, 'blub', 'exec'))
118 |
119 | reraise.__doc__ = """
120 | Re-raise `exception` with a `traceback` object.
121 |
122 | Usage::
123 |
124 | reraise(Exception, sys.exc_info()[2])
125 |
126 | """
127 |
128 | # hasattr function used because python
129 | if is_py3:
130 | hasattr = hasattr
131 | else:
132 | def hasattr(obj, name):
133 | try:
134 | getattr(obj, name)
135 | return True
136 | except AttributeError:
137 | return False
138 |
139 |
140 | class Python3Method(object):
141 | def __init__(self, func):
142 | self.func = func
143 |
144 | def __get__(self, obj, objtype):
145 | if obj is None:
146 | return lambda *args, **kwargs: self.func(*args, **kwargs)
147 | else:
148 | return lambda *args, **kwargs: self.func(obj, *args, **kwargs)
149 |
150 |
151 | def use_metaclass(meta, *bases):
152 | """ Create a class with a metaclass. """
153 | if not bases:
154 | bases = (object,)
155 | return meta("HackClass", bases, {})
156 |
157 |
158 | try:
159 | encoding = sys.stdout.encoding
160 | if encoding is None:
161 | encoding = 'utf-8'
162 | except AttributeError:
163 | encoding = 'ascii'
164 |
165 |
166 | def u(string):
167 | """Cast to unicode DAMMIT!
168 | Written because Python2 repr always implicitly casts to a string, so we
169 | have to cast back to a unicode (and we now that we always deal with valid
170 | unicode, because we check that in the beginning).
171 | """
172 | if is_py3:
173 | return str(string)
174 | elif not isinstance(string, unicode):
175 | return unicode(str(string), 'UTF-8')
176 | return string
177 |
178 | try:
179 | import builtins # module name in python 3
180 | except ImportError:
181 | import __builtin__ as builtins
182 |
183 |
184 | import ast
185 |
186 |
187 | def literal_eval(string):
188 | # py3.0, py3.1 and py32 don't support unicode literals. Support those, I
189 | # don't want to write two versions of the tokenizer.
190 | if is_py3 and sys.version_info.minor < 3:
191 | if re.match('[uU][\'"]', string):
192 | string = string[1:]
193 | return ast.literal_eval(string)
194 |
195 |
196 | try:
197 | from itertools import zip_longest
198 | except ImportError:
199 | from itertools import izip_longest as zip_longest # Python 2
200 |
--------------------------------------------------------------------------------
/multi_script_editor/jedi/api/helpers.py:
--------------------------------------------------------------------------------
1 | """
2 | Helpers for the API
3 | """
4 | import re
5 |
6 | from jedi.evaluate import imports
7 |
8 |
9 | def completion_parts(path_until_cursor):
10 | """
11 | Returns the parts for the completion
12 | :return: tuple - (path, dot, like)
13 | """
14 | match = re.match(r'^(.*?)(\.|)(\w?[\w\d]*)$', path_until_cursor, flags=re.S)
15 | return match.groups()
16 |
17 |
18 | def sorted_definitions(defs):
19 | # Note: `or ''` below is required because `module_path` could be
20 | return sorted(defs, key=lambda x: (x.module_path or '', x.line or 0, x.column or 0))
21 |
22 |
23 | def get_on_import_stmt(evaluator, user_context, user_stmt, is_like_search=False):
24 | """
25 | Resolve the user statement, if it is an import. Only resolve the
26 | parts until the user position.
27 | """
28 | import_names = user_stmt.get_all_import_names()
29 | kill_count = -1
30 | cur_name_part = None
31 | for i in import_names:
32 | if user_stmt.alias == i:
33 | continue
34 | for name_part in i.names:
35 | if name_part.end_pos >= user_context.position:
36 | if not cur_name_part:
37 | cur_name_part = name_part
38 | kill_count += 1
39 |
40 | context = user_context.get_context()
41 | just_from = next(context) == 'from'
42 |
43 | i = imports.ImportWrapper(evaluator, user_stmt, is_like_search,
44 | kill_count=kill_count, nested_resolve=True,
45 | is_just_from=just_from)
46 | return i, cur_name_part
47 |
--------------------------------------------------------------------------------
/multi_script_editor/jedi/api/interpreter.py:
--------------------------------------------------------------------------------
1 | import inspect
2 | import re
3 |
4 | from jedi._compatibility import builtins
5 | from jedi import debug
6 | from jedi.common import source_to_unicode
7 | from jedi.cache import underscore_memoization
8 | from jedi.evaluate import compiled
9 | from jedi.evaluate.compiled.fake import get_module
10 | from jedi.parser import representation as pr
11 | from jedi.parser.fast import FastParser
12 | from jedi.evaluate import helpers
13 |
14 |
15 | class InterpreterNamespace(pr.Module):
16 | def __init__(self, evaluator, namespace, parser_module):
17 | self.namespace = namespace
18 | self.parser_module = parser_module
19 | self._evaluator = evaluator
20 |
21 | @underscore_memoization
22 | def get_defined_names(self):
23 | for name in self.parser_module.get_defined_names():
24 | yield name
25 | for key, value in self.namespace.items():
26 | yield LazyName(self._evaluator, key, value)
27 |
28 | def scope_names_generator(self, position=None):
29 | yield self, list(self.get_defined_names())
30 |
31 | def __getattr__(self, name):
32 | return getattr(self.parser_module, name)
33 |
34 |
35 | class LazyName(helpers.FakeName):
36 | def __init__(self, evaluator, name, value):
37 | super(LazyName, self).__init__(name)
38 | self._evaluator = evaluator
39 | self._value = value
40 | self._name = name
41 |
42 | @property
43 | @underscore_memoization
44 | def parent(self):
45 | obj = self._value
46 | parser_path = []
47 | if inspect.ismodule(obj):
48 | module = obj
49 | else:
50 | class FakeParent(pr.Base):
51 | parent = None # To avoid having no parent for NamePart.
52 | path = None
53 |
54 | names = []
55 | try:
56 | o = obj.__objclass__
57 | names.append(obj.__name__)
58 | obj = o
59 | except AttributeError:
60 | pass
61 |
62 | try:
63 | module_name = obj.__module__
64 | names.insert(0, obj.__name__)
65 | except AttributeError:
66 | # Unfortunately in some cases like `int` there's no __module__
67 | module = builtins
68 | else:
69 | module = __import__(module_name)
70 | fake_name = helpers.FakeName(names, FakeParent())
71 | parser_path = fake_name.names
72 | raw_module = get_module(self._value)
73 |
74 | try:
75 | path = module.__file__
76 | except AttributeError:
77 | pass
78 | else:
79 | path = re.sub('c$', '', path)
80 | if path.endswith('.py'):
81 | # cut the `c` from `.pyc`
82 | with open(path) as f:
83 | source = source_to_unicode(f.read())
84 | mod = FastParser(source, path[:-1]).module
85 | if not parser_path:
86 | return mod
87 | found = self._evaluator.eval_call_path(iter(parser_path), mod, None)
88 | if found:
89 | return found[0]
90 | debug.warning('Interpreter lookup for Python code failed %s',
91 | mod)
92 |
93 | module = compiled.CompiledObject(raw_module)
94 | if raw_module == builtins:
95 | # The builtins module is special and always cached.
96 | module = compiled.builtin
97 | return compiled.create(self._evaluator, self._value, module, module)
98 |
99 | @parent.setter
100 | def parent(self, value):
101 | """Needed because of the ``representation.Simple`` super class."""
102 |
103 |
104 | def create(evaluator, namespace, parser_module):
105 | ns = InterpreterNamespace(evaluator, namespace, parser_module)
106 | for attr_name in pr.SCOPE_CONTENTS:
107 | for something in getattr(parser_module, attr_name):
108 | something.parent = ns
109 |
--------------------------------------------------------------------------------
/multi_script_editor/jedi/api/keywords.py:
--------------------------------------------------------------------------------
1 | import pydoc
2 | import keyword
3 |
4 | from jedi._compatibility import is_py3
5 | from jedi import common
6 | from jedi.evaluate import compiled
7 |
8 | try:
9 | from pydoc_data import topics as pydoc_topics
10 | except ImportError:
11 | # Python 2.6
12 | import pydoc_topics
13 |
14 | if is_py3:
15 | keys = keyword.kwlist
16 | else:
17 | keys = keyword.kwlist + ['None', 'False', 'True']
18 |
19 |
20 | def keywords(string='', pos=(0, 0), all=False):
21 | if all:
22 | return set([Keyword(k, pos) for k in keys])
23 | if string in keys:
24 | return set([Keyword(string, pos)])
25 | return set()
26 |
27 |
28 | def keyword_names(*args, **kwargs):
29 | kwds = []
30 | for k in keywords(*args, **kwargs):
31 | start = k.start_pos
32 | kwds.append(KeywordName(k, k.name, start))
33 | return kwds
34 |
35 |
36 | def get_operator(string, pos):
37 | return Keyword(string, pos)
38 |
39 |
40 | class KeywordName(object):
41 | def __init__(self, parent, name, start_pos):
42 | self.parent = parent
43 | self.names = [name]
44 | self.start_pos = start_pos
45 |
46 | @property
47 | def end_pos(self):
48 | return self.start_pos[0], self.start_pos[1] + len(self.name)
49 |
50 |
51 | class Keyword(object):
52 | def __init__(self, name, pos):
53 | self.name = name
54 | self.start_pos = pos
55 | self.parent = compiled.builtin
56 |
57 | def get_parent_until(self):
58 | return self.parent
59 |
60 | @property
61 | def names(self):
62 | """ For a `parsing.Name` like comparision """
63 | return [self.name]
64 |
65 | @property
66 | def docstr(self):
67 | return imitate_pydoc(self.name)
68 |
69 | def __repr__(self):
70 | return '<%s: %s>' % (type(self).__name__, self.name)
71 |
72 |
73 | def imitate_pydoc(string):
74 | """
75 | It's not possible to get the pydoc's without starting the annoying pager
76 | stuff.
77 | """
78 | # str needed because of possible unicode stuff in py2k (pydoc doesn't work
79 | # with unicode strings)
80 | string = str(string)
81 | h = pydoc.help
82 | with common.ignored(KeyError):
83 | # try to access symbols
84 | string = h.symbols[string]
85 | string, _, related = string.partition(' ')
86 |
87 | get_target = lambda s: h.topics.get(s, h.keywords.get(s))
88 | while isinstance(string, str):
89 | string = get_target(string)
90 |
91 | try:
92 | # is a tuple now
93 | label, related = string
94 | except TypeError:
95 | return ''
96 |
97 | try:
98 | return pydoc_topics.topics[label] if pydoc_topics else ''
99 | except KeyError:
100 | return ''
101 |
--------------------------------------------------------------------------------
/multi_script_editor/jedi/api/replstartup.py:
--------------------------------------------------------------------------------
1 | """
2 | To use Jedi completion in Python interpreter, add the following in your shell
3 | setup (e.g., ``.bashrc``)::
4 |
5 | export PYTHONSTARTUP="$(python -m jedi repl)"
6 |
7 | Then you will be able to use Jedi completer in your Python interpreter::
8 |
9 | $ python
10 | Python 2.7.2+ (default, Jul 20 2012, 22:15:08)
11 | [GCC 4.6.1] on linux2
12 | Type "help", "copyright", "credits" or "license" for more information.
13 | >>> import os
14 | >>> os.path.join().split().in # doctest: +SKIP
15 | os.path.join().split().index os.path.join().split().insert
16 |
17 | """
18 | import jedi.utils
19 | from jedi import __version__ as __jedi_version__
20 |
21 | print('REPL completion using Jedi %s' % __jedi_version__)
22 | jedi.utils.setup_readline()
23 |
24 | del jedi
25 |
26 | # Note: try not to do many things here, as it will contaminate global
27 | # namespace of the interpreter.
28 |
--------------------------------------------------------------------------------
/multi_script_editor/jedi/api/usages.py:
--------------------------------------------------------------------------------
1 | from jedi._compatibility import u, unicode
2 | from jedi import common
3 | from jedi.api import classes
4 | from jedi.parser import representation as pr
5 | from jedi.evaluate import imports
6 | from jedi.evaluate import helpers
7 |
8 |
9 | def usages(evaluator, definitions, search_name, mods):
10 | def compare_array(definitions):
11 | """ `definitions` are being compared by module/start_pos, because
12 | sometimes the id's of the objects change (e.g. executions).
13 | """
14 | result = []
15 | for d in definitions:
16 | module = d.get_parent_until()
17 | result.append((module, d.start_pos))
18 | return result
19 |
20 | def check_call_for_usage(call):
21 | stmt = call.parent
22 | while not isinstance(stmt.parent, pr.IsScope):
23 | stmt = stmt.parent
24 | # New definition, call cannot be a part of stmt
25 | if len(call.name) == 1 and call.execution is None \
26 | and call.name in stmt.get_defined_names():
27 | # Class params are not definitions (like function params). They
28 | # are super classes, that need to be resolved.
29 | if not (isinstance(stmt, pr.Param) and isinstance(stmt.parent, pr.Class)):
30 | return
31 |
32 | follow = [] # There might be multiple search_name's in one call_path
33 | call_path = list(call.generate_call_path())
34 | for i, name in enumerate(call_path):
35 | # name is `pr.NamePart`.
36 | if u(name) == search_name:
37 | follow.append(call_path[:i + 1])
38 |
39 | for call_path in follow:
40 | follow_res, search = evaluator.goto(call.parent, call_path)
41 | # names can change (getattr stuff), therefore filter names that
42 | # don't match `search`.
43 |
44 | # TODO add something like that in the future - for now usages are
45 | # completely broken anyway.
46 | #follow_res = [r for r in follow_res if str(r) == search]
47 | #print search.start_pos,search_name.start_pos
48 | #print follow_res, search, search_name, [(r, r.start_pos) for r in follow_res]
49 | follow_res = usages_add_import_modules(evaluator, follow_res, search)
50 |
51 | compare_follow_res = compare_array(follow_res)
52 | # compare to see if they match
53 | if any(r in compare_definitions for r in compare_follow_res):
54 | yield classes.Definition(evaluator, search)
55 |
56 | if not definitions:
57 | return set()
58 |
59 | compare_definitions = compare_array(definitions)
60 | mods |= set([d.get_parent_until() for d in definitions])
61 | names = []
62 | for m in imports.get_modules_containing_name(mods, search_name):
63 | try:
64 | stmts = m.used_names[search_name]
65 | except KeyError:
66 | continue
67 | for stmt in stmts:
68 | if isinstance(stmt, pr.Import):
69 | count = 0
70 | imps = []
71 | for i in stmt.get_all_import_names():
72 | for name_part in i.names:
73 | count += 1
74 | if unicode(name_part) == search_name:
75 | imps.append((count, name_part))
76 |
77 | for used_count, name_part in imps:
78 | i = imports.ImportWrapper(evaluator, stmt, kill_count=count - used_count,
79 | nested_resolve=True)
80 | f = i.follow(is_goto=True)
81 | if set(f) & set(definitions):
82 | names.append(classes.Definition(evaluator, name_part))
83 | else:
84 | for call in helpers.scan_statement_for_calls(stmt, search_name, assignment_details=True):
85 | names += check_call_for_usage(call)
86 | return names
87 |
88 |
89 | def usages_add_import_modules(evaluator, definitions, search_name):
90 | """ Adds the modules of the imports """
91 | new = set()
92 | for d in definitions:
93 | if isinstance(d.parent, pr.Import):
94 | s = imports.ImportWrapper(evaluator, d.parent, nested_resolve=True)
95 | with common.ignored(IndexError):
96 | new.add(s.follow(is_goto=True)[0])
97 | return set(definitions) | new
98 |
--------------------------------------------------------------------------------
/multi_script_editor/jedi/common.py:
--------------------------------------------------------------------------------
1 | """ A universal module with functions / classes without dependencies. """
2 | import sys
3 | import contextlib
4 | import functools
5 | import re
6 | from ast import literal_eval
7 |
8 | from jedi._compatibility import unicode, next, reraise
9 | from jedi import settings
10 |
11 |
12 | class MultiLevelStopIteration(Exception):
13 | """
14 | StopIteration's get catched pretty easy by for loops, let errors propagate.
15 | """
16 |
17 |
18 | class UncaughtAttributeError(Exception):
19 | """
20 | Important, because `__getattr__` and `hasattr` catch AttributeErrors
21 | implicitly. This is really evil (mainly because of `__getattr__`).
22 | `hasattr` in Python 2 is even more evil, because it catches ALL exceptions.
23 | Therefore this class originally had to be derived from `BaseException`
24 | instead of `Exception`. But because I removed relevant `hasattr` from
25 | the code base, we can now switch back to `Exception`.
26 |
27 | :param base: return values of sys.exc_info().
28 | """
29 |
30 |
31 | def safe_property(func):
32 | return property(reraise_uncaught(func))
33 |
34 |
35 | def reraise_uncaught(func):
36 | """
37 | Re-throw uncaught `AttributeError`.
38 |
39 | Usage: Put ``@rethrow_uncaught`` in front of the function
40 | which does **not** suppose to raise `AttributeError`.
41 |
42 | AttributeError is easily get caught by `hasattr` and another
43 | ``except AttributeError`` clause. This becomes problem when you use
44 | a lot of "dynamic" attributes (e.g., using ``@property``) because you
45 | can't distinguish if the property does not exist for real or some code
46 | inside of the "dynamic" attribute through that error. In a well
47 | written code, such error should not exist but getting there is very
48 | difficult. This decorator is to help us getting there by changing
49 | `AttributeError` to `UncaughtAttributeError` to avoid unexpected catch.
50 | This helps us noticing bugs earlier and facilitates debugging.
51 |
52 | .. note:: Treating StopIteration here is easy.
53 | Add that feature when needed.
54 | """
55 | @functools.wraps(func)
56 | def wrapper(*args, **kwds):
57 | try:
58 | return func(*args, **kwds)
59 | except AttributeError:
60 | exc_info = sys.exc_info()
61 | reraise(UncaughtAttributeError(exc_info[1]), exc_info[2])
62 | return wrapper
63 |
64 |
65 | class PushBackIterator(object):
66 | def __init__(self, iterator):
67 | self.pushes = []
68 | self.iterator = iterator
69 | self.current = None
70 |
71 | def push_back(self, value):
72 | self.pushes.append(value)
73 |
74 | def __iter__(self):
75 | return self
76 |
77 | def next(self):
78 | """ Python 2 Compatibility """
79 | return self.__next__()
80 |
81 | def __next__(self):
82 | if self.pushes:
83 | self.current = self.pushes.pop()
84 | else:
85 | self.current = next(self.iterator)
86 | return self.current
87 |
88 |
89 | @contextlib.contextmanager
90 | def scale_speed_settings(factor):
91 | a = settings.max_executions
92 | b = settings.max_until_execution_unique
93 | settings.max_executions *= factor
94 | settings.max_until_execution_unique *= factor
95 | yield
96 | settings.max_executions = a
97 | settings.max_until_execution_unique = b
98 |
99 |
100 | def indent_block(text, indention=' '):
101 | """This function indents a text block with a default of four spaces."""
102 | temp = ''
103 | while text and text[-1] == '\n':
104 | temp += text[-1]
105 | text = text[:-1]
106 | lines = text.split('\n')
107 | return '\n'.join(map(lambda s: indention + s, lines)) + temp
108 |
109 |
110 | @contextlib.contextmanager
111 | def ignored(*exceptions):
112 | """
113 | Context manager that ignores all of the specified exceptions. This will
114 | be in the standard library starting with Python 3.4.
115 | """
116 | try:
117 | yield
118 | except exceptions:
119 | pass
120 |
121 |
122 | def source_to_unicode(source, encoding=None):
123 | def detect_encoding():
124 | """
125 | For the implementation of encoding definitions in Python, look at:
126 | - http://www.python.org/dev/peps/pep-0263/
127 | - http://docs.python.org/2/reference/lexical_analysis.html#encoding-declarations
128 | """
129 | byte_mark = literal_eval(r"b'\xef\xbb\xbf'")
130 | if source.startswith(byte_mark):
131 | # UTF-8 byte-order mark
132 | return 'utf-8'
133 |
134 | first_two_lines = re.match(r'(?:[^\n]*\n){0,2}', str(source)).group(0)
135 | possible_encoding = re.search(r"coding[=:]\s*([-\w.]+)",
136 | first_two_lines)
137 | if possible_encoding:
138 | return possible_encoding.group(1)
139 | else:
140 | # the default if nothing else has been set -> PEP 263
141 | return encoding if encoding is not None else 'iso-8859-1'
142 |
143 | if isinstance(source, unicode):
144 | # only cast str/bytes
145 | return source
146 |
147 | # cast to unicode by default
148 | return unicode(source, detect_encoding(), 'replace')
149 |
150 |
151 | def splitlines(string):
152 | """
153 | A splitlines for Python code. In contrast to Python's ``str.splitlines``,
154 | looks at form feeds and other special characters as normal text. Just
155 | splits ``\n`` and ``\r\n``.
156 | Also different: Returns ``['']`` for an empty string input.
157 | """
158 | return re.split('\n|\r\n', string)
159 |
--------------------------------------------------------------------------------
/multi_script_editor/jedi/debug.py:
--------------------------------------------------------------------------------
1 | from jedi._compatibility import encoding, is_py3
2 | import inspect
3 | import os
4 | import time
5 |
6 | try:
7 | if os.name == 'nt':
8 | # does not work on Windows, as pyreadline and colorama interfere
9 | raise ImportError
10 | else:
11 | # Use colorama for nicer console output.
12 | from colorama import Fore, init
13 | from colorama import initialise
14 | # pytest resets the stream at the end - causes troubles. Since after
15 | # every output the stream is reset automatically we don't need this.
16 | initialise.atexit_done = True
17 | init()
18 | except ImportError:
19 | class Fore(object):
20 | RED = ''
21 | GREEN = ''
22 | YELLOW = ''
23 | RESET = ''
24 |
25 | NOTICE = object()
26 | WARNING = object()
27 | SPEED = object()
28 |
29 | enable_speed = False
30 | enable_warning = False
31 | enable_notice = False
32 |
33 | # callback, interface: level, str
34 | debug_function = None
35 | ignored_modules = ['jedi.evaluate.builtin', 'jedi.parser']
36 | _debug_indent = -1
37 | _start_time = time.time()
38 |
39 |
40 | def reset_time():
41 | global _start_time, _debug_indent
42 | _start_time = time.time()
43 | _debug_indent = -1
44 |
45 |
46 | def increase_indent(func):
47 | """Decorator for makin """
48 | def wrapper(*args, **kwargs):
49 | global _debug_indent
50 | _debug_indent += 1
51 | result = func(*args, **kwargs)
52 | _debug_indent -= 1
53 | return result
54 | return wrapper
55 |
56 |
57 | def dbg(message, *args):
58 | """ Looks at the stack, to see if a debug message should be printed. """
59 | if debug_function and enable_notice:
60 | frm = inspect.stack()[1]
61 | mod = inspect.getmodule(frm[0])
62 | if not (mod.__name__ in ignored_modules):
63 | i = ' ' * _debug_indent
64 | debug_function(NOTICE, i + 'dbg: ' + message % args)
65 |
66 |
67 | def warning(message, *args):
68 | if debug_function and enable_warning:
69 | i = ' ' * _debug_indent
70 | debug_function(WARNING, i + 'warning: ' + message % args)
71 |
72 |
73 | def speed(name):
74 | if debug_function and enable_speed:
75 | now = time.time()
76 | i = ' ' * _debug_indent
77 | debug_function(SPEED, i + 'speed: ' + '%s %s' % (name, now - _start_time))
78 |
79 |
80 | def print_to_stdout(level, str_out):
81 | """ The default debug function """
82 | if level == NOTICE:
83 | col = Fore.GREEN
84 | elif level == WARNING:
85 | col = Fore.RED
86 | else:
87 | col = Fore.YELLOW
88 | if not is_py3:
89 | str_out = str_out.encode(encoding, 'replace')
90 | print(col + str_out + Fore.RESET)
91 |
92 |
93 | # debug_function = print_to_stdout
94 |
--------------------------------------------------------------------------------
/multi_script_editor/jedi/evaluate/cache.py:
--------------------------------------------------------------------------------
1 | """
2 | - the popular ``memoize_default`` works like a typical memoize and returns the
3 | default otherwise.
4 | - ``CachedMetaClass`` uses ``memoize_default`` to do the same with classes.
5 | """
6 |
7 | NO_DEFAULT = object()
8 |
9 |
10 | def memoize_default(default=None, evaluator_is_first_arg=False, second_arg_is_evaluator=False):
11 | """ This is a typical memoization decorator, BUT there is one difference:
12 | To prevent recursion it sets defaults.
13 |
14 | Preventing recursion is in this case the much bigger use than speed. I
15 | don't think, that there is a big speed difference, but there are many cases
16 | where recursion could happen (think about a = b; b = a).
17 | """
18 | def func(function):
19 | def wrapper(obj, *args, **kwargs):
20 | if evaluator_is_first_arg:
21 | cache = obj.memoize_cache
22 | elif second_arg_is_evaluator: # needed for meta classes
23 | cache = args[0].memoize_cache
24 | else:
25 | cache = obj._evaluator.memoize_cache
26 |
27 | try:
28 | memo = cache[function]
29 | except KeyError:
30 | memo = {}
31 | cache[function] = memo
32 |
33 | key = (obj, args, frozenset(kwargs.items()))
34 | if key in memo:
35 | return memo[key]
36 | else:
37 | if default is not NO_DEFAULT:
38 | memo[key] = default
39 | rv = function(obj, *args, **kwargs)
40 | memo[key] = rv
41 | return rv
42 | return wrapper
43 | return func
44 |
45 |
46 | class CachedMetaClass(type):
47 | """
48 | This is basically almost the same than the decorator above, it just caches
49 | class initializations. I haven't found any other way, so I'm doing it with
50 | meta classes.
51 | """
52 | @memoize_default(None, second_arg_is_evaluator=True)
53 | def __call__(self, *args, **kwargs):
54 | return super(CachedMetaClass, self).__call__(*args, **kwargs)
55 |
--------------------------------------------------------------------------------
/multi_script_editor/jedi/evaluate/compiled/fake.py:
--------------------------------------------------------------------------------
1 | """
2 | Loads functions that are mixed in to the standard library. E.g. builtins are
3 | written in C (binaries), but my autocompletion only understands Python code. By
4 | mixing in Python code, the autocompletion should work much better for builtins.
5 | """
6 |
7 | import os
8 | import inspect
9 |
10 | from jedi._compatibility import is_py3, builtins, unicode
11 | from jedi.parser import Parser
12 | from jedi.parser import tokenize
13 | from jedi.parser.representation import Class
14 | from jedi.evaluate.helpers import FakeName
15 |
16 | modules = {}
17 |
18 |
19 | def _load_faked_module(module):
20 | module_name = module.__name__
21 | if module_name == '__builtin__' and not is_py3:
22 | module_name = 'builtins'
23 |
24 | try:
25 | return modules[module_name]
26 | except KeyError:
27 | path = os.path.dirname(os.path.abspath(__file__))
28 | try:
29 | with open(os.path.join(path, 'fake', module_name) + '.pym') as f:
30 | source = f.read()
31 | except IOError:
32 | modules[module_name] = None
33 | return
34 | module = Parser(unicode(source), module_name).module
35 | modules[module_name] = module
36 |
37 | if module_name == 'builtins' and not is_py3:
38 | # There are two implementations of `open` for either python 2/3.
39 | # -> Rename the python2 version (`look at fake/builtins.pym`).
40 | open_func = search_scope(module, 'open')
41 | open_func.name = FakeName('open_python3')
42 | open_func = search_scope(module, 'open_python2')
43 | open_func.name = FakeName('open')
44 | return module
45 |
46 |
47 | def search_scope(scope, obj_name):
48 | for s in scope.subscopes:
49 | if str(s.name) == obj_name:
50 | return s
51 |
52 |
53 | def get_module(obj):
54 | if inspect.ismodule(obj):
55 | return obj
56 | try:
57 | obj = obj.__objclass__
58 | except AttributeError:
59 | pass
60 |
61 | try:
62 | imp_plz = obj.__module__
63 | except AttributeError:
64 | # Unfortunately in some cases like `int` there's no __module__
65 | return builtins
66 | else:
67 | return __import__(imp_plz)
68 |
69 |
70 | def _faked(module, obj, name):
71 | # Crazy underscore actions to try to escape all the internal madness.
72 | if module is None:
73 | module = get_module(obj)
74 |
75 | faked_mod = _load_faked_module(module)
76 | if faked_mod is None:
77 | return
78 |
79 | # Having the module as a `parser.representation.module`, we need to scan
80 | # for methods.
81 | if name is None:
82 | if inspect.isbuiltin(obj):
83 | return search_scope(faked_mod, obj.__name__)
84 | elif not inspect.isclass(obj):
85 | # object is a method or descriptor
86 | cls = search_scope(faked_mod, obj.__objclass__.__name__)
87 | if cls is None:
88 | return
89 | return search_scope(cls, obj.__name__)
90 | else:
91 | if obj == module:
92 | return search_scope(faked_mod, name)
93 | else:
94 | cls = search_scope(faked_mod, obj.__name__)
95 | if cls is None:
96 | return
97 | return search_scope(cls, name)
98 |
99 |
100 | def get_faked(module, obj, name=None):
101 | obj = obj.__class__ if is_class_instance(obj) else obj
102 | result = _faked(module, obj, name)
103 | if not isinstance(result, Class) and result is not None:
104 | # Set the docstr which was previously not set (faked modules don't
105 | # contain it).
106 | doc = '''"""%s"""''' % obj.__doc__ # TODO need escapes.
107 | result.add_docstr(tokenize.Token(tokenize.STRING, doc, (0, 0)))
108 | return result
109 |
110 |
111 | def is_class_instance(obj):
112 | """Like inspect.* methods."""
113 | return not (inspect.isclass(obj) or inspect.ismodule(obj)
114 | or inspect.isbuiltin(obj) or inspect.ismethod(obj)
115 | or inspect.ismethoddescriptor(obj) or inspect.iscode(obj)
116 | or inspect.isgenerator(obj))
117 |
--------------------------------------------------------------------------------
/multi_script_editor/jedi/evaluate/compiled/fake/_functools.pym:
--------------------------------------------------------------------------------
1 | class partial():
2 | def __init__(self, func, *args, **keywords):
3 | self.__func = func
4 | self.__args = args
5 | self.__keywords = keywords
6 |
7 | def __call__(self, *args, **kwargs):
8 | # I know this doesn't work in Python, but in Jedi it does ;-)
9 | return self.__func(*self.__args, *args, **self.keywords, **kwargs)
10 |
--------------------------------------------------------------------------------
/multi_script_editor/jedi/evaluate/compiled/fake/_sqlite3.pym:
--------------------------------------------------------------------------------
1 | def connect(database, timeout=None, isolation_level=None, detect_types=None, factory=None):
2 | return Connection()
3 |
4 |
5 | class Connection():
6 | def cursor(self):
7 | return Cursor()
8 |
9 |
10 | class Cursor():
11 | def cursor(self):
12 | return Cursor()
13 |
14 | def fetchone(self):
15 | return Row()
16 |
17 | def fetchmany(self, size=cursor.arraysize):
18 | return [self.fetchone()]
19 |
20 | def fetchall(self):
21 | return [self.fetchone()]
22 |
23 |
24 | class Row():
25 | def keys(self):
26 | return ['']
27 |
--------------------------------------------------------------------------------
/multi_script_editor/jedi/evaluate/compiled/fake/_sre.pym:
--------------------------------------------------------------------------------
1 | def compile():
2 | class SRE_Match():
3 | endpos = int()
4 | lastgroup = int()
5 | lastindex = int()
6 | pos = int()
7 | string = str()
8 | regs = ((int(), int()),)
9 |
10 | def __init__(self, pattern):
11 | self.re = pattern
12 |
13 | def start(self):
14 | return int()
15 |
16 | def end(self):
17 | return int()
18 |
19 | def span(self):
20 | return int(), int()
21 |
22 | def expand(self):
23 | return str()
24 |
25 | def group(self, nr):
26 | return str()
27 |
28 | def groupdict(self):
29 | return {str(): str()}
30 |
31 | def groups(self):
32 | return (str(),)
33 |
34 | class SRE_Pattern():
35 | flags = int()
36 | groupindex = {}
37 | groups = int()
38 | pattern = str()
39 |
40 | def findall(self, string, pos=None, endpos=None):
41 | """
42 | findall(string[, pos[, endpos]]) --> list.
43 | Return a list of all non-overlapping matches of pattern in string.
44 | """
45 | return [str()]
46 |
47 | def finditer(self, string, pos=None, endpos=None):
48 | """
49 | finditer(string[, pos[, endpos]]) --> iterator.
50 | Return an iterator over all non-overlapping matches for the
51 | RE pattern in string. For each match, the iterator returns a
52 | match object.
53 | """
54 | yield SRE_Match(self)
55 |
56 | def match(self, string, pos=None, endpos=None):
57 | """
58 | match(string[, pos[, endpos]]) --> match object or None.
59 | Matches zero or more characters at the beginning of the string
60 | pattern
61 | """
62 | return SRE_Match(self)
63 |
64 | def scanner(self, string, pos=None, endpos=None):
65 | pass
66 |
67 | def search(self, string, pos=None, endpos=None):
68 | """
69 | search(string[, pos[, endpos]]) --> match object or None.
70 | Scan through string looking for a match, and return a corresponding
71 | MatchObject instance. Return None if no position in the string matches.
72 | """
73 | return SRE_Match(self)
74 |
75 | def split(self, string, maxsplit=0]):
76 | """
77 | split(string[, maxsplit = 0]) --> list.
78 | Split string by the occurrences of pattern.
79 | """
80 | return [str()]
81 |
82 | def sub(self, repl, string, count=0):
83 | """
84 | sub(repl, string[, count = 0]) --> newstring
85 | Return the string obtained by replacing the leftmost non-overlapping
86 | occurrences of pattern in string by the replacement repl.
87 | """
88 | return str()
89 |
90 | def subn(self, repl, string, count=0):
91 | """
92 | subn(repl, string[, count = 0]) --> (newstring, number of subs)
93 | Return the tuple (new_string, number_of_subs_made) found by replacing
94 | the leftmost non-overlapping occurrences of pattern with the
95 | replacement repl.
96 | """
97 | return (str(), int())
98 |
99 | return SRE_Pattern()
100 |
--------------------------------------------------------------------------------
/multi_script_editor/jedi/evaluate/compiled/fake/_weakref.pym:
--------------------------------------------------------------------------------
1 | def proxy(object, callback=None):
2 | return object
3 |
4 | class weakref():
5 | def __init__(self, object, callback=None):
6 | self.__object = object
7 | def __call__(self):
8 | return self.__object
9 |
--------------------------------------------------------------------------------
/multi_script_editor/jedi/evaluate/compiled/fake/builtins.pym:
--------------------------------------------------------------------------------
1 | """
2 | Pure Python implementation of some builtins.
3 | This code is not going to be executed anywhere.
4 | These implementations are not always correct, but should work as good as
5 | possible for the auto completion.
6 | """
7 |
8 |
9 | def next(iterator, default=None):
10 | if hasattr("next"):
11 | return iterator.next()
12 | else:
13 | return iterator.__next__()
14 | return default
15 |
16 |
17 | def iter(collection, sentinel=None):
18 | if sentinel:
19 | yield collection()
20 | else:
21 | for c in collection:
22 | yield c
23 |
24 |
25 | def range(start, stop=None, step=1):
26 | return [0]
27 |
28 |
29 | class file():
30 | def __iter__(self):
31 | yield ''
32 | def next(self):
33 | return ''
34 |
35 |
36 | class xrange():
37 | # Attention: this function doesn't exist in Py3k (there it is range).
38 | def __iter__(self):
39 | yield 1
40 |
41 | def count(self):
42 | return 1
43 |
44 | def index(self):
45 | return 1
46 |
47 |
48 | def open(file, mode='r', buffering=-1, encoding=None, errors=None, newline=None, closefd=True):
49 | import io
50 | return io.TextIOWrapper(file, mode, buffering, encoding, errors, newline, closefd)
51 |
52 |
53 | def open_python2(name, mode=None, buffering=None):
54 | return file(name, mode, buffering)
55 |
56 |
57 | #--------------------------------------------------------
58 | # descriptors
59 | #--------------------------------------------------------
60 | class property():
61 | def __init__(self, fget, fset=None, fdel=None, doc=None):
62 | self.fget = fget
63 | self.fset = fset
64 | self.fdel = fdel
65 | self.__doc__ = doc
66 |
67 | def __get__(self, obj, cls):
68 | return self.fget(obj)
69 |
70 | def __set__(self, obj, value):
71 | self.fset(obj, value)
72 |
73 | def __delete__(self, obj):
74 | self.fdel(obj)
75 |
76 | def setter(self, func):
77 | self.fset = func
78 | return self
79 |
80 | def getter(self, func):
81 | self.fget = func
82 | return self
83 |
84 | def deleter(self, func):
85 | self.fdel = func
86 | return self
87 |
88 |
89 | class staticmethod():
90 | def __init__(self, func):
91 | self.__func = func
92 |
93 | def __get__(self, obj, cls):
94 | return self.__func
95 |
96 |
97 | class classmethod():
98 | def __init__(self, func):
99 | self.__func = func
100 |
101 | def __get__(self, obj, cls):
102 | def _method(*args, **kwargs):
103 | return self.__func(cls, *args, **kwargs)
104 | return _method
105 |
106 |
107 | #--------------------------------------------------------
108 | # array stuff
109 | #--------------------------------------------------------
110 | class list():
111 | def __init__(self, iterable=[]):
112 | self.__iterable = []
113 | for i in iterable:
114 | self.__iterable += [i]
115 |
116 | def __iter__(self):
117 | for i in self.__iterable:
118 | yield i
119 |
120 | def __getitem__(self, y):
121 | return self.__iterable[y]
122 |
123 | def pop(self):
124 | return self.__iterable[-1]
125 |
126 |
127 | class tuple():
128 | def __init__(self, iterable=[]):
129 | self.__iterable = []
130 | for i in iterable:
131 | self.__iterable += [i]
132 |
133 | def __iter__(self):
134 | for i in self.__iterable:
135 | yield i
136 |
137 | def __getitem__(self, y):
138 | return self.__iterable[y]
139 |
140 | def index(self):
141 | return 1
142 |
143 | def count(self):
144 | return 1
145 |
146 |
147 | class set():
148 | def __init__(self, iterable=[]):
149 | self.__iterable = iterable
150 |
151 | def __iter__(self):
152 | for i in self.__iterable:
153 | yield i
154 |
155 | def pop(self):
156 | return self.__iterable.pop()
157 |
158 | def copy(self):
159 | return self
160 |
161 | def difference(self, other):
162 | return self - other
163 |
164 | def intersection(self, other):
165 | return self & other
166 |
167 | def symmetric_difference(self, other):
168 | return self ^ other
169 |
170 | def union(self, other):
171 | return self | other
172 |
173 |
174 | class frozenset():
175 | def __init__(self, iterable=[]):
176 | self.__iterable = iterable
177 |
178 | def __iter__(self):
179 | for i in self.__iterable:
180 | yield i
181 |
182 | def copy(self):
183 | return self
184 |
185 |
186 | class dict():
187 | def __init__(self, **elements):
188 | self.__elements = elements
189 |
190 | def clear(self):
191 | # has a strange docstr
192 | pass
193 |
194 | def get(self, k, d=None):
195 | # TODO implement
196 | try:
197 | #return self.__elements[k]
198 | pass
199 | except KeyError:
200 | return d
201 |
202 | def setdefault(self, k, d):
203 | # TODO maybe also return the content
204 | return d
205 |
206 |
207 | class reversed():
208 | def __init__(self, sequence):
209 | self.__sequence = sequence
210 |
211 | def __iter__(self):
212 | for i in self.__sequence:
213 | yield i
214 |
215 | def __next__(self):
216 | return next(self.__iter__())
217 |
218 | def next(self):
219 | return next(self.__iter__())
220 |
221 |
222 | def sorted(iterable, cmp=None, key=None, reverse=False):
223 | return iterable
224 |
225 |
226 | #--------------------------------------------------------
227 | # basic types
228 | #--------------------------------------------------------
229 | class int():
230 | def __init__(self, x, base=None):
231 | pass
232 |
233 |
234 | class str():
235 | def __init__(self, obj):
236 | pass
237 |
238 |
239 | class type():
240 | def mro():
241 | return [object]
242 |
--------------------------------------------------------------------------------
/multi_script_editor/jedi/evaluate/compiled/fake/datetime.pym:
--------------------------------------------------------------------------------
1 | class datetime():
2 | @staticmethod
3 | def now():
4 | return datetime()
5 |
--------------------------------------------------------------------------------
/multi_script_editor/jedi/evaluate/compiled/fake/io.pym:
--------------------------------------------------------------------------------
1 | class TextIOWrapper():
2 | def __next__(self):
3 | return 'hacked io return'
4 |
--------------------------------------------------------------------------------
/multi_script_editor/jedi/evaluate/compiled/fake/posix.pym:
--------------------------------------------------------------------------------
1 | def getcwd():
2 | return ''
3 |
4 | def getcwdu():
5 | return ''
6 |
--------------------------------------------------------------------------------
/multi_script_editor/jedi/evaluate/docstrings.py:
--------------------------------------------------------------------------------
1 | """
2 | Docstrings are another source of information for functions and classes.
3 | :mod:`jedi.evaluate.dynamic` tries to find all executions of functions, while
4 | the docstring parsing is much easier. There are two different types of
5 | docstrings that |jedi| understands:
6 |
7 | - `Sphinx `_
8 | - `Epydoc `_
9 |
10 | For example, the sphinx annotation ``:type foo: str`` clearly states that the
11 | type of ``foo`` is ``str``.
12 |
13 | As an addition to parameter searching, this module also provides return
14 | annotations.
15 | """
16 |
17 | import re
18 | from itertools import chain
19 | from textwrap import dedent
20 |
21 | from jedi.evaluate.cache import memoize_default
22 | from jedi.parser import Parser
23 | from jedi.common import indent_block
24 |
25 | DOCSTRING_PARAM_PATTERNS = [
26 | r'\s*:type\s+%s:\s*([^\n]+)', # Sphinx
27 | r'\s*@type\s+%s:\s*([^\n]+)', # Epydoc
28 | ]
29 |
30 | DOCSTRING_RETURN_PATTERNS = [
31 | re.compile(r'\s*:rtype:\s*([^\n]+)', re.M), # Sphinx
32 | re.compile(r'\s*@rtype:\s*([^\n]+)', re.M), # Epydoc
33 | ]
34 |
35 | REST_ROLE_PATTERN = re.compile(r':[^`]+:`([^`]+)`')
36 |
37 |
38 | @memoize_default(None, evaluator_is_first_arg=True)
39 | def follow_param(evaluator, param):
40 | func = param.parent_function
41 | param_str = _search_param_in_docstr(func.raw_doc, str(param.get_name()))
42 | return _evaluate_for_statement_string(evaluator, param_str, param.get_parent_until())
43 |
44 |
45 | def _search_param_in_docstr(docstr, param_str):
46 | """
47 | Search `docstr` for a type of `param_str`.
48 |
49 | >>> _search_param_in_docstr(':type param: int', 'param')
50 | 'int'
51 | >>> _search_param_in_docstr('@type param: int', 'param')
52 | 'int'
53 | >>> _search_param_in_docstr(
54 | ... ':type param: :class:`threading.Thread`', 'param')
55 | 'threading.Thread'
56 | >>> _search_param_in_docstr('no document', 'param') is None
57 | True
58 |
59 | """
60 | # look at #40 to see definitions of those params
61 | patterns = [re.compile(p % re.escape(param_str))
62 | for p in DOCSTRING_PARAM_PATTERNS]
63 | for pattern in patterns:
64 | match = pattern.search(docstr)
65 | if match:
66 | return _strip_rst_role(match.group(1))
67 |
68 | return None
69 |
70 |
71 | def _strip_rst_role(type_str):
72 | """
73 | Strip off the part looks like a ReST role in `type_str`.
74 |
75 | >>> _strip_rst_role(':class:`ClassName`') # strip off :class:
76 | 'ClassName'
77 | >>> _strip_rst_role(':py:obj:`module.Object`') # works with domain
78 | 'module.Object'
79 | >>> _strip_rst_role('ClassName') # do nothing when not ReST role
80 | 'ClassName'
81 |
82 | See also:
83 | http://sphinx-doc.org/domains.html#cross-referencing-python-objects
84 |
85 | """
86 | match = REST_ROLE_PATTERN.match(type_str)
87 | if match:
88 | return match.group(1)
89 | else:
90 | return type_str
91 |
92 |
93 | def _evaluate_for_statement_string(evaluator, string, module):
94 | code = dedent("""
95 | def pseudo_docstring_stuff():
96 | '''Create a pseudo function for docstring statements.'''
97 | %s
98 | """)
99 | if string is None:
100 | return []
101 |
102 | for element in re.findall('((?:\w+\.)*\w+)\.', string):
103 | # Try to import module part in dotted name.
104 | # (e.g., 'threading' in 'threading.Thread').
105 | string = 'import %s\n' % element + string
106 |
107 | p = Parser(code % indent_block(string), no_docstr=True)
108 | pseudo_cls = p.module.subscopes[0]
109 | try:
110 | stmt = pseudo_cls.statements[-1]
111 | except IndexError:
112 | return []
113 |
114 | # Use the module of the param.
115 | # TODO this module is not the module of the param in case of a function
116 | # call. In that case it's the module of the function call.
117 | # stuffed with content from a function call.
118 | pseudo_cls.parent = module
119 | definitions = evaluator.eval_statement(stmt)
120 | it = (evaluator.execute(d) for d in definitions)
121 | # TODO Executing tuples does not make sense, people tend to say
122 | # `(str, int)` in a type annotation, which means that it returns a tuple
123 | # with both types.
124 | # At this point we just return the classes if executing wasn't possible,
125 | # i.e. is a tuple.
126 | return list(chain.from_iterable(it)) or definitions
127 |
128 |
129 | @memoize_default(None, evaluator_is_first_arg=True)
130 | def find_return_types(evaluator, func):
131 | def search_return_in_docstr(code):
132 | for p in DOCSTRING_RETURN_PATTERNS:
133 | match = p.search(code)
134 | if match:
135 | return _strip_rst_role(match.group(1))
136 |
137 | type_str = search_return_in_docstr(func.raw_doc)
138 | return _evaluate_for_statement_string(evaluator, type_str, func.get_parent_until())
139 |
--------------------------------------------------------------------------------
/multi_script_editor/jedi/evaluate/dynamic.py:
--------------------------------------------------------------------------------
1 | """
2 | One of the really important features of |jedi| is to have an option to
3 | understand code like this::
4 |
5 | def foo(bar):
6 | bar. # completion here
7 | foo(1)
8 |
9 | There's no doubt wheter bar is an ``int`` or not, but if there's also a call
10 | like ``foo('str')``, what would happen? Well, we'll just show both. Because
11 | that's what a human would expect.
12 |
13 | It works as follows:
14 |
15 | - |Jedi| sees a param
16 | - search for function calls named ``foo``
17 | - execute these calls and check the input. This work with a ``ParamListener``.
18 | """
19 |
20 | from jedi._compatibility import unicode
21 | from jedi.parser import representation as pr
22 | from jedi import settings
23 | from jedi.evaluate import helpers
24 | from jedi.evaluate.cache import memoize_default
25 | from jedi.evaluate import imports
26 |
27 | # This is something like the sys.path, but only for searching params. It means
28 | # that this is the order in which Jedi searches params.
29 | search_param_modules = ['.']
30 |
31 |
32 | class ParamListener(object):
33 | """
34 | This listener is used to get the params for a function.
35 | """
36 | def __init__(self):
37 | self.param_possibilities = []
38 |
39 | def execute(self, params):
40 | self.param_possibilities.append(params)
41 |
42 |
43 | @memoize_default([], evaluator_is_first_arg=True)
44 | def search_params(evaluator, param):
45 | """
46 | This is a dynamic search for params. If you try to complete a type:
47 |
48 | >>> def func(foo):
49 | ... foo
50 | >>> func(1)
51 | >>> func("")
52 |
53 | It is not known what the type is, because it cannot be guessed with
54 | recursive madness. Therefore one has to analyse the statements that are
55 | calling the function, as well as analyzing the incoming params.
56 | """
57 | if not settings.dynamic_params:
58 | return []
59 |
60 | def get_params_for_module(module):
61 | """
62 | Returns the values of a param, or an empty array.
63 | """
64 | @memoize_default([], evaluator_is_first_arg=True)
65 | def get_posibilities(evaluator, module, func_name):
66 | try:
67 | possible_stmts = module.used_names[func_name]
68 | except KeyError:
69 | return []
70 |
71 | for stmt in possible_stmts:
72 | if isinstance(stmt, pr.Import):
73 | continue
74 | calls = helpers.scan_statement_for_calls(stmt, func_name)
75 | for c in calls:
76 | # no execution means that params cannot be set
77 | call_path = list(c.generate_call_path())
78 | pos = c.start_pos
79 | scope = stmt.parent
80 |
81 | # this whole stuff is just to not execute certain parts
82 | # (speed improvement), basically we could just call
83 | # ``eval_call_path`` on the call_path and it would
84 | # also work.
85 | def listRightIndex(lst, value):
86 | return len(lst) - lst[-1::-1].index(value) - 1
87 |
88 | # Need to take right index, because there could be a
89 | # func usage before.
90 | call_path_simple = [unicode(d) if isinstance(d, pr.NamePart)
91 | else d for d in call_path]
92 | i = listRightIndex(call_path_simple, func_name)
93 | first, last = call_path[:i], call_path[i + 1:]
94 | if not last and not call_path_simple.index(func_name) != i:
95 | continue
96 | scopes = [scope]
97 | if first:
98 | scopes = evaluator.eval_call_path(iter(first), c.parent, pos)
99 | pos = None
100 | from jedi.evaluate import representation as er
101 | for scope in scopes:
102 | s = evaluator.find_types(scope, func_name, position=pos,
103 | search_global=not first,
104 | resolve_decorator=False)
105 |
106 | c = [getattr(escope, 'base_func', None) or escope.base
107 | for escope in s
108 | if escope.isinstance(er.Function, er.Class)]
109 | if compare in c:
110 | # only if we have the correct function we execute
111 | # it, otherwise just ignore it.
112 | evaluator.follow_path(iter(last), s, scope)
113 | return listener.param_possibilities
114 |
115 | result = []
116 | for params in get_posibilities(evaluator, module, func_name):
117 | for p in params:
118 | if str(p) == param_name:
119 | result += evaluator.eval_statement(p.parent)
120 | return result
121 |
122 | func = param.get_parent_until(pr.Function)
123 | current_module = param.get_parent_until()
124 | func_name = unicode(func.name)
125 | compare = func
126 | if func_name == '__init__' and isinstance(func.parent, pr.Class):
127 | func_name = unicode(func.parent.name)
128 | compare = func.parent
129 |
130 | # get the param name
131 | if param.assignment_details:
132 | # first assignment details, others would be a syntax error
133 | expression_list, op = param.assignment_details[0]
134 | else:
135 | expression_list = param.expression_list()
136 | offset = 1 if expression_list[0] in ['*', '**'] else 0
137 | param_name = str(expression_list[offset].name)
138 |
139 | # add the listener
140 | listener = ParamListener()
141 | func.listeners.add(listener)
142 |
143 | result = []
144 | # This is like backtracking: Get the first possible result.
145 | for mod in imports.get_modules_containing_name([current_module], func_name):
146 | result = get_params_for_module(mod)
147 | if result:
148 | break
149 |
150 | # cleanup: remove the listener; important: should not stick.
151 | func.listeners.remove(listener)
152 |
153 | return result
154 |
--------------------------------------------------------------------------------
/multi_script_editor/jedi/evaluate/recursion.py:
--------------------------------------------------------------------------------
1 | """
2 | Recursions are the recipe of |jedi| to conquer Python code. However, someone
3 | must stop recursions going mad. Some settings are here to make |jedi| stop at
4 | the right time. You can read more about them :ref:`here `.
5 |
6 | Next to :mod:`jedi.evaluate.cache` this module also makes |jedi| not
7 | thread-safe. Why? ``ExecutionRecursionDecorator`` uses class variables to
8 | count the function calls.
9 | """
10 | from jedi.parser import representation as pr
11 | from jedi import debug
12 | from jedi import settings
13 | from jedi.evaluate import compiled
14 | from jedi.evaluate import iterable
15 |
16 |
17 | def recursion_decorator(func):
18 | def run(evaluator, stmt, *args, **kwargs):
19 | rec_detect = evaluator.recursion_detector
20 | # print stmt, len(self.node_statements())
21 | if rec_detect.push_stmt(stmt):
22 | return []
23 | else:
24 | result = func(evaluator, stmt, *args, **kwargs)
25 | rec_detect.pop_stmt()
26 | return result
27 | return run
28 |
29 |
30 | class RecursionDetector(object):
31 | """
32 | A decorator to detect recursions in statements. In a recursion a statement
33 | at the same place, in the same module may not be executed two times.
34 | """
35 | def __init__(self):
36 | self.top = None
37 | self.current = None
38 |
39 | def push_stmt(self, stmt):
40 | self.current = _RecursionNode(stmt, self.current)
41 | check = self._check_recursion()
42 | if check:
43 | debug.warning('catched stmt recursion: %s against %s @%s', stmt,
44 | check.stmt, stmt.start_pos)
45 | self.pop_stmt()
46 | return True
47 | return False
48 |
49 | def pop_stmt(self):
50 | if self.current is not None:
51 | # I don't know how current can be None, but sometimes it happens
52 | # with Python3.
53 | self.current = self.current.parent
54 |
55 | def _check_recursion(self):
56 | test = self.current
57 | while True:
58 | test = test.parent
59 | if self.current == test:
60 | return test
61 | if not test:
62 | return False
63 |
64 | def node_statements(self):
65 | result = []
66 | n = self.current
67 | while n:
68 | result.insert(0, n.stmt)
69 | n = n.parent
70 | return result
71 |
72 |
73 | class _RecursionNode(object):
74 | """ A node of the RecursionDecorator. """
75 | def __init__(self, stmt, parent):
76 | self.script = stmt.get_parent_until()
77 | self.position = stmt.start_pos
78 | self.parent = parent
79 | self.stmt = stmt
80 |
81 | # Don't check param instances, they are not causing recursions
82 | # The same's true for the builtins, because the builtins are really
83 | # simple.
84 | self.is_ignored = isinstance(stmt, pr.Param) \
85 | or (self.script == compiled.builtin)
86 |
87 | def __eq__(self, other):
88 | if not other:
89 | return None
90 |
91 | # List Comprehensions start on the same line as its statement.
92 | # Therefore we have the unfortunate situation of the same start_pos for
93 | # two statements.
94 | is_list_comp = lambda x: isinstance(x, pr.ListComprehension)
95 | return self.script == other.script \
96 | and self.position == other.position \
97 | and not is_list_comp(self.stmt.parent) \
98 | and not is_list_comp(other.parent) \
99 | and not self.is_ignored and not other.is_ignored
100 |
101 |
102 | def execution_recursion_decorator(func):
103 | def run(execution, evaluate_generator=False):
104 | detector = execution._evaluator.execution_recursion_detector
105 | if detector.push_execution(execution, evaluate_generator):
106 | result = []
107 | else:
108 | result = func(execution, evaluate_generator)
109 | detector.pop_execution()
110 | return result
111 |
112 | return run
113 |
114 |
115 | class ExecutionRecursionDetector(object):
116 | """
117 | Catches recursions of executions.
118 | It is designed like a Singelton. Only one instance should exist.
119 | """
120 | def __init__(self):
121 | self.recursion_level = 0
122 | self.parent_execution_funcs = []
123 | self.execution_funcs = set()
124 | self.execution_count = 0
125 |
126 | def __call__(self, execution, evaluate_generator=False):
127 | debug.dbg('Execution recursions: %s', execution, self.recursion_level,
128 | self.execution_count, len(self.execution_funcs))
129 | if self.check_recursion(execution, evaluate_generator):
130 | result = []
131 | else:
132 | result = self.func(execution, evaluate_generator)
133 | self.pop_execution()
134 | return result
135 |
136 | def pop_execution(cls):
137 | cls.parent_execution_funcs.pop()
138 | cls.recursion_level -= 1
139 |
140 | def push_execution(cls, execution, evaluate_generator):
141 | in_par_execution_funcs = execution.base in cls.parent_execution_funcs
142 | in_execution_funcs = execution.base in cls.execution_funcs
143 | cls.recursion_level += 1
144 | cls.execution_count += 1
145 | cls.execution_funcs.add(execution.base)
146 | cls.parent_execution_funcs.append(execution.base)
147 |
148 | if cls.execution_count > settings.max_executions:
149 | return True
150 |
151 | if isinstance(execution.base, (iterable.Array, iterable.Generator)):
152 | return False
153 | module = execution.get_parent_until()
154 | if evaluate_generator or module == compiled.builtin:
155 | return False
156 |
157 | if in_par_execution_funcs:
158 | if cls.recursion_level > settings.max_function_recursion_level:
159 | return True
160 | if in_execution_funcs and \
161 | len(cls.execution_funcs) > settings.max_until_execution_unique:
162 | return True
163 | if cls.execution_count > settings.max_executions_without_builtins:
164 | return True
165 | return False
166 |
--------------------------------------------------------------------------------
/multi_script_editor/jedi/evaluate/stdlib.py:
--------------------------------------------------------------------------------
1 | """
2 | Implementations of standard library functions, because it's not possible to
3 | understand them with Jedi.
4 | """
5 | from jedi._compatibility import unicode
6 | from jedi.evaluate import compiled
7 | from jedi.evaluate import representation as er
8 | from jedi.evaluate import iterable
9 | from jedi.evaluate.helpers import FakeArray, FakeStatement
10 | from jedi.parser import representation as pr
11 | from jedi import debug
12 |
13 |
14 | class NotInStdLib(LookupError):
15 | pass
16 |
17 |
18 | def execute(evaluator, obj, params):
19 | try:
20 | obj_name = str(obj.name)
21 | except AttributeError:
22 | pass
23 | else:
24 | if obj.parent == compiled.builtin:
25 | module_name = 'builtins'
26 | elif isinstance(obj.parent, pr.Module):
27 | module_name = str(obj.parent.name)
28 | else:
29 | module_name = ''
30 |
31 | # for now we just support builtin functions.
32 | try:
33 | return _implemented[module_name][obj_name](evaluator, obj, params)
34 | except KeyError:
35 | pass
36 | raise NotInStdLib()
37 |
38 |
39 | def _follow_param(evaluator, params, index):
40 | try:
41 | stmt = params[index]
42 | except IndexError:
43 | return []
44 | else:
45 | if isinstance(stmt, pr.Statement):
46 | return evaluator.eval_statement(stmt)
47 | else:
48 | return [stmt] # just some arbitrary object
49 |
50 |
51 | def builtins_getattr(evaluator, obj, params):
52 | stmts = []
53 | # follow the first param
54 | objects = _follow_param(evaluator, params, 0)
55 | names = _follow_param(evaluator, params, 1)
56 | for obj in objects:
57 | if not isinstance(obj, (er.Instance, er.Class, pr.Module, compiled.CompiledObject)):
58 | debug.warning('getattr called without instance')
59 | continue
60 |
61 | for name in names:
62 | s = unicode, str
63 | if isinstance(name, compiled.CompiledObject) and isinstance(name.obj, s):
64 | stmts += evaluator.follow_path(iter([name.obj]), [obj], obj)
65 | else:
66 | debug.warning('getattr called without str')
67 | continue
68 | return stmts
69 |
70 |
71 | def builtins_type(evaluator, obj, params):
72 | if len(params) == 1:
73 | # otherwise it would be a metaclass... maybe someday...
74 | objects = _follow_param(evaluator, params, 0)
75 | return [o.base for o in objects if isinstance(o, er.Instance)]
76 | return []
77 |
78 |
79 | def builtins_super(evaluator, obj, params):
80 | # TODO make this able to detect multiple inheritance super
81 | accept = (pr.Function,)
82 | func = params.get_parent_until(accept)
83 | if func.isinstance(*accept):
84 | cls = func.get_parent_until(accept + (pr.Class,),
85 | include_current=False)
86 | if isinstance(cls, pr.Class):
87 | cls = er.Class(evaluator, cls)
88 | su = cls.get_super_classes()
89 | if su:
90 | return evaluator.execute(su[0])
91 | return []
92 |
93 |
94 | def builtins_reversed(evaluator, obj, params):
95 | objects = tuple(_follow_param(evaluator, params, 0))
96 | if objects:
97 | # unpack the iterator values
98 | objects = tuple(iterable.get_iterator_types(objects))
99 | if objects:
100 | rev = reversed(objects)
101 | # Repack iterator values and then run it the normal way. This is
102 | # necessary, because `reversed` is a function and autocompletion
103 | # would fail in certain cases like `reversed(x).__iter__` if we
104 | # just returned the result directly.
105 | stmts = [FakeStatement([r]) for r in rev]
106 | objects = (iterable.Array(evaluator, FakeArray(stmts, objects[0].parent)),)
107 | return [er.Instance(evaluator, obj, objects)]
108 |
109 |
110 | def _return_first_param(evaluator, obj, params):
111 | if len(params) == 1:
112 | return _follow_param(evaluator, params, 0)
113 | return []
114 |
115 |
116 | _implemented = {
117 | 'builtins': {
118 | 'getattr': builtins_getattr,
119 | 'type': builtins_type,
120 | 'super': builtins_super,
121 | 'reversed': builtins_reversed,
122 | },
123 | 'copy': {
124 | 'copy': _return_first_param,
125 | 'deepcopy': _return_first_param,
126 | },
127 | 'json': {
128 | 'load': lambda *args: [],
129 | 'loads': lambda *args: [],
130 | },
131 | }
132 |
--------------------------------------------------------------------------------
/multi_script_editor/jedi/evaluate/sys_path.py:
--------------------------------------------------------------------------------
1 | import os
2 | import sys
3 |
4 | from jedi._compatibility import exec_function, unicode
5 | from jedi.parser import representation as pr
6 | from jedi.parser import Parser
7 | from jedi.evaluate.cache import memoize_default
8 | from jedi import debug
9 | from jedi import common
10 |
11 |
12 | def get_sys_path():
13 | def check_virtual_env(sys_path):
14 | """ Add virtualenv's site-packages to the `sys.path`."""
15 | venv = os.getenv('VIRTUAL_ENV')
16 | if not venv:
17 | return
18 | venv = os.path.abspath(venv)
19 | if os.name == 'nt':
20 | p = os.path.join(venv, 'lib', 'site-packages')
21 | else:
22 | p = os.path.join(venv, 'lib', 'python%d.%d' % sys.version_info[:2],
23 | 'site-packages')
24 | if p not in sys_path:
25 | sys_path.insert(0, p)
26 |
27 | check_virtual_env(sys.path)
28 | return [p for p in sys.path if p != ""]
29 |
30 |
31 | def _execute_code(module_path, code):
32 | c = "import os; from os.path import *; result=%s"
33 | variables = {'__file__': module_path}
34 | try:
35 | exec_function(c % code, variables)
36 | except Exception:
37 | debug.warning('sys.path manipulation detected, but failed to evaluate.')
38 | return None
39 | try:
40 | res = variables['result']
41 | if isinstance(res, str):
42 | return os.path.abspath(res)
43 | else:
44 | return None
45 | except KeyError:
46 | return None
47 |
48 |
49 | def _paths_from_assignment(statement):
50 | """
51 | extracts the assigned strings from an assignment that looks as follows::
52 |
53 | >>> sys.path[0:0] = ['module/path', 'another/module/path']
54 | """
55 |
56 | names = statement.get_defined_names()
57 | if len(names) != 1:
58 | return []
59 | if [unicode(x) for x in names[0].names] != ['sys', 'path']:
60 | return []
61 | expressions = statement.expression_list()
62 | if len(expressions) != 1 or not isinstance(expressions[0], pr.Array):
63 | return
64 | stmts = (s for s in expressions[0].values if isinstance(s, pr.Statement))
65 | expression_lists = (s.expression_list() for s in stmts)
66 | return [e.value for exprs in expression_lists for e in exprs
67 | if isinstance(e, pr.Literal) and e.value]
68 |
69 |
70 | def _paths_from_insert(module_path, exe):
71 | """ extract the inserted module path from an "sys.path.insert" statement
72 | """
73 | exe_type, exe.type = exe.type, pr.Array.NOARRAY
74 | exe_pop = exe.values.pop(0)
75 | res = _execute_code(module_path, exe.get_code())
76 | exe.type = exe_type
77 | exe.values.insert(0, exe_pop)
78 | return res
79 |
80 |
81 | def _paths_from_call_expression(module_path, call):
82 | """ extract the path from either "sys.path.append" or "sys.path.insert" """
83 | if call.execution is None:
84 | return
85 | n = call.name
86 | if not isinstance(n, pr.Name) or len(n.names) != 3:
87 | return
88 | names = [unicode(x) for x in n.names]
89 | if names[:2] != ['sys', 'path']:
90 | return
91 | cmd = names[2]
92 | exe = call.execution
93 | if cmd == 'insert' and len(exe) == 2:
94 | path = _paths_from_insert(module_path, exe)
95 | elif cmd == 'append' and len(exe) == 1:
96 | path = _execute_code(module_path, exe.get_code())
97 | return path and [path] or []
98 |
99 |
100 | def _check_module(module):
101 | try:
102 | possible_stmts = module.used_names['path']
103 | except KeyError:
104 | return get_sys_path()
105 | sys_path = list(get_sys_path()) # copy
106 | statements = (p for p in possible_stmts if isinstance(p, pr.Statement))
107 | for stmt in statements:
108 | expressions = stmt.expression_list()
109 | if len(expressions) == 1 and isinstance(expressions[0], pr.Call):
110 | sys_path.extend(
111 | _paths_from_call_expression(module.path, expressions[0]) or [])
112 | elif (
113 | hasattr(stmt, 'assignment_details') and
114 | len(stmt.assignment_details) == 1
115 | ):
116 | sys_path.extend(_paths_from_assignment(stmt) or [])
117 | return sys_path
118 |
119 |
120 | @memoize_default(evaluator_is_first_arg=True)
121 | def sys_path_with_modifications(evaluator, module):
122 | if module.path is None:
123 | # Support for modules without a path is bad, therefore return the
124 | # normal path.
125 | return list(get_sys_path())
126 |
127 | curdir = os.path.abspath(os.curdir)
128 | with common.ignored(OSError):
129 | os.chdir(os.path.dirname(module.path))
130 |
131 | result = _check_module(module)
132 | result += _detect_django_path(module.path)
133 | # buildout scripts often contain the same sys.path modifications
134 | # the set here is used to avoid duplicate sys.path entries
135 | buildout_paths = set()
136 | for module_path in _get_buildout_scripts(module.path):
137 | try:
138 | with open(module_path, 'rb') as f:
139 | source = f.read()
140 | except IOError:
141 | pass
142 | else:
143 | p = Parser(common.source_to_unicode(source), module_path)
144 | for path in _check_module(p.module):
145 | if path not in buildout_paths:
146 | buildout_paths.add(path)
147 | result.append(path)
148 | # cleanup, back to old directory
149 | os.chdir(curdir)
150 | return list(result)
151 |
152 |
153 | def _traverse_parents(path):
154 | while True:
155 | new = os.path.dirname(path)
156 | if new == path:
157 | return
158 | path = new
159 | yield path
160 |
161 |
162 | def _get_parent_dir_with_file(path, filename):
163 | for parent in _traverse_parents(path):
164 | if os.path.isfile(os.path.join(parent, filename)):
165 | return parent
166 | return None
167 |
168 |
169 | def _detect_django_path(module_path):
170 | """ Detects the path of the very well known Django library (if used) """
171 | result = []
172 |
173 | for parent in _traverse_parents(module_path):
174 | with common.ignored(IOError):
175 | with open(parent + os.path.sep + 'manage.py'):
176 | debug.dbg('Found django path: %s', module_path)
177 | result.append(parent)
178 | return result
179 |
180 |
181 | def _get_buildout_scripts(module_path):
182 | """
183 | if there is a 'buildout.cfg' file in one of the parent directories of the
184 | given module it will return a list of all files in the buildout bin
185 | directory that look like python files.
186 |
187 | :param module_path: absolute path to the module.
188 | :type module_path: str
189 | """
190 | project_root = _get_parent_dir_with_file(module_path, 'buildout.cfg')
191 | if not project_root:
192 | return []
193 | bin_path = os.path.join(project_root, 'bin')
194 | if not os.path.exists(bin_path):
195 | return []
196 | extra_module_paths = []
197 | for filename in os.listdir(bin_path):
198 | try:
199 | filepath = os.path.join(bin_path, filename)
200 | with open(filepath, 'r') as f:
201 | firstline = f.readline()
202 | if firstline.startswith('#!') and 'python' in firstline:
203 | extra_module_paths.append(filepath)
204 | except IOError as e:
205 | # either permission error or race cond. because file got deleted
206 | # ignore
207 | debug.warning(unicode(e))
208 | continue
209 | return extra_module_paths
210 |
--------------------------------------------------------------------------------
/multi_script_editor/jedi/refactoring.py:
--------------------------------------------------------------------------------
1 | """
2 | Introduce some basic refactoring functions to |jedi|. This module is still in a
3 | very early development stage and needs much testing and improvement.
4 |
5 | .. warning:: I won't do too much here, but if anyone wants to step in, please
6 | do. Refactoring is none of my priorities
7 |
8 | It uses the |jedi| `API `_ and supports currently the
9 | following functions (sometimes bug-prone):
10 |
11 | - rename
12 | - extract variable
13 | - inline variable
14 | """
15 | import difflib
16 |
17 | from jedi import common
18 | from jedi.evaluate import helpers
19 | from jedi.parser import representation as pr
20 |
21 |
22 | class Refactoring(object):
23 | def __init__(self, change_dct):
24 | """
25 | :param change_dct: dict(old_path=(new_path, old_lines, new_lines))
26 | """
27 | self.change_dct = change_dct
28 |
29 | def old_files(self):
30 | dct = {}
31 | for old_path, (new_path, old_l, new_l) in self.change_dct.items():
32 | dct[new_path] = '\n'.join(new_l)
33 | return dct
34 |
35 | def new_files(self):
36 | dct = {}
37 | for old_path, (new_path, old_l, new_l) in self.change_dct.items():
38 | dct[new_path] = '\n'.join(new_l)
39 | return dct
40 |
41 | def diff(self):
42 | texts = []
43 | for old_path, (new_path, old_l, new_l) in self.change_dct.items():
44 | if old_path:
45 | udiff = difflib.unified_diff(old_l, new_l)
46 | else:
47 | udiff = difflib.unified_diff(old_l, new_l, old_path, new_path)
48 | texts.append('\n'.join(udiff))
49 | return '\n'.join(texts)
50 |
51 |
52 | def rename(script, new_name):
53 | """ The `args` / `kwargs` params are the same as in `api.Script`.
54 | :param operation: The refactoring operation to execute.
55 | :type operation: str
56 | :type source: str
57 | :return: list of changed lines/changed files
58 | """
59 | return Refactoring(_rename(script.usages(), new_name))
60 |
61 |
62 | def _rename(names, replace_str):
63 | """ For both rename and inline. """
64 | order = sorted(names, key=lambda x: (x.module_path, x.line, x.column),
65 | reverse=True)
66 |
67 | def process(path, old_lines, new_lines):
68 | if new_lines is not None: # goto next file, save last
69 | dct[path] = path, old_lines, new_lines
70 |
71 | dct = {}
72 | current_path = object()
73 | new_lines = old_lines = None
74 | for name in order:
75 | if name.in_builtin_module():
76 | continue
77 | if current_path != name.module_path:
78 | current_path = name.module_path
79 |
80 | process(current_path, old_lines, new_lines)
81 | if current_path is not None:
82 | # None means take the source that is a normal param.
83 | with open(current_path) as f:
84 | source = f.read()
85 |
86 | new_lines = common.splitlines(common.source_to_unicode(source))
87 | old_lines = new_lines[:]
88 |
89 | nr, indent = name.line, name.column
90 | line = new_lines[nr - 1]
91 | new_lines[nr - 1] = line[:indent] + replace_str + \
92 | line[indent + len(name.name):]
93 | process(current_path, old_lines, new_lines)
94 | return dct
95 |
96 |
97 | def extract(script, new_name):
98 | """ The `args` / `kwargs` params are the same as in `api.Script`.
99 | :param operation: The refactoring operation to execute.
100 | :type operation: str
101 | :type source: str
102 | :return: list of changed lines/changed files
103 | """
104 | new_lines = common.splitlines(common.source_to_unicode(script.source))
105 | old_lines = new_lines[:]
106 |
107 | user_stmt = script._parser.user_stmt()
108 |
109 | # TODO care for multiline extracts
110 | dct = {}
111 | if user_stmt:
112 | pos = script._pos
113 | line_index = pos[0] - 1
114 | arr, index = helpers.array_for_pos(user_stmt, pos)
115 | if arr is not None:
116 | start_pos = arr[index].start_pos
117 | end_pos = arr[index].end_pos
118 |
119 | # take full line if the start line is different from end line
120 | e = end_pos[1] if end_pos[0] == start_pos[0] else None
121 | start_line = new_lines[start_pos[0] - 1]
122 | text = start_line[start_pos[1]:e]
123 | for l in range(start_pos[0], end_pos[0] - 1):
124 | text += '\n' + l
125 | if e is None:
126 | end_line = new_lines[end_pos[0] - 1]
127 | text += '\n' + end_line[:end_pos[1]]
128 |
129 | # remove code from new lines
130 | t = text.lstrip()
131 | del_start = start_pos[1] + len(text) - len(t)
132 |
133 | text = t.rstrip()
134 | del_end = len(t) - len(text)
135 | if e is None:
136 | new_lines[end_pos[0] - 1] = end_line[end_pos[1] - del_end:]
137 | e = len(start_line)
138 | else:
139 | e = e - del_end
140 | start_line = start_line[:del_start] + new_name + start_line[e:]
141 | new_lines[start_pos[0] - 1] = start_line
142 | new_lines[start_pos[0]:end_pos[0] - 1] = []
143 |
144 | # add parentheses in multiline case
145 | open_brackets = ['(', '[', '{']
146 | close_brackets = [')', ']', '}']
147 | if '\n' in text and not (text[0] in open_brackets and text[-1] ==
148 | close_brackets[open_brackets.index(text[0])]):
149 | text = '(%s)' % text
150 |
151 | # add new line before statement
152 | indent = user_stmt.start_pos[1]
153 | new = "%s%s = %s" % (' ' * indent, new_name, text)
154 | new_lines.insert(line_index, new)
155 | dct[script.path] = script.path, old_lines, new_lines
156 | return Refactoring(dct)
157 |
158 |
159 | def inline(script):
160 | """
161 | :type script: api.Script
162 | """
163 | new_lines = common.splitlines(common.source_to_unicode(script.source))
164 |
165 | dct = {}
166 |
167 | definitions = script.goto_assignments()
168 | with common.ignored(AssertionError):
169 | assert len(definitions) == 1
170 | stmt = definitions[0]._definition
171 | usages = script.usages()
172 | inlines = [r for r in usages
173 | if not stmt.start_pos <= (r.line, r.column) <= stmt.end_pos]
174 | inlines = sorted(inlines, key=lambda x: (x.module_path, x.line, x.column),
175 | reverse=True)
176 | expression_list = stmt.expression_list()
177 | # don't allow multiline refactorings for now.
178 | assert stmt.start_pos[0] == stmt.end_pos[0]
179 | index = stmt.start_pos[0] - 1
180 |
181 | line = new_lines[index]
182 | replace_str = line[expression_list[0].start_pos[1]:stmt.end_pos[1] + 1]
183 | replace_str = replace_str.strip()
184 | # tuples need parentheses
185 | if expression_list and isinstance(expression_list[0], pr.Array):
186 | arr = expression_list[0]
187 | if replace_str[0] not in ['(', '[', '{'] and len(arr) > 1:
188 | replace_str = '(%s)' % replace_str
189 |
190 | # if it's the only assignment, remove the statement
191 | if len(stmt.get_defined_names()) == 1:
192 | line = line[:stmt.start_pos[1]] + line[stmt.end_pos[1]:]
193 |
194 | dct = _rename(inlines, replace_str)
195 | # remove the empty line
196 | new_lines = dct[script.path][2]
197 | if line.strip():
198 | new_lines[index] = line
199 | else:
200 | new_lines.pop(index)
201 |
202 | return Refactoring(dct)
203 |
--------------------------------------------------------------------------------
/multi_script_editor/jedi/settings.py:
--------------------------------------------------------------------------------
1 | """
2 | This module contains variables with global |jedi| settings. To change the
3 | behavior of |jedi|, change the variables defined in :mod:`jedi.settings`.
4 |
5 | Plugins should expose an interface so that the user can adjust the
6 | configuration.
7 |
8 |
9 | Example usage::
10 |
11 | from jedi import settings
12 | settings.case_insensitive_completion = True
13 |
14 |
15 | Completion output
16 | ~~~~~~~~~~~~~~~~~
17 |
18 | .. autodata:: case_insensitive_completion
19 | .. autodata:: add_dot_after_module
20 | .. autodata:: add_bracket_after_function
21 | .. autodata:: no_completion_duplicates
22 |
23 |
24 | Filesystem cache
25 | ~~~~~~~~~~~~~~~~
26 |
27 | .. autodata:: cache_directory
28 | .. autodata:: use_filesystem_cache
29 |
30 |
31 | Parser
32 | ~~~~~~
33 |
34 | .. autodata:: fast_parser
35 |
36 |
37 | Dynamic stuff
38 | ~~~~~~~~~~~~~
39 |
40 | .. autodata:: dynamic_arrays_instances
41 | .. autodata:: dynamic_array_additions
42 | .. autodata:: dynamic_params
43 | .. autodata:: dynamic_params_for_other_modules
44 | .. autodata:: additional_dynamic_modules
45 | .. autodata:: auto_import_modules
46 |
47 |
48 | .. _settings-recursion:
49 |
50 | Recursions
51 | ~~~~~~~~~~
52 |
53 | Recursion settings are important if you don't want extremly
54 | recursive python code to go absolutely crazy. First of there is a
55 | global limit :data:`max_executions`. This limit is important, to set
56 | a maximum amount of time, the completion may use.
57 |
58 | The default values are based on experiments while completing the |jedi| library
59 | itself (inception!). But I don't think there's any other Python library that
60 | uses recursion in a similarly extreme way. These settings make the completion
61 | definitely worse in some cases. But a completion should also be fast.
62 |
63 | .. autodata:: max_until_execution_unique
64 | .. autodata:: max_function_recursion_level
65 | .. autodata:: max_executions_without_builtins
66 | .. autodata:: max_executions
67 | .. autodata:: scale_call_signatures
68 |
69 |
70 | Caching
71 | ~~~~~~~
72 |
73 | .. autodata:: star_import_cache_validity
74 | .. autodata:: call_signatures_validity
75 |
76 |
77 | """
78 | import os
79 | import platform
80 |
81 | # ----------------
82 | # completion output settings
83 | # ----------------
84 |
85 | case_insensitive_completion = True
86 | """
87 | The completion is by default case insensitive.
88 | """
89 |
90 | add_dot_after_module = False
91 | """
92 | Adds a dot after a module, because a module that is not accessed this way is
93 | definitely not the normal case. However, in VIM this doesn't work, that's why
94 | it isn't used at the moment.
95 | """
96 |
97 | add_bracket_after_function = False
98 | """
99 | Adds an opening bracket after a function, because that's normal behaviour.
100 | Removed it again, because in VIM that is not very practical.
101 | """
102 |
103 | no_completion_duplicates = True
104 | """
105 | If set, completions with the same name don't appear in the output anymore,
106 | but are in the `same_name_completions` attribute.
107 | """
108 |
109 | # ----------------
110 | # Filesystem cache
111 | # ----------------
112 |
113 | use_filesystem_cache = True
114 | """
115 | Use filesystem cache to save once parsed files with pickle.
116 | """
117 |
118 | if platform.system().lower() == 'windows':
119 | _cache_directory = os.path.join(os.getenv('APPDATA') or '~', 'Jedi',
120 | 'Jedi')
121 | elif platform.system().lower() == 'darwin':
122 | _cache_directory = os.path.join('~', 'Library', 'Caches', 'Jedi')
123 | else:
124 | _cache_directory = os.path.join(os.getenv('XDG_CACHE_HOME') or '~/.cache',
125 | 'jedi')
126 | cache_directory = os.path.expanduser(_cache_directory)
127 | """
128 | The path where all the caches can be found.
129 |
130 | On Linux, this defaults to ``~/.cache/jedi/``, on OS X to
131 | ``~/Library/Caches/Jedi/`` and on Windows to ``%APPDATA%\\Jedi\\Jedi\\``.
132 | On Linux, if environment variable ``$XDG_CACHE_HOME`` is set,
133 | ``$XDG_CACHE_HOME/jedi`` is used instead of the default one.
134 | """
135 |
136 | # ----------------
137 | # parser
138 | # ----------------
139 |
140 | fast_parser = True
141 | """
142 | Use the fast parser. This means that reparsing is only being done if
143 | something has been changed e.g. to a function. If this happens, only the
144 | function is being reparsed.
145 | """
146 |
147 | # ----------------
148 | # dynamic stuff
149 | # ----------------
150 |
151 | dynamic_arrays_instances = True
152 | """
153 | Check for `append`, etc. on array instances like list()
154 | """
155 |
156 | dynamic_array_additions = True
157 | """
158 | check for `append`, etc. on arrays: [], {}, ()
159 | """
160 |
161 | dynamic_params = True
162 | """
163 | A dynamic param completion, finds the callees of the function, which define
164 | the params of a function.
165 | """
166 |
167 | dynamic_params_for_other_modules = True
168 | """
169 | Do the same for other modules.
170 | """
171 |
172 | additional_dynamic_modules = []
173 | """
174 | Additional modules in which |jedi| checks if statements are to be found. This
175 | is practical for IDEs, that want to administrate their modules themselves.
176 | """
177 |
178 | dynamic_flow_information = True
179 | """
180 | Check for `isinstance` and other information to infer a type.
181 | """
182 |
183 | auto_import_modules = [
184 | 'hashlib', # setattr
185 | ]
186 | """
187 | Modules that are not analyzed but imported, although they contain Python code.
188 | This improves autocompletion for libraries that use ``setattr`` or
189 | ``globals()`` modifications a lot.
190 | """
191 |
192 | # ----------------
193 | # recursions
194 | # ----------------
195 |
196 | max_until_execution_unique = 50
197 | """
198 | This limit is probably the most important one, because if this limit is
199 | exceeded, functions can only be one time executed. So new functions will be
200 | executed, complex recursions with the same functions again and again, are
201 | ignored.
202 | """
203 |
204 | max_function_recursion_level = 5
205 | """
206 | `max_function_recursion_level` is more about whether the recursions are
207 | stopped in deepth or in width. The ratio beetween this and
208 | `max_until_execution_unique` is important here. It stops a recursion (after
209 | the number of function calls in the recursion), if it was already used
210 | earlier.
211 | """
212 |
213 | max_executions_without_builtins = 200
214 | """
215 | .. todo:: Document this.
216 | """
217 |
218 | max_executions = 250
219 | """
220 | A maximum amount of time, the completion may use.
221 | """
222 |
223 | scale_call_signatures = 0.1
224 | """
225 | Because call_signatures is normally used on every single key hit, it has
226 | to be faster than a normal completion. This is the factor that is used to
227 | scale `max_executions` and `max_until_execution_unique`:
228 | """
229 |
230 | # ----------------
231 | # caching validity (time)
232 | # ----------------
233 |
234 | star_import_cache_validity = 60.0
235 | """
236 | In huge packages like numpy, checking all star imports on every completion
237 | might be slow, therefore we do a star import caching, that lasts a certain
238 | time span (in seconds).
239 | """
240 |
241 | call_signatures_validity = 3.0
242 | """
243 | Finding function calls might be slow (0.1-0.5s). This is not acceptible for
244 | normal writing. Therefore cache it for a short time.
245 | """
246 |
--------------------------------------------------------------------------------
/multi_script_editor/jedi/utils.py:
--------------------------------------------------------------------------------
1 | """
2 | Utilities for end-users.
3 | """
4 |
5 | from __future__ import absolute_import
6 | import __main__
7 | from collections import namedtuple
8 | import re
9 | import os
10 | import sys
11 |
12 | from jedi import Interpreter
13 | from jedi.api.helpers import completion_parts
14 | from jedi.parser.user_context import UserContext
15 |
16 |
17 | def setup_readline(namespace_module=__main__):
18 | """
19 | Install Jedi completer to :mod:`readline`.
20 |
21 | This function setups :mod:`readline` to use Jedi in Python interactive
22 | shell. If you want to use a custom ``PYTHONSTARTUP`` file (typically
23 | ``$HOME/.pythonrc.py``), you can add this piece of code::
24 |
25 | try:
26 | from jedi.utils import setup_readline
27 | setup_readline()
28 | except ImportError:
29 | # Fallback to the stdlib readline completer if it is installed.
30 | # Taken from http://docs.python.org/2/library/rlcompleter.html
31 | print("Jedi is not installed, falling back to readline")
32 | try:
33 | import readline
34 | import rlcompleter
35 | readline.parse_and_bind("tab: complete")
36 | except ImportError:
37 | print("Readline is not installed either. No tab completion is enabled.")
38 |
39 | This will fallback to the readline completer if Jedi is not installed.
40 | The readline completer will only complete names in the global namespace,
41 | so for example::
42 |
43 | ran
44 |
45 | will complete to ``range``
46 |
47 | with both Jedi and readline, but::
48 |
49 | range(10).cou
50 |
51 | will show complete to ``range(10).count`` only with Jedi.
52 |
53 | You'll also need to add ``export PYTHONSTARTUP=$HOME/.pythonrc.py`` to
54 | your shell profile (usually ``.bash_profile`` or ``.profile`` if you use
55 | bash).
56 |
57 | """
58 | class JediRL(object):
59 | def complete(self, text, state):
60 | """
61 | This complete stuff is pretty weird, a generator would make
62 | a lot more sense, but probably due to backwards compatibility
63 | this is still the way how it works.
64 |
65 | The only important part is stuff in the ``state == 0`` flow,
66 | everything else has been copied from the ``rlcompleter`` std.
67 | library module.
68 | """
69 | if state == 0:
70 | sys.path.insert(0, os.getcwd())
71 | # Calling python doesn't have a path, so add to sys.path.
72 | try:
73 | interpreter = Interpreter(text, [namespace_module.__dict__])
74 |
75 | path = UserContext(text, (1, len(text))).get_path_until_cursor()
76 | path, dot, like = completion_parts(path)
77 | before = text[:len(text) - len(like)]
78 | completions = interpreter.completions()
79 | finally:
80 | sys.path.pop(0)
81 |
82 | self.matches = [before + c.name_with_symbols for c in completions]
83 | try:
84 | return self.matches[state]
85 | except IndexError:
86 | return None
87 |
88 | try:
89 | import readline
90 | except ImportError:
91 | print("Module readline not available.")
92 | else:
93 | readline.set_completer(JediRL().complete)
94 | readline.parse_and_bind("tab: complete")
95 | # jedi itself does the case matching
96 | readline.parse_and_bind("set completion-ignore-case on")
97 | # because it's easier to hit the tab just once
98 | readline.parse_and_bind("set show-all-if-unmodified")
99 | readline.parse_and_bind("set show-all-if-ambiguous on")
100 | # don't repeat all the things written in the readline all the time
101 | readline.parse_and_bind("set completion-prefix-display-length 2")
102 | # No delimiters, Jedi handles that.
103 | readline.set_completer_delims('')
104 |
105 |
106 | def version_info():
107 | """
108 | Returns a namedtuple of Jedi's version, similar to Python's
109 | ``sys.version_info``.
110 | """
111 | Version = namedtuple('Version', 'major, minor, micro, releaselevel, serial')
112 | from jedi import __version__
113 | tupl = re.findall('[a-z]+|\d+', __version__)
114 | return Version(*[x if i == 3 else int(x) for i, x in enumerate(tupl)])
115 |
--------------------------------------------------------------------------------
/multi_script_editor/managers/_3dsmax.py:
--------------------------------------------------------------------------------
1 | import os, sys, re
2 | try:
3 | from PySide.QtGui import *
4 | from PySide.QtCore import *
5 | except:
6 | from PySide2.QtGui import *
7 | from PySide2.QtCore import *
8 | from PySide2.QtWidgets import *
9 |
10 | from multi_script_editor import scriptEditor
11 | reload(scriptEditor)
12 | import MaxPlus
13 | q3dsmax = QApplication.instance()
14 |
15 | class MaxDialogEvents(QObject):
16 | def eventFilter(self, obj, event):
17 | import MaxPlus
18 | if event.type() == QEvent.WindowActivate:
19 | MaxPlus.CUI.DisableAccelerators()
20 | elif event.type() == QEvent.WindowDeactivate:
21 | MaxPlus.CUI.EnableAccelerators()
22 | return False
23 |
24 | def show():
25 | try:
26 | qtwindow = MaxPlus.GetQMaxWindow()
27 | except:
28 | qtwindow = MaxPlus.GetQMaxMainWindow()
29 | se = scriptEditor.scriptEditorClass(parent=qtwindow)
30 | #se.installEventFilter(MaxDialogEvents())
31 | se.runCommand('import MaxPlus')
32 | #se.MaxEventFilter = MaxDialogEvents()
33 | #se.installEventFilter(se.MaxEventFilter)
34 | se.show()
35 |
--------------------------------------------------------------------------------
/multi_script_editor/managers/__init__.py:
--------------------------------------------------------------------------------
1 | main = __import__('__main__')
2 | import platform
3 |
4 |
5 | ####### COMPLETERS ##############################################
6 |
7 | # NUKE
8 | def nukeCompleter(*args):
9 | from managers import _nuke
10 | return _nuke.completer(*args)
11 |
12 | def getNukeContextMenu(*args):
13 | from managers import _nuke
14 | reload(_nuke)
15 | return _nuke.contextMenu(*args)
16 | ###################################################################
17 |
18 | # HOUDINI
19 | def houdiniCompleter(*args):
20 | from managers import _houdini
21 | return _houdini.completer(*args)
22 | def getHoudiniContextMenu(*args):
23 | from managers import _houdini
24 | reload(_houdini)
25 | return _houdini.contextMenu(*args)
26 | def houdiniDropEvent(*args):
27 | from managers import _houdini
28 | reload(_houdini)
29 | return _houdini.wrapDroppedText(*args)
30 | ###################################################################
31 |
32 | # MAYA
33 | def mayaCompleter(*args):
34 | from managers import _maya
35 | reload(_maya)
36 | return _maya.completer(*args)
37 |
38 | def mayaDropEvent(*args):
39 | from managers import _maya
40 | return _maya.wrapDroppedText(*args)
41 | def getMayaContextMenu(*args):
42 | from managers import _maya
43 | reload(_maya)
44 | return _maya.contextMenu(*args)
45 | ###################################################################
46 |
47 |
48 | contextCompleters = dict(
49 | nuke=nukeCompleter,
50 | hou=houdiniCompleter,
51 | maya=mayaCompleter
52 | )
53 |
54 | contextMenus = dict(
55 | hou=getHoudiniContextMenu,
56 | nuke=getNukeContextMenu,
57 | maya=getMayaContextMenu
58 | )
59 |
60 | dropEvents = dict(
61 | maya=mayaDropEvent,
62 | hou=houdiniDropEvent
63 | )
64 |
65 | autoImport = dict(
66 | hou='import hou\n',
67 | nuke='import nuke\n',
68 | max='import MaxPlus\n'
69 | )
70 | mayaDragTempData = 'maya_temp_drag_empty_Data'
71 |
72 | main_parent = None
73 | context = None
74 | if 'hou' in main.__dict__:
75 | context = 'hou'
76 | if main.__dict__['hou'].applicationVersion()[0] >= 15:
77 | main_parent = main.__dict__['hou'].ui.mainQtWindow()
78 | elif 'cmds' in main.__dict__:
79 | context = 'maya'
80 | elif 'nuke' in main.__dict__:
81 | context = 'nuke'
82 | elif 'MaxPlus' in main.__dict__:
83 | context = 'max'
84 |
85 |
86 |
87 |
88 | if platform.system().lower() == 'windows':
89 | _s = 'w'
90 | elif platform.system().lower() == 'darwin':
91 | _s = 'x'
92 | else:
93 | _s = 'l'
--------------------------------------------------------------------------------
/multi_script_editor/managers/_nuke.py:
--------------------------------------------------------------------------------
1 | import os, sys, re
2 | # import nuke
3 | main = __import__('__main__')
4 | ns = main.__dict__
5 | exec 'import nuke' in ns
6 | exec 'import nukescripts' in ns
7 | nuke = ns['nuke']
8 | import nukescripts
9 | from managers.nuke import nodes
10 | nuke_nodes = dir(nodes)
11 | from managers.completeWidget import contextCompleterClass
12 |
13 | try:
14 | from PySide.QtGui import *
15 | from PySide.QtCore import *
16 | except ImportError:
17 | from PySide2.QtGui import *
18 | from PySide2.QtCore import *
19 | from PySide2.QtWidgets import *
20 |
21 | p = os.path.dirname(__file__).replace('\\','/')
22 | if not p in sys.path:
23 | sys.path.insert(0, p)
24 |
25 | from multi_script_editor import scriptEditor
26 | reload(scriptEditor)
27 |
28 | # QT
29 | qApp = QApplication.instance()
30 |
31 | def getMainWindow():
32 | for widget in qApp.topLevelWidgets():
33 | if widget.metaObject().className() == 'Foundry::UI::DockMainWindow':
34 | return widget
35 | qNuke = getMainWindow()
36 |
37 | def show(panel=False):
38 | if panel:
39 | import multi_script_editor.scriptEditor
40 | nukescripts.panels.registerWidgetAsPanel("multi_script_editor.scriptEditor.scriptEditorClass", "Multi Script Editor", "pw_multi_script_editor")
41 | else:
42 | showWindow()
43 |
44 |
45 | def showWindow():
46 | se = scriptEditor.scriptEditorClass(qNuke)
47 | se.runCommand('import nuke')
48 | se.show()
49 |
50 |
51 | # add to menu.py
52 | # Add to menu.py
53 | # menubar = nuke.menu("Nuke")
54 | # toolMenu = menubar.addMenu('&Tools')
55 | # path = 'path/to/MultiScriptEditor_module'
56 | # # example c:/nuke/python/lib
57 | # if not path in sys.path:
58 | # sys.path.append(path)
59 | #
60 | # import multi_script_editor
61 | # # add to menu
62 | # toolMenu.addCommand("Multi Script Editor", "multi_script_editor.showNuke()")
63 | # # create new pane
64 | # multi_script_editor.showNuke(panel=True)
65 |
66 |
67 | ############ COMPLETER
68 |
69 | def completer(line, ns):
70 | # node types
71 | p1 = r"nuke\.createNode\(\w*['\"](\w*)$"
72 | m = re.search(p1, line)
73 | if m:
74 | name = m.group(1)
75 | l = len(name)
76 | if name:
77 | auto = [x for x in nuke_nodes if x.lower().startswith(name.lower())]
78 | else:
79 | auto = nuke_nodes
80 | return [contextCompleterClass(x, x[l:], True) for x in auto], None
81 |
82 | # p2 = r"nuke\.allNodes\(.*(filter=)*['\"](\w*)$"
83 | funcs = ['allNodes', 'selectedNodes']
84 | for f in funcs:
85 | p2 = r"nuke\."+f+"\(\w*(filter=)*['\"]{1}(\w*)$"
86 | m = re.search(p2, line)# or re.search(p2, line)
87 | if m:
88 | name = m.group(2)
89 | l = len(name)
90 | if name:
91 | auto = [x for x in nuke_nodes if x.lower().startswith(name.lower())]
92 | else:
93 | auto = nuke_nodes
94 | return [contextCompleterClass(x, x[l:], True) for x in auto], None
95 |
96 | # exists nodes
97 | p3 = r"nuke\.toNode\(\w*['\"](\w*)$"
98 | m = re.search(p3, line)
99 | if m:
100 | name = m.group(1)
101 | # nuke.tprint(name)
102 | nodes = [x.name() for x in nuke.allNodes()] + ['preferences', 'root'] #recurseGroups=True
103 | if name:
104 | result = [x for x in nodes if x.lower().startswith(name.lower())]
105 | else:
106 | result = nodes
107 | l = len(name)
108 | return [contextCompleterClass(x, x[l:], True) for x in result], None
109 | # node knobs
110 | p4 = r"(\w+)\[['\"]{1}(\w*)$"
111 | m = re.search(p4, line)
112 | if m:
113 | node = m.group(1)
114 | name = m.group(2)
115 |
116 | if node in ns:
117 | if hasattr(ns[node], 'allKnobs'):
118 | names = [x.name() for x in ns[node].allKnobs()]
119 | if name:
120 | result = [x for x in names if x.lower().startswith(name.lower())]
121 | else:
122 | result = names
123 | l = len(name)
124 | return [contextCompleterClass(x, x[l:], True) for x in result if x], None
125 | # nuke.tprint(ns[node])
126 | return None, None
127 |
128 | ################ CONTEXT MENU
129 |
130 | def contextMenu(parent):
131 | m = nukeContextMenu(parent)
132 | return m
133 |
134 | class nukeContextMenu(QMenu):
135 | def __init__(self, parent):
136 | super(nukeContextMenu, self).__init__('Nuke')
137 | self.par = parent
138 | self.setTearOffEnabled(1)
139 | self.setWindowTitle('MSE %s Nuke' % self.par.ver)
140 | self.addAction(QAction('Read PyScript Knob', parent, triggered=self.readPyScriptKnob))
141 | self.addAction(QAction('Save To PyScript Knob', parent, triggered=self.saveToKnob))
142 | self.addSeparator()
143 | self.addAction(QAction('From Selected', parent, triggered=self.nodeToCode))
144 | self.addAction(QAction('From Clipboard', parent, triggered=self.nodesFromClipboard))
145 |
146 | def nodeToCode(self):
147 | nodes = nuke.selectedNodes()
148 | names = [x.name() for x in nodes]
149 | result = '\n'.join(["nuke.toNode('%s')" % x for x in names])
150 | self.par.insertText(result)
151 |
152 | def getPyKnob(self, title):
153 | s = nuke.selectedNodes()
154 | if s:
155 | s = s[0]
156 | pyKnobs = [x for x in s.knobs().values() if x.Class() in ['PyScript_Knob','PythonCustomKnob']]
157 | if pyKnobs:
158 | result = {}
159 | for k in pyKnobs:
160 | result[k.name()] = k
161 | if result:
162 | curr = self.par.tab.currentTabName()
163 | selected = curr.split('|')[-1].strip()
164 | dial = selectDialog(result.keys(), title, selected)
165 | if dial.exec_():
166 | name = dial.list.currentItem().text()
167 | knob = result[name]
168 | return knob
169 | else:
170 | nuke.message('Python Knobs not found')
171 | else:
172 | nuke.message('Select one node')
173 |
174 | def readPyScriptKnob(self):
175 | knob = self.getPyKnob('Select Python Knob to Read')
176 | if knob:
177 | text = knob.value()
178 | self.par.tab.addNewTab(knob.node().name()+' | '+knob.name(), text)
179 |
180 | def saveToKnob(self):
181 | knob = self.getPyKnob('Select Python Knob to Save')
182 | if knob:
183 | text = self.par.tab.getCurrentText()
184 | knob.setValue(text)
185 |
186 | def nodesFromClipboard(self):
187 | # nuke.tprint(str(self.par))
188 | text = QApplication.clipboard().text()
189 | nodes = []
190 | if text:
191 | for l in text.split('\n'):
192 | res = re.findall(r'name \w+$', l)
193 | if res:
194 | name = res[0].split()[1]
195 | nodes.append(name)
196 | for n in nodes:
197 | self.par.tab.addToCurrent('nuke.toNode("%s")\n' % n)
198 |
199 | class selectDialog(QDialog):
200 | def __init__(self, items, title, sel=None):
201 | super(selectDialog, self).__init__()
202 | self.setWindowTitle(title)
203 | self.setWindowFlags(Qt.Tool)
204 | self.list = QListWidget(self)
205 | self.list.setSelectionMode(QAbstractItemView.SingleSelection)
206 | self.ly = QVBoxLayout(self)
207 | self.setLayout(self.ly)
208 | self.ly.addWidget(self.list)
209 | self.btn = QPushButton('Select')
210 | self.ly.addWidget(self.btn)
211 | self.btn.clicked.connect(self.accept)
212 | selected = None
213 | for i in items:
214 | item = QListWidgetItem(i)
215 | item.setData(32, i)
216 | self.list.addItem(item)
217 | if i == sel:
218 | selected = item
219 | if selected:
220 | self.list.setCurrentIndex(self.list.indexFromItem(selected))
221 |
222 |
223 |
224 | def closeEvent(self, *args, **kwargs):
225 | self.reject()
226 | super(selectDialog, self).closeEvent( *args, **kwargs)
227 |
228 |
--------------------------------------------------------------------------------
/multi_script_editor/managers/completeWidget.py:
--------------------------------------------------------------------------------
1 |
2 |
3 | # context completer
4 | class contextCompleterClass(object):
5 | def __init__(self, name, complete, end=None):
6 | self.name = name
7 | self.complete = complete
8 | self.end_char = end
--------------------------------------------------------------------------------
/multi_script_editor/managers/houdini/multi_script_editor_16.pypanel:
--------------------------------------------------------------------------------
1 |
2 |
3 |
4 |
17 |
18 |
19 |
20 |
--------------------------------------------------------------------------------
/multi_script_editor/managers/nuke/__init__.py:
--------------------------------------------------------------------------------
1 | from main import *
2 | from . import math
3 | import geo
4 | import nodes
5 |
6 | AFTER_CONST = 21
7 | AFTER_LINEAR = 22
8 | ALL = 1
9 | ALWAYS_SAVE = 1048576
10 | BEFORE_CONST = 19
11 | BEFORE_LINEAR = 20
12 | BREAK = 18
13 | CATMULL_ROM = 3
14 | CONSTANT = 1
15 | CUBIC = 4
16 | DISABLED = 128
17 | DO_NOT_WRITE = 512
18 | ENDLINE = 8192
19 | EXE_PATH = ''
20 | EXPRESSIONS = 1
21 | FLOAT = 5
22 | FONT = 4
23 | GEO = 16
24 | GUI = False
25 | HIDDEN_INPUTS = 4
26 | HORIZONTAL = 17
27 | IMAGE = 1
28 | INPUTS = 2
29 | INT16 = 3
30 | INT8 = 2
31 | INTERACTIVE = True
32 | INVISIBLE = 1024
33 | KNOB_CHANGED_RECURSIVE = 134217728
34 | LINEAR = 2
35 | LOG = 4
36 | MATCH_CLASS = 0
37 | MATCH_COLOR = 2
38 | MATCH_LABEL = 1
39 | MONITOR = 0
40 | NODIR = 2
41 | NO_ANIMATION = 256
42 | NO_CHECKMARKS = 1
43 | NO_MULTIVIEW = 1073741824
44 | NO_POSTAGESTAMPS = False
45 | NO_UNDO = 524288
46 | NUKE_VERSION_DATE = ''
47 | NUKE_VERSION_MAJOR = 6
48 | NUKE_VERSION_MINOR = 3
49 | NUKE_VERSION_PHASE = ''
50 | NUKE_VERSION_PHASENUMBER = 0
51 | NUKE_VERSION_RELEASE = 1
52 | NUKE_VERSION_STRING = ''
53 | NUM_CPUS = 4
54 | NUM_INTERPOLATIONS = 5
55 | PLUGIN_EXT = 'dll'
56 | PREPEND = 8
57 | PYTHON = 32
58 | REPLACE = 1
59 | SAVE_MENU = 33554432
60 | SCRIPT = 2
61 | SMOOTH = 0
62 | STARTLINE = 4096
63 | TABBEGINCLOSEDGROUP = 2
64 | TABBEGINGROUP = 1
65 | TABENDGROUP = -1
66 | TABKNOB = 0
67 | THREADS = 4
68 | TO_SCRIPT = 1
69 | TO_VALUE = 2
70 | USER_SET_SLOPE = 16
71 | VIEWER = 1
72 | WRITE_ALL = 8
73 | WRITE_NON_DEFAULT_ONLY = 16
74 | WRITE_USER_KNOB_DEFS = 4
75 | __package__ = 'nuke'
76 | afterBackgroundFrameRenders = []
77 | afterBackgroundRenders = []
78 | afterFrameRenders = {}
79 | afterRenders = {}
80 | autoSaveDeleteFilters = {}
81 | autoSaveFilters = {}
82 | autoSaveRestoreFilters = {}
83 | autolabels = {}
84 | beforeBackgroundRenders = []
85 | beforeFrameRenders = {}
86 | beforeRenders = {}
87 | env = {}
88 | filenameFilters = {}
89 | knobChangeds = {}
90 | # nodes =
91 | onCreates = {}
92 | onDestroys = {}
93 | onScriptCloses = {}
94 | onScriptLoads = {}
95 | onScriptSaves = {}
96 | onUserCreates = {}
97 | rawArgs = []
98 | untitled = 'Untitled'
99 | updateUIs = {}
100 | validateFilenames = {}
101 | __all__=[]
102 | threading = None
--------------------------------------------------------------------------------
/multi_script_editor/managers/nuke/geo.py:
--------------------------------------------------------------------------------
1 | class AttrGroup(object):
2 | Group_Attributes = 5
3 | Group_Last = 6
4 | Group_Matrix = 4
5 | Group_None = -1
6 | Group_Object = 3
7 | Group_Points = 2
8 | Group_Primitives = 0
9 | Group_Vertices = 1
10 |
11 | class AttrType(object):
12 | eFloatAttrib = 0
13 | eIntAttrib = 5
14 | eInvalidAttrib = -1
15 | eMatrix3Attrib = 8
16 | eMatrix4Attrib = 9
17 | eNormalAttrib = 4
18 | ePointerAttrib = 7
19 | eStringAttrib = 6
20 | eVector2Attrib = 1
21 | eVector3Attrib = 2
22 | eVector4Attrib = 3
23 |
24 | class AttribContext(object):
25 | attribute=None
26 | #The attribute itself.
27 | channel=None
28 | #The channel number for the underlying attribute.
29 | group=None
30 | #What this attribute applies to (points, vertices, faces, etc.).
31 | name=None
32 | #The name for the attribute.
33 | recursive=None
34 | #Boolean value to indicate whether or not the attribute is applied recursively.
35 | type=None
36 | #The type of the attribute values.
37 | varying=None
38 | #Boolean value to indicate whether or not the attribute varies.
39 | def empty(self):
40 | """Whether this attribute is empty, i.e.
41 | """
42 | pass
43 |
44 | class Attribute(object):
45 | name=None
46 | #The name for the attribute.
47 | type=None
48 | #The type of the attribute values.
49 | def invalid(self):
50 | return True
51 | def valid(self):
52 | return True
53 |
54 |
55 | class GeoInfo(object):
56 | def attribContext(self, name, group, type):
57 | """
58 | Gets an attribute context for the named attribute of this object.
59 | list of (x,y,z) tuples"""
60 | return AttribContext()
61 |
62 | def normals(self):
63 | """
64 | Gets the list of vertex normals for this object.
65 | PointList"""
66 | return ((0,0,0),)
67 |
68 | def points(self):
69 | """
70 | Gets the point list for this object.
71 | list of point index lists"""
72 | return PointList()
73 |
74 | def primitives(self):
75 | """
76 | Gets the list of primitives which make up this object.
77 | 4x4 tuple of floats"""
78 | return [1,]
79 |
80 | def transform(self):
81 | """
82 | Gets the transform matrix from the objects local coordinate system to world coordinates.
83 | """
84 | return (0,)
85 |
86 | class GeometryList(object):
87 | def __getitem__(self, x, y):
88 | pass
89 |
90 | class PointList(object):
91 | def __getitem__(self, x, y):
92 | pass
93 |
94 | class Primitive(object):
95 |
96 | def averageCenter(self, pointlist):
97 | """
98 | Get the average x,y,z location of all points in this primitive.
99 | (x, y, z) """
100 | return (0,0,0)
101 |
102 | def faceAverageCenter(self, faceIndex, pointlist):
103 | """
104 | Get the average x,y,z location of all points in a particular face.
105 | list of int"""
106 | return (0,0,0)
107 |
108 | def faceVertices(self, faceIndex):
109 | """
110 | Get the list of point indexes for a particular face.
111 | int """
112 | return [0,]
113 |
114 | def faces(self):
115 | """
116 | Get the number of sub-faces this primitive generates.
117 | (x, y, z) """
118 | return (0,0,0)
119 |
120 | def normal(self):
121 | """
122 | Get the normal for this primitive.
123 | list of int"""
124 | return (0,0,0)
125 |
126 | def points(self):
127 | """
128 | Get the list of point indexes for this primitive."""
129 | return [0,]
--------------------------------------------------------------------------------
/multi_script_editor/managers/nuke/math.py:
--------------------------------------------------------------------------------
1 | class Matrix3(object):
2 | def set(self, v1,v2,v3,v4,v5,v6,v7,v8,v9):
3 | pass
4 | def makeIdentity(self):
5 | pass
6 | def rotateX(self, value):
7 | pass
8 | def rotateY(self, value):
9 | pass
10 | def rotateZ(self, value):
11 | pass
12 | def scale(self, x, y, z):
13 | return 0
14 | def scaling(self, x, y, z):
15 | return 0
16 | def transform(self, vector):
17 | pass
18 | def skew(self, value):
19 | pass
20 | def determinant(self):
21 | pass
22 | def identity(self):
23 | pass
24 | def inverse(self):
25 | return Matrix3()
26 | def rotate(self, vector_or_x, y=0, z=0, w=0):
27 | pass
28 | def rotation(self):
29 | pass
30 | def rotationX(self, value):
31 | pass
32 | def rotationY(self, value):
33 | pass
34 | def rotationZ(self, value):
35 | pass
36 |
37 |
38 | class Matrix4(Matrix3):
39 | def set(self, v1,v2,v3,v4,v5,v6,v7,v8,v9,v10,v11,v12,v13,v14,v15,v16):
40 | pass
41 | def scaleOnly(self):
42 | pass
43 | def rotationOnly(self):
44 | pass
45 | def rotationsZXY(self):
46 | return (0,0,0)
47 | def mapQuadToUnitSquare(self, v1,v2,v3,v4,v5,v6,v7,v8,v9):
48 | pass
49 | def mapUnitSquareToQuad(self, v1,v2,v3,v4,v5,v6,v7,v8,v9):
50 | pass
51 | def ntransform(self, vector):
52 | return Vector3()
53 | def projection(self, v1, v2, v3, bol):
54 | pass
55 | def scaleAndRotationOnly(self):
56 | pass
57 | def skewXY(self):
58 | pass
59 | def translate(self):
60 | pass
61 | def transpose(self):
62 | pass
63 | def translationOnly(self):
64 | pass
65 | def vtransform(self, vector):
66 | return Vector3
67 | def xAxis(self):
68 | return Vector3
69 | def yAxis(self):
70 | return Vector3
71 | def zAxis(self):
72 | return Vector3
73 |
74 | class Vector2(object):
75 | def __init__(self, x=0, y=0):
76 | pass
77 | def cross(self, vetor2):
78 | return Vector2()
79 | def distanceBetween(self):
80 | pass
81 | def distanceSquared(self):
82 | pass
83 | def dot(self):
84 | return 0
85 | def length(self):
86 | pass
87 | def lengthSquared(self):
88 | pass
89 | def negate(self):
90 | pass
91 | def normalize(self):
92 | pass
93 | def set(self):
94 | pass
95 | def x(self):
96 | pass
97 | def y(self):
98 | pass
99 |
100 | class Vector3(Vector2):
101 | def __init__(self, x=0, y=0, z=0):
102 | super(Vector3, self).__init__()
103 | pass
104 | def distanceFromPlane(self):
105 | pass
106 | def fast_normalize(self):
107 | pass
108 | def maximum(self):
109 | pass
110 | def minimum(self):
111 | pass
112 | def reflect(self):
113 | pass
114 | def cross(self, vector3):
115 | return Vector3()
116 |
117 | class Vector4(object):
118 | def x(self):
119 | pass
120 | def y(self):
121 | pass
122 | def z(self):
123 | pass
124 | def w(self):
125 | pass
126 |
127 | class Quaternion(object):
128 | s=0.0
129 | vx=0.0
130 | vy=0.0
131 | vz=0.0
132 | def addInverse(self):
133 | pass
134 | def conjugate(self):
135 | pass
136 | def magnitude(self):
137 | pass
138 | def matrix(self):
139 | return Matrix4()
140 | def multInverse(self):
141 | return Quaternion()
142 | def set(self):
143 | pass
144 | def slerp(self):
145 | pass
146 |
--------------------------------------------------------------------------------
/multi_script_editor/managers/nuke/rotopaint.py:
--------------------------------------------------------------------------------
1 | from main import Knob
2 |
3 |
4 | class AnimAttributes(object):
5 | pass
6 | class AnimCTransform(object):
7 | pass
8 | class AnimControlPoint(object):
9 | pass
10 | class AnimCurve(object):
11 | pass
12 | class AnimCurveKey(object):
13 | pass
14 | class AnimCurveViews(object):
15 | pass
16 | class BaseCurve(object):
17 | pass
18 | class CMatrix4(object):
19 | pass
20 | class CTransform(object):
21 | pass
22 | class CVec2(object):
23 | pass
24 | class CVec3(object):
25 | pass
26 | class CVec4(object):
27 | pass
28 | class ControlPoint(object):
29 | pass
30 | class CubicCurve(object):
31 | pass
32 | class CurveKnob(Knob):
33 | curveWidget=None
34 | rootLayer=None
35 | def changed(self):
36 | """
37 | Call this after performing updates on the tree, to tell it that it's been updated. For many operations this is called automatically, but you can call it manually for those cases where it isn't.
38 |
39 | Returns: None
40 | """
41 | def toElement(self, path):
42 | """
43 | Takes a path which identifies a particular element in the curve tree and returns the corresponding Layer, Stroke or Shape object. The path is a slash separated string and is always resolved relative to the root layer. So if, for example, you have a RotoPaint node with a layer called 'Layer1' which contains a shape called 'Shape1', the path to the shape would be 'Layer1/Shape1'. >>> knob = nuke.toNode('RotoPaint1)['curves'] >>> shape = knob.toElement('Layer1/Shape1') >>> shape.name 'Shape1'
44 |
45 | Returns: Element
46 | """
47 |
48 | class RotoKnob(CurveKnob):
49 | pass
50 | class CurveType(object):
51 | pass
52 | class Element(object):
53 | pass
54 | class ExtrapolationType(object):
55 | pass
56 | class Flag(object):
57 | pass
58 | class FlagType(object):
59 | pass
60 | class Hash(object):
61 | pass
62 | class InterpolationType(object):
63 | pass
64 | class Layer(object):
65 | pass
66 | class RotationOrder(object):
67 | pass
68 | class Shape(object):
69 | pass
70 | class ShapeControlPoint(object):
71 | pass
72 | class Stroke(object):
73 | pass
74 | class TransformOrder(object):
75 | pass
76 |
77 | def convertDirectoryToNuke6():
78 | pass
79 | def convertDirectoryToNuke7():
80 | pass
81 | def convertToNuke6():
82 | pass
83 | def convertToNuke7():
84 | pass
85 |
--------------------------------------------------------------------------------
/multi_script_editor/managers/run_3dsmax.py:
--------------------------------------------------------------------------------
1 | import multi_script_editor
2 | reload(multi_script_editor)
3 | multi_script_editor.show3DSMax()
--------------------------------------------------------------------------------
/multi_script_editor/run.cmd:
--------------------------------------------------------------------------------
1 | start c:\python27\python.exe scriptEditor.py %*
--------------------------------------------------------------------------------
/multi_script_editor/run.sh:
--------------------------------------------------------------------------------
1 | #!/bin/bash
2 | CURRENT=`dirname $(readlink -f $0)`
3 | python "$CURRENT/scriptEditor.py"
4 |
--------------------------------------------------------------------------------
/multi_script_editor/sessionManager.py:
--------------------------------------------------------------------------------
1 | from __future__ import with_statement
2 | import json
3 | import os
4 | import codecs
5 | import settingsManager
6 |
7 | sessionFilename = 'pw_scriptEditor_session.json'
8 |
9 |
10 | class sessionManagerClass(object):
11 | def __init__(self):
12 | self.path = os.path.normpath(os.path.join(settingsManager.userPrefFolder(), sessionFilename))
13 | if not os.path.exists(self.path):
14 | f = open(self.path, 'w')
15 | f.write('{}')
16 | f.close()
17 |
18 | def readSession(self):
19 | if os.path.exists(self.path):
20 | with codecs.open(self.path, "r", "utf-16") as stream:
21 | try:
22 | return json.load(stream)
23 | except:
24 | return []
25 | return []
26 |
27 | def writeSession(self, data):
28 | with codecs.open(self.path, "w", "utf-16") as stream:
29 | json.dump(data, stream, indent=4)
30 | return self.path
31 |
--------------------------------------------------------------------------------
/multi_script_editor/settingsManager.py:
--------------------------------------------------------------------------------
1 | import json
2 | import os
3 | import codecs
4 | from managers import context
5 |
6 | settingsFilename = 'pw_scriptEditor_pref.json'
7 |
8 | def userPrefFolder():
9 | appData = None
10 | if context == 'hou':
11 | appData = os.getenv('HOUDINI_USER_PREF_DIR')
12 | elif context == 'maya':
13 | appData = os.getenv('MAYA_APP_DIR')
14 | elif context == 'nuke':
15 | appData = os.path.join(os.environ['HOME'], '.nuke')
16 | elif context == 'max':
17 | import MaxPlus
18 | appData = os.path.dirname(MaxPlus.PathManager.GetTempDir())
19 | if not appData:
20 | appData = os.getenv('HOME') or os.path.expanduser('~')
21 | return appData
22 |
23 | def settingsFile():
24 | path = os.path.normpath(os.path.join(userPrefFolder(), settingsFilename)).replace('\\','/')
25 | if not os.path.exists(path):
26 | with open(path, 'w') as f:
27 | f.write('[]')
28 | return path
29 |
30 |
31 | class scriptEditorClass(object):
32 | def __init__(self):
33 | super(scriptEditorClass, self).__init__()
34 | self.path = settingsFile()
35 |
36 | def readSettings(self):
37 | if os.path.exists(self.path) and os.path.isfile(self.path):
38 | with codecs.open(self.path, "r", "utf-16") as stream:
39 | try:
40 | return json.load(stream)
41 | except:
42 | return self.defaults()
43 | return self.defaults()
44 |
45 | def writeSettings(self, data):
46 | with codecs.open(self.path, "w", "utf-16") as stream:
47 | json.dump(data, stream, indent=4)
48 |
49 | def defaults(self):
50 | return dict(geometry=None,
51 | outFontSize=8
52 | )
53 |
54 |
--------------------------------------------------------------------------------
/multi_script_editor/shortcuts.txt:
--------------------------------------------------------------------------------
1 | Execute selected > CTRL + ENTER
2 | Execute All > CTRL + SHIFT + ENTER
3 | Indent Selection > TAB
4 | Unindent Selection > SHIFT + TAB
5 | Activate Completer > UP or DOWN
6 | Deactivate Completer > BACKSPACE or ane char
7 | Hide Completer > ESC
8 | Autocomplete code > ENTER (in Completer)
9 | Autocomplete first > ENTER or TAB in Editor
10 | Font Size > CTRL + MouseWheel
11 | Scroll Code Left-Right > ALT + MouseWheel
12 | Duplicate line or selected text > CTRL + D
13 | Comment/Uncomment line or selected lines > ALT + Q
14 | Undo > CTRL + Z
15 | Redo > CTRL + Y
16 | Copy > CTRL + C
17 | Paste > CTRL + V
18 | Cut > CTRL + X
19 | Find and Replace > CTRL + F
20 | Wrap dropped nodes (Maya and Houdini) > MMB Drag + ALT
--------------------------------------------------------------------------------
/multi_script_editor/style/__init__.py:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/paulwinex/pw_MultiScriptEditor/e447e99f87cb07e238baf693b7e124e50efdbc51/multi_script_editor/style/__init__.py
--------------------------------------------------------------------------------
/multi_script_editor/style/completer.qss:
--------------------------------------------------------------------------------
1 | /*********** LIST WIDGET**************/
2 | QAbstractItemView
3 | {
4 | color: rgb[completer_text];
5 | background-color: rgb[completer_background];
6 | alternate-background-color: rgb[completer_alt_background];
7 | height: 25px;
8 | font: [textsize]pt;
9 | }
10 | QListView::item:selected{
11 | background: rgb[completer_selected_background];
12 | }
13 |
14 |
15 | QListView:item:hover
16 | {
17 | background-color:rgb[completer_hover_background];
18 | color: rgb[completer_text];
19 | }
20 |
21 | /***********SCROLLBAR***************/
22 |
23 | QScrollBar::handle:vertical
24 | {
25 | background: #565656;
26 | min-height: 20px;
27 | border-radius: 2px;
28 | }
29 | QScrollBar::handle:vertical:pressed
30 | {
31 | background: #5b5a5a;
32 | }
33 |
34 | QScrollBar::add-line:vertical, QScrollBar::sub-line:vertical
35 | {
36 | border: 1px solid #1b1b19;
37 | border-radius: 1px;
38 | background: #565656;
39 | height: 14px;
40 | subcontrol-origin: margin;
41 | }
42 | QScrollBar::add-line:vertical:pressed, QScrollBar::sub-line:vertical:pressed
43 | {
44 | background: #5b5a5a;
45 | }
46 |
47 | QScrollBar::sub-line:vertical
48 | {
49 | subcontrol-position: top;
50 | }
51 | QScrollBar::add-line:vertical
52 | {
53 | subcontrol-position: bottom;
54 | }
55 |
56 | QScrollBar:vertical
57 | {
58 | background: QLinearGradient( x1: 0, y1: 0, x2: 1, y2: 0, stop: 0.0 #121212, stop: 0.2 #282828, stop: 1 #484848);
59 | width: 15px;
60 | margin: 16px 0 16px 0;
61 | border: 1px solid #222222;
62 | }
63 |
64 | QScrollBar::up-arrow:vertical, QScrollBar::down-arrow:vertical
65 | {
66 | border: 0px;
67 | width: 10px;
68 | height: 10px;
69 | background: #565656;
70 | }
71 | QScrollBar::up-arrow:vertical:pressed, QScrollBar::down-arrow:vertical:pressed
72 | {
73 | background: #5b5a5a;
74 | }
75 |
76 | QScrollBar::up-arrow:vertical
77 | {
78 | border-image: url(:/arrow_up.png) 1;
79 | }
80 | QScrollBar::down-arrow:vertical
81 | {
82 | border-image: url(:/arrow_down.png) 1;
83 | }
84 |
85 | QScrollBar::add-page:vertical, QScrollBar::sub-page:vertical
86 | {
87 | background: none;
88 | }
89 |
90 | QTextEdit{
91 | background: rgb[background];
92 | }
--------------------------------------------------------------------------------
/multi_script_editor/style/links.py:
--------------------------------------------------------------------------------
1 | links = dict(
2 | donate='',
3 | tutorials='http://paulwinex.com/portfolio/multi-script-editor/',
4 | manual='https://github.com/paulwinex/pw_MultiScriptEditor',
5 | site='http://www.paulwinex.com/'
6 | )
--------------------------------------------------------------------------------
/multi_script_editor/style/pw.ico:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/paulwinex/pw_MultiScriptEditor/e447e99f87cb07e238baf693b7e124e50efdbc51/multi_script_editor/style/pw.ico
--------------------------------------------------------------------------------
/multi_script_editor/style/pw.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/paulwinex/pw_MultiScriptEditor/e447e99f87cb07e238baf693b7e124e50efdbc51/multi_script_editor/style/pw.png
--------------------------------------------------------------------------------
/multi_script_editor/style/script_editor.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/paulwinex/pw_MultiScriptEditor/e447e99f87cb07e238baf693b7e124e50efdbc51/multi_script_editor/style/script_editor.png
--------------------------------------------------------------------------------
/multi_script_editor/tested.txt:
--------------------------------------------------------------------------------
1 | Supported applications:
2 | Maya
3 | Houdini
4 | Nuke
5 | 3DsMax
6 |
7 | Tested on:
8 | Windows 7x64
9 | Windows 10x64
10 | Ubuntu 12.04
11 | Ubuntu 14.04
12 | Ubuntu 16.04
13 |
14 | Maya 2018
15 | Maya 2017
16 | Maya 2016
17 | Maya 2015
18 | Maya 2014
19 | Maya 2013
20 | Houdini 13
21 | Houdini 14
22 | Houdini 15
23 | Nuke 8
24 | Nuke 9
25 | Nuke 10
26 |
--------------------------------------------------------------------------------
/multi_script_editor/widgets/__init__.py:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/paulwinex/pw_MultiScriptEditor/e447e99f87cb07e238baf693b7e124e50efdbc51/multi_script_editor/widgets/__init__.py
--------------------------------------------------------------------------------
/multi_script_editor/widgets/about.py:
--------------------------------------------------------------------------------
1 | try:
2 | from PySide.QtCore import *
3 | from PySide.QtGui import *
4 | except:
5 | from PySide2.QtCore import *
6 | from PySide2.QtGui import *
7 | from PySide2.QtWidgets import *
8 | import icons
9 | import about_UIs
10 | import os
11 |
12 | class aboutClass(QDialog, about_UIs.Ui_Dialog):
13 | def __init__(self, parent):
14 | super(aboutClass, self).__init__(parent)
15 | self.setupUi(self)
16 | self.title_lb.setText(self.title_lb.text()+str(parent.ver))
17 | self.text_link_lb.setText(text)
18 | self.icon_lb.setPixmap(QPixmap(icons.icons['pw']).scaled(60,60, Qt.KeepAspectRatio, Qt.SmoothTransformation))
19 | self.donate_btn.setMinimumHeight(35)
20 | self.donate_btn.setIconSize(QSize(24,24))
21 | self.donate_btn.setIcon(QIcon(icons.icons['donate']))
22 | self.donate_btn.clicked.connect(lambda :parent.openLink('donate'))
23 | self.donate_btn.hide()
24 | testedFile = os.path.join(os.path.dirname(os.path.dirname(__file__)), 'tested.txt')
25 | if os.path.exists(testedFile):
26 | outText = open(testedFile).read()
27 | self.textBrowser.setPlainText(outText)
28 |
29 |
30 | text = '''Paul Winex 2018
31 | Any question or bug report: paulwinex@gmail.com
32 | '''
--------------------------------------------------------------------------------
/multi_script_editor/widgets/about.ui:
--------------------------------------------------------------------------------
1 |
2 |
3 | Dialog
4 |
5 |
6 |
7 | 0
8 | 0
9 | 465
10 | 393
11 |
12 |
13 |
14 | About Multi Script Editor
15 |
16 |
17 | -
18 |
19 |
20 | 20
21 |
22 |
23 | 20
24 |
25 |
-
26 |
27 |
28 | Qt::Horizontal
29 |
30 |
31 |
32 | 40
33 | 20
34 |
35 |
36 |
37 |
38 | -
39 |
40 |
41 |
42 | 20
43 |
44 |
45 |
46 |
47 |
48 |
49 |
50 | -
51 |
52 |
53 |
54 | 20
55 |
56 |
57 |
58 | Multi Script Editor v
59 |
60 |
61 |
62 | -
63 |
64 |
65 | Qt::Horizontal
66 |
67 |
68 |
69 | 40
70 | 20
71 |
72 |
73 |
74 |
75 |
76 |
77 | -
78 |
79 |
80 | Paul Winex 2015
81 |
82 |
83 |
84 | -
85 |
86 |
87 | <!DOCTYPE HTML PUBLIC "-//W3C//DTD HTML 4.0//EN" "http://www.w3.org/TR/REC-html40/strict.dtd">
88 | <html><head><meta name="qrichtext" content="1" /><style type="text/css">
89 | p, li { white-space: pre-wrap; }
90 | </style></head><body style=" font-family:'MS Shell Dlg 2'; font-size:8.25pt; font-weight:400; font-style:normal;">
91 | <p style=" margin-top:0px; margin-bottom:0px; margin-left:0px; margin-right:0px; -qt-block-indent:0; text-indent:0px;"><span style=" font-size:8pt;">File not Found :(</span></p></body></html>
92 |
93 |
94 |
95 | -
96 |
97 |
-
98 |
99 |
100 | Donate
101 |
102 |
103 |
104 |
105 |
106 |
107 |
108 |
109 |
110 |
111 |
--------------------------------------------------------------------------------
/multi_script_editor/widgets/about_UIs.py:
--------------------------------------------------------------------------------
1 | # -*- coding: utf-8 -*-
2 |
3 | # Form implementation generated from reading ui file 'D:\Dropbox\Dropbox\pw_prefs\RnD\tools\pw_scriptEditor\multi_script_editor\widgets\about.ui'
4 | #
5 | # Created: Thu Apr 02 22:56:45 2015
6 | # by: pyside-uic 0.2.15 running on PySide 1.2.2
7 | #
8 | # WARNING! All changes made in this file will be lost!
9 |
10 | try:
11 | from PySide.QtCore import *
12 | from PySide.QtGui import *
13 | except:
14 | from PySide2.QtCore import *
15 | from PySide2.QtGui import *
16 | from PySide2.QtWidgets import *
17 |
18 | class Ui_Dialog(object):
19 | def setupUi(self, Dialog):
20 | Dialog.setObjectName("Dialog")
21 | Dialog.resize(465, 393)
22 | self.verticalLayout = QVBoxLayout(Dialog)
23 | self.verticalLayout.setObjectName("verticalLayout")
24 | self.horizontalLayout = QHBoxLayout()
25 | self.horizontalLayout.setContentsMargins(-1, 20, -1, 20)
26 | self.horizontalLayout.setObjectName("horizontalLayout")
27 | spacerItem = QSpacerItem(40, 20, QSizePolicy.Expanding, QSizePolicy.Minimum)
28 | self.horizontalLayout.addItem(spacerItem)
29 | self.icon_lb = QLabel(Dialog)
30 | font = QFont()
31 | font.setPointSize(20)
32 | self.icon_lb.setFont(font)
33 | self.icon_lb.setText("")
34 | self.icon_lb.setObjectName("icon_lb")
35 | self.horizontalLayout.addWidget(self.icon_lb)
36 | self.title_lb = QLabel(Dialog)
37 | font = QFont()
38 | font.setPointSize(20)
39 | self.title_lb.setFont(font)
40 | self.title_lb.setObjectName("title_lb")
41 | self.horizontalLayout.addWidget(self.title_lb)
42 | spacerItem1 = QSpacerItem(40, 20, QSizePolicy.Expanding, QSizePolicy.Minimum)
43 | self.horizontalLayout.addItem(spacerItem1)
44 | self.verticalLayout.addLayout(self.horizontalLayout)
45 | self.text_link_lb = QLabel(Dialog)
46 | self.text_link_lb.setObjectName("text_link_lb")
47 | self.verticalLayout.addWidget(self.text_link_lb)
48 | self.textBrowser = QTextBrowser(Dialog)
49 | self.textBrowser.setObjectName("textBrowser")
50 | self.verticalLayout.addWidget(self.textBrowser)
51 | self.horizontalLayout_2 = QHBoxLayout()
52 | self.horizontalLayout_2.setObjectName("horizontalLayout_2")
53 | self.donate_btn = QPushButton(Dialog)
54 | self.donate_btn.setObjectName("donate_btn")
55 | self.horizontalLayout_2.addWidget(self.donate_btn)
56 | self.verticalLayout.addLayout(self.horizontalLayout_2)
57 | self.verticalLayout.setStretch(2, 1)
58 |
59 | self.retranslateUi(Dialog)
60 | QMetaObject.connectSlotsByName(Dialog)
61 |
62 | def retranslateUi(self, Dialog):
63 | Dialog.setWindowTitle(QApplication.translate("Dialog", "About Multi Script Editor", None))
64 | self.title_lb.setText(QApplication.translate("Dialog", "Multi Script Editor v", None))
65 | self.text_link_lb.setText(QApplication.translate("Dialog", "Paul Winex 2015", None))
66 | self.textBrowser.setHtml(QApplication.translate("Dialog", "\n"
67 | "\n"
70 | "File not Found :(
", None))
71 | self.donate_btn.setText(QApplication.translate("Dialog", "Donate", None))
72 |
73 |
--------------------------------------------------------------------------------
/multi_script_editor/widgets/completeWidget.py:
--------------------------------------------------------------------------------
1 | try:
2 | from PySide.QtCore import *
3 | from PySide.QtGui import *
4 | except:
5 | from PySide2.QtCore import *
6 | from PySide2.QtGui import *
7 | from PySide2.QtWidgets import *
8 | import os, re
9 | from . pythonSyntax import design
10 | import managers
11 | style = os.path.join(os.path.dirname(os.path.dirname(__file__)), 'style', 'completer.qss')
12 | if not os.path.exists(style):
13 | style=None
14 |
15 |
16 | class completeMenuClass(QListWidget):
17 | def __init__(self, parent=None, editor=None):
18 | # if managers.context == 'hou':
19 | # super(completeMenuClass, self).__init__(managers.main_parent or parent)
20 | # else:
21 | super(completeMenuClass, self).__init__(parent)
22 | self.setAlternatingRowColors(1)
23 | self.lineHeight = 18
24 | self.e = editor
25 | self.setAttribute(Qt.WA_ShowWithoutActivating)
26 | if managers._s == 'w':
27 | self.setWindowFlags(Qt.FramelessWindowHint | Qt.Window)
28 | else:
29 | self.setWindowFlags(Qt.FramelessWindowHint | Qt.Window | Qt.WindowStaysOnTopHint)
30 | @self.itemDoubleClicked.connect
31 | def insertSelected(item):
32 | if item:
33 | comp = item.data(32)
34 | self.sendText(comp)
35 | self.hideMe()
36 |
37 | def updateStyle(self, colors=None):
38 | text = design.editorStyle()
39 | self.setStyleSheet(text)
40 |
41 | def updateCompleteList(self, lines=None, extra=None):
42 | self.clear()
43 | if lines or extra:
44 | self.showMe()
45 | if lines:
46 | for i in [x for x in lines if not x.name == 'mro']:
47 | item = QListWidgetItem(i.name)
48 | item.setData(32, i)
49 | self.addItem(item)
50 | if extra:
51 |
52 | font = self.font()
53 | font.setItalic(1)
54 | font.setPointSize(font.pointSize()*0.8)
55 | for e in extra:
56 | item = QListWidgetItem(e.name)
57 | item.setData(32, e)
58 | item.setFont(font)
59 | self.addItem(item)
60 |
61 | font = QFont("monospace", self.lineHeight, False)
62 | fm = QFontMetrics (font)
63 | width = fm.width(' ') * max([len(x.name) for x in lines or extra]) + 40
64 |
65 | self.resize(max(250,width), 250)
66 | self.setCurrentRow(0)
67 | else:
68 | self.hideMe()
69 |
70 | def applyCurrentComplete(self):
71 | i = self.selectedItems()
72 | if i:
73 | comp = i[0].data(32)
74 | self.sendText(comp)
75 | self.hideMe()
76 |
77 | def keyPressEvent(self, event):
78 | if event.key() == Qt.Key_Escape:
79 | self.close()
80 | # elif event.text():
81 | # self.editor().setFocus()
82 | elif event.key() == Qt.Key_Return or event.key() == Qt.Key_Enter:
83 | self.editor().setFocus()
84 | self.applyCurrentComplete()
85 | return event
86 | elif event.key() == Qt.Key_Up:
87 | sel = self.selectedItems()
88 | if sel:
89 | i = self.row(sel[0])
90 | if i == 0:
91 | QListWidget.keyPressEvent(self, event)
92 | self.setCurrentRow(self.count()-1)
93 | return
94 | elif event.key() == Qt.Key_Down:
95 | sel = self.selectedItems()
96 | if sel:
97 | i = self.row(sel[0])
98 | if i+1 == self.count():
99 | QListWidget.keyPressEvent(self, event)
100 | self.setCurrentRow(0)
101 | return
102 | elif event.key() == Qt.Key_Backspace:
103 | self.editor().setFocus()
104 | self.editor().activateWindow()
105 | elif event.text():
106 | self.editor().keyPressEvent(event)
107 | return
108 |
109 | QListWidget.keyPressEvent(self, event)
110 |
111 | def sendText(self, comp):
112 | self.editor().insertText(comp)
113 |
114 | def editor(self):
115 | return self.e
116 |
117 | def activateCompleter(self, key=False):
118 | self.activateWindow()
119 | if not key==Qt.Key_Up:
120 | self.setCurrentRow(min(1, self.count()-1))
121 | else:
122 | self.setCurrentRow(self.count()-1)
123 |
124 | def showMe(self):
125 | self.show()
126 | self.e.moveCompleter()
127 |
128 | def hideMe(self):
129 | self.hide()
130 |
--------------------------------------------------------------------------------
/multi_script_editor/widgets/findWidget.py:
--------------------------------------------------------------------------------
1 | try:
2 | from PySide.QtCore import *
3 | from PySide.QtGui import *
4 | except:
5 | from PySide2.QtCore import *
6 | from PySide2.QtGui import *
7 | from PySide2.QtWidgets import *
8 | import findWidget_UIs as ui
9 |
10 | class findWidgetClass(QWidget, ui.Ui_findReplace):
11 | searchSignal = Signal(str)
12 | replaceSignal = Signal(list)
13 | replaceAllSignal = Signal(list)
14 | def __init__(self, parent):
15 | super(findWidgetClass, self).__init__(parent)
16 | self.setupUi(self)
17 | self.setWindowFlags(Qt.Tool)
18 | center = parent.parent().mapToGlobal(parent.geometry().center())
19 | myGeo = self.geometry()
20 | myGeo.moveCenter(center)
21 | self.setGeometry(myGeo)
22 | self.find_le.setFocus()
23 | #connect
24 | self.find_btn.clicked.connect(self.search)
25 | self.find_le.returnPressed.connect(self.search)
26 | self.replace_btn.clicked.connect(self.replace)
27 | self.replace_le.returnPressed.connect(self.replace)
28 | self.replaceAll_btn.clicked.connect(self.replaceAll)
29 |
30 | def search(self):
31 | self.searchSignal.emit(self.find_le.text())
32 | QTimer.singleShot(10, self.find_le.setFocus)
33 |
34 | def replace(self):
35 | find = self.find_le.text()
36 | rep = self.replace_le.text()
37 | self.replaceSignal.emit([find, rep])
38 | QTimer.singleShot(10, self.replace_le.setFocus)
39 |
40 | def replaceAll(self):
41 | find = self.find_le.text()
42 | rep = self.replace_le.text()
43 | self.replaceAllSignal.emit([find, rep])
44 | QTimer.singleShot(10, self.replace_le.setFocus)
45 |
46 | def keyPressEvent(self, event):
47 | if event.key() == Qt.Key_Escape:
48 | self.close()
49 | super(findWidgetClass, self).keyPressEvent(event)
--------------------------------------------------------------------------------
/multi_script_editor/widgets/findWidget.ui:
--------------------------------------------------------------------------------
1 |
2 |
3 | findReplace
4 |
5 |
6 |
7 | 0
8 | 0
9 | 246
10 | 101
11 |
12 |
13 |
14 | Find and Replace
15 |
16 |
17 | -
18 |
19 |
-
20 |
21 |
22 | -
23 |
24 |
25 | -
26 |
27 |
28 | Find
29 |
30 |
31 |
32 | -
33 |
34 |
35 | Replace
36 |
37 |
38 |
39 | -
40 |
41 |
42 | Replace All
43 |
44 |
45 |
46 |
47 |
48 |
49 |
50 |
51 | find_le
52 | replace_le
53 | find_btn
54 | replace_btn
55 | replaceAll_btn
56 |
57 |
58 |
59 |
60 |
--------------------------------------------------------------------------------
/multi_script_editor/widgets/findWidget_UIs.py:
--------------------------------------------------------------------------------
1 | # -*- coding: utf-8 -*-
2 |
3 |
4 | # Created: Thu Apr 02 15:51:34 2015
5 | # by: pyside-uic 0.2.15 running on PySide 1.2.2
6 | #
7 | # WARNING! All changes made in this file will be lost!
8 |
9 | try:
10 | from PySide.QtCore import *
11 | from PySide.QtGui import *
12 | except:
13 | from PySide2.QtCore import *
14 | from PySide2.QtGui import *
15 | from PySide2.QtWidgets import *
16 |
17 |
18 | class Ui_findReplace(object):
19 | def setupUi(self, findReplace):
20 | findReplace.setObjectName("findReplace")
21 | findReplace.resize(246, 101)
22 | self.verticalLayout = QVBoxLayout(findReplace)
23 | self.verticalLayout.setObjectName("verticalLayout")
24 | self.gridLayout = QGridLayout()
25 | self.gridLayout.setObjectName("gridLayout")
26 | self.replace_le = QLineEdit(findReplace)
27 | self.replace_le.setObjectName("replace_le")
28 | self.gridLayout.addWidget(self.replace_le, 1, 0, 1, 1)
29 | self.find_le = QLineEdit(findReplace)
30 | self.find_le.setObjectName("find_le")
31 | self.gridLayout.addWidget(self.find_le, 0, 0, 1, 1)
32 | self.find_btn = QPushButton(findReplace)
33 | self.find_btn.setObjectName("find_btn")
34 | self.gridLayout.addWidget(self.find_btn, 0, 1, 1, 1)
35 | self.replace_btn = QPushButton(findReplace)
36 | self.replace_btn.setObjectName("replace_btn")
37 | self.gridLayout.addWidget(self.replace_btn, 1, 1, 1, 1)
38 | self.replaceAll_btn = QPushButton(findReplace)
39 | self.replaceAll_btn.setObjectName("replaceAll_btn")
40 | self.gridLayout.addWidget(self.replaceAll_btn, 2, 1, 1, 1)
41 | self.verticalLayout.addLayout(self.gridLayout)
42 |
43 | self.retranslateUi(findReplace)
44 | QMetaObject.connectSlotsByName(findReplace)
45 | findReplace.setTabOrder(self.find_le, self.replace_le)
46 | findReplace.setTabOrder(self.replace_le, self.find_btn)
47 | findReplace.setTabOrder(self.find_btn, self.replace_btn)
48 | findReplace.setTabOrder(self.replace_btn, self.replaceAll_btn)
49 |
50 | def retranslateUi(self, findReplace):
51 | findReplace.setWindowTitle(QApplication.translate("findReplace", "Find and Replace", None))
52 | self.find_btn.setText(QApplication.translate("findReplace", "Find", None))
53 | self.replace_btn.setText(QApplication.translate("findReplace", "Replace", None))
54 | self.replaceAll_btn.setText(QApplication.translate("findReplace", "Replace All", None))
55 |
56 |
--------------------------------------------------------------------------------
/multi_script_editor/widgets/numBarWidget.py:
--------------------------------------------------------------------------------
1 | try:
2 | from PySide.QtCore import *
3 | from PySide.QtGui import *
4 | except:
5 | from PySide2.QtCore import *
6 | from PySide2.QtGui import *
7 | from PySide2.QtWidgets import *
8 | import managers
9 |
10 | class lineNumberBarClass(QWidget):
11 | def __init__(self, edit, parent=None):
12 | QWidget.__init__(self, parent)
13 | self.edit = edit
14 | self.highest_line = 0
15 | self.setMinimumWidth(30)
16 | # self.edit.installEventFilter(self)
17 | # self.edit.viewport().installEventFilter(self)
18 | self.bg = None
19 |
20 | def update(self, *args):
21 | '''
22 | Updates the number bar to display the current set of numbers.
23 | Also, adjusts the width of the number bar if necessary.
24 | '''
25 | # The + 4 is used to compensate for the current line being bold.
26 | if managers.context == 'hou':
27 | fontSize = self.edit.fs
28 | else:
29 | fontSize = self.edit.font().pointSize()
30 | width = ((self.fontMetrics().width(str(self.highest_line)) + 7))*(fontSize/13.0)
31 | if self.width() != width and width > 10:
32 | self.setFixedWidth(width)
33 | bg = self.palette().brush(QPalette.Normal,QPalette.Window).color().toHsv()
34 | v = bg.value()
35 | if v > 20:
36 | v = int(bg.value()*0.8)
37 | else:
38 | v = int(bg.value()*1.1)
39 | self.bg = QColor.fromHsv(bg.hue(), bg.saturation(), v)
40 | QWidget.update(self, *args)
41 |
42 | def paintEvent(self, event):
43 | contents_y = self.edit.verticalScrollBar().value()
44 | page_bottom = contents_y + self.edit.viewport().height()
45 | font_metrics = self.fontMetrics()
46 | current_block = self.edit.document().findBlock(self.edit.textCursor().position())
47 | painter = QPainter(self)
48 | line_count = 0
49 | # Iterate over all text blocks in the document.
50 | block = self.edit.document().begin()
51 | if managers.context == 'hou':
52 | fontSize = self.edit.fs
53 | font = QFont('monospace', fontSize*0.7)
54 | offset = (font_metrics.ascent() + font_metrics.descent())/2
55 | else:
56 | fontSize = self.edit.font().pointSize()
57 | font = painter.font()
58 | font.setPixelSize(fontSize)
59 | offset = font_metrics.ascent() + font_metrics.descent()
60 | color = painter.pen().color()
61 | painter.setFont(font)
62 | align = Qt.AlignRight
63 | while block.isValid():
64 | line_count += 1
65 | # The top left position of the block in the document
66 | position = self.edit.document().documentLayout().blockBoundingRect(block).topLeft()
67 | # Check if the position of the block is out side of the visible
68 | # area.
69 | if position.y() == page_bottom:
70 | break
71 |
72 | rec = QRect(0,
73 | round(position.y()) - contents_y,
74 | self.width()-5,
75 | fontSize + offset)
76 |
77 | # draw line rect
78 | if block == current_block:
79 | painter.setPen(Qt.NoPen)
80 | painter.setBrush(QBrush(self.bg))
81 | painter.drawRect(QRect(0,
82 | round(position.y()) - contents_y,
83 | self.width(),
84 | fontSize + (offset/2) ))
85 | # #restore color
86 | painter.setPen(QPen(color))
87 |
88 | # draw text
89 | painter.drawText(rec, align, str(line_count))
90 | # control points
91 |
92 | block = block.next()
93 | self.highest_line = line_count
94 | painter.end()
95 | QWidget.paintEvent(self, event)
96 |
97 | def eventFilter(self, object, event):
98 | # Update the line numbers for all events on the text edit and the viewport.
99 | # This is easier than connecting all necessary singals.
100 | if object in (self.edit, self.edit.viewport()):
101 | self.update()
102 | return False
103 | return QWidget.eventFilter(object, event)
--------------------------------------------------------------------------------
/multi_script_editor/widgets/outputWidget.py:
--------------------------------------------------------------------------------
1 | try:
2 | from PySide.QtCore import *
3 | from PySide.QtGui import *
4 | except:
5 | from PySide2.QtCore import *
6 | from PySide2.QtGui import *
7 | from PySide2.QtWidgets import *
8 |
9 | from managers import context
10 | font_name = 'Courier'
11 |
12 |
13 | class outputClass(QTextBrowser):
14 | def __init__(self):
15 | super(outputClass, self).__init__()
16 | self.setWordWrapMode(QTextOption.NoWrap)
17 | font = QFont("Courier")
18 | font.setStyleHint(QFont.Monospace)
19 | font.setFixedPitch(True)
20 | self.setFont(font)
21 | self.fs = 14
22 | self.document().setDefaultFont(QFont(font_name, self.fs, QFont.Monospace))
23 | metrics = QFontMetrics(self.document().defaultFont())
24 | self.setTabStopWidth(4 * metrics.width(' '))
25 | self.setMouseTracking(1)
26 |
27 | def showMessage(self, msg):
28 | self.moveCursor(QTextCursor.End)
29 | cursor = self.textCursor()
30 | cursor.insertText(str(msg)+'\n')
31 | self.setTextCursor(cursor)
32 | self.moveCursor(QTextCursor.End)
33 | self.ensureCursorVisible()
34 |
35 | def setTextEditFontSize(self, size):
36 | style = '''QTextEdit
37 | {
38 | font-size: %spx;
39 | }''' % size
40 | self.setStyleSheet(style)
41 |
42 |
43 | def wheelEvent(self, event):
44 | if event.modifiers() == Qt.ControlModifier:
45 | if event.delta() > 0:
46 | self.changeFontSize(True)
47 | else:
48 | self.changeFontSize(False)
49 | # super(outputClass, self).wheelEvent(event)
50 | QTextBrowser.wheelEvent(self, event)
51 |
52 | def changeFontSize(self, up):
53 | if context == 'hou':
54 | if up:
55 | self.fs = min(30, self.fs+1)
56 | else:
57 | self.fs = max(8, self.fs - 1)
58 | self.setTextEditFontSize(self.fs)
59 | else:
60 | f = self.font()
61 | size = f.pointSize()
62 | if up:
63 | size = min(30, size+1)
64 | else:
65 | size = max(8, size - 1)
66 | f.setPointSize(size)
67 | self.setFont(f)
68 |
69 |
70 | # def mousePressEvent(self, event):
71 | # print context
72 | # if context == 'hou':
73 | # if event.button() == Qt.LeftButton:
74 | # # super(outputClass, self).mousePressEvent(event)
75 | # QTextBrowser.mousePressEvent(self, event)
76 | # else:
77 | # QTextBrowser.mousePressEvent(self, event)
--------------------------------------------------------------------------------
/multi_script_editor/widgets/pythonSyntax/__init__.py:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/paulwinex/pw_MultiScriptEditor/e447e99f87cb07e238baf693b7e124e50efdbc51/multi_script_editor/widgets/pythonSyntax/__init__.py
--------------------------------------------------------------------------------
/multi_script_editor/widgets/pythonSyntax/design.py:
--------------------------------------------------------------------------------
1 | import settingsManager
2 | import os, re
3 |
4 | EditorStyle = os.path.join(os.path.dirname(os.path.dirname(os.path.dirname(__file__))), 'style', 'completer.qss')
5 | if not os.path.exists(EditorStyle):
6 | EditorStyle=None
7 |
8 | defaultColors = dict(
9 | background = (40,40,40),
10 | keywords = (65,255,130),
11 | digits = (250,255,62),
12 | definition = (255,160,250),
13 | operator = (230, 220, 110),
14 | extra = (110,180,230),
15 | methods = (120, 190, 205),
16 | comment = (110,100,100),
17 | string = (245,165,18),
18 | docstring = (130,160,75),
19 | boolean = (160,220,120),
20 | brace = (235,235,195),
21 | completer_text=(200,200,200),
22 | completer_selected_text= (105,105,105),
23 | completer_hover_text= (255,255,255),
24 | completer_background=(59,59,59),
25 | completer_alt_background= (65,65,65),
26 | completer_hover_background= (85,85,85),
27 | completer_selected_background= (123,123,123),
28 | default=(210,210,210)
29 | )
30 |
31 |
32 | def getColors(theme=False):
33 | s = settingsManager.scriptEditorClass()
34 | settings = s.readSettings()
35 | if not theme:
36 | theme = settings.get('theme')
37 | result = {k:v for k,v in defaultColors.items()}
38 | if theme:
39 | if 'colors' in settings:
40 | colors = settings['colors'].get(theme)
41 | if colors:
42 | for k, v in colors.items():
43 | result[k] = v
44 | return result
45 |
46 |
47 | def editorStyle(theme=None):
48 | colors = getColors(theme)
49 | colors = {k:tuple(v) if isinstance(v, list) else v for k,v in colors.items()}
50 | if EditorStyle:
51 | text = open(EditorStyle).read()
52 | proxys = re.findall('\[.*\]', text)
53 | for p in proxys:
54 | name = p[1:-1]
55 | if name in colors:
56 | text = text.replace(p, str(colors[name]))
57 | return text
58 |
59 | def applyColorToEditorStyle(colors=None):
60 | if EditorStyle:
61 | text = open(EditorStyle).read()
62 | proxys = re.findall('\[.*\]', text)
63 | for p in proxys:
64 | name = p[1:-1]
65 | if name in colors:
66 | c = colors[name]
67 | if isinstance(c, list):
68 | c = tuple(c)
69 | text = text.replace(p, str(c))
70 | return text
71 | return ''
72 |
--------------------------------------------------------------------------------
/multi_script_editor/widgets/pythonSyntax/keywords.py:
--------------------------------------------------------------------------------
1 | syntax = {"extension": [
2 | "py", "pyw"],
3 |
4 | "comment": ["#"],
5 |
6 | "string": ["\"", "'"],
7 |
8 | "operators": ["=",
9 | # Comparison
10 | "!=", "<", ">",
11 | # Arithmetic
12 | "+", "\-", "*", "/", "%", "*",
13 | # Bitwise
14 | "^", "|", "&"],
15 | "keywords": [ "and",
16 | "assert",
17 | "break",
18 | "continue",
19 | "del",
20 | "elif",
21 | "else",
22 | "except",
23 | "exec",
24 | "finally",
25 | "for",
26 | "from",
27 | "global",
28 | "if",
29 | "import",
30 | "in",
31 | "is",
32 | "not",
33 | "or",
34 | "pass",
35 | "print",
36 | "raise",
37 | "return",
38 | "try",
39 | "while",
40 | "yield",
41 | "None",
42 | "with"
43 | ],
44 | "definition":["class",
45 | "def",
46 | "lambda"],
47 | "boolean" : ["True",
48 | "False"],
49 | "extras": [
50 | "abs",
51 | "all",
52 | "any",
53 | "basestring",
54 | "bin",
55 | "bool",
56 | "bytearray",
57 | "callable",
58 | "chr",
59 | "classmethod",
60 | "cmp",
61 | "compile",
62 | "complex",
63 | "delattr",
64 | "dict",
65 | "dir",
66 | "divmod",
67 | "enumerate",
68 | "eval",
69 | "execfile",
70 | "file",
71 | "filter",
72 | "float",
73 | "format",
74 | "frozenset",
75 | "getattr",
76 | "globals",
77 | "hasattr",
78 | "hash",
79 | "help",
80 | "hex",
81 | "id",
82 | "input",
83 | "int",
84 | "isinstance",
85 | "issubclass",
86 | "iter",
87 | "len",
88 | "list",
89 | "locals",
90 | "long",
91 | "map",
92 | "max",
93 | "memoryview",
94 | "min",
95 | "next",
96 | "object",
97 | "oct",
98 | "open",
99 | "ord",
100 | "pow",
101 | "property",
102 | "range",
103 | "raw_input",
104 | "reduce",
105 | "reload",
106 | "repr",
107 | "reversed",
108 | "round",
109 | "set",
110 | "setattr",
111 | "slice",
112 | "sorted",
113 | "staticmethod",
114 | "str",
115 | "sum",
116 | "tuple",
117 | "type",
118 | "unichr",
119 | "unicode",
120 | "vars",
121 | "xrange",
122 | "zip",
123 | "apply",
124 | "buffer",
125 | "coerce",
126 | "intern"
127 | ],
128 | "braces" :['\{', '\}', '\(', '\)', '\[', '\]']
129 |
130 | }
131 |
132 |
--------------------------------------------------------------------------------
/multi_script_editor/widgets/pythonSyntax/syntaxHighLighter.py:
--------------------------------------------------------------------------------
1 | try:
2 | from PySide.QtCore import *
3 | from PySide.QtGui import *
4 | except:
5 | from PySide2.QtCore import *
6 | from PySide2.QtGui import *
7 | from PySide2.QtWidgets import *
8 | import re
9 | import design
10 | import keywords
11 |
12 |
13 | class PythonHighlighterClass (QSyntaxHighlighter):
14 | def __init__(self, document, colors=None):
15 | QSyntaxHighlighter.__init__(self, document)
16 |
17 | if colors:
18 | self.colors = colors
19 | else:
20 | self.colors = design.getColors()
21 |
22 | # Multi line comments
23 | self.tri_single = (QRegExp("'''"), 1, self.getStyle(self.colors['docstring']))
24 | self.tri_double = (QRegExp('"""'), 2, self.getStyle(self.colors['docstring']))
25 |
26 | rules = []
27 | # defaults
28 | # rules += [(r".*", 0, self.getStyle(self.colors['default'], False))]
29 | # Keywords
30 | rules += [('\\b%s\\b' % w, 0, self.getStyle(self.colors['keywords'], True))
31 | for w in keywords.syntax['keywords']]
32 | # Methods
33 | rules += [("\\b[A-Za-z0-9_]+(?=\\()", 0, self.getStyle(self.colors['methods'], False))]
34 | # Operators
35 | rules += [(r'[~!@$%^&*()-+=]', 0, self.getStyle(self.colors['operator']))]
36 | # for o in pythonSyntax.syntax['operators']]
37 | #Braces
38 | rules += [(r'%s' % b, 0, self.getStyle(self.colors['brace']))
39 | for b in keywords.syntax['braces']]
40 | # Definition
41 | rules += [("\\b%s\\b" % b, 0, self.getStyle(self.colors['definition'], True))
42 | for b in keywords.syntax['definition']]
43 | # Extra
44 | rules += [("\\b%s\\b" % b, 0, self.getStyle(self.colors['extra']))
45 | for b in keywords.syntax['extras']]
46 |
47 | # Comment
48 | # rules += [(r'#([.*]+|[^#]*)', 0, self.getStyle(design.comment))]
49 |
50 | # Digits
51 | rules += [(r"\b[\d]+\b", 0, self.getStyle(self.colors['digits']))]
52 | # ("(?:^|[^A-Za-z])([\d|\.]*\d+)", 0, self.getStyle(design.digits)),
53 |
54 |
55 | # Double-quoted string
56 | rules += [(r'[ru]?"[^"\\]*(\\.[^"\\]*)*"', 0, self.getStyle(self.colors['string']))]
57 |
58 | # Single-quoted string
59 | rules += [(r"[ru]?'[^'\\]*(\\.[^'\\]*)*'", 0, self.getStyle(self.colors['string']))]
60 |
61 |
62 | # Build a QRegExp for each pattern
63 | self.rules = [(QRegExp(pat), index, fmt) for (pat, index, fmt) in rules]
64 | # self.rehighlight()
65 |
66 |
67 | def getStyle(self, color, bold=False):
68 | brush = QBrush( QColor(*color))
69 | f = QTextCharFormat()
70 | if bold:
71 | f.setFontWeight( QFont.Bold )
72 | f.setForeground( brush )
73 | return f
74 |
75 | def highlightBlock(self, text):
76 | """Apply syntax highlighting to the given block of text.
77 | """
78 | defFormat = self.getStyle(self.colors['default'])
79 | self.setFormat(0, len(text), defFormat)
80 |
81 | # Do other syntax formatting
82 | for expression, nth, format in self.rules:
83 | index = expression.indexIn(text, 0)
84 | # print expression, index
85 | while index >= 0:
86 | # We actually want the index of the nth match
87 | index = expression.pos(nth)
88 | length = len(expression.cap(nth))
89 | self.setFormat(index, length, format)
90 | index = expression.indexIn(text, index + length)
91 |
92 |
93 | strings = re.findall(r'(".*?")|(\'.*?\')', text)
94 | if '#' in text:
95 | copy = text
96 | if strings:
97 | pat = []
98 | for s in strings:
99 | for match in s:
100 | if match:
101 | pat.append(match)
102 | for s in pat:
103 | copy = copy.replace(s, '_'*len(s))
104 | if '#' in copy:
105 | index = copy.index('#')
106 | length = len(copy) - index
107 | self.setFormat(index, length, self.getStyle(self.colors['comment']))
108 |
109 | self.setCurrentBlockState(0)
110 |
111 | # Do multi-line strings
112 | in_multiline = self.match_multiline(text, *self.tri_single)
113 | if not in_multiline:
114 | in_multiline = self.match_multiline(text, *self.tri_double)
115 |
116 |
117 | def match_multiline(self, text, delimiter, in_state, style):
118 | """Do highlighting of multi-line strings. ``delimiter`` should be a
119 | ``QRegExp`` for triple-single-quotes or triple-double-quotes, and
120 | ``in_state`` should be a unique integer to represent the corresponding
121 | state changes when inside those strings. Returns True if we're still
122 | inside a multi-line string when this function is finished. """
123 | # If inside triple-single quotes, start at 0
124 | if self.previousBlockState() == in_state:
125 | start = 0
126 | add = 0
127 | # Otherwise, look for the delimiter on this line
128 | else:
129 | start = delimiter.indexIn(text)
130 | # Move past this match
131 | add = delimiter.matchedLength()
132 |
133 | # As long as there's a delimiter match on this line...
134 | while start >= 0:
135 | # Look for the ending delimiter
136 | end = delimiter.indexIn(text, start + add)
137 | # Ending delimiter on this line?
138 | if end >= add:
139 | length = end - start + add + delimiter.matchedLength()
140 | self.setCurrentBlockState(0)
141 | # No; multi-line string
142 | else:
143 | self.setCurrentBlockState(in_state)
144 | length = len(text) - start + add
145 | # Apply formatting
146 | self.setFormat(start, length, style)
147 | # Look for the next match
148 |
149 | start = delimiter.indexIn(text, start + length)
150 |
151 | # Return True if still inside a multi-line string, False otherwise
152 | if self.currentBlockState() == in_state:
153 | return True
154 | else:
155 | return False
156 |
157 |
158 |
--------------------------------------------------------------------------------
/multi_script_editor/widgets/shortcuts.py:
--------------------------------------------------------------------------------
1 | try:
2 | from PySide.QtCore import *
3 | from PySide.QtGui import *
4 | except:
5 | from PySide2.QtCore import *
6 | from PySide2.QtGui import *
7 | from PySide2.QtWidgets import *
8 | import shortcuts_UIs
9 | import os
10 |
11 | class shortcutsClass(QDialog, shortcuts_UIs.Ui_Dialog):
12 | def __init__(self, parent):
13 | super(shortcutsClass, self).__init__(parent)
14 | self.setupUi(self)
15 | self.table.horizontalHeader().setResizeMode(QHeaderView.Stretch)
16 | self.table.setColumnCount(2)
17 | self.table.setHorizontalHeaderLabels(['Action', 'Shortcut'])
18 | self.read()
19 |
20 | def read(self):
21 | src = os.path.join(os.path.dirname(os.path.dirname(__file__)), 'shortcuts.txt')
22 | if os.path.exists(src):
23 | self.label.hide()
24 | lines = open(src).readlines()
25 | for i, l in enumerate(lines):
26 | self.table.insertRow(self.table.rowCount())
27 | description, shortcut = l.split('>')
28 | item = QTableWidgetItem(description)
29 | self.table.setItem(i, 0, item)
30 | item.setFlags(Qt.ItemIsEnabled)
31 | item = QTableWidgetItem(shortcut)
32 | item.setFlags(Qt.ItemIsEnabled)
33 | self.table.setItem(i, 1, item)
34 | else:
35 | self.table.hide()
36 |
--------------------------------------------------------------------------------
/multi_script_editor/widgets/shortcuts.ui:
--------------------------------------------------------------------------------
1 |
2 |
3 | Dialog
4 |
5 |
6 |
7 | 0
8 | 0
9 | 573
10 | 391
11 |
12 |
13 |
14 | Shortcuts list
15 |
16 |
17 | -
18 |
19 |
20 | -
21 |
22 |
23 |
24 | 12
25 | 75
26 | false
27 | true
28 |
29 |
30 |
31 | QFrame::NoFrame
32 |
33 |
34 | Shortcut list hot found!!!
35 |
36 |
37 | Qt::AutoText
38 |
39 |
40 | Qt::AlignCenter
41 |
42 |
43 |
44 |
45 |
46 |
47 |
48 |
49 |
--------------------------------------------------------------------------------
/multi_script_editor/widgets/shortcuts_UIs.py:
--------------------------------------------------------------------------------
1 | # -*- coding: utf-8 -*-
2 |
3 | # Form implementation generated from reading ui file 'D:\Dropbox\Dropbox\pw_prefs\RnD\tools\pw_scriptEditor\multi_script_editor\widgets\shortcuts.ui'
4 | #
5 | # Created: Wed Apr 01 13:33:16 2015
6 | # by: pyside-uic 0.2.15 running on PySide 1.2.2
7 | #
8 | # WARNING! All changes made in this file will be lost!
9 |
10 | try:
11 | from PySide.QtCore import *
12 | from PySide.QtGui import *
13 | except:
14 | from PySide2.QtCore import *
15 | from PySide2.QtGui import *
16 | from PySide2.QtWidgets import *
17 |
18 | class Ui_Dialog(object):
19 | def setupUi(self, Dialog):
20 | Dialog.setObjectName("Dialog")
21 | Dialog.resize(573, 391)
22 | self.verticalLayout = QVBoxLayout(Dialog)
23 | self.verticalLayout.setObjectName("verticalLayout")
24 | self.table = QTableWidget(Dialog)
25 | self.table.setObjectName("table")
26 | self.table.setColumnCount(0)
27 | self.table.setRowCount(0)
28 | self.verticalLayout.addWidget(self.table)
29 | self.label = QLabel(Dialog)
30 | font = QFont()
31 | font.setPointSize(12)
32 | font.setWeight(75)
33 | font.setItalic(False)
34 | font.setBold(True)
35 | self.label.setFont(font)
36 | self.label.setFrameShape(QFrame.NoFrame)
37 | self.label.setTextFormat(Qt.AutoText)
38 | self.label.setAlignment(Qt.AlignCenter)
39 | self.label.setObjectName("label")
40 | self.verticalLayout.addWidget(self.label)
41 |
42 | self.retranslateUi(Dialog)
43 | QMetaObject.connectSlotsByName(Dialog)
44 |
45 | def retranslateUi(self, Dialog):
46 | Dialog.setWindowTitle(QApplication.translate("Dialog", "Shortcuts list", None))
47 | self.label.setText(QApplication.translate("Dialog", "Shortcut list hot found!!!", None))
48 |
49 |
--------------------------------------------------------------------------------
/multi_script_editor/widgets/tabWidget.py:
--------------------------------------------------------------------------------
1 | try:
2 | from PySide.QtCore import *
3 | from PySide.QtGui import *
4 | qt = 1
5 | except:
6 | from PySide2.QtCore import *
7 | from PySide2.QtGui import *
8 | from PySide2.QtWidgets import *
9 | qt = 2
10 | import os
11 | import numBarWidget, inputWidget
12 | reload(inputWidget)
13 | reload(numBarWidget)
14 | from managers import context
15 |
16 |
17 | style = os.path.join(os.path.dirname(os.path.dirname(__file__)), 'style', 'completer.qss')
18 | if not os.path.exists(style):
19 | style=None
20 |
21 |
22 | class tabWidgetClass(QTabWidget):
23 | def __init__(self, parent=None):
24 | super(tabWidgetClass, self).__init__(parent)
25 | # ui
26 | self.setTabsClosable(True)
27 | self.setMovable(True)
28 | self.tabCloseRequested.connect(self.closeTab)
29 | self.tabBar().setContextMenuPolicy(Qt.CustomContextMenu)
30 | self.tabBar().customContextMenuRequested.connect(self.openMenu)
31 | newTabButton = QPushButton(self)
32 | newTabButton.setMaximumWidth(30)
33 | self.setCornerWidget(newTabButton, Qt.TopLeftCorner)
34 | newTabButton.setCursor(Qt.ArrowCursor)
35 | newTabButton.setText('+')
36 | newTabButton.clicked.connect(self.addNewTab)
37 | newTabButton.setToolTip("Add Tab")
38 | self.desk = QApplication.desktop()
39 | # variables
40 | self.p = parent
41 | self.lastSearch = [0, None]
42 |
43 | #connects
44 | self.currentChanged.connect(self.hideAllCompleters)
45 |
46 | def closeTab(self, i):
47 | if self.count() > 1:
48 | if self.getCurrentText(i).strip():
49 | # if qt == 1:
50 | if self.yes_no_question('Close this tab without saving?\n'+self.tabText(i)):
51 | # if QMessageBox.question(self, 'Close Tab',
52 | # 'Close this tab without saving?\n'+self.tabText(i),
53 | # self.buttons) == QMessageBox.Yes:
54 | self.removeTab(i)
55 | # else:
56 | # if QMessageBox.question(self, 'Close Tab',
57 | # 'Close this tab without saving?\n'+self.tabText(i)) == QMessageBox.Yes:
58 | # self.removeTab(i)
59 | else:
60 | self.removeTab(i)
61 |
62 | def openMenu(self):
63 | menu = QMenu(self)
64 | menu.addAction(QAction('Rename Current Tab', self, triggered = self.renameTab))
65 | menu.exec_(QCursor.pos())
66 |
67 | def renameTab(self):
68 | index = self.currentIndex()
69 | text = self.tabText(index)
70 | result = QInputDialog.getText(self, 'New name', 'Enter New Name', text=text)
71 | if result[1]:
72 | self.setTabText(index, result[0])
73 |
74 | def currentTabName(self):
75 | index = self.currentIndex()
76 | text = self.tabText(index)
77 | return text
78 |
79 | def addNewTab(self, name='New Tab', text = None):
80 | cont = container(text, self.p, self.desk)#, self.completer)
81 | cont.edit.saveSignal.connect(self.p.saveSession)
82 | # cont.edit.executeSignal.connect(self.p.executeSelected)
83 | self.addTab(cont, name)
84 | cont.edit.moveCursor(QTextCursor.Start)
85 | self.setCurrentIndex(self.count()-1)
86 | return cont.edit
87 |
88 | def getTabText(self, i):
89 | text = self.widget(i).edit.toPlainText()
90 | return text
91 |
92 | def addToCurrent(self, text):
93 | i = self.currentIndex()
94 | self.widget(i).edit.insertPlainText(text)
95 |
96 | def getCurrentSelectedText(self):
97 | i = self.currentIndex()
98 | text = self.widget(i).edit.getSelection()
99 | return text
100 |
101 | def getCurrentText(self, i=None):
102 | if i is None:
103 | i = self.currentIndex()
104 | text = self.widget(i).edit.toPlainText()
105 | return text
106 |
107 | def setCurrentText(self, text):
108 | i = self.currentIndex()
109 | self.widget(i).edit.setPlainText(text)
110 |
111 |
112 | def hideAllCompleters(self):
113 | for i in range(self.count()):
114 | self.widget(i).edit.completer.hideMe()
115 |
116 | def current(self):
117 | return self.widget(self.currentIndex()).edit
118 |
119 | ############################## editor commands
120 | def undo(self):
121 | self.current().undo()
122 |
123 | def redo(self):
124 | self.current().redo()
125 |
126 | def cut(self):
127 | self.current().cut()
128 |
129 | def copy(self):
130 | self.current().copy()
131 |
132 | def paste(self):
133 | self.current().paste()
134 |
135 | def search(self, text=None):
136 | if text:
137 | if text == self.lastSearch[0]:
138 | self.lastSearch[1] += 1
139 | else:
140 | self.lastSearch = [text, 0]
141 | self.lastSearch[1] = self.current().selectWord(text, self.lastSearch[1])
142 |
143 | def replace(self, parts):
144 | find, rep = parts
145 | self.lastSearch = [find, 0]
146 | self.lastSearch[1] = self.current().selectWord(find, self.lastSearch[1], rep)
147 | self.current().selectWord(find, self.lastSearch[1])
148 |
149 | def replaceAll(self, pat):
150 | find, rep = pat
151 | text = self.current().toPlainText()
152 | text = text.replace(find, rep)
153 | self.current().setPlainText(text)
154 |
155 | def comment(self):
156 | self.current().commentSelected()
157 |
158 | def yes_no_question(self, question):
159 | msg_box = QMessageBox(self)
160 | msg_box.setText(question)
161 | yes_button = msg_box.addButton("Yes", QMessageBox.YesRole)
162 | no_button = msg_box.addButton("No", QMessageBox.NoRole)
163 | msg_box.exec_()
164 | return msg_box.clickedButton() == yes_button
165 |
166 |
167 | class container(QWidget):
168 | def __init__(self, text, parent, desk):
169 | super(container, self).__init__()
170 | hbox = QHBoxLayout(self)
171 | hbox.setSpacing(0)
172 | hbox.setContentsMargins(0,0,0,0)
173 | # input widget
174 | self.edit = inputWidget.inputClass(parent, desk)
175 | self.edit.executeSignal.connect(parent.executeSelected)
176 | if text:
177 | self.edit.addText(text)
178 | # if not context == 'hou':
179 | # line number
180 | # if context == 'hou':
181 | # import hou
182 | # if hou.applicationVersion()[0] > 14:
183 | hbox.addWidget(self.edit)
184 | # return
185 | self.lineNum = numBarWidget.lineNumberBarClass(self.edit, self)
186 | self.edit.verticalScrollBar().valueChanged.connect(lambda :self.lineNum.update())
187 | self.edit.inputSignal.connect(lambda :self.lineNum.update())
188 |
189 | hbox.addWidget(self.lineNum)
190 | hbox.addWidget(self.edit)
191 |
192 |
193 | if __name__ == '__main__':
194 | app = QApplication([])
195 | w = tabWidgetClass()
196 | w.show()
197 | app.exec_()
--------------------------------------------------------------------------------
/multi_script_editor/widgets/themeEditor.py:
--------------------------------------------------------------------------------
1 | try:
2 | from PySide.QtCore import *
3 | from PySide.QtGui import *
4 | qt = 1
5 | except:
6 | from PySide2.QtCore import *
7 | from PySide2.QtGui import *
8 | from PySide2.QtWidgets import *
9 | qt = 2
10 | import themeEditor_UIs as ui
11 | import settingsManager
12 | import os
13 | from .pythonSyntax import design
14 | from .pythonSyntax import syntaxHighLighter
15 | from . import inputWidget
16 | import icons_rcs
17 |
18 |
19 | class themeEditorClass(QDialog, ui.Ui_themeEditor):
20 | def __init__(self, parent = None, desk=None):
21 | super(themeEditorClass, self).__init__(parent)
22 | self.setupUi(self)
23 | self.preview_twd = inputWidget.inputClass(self, desk)
24 | self.preview_ly.addWidget(self.preview_twd)
25 | self.preview_twd.setPlainText(defaultText)
26 | self.splitter.setSizes([200,300])
27 | self.s = settingsManager.scriptEditorClass()
28 | self.colors_lwd.itemDoubleClicked.connect(self.getNewColor)
29 | self.save_btn.clicked.connect(self.saveTheme)
30 | self.del_btn.clicked.connect(self.deleteTheme)
31 | self.themeList_cbb.currentIndexChanged.connect(self.updateColors)
32 | self.apply_btn.clicked.connect(self.apply)
33 | self.apply_btn.setText('Close')
34 | self.textSize_spb.valueChanged.connect(self.updateExample)
35 |
36 | self.fillUI()
37 | self.updateUI()
38 | self.updateColors()
39 | self.preview_twd.completer.updateCompleteList()
40 | self.namespace={}
41 |
42 |
43 | def fillUI(self, restore=None):
44 | if restore is None:
45 | restore = self.themeList_cbb.currentText()
46 | settings = self.s.readSettings()
47 | self.themeList_cbb.clear()
48 | self.themeList_cbb.addItem('default')
49 | if settings.get('colors'):
50 | for x in settings.get('colors'):
51 | self.themeList_cbb.addItem(x)
52 | if not restore:
53 | restore = settings.get('theme')
54 | if restore:
55 | index = self.themeList_cbb.findText(restore)
56 | self.themeList_cbb.setCurrentIndex(index)
57 | self.updateExample()
58 |
59 |
60 | def updateColors(self):
61 | curTheme = self.themeList_cbb.currentText()
62 | if curTheme == 'default':
63 | self.del_btn.setEnabled(0)
64 | colors = design.defaultColors
65 | else:
66 | self.del_btn.setEnabled(1)
67 | settings = self.s.readSettings()
68 | allThemes = settings.get('colors')
69 | if allThemes and curTheme in allThemes:
70 | colors = allThemes.get(curTheme)
71 | for k, v in design.getColors().items():
72 | if not k in colors:
73 | colors[k] = v
74 | else:
75 | colors = design.getColors()
76 |
77 | self.colors_lwd.clear()
78 | for x in sorted(colors.keys()):
79 | if x == 'textsize':
80 | self.textSize_spb.setValue(colors['textsize'])
81 | else:
82 | item = QListWidgetItem(x)
83 | pix = QPixmap(QSize(16,16))
84 | pix.fill(QColor(*colors[x]))
85 | item.setIcon(QIcon(pix))
86 | item.setData(32, colors[x])
87 | self.colors_lwd.addItem(item)
88 | self.updateExample()
89 |
90 | def updateExample(self):
91 | colors = self.getCurrentColors()
92 | self.preview_twd.applyPreviewStyle(colors)
93 |
94 | def getCurrentColors(self):
95 | colors = {}
96 | for i in range(self.colors_lwd.count()):
97 | item = self.colors_lwd.item(i)
98 | colors[item.text()] = item.data(32)
99 | colors['textsize'] = self.textSize_spb.value()
100 | return colors
101 |
102 | def getNewColor(self):
103 | items = self.colors_lwd.selectedItems()
104 | if items:
105 | item = items[0]
106 | init = QColor(*item.data(32))
107 | color = QColorDialog.getColor(init ,self)
108 | if color.isValid():
109 | newColor = (color.red(), color.green(), color.blue())
110 | item.setData(32, newColor)
111 | pix = QPixmap(QSize(16,16))
112 | pix.fill(QColor(*newColor))
113 | item.setIcon(QIcon(pix))
114 | self.updateExample()
115 |
116 | def saveTheme(self):
117 | text = self.themeList_cbb.currentText() or 'NewTheme'
118 | name = QInputDialog.getText(self, 'Theme name', 'Enter Theme name', QLineEdit.Normal, text)
119 | if name[1]:
120 | name = name[0]
121 | if name == 'default':
122 | name = 'Not default'
123 | settings = self.s.readSettings()
124 | if 'colors' in settings:
125 | if name in settings['colors']:
126 | if not self.yes_no_question('Replace exists?'):
127 | return
128 |
129 | colors = self.getCurrentColors()
130 | if 'colors' in settings:
131 | settings['colors'][name] = colors
132 | else:
133 | settings['colors'] = {name: colors}
134 | self.s.writeSettings(settings)
135 | self.fillUI(name)
136 | # self.updateUI()
137 |
138 | def deleteTheme(self):
139 | text = self.themeList_cbb.currentText()
140 | if text:
141 | if self.yes_no_question('Remove current theme?'):
142 | name = self.themeList_cbb.currentText()
143 | settings = self.s.readSettings()
144 | if 'colors' in settings:
145 | if name in settings['colors']:
146 | del settings['colors'][name]
147 | self.s.writeSettings(settings)
148 | self.fillUI(False)
149 | self.updateUI()
150 |
151 | def updateUI(self):
152 | if not self.themeList_cbb.count():
153 | self.apply_btn.setEnabled(0)
154 | else:
155 | self.apply_btn.setEnabled(1)
156 |
157 | def apply(self):
158 | name = self.themeList_cbb.currentText()
159 | if name:
160 | settings = self.s.readSettings()
161 | settings['theme'] = name
162 | self.s.writeSettings(settings)
163 | self.accept()
164 |
165 | def keyPressEvent(self, event):
166 | if event.key() == Qt.Key_Escape:
167 | event.ignore()
168 | else:
169 | super(themeEditorClass, self).keyPressEvent(event)
170 |
171 | def current(self):
172 | pass
173 | # print self.colors_lwd.selectedItems()[0].data(32)
174 |
175 | def yes_no_question(self, question):
176 | msg_box = QMessageBox(self)
177 | msg_box.setText(question)
178 | yes_button = msg_box.addButton("Yes", QMessageBox.YesRole)
179 | no_button = msg_box.addButton("No", QMessageBox.NoRole)
180 | msg_box.exec_()
181 | return msg_box.clickedButton() == yes_button
182 |
183 | defaultText = r'''@decorator(param=1)
184 | def f(x):
185 | """ Syntax Highlighting Demo
186 | @param x Parameter"""
187 | s = ("Test", 2+3, {'a': 'b'}, x) # Comment
188 | print s[0].lower()
189 |
190 | class Foo:
191 | def __init__(self):
192 | string = 'newline'
193 | self.makeSense(whatever=1)
194 |
195 | def makeSense(self, whatever):
196 | self.sense = whatever
197 |
198 | x = len('abc')
199 | print(f.__doc__)
200 | '''
201 |
202 |
203 |
204 |
205 | if __name__ == '__main__':
206 | app = QApplication([])
207 | w = themeEditorClass()
208 | w.show()
209 | qss = os.path.join(os.path.dirname(os.path.dirname(__file__)),'style', 'style.css')
210 | if os.path.exists(qss):
211 | w.setStyleSheet(open(qss).read())
212 | app.exec_()
213 |
214 |
215 |
--------------------------------------------------------------------------------
/multi_script_editor/widgets/themeEditor.ui:
--------------------------------------------------------------------------------
1 |
2 |
3 | themeEditor
4 |
5 |
6 |
7 | 0
8 | 0
9 | 724
10 | 461
11 |
12 |
13 |
14 | Code Theme Editor
15 |
16 |
17 | -
18 |
19 |
20 | Qt::Horizontal
21 |
22 |
23 |
24 |
-
25 |
26 |
27 | -
28 |
29 |
-
30 |
31 |
32 | Completer text size
33 |
34 |
35 |
36 | -
37 |
38 |
39 | 9
40 |
41 |
42 | 25
43 |
44 |
45 | 11
46 |
47 |
48 |
49 | -
50 |
51 |
52 | Qt::Horizontal
53 |
54 |
55 |
56 | 40
57 | 20
58 |
59 |
60 |
61 |
62 |
63 |
64 |
65 |
66 |
67 |
68 | -
69 |
70 |
-
71 |
72 |
73 | -
74 |
75 |
76 |
77 | 60
78 | 16777215
79 |
80 |
81 |
82 | Save
83 |
84 |
85 |
86 | -
87 |
88 |
89 |
90 | 60
91 | 16777215
92 |
93 |
94 |
95 | Del
96 |
97 |
98 |
99 |
100 |
101 | -
102 |
103 |
104 |
105 |
106 |
107 |
108 | -
109 |
110 |
-
111 |
112 |
113 | Qt::Horizontal
114 |
115 |
116 |
117 | 40
118 | 20
119 |
120 |
121 |
122 |
123 | -
124 |
125 |
126 | Save
127 |
128 |
129 |
130 |
131 |
132 |
133 |
134 |
135 |
136 |
137 |
--------------------------------------------------------------------------------
/multi_script_editor/widgets/themeEditor_UI.py:
--------------------------------------------------------------------------------
1 | # -*- coding: utf-8 -*-
2 |
3 | # Form implementation generated from reading ui file 'D:\Dropbox\Dropbox\pw_prefs\RnD\tools\pw_scriptEditor\widgets\themeEditor.ui'
4 | #
5 | # Created: Mon Mar 16 10:29:57 2015
6 | # by: PyQt4 UI code generator 4.11.3
7 | #
8 | # WARNING! All changes made in this file will be lost!
9 |
10 | from PyQt4 import QtCore, QtGui
11 |
12 | try:
13 | _fromUtf8 = QtCore.QString.fromUtf8
14 | except AttributeError:
15 | def _fromUtf8(s):
16 | return s
17 |
18 | try:
19 | _encoding = QtGui.QApplication.UnicodeUTF8
20 | def _translate(context, text, disambig):
21 | return QtGui.QApplication.translate(context, text, disambig, _encoding)
22 | except AttributeError:
23 | def _translate(context, text, disambig):
24 | return QtGui.QApplication.translate(context, text, disambig)
25 |
26 | class Ui_themeEditor(object):
27 | def setupUi(self, themeEditor):
28 | themeEditor.setObjectName(_fromUtf8("themeEditor"))
29 | themeEditor.resize(724, 461)
30 | self.verticalLayout_3 = QtGui.QVBoxLayout(themeEditor)
31 | self.verticalLayout_3.setObjectName(_fromUtf8("verticalLayout_3"))
32 | self.splitter = QtGui.QSplitter(themeEditor)
33 | self.splitter.setOrientation(QtCore.Qt.Horizontal)
34 | self.splitter.setObjectName(_fromUtf8("splitter"))
35 | self.widget = QtGui.QWidget(self.splitter)
36 | self.widget.setObjectName(_fromUtf8("widget"))
37 | self.verticalLayout_2 = QtGui.QVBoxLayout(self.widget)
38 | self.verticalLayout_2.setMargin(0)
39 | self.verticalLayout_2.setObjectName(_fromUtf8("verticalLayout_2"))
40 | self.colors_lwd = QtGui.QListWidget(self.widget)
41 | self.colors_lwd.setObjectName(_fromUtf8("colors_lwd"))
42 | self.verticalLayout_2.addWidget(self.colors_lwd)
43 | self.horizontalLayout_3 = QtGui.QHBoxLayout()
44 | self.horizontalLayout_3.setObjectName(_fromUtf8("horizontalLayout_3"))
45 | self.label = QtGui.QLabel(self.widget)
46 | self.label.setObjectName(_fromUtf8("label"))
47 | self.horizontalLayout_3.addWidget(self.label)
48 | self.textSize_spb = QtGui.QSpinBox(self.widget)
49 | self.textSize_spb.setMinimum(9)
50 | self.textSize_spb.setMaximum(25)
51 | self.textSize_spb.setProperty("value", 11)
52 | self.textSize_spb.setObjectName(_fromUtf8("textSize_spb"))
53 | self.horizontalLayout_3.addWidget(self.textSize_spb)
54 | spacerItem = QtGui.QSpacerItem(40, 20, QtGui.QSizePolicy.Expanding, QtGui.QSizePolicy.Minimum)
55 | self.horizontalLayout_3.addItem(spacerItem)
56 | self.verticalLayout_2.addLayout(self.horizontalLayout_3)
57 | self.layoutWidget = QtGui.QWidget(self.splitter)
58 | self.layoutWidget.setObjectName(_fromUtf8("layoutWidget"))
59 | self.verticalLayout = QtGui.QVBoxLayout(self.layoutWidget)
60 | self.verticalLayout.setMargin(0)
61 | self.verticalLayout.setObjectName(_fromUtf8("verticalLayout"))
62 | self.horizontalLayout = QtGui.QHBoxLayout()
63 | self.horizontalLayout.setObjectName(_fromUtf8("horizontalLayout"))
64 | self.themeList_cbb = QtGui.QComboBox(self.layoutWidget)
65 | self.themeList_cbb.setObjectName(_fromUtf8("themeList_cbb"))
66 | self.horizontalLayout.addWidget(self.themeList_cbb)
67 | self.save_btn = QtGui.QPushButton(self.layoutWidget)
68 | self.save_btn.setMaximumSize(QtCore.QSize(60, 16777215))
69 | self.save_btn.setObjectName(_fromUtf8("save_btn"))
70 | self.horizontalLayout.addWidget(self.save_btn)
71 | self.del_btn = QtGui.QPushButton(self.layoutWidget)
72 | self.del_btn.setMaximumSize(QtCore.QSize(60, 16777215))
73 | self.del_btn.setObjectName(_fromUtf8("del_btn"))
74 | self.horizontalLayout.addWidget(self.del_btn)
75 | self.horizontalLayout.setStretch(0, 1)
76 | self.verticalLayout.addLayout(self.horizontalLayout)
77 | self.preview_ly = QtGui.QVBoxLayout()
78 | self.preview_ly.setObjectName(_fromUtf8("preview_ly"))
79 | self.verticalLayout.addLayout(self.preview_ly)
80 | self.verticalLayout.setStretch(1, 1)
81 | self.verticalLayout_3.addWidget(self.splitter)
82 | self.horizontalLayout_2 = QtGui.QHBoxLayout()
83 | self.horizontalLayout_2.setObjectName(_fromUtf8("horizontalLayout_2"))
84 | spacerItem1 = QtGui.QSpacerItem(40, 20, QtGui.QSizePolicy.Expanding, QtGui.QSizePolicy.Minimum)
85 | self.horizontalLayout_2.addItem(spacerItem1)
86 | self.apply_btn = QtGui.QPushButton(themeEditor)
87 | self.apply_btn.setObjectName(_fromUtf8("apply_btn"))
88 | self.horizontalLayout_2.addWidget(self.apply_btn)
89 | self.verticalLayout_3.addLayout(self.horizontalLayout_2)
90 | self.verticalLayout_3.setStretch(0, 1)
91 |
92 | self.retranslateUi(themeEditor)
93 | QtCore.QMetaObject.connectSlotsByName(themeEditor)
94 |
95 | def retranslateUi(self, themeEditor):
96 | themeEditor.setWindowTitle(_translate("themeEditor", "Code Theme Editor", None))
97 | self.label.setText(_translate("themeEditor", "Completer text size", None))
98 | self.save_btn.setText(_translate("themeEditor", "Save", None))
99 | self.del_btn.setText(_translate("themeEditor", "Del", None))
100 | self.apply_btn.setText(_translate("themeEditor", "Save", None))
101 |
102 |
--------------------------------------------------------------------------------
/multi_script_editor/widgets/themeEditor_UIs.py:
--------------------------------------------------------------------------------
1 | # -*- coding: utf-8 -*-
2 |
3 | # Form implementation generated from reading ui file 'D:\Dropbox\Dropbox\pw_prefs\RnD\tools\pw_scriptEditor\widgets\themeEditor.ui'
4 | #
5 | # Created: Mon Mar 16 10:29:58 2015
6 | # by: pyside-uic 0.2.15 running on PySide 1.2.2
7 | #
8 | # WARNING! All changes made in this file will be lost!
9 |
10 | try:
11 | from PySide.QtCore import *
12 | from PySide.QtGui import *
13 | except:
14 | from PySide2.QtCore import *
15 | from PySide2.QtGui import *
16 | from PySide2.QtWidgets import *
17 |
18 | class Ui_themeEditor(object):
19 | def setupUi(self, themeEditor):
20 | themeEditor.setObjectName("themeEditor")
21 | themeEditor.resize(724, 461)
22 | self.verticalLayout_3 = QVBoxLayout(themeEditor)
23 | self.verticalLayout_3.setObjectName("verticalLayout_3")
24 | self.splitter = QSplitter(themeEditor)
25 | self.splitter.setOrientation(Qt.Horizontal)
26 | self.splitter.setObjectName("splitter")
27 | self.widget = QWidget(self.splitter)
28 | self.widget.setObjectName("widget")
29 | self.verticalLayout_2 = QVBoxLayout(self.widget)
30 | self.verticalLayout_2.setContentsMargins(0, 0, 0, 0)
31 | self.verticalLayout_2.setObjectName("verticalLayout_2")
32 | self.colors_lwd = QListWidget(self.widget)
33 | self.colors_lwd.setObjectName("colors_lwd")
34 | self.verticalLayout_2.addWidget(self.colors_lwd)
35 | self.horizontalLayout_3 = QHBoxLayout()
36 | self.horizontalLayout_3.setObjectName("horizontalLayout_3")
37 | self.label = QLabel(self.widget)
38 | self.label.setObjectName("label")
39 | self.horizontalLayout_3.addWidget(self.label)
40 | self.textSize_spb = QSpinBox(self.widget)
41 | self.textSize_spb.setMinimum(9)
42 | self.textSize_spb.setMaximum(25)
43 | self.textSize_spb.setProperty("value", 11)
44 | self.textSize_spb.setObjectName("textSize_spb")
45 | self.horizontalLayout_3.addWidget(self.textSize_spb)
46 | spacerItem = QSpacerItem(40, 20, QSizePolicy.Expanding, QSizePolicy.Minimum)
47 | self.horizontalLayout_3.addItem(spacerItem)
48 | self.verticalLayout_2.addLayout(self.horizontalLayout_3)
49 | self.layoutWidget = QWidget(self.splitter)
50 | self.layoutWidget.setObjectName("layoutWidget")
51 | self.verticalLayout = QVBoxLayout(self.layoutWidget)
52 | self.verticalLayout.setContentsMargins(0, 0, 0, 0)
53 | self.verticalLayout.setObjectName("verticalLayout")
54 | self.horizontalLayout = QHBoxLayout()
55 | self.horizontalLayout.setObjectName("horizontalLayout")
56 | self.themeList_cbb = QComboBox(self.layoutWidget)
57 | self.themeList_cbb.setObjectName("themeList_cbb")
58 | self.horizontalLayout.addWidget(self.themeList_cbb)
59 | self.save_btn = QPushButton(self.layoutWidget)
60 | self.save_btn.setMaximumSize(QSize(60, 16777215))
61 | self.save_btn.setObjectName("save_btn")
62 | self.horizontalLayout.addWidget(self.save_btn)
63 | self.del_btn = QPushButton(self.layoutWidget)
64 | self.del_btn.setMaximumSize(QSize(60, 16777215))
65 | self.del_btn.setObjectName("del_btn")
66 | self.horizontalLayout.addWidget(self.del_btn)
67 | self.horizontalLayout.setStretch(0, 1)
68 | self.verticalLayout.addLayout(self.horizontalLayout)
69 | self.preview_ly = QVBoxLayout()
70 | self.preview_ly.setObjectName("preview_ly")
71 | self.verticalLayout.addLayout(self.preview_ly)
72 | self.verticalLayout.setStretch(1, 1)
73 | self.verticalLayout_3.addWidget(self.splitter)
74 | self.horizontalLayout_2 = QHBoxLayout()
75 | self.horizontalLayout_2.setObjectName("horizontalLayout_2")
76 | spacerItem1 = QSpacerItem(40, 20, QSizePolicy.Expanding, QSizePolicy.Minimum)
77 | self.horizontalLayout_2.addItem(spacerItem1)
78 | self.apply_btn = QPushButton(themeEditor)
79 | self.apply_btn.setObjectName("apply_btn")
80 | self.horizontalLayout_2.addWidget(self.apply_btn)
81 | self.verticalLayout_3.addLayout(self.horizontalLayout_2)
82 | self.verticalLayout_3.setStretch(0, 1)
83 |
84 | self.retranslateUi(themeEditor)
85 | QMetaObject.connectSlotsByName(themeEditor)
86 |
87 | def retranslateUi(self, themeEditor):
88 | themeEditor.setWindowTitle(QApplication.translate("themeEditor", "Code Theme Editor", None))
89 | self.label.setText(QApplication.translate("themeEditor", "Completer text size", None))
90 | self.save_btn.setText(QApplication.translate("themeEditor", "Save", None))
91 | self.del_btn.setText(QApplication.translate("themeEditor", "Del", None))
92 | self.apply_btn.setText(QApplication.translate("themeEditor", "Save", None))
93 |
94 |
--------------------------------------------------------------------------------
/readme.md:
--------------------------------------------------------------------------------
1 | # Multi Script Editor v2.0.4
2 |
3 | 
4 |
5 | #### [paulwinex.com](http://paulwinex.com/portfolio/multi-script-editor/)
6 |
7 | ## [Tutorials](https://vimeo.com/channels/multiscripteditor)
8 |
9 | This is a cross application, cross platform and open source Python editor, which can be run as a standalone application
10 | or embedded in another application. The main purpose for integration - the ability to script in Python.
11 |
12 | ### Key features
13 |
14 | - Preserve and load of tabs and code in them
15 | - Interactive execute of the selected code by pressing Ctrl + Enter
16 | - Adjust the color theme of the code editor
17 | - Code completion (module [jedi](https://github.com/davidhalter/jedi))
18 | - Context completion for different functions like existing nodes and path in scene
19 |
20 | ### Existing integration modules
21 |
22 | - Houdini 13-17
23 | - Nuke 8-10
24 | - Maya 2014-2017
25 | - 3DsMax 2014-2017
26 |
27 | RV Integration in [nebukadhezer branch](https://github.com/nebukadhezer/multi_script_editor)
28 |
29 | If necessary, you can extend this to make your own integration module.
30 | The main pre condition - Should be used Python2.7.
31 |
32 |
33 | ### Houdini features
34 | - Code completion for all modules and return types (remastered hou library)
35 | - Context completion for functions CreateNode, CreateInputNode and CreateOutputNode with existing houdini node types
36 | - Context completion string for absolute houdini internal path and node parameters. To use this complete start string with "/ or '/
37 | - Drag&Drop Houdini nodes and parameters fills in their path. Use Alt modifier to wrap node or parameter as code like in Houdini Python Shell.
38 | - Reading and writing to PythonSOP code and asset sections
39 |
40 | ### Nuke features
41 | - Code completion for all modules and return types (remastered nuke library)
42 | - Context completion for createNode with existing Nuke node types
43 | - Context completion for function toNode with existing nodes in current script
44 | - Converting selected nodes to code with function nuke.toNode
45 | - Searching and converting nodes to code from clipboard
46 | - Reading and writing from PythonKnobs code
47 |
48 | ### Maya features
49 | - Save code to shelf and accept dropped shelf button code, like default Maya script editor
50 | - Drag&Drop Maya nodes fills in their names. Use Alt modifier to wrap node as code. Import PyMEL before doing this!
51 | - Context completion for function PyNode with existing nodes in current scene
52 | - Context completion for function pm.createNode and cmds.createNode with existing Maya node types
53 |
54 | ### 3DsMax features
55 | - Works for now...
56 |
57 |
58 | # How to install
59 |
60 |
61 | [Standalone](https://github.com/nebukadhezer/multi_script_editor)
62 | [Houdini install](https://github.com/nebukadhezer/multi_script_editor)
63 | [Maya install](https://github.com/nebukadhezer/multi_script_editor)
64 | [Nuke install](https://github.com/nebukadhezer/multi_script_editor)
65 | [3DsMax install](https://github.com/nebukadhezer/multi_script_editor)
66 |
67 | You can use single module installation for each case. Just extract module `multi_script_editor`
68 | to somewhere (no spaces and non ascii characters in path) and add this path to PYTHONPATH before start your app.
69 |
70 | For example:
71 |
72 | Extract to `'/home/username/myscripts/multi_script_editor'`
73 |
74 | Add this path to PYTHONPATH environment variable
75 |
76 | Windows:
77 |
78 | `set PYTHONPATH=c:/users/username/myscripts`
79 |
80 | Linux
81 |
82 | `export PYTHONPATH=/home/username/myscripts`
83 |
84 | Python
85 |
86 | `os.environ['PYTHONPATH'] = '/home/username/myscripts'`
87 |
88 | Do this for each app.
89 |
90 | # License
91 |
92 | This project is licensed under the MIT License
93 |
--------------------------------------------------------------------------------
/readme_3dsmax.md:
--------------------------------------------------------------------------------
1 | ## 3DsMax Install
2 | - extract module `multi_script_editor` to PYTHONPATH
3 |
4 | Example: `{MAX_INSTALL_DIR}/python/multi_script_editor`
5 | - create a menu, toolbar, etc with the following maxscript:
6 |
7 | ```maxscript
8 | python.executefile("path\\to\\multi_script_editor\\managers\\run_3dsmax.py")
9 | ```
10 | or
11 | ```
12 | macroScript MultiScriptEditor
13 | category:"scripts"
14 | toolTip:"MultiScriptEditor"
15 | (
16 | python.Execute "import multi_script_editor"
17 | python.Execute "multi_script_editor.show3DSMax()"
18 | )
19 |
20 | ```
--------------------------------------------------------------------------------
/readme_houdini.md:
--------------------------------------------------------------------------------
1 | ## Houdini install
2 |
3 | ### Houdini 13
4 |
5 | - install PySide to default Python interpreter
6 | - create new tool on shelf
7 | - extract module `multi_script_editor` to PYTHONPATH
8 |
9 | Example: `{HOME}/Documents/Houdini13.X/scripts/python/multi_script_editor`
10 |
11 | ```python
12 | import multi_script_editor
13 | multi_script_editor.showHoudini(ontop=1)
14 | ```
15 |
16 | ### Houdini 14+
17 |
18 | - extract module `multi_script_editor` to PYTHONPATH
19 |
20 | Example: `{HOME}/Documents/Houdini13.X/scripts/python/multi_script_editor`
21 | - create new tool on shelf
22 |
23 | ```python
24 | import multi_script_editor
25 | multi_script_editor.showHoudini(name='Multi Script Editor', replacePyPanel=1, hideTitleMenu=0)
26 | ```
27 |
28 | Also you can use .pypanel file without hqt module
29 | >/managers/houdini/multi_script_editor_16.pypanel (for Houdini 14-16)
30 | >/managers/houdini/multi_script_editor_17.pypanel (for Houdini 17+)
31 |
--------------------------------------------------------------------------------
/readme_maya.md:
--------------------------------------------------------------------------------
1 | ## Maya Install
2 |
3 | - Create shelf button with code
4 | - extract module `multi_script_editor` to PYTHONPATH
5 |
6 | Example: `{HOME}/Documents/maya/scripts/multi_script_editor`
7 |
8 |
9 | ```python
10 | import multi_script_editor
11 | multi_script_editor.showMaya(dock=True)
12 | ```
13 |
--------------------------------------------------------------------------------
/readme_nuke.md:
--------------------------------------------------------------------------------
1 | ## Nuke Install
2 |
3 | - extract module `multi_script_editor` to PYTHONPATH
4 |
5 | Example: `{HOME}/.nuke/multi_script_editor`
6 | - Add next code to menu.py:
7 |
8 | ```python
9 |
10 | import multi_script_editor
11 |
12 | # add to menu
13 | menubar = nuke.menu("Nuke")
14 | toolMenu = menubar.addMenu('&Tools')
15 | toolMenu.addCommand("Multi Script Editor", multi_script_editor.showNuke)
16 |
17 | # create new pane
18 | multi_script_editor.showNuke(panel=True)
19 | ```
20 |
--------------------------------------------------------------------------------
/readme_standalone.md:
--------------------------------------------------------------------------------
1 |
2 | ### Standalone
3 |
4 | #### How to start
5 |
6 | - install Python 2.7
7 | - install PySide
8 | - use run.cmd (Windows) or run.sh (Linux) to start
9 |
--------------------------------------------------------------------------------
/releaseNote.md:
--------------------------------------------------------------------------------
1 | ## v2.1
2 | ### What new
3 |
4 | - update all managers
5 | - update auto completion fou Houdini ?
6 |
7 |
8 | ## v2.0.5
9 | ### Fix
10 | - Support Houdini 16.5
11 | - update Houdini auto completion
12 | - fix connector for Maya 2017
13 | - add hqt.py by default
14 |
15 | ## v2.0.4
16 | ### Fix
17 | - comment/uncomment function
18 | - optimised Nuke library
19 | - fixed line number bar for Houdini
20 | - update 3DsMax connector script
21 |
22 | ## v2.0.3
23 | ### What new
24 |
25 | #### Editor
26 |
27 | - added default text editors functions in Edit menu: undo, redo, copy, paste etc
28 | - added Find and Replace window
29 | - auto indent on new line and new block after ":"
30 | - backspace remove 4 spaces
31 | - duplicate line or selected text by Ctrl+D
32 | - Alt+Q to comment/uncomment line or selected lines
33 |
34 | #### Houdini
35 |
36 | - added app-context menu for Houdini
37 | - read python scripts from node sections or PythonSOP code
38 | - save python script to section or PythonSOP node
39 | - added Houdini path autocomplete. Path contains the absolute path to the Houdini node and its parameters
40 | - finished default Houdini autocomplete modules. Now all functions return correct types
41 | - added automatic wrap dragged nodes and parameters with ALT modifier (H14)
42 | - no need to import main module (hou) to make completer worked
43 |
44 | #### Nuke
45 |
46 | - added app-context menu for Nuke
47 | - read script from PythonButtonKnob
48 | - save script to PythonButtonKnob
49 | - get selected nodes as code "nuke.toNode('')"
50 | - search nodes in clipboard text
51 | - added autocomplete for "toNode" function with existing nodes
52 | - added autocomplete for "allNodes" function with all node types (parameter "filter=")
53 | - finished default Nuke autocomplete modules. Now all functions return correct types
54 | - no need to import main module (import nuke) to make completer worked
55 |
56 | #### Maya
57 |
58 | - added dock control mode for Maya
59 | - added context autocomplete for Maya function "PyNode" with existing nodes
60 | - added automatic wrap dragged nodes for Maya with ALT modifier
61 |
62 | ### Fix
63 | - some corrections output window
64 | - fast complete first line with Tab key or Enter key
65 | - fix menu stylesheet for stand alone
66 | - fixed line numbers widget for Houdini
67 | - now comments and doc strings have different colors in theme
68 | - editor format set to UTF8
69 |
70 | --------------------------------------------
71 |
72 | ## v2.0.1
73 | ### What new
74 | - added context depended completer for 'createNode' function
75 | - Hodudini : createNode, createInputNode and createOutputNode
76 | - Maya: pm.createNode and cmds.createNode
77 | - Nuke: nuke.createNode and nuke.nodes
78 |
79 | ### Fix
80 | - move complete when widget geometry offscreen
81 | - Nuke 8 now worked
82 | - Maya 2015 now worked
83 |
84 | --------------------------------------------
85 |
86 | ## v2.0
87 | ### What new
88 | - added auto complete
89 | - fix auto completion for Houdini
90 | - fix auto completion for Nuke
91 |
92 | ## v1.0
93 |
94 | Not released
--------------------------------------------------------------------------------
/setup.py:
--------------------------------------------------------------------------------
1 | from setuptools import setup, find_packages
2 |
3 | setup(name='multi_script_editor',
4 | version='2.1',
5 | description='Python editor for multiple platforms',
6 | long_description='Python editor for multiple platforms and CG software applications',
7 | classifiers=[
8 | 'Development Status :: Release 2.1',
9 | 'License :: OSI Approved :: MIT License',
10 | 'Programming Language :: Python :: 2.7',
11 | ],
12 | keywords='python ide script_editor',
13 | url='https://github.com/paulwinex/pw_MultiScriptEditor',
14 | author='Paul Winex',
15 | author_email='paulwinex@gmail.com',
16 | license='MIT',
17 | packages=find_packages(),
18 | install_requires=[],
19 | include_package_data=True,
20 | zip_safe=False)
21 |
--------------------------------------------------------------------------------