pmacs3/mode/perl.py

859 lines
35 KiB
Python
Raw Normal View History

2009-04-09 00:34:43 -04:00
import os
import re
import string
from subprocess import Popen, PIPE, STDOUT
2009-04-09 00:34:43 -04:00
import completer
import context
from mode import Fundamental
import regex
from buffer import IperlBuffer
from point import Point
from lex import Grammar, PatternRule, ContextPatternRule, RegionRule
from lex import OverridePatternRule, PatternMatchRule
2009-04-09 00:34:43 -04:00
from method import Argument, Method, WrapParagraph, arg
from method.introspect import TokenComplete
2008-10-02 23:57:32 -04:00
from tab import StackTabber, StackTabber2
2009-02-04 08:48:58 -05:00
from parse import Any, And, Or, Optional, Name, Match, Matchs
2009-03-16 14:35:13 -04:00
import term
2009-02-04 08:48:58 -05:00
class PodDataGrammar(Grammar):
rules = [PatternRule(r'data', r'[^\n]+\n$')]
2007-07-21 11:40:53 -04:00
class PodGrammar(Grammar):
rules = [
RegionRule('entry', '(?<=^=head[1-4]) +.*$', PodDataGrammar, r'^\n$'),
RegionRule('entry', '(?<=^=over) +.*$', PodDataGrammar, r'^\n$'),
RegionRule('entry', '(?<=^=item) +.*$', PodDataGrammar, r'^\n$'),
RegionRule('entry', '(?:(?<=^=begin)|(?<=^=end)) +.*$',
PodDataGrammar, r'^\n$'),
RegionRule('entry', '(?<=^=encoding) +.*$', PodDataGrammar, r'^\n$'),
2007-07-21 11:40:53 -04:00
]
strg1 = r"'(?:\\.|[^'\\])*'"
strg2 = r'"(?:\\.|[^"\\])*"'
wchr1 = '[a-zA-Z_]'
wchr2 = '[a-zA-Z0-9_]'
2009-04-27 16:34:42 -04:00
wchr3 = '[-a-zA-Z0-9_]'
hword = wchr3 + '+'
word1 = wchr1 + wchr2 + '*'
word2 = '(?:' + word1 + '::)*' + word1
pname = '[.a-zA-Z0-9_]+'
2008-09-30 01:47:20 -04:00
def _make_string_rules(forbidden):
2007-07-21 11:40:53 -04:00
rules = [
PatternRule('octal', r'\\[0-7]{3}'),
PatternRule('escaped', r'\\.'),
PatternRule('deref', r"\$+" + word2 + "(?:" + "(?:->)?{\$?(?:" +
hword + "|" + strg1 + "|" + strg2 + ")}|" +
"(?:->)?\[\$?"+hword+"\]"+ ")+"),
PatternRule('length', r"\$#" + word2),
PatternRule('scalar', r"\$\$*" + word2),
PatternRule('scalar', r"\$[0-9]+"),
PatternRule('cast', r"[\$\@\%\&]{.+?}"),
PatternRule('array', r"@\$*" + word2),
2007-07-21 11:40:53 -04:00
]
return rules
2009-03-26 17:32:52 -04:00
class QuotedWords(Grammar): rules = [
PatternRule('data', hword),
PatternRule('eol', r'\n'),
PatternRule('spaces', ' +'),
2009-03-26 17:32:52 -04:00
]
class NoParen(Grammar): rules = [PatternRule('data', '[^)]+')]
class NoBrace(Grammar): rules = [PatternRule('data', r'[^\}]+')]
class NoBracket(Grammar): rules = [PatternRule('data', r'[^\]]+')]
class NoAngle(Grammar): rules = [PatternRule('data', '[^>]+')]
class NoHash(Grammar): rules = [PatternRule('data', '[^#]+')]
class DataGrammar(Grammar): rules = [PatternRule('data', '.+')]
class StrictStringGrammar(Grammar): rules = [PatternRule('escaped', r"\\'")]
class StringGrammar(Grammar): rules = _make_string_rules('"')
class EvalGrammar(Grammar): rules = _make_string_rules('`')
class TranslateGrammar1(Grammar):
rules = [PatternRule('data', r"(?:\\.|[^\\/])")]
class TranslateGrammar2(Grammar):
rules = [PatternRule('data', r"(?:\\.|[^\\#])")]
class TranslateGrammarX(Grammar):
rules = [PatternRule('data', r"(?:\\.|[^\\%(delim)s])")]
class MatchGrammar1(Grammar): rules = _make_string_rules('/')
class MatchGrammar2(Grammar): rules = _make_string_rules('#')
class MatchGrammar3(Grammar): rules = _make_string_rules(')')
class MatchGrammar4(Grammar): rules = _make_string_rules(']')
class MatchGrammar5(Grammar): rules = _make_string_rules('}')
class MatchGrammar6(Grammar): rules = _make_string_rules('>')
class QuotedGrammar1(Grammar): rules = _make_string_rules(')')
class QuotedGrammar2(Grammar): rules = _make_string_rules('}')
class QuotedGrammar3(Grammar): rules = _make_string_rules('>')
class QuotedGrammar4(Grammar): rules = _make_string_rules(']')
2007-07-21 11:40:53 -04:00
class PerlGrammar(Grammar):
rules = [
RegionRule('perl.heredoc', r"<<(?P<heredoc>" + word1 + ")", None,
r';\n', StringGrammar, r'^%(heredoc)s$'),
RegionRule('perl.heredoc', r'<< *"(?P<heredoc>[^"]+)"', None,
r';\n', StringGrammar, r'^%(heredoc)s$'),
RegionRule('perl.heredoc', r"<< *'(?P<heredoc>[^']+)'", None,
r";\n", DataGrammar, r'^%(heredoc)s$'),
RegionRule('perl.evaldoc', r"<< *`(?P<heredoc>[^`]+)`", None,
r";\n", StringGrammar, r'^%(heredoc)s$'),
RegionRule('perl.endblock', "^__END__|__DATA__ *$", DataGrammar, ''),
RegionRule('perl.pod', '^=' + word1, PodGrammar, '^=cut'),
OverridePatternRule('perl.comment', '#@@:(?P<token>' + pname +
'):(?P<mode>' + pname + ') *$'),
PatternMatchRule('x', '(sub)( +)(' + word2 +
r')( *)(\()( *)([\[\]\\@$%&*;]+)( *)(\))',
'perl.keyword', 'spaces', 'perl.sub', 'spaces',
'delimiter', 'spaces', 'perl.prototype',
'spaces', 'delimiter'),
PatternRule('perl.comment', '#.*$'),
RegionRule('perl.string', '"', StringGrammar, '"'),
RegionRule('perl.string', "'", StrictStringGrammar, "'"),
RegionRule('perl.evalstring', "`", EvalGrammar, "`"),
PatternRule('perl.number', r'0?\.[0-9]+|[0-9]+(?:\.[0-9]+)?'),
PatternRule('perl.keyword', "(?<!->)(?:STDIN|STDERR|STDOUT|continue|do|else|elsif|eval|foreach|for|if|last|my|next|our|package|require|return|sub|undef|unless|until|use|while)(?![a-zA-Z0-9_])"),
PatternRule('perl.hash_key', '(?<={)' + wchr2 + '+(?=})'),
PatternRule('perl.method', '(?<=->)' + word1),
PatternRule('perl.hash_key', wchr2 + '+(?= *=>)'),
PatternRule('perl.length', r"\$#" + word2),
PatternRule('perl.cast', r'[\$\@\%\&\*](?= *{)'),
PatternRule('perl.scalar', r"\$[\[\]<>ab/'\"_@\?#\$!%^|&*()](?!" +
wchr2 + ")"),
PatternRule('perl.array', "@_"),
PatternRule('perl.function', r"\$\$*" + word2 + "(?=-> *\()"),
PatternRule('perl.scalar', r"\$[0-9]+"),
PatternRule('perl.scalar', r"\$\$*" + word2),
PatternRule('perl.array', r"@\$*" + word2),
PatternRule('perl.hash', r"%\$*" + word2),
2007-07-21 11:40:53 -04:00
# match regexes; paired delimiters
RegionRule('perl.match', r'm *(?P<delim>\()',
MatchGrammar3, r'\)[a-z]*'),
RegionRule('perl.match', r'm *(?P<delim>\[)',
MatchGrammar4, r'\][a-z]*'),
RegionRule('perl.match', r'm *(?P<delim>\{)',
MatchGrammar5, r'\}[a-z]*'),
RegionRule('perl.match', r'm *(?P<delim>\<)',
MatchGrammar6, r'\>[a-z]*'),
2007-07-21 11:40:53 -04:00
# match regexes
RegionRule('perl.match', r'(?:(?<==~)|(?<=!~)|(?<=\()|(?<=split)|(?<=if)|(?<=unless)|(?<=while)|(?<=until)) *(?P<delim>/)', MatchGrammar1, '/[a-z]*'),
RegionRule('perl.match', 'm *(?P<delim>/)', MatchGrammar1, '/[a-z]*'),
RegionRule('perl.match', 'm *(?P<delim>[^ #a-zA-Z0-9_])',
StringGrammar, '%(delim)s[a-z]*'),
RegionRule('perl.match', 'm(?P<delim>#)', MatchGrammar2, '#[a-z]*'),
2007-07-21 11:40:53 -04:00
# match regexes; paired delimiters
RegionRule('perl.replace', r's *(?P<delim>\()', MatchGrammar3,
r'\) *\(', MatchGrammar3, r'\)[a-z]*'),
RegionRule('perl.replace', r's *(?P<delim>\[)', MatchGrammar4,
r'\] *\[', MatchGrammar4, r'\][a-z]*'),
RegionRule('perl.replace', r's *(?P<delim>\{)', MatchGrammar5,
r'\} *\{', MatchGrammar5, r'\}[a-z]*'),
RegionRule('perl.replace', r's *(?P<delim>\<)', MatchGrammar6,
r'\> *\<', MatchGrammar6, r'\>[a-z]*'),
2007-07-21 11:40:53 -04:00
# replace regexes
RegionRule('perl.replace', 's *(?P<delim>/)', MatchGrammar1,
'/', MatchGrammar1, '/[a-z]*'),
RegionRule('perl.replace', 's *(?P<delim>[^ a-zA-Z0-9_])',
StringGrammar, '%(delim)s',
StringGrammar, '%(delim)s[a-z]*'),
RegionRule('perl.replace', 's(?P<delim>#)',
MatchGrammar2, '#', MatchGrammar2, '#[a-z]*'),
2007-07-21 11:40:53 -04:00
# translate operator
RegionRule('perl.translate', '(?:y|tr) *(?P<delim>/)',
TranslateGrammar1, '/', TranslateGrammar1, '/[a-z]*'),
RegionRule('perl.translate', '(?:y|tr)#', TranslateGrammar2,
'#', TranslateGrammar2, '#[a-z]*'),
RegionRule('perl.translate', '(?:y|tr) *(?P<delim>[^ a-zA-Z0-9_])',
TranslateGrammarX, '%(delim)s', TranslateGrammarX,
'%(delim)s[a-z]*'),
2007-07-21 11:40:53 -04:00
# some more basic stuff
PatternRule('perl.package', "(?<=package )" + word2),
PatternRule('perl.sub', "(?<=sub )" + word2),
PatternRule('perl.use', "(?<=use )" + word2),
PatternRule('perl.require', "(?<=require )" + word2),
PatternRule('perl.label', word1 + ':(?!:)'),
PatternRule('perl.function', r"&\$*" + word2),
PatternRule('perl.builtin', "(?<!->)&?(?: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_])"),
2007-07-21 11:40:53 -04:00
# quote operator: qq(), qx() and qr() usually interpolate
RegionRule('perl.quoted', r'q[rqx] *(?P<delim>\()', QuotedGrammar1, r'\)'),
RegionRule('perl.quoted', 'q[rqx] *(?P<delim>{)', QuotedGrammar2, '}'),
RegionRule('perl.quoted', 'q[rqx] *(?P<delim><)', QuotedGrammar3, '>'),
RegionRule('perl.quoted', r'q[rqx] *(?P<delim>\[)', QuotedGrammar4, r'\]'),
RegionRule('perl.quoted', "q[rqx] *(?P<delim>')", Grammar, "'"),
RegionRule('perl.quoted', 'q[rqx] *(?P<delim>[^ a-zA-Z0-9#])',
StringGrammar, '%(delim)s'),
RegionRule('perl.quoted', 'q[rqx](?P<delim>#)', StringGrammar, '#'),
# quote operator: q() and qw() do not interpolate
RegionRule('perl.quoted', r'qw? *\(', QuotedWords, r'\)'),
RegionRule('perl.quoted', 'qw? *{', NoBrace, '}'),
RegionRule('perl.quoted', 'qw? *<', NoAngle, '>'),
RegionRule('perl.quoted', r'qw? *\[', NoBracket, r'\]'),
RegionRule('perl.quoted', 'qw?#', NoHash, '#'),
RegionRule('perl.quoted', 'qw? *(?P<delim>[^ a-zA-Z0-9#])',
Grammar, '%(delim)s'),
2007-07-21 11:40:53 -04:00
PatternRule('perl.function', word2 + r"(?= *\()"),
PatternRule('perl.class', word2 + "(?=->)"),
2007-07-21 11:40:53 -04:00
# some basic stuff
PatternRule('delimiter', r"::|->|=>|(?<!:):(?!=:)|[,;=\?(){}\[\]\(\)]"),
PatternRule('perl.noperator', "-[rwxoRWXOezsfdlpSbctugkTBMAC](?!" +
wchr2 + ")"),
PatternRule('perl.operator', r"\+=|-=|\*=|/=|//=|%=|&=\|\^=|>>=|<<=|\*\*=|\\"),
PatternRule('perl.operator', r"\+\+|\+|<=>|<>|<<|<=|<|-|>>|>=|>|\*\*|\*|&&|&|\|\||\||/|\^|==|//|~|=~|!~|!=|%|!|\.|x(?![a-zA-Z_])"),
PatternRule('perl.noperator', "(?:xor|or|not|ne|lt|le|gt|ge|eq|cmp|and)(?![a-zA-Z_])"),
PatternRule('perl.bareword', word2),
PatternRule('spaces', ' +'),
PatternRule('eol', r"\n$"),
2007-07-21 11:40:53 -04:00
]
2008-10-02 23:57:32 -04:00
class PerlTabber2(StackTabber2):
is_ignored_tokens = ('spaces', 'eol', 'perl.comment')
open_tokens = {'delimiter': {'{': '}', '(': ')', '[': ']'}}
close_tokens = {'delimiter': {'}': '{', ')': '(', ']': '['}}
end_at_eof = False
end_at_tokens = {'delimiter': {';': 1}}
nocontinue_tokens = {'delimiter': {';': 1, ',': 1, '}': 1},
2009-04-09 00:34:43 -04:00
'perl.heredoc.end': 1,
'perl.evaldoc.end': 1, 'perl.pod.end': 1}
start_free_tokens = {'perl.string.start': 1, 'perl.pod.start': 1,
'perl.heredoc.start': 1, 'perl.evaldoc.start': 1}
end_free_tokens = {'perl.string.end': 1, 'perl.pod.end': 1,
'perl.heredoc.end': 1, 'perl.evaldoc.start': 1}
2008-10-02 23:57:32 -04:00
2007-07-21 11:40:53 -04:00
class PerlSetLib(Method):
'''Set the path(s) to find perl modules'''
2009-04-09 00:34:43 -04:00
args = [arg("lib", dt='path', p="Lib: ", dv=lambda w: '.')]
2007-07-21 11:40:53 -04:00
def _execute(self, w, **vargs):
2009-03-16 10:42:17 -04:00
w.application.config['perl.libs'] = vargs['lib'].split(':')
class PerlAddLib(PerlSetLib):
2008-12-02 15:21:28 -05:00
'''Set the path(s) to find perl modules'''
def _execute(self, w, **vargs):
w.application.config['perl.libs'].append(vargs['lib'])
2009-03-16 10:42:17 -04:00
class PerlBase(Method):
bname = '*Perl*'
def get_args(self, w, **vargs):
return ['perl', '-e', 'print "hello world\n"']
def run_pipe(self, w, args, switch, mname=None):
return w.application.run_pipe(args, w.buffer, self.bname, switch, mname)
class PerlCheckSyntax(PerlBase):
2007-07-21 11:40:53 -04:00
'''Check the syntax of a perl file'''
2009-03-16 10:42:17 -04:00
bname = '*Perl-Syntax*'
def get_args(self, w, **vargs):
args = ['perl']
for l in w.application.config.get('perl.libs', []):
args.extend(('-I', l))
return args + ['-c', '-']
2007-07-21 11:40:53 -04:00
def _execute(self, w, **vargs):
2009-03-16 10:42:17 -04:00
args = self.get_args(w, **vargs)
r = self.run_pipe(w, args, lambda x: x != 0, 'error')
b = w.application.get_buffer_by_name(self.bname)
2008-12-03 19:30:01 -05:00
b.orig_path = w.buffer.path
2009-03-16 10:42:17 -04:00
if r == 0: w.set_error("Syntax OK")
2009-03-14 19:53:44 -04:00
2009-03-16 10:42:17 -04:00
class PerldocModule(PerlBase):
'''View documentation about this buffer using perldoc'''
2009-03-16 10:42:17 -04:00
bname = '*Perldoc*'
prog = 'use Pod::Text; Pod::Text->new()->parse_from_filehandle();';
2009-03-16 14:35:13 -04:00
#prog = 'use Pod::Text::Termcap; Pod::Text::Termcap->new()->parse_from_filehandle();';
2009-03-16 10:42:17 -04:00
def get_args(self, w, **vargs):
return ('perl', '-e', self.prog)
2007-07-21 11:40:53 -04:00
def _execute(self, w, **vargs):
2009-03-16 10:42:17 -04:00
self.run_pipe(w, self.get_args(w, **vargs), True)
2007-07-21 11:40:53 -04:00
2009-03-14 19:53:44 -04:00
class Perldoc(Method):
2009-03-08 00:19:58 -05:00
name_re = re.compile('(?:[a-zA-Z_][a-zA-Z0-9_]*::)*[a-zA-Z_][a-zA-Z0-9_]*')
2009-04-09 00:34:43 -04:00
args = [arg("name", p="Perldoc: ")]
2009-03-08 00:19:58 -05:00
def _execute(self, w, **vargs):
name = vargs['name']
if not self.name_re.match(name):
w.set_error("name %r is invalid" % name)
return
# try it as a module first
parts = name.split('::')
while len(parts) > 0:
newname = '::'.join(parts)
data = self._try(w, newname, asfunc=False)
if data:
self._show(w, data, newname)
return
parts.pop(-1)
# then try it as a function
data = self._try(w, name, asfunc=True)
if data:
self._show(w, data, name)
else:
2009-03-16 10:42:17 -04:00
w.set_error('nothing found for %r' % name)
2009-03-08 00:19:58 -05:00
def _try(self, w, name, asfunc=False):
2007-07-21 11:40:53 -04:00
if asfunc:
2009-03-16 14:35:13 -04:00
cmd = "perldoc -f '%s'" % name
2007-07-21 11:40:53 -04:00
else:
2009-03-16 14:35:13 -04:00
cmd = "perldoc '%s'" % name
2009-03-16 10:42:17 -04:00
l = w.application.config.get('perl.libs', [])
if l:
cmd = 'PERL5LIB=%r %s' % (':'.join(['%r' % x for x in l]), cmd)
2008-12-02 15:21:28 -05:00
2009-03-16 14:35:13 -04:00
p = Popen(cmd, shell=True, stdout=PIPE, stderr=PIPE)
output = p.stdout.read()
result = p.wait()
status = os.WEXITSTATUS(result)
2007-07-21 11:40:53 -04:00
if status == 0:
2009-03-16 14:35:13 -04:00
xterm = term.XTerm(cbuf=True)
output = xterm.term_filter(output)
return output
2007-07-21 11:40:53 -04:00
else:
return None
2009-03-08 00:19:58 -05:00
def _show(self, w, data, name):
2009-03-16 14:35:13 -04:00
w.application.color_data_buffer("*Perldoc*", data, switch_to=True)
2009-03-16 10:42:17 -04:00
w.set_error('displaying perldoc for %r' % name)
2009-03-08 00:19:58 -05:00
2009-03-24 13:01:54 -04:00
class PerldocF(Perldoc):
def _execute(self, w, **vargs):
name = vargs['name']
if not self.name_re.match(name):
w.set_error("name %r is invalid" % name)
return
# then try it as a function
data = self._try(w, name, asfunc=True)
if data:
self._show(w, data, name)
else:
w.set_error('nothing found for %r' % name)
2009-03-14 19:53:44 -04:00
class PerldocWord(Perldoc):
2009-03-08 00:19:58 -05:00
'''View documentation about a package or function using perldoc'''
args = []
2007-07-21 11:40:53 -04:00
def _execute(self, w, **vargs):
2009-03-16 10:42:17 -04:00
word = w.get_token().string
2007-07-21 11:40:53 -04:00
if word is None:
2009-03-16 10:42:17 -04:00
w.set_error('no word selected')
2007-07-21 11:40:53 -04:00
return
2009-03-14 21:35:43 -04:00
return Perldoc._execute(self, w, name=word)
2007-07-21 11:40:53 -04:00
class PerlInitFunctions(Method):
'''Jump to a function defined in this module'''
def _execute(self, w, **vargs):
2008-05-12 18:12:56 -04:00
w.mode.context.build_name_map()
2009-03-16 10:42:17 -04:00
w.set_error("Initialized function map")
2007-07-21 11:40:53 -04:00
class PerlGotoFunction(Method):
'''Jump to a function defined in this module'''
args = [Argument("name", type(""), "perlfunction", "Goto Function: ")]
def _execute(self, w, **vargs):
name = vargs['name']
functions = w.mode.context.get_names()
2007-07-21 11:40:53 -04:00
if name in functions:
w.goto(Point(0, functions[name]))
else:
2009-03-16 10:42:17 -04:00
w.set_error("Function %r was not found" % name)
2007-07-21 11:40:53 -04:00
class PerlListFunctions(Method):
'''Show the user all functions defined in this module'''
def _execute(self, w, **vargs):
names = w.mode.context.get_name_list()
2007-07-21 11:40:53 -04:00
output = "\n".join(names) + "\n"
w.application.data_buffer("*Perl-List-Functions*", output, switch_to=True)
class PerlWhichFunction(Method):
'''Show the user what function they are in'''
def _execute(self, w, **vargs):
cursor = w.logical_cursor()
name = w.mode.context.get_line_name(cursor.y)
2007-07-21 11:40:53 -04:00
if name is None:
2009-03-16 10:42:17 -04:00
w.set_error("None");
2007-07-21 11:40:53 -04:00
else:
functions = w.mode.context.get_names()
2008-05-08 02:57:23 -04:00
i = functions[name] + 1
2009-03-16 10:42:17 -04:00
w.set_error("line %d: %s" % (i, name))
2007-07-21 11:40:53 -04:00
class PerlHashCleanup(Method):
'''Correctly align assignment blocks and literal hashes'''
2007-08-21 09:23:45 -04:00
def _execute(self, w, **vargs):
cursor = w.logical_cursor()
b = w.buffer
2007-07-21 11:40:53 -04:00
# so this is where we will store the groups that we find
groups_by_line = {}
# the regex we will try
regexes = [regex.perl_hash_cleanup,
regex.perl_assign_cleanup]
# if we aren't in a hash, inform the user and exit
line = b.lines[cursor.y]
myregex = None
for r in regexes:
if r.match(line):
myregex = r
if myregex is None:
raise Exception, "Not a perl hash line"
groups_by_line[cursor.y] = myregex.match(line).groups()
# find the beginning of this hash block
start = 0
i = cursor.y - 1
while i >= 0:
line = b.lines[i]
m = myregex.match(line)
if not m:
start = i + 1
break
else:
groups_by_line[i] = m.groups()
i -= 1
# find the end of this hash block
end = len(b.lines) - 1
i = cursor.y + 1
while i < len(b.lines):
line = b.lines[i]
m = myregex.match(line)
if not m:
end = i - 1
break
else:
groups_by_line[i] = m.groups()
i += 1
# assume that the least indented line is correct
indent_w = min([len(groups_by_line[k][0]) for k in groups_by_line])
# find the longest hash key to base all the other padding on
key_w = max([len(groups_by_line[k][1]) for k in groups_by_line])
# for each line, format it correctly
keys = groups_by_line.keys()
keys.sort()
data = ''
for i in keys:
indent_pad = ' ' * indent_w
key = groups_by_line[i][1]
sep = groups_by_line[i][3]
value = groups_by_line[i][5]
key_pad = ' ' * (key_w - len(key))
data += indent_pad + key + key_pad + ' ' + sep + ' ' + value + '\n'
# remove the old text and add the new
start_p = Point(0, start)
2007-08-21 09:23:45 -04:00
if end < len(w.buffer.lines) - 1:
end_p = Point(0, end + 1)
else:
end_p = Point(len(w.buffer.lines[end]), end)
w.delete(start_p, end_p)
2007-08-21 09:23:45 -04:00
w.insert_string(start_p, data)
2007-07-21 11:40:53 -04:00
2009-04-09 00:34:43 -04:00
class PerlWrapParagraph(WrapParagraph):
2007-07-21 11:40:53 -04:00
'''Wrap Comments and POD'''
2007-08-22 12:32:32 -04:00
# enumerations for line types
LT_COMMENT = 1
LT_POD = 2
margin = 80
comment_re = re.compile('( *)(#+)( *)(.*)')
2007-07-21 11:40:53 -04:00
def _is_newline(self, t):
return t.name == 'eol'
def _is_space(self, t):
2008-09-30 01:47:20 -04:00
return t.name == 'spaces'
2007-07-21 11:40:53 -04:00
def _detect_line_type(self, w, y):
h = w.buffer.highlights[w.mode.name]
2007-07-21 11:40:53 -04:00
ltype = None
2007-08-22 12:32:32 -04:00
for t in h.tokens[y]:
fqname = t.fqname()
2008-09-30 01:47:20 -04:00
if fqname == 'spaces' or fqname == 'eol':
2007-07-21 11:40:53 -04:00
pass
2007-08-22 12:32:32 -04:00
elif fqname.startswith('comment'):
if ltype and ltype != 'comment':
ltype = None
break
ltype = self.LT_COMMENT
elif fqname.startswith('pod'):
if ltype and ltype != 'pod':
ltype = None
break
ltype = self.LT_POD
else:
ltype = None
break
return ltype
def _fix_comments(self, c, w):
h = w.buffer.highlights[w.mode.name]
2007-08-22 12:32:32 -04:00
y1 = c.y
y2 = c.y
while y2 < len(w.buffer.lines) - 1:
if self._detect_line_type(w, y2 + 1):
y2 += 1
else:
break
lines = w.buffer.lines[y1:y2 + 1]
m = self.comment_re.match(lines[0])
assert m
prepend = m.group(1) + m.group(2)
rmargin = self.margin - len(prepend)
dpad = m.group(3)
segments = []
for line in lines:
m = self.comment_re.match(line)
assert m
pad, data = m.group(3), m.group(4)
if segments and pad == dpad and segments[-1][0] == dpad and segments[-1][1]:
data = segments.pop(-1)[1] + ' ' + data
i = 0
while len(pad) + len(data[i:]) > rmargin:
while data[i] == ' ':
i += 1
j = rmargin - len(pad)
while j >= 0 and data[i + j] != ' ':
j -= 1
if j < 0:
j = rmargin - len(pad)
segments.append([pad, data[i:i + j]])
i += j
if data:
while data[i] == ' ':
i += 1
segments.append([pad, data[i:]])
2007-07-21 11:40:53 -04:00
else:
2007-08-22 12:32:32 -04:00
segments.append(['', ''])
lines2 = [prepend + x[0] + x[1] for x in segments]
p1 = Point(0, y1)
p2 = Point(len(w.buffer.lines[y2]), y2)
w.buffer.delete(p1, p2)
w.buffer.insert_lines(p1, lines2)
w.set_error("wrapped comment lines %d-%d" % (y1 + 1, y2 + 1))
def _fix_pod(self, c, w):
w.set_error("pod wrapping not yet supported")
2007-07-21 11:40:53 -04:00
def _execute(self, w, **vargs):
c = w.logical_cursor()
ltype = self._detect_line_type(w, c.y)
2007-08-22 12:32:32 -04:00
if ltype == self.LT_COMMENT:
self._fix_comments(c, w)
elif ltype == self.LT_POD:
2007-08-23 10:51:05 -04:00
WrapParagraph._execute(self, w, **vargs)
2007-07-21 11:40:53 -04:00
else:
w.set_error("did not detect comment or pod lines")
2007-10-19 02:41:33 -04:00
2009-04-09 00:34:43 -04:00
class PerlSemanticComplete(TokenComplete):
_mini_prompt = 'Semantic Complete'
2008-06-18 11:19:12 -04:00
def _min_completion(self, w, x1, x2, y):
2008-05-28 18:13:40 -04:00
a = w.application
a.methods['iperl-path-start'].execute(w, switch=False)
2009-04-09 00:34:43 -04:00
name = IperlBuffer.create_name(w.buffer)
2008-05-28 18:13:40 -04:00
b = a.get_buffer_by_name(name)
2008-06-18 11:19:12 -04:00
line = w.buffer.lines[y]
2008-06-06 09:11:46 -04:00
candidates = b.readline_completions(x1, x2, line)
2008-06-18 11:19:12 -04:00
if not candidates:
return ([], line[x1:x2])
2008-05-28 18:13:40 -04:00
2008-06-18 11:19:12 -04:00
i = 0
while i < len(candidates[0]):
for s in candidates:
if len(s) <= i or s[i] != candidates[0][i]:
break
i += 1
return (candidates, candidates[0][:i])
2008-06-06 09:11:46 -04:00
def _execute(self, w, **vargs):
2008-06-18 11:19:12 -04:00
b = w.buffer
x2, y = w.logical_cursor().xy()
if y >= len(b.lines):
2008-06-06 09:11:46 -04:00
return
2008-06-18 11:19:12 -04:00
x1 = x2
while x1 > 0 and b.lines[y][x1 - 1] in "$@%*&abcdefghijklmnopqrstuvwxyzABCDEFGHIJKLMNOPQRSTUVWXYZ0123456789_:":
x1 -= 1
assert x1 >= 0, "%r %r %r" % (x1, x2, len(b.lines[y]))
(candidates, result) = self._min_completion(w, x1, x2, y)
2008-06-06 09:11:46 -04:00
if candidates:
2008-06-18 11:19:12 -04:00
p1 = Point(x1, y)
p2 = Point(x2, y)
2008-06-06 09:11:46 -04:00
w.buffer.delete(p1, p2)
w.insert_string(p1, result)
if not candidates:
w.set_error("No completion: %r" % result)
elif len(candidates) == 1:
w.set_error("Unique completion: %r" % result)
elif result in candidates:
w.set_error("Ambiguous completion: %r" % candidates)
else:
w.set_error("Partial completion: %r" % candidates)
2008-05-28 18:13:40 -04:00
2009-04-09 00:34:43 -04:00
class PerlOpenModule(Method):
args = [Argument("module", type=type(""), prompt="Open Perl Module: ")]
def _execute(self, w, **vargs):
path = w.mode.find_module(vargs['module'])
if path:
w.application.methods['open-file'].execute(w, filename=path)
else:
w.set_error("Could not find module %r" % vargs['module'])
2009-04-09 00:34:43 -04:00
class PerlOpenModuleWord(Method):
2008-05-20 11:10:42 -04:00
namechars = string.letters + string.digits + '_'
def _execute(self, w, **vargs):
word = pkg = w.get_token().string
path = None
2008-05-20 11:10:42 -04:00
while pkg and pkg[0] not in self.namechars:
pkg = pkg[1:]
while True:
path = w.mode.find_module(pkg)
if path:
break
2008-05-20 11:10:42 -04:00
parent = pkg.rsplit('::', 1)[0]
if parent == pkg:
break
2008-05-20 11:10:42 -04:00
else:
pkg = parent
if path:
w.application.methods['open-file'].execute(w, filename=path)
else:
2008-05-20 11:10:42 -04:00
w.set_error("Could not find module related to %r" % word)
class PerlFunctionCompleter(completer.Completer):
def get_candidates(self, s, w=None):
old_window = w.buffer.method.old_window
functions = old_window.mode.context.get_names()
return [n for n in functions if n.startswith(s)]
2008-05-11 23:37:57 -04:00
class PerlContext(context.Context):
2009-02-04 08:48:58 -05:00
sub_match = And(Optional(Name('spaces')),
Match('perl.keyword', 'sub'),
2009-02-04 08:48:58 -05:00
Name('spaces'),
Name('perl.sub'))
2008-05-12 18:12:56 -04:00
def _regen_stack(self, y):
if y > 0 and self.namelines[y - 1][1]:
return list(self.namelines[y - 1][1])
2008-05-12 18:12:56 -04:00
else:
return []
2008-05-11 23:37:57 -04:00
def _build_name_map(self, y1, y2, last, curr, stack):
2009-02-04 08:48:58 -05:00
tokenlines = self.mode.window.get_highlighter().tokens
2009-02-04 08:48:58 -05:00
i = y1
while i < y2:
tokens = tokenlines[i]
if not stack:
result = self.sub_match.match(tokens)
if result: curr = tokens[result[0] - 1].string
2008-05-20 11:10:42 -04:00
2009-02-04 08:48:58 -05:00
if curr is not None: self.names.setdefault(curr, i)
2008-05-11 23:37:57 -04:00
2009-02-04 08:48:58 -05:00
for t in tokens:
if t.match('delimiter', '{'):
stack.append(curr)
elif t.match('delimiter', '}'):
if stack: stack.pop(-1)
if not stack: curr = None
2008-05-11 23:37:57 -04:00
2009-02-04 08:48:58 -05:00
if curr: self.namelines[i] = (curr, tuple(stack))
2008-05-12 18:12:56 -04:00
i += 1
2008-05-11 23:37:57 -04:00
2009-04-09 00:34:43 -04:00
class Perl(Fundamental):
name = 'Perl'
extensions = ['.pl', '.pm', '.pod']
2008-04-18 23:32:08 -04:00
detection = ['perl']
2008-10-02 23:57:32 -04:00
tabbercls = PerlTabber2
2008-04-18 23:32:08 -04:00
grammar = PerlGrammar
2009-02-15 12:06:35 -05:00
commentc = '#'
2008-04-18 23:32:08 -04:00
opentokens = ('delimiter',)
opentags = {'(': ')', '[': ']', '{': '}'}
closetokens = ('delimiter',)
closetags = {')': '(', ']': '[', '}': '{'}
colors = {
# comments
'endblock.start': ('red', 'default', 'bold'),
2009-04-20 15:52:13 -04:00
'endblock.data': ('red', 'default', 'bold'),
'endblock.null': ('red', 'default', 'bold'),
'endblock.end': ('red', 'default', 'bold'),
2008-04-18 23:32:08 -04:00
# pod
'pod.start': ('red', 'default', 'bold'),
'pod.null': ('red', 'default', 'bold'),
'pod.entry.start': ('magenta', 'default', 'bold'),
2009-02-04 08:48:58 -05:00
'pod.entry.data': ('magenta', 'default', 'bold'),
'pod.entry.null': ('magenta', 'default', 'bold'),
'pod.entry.end': ('magenta', 'default', 'bold'),
'pod.end': ('red', 'default', 'bold'),
2008-04-18 23:32:08 -04:00
# basic stuff
2008-05-30 14:13:26 -04:00
'null': ('default', 'default', 'bold'),
'sub': ('cyan', 'default', 'bold'),
2008-08-07 19:17:57 -04:00
'prototype': ('yellow', 'default', 'bold'),
2008-05-30 14:13:26 -04:00
'noperator': ('magenta', 'default', 'bold'),
'endblock': ('red', 'default', 'bold'),
'perl.keyword': ('magenta', 'default', 'bold'),
2008-05-30 14:13:26 -04:00
'cast': ('yellow', 'default', 'bold'),
'scalar': ('yellow', 'default', 'bold'),
2009-02-04 08:48:58 -05:00
'length': ('yellow', 'default', 'bold'),
2008-05-30 14:13:26 -04:00
'array': ('yellow', 'default', 'bold'),
'deref': ('yellow', 'default', 'bold'),
'perl.hash': ('yellow', 'default', 'bold'),
2008-05-30 14:13:26 -04:00
'hash_key': ('green', 'default', 'bold'),
'perl.method': ('cyan', 'default', 'bold'),
'perl.function': ('cyan', 'default', 'bold'),
'perl.builtin': ('magenta', 'default', 'bold'),
'perl.label': ('cyan', 'default', 'bold'),
2008-05-30 14:13:26 -04:00
'package': ('cyan', 'default', 'bold'),
'perl.class': ('cyan', 'default', 'bold'),
2008-05-30 14:13:26 -04:00
'use': ('cyan', 'default', 'bold'),
'require': ('cyan', 'default', 'bold'),
2008-04-18 23:32:08 -04:00
# heredoc/evaldoc
'heredoc.start': ('green', 'default', 'bold'),
2009-04-20 15:52:13 -04:00
'heredoc.data': ('green', 'default', 'bold'),
'heredoc.null': ('green', 'default', 'bold'),
'heredoc.end': ('green', 'default', 'bold'),
'evaldoc.start': ('cyan', 'default', 'bold'),
'evaldoc.null': ('cyan', 'default', 'bold'),
'evaldoc.end': ('cyan', 'default', 'bold'),
2008-04-18 23:32:08 -04:00
# strings
'perl.string.start': ('green', 'default', 'bold'),
'perl.string.null': ('green', 'default', 'bold'),
'perl.string.escaped': ('magenta', 'default', 'bold'),
'perl.string.deref': ('yellow', 'default', 'bold'),
'perl.string.end': ('green', 'default', 'bold'),
2008-04-18 23:32:08 -04:00
# `` strings
'evalstring.start': ('cyan', 'default', 'bold'),
'evalstring.null': ('cyan', 'default', 'bold'),
'evalstring.escaped': ('magenta', 'default', 'bold'),
'evalstring.deref': ('yellow', 'default', 'bold'),
'evalstring.end': ('cyan', 'default', 'bold'),
2008-04-18 23:32:08 -04:00
# quoted region
'perl.quoted': ('cyan', 'default', 'bold'),
'perl.quoted.start': ('cyan', 'default', 'bold'),
'perl.quoted.null': ('cyan', 'default', 'bold'),
'perl.quoted.data': ('cyan', 'default', 'bold'),
'perl.quoted.escaped': ('magenta', 'default', 'bold'),
'perl.quoted.deref': ('yellow', 'default', 'bold'),
'perl.quoted.end': ('cyan', 'default', 'bold'),
2008-04-18 23:32:08 -04:00
# match regex
'match.start': ('cyan', 'default', 'bold'),
'match.end': ('cyan', 'default', 'bold'),
'match.null': ('cyan', 'default', 'bold'),
2008-04-18 23:32:08 -04:00
# replace regex
'replace.start': ('cyan', 'default', 'bold'),
'replace.middle0': ('cyan', 'default', 'bold'),
'replace.end': ('cyan', 'default', 'bold'),
'replace.null': ('cyan', 'default', 'bold'),
'replace.escaped': ('magenta', 'default', 'bold'),
'replace.deref': ('yellow', 'default', 'bold'),
'replace.length': ('yellow', 'default', 'bold'),
'replace.scalar': ('yellow', 'default', 'bold'),
'replace.perl.hash': ('yellow', 'default', 'bold'),
'replace.cast': ('yellow', 'default', 'bold'),
2008-04-18 23:32:08 -04:00
# translate regex
'translate.start': ('magenta', 'default', 'bold'),
'translate.middle0': ('magenta', 'default', 'bold'),
'translate.end': ('magenta', 'default', 'bold'),
'translate.null': ('magenta', 'default', 'bold'),
2008-04-18 23:32:08 -04:00
}
2009-03-16 10:42:17 -04:00
config = {}
lconfig = {'perl.libs': []}
2008-04-18 23:32:08 -04:00
actions = [PerlSetLib, PerlCheckSyntax, PerlHashCleanup,
PerldocModule, PerldocWord, Perldoc, PerldocF,
PerlWrapParagraph, PerlInitFunctions, PerlGotoFunction,
PerlWhichFunction, PerlListFunctions, PerlOpenModule,
PerlOpenModuleWord, PerlSemanticComplete]
completers = {
'perlfunction': PerlFunctionCompleter(None),
}
format = "%(flag)s %(bname)-18s (%(mname)s) %(indent)s %(cursor)s %(perc)s [%(func)s] %(vc-info)s"
2009-04-09 00:34:43 -04:00
_bindings = {
'perl-set-lib': ('C-c l',),
'perl-check-syntax': ('C-c s',),
'perl-hash-cleanup': ('C-c h',),
'perldoc-module': ('C-c v',),
'perldoc-word': ('C-c p',),
'perl-wrap-paragraph': ('M-q',),
'perl-goto-function': ('C-c M-g',),
'perl-open-module': ('C-c C-f',),
'perl-open-module-word': ('C-c M-f',),
'perl-semantic-complete': ('C-c TAB',),
'close-paren': (')'),
'close-bracket': (']'),
'close-brace': ('}'),
}
2008-04-18 23:32:08 -04:00
def __init__(self, w):
2009-04-09 00:34:43 -04:00
Fundamental.__init__(self, w)
2009-04-10 15:44:24 -04:00
2009-01-24 12:49:19 -05:00
self.context = PerlContext(self)
2008-04-18 23:32:08 -04:00
self.functions = None
2008-05-08 02:57:23 -04:00
self.funclines = None
2008-11-08 13:32:32 -05:00
self.perlinc = None
2008-04-18 23:32:08 -04:00
def find_module(self, module):
parts = module.split('::')
parts[-1] += '.pm'
relpath = os.path.join(*parts)
path = None
for d in self.get_inc():
path2 = os.path.join(d, relpath)
if os.path.exists(path2):
path = path2
break
return path
def get_inc(self):
2009-01-06 18:45:05 -05:00
a = self.window.application
2008-11-08 13:32:32 -05:00
if self.perlinc is None:
2008-12-02 15:21:28 -05:00
cmd = "perl -e 'print join(\"\\n\", @INC);'"
if a.config.get('perl.libs', None):
2009-01-06 18:45:05 -05:00
s = ':'.join(['%s' % x for x in a.config.get('perl.libs')])
2008-12-02 15:21:28 -05:00
cmd = 'PERL5LIB=%r %s' % (s, cmd)
2009-03-16 10:42:17 -04:00
2009-04-09 00:34:43 -04:00
p = Popen(cmd, shell=True, stdout=PIPE, stderr=PIPE)
data = p.stdout.read()
status = p.wait()
if status != 0: raise Exception, "%r failed" % cmd
2008-11-08 13:32:32 -05:00
self.perlinc = data.split('\n')
return self.perlinc
2009-04-09 00:34:43 -04:00
def get_functions(self): return self.context.get_names()
def get_function_names(self): return self.context.get_name_list()
def get_line_function(self, y): return self.context.get_line_name(y)
def get_status_names(self):
names = Fundamental.get_status_names(self)
c = self.window.logical_cursor()
names['func'] = self.get_line_function(c.y)
return names
2008-04-18 23:32:08 -04:00
2007-10-19 02:41:33 -04:00
install = Perl.install