parent
990414dc34
commit
2f67f708cd
|
@ -152,7 +152,7 @@ class Application(object):
|
||||||
'method', 'method.svn', 'method.cvs', 'method.search',
|
'method', 'method.svn', 'method.cvs', 'method.search',
|
||||||
'method.buffers', 'method.move', 'method.shell',
|
'method.buffers', 'method.move', 'method.shell',
|
||||||
'method.introspect', 'method.help', 'method.numbers',
|
'method.introspect', 'method.help', 'method.numbers',
|
||||||
'method.spell', 'method.hg', 'method.utf8',
|
'method.spell', 'method.hg', 'method.utf8', 'method.tags',
|
||||||
)
|
)
|
||||||
for name in names:
|
for name in names:
|
||||||
exec("import %s" % name)
|
exec("import %s" % name)
|
||||||
|
@ -231,6 +231,15 @@ class Application(object):
|
||||||
self.registers = {}
|
self.registers = {}
|
||||||
self.arg_history = {'default': []}
|
self.arg_history = {'default': []}
|
||||||
|
|
||||||
|
# this is used to maintain state about various things (ctags, version
|
||||||
|
# control, etc) which is shared across several different buffers.
|
||||||
|
# this is so it will be correctly inherited by new buffers, and also
|
||||||
|
# so that when buffers are closed this state won't be lost.
|
||||||
|
self.state = {
|
||||||
|
'tags': {},
|
||||||
|
'vc': {},
|
||||||
|
}
|
||||||
|
|
||||||
# initialize tab handlers
|
# initialize tab handlers
|
||||||
completer.set_completer('path', completer.FileCompleter(self))
|
completer.set_completer('path', completer.FileCompleter(self))
|
||||||
completer.set_completer('buffer', completer.BufferCompleter(self))
|
completer.set_completer('buffer', completer.BufferCompleter(self))
|
||||||
|
|
12
default.py
12
default.py
|
@ -15,6 +15,18 @@ def last_buffer(w):
|
||||||
def current_buffer(w):
|
def current_buffer(w):
|
||||||
return w.buffer.name()
|
return w.buffer.name()
|
||||||
|
|
||||||
|
def current_word(w):
|
||||||
|
return w.get_word() or ''
|
||||||
|
|
||||||
|
def current_token(w):
|
||||||
|
if w.mode.name not in w.buffer.highlights:
|
||||||
|
return ''
|
||||||
|
token = w.get_token()
|
||||||
|
if token:
|
||||||
|
return token.string
|
||||||
|
else:
|
||||||
|
return ''
|
||||||
|
|
||||||
def last_replace_before(w):
|
def last_replace_before(w):
|
||||||
a = w.application
|
a = w.application
|
||||||
if a.config.get('use_last_replace') and a.last_replace_before:
|
if a.config.get('use_last_replace') and a.last_replace_before:
|
||||||
|
|
|
@ -0,0 +1,186 @@
|
||||||
|
"""
|
||||||
|
Etags parser
|
||||||
|
|
||||||
|
:author: Dan Williams
|
||||||
|
"""
|
||||||
|
|
||||||
|
import os
|
||||||
|
import re
|
||||||
|
from stat import ST_MTIME
|
||||||
|
from subprocess import Popen, PIPE, STDOUT
|
||||||
|
|
||||||
|
|
||||||
|
class EtagsRunError(Exception): pass
|
||||||
|
|
||||||
|
|
||||||
|
class TagManager(object):
|
||||||
|
lang = None
|
||||||
|
prune = ('SCCS', 'RCS', 'CVS', '.svn', '.hg', '.git', '.bzr')
|
||||||
|
exts = set()
|
||||||
|
|
||||||
|
def __init__(self, base='.'):
|
||||||
|
self.etags = None
|
||||||
|
self.base = base
|
||||||
|
self.path = os.path.join(self.base, 'TAGS')
|
||||||
|
self.update()
|
||||||
|
|
||||||
|
def has(self, name):
|
||||||
|
return name in self.etags.tag_map
|
||||||
|
|
||||||
|
def get(self, name):
|
||||||
|
return self.etags.tag_map.get(name, [])
|
||||||
|
|
||||||
|
def update(self):
|
||||||
|
if self.is_outdated():
|
||||||
|
self.run()
|
||||||
|
self.etags = Etags(self.path)
|
||||||
|
self.etags.parse()
|
||||||
|
|
||||||
|
def run(self):
|
||||||
|
lf = '--language-force=%s' % self.lang
|
||||||
|
args = ['ctags', '-e', '-f', self.path, lf, '-L-']
|
||||||
|
pipe = Popen(args, stdin=PIPE, stdout=PIPE, stderr=STDOUT)
|
||||||
|
indata = '\n'.join(self.get_paths()) + '\n'
|
||||||
|
outdata = pipe.communicate(indata)
|
||||||
|
if pipe.returncode != 0:
|
||||||
|
raise EtagsRunError(outdata)
|
||||||
|
|
||||||
|
def get_paths(self):
|
||||||
|
return list(self._walk(mtime=0))
|
||||||
|
|
||||||
|
def is_outdated(self):
|
||||||
|
if not os.path.exists(self.path):
|
||||||
|
return True
|
||||||
|
mtime = os.stat(self.path)[ST_MTIME]
|
||||||
|
itr = self._walk(mtime)
|
||||||
|
try:
|
||||||
|
itr.next()
|
||||||
|
return True
|
||||||
|
except StopIteration:
|
||||||
|
return False
|
||||||
|
|
||||||
|
def _walk(self, mtime=0):
|
||||||
|
paths = []
|
||||||
|
for root, dirs, files in os.walk(self.base):
|
||||||
|
for d in dirs:
|
||||||
|
if d in self.prune:
|
||||||
|
dirs.remove(d)
|
||||||
|
for f in files:
|
||||||
|
path = os.path.join(root, f)
|
||||||
|
if not self._match(path):
|
||||||
|
continue
|
||||||
|
elif os.stat(path)[ST_MTIME] < mtime:
|
||||||
|
continue
|
||||||
|
else:
|
||||||
|
yield os.path.join(root, f)
|
||||||
|
raise StopIteration
|
||||||
|
|
||||||
|
def _match(self, path):
|
||||||
|
_, ext = os.path.splitext(path)
|
||||||
|
return ext in self.exts
|
||||||
|
|
||||||
|
class Etags(object):
|
||||||
|
def __init__(self, fname=None):
|
||||||
|
self.fname = fname
|
||||||
|
self.rawdata = None
|
||||||
|
self.record_list = []
|
||||||
|
self.tag_map = {}
|
||||||
|
|
||||||
|
def _load(self):
|
||||||
|
fd = file(self.fname, 'r')
|
||||||
|
self.rawdata = fd.read()
|
||||||
|
fd.close()
|
||||||
|
|
||||||
|
def lookup(self, tag):
|
||||||
|
return self.tag_map[tag]
|
||||||
|
|
||||||
|
def __getitem__(self, tag):
|
||||||
|
return self.lookup(tag)
|
||||||
|
|
||||||
|
def parse(self, fname=None):
|
||||||
|
"""
|
||||||
|
Parser is based on the little info found in
|
||||||
|
Wikipedia: http://en.wikipedia.org/wiki/Ctags
|
||||||
|
"""
|
||||||
|
if fname:
|
||||||
|
self.fname = fname
|
||||||
|
self._load()
|
||||||
|
i = 0
|
||||||
|
data_len = len(self.rawdata)
|
||||||
|
data = self.rawdata
|
||||||
|
while i < data_len:
|
||||||
|
if ord(data[i]) == 0xc:
|
||||||
|
i = self._parse_block(data, i+2)
|
||||||
|
|
||||||
|
def _add_record(self, record):
|
||||||
|
self.record_list.append(record)
|
||||||
|
name = record.name
|
||||||
|
if name is None:
|
||||||
|
return
|
||||||
|
self.tag_map.setdefault(name, [])
|
||||||
|
self.tag_map[name].append(record)
|
||||||
|
|
||||||
|
def _parse_block(self, data, i):
|
||||||
|
n = data[i:].find('\n') + i
|
||||||
|
l = data[i:n]
|
||||||
|
try:
|
||||||
|
filename, size = l.split(',')
|
||||||
|
except ValueError:
|
||||||
|
print i
|
||||||
|
raise
|
||||||
|
size = int(size)
|
||||||
|
subblock = data[n+1:n+size+1]
|
||||||
|
# ...
|
||||||
|
for lineitem in subblock.split('\n'):
|
||||||
|
if len(lineitem) == 0:
|
||||||
|
continue
|
||||||
|
record = self._parse_record(lineitem, filename)
|
||||||
|
self._add_record(record)
|
||||||
|
return n+size+1
|
||||||
|
|
||||||
|
def _parse_record(self, lineitem, filename):
|
||||||
|
try:
|
||||||
|
defn, rest = lineitem.split(chr(0x7f))
|
||||||
|
except ValueError:
|
||||||
|
print lineitem
|
||||||
|
raise
|
||||||
|
name = None
|
||||||
|
if chr(0x01) in rest:
|
||||||
|
name, rest = rest.split(chr(0x01))
|
||||||
|
else:
|
||||||
|
txt = defn.strip()
|
||||||
|
sp = re.split('[ ,;*()\t&=]', txt)
|
||||||
|
sp = [x for x in sp if x != '']
|
||||||
|
if len(sp):
|
||||||
|
name = sp[-1]
|
||||||
|
|
||||||
|
tokens = rest.split(',')
|
||||||
|
line = int(tokens[0])
|
||||||
|
byte = int(tokens[1])
|
||||||
|
|
||||||
|
record = EtagRecord(path=filename, defn=defn, name=name, line=line, byte=byte)
|
||||||
|
return record
|
||||||
|
|
||||||
|
class EtagRecord(object):
|
||||||
|
def __init__(self, **kwargs):
|
||||||
|
self.path = None
|
||||||
|
self.defn = None
|
||||||
|
self.name = None
|
||||||
|
self.line = -1
|
||||||
|
self.byte = None
|
||||||
|
self.__dict__.update(kwargs)
|
||||||
|
|
||||||
|
def __repr__(self):
|
||||||
|
return "%s [%s:%d]" % (self.name, self.path, self.line)
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
if __name__ == '__main__':
|
||||||
|
import sys
|
||||||
|
from pprint import pprint
|
||||||
|
etags = Etags(sys.argv[1])
|
||||||
|
etags.parse()
|
||||||
|
if len(sys.argv) > 2:
|
||||||
|
print etags[sys.argv[2]]
|
||||||
|
else:
|
||||||
|
pprint(etags.record_list)
|
|
@ -0,0 +1,91 @@
|
||||||
|
import os.path
|
||||||
|
from method import Method, arg
|
||||||
|
import completer
|
||||||
|
from etags import TagManager
|
||||||
|
import default
|
||||||
|
|
||||||
|
class FindTag(Method):
|
||||||
|
args = [arg('tag', p='Tag name: ', dv=default.current_token, ld=True, h='Search for a tag')]
|
||||||
|
def _execute(self, w, **vargs):
|
||||||
|
tag = vargs['tag']
|
||||||
|
b = w.buffer
|
||||||
|
a = w.application
|
||||||
|
|
||||||
|
InitTags().execute(w)
|
||||||
|
base = b.settings[w.mode.name].get('tag-base')
|
||||||
|
m = a.state['tags'][base]
|
||||||
|
|
||||||
|
records = m.get(tag)
|
||||||
|
if not records:
|
||||||
|
w.set_error('tag %r was not found' % tag)
|
||||||
|
return
|
||||||
|
|
||||||
|
if len(records) == 1:
|
||||||
|
b = a.open_path(records[0].path)
|
||||||
|
a.switch_buffer(b)
|
||||||
|
b.windows[0].goto_line(records[0].line)
|
||||||
|
w.set_error('found one record for tag %r' % tag)
|
||||||
|
return
|
||||||
|
|
||||||
|
cwd = os.getcwd()
|
||||||
|
if not cwd.endswith('/'):
|
||||||
|
cwd += '/'
|
||||||
|
|
||||||
|
tpls = [(r.path.replace(cwd, ''), r.line, r.defn) for r in records]
|
||||||
|
|
||||||
|
data = '\n'.join(['%s:%d:%s' % tpl for tpl in tpls]) + '\n'
|
||||||
|
a.data_buffer("*Tag-Records*", data, switch_to=True, modename='error')
|
||||||
|
w.set_error('found %d records for tag %r' % (len(records), tag))
|
||||||
|
|
||||||
|
|
||||||
|
class InitTags(Method):
|
||||||
|
manager_cls = None
|
||||||
|
def _save_manager(self, w, base):
|
||||||
|
m = w.mode.tagcls(base)
|
||||||
|
w.application.state['tags'][base] = m
|
||||||
|
w.buffer.settings[w.mode.name]['tag-base'] = base
|
||||||
|
ntag, nrec = len(m.etags.tag_map), len(m.etags.record_list)
|
||||||
|
w.set_error('%s: loaded %d names (%d records)' % (m.path, ntag, nrec))
|
||||||
|
|
||||||
|
def _execute(self, w, **vargs):
|
||||||
|
b = w.buffer
|
||||||
|
a = w.application
|
||||||
|
a.state.setdefault('tags', {})
|
||||||
|
|
||||||
|
if not b.path:
|
||||||
|
raise Exception('Buffer %r has no path' % b.name())
|
||||||
|
|
||||||
|
t = b.settings['C'].get('tag-base')
|
||||||
|
if t and t in a.state['tags']:
|
||||||
|
m = a.state['tags'][t]
|
||||||
|
if m.is_outdated():
|
||||||
|
m.update()
|
||||||
|
ntag, nrec = len(m.etags.tag_map), len(m.etags.record_list)
|
||||||
|
fmt = '%s: updated %d names (%d records)'
|
||||||
|
w.set_error(fmt % (m.path, ntag, nrec))
|
||||||
|
else:
|
||||||
|
w.set_error('%s: is up-to-date' % m.path)
|
||||||
|
return
|
||||||
|
|
||||||
|
base = b.path
|
||||||
|
while 'tag-base' not in b.settings['C']:
|
||||||
|
base, tail = os.path.split(base)
|
||||||
|
if not tail:
|
||||||
|
break
|
||||||
|
|
||||||
|
if base in a.state['tags']:
|
||||||
|
return self._save_manager(w, base)
|
||||||
|
elif os.path.exists(os.path.join(base, 'TAGS')):
|
||||||
|
return self._save_manager(w, base)
|
||||||
|
|
||||||
|
if 'tag-base' not in b.settings['C']:
|
||||||
|
self._old_window = w
|
||||||
|
self._prompt = "Enter source directory: "
|
||||||
|
c = completer.get_completer('path')
|
||||||
|
d = os.path.dirname(w.buffer.path)
|
||||||
|
a.open_mini_buffer(self._prompt, self._cb, tabber=c, startvalue=d)
|
||||||
|
|
||||||
|
def _cb(self, v):
|
||||||
|
w = self._old_window
|
||||||
|
w.application.close_mini_buffer()
|
||||||
|
self._save_manager(w, v)
|
|
@ -261,9 +261,9 @@ class Fundamental(Handler):
|
||||||
self.add_bindings('insert-multiline-text', ('C-c m',))
|
self.add_bindings('insert-multiline-text', ('C-c m',))
|
||||||
self.add_bindings('increment', ('M-+', 'M-='))
|
self.add_bindings('increment', ('M-+', 'M-='))
|
||||||
self.add_bindings('decrement', ('M--',))
|
self.add_bindings('decrement', ('M--',))
|
||||||
|
|
||||||
self.add_bindings('uppercase-word', ('M-u',))
|
self.add_bindings('uppercase-word', ('M-u',))
|
||||||
self.add_bindings('lowercase-word', ('M-l',))
|
self.add_bindings('lowercase-word', ('M-l',))
|
||||||
|
self.add_bindings('find-tag', ('M-.',))
|
||||||
|
|
||||||
# used for all word operations
|
# used for all word operations
|
||||||
if not self.word_letters:
|
if not self.word_letters:
|
||||||
|
|
11
mode/c.py
11
mode/c.py
|
@ -1,10 +1,14 @@
|
||||||
import os.path
|
import os.path
|
||||||
from subprocess import Popen, PIPE, STDOUT
|
from subprocess import Popen, PIPE, STDOUT
|
||||||
|
from method import Method, arg
|
||||||
from method.shell import Exec
|
from method.shell import Exec
|
||||||
|
from method.tags import InitTags
|
||||||
from mode import Fundamental
|
from mode import Fundamental
|
||||||
import tab
|
import tab
|
||||||
|
import completer
|
||||||
from lex import Grammar, PatternRule, RegionRule, PatternMatchRule, OverridePatternRule
|
from lex import Grammar, PatternRule, RegionRule, PatternMatchRule, OverridePatternRule
|
||||||
from mode.python import StringGrammar2
|
from mode.python import StringGrammar2
|
||||||
|
from etags import TagManager
|
||||||
|
|
||||||
class CommentGrammar(Grammar):
|
class CommentGrammar(Grammar):
|
||||||
rules = [
|
rules = [
|
||||||
|
@ -148,6 +152,11 @@ class CTabber2(tab.StackTabber2):
|
||||||
return t.fqisa('spaces', 'eol', 'c.comment', 'c.comment.start',
|
return t.fqisa('spaces', 'eol', 'c.comment', 'c.comment.start',
|
||||||
'c.comment.data', 'c.comment.null', 'c.comment.end')
|
'c.comment.data', 'c.comment.null', 'c.comment.end')
|
||||||
|
|
||||||
|
class CTagManager(TagManager):
|
||||||
|
lang = 'C'
|
||||||
|
exts = set(('.c', '.h'))
|
||||||
|
|
||||||
|
|
||||||
class CCheckSyntax(Exec):
|
class CCheckSyntax(Exec):
|
||||||
'''Build this C program (using the mode's make cmd)'''
|
'''Build this C program (using the mode's make cmd)'''
|
||||||
show_success = False
|
show_success = False
|
||||||
|
@ -207,11 +216,13 @@ class C(Fundamental):
|
||||||
name = 'C'
|
name = 'C'
|
||||||
extensions = ['.c', '.h', '.cpp']
|
extensions = ['.c', '.h', '.cpp']
|
||||||
tabbercls = CTabber2
|
tabbercls = CTabber2
|
||||||
|
tagcls = CTagManager
|
||||||
grammar = CGrammar
|
grammar = CGrammar
|
||||||
opentokens = ('delimiter',)
|
opentokens = ('delimiter',)
|
||||||
opentags = {'(': ')', '[': ']', '{': '}'}
|
opentags = {'(': ')', '[': ']', '{': '}'}
|
||||||
closetokens = ('delimiter',)
|
closetokens = ('delimiter',)
|
||||||
closetags = {')': '(', ']': '[', '}': '{'}
|
closetags = {')': '(', ']': '[', '}': '{'}
|
||||||
|
#actions = [CCheckSyntax, CMake, CInitTags]
|
||||||
actions = [CCheckSyntax, CMake]
|
actions = [CCheckSyntax, CMake]
|
||||||
format = "%(flag)s %(bname)s (%(mname)s) %(indent)s %(cursor)s %(perc)s [%(func)s] %(vc-info)s"
|
format = "%(flag)s %(bname)s (%(mname)s) %(indent)s %(cursor)s %(perc)s [%(func)s] %(vc-info)s"
|
||||||
commentc = '//'
|
commentc = '//'
|
||||||
|
|
Loading…
Reference in New Issue