103 lines
2.6 KiB
Ruby
103 lines
2.6 KiB
Ruby
|
# -*- coding: utf-8 -*- #
|
||
|
# frozen_string_literal: true
|
||
|
|
||
|
module Rouge
|
||
|
module Lexers
|
||
|
class VimL < RegexLexer
|
||
|
title "VimL"
|
||
|
desc "VimL, the scripting language for the Vim editor (vim.org)"
|
||
|
tag 'viml'
|
||
|
aliases 'vim', 'vimscript', 'ex'
|
||
|
filenames '*.vim', '*.vba', '.vimrc', '.exrc', '.gvimrc',
|
||
|
'_vimrc', '_exrc', '_gvimrc' # _ names for windows
|
||
|
|
||
|
mimetypes 'text/x-vim'
|
||
|
|
||
|
def self.keywords
|
||
|
load File.join(__dir__, 'viml/keywords.rb')
|
||
|
self.keywords
|
||
|
end
|
||
|
|
||
|
state :root do
|
||
|
rule %r/^(\s*)(".*?)$/ do
|
||
|
groups Text, Comment
|
||
|
end
|
||
|
|
||
|
rule %r/^\s*\\/, Str::Escape
|
||
|
|
||
|
rule %r/[ \t]+/, Text
|
||
|
|
||
|
# TODO: regexes can have other delimiters
|
||
|
rule %r(/(\\\\|\\/|[^\n/])*/), Str::Regex
|
||
|
rule %r("(\\\\|\\"|[^\n"])*"), Str::Double
|
||
|
rule %r('(\\\\|\\'|[^\n'])*'), Str::Single
|
||
|
|
||
|
# if it's not a string, it's a comment.
|
||
|
rule %r/(?<=\s)"[^-:.%#=*].*?$/, Comment
|
||
|
|
||
|
rule %r/-?\d+/, Num
|
||
|
rule %r/#[0-9a-f]{6}/i, Num::Hex
|
||
|
rule %r/^:/, Punctuation
|
||
|
rule %r/[():<>+=!\[\]{}\|,~.-]/, Punctuation
|
||
|
rule %r/\b(let|if|else|endif|elseif|fun|function|endfunction)\b/,
|
||
|
Keyword
|
||
|
|
||
|
rule %r/\b(NONE|bold|italic|underline|dark|light)\b/, Name::Builtin
|
||
|
|
||
|
rule %r/[absg]:\w+\b/, Name::Variable
|
||
|
rule %r/\b\w+\b/ do |m|
|
||
|
name = m[0]
|
||
|
keywords = self.class.keywords
|
||
|
|
||
|
if mapping_contains?(keywords[:command], name)
|
||
|
token Keyword
|
||
|
elsif mapping_contains?(keywords[:option], name)
|
||
|
token Name::Builtin
|
||
|
elsif mapping_contains?(keywords[:auto], name)
|
||
|
token Name::Builtin
|
||
|
else
|
||
|
token Text
|
||
|
end
|
||
|
end
|
||
|
|
||
|
# no errors in VimL!
|
||
|
rule %r/./m, Text
|
||
|
end
|
||
|
|
||
|
def mapping_contains?(mapping, word)
|
||
|
shortest, longest = find_likely_mapping(mapping, word)
|
||
|
|
||
|
shortest and word.start_with?(shortest) and
|
||
|
longest and longest.start_with?(word)
|
||
|
end
|
||
|
|
||
|
# binary search through the mappings to find the one that's likely
|
||
|
# to actually work.
|
||
|
def find_likely_mapping(mapping, word)
|
||
|
min = 0
|
||
|
max = mapping.size
|
||
|
|
||
|
until max == min
|
||
|
mid = (max + min) / 2
|
||
|
|
||
|
cmp, _ = mapping[mid]
|
||
|
|
||
|
case word <=> cmp
|
||
|
when 1
|
||
|
# too low
|
||
|
min = mid + 1
|
||
|
when -1
|
||
|
# too high
|
||
|
max = mid
|
||
|
when 0
|
||
|
# just right, abort!
|
||
|
return mapping[mid]
|
||
|
end
|
||
|
end
|
||
|
|
||
|
mapping[max - 1]
|
||
|
end
|
||
|
end
|
||
|
end
|
||
|
end
|