parent
733dba7274
commit
975b3665d9
|
@ -14,6 +14,7 @@ use strict;
|
||||||
use warnings;
|
use warnings;
|
||||||
|
|
||||||
use DBI;
|
use DBI;
|
||||||
|
use Carp;
|
||||||
|
|
||||||
use TBB::ID;
|
use TBB::ID;
|
||||||
|
|
||||||
|
|
327
mode_perl.py
327
mode_perl.py
|
@ -79,8 +79,6 @@ class PerlGrammar(Grammar):
|
||||||
PatternRule(name=r'method', pattern=r"(?<=->)[a-zA-Z_][a-zA-Z_0-9]*"),
|
PatternRule(name=r'method', pattern=r"(?<=->)[a-zA-Z_][a-zA-Z_0-9]*"),
|
||||||
PatternRule(name=r'function', pattern=r"&\$*(?:[a-zA-Z_][a-zA-Z_0-9]*::)*[a-zA-Z_][a-zA-Z_0-9]*"),
|
PatternRule(name=r'function', pattern=r"&\$*(?:[a-zA-Z_][a-zA-Z_0-9]*::)*[a-zA-Z_][a-zA-Z_0-9]*"),
|
||||||
PatternRule(name=r'builtin', pattern=r"(?<!->)&?(?:write|warn|wantarray|waitpid|wait|vec|values|utime|use|untie|unshift|unpack|unlink|undef|umask|ucfirst|uc|truncate|times|time|tied|tie|telldir|tell|syswrite|system|sysseek|sysread|sysopen|syscall|symlink|substr|sub|study|stat|srand|sqrt|sprintf|split|splice|sort|socketpair|socket|sleep|sin|shutdown|shmwrite|shmread|shmget|shmctl|shift|setsockopt|setservent|setpwent|setprotoent|setpriority|setpgrp|setnetent|sethostent|setgrent|send|semop|semget|semctl|select|seekdir|seek|scalar|rmdir|rindex|rewinddir|reverse|return|reset|require|rename|ref|redo|recv|readpipe|readlink|readline|readdir|read|rand|quotemeta|push|prototype|printf|print|pos|pop|pipe|package|pack|our|ord|opendir|open|oct|no|next|my|msgsnd|msgrcv|msgget|msgctl|mkdir|map|lstat|log|lock|localtime|local|listen|link|length|lcfirst|lc|last|kill|keys|join|ioctl|int|index|import|hex|grep|goto|gmtime|glob|getsockopt|getsockname|getservent|getservbyport|getservbyname|getpwuid|getpwnam|getpwent|getprotoent|getprotobynumber|getprotobyname|getpriority|getppid|getpgrp|getpeername|getnetent|getnetbyname|getnetbyaddr|getlogin|gethostent|gethostbyname|gethostbyaddr|getgrnam|getgrgid|getgrent|getc|formline|format|fork|flock|fileno|fcntl|exp|exit|exists|exec|eval|eof|endservent|endpwent|endprotoent|endnetent|endhostent|endgrent|each|dump|do|die|delete|defined|dbmopen|dbmclose|crypt|cos|continue|connect|closedir|close|chroot|chr|chown|chop|chomp|chmod|chdir|caller|bless|binmode|bind|atan2|alarm|accept|abs)(?![a-zA-Z0-9_])"),
|
PatternRule(name=r'builtin', pattern=r"(?<!->)&?(?:write|warn|wantarray|waitpid|wait|vec|values|utime|use|untie|unshift|unpack|unlink|undef|umask|ucfirst|uc|truncate|times|time|tied|tie|telldir|tell|syswrite|system|sysseek|sysread|sysopen|syscall|symlink|substr|sub|study|stat|srand|sqrt|sprintf|split|splice|sort|socketpair|socket|sleep|sin|shutdown|shmwrite|shmread|shmget|shmctl|shift|setsockopt|setservent|setpwent|setprotoent|setpriority|setpgrp|setnetent|sethostent|setgrent|send|semop|semget|semctl|select|seekdir|seek|scalar|rmdir|rindex|rewinddir|reverse|return|reset|require|rename|ref|redo|recv|readpipe|readlink|readline|readdir|read|rand|quotemeta|push|prototype|printf|print|pos|pop|pipe|package|pack|our|ord|opendir|open|oct|no|next|my|msgsnd|msgrcv|msgget|msgctl|mkdir|map|lstat|log|lock|localtime|local|listen|link|length|lcfirst|lc|last|kill|keys|join|ioctl|int|index|import|hex|grep|goto|gmtime|glob|getsockopt|getsockname|getservent|getservbyport|getservbyname|getpwuid|getpwnam|getpwent|getprotoent|getprotobynumber|getprotobyname|getpriority|getppid|getpgrp|getpeername|getnetent|getnetbyname|getnetbyaddr|getlogin|gethostent|gethostbyname|gethostbyaddr|getgrnam|getgrgid|getgrent|getc|formline|format|fork|flock|fileno|fcntl|exp|exit|exists|exec|eval|eof|endservent|endpwent|endprotoent|endnetent|endhostent|endgrent|each|dump|do|die|delete|defined|dbmopen|dbmclose|crypt|cos|continue|connect|closedir|close|chroot|chr|chown|chop|chomp|chmod|chdir|caller|bless|binmode|bind|atan2|alarm|accept|abs)(?![a-zA-Z0-9_])"),
|
||||||
PatternRule(name=r'function', pattern=r"(?:[a-zA-Z_][a-zA-Z_0-9]*::)*[a-zA-Z_][a-zA-Z_0-9]*(?= *\()"),
|
|
||||||
PatternRule(name=r'class', pattern=r"(?:[a-zA-Z_][a-zA-Z_0-9]*::)*[a-zA-Z_][a-zA-Z_0-9]*(?=->)"),
|
|
||||||
|
|
||||||
# quote operator
|
# quote operator
|
||||||
RegionRule(name=r'quoted', start=r'q[rqwx]? *\(', grammar=g, end=r'\)'),
|
RegionRule(name=r'quoted', start=r'q[rqwx]? *\(', grammar=g, end=r'\)'),
|
||||||
|
@ -90,6 +88,9 @@ class PerlGrammar(Grammar):
|
||||||
RegionRule(name=r'quoted', start=r'q[rqwx]? *(?P<delim>[^ #])', grammar=g, end=r'%(delim)s'),
|
RegionRule(name=r'quoted', start=r'q[rqwx]? *(?P<delim>[^ #])', grammar=g, end=r'%(delim)s'),
|
||||||
RegionRule(name=r'quoted', start=r'q[rqwx]?#', grammar=g, end=r'#'),
|
RegionRule(name=r'quoted', start=r'q[rqwx]?#', grammar=g, end=r'#'),
|
||||||
|
|
||||||
|
PatternRule(name=r'function', pattern=r"(?:[a-zA-Z_][a-zA-Z_0-9]*::)*[a-zA-Z_][a-zA-Z_0-9]*(?= *\()"),
|
||||||
|
PatternRule(name=r'class', pattern=r"(?:[a-zA-Z_][a-zA-Z_0-9]*::)*[a-zA-Z_][a-zA-Z_0-9]*(?=->)"),
|
||||||
|
|
||||||
# nested regions
|
# nested regions
|
||||||
#RegionRule(name=r'paren', start=r'\(', grammar=None, end=r'\)'),
|
#RegionRule(name=r'paren', start=r'\(', grammar=None, end=r'\)'),
|
||||||
#RegionRule(name=r'brace', start=r'{', grammar=None, end=r'}'),
|
#RegionRule(name=r'brace', start=r'{', grammar=None, end=r'}'),
|
||||||
|
@ -169,12 +170,13 @@ class Perl(mode2.Fundamental):
|
||||||
def __init__(self, w):
|
def __init__(self, w):
|
||||||
mode2.Fundamental.__init__(self, w)
|
mode2.Fundamental.__init__(self, w)
|
||||||
|
|
||||||
|
self.add_action_and_bindings(PerlSetLib(), ('C-c l',))
|
||||||
self.add_action_and_bindings(PerlCheckSyntax(), ('C-c s',))
|
self.add_action_and_bindings(PerlCheckSyntax(), ('C-c s',))
|
||||||
self.add_action_and_bindings(PerlHashCleanup(), ('C-c h',))
|
self.add_action_and_bindings(PerlHashCleanup(), ('C-c h',))
|
||||||
#self.add_action_and_bindings(PerlHashCleanup2(), ('C-c h',))
|
#self.add_action_and_bindings(PerlHashCleanup2(), ('C-c h',))
|
||||||
self.add_action_and_bindings(PerlViewModulePerldoc(), ('C-c v',))
|
self.add_action_and_bindings(PerlViewModulePerldoc(), ('C-c v',))
|
||||||
self.add_action_and_bindings(PerlViewWordPerldoc(), ('C-c p',))
|
self.add_action_and_bindings(PerlViewWordPerldoc(), ('C-c p',))
|
||||||
self.add_action_and_bindings(PerlWrapLine(), ('M-q',))
|
#self.add_action_and_bindings(PerlWrapLine(), ('M-q',))
|
||||||
self.add_action_and_bindings(PerlGotoFunction(), ('C-c M-g',))
|
self.add_action_and_bindings(PerlGotoFunction(), ('C-c M-g',))
|
||||||
self.add_action_and_bindings(PerlWhichFunction(), ('C-c w',))
|
self.add_action_and_bindings(PerlWhichFunction(), ('C-c w',))
|
||||||
self.add_action_and_bindings(PerlListFunctions(), ('C-c W',))
|
self.add_action_and_bindings(PerlListFunctions(), ('C-c W',))
|
||||||
|
@ -279,6 +281,10 @@ class Perl(mode2.Fundamental):
|
||||||
'translate.end': color.build('magenta', 'default'),
|
'translate.end': color.build('magenta', 'default'),
|
||||||
'translate.null': color.build('magenta', 'default'),
|
'translate.null': color.build('magenta', 'default'),
|
||||||
}
|
}
|
||||||
|
|
||||||
|
# perl-specific
|
||||||
|
self.functions = None
|
||||||
|
self.perllib = 'lib'
|
||||||
|
|
||||||
def name(self):
|
def name(self):
|
||||||
return "Perl"
|
return "Perl"
|
||||||
|
@ -290,88 +296,36 @@ class Perl(mode2.Fundamental):
|
||||||
m = regex.perl_function.match(b.lines[i])
|
m = regex.perl_function.match(b.lines[i])
|
||||||
if m:
|
if m:
|
||||||
self.functions[m.group(1)] = i
|
self.functions[m.group(1)] = i
|
||||||
|
|
||||||
def get_functions(self):
|
def get_functions(self):
|
||||||
if self.functions is None:
|
if self.functions is None:
|
||||||
self.build_function_map()
|
self.build_function_map()
|
||||||
return self.functions
|
return self.functions
|
||||||
|
|
||||||
def get_function_names(self):
|
def get_function_names(self):
|
||||||
functions = self.get_functions()
|
functions = self.get_functions()
|
||||||
pairs = [[functions[key], key] for key in functions]
|
pairs = [[functions[key], key] for key in functions]
|
||||||
pairs.sort()
|
pairs.sort()
|
||||||
names = [x[1] for x in pairs]
|
names = [x[1] for x in pairs]
|
||||||
return names
|
return names
|
||||||
|
|
||||||
class PerlWrapLine(method.Method):
|
|
||||||
'''Wrap lines, comments, POD'''
|
|
||||||
margin = 80
|
|
||||||
comment_re = re.compile('^( *)(#+)( *)([^ ].*)$')
|
|
||||||
def _execute(self, w, **vargs):
|
|
||||||
pcursor = w.physical_cursor()
|
|
||||||
r = w.get_region(pcursor)
|
|
||||||
if r is None:
|
|
||||||
return
|
|
||||||
|
|
||||||
t = r[4]
|
class PerlSetLib(method.Method):
|
||||||
if t == 'pod':
|
'''Set the path(s) to find perl modules'''
|
||||||
assert False, 'POD: %s' % repr(r)
|
|
||||||
elif t == 'comment':
|
|
||||||
self._wrap_comment(w)
|
|
||||||
else:
|
|
||||||
return
|
|
||||||
|
|
||||||
def _wrap_comment(self, w):
|
|
||||||
l = w.logical_cursor()
|
|
||||||
m = self.comment_re.match(w.buffer.lines[l.y])
|
|
||||||
if not m:
|
|
||||||
assert False, 'no match oh geez'
|
|
||||||
|
|
||||||
pad = m.group(1) + m.group(2) + m.group(3)
|
|
||||||
data = m.group(4) + ' '
|
|
||||||
|
|
||||||
start = l.y
|
|
||||||
end = l.y + 1
|
|
||||||
|
|
||||||
while end < len(w.buffer.lines):
|
|
||||||
m = self.comment_re.match(w.buffer.lines[end])
|
|
||||||
if m:
|
|
||||||
data += m.group(4) + ' '
|
|
||||||
end += 1
|
|
||||||
else:
|
|
||||||
break
|
|
||||||
|
|
||||||
words = [word for word in data.split() if word]
|
|
||||||
|
|
||||||
lines = [pad]
|
|
||||||
for word in words:
|
|
||||||
if len(lines[-1]) == len(pad):
|
|
||||||
lines[-1] += word
|
|
||||||
elif len(lines[-1]) + 1 + len(word) <= self.margin:
|
|
||||||
lines[-1] += ' ' + word
|
|
||||||
else:
|
|
||||||
lines.append(pad + word)
|
|
||||||
|
|
||||||
# remove the old text and add the new
|
|
||||||
start_p = Point(0, start)
|
|
||||||
end_p = Point(len(w.buffer.lines[end-1]), end-1)
|
|
||||||
w.kill(start_p, end_p)
|
|
||||||
w.insert(start_p, '\n'.join(lines))
|
|
||||||
|
|
||||||
class PerlCheckSyntax(method.Method):
|
|
||||||
'''Check the syntax of a perl file'''
|
|
||||||
def _args(self):
|
def _args(self):
|
||||||
return [method.Argument("lib", type=type(""), prompt="Location of lib: ",
|
return [method.Argument("lib", type=type(""), prompt="Location of lib: ",
|
||||||
default=default.build_constant("."))]
|
default=default.build_constant("."))]
|
||||||
def _execute(self, window, **vargs):
|
def _execute(self, w, **vargs):
|
||||||
a = vargs['lib']
|
w.mode.perllib = vargs['lib']
|
||||||
cmd = "perl -c -I '%s' '%s'" % (a, window.buffer.path)
|
|
||||||
|
class PerlCheckSyntax(method.Method):
|
||||||
|
'''Check the syntax of a perl file'''
|
||||||
|
def _execute(self, w, **vargs):
|
||||||
|
app = w.application
|
||||||
|
cmd = "perl -c -I '%s' '%s'" % (w.mode.perllib, w.buffer.path)
|
||||||
(status, output) = commands.getstatusoutput(cmd)
|
(status, output) = commands.getstatusoutput(cmd)
|
||||||
if status == 0:
|
if status == 0:
|
||||||
window.application.set_error("Syntax OK")
|
app.set_error("Syntax OK")
|
||||||
window.application.data_buffer("*Perl-Check-Syntax*", output, switch_to=False)
|
app.data_buffer("*Perl-Check-Syntax*", output, switch_to=False)
|
||||||
else:
|
else:
|
||||||
window.application.data_buffer("*Perl-Check-Syntax*", output)
|
app.data_buffer("*Perl-Check-Syntax*", output)
|
||||||
|
|
||||||
class PerlViewModulePerldoc(method.Method):
|
class PerlViewModulePerldoc(method.Method):
|
||||||
'''View documentation about this file using perldoc'''
|
'''View documentation about this file using perldoc'''
|
||||||
|
@ -382,65 +336,46 @@ class PerlViewModulePerldoc(method.Method):
|
||||||
|
|
||||||
class PerlViewWordPerldoc(method.Method):
|
class PerlViewWordPerldoc(method.Method):
|
||||||
'''View documentation about a package or function using perldoc'''
|
'''View documentation about a package or function using perldoc'''
|
||||||
|
def _try(self, w, word, asfunc=False):
|
||||||
|
if asfunc:
|
||||||
|
cmd = "PERL5LIB=%r perldoc -t -T -f '%s'" % (w.mode.perllib, word)
|
||||||
|
else:
|
||||||
|
cmd = "PERL5LIB=%r perldoc -t -T '%s'" % (w.mode.perllib, word)
|
||||||
|
(status, data) = commands.getstatusoutput(cmd)
|
||||||
|
if status == 0:
|
||||||
|
return data
|
||||||
|
else:
|
||||||
|
return None
|
||||||
|
def _show(self, w, data, word):
|
||||||
|
w.application.data_buffer("*Perldoc*", data, switch_to=True)
|
||||||
|
w.application.set_error('displaying documentation for %r' % word)
|
||||||
def _execute(self, w, **vargs):
|
def _execute(self, w, **vargs):
|
||||||
cursor = w.logical_cursor()
|
word = w.get_word(wl=string.letters + string.digits + '_:')
|
||||||
line = w.buffer.lines[cursor.y]
|
|
||||||
|
|
||||||
word_chars = string.letters + string.digits + '_:'
|
# make sure that the name is (mostly) valid
|
||||||
|
if word is None:
|
||||||
if line[cursor.x] not in word_chars:
|
w.application.set_error('no word selected')
|
||||||
w.application.set_error('error: no word selected')
|
|
||||||
return
|
return
|
||||||
|
elif ':' in word and '::' not in word:
|
||||||
start = cursor.x
|
|
||||||
while start > 0 and line[start - 1] in word_chars:
|
|
||||||
start -= 1
|
|
||||||
|
|
||||||
end = cursor.x + 1
|
|
||||||
while end < len(line) - 1 and line[end] in word_chars:
|
|
||||||
end += 1
|
|
||||||
|
|
||||||
word = line[start:end]
|
|
||||||
w.application.set_error('the current word is: %r' % word)
|
|
||||||
|
|
||||||
ok = False
|
|
||||||
data = ''
|
|
||||||
|
|
||||||
perl_word_re = re.compile('^[a-zA-Z_][a-zA-Z_0-9]*(?:::[a-zA-Z_][a-zA-Z0-9]*)*$')
|
|
||||||
if not perl_word_re.match(word):
|
|
||||||
w.application.set_error('invalid word: %r' % word)
|
w.application.set_error('invalid word: %r' % word)
|
||||||
return
|
return
|
||||||
|
|
||||||
if '::' in word:
|
# first try it is a package
|
||||||
# we are probably dealing with a package
|
parts = word.split('::')
|
||||||
parts = word.split('::')
|
while len(parts) > 0:
|
||||||
while len(parts) > 0:
|
newword = '::'.join(parts)
|
||||||
newword = '::'.join(parts)
|
data = self._try(w, newword, asfunc=False)
|
||||||
cmd = "perldoc -t -T '%s'" % newword
|
if data:
|
||||||
(status, data) = commands.getstatusoutput(cmd)
|
self._show(w, data, newword)
|
||||||
if status == 0:
|
return
|
||||||
word = newword
|
parts.pop(-1)
|
||||||
ok = True
|
|
||||||
break
|
|
||||||
parts.pop(-1)
|
|
||||||
elif ':' in word:
|
|
||||||
w.application.set_error('invalid word2222: %r' % word)
|
|
||||||
return
|
|
||||||
else:
|
|
||||||
cmd = "perldoc -t -T -f '%s'" % word
|
|
||||||
(status, data) = commands.getstatusoutput(cmd)
|
|
||||||
if status == 0:
|
|
||||||
ok = True
|
|
||||||
else:
|
|
||||||
cmd = "perldoc -t -T -f '%s'" % word
|
|
||||||
(status, data) = commands.getstatusoutput(cmd)
|
|
||||||
ok = status == 0
|
|
||||||
|
|
||||||
if not ok:
|
# then try it as a function
|
||||||
w.application.set_error('nothing found for %r' % word)
|
data = self._try(w, word, asfunc=True)
|
||||||
|
if data:
|
||||||
|
self._show(w, data, parts[0])
|
||||||
else:
|
else:
|
||||||
w.application.data_buffer("*Perldoc*", data, switch_to=True)
|
w.application.set_error('nothing found for %r' % word)
|
||||||
w.application.set_error('displaying documentation for %r' % word)
|
|
||||||
|
|
||||||
class PerlGotoFunction(method.Method):
|
class PerlGotoFunction(method.Method):
|
||||||
'''Jump to a function defined in this module'''
|
'''Jump to a function defined in this module'''
|
||||||
|
@ -557,103 +492,57 @@ class PerlHashCleanup(method.Method):
|
||||||
window.kill(start_p, end_p)
|
window.kill(start_p, end_p)
|
||||||
window.insert_string(start_p, data)
|
window.insert_string(start_p, data)
|
||||||
|
|
||||||
class PerlHashCleanup2(method.Method):
|
#class PerlWrapLine(method.Method):
|
||||||
'''Correctly align assignment blocks and literal hashes'''
|
# '''Wrap lines, comments, POD'''
|
||||||
def process_line2(self, line_regions, sep=None, indent=None):
|
# margin = 80
|
||||||
(pre_toks, sep_tok, post_toks) = ([], None, [])
|
# comment_re = re.compile('^( *)(#+)( *)([^ ].*)$')
|
||||||
ok = False
|
# def _execute(self, w, **vargs):
|
||||||
before = True
|
# pcursor = w.physical_cursor()
|
||||||
for r in line_regions:
|
# r = w.get_region(pcursor)
|
||||||
(start, end, attr, s, name) = r
|
# if r is None:
|
||||||
if name == "":
|
# return
|
||||||
continue
|
#
|
||||||
elif before:
|
# t = r[4]
|
||||||
if len(pre_toks) == 0:
|
# if t == 'pod':
|
||||||
pre_toks.append(r)
|
# assert False, 'POD: %s' % repr(r)
|
||||||
elif (name == "delimiter" and s == sep or
|
# elif t == 'comment':
|
||||||
(sep is None and (s == "=" or s == "=>"))):
|
# self._wrap_comment(w)
|
||||||
sep_tok = r
|
# else:
|
||||||
before = False
|
# return
|
||||||
else:
|
#
|
||||||
pre_toks.append(r)
|
# def _wrap_comment(self, w):
|
||||||
else:
|
# l = w.logical_cursor()
|
||||||
post_toks.append(r)
|
# m = self.comment_re.match(w.buffer.lines[l.y])
|
||||||
ok = True
|
# if not m:
|
||||||
|
# assert False, 'no match oh geez'
|
||||||
if ok:
|
#
|
||||||
return (True, sep_tok[3], (pre_toks, sep_tok, post_toks))
|
# pad = m.group(1) + m.group(2) + m.group(3)
|
||||||
else:
|
# data = m.group(4) + ' '
|
||||||
return (False, "", ([], None, []))
|
#
|
||||||
|
# start = l.y
|
||||||
def _execute(self, w, **vargs):
|
# end = l.y + 1
|
||||||
cursor = w.logical_cursor()
|
#
|
||||||
b = w.buffer
|
# while end < len(w.buffer.lines):
|
||||||
|
# m = self.comment_re.match(w.buffer.lines[end])
|
||||||
# so this is where we will store the groups that we find
|
# if m:
|
||||||
groups_by_line = {}
|
# data += m.group(4) + ' '
|
||||||
|
# end += 1
|
||||||
all_regions = w.mode.get_regions()
|
# else:
|
||||||
line_regions = all_regions[cursor.y]
|
# break
|
||||||
(ok, sep, group) = self.process_line2(line_regions)
|
#
|
||||||
|
# words = [word for word in data.split() if word]
|
||||||
if not ok:
|
#
|
||||||
raise Exception, "Not a perl hash line"
|
# lines = [pad]
|
||||||
groups_by_line[cursor.y] = group
|
# for word in words:
|
||||||
|
# if len(lines[-1]) == len(pad):
|
||||||
# find the beginning of this hash block
|
# lines[-1] += word
|
||||||
start = cursor.y
|
# elif len(lines[-1]) + 1 + len(word) <= self.margin:
|
||||||
while start >= 0:
|
# lines[-1] += ' ' + word
|
||||||
(ok2, sep2, group2) = self.process_line2(all_regions[start - 1], sep)
|
# else:
|
||||||
if not ok2:
|
# lines.append(pad + word)
|
||||||
break
|
#
|
||||||
start -= 1
|
# # remove the old text and add the new
|
||||||
groups_by_line[start] = group2
|
# start_p = Point(0, start)
|
||||||
|
# end_p = Point(len(w.buffer.lines[end-1]), end-1)
|
||||||
# find the end of this hash block
|
# w.kill(start_p, end_p)
|
||||||
end = cursor.y
|
# w.insert(start_p, '\n'.join(lines))
|
||||||
while end < len(b.lines) - 1:
|
|
||||||
(ok2, sep2, group2) = self.process_line2(all_regions[end + 1], sep)
|
|
||||||
if not ok2:
|
|
||||||
break
|
|
||||||
end += 1
|
|
||||||
groups_by_line[end] = group2
|
|
||||||
|
|
||||||
# find the minimum indented line
|
|
||||||
indent_w = None
|
|
||||||
for k in groups_by_line:
|
|
||||||
x = groups_by_line[k][0][0].start
|
|
||||||
if indent_w is None or x < indent_w:
|
|
||||||
indent_w = x
|
|
||||||
|
|
||||||
# find the max key length
|
|
||||||
key_w = None
|
|
||||||
for k in groups_by_line:
|
|
||||||
x = groups_by_line[k][0][-1].end - groups_by_line[k][0][0].start
|
|
||||||
if key_w is None or x > key_w:
|
|
||||||
key_w = x
|
|
||||||
|
|
||||||
# for each line, format it correctly
|
|
||||||
keys = groups_by_line.keys()
|
|
||||||
keys.sort()
|
|
||||||
data = ''
|
|
||||||
for i in keys:
|
|
||||||
line = ' ' * indent_w
|
|
||||||
l = groups_by_line[i][0][0].start
|
|
||||||
for t in groups_by_line[i][0]:
|
|
||||||
line += ' ' * max(0, t.start - l)
|
|
||||||
line += t.value
|
|
||||||
l = t.end
|
|
||||||
line += ' ' * max(0, key_w - l + groups_by_line[i][0][0].start)
|
|
||||||
line += ' ' + groups_by_line[i][1].value + ' '
|
|
||||||
l = groups_by_line[i][2][0].start
|
|
||||||
for t in groups_by_line[i][2]:
|
|
||||||
line += ' ' * max(0, t.start - l)
|
|
||||||
line += t.value
|
|
||||||
l = t.end
|
|
||||||
data += line + '\n'
|
|
||||||
|
|
||||||
# remove the old text and add the new
|
|
||||||
start_p = Point(0, start)
|
|
||||||
end_p = Point(0, end + 1)
|
|
||||||
w.kill(start_p, end_p)
|
|
||||||
w.insert(start_p, data)
|
|
||||||
|
|
138
mode_python.py
138
mode_python.py
|
@ -120,8 +120,142 @@ class PythonGrammar(Grammar):
|
||||||
),
|
),
|
||||||
]
|
]
|
||||||
|
|
||||||
class PythonTabber(tab2.StackTabber):
|
class PythonTabber(tab2.Tabber):
|
||||||
pass
|
start_tags = {'(': ')',
|
||||||
|
'{': '}',
|
||||||
|
'[': ']'}
|
||||||
|
|
||||||
|
close_tags = {')': '(',
|
||||||
|
'}': '{',
|
||||||
|
']': '['}
|
||||||
|
|
||||||
|
def __init__(self, m):
|
||||||
|
tab2.Tabber.__init__(self, m)
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
def stack_append(self, item):
|
||||||
|
self.tab_stack.append(item)
|
||||||
|
def stack_pop(self):
|
||||||
|
self.tab_stack.pop(-1)
|
||||||
|
|
||||||
|
def base_indentation_level(self, y):
|
||||||
|
return y == 0
|
||||||
|
|
||||||
|
def calculate_tabs(self, start=0, goal=None):
|
||||||
|
lines = self.mode.window.buffer.lines
|
||||||
|
tokens = self.mode.highlighter.tokens
|
||||||
|
buffer = self.mode.window.buffer
|
||||||
|
|
||||||
|
if self.levels is None:
|
||||||
|
self.levels = [None] * (len(lines))
|
||||||
|
|
||||||
|
self.index = 0
|
||||||
|
self.y = start
|
||||||
|
self.base = 0
|
||||||
|
self.tab_stack = []
|
||||||
|
|
||||||
|
# we want to process every logical line in the file
|
||||||
|
while self.y < len(lines):
|
||||||
|
line = lines[self.y]
|
||||||
|
start_index = self.index
|
||||||
|
|
||||||
|
start_point = point.Point(0, self.y)
|
||||||
|
start_offset = buffer.get_point_offset(start_point)
|
||||||
|
end_point = point.Point(len(line), self.y)
|
||||||
|
end_offset = buffer.get_point_offset(end_point)
|
||||||
|
|
||||||
|
# we want to find all the tokens on the line we are currently processing
|
||||||
|
while self.index < len(tokens):
|
||||||
|
token = tokens[self.index]
|
||||||
|
if token.end > end_offset:
|
||||||
|
break
|
||||||
|
self.index += 1
|
||||||
|
|
||||||
|
self.handle_line(line,
|
||||||
|
start_offset, start_index,
|
||||||
|
end_offset, self.index)
|
||||||
|
|
||||||
|
self.levels[self.y] = self.line_depth
|
||||||
|
self.y += 1
|
||||||
|
if goal is not None and self.y > goal:
|
||||||
|
break
|
||||||
|
|
||||||
|
def get_line_depth(self):
|
||||||
|
if len(self.tab_stack) > 0:
|
||||||
|
return self.tab_stack[-1][1]
|
||||||
|
else:
|
||||||
|
return self.base
|
||||||
|
|
||||||
|
def handle_line(self, line, start_offset, start_index, end_offset, end_index):
|
||||||
|
self.line_depth = self.get_line_depth()
|
||||||
|
tokens = self.mode.highlighter.tokens
|
||||||
|
|
||||||
|
if start_index >= len(tokens):
|
||||||
|
return
|
||||||
|
if regex.whitespace.match(line):
|
||||||
|
return
|
||||||
|
|
||||||
|
if len(self.tab_stack) == 0 and tokens[start_index].start >= start_offset:
|
||||||
|
self.base = util.count_leading_whitespace(line)
|
||||||
|
|
||||||
|
for i in range(start_index, end_index):
|
||||||
|
token = tokens[i]
|
||||||
|
s = token.string
|
||||||
|
if s in self.start_tags:
|
||||||
|
if i < end_index - 1:
|
||||||
|
i = tokens[i+1].start - start_offset
|
||||||
|
elif len(self.tab_stack) > 0:
|
||||||
|
i = self.tab_stack[-1][1] + 4
|
||||||
|
else:
|
||||||
|
i = self.base + 4
|
||||||
|
self.stack_append((s, i))
|
||||||
|
elif s in self.close_tags:
|
||||||
|
assert len(self.tab_stack), "Unbalanced closing tag"
|
||||||
|
assert self.tab_stack[-1][0] == self.close_tags[s], "Unmatched closing tag"
|
||||||
|
self.stack_pop()
|
||||||
|
if i == start_index:
|
||||||
|
self.line_depth = self.get_line_depth()
|
||||||
|
|
||||||
|
if tokens[start_index].start < start_offset:
|
||||||
|
self.line_depth = -1
|
||||||
|
|
||||||
|
prebase = self.base
|
||||||
|
s = tokens[start_index].string
|
||||||
|
e = tokens[end_index-1].string
|
||||||
|
|
||||||
|
if s == "except" or s == "elif" or s == "else":
|
||||||
|
if self.y > 0 and self.line_depth == self.levels[self.y - 1]:
|
||||||
|
self.line_depth = max(0, self.line_depth - 4)
|
||||||
|
elif (s == "return" or s == "raise" or s == "yield" or s == "break" or
|
||||||
|
s == "pass" or s == 'continue'):
|
||||||
|
self.base = max(0, self.base - 4)
|
||||||
|
|
||||||
|
if e == "\\":
|
||||||
|
if len(self.tab_stack) and self.tab_stack[-1][0] == "\\":
|
||||||
|
pass
|
||||||
|
else:
|
||||||
|
self.stack_append(("\\", prebase + 4))
|
||||||
|
return
|
||||||
|
elif e == ":":
|
||||||
|
self.base += 4
|
||||||
|
elif len(self.tab_stack) and self.tab_stack[-1][0] == "\\":
|
||||||
|
self.stack_pop()
|
||||||
|
|
||||||
|
def get_indentation_level(self, y):
|
||||||
|
if self.levels is not None and self.levels[y] is not None:
|
||||||
|
result = self.levels[y]
|
||||||
|
else:
|
||||||
|
i = max(0, y - 1)
|
||||||
|
while i > 0:
|
||||||
|
if self.base_indentation_level(i):
|
||||||
|
break
|
||||||
|
i -= 1
|
||||||
|
self.calculate_tabs(i, y)
|
||||||
|
result = self.levels[y]
|
||||||
|
if result == -1:
|
||||||
|
return None
|
||||||
|
return result
|
||||||
|
|
||||||
class Python(mode2.Fundamental):
|
class Python(mode2.Fundamental):
|
||||||
tabbercls = PythonTabber
|
tabbercls = PythonTabber
|
||||||
|
|
36
window2.py
36
window2.py
|
@ -2,7 +2,8 @@ import os.path, string
|
||||||
import regex
|
import regex
|
||||||
from point2 import Point
|
from point2 import Point
|
||||||
|
|
||||||
WORD_LETTERS = list(string.letters + string.digits)
|
#WORD_LETTERS = list(string.letters + string.digits)
|
||||||
|
WORD_LETTERS = string.letters + string.digits + "_"
|
||||||
|
|
||||||
# note about the cursor: the cursor position will insert in front of the
|
# note about the cursor: the cursor position will insert in front of the
|
||||||
# character it highlights. to this end, it needs to be able to highlight behind
|
# character it highlights. to this end, it needs to be able to highlight behind
|
||||||
|
@ -294,26 +295,27 @@ class Window(object):
|
||||||
p = self.find_right_word()
|
p = self.find_right_word()
|
||||||
if p is not None:
|
if p is not None:
|
||||||
self.goto(p)
|
self.goto(p)
|
||||||
def get_word_bounds_at_point(self, p):
|
def get_word_bounds_at_point(self, p, wl=WORD_LETTERS):
|
||||||
if len(self.buffer.lines[p.y]) == 0:
|
if len(self.buffer.lines[p.y]) == 0:
|
||||||
return
|
return None
|
||||||
elif p.x == 0:
|
elif self.cursor_char() not in wl:
|
||||||
p1 = self.find_left_word(Point(p.x + 1, p.y))
|
return None
|
||||||
p2 = self.find_right_word()
|
x1 = x2 = p.x
|
||||||
else:
|
while x1 > 0 and self.xy_char(x1 - 1, p.y) in wl:
|
||||||
p1 = self.find_left_word()
|
x1 -= 1
|
||||||
p2 = self.find_right_word(Point(p.x - 1, p.y))
|
while x2 < len(self.buffer.lines[p.y]) and self.xy_char(x2, p.y) in wl:
|
||||||
return (p1, p2)
|
x2 += 1
|
||||||
def get_word_at_point(self, p):
|
return (Point(x1, p.y), Point(x2, p.y))
|
||||||
bounds = self.get_word_bounds_at_point(p)
|
def get_word_at_point(self, p, wl=WORD_LETTERS):
|
||||||
|
bounds = self.get_word_bounds_at_point(p, wl)
|
||||||
if bounds is None:
|
if bounds is None:
|
||||||
return None
|
return None
|
||||||
else:
|
else:
|
||||||
return self.buffer.get_substring(bounds[0], bounds[1])
|
return self.buffer.get_substring(bounds[0], bounds[1])
|
||||||
def get_word_bounds(self):
|
def get_word_bounds(self, wl=WORD_LETTERS):
|
||||||
return self.get_word_bounds_at_point(self.logical_cursor())
|
return self.get_word_bounds_at_point(self.logical_cursor(), wl)
|
||||||
def get_word(self):
|
def get_word(self, wl=WORD_LETTERS):
|
||||||
return self.get_word_at_point(self.logical_cursor())
|
return self.get_word_at_point(self.logical_cursor(), wl)
|
||||||
|
|
||||||
# page up/down
|
# page up/down
|
||||||
def _pshift_up(self, p, num):
|
def _pshift_up(self, p, num):
|
||||||
|
@ -496,7 +498,7 @@ class Window(object):
|
||||||
|
|
||||||
# querying
|
# querying
|
||||||
def cursor_char(self):
|
def cursor_char(self):
|
||||||
self.point_char(self.logical_cursor())
|
return self.point_char(self.logical_cursor())
|
||||||
def point_char(self, p):
|
def point_char(self, p):
|
||||||
return self.xy_char(p.x, p.y)
|
return self.xy_char(p.x, p.y)
|
||||||
def xy_char(self, x, y):
|
def xy_char(self, x, y):
|
||||||
|
|
Loading…
Reference in New Issue