0
|
1 // CodeMirror, copyright (c) by Marijn Haverbeke and others
|
|
2 // Distributed under an MIT license: https://codemirror.net/5/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('shell', function() {
|
|
15
|
|
16 var words = {};
|
|
17 function define(style, dict) {
|
|
18 for(var i = 0; i < dict.length; i++) {
|
|
19 words[dict[i]] = style;
|
|
20 }
|
|
21 };
|
|
22
|
|
23 var commonAtoms = ["true", "false"];
|
|
24 var commonKeywords = ["if", "then", "do", "else", "elif", "while", "until", "for", "in", "esac", "fi",
|
|
25 "fin", "fil", "done", "exit", "set", "unset", "export", "function"];
|
|
26 var commonCommands = ["ab", "awk", "bash", "beep", "cat", "cc", "cd", "chown", "chmod", "chroot", "clear",
|
|
27 "cp", "curl", "cut", "diff", "echo", "find", "gawk", "gcc", "get", "git", "grep", "hg", "kill", "killall",
|
|
28 "ln", "ls", "make", "mkdir", "openssl", "mv", "nc", "nl", "node", "npm", "ping", "ps", "restart", "rm",
|
|
29 "rmdir", "sed", "service", "sh", "shopt", "shred", "source", "sort", "sleep", "ssh", "start", "stop",
|
|
30 "su", "sudo", "svn", "tee", "telnet", "top", "touch", "vi", "vim", "wall", "wc", "wget", "who", "write",
|
|
31 "yes", "zsh"];
|
|
32
|
|
33 CodeMirror.registerHelper("hintWords", "shell", commonAtoms.concat(commonKeywords, commonCommands));
|
|
34
|
|
35 define('atom', commonAtoms);
|
|
36 define('keyword', commonKeywords);
|
|
37 define('builtin', commonCommands);
|
|
38
|
|
39 function tokenBase(stream, state) {
|
|
40 if (stream.eatSpace()) return null;
|
|
41
|
|
42 var sol = stream.sol();
|
|
43 var ch = stream.next();
|
|
44
|
|
45 if (ch === '\\') {
|
|
46 stream.next();
|
|
47 return null;
|
|
48 }
|
|
49 if (ch === '\'' || ch === '"' || ch === '`') {
|
|
50 state.tokens.unshift(tokenString(ch, ch === "`" ? "quote" : "string"));
|
|
51 return tokenize(stream, state);
|
|
52 }
|
|
53 if (ch === '#') {
|
|
54 if (sol && stream.eat('!')) {
|
|
55 stream.skipToEnd();
|
|
56 return 'meta'; // 'comment'?
|
|
57 }
|
|
58 stream.skipToEnd();
|
|
59 return 'comment';
|
|
60 }
|
|
61 if (ch === '$') {
|
|
62 state.tokens.unshift(tokenDollar);
|
|
63 return tokenize(stream, state);
|
|
64 }
|
|
65 if (ch === '+' || ch === '=') {
|
|
66 return 'operator';
|
|
67 }
|
|
68 if (ch === '-') {
|
|
69 stream.eat('-');
|
|
70 stream.eatWhile(/\w/);
|
|
71 return 'attribute';
|
|
72 }
|
|
73 if (ch == "<") {
|
|
74 if (stream.match("<<")) return "operator"
|
|
75 var heredoc = stream.match(/^<-?\s*['"]?([^'"]*)['"]?/)
|
|
76 if (heredoc) {
|
|
77 state.tokens.unshift(tokenHeredoc(heredoc[1]))
|
|
78 return 'string-2'
|
|
79 }
|
|
80 }
|
|
81 if (/\d/.test(ch)) {
|
|
82 stream.eatWhile(/\d/);
|
|
83 if(stream.eol() || !/\w/.test(stream.peek())) {
|
|
84 return 'number';
|
|
85 }
|
|
86 }
|
|
87 stream.eatWhile(/[\w-]/);
|
|
88 var cur = stream.current();
|
|
89 if (stream.peek() === '=' && /\w+/.test(cur)) return 'def';
|
|
90 return words.hasOwnProperty(cur) ? words[cur] : null;
|
|
91 }
|
|
92
|
|
93 function tokenString(quote, style) {
|
|
94 var close = quote == "(" ? ")" : quote == "{" ? "}" : quote
|
|
95 return function(stream, state) {
|
|
96 var next, escaped = false;
|
|
97 while ((next = stream.next()) != null) {
|
|
98 if (next === close && !escaped) {
|
|
99 state.tokens.shift();
|
|
100 break;
|
|
101 } else if (next === '$' && !escaped && quote !== "'" && stream.peek() != close) {
|
|
102 escaped = true;
|
|
103 stream.backUp(1);
|
|
104 state.tokens.unshift(tokenDollar);
|
|
105 break;
|
|
106 } else if (!escaped && quote !== close && next === quote) {
|
|
107 state.tokens.unshift(tokenString(quote, style))
|
|
108 return tokenize(stream, state)
|
|
109 } else if (!escaped && /['"]/.test(next) && !/['"]/.test(quote)) {
|
|
110 state.tokens.unshift(tokenStringStart(next, "string"));
|
|
111 stream.backUp(1);
|
|
112 break;
|
|
113 }
|
|
114 escaped = !escaped && next === '\\';
|
|
115 }
|
|
116 return style;
|
|
117 };
|
|
118 };
|
|
119
|
|
120 function tokenStringStart(quote, style) {
|
|
121 return function(stream, state) {
|
|
122 state.tokens[0] = tokenString(quote, style)
|
|
123 stream.next()
|
|
124 return tokenize(stream, state)
|
|
125 }
|
|
126 }
|
|
127
|
|
128 var tokenDollar = function(stream, state) {
|
|
129 if (state.tokens.length > 1) stream.eat('$');
|
|
130 var ch = stream.next()
|
|
131 if (/['"({]/.test(ch)) {
|
|
132 state.tokens[0] = tokenString(ch, ch == "(" ? "quote" : ch == "{" ? "def" : "string");
|
|
133 return tokenize(stream, state);
|
|
134 }
|
|
135 if (!/\d/.test(ch)) stream.eatWhile(/\w/);
|
|
136 state.tokens.shift();
|
|
137 return 'def';
|
|
138 };
|
|
139
|
|
140 function tokenHeredoc(delim) {
|
|
141 return function(stream, state) {
|
|
142 if (stream.sol() && stream.string == delim) state.tokens.shift()
|
|
143 stream.skipToEnd()
|
|
144 return "string-2"
|
|
145 }
|
|
146 }
|
|
147
|
|
148 function tokenize(stream, state) {
|
|
149 return (state.tokens[0] || tokenBase) (stream, state);
|
|
150 };
|
|
151
|
|
152 return {
|
|
153 startState: function() {return {tokens:[]};},
|
|
154 token: function(stream, state) {
|
|
155 return tokenize(stream, state);
|
|
156 },
|
|
157 closeBrackets: "()[]{}''\"\"``",
|
|
158 lineComment: '#',
|
|
159 fold: "brace"
|
|
160 };
|
|
161 });
|
|
162
|
|
163 CodeMirror.defineMIME('text/x-sh', 'shell');
|
|
164 // Apache uses a slightly different Media Type for Shell scripts
|
|
165 // http://svn.apache.org/repos/asf/httpd/httpd/trunk/docs/conf/mime.types
|
|
166 CodeMirror.defineMIME('application/x-sh', 'shell');
|
|
167
|
|
168 });
|