pmacs3/mode/perl.py

852 lines
35 KiB
Python
Raw Normal View History

2008-11-08 10:30:04 -05:00
import os, re, string, sys
from subprocess import Popen, PIPE, STDOUT
2009-03-16 10:42:17 -04:00
import buffer, color, commands, completer, context, method, mode, regex, tab
from point import Point
from lex import Grammar, PatternRule, ContextPatternRule, RegionRule
from lex import OverridePatternRule, PatternMatchRule
2007-08-23 10:51:05 -04:00
from method import Argument, Method, WrapParagraph
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_]'
hword = wchr2 + '+'
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('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',
'perl.delimiter', 'spaces', 'perl.prototype',
'spaces', 'perl.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),
2009-04-08 00:41:30 -04:00
#PatternMatchRule('x', '(' + wchr2 + '+)( *)(=>)'
# 'perl.hash_key', 'spaces', 'delimiter'),
PatternRule('perl.hash_key', wchr2 + '+(?= *=>)'),
PatternRule('perl.length', r"\$#" + word2),
2009-04-08 00:41:30 -04:00
#PatternMatchRule('x', r'([\$\@\%\&\*])( *)({)',
# 'perl.cast', 'spaces', 'delimiter'),
PatternRule('perl.cast', r'[\$\@\%\&\*](?= *{)'),
PatternRule('perl.scalar', r"\$[\[\]<>ab/'\"_@\?#\$!%^|&*()](?!" +
wchr2 + ")"),
PatternRule('perl.array', "@_"),
2009-04-08 00:41:30 -04:00
#PatternMatchRule('x', r"(\$\$*" + word2 + ")(->)( *)(\()",
# 'perl.function', 'delimiter', 'spaces', 'delimiter'),
PatternRule('perl.function', r"\$\$*" + word2 + "(?=-> *\()"),
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('perl.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 = {'perl.delimiter': {'{': '}', '(': ')', '[': ']'}}
close_tokens = {'perl.delimiter': {'}': '{', ')': '(', ']': '['}}
end_at_eof = False
end_at_tokens = {'perl.delimiter': {';': 1}}
nocontinue_tokens = {'perl.delimiter': {';': 1, ',': 1, '}': 1},
'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-03-16 10:42:17 -04:00
args = [method.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-03-16 10:42:17 -04:00
args = [method.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
class PerlWrapParagraph(method.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
2008-05-28 18:13:40 -04:00
class PerlSemanticComplete(method.introspect.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)
name = buffer.IperlBuffer.create_name(w.buffer)
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
class PerlOpenModule(method.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'])
class PerlOpenModuleWord(method.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('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
2008-04-18 23:32:08 -04:00
class Perl(mode.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'),
'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'),
'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),
}
2009-03-16 20:00:28 -04:00
format = "%(flag)s %(bname)-18s (%(mname)s) %(indent)s %(cursor)s %(perc)s [%(func)s]"
def get_status_names(self):
names = mode.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
def __init__(self, w):
mode.Fundamental.__init__(self, w)
self.add_bindings('perl-set-lib', ('C-c l',))
self.add_bindings('perl-check-syntax', ('C-c s',))
self.add_bindings('perl-hash-cleanup', ('C-c h',))
2009-03-14 19:53:44 -04:00
#self.add_bindings('perl-view-module-perldoc', ('C-c v',))
#self.add_bindings('perl-view-word-perldoc', ('C-c p',))
self.add_bindings('perldoc-module', ('C-c v',))
self.add_bindings('perldoc-word', ('C-c p',))
2008-04-18 23:32:08 -04:00
self.add_bindings('perl-wrap-paragraph', ('M-q',))
self.add_bindings('perl-goto-function', ('C-c M-g',))
2008-05-20 11:10:42 -04:00
self.add_bindings('perl-open-module', ('C-c C-f',))
self.add_bindings('perl-open-module-word', ('C-c M-f',))
2008-05-28 18:13:40 -04:00
self.add_bindings('perl-semantic-complete', ('C-c TAB',))
2008-04-18 23:32:08 -04:00
self.add_bindings('close-paren', (')'))
self.add_bindings('close-bracket', (']'))
self.add_bindings('close-brace', ('}'))
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)
(status, data) = commands.getstatusoutput(cmd)
2009-03-16 10:42:17 -04:00
if status != 0:
raise Exception, "%r failed" % cmd
2008-11-08 13:32:32 -05:00
self.perlinc = data.split('\n')
return self.perlinc
2008-04-18 23:32:08 -04:00
def get_functions(self):
2009-01-24 12:49:19 -05:00
return self.context.get_names()
2008-04-18 23:32:08 -04:00
def get_function_names(self):
2009-01-24 12:49:19 -05:00
return self.context.get_name_list()
2008-05-08 02:57:23 -04:00
def get_line_function(self, y):
2009-01-24 12:49:19 -05:00
return self.context.get_line_name(y)
2008-04-18 23:32:08 -04:00
2007-10-19 02:41:33 -04:00
install = Perl.install