pmacs3/mode/scala.py

211 lines
6.9 KiB
Python

from tab import StackTabber2
from mode import Fundamental
from lex import Grammar, PatternRule, RegionRule, PatternMatchRule
from mode.xml import XMLGrammar
from mode.pipe import Pipe
from method.shell import Interact
from method import Method, arg
import default
import urllib2
import os
chr1 = '[a-zA-Z_]'
chr2 = '[a-zA-Z_0-9]'
word = chr1 + chr2 + '*'
class NestedCommentGrammar(Grammar): pass
NestedCommentGrammar.rules = [
RegionRule('comment', r'/\*', NestedCommentGrammar, r'\*/'),
PatternRule('data', r'(?:[^\*]|\*(?!/))+'),
]
class StringGrammar(Grammar):
rules = [
PatternRule('escaped', r"\\u[0-9A-Fa-f]{4}|\\[0-7]{1,3}|\\[btnfr\"'\\]"),
PatternRule('data', r'[^\\"]+'),
]
class ScalaGrammar(Grammar):
rules = [
PatternRule('scala.comment', '//.*$'),
RegionRule('scala.comment', r'/\*', NestedCommentGrammar, r'\*/'),
PatternMatchRule('x', r'(?<=[a-zA-Z0-9_ ])(:)([a-zA-Z0-9_]+)',
'delimiter', 'scala.type'),
PatternMatchRule('x', r'(?<=[a-zA-Z0-9_ ])(:)( +)([a-zA-Z0-9_]+)',
'delimiter', 'spaces', 'scala.type'),
PatternRule('delimiter', r'(?:;|{|}|\[|\]|\(|\)|,|\.|<(?![a-zA-Z_])|>|:|/|\+|-|\*|=)'),
RegionRule('scala.inline', r'(?:^| )(?=<[a-zA-Z_])', XMLGrammar, '^[ \t]*$'),
PatternRule('spaces', r'(?:\t| )+'),
PatternRule('eol', r'\n'),
PatternRule('scala.def', '(?<=(?<![a-zA-Z0-9_])def )[a-zA-Z_][a-zA-Z0-9_]*'),
PatternRule('scala.class', '(?<=(?<![a-zA-Z0-9_])class )[a-zA-Z_][a-zA-Z0-9_]*'),
PatternRule('scala.object', '(?<=(?<![a-zA-Z0-9_])object )[a-zA-Z_][a-zA-Z0-9_]*'),
PatternRule('scala.trait', '(?<=(?<![a-zA-Z0-9_])trait )[a-zA-Z_][a-zA-Z0-9_]*'),
PatternRule('scala.pseudo', '(?:true|null|false)'),
PatternRule('scala.reserved', '(?:yield|with|while|var|val|until|type|true|try|trait|throw|this|super|sealed|return|protected|private|package|override|object|null|new|match|lazy|import|implicit|if|forSome|for|finally|final|false|extends|else|do|def|class|catch|case|abstract)(?!%s)' % word),
PatternRule('scala.integer', '-?(?:0|[1-9])[0-9]*[Ll]?'),
PatternRule('scala.integer', '-?0x[0-9A-Fa-f]+[Ll]?'),
PatternRule('scala.integer', '-?0[0-7]+[Ll]?'),
PatternRule('scala.float', r'-?[0-9]+\.[0-9]*'), # FIXME
PatternRule('scala.char', r"'(?:[^'\\]|\\u[0-9A-Fa-f]{4}|\\[0-7]{1,3}|\\[btnfr\"'\\])'"),
RegionRule('scala.string', '"', StringGrammar, '"'),
PatternRule('scala.symbol', "'[a-zA-Z_][a-zA-Z0-9_]*"),
PatternRule('scala.annotation', '@[a-zA-Z_][a-zA-Z0-9_]*'),
PatternRule('scala.bareword', '[a-zA-Z_][a-zA-Z0-9_]*'),
]
class ScalaTabber(StackTabber2):
open_tokens = {'delimiter': {'{': '}', '(': ')', '[': ']'}}
close_tokens = {'delimiter': {'}': '{', ')': '(', ']': '['}}
control_tokens = {'scala.reserved': set(('if', 'else', 'while', 'do', 'for'))}
end_at_eof = True
start_free_tokens = {'string.start': 'string.end'}
end_free_tokens = {'string.end': 'string.start'}
is_ignored_tokens = set(('spaces', 'eol', 'comment', 'comment.start',
'comment.data', 'comment.null', 'comment.end'))
is_indent_tokens = set(('spaces',))
def _is_base(self, y):
# the first line is always safe
if y == 0: return True
# if there are no tokens we don't really have any info
tokens = self._get_tokens(y)
if not tokens: return False
# if it looks like a top-level class, object or function, then say ok
t = tokens[0]
if t.fqmatchs('scala.reserved', ('class', 'object', 'def')):
return True
# the default is to assume no
return False
class ScalaStart(Interact):
args = []
modename = 'scalapipe'
reuse = True
def _execute(self, w, **vargs):
Interact._execute(self, w, bname='*Scala*', cmd='scala')
class ScalaDocBrowse(Method):
def _execute(self, w, **vargs):
a = w.application
url = a.config['scala.api']
Interact().execute(w, bname='*Scala-Doc*', cmd='links "%s"' % url)
class ScalaDocLookup(Method):
args = [arg('name', t='string', p='Name: ', dv=default.current_word,
ld=True, h='The Scala name to get help on')]
def _execute(self, w, **vargs):
try:
import BeautifulSoup
except ImportError:
w.set_error('BeautifulSoup is not installed...')
return
a = w.application
name = vargs.get('name')
path = a.getpath('cache', 'scala', 'api.html')
url = a.config['scala.api']
a.mkdirs('cache', 'scala')
if not os.path.exists(path):
open(path, 'w').write(urllib2.urlopen(url).read())
w.set_error('looking up %s...' % name)
# white is for delimiters, operators, numbers
default = ('default', 'default')
# magenta is for keywords/builtins
lo_magenta = ('magenta202', 'default')
hi_magenta = ('magenta414', 'default')
# red is for comments
lo_red = ('red300', 'default')
hi_red = ('red511', 'default')
# orange is for macro definitions, headers and constants
hi_orange = ('yellow531', 'default')
lo_orange = ('yellow520', 'default')
# yellow is for parts of macros
hi_yellow = ('yellow551', 'default')
lo_yellow = ('yellow330', 'default')
# green is for strings and characters
lo_green = ('green030', 'default')
hi_green = ('green050', 'default')
# cyan is for types
lo_cyan = ('cyan033', 'default')
hi_cyan = ('cyan155', 'default')
# blue is definitions, functions and some macros
lo_blue = ('blue113', 'default')
hi_blue = ('blue225', 'default')
class Scala(Fundamental):
name = 'Scala'
extensions = ['.scala']
tabwidth = 2
tabbercls = ScalaTabber
grammar = ScalaGrammar
commentc = '//'
actions = [ScalaStart, ScalaDocBrowse, ScalaDocLookup]
opentokens = ('delimiter',)
opentags = {'(': ')', '[': ']', '{': '}'}
closetokens = ('delimiter',)
closetags = {')': '(', ']': '[', '}': '{'}
config = {
'scala.api': 'http://www.scala-lang.org/api/current/allclasses.html',
}
colors = {
'scala.annotation': lo_green,
'scala.pseudo': hi_magenta,
'scala.reserved': hi_cyan,
'scala.integer': default,
'scala.float': default,
'scala.bareword': default,
'scala.symbol': hi_orange,
'scala.class': hi_yellow,
'scala.object': hi_yellow,
'scala.trait': hi_yellow,
'scala.type': hi_magenta,
'scala.def': hi_blue,
'scala.def': hi_blue,
}
_bindings = {
'close-paren': (')',),
'close-brace': ('}',),
'close-bracket': (']',),
}
class ScalaPipe(Pipe):
name = 'scalapipe'
grammar = ScalaGrammar
def install(*args):
Scala.install(*args)
ScalaPipe.install(*args)