Fix error in powerline (ascii can't decode)
Get vim plugins directly from git
This commit is contained in:
Christophe Buffenoir 2013-05-03 10:58:55 +02:00
parent dff73a7a8b
commit 8385ed00af
73 changed files with 7702 additions and 7360 deletions

2
.hgignore Normal file
View file

@ -0,0 +1,2 @@
syntax: glob
*.pyc

View file

@ -0,0 +1,5 @@
c = get_config()
c.InteractiveShellApp.extensions = [
'powerline.bindings.ipython.post_0_11'
]

View file

@ -24,10 +24,6 @@
} }
}, },
"shell": { "shell": {
"colorscheme": "default",
"theme": "default"
},
"tmux": {
"colorscheme": "default", "colorscheme": "default",
"theme": "default", "theme": "default",
"segments" : { "segments" : {
@ -37,6 +33,10 @@
} }
} }
}, },
"tmux": {
"colorscheme": "default",
"theme": "default"
},
"vim": { "vim": {
"colorscheme": "default", "colorscheme": "default",
"theme": "default", "theme": "default",

View file

@ -0,0 +1,325 @@
#!/usr/bin/env python
# -*- coding: utf-8 -*-
import os
import subprocess
import sys
import re
import argparse
def warn(msg):
print '[powerline-zsh] ', msg
class Color:
# The following link is a pretty good resources for color values:
# http://www.calmar.ws/vim/color-output.png
PATH_BG = 237 # dark grey
PATH_FG = 250 # light grey
CWD_FG = 254 # nearly-white grey
SEPARATOR_FG = 244
REPO_CLEAN_BG = 148 # a light green color
REPO_CLEAN_FG = 0 # black
REPO_DIRTY_BG = 161 # pink/red
REPO_DIRTY_FG = 15 # white
CMD_PASSED_BG = 236
CMD_PASSED_FG = 15
CMD_FAILED_BG = 161
CMD_FAILED_FG = 15
SVN_CHANGES_BG = 148
SVN_CHANGES_FG = 22 # dark green
VIRTUAL_ENV_BG = 35 # a mid-tone green
VIRTUAL_ENV_FG = 22
class Powerline:
symbols = {
'compatible': {
'separator': u'\u25B6',
'separator_thin': u'\u276F'
},
'patched': {
'separator': u'\u2B80',
'separator_thin': u'\u2B81'
},
'default': {
'separator': '',
'separator_thin': ''
}
}
LSQESCRSQ = '\\[\\e%s\\]'
reset = ' %f%k'
def __init__(self, mode='default'):
self.separator = Powerline.symbols[mode]['separator']
self.separator_thin = Powerline.symbols[mode]['separator_thin']
self.segments = []
def color(self, prefix, code):
if prefix == '38':
return '%%F{%s}' % code
elif prefix == '48':
return '%%K{%s}' % code
def fgcolor(self, code):
return self.color('38', code)
def bgcolor(self, code):
return self.color('48', code)
def append(self, segment):
self.segments.append(segment)
def draw(self):
return (''.join((s[0].draw(self, s[1]) for s in zip(self.segments, self.segments[1:] + [None])))
+ self.reset)
class Segment:
def __init__(self, powerline, content, fg, bg, separator=None, separator_fg=None):
self.powerline = powerline
self.content = content
self.fg = fg
self.bg = bg
self.separator = separator or powerline.separator
self.separator_fg = separator_fg or bg
def draw(self, powerline, next_segment=None):
if next_segment:
separator_bg = powerline.bgcolor(next_segment.bg)
else:
separator_bg = powerline.reset
return ''.join((
powerline.fgcolor(self.fg),
powerline.bgcolor(self.bg),
self.content,
separator_bg,
powerline.fgcolor(self.separator_fg),
self.separator))
def add_cwd_segment(powerline, cwd, maxdepth, cwd_only=False):
#powerline.append(' \\w ', 15, 237)
home = os.getenv('HOME')
cwd = os.getenv('PWD')
if cwd.find(home) == 0:
cwd = cwd.replace(home, '~', 1)
if cwd[0] == '/':
cwd = cwd[1:]
names = cwd.split('/')
if len(names) > maxdepth:
names = names[:2] + [''] + names[2 - maxdepth:]
if not cwd_only:
for n in names[:-1]:
powerline.append(Segment(powerline, ' %s ' % n, Color.PATH_FG, Color.PATH_BG, powerline.separator_thin, Color.SEPARATOR_FG))
powerline.append(Segment(powerline, ' %s ' % names[-1], Color.CWD_FG, Color.PATH_BG))
def get_hg_status():
has_modified_files = False
has_untracked_files = False
has_missing_files = False
output = subprocess.Popen(['hg', 'status'], stdout=subprocess.PIPE).communicate()[0]
for line in output.split('\n'):
if line == '':
continue
elif line[0] == '?':
has_untracked_files = True
elif line[0] == '!':
has_missing_files = True
else:
has_modified_files = True
return has_modified_files, has_untracked_files, has_missing_files
def add_hg_segment(powerline, cwd):
branch = os.popen('hg branch 2> /dev/null').read().rstrip()
if len(branch) == 0:
return False
bg = Color.REPO_CLEAN_BG
fg = Color.REPO_CLEAN_FG
has_modified_files, has_untracked_files, has_missing_files = get_hg_status()
if has_modified_files or has_untracked_files or has_missing_files:
bg = Color.REPO_DIRTY_BG
fg = Color.REPO_DIRTY_FG
extra = ''
if has_untracked_files:
extra += '+'
if has_missing_files:
extra += '!'
branch += (' ' + extra if extra != '' else '')
powerline.append(Segment(powerline, ' %s ' % branch, fg, bg))
return True
def get_git_status():
has_pending_commits = True
has_untracked_files = False
detached_head = False
origin_position = ""
current_branch = ''
output = subprocess.Popen(['git', 'status', '-unormal'], stdout=subprocess.PIPE).communicate()[0]
for line in output.split('\n'):
origin_status = re.findall("Your branch is (ahead|behind).*?(\d+) comm", line)
if len(origin_status) > 0:
origin_position = " %d" % int(origin_status[0][1])
if origin_status[0][0] == 'behind':
origin_position += ''
if origin_status[0][0] == 'ahead':
origin_position += ''
if line.find('nothing to commit (working directory clean)') >= 0:
has_pending_commits = False
if line.find('Untracked files') >= 0:
has_untracked_files = True
if line.find('Not currently on any branch') >= 0:
detached_head = True
if line.find('On branch') >= 0:
current_branch = re.findall('On branch ([^ ]+)', line)[0]
return has_pending_commits, has_untracked_files, origin_position, detached_head, current_branch
def add_git_segment(powerline, cwd):
#cmd = "git branch 2> /dev/null | grep -e '\\*'"
p1 = subprocess.Popen(['git', 'branch'], stdout=subprocess.PIPE, stderr=subprocess.PIPE)
p2 = subprocess.Popen(['grep', '-e', '\\*'], stdin=p1.stdout, stdout=subprocess.PIPE)
output = p2.communicate()[0].strip()
if len(output) == 0:
return False
has_pending_commits, has_untracked_files, origin_position, detached_head, current_branch = get_git_status()
if len(current_branch) > 0:
branch = current_branch
elif detached_head:
branch = subprocess.Popen(['git', 'describe', '--all', '--contains', '--abbrev=4', 'HEAD'], stdout=subprocess.PIPE, stderr=subprocess.PIPE)
branch = '((' + branch.communicate()[0].strip() + '))'
else:
return 'master'
branch += origin_position
if has_untracked_files:
branch += ' +'
bg = Color.REPO_CLEAN_BG
fg = Color.REPO_CLEAN_FG
if has_pending_commits:
bg = Color.REPO_DIRTY_BG
fg = Color.REPO_DIRTY_FG
powerline.append(Segment(powerline, ' %s ' % branch, fg, bg))
return True
def add_svn_segment(powerline, cwd):
if not os.path.exists(os.path.join(cwd, '.svn')):
return
'''svn info:
First column: Says if item was added, deleted, or otherwise changed
' ' no modifications
'A' Added
'C' Conflicted
'D' Deleted
'I' Ignored
'M' Modified
'R' Replaced
'X' an unversioned directory created by an externals definition
'?' item is not under version control
'!' item is missing (removed by non-svn command) or incomplete
'~' versioned item obstructed by some item of a different kind
'''
#TODO: Color segment based on above status codes
try:
#cmd = '"svn status | grep -c "^[ACDIMRX\\!\\~]"'
p1 = subprocess.Popen(['svn', 'status'], stdout=subprocess.PIPE, stderr=subprocess.PIPE)
p2 = subprocess.Popen(['grep', '-c', '^[ACDIMRX\\!\\~]'], stdin=p1.stdout, stdout=subprocess.PIPE)
output = p2.communicate()[0].strip()
if len(output) > 0 and int(output) > 0:
changes = output.strip()
powerline.append(Segment(powerline, ' %s ' % changes, Color.SVN_CHANGES_FG, Color.SVN_CHANGES_BG))
except OSError:
return False
except subprocess.CalledProcessError:
return False
return True
def add_repo_segment(powerline, cwd):
for add_repo_segment in [add_git_segment, add_svn_segment, add_hg_segment]:
try:
if add_repo_segment(p, cwd):
return
except subprocess.CalledProcessError:
pass
except OSError:
pass
def add_virtual_env_segment(powerline, cwd):
env = os.getenv("VIRTUAL_ENV")
if env is None:
return False
env_name = os.path.basename(env)
bg = Color.VIRTUAL_ENV_BG
fg = Color.VIRTUAL_ENV_FG
powerline.append(Segment(powerline, ' %s ' % env_name, fg, bg))
return True
def add_root_indicator(powerline, error):
bg = Color.CMD_PASSED_BG
fg = Color.CMD_PASSED_FG
if int(error) != 0:
fg = Color.CMD_FAILED_FG
bg = Color.CMD_FAILED_BG
powerline.append(Segment(powerline, ' $ ', fg, bg))
def get_valid_cwd():
try:
cwd = os.getcwd()
except:
cwd = os.getenv('PWD') # This is where the OS thinks we are
parts = cwd.split(os.sep)
up = cwd
while parts and not os.path.exists(up):
parts.pop()
up = os.sep.join(parts)
try:
os.chdir(up)
except:
warn("Your current directory is invalid.")
sys.exit(1)
warn("Your current directory is invalid. Lowest valid directory: " + up)
return cwd
if __name__ == '__main__':
arg_parser = argparse.ArgumentParser()
arg_parser.add_argument('--cwd-only', action="store_true")
arg_parser.add_argument('prev_error', nargs='?', default=0)
args = arg_parser.parse_args()
p = Powerline(mode='default')
cwd = get_valid_cwd()
add_virtual_env_segment(p, cwd)
#p.append(Segment(' \\u ', 250, 240))
#p.append(Segment(' \\h ', 250, 238))
add_cwd_segment(p, cwd, 5, args.cwd_only)
add_repo_segment(p, cwd)
add_root_indicator(p, args.prev_error)
sys.stdout.write(p.draw())
# vim: set expandtab:

View file

@ -0,0 +1,282 @@
Metadata-Version: 1.1
Name: jedi
Version: 0.5b5
Summary: An autocompletion tool for Python that can be used for text editors.
Home-page: https://github.com/davidhalter/jedi
Author: David Halter
Author-email: davidhalter88@gmail.com
License: LGPLv3
Description: ########################################
Jedi - an awesome Python auto-completion
########################################
.. image:: https://secure.travis-ci.org/davidhalter/jedi.png?branch=master
:target: http://travis-ci.org/davidhalter/jedi
:alt: Travis-CI build status
**beta testing**
*If you have any comments or feature requests, please tell me! I really want to
know, what you think about Jedi.*
Jedi is an autocompletion tool for Python. It works. With and without syntax
errors. Sometimes it sucks, but that's normal in dynamic languages. But it
sucks less than other tools. It understands almost all of the basic Python
syntax elements including many builtins.
Jedi suports two different goto functions and has support for renaming.
Probably it will also have some support for refactoring in the future.
Jedi uses a very simple interface to connect with IDE's. As an reference, there
is a VIM implementation, which uses Jedi's autocompletion. However, I encourage
you to use Jedi in your IDEs. Start writing plugins! If there are problems with
licensing, just contact me.
At the moment Jedi can be used as a
`VIM-Plugin <http://github.com/davidhalter/jedi-vim>`_. So, if you want to test
Jedi for now, you'll have to use VIM. But there are new plugins emerging:
- `Emacs-Plugin <https://github.com/tkf/emacs-jedi>`_
- `Sublime-Plugin <https://github.com/svaiter/SublimeJEDI>`_ **Under construction**
Here are some pictures:
.. image:: https://github.com/davidhalter/jedi/raw/master/screenshot_complete.png
Completion for almost anything (Ctrl+Space).
.. image:: https://github.com/davidhalter/jedi/raw/master/screenshot_function.png
Display of function/class bodies, docstrings.
.. image:: https://github.com/davidhalter/jedi/raw/master/screenshot_pydoc.png
Pydoc support (with highlighting, Shift+k).
There is also support for goto and renaming.
Get the latest from `github <http://github.com/davidhalter/jedi>`_.
Installation
============
You can either include Jedi as a submodule in your text editor plugin (like
jedi-vim_ does it by default), or you
can install Jedi systemwide.
The preferred way to install the Jedi library into your system is by using
pip_::
sudo pip install jedi
If you want to install the current development version::
sudo pip install -e git://github.com/davidhalter/jedi.git#egg=jedi
Note: This just installs the Jedi library, not the editor plugins. For
information about how to make it work with your editor, refer to the
corresponding documentation.
Support
=======
Jedi supports Python 2.5 up to 3.x. There is just one code base, for both
Python 2 and 3.
Jedi supports many of the widely used Python features:
- builtin functions/classes support
- complex module / function / class structures
- ignores syntax and indentation errors
- multiple returns / yields
- tuple assignments / array indexing / dictionary indexing
- exceptions / with-statement
- \*args / \*\*kwargs
- decorators
- descriptors -> property / staticmethod / classmethod
- closures
- generators (yield statement) / iterators
- support for some magic methods: ``__call__``, ``__iter__``, ``__next__``,
``__get__``, ``__getitem__``, ``__init__``
- support for list.append, set.add, list.extend, etc.
- (nested) list comprehensions / ternary expressions
- relative imports
- ``getattr()`` / ``__getattr__`` / ``__getattribute__``
- function annotations (py3k feature, are ignored right now, but being parsed.
I don't know what to do with them.)
- class decorators (py3k feature, are being ignored too, until I find a use
case, that doesn't work with Jedi)
- simple/usual ``sys.path`` modifications
- ``isinstance`` checks for if/while/assert
- virtualenv support
- infer function arguments with sphinx (and other) docstrings
However, it does not yet support (and probably will in future versions, because
they are on my todo list):
- manipulations of instances outside the instance variables, without using
functions
It does not support (and most probably will not in future versions):
- metaclasses (how could an auto-completion ever support this)
- ``setattr()``, ``__import__()``
- Writing to some dicts: ``globals()``, ``locals()``, ``object.__dict__``
- evaluate ``if`` / ``while``
Caveats
=======
This framework should work for both Python 2/3. However, some things were just
not as *pythonic* in Python 2 as things should be. To keep things simple, some
things have been held back:
- Classes: Always Python 3 like, therefore all classes inherit from ``object``.
- Generators: No ``next`` method. The ``__next__`` method is used instead.
- Exceptions are only looked at in the form of ``Exception as e``, no comma!
Syntax errors and other strange stuff, that is defined differently in the
Python language, may lead to undefined behaviour of the completion. Jedi is
**NOT** a Python compiler, that tries to correct you. It is a tool that wants
to help you. But **YOU** have to know Python, not Jedi.
Importing ``numpy`` can be quite slow sometimes, as well as loading the builtins
the first time. If you want to speed it up, you could write import hooks in
jedi, which preloads this stuff. However, once loaded, this is not a problem
anymore. The same is true for huge modules like ``PySide``, ``wx``, etc.
Security is an important issue for Jedi. Therefore no Python code is executed.
As long as you write pure python, everything is evaluated statically. But: If
you use builtin modules (`c_builtin`) there is no other option than to execute
those modules. However: Execute isn't that critical (as e.g. in pythoncomplete,
which used to execute *every* import!), because it means one import and no
more. So basically the only dangerous thing is using the import itself. If your
`c_builtin` uses some strange initializations, it might be dangerous. But if it
does you're screwed anyways, because eventualy you're going to execute your
code, which executes the import.
A little history
================
The Star Wars Jedi are awesome. My Jedi software tries to imitate a little bit
of the precognition the Jedi have. There is even an awesome `scene
<http://www.youtube.com/watch?v=5BDO3pyavOY>`_ of Monty Python Jedi's :-).
But actually the name hasn't so much to do with Star Wars. It's part of my
second name.
After I explained Guido van Rossum, how some parts of my auto-completion work,
he said (we drank a beer or two):
*Oh, that worries me*
When it's finished, I hope he'll like it :-)
I actually started Jedi, because there were no good solutions available for
VIM. Most auto-completions just didn't work well. The only good solution was
PyCharm. I just like my good old VIM. Rope was never really intended to be an
auto-completion (and also I really hate project folders for my Python scripts).
It's more of a refactoring suite. So I decided to do my own version of a
completion, which would execute non-dangerous code. But I soon realized, that
this wouldn't work. So I built an extremely recursive thing which understands
many of Python's key features.
By the way, I really tried to program it as understandable as possible. But I
think understanding it might need quite some time, because of its recursive
nature.
API-Design for IDEs
===================
If you want to set up an IDE with Jedi, you need to ``import jedi``. You should
have the following objects available:
::
Script(source, line, column, source_path)
``source`` would be the source of your python file/script, separated by new
lines. ``line`` is the current line you want to perform actions on (starting
with line #1 as the first line). ``column`` represents the current
column/indent of the cursor (starting with zero). ``source_path`` should be the
path of your file in the file system.
It returns a script object that contains the relevant information for the other
functions to work without params.
::
Script().complete
Returns ``api.Completion`` objects. Those objects have got
informations about the completions. More than just names.
::
Script().goto
Similar to complete. The returned ``api.Definition`` objects contain
information about the definitions found.
::
Script().get_definition
Mostly used for tests. Like goto, but follows statements and imports and
doesn't break there. You probably don't want to use this function. It's
mostly for testing.
::
Script().related_names
Returns all names that point to the definition of the name under the
cursor. This is also very useful for refactoring (renaming).
::
Script().get_in_function_call
Get the ``Function`` object of the call you're currently in, e.g.: ``abs(``
with the cursor at the end would return the builtin ``abs`` function.
::
NotFoundError
If you use the goto function and no valid identifier (name) is at the
place of the cursor (position). It will raise this exception.
::
set_debug_function
Sets a callback function for ``debug.py``. This function is called with
multiple text objects, in python 3 you could insert ``print``.
::
settings
Access to the ``settings.py`` module. The settings are described there.
.. _jedi-vim: http://github.com/davidhalter/jedi-vim
.. _pip: http://www.pip-installer.org/
Keywords: python completion refactoring vim
Platform: any
Classifier: Development Status :: 4 - Beta
Classifier: Environment :: Plugins
Classifier: Intended Audience :: Developers
Classifier: License :: OSI Approved :: GNU Library or Lesser General Public License (LGPL)
Classifier: Operating System :: OS Independent
Classifier: Programming Language :: Python
Classifier: Topic :: Software Development :: Libraries :: Python Modules
Classifier: Topic :: Text Editors :: Integrated Development Environments (IDE)
Classifier: Topic :: Utilities

View file

@ -0,0 +1,31 @@
AUTHORS.txt
LICENSE.txt
MANIFEST.in
README.rst
setup.cfg
setup.py
jedi/__init__.py
jedi/_compatibility.py
jedi/api.py
jedi/api_classes.py
jedi/builtin.py
jedi/debug.py
jedi/docstrings.py
jedi/dynamic.py
jedi/evaluate.py
jedi/helpers.py
jedi/imports.py
jedi/keywords.py
jedi/modules.py
jedi/parsing.py
jedi/settings.py
jedi.egg-info/PKG-INFO
jedi.egg-info/SOURCES.txt
jedi.egg-info/dependency_links.txt
jedi.egg-info/top_level.txt
jedi/mixin/_functools.pym
jedi/mixin/_sre.pym
jedi/mixin/_weakref.pym
jedi/mixin/builtins.pym
jedi/mixin/datetime.pym
jedi/mixin/posix.pym

View file

@ -0,0 +1,41 @@
../jedi/builtin.py
../jedi/evaluate.py
../jedi/debug.py
../jedi/helpers.py
../jedi/settings.py
../jedi/keywords.py
../jedi/api_classes.py
../jedi/api.py
../jedi/modules.py
../jedi/__init__.py
../jedi/parsing.py
../jedi/docstrings.py
../jedi/_compatibility.py
../jedi/imports.py
../jedi/dynamic.py
../jedi/mixin/builtins.pym
../jedi/mixin/posix.pym
../jedi/mixin/_functools.pym
../jedi/mixin/_weakref.pym
../jedi/mixin/_sre.pym
../jedi/mixin/datetime.pym
../jedi/builtin.pyc
../jedi/evaluate.pyc
../jedi/debug.pyc
../jedi/helpers.pyc
../jedi/settings.pyc
../jedi/keywords.pyc
../jedi/api_classes.pyc
../jedi/api.pyc
../jedi/modules.pyc
../jedi/__init__.pyc
../jedi/parsing.pyc
../jedi/docstrings.pyc
../jedi/_compatibility.pyc
../jedi/imports.pyc
../jedi/dynamic.pyc
./
SOURCES.txt
PKG-INFO
dependency_links.txt
top_level.txt

View file

@ -0,0 +1,16 @@
import sys
# python imports are hell sometimes. Especially the combination of relative
# imports and circular imports... Just avoid it:
sys.path.insert(0, __path__[0])
from .api import Script, NotFoundError, set_debug_function
from . import settings
from . import api
__doc__ = api.__doc__
del api
sys.path.pop(0)

View file

@ -0,0 +1,169 @@
"""
This is a compatibility module, to make it possible to use jedi also with older
python versions.
"""
import sys
is_py3k = sys.hexversion >= 0x03000000
is_py25 = sys.hexversion < 0x02060000
# next was defined in python 2.6, in python 3 obj.next won't be possible
# anymore
try:
next = next
except NameError:
_raiseStopIteration = object()
def next(iterator, default=_raiseStopIteration):
if not hasattr(iterator, 'next'):
raise TypeError("not an iterator")
try:
return iterator.next()
except StopIteration:
if default is _raiseStopIteration:
raise
else:
return default
# ast module was defined in python 2.6
try:
from ast import literal_eval
except ImportError:
literal_eval = eval
# properties in 2.5
try:
property.setter
except AttributeError:
class property(property):
def __init__(self, fget, *args, **kwargs):
self.__doc__ = fget.__doc__
super(property, self).__init__(fget, *args, **kwargs)
def setter(self, fset):
cls_ns = sys._getframe(1).f_locals
for k, v in cls_ns.iteritems():
if v == self:
propname = k
break
cls_ns[propname] = property(self.fget, fset,
self.fdel, self.__doc__)
return cls_ns[propname]
else:
property = property
# unicode function
try:
unicode = unicode
except NameError:
unicode = str
if is_py3k:
utf8 = lambda s: s
else:
utf8 = lambda s: s.decode('utf-8')
utf8.__doc__ = """
Decode a raw string into unicode object. Do nothing in Python 3.
"""
# exec function
if is_py3k:
def exec_function(source, global_map):
exec(source, global_map)
else:
eval(compile("""def exec_function(source, global_map):
exec source in global_map """, 'blub', 'exec'))
# StringIO (Python 2.5 has no io module), so use io only for py3k
try:
from StringIO import StringIO
except ImportError:
from io import StringIO
# hasattr function used because python
if is_py3k:
hasattr = hasattr
else:
def hasattr(obj, name):
try:
getattr(obj, name)
return True
except AttributeError:
return False
class Python3Method(object):
def __init__(self, func):
self.func = func
def __get__(self, obj, objtype):
if obj is None:
return lambda *args, **kwargs: self.func(*args, **kwargs)
else:
return lambda *args, **kwargs: self.func(obj, *args, **kwargs)
try:
# the python3 way
from functools import reduce
except ImportError:
reduce = reduce
def use_metaclass(meta, *bases):
""" Create a class with a metaclass. """
if not bases:
bases = (object,)
return meta("HackClass", bases, {})
try:
from inspect import cleandoc
except ImportError:
# python 2.5 doesn't have this method
import string
def cleandoc(doc):
"""Clean up indentation from docstrings.
Any whitespace that can be uniformly removed from the second line
onwards is removed."""
try:
lines = string.split(string.expandtabs(doc), '\n')
except UnicodeError:
return None
else:
# Find minimum indentation of any non-blank lines after first line.
margin = sys.maxint
for line in lines[1:]:
content = len(string.lstrip(line))
if content:
indent = len(line) - content
margin = min(margin, indent)
# Remove indentation.
if lines:
lines[0] = lines[0].lstrip()
if margin < sys.maxint:
for i in range(1, len(lines)):
lines[i] = lines[i][margin:]
# Remove any trailing or leading blank lines.
while lines and not lines[-1]:
lines.pop()
while lines and not lines[0]:
lines.pop(0)
return string.join(lines, '\n')
if is_py25:
# adds the `itertools.chain.from_iterable` constructor
import itertools
class chain(itertools.chain):
@staticmethod
def from_iterable(iterables):
# chain.from_iterable(['ABC', 'DEF']) --> A B C D E F
for it in iterables:
for element in it:
yield element
itertools.chain = chain
del chain

View file

@ -0,0 +1,457 @@
"""
Jedi is an autocompletion library for Python. It offers additonal
services such as goto / get_definition / pydoc support /
get_in_function_call / related names.
To give you a simple exmple how you can use the jedi library,
here is an exmple for the autocompletion feature:
>>> import jedi
>>> source = '''import json; json.l'''
>>> script = jedi.Script(source, 1, 19, '')
>>> script
<jedi.api.Script at 0x7f6d40f3db90>
>>> completions = script.complete()
>>> completions
[<Completion: load>, <Completion: loads>]
>>> completions[0].complete
'oad'
>>> completions[0].word
'load'
As you see Jedi is pretty simple and allows you to concentrate
writing a good text editor, while still having very good IDE features
for Python.
"""
from __future__ import with_statement
__all__ = ['Script', 'NotFoundError', 'set_debug_function']
import re
import parsing
import dynamic
import imports
import evaluate
import modules
import debug
import settings
import keywords
import helpers
import builtin
import api_classes
from _compatibility import next, unicode
class NotFoundError(Exception):
""" A custom error to avoid catching the wrong exceptions """
pass
class Script(object):
"""
A Script is the base for a completion, goto or whatever call.
:param source: The source code of the current file
:type source: string
:param line: The line to complete in.
:type line: int
:param col: The column to complete in.
:type col: int
:param source_path: The path in the os, the current module is in.
:type source_path: string or None
:param source_encoding: encoding for decoding `source`, when it
is not a `unicode` object.
:type source_encoding: string
"""
def __init__(self, source, line, column, source_path,
source_encoding='utf-8'):
debug.reset_time()
try:
source = unicode(source, source_encoding, 'replace')
# Use 'replace' over 'ignore' to hold code structure.
except TypeError: # `source` is already a unicode object
pass
self.pos = line, column
self.module = modules.ModuleWithCursor(source_path, source=source,
position=self.pos)
self.source_path = source_path
debug.speed('init')
@property
def parser(self):
""" The lazy parser """
return self.module.parser
def complete(self):
"""
An auto completer for python files.
:return: list of Completion objects, sorted by name and __ comes last.
:rtype: list
"""
def follow_imports_if_possible(name):
# TODO remove this, or move to another place (not used)
par = name.parent
if isinstance(par, parsing.Import) and not \
isinstance(self.parser.user_stmt, parsing.Import):
new = imports.ImportPath(par).follow(is_goto=True)
# Only remove the old entry if a new one has been found.
#print par, new, par.parent
if new:
try:
return new
except AttributeError: # .name undefined
pass
return [name]
debug.speed('complete start')
path = self.module.get_path_until_cursor()
path, dot, like = self._get_completion_parts(path)
try:
scopes = list(self._prepare_goto(path, True))
except NotFoundError:
scopes = []
scope_generator = evaluate.get_names_for_scope(
self.parser.user_scope, self.pos)
completions = []
for scope, name_list in scope_generator:
for c in name_list:
completions.append((c, scope))
else:
completions = []
debug.dbg('possible scopes', scopes)
for s in scopes:
if s.isinstance(evaluate.Function):
names = s.get_magic_method_names()
else:
if isinstance(s, imports.ImportPath):
if like == 'import':
l = self.module.get_line(self.pos[0])[:self.pos[1]]
if not l.endswith('import import'):
continue
names = s.get_defined_names(on_import_stmt=True)
else:
names = s.get_defined_names()
for c in names:
completions.append((c, s))
if not dot: # named_params have no dots
call_def = self.get_in_function_call()
if call_def:
if not call_def.module.is_builtin():
for p in call_def.params:
completions.append((p.get_name(), p))
# Do the completion if there is no path before and no import stmt.
if (not scopes or not isinstance(scopes[0], imports.ImportPath)) \
and not path:
# add keywords
bs = builtin.Builtin.scope
completions += ((k, bs) for k in keywords.get_keywords(
all=True))
needs_dot = not dot and path
comps = []
for c, s in set(completions):
n = c.names[-1]
if settings.case_insensitive_completion \
and n.lower().startswith(like.lower()) \
or n.startswith(like):
if not evaluate.filter_private_variable(s,
self.parser.user_stmt, n):
new = api_classes.Completion(c, needs_dot,
len(like), s)
comps.append(new)
debug.speed('complete end')
return sorted(comps, key=lambda x: (x.word.startswith('__'),
x.word.startswith('_'),
x.word.lower()))
def _prepare_goto(self, goto_path, is_like_search=False):
""" Base for complete, goto and get_definition. Basically it returns
the resolved scopes under cursor. """
debug.dbg('start: %s in %s' % (goto_path, self.parser.scope))
user_stmt = self.parser.user_stmt
debug.speed('parsed')
if not user_stmt and len(goto_path.split('\n')) > 1:
# If the user_stmt is not defined and the goto_path is multi line,
# something's strange. Most probably the backwards tokenizer
# matched to much.
return []
if isinstance(user_stmt, parsing.Import):
scopes = [self._get_on_import_stmt(is_like_search)[0]]
else:
# just parse one statement, take it and evaluate it
stmt = self._get_under_cursor_stmt(goto_path)
scopes = evaluate.follow_statement(stmt)
return scopes
def _get_under_cursor_stmt(self, cursor_txt):
r = parsing.PyFuzzyParser(cursor_txt, self.source_path, no_docstr=True)
try:
stmt = r.module.statements[0]
except IndexError:
raise NotFoundError()
stmt.start_pos = self.pos
stmt.parent = self.parser.user_scope
return stmt
def get_definition(self):
"""
Returns the definitions of a the path under the cursor. This is
not a goto function! This follows complicated paths and returns the
end, not the first definition.
The big difference of goto and get_definition is that goto doesn't
follow imports and statements.
Multiple objects may be returned, because Python itself is a dynamic
language, which means depending on an option you can have two different
versions of a function.
:return: list of Definition objects, which are basically scopes.
:rtype: list
"""
def resolve_import_paths(scopes):
for s in scopes.copy():
if isinstance(s, imports.ImportPath):
scopes.remove(s)
scopes.update(resolve_import_paths(set(s.follow())))
return scopes
goto_path = self.module.get_path_under_cursor()
context = self.module.get_context()
if next(context) in ('class', 'def'):
scopes = set([self.module.parser.user_scope])
elif not goto_path:
op = self.module.get_operator_under_cursor()
scopes = set([keywords.get_operator(op, self.pos)] if op else [])
else:
scopes = set(self._prepare_goto(goto_path))
scopes = resolve_import_paths(scopes)
# add keywords
scopes |= keywords.get_keywords(string=goto_path, pos=self.pos)
d = set([api_classes.Definition(s) for s in scopes
if not isinstance(s, imports.ImportPath._GlobalNamespace)])
return sorted(d, key=lambda x: (x.module_path, x.start_pos))
def goto(self):
"""
Returns the first definition found by goto. This means: It doesn't
follow imports and statements.
Multiple objects may be returned, because Python itself is a dynamic
language, which means depending on an option you can have two different
versions of a function.
:return: list of Definition objects, which are basically scopes.
"""
d = [api_classes.Definition(d) for d in set(self._goto()[0])]
return sorted(d, key=lambda x: (x.module_path, x.start_pos))
def _goto(self, add_import_name=False):
"""
Used for goto and related_names.
:param add_import_name: TODO add description
"""
def follow_inexistent_imports(defs):
""" Imports can be generated, e.g. following
`multiprocessing.dummy` generates an import dummy in the
multiprocessing module. The Import doesn't exist -> follow.
"""
definitions = set(defs)
for d in defs:
if isinstance(d.parent, parsing.Import) \
and d.start_pos == (0, 0):
i = imports.ImportPath(d.parent).follow(is_goto=True)
definitions.remove(d)
definitions |= follow_inexistent_imports(i)
return definitions
goto_path = self.module.get_path_under_cursor()
context = self.module.get_context()
if next(context) in ('class', 'def'):
user_scope = self.parser.user_scope
definitions = set([user_scope.name])
search_name = str(user_scope.name)
elif isinstance(self.parser.user_stmt, parsing.Import):
s, name_part = self._get_on_import_stmt()
try:
definitions = [s.follow(is_goto=True)[0]]
except IndexError:
definitions = []
search_name = str(name_part)
if add_import_name:
import_name = self.parser.user_stmt.get_defined_names()
# imports have only one name
if name_part == import_name[0].names[-1]:
definitions.append(import_name[0])
else:
stmt = self._get_under_cursor_stmt(goto_path)
defs, search_name = evaluate.goto(stmt)
definitions = follow_inexistent_imports(defs)
return definitions, search_name
def related_names(self, additional_module_paths=[]):
"""
Returns `dynamic.RelatedName` objects, which contain all names, that
are defined by the same variable, function, class or import.
This function can be used either to show all the usages of a variable
or for renaming purposes.
TODO implement additional_module_paths
"""
user_stmt = self.parser.user_stmt
definitions, search_name = self._goto(add_import_name=True)
if isinstance(user_stmt, parsing.Statement) \
and self.pos < user_stmt.get_assignment_calls().start_pos:
# the search_name might be before `=`
definitions = [v for v in user_stmt.set_vars
if str(v) == search_name]
if not isinstance(user_stmt, parsing.Import):
# import case is looked at with add_import_name option
definitions = dynamic.related_name_add_import_modules(definitions,
search_name)
module = set([d.get_parent_until() for d in definitions])
module.add(self.parser.module)
names = dynamic.related_names(definitions, search_name, module)
for d in set(definitions):
if isinstance(d, parsing.Module):
names.append(api_classes.RelatedName(d, d))
else:
names.append(api_classes.RelatedName(d.names[0], d))
return sorted(set(names), key=lambda x: (x.module_path, x.start_pos),
reverse=True)
def get_in_function_call(self):
"""
Return the function, that the cursor is in, e.g.:
>>> isinstance(| # | <-- cursor is here
This would return the `isinstance` function. In contrary:
>>> isinstance()| # | <-- cursor is here
This would return `None`.
"""
def check_user_stmt(user_stmt):
if user_stmt is None \
or not isinstance(user_stmt, parsing.Statement):
return None, 0
ass = helpers.fast_parent_copy(user_stmt.get_assignment_calls())
call, index, stop = helpers.scan_array_for_pos(ass, self.pos)
return call, index
def check_cache():
""" Do the parsing with a part parser, therefore reduce ressource
costs.
TODO this is not working with multi-line docstrings, improve.
"""
if self.source_path is None:
return None, 0
try:
timestamp, parser = builtin.CachedModule.cache[
self.source_path]
except KeyError:
return None, 0
part_parser = self.module.get_part_parser()
user_stmt = part_parser.user_stmt
call, index = check_user_stmt(user_stmt)
if call:
old_stmt = parser.module.get_statement_for_position(self.pos)
if old_stmt is None:
return None, 0
old_call, old_index = check_user_stmt(old_stmt)
if old_call:
# compare repr because that should definitely be the same.
# Otherwise the whole thing is out of sync.
if repr(old_call) == repr(call):
# return the index of the part_parser
return old_call, index
return None, 0
else:
raise NotFoundError()
debug.speed('func_call start')
try:
call, index = check_cache()
except NotFoundError:
return None
debug.speed('func_call parsed')
if call is None:
# This is a backup, if the above is not successful.
user_stmt = self.parser.user_stmt
call, index = check_user_stmt(user_stmt)
if call is None:
return None
debug.speed('func_call user_stmt')
with helpers.scale_speed_settings(settings.scale_get_in_function_call):
origins = evaluate.follow_call(call)
debug.speed('func_call followed')
if len(origins) == 0:
return None
# just take entry zero, because we need just one.
executable = origins[0]
return api_classes.CallDef(executable, index, call)
def _get_on_import_stmt(self, is_like_search=False):
""" Resolve the user statement, if it is an import. Only resolve the
parts until the user position. """
user_stmt = self.parser.user_stmt
import_names = user_stmt.get_all_import_names()
kill_count = -1
cur_name_part = None
for i in import_names:
if user_stmt.alias == i:
continue
for name_part in i.names:
if name_part.end_pos >= self.pos:
if not cur_name_part:
cur_name_part = name_part
kill_count += 1
i = imports.ImportPath(user_stmt, is_like_search,
kill_count=kill_count, direct_resolve=True)
return i, cur_name_part
def _get_completion_parts(self, path):
"""
Returns the parts for the completion
:return: tuple - (path, dot, like)
"""
match = re.match(r'^(.*?)(\.|)(\w?[\w\d]*)$', path, flags=re.S)
return match.groups()
def __del__(self):
evaluate.clear_caches()
def set_debug_function(func_cb=debug.print_to_stdout, warnings=True,
notices=True, speed=True):
"""
You can define a callback debug function to get all the debug messages.
:param func_cb: The callback function for debug messages, with n params.
"""
debug.debug_function = func_cb
debug.enable_warning = warnings
debug.enable_notice = notices
debug.enable_speed = speed

View file

@ -0,0 +1,311 @@
""" The classes returned by the api """
import re
import os
import settings
import evaluate
import imports
import parsing
import keywords
class BaseDefinition(object):
_mapping = {'posixpath': 'os.path',
'riscospath': 'os.path',
'ntpath': 'os.path',
'os2emxpath': 'os.path',
'macpath': 'os.path',
'genericpath': 'os.path',
'_io': 'io',
'__builtin__': '',
'builtins': '',
}
_tuple_mapping = dict((tuple(k.split('.')), v) for (k, v) in {
'argparse._ActionsContainer': 'argparse.ArgumentParser',
'_sre.SRE_Match': 're.MatchObject',
'_sre.SRE_Pattern': 're.RegexObject',
}.items())
def __init__(self, definition, start_pos):
self.start_pos = start_pos
self.definition = definition
self.is_keyword = isinstance(definition, keywords.Keyword)
# generate a path to the definition
self.module_path = str(definition.get_parent_until().path)
@property
def type(self):
# generate the type
stripped = self.definition
if isinstance(self.definition, evaluate.InstanceElement):
stripped = self.definition.var
return type(stripped).__name__
@property
def path(self):
path = []
if not isinstance(self.definition, keywords.Keyword):
par = self.definition
while par is not None:
try:
path.insert(0, par.name)
except AttributeError:
pass
par = par.parent
return path
@property
def module_name(self):
path = self.module_path
sep = os.path.sep
p = re.sub(r'^.*?([\w\d]+)(%s__init__)?.py$' % sep, r'\1', path)
return p
def in_builtin_module(self):
return not self.module_path.endswith('.py')
@property
def line_nr(self):
return self.start_pos[0]
@property
def column(self):
return self.start_pos[1]
@property
def doc(self):
""" Return a document string for this completion object. """
try:
return self.definition.doc
except AttributeError:
return self.raw_doc
@property
def raw_doc(self):
""" Returns the raw docstring `__doc__` for any object """
try:
return str(self.definition.docstr)
except AttributeError:
return ''
@property
def description(self):
return str(self.definition)
@property
def full_name(self):
"""
Returns the path to a certain class/function, see #61.
"""
path = [str(p) for p in self.path]
# TODO add further checks, the mapping should only occur on stdlib.
try:
path[0] = self._mapping[path[0]]
except KeyError:
pass
for key, repl in self._tuple_mapping.items():
if tuple(path[:len(key)]) == key:
path = [repl] + path[len(key):]
return '.'.join(path if path[0] else path[1:])
def __repr__(self):
return "<%s %s>" % (type(self).__name__, self.description)
class Completion(BaseDefinition):
""" `Completion` objects are returned from `Script.complete`. Providing
some useful functions for IDE's. """
def __init__(self, name, needs_dot, like_name_length, base):
super(Completion, self).__init__(name.parent, name.start_pos)
self.name = name
self.needs_dot = needs_dot
self.like_name_length = like_name_length
self.base = base
self._followed_definitions = None
@property
def complete(self):
""" Delievers the rest of the word, e.g. completing `isinstance`
>>> isinstan
would return the string 'ce'. It also adds additional stuff, depending
on your `settings.py`
"""
dot = '.' if self.needs_dot else ''
append = ''
if settings.add_bracket_after_function \
and self.type == 'Function':
append = '('
if settings.add_dot_after_module:
if isinstance(self.base, parsing.Module):
append += '.'
if isinstance(self.base, parsing.Param):
append += '='
return dot + self.name.names[-1][self.like_name_length:] + append
@property
def word(self):
""" In contrary to `complete` returns the whole word, e.g.
>>> isinstan
would return 'isinstance'.
"""
return str(self.name.names[-1])
@property
def description(self):
""" Provides a description of the completion object
TODO return value is just __repr__ of some objects, improve! """
parent = self.name.parent
if parent is None:
return ''
t = self.type
if t == 'Statement' or t == 'Import':
desc = self.definition.get_code(False)
else:
desc = '.'.join(str(p) for p in self.path)
line_nr = '' if self.in_builtin_module else '@%s' % self.line_nr
return '%s: %s%s' % (t, desc, line_nr)
def follow_definition(self):
""" Returns you the original definitions. I strongly recommend not
using it for your completions, because it might slow down Jedi. If you
want to read only a few objects (<=20). I think it might be useful,
especially to get the original docstrings.
The basic problem of this function is that it follows all results. This
means with 1000 completions (e.g. numpy), it's just PITA slow.
"""
if self._followed_definitions is None:
if self.definition.isinstance(parsing.Statement):
defs = evaluate.follow_statement(self.definition)
elif self.definition.isinstance(parsing.Import):
defs = imports.strip_imports([self.definition])
else:
return [self]
self._followed_definitions = \
[BaseDefinition(d, d.start_pos) for d in defs]
evaluate.clear_caches()
return self._followed_definitions
def __repr__(self):
return '<%s: %s>' % (type(self).__name__, self.name)
class Definition(BaseDefinition):
""" These are the objects returned by either `Script.goto` or
`Script.get_definition`. """
def __init__(self, definition):
super(Definition, self).__init__(definition, definition.start_pos)
@property
def description(self):
""" A description of the Definition object, which is heavily used in
testing. e.g. for `isinstance` it returns 'def isinstance' """
d = self.definition
if isinstance(d, evaluate.InstanceElement):
d = d.var
if isinstance(d, evaluate.parsing.Name):
d = d.parent
if isinstance(d, evaluate.Array):
d = 'class ' + d.type
elif isinstance(d, (parsing.Class, evaluate.Class, evaluate.Instance)):
d = 'class ' + str(d.name)
elif isinstance(d, (evaluate.Function, evaluate.parsing.Function)):
d = 'def ' + str(d.name)
elif isinstance(d, evaluate.parsing.Module):
# only show module name
d = 'module %s' % self.module_name
elif self.is_keyword:
d = 'keyword %s' % d.name
else:
d = d.get_code().replace('\n', '')
return d
@property
def desc_with_module(self):
""" In addition to the Definition, it also returns the module. Don't
use it yet, its behaviour may change. If you really need it, talk to me
TODO add full path. This function is should return a
module.class.function path. """
if self.module_path.endswith('.py') \
and not isinstance(self.definition, parsing.Module):
position = '@%s' % (self.line_nr)
else:
# is a builtin or module
position = ''
return "%s:%s%s" % (self.module_name, self.description, position)
class RelatedName(BaseDefinition):
def __init__(self, name_part, scope):
super(RelatedName, self).__init__(scope, name_part.start_pos)
self.name_part = name_part
self.text = str(name_part)
self.end_pos = name_part.end_pos
@property
def description(self):
return "%s@%s,%s" % (self.text, self.start_pos[0], self.start_pos[1])
def __eq__(self, other):
return self.start_pos == other.start_pos \
and self.module_path == other.module_path
def __hash__(self):
return hash((self.start_pos, self.module_path))
class CallDef(object):
""" `CallDef` objects is the return value of `Script.get_in_function_call`.
It knows what functions you are currently in. e.g. `isinstance(` would
return the `isinstance` function. without `(` it would return nothing."""
def __init__(self, executable, index, call):
self.executable = executable
self.index = index
self.call = call
@property
def params(self):
if self.executable.isinstance(evaluate.Function):
if isinstance(self.executable, evaluate.InstanceElement):
return self.executable.params[1:]
return self.executable.params
else:
try:
sub = self.executable.get_subscope_by_name('__init__')
return sub.params[1:] # ignore self
except KeyError:
return []
@property
def bracket_start(self):
""" The indent of the bracket that is responsible for the last function
call. """
c = self.call
while c.next is not None:
c = c.next
return c.name.end_pos
@property
def call_name(self):
""" The name (e.g. 'isinstance') as a string. """
return str(self.executable.name)
@property
def module(self):
return self.executable.get_parent_until()
def __repr__(self):
return '<%s: %s index %s>' % (type(self).__name__, self.executable,
self.index)

View file

@ -0,0 +1,472 @@
from __future__ import with_statement
from _compatibility import exec_function, is_py3k
import re
import sys
import os
if is_py3k:
import io
import types
import inspect
import debug
import parsing
import imports
import evaluate
def get_sys_path():
def check_virtual_env(sys_path):
""" Add virtualenv's site-packages to the `sys.path`."""
venv = os.getenv('VIRTUAL_ENV')
if not venv:
return
venv = os.path.abspath(venv)
p = os.path.join(
venv, 'lib', 'python%d.%d' % sys.version_info[:2], 'site-packages')
sys_path.insert(0, p)
p = sys.path[1:]
check_virtual_env(p)
return p
class CachedModule(object):
"""
The base type for all modules, which is not to be confused with
`parsing.Module`. Caching happens here.
"""
cache = {}
def __init__(self, path=None, name=None):
self.path = path and os.path.abspath(path)
self.name = name
self._parser = None
@property
def parser(self):
""" get the parser lazy """
if not self._parser:
try:
timestamp, parser = self.cache[self.path or self.name]
if not self.path or os.path.getmtime(self.path) <= timestamp:
self._parser = parser
else:
# In case there is already a module cached and this module
# has to be reparsed, we also need to invalidate the import
# caches.
imports.invalidate_star_import_cache(parser.module)
raise KeyError()
except KeyError:
self._load_module()
return self._parser
def _get_source(self):
raise NotImplementedError()
def _load_module(self):
source = self._get_source()
self._parser = parsing.PyFuzzyParser(source, self.path or self.name)
p_time = None if not self.path else os.path.getmtime(self.path)
if self.path or self.name:
self.cache[self.path or self.name] = p_time, self._parser
class Parser(CachedModule):
"""
This module is a parser for all builtin modules, which are programmed in
C/C++. It should also work on third party modules.
It can be instantiated with either a path or a name of the module. The path
is important for third party modules.
:param name: The name of the module.
:param path: The path of the module.
:param sys_path: The sys.path, which is can be customizable.
"""
map_types = {
'floating point number': '0.0',
'string': '""',
'str': '""',
'character': '"a"',
'integer': '0',
'int': '0',
'dictionary': '{}',
'list': '[]',
'file object': 'file("")',
# TODO things like dbg: ('not working', 'tuple of integers')
}
if is_py3k:
map_types['file object'] = 'import io; return io.TextIOWrapper()'
module_cache = {}
def __init__(self, path=None, name=None, sys_path=None):
if sys_path is None:
sys_path = get_sys_path()
if not name:
name = os.path.basename(path)
name = name.rpartition('.')[0] # cut file type (normally .so)
super(Parser, self).__init__(path=path, name=name)
self.sys_path = list(sys_path)
self._module = None
@property
def module(self):
def load_module(name, path):
if path:
self.sys_path.insert(0, path)
temp, sys.path = sys.path, self.sys_path
content = {}
try:
exec_function('import %s as module' % name, content)
self._module = content['module']
except AttributeError:
# use sys.modules, because you cannot access some modules
# directly. -> #59
self._module = sys.modules[name]
sys.path = temp
if path:
self.sys_path.pop(0)
# module might already be defined
if not self._module:
path = self.path
name = self.name
if self.path:
dot_path = []
p = self.path
# search for the builtin with the correct path
while p and p not in sys.path:
p, sep, mod = p.rpartition(os.path.sep)
dot_path.append(mod.partition('.')[0])
if p:
name = ".".join(reversed(dot_path))
path = p
else:
path = os.path.dirname(self.path)
load_module(name, path)
return self._module
def _get_source(self):
""" Override this abstract method """
return _generate_code(self.module, self._load_mixins())
def _load_mixins(self):
"""
Load functions that are mixed in to the standard library.
E.g. builtins are written in C (binaries), but my autocompletion only
understands Python code. By mixing in Python code, the autocompletion
should work much better for builtins.
"""
regex = r'^(def|class)\s+([\w\d]+)'
def process_code(code, depth=0):
funcs = {}
matches = list(re.finditer(regex, code, re.MULTILINE))
positions = [m.start() for m in matches]
for i, pos in enumerate(positions):
try:
code_block = code[pos:positions[i + 1]]
except IndexError:
code_block = code[pos:len(code)]
structure_name = matches[i].group(1)
name = matches[i].group(2)
if structure_name == 'def':
funcs[name] = code_block
elif structure_name == 'class':
if depth > 0:
raise NotImplementedError()
# remove class line
c = re.sub(r'^[^\n]+', '', code_block)
# remove whitespace
c = re.compile(r'^[ ]{4}', re.MULTILINE).sub('', c)
funcs[name] = process_code(c)
else:
raise NotImplementedError()
return funcs
try:
name = self.name
if name == '__builtin__' and not is_py3k:
name = 'builtins'
path = os.path.dirname(os.path.abspath(__file__))
with open(os.path.sep.join([path, 'mixin', name]) + '.pym') as f:
s = f.read()
except IOError:
return {}
else:
mixin_dct = process_code(s)
if is_py3k and self.name == Builtin.name:
# in the case of Py3k xrange is now range
mixin_dct['range'] = mixin_dct['xrange']
return mixin_dct
def _generate_code(scope, mixin_funcs={}, depth=0):
"""
Generate a string, which uses python syntax as an input to the
PyFuzzyParser.
"""
def get_doc(obj, indent=False):
doc = inspect.getdoc(obj)
if doc:
doc = ('r"""\n%s\n"""\n' % doc)
if indent:
doc = parsing.indent_block(doc)
return doc
return ''
def is_in_base_classes(cls, name, comparison):
""" Base classes may contain the exact same object """
if name in mixin_funcs:
return False
try:
mro = cls.mro()
except TypeError:
# this happens, if cls == type
return False
for base in mro[1:]:
try:
attr = getattr(base, name)
except AttributeError:
continue
if attr == comparison:
return True
return False
def get_scope_objects(names):
"""
Looks for the names defined with dir() in an objects and divides
them into different object types.
"""
classes = {}
funcs = {}
stmts = {}
members = {}
for n in names:
try:
# this has a builtin_function_or_method
exe = getattr(scope, n)
except AttributeError:
# happens e.g. in properties of
# PyQt4.QtGui.QStyleOptionComboBox.currentText
# -> just set it to None
members[n] = None
else:
if inspect.isclass(scope):
if is_in_base_classes(scope, n, exe):
continue
if inspect.isbuiltin(exe) or inspect.ismethod(exe) \
or inspect.ismethoddescriptor(exe):
funcs[n] = exe
elif inspect.isclass(exe):
classes[n] = exe
elif inspect.ismemberdescriptor(exe):
members[n] = exe
else:
stmts[n] = exe
return classes, funcs, stmts, members
code = ''
if inspect.ismodule(scope): # generate comment where the code's from.
try:
path = scope.__file__
except AttributeError:
path = '?'
code += '# Generated module %s from %s\n' % (scope.__name__, path)
code += get_doc(scope)
names = set(dir(scope)) - set(['__file__', '__name__', '__doc__',
'__path__', '__package__']) \
| set(['mro'])
classes, funcs, stmts, members = get_scope_objects(names)
# classes
for name, cl in classes.items():
bases = (c.__name__ for c in cl.__bases__)
code += 'class %s(%s):\n' % (name, ','.join(bases))
if depth == 0:
try:
mixin = mixin_funcs[name]
except KeyError:
mixin = {}
cl_code = _generate_code(cl, mixin, depth + 1)
code += parsing.indent_block(cl_code)
code += '\n'
# functions
for name, func in funcs.items():
params, ret = parse_function_doc(func)
if depth > 0:
params = 'self, ' + params
doc_str = get_doc(func, indent=True)
try:
mixin = mixin_funcs[name]
except KeyError:
# normal code generation
code += 'def %s(%s):\n' % (name, params)
code += doc_str
code += parsing.indent_block('%s\n\n' % ret)
else:
# generation of code with mixins
# the parser only supports basic functions with a newline after
# the double dots
# find doc_str place
pos = re.search(r'\):\s*\n', mixin).end()
if pos is None:
raise Exception("Builtin function not parsed correctly")
code += mixin[:pos] + doc_str + mixin[pos:]
# class members (functions) properties?
for name, func in members.items():
# recursion problem in properties TODO remove
if name in ['fget', 'fset', 'fdel']:
continue
ret = 'pass'
code += '@property\ndef %s(self):\n' % (name)
code += parsing.indent_block(get_doc(func) + '%s\n\n' % ret)
# variables
for name, value in stmts.items():
if is_py3k:
file_type = io.TextIOWrapper
else:
file_type = types.FileType
if type(value) == file_type:
value = 'open()'
elif name == 'None':
value = ''
elif type(value).__name__ in ['int', 'bool', 'float',
'dict', 'list', 'tuple']:
value = repr(value)
else:
# get the type, if the type is not simple.
mod = type(value).__module__
value = type(value).__name__ + '()'
if mod != '__builtin__':
value = '%s.%s' % (mod, value)
code += '%s = %s\n' % (name, value)
if depth == 0:
#with open('writeout.py', 'w') as f:
# f.write(code)
#import sys
#sys.stdout.write(code)
#exit()
pass
return code
def parse_function_doc(func):
"""
Takes a function and returns the params and return value as a tuple.
This is nothing more than a docstring parser.
"""
# TODO: things like utime(path, (atime, mtime)) and a(b [, b]) -> None
doc = inspect.getdoc(func)
# get full string, parse round parentheses: def func(a, (b,c))
try:
count = 0
debug.dbg(func, func.__name__, doc)
start = doc.index('(')
for i, s in enumerate(doc[start:]):
if s == '(':
count += 1
elif s == ')':
count -= 1
if count == 0:
end = start + i
break
param_str = doc[start + 1:end]
# remove square brackets, that show an optional param ( = None)
def change_options(m):
args = m.group(1).split(',')
for i, a in enumerate(args):
if a and '=' not in a:
args[i] += '=None'
return ','.join(args)
while True:
param_str, changes = re.subn(r' ?\[([^\[\]]+)\]',
change_options, param_str)
if changes == 0:
break
except (ValueError, AttributeError):
debug.dbg('no brackets found - no param')
end = 0
param_str = ''
param_str = param_str.replace('-', '_') # see: isinstance.__doc__
if doc is not None:
r = re.search('-[>-]* ', doc[end:end + 7])
if doc is None or r is None:
ret = 'pass'
else:
index = end + r.end()
# get result type, which can contain newlines
pattern = re.compile(r'(,\n|[^\n-])+')
ret_str = pattern.match(doc, index).group(0).strip()
# New object -> object()
ret_str = re.sub(r'[nN]ew (.*)', r'\1()', ret_str)
ret = Parser.map_types.get(ret_str, ret_str)
if ret == ret_str and ret not in ['None', 'object', 'tuple', 'set']:
debug.dbg('not working', ret_str)
if ret != 'pass':
ret = ('return ' if 'return' not in ret else '') + ret
return param_str, ret
class Builtin(object):
""" The builtin scope / module """
# Python 3 compatibility
if is_py3k:
name = 'builtins'
else:
name = '__builtin__'
_builtin = None
@property
def builtin(self):
if self._builtin is None:
self._builtin = Parser(name=self.name)
return self._builtin
@property
def scope(self):
return self.builtin.parser.module
@property
def magic_function_scope(self):
try:
return self._magic_function_scope
except AttributeError:
# depth = 1 because this is not a module
class Container(object):
FunctionType = types.FunctionType
source = _generate_code(Container, depth=0)
parser = parsing.PyFuzzyParser(source, None)
module = parser.module
module.parent = self.scope
typ = evaluate.follow_path(iter(['FunctionType']), module, module)
s = self._magic_function_scope = typ.pop()
return s
Builtin = Builtin()

View file

@ -0,0 +1,64 @@
import inspect
import time
try:
# Use colorama for nicer console output.
from colorama import Fore, init
init()
except ImportError:
class Fore(object):
RED = ''
GREEN = ''
YELLOW = ''
RESET = ''
NOTICE = object()
WARNING = object()
SPEED = object()
enable_speed = False
enable_warning = False
enable_notice = False
# callback, interface: level, str
debug_function = None
ignored_modules = ['parsing', 'builtin', 'jedi.builtin', 'jedi.parsing']
def reset_time():
global start_time
start_time = time.time()
def dbg(*args):
""" Looks at the stack, to see if a debug message should be printed. """
if debug_function and enable_notice:
frm = inspect.stack()[1]
mod = inspect.getmodule(frm[0])
if not (mod.__name__ in ignored_modules):
debug_function(NOTICE, 'dbg: ' + ', '.join(str(a) for a in args))
def warning(*args):
if debug_function and enable_warning:
debug_function(WARNING, 'warning: ' + ', '.join(str(a) for a in args))
def speed(name):
if debug_function and enable_speed:
now = time.time()
debug_function(SPEED, 'speed: ' + '%s %s' % (name, now - start_time))
def print_to_stdout(level, str_out):
""" The default debug function """
if level == NOTICE:
col = Fore.GREEN
elif level == WARNING:
col = Fore.RED
else:
col = Fore.YELLOW
print(col + str_out + Fore.RESET)
#debug_function = print_to_stdout

View file

@ -0,0 +1,34 @@
""" Processing of docstrings, which means parsing for types. """
import re
import evaluate
#@evaluate.memoize_default() # TODO add
def follow_param(param):
func = param.parent_function
#print func, param, param.parent_function
param_str = search_param_in_docstr(func.docstr, str(param.get_name()))
if param_str is not None:
scope = func.get_parent_until()
return evaluate.get_scopes_for_name(scope, param_str,
search_global=True)
return []
def search_param_in_docstr(docstr, param_str):
lines = docstr.split('\n')
# look at #40 to see definitions of those params
sphinx_comp = ':type %s:' % param_str
googley_comp = re.compile('\s*%s\s+\(([^()]+)\)' % re.escape(param_str))
for l in lines:
if l.startswith(sphinx_comp):
return l.replace(sphinx_comp, '', 1).strip()
r = re.match(googley_comp, l)
if r is not None:
return r.group(1)
return None

View file

@ -0,0 +1,507 @@
"""
For dynamic completion.
Sorry to everyone who is reading this code. Especially the array parts are
really cryptic and not understandable. It's just a hack, that turned out to be
working quite good.
"""
from __future__ import with_statement
import os
import parsing
import modules
import evaluate
import helpers
import settings
import debug
import builtin
import imports
import api_classes
# This is something like the sys.path, but only for searching params. It means
# that this is the order in which Jedi searches params.
search_param_modules = ['.']
search_param_cache = {}
def get_directory_modules_for_name(mods, name):
"""
Search a name in the directories of modules.
"""
def check_python_file(path):
try:
return builtin.CachedModule.cache[path][1].module
except KeyError:
try:
return check_fs(path)
except IOError:
return None
def check_fs(path):
with open(path) as f:
source = f.read()
if name in source:
return modules.Module(path, source).parser.module
# skip non python modules
mods = set(m for m in mods if m.path.endswith('.py'))
mod_paths = set()
for m in mods:
mod_paths.add(m.path)
yield m
if settings.dynamic_params_for_other_modules:
paths = set(settings.additional_dynamic_modules)
for p in mod_paths:
d = os.path.dirname(p)
for entry in os.listdir(d):
if entry not in mod_paths:
if entry.endswith('.py'):
paths.add(d + os.path.sep + entry)
for p in paths:
c = check_python_file(p)
if c is not None and c not in mods:
yield c
def search_param_memoize(func):
"""
Is only good for search params memoize, respectively the closure,
because it just caches the input, not the func, like normal memoize does.
"""
def wrapper(*args, **kwargs):
key = (args, frozenset(kwargs.items()))
if key in search_param_cache:
return search_param_cache[key]
else:
rv = func(*args, **kwargs)
search_param_cache[key] = rv
return rv
return wrapper
class ParamListener(object):
"""
This listener is used to get the params for a function.
"""
def __init__(self):
self.param_possibilities = []
def execute(self, params):
self.param_possibilities.append(params)
@evaluate.memoize_default([])
def search_params(param):
"""
This is a dynamic search for params. If you try to complete a type:
>>> def func(foo):
>>> # here is the completion
>>> foo
>>> func(1)
>>> func("")
It is not known what the type is, because it cannot be guessed with
recursive madness. Therefore one has to analyse the statements that are
calling the function, as well as analyzing the incoming params.
"""
if not settings.dynamic_params:
return []
def get_params_for_module(module):
"""
Returns the values of a param, or an empty array.
"""
@search_param_memoize
def get_posibilities(module, func_name):
try:
possible_stmts = module.used_names[func_name]
except KeyError:
return []
for stmt in possible_stmts:
if not isinstance(stmt, parsing.Import):
calls = _scan_array(stmt.get_assignment_calls(), func_name)
for c in calls:
# no execution means that params cannot be set
call_path = c.generate_call_path()
pos = c.start_pos
scope = stmt.parent
evaluate.follow_call_path(call_path, scope, pos)
return listener.param_possibilities
result = []
for params in get_posibilities(module, func_name):
for p in params:
if str(p) == param_name:
result += evaluate.follow_statement(p.parent)
return result
func = param.get_parent_until(parsing.Function)
current_module = param.get_parent_until()
func_name = str(func.name)
if func_name == '__init__' and isinstance(func.parent, parsing.Class):
func_name = str(func.parent.name)
# get the param name
if param.assignment_details:
arr = param.assignment_details[0][1]
else:
arr = param.get_assignment_calls()
offset = 1 if arr[0][0] in ['*', '**'] else 0
param_name = str(arr[0][offset].name)
# add the listener
listener = ParamListener()
func.listeners.add(listener)
result = []
# This is like backtracking: Get the first possible result.
for mod in get_directory_modules_for_name([current_module], func_name):
result = get_params_for_module(mod)
if result:
break
# cleanup: remove the listener; important: should not stick.
func.listeners.remove(listener)
return result
def check_array_additions(array):
""" Just a mapper function for the internal _check_array_additions """
if array._array.type not in ['list', 'set']:
# TODO also check for dict updates
return []
is_list = array._array.type == 'list'
current_module = array._array.parent_stmt.get_parent_until()
res = _check_array_additions(array, current_module, is_list)
return res
def _scan_array(arr, search_name):
""" Returns the function Call that match search_name in an Array. """
result = []
for sub in arr:
for s in sub:
if isinstance(s, parsing.Array):
result += _scan_array(s, search_name)
elif isinstance(s, parsing.Call):
s_new = s
while s_new is not None:
n = s_new.name
if isinstance(n, parsing.Name) and search_name in n.names:
result.append(s)
if s_new.execution is not None:
result += _scan_array(s_new.execution, search_name)
s_new = s_new.next
return result
counter = 0
def dec(func):
""" TODO delete this """
def wrapper(*args, **kwargs):
global counter
element = args[0]
if isinstance(element, evaluate.Array):
stmt = element._array.parent_stmt
else:
# must be instance
stmt = element.var_args.parent_stmt
print(' ' * counter + 'recursion,', stmt)
counter += 1
res = func(*args, **kwargs)
counter -= 1
#print ' '*counter + 'end,'
return res
return wrapper
#@dec
@evaluate.memoize_default([])
def _check_array_additions(compare_array, module, is_list):
"""
Checks if a `parsing.Array` has "add" statements:
>>> a = [""]
>>> a.append(1)
"""
if not settings.dynamic_array_additions or module.is_builtin():
return []
def check_calls(calls, add_name):
"""
Calls are processed here. The part before the call is searched and
compared with the original Array.
"""
result = []
for c in calls:
call_path = list(c.generate_call_path())
separate_index = call_path.index(add_name)
if add_name == call_path[-1] or separate_index == 0:
# this means that there is no execution -> [].append
# or the keyword is at the start -> append()
continue
backtrack_path = iter(call_path[:separate_index])
position = c.start_pos
scope = c.parent_stmt.parent
found = evaluate.follow_call_path(backtrack_path, scope, position)
if not compare_array in found:
continue
params = call_path[separate_index + 1]
if not params.values:
continue # no params: just ignore it
if add_name in ['append', 'add']:
result += evaluate.follow_call_list(params)
elif add_name in ['insert']:
try:
second_param = params[1]
except IndexError:
continue
else:
result += evaluate.follow_call_list([second_param])
elif add_name in ['extend', 'update']:
iterators = evaluate.follow_call_list(params)
result += evaluate.get_iterator_types(iterators)
return result
def get_execution_parent(element, *stop_classes):
""" Used to get an Instance/Execution parent """
if isinstance(element, evaluate.Array):
stmt = element._array.parent_stmt
else:
# must be instance
stmt = element.var_args.parent_stmt
if isinstance(stmt, evaluate.InstanceElement):
stop_classes = list(stop_classes) + [evaluate.Function]
return stmt.get_parent_until(stop_classes)
temp_param_add = settings.dynamic_params_for_other_modules
settings.dynamic_params_for_other_modules = False
search_names = ['append', 'extend', 'insert'] if is_list else \
['add', 'update']
comp_arr_parent = get_execution_parent(compare_array, evaluate.Execution)
possible_stmts = []
res = []
for n in search_names:
try:
possible_stmts += module.used_names[n]
except KeyError:
continue
for stmt in possible_stmts:
# Check if the original scope is an execution. If it is, one
# can search for the same statement, that is in the module
# dict. Executions are somewhat special in jedi, since they
# literally copy the contents of a function.
if isinstance(comp_arr_parent, evaluate.Execution):
stmt = comp_arr_parent. \
get_statement_for_position(stmt.start_pos)
if stmt is None:
continue
# InstanceElements are special, because they don't get copied,
# but have this wrapper around them.
if isinstance(comp_arr_parent, evaluate.InstanceElement):
stmt = evaluate.InstanceElement(comp_arr_parent.instance, stmt)
if evaluate.follow_statement.push_stmt(stmt):
# check recursion
continue
res += check_calls(_scan_array(stmt.get_assignment_calls(), n), n)
evaluate.follow_statement.pop_stmt()
# reset settings
settings.dynamic_params_for_other_modules = temp_param_add
return res
def check_array_instances(instance):
""" Used for set() and list() instances. """
if not settings.dynamic_arrays_instances:
return instance.var_args
ai = ArrayInstance(instance)
return helpers.generate_param_array([ai], instance.var_args.parent_stmt)
class ArrayInstance(parsing.Base):
"""
Used for the usage of set() and list().
This is definitely a hack, but a good one :-)
It makes it possible to use set/list conversions.
"""
def __init__(self, instance):
self.instance = instance
self.var_args = instance.var_args
def iter_content(self):
"""
The index is here just ignored, because of all the appends, etc.
lists/sets are too complicated too handle that.
"""
items = []
for array in evaluate.follow_call_list(self.var_args):
if isinstance(array, evaluate.Instance) and len(array.var_args):
temp = array.var_args[0][0]
if isinstance(temp, ArrayInstance):
# prevent recursions
# TODO compare Modules
if self.var_args.start_pos != temp.var_args.start_pos:
items += temp.iter_content()
else:
debug.warning('ArrayInstance recursion', self.var_args)
continue
items += evaluate.get_iterator_types([array])
if self.var_args.parent_stmt is None:
return [] # generated var_args should not be checked for arrays
module = self.var_args.parent_stmt.get_parent_until()
is_list = str(self.instance.name) == 'list'
items += _check_array_additions(self.instance, module, is_list)
return items
def related_names(definitions, search_name, mods):
def check_call(call):
result = []
follow = [] # There might be multiple search_name's in one call_path
call_path = list(call.generate_call_path())
for i, name in enumerate(call_path):
# name is `parsing.NamePart`.
if name == search_name:
follow.append(call_path[:i + 1])
for f in follow:
follow_res, search = evaluate.goto(call.parent_stmt, f)
follow_res = related_name_add_import_modules(follow_res, search)
#print follow_res, [d.parent for d in follow_res]
# compare to see if they match
if any(r in definitions for r in follow_res):
scope = call.parent_stmt
result.append(api_classes.RelatedName(search, scope))
return result
if not definitions:
return set()
def is_definition(arr):
try:
for a in arr:
assert len(a) == 1
a = a[0]
if a.isinstance(parsing.Array):
assert is_definition(a)
elif a.isinstance(parsing.Call):
assert a.execution is None
return True
except AssertionError:
return False
mods |= set([d.get_parent_until() for d in definitions])
names = []
for m in get_directory_modules_for_name(mods, search_name):
try:
stmts = m.used_names[search_name]
except KeyError:
continue
for stmt in stmts:
if isinstance(stmt, parsing.Import):
count = 0
imps = []
for i in stmt.get_all_import_names():
for name_part in i.names:
count += 1
if name_part == search_name:
imps.append((count, name_part))
for used_count, name_part in imps:
i = imports.ImportPath(stmt, kill_count=count - used_count,
direct_resolve=True)
f = i.follow(is_goto=True)
if set(f) & set(definitions):
names.append(api_classes.RelatedName(name_part, stmt))
else:
calls = _scan_array(stmt.get_assignment_calls(), search_name)
for d in stmt.assignment_details:
if not is_definition(d[1]):
calls += _scan_array(d[1], search_name)
for call in calls:
names += check_call(call)
return names
def related_name_add_import_modules(definitions, search_name):
""" Adds the modules of the imports """
new = set()
for d in definitions:
if isinstance(d.parent, parsing.Import):
s = imports.ImportPath(d.parent, direct_resolve=True)
try:
new.add(s.follow(is_goto=True)[0])
except IndexError:
pass
return set(definitions) | new
def check_flow_information(flow, search_name, pos):
""" Try to find out the type of a variable just with the information that
is given by the flows: e.g. It is also responsible for assert checks.
>>> if isinstance(k, str):
>>> k. # <- completion here
ensures that `k` is a string.
"""
result = []
if isinstance(flow, parsing.Scope) and not result:
for ass in reversed(flow.asserts):
if pos is None or ass.start_pos > pos:
continue
result = check_statement_information(ass, search_name)
if result:
break
if isinstance(flow, parsing.Flow) and not result:
if flow.command in ['if', 'while'] and len(flow.inits) == 1:
result = check_statement_information(flow.inits[0], search_name)
return result
def check_statement_information(stmt, search_name):
try:
ass = stmt.get_assignment_calls()
try:
call = ass.get_only_subelement()
except AttributeError:
assert False
assert type(call) == parsing.Call and str(call.name) == 'isinstance'
assert bool(call.execution)
# isinstance check
isinst = call.execution.values
assert len(isinst) == 2 # has two params
assert len(isinst[0]) == 1
assert len(isinst[1]) == 1
assert isinstance(isinst[0][0], parsing.Call)
# names fit?
assert str(isinst[0][0].name) == search_name
classes_call = isinst[1][0] # class_or_type_or_tuple
assert isinstance(classes_call, parsing.Call)
result = []
for c in evaluate.follow_call(classes_call):
if isinstance(c, evaluate.Array):
result += c.get_index_types()
else:
result.append(c)
for i, c in enumerate(result):
result[i] = evaluate.Instance(c)
return result
except AssertionError:
return []

File diff suppressed because it is too large Load diff

View file

@ -0,0 +1,266 @@
import copy
import contextlib
import parsing
import evaluate
import debug
import builtin
import settings
class RecursionDecorator(object):
"""
A decorator to detect recursions in statements. In a recursion a statement
at the same place, in the same module may not be executed two times.
"""
def __init__(self, func):
self.func = func
self.reset()
def __call__(self, stmt, *args, **kwargs):
#print stmt, len(self.node_statements())
if self.push_stmt(stmt):
return []
else:
result = self.func(stmt, *args, **kwargs)
self.pop_stmt()
return result
def push_stmt(self, stmt):
self.current = RecursionNode(stmt, self.current)
if self._check_recursion():
debug.warning('catched recursion', stmt)
self.pop_stmt()
return True
return False
def pop_stmt(self):
if self.current is not None:
# I don't know how current can be None, but sometimes it happens
# with Python3.
self.current = self.current.parent
def _check_recursion(self):
test = self.current
while True:
test = test.parent
if self.current == test:
return True
if not test:
return False
def reset(self):
self.top = None
self.current = None
def node_statements(self):
result = []
n = self.current
while n:
result.insert(0, n.stmt)
n = n.parent
return result
class RecursionNode(object):
""" A node of the RecursionDecorator. """
def __init__(self, stmt, parent):
self.script = stmt.get_parent_until()
self.position = stmt.start_pos
self.parent = parent
self.stmt = stmt
# Don't check param instances, they are not causing recursions
# The same's true for the builtins, because the builtins are really
# simple.
self.is_ignored = isinstance(stmt, parsing.Param) \
or (self.script == builtin.Builtin.scope)
def __eq__(self, other):
if not other:
return None
return self.script == other.script \
and self.position == other.position \
and not self.is_ignored and not other.is_ignored
class ExecutionRecursionDecorator(object):
"""
Catches recursions of executions.
It is designed like a Singelton. Only one instance should exist.
"""
def __init__(self, func):
self.func = func
self.reset()
def __call__(self, execution, evaluate_generator=False):
debug.dbg('Execution recursions: %s' % execution, self.recursion_level,
self.execution_count, len(self.execution_funcs))
if self.check_recursion(execution, evaluate_generator):
result = []
else:
result = self.func(execution, evaluate_generator)
self.cleanup()
return result
@classmethod
def cleanup(cls):
cls.parent_execution_funcs.pop()
cls.recursion_level -= 1
@classmethod
def check_recursion(cls, execution, evaluate_generator):
in_par_execution_funcs = execution.base in cls.parent_execution_funcs
in_execution_funcs = execution.base in cls.execution_funcs
cls.recursion_level += 1
cls.execution_count += 1
cls.execution_funcs.add(execution.base)
cls.parent_execution_funcs.append(execution.base)
if cls.execution_count > settings.max_executions:
return True
if isinstance(execution.base, (evaluate.Generator, evaluate.Array)):
return False
module = execution.get_parent_until()
if evaluate_generator or module == builtin.Builtin.scope:
return False
if in_par_execution_funcs:
if cls.recursion_level > settings.max_function_recursion_level:
return True
if in_execution_funcs and \
len(cls.execution_funcs) > settings.max_until_execution_unique:
return True
if cls.execution_count > settings.max_executions_without_builtins:
return True
return False
@classmethod
def reset(cls):
cls.recursion_level = 0
cls.parent_execution_funcs = []
cls.execution_funcs = set()
cls.execution_count = 0
def fast_parent_copy(obj):
"""
Much, much faster than copy.deepcopy, but just for certain elements.
"""
new_elements = {}
def recursion(obj):
new_obj = copy.copy(obj)
new_elements[obj] = new_obj
items = new_obj.__dict__.items()
for key, value in items:
# replace parent (first try _parent and then parent)
if key in ['parent', '_parent', '_parent_stmt'] \
and value is not None:
if key == 'parent' and '_parent' in items:
# parent can be a property
continue
try:
setattr(new_obj, key, new_elements[value])
except KeyError:
pass
elif key in ['parent_stmt', 'parent_function']:
continue
elif isinstance(value, list):
setattr(new_obj, key, list_rec(value))
elif isinstance(value, (parsing.Simple, parsing.Call)):
setattr(new_obj, key, recursion(value))
return new_obj
def list_rec(list_obj):
copied_list = list_obj[:] # lists, tuples, strings, unicode
for i, el in enumerate(copied_list):
if isinstance(el, (parsing.Simple, parsing.Call)):
copied_list[i] = recursion(el)
elif isinstance(el, list):
copied_list[i] = list_rec(el)
return copied_list
return recursion(obj)
def generate_param_array(args_tuple, parent_stmt=None):
""" This generates an array, that can be used as a param. """
values = []
for arg in args_tuple:
if arg is None:
values.append([])
else:
values.append([arg])
pos = None
arr = parsing.Array(pos, parsing.Array.TUPLE, parent_stmt, values=values)
evaluate.faked_scopes.append(arr)
return arr
def scan_array_for_pos(arr, pos):
"""
Returns the function Call that match search_name in an Array.
Makes changes to arr!
"""
def check_arr_index():
positions = arr.arr_el_pos
for index, comma_pos in enumerate(positions):
if pos < comma_pos:
return index
return len(positions)
call = None
stop = False
for sub in arr.values:
call = None
for s in sub:
if isinstance(s, parsing.Array):
new = scan_array_for_pos(s, pos)
if new[0] is not None:
call, index, stop = new
if stop:
return call, index, stop
elif isinstance(s, parsing.Call):
start_s = s
# check parts of calls
while s is not None:
if s.start_pos >= pos:
return call, check_arr_index(), stop
elif s.execution is not None:
end = s.execution.end_pos
if s.execution.start_pos < pos and \
(end is None or pos < end):
c, index, stop = scan_array_for_pos(
s.execution, pos)
if stop:
return c, index, stop
# call should return without execution and
# next
reset = c or s
if reset.execution.type not in \
[parsing.Array.TUPLE,
parsing.Array.NOARRAY]:
return start_s, index, False
reset.execution = None
reset.next = None
return c or start_s, index, True
s = s.next
# The third return is just necessary for recursion inside, because
# it needs to know when to stop iterating.
return call, check_arr_index(), stop
@contextlib.contextmanager
def scale_speed_settings(factor):
a = settings.max_executions
b = settings.max_until_execution_unique
settings.max_executions *= factor
settings.max_until_execution_unique *= factor
yield
settings.max_executions = a
settings.max_until_execution_unique = b

View file

@ -0,0 +1,332 @@
from __future__ import with_statement
import os
import pkgutil
import imp
import sys
import time
import builtin
import modules
import debug
import parsing
import evaluate
import itertools
import settings
# for debugging purposes only
imports_processed = 0
star_import_cache = {}
class ModuleNotFound(Exception):
pass
class ImportPath(parsing.Base):
"""
An ImportPath is the path of a `parsing.Import` object.
"""
class _GlobalNamespace(object):
def get_defined_names(self):
return []
def get_imports(self):
return []
@property
def start_pos(self):
return (0, 0)
def get_parent_until(self):
return None
GlobalNamespace = _GlobalNamespace()
def __init__(self, import_stmt, is_like_search=False, kill_count=0,
direct_resolve=False):
self.import_stmt = import_stmt
self.is_like_search = is_like_search
self.direct_resolve = direct_resolve
self.is_partial_import = bool(kill_count)
path = import_stmt.get_parent_until().path
self.file_path = os.path.dirname(path) if path is not None else None
# rest is import_path resolution
self.import_path = []
if import_stmt.from_ns:
self.import_path += import_stmt.from_ns.names
if import_stmt.namespace:
if self.is_nested_import() and not direct_resolve:
self.import_path.append(import_stmt.namespace.names[0])
else:
self.import_path += import_stmt.namespace.names
for i in range(kill_count + int(is_like_search)):
self.import_path.pop()
def __repr__(self):
return '<%s: %s>' % (type(self).__name__, self.import_stmt)
def is_nested_import(self):
"""
This checks for the special case of nested imports, without aliases and
from statement:
>>> import foo.bar
"""
return not self.import_stmt.alias and not self.import_stmt.from_ns \
and len(self.import_stmt.namespace.names) > 1 \
and not self.direct_resolve
def get_nested_import(self, parent):
"""
See documentation of `self.is_nested_import`.
Generates an Import statement, that can be used to fake nested imports.
"""
i = self.import_stmt
# This is not an existing Import statement. Therefore, set position to
# 0 (0 is not a valid line number).
zero = (0, 0)
n = parsing.Name(i.namespace.names[1:], zero, zero, self.import_stmt)
new = parsing.Import(zero, zero, n)
new.parent = parent
evaluate.faked_scopes.append(new)
debug.dbg('Generated a nested import: %s' % new)
return new
def get_defined_names(self, on_import_stmt=False):
names = []
for scope in self.follow():
if scope is ImportPath.GlobalNamespace:
if self.import_stmt.relative_count == 0:
names += self.get_module_names()
if self.file_path is not None:
path = os.path.abspath(self.file_path)
for i in range(self.import_stmt.relative_count - 1):
path = os.path.dirname(path)
names += self.get_module_names([path])
else:
if on_import_stmt and isinstance(scope, parsing.Module) \
and scope.path.endswith('__init__.py'):
pkg_path = os.path.dirname(scope.path)
names += self.get_module_names([pkg_path])
for s, scope_names in evaluate.get_names_for_scope(scope,
include_builtin=False):
for n in scope_names:
if self.import_stmt.from_ns is None \
or self.is_partial_import:
# from_ns must be defined to access module
# values plus a partial import means that there
# is something after the import, which
# automatically implies that there must not be
# any non-module scope.
continue
names.append(n)
return names
def get_module_names(self, search_path=None):
"""
Get the names of all modules in the search_path. This means file names
and not names defined in the files.
"""
if not search_path:
search_path = self.sys_path_with_modifications()
names = []
for module_loader, name, is_pkg in pkgutil.iter_modules(search_path):
inf_pos = (float('inf'), float('inf'))
names.append(parsing.Name([(name, inf_pos)], inf_pos, inf_pos,
self.import_stmt))
return names
def sys_path_with_modifications(self):
module = self.import_stmt.get_parent_until()
return modules.sys_path_with_modifications(module)
def follow(self, is_goto=False):
"""
Returns the imported modules.
"""
if evaluate.follow_statement.push_stmt(self.import_stmt):
# check recursion
return []
if self.import_path:
try:
scope, rest = self._follow_file_system()
except ModuleNotFound:
debug.warning('Module not found: ' + str(self.import_stmt))
evaluate.follow_statement.pop_stmt()
return []
scopes = [scope]
scopes += itertools.chain.from_iterable(
remove_star_imports(s) for s in scopes)
# follow the rest of the import (not FS -> classes, functions)
if len(rest) > 1 or rest and self.is_like_search:
scopes = []
elif rest:
if is_goto:
scopes = itertools.chain.from_iterable(
evaluate.get_scopes_for_name(s, rest[0], is_goto=True)
for s in scopes)
else:
scopes = itertools.chain.from_iterable(
evaluate.follow_path(iter(rest), s, s)
for s in scopes)
scopes = list(scopes)
if self.is_nested_import():
scopes.append(self.get_nested_import(scope))
else:
scopes = [ImportPath.GlobalNamespace]
debug.dbg('after import', scopes)
evaluate.follow_statement.pop_stmt()
return scopes
def _follow_file_system(self):
"""
Find a module with a path (of the module, like usb.backend.libusb10).
"""
def follow_str(ns, string):
debug.dbg('follow_module', ns, string)
path = None
if ns:
path = ns[1]
elif self.import_stmt.relative_count:
module = self.import_stmt.get_parent_until()
path = os.path.abspath(module.path)
for i in range(self.import_stmt.relative_count):
path = os.path.dirname(path)
global imports_processed
imports_processed += 1
if path is not None:
return imp.find_module(string, [path])
else:
debug.dbg('search_module', string, self.file_path)
# Override the sys.path. It works only good that way.
# Injecting the path directly into `find_module` did not work.
sys.path, temp = sys_path_mod, sys.path
try:
i = imp.find_module(string)
except ImportError:
sys.path = temp
raise
sys.path = temp
return i
if self.file_path:
sys_path_mod = list(self.sys_path_with_modifications())
sys_path_mod.insert(0, self.file_path)
else:
sys_path_mod = list(builtin.get_sys_path())
current_namespace = None
# now execute those paths
rest = []
for i, s in enumerate(self.import_path):
try:
current_namespace = follow_str(current_namespace, s)
except ImportError:
if current_namespace:
rest = self.import_path[i:]
else:
raise ModuleNotFound(
'The module you searched has not been found')
sys_path_mod.pop(0) # TODO why is this here?
path = current_namespace[1]
is_package_directory = current_namespace[2][2] == imp.PKG_DIRECTORY
f = None
if is_package_directory or current_namespace[0]:
# is a directory module
if is_package_directory:
path += '/__init__.py'
with open(path) as f:
source = f.read()
else:
source = current_namespace[0].read()
current_namespace[0].close()
if path.endswith('.py'):
f = modules.Module(path, source)
else:
f = builtin.Parser(path=path)
else:
f = builtin.Parser(name=path)
return f.parser.module, rest
def strip_imports(scopes):
"""
Here we strip the imports - they don't get resolved necessarily.
Really used anymore? Merge with remove_star_imports?
"""
result = []
for s in scopes:
if isinstance(s, parsing.Import):
result += ImportPath(s).follow()
else:
result.append(s)
return result
def cache_star_import(func):
def wrapper(scope, *args, **kwargs):
try:
mods = star_import_cache[scope]
if mods[0] + settings.star_import_cache_validity > time.time():
return mods[1]
except KeyError:
pass
# cache is too old and therefore invalid or not available
invalidate_star_import_cache(scope)
mods = func(scope, *args, **kwargs)
star_import_cache[scope] = time.time(), mods
return mods
return wrapper
def invalidate_star_import_cache(module, only_main=False):
""" Important if some new modules are being reparsed """
try:
t, mods = star_import_cache[module]
del star_import_cache[module]
for m in mods:
invalidate_star_import_cache(m, only_main=True)
except KeyError:
pass
if not only_main:
# We need a list here because otherwise the list is being changed
# during the iteration in py3k: iteritems -> items.
for key, (t, mods) in list(star_import_cache.items()):
if module in mods:
invalidate_star_import_cache(key)
@cache_star_import
def remove_star_imports(scope, ignored_modules=[]):
"""
Check a module for star imports:
>>> from module import *
and follow these modules.
"""
modules = strip_imports(i for i in scope.get_imports() if i.star)
new = []
for m in modules:
if m not in ignored_modules:
new += remove_star_imports(m, modules)
modules += new
# Filter duplicate modules.
return set(modules)

View file

@ -0,0 +1,86 @@
import keyword
from _compatibility import is_py3k
import builtin
import pydoc
try:
from pydoc_data import topics as pydoc_topics
except ImportError:
# Python 2.6
try:
import pydoc_topics
except ImportError:
# Python 2.5
pydoc_topics = None
if is_py3k:
keys = keyword.kwlist
else:
keys = keyword.kwlist + ['None', 'False', 'True']
def get_keywords(string='', pos=(0, 0), all=False):
if all:
return set([Keyword(k, pos) for k in keys])
if string in keys:
return set([Keyword(string, pos)])
return set()
def get_operator(string, pos):
return Keyword(string, pos)
class Keyword(object):
def __init__(self, name, pos):
self.name = name
self.start_pos = pos
self.parent = builtin.Builtin.scope
def get_parent_until(self):
return self.parent
@property
def names(self):
""" For a `parsing.Name` like comparision """
return [self.name]
@property
def docstr(self):
return imitate_pydoc(self.name)
def __repr__(self):
return '<%s: %s>' % (type(self).__name__, self.name)
def imitate_pydoc(string):
"""
It's not possible to get the pydoc's without starting the annoying pager
stuff.
"""
# str needed because of possible unicode stuff in py2k (pydoc doesn't work
# with unicode strings)
string = str(string)
h = pydoc.help
try:
# try to access symbols
string = h.symbols[string]
string, _, related = string.partition(' ')
except KeyError:
pass
get_target = lambda s: h.topics.get(s, h.keywords.get(s))
while isinstance(string, str):
string = get_target(string)
try:
# is a tuple now
label, related = string
except TypeError:
return ''
try:
return pydoc_topics.topics[label] if pydoc_topics else ''
except KeyError:
return ''

View file

@ -0,0 +1,9 @@
class partial():
def __init__(self, func, *args, **keywords):
self.__func = func
self.__args = args
self.__keywords = keywords
def __call__(self, *args, **kwargs):
# I know this doesn't work in Python, but Jedi can this ;-)
return self.__func(*self.__args, *args, **self.keywords, **kwargs)

View file

@ -0,0 +1,99 @@
def compile():
class SRE_Match():
endpos = 1
lastgroup = 0
lastindex = 1
pos = 0
string = 'a'
regs = ((0, 1),)
def __init__(self, pattern):
self.re = pattern
def start(self):
return 0
def end(self):
return 1
def span(self):
return 0, 1
def expand(self):
return ''
def group(self):
return ''
def groupdict(self):
return {'a', 'a'}
def groups(self):
return ('a',)
class SRE_Pattern():
flags = 0
groupindex = {}
groups = 0
pattern = 'a'
def findall(self):
"""
findall(string[, pos[, endpos]]) --> list.
Return a list of all non-overlapping matches of pattern in string.
"""
return ['a']
def finditer(self):
"""
finditer(string[, pos[, endpos]]) --> iterator.
Return an iterator over all non-overlapping matches for the
RE pattern in string. For each match, the iterator returns a
match object.
"""
yield SRE_Match(self)
def match(self):
"""
match(string[, pos[, endpos]]) --> match object or None.
Matches zero or more characters at the beginning of the string
pattern
"""
return SRE_Match(self)
def scanner(self):
pass
def search(self):
"""
search(string[, pos[, endpos]]) --> match object or None.
Scan through string looking for a match, and return a corresponding
MatchObject instance. Return None if no position in the string matches.
"""
return SRE_Match(self)
def split(self):
"""
split(string[, maxsplit = 0]) --> list.
Split string by the occurrences of pattern.
"""
return ['a']
def sub(self):
"""
sub(repl, string[, count = 0]) --> newstring
Return the string obtained by replacing the leftmost non-overlapping
occurrences of pattern in string by the replacement repl.
"""
return ''
def subn(self):
"""
subn(repl, string[, count = 0]) --> (newstring, number of subs)
Return the tuple (new_string, number_of_subs_made) found by replacing
the leftmost non-overlapping occurrences of pattern with the
replacement repl.
"""
return ('', 1)
return SRE_Pattern()

View file

@ -0,0 +1,8 @@
def proxy(object, callback=None):
return object
class ref():
def __init__(self, object, callback=None):
self.__object = object
def __call__(self):
return self.__object

View file

@ -0,0 +1,218 @@
"""
Pure Python implementation of some builtins.
This code is not going to be executed anywhere.
These implementations are not always correct, but should work as good as
possible for the auto completion.
"""
def next(iterator, default=None):
if hasattr("next"):
return iterator.next()
else:
return iterator.__next__()
return default
def iter(collection, sentinel=None):
if sentinel:
yield collection()
else:
for c in collection:
yield c
def range(start, stop=None, step=1):
return [0]
class xrange():
# Attention: this function doesn't exist in Py3k (there it is range).
def __iter__(self):
yield 1
def count(self):
return 1
def index(self):
return 1
#--------------------------------------------------------
# descriptors
#--------------------------------------------------------
class property():
def __init__(self, fget, fset=None, fdel=None, doc=None):
self.fget = fget
self.fset = fset
self.fdel = fdel
self.__doc__ = doc
def __get__(self, obj, cls):
return self.fget(obj)
def __set__(self, obj, value):
self.fset(obj, value)
def __delete__(self, obj):
self.fdel(obj)
def setter(self, func):
self.fset = func
return self
def getter(self, func):
self.fget = func
return self
def deleter(self, func):
self.fdel = func
return self
class staticmethod():
def __init__(self, func):
self.__func = func
def __get__(self, obj, cls):
return self.__func
class classmethod():
def __init__(self, func):
self.__func = func
def __get__(self, obj, cls):
def _method(*args, **kwargs):
return self.__func(cls, *args, **kwargs)
return _method
#--------------------------------------------------------
# array stuff
#--------------------------------------------------------
class list():
def __init__(self, iterable=[]):
self.__iterable = []
for i in iterable:
self.__iterable += [i]
def __iter__(self):
for i in self.__iterable:
yield i
def __getitem__(self, y):
return self.__iterable[y]
def pop(self):
return self.__iterable[-1]
class tuple():
def __init__(self, iterable=[]):
self.__iterable = []
for i in iterable:
self.__iterable += [i]
def __iter__(self):
for i in self.__iterable:
yield i
def __getitem__(self, y):
return self.__iterable[y]
def index(self):
return 1
def count(self):
return 1
class set():
def __init__(self, iterable=[]):
self.__iterable = iterable
def __iter__(self):
for i in self.__iterable:
yield i
def pop(self):
return self.__iterable.pop()
def copy(self):
return self
def difference(self, other):
return self - other
def intersection(self, other):
return self & other
def symmetric_difference(self, other):
return self ^ other
def union(self, other):
return self | other
class frozenset():
def __init__(self, iterable=[]):
self.__iterable = iterable
def __iter__(self):
for i in self.__iterable:
yield i
def copy(self):
return self
class dict():
def __init__(self, **elements):
self.__elements = elements
def clear(self):
# has a strange docstr
pass
def get(self, k, d=None):
# TODO implement
try:
#return self.__elements[k]
pass
except KeyError:
return d
class reversed():
def __init__(self, sequence):
self.__sequence = sequence
def __iter__(self):
for i in self.__sequence:
yield i
def __next__(self):
return next(self.__iter__())
def next(self):
return self.__next__()
#--------------------------------------------------------
# basic types
#--------------------------------------------------------
class int():
def __init__(self, x, base=None):
pass
class str():
def __init__(self, obj):
pass
class object():
def mro():
""" mro() -> list
return a type's method resolution order """
return [object]

View file

@ -0,0 +1,4 @@
class datetime():
@staticmethod
def now():
return datetime()

View file

@ -0,0 +1,5 @@
def getcwd():
return ''
def getcwdu():
return ''

View file

@ -0,0 +1,312 @@
from __future__ import with_statement
from _compatibility import exec_function
import re
import tokenize
import sys
import os
import time
import parsing
import builtin
import debug
import evaluate
import settings
import imports
class Module(builtin.CachedModule):
"""
Manages all files, that are parsed and caches them.
:param path: The module path of the file.
:param source: The source code of the file.
"""
def __init__(self, path, source):
super(Module, self).__init__(path=path)
self.source = source
self._line_cache = None
def _get_source(self):
""" Just one time """
s = self.source
del self.source # memory efficiency
return s
class ModuleWithCursor(Module):
"""
Manages all files, that are parsed and caches them.
Important are the params source and path, one of them has to
be there.
:param source: The source code of the file.
:param path: The module path of the file or None.
:param position: The position, the user is currently in. Only important \
for the main file.
"""
def __init__(self, path, source, position):
super(ModuleWithCursor, self).__init__(path, source)
self.position = position
# this two are only used, because there is no nonlocal in Python 2
self._line_temp = None
self._relevant_temp = None
self.source = source
self._part_parser = None
@property
def parser(self):
""" get the parser lazy """
if not self._parser:
try:
ts, parser = builtin.CachedModule.cache[self.path]
imports.invalidate_star_import_cache(parser.module)
del builtin.CachedModule.cache[self.path]
except KeyError:
pass
# Call the parser already here, because it will be used anyways.
# Also, the position is here important (which will not be used by
# default), therefore fill the cache here.
self._parser = parsing.PyFuzzyParser(self.source, self.path,
self.position)
if self.path is not None:
builtin.CachedModule.cache[self.path] = time.time(), \
self._parser
return self._parser
def get_path_until_cursor(self):
""" Get the path under the cursor. """
result = self._get_path_until_cursor()
self._start_cursor_pos = self._line_temp + 1, self._column_temp
return result
def _get_path_until_cursor(self, start_pos=None):
def fetch_line():
line = self.get_line(self._line_temp)
if self._is_first:
self._is_first = False
self._line_length = self._column_temp
line = line[:self._column_temp]
else:
self._line_length = len(line)
line = line + '\n'
# add lines with a backslash at the end
while 1:
self._line_temp -= 1
last_line = self.get_line(self._line_temp)
if last_line and last_line[-1] == '\\':
line = last_line[:-1] + ' ' + line
else:
break
return line[::-1]
self._is_first = True
if start_pos is None:
self._line_temp = self.position[0]
self._column_temp = self.position[1]
else:
self._line_temp, self._column_temp = start_pos
open_brackets = ['(', '[', '{']
close_brackets = [')', ']', '}']
gen = tokenize.generate_tokens(fetch_line)
string = ''
level = 0
force_point = False
try:
for token_type, tok, start, end, line in gen:
#print 'tok', token_type, tok, force_point
if level > 0:
if tok in close_brackets:
level += 1
if tok in open_brackets:
level -= 1
elif tok == '.':
force_point = False
elif force_point:
# it is reversed, therefore a number is getting recognized
# as a floating point number
if token_type == tokenize.NUMBER and tok[0] == '.':
force_point = False
else:
break
elif tok in close_brackets:
level += 1
elif token_type in [tokenize.NAME, tokenize.STRING]:
force_point = True
elif token_type == tokenize.NUMBER:
pass
else:
break
self._column_temp = self._line_length - end[1]
string += tok
except tokenize.TokenError:
debug.warning("Tokenize couldn't finish", sys.exc_info)
return string[::-1]
def get_path_under_cursor(self):
"""
Return the path under the cursor. If there is a rest of the path left,
it will be added to the stuff before it.
"""
line = self.get_line(self.position[0])
after = re.search("[\w\d]*", line[self.position[1]:]).group(0)
return self.get_path_until_cursor() + after
def get_operator_under_cursor(self):
line = self.get_line(self.position[0])
after = re.match("[^\w\s]+", line[self.position[1]:])
before = re.match("[^\w\s]+", line[:self.position[1]][::-1])
return (before.group(0) if before is not None else '') \
+ (after.group(0) if after is not None else '')
def get_context(self):
pos = self._start_cursor_pos
while pos > (1, 0):
# remove non important white space
line = self.get_line(pos[0])
while pos[1] > 0 and line[pos[1] - 1].isspace():
pos = pos[0], pos[1] - 1
try:
yield self._get_path_until_cursor(start_pos=pos)
except StopIteration:
yield ''
pos = self._line_temp, self._column_temp
while True:
yield ''
def get_line(self, line_nr):
if not self._line_cache:
self._line_cache = self.source.split('\n')
if line_nr == 0:
# This is a fix for the zeroth line. We need a newline there, for
# the backwards parser.
return ''
if line_nr < 0:
raise StopIteration()
try:
return self._line_cache[line_nr - 1]
except IndexError:
raise StopIteration()
def get_part_parser(self):
""" Returns a parser that contains only part of the source code. This
exists only because of performance reasons.
"""
if self._part_parser:
return self._part_parser
# TODO check for docstrings
length = settings.part_line_length
offset = max(self.position[0] - length, 0)
s = '\n'.join(self.source.split('\n')[offset:offset + length])
self._part_parser = parsing.PyFuzzyParser(s, self.path, self.position,
line_offset=offset)
return self._part_parser
@evaluate.memoize_default([])
def sys_path_with_modifications(module):
def execute_code(code):
c = "import os; from os.path import *; result=%s"
variables = {'__file__': module.path}
try:
exec_function(c % code, variables)
except Exception:
debug.warning('sys path detected, but failed to evaluate')
return None
try:
res = variables['result']
if isinstance(res, str):
return os.path.abspath(res)
else:
return None
except KeyError:
return None
def check_module(module):
try:
possible_stmts = module.used_names['path']
except KeyError:
return builtin.get_sys_path()
sys_path = list(builtin.get_sys_path()) # copy
for p in possible_stmts:
try:
call = p.get_assignment_calls().get_only_subelement()
except AttributeError:
continue
n = call.name
if not isinstance(n, parsing.Name) or len(n.names) != 3:
continue
if n.names[:2] != ('sys', 'path'):
continue
array_cmd = n.names[2]
if call.execution is None:
continue
exe = call.execution
if not (array_cmd == 'insert' and len(exe) == 2
or array_cmd == 'append' and len(exe) == 1):
continue
if array_cmd == 'insert':
exe_type, exe.type = exe.type, parsing.Array.NOARRAY
exe_pop = exe.values.pop(0)
res = execute_code(exe.get_code())
if res is not None:
sys_path.insert(0, res)
debug.dbg('sys path inserted: %s' % res)
exe.type = exe_type
exe.values.insert(0, exe_pop)
elif array_cmd == 'append':
res = execute_code(exe.get_code())
if res is not None:
sys_path.append(res)
debug.dbg('sys path added: %s' % res)
return sys_path
if module.path is None:
return [] # support for modules without a path is intentionally bad.
curdir = os.path.abspath(os.curdir)
try:
os.chdir(os.path.dirname(module.path))
except OSError:
pass
result = check_module(module)
result += detect_django_path(module.path)
# cleanup, back to old directory
os.chdir(curdir)
return result
def detect_django_path(module_path):
""" Detects the path of the very well known Django library (if used) """
result = []
while True:
new = os.path.dirname(module_path)
# If the module_path doesn't change anymore, we're finished -> /
if new == module_path:
break
else:
module_path = new
try:
with open(module_path + os.path.sep + 'manage.py'):
debug.dbg('Found django path: %s' % module_path)
result.append(module_path)
except IOError:
pass
return result

File diff suppressed because it is too large Load diff

View file

@ -0,0 +1,87 @@
# ----------------
# completion output settings
# ----------------
# The completion is by default case insensitive.
case_insensitive_completion = True
# Adds a dot after a module, because a module that is not accessed this way is
# definitely not the normal case. However, in VIM this doesn't work, that's why
# it isn't used at the moment.
add_dot_after_module = False
# Adds an opening bracket after a function, because that's normal behaviour.
# Removed it again, because in VIM that is not very practical.
add_bracket_after_function = False
# ----------------
# dynamic stuff
# ----------------
# check for `append`, etc. on array instances like list()
dynamic_arrays_instances = True
# check for `append`, etc. on arrays: [], {}, ()
dynamic_array_additions = True
# A dynamic param completion, finds the callees of the function, which define
# the params of a function.
dynamic_params = True
# Do the same for other modules.
dynamic_params_for_other_modules = True
# Additional modules in which Jedi checks if statements are to be found. This
# is practical for IDE's, that want to administrate their modules themselves.
additional_dynamic_modules = []
# ----------------
# recursions
# ----------------
# Recursion settings are important if you don't want extremly recursive python
# code to go absolutely crazy. First of there is a global limit
# `max_executions`. This limit is important, to set a maximum amount of time,
# the completion may use.
#
# The `max_until_execution_unique` limit is probably the most important one,
# because if that limit is passed, functions can only be one time executed. So
# new functions will be executed, complex recursions with the same functions
# again and again, are ignored.
#
# `max_function_recursion_level` is more about whether the recursions are
# stopped in deepth or in width. The ratio beetween this and
# `max_until_execution_unique` is important here. It stops a recursion (after
# the number of function calls in the recursion), if it was already used
# earlier.
#
# The values are based on my experimental tries, used on the jedi library. But
# I don't think there's any other Python library, that uses recursion in a
# similar (extreme) way. This makes the completion definitely worse in some
# cases. But a completion should also be fast.
max_function_recursion_level = 5
max_until_execution_unique = 50
max_executions_without_builtins = 200
max_executions = 250
# Because get_in_function_call is normally used on every single key hit, it has
# to be faster than a normal completion. This is the factor that is used to
# scale `max_executions` and `max_until_execution_unique`:
scale_get_in_function_call = 0.1
# ----------------
# various
# ----------------
# Size of the current code part, which is used to speed up parsing.
part_line_length = 20
# ----------------
# star import caching
# ----------------
# In huge packages like numpy, checking all star imports on every completion
# might be slow, therefore we do a star import caching, that lasts a certain
# time span (in seconds).
star_import_cache_validity = 60.0

View file

@ -184,7 +184,7 @@ def file_name(pl, segment_info, display_no_file=False, no_file_text='[No file]')
}] }]
else: else:
return None return None
file_name = vim_funcs['fnamemodify'](name, ':~:.:t') file_name = vim_funcs['fnamemodify'](name, ':~:.:t').decode('utf-8')
return file_name return file_name

View file

@ -1 +0,0 @@
ref: refs/heads/master

View file

@ -1,11 +0,0 @@
[core]
repositoryformatversion = 0
filemode = true
bare = false
logallrefupdates = true
[remote "origin"]
fetch = +refs/heads/*:refs/remotes/origin/*
url = https://github.com/scrooloose/nerdtree.git
[branch "master"]
remote = origin
merge = refs/heads/master

View file

@ -1 +0,0 @@
Unnamed repository; edit this file 'description' to name the repository.

View file

@ -1,15 +0,0 @@
#!/bin/sh
#
# An example hook script to check the commit log message taken by
# applypatch from an e-mail message.
#
# The hook should exit with non-zero status after issuing an
# appropriate message if it wants to stop the commit. The hook is
# allowed to edit the commit message file.
#
# To enable this hook, rename this file to "applypatch-msg".
. git-sh-setup
test -x "$GIT_DIR/hooks/commit-msg" &&
exec "$GIT_DIR/hooks/commit-msg" ${1+"$@"}
:

View file

@ -1,24 +0,0 @@
#!/bin/sh
#
# An example hook script to check the commit log message.
# Called by "git commit" with one argument, the name of the file
# that has the commit message. The hook should exit with non-zero
# status after issuing an appropriate message if it wants to stop the
# commit. The hook is allowed to edit the commit message file.
#
# To enable this hook, rename this file to "commit-msg".
# Uncomment the below to add a Signed-off-by line to the message.
# Doing this in a hook is a bad idea in general, but the prepare-commit-msg
# hook is more suited to it.
#
# SOB=$(git var GIT_AUTHOR_IDENT | sed -n 's/^\(.*>\).*$/Signed-off-by: \1/p')
# grep -qs "^$SOB" "$1" || echo "$SOB" >> "$1"
# This example catches duplicate Signed-off-by lines.
test "" = "$(grep '^Signed-off-by: ' "$1" |
sort | uniq -c | sed -e '/^[ ]*1[ ]/d')" || {
echo >&2 Duplicate Signed-off-by lines.
exit 1
}

View file

@ -1,8 +0,0 @@
#!/bin/sh
#
# An example hook script to prepare a packed repository for use over
# dumb transports.
#
# To enable this hook, rename this file to "post-update".
exec git update-server-info

View file

@ -1,14 +0,0 @@
#!/bin/sh
#
# An example hook script to verify what is about to be committed
# by applypatch from an e-mail message.
#
# The hook should exit with non-zero status after issuing an
# appropriate message if it wants to stop the commit.
#
# To enable this hook, rename this file to "pre-applypatch".
. git-sh-setup
test -x "$GIT_DIR/hooks/pre-commit" &&
exec "$GIT_DIR/hooks/pre-commit" ${1+"$@"}
:

View file

@ -1,50 +0,0 @@
#!/bin/sh
#
# An example hook script to verify what is about to be committed.
# Called by "git commit" with no arguments. The hook should
# exit with non-zero status after issuing an appropriate message if
# it wants to stop the commit.
#
# To enable this hook, rename this file to "pre-commit".
if git rev-parse --verify HEAD >/dev/null 2>&1
then
against=HEAD
else
# Initial commit: diff against an empty tree object
against=4b825dc642cb6eb9a060e54bf8d69288fbee4904
fi
# If you want to allow non-ascii filenames set this variable to true.
allownonascii=$(git config hooks.allownonascii)
# Redirect output to stderr.
exec 1>&2
# Cross platform projects tend to avoid non-ascii filenames; prevent
# them from being added to the repository. We exploit the fact that the
# printable range starts at the space character and ends with tilde.
if [ "$allownonascii" != "true" ] &&
# Note that the use of brackets around a tr range is ok here, (it's
# even required, for portability to Solaris 10's /usr/bin/tr), since
# the square bracket bytes happen to fall in the designated range.
test $(git diff --cached --name-only --diff-filter=A -z $against |
LC_ALL=C tr -d '[ -~]\0' | wc -c) != 0
then
echo "Error: Attempt to add a non-ascii file name."
echo
echo "This can cause problems if you want to work"
echo "with people on other platforms."
echo
echo "To be portable it is advisable to rename the file ..."
echo
echo "If you know what you are doing you can disable this"
echo "check using:"
echo
echo " git config hooks.allownonascii true"
echo
exit 1
fi
# If there are whitespace errors, print the offending file names and fail.
exec git diff-index --check --cached $against --

View file

@ -1,169 +0,0 @@
#!/bin/sh
#
# Copyright (c) 2006, 2008 Junio C Hamano
#
# The "pre-rebase" hook is run just before "git rebase" starts doing
# its job, and can prevent the command from running by exiting with
# non-zero status.
#
# The hook is called with the following parameters:
#
# $1 -- the upstream the series was forked from.
# $2 -- the branch being rebased (or empty when rebasing the current branch).
#
# This sample shows how to prevent topic branches that are already
# merged to 'next' branch from getting rebased, because allowing it
# would result in rebasing already published history.
publish=next
basebranch="$1"
if test "$#" = 2
then
topic="refs/heads/$2"
else
topic=`git symbolic-ref HEAD` ||
exit 0 ;# we do not interrupt rebasing detached HEAD
fi
case "$topic" in
refs/heads/??/*)
;;
*)
exit 0 ;# we do not interrupt others.
;;
esac
# Now we are dealing with a topic branch being rebased
# on top of master. Is it OK to rebase it?
# Does the topic really exist?
git show-ref -q "$topic" || {
echo >&2 "No such branch $topic"
exit 1
}
# Is topic fully merged to master?
not_in_master=`git rev-list --pretty=oneline ^master "$topic"`
if test -z "$not_in_master"
then
echo >&2 "$topic is fully merged to master; better remove it."
exit 1 ;# we could allow it, but there is no point.
fi
# Is topic ever merged to next? If so you should not be rebasing it.
only_next_1=`git rev-list ^master "^$topic" ${publish} | sort`
only_next_2=`git rev-list ^master ${publish} | sort`
if test "$only_next_1" = "$only_next_2"
then
not_in_topic=`git rev-list "^$topic" master`
if test -z "$not_in_topic"
then
echo >&2 "$topic is already up-to-date with master"
exit 1 ;# we could allow it, but there is no point.
else
exit 0
fi
else
not_in_next=`git rev-list --pretty=oneline ^${publish} "$topic"`
/usr/bin/perl -e '
my $topic = $ARGV[0];
my $msg = "* $topic has commits already merged to public branch:\n";
my (%not_in_next) = map {
/^([0-9a-f]+) /;
($1 => 1);
} split(/\n/, $ARGV[1]);
for my $elem (map {
/^([0-9a-f]+) (.*)$/;
[$1 => $2];
} split(/\n/, $ARGV[2])) {
if (!exists $not_in_next{$elem->[0]}) {
if ($msg) {
print STDERR $msg;
undef $msg;
}
print STDERR " $elem->[1]\n";
}
}
' "$topic" "$not_in_next" "$not_in_master"
exit 1
fi
<<\DOC_END
This sample hook safeguards topic branches that have been
published from being rewound.
The workflow assumed here is:
* Once a topic branch forks from "master", "master" is never
merged into it again (either directly or indirectly).
* Once a topic branch is fully cooked and merged into "master",
it is deleted. If you need to build on top of it to correct
earlier mistakes, a new topic branch is created by forking at
the tip of the "master". This is not strictly necessary, but
it makes it easier to keep your history simple.
* Whenever you need to test or publish your changes to topic
branches, merge them into "next" branch.
The script, being an example, hardcodes the publish branch name
to be "next", but it is trivial to make it configurable via
$GIT_DIR/config mechanism.
With this workflow, you would want to know:
(1) ... if a topic branch has ever been merged to "next". Young
topic branches can have stupid mistakes you would rather
clean up before publishing, and things that have not been
merged into other branches can be easily rebased without
affecting other people. But once it is published, you would
not want to rewind it.
(2) ... if a topic branch has been fully merged to "master".
Then you can delete it. More importantly, you should not
build on top of it -- other people may already want to
change things related to the topic as patches against your
"master", so if you need further changes, it is better to
fork the topic (perhaps with the same name) afresh from the
tip of "master".
Let's look at this example:
o---o---o---o---o---o---o---o---o---o "next"
/ / / /
/ a---a---b A / /
/ / / /
/ / c---c---c---c B /
/ / / \ /
/ / / b---b C \ /
/ / / / \ /
---o---o---o---o---o---o---o---o---o---o---o "master"
A, B and C are topic branches.
* A has one fix since it was merged up to "next".
* B has finished. It has been fully merged up to "master" and "next",
and is ready to be deleted.
* C has not merged to "next" at all.
We would want to allow C to be rebased, refuse A, and encourage
B to be deleted.
To compute (1):
git rev-list ^master ^topic next
git rev-list ^master next
if these match, topic has not merged in next at all.
To compute (2):
git rev-list master..topic
if this is empty, it is fully merged to "master".
DOC_END

View file

@ -1,36 +0,0 @@
#!/bin/sh
#
# An example hook script to prepare the commit log message.
# Called by "git commit" with the name of the file that has the
# commit message, followed by the description of the commit
# message's source. The hook's purpose is to edit the commit
# message file. If the hook fails with a non-zero status,
# the commit is aborted.
#
# To enable this hook, rename this file to "prepare-commit-msg".
# This hook includes three examples. The first comments out the
# "Conflicts:" part of a merge commit.
#
# The second includes the output of "git diff --name-status -r"
# into the message, just before the "git status" output. It is
# commented because it doesn't cope with --amend or with squashed
# commits.
#
# The third example adds a Signed-off-by line to the message, that can
# still be edited. This is rarely a good idea.
case "$2,$3" in
merge,)
/usr/bin/perl -i.bak -ne 's/^/# /, s/^# #/#/ if /^Conflicts/ .. /#/; print' "$1" ;;
# ,|template,)
# /usr/bin/perl -i.bak -pe '
# print "\n" . `git diff --cached --name-status -r`
# if /^#/ && $first++ == 0' "$1" ;;
*) ;;
esac
# SOB=$(git var GIT_AUTHOR_IDENT | sed -n 's/^\(.*>\).*$/Signed-off-by: \1/p')
# grep -qs "^$SOB" "$1" || echo "$SOB" >> "$1"

View file

@ -1,128 +0,0 @@
#!/bin/sh
#
# An example hook script to blocks unannotated tags from entering.
# Called by "git receive-pack" with arguments: refname sha1-old sha1-new
#
# To enable this hook, rename this file to "update".
#
# Config
# ------
# hooks.allowunannotated
# This boolean sets whether unannotated tags will be allowed into the
# repository. By default they won't be.
# hooks.allowdeletetag
# This boolean sets whether deleting tags will be allowed in the
# repository. By default they won't be.
# hooks.allowmodifytag
# This boolean sets whether a tag may be modified after creation. By default
# it won't be.
# hooks.allowdeletebranch
# This boolean sets whether deleting branches will be allowed in the
# repository. By default they won't be.
# hooks.denycreatebranch
# This boolean sets whether remotely creating branches will be denied
# in the repository. By default this is allowed.
#
# --- Command line
refname="$1"
oldrev="$2"
newrev="$3"
# --- Safety check
if [ -z "$GIT_DIR" ]; then
echo "Don't run this script from the command line." >&2
echo " (if you want, you could supply GIT_DIR then run" >&2
echo " $0 <ref> <oldrev> <newrev>)" >&2
exit 1
fi
if [ -z "$refname" -o -z "$oldrev" -o -z "$newrev" ]; then
echo "Usage: $0 <ref> <oldrev> <newrev>" >&2
exit 1
fi
# --- Config
allowunannotated=$(git config --bool hooks.allowunannotated)
allowdeletebranch=$(git config --bool hooks.allowdeletebranch)
denycreatebranch=$(git config --bool hooks.denycreatebranch)
allowdeletetag=$(git config --bool hooks.allowdeletetag)
allowmodifytag=$(git config --bool hooks.allowmodifytag)
# check for no description
projectdesc=$(sed -e '1q' "$GIT_DIR/description")
case "$projectdesc" in
"Unnamed repository"* | "")
echo "*** Project description file hasn't been set" >&2
exit 1
;;
esac
# --- Check types
# if $newrev is 0000...0000, it's a commit to delete a ref.
zero="0000000000000000000000000000000000000000"
if [ "$newrev" = "$zero" ]; then
newrev_type=delete
else
newrev_type=$(git cat-file -t $newrev)
fi
case "$refname","$newrev_type" in
refs/tags/*,commit)
# un-annotated tag
short_refname=${refname##refs/tags/}
if [ "$allowunannotated" != "true" ]; then
echo "*** The un-annotated tag, $short_refname, is not allowed in this repository" >&2
echo "*** Use 'git tag [ -a | -s ]' for tags you want to propagate." >&2
exit 1
fi
;;
refs/tags/*,delete)
# delete tag
if [ "$allowdeletetag" != "true" ]; then
echo "*** Deleting a tag is not allowed in this repository" >&2
exit 1
fi
;;
refs/tags/*,tag)
# annotated tag
if [ "$allowmodifytag" != "true" ] && git rev-parse $refname > /dev/null 2>&1
then
echo "*** Tag '$refname' already exists." >&2
echo "*** Modifying a tag is not allowed in this repository." >&2
exit 1
fi
;;
refs/heads/*,commit)
# branch
if [ "$oldrev" = "$zero" -a "$denycreatebranch" = "true" ]; then
echo "*** Creating a branch is not allowed in this repository" >&2
exit 1
fi
;;
refs/heads/*,delete)
# delete branch
if [ "$allowdeletebranch" != "true" ]; then
echo "*** Deleting a branch is not allowed in this repository" >&2
exit 1
fi
;;
refs/remotes/*,commit)
# tracking branch
;;
refs/remotes/*,delete)
# delete tracking branch
if [ "$allowdeletebranch" != "true" ]; then
echo "*** Deleting a tracking branch is not allowed in this repository" >&2
exit 1
fi
;;
*)
# Anything else (is there anything else?)
echo "*** Update hook: unknown type of update to ref $refname of type $newrev_type" >&2
exit 1
;;
esac
# --- Finished
exit 0

View file

@ -1,6 +0,0 @@
# git ls-files --others --exclude-from=.git/info/exclude
# Lines that start with '#' are comments.
# For a project mostly in C, the following would be a good set of
# exclude patterns (uncomment them if you want to use them):
# *.[oa]
# *~

View file

@ -1 +0,0 @@
0000000000000000000000000000000000000000 c3b63d2fd9c929359231363bcabc880ba29eb96e Christophe Buffenoir <kujiu@pakunoda.kujiu.org> 1357751567 +0100 clone: from https://github.com/scrooloose/nerdtree.git

View file

@ -1 +0,0 @@
0000000000000000000000000000000000000000 c3b63d2fd9c929359231363bcabc880ba29eb96e Christophe Buffenoir <kujiu@pakunoda.kujiu.org> 1357751567 +0100 clone: from https://github.com/scrooloose/nerdtree.git

View file

@ -1 +0,0 @@
0000000000000000000000000000000000000000 c3b63d2fd9c929359231363bcabc880ba29eb96e Christophe Buffenoir <kujiu@pakunoda.kujiu.org> 1357751567 +0100 clone: from https://github.com/scrooloose/nerdtree.git

View file

@ -1,23 +0,0 @@
# pack-refs with: peeled
c3b63d2fd9c929359231363bcabc880ba29eb96e refs/remotes/origin/master
57ccede2509ed5c421cc7baacc9fade973069cd6 refs/remotes/origin/refactor-open
a856622f0cc38223b5147531394829a8456ec566 refs/tags/2.10.0
95ee07c9d377c4e844d95e5272ea0152323b96e1 refs/tags/2.10.0rc1
e6d2f12bf68a55f68ec9d99d467042fc799f677d refs/tags/2.11.0
3cb3227d56959cca112f20973b41327213e3ba5a refs/tags/2.12.0
0620b91efa4041288dc69325c53b3d98e8478536 refs/tags/2.13.0
2ca4573b016a150bdb25e6a81062489e10f59463 refs/tags/2.14.0
5fcdd03f1233305be3277dc72af3d0d1e3a41c9f refs/tags/2.14.1
090791407e0b451838ad4f2eff086945f81a9d54 refs/tags/2.14.2
9aba1c17f6a2803a8cc36ffea3d5ca2f63f544b7 refs/tags/2.14.3
6a77424c253b1e2ec0d8f7d1f00a21b2fb27cb07 refs/tags/2.7.0
a5bc034851fabe38e53527b569860fdc8a4a8cce refs/tags/2.7.1
c008fb3983775f553b06db8f806757677b759113 refs/tags/2.8.0
80e0bca4dc7f5ca80712f501d662efddc83755a7 refs/tags/2.9.0
6f2401346381ccae42a818cb16ac1c52ee6728e2 refs/tags/3.0.0
efe03d6988f6835c857ff27c99ebbe6d158a0c78 refs/tags/3.0.1
bdfac3e25cd37ce757703c569f5abbcd70a2da83 refs/tags/3.1.0
e7ebee3084cfe97b2084782f004a723f83be243f refs/tags/3.1.1
241f2e9dfe866010889c9c190278b95080d944ed refs/tags/4.0.0
153041ac939502746e5a24468910eb7214a3f593 refs/tags/4.1.0
205367ab3f46dcc88b6ebb819a276e793a21e995 refs/tags/4.2.0

View file

@ -1 +0,0 @@
c3b63d2fd9c929359231363bcabc880ba29eb96e

View file

@ -1 +0,0 @@
ref: refs/remotes/origin/master

View file

@ -1,3 +0,0 @@
*~
*.swp
tags

View file

@ -1,108 +0,0 @@
The NERD Tree
=============
Intro
-----
The NERD tree allows you to explore your filesystem and to open files and
directories. It presents the filesystem to you in the form of a tree which you
manipulate with the keyboard and/or mouse. It also allows you to perform
simple filesystem operations.
The following features and functionality are provided by the NERD tree:
* Files and directories are displayed in a hierarchical tree structure
* Different highlighting is provided for the following types of nodes:
* files
* directories
* sym-links
* windows .lnk files
* read-only files
* executable files
* Many (customisable) mappings are provided to manipulate the tree:
* Mappings to open/close/explore directory nodes
* Mappings to open files in new/existing windows/tabs
* Mappings to change the current root of the tree
* Mappings to navigate around the tree
* ...
* Directories and files can be bookmarked.
* Most NERD tree navigation can also be done with the mouse
* Filtering of tree content (can be toggled at runtime)
* custom file filters to prevent e.g. vim backup files being displayed
* optional displaying of hidden files (. files)
* files can be "turned off" so that only directories are displayed
* The position and size of the NERD tree window can be customised
* The order in which the nodes in the tree are listed can be customised.
* A model of your filesystem is created/maintained as you explore it. This
has several advantages:
* All filesystem information is cached and is only re-read on demand
* If you revisit a part of the tree that you left earlier in your
session, the directory nodes will be opened/closed as you left them
* The script remembers the cursor position and window position in the NERD
tree so you can toggle it off (or just close the tree window) and then
reopen it (with NERDTreeToggle) the NERD tree window will appear exactly
as you left it
* You can have a separate NERD tree for each tab, share trees across tabs,
or a mix of both.
* By default the script overrides the default file browser (netw), so if
you :edit a directory a (slighly modified) NERD tree will appear in the
current window
* A programmable menu system is provided (simulates right clicking on a node)
* one default menu plugin is provided to perform basic filesytem
operations (create/delete/move/copy files/directories)
* There's an API for adding your own keymappings
Installation
------------
[pathogen.vim](https://github.com/tpope/vim-pathogen) is the recommended way to install nerdtree.
cd ~/.vim/bundle
git clone https://github.com/scrooloose/nerdtree.git
Then reload vim, run `:helptags`, and check out `:help NERD_tree.txt`.
Faq
---
__Q. Can I have the nerdtree on every tab automatically?__
A. Nope. If this is something you want then chances are you aren't using tabs
and buffers as they were intended to be used. Read this
http://stackoverflow.com/questions/102384/using-vims-tabs-like-buffers
If you are interested in this behaviour then consider [vim-nerdtree-tabs](https://github.com/jistr/vim-nerdtree-tabs)
__Q. How can I open a NERDTree automatically when vim starts up?__
A. Stick this in your vimrc: `autocmd vimenter * NERDTree`
__Q. How can I open a NERDTree automatically when vim starts up if no files were specified?__
A. Stick this in your vimrc `autocmd vimenter * if !argc() | NERDTree | endif`
__Q. How can I map a specific key or shortcut to open NERDTree?__
A. Stick this in your vimrc to open NERDTree with `Ctrl+n` (you can set whatever key you want): `map <C-n> :NERDTreeToggle<CR>`
__Q. How can I close vim if the only window left open is a NERDTree?__
A. Stick this in your vimrc:
`autocmd bufenter * if (winnr("$") == 1 && exists("b:NERDTreeType") && b:NERDTreeType == "primary") | q | endif`
Changelog
---------
4.2.0 (2011-12-28)
* Add NERDTreeDirArrows option to make the UI use pretty arrow chars instead of the old +~| chars to define the tree structure (sickill)
* shift the syntax highlighting out into its own syntax file (gnap) * add some mac specific options to the filesystem menu - for macvim only (andersonfreitas)
* Add NERDTreeMinimalUI option to remove some non functional parts of the nerdtree ui (camthompson)
* tweak the behaviour of :NERDTreeFind - see :help :NERDTreeFind for the new behaviour (benjamingeiger)
* if no name is given to :Bookmark, make it default to the name of the target file/dir (minyoung)
* use 'file' completion when doing copying, create, and move operations (EvanDotPro)
* lots of misc bug fixes (paddyoloughlin, sdewald, camthompson, Vitaly Bogdanov, AndrewRadev, mathias, scottstvnsn, kml, wycats, me RAWR!)

File diff suppressed because it is too large Load diff

File diff suppressed because it is too large Load diff

View file

@ -1,41 +0,0 @@
" ============================================================================
" File: exec_menuitem.vim
" Description: plugin for NERD Tree that provides an execute file menu item
" Maintainer: Martin Grenfell <martin.grenfell at gmail dot com>
" Last Change: 22 July, 2009
" License: This program is free software. It comes without any warranty,
" to the extent permitted by applicable law. You can redistribute
" it and/or modify it under the terms of the Do What The Fuck You
" Want To Public License, Version 2, as published by Sam Hocevar.
" See http://sam.zoy.org/wtfpl/COPYING for more details.
"
" ============================================================================
if exists("g:loaded_nerdtree_exec_menuitem")
finish
endif
let g:loaded_nerdtree_exec_menuitem = 1
call NERDTreeAddMenuItem({
\ 'text': '(!)Execute file',
\ 'shortcut': '!',
\ 'callback': 'NERDTreeExecFile',
\ 'isActiveCallback': 'NERDTreeExecFileActive' })
function! NERDTreeExecFileActive()
let node = g:NERDTreeFileNode.GetSelected()
return !node.path.isDirectory && node.path.isExecutable
endfunction
function! NERDTreeExecFile()
let treenode = g:NERDTreeFileNode.GetSelected()
echo "==========================================================\n"
echo "Complete the command to execute (add arguments etc):\n"
let cmd = treenode.path.str({'escape': 1})
let cmd = input(':!', cmd . ' ')
if cmd != ''
exec ':!' . cmd
else
echo "Aborted"
endif
endfunction

View file

@ -1,262 +0,0 @@
" ============================================================================
" File: fs_menu.vim
" Description: plugin for the NERD Tree that provides a file system menu
" Maintainer: Martin Grenfell <martin.grenfell at gmail dot com>
" Last Change: 17 July, 2009
" License: This program is free software. It comes without any warranty,
" to the extent permitted by applicable law. You can redistribute
" it and/or modify it under the terms of the Do What The Fuck You
" Want To Public License, Version 2, as published by Sam Hocevar.
" See http://sam.zoy.org/wtfpl/COPYING for more details.
"
" ============================================================================
if exists("g:loaded_nerdtree_fs_menu")
finish
endif
let g:loaded_nerdtree_fs_menu = 1
"Automatically delete the buffer after deleting or renaming a file
if !exists("g:NERDTreeAutoDeleteBuffer")
let g:NERDTreeAutoDeleteBuffer = 0
endif
call NERDTreeAddMenuItem({'text': '(a)dd a childnode', 'shortcut': 'a', 'callback': 'NERDTreeAddNode'})
call NERDTreeAddMenuItem({'text': '(m)ove the current node', 'shortcut': 'm', 'callback': 'NERDTreeMoveNode'})
call NERDTreeAddMenuItem({'text': '(d)elete the current node', 'shortcut': 'd', 'callback': 'NERDTreeDeleteNode'})
if has("gui_mac") || has("gui_macvim")
call NERDTreeAddMenuItem({'text': '(r)eveal in Finder the current node', 'shortcut': 'r', 'callback': 'NERDTreeRevealInFinder'})
call NERDTreeAddMenuItem({'text': '(o)pen the current node with system editor', 'shortcut': 'o', 'callback': 'NERDTreeExecuteFile'})
call NERDTreeAddMenuItem({'text': '(q)uicklook the current node', 'shortcut': 'q', 'callback': 'NERDTreeQuickLook'})
endif
if g:NERDTreePath.CopyingSupported()
call NERDTreeAddMenuItem({'text': '(c)opy the current node', 'shortcut': 'c', 'callback': 'NERDTreeCopyNode'})
endif
"FUNCTION: s:echo(msg){{{1
function! s:echo(msg)
redraw
echomsg "NERDTree: " . a:msg
endfunction
"FUNCTION: s:echoWarning(msg){{{1
function! s:echoWarning(msg)
echohl warningmsg
call s:echo(a:msg)
echohl normal
endfunction
"FUNCTION: s:promptToDelBuffer(bufnum, msg){{{1
"prints out the given msg and, if the user responds by pushing 'y' then the
"buffer with the given bufnum is deleted
"
"Args:
"bufnum: the buffer that may be deleted
"msg: a message that will be echoed to the user asking them if they wish to
" del the buffer
function! s:promptToDelBuffer(bufnum, msg)
echo a:msg
if g:NERDTreeAutoDeleteBuffer || nr2char(getchar()) ==# 'y'
" 1. ensure that all windows which display the just deleted filename
" now display an empty buffer (so a layout is preserved).
" Is not it better to close single tabs with this file only ?
let s:originalTabNumber = tabpagenr()
let s:originalWindowNumber = winnr()
exec "tabdo windo if winbufnr(0) == " . a:bufnum . " | exec ':enew! ' | endif"
exec "tabnext " . s:originalTabNumber
exec s:originalWindowNumber . "wincmd w"
" 3. We don't need a previous buffer anymore
exec "bwipeout! " . a:bufnum
endif
endfunction
"FUNCTION: s:promptToRenameBuffer(bufnum, msg){{{1
"prints out the given msg and, if the user responds by pushing 'y' then the
"buffer with the given bufnum is replaced with a new one
"
"Args:
"bufnum: the buffer that may be deleted
"msg: a message that will be echoed to the user asking them if they wish to
" del the buffer
function! s:promptToRenameBuffer(bufnum, msg, newFileName)
echo a:msg
if g:NERDTreeAutoDeleteBuffer || nr2char(getchar()) ==# 'y'
" 1. ensure that a new buffer is loaded
exec "badd " . a:newFileName
" 2. ensure that all windows which display the just deleted filename
" display a buffer for a new filename.
let s:originalTabNumber = tabpagenr()
let s:originalWindowNumber = winnr()
exec "tabdo windo if winbufnr(0) == " . a:bufnum . " | exec ':e! " . a:newFileName . "' | endif"
exec "tabnext " . s:originalTabNumber
exec s:originalWindowNumber . "wincmd w"
" 3. We don't need a previous buffer anymore
exec "bwipeout! " . a:bufnum
endif
endfunction
"FUNCTION: NERDTreeAddNode(){{{1
function! NERDTreeAddNode()
let curDirNode = g:NERDTreeDirNode.GetSelected()
let newNodeName = input("Add a childnode\n".
\ "==========================================================\n".
\ "Enter the dir/file name to be created. Dirs end with a '/'\n" .
\ "", curDirNode.path.str() . g:NERDTreePath.Slash(), "file")
if newNodeName ==# ''
call s:echo("Node Creation Aborted.")
return
endif
try
let newPath = g:NERDTreePath.Create(newNodeName)
let parentNode = b:NERDTreeRoot.findNode(newPath.getParent())
let newTreeNode = g:NERDTreeFileNode.New(newPath)
if parentNode.isOpen || !empty(parentNode.children)
call parentNode.addChild(newTreeNode, 1)
call NERDTreeRender()
call newTreeNode.putCursorHere(1, 0)
endif
catch /^NERDTree/
call s:echoWarning("Node Not Created.")
endtry
endfunction
"FUNCTION: NERDTreeMoveNode(){{{1
function! NERDTreeMoveNode()
let curNode = g:NERDTreeFileNode.GetSelected()
let newNodePath = input("Rename the current node\n" .
\ "==========================================================\n" .
\ "Enter the new path for the node: \n" .
\ "", curNode.path.str(), "file")
if newNodePath ==# ''
call s:echo("Node Renaming Aborted.")
return
endif
try
let bufnum = bufnr(curNode.path.str())
call curNode.rename(newNodePath)
call NERDTreeRender()
"if the node is open in a buffer, ask the user if they want to
"close that buffer
if bufnum != -1
let prompt = "\nNode renamed.\n\nThe old file is open in buffer ". bufnum . (bufwinnr(bufnum) ==# -1 ? " (hidden)" : "") .". Replace this buffer with a new file? (yN)"
call s:promptToRenameBuffer(bufnum, prompt, newNodePath)
endif
call curNode.putCursorHere(1, 0)
redraw
catch /^NERDTree/
call s:echoWarning("Node Not Renamed.")
endtry
endfunction
" FUNCTION: NERDTreeDeleteNode() {{{1
function! NERDTreeDeleteNode()
let currentNode = g:NERDTreeFileNode.GetSelected()
let confirmed = 0
if currentNode.path.isDirectory
let choice =input("Delete the current node\n" .
\ "==========================================================\n" .
\ "STOP! To delete this entire directory, type 'yes'\n" .
\ "" . currentNode.path.str() . ": ")
let confirmed = choice ==# 'yes'
else
echo "Delete the current node\n" .
\ "==========================================================\n".
\ "Are you sure you wish to delete the node:\n" .
\ "" . currentNode.path.str() . " (yN):"
let choice = nr2char(getchar())
let confirmed = choice ==# 'y'
endif
if confirmed
try
call currentNode.delete()
call NERDTreeRender()
"if the node is open in a buffer, ask the user if they want to
"close that buffer
let bufnum = bufnr(currentNode.path.str())
if buflisted(bufnum)
let prompt = "\nNode deleted.\n\nThe file is open in buffer ". bufnum . (bufwinnr(bufnum) ==# -1 ? " (hidden)" : "") .". Delete this buffer? (yN)"
call s:promptToDelBuffer(bufnum, prompt)
endif
redraw
catch /^NERDTree/
call s:echoWarning("Could not remove node")
endtry
else
call s:echo("delete aborted")
endif
endfunction
" FUNCTION: NERDTreeCopyNode() {{{1
function! NERDTreeCopyNode()
let currentNode = g:NERDTreeFileNode.GetSelected()
let newNodePath = input("Copy the current node\n" .
\ "==========================================================\n" .
\ "Enter the new path to copy the node to: \n" .
\ "", currentNode.path.str(), "file")
if newNodePath != ""
"strip trailing slash
let newNodePath = substitute(newNodePath, '\/$', '', '')
let confirmed = 1
if currentNode.path.copyingWillOverwrite(newNodePath)
call s:echo("Warning: copying may overwrite files! Continue? (yN)")
let choice = nr2char(getchar())
let confirmed = choice ==# 'y'
endif
if confirmed
try
let newNode = currentNode.copy(newNodePath)
if !empty(newNode)
call NERDTreeRender()
call newNode.putCursorHere(0, 0)
endif
catch /^NERDTree/
call s:echoWarning("Could not copy node")
endtry
endif
else
call s:echo("Copy aborted.")
endif
redraw
endfunction
function! NERDTreeQuickLook()
let treenode = g:NERDTreeFileNode.GetSelected()
if treenode != {}
call system("qlmanage -p 2>/dev/null '" . treenode.path.str() . "'")
endif
endfunction
function! NERDTreeRevealInFinder()
let treenode = g:NERDTreeFileNode.GetSelected()
if treenode != {}
let x = system("open -R '" . treenode.path.str() . "'")
endif
endfunction
function! NERDTreeExecuteFile()
let treenode = g:NERDTreeFileNode.GetSelected()
if treenode != {}
let x = system("open '" . treenode.path.str() . "'")
endif
endfunction
" vim: set sw=4 sts=4 et fdm=marker:

View file

@ -1,218 +0,0 @@
" ============================================================================
" File: NERD_tree.vim
" Description: vim global plugin that provides a nice tree explorer
" Maintainer: Martin Grenfell <martin.grenfell at gmail dot com>
" Last Change: 28 December, 2011
" License: This program is free software. It comes without any warranty,
" to the extent permitted by applicable law. You can redistribute
" it and/or modify it under the terms of the Do What The Fuck You
" Want To Public License, Version 2, as published by Sam Hocevar.
" See http://sam.zoy.org/wtfpl/COPYING for more details.
"
" ============================================================================
"
" SECTION: Script init stuff {{{1
"============================================================
if exists("loaded_nerd_tree")
finish
endif
if v:version < 700
echoerr "NERDTree: this plugin requires vim >= 7. DOWNLOAD IT! You'll thank me later!"
finish
endif
let loaded_nerd_tree = 1
"for line continuation - i.e dont want C in &cpo
let s:old_cpo = &cpo
set cpo&vim
"Function: s:initVariable() function {{{2
"This function is used to initialise a given variable to a given value. The
"variable is only initialised if it does not exist prior
"
"Args:
"var: the name of the var to be initialised
"value: the value to initialise var to
"
"Returns:
"1 if the var is set, 0 otherwise
function! s:initVariable(var, value)
if !exists(a:var)
exec 'let ' . a:var . ' = ' . "'" . substitute(a:value, "'", "''", "g") . "'"
return 1
endif
return 0
endfunction
"SECTION: Init variable calls and other random constants {{{2
call s:initVariable("g:NERDChristmasTree", 1)
call s:initVariable("g:NERDTreeAutoCenter", 1)
call s:initVariable("g:NERDTreeAutoCenterThreshold", 3)
call s:initVariable("g:NERDTreeCaseSensitiveSort", 0)
call s:initVariable("g:NERDTreeChDirMode", 0)
call s:initVariable("g:NERDTreeMinimalUI", 0)
if !exists("g:NERDTreeIgnore")
let g:NERDTreeIgnore = ['\~$']
endif
call s:initVariable("g:NERDTreeBookmarksFile", expand('$HOME') . '/.NERDTreeBookmarks')
call s:initVariable("g:NERDTreeHighlightCursorline", 1)
call s:initVariable("g:NERDTreeHijackNetrw", 1)
call s:initVariable("g:NERDTreeMouseMode", 1)
call s:initVariable("g:NERDTreeNotificationThreshold", 100)
call s:initVariable("g:NERDTreeQuitOnOpen", 0)
call s:initVariable("g:NERDTreeShowBookmarks", 0)
call s:initVariable("g:NERDTreeShowFiles", 1)
call s:initVariable("g:NERDTreeShowHidden", 0)
call s:initVariable("g:NERDTreeShowLineNumbers", 0)
call s:initVariable("g:NERDTreeSortDirs", 1)
call s:initVariable("g:NERDTreeDirArrows", !nerdtree#runningWindows())
call s:initVariable("g:NERDTreeCasadeOpenSingleChildDir", 1)
if !exists("g:NERDTreeSortOrder")
let g:NERDTreeSortOrder = ['\/$', '*', '\.swp$', '\.bak$', '\~$']
else
"if there isnt a * in the sort sequence then add one
if count(g:NERDTreeSortOrder, '*') < 1
call add(g:NERDTreeSortOrder, '*')
endif
endif
if !exists('g:NERDTreeStatusline')
"the exists() crap here is a hack to stop vim spazzing out when
"loading a session that was created with an open nerd tree. It spazzes
"because it doesnt store b:NERDTreeRoot (its a b: var, and its a hash)
let g:NERDTreeStatusline = "%{exists('b:NERDTreeRoot')?b:NERDTreeRoot.path.str():''}"
endif
call s:initVariable("g:NERDTreeWinPos", "left")
call s:initVariable("g:NERDTreeWinSize", 31)
"init the shell commands that will be used to copy nodes, and remove dir trees
"
"Note: the space after the command is important
if nerdtree#runningWindows()
call s:initVariable("g:NERDTreeRemoveDirCmd", 'rmdir /s /q ')
else
call s:initVariable("g:NERDTreeRemoveDirCmd", 'rm -rf ')
call s:initVariable("g:NERDTreeCopyCmd", 'cp -r ')
endif
"SECTION: Init variable calls for key mappings {{{2
call s:initVariable("g:NERDTreeMapActivateNode", "o")
call s:initVariable("g:NERDTreeMapChangeRoot", "C")
call s:initVariable("g:NERDTreeMapChdir", "cd")
call s:initVariable("g:NERDTreeMapCloseChildren", "X")
call s:initVariable("g:NERDTreeMapCloseDir", "x")
call s:initVariable("g:NERDTreeMapDeleteBookmark", "D")
call s:initVariable("g:NERDTreeMapMenu", "m")
call s:initVariable("g:NERDTreeMapHelp", "?")
call s:initVariable("g:NERDTreeMapJumpFirstChild", "K")
call s:initVariable("g:NERDTreeMapJumpLastChild", "J")
call s:initVariable("g:NERDTreeMapJumpNextSibling", "<C-j>")
call s:initVariable("g:NERDTreeMapJumpParent", "p")
call s:initVariable("g:NERDTreeMapJumpPrevSibling", "<C-k>")
call s:initVariable("g:NERDTreeMapJumpRoot", "P")
call s:initVariable("g:NERDTreeMapOpenExpl", "e")
call s:initVariable("g:NERDTreeMapOpenInTab", "t")
call s:initVariable("g:NERDTreeMapOpenInTabSilent", "T")
call s:initVariable("g:NERDTreeMapOpenRecursively", "O")
call s:initVariable("g:NERDTreeMapOpenSplit", "i")
call s:initVariable("g:NERDTreeMapOpenVSplit", "s")
call s:initVariable("g:NERDTreeMapPreview", "g" . NERDTreeMapActivateNode)
call s:initVariable("g:NERDTreeMapPreviewSplit", "g" . NERDTreeMapOpenSplit)
call s:initVariable("g:NERDTreeMapPreviewVSplit", "g" . NERDTreeMapOpenVSplit)
call s:initVariable("g:NERDTreeMapQuit", "q")
call s:initVariable("g:NERDTreeMapRefresh", "r")
call s:initVariable("g:NERDTreeMapRefreshRoot", "R")
call s:initVariable("g:NERDTreeMapToggleBookmarks", "B")
call s:initVariable("g:NERDTreeMapToggleFiles", "F")
call s:initVariable("g:NERDTreeMapToggleFilters", "f")
call s:initVariable("g:NERDTreeMapToggleHidden", "I")
call s:initVariable("g:NERDTreeMapToggleZoom", "A")
call s:initVariable("g:NERDTreeMapUpdir", "u")
call s:initVariable("g:NERDTreeMapUpdirKeepOpen", "U")
call s:initVariable("g:NERDTreeMapCWD", "CD")
"SECTION: Load class files{{{2
runtime plugin/nerdtree/path.vim
runtime plugin/nerdtree/menu_controller.vim
runtime plugin/nerdtree/menu_item.vim
runtime plugin/nerdtree/key_map.vim
runtime plugin/nerdtree/bookmark.vim
runtime plugin/nerdtree/tree_file_node.vim
runtime plugin/nerdtree/tree_dir_node.vim
runtime plugin/nerdtree/opener.vim
runtime plugin/nerdtree/creator.vim
" SECTION: Commands {{{1
"============================================================
"init the command that users start the nerd tree with
command! -n=? -complete=dir -bar NERDTree :call g:NERDTreeCreator.CreatePrimary('<args>')
command! -n=? -complete=dir -bar NERDTreeToggle :call g:NERDTreeCreator.TogglePrimary('<args>')
command! -n=0 -bar NERDTreeClose :call nerdtree#closeTreeIfOpen()
command! -n=1 -complete=customlist,nerdtree#completeBookmarks -bar NERDTreeFromBookmark call g:NERDTreeCreator.CreatePrimary('<args>')
command! -n=0 -bar NERDTreeMirror call g:NERDTreeCreator.CreateMirror()
command! -n=0 -bar NERDTreeFind call nerdtree#findAndRevealPath()
command! -n=0 -bar NERDTreeFocus call NERDTreeFocus()
command! -n=0 -bar NERDTreeCWD call NERDTreeCWD()
" SECTION: Auto commands {{{1
"============================================================
augroup NERDTree
"Save the cursor position whenever we close the nerd tree
exec "autocmd BufWinLeave ". nerdtree#bufNamePrefix() ."* call nerdtree#saveScreenState()"
"disallow insert mode in the NERDTree
exec "autocmd BufEnter ". nerdtree#bufNamePrefix() ."* stopinsert"
augroup END
if g:NERDTreeHijackNetrw
augroup NERDTreeHijackNetrw
autocmd VimEnter * silent! autocmd! FileExplorer
au BufEnter,VimEnter * call nerdtree#checkForBrowse(expand("<amatch>"))
augroup END
endif
" SECTION: Public API {{{1
"============================================================
function! NERDTreeAddMenuItem(options)
call g:NERDTreeMenuItem.Create(a:options)
endfunction
function! NERDTreeAddMenuSeparator(...)
let opts = a:0 ? a:1 : {}
call g:NERDTreeMenuItem.CreateSeparator(opts)
endfunction
function! NERDTreeAddSubmenu(options)
return g:NERDTreeMenuItem.Create(a:options)
endfunction
function! NERDTreeAddKeyMap(options)
call g:NERDTreeKeyMap.Create(a:options)
endfunction
function! NERDTreeRender()
call nerdtree#renderView()
endfunction
function! NERDTreeFocus()
if nerdtree#isTreeOpen()
call nerdtree#putCursorInTreeWin()
else
call g:NERDTreeCreator.TogglePrimary("")
endif
endfunction
function! NERDTreeCWD()
call NERDTreeFocus()
call nerdtree#chRootCwd()
endfunction
" SECTION: Post Source Actions {{{1
call nerdtree#postSourceActions()
"reset &cpo back to users setting
let &cpo = s:old_cpo
" vim: set sw=4 sts=4 et fdm=marker:

View file

@ -1,315 +0,0 @@
"CLASS: Bookmark
"============================================================
let s:Bookmark = {}
let g:NERDTreeBookmark = s:Bookmark
" FUNCTION: Bookmark.activate() {{{1
function! s:Bookmark.activate(...)
call self.open(a:0 ? a:1 : {})
endfunction
" FUNCTION: Bookmark.AddBookmark(name, path) {{{1
" Class method to add a new bookmark to the list, if a previous bookmark exists
" with the same name, just update the path for that bookmark
function! s:Bookmark.AddBookmark(name, path)
for i in s:Bookmark.Bookmarks()
if i.name ==# a:name
let i.path = a:path
return
endif
endfor
call add(s:Bookmark.Bookmarks(), s:Bookmark.New(a:name, a:path))
call s:Bookmark.Sort()
endfunction
" FUNCTION: Bookmark.Bookmarks() {{{1
" Class method to get all bookmarks. Lazily initializes the bookmarks global
" variable
function! s:Bookmark.Bookmarks()
if !exists("g:NERDTreeBookmarks")
let g:NERDTreeBookmarks = []
endif
return g:NERDTreeBookmarks
endfunction
" FUNCTION: Bookmark.BookmarkExistsFor(name) {{{1
" class method that returns 1 if a bookmark with the given name is found, 0
" otherwise
function! s:Bookmark.BookmarkExistsFor(name)
try
call s:Bookmark.BookmarkFor(a:name)
return 1
catch /^NERDTree.BookmarkNotFoundError/
return 0
endtry
endfunction
" FUNCTION: Bookmark.BookmarkFor(name) {{{1
" Class method to get the bookmark that has the given name. {} is return if no
" bookmark is found
function! s:Bookmark.BookmarkFor(name)
for i in s:Bookmark.Bookmarks()
if i.name ==# a:name
return i
endif
endfor
throw "NERDTree.BookmarkNotFoundError: no bookmark found for name: \"". a:name .'"'
endfunction
" FUNCTION: Bookmark.BookmarkNames() {{{1
" Class method to return an array of all bookmark names
function! s:Bookmark.BookmarkNames()
let names = []
for i in s:Bookmark.Bookmarks()
call add(names, i.name)
endfor
return names
endfunction
" FUNCTION: Bookmark.CacheBookmarks(silent) {{{1
" Class method to read all bookmarks from the bookmarks file intialize
" bookmark objects for each one.
"
" Args:
" silent - dont echo an error msg if invalid bookmarks are found
function! s:Bookmark.CacheBookmarks(silent)
if filereadable(g:NERDTreeBookmarksFile)
let g:NERDTreeBookmarks = []
let g:NERDTreeInvalidBookmarks = []
let bookmarkStrings = readfile(g:NERDTreeBookmarksFile)
let invalidBookmarksFound = 0
for i in bookmarkStrings
"ignore blank lines
if i != ''
let name = substitute(i, '^\(.\{-}\) .*$', '\1', '')
let path = substitute(i, '^.\{-} \(.*\)$', '\1', '')
try
let bookmark = s:Bookmark.New(name, g:NERDTreePath.New(path))
call add(g:NERDTreeBookmarks, bookmark)
catch /^NERDTree.InvalidArgumentsError/
call add(g:NERDTreeInvalidBookmarks, i)
let invalidBookmarksFound += 1
endtry
endif
endfor
if invalidBookmarksFound
call s:Bookmark.Write()
if !a:silent
call nerdtree#echo(invalidBookmarksFound . " invalid bookmarks were read. See :help NERDTreeInvalidBookmarks for info.")
endif
endif
call s:Bookmark.Sort()
endif
endfunction
" FUNCTION: Bookmark.compareTo(otherbookmark) {{{1
" Compare these two bookmarks for sorting purposes
function! s:Bookmark.compareTo(otherbookmark)
return a:otherbookmark.name < self.name
endfunction
" FUNCTION: Bookmark.ClearAll() {{{1
" Class method to delete all bookmarks.
function! s:Bookmark.ClearAll()
for i in s:Bookmark.Bookmarks()
call i.delete()
endfor
call s:Bookmark.Write()
endfunction
" FUNCTION: Bookmark.delete() {{{1
" Delete this bookmark. If the node for this bookmark is under the current
" root, then recache bookmarks for its Path object
function! s:Bookmark.delete()
let node = {}
try
let node = self.getNode(1)
catch /^NERDTree.BookmarkedNodeNotFoundError/
endtry
call remove(s:Bookmark.Bookmarks(), index(s:Bookmark.Bookmarks(), self))
if !empty(node)
call node.path.cacheDisplayString()
endif
call s:Bookmark.Write()
endfunction
" FUNCTION: Bookmark.getNode(searchFromAbsoluteRoot) {{{1
" Gets the treenode for this bookmark
"
" Args:
" searchFromAbsoluteRoot: specifies whether we should search from the current
" tree root, or the highest cached node
function! s:Bookmark.getNode(searchFromAbsoluteRoot)
let searchRoot = a:searchFromAbsoluteRoot ? g:NERDTreeDirNode.AbsoluteTreeRoot() : b:NERDTreeRoot
let targetNode = searchRoot.findNode(self.path)
if empty(targetNode)
throw "NERDTree.BookmarkedNodeNotFoundError: no node was found for bookmark: " . self.name
endif
return targetNode
endfunction
" FUNCTION: Bookmark.GetNodeForName(name, searchFromAbsoluteRoot) {{{1
" Class method that finds the bookmark with the given name and returns the
" treenode for it.
function! s:Bookmark.GetNodeForName(name, searchFromAbsoluteRoot)
let bookmark = s:Bookmark.BookmarkFor(a:name)
return bookmark.getNode(a:searchFromAbsoluteRoot)
endfunction
" FUNCTION: Bookmark.GetSelected() {{{1
" returns the Bookmark the cursor is over, or {}
function! s:Bookmark.GetSelected()
let line = getline(".")
let name = substitute(line, '^>\(.\{-}\) .\+$', '\1', '')
if name != line
try
return s:Bookmark.BookmarkFor(name)
catch /^NERDTree.BookmarkNotFoundError/
return {}
endtry
endif
return {}
endfunction
" FUNCTION: Bookmark.InvalidBookmarks() {{{1
" Class method to get all invalid bookmark strings read from the bookmarks
" file
function! s:Bookmark.InvalidBookmarks()
if !exists("g:NERDTreeInvalidBookmarks")
let g:NERDTreeInvalidBookmarks = []
endif
return g:NERDTreeInvalidBookmarks
endfunction
" FUNCTION: Bookmark.mustExist() {{{1
function! s:Bookmark.mustExist()
if !self.path.exists()
call s:Bookmark.CacheBookmarks(1)
throw "NERDTree.BookmarkPointsToInvalidLocationError: the bookmark \"".
\ self.name ."\" points to a non existing location: \"". self.path.str()
endif
endfunction
" FUNCTION: Bookmark.New(name, path) {{{1
" Create a new bookmark object with the given name and path object
function! s:Bookmark.New(name, path)
if a:name =~# ' '
throw "NERDTree.IllegalBookmarkNameError: illegal name:" . a:name
endif
let newBookmark = copy(self)
let newBookmark.name = a:name
let newBookmark.path = a:path
return newBookmark
endfunction
" FUNCTION: Bookmark.open([options]) {{{1
"Args:
"A dictionary containing the following keys (all optional):
" 'where': Specifies whether the node should be opened in new split/tab or in
" the previous window. Can be either 'v' (vertical split), 'h'
" (horizontal split), 't' (new tab) or 'p' (previous window).
" 'reuse': if a window is displaying the file then jump the cursor there
" 'keepopen': dont close the tree window
" 'stay': open the file, but keep the cursor in the tree win
"
function! s:Bookmark.open(...)
let opts = a:0 ? a:1 : {}
if self.path.isDirectory && !has_key(opts, 'where')
call self.toRoot()
else
let opener = g:NERDTreeOpener.New(self.path, opts)
call opener.open(self)
endif
endfunction
" FUNCTION: Bookmark.openInNewTab(options) {{{1
" Create a new bookmark object with the given name and path object
function! s:Bookmark.openInNewTab(options)
call nerdtree#deprecated('Bookmark.openInNewTab', 'is deprecated, use open() instead')
call self.open(a:options)
endfunction
" FUNCTION: Bookmark.setPath(path) {{{1
" makes this bookmark point to the given path
function! s:Bookmark.setPath(path)
let self.path = a:path
endfunction
" FUNCTION: Bookmark.Sort() {{{1
" Class method that sorts all bookmarks
function! s:Bookmark.Sort()
let CompareFunc = function("nerdtree#compareBookmarks")
call sort(s:Bookmark.Bookmarks(), CompareFunc)
endfunction
" FUNCTION: Bookmark.str() {{{1
" Get the string that should be rendered in the view for this bookmark
function! s:Bookmark.str()
let pathStrMaxLen = winwidth(nerdtree#getTreeWinNum()) - 4 - len(self.name)
if &nu
let pathStrMaxLen = pathStrMaxLen - &numberwidth
endif
let pathStr = self.path.str({'format': 'UI'})
if len(pathStr) > pathStrMaxLen
let pathStr = '<' . strpart(pathStr, len(pathStr) - pathStrMaxLen)
endif
return '>' . self.name . ' ' . pathStr
endfunction
" FUNCTION: Bookmark.toRoot() {{{1
" Make the node for this bookmark the new tree root
function! s:Bookmark.toRoot()
if self.validate()
try
let targetNode = self.getNode(1)
catch /^NERDTree.BookmarkedNodeNotFoundError/
let targetNode = g:NERDTreeFileNode.New(s:Bookmark.BookmarkFor(self.name).path)
endtry
call targetNode.makeRoot()
call nerdtree#renderView()
call targetNode.putCursorHere(0, 0)
endif
endfunction
" FUNCTION: Bookmark.ToRoot(name) {{{1
" Make the node for this bookmark the new tree root
function! s:Bookmark.ToRoot(name)
let bookmark = s:Bookmark.BookmarkFor(a:name)
call bookmark.toRoot()
endfunction
" FUNCTION: Bookmark.validate() {{{1
function! s:Bookmark.validate()
if self.path.exists()
return 1
else
call s:Bookmark.CacheBookmarks(1)
call nerdtree#renderView()
call nerdtree#echo(self.name . "now points to an invalid location. See :help NERDTreeInvalidBookmarks for info.")
return 0
endif
endfunction
" FUNCTION: Bookmark.Write() {{{1
" Class method to write all bookmarks to the bookmarks file
function! s:Bookmark.Write()
let bookmarkStrings = []
for i in s:Bookmark.Bookmarks()
call add(bookmarkStrings, i.name . ' ' . i.path.str())
endfor
"add a blank line before the invalid ones
call add(bookmarkStrings, "")
for j in s:Bookmark.InvalidBookmarks()
call add(bookmarkStrings, j)
endfor
call writefile(bookmarkStrings, g:NERDTreeBookmarksFile)
endfunction
" vim: set sw=4 sts=4 et fdm=marker:

View file

@ -1,298 +0,0 @@
"CLASS: Creator
"Creates primary/secondary/mirror nerdtree windows. Sets up all the window and
"buffer options and key mappings etc.
"============================================================
let s:Creator = {}
let g:NERDTreeCreator = s:Creator
"FUNCTION: s:Creator._bindMappings() {{{1
function! s:Creator._bindMappings()
call g:NERDTreeKeyMap.BindAll()
"make <cr> do the same as the default 'o' mapping
exec "nnoremap <silent> <buffer> <cr> :call nerdtree#invokeKeyMap('". g:NERDTreeMapActivateNode ."')<cr>"
command! -buffer -nargs=? Bookmark :call nerdtree#bookmarkNode('<args>')
command! -buffer -complete=customlist,nerdtree#completeBookmarks -nargs=1 RevealBookmark :call nerdtree#revealBookmark('<args>')
command! -buffer -complete=customlist,nerdtree#completeBookmarks -nargs=1 OpenBookmark :call nerdtree#openBookmark('<args>')
command! -buffer -complete=customlist,nerdtree#completeBookmarks -nargs=* ClearBookmarks call nerdtree#clearBookmarks('<args>')
command! -buffer -complete=customlist,nerdtree#completeBookmarks -nargs=+ BookmarkToRoot call g:NERDTreeBookmark.ToRoot('<args>')
command! -buffer -nargs=0 ClearAllBookmarks call g:NERDTreeBookmark.ClearAll() <bar> call nerdtree#renderView()
command! -buffer -nargs=0 ReadBookmarks call g:NERDTreeBookmark.CacheBookmarks(0) <bar> call nerdtree#renderView()
command! -buffer -nargs=0 WriteBookmarks call g:NERDTreeBookmark.Write()
endfunction
"FUNCTION: s:Creator._broadcastInitEvent() {{{1
function! s:Creator._broadcastInitEvent()
silent doautocmd User NERDTreeInit
endfunction
"FUNCTION: s:Creator.CreatePrimary(a:name) {{{1
function! s:Creator.CreatePrimary(name)
let creator = s:Creator.New()
call creator.createPrimary(a:name)
endfunction
"FUNCTION: s:Creator.createPrimary(a:name) {{{1
"name: the name of a bookmark or a directory
function! s:Creator.createPrimary(name)
let path = self._pathForString(a:name)
"if instructed to, then change the vim CWD to the dir the NERDTree is
"inited in
if g:NERDTreeChDirMode != 0
call path.changeToDir()
endif
if nerdtree#treeExistsForTab()
if nerdtree#isTreeOpen()
call nerdtree#closeTree()
endif
unlet t:NERDTreeBufName
endif
let newRoot = g:NERDTreeDirNode.New(path)
call newRoot.open()
call self._createTreeWin()
let b:treeShowHelp = 0
let b:NERDTreeIgnoreEnabled = 1
let b:NERDTreeShowFiles = g:NERDTreeShowFiles
let b:NERDTreeShowHidden = g:NERDTreeShowHidden
let b:NERDTreeShowBookmarks = g:NERDTreeShowBookmarks
let b:NERDTreeRoot = newRoot
let b:NERDTreeType = "primary"
call nerdtree#renderView()
call b:NERDTreeRoot.putCursorHere(0, 0)
call self._broadcastInitEvent()
endfunction
"FUNCTION: s:Creator.CreateSecondary(dir) {{{1
function! s:Creator.CreateSecondary(dir)
let creator = s:Creator.New()
call creator.createSecondary(a:dir)
endfunction
"FUNCTION: s:Creator.createSecondary(dir) {{{1
function! s:Creator.createSecondary(dir)
try
let path = g:NERDTreePath.New(a:dir)
catch /^NERDTree.InvalidArgumentsError/
call nerdtree#echo("Invalid directory name:" . a:name)
return
endtry
"we want the directory buffer to disappear when we do the :edit below
setlocal bufhidden=wipe
let previousBuf = expand("#")
"we need a unique name for each secondary tree buffer to ensure they are
"all independent
exec "silent edit " . nerdtree#nextBufferName()
let b:NERDTreePreviousBuf = bufnr(previousBuf)
let b:NERDTreeRoot = g:NERDTreeDirNode.New(path)
call b:NERDTreeRoot.open()
call self._setCommonBufOptions()
let b:NERDTreeType = "secondary"
call nerdtree#renderView()
call self._broadcastInitEvent()
endfunction
" FUNCTION: s:Creator.CreateMirror() {{{1
function! s:Creator.CreateMirror()
let creator = s:Creator.New()
call creator.createMirror()
endfunction
" FUNCTION: s:Creator.createMirror() {{{1
function! s:Creator.createMirror()
"get the names off all the nerd tree buffers
let treeBufNames = []
for i in range(1, tabpagenr("$"))
let nextName = nerdtree#tabpagevar(i, 'NERDTreeBufName')
if nextName != -1 && (!exists("t:NERDTreeBufName") || nextName != t:NERDTreeBufName)
call add(treeBufNames, nextName)
endif
endfor
let treeBufNames = nerdtree#unique(treeBufNames)
"map the option names (that the user will be prompted with) to the nerd
"tree buffer names
let options = {}
let i = 0
while i < len(treeBufNames)
let bufName = treeBufNames[i]
let treeRoot = getbufvar(bufName, "NERDTreeRoot")
let options[i+1 . '. ' . treeRoot.path.str() . ' (buf name: ' . bufName . ')'] = bufName
let i = i + 1
endwhile
"work out which tree to mirror, if there is more than 1 then ask the user
let bufferName = ''
if len(keys(options)) > 1
let choices = ["Choose a tree to mirror"]
let choices = extend(choices, sort(keys(options)))
let choice = inputlist(choices)
if choice < 1 || choice > len(options) || choice ==# ''
return
endif
let bufferName = options[sort(keys(options))[choice-1]]
elseif len(keys(options)) ==# 1
let bufferName = values(options)[0]
else
call nerdtree#echo("No trees to mirror")
return
endif
if nerdtree#treeExistsForTab() && nerdtree#isTreeOpen()
call nerdtree#closeTree()
endif
let t:NERDTreeBufName = bufferName
call self._createTreeWin()
exec 'buffer ' . bufferName
if !&hidden
call nerdtree#renderView()
endif
endfunction
"FUNCTION: s:Creator._createTreeWin() {{{1
"Inits the NERD tree window. ie. opens it, sizes it, sets all the local
"options etc
function! s:Creator._createTreeWin()
"create the nerd tree window
let splitLocation = g:NERDTreeWinPos ==# "left" ? "topleft " : "botright "
let splitSize = g:NERDTreeWinSize
if !exists('t:NERDTreeBufName')
let t:NERDTreeBufName = nerdtree#nextBufferName()
silent! exec splitLocation . 'vertical ' . splitSize . ' new'
silent! exec "edit " . t:NERDTreeBufName
else
silent! exec splitLocation . 'vertical ' . splitSize . ' split'
silent! exec "buffer " . t:NERDTreeBufName
endif
setlocal winfixwidth
call self._setCommonBufOptions()
endfunction
"FUNCTION: s:Creator.New() {{{1
function! s:Creator.New()
let newCreator = copy(self)
return newCreator
endfunction
"FUNCTION: s:Creator._pathForString(str) {{{1
"find a bookmark or adirectory for the given string
function! s:Creator._pathForString(str)
let path = {}
if g:NERDTreeBookmark.BookmarkExistsFor(a:str)
let path = g:NERDTreeBookmark.BookmarkFor(a:str).path
else
let dir = a:str ==# '' ? getcwd() : a:str
"hack to get an absolute path if a relative path is given
if dir =~# '^\.'
let dir = getcwd() . g:NERDTreePath.Slash() . dir
endif
let dir = g:NERDTreePath.Resolve(dir)
try
let path = g:NERDTreePath.New(dir)
catch /^NERDTree.InvalidArgumentsError/
call nerdtree#echo("No bookmark or directory found for: " . a:str)
return
endtry
endif
if !path.isDirectory
let path = path.getParent()
endif
return path
endfunction
"FUNCTION: s:Creator._setCommonBufOptions() {{{1
function! s:Creator._setCommonBufOptions()
"throwaway buffer options
setlocal noswapfile
setlocal buftype=nofile
setlocal bufhidden=hide
setlocal nowrap
setlocal foldcolumn=0
setlocal foldmethod=manual
setlocal nofoldenable
setlocal nobuflisted
setlocal nospell
if g:NERDTreeShowLineNumbers
setlocal nu
else
setlocal nonu
if v:version >= 703
setlocal nornu
endif
endif
iabc <buffer>
if g:NERDTreeHighlightCursorline
setlocal cursorline
endif
call self._setupStatusline()
let b:treeShowHelp = 0
let b:NERDTreeIgnoreEnabled = 1
let b:NERDTreeShowFiles = g:NERDTreeShowFiles
let b:NERDTreeShowHidden = g:NERDTreeShowHidden
let b:NERDTreeShowBookmarks = g:NERDTreeShowBookmarks
setfiletype nerdtree
call self._bindMappings()
endfunction
"FUNCTION: s:Creator._setupStatusline() {{{1
function! s:Creator._setupStatusline()
if g:NERDTreeStatusline != -1
let &l:statusline = g:NERDTreeStatusline
endif
endfunction
"FUNCTION: s:Creator.TogglePrimary(dir) {{{1
function! s:Creator.TogglePrimary(dir)
let creator = s:Creator.New()
call creator.togglePrimary(a:dir)
endfunction
"FUNCTION: s:Creator.togglePrimary(dir) {{{1
"Toggles the NERD tree. I.e the NERD tree is open, it is closed, if it is
"closed it is restored or initialized (if it doesnt exist)
"
"Args:
"dir: the full path for the root node (is only used if the NERD tree is being
"initialized.
function! s:Creator.togglePrimary(dir)
if nerdtree#treeExistsForTab()
if !nerdtree#isTreeOpen()
call self._createTreeWin()
if !&hidden
call nerdtree#renderView()
endif
call nerdtree#restoreScreenState()
else
call nerdtree#closeTree()
endif
else
call self.createPrimary(a:dir)
endif
endfunction
" vim: set sw=4 sts=4 et fdm=marker:

View file

@ -1,143 +0,0 @@
"CLASS: KeyMap
"============================================================
let s:KeyMap = {}
let g:NERDTreeKeyMap = s:KeyMap
"FUNCTION: KeyMap.All() {{{1
function! s:KeyMap.All()
if !exists("s:keyMaps")
let s:keyMaps = []
endif
return s:keyMaps
endfunction
"FUNCTION: KeyMap.FindFor(key, scope) {{{1
function! s:KeyMap.FindFor(key, scope)
for i in s:KeyMap.All()
if i.key ==# a:key && i.scope ==# a:scope
return i
endif
endfor
return {}
endfunction
"FUNCTION: KeyMap.BindAll() {{{1
function! s:KeyMap.BindAll()
for i in s:KeyMap.All()
call i.bind()
endfor
endfunction
"FUNCTION: KeyMap.bind() {{{1
function! s:KeyMap.bind()
" If the key sequence we're trying to map contains any '<>' notation, we
" must replace each of the '<' characters with '<lt>' to ensure the string
" is not translated into its corresponding keycode during the later part
" of the map command below
" :he <>
let specialNotationRegex = '\m<\([[:alnum:]_-]\+>\)'
if self.key =~# specialNotationRegex
let keymapInvokeString = substitute(self.key, specialNotationRegex, '<lt>\1', 'g')
else
let keymapInvokeString = self.key
endif
let premap = self.key == "<LeftRelease>" ? " <LeftRelease>" : " "
exec 'nnoremap <buffer> <silent> '. self.key . premap . ':call nerdtree#invokeKeyMap("'. keymapInvokeString .'")<cr>'
endfunction
"FUNCTION: KeyMap.Remove(key, scope) {{{1
function! s:KeyMap.Remove(key, scope)
let maps = s:KeyMap.All()
for i in range(len(maps))
if maps[i].key ==# a:key && maps[i].scope ==# a:scope
return remove(maps, i)
endif
endfor
endfunction
"FUNCTION: KeyMap.invoke() {{{1
"Call the KeyMaps callback function
function! s:KeyMap.invoke(...)
let Callback = function(self.callback)
if a:0
call Callback(a:1)
else
call Callback()
endif
endfunction
"FUNCTION: KeyMap.Invoke() {{{1
"Find a keymapping for a:key and the current scope invoke it.
"
"Scope is determined as follows:
" * if the cursor is on a dir node then "DirNode"
" * if the cursor is on a file node then "FileNode"
" * if the cursor is on a bookmark then "Bookmark"
"
"If a keymap has the scope of "all" then it will be called if no other keymap
"is found for a:key and the scope.
function! s:KeyMap.Invoke(key)
let node = g:NERDTreeFileNode.GetSelected()
if !empty(node)
"try file node
if !node.path.isDirectory
let km = s:KeyMap.FindFor(a:key, "FileNode")
if !empty(km)
return km.invoke(node)
endif
endif
"try dir node
if node.path.isDirectory
let km = s:KeyMap.FindFor(a:key, "DirNode")
if !empty(km)
return km.invoke(node)
endif
endif
"try generic node
let km = s:KeyMap.FindFor(a:key, "Node")
if !empty(km)
return km.invoke(node)
endif
endif
"try bookmark
let bm = g:NERDTreeBookmark.GetSelected()
if !empty(bm)
let km = s:KeyMap.FindFor(a:key, "Bookmark")
if !empty(km)
return km.invoke(bm)
endif
endif
"try all
let km = s:KeyMap.FindFor(a:key, "all")
if !empty(km)
return km.invoke()
endif
endfunction
"FUNCTION: KeyMap.Create(options) {{{1
function! s:KeyMap.Create(options)
let newKeyMap = copy(self)
let opts = extend({'scope': 'all', 'quickhelpText': ''}, copy(a:options))
let newKeyMap.key = opts['key']
let newKeyMap.quickhelpText = opts['quickhelpText']
let newKeyMap.callback = opts['callback']
let newKeyMap.scope = opts['scope']
call s:KeyMap.Add(newKeyMap)
endfunction
"FUNCTION: KeyMap.Add(keymap) {{{1
function! s:KeyMap.Add(keymap)
call s:KeyMap.Remove(a:keymap.key, a:keymap.scope)
call add(s:KeyMap.All(), a:keymap)
endfunction
" vim: set sw=4 sts=4 et fdm=marker:

View file

@ -1,180 +0,0 @@
"CLASS: MenuController
"============================================================
let s:MenuController = {}
let g:NERDTreeMenuController = s:MenuController
"FUNCTION: MenuController.New(menuItems) {{{1
"create a new menu controller that operates on the given menu items
function! s:MenuController.New(menuItems)
let newMenuController = copy(self)
if a:menuItems[0].isSeparator()
let newMenuController.menuItems = a:menuItems[1:-1]
else
let newMenuController.menuItems = a:menuItems
endif
return newMenuController
endfunction
"FUNCTION: MenuController.showMenu() {{{1
"start the main loop of the menu and get the user to choose/execute a menu
"item
function! s:MenuController.showMenu()
call self._saveOptions()
try
let self.selection = 0
let done = 0
while !done
redraw!
call self._echoPrompt()
let key = nr2char(getchar())
let done = self._handleKeypress(key)
endwhile
finally
call self._restoreOptions()
endtry
if self.selection != -1
let m = self._current()
call m.execute()
endif
endfunction
"FUNCTION: MenuController._echoPrompt() {{{1
function! s:MenuController._echoPrompt()
echo "NERDTree Menu. Use j/k/enter and the shortcuts indicated"
echo "=========================================================="
for i in range(0, len(self.menuItems)-1)
if self.selection == i
echo "> " . self.menuItems[i].text
else
echo " " . self.menuItems[i].text
endif
endfor
endfunction
"FUNCTION: MenuController._current(key) {{{1
"get the MenuItem that is currently selected
function! s:MenuController._current()
return self.menuItems[self.selection]
endfunction
"FUNCTION: MenuController._handleKeypress(key) {{{1
"change the selection (if appropriate) and return 1 if the user has made
"their choice, 0 otherwise
function! s:MenuController._handleKeypress(key)
if a:key == 'j'
call self._cursorDown()
elseif a:key == 'k'
call self._cursorUp()
elseif a:key == nr2char(27) "escape
let self.selection = -1
return 1
elseif a:key == "\r" || a:key == "\n" "enter and ctrl-j
return 1
else
let index = self._nextIndexFor(a:key)
if index != -1
let self.selection = index
if len(self._allIndexesFor(a:key)) == 1
return 1
endif
endif
endif
return 0
endfunction
"FUNCTION: MenuController._allIndexesFor(shortcut) {{{1
"get indexes to all menu items with the given shortcut
function! s:MenuController._allIndexesFor(shortcut)
let toReturn = []
for i in range(0, len(self.menuItems)-1)
if self.menuItems[i].shortcut == a:shortcut
call add(toReturn, i)
endif
endfor
return toReturn
endfunction
"FUNCTION: MenuController._nextIndexFor(shortcut) {{{1
"get the index to the next menu item with the given shortcut, starts from the
"current cursor location and wraps around to the top again if need be
function! s:MenuController._nextIndexFor(shortcut)
for i in range(self.selection+1, len(self.menuItems)-1)
if self.menuItems[i].shortcut == a:shortcut
return i
endif
endfor
for i in range(0, self.selection)
if self.menuItems[i].shortcut == a:shortcut
return i
endif
endfor
return -1
endfunction
"FUNCTION: MenuController._setCmdheight() {{{1
"sets &cmdheight to whatever is needed to display the menu
function! s:MenuController._setCmdheight()
let &cmdheight = len(self.menuItems) + 3
endfunction
"FUNCTION: MenuController._saveOptions() {{{1
"set any vim options that are required to make the menu work (saving their old
"values)
function! s:MenuController._saveOptions()
let self._oldLazyredraw = &lazyredraw
let self._oldCmdheight = &cmdheight
set nolazyredraw
call self._setCmdheight()
endfunction
"FUNCTION: MenuController._restoreOptions() {{{1
"restore the options we saved in _saveOptions()
function! s:MenuController._restoreOptions()
let &cmdheight = self._oldCmdheight
let &lazyredraw = self._oldLazyredraw
endfunction
"FUNCTION: MenuController._cursorDown() {{{1
"move the cursor to the next menu item, skipping separators
function! s:MenuController._cursorDown()
let done = 0
while !done
if self.selection < len(self.menuItems)-1
let self.selection += 1
else
let self.selection = 0
endif
if !self._current().isSeparator()
let done = 1
endif
endwhile
endfunction
"FUNCTION: MenuController._cursorUp() {{{1
"move the cursor to the previous menu item, skipping separators
function! s:MenuController._cursorUp()
let done = 0
while !done
if self.selection > 0
let self.selection -= 1
else
let self.selection = len(self.menuItems)-1
endif
if !self._current().isSeparator()
let done = 1
endif
endwhile
endfunction
" vim: set sw=4 sts=4 et fdm=marker:

View file

@ -1,114 +0,0 @@
"CLASS: MenuItem
"============================================================
let s:MenuItem = {}
let g:NERDTreeMenuItem = s:MenuItem
"FUNCTION: MenuItem.All() {{{1
"get all top level menu items
function! s:MenuItem.All()
if !exists("s:menuItems")
let s:menuItems = []
endif
return s:menuItems
endfunction
"FUNCTION: MenuItem.AllEnabled() {{{1
"get all top level menu items that are currently enabled
function! s:MenuItem.AllEnabled()
let toReturn = []
for i in s:MenuItem.All()
if i.enabled()
call add(toReturn, i)
endif
endfor
return toReturn
endfunction
"FUNCTION: MenuItem.Create(options) {{{1
"make a new menu item and add it to the global list
function! s:MenuItem.Create(options)
let newMenuItem = copy(self)
let newMenuItem.text = a:options['text']
let newMenuItem.shortcut = a:options['shortcut']
let newMenuItem.children = []
let newMenuItem.isActiveCallback = -1
if has_key(a:options, 'isActiveCallback')
let newMenuItem.isActiveCallback = a:options['isActiveCallback']
endif
let newMenuItem.callback = -1
if has_key(a:options, 'callback')
let newMenuItem.callback = a:options['callback']
endif
if has_key(a:options, 'parent')
call add(a:options['parent'].children, newMenuItem)
else
call add(s:MenuItem.All(), newMenuItem)
endif
return newMenuItem
endfunction
"FUNCTION: MenuItem.CreateSeparator(options) {{{1
"make a new separator menu item and add it to the global list
function! s:MenuItem.CreateSeparator(options)
let standard_options = { 'text': '--------------------',
\ 'shortcut': -1,
\ 'callback': -1 }
let options = extend(a:options, standard_options, "force")
return s:MenuItem.Create(options)
endfunction
"FUNCTION: MenuItem.CreateSubmenu(options) {{{1
"make a new submenu and add it to global list
function! s:MenuItem.CreateSubmenu(options)
let standard_options = { 'callback': -1 }
let options = extend(a:options, standard_options, "force")
return s:MenuItem.Create(options)
endfunction
"FUNCTION: MenuItem.enabled() {{{1
"return 1 if this menu item should be displayed
"
"delegates off to the isActiveCallback, and defaults to 1 if no callback was
"specified
function! s:MenuItem.enabled()
if self.isActiveCallback != -1
return {self.isActiveCallback}()
endif
return 1
endfunction
"FUNCTION: MenuItem.execute() {{{1
"perform the action behind this menu item, if this menuitem has children then
"display a new menu for them, otherwise deletegate off to the menuitem's
"callback
function! s:MenuItem.execute()
if len(self.children)
let mc = s:MenuController.New(self.children)
call mc.showMenu()
else
if self.callback != -1
call {self.callback}()
endif
endif
endfunction
"FUNCTION: MenuItem.isSeparator() {{{1
"return 1 if this menuitem is a separator
function! s:MenuItem.isSeparator()
return self.callback == -1 && self.children == []
endfunction
"FUNCTION: MenuItem.isSubmenu() {{{1
"return 1 if this menuitem is a submenu
function! s:MenuItem.isSubmenu()
return self.callback == -1 && !empty(self.children)
endfunction
" vim: set sw=4 sts=4 et fdm=marker:

View file

@ -1,264 +0,0 @@
"CLASS: Opener
"============================================================
let s:Opener = {}
let g:NERDTreeOpener = s:Opener
"FUNCTION: Opener._checkToCloseTree(newtab) {{{1
"Check the class options and global options (i.e. NERDTreeQuitOnOpen) to see
"if the tree should be closed now.
"
"Args:
"a:newtab - boolean. If set, only close the tree now if we are opening the
"target in a new tab. This is needed because we have to close tree before we
"leave the tab
function! s:Opener._checkToCloseTree(newtab)
if self._keepopen
return
endif
if (a:newtab && self._where == 't') || !a:newtab
call nerdtree#closeTreeIfQuitOnOpen()
endif
endfunction
"FUNCTION: Opener._gotoTargetWin() {{{1
function! s:Opener._gotoTargetWin()
if b:NERDTreeType ==# "secondary"
if self._where == 'v'
vsplit
elseif self._where == 'h'
split
elseif self._where == 't'
tabnew
endif
else
call self._checkToCloseTree(1)
if self._where == 'v'
call self._newVSplit()
elseif self._where == 'h'
call self._newSplit()
elseif self._where == 't'
tabnew
elseif self._where == 'p'
call self._previousWindow()
endif
call self._checkToCloseTree(0)
endif
endfunction
"FUNCTION: Opener.New(path, opts) {{{1
"Args:
"
"a:path: The path object that is to be opened.
"
"a:opts:
"
"A dictionary containing the following keys (all optional):
" 'where': Specifies whether the node should be opened in new split/tab or in
" the previous window. Can be either 'v' or 'h' or 't' (for open in
" new tab)
" 'reuse': if a window is displaying the file then jump the cursor there
" 'keepopen': dont close the tree window
" 'stay': open the file, but keep the cursor in the tree win
function! s:Opener.New(path, opts)
let newObj = copy(self)
let newObj._path = a:path
let newObj._stay = nerdtree#has_opt(a:opts, 'stay')
let newObj._reuse = nerdtree#has_opt(a:opts, 'reuse')
let newObj._keepopen = nerdtree#has_opt(a:opts, 'keepopen')
let newObj._where = has_key(a:opts, 'where') ? a:opts['where'] : ''
let newObj._treetype = b:NERDTreeType
call newObj._saveCursorPos()
return newObj
endfunction
"FUNCTION: Opener._newSplit() {{{1
function! s:Opener._newSplit()
" Save the user's settings for splitbelow and splitright
let savesplitbelow=&splitbelow
let savesplitright=&splitright
" 'there' will be set to a command to move from the split window
" back to the explorer window
"
" 'back' will be set to a command to move from the explorer window
" back to the newly split window
"
" 'right' and 'below' will be set to the settings needed for
" splitbelow and splitright IF the explorer is the only window.
"
let there= g:NERDTreeWinPos ==# "left" ? "wincmd h" : "wincmd l"
let back = g:NERDTreeWinPos ==# "left" ? "wincmd l" : "wincmd h"
let right= g:NERDTreeWinPos ==# "left"
let below=0
" Attempt to go to adjacent window
call nerdtree#exec(back)
let onlyOneWin = (winnr("$") ==# 1)
" If no adjacent window, set splitright and splitbelow appropriately
if onlyOneWin
let &splitright=right
let &splitbelow=below
else
" found adjacent window - invert split direction
let &splitright=!right
let &splitbelow=!below
endif
let splitMode = onlyOneWin ? "vertical" : ""
" Open the new window
try
exec(splitMode." sp ")
catch /^Vim\%((\a\+)\)\=:E37/
call nerdtree#putCursorInTreeWin()
throw "NERDTree.FileAlreadyOpenAndModifiedError: ". self._path.str() ." is already open and modified."
catch /^Vim\%((\a\+)\)\=:/
"do nothing
endtry
"resize the tree window if no other window was open before
if onlyOneWin
let size = exists("b:NERDTreeOldWindowSize") ? b:NERDTreeOldWindowSize : g:NERDTreeWinSize
call nerdtree#exec(there)
exec("silent ". splitMode ." resize ". size)
call nerdtree#exec('wincmd p')
endif
" Restore splitmode settings
let &splitbelow=savesplitbelow
let &splitright=savesplitright
endfunction
"FUNCTION: Opener._newVSplit() {{{1
function! s:Opener._newVSplit()
let winwidth = winwidth(".")
if winnr("$")==#1
let winwidth = g:NERDTreeWinSize
endif
call nerdtree#exec("wincmd p")
vnew
"resize the nerd tree back to the original size
call nerdtree#putCursorInTreeWin()
exec("silent vertical resize ". winwidth)
call nerdtree#exec('wincmd p')
endfunction
"FUNCTION: Opener.open(target) {{{1
function! s:Opener.open(target)
if self._path.isDirectory
call self._openDirectory(a:target)
else
call self._openFile()
endif
endfunction
"FUNCTION: Opener._openFile() {{{1
function! s:Opener._openFile()
if self._reuse && self._reuseWindow()
return
endif
call self._gotoTargetWin()
if self._treetype ==# "secondary"
call self._path.edit()
else
call self._path.edit()
if self._stay
call self._restoreCursorPos()
endif
endif
endfunction
"FUNCTION: Opener._openDirectory(node) {{{1
function! s:Opener._openDirectory(node)
if self._treetype ==# "secondary"
call self._gotoTargetWin()
call g:NERDTreeCreator.CreateSecondary(a:node.path.str())
else
call self._gotoTargetWin()
if empty(self._where)
call a:node.makeRoot()
call nerdtree#renderView()
call a:node.putCursorHere(0, 0)
elseif self._where == 't'
call g:NERDTreeCreator.CreatePrimary(a:node.path.str())
else
call g:NERDTreeCreator.CreateSecondary(a:node.path.str())
endif
endif
if self._stay
call self._restoreCursorPos()
endif
endfunction
"FUNCTION: Opener._previousWindow() {{{1
function! s:Opener._previousWindow()
if !nerdtree#isWindowUsable(winnr("#")) && nerdtree#firstUsableWindow() ==# -1
call self._newSplit()
else
try
if !nerdtree#isWindowUsable(winnr("#"))
call nerdtree#exec(nerdtree#firstUsableWindow() . "wincmd w")
else
call nerdtree#exec('wincmd p')
endif
catch /^Vim\%((\a\+)\)\=:E37/
call nerdtree#putCursorInTreeWin()
throw "NERDTree.FileAlreadyOpenAndModifiedError: ". self._path.str() ." is already open and modified."
catch /^Vim\%((\a\+)\)\=:/
echo v:exception
endtry
endif
endfunction
"FUNCTION: Opener._restoreCursorPos(){{{1
function! s:Opener._restoreCursorPos()
call nerdtree#exec('normal ' . self._tabnr . 'gt')
call nerdtree#exec(bufwinnr(self._bufnr) . 'wincmd w')
endfunction
"FUNCTION: Opener._reuseWindow(){{{1
"put the cursor in the first window we find for this file
"
"return 1 if we were successful
function! s:Opener._reuseWindow()
"check the current tab for the window
let winnr = bufwinnr('^' . self._path.str() . '$')
if winnr != -1
call nerdtree#exec(winnr . "wincmd w")
call self._checkToCloseTree(0)
return 1
else
"check other tabs
let tabnr = self._path.tabnr()
if tabnr
call self._checkToCloseTree(1)
call nerdtree#exec('normal! ' . tabnr . 'gt')
let winnr = bufwinnr('^' . self._path.str() . '$')
call nerdtree#exec(winnr . "wincmd w")
return 1
endif
endif
return 0
endfunction
"FUNCTION: Opener._saveCursorPos(){{{1
function! s:Opener._saveCursorPos()
let self._bufnr = bufnr("")
let self._tabnr = tabpagenr()
endfunction
" vim: set sw=4 sts=4 et fdm=marker:

View file

@ -1,724 +0,0 @@
"we need to use this number many times for sorting... so we calculate it only
"once here
let s:NERDTreeSortStarIndex = index(g:NERDTreeSortOrder, '*')
"CLASS: Path
"============================================================
let s:Path = {}
let g:NERDTreePath = s:Path
"FUNCTION: Path.AbsolutePathFor(str) {{{1
function! s:Path.AbsolutePathFor(str)
let prependCWD = 0
if nerdtree#runningWindows()
let prependCWD = a:str !~# '^.:\(\\\|\/\)' && a:str !~# '^\(\\\\\|\/\/\)'
else
let prependCWD = a:str !~# '^/'
endif
let toReturn = a:str
if prependCWD
let toReturn = getcwd() . s:Path.Slash() . a:str
endif
return toReturn
endfunction
"FUNCTION: Path.bookmarkNames() {{{1
function! s:Path.bookmarkNames()
if !exists("self._bookmarkNames")
call self.cacheDisplayString()
endif
return self._bookmarkNames
endfunction
"FUNCTION: Path.cacheDisplayString() {{{1
function! s:Path.cacheDisplayString()
let self.cachedDisplayString = self.getLastPathComponent(1)
if self.isExecutable
let self.cachedDisplayString = self.cachedDisplayString . '*'
endif
let self._bookmarkNames = []
for i in g:NERDTreeBookmark.Bookmarks()
if i.path.equals(self)
call add(self._bookmarkNames, i.name)
endif
endfor
if !empty(self._bookmarkNames)
let self.cachedDisplayString .= ' {' . join(self._bookmarkNames) . '}'
endif
if self.isSymLink
let self.cachedDisplayString .= ' -> ' . self.symLinkDest
endif
if self.isReadOnly
let self.cachedDisplayString .= ' [RO]'
endif
endfunction
"FUNCTION: Path.changeToDir() {{{1
function! s:Path.changeToDir()
let dir = self.str({'format': 'Cd'})
if self.isDirectory ==# 0
let dir = self.getParent().str({'format': 'Cd'})
endif
try
execute "cd " . dir
call nerdtree#echo("CWD is now: " . getcwd())
catch
throw "NERDTree.PathChangeError: cannot change CWD to " . dir
endtry
endfunction
"FUNCTION: Path.compareTo() {{{1
"
"Compares this Path to the given path and returns 0 if they are equal, -1 if
"this Path is "less than" the given path, or 1 if it is "greater".
"
"Args:
"path: the path object to compare this to
"
"Return:
"1, -1 or 0
function! s:Path.compareTo(path)
let thisPath = self.getLastPathComponent(1)
let thatPath = a:path.getLastPathComponent(1)
"if the paths are the same then clearly we return 0
if thisPath ==# thatPath
return 0
endif
let thisSS = self.getSortOrderIndex()
let thatSS = a:path.getSortOrderIndex()
"compare the sort sequences, if they are different then the return
"value is easy
if thisSS < thatSS
return -1
elseif thisSS > thatSS
return 1
else
"if the sort sequences are the same then compare the paths
"alphabetically
let pathCompare = g:NERDTreeCaseSensitiveSort ? thisPath <# thatPath : thisPath <? thatPath
if pathCompare
return -1
else
return 1
endif
endif
endfunction
"FUNCTION: Path.Create(fullpath) {{{1
"
"Factory method.
"
"Creates a path object with the given path. The path is also created on the
"filesystem. If the path already exists, a NERDTree.Path.Exists exception is
"thrown. If any other errors occur, a NERDTree.Path exception is thrown.
"
"Args:
"fullpath: the full filesystem path to the file/dir to create
function! s:Path.Create(fullpath)
"bail if the a:fullpath already exists
if isdirectory(a:fullpath) || filereadable(a:fullpath)
throw "NERDTree.CreatePathError: Directory Exists: '" . a:fullpath . "'"
endif
try
"if it ends with a slash, assume its a dir create it
if a:fullpath =~# '\(\\\|\/\)$'
"whack the trailing slash off the end if it exists
let fullpath = substitute(a:fullpath, '\(\\\|\/\)$', '', '')
call mkdir(fullpath, 'p')
"assume its a file and create
else
call writefile([], a:fullpath)
endif
catch
throw "NERDTree.CreatePathError: Could not create path: '" . a:fullpath . "'"
endtry
return s:Path.New(a:fullpath)
endfunction
"FUNCTION: Path.copy(dest) {{{1
"
"Copies the file/dir represented by this Path to the given location
"
"Args:
"dest: the location to copy this dir/file to
function! s:Path.copy(dest)
if !s:Path.CopyingSupported()
throw "NERDTree.CopyingNotSupportedError: Copying is not supported on this OS"
endif
let dest = s:Path.WinToUnixPath(a:dest)
let cmd = g:NERDTreeCopyCmd . " " . escape(self.str(), nerdtree#escChars()) . " " . escape(dest, nerdtree#escChars())
let success = system(cmd)
if success != 0
throw "NERDTree.CopyError: Could not copy ''". self.str() ."'' to: '" . a:dest . "'"
endif
endfunction
"FUNCTION: Path.CopyingSupported() {{{1
"
"returns 1 if copying is supported for this OS
function! s:Path.CopyingSupported()
return exists('g:NERDTreeCopyCmd')
endfunction
"FUNCTION: Path.copyingWillOverwrite(dest) {{{1
"
"returns 1 if copy this path to the given location will cause files to
"overwritten
"
"Args:
"dest: the location this path will be copied to
function! s:Path.copyingWillOverwrite(dest)
if filereadable(a:dest)
return 1
endif
if isdirectory(a:dest)
let path = s:Path.JoinPathStrings(a:dest, self.getLastPathComponent(0))
if filereadable(path)
return 1
endif
endif
endfunction
"FUNCTION: Path.delete() {{{1
"
"Deletes the file represented by this path.
"Deletion of directories is not supported
"
"Throws NERDTree.Path.Deletion exceptions
function! s:Path.delete()
if self.isDirectory
let cmd = g:NERDTreeRemoveDirCmd . self.str({'escape': 1})
let success = system(cmd)
if v:shell_error != 0
throw "NERDTree.PathDeletionError: Could not delete directory: '" . self.str() . "'"
endif
else
let success = delete(self.str())
if success != 0
throw "NERDTree.PathDeletionError: Could not delete file: '" . self.str() . "'"
endif
endif
"delete all bookmarks for this path
for i in self.bookmarkNames()
let bookmark = g:NERDTreeBookmark.BookmarkFor(i)
call bookmark.delete()
endfor
endfunction
"FUNCTION: Path.displayString() {{{1
"
"Returns a string that specifies how the path should be represented as a
"string
function! s:Path.displayString()
if self.cachedDisplayString ==# ""
call self.cacheDisplayString()
endif
return self.cachedDisplayString
endfunction
"FUNCTION: Path.edit() {{{1
function! s:Path.edit()
exec "edit " . self.str({'format': 'Edit'})
endfunction
"FUNCTION: Path.extractDriveLetter(fullpath) {{{1
"
"If running windows, cache the drive letter for this path
function! s:Path.extractDriveLetter(fullpath)
if nerdtree#runningWindows()
if a:fullpath =~ '^\(\\\\\|\/\/\)'
"For network shares, the 'drive' consists of the first two parts of the path, i.e. \\boxname\share
let self.drive = substitute(a:fullpath, '^\(\(\\\\\|\/\/\)[^\\\/]*\(\\\|\/\)[^\\\/]*\).*', '\1', '')
let self.drive = substitute(self.drive, '/', '\', "g")
else
let self.drive = substitute(a:fullpath, '\(^[a-zA-Z]:\).*', '\1', '')
endif
else
let self.drive = ''
endif
endfunction
"FUNCTION: Path.exists() {{{1
"return 1 if this path points to a location that is readable or is a directory
function! s:Path.exists()
let p = self.str()
return filereadable(p) || isdirectory(p)
endfunction
"FUNCTION: Path.getDir() {{{1
"
"Returns this path if it is a directory, else this paths parent.
"
"Return:
"a Path object
function! s:Path.getDir()
if self.isDirectory
return self
else
return self.getParent()
endif
endfunction
"FUNCTION: Path.getParent() {{{1
"
"Returns a new path object for this paths parent
"
"Return:
"a new Path object
function! s:Path.getParent()
if nerdtree#runningWindows()
let path = self.drive . '\' . join(self.pathSegments[0:-2], '\')
else
let path = '/'. join(self.pathSegments[0:-2], '/')
endif
return s:Path.New(path)
endfunction
"FUNCTION: Path.getLastPathComponent(dirSlash) {{{1
"
"Gets the last part of this path.
"
"Args:
"dirSlash: if 1 then a trailing slash will be added to the returned value for
"directory nodes.
function! s:Path.getLastPathComponent(dirSlash)
if empty(self.pathSegments)
return ''
endif
let toReturn = self.pathSegments[-1]
if a:dirSlash && self.isDirectory
let toReturn = toReturn . '/'
endif
return toReturn
endfunction
"FUNCTION: Path.getSortOrderIndex() {{{1
"returns the index of the pattern in g:NERDTreeSortOrder that this path matches
function! s:Path.getSortOrderIndex()
let i = 0
while i < len(g:NERDTreeSortOrder)
if self.getLastPathComponent(1) =~# g:NERDTreeSortOrder[i]
return i
endif
let i = i + 1
endwhile
return s:NERDTreeSortStarIndex
endfunction
"FUNCTION: Path.isUnixHiddenFile() {{{1
"check for unix hidden files
function! s:Path.isUnixHiddenFile()
return self.getLastPathComponent(0) =~# '^\.'
endfunction
"FUNCTION: Path.isUnixHiddenPath() {{{1
"check for unix path with hidden components
function! s:Path.isUnixHiddenPath()
if self.getLastPathComponent(0) =~# '^\.'
return 1
else
for segment in self.pathSegments
if segment =~# '^\.'
return 1
endif
endfor
return 0
endif
endfunction
"FUNCTION: Path.ignore() {{{1
"returns true if this path should be ignored
function! s:Path.ignore()
"filter out the user specified paths to ignore
if b:NERDTreeIgnoreEnabled
for i in g:NERDTreeIgnore
if self._ignorePatternMatches(i)
return 1
endif
endfor
endif
"dont show hidden files unless instructed to
if b:NERDTreeShowHidden ==# 0 && self.isUnixHiddenFile()
return 1
endif
if b:NERDTreeShowFiles ==# 0 && self.isDirectory ==# 0
return 1
endif
if exists("*NERDTreeCustomIgnoreFilter") && NERDTreeCustomIgnoreFilter(self)
return 1
endif
return 0
endfunction
"FUNCTION: Path._ignorePatternMatches(pattern) {{{1
"returns true if this path matches the given ignore pattern
function! s:Path._ignorePatternMatches(pattern)
let pat = a:pattern
if strpart(pat,len(pat)-7) == '[[dir]]'
if !self.isDirectory
return 0
endif
let pat = strpart(pat,0, len(pat)-7)
elseif strpart(pat,len(pat)-8) == '[[file]]'
if self.isDirectory
return 0
endif
let pat = strpart(pat,0, len(pat)-8)
endif
return self.getLastPathComponent(0) =~# pat
endfunction
"FUNCTION: Path.isUnder(path) {{{1
"return 1 if this path is somewhere under the given path in the filesystem.
"
"a:path should be a dir
function! s:Path.isUnder(path)
if a:path.isDirectory == 0
return 0
endif
let this = self.str()
let that = a:path.str()
return stridx(this, that . s:Path.Slash()) == 0
endfunction
"FUNCTION: Path.JoinPathStrings(...) {{{1
function! s:Path.JoinPathStrings(...)
let components = []
for i in a:000
let components = extend(components, split(i, '/'))
endfor
return '/' . join(components, '/')
endfunction
"FUNCTION: Path.equals() {{{1
"
"Determines whether 2 path objects are "equal".
"They are equal if the paths they represent are the same
"
"Args:
"path: the other path obj to compare this with
function! s:Path.equals(path)
return self.str() ==# a:path.str()
endfunction
"FUNCTION: Path.New() {{{1
"The Constructor for the Path object
function! s:Path.New(path)
let newPath = copy(self)
call newPath.readInfoFromDisk(s:Path.AbsolutePathFor(a:path))
let newPath.cachedDisplayString = ""
return newPath
endfunction
"FUNCTION: Path.Slash() {{{1
"return the slash to use for the current OS
function! s:Path.Slash()
return nerdtree#runningWindows() ? '\' : '/'
endfunction
"FUNCTION: Path.Resolve() {{{1
"Invoke the vim resolve() function and return the result
"This is necessary because in some versions of vim resolve() removes trailing
"slashes while in other versions it doesn't. This always removes the trailing
"slash
function! s:Path.Resolve(path)
let tmp = resolve(a:path)
return tmp =~# '.\+/$' ? substitute(tmp, '/$', '', '') : tmp
endfunction
"FUNCTION: Path.readInfoFromDisk(fullpath) {{{1
"
"
"Throws NERDTree.Path.InvalidArguments exception.
function! s:Path.readInfoFromDisk(fullpath)
call self.extractDriveLetter(a:fullpath)
let fullpath = s:Path.WinToUnixPath(a:fullpath)
if getftype(fullpath) ==# "fifo"
throw "NERDTree.InvalidFiletypeError: Cant handle FIFO files: " . a:fullpath
endif
let self.pathSegments = split(fullpath, '/')
let self.isReadOnly = 0
if isdirectory(a:fullpath)
let self.isDirectory = 1
elseif filereadable(a:fullpath)
let self.isDirectory = 0
let self.isReadOnly = filewritable(a:fullpath) ==# 0
else
throw "NERDTree.InvalidArgumentsError: Invalid path = " . a:fullpath
endif
let self.isExecutable = 0
if !self.isDirectory
let self.isExecutable = getfperm(a:fullpath) =~# 'x'
endif
"grab the last part of the path (minus the trailing slash)
let lastPathComponent = self.getLastPathComponent(0)
"get the path to the new node with the parent dir fully resolved
let hardPath = s:Path.Resolve(self.strTrunk()) . '/' . lastPathComponent
"if the last part of the path is a symlink then flag it as such
let self.isSymLink = (s:Path.Resolve(hardPath) != hardPath)
if self.isSymLink
let self.symLinkDest = s:Path.Resolve(fullpath)
"if the link is a dir then slap a / on the end of its dest
if isdirectory(self.symLinkDest)
"we always wanna treat MS windows shortcuts as files for
"simplicity
if hardPath !~# '\.lnk$'
let self.symLinkDest = self.symLinkDest . '/'
endif
endif
endif
endfunction
"FUNCTION: Path.refresh() {{{1
function! s:Path.refresh()
call self.readInfoFromDisk(self.str())
call self.cacheDisplayString()
endfunction
"FUNCTION: Path.rename() {{{1
"
"Renames this node on the filesystem
function! s:Path.rename(newPath)
if a:newPath ==# ''
throw "NERDTree.InvalidArgumentsError: Invalid newPath for renaming = ". a:newPath
endif
let success = rename(self.str(), a:newPath)
if success != 0
throw "NERDTree.PathRenameError: Could not rename: '" . self.str() . "'" . 'to:' . a:newPath
endif
call self.readInfoFromDisk(a:newPath)
for i in self.bookmarkNames()
let b = g:NERDTreeBookmark.BookmarkFor(i)
call b.setPath(copy(self))
endfor
call g:NERDTreeBookmark.Write()
endfunction
"FUNCTION: Path.str() {{{1
"
"Returns a string representation of this Path
"
"Takes an optional dictionary param to specify how the output should be
"formatted.
"
"The dict may have the following keys:
" 'format'
" 'escape'
" 'truncateTo'
"
"The 'format' key may have a value of:
" 'Cd' - a string to be used with the :cd command
" 'Edit' - a string to be used with :e :sp :new :tabedit etc
" 'UI' - a string used in the NERD tree UI
"
"The 'escape' key, if specified will cause the output to be escaped with
"shellescape()
"
"The 'truncateTo' key causes the resulting string to be truncated to the value
"'truncateTo' maps to. A '<' char will be prepended.
function! s:Path.str(...)
let options = a:0 ? a:1 : {}
let toReturn = ""
if has_key(options, 'format')
let format = options['format']
if has_key(self, '_strFor' . format)
exec 'let toReturn = self._strFor' . format . '()'
else
raise 'NERDTree.UnknownFormatError: unknown format "'. format .'"'
endif
else
let toReturn = self._str()
endif
if nerdtree#has_opt(options, 'escape')
let toReturn = shellescape(toReturn)
endif
if has_key(options, 'truncateTo')
let limit = options['truncateTo']
if len(toReturn) > limit
let toReturn = "<" . strpart(toReturn, len(toReturn) - limit + 1)
endif
endif
return toReturn
endfunction
"FUNCTION: Path._strForUI() {{{1
function! s:Path._strForUI()
let toReturn = '/' . join(self.pathSegments, '/')
if self.isDirectory && toReturn != '/'
let toReturn = toReturn . '/'
endif
return toReturn
endfunction
"FUNCTION: Path._strForCd() {{{1
"
" returns a string that can be used with :cd
function! s:Path._strForCd()
return escape(self.str(), nerdtree#escChars())
endfunction
"FUNCTION: Path._strForEdit() {{{1
"
"Return: the string for this path that is suitable to be used with the :edit
"command
function! s:Path._strForEdit()
let p = escape(self.str({'format': 'UI'}), nerdtree#escChars())
let cwd = getcwd() . s:Path.Slash()
"return a relative path if we can
let isRelative = 0
if nerdtree#runningWindows()
let isRelative = stridx(tolower(p), tolower(cwd)) == 0
else
let isRelative = stridx(p, cwd) == 0
endif
if isRelative
let p = strpart(p, strlen(cwd))
"handle the edge case where the file begins with a + (vim interprets
"the +foo in `:e +foo` as an option to :edit)
if p[0] == "+"
let p = '\' . p
endif
endif
if p ==# ''
let p = '.'
endif
return p
endfunction
"FUNCTION: Path._strForGlob() {{{1
function! s:Path._strForGlob()
let lead = s:Path.Slash()
"if we are running windows then slap a drive letter on the front
if nerdtree#runningWindows()
let lead = self.drive . '\'
endif
let toReturn = lead . join(self.pathSegments, s:Path.Slash())
if !nerdtree#runningWindows()
let toReturn = escape(toReturn, nerdtree#escChars())
endif
return toReturn
endfunction
"FUNCTION: Path._str() {{{1
"
"Gets the string path for this path object that is appropriate for the OS.
"EG, in windows c:\foo\bar
" in *nix /foo/bar
function! s:Path._str()
let lead = s:Path.Slash()
"if we are running windows then slap a drive letter on the front
if nerdtree#runningWindows()
let lead = self.drive . '\'
endif
return lead . join(self.pathSegments, s:Path.Slash())
endfunction
"FUNCTION: Path.strTrunk() {{{1
"Gets the path without the last segment on the end.
function! s:Path.strTrunk()
return self.drive . '/' . join(self.pathSegments[0:-2], '/')
endfunction
" FUNCTION: Path.tabnr() {{{1
" return the number of the first tab that is displaying this file
"
" return 0 if no tab was found
function! s:Path.tabnr()
let str = self.str()
for t in range(tabpagenr('$'))
for b in tabpagebuflist(t+1)
if str == expand('#' . b . ':p')
return t+1
endif
endfor
endfor
return 0
endfunction
"FUNCTION: Path.WinToUnixPath(pathstr){{{1
"Takes in a windows path and returns the unix equiv
"
"A class level method
"
"Args:
"pathstr: the windows path to convert
function! s:Path.WinToUnixPath(pathstr)
if !nerdtree#runningWindows()
return a:pathstr
endif
let toReturn = a:pathstr
"remove the x:\ of the front
let toReturn = substitute(toReturn, '^.*:\(\\\|/\)\?', '/', "")
"remove the \\ network share from the front
let toReturn = substitute(toReturn, '^\(\\\\\|\/\/\)[^\\\/]*\(\\\|\/\)[^\\\/]*\(\\\|\/\)\?', '/', "")
"convert all \ chars to /
let toReturn = substitute(toReturn, '\', '/', "g")
return toReturn
endfunction
" vim: set sw=4 sts=4 et fdm=marker:

View file

@ -1,528 +0,0 @@
"CLASS: TreeDirNode
"A subclass of NERDTreeFileNode.
"
"The 'composite' part of the file/dir composite.
"============================================================
let s:TreeDirNode = copy(g:NERDTreeFileNode)
let g:NERDTreeDirNode = s:TreeDirNode
"FUNCTION: TreeDirNode.AbsoluteTreeRoot(){{{1
"class method that returns the highest cached ancestor of the current root
function! s:TreeDirNode.AbsoluteTreeRoot()
let currentNode = b:NERDTreeRoot
while currentNode.parent != {}
let currentNode = currentNode.parent
endwhile
return currentNode
endfunction
"FUNCTION: TreeDirNode.activate([options]) {{{1
unlet s:TreeDirNode.activate
function! s:TreeDirNode.activate(...)
let opts = a:0 ? a:1 : {}
call self.toggleOpen(opts)
call nerdtree#renderView()
call self.putCursorHere(0, 0)
endfunction
"FUNCTION: TreeDirNode.addChild(treenode, inOrder) {{{1
"Adds the given treenode to the list of children for this node
"
"Args:
"-treenode: the node to add
"-inOrder: 1 if the new node should be inserted in sorted order
function! s:TreeDirNode.addChild(treenode, inOrder)
call add(self.children, a:treenode)
let a:treenode.parent = self
if a:inOrder
call self.sortChildren()
endif
endfunction
"FUNCTION: TreeDirNode.close() {{{1
"Closes this directory
function! s:TreeDirNode.close()
let self.isOpen = 0
endfunction
"FUNCTION: TreeDirNode.closeChildren() {{{1
"Closes all the child dir nodes of this node
function! s:TreeDirNode.closeChildren()
for i in self.children
if i.path.isDirectory
call i.close()
call i.closeChildren()
endif
endfor
endfunction
"FUNCTION: TreeDirNode.createChild(path, inOrder) {{{1
"Instantiates a new child node for this node with the given path. The new
"nodes parent is set to this node.
"
"Args:
"path: a Path object that this node will represent/contain
"inOrder: 1 if the new node should be inserted in sorted order
"
"Returns:
"the newly created node
function! s:TreeDirNode.createChild(path, inOrder)
let newTreeNode = g:NERDTreeFileNode.New(a:path)
call self.addChild(newTreeNode, a:inOrder)
return newTreeNode
endfunction
"FUNCTION: TreeDirNode.findNode(path) {{{1
"Will find one of the children (recursively) that has the given path
"
"Args:
"path: a path object
unlet s:TreeDirNode.findNode
function! s:TreeDirNode.findNode(path)
if a:path.equals(self.path)
return self
endif
if stridx(a:path.str(), self.path.str(), 0) ==# -1
return {}
endif
if self.path.isDirectory
for i in self.children
let retVal = i.findNode(a:path)
if retVal != {}
return retVal
endif
endfor
endif
return {}
endfunction
"FUNCTION: TreeDirNode.getChildCount() {{{1
"Returns the number of children this node has
function! s:TreeDirNode.getChildCount()
return len(self.children)
endfunction
"FUNCTION: TreeDirNode.getChild(path) {{{1
"Returns child node of this node that has the given path or {} if no such node
"exists.
"
"This function doesnt not recurse into child dir nodes
"
"Args:
"path: a path object
function! s:TreeDirNode.getChild(path)
if stridx(a:path.str(), self.path.str(), 0) ==# -1
return {}
endif
let index = self.getChildIndex(a:path)
if index ==# -1
return {}
else
return self.children[index]
endif
endfunction
"FUNCTION: TreeDirNode.getChildByIndex(indx, visible) {{{1
"returns the child at the given index
"Args:
"indx: the index to get the child from
"visible: 1 if only the visible children array should be used, 0 if all the
"children should be searched.
function! s:TreeDirNode.getChildByIndex(indx, visible)
let array_to_search = a:visible? self.getVisibleChildren() : self.children
if a:indx > len(array_to_search)
throw "NERDTree.InvalidArgumentsError: Index is out of bounds."
endif
return array_to_search[a:indx]
endfunction
"FUNCTION: TreeDirNode.getChildIndex(path) {{{1
"Returns the index of the child node of this node that has the given path or
"-1 if no such node exists.
"
"This function doesnt not recurse into child dir nodes
"
"Args:
"path: a path object
function! s:TreeDirNode.getChildIndex(path)
if stridx(a:path.str(), self.path.str(), 0) ==# -1
return -1
endif
"do a binary search for the child
let a = 0
let z = self.getChildCount()
while a < z
let mid = (a+z)/2
let diff = a:path.compareTo(self.children[mid].path)
if diff ==# -1
let z = mid
elseif diff ==# 1
let a = mid+1
else
return mid
endif
endwhile
return -1
endfunction
"FUNCTION: TreeDirNode.GetSelected() {{{1
"Returns the current node if it is a dir node, or else returns the current
"nodes parent
unlet s:TreeDirNode.GetSelected
function! s:TreeDirNode.GetSelected()
let currentDir = g:NERDTreeFileNode.GetSelected()
if currentDir != {} && !currentDir.isRoot()
if currentDir.path.isDirectory ==# 0
let currentDir = currentDir.parent
endif
endif
return currentDir
endfunction
"FUNCTION: TreeDirNode.getVisibleChildCount() {{{1
"Returns the number of visible children this node has
function! s:TreeDirNode.getVisibleChildCount()
return len(self.getVisibleChildren())
endfunction
"FUNCTION: TreeDirNode.getVisibleChildren() {{{1
"Returns a list of children to display for this node, in the correct order
"
"Return:
"an array of treenodes
function! s:TreeDirNode.getVisibleChildren()
let toReturn = []
for i in self.children
if i.path.ignore() ==# 0
call add(toReturn, i)
endif
endfor
return toReturn
endfunction
"FUNCTION: TreeDirNode.hasVisibleChildren() {{{1
"returns 1 if this node has any childre, 0 otherwise..
function! s:TreeDirNode.hasVisibleChildren()
return self.getVisibleChildCount() != 0
endfunction
"FUNCTION: TreeDirNode._initChildren() {{{1
"Removes all childen from this node and re-reads them
"
"Args:
"silent: 1 if the function should not echo any "please wait" messages for
"large directories
"
"Return: the number of child nodes read
function! s:TreeDirNode._initChildren(silent)
"remove all the current child nodes
let self.children = []
"get an array of all the files in the nodes dir
let dir = self.path
let globDir = dir.str({'format': 'Glob'})
if version >= 703
let filesStr = globpath(globDir, '*', 1) . "\n" . globpath(globDir, '.*', 1)
else
let filesStr = globpath(globDir, '*') . "\n" . globpath(globDir, '.*')
endif
let files = split(filesStr, "\n")
if !a:silent && len(files) > g:NERDTreeNotificationThreshold
call nerdtree#echo("Please wait, caching a large dir ...")
endif
let invalidFilesFound = 0
for i in files
"filter out the .. and . directories
"Note: we must match .. AND ../ cos sometimes the globpath returns
"../ for path with strange chars (eg $)
if i !~# '\/\.\.\/\?$' && i !~# '\/\.\/\?$'
"put the next file in a new node and attach it
try
let path = g:NERDTreePath.New(i)
call self.createChild(path, 0)
catch /^NERDTree.\(InvalidArguments\|InvalidFiletype\)Error/
let invalidFilesFound += 1
endtry
endif
endfor
call self.sortChildren()
if !a:silent && len(files) > g:NERDTreeNotificationThreshold
call nerdtree#echo("Please wait, caching a large dir ... DONE (". self.getChildCount() ." nodes cached).")
endif
if invalidFilesFound
call nerdtree#echoWarning(invalidFilesFound . " file(s) could not be loaded into the NERD tree")
endif
return self.getChildCount()
endfunction
"FUNCTION: TreeDirNode.New(path) {{{1
"Returns a new TreeNode object with the given path and parent
"
"Args:
"path: a path object representing the full filesystem path to the file/dir that the node represents
unlet s:TreeDirNode.New
function! s:TreeDirNode.New(path)
if a:path.isDirectory != 1
throw "NERDTree.InvalidArgumentsError: A TreeDirNode object must be instantiated with a directory Path object."
endif
let newTreeNode = copy(self)
let newTreeNode.path = a:path
let newTreeNode.isOpen = 0
let newTreeNode.children = []
let newTreeNode.parent = {}
return newTreeNode
endfunction
"FUNCTION: TreeDirNode.open([opts]) {{{1
"Open the dir in the current tree or in a new tree elsewhere.
"
"If opening in the current tree, return the number of cached nodes.
unlet s:TreeDirNode.open
function! s:TreeDirNode.open(...)
let opts = a:0 ? a:1 : {}
if has_key(opts, 'where') && !empty(opts['where'])
let opener = g:NERDTreeOpener.New(self.path, opts)
call opener.open(self)
else
let self.isOpen = 1
if self.children ==# []
return self._initChildren(0)
else
return 0
endif
endif
endfunction
"FUNCTION: TreeDirNode.openAlong([opts]) {{{1
"recursive open the dir if it has only one directory child.
"
"return the level of opened directories.
function! s:TreeDirNode.openAlong(...)
let opts = a:0 ? a:1 : {}
let level = 0
let node = self
while node.path.isDirectory
call node.open(opts)
let level += 1
if node.getVisibleChildCount() == 1
let node = node.getChildByIndex(0, 1)
else
break
endif
endwhile
return level
endfunction
" FUNCTION: TreeDirNode.openExplorer() {{{1
" opens an explorer window for this node in the previous window (could be a
" nerd tree or a netrw)
function! s:TreeDirNode.openExplorer()
call self.open({'where': 'p'})
endfunction
"FUNCTION: TreeDirNode.openInNewTab(options) {{{1
unlet s:TreeDirNode.openInNewTab
function! s:TreeDirNode.openInNewTab(options)
call nerdtree#deprecated('TreeDirNode.openInNewTab', 'is deprecated, use open() instead')
call self.open({'where': 't'})
endfunction
"FUNCTION: TreeDirNode._openInNewTab() {{{1
function! s:TreeDirNode._openInNewTab()
tabnew
call g:NERDTreeCreator.CreatePrimary(self.path.str())
endfunction
"FUNCTION: TreeDirNode.openRecursively() {{{1
"Opens this treenode and all of its children whose paths arent 'ignored'
"because of the file filters.
"
"This method is actually a wrapper for the OpenRecursively2 method which does
"the work.
function! s:TreeDirNode.openRecursively()
call self._openRecursively2(1)
endfunction
"FUNCTION: TreeDirNode._openRecursively2() {{{1
"Opens this all children of this treenode recursively if either:
" *they arent filtered by file filters
" *a:forceOpen is 1
"
"Args:
"forceOpen: 1 if this node should be opened regardless of file filters
function! s:TreeDirNode._openRecursively2(forceOpen)
if self.path.ignore() ==# 0 || a:forceOpen
let self.isOpen = 1
if self.children ==# []
call self._initChildren(1)
endif
for i in self.children
if i.path.isDirectory ==# 1
call i._openRecursively2(0)
endif
endfor
endif
endfunction
"FUNCTION: TreeDirNode.refresh() {{{1
unlet s:TreeDirNode.refresh
function! s:TreeDirNode.refresh()
call self.path.refresh()
"if this node was ever opened, refresh its children
if self.isOpen || !empty(self.children)
"go thru all the files/dirs under this node
let newChildNodes = []
let invalidFilesFound = 0
let dir = self.path
let globDir = dir.str({'format': 'Glob'})
let filesStr = globpath(globDir, '*') . "\n" . globpath(globDir, '.*')
let files = split(filesStr, "\n")
for i in files
"filter out the .. and . directories
"Note: we must match .. AND ../ cos sometimes the globpath returns
"../ for path with strange chars (eg $)
if i !~# '\/\.\.\/\?$' && i !~# '\/\.\/\?$'
try
"create a new path and see if it exists in this nodes children
let path = g:NERDTreePath.New(i)
let newNode = self.getChild(path)
if newNode != {}
call newNode.refresh()
call add(newChildNodes, newNode)
"the node doesnt exist so create it
else
let newNode = g:NERDTreeFileNode.New(path)
let newNode.parent = self
call add(newChildNodes, newNode)
endif
catch /^NERDTree.InvalidArgumentsError/
let invalidFilesFound = 1
endtry
endif
endfor
"swap this nodes children out for the children we just read/refreshed
let self.children = newChildNodes
call self.sortChildren()
if invalidFilesFound
call nerdtree#echoWarning("some files could not be loaded into the NERD tree")
endif
endif
endfunction
"FUNCTION: TreeDirNode.reveal(path) {{{1
"reveal the given path, i.e. cache and open all treenodes needed to display it
"in the UI
function! s:TreeDirNode.reveal(path)
if !a:path.isUnder(self.path)
throw "NERDTree.InvalidArgumentsError: " . a:path.str() . " should be under " . self.path.str()
endif
call self.open()
if self.path.equals(a:path.getParent())
let n = self.findNode(a:path)
call nerdtree#renderView()
call n.putCursorHere(1,0)
return
endif
let p = a:path
while !p.getParent().equals(self.path)
let p = p.getParent()
endwhile
let n = self.findNode(p)
call n.reveal(a:path)
endfunction
"FUNCTION: TreeDirNode.removeChild(treenode) {{{1
"
"Removes the given treenode from this nodes set of children
"
"Args:
"treenode: the node to remove
"
"Throws a NERDTree.ChildNotFoundError if the given treenode is not found
function! s:TreeDirNode.removeChild(treenode)
for i in range(0, self.getChildCount()-1)
if self.children[i].equals(a:treenode)
call remove(self.children, i)
return
endif
endfor
throw "NERDTree.ChildNotFoundError: child node was not found"
endfunction
"FUNCTION: TreeDirNode.sortChildren() {{{1
"
"Sorts the children of this node according to alphabetical order and the
"directory priority.
"
function! s:TreeDirNode.sortChildren()
let CompareFunc = function("nerdtree#compareNodes")
call sort(self.children, CompareFunc)
endfunction
"FUNCTION: TreeDirNode.toggleOpen([options]) {{{1
"Opens this directory if it is closed and vice versa
function! s:TreeDirNode.toggleOpen(...)
let opts = a:0 ? a:1 : {}
if self.isOpen ==# 1
call self.close()
else
if g:NERDTreeCasadeOpenSingleChildDir == 0
call self.open(opts)
else
call self.openAlong(opts)
endif
endif
endfunction
"FUNCTION: TreeDirNode.transplantChild(newNode) {{{1
"Replaces the child of this with the given node (where the child node's full
"path matches a:newNode's fullpath). The search for the matching node is
"non-recursive
"
"Arg:
"newNode: the node to graft into the tree
function! s:TreeDirNode.transplantChild(newNode)
for i in range(0, self.getChildCount()-1)
if self.children[i].equals(a:newNode)
let self.children[i] = a:newNode
let a:newNode.parent = self
break
endif
endfor
endfunction
" vim: set sw=4 sts=4 et fdm=marker:

View file

@ -1,485 +0,0 @@
"CLASS: TreeFileNode
"This class is the parent of the TreeDirNode class and is the
"'Component' part of the composite design pattern between the treenode
"classes.
"============================================================
let s:TreeFileNode = {}
let g:NERDTreeFileNode = s:TreeFileNode
"FUNCTION: TreeFileNode.activate(...) {{{1
function! s:TreeFileNode.activate(...)
call self.open(a:0 ? a:1 : {})
endfunction
"FUNCTION: TreeFileNode.bookmark(name) {{{1
"bookmark this node with a:name
function! s:TreeFileNode.bookmark(name)
"if a bookmark exists with the same name and the node is cached then save
"it so we can update its display string
let oldMarkedNode = {}
try
let oldMarkedNode = g:NERDTreeBookmark.GetNodeForName(a:name, 1)
catch /^NERDTree.BookmarkNotFoundError/
catch /^NERDTree.BookmarkedNodeNotFoundError/
endtry
call g:NERDTreeBookmark.AddBookmark(a:name, self.path)
call self.path.cacheDisplayString()
call g:NERDTreeBookmark.Write()
if !empty(oldMarkedNode)
call oldMarkedNode.path.cacheDisplayString()
endif
endfunction
"FUNCTION: TreeFileNode.cacheParent() {{{1
"initializes self.parent if it isnt already
function! s:TreeFileNode.cacheParent()
if empty(self.parent)
let parentPath = self.path.getParent()
if parentPath.equals(self.path)
throw "NERDTree.CannotCacheParentError: already at root"
endif
let self.parent = s:TreeFileNode.New(parentPath)
endif
endfunction
"FUNCTION: TreeFileNode.clearBookmarks() {{{1
function! s:TreeFileNode.clearBookmarks()
for i in g:NERDTreeBookmark.Bookmarks()
if i.path.equals(self.path)
call i.delete()
end
endfor
call self.path.cacheDisplayString()
endfunction
"FUNCTION: TreeFileNode.copy(dest) {{{1
function! s:TreeFileNode.copy(dest)
call self.path.copy(a:dest)
let newPath = g:NERDTreePath.New(a:dest)
let parent = b:NERDTreeRoot.findNode(newPath.getParent())
if !empty(parent)
call parent.refresh()
return parent.findNode(newPath)
else
return {}
endif
endfunction
"FUNCTION: TreeFileNode.delete {{{1
"Removes this node from the tree and calls the Delete method for its path obj
function! s:TreeFileNode.delete()
call self.path.delete()
call self.parent.removeChild(self)
endfunction
"FUNCTION: TreeFileNode.displayString() {{{1
"
"Returns a string that specifies how the node should be represented as a
"string
"
"Return:
"a string that can be used in the view to represent this node
function! s:TreeFileNode.displayString()
return self.path.displayString()
endfunction
"FUNCTION: TreeFileNode.equals(treenode) {{{1
"
"Compares this treenode to the input treenode and returns 1 if they are the
"same node.
"
"Use this method instead of == because sometimes when the treenodes contain
"many children, vim seg faults when doing ==
"
"Args:
"treenode: the other treenode to compare to
function! s:TreeFileNode.equals(treenode)
return self.path.str() ==# a:treenode.path.str()
endfunction
"FUNCTION: TreeFileNode.findNode(path) {{{1
"Returns self if this node.path.Equals the given path.
"Returns {} if not equal.
"
"Args:
"path: the path object to compare against
function! s:TreeFileNode.findNode(path)
if a:path.equals(self.path)
return self
endif
return {}
endfunction
"FUNCTION: TreeFileNode.findOpenDirSiblingWithVisibleChildren(direction) {{{1
"
"Finds the next sibling for this node in the indicated direction. This sibling
"must be a directory and may/may not have children as specified.
"
"Args:
"direction: 0 if you want to find the previous sibling, 1 for the next sibling
"
"Return:
"a treenode object or {} if no appropriate sibling could be found
function! s:TreeFileNode.findOpenDirSiblingWithVisibleChildren(direction)
"if we have no parent then we can have no siblings
if self.parent != {}
let nextSibling = self.findSibling(a:direction)
while nextSibling != {}
if nextSibling.path.isDirectory && nextSibling.hasVisibleChildren() && nextSibling.isOpen
return nextSibling
endif
let nextSibling = nextSibling.findSibling(a:direction)
endwhile
endif
return {}
endfunction
"FUNCTION: TreeFileNode.findSibling(direction) {{{1
"
"Finds the next sibling for this node in the indicated direction
"
"Args:
"direction: 0 if you want to find the previous sibling, 1 for the next sibling
"
"Return:
"a treenode object or {} if no sibling could be found
function! s:TreeFileNode.findSibling(direction)
"if we have no parent then we can have no siblings
if self.parent != {}
"get the index of this node in its parents children
let siblingIndx = self.parent.getChildIndex(self.path)
if siblingIndx != -1
"move a long to the next potential sibling node
let siblingIndx = a:direction ==# 1 ? siblingIndx+1 : siblingIndx-1
"keep moving along to the next sibling till we find one that is valid
let numSiblings = self.parent.getChildCount()
while siblingIndx >= 0 && siblingIndx < numSiblings
"if the next node is not an ignored node (i.e. wont show up in the
"view) then return it
if self.parent.children[siblingIndx].path.ignore() ==# 0
return self.parent.children[siblingIndx]
endif
"go to next node
let siblingIndx = a:direction ==# 1 ? siblingIndx+1 : siblingIndx-1
endwhile
endif
endif
return {}
endfunction
"FUNCTION: TreeFileNode.getLineNum(){{{1
"returns the line number this node is rendered on, or -1 if it isnt rendered
function! s:TreeFileNode.getLineNum()
"if the node is the root then return the root line no.
if self.isRoot()
return s:TreeFileNode.GetRootLineNum()
endif
let totalLines = line("$")
"the path components we have matched so far
let pathcomponents = [substitute(b:NERDTreeRoot.path.str({'format': 'UI'}), '/ *$', '', '')]
"the index of the component we are searching for
let curPathComponent = 1
let fullpath = self.path.str({'format': 'UI'})
let lnum = s:TreeFileNode.GetRootLineNum()
while lnum > 0
let lnum = lnum + 1
"have we reached the bottom of the tree?
if lnum ==# totalLines+1
return -1
endif
let curLine = getline(lnum)
let indent = nerdtree#indentLevelFor(curLine)
if indent ==# curPathComponent
let curLine = nerdtree#stripMarkupFromLine(curLine, 1)
let curPath = join(pathcomponents, '/') . '/' . curLine
if stridx(fullpath, curPath, 0) ==# 0
if fullpath ==# curPath || strpart(fullpath, len(curPath)-1,1) ==# '/'
let curLine = substitute(curLine, '/ *$', '', '')
call add(pathcomponents, curLine)
let curPathComponent = curPathComponent + 1
if fullpath ==# curPath
return lnum
endif
endif
endif
endif
endwhile
return -1
endfunction
"FUNCTION: TreeFileNode.GetRootForTab(){{{1
"get the root node for this tab
function! s:TreeFileNode.GetRootForTab()
if nerdtree#treeExistsForTab()
return getbufvar(t:NERDTreeBufName, 'NERDTreeRoot')
end
return {}
endfunction
"FUNCTION: TreeFileNode.GetRootLineNum(){{{1
"gets the line number of the root node
function! s:TreeFileNode.GetRootLineNum()
let rootLine = 1
while getline(rootLine) !~# '^\(/\|<\)'
let rootLine = rootLine + 1
endwhile
return rootLine
endfunction
"FUNCTION: TreeFileNode.GetSelected() {{{1
"gets the treenode that the cursor is currently over
function! s:TreeFileNode.GetSelected()
try
let path = nerdtree#getPath(line("."))
if path ==# {}
return {}
endif
return b:NERDTreeRoot.findNode(path)
catch /^NERDTree/
return {}
endtry
endfunction
"FUNCTION: TreeFileNode.isVisible() {{{1
"returns 1 if this node should be visible according to the tree filters and
"hidden file filters (and their on/off status)
function! s:TreeFileNode.isVisible()
return !self.path.ignore()
endfunction
"FUNCTION: TreeFileNode.isRoot() {{{1
"returns 1 if this node is b:NERDTreeRoot
function! s:TreeFileNode.isRoot()
if !nerdtree#treeExistsForBuf()
throw "NERDTree.NoTreeError: No tree exists for the current buffer"
endif
return self.equals(b:NERDTreeRoot)
endfunction
"FUNCTION: TreeFileNode.makeRoot() {{{1
"Make this node the root of the tree
function! s:TreeFileNode.makeRoot()
if self.path.isDirectory
let b:NERDTreeRoot = self
else
call self.cacheParent()
let b:NERDTreeRoot = self.parent
endif
call b:NERDTreeRoot.open()
"change dir to the dir of the new root if instructed to
if g:NERDTreeChDirMode ==# 2
exec "cd " . b:NERDTreeRoot.path.str({'format': 'Edit'})
endif
silent doautocmd User NERDTreeNewRoot
endfunction
"FUNCTION: TreeFileNode.New(path) {{{1
"Returns a new TreeNode object with the given path and parent
"
"Args:
"path: a path object representing the full filesystem path to the file/dir that the node represents
function! s:TreeFileNode.New(path)
if a:path.isDirectory
return g:NERDTreeDirNode.New(a:path)
else
let newTreeNode = copy(self)
let newTreeNode.path = a:path
let newTreeNode.parent = {}
return newTreeNode
endif
endfunction
"FUNCTION: TreeFileNode.open() {{{1
function! s:TreeFileNode.open(...)
let opts = a:0 ? a:1 : {}
let opener = g:NERDTreeOpener.New(self.path, opts)
call opener.open(self)
endfunction
"FUNCTION: TreeFileNode.openSplit() {{{1
"Open this node in a new window
function! s:TreeFileNode.openSplit()
call nerdtree#deprecated('TreeFileNode.openSplit', 'is deprecated, use .open() instead.')
call self.open({'where': 'h'})
endfunction
"FUNCTION: TreeFileNode.openVSplit() {{{1
"Open this node in a new vertical window
function! s:TreeFileNode.openVSplit()
call nerdtree#deprecated('TreeFileNode.openVSplit', 'is deprecated, use .open() instead.')
call self.open({'where': 'v'})
endfunction
"FUNCTION: TreeFileNode.openInNewTab(options) {{{1
function! s:TreeFileNode.openInNewTab(options)
echomsg 'TreeFileNode.openInNewTab is deprecated'
call self.open(extend({'where': 't'}, a:options))
endfunction
"FUNCTION: TreeFileNode.putCursorHere(isJump, recurseUpward){{{1
"Places the cursor on the line number this node is rendered on
"
"Args:
"isJump: 1 if this cursor movement should be counted as a jump by vim
"recurseUpward: try to put the cursor on the parent if the this node isnt
"visible
function! s:TreeFileNode.putCursorHere(isJump, recurseUpward)
let ln = self.getLineNum()
if ln != -1
if a:isJump
mark '
endif
call cursor(ln, col("."))
else
if a:recurseUpward
let node = self
while node != {} && node.getLineNum() ==# -1
let node = node.parent
call node.open()
endwhile
call nerdtree#renderView()
call node.putCursorHere(a:isJump, 0)
endif
endif
endfunction
"FUNCTION: TreeFileNode.refresh() {{{1
function! s:TreeFileNode.refresh()
call self.path.refresh()
endfunction
"FUNCTION: TreeFileNode.rename() {{{1
"Calls the rename method for this nodes path obj
function! s:TreeFileNode.rename(newName)
let newName = substitute(a:newName, '\(\\\|\/\)$', '', '')
call self.path.rename(newName)
call self.parent.removeChild(self)
let parentPath = self.path.getParent()
let newParent = b:NERDTreeRoot.findNode(parentPath)
if newParent != {}
call newParent.createChild(self.path, 1)
call newParent.refresh()
endif
endfunction
"FUNCTION: TreeFileNode.renderToString {{{1
"returns a string representation for this tree to be rendered in the view
function! s:TreeFileNode.renderToString()
return self._renderToString(0, 0, [], self.getChildCount() ==# 1)
endfunction
"Args:
"depth: the current depth in the tree for this call
"drawText: 1 if we should actually draw the line for this node (if 0 then the
"child nodes are rendered only)
"vertMap: a binary array that indicates whether a vertical bar should be draw
"for each depth in the tree
"isLastChild:true if this curNode is the last child of its parent
function! s:TreeFileNode._renderToString(depth, drawText, vertMap, isLastChild)
let output = ""
if a:drawText ==# 1
let treeParts = ''
"get all the leading spaces and vertical tree parts for this line
if a:depth > 1
for j in a:vertMap[0:-2]
if g:NERDTreeDirArrows
let treeParts = treeParts . ' '
else
if j ==# 1
let treeParts = treeParts . '| '
else
let treeParts = treeParts . ' '
endif
endif
endfor
endif
"get the last vertical tree part for this line which will be different
"if this node is the last child of its parent
if !g:NERDTreeDirArrows
if a:isLastChild
let treeParts = treeParts . '`'
else
let treeParts = treeParts . '|'
endif
endif
"smack the appropriate dir/file symbol on the line before the file/dir
"name itself
if self.path.isDirectory
if self.isOpen
if g:NERDTreeDirArrows
let treeParts = treeParts . '▾ '
else
let treeParts = treeParts . '~'
endif
else
if g:NERDTreeDirArrows
let treeParts = treeParts . '▸ '
else
let treeParts = treeParts . '+'
endif
endif
else
if g:NERDTreeDirArrows
let treeParts = treeParts . ' '
else
let treeParts = treeParts . '-'
endif
endif
let line = treeParts . self.displayString()
let output = output . line . "\n"
endif
"if the node is an open dir, draw its children
if self.path.isDirectory ==# 1 && self.isOpen ==# 1
let childNodesToDraw = self.getVisibleChildren()
if len(childNodesToDraw) > 0
"draw all the nodes children except the last
let lastIndx = len(childNodesToDraw)-1
if lastIndx > 0
for i in childNodesToDraw[0:lastIndx-1]
let output = output . i._renderToString(a:depth + 1, 1, add(copy(a:vertMap), 1), 0)
endfor
endif
"draw the last child, indicating that it IS the last
let output = output . childNodesToDraw[lastIndx]._renderToString(a:depth + 1, 1, add(copy(a:vertMap), 0), 1)
endif
endif
return output
endfunction
" vim: set sw=4 sts=4 et fdm=marker:

View file

@ -1,88 +0,0 @@
let s:tree_up_dir_line = '.. (up a dir)'
"NERDTreeFlags are syntax items that should be invisible, but give clues as to
"how things should be highlighted
syn match NERDTreeFlag #\~#
syn match NERDTreeFlag #\[RO\]#
"highlighting for the .. (up dir) line at the top of the tree
execute "syn match NERDTreeUp #\\V". s:tree_up_dir_line ."#"
"highlighting for the ~/+ symbols for the directory nodes
syn match NERDTreeClosable #\~\<#
syn match NERDTreeClosable #\~\.#
syn match NERDTreeOpenable #+\<#
syn match NERDTreeOpenable #+\.#he=e-1
"highlighting for the tree structural parts
syn match NERDTreePart #|#
syn match NERDTreePart #`#
syn match NERDTreePartFile #[|`]-#hs=s+1 contains=NERDTreePart
"quickhelp syntax elements
syn match NERDTreeHelpKey #" \{1,2\}[^ ]*:#hs=s+2,he=e-1
syn match NERDTreeHelpKey #" \{1,2\}[^ ]*,#hs=s+2,he=e-1
syn match NERDTreeHelpTitle #" .*\~#hs=s+2,he=e-1 contains=NERDTreeFlag
syn match NERDTreeToggleOn #".*(on)#hs=e-2,he=e-1 contains=NERDTreeHelpKey
syn match NERDTreeToggleOff #".*(off)#hs=e-3,he=e-1 contains=NERDTreeHelpKey
syn match NERDTreeHelpCommand #" :.\{-}\>#hs=s+3
syn match NERDTreeHelp #^".*# contains=NERDTreeHelpKey,NERDTreeHelpTitle,NERDTreeFlag,NERDTreeToggleOff,NERDTreeToggleOn,NERDTreeHelpCommand
"highlighting for readonly files
syn match NERDTreeRO #.*\[RO\]#hs=s+2 contains=NERDTreeFlag,NERDTreeBookmark,NERDTreePart,NERDTreePartFile
"highlighting for sym links
syn match NERDTreeLink #[^-| `].* -> # contains=NERDTreeBookmark,NERDTreeOpenable,NERDTreeClosable,NERDTreeDirSlash
"highlighing for directory nodes and file nodes
syn match NERDTreeDirSlash #/#
syn match NERDTreeDir #[^-| `].*/# contains=NERDTreeLink,NERDTreeDirSlash,NERDTreeOpenable,NERDTreeClosable
syn match NERDTreeExecFile #[|` ].*\*\($\| \)# contains=NERDTreeLink,NERDTreePart,NERDTreeRO,NERDTreePartFile,NERDTreeBookmark
syn match NERDTreeFile #|-.*# contains=NERDTreeLink,NERDTreePart,NERDTreeRO,NERDTreePartFile,NERDTreeBookmark,NERDTreeExecFile
syn match NERDTreeFile #`-.*# contains=NERDTreeLink,NERDTreePart,NERDTreeRO,NERDTreePartFile,NERDTreeBookmark,NERDTreeExecFile
syn match NERDTreeCWD #^[</].*$#
"highlighting for bookmarks
syn match NERDTreeBookmark # {.*}#hs=s+1
"highlighting for the bookmarks table
syn match NERDTreeBookmarksLeader #^>#
syn match NERDTreeBookmarksHeader #^>-\+Bookmarks-\+$# contains=NERDTreeBookmarksLeader
syn match NERDTreeBookmarkName #^>.\{-} #he=e-1 contains=NERDTreeBookmarksLeader
syn match NERDTreeBookmark #^>.*$# contains=NERDTreeBookmarksLeader,NERDTreeBookmarkName,NERDTreeBookmarksHeader
if exists("g:NERDChristmasTree") && g:NERDChristmasTree
hi def link NERDTreePart Special
hi def link NERDTreePartFile Type
hi def link NERDTreeFile Normal
hi def link NERDTreeExecFile Title
hi def link NERDTreeDirSlash Identifier
hi def link NERDTreeClosable Type
else
hi def link NERDTreePart Normal
hi def link NERDTreePartFile Normal
hi def link NERDTreeFile Normal
hi def link NERDTreeClosable Title
endif
hi def link NERDTreeBookmarksHeader statement
hi def link NERDTreeBookmarksLeader ignore
hi def link NERDTreeBookmarkName Identifier
hi def link NERDTreeBookmark normal
hi def link NERDTreeHelp String
hi def link NERDTreeHelpKey Identifier
hi def link NERDTreeHelpCommand Identifier
hi def link NERDTreeHelpTitle Macro
hi def link NERDTreeToggleOn Question
hi def link NERDTreeToggleOff WarningMsg
hi def link NERDTreeDir Directory
hi def link NERDTreeUp Directory
hi def link NERDTreeCWD Statement
hi def link NERDTreeLink Macro
hi def link NERDTreeOpenable Title
hi def link NERDTreeFlag ignore
hi def link NERDTreeRO WarningMsg
hi def link NERDTreeBookmark Statement
hi def link NERDTreeCurrentNode Search

View file

@ -1,327 +0,0 @@
" File: closetag.vim
" Summary: Functions and mappings to close open HTML/XML tags
" Uses: <C-_> -- close matching open tag
" Author: Steven Mueller <diffusor@ugcs.caltech.edu>
" Last Modified: Tue May 24 13:29:48 PDT 2005
" Version: 0.9.1
" XXX - breaks if close attempted while XIM is in preedit mode
" TODO - allow usability as a global plugin -
" Add g:unaryTagsStack - always contains html tags settings
" and g:closetag_default_xml - user should define this to default to xml
" When a close is attempted but b:unaryTagsStack undefined,
" use b:closetag_html_style to determine if the file is to be treated
" as html or xml. Failing that, check the filetype for xml or html.
" Finally, default to g:closetag_html_style.
" If the file is html, let b:unaryTagsStack=g:unaryTagsStack
" otherwise, let b:unaryTagsStack=""
" TODO - make matching work for all comments
" -- kinda works now, but needs syn sync minlines to be very long
" -- Only check whether in syntax in the beginning, then store comment tags
" in the tagstacks to determine whether to move into or out of comment mode
" TODO - The new normal mode mapping clears recent messages with its <ESC>, and
" it doesn't fix the null-undo issue for vim 5.7 anyway.
" TODO - make use of the following neat features:
" -- the ternary ?: operator
" -- :echomsg and :echoerr
" -- curly brace expansion for variables and function name definitions?
" -- check up on map <blah> \FuncName
"
" Description:
" This script eases redundant typing when writing html or xml files (even if
" you're very good with ctrl-p and ctrl-n :). Hitting ctrl-_ will initiate a
" search for the most recent open tag above that is not closed in the
" intervening space and then insert the matching close tag at the cursor. In
" normal mode, the close tag is inserted one character after cursor rather than
" at it, as if a<C-_> had been used. This allows putting close tags at the
" ends of lines while in normal mode, but disallows inserting them in the
" first column.
"
" For HTML, a configurable list of tags are ignored in the matching process.
" By default, the following tags will not be matched and thus not closed
" automatically: area, base, br, dd, dt, hr, img, input, link, meta, and
" param.
"
" For XML, all tags must have a closing match or be terminated by />, as in
" <empty-element/>. These empty element tags are ignored for matching.
"
" Comment checking is now handled by vim's internal syntax checking. If tag
" closing is initiated outside a comment, only tags outside of comments will
" be matched. When closing tags in comments, only tags within comments will
" be matched, skipping any non-commented out code (wee!). However, the
" process of determining the syntax ID of an arbitrary position can still be
" erroneous if a comment is not detected because the syntax highlighting is
" out of sync, or really slow if syn sync minlines is large.
" Set the b:closetag_disable_synID variable to disable this feature if you
" have really big chunks of comment in your code and closing tags is too slow.
"
" If syntax highlighting is not enabled, comments will not be handled very
" well. Commenting out HTML in certain ways may cause a "tag mismatch"
" message and no completion. For example, '<!--a href="blah">link!</a-->'
" between the cursor and the most recent unclosed open tag above causes
" trouble. Properly matched well formed tags in comments don't cause a
" problem.
"
" Install:
" To use, place this file in your standard vim scripts directory, and source
" it while editing the file you wish to close tags in. If the filetype is not
" set or the file is some sort of template with embedded HTML, you may force
" HTML style tag matching by first defining the b:closetag_html_style buffer
" variable. Otherwise, the default is XML style tag matching.
"
" Example:
" :let b:closetag_html_style=1
" :source ~/.vim/scripts/closetag.vim
"
" For greater convenience, load this script in an autocommand:
" :au Filetype html,xml,xsl source ~/.vim/scripts/closetag.vim
"
" Also, set noignorecase for html files or edit b:unaryTagsStack to match your
" capitalization style. You may set this variable before or after loading the
" script, or simply change the file itself.
"
" Configuration Variables:
"
" b:unaryTagsStack Buffer local string containing a whitespace
" seperated list of element names that should be
" ignored while finding matching closetags. Checking
" is done according to the current setting of the
" ignorecase option.
"
" b:closetag_html_style Define this (as with let b:closetag_html_style=1)
" and source the script again to set the
" unaryTagsStack to its default value for html.
"
" b:closetag_disable_synID Define this to disable comment checking if tag
" closing is too slow. This can be set or unset
" without having to source again.
"
" Changelog:
" May 24, 2005 Tuesday
" * Changed function names to be script-local to avoid conflicts with other
" scripts' stack implementations.
"
" June 07, 2001 Thursday
" * Added comment handling. Currently relies on synID, so if syn sync
" minlines is small, the chance for failure is high, but if minlines is
" large, tagclosing becomes rather slow...
"
" * Changed normal mode closetag mapping to use <C-R> in insert mode
" rather than p in normal mode. This has 2 implications:
" - Tag closing no longer clobbers the unnamed register
" - When tag closing fails or finds no match, no longer adds to the undo
" buffer for recent vim 6.0 development versions.
" - However, clears the last message when closing tags in normal mode
"
" * Changed the closetag_html_style variable to be buffer-local rather than
" global.
"
" * Expanded documentation
"------------------------------------------------------------------------------
" User configurable settings
"------------------------------------------------------------------------------
" if html, don't close certain tags. Works best if ignorecase is set.
" otherwise, capitalize these elements according to your html editing style
if !exists("b:unaryTagsStack") || exists("b:closetag_html_style")
if &filetype == "html" || exists("b:closetag_html_style")
let b:unaryTagsStack="area base br dd dt hr img input link meta param"
else " for xsl and xsl
let b:unaryTagsStack=""
endif
endif
" Has this already been loaded?
if exists("loaded_closetag")
finish
endif
let loaded_closetag=1
" set up mappings for tag closing
inoremap <C-_> <C-R>=GetCloseTag()<CR>
map <C-_> a<C-_><ESC>
"------------------------------------------------------------------------------
" Tag closer - uses the stringstack implementation below
"------------------------------------------------------------------------------
" Returns the most recent unclosed tag-name
" (ignores tags in the variable referenced by a:unaryTagsStack)
function! GetLastOpenTag(unaryTagsStack)
" Search backwards through the file line by line using getline()
" Overall strategy (moving backwards through the file from the cursor):
" Push closing tags onto a stack.
" On an opening tag, if the tag matches the stack top, discard both.
" -- if the tag doesn't match, signal an error.
" -- if the stack is empty, use this tag
let linenum=line(".")
let lineend=col(".") - 1 " start: cursor position
let first=1 " flag for first line searched
let b:TagStack="" " main stack of tags
let startInComment=s:InComment()
let tagpat='</\=\(\k\|[-:]\)\+\|/>'
" Search for: closing tags </tag, opening tags <tag, and unary tag ends />
while (linenum>0)
" Every time we see an end-tag, we push it on the stack. When we see an
" open tag, if the stack isn't empty, we pop it and see if they match.
" If no, signal an error.
" If yes, continue searching backwards.
" If stack is empty, return this open tag as the one that needs closing.
let line=getline(linenum)
if first
let line=strpart(line,0,lineend)
else
let lineend=strlen(line)
endif
let b:lineTagStack=""
let mpos=0
let b:TagCol=0
" Search the current line in the forward direction, pushing any tags
" onto a special stack for the current line
while (mpos > -1)
let mpos=matchend(line,tagpat)
if mpos > -1
let b:TagCol=b:TagCol+mpos
let tag=matchstr(line,tagpat)
if exists("b:closetag_disable_synID") || startInComment==s:InCommentAt(linenum, b:TagCol)
let b:TagLine=linenum
call s:Push(matchstr(tag,'[^<>]\+'),"b:lineTagStack")
endif
"echo "Tag: ".tag." ending at position ".mpos." in '".line."'."
let lineend=lineend-mpos
let line=strpart(line,mpos,lineend)
endif
endwhile
" Process the current line stack
while (!s:EmptystackP("b:lineTagStack"))
let tag=s:Pop("b:lineTagStack")
if match(tag, "^/") == 0 "found end tag
call s:Push(tag,"b:TagStack")
"echo linenum." ".b:TagStack
elseif s:EmptystackP("b:TagStack") && !s:Instack(tag, a:unaryTagsStack) "found unclosed tag
return tag
else
let endtag=s:Peekstack("b:TagStack")
if endtag == "/".tag || endtag == "/"
call s:Pop("b:TagStack") "found a open/close tag pair
"echo linenum." ".b:TagStack
elseif !s:Instack(tag, a:unaryTagsStack) "we have a mismatch error
echohl Error
echon "\rError:"
echohl None
echo " tag mismatch: <".tag."> doesn't match <".endtag.">. (Line ".linenum." Tagstack: ".b:TagStack.")"
return ""
endif
endif
endwhile
let linenum=linenum-1 | let first=0
endwhile
" At this point, we have exhausted the file and not found any opening tag
echo "No opening tags."
return ""
endfunction
" Returns closing tag for most recent unclosed tag, respecting the
" current setting of b:unaryTagsStack for tags that should not be closed
function! GetCloseTag()
let tag=GetLastOpenTag("b:unaryTagsStack")
if tag == ""
return ""
else
return "</".tag.">"
endif
endfunction
" return 1 if the cursor is in a syntactically identified comment field
" (fails for empty lines: always returns not-in-comment)
function! s:InComment()
return synIDattr(synID(line("."), col("."), 0), "name") =~ 'Comment'
endfunction
" return 1 if the position specified is in a syntactically identified comment field
function! s:InCommentAt(line, col)
return synIDattr(synID(a:line, a:col, 0), "name") =~ 'Comment'
endfunction
"------------------------------------------------------------------------------
" String Stacks
"------------------------------------------------------------------------------
" These are strings of whitespace-separated elements, matched using the \< and
" \> patterns after setting the iskeyword option.
"
" The sname argument should contain a symbolic reference to the stack variable
" on which method should operate on (i.e., sname should be a string containing
" a fully qualified (ie: g:, b:, etc) variable name.)
" Helper functions
function! s:SetKeywords()
let g:IsKeywordBak=&iskeyword
let &iskeyword="33-255"
endfunction
function! s:RestoreKeywords()
let &iskeyword=g:IsKeywordBak
endfunction
" Push el onto the stack referenced by sname
function! s:Push(el, sname)
if !s:EmptystackP(a:sname)
exe "let ".a:sname."=a:el.' '.".a:sname
else
exe "let ".a:sname."=a:el"
endif
endfunction
" Check whether the stack is empty
function! s:EmptystackP(sname)
exe "let stack=".a:sname
if match(stack,"^ *$") == 0
return 1
else
return 0
endif
endfunction
" Return 1 if el is in stack sname, else 0.
function! s:Instack(el, sname)
exe "let stack=".a:sname
call s:SetKeywords()
let m=match(stack, "\\<".a:el."\\>")
call s:RestoreKeywords()
if m < 0
return 0
else
return 1
endif
endfunction
" Return the first element in the stack
function! s:Peekstack(sname)
call s:SetKeywords()
exe "let stack=".a:sname
let top=matchstr(stack, "\\<.\\{-1,}\\>")
call s:RestoreKeywords()
return top
endfunction
" Remove and return the first element in the stack
function! s:Pop(sname)
if s:EmptystackP(a:sname)
echo "Error! Stack ".a:sname." is empty and can't be popped."
return ""
endif
exe "let stack=".a:sname
" Find the first space, loc is 0-based. Marks the end of 1st elt in stack.
call s:SetKeywords()
let loc=matchend(stack,"\\<.\\{-1,}\\>")
exe "let ".a:sname."=strpart(stack, loc+1, strlen(stack))"
let top=strpart(stack, match(stack, "\\<"), loc)
call s:RestoreKeywords()
return top
endfunction
function! s:Clearstack(sname)
exe "let ".a:sname."=''"
endfunction

View file

@ -27,6 +27,8 @@
set encoding=utf-8 set encoding=utf-8
set nocompatible " désactivation de la compatibilité avec vi set nocompatible " désactivation de la compatibilité avec vi
set shortmess+=filmnrxoOtT " retire le hit <Enter>
colorscheme distinguished " couleur colorscheme distinguished " couleur
set background=dark " fond noir par défaut set background=dark " fond noir par défaut
"hi CursorLine guibg=#606060 " couleur de la ligne de curseur "hi CursorLine guibg=#606060 " couleur de la ligne de curseur
@ -78,6 +80,12 @@ set foldcolumn=6 " colonne de replis(fold colding)
set viminfo='10,\"100,:20,%,n~/.viminfo set viminfo='10,\"100,:20,%,n~/.viminfo
au BufReadPost * if line("'\"") > 0|if line("'\"") <= line("$")|exe("norm '\"")|else|exe "norm $"|endif|endif au BufReadPost * if line("'\"") > 0|if line("'\"") <= line("$")|exe("norm '\"")|else|exe "norm $"|endif|endif
""""""""""""""""""""""""""""""""""""""""""""""""""
"Appel des bundles avec pathogen
""""""""""""""""""""""""""""""""""""""""""""""""""
runtime! autoload/pathogen.vim
silent! call pathogen#helptags()
silent! call pathogen#runtime_append_all_bundles()
"""""""""""""""""""""""""""""""""""""""""""""""""" """"""""""""""""""""""""""""""""""""""""""""""""""
@ -106,18 +114,18 @@ imap <silent> <F11> <Esc>:set noexpandtab<CR>
"Sauvegarde automatique des vues, utiles pour les "Sauvegarde automatique des vues, utiles pour les
"replis manuels "replis manuels
"""""""""""""""""""""""""""""""""""""""""""""""""" """"""""""""""""""""""""""""""""""""""""""""""""""
au BufWinLeave *.html mkview "au BufWinLeave *.html mkview
au BufWinEnter *.html silent loadview "au BufWinEnter *.html silent loadview
au BufWinLeave *.css mkview "au BufWinLeave *.css mkview
au BufWinEnter *.css silent loadview "au BufWinEnter *.css silent loadview
au BufWinLeave *.php mkview "au BufWinLeave *.php mkview
au BufWinEnter *.php silent loadview "au BufWinEnter *.php silent loadview
au BufWinLeave *.js mkview "au BufWinLeave *.js mkview
au BufWinEnter *.js silent loadview "au BufWinEnter *.js silent loadview
au BufWinLeave *.py mkview "au BufWinLeave *.py mkview
au BufWinEnter *.py silent loadview "au BufWinEnter *.py silent loadview
au BufWinLeave *.java mkview "au BufWinLeave *.java mkview
au BufWinEnter *.java silent loadview "au BufWinEnter *.java silent loadview
"""""""""""""""""""""""""""""""""""""""""""""""" """"""""""""""""""""""""""""""""""""""""""""""""
"Options du fichier de syntaxe python "Options du fichier de syntaxe python
@ -209,3 +217,5 @@ if ! has('gui_running')
au InsertLeave * set timeoutlen=1000 au InsertLeave * set timeoutlen=1000
augroup END augroup END
endif endif
let g:jedi#autocompletion_command = "<C-f>"

View file

@ -100,4 +100,3 @@ hisoka() { ssc hisoka.kujiu.org ${argv[-1]:-`whoami`} 22 $argv[1,-2];
hl() { highlight --out-format=xterm256 -l ${argv} | less -R; } hl() { highlight --out-format=xterm256 -l ${argv} | less -R; }
bindkey "^[[A" history-search-backward bindkey "^[[A" history-search-backward
bindkey "^[[B" history-search-forward bindkey "^[[B" history-search-forward

View file

@ -1,13 +1,75 @@
#!/usr/bin/env bash #!/usr/bin/env bash
# Remove zsh completion dump
rm ~/.zcompdump
# Remove vim config directory
rm -rf ~/.vim-backup
mv ~/.vim ~/.vim-backup
mkdir -p ~/.vim/{autoload,bundle,colors,doc,ftplugin,plugin,spell,syntax}
# Install oh-my-zsh
rm -rf ~/.oh-my-zsh rm -rf ~/.oh-my-zsh
git clone git://github.com/robbyrussell/oh-my-zsh.git ~/.oh-my-zsh git clone git://github.com/robbyrussell/oh-my-zsh.git ~/.oh-my-zsh
# Copy the graphical part if needed
if [ "$1" == '--graphic' ] if [ "$1" == '--graphic' ]
then then
rsync -av graphic/ ~/ rsync -av graphic/ ~/
fc-cache -vf ~/.fonts fc-cache -vf ~/.fonts
fi fi
# Copy the common part
rsync -av common/ ~/ rsync -av common/ ~/
# Adjust the tmux conf
sed -i "s/REPLACEMEWITHFIRSTLETTEROFHOSTNAME/`expr substr \`hostname\` 1 1`/g" ~/.tmux.conf sed -i "s/REPLACEMEWITHFIRSTLETTEROFHOSTNAME/`expr substr \`hostname\` 1 1`/g" ~/.tmux.conf
######### VIM Config ###########
# Install pathogen
curl -Sso ~/.vim/autoload/pathogen.vim https://raw.github.com/tpope/vim-pathogen/master/autoload/pathogen.vim
# Install jedi plugin for vim
rm -rf ~/.vim/bundle/jedi
git clone https://github.com/davidhalter/jedi-vim.git ~/.vim/bundle/jedi
# Install nerdtree for vim
rm -rf ~/.vim/bundle/nerdtree
git clone https://github.com/scrooloose/nerdtree.git ~/.vim/bundle/nerdtree
# Install xmledit for vim
rm -rf ~/.vim/bundle/xmledit
git clone https://github.com/sukima/xmledit.git ~/.vim/bundle/xmledit
pushd ~/.vim/bundle/xmledit
make install
popd
# Install pyflakes plugin
rm -rf ~/.vim/bundle/pyflakes
git clone --recursive git://github.com/kevinw/pyflakes-vim.git ~/.vim/bundle/pyflakes
# Install autoclose plugin
rm -rf ~/.vim/bundle/autoclose
git clone https://github.com/Townk/vim-autoclose.git ~/.vim/bundle/autoclose
# Install tagbar plugin
rm -rf ~/.vim/bundle/tagbar
git clone https://github.com/majutsushi/tagbar ~/.vim/bundle/tagbar
# Install vcscommand plugin
rm -rf ~/.vim/bundle/vcscommand
git clone https://github.com/vim-scripts/vcscommand.vim.git ~/.vim/bundle/vcscommand
# Install easytag plugin
rm -rf ~/.vim/bundle/easytag
git clone https://github.com/vim-scripts/easytags.vim.git ~/.vim/bundle/easytag
# Install closetag plugin
rm -rf ~/.vim/bundle/closetag
git clone https://github.com/vim-scripts/closetag.vim.git ~/.vim/bundle/closetag
# Install css3 syntax
rm -rf ~/.vim/bundle/css3-mod
git clone https://github.com/vim-scripts/css3-mod.git ~/.vim/bundle/css3-mod