123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358 |
- // CodeMirror, copyright (c) by Marijn Haverbeke and others
- // Distributed under an MIT license: https://codemirror.net/LICENSE
- ;(function (mod) {
- if (typeof exports == 'object' && typeof module == 'object')
- // CommonJS
- mod(require('../../lib/codemirror'))
- else if (typeof define == 'function' && define.amd)
- // AMD
- define(['../../lib/codemirror'], mod)
- // Plain browser env
- else mod(CodeMirror)
- })(function (CodeMirror) {
- 'use strict'
- function wordObj(words) {
- var o = {}
- for (var i = 0, e = words.length; i < e; ++i) o[words[i]] = true
- return o
- }
- var keywordList = [
- 'alias',
- 'and',
- 'BEGIN',
- 'begin',
- 'break',
- 'case',
- 'class',
- 'def',
- 'defined?',
- 'do',
- 'else',
- 'elsif',
- 'END',
- 'end',
- 'ensure',
- 'false',
- 'for',
- 'if',
- 'in',
- 'module',
- 'next',
- 'not',
- 'or',
- 'redo',
- 'rescue',
- 'retry',
- 'return',
- 'self',
- 'super',
- 'then',
- 'true',
- 'undef',
- 'unless',
- 'until',
- 'when',
- 'while',
- 'yield',
- 'nil',
- 'raise',
- 'throw',
- 'catch',
- 'fail',
- 'loop',
- 'callcc',
- 'caller',
- 'lambda',
- 'proc',
- 'public',
- 'protected',
- 'private',
- 'require',
- 'load',
- 'require_relative',
- 'extend',
- 'autoload',
- '__END__',
- '__FILE__',
- '__LINE__',
- '__dir__',
- ],
- keywords = wordObj(keywordList)
- var indentWords = wordObj(['def', 'class', 'case', 'for', 'while', 'until', 'module', 'then', 'catch', 'loop', 'proc', 'begin'])
- var dedentWords = wordObj(['end', 'until'])
- var opening = { '[': ']', '{': '}', '(': ')' }
- var closing = { ']': '[', '}': '{', ')': '(' }
- CodeMirror.defineMode('ruby', function (config) {
- var curPunc
- function chain(newtok, stream, state) {
- state.tokenize.push(newtok)
- return newtok(stream, state)
- }
- function tokenBase(stream, state) {
- if (stream.sol() && stream.match('=begin') && stream.eol()) {
- state.tokenize.push(readBlockComment)
- return 'comment'
- }
- if (stream.eatSpace()) return null
- var ch = stream.next(),
- m
- if (ch == '`' || ch == "'" || ch == '"') {
- return chain(readQuoted(ch, 'string', ch == '"' || ch == '`'), stream, state)
- } else if (ch == '/') {
- if (regexpAhead(stream)) return chain(readQuoted(ch, 'string-2', true), stream, state)
- else return 'operator'
- } else if (ch == '%') {
- var style = 'string',
- embed = true
- if (stream.eat('s')) style = 'atom'
- else if (stream.eat(/[WQ]/)) style = 'string'
- else if (stream.eat(/[r]/)) style = 'string-2'
- else if (stream.eat(/[wxq]/)) {
- style = 'string'
- embed = false
- }
- var delim = stream.eat(/[^\w\s=]/)
- if (!delim) return 'operator'
- if (opening.propertyIsEnumerable(delim)) delim = opening[delim]
- return chain(readQuoted(delim, style, embed, true), stream, state)
- } else if (ch == '#') {
- stream.skipToEnd()
- return 'comment'
- } else if (ch == '<' && (m = stream.match(/^<([-~])[\`\"\']?([a-zA-Z_?]\w*)[\`\"\']?(?:;|$)/))) {
- return chain(readHereDoc(m[2], m[1]), stream, state)
- } else if (ch == '0') {
- if (stream.eat('x')) stream.eatWhile(/[\da-fA-F]/)
- else if (stream.eat('b')) stream.eatWhile(/[01]/)
- else stream.eatWhile(/[0-7]/)
- return 'number'
- } else if (/\d/.test(ch)) {
- stream.match(/^[\d_]*(?:\.[\d_]+)?(?:[eE][+\-]?[\d_]+)?/)
- return 'number'
- } else if (ch == '?') {
- while (stream.match(/^\\[CM]-/)) {}
- if (stream.eat('\\')) stream.eatWhile(/\w/)
- else stream.next()
- return 'string'
- } else if (ch == ':') {
- if (stream.eat("'")) return chain(readQuoted("'", 'atom', false), stream, state)
- if (stream.eat('"')) return chain(readQuoted('"', 'atom', true), stream, state)
- // :> :>> :< :<< are valid symbols
- if (stream.eat(/[\<\>]/)) {
- stream.eat(/[\<\>]/)
- return 'atom'
- }
- // :+ :- :/ :* :| :& :! are valid symbols
- if (stream.eat(/[\+\-\*\/\&\|\:\!]/)) {
- return 'atom'
- }
- // Symbols can't start by a digit
- if (stream.eat(/[a-zA-Z$@_\xa1-\uffff]/)) {
- stream.eatWhile(/[\w$\xa1-\uffff]/)
- // Only one ? ! = is allowed and only as the last character
- stream.eat(/[\?\!\=]/)
- return 'atom'
- }
- return 'operator'
- } else if (ch == '@' && stream.match(/^@?[a-zA-Z_\xa1-\uffff]/)) {
- stream.eat('@')
- stream.eatWhile(/[\w\xa1-\uffff]/)
- return 'variable-2'
- } else if (ch == '$') {
- if (stream.eat(/[a-zA-Z_]/)) {
- stream.eatWhile(/[\w]/)
- } else if (stream.eat(/\d/)) {
- stream.eat(/\d/)
- } else {
- stream.next() // Must be a special global like $: or $!
- }
- return 'variable-3'
- } else if (/[a-zA-Z_\xa1-\uffff]/.test(ch)) {
- stream.eatWhile(/[\w\xa1-\uffff]/)
- stream.eat(/[\?\!]/)
- if (stream.eat(':')) return 'atom'
- return 'ident'
- } else if (ch == '|' && (state.varList || state.lastTok == '{' || state.lastTok == 'do')) {
- curPunc = '|'
- return null
- } else if (/[\(\)\[\]{}\\;]/.test(ch)) {
- curPunc = ch
- return null
- } else if (ch == '-' && stream.eat('>')) {
- return 'arrow'
- } else if (/[=+\-\/*:\.^%<>~|]/.test(ch)) {
- var more = stream.eatWhile(/[=+\-\/*:\.^%<>~|]/)
- if (ch == '.' && !more) curPunc = '.'
- return 'operator'
- } else {
- return null
- }
- }
- function regexpAhead(stream) {
- var start = stream.pos,
- depth = 0,
- next,
- found = false,
- escaped = false
- while ((next = stream.next()) != null) {
- if (!escaped) {
- if ('[{('.indexOf(next) > -1) {
- depth++
- } else if (']})'.indexOf(next) > -1) {
- depth--
- if (depth < 0) break
- } else if (next == '/' && depth == 0) {
- found = true
- break
- }
- escaped = next == '\\'
- } else {
- escaped = false
- }
- }
- stream.backUp(stream.pos - start)
- return found
- }
- function tokenBaseUntilBrace(depth) {
- if (!depth) depth = 1
- return function (stream, state) {
- if (stream.peek() == '}') {
- if (depth == 1) {
- state.tokenize.pop()
- return state.tokenize[state.tokenize.length - 1](stream, state)
- } else {
- state.tokenize[state.tokenize.length - 1] = tokenBaseUntilBrace(depth - 1)
- }
- } else if (stream.peek() == '{') {
- state.tokenize[state.tokenize.length - 1] = tokenBaseUntilBrace(depth + 1)
- }
- return tokenBase(stream, state)
- }
- }
- function tokenBaseOnce() {
- var alreadyCalled = false
- return function (stream, state) {
- if (alreadyCalled) {
- state.tokenize.pop()
- return state.tokenize[state.tokenize.length - 1](stream, state)
- }
- alreadyCalled = true
- return tokenBase(stream, state)
- }
- }
- function readQuoted(quote, style, embed, unescaped) {
- return function (stream, state) {
- var escaped = false,
- ch
- if (state.context.type === 'read-quoted-paused') {
- state.context = state.context.prev
- stream.eat('}')
- }
- while ((ch = stream.next()) != null) {
- if (ch == quote && (unescaped || !escaped)) {
- state.tokenize.pop()
- break
- }
- if (embed && ch == '#' && !escaped) {
- if (stream.eat('{')) {
- if (quote == '}') {
- state.context = { prev: state.context, type: 'read-quoted-paused' }
- }
- state.tokenize.push(tokenBaseUntilBrace())
- break
- } else if (/[@\$]/.test(stream.peek())) {
- state.tokenize.push(tokenBaseOnce())
- break
- }
- }
- escaped = !escaped && ch == '\\'
- }
- return style
- }
- }
- function readHereDoc(phrase, mayIndent) {
- return function (stream, state) {
- if (mayIndent) stream.eatSpace()
- if (stream.match(phrase)) state.tokenize.pop()
- else stream.skipToEnd()
- return 'string'
- }
- }
- function readBlockComment(stream, state) {
- if (stream.sol() && stream.match('=end') && stream.eol()) state.tokenize.pop()
- stream.skipToEnd()
- return 'comment'
- }
- return {
- startState: function () {
- return { tokenize: [tokenBase], indented: 0, context: { type: 'top', indented: -config.indentUnit }, continuedLine: false, lastTok: null, varList: false }
- },
- token: function (stream, state) {
- curPunc = null
- if (stream.sol()) state.indented = stream.indentation()
- var style = state.tokenize[state.tokenize.length - 1](stream, state),
- kwtype
- var thisTok = curPunc
- if (style == 'ident') {
- var word = stream.current()
- style =
- state.lastTok == '.'
- ? 'property'
- : keywords.propertyIsEnumerable(stream.current())
- ? 'keyword'
- : /^[A-Z]/.test(word)
- ? 'tag'
- : state.lastTok == 'def' || state.lastTok == 'class' || state.varList
- ? 'def'
- : 'variable'
- if (style == 'keyword') {
- thisTok = word
- if (indentWords.propertyIsEnumerable(word)) kwtype = 'indent'
- else if (dedentWords.propertyIsEnumerable(word)) kwtype = 'dedent'
- else if ((word == 'if' || word == 'unless') && stream.column() == stream.indentation()) kwtype = 'indent'
- else if (word == 'do' && state.context.indented < state.indented) kwtype = 'indent'
- }
- }
- if (curPunc || (style && style != 'comment')) state.lastTok = thisTok
- if (curPunc == '|') state.varList = !state.varList
- if (kwtype == 'indent' || /[\(\[\{]/.test(curPunc)) state.context = { prev: state.context, type: curPunc || style, indented: state.indented }
- else if ((kwtype == 'dedent' || /[\)\]\}]/.test(curPunc)) && state.context.prev) state.context = state.context.prev
- if (stream.eol()) state.continuedLine = curPunc == '\\' || style == 'operator'
- return style
- },
- indent: function (state, textAfter) {
- if (state.tokenize[state.tokenize.length - 1] != tokenBase) return CodeMirror.Pass
- var firstChar = textAfter && textAfter.charAt(0)
- var ct = state.context
- var closed = ct.type == closing[firstChar] || (ct.type == 'keyword' && /^(?:end|until|else|elsif|when|rescue)\b/.test(textAfter))
- return ct.indented + (closed ? 0 : config.indentUnit) + (state.continuedLine ? config.indentUnit : 0)
- },
- electricInput: /^\s*(?:end|rescue|elsif|else|\})$/,
- lineComment: '#',
- fold: 'indent',
- }
- })
- CodeMirror.defineMIME('text/x-ruby', 'ruby')
- CodeMirror.registerHelper('hintWords', 'ruby', keywordList)
- })
|