web_sandbox_console 0.1.0 → 0.6.0
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- checksums.yaml +4 -4
- data/README.md +143 -6
- data/app/assets/images/web_sandbox_console/logo-icon.png +0 -0
- data/app/assets/javascripts/web_sandbox_console/application.js +0 -1
- data/app/assets/javascripts/web_sandbox_console/code_editor.js +0 -0
- data/app/assets/javascripts/web_sandbox_console/codemirror.js +9778 -0
- data/app/assets/javascripts/web_sandbox_console/matchbrackets.js +158 -0
- data/app/assets/javascripts/web_sandbox_console/ruby.js +298 -0
- data/app/assets/stylesheets/web_sandbox_console/application.css +85 -14
- data/app/assets/stylesheets/web_sandbox_console/codemirror.css +349 -0
- data/app/assets/stylesheets/web_sandbox_console/common.css +231 -0
- data/app/assets/stylesheets/web_sandbox_console/docs.css +274 -0
- data/app/assets/stylesheets/web_sandbox_console/home.css +47 -0
- data/app/assets/stylesheets/web_sandbox_console/lucario.css +45 -0
- data/app/controllers/web_sandbox_console/application_controller.rb +4 -0
- data/app/controllers/web_sandbox_console/authorization_controller.rb +82 -0
- data/app/controllers/web_sandbox_console/home_controller.rb +44 -4
- data/app/views/layouts/web_sandbox_console/application.html.erb +82 -11
- data/app/views/web_sandbox_console/authorization/auth_page.html.erb +33 -0
- data/app/views/web_sandbox_console/home/do_view_file.js.erb +14 -0
- data/app/views/web_sandbox_console/home/download_page.html.erb +22 -0
- data/app/views/web_sandbox_console/home/eval_code.js.erb +1 -1
- data/app/views/web_sandbox_console/home/index.html.erb +34 -7
- data/app/views/web_sandbox_console/home/view_file.html.erb +39 -0
- data/config/routes.rb +12 -0
- data/lib/generators/web_sandbox_console/templates/web_sandbox_console.rb +20 -3
- data/lib/generators/web_sandbox_console/templates/web_sandbox_console.yml +5 -0
- data/lib/generators/web_sandbox_console/web_sandbox_console_generator.rb +5 -0
- data/lib/web_sandbox_console.rb +4 -0
- data/lib/web_sandbox_console/common.rb +29 -8
- data/lib/web_sandbox_console/configuration.rb +9 -1
- data/lib/web_sandbox_console/safe_ruby.rb +116 -16
- data/lib/web_sandbox_console/sandbox.rb +119 -20
- data/lib/web_sandbox_console/sandbox_error.rb +4 -0
- data/lib/web_sandbox_console/version.rb +1 -1
- data/lib/web_sandbox_console/view_file.rb +257 -0
- data/lib/web_sandbox_console/view_file_error.rb +4 -0
- metadata +28 -6
- data/app/jobs/web_sandbox_console/application_job.rb +0 -4
@@ -0,0 +1,158 @@
|
|
1
|
+
// CodeMirror, copyright (c) by Marijn Haverbeke and others
|
2
|
+
// Distributed under an MIT license: https://codemirror.net/LICENSE
|
3
|
+
|
4
|
+
(function(mod) {
|
5
|
+
if (typeof exports == "object" && typeof module == "object") // CommonJS
|
6
|
+
mod(require("../../lib/codemirror"));
|
7
|
+
else if (typeof define == "function" && define.amd) // AMD
|
8
|
+
define(["../../lib/codemirror"], mod);
|
9
|
+
else // Plain browser env
|
10
|
+
mod(CodeMirror);
|
11
|
+
})(function(CodeMirror) {
|
12
|
+
var ie_lt8 = /MSIE \d/.test(navigator.userAgent) &&
|
13
|
+
(document.documentMode == null || document.documentMode < 8);
|
14
|
+
|
15
|
+
var Pos = CodeMirror.Pos;
|
16
|
+
|
17
|
+
var matching = {"(": ")>", ")": "(<", "[": "]>", "]": "[<", "{": "}>", "}": "{<", "<": ">>", ">": "<<"};
|
18
|
+
|
19
|
+
function bracketRegex(config) {
|
20
|
+
return config && config.bracketRegex || /[(){}[\]]/
|
21
|
+
}
|
22
|
+
|
23
|
+
function findMatchingBracket(cm, where, config) {
|
24
|
+
var line = cm.getLineHandle(where.line), pos = where.ch - 1;
|
25
|
+
var afterCursor = config && config.afterCursor
|
26
|
+
if (afterCursor == null)
|
27
|
+
afterCursor = /(^| )cm-fat-cursor($| )/.test(cm.getWrapperElement().className)
|
28
|
+
var re = bracketRegex(config)
|
29
|
+
|
30
|
+
// A cursor is defined as between two characters, but in in vim command mode
|
31
|
+
// (i.e. not insert mode), the cursor is visually represented as a
|
32
|
+
// highlighted box on top of the 2nd character. Otherwise, we allow matches
|
33
|
+
// from before or after the cursor.
|
34
|
+
var match = (!afterCursor && pos >= 0 && re.test(line.text.charAt(pos)) && matching[line.text.charAt(pos)]) ||
|
35
|
+
re.test(line.text.charAt(pos + 1)) && matching[line.text.charAt(++pos)];
|
36
|
+
if (!match) return null;
|
37
|
+
var dir = match.charAt(1) == ">" ? 1 : -1;
|
38
|
+
if (config && config.strict && (dir > 0) != (pos == where.ch)) return null;
|
39
|
+
var style = cm.getTokenTypeAt(Pos(where.line, pos + 1));
|
40
|
+
|
41
|
+
var found = scanForBracket(cm, Pos(where.line, pos + (dir > 0 ? 1 : 0)), dir, style || null, config);
|
42
|
+
if (found == null) return null;
|
43
|
+
return {from: Pos(where.line, pos), to: found && found.pos,
|
44
|
+
match: found && found.ch == match.charAt(0), forward: dir > 0};
|
45
|
+
}
|
46
|
+
|
47
|
+
// bracketRegex is used to specify which type of bracket to scan
|
48
|
+
// should be a regexp, e.g. /[[\]]/
|
49
|
+
//
|
50
|
+
// Note: If "where" is on an open bracket, then this bracket is ignored.
|
51
|
+
//
|
52
|
+
// Returns false when no bracket was found, null when it reached
|
53
|
+
// maxScanLines and gave up
|
54
|
+
function scanForBracket(cm, where, dir, style, config) {
|
55
|
+
var maxScanLen = (config && config.maxScanLineLength) || 10000;
|
56
|
+
var maxScanLines = (config && config.maxScanLines) || 1000;
|
57
|
+
|
58
|
+
var stack = [];
|
59
|
+
var re = bracketRegex(config)
|
60
|
+
var lineEnd = dir > 0 ? Math.min(where.line + maxScanLines, cm.lastLine() + 1)
|
61
|
+
: Math.max(cm.firstLine() - 1, where.line - maxScanLines);
|
62
|
+
for (var lineNo = where.line; lineNo != lineEnd; lineNo += dir) {
|
63
|
+
var line = cm.getLine(lineNo);
|
64
|
+
if (!line) continue;
|
65
|
+
var pos = dir > 0 ? 0 : line.length - 1, end = dir > 0 ? line.length : -1;
|
66
|
+
if (line.length > maxScanLen) continue;
|
67
|
+
if (lineNo == where.line) pos = where.ch - (dir < 0 ? 1 : 0);
|
68
|
+
for (; pos != end; pos += dir) {
|
69
|
+
var ch = line.charAt(pos);
|
70
|
+
if (re.test(ch) && (style === undefined || cm.getTokenTypeAt(Pos(lineNo, pos + 1)) == style)) {
|
71
|
+
var match = matching[ch];
|
72
|
+
if (match && (match.charAt(1) == ">") == (dir > 0)) stack.push(ch);
|
73
|
+
else if (!stack.length) return {pos: Pos(lineNo, pos), ch: ch};
|
74
|
+
else stack.pop();
|
75
|
+
}
|
76
|
+
}
|
77
|
+
}
|
78
|
+
return lineNo - dir == (dir > 0 ? cm.lastLine() : cm.firstLine()) ? false : null;
|
79
|
+
}
|
80
|
+
|
81
|
+
function matchBrackets(cm, autoclear, config) {
|
82
|
+
// Disable brace matching in long lines, since it'll cause hugely slow updates
|
83
|
+
var maxHighlightLen = cm.state.matchBrackets.maxHighlightLineLength || 1000;
|
84
|
+
var marks = [], ranges = cm.listSelections();
|
85
|
+
for (var i = 0; i < ranges.length; i++) {
|
86
|
+
var match = ranges[i].empty() && findMatchingBracket(cm, ranges[i].head, config);
|
87
|
+
if (match && cm.getLine(match.from.line).length <= maxHighlightLen) {
|
88
|
+
var style = match.match ? "CodeMirror-matchingbracket" : "CodeMirror-nonmatchingbracket";
|
89
|
+
marks.push(cm.markText(match.from, Pos(match.from.line, match.from.ch + 1), {className: style}));
|
90
|
+
if (match.to && cm.getLine(match.to.line).length <= maxHighlightLen)
|
91
|
+
marks.push(cm.markText(match.to, Pos(match.to.line, match.to.ch + 1), {className: style}));
|
92
|
+
}
|
93
|
+
}
|
94
|
+
|
95
|
+
if (marks.length) {
|
96
|
+
// Kludge to work around the IE bug from issue #1193, where text
|
97
|
+
// input stops going to the textare whever this fires.
|
98
|
+
if (ie_lt8 && cm.state.focused) cm.focus();
|
99
|
+
|
100
|
+
var clear = function() {
|
101
|
+
cm.operation(function() {
|
102
|
+
for (var i = 0; i < marks.length; i++) marks[i].clear();
|
103
|
+
});
|
104
|
+
};
|
105
|
+
if (autoclear) setTimeout(clear, 800);
|
106
|
+
else return clear;
|
107
|
+
}
|
108
|
+
}
|
109
|
+
|
110
|
+
function doMatchBrackets(cm) {
|
111
|
+
cm.operation(function() {
|
112
|
+
if (cm.state.matchBrackets.currentlyHighlighted) {
|
113
|
+
cm.state.matchBrackets.currentlyHighlighted();
|
114
|
+
cm.state.matchBrackets.currentlyHighlighted = null;
|
115
|
+
}
|
116
|
+
cm.state.matchBrackets.currentlyHighlighted = matchBrackets(cm, false, cm.state.matchBrackets);
|
117
|
+
});
|
118
|
+
}
|
119
|
+
|
120
|
+
CodeMirror.defineOption("matchBrackets", false, function(cm, val, old) {
|
121
|
+
function clear(cm) {
|
122
|
+
if (cm.state.matchBrackets && cm.state.matchBrackets.currentlyHighlighted) {
|
123
|
+
cm.state.matchBrackets.currentlyHighlighted();
|
124
|
+
cm.state.matchBrackets.currentlyHighlighted = null;
|
125
|
+
}
|
126
|
+
}
|
127
|
+
|
128
|
+
if (old && old != CodeMirror.Init) {
|
129
|
+
cm.off("cursorActivity", doMatchBrackets);
|
130
|
+
cm.off("focus", doMatchBrackets)
|
131
|
+
cm.off("blur", clear)
|
132
|
+
clear(cm);
|
133
|
+
}
|
134
|
+
if (val) {
|
135
|
+
cm.state.matchBrackets = typeof val == "object" ? val : {};
|
136
|
+
cm.on("cursorActivity", doMatchBrackets);
|
137
|
+
cm.on("focus", doMatchBrackets)
|
138
|
+
cm.on("blur", clear)
|
139
|
+
}
|
140
|
+
});
|
141
|
+
|
142
|
+
CodeMirror.defineExtension("matchBrackets", function() {matchBrackets(this, true);});
|
143
|
+
CodeMirror.defineExtension("findMatchingBracket", function(pos, config, oldConfig){
|
144
|
+
// Backwards-compatibility kludge
|
145
|
+
if (oldConfig || typeof config == "boolean") {
|
146
|
+
if (!oldConfig) {
|
147
|
+
config = config ? {strict: true} : null
|
148
|
+
} else {
|
149
|
+
oldConfig.strict = config
|
150
|
+
config = oldConfig
|
151
|
+
}
|
152
|
+
}
|
153
|
+
return findMatchingBracket(this, pos, config)
|
154
|
+
});
|
155
|
+
CodeMirror.defineExtension("scanForBracket", function(pos, dir, style, config){
|
156
|
+
return scanForBracket(this, pos, dir, style, config);
|
157
|
+
});
|
158
|
+
});
|
@@ -0,0 +1,298 @@
|
|
1
|
+
// CodeMirror, copyright (c) by Marijn Haverbeke and others
|
2
|
+
// Distributed under an MIT license: https://codemirror.net/LICENSE
|
3
|
+
|
4
|
+
(function(mod) {
|
5
|
+
if (typeof exports == "object" && typeof module == "object") // CommonJS
|
6
|
+
mod(require("../../lib/codemirror"));
|
7
|
+
else if (typeof define == "function" && define.amd) // AMD
|
8
|
+
define(["../../lib/codemirror"], mod);
|
9
|
+
else // Plain browser env
|
10
|
+
mod(CodeMirror);
|
11
|
+
})(function(CodeMirror) {
|
12
|
+
"use strict";
|
13
|
+
|
14
|
+
CodeMirror.defineMode("ruby", function(config) {
|
15
|
+
function wordObj(words) {
|
16
|
+
var o = {};
|
17
|
+
for (var i = 0, e = words.length; i < e; ++i) o[words[i]] = true;
|
18
|
+
return o;
|
19
|
+
}
|
20
|
+
var keywords = wordObj([
|
21
|
+
"alias", "and", "BEGIN", "begin", "break", "case", "class", "def", "defined?", "do", "else",
|
22
|
+
"elsif", "END", "end", "ensure", "false", "for", "if", "in", "module", "next", "not", "or",
|
23
|
+
"redo", "rescue", "retry", "return", "self", "super", "then", "true", "undef", "unless",
|
24
|
+
"until", "when", "while", "yield", "nil", "raise", "throw", "catch", "fail", "loop", "callcc",
|
25
|
+
"caller", "lambda", "proc", "public", "protected", "private", "require", "load",
|
26
|
+
"require_relative", "extend", "autoload", "__END__", "__FILE__", "__LINE__", "__dir__"
|
27
|
+
]);
|
28
|
+
var indentWords = wordObj(["def", "class", "case", "for", "while", "until", "module", "then",
|
29
|
+
"catch", "loop", "proc", "begin"]);
|
30
|
+
var dedentWords = wordObj(["end", "until"]);
|
31
|
+
var opening = {"[": "]", "{": "}", "(": ")"};
|
32
|
+
var closing = {"]": "[", "}": "{", ")": "("};
|
33
|
+
var curPunc;
|
34
|
+
|
35
|
+
function chain(newtok, stream, state) {
|
36
|
+
state.tokenize.push(newtok);
|
37
|
+
return newtok(stream, state);
|
38
|
+
}
|
39
|
+
|
40
|
+
function tokenBase(stream, state) {
|
41
|
+
if (stream.sol() && stream.match("=begin") && stream.eol()) {
|
42
|
+
state.tokenize.push(readBlockComment);
|
43
|
+
return "comment";
|
44
|
+
}
|
45
|
+
if (stream.eatSpace()) return null;
|
46
|
+
var ch = stream.next(), m;
|
47
|
+
if (ch == "`" || ch == "'" || ch == '"') {
|
48
|
+
return chain(readQuoted(ch, "string", ch == '"' || ch == "`"), stream, state);
|
49
|
+
} else if (ch == "/") {
|
50
|
+
if (regexpAhead(stream))
|
51
|
+
return chain(readQuoted(ch, "string-2", true), stream, state);
|
52
|
+
else
|
53
|
+
return "operator";
|
54
|
+
} else if (ch == "%") {
|
55
|
+
var style = "string", embed = true;
|
56
|
+
if (stream.eat("s")) style = "atom";
|
57
|
+
else if (stream.eat(/[WQ]/)) style = "string";
|
58
|
+
else if (stream.eat(/[r]/)) style = "string-2";
|
59
|
+
else if (stream.eat(/[wxq]/)) { style = "string"; embed = false; }
|
60
|
+
var delim = stream.eat(/[^\w\s=]/);
|
61
|
+
if (!delim) return "operator";
|
62
|
+
if (opening.propertyIsEnumerable(delim)) delim = opening[delim];
|
63
|
+
return chain(readQuoted(delim, style, embed, true), stream, state);
|
64
|
+
} else if (ch == "#") {
|
65
|
+
stream.skipToEnd();
|
66
|
+
return "comment";
|
67
|
+
} else if (ch == "<" && (m = stream.match(/^<([-~])[\`\"\']?([a-zA-Z_?]\w*)[\`\"\']?(?:;|$)/))) {
|
68
|
+
return chain(readHereDoc(m[2], m[1]), stream, state);
|
69
|
+
} else if (ch == "0") {
|
70
|
+
if (stream.eat("x")) stream.eatWhile(/[\da-fA-F]/);
|
71
|
+
else if (stream.eat("b")) stream.eatWhile(/[01]/);
|
72
|
+
else stream.eatWhile(/[0-7]/);
|
73
|
+
return "number";
|
74
|
+
} else if (/\d/.test(ch)) {
|
75
|
+
stream.match(/^[\d_]*(?:\.[\d_]+)?(?:[eE][+\-]?[\d_]+)?/);
|
76
|
+
return "number";
|
77
|
+
} else if (ch == "?") {
|
78
|
+
while (stream.match(/^\\[CM]-/)) {}
|
79
|
+
if (stream.eat("\\")) stream.eatWhile(/\w/);
|
80
|
+
else stream.next();
|
81
|
+
return "string";
|
82
|
+
} else if (ch == ":") {
|
83
|
+
if (stream.eat("'")) return chain(readQuoted("'", "atom", false), stream, state);
|
84
|
+
if (stream.eat('"')) return chain(readQuoted('"', "atom", true), stream, state);
|
85
|
+
|
86
|
+
// :> :>> :< :<< are valid symbols
|
87
|
+
if (stream.eat(/[\<\>]/)) {
|
88
|
+
stream.eat(/[\<\>]/);
|
89
|
+
return "atom";
|
90
|
+
}
|
91
|
+
|
92
|
+
// :+ :- :/ :* :| :& :! are valid symbols
|
93
|
+
if (stream.eat(/[\+\-\*\/\&\|\:\!]/)) {
|
94
|
+
return "atom";
|
95
|
+
}
|
96
|
+
|
97
|
+
// Symbols can't start by a digit
|
98
|
+
if (stream.eat(/[a-zA-Z$@_\xa1-\uffff]/)) {
|
99
|
+
stream.eatWhile(/[\w$\xa1-\uffff]/);
|
100
|
+
// Only one ? ! = is allowed and only as the last character
|
101
|
+
stream.eat(/[\?\!\=]/);
|
102
|
+
return "atom";
|
103
|
+
}
|
104
|
+
return "operator";
|
105
|
+
} else if (ch == "@" && stream.match(/^@?[a-zA-Z_\xa1-\uffff]/)) {
|
106
|
+
stream.eat("@");
|
107
|
+
stream.eatWhile(/[\w\xa1-\uffff]/);
|
108
|
+
return "variable-2";
|
109
|
+
} else if (ch == "$") {
|
110
|
+
if (stream.eat(/[a-zA-Z_]/)) {
|
111
|
+
stream.eatWhile(/[\w]/);
|
112
|
+
} else if (stream.eat(/\d/)) {
|
113
|
+
stream.eat(/\d/);
|
114
|
+
} else {
|
115
|
+
stream.next(); // Must be a special global like $: or $!
|
116
|
+
}
|
117
|
+
return "variable-3";
|
118
|
+
} else if (/[a-zA-Z_\xa1-\uffff]/.test(ch)) {
|
119
|
+
stream.eatWhile(/[\w\xa1-\uffff]/);
|
120
|
+
stream.eat(/[\?\!]/);
|
121
|
+
if (stream.eat(":")) return "atom";
|
122
|
+
return "ident";
|
123
|
+
} else if (ch == "|" && (state.varList || state.lastTok == "{" || state.lastTok == "do")) {
|
124
|
+
curPunc = "|";
|
125
|
+
return null;
|
126
|
+
} else if (/[\(\)\[\]{}\\;]/.test(ch)) {
|
127
|
+
curPunc = ch;
|
128
|
+
return null;
|
129
|
+
} else if (ch == "-" && stream.eat(">")) {
|
130
|
+
return "arrow";
|
131
|
+
} else if (/[=+\-\/*:\.^%<>~|]/.test(ch)) {
|
132
|
+
var more = stream.eatWhile(/[=+\-\/*:\.^%<>~|]/);
|
133
|
+
if (ch == "." && !more) curPunc = ".";
|
134
|
+
return "operator";
|
135
|
+
} else {
|
136
|
+
return null;
|
137
|
+
}
|
138
|
+
}
|
139
|
+
|
140
|
+
function regexpAhead(stream) {
|
141
|
+
var start = stream.pos, depth = 0, next, found = false, escaped = false
|
142
|
+
while ((next = stream.next()) != null) {
|
143
|
+
if (!escaped) {
|
144
|
+
if ("[{(".indexOf(next) > -1) {
|
145
|
+
depth++
|
146
|
+
} else if ("]})".indexOf(next) > -1) {
|
147
|
+
depth--
|
148
|
+
if (depth < 0) break
|
149
|
+
} else if (next == "/" && depth == 0) {
|
150
|
+
found = true
|
151
|
+
break
|
152
|
+
}
|
153
|
+
escaped = next == "\\"
|
154
|
+
} else {
|
155
|
+
escaped = false
|
156
|
+
}
|
157
|
+
}
|
158
|
+
stream.backUp(stream.pos - start)
|
159
|
+
return found
|
160
|
+
}
|
161
|
+
|
162
|
+
function tokenBaseUntilBrace(depth) {
|
163
|
+
if (!depth) depth = 1;
|
164
|
+
return function(stream, state) {
|
165
|
+
if (stream.peek() == "}") {
|
166
|
+
if (depth == 1) {
|
167
|
+
state.tokenize.pop();
|
168
|
+
return state.tokenize[state.tokenize.length-1](stream, state);
|
169
|
+
} else {
|
170
|
+
state.tokenize[state.tokenize.length - 1] = tokenBaseUntilBrace(depth - 1);
|
171
|
+
}
|
172
|
+
} else if (stream.peek() == "{") {
|
173
|
+
state.tokenize[state.tokenize.length - 1] = tokenBaseUntilBrace(depth + 1);
|
174
|
+
}
|
175
|
+
return tokenBase(stream, state);
|
176
|
+
};
|
177
|
+
}
|
178
|
+
function tokenBaseOnce() {
|
179
|
+
var alreadyCalled = false;
|
180
|
+
return function(stream, state) {
|
181
|
+
if (alreadyCalled) {
|
182
|
+
state.tokenize.pop();
|
183
|
+
return state.tokenize[state.tokenize.length-1](stream, state);
|
184
|
+
}
|
185
|
+
alreadyCalled = true;
|
186
|
+
return tokenBase(stream, state);
|
187
|
+
};
|
188
|
+
}
|
189
|
+
function readQuoted(quote, style, embed, unescaped) {
|
190
|
+
return function(stream, state) {
|
191
|
+
var escaped = false, ch;
|
192
|
+
|
193
|
+
if (state.context.type === 'read-quoted-paused') {
|
194
|
+
state.context = state.context.prev;
|
195
|
+
stream.eat("}");
|
196
|
+
}
|
197
|
+
|
198
|
+
while ((ch = stream.next()) != null) {
|
199
|
+
if (ch == quote && (unescaped || !escaped)) {
|
200
|
+
state.tokenize.pop();
|
201
|
+
break;
|
202
|
+
}
|
203
|
+
if (embed && ch == "#" && !escaped) {
|
204
|
+
if (stream.eat("{")) {
|
205
|
+
if (quote == "}") {
|
206
|
+
state.context = {prev: state.context, type: 'read-quoted-paused'};
|
207
|
+
}
|
208
|
+
state.tokenize.push(tokenBaseUntilBrace());
|
209
|
+
break;
|
210
|
+
} else if (/[@\$]/.test(stream.peek())) {
|
211
|
+
state.tokenize.push(tokenBaseOnce());
|
212
|
+
break;
|
213
|
+
}
|
214
|
+
}
|
215
|
+
escaped = !escaped && ch == "\\";
|
216
|
+
}
|
217
|
+
return style;
|
218
|
+
};
|
219
|
+
}
|
220
|
+
function readHereDoc(phrase, mayIndent) {
|
221
|
+
return function(stream, state) {
|
222
|
+
if (mayIndent) stream.eatSpace()
|
223
|
+
if (stream.match(phrase)) state.tokenize.pop();
|
224
|
+
else stream.skipToEnd();
|
225
|
+
return "string";
|
226
|
+
};
|
227
|
+
}
|
228
|
+
function readBlockComment(stream, state) {
|
229
|
+
if (stream.sol() && stream.match("=end") && stream.eol())
|
230
|
+
state.tokenize.pop();
|
231
|
+
stream.skipToEnd();
|
232
|
+
return "comment";
|
233
|
+
}
|
234
|
+
|
235
|
+
return {
|
236
|
+
startState: function() {
|
237
|
+
return {tokenize: [tokenBase],
|
238
|
+
indented: 0,
|
239
|
+
context: {type: "top", indented: -config.indentUnit},
|
240
|
+
continuedLine: false,
|
241
|
+
lastTok: null,
|
242
|
+
varList: false};
|
243
|
+
},
|
244
|
+
|
245
|
+
token: function(stream, state) {
|
246
|
+
curPunc = null;
|
247
|
+
if (stream.sol()) state.indented = stream.indentation();
|
248
|
+
var style = state.tokenize[state.tokenize.length-1](stream, state), kwtype;
|
249
|
+
var thisTok = curPunc;
|
250
|
+
if (style == "ident") {
|
251
|
+
var word = stream.current();
|
252
|
+
style = state.lastTok == "." ? "property"
|
253
|
+
: keywords.propertyIsEnumerable(stream.current()) ? "keyword"
|
254
|
+
: /^[A-Z]/.test(word) ? "tag"
|
255
|
+
: (state.lastTok == "def" || state.lastTok == "class" || state.varList) ? "def"
|
256
|
+
: "variable";
|
257
|
+
if (style == "keyword") {
|
258
|
+
thisTok = word;
|
259
|
+
if (indentWords.propertyIsEnumerable(word)) kwtype = "indent";
|
260
|
+
else if (dedentWords.propertyIsEnumerable(word)) kwtype = "dedent";
|
261
|
+
else if ((word == "if" || word == "unless") && stream.column() == stream.indentation())
|
262
|
+
kwtype = "indent";
|
263
|
+
else if (word == "do" && state.context.indented < state.indented)
|
264
|
+
kwtype = "indent";
|
265
|
+
}
|
266
|
+
}
|
267
|
+
if (curPunc || (style && style != "comment")) state.lastTok = thisTok;
|
268
|
+
if (curPunc == "|") state.varList = !state.varList;
|
269
|
+
|
270
|
+
if (kwtype == "indent" || /[\(\[\{]/.test(curPunc))
|
271
|
+
state.context = {prev: state.context, type: curPunc || style, indented: state.indented};
|
272
|
+
else if ((kwtype == "dedent" || /[\)\]\}]/.test(curPunc)) && state.context.prev)
|
273
|
+
state.context = state.context.prev;
|
274
|
+
|
275
|
+
if (stream.eol())
|
276
|
+
state.continuedLine = (curPunc == "\\" || style == "operator");
|
277
|
+
return style;
|
278
|
+
},
|
279
|
+
|
280
|
+
indent: function(state, textAfter) {
|
281
|
+
if (state.tokenize[state.tokenize.length-1] != tokenBase) return CodeMirror.Pass;
|
282
|
+
var firstChar = textAfter && textAfter.charAt(0);
|
283
|
+
var ct = state.context;
|
284
|
+
var closed = ct.type == closing[firstChar] ||
|
285
|
+
ct.type == "keyword" && /^(?:end|until|else|elsif|when|rescue)\b/.test(textAfter);
|
286
|
+
return ct.indented + (closed ? 0 : config.indentUnit) +
|
287
|
+
(state.continuedLine ? config.indentUnit : 0);
|
288
|
+
},
|
289
|
+
|
290
|
+
electricInput: /^\s*(?:end|rescue|elsif|else|\})$/,
|
291
|
+
lineComment: "#",
|
292
|
+
fold: "indent"
|
293
|
+
};
|
294
|
+
});
|
295
|
+
|
296
|
+
CodeMirror.defineMIME("text/x-ruby", "ruby");
|
297
|
+
|
298
|
+
});
|