##// END OF EJS Templates
CodeMirror code editing added....
Brian Granger -
Show More
@@ -0,0 +1,19 b''
1 Copyright (C) 2011 by Marijn Haverbeke <marijnh@gmail.com>
2
3 Permission is hereby granted, free of charge, to any person obtaining a copy
4 of this software and associated documentation files (the "Software"), to deal
5 in the Software without restriction, including without limitation the rights
6 to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
7 copies of the Software, and to permit persons to whom the Software is
8 furnished to do so, subject to the following conditions:
9
10 The above copyright notice and this permission notice shall be included in
11 all copies or substantial portions of the Software.
12
13 THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
14 IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
15 FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
16 AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
17 LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
18 OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
19 THE SOFTWARE.
@@ -0,0 +1,58 b''
1 .CodeMirror {
2 overflow: hidden; /* Changed from auto to remove scrollbar */
3 height: auto; /* Changed to auto to autogrow */
4 /* line-height: 1em;*/
5 font-family: monospace;
6 }
7
8 .CodeMirror-gutter {
9 position: absolute; left: 0; top: 0;
10 background-color: #f7f7f7;
11 border-right: 1px solid #eee;
12 min-width: 2em;
13 height: 100%;
14 }
15 .CodeMirror-gutter-text {
16 color: #aaa;
17 text-align: right;
18 padding: .4em .2em .4em .4em;
19 }
20 .CodeMirror-lines {
21 /* Changed from 0.4em, but this gives us jitters upon typing, but I found
22 that removing .CodeMirror line-height: 1em above, it goes away. */
23 padding: 0em;
24 }
25
26 .CodeMirror pre {
27 -moz-border-radius: 0;
28 -webkit-border-radius: 0;
29 -o-border-radius: 0;
30 border-radius: 0;
31 border-width: 0; margin: 0; padding: 0; background: transparent;
32 font-family: inherit;
33 }
34
35 .CodeMirror textarea {
36 font-family: monospace !important;
37 }
38
39 .CodeMirror-cursor {
40 z-index: 10;
41 position: absolute;
42 visibility: hidden;
43 border-left: 1px solid black !important;
44 }
45 .CodeMirror-focused .CodeMirror-cursor {
46 visibility: visible;
47 }
48
49 span.CodeMirror-selected {
50 background: #ccc !important;
51 color: HighlightText !important;
52 }
53 .CodeMirror-focused span.CodeMirror-selected {
54 background: Highlight !important;
55 }
56
57 .CodeMirror-matchingbracket {color: #0f0 !important;}
58 .CodeMirror-nonmatchingbracket {color: #f22 !important;}
This diff has been collapsed as it changes many lines, (1985 lines changed) Show them Hide them
@@ -0,0 +1,1985 b''
1 // All functions that need access to the editor's state live inside
2 // the CodeMirror function. Below that, at the bottom of the file,
3 // some utilities are defined.
4
5 // CodeMirror is the only global var we claim
6 var CodeMirror = (function() {
7 // This is the function that produces an editor instance. It's
8 // closure is used to store the editor state.
9 function CodeMirror(place, givenOptions) {
10 // Determine effective options based on given values and defaults.
11 var options = {}, defaults = CodeMirror.defaults;
12 for (var opt in defaults)
13 if (defaults.hasOwnProperty(opt))
14 options[opt] = (givenOptions && givenOptions.hasOwnProperty(opt) ? givenOptions : defaults)[opt];
15
16 var targetDocument = options["document"];
17 // The element in which the editor lives. Takes care of scrolling
18 // (if enabled).
19 var wrapper = targetDocument.createElement("div");
20 wrapper.className = "CodeMirror";
21 // Work around http://www.quirksmode.org/bugreports/archives/2006/09/Overflow_Hidden_not_hiding.html
22 if (window.ActiveXObject && /MSIE [1-7]\b/.test(navigator.userAgent))
23 wrapper.style.position = "relative";
24 // This mess creates the base DOM structure for the editor.
25 wrapper.innerHTML =
26 '<div style="position: relative">' + // Set to the height of the text, causes scrolling
27 '<div style="position: absolute; height: 0; width: 0; overflow: hidden;"></div>' +
28 '<div style="position: relative">' + // Moved around its parent to cover visible view
29 '<div class="CodeMirror-gutter"><div class="CodeMirror-gutter-text"></div></div>' +
30 '<div style="overflow: hidden; position: absolute; width: 1px; height: 0; left: 0">' + // Wraps and hides input textarea
31 '<textarea style="position: absolute; width: 100000px;" wrap="off"></textarea></div>' +
32 // Provides positioning relative to (visible) text origin
33 '<div class="CodeMirror-lines"><div style="position: relative">' +
34 '<pre class="CodeMirror-cursor">&#160;</pre>' + // Absolutely positioned blinky cursor
35 '<div></div></div></div></div></div>'; // This DIV contains the actual code
36 if (place.appendChild) place.appendChild(wrapper); else place(wrapper);
37 // I've never seen more elegant code in my life.
38 var code = wrapper.firstChild, measure = code.firstChild, mover = measure.nextSibling,
39 gutter = mover.firstChild, gutterText = gutter.firstChild,
40 inputDiv = gutter.nextSibling, input = inputDiv.firstChild,
41 lineSpace = inputDiv.nextSibling.firstChild, cursor = lineSpace.firstChild, lineDiv = cursor.nextSibling;
42 if (options.tabindex != null) input.tabindex = options.tabindex;
43 if (!options.gutter && !options.lineNumbers) gutter.style.display = "none";
44
45 // Delayed object wrap timeouts, making sure only one is active. blinker holds an interval.
46 var poll = new Delayed(), highlight = new Delayed(), blinker;
47
48 // mode holds a mode API object. lines an array of Line objects
49 // (see Line constructor), work an array of lines that should be
50 // parsed, and history the undo history (instance of History
51 // constructor).
52 var mode, lines = [new Line("")], work, history = new History(), focused;
53 loadMode();
54 // The selection. These are always maintained to point at valid
55 // positions. Inverted is used to remember that the user is
56 // selecting bottom-to-top.
57 var sel = {from: {line: 0, ch: 0}, to: {line: 0, ch: 0}, inverted: false};
58 // Selection-related flags. shiftSelecting obviously tracks
59 // whether the user is holding shift. reducedSelection is a hack
60 // to get around the fact that we can't create inverted
61 // selections. See below.
62 var shiftSelecting, reducedSelection, lastDoubleClick;
63 // Variables used by startOperation/endOperation to track what
64 // happened during the operation.
65 var updateInput, changes, textChanged, selectionChanged, leaveInputAlone;
66 // Current visible range (may be bigger than the view window).
67 var showingFrom = 0, showingTo = 0, lastHeight = 0, curKeyId = null;
68 // editing will hold an object describing the things we put in the
69 // textarea, to help figure out whether something changed.
70 // bracketHighlighted is used to remember that a backet has been
71 // marked.
72 var editing, bracketHighlighted;
73 // Tracks the maximum line length so that the horizontal scrollbar
74 // can be kept static when scrolling.
75 var maxLine = "";
76
77 // Initialize the content. Somewhat hacky (delayed prepareInput)
78 // to work around browser issues.
79 operation(function(){setValue(options.value || ""); updateInput = false;})();
80 setTimeout(prepareInput, 20);
81
82 // Register our event handlers.
83 connect(wrapper, "mousedown", operation(onMouseDown));
84 // Gecko browsers fire contextmenu *after* opening the menu, at
85 // which point we can't mess with it anymore. Context menu is
86 // handled in onMouseDown for Gecko.
87 if (!gecko) connect(wrapper, "contextmenu", operation(onContextMenu));
88 connect(code, "dblclick", operation(onDblClick));
89 connect(wrapper, "scroll", function() {updateDisplay([]); if (options.onScroll) options.onScroll(instance);});
90 connect(window, "resize", function() {updateDisplay(true);});
91 connect(input, "keyup", operation(onKeyUp));
92 connect(input, "keydown", operation(onKeyDown));
93 connect(input, "keypress", operation(onKeyPress));
94 connect(input, "focus", onFocus);
95 connect(input, "blur", onBlur);
96
97 connect(wrapper, "dragenter", function(e){e.stop();});
98 connect(wrapper, "dragover", function(e){e.stop();});
99 connect(wrapper, "drop", operation(onDrop));
100 connect(wrapper, "paste", function(){input.focus(); fastPoll();});
101 connect(input, "paste", function(){fastPoll();});
102 connect(input, "cut", function(){fastPoll();});
103
104 if (targetDocument.activeElement == input) onFocus();
105 else onBlur();
106
107 function isLine(l) {return l >= 0 && l < lines.length;}
108 // The instance object that we'll return. Mostly calls out to
109 // local functions in the CodeMirror function. Some do some extra
110 // range checking and/or clipping. operation is used to wrap the
111 // call so that changes it makes are tracked, and the display is
112 // updated afterwards.
113 var instance = {
114 getValue: getValue,
115 setValue: operation(setValue),
116 getSelection: getSelection,
117 replaceSelection: operation(replaceSelection),
118 focus: function(){input.focus(); onFocus(); fastPoll();},
119 setOption: function(option, value) {
120 options[option] = value;
121 if (option == "lineNumbers" || option == "gutter") gutterChanged();
122 else if (option == "mode" || option == "indentUnit") loadMode();
123 },
124 getOption: function(option) {return options[option];},
125 undo: operation(undo),
126 redo: operation(redo),
127 indentLine: operation(function(n) {if (isLine(n)) indentLine(n, "smart");}),
128 historySize: function() {return {undo: history.done.length, redo: history.undone.length};},
129 matchBrackets: operation(function(){matchBrackets(true);}),
130 getTokenAt: function(pos) {
131 pos = clipPos(pos);
132 return lines[pos.line].getTokenAt(mode, getStateBefore(pos.line), pos.ch);
133 },
134 cursorCoords: function(start){
135 if (start == null) start = sel.inverted;
136 return pageCoords(start ? sel.from : sel.to);
137 },
138 charCoords: function(pos){return pageCoords(clipPos(pos));},
139 coordsChar: function(coords) {
140 var off = eltOffset(lineSpace);
141 var line = clipLine(Math.min(lines.length - 1, showingFrom + Math.floor((coords.y - off.top) / lineHeight())));
142 return clipPos({line: line, ch: charFromX(clipLine(line), coords.x - off.left)});
143 },
144 getSearchCursor: function(query, pos, caseFold) {return new SearchCursor(query, pos, caseFold);},
145 markText: operation(function(a, b, c){return operation(markText(a, b, c));}),
146 setMarker: addGutterMarker,
147 clearMarker: removeGutterMarker,
148 setLineClass: operation(setLineClass),
149 lineInfo: lineInfo,
150 addWidget: function(pos, node, scroll) {
151 var pos = localCoords(clipPos(pos), true);
152 node.style.top = (showingFrom * lineHeight() + pos.yBot + paddingTop()) + "px";
153 node.style.left = (pos.x + paddingLeft()) + "px";
154 code.appendChild(node);
155 if (scroll)
156 scrollIntoView(pos.x, pos.yBot, pos.x + node.offsetWidth, pos.yBot + node.offsetHeight);
157 },
158
159 lineCount: function() {return lines.length;},
160 getCursor: function(start) {
161 if (start == null) start = sel.inverted;
162 return copyPos(start ? sel.from : sel.to);
163 },
164 somethingSelected: function() {return !posEq(sel.from, sel.to);},
165 setCursor: operation(function(line, ch) {
166 if (ch == null && typeof line.line == "number") setCursor(line.line, line.ch);
167 else setCursor(line, ch);
168 }),
169 setSelection: operation(function(from, to) {setSelection(clipPos(from), clipPos(to || from));}),
170 getLine: function(line) {if (isLine(line)) return lines[line].text;},
171 setLine: operation(function(line, text) {
172 if (isLine(line)) replaceRange(text, {line: line, ch: 0}, {line: line, ch: lines[line].text.length});
173 }),
174 removeLine: operation(function(line) {
175 if (isLine(line)) replaceRange("", {line: line, ch: 0}, clipPos({line: line+1, ch: 0}));
176 }),
177 replaceRange: operation(replaceRange),
178 getRange: function(from, to) {return getRange(clipPos(from), clipPos(to));},
179
180 operation: function(f){return operation(f)();},
181 refresh: function(){updateDisplay(true);},
182 getInputField: function(){return input;},
183 getWrapperElement: function(){return wrapper;}
184 };
185
186 function setValue(code) {
187 history = null;
188 var top = {line: 0, ch: 0};
189 updateLines(top, {line: lines.length - 1, ch: lines[lines.length-1].text.length},
190 splitLines(code), top, top);
191 history = new History();
192 }
193 function getValue(code) {
194 var text = [];
195 for (var i = 0, l = lines.length; i < l; ++i)
196 text.push(lines[i].text);
197 return text.join("\n");
198 }
199
200 function onMouseDown(e) {
201 var ld = lastDoubleClick; lastDoubleClick = null;
202 // First, see if this is a click in the gutter
203 for (var n = e.target(); n != wrapper; n = n.parentNode)
204 if (n.parentNode == gutterText) {
205 if (options.onGutterClick)
206 options.onGutterClick(instance, indexOf(gutterText.childNodes, n) + showingFrom);
207 return e.stop();
208 }
209
210 if (gecko && e.button() == 3) onContextMenu(e);
211 if (e.button() != 1) return;
212 // For button 1, if it was clicked inside the editor
213 // (posFromMouse returning non-null), we have to adjust the
214 // selection.
215 var start = posFromMouse(e), last = start, going;
216 if (!start) {if (e.target() == wrapper) e.stop(); return;}
217
218 if (!focused) onFocus();
219 e.stop();
220 if (ld && +new Date - ld < 400) return selectLine(start.line);
221
222 setCursor(start.line, start.ch, true);
223 // And then we have to see if it's a drag event, in which case
224 // the dragged-over text must be selected.
225 function end() {
226 input.focus();
227 updateInput = true;
228 move(); up();
229 }
230 function extend(e) {
231 var cur = posFromMouse(e, true);
232 if (cur && !posEq(cur, last)) {
233 if (!focused) onFocus();
234 last = cur;
235 setSelectionUser(start, cur);
236 updateInput = false;
237 var visible = visibleLines();
238 if (cur.line >= visible.to || cur.line < visible.from)
239 going = setTimeout(operation(function(){extend(e);}), 150);
240 }
241 }
242
243 var move = connect(targetDocument, "mousemove", operation(function(e) {
244 clearTimeout(going);
245 e.stop();
246 extend(e);
247 }), true);
248 var up = connect(targetDocument, "mouseup", operation(function(e) {
249 clearTimeout(going);
250 var cur = posFromMouse(e);
251 if (cur) setSelectionUser(start, cur);
252 e.stop();
253 end();
254 }), true);
255 }
256 function onDblClick(e) {
257 var pos = posFromMouse(e);
258 if (!pos) return;
259 selectWordAt(pos);
260 e.stop();
261 lastDoubleClick = +new Date;
262 }
263 function onDrop(e) {
264 var pos = posFromMouse(e, true), files = e.e.dataTransfer.files;
265 if (!pos || options.readOnly) return;
266 if (files && files.length && window.FileReader && window.File) {
267 var n = files.length, text = Array(n), read = 0;
268 for (var i = 0; i < n; ++i) loadFile(files[i], i);
269 function loadFile(file, i) {
270 var reader = new FileReader;
271 reader.onload = function() {
272 text[i] = reader.result;
273 if (++read == n) replaceRange(text.join(""), clipPos(pos), clipPos(pos));
274 };
275 reader.readAsText(file);
276 }
277 }
278 else {
279 try {
280 var text = e.e.dataTransfer.getData("Text");
281 if (text) replaceRange(text, pos, pos);
282 }
283 catch(e){}
284 }
285 }
286 function onKeyDown(e) {
287 if (!focused) onFocus();
288
289 var code = e.e.keyCode;
290 // Tries to detect ctrl on non-mac, cmd on mac.
291 var mod = (mac ? e.e.metaKey : e.e.ctrlKey) && !e.e.altKey, anyMod = e.e.ctrlKey || e.e.altKey || e.e.metaKey;
292 if (code == 16 || e.e.shiftKey) shiftSelecting = shiftSelecting || (sel.inverted ? sel.to : sel.from);
293 else shiftSelecting = null;
294 // First give onKeyEvent option a chance to handle this.
295 if (options.onKeyEvent && options.onKeyEvent(instance, addStop(e.e))) return;
296
297 if (code == 33 || code == 34) {scrollPage(code == 34); return e.stop();} // page up/down
298 if (mod && ((code == 36 || code == 35) || // ctrl-home/end
299 mac && (code == 38 || code == 40))) { // cmd-up/down
300 scrollEnd(code == 36 || code == 38); return e.stop();
301 }
302 if (mod && code == 65) {selectAll(); return e.stop();} // ctrl-a
303 if (!options.readOnly) {
304 if (!anyMod && code == 13) {return;} // enter
305 if (!anyMod && code == 9 && handleTab(e.e.shiftKey)) return e.stop(); // tab
306 if (mod && code == 90) {undo(); return e.stop();} // ctrl-z
307 if (mod && ((e.e.shiftKey && code == 90) || code == 89)) {redo(); return e.stop();} // ctrl-shift-z, ctrl-y
308 }
309
310 // Key id to use in the movementKeys map. We also pass it to
311 // fastPoll in order to 'self learn'. We need this because
312 // reducedSelection, the hack where we collapse the selection to
313 // its start when it is inverted and a movement key is pressed
314 // (and later restore it again), shouldn't be used for
315 // non-movement keys.
316 curKeyId = (mod ? "c" : "") + code;
317 if (sel.inverted && movementKeys.hasOwnProperty(curKeyId)) {
318 var range = selRange(input);
319 if (range) {
320 reducedSelection = {anchor: range.start};
321 setSelRange(input, range.start, range.start);
322 }
323 }
324 fastPoll(curKeyId);
325 }
326 function onKeyUp(e) {
327 if (reducedSelection) {
328 reducedSelection = null;
329 updateInput = true;
330 }
331 if (e.e.keyCode == 16) shiftSelecting = null;
332 }
333 function onKeyPress(e) {
334 if (options.onKeyEvent && options.onKeyEvent(instance, addStop(e.e))) return;
335 if (options.electricChars && mode.electricChars) {
336 var ch = String.fromCharCode(e.e.charCode == null ? e.e.keyCode : e.e.charCode);
337 if (mode.electricChars.indexOf(ch) > -1)
338 setTimeout(operation(function() {indentLine(sel.to.line, "smart");}), 50);
339 }
340 var code = e.e.keyCode;
341 // Re-stop tab and enter. Necessary on some browsers.
342 if (code == 13) {if (!options.readOnly) handleEnter(); e.stop();}
343 else if (!e.e.ctrlKey && !e.e.altKey && !e.e.metaKey && code == 9 && options.tabMode != "default") e.stop();
344 else fastPoll(curKeyId);
345 }
346
347 function onFocus() {
348 if (!focused && options.onFocus) options.onFocus(instance);
349 focused = true;
350 slowPoll();
351 if (wrapper.className.search(/\bCodeMirror-focused\b/) == -1)
352 wrapper.className += " CodeMirror-focused";
353 restartBlink();
354 }
355 function onBlur() {
356 if (focused && options.onBlur) options.onBlur(instance);
357 clearInterval(blinker);
358 shiftSelecting = null;
359 focused = false;
360 wrapper.className = wrapper.className.replace(" CodeMirror-focused", "");
361 }
362
363 // Replace the range from from to to by the strings in newText.
364 // Afterwards, set the selection to selFrom, selTo.
365 function updateLines(from, to, newText, selFrom, selTo) {
366 if (history) {
367 var old = [];
368 for (var i = from.line, e = to.line + 1; i < e; ++i) old.push(lines[i].text);
369 history.addChange(from.line, newText.length, old);
370 while (history.done.length > options.undoDepth) history.done.shift();
371 }
372 updateLinesNoUndo(from, to, newText, selFrom, selTo);
373 }
374 function unredoHelper(from, to) {
375 var change = from.pop();
376 if (change) {
377 var replaced = [], end = change.start + change.added;
378 for (var i = change.start; i < end; ++i) replaced.push(lines[i].text);
379 to.push({start: change.start, added: change.old.length, old: replaced});
380 var pos = clipPos({line: change.start + change.old.length - 1,
381 ch: editEnd(replaced[replaced.length-1], change.old[change.old.length-1])});
382 updateLinesNoUndo({line: change.start, ch: 0}, {line: end - 1, ch: lines[end-1].text.length}, change.old, pos, pos);
383 }
384 }
385 function undo() {unredoHelper(history.done, history.undone);}
386 function redo() {unredoHelper(history.undone, history.done);}
387
388 function updateLinesNoUndo(from, to, newText, selFrom, selTo) {
389 var recomputeMaxLength = false, maxLineLength = maxLine.length;
390 for (var i = from.line; i < to.line; ++i) {
391 if (lines[i].text.length == maxLineLength) {recomputeMaxLength = true; break;}
392 }
393
394 var nlines = to.line - from.line, firstLine = lines[from.line], lastLine = lines[to.line];
395 // First adjust the line structure, taking some care to leave highlighting intact.
396 if (firstLine == lastLine) {
397 if (newText.length == 1)
398 firstLine.replace(from.ch, to.ch, newText[0]);
399 else {
400 lastLine = firstLine.split(to.ch, newText[newText.length-1]);
401 var spliceargs = [from.line + 1, nlines];
402 firstLine.replace(from.ch, firstLine.text.length, newText[0]);
403 for (var i = 1, e = newText.length - 1; i < e; ++i) spliceargs.push(new Line(newText[i]));
404 spliceargs.push(lastLine);
405 lines.splice.apply(lines, spliceargs);
406 }
407 }
408 else if (newText.length == 1) {
409 firstLine.replace(from.ch, firstLine.text.length, newText[0] + lastLine.text.slice(to.ch));
410 lines.splice(from.line + 1, nlines);
411 }
412 else {
413 var spliceargs = [from.line + 1, nlines - 1];
414 firstLine.replace(from.ch, firstLine.text.length, newText[0]);
415 lastLine.replace(0, to.ch, newText[newText.length-1]);
416 for (var i = 1, e = newText.length - 1; i < e; ++i) spliceargs.push(new Line(newText[i]));
417 lines.splice.apply(lines, spliceargs);
418 }
419
420
421 for (var i = from.line, e = i + newText.length; i < e; ++i) {
422 var l = lines[i].text;
423 if (l.length > maxLineLength) {
424 maxLine = l; maxLineLength = l.length;
425 recomputeMaxLength = false;
426 }
427 }
428 if (recomputeMaxLength) {
429 maxLineLength = 0;
430 for (var i = 0, e = lines.length; i < e; ++i) {
431 var l = lines[i].text;
432 if (l.length > maxLineLength) {
433 maxLineLength = l.length; maxLine = l;
434 }
435 }
436 }
437
438 // Add these lines to the work array, so that they will be
439 // highlighted. Adjust work lines if lines were added/removed.
440 var newWork = [], lendiff = newText.length - nlines - 1;
441 for (var i = 0, l = work.length; i < l; ++i) {
442 var task = work[i];
443 if (task < from.line) newWork.push(task);
444 else if (task > to.line) newWork.push(task + lendiff);
445 }
446 if (newText.length) newWork.push(from.line);
447 work = newWork;
448 startWorker(100);
449 // Remember that these lines changed, for updating the display
450 changes.push({from: from.line, to: to.line + 1, diff: lendiff});
451 textChanged = {from: from, to: to, text: newText};
452
453 // Update the selection
454 function updateLine(n) {return n <= Math.min(to.line, to.line + lendiff) ? n : n + lendiff;}
455 setSelection(selFrom, selTo, updateLine(sel.from.line), updateLine(sel.to.line));
456
457 // Make sure the scroll-size div has the correct height.
458 code.style.height = (lines.length * lineHeight() + 2 * paddingTop()) + "px";
459 }
460
461 function replaceRange(code, from, to) {
462 from = clipPos(from);
463 if (!to) to = from; else to = clipPos(to);
464 code = splitLines(code);
465 function adjustPos(pos) {
466 if (posLess(pos, from)) return pos;
467 if (!posLess(to, pos)) return end;
468 var line = pos.line + code.length - (to.line - from.line) - 1;
469 var ch = pos.ch;
470 if (pos.line == to.line)
471 ch += code[code.length-1].length - (to.ch - (to.line == from.line ? from.ch : 0));
472 return {line: line, ch: ch};
473 }
474 var end;
475 replaceRange1(code, from, to, function(end1) {
476 end = end1;
477 return {from: adjustPos(sel.from), to: adjustPos(sel.to)};
478 });
479 return end;
480 }
481 function replaceSelection(code, collapse) {
482 replaceRange1(splitLines(code), sel.from, sel.to, function(end) {
483 if (collapse == "end") return {from: end, to: end};
484 else if (collapse == "start") return {from: sel.from, to: sel.from};
485 else return {from: sel.from, to: end};
486 });
487 }
488 function replaceRange1(code, from, to, computeSel) {
489 var endch = code.length == 1 ? code[0].length + from.ch : code[code.length-1].length;
490 var newSel = computeSel({line: from.line + code.length - 1, ch: endch});
491 updateLines(from, to, code, newSel.from, newSel.to);
492 }
493
494 function getRange(from, to) {
495 var l1 = from.line, l2 = to.line;
496 if (l1 == l2) return lines[l1].text.slice(from.ch, to.ch);
497 var code = [lines[l1].text.slice(from.ch)];
498 for (var i = l1 + 1; i < l2; ++i) code.push(lines[i].text);
499 code.push(lines[l2].text.slice(0, to.ch));
500 return code.join("\n");
501 }
502 function getSelection() {
503 return getRange(sel.from, sel.to);
504 }
505
506 var pollingFast = false; // Ensures slowPoll doesn't cancel fastPoll
507 function slowPoll() {
508 if (pollingFast) return;
509 poll.set(2000, function() {
510 startOperation();
511 readInput();
512 if (focused) slowPoll();
513 endOperation();
514 });
515 }
516 function fastPoll(keyId) {
517 var missed = false;
518 pollingFast = true;
519 function p() {
520 startOperation();
521 var changed = readInput();
522 if (changed == "moved" && keyId) movementKeys[keyId] = true;
523 if (!changed && !missed) {missed = true; poll.set(80, p);}
524 else {pollingFast = false; slowPoll();}
525 endOperation();
526 }
527 poll.set(20, p);
528 }
529
530 // Inspects the textarea, compares its state (content, selection)
531 // to the data in the editing variable, and updates the editor
532 // content or cursor if something changed.
533 function readInput() {
534 var changed = false, text = input.value, sr = selRange(input);
535 if (!sr) return false;
536 var changed = editing.text != text, rs = reducedSelection;
537 var moved = changed || sr.start != editing.start || sr.end != (rs ? editing.start : editing.end);
538 if (!moved && !rs) return false;
539 if (changed) {
540 shiftSelecting = reducedSelection = null;
541 if (options.readOnly) {updateInput = true; return "changed";}
542 }
543
544 // Compute selection start and end based on start/end offsets in textarea
545 function computeOffset(n, startLine) {
546 var pos = 0;
547 for (;;) {
548 var found = text.indexOf("\n", pos);
549 if (found == -1 || (text.charAt(found-1) == "\r" ? found - 1 : found) >= n)
550 return {line: startLine, ch: n - pos};
551 ++startLine;
552 pos = found + 1;
553 }
554 }
555 var from = computeOffset(sr.start, editing.from),
556 to = computeOffset(sr.end, editing.from);
557 // Here we have to take the reducedSelection hack into account,
558 // so that you can, for example, press shift-up at the start of
559 // your selection and have the right thing happen.
560 if (rs) {
561 from = sr.start == rs.anchor ? to : from;
562 to = shiftSelecting ? sel.to : sr.start == rs.anchor ? from : to;
563 if (!posLess(from, to)) {
564 reducedSelection = null;
565 sel.inverted = false;
566 var tmp = from; from = to; to = tmp;
567 }
568 }
569
570 // In some cases (cursor on same line as before), we don't have
571 // to update the textarea content at all.
572 if (from.line == to.line && from.line == sel.from.line && from.line == sel.to.line && !shiftSelecting)
573 updateInput = false;
574
575 // Magic mess to extract precise edited range from the changed
576 // string.
577 if (changed) {
578 var start = 0, end = text.length, len = Math.min(end, editing.text.length);
579 var c, line = editing.from, nl = -1;
580 while (start < len && (c = text.charAt(start)) == editing.text.charAt(start)) {
581 ++start;
582 if (c == "\n") {line++; nl = start;}
583 }
584 var ch = nl > -1 ? start - nl : start, endline = editing.to - 1, edend = editing.text.length;
585 for (;;) {
586 c = editing.text.charAt(edend);
587 if (c == "\n") endline--;
588 if (text.charAt(end) != c) {++end; ++edend; break;}
589 if (edend <= start || end <= start) break;
590 --end; --edend;
591 }
592 var nl = editing.text.lastIndexOf("\n", edend - 1), endch = nl == -1 ? edend : edend - nl - 1;
593 updateLines({line: line, ch: ch}, {line: endline, ch: endch}, splitLines(text.slice(start, end)), from, to);
594 if (line != endline || from.line != line) updateInput = true;
595 }
596 else setSelection(from, to);
597
598 editing.text = text; editing.start = sr.start; editing.end = sr.end;
599 return changed ? "changed" : moved ? "moved" : false;
600 }
601
602 // Set the textarea content and selection range to match the
603 // editor state.
604 function prepareInput() {
605 var text = [];
606 var from = Math.max(0, sel.from.line - 1), to = Math.min(lines.length, sel.to.line + 2);
607 for (var i = from; i < to; ++i) text.push(lines[i].text);
608 text = input.value = text.join(lineSep);
609 var startch = sel.from.ch, endch = sel.to.ch;
610 for (var i = from; i < sel.from.line; ++i)
611 startch += lineSep.length + lines[i].text.length;
612 for (var i = from; i < sel.to.line; ++i)
613 endch += lineSep.length + lines[i].text.length;
614 editing = {text: text, from: from, to: to, start: startch, end: endch};
615 setSelRange(input, startch, reducedSelection ? startch : endch);
616 }
617
618 function scrollCursorIntoView() {
619 var cursor = localCoords(sel.inverted ? sel.from : sel.to);
620 return scrollIntoView(cursor.x, cursor.y, cursor.x, cursor.yBot);
621 }
622 function scrollIntoView(x1, y1, x2, y2) {
623 var pl = paddingLeft(), pt = paddingTop(), lh = lineHeight();
624 y1 += pt; y2 += pt; x1 += pl; x2 += pl;
625 var screen = wrapper.clientHeight, screentop = wrapper.scrollTop, scrolled = false, result = true;
626 if (y1 < screentop) {wrapper.scrollTop = Math.max(0, y1 - 2*lh); scrolled = true;}
627 else if (y2 > screentop + screen) {wrapper.scrollTop = y2 + lh - screen; scrolled = true;}
628
629 var screenw = wrapper.clientWidth, screenleft = wrapper.scrollLeft;
630 if (x1 < screenleft) {wrapper.scrollLeft = Math.max(0, x1 - 10); scrolled = true;}
631 else if (x2 > screenw + screenleft) {
632 wrapper.scrollLeft = x2 + 10 - screenw;
633 scrolled = true;
634 if (x2 > code.clientWidth) result = false;
635 }
636 if (scrolled && options.onScroll) options.onScroll(instance);
637 return result;
638 }
639
640 function visibleLines() {
641 var lh = lineHeight(), top = wrapper.scrollTop - paddingTop();
642 return {from: Math.min(lines.length, Math.max(0, Math.floor(top / lh))),
643 to: Math.min(lines.length, Math.ceil((top + wrapper.clientHeight) / lh))};
644 }
645 // Uses a set of changes plus the current scroll position to
646 // determine which DOM updates have to be made, and makes the
647 // updates.
648 function updateDisplay(changes) {
649 if (!wrapper.clientWidth) {
650 showingFrom = showingTo = 0;
651 return;
652 }
653 // First create a range of theoretically intact lines, and punch
654 // holes in that using the change info.
655 var intact = changes === true ? [] : [{from: showingFrom, to: showingTo, domStart: 0}];
656 for (var i = 0, l = changes.length || 0; i < l; ++i) {
657 var change = changes[i], intact2 = [], diff = change.diff || 0;
658 for (var j = 0, l2 = intact.length; j < l2; ++j) {
659 var range = intact[j];
660 if (change.to <= range.from)
661 intact2.push({from: range.from + diff, to: range.to + diff, domStart: range.domStart});
662 else if (range.to <= change.from)
663 intact2.push(range);
664 else {
665 if (change.from > range.from)
666 intact2.push({from: range.from, to: change.from, domStart: range.domStart})
667 if (change.to < range.to)
668 intact2.push({from: change.to + diff, to: range.to + diff,
669 domStart: range.domStart + (change.to - range.from)});
670 }
671 }
672 intact = intact2;
673 }
674
675 // Then, determine which lines we'd want to see, and which
676 // updates have to be made to get there.
677 var visible = visibleLines();
678 var from = Math.min(showingFrom, Math.max(visible.from - 3, 0)),
679 to = Math.min(lines.length, Math.max(showingTo, visible.to + 3)),
680 updates = [], domPos = 0, domEnd = showingTo - showingFrom, pos = from, changedLines = 0;
681
682 for (var i = 0, l = intact.length; i < l; ++i) {
683 var range = intact[i];
684 if (range.to <= from) continue;
685 if (range.from >= to) break;
686 if (range.domStart > domPos || range.from > pos) {
687 updates.push({from: pos, to: range.from, domSize: range.domStart - domPos, domStart: domPos});
688 changedLines += range.from - pos;
689 }
690 pos = range.to;
691 domPos = range.domStart + (range.to - range.from);
692 }
693 if (domPos != domEnd || pos != to) {
694 changedLines += Math.abs(to - pos);
695 updates.push({from: pos, to: to, domSize: domEnd - domPos, domStart: domPos});
696 }
697
698 if (!updates.length) return;
699 lineDiv.style.display = "none";
700 // If more than 30% of the screen needs update, just do a full
701 // redraw (which is quicker than patching)
702 if (changedLines > (visible.to - visible.from) * .3)
703 refreshDisplay(from = Math.max(visible.from - 10, 0), to = Math.min(visible.to + 7, lines.length));
704 // Otherwise, only update the stuff that needs updating.
705 else
706 patchDisplay(updates);
707 lineDiv.style.display = "";
708
709 // Position the mover div to align with the lines it's supposed
710 // to be showing (which will cover the visible display)
711 var different = from != showingFrom || to != showingTo || lastHeight != wrapper.clientHeight;
712 showingFrom = from; showingTo = to;
713 mover.style.top = (from * lineHeight()) + "px";
714 if (different) {
715 lastHeight = wrapper.clientHeight;
716 code.style.height = (lines.length * lineHeight() + 2 * paddingTop()) + "px";
717 updateGutter();
718 }
719
720 var textWidth = stringWidth(maxLine);
721 lineSpace.style.width = textWidth > wrapper.clientWidth ? textWidth + "px" : "";
722
723 // Since this is all rather error prone, it is honoured with the
724 // only assertion in the whole file.
725 if (lineDiv.childNodes.length != showingTo - showingFrom)
726 throw new Error("BAD PATCH! " + JSON.stringify(updates) + " size=" + (showingTo - showingFrom) +
727 " nodes=" + lineDiv.childNodes.length);
728 updateCursor();
729 }
730
731 function refreshDisplay(from, to) {
732 var html = [], start = {line: from, ch: 0}, inSel = posLess(sel.from, start) && !posLess(sel.to, start);
733 for (var i = from; i < to; ++i) {
734 var ch1 = null, ch2 = null;
735 if (inSel) {
736 ch1 = 0;
737 if (sel.to.line == i) {inSel = false; ch2 = sel.to.ch;}
738 }
739 else if (sel.from.line == i) {
740 if (sel.to.line == i) {ch1 = sel.from.ch; ch2 = sel.to.ch;}
741 else {inSel = true; ch1 = sel.from.ch;}
742 }
743 html.push(lines[i].getHTML(ch1, ch2, true));
744 }
745 lineDiv.innerHTML = html.join("");
746 }
747 function patchDisplay(updates) {
748 // Slightly different algorithm for IE (badInnerHTML), since
749 // there .innerHTML on PRE nodes is dumb, and discards
750 // whitespace.
751 var sfrom = sel.from.line, sto = sel.to.line, off = 0,
752 scratch = badInnerHTML && targetDocument.createElement("div");
753 for (var i = 0, e = updates.length; i < e; ++i) {
754 var rec = updates[i];
755 var extra = (rec.to - rec.from) - rec.domSize;
756 var nodeAfter = lineDiv.childNodes[rec.domStart + rec.domSize + off] || null;
757 if (badInnerHTML)
758 for (var j = Math.max(-extra, rec.domSize); j > 0; --j)
759 lineDiv.removeChild(nodeAfter ? nodeAfter.previousSibling : lineDiv.lastChild);
760 else if (extra) {
761 for (var j = Math.max(0, extra); j > 0; --j)
762 lineDiv.insertBefore(targetDocument.createElement("pre"), nodeAfter);
763 for (var j = Math.max(0, -extra); j > 0; --j)
764 lineDiv.removeChild(nodeAfter ? nodeAfter.previousSibling : lineDiv.lastChild);
765 }
766 var node = lineDiv.childNodes[rec.domStart + off], inSel = sfrom < rec.from && sto >= rec.from;
767 for (var j = rec.from; j < rec.to; ++j) {
768 var ch1 = null, ch2 = null;
769 if (inSel) {
770 ch1 = 0;
771 if (sto == j) {inSel = false; ch2 = sel.to.ch;}
772 }
773 else if (sfrom == j) {
774 if (sto == j) {ch1 = sel.from.ch; ch2 = sel.to.ch;}
775 else {inSel = true; ch1 = sel.from.ch;}
776 }
777 if (badInnerHTML) {
778 scratch.innerHTML = lines[j].getHTML(ch1, ch2, true);
779 lineDiv.insertBefore(scratch.firstChild, nodeAfter);
780 }
781 else {
782 node.innerHTML = lines[j].getHTML(ch1, ch2, false);
783 node.className = lines[j].className || "";
784 node = node.nextSibling;
785 }
786 }
787 off += extra;
788 }
789 }
790
791 function updateGutter() {
792 if (!options.gutter && !options.lineNumbers) return;
793 var hText = mover.offsetHeight, hEditor = wrapper.clientHeight;
794 gutter.style.height = (hText - hEditor < 2 ? hEditor : hText) + "px";
795 var html = [];
796 for (var i = showingFrom; i < showingTo; ++i) {
797 var marker = lines[i].gutterMarker;
798 var text = options.lineNumbers ? i + options.firstLineNumber : null;
799 if (marker && marker.text)
800 text = marker.text.replace("%N%", text != null ? text : "");
801 else if (text == null)
802 text = "\u00a0";
803 html.push((marker && marker.style ? '<pre class="' + marker.style + '">' : "<pre>"), text, "</pre>");
804 }
805 gutter.style.display = "none";
806 gutterText.innerHTML = html.join("");
807 var minwidth = String(lines.length).length, firstNode = gutterText.firstChild, val = eltText(firstNode), pad = "";
808 while (val.length + pad.length < minwidth) pad += "\u00a0";
809 if (pad) firstNode.insertBefore(targetDocument.createTextNode(pad), firstNode.firstChild);
810 gutter.style.display = "";
811 lineSpace.style.marginLeft = gutter.offsetWidth + "px";
812 }
813 function updateCursor() {
814 var head = sel.inverted ? sel.from : sel.to;
815 var x = charX(head.line, head.ch) + "px", y = (head.line - showingFrom) * lineHeight() + "px";
816 inputDiv.style.top = y;
817 if (posEq(sel.from, sel.to)) {
818 cursor.style.top = y; cursor.style.left = x;
819 cursor.style.display = "";
820 }
821 else cursor.style.display = "none";
822 }
823
824 function setSelectionUser(from, to) {
825 var sh = shiftSelecting && clipPos(shiftSelecting);
826 if (sh) {
827 if (posLess(sh, from)) from = sh;
828 else if (posLess(to, sh)) to = sh;
829 }
830 setSelection(from, to);
831 }
832 // Update the selection. Last two args are only used by
833 // updateLines, since they have to be expressed in the line
834 // numbers before the update.
835 function setSelection(from, to, oldFrom, oldTo) {
836 if (posEq(sel.from, from) && posEq(sel.to, to)) return;
837 if (posLess(to, from)) {var tmp = to; to = from; from = tmp;}
838
839 var startEq = posEq(sel.to, to), endEq = posEq(sel.from, from);
840 if (posEq(from, to)) sel.inverted = false;
841 else if (startEq && !endEq) sel.inverted = true;
842 else if (endEq && !startEq) sel.inverted = false;
843
844 // Some ugly logic used to only mark the lines that actually did
845 // see a change in selection as changed, rather than the whole
846 // selected range.
847 if (oldFrom == null) {oldFrom = sel.from.line; oldTo = sel.to.line;}
848 if (posEq(from, to)) {
849 if (!posEq(sel.from, sel.to))
850 changes.push({from: oldFrom, to: oldTo + 1});
851 }
852 else if (posEq(sel.from, sel.to)) {
853 changes.push({from: from.line, to: to.line + 1});
854 }
855 else {
856 if (!posEq(from, sel.from)) {
857 if (from.line < oldFrom)
858 changes.push({from: from.line, to: Math.min(to.line, oldFrom) + 1});
859 else
860 changes.push({from: oldFrom, to: Math.min(oldTo, from.line) + 1});
861 }
862 if (!posEq(to, sel.to)) {
863 if (to.line < oldTo)
864 changes.push({from: Math.max(oldFrom, from.line), to: oldTo + 1});
865 else
866 changes.push({from: Math.max(from.line, oldTo), to: to.line + 1});
867 }
868 }
869 sel.from = from; sel.to = to;
870 selectionChanged = true;
871 }
872 function setCursor(line, ch, user) {
873 var pos = clipPos({line: line, ch: ch || 0});
874 (user ? setSelectionUser : setSelection)(pos, pos);
875 }
876
877 function clipLine(n) {return Math.max(0, Math.min(n, lines.length-1));}
878 function clipPos(pos) {
879 if (pos.line < 0) return {line: 0, ch: 0};
880 if (pos.line >= lines.length) return {line: lines.length-1, ch: lines[lines.length-1].text.length};
881 var ch = pos.ch, linelen = lines[pos.line].text.length;
882 if (ch == null || ch > linelen) return {line: pos.line, ch: linelen};
883 else if (ch < 0) return {line: pos.line, ch: 0};
884 else return pos;
885 }
886
887 function scrollPage(down) {
888 var linesPerPage = Math.floor(wrapper.clientHeight / lineHeight()), head = sel.inverted ? sel.from : sel.to;
889 setCursor(head.line + (Math.max(linesPerPage - 1, 1) * (down ? 1 : -1)), head.ch, true);
890 }
891 function scrollEnd(top) {
892 setCursor(top ? 0 : lines.length - 1, true);
893 }
894 function selectAll() {
895 var endLine = lines.length - 1;
896 setSelection({line: 0, ch: 0}, {line: endLine, ch: lines[endLine].text.length});
897 }
898 function selectWordAt(pos) {
899 var line = lines[pos.line].text;
900 var start = pos.ch, end = pos.ch;
901 while (start > 0 && /\w/.test(line.charAt(start - 1))) --start;
902 while (end < line.length && /\w/.test(line.charAt(end))) ++end;
903 setSelectionUser({line: pos.line, ch: start}, {line: pos.line, ch: end});
904 }
905 function selectLine(line) {
906 setSelectionUser({line: line, ch: 0}, {line: line, ch: lines[line].text.length});
907 }
908 function handleEnter() {
909 replaceSelection("\n", "end");
910 if (options.enterMode != "flat")
911 indentLine(sel.from.line, options.enterMode == "keep" ? "prev" : "smart");
912 }
913 function handleTab(shift) {
914 shiftSelecting = null;
915 switch (options.tabMode) {
916 case "default":
917 return false;
918 case "indent":
919 for (var i = sel.from.line, e = sel.to.line; i <= e; ++i) indentLine(i, "smart");
920 break;
921 case "classic":
922 if (posEq(sel.from, sel.to)) {
923 if (shift) indentLine(sel.from.line, "smart");
924 else replaceSelection("\t", "end");
925 break;
926 }
927 case "shift":
928 for (var i = sel.from.line, e = sel.to.line; i <= e; ++i) indentLine(i, shift ? "subtract" : "add");
929 break;
930 }
931 return true;
932 }
933
934 function indentLine(n, how) {
935 if (how == "smart") {
936 if (!mode.indent) how = "prev";
937 else var state = getStateBefore(n);
938 }
939
940 var line = lines[n], curSpace = line.indentation(), curSpaceString = line.text.match(/^\s*/)[0], indentation;
941 if (how == "prev") {
942 if (n) indentation = lines[n-1].indentation();
943 else indentation = 0;
944 }
945 else if (how == "smart") indentation = mode.indent(state, line.text.slice(curSpaceString.length));
946 else if (how == "add") indentation = curSpace + options.indentUnit;
947 else if (how == "subtract") indentation = curSpace - options.indentUnit;
948 indentation = Math.max(0, indentation);
949 var diff = indentation - curSpace;
950
951 if (!diff) {
952 if (sel.from.line != n && sel.to.line != n) return;
953 var indentString = curSpaceString;
954 }
955 else {
956 var indentString = "", pos = 0;
957 if (options.indentWithTabs)
958 for (var i = Math.floor(indentation / tabSize); i; --i) {pos += tabSize; indentString += "\t";}
959 while (pos < indentation) {++pos; indentString += " ";}
960 }
961
962 replaceRange(indentString, {line: n, ch: 0}, {line: n, ch: curSpaceString.length});
963 }
964
965 function loadMode() {
966 mode = CodeMirror.getMode(options, options.mode);
967 for (var i = 0, l = lines.length; i < l; ++i)
968 lines[i].stateAfter = null;
969 work = [0];
970 startWorker();
971 }
972 function gutterChanged() {
973 var visible = options.gutter || options.lineNumbers;
974 gutter.style.display = visible ? "" : "none";
975 if (visible) updateGutter();
976 else lineDiv.parentNode.style.marginLeft = 0;
977 }
978
979 function markText(from, to, className) {
980 from = clipPos(from); to = clipPos(to);
981 var accum = [];
982 function add(line, from, to, className) {
983 var line = lines[line], mark = line.addMark(from, to, className);
984 mark.line = line;
985 accum.push(mark);
986 }
987 if (from.line == to.line) add(from.line, from.ch, to.ch, className);
988 else {
989 add(from.line, from.ch, null, className);
990 for (var i = from.line + 1, e = to.line; i < e; ++i)
991 add(i, 0, null, className);
992 add(to.line, 0, to.ch, className);
993 }
994 changes.push({from: from.line, to: to.line + 1});
995 return function() {
996 var start, end;
997 for (var i = 0; i < accum.length; ++i) {
998 var mark = accum[i], found = indexOf(lines, mark.line);
999 mark.line.removeMark(mark);
1000 if (found > -1) {
1001 if (start == null) start = found;
1002 end = found;
1003 }
1004 }
1005 if (start != null) changes.push({from: start, to: end + 1});
1006 };
1007 }
1008
1009 function addGutterMarker(line, text, className) {
1010 if (typeof line == "number") line = lines[clipLine(line)];
1011 line.gutterMarker = {text: text, style: className};
1012 updateGutter();
1013 return line;
1014 }
1015 function removeGutterMarker(line) {
1016 if (typeof line == "number") line = lines[clipLine(line)];
1017 line.gutterMarker = null;
1018 updateGutter();
1019 }
1020 function setLineClass(line, className) {
1021 if (typeof line == "number") {
1022 var no = line;
1023 line = lines[clipLine(line)];
1024 }
1025 else {
1026 var no = indexOf(lines, line);
1027 if (no == -1) return null;
1028 }
1029 line.className = className;
1030 changes.push({from: no, to: no + 1});
1031 return line;
1032 }
1033
1034 function lineInfo(line) {
1035 if (typeof line == "number") {
1036 var n = line;
1037 line = lines[line];
1038 if (!line) return null;
1039 }
1040 else {
1041 var n = indexOf(lines, line);
1042 if (n == -1) return null;
1043 }
1044 var marker = line.gutterMarker;
1045 return {line: n, text: line.text, markerText: marker && marker.text, markerClass: marker && marker.style};
1046 }
1047
1048 function stringWidth(str) {
1049 measure.innerHTML = "<pre><span>x</span></pre>";
1050 measure.firstChild.firstChild.firstChild.nodeValue = str;
1051 return measure.firstChild.firstChild.offsetWidth || 10;
1052 }
1053 // These are used to go from pixel positions to character
1054 // positions, taking varying character widths into account.
1055 function charX(line, pos) {
1056 if (pos == 0) return 0;
1057 measure.innerHTML = "<pre><span>" + lines[line].getHTML(null, null, false, pos) + "</span></pre>";
1058 return measure.firstChild.firstChild.offsetWidth;
1059 }
1060 function charFromX(line, x) {
1061 if (x <= 0) return 0;
1062 var lineObj = lines[line], text = lineObj.text;
1063 function getX(len) {
1064 measure.innerHTML = "<pre><span>" + lineObj.getHTML(null, null, false, len) + "</span></pre>";
1065 return measure.firstChild.firstChild.offsetWidth;
1066 }
1067 var from = 0, fromX = 0, to = text.length, toX;
1068 // Guess a suitable upper bound for our search.
1069 var estimated = Math.min(to, Math.ceil(x / stringWidth("x")));
1070 for (;;) {
1071 var estX = getX(estimated);
1072 if (estX <= x && estimated < to) estimated = Math.min(to, Math.ceil(estimated * 1.2));
1073 else {toX = estX; to = estimated; break;}
1074 }
1075 if (x > toX) return to;
1076 // Try to guess a suitable lower bound as well.
1077 estimated = Math.floor(to * 0.8); estX = getX(estimated);
1078 if (estX < x) {from = estimated; fromX = estX;}
1079 // Do a binary search between these bounds.
1080 for (;;) {
1081 if (to - from <= 1) return (toX - x > x - fromX) ? from : to;
1082 var middle = Math.ceil((from + to) / 2), middleX = getX(middle);
1083 if (middleX > x) {to = middle; toX = middleX;}
1084 else {from = middle; fromX = middleX;}
1085 }
1086 }
1087
1088 function localCoords(pos, inLineWrap) {
1089 var lh = lineHeight(), line = pos.line - (inLineWrap ? showingFrom : 0);
1090 return {x: charX(pos.line, pos.ch), y: line * lh, yBot: (line + 1) * lh};
1091 }
1092 function pageCoords(pos) {
1093 var local = localCoords(pos, true), off = eltOffset(lineSpace);
1094 return {x: off.left + local.x, y: off.top + local.y, yBot: off.top + local.yBot};
1095 }
1096
1097 function lineHeight() {
1098 var nlines = lineDiv.childNodes.length;
1099 if (nlines) return lineDiv.offsetHeight / nlines;
1100 measure.innerHTML = "<pre>x</pre>";
1101 return measure.firstChild.offsetHeight || 1;
1102 }
1103 function paddingTop() {return lineSpace.offsetTop;}
1104 function paddingLeft() {return lineSpace.offsetLeft;}
1105
1106 function posFromMouse(e, liberal) {
1107 var offW = eltOffset(wrapper), x = e.pageX(), y = e.pageY();
1108 // This is a mess of a heuristic to try and determine whether a
1109 // scroll-bar was clicked or not, and to return null if one was
1110 // (and !liberal).
1111 if (!liberal && (x - offW.left > wrapper.clientWidth || y - offW.top > wrapper.clientHeight))
1112 return null;
1113 var offL = eltOffset(lineSpace);
1114 var line = showingFrom + Math.floor((y - offL.top) / lineHeight());
1115 return clipPos({line: line, ch: charFromX(clipLine(line), x - offL.left)});
1116 }
1117 function onContextMenu(e) {
1118 var pos = posFromMouse(e);
1119 if (!pos || window.opera) return; // Opera is difficult.
1120 if (posEq(sel.from, sel.to) || posLess(pos, sel.from) || !posLess(pos, sel.to))
1121 setCursor(pos.line, pos.ch);
1122
1123 var oldCSS = input.style.cssText;
1124 input.style.cssText = "position: fixed; width: 30px; height: 30px; top: " + (e.pageY() - 1) +
1125 "px; left: " + (e.pageX() - 1) + "px; z-index: 1000; background: white; " +
1126 "border-width: 0; outline: none; overflow: hidden;";
1127 var val = input.value = getSelection();
1128 input.focus();
1129 setSelRange(input, 0, val.length);
1130 if (gecko) e.stop();
1131 leaveInputAlone = true;
1132 setTimeout(function() {
1133 if (input.value != val) operation(replaceSelection)(input.value, "end");
1134 input.style.cssText = oldCSS;
1135 leaveInputAlone = false;
1136 prepareInput();
1137 slowPoll();
1138 }, 50);
1139 }
1140
1141 // Cursor-blinking
1142 function restartBlink() {
1143 clearInterval(blinker);
1144 var on = true;
1145 cursor.style.visibility = "";
1146 blinker = setInterval(function() {
1147 cursor.style.visibility = (on = !on) ? "" : "hidden";
1148 }, 650);
1149 }
1150
1151 var matching = {"(": ")>", ")": "(<", "[": "]>", "]": "[<", "{": "}>", "}": "{<"};
1152 function matchBrackets(autoclear) {
1153 var head = sel.inverted ? sel.from : sel.to, line = lines[head.line], pos = head.ch - 1;
1154 var match = (pos >= 0 && matching[line.text.charAt(pos)]) || matching[line.text.charAt(++pos)];
1155 if (!match) return;
1156 var ch = match.charAt(0), forward = match.charAt(1) == ">", d = forward ? 1 : -1, st = line.styles;
1157 for (var off = pos + 1, i = 0, e = st.length; i < e; i+=2)
1158 if ((off -= st[i].length) <= 0) {var style = st[i+1]; break;}
1159
1160 var stack = [line.text.charAt(pos)], re = /[(){}[\]]/;
1161 function scan(line, from, to) {
1162 if (!line.text) return;
1163 var st = line.styles, pos = forward ? 0 : line.text.length - 1, cur;
1164 for (var i = forward ? 0 : st.length - 2, e = forward ? st.length : -2; i != e; i += 2*d) {
1165 var text = st[i];
1166 if (st[i+1] != null && st[i+1] != style) {pos += d * text.length; continue;}
1167 for (var j = forward ? 0 : text.length - 1, te = forward ? text.length : -1; j != te; j += d, pos+=d) {
1168 if (pos >= from && pos < to && re.test(cur = text.charAt(j))) {
1169 var match = matching[cur];
1170 if (match.charAt(1) == ">" == forward) stack.push(cur);
1171 else if (stack.pop() != match.charAt(0)) return {pos: pos, match: false};
1172 else if (!stack.length) return {pos: pos, match: true};
1173 }
1174 }
1175 }
1176 }
1177 for (var i = head.line, e = forward ? Math.min(i + 50, lines.length) : Math.max(-1, i - 50); i != e; i+=d) {
1178 var line = lines[i], first = i == head.line;
1179 var found = scan(line, first && forward ? pos + 1 : 0, first && !forward ? pos : line.text.length);
1180 if (found) {
1181 var style = found.match ? "CodeMirror-matchingbracket" : "CodeMirror-nonmatchingbracket";
1182 var one = markText({line: head.line, ch: pos}, {line: head.line, ch: pos+1}, style),
1183 two = markText({line: i, ch: found.pos}, {line: i, ch: found.pos + 1}, style);
1184 var clear = operation(function(){one(); two();});
1185 if (autoclear) setTimeout(clear, 800);
1186 else bracketHighlighted = clear;
1187 break;
1188 }
1189 }
1190 }
1191
1192 // Finds the line to start with when starting a parse. Tries to
1193 // find a line with a stateAfter, so that it can start with a
1194 // valid state. If that fails, it returns the line with the
1195 // smallest indentation, which tends to need the least context to
1196 // parse correctly.
1197 function findStartLine(n) {
1198 var minindent, minline;
1199 for (var search = n, lim = n - 40; search > lim; --search) {
1200 if (search == 0) return 0;
1201 var line = lines[search-1];
1202 if (line.stateAfter) return search;
1203 var indented = line.indentation();
1204 if (minline == null || minindent > indented) {
1205 minline = search;
1206 minindent = indented;
1207 }
1208 }
1209 return minline;
1210 }
1211 function getStateBefore(n) {
1212 var start = findStartLine(n), state = start && lines[start-1].stateAfter;
1213 if (!state) state = startState(mode);
1214 else state = copyState(mode, state);
1215 for (var i = start; i < n; ++i) {
1216 var line = lines[i];
1217 line.highlight(mode, state);
1218 line.stateAfter = copyState(mode, state);
1219 }
1220 if (!lines[n].stateAfter) work.push(n);
1221 return state;
1222 }
1223 function highlightWorker() {
1224 var end = +new Date + options.workTime;
1225 while (work.length) {
1226 if (!lines[showingFrom].stateAfter) var task = showingFrom;
1227 else var task = work.pop();
1228 if (task >= lines.length) continue;
1229 var start = findStartLine(task), state = start && lines[start-1].stateAfter;
1230 if (state) state = copyState(mode, state);
1231 else state = startState(mode);
1232
1233 for (var i = start, l = lines.length; i < l; ++i) {
1234 var line = lines[i], hadState = line.stateAfter;
1235 if (+new Date > end) {
1236 work.push(i);
1237 startWorker(options.workDelay);
1238 changes.push({from: task, to: i});
1239 return;
1240 }
1241 var changed = line.highlight(mode, state);
1242 line.stateAfter = copyState(mode, state);
1243 if (hadState && !changed && line.text) break;
1244 }
1245 changes.push({from: task, to: i});
1246 }
1247 }
1248 function startWorker(time) {
1249 if (!work.length) return;
1250 highlight.set(time, operation(highlightWorker));
1251 }
1252
1253 // Operations are used to wrap changes in such a way that each
1254 // change won't have to update the cursor and display (which would
1255 // be awkward, slow, and error-prone), but instead updates are
1256 // batched and then all combined and executed at once.
1257 function startOperation() {
1258 updateInput = null; changes = []; textChanged = selectionChanged = false;
1259 }
1260 function endOperation() {
1261 var reScroll = false;
1262 if (selectionChanged) reScroll = !scrollCursorIntoView();
1263 if (changes.length) updateDisplay(changes);
1264 else if (selectionChanged) updateCursor();
1265 if (reScroll) scrollCursorIntoView();
1266 if (selectionChanged) restartBlink();
1267
1268 // updateInput can be set to a boolean value to force/prevent an
1269 // update.
1270 if (!leaveInputAlone && (updateInput === true || (updateInput !== false && selectionChanged)))
1271 prepareInput();
1272
1273 if (selectionChanged && options.matchBrackets)
1274 setTimeout(operation(function() {
1275 if (bracketHighlighted) {bracketHighlighted(); bracketHighlighted = null;}
1276 matchBrackets(false);
1277 }), 20);
1278 var tc = textChanged; // textChanged can be reset by cursoractivity callback
1279 if (selectionChanged && options.onCursorActivity)
1280 options.onCursorActivity(instance);
1281 if (tc && options.onChange && instance)
1282 options.onChange(instance, tc);
1283 }
1284 var nestedOperation = 0;
1285 function operation(f) {
1286 return function() {
1287 if (!nestedOperation++) startOperation();
1288 try {var result = f.apply(this, arguments);}
1289 finally {if (!--nestedOperation) endOperation();}
1290 return result;
1291 };
1292 }
1293
1294 function SearchCursor(query, pos, caseFold) {
1295 this.atOccurrence = false;
1296 if (caseFold == null) caseFold = typeof query == "string" && query == query.toLowerCase();
1297
1298 if (pos && typeof pos == "object") pos = clipPos(pos);
1299 else pos = {line: 0, ch: 0};
1300 this.pos = {from: pos, to: pos};
1301
1302 // The matches method is filled in based on the type of query.
1303 // It takes a position and a direction, and returns an object
1304 // describing the next occurrence of the query, or null if no
1305 // more matches were found.
1306 if (typeof query != "string") // Regexp match
1307 this.matches = function(reverse, pos) {
1308 if (reverse) {
1309 var line = lines[pos.line].text.slice(0, pos.ch), match = line.match(query), start = 0;
1310 while (match) {
1311 var ind = line.indexOf(match[0]);
1312 start += ind;
1313 line = line.slice(ind + 1);
1314 var newmatch = line.match(query);
1315 if (newmatch) match = newmatch;
1316 else break;
1317 }
1318 }
1319 else {
1320 var line = lines[pos.line].text.slice(pos.ch), match = line.match(query),
1321 start = match && pos.ch + line.indexOf(match[0]);
1322 }
1323 if (match)
1324 return {from: {line: pos.line, ch: start},
1325 to: {line: pos.line, ch: start + match[0].length},
1326 match: match};
1327 };
1328 else { // String query
1329 if (caseFold) query = query.toLowerCase();
1330 var fold = caseFold ? function(str){return str.toLowerCase();} : function(str){return str;};
1331 var target = query.split("\n");
1332 // Different methods for single-line and multi-line queries
1333 if (target.length == 1)
1334 this.matches = function(reverse, pos) {
1335 var line = fold(lines[pos.line].text), len = query.length, match;
1336 if (reverse ? (pos.ch >= len && (match = line.lastIndexOf(query, pos.ch - len)) != -1)
1337 : (match = line.indexOf(query, pos.ch)) != -1)
1338 return {from: {line: pos.line, ch: match},
1339 to: {line: pos.line, ch: match + len}};
1340 };
1341 else
1342 this.matches = function(reverse, pos) {
1343 var ln = pos.line, idx = (reverse ? target.length - 1 : 0), match = target[idx], line = fold(lines[ln].text);
1344 var offsetA = (reverse ? line.indexOf(match) + match.length : line.lastIndexOf(match));
1345 if (reverse ? offsetA >= pos.ch || offsetA != match.length
1346 : offsetA <= pos.ch || offsetA != line.length - match.length)
1347 return;
1348 for (;;) {
1349 if (reverse ? !ln : ln == lines.length - 1) return;
1350 line = fold(lines[ln += reverse ? -1 : 1].text);
1351 match = target[reverse ? --idx : ++idx];
1352 if (idx > 0 && idx < target.length - 1) {
1353 if (line != match) return;
1354 else continue;
1355 }
1356 var offsetB = (reverse ? line.lastIndexOf(match) : line.indexOf(match) + match.length);
1357 if (reverse ? offsetB != line.length - match.length : offsetB != match.length)
1358 return;
1359 var start = {line: pos.line, ch: offsetA}, end = {line: ln, ch: offsetB};
1360 return {from: reverse ? end : start, to: reverse ? start : end};
1361 }
1362 };
1363 }
1364 }
1365
1366 SearchCursor.prototype = {
1367 findNext: function() {return this.find(false);},
1368 findPrevious: function() {return this.find(true);},
1369
1370 find: function(reverse) {
1371 var self = this, pos = clipPos(reverse ? this.pos.from : this.pos.to);
1372 function savePosAndFail(line) {
1373 var pos = {line: line, ch: 0};
1374 self.pos = {from: pos, to: pos};
1375 self.atOccurrence = false;
1376 return false;
1377 }
1378
1379 for (;;) {
1380 if (this.pos = this.matches(reverse, pos)) {
1381 this.atOccurrence = true;
1382 return this.pos.match || true;
1383 }
1384 if (reverse) {
1385 if (!pos.line) return savePosAndFail(0);
1386 pos = {line: pos.line-1, ch: lines[pos.line-1].text.length};
1387 }
1388 else {
1389 if (pos.line == lines.length - 1) return savePosAndFail(lines.length);
1390 pos = {line: pos.line+1, ch: 0};
1391 }
1392 }
1393 },
1394
1395 from: function() {if (this.atOccurrence) return copyPos(this.pos.from);},
1396 to: function() {if (this.atOccurrence) return copyPos(this.pos.to);}
1397 };
1398
1399 return instance;
1400 } // (end of function CodeMirror)
1401
1402 // The default configuration options.
1403 CodeMirror.defaults = {
1404 value: "",
1405 mode: null,
1406 indentUnit: 2,
1407 indentWithTabs: false,
1408 tabMode: "classic",
1409 enterMode: "indent",
1410 electricChars: true,
1411 onKeyEvent: null,
1412 lineNumbers: false,
1413 gutter: false,
1414 firstLineNumber: 1,
1415 readOnly: false,
1416 onChange: null,
1417 onCursorActivity: null,
1418 onGutterClick: null,
1419 onFocus: null, onBlur: null, onScroll: null,
1420 matchBrackets: false,
1421 workTime: 100,
1422 workDelay: 200,
1423 undoDepth: 40,
1424 tabindex: null,
1425 document: window.document
1426 };
1427
1428 // Known modes, by name and by MIME
1429 var modes = {}, mimeModes = {};
1430 CodeMirror.defineMode = function(name, mode) {
1431 if (!CodeMirror.defaults.mode && name != "null") CodeMirror.defaults.mode = name;
1432 modes[name] = mode;
1433 };
1434 CodeMirror.defineMIME = function(mime, spec) {
1435 mimeModes[mime] = spec;
1436 };
1437 CodeMirror.getMode = function(options, spec) {
1438 if (typeof spec == "string" && mimeModes.hasOwnProperty(spec))
1439 spec = mimeModes[spec];
1440 if (typeof spec == "string")
1441 var mname = spec, config = {};
1442 else
1443 var mname = spec.name, config = spec;
1444 var mfactory = modes[mname];
1445 if (!mfactory) {
1446 if (window.console) console.warn("No mode " + mname + " found, falling back to plain text.");
1447 return CodeMirror.getMode(options, "text/plain");
1448 }
1449 return mfactory(options, config);
1450 }
1451 CodeMirror.listModes = function() {
1452 var list = [];
1453 for (var m in modes)
1454 if (modes.propertyIsEnumerable(m)) list.push(m);
1455 return list;
1456 };
1457 CodeMirror.listMIMEs = function() {
1458 var list = [];
1459 for (var m in mimeModes)
1460 if (mimeModes.propertyIsEnumerable(m)) list.push(m);
1461 return list;
1462 };
1463
1464 CodeMirror.fromTextArea = function(textarea, options) {
1465 if (!options) options = {};
1466 options.value = textarea.value;
1467 if (!options.tabindex && textarea.tabindex)
1468 options.tabindex = textarea.tabindex;
1469
1470 function save() {textarea.value = instance.getValue();}
1471 if (textarea.form) {
1472 // Deplorable hack to make the submit method do the right thing.
1473 var rmSubmit = connect(textarea.form, "submit", save, true);
1474 if (typeof textarea.form.submit == "function") {
1475 var realSubmit = textarea.form.submit;
1476 function wrappedSubmit() {
1477 save();
1478 textarea.form.submit = realSubmit;
1479 textarea.form.submit();
1480 textarea.form.submit = wrappedSubmit;
1481 }
1482 textarea.form.submit = wrappedSubmit;
1483 }
1484 }
1485
1486 textarea.style.display = "none";
1487 var instance = CodeMirror(function(node) {
1488 textarea.parentNode.insertBefore(node, textarea.nextSibling);
1489 }, options);
1490 instance.save = save;
1491 instance.toTextArea = function() {
1492 save();
1493 textarea.parentNode.removeChild(instance.getWrapperElement());
1494 textarea.style.display = "";
1495 if (textarea.form) {
1496 rmSubmit();
1497 if (typeof textarea.form.submit == "function")
1498 textarea.form.submit = realSubmit;
1499 }
1500 };
1501 return instance;
1502 };
1503
1504 // Utility functions for working with state. Exported because modes
1505 // sometimes need to do this.
1506 function copyState(mode, state) {
1507 if (state === true) return state;
1508 if (mode.copyState) return mode.copyState(state);
1509 var nstate = {};
1510 for (var n in state) {
1511 var val = state[n];
1512 if (val instanceof Array) val = val.concat([]);
1513 nstate[n] = val;
1514 }
1515 return nstate;
1516 }
1517 CodeMirror.startState = startState;
1518 function startState(mode, a1, a2) {
1519 return mode.startState ? mode.startState(a1, a2) : true;
1520 }
1521 CodeMirror.copyState = copyState;
1522
1523 // The character stream used by a mode's parser.
1524 function StringStream(string) {
1525 this.pos = this.start = 0;
1526 this.string = string;
1527 }
1528 StringStream.prototype = {
1529 eol: function() {return this.pos >= this.string.length;},
1530 sol: function() {return this.pos == 0;},
1531 peek: function() {return this.string.charAt(this.pos);},
1532 next: function() {
1533 if (this.pos < this.string.length)
1534 return this.string.charAt(this.pos++);
1535 },
1536 eat: function(match) {
1537 var ch = this.string.charAt(this.pos);
1538 if (typeof match == "string") var ok = ch == match;
1539 else var ok = ch && (match.test ? match.test(ch) : match(ch));
1540 if (ok) {++this.pos; return ch;}
1541 },
1542 eatWhile: function(match) {
1543 var start = this.start;
1544 while (this.eat(match)){}
1545 return this.pos > start;
1546 },
1547 eatSpace: function() {
1548 var start = this.pos;
1549 while (/[\s\u00a0]/.test(this.string.charAt(this.pos))) ++this.pos;
1550 return this.pos > start;
1551 },
1552 skipToEnd: function() {this.pos = this.string.length;},
1553 skipTo: function(ch) {
1554 var found = this.string.indexOf(ch, this.pos);
1555 if (found > -1) {this.pos = found; return true;}
1556 },
1557 backUp: function(n) {this.pos -= n;},
1558 column: function() {return countColumn(this.string, this.start);},
1559 indentation: function() {return countColumn(this.string);},
1560 match: function(pattern, consume, caseInsensitive) {
1561 if (typeof pattern == "string") {
1562 function cased(str) {return caseInsensitive ? str.toLowerCase() : str;}
1563 if (cased(this.string).indexOf(cased(pattern), this.pos) == this.pos) {
1564 if (consume !== false) this.pos += pattern.length;
1565 return true;
1566 }
1567 }
1568 else {
1569 var match = this.string.slice(this.pos).match(pattern);
1570 if (match && consume !== false) this.pos += match[0].length;
1571 return match;
1572 }
1573 },
1574 current: function(){return this.string.slice(this.start, this.pos);}
1575 };
1576
1577 // Line objects. These hold state related to a line, including
1578 // highlighting info (the styles array).
1579 function Line(text, styles) {
1580 this.styles = styles || [text, null];
1581 this.stateAfter = null;
1582 this.text = text;
1583 this.marked = this.gutterMarker = this.className = null;
1584 }
1585 Line.prototype = {
1586 // Replace a piece of a line, keeping the styles around it intact.
1587 replace: function(from, to, text) {
1588 var st = [], mk = this.marked;
1589 copyStyles(0, from, this.styles, st);
1590 if (text) st.push(text, null);
1591 copyStyles(to, this.text.length, this.styles, st);
1592 this.styles = st;
1593 this.text = this.text.slice(0, from) + text + this.text.slice(to);
1594 this.stateAfter = null;
1595 if (mk) {
1596 var diff = text.length - (to - from), end = this.text.length;
1597 function fix(n) {return n <= Math.min(to, to + diff) ? n : n + diff;}
1598 for (var i = 0; i < mk.length; ++i) {
1599 var mark = mk[i], del = false;
1600 if (mark.from >= end) del = true;
1601 else {mark.from = fix(mark.from); if (mark.to != null) mark.to = fix(mark.to);}
1602 if (del || mark.from >= mark.to) {mk.splice(i, 1); i--;}
1603 }
1604 }
1605 },
1606 // Split a line in two, again keeping styles intact.
1607 split: function(pos, textBefore) {
1608 var st = [textBefore, null];
1609 copyStyles(pos, this.text.length, this.styles, st);
1610 return new Line(textBefore + this.text.slice(pos), st);
1611 },
1612 addMark: function(from, to, style) {
1613 var mk = this.marked, mark = {from: from, to: to, style: style};
1614 if (this.marked == null) this.marked = [];
1615 this.marked.push(mark);
1616 this.marked.sort(function(a, b){return a.from - b.from;});
1617 return mark;
1618 },
1619 removeMark: function(mark) {
1620 var mk = this.marked;
1621 if (!mk) return;
1622 for (var i = 0; i < mk.length; ++i)
1623 if (mk[i] == mark) {mk.splice(i, 1); break;}
1624 },
1625 // Run the given mode's parser over a line, update the styles
1626 // array, which contains alternating fragments of text and CSS
1627 // classes.
1628 highlight: function(mode, state) {
1629 var stream = new StringStream(this.text), st = this.styles, pos = 0;
1630 var changed = false, curWord = st[0], prevWord;
1631 while (!stream.eol()) {
1632 var style = mode.token(stream, state);
1633 var substr = this.text.slice(stream.start, stream.pos);
1634 stream.start = stream.pos;
1635 if (pos && st[pos-1] == style)
1636 st[pos-2] += substr;
1637 else if (substr) {
1638 if (!changed && (st[pos+1] != style || (pos && st[pos-2] != prevWord))) changed = true;
1639 st[pos++] = substr; st[pos++] = style;
1640 prevWord = curWord; curWord = st[pos];
1641 }
1642 // Give up when line is ridiculously long
1643 if (stream.pos > 5000) {
1644 st[pos++] = this.text.slice(stream.pos); st[pos++] = null;
1645 break;
1646 }
1647 }
1648 if (st.length != pos) {st.length = pos; changed = true;}
1649 if (pos && st[pos-2] != prevWord) changed = true;
1650 return changed;
1651 },
1652 // Fetch the parser token for a given character. Useful for hacks
1653 // that want to inspect the mode state (say, for completion).
1654 getTokenAt: function(mode, state, ch) {
1655 var txt = this.text, stream = new StringStream(txt);
1656 while (stream.pos < ch && !stream.eol()) {
1657 stream.start = stream.pos;
1658 var style = mode.token(stream, state);
1659 }
1660 return {start: stream.start,
1661 end: stream.pos,
1662 string: stream.current(),
1663 className: style || null,
1664 state: state};
1665 },
1666 indentation: function() {return countColumn(this.text);},
1667 // Produces an HTML fragment for the line, taking selection,
1668 // marking, and highlighting into account.
1669 getHTML: function(sfrom, sto, includePre, endAt) {
1670 var html = [];
1671 if (includePre)
1672 html.push(this.className ? '<pre class="' + this.className + '">': "<pre>");
1673 function span(text, style) {
1674 if (!text) return;
1675 if (style) html.push('<span class="', style, '">', htmlEscape(text), "</span>");
1676 else html.push(htmlEscape(text));
1677 }
1678 var st = this.styles, allText = this.text, marked = this.marked;
1679 if (sfrom == sto) sfrom = null;
1680 var len = allText.length;
1681 if (endAt != null) len = Math.min(endAt, len);
1682
1683 if (!allText && endAt == null)
1684 span(" ", sfrom != null && sto == null ? "CodeMirror-selected" : null);
1685 else if (!marked && sfrom == null)
1686 for (var i = 0, ch = 0; ch < len; i+=2) {
1687 var str = st[i], l = str.length;
1688 if (ch + l > len) str = str.slice(0, len - ch);
1689 ch += l;
1690 span(str, st[i+1]);
1691 }
1692 else {
1693 var pos = 0, i = 0, text = "", style, sg = 0;
1694 var markpos = -1, mark = null;
1695 function nextMark() {
1696 if (marked) {
1697 markpos += 1;
1698 mark = (markpos < marked.length) ? marked[markpos] : null;
1699 }
1700 }
1701 nextMark();
1702 while (pos < len) {
1703 var upto = len;
1704 var extraStyle = "";
1705 if (sfrom != null) {
1706 if (sfrom > pos) upto = sfrom;
1707 else if (sto == null || sto > pos) {
1708 extraStyle = " CodeMirror-selected";
1709 if (sto != null) upto = Math.min(upto, sto);
1710 }
1711 }
1712 while (mark && mark.to != null && mark.to <= pos) nextMark();
1713 if (mark) {
1714 if (mark.from > pos) upto = Math.min(upto, mark.from);
1715 else {
1716 extraStyle += " " + mark.style;
1717 if (mark.to != null) upto = Math.min(upto, mark.to);
1718 }
1719 }
1720 for (;;) {
1721 var end = pos + text.length;
1722 var apliedStyle = style;
1723 if (extraStyle) apliedStyle = style ? style + extraStyle : extraStyle;
1724 span(end > upto ? text.slice(0, upto - pos) : text, apliedStyle);
1725 if (end >= upto) {text = text.slice(upto - pos); pos = upto; break;}
1726 pos = end;
1727 text = st[i++]; style = st[i++];
1728 }
1729 }
1730 if (sfrom != null && sto == null) span(" ", "CodeMirror-selected");
1731 }
1732 if (includePre) html.push("</pre>");
1733 return html.join("");
1734 }
1735 };
1736 // Utility used by replace and split above
1737 function copyStyles(from, to, source, dest) {
1738 for (var i = 0, pos = 0, state = 0; pos < to; i+=2) {
1739 var part = source[i], end = pos + part.length;
1740 if (state == 0) {
1741 if (end > from) dest.push(part.slice(from - pos, Math.min(part.length, to - pos)), source[i+1]);
1742 if (end >= from) state = 1;
1743 }
1744 else if (state == 1) {
1745 if (end > to) dest.push(part.slice(0, to - pos), source[i+1]);
1746 else dest.push(part, source[i+1]);
1747 }
1748 pos = end;
1749 }
1750 }
1751
1752 // The history object 'chunks' changes that are made close together
1753 // and at almost the same time into bigger undoable units.
1754 function History() {
1755 this.time = 0;
1756 this.done = []; this.undone = [];
1757 }
1758 History.prototype = {
1759 addChange: function(start, added, old) {
1760 this.undone.length = 0;
1761 var time = +new Date, last = this.done[this.done.length - 1];
1762 if (time - this.time > 400 || !last ||
1763 last.start > start + added || last.start + last.added < start - last.added + last.old.length)
1764 this.done.push({start: start, added: added, old: old});
1765 else {
1766 var oldoff = 0;
1767 if (start < last.start) {
1768 for (var i = last.start - start - 1; i >= 0; --i)
1769 last.old.unshift(old[i]);
1770 last.added += last.start - start;
1771 last.start = start;
1772 }
1773 else if (last.start < start) {
1774 oldoff = start - last.start;
1775 added += oldoff;
1776 }
1777 for (var i = last.added - oldoff, e = old.length; i < e; ++i)
1778 last.old.push(old[i]);
1779 if (last.added < added) last.added = added;
1780 }
1781 this.time = time;
1782 }
1783 };
1784
1785 // Event stopping compatibility wrapper.
1786 function stopEvent() {
1787 if (this.preventDefault) {this.preventDefault(); this.stopPropagation();}
1788 else {this.returnValue = false; this.cancelBubble = true;}
1789 }
1790 // Ensure an event has a stop method.
1791 function addStop(event) {
1792 if (!event.stop) event.stop = stopEvent;
1793 return event;
1794 }
1795
1796 // Event wrapper, exposing the few operations we need.
1797 function Event(orig) {this.e = orig;}
1798 Event.prototype = {
1799 stop: function() {stopEvent.call(this.e);},
1800 target: function() {return this.e.target || this.e.srcElement;},
1801 button: function() {
1802 if (this.e.which) return this.e.which;
1803 else if (this.e.button & 1) return 1;
1804 else if (this.e.button & 2) return 3;
1805 else if (this.e.button & 4) return 2;
1806 },
1807 pageX: function() {
1808 if (this.e.pageX != null) return this.e.pageX;
1809 var doc = this.target().ownerDocument;
1810 return this.e.clientX + doc.body.scrollLeft + doc.documentElement.scrollLeft;
1811 },
1812 pageY: function() {
1813 if (this.e.pageY != null) return this.e.pageY;
1814 var doc = this.target().ownerDocument;
1815 return this.e.clientY + doc.body.scrollTop + doc.documentElement.scrollTop;
1816 }
1817 };
1818
1819 // Event handler registration. If disconnect is true, it'll return a
1820 // function that unregisters the handler.
1821 function connect(node, type, handler, disconnect) {
1822 function wrapHandler(event) {handler(new Event(event || window.event));}
1823 if (typeof node.addEventListener == "function") {
1824 node.addEventListener(type, wrapHandler, false);
1825 if (disconnect) return function() {node.removeEventListener(type, wrapHandler, false);};
1826 }
1827 else {
1828 node.attachEvent("on" + type, wrapHandler);
1829 if (disconnect) return function() {node.detachEvent("on" + type, wrapHandler);};
1830 }
1831 }
1832
1833 function Delayed() {this.id = null;}
1834 Delayed.prototype = {set: function(ms, f) {clearTimeout(this.id); this.id = setTimeout(f, ms);}};
1835
1836 // Some IE versions don't preserve whitespace when setting the
1837 // innerHTML of a PRE tag.
1838 var badInnerHTML = (function() {
1839 var pre = document.createElement("pre");
1840 pre.innerHTML = " "; return !pre.innerHTML;
1841 })();
1842
1843 var gecko = /gecko\/\d{7}/i.test(navigator.userAgent);
1844
1845 var lineSep = "\n";
1846 // Feature-detect whether newlines in textareas are converted to \r\n
1847 (function () {
1848 var te = document.createElement("textarea");
1849 te.value = "foo\nbar";
1850 if (te.value.indexOf("\r") > -1) lineSep = "\r\n";
1851 }());
1852
1853 var tabSize = 8;
1854 var mac = /Mac/.test(navigator.platform);
1855 var movementKeys = {};
1856 for (var i = 35; i <= 40; ++i)
1857 movementKeys[i] = movementKeys["c" + i] = true;
1858
1859 // Counts the column offset in a string, taking tabs into account.
1860 // Used mostly to find indentation.
1861 function countColumn(string, end) {
1862 if (end == null) {
1863 end = string.search(/[^\s\u00a0]/);
1864 if (end == -1) end = string.length;
1865 }
1866 for (var i = 0, n = 0; i < end; ++i) {
1867 if (string.charAt(i) == "\t") n += tabSize - (n % tabSize);
1868 else ++n;
1869 }
1870 return n;
1871 }
1872
1873 // Find the position of an element by following the offsetParent chain.
1874 function eltOffset(node) {
1875 var x = 0, y = 0;
1876 for (var n = node; n; n = n.offsetParent) {x += n.offsetLeft; y += n.offsetTop;}
1877 for (var n = node.parentNode; n != node.ownerDocument.body; n = n.parentNode) {x -= n.scrollLeft; y -= n.scrollTop;}
1878 return {left: x, top: y};
1879 }
1880 // Get a node's text content.
1881 function eltText(node) {
1882 return node.textContent || node.innerText || node.nodeValue || "";
1883 }
1884
1885 // Operations on {line, ch} objects.
1886 function posEq(a, b) {return a.line == b.line && a.ch == b.ch;}
1887 function posLess(a, b) {return a.line < b.line || (a.line == b.line && a.ch < b.ch);}
1888 function copyPos(x) {return {line: x.line, ch: x.ch};}
1889
1890 function htmlEscape(str) {
1891 return str.replace(/[<>&]/g, function(str) {
1892 return str == "&" ? "&amp;" : str == "<" ? "&lt;" : "&gt;";
1893 });
1894 }
1895
1896 // Used to position the cursor after an undo/redo by finding the
1897 // last edited character.
1898 function editEnd(from, to) {
1899 if (!to) return from ? from.length : 0;
1900 if (!from) return to.length;
1901 for (var i = from.length, j = to.length; i >= 0 && j >= 0; --i, --j)
1902 if (from.charAt(i) != to.charAt(j)) break;
1903 return j + 1;
1904 }
1905
1906 function indexOf(collection, elt) {
1907 if (collection.indexOf) return collection.indexOf(elt);
1908 for (var i = 0, e = collection.length; i < e; ++i)
1909 if (collection[i] == elt) return i;
1910 return -1;
1911 }
1912
1913 // See if "".split is the broken IE version, if so, provide an
1914 // alternative way to split lines.
1915 if ("\n\nb".split(/\n/).length != 3)
1916 var splitLines = function(string) {
1917 var pos = 0, nl, result = [];
1918 while ((nl = string.indexOf("\n", pos)) > -1) {
1919 result.push(string.slice(pos, string.charAt(nl-1) == "\r" ? nl - 1 : nl));
1920 pos = nl + 1;
1921 }
1922 result.push(string.slice(pos));
1923 return result;
1924 };
1925 else
1926 var splitLines = function(string){return string.split(/\r?\n/);};
1927
1928 // Sane model of finding and setting the selection in a textarea
1929 if (window.getSelection) {
1930 var selRange = function(te) {
1931 try {return {start: te.selectionStart, end: te.selectionEnd};}
1932 catch(e) {return null;}
1933 };
1934 var setSelRange = function(te, start, end) {
1935 try {te.setSelectionRange(start, end);}
1936 catch(e) {} // Fails on Firefox when textarea isn't part of the document
1937 };
1938 }
1939 // IE model. Don't ask.
1940 else {
1941 var selRange = function(te) {
1942 try {var range = te.ownerDocument.selection.createRange();}
1943 catch(e) {return null;}
1944 if (!range || range.parentElement() != te) return null;
1945 var val = te.value, len = val.length, localRange = te.createTextRange();
1946 localRange.moveToBookmark(range.getBookmark());
1947 var endRange = te.createTextRange();
1948 endRange.collapse(false);
1949
1950 if (localRange.compareEndPoints("StartToEnd", endRange) > -1)
1951 return {start: len, end: len};
1952
1953 var start = -localRange.moveStart("character", -len);
1954 for (var i = val.indexOf("\r"); i > -1 && i < start; i = val.indexOf("\r", i+1), start++) {}
1955
1956 if (localRange.compareEndPoints("EndToEnd", endRange) > -1)
1957 return {start: start, end: len};
1958
1959 var end = -localRange.moveEnd("character", -len);
1960 for (var i = val.indexOf("\r"); i > -1 && i < end; i = val.indexOf("\r", i+1), end++) {}
1961 return {start: start, end: end};
1962 };
1963 var setSelRange = function(te, start, end) {
1964 var range = te.createTextRange();
1965 range.collapse(true);
1966 var endrange = range.duplicate();
1967 var newlines = 0, txt = te.value;
1968 for (var pos = txt.indexOf("\n"); pos > -1 && pos < start; pos = txt.indexOf("\n", pos + 1))
1969 ++newlines;
1970 range.move("character", start - newlines);
1971 for (; pos > -1 && pos < end; pos = txt.indexOf("\n", pos + 1))
1972 ++newlines;
1973 endrange.move("character", end - newlines);
1974 range.setEndPoint("EndToEnd", endrange);
1975 range.select();
1976 };
1977 }
1978
1979 CodeMirror.defineMode("null", function() {
1980 return {token: function(stream) {stream.skipToEnd();}};
1981 });
1982 CodeMirror.defineMIME("text/plain", "null");
1983
1984 return CodeMirror;
1985 })();
@@ -0,0 +1,51 b''
1 // Utility function that allows modes to be combined. The mode given
2 // as the base argument takes care of most of the normal mode
3 // functionality, but a second (typically simple) mode is used, which
4 // can override the style of text. Both modes get to parse all of the
5 // text, but when both assign a non-null style to a piece of code, the
6 // overlay wins, unless the combine argument was true, in which case
7 // the styles are combined.
8
9 CodeMirror.overlayParser = function(base, overlay, combine) {
10 return {
11 startState: function() {
12 return {
13 base: CodeMirror.startState(base),
14 overlay: CodeMirror.startState(overlay),
15 basePos: 0, baseCur: null,
16 overlayPos: 0, overlayCur: null
17 };
18 },
19 copyState: function(state) {
20 return {
21 base: CodeMirror.copyState(base, state.base),
22 overlay: CodeMirror.copyState(overlay, state.overlay),
23 basePos: state.basePos, baseCur: null,
24 overlayPos: state.overlayPos, overlayCur: null
25 };
26 },
27
28 token: function(stream, state) {
29 if (stream.start == state.basePos) {
30 state.baseCur = base.token(stream, state.base);
31 state.basePos = stream.pos;
32 }
33 if (stream.start == state.overlayPos) {
34 stream.pos = stream.start;
35 state.overlayCur = overlay.token(stream, state.overlay);
36 state.overlayPos = stream.pos;
37 }
38 stream.pos = Math.min(state.basePos, state.overlayPos);
39 if (stream.eol()) state.basePos = state.overlayPos = 0;
40
41 if (state.overlayCur == null) return state.baseCur;
42 if (state.baseCur != null && combine) return state.baseCur + " " + state.overlayCur;
43 else return state.overlayCur;
44 },
45
46 indent: function(state, textAfter) {
47 return base.indent(state.base, textAfter);
48 },
49 electricChars: base.electricChars
50 };
51 };
@@ -0,0 +1,74 b''
1 CodeMirror.defineMode("htmlmixed", function(config, parserConfig) {
2 var htmlMode = CodeMirror.getMode(config, {name: "xml", htmlMode: true});
3 var jsMode = CodeMirror.getMode(config, "javascript");
4 var cssMode = CodeMirror.getMode(config, "css");
5
6 function html(stream, state) {
7 var style = htmlMode.token(stream, state.htmlState);
8 if (style == "xml-tag" && stream.current() == ">" && state.htmlState.context) {
9 if (/^script$/i.test(state.htmlState.context.tagName)) {
10 state.token = javascript;
11 state.localState = jsMode.startState(htmlMode.indent(state.htmlState, ""));
12 }
13 else if (/^style$/i.test(state.htmlState.context.tagName)) {
14 state.token = css;
15 state.localState = cssMode.startState(htmlMode.indent(state.htmlState, ""));
16 }
17 }
18 return style;
19 }
20 function maybeBackup(stream, pat, style) {
21 var cur = stream.current();
22 var close = cur.search(pat);
23 if (close > -1) stream.backUp(cur.length - close);
24 return style;
25 }
26 function javascript(stream, state) {
27 if (stream.match(/^<\/\s*script\s*>/i, false)) {
28 state.token = html;
29 state.curState = null;
30 return html(stream, state);
31 }
32 return maybeBackup(stream, /<\/\s*script\s*>/,
33 jsMode.token(stream, state.localState));
34 }
35 function css(stream, state) {
36 if (stream.match(/^<\/\s*style\s*>/i, false)) {
37 state.token = html;
38 state.localState = null;
39 return html(stream, state);
40 }
41 return maybeBackup(stream, /<\/\s*style\s*>/,
42 cssMode.token(stream, state.localState));
43 }
44
45 return {
46 startState: function() {
47 var state = htmlMode.startState();
48 return {token: html, localState: null, htmlState: state};
49 },
50
51 copyState: function(state) {
52 if (state.localState)
53 var local = CodeMirror.copyState(state.token == css ? cssMode : jsMode, state.localState);
54 return {token: state.token, localState: local, htmlState: CodeMirror.copyState(htmlMode, state.htmlState)};
55 },
56
57 token: function(stream, state) {
58 return state.token(stream, state);
59 },
60
61 indent: function(state, textAfter) {
62 if (state.token == html || /^\s*<\//.test(textAfter))
63 return htmlMode.indent(state.htmlState, textAfter);
64 else if (state.token == javascript)
65 return jsMode.indent(state.localState, textAfter);
66 else
67 return cssMode.indent(state.localState, textAfter);
68 },
69
70 electricChars: "/{}:"
71 }
72 });
73
74 CodeMirror.defineMIME("text/html", "htmlmixed");
@@ -0,0 +1,54 b''
1 <!doctype html>
2 <html>
3 <head>
4 <title>CodeMirror 2: HTML mixed mode</title>
5 <link rel="stylesheet" href="../../lib/codemirror.css">
6 <script src="../../lib/codemirror.js"></script>
7 <script src="../xml/xml.js"></script>
8 <link rel="stylesheet" href="../xml/xml.css">
9 <script src="../javascript/javascript.js"></script>
10 <link rel="stylesheet" href="../javascript/javascript.css">
11 <script src="../css/css.js"></script>
12 <link rel="stylesheet" href="../css/css.css">
13 <script src="htmlmixed.js"></script>
14 <link rel="stylesheet" href="../../css/docs.css">
15 <style>.CodeMirror {border-top: 1px solid black; border-bottom: 1px solid black;}</style>
16 </head>
17 <body>
18 <h1>CodeMirror 2: HTML mixed mode</h1>
19 <form><textarea id="code" name="code">
20 <html style="color: green">
21 <!-- this is a comment -->
22 <head>
23 <title>Mixed HTML Example</title>
24 <style type="text/css">
25 h1 {font-family: comic sans; color: #f0f;}
26 div {background: yellow !important;}
27 body {
28 max-width: 50em;
29 margin: 1em 2em 1em 5em;
30 }
31 </style>
32 </head>
33 <body>
34 <h1>Mixed HTML Example</h1>
35 <script>
36 function jsFunc(arg1, arg2) {
37 if (arg1 && arg2) document.body.innerHTML = "achoo";
38 }
39 </script>
40 </body>
41 </html>
42 </textarea></form>
43 <script>
44 var editor = CodeMirror.fromTextArea(document.getElementById("code"), {mode: "text/html", tabMode: "indent"});
45 </script>
46
47 <p>The HTML mixed mode depends on the XML, JavaScript, and CSS modes.</p>
48
49 <p><strong>MIME types defined:</strong> <code>text/html</code>
50 (redefined, only takes effect if you load this parser after the
51 XML parser).</p>
52
53 </body>
54 </html>
@@ -0,0 +1,21 b''
1 The MIT License
2
3 Copyright (c) 2010 Timothy Farrell
4
5 Permission is hereby granted, free of charge, to any person obtaining a copy
6 of this software and associated documentation files (the "Software"), to deal
7 in the Software without restriction, including without limitation the rights
8 to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
9 copies of the Software, and to permit persons to whom the Software is
10 furnished to do so, subject to the following conditions:
11
12 The above copyright notice and this permission notice shall be included in
13 all copies or substantial portions of the Software.
14
15 THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
16 IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
17 FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
18 AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
19 LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
20 OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
21 THE SOFTWARE. No newline at end of file
@@ -0,0 +1,123 b''
1 <!doctype html>
2 <html>
3 <head>
4 <title>CodeMirror 2: Python mode</title>
5 <link rel="stylesheet" href="../../lib/codemirror.css">
6 <script src="../../lib/codemirror.js"></script>
7 <script src="python.js"></script>
8 <link rel="stylesheet" href="python.css">
9 <link rel="stylesheet" href="../../css/docs.css">
10 <style type="text/css">.CodeMirror {border-top: 1px solid black; border-bottom: 1px solid black;}</style>
11 </head>
12 <body>
13 <h1>CodeMirror 2: Python mode</h1>
14
15 <div><textarea id="code" name="code">
16 # Literals
17 1234
18 0.0e101
19 .123
20 0b01010011100
21 0o01234567
22 0x0987654321abcdef
23 7
24 2147483647
25 3L
26 79228162514264337593543950336L
27 0x100000000L
28 79228162514264337593543950336
29 0xdeadbeef
30 3.14j
31 10.j
32 10j
33 .001j
34 1e100j
35 3.14e-10j
36
37
38 # String Literals
39 'For\''
40 "God\""
41 """so loved
42 the world"""
43 '''that he gave
44 his only begotten\' '''
45 'that whosoever believeth \
46 in him'
47 ''
48
49 # Identifiers
50 __a__
51 a.b
52 a.b.c
53
54 # Operators
55 + - * / % & | ^ ~ < >
56 == != <= >= <> << >> // **
57 and or not in is
58
59 # Delimiters
60 () [] {} , : ` = ; @ . # Note that @ and . require the proper context.
61 += -= *= /= %= &= |= ^=
62 //= >>= <<= **=
63
64 # Keywords
65 as assert break class continue def del elif else except
66 finally for from global if import lambda pass raise
67 return try while with yield
68
69 # Python 2 Keywords (otherwise Identifiers)
70 exec print
71
72 # Python 3 Keywords (otherwise Identifiers)
73 nonlocal
74
75 # Types
76 bool classmethod complex dict enumerate float frozenset int list object
77 property reversed set slice staticmethod str super tuple type
78
79 # Python 2 Types (otherwise Identifiers)
80 basestring buffer file long unicode xrange
81
82 # Python 3 Types (otherwise Identifiers)
83 bytearray bytes filter map memoryview open range zip
84
85 # Some Example code
86 import os
87 from package import ParentClass
88
89 @nonsenseDecorator
90 def doesNothing():
91 pass
92
93 class ExampleClass(ParentClass):
94 @staticmethod
95 def example(inputStr):
96 a = list(inputStr)
97 a.reverse()
98 return ''.join(a)
99
100 def __init__(self, mixin = 'Hello'):
101 self.mixin = mixin
102
103 </textarea></div>
104 <script>
105 var editor = CodeMirror.fromTextArea(document.getElementById("code"), {
106 mode: {name: "python",
107 version: 2,
108 singleLineStringErrors: false},
109 lineNumbers: true,
110 indentUnit: 4,
111 tabMode: "shift",
112 matchBrackets: true
113 });
114 </script>
115 <h2>Configuration Options:</h2>
116 <ul>
117 <li>version - 2/3 - The version of Python to recognize. Default is 2.</li>
118 <li>singleLineStringErrors - true/false - If you have a single-line string that is not terminated at the end of the line, this will show subsequent lines as errors if true, otherwise it will consider the newline as the end of the string. Default is false.</li>
119 </ul>
120
121 <p><strong>MIME types defined:</strong> <code>text/x-python</code>.</p>
122 </body>
123 </html>
@@ -0,0 +1,23 b''
1 span.py-delimiter,
2 span.py-special {color: #666666;}
3
4 span.py-operator {color: #AA22FF; font-weight: bold;}
5
6 span.py-error {background-color: #000000; color: #FF0000;}
7
8 span.py-keyword {color: #008000; font-weight: bold;}
9
10 span.py-number {color: #666666;}
11
12 span.py-identifier,
13 span.py-func {color: #000000;}
14
15 span.py-type {color: #008000;}
16 span.py-decorator {color: #AA22FF;}
17
18 span.py-comment {color: #408080; font-style: italic;}
19
20 span.py-string,
21 span.py-bytes,
22 span.py-raw,
23 span.py-unicode {color: #BA2121;} No newline at end of file
@@ -0,0 +1,318 b''
1 CodeMirror.defineMode("python", function(conf) {
2 var ERRORCLASS = 'py-error';
3
4 function wordRegexp(words) {
5 return new RegExp("^((" + words.join(")|(") + "))\\b");
6 }
7
8 var singleOperators = new RegExp("^[\\+\\-\\*/%&|\\^~<>!]");
9 var singleDelimiters = new RegExp('^[\\(\\)\\[\\]\\{\\}@,:`=;\\.]');
10 var doubleOperators = new RegExp("^((==)|(!=)|(<=)|(>=)|(<>)|(<<)|(>>)|(//)|(\\*\\*))");
11 var doubleDelimiters = new RegExp("^((\\+=)|(\\-=)|(\\*=)|(%=)|(/=)|(&=)|(\\|=)|(\\^=))");
12 var tripleDelimiters = new RegExp("^((//=)|(>>=)|(<<=)|(\\*\\*=))");
13 var identifiers = new RegExp("^[_A-Za-z][_A-Za-z0-9]*");
14
15 var wordOperators = wordRegexp(['and', 'or', 'not', 'is', 'in']);
16 var commonkeywords = ['as', 'assert', 'break', 'class', 'continue',
17 'def', 'del', 'elif', 'else', 'except', 'finally',
18 'for', 'from', 'global', 'if', 'import',
19 'lambda', 'pass', 'raise', 'return',
20 'try', 'while', 'with', 'yield'];
21 var commontypes = ['bool', 'classmethod', 'complex', 'dict', 'enumerate',
22 'float', 'frozenset', 'int', 'list', 'object',
23 'property', 'reversed', 'set', 'slice', 'staticmethod',
24 'str', 'super', 'tuple', 'type'];
25 var py2 = {'types': ['basestring', 'buffer', 'file', 'long', 'unicode',
26 'xrange'],
27 'keywords': ['exec', 'print']};
28 var py3 = {'types': ['bytearray', 'bytes', 'filter', 'map', 'memoryview',
29 'open', 'range', 'zip'],
30 'keywords': ['nonlocal']};
31
32 if (!!conf.mode.version && parseInt(conf.mode.version, 10) === 3) {
33 commonkeywords = commonkeywords.concat(py3.keywords);
34 commontypes = commontypes.concat(py3.types);
35 var stringPrefixes = new RegExp("^(([rb]|(br))?('{3}|\"{3}|['\"]))", "i");
36 } else {
37 commonkeywords = commonkeywords.concat(py2.keywords);
38 commontypes = commontypes.concat(py2.types);
39 var stringPrefixes = new RegExp("^(([rub]|(ur)|(br))?('{3}|\"{3}|['\"]))", "i");
40 }
41 var keywords = wordRegexp(commonkeywords);
42 var types = wordRegexp(commontypes);
43
44 // tokenizers
45 function tokenBase(stream, state) {
46 // Handle scope changes
47 if (stream.sol()) {
48 var scopeOffset = state.scopes[0].offset;
49 if (stream.eatSpace()) {
50 var lineOffset = stream.indentation();
51 if (lineOffset > scopeOffset) {
52 return 'py-indent';
53 } else if (lineOffset < scopeOffset) {
54 return 'py-dedent';
55 }
56 return 'whitespace';
57 } else {
58 if (scopeOffset > 0) {
59 dedent(stream, state);
60 }
61 }
62 }
63 if (stream.eatSpace()) {
64 return 'py-space';
65 }
66
67 var ch = stream.peek();
68
69 // Handle Comments
70 if (ch === '#') {
71 stream.skipToEnd();
72 return 'py-comment';
73 }
74
75 // Handle Number Literals
76 if (stream.match(/^[0-9\.]/, false)) {
77 var floatLiteral = false;
78 // Floats
79 if (stream.match(/^\d*\.\d+(e[\+\-]?\d+)?/i)) { floatLiteral = true; }
80 if (stream.match(/^\d+\.\d*/)) { floatLiteral = true; }
81 if (stream.match(/^\.\d+/)) { floatLiteral = true; }
82 if (floatLiteral) {
83 // Float literals may be "imaginary"
84 stream.eat(/J/i);
85 return 'py-literal';
86 }
87 // Integers
88 var intLiteral = false;
89 // Hex
90 if (stream.match(/^0x[0-9a-f]+/i)) { intLiteral = true; }
91 // Binary
92 if (stream.match(/^0b[01]+/i)) { intLiteral = true; }
93 // Octal
94 if (stream.match(/^0o[0-7]+/i)) { intLiteral = true; }
95 // Decimal
96 if (stream.match(/^[1-9]\d*(e[\+\-]?\d+)?/)) {
97 // Decimal literals may be "imaginary"
98 stream.eat(/J/i);
99 // TODO - Can you have imaginary longs?
100 intLiteral = true;
101 }
102 // Zero by itself with no other piece of number.
103 if (stream.match(/^0(?![\dx])/i)) { intLiteral = true; }
104 if (intLiteral) {
105 // Integer literals may be "long"
106 stream.eat(/L/i);
107 return 'py-literal';
108 }
109 }
110
111 // Handle Strings
112 if (stream.match(stringPrefixes)) {
113 state.tokenize = tokenStringFactory(stream.current());
114 return state.tokenize(stream, state);
115 }
116
117 // Handle operators and Delimiters
118 if (stream.match(tripleDelimiters) || stream.match(doubleDelimiters)) {
119 return 'py-delimiter';
120 }
121 if (stream.match(doubleOperators)
122 || stream.match(singleOperators)
123 || stream.match(wordOperators)) {
124 return 'py-operator';
125 }
126 if (stream.match(singleDelimiters)) {
127 return 'py-delimiter';
128 }
129
130 if (stream.match(types)) {
131 return 'py-type';
132 }
133
134 if (stream.match(keywords)) {
135 return 'py-keyword';
136 }
137
138 if (stream.match(identifiers)) {
139 return 'py-identifier';
140 }
141
142 // Handle non-detected items
143 stream.next();
144 return ERRORCLASS;
145 }
146
147 function tokenStringFactory(delimiter) {
148 while ('rub'.indexOf(delimiter[0].toLowerCase()) >= 0) {
149 delimiter = delimiter.substr(1);
150 }
151 var delim_re = new RegExp(delimiter);
152 var singleline = delimiter.length == 1;
153 var OUTCLASS = 'py-string';
154
155 return function tokenString(stream, state) {
156 while (!stream.eol()) {
157 stream.eatWhile(/[^'"\\]/);
158 if (stream.eat('\\')) {
159 stream.next();
160 if (singleline && stream.eol()) {
161 return OUTCLASS;
162 }
163 } else if (stream.match(delim_re)) {
164 state.tokenize = tokenBase;
165 return OUTCLASS;
166 } else {
167 stream.eat(/['"]/);
168 }
169 }
170 if (singleline) {
171 if (conf.mode.singleLineStringErrors) {
172 OUTCLASS = ERRORCLASS
173 } else {
174 state.tokenize = tokenBase;
175 }
176 }
177 return OUTCLASS;
178 };
179 }
180
181 function indent(stream, state, type) {
182 type = type || 'py';
183 var indentUnit = 0;
184 if (type === 'py') {
185 for (var i = 0; i < state.scopes.length; ++i) {
186 if (state.scopes[i].type === 'py') {
187 indentUnit = state.scopes[i].offset + conf.indentUnit;
188 break;
189 }
190 }
191 } else {
192 indentUnit = stream.column() + stream.current().length;
193 }
194 state.scopes.unshift({
195 offset: indentUnit,
196 type: type
197 });
198 }
199
200 function dedent(stream, state) {
201 if (state.scopes.length == 1) return;
202 if (state.scopes[0].type === 'py') {
203 var _indent = stream.indentation();
204 var _indent_index = -1;
205 for (var i = 0; i < state.scopes.length; ++i) {
206 if (_indent === state.scopes[i].offset) {
207 _indent_index = i;
208 break;
209 }
210 }
211 if (_indent_index === -1) {
212 return true;
213 }
214 while (state.scopes[0].offset !== _indent) {
215 state.scopes.shift();
216 }
217 return false
218 } else {
219 state.scopes.shift();
220 return false;
221 }
222 }
223
224 function tokenLexer(stream, state) {
225 var style = state.tokenize(stream, state);
226 var current = stream.current();
227
228 // Handle '.' connected identifiers
229 if (current === '.') {
230 style = state.tokenize(stream, state);
231 current = stream.current();
232 if (style === 'py-identifier') {
233 return 'py-identifier';
234 } else {
235 return ERRORCLASS;
236 }
237 }
238
239 // Handle decorators
240 if (current === '@') {
241 style = state.tokenize(stream, state);
242 current = stream.current();
243 if (style === 'py-identifier'
244 || current === '@staticmethod'
245 || current === '@classmethod') {
246 return 'py-decorator';
247 } else {
248 return ERRORCLASS;
249 }
250 }
251
252 // Handle scope changes.
253 if (current === 'pass' || current === 'return') {
254 state.dedent += 1;
255 }
256 if ((current === ':' && !state.lambda && state.scopes[0].type == 'py')
257 || style === 'py-indent') {
258 indent(stream, state);
259 }
260 var delimiter_index = '[({'.indexOf(current);
261 if (delimiter_index !== -1) {
262 indent(stream, state, '])}'.slice(delimiter_index, delimiter_index+1));
263 }
264 if (style === 'py-dedent') {
265 if (dedent(stream, state)) {
266 return ERRORCLASS;
267 }
268 }
269 delimiter_index = '])}'.indexOf(current);
270 if (delimiter_index !== -1) {
271 if (dedent(stream, state)) {
272 return ERRORCLASS;
273 }
274 }
275 if (state.dedent > 0 && stream.eol() && state.scopes[0].type == 'py') {
276 if (state.scopes.length > 1) state.scopes.shift();
277 state.dedent -= 1;
278 }
279
280 return style;
281 }
282
283 var external = {
284 startState: function(basecolumn) {
285 return {
286 tokenize: tokenBase,
287 scopes: [{offset:basecolumn || 0, type:'py'}],
288 lastToken: null,
289 lambda: false,
290 dedent: 0
291 };
292 },
293
294 token: function(stream, state) {
295 var style = tokenLexer(stream, state);
296
297 state.lastToken = {style:style, content: stream.current()};
298
299 if (stream.eol() && stream.lambda) {
300 state.lambda = false;
301 }
302
303 return style;
304 },
305
306 indent: function(state, textAfter) {
307 if (state.tokenize != tokenBase) {
308 return 0;
309 }
310
311 return state.scopes[0].offset;
312 }
313
314 };
315 return external;
316 });
317
318 CodeMirror.defineMIME("text/x-python", "python");
This diff has been collapsed as it changes many lines, (526 lines changed) Show them Hide them
@@ -0,0 +1,526 b''
1 <!doctype html>
2 <html>
3 <head>
4 <title>CodeMirror 2: reStructuredText mode</title>
5 <link rel="stylesheet" href="../../lib/codemirror.css">
6 <script src="../../lib/codemirror.js"></script>
7 <script src="rst.js"></script>
8 <link rel="stylesheet" href="rst.css">
9 <style type="text/css">.CodeMirror {border-top: 1px solid black; border-bottom: 1px solid black;}</style>
10 <link rel="stylesheet" href="../../css/docs.css">
11 </head>
12 <body>
13 <h1>CodeMirror 2: reStructuredText mode</h1>
14
15 <form><textarea id="code" name="code">
16 .. This is an excerpt from Sphinx documentation: http://sphinx.pocoo.org/_sources/rest.txt
17
18 .. highlightlang:: rest
19
20 .. _rst-primer:
21
22 reStructuredText Primer
23 =======================
24
25 This section is a brief introduction to reStructuredText (reST) concepts and
26 syntax, intended to provide authors with enough information to author documents
27 productively. Since reST was designed to be a simple, unobtrusive markup
28 language, this will not take too long.
29
30 .. seealso::
31
32 The authoritative `reStructuredText User Documentation
33 &lt;http://docutils.sourceforge.net/rst.html&gt;`_. The "ref" links in this
34 document link to the description of the individual constructs in the reST
35 reference.
36
37
38 Paragraphs
39 ----------
40
41 The paragraph (:duref:`ref &lt;paragraphs&gt;`) is the most basic block in a reST
42 document. Paragraphs are simply chunks of text separated by one or more blank
43 lines. As in Python, indentation is significant in reST, so all lines of the
44 same paragraph must be left-aligned to the same level of indentation.
45
46
47 .. _inlinemarkup:
48
49 Inline markup
50 -------------
51
52 The standard reST inline markup is quite simple: use
53
54 * one asterisk: ``*text*`` for emphasis (italics),
55 * two asterisks: ``**text**`` for strong emphasis (boldface), and
56 * backquotes: ````text```` for code samples.
57
58 If asterisks or backquotes appear in running text and could be confused with
59 inline markup delimiters, they have to be escaped with a backslash.
60
61 Be aware of some restrictions of this markup:
62
63 * it may not be nested,
64 * content may not start or end with whitespace: ``* text*`` is wrong,
65 * it must be separated from surrounding text by non-word characters. Use a
66 backslash escaped space to work around that: ``thisis\ *one*\ word``.
67
68 These restrictions may be lifted in future versions of the docutils.
69
70 reST also allows for custom "interpreted text roles"', which signify that the
71 enclosed text should be interpreted in a specific way. Sphinx uses this to
72 provide semantic markup and cross-referencing of identifiers, as described in
73 the appropriate section. The general syntax is ``:rolename:`content```.
74
75 Standard reST provides the following roles:
76
77 * :durole:`emphasis` -- alternate spelling for ``*emphasis*``
78 * :durole:`strong` -- alternate spelling for ``**strong**``
79 * :durole:`literal` -- alternate spelling for ````literal````
80 * :durole:`subscript` -- subscript text
81 * :durole:`superscript` -- superscript text
82 * :durole:`title-reference` -- for titles of books, periodicals, and other
83 materials
84
85 See :ref:`inline-markup` for roles added by Sphinx.
86
87
88 Lists and Quote-like blocks
89 ---------------------------
90
91 List markup (:duref:`ref &lt;bullet-lists&gt;`) is natural: just place an asterisk at
92 the start of a paragraph and indent properly. The same goes for numbered lists;
93 they can also be autonumbered using a ``#`` sign::
94
95 * This is a bulleted list.
96 * It has two items, the second
97 item uses two lines.
98
99 1. This is a numbered list.
100 2. It has two items too.
101
102 #. This is a numbered list.
103 #. It has two items too.
104
105
106 Nested lists are possible, but be aware that they must be separated from the
107 parent list items by blank lines::
108
109 * this is
110 * a list
111
112 * with a nested list
113 * and some subitems
114
115 * and here the parent list continues
116
117 Definition lists (:duref:`ref &lt;definition-lists&gt;`) are created as follows::
118
119 term (up to a line of text)
120 Definition of the term, which must be indented
121
122 and can even consist of multiple paragraphs
123
124 next term
125 Description.
126
127 Note that the term cannot have more than one line of text.
128
129 Quoted paragraphs (:duref:`ref &lt;block-quotes&gt;`) are created by just indenting
130 them more than the surrounding paragraphs.
131
132 Line blocks (:duref:`ref &lt;line-blocks&gt;`) are a way of preserving line breaks::
133
134 | These lines are
135 | broken exactly like in
136 | the source file.
137
138 There are also several more special blocks available:
139
140 * field lists (:duref:`ref &lt;field-lists&gt;`)
141 * option lists (:duref:`ref &lt;option-lists&gt;`)
142 * quoted literal blocks (:duref:`ref &lt;quoted-literal-blocks&gt;`)
143 * doctest blocks (:duref:`ref &lt;doctest-blocks&gt;`)
144
145
146 Source Code
147 -----------
148
149 Literal code blocks (:duref:`ref &lt;literal-blocks&gt;`) are introduced by ending a
150 paragraph with the special marker ``::``. The literal block must be indented
151 (and, like all paragraphs, separated from the surrounding ones by blank lines)::
152
153 This is a normal text paragraph. The next paragraph is a code sample::
154
155 It is not processed in any way, except
156 that the indentation is removed.
157
158 It can span multiple lines.
159
160 This is a normal text paragraph again.
161
162 The handling of the ``::`` marker is smart:
163
164 * If it occurs as a paragraph of its own, that paragraph is completely left
165 out of the document.
166 * If it is preceded by whitespace, the marker is removed.
167 * If it is preceded by non-whitespace, the marker is replaced by a single
168 colon.
169
170 That way, the second sentence in the above example's first paragraph would be
171 rendered as "The next paragraph is a code sample:".
172
173
174 .. _rst-tables:
175
176 Tables
177 ------
178
179 Two forms of tables are supported. For *grid tables* (:duref:`ref
180 &lt;grid-tables&gt;`), you have to "paint" the cell grid yourself. They look like
181 this::
182
183 +------------------------+------------+----------+----------+
184 | Header row, column 1 | Header 2 | Header 3 | Header 4 |
185 | (header rows optional) | | | |
186 +========================+============+==========+==========+
187 | body row 1, column 1 | column 2 | column 3 | column 4 |
188 +------------------------+------------+----------+----------+
189 | body row 2 | ... | ... | |
190 +------------------------+------------+----------+----------+
191
192 *Simple tables* (:duref:`ref &lt;simple-tables&gt;`) are easier to write, but
193 limited: they must contain more than one row, and the first column cannot
194 contain multiple lines. They look like this::
195
196 ===== ===== =======
197 A B A and B
198 ===== ===== =======
199 False False False
200 True False False
201 False True False
202 True True True
203 ===== ===== =======
204
205
206 Hyperlinks
207 ----------
208
209 External links
210 ^^^^^^^^^^^^^^
211
212 Use ```Link text &lt;http://example.com/&gt;`_`` for inline web links. If the link
213 text should be the web address, you don't need special markup at all, the parser
214 finds links and mail addresses in ordinary text.
215
216 You can also separate the link and the target definition (:duref:`ref
217 &lt;hyperlink-targets&gt;`), like this::
218
219 This is a paragraph that contains `a link`_.
220
221 .. _a link: http://example.com/
222
223
224 Internal links
225 ^^^^^^^^^^^^^^
226
227 Internal linking is done via a special reST role provided by Sphinx, see the
228 section on specific markup, :ref:`ref-role`.
229
230
231 Sections
232 --------
233
234 Section headers (:duref:`ref &lt;sections&gt;`) are created by underlining (and
235 optionally overlining) the section title with a punctuation character, at least
236 as long as the text::
237
238 =================
239 This is a heading
240 =================
241
242 Normally, there are no heading levels assigned to certain characters as the
243 structure is determined from the succession of headings. However, for the
244 Python documentation, this convention is used which you may follow:
245
246 * ``#`` with overline, for parts
247 * ``*`` with overline, for chapters
248 * ``=``, for sections
249 * ``-``, for subsections
250 * ``^``, for subsubsections
251 * ``"``, for paragraphs
252
253 Of course, you are free to use your own marker characters (see the reST
254 documentation), and use a deeper nesting level, but keep in mind that most
255 target formats (HTML, LaTeX) have a limited supported nesting depth.
256
257
258 Explicit Markup
259 ---------------
260
261 "Explicit markup" (:duref:`ref &lt;explicit-markup-blocks&gt;`) is used in reST for
262 most constructs that need special handling, such as footnotes,
263 specially-highlighted paragraphs, comments, and generic directives.
264
265 An explicit markup block begins with a line starting with ``..`` followed by
266 whitespace and is terminated by the next paragraph at the same level of
267 indentation. (There needs to be a blank line between explicit markup and normal
268 paragraphs. This may all sound a bit complicated, but it is intuitive enough
269 when you write it.)
270
271
272 .. _directives:
273
274 Directives
275 ----------
276
277 A directive (:duref:`ref &lt;directives&gt;`) is a generic block of explicit markup.
278 Besides roles, it is one of the extension mechanisms of reST, and Sphinx makes
279 heavy use of it.
280
281 Docutils supports the following directives:
282
283 * Admonitions: :dudir:`attention`, :dudir:`caution`, :dudir:`danger`,
284 :dudir:`error`, :dudir:`hint`, :dudir:`important`, :dudir:`note`,
285 :dudir:`tip`, :dudir:`warning` and the generic :dudir:`admonition`.
286 (Most themes style only "note" and "warning" specially.)
287
288 * Images:
289
290 - :dudir:`image` (see also Images_ below)
291 - :dudir:`figure` (an image with caption and optional legend)
292
293 * Additional body elements:
294
295 - :dudir:`contents` (a local, i.e. for the current file only, table of
296 contents)
297 - :dudir:`container` (a container with a custom class, useful to generate an
298 outer ``&lt;div&gt;`` in HTML)
299 - :dudir:`rubric` (a heading without relation to the document sectioning)
300 - :dudir:`topic`, :dudir:`sidebar` (special highlighted body elements)
301 - :dudir:`parsed-literal` (literal block that supports inline markup)
302 - :dudir:`epigraph` (a block quote with optional attribution line)
303 - :dudir:`highlights`, :dudir:`pull-quote` (block quotes with their own
304 class attribute)
305 - :dudir:`compound` (a compound paragraph)
306
307 * Special tables:
308
309 - :dudir:`table` (a table with title)
310 - :dudir:`csv-table` (a table generated from comma-separated values)
311 - :dudir:`list-table` (a table generated from a list of lists)
312
313 * Special directives:
314
315 - :dudir:`raw` (include raw target-format markup)
316 - :dudir:`include` (include reStructuredText from another file)
317 -- in Sphinx, when given an absolute include file path, this directive takes
318 it as relative to the source directory
319 - :dudir:`class` (assign a class attribute to the next element) [1]_
320
321 * HTML specifics:
322
323 - :dudir:`meta` (generation of HTML ``&lt;meta&gt;`` tags)
324 - :dudir:`title` (override document title)
325
326 * Influencing markup:
327
328 - :dudir:`default-role` (set a new default role)
329 - :dudir:`role` (create a new role)
330
331 Since these are only per-file, better use Sphinx' facilities for setting the
332 :confval:`default_role`.
333
334 Do *not* use the directives :dudir:`sectnum`, :dudir:`header` and
335 :dudir:`footer`.
336
337 Directives added by Sphinx are described in :ref:`sphinxmarkup`.
338
339 Basically, a directive consists of a name, arguments, options and content. (Keep
340 this terminology in mind, it is used in the next chapter describing custom
341 directives.) Looking at this example, ::
342
343 .. function:: foo(x)
344 foo(y, z)
345 :module: some.module.name
346
347 Return a line of text input from the user.
348
349 ``function`` is the directive name. It is given two arguments here, the
350 remainder of the first line and the second line, as well as one option
351 ``module`` (as you can see, options are given in the lines immediately following
352 the arguments and indicated by the colons). Options must be indented to the
353 same level as the directive content.
354
355 The directive content follows after a blank line and is indented relative to the
356 directive start.
357
358
359 Images
360 ------
361
362 reST supports an image directive (:dudir:`ref &lt;image&gt;`), used like so::
363
364 .. image:: gnu.png
365 (options)
366
367 When used within Sphinx, the file name given (here ``gnu.png``) must either be
368 relative to the source file, or absolute which means that they are relative to
369 the top source directory. For example, the file ``sketch/spam.rst`` could refer
370 to the image ``images/spam.png`` as ``../images/spam.png`` or
371 ``/images/spam.png``.
372
373 Sphinx will automatically copy image files over to a subdirectory of the output
374 directory on building (e.g. the ``_static`` directory for HTML output.)
375
376 Interpretation of image size options (``width`` and ``height``) is as follows:
377 if the size has no unit or the unit is pixels, the given size will only be
378 respected for output channels that support pixels (i.e. not in LaTeX output).
379 Other units (like ``pt`` for points) will be used for HTML and LaTeX output.
380
381 Sphinx extends the standard docutils behavior by allowing an asterisk for the
382 extension::
383
384 .. image:: gnu.*
385
386 Sphinx then searches for all images matching the provided pattern and determines
387 their type. Each builder then chooses the best image out of these candidates.
388 For instance, if the file name ``gnu.*`` was given and two files :file:`gnu.pdf`
389 and :file:`gnu.png` existed in the source tree, the LaTeX builder would choose
390 the former, while the HTML builder would prefer the latter.
391
392 .. versionchanged:: 0.4
393 Added the support for file names ending in an asterisk.
394
395 .. versionchanged:: 0.6
396 Image paths can now be absolute.
397
398
399 Footnotes
400 ---------
401
402 For footnotes (:duref:`ref &lt;footnotes&gt;`), use ``[#name]_`` to mark the footnote
403 location, and add the footnote body at the bottom of the document after a
404 "Footnotes" rubric heading, like so::
405
406 Lorem ipsum [#f1]_ dolor sit amet ... [#f2]_
407
408 .. rubric:: Footnotes
409
410 .. [#f1] Text of the first footnote.
411 .. [#f2] Text of the second footnote.
412
413 You can also explicitly number the footnotes (``[1]_``) or use auto-numbered
414 footnotes without names (``[#]_``).
415
416
417 Citations
418 ---------
419
420 Standard reST citations (:duref:`ref &lt;citations&gt;`) are supported, with the
421 additional feature that they are "global", i.e. all citations can be referenced
422 from all files. Use them like so::
423
424 Lorem ipsum [Ref]_ dolor sit amet.
425
426 .. [Ref] Book or article reference, URL or whatever.
427
428 Citation usage is similar to footnote usage, but with a label that is not
429 numeric or begins with ``#``.
430
431
432 Substitutions
433 -------------
434
435 reST supports "substitutions" (:duref:`ref &lt;substitution-definitions&gt;`), which
436 are pieces of text and/or markup referred to in the text by ``|name|``. They
437 are defined like footnotes with explicit markup blocks, like this::
438
439 .. |name| replace:: replacement *text*
440
441 or this::
442
443 .. |caution| image:: warning.png
444 :alt: Warning!
445
446 See the :duref:`reST reference for substitutions &lt;substitution-definitions&gt;`
447 for details.
448
449 If you want to use some substitutions for all documents, put them into
450 :confval:`rst_prolog` or put them into a separate file and include it into all
451 documents you want to use them in, using the :rst:dir:`include` directive. (Be
452 sure to give the include file a file name extension differing from that of other
453 source files, to avoid Sphinx finding it as a standalone document.)
454
455 Sphinx defines some default substitutions, see :ref:`default-substitutions`.
456
457
458 Comments
459 --------
460
461 Every explicit markup block which isn't a valid markup construct (like the
462 footnotes above) is regarded as a comment (:duref:`ref &lt;comments&gt;`). For
463 example::
464
465 .. This is a comment.
466
467 You can indent text after a comment start to form multiline comments::
468
469 ..
470 This whole indented block
471 is a comment.
472
473 Still in the comment.
474
475
476 Source encoding
477 ---------------
478
479 Since the easiest way to include special characters like em dashes or copyright
480 signs in reST is to directly write them as Unicode characters, one has to
481 specify an encoding. Sphinx assumes source files to be encoded in UTF-8 by
482 default; you can change this with the :confval:`source_encoding` config value.
483
484
485 Gotchas
486 -------
487
488 There are some problems one commonly runs into while authoring reST documents:
489
490 * **Separation of inline markup:** As said above, inline markup spans must be
491 separated from the surrounding text by non-word characters, you have to use a
492 backslash-escaped space to get around that. See `the reference
493 &lt;http://docutils.sf.net/docs/ref/rst/restructuredtext.html#inline-markup&gt;`_
494 for the details.
495
496 * **No nested inline markup:** Something like ``*see :func:`foo`*`` is not
497 possible.
498
499
500 .. rubric:: Footnotes
501
502 .. [1] When the default domain contains a :rst:dir:`class` directive, this directive
503 will be shadowed. Therefore, Sphinx re-exports it as :rst:dir:`rst-class`.
504 </textarea></form>
505
506 <script>
507 var editor = CodeMirror.fromTextArea(document.getElementById("code"), {
508 lineNumbers: true,
509 });
510 </script>
511 <p>The reStructuredText mode supports one configuration parameter:</p>
512 <dl>
513 <dt><code>verbatim (string)</code></dt>
514 <dd>A name or MIME type of a mode that will be used for highlighting
515 verbatim blocks. By default, reStructuredText mode uses uniform color
516 for whole block of verbatim text if no mode is given.</dd>
517 </dl>
518 <p>If <code>python</code> mode is available (not a part of CodeMirror 2 yet),
519 it will be used for highlighting blocks containing Python/IPython terminal
520 sessions (blocks starting with <code>&gt;&gt;&gt;</code> (for Python) or
521 <code>In [num]:</code> (for IPython).
522
523 <p><strong>MIME types defined:</strong> <code>text/x-rst</code>.</p>
524 </body>
525 </html>
526
@@ -0,0 +1,77 b''
1
2 span.rst-emphasis {
3 font-style: italic;
4 }
5
6 span.rst-strong {
7 font-weight: bold;
8 }
9
10 span.rst-interpreted {
11 color: #33cc66;
12 }
13
14 span.rst-inline {
15 color: #3399cc;
16 }
17
18 span.rst-role {
19 color: #666699;
20 }
21
22 span.rst-list {
23 color: #cc0099;
24 font-weight: bold;
25 }
26
27 span.rst-body {
28 color: #6699cc;
29 }
30
31 span.rst-verbatim {
32 color: #3366ff;
33 }
34
35 span.rst-comment {
36 color: #aa7700;
37 }
38
39 span.rst-directive {
40 font-weight: bold;
41 color: #3399ff;
42 }
43
44 span.rst-hyperlink {
45 font-weight: bold;
46 color: #3366ff;
47 }
48
49 span.rst-footnote {
50 font-weight: bold;
51 color: #3333ff;
52 }
53
54 span.rst-citation {
55 font-weight: bold;
56 color: #3300ff;
57 }
58
59 span.rst-replacement {
60 color: #9933cc;
61 }
62
63 span.rst-section {
64 font-weight: bold;
65 color: #cc0099;
66 }
67
68 span.rst-directive-marker {
69 font-weight: bold;
70 color: #3399ff;
71 }
72
73 span.rst-verbatim-marker {
74 font-weight: bold;
75 color: #9900ff;
76 }
77
@@ -0,0 +1,335 b''
1
2 CodeMirror.defineMode('rst', function(config, options) {
3 function setState(state, fn, ctx) {
4 state.fn = fn;
5 setCtx(state, ctx);
6 }
7
8 function setCtx(state, ctx) {
9 state.ctx = ctx || {};
10 }
11
12 function setNormal(state, ch) {
13 if (ch && (typeof ch !== 'string')) {
14 var str = ch.current();
15 ch = str[str.length-1];
16 }
17
18 setState(state, normal, {back: ch});
19 }
20
21 function hasMode(mode) {
22 if (mode) {
23 var modes = CodeMirror.listModes();
24
25 for (var i in modes) {
26 if (modes[i] == mode) {
27 return true;
28 }
29 }
30 }
31
32 return false;
33 }
34
35 function getMode(mode) {
36 if (hasMode(mode)) {
37 return CodeMirror.getMode(config, mode);
38 } else {
39 return null;
40 }
41 }
42
43 var verbatimMode = getMode(options.verbatim);
44 var pythonMode = getMode('python');
45
46 var reSection = /^[!"#$%&'()*+,-./:;<=>?@[\\\]^_`{|}~]/;
47 var reDirective = /^\s*\w([-:.\w]*\w)?::(\s|$)/;
48 var reHyperlink = /^\s*_[\w-]+:(\s|$)/;
49 var reFootnote = /^\s*\[(\d+|#)\](\s|$)/;
50 var reCitation = /^\s*\[[A-Za-z][\w-]*\](\s|$)/;
51 var reFootnoteRef = /^\[(\d+|#)\]_/;
52 var reCitationRef = /^\[[A-Za-z][\w-]*\]_/;
53 var reDirectiveMarker = /^\.\.(\s|$)/;
54 var reVerbatimMarker = /^::\s*$/;
55 var rePreInline = /^[-\s"([{</:]/;
56 var rePostInline = /^[-\s`'")\]}>/:.,;!?\\_]/;
57 var reEnumeratedList = /^\s*((\d+|[A-Za-z#])[.)]|\((\d+|[A-Z-a-z#])\))\s/;
58 var reBulletedList = /^\s*[-\+\*]\s/;
59 var reExamples = /^\s+(>>>|In \[\d+\]:)\s/;
60
61 function normal(stream, state) {
62 var ch, sol, i;
63
64 if (stream.eat(/\\/)) {
65 ch = stream.next();
66 setNormal(state, ch);
67 return null;
68 }
69
70 sol = stream.sol();
71
72 if (sol && (ch = stream.eat(reSection))) {
73 for (i = 0; stream.eat(ch); i++);
74
75 if (i >= 3 && stream.match(/^\s*$/)) {
76 setNormal(state, null);
77 return 'rst-section';
78 } else {
79 stream.backUp(i + 1);
80 }
81 }
82
83 if (sol && stream.match(reDirectiveMarker)) {
84 if (!stream.eol()) {
85 setState(state, directive);
86 }
87
88 return 'rst-directive-marker';
89 }
90
91 if (stream.match(reVerbatimMarker)) {
92 if (!verbatimMode) {
93 setState(state, verbatim);
94 } else {
95 var mode = verbatimMode;
96
97 setState(state, verbatim, {
98 mode: mode,
99 local: mode.startState()
100 });
101 }
102
103 return 'rst-verbatim-marker';
104 }
105
106 if (sol && stream.match(reExamples, false)) {
107 if (!pythonMode) {
108 setState(state, verbatim);
109 return 'rst-verbatim-marker';
110 } else {
111 var mode = pythonMode;
112
113 setState(state, verbatim, {
114 mode: mode,
115 local: mode.startState()
116 });
117
118 return null;
119 }
120 }
121
122 if (sol && (stream.match(reEnumeratedList) ||
123 stream.match(reBulletedList))) {
124 setNormal(state, stream);
125 return 'rst-list';
126 }
127
128 function testBackward(re) {
129 return sol || !state.ctx.back || re.test(state.ctx.back);
130 }
131
132 function testForward(re) {
133 return stream.eol() || stream.match(re, false);
134 }
135
136 function testInline(re) {
137 return stream.match(re) && testBackward(/\W/) && testForward(/\W/);
138 }
139
140 if (testInline(reFootnoteRef)) {
141 setNormal(state, stream);
142 return 'rst-footnote';
143 }
144
145 if (testInline(reCitationRef)) {
146 setNormal(state, stream);
147 return 'rst-citation';
148 }
149
150 ch = stream.next();
151
152 if (testBackward(rePreInline)) {
153 if ((ch === ':' || ch === '|') && stream.eat(/\S/)) {
154 var token;
155
156 if (ch === ':') {
157 token = 'rst-role';
158 } else {
159 token = 'rst-replacement';
160 }
161
162 setState(state, inline, {
163 ch: ch,
164 wide: false,
165 prev: null,
166 token: token
167 });
168
169 return token;
170 }
171
172 if (ch === '*' || ch === '`') {
173 var orig = ch,
174 wide = false;
175
176 ch = stream.next();
177
178 if (ch == orig) {
179 wide = true;
180 ch = stream.next();
181 }
182
183 if (ch && !/\s/.test(ch)) {
184 var token;
185
186 if (orig === '*') {
187 token = wide ? 'rst-strong' : 'rst-emphasis';
188 } else {
189 token = wide ? 'rst-inline' : 'rst-interpreted';
190 }
191
192 setState(state, inline, {
193 ch: orig, // inline() has to know what to search for
194 wide: wide, // are we looking for `ch` or `chch`
195 prev: null, // terminator must not be preceeded with whitespace
196 token: token // I don't want to recompute this all the time
197 });
198
199 return token;
200 }
201 }
202 }
203
204 setNormal(state, ch);
205 return null;
206 }
207
208 function inline(stream, state) {
209 var ch = stream.next(),
210 token = state.ctx.token;
211
212 function finish(ch) {
213 state.ctx.prev = ch;
214 return token;
215 }
216
217 if (ch != state.ctx.ch) {
218 return finish(ch);
219 }
220
221 if (/\s/.test(state.ctx.prev)) {
222 return finish(ch);
223 }
224
225 if (state.ctx.wide) {
226 ch = stream.next();
227
228 if (ch != state.ctx.ch) {
229 return finish(ch);
230 }
231 }
232
233 if (!stream.eol() && !rePostInline.test(stream.peek())) {
234 if (state.ctx.wide) {
235 stream.backUp(1);
236 }
237
238 return finish(ch);
239 }
240
241 setState(state, normal);
242 setNormal(state, ch);
243
244 return token;
245 }
246
247 function directive(stream, state) {
248 var token = null;
249
250 if (stream.match(reDirective)) {
251 token = 'rst-directive';
252 } else if (stream.match(reHyperlink)) {
253 token = 'rst-hyperlink';
254 } else if (stream.match(reFootnote)) {
255 token = 'rst-footnote';
256 } else if (stream.match(reCitation)) {
257 token = 'rst-citation';
258 } else {
259 stream.eatSpace();
260
261 if (stream.eol()) {
262 setNormal(state, stream);
263 return null;
264 } else {
265 stream.skipToEnd();
266 setState(state, comment);
267 return 'rst-comment';
268 }
269 }
270
271 setState(state, body, {start: true});
272 return token;
273 }
274
275 function body(stream, state) {
276 var token = 'rst-body';
277
278 if (!state.ctx.start || stream.sol()) {
279 return block(stream, state, token);
280 }
281
282 stream.skipToEnd();
283 setCtx(state);
284
285 return token;
286 }
287
288 function comment(stream, state) {
289 return block(stream, state, 'rst-comment');
290 }
291
292 function verbatim(stream, state) {
293 if (!verbatimMode) {
294 return block(stream, state, 'rst-verbatim');
295 } else {
296 if (stream.sol()) {
297 if (!stream.eatSpace()) {
298 setNormal(state, stream);
299 }
300
301 return null;
302 }
303
304 return verbatimMode.token(stream, state.ctx.local);
305 }
306 }
307
308 function block(stream, state, token) {
309 if (stream.eol() || stream.eatSpace()) {
310 stream.skipToEnd();
311 return token;
312 } else {
313 setNormal(state, stream);
314 return null;
315 }
316 }
317
318 return {
319 startState: function() {
320 return {fn: normal, ctx: {}};
321 },
322
323 copyState: function(state) {
324 return {fn: state.fn, ctx: state.ctx};
325 },
326
327 token: function(stream, state) {
328 var token = state.fn(stream, state);
329 return token;
330 }
331 };
332 });
333
334 CodeMirror.defineMIME("text/x-rst", "rst");
335
@@ -144,18 +144,7 b' div.prompt {'
144 144 }
145 145
146 146 div.input_prompt {
147 color: blue;
148 }
149
150 textarea.input_textarea {
151 width: 100%;
152 text-align: left;
153 border-style: none;
154 padding: 0px;
155 margin: 0px;
156 overflow: auto;
157 outline: none;
158 resize: none;
147 color: navy;
159 148 }
160 149
161 150 div.output {
@@ -164,7 +153,7 b' div.output {'
164 153 }
165 154
166 155 div.output_prompt {
167 color: red;
156 color: darkred;
168 157 }
169 158
170 159 div.output_area {
@@ -131,13 +131,6 b' Notebook.prototype.bind_events = function () {'
131 131 };
132 132 };
133 133 }
134 } else if (event.which == 9) {
135 event.preventDefault();
136 var cell = that.selected_cell();
137 if (cell instanceof CodeCell) {
138 var ta = cell.element.find("textarea.input_textarea");
139 ta.val(ta.val() + " ");
140 };
141 134 };
142 135 });
143 136 };
@@ -442,7 +435,7 b' Notebook.prototype._kernel_started = function () {'
442 435
443 436 this.kernel.shell_channel.onmessage = function (e) {
444 437 reply = $.parseJSON(e.data);
445 console.log(reply);
438 // console.log(reply);
446 439 var msg_type = reply.msg_type;
447 440 var cell = that.cell_for_msg(reply.parent_header.msg_id);
448 441 if (msg_type === "execute_reply") {
@@ -453,7 +446,7 b' Notebook.prototype._kernel_started = function () {'
453 446 this.kernel.iopub_channel.onmessage = function (e) {
454 447 reply = $.parseJSON(e.data);
455 448 var content = reply.content;
456 console.log(reply);
449 // console.log(reply);
457 450 var msg_type = reply.msg_type;
458 451 var cell = that.cell_for_msg(reply.parent_header.msg_id);
459 452 if (msg_type === "stream") {
@@ -650,8 +643,9 b' Cell.prototype.create_element = function () {};'
650 643
651 644
652 645 var CodeCell = function (notebook) {
653 Cell.apply(this, arguments);
646 this.code_mirror = null;
654 647 this.input_prompt_number = ' ';
648 Cell.apply(this, arguments);
655 649 };
656 650
657 651
@@ -662,8 +656,13 b' CodeCell.prototype.create_element = function () {'
662 656 var cell = $('<div></div>').addClass('cell code_cell vbox border-box-sizing');
663 657 var input = $('<div></div>').addClass('input hbox border-box-sizing');
664 658 input.append($('<div/>').addClass('prompt input_prompt monospace-font'));
665 var input_textarea = $('<textarea/>').addClass('input_textarea monospace-font').attr('rows',1).attr('wrap','hard').autogrow();
666 input.append($('<div/>').addClass('input_area box-flex1 border-box-sizing').append(input_textarea));
659 var input_area = $('<div/>').addClass('input_area box-flex1 border-box-sizing');
660 this.code_mirror = CodeMirror(input_area.get(0), {
661 indentUnit : 4,
662 enterMode : 'flat',
663 tabMode: 'shift'
664 });
665 input.append(input_area);
667 666 var output = $('<div></div>').addClass('output vbox border-box-sizing');
668 667 cell.append(input).append(output);
669 668 this.element = cell;
@@ -671,6 +670,12 b' CodeCell.prototype.create_element = function () {'
671 670 };
672 671
673 672
673 CodeCell.prototype.select = function () {
674 Cell.prototype.select.apply(this);
675 this.code_mirror.focus();
676 };
677
678
674 679 CodeCell.prototype.append_pyout = function (data, n) {
675 680 var toinsert = $("<div/>").addClass("output_area output_pyout hbox monospace-font");
676 681 toinsert.append($('<div/>').
@@ -768,12 +773,12 b' CodeCell.prototype.set_input_prompt = function (number) {'
768 773
769 774
770 775 CodeCell.prototype.get_code = function () {
771 return this.element.find("textarea.input_textarea").val();
776 return this.code_mirror.getValue();
772 777 };
773 778
774 779
775 780 CodeCell.prototype.set_code = function (code) {
776 return this.element.find("textarea.input_textarea").val(code);
781 return this.code_mirror.setValue(code);
777 782 };
778 783
779 784
@@ -781,7 +786,6 b' CodeCell.prototype.fromJSON = function (data) {'
781 786 if (data.cell_type === 'code') {
782 787 this.set_code(data.code);
783 788 this.set_input_prompt(data.prompt_number);
784 this.grow(this.element.find("textarea.input_textarea"));
785 789 };
786 790 };
787 791
@@ -6,9 +6,6 b''
6 6
7 7 <title>IPython Notebook</title>
8 8
9 <link rel="stylesheet" href="static/css/layout.css" type="text/css" />
10 <link rel="stylesheet" href="static/css/notebook.css" type="text/css" />
11
12 9 <link rel="stylesheet" href="static/jquery/css/themes/aristo/jquery-wijmo.css" type="text/css" />
13 10 <!-- <link rel="stylesheet" href="static/jquery/css/themes/rocket/jquery-wijmo.css" type="text/css" /> -->
14 11 <!-- <link rel="stylesheet" href="static/jquery/css/themes/smoothness/jquery-ui-1.8.11.custom.css" type="text/css" /> -->
@@ -22,6 +19,12 b''
22 19 }
23 20 </script>
24 21
22 <link rel="stylesheet" href="static/codemirror2/lib/codemirror.css">
23 <link rel="stylesheet" href="static/codemirror2/mode/python/python.css">
24
25 <link rel="stylesheet" href="static/css/layout.css" type="text/css" />
26 <link rel="stylesheet" href="static/css/notebook.css" type="text/css" />
27
25 28 </head>
26 29
27 30 <body>
@@ -94,6 +97,8 b''
94 97 <script src="static/jquery/js/jquery-ui-1.8.11.custom.min.js" type="text/javascript" charset="utf-8"></script>
95 98 <script src="static/jquery/js/jquery.autogrow.js" type="text/javascript" charset="utf-8"></script>
96 99 <script src="static/js/notebook.js" type="text/javascript" charset="utf-8"></script>
100 <script src="static/codemirror2/lib/codemirror.js"></script>
101 <script src="static/codemirror2/mode/python/python.js"></script>
97 102
98 103 </body>
99 104
General Comments 0
You need to be logged in to leave comments. Login now