From 3d787908adcba42a8565bf9ee45d39505ab5b8f8 Mon Sep 17 00:00:00 2001 From: Marijn Haverbeke Date: Thu, 23 Aug 2012 09:45:16 +0200 Subject: [PATCH 0001/5780] Make the linter complain about byte-order marks in files --- test/lint/lint.js | 4 +++- 1 file changed, 3 insertions(+), 1 deletion(-) diff --git a/test/lint/lint.js b/test/lint/lint.js index bcd3e92f9f..1a3125cefa 100644 --- a/test/lint/lint.js +++ b/test/lint/lint.js @@ -93,9 +93,11 @@ function fail(msg, pos) { } function checkFile(fileName) { + curFile = fileName.match(/[^\/+]*\.js$/)[0]; var file = fs.readFileSync(fileName, "utf8"); + var badChar = file.match(/[\x00-\x08\x0b\x0c\x0e-\x19\uFEFF]/); + if (badChar) fail("Undesirable character " + badChar[0].charCodeAt(0) + " at position " + badChar.index); if (/^#!/.test(file)) file = file.slice(file.indexOf("\n") + 1); - curFile = fileName.match(/[^\/+]*\.js$/)[0]; try { var parsed = parse_js(file, true, true); } catch(e) { From ef27ccc0602eb9c31ed3d71ebd65fd8be9e2e38d Mon Sep 17 00:00:00 2001 From: Jan Keromnes Date: Thu, 23 Aug 2012 07:22:38 +0400 Subject: [PATCH 0002/5780] remove byte order mark in xml-hint.js --- lib/util/xml-hint.js | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/lib/util/xml-hint.js b/lib/util/xml-hint.js index 71f43ae931..816e3b4a69 100644 --- a/lib/util/xml-hint.js +++ b/lib/util/xml-hint.js @@ -1,4 +1,4 @@ - + (function() { CodeMirror.xmlHints = []; From 65e02a72b962adc142f1b1066199bf51c59682cc Mon Sep 17 00:00:00 2001 From: Marijn Haverbeke Date: Thu, 23 Aug 2012 09:48:36 +0200 Subject: [PATCH 0003/5780] Remove '2' from dev snapshot zip file name --- index.html | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/index.html b/index.html index 5bad41beff..7c6cadda4a 100644 --- a/index.html +++ b/index.html @@ -150,7 +150,7 @@

Getting the code

href="LICENSE">MIT-style license. To get it, you can download the latest release or the current development + href="http://codemirror.net/codemirror-latest.zip">development snapshot as zip files. To create a custom minified script file, you can use the compression API.

From 3d786d42095bff010fdc1c0a34bdab2c7cbf20f7 Mon Sep 17 00:00:00 2001 From: Marijn Haverbeke Date: Thu, 23 Aug 2012 10:23:02 +0200 Subject: [PATCH 0004/5780] Add Github android app to real-world uses --- index.html | 1 + 1 file changed, 1 insertion(+) diff --git a/index.html b/index.html index 7c6cadda4a..9399a27866 100644 --- a/index.html +++ b/index.html @@ -120,6 +120,7 @@

Real-world uses:

  • Adobe Brackets (code editor)
  • Mergely (interactive diffing)
  • Google Apps Script
  • +
  • GitHub's Android app
  • Eloquent JavaScript (book)
  • Zen Coding (fast XML editing)
  • Paper.js (graphics scripting)
  • From 3704970ff009c5f5405a60ec084604c6f8651f68 Mon Sep 17 00:00:00 2001 From: Marijn Haverbeke Date: Thu, 23 Aug 2012 11:00:42 +0200 Subject: [PATCH 0005/5780] Don't force a width on IE7 It produces a bogus horizontal scrollbar there. --- lib/codemirror.js | 6 ++++-- test/test.js | 2 +- 2 files changed, 5 insertions(+), 3 deletions(-) diff --git a/lib/codemirror.js b/lib/codemirror.js index 9ec673437b..552cc4ae01 100644 --- a/lib/codemirror.js +++ b/lib/codemirror.js @@ -1983,8 +1983,10 @@ window.CodeMirror = (function() { if (updateMaxLine) computeMaxLength(); if (maxLineChanged && !options.lineWrapping) { var cursorWidth = widthForcer.offsetWidth, left = measureLine(maxLine, maxLine.text.length).left; - widthForcer.style.left = left + "px"; - lineSpace.style.minWidth = (left + cursorWidth) + "px"; + if (!ie_lt8) { + widthForcer.style.left = left + "px"; + lineSpace.style.minWidth = (left + cursorWidth) + "px"; + } maxLineChanged = false; } var newScrollPos, updated; diff --git a/test/test.js b/test/test.js index 16227993e0..2c8e398c83 100644 --- a/test/test.js +++ b/test/test.js @@ -363,7 +363,7 @@ testCM("selectionPos", function(cm) { } } is(sawTop && sawBottom && sawMiddle, "all parts"); -}); +}, null, ie_lt8); testCM("restoreHistory", function(cm) { cm.setValue("abc\ndef"); From 805b5a44bdfd4559d6b3893969a32842594d2b2c Mon Sep 17 00:00:00 2001 From: Marijn Haverbeke Date: Thu, 23 Aug 2012 11:21:58 +0200 Subject: [PATCH 0006/5780] Mark release 2.33 --- doc/compress.html | 1 + doc/oldrelease.html | 12 ++++++++++++ index.html | 26 ++++++++++++++------------ package.json | 40 +++++++++++++--------------------------- 4 files changed, 40 insertions(+), 39 deletions(-) diff --git a/doc/compress.html b/doc/compress.html index 4438673522..3e4abc1479 100644 --- a/doc/compress.html +++ b/doc/compress.html @@ -27,6 +27,7 @@

    { } CodeMi

    Version:

    Click the line-number gutter to add or remove 'breakpoints'.

    diff --git a/doc/manual.html b/doc/manual.html index 6b4dc8c7b5..4d1a25957e 100644 --- a/doc/manual.html +++ b/doc/manual.html @@ -193,14 +193,17 @@

    Configuration

    lineNumberFormatter (function(integer))
    A function used to format line numbers. The function is passed the current line number. Default prints the line number verbatim.
    -
    gutter (boolean)
    -
    Can be used to force a 'gutter' (empty space on the left of - the editor) to be shown even when no line numbers are active. - This is useful for setting markers.
    - -
    fixedGutter (boolean)
    -
    When enabled (off by default), this will make the gutter - stay visible when the document is scrolled horizontally.
    +
    gutters (array)
    +
    Can be used to add extra gutters (beyond or instead of the + line number gutter). Should be an array of CSS class names, each + of which defines a width (and optionally a + background), and which will be used to draw the background of + the gutters. May include + the CodeMirror-linenumbers class, in order to + explicitly set the position of the line number gutter (it will + default to be to the right of all other gutters). These class + names are the keys passed + to setGutterMarker.
    readOnly (boolean)
    This disables editing of the editor content by the user. If @@ -238,8 +241,9 @@

    Configuration

    When given, will be called whenever the editor gutter (the line-number area) is clicked. Will be given the editor instance as first argument, the (zero-based) number of the line that was - clicked as second argument, and the raw mousedown - event object as third argument.
    + clicked as second argument, the raw mousedown event + object as third argument, and the CSS class of the gutter that + was clicked as fourth argument.
    onFocus, onBlur (function)
    The given functions will be called whenever the editor is @@ -421,25 +425,25 @@

    Customized Styling

    class. This is used to hide the cursor and give the selection a different color when the editor is not focused.
    -
    CodeMirror-gutter
    -
    Use this for giving a background or a border to the editor - gutter. Don't set any padding here, - use CodeMirror-gutter-text for that. By default, - the gutter is 'fluid', meaning it will adjust its width to the - maximum line number or line marker width. You can also set a - fixed width if you want.
    - -
    CodeMirror-gutter-text
    -
    Used to style the actual line numbers. For the numbers to - line up, you must make sure that the font in the gutter is the - same as the one in the rest of the editor, so you should - probably only set font style and size in - the CodeMirror class.
    +
    CodeMirror-gutters
    +
    This is the backdrop for all gutters. Use it to set the + default gutter background color, and optionally add a border on + the right of the gutters.
    + +
    CodeMirror-linenumbers
    +
    Use this for giving a background or width to the line number + gutter.
    + +
    CodeMirror-linenumber
    +
    Used to style the actual individual line numbers. These + won't be children of the CodeMirror-linenumbers + (plural) element, but rather will be absolutely positioned to + overlay it. Use this to set alignment and text properties for + the line numbers.
    CodeMirror-lines
    -
    The visible lines. If this has vertical - padding, CodeMirror-gutter should have the same - padding.
    +
    The visible lines. This is where you specify vertical + padding for the editor content.
    CodeMirror-cursor
    The cursor is a block element that is absolutely positioned. @@ -459,7 +463,9 @@

    Customized Styling

    the wrapper (class CodeMirror) element, and a height on the scroller - (class CodeMirror-scroll) element.

    + (class CodeMirror-scroll) element. + The setSize method is the best + way to dynamically change size at runtime.

    The actual lines, as well as the cursor, are represented by pre elements. By default no text styling (such as @@ -613,25 +619,19 @@

    Programming API

    Returns an array of all the bookmarks and marked ranges present at the given position.
    -
    setMarker(line, text, className) → lineHandle
    -
    Add a gutter marker for the given line. Gutter markers are - shown in the line-number area (instead of the number for this - line). Both text and className are - optional. Setting text to a Unicode character like - ● tends to give a nice effect. To put a picture in the gutter, - set text to a space and className to - something that sets a background image. If you - specify text, the given text (which may contain - HTML) will, by default, replace the line number for that line. - If this is not what you want, you can include the - string %N% in the text, which will be replaced by - the line number.
    -
    clearMarker(line)
    -
    Clears a marker created - with setMarker. line can be either a - number or a handle returned by setMarker (since a - number may now refer to a different line if something was added - or deleted).
    +
    setGutterMarker(line, gutterID, value) → lineHandle
    +
    Sets the gutter marker for the given gutter (identified by + its CSS class, see + the gutters option) + to the given value. Value can be either null, to + clear the marker, or a DOM element, to set it. The DOM element + will be shown in the specified gutter next to the specified + line.
    + +
    clearGutter(gutterID)
    +
    Remove all gutter markers in + the gutter with the given ID.
    +
    setLineClass(line, className, backgroundClassName) → lineHandle
    Set a CSS class name for the given line. line can be a number or a line handle (as returned @@ -658,8 +658,9 @@

    Programming API

    Returns the line number, text content, and marker status of the given line, which can be either a number or a handle returned by setMarker. The returned object has the - structure {line, handle, text, markerText, markerClass, - lineClass, bgClass}.
    + structure {line, handle, text, gutterMarkers, lineClass, + bgClass}, where gutterMarkers is an object + mapping gutter IDs to marker elements.
    getLineHandle(num) → lineHandle
    Fetches the line handle for the given line number.
    @@ -772,7 +773,7 @@

    Programming API

    the refresh method afterwards.)
    getGutterElement() → node
    -
    Fetches the DOM node that represents the editor gutter.
    +
    Fetches the DOM node that contains the editor gutters.
    getStateAfter(line) → state
    Returns the mode's parser state, if any, at the end of the diff --git a/lib/codemirror.css b/lib/codemirror.css index f0e91b2d73..ddd3ac04ac 100644 --- a/lib/codemirror.css +++ b/lib/codemirror.css @@ -46,36 +46,47 @@ min-width: 18px; } -.CodeMirror-gutter { +.CodeMirror-gutters { position: absolute; left: 0; top: 0; - z-index: 10; + height: 100%; + border-right: 1px solid #ddd; background-color: #f7f7f7; - border-right: 1px solid #eee; - min-width: 2em; +} +.CodeMirror-gutter { height: 100%; + float: left; +} +.CodeMirror-gutter-elt { + position: absolute; + top: 0; + cursor: default; +} + +.CodeMirror-linenumbers { + padding: 0 .2em; } -.CodeMirror-gutter-text { - color: #aaa; +.CodeMirror-linenumber { + min-width: 1.8em; text-align: right; - padding: .4em .2em .4em .4em; + color: #999; + padding: 0 .2em; white-space: pre !important; - cursor: default; + font-size: 80%; } + .CodeMirror-lines { - padding: .4em; + padding: .4em 0; white-space: pre; cursor: text; } .CodeMirror pre { - -moz-border-radius: 0; - -webkit-border-radius: 0; - -o-border-radius: 0; - border-radius: 0; - border-width: 0; margin: 0; padding: 0; background: transparent; + -moz-border-radius: 0; -webkit-border-radius: 0; -o-border-radius: 0; border-radius: 0; + border-width: 0; + background: transparent; font-family: inherit; font-size: inherit; - padding: 0; margin: 0; + padding: 0 .4em; margin: 0; white-space: pre; word-wrap: normal; line-height: inherit; @@ -91,8 +102,11 @@ overflow-x: hidden; } -.CodeMirror textarea { - outline: none !important; +.CodeMirror-measure { + position: absolute; + width: 100%; height: 0px; + overflow: hidden; + visibility: hidden; } .CodeMirror pre.CodeMirror-cursor { @@ -164,10 +178,8 @@ div.CodeMirror span.CodeMirror-matchingbracket {color: #0f0;} div.CodeMirror span.CodeMirror-nonmatchingbracket {color: #f22;} @media print { - /* Hide the cursor when printing */ .CodeMirror pre.CodeMirror-cursor { visibility: hidden; } - -} \ No newline at end of file +} diff --git a/lib/codemirror.js b/lib/codemirror.js index 56ec4d3508..227b78b090 100644 --- a/lib/codemirror.js +++ b/lib/codemirror.js @@ -14,7 +14,7 @@ window.CodeMirror = (function() { if (defaults.hasOwnProperty(opt)) options[opt] = (givenOptions && givenOptions.hasOwnProperty(opt) ? givenOptions : defaults)[opt]; - var input = elt("textarea", null, null, "position: absolute; padding: 0; width: 1px; height: 1em"); + var input = elt("textarea", null, null, "position: absolute; padding: 0; width: 1px; height: 1em; outline: none;"); input.setAttribute("wrap", "off"); input.setAttribute("autocorrect", "off"); input.setAttribute("autocapitalize", "off"); // Wraps and hides input textarea var inputDiv = elt("div", [input], null, "overflow: hidden; position: relative; width: 3px; height: 0px;"); @@ -27,13 +27,13 @@ window.CodeMirror = (function() { // Blinky cursor, and element used to ensure cursor fits at the end of a line var cursor = elt("pre", "\u00a0", "CodeMirror-cursor"), widthForcer = elt("pre", "\u00a0", "CodeMirror-cursor", "visibility: hidden"); // Used to measure text size - var measure = elt("div", null, null, "position: absolute; width: 100%; height: 0px; overflow: hidden; visibility: hidden;"); + var measure = elt("div", null, "CodeMirror-measure"); var lineSpace = elt("div", [measure, cursor, widthForcer, selectionDiv, lineDiv], null, "position: relative; z-index: 0"); - var gutterText = elt("div", null, "CodeMirror-gutter-text"), gutter = elt("div", [gutterText], "CodeMirror-gutter"); // Moved around its parent to cover visible view - var mover = elt("div", [gutter, elt("div", [lineSpace], "CodeMirror-lines")], null, "position: relative"); + var mover = elt("div", [elt("div", [lineSpace], "CodeMirror-lines")], null, "position: relative"); + var gutters = elt("div", null, "CodeMirror-gutters"), lineGutter; // Set to the height of the text, causes scrolling - var sizer = elt("div", [mover], null, "position: relative"); + var sizer = elt("div", [gutters, mover], null, "position: relative;"); // Provides scrolling var scroller = elt("div", [sizer], "CodeMirror-scroll"); scroller.setAttribute("tabIndex", "-1"); @@ -48,7 +48,7 @@ window.CodeMirror = (function() { lineSpace.style.outline = "none"; if (options.tabindex != null) input.tabIndex = options.tabindex; if (options.autofocus) focusInput(); - if (!options.gutter && !options.lineNumbers) gutter.style.display = "none"; + setGuttersForLineNumbers(); updateGutters(); // Needed to handle Tab key in KHTML if (khtml) inputDiv.style.height = "1px", inputDiv.style.position = "absolute"; @@ -79,7 +79,7 @@ window.CodeMirror = (function() { // Variables used by startOperation/endOperation to track what // happened during the operation. var updateInput, userSelChange, changes, textChanged, selectionChanged, leaveInputAlone, - gutterDirty, callbacks; + callbacks; // Current visible range (may be bigger than the view window). var displayOffset = 0, showingFrom = 0, showingTo = 0, lastSizeC = 0; // bracketHighlighted is used to remember that a bracket has been @@ -98,6 +98,7 @@ window.CodeMirror = (function() { // Register our event handlers. connect(scroller, "mousedown", operation(onMouseDown)); + connect(gutters, "mousedown", clickInGutter); connect(scroller, "dblclick", operation(onDoubleClick)); connect(lineSpace, "selectstart", e_preventDefault); // Gecko browsers fire contextmenu *after* opening the menu, at @@ -168,11 +169,10 @@ window.CodeMirror = (function() { else if (option == "lineWrapping" && oldVal != value) operation(wrappingChanged)(); else if (option == "tabSize") updateDisplay(true); else if (option == "keyMap") keyMapChanged(); - if (option == "lineNumbers" || option == "gutter" || option == "firstLineNumber" || - option == "theme" || option == "lineNumberFormatter") { - gutterChanged(); - updateDisplay(true); - } + else if (option == "gutters" || option == "lineNumbers") setGuttersForLineNumbers(); + if (option == "lineNumbers" || option == "gutters" || option == "firstLineNumber" || + option == "theme" || option == "lineNumberFormatter") + guttersChanged(); }, getOption: function(option) {return options[option];}, undo: operation(undo), @@ -222,8 +222,8 @@ window.CodeMirror = (function() { markText: operation(markText), setBookmark: setBookmark, findMarksAt: findMarksAt, - setMarker: operation(addGutterMarker), - clearMarker: operation(removeGutterMarker), + setGutterMarker: operation(setGutterMarker), + clearGutter: operation(clearGutter), setLineClass: operation(setLineClass), hideLine: operation(function(h) {return setLineHidden(h, true);}), showLine: operation(function(h) {return setLineHidden(h, false);}), @@ -245,7 +245,7 @@ window.CodeMirror = (function() { if (vert == "over") top = pos.y; else if (vert == "near") { var vspace = Math.max(scroller.offsetHeight, doc.height * textHeight()), - hspace = Math.max(sizer.clientWidth, lineSpace.clientWidth) - paddingLeft(); + hspace = Math.max(sizer.clientWidth, lineSpace.clientWidth); if (pos.yBot + node.offsetHeight > vspace && pos.y > node.offsetHeight) top = pos.y - node.offsetHeight; if (left + node.offsetWidth > hspace) @@ -259,7 +259,7 @@ window.CodeMirror = (function() { } else { if (horiz == "left") left = 0; else if (horiz == "middle") left = (sizer.clientWidth - node.offsetWidth) / 2; - node.style.left = (left + paddingLeft()) + "px"; + node.style.left = left + "px"; } if (scroll) scrollIntoView(left, top, left + node.offsetWidth, top + node.offsetHeight); @@ -353,12 +353,11 @@ window.CodeMirror = (function() { getInputField: function(){return input;}, getWrapperElement: function(){return wrapper;}, getScrollerElement: function(){return scroller;}, - getGutterElement: function(){return gutter;} + getGutterElement: function(){return gutters;} }; function getLine(n) { return getLineAt(doc, n); } function updateLineHeight(line, height) { - gutterDirty = true; var diff = height - line.height; for (var n = line; n; n = n.parent) n.height += diff; } @@ -383,8 +382,6 @@ window.CodeMirror = (function() { } function onScrollMain(e) { - if (options.fixedGutter && gutter.style.left != scroller.scrollLeft + "px") - gutter.style.left = scroller.scrollLeft + "px"; if (scroller.scrollTop != lastScrollTop) { lastScrollTop = scroller.scrollTop; if (scrollbar.scrollTop != lastScrollTop) @@ -400,14 +397,7 @@ window.CodeMirror = (function() { for (var n = e_target(e); n != wrapper; n = n.parentNode) if (n.parentNode == sizer && n != mover) return; - // See if this is a click in the gutter - for (var n = e_target(e); n != wrapper; n = n.parentNode) - if (n.parentNode == gutterText) { - if (options.onGutterClick) - options.onGutterClick(instance, indexOf(gutterText.childNodes, n) + showingFrom, e); - return e_preventDefault(e); - } - + if (clickInGutter(e)) return; var start = posFromMouse(e); switch (e_button(e)) { @@ -512,8 +502,6 @@ window.CodeMirror = (function() { var up = connect(document, "mouseup", operation(done), true); } function onDoubleClick(e) { - for (var n = e_target(e); n != wrapper; n = n.parentNode) - if (n.parentNode == gutterText) return e_preventDefault(e); e_preventDefault(e); } function onDrop(e) { @@ -556,6 +544,30 @@ window.CodeMirror = (function() { catch(e){} } } + + function clickInGutter(e) { + try { var mX = e.clientX, mY = e.clientY; } + catch(e) { return false; } + + if (mX >= Math.floor(gutters.getBoundingClientRect().right)) return false; + if (options.onGutterClick) { + mY -= wrapper.getBoundingClientRect().top; + for (var i = 0; i < options.gutters.length; ++i) { + var g = gutters.childNodes[i]; + if (g && g.getBoundingClientRect().right >= mX) { + if (mY < lineDiv.offsetHeight) { + var line = lineAtHeight(doc, mY); + var gutter = options.gutters[i]; + setTimeout(function() {options.onGutterClick(instance, line, e, gutter);}, 20); + } + break; + } + } + } + e_preventDefault(e); + return true; + } + function onDragStart(e) { var txt = getSelection(); e.dataTransfer.setData("Text", txt); @@ -731,7 +743,6 @@ window.CodeMirror = (function() { doc.iter(from.line, to.line + 1, function(line) { if (!line.hidden && line.text.length == maxLineLength) {recomputeMaxLength = true; return true;} }); - if (from.line != to.line || newText.length > 1) gutterDirty = true; var nlines = to.line - from.line, firstLine = getLine(from.line), lastLine = getLine(to.line); // First adjust the line structure, taking some care to leave highlighting intact. @@ -847,6 +858,7 @@ window.CodeMirror = (function() { } } else { sizer.style.minHeight = ""; + sizer.style.minHeight = scroller.clientHeight + "px"; } // Position the mover div to align with the current virtual scroll position mover.style.top = displayOffset * textHeight() + "px"; @@ -994,8 +1006,8 @@ window.CodeMirror = (function() { if (scrollPos.scrollTop != null) {scrollbar.scrollTop = scroller.scrollTop = scrollPos.scrollTop;} } function calculateScrollPos(x1, y1, x2, y2) { - var pl = paddingLeft(), pt = paddingTop(); - y1 += pt; y2 += pt; x1 += pl; x2 += pl; + var pt = paddingTop(); + y1 += pt; y2 += pt; var screen = scroller.clientHeight, screentop = scrollbar.scrollTop, result = {}; var docBottom = needsScrollbar() || Infinity; var atTop = y1 < pt + 10, atBottom = y2 + pt > docBottom - 10; @@ -1003,8 +1015,8 @@ window.CodeMirror = (function() { else if (y2 > screentop + screen) result.scrollTop = (atBottom ? docBottom : y2) - screen; var screenw = scroller.clientWidth, screenleft = scroller.scrollLeft; - var gutterw = options.fixedGutter ? gutter.clientWidth : 0; - var atLeft = x1 < gutterw + pl + 10; + var gutterw = gutters.offsetWidth; + var atLeft = x1 < gutterw + 10; if (x1 < screenleft + gutterw || atLeft) { if (atLeft) x1 = 0; result.scrollLeft = Math.max(0, x1 - 10 - gutterw); @@ -1038,6 +1050,15 @@ window.CodeMirror = (function() { updateVerticalScroll(scrollTop); return; } + if (changes && changes !== true && maybeUpdateLineNumberWidth()) + changes = true; + mover.style.marginLeft = gutters.offsetWidth + "px"; + // Used to determine which lines need their line numbers updated + var positionsChangedFrom = changes === true ? 0 : Infinity; + if (options.lineNumbers && changes && changes !== true) + for (var i = 0; i < changes.length; ++i) + if (changes[i].diff) { positionsChangedFrom = changes[i].from; break; } + var from = Math.max(visible.from - 100, 0), to = Math.min(doc.size, visible.to + 100); if (showingFrom < from && from - showingFrom < 20) from = showingFrom; if (showingTo > to && showingTo - to < 20) to = Math.min(doc.size, showingTo); @@ -1061,10 +1082,10 @@ window.CodeMirror = (function() { } intact.sort(function(a, b) {return a.domStart - b.domStart;}); - var th = textHeight(), gutterDisplay = gutter.style.display; + var th = textHeight(); lineDiv.style.display = "none"; - patchDisplay(from, to, intact); - lineDiv.style.display = gutter.style.display = ""; + patchDisplay(from, to, intact, positionsChangedFrom); + lineDiv.style.display = ""; var different = from != showingFrom || to != showingTo || lastSizeC != scroller.clientHeight + th; // This is just a bogus formula that detects when the editor is @@ -1094,7 +1115,7 @@ window.CodeMirror = (function() { var height = Math.round(curNode.offsetHeight / th) || 1; if (line.height != height) { updateLineHeight(line, height); - gutterDirty = heightChanged = true; + heightChanged = true; } } curNode = curNode.nextSibling; @@ -1104,11 +1125,6 @@ window.CodeMirror = (function() { if (options.lineWrapping) checkHeights(); - gutter.style.display = gutterDisplay; - if (different || gutterDirty) { - // If the gutter grew in size, re-check heights. If those changed, re-draw gutter. - updateGutter() && options.lineWrapping && checkHeights() && updateGutter(); - } updateVerticalScroll(scrollTop); updateSelection(); if (!suppressCallback && options.onUpdate) options.onUpdate(instance); @@ -1138,12 +1154,17 @@ window.CodeMirror = (function() { return intact; } - function patchDisplay(from, to, intact) { + function lineNumberFor(i) { + return String(options.lineNumberFormatter(i + options.firstLineNumber)); + } + + function patchDisplay(from, to, intact, updateNumbersFrom) { function killNode(node) { var tmp = node.nextSibling; node.parentNode.removeChild(node); return tmp; } + var lineNumbers = options.lineNumbers; // The first pass removes the DOM nodes that aren't intact. if (!intact.length) removeChildren(lineDiv); else { @@ -1151,23 +1172,47 @@ window.CodeMirror = (function() { for (var i = 0; i < intact.length; ++i) { var cur = intact[i]; while (cur.domStart > domPos) {curNode = killNode(curNode); domPos++;} - for (var j = 0, e = cur.to - cur.from; j < e; ++j) {curNode = curNode.nextSibling; domPos++;} + for (var j = cur.from, e = cur.to; j < e; ++j) { + if (lineNumbers && updateNumbersFrom <= j && curNode.firstChild) + setTextContent(curNode.firstChild, lineNumberFor(j)); + curNode = curNode.nextSibling; domPos++; + } } while (curNode) curNode = killNode(curNode); } // This pass fills in the lines that actually changed. - var nextIntact = intact.shift(), curNode = lineDiv.firstChild, j = from; + var nextIntact = intact.shift(), curNode = lineDiv.firstChild, j = from, gutterSpecs = options.gutters; doc.iter(from, to, function(line) { if (nextIntact && nextIntact.to == j) nextIntact = intact.shift(); if (!nextIntact || nextIntact.from > j) { - if (line.hidden) var lineElement = elt("pre"); + if (line.hidden) var lineElement = elt("div"); else { - var lineElement = line.getElement(makeTab); + var lineElement = line.getElement(makeTab), markers = line.gutterMarkers; if (line.className) lineElement.className = line.className; - // Kludge to make sure the styled element lies behind the selection (by z-index) - if (line.bgClassName) { - var pre = elt("pre", "\u00a0", line.bgClassName, "position: absolute; left: 0; right: 0; top: 0; bottom: 0; z-index: -2"); - lineElement = elt("div", [pre, lineElement], null, "position: relative"); + // Lines with gutter elements or a background class need + // to be wrapped again, and have the extra elements added + // to the wrapper div + if (lineNumbers || markers || line.bgClassName) { + var inside = []; + if (lineNumbers) + inside.push(elt("div", lineNumberFor(j), + "CodeMirror-linenumber CodeMirror-gutter-elt", + "left: " + (lineGutter.offsetLeft - gutters.offsetWidth) + "px; width: " + + currentLineNumberWidth + "px")); + if (markers) + for (var k = 0; k < gutterSpecs.length; ++k) { + var id = gutterSpecs[k], found = markers.hasOwnProperty(id) && markers[id]; + if (found) { + var gutterElt = gutters.childNodes[k]; + inside.push(elt("div", [found], "CodeMirror-gutter-elt", "left: " + (gutterElt.offsetLeft - gutters.offsetWidth) + + "px; width: " + gutterElt.clientWidth + "px")); + } + } + // Kludge to make sure the styled element lies behind the selection (by z-index) + if (line.bgClassName) + inside.push(elt("pre", "\u00a0", line.bgClassName, "position: absolute; left: 0; right: 0; top: 0; bottom: 0; z-index: -2")); + inside.push(lineElement); + lineElement = elt("div", inside, null, "position: relative"); } } lineDiv.insertBefore(lineElement, curNode); @@ -1178,46 +1223,48 @@ window.CodeMirror = (function() { }); } - function updateGutter() { - if (!options.gutter && !options.lineNumbers) return; - var hText = mover.offsetHeight, hEditor = scroller.clientHeight; - gutter.style.height = (hText - hEditor < 2 ? hEditor : hText) + "px"; - var fragment = document.createDocumentFragment(), i = showingFrom, normalNode; - doc.iter(showingFrom, Math.max(showingTo, showingFrom + 1), function(line) { - if (line.hidden) { - fragment.appendChild(elt("pre")); - } else { - var marker = line.gutterMarker; - var text = options.lineNumbers ? options.lineNumberFormatter(i + options.firstLineNumber) : null; - if (marker && marker.text) - text = marker.text.replace("%N%", text != null ? text : ""); - else if (text == null) - text = "\u00a0"; - var markerElement = fragment.appendChild(elt("pre", null, marker && marker.style)); - markerElement.innerHTML = text; - for (var j = 1; j < line.height; ++j) { - markerElement.appendChild(elt("br")); - markerElement.appendChild(document.createTextNode("\u00a0")); - } - if (!marker) normalNode = i; + var currentLineNumberWidth, currentLineNumberChars; + function maybeUpdateLineNumberWidth() { + if (!options.lineNumbers) return false; + var last = lineNumberFor(doc.size - 1); + if (last.length != currentLineNumberChars) { + var test = measure.appendChild(elt("div", last, "CodeMirror-linenumber CodeMirror-gutter-elt")); + currentLineNumberWidth = test.clientWidth; + currentLineNumberChars = currentLineNumberWidth ? last.length : -1; + lineGutter.style.width = currentLineNumberWidth + "px"; + return true; + } + return false; + } + + function updateGutters() { + removeChildren(gutters); + for (var i = 0; i < options.gutters.length; ++i) { + var gutterClass = options.gutters[i]; + var gElt = gutters.appendChild(elt("div", null, "CodeMirror-gutter " + gutterClass)); + if (gutterClass == "CodeMirror-linenumbers") { + lineGutter = gElt; + gElt.style.width = currentLineNumberWidth + "px"; } - ++i; - }); - gutter.style.display = "none"; - removeChildrenAndAdd(gutterText, fragment); - // Make sure scrolling doesn't cause number gutter size to pop - if (normalNode != null && options.lineNumbers) { - var node = gutterText.childNodes[normalNode - showingFrom]; - var minwidth = String(doc.size).length, val = eltText(node.firstChild), pad = ""; - while (val.length + pad.length < minwidth) pad += "\u00a0"; - if (pad) node.insertBefore(document.createTextNode(pad), node.firstChild); - } - gutter.style.display = ""; - var resized = Math.abs((parseInt(lineSpace.style.marginLeft) || 0) - gutter.offsetWidth) > 2; - lineSpace.style.marginLeft = gutter.offsetWidth + "px"; - gutterDirty = false; - return resized; + } + gutters.style.display = i ? "" : "none"; + } + function guttersChanged() { + updateGutters(); + updateDisplay(true); } + function setGuttersForLineNumbers() { + var found = false; + for (var i = 0; i < options.gutters.length; ++i) { + if (options.gutters[i] == "CodeMirror-linenumbers") { + if (options.lineNumbers) found = true; + else options.gutters.splice(i--, 1); + } + } + if (!found && options.lineNumbers) + options.gutters.push("CodeMirror-linenumbers"); + } + function updateSelection() { var collapsed = posEq(sel.from, sel.to); var fromPos = localCoords(sel.from, true); @@ -1248,9 +1295,9 @@ window.CodeMirror = (function() { var middleStart = Math.max(0, fromPos.y + (sel.from.ch ? th : 0)); var middleHeight = Math.min(toPos.y, clientHeight) - middleStart; if (middleHeight > 0.2 * th) - add(0, middleStart, 0, middleHeight); - if ((!sameLine || !sel.from.ch) && toPos.y < clientHeight - .5 * th) - add(0, toPos.y, clientWidth - toPos.x, th); + add(paddingLeft(), middleStart, 0, middleHeight); + if ((!sameLine || !sel.from.ch) && sel.to.ch && toPos.y < clientHeight - .5 * th) + add(paddingLeft(), toPos.y, clientWidth - toPos.x, th); removeChildrenAndAdd(selectionDiv, fragment); cursor.style.display = "none"; selectionDiv.style.display = ""; @@ -1458,12 +1505,6 @@ window.CodeMirror = (function() { work = [0]; startWorker(); } - function gutterChanged() { - var visible = options.gutter || options.lineNumbers; - gutter.style.display = visible ? "" : "none"; - if (visible) gutterDirty = true; - else lineDiv.parentNode.style.marginLeft = 0; - } function wrappingChanged(from, to) { if (options.lineWrapping) { wrapper.className += " CodeMirror-wrap"; @@ -1572,16 +1613,29 @@ window.CodeMirror = (function() { return markers; } - function addGutterMarker(line, text, className) { - if (typeof line == "number") line = getLine(clipLine(line)); - line.gutterMarker = {text: text, style: className}; - gutterDirty = true; - return line; + function isEmpty(obj) { + var c = 0; + for (var n in obj) if (obj.hasOwnProperty(n) && obj[n]) ++c; + return !c; } - function removeGutterMarker(line) { - if (typeof line == "number") line = getLine(clipLine(line)); - line.gutterMarker = null; - gutterDirty = true; + function setGutterMarker(line, gutterID, value) { + return changeLine(line, function(line) { + var markers = line.gutterMarkers || (line.gutterMarkers = {}); + markers[gutterID] = value; + if (!value && isEmpty(markers)) line.gutterMarkers = null; + return true; + }); + } + function clearGutter(gutterID) { + var i = 0; + doc.iter(0, doc.size, function(line) { + if (line.gutterMarkers && line.gutterMarkers[gutterID]) { + line.gutterMarkers[gutterID] = null; + changes.push({from: i, to: i + 1}); + if (isEmpty(line.gutterMarkers)) line.gutterMarkers = null; + } + ++i; + }); } function changeLine(handle, op) { @@ -1622,7 +1676,7 @@ window.CodeMirror = (function() { if (!to) return; setSelection(from, to); } - return (gutterDirty = true); + return true; } }); } @@ -1637,13 +1691,18 @@ window.CodeMirror = (function() { var n = lineNo(line); if (n == null) return null; } - var marker = line.gutterMarker; - return {line: n, handle: line, text: line.text, markerText: marker && marker.text, - markerClass: marker && marker.style, lineClass: line.className, bgClass: line.bgClassName}; + return {line: n, handle: line, text: line.text, gutterMarkers: line.gutterMarkers, + lineClass: line.className, bgClass: line.bgClassName}; + } + + function paddingLeft() { + var e = removeChildrenAndAdd(measure, elt("pre")).appendChild(elt("span", "x")); + return e.offsetLeft; } function measureLine(line, ch) { - if (ch == 0) return {top: 0, left: 0}; + if (ch == 0) return {top: 0, left: paddingLeft()}; + var wbr = options.lineWrapping && ch < line.text.length && spanAffectsWrapping.test(line.text.slice(ch - 1, ch + 1)); var pre = line.getElement(makeTab, ch, wbr); @@ -1660,12 +1719,9 @@ window.CodeMirror = (function() { } function localCoords(pos, inLineWrap) { var x, lh = textHeight(), y = lh * (heightAtLine(doc, pos.line) - (inLineWrap ? displayOffset : 0)); - if (pos.ch == 0) x = 0; - else { - var sp = measureLine(getLine(pos.line), pos.ch); - x = sp.left; - if (options.lineWrapping) y += Math.max(0, sp.top); - } + var sp = measureLine(getLine(pos.line), pos.ch); + x = sp.left; + if (options.lineWrapping) y += Math.max(0, sp.top); return {x: x, y: y, yBot: y + lh}; } // Coords must be lineSpace-local @@ -1743,7 +1799,6 @@ window.CodeMirror = (function() { return (cachedWidth = anchor.offsetWidth || 10); } function paddingTop() {return lineSpace.offsetTop;} - function paddingLeft() {return lineSpace.offsetLeft;} function posFromMouse(e, liberal) { var offW = eltOffset(scroller, true), x, y; @@ -1766,7 +1821,7 @@ window.CodeMirror = (function() { var oldCSS = input.style.cssText; inputDiv.style.position = "absolute"; input.style.cssText = "position: fixed; width: 30px; height: 30px; top: " + (e.clientY - 5) + - "px; left: " + (e.clientX - 5) + "px; z-index: 1000; background: white; " + + "px; left: " + (e.clientX - 5) + "px; z-index: 1000; background: white; outline: none;" + "border-width: 0; outline: none; overflow: hidden; opacity: .05; filter: alpha(opacity=5);"; leaveInputAlone = true; var val = input.value = getSelection(); @@ -1957,7 +2012,6 @@ window.CodeMirror = (function() { updated = updateDisplay(changes, true, newScrollPos && newScrollPos.scrollTop); if (!updated) { if (selectionChanged) updateSelection(); - if (gutterDirty) updateGutter(); } if (newScrollPos) scrollCursorIntoView(); if (selectionChanged) restartBlink(); @@ -2018,7 +2072,7 @@ window.CodeMirror = (function() { onDragEvent: null, lineWrapping: false, lineNumbers: false, - gutter: false, + gutters: [], fixedGutter: false, firstLineNumber: 1, readOnly: false, @@ -3107,7 +3161,7 @@ window.CodeMirror = (function() { return e; } function removeChildrenAndAdd(parent, e) { - removeChildren(parent).appendChild(e); + return removeChildren(parent).appendChild(e); } function setTextContent(e, str) { if (ie_lt9) { diff --git a/lib/util/foldcode.js b/lib/util/foldcode.js index 02cfb50ab7..cbe1b353b5 100644 --- a/lib/util/foldcode.js +++ b/lib/util/foldcode.js @@ -156,7 +156,7 @@ CodeMirror.indentRangeFinder = function(cm, line) { CodeMirror.newFoldFunction = function(rangeFinder, markText, hideEnd) { var folded = []; - if (markText == null) markText = '
    %N%'; + if (markText == null) markText = "\u25bc"; function isFolded(cm, n) { for (var i = 0; i < folded.length; ++i) { @@ -167,7 +167,7 @@ CodeMirror.newFoldFunction = function(rangeFinder, markText, hideEnd) { } function expand(cm, region) { - cm.clearMarker(region.start); + cm.setGutterMarker(region.start, "CodeMirror-folded", null); for (var i = 0; i < region.hidden.length; ++i) cm.showLine(region.hidden[i]); } @@ -186,7 +186,10 @@ CodeMirror.newFoldFunction = function(rangeFinder, markText, hideEnd) { var handle = cm.hideLine(i); if (handle) hidden.push(handle); } - var first = cm.setMarker(line, markText); + var elt = document.createElement("div"); + elt.className = "CodeMirror-foldmarker"; + elt.innerHTML = markText; + var first = cm.setGutterMarker(line, "CodeMirror-folded", elt); var region = {start: first, hidden: hidden}; cm.onDeleteLine(first, function() { expand(cm, region); }); folded.push(region); diff --git a/test/test.js b/test/test.js index 2c8e398c83..4ba5d5de71 100644 --- a/test/test.js +++ b/test/test.js @@ -132,15 +132,20 @@ test("defaults", function() { testCM("lineInfo", function(cm) { eq(cm.lineInfo(-1), null); - var lh = cm.setMarker(1, "FOO", "bar"); + var mark = document.createElement("span"); + var lh = cm.setGutterMarker(1, "FOO", mark); var info = cm.lineInfo(1); eq(info.text, "222222"); - eq(info.markerText, "FOO"); - eq(info.markerClass, "bar"); + eq(info.gutterMarkers.FOO, mark); eq(info.line, 1); - eq(cm.lineInfo(2).markerText, null); - cm.clearMarker(lh); - eq(cm.lineInfo(1).markerText, null); + eq(cm.lineInfo(2).gutterMarkers, null); + cm.setGutterMarker(lh, "FOO", null); + eq(cm.lineInfo(1).gutterMarkers, null); + cm.setGutterMarker(1, "FOO", mark); + cm.setGutterMarker(0, "FOO", mark); + cm.clearGutter("FOO"); + eq(cm.lineInfo(0).gutterMarkers, null); + eq(cm.lineInfo(1).gutterMarkers, null); }, {value: "111111\n222222\n333333"}); testCM("coords", function(cm) { @@ -458,7 +463,7 @@ testCM("wrappingAndResizing", function(cm) { cm.setCursor({line: 0, ch: doc.length}); eq(wrap.offsetHeight, h0); cm.replaceSelection("x"); - is(wrap.offsetHeight > h0); + is(wrap.offsetHeight > h0, "wrapping happens"); // Now add a max-height and, in a document consisting of // almost-wrapped lines, go over it so that a scrollbar appears. cm.setValue(doc + "\n" + doc + "\n"); From 6ac8c130e589952e74c72a1840abc6e2358550b3 Mon Sep 17 00:00:00 2001 From: Marijn Haverbeke Date: Wed, 15 Aug 2012 13:19:43 +0200 Subject: [PATCH 0016/5780] Implement arbitrary-height lines Experimental for now. --- lib/codemirror.css | 2 +- lib/codemirror.js | 223 +++++++++++++++++++++++++-------------------- test/index.html | 1 + test/test.js | 17 ++-- 4 files changed, 136 insertions(+), 107 deletions(-) diff --git a/lib/codemirror.css b/lib/codemirror.css index ddd3ac04ac..623c799885 100644 --- a/lib/codemirror.css +++ b/lib/codemirror.css @@ -1,5 +1,5 @@ .CodeMirror { - line-height: 1em; + line-height: 1; font-family: monospace; /* Necessary so the scrollbar can be absolutely positioned within the wrapper on Lion. */ diff --git a/lib/codemirror.js b/lib/codemirror.js index 227b78b090..5408a0bf8b 100644 --- a/lib/codemirror.js +++ b/lib/codemirror.js @@ -66,7 +66,7 @@ window.CodeMirror = (function() { // mode holds a mode API object. doc is the tree of Line objects, // work an array of lines that should be parsed, and history the // undo history (instance of History constructor). - var mode, doc = new BranchChunk([new LeafChunk([new Line("")])]), work, focused; + var mode, doc = new BranchChunk([new LeafChunk([new Line("", null, textHeight())])]), work, focused; loadMode(); // The selection. These are always maintained to point at valid // positions. Inverted is used to remember that the user is @@ -244,7 +244,7 @@ window.CodeMirror = (function() { sizer.appendChild(node); if (vert == "over") top = pos.y; else if (vert == "near") { - var vspace = Math.max(scroller.offsetHeight, doc.height * textHeight()), + var vspace = Math.max(scroller.offsetHeight, doc.height), hspace = Math.max(sizer.clientWidth, lineSpace.clientWidth); if (pos.yBot + node.offsetHeight > vspace && pos.y > node.offsetHeight) top = pos.y - node.offsetHeight; @@ -745,6 +745,7 @@ window.CodeMirror = (function() { }); var nlines = to.line - from.line, firstLine = getLine(from.line), lastLine = getLine(to.line); + var th = textHeight(); // First adjust the line structure, taking some care to leave highlighting intact. if (from.ch == 0 && to.ch == 0 && newText[newText.length - 1] == "") { // This is a whole-line replace. Treated specially to make @@ -755,7 +756,7 @@ window.CodeMirror = (function() { prevLine.fixMarkEnds(lastLine); } else lastLine.fixMarkStarts(); for (var i = 0, e = newText.length - 1; i < e; ++i) - added.push(Line.inheritMarks(newText[i], prevLine)); + added.push(Line.inheritMarks(newText[i], prevLine, th)); if (nlines) doc.remove(from.line, nlines, callbacks); if (added.length) doc.insert(from.line, added); } else if (firstLine == lastLine) { @@ -767,7 +768,7 @@ window.CodeMirror = (function() { firstLine.fixMarkEnds(lastLine); var added = []; for (var i = 1, e = newText.length - 1; i < e; ++i) - added.push(Line.inheritMarks(newText[i], firstLine)); + added.push(Line.inheritMarks(newText[i], firstLine, th)); added.push(lastLine); doc.insert(from.line + 1, added); } @@ -782,7 +783,7 @@ window.CodeMirror = (function() { lastLine.replace(null, to.ch, newText[newText.length-1]); firstLine.fixMarkEnds(lastLine); for (var i = 1, e = newText.length - 1; i < e; ++i) - added.push(Line.inheritMarks(newText[i], firstLine)); + added.push(Line.inheritMarks(newText[i], firstLine, th)); if (nlines > 1) doc.remove(from.line + 1, nlines - 1, callbacks); doc.insert(from.line + 1, added); } @@ -790,7 +791,7 @@ window.CodeMirror = (function() { var perLine = Math.max(5, scroller.clientWidth / charWidth() - 3); doc.iter(from.line, from.line + newText.length, function(line) { if (line.hidden) return; - var guess = Math.ceil(line.text.length / perLine) || 1; + var guess = (Math.ceil(line.text.length / perLine) || 1) * th; if (guess != line.height) updateLineHeight(line, guess); }); } else { @@ -832,8 +833,8 @@ window.CodeMirror = (function() { } function needsScrollbar() { - var realHeight = doc.height * textHeight() + 2 * paddingTop(); - return realHeight * .99 > scroller.offsetHeight ? realHeight : false; + var realHeight = doc.height + 2 * paddingTop(); + return realHeight - 1 > scroller.offsetHeight ? realHeight : false; } function updateVerticalScroll(scrollTop) { @@ -861,7 +862,7 @@ window.CodeMirror = (function() { sizer.style.minHeight = scroller.clientHeight + "px"; } // Position the mover div to align with the current virtual scroll position - mover.style.top = displayOffset * textHeight() + "px"; + mover.style.top = displayOffset + "px"; } function computeMaxLength() { @@ -1027,9 +1028,9 @@ window.CodeMirror = (function() { } function visibleLines(scrollTop) { - var lh = textHeight(), top = (scrollTop != null ? scrollTop : scrollbar.scrollTop) - paddingTop(); - var fromHeight = Math.max(0, Math.floor(top / lh)); - var toHeight = Math.ceil((top + scroller.clientHeight) / lh); + var top = (scrollTop != null ? scrollTop : scrollbar.scrollTop) - paddingTop(); + var fromHeight = Math.max(0, Math.floor(top)); + var toHeight = Math.ceil(top + scroller.clientHeight); return {from: lineAtHeight(doc, fromHeight), to: lineAtHeight(doc, toHeight)}; } @@ -1082,15 +1083,14 @@ window.CodeMirror = (function() { } intact.sort(function(a, b) {return a.domStart - b.domStart;}); - var th = textHeight(); lineDiv.style.display = "none"; patchDisplay(from, to, intact, positionsChangedFrom); lineDiv.style.display = ""; - var different = from != showingFrom || to != showingTo || lastSizeC != scroller.clientHeight + th; + var different = from != showingFrom || to != showingTo || lastSizeC != scroller.clientHeight; // This is just a bogus formula that detects when the editor is // resized or the font size changes. - if (different) lastSizeC = scroller.clientHeight + th; + if (different) lastSizeC = scroller.clientHeight; if (from != showingFrom || to != showingTo && options.onViewportChange) setTimeout(function(){ if (options.onViewportChange) options.onViewportChange(instance, from, to); @@ -1104,26 +1104,27 @@ window.CodeMirror = (function() { throw new Error("BAD PATCH! " + JSON.stringify(intact) + " size=" + (showingTo - showingFrom) + " nodes=" + lineDiv.childNodes.length); - function checkHeights() { - var curNode = lineDiv.firstChild, heightChanged = false; - doc.iter(showingFrom, showingTo, function(line) { - // Work around bizarro IE7 bug where, sometimes, our curNode - // is magically replaced with a new node in the DOM, leaving - // us with a reference to an orphan (nextSibling-less) node. - if (!curNode) return; - if (!line.hidden) { - var height = Math.round(curNode.offsetHeight / th) || 1; - if (line.height != height) { - updateLineHeight(line, height); - heightChanged = true; - } + // Update line heights for visible lines based on actual DOM + // sizes + var curNode = lineDiv.firstChild, heightChanged = false; + var relativeTo = curNode.offsetTop; + doc.iter(showingFrom, showingTo, function(line) { + // Work around bizarro IE7 bug where, sometimes, our curNode + // is magically replaced with a new node in the DOM, leaving + // us with a reference to an orphan (nextSibling-less) node. + if (!curNode) return; + if (!line.hidden) { + var end = curNode.offsetHeight + curNode.offsetTop; + var height = end - relativeTo, diff = line.height - height; + if (height < 2) height = textHeight(); + relativeTo = end; + if (diff > .001 || diff < -.001) { + updateLineHeight(line, height); + heightChanged = true; } - curNode = curNode.nextSibling; - }); - return heightChanged; - } - - if (options.lineWrapping) checkHeights(); + } + curNode = curNode.nextSibling; + }); updateVerticalScroll(scrollTop); updateSelection(); @@ -1269,35 +1270,36 @@ window.CodeMirror = (function() { var collapsed = posEq(sel.from, sel.to); var fromPos = localCoords(sel.from, true); var toPos = collapsed ? fromPos : localCoords(sel.to, true); - var headPos = sel.inverted ? fromPos : toPos, th = textHeight(); + var headPos = sel.inverted ? fromPos : toPos; var wrapOff = eltOffset(wrapper), lineOff = eltOffset(lineDiv); inputDiv.style.top = Math.max(0, Math.min(scroller.offsetHeight, headPos.y + lineOff.top - wrapOff.top)) + "px"; inputDiv.style.left = Math.max(0, Math.min(scroller.offsetWidth, headPos.x + lineOff.left - wrapOff.left)) + "px"; if (collapsed) { cursor.style.top = headPos.y + "px"; cursor.style.left = (options.lineWrapping ? Math.min(headPos.x, lineSpace.offsetWidth) : headPos.x) + "px"; + cursor.style.height = (headPos.yBot - headPos.y) * .85 + "px"; cursor.style.display = ""; selectionDiv.style.display = "none"; } else { + fromPos.y = Math.max(0, fromPos.y); toPos.y = Math.max(0, toPos.y); var sameLine = fromPos.y == toPos.y, fragment = document.createDocumentFragment(); var clientWidth = lineSpace.clientWidth || lineSpace.offsetWidth; - var clientHeight = lineSpace.clientHeight || lineSpace.offsetHeight; var add = function(left, top, right, height) { var rstyle = quirksMode ? "width: " + (!right ? clientWidth : clientWidth - right - left) + "px" : "right: " + right + "px"; fragment.appendChild(elt("div", null, "CodeMirror-selected", "position: absolute; left: " + left + "px; top: " + top + "px; " + rstyle + "; height: " + height + "px")); }; - if (sel.from.ch && fromPos.y >= 0) { + if (sel.from.ch) { var right = sameLine ? clientWidth - toPos.x : 0; - add(fromPos.x, fromPos.y, right, th); + add(fromPos.x, fromPos.y, right, fromPos.yBot - fromPos.y); } - var middleStart = Math.max(0, fromPos.y + (sel.from.ch ? th : 0)); - var middleHeight = Math.min(toPos.y, clientHeight) - middleStart; - if (middleHeight > 0.2 * th) + var middleStart = Math.max(0, sel.from.ch ? fromPos.yBot - .5 : fromPos.y); + var middleHeight = toPos.y + (sel.from.ch ? 1 : 0) - middleStart; + if (middleHeight > 2) add(paddingLeft(), middleStart, 0, middleHeight); - if ((!sameLine || !sel.from.ch) && sel.to.ch && toPos.y < clientHeight - .5 * th) - add(paddingLeft(), toPos.y, clientWidth - toPos.x, th); + if ((!sameLine || !sel.from.ch) && sel.to.ch) + add(paddingLeft(), toPos.y, clientWidth - toPos.x, toPos.yBot - toPos.y); removeChildrenAndAdd(selectionDiv, fragment); cursor.style.display = "none"; selectionDiv.style.display = ""; @@ -1432,17 +1434,18 @@ window.CodeMirror = (function() { } function moveV(dir, unit) { var dist = 0, pos = localCoords(sel.inverted ? sel.from : sel.to, true); - if (goalColumn != null) pos.x = goalColumn; + var x = pos.x, y; + if (goalColumn != null) x = goalColumn; if (unit == "page") { - var screen = Math.min(scroller.clientHeight, window.innerHeight || document.documentElement.clientHeight); - var target = coordsChar(pos.x, pos.y + screen * dir); + var pageSize = Math.min(scroller.clientHeight, window.innerHeight || document.documentElement.clientHeight); + y = pos.y + dir * pageSize; } else if (unit == "line") { - var th = textHeight(); - var target = coordsChar(pos.x, pos.y + .5 * th + dir * th); + y = dir > 0 ? pos.yBot + 3 : pos.y - 3; } + var target = coordsChar(x, y); if (unit == "page") scrollbar.scrollTop += localCoords(target, true).y - pos.y; setCursor(target.line, target.ch, true); - goalColumn = pos.x; + goalColumn = x; } function findWordAt(pos) { @@ -1701,51 +1704,73 @@ window.CodeMirror = (function() { } function measureLine(line, ch) { - if (ch == 0) return {top: 0, left: paddingLeft()}; - - var wbr = options.lineWrapping && ch < line.text.length && - spanAffectsWrapping.test(line.text.slice(ch - 1, ch + 1)); - var pre = line.getElement(makeTab, ch, wbr); + var pre = line.getElement(makeTab, ch, options.lineWrapping); removeChildrenAndAdd(measure, pre); - var anchor = pre.anchor; + var anchor = pre.anchor, left = anchor.offsetLeft; + // We'll sample once at the top, once at the bottom of the line, + // to get the real line height (in case there tokens on the line + // with bigger fonts) + anchor.style.verticalAlign = "top"; + if (ie) { + // In IE, verticalAlign does not influence offsetTop, unless + // the element is an inline-block. Unfortunately, inline + // blocks have different wrapping behaviour, so we have to do + // some icky thing with inserting "Zero-Width No-Break Spaces" + // to compensate for wrapping artifacts. + anchor.style.display = "inline-block"; + if (options.lineWrapping && anchor.offsetLeft != left) { + anchor.parentNode.insertBefore(document.createTextNode("\ufeff"), anchor); + if (anchor.offsetLeft != left) + anchor.parentNode.insertBefore(document.createTextNode("\ufeff"), anchor.nextSibling); + if (anchor.offsetLeft != left) + anchor.parentNode.removeChild(anchor.previousSibling); + } + } var top = anchor.offsetTop, left = anchor.offsetLeft; // Older IEs report zero offsets for spans directly after a wrap if (ie && top == 0 && left == 0) { - var backup = elt("span", "x"); - anchor.parentNode.insertBefore(backup, anchor.nextSibling); - top = backup.offsetTop; + anchor = anchor.parentNode.insertBefore(elt("span", "x"), anchor.nextSibling); + top = anchor.offsetTop; left = anchor.offsetLeft; } - return {top: top, left: left}; + anchor.style.verticalAlign = "bottom"; + var bottom = anchor.offsetTop + anchor.offsetHeight; + // Work around Opera issue -- we can't get reliable wrapping + // info if we put anything in the anchor, but we can't get a + // proper height on empty nodes. + if (!eolSpanContent && bottom == top && ch == line.text.length) { + setTextContent(anchor, " "); + bottom = anchor.offsetTop + anchor.offsetHeight * (left == anchor.offsetLeft ? 1 : .5); + } + return {top: top, bottom: bottom, left: left}; } function localCoords(pos, inLineWrap) { - var x, lh = textHeight(), y = lh * (heightAtLine(doc, pos.line) - (inLineWrap ? displayOffset : 0)); + var x, y = heightAtLine(doc, pos.line) - (inLineWrap ? displayOffset : 0); var sp = measureLine(getLine(pos.line), pos.ch); - x = sp.left; - if (options.lineWrapping) y += Math.max(0, sp.top); - return {x: x, y: y, yBot: y + lh}; + return {x: sp.left, y: y + sp.top, yBot: y + sp.bottom}; } // Coords must be lineSpace-local function coordsChar(x, y) { - var th = textHeight(), cw = charWidth(), heightPos = displayOffset + Math.floor(y / th); + var cw = charWidth(), heightPos = displayOffset + y; if (heightPos < 0) return {line: 0, ch: 0}; var lineNo = lineAtHeight(doc, heightPos); if (lineNo >= doc.size) return {line: doc.size - 1, ch: getLine(doc.size - 1).text.length}; var lineObj = getLine(lineNo), text = lineObj.text; var tw = options.lineWrapping, innerOff = tw ? heightPos - heightAtLine(doc, lineNo) : 0; - if (x <= 0 && innerOff == 0) return {line: lineNo, ch: 0}; + if (x < 0) x = 0; var wrongLine = false; function getX(len) { var sp = measureLine(lineObj, len); if (tw) { - var off = Math.round(sp.top / th); - wrongLine = off != innerOff; - return Math.max(0, sp.left + (off - innerOff) * scroller.clientWidth); + wrongLine = true; + if (innerOff > sp.bottom) return Math.max(0, sp.left - scroller.clientWidth); + else if (innerOff < sp.top) return sp.left + scroller.clientWidth; + else wrongLine = false; } return sp.left; } var from = 0, fromX = 0, to = text.length, toX; // Guess a suitable upper bound for our search. - var estimated = Math.min(to, Math.ceil((x + innerOff * scroller.clientWidth * .9) / cw)); + var estimated = Math.min(to, Math.ceil((x + Math.floor(innerOff / textHeight()) * scroller.clientWidth * .9) / cw)); for (;;) { var estX = getX(estimated); if (estX <= x && estimated < to) estimated = Math.min(to, Math.ceil(estimated * 1.2)); @@ -2478,13 +2503,13 @@ window.CodeMirror = (function() { // Line objects. These hold state related to a line, including // highlighting info (the styles array). - function Line(text, styles) { + function Line(text, styles, height) { this.styles = styles || [text, null]; this.text = text; - this.height = 1; + this.height = height; } - Line.inheritMarks = function(text, orig) { - var ln = new Line(text), mk = orig && orig.marked; + Line.inheritMarks = function(text, orig, height) { + var ln = new Line(text, null, height), mk = orig && orig.marked; if (mk) { for (var i = 0; i < mk.length; ++i) { if (mk[i].to == null && mk[i].style) { @@ -2518,7 +2543,7 @@ window.CodeMirror = (function() { split: function(pos, textBefore) { var st = [textBefore, null], mk = this.marked; copyStyles(pos, this.text.length, this.styles, st); - var taken = new Line(textBefore + this.text.slice(pos), st); + var taken = new Line(textBefore + this.text.slice(pos), st, this.height); if (mk) { for (var i = 0; i < mk.length; ++i) { var mark = mk[i]; @@ -2645,10 +2670,10 @@ window.CodeMirror = (function() { indentation: function(tabSize) {return countColumn(this.text, null, tabSize);}, // Produces an HTML fragment for the line, taking selection, // marking, and highlighting into account. - getElement: function(makeTab, wrapAt, wrapWBR) { + getElement: function(makeTab, wrapAt, compensateForWrapping) { var first = true, col = 0, specials = /[\t\u0000-\u0019\u200b\u2028\u2029\uFEFF]/g; var pre = elt("pre"); - function span_(html, text, style) { + function span_(text, style) { if (!text) return; // Work around a bug where, in some compat modes, IE ignores leading spaces if (first && ie && text.charAt(0) == " ") text = "\u00a0" + text.slice(1); @@ -2680,35 +2705,37 @@ window.CodeMirror = (function() { } } } - if (style) html.appendChild(elt("span", [content], style)); - else html.appendChild(content); + if (style != null) return pre.appendChild(elt("span", [content], style)); + else return pre.appendChild(content); } var span = span_; if (wrapAt != null) { - var outPos = 0, anchor = pre.anchor = elt("span"); - span = function(html, text, style) { + var outPos = 0; + span = function(text, style) { var l = text.length; if (wrapAt >= outPos && wrapAt < outPos + l) { + var cut = wrapAt - outPos; if (wrapAt > outPos) { - span_(html, text.slice(0, wrapAt - outPos), style); + span_(text.slice(0, cut), style); // See comment at the definition of spanAffectsWrapping - if (wrapWBR) html.appendChild(elt("wbr")); + if (compensateForWrapping && spanAffectsWrapping.test(text.slice(cut - 1, cut + 1))) + pre.appendChild(elt("wbr")); + } + if (cut + 1 == l) { + pre.anchor = span_(text.slice(cut), style || ""); + wrapAt--; + } else { + pre.anchor = span_(text.slice(cut, cut + 1), style || ""); + if (compensateForWrapping && spanAffectsWrapping.test(text.slice(cut, cut + 2))) + pre.appendChild(elt("wbr")); + span_(text.slice(cut + 1), style); } - html.appendChild(anchor); - var cut = wrapAt - outPos; - span_(anchor, opera ? text.slice(cut, cut + 1) : text.slice(cut), style); - if (opera) span_(html, text.slice(cut + 1), style); - wrapAt--; outPos += l; } else { outPos += l; - span_(html, text, style); - if (outPos == wrapAt && outPos == len) { - setTextContent(anchor, eolSpanContent); - html.appendChild(anchor); - } - // Stop outputting HTML when gone sufficiently far beyond measure - else if (outPos > wrapAt + 10 && /\s/.test(text)) span = function(){}; + span_(text, style); + if (outPos == wrapAt && outPos == len) + pre.anchor = pre.appendChild(elt("span", eolSpanContent)); } }; } @@ -2719,14 +2746,14 @@ window.CodeMirror = (function() { if (!style) return null; return "cm-" + style.replace(/ +/g, " cm-"); } - if (!allText && wrapAt == null) { - span(pre, " "); + if (!allText) { + span("\u00a0"); } else if (!marked || !marked.length) { for (var i = 0, ch = 0; ch < len; i+=2) { var str = st[i], style = st[i+1], l = str.length; if (ch + l > len) str = str.slice(0, len - ch); ch += l; - span(pre, str, styleToClass(style)); + span(str, styleToClass(style)); } } else { var pos = 0, i = 0, text = "", style, sg = 0; @@ -2756,7 +2783,7 @@ window.CodeMirror = (function() { var appliedStyle = style; for (var j = 0; j < marks.length; ++j) appliedStyle = (appliedStyle ? appliedStyle + " " : "") + marks[j].style; - span(pre, end > upto ? text.slice(0, upto - pos) : text, appliedStyle); + span(end > upto ? text.slice(0, upto - pos) : text, appliedStyle); if (end >= upto) {text = text.slice(upto - pos); pos = upto; break;} pos = end; } diff --git a/test/index.html b/test/index.html index dd319e9db7..13aba5042f 100644 --- a/test/index.html +++ b/test/index.html @@ -1,6 +1,7 @@ + CodeMirror: Test Suite diff --git a/test/test.js b/test/test.js index 4ba5d5de71..099f8a934a 100644 --- a/test/test.js +++ b/test/test.js @@ -23,6 +23,7 @@ function byClassName(elt, cls) { } var ie_lt8 = /MSIE [1-7]\b/.test(navigator.userAgent); +var phantom = /PhantomJS/.test(navigator.userAgent); test("fromTextArea", function() { var te = document.getElementById("code"); @@ -168,9 +169,8 @@ testCM("coordsChar", function(cm) { for (var line = 0; line < 70; line += 5) { cm.setCursor(line, ch); var coords = cm.charCoords({line: line, ch: ch}); - var pos = cm.coordsChar({x: coords.x, y: coords.y + 1}); - eq(pos.line, line); - eq(pos.ch, ch); + var pos = cm.coordsChar({x: coords.x, y: coords.y + 5}); + eqPos(pos, {line: line, ch: ch}); } } }); @@ -341,7 +341,7 @@ testCM("selectionPos", function(cm) { addDoc(cm, 200, 100); cm.setSelection({line: 1, ch: 100}, {line: 98, ch: 100}); var lineWidth = cm.charCoords({line: 0, ch: 200}, "local").x; - var lineHeight = cm.charCoords({line: 1}).y - cm.charCoords({line: 0}).y; + var lineHeight = (cm.charCoords({line: 99}).y - cm.charCoords({line: 0}).y) / 100; cm.scrollTo(0, 0); var selElt = byClassName(cm.getWrapperElement(), "CodeMirror-selected"); var outer = cm.getWrapperElement().getBoundingClientRect(); @@ -353,7 +353,7 @@ testCM("selectionPos", function(cm) { var atRight = box.right - outer.left > .8 * lineWidth; if (atLeft && atRight) { sawMiddle = true; - is(box.bottom - box.top > 95 * lineHeight, "middle high"); + is(box.bottom - box.top > 90 * lineHeight, "middle high"); is(width > .9 * lineWidth, "middle wide"); } else { is(width > .4 * lineWidth, "top/bot wide enough"); @@ -473,7 +473,7 @@ testCM("wrappingAndResizing", function(cm) { {line: 0, ch: 0}, {line: 1, ch: doc.length}, {line: 1, ch: doc.length - 1}], function(pos) { var coords = cm.charCoords(pos); - eqPos(pos, cm.coordsChar({x: coords.x + 2, y: coords.y + 2})); + eqPos(pos, cm.coordsChar({x: coords.x + 2, y: coords.y + 5})); }); }, null, ie_lt8); @@ -519,7 +519,7 @@ testCM("moveV stuck", function(cm) { cm.setCursor({line: 0, ch: val.length - 1}); cm.moveV(-1, "line"); eqPos(cm.getCursor(), {line: 0, ch: 26}); -}, {lineWrapping: true}, ie_lt8); +}, {lineWrapping: true}); testCM("clickTab", function(cm) { var p0 = cm.charCoords({line: 0, ch: 0}), p1 = cm.charCoords({line: 0, ch: 1}); @@ -624,7 +624,8 @@ testCM("verticalMovementCommands", function(cm) { cm.execCommand("goLineUp"); eqPos(cm.getCursor(), {line: 0, ch: 0}); cm.execCommand("goLineDown"); - eqPos(cm.getCursor(), {line: 1, ch: 0}); + if (!phantom) // This fails in PhantomJS, though not in a real Webkit + eqPos(cm.getCursor(), {line: 1, ch: 0}); cm.setCursor({line: 1, ch: 12}); cm.execCommand("goLineDown"); eqPos(cm.getCursor(), {line: 2, ch: 5}); From 667e227f7c81e2d70df4ad1c69757774ada2e84a Mon Sep 17 00:00:00 2001 From: Marijn Haverbeke Date: Wed, 15 Aug 2012 16:24:31 +0200 Subject: [PATCH 0017/5780] Move to {left,top,bottom} objects throughout No more random {x,y,yBot} thrown into the mix. --- doc/manual.html | 8 +++--- lib/codemirror.js | 61 +++++++++++++++++++---------------------- lib/util/simple-hint.js | 8 +++--- test/test.js | 34 +++++++++++------------ 4 files changed, 53 insertions(+), 58 deletions(-) diff --git a/doc/manual.html b/doc/manual.html index 4d1a25957e..7661c65420 100644 --- a/doc/manual.html +++ b/doc/manual.html @@ -527,7 +527,7 @@

    Programming API

    arguments may be left as null or undefined to have no effect.
    getScrollInfo()
    -
    Get an {x, y, width, height} object that +
    Get an {left, top, width, height} object that represents the current scroll position and scrollable area size of the editor.
    @@ -541,12 +541,12 @@

    Programming API

    editor instance.
    cursorCoords(start, mode) → object
    -
    Returns an {x, y, yBot} object containing the +
    Returns an {left, top, bottom} object containing the coordinates of the cursor. If mode is "local", they will be relative to the top-left corner of the editable document. If it is "page" or not given, they are relative to the top-left corner of the - page. yBot is the coordinate of the bottom of the + page. bottom is the coordinate of the bottom of the cursor. start is a boolean indicating whether you want the start or the end of the selection.
    charCoords(pos, mode) → object
    @@ -554,7 +554,7 @@

    Programming API

    an arbitrary characters. pos should be a {line, ch} object.
    coordsChar(object) → pos
    -
    Given an {x, y} object (in page coordinates), +
    Given an {left, top} object (in page coordinates), returns the {line, ch} position that corresponds to it.
    diff --git a/lib/codemirror.js b/lib/codemirror.js index 5408a0bf8b..2b8b1df862 100644 --- a/lib/codemirror.js +++ b/lib/codemirror.js @@ -217,7 +217,7 @@ window.CodeMirror = (function() { }, coordsChar: function(coords) { var off = eltOffset(lineSpace); - return coordsChar(coords.x - off.left, coords.y - off.top); + return coordsChar(coords.left - off.left, coords.top - off.top); }, markText: operation(markText), setBookmark: setBookmark, @@ -239,15 +239,15 @@ window.CodeMirror = (function() { getViewport: function() { return {from: showingFrom, to: showingTo};}, addWidget: function(pos, node, scroll, vert, horiz) { pos = localCoords(clipPos(pos)); - var top = pos.yBot, left = pos.x; + var top = pos.top, left = pos.left; node.style.position = "absolute"; sizer.appendChild(node); - if (vert == "over") top = pos.y; + if (vert == "over") top = pos.top; else if (vert == "near") { var vspace = Math.max(scroller.offsetHeight, doc.height), hspace = Math.max(sizer.clientWidth, lineSpace.clientWidth); - if (pos.yBot + node.offsetHeight > vspace && pos.y > node.offsetHeight) - top = pos.y - node.offsetHeight; + if (pos.bottom + node.offsetHeight > vspace && pos.top > node.offsetHeight) + top = pos.top - node.offsetHeight; if (left + node.offsetWidth > hspace) left = hspace - node.offsetWidth; } @@ -330,7 +330,7 @@ window.CodeMirror = (function() { updateDisplay([]); }, getScrollInfo: function() { - return {x: scroller.scrollLeft, y: scrollbar.scrollTop, + return {left: scroller.scrollLeft, top: scrollbar.scrollTop, height: scrollbar.scrollHeight, width: scroller.scrollWidth}; }, setSize: function(width, height) { @@ -979,12 +979,12 @@ window.CodeMirror = (function() { } function scrollCursorIntoView() { - var coords = calculateCursorCoords(); - scrollIntoView(coords.x, coords.y, coords.x, coords.yBot); + var coords = localCoords(sel.inverted ? sel.from : sel.to); + scrollIntoView(coords.left, coords.top, coords.left, coords.bottom); if (!focused) return; var box = sizer.getBoundingClientRect(), doScroll = null; - if (coords.y + box.top < 0) doScroll = true; - else if (coords.y + box.top + textHeight() > (window.innerHeight || document.documentElement.clientHeight)) doScroll = false; + if (coords.top + box.top < 0) doScroll = true; + else if (coords.bottom + box.top > (window.innerHeight || document.documentElement.clientHeight)) doScroll = false; if (doScroll != null) { var hidden = cursor.style.display == "none"; if (hidden) { @@ -996,11 +996,6 @@ window.CodeMirror = (function() { if (hidden) cursor.style.display = "none"; } } - function calculateCursorCoords() { - var cursor = localCoords(sel.inverted ? sel.from : sel.to); - var x = options.lineWrapping ? Math.min(cursor.x, lineSpace.offsetWidth) : cursor.x; - return {x: x, y: cursor.y, yBot: cursor.yBot}; - } function scrollIntoView(x1, y1, x2, y2) { var scrollPos = calculateScrollPos(x1, y1, x2, y2); if (scrollPos.scrollLeft != null) {scroller.scrollLeft = scrollPos.scrollLeft;} @@ -1275,14 +1270,14 @@ window.CodeMirror = (function() { inputDiv.style.top = Math.max(0, Math.min(scroller.offsetHeight, headPos.y + lineOff.top - wrapOff.top)) + "px"; inputDiv.style.left = Math.max(0, Math.min(scroller.offsetWidth, headPos.x + lineOff.left - wrapOff.left)) + "px"; if (collapsed) { - cursor.style.top = headPos.y + "px"; - cursor.style.left = (options.lineWrapping ? Math.min(headPos.x, lineSpace.offsetWidth) : headPos.x) + "px"; - cursor.style.height = (headPos.yBot - headPos.y) * .85 + "px"; + cursor.style.top = headPos.top + "px"; + cursor.style.left = (options.lineWrapping ? Math.min(headPos.left, lineSpace.offsetWidth) : headPos.left) + "px"; + cursor.style.height = (headPos.bottom - headPos.top) * .85 + "px"; cursor.style.display = ""; selectionDiv.style.display = "none"; } else { - fromPos.y = Math.max(0, fromPos.y); toPos.y = Math.max(0, toPos.y); - var sameLine = fromPos.y == toPos.y, fragment = document.createDocumentFragment(); + fromPos.top = Math.max(0, fromPos.top); toPos.top = Math.max(0, toPos.top); + var sameLine = fromPos.top == toPos.top, fragment = document.createDocumentFragment(); var clientWidth = lineSpace.clientWidth || lineSpace.offsetWidth; var add = function(left, top, right, height) { var rstyle = quirksMode ? "width: " + (!right ? clientWidth : clientWidth - right - left) + "px" @@ -1291,15 +1286,15 @@ window.CodeMirror = (function() { "px; top: " + top + "px; " + rstyle + "; height: " + height + "px")); }; if (sel.from.ch) { - var right = sameLine ? clientWidth - toPos.x : 0; - add(fromPos.x, fromPos.y, right, fromPos.yBot - fromPos.y); + var right = sameLine ? clientWidth - toPos.left : 0; + add(fromPos.left, fromPos.top, right, fromPos.bottom - fromPos.top); } - var middleStart = Math.max(0, sel.from.ch ? fromPos.yBot - .5 : fromPos.y); - var middleHeight = toPos.y + (sel.from.ch ? 1 : 0) - middleStart; + var middleStart = Math.max(0, sel.from.ch ? fromPos.bottom - .5 : fromPos.top); + var middleHeight = toPos.top + (sel.from.ch ? 1 : 0) - middleStart; if (middleHeight > 2) add(paddingLeft(), middleStart, 0, middleHeight); if ((!sameLine || !sel.from.ch) && sel.to.ch) - add(paddingLeft(), toPos.y, clientWidth - toPos.x, toPos.yBot - toPos.y); + add(paddingLeft(), toPos.top, clientWidth - toPos.left, toPos.bottom - toPos.top); removeChildrenAndAdd(selectionDiv, fragment); cursor.style.display = "none"; selectionDiv.style.display = ""; @@ -1434,16 +1429,16 @@ window.CodeMirror = (function() { } function moveV(dir, unit) { var dist = 0, pos = localCoords(sel.inverted ? sel.from : sel.to, true); - var x = pos.x, y; + var x = pos.left, y; if (goalColumn != null) x = goalColumn; if (unit == "page") { var pageSize = Math.min(scroller.clientHeight, window.innerHeight || document.documentElement.clientHeight); - y = pos.y + dir * pageSize; + y = pos.top + dir * pageSize; } else if (unit == "line") { - y = dir > 0 ? pos.yBot + 3 : pos.y - 3; + y = dir > 0 ? pos.bottom + 3 : pos.top - 3; } var target = coordsChar(x, y); - if (unit == "page") scrollbar.scrollTop += localCoords(target, true).y - pos.y; + if (unit == "page") scrollbar.scrollTop += localCoords(target, true).top - pos.top; setCursor(target.line, target.ch, true); goalColumn = x; } @@ -1746,7 +1741,7 @@ window.CodeMirror = (function() { function localCoords(pos, inLineWrap) { var x, y = heightAtLine(doc, pos.line) - (inLineWrap ? displayOffset : 0); var sp = measureLine(getLine(pos.line), pos.ch); - return {x: sp.left, y: y + sp.top, yBot: y + sp.bottom}; + return {left: sp.left, top: y + sp.top, bottom: y + sp.bottom}; } // Coords must be lineSpace-local function coordsChar(x, y) { @@ -1793,7 +1788,7 @@ window.CodeMirror = (function() { } function pageCoords(pos) { var local = localCoords(pos, true), off = eltOffset(lineSpace); - return {x: off.left + local.x, y: off.top + local.y, yBot: off.top + local.yBot}; + return {left: off.left + local.left, top: off.top + local.top, bottom: off.top + local.bottom}; } var cachedHeight, cachedHeightFor, measurePre; @@ -2030,8 +2025,8 @@ window.CodeMirror = (function() { } var newScrollPos, updated; if (selectionChanged) { - var coords = calculateCursorCoords(); - newScrollPos = calculateScrollPos(coords.x, coords.y, coords.x, coords.yBot); + var coords = localCoords(sel.inverted ? sel.from : sel.to); + newScrollPos = calculateScrollPos(coords.left, coords.top, coords.left, coords.bottom); } if (changes.length || newScrollPos && newScrollPos.scrollTop != null) updated = updateDisplay(changes, true, newScrollPos && newScrollPos.scrollTop); diff --git a/lib/util/simple-hint.js b/lib/util/simple-hint.js index 8e481c37c2..83df572ed4 100644 --- a/lib/util/simple-hint.js +++ b/lib/util/simple-hint.js @@ -42,13 +42,13 @@ sel.firstChild.selected = true; sel.size = Math.min(10, completions.length); var pos = editor.cursorCoords(); - complete.style.left = pos.x + "px"; - complete.style.top = pos.yBot + "px"; + complete.style.left = pos.left + "px"; + complete.style.top = pos.bottom + "px"; document.body.appendChild(complete); // If we're at the edge of the screen, then we want the menu to appear on the left of the cursor. var winW = window.innerWidth || Math.max(document.body.offsetWidth, document.documentElement.offsetWidth); - if(winW - pos.x < sel.clientWidth) - complete.style.left = (pos.x - sel.clientWidth) + "px"; + if(winW - pos.left < sel.clientWidth) + complete.style.left = (pos.left - sel.clientWidth) + "px"; // Hack to hide the scrollbar. if (completions.length <= 10) complete.style.width = (sel.clientWidth - 1) + "px"; diff --git a/test/test.js b/test/test.js index 099f8a934a..19a227578a 100644 --- a/test/test.js +++ b/test/test.js @@ -154,13 +154,13 @@ testCM("coords", function(cm) { addDoc(cm, 32, 200); var top = cm.charCoords({line: 0, ch: 0}); var bot = cm.charCoords({line: 200, ch: 30}); - is(top.x < bot.x); - is(top.y < bot.y); - is(top.y < top.yBot); + is(top.left < bot.left); + is(top.top < bot.top); + is(top.top < top.bottom); cm.scrollTo(null, 100); var top2 = cm.charCoords({line: 0, ch: 0}); - is(top.y > top2.y); - eq(top.x, top2.x); + is(top.top > top2.top); + eq(top.left, top2.left); }); testCM("coordsChar", function(cm) { @@ -169,7 +169,7 @@ testCM("coordsChar", function(cm) { for (var line = 0; line < 70; line += 5) { cm.setCursor(line, ch); var coords = cm.charCoords({line: line, ch: ch}); - var pos = cm.coordsChar({x: coords.x, y: coords.y + 5}); + var pos = cm.coordsChar({left: coords.left, top: coords.top + 5}); eqPos(pos, {line: line, ch: ch}); } } @@ -326,22 +326,22 @@ testCM("scrollSnap", function(cm) { addDoc(cm, 200, 200); cm.setCursor({line: 100, ch: 180}); var info = cm.getScrollInfo(); - is(info.x > 0 && info.y > 0); + is(info.left > 0 && info.top > 0); cm.setCursor({line: 0, ch: 0}); info = cm.getScrollInfo(); - is(info.x == 0 && info.y == 0, "scrolled clean to top"); + is(info.left == 0 && info.top == 0, "scrolled clean to top"); cm.setCursor({line: 100, ch: 180}); cm.setCursor({line: 199, ch: 0}); info = cm.getScrollInfo(); - is(info.x == 0 && info.y > info.height - 100, "scrolled clean to bottom"); + is(info.left == 0 && info.top > info.height - 100, "scrolled clean to bottom"); }); testCM("selectionPos", function(cm) { cm.setSize(100, 100); addDoc(cm, 200, 100); cm.setSelection({line: 1, ch: 100}, {line: 98, ch: 100}); - var lineWidth = cm.charCoords({line: 0, ch: 200}, "local").x; - var lineHeight = (cm.charCoords({line: 99}).y - cm.charCoords({line: 0}).y) / 100; + var lineWidth = cm.charCoords({line: 0, ch: 200}, "local").left; + var lineHeight = (cm.charCoords({line: 99}).top - cm.charCoords({line: 0}).top) / 100; cm.scrollTo(0, 0); var selElt = byClassName(cm.getWrapperElement(), "CodeMirror-selected"); var outer = cm.getWrapperElement().getBoundingClientRect(); @@ -473,7 +473,7 @@ testCM("wrappingAndResizing", function(cm) { {line: 0, ch: 0}, {line: 1, ch: doc.length}, {line: 1, ch: doc.length - 1}], function(pos) { var coords = cm.charCoords(pos); - eqPos(pos, cm.coordsChar({x: coords.x + 2, y: coords.y + 5})); + eqPos(pos, cm.coordsChar({left: coords.left + 2, top: coords.top + 5})); }); }, null, ie_lt8); @@ -490,10 +490,10 @@ testCM("measureEndOfLine", function(cm) { } cm.setValue(cm.getValue() + "\n\n"); var endPos = cm.charCoords({line: 0, ch: 18}, "local"); - is(endPos.y > lh * .8, "not at top"); - is(endPos.x > w - 20, "not at right"); + is(endPos.top > lh * .8, "not at top"); + is(endPos.left > w - 20, "not at right"); endPos = cm.charCoords({line: 0, ch: 18}); - eqPos(cm.coordsChar({x: endPos.x, y: endPos.y + 2}), {line: 0, ch: 18}); + eqPos(cm.coordsChar({left: endPos.left, top: endPos.top + 5}), {line: 0, ch: 18}); }, {mode: "text/html", value: "", lineWrapping: true}, ie_lt8); testCM("scrollVerticallyAndHorizontally", function(cm) { @@ -523,8 +523,8 @@ testCM("moveV stuck", function(cm) { testCM("clickTab", function(cm) { var p0 = cm.charCoords({line: 0, ch: 0}), p1 = cm.charCoords({line: 0, ch: 1}); - eqPos(cm.coordsChar({x: p0.x + 5, y: p0.y + 5}), {line: 0, ch: 0}); - eqPos(cm.coordsChar({x: p1.x - 5, y: p1.y + 5}), {line: 0, ch: 1}); + eqPos(cm.coordsChar({left: p0.left + 5, top: p0.top + 5}), {line: 0, ch: 0}); + eqPos(cm.coordsChar({left: p1.left - 5, top: p1.top + 5}), {line: 0, ch: 1}); }, {value: "\t\n\n", lineWrapping: true, tabSize: 8}); testCM("verticalScroll", function(cm) { From 165064fc2e474ef507e1705ee9b9806084d21bc5 Mon Sep 17 00:00:00 2001 From: Marijn Haverbeke Date: Wed, 15 Aug 2012 16:25:41 +0200 Subject: [PATCH 0018/5780] More stable drawing of selection --- lib/codemirror.js | 10 ++++------ 1 file changed, 4 insertions(+), 6 deletions(-) diff --git a/lib/codemirror.js b/lib/codemirror.js index 2b8b1df862..267d4f7c11 100644 --- a/lib/codemirror.js +++ b/lib/codemirror.js @@ -1285,12 +1285,10 @@ window.CodeMirror = (function() { fragment.appendChild(elt("div", null, "CodeMirror-selected", "position: absolute; left: " + left + "px; top: " + top + "px; " + rstyle + "; height: " + height + "px")); }; - if (sel.from.ch) { - var right = sameLine ? clientWidth - toPos.left : 0; - add(fromPos.left, fromPos.top, right, fromPos.bottom - fromPos.top); - } - var middleStart = Math.max(0, sel.from.ch ? fromPos.bottom - .5 : fromPos.top); - var middleHeight = toPos.top + (sel.from.ch ? 1 : 0) - middleStart; + var right = sameLine ? clientWidth - toPos.left : 0; + add(fromPos.left, fromPos.top, right, fromPos.bottom - fromPos.top); + var middleStart = Math.max(0, fromPos.bottom - .5); + var middleHeight = toPos.top + 1 - middleStart; if (middleHeight > 2) add(paddingLeft(), middleStart, 0, middleHeight); if ((!sameLine || !sel.from.ch) && sel.to.ch) From 94091f86d4deba7c4375d181370df1c1b4ba3579 Mon Sep 17 00:00:00 2001 From: Marijn Haverbeke Date: Fri, 17 Aug 2012 12:03:21 +0200 Subject: [PATCH 0019/5780] Also measure a 'right' in measureLine Get rid of eolSpanContent mess. Preparation for RTL text handling. --- lib/codemirror.js | 46 +++++++++++++--------------------------------- 1 file changed, 13 insertions(+), 33 deletions(-) diff --git a/lib/codemirror.js b/lib/codemirror.js index 267d4f7c11..75fe2bb017 100644 --- a/lib/codemirror.js +++ b/lib/codemirror.js @@ -1267,8 +1267,8 @@ window.CodeMirror = (function() { var toPos = collapsed ? fromPos : localCoords(sel.to, true); var headPos = sel.inverted ? fromPos : toPos; var wrapOff = eltOffset(wrapper), lineOff = eltOffset(lineDiv); - inputDiv.style.top = Math.max(0, Math.min(scroller.offsetHeight, headPos.y + lineOff.top - wrapOff.top)) + "px"; - inputDiv.style.left = Math.max(0, Math.min(scroller.offsetWidth, headPos.x + lineOff.left - wrapOff.left)) + "px"; + inputDiv.style.top = Math.max(0, Math.min(scroller.offsetHeight, headPos.top + lineOff.top - wrapOff.top)) + "px"; + inputDiv.style.left = Math.max(0, Math.min(scroller.offsetWidth, headPos.left + lineOff.left - wrapOff.left)) + "px"; if (collapsed) { cursor.style.top = headPos.top + "px"; cursor.style.left = (options.lineWrapping ? Math.min(headPos.left, lineSpace.offsetWidth) : headPos.left) + "px"; @@ -1697,13 +1697,15 @@ window.CodeMirror = (function() { } function measureLine(line, ch) { - var pre = line.getElement(makeTab, ch, options.lineWrapping); + var atEnd = ch == line.text.length; + var pre = line.getElement(makeTab, atEnd && ch ? ch - 1 : ch, options.lineWrapping); removeChildrenAndAdd(measure, pre); - var anchor = pre.anchor, left = anchor.offsetLeft; + var anchor = pre.anchor; // We'll sample once at the top, once at the bottom of the line, // to get the real line height (in case there tokens on the line // with bigger fonts) anchor.style.verticalAlign = "top"; + var left = anchor.offsetLeft, right = anchor.offsetLeft + anchor.offsetWidth; if (ie) { // In IE, verticalAlign does not influence offsetTop, unless // the element is an inline-block. Unfortunately, inline @@ -1719,27 +1721,16 @@ window.CodeMirror = (function() { anchor.parentNode.removeChild(anchor.previousSibling); } } - var top = anchor.offsetTop, left = anchor.offsetLeft; - // Older IEs report zero offsets for spans directly after a wrap - if (ie && top == 0 && left == 0) { - anchor = anchor.parentNode.insertBefore(elt("span", "x"), anchor.nextSibling); - top = anchor.offsetTop; left = anchor.offsetLeft; - } + var top = anchor.offsetTop; anchor.style.verticalAlign = "bottom"; var bottom = anchor.offsetTop + anchor.offsetHeight; - // Work around Opera issue -- we can't get reliable wrapping - // info if we put anything in the anchor, but we can't get a - // proper height on empty nodes. - if (!eolSpanContent && bottom == top && ch == line.text.length) { - setTextContent(anchor, " "); - bottom = anchor.offsetTop + anchor.offsetHeight * (left == anchor.offsetLeft ? 1 : .5); - } - return {top: top, bottom: bottom, left: left}; + return {top: top, bottom: bottom, left: atEnd ? right : left, right: right}; } function localCoords(pos, inLineWrap) { - var x, y = heightAtLine(doc, pos.line) - (inLineWrap ? displayOffset : 0); + var off = heightAtLine(doc, pos.line) - (inLineWrap ? displayOffset : 0); var sp = measureLine(getLine(pos.line), pos.ch); - return {left: sp.left, top: y + sp.top, bottom: y + sp.bottom}; + sp.top += off; sp.bottom += off; + return sp; } // Coords must be lineSpace-local function coordsChar(x, y) { @@ -1786,7 +1777,8 @@ window.CodeMirror = (function() { } function pageCoords(pos) { var local = localCoords(pos, true), off = eltOffset(lineSpace); - return {left: off.left + local.left, top: off.top + local.top, bottom: off.top + local.bottom}; + local.left += off.left; local.right += off.left; local.top += off.top; local.bottom += off.top; + return local; } var cachedHeight, cachedHeightFor, measurePre; @@ -2484,16 +2476,6 @@ window.CodeMirror = (function() { } }; - // When measuring the position of the end of a line, different - // browsers require different approaches. If an empty span is added, - // many browsers report bogus offsets. Of those, some (Webkit, - // recent IE) will accept a space without moving the whole span to - // the next line when wrapping it, others work with a zero-width - // space. - var eolSpanContent = " "; - if (gecko || (ie && !ie_lt8)) eolSpanContent = "\u200b"; - else if (opera) eolSpanContent = ""; - // Line objects. These hold state related to a line, including // highlighting info (the styles array). function Line(text, styles, height) { @@ -2727,8 +2709,6 @@ window.CodeMirror = (function() { } else { outPos += l; span_(text, style); - if (outPos == wrapAt && outPos == len) - pre.anchor = pre.appendChild(elt("span", eolSpanContent)); } }; } From c9b03929a009952b1ba1a8463783b9057a72e184 Mon Sep 17 00:00:00 2001 From: Marijn Haverbeke Date: Fri, 17 Aug 2012 09:52:13 +0200 Subject: [PATCH 0020/5780] First stab at bi-directional text support Issue #294 --- doc/manual.html | 23 ++- lib/codemirror.css | 3 + lib/codemirror.js | 501 +++++++++++++++++++++++++++++++++++++-------- test/driver.js | 6 +- test/test.js | 26 ++- 5 files changed, 467 insertions(+), 92 deletions(-) diff --git a/doc/manual.html b/doc/manual.html index 7661c65420..4d215b8f24 100644 --- a/doc/manual.html +++ b/doc/manual.html @@ -541,18 +541,23 @@

    Programming API

    editor instance.
    cursorCoords(start, mode) → object
    -
    Returns an {left, top, bottom} object containing the - coordinates of the cursor. If mode - is "local", they will be relative to the top-left - corner of the editable document. If it is "page" or - not given, they are relative to the top-left corner of the - page. bottom is the coordinate of the bottom of the - cursor. start is a boolean indicating whether you - want the start or the end of the selection.
    +
    Returns an {left, top, bottom} object + containing the coordinates of the cursor position. + If mode is "local", they will be + relative to the top-left corner of the editable document. If it + is "page" or not given, they are relative to the + top-left corner of the page. bottom is the + coordinate of the bottom of the cursor. start is a + boolean indicating whether you want the start or the end of the + selection, or, if a {line, ch} object is given, it + specifies the precise position at which you want to measure.
    charCoords(pos, mode) → object
    Like cursorCoords, but returns the position of an arbitrary characters. pos should be - a {line, ch} object.
    + a {line, ch} object. This differs + from cursorCoords in that it'll give the size of + the whole character, rather than just the position that the + cursor would have when it would sit at that position.
    coordsChar(object) → pos
    Given an {left, top} object (in page coordinates), returns the {line, ch} position that corresponds to diff --git a/lib/codemirror.css b/lib/codemirror.css index 623c799885..53e1ea0eb1 100644 --- a/lib/codemirror.css +++ b/lib/codemirror.css @@ -117,6 +117,9 @@ border-right: none; width: 0; } +.CodeMirror pre.CodeMirror-secondarycursor { + border-left: 1px solid silver; +} .cm-keymap-fat-cursor pre.CodeMirror-cursor { width: auto; border: 0; diff --git a/lib/codemirror.js b/lib/codemirror.js index 75fe2bb017..bcb48fb2e0 100644 --- a/lib/codemirror.js +++ b/lib/codemirror.js @@ -26,9 +26,10 @@ window.CodeMirror = (function() { var lineDiv = elt("div"), selectionDiv = elt("div", null, null, "position: relative; z-index: -1"); // Blinky cursor, and element used to ensure cursor fits at the end of a line var cursor = elt("pre", "\u00a0", "CodeMirror-cursor"), widthForcer = elt("pre", "\u00a0", "CodeMirror-cursor", "visibility: hidden"); + var otherCursor = elt("pre", "\u00a0", "CodeMirror-cursor CodeMirror-secondarycursor"); // Used to measure text size var measure = elt("div", null, "CodeMirror-measure"); - var lineSpace = elt("div", [measure, cursor, widthForcer, selectionDiv, lineDiv], null, "position: relative; z-index: 0"); + var lineSpace = elt("div", [measure, cursor, otherCursor, widthForcer, selectionDiv, lineDiv], null, "position: relative; z-index: 0"); // Moved around its parent to cover visible view var mover = elt("div", [elt("div", [lineSpace], "CodeMirror-lines")], null, "position: relative"); var gutters = elt("div", null, "CodeMirror-gutters"), lineGutter; @@ -206,14 +207,14 @@ window.CodeMirror = (function() { return getStateBefore(line + 1); }, cursorCoords: function(start, mode) { + var pos; if (start == null) start = sel.inverted; - return this.charCoords(start ? sel.from : sel.to, mode); + if (typeof start == "object") pos = clipPos(start); + else pos = start ? sel.from : sel.to; + return cursorCoords(pos, mode || "page"); }, charCoords: function(pos, mode) { - pos = clipPos(pos); - if (mode == "local") return localCoords(pos, false); - if (mode == "div") return localCoords(pos, true); - return pageCoords(pos); + return charCoords(clipPos(pos), mode || "page"); }, coordsChar: function(coords) { var off = eltOffset(lineSpace); @@ -238,7 +239,7 @@ window.CodeMirror = (function() { lineInfo: lineInfo, getViewport: function() { return {from: showingFrom, to: showingTo};}, addWidget: function(pos, node, scroll, vert, horiz) { - pos = localCoords(clipPos(pos)); + pos = cursorCoords(clipPos(pos)); var top = pos.top, left = pos.left; node.style.position = "absolute"; sizer.appendChild(node); @@ -880,6 +881,7 @@ window.CodeMirror = (function() { function replaceRange(code, from, to) { from = clipPos(from); if (!to) to = from; else to = clipPos(to); + if (posLess(to, from)) { var tmp = to; to = from; from = tmp; } code = splitLines(code); function adjustPos(pos) { if (posLess(pos, from)) return pos; @@ -979,7 +981,7 @@ window.CodeMirror = (function() { } function scrollCursorIntoView() { - var coords = localCoords(sel.inverted ? sel.from : sel.to); + var coords = cursorCoords(sel.inverted ? sel.from : sel.to); scrollIntoView(coords.left, coords.top, coords.left, coords.bottom); if (!focused) return; var box = sizer.getBoundingClientRect(), doScroll = null; @@ -1262,41 +1264,80 @@ window.CodeMirror = (function() { } function updateSelection() { - var collapsed = posEq(sel.from, sel.to); - var fromPos = localCoords(sel.from, true); - var toPos = collapsed ? fromPos : localCoords(sel.to, true); - var headPos = sel.inverted ? fromPos : toPos; - var wrapOff = eltOffset(wrapper), lineOff = eltOffset(lineDiv); - inputDiv.style.top = Math.max(0, Math.min(scroller.offsetHeight, headPos.top + lineOff.top - wrapOff.top)) + "px"; - inputDiv.style.left = Math.max(0, Math.min(scroller.offsetWidth, headPos.left + lineOff.left - wrapOff.left)) + "px"; - if (collapsed) { - cursor.style.top = headPos.top + "px"; - cursor.style.left = (options.lineWrapping ? Math.min(headPos.left, lineSpace.offsetWidth) : headPos.left) + "px"; - cursor.style.height = (headPos.bottom - headPos.top) * .85 + "px"; + var headPos; + if (posEq(sel.from, sel.to)) { // No selection, single cursor + var pos = headPos = cursorCoords(sel.from, "div"); + cursor.style.left = pos.left + "px"; + cursor.style.top = pos.top + "px"; + cursor.style.height = (pos.bottom - pos.top) * .85 + "px"; cursor.style.display = ""; selectionDiv.style.display = "none"; + + if (pos.other) { + otherCursor.style.display = ""; + otherCursor.style.left = pos.other.left + "px"; + otherCursor.style.top = pos.other.top + "px"; + otherCursor.style.height = (pos.other.bottom - pos.other.top) * .85 + "px"; + } else { otherCursor.style.display = "none"; } } else { - fromPos.top = Math.max(0, fromPos.top); toPos.top = Math.max(0, toPos.top); - var sameLine = fromPos.top == toPos.top, fragment = document.createDocumentFragment(); + headPos = cursorCoords(sel.inverted ? sel.from : sel.to, "div"); + var fragment = document.createDocumentFragment(); var clientWidth = lineSpace.clientWidth || lineSpace.offsetWidth; - var add = function(left, top, right, height) { + var add = function(left, top, right, bottom) { + if (top < 0) top = 0; var rstyle = quirksMode ? "width: " + (!right ? clientWidth : clientWidth - right - left) + "px" : "right: " + right + "px"; fragment.appendChild(elt("div", null, "CodeMirror-selected", "position: absolute; left: " + left + - "px; top: " + top + "px; " + rstyle + "; height: " + height + "px")); + "px; top: " + top + "px; " + rstyle + "; height: " + (bottom - top) + "px")); + }; + + var middleFrom = sel.from.line + 1, middleTo = sel.to.line - 1, sameLine = sel.from.line == sel.to.line; + var drawForLine = function(line, from, toArg) { + var lineObj = getLine(line), lineLen = lineObj.text.length; + var coords = function(ch) { return charCoords({line: line, ch: ch}, "div", lineObj); }; + iterateBidiSections(getOrder(lineObj), from, toArg == null ? lineLen : toArg, function(from, to, dir) { + var leftPos = coords(dir == "rtl" ? to - 1 : from); + var rightPos = coords(dir == "rtl" ? from : to - 1); + var left = leftPos.left, right = rightPos.right; + if (rightPos.top - leftPos.top > 3) { // Different lines, draw top part + add(left, leftPos.top, 0, leftPos.bottom); + left = paddingLeft(); + } + if (toArg == null && to == lineLen) right = clientWidth; + add(left, rightPos.top, clientWidth - right, rightPos.bottom); + }); }; - var right = sameLine ? clientWidth - toPos.left : 0; - add(fromPos.left, fromPos.top, right, fromPos.bottom - fromPos.top); - var middleStart = Math.max(0, fromPos.bottom - .5); - var middleHeight = toPos.top + 1 - middleStart; - if (middleHeight > 2) - add(paddingLeft(), middleStart, 0, middleHeight); - if ((!sameLine || !sel.from.ch) && sel.to.ch) - add(paddingLeft(), toPos.top, clientWidth - toPos.left, toPos.bottom - toPos.top); + + if (sel.from.ch || sameLine) + // Draw the first line of selection. + drawForLine(sel.from.line, sel.from.ch, sameLine ? sel.to.ch : null); + else + // Simply include it in the middle block. + middleFrom = sel.from.line; + + if (middleFrom <= middleTo) { + // Draw the middle + var botLine = getLine(middleTo), + bottom = charCoords({line: middleTo, ch: botLine.text.length}, "div", botLine); + // Kludge to try and prevent fetching coordinates twice if + // start end end are on same line. + var top = (middleFrom != middleTo || botLine.height > bottom.bottom - bottom.top) ? + charCoords({line: middleFrom, ch: 0}, "div") : bottom; + add(paddingLeft(), top.top, 0, bottom.bottom); + } + + if (!sameLine && sel.to.ch) + drawForLine(sel.to.line, 0, sel.to.ch); + removeChildrenAndAdd(selectionDiv, fragment); - cursor.style.display = "none"; + cursor.style.display = otherCursor.style.display = "none"; selectionDiv.style.display = ""; } + + // Move the hidden textarea near the cursor to prevent scrolling artifacts + var wrapOff = eltOffset(wrapper), lineOff = eltOffset(lineDiv); + inputDiv.style.top = Math.max(0, Math.min(scroller.offsetHeight, headPos.top + lineOff.top - wrapOff.top)) + "px"; + inputDiv.style.left = Math.max(0, Math.min(scroller.offsetWidth, headPos.left + lineOff.left - wrapOff.left)) + "px"; } function setShift(val) { @@ -1385,7 +1426,7 @@ window.CodeMirror = (function() { else return pos; } - function findPosH(dir, unit) { + function findPosH(dir, unit, visually) { var end = sel.inverted ? sel.from : sel.to, line = end.line, ch = end.ch; var lineObj = getLine(line); function findNextLine() { @@ -1395,10 +1436,13 @@ window.CodeMirror = (function() { } } function moveOnce(boundToLine) { - if (ch == (dir < 0 ? 0 : lineObj.text.length)) { - if (!boundToLine && findNextLine()) ch = dir < 0 ? lineObj.text.length : 0; - else return false; - } else ch += dir; + var next = (visually ? moveVisually : moveLogically)(lineObj, ch, dir); + if (next == null) { + if (!boundToLine && findNextLine()) { + if (visually) ch = (dir < 0 ? lineRight : lineLeft)(lineObj); + else ch = dir < 0 ? lineObj.text.length : 0; + } else return false; + } else ch = next; return true; } if (unit == "char") moveOnce(); @@ -1416,17 +1460,16 @@ window.CodeMirror = (function() { } function moveH(dir, unit) { var pos = dir < 0 ? sel.from : sel.to; - if (shiftSelecting || posEq(sel.from, sel.to)) pos = findPosH(dir, unit); + if (shiftSelecting || posEq(sel.from, sel.to)) pos = findPosH(dir, unit, true); setCursor(pos.line, pos.ch, true); } function deleteH(dir, unit) { if (!posEq(sel.from, sel.to)) replaceRange("", sel.from, sel.to); - else if (dir < 0) replaceRange("", findPosH(dir, unit), sel.to); - else replaceRange("", sel.from, findPosH(dir, unit)); + else replaceRange("", sel.from, findPosH(dir, unit, false)); userSelChange = true; } function moveV(dir, unit) { - var dist = 0, pos = localCoords(sel.inverted ? sel.from : sel.to, true); + var dist = 0, pos = cursorCoords(sel.inverted ? sel.from : sel.to, "div"); // FIXME cursorCoords var x = pos.left, y; if (goalColumn != null) x = goalColumn; if (unit == "page") { @@ -1436,7 +1479,7 @@ window.CodeMirror = (function() { y = dir > 0 ? pos.bottom + 3 : pos.top - 3; } var target = coordsChar(x, y); - if (unit == "page") scrollbar.scrollTop += localCoords(target, true).top - pos.top; + if (unit == "page") scrollbar.scrollTop += charCoords(target, "div").top - pos.top; setCursor(target.line, target.ch, true); goalColumn = x; } @@ -1697,8 +1740,8 @@ window.CodeMirror = (function() { } function measureLine(line, ch) { - var atEnd = ch == line.text.length; - var pre = line.getElement(makeTab, atEnd && ch ? ch - 1 : ch, options.lineWrapping); + var atEnd = ch && ch == line.text.length; + var pre = line.getElement(makeTab, atEnd ? ch - 1 : ch, options.lineWrapping); removeChildrenAndAdd(measure, pre); var anchor = pre.anchor; // We'll sample once at the top, once at the bottom of the line, @@ -1726,24 +1769,65 @@ window.CodeMirror = (function() { var bottom = anchor.offsetTop + anchor.offsetHeight; return {top: top, bottom: bottom, left: atEnd ? right : left, right: right}; } - function localCoords(pos, inLineWrap) { - var off = heightAtLine(doc, pos.line) - (inLineWrap ? displayOffset : 0); - var sp = measureLine(getLine(pos.line), pos.ch); - sp.top += off; sp.bottom += off; - return sp; + + function intoCoordSystem(pos, rect, context) { + if (context == "line") return rect; + var yOff = heightAtLine(doc, pos.line) - (context == "div" ? displayOffset : 0); + if (context == "page") { + var lOff = eltOffset(lineSpace); + yOff += lOff.top; rect.left += lOff.left; rect.right += lOff.right; + } + rect.top += yOff; rect.bottom += yOff; + return rect; + } + + function charCoords(pos, context, lineObj) { + return intoCoordSystem(pos, measureLine(lineObj || getLine(pos.line), pos.ch), context); + } + + function cursorCoords(pos, context, lineObj) { + lineObj = lineObj || getLine(pos.line); + function get(ch, right) { + var m = measureLine(lineObj, ch); + if (right) m.left = m.right; else m.right = m.left; + return intoCoordSystem(pos, m, context); + } + var order = getOrder(lineObj), ch = pos.ch; + if (!order) return get(ch); + var main, other, linedir = order[0].level, last = order[order.length-1]; + // Special case for inconsistence between start and end order. + if (!linedir && last.level % 2 != linedir && ch == lineObj.text.length) + return get(last.from, true); + for (var i = 0; i < order.length; ++i) { + var part = order[i], rtl = part.level % 2; + if (part.from < ch && part.to > ch) return get(ch, rtl); + var left = rtl ? part.to : part.from, right = rtl ? part.from : part.to; + if (left == ch) { + var here = get(rtl ? ch - 1 : ch); + if (rtl == linedir) main = here; else other = here; + } else if (right == ch) { + var here = get(rtl ? ch : ch - 1, true); + if (rtl == linedir) main = here; else other = here; + } + } + if (!main) return other; + if (other) main.other = other; + return main; } + // Coords must be lineSpace-local function coordsChar(x, y) { var cw = charWidth(), heightPos = displayOffset + y; if (heightPos < 0) return {line: 0, ch: 0}; var lineNo = lineAtHeight(doc, heightPos); if (lineNo >= doc.size) return {line: doc.size - 1, ch: getLine(doc.size - 1).text.length}; - var lineObj = getLine(lineNo), text = lineObj.text; + var lineObj = getLine(lineNo); + if (!lineObj.text.length) return {line: lineNo, ch: 0}; var tw = options.lineWrapping, innerOff = tw ? heightPos - heightAtLine(doc, lineNo) : 0; if (x < 0) x = 0; var wrongLine = false; - function getX(len) { - var sp = measureLine(lineObj, len); + function getX(ch) { + var sp = cursorCoords({line: lineNo, ch: ch}, "line", lineObj); if (tw) { wrongLine = true; if (innerOff > sp.bottom) return Math.max(0, sp.left - scroller.clientWidth); @@ -1752,34 +1836,38 @@ window.CodeMirror = (function() { } return sp.left; } - var from = 0, fromX = 0, to = text.length, toX; - // Guess a suitable upper bound for our search. - var estimated = Math.min(to, Math.ceil((x + Math.floor(innerOff / textHeight()) * scroller.clientWidth * .9) / cw)); - for (;;) { - var estX = getX(estimated); - if (estX <= x && estimated < to) estimated = Math.min(to, Math.ceil(estimated * 1.2)); - else {toX = estX; to = estimated; break;} - } + var bidi = getOrder(lineObj), dist = lineObj.text.length; + var from = lineLeft(lineObj), fromX = 0, to = lineRight(lineObj), toX; + if (!bidi) { + // Guess a suitable upper bound for our search. + var estimated = Math.min(to, Math.ceil((x + Math.floor(innerOff / textHeight()) * scroller.clientWidth * .9) / cw)); + for (;;) { + var estX = getX(estimated); + if (estX <= x && estimated < to) estimated = Math.min(to, Math.ceil(estimated * 1.2)); + else {toX = estX; to = estimated; break;} + } + // Try to guess a suitable lower bound as well. + estimated = Math.floor(to * 0.8); estX = getX(estimated); + if (estX < x) {from = estimated; fromX = estX;} + dist = to - from; + } else toX = getX(to); if (x > toX) return {line: lineNo, ch: to}; - // Try to guess a suitable lower bound as well. - estimated = Math.floor(to * 0.8); estX = getX(estimated); - if (estX < x) {from = estimated; fromX = estX;} // Do a binary search between these bounds. for (;;) { - if (to - from <= 1) { + if (bidi ? to == from || to == moveVisually(lineObj, from, 1) : to - from <= 1) { var after = x - fromX < toX - x; return {line: lineNo, ch: after ? from : to, after: after}; } - var middle = Math.ceil((from + to) / 2), middleX = getX(middle); - if (middleX > x) {to = middle; toX = middleX; if (wrongLine) toX += 1000; } - else {from = middle; fromX = middleX;} + var step = Math.ceil(dist / 2), middle = from + step; + if (bidi) { + middle = from; + for (var i = 0; i < step; ++i) middle = moveVisually(lineObj, middle, 1); + } + var middleX = getX(middle); + if (middleX > x) {to = middle; toX = middleX; if (wrongLine) toX += 1000; dist -= step;} + else {from = middle; fromX = middleX; dist = step;} } } - function pageCoords(pos) { - var local = localCoords(pos, true), off = eltOffset(lineSpace); - local.left += off.left; local.right += off.left; local.top += off.top; local.bottom += off.top; - return local; - } var cachedHeight, cachedHeightFor, measurePre; function textHeight() { @@ -1863,9 +1951,9 @@ window.CodeMirror = (function() { function restartBlink() { clearInterval(blinker); var on = true; - cursor.style.visibility = ""; + cursor.style.visibility = otherCursor.style.visibility = ""; blinker = setInterval(function() { - cursor.style.visibility = (on = !on) ? "" : "hidden"; + cursor.style.visibility = otherCursor.style.visibility = (on = !on) ? "" : "hidden"; }, options.cursorBlinkRate); } @@ -2015,7 +2103,7 @@ window.CodeMirror = (function() { } var newScrollPos, updated; if (selectionChanged) { - var coords = localCoords(sel.inverted ? sel.from : sel.to); + var coords = cursorCoords(sel.inverted ? sel.from : sel.to); newScrollPos = calculateScrollPos(coords.left, coords.top, coords.left, coords.bottom); } if (changes.length || newScrollPos && newScrollPos.scrollTop != null) @@ -2166,13 +2254,21 @@ window.CodeMirror = (function() { redo: function(cm) {cm.redo();}, goDocStart: function(cm) {cm.setCursor(0, 0, true);}, goDocEnd: function(cm) {cm.setSelection({line: cm.lineCount() - 1}, null, true);}, - goLineStart: function(cm) {cm.setCursor(cm.getCursor().line, 0, true);}, + goLineStart: function(cm) { + var line = cm.getCursor().line; + cm.setCursor(line, lineStart(cm.getLineHandle(line)), true); + }, goLineStartSmart: function(cm) { - var cur = cm.getCursor(); - var text = cm.getLine(cur.line), firstNonWS = Math.max(0, text.search(/\S/)); - cm.setCursor(cur.line, cur.ch <= firstNonWS && cur.ch ? 0 : firstNonWS, true); + var cur = cm.getCursor(), line = cm.getLineHandle(cur.line), order = getOrder(line); + if (!order || order[0].level == 0) { + var firstNonWS = Math.max(0, line.text.search(/\S/)); + cm.setCursor(cur.line, cur.ch <= firstNonWS && cur.ch ? 0 : firstNonWS, true); + } else cm.setCursor(cur.line, lineStart(line), true); + }, + goLineEnd: function(cm) { + var line = cm.getCursor().line; + cm.setCursor(line, lineEnd(cm.getLineHandle(line)), true); }, - goLineEnd: function(cm) {cm.setSelection({line: cm.getCursor().line}, null, true);}, goLineUp: function(cm) {cm.moveV(-1, "line");}, goLineDown: function(cm) {cm.moveV(1, "line");}, goPageUp: function(cm) {cm.moveV(-1, "page");}, @@ -2504,6 +2600,7 @@ window.CodeMirror = (function() { copyStyles(to, this.text.length, this.styles, st); this.styles = st; this.text = this.text.slice(0, from) + text + this.text.slice(to); + this.order = null; this.stateAfter = null; if (mk) { var diff = text.length - (to - from); @@ -2535,6 +2632,7 @@ window.CodeMirror = (function() { append: function(line) { var mylen = this.text.length, mk = line.marked, mymk = this.marked; this.text += line.text; + this.order = this.order || line.order ? null : false; copyStyles(0, line.text.length, line.styles, this.styles); if (mymk) { for (var i = 0; i < mymk.length; ++i) @@ -3244,5 +3342,248 @@ window.CodeMirror = (function() { for (var i = 1; i <= 12; i++) keyNames[i + 111] = keyNames[i + 63235] = "F" + i; })(); + function iterateBidiSections(order, from, to, f) { + if (!order) return f(from, to, "ltr"); + for (var i = 0; i < order.length; ++i) { + var part = order[i]; + if (part.from < to && part.to > from) + f(Math.max(part.from, from), Math.min(part.to, to), part.level == 1 ? "rtl" : "ltr"); + } + } + + function bidiLeft(part) { return part.level % 2 ? part.to : part.from; } + function bidiRight(part) { return part.level % 2 ? part.from : part.to; } + + function lineLeft(line) { var order = getOrder(line); return order ? bidiLeft(order[0]) : 0; } + function lineRight(line) { + var order = getOrder(line); + if (!order) return line.text.length; + var last = order[order.length-1]; + return order[0].level != last.level ? line.text.length : bidiRight(last); + } + function lineStart(line) { + var order = getOrder(line); + if (!order) return 0; + return order[0].level % 2 ? lineRight(line) : lineLeft(line); + } + function lineEnd(line) { + var order = getOrder(line); + if (!order) return line.text.length; + return order[0].level % 2 ? lineLeft(line) : lineRight(line); + } + + // This is somewhat involved. It is needed in order to move + // 'visually' through bi-directional text -- i.e., pressing left + // should make the cursor go left, even when in RTL text. The + // tricky part is the 'jumps', where RTL and LTR text touch each + // other. This often requires the cursor offset to move more than + // one unit, in order to visually move one unit. + function moveVisually(line, start, dir) { + var bidi = getOrder(line); + if (!bidi) return moveLogically(line, start, dir); + var linedir = bidi[0].level, last = bidi[bidi.length-1]; + if (linedir == last.level % 2) last = null; + for (var i = 0; i < bidi.length; ++i) { + var part = bidi[i], sticky = part.level % 2 == linedir; + if ((part.from < start && part.to > start) || + (sticky && (part.from == start || part.to == start))) break; + } + var target = start + (part.level % 2 ? -dir : dir); + if (i == bidi.length) { + if (dir > 0) return null; // Moving right from EOL + target = last.level % 1 ? last.from + 1 : last.to - 1; + if (last.to - last.from == 1) return bidiRight(bidi[i-2]); + else return target; + } + + while (target != null) { + if (part.level % 2 == linedir) { + if (target < part.from || target > part.to) { + part = bidi[i += dir]; + target = part && (dir > 0 == part.level % 2 ? part.to - 1 : part.from + 1); + } else break; + } else { + if (target == bidiLeft(part)) { + part = bidi[--i]; + target = part && bidiRight(part); + } else if (target == bidiRight(part)) { + if (part == last) return line.text.length; + part = bidi[++i]; + target = part && bidiLeft(part); + } else break; + } + } + + return target < 0 || target > line.text.length ? null : target; + } + + function moveLogically(line, start, dir) { + var target = start + dir; + return target < 0 || target > line.text.length ? null : target; + } + + function getOrder(line) { + var order = line.order; + if (order == null) order = line.order = bidiOrdering(line.text); + return order; + } + + // Bidirectional ordering algorithm + // See http://unicode.org/reports/tr9/tr9-13.html for the algorithm + // that this (partially) implements. + + // One-char codes used for character types: + // L (L): Left-to-Right + // R (R): Right-to-Left + // r (AL): Right-to-Left Arabic + // 1 (EN): European Number + // + (ES): European Number Separator + // % (ET): European Number Terminator + // n (AN): Arabic Number + // , (CS): Common Number Separator + // m (NSM): Non-Spacing Mark + // b (BN): Boundary Neutral + // s (B): Paragraph Separator + // t (S): Segment Separator + // w (WS): Whitespace + // N (ON): Other Neutrals + + // Returns null if characters are ordered as they appear + // (left-to-right), or an array of sections ({from, to, level} + // objects) in the order in which they occur visually. + var bidiOrdering = (function() { + // Character types for codepoints 0 to 0xff + var lowTypes = "bbbbbbbbbtstwsbbbbbbbbbbbbbbssstwNN%%%NNNNNN,N,N1111111111NNNNNNNLLLLLLLLLLLLLLLLLLLLLLLLLLNNNNNNLLLLLLLLLLLLLLLLLLLLLLLLLLNNNNbbbbbbsbbbbbbbbbbbbbbbbbbbbbbbbbb,N%%%%NNNNLNNNNN%%11NLNNN1LNNNNNLLLLLLLLLLLLLLLLLLLLLLLNLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLNLLLLLLLL"; + // Character types for codepoints 0x600 to 0x6ff + var arabicTypes = "rrrrrrrrrrrr,rNNmmmmmmrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrmmmmmmmmmmmmmmrrrrrrrnnnnnnnnnn%nnrrrmrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrmmmmmmmmmmmmmmmmmmmNmmmmrrrrrrrrrrrrrrrrrr"; + function charType(code) { + var type = "L"; + if (code <= 0xff) return lowTypes.charAt(code); + else if (0x590 <= code && code <= 0x5f4) return "R"; + else if (0x600 <= code && code <= 0x6ff) return arabicTypes.charAt(code - 0x600); + else if (0x700 <= code && code <= 0x8ac) return "r"; + else return "L"; + } + + var bidiRE = /[\u0590-\u05f4\u0600-\u06ff\u0700-\u08ac]/; + var isNeutral = /[stwN]/, isStrong = /[LRr]/, countsAsLeft = /[Lb1n]/, countsAsNum = /[1n]/; + + return function charOrdering(str) { + if (!bidiRE.test(str)) return false; + var len = str.length, types = new Array(len), startType = null; + for (var i = 0; i < len; ++i) { + var type = charType(str.charCodeAt(i)); + types[i] = type; + if (startType == null) { + if (type == "L") startType = "L"; + else if (type == "R" || type == "r") startType = "R"; + } + } + if (startType == null) startType = "L"; + + // W1. Examine each non-spacing mark (NSM) in the level run, and + // change the type of the NSM to the type of the previous + // character. If the NSM is at the start of the level run, it will + // get the type of sor. + for (var i = 0, prev = startType; i < len; ++i) { + var type = types[i]; + if (type == "m") types[i] = lastType; + else prev = type; + } + + // W2. Search backwards from each instance of a European number + // until the first strong type (R, L, AL, or sor) is found. If an + // AL is found, change the type of the European number to Arabic + // number. + // W3. Change all ALs to R. + for (var i = 0, cur = startType; i < len; ++i) { + var type = types[i]; + if (type == "1" && cur == "r") types[i] = "n"; + else if (isStrong.test(type)) { cur = type; if (type == "r") types[i] = "R"; } + } + + // W4. A single European separator between two European numbers + // changes to a European number. A single common separator between + // two numbers of the same type changes to that type. + for (var i = 1, prev = types[0]; i < len - 1; ++i) { + var type = types[i]; + if (type == "+" && prev == "1" && types[i+1] == "1") types[i] = "1"; + else if (type == "," && prev == types[i+1] && + (prev == "1" || prev == "n")) types[i] = prev; + prev = type; + } + + // W5. A sequence of European terminators adjacent to European + // numbers changes to all European numbers. + // W6. Otherwise, separators and terminators change to Other + // Neutral. + for (var i = 0; i < len; ++i) { + var type = types[i]; + if (type == ",") types[i] = "N"; + else if (type == "%") { + for (var end = i + 1; end < len && types[end] == "%"; ++end) {} + var replace = (i && types[i-1] == "!") || (end < len - 1 && types[end] == "1") ? "1" : "N"; + for (var j = i; j < end; ++j) types[j] = replace; + i = end - 1; + } + } + + // W7. Search backwards from each instance of a European number + // until the first strong type (R, L, or sor) is found. If an L is + // found, then change the type of the European number to L. + for (var i = 0, cur = startType; i < len; ++i) { + var type = types[i]; + if (cur == "L" && type == "1") types[i] = "L"; + else if (isStrong.test(type)) cur = type; + } + + // N1. A sequence of neutrals takes the direction of the + // surrounding strong text if the text on both sides has the same + // direction. European and Arabic numbers act as if they were R in + // terms of their influence on neutrals. Start-of-level-run (sor) + // and end-of-level-run (eor) are used at level run boundaries. + // N2. Any remaining neutrals take the embedding direction. + for (var i = 0; i < len; ++i) { + if (isNeutral.test(types[i])) { + for (var end = i + 1; end < len && isNeutral.test(types[end]); ++end) {} + var before = (i ? types[i-1] : startType) == "L"; + var after = (end < len - 1 ? types[end] : startType) == "L"; + var replace = before == after ? (before ? "L" : "R") : startType; + for (var j = i; j < end; ++j) types[j] = replace; + i = end - 1; + } + } + + // Here we depart from the documented algorithm, in order to avoid + // building up an actual levels array. Since there are only three + // levels (0, 1, 2) in an implementation that doesn't take + // explicit embedding into account, we can build up the order on + // the fly, without following the level-based algorithm. + var order = []; + for (var i = 0; i < len;) { + if (countsAsLeft.test(types[i])) { + var start = i; + for (++i; i < len && countsAsLeft.test(types[i]); ++i) {} + order.push({from: start, to: i, level: 0}); + } else { + var pos = i, at = order.length; + for (++i; i < len && types[i] != "L"; ++i) {} + for (var j = pos; j < i;) { + if (countsAsNum.test(types[j])) { + if (pos < j) order.splice(at, 0, {from: pos, to: j, level: 1}); + var nstart = j; + for (++j; j < i && countsAsNum.test(types[j]); ++j) {} + order.splice(at, 0, {from: nstart, to: j, level: 2}); + pos = j; + } else ++j; + } + if (pos < i) order.splice(at, 0, {from: pos, to: i, level: 1}); + } + } + + return order; + }; + })(); + return CodeMirror; })(); diff --git a/test/driver.js b/test/driver.js index 975c24f9fc..12bdc1e29f 100644 --- a/test/driver.js +++ b/test/driver.js @@ -1,6 +1,7 @@ var tests = [], debug = null; function Failure(why) {this.message = why;} +Failure.prototype.toString = function() { return this.message; }; function test(name, run, expectedFail) { tests.push({name: name, func: run, expectedFail: expectedFail}); @@ -19,7 +20,10 @@ function runTests(callback) { function step(i) { if (i == tests.length) return callback("done"); var test = tests[i], expFail = test.expectedFail; - if (debug != null && debug != test.name) return step(i + 1); + if (debug != null) { + if (debug == test.name) return test.func(); + else return step(i + 1); + } try { test.func(); if (expFail) callback("fail", test.name, "was expected to fail, but succeeded"); diff --git a/test/test.js b/test/test.js index 19a227578a..5f26fdd986 100644 --- a/test/test.js +++ b/test/test.js @@ -199,7 +199,7 @@ testCM("posFromIndex", function(cm) { eq(pos.ch, example.ch); if (example.index >= 0 && example.index < 64) eq(cm.indexFromPos(pos), example.index); - } + } }); testCM("undo", function(cm) { @@ -397,7 +397,7 @@ testCM("doubleScrollbar", function(cm) { cm.setSize(null, 100); addDoc(cm, 1, 300); var wrap = cm.getWrapperElement(); - is(wrap.offsetWidth - byClassName(wrap, "CodeMirror-lines")[0].offsetWidth <= scrollbarWidth); + is(wrap.offsetWidth - byClassName(wrap, "CodeMirror-lines")[0].offsetWidth <= scrollbarWidth + 1); }); testCM("weirdLinebreaks", function(cm) { @@ -658,3 +658,25 @@ testCM("verticalMovementCommandsWrapping", function(cm) { } }, {value: "a very long line that wraps around somehow so that we can test cursor movement\nshortone\nk", lineWrapping: true}); + +testCM("rtlMovement", function(cm) { + forEach(["خحج", "خحabcخحج", "abخحخحجcd", "abخde", "abخح2342خ1حج", "خ1ح2خح3حxج", "خحcd", "1خحcd", "abcdeح1ج"], function(line) { + var inv = line.charAt(0) == "خ"; + cm.setValue(line + "\n"); cm.execCommand(inv ? "goLineEnd" : "goLineStart"); + var cursor = byClassName(cm.getWrapperElement(), "CodeMirror-cursor")[0]; + var prevX = cursor.offsetLeft, prevY = cursor.offsetTop; + for (var i = 0; i <= line.length; ++i) { + cm.execCommand("goCharRight"); + if (i == line.length) is(cursor.offsetTop > prevY, "next line"); + else is(cursor.offsetLeft > prevX, "moved right"); + prevX = cursor.offsetLeft; prevY = cursor.offsetTop; + } + cm.setCursor(0, 0); cm.execCommand(inv ? "goLineStart" : "goLineEnd"); + prevX = cursor.offsetLeft; + for (var i = 0; i < line.length; ++i) { + cm.execCommand("goCharLeft"); + is(cursor.offsetLeft < prevX, "moved left"); + prevX = cursor.offsetLeft; + } + }); +}); From 6e9c3de2b65f205451b11201400e6abaf5790e8b Mon Sep 17 00:00:00 2001 From: Marijn Haverbeke Date: Thu, 23 Aug 2012 13:40:48 +0200 Subject: [PATCH 0021/5780] Memoize measureLine --- lib/codemirror.js | 15 ++++++++++++++- 1 file changed, 14 insertions(+), 1 deletion(-) diff --git a/lib/codemirror.js b/lib/codemirror.js index bcb48fb2e0..981eed38d0 100644 --- a/lib/codemirror.js +++ b/lib/codemirror.js @@ -64,6 +64,7 @@ window.CodeMirror = (function() { // Delayed object wrap timeouts, making sure only one is active. blinker holds an interval. var poll = new Delayed(), highlight = new Delayed(), blinker; + var measureLineMemo = [], measureLineMemoPos = 0; // mode holds a mode API object. doc is the tree of Line objects, // work an array of lines that should be parsed, and history the // undo history (instance of History constructor). @@ -1740,6 +1741,12 @@ window.CodeMirror = (function() { } function measureLine(line, ch) { + for (var i = 0; i < measureLineMemo.length; ++i) { + var memo = measureLineMemo[i]; + if (memo.ch == ch && memo.text == line.text && + (!options.lineWrapping || scroller.clientWidth == memo.width)) + return {top: memo.top, bottom: memo.bottom, left: memo.left, right: memo.right}; + } var atEnd = ch && ch == line.text.length; var pre = line.getElement(makeTab, atEnd ? ch - 1 : ch, options.lineWrapping); removeChildrenAndAdd(measure, pre); @@ -1767,7 +1774,13 @@ window.CodeMirror = (function() { var top = anchor.offsetTop; anchor.style.verticalAlign = "bottom"; var bottom = anchor.offsetTop + anchor.offsetHeight; - return {top: top, bottom: bottom, left: atEnd ? right : left, right: right}; + if (atEnd) left = right; + + var memo = {ch: ch, text: line.text, width: scroller.clientWidth, + top: top, bottom: bottom, left: left, right: right}; + if (measureLineMemo.length == 8) measureLineMemo[++measureLineMemoPos % 8] = memo; + else measureLineMemo.push(memo); + return {top: top, bottom: bottom, left: left, right: right}; } function intoCoordSystem(pos, rect, context) { From 01a6f5a206dcb69047957a3f796e87ae60d9d7a5 Mon Sep 17 00:00:00 2001 From: Marijn Haverbeke Date: Fri, 24 Aug 2012 18:14:11 +0200 Subject: [PATCH 0022/5780] New scrolling model This will make fixedGutter work properly, and make it the only model (no more gutter that scrolls out of view). Also reorganizes the basic css file to be easier to modify for users. Issue #730 --- lib/codemirror.css | 237 +++++++++++++++++++++++------------------- lib/codemirror.js | 217 ++++++++++++++++++++++---------------- test/test.js | 4 +- theme/ambiance.css | 9 +- theme/blackboard.css | 4 +- theme/cobalt.css | 4 +- theme/erlang-dark.css | 4 +- theme/lesser-dark.css | 4 +- theme/monokai.css | 4 +- theme/night.css | 4 +- theme/rubyblue.css | 4 +- theme/vibrant-ink.css | 4 +- theme/xq-dark.css | 4 +- 13 files changed, 280 insertions(+), 223 deletions(-) diff --git a/lib/codemirror.css b/lib/codemirror.css index 53e1ea0eb1..dd2e71b80a 100644 --- a/lib/codemirror.css +++ b/lib/codemirror.css @@ -1,98 +1,175 @@ +/* BASICS */ + .CodeMirror { - line-height: 1; + /* Set width, borders, and global font properties here */ font-family: monospace; +} +.CodeMirror-scroll { + /* Set height and scrolling behaviour here */ + height: 300px; + overflow: auto; +} + +/* PADDING */ + +.CodeMirror-lines { + padding: 4px 0; /* Vertical padding around content */ +} +.CodeMirror pre { + padding: 0 4px; /* Horizontal padding of content */ +} + +.CodeMirror-scrollbar-filler { + background-color: white; /* The little square between H and V scrollbars */ +} + +/* GUTTER */ + +.CodeMirror-gutters { + border-right: 1px solid #ddd; + background-color: #f7f7f7; +} +.CodeMirror-linenumbers { + padding: 0 4px; +} +.CodeMirror-linenumber { + padding: 0 4px; + min-width: 20px; + text-align: right; + color: #999; +} + +/* CURSOR */ + +.CodeMirror pre.CodeMirror-cursor { + border-left: 1px solid black; +} +/* Shown when moving in bi-directional text */ +.CodeMirror pre.CodeMirror-secondarycursor { + border-left: 1px solid silver; +} +.cm-keymap-fat-cursor pre.CodeMirror-cursor { + width: auto; + border: 0; + background: transparent; + background: rgba(0, 200, 0, .4); + filter: progid:DXImageTransform.Microsoft.gradient(startColorstr=#6600c800, endColorstr=#4c00c800); +} +/* Kludge to turn off filter in ie9+, which also accepts rgba */ +.cm-keymap-fat-cursor pre.CodeMirror-cursor:not(#nonsense_id) { + filter: progid:DXImageTransform.Microsoft.gradient(enabled=false); +} +/* Can style cursor different in overwrite (non-insert) mode */ +.CodeMirror pre.CodeMirror-cursor.CodeMirror-overwrite {} + +/* DEFAULT THEME */ + +.cm-s-default .cm-keyword {color: #708;} +.cm-s-default .cm-atom {color: #219;} +.cm-s-default .cm-number {color: #164;} +.cm-s-default .cm-def {color: #00f;} +.cm-s-default .cm-variable {color: black;} +.cm-s-default .cm-variable-2 {color: #05a;} +.cm-s-default .cm-variable-3 {color: #085;} +.cm-s-default .cm-property {color: black;} +.cm-s-default .cm-operator {color: black;} +.cm-s-default .cm-comment {color: #a50;} +.cm-s-default .cm-string {color: #a11;} +.cm-s-default .cm-string-2 {color: #f50;} +.cm-s-default .cm-meta {color: #555;} +.cm-s-default .cm-error {color: #f00;} +.cm-s-default .cm-qualifier {color: #555;} +.cm-s-default .cm-builtin {color: #30a;} +.cm-s-default .cm-bracket {color: #cc7;} +.cm-s-default .cm-tag {color: #170;} +.cm-s-default .cm-attribute {color: #00c;} +.cm-s-default .cm-header {color: blue;} +.cm-s-default .cm-quote {color: #090;} +.cm-s-default .cm-hr {color: #999;} +.cm-s-default .cm-link {color: #00c;} + +.cm-header, .cm-strong {font-weight: bold;} +.cm-em {font-style: italic;} +.cm-emstrong {font-style: italic; font-weight: bold;} +.cm-link {text-decoration: underline;} + +.cm-invalidchar {color: #f00;} + +div.CodeMirror span.CodeMirror-matchingbracket {color: #0f0;} +div.CodeMirror span.CodeMirror-nonmatchingbracket {color: #f22;} - /* Necessary so the scrollbar can be absolutely positioned within the wrapper on Lion. */ +/* STOP */ + +/* The rest of this file contains styles related to the mechanics of + the editor. You probably shouldn't touch them. */ + +.CodeMirror { + line-height: 1; position: relative; - /* This prevents unwanted scrollbars from showing up on the body and wrapper in IE. */ overflow: hidden; } .CodeMirror-scroll { - overflow: auto; - height: 300px; - /* This is needed to prevent an IE[67] bug where the scrolled content - is visible outside of the scrolling box. */ + /* 30px is the magic margin used to hide the element's real scrollbars */ + /* See overflow: hidden in .CodeMirror, and the paddings in .CodeMirror-sizer */ + margin-bottom: -30px; margin-right: -30px; + outline: none; /* Prevent dragging from highlighting the element */ +} +.CodeMirror-sizer { + padding: 0 30px 30px 0; /* Scrollbar-hiding hack */ position: relative; - outline: none; } -/* Vertical scrollbar */ -.CodeMirror-scrollbar { +/* The fake, visible scrollbars. Used to force redraw during scrolling + before actuall scrolling happens, thus preventing shaking and + flickering artifacts. */ +.CodeMirror-vscrollbar, .CodeMirror-hscrollbar, .CodeMirror-scrollbar-filler { position: absolute; + z-index: 5; + display: none; +} +.CodeMirror-vscrollbar { right: 0; top: 0; overflow-x: hidden; overflow-y: scroll; - z-index: 5; } -.CodeMirror-scrollbar-inner { - /* This needs to have a nonzero width in order for the scrollbar to appear - in Firefox and IE9. */ - width: 1px; -} -.CodeMirror-scrollbar.cm-sb-overlap { - /* Ensure that the scrollbar appears in Lion, and that it overlaps the content - rather than sitting to the right of it. */ - position: absolute; - z-index: 1; - float: none; - right: 0; - min-width: 12px; +.CodeMirror-hscrollbar { + bottom: 0; left: 0; + overflow-y: hidden; + overflow-x: scroll; } -.CodeMirror-scrollbar.cm-sb-nonoverlap { - min-width: 12px; -} -.CodeMirror-scrollbar.cm-sb-ie7 { - min-width: 18px; +.CodeMirror-scrollbar-filler { + right: 0; bottom: 0; + z-index: 5; } .CodeMirror-gutters { position: absolute; left: 0; top: 0; height: 100%; - border-right: 1px solid #ddd; - background-color: #f7f7f7; -} -.CodeMirror-gutter { - height: 100%; - float: left; + z-index: 1; } .CodeMirror-gutter-elt { position: absolute; - top: 0; cursor: default; -} - -.CodeMirror-linenumbers { - padding: 0 .2em; -} -.CodeMirror-linenumber { - min-width: 1.8em; - text-align: right; - color: #999; - padding: 0 .2em; - white-space: pre !important; - font-size: 80%; + z-index: 2; } .CodeMirror-lines { - padding: .4em 0; - white-space: pre; cursor: text; } - .CodeMirror pre { + /* Reset some styles that the rest of the page might have set */ -moz-border-radius: 0; -webkit-border-radius: 0; -o-border-radius: 0; border-radius: 0; border-width: 0; background: transparent; font-family: inherit; font-size: inherit; - padding: 0 .4em; margin: 0; + margin: 0; white-space: pre; word-wrap: normal; line-height: inherit; color: inherit; } - .CodeMirror-wrap pre { word-wrap: break-word; white-space: pre-wrap; @@ -110,28 +187,12 @@ } .CodeMirror pre.CodeMirror-cursor { - z-index: 10; + z-index: 4; position: absolute; visibility: hidden; - border-left: 1px solid black; border-right: none; width: 0; } -.CodeMirror pre.CodeMirror-secondarycursor { - border-left: 1px solid silver; -} -.cm-keymap-fat-cursor pre.CodeMirror-cursor { - width: auto; - border: 0; - background: transparent; - background: rgba(0, 200, 0, .4); - filter: progid:DXImageTransform.Microsoft.gradient(startColorstr=#6600c800, endColorstr=#4c00c800); -} -/* Kludge to turn off filter in ie9+, which also accepts rgba */ -.cm-keymap-fat-cursor pre.CodeMirror-cursor:not(#nonsense_id) { - filter: progid:DXImageTransform.Microsoft.gradient(enabled=false); -} -.CodeMirror pre.CodeMirror-cursor.CodeMirror-overwrite {} .CodeMirror-focused pre.CodeMirror-cursor { visibility: visible; } @@ -144,42 +205,6 @@ div.CodeMirror-selected { background: #d9d9d9; } background: rgba(255, 255, 0, .4); } -/* Default theme */ - -.cm-s-default span.cm-keyword {color: #708;} -.cm-s-default span.cm-atom {color: #219;} -.cm-s-default span.cm-number {color: #164;} -.cm-s-default span.cm-def {color: #00f;} -.cm-s-default span.cm-variable {color: black;} -.cm-s-default span.cm-variable-2 {color: #05a;} -.cm-s-default span.cm-variable-3 {color: #085;} -.cm-s-default span.cm-property {color: black;} -.cm-s-default span.cm-operator {color: black;} -.cm-s-default span.cm-comment {color: #a50;} -.cm-s-default span.cm-string {color: #a11;} -.cm-s-default span.cm-string-2 {color: #f50;} -.cm-s-default span.cm-meta {color: #555;} -.cm-s-default span.cm-error {color: #f00;} -.cm-s-default span.cm-qualifier {color: #555;} -.cm-s-default span.cm-builtin {color: #30a;} -.cm-s-default span.cm-bracket {color: #cc7;} -.cm-s-default span.cm-tag {color: #170;} -.cm-s-default span.cm-attribute {color: #00c;} -.cm-s-default span.cm-header {color: blue;} -.cm-s-default span.cm-quote {color: #090;} -.cm-s-default span.cm-hr {color: #999;} -.cm-s-default span.cm-link {color: #00c;} - -span.cm-header, span.cm-strong {font-weight: bold;} -span.cm-em {font-style: italic;} -span.cm-emstrong {font-style: italic; font-weight: bold;} -span.cm-link {text-decoration: underline;} - -span.cm-invalidchar {color: #f00;} - -div.CodeMirror span.CodeMirror-matchingbracket {color: #0f0;} -div.CodeMirror span.CodeMirror-nonmatchingbracket {color: #f22;} - @media print { /* Hide the cursor when printing */ .CodeMirror pre.CodeMirror-cursor { diff --git a/lib/codemirror.js b/lib/codemirror.js index 981eed38d0..1ec0520842 100644 --- a/lib/codemirror.js +++ b/lib/codemirror.js @@ -18,28 +18,31 @@ window.CodeMirror = (function() { input.setAttribute("wrap", "off"); input.setAttribute("autocorrect", "off"); input.setAttribute("autocapitalize", "off"); // Wraps and hides input textarea var inputDiv = elt("div", [input], null, "overflow: hidden; position: relative; width: 3px; height: 0px;"); - // The empty scrollbar content, used solely for managing the scrollbar thumb. - var scrollbarInner = elt("div", null, "CodeMirror-scrollbar-inner"); - // The vertical scrollbar. Horizontal scrolling is handled by the scroller itself. - var scrollbar = elt("div", [scrollbarInner], "CodeMirror-scrollbar"); + // The actual fake scrollbars. + var scrollbarH = elt("div", [elt("div", null, null, "height: 1px")], "CodeMirror-hscrollbar"); + var scrollbarV = elt("div", [elt("div", null, null, "width: 1px")], "CodeMirror-vscrollbar"); + var scrollbarFiller = elt("div", null, "CodeMirror-scrollbar-filler"); // DIVs containing the selection and the actual code var lineDiv = elt("div"), selectionDiv = elt("div", null, null, "position: relative; z-index: -1"); // Blinky cursor, and element used to ensure cursor fits at the end of a line - var cursor = elt("pre", "\u00a0", "CodeMirror-cursor"), widthForcer = elt("pre", "\u00a0", "CodeMirror-cursor", "visibility: hidden"); + var cursor = elt("pre", "\u00a0", "CodeMirror-cursor"); + // Secondary cursor, shown when on a 'jump' in bi-directional text var otherCursor = elt("pre", "\u00a0", "CodeMirror-cursor CodeMirror-secondarycursor"); // Used to measure text size var measure = elt("div", null, "CodeMirror-measure"); - var lineSpace = elt("div", [measure, cursor, otherCursor, widthForcer, selectionDiv, lineDiv], null, "position: relative; z-index: 0"); + // Wraps everything that needs to exist inside the vertically-padded coordinate system + var lineSpace = elt("div", [measure, cursor, otherCursor, selectionDiv, lineDiv], null, "position: relative"); // Moved around its parent to cover visible view var mover = elt("div", [elt("div", [lineSpace], "CodeMirror-lines")], null, "position: relative"); var gutters = elt("div", null, "CodeMirror-gutters"), lineGutter; // Set to the height of the text, causes scrolling - var sizer = elt("div", [gutters, mover], null, "position: relative;"); + var sizer = elt("div", [mover], "CodeMirror-sizer"); // Provides scrolling var scroller = elt("div", [sizer], "CodeMirror-scroll"); scroller.setAttribute("tabIndex", "-1"); // The element in which the editor lives. - var wrapper = elt("div", [inputDiv, scrollbar, scroller], "CodeMirror" + (options.lineWrapping ? " CodeMirror-wrap" : "")); + var wrapper = elt("div", [gutters, inputDiv, scrollbarH, scrollbarV, scrollbarFiller, scroller], + "CodeMirror" + (options.lineWrapping ? " CodeMirror-wrap" : "")); if (place.appendChild) place.appendChild(wrapper); else place(wrapper); themeChanged(); keyMapChanged(); @@ -53,13 +56,8 @@ window.CodeMirror = (function() { // Needed to handle Tab key in KHTML if (khtml) inputDiv.style.height = "1px", inputDiv.style.position = "absolute"; - // Check for OS X >= 10.7. This has transparent scrollbars, so the - // overlaying of one scrollbar with another won't work. This is a - // temporary hack to simply turn off the overlay scrollbar. See - // issue #727. - if (mac_geLion) { scrollbar.style.zIndex = -2; scrollbar.style.visibility = "hidden"; } // Need to set a minimum width to see the scrollbar on IE7 (but must not set it on IE8). - else if (ie_lt8) scrollbar.style.minWidth = "18px"; + else if (ie_lt8) scrollbarH.style.minWidth = scrollbarV.style.minWidth = "18px"; // Delayed object wrap timeouts, making sure only one is active. blinker holds an interval. var poll = new Delayed(), highlight = new Delayed(), blinker; @@ -76,7 +74,7 @@ window.CodeMirror = (function() { var sel = {from: {line: 0, ch: 0}, to: {line: 0, ch: 0}, inverted: false}; // Selection-related flags. shiftSelecting obviously tracks // whether the user is holding shift. - var shiftSelecting, lastClick, lastDoubleClick, lastScrollTop = 0, draggingText, + var shiftSelecting, lastClick, lastDoubleClick, lastScrollTop = 0, lastScrollLeft = 0, draggingText, overwrite = false, suppressEdits = false; // Variables used by startOperation/endOperation to track what // happened during the operation. @@ -108,8 +106,10 @@ window.CodeMirror = (function() { // handled in onMouseDown for Gecko. if (!gecko) connect(scroller, "contextmenu", onContextMenu); connect(scroller, "scroll", onScrollMain); - connect(scrollbar, "scroll", onScrollBar); - connect(scrollbar, "mousedown", function() {if (focused) setTimeout(focusInput, 0);}); + connect(scrollbarH, "scroll", onScrollBarH); + connect(scrollbarV, "scroll", onScrollBarV); + connect(scrollbarH, "mousedown", function() {if (focused) setTimeout(focusInput, 0);}); + connect(scrollbarV, "mousedown", function() {if (focused) setTimeout(focusInput, 0);}); var resizeHandler = connect(window, "resize", function() { if (wrapper.parentNode) updateDisplay(true); else resizeHandler(); @@ -246,7 +246,7 @@ window.CodeMirror = (function() { sizer.appendChild(node); if (vert == "over") top = pos.top; else if (vert == "near") { - var vspace = Math.max(scroller.offsetHeight, doc.height), + var vspace = Math.max(wrapper.clientHeight, doc.height), hspace = Math.max(sizer.clientWidth, lineSpace.clientWidth); if (pos.bottom + node.offsetHeight > vspace && pos.top > node.offsetHeight) top = pos.top - node.offsetHeight; @@ -327,13 +327,13 @@ window.CodeMirror = (function() { return index; }, scrollTo: function(x, y) { - if (x != null) scroller.scrollLeft = x; - if (y != null) scrollbar.scrollTop = scroller.scrollTop = y; + if (x != null) scrollbarH.scrollLeft = scroller.scrollLeft = x; + if (y != null) scrollbarV.scrollTop = scroller.scrollTop = y; updateDisplay([]); }, getScrollInfo: function() { - return {left: scroller.scrollLeft, top: scrollbar.scrollTop, - height: scrollbar.scrollHeight, width: scroller.scrollWidth}; + return {left: scroller.scrollLeft, top: scroller.scrollTop, + height: scroller.scrollHeight, width: scroller.scrollWidth}; }, setSize: function(width, height) { function interpret(val) { @@ -349,8 +349,8 @@ window.CodeMirror = (function() { compoundChange: function(f){return compoundChange(f);}, refresh: function(){ updateDisplay(true, null, lastScrollTop); - if (scrollbar.scrollHeight > lastScrollTop) - scrollbar.scrollTop = lastScrollTop; + if (scrollbarV.scrollHeight > lastScrollTop) + scrollbarV.scrollTop = lastScrollTop; }, getInputField: function(){return input;}, getWrapperElement: function(){return wrapper;}, @@ -376,20 +376,38 @@ window.CodeMirror = (function() { return text.join(lineSep || "\n"); } - function onScrollBar(e) { - if (scrollbar.scrollTop != lastScrollTop) { - lastScrollTop = scroller.scrollTop = scrollbar.scrollTop; + function lineNumberLeftPos() { + return scroller.getBoundingClientRect().left - sizer.getBoundingClientRect().left + lineGutter.offsetLeft - gutters.offsetWidth; + } + function alignLineNumbers() { + if (!options.lineNumbers) return; + var l = lineNumberLeftPos() + "px"; + for (var n = lineDiv.firstChild; n; n = n.nextSibling) + n.firstChild.style.left = l; + } + + function onScrollBarH() { + if (scrollbarH.scrollLeft != lastScrollLeft) { + scroller.scrollLeft = lastScrollLeft = scrollbarH.scrollLeft; + alignLineNumbers(); + } + } + function onScrollBarV() { + if (scrollbarV.scrollTop != lastScrollTop) { + scroller.scrollTop = lastScrollTop = scrollbarV.scrollTop; updateDisplay([]); } } - function onScrollMain(e) { + function onScrollMain() { if (scroller.scrollTop != lastScrollTop) { - lastScrollTop = scroller.scrollTop; - if (scrollbar.scrollTop != lastScrollTop) - scrollbar.scrollTop = lastScrollTop; + scrollbarV.scrollTop = lastScrollTop = scroller.scrollTop; updateDisplay([]); } + if (scroller.scrollLeft != lastScrollLeft) { + scrollbarH.scrollLeft = lastScrollLeft = scroller.scrollLeft; + alignLineNumbers(); + } if (options.onScroll) options.onScroll(instance); } @@ -790,7 +808,7 @@ window.CodeMirror = (function() { doc.insert(from.line + 1, added); } if (options.lineWrapping) { - var perLine = Math.max(5, scroller.clientWidth / charWidth() - 3); + var perLine = Math.max(5, wrapper.clientWidth / charWidth() - 3); doc.iter(from.line, from.line + newText.length, function(line) { if (line.hidden) return; var guess = (Math.ceil(line.text.length / perLine) || 1) * th; @@ -834,19 +852,16 @@ window.CodeMirror = (function() { updateLine(sel.from.line), updateLine(sel.to.line)); } - function needsScrollbar() { - var realHeight = doc.height + 2 * paddingTop(); - return realHeight - 1 > scroller.offsetHeight ? realHeight : false; - } - - function updateVerticalScroll(scrollTop) { - var scrollHeight = needsScrollbar(); - scrollbar.style.display = scrollHeight ? "block" : "none"; - if (scrollHeight) { - scrollbarInner.style.height = sizer.style.minHeight = scrollHeight + "px"; - scrollbar.style.height = scroller.clientHeight + "px"; + function updateScrollbars(scrollTop) { + sizer.style.minHeight = (doc.height + 2 * paddingTop()) + "px"; + var needsH = scroller.scrollWidth > scroller.clientWidth; + var needsV = scroller.scrollHeight > scroller.clientHeight; + if (needsV) { + scrollbarV.style.display = "block"; + scrollbarV.style.bottom = needsH ? scrollbarWidth(measure) + "px" : "0"; + scrollbarV.firstChild.style.height = (scroller.scrollHeight - scroller.clientHeight + scrollbarV.clientHeight) + "px"; if (scrollTop != null) { - scrollbar.scrollTop = scroller.scrollTop = scrollTop; + scrollbarV.scrollTop = scroller.scrollTop = scrollTop; // 'Nudge' the scrollbar to work around a Webkit bug where, // in some situations, we'd end up with a scrollbar that // reported its scrollTop (and looked) as expected, but @@ -854,17 +869,21 @@ window.CodeMirror = (function() { // couldn't scroll up, even though it appeared to be at the // bottom). if (webkit) setTimeout(function() { - if (scrollbar.scrollTop != scrollTop) return; - scrollbar.scrollTop = scrollTop + (scrollTop ? -1 : 1); - scrollbar.scrollTop = scrollTop; + if (scrollbarV.scrollTop != scrollTop) return; + scrollbarV.scrollTop = scrollTop + (scrollTop ? -1 : 1); + scrollbarV.scrollTop = scrollTop; }, 0); } - } else { - sizer.style.minHeight = ""; - sizer.style.minHeight = scroller.clientHeight + "px"; - } - // Position the mover div to align with the current virtual scroll position - mover.style.top = displayOffset + "px"; + } else scrollbarV.style.display = ""; + if (needsH) { + scrollbarH.style.display = "block"; + scrollbarH.style.right = needsV ? scrollbarWidth(measure) + "px" : "0"; + scrollbarH.firstChild.style.width = (scroller.scrollWidth - scroller.clientWidth + scrollbarH.clientWidth) + "px"; + } else scrollbarH.style.display = ""; + if (needsH && needsV) { + scrollbarFiller.style.display = "block"; + scrollbarFiller.style.height = scrollbarFiller.style.width = scrollbarWidth(measure) + "px"; + } else scrollbarFiller.style.display = ""; } function computeMaxLength() { @@ -1001,19 +1020,20 @@ window.CodeMirror = (function() { } function scrollIntoView(x1, y1, x2, y2) { var scrollPos = calculateScrollPos(x1, y1, x2, y2); - if (scrollPos.scrollLeft != null) {scroller.scrollLeft = scrollPos.scrollLeft;} - if (scrollPos.scrollTop != null) {scrollbar.scrollTop = scroller.scrollTop = scrollPos.scrollTop;} + if (scrollPos.scrollLeft != null) {scrollbarH.scrollLeft = scroller.scrollLeft = scrollPos.scrollLeft;} + if (scrollPos.scrollTop != null) {scrollbarV.scrollTop = scroller.scrollTop = scrollPos.scrollTop;} } function calculateScrollPos(x1, y1, x2, y2) { var pt = paddingTop(); y1 += pt; y2 += pt; - var screen = scroller.clientHeight, screentop = scrollbar.scrollTop, result = {}; - var docBottom = needsScrollbar() || Infinity; + var screen = scroller.clientHeight - scrollerCutOff, screentop = scroller.scrollTop, result = {}; + var docBottom = scroller.scrollHeight; var atTop = y1 < pt + 10, atBottom = y2 + pt > docBottom - 10; if (y1 < screentop) result.scrollTop = atTop ? 0 : Math.max(0, y1); else if (y2 > screentop + screen) result.scrollTop = (atBottom ? docBottom : y2) - screen; - var screenw = scroller.clientWidth, screenleft = scroller.scrollLeft; + var screenw = scroller.clientWidth - scrollerCutOff, screenleft = scroller.scrollLeft; + x1 += gutters.offsetWidth; x2 += gutters.offsetWidth; var gutterw = gutters.offsetWidth; var atLeft = x1 < gutterw + 10; if (x1 < screenleft + gutterw || atLeft) { @@ -1026,9 +1046,9 @@ window.CodeMirror = (function() { } function visibleLines(scrollTop) { - var top = (scrollTop != null ? scrollTop : scrollbar.scrollTop) - paddingTop(); + var top = (scrollTop != null ? scrollTop : scroller.scrollTop) - paddingTop(); var fromHeight = Math.max(0, Math.floor(top)); - var toHeight = Math.ceil(top + scroller.clientHeight); + var toHeight = Math.ceil(top + wrapper.clientHeight); return {from: lineAtHeight(doc, fromHeight), to: lineAtHeight(doc, toHeight)}; } @@ -1036,7 +1056,7 @@ window.CodeMirror = (function() { // determine which DOM updates have to be made, and makes the // updates. function updateDisplay(changes, suppressCallback, scrollTop) { - if (!scroller.clientWidth) { + if (!wrapper.clientWidth) { showingFrom = showingTo = displayOffset = 0; return; } @@ -1046,12 +1066,12 @@ window.CodeMirror = (function() { var visible = visibleLines(scrollTop); // Bail out if the visible area is already rendered and nothing changed. if (changes !== true && changes.length == 0 && visible.from > showingFrom && visible.to < showingTo) { - updateVerticalScroll(scrollTop); + updateScrollbars(scrollTop); return; } if (changes && changes !== true && maybeUpdateLineNumberWidth()) changes = true; - mover.style.marginLeft = gutters.offsetWidth + "px"; + mover.style.marginLeft = scrollbarH.style.left = gutters.offsetWidth + "px"; // Used to determine which lines need their line numbers updated var positionsChangedFrom = changes === true ? 0 : Infinity; if (options.lineNumbers && changes && changes !== true) @@ -1076,7 +1096,7 @@ window.CodeMirror = (function() { else intactLines += range.to - range.from; } if (intactLines == to - from && from == showingFrom && to == showingTo) { - updateVerticalScroll(scrollTop); + updateScrollbars(scrollTop); return; } intact.sort(function(a, b) {return a.domStart - b.domStart;}); @@ -1085,10 +1105,10 @@ window.CodeMirror = (function() { patchDisplay(from, to, intact, positionsChangedFrom); lineDiv.style.display = ""; - var different = from != showingFrom || to != showingTo || lastSizeC != scroller.clientHeight; + var different = from != showingFrom || to != showingTo || lastSizeC != wrapper.clientHeight; // This is just a bogus formula that detects when the editor is // resized or the font size changes. - if (different) lastSizeC = scroller.clientHeight; + if (different) lastSizeC = wrapper.clientHeight; if (from != showingFrom || to != showingTo && options.onViewportChange) setTimeout(function(){ if (options.onViewportChange) options.onViewportChange(instance, from, to); @@ -1124,7 +1144,9 @@ window.CodeMirror = (function() { curNode = curNode.nextSibling; }); - updateVerticalScroll(scrollTop); + // Position the mover div to align with the current virtual scroll position + mover.style.top = displayOffset + "px"; + updateScrollbars(scrollTop); updateSelection(); if (!suppressCallback && options.onUpdate) options.onUpdate(instance); return true; @@ -1164,6 +1186,7 @@ window.CodeMirror = (function() { return tmp; } var lineNumbers = options.lineNumbers; + var lineNumberPos = lineNumbers && lineNumberLeftPos(); // The first pass removes the DOM nodes that aren't intact. if (!intact.length) removeChildren(lineDiv); else { @@ -1196,7 +1219,7 @@ window.CodeMirror = (function() { if (lineNumbers) inside.push(elt("div", lineNumberFor(j), "CodeMirror-linenumber CodeMirror-gutter-elt", - "left: " + (lineGutter.offsetLeft - gutters.offsetWidth) + "px; width: " + "left: " + lineNumberPos + "px; width: " + currentLineNumberWidth + "px")); if (markers) for (var k = 0; k < gutterSpecs.length; ++k) { @@ -1227,8 +1250,8 @@ window.CodeMirror = (function() { if (!options.lineNumbers) return false; var last = lineNumberFor(doc.size - 1); if (last.length != currentLineNumberChars) { - var test = measure.appendChild(elt("div", last, "CodeMirror-linenumber CodeMirror-gutter-elt")); - currentLineNumberWidth = test.clientWidth; + var test = measure.appendChild(elt("div", [elt("div", last)], "CodeMirror-linenumber CodeMirror-gutter-elt")); + currentLineNumberWidth = test.firstChild.offsetWidth; currentLineNumberChars = currentLineNumberWidth ? last.length : -1; lineGutter.style.width = currentLineNumberWidth + "px"; return true; @@ -1337,8 +1360,8 @@ window.CodeMirror = (function() { // Move the hidden textarea near the cursor to prevent scrolling artifacts var wrapOff = eltOffset(wrapper), lineOff = eltOffset(lineDiv); - inputDiv.style.top = Math.max(0, Math.min(scroller.offsetHeight, headPos.top + lineOff.top - wrapOff.top)) + "px"; - inputDiv.style.left = Math.max(0, Math.min(scroller.offsetWidth, headPos.left + lineOff.left - wrapOff.left)) + "px"; + inputDiv.style.top = Math.max(0, Math.min(wrapper.clientHeight - 10, headPos.top + lineOff.top - wrapOff.top)) + "px"; + inputDiv.style.left = Math.max(0, Math.min(wrapper.clientWidth - 10, headPos.left + lineOff.left - wrapOff.left)) + "px"; } function setShift(val) { @@ -1470,17 +1493,17 @@ window.CodeMirror = (function() { userSelChange = true; } function moveV(dir, unit) { - var dist = 0, pos = cursorCoords(sel.inverted ? sel.from : sel.to, "div"); // FIXME cursorCoords + var dist = 0, pos = cursorCoords(sel.inverted ? sel.from : sel.to, "div"); var x = pos.left, y; if (goalColumn != null) x = goalColumn; if (unit == "page") { - var pageSize = Math.min(scroller.clientHeight, window.innerHeight || document.documentElement.clientHeight); + var pageSize = Math.min(wrapper.clientHeight, window.innerHeight || document.documentElement.clientHeight); y = pos.top + dir * pageSize; } else if (unit == "line") { y = dir > 0 ? pos.bottom + 3 : pos.top - 3; } var target = coordsChar(x, y); - if (unit == "page") scrollbar.scrollTop += charCoords(target, "div").top - pos.top; + if (unit == "page") scrollbarV.scrollTop += charCoords(target, "div").top - pos.top; setCursor(target.line, target.ch, true); goalColumn = x; } @@ -1548,13 +1571,13 @@ window.CodeMirror = (function() { function wrappingChanged(from, to) { if (options.lineWrapping) { wrapper.className += " CodeMirror-wrap"; - var perLine = scroller.clientWidth / charWidth() - 3; + var perLine = wrapper.clientWidth / charWidth() - 3; doc.iter(0, doc.size, function(line) { if (line.hidden) return; var guess = Math.ceil(line.text.length / perLine) || 1; if (guess != 1) updateLineHeight(line, guess); }); - lineSpace.style.minWidth = widthForcer.style.left = ""; + sizer.style.minWidth = ""; } else { wrapper.className = wrapper.className.replace(" CodeMirror-wrap", ""); computeMaxLength(); @@ -1572,7 +1595,7 @@ window.CodeMirror = (function() { return (tabCache[w] = {element: span, width: w}); } function themeChanged() { - scroller.className = scroller.className.replace(/\s*cm-s-\S+/g, "") + + wrapper.className = wrapper.className.replace(/\s*cm-s-\S+/g, "") + options.theme.replace(/(^|\s)\s*/g, " cm-s-"); } function keyMapChanged() { @@ -1776,7 +1799,7 @@ window.CodeMirror = (function() { var bottom = anchor.offsetTop + anchor.offsetHeight; if (atEnd) left = right; - var memo = {ch: ch, text: line.text, width: scroller.clientWidth, + var memo = {ch: ch, text: line.text, width: wrapper.clientWidth, top: top, bottom: bottom, left: left, right: right}; if (measureLineMemo.length == 8) measureLineMemo[++measureLineMemoPos % 8] = memo; else measureLineMemo.push(memo); @@ -1843,8 +1866,8 @@ window.CodeMirror = (function() { var sp = cursorCoords({line: lineNo, ch: ch}, "line", lineObj); if (tw) { wrongLine = true; - if (innerOff > sp.bottom) return Math.max(0, sp.left - scroller.clientWidth); - else if (innerOff < sp.top) return sp.left + scroller.clientWidth; + if (innerOff > sp.bottom) return Math.max(0, sp.left - wrapper.clientWidth); + else if (innerOff < sp.top) return sp.left + wrapper.clientWidth; else wrongLine = false; } return sp.left; @@ -1853,7 +1876,7 @@ window.CodeMirror = (function() { var from = lineLeft(lineObj), fromX = 0, to = lineRight(lineObj), toX; if (!bidi) { // Guess a suitable upper bound for our search. - var estimated = Math.min(to, Math.ceil((x + Math.floor(innerOff / textHeight()) * scroller.clientWidth * .9) / cw)); + var estimated = Math.min(to, Math.ceil((x + Math.floor(innerOff / textHeight()) * wrapper.clientWidth * .9) / cw)); for (;;) { var estX = getX(estimated); if (estX <= x && estimated < to) estimated = Math.min(to, Math.ceil(estimated * 1.2)); @@ -1902,8 +1925,8 @@ window.CodeMirror = (function() { } var cachedWidth, cachedWidthFor = 0; function charWidth() { - if (scroller.clientWidth == cachedWidthFor) return cachedWidth; - cachedWidthFor = scroller.clientWidth; + if (wrapper.clientWidth == cachedWidthFor) return cachedWidth; + cachedWidthFor = wrapper.clientWidth; var anchor = elt("span", "x"); var pre = elt("pre", [anchor]); removeChildrenAndAdd(measure, pre); @@ -1918,13 +1941,13 @@ window.CodeMirror = (function() { // This is a mess of a heuristic to try and determine whether a // scroll-bar was clicked or not, and to return null if one was // (and !liberal). - if (!liberal && (x - offW.left > scroller.clientWidth || y - offW.top > scroller.clientHeight)) + if (!liberal && (x - offW.left > wrapper.clientWidth || y - offW.top > wrapper.clientHeight)) return null; var offL = eltOffset(lineSpace, true); return coordsChar(x - offL.left, y - offL.top); } function onContextMenu(e) { - var pos = posFromMouse(e), scrollPos = scrollbar.scrollTop; + var pos = posFromMouse(e), scrollPos = scroller.scrollTop; if (!pos || opera) return; // Opera is difficult. if (posEq(sel.from, sel.to) || posLess(pos, sel.from) || !posLess(pos, sel.to)) operation(setCursor)(pos.line, pos.ch); @@ -1943,7 +1966,7 @@ window.CodeMirror = (function() { if (newVal != val && !options.readOnly) operation(replaceSelection)(newVal, "end"); inputDiv.style.position = "relative"; input.style.cssText = oldCSS; - if (ie_lt9) scrollbar.scrollTop = scrollPos; + if (ie_lt9) scrollbarV.scrollTop = scroller.scrollTop = scrollPos; leaveInputAlone = false; resetInput(true); slowPoll(); @@ -2107,11 +2130,9 @@ window.CodeMirror = (function() { function endOperation() { if (updateMaxLine) computeMaxLength(); if (maxLineChanged && !options.lineWrapping) { - var cursorWidth = widthForcer.offsetWidth, left = measureLine(maxLine, maxLine.text.length).left; - if (!ie_lt8) { - widthForcer.style.left = left + "px"; - lineSpace.style.minWidth = (left + cursorWidth) + "px"; - } + var width = measureLine(maxLine, maxLine.text.length).left; + if (!ie_lt8) + sizer.style.minWidth = (width + scrollerCutOff) + "px"; maxLineChanged = false; } var newScrollPos, updated; @@ -2447,6 +2468,9 @@ window.CodeMirror = (function() { var khtml = /KHTML\//.test(navigator.userAgent); var mac_geLion = /Mac OS X 10\D([7-9]|\d\d)\D/.test(navigator.userAgent); + // Number of pixels added to scroller and sizer to hide scrollbar + var scrollerCutOff = 30; + // Utility functions for working with state. Exported because modes // sometimes need to do this. function copyState(mode, state) { @@ -3282,6 +3306,15 @@ window.CodeMirror = (function() { } CodeMirror.setTextContent = setTextContent; + var knownScrollbarWidth; + function scrollbarWidth(measure) { + if (knownScrollbarWidth) return knownScrollbarWidth; + var test = elt("div", null, null, "width: 50px; height: 50px; overflow-x: scroll"); + removeChildrenAndAdd(measure, test); + knownScrollbarWidth = test.offsetHeight - test.clientHeight; + return knownScrollbarWidth; + } + // Used to position the cursor after an undo/redo by finding the // last edited character. function editEnd(from, to) { diff --git a/test/test.js b/test/test.js index 5f26fdd986..13f7e7aca0 100644 --- a/test/test.js +++ b/test/test.js @@ -500,13 +500,13 @@ testCM("scrollVerticallyAndHorizontally", function(cm) { cm.setSize(100, 100); addDoc(cm, 40, 40); cm.setCursor(39); - var wrap = cm.getWrapperElement(), bar = byClassName(wrap, "CodeMirror-scrollbar")[0]; + var wrap = cm.getWrapperElement(), bar = byClassName(wrap, "CodeMirror-vscrollbar")[0]; is(bar.offsetHeight < wrap.offsetHeight, "vertical scrollbar limited by horizontal one"); var cursorBox = byClassName(wrap, "CodeMirror-cursor")[0].getBoundingClientRect(); var editorBox = wrap.getBoundingClientRect(); is(cursorBox.bottom < editorBox.top + cm.getScrollerElement().clientHeight, "bottom line visible"); -}, {gutter: true}); +}, {lineNumbers: true}); testCM("moveV stuck", function(cm) { var lines = byClassName(cm.getWrapperElement(), "CodeMirror-lines")[0].firstChild, h0 = lines.offsetHeight; diff --git a/theme/ambiance.css b/theme/ambiance.css index ef5f8d09ba..5c5dcdc5ee 100644 --- a/theme/ambiance.css +++ b/theme/ambiance.css @@ -50,17 +50,16 @@ box-shadow: inset 0 0 10px black; } -.cm-s-ambiance .CodeMirror-gutter { +.cm-s-ambiance .CodeMirror-gutters { background: #3D3D3D; - padding: 0 5px; - text-shadow: #333 1px 1px; border-right: 1px solid #4D4D4D; box-shadow: 0 10px 20px black; } -.cm-s-ambiance .CodeMirror-gutter .CodeMirror-gutter-text { +.cm-s-ambiance .CodeMirror-linenumber { text-shadow: 0px 1px 1px #4d4d4d; color: #222; + padding: 0 5px; } .cm-s-ambiance .CodeMirror-lines { @@ -76,6 +75,6 @@ } .cm-s-ambiance, -.cm-s-ambiance .CodeMirror-gutter { +.cm-s-ambiance .CodeMirror-gutters { background-image: url("data:image/png;base64,iVBORw0KGgoAAAANSUhEUgAAAMgAAADICAQAAAAHUWYVAABFFUlEQVQYGbzBCeDVU/74/6fj9HIcx/FRHx9JCFmzMyGRURhLZIkUsoeRfUjS2FNDtr6WkMhO9sm+S8maJfu+Jcsg+/o/c+Z4z/t97/vezy3z+z8ekGlnYICG/o7gdk+wmSHZ1z4pJItqapjoKXWahm8NmV6eOTbWUOp6/6a/XIg6GQqmenJ2lDHyvCFZ2cBDbmtHA043VFhHwXxClWmeYAdLhV00Bd85go8VmaFCkbVkzlQENzfBDZ5gtN7HwF0KDrTwJ0dypSOzpaKCMwQHKTIreYIxlmhXTzTWkVm+LTynZhiSBT3RZQ7aGfjGEd3qyXQ1FDymqbKxpspERQN2MiRjNZlFFQXfCNFm9nM1zpAsoYjmtRTc5ajwuaXc5xrWskT97RaKzAGe5ARHhVUsDbjKklziiX5WROcJwSNCNI+9w1Jwv4Zb2r7lCMZ4oq5C0EdTx+2GzNuKpJ+iFf38JEWkHJn9DNF7mmBDITrWEg0VWL3pHU20tSZnuqWu+R3BtYa8XxV1HO7GyD32UkOpL/yDloINFTmvtId+nmAjxRw40VMwVKiwrKLE4bK5UOVntYwhOcSSXKrJHKPJedocpGjVz/ZMIbnYUPB10/eKCrs5apqpgVmWzBYWpmtKHecJPjaUuEgRDDaU0oZghCJ6zNMQ5ZhDYx05r5v2muQdM0EILtXUsaKiQX9WMEUotagQzFbUNN6NUPC2nm5pxEWGCjMc3GdJHjSU2kORLK/JGSrkfGEIjncU/CYUnOipoYemwj8tST9NsJmB7TUVXtbUtXATJVZXBMvYeTXJfobgJUPmGMP/yFaWonaa6BcFO3nqcIqCozSZoZoSr1g4zJOzuyGnxTEX3lUEJ7WcZgme8ddaWvWJo2AJR9DZU3CUIbhCSG6ybSwN6qtJVnCU2svDTP2ZInOw2cBTrqtQahtNZn9NcJ4l2NaSmSkkP1noZWnVwkLmdUPOwLZEwy2Z3S3R+4rIG9hcbpPXHFVWcQdZkn2FOta3cKWQnNRC5g1LsJah4GCzSVsKnCOY5OAFRTBekyyryeyilhFKva75r4Mc0aWanGEaThcy31s439KKxTzJYY5WTHPU1FtIHjQU3Oip4xlNzj/lBw23dYZVliQa7WAXf4shetcQfatI+jWRDBPmyNeW6A1P5kdDgyYJlba0BIM8BZu1JfrFwItyjcAMR3K0BWOIrtMEXyhyrlVEx3ui5dUBjmB/Q3CXW85R4mBD0s7B+4q5tKUjOlb9qqmhi5AZ6GFIC5HXtOobdYGlVdMVbNJ8toNTFcHxnoL+muBagcctjWnbNMuR00uI7nQESwg5q2qqrKWIfrNUmeQocY6HuyxJV02wj36w00yhpmUFenv4p6fUkZYqLyuinx2RGOjhCXYyJF84oiU00YMOOhhquNdfbOB7gU88pY4xJO8LVdp6/q2voeB4R04vIdhSE40xZObx1HGGJ/ja0LBthFInKaLPPFzuCaYaoj8JjPME8yoyxo6zlBqkiUZYgq00OYMswbWO5NGmq+xhipxHLRW29ARjNKXO0wRnear8XSg4XFPLKEPUS1GqvyLwiuBUoa7zpZ0l5xxFwWmWZC1H5h5FwU8eQ7K+g8UcVY6TMQreVQT/8uQ8Z+ALIXnSEa2pYZQneE9RZbSBNYXfWYJzW/h/4j4Dp1tYVcFIC5019Vyi4ThPqSFCzjGWaHQTBU8q6vrVwgxP9Lkm840imWKpcLCjYTtrKuwvsKSnrvHCXGkSMk9p6lhckfRpIeis+N2PiszT+mFLspyGleUhDwcLrZqmyeylxwjBcKHEapqkmyangyLZRVOijwOtCY5SsG5zL0OwlCJ4y5KznF3EUNDDrinwiyLZRzOXtlBbK5ITHFGLp8Q0R6ab6mS7enI2cFrxOyHvOCFaT1HThS1krjCwqWeurCkk+willhCC+RSZnRXBiZaC5RXRIZYKp2lyfrHwiKPKR0JDzrdU2EFgpidawlFDR6FgXUMNa+g1FY3bUQh2cLCwosRdnuQTS/S+JVrGLeWIvtQUvONJxlqSQYYKpwoN2kaocLjdVsis4Mk80ESF2YpSkzwldjHkjFCUutI/r+EHDU8oCs6yzL3PhWiEooZdFMkymlas4AcI3KmoMMNSQ3tHzjGWCrcJJdYyZC7QFGwjRL9p+MrRkAGWzIaWCn9W0F3TsK01c2ZvQw0byvxuQU0r1lM0qJO7wW0kRIMdDTtXEdzi4VIh+EoIHm0mWtAtpCixlabgn83fKTI7anJe9ST7WIK1DMGpQmYeA58ImV6ezOGOzK2Kgq01pd60cKWiUi9Lievb/0vIDPHQ05Kzt4ddPckQBQtoaurjyHnek/nKzpQLrVgKPjIkh2v4uyezpv+Xoo7fPFXaGFp1vaLKxQ4uUpQQS5VuQs7BCq4xRJv7fwpVvvFEB3j+620haOuocqMhWd6TTPAEx+mdFNGHdranFe95WrWmIvlY4F1Dle2ECgc6cto7SryuqGGGha0tFQ5V53migUKmg6XKAo4qS3mik+0OZpAhOLeZKicacgaYcyx5hypYQE02ZA4xi/pNhOQxR4klNKyqacj+mpxnLTnnGSo85++3ZCZq6lrZkXlGEX3o+C9FieccJbZWVFjC0Yo1FZnJhoYMFoI1hEZ9r6hwg75HwzBNhbZCdJEfJwTPGzJvaKImw1yYX1HDAmpXR+ZJQ/SmgqMNVQb5vgamGwLtt7VwvP7Qk1xpiM5x5Cyv93E06MZmgs0Nya2azIKOYKCGBQQW97RmhKNKF02JZqHEJ4o58qp7X5EcZmc56trXEqzjCBZ1MFGR87Ql2tSTs6CGxS05PTzRQorkbw7aKoKXFDXsYW42VJih/q+FP2BdTzDTwVqOYB13liM50vG7wy28qagyuIXMeQI/Oqq8bcn5wJI50xH00CRntyfpL1T4hydYpoXgNiFzoIUTDZnLNRzh4TBHwbYGDvZkxmlyJloyr6tRihpeUG94GnKtIznREF0tzJG/OOr73JBcrSh1k6WuTprgLU+mnSGnv6Zge0NNz+kTDdH8nuAuTdJDCNb21LCiIuqlYbqGzT3RAoZofQfjFazkqeNWdYaGvYTM001EW2oKPvVk1ldUGSgUtHFwjKM1h9jnFcmy5lChoLNaQMGGDsYbKixlaMBmmsx1QjCfflwTfO/gckW0ruZ3jugKR3R5W9hGUWqCgxuFgsuaCHorotGKzGaeZB9DMsaTnKCpMtwTvOzhYk0rdrArKCqcaWmVk1+F372ur1YkKxgatI8Qfe1gIX9wE9FgS8ESmuABIXnRUbCapcKe+nO7slClSZFzpV/LkLncEb1qiO42fS3R855Su2mCLh62t1SYZZYVmKwIHjREF2uihTzB20JOkz7dkxzYQnK0UOU494wh+VWRc6Un2kpTaVgLDFEkJ/uhzRcI0YKGgpGWOlocBU/a4fKoJ/pEaNV6jip3+Es9VXY078rGnmAdf7t9ylPXS34RBSuYPs1UecZTU78WanhBCHpZ5sAoTz0LGZKjPf9TRypqWEiTvOFglL1fCEY3wY/++rbk7C8bWebA6p6om6PgOL2kp44TFJlVNBXae2rqqdZztOJpT87GQsE9jqCPIe9VReZuQ/CIgacsyZdCpIScSYqcZk8r+nsyCzhyfhOqHGOIvrLknC8wTpFcaYiGC/RU1NRbUeUpocQOnkRpGOrIOcNRx+1uA0UrzhSSt+VyS3SJpnFWkzNDqOFGIWcfR86DnmARTQ1HKIL33ExPiemeOhYSSjzlSUZZuE4TveoJLnBUOFof6KiysCbnAEcZgcUNTDOwkqWu3RWtmGpZwlHhJENdZ3miGz0lJlsKnjbwqSHQjpxnFDlTLLwqJPMZMjd7KrzkSG7VsxXBZE+F8YZkb01Oe00yyRK9psh5SYh29ySPKBo2ylNht7ZkZnsKenjKNJu9PNEyZpaCHv4Kt6RQsLvAVp7M9kIimmCUwGeWqLMmGuIotYMmWNpSahkhZw9FqZsVnKJhsjAHvtHMsTM9fCI06Dx/u3vfUXCqfsKRc4oFY2jMsoo/7DJDwZ1CsIKnJu+J9ldkpmiCxQx1rWjI+T9FwcWWzOuaYH0Hj7klNRVWEQpmaqosakiGNTFHdjS/qnUdmf0NJW5xsL0HhimCCZZSRzmSPTXJQ4aaztAwtZnoabebJ+htCaZ7Cm535ByoqXKbX1WRc4Eh2MkRXWzImVc96Cj4VdOKVxR84VdQsIUM8Psoou2byVHyZFuq7O8otbSQ2UAoeEWTudATLGSpZzVLlXVkPU2Jc+27lsw2jmg5T5VhbeE3BT083K9WsTTkFU/Osi0rC5lRlpwRHUiesNS0sOvmqGML1aRbPAxTJD9ZKtxuob+hhl8cwYGWpJ8nub7t5p6coYbMovZ1BTdaKn1jYD6h4GFDNFyT/Kqe1XCXphXHOKLZmuRSRdBPEfVUXQzJm5YGPGGJdvAEr7hHNdGZnuBvrpciGmopOLf5N0uVMy0FfYToJk90uUCbJupaVpO53UJXR2bVpoU00V2KOo4zMFrBd0Jtz2pa0clT5Q5L8IpQ177mWQejPMEJhuQjS10ref6HHjdEhy1P1EYR7GtO0uSsKJQYLiTnG1rVScj5lyazpqWGl5uBbRWl7m6ixGOOnEsMJR7z8J0n6KMnCdxhiNYQCoZ6CmYLnO8omC3MkW3bktlPmEt/VQQHejL3+dOE5FlPdK/Mq8hZxxJtLyRrepLThYKbLZxkSb5W52vYxNOaOxUF0yxMUPwBTYqCzy01XayYK0sJyWBLqX0MwU5CzoymRzV0EjjeUeLgDpTo6ij42ZAzvD01dHUUTPLU96MdLbBME8nFBn7zJCMtJcZokn8YoqU0FS5WFKyniHobguMcmW8N0XkWZjkyN3hqOMtS08r+/xTBwpZSZ3qiVRX8SzMHHjfUNFjgHEPmY9PL3ykEzxkSre/1ZD6z/NuznuB0RcE1TWTm9zRgfUWVJiG6yrzgmWPXC8EAR4Wxhlad0ZbgQyEz3pG5RVEwwDJH2mgKpjcTiCOzn1lfUWANFbZ2BA8balnEweJC9J0iuaeZoI+ippFCztEKVvckR2iice1JvhVytrQwUAZpgsubCPaU7xUe9vWnaOpaSBEspalykhC9bUlOMpT42ZHca6hyrqKmw/wMR8H5ZmdFoBVJb03O4UL0tSNnvIeRmkrLWqrs78gcrEn2tpcboh0UPOW3UUR9PMk4T4nnNKWmCjlrefhCwxRNztfmIQVdDElvS4m1/WuOujoZCs5XVOjtKPGokJzsYCtFYoWonSPT21DheU/wWhM19FcElwqNGOsp9Q8N/cwXaiND1MmeL1Q5XROtYYgGeFq1aTMsoMmcrKjQrOFQTQ1fmBYhmW6o8Jkjc7iDJRTBIo5kgJD5yMEYA3srCg7VFKwiVJkmRCc5ohGOKhsYMn/XBLdo5taZjlb9YAlGWRimqbCsoY7HFAXLa5I1HPRxMMsQDHFkWtRNniqT9UEeNjcE7RUlrCJ4R2CSJuqlKHWvJXjAUNcITYkenuBRB84TbeepcqTj3zZyFJzgYQdHnqfgI0ddUwS6GqWpsKWhjq9cV0vBAEMN2znq+EBfIWT+pClYw5xsTlJU6GeIBsjGmmANTzJZiIYpgrM0Oa8ZMjd7NP87jxhqGOhJlnQtjuQpB+8aEE00wZFznSJPyHxgH3HkPOsJFvYk8zqCHzTs1BYOa4J3PFU+UVRZxlHDM4YavlNUuMoRveiZA2d7grMNc2g+RbSCEKzmgYsUmWmazFJyoiOZ4KnyhKOGRzWJa0+moyV4TVHDzn51Awtqaphfk/lRQ08FX1iiqxTB/kLwd0VynKfEvI6cd4XMV5bMhZ7gZUWVzYQ6Nm2BYzxJbw3bGthEUUMfgbGeorae6DxHtJoZ6alhZ0+ytiVoK1R4z5PTrOECT/SugseEOlb1MMNR4VRNcJy+V1Hg9ONClSZFZjdHlc6W6FBLdJja2MC5hhpu0DBYEY1TFGwiFAxRRCsYkiM9JRb0JNMVkW6CZYT/2EiTGWmo8k+h4FhDNE7BvppoTSFnmCV5xZKzvcCdDo7VVPnIU+I+Rc68juApC90MwcFCsJ5hDqxgScYKreruyQwTqrzoqDCmhWi4IbhB0Yrt3RGa6GfDv52rKXWhh28dyZaWUvcZeMTBaZoSGyiCtRU5J8iviioHaErs7Jkj61syVzTTgOcUOQ8buFBTYWdL5g3T4qlpe0+wvD63heAXRfCCIed9RbCsp2CiI7raUOYOTU13N8PNHvpaGvayo4a3LLT1lDrVEPT2zLUlheB1R+ZTRfKWJ+dcocLJfi11vyJ51lLqJ0WD7tRwryezjiV5W28uJO9qykzX8JDe2lHl/9oyBwa2UMfOngpXCixvKdXTk3wrsKmiVYdZIqsoWEERjbcUNDuiaQomGoIbFdEHmsyWnuR+IeriKDVLnlawlyNHKwKlSU631PKep8J4Q+ayjkSLKYLhalNHlYvttb6fHm0p6OApsZ4l2VfdqZkjuysy6ysKLlckf1KUutCTs39bmCgEyyoasIWlVaMF7mgmWtBT8Kol5xpH9IGllo8cJdopcvZ2sImlDmMIbtDk3KIpeNiS08lQw11NFPTwVFlPP6pJ2gvRfI7gQUfmNAtf6Gs0wQxDsKGlVBdF8rCa3jzdwMaGHOsItrZk7hAyOzpK9VS06j5F49b0VNGOOfKs3lDToMsMBe9ZWtHFEgxTJLs7qrygKZjUnmCYoeAqeU6jqWuLJup4WghOdvCYJnrSkSzoyRkm5M2StQwVltPkfCAk58tET/CSg+8MUecmotMEnhBKfWBIZsg2ihruMJQaoIm+tkTLKEqspMh00w95gvFCQRtDwTT1gVDDSEVdlwqZfxoQRbK0g+tbiBZxzKlpnpypejdDwTaeOvorMk/IJE10h9CqRe28hhLbe0pMsdSwv4ZbhKivo2BjDWfL8UKJgeavwlwb5KlwhyE4u4XkGE2ytZCznKLCDZZq42VzT8HLCrpruFbIfOIINmh/qCdZ1ZBc65kLHR1Bkyf5zn6pN3SvGKIlFNGplhrO9QSXanLOMQTLCa0YJCRrCZm/CZmrLTm7WzCK4GJDiWUdFeYx1LCFg3NMd0XmCuF3Y5rITLDUsYS9zoHVzwnJoYpSTQoObyEzr4cFBNqYTopoaU/wkyLZ2lPhX/5Y95ulxGTV7KjhWrOZgl8MyUUafjYraNjNU1N3IWcjT5WzWqjwtoarHSUObGYO3GCJZpsBlnJGPd6ZYLyl1GdCA2625IwwJDP8GUKymbzuyPlZlvTUsaUh5zFDhRWFzPKKZLAlWdcQbObgF9tOqOsmB1dqcqYJmWstFbZRRI9poolmqiLnU0POvxScpah2iSL5UJNzgScY5+AuIbpO0YD3NCW+dLMszFSdFCWGqG6eVq2uYVNDdICGD6W7EPRWZEY5gpsE9rUkS3mijzzJnm6UpUFXG1hCUeVoS5WfNcFpblELL2qqrCvMvRfd45oalvKU2tiQ6ePJOVMRXase9iTtLJztPxJKLWpo2CRDcJwn2sWSLKIO1WQWNTCvpVUvOZhgSC40JD0dOctaSqzkCRbXsKlb11Oip6PCJ0IwSJM31j3akRxlP7Rwn6aGaUL0qiLnJkvB3xWZ2+Q1TfCwpQH3G0o92UzmX4o/oJNQMMSQc547wVHhdk+VCw01DFYEnTxzZKAm74QmeNNR1w6WzEhNK15VJzuCdxQ53dRUDws5KvwgBMOEgpcVNe0hZI6RXT1Jd0cyj5nsaEAHgVmGaJIlWdsc5Ui2ElrRR6jrRAttNMEAIWrTDFubkZaok7/AkzfIwfuWVq0jHzuCK4QabtLUMVPB3kJ0oyHTSVFlqMALilJf2Rf8k5aaHtMfayocLBS8L89oKoxpJvnAkDPa0qp5DAUTHKWmCcnthlou8iCKaFFLHWcINd1nyIwXqrSxMNmSs6KmoL2QrKuWtlQ5V0120xQ5vRyZS1rgFkWwhiOwiuQbR0OOVhQM9iS3tiXp4RawRPMp5tDletOOBL95MpM01dZTBM9pkn5qF010rIeHFcFZhmSGpYpTsI6nwhqe5C9ynhlpp5ophuRb6WcJFldkVnVEwwxVfrVkvnWUuNLCg5bgboFHPDlDPDmnK7hUrWiIbjadDclujlZcaokOFup4Ri1kacV6jmrrK1hN9bGwpKEBQ4Q6DvIUXOmo6U5LqQM6EPyiKNjVkPnJkDPNEaxhiFay5ExW1NXVUGqcpYYdPcGiCq7z/TSlbhL4pplWXKd7NZO5QQFrefhRQW/NHOsqcIglc4UhWklR8K0QzbAw08CBDnpbgqXdeD/QUsM4RZXDFBW6WJKe/mFPdH0LtBgiq57wFLzlyQzz82qYx5D5WJP5yVJDW01BfyHnS6HKO/reZqId1WGa4Hkh2kWodJ8i6KoIPlAj2hPt76CzXsVR6koPRzWTfKqIentatYpQw2me4AA3y1Kind3SwoOKZDcFXTwl9tWU6mfgRk9d71sKtlNwrjnYw5tC5n5LdKiGry3JKNlHEd3oaMCFHrazBPMp/uNJ+V7IudcSbeOIdjUEdwl0VHCOZo5t6YluEuaC9mQeMgSfOyKnYGFHcIeQ84yQWbuJYJpZw5CzglDH7gKnWqqM9ZTaXcN0TeYhR84eQtJT76JJ1lREe7WnnvsMmRc9FQ7SBBM9mV3lCUdmHk/S2RAMt0QjFNFqQpWjDPQ01DXWUdDBkXziKPjGEP3VP+zIWU2t7im41FOloyWzn/L6dkUy3VLDaZ6appgDLHPjJEsyvJngWEPUyVBiAaHCTEXwrLvSEbV1e1gKJniicWorC1MUrVjB3uDhJE/wgSOzk1DXpk0k73qCM8xw2UvD5kJmDUfOomqMpWCkJRlvKXGmoeBm18USjVIk04SClxTB6YrgLAPLWYK9HLUt5cmc0vYES8GnTeRc6skZbQkWdxRsIcyBRzx1DbTk9FbU0caTPOgJHhJKnOGIVhQqvKmo0llRw9sabrZkDtdg3PqaKi9oatjY8B+G371paMg6+mZFNNtQ04mWBq3rYLOmtWWQp8KJnpy9DdFensyjdqZ+yY40VJlH8wcdLzC8PZnvHMFUTZUrDTkLyQaGus5X5LzpYAf3i+e/ZlhqGqWhh6Ou6xTR9Z6oi5AZZtp7Mj2EEm8oSpxiYZCHU/1fbGdNNNRRoZMhmilEb2gqHOEJDtXkHK/JnG6IrvbPCwV3NhONVdS1thBMs1T4QOBcTWa2IzhMk2nW5Kyn9tXUtpv9RsG2msxk+ZsQzRQacJncpgke0+T8y5Fzj8BiGo7XlJjaTIlpQs7KFjpqGnKuoyEPeIKnFMkZHvopgh81ySxNFWvJWcKRs70j2FOT012IllEEO1n4pD1513Yg2ssQPOThOkvyrqHUdEXOSEsihmBbTbKX1kLBPWqWkLOqJbjB3GBIZmoa8qWl4CG/iZ7oiA72ZL7TJNeZUY7kFQftDcHHluBzRbCegzMtrRjVQpX2lgoPKKLJAkcbMl01XK2p7yhL8pCBbQ3BN2avJgKvttcrWDK3CiUOVxQ8ZP+pqXKyIxnmBymCg5vJjNfkPK4+c8cIfK8ocVt7kmfd/I5SR1hKvCzUtb+lhgc00ZaO6CyhIQP1Uv4yIZjload72PXX0OIJvnFU+0Zf6MhsJwTfW0r0UwQfW4LNLZl5HK261JCZ4qnBaAreVAS3WrjV0LBnNDUNNDToCEeFfwgcb4gOEqLRhirWkexrCEYKVV711DLYEE1XBEsp5tpTGjorkomKYF9FDXv7fR3BGwbettSxnyL53MBPjsxDZjMh+VUW9NRxq1DhVk+FSxQcaGjV9Pawv6eGByw5qzoy7xk4RsOShqjJwWKe/1pEEfzkobeD/dQJmpqedcyBTy2sr4nGNRH0c0SPWTLrqAc0OQcb/gemKgqucQT7ySWKCn2EUotoCvpZct7RO2sy/QW0IWcXd7pQRQyZVwT2USRO87uhjioTLKV2brpMUcMQRbKH/N2T+UlTpaMls6cmc6CCNy3JdYYSUzzJQ4oSD3oKLncULOiJvjBEC2oqnCJkJluCYy2ZQ5so9YYlZ1VLlQU1mXEW1jZERwj/MUSRc24TdexlqLKfQBtDTScJUV8FszXBEY5ktpD5Ur9hYB4Nb1iikw3JoYpkKX+RodRKFt53MMuRnKSpY31PwYaGaILh3wxJGz9TkTPEETxoCWZrgvOlmyMzxFEwVJE5xZKzvyJ4WxEc16Gd4Xe3Weq4XH2jKRikqOkGQ87hQnC7wBmGYLAnesX3M+S87eFATauuN+Qcrh7xIxXJbUIdMw3JGE3ylCWzrieaqCn4zhGM19TQ3z1oH1AX+pWEqIc7wNGAkULBo/ZxRaV9NNyh4Br3rCHZzbzmSfawBL0dNRwpW1kK9mxPXR9povcdrGSZK9c2k0xwFGzjuniCtRSZCZ6ccZ7gaktmgAOtKbG/JnOkJrjcQTdFMsxRQ2cLY3WTIrlCw1eWKn8R6pvt4GFDso3QoL4a3nLk3G6JrtME3dSenpx7PNFTmga0EaJTLQ061sEeQoWXhSo9LTXsaSjoJQRXeZLtDclbCrYzfzHHeaKjHCVOUkQHO3JeEepr56mhiyaYYKjjNU+Fed1wS5VlhWSqI/hYUdDOkaxiKehoyOnrCV5yBHtbWFqTHCCwtpDcYolesVR5yUzTZBb3RNMd0d6WP+SvhuBmRcGxnuQzT95IC285cr41cLGQ6aJJhmi4TMGempxeimBRQw1tFKV+8jd6KuzoSTqqDxzRtpZkurvKEHxlqXKRIjjfUNNXQsNOsRScoWFLT+YeRZVD3GRN0MdQcKqQjHDMrdGGVu3iYJpQx3WGUvfbmxwFfR20WBq0oYY7LMFhhgYtr8jpaEnaOzjawWWaTP8mMr0t/EPDPoqcnxTBI5o58L7uoWnMrpoqPwgVrlAUWE+V+TQl9rawoyP6QGAlQw2TPRX+YSkxyBC8Z6jhHkXBgQL7WII3DVFnRfCrBfxewv9D6xsyjys4VkhWb9pUU627JllV0YDNHMku/ldNMMXDEo4aFnAkk4U6frNEU4XgZUPmEKHUl44KrzmYamjAbh0JFvGnaTLPu1s9jPCwjFpYiN7z1DTOk/nc07CfDFzmCf7i+bfNHXhDtLeBXzTBT5rkMvWOIxpl4EMh2LGJBu2syDnAEx2naEhHDWMMzPZEhygyS1mS5RTJr5ZkoKbEUoYqr2kqdDUE8ztK7OaIntJkFrIECwv8LJTaVx5XJE86go8dFeZ3FN3rjabCAYpoYEeC9zzJVULBbmZhDyd7ko09ydpNZ3nm2Kee4FPPXHnYEF1nqOFEC08LUVcDvYXkJHW8gTaKCk9YGOeIJhqiE4ToPEepdp7IWFjdwnWaufGMwJJCMtUTTBBK9BGCOy2tGGrJTHIwyEOzp6aPzNMOtlZkDvcEWpP5SVNhfkvDxhmSazTJXYrM9U1E0xwFVwqZQwzJxw6+kGGGUj2FglGGmnb1/G51udRSMNlTw6GGnCcUwVcOpmsqTHa06o72sw1RL02p9z0VbnMLOaIX3QKaYKSCFQzBKEUNHTSc48k53RH9wxGMtpQa5KjjW0W0n6XCCCG4yxNNdhQ4R4l1Ff+2sSd6UFHiIEOyqqFgT01mEUMD+joy75jPhOA+oVVLm309FR4yVOlp4RhLiScNmSmaYF5Pw0STrOIoWMSR2UkRXOMp+M4SHW8o8Zoi6OZgjKOaFar8zZDzkWzvKOjkKBjmCXby8JahhjXULY4KlzgKLvAwxVGhvyd4zxB1d9T0piazmKLCVZY5sKiD0y2ZSYrkUEPUbIk+dlQ4SJHTR50k1DPaUWIdTZW9NJwnJMOECgd7ou/MnppMJ02O1VT4Wsh85MnZzcFTngpXGKo84qmwgKbCL/orR/SzJ2crA+t6Mp94KvxJUeIbT3CQu1uIdlQEOzlKfS3UMcrTiFmOuroocrZrT2AcmamOKg8YomeEKm/rlT2sociMaybaUlFhuqHCM2qIJ+rg4EcDFymiDSxzaHdPcpE62pD5kyM5SBMoA1PaUtfIthS85ig1VPiPPYXgYEMNk4Qq7TXBgo7oT57gPUdwgCHzhIVFPFU6OYJzHAX9m5oNrVjeE61miDrqQ4VSa1oiURTsKHC0IfjNwU2WzK6eqK8jWln4g15TVBnqmDteCJ501PGAocJhhqjZdtBEB6lnhLreFJKxmlKbeGrqLiSThVIbCdGzloasa6lpMQXHCME2boLpJgT7yWaemu6wBONbqGNVRS0PKIL7LckbjmQtR7K8I5qtqel+T/ChJTNIKLjdUMNIRyvOEko9YYl2cwQveBikCNawJKcLBbc7+JM92mysNvd/Fqp8a0k6CNEe7cnZrxlW0wQXaXjaktnRwNOGZKYiONwS7a1JVheq3WgJHlQUGKHKmp4KAxXR/ULURcNgoa4zhKSLpZR3kxRRb0NmD0OFn+UCS7CzI1nbP6+o4x47QZE5xRCt3ZagnYcvmpYQktXdk5YKXTzBC57kKEe0VVuiSYqapssMS3C9p2CKkHOg8B8Pa8p5atrIw3qezIWanMGa5HRDNF6RM9wcacl0N+Q8Z8hsIkSnaIIdHRUOEebAPy1zbCkhM062FCJtif7PU+UtoVXzWKqM1PxXO8cfdruhFQ/a6x3JKYagvVDhQEtNiyiiSQ7OsuRsZUku0CRNDs4Sog6KKjsZgk2bYJqijgsEenoKeniinRXBn/U3lgpPdyDZynQx8IiioMnCep5Ky8mjGs6Wty0l1hUQTcNWswS3WRp2kCNZwJG8omG8JphPUaFbC8lEfabwP7VtM9yoaNCAjpR41VNhrD9LkbN722v0CoZMByFzhaW+MyzRYEWFDQwN2M4/JiT76PuljT3VU/A36eaIThb+R9oZGOAJ9tewkgGvqOMNRWYjT/Cwu99Q8LqDE4TgbLWxJ1jaDDAERsFOFrobgjUsBScaguXU8kKm2RL19tRypSHnHNlHiIZqgufs4opgQdVdwxBNNFBR6kVFqb8ogimOzB6a6HTzrlDHEpYaxjiiA4TMQobkDg2vejjfwJGWmnbVFAw3H3hq2NyQfG7hz4aC+w3BbwbesG0swYayvpAs6++Ri1Vfzx93mFChvyN5xVHTS+0p9aqCAxyZ6ZacZyw5+7uuQkFPR9DDk9NOiE7X1PCYJVjVUqq7JlrHwWALF5nfHNGjApdpqgzx5OwilDhCiDYTgnc9waGW4BdLNNUQvOtpzDOWHDH8D7TR/A/85KljEQu3NREc4Pl/6B1Hhc8Umb5CsKMmGC9EPcxoT2amwHNCmeOEnOPbklnMkbOgIvO5UMOpQrS9UGVdt6iH/fURjhI/WOpaW9OKLYRod6HCUEdOX000wpDZQ6hwg6LgZfOqo1RfT/CrJzjekXOGhpc1VW71ZLbXyyp+93ILbC1kPtIEYx0FIx1VDrLoVzXRKRYWk809yYlC9ImcrinxtabKnzRJk3lAU1OLEN1j2zrYzr2myHRXJFf4h4QKT1qSTzTB5+ZNTzTRkAxX8FcLV2uS8eoQQ2aAkFzvCM72sJIcJET3WPjRk5wi32uSS9rfZajpWEvj9hW42F4o5NytSXYy8IKHay10VYdrcl4SkqscrXpMwyGOgtkajheSxdQqmpxP1L3t4R5PqasFnrQEjytq6qgp9Y09Qx9o4S1FzhUCn1kyHSzBWLemoSGvOqLNhZyBjmCaAUYpMgt4Ck7wBBMMwWKWgjsUwTaGVsxWC1mYoKiyqqeGKYqonSIRQ3KIkHO0pmAxTdBHkbOvfllfr+AA+7gnc50huVKYK393FOyg7rbPO/izI7hE4CnHHHnJ0ogNPRUGeUpsrZZTBJcrovUcJe51BPsr6GkJdhCCsZ6aTtMEb2pqWkqeVtDXE/QVggsU/Nl86d9RMF3DxvZTA58agu810RWawCiSzzXBeU3MMW9oyJUedvNEvQyNu1f10BSMddR1vaLCYpYa/mGocLSiYDcLbQz8aMn5iyF4xBNMs1P0QEOV7o5gaWGuzSeLue4tt3ro7y4Tgm4G/mopdZgl6q0o6KzJWE3mMksNr3r+a6CbT8g5wZNzT9O7fi/zpaOmnz3BRoqos+tv9zMbdpxsqDBOEewtJLt7cg5wtKKbvldpSzRRCD43VFheCI7yZLppggMVBS/KMAdHODJvOwq2NQSbKKKPLdFWQs7Fqo+mpl01JXYRgq8dnGLhTiFzqmWsUMdpllZdbKlyvSdYxhI9YghOtxR8LgSLWHK62mGGVoxzBE8LNWzqH9CUesQzFy5RQzTc56mhi6fgXEWwpKfE5Z7M05ZgZUPmo6auiv8YKzDYwWBLMErIbKHJvOwIrvEdhOBcQ9JdU1NHQ7CXn2XIDFBKU2WAgcX9UAUzDXWd5alwuyJ41Z9rjKLCL4aCp4WarhPm2rH+SaHUYE001JDZ2ZAzXPjdMpZWvC9wmqIB2lLhQ01D5jO06hghWMndbM7yRJMsoCj1vYbnFQVrW9jak3OlEJ3s/96+p33dEPRV5GxiqaGjIthUU6FFEZyqCa5qJrpBdzSw95IUnOPIrCUUjRZQFrbw5PR0R1qiYx3cb6nrWUMrBmmiBQxVHtTew5ICP/ip6g4hed/Akob/32wvBHsIOX83cI8hGeNeNPCIkPmXe8fPKx84OMSRM1MTdXSwjCZ4S30jVGhvqTRak/OVhgGazHuOCud5onEO1lJr6ecVyaOK6H7zqlBlIaHE0oroCgfvGJIdPcmfLNGLjpz7hZwZQpUbFME0A1cIJa7VNORkgfsMBatbKgwwJM9bSvQXeNOvbIjelg6WWvo5kvbKaJJNHexkKNHL9xRyFlH8Ti2riB5wVPhUk7nGkJnoCe428LR/wRGdYIlmWebCyxou1rCk4g/ShugBDX0V0ZQWkh0dOVsagkM0yV6OoLd5ye+pRlsCr0n+KiQrGuq5yJDzrTAXHtLUMduTDBVKrSm3eHL+6ijxhFDX9Z5gVU/wliHYTMiMFpKLNMEywu80wd3meoFmt6VbRMPenhrOc6DVe4pgXU8DnnHakLOIIrlF4FZPIw6R+zxBP0dyq6OOZ4Q5sLKCcz084ok+VsMMyQhNZmmBgX5xIXOEJTmi7VsGTvMTNdHHhpzdbE8Du2oKxgvBqQKdDDnTFOylCFaxR1syz2iqrOI/FEpNc3C6f11/7+ASS6l2inq2ciTrCCzgyemrCL5SVPjQkdPZUmGy2c9Sw9FtR1sS30RmsKPCS4rkIC/2U0MduwucYolGaPjKEyhzmiPYXagyWbYz8LWBDdzRimAXzxx4z8K9hpzlhLq+NiQ97HuKorMUfK/OVvC2JfiHUPCQI/q7J2gjK+tTDNxkCc4TMssqCs4TGtLVwQihyoAWgj9bosU80XGW6Ac9TJGziaUh5+hnFcHOnlaM1iRn29NaqGENTTTSUHCH2tWTeV0osUhH6psuVLjRUmGWhm6OZEshGeNowABHcJ2Bpy2ZszRcKkRXd2QuKVEeXnbfaEq825FguqfgfE2whlChSRMdron+LATTPQ2Z369t4B9C5gs/ylzv+CMmepIDPclFQl13W0rspPd1JOcbghGOEutqCv5qacURQl3dDKyvyJlqKXGPgcM9FfawJAMVmdcspcYKOZc4GjDYkFlK05olNMHyHn4zFNykyOxt99RkHlfwmiHo60l2EKI+mhreEKp080Tbug08BVPcgoqC5zWt+NLDTZ7oNSF51N1qie7Va3uCCwyZbkINf/NED6jzOsBdZjFN8oqG3wxVunqCSYYKf3EdhJyf9YWGf7tRU2oH3VHgPr1fe5J9hOgHd7xQ0y7qBwXr23aGErP0cm64JVjZwsOGqL+mhNgZmhJLW2oY4UhedsyBgzrCKrq7BmcpNVhR6jBPq64Vgi+kn6XE68pp8J5/+0wRHGOpsKenQn9DZntPzjRLZpDAdD2fnSgkG9tmIXnUwQ6WVighs7Yi2MxQ0N3CqYaCXkJ0oyOztMDJjmSSpcpvlrk0RMMOjmArQ04PRV1DO1FwhCVaUVPpKUM03JK5SxPsIWRu8/CGHi8UHChiqGFDTbSRJWeYUDDcH6vJWUxR4k1FXbMUwV6e4AJFXS8oMqsZKqzvYQ9DDQdZckY4aGsIhtlubbd2r3j4QBMoTamdPZk7O/Bf62lacZwneNjQoGcdVU7zJOd7ghsUHOkosagic6cnWc8+4gg285R6zZP5s1/LUbCKIznTwK36PkdwlOrl4U1LwfdCCa+IrvFkmgw1PCAUXKWo0sURXWcI2muKJlgyFzhynCY4RBOsqCjoI1R5zREco0n2Vt09BQtYSizgKNHfUmUrQ5UOCh51BFcLmY7umhYqXKQomOop8bUnWNNQcIiBcYaC6xzMNOS8JQQfeqKBmmglB+97ok/lfk3ygaHSyZaCRTzRxQo6GzLfa2jWBPepw+UmT7SQEJyiyRkhBLMVOfcoMjcK0eZChfUNzFAUzCsEN5vP/X1uP/n/aoMX+K+nw/Hjr/9xOo7j7Pju61tLcgvJpTWXNbfN5jLpi6VfCOviTktKlFusQixdEKWmEBUKNaIpjZRSSOXSgzaaKLdabrm1/9nZ+/f+vd/vz/v9+Xy+zZ7PRorYoZqyLrCwQdEAixxVOEXNNnjX2nUSRlkqGmWowk8lxR50JPy9Bo6qJXaXwNvREBvnThPEPrewryLhcAnj5WE15Fqi8W7R1sAuEu86S4ENikItFN4xkv9Af4nXSnUVcLiA9xzesFpivRRVeFKtsMRaKBhuSbjOELnAUtlSQUpXgdfB4Z1oSbnFEetbQ0IrAe+Y+pqnDcEJFj6S8LDZzZHwY4e3XONNlARraomNEt2bkvGsosA3ioyHm+6jCMbI59wqt4eeara28IzEmyPgoRaUOEDhTVdEJhmCoTWfC0p8aNkCp0oYqih2iqGi4yXeMkOsn4LdLLnmKfh/YogjNsPebeFGR4m9BJHLzB61XQ3BtpISfS2FugsK9FAtLWX1dCRcrCnUp44CNzuCowUZmxSRgYaE6Za0W2u/E7CVXCiI/UOR8aAm1+OSyE3mOUcwyc1zBBeoX1kiKy0Zfxck1Gsyulti11i83QTBF5Kg3pDQThFMVHiPSlK+0cSedng/VaS8bOZbtsBcTcZAR8JP5KeqQ1OYKAi20njdNNRpgnsU//K+JnaXJaGTomr7aYIphoRn9aeShJWKEq9LcozSF7QleEfDI5LYm5bgVkFkRwVDBCVu0DDIkGupo8TZBq+/pMQURYErJQmPKGKjNDkWOLx7Jd5QizdUweIaKrlP7SwJDhZvONjLkOsBBX9UpGxnydhXkfBLQ8IxgojQbLFnJf81JytSljclYYyEFyx0kVBvKWOFJmONpshGAcsduQY5giVNCV51eOdJYo/pLhbvM0uDHSevNKRcrKZIqnCtJeEsO95RoqcgGK4ocZcho1tTYtcZvH41pNQ7vA0WrhIfOSraIIntIAi+NXWCErdbkvrWwjRLrt0NKUdL6KSOscTOdMSOUtBHwL6OLA0vNSdynaWQEnCpIvKaIrJJEbvHkmuNhn6OjM8VkSGSqn1uYJCGHnq9I3aLhNME3t6GjIkO7xrNFumpyTNX/NrwX7CrIRiqqWijI9JO4d1iieykyfiposQIQ8YjjsjlBh6oHWbwRjgYJQn2NgSnNycmJAk3NiXhx44Sxykihxm8ybUwT1OVKySc7vi3OXVkdBJ4AyXBeksDXG0IhgtYY0lY5ahCD0ehborIk5aUWRJviMA7Xt5kyRjonrXENkm8yYqgs8VzgrJmClK20uMM3jRJ0FiQICQF9hdETlLQWRIb5ki6WDfWRPobvO6a4GP5mcOrNzDFELtTkONLh9dXE8xypEg7z8A9jkhrQ6Fhjlg/QVktJXxt4WXzT/03Q8IaQWSqIuEvloQ2mqC9Jfi7wRul4RX3pSPlzpoVlmCtI2jvKHCFhjcM3sN6lqF6HxnKelLjXWbwrpR4xzuCrTUZx2qq9oAh8p6ixCUGr78g8oyjRAtB5CZFwi80VerVpI0h+IeBxa6Zg6kWvpDHaioYYuEsRbDC3eOmC2JvGYLeioxGknL2UATNJN6hmtj1DlpLvDVmocYbrGCVJKOrg4X6DgddLA203BKMFngdJJFtFd7vJLm6KEpc5yjQrkk7M80SGe34X24nSex1Ra5Omgb71JKyg8SrU3i/kARKwWpH0kOGhKkObyfd0ZGjvyXlAkVZ4xRbYJ2irFMkFY1SwyWxr2oo4zlNiV+7zmaweFpT4kR3kaDAFW6xpSqzJay05FtYR4HmZhc9UxKbbfF2V8RG1MBmSaE+kmC6JnaRXK9gsiXhJHl/U0qM0WTcbyhwkYIvFGwjSbjfwhiJt8ZSQU+Bd5+marPMOkVkD0muxYLIfEuhh60x/J92itguihJSEMySVPQnTewnEm+620rTQEMsOfo4/kP/0ARvWjitlpSX7GxBgcMEsd3EEeYWvdytd+Saawi6aCIj1CkGb6Aj9rwhx16Cf3vAwFy5pyLhVonXzy51FDpdEblbkdJbUcEPDEFzQ8qNmhzzLTmmKWKbFCXeEuRabp6rxbvAtLF442QjQ+wEA9eL1xSR7Q0JXzlSHjJ4exq89yR0laScJ/FW6z4a73pFMEfDiRZvuvijIt86RaSFOl01riV2mD1UEvxGk/Geg5aWwGki1zgKPG9J2U8PEg8qYvMsZeytiTRXBMslCU8JSlxi8EabjwUldlDNLfzTUmCgxWsjqWCOHavYAqsknKFIO0yQ61VL5AVFxk6WhEaCAkdJgt9aSkzXlKNX2jEa79waYuc7gq0N3GDJGCBhoiTXUEPsdknCUE1CK0fwsiaylSF2uiDyO4XX3pFhNd7R4itFGc0k/ElBZwWvq+GC6szVeEoS/MZ+qylwpKNKv9Z469UOjqCjwlusicyTxG6VpNxcQ8IncoR4RhLbR+NdpGGmJWOcIzJGUuKPGpQg8rrG21dOMqQssJQ4RxH5jaUqnZuQ0F4Q+cjxLwPtpZbIAk3QTJHQWBE5S1BokoVtDd6lhqr9UpHSUxMcIYl9pojsb8h4SBOsMQcqvOWC2E8EVehqiJ1hrrAEbQxeK0NGZ0Gkq+guSRgniM23bIHVkqwx4hiHd7smaOyglyIyQuM978j4VS08J/A2G1KeMBRo4fBaSNhKUEZfQewVQ/C1I+MgfbEleEzCUw7mKXI0M3hd1EESVji8x5uQ41nxs1q4RMJCCXs7Iq9acpxn22oSDnQ/sJTxsCbHIYZiLyhY05TY0ZLIOQrGaSJDDN4t8pVaIrsqqFdEegtizc1iTew5Q4ayBDMUsQMkXocaYkc0hZua412siZ1rSXlR460zRJ5SlHGe5j801RLMlJTxtaOM3Q1pvxJ45zUlWFD7rsAbpfEm1JHxG0eh8w2R7QQVzBUw28FhFp5QZzq8t2rx2joqulYTWSuJdTYfWwqMFMcovFmSyJPNyLhE4E10pHzYjOC3huArRa571ZsGajQpQx38SBP5pyZB6lMU3khDnp0MBV51BE9o2E+TY5Ml2E8S7C0o6w1xvCZjf0HkVEHCzFoyNmqC+9wdcqN+Tp7jSDheE9ws8Y5V0NJCn2bk2tqSY4okdrEhx1iDN8cSudwepWmAGXKcJXK65H9to8jYQRH7SBF01ESUJdd0TayVInaWhLkOjlXE5irKGOnI6GSWGCJa482zBI9rCr0jyTVcEuzriC1vcr6mwFGSiqy5zMwxBH/TJHwjSPhL8+01kaaSUuMFKTcLEvaUePcrSmwn8DZrgikWb7CGPxkSjhQwrRk57tctmxLsb9sZvL9LSlyuSLlWkqOjwduo8b6Uv1DkmudIeFF2dHCgxVtk8dpIvHpBxhEOdhKk7OLIUSdJ+cSRY57B+0DgGUUlNfpthTfGkauzxrvTsUUaCVhlKeteTXCoJDCa2NOKhOmC4G1H8JBd4OBZReSRGkqcb/CO1PyLJTLB4j1q8JYaIutEjSLX8YKM+a6phdMsdLFUoV5RTm9JSkuDN8WcIon0NZMNZWh1q8C7SJEwV5HxrmnnTrf3KoJBlmCYI2ilSLlfEvlE4011NNgjgthzEua0oKK7JLE7HZHlEl60BLMVFewg4EWNt0ThrVNEVkkiTwpKXSWJzdRENgvKGq4IhjsiezgSFtsfCUq8qki5S1LRQeYQQ4nemmCkImWMw3tFUoUBZk4NOeZYEp4XRKTGa6wJjrWNHBVJR4m3FCnbuD6aak2WsMTh3SZImGCIPKNgsDpVwnsa70K31lCFJZYcwwSMFcQulGTsZuEaSdBXkPGZhu0FsdUO73RHjq8MPGGIfaGIbVTk6iuI3GFgucHrIQkmWSJdBd7BBu+uOryWAhY7+Lki9rK5wtEQzWwvtbqGhIMFwWRJsElsY4m9IIg9L6lCX0VklaPAYkfkZEGDnOWowlBJjtMUkcGK4Lg6EtoZInMUBVYLgn0UsdmCyCz7gIGHFfk+k1QwTh5We7A9x+IdJ6CvIkEagms0hR50eH9UnTQJ+2oiKyVlLFUE+8gBGu8MQ3CppUHesnjTHN4QB/UGPhCTHLFPHMFrCqa73gqObUJGa03wgbhHkrCfpEpzNLE7JDS25FMKhlhKKWKfCgqstLCPu1zBXy0J2ztwjtixBu8UTRn9LVtkmCN2iyFhtME70JHRQ1KVZXqKI/KNIKYMCYs1GUMEKbM1bKOI9LDXC7zbHS+bt+1MTWS9odA9DtrYtpbImQJ2VHh/lisEwaHqUk1kjKTAKknkBEXkbkdMGwq0dnhzLJF3NJH3JVwrqOB4Sca2hti75nmJN0WzxS6UxDYoEpxpa4htVlRjkYE7DZGzJVU72uC9IyhQL4i8YfGWSYLLNcHXloyz7QhNifmKSE9JgfGmuyLhc403Xm9vqcp6gXe3xuuv8F6VJNxkyTHEkHG2g0aKXL0MsXc1bGfgas2//dCONXiNLCX+5mB7eZIl1kHh7ajwpikyzlUUWOVOsjSQlsS+M0R+pPje/dzBXRZGO0rMtgQrLLG9VSu9n6CMXS3BhwYmSoIBhsjNBmZbgusE9BCPCP5triU4VhNbJfE+swSP27aayE8tuTpYYjtrYjMVGZdp2NpS1s6aBnKSHDsbKuplKbHM4a0wMFd/5/DmGyKrJSUaW4IBrqUhx0vyfzTBBLPIUcnZdrAkNsKR0sWRspumSns6Ch0v/qqIbBYUWKvPU/CFoyrDJGwSNFhbA/MlzKqjrO80hRbpKx0Jewsi/STftwGSlKc1JZyAzx05dhLEdnfQvhZOqiHWWEAHC7+30FuRcZUgaO5gpaIK+xsiHRUsqaPElTV40xQZQ107Q9BZE1nryDVGU9ZSQ47bmhBpLcYpUt7S+xuK/FiT8qKjwXYw5ypS2iuCv7q1gtgjhuBuB8LCFY5cUuCNtsQOFcT+4Ih9JX+k8Ea6v0iCIRZOtCT0Et00JW5UeC85Cg0ScK0k411HcG1zKtre3SeITBRk7WfwDhEvaYLTHP9le0m8By0JDwn4TlLW/aJOvGHxdjYUes+ScZigCkYQdNdEOhkiezgShqkx8ueKjI8lDfK2oNiOFvrZH1hS+tk7NV7nOmLHicGWEgubkXKdwdtZknCLJXaCpkrjZBtLZFsDP9CdxWsSr05Sxl6CMmoFbCOgryX40uDtamB7SVmXW4Ihlgpmq+00tBKUUa83WbjLUNkzDmY7cow1JDygyPGlhgGKYKz4vcV7QBNbJIgM11TUqZaMdwTeSguH6rOaw1JRKzaaGyxVm2EJ/uCIrVWUcZUkcp2grMsEjK+DMwS59jQk3Kd6SEq1d0S6uVmO4Bc1lDXTUcHjluCXEq+1OlBDj1pi9zgiXxnKuE0SqTXwhqbETW6RggMEnGl/q49UT2iCzgJvRwVXS2K/d6+ZkyUl7jawSVLit46EwxVljDZwoSQ20sDBihztHfk2yA8NVZghiXwrYHQdfKAOtzsayjhY9bY0yE2CWEeJ9xfzO423xhL5syS2TFJofO2pboHob0nY4GiAgRrvGQEDa/FWSsoaaYl0syRsEt3kWoH3B01shCXhTUWe9w3Bt44SC9QCh3eShQctwbaK2ApLroGCMlZrYqvlY3qYhM0aXpFkPOuoqJ3Dm6fxXrGwVF9gCWZagjPqznfkuMKQ8DPTQRO8ZqG1hPGKEm9IgpGW4DZDgTNriTxvFiq+Lz+0cKfp4wj6OCK9JSnzNSn9LFU7UhKZZMnYwcJ8s8yRsECScK4j5UOB95HFO0CzhY4xJxuCix0lDlEUeMdS6EZBkTsUkZ4K74dugyTXS7aNgL8aqjDfkCE0ZbwkCXpaWCKhl8P7VD5jxykivSyxyZrYERbe168LYu9ZYh86IkscgVLE7tWPKmJv11CgoyJltMEbrohtVAQfO4ImltiHEroYEs7RxAarVpY8AwXMcMReFOTYWe5iiLRQxJ5Q8DtJ8LQhWOhIeFESPGsILhbNDRljNbHzNRlTFbk2S3L0NOS6V1KFJYKUbSTcIIhM0wQ/s2TM0SRMNcQmSap3jCH4yhJZKSkwyRHpYYgsFeQ4U7xoCB7VVOExhXepo9ABBsYbvGWKXPME3lyH95YioZ0gssQRWWbI+FaSMkXijZXwgiTlYdPdkNLaETxlyDVIwqeaEus0aTcYcg0RVOkpR3CSJqIddK+90JCxzsDVloyrFd5ZAr4TBKfaWa6boEA7C7s6EpYaeFPjveooY72mjIccLHJ9HUwVlDhKkmutJDJBwnp1rvulJZggKDRfbXAkvC/4l3ozQOG9a8lxjx0i7nV4jSXc7vhe3OwIxjgSHjdEhhsif9YkPGlus3iLFDnWOFhtCZbJg0UbQcIaR67JjthoCyMEZRwhiXWyxO5QxI6w5NhT4U1WsJvDO60J34fW9hwzwlKij6ZAW9ne4L0s8C6XeBMEkd/LQy1VucBRot6QMlbivaBhoBgjqGiCJNhsqVp/S2SsG6DIONCR0dXhvWbJ+MRRZJkkuEjgDXJjFQW6SSL7GXK8Z2CZg7cVsbWGoKmEpzQ5elpiy8Ryg7dMkLLUEauzeO86CuwlSOlgYLojZWeJ9xM3S1PWfEfKl5ISLQ0MEKR8YOB2QfCxJBjrKPCN4f9MkaSsqoVXJBmP7EpFZ9UQfOoOFwSzBN4MQ8LsGrymlipcJQhmy0GaQjPqCHaXRwuCZwRbqK2Fg9wlClZqYicrIgMdZfxTQ0c7TBIbrChxmuzoKG8XRaSrIhhiyNFJkrC7oIAWMEOQa5aBekPCRknCo4IKPrYkvCDI8aYmY7WFtprgekcJZ3oLIqssCSMtFbQTJKwXYy3BY5oCh2iKPCpJOE+zRdpYgi6O2KmOAgvVCYaU4ySRek1sgyFhJ403QFHiVEmJHwtybO1gs8Hr5+BETQX3War0qZngYGgtVZtoqd6vFSk/UwdZElYqyjrF4HXUeFspIi9IGKf4j92pKGAdCYMVsbcV3kRF0N+R8LUd5PCsIGWoxDtBkCI0nKofdJQxT+LtZflvuc8Q3CjwWkq8KwUpHzkK/NmSsclCL0nseQdj5FRH5CNHSgtLiW80Of5HU9Hhlsga9bnBq3fEVltKfO5IaSTmGjjc4J0otcP7QsJUSQM8pEj5/wCuUuC2DWz8AAAAAElFTkSuQmCC"); } diff --git a/theme/blackboard.css b/theme/blackboard.css index 7b73a924f5..6e7bab7d03 100644 --- a/theme/blackboard.css +++ b/theme/blackboard.css @@ -2,8 +2,8 @@ .cm-s-blackboard { background: #0C1021; color: #F8F8F8; } .cm-s-blackboard .CodeMirror-selected { background: #253B76 !important; } -.cm-s-blackboard .CodeMirror-gutter { background: #0C1021; border-right: 0; } -.cm-s-blackboard .CodeMirror-gutter-text { color: #888; } +.cm-s-blackboard .CodeMirror-gutters { background: #0C1021; border-right: 0; } +.cm-s-blackboard .CodeMirror-linenumber { color: #888; } .cm-s-blackboard .CodeMirror-cursor { border-left: 1px solid #A7A7A7 !important; } .cm-s-blackboard .cm-keyword { color: #FBDE2D; } diff --git a/theme/cobalt.css b/theme/cobalt.css index dbbb7e4960..daf0abe366 100644 --- a/theme/cobalt.css +++ b/theme/cobalt.css @@ -1,7 +1,7 @@ .cm-s-cobalt { background: #002240; color: white; } .cm-s-cobalt div.CodeMirror-selected { background: #b36539 !important; } -.cm-s-cobalt .CodeMirror-gutter { background: #002240; border-right: 1px solid #aaa; } -.cm-s-cobalt .CodeMirror-gutter-text { color: #d0d0d0; } +.cm-s-cobalt .CodeMirror-gutters { background: #002240; border-right: 1px solid #aaa; } +.cm-s-cobalt .CodeMirror-linenumber { color: #d0d0d0; } .cm-s-cobalt .CodeMirror-cursor { border-left: 1px solid white !important; } .cm-s-cobalt span.cm-comment { color: #08f; } diff --git a/theme/erlang-dark.css b/theme/erlang-dark.css index 486b1c47f3..d6a6d377db 100644 --- a/theme/erlang-dark.css +++ b/theme/erlang-dark.css @@ -1,7 +1,7 @@ .cm-s-erlang-dark { background: #002240; color: white; } .cm-s-erlang-dark div.CodeMirror-selected { background: #b36539 !important; } -.cm-s-erlang-dark .CodeMirror-gutter { background: #002240; border-right: 1px solid #aaa; } -.cm-s-erlang-dark .CodeMirror-gutter-text { color: #d0d0d0; } +.cm-s-erlang-dark .CodeMirror-gutters { background: #002240; border-right: 1px solid #aaa; } +.cm-s-erlang-dark .CodeMirror-linenumber { color: #d0d0d0; } .cm-s-erlang-dark .CodeMirror-cursor { border-left: 1px solid white !important; } .cm-s-erlang-dark span.cm-atom { color: #845dc4; } diff --git a/theme/lesser-dark.css b/theme/lesser-dark.css index ffa6a3f2b3..de624501bc 100644 --- a/theme/lesser-dark.css +++ b/theme/lesser-dark.css @@ -16,8 +16,8 @@ Ported to CodeMirror by Peter Kroon div.CodeMirror span.CodeMirror-matchingbracket { color: #7EFC7E; }/*65FC65*/ -.cm-s-lesser-dark .CodeMirror-gutter { background: #262626; border-right:1px solid #aaa; padding-right:3px; min-width:2.5em; } -.cm-s-lesser-dark .CodeMirror-gutter-text { color: #777; } +.cm-s-lesser-dark .CodeMirror-gutters { background: #262626; border-right:1px solid #aaa; padding-right:3px; min-width:2.5em; } +.cm-s-lesser-dark .CodeMirror-linenumber { color: #777; } .cm-s-lesser-dark span.cm-keyword { color: #599eff; } .cm-s-lesser-dark span.cm-atom { color: #C2B470; } diff --git a/theme/monokai.css b/theme/monokai.css index f01d066f13..89692f5d6a 100644 --- a/theme/monokai.css +++ b/theme/monokai.css @@ -2,8 +2,8 @@ .cm-s-monokai {background: #272822; color: #f8f8f2;} .cm-s-monokai div.CodeMirror-selected {background: #49483E !important;} -.cm-s-monokai .CodeMirror-gutter {background: #272822; border-right: 0px;} -.cm-s-monokai .CodeMirror-gutter-text {color: #d0d0d0;} +.cm-s-monokai .CodeMirror-gutters {background: #272822; border-right: 0px;} +.cm-s-monokai .CodeMirror-linenumber {color: #d0d0d0;} .cm-s-monokai .CodeMirror-cursor {border-left: 1px solid #f8f8f0 !important;} .cm-s-monokai span.cm-comment {color: #75715e;} diff --git a/theme/night.css b/theme/night.css index 9d51d950a4..b403e54dd6 100644 --- a/theme/night.css +++ b/theme/night.css @@ -2,8 +2,8 @@ .cm-s-night { background: #0a001f; color: #f8f8f8; } .cm-s-night div.CodeMirror-selected { background: #447 !important; } -.cm-s-night .CodeMirror-gutter { background: #0a001f; border-right: 1px solid #aaa; } -.cm-s-night .CodeMirror-gutter-text { color: #f8f8f8; } +.cm-s-night .CodeMirror-gutters { background: #0a001f; border-right: 1px solid #aaa; } +.cm-s-night .CodeMirror-linenumber { color: #f8f8f8; } .cm-s-night .CodeMirror-cursor { border-left: 1px solid white !important; } .cm-s-night span.cm-comment { color: #6900a1; } diff --git a/theme/rubyblue.css b/theme/rubyblue.css index 502817aead..495418bbb1 100644 --- a/theme/rubyblue.css +++ b/theme/rubyblue.css @@ -2,8 +2,8 @@ .cm-s-rubyblue { background: #112435; color: white; } .cm-s-rubyblue div.CodeMirror-selected { background: #38566F !important; } -.cm-s-rubyblue .CodeMirror-gutter { background: #1F4661; border-right: 7px solid #3E7087; min-width:2.5em; } -.cm-s-rubyblue .CodeMirror-gutter-text { color: white; } +.cm-s-rubyblue .CodeMirror-gutters { background: #1F4661; border-right: 7px solid #3E7087; min-width:2.5em; } +.cm-s-rubyblue .CodeMirror-linenumber { color: white; } .cm-s-rubyblue .CodeMirror-cursor { border-left: 1px solid white !important; } .cm-s-rubyblue span.cm-comment { color: #999; font-style:italic; line-height: 1em; } diff --git a/theme/vibrant-ink.css b/theme/vibrant-ink.css index de5bc2c9cb..9a6fd4df49 100644 --- a/theme/vibrant-ink.css +++ b/theme/vibrant-ink.css @@ -3,8 +3,8 @@ .cm-s-vibrant-ink { background: black; color: white; } .cm-s-vibrant-ink .CodeMirror-selected { background: #35493c !important; } -.cm-s-vibrant-ink .CodeMirror-gutter { background: #002240; border-right: 1px solid #aaa; } -.cm-s-vibrant-ink .CodeMirror-gutter-text { color: #d0d0d0; } +.cm-s-vibrant-ink .CodeMirror-gutters { background: #002240; border-right: 1px solid #aaa; } +.cm-s-vibrant-ink .CodeMirror-linenumber { color: #d0d0d0; } .cm-s-vibrant-ink .CodeMirror-cursor { border-left: 1px solid white !important; } .cm-s-vibrant-ink .cm-keyword { color: #CC7832; } diff --git a/theme/xq-dark.css b/theme/xq-dark.css index 493e3a639a..5add87be70 100644 --- a/theme/xq-dark.css +++ b/theme/xq-dark.css @@ -22,8 +22,8 @@ THE SOFTWARE. */ .cm-s-xq-dark { background: #0a001f; color: #f8f8f8; } .cm-s-xq-dark span.CodeMirror-selected { background: #a8f !important; } -.cm-s-xq-dark .CodeMirror-gutter { background: #0a001f; border-right: 1px solid #aaa; } -.cm-s-xq-dark .CodeMirror-gutter-text { color: #f8f8f8; } +.cm-s-xq-dark .CodeMirror-gutters { background: #0a001f; border-right: 1px solid #aaa; } +.cm-s-xq-dark .CodeMirror-linenumber { color: #f8f8f8; } .cm-s-xq-dark .CodeMirror-cursor { border-left: 1px solid white !important; } .cm-s-xq-dark span.cm-keyword {color: #FFBD40;} From 28e96d7100df8ad7ed896ad34c98906caa2a42a0 Mon Sep 17 00:00:00 2001 From: Marijn Haverbeke Date: Mon, 27 Aug 2012 13:32:47 +0200 Subject: [PATCH 0023/5780] Properly handle leading/trailing whitespace in bidi algorithm --- lib/codemirror.js | 10 +++++++++- 1 file changed, 9 insertions(+), 1 deletion(-) diff --git a/lib/codemirror.js b/lib/codemirror.js index 1ec0520842..471f60f3aa 100644 --- a/lib/codemirror.js +++ b/lib/codemirror.js @@ -3605,7 +3605,7 @@ window.CodeMirror = (function() { // levels (0, 1, 2) in an implementation that doesn't take // explicit embedding into account, we can build up the order on // the fly, without following the level-based algorithm. - var order = []; + var order = [], m; for (var i = 0; i < len;) { if (countsAsLeft.test(types[i])) { var start = i; @@ -3626,6 +3626,14 @@ window.CodeMirror = (function() { if (pos < i) order.splice(at, 0, {from: pos, to: i, level: 1}); } } + if (order[0].level == 1 && (m = str.match(/^\s+/))) { + order[0].from = m[0].length; + order.unshift({from: 0, to: m[0].length, level: 0}); + } + if (order[order.length-1].level == 1 && (m = str.match(/\s+$/))) { + order[order.length-1].to -= m[0].length; + order.push({from: len - m[0].length, to: len, level: 0}); + } return order; }; From bf1f071cedb74abf2691a0f7c4ed8a9600a86194 Mon Sep 17 00:00:00 2001 From: Marijn Haverbeke Date: Mon, 27 Aug 2012 14:30:49 +0200 Subject: [PATCH 0024/5780] Skip over combining characters when moving through text Makes Hebrew text mostly usable --- lib/codemirror.js | 37 ++++++++++++++++++++++++------------- 1 file changed, 24 insertions(+), 13 deletions(-) diff --git a/lib/codemirror.js b/lib/codemirror.js index 471f60f3aa..501bb703a8 100644 --- a/lib/codemirror.js +++ b/lib/codemirror.js @@ -1460,7 +1460,7 @@ window.CodeMirror = (function() { } } function moveOnce(boundToLine) { - var next = (visually ? moveVisually : moveLogically)(lineObj, ch, dir); + var next = (visually ? moveVisually : moveLogically)(lineObj, ch, dir, true); if (next == null) { if (!boundToLine && findNextLine()) { if (visually) ch = (dir < 0 ? lineRight : lineLeft)(lineObj); @@ -1891,8 +1891,9 @@ window.CodeMirror = (function() { // Do a binary search between these bounds. for (;;) { if (bidi ? to == from || to == moveVisually(lineObj, from, 1) : to - from <= 1) { - var after = x - fromX < toX - x; - return {line: lineNo, ch: after ? from : to, after: after}; + var after = x - fromX < toX - x, ch = after ? from : to; + while (isExtendingChar.test(lineObj.text.charAt(ch))) ++ch; + return {line: lineNo, ch: ch, after: after}; } var step = Math.ceil(dist / 2), middle = from + step; if (bidi) { @@ -2835,10 +2836,12 @@ window.CodeMirror = (function() { pre.anchor = span_(text.slice(cut), style || ""); wrapAt--; } else { - pre.anchor = span_(text.slice(cut, cut + 1), style || ""); - if (compensateForWrapping && spanAffectsWrapping.test(text.slice(cut, cut + 2))) + var end = cut + 1; + while (isExtendingChar.test(text.charAt(end))) ++end; + pre.anchor = span_(text.slice(cut, end), style || ""); + if (compensateForWrapping && spanAffectsWrapping.test(text.slice(cut, end + 1))) pre.appendChild(elt("wbr")); - span_(text.slice(cut + 1), style); + span_(text.slice(end), style); } outPos += l; } else { @@ -3418,15 +3421,22 @@ window.CodeMirror = (function() { return order[0].level % 2 ? lineLeft(line) : lineRight(line); } + var isExtendingChar = /[\u0300-\u036F\u0483-\u0487\u0488-\u0489\u0591-\u05BD\u05BF\u05C1-\u05C2\u05C4-\u05C5\u05C7\u0610-\u061A\u064B-\u065F\u0670\u06D6-\u06DC\u06DF-\u06E4\u06E7-\u06E8\u06EA-\u06ED\uA66F\uA670-\uA672\uA674-\uA67D\uA69F]/; + // This is somewhat involved. It is needed in order to move // 'visually' through bi-directional text -- i.e., pressing left // should make the cursor go left, even when in RTL text. The // tricky part is the 'jumps', where RTL and LTR text touch each // other. This often requires the cursor offset to move more than // one unit, in order to visually move one unit. - function moveVisually(line, start, dir) { + function moveVisually(line, start, dir, byUnit) { var bidi = getOrder(line); - if (!bidi) return moveLogically(line, start, dir); + if (!bidi) return moveLogically(line, start, dir, byUnit); + var moveOneUnit = byUnit ? function(pos, dir) { + do pos += dir; + while (pos > 0 && isExtendingChar.test(line.text.charAt(pos))); + return pos; + } : function(pos, dir) { return pos + dir; }; var linedir = bidi[0].level, last = bidi[bidi.length-1]; if (linedir == last.level % 2) last = null; for (var i = 0; i < bidi.length; ++i) { @@ -3434,11 +3444,11 @@ window.CodeMirror = (function() { if ((part.from < start && part.to > start) || (sticky && (part.from == start || part.to == start))) break; } - var target = start + (part.level % 2 ? -dir : dir); + var target = moveOneUnit(start, part.level % 2 ? -dir : dir); if (i == bidi.length) { if (dir > 0) return null; // Moving right from EOL - target = last.level % 1 ? last.from + 1 : last.to - 1; - if (last.to - last.from == 1) return bidiRight(bidi[i-2]); + target = last.level % 1 ? moveOneUnit(last.from, 1) : moveOneUnit(last.to, -1); + if (moveOneUnit(last.from, 1) == last.to) return bidiRight(bidi[i-2]); else return target; } @@ -3446,7 +3456,7 @@ window.CodeMirror = (function() { if (part.level % 2 == linedir) { if (target < part.from || target > part.to) { part = bidi[i += dir]; - target = part && (dir > 0 == part.level % 2 ? part.to - 1 : part.from + 1); + target = part && (dir > 0 == part.level % 2 ? moveOneUnit(part.to, -1) : moveOneUnit(part.from, 1)); } else break; } else { if (target == bidiLeft(part)) { @@ -3463,8 +3473,9 @@ window.CodeMirror = (function() { return target < 0 || target > line.text.length ? null : target; } - function moveLogically(line, start, dir) { + function moveLogically(line, start, dir, byUnit) { var target = start + dir; + if (byUnit) while (target > 0 && isExtendingChar.test(line.text.charAt(target))) target += dir; return target < 0 || target > line.text.length ? null : target; } From 873fe349ce1e0c9b5702c07409bc5a98bc82f1b3 Mon Sep 17 00:00:00 2001 From: Marijn Haverbeke Date: Tue, 28 Aug 2012 09:24:30 +0200 Subject: [PATCH 0025/5780] Base the callback interface on events This allows multiple handlers to listen for a single event, and makes the API a bit more uniform. Issue #725 --- demo/activeline.html | 10 +- demo/changemode.html | 10 +- demo/folding.html | 6 +- demo/marker.html | 24 +- demo/matchhighlighter.html | 8 +- demo/preview.html | 10 +- doc/manual.html | 144 ++++++----- lib/codemirror.js | 448 ++++++++++++++++++---------------- lib/util/foldcode.js | 2 +- lib/util/match-highlighter.js | 2 +- 10 files changed, 354 insertions(+), 310 deletions(-) diff --git a/demo/activeline.html b/demo/activeline.html index a42ce97ba8..70e5836666 100644 --- a/demo/activeline.html +++ b/demo/activeline.html @@ -58,13 +58,13 @@

    CodeMirror: Active Line Demo

    var editor = CodeMirror.fromTextArea(document.getElementById("code"), { mode: "application/xml", lineNumbers: true, - lineWrapping: true, - onCursorActivity: function() { - editor.setLineClass(hlLine, null, null); - hlLine = editor.setLineClass(editor.getCursor().line, null, "activeline"); - } + lineWrapping: true }); var hlLine = editor.setLineClass(0, "activeline"); +editor.connect("cursorActivity", function() { + editor.setLineClass(hlLine, null, null); + hlLine = editor.setLineClass(editor.getCursor().line, null, "activeline"); +});

    Styling the current cursor line.

    diff --git a/demo/changemode.html b/demo/changemode.html index a3d42c04bf..17c82d277c 100644 --- a/demo/changemode.html +++ b/demo/changemode.html @@ -33,11 +33,11 @@

    CodeMirror: Mode-Changing demo

    mode: "scheme", lineNumbers: true, matchBrackets: true, - tabMode: "indent", - onChange: function() { - clearTimeout(pending); - setTimeout(update, 400); - } + tabMode: "indent" + }); + editor.connect("change", function() { + clearTimeout(pending); + setTimeout(update, 400); }); var pending; function looksLikeScheme(code) { diff --git a/demo/folding.html b/demo/folding.html index 994f5862fc..9c2fc9e339 100644 --- a/demo/folding.html +++ b/demo/folding.html @@ -42,9 +42,9 @@

    CodeMirror: Code Folding Demo

    lineNumbers: true, gutters: ["CodeMirror-linenumbers", "CodeMirror-folded"], lineWrapping: true, - onGutterClick: foldFunc, extraKeys: {"Ctrl-Q": function(cm){foldFunc(cm, cm.getCursor().line);}} }); + editor.connect("gutterClick", foldFunc); foldFunc(editor, 9); foldFunc(editor, 20); @@ -54,9 +54,9 @@

    CodeMirror: Code Folding Demo

    lineNumbers: true, gutters: ["CodeMirror-linenumbers", "CodeMirror-folded"], lineWrapping: true, - onGutterClick: foldFunc_html, extraKeys: {"Ctrl-Q": function(cm){foldFunc_html(cm, cm.getCursor().line);}} - }) + }); + editor_html.connect("gutterClick", foldFunc_html); foldFunc_html(editor_html, 1); foldFunc_html(editor_html, 15); }; diff --git a/demo/marker.html b/demo/marker.html index c570be3546..f557be49c3 100644 --- a/demo/marker.html +++ b/demo/marker.html @@ -17,13 +17,13 @@

    CodeMirror: Breakpoint demo

    diff --git a/demo/preview.html b/demo/preview.html index f356891200..583ebf5f15 100644 --- a/demo/preview.html +++ b/demo/preview.html @@ -56,11 +56,11 @@

    CodeMirror: HTML5 preview

    // Initialize CodeMirror editor with a nice html5 canvas demo. var editor = CodeMirror.fromTextArea(document.getElementById('code'), { mode: 'text/html', - tabMode: 'indent', - onChange: function() { - clearTimeout(delay); - delay = setTimeout(updatePreview, 300); - } + tabMode: 'indent' + }); + editor.connect("change", function() { + clearTimeout(delay); + delay = setTimeout(updatePreview, 300); }); function updatePreview() { diff --git a/doc/manual.html b/doc/manual.html index 4d215b8f24..197121e41e 100644 --- a/doc/manual.html +++ b/doc/manual.html @@ -211,56 +211,6 @@

    Configuration

    simply true), focusing of the editor is also disallowed.
    -
    onChange (function)
    -
    When given, this function will be called every time the - content of the editor is changed. It will be given the editor - instance as first argument, and an {from, to, text, next} - object containing information about the changes - that occurred as second argument. from - and to are the positions (in the pre-change - coordinate system) where the change started and - ended (for example, it might be {ch:0, line:18} if the - position is at the beginning of line #19). text - is an array of strings representing the text that replaced the changed - range (split by line). If multiple changes happened during a single - operation, the object will have a next property pointing to - another change object (which may point to another, etc).
    - -
    onCursorActivity (function)
    -
    Will be called when the cursor or selection moves, or any - change is made to the editor content.
    - -
    onViewportChange (function)
    -
    When given, will be called whenever - the view port of the editor changes - (due to scrolling, editing, or any other factor). It will be - passed three arguments, the editor instance, the start of the - viewport, and its end.
    - -
    onGutterClick (function)
    -
    When given, will be called whenever the editor gutter (the - line-number area) is clicked. Will be given the editor instance - as first argument, the (zero-based) number of the line that was - clicked as second argument, the raw mousedown event - object as third argument, and the CSS class of the gutter that - was clicked as fourth argument.
    - -
    onFocus, onBlur (function)
    -
    The given functions will be called whenever the editor is - focused or unfocused.
    - -
    onScroll (function)
    -
    When given, will be called whenever the editor is - scrolled.
    - -
    onHighlightComplete (function)
    -
    Whenever the editor's content has been fully highlighted, - this function (if given) will be called. It'll be given a single - argument, the editor instance.
    - -
    onUpdate (function)
    -
    Will be called whenever CodeMirror updates its DOM display.
    -
    matchBrackets (boolean)
    Determines whether brackets are matched whenever the cursor is moved next to a bracket.
    @@ -333,6 +283,79 @@

    Configuration

    that need character data). +

    Events

    + +

    A CodeMirror instance emits a number of events, which allow + client code to react to various situations. These are registered + with the connect method (and + removed with the disconnect + method). These are the events that fire on the instance object. + The name of the event is followed by the arguments that will be + passed to the handler. The instance argument always + refers to the editor instance.

    + +
    +
    "change" (instance, changeObj)
    +
    Fires every time the content of the editor is changed. + The changeObj is a {from, to, text, + next} object containing information about the changes + that occurred as second argument. from + and to are the positions (in the pre-change + coordinate system) where the change started and ended (for + example, it might be {ch:0, line:18} if the + position is at the beginning of line #19). text is + an array of strings representing the text that replaced the + changed range (split by line). If multiple changes happened + during a single operation, the object will have + a next property pointing to another change object + (which may point to another, etc).
    + +
    "cursorActivity" (instance)
    +
    Will be fired when the cursor or selection moves, or any + change is made to the editor content.
    + +
    "viewportChange" (instance, from, to)
    +
    Fires whenever the view port of + the editor changes (due to scrolling, editing, or any other + factor). The from and to arguments + give the new start and end of the viewport.
    + +
    "gutterClick" (instance, line, gutter, clickEvent)
    +
    Fires when the editor gutter (the line-number area) is + clicked. Will pass the editor instance as first argument, the + (zero-based) number of the line that was clicked as second + argument, the CSS class of the gutter that was clicked as third + argument, and the raw mousedown event object as + fourth argument.
    + +
    "focus", "blur" (instance)
    +
    These fire whenever the editor is focused or unfocused.
    + +
    "scroll" (instance)
    +
    Fires when the editor is scrolled.
    + +
    "highlightComplete" (instance)
    +
    Whenever the editor's content has been fully highlighted, + this event is fired.
    + +
    "update" (instance)
    +
    Will be fired whenever CodeMirror updates its DOM display.
    +
    + +

    It is also possible to register events + on other objects. Line handles (as returned by, for + example, getLineHandle) + can be listened on with CodeMirror.connect(handle, "delete", + myFunc). They support the following events:

    + +
    +
    "delete" ()
    +
    Will be fired when the line object is deleted. A line object + is associated with the start of the line. Mostly useful + when you need to find out when your gutter + markers on a given line are removed.
    +
    +

    Keymaps

    Keymaps are ways to associate keys with functionality. A keymap @@ -540,6 +563,16 @@

    Programming API

    Retrieves the current value of the given option for this editor instance.
    +
    connect(type, func)
    +
    Register an event handler for the given event type (a + string) on the editor instance. There is also + a CodeMirror.connect(object, type, func) version + that allows registering of events on any object.
    +
    disconnect(type, func)
    +
    Remove an event handler on the editor instance. An + equivalent CodeMirror.disconnect(object, type, + func) also exists.
    +
    cursorCoords(start, mode) → object
    Returns an {left, top, bottom} object containing the coordinates of the cursor position. @@ -655,10 +688,6 @@

    Programming API

    The inverse of hideLine—re-shows a previously hidden line, by number or by handle.
    -
    onDeleteLine(line, func)
    -
    Register a function that should be called when the line is - deleted from the document.
    -
    lineInfo(line) → object
    Returns the line number, text content, and marker status of the given line, which can be either a number or a handle @@ -676,8 +705,8 @@

    Programming API

    part of the document. In big documents, when most content is scrolled out of view, CodeMirror will only render the visible part, and a margin around it. See also - the onViewportChange - option.
    + the viewportChange + event.
    addWidget(pos, node, scrollIntoView)
    Puts node, which should be an absolutely @@ -930,7 +959,7 @@

    Add-ons

    match-highlighter.js
    Adds a matchHighlight method to CodeMirror instances that can be called (typically from - a onCursorActivity + a cursorActivity handler) to highlight all instances of a currently selected word with the a classname given as a first argument to the method. Depends on @@ -1174,6 +1203,7 @@

    Contents

  • Overview
  • Basic Usage
  • Configuration
  • +
  • Events
  • Keymaps
  • Customized Styling
  • Programming API
  • diff --git a/lib/codemirror.js b/lib/codemirror.js index 501bb703a8..561dfca304 100644 --- a/lib/codemirror.js +++ b/lib/codemirror.js @@ -8,154 +8,13 @@ window.CodeMirror = (function() { // This is the function that produces an editor instance. Its // closure is used to store the editor state. function CodeMirror(place, givenOptions) { - // Determine effective options based on given values and defaults. - var options = {}, defaults = CodeMirror.defaults; - for (var opt in defaults) - if (defaults.hasOwnProperty(opt)) - options[opt] = (givenOptions && givenOptions.hasOwnProperty(opt) ? givenOptions : defaults)[opt]; - - var input = elt("textarea", null, null, "position: absolute; padding: 0; width: 1px; height: 1em; outline: none;"); - input.setAttribute("wrap", "off"); input.setAttribute("autocorrect", "off"); input.setAttribute("autocapitalize", "off"); - // Wraps and hides input textarea - var inputDiv = elt("div", [input], null, "overflow: hidden; position: relative; width: 3px; height: 0px;"); - // The actual fake scrollbars. - var scrollbarH = elt("div", [elt("div", null, null, "height: 1px")], "CodeMirror-hscrollbar"); - var scrollbarV = elt("div", [elt("div", null, null, "width: 1px")], "CodeMirror-vscrollbar"); - var scrollbarFiller = elt("div", null, "CodeMirror-scrollbar-filler"); - // DIVs containing the selection and the actual code - var lineDiv = elt("div"), selectionDiv = elt("div", null, null, "position: relative; z-index: -1"); - // Blinky cursor, and element used to ensure cursor fits at the end of a line - var cursor = elt("pre", "\u00a0", "CodeMirror-cursor"); - // Secondary cursor, shown when on a 'jump' in bi-directional text - var otherCursor = elt("pre", "\u00a0", "CodeMirror-cursor CodeMirror-secondarycursor"); - // Used to measure text size - var measure = elt("div", null, "CodeMirror-measure"); - // Wraps everything that needs to exist inside the vertically-padded coordinate system - var lineSpace = elt("div", [measure, cursor, otherCursor, selectionDiv, lineDiv], null, "position: relative"); - // Moved around its parent to cover visible view - var mover = elt("div", [elt("div", [lineSpace], "CodeMirror-lines")], null, "position: relative"); - var gutters = elt("div", null, "CodeMirror-gutters"), lineGutter; - // Set to the height of the text, causes scrolling - var sizer = elt("div", [mover], "CodeMirror-sizer"); - // Provides scrolling - var scroller = elt("div", [sizer], "CodeMirror-scroll"); - scroller.setAttribute("tabIndex", "-1"); - // The element in which the editor lives. - var wrapper = elt("div", [gutters, inputDiv, scrollbarH, scrollbarV, scrollbarFiller, scroller], - "CodeMirror" + (options.lineWrapping ? " CodeMirror-wrap" : "")); - if (place.appendChild) place.appendChild(wrapper); else place(wrapper); - - themeChanged(); keyMapChanged(); - // Needed to hide big blue blinking cursor on Mobile Safari - if (ios) input.style.width = "0px"; - if (!webkit) scroller.draggable = true; - lineSpace.style.outline = "none"; - if (options.tabindex != null) input.tabIndex = options.tabindex; - if (options.autofocus) focusInput(); - setGuttersForLineNumbers(); updateGutters(); - // Needed to handle Tab key in KHTML - if (khtml) inputDiv.style.height = "1px", inputDiv.style.position = "absolute"; - - // Need to set a minimum width to see the scrollbar on IE7 (but must not set it on IE8). - else if (ie_lt8) scrollbarH.style.minWidth = scrollbarV.style.minWidth = "18px"; - - // Delayed object wrap timeouts, making sure only one is active. blinker holds an interval. - var poll = new Delayed(), highlight = new Delayed(), blinker; - - var measureLineMemo = [], measureLineMemoPos = 0; - // mode holds a mode API object. doc is the tree of Line objects, - // work an array of lines that should be parsed, and history the - // undo history (instance of History constructor). - var mode, doc = new BranchChunk([new LeafChunk([new Line("", null, textHeight())])]), work, focused; - loadMode(); - // The selection. These are always maintained to point at valid - // positions. Inverted is used to remember that the user is - // selecting bottom-to-top. - var sel = {from: {line: 0, ch: 0}, to: {line: 0, ch: 0}, inverted: false}; - // Selection-related flags. shiftSelecting obviously tracks - // whether the user is holding shift. - var shiftSelecting, lastClick, lastDoubleClick, lastScrollTop = 0, lastScrollLeft = 0, draggingText, - overwrite = false, suppressEdits = false; - // Variables used by startOperation/endOperation to track what - // happened during the operation. - var updateInput, userSelChange, changes, textChanged, selectionChanged, leaveInputAlone, - callbacks; - // Current visible range (may be bigger than the view window). - var displayOffset = 0, showingFrom = 0, showingTo = 0, lastSizeC = 0; - // bracketHighlighted is used to remember that a bracket has been - // marked. - var bracketHighlighted; - // Tracks the maximum line length so that the horizontal scrollbar - // can be kept static when scrolling. - var maxLine = getLine(0), updateMaxLine = false, maxLineChanged = true; - var tabCache = {}; - var pollingFast = false; // Ensures slowPoll doesn't cancel fastPoll - var goalColumn = null; - - // Initialize the content. - operation(function(){setValue(options.value || ""); updateInput = false;})(); - var history = new History(); - - // Register our event handlers. - connect(scroller, "mousedown", operation(onMouseDown)); - connect(gutters, "mousedown", clickInGutter); - connect(scroller, "dblclick", operation(onDoubleClick)); - connect(lineSpace, "selectstart", e_preventDefault); - // Gecko browsers fire contextmenu *after* opening the menu, at - // which point we can't mess with it anymore. Context menu is - // handled in onMouseDown for Gecko. - if (!gecko) connect(scroller, "contextmenu", onContextMenu); - connect(scroller, "scroll", onScrollMain); - connect(scrollbarH, "scroll", onScrollBarH); - connect(scrollbarV, "scroll", onScrollBarV); - connect(scrollbarH, "mousedown", function() {if (focused) setTimeout(focusInput, 0);}); - connect(scrollbarV, "mousedown", function() {if (focused) setTimeout(focusInput, 0);}); - var resizeHandler = connect(window, "resize", function() { - if (wrapper.parentNode) updateDisplay(true); - else resizeHandler(); - }, true); - connect(input, "keyup", operation(onKeyUp)); - connect(input, "input", fastPoll); - connect(input, "keydown", operation(onKeyDown)); - connect(input, "keypress", operation(onKeyPress)); - connect(input, "focus", onFocus); - connect(input, "blur", onBlur); - - function drag_(e) { - if (options.onDragEvent && options.onDragEvent(instance, addStop(e))) return; - e_stop(e); - } - if (options.dragDrop) { - connect(scroller, "dragstart", onDragStart); - connect(scroller, "dragenter", drag_); - connect(scroller, "dragover", drag_); - connect(scroller, "drop", operation(onDrop)); - } - connect(scroller, "paste", function(){focusInput(); fastPoll();}); - connect(input, "paste", fastPoll); - connect(input, "cut", operation(function(){ - if (!options.readOnly) replaceSelection(""); - })); - - // Needed to handle Tab key in KHTML - if (khtml) connect(sizer, "mouseup", function() { - if (document.activeElement == input) input.blur(); - focusInput(); - }); - - // IE throws unspecified error in certain cases, when - // trying to access activeElement before onload - var hasFocus; try { hasFocus = (document.activeElement == input); } catch(e) { } - if (hasFocus || options.autofocus) setTimeout(onFocus, 20); - else onBlur(); - function isLine(l) {return l >= 0 && l < doc.size;} // The instance object that we'll return. Mostly calls out to // local functions in the CodeMirror function. Some do some extra // range checking and/or clipping. operation is used to wrap the // call so that changes it makes are tracked, and the display is // updated afterwards. - var instance = wrapper.CodeMirror = { + var instance = { getValue: getValue, setValue: operation(setValue), getSelection: getSelection, @@ -229,14 +88,6 @@ window.CodeMirror = (function() { setLineClass: operation(setLineClass), hideLine: operation(function(h) {return setLineHidden(h, true);}), showLine: operation(function(h) {return setLineHidden(h, false);}), - onDeleteLine: function(line, f) { - if (typeof line == "number") { - if (!isLine(line)) return null; - line = getLine(line); - } - (line.handlers || (line.handlers = [])).push(f); - return line; - }, lineInfo: lineInfo, getViewport: function() { return {from: showingFrom, to: showingTo};}, addWidget: function(pos, node, scroll, vert, horiz) { @@ -344,6 +195,8 @@ window.CodeMirror = (function() { if (height != null) scroller.style.height = interpret(height); instance.refresh(); }, + connect: function(type, f) {connect(this, type, f);}, + disconnect: function(type, f) {disconnect(this, type, f);}, operation: function(f){return operation(f)();}, compoundChange: function(f){return compoundChange(f);}, @@ -358,6 +211,148 @@ window.CodeMirror = (function() { getGutterElement: function(){return gutters;} }; + // Determine effective options based on given values and defaults. + var options = {}, defaults = CodeMirror.defaults; + for (var opt in defaults) + if (defaults.hasOwnProperty(opt)) + options[opt] = (givenOptions && givenOptions.hasOwnProperty(opt) ? givenOptions : defaults)[opt]; + + var input = elt("textarea", null, null, "position: absolute; padding: 0; width: 1px; height: 1em; outline: none;"); + input.setAttribute("wrap", "off"); input.setAttribute("autocorrect", "off"); input.setAttribute("autocapitalize", "off"); + // Wraps and hides input textarea + var inputDiv = elt("div", [input], null, "overflow: hidden; position: relative; width: 3px; height: 0px;"); + // The actual fake scrollbars. + var scrollbarH = elt("div", [elt("div", null, null, "height: 1px")], "CodeMirror-hscrollbar"); + var scrollbarV = elt("div", [elt("div", null, null, "width: 1px")], "CodeMirror-vscrollbar"); + var scrollbarFiller = elt("div", null, "CodeMirror-scrollbar-filler"); + // DIVs containing the selection and the actual code + var lineDiv = elt("div"), selectionDiv = elt("div", null, null, "position: relative; z-index: -1"); + // Blinky cursor, and element used to ensure cursor fits at the end of a line + var cursor = elt("pre", "\u00a0", "CodeMirror-cursor"); + // Secondary cursor, shown when on a 'jump' in bi-directional text + var otherCursor = elt("pre", "\u00a0", "CodeMirror-cursor CodeMirror-secondarycursor"); + // Used to measure text size + var measure = elt("div", null, "CodeMirror-measure"); + // Wraps everything that needs to exist inside the vertically-padded coordinate system + var lineSpace = elt("div", [measure, cursor, otherCursor, selectionDiv, lineDiv], null, "position: relative"); + // Moved around its parent to cover visible view + var mover = elt("div", [elt("div", [lineSpace], "CodeMirror-lines")], null, "position: relative"); + var gutters = elt("div", null, "CodeMirror-gutters"), lineGutter; + // Set to the height of the text, causes scrolling + var sizer = elt("div", [mover], "CodeMirror-sizer"); + // Provides scrolling + var scroller = elt("div", [sizer], "CodeMirror-scroll"); + scroller.setAttribute("tabIndex", "-1"); + // The element in which the editor lives. + var wrapper = elt("div", [gutters, inputDiv, scrollbarH, scrollbarV, scrollbarFiller, scroller], + "CodeMirror" + (options.lineWrapping ? " CodeMirror-wrap" : "")); + wrapper.CodeMirror = instance; + if (place.appendChild) place.appendChild(wrapper); else place(wrapper); + + themeChanged(); keyMapChanged(); + // Needed to hide big blue blinking cursor on Mobile Safari + if (ios) input.style.width = "0px"; + if (!webkit) scroller.draggable = true; + lineSpace.style.outline = "none"; + if (options.tabindex != null) input.tabIndex = options.tabindex; + if (options.autofocus) focusInput(); + setGuttersForLineNumbers(); updateGutters(); + // Needed to handle Tab key in KHTML + if (khtml) inputDiv.style.height = "1px", inputDiv.style.position = "absolute"; + + // Need to set a minimum width to see the scrollbar on IE7 (but must not set it on IE8). + else if (ie_lt8) scrollbarH.style.minWidth = scrollbarV.style.minWidth = "18px"; + + // Delayed object wrap timeouts, making sure only one is active. blinker holds an interval. + var poll = new Delayed(), highlight = new Delayed(), blinker; + + var measureLineMemo = [], measureLineMemoPos = 0; + // mode holds a mode API object. doc is the tree of Line objects, + // work an array of lines that should be parsed, and history the + // undo history (instance of History constructor). + var mode, doc = new BranchChunk([new LeafChunk([new Line("", null, textHeight())])]), work, focused; + loadMode(); + // The selection. These are always maintained to point at valid + // positions. Inverted is used to remember that the user is + // selecting bottom-to-top. + var sel = {from: {line: 0, ch: 0}, to: {line: 0, ch: 0}, inverted: false}; + // Selection-related flags. shiftSelecting obviously tracks + // whether the user is holding shift. + var shiftSelecting, lastClick, lastDoubleClick, lastScrollTop = 0, lastScrollLeft = 0, draggingText, + overwrite = false, suppressEdits = false; + // Variables used by startOperation/endOperation to track what + // happened during the operation. + var updateInput, userSelChange, changes, textChanged, selectionChanged, leaveInputAlone; + var delayedCallbacks; + // Current visible range (may be bigger than the view window). + var displayOffset = 0, showingFrom = 0, showingTo = 0, lastSizeC = 0; + // bracketHighlighted is used to remember that a bracket has been + // marked. + var bracketHighlighted; + // Tracks the maximum line length so that the horizontal scrollbar + // can be kept static when scrolling. + var maxLine = getLine(0), updateMaxLine = false, maxLineChanged = true; + var tabCache = {}; + var pollingFast = false; // Ensures slowPoll doesn't cancel fastPoll + var goalColumn = null; + + // Initialize the content. + operation(function(){setValue(options.value || ""); updateInput = false;})(); + var history = new History(); + + // Register our event handlers. + connect(scroller, "mousedown", operation(onMouseDown)); + connect(gutters, "mousedown", operation(clickInGutter)); + connect(scroller, "dblclick", operation(onDoubleClick)); + connect(lineSpace, "selectstart", e_preventDefault); + // Gecko browsers fire contextmenu *after* opening the menu, at + // which point we can't mess with it anymore. Context menu is + // handled in onMouseDown for Gecko. + if (!gecko) connect(scroller, "contextmenu", onContextMenu); + connect(scroller, "scroll", onScrollMain); + connect(scrollbarH, "scroll", onScrollBarH); + connect(scrollbarV, "scroll", onScrollBarV); + connect(scrollbarH, "mousedown", function() {if (focused) setTimeout(focusInput, 0);}); + connect(scrollbarV, "mousedown", function() {if (focused) setTimeout(focusInput, 0);}); + connect(window, "resize", function resizeHandler() { + if (wrapper.parentNode) updateDisplay(true); + else disconnect(window, "resize", resizeHandler); + }); + connect(input, "keyup", operation(onKeyUp)); + connect(input, "input", fastPoll); + connect(input, "keydown", operation(onKeyDown)); + connect(input, "keypress", operation(onKeyPress)); + connect(input, "focus", onFocus); + connect(input, "blur", onBlur); + + function drag_(e) { + if (options.onDragEvent && options.onDragEvent(instance, addStop(e))) return; + e_stop(e); + } + if (options.dragDrop) { + connect(scroller, "dragstart", onDragStart); + connect(scroller, "dragenter", drag_); + connect(scroller, "dragover", drag_); + connect(scroller, "drop", operation(onDrop)); + } + connect(scroller, "paste", function(){focusInput(); fastPoll();}); + connect(input, "paste", fastPoll); + connect(input, "cut", operation(function(){ + if (!options.readOnly) replaceSelection(""); + })); + + // Needed to handle Tab key in KHTML + if (khtml) connect(sizer, "mouseup", function() { + if (document.activeElement == input) input.blur(); + focusInput(); + }); + + // IE throws unspecified error in certain cases, when + // trying to access activeElement before onload + var hasFocus; try { hasFocus = (document.activeElement == input); } catch(e) { } + if (hasFocus || options.autofocus) setTimeout(onFocus, 20); + else onBlur(); + function getLine(n) { return getLineAt(doc, n); } function updateLineHeight(line, height) { var diff = height - line.height; @@ -408,7 +403,7 @@ window.CodeMirror = (function() { scrollbarH.scrollLeft = lastScrollLeft = scroller.scrollLeft; alignLineNumbers(); } - if (options.onScroll) options.onScroll(instance); + signal(instance, "scroll", instance); } function onMouseDown(e) { @@ -451,26 +446,27 @@ window.CodeMirror = (function() { setSelectionUser(word.from, word.to); } else { lastClick = {time: now, pos: start}; } - function dragEnd(e2) { - if (webkit) scroller.draggable = false; - draggingText = false; - up(); drop(); - if (Math.abs(e.clientX - e2.clientX) + Math.abs(e.clientY - e2.clientY) < 10) { - e_preventDefault(e2); - setCursor(start.line, start.ch, true); - focusInput(); - } - } var last = start, going; if (options.dragDrop && dragAndDrop && !options.readOnly && !posEq(sel.from, sel.to) && !posLess(start, sel.from) && !posLess(sel.to, start) && type == "single") { + var dragEnd = operation(function(e2) { + if (webkit) scroller.draggable = false; + draggingText = false; + disconnect(document, "mouseup", dragEnd); + disconnect(scroller, "drop", dragEnd); + if (Math.abs(e.clientX - e2.clientX) + Math.abs(e.clientY - e2.clientY) < 10) { + e_preventDefault(e2); + setCursor(start.line, start.ch, true); + focusInput(); + } + }); // Let the drag handler handle this. if (webkit) scroller.draggable = true; - var up = connect(document, "mouseup", operation(dragEnd), true); - var drop = connect(scroller, "drop", operation(dragEnd), true); draggingText = true; // IE's approach to draggable if (scroller.dragDrop) scroller.dragDrop(); + connect(document, "mouseup", dragEnd); + connect(scroller, "drop", dragEnd); return; } e_preventDefault(e); @@ -511,15 +507,19 @@ window.CodeMirror = (function() { e_preventDefault(e); focusInput(); updateInput = true; - move(); up(); + disconnect(document, "mousemove", move); + disconnect(document, "mouseup", up); } - var move = connect(document, "mousemove", operation(function(e) { + + var move = operation(function(e) { clearTimeout(going); e_preventDefault(e); if (!ie && !e_button(e)) done(e); else extend(e); - }), true); - var up = connect(document, "mouseup", operation(done), true); + }); + var up = operation(done); + connect(document, "mousemove", move); + connect(document, "mouseup", up); } function onDoubleClick(e) { e_preventDefault(e); @@ -570,7 +570,7 @@ window.CodeMirror = (function() { catch(e) { return false; } if (mX >= Math.floor(gutters.getBoundingClientRect().right)) return false; - if (options.onGutterClick) { + if (hasHandler(instance, "gutterClick")) { mY -= wrapper.getBoundingClientRect().top; for (var i = 0; i < options.gutters.length; ++i) { var g = gutters.childNodes[i]; @@ -578,7 +578,7 @@ window.CodeMirror = (function() { if (mY < lineDiv.offsetHeight) { var line = lineAtHeight(doc, mY); var gutter = options.gutters[i]; - setTimeout(function() {options.onGutterClick(instance, line, e, gutter);}, 20); + signalLater(instance, "gutterClick", delayedCallbacks, instance, line, gutter, e); } break; } @@ -703,7 +703,7 @@ window.CodeMirror = (function() { function onFocus() { if (options.readOnly == "nocursor") return; if (!focused) { - if (options.onFocus) options.onFocus(instance); + signal(instance, "focus", instance); focused = true; if (scroller.className.search(/\bCodeMirror-focused\b/) == -1) scroller.className += " CodeMirror-focused"; @@ -714,7 +714,7 @@ window.CodeMirror = (function() { } function onBlur() { if (focused) { - if (options.onBlur) options.onBlur(instance); + signal(instance, "blur", instance); focused = false; if (bracketHighlighted) operation(function(){ @@ -777,7 +777,7 @@ window.CodeMirror = (function() { } else lastLine.fixMarkStarts(); for (var i = 0, e = newText.length - 1; i < e; ++i) added.push(Line.inheritMarks(newText[i], prevLine, th)); - if (nlines) doc.remove(from.line, nlines, callbacks); + if (nlines) doc.remove(from.line, nlines, delayedCallbacks); if (added.length) doc.insert(from.line, added); } else if (firstLine == lastLine) { if (newText.length == 1) @@ -796,7 +796,7 @@ window.CodeMirror = (function() { firstLine.replace(from.ch, null, newText[0]); lastLine.replace(null, to.ch, ""); firstLine.append(lastLine); - doc.remove(from.line + 1, nlines, callbacks); + doc.remove(from.line + 1, nlines, delayedCallbacks); } else { var added = []; firstLine.replace(from.ch, null, newText[0]); @@ -804,7 +804,7 @@ window.CodeMirror = (function() { firstLine.fixMarkEnds(lastLine); for (var i = 1, e = newText.length - 1; i < e; ++i) added.push(Line.inheritMarks(newText[i], firstLine, th)); - if (nlines > 1) doc.remove(from.line + 1, nlines - 1, callbacks); + if (nlines > 1) doc.remove(from.line + 1, nlines - 1, delayedCallbacks); doc.insert(from.line + 1, added); } if (options.lineWrapping) { @@ -1109,10 +1109,8 @@ window.CodeMirror = (function() { // This is just a bogus formula that detects when the editor is // resized or the font size changes. if (different) lastSizeC = wrapper.clientHeight; - if (from != showingFrom || to != showingTo && options.onViewportChange) - setTimeout(function(){ - if (options.onViewportChange) options.onViewportChange(instance, from, to); - }); + if (from != showingFrom || to != showingTo) + signalLater(instance, "viewportChange", delayedCallbacks, instance, from, to); showingFrom = from; showingTo = to; displayOffset = heightAtLine(doc, from); @@ -1148,7 +1146,7 @@ window.CodeMirror = (function() { mover.style.top = displayOffset + "px"; updateScrollbars(scrollTop); updateSelection(); - if (!suppressCallback && options.onUpdate) options.onUpdate(instance); + if (!suppressCallback) signal(instance, "update", instance); return true; } @@ -1975,10 +1973,10 @@ window.CodeMirror = (function() { if (gecko) { e_stop(e); - var mouseup = connect(window, "mouseup", function() { - mouseup(); + connect(window, "mouseup", function mouseup() { + disconnect(window, "mouseup", mouseup); setTimeout(rehide, 20); - }, true); + }); } else { setTimeout(rehide, 50); } @@ -2112,8 +2110,7 @@ window.CodeMirror = (function() { if (bail) return; if (realChange) changes.push({from: task, to: i + 1}); } - if (foundWork && options.onHighlightComplete) - options.onHighlightComplete(instance); + if (foundWork) signal(instance, "highlightComplete", instance); } function startWorker(time) { if (!work.length) return; @@ -2126,7 +2123,7 @@ window.CodeMirror = (function() { // batched and then all combined and executed at once. function startOperation() { updateInput = userSelChange = textChanged = null; - changes = []; selectionChanged = false; callbacks = []; + changes = []; selectionChanged = false; delayedCallbacks = []; } function endOperation() { if (updateMaxLine) computeMaxLength(); @@ -2143,9 +2140,7 @@ window.CodeMirror = (function() { } if (changes.length || newScrollPos && newScrollPos.scrollTop != null) updated = updateDisplay(changes, true, newScrollPos && newScrollPos.scrollTop); - if (!updated) { - if (selectionChanged) updateSelection(); - } + if (!updated && selectionChanged) updateSelection(); if (newScrollPos) scrollCursorIntoView(); if (selectionChanged) restartBlink(); @@ -2158,13 +2153,13 @@ window.CodeMirror = (function() { if (bracketHighlighted) {bracketHighlighted(); bracketHighlighted = null;} if (posEq(sel.from, sel.to)) matchBrackets(false); }), 20); - var sc = selectionChanged, cbs = callbacks; // these can be reset by callbacks - if (textChanged && options.onChange && instance) - options.onChange(instance, textChanged); - if (sc && options.onCursorActivity) - options.onCursorActivity(instance); + var sc = selectionChanged, cbs = delayedCallbacks; // these can be reset by callbacks + delayedCallbacks = null; + if (textChanged) + signal(instance, "change", instance, textChanged); + if (sc) signal(instance, "cursorActivity", instance); for (var i = 0; i < cbs.length; ++i) cbs[i](instance); - if (updated && options.onUpdate) options.onUpdate(instance); + if (updated) signal(instance, "update", instance); } var nestedOperation = 0; function operation(f) { @@ -2210,13 +2205,6 @@ window.CodeMirror = (function() { firstLineNumber: 1, readOnly: false, dragDrop: true, - onChange: null, - onCursorActivity: null, - onViewportChange: null, - onGutterClick: null, - onHighlightComplete: null, - onUpdate: null, - onFocus: null, onBlur: null, onScroll: null, matchBrackets: false, cursorBlinkRate: 530, workTime: 100, @@ -2426,7 +2414,7 @@ window.CodeMirror = (function() { function save() {textarea.value = instance.getValue();} if (textarea.form) { // Deplorable hack to make the submit method do the right thing. - var rmSubmit = connect(textarea.form, "submit", save, true); + connect(textarea.form, "submit", save); if (typeof textarea.form.submit == "function") { var realSubmit = textarea.form.submit; textarea.form.submit = function wrappedSubmit() { @@ -2449,7 +2437,7 @@ window.CodeMirror = (function() { textarea.parentNode.removeChild(instance.getWrapperElement()); textarea.style.display = ""; if (textarea.form) { - rmSubmit(); + disconnect(textarea.form, "submit", save); if (typeof textarea.form.submit == "function") textarea.form.submit = realSubmit; } @@ -2942,8 +2930,7 @@ window.CodeMirror = (function() { var line = this.lines[i]; this.height -= line.height; line.cleanUp(); - if (line.handlers) - for (var j = 0; j < line.handlers.length; ++j) callbacks.push(line.handlers[j]); + signalLater(line, "delete", callbacks); } this.lines.splice(at, n); }, @@ -3190,19 +3177,48 @@ window.CodeMirror = (function() { return overridden ? e.override[prop] : e[prop]; } - // Event handler registration. If disconnect is true, it'll return a - // function that unregisters the handler. - function connect(node, type, handler, disconnect) { - if (typeof node.addEventListener == "function") { - node.addEventListener(type, handler, false); - if (disconnect) return function() {node.removeEventListener(type, handler, false);}; - } else { - var wrapHandler = function(event) {handler(event || window.event);}; - node.attachEvent("on" + type, wrapHandler); - if (disconnect) return function() {node.detachEvent("on" + type, wrapHandler);}; + function connect(emitter, type, f) { + if (emitter.addEventListener) + emitter.addEventListener(type, f, false); + else if (emitter.attachEvent) + emitter.attachEvent("on" + type, f); + else { + var map = emitter._handlers || (emitter._handlers = {}); + var arr = map[type] || (map[type] = []); + arr.push(f); + } + } + function disconnect(emitter, type, f) { + if (emitter.removeEventListener) + emitter.removeEventListener(type, f, false); + else if (emitter.detachEvent) + emitter.detachEvent("on" + type, f); + else { + var arr = emitter._handlers && emitter._handlers[type]; + if (!arr) return; + for (var i = 0; i < arr.length; ++i) + if (arr[i] == f) { arr.splice(i, 1); break; } } } - CodeMirror.connect = connect; + function signal(emitter, type /*, values...*/) { + var arr = emitter._handlers && emitter._handlers[type]; + if (!arr) return; + var args = Array.prototype.slice.call(arguments, 2); + for (var i = 0; i < arr.length; ++i) arr[i].apply(null, args); + } + function signalLater(emitter, type, flist /*, values...*/) { + var arr = emitter._handlers && emitter._handlers[type]; + if (!arr) return; + var args = Array.prototype.slice.call(arguments, 3); + function bnd(f) {return function(){f.apply(null, args);};}; + for (var i = 0; i < arr.length; ++i) + if (flist) flist.push(bnd(arr[i])); else arr[i].apply(null, args); + } + function hasHandler(emitter, type) { + var arr = emitter._handlers && emitter._handlers[type]; + return arr && arr.length > 0; + } + CodeMirror.connect = connect; CodeMirror.disconnect = disconnect; CodeMirror.signal = signal; function Delayed() {this.id = null;} Delayed.prototype = {set: function(ms, f) {clearTimeout(this.id); this.id = setTimeout(f, ms);}}; diff --git a/lib/util/foldcode.js b/lib/util/foldcode.js index cbe1b353b5..48327e8c00 100644 --- a/lib/util/foldcode.js +++ b/lib/util/foldcode.js @@ -191,7 +191,7 @@ CodeMirror.newFoldFunction = function(rangeFinder, markText, hideEnd) { elt.innerHTML = markText; var first = cm.setGutterMarker(line, "CodeMirror-folded", elt); var region = {start: first, hidden: hidden}; - cm.onDeleteLine(first, function() { expand(cm, region); }); + CodeMirror.connect(first, "delete", function() { expand(cm, region); }); folded.push(region); } }); diff --git a/lib/util/match-highlighter.js b/lib/util/match-highlighter.js index 59098ff86c..5e52f837e8 100644 --- a/lib/util/match-highlighter.js +++ b/lib/util/match-highlighter.js @@ -1,5 +1,5 @@ // Define match-highlighter commands. Depends on searchcursor.js -// Use by attaching the following function call to the onCursorActivity event: +// Use by attaching the following function call to the cursorActivity event: //myCodeMirror.matchHighlight(minChars); // And including a special span.CodeMirror-matchhighlight css class (also optionally a separate one for .CodeMirror-focused -- see demo matchhighlighter.html) From e2a278b7a235a7c0f72ef29fe1e27fb7299e0fe7 Mon Sep 17 00:00:00 2001 From: Marijn Haverbeke Date: Tue, 28 Aug 2012 12:10:29 +0200 Subject: [PATCH 0026/5780] [tests] Add test for text-unit motion --- test/test.js | 15 +++++++++++++++ 1 file changed, 15 insertions(+) diff --git a/test/test.js b/test/test.js index 13f7e7aca0..abf5cf79d3 100644 --- a/test/test.js +++ b/test/test.js @@ -680,3 +680,18 @@ testCM("rtlMovement", function(cm) { } }); }); + +testCM("movebyTextUnit", function(cm) { + cm.setValue("בְּרֵאשִ\ńéée\n"); + cm.execCommand("goLineEnd"); + for (var i = 0; i < 4; ++i) cm.execCommand("goCharRight"); + eqPos(cm.getCursor(), {line: 0, ch: 0}); + cm.execCommand("goCharRight"); + eqPos(cm.getCursor(), {line: 1, ch: 0}); + cm.execCommand("goCharRight"); + cm.execCommand("goCharRight"); + eqPos(cm.getCursor(), {line: 1, ch: 3}); + cm.execCommand("goCharRight"); + cm.execCommand("goCharRight"); + eqPos(cm.getCursor(), {line: 1, ch: 6}); +}); From 564aa8775622e8be81cfc4300ae0de100fb84994 Mon Sep 17 00:00:00 2001 From: Marijn Haverbeke Date: Tue, 28 Aug 2012 14:05:16 +0200 Subject: [PATCH 0027/5780] Overhaul the folding system It is now more structured (knows about ranges) and allows automatic unfolding when the cursor enters the folded range. --- doc/manual.html | 38 +++++++++++++++----- lib/codemirror.js | 84 +++++++++++++++++++++++++------------------- lib/util/foldcode.js | 39 ++++++++++---------- test/test.js | 23 ++++++++++-- 4 files changed, 116 insertions(+), 68 deletions(-) diff --git a/doc/manual.html b/doc/manual.html index 197121e41e..10f0a99c98 100644 --- a/doc/manual.html +++ b/doc/manual.html @@ -356,6 +356,18 @@

    Events

    markers on a given line are removed.
    +

    Folded range handles, as returned + by foldLines, emit the + following event:

    + +
    +
    "unfold" ()
    +
    Fired when the range is unfolded, either through cursor + movement or through a call + to unfoldLines. Will + only be fired once per handle.
    +
    +

    Keymaps

    Keymaps are ways to associate keys with functionality. A keymap @@ -678,15 +690,23 @@

    Programming API

    for the line, and backgroundClassName to style its background (which lies behind the selection). Pass null to clear the classes for a line. -
    hideLine(line) → lineHandle
    -
    Hide the given line (either by number or by handle). Hidden - lines don't show up in the editor, and their numbers are skipped - when line numbers are enabled. - Deleting a region around them does delete them, and coping a - region around will include them in the copied text.
    -
    showLine(line) → lineHandle
    -
    The inverse of hideLine—re-shows a previously - hidden line, by number or by handle.
    + +
    foldLines(from, to, unfoldOnEnter) → foldHandle
    +
    Hide the given range of lines (to is + non-inclusive). Hidden lines don't show up in the editor. + Deleting a region around them does delete them, and copying a + region around will include them in the copied text. Vertical + cursor movement will skip hidden lines. + When unfoldOnEnter is true, horizontal movement + into the range will un-hide the range. When it is false, + horizonal cursor movement will skip it as if it isn't there. The + handle this function returns can be used to unfold the range, + and emits an unfold + event when unfolded. Folded ranges may overlap and nest.
    +
    unfoldLines(foldHandle)
    +
    When given a handle as returned + by foldLines, this will + unfold that range of lines.
    lineInfo(line) → object
    Returns the line number, text content, and marker status of diff --git a/lib/codemirror.js b/lib/codemirror.js index 561dfca304..71e02e43d5 100644 --- a/lib/codemirror.js +++ b/lib/codemirror.js @@ -86,8 +86,8 @@ window.CodeMirror = (function() { setGutterMarker: operation(setGutterMarker), clearGutter: operation(clearGutter), setLineClass: operation(setLineClass), - hideLine: operation(function(h) {return setLineHidden(h, true);}), - showLine: operation(function(h) {return setLineHidden(h, false);}), + foldLines: operation(foldLines), + unfoldLines: operation(unfoldLines), lineInfo: lineInfo, getViewport: function() { return {from: showingFrom, to: showingTo};}, addWidget: function(pos, node, scroll, vert, horiz) { @@ -847,9 +847,7 @@ window.CodeMirror = (function() { } else textChanged = changeObj; // Update the selection - function updateLine(n) {return n <= Math.min(to.line, to.line + lendiff) ? n : n + lendiff;} - setSelection(clipPos(selFrom), clipPos(selTo), - updateLine(sel.from.line), updateLine(sel.to.line)); + setSelection(clipPos(selFrom), clipPos(selTo), true); } function updateScrollbars(scrollTop) { @@ -1378,20 +1376,19 @@ window.CodeMirror = (function() { // Update the selection. Last two args are only used by // updateLines, since they have to be expressed in the line // numbers before the update. - function setSelection(from, to, oldFrom, oldTo) { + function setSelection(from, to, isChange) { goalColumn = null; - if (oldFrom == null) {oldFrom = sel.from.line; oldTo = sel.to.line;} if (posEq(sel.from, from) && posEq(sel.to, to)) return; if (posLess(to, from)) {var tmp = to; to = from; from = tmp;} // Skip over hidden lines. - if (from.line != oldFrom) { - var from1 = skipHidden(from, oldFrom, sel.from.ch); + if (isChange || from.line != sel.from.line) { + var from1 = skipHidden(from, sel.from.line, sel.from.ch, true); // If there is no non-hidden line left, force visibility on current line - if (!from1) setLineHidden(from.line, false); + if (!from1) unfoldLines(getLine(from.line).hidden.id); else from = from1; } - if (to.line != oldTo) to = skipHidden(to, oldTo, sel.to.ch); + if (isChange || to.line != sel.to.line) to = skipHidden(to, sel.to.line, sel.to.ch, true); if (posEq(from, to)) sel.inverted = false; else if (posEq(from, sel.to)) sel.inverted = false; @@ -1414,7 +1411,7 @@ window.CodeMirror = (function() { sel.from = from; sel.to = to; selectionChanged = true; } - function skipHidden(pos, oldLine, oldCh) { + function skipHidden(pos, oldLine, oldCh, allowUnfold) { function getNonHidden(dir) { var lNo = pos.line + dir, end = dir == 1 ? doc.size : -1; while (lNo != end) { @@ -1428,8 +1425,10 @@ window.CodeMirror = (function() { } } var line = getLine(pos.line); - var toEnd = pos.ch == line.text.length && pos.ch != oldCh; + while (allowUnfold && line.hidden && line.hidden.handle.unfoldOnEnter) + unfoldLines(line.hidden.handle); if (!line.hidden) return pos; + var toEnd = pos.ch == line.text.length && pos.ch != oldCh; if (pos.line >= oldLine) return getNonHidden(1) || getNonHidden(-1); else return getNonHidden(-1) || getNonHidden(1); } @@ -1454,7 +1453,7 @@ window.CodeMirror = (function() { function findNextLine() { for (var l = line + dir, e = dir < 0 ? -1 : doc.size; l != e; l += dir) { var lo = getLine(l); - if (!lo.hidden) { line = l; lineObj = lo; return true; } + if (!lo.hidden || !lo.hidden.handle.unfoldOnEneter) { line = l; lineObj = lo; return true; } } } function moveOnce(boundToLine) { @@ -1717,29 +1716,42 @@ window.CodeMirror = (function() { } }); } - function setLineHidden(handle, hidden) { - return changeLine(handle, function(line, no) { - if (line.hidden != hidden) { - line.hidden = hidden; - if (!options.lineWrapping) { - if (hidden && line.text.length == maxLine.text.length) { - updateMaxLine = true; - } else if (!hidden && line.text.length > maxLine.text.length) { - maxLine = line; updateMaxLine = false; - } - } - updateLineHeight(line, hidden ? 0 : 1); - var fline = sel.from.line, tline = sel.to.line; - if (hidden && (fline == no || tline == no)) { - var from = fline == no ? skipHidden({line: fline, ch: 0}, fline, 0) : sel.from; - var to = tline == no ? skipHidden({line: tline, ch: 0}, tline, 0) : sel.to; - // Can't hide the last visible line, we'd have no place to put the cursor - if (!to) return; - setSelection(from, to); - } - return true; - } + function foldLines(from, to, unfoldOnEnter) { + if (typeof from != "number") from = lineNo(from); + if (typeof to != "number") to = lineNo(to); + if (from > to) return; + var lines = [], handle = {lines: lines, unfoldOnEnter: unfoldOnEnter}; + doc.iter(from, to, function(line) { + lines.push(line); + if (!line.hidden && line.text.length == maxLine.text.length) updateMaxLine = true; + line.hidden = {handle: handle, prev: line.hidden}; + updateLineHeight(line, 0); }); + var selFrom = sel.from, selTo = sel.to; + if (selFrom.line >= from && selFrom.line < to) selFrom = skipHidden({line: selFrom.line, ch: 0}, selFrom.line, 0); + if (selTo.line >= from && selTo.line < to) selTo = skipHidden({line: selTo.line, ch: 0}, selTo.line, 0); + if (selFrom != sel.from || selTo != sel.to) setSelection(selFrom, selTo); + changes.push({from: from, to: to}); + return handle; + } + function unfoldLines(handle) { + var from, to; + for (var i = 0; i < handle.lines.length; ++i) { + var line = handle.lines[i], hidden = line.hidden; + if (!line.parent) continue; + if (hidden && hidden.handle == handle) line.hidden = line.hidden.prev; + else for (var h = hidden; h; h = h.prev) if (h.prev && h.prev.handle == handle) h.prev = h.prev.prev; + if (hidden && !line.hidden) { + var no = lineNo(handle); + from = Math.min(from, no); to = Math.max(to, no); + updateLineHeight(line, textHeight()); + if (line.text.length > maxLine.text.length) { maxLine = line; maxLineChanged = true; } + } + } + if (from != null) { + changes.push({from: from, to: to + 1}); + signalLater(handle, "unfold", delayedCallbacks); + } } function lineInfo(line) { diff --git a/lib/util/foldcode.js b/lib/util/foldcode.js index 48327e8c00..bf4ba1d785 100644 --- a/lib/util/foldcode.js +++ b/lib/util/foldcode.js @@ -158,41 +158,40 @@ CodeMirror.newFoldFunction = function(rangeFinder, markText, hideEnd) { var folded = []; if (markText == null) markText = "\u25bc"; - function isFolded(cm, n) { + function isFolded(handle) { for (var i = 0; i < folded.length; ++i) { - var start = cm.lineInfo(folded[i].start); - if (!start) folded.splice(i--, 1); - else if (start.line == n) return {pos: i, region: folded[i]}; + if (folded[i].start == handle) return {pos: i, region: folded[i]}; + if (!folded[i].start.parent) folded.splice(i--, 1); } } - function expand(cm, region) { - cm.setGutterMarker(region.start, "CodeMirror-folded", null); - for (var i = 0; i < region.hidden.length; ++i) - cm.showLine(region.hidden[i]); - } - return function(cm, line) { cm.operation(function() { - var known = isFolded(cm, line); + var l = cm.getLineHandle(line), known = isFolded(l); if (known) { folded.splice(known.pos, 1); - expand(cm, known.region); + cm.unfoldLines(known.region.handle); + cm.setGutterMarker(l, "CodeMirror-folded", null); } else { var end = rangeFinder(cm, line, hideEnd); if (end == null) return; - var hidden = []; - for (var i = line + 1; i < end; ++i) { - var handle = cm.hideLine(i); - if (handle) hidden.push(handle); - } + var handle = cm.foldLines(line + 1, end, true); + CodeMirror.connect(handle, "unfold", function() { + cm.setGutterMarker(l, "CodeMirror-folded", null); + }); var elt = document.createElement("div"); elt.className = "CodeMirror-foldmarker"; elt.innerHTML = markText; var first = cm.setGutterMarker(line, "CodeMirror-folded", elt); - var region = {start: first, hidden: hidden}; - CodeMirror.connect(first, "delete", function() { expand(cm, region); }); - folded.push(region); + function clear() { + var known = isFolded(first); + if (!known) return; + cm.unfoldLines(known.region.handle); + folded.splice(known.pos, 1); + } + CodeMirror.connect(first, "delete", clear); + CodeMirror.connect(cm.getLineHandle(line + 1), "delete", clear); + folded.push({start: first, handle: handle}); } }); }; diff --git a/test/test.js b/test/test.js index abf5cf79d3..2f01e0e285 100644 --- a/test/test.js +++ b/test/test.js @@ -422,7 +422,8 @@ testCM("setSize", function(cm) { testCM("hiddenLines", function(cm) { addDoc(cm, 4, 10); - cm.hideLine(4); + var folded = cm.foldLines(4, 5), unfolded = 0; + CodeMirror.connect(folded, "unfold", function() {unfolded++;}); cm.setCursor({line: 3, ch: 0}); CodeMirror.commands.goLineDown(cm); eqPos(cm.getCursor(), {line: 5, ch: 0}); @@ -434,12 +435,28 @@ testCM("hiddenLines", function(cm) { cm.setCursor({line: 3, ch: 2}); CodeMirror.commands.goLineDown(cm); eqPos(cm.getCursor(), {line: 5, ch: 2}); + cm.unfoldLines(folded); cm.unfoldLines(folded); + eq(unfolded, 1); }); +testCM("hiddenLinesAutoUnfold", function(cm) { + var folded = cm.foldLines(1, 3, true), unfolded = 0; + CodeMirror.connect(folded, "unfold", function() {unfolded++;}); + cm.setCursor({line: 3, ch: 0}); + eq(unfolded, 0); + cm.execCommand("goCharLeft"); + eq(unfolded, 1); + var folded = cm.foldLines(1, 3, true), unfolded = 0; + CodeMirror.connect(folded, "unfold", function() {unfolded++;}); + eqPos(cm.getCursor(), {line: 3, ch: 0}); + cm.setCursor({line: 0, ch: 3}); + cm.execCommand("goCharRight"); + eq(unfolded, 1); +}, {value: "abc\ndef\nghi\njkl"}); + testCM("hiddenLinesSelectAll", function(cm) { // Issue #484 addDoc(cm, 4, 20); - for (var i = 0; i < 20; ++i) - if (i != 10) cm.hideLine(i); + cm.foldLines(0, 10); cm.foldLines(11, 20); CodeMirror.commands.selectAll(cm); eqPos(cm.getCursor(true), {line: 10, ch: 0}); eqPos(cm.getCursor(false), {line: 10, ch: 4}); From 3e36c4db6812be6e77108d340ae618e4be3854fb Mon Sep 17 00:00:00 2001 From: Marijn Haverbeke Date: Tue, 28 Aug 2012 14:12:10 +0200 Subject: [PATCH 0028/5780] Add a change event to lines, use it to make folding more robust --- doc/manual.html | 3 +++ lib/codemirror.js | 6 ++++-- lib/util/foldcode.js | 1 + test/test.js | 20 ++++++++++++++++++++ 4 files changed, 28 insertions(+), 2 deletions(-) diff --git a/doc/manual.html b/doc/manual.html index 10f0a99c98..f94b867775 100644 --- a/doc/manual.html +++ b/doc/manual.html @@ -354,6 +354,9 @@

    Events

    is associated with the start of the line. Mostly useful when you need to find out when your gutter markers on a given line are removed.
    +
    "change" ()
    +
    Fires when the line's text content is changed in any way + (but the line is not deleted outright).

    Folded range handles, as returned diff --git a/lib/codemirror.js b/lib/codemirror.js index 71e02e43d5..87bf6bc779 100644 --- a/lib/codemirror.js +++ b/lib/codemirror.js @@ -765,9 +765,10 @@ window.CodeMirror = (function() { }); var nlines = to.line - from.line, firstLine = getLine(from.line), lastLine = getLine(to.line); - var th = textHeight(); + var th = textHeight(), wholeLines = from.ch == 0 && to.ch == 0 && newText[newText.length - 1] == ""; + if (!wholeLines) signalLater(firstLine, "change", delayedCallbacks); // First adjust the line structure, taking some care to leave highlighting intact. - if (from.ch == 0 && to.ch == 0 && newText[newText.length - 1] == "") { + if (wholeLines) { // This is a whole-line replace. Treated specially to make // sure line objects move the way they are supposed to. var added = [], prevLine = null; @@ -800,6 +801,7 @@ window.CodeMirror = (function() { } else { var added = []; firstLine.replace(from.ch, null, newText[0]); + signalLater(lastLine, "change", delayedCallbacks); lastLine.replace(null, to.ch, newText[newText.length-1]); firstLine.fixMarkEnds(lastLine); for (var i = 1, e = newText.length - 1; i < e; ++i) diff --git a/lib/util/foldcode.js b/lib/util/foldcode.js index bf4ba1d785..46c25bf46c 100644 --- a/lib/util/foldcode.js +++ b/lib/util/foldcode.js @@ -190,6 +190,7 @@ CodeMirror.newFoldFunction = function(rangeFinder, markText, hideEnd) { folded.splice(known.pos, 1); } CodeMirror.connect(first, "delete", clear); + CodeMirror.connect(first, "change", clear); CodeMirror.connect(cm.getLineHandle(line + 1), "delete", clear); folded.push({start: first, handle: handle}); } diff --git a/test/test.js b/test/test.js index 2f01e0e285..545ca6766e 100644 --- a/test/test.js +++ b/test/test.js @@ -712,3 +712,23 @@ testCM("movebyTextUnit", function(cm) { cm.execCommand("goCharRight"); eqPos(cm.getCursor(), {line: 1, ch: 6}); }); + +testCM("lineChangeEvents", function(cm) { + addDoc(cm, 3, 5); + var log = [], want = ["ch 0", "ch 1", "del 2", "ch 0", "ch 0", "del 1", "del 3", "del 4"]; + for (var i = 0; i < 5; ++i) { + CodeMirror.connect(cm.getLineHandle(i), "delete", function(i) { + return function() {log.push("del " + i);}; + }(i)); + CodeMirror.connect(cm.getLineHandle(i), "change", function(i) { + return function() {log.push("ch " + i);}; + }(i)); + } + cm.replaceRange("x", {line: 0, ch: 1}); + cm.replaceRange("xy", {line: 1, ch: 1}, {line: 2}); + cm.replaceRange("foo\nbar", {line: 0, ch: 1}); + cm.replaceRange("", {line: 0, ch: 0}, {line: cm.lineCount()}); + eq(log.length, want.length, "same length"); + for (var i = 0; i < log.length; ++i) + eq(log[i], want[i]); +}); From 6ef1983809e1d26bf2f73aa97812fde5feac2062 Mon Sep 17 00:00:00 2001 From: Marijn Haverbeke Date: Tue, 28 Aug 2012 14:28:59 +0200 Subject: [PATCH 0029/5780] Get rid of eltOffset helper, use getBoundingClientRect directly --- lib/codemirror.js | 40 +++++++++++----------------------------- 1 file changed, 11 insertions(+), 29 deletions(-) diff --git a/lib/codemirror.js b/lib/codemirror.js index 87bf6bc779..fe3cd461b1 100644 --- a/lib/codemirror.js +++ b/lib/codemirror.js @@ -77,7 +77,7 @@ window.CodeMirror = (function() { return charCoords(clipPos(pos), mode || "page"); }, coordsChar: function(coords) { - var off = eltOffset(lineSpace); + var off = lineSpace.getBoundingClientRect(); return coordsChar(coords.left - off.left, coords.top - off.top); }, markText: operation(markText), @@ -1357,7 +1357,7 @@ window.CodeMirror = (function() { } // Move the hidden textarea near the cursor to prevent scrolling artifacts - var wrapOff = eltOffset(wrapper), lineOff = eltOffset(lineDiv); + var wrapOff = wrapper.getBoundingClientRect(), lineOff = lineDiv.getBoundingClientRect(); inputDiv.style.top = Math.max(0, Math.min(wrapper.clientHeight - 10, headPos.top + lineOff.top - wrapOff.top)) + "px"; inputDiv.style.left = Math.max(0, Math.min(wrapper.clientWidth - 10, headPos.left + lineOff.left - wrapOff.left)) + "px"; } @@ -1822,7 +1822,7 @@ window.CodeMirror = (function() { if (context == "line") return rect; var yOff = heightAtLine(doc, pos.line) - (context == "div" ? displayOffset : 0); if (context == "page") { - var lOff = eltOffset(lineSpace); + var lOff = lineSpace.getBoundingClientRect(); yOff += lOff.top; rect.left += lOff.left; rect.right += lOff.right; } rect.top += yOff; rect.bottom += yOff; @@ -1948,16 +1948,16 @@ window.CodeMirror = (function() { function paddingTop() {return lineSpace.offsetTop;} function posFromMouse(e, liberal) { - var offW = eltOffset(scroller, true), x, y; + if (!liberal) { + var target = e_target(e); + if (target == scrollbarH || target == scrollbarH.firstChild || + target == scrollbarV || target == scrollbarV.firstChild || + target == scrollbarFiller) return null; + } + var x, y, space = lineSpace.getBoundingClientRect(); // Fails unpredictably on IE[67] when mouse is dragged around quickly. try { x = e.clientX; y = e.clientY; } catch (e) { return null; } - // This is a mess of a heuristic to try and determine whether a - // scroll-bar was clicked or not, and to return null if one was - // (and !liberal). - if (!liberal && (x - offW.left > wrapper.clientWidth || y - offW.top > wrapper.clientHeight)) - return null; - var offL = eltOffset(lineSpace, true); - return coordsChar(x - offL.left, y - offL.top); + return coordsChar(x - space.left, y - space.top); } function onContextMenu(e) { var pos = posFromMouse(e), scrollPos = scroller.scrollTop; @@ -3282,24 +3282,6 @@ window.CodeMirror = (function() { return n; } - function eltOffset(node, screen) { - // Take the parts of bounding client rect that we are interested in so we are able to edit if need be, - // since the returned value cannot be changed externally (they are kept in sync as the element moves within the page) - try { var box = node.getBoundingClientRect(); box = { top: box.top, left: box.left }; } - catch(e) { box = {top: 0, left: 0}; } - if (!screen) { - // Get the toplevel scroll, working around browser differences. - if (window.pageYOffset == null) { - var t = document.documentElement || document.body.parentNode; - if (t.scrollTop == null) t = document.body; - box.top += t.scrollTop; box.left += t.scrollLeft; - } else { - box.top += window.pageYOffset; box.left += window.pageXOffset; - } - } - return box; - } - // Get a node's text content. function eltText(node) { return node.textContent || node.innerText || node.nodeValue || ""; From 95d1c5bc98394605b01aff28b3431f791c482cec Mon Sep 17 00:00:00 2001 From: Marijn Haverbeke Date: Tue, 28 Aug 2012 14:29:20 +0200 Subject: [PATCH 0030/5780] Drop unused eltText helper function --- lib/codemirror.js | 4 ---- 1 file changed, 4 deletions(-) diff --git a/lib/codemirror.js b/lib/codemirror.js index fe3cd461b1..2ad989908d 100644 --- a/lib/codemirror.js +++ b/lib/codemirror.js @@ -3282,10 +3282,6 @@ window.CodeMirror = (function() { return n; } - // Get a node's text content. - function eltText(node) { - return node.textContent || node.innerText || node.nodeValue || ""; - } function selectInput(node) { if (ios) { // Mobile Safari apparently has a bug where select() is broken. node.selectionStart = 0; From a8fb542fa9c1330919d8bb51b2fb14a955b482ab Mon Sep 17 00:00:00 2001 From: Marijn Haverbeke Date: Tue, 28 Aug 2012 15:31:56 +0200 Subject: [PATCH 0031/5780] Start new z-stacking context in mover div Prevents styled backgrounds from hiding the selection --- lib/codemirror.js | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/lib/codemirror.js b/lib/codemirror.js index 2ad989908d..383593cf02 100644 --- a/lib/codemirror.js +++ b/lib/codemirror.js @@ -236,7 +236,7 @@ window.CodeMirror = (function() { // Wraps everything that needs to exist inside the vertically-padded coordinate system var lineSpace = elt("div", [measure, cursor, otherCursor, selectionDiv, lineDiv], null, "position: relative"); // Moved around its parent to cover visible view - var mover = elt("div", [elt("div", [lineSpace], "CodeMirror-lines")], null, "position: relative"); + var mover = elt("div", [elt("div", [lineSpace], "CodeMirror-lines")], null, "position: relative; z-index: 0"); var gutters = elt("div", null, "CodeMirror-gutters"), lineGutter; // Set to the height of the text, causes scrolling var sizer = elt("div", [mover], "CodeMirror-sizer"); From 753e838baa74e2513db59824361cb7475e6011a7 Mon Sep 17 00:00:00 2001 From: Marijn Haverbeke Date: Tue, 28 Aug 2012 15:41:57 +0200 Subject: [PATCH 0032/5780] Prevent vertical gaps from showing up in selection marker --- lib/codemirror.js | 18 ++++++++++++------ 1 file changed, 12 insertions(+), 6 deletions(-) diff --git a/lib/codemirror.js b/lib/codemirror.js index 383593cf02..afc15dd83e 100644 --- a/lib/codemirror.js +++ b/lib/codemirror.js @@ -1314,9 +1314,10 @@ window.CodeMirror = (function() { }; var middleFrom = sel.from.line + 1, middleTo = sel.to.line - 1, sameLine = sel.from.line == sel.to.line; - var drawForLine = function(line, from, toArg) { + var drawForLine = function(line, from, toArg, retTop) { var lineObj = getLine(line), lineLen = lineObj.text.length; var coords = function(ch) { return charCoords({line: line, ch: ch}, "div", lineObj); }; + var rVal = retTop ? Infinity : -Infinity; iterateBidiSections(getOrder(lineObj), from, toArg == null ? lineLen : toArg, function(from, to, dir) { var leftPos = coords(dir == "rtl" ? to - 1 : from); var rightPos = coords(dir == "rtl" ? from : to - 1); @@ -1326,17 +1327,23 @@ window.CodeMirror = (function() { left = paddingLeft(); } if (toArg == null && to == lineLen) right = clientWidth; + rVal = retTop ? Math.min(rightPos.top, rVal) : Math.max(rightPos.bottom, rVal); add(left, rightPos.top, clientWidth - right, rightPos.bottom); }); + return rVal; }; + var middleTop = Infinity, middleBot = -Infinity; if (sel.from.ch || sameLine) // Draw the first line of selection. - drawForLine(sel.from.line, sel.from.ch, sameLine ? sel.to.ch : null); + middleTop = drawForLine(sel.from.line, sel.from.ch, sameLine ? sel.to.ch : null); else // Simply include it in the middle block. middleFrom = sel.from.line; + if (!sameLine && sel.to.ch) + middleBot = drawForLine(sel.to.line, 0, sel.to.ch, true); + if (middleFrom <= middleTo) { // Draw the middle var botLine = getLine(middleTo), @@ -1345,11 +1352,10 @@ window.CodeMirror = (function() { // start end end are on same line. var top = (middleFrom != middleTo || botLine.height > bottom.bottom - bottom.top) ? charCoords({line: middleFrom, ch: 0}, "div") : bottom; - add(paddingLeft(), top.top, 0, bottom.bottom); + middleTop = Math.min(middleTop, top.top); + middleBot = Math.max(middleBot, bottom.bottom); } - - if (!sameLine && sel.to.ch) - drawForLine(sel.to.line, 0, sel.to.ch); + if (middleTop < middleBot) add(paddingLeft(), middleTop, 0, middleBot); removeChildrenAndAdd(selectionDiv, fragment); cursor.style.display = otherCursor.style.display = "none"; From 3721870ef320c4621a2998a12f424a19fb77ba87 Mon Sep 17 00:00:00 2001 From: Marijn Haverbeke Date: Tue, 28 Aug 2012 16:48:54 +0200 Subject: [PATCH 0033/5780] Move matchBrackets out of the core Add CodeMirror.defineOption function for defining new options. Add findMatchingBracket to the methods exported by this extension. Closes #746 --- demo/changemode.html | 1 - doc/compress.html | 1 + doc/manual.html | 22 ++++++++----- lib/codemirror.js | 61 +++++++----------------------------- lib/util/matchbrackets.js | 61 ++++++++++++++++++++++++++++++++++++ mode/clike/index.html | 1 + mode/clike/scala.html | 1 + mode/ecl/index.html | 5 +-- mode/erlang/index.html | 1 + mode/gfm/index.html | 1 - mode/go/index.html | 1 + mode/groovy/index.html | 1 + mode/haskell/index.html | 1 + mode/haxe/index.html | 1 - mode/htmlembedded/index.html | 1 - mode/javascript/index.html | 1 + mode/less/index.html | 1 + mode/lua/index.html | 1 + mode/markdown/index.html | 1 - mode/mysql/index.html | 3 +- mode/ocaml/index.html | 1 + mode/pascal/index.html | 1 - mode/perl/index.html | 3 +- mode/php/index.html | 1 + mode/pig/index.html | 1 - mode/plsql/index.html | 1 - mode/python/index.html | 1 + mode/rpm/changes/index.html | 3 +- mode/rpm/spec/index.html | 3 +- mode/ruby/index.html | 1 + mode/rust/index.html | 1 - mode/shell/index.html | 1 + mode/smalltalk/index.html | 1 + mode/sparql/index.html | 1 + mode/tiddlywiki/index.html | 1 + mode/tiki/index.html | 4 +-- mode/vb/index.html | 1 - mode/vbscript/index.html | 3 +- mode/velocity/index.html | 1 - mode/verilog/index.html | 1 - 40 files changed, 113 insertions(+), 85 deletions(-) create mode 100644 lib/util/matchbrackets.js diff --git a/demo/changemode.html b/demo/changemode.html index 17c82d277c..89536277a7 100644 --- a/demo/changemode.html +++ b/demo/changemode.html @@ -32,7 +32,6 @@

    CodeMirror: Mode-Changing demo

    var editor = CodeMirror.fromTextArea(document.getElementById("code"), { mode: "scheme", lineNumbers: true, - matchBrackets: true, tabMode: "indent" }); editor.connect("change", function() { diff --git a/doc/compress.html b/doc/compress.html index 3e4abc1479..7e3438edb4 100644 --- a/doc/compress.html +++ b/doc/compress.html @@ -119,6 +119,7 @@

    { } CodeMi + diff --git a/doc/manual.html b/doc/manual.html index f94b867775..2b56b6ff07 100644 --- a/doc/manual.html +++ b/doc/manual.html @@ -211,10 +211,6 @@

    Configuration

    simply true), focusing of the editor is also disallowed. -
    matchBrackets (boolean)
    -
    Determines whether brackets are matched whenever the cursor - is moved next to a bracket.
    -
    cursorBlinkRate (number)
    Half-period in milliseconds used for cursor blinking. The default blink rate is 530ms.
    @@ -740,9 +736,6 @@

    Programming API

    widget again, simply use DOM methods (move it somewhere else, or call removeChild on its parent). -
    matchBrackets()
    -
    Force matching-bracket-highlighting to happen.
    -
    lineCount() → number
    Get the number of lines in the editor.
    @@ -867,6 +860,13 @@

    Programming API

    will cause the given value (usually a method) to be added to all CodeMirror instances created from then on.

    +

    Similarly, CodeMirror.defineOption(name, + default, updateFunc) can be used to define new options for + CodeMirror. The updateFunc will be called with the + editor instance and the new value when an editor is initialized, + and whenever the option is modified + through setOption.

    +

    Add-ons

    The lib/util directory in the distribution @@ -917,6 +917,14 @@

    Add-ons

    on searchcursor.js, and will make use of openDialog when available to make prompting for search queries less ugly. +
    matchbrackets.js
    +
    Defines an option matchBrackets which, when set + to true, causes matching brackets to be highlighted whenever the + cursor is next to them. It also adds a + method matchBrackets that forces this to happen + once, and a method findMatchingBracket that can be + used to run the bracket-finding algorithm that this uses + internally.
    foldcode.js
    Helps with code folding. See the demo for an example. diff --git a/lib/codemirror.js b/lib/codemirror.js index afc15dd83e..54af4cad2e 100644 --- a/lib/codemirror.js +++ b/lib/codemirror.js @@ -21,19 +21,21 @@ window.CodeMirror = (function() { replaceSelection: operation(replaceSelection), focus: function(){window.focus(); focusInput(); onFocus(); fastPoll();}, setOption: function(option, value) { - var oldVal = options[option]; + if (options[option] == value) return; options[option] = value; if (option == "mode" || option == "indentUnit") loadMode(); else if (option == "readOnly" && value == "nocursor") {onBlur(); input.blur();} else if (option == "readOnly" && !value) {resetInput(true);} else if (option == "theme") themeChanged(); - else if (option == "lineWrapping" && oldVal != value) operation(wrappingChanged)(); + else if (option == "lineWrapping") operation(wrappingChanged)(); else if (option == "tabSize") updateDisplay(true); else if (option == "keyMap") keyMapChanged(); else if (option == "gutters" || option == "lineNumbers") setGuttersForLineNumbers(); if (option == "lineNumbers" || option == "gutters" || option == "firstLineNumber" || option == "theme" || option == "lineNumberFormatter") guttersChanged(); + if (optionHandlers.hasOwnProperty(option)) + optionHandlers[option](instance, value); }, getOption: function(option) {return options[option];}, undo: operation(undo), @@ -57,7 +59,6 @@ window.CodeMirror = (function() { history.time = 0; return {done: history.done.concat([]), undone: history.undone.concat([])}; }, - matchBrackets: operation(function(){matchBrackets(true);}), getTokenAt: operation(function(pos) { pos = clipPos(pos); return getLine(pos.line).getTokenAt(mode, getStateBefore(pos.line), options.tabSize, pos.ch); @@ -2012,46 +2013,6 @@ window.CodeMirror = (function() { }, options.cursorBlinkRate); } - var matching = {"(": ")>", ")": "(<", "[": "]>", "]": "[<", "{": "}>", "}": "{<"}; - function matchBrackets(autoclear) { - var head = sel.inverted ? sel.from : sel.to, line = getLine(head.line), pos = head.ch - 1; - var match = (pos >= 0 && matching[line.text.charAt(pos)]) || matching[line.text.charAt(++pos)]; - if (!match) return; - var ch = match.charAt(0), forward = match.charAt(1) == ">", d = forward ? 1 : -1, st = line.styles; - for (var off = pos + 1, i = 0, e = st.length; i < e; i+=2) - if ((off -= st[i].length) <= 0) {var style = st[i+1]; break;} - - var stack = [line.text.charAt(pos)], re = /[(){}[\]]/; - function scan(line, from, to) { - if (!line.text) return; - var st = line.styles, pos = forward ? 0 : line.text.length - 1, cur; - for (var i = forward ? 0 : st.length - 2, e = forward ? st.length : -2; i != e; i += 2*d) { - var text = st[i]; - if (st[i+1] != style) {pos += d * text.length; continue;} - for (var j = forward ? 0 : text.length - 1, te = forward ? text.length : -1; j != te; j += d, pos+=d) { - if (pos >= from && pos < to && re.test(cur = text.charAt(j))) { - var match = matching[cur]; - if (match.charAt(1) == ">" == forward) stack.push(cur); - else if (stack.pop() != match.charAt(0)) return {pos: pos, match: false}; - else if (!stack.length) return {pos: pos, match: true}; - } - } - } - } - for (var i = head.line, e = forward ? Math.min(i + 100, doc.size) : Math.max(-1, i - 100); i != e; i+=d) { - var line = getLine(i), first = i == head.line; - var found = scan(line, first && forward ? pos + 1 : 0, first && !forward ? pos : line.text.length); - if (found) break; - } - if (!found) found = {pos: null, match: false}; - var style = found.match ? "CodeMirror-matchingbracket" : "CodeMirror-nonmatchingbracket"; - var one = markText({line: head.line, ch: pos}, {line: head.line, ch: pos+1}, style), - two = found.pos != null && markText({line: i, ch: found.pos}, {line: i, ch: found.pos + 1}, style); - var clear = operation(function(){one.clear(); two && two.clear();}); - if (autoclear) setTimeout(clear, 800); - else bracketHighlighted = clear; - } - // Finds the line to start with when starting a parse. Tries to // find a line with a stateAfter, so that it can start with a // valid state. If that fails, it returns the line with the @@ -2168,11 +2129,6 @@ window.CodeMirror = (function() { (updateInput === true || (updateInput !== false && selectionChanged))) resetInput(userSelChange); - if (selectionChanged && options.matchBrackets) - setTimeout(operation(function() { - if (bracketHighlighted) {bracketHighlighted(); bracketHighlighted = null;} - if (posEq(sel.from, sel.to)) matchBrackets(false); - }), 20); var sc = selectionChanged, cbs = delayedCallbacks; // these can be reset by callbacks delayedCallbacks = null; if (textChanged) @@ -2200,6 +2156,9 @@ window.CodeMirror = (function() { if (extensions.propertyIsEnumerable(ext) && !instance.propertyIsEnumerable(ext)) instance[ext] = extensions[ext]; + for (var opt in optionHandlers) + if (optionHandlers.propertyIsEnumerable(opt)) + optionHandlers[opt](instance, options[opt]); return instance; } // (end of function CodeMirror) @@ -2225,7 +2184,6 @@ window.CodeMirror = (function() { firstLineNumber: 1, readOnly: false, dragDrop: true, - matchBrackets: false, cursorBlinkRate: 530, workTime: 100, workDelay: 200, @@ -2284,6 +2242,11 @@ window.CodeMirror = (function() { CodeMirror.defineExtension = function(name, func) { extensions[name] = func; }; + var optionHandlers = CodeMirror.optionHandlers = {}; + CodeMirror.defineOption = function(name, deflt, handler) { + CodeMirror.defaults[name] = deflt; + optionHandlers[name] = handler; + }; var commands = CodeMirror.commands = { selectAll: function(cm) {cm.setSelection({line: 0, ch: 0}, {line: cm.lineCount() - 1});}, diff --git a/lib/util/matchbrackets.js b/lib/util/matchbrackets.js new file mode 100644 index 0000000000..52583e2f26 --- /dev/null +++ b/lib/util/matchbrackets.js @@ -0,0 +1,61 @@ +(function() { + var matching = {"(": ")>", ")": "(<", "[": "]>", "]": "[<", "{": "}>", "}": "{<"}; + function findMatchingBracket(cm) { + var cur = cm.getCursor(), line = cm.getLineHandle(cur.line), pos = cur.ch - 1; + var match = (pos >= 0 && matching[line.text.charAt(pos)]) || matching[line.text.charAt(++pos)]; + if (!match) return null; + var ch = match.charAt(0), forward = match.charAt(1) == ">", d = forward ? 1 : -1; + var style = cm.getTokenAt({line: cur.line, ch: pos + 1}).className; + + var stack = [line.text.charAt(pos)], re = /[(){}[\]]/; + function scan(line, lineNo, start) { + if (!line.text) return; + var pos = forward ? 0 : line.text.length - 1, end = forward ? line.text.length : -1; + if (start != null) pos = start + d; + for (; pos != end; pos += d) { + var ch = line.text.charAt(pos); + if (re.test(ch) && cm.getTokenAt({line: lineNo, ch: pos + 1}).className == style) { + var match = matching[ch]; + if (match.charAt(1) == ">" == forward) stack.push(ch); + else if (stack.pop() != match.charAt(0)) return {pos: pos, match: false}; + else if (!stack.length) return {pos: pos, match: true}; + } + } + } + for (var i = cur.line, found, e = forward ? Math.min(i + 100, cm.lineCount()) : Math.max(-1, i - 100); i != e; i+=d) { + if (i == cur.line) found = scan(line, i, pos); + else found = scan(cm.getLineHandle(i), i); + if (found) break; + } + return {from: {line: cur.line, ch: pos}, to: found && {line: i, ch: found.pos}, match: found && found.match}; + } + + function matchBrackets(cm, autoclear) { + var found = findMatchingBracket(cm); + if (!found) return; + var style = found.match ? "CodeMirror-matchingbracket" : "CodeMirror-nonmatchingbracket"; + var one = cm.markText(found.from, {line: found.from.line, ch: found.from.ch + 1}, style); + var two = found.to && cm.markText(found.to, {line: found.to.line, ch: found.to.ch + 1}, style); + var clear = function() { + cm.operation(function() { one.clear(); two && two.clear(); }); + }; + if (autoclear) setTimeout(clear, 800); + else return clear; + } + + var currentlyHighlighted = null; + function doMatchBrackets(cm) { + setTimeout(cm.operation(function() { + if (currentlyHighlighted) {currentlyHighlighted(); currentlyHighlighted = null;} + if (!cm.somethingSelected()) currentlyHighlighted = matchBrackets(cm, false); + }), 20); + } + + CodeMirror.defineOption("matchBrackets", false, function(cm, val) { + if (val) cm.connect("cursorActivity", doMatchBrackets); + else cm.disconnect("cursorActivity", doMatchBrackets); + }); + + CodeMirror.defineExtension("matchBrackets", function() {matchBrackets(this, true);}); + CodeMirror.defineExtension("findMatchingBracket", function(){return findMatchingBracket(this);}); +})(); diff --git a/mode/clike/index.html b/mode/clike/index.html index 90a5fc105b..ca10c4bc23 100644 --- a/mode/clike/index.html +++ b/mode/clike/index.html @@ -5,6 +5,7 @@ CodeMirror: C-like mode + diff --git a/mode/clike/scala.html b/mode/clike/scala.html index a5aba99cac..39115967ab 100644 --- a/mode/clike/scala.html +++ b/mode/clike/scala.html @@ -6,6 +6,7 @@ + diff --git a/mode/gfm/index.html b/mode/gfm/index.html index d0214c17d6..08c8b22791 100644 --- a/mode/gfm/index.html +++ b/mode/gfm/index.html @@ -39,7 +39,6 @@

    CodeMirror: GFM mode

    var editor = CodeMirror.fromTextArea(document.getElementById("code"), { mode: 'gfm', lineNumbers: true, - matchBrackets: true, theme: "default" }); diff --git a/mode/go/index.html b/mode/go/index.html index 24b1cb9db0..1a9ef53852 100644 --- a/mode/go/index.html +++ b/mode/go/index.html @@ -6,6 +6,7 @@ + diff --git a/mode/groovy/index.html b/mode/groovy/index.html index 0393362a47..d0d76bfa90 100644 --- a/mode/groovy/index.html +++ b/mode/groovy/index.html @@ -5,6 +5,7 @@ CodeMirror: Groovy mode + diff --git a/mode/haskell/index.html b/mode/haskell/index.html index 963430f3f6..b304a27682 100644 --- a/mode/haskell/index.html +++ b/mode/haskell/index.html @@ -5,6 +5,7 @@ CodeMirror: Haskell mode + diff --git a/mode/haxe/index.html b/mode/haxe/index.html index ee5983ae0d..1125741ad5 100644 --- a/mode/haxe/index.html +++ b/mode/haxe/index.html @@ -80,7 +80,6 @@

    CodeMirror: Haxe mode

    + diff --git a/mode/less/index.html b/mode/less/index.html index 69467bbb2d..dd2f588fd9 100644 --- a/mode/less/index.html +++ b/mode/less/index.html @@ -5,6 +5,7 @@ CodeMirror: LESS mode + diff --git a/mode/lua/index.html b/mode/lua/index.html index 6e984f4149..df83f9b47d 100644 --- a/mode/lua/index.html +++ b/mode/lua/index.html @@ -4,6 +4,7 @@ CodeMirror: Lua mode + diff --git a/mode/markdown/index.html b/mode/markdown/index.html index 59e79f6fd0..f5bd1f7f88 100644 --- a/mode/markdown/index.html +++ b/mode/markdown/index.html @@ -328,7 +328,6 @@

    CodeMirror: Markdown mode

    var editor = CodeMirror.fromTextArea(document.getElementById("code"), { mode: 'markdown', lineNumbers: true, - matchBrackets: true, theme: "default" }); diff --git a/mode/mysql/index.html b/mode/mysql/index.html index bbac836bd1..0403a9699e 100644 --- a/mode/mysql/index.html +++ b/mode/mysql/index.html @@ -31,8 +31,7 @@

    CodeMirror: MySQL mode

    diff --git a/mode/ocaml/index.html b/mode/ocaml/index.html index d286edc176..962fa29eb1 100644 --- a/mode/ocaml/index.html +++ b/mode/ocaml/index.html @@ -10,6 +10,7 @@ +

    CodeMirror: OCaml mode

    diff --git a/mode/pascal/index.html b/mode/pascal/index.html index ffd3c74172..b3016afb10 100644 --- a/mode/pascal/index.html +++ b/mode/pascal/index.html @@ -39,7 +39,6 @@

    CodeMirror: Pascal mode

    diff --git a/mode/perl/index.html b/mode/perl/index.html index 8f0b38da2a..13c7af64d3 100644 --- a/mode/perl/index.html +++ b/mode/perl/index.html @@ -53,8 +53,7 @@

    CodeMirror: Perl mode

    diff --git a/mode/php/index.html b/mode/php/index.html index cd189a4dec..9ac66ccb0f 100644 --- a/mode/php/index.html +++ b/mode/php/index.html @@ -5,6 +5,7 @@ CodeMirror: PHP mode + diff --git a/mode/pig/index.html b/mode/pig/index.html index 02b0368a10..1b0c60267f 100644 --- a/mode/pig/index.html +++ b/mode/pig/index.html @@ -28,7 +28,6 @@

    CodeMirror: Pig Latin mode

    + diff --git a/mode/rpm/changes/index.html b/mode/rpm/changes/index.html index 3f3dccc871..e0e2d8778b 100644 --- a/mode/rpm/changes/index.html +++ b/mode/rpm/changes/index.html @@ -44,8 +44,7 @@

    CodeMirror: RPM changes mode

    mode: {name: "changes"}, lineNumbers: true, indentUnit: 4, - tabMode: "shift", - matchBrackets: true + tabMode: "shift" }); diff --git a/mode/rpm/spec/index.html b/mode/rpm/spec/index.html index 23aef9841c..8be98b63ec 100644 --- a/mode/rpm/spec/index.html +++ b/mode/rpm/spec/index.html @@ -90,8 +90,7 @@

    CodeMirror: RPM spec mode

    var editor = CodeMirror.fromTextArea(document.getElementById("code"), { mode: {name: "spec"}, lineNumbers: true, - indentUnit: 4, - matchBrackets: true + indentUnit: 4 }); diff --git a/mode/ruby/index.html b/mode/ruby/index.html index 282115b62a..f226289d7d 100644 --- a/mode/ruby/index.html +++ b/mode/ruby/index.html @@ -5,6 +5,7 @@ CodeMirror: Ruby mode + +

    CodeMirror: Shell mode

    diff --git a/mode/smalltalk/index.html b/mode/smalltalk/index.html index 9a48ec19fc..690b560fd7 100644 --- a/mode/smalltalk/index.html +++ b/mode/smalltalk/index.html @@ -5,6 +5,7 @@ CodeMirror: Smalltalk mode + diff --git a/mode/tiddlywiki/index.html b/mode/tiddlywiki/index.html index 40c2dff5b3..89ae85892b 100644 --- a/mode/tiddlywiki/index.html +++ b/mode/tiddlywiki/index.html @@ -5,6 +5,7 @@ CodeMirror: TiddlyWiki mode + diff --git a/mode/tiki/index.html b/mode/tiki/index.html index 3579cff508..7b85a44a7e 100644 --- a/mode/tiki/index.html +++ b/mode/tiki/index.html @@ -73,9 +73,7 @@

    CodeMirror: Tiki wiki mode

    diff --git a/mode/vb/index.html b/mode/vb/index.html index af313419be..55dfabfa56 100644 --- a/mode/vb/index.html +++ b/mode/vb/index.html @@ -65,7 +65,6 @@

    CodeMirror: VB.NET mode

    function init() { editor = CodeMirror.fromTextArea(document.getElementById("solution"), { lineNumbers: true, - matchBrackets: true, mode: "text/x-vb", readOnly: false, tabMode: "shift" diff --git a/mode/vbscript/index.html b/mode/vbscript/index.html index e7375fb0fc..8c86f9ef94 100644 --- a/mode/vbscript/index.html +++ b/mode/vbscript/index.html @@ -32,8 +32,7 @@

    CodeMirror: VBScript mode

    diff --git a/mode/velocity/index.html b/mode/velocity/index.html index 1e25c651e7..fb59cb590d 100644 --- a/mode/velocity/index.html +++ b/mode/velocity/index.html @@ -90,7 +90,6 @@

    CodeMirror: Velocity mode

    From 6e190f7e4c7c84ea29b040afe644f8e8c5b3ab8f Mon Sep 17 00:00:00 2001 From: Marijn Haverbeke Date: Tue, 28 Aug 2012 16:53:26 +0200 Subject: [PATCH 0034/5780] Emergency code reorg to please linter A *real* cleanup / reorder is becoming painfully needed, but this'll at least make the tests pass. --- lib/codemirror.js | 6 ++++-- 1 file changed, 4 insertions(+), 2 deletions(-) diff --git a/lib/codemirror.js b/lib/codemirror.js index 54af4cad2e..c1db61d8ce 100644 --- a/lib/codemirror.js +++ b/lib/codemirror.js @@ -8,6 +8,8 @@ window.CodeMirror = (function() { // This is the function that produces an editor instance. Its // closure is used to store the editor state. function CodeMirror(place, givenOptions) { + var history, overwrite = false; + function isLine(l) {return l >= 0 && l < doc.size;} // The instance object that we'll return. Mostly calls out to // local functions in the CodeMirror function. Some do some extra @@ -280,7 +282,7 @@ window.CodeMirror = (function() { // Selection-related flags. shiftSelecting obviously tracks // whether the user is holding shift. var shiftSelecting, lastClick, lastDoubleClick, lastScrollTop = 0, lastScrollLeft = 0, draggingText, - overwrite = false, suppressEdits = false; + suppressEdits = false; // Variables used by startOperation/endOperation to track what // happened during the operation. var updateInput, userSelChange, changes, textChanged, selectionChanged, leaveInputAlone; @@ -299,7 +301,7 @@ window.CodeMirror = (function() { // Initialize the content. operation(function(){setValue(options.value || ""); updateInput = false;})(); - var history = new History(); + history = new History(); // Register our event handlers. connect(scroller, "mousedown", operation(onMouseDown)); From 2cb154522b89e3efa72ac2fc57e87ad68c66d51f Mon Sep 17 00:00:00 2001 From: Marijn Haverbeke Date: Tue, 28 Aug 2012 16:59:53 +0200 Subject: [PATCH 0035/5780] Another fix for the z-order selection problem The previous one broke line numbers --- lib/codemirror.js | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/lib/codemirror.js b/lib/codemirror.js index c1db61d8ce..558f7c59d4 100644 --- a/lib/codemirror.js +++ b/lib/codemirror.js @@ -229,7 +229,7 @@ window.CodeMirror = (function() { var scrollbarV = elt("div", [elt("div", null, null, "width: 1px")], "CodeMirror-vscrollbar"); var scrollbarFiller = elt("div", null, "CodeMirror-scrollbar-filler"); // DIVs containing the selection and the actual code - var lineDiv = elt("div"), selectionDiv = elt("div", null, null, "position: relative; z-index: -1"); + var lineDiv = elt("div"), selectionDiv = elt("div", null, null, "position: relative; z-index: 0"); // Blinky cursor, and element used to ensure cursor fits at the end of a line var cursor = elt("pre", "\u00a0", "CodeMirror-cursor"); // Secondary cursor, shown when on a 'jump' in bi-directional text @@ -239,7 +239,7 @@ window.CodeMirror = (function() { // Wraps everything that needs to exist inside the vertically-padded coordinate system var lineSpace = elt("div", [measure, cursor, otherCursor, selectionDiv, lineDiv], null, "position: relative"); // Moved around its parent to cover visible view - var mover = elt("div", [elt("div", [lineSpace], "CodeMirror-lines")], null, "position: relative; z-index: 0"); + var mover = elt("div", [elt("div", [lineSpace], "CodeMirror-lines")], null, "position: relative"); var gutters = elt("div", null, "CodeMirror-gutters"), lineGutter; // Set to the height of the text, causes scrolling var sizer = elt("div", [mover], "CodeMirror-sizer"); From 8d0c522b7fe29bb205c90f5bd1617a4e83e7c2e4 Mon Sep 17 00:00:00 2001 From: Marijn Haverbeke Date: Tue, 28 Aug 2012 17:01:17 +0200 Subject: [PATCH 0036/5780] Prevent selection marker from 'bleeding' into adjacent lines By clipping its top/bot to the borders of the outer div --- lib/codemirror.js | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/lib/codemirror.js b/lib/codemirror.js index 558f7c59d4..46d3554a21 100644 --- a/lib/codemirror.js +++ b/lib/codemirror.js @@ -1815,9 +1815,9 @@ window.CodeMirror = (function() { anchor.parentNode.removeChild(anchor.previousSibling); } } - var top = anchor.offsetTop; + var top = Math.max(0, anchor.offsetTop); anchor.style.verticalAlign = "bottom"; - var bottom = anchor.offsetTop + anchor.offsetHeight; + var bottom = Math.min(anchor.offsetTop + anchor.offsetHeight, pre.offsetHeight); if (atEnd) left = right; var memo = {ch: ch, text: line.text, width: wrapper.clientWidth, From b1801379127722fc71b394f21764d1f662c7c12a Mon Sep 17 00:00:00 2001 From: Marijn Haverbeke Date: Wed, 29 Aug 2012 10:01:40 +0200 Subject: [PATCH 0037/5780] Fix bug in activeline.html demo --- demo/activeline.html | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/demo/activeline.html b/demo/activeline.html index 70e5836666..1a3ec586f3 100644 --- a/demo/activeline.html +++ b/demo/activeline.html @@ -60,7 +60,7 @@

    CodeMirror: Active Line Demo

    lineNumbers: true, lineWrapping: true }); -var hlLine = editor.setLineClass(0, "activeline"); +var hlLine = editor.setLineClass(0, null, "activeline"); editor.connect("cursorActivity", function() { editor.setLineClass(hlLine, null, null); hlLine = editor.setLineClass(editor.getCursor().line, null, "activeline"); From 577e707b731e8db8884608ba12be35923cac56bf Mon Sep 17 00:00:00 2001 From: Marijn Haverbeke Date: Wed, 29 Aug 2012 10:38:16 +0200 Subject: [PATCH 0038/5780] More rigorous z-index ordering of editor components Closes #760 and fixes line backgrounds ending up behind editor background. Discussion in issue #762 --- lib/codemirror.css | 12 ++++++++++-- lib/codemirror.js | 4 ++-- 2 files changed, 12 insertions(+), 4 deletions(-) diff --git a/lib/codemirror.css b/lib/codemirror.css index dd2e71b80a..3edff853c1 100644 --- a/lib/codemirror.css +++ b/lib/codemirror.css @@ -146,12 +146,12 @@ div.CodeMirror span.CodeMirror-nonmatchingbracket {color: #f22;} .CodeMirror-gutters { position: absolute; left: 0; top: 0; height: 100%; - z-index: 1; + z-index: 3; } .CodeMirror-gutter-elt { position: absolute; cursor: default; - z-index: 2; + z-index: 4; } .CodeMirror-lines { @@ -169,12 +169,20 @@ div.CodeMirror span.CodeMirror-nonmatchingbracket {color: #f22;} word-wrap: normal; line-height: inherit; color: inherit; + z-index: 2; + position: relative; } .CodeMirror-wrap pre { word-wrap: break-word; white-space: pre-wrap; word-break: normal; } +.CodeMirror-linebackground { + position: absolute; + left: 0; right: 0; top: 0; bottom: 0; + z-index: 0; +} + .CodeMirror-wrap .CodeMirror-scroll { overflow-x: hidden; } diff --git a/lib/codemirror.js b/lib/codemirror.js index 46d3554a21..01c7f67382 100644 --- a/lib/codemirror.js +++ b/lib/codemirror.js @@ -229,7 +229,7 @@ window.CodeMirror = (function() { var scrollbarV = elt("div", [elt("div", null, null, "width: 1px")], "CodeMirror-vscrollbar"); var scrollbarFiller = elt("div", null, "CodeMirror-scrollbar-filler"); // DIVs containing the selection and the actual code - var lineDiv = elt("div"), selectionDiv = elt("div", null, null, "position: relative; z-index: 0"); + var lineDiv = elt("div"), selectionDiv = elt("div", null, null, "position: relative; z-index: 1"); // Blinky cursor, and element used to ensure cursor fits at the end of a line var cursor = elt("pre", "\u00a0", "CodeMirror-cursor"); // Secondary cursor, shown when on a 'jump' in bi-directional text @@ -1233,7 +1233,7 @@ window.CodeMirror = (function() { } // Kludge to make sure the styled element lies behind the selection (by z-index) if (line.bgClassName) - inside.push(elt("pre", "\u00a0", line.bgClassName, "position: absolute; left: 0; right: 0; top: 0; bottom: 0; z-index: -2")); + inside.push(elt("div", "\u00a0", line.bgClassName + " CodeMirror-linebackground")); inside.push(lineElement); lineElement = elt("div", inside, null, "position: relative"); } From 4197adb94bf186cab7afab0dd345c930b51e4a5d Mon Sep 17 00:00:00 2001 From: Marijn Haverbeke Date: Wed, 29 Aug 2012 11:34:18 +0200 Subject: [PATCH 0039/5780] Fix RTL cursor measurement on Opera Closes #763 --- lib/codemirror.js | 15 ++++++++++----- 1 file changed, 10 insertions(+), 5 deletions(-) diff --git a/lib/codemirror.js b/lib/codemirror.js index 01c7f67382..a92b7a6734 100644 --- a/lib/codemirror.js +++ b/lib/codemirror.js @@ -1799,7 +1799,7 @@ window.CodeMirror = (function() { // to get the real line height (in case there tokens on the line // with bigger fonts) anchor.style.verticalAlign = "top"; - var left = anchor.offsetLeft, right = anchor.offsetLeft + anchor.offsetWidth; + var left = anchor.offsetLeft, right = left + anchor.offsetWidth; if (ie) { // In IE, verticalAlign does not influence offsetTop, unless // the element is an inline-block. Unfortunately, inline @@ -1819,7 +1819,7 @@ window.CodeMirror = (function() { anchor.style.verticalAlign = "bottom"; var bottom = Math.min(anchor.offsetTop + anchor.offsetHeight, pre.offsetHeight); if (atEnd) left = right; - + var memo = {ch: ch, text: line.text, width: wrapper.clientWidth, top: top, bottom: bottom, left: left, right: right}; if (measureLineMemo.length == 8) measureLineMemo[++measureLineMemoPos % 8] = memo; @@ -1856,14 +1856,19 @@ window.CodeMirror = (function() { if (!linedir && last.level % 2 != linedir && ch == lineObj.text.length) return get(last.from, true); for (var i = 0; i < order.length; ++i) { - var part = order[i], rtl = part.level % 2; + var part = order[i], rtl = part.level % 2, nb, here; if (part.from < ch && part.to > ch) return get(ch, rtl); var left = rtl ? part.to : part.from, right = rtl ? part.from : part.to; if (left == ch) { - var here = get(rtl ? ch - 1 : ch); + // Opera return bogus offsets and widths for edges where the + // direction flips, but only for the side with the lower + // level. + if (opera && i && part.level < (nb = order[i-1]).level) here = get(nb.level % 2 ? nb.from : nb.to - 1, true); + else here = get(rtl ? ch - 1 : ch); if (rtl == linedir) main = here; else other = here; } else if (right == ch) { - var here = get(rtl ? ch : ch - 1, true); + if (opera && i < order.length - 1 && part.level < (nb = order[i+1]).level) here = get(nb.level % 2 ? nb.to - 1 : nb.from); + else here = get(rtl ? ch : ch - 1, true); if (rtl == linedir) main = here; else other = here; } } From b6345c4da984df60c7f77a25515cc3a40e79fe64 Mon Sep 17 00:00:00 2001 From: Marijn Haverbeke Date: Wed, 29 Aug 2012 11:48:14 +0200 Subject: [PATCH 0040/5780] Fix bug in new matchbrackets implementation It was passing undefined to setTimeout. --- lib/util/matchbrackets.js | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/lib/util/matchbrackets.js b/lib/util/matchbrackets.js index 52583e2f26..f56930c281 100644 --- a/lib/util/matchbrackets.js +++ b/lib/util/matchbrackets.js @@ -45,10 +45,10 @@ var currentlyHighlighted = null; function doMatchBrackets(cm) { - setTimeout(cm.operation(function() { + cm.operation(function() { if (currentlyHighlighted) {currentlyHighlighted(); currentlyHighlighted = null;} if (!cm.somethingSelected()) currentlyHighlighted = matchBrackets(cm, false); - }), 20); + }); } CodeMirror.defineOption("matchBrackets", false, function(cm, val) { From a08e2487da2f2cf56099149ca17e399a2a13c7c9 Mon Sep 17 00:00:00 2001 From: Marijn Haverbeke Date: Wed, 29 Aug 2012 11:48:36 +0200 Subject: [PATCH 0041/5780] Make tests pass on IE8 again Closes #765 --- lib/codemirror.js | 11 ++++++----- 1 file changed, 6 insertions(+), 5 deletions(-) diff --git a/lib/codemirror.js b/lib/codemirror.js index a92b7a6734..28a513dcf3 100644 --- a/lib/codemirror.js +++ b/lib/codemirror.js @@ -1267,7 +1267,7 @@ window.CodeMirror = (function() { var gElt = gutters.appendChild(elt("div", null, "CodeMirror-gutter " + gutterClass)); if (gutterClass == "CodeMirror-linenumbers") { lineGutter = gElt; - gElt.style.width = currentLineNumberWidth + "px"; + gElt.style.width = (currentLineNumberWidth || 1) + "px"; } } gutters.style.display = i ? "" : "none"; @@ -1860,14 +1860,15 @@ window.CodeMirror = (function() { if (part.from < ch && part.to > ch) return get(ch, rtl); var left = rtl ? part.to : part.from, right = rtl ? part.from : part.to; if (left == ch) { - // Opera return bogus offsets and widths for edges where the - // direction flips, but only for the side with the lower + // Opera and IE return bogus offsets and widths for edges + // where the direction flips, but only for the side with the + // lower level. So we try to use the side with the higher // level. - if (opera && i && part.level < (nb = order[i-1]).level) here = get(nb.level % 2 ? nb.from : nb.to - 1, true); + if (i && part.level < (nb = order[i-1]).level) here = get(nb.level % 2 ? nb.from : nb.to - 1, true); else here = get(rtl ? ch - 1 : ch); if (rtl == linedir) main = here; else other = here; } else if (right == ch) { - if (opera && i < order.length - 1 && part.level < (nb = order[i+1]).level) here = get(nb.level % 2 ? nb.to - 1 : nb.from); + if (i < order.length - 1 && part.level < (nb = order[i+1]).level) here = get(nb.level % 2 ? nb.to - 1 : nb.from); else here = get(rtl ? ch : ch - 1, true); if (rtl == linedir) main = here; else other = here; } From 2151b96b3291496a6a2debffa2de188ccb2ae362 Mon Sep 17 00:00:00 2001 From: Marijn Haverbeke Date: Wed, 29 Aug 2012 13:20:30 +0200 Subject: [PATCH 0042/5780] Resolve some horizontal scrolling confusion Fix bad rules in themes, measure width correctly. --- lib/codemirror.css | 7 ++++++- lib/codemirror.js | 7 +++---- test/test.js | 11 +++++++++-- theme/ambiance.css | 4 ---- theme/lesser-dark.css | 4 ++-- theme/rubyblue.css | 2 +- 6 files changed, 21 insertions(+), 14 deletions(-) diff --git a/lib/codemirror.css b/lib/codemirror.css index 3edff853c1..15272a0373 100644 --- a/lib/codemirror.css +++ b/lib/codemirror.css @@ -33,7 +33,7 @@ padding: 0 4px; } .CodeMirror-linenumber { - padding: 0 4px; + padding: 0 3px 0 5px; min-width: 20px; text-align: right; color: #999; @@ -148,6 +148,10 @@ div.CodeMirror span.CodeMirror-nonmatchingbracket {color: #f22;} height: 100%; z-index: 3; } +.CodeMirror-gutter { + height: 100%; + float: left; +} .CodeMirror-gutter-elt { position: absolute; cursor: default; @@ -193,6 +197,7 @@ div.CodeMirror span.CodeMirror-nonmatchingbracket {color: #f22;} overflow: hidden; visibility: hidden; } +.CodeMirror-measure pre { position: static; } .CodeMirror pre.CodeMirror-cursor { z-index: 4; diff --git a/lib/codemirror.js b/lib/codemirror.js index 28a513dcf3..0e18076afc 100644 --- a/lib/codemirror.js +++ b/lib/codemirror.js @@ -375,7 +375,7 @@ window.CodeMirror = (function() { } function lineNumberLeftPos() { - return scroller.getBoundingClientRect().left - sizer.getBoundingClientRect().left + lineGutter.offsetLeft - gutters.offsetWidth; + return scroller.getBoundingClientRect().left - sizer.getBoundingClientRect().left + lineGutter.offsetLeft; } function alignLineNumbers() { if (!options.lineNumbers) return; @@ -1074,7 +1074,7 @@ window.CodeMirror = (function() { } if (changes && changes !== true && maybeUpdateLineNumberWidth()) changes = true; - mover.style.marginLeft = scrollbarH.style.left = gutters.offsetWidth + "px"; + sizer.style.marginLeft = scrollbarH.style.left = gutters.offsetWidth + "px"; // Used to determine which lines need their line numbers updated var positionsChangedFrom = changes === true ? 0 : Infinity; if (options.lineNumbers && changes && changes !== true) @@ -2118,8 +2118,7 @@ window.CodeMirror = (function() { if (updateMaxLine) computeMaxLength(); if (maxLineChanged && !options.lineWrapping) { var width = measureLine(maxLine, maxLine.text.length).left; - if (!ie_lt8) - sizer.style.minWidth = (width + scrollerCutOff) + "px"; + sizer.style.minWidth = (width + 3) + "px"; maxLineChanged = false; } var newScrollPos, updated; diff --git a/test/test.js b/test/test.js index 545ca6766e..f66513f07b 100644 --- a/test/test.js +++ b/test/test.js @@ -368,7 +368,7 @@ testCM("selectionPos", function(cm) { } } is(sawTop && sawBottom && sawMiddle, "all parts"); -}, null, ie_lt8); +}, null); testCM("restoreHistory", function(cm) { cm.setValue("abc\ndef"); @@ -511,7 +511,7 @@ testCM("measureEndOfLine", function(cm) { is(endPos.left > w - 20, "not at right"); endPos = cm.charCoords({line: 0, ch: 18}); eqPos(cm.coordsChar({left: endPos.left, top: endPos.top + 5}), {line: 0, ch: 18}); -}, {mode: "text/html", value: "", lineWrapping: true}, ie_lt8); +}, {mode: "text/html", value: "", lineWrapping: true}); testCM("scrollVerticallyAndHorizontally", function(cm) { cm.setSize(100, 100); @@ -732,3 +732,10 @@ testCM("lineChangeEvents", function(cm) { for (var i = 0; i < log.length; ++i) eq(log[i], want[i]); }); + +testCM("scrollEntirelyToRight", function(cm) { + addDoc(cm, 500, 2); + cm.setCursor({line: 0, ch: 500}); + var wrap = cm.getWrapperElement(), cur = byClassName(wrap, "CodeMirror-cursor")[0]; + is(wrap.getBoundingClientRect().right > cur.getBoundingClientRect().left); +}); diff --git a/theme/ambiance.css b/theme/ambiance.css index 5c5dcdc5ee..9902ecad70 100644 --- a/theme/ambiance.css +++ b/theme/ambiance.css @@ -62,10 +62,6 @@ padding: 0 5px; } -.cm-s-ambiance .CodeMirror-lines { - -} - .cm-s-ambiance .CodeMirror-lines .CodeMirror-cursor { border-left: 1px solid #7991E8; } diff --git a/theme/lesser-dark.css b/theme/lesser-dark.css index de624501bc..509b1e804e 100644 --- a/theme/lesser-dark.css +++ b/theme/lesser-dark.css @@ -12,11 +12,11 @@ Ported to CodeMirror by Peter Kroon .cm-s-lesser-dark { background: #262626; color: #EBEFE7; text-shadow: 0 -1px 1px #262626; } .cm-s-lesser-dark div.CodeMirror-selected {background: #45443B !important;} /* 33322B*/ .cm-s-lesser-dark .CodeMirror-cursor { border-left: 1px solid white !important; } -.cm-s-lesser-dark .CodeMirror-lines { margin-left:3px; margin-right:3px; }/*editable code holder*/ +.cm-s-lesser-dark pre { padding: 0 8px; }/*editable code holder*/ div.CodeMirror span.CodeMirror-matchingbracket { color: #7EFC7E; }/*65FC65*/ -.cm-s-lesser-dark .CodeMirror-gutters { background: #262626; border-right:1px solid #aaa; padding-right:3px; min-width:2.5em; } +.cm-s-lesser-dark .CodeMirror-gutters { background: #262626; border-right:1px solid #aaa; } .cm-s-lesser-dark .CodeMirror-linenumber { color: #777; } .cm-s-lesser-dark span.cm-keyword { color: #599eff; } diff --git a/theme/rubyblue.css b/theme/rubyblue.css index 495418bbb1..47e0b69e08 100644 --- a/theme/rubyblue.css +++ b/theme/rubyblue.css @@ -2,7 +2,7 @@ .cm-s-rubyblue { background: #112435; color: white; } .cm-s-rubyblue div.CodeMirror-selected { background: #38566F !important; } -.cm-s-rubyblue .CodeMirror-gutters { background: #1F4661; border-right: 7px solid #3E7087; min-width:2.5em; } +.cm-s-rubyblue .CodeMirror-gutters { background: #1F4661; border-right: 7px solid #3E7087; } .cm-s-rubyblue .CodeMirror-linenumber { color: white; } .cm-s-rubyblue .CodeMirror-cursor { border-left: 1px solid white !important; } From 59e788f09d6a3898e3ebc697dafd07ded804d374 Mon Sep 17 00:00:00 2001 From: Marijn Haverbeke Date: Wed, 29 Aug 2012 13:24:28 +0200 Subject: [PATCH 0043/5780] Partially fix things on IE7, downgrade IE7 to half-supported Closes #764 --- index.html | 9 ++++++--- lib/codemirror.js | 3 +++ 2 files changed, 9 insertions(+), 3 deletions(-) diff --git a/index.html b/index.html index 6d074dd334..8e3797210b 100644 --- a/index.html +++ b/index.html @@ -205,12 +205,15 @@

    Supported browsers

    • Firefox 2 or higher
    • Chrome, any version
    • -
    • Safari 3 or higher
    • +
    • Safari 5.2 or higher
    • Opera 9 or higher (with some key-handling problems on OS X)
    • -
    • Internet Explorer 7 or higher in standards mode
      - (So not quirks mode. But quasi-standards mode with a +
    • Internet Explorer 8 or higher in standards mode
      + (Not quirks mode. But quasi-standards mode with a transitional doctype is also flaky. <!doctype html> is recommended.)
    • +
    • Internet Explorer 7 (standards mode) is usable, but buggy. It + has a z-index + bug that prevents CodeMirror from working properly.

    I am not actively testing against every new browser release, and diff --git a/lib/codemirror.js b/lib/codemirror.js index 0e18076afc..17546493b1 100644 --- a/lib/codemirror.js +++ b/lib/codemirror.js @@ -249,6 +249,8 @@ window.CodeMirror = (function() { // The element in which the editor lives. var wrapper = elt("div", [gutters, inputDiv, scrollbarH, scrollbarV, scrollbarFiller, scroller], "CodeMirror" + (options.lineWrapping ? " CodeMirror-wrap" : "")); + // Work around IE7 z-index and scrollable div bugs + if (ie_lt8) { gutters.style.zIndex = -1; scroller.style.position = "relative"; } wrapper.CodeMirror = instance; if (place.appendChild) place.appendChild(wrapper); else place(wrapper); @@ -1236,6 +1238,7 @@ window.CodeMirror = (function() { inside.push(elt("div", "\u00a0", line.bgClassName + " CodeMirror-linebackground")); inside.push(lineElement); lineElement = elt("div", inside, null, "position: relative"); + if (ie_lt8) lineElement.style.zIndex = 2; } } lineDiv.insertBefore(lineElement, curNode); From 42f23ed9dcddaef01f5ad16c5eb61801414a5e6a Mon Sep 17 00:00:00 2001 From: Marijn Haverbeke Date: Wed, 29 Aug 2012 13:37:25 +0200 Subject: [PATCH 0044/5780] Make StringStream.match return null when the match isn't at current position Closes #761 --- lib/codemirror.js | 1 + 1 file changed, 1 insertion(+) diff --git a/lib/codemirror.js b/lib/codemirror.js index 17546493b1..df50cbeaeb 100644 --- a/lib/codemirror.js +++ b/lib/codemirror.js @@ -2519,6 +2519,7 @@ window.CodeMirror = (function() { } } else { var match = this.string.slice(this.pos).match(pattern); + if (match && match.index > 0) return null; if (match && consume !== false) this.pos += match[0].length; return match; } From 19b9cc473d6deb1826ffa33b5c3194d1d442b50f Mon Sep 17 00:00:00 2001 From: Marijn Haverbeke Date: Wed, 29 Aug 2012 13:46:01 +0200 Subject: [PATCH 0045/5780] Always force a re-highlight when the 'mode' option is set Closes #759 --- lib/codemirror.js | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/lib/codemirror.js b/lib/codemirror.js index df50cbeaeb..8423b8093a 100644 --- a/lib/codemirror.js +++ b/lib/codemirror.js @@ -23,7 +23,7 @@ window.CodeMirror = (function() { replaceSelection: operation(replaceSelection), focus: function(){window.focus(); focusInput(); onFocus(); fastPoll();}, setOption: function(option, value) { - if (options[option] == value) return; + if (options[option] == value && option != "mode") return; options[option] = value; if (option == "mode" || option == "indentUnit") loadMode(); else if (option == "readOnly" && value == "nocursor") {onBlur(); input.blur();} From a71fda88216b95c528a1f224e7e34d4f747a802e Mon Sep 17 00:00:00 2001 From: Marijn Haverbeke Date: Wed, 29 Aug 2012 13:55:37 +0200 Subject: [PATCH 0046/5780] Fix XQuery demo loading codemirror.js from the website (And codemirror.css from a relative link.) Closes #756 --- mode/xquery/index.html | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/mode/xquery/index.html b/mode/xquery/index.html index 4364fe18a4..bea4ab4ac8 100644 --- a/mode/xquery/index.html +++ b/mode/xquery/index.html @@ -28,7 +28,7 @@ CodeMirror: XQuery mode - + From 796f4af6ae1f7a96c1d67391d2cf5269a3f4e956 Mon Sep 17 00:00:00 2001 From: Marijn Haverbeke Date: Wed, 29 Aug 2012 13:56:17 +0200 Subject: [PATCH 0047/5780] Ensure wrapper div stays scrolled to top-left Some things appear to trigger it to scroll, with very bad effects. --- lib/codemirror.js | 1 + 1 file changed, 1 insertion(+) diff --git a/lib/codemirror.js b/lib/codemirror.js index 8423b8093a..a65b6072c8 100644 --- a/lib/codemirror.js +++ b/lib/codemirror.js @@ -319,6 +319,7 @@ window.CodeMirror = (function() { connect(scrollbarV, "scroll", onScrollBarV); connect(scrollbarH, "mousedown", function() {if (focused) setTimeout(focusInput, 0);}); connect(scrollbarV, "mousedown", function() {if (focused) setTimeout(focusInput, 0);}); + connect(wrapper, "scroll", function() { wrapper.scrollTop = wrapper.scrollLeft = 0; }); connect(window, "resize", function resizeHandler() { if (wrapper.parentNode) updateDisplay(true); else disconnect(window, "resize", resizeHandler); From cb456e05ff25c2aa9788d317c619727b51cd46b6 Mon Sep 17 00:00:00 2001 From: Marijn Haverbeke Date: Wed, 29 Aug 2012 14:41:37 +0200 Subject: [PATCH 0048/5780] Make right-click select-all work on some browsers The hack is horrible, but it seems to do the trick. Also simplifies context-menu handling somewhat. Issue #755 --- lib/codemirror.js | 28 ++++++++++++++++++---------- 1 file changed, 18 insertions(+), 10 deletions(-) diff --git a/lib/codemirror.js b/lib/codemirror.js index a65b6072c8..cc62f4f937 100644 --- a/lib/codemirror.js +++ b/lib/codemirror.js @@ -287,7 +287,7 @@ window.CodeMirror = (function() { suppressEdits = false; // Variables used by startOperation/endOperation to track what // happened during the operation. - var updateInput, userSelChange, changes, textChanged, selectionChanged, leaveInputAlone; + var updateInput, userSelChange, changes, textChanged, selectionChanged; var delayedCallbacks; // Current visible range (may be bigger than the view window). var displayOffset = 0, showingFrom = 0, showingTo = 0, lastSizeC = 0; @@ -713,7 +713,6 @@ window.CodeMirror = (function() { focused = true; if (scroller.className.search(/\bCodeMirror-focused\b/) == -1) scroller.className += " CodeMirror-focused"; - if (!leaveInputAlone) resetInput(true); } slowPoll(); restartBlink(); @@ -979,7 +978,7 @@ window.CodeMirror = (function() { // supported or compatible enough yet to rely on.) var prevInput = ""; function readInput() { - if (leaveInputAlone || !focused || hasSelection(input) || options.readOnly) return false; + if (!focused || hasSelection(input) || options.readOnly) return false; var text = input.value; if (text == prevInput) return false; shiftSelecting = null; @@ -1978,6 +1977,7 @@ window.CodeMirror = (function() { try { x = e.clientX; y = e.clientY; } catch (e) { return null; } return coordsChar(x - space.left, y - space.top); } + var detectingSelectAll; function onContextMenu(e) { var pos = posFromMouse(e), scrollPos = scroller.scrollTop; if (!pos || opera) return; // Opera is difficult. @@ -1989,19 +1989,28 @@ window.CodeMirror = (function() { input.style.cssText = "position: fixed; width: 30px; height: 30px; top: " + (e.clientY - 5) + "px; left: " + (e.clientX - 5) + "px; z-index: 1000; background: white; outline: none;" + "border-width: 0; outline: none; overflow: hidden; opacity: .05; filter: alpha(opacity=5);"; - leaveInputAlone = true; - var val = input.value = getSelection(); focusInput(); selectInput(input); + clearTimeout(detectingSelectAll); function rehide() { - var newVal = splitLines(input.value).join("\n"); - if (newVal != val && !options.readOnly) operation(replaceSelection)(newVal, "end"); inputDiv.style.position = "relative"; input.style.cssText = oldCSS; if (ie_lt9) scrollbarV.scrollTop = scroller.scrollTop = scrollPos; - leaveInputAlone = false; resetInput(true); slowPoll(); + + // Try to detect the user choosing select-all + if (input.selectionStart != null) { + var extval = input.value = " " + input.value, i = 0; + prevInput = " "; + input.selectionStart = 1; input.selectionEnd = extval.length; + detectingSelectAll = setTimeout(function poll(){ + if (prevInput == " " && input.selectionStart == 0) + operation(commands.selectAll)(instance); + else if (i++ < 10) detectingSelectAll = setTimeout(poll, 500); + else resetInput(); + }, 200); + } } if (gecko) { @@ -2136,8 +2145,7 @@ window.CodeMirror = (function() { if (newScrollPos) scrollCursorIntoView(); if (selectionChanged) restartBlink(); - if (focused && !leaveInputAlone && - (updateInput === true || (updateInput !== false && selectionChanged))) + if (focused && (updateInput === true || (updateInput !== false && selectionChanged))) resetInput(userSelChange); var sc = selectionChanged, cbs = delayedCallbacks; // these can be reset by callbacks From 874d14bd94cc5716dbb5b25a4e1f4ace30849792 Mon Sep 17 00:00:00 2001 From: Marijn Haverbeke Date: Wed, 29 Aug 2012 16:01:25 +0200 Subject: [PATCH 0049/5780] Add variable-height demo Closes #768 --- demo/variableheight.html | 53 ++++++++++++++++++++++++++++++++++++++++ index.html | 1 + 2 files changed, 54 insertions(+) create mode 100644 demo/variableheight.html diff --git a/demo/variableheight.html b/demo/variableheight.html new file mode 100644 index 0000000000..4e23e910d2 --- /dev/null +++ b/demo/variableheight.html @@ -0,0 +1,53 @@ + + + + + CodeMirror: Variable Height Demo + + + + + + + + + +

    CodeMirror: Variable Height Demo

    + +

    +
    + + + diff --git a/index.html b/index.html index 8e3797210b..87047ac71a 100644 --- a/index.html +++ b/index.html @@ -101,6 +101,7 @@

    Usage demos:

  • Highlighting the current line
  • Highlighting selection matches
  • Theming
  • +
  • Mixed font sizes
  • Stand-alone highlighting
  • Full-screen editing
  • Mode auto-changing
  • From a8c7092def0e0337a4949cd3232f7b4f0f6e3ffc Mon Sep 17 00:00:00 2001 From: Marijn Haverbeke Date: Thu, 30 Aug 2012 10:29:19 +0200 Subject: [PATCH 0050/5780] Fill up selection marker when it spans a wrapped line Closes #773 --- lib/codemirror.js | 1 + 1 file changed, 1 insertion(+) diff --git a/lib/codemirror.js b/lib/codemirror.js index cc62f4f937..a69e2558da 100644 --- a/lib/codemirror.js +++ b/lib/codemirror.js @@ -1331,6 +1331,7 @@ window.CodeMirror = (function() { if (rightPos.top - leftPos.top > 3) { // Different lines, draw top part add(left, leftPos.top, 0, leftPos.bottom); left = paddingLeft(); + if (leftPos.bottom < rightPos.top) add(left, leftPos.bottom, 0, rightPos.top); } if (toArg == null && to == lineLen) right = clientWidth; rVal = retTop ? Math.min(rightPos.top, rVal) : Math.max(rightPos.bottom, rVal); From c09b348aa019b610449eacb52066d8d4cff94c6e Mon Sep 17 00:00:00 2001 From: Marijn Haverbeke Date: Fri, 31 Aug 2012 10:21:15 +0200 Subject: [PATCH 0051/5780] Fix assumption that StringStream.peek returns a string Closes #776 --- mode/clojure/clojure.js | 10 ++++------ 1 file changed, 4 insertions(+), 6 deletions(-) diff --git a/mode/clojure/clojure.js b/mode/clojure/clojure.js index d0268a78d6..ce389927db 100644 --- a/mode/clojure/clojure.js +++ b/mode/clojure/clojure.js @@ -40,9 +40,9 @@ CodeMirror.defineMode("clojure", function (config, mode) { var tests = { digit: /\d/, digit_or_colon: /[\d:]/, - hex: /[0-9a-fA-F]/, + hex: /[0-9a-f]/i, sign: /[+-]/, - exponent: /[eE]/, + exponent: /e/i, keyword_char: /[^\s\(\[\;\)\]]/, basic: /[\w\$_\-]/, lang_keyword: /[\w*+!\-_?:\/]/ @@ -64,8 +64,7 @@ CodeMirror.defineMode("clojure", function (config, mode) { function isNumber(ch, stream){ // hex - if ( ch === '0' && 'x' == stream.peek().toLowerCase() ) { - stream.eat('x'); + if ( ch === '0' && stream.eat(/x/i) ) { stream.eatWhile(tests.hex); return true; } @@ -85,8 +84,7 @@ CodeMirror.defineMode("clojure", function (config, mode) { stream.eatWhile(tests.digit); } - if ( 'e' == stream.peek().toLowerCase() ) { - stream.eat(tests.exponent); + if ( stream.eat(tests.exponent) ) { stream.eat(tests.sign); stream.eatWhile(tests.digit); } From 288504cdeaf85a402c18a0219da9216d627f87b2 Mon Sep 17 00:00:00 2001 From: Marijn Haverbeke Date: Fri, 31 Aug 2012 11:27:59 +0200 Subject: [PATCH 0052/5780] Modify the vertical sizing model once more Fixes a problem where the editor would always be 30 pixels smaller than its set height. The height is now set on the outer wrapper DIV again, and some height: 100% tricks are used to make height information flow both ways (inside-out in demo/resize.html mode, outside-in in normal, fixed-height mode). Closes #775 --- demo/complete.html | 5 ++++- demo/fullscreen.html | 6 +++--- demo/resize.html | 10 +++++++--- demo/xmlcomplete.html | 5 ++++- doc/manual.html | 27 ++++++++++----------------- lib/codemirror.css | 10 ++++++---- lib/codemirror.js | 7 ++++--- mode/less/index.html | 2 +- mode/vb/index.html | 4 ++-- test/test.js | 15 ++++++++------- 10 files changed, 49 insertions(+), 42 deletions(-) diff --git a/demo/complete.html b/demo/complete.html index 3b522ec25e..0143949417 100644 --- a/demo/complete.html +++ b/demo/complete.html @@ -10,7 +10,10 @@ - +

    CodeMirror: Autocomplete demo

    diff --git a/demo/fullscreen.html b/demo/fullscreen.html index 69541674d7..d2df04de14 100644 --- a/demo/fullscreen.html +++ b/demo/fullscreen.html @@ -111,14 +111,14 @@

    CodeMirror: Full Screen Editing

    return window.innerHeight || (document.documentElement || document.body).clientHeight; } function setFullScreen(cm, full) { - var wrap = cm.getWrapperElement(), scroll = cm.getScrollerElement(); + var wrap = cm.getWrapperElement(); if (full) { wrap.className += " CodeMirror-fullscreen"; - scroll.style.height = winHeight() + "px"; + wrap.style.height = winHeight() + "px"; document.documentElement.style.overflow = "hidden"; } else { wrap.className = wrap.className.replace(" CodeMirror-fullscreen", ""); - scroll.style.height = ""; + wrap.style.height = ""; document.documentElement.style.overflow = ""; } cm.refresh(); diff --git a/demo/resize.html b/demo/resize.html index f0c9750acf..ddd4e56695 100644 --- a/demo/resize.html +++ b/demo/resize.html @@ -11,9 +11,9 @@ +

    CodeMirror: XML Autocomplete demo

    diff --git a/doc/manual.html b/doc/manual.html index 2b56b6ff07..1291253650 100644 --- a/doc/manual.html +++ b/doc/manual.html @@ -443,16 +443,18 @@

    Customized Styling

    CodeMirror
    The outer element of the editor. This should be used for the - editor width, borders and positioning. Can also be used to set - styles that should hold for everything inside the editor (such - as font and font size), or to set a background.
    + editor width, height, borders and positioning. Can also be used + to set styles that should hold for everything inside the editor + (such as font and font size), or to set a background.
    CodeMirror-scroll
    -
    This determines the editor's height, and whether the editor - scrolls (overflow: auto + fixed height). By - default, it does. Giving this height: auto; overflow: - visible; will cause the editor to resize to fit its - content.
    +
    Whether the editor scrolls (overflow: auto + + fixed height). By default, it does. Setting + the CodeMirror class to have height: + auto and giving this class overflow-x: auto; + overflow-y: hidden; will cause the editor + to resize to fit its + content.
    CodeMirror-focused
    Whenever the editor is focused, the top element gets this @@ -492,15 +494,6 @@

    Customized Styling

    These are used to style matched (or unmatched) brackets.
    -

    So note carefully that, in order to resize the - editor, you should set a width on - the wrapper - (class CodeMirror) element, and a height on - the scroller - (class CodeMirror-scroll) element. - The setSize method is the best - way to dynamically change size at runtime.

    -

    The actual lines, as well as the cursor, are represented by pre elements. By default no text styling (such as bold) that might change line height is applied. If you do want diff --git a/lib/codemirror.css b/lib/codemirror.css index 15272a0373..9b3fe40adc 100644 --- a/lib/codemirror.css +++ b/lib/codemirror.css @@ -1,12 +1,12 @@ /* BASICS */ .CodeMirror { - /* Set width, borders, and global font properties here */ + /* Set height, width, borders, and global font properties here */ font-family: monospace; + height: 300px; } .CodeMirror-scroll { - /* Set height and scrolling behaviour here */ - height: 300px; + /* Set scrolling behaviour here */ overflow: auto; } @@ -113,10 +113,12 @@ div.CodeMirror span.CodeMirror-nonmatchingbracket {color: #f22;} /* 30px is the magic margin used to hide the element's real scrollbars */ /* See overflow: hidden in .CodeMirror, and the paddings in .CodeMirror-sizer */ margin-bottom: -30px; margin-right: -30px; + border: 0px solid white; + border-right-width: 30px; border-bottom-width: 30px; + height: 100%; outline: none; /* Prevent dragging from highlighting the element */ } .CodeMirror-sizer { - padding: 0 30px 30px 0; /* Scrollbar-hiding hack */ position: relative; } diff --git a/lib/codemirror.js b/lib/codemirror.js index a69e2558da..ddf889a1a2 100644 --- a/lib/codemirror.js +++ b/lib/codemirror.js @@ -195,7 +195,7 @@ window.CodeMirror = (function() { return /^\d+$/.test(val) ? val + "px" : val; } if (width != null) wrapper.style.width = interpret(width); - if (height != null) scroller.style.height = interpret(height); + if (height != null) wrapper.style.height = interpret(height); instance.refresh(); }, connect: function(type, f) {connect(this, type, f);}, @@ -1032,10 +1032,10 @@ window.CodeMirror = (function() { var pt = paddingTop(); y1 += pt; y2 += pt; var screen = scroller.clientHeight - scrollerCutOff, screentop = scroller.scrollTop, result = {}; - var docBottom = scroller.scrollHeight; + var docBottom = scroller.scrollHeight - scrollerCutOff; var atTop = y1 < pt + 10, atBottom = y2 + pt > docBottom - 10; if (y1 < screentop) result.scrollTop = atTop ? 0 : Math.max(0, y1); - else if (y2 > screentop + screen) result.scrollTop = (atBottom ? docBottom : y2) - screen; + else if (y2 > screentop + screen) result.scrollTop = (atBottom ? docBottom : y2 - screen); var screenw = scroller.clientWidth - scrollerCutOff, screenleft = scroller.scrollLeft; x1 += gutters.offsetWidth; x2 += gutters.offsetWidth; @@ -1065,6 +1065,7 @@ window.CodeMirror = (function() { showingFrom = showingTo = displayOffset = 0; return; } + // Compute the new visible window // If scrollTop is specified, use that to determine which lines // to render instead of the current scrollbar position. diff --git a/mode/less/index.html b/mode/less/index.html index dd2f588fd9..7f27cf30e9 100644 --- a/mode/less/index.html +++ b/mode/less/index.html @@ -7,7 +7,7 @@ - + diff --git a/mode/vb/index.html b/mode/vb/index.html index 55dfabfa56..1670c7d05b 100644 --- a/mode/vb/index.html +++ b/mode/vb/index.html @@ -8,8 +8,8 @@ diff --git a/test/test.js b/test/test.js index f66513f07b..e9f51e3f01 100644 --- a/test/test.js +++ b/test/test.js @@ -333,7 +333,7 @@ testCM("scrollSnap", function(cm) { cm.setCursor({line: 100, ch: 180}); cm.setCursor({line: 199, ch: 0}); info = cm.getScrollInfo(); - is(info.left == 0 && info.top > info.height - 100, "scrolled clean to bottom"); + is(info.left == 0 && info.top + 2 > info.height - cm.getScrollerElement().clientHeight, "scrolled clean to bottom"); }); testCM("selectionPos", function(cm) { @@ -410,14 +410,15 @@ testCM("weirdLinebreaks", function(cm) { testCM("setSize", function(cm) { cm.setSize(100, 100); - is(cm.getWrapperElement().offsetWidth, 100); - is(cm.getWrapperElement().offsetHeight, 100); + var wrap = cm.getWrapperElement(); + is(wrap.offsetWidth, 100); + is(wrap.offsetHeight, 100); cm.setSize("100%", "3em"); - is(cm.getWrapperElement().style.width, "100%"); - is(cm.getScrollerElement().style.height, "3em"); + is(wrap.style.width, "100%"); + is(wrap.style.height, "3em"); cm.setSize(null, 40); - is(cm.getWrapperElement().style.width, "100%"); - is(cm.getScrollerElement().style.height, "40px"); + is(wrap.style.width, "100%"); + is(wrap.style.height, "40px"); }); testCM("hiddenLines", function(cm) { From 40f48a0d4ee02ea534a8535e1d265700153ef8c1 Mon Sep 17 00:00:00 2001 From: Marijn Haverbeke Date: Fri, 31 Aug 2012 15:30:05 +0200 Subject: [PATCH 0053/5780] Save overhead of operation when polling doesn't find a change --- lib/codemirror.js | 6 ++---- 1 file changed, 2 insertions(+), 4 deletions(-) diff --git a/lib/codemirror.js b/lib/codemirror.js index ddf889a1a2..939133c247 100644 --- a/lib/codemirror.js +++ b/lib/codemirror.js @@ -952,21 +952,17 @@ window.CodeMirror = (function() { function slowPoll() { if (pollingFast) return; poll.set(options.pollInterval, function() { - startOperation(); readInput(); if (focused) slowPoll(); - endOperation(); }); } function fastPoll() { var missed = false; pollingFast = true; function p() { - startOperation(); var changed = readInput(); if (!changed && !missed) {missed = true; poll.set(60, p);} else {pollingFast = false; slowPoll();} - endOperation(); } poll.set(20, p); } @@ -981,6 +977,7 @@ window.CodeMirror = (function() { if (!focused || hasSelection(input) || options.readOnly) return false; var text = input.value; if (text == prevInput) return false; + startOperation(); shiftSelecting = null; var same = 0, l = Math.min(prevInput.length, text.length); while (same < l && prevInput[same] == text[same]) ++same; @@ -991,6 +988,7 @@ window.CodeMirror = (function() { replaceSelection(text.slice(same), "end"); if (text.length > 1000) { input.value = prevInput = ""; } else prevInput = text; + endOperation(); return true; } function resetInput(user) { From f9ad617aa44c0e215e1ebf4c5725dc5ca94b0f1d Mon Sep 17 00:00:00 2001 From: Marijn Haverbeke Date: Fri, 31 Aug 2012 12:48:49 +0200 Subject: [PATCH 0054/5780] Simplify line content-node API, get rid of makeTab indirection --- lib/codemirror.js | 33 ++++++++++++++++++--------------- 1 file changed, 18 insertions(+), 15 deletions(-) diff --git a/lib/codemirror.js b/lib/codemirror.js index 939133c247..17f2f0fd48 100644 --- a/lib/codemirror.js +++ b/lib/codemirror.js @@ -297,7 +297,6 @@ window.CodeMirror = (function() { // Tracks the maximum line length so that the horizontal scrollbar // can be kept static when scrolling. var maxLine = getLine(0), updateMaxLine = false, maxLineChanged = true; - var tabCache = {}; var pollingFast = false; // Ensures slowPoll doesn't cancel fastPoll var goalColumn = null; @@ -365,6 +364,10 @@ window.CodeMirror = (function() { for (var n = line; n; n = n.parent) n.height += diff; } + function lineContent(line, wrapAt) { + return line.getContent(options.tabSize, wrapAt, options.lineWrapping); + } + function setValue(code) { var top = {line: 0, ch: 0}; updateLines(top, {line: doc.size - 1, ch: getLine(doc.size-1).text.length}, @@ -1211,7 +1214,7 @@ window.CodeMirror = (function() { if (!nextIntact || nextIntact.from > j) { if (line.hidden) var lineElement = elt("div"); else { - var lineElement = line.getElement(makeTab), markers = line.gutterMarkers; + var lineElement = lineContent(line), markers = line.gutterMarkers; if (line.className) lineElement.className = line.className; // Lines with gutter elements or a background class need // to be wrapped again, and have the extra elements added @@ -1567,7 +1570,7 @@ window.CodeMirror = (function() { var indentString = "", pos = 0; if (options.indentWithTabs) for (var i = Math.floor(indentation / options.tabSize); i; --i) {pos += options.tabSize; indentString += "\t";} - while (pos < indentation) {++pos; indentString += " ";} + if (pos < indentation) indentString += spaceStr(indentation - pos); if (indentString != curSpaceString) replaceRange(indentString, {line: n, ch: 0}, {line: n, ch: curSpaceString.length}); @@ -1598,13 +1601,6 @@ window.CodeMirror = (function() { } changes.push({from: 0, to: doc.size}); } - function makeTab(col) { - var w = options.tabSize - col % options.tabSize, cached = tabCache[w]; - if (cached) return cached; - for (var str = "", i = 0; i < w; ++i) str += " "; - var span = elt("span", str, "cm-tab"); - return (tabCache[w] = {element: span, width: w}); - } function themeChanged() { wrapper.className = wrapper.className.replace(/\s*cm-s-\S+/g, "") + options.theme.replace(/(^|\s)\s*/g, " cm-s-"); @@ -1795,7 +1791,7 @@ window.CodeMirror = (function() { return {top: memo.top, bottom: memo.bottom, left: memo.left, right: memo.right}; } var atEnd = ch && ch == line.text.length; - var pre = line.getElement(makeTab, atEnd ? ch - 1 : ch, options.lineWrapping); + var pre = lineContent(line, atEnd ? ch - 1 : ch); removeChildrenAndAdd(measure, pre); var anchor = pre.anchor; // We'll sample once at the top, once at the bottom of the line, @@ -2772,7 +2768,7 @@ window.CodeMirror = (function() { indentation: function(tabSize) {return countColumn(this.text, null, tabSize);}, // Produces an HTML fragment for the line, taking selection, // marking, and highlighting into account. - getElement: function(makeTab, wrapAt, compensateForWrapping) { + getContent: function(tabSize, wrapAt, compensateForWrapping) { var first = true, col = 0, specials = /[\t\u0000-\u0019\u200b\u2028\u2029\uFEFF]/g; var pre = elt("pre"); function span_(text, style) { @@ -2796,9 +2792,9 @@ window.CodeMirror = (function() { if (!m) break; pos += skipped + 1; if (m[0] == "\t") { - var tab = makeTab(col); - content.appendChild(tab.element.cloneNode(true)); - col += tab.width; + var tabWidth = tabSize - col % tabSize; + content.appendChild(elt("span", spaceStr(tabWidth), "cm-tab")); + col += tabWidth; } else { var token = elt("span", "\u2022", "cm-invalidchar"); token.title = "\\u" + m[0].charCodeAt(0).toString(16); @@ -3271,6 +3267,13 @@ window.CodeMirror = (function() { return n; } + var spaceStrs = [""]; + function spaceStr(n) { + while (spaceStrs.length <= n) + spaceStrs.push(spaceStrs[spaceStrs.length - 1] + " "); + return spaceStrs[n]; + } + function selectInput(node) { if (ios) { // Mobile Safari apparently has a bug where select() is broken. node.selectionStart = 0; From 846d043e9fd88afd98f8615b5df117de72242206 Mon Sep 17 00:00:00 2001 From: Marijn Haverbeke Date: Fri, 31 Aug 2012 13:57:34 +0200 Subject: [PATCH 0055/5780] Change the way highlighting information is updated - Background parsing now never goes past the visible part of the document. - We only store style information for lines that are actually visible (and then keep it cached). - When the document changes, background highlighting always re-highlights from the change to the end of the visible part. - Gets rid of compareState mode methods and the hairy heuristic that tried to simulate it when absent. - The onHighlightComplete callback was removed, since it no longer really applies -- the document is only fully parsed when scrolled to its end. This should help preserve memory (a huge document will no longer immediately have parser state and highlighting information built up for all lines, but only for the parts that you look at), and removes the pathological case when you, for example, are typing at the top of a huge XML document, and opening or closing a tag causes a whole re-highlight to cascade all the way to the bottom because the compareState will detect a change. Issue #688 --- doc/manual.html | 16 ---- lib/codemirror.js | 173 ++++++++++++------------------------ lib/util/multiplex.js | 8 -- mode/haxe/haxe.js | 3 - mode/htmlmixed/htmlmixed.js | 6 -- mode/tiki/tiki.js | 7 -- mode/xml/xml.js | 8 -- 7 files changed, 58 insertions(+), 163 deletions(-) diff --git a/doc/manual.html b/doc/manual.html index 1291253650..0d257148d0 100644 --- a/doc/manual.html +++ b/doc/manual.html @@ -330,10 +330,6 @@

    Events

    "scroll" (instance)
    Fires when the editor is scrolled.
    -
    "highlightComplete" (instance)
    -
    Whenever the editor's content has been fully highlighted, - this event is fired.
    -
    "update" (instance)
    Will be fired whenever CodeMirror updates its DOM display.
    @@ -1144,18 +1140,6 @@

    Writing CodeMirror Modes

    which is given a state and should return a safe copy of that state.

    -

    By default, CodeMirror will stop re-parsing - a document as soon as it encounters a few lines that were - highlighted the same in the old parse as in the new one. It is - possible to provide an explicit way to test whether a state is - equivalent to another one, which CodeMirror will use (instead of - the unchanged-lines heuristic) to decide when to stop - highlighting. You do this by providing - a compareStates method on your mode object, which - takes two state arguments and returns a boolean indicating whether - they are equivalent. See the XML mode, which uses this to provide - reliable highlighting of bad closing tags, as an example.

    -

    If you want your mode to provide smart indentation (though the indentLine method and the indentAuto diff --git a/lib/codemirror.js b/lib/codemirror.js index 17f2f0fd48..0618d1f42d 100644 --- a/lib/codemirror.js +++ b/lib/codemirror.js @@ -273,9 +273,9 @@ window.CodeMirror = (function() { var measureLineMemo = [], measureLineMemoPos = 0; // mode holds a mode API object. doc is the tree of Line objects, - // work an array of lines that should be parsed, and history the - // undo history (instance of History constructor). - var mode, doc = new BranchChunk([new LeafChunk([new Line("", null, textHeight())])]), work, focused; + // frontier is the point up to which the content has been parsed, + // and history the undo history (instance of History constructor). + var mode, doc = new BranchChunk([new LeafChunk([new Line("", textHeight())])]), frontier = 0, focused; loadMode(); // The selection. These are always maintained to point at valid // positions. Inverted is used to remember that the user is @@ -365,6 +365,8 @@ window.CodeMirror = (function() { } function lineContent(line, wrapAt) { + if (!line.styles) + line.highlight(mode, line.stateAfter = getStateBefore(lineNo(line)), options.tabSize); return line.getContent(options.tabSize, wrapAt, options.lineWrapping); } @@ -835,19 +837,11 @@ window.CodeMirror = (function() { if (recomputeMaxLength) updateMaxLine = true; } - // Add these lines to the work array, so that they will be - // highlighted. Adjust work lines if lines were added/removed. - var newWork = [], lendiff = newText.length - nlines - 1; - for (var i = 0, l = work.length; i < l; ++i) { - var task = work[i]; - if (task < from.line) newWork.push(task); - else if (task > to.line) newWork.push(task + lendiff); - } - var hlEnd = from.line + Math.min(newText.length, 500); - highlightLines(from.line, hlEnd); - newWork.push(hlEnd); - work = newWork; - startWorker(100); + // Adjust frontier, schedule worker + frontier = Math.min(frontier, from.line); + startWorker(400); + + var lendiff = newText.length - nlines - 1; // Remember that these lines changed, for updating the display changes.push({from: from.line, to: to.line + 1, diff: lendiff}); var changeObj = {from: from, to: to, text: newText}; @@ -1120,6 +1114,7 @@ window.CodeMirror = (function() { signalLater(instance, "viewportChange", delayedCallbacks, instance, from, to); showingFrom = from; showingTo = to; displayOffset = heightAtLine(doc, from); + startWorker(100); // Since this is all rather error prone, it is honoured with the // only assertion in the whole file. @@ -1579,8 +1574,8 @@ window.CodeMirror = (function() { function loadMode() { mode = CodeMirror.getMode(options, options.mode); doc.iter(0, doc.size, function(line) { line.stateAfter = null; }); - work = [0]; - startWorker(); + frontier = 0; + startWorker(100); } function wrappingChanged(from, to) { if (options.lineWrapping) { @@ -2050,69 +2045,39 @@ window.CodeMirror = (function() { return minline; } function getStateBefore(n) { - var start = findStartLine(n), state = start && getLine(start-1).stateAfter; + var pos = findStartLine(n), state = pos && getLine(pos-1).stateAfter; if (!state) state = startState(mode); else state = copyState(mode, state); - doc.iter(start, n, function(line) { - line.highlight(mode, state, options.tabSize); - line.stateAfter = copyState(mode, state); + doc.iter(pos, n, function(line) { + line.process(mode, state, options.tabSize); + line.stateAfter = (pos == n - 1 || pos % 5 == 0) ? copyState(mode, state) : null; }); - if (start < n) changes.push({from: start, to: n}); - if (n < doc.size && !getLine(n).stateAfter) work.push(n); return state; } - function highlightLines(start, end) { - var state = getStateBefore(start); - doc.iter(start, end, function(line) { - line.highlight(mode, state, options.tabSize); - line.stateAfter = copyState(mode, state); - }); - } function highlightWorker() { - var end = +new Date + options.workTime; - var foundWork = work.length; - while (work.length) { - if (!getLine(showingFrom).stateAfter) var task = showingFrom; - else var task = work.pop(); - if (task >= doc.size) continue; - var start = findStartLine(task), state = start && getLine(start-1).stateAfter; - if (state) state = copyState(mode, state); - else state = startState(mode); - - var unchanged = 0, compare = mode.compareStates, realChange = false, - i = start, bail = false; - doc.iter(i, doc.size, function(line) { - var hadState = line.stateAfter; - if (+new Date > end) { - work.push(i); - startWorker(options.workDelay); - if (realChange) changes.push({from: task, to: i + 1}); - return (bail = true); - } - var changed = line.highlight(mode, state, options.tabSize); - if (changed) realChange = true; + if (frontier >= showingTo) return; + var end = +new Date + options.workTime, state = copyState(mode, getStateBefore(frontier)); + var startFrontier = frontier; + doc.iter(frontier, showingTo, function(line) { + if (frontier >= showingFrom) { // Visible + line.highlight(mode, state, options.tabSize); line.stateAfter = copyState(mode, state); - var done = null; - if (compare) { - var same = hadState && compare(hadState, state); - if (same != Pass) done = !!same; - } - if (done == null) { - if (changed !== false || !hadState) unchanged = 0; - else if (++unchanged > 3 && (!mode.indent || mode.indent(hadState, "") == mode.indent(state, ""))) - done = true; - } - if (done) return true; - ++i; - }); - if (bail) return; - if (realChange) changes.push({from: task, to: i + 1}); - } - if (foundWork) signal(instance, "highlightComplete", instance); + } else { + line.process(mode, state, options.tabSize); + line.stateAfter = frontier % 5 == 0 ? copyState(mode, state) : null; + } + ++frontier; + if (+new Date > end) { + startWorker(options.workDelay); + return true; + } + }); + if (showingTo > startFrontier && frontier >= showingFrom) + operation(function() {changes.push({from: startFrontier, to: frontier});})(); } function startWorker(time) { - if (!work.length) return; - highlight.set(time, operation(highlightWorker)); + if (frontier < showingTo) + highlight.set(time, highlightWorker); } // Operations are used to wrap changes in such a way that each @@ -2599,8 +2564,7 @@ window.CodeMirror = (function() { // Line objects. These hold state related to a line, including // highlighting info (the styles array). - function Line(text, styles, height) { - this.styles = styles || [text, null]; + function Line(text, height) { this.text = text; this.height = height; } @@ -2617,16 +2581,11 @@ window.CodeMirror = (function() { return ln; }; Line.prototype = { - // Replace a piece of a line, keeping the styles around it intact. + // Replace a piece of a line, keeping the markers intact. replace: function(from, to_, text) { - var st = [], mk = this.marked, to = to_ == null ? this.text.length : to_; - copyStyles(0, from, this.styles, st); - if (text) st.push(text, null); - copyStyles(to, this.text.length, this.styles, st); - this.styles = st; + var mk = this.marked, to = to_ == null ? this.text.length : to_; this.text = this.text.slice(0, from) + text + this.text.slice(to); - this.order = null; - this.stateAfter = null; + this.order = this.stateAfter = this.styles = null; if (mk) { var diff = text.length - (to - from); for (var i = 0; i < mk.length; ++i) { @@ -2636,11 +2595,10 @@ window.CodeMirror = (function() { } } }, - // Split a part off a line, keeping styles and markers intact. + // Split a part off a line, keeping markers intact. split: function(pos, textBefore) { - var st = [textBefore, null], mk = this.marked; - copyStyles(pos, this.text.length, this.styles, st); - var taken = new Line(textBefore + this.text.slice(pos), st, this.height); + var mk = this.marked; + var taken = new Line(textBefore + this.text.slice(pos), this.height); if (mk) { for (var i = 0; i < mk.length; ++i) { var mark = mk[i]; @@ -2658,7 +2616,7 @@ window.CodeMirror = (function() { var mylen = this.text.length, mk = line.marked, mymk = this.marked; this.text += line.text; this.order = this.order || line.order ? null : false; - copyStyles(0, line.text.length, line.styles, this.styles); + this.styles = this.stateAfter = null; if (mymk) { for (var i = 0; i < mymk.length; ++i) if (mymk[i].to == null) mymk[i].to = mylen; @@ -2724,19 +2682,16 @@ window.CodeMirror = (function() { // array, which contains alternating fragments of text and CSS // classes. highlight: function(mode, state, tabSize) { - var stream = new StringStream(this.text, tabSize), st = this.styles, pos = 0; - var changed = false, curWord = st[0], prevWord; + var stream = new StringStream(this.text, tabSize), st = this.styles || (this.styles = []); + var pos = st.length = 0; if (this.text == "" && mode.blankLine) mode.blankLine(state); while (!stream.eol()) { - var style = mode.token(stream, state); - var substr = this.text.slice(stream.start, stream.pos); + var style = mode.token(stream, state), substr = stream.current(); stream.start = stream.pos; - if (pos && st[pos-1] == style) + if (pos && st[pos-1] == style) { st[pos-2] += substr; - else if (substr) { - if (!changed && (st[pos+1] != style || (pos && st[pos-2] != prevWord))) changed = true; + } else if (substr) { st[pos++] = substr; st[pos++] = style; - prevWord = curWord; curWord = st[pos]; } // Give up when line is ridiculously long if (stream.pos > 5000) { @@ -2744,12 +2699,14 @@ window.CodeMirror = (function() { break; } } - if (st.length != pos) {st.length = pos; changed = true;} - if (pos && st[pos-2] != prevWord) changed = true; - // Short lines with simple highlights return null, and are - // counted as changed by the driver because they are likely to - // highlight the same way in various contexts. - return changed || (st.length < 5 && this.text.length < 10 ? null : false); + }, + process: function(mode, state, tabSize) { + var stream = new StringStream(this.text, tabSize); + if (this.text == "" && mode.blankLine) mode.blankLine(state); + while (!stream.eol() && stream.pos <= 5000) { + mode.token(stream, state); + stream.start = stream.pos; + } }, // Fetch the parser token for a given character. Useful for hacks // that want to inspect the mode state (say, for completion). @@ -2897,20 +2854,6 @@ window.CodeMirror = (function() { for (var i = 0, e = this.marked.length; i < e; ++i) this.marked[i].detach(this); } }; - // Utility used by replace and split above - function copyStyles(from, to, source, dest) { - for (var i = 0, pos = 0, state = 0; pos < to; i+=2) { - var part = source[i], end = pos + part.length; - if (state == 0) { - if (end > from) dest.push(part.slice(from - pos, Math.min(part.length, to - pos)), source[i+1]); - if (end >= from) state = 1; - } else if (state == 1) { - if (end > to) dest.push(part.slice(0, to - pos), source[i+1]); - else dest.push(part, source[i+1]); - } - pos = end; - } - } // Data structure that holds the sequence of lines. function LeafChunk(lines) { diff --git a/lib/util/multiplex.js b/lib/util/multiplex.js index 755588b9c4..b7c1838f62 100644 --- a/lib/util/multiplex.js +++ b/lib/util/multiplex.js @@ -68,14 +68,6 @@ CodeMirror.multiplexingMode = function(outer /*, others */) { return mode.indent(state.innerActive ? state.inner : state.outer, textAfter); }, - compareStates: function(a, b) { - if (a.innerActive != b.innerActive) return false; - var mode = a.innerActive || outer; - if (!mode.compareStates) return CodeMirror.Pass; - return mode.compareStates(a.innerActive ? a.inner : a.outer, - b.innerActive ? b.inner : b.outer); - }, - electricChars: outer.electricChars }; }; diff --git a/mode/haxe/haxe.js b/mode/haxe/haxe.js index ea8bd834e6..64f4eb3ff8 100644 --- a/mode/haxe/haxe.js +++ b/mode/haxe/haxe.js @@ -421,9 +421,6 @@ CodeMirror.defineMode("haxe", function(config, parserConfig) { else if (lexical.align) return lexical.column + (closing ? 0 : 1); else return lexical.indented + (closing ? 0 : indentUnit); }, - compareStates: function(state1, state2) { - return (state1.localVars == state2.localVars) && (state1.context == state2.context); - }, electricChars: "{}" }; diff --git a/mode/htmlmixed/htmlmixed.js b/mode/htmlmixed/htmlmixed.js index 260a6d0dfb..5f2fc238c9 100644 --- a/mode/htmlmixed/htmlmixed.js +++ b/mode/htmlmixed/htmlmixed.js @@ -76,12 +76,6 @@ CodeMirror.defineMode("htmlmixed", function(config, parserConfig) { return cssMode.indent(state.localState, textAfter); }, - compareStates: function(a, b) { - if (a.mode != b.mode) return false; - if (a.localState) return CodeMirror.Pass; - return htmlMode.compareStates(a.htmlState, b.htmlState); - }, - electricChars: "/{}:" }; }, "xml", "javascript", "css"); diff --git a/mode/tiki/tiki.js b/mode/tiki/tiki.js index 24bf0fbfe5..af83dc1b5b 100644 --- a/mode/tiki/tiki.js +++ b/mode/tiki/tiki.js @@ -301,13 +301,6 @@ CodeMirror.defineMode('tiki', function(config, parserConfig) { if (context) return context.indent + indentUnit; else return 0; }, - compareStates: function(a, b) { - if (a.indented != b.indented || a.pluginName != b.pluginName) return false; - for (var ca = a.context, cb = b.context; ; ca = ca.prev, cb = cb.prev) { - if (!ca || !cb) return ca == cb; - if (ca.pluginName != cb.pluginName) return false; - } - }, electricChars: "/" }; }); diff --git a/mode/xml/xml.js b/mode/xml/xml.js index cd69f62fd5..860e368f5b 100644 --- a/mode/xml/xml.js +++ b/mode/xml/xml.js @@ -308,14 +308,6 @@ CodeMirror.defineMode("xml", function(config, parserConfig) { else return 0; }, - compareStates: function(a, b) { - if (a.indented != b.indented || a.tokenize != b.tokenize) return false; - for (var ca = a.context, cb = b.context; ; ca = ca.prev, cb = cb.prev) { - if (!ca || !cb) return ca == cb; - if (ca.tagName != cb.tagName || ca.indent != cb.indent) return false; - } - }, - electricChars: "/" }; }); From b914a9652e39a72e62a30acb02da09d68fae527f Mon Sep 17 00:00:00 2001 From: Brandon Frohs Date: Mon, 3 Sep 2012 02:42:36 -0400 Subject: [PATCH 0056/5780] Add highlighting tests for Markdown mode. --- mode/markdown/test.html | 419 ++++++++++++++++++++++++++++++++++++++++ 1 file changed, 419 insertions(+) create mode 100644 mode/markdown/test.html diff --git a/mode/markdown/test.html b/mode/markdown/test.html new file mode 100644 index 0000000000..3f4e9e0c90 --- /dev/null +++ b/mode/markdown/test.html @@ -0,0 +1,419 @@ + + + + + CodeMirror: Markdown mode + + + + + + + + + + +

    Tests for the Markdown Mode

    +

    Basics

    + + +

    Code

    + + +

    Headers

    + + +

    Blockquotes

    + + +

    Horizontal rules

    + + +

    Links

    + + +

    Emphasis

    + + +

    Escaping

    + + +

    Summary

    + + + + + From 92e0114a54843792d88c68c49a57662343d05c15 Mon Sep 17 00:00:00 2001 From: Marijn Haverbeke Date: Mon, 3 Sep 2012 09:48:59 +0200 Subject: [PATCH 0057/5780] [vim keymap] Reset keymap after YY is pressed Closes #774 --- keymap/vim.js | 5 ++++- 1 file changed, 4 insertions(+), 1 deletion(-) diff --git a/keymap/vim.js b/keymap/vim.js index 73a4a0a771..b6a41850ef 100644 --- a/keymap/vim.js +++ b/keymap/vim.js @@ -509,7 +509,10 @@ setupPrefixBindingForKey("Space"); CodeMirror.keyMap["vim-prefix-y"] = { - "Y": countTimes(function(cm) { pushInBuffer("\n"+cm.getLine(cm.getCursor().line+yank)); yank++; }), + "Y": countTimes(function(cm) { + pushInBuffer("\n"+cm.getLine(cm.getCursor().line+yank)); yank++; + cm.setOption("keyMap", "vim"); + }), "'": function(cm) {cm.setOption("keyMap", "vim-prefix-y'"); emptyBuffer();}, nofallthrough: true, style: "fat-cursor" }; From 0c8875d8c862776410e63c486802fbfc6db5bb86 Mon Sep 17 00:00:00 2001 From: Marijn Haverbeke Date: Mon, 3 Sep 2012 09:59:48 +0200 Subject: [PATCH 0058/5780] Fix bad arguments passed to new Line Introduced in 846d043e9fd88afd98f8615b5df117de72242206 Closes #779 --- lib/codemirror.js | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/lib/codemirror.js b/lib/codemirror.js index 0618d1f42d..98f8cc5cf1 100644 --- a/lib/codemirror.js +++ b/lib/codemirror.js @@ -2569,7 +2569,7 @@ window.CodeMirror = (function() { this.height = height; } Line.inheritMarks = function(text, orig, height) { - var ln = new Line(text, null, height), mk = orig && orig.marked; + var ln = new Line(text, height), mk = orig && orig.marked; if (mk) { for (var i = 0; i < mk.length; ++i) { if (mk[i].to == null && mk[i].style) { From c67b061fa2f91334acf118c15ba7418e622555b1 Mon Sep 17 00:00:00 2001 From: Marijn Haverbeke Date: Mon, 3 Sep 2012 10:04:53 +0200 Subject: [PATCH 0059/5780] Fix XQuery demo page to not set height on scroller element Closes #780 --- mode/xquery/index.html | 12 +++++------- 1 file changed, 5 insertions(+), 7 deletions(-) diff --git a/mode/xquery/index.html b/mode/xquery/index.html index bea4ab4ac8..27acb89786 100644 --- a/mode/xquery/index.html +++ b/mode/xquery/index.html @@ -33,13 +33,11 @@ + .CodeMirror { + border-top: 1px solid black; border-bottom: 1px solid black; + height:400px; + } +

    CodeMirror: XQuery mode

    From 7ee24811ca2d4ca6c7feac743d8c65b9a994584b Mon Sep 17 00:00:00 2001 From: Marijn Haverbeke Date: Mon, 3 Sep 2012 10:15:16 +0200 Subject: [PATCH 0060/5780] Remove obsolete before_script section in .travis.yml --- .travis.yml | 3 --- 1 file changed, 3 deletions(-) diff --git a/.travis.yml b/.travis.yml index 9bdccd0e3e..baa0031d50 100644 --- a/.travis.yml +++ b/.travis.yml @@ -1,6 +1,3 @@ language: node_js node_js: - 0.8 -before_script: - - "export DISPLAY=:99.0" - - "sh -e /etc/init.d/xvfb start" From b98247a43d879e44442950721dbcbdc32eecbaff Mon Sep 17 00:00:00 2001 From: Brandon Frohs Date: Sun, 2 Sep 2012 22:53:30 -0400 Subject: [PATCH 0061/5780] [clojure mode] Check if +/- is immediately followed by digit before marking as leading sign Closes #782 Correct highlighting for the following code snippet should highlight the + as an operator, and each 2 as a number (taken from http://clojuredocs.org/clojure_core/1.2.0/clojure.test/deftest) (is (= 4 (+ 2 2))) --- mode/clojure/clojure.js | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/mode/clojure/clojure.js b/mode/clojure/clojure.js index ce389927db..84f6073fd5 100644 --- a/mode/clojure/clojure.js +++ b/mode/clojure/clojure.js @@ -70,7 +70,7 @@ CodeMirror.defineMode("clojure", function (config, mode) { } // leading sign - if ( ch == '+' || ch == '-' ) { + if ( ( ch == '+' || ch == '-' ) && ( tests.digit.test(stream.peek()) ) ) { stream.eat(tests.sign); ch = stream.next(); } From 952eafb56111e794186d86561d55b2b4ae0ab317 Mon Sep 17 00:00:00 2001 From: Marijn Haverbeke Date: Mon, 3 Sep 2012 10:38:23 +0200 Subject: [PATCH 0062/5780] Tweak context menu hack to make select-all reliable on FF Issue #755 --- lib/codemirror.js | 10 ++++++---- 1 file changed, 6 insertions(+), 4 deletions(-) diff --git a/lib/codemirror.js b/lib/codemirror.js index 98f8cc5cf1..f1b85d6fa6 100644 --- a/lib/codemirror.js +++ b/lib/codemirror.js @@ -1981,18 +1981,20 @@ window.CodeMirror = (function() { "px; left: " + (e.clientX - 5) + "px; z-index: 1000; background: white; outline: none;" + "border-width: 0; outline: none; overflow: hidden; opacity: .05; filter: alpha(opacity=5);"; focusInput(); - selectInput(input); - clearTimeout(detectingSelectAll); + resetInput(true); + // Adds "Select all" to context menu in FF + if (posEq(sel.from, sel.to)) input.value = prevInput = " "; + function rehide() { inputDiv.style.position = "relative"; input.style.cssText = oldCSS; if (ie_lt9) scrollbarV.scrollTop = scroller.scrollTop = scrollPos; - resetInput(true); slowPoll(); // Try to detect the user choosing select-all if (input.selectionStart != null) { - var extval = input.value = " " + input.value, i = 0; + clearTimeout(detectingSelectAll); + var extval = input.value = " " + (posEq(sel.from, sel.to) ? "" : input.value), i = 0; prevInput = " "; input.selectionStart = 1; input.selectionEnd = extval.length; detectingSelectAll = setTimeout(function poll(){ From 126460da1b41a1873cb2d39804f166ee9f5c4f72 Mon Sep 17 00:00:00 2001 From: Marijn Haverbeke Date: Mon, 3 Sep 2012 13:09:25 +0200 Subject: [PATCH 0063/5780] Fix corruption of operation data introduced by 6ba24813c96687dac2f23db100780bc42e6ed808 --- lib/codemirror.js | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/lib/codemirror.js b/lib/codemirror.js index f1b85d6fa6..53fc3b4c92 100644 --- a/lib/codemirror.js +++ b/lib/codemirror.js @@ -974,7 +974,7 @@ window.CodeMirror = (function() { if (!focused || hasSelection(input) || options.readOnly) return false; var text = input.value; if (text == prevInput) return false; - startOperation(); + if (!nestedOperation) startOperation(); shiftSelecting = null; var same = 0, l = Math.min(prevInput.length, text.length); while (same < l && prevInput[same] == text[same]) ++same; @@ -985,7 +985,7 @@ window.CodeMirror = (function() { replaceSelection(text.slice(same), "end"); if (text.length > 1000) { input.value = prevInput = ""; } else prevInput = text; - endOperation(); + if (!nestedOperation) endOperation(); return true; } function resetInput(user) { From 155c24fab1d40fb3aa363ccd25f06636a8a416db Mon Sep 17 00:00:00 2001 From: Marijn Haverbeke Date: Mon, 3 Sep 2012 13:08:12 +0200 Subject: [PATCH 0064/5780] Add addLineWidget/removeLineWidget API functions --- demo/variableheight.html | 1 - demo/widget.html | 68 +++++++++++++++++++++++++++++++++++++ doc/manual.html | 23 +++++++++++-- index.html | 1 + lib/codemirror.css | 10 ++++-- lib/codemirror.js | 72 ++++++++++++++++++++++++++++++++++------ test/test.js | 14 ++++++++ 7 files changed, 172 insertions(+), 17 deletions(-) create mode 100644 demo/widget.html diff --git a/demo/variableheight.html b/demo/variableheight.html index 4e23e910d2..8523027368 100644 --- a/demo/variableheight.html +++ b/demo/variableheight.html @@ -18,7 +18,6 @@

    CodeMirror: Variable Height Demo

    -

    - + + + + + + diff --git a/test/mode_test.css b/test/mode_test.css index f425922e5d..1ac66737fb 100644 --- a/test/mode_test.css +++ b/test/mode_test.css @@ -8,15 +8,3 @@ .mt-output .mt-style { font-size: x-small; } - -.mt-test { - border-left: 10px solid #fff; -} - -.mt-pass { - border-left: 10px solid #cfc; -} - -.mt-fail { - border-left: 10px solid #fcc; -} diff --git a/test/mode_test.js b/test/mode_test.js index 0bda5a2e78..b257d6ca36 100644 --- a/test/mode_test.js +++ b/test/mode_test.js @@ -10,33 +10,50 @@ ModeTest.modeOptions = {}; ModeTest.modeName = CodeMirror.defaults.mode; /* keep track of results for printSummary */ -ModeTest.tests = 0; +ModeTest.testCount = 0; ModeTest.passes = 0; /** * Run a test; prettyprints the results using document.write(). - * - * @param string to highlight - * - * @param style[i] expected style of the i'th token in string - * - * @param token[i] expected value for the i'th token in string + * + * @param name Name of test + * @param text String to highlight. + * @param expected Expected styles and tokens: Array(style, token, [style, token,...]) + * @param modeName + * @param modeOptions + * @param expectedFail */ -ModeTest.test = function() { - ModeTest.tests += 1; +ModeTest.testMode = function(name, text, expected, modeName, modeOptions, expectedFail) { + ModeTest.testCount += 1; + + if (!modeName) modeName = ModeTest.modeName; + + if (!modeOptions) modeOptions = ModeTest.modeOptions; - var mode = CodeMirror.getMode(ModeTest.modeOptions, ModeTest.modeName); + var mode = CodeMirror.getMode(modeOptions, modeName); - if (arguments.length < 1) { - throw "must have text for test"; + if (expected.length < 0) { + throw "must have text for test (" + name + ")"; } - if (arguments.length % 2 != 1) { - throw "must have text for test plus expected (style, token) pairs"; + if (expected.length % 2 != 0) { + throw "must have text for test (" + name + ") plus expected (style, token) pairs"; } + return test( + modeName + "_" + name, + function(){ + var place = document.getElementById("testground"), cm = CodeMirror(place); + try {return ModeTest.compare(cm, text, expected, mode);} + finally {place.removeChild(cm.getWrapperElement());} + }, + expectedFail + ); + +} + +ModeTest.compare = function (cm, text, arguments, mode) { - var text = arguments[0]; var expectedOutput = []; - for (var i = 1; i < arguments.length; i += 2) { + for (var i = 0; i < arguments.length; i += 2) { arguments[i] = (arguments[i] != null ? arguments[i].split(' ').sort().join(' ') : arguments[i]); expectedOutput.push([arguments[i],arguments[i + 1]]); } @@ -51,20 +68,26 @@ ModeTest.test = function() { } var s = ''; - s += '
    '; - s += '
    ' + ModeTest.htmlEscape(text) + '
    '; - s += '
    '; if (pass || expectedOutput.length == 0) { + s += '
    '; + s += '
    ' + ModeTest.htmlEscape(text) + '
    '; + s += '
    '; s += ModeTest.prettyPrintOutputTable(observedOutput); + s += '
    '; + s += '
    '; + return s; } else { + s += '
    '; + s += '
    ' + ModeTest.htmlEscape(text) + '
    '; + s += '
    '; s += 'expected:'; s += ModeTest.prettyPrintOutputTable(expectedOutput); s += 'observed:'; s += ModeTest.prettyPrintOutputTable(observedOutput); + s += '
    '; + s += '
    '; + throw s; } - s += '
    '; - s += '
    '; - document.write(s); } /** @@ -167,7 +190,8 @@ ModeTest.prettyPrintOutputTable = function(output) { * Print how many tests have run so far and how many of those passed. */ ModeTest.printSummary = function() { - document.write(ModeTest.passes + ' passes for ' + ModeTest.tests + ' tests'); + ModeTest.runTests(ModeTest.displayTest); + document.write(ModeTest.passes + ' passes for ' + ModeTest.testCount + ' tests'); } /** diff --git a/test/phantom_driver.js b/test/phantom_driver.js index ad48fd1a84..e5072946c6 100644 --- a/test/phantom_driver.js +++ b/test/phantom_driver.js @@ -7,14 +7,14 @@ page.open("http://localhost:3000/test/index.html", function (status) { } waitFor(function () { return page.evaluate(function () { - var output = document.getElementById('output'); + var output = document.getElementById('status'); if (!output) { return false; } - return (/(\d+ failures?|all passed)$/i).test(output.innerText); + return (/^(\d+ failures?|all passed)/i).test(output.innerText); }); }, function () { var failed = page.evaluate(function () { return window.failed; }); var output = page.evaluate(function () { - return document.getElementById('output').innerText; + return document.getElementById('status').innerText; }); console.log(output); phantom.exit(failed > 0 ? 1 : 0); @@ -27,4 +27,4 @@ function waitFor (test, cb) { } else { setTimeout(function () { waitFor(test, cb); }, 250); } -} \ No newline at end of file +} diff --git a/test/test.js b/test/test.js index cd00f6adab..54ab7ad2aa 100644 --- a/test/test.js +++ b/test/test.js @@ -25,7 +25,7 @@ function byClassName(elt, cls) { var ie_lt8 = /MSIE [1-7]\b/.test(navigator.userAgent); var phantom = /PhantomJS/.test(navigator.userAgent); -test("fromTextArea", function() { +test("core_fromTextArea", function() { var te = document.getElementById("code"); te.value = "CONTENT"; var cm = CodeMirror.fromTextArea(te); @@ -109,7 +109,7 @@ testCM("indent", function(cm) { eq(cm.getLine(1), "\t\t blah();"); }, {value: "if (x) {\nblah();\n}", indentUnit: 3, indentWithTabs: true, tabSize: 8}); -test("defaults", function() { +test("core_defaults", function() { var olddefaults = CodeMirror.defaults, defs = CodeMirror.defaults = {}; for (var opt in olddefaults) defs[opt] = olddefaults[opt]; defs.indentUnit = 5; From 309172b0e21c48ea17f4a46daa6bbf348d65c8f7 Mon Sep 17 00:00:00 2001 From: Marijn Haverbeke Date: Fri, 7 Sep 2012 10:44:52 +0200 Subject: [PATCH 0085/5780] Stop creating unused CodeMirror instances in mode tests --- test/mode_test.js | 6 ++---- 1 file changed, 2 insertions(+), 4 deletions(-) diff --git a/test/mode_test.js b/test/mode_test.js index b257d6ca36..8d9df65e6c 100644 --- a/test/mode_test.js +++ b/test/mode_test.js @@ -41,16 +41,14 @@ ModeTest.testMode = function(name, text, expected, modeName, modeOptions, expect return test( modeName + "_" + name, function(){ - var place = document.getElementById("testground"), cm = CodeMirror(place); - try {return ModeTest.compare(cm, text, expected, mode);} - finally {place.removeChild(cm.getWrapperElement());} + return ModeTest.compare(text, expected, mode); }, expectedFail ); } -ModeTest.compare = function (cm, text, arguments, mode) { +ModeTest.compare = function (text, arguments, mode) { var expectedOutput = []; for (var i = 0; i < arguments.length; i += 2) { From bb6fef03e09e78230c5b18151397fcd6e856bcfa Mon Sep 17 00:00:00 2001 From: Marijn Haverbeke Date: Fri, 7 Sep 2012 10:46:59 +0200 Subject: [PATCH 0086/5780] Don't wait 50 ms between tests There are now enough tests to make this take a serious amount of time. A 0 timeout seems to work just as well. --- test/driver.js | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/test/driver.js b/test/driver.js index 4616eec0d5..5378a44284 100644 --- a/test/driver.js +++ b/test/driver.js @@ -83,7 +83,7 @@ function runTests(callback) { else callback("error", test.name, e.toString()); } if (!quit) { // Run next test - setTimeout(function(){step(i + 1);}, 50); + setTimeout(function(){step(i + 1);}, 0); } else { // Quit tests running = false; return null; From 37190091a1e9b41349108a917717f387fa3d5d72 Mon Sep 17 00:00:00 2001 From: Brandon Frohs Date: Fri, 7 Sep 2012 10:59:48 -0400 Subject: [PATCH 0087/5780] Prevent tests from locking up browser on slower machines (closes #805). --- test/driver.js | 14 ++++++++++++-- 1 file changed, 12 insertions(+), 2 deletions(-) diff --git a/test/driver.js b/test/driver.js index 5378a44284..968a4e98c9 100644 --- a/test/driver.js +++ b/test/driver.js @@ -45,12 +45,16 @@ function runTests(callback) { } } } + var totalTime = 0; function step(i) { if (i === tests.length){ running = false; return callback("done"); } - var test = tests[i], expFail = test.expectedFail; + var test = tests[i] + , expFail = test.expectedFail + , startTime = Date.now() + ; if (debug !== null) { var debugIndex = debug.indexOf(test.name); if (debugIndex !== -1) { @@ -83,7 +87,13 @@ function runTests(callback) { else callback("error", test.name, e.toString()); } if (!quit) { // Run next test - setTimeout(function(){step(i + 1);}, 0); + var delay = 0; + totalTime += (Date.now() - startTime); + if (totalTime > 500){ + totalTime = 0; + delay = 50; + } + setTimeout(function(){step(i + 1);}, delay); } else { // Quit tests running = false; return null; From c7bcf6aadeb2f190b3d395195e977c3b3a28cd6d Mon Sep 17 00:00:00 2001 From: Marijn Haverbeke Date: Fri, 7 Sep 2012 18:18:43 +0200 Subject: [PATCH 0088/5780] Fix a number of ECMA-5-isms in the test runner We do still support IE7/8. Also removes some non-CodeMirror-code-style things like left-aligned commas and semicolons. --- test/driver.js | 24 ++++++++++++++---------- test/index.html | 27 +++++++++++++-------------- 2 files changed, 27 insertions(+), 24 deletions(-) diff --git a/test/driver.js b/test/driver.js index 968a4e98c9..882a8f475b 100644 --- a/test/driver.js +++ b/test/driver.js @@ -3,11 +3,18 @@ var tests = [], debug = null, debugUsed = new Array(), allNames = []; function Failure(why) {this.message = why;} Failure.prototype.toString = function() { return this.message; }; +function indexOf(collection, elt) { + if (collection.indexOf) return collection.indexOf(elt); + for (var i = 0, e = collection.length; i < e; ++i) + if (collection[i] == elt) return i; + return -1; +} + function test(name, run, expectedFail) { // Force unique names var originalName = name; var i = 2; // Second function would be NAME_2 - while(allNames.indexOf(name) !== -1){ + while (indexOf(allNames, name) !== -1){ i++; name = originalName + "_" + i; } @@ -33,7 +40,7 @@ function testCM(name, run, opts, expectedFail) { function runTests(callback) { if (debug) { - if (debug.indexOf("verbose") === 0) { + if (indexOf(debug, "verbose") === 0) { verbose = true; debug.splice(0, 1); } @@ -51,24 +58,21 @@ function runTests(callback) { running = false; return callback("done"); } - var test = tests[i] - , expFail = test.expectedFail - , startTime = Date.now() - ; + var test = tests[i], expFail = test.expectedFail, startTime = +new Date; if (debug !== null) { - var debugIndex = debug.indexOf(test.name); + var debugIndex = indexOf(debug, test.name); if (debugIndex !== -1) { // Remove from array for reporting incorrect tests later debug.splice(debugIndex, 1); } else { var wildcardName = test.name.split("_").shift() + "_*"; - debugIndex = debug.indexOf(wildcardName); + debugIndex = indexOf(debug, wildcardName); if (debugIndex !== -1) { // Remove from array for reporting incorrect tests later debug.splice(debugIndex, 1); debugUsed.push(wildcardName); } else { - debugIndex = debugUsed.indexOf(wildcardName); + debugIndex = indexOf(debugUsed, wildcardName); if (debugIndex !== -1) { totalTests++; } else { @@ -88,7 +92,7 @@ function runTests(callback) { } if (!quit) { // Run next test var delay = 0; - totalTime += (Date.now() - startTime); + totalTime += (+new Date) - startTime; if (totalTime > 500){ totalTime = 0; delay = 50; diff --git a/test/index.html b/test/index.html index 486d6d99e6..af12d844f0 100644 --- a/test/index.html +++ b/test/index.html @@ -41,7 +41,7 @@

    CodeMirror: Test Suite

    window.onload = function() { runHarness(); }; - window.addEventListener('hashchange', function(){ + CodeMirror.connect(window, 'hashchange', function(){ runHarness(); }); @@ -49,17 +49,16 @@

    CodeMirror: Test Suite

    return str.replace(/[<&]/, function(ch) { return ch == "<" ? "<" : "&"; }); } - var output = document.getElementById("output") - , progress = document.getElementById("progress") - , progressRan = document.getElementById("progress_ran").childNodes[0] - , progressTotal = document.getElementById("progress_total").childNodes[0]; - var count = 0 - , failed = 0 - , bad = "" - , running = false // Flag that states tests are running - , quit = false // Flag to quit tests ASAP - , verbose = false // Adds message for *every* test to output - ; + var output = document.getElementById("output"), + progress = document.getElementById("progress"), + progressRan = document.getElementById("progress_ran").childNodes[0], + progressTotal = document.getElementById("progress_total").childNodes[0]; + var count = 0, + failed = 0, + bad = "", + running = false, // Flag that states tests are running + quit = false, // Flag to quit tests ASAP + verbose = false; // Adds message for *every* test to output function runHarness(){ if (running) { @@ -96,12 +95,12 @@

    CodeMirror: Test Suite

    if (!message) throw("must provide message"); var status = document.getElementById("status").childNodes[0]; status.nodeValue = message; - status.parentNode.setAttribute("class", className); + status.parentNode.className = className; } function addOutput(name, className, code){ var newOutput = document.createElement("dl"); var newTitle = document.createElement("dt"); - newTitle.setAttribute("class", className); + newTitle.className = className; newTitle.appendChild(document.createTextNode(name)); newOutput.appendChild(newTitle); var newMessage = document.createElement("dd"); From 4ecc0b58638bce20bd7bdb1d5327b56d09971e56 Mon Sep 17 00:00:00 2001 From: Brandon Frohs Date: Fri, 7 Sep 2012 11:12:58 -0400 Subject: [PATCH 0089/5780] A few small improvements to the test suite: - Move unchanging styles for #progress to style sheet - Forgot to update testCM() when I added the `verbose` option --- test/driver.js | 4 +++- test/index.html | 8 +++++++- 2 files changed, 10 insertions(+), 2 deletions(-) diff --git a/test/driver.js b/test/driver.js index 882a8f475b..a9dbdbf09a 100644 --- a/test/driver.js +++ b/test/driver.js @@ -26,10 +26,12 @@ function test(name, run, expectedFail) { function testCM(name, run, opts, expectedFail) { return test("core_" + name, function() { var place = document.getElementById("testground"), cm = CodeMirror(place, opts); + var successful = false; try { run(cm); + successful = true; } finally { - if (debug) { + if ((debug && !successful) || verbose) { place.style.visibility = ""; } else { place.removeChild(cm.getWrapperElement()); diff --git a/test/index.html b/test/index.html index af12d844f0..85fc7b8d48 100644 --- a/test/index.html +++ b/test/index.html @@ -15,6 +15,12 @@ .fail {color: #e00;} .error {color: #c90;} .done {font-weight: bold;} + #progress { + background: #45d; + color: white; + font-weight: bold; + white-space: pre; + } @@ -23,7 +29,7 @@

    CodeMirror: Test Suite

    A limited set of programmatic sanity tests for CodeMirror.

    -
    Ran 0 of 0 tests
    +
    Ran 0 of 0 tests

    Please enable JavaScript...

    From 66c2c8aa727ee42f0b456adad997d0a8a76e9139 Mon Sep 17 00:00:00 2001 From: dagsta Date: Fri, 7 Sep 2012 19:56:51 +0300 Subject: [PATCH 0090/5780] Extend closetag to work with html / PHP Mixed Code I just added support for closing Tags inside a file in "application/x-httpd-php" Mode including HTML Code --- lib/util/closetag.js | 8 ++++---- 1 file changed, 4 insertions(+), 4 deletions(-) diff --git a/lib/util/closetag.js b/lib/util/closetag.js index 656e93c288..8446ae67e9 100644 --- a/lib/util/closetag.js +++ b/lib/util/closetag.js @@ -41,7 +41,7 @@ var mode = cm.getOption('mode'); - if (mode == 'text/html' || mode == 'xml') { + if (mode == 'text/html' || mode == 'xml' || mode == 'application/x-httpd-php') { /* * Relevant structure of token: @@ -72,7 +72,7 @@ } if (ch == '>') { - var type = state.htmlState ? state.htmlState.type : state.type; // htmlmixed : xml + var type = state.htmlState ? state.htmlState.type : state.html ? state.html.type : state.type; // htmlmixed : xml : php if (tok.className == 'tag' && type == 'closeTag') { throw CodeMirror.Pass; // Don't process the '>' at the end of an end-tag. @@ -84,10 +84,10 @@ tok = cm.getTokenAt(cm.getCursor()); state = tok.state; - type = state.htmlState ? state.htmlState.type : state.type; // htmlmixed : xml + type = state.htmlState ? state.htmlState.type : state.html ? state.html.type : state.type; // htmlmixed : xml : php if (tok.className == 'tag' && type != 'selfcloseTag') { - var tagName = state.htmlState ? state.htmlState.tagName : state.tagName; // htmlmixed : xml + var tagName = state.htmlState ? state.htmlState.tagName : state.html ? state.html.tagName : state.tagName; // htmlmixed : xml : php if (tagName.length > 0 && shouldClose(cm, vd, tagName)) { insertEndTag(cm, indent, pos, tagName); } From ac57ef6aea7165ee846eafb12df527e7c16213c7 Mon Sep 17 00:00:00 2001 From: Marijn Haverbeke Date: Fri, 7 Sep 2012 20:34:34 +0200 Subject: [PATCH 0091/5780] [util/closetag] Clean up handling of different modes --- lib/util/closetag.js | 22 ++++++++++++++++------ 1 file changed, 16 insertions(+), 6 deletions(-) diff --git a/lib/util/closetag.js b/lib/util/closetag.js index 8446ae67e9..566d2a30e0 100644 --- a/lib/util/closetag.js +++ b/lib/util/closetag.js @@ -26,6 +26,17 @@ /** Array of tag names where an end tag is forbidden. */ CodeMirror.defaults['closeTagVoid'] = ['area', 'base', 'br', 'col', 'command', 'embed', 'hr', 'img', 'input', 'keygen', 'link', 'meta', 'param', 'source', 'track', 'wbr']; + function resolveMode(mode) { + if (typeof mode == "object") return mode.name; + if (mode.indexOf("/") > -1) return resolveMode(CodeMirror.mimeModes[mode]); + else return mode; + } + + function innerState(state) { + // htmlmixed uses .htmlState, PHP .html, XML just the top state object + return state.htmlState || state.html || state; + } + /** * Call during key processing to close tags. Handles the key event if the tag is closed, otherwise throws CodeMirror.Pass. * - cm: The editor instance. @@ -39,9 +50,8 @@ throw CodeMirror.Pass; } - var mode = cm.getOption('mode'); - if (mode == 'text/html' || mode == 'xml' || mode == 'application/x-httpd-php') { + if (/^(xml|php|htmlmixed)$/.test(resolveMode(cm.getOption('mode')))) { /* * Relevant structure of token: @@ -72,7 +82,7 @@ } if (ch == '>') { - var type = state.htmlState ? state.htmlState.type : state.html ? state.html.type : state.type; // htmlmixed : xml : php + var type = innerState(state).type; if (tok.className == 'tag' && type == 'closeTag') { throw CodeMirror.Pass; // Don't process the '>' at the end of an end-tag. @@ -84,10 +94,10 @@ tok = cm.getTokenAt(cm.getCursor()); state = tok.state; - type = state.htmlState ? state.htmlState.type : state.html ? state.html.type : state.type; // htmlmixed : xml : php + var type = innerState(state).type; if (tok.className == 'tag' && type != 'selfcloseTag') { - var tagName = state.htmlState ? state.htmlState.tagName : state.html ? state.html.tagName : state.tagName; // htmlmixed : xml : php + var tagName = innerState(state).tagName; if (tagName.length > 0 && shouldClose(cm, vd, tagName)) { insertEndTag(cm, indent, pos, tagName); } @@ -100,7 +110,7 @@ } else if (ch == '/') { if (tok.className == 'tag' && tok.string == '<') { - var tagName = state.htmlState ? (state.htmlState.context ? state.htmlState.context.tagName : '') : (state.context ? state.context.tagName : ''); // htmlmixed : xml + var ctx = innerState(state).context, tagName = ctx && ctx.tagName; if (tagName.length > 0) { completeEndTag(cm, pos, tagName); return; From 4d71ecf44774eb577ad8ce5f2d403cb5da7a9103 Mon Sep 17 00:00:00 2001 From: Marijn Haverbeke Date: Fri, 7 Sep 2012 20:37:55 +0200 Subject: [PATCH 0092/5780] [util/closetag] Fix problem introduced by previous patch --- lib/util/closetag.js | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/lib/util/closetag.js b/lib/util/closetag.js index 566d2a30e0..5b2a900139 100644 --- a/lib/util/closetag.js +++ b/lib/util/closetag.js @@ -110,7 +110,7 @@ } else if (ch == '/') { if (tok.className == 'tag' && tok.string == '<') { - var ctx = innerState(state).context, tagName = ctx && ctx.tagName; + var ctx = innerState(state).context, tagName = ctx ? ctx.tagName : ''; if (tagName.length > 0) { completeEndTag(cm, pos, tagName); return; From 7aa8fa5a9c8473c4ce2aecf95aae378cbb20c334 Mon Sep 17 00:00:00 2001 From: Marijn Haverbeke Date: Fri, 7 Sep 2012 23:13:03 +0200 Subject: [PATCH 0093/5780] [test] Fix use of CodeMirror.connect (now CodeMirror.on) --- test/index.html | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/test/index.html b/test/index.html index 85fc7b8d48..e1bbae432c 100644 --- a/test/index.html +++ b/test/index.html @@ -47,7 +47,7 @@

    CodeMirror: Test Suite

    window.onload = function() { runHarness(); }; - CodeMirror.connect(window, 'hashchange', function(){ + CodeMirror.on(window, 'hashchange', function(){ runHarness(); }); From a1fd0d3d88d863c41da44b0ba0a2e160fbb22183 Mon Sep 17 00:00:00 2001 From: Brandon Frohs Date: Sat, 8 Sep 2012 16:21:50 -0400 Subject: [PATCH 0094/5780] Reset `verbose` flag when running tests. --- test/index.html | 1 + 1 file changed, 1 insertion(+) diff --git a/test/index.html b/test/index.html index e1bbae432c..15935c0b0f 100644 --- a/test/index.html +++ b/test/index.html @@ -84,6 +84,7 @@

    CodeMirror: Test Suite

    count = 0; failed = 0; bad = ""; + verbose = false; debugUsed = Array(); totalTests = tests.length; progressTotal.nodeValue = " of " + totalTests; From 1fb23c619bc820b452fa43fafb914eb5f6cc0e45 Mon Sep 17 00:00:00 2001 From: Brandon Frohs Date: Sun, 9 Sep 2012 01:18:34 -0400 Subject: [PATCH 0095/5780] Fix bug with duplicate test names starting out at the wrong number. If two tests have the name "foo", the second should appear as "foo_2", but instead it appeared as "foo_3". --- test/driver.js | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/test/driver.js b/test/driver.js index a9dbdbf09a..56b848b67d 100644 --- a/test/driver.js +++ b/test/driver.js @@ -15,8 +15,8 @@ function test(name, run, expectedFail) { var originalName = name; var i = 2; // Second function would be NAME_2 while (indexOf(allNames, name) !== -1){ - i++; name = originalName + "_" + i; + i++; } allNames.push(name); // Add test From a1793c2391840885954ed8e81bf9d2d41ebd4eec Mon Sep 17 00:00:00 2001 From: Brandon Frohs Date: Sun, 9 Sep 2012 01:47:00 -0400 Subject: [PATCH 0096/5780] Add a text-shadow behind test progress text so it's readable while the progress bar hasn't passed yet. --- test/index.html | 1 + 1 file changed, 1 insertion(+) diff --git a/test/index.html b/test/index.html index 15935c0b0f..e205b1da70 100644 --- a/test/index.html +++ b/test/index.html @@ -18,6 +18,7 @@ #progress { background: #45d; color: white; + text-shadow: 0 0 1px #45d, 0 0 2px #45d, 0 0 3px #45d; font-weight: bold; white-space: pre; } From 0dc9224870d11c00e5f265156323c4b49a21c057 Mon Sep 17 00:00:00 2001 From: Brandon Frohs Date: Sat, 8 Sep 2012 18:36:09 -0400 Subject: [PATCH 0097/5780] Add tests for CSS mode and fixed found bugs - Added link to tests on mode page - More consistent naming with CSS spec (some of the previous var naming was completely incorrect/misleading) - Improved highlighting - Match vendor prefixes - Check for known properties and values (known properties are bold, unknown are not) - Added highlighting for media queries - Cleaner stacking of state --- mode/css/css.css | 7 + mode/css/css.js | 400 +++++++++++++++++++++++++++++------ mode/css/index.html | 3 + mode/css/test.js | 501 ++++++++++++++++++++++++++++++++++++++++++++ test/index.html | 2 + 5 files changed, 850 insertions(+), 63 deletions(-) create mode 100644 mode/css/css.css create mode 100644 mode/css/test.js diff --git a/mode/css/css.css b/mode/css/css.css new file mode 100644 index 0000000000..caa2afdac8 --- /dev/null +++ b/mode/css/css.css @@ -0,0 +1,7 @@ +/* Change error (warning) so it's not so scary */ +.cm-s-default span.cm-property {font-weight: bold;} +.cm-s-default span.cm-property.cm-error {color: black; font-weight: normal;} + +/* Differentiate color from .cm-def and change error (warning) so it's not so scary */ +.cm-s-default span.cm-attribute {color: #c0c; font-weight: bold;} +.cm-s-default span.cm-attribute.cm-error {color: #c0c; font-weight: normal;} \ No newline at end of file diff --git a/mode/css/css.js b/mode/css/css.js index 9428c4e32e..d7163201dd 100644 --- a/mode/css/css.js +++ b/mode/css/css.js @@ -1,60 +1,196 @@ CodeMirror.defineMode("css", function(config) { var indentUnit = config.indentUnit, type; + + var atMediaTypes = keySet([ + "all", "aural", "braille", "handheld", "print", "projection", "screen", + "tty", "tv", "embossed" + ]); + + var atMediaFeatures = keySet([ + "width", "min-width", "max-width", "height", "min-height", "max-height", + "device-width", "min-device-width", "max-device-width", "device-height", + "min-device-height", "max-device-height", "aspect-ratio", + "min-aspect-ratio", "max-aspect-ratio", "device-aspect-ratio", + "min-device-aspect-ratio", "max-device-aspect-ratio", "color", "min-color", + "max-color", "color-index", "min-color-index", "max-color-index", + "monochrome", "min-monochrome", "max-monochrome", "resolution", + "min-resolution", "max-resolution", "scan", "grid" + ]); - var keywords = keySet(["above", "absolute", "activeborder", "activecaption", "afar", "after-white-space", "ahead", "alias", "all", "all-scroll", - "alternate", "always", "amharic", "amharic-abegede", "antialiased", "appworkspace", "arabic-indic", "armenian", "asterisks", - "auto", "avoid", "background", "backwards", "baseline", "below", "bidi-override", "binary", "bengali", "blink", - "block", "block-axis", "bold", "bolder", "border", "border-box", "both", "bottom", "break-all", "break-word", "button", - "button-bevel", "buttonface", "buttonhighlight", "buttonshadow", "buttontext", "cambodian", "capitalize", "caps-lock-indicator", - "caption", "captiontext", "caret", "cell", "center", "checkbox", "circle", "cjk-earthly-branch", "cjk-heavenly-stem", "cjk-ideographic", - "clear", "clip", "close-quote", "col-resize", "collapse", "compact", "condensed", "contain", "content", "content-box", "context-menu", - "continuous", "copy", "cover", "crop", "cross", "crosshair", "currentcolor", "cursive", "dashed", "decimal", "decimal-leading-zero", "default", - "default-button", "destination-atop", "destination-in", "destination-out", "destination-over", "devanagari", "disc", "discard", "document", - "dot-dash", "dot-dot-dash", "dotted", "double", "down", "e-resize", "ease", "ease-in", "ease-in-out", "ease-out", "element", - "ellipsis", "embed", "end", "ethiopic", "ethiopic-abegede", "ethiopic-abegede-am-et", "ethiopic-abegede-gez", - "ethiopic-abegede-ti-er", "ethiopic-abegede-ti-et", "ethiopic-halehame-aa-er", "ethiopic-halehame-aa-et", - "ethiopic-halehame-am-et", "ethiopic-halehame-gez", "ethiopic-halehame-om-et", "ethiopic-halehame-sid-et", - "ethiopic-halehame-so-et", "ethiopic-halehame-ti-er", "ethiopic-halehame-ti-et", "ethiopic-halehame-tig", "ew-resize", "expanded", - "extra-condensed", "extra-expanded", "fantasy", "fast", "fill", "fixed", "flat", "footnotes", "forwards", "from", "geometricPrecision", - "georgian", "graytext", "groove", "gujarati", "gurmukhi", "hand", "hangul", "hangul-consonant", "hebrew", "help", - "hidden", "hide", "higher", "highlight", "highlighttext", "hiragana", "hiragana-iroha", "horizontal", "hsl", "hsla", "icon", "ignore", - "inactiveborder", "inactivecaption", "inactivecaptiontext", "infinite", "infobackground", "infotext", "inherit", "initial", "inline", - "inline-axis", "inline-block", "inline-table", "inset", "inside", "intrinsic", "invert", "italic", "justify", "kannada", "katakana", - "katakana-iroha", "khmer", "landscape", "lao", "large", "larger", "left", "level", "lighter", "line-through", "linear", "lines", - "list-item", "listbox", "listitem", "local", "logical", "loud", "lower", "lower-alpha", "lower-armenian", "lower-greek", - "lower-hexadecimal", "lower-latin", "lower-norwegian", "lower-roman", "lowercase", "ltr", "malayalam", "match", "media-controls-background", - "media-current-time-display", "media-fullscreen-button", "media-mute-button", "media-play-button", "media-return-to-realtime-button", - "media-rewind-button", "media-seek-back-button", "media-seek-forward-button", "media-slider", "media-sliderthumb", "media-time-remaining-display", - "media-volume-slider", "media-volume-slider-container", "media-volume-sliderthumb", "medium", "menu", "menulist", "menulist-button", - "menulist-text", "menulist-textfield", "menutext", "message-box", "middle", "min-intrinsic", "mix", "mongolian", "monospace", "move", "multiple", - "myanmar", "n-resize", "narrower", "navy", "ne-resize", "nesw-resize", "no-close-quote", "no-drop", "no-open-quote", "no-repeat", "none", - "normal", "not-allowed", "nowrap", "ns-resize", "nw-resize", "nwse-resize", "oblique", "octal", "open-quote", "optimizeLegibility", - "optimizeSpeed", "oriya", "oromo", "outset", "outside", "overlay", "overline", "padding", "padding-box", "painted", "paused", - "persian", "plus-darker", "plus-lighter", "pointer", "portrait", "pre", "pre-line", "pre-wrap", "preserve-3d", "progress", - "push-button", "radio", "read-only", "read-write", "read-write-plaintext-only", "relative", "repeat", "repeat-x", - "repeat-y", "reset", "reverse", "rgb", "rgba", "ridge", "right", "round", "row-resize", "rtl", "run-in", "running", "s-resize", "sans-serif", - "scroll", "scrollbar", "se-resize", "searchfield", "searchfield-cancel-button", "searchfield-decoration", "searchfield-results-button", - "searchfield-results-decoration", "semi-condensed", "semi-expanded", "separate", "serif", "show", "sidama", "single", - "skip-white-space", "slide", "slider-horizontal", "slider-vertical", "sliderthumb-horizontal", "sliderthumb-vertical", "slow", - "small", "small-caps", "small-caption", "smaller", "solid", "somali", "source-atop", "source-in", "source-out", "source-over", - "space", "square", "square-button", "start", "static", "status-bar", "stretch", "stroke", "sub", "subpixel-antialiased", "super", - "sw-resize", "table", "table-caption", "table-cell", "table-column", "table-column-group", "table-footer-group", "table-header-group", - "table-row", "table-row-group", "telugu", "text", "text-bottom", "text-top", "textarea", "textfield", "thai", "thick", "thin", - "threeddarkshadow", "threedface", "threedhighlight", "threedlightshadow", "threedshadow", "tibetan", "tigre", "tigrinya-er", "tigrinya-er-abegede", - "tigrinya-et", "tigrinya-et-abegede", "to", "top", "transparent", "ultra-condensed", "ultra-expanded", "underline", "up", "upper-alpha", "upper-armenian", - "upper-greek", "upper-hexadecimal", "upper-latin", "upper-norwegian", "upper-roman", "uppercase", "urdu", "url", "vertical", "vertical-text", "visible", - "visibleFill", "visiblePainted", "visibleStroke", "visual", "w-resize", "wait", "wave", "white", "wider", "window", "windowframe", "windowtext", - "x-large", "x-small", "xor", "xx-large", "xx-small", "yellow", "-wap-marquee", "-webkit-activelink", "-webkit-auto", "-webkit-baseline-middle", - "-webkit-body", "-webkit-box", "-webkit-center", "-webkit-control", "-webkit-focus-ring-color", "-webkit-grab", "-webkit-grabbing", - "-webkit-gradient", "-webkit-inline-box", "-webkit-left", "-webkit-link", "-webkit-marquee", "-webkit-mini-control", "-webkit-nowrap", "-webkit-pictograph", - "-webkit-right", "-webkit-small-control", "-webkit-text", "-webkit-xxx-large", "-webkit-zoom-in", "-webkit-zoom-out"]); + var propertyKeywords = keySet([ + "align-content", "align-items", "align-self", "alignment-adjust", + "alignment-baseline", "anchor-point", "animation", "animation-delay", + "animation-direction", "animation-duration", "animation-iteration-count", + "animation-name", "animation-play-state", "animation-timing-function", + "appearance", "azimuth", "backface-visibility", "background", + "background-attachment", "background-clip", "background-color", + "background-image", "background-origin", "background-position", + "background-repeat", "background-size", "baseline-shift", "binding", + "bleed", "bookmark-label", "bookmark-level", "bookmark-state", + "bookmark-target", "border", "border-bottom", "border-bottom-color", + "border-bottom-left-radius", "border-bottom-right-radius", + "border-bottom-style", "border-bottom-width", "border-collapse", + "border-color", "border-image", "border-image-outset", + "border-image-repeat", "border-image-slice", "border-image-source", + "border-image-width", "border-left", "border-left-color", + "border-left-style", "border-left-width", "border-radius", "border-right", + "border-right-color", "border-right-style", "border-right-width", + "border-spacing", "border-style", "border-top", "border-top-color", + "border-top-left-radius", "border-top-right-radius", "border-top-style", + "border-top-width", "border-width", "bottom", "box-decoration-break", + "box-shadow", "box-sizing", "break-after", "break-before", "break-inside", + "caption-side", "clear", "clip", "color", "color-profile", "column-count", + "column-fill", "column-gap", "column-rule", "column-rule-color", + "column-rule-style", "column-rule-width", "column-span", "column-width", + "columns", "content", "counter-increment", "counter-reset", "crop", "cue", + "cue-after", "cue-before", "cursor", "direction", "display", + "dominant-baseline", "drop-initial-after-adjust", + "drop-initial-after-align", "drop-initial-before-adjust", + "drop-initial-before-align", "drop-initial-size", "drop-initial-value", + "elevation", "empty-cells", "fit", "fit-position", "flex", "flex-basis", + "flex-direction", "flex-flow", "flex-grow", "flex-shrink", "flex-wrap", + "float", "float-offset", "font", "font-feature-settings", "font-family", + "font-kerning", "font-language-override", "font-size", "font-size-adjust", + "font-stretch", "font-style", "font-synthesis", "font-variant", + "font-variant-alternates", "font-variant-caps", "font-variant-east-asian", + "font-variant-ligatures", "font-variant-numeric", "font-variant-position", + "font-weight", "grid-cell", "grid-column", "grid-column-align", + "grid-column-sizing", "grid-column-span", "grid-columns", "grid-flow", + "grid-row", "grid-row-align", "grid-row-sizing", "grid-row-span", + "grid-rows", "grid-template", "hanging-punctuation", "height", "hyphens", + "icon", "image-orientation", "image-rendering", "image-resolution", + "inline-box-align", "justify-content", "left", "letter-spacing", + "line-break", "line-height", "line-stacking", "line-stacking-ruby", + "line-stacking-shift", "line-stacking-strategy", "list-style", + "list-style-image", "list-style-position", "list-style-type", "margin", + "margin-bottom", "margin-left", "margin-right", "margin-top", + "marker-offset", "marks", "marquee-direction", "marquee-loop", + "marquee-play-count", "marquee-speed", "marquee-style", "max-height", + "max-width", "min-height", "min-width", "move-to", "nav-down", "nav-index", + "nav-left", "nav-right", "nav-up", "opacity", "order", "orphans", "outline", + "outline-color", "outline-offset", "outline-style", "outline-width", + "overflow", "overflow-style", "overflow-wrap", "overflow-x", "overflow-y", + "padding", "padding-bottom", "padding-left", "padding-right", "padding-top", + "page", "page-break-after", "page-break-before", "page-break-inside", + "page-policy", "pause", "pause-after", "pause-before", "perspective", + "perspective-origin", "pitch", "pitch-range", "play-during", "position", + "presentation-level", "punctuation-trim", "quotes", "rendering-intent", + "resize", "rest", "rest-after", "rest-before", "richness", "right", + "rotation", "rotation-point", "ruby-align", "ruby-overhang", + "ruby-position", "ruby-span", "size", "speak", "speak-as", "speak-header", + "speak-numeral", "speak-punctuation", "speech-rate", "stress", "string-set", + "tab-size", "table-layout", "target", "target-name", "target-new", + "target-position", "text-align", "text-align-last", "text-decoration", + "text-decoration-color", "text-decoration-line", "text-decoration-skip", + "text-decoration-style", "text-emphasis", "text-emphasis-color", + "text-emphasis-position", "text-emphasis-style", "text-height", + "text-indent", "text-justify", "text-outline", "text-shadow", + "text-space-collapse", "text-transform", "text-underline-position", + "text-wrap", "top", "transform", "transform-origin", "transform-style", + "transition", "transition-delay", "transition-duration", + "transition-property", "transition-timing-function", "unicode-bidi", + "vertical-align", "visibility", "voice-balance", "voice-duration", + "voice-family", "voice-pitch", "voice-range", "voice-rate", "voice-stress", + "voice-volume", "volume", "white-space", "widows", "width", "word-break", + "word-spacing", "word-wrap", "z-index" + ]); + + var colorKeywords = keySet([ + "black", "silver", "gray", "white", "maroon", "red", "purple", "fuchsia", + "green", "lime", "olive", "yellow", "navy", "blue", "teal", "aqua" + ]); + + var valueKeywords = keySet([ + "above", "absolute", "activeborder", "activecaption", "afar", + "after-white-space", "ahead", "alias", "all", "all-scroll", "alternate", + "always", "amharic", "amharic-abegede", "antialiased", "appworkspace", + "arabic-indic", "armenian", "asterisks", "auto", "avoid", "background", + "backwards", "baseline", "below", "bidi-override", "binary", "bengali", + "blink", "block", "block-axis", "bold", "bolder", "border", "border-box", + "both", "bottom", "break-all", "break-word", "button", "button-bevel", + "buttonface", "buttonhighlight", "buttonshadow", "buttontext", "cambodian", + "capitalize", "caps-lock-indicator", "caption", "captiontext", "caret", + "cell", "center", "checkbox", "circle", "cjk-earthly-branch", + "cjk-heavenly-stem", "cjk-ideographic", "clear", "clip", "close-quote", + "col-resize", "collapse", "compact", "condensed", "contain", "content", + "content-box", "context-menu", "continuous", "copy", "cover", "crop", + "cross", "crosshair", "currentcolor", "cursive", "dashed", "decimal", + "decimal-leading-zero", "default", "default-button", "destination-atop", + "destination-in", "destination-out", "destination-over", "devanagari", + "disc", "discard", "document", "dot-dash", "dot-dot-dash", "dotted", + "double", "down", "e-resize", "ease", "ease-in", "ease-in-out", "ease-out", + "element", "ellipsis", "embed", "end", "ethiopic", "ethiopic-abegede", + "ethiopic-abegede-am-et", "ethiopic-abegede-gez", "ethiopic-abegede-ti-er", + "ethiopic-abegede-ti-et", "ethiopic-halehame-aa-er", + "ethiopic-halehame-aa-et", "ethiopic-halehame-am-et", + "ethiopic-halehame-gez", "ethiopic-halehame-om-et", + "ethiopic-halehame-sid-et", "ethiopic-halehame-so-et", + "ethiopic-halehame-ti-er", "ethiopic-halehame-ti-et", + "ethiopic-halehame-tig", "ew-resize", "expanded", "extra-condensed", + "extra-expanded", "fantasy", "fast", "fill", "fixed", "flat", "footnotes", + "forwards", "from", "geometricPrecision", "georgian", "graytext", "groove", + "gujarati", "gurmukhi", "hand", "hangul", "hangul-consonant", "hebrew", + "help", "hidden", "hide", "higher", "highlight", "highlighttext", + "hiragana", "hiragana-iroha", "horizontal", "hsl", "hsla", "icon", "ignore", + "inactiveborder", "inactivecaption", "inactivecaptiontext", "infinite", + "infobackground", "infotext", "inherit", "initial", "inline", "inline-axis", + "inline-block", "inline-table", "inset", "inside", "intrinsic", "invert", + "italic", "justify", "kannada", "katakana", "katakana-iroha", "khmer", + "landscape", "lao", "large", "larger", "left", "level", "lighter", + "line-through", "linear", "lines", "list-item", "listbox", "listitem", + "local", "logical", "loud", "lower", "lower-alpha", "lower-armenian", + "lower-greek", "lower-hexadecimal", "lower-latin", "lower-norwegian", + "lower-roman", "lowercase", "ltr", "malayalam", "match", + "media-controls-background", "media-current-time-display", + "media-fullscreen-button", "media-mute-button", "media-play-button", + "media-return-to-realtime-button", "media-rewind-button", + "media-seek-back-button", "media-seek-forward-button", "media-slider", + "media-sliderthumb", "media-time-remaining-display", "media-volume-slider", + "media-volume-slider-container", "media-volume-sliderthumb", "medium", + "menu", "menulist", "menulist-button", "menulist-text", + "menulist-textfield", "menutext", "message-box", "middle", "min-intrinsic", + "mix", "mongolian", "monospace", "move", "multiple", "myanmar", "n-resize", + "narrower", "navy", "ne-resize", "nesw-resize", "no-close-quote", "no-drop", + "no-open-quote", "no-repeat", "none", "normal", "not-allowed", "nowrap", + "ns-resize", "nw-resize", "nwse-resize", "oblique", "octal", "open-quote", + "optimizeLegibility", "optimizeSpeed", "oriya", "oromo", "outset", + "outside", "overlay", "overline", "padding", "padding-box", "painted", + "paused", "persian", "plus-darker", "plus-lighter", "pointer", "portrait", + "pre", "pre-line", "pre-wrap", "preserve-3d", "progress", "push-button", + "radio", "read-only", "read-write", "read-write-plaintext-only", "relative", + "repeat", "repeat-x", "repeat-y", "reset", "reverse", "rgb", "rgba", + "ridge", "right", "round", "row-resize", "rtl", "run-in", "running", + "s-resize", "sans-serif", "scroll", "scrollbar", "se-resize", "searchfield", + "searchfield-cancel-button", "searchfield-decoration", + "searchfield-results-button", "searchfield-results-decoration", + "semi-condensed", "semi-expanded", "separate", "serif", "show", "sidama", + "single", "skip-white-space", "slide", "slider-horizontal", + "slider-vertical", "sliderthumb-horizontal", "sliderthumb-vertical", "slow", + "small", "small-caps", "small-caption", "smaller", "solid", "somali", + "source-atop", "source-in", "source-out", "source-over", "space", "square", + "square-button", "start", "static", "status-bar", "stretch", "stroke", + "sub", "subpixel-antialiased", "super", "sw-resize", "table", + "table-caption", "table-cell", "table-column", "table-column-group", + "table-footer-group", "table-header-group", "table-row", "table-row-group", + "telugu", "text", "text-bottom", "text-top", "textarea", "textfield", "thai", + "thick", "thin", "threeddarkshadow", "threedface", "threedhighlight", + "threedlightshadow", "threedshadow", "tibetan", "tigre", "tigrinya-er", + "tigrinya-er-abegede", "tigrinya-et", "tigrinya-et-abegede", "to", "top", + "transparent", "ultra-condensed", "ultra-expanded", "underline", "up", + "upper-alpha", "upper-armenian", "upper-greek", "upper-hexadecimal", + "upper-latin", "upper-norwegian", "upper-roman", "uppercase", "urdu", "url", + "vertical", "vertical-text", "visible", "visibleFill", "visiblePainted", + "visibleStroke", "visual", "w-resize", "wait", "wave", "white", "wider", + "window", "windowframe", "windowtext", "x-large", "x-small", "xor", + "xx-large", "xx-small", "yellow" + ]); function keySet(array) { var keys = {}; for (var i = 0; i < array.length; ++i) keys[array[i]] = true; return keys; } function ret(style, tp) {type = tp; return style;} function tokenBase(stream, state) { var ch = stream.next(); - if (ch == "@") {stream.eatWhile(/[\w\\\-]/); return ret("meta", stream.current());} + if (ch == "@") {stream.eatWhile(/[\w\\\-]/); return ret("def", stream.current());} else if (ch == "/" && stream.eat("*")) { state.tokenize = tokenCComment; return tokenCComment(stream, state); @@ -75,21 +211,35 @@ CodeMirror.defineMode("css", function(config) { } else if (ch == "!") { stream.match(/^\s*\w*/); - return ret("keyword", "important"); + return ret("builtin", "important"); } else if (/\d/.test(ch)) { stream.eatWhile(/[\w.%]/); return ret("number", "unit"); } - else if (/[,.+>*\/]/.test(ch)) { + else if (ch === "-") { + if (/\d/.test(stream.peek())) { + stream.eatWhile(/[\w.%]/); + return ret("number", "unit"); + } else if (stream.match(/^[^-]+-/)) { + return ret("meta", type); + } + } + else if (/[,+>*\/]/.test(ch)) { return ret(null, "select-op"); } - else if (/[;{}:\[\]\(\)]/.test(ch)) { + else if (ch == "." && stream.match(/^\w+/)) { + return ret("qualifier", type); + } + else if (ch == ":") { + return ret("operator", ch); + } + else if (/[;{}\[\]\(\)]/.test(ch)) { return ret(null, ch); } else { stream.eatWhile(/[\w\\\-]/); - return ret("variable", "variable"); + return ret("property", "variable"); } } @@ -138,32 +288,156 @@ CodeMirror.defineMode("css", function(config) { }, token: function(stream, state) { + + // Use these terms when applicable (see http://www.xanthir.com/blog/b4E50) + // + // rule** or **ruleset: + // A selector + braces combo, or an at-rule. + // + // declaration block: + // A sequence of declarations. + // + // declaration: + // A property + colon + value combo. + // + // property value: + // The entire value of a property. + // + // component value: + // A single piece of a property value. Like the 5px in + // text-shadow: 0 0 5px blue;. Can also refer to things that are + // multiple terms, like the 1-4 terms that make up the background-size + // portion of the background shorthand. + // + // term: + // The basic unit of author-facing CSS, like a single number (5), + // dimension (5px), string ("foo"), or function. Officially defined + // by the CSS 2.1 grammar (look for the 'term' production) + // + // + // simple selector: + // A single atomic selector, like a type selector, an attr selector, a + // class selector, etc. + // + // compound selector: + // One or more simple selectors without a combinator. div.example is + // compound, div > .example is not. + // + // complex selector: + // One or more compound selectors chained with combinators. + // + // combinator: + // The parts of selectors that express relationships. There are four + // currently - the space (descendant combinator), the greater-than + // bracket (child combinator), the plus sign (next sibling combinator), + // and the tilda (following sibling combinator). + // + // sequence of selectors: + // One or more of the named type of selector chained with commas. + if (stream.eatSpace()) return null; var style = state.tokenize(stream, state); + // Changing style returned based on context var context = state.stack[state.stack.length-1]; - if (type == "hash" && context != "rule") style = "string-2"; - else if (style == "variable") { - if (context == "rule") style = keywords[stream.current()] ? "keyword" : "number"; - else if (!context || context == "@media{") style = "tag"; + if (style == "property") { + if (context == "propertyValue"){ + if (valueKeywords[stream.current()]) { + style = "string-2"; + } else if (colorKeywords[stream.current()]) { + style = "keyword"; + } else { + style = "variable-2"; + } + } else if (context == "rule") { + if (!propertyKeywords[stream.current()]) { + style += " error"; + } + } else if (!context || context == "@media{") { + style = "tag"; + } else if (context == "@media") { + if (atMediaTypes[stream.current()]) { + style = "attribute"; // Known attribute + } else if (/^(only|not)$/i.test(stream.current())) { + style = "keyword"; + } else if (stream.current().toLowerCase() == "and") { + style = "error"; // "and" is only allowed in @mediaType + } else if (atMediaFeatures[stream.current()]) { + style = "error"; // Known property, should be in @mediaType( + } else { + // Unknown, expecting keyword or attribute, assuming attribute + style = "attribute error"; + } + } else if (context == "@mediaType") { + if (atMediaTypes[stream.current()]) { + style = "attribute"; + } else if (stream.current().toLowerCase() == "and") { + style = "operator"; + } else if (/^(only|not)$/i.test(stream.current())) { + style = "error"; // Only allowed in @media + } else if (atMediaFeatures[stream.current()]) { + style = "error"; // Known property, should be in parentheses + } else { + // Unknown attribute or property, but expecting property (preceded + // by "and"). Should be in parentheses + style = "error"; + } + } else if (context == "@mediaType(") { + if (propertyKeywords[stream.current()]) { + // do nothing, remains "property" + } else if (atMediaTypes[stream.current()]) { + style = "error"; // Known property, should be in parentheses + } else if (stream.current().toLowerCase() == "and") { + style = "operator"; + } else if (/^(only|not)$/i.test(stream.current())) { + style = "error"; // Only allowed in @media + } else { + style += " error"; + } + } else { + style = "error"; + } + } else if (style == "atom") { + if(!context || context == "@media{") { + style = "header"; + } else if (context == "propertyValue") { + if (!/^#([0-9a-fA-f]{3}|[0-9a-fA-f]{6})$/.test(stream.current())) { + style += " error"; + } + } else { + style = "error"; + } + } else if (context == "@media" && type == "{") { + style = "error"; } - if (context == "rule" && /^[\{\};]$/.test(type)) - state.stack.pop(); + // Push/pop context stack if (type == "{") { - if (context == "@media") state.stack[state.stack.length-1] = "@media{"; - else state.stack.push("{"); + if (context == "@media" || context == "@mediaType") { + state.stack.pop(); + state.stack[state.stack.length-1] = "@media{"; + } + else state.stack.push("rule"); + } + else if (type == "}") { + state.stack.pop(); + if (context == "propertyValue") state.stack.pop(); } - else if (type == "}") state.stack.pop(); else if (type == "@media") state.stack.push("@media"); - else if (context == "{" && type != "comment") state.stack.push("rule"); + else if (context == "@media" && /\b(keyword|attribute)\b/.test(style)) + state.stack.push("@mediaType"); + else if (context == "@mediaType" && stream.current() == ",") state.stack.pop(); + else if (context == "@mediaType" && type == "(") state.stack.push("@mediaType("); + else if (context == "@mediaType(" && type == ")") state.stack.pop(); + else if (context == "rule" && type == ":") state.stack.push("propertyValue"); + else if (context == "propertyValue" && type == ";") state.stack.pop(); return style; }, indent: function(state, textAfter) { var n = state.stack.length; if (/^\}/.test(textAfter)) - n -= state.stack[state.stack.length-1] == "rule" ? 2 : 1; + n -= state.stack[state.stack.length-1] == "propertyValue" ? 2 : 1; return state.baseIndent + n * indentUnit; }, diff --git a/mode/css/index.html b/mode/css/index.html index 1a591cbf3d..c77a11c5db 100644 --- a/mode/css/index.html +++ b/mode/css/index.html @@ -6,6 +6,7 @@ + @@ -52,5 +53,7 @@

    CodeMirror: CSS mode

    MIME types defined: text/css.

    +

    Parsing/Highlighting Tests: normal, verbose.

    + diff --git a/mode/css/test.js b/mode/css/test.js new file mode 100644 index 0000000000..f54336e1e5 --- /dev/null +++ b/mode/css/test.js @@ -0,0 +1,501 @@ +// Initiate ModeTest and set defaults +var MT = ModeTest; +MT.modeName = 'css'; +MT.modeOptions = {}; + +// Requires at least one media query +MT.testMode( + 'atMediaEmpty', + '@media { }', + [ + 'def', '@media', + null, ' ', + 'error', '{', + null, ' }' + ] +); + +MT.testMode( + 'atMediaMultiple', + '@media not screen and (color), not print and (color) { }', + [ + 'def', '@media', + null, ' ', + 'keyword', 'not', + null, ' ', + 'attribute', 'screen', + null, ' ', + 'operator', 'and', + null, ' (', + 'property', 'color', + null, '), ', + 'keyword', 'not', + null, ' ', + 'attribute', 'print', + null, ' ', + 'operator', 'and', + null, ' (', + 'property', 'color', + null, ') { }' + ] +); + +MT.testMode( + 'atMediaCheckStack', + '@media screen { } foo { }', + [ + 'def', '@media', + null, ' ', + 'attribute', 'screen', + null, ' { } ', + 'tag', 'foo', + null, ' { }' + ] +); + +MT.testMode( + 'atMediaCheckStack', + '@media screen (color) { } foo { }', + [ + 'def', '@media', + null, ' ', + 'attribute', 'screen', + null, ' (', + 'property', 'color', + null, ') { } ', + 'tag', 'foo', + null, ' { }' + ] +); + +MT.testMode( + 'atMediaCheckStackInvalidAttribute', + '@media foobarhello { } foo { }', + [ + 'def', '@media', + null, ' ', + 'attribute error', 'foobarhello', + null, ' { } ', + 'tag', 'foo', + null, ' { }' + ] +); + +// Error, because "and" is only allowed immediately preceding a media expression +MT.testMode( + 'atMediaInvalidAttribute', + '@media foobarhello { }', + [ + 'def', '@media', + null, ' ', + 'attribute error', 'foobarhello', + null, ' { }' + ] +); + +// Error, because "and" is only allowed immediately preceding a media expression +MT.testMode( + 'atMediaInvalidAnd', + '@media and screen { }', + [ + 'def', '@media', + null, ' ', + 'error', 'and', + null, ' ', + 'attribute', 'screen', + null, ' { }' + ] +); + +// Error, because "not" is only allowed as the first item in each media query +MT.testMode( + 'atMediaInvalidNot', + '@media screen not (not) { }', + [ + 'def', '@media', + null, ' ', + 'attribute', 'screen', + null, ' ', + 'error', 'not', + null, ' (', + 'error', 'not', + null, ') { }' + ] +); + +// Error, because "only" is only allowed as the first item in each media query +MT.testMode( + 'atMediaInvalidOnly', + '@media screen only (only) { }', + [ + 'def', '@media', + null, ' ', + 'attribute', 'screen', + null, ' ', + 'error', 'only', + null, ' (', + 'error', 'only', + null, ') { }' + ] +); + +// Error, because "foobarhello" is neither a known type or property, but +// property was expected (after "and"), and it should be in parenthese. +MT.testMode( + 'atMediaUnknownType', + '@media screen and foobarhello { }', + [ + 'def', '@media', + null, ' ', + 'attribute', 'screen', + null, ' ', + 'operator', 'and', + null, ' ', + 'error', 'foobarhello', + null, ' { }' + ] +); + +// Error, because "color" is not a known type, but is a known property, and +// should be in parentheses. +MT.testMode( + 'atMediaInvalidType', + '@media screen and color { }', + [ + 'def', '@media', + null, ' ', + 'attribute', 'screen', + null, ' ', + 'operator', 'and', + null, ' ', + 'error', 'color', + null, ' { }' + ] +); + +// Error, because "print" is not a known property, but is a known type, +// and should not be in parenthese. +MT.testMode( + 'atMediaInvalidProperty', + '@media screen and (print) { }', + [ + 'def', '@media', + null, ' ', + 'attribute', 'screen', + null, ' ', + 'operator', 'and', + null, ' (', + 'error', 'print', + null, ') { }' + ] +); + +// Soft error, because "foobarhello" is not a known property or type. +MT.testMode( + 'atMediaUnknownProperty', + '@media screen and (foobarhello) { }', + [ + 'def', '@media', + null, ' ', + 'attribute', 'screen', + null, ' ', + 'operator', 'and', + null, ' (', + 'property error', 'foobarhello', + null, ') { }' + ] +); + +MT.testMode( + 'tagSelector', + 'foo { }', + [ + 'tag', 'foo', + null, ' { }' + ] +); + +MT.testMode( + 'classSelector', + '.foo { }', + [ + 'qualifier', '.foo', + null, ' { }' + ] +); + +MT.testMode( + 'idSelector', + '#foo { #foo }', + [ + 'header', '#foo', + null, ' { ', + 'error', '#foo', + null, ' }' + ] +); + +MT.testMode( + 'tagSelectorUnclosed', + 'foo { margin: 0 } bar { }', + [ + 'tag', 'foo', + null, ' { ', + 'property', 'margin', + 'operator', ':', + null, ' ', + 'number', '0', + null, ' } ', + 'tag', 'bar', + null, ' { }' + ] +); + +MT.testMode( + 'tagStringNoQuotes', + 'foo { font-family: hello world; }', + [ + 'tag', 'foo', + null, ' { ', + 'property', 'font-family', + 'operator', ':', + null, ' ', + 'variable-2', 'hello', + null, ' ', + 'variable-2', 'world', + null, '; }' + ] +); + +MT.testMode( + 'tagStringDouble', + 'foo { font-family: "hello world"; }', + [ + 'tag', 'foo', + null, ' { ', + 'property', 'font-family', + 'operator', ':', + null, ' ', + 'string', '"hello world"', + null, '; }' + ] +); + +MT.testMode( + 'tagStringSingle', + 'foo { font-family: \'hello world\'; }', + [ + 'tag', 'foo', + null, ' { ', + 'property', 'font-family', + 'operator', ':', + null, ' ', + 'string', '\'hello world\'', + null, '; }' + ] +); + +MT.testMode( + 'tagColorKeyword', + 'foo { color: black; }', + [ + 'tag', 'foo', + null, ' { ', + 'property', 'color', + 'operator', ':', + null, ' ', + 'keyword', 'black', + null, '; }' + ] +); + +MT.testMode( + 'tagColorHex3', + 'foo { background: #fff; }', + [ + 'tag', 'foo', + null, ' { ', + 'property', 'background', + 'operator', ':', + null, ' ', + 'atom', '#fff', + null, '; }' + ] +); + +MT.testMode( + 'tagColorHex6', + 'foo { background: #ffffff; }', + [ + 'tag', 'foo', + null, ' { ', + 'property', 'background', + 'operator', ':', + null, ' ', + 'atom', '#ffffff', + null, '; }' + ] +); + +MT.testMode( + 'tagColorHex4', + 'foo { background: #ffff; }', + [ + 'tag', 'foo', + null, ' { ', + 'property', 'background', + 'operator', ':', + null, ' ', + 'atom error', '#ffff', + null, '; }' + ] +); + +MT.testMode( + 'tagColorHexInvalid', + 'foo { background: #ffg; }', + [ + 'tag', 'foo', + null, ' { ', + 'property', 'background', + 'operator', ':', + null, ' ', + 'atom error', '#ffg', + null, '; }' + ] +); + +MT.testMode( + 'tagNegativeNumber', + 'foo { margin: -5px; }', + [ + 'tag', 'foo', + null, ' { ', + 'property', 'margin', + 'operator', ':', + null, ' ', + 'number', '-5px', + null, '; }' + ] +); + +MT.testMode( + 'tagPositiveNumber', + 'foo { padding: 5px; }', + [ + 'tag', 'foo', + null, ' { ', + 'property', 'padding', + 'operator', ':', + null, ' ', + 'number', '5px', + null, '; }' + ] +); + +MT.testMode( + 'tagVendor', + 'foo { -foo-box-sizing: -foo-border-box; }', + [ + 'tag', 'foo', + null, ' { ', + 'meta', '-foo-', + 'property', 'box-sizing', + 'operator', ':', + null, ' ', + 'meta', '-foo-', + 'string-2', 'border-box', + null, '; }' + ] +); + +MT.testMode( + 'tagBogusProperty', + 'foo { barhelloworld: 0; }', + [ + 'tag', 'foo', + null, ' { ', + 'property error', 'barhelloworld', + 'operator', ':', + null, ' ', + 'number', '0', + null, '; }' + ] +); + +MT.testMode( + 'tagTwoProperties', + 'foo { margin: 0; padding: 0; }', + [ + 'tag', 'foo', + null, ' { ', + 'property', 'margin', + 'operator', ':', + null, ' ', + 'number', '0', + null, '; ', + 'property', 'padding', + 'operator', ':', + null, ' ', + 'number', '0', + null, '; }' + ] +); +// +//MT.testMode( +// 'tagClass', +// '@media only screen and (min-width: 500px), print {foo.bar#hello { color: black !important; background: #f00; margin: -5px; padding: 5px; -foo-box-sizing: border-box; } /* world */}', +// [ +// 'def', '@media', +// null, ' ', +// 'keyword', 'only', +// null, ' ', +// 'attribute', 'screen', +// null, ' ', +// 'operator', 'and', +// null, ' ', +// 'bracket', '(', +// 'property', 'min-width', +// 'operator', ':', +// null, ' ', +// 'number', '500px', +// 'bracket', ')', +// null, ', ', +// 'attribute', 'print', +// null, ' {', +// 'tag', 'foo', +// 'qualifier', '.bar', +// 'header', '#hello', +// null, ' { ', +// 'property', 'color', +// 'operator', ':', +// null, ' ', +// 'keyword', 'black', +// null, ' ', +// 'builtin', '!important', +// null, '; ', +// 'property', 'background', +// 'operator', ':', +// null, ' ', +// 'atom', '#f00', +// null, '; ', +// 'property', 'padding', +// 'operator', ':', +// null, ' ', +// 'number', '5px', +// null, '; ', +// 'property', 'margin', +// 'operator', ':', +// null, ' ', +// 'number', '-5px', +// null, '; ', +// 'meta', '-foo-', +// 'property', 'box-sizing', +// 'operator', ':', +// null, ' ', +// 'string-2', 'border-box', +// null, '; } ', +// 'comment', '/* world */', +// null, '}' +// ] +//); \ No newline at end of file diff --git a/test/index.html b/test/index.html index e205b1da70..0e00720ce6 100644 --- a/test/index.html +++ b/test/index.html @@ -40,6 +40,8 @@

    CodeMirror: Test Suite

    + + From 3c5caf1aabcc84dbb3de2f83c4ee72b4d34eb6f1 Mon Sep 17 00:00:00 2001 From: Marijn Haverbeke Date: Mon, 10 Sep 2012 09:53:55 +0200 Subject: [PATCH 0098/5780] [css mode] Partially move back to old look --- mode/css/css.css | 7 ------- mode/css/css.js | 4 ++-- mode/css/index.html | 1 - mode/css/test.js | 4 ++-- 4 files changed, 4 insertions(+), 12 deletions(-) delete mode 100644 mode/css/css.css diff --git a/mode/css/css.css b/mode/css/css.css deleted file mode 100644 index caa2afdac8..0000000000 --- a/mode/css/css.css +++ /dev/null @@ -1,7 +0,0 @@ -/* Change error (warning) so it's not so scary */ -.cm-s-default span.cm-property {font-weight: bold;} -.cm-s-default span.cm-property.cm-error {color: black; font-weight: normal;} - -/* Differentiate color from .cm-def and change error (warning) so it's not so scary */ -.cm-s-default span.cm-attribute {color: #c0c; font-weight: bold;} -.cm-s-default span.cm-attribute.cm-error {color: #c0c; font-weight: normal;} \ No newline at end of file diff --git a/mode/css/css.js b/mode/css/css.js index d7163201dd..5e3e233edb 100644 --- a/mode/css/css.js +++ b/mode/css/css.js @@ -211,7 +211,7 @@ CodeMirror.defineMode("css", function(config) { } else if (ch == "!") { stream.match(/^\s*\w*/); - return ret("builtin", "important"); + return ret("keyword", "important"); } else if (/\d/.test(ch)) { stream.eatWhile(/[\w.%]/); @@ -399,7 +399,7 @@ CodeMirror.defineMode("css", function(config) { } } else if (style == "atom") { if(!context || context == "@media{") { - style = "header"; + style = "builtin"; } else if (context == "propertyValue") { if (!/^#([0-9a-fA-f]{3}|[0-9a-fA-f]{6})$/.test(stream.current())) { style += " error"; diff --git a/mode/css/index.html b/mode/css/index.html index c77a11c5db..ae2c3bfcee 100644 --- a/mode/css/index.html +++ b/mode/css/index.html @@ -6,7 +6,6 @@ - diff --git a/mode/css/test.js b/mode/css/test.js index f54336e1e5..4e2d0e8e56 100644 --- a/mode/css/test.js +++ b/mode/css/test.js @@ -228,7 +228,7 @@ MT.testMode( 'idSelector', '#foo { #foo }', [ - 'header', '#foo', + 'builtin', '#foo', null, ' { ', 'error', '#foo', null, ' }' @@ -472,7 +472,7 @@ MT.testMode( // null, ' ', // 'keyword', 'black', // null, ' ', -// 'builtin', '!important', +// 'keyword', '!important', // null, '; ', // 'property', 'background', // 'operator', ':', From adabe4ca729722563bda2266385da92d621cafb1 Mon Sep 17 00:00:00 2001 From: Marijn Haverbeke Date: Mon, 10 Sep 2012 10:28:36 +0200 Subject: [PATCH 0099/5780] On Webkit, add file/line no to test failures caused by errors --- test/driver.js | 5 ++++- 1 file changed, 4 insertions(+), 1 deletion(-) diff --git a/test/driver.js b/test/driver.js index 56b848b67d..43ffb1873e 100644 --- a/test/driver.js +++ b/test/driver.js @@ -90,7 +90,10 @@ function runTests(callback) { } catch(e) { if (expFail) callback("expected", test.name); else if (e instanceof Failure) callback("fail", test.name, e.message); - else callback("error", test.name, e.toString()); + else { + var pos = /\bat .*?([^\/:]+):(\d+):/.exec(e.stack); + callback("error", test.name, e.toString() + (pos ? " (" + pos[1] + ":" + pos[2] + ")" : "")); + } } if (!quit) { // Run next test var delay = 0; From 40c42b58440925f5204fb68913dacb21844ebaeb Mon Sep 17 00:00:00 2001 From: Marijn Haverbeke Date: Fri, 7 Sep 2012 18:05:32 +0200 Subject: [PATCH 0100/5780] Make undo/redo preserve text markers and bookmarks Cleans up the implementation of marked ranges, makes the data structure for markers-within-a-line persistant, and attaches them to the lines stored in the undo history when necessary. Closes #675 --- doc/manual.html | 12 +- lib/codemirror.js | 480 +++++++++++++++++++++------------------------- test/test.js | 26 ++- 3 files changed, 247 insertions(+), 271 deletions(-) diff --git a/doc/manual.html b/doc/manual.html index c54afea35d..9d06987758 100644 --- a/doc/manual.html +++ b/doc/manual.html @@ -639,14 +639,20 @@

    Programming API

    state
    The mode's state at the end of this token.
    -
    markText(from, to, className) → object
    +
    markText(from, to, className, options) → object
    Can be used to mark a range of text with a specific CSS class name. from and to should - be {line, ch} objects. The method will return an + be {line, ch} objects. The options + parameter is optional, and can be an object + with inclusiveLeft and inclusiveRight + properties, which determine whether typing at the left or right + of the marker will cause the new text to become part of the + marker (the default is false for both). The method will return an object with two methods, clear(), which removes the mark, and find(), which returns a {from, to} (both document positions), indicating the current - position of the marked range.
    + position of the marked range, or undefined if the + marker is no longer in the document.
    setBookmark(pos) → object
    Inserts a bookmark, a handle that follows the text around it diff --git a/lib/codemirror.js b/lib/codemirror.js index b83bd7d148..61148f830e 100644 --- a/lib/codemirror.js +++ b/lib/codemirror.js @@ -280,7 +280,7 @@ window.CodeMirror = (function() { // mode holds a mode API object. doc is the tree of Line objects, // frontier is the point up to which the content has been parsed, // and history the undo history (instance of History constructor). - var mode, doc = new BranchChunk([new LeafChunk([new Line("", textHeight())])]), frontier = 0, focused; + var mode, doc = new BranchChunk([new LeafChunk([new Line("", null, textHeight())])]), frontier = 0, focused; loadMode(); // The selection. These are always maintained to point at valid // positions. Inverted is used to remember that the user is @@ -767,13 +767,16 @@ window.CodeMirror = (function() { // Afterwards, set the selection to selFrom, selTo. function updateLines(from, to, newText, selFrom, selTo) { if (suppressEdits) return; + var old = []; + doc.iter(from.line, to.line + 1, function(line) { + old.push(newHL(line.text, line.markedSpans)); + }); if (history) { - var old = []; - doc.iter(from.line, to.line + 1, function(line) { old.push(line.text); }); history.addChange(from.line, newText.length, old); while (history.done.length > options.undoDepth) history.done.shift(); } - updateLinesNoUndo(from, to, newText, selFrom, selTo); + var lines = updateMarkedSpans(hlSpans(old[0]), hlSpans(old[old.length-1]), from.ch, to.ch, newText); + updateLinesNoUndo(from, to, lines, selFrom, selTo); } function unredoHelper(from, to) { if (!from.length) return; @@ -781,11 +784,12 @@ window.CodeMirror = (function() { for (var i = set.length - 1; i >= 0; i -= 1) { var change = set[i]; var replaced = [], end = change.start + change.added; - doc.iter(change.start, end, function(line) { replaced.push(line.text); }); + doc.iter(change.start, end, function(line) { replaced.push(newHL(line.text, line.markedSpans)); }); out.push({start: change.start, added: change.old.length, old: replaced}); var pos = {line: change.start + change.old.length - 1, - ch: editEnd(replaced[replaced.length-1], change.old[change.old.length-1])}; - updateLinesNoUndo({line: change.start, ch: 0}, {line: end - 1, ch: getLine(end-1).text.length}, change.old, pos, pos); + ch: editEnd(hlText(replaced[replaced.length-1]), hlText(change.old[change.old.length-1]))}; + updateLinesNoUndo({line: change.start, ch: 0}, {line: end - 1, ch: getLine(end-1).text.length}, + change.old, pos, pos); } updateInput = true; to.push(out); @@ -793,7 +797,7 @@ window.CodeMirror = (function() { function undo() {unredoHelper(history.done, history.undone);} function redo() {unredoHelper(history.undone, history.done);} - function updateLinesNoUndo(from, to, newText, selFrom, selTo) { + function updateLinesNoUndo(from, to, lines, selFrom, selTo) { if (suppressEdits) return; var recomputeMaxLength = false, maxLineLength = maxLine.text.length; if (!options.lineWrapping) @@ -802,59 +806,52 @@ window.CodeMirror = (function() { }); var nlines = to.line - from.line, firstLine = getLine(from.line), lastLine = getLine(to.line); - var th = textHeight(), wholeLines = from.ch == 0 && to.ch == 0 && newText[newText.length - 1] == ""; - if (!wholeLines) signalLater(firstLine, "change", delayedCallbacks); - // First adjust the line structure, taking some care to leave highlighting intact. - if (wholeLines) { + var lastHL = lines[lines.length-1], th = textHeight(); + + // First adjust the line structure + if (from.ch == 0 && to.ch == 0 && hlText(lastHL) == "") { // This is a whole-line replace. Treated specially to make // sure line objects move the way they are supposed to. var added = [], prevLine = null; - if (from.line) { - prevLine = getLine(from.line - 1); - prevLine.fixMarkEnds(lastLine); - } else lastLine.fixMarkStarts(); - for (var i = 0, e = newText.length - 1; i < e; ++i) - added.push(Line.inheritMarks(newText[i], prevLine, th)); + for (var i = 0, e = lines.length - 1; i < e; ++i) + added.push(new Line(hlText(lines[i]), hlSpans(lines[i]), th)); + lastLine.update(lastLine.text, hlSpans(lastHL), delayedCallbacks); if (nlines) doc.remove(from.line, nlines, delayedCallbacks); if (added.length) doc.insert(from.line, added); } else if (firstLine == lastLine) { - if (newText.length == 1) - firstLine.replace(from.ch, to.ch, newText[0]); - else { - lastLine = firstLine.split(to.ch, newText[newText.length-1]); - firstLine.replace(from.ch, null, newText[0]); - firstLine.fixMarkEnds(lastLine); - var added = []; - for (var i = 1, e = newText.length - 1; i < e; ++i) - added.push(Line.inheritMarks(newText[i], firstLine, th)); - added.push(lastLine); + if (lines.length == 1) { + firstLine.update(firstLine.text.slice(0, from.ch) + hlText(lines[0]) + firstLine.text.slice(to.ch), + hlSpans(lines[0]), delayedCallbacks); + } else { + for (var added = [], i = 1, e = lines.length - 1; i < e; ++i) + added.push(new Line(hlText(lines[i]), hlSpans(lines[i]), th)); + added.push(new Line(hlText(lastHL) + firstLine.text.slice(to.ch), hlSpans(lastHL), th)); + firstLine.update(firstLine.text.slice(0, from.ch) + hlText(lines[0]), hlSpans(lines[0]), delayedCallbacks); doc.insert(from.line + 1, added); } - } else if (newText.length == 1) { - firstLine.replace(from.ch, null, newText[0]); - lastLine.replace(null, to.ch, ""); - firstLine.append(lastLine); + } else if (lines.length == 1) { + firstLine.update(firstLine.text.slice(0, from.ch) + hlText(lines[0]) + lastLine.text.slice(to.ch), + hlSpans(lines[0]), delayedCallbacks); doc.remove(from.line + 1, nlines, delayedCallbacks); } else { var added = []; - firstLine.replace(from.ch, null, newText[0]); - signalLater(lastLine, "change", delayedCallbacks); - lastLine.replace(null, to.ch, newText[newText.length-1]); - firstLine.fixMarkEnds(lastLine); - for (var i = 1, e = newText.length - 1; i < e; ++i) - added.push(Line.inheritMarks(newText[i], firstLine, th)); + firstLine.update(firstLine.text.slice(0, from.ch) + hlText(lines[0]), hlSpans(lines[0]), + delayedCallbacks); + lastLine.update(hlText(lastHL) + lastLine.text.slice(to.ch), hlSpans(lastHL), delayedCallbacks); + for (var i = 1, e = lines.length - 1; i < e; ++i) + added.push(new Line(hlText(lines[i]), hlSpans(lines[i]), th)); if (nlines > 1) doc.remove(from.line + 1, nlines - 1, delayedCallbacks); doc.insert(from.line + 1, added); } if (options.lineWrapping) { - var perLine = Math.max(5, wrapper.clientWidth / charWidth() - 3); - doc.iter(from.line, from.line + newText.length, function(line) { + var perLine = Math.max(5, scroller.clientWidth / charWidth() - 3); + doc.iter(from.line, from.line + lines.length, function(line) { if (line.hidden) return; var guess = (Math.ceil(line.text.length / perLine) || 1) * th; if (guess != line.height) updateLineHeight(line, guess); }); } else { - doc.iter(from.line, from.line + newText.length, function(line) { + doc.iter(from.line, from.line + lines.length, function(line) { var l = line.text; if (!line.hidden && l.length > maxLineLength) { maxLine = line; maxLineLength = l.length; maxLineChanged = true; @@ -868,14 +865,20 @@ window.CodeMirror = (function() { frontier = Math.min(frontier, from.line); startWorker(400); - var lendiff = newText.length - nlines - 1; + var lendiff = lines.length - nlines - 1; // Remember that these lines changed, for updating the display changes.push({from: from.line, to: to.line + 1, diff: lendiff}); - var changeObj = {from: from, to: to, text: newText}; - if (textChanged) { - for (var cur = textChanged; cur.next; cur = cur.next) {} - cur.next = changeObj; - } else textChanged = changeObj; + if (options.onChange) { + // Normalize lines to contain only strings, since that's what + // the change event handler expects + for (var i = 0; i < lines.length; ++i) + if (typeof lines[i] != "string") lines[i] = lines[i].text; + var changeObj = {from: from, to: to, text: lines}; + if (textChanged) { + for (var cur = textChanged; cur.next; cur = cur.next) {} + cur.next = changeObj; + } else textChanged = changeObj; + } // Update the selection setSelection(clipPos(selFrom), clipPos(selTo), true); @@ -1656,74 +1659,71 @@ window.CodeMirror = (function() { (style ? " cm-keymap-" + style : ""); } - function TextMarker() { this.set = []; } + function TextMarker(type, style) { this.lines = []; this.type = type; if (style) this.style = style; } TextMarker.prototype.clear = operation(function() { var min = Infinity, max = -Infinity; - for (var i = 0, e = this.set.length; i < e; ++i) { - var line = this.set[i], mk = line.marked; - if (!mk || !line.parent) continue; - var lineN = lineNo(line); - min = Math.min(min, lineN); max = Math.max(max, lineN); - for (var j = 0; j < mk.length; ++j) - if (mk[j].marker == this) mk.splice(j--, 1); + for (var i = 0; i < this.lines.length; ++i) { + var line = this.lines[i]; + var span = getMarkedSpanFor(line.markedSpans, this, true); + if (span.from != null || span.to != null) { + var lineN = lineNo(line); + min = Math.min(min, lineN); max = Math.max(max, lineN); + } } if (min != Infinity) changes.push({from: min, to: max + 1}); + this.lines.length = 0; }); TextMarker.prototype.find = function() { var from, to; - for (var i = 0, e = this.set.length; i < e; ++i) { - var line = this.set[i], mk = line.marked; - for (var j = 0; j < mk.length; ++j) { - var mark = mk[j]; - if (mark.marker == this) { - if (mark.from != null || mark.to != null) { - var found = lineNo(line); - if (found != null) { - if (mark.from != null) from = {line: found, ch: mark.from}; - if (mark.to != null) to = {line: found, ch: mark.to}; - } - } - } + for (var i = 0; i < this.lines.length; ++i) { + var line = this.lines[i]; + var span = getMarkedSpanFor(line.markedSpans, this); + if (span.from != null || span.to != null) { + var found = lineNo(line); + if (span.from != null) from = {line: found, ch: span.from}; + if (span.to != null) to = {line: found, ch: span.to}; } } - return {from: from, to: to}; + if (this.type == "bookmark") return from; + return from && {from: from, to: to}; }; - function markText(from, to, className) { + function markText(from, to, className, options) { from = clipPos(from); to = clipPos(to); - var tm = new TextMarker(); - if (!posLess(from, to)) return tm; - function add(line, from, to, className) { - getLine(line).addMark(new MarkedText(from, to, className, tm)); - } - if (from.line == to.line) add(from.line, from.ch, to.ch, className); - else { - add(from.line, from.ch, null, className); - for (var i = from.line + 1, e = to.line; i < e; ++i) - add(i, null, null, className); - add(to.line, null, to.ch, className); - } + var marker = new TextMarker("range", className); + if (options && options.inclusiveLeft) marker.inclusiveLeft = true; + if (options && options.inclusiveRight) marker.inclusiveRight = true; + var curLine = from.line; + doc.iter(curLine, to.line + 1, function(line) { + var span = {from: curLine == from.line ? from.ch : null, + to: curLine == to.line ? to.ch : null, + marker: marker}; + (line.markedSpans || (line.markedSpans = [])).push(span); + marker.lines.push(line); + ++curLine; + }); changes.push({from: from.line, to: to.line + 1}); - return tm; + return marker; } function setBookmark(pos) { pos = clipPos(pos); - var bm = new Bookmark(pos.ch); - getLine(pos.line).addMark(bm); - return bm; + var marker = new TextMarker("bookmark"), line = getLine(pos.line); + var span = {from: pos.ch, to: pos.ch, marker: marker}; + (line.markedSpans || (line.markedSpans = [])).push(span); + marker.lines.push(line); + return marker; } function findMarksAt(pos) { pos = clipPos(pos); - var markers = [], marked = getLine(pos.line).marked; - if (!marked) return markers; - for (var i = 0, e = marked.length; i < e; ++i) { - var m = marked[i]; - if ((m.from == null || m.from <= pos.ch) && - (m.to == null || m.to >= pos.ch)) - markers.push(m.marker || m); + var markers = [], spans = getLine(pos.line).markedSpans; + if (spans) for (var i = 0; i < spans.length; ++i) { + var span = spans[i]; + if ((span.from == null || span.from <= pos.ch) && + (span.to == null || span.to >= pos.ch)) + markers.push(span.marker); } return markers; } @@ -2184,7 +2184,6 @@ window.CodeMirror = (function() { if (textChanged) signal(instance, "change", instance, textChanged); if (sc) signal(instance, "cursorActivity", instance); - if (!cbs) debugger; for (var i = 0; i < cbs.length; ++i) cbs[i](instance); if (updated) signal(instance, "update", instance); } @@ -2569,185 +2568,138 @@ window.CodeMirror = (function() { }; CodeMirror.StringStream = StringStream; - function MarkedText(from, to, className, marker) { - this.from = from; this.to = to; this.style = className; this.marker = marker; + function MarkedSpan(from, to, marker) { + this.from = from; this.to = to; this.marker = marker; } - MarkedText.prototype = { - attach: function(line) { this.marker.set.push(line); }, - detach: function(line) { - var ix = indexOf(this.marker.set, line); - if (ix > -1) this.marker.set.splice(ix, 1); - }, - split: function(pos, lenBefore) { - if (this.to <= pos && this.to != null) return null; - var from = this.from < pos || this.from == null ? null : this.from - pos + lenBefore; - var to = this.to == null ? null : this.to - pos + lenBefore; - return new MarkedText(from, to, this.style, this.marker); - }, - dup: function() { return new MarkedText(null, null, this.style, this.marker); }, - clipTo: function(fromOpen, from, toOpen, to, diff) { - if (fromOpen && to > this.from && (to < this.to || this.to == null)) - this.from = null; - else if (this.from != null && this.from >= from) - this.from = Math.max(to, this.from) + diff; - if (toOpen && (from < this.to || this.to == null) && (from > this.from || this.from == null)) - this.to = null; - else if (this.to != null && this.to > from) - this.to = to < this.to ? this.to + diff : from; - }, - isDead: function() { return this.from != null && this.to != null && this.from >= this.to; }, - sameSet: function(x) { return this.marker == x.marker; } - }; - function Bookmark(pos) { - this.from = pos; this.to = pos; this.line = null; + function getMarkedSpanFor(spans, marker, del) { + if (spans) for (var i = 0; i < spans.length; ++i) { + var span = spans[i]; + if (span.marker == marker) { + if (del) spans.splice(i, 1); + return span; + } + } } - Bookmark.prototype = { - attach: function(line) { this.line = line; }, - detach: function(line) { if (this.line == line) this.line = null; }, - split: function(pos, lenBefore) { - if (pos < this.from) { - this.from = this.to = (this.from - pos) + lenBefore; - return this; + + function markedSpansBefore(old, startCh, endCh) { + if (old) for (var i = 0, nw; i < old.length; ++i) { + var span = old[i], marker = span.marker; + var startsBefore = span.from == null || (marker.inclusiveLeft ? span.from <= startCh : span.from < startCh); + if (startsBefore || marker.type == "bookmark" && span.from == startCh && span.from != endCh) { + var endsAfter = span.to == null || (marker.inclusiveRight ? span.to >= startCh : span.to > startCh); + (nw || (nw = [])).push({from: span.from, + to: endsAfter ? null : span.to, + marker: marker}); } - }, - isDead: function() { return this.from > this.to; }, - clipTo: function(fromOpen, from, toOpen, to, diff) { - if ((fromOpen || from < this.from) && (toOpen || to > this.to)) { - this.from = 0; this.to = -1; - } else if (this.from > from) { - this.from = this.to = Math.max(to, this.from) + diff; + } + return nw; + } + + function markedSpansAfter(old, endCh) { + if (old) for (var i = 0, nw; i < old.length; ++i) { + var span = old[i], marker = span.marker; + var endsAfter = span.to == null || (marker.inclusiveRight ? span.to >= endCh : span.to > endCh); + if (endsAfter || marker.type == "bookmark" && span.from == endCh) { + var startsBefore = span.from == null || (marker.inclusiveLeft ? span.from <= endCh : span.from < endCh); + (nw || (nw = [])).push({from: startsBefore ? null : span.from - endCh, + to: span.to == null ? null : span.to - endCh, + marker: marker}); } - }, - sameSet: function(x) { return false; }, - find: function() { - if (!this.line || !this.line.parent) return null; - return {line: lineNo(this.line), ch: this.from}; - }, - clear: function() { - if (this.line) { - var found = indexOf(this.line.marked, this); - if (found != -1) this.line.marked.splice(found, 1); - this.line = null; + } + return nw; + } + + function updateMarkedSpans(oldFirst, oldLast, startCh, endCh, newText) { + if (!oldFirst && !oldLast) return newText; + // Get the spans that 'stick out' on both sides + var first = markedSpansBefore(oldFirst, startCh); + var last = markedSpansAfter(oldLast, endCh); + + // Next, merge those two ends + var sameLine = newText.length == 1, offset = newText[newText.length-1].length + (sameLine ? startCh : 0); + if (first) { + // Fix up .to properties of first + for (var i = 0; i < first.length; ++i) { + var span = first[i]; + if (span.to == null) { + var found = getMarkedSpanFor(last, span.marker); + if (!found) span.to = startCh; + else if (sameLine) span.to = found.to == null ? null : found.to + offset; + } } } - }; + if (last) { + // Fix up .from in last (or move them into first in case of sameLine) + for (var i = 0; i < last.length; ++i) { + var span = last[i]; + if (span.to != null) span.to += offset; + if (span.from == null) { + var found = getMarkedSpanFor(first, span.marker); + if (!found) { + span.from = offset; + if (sameLine) (first || (first = [])).push(span); + } + } else { + span.from += offset; + if (sameLine) (first || (first = [])).push(span); + } + } + } + + var newMarkers = [newHL(newText[0], first)]; + if (!sameLine) { + // Fill gap with whole-line-spans + var gap = newText.length - 2, gapMarkers; + if (gap > 0 && first) + for (var i = 0; i < first.length; ++i) + if (first[i].to == null) + (gapMarkers || (gapMarkers = [])).push({from: null, to: null, marker: first[i].marker}); + for (var i = 0; i < gap; ++i) + newMarkers.push(newHL(newText[i+1], gapMarkers)); + newMarkers.push(newHL(newText[newText.length-1], last)); + } + return newMarkers; + } + + // hl stands for history-line, a data structure that can be either a + // string (line without markers) or a {text, markedSpans} object. + function hlText(val) { return typeof val == "string" ? val : val.text; } + function hlSpans(val) { return typeof val == "string" ? null : val.markedSpans; } + function newHL(text, spans) { return spans ? {text: text, markedSpans: spans} : text; } + + function detachMarkedSpans(line) { + var spans = line.markedSpans; + if (!spans) return; + for (var i = 0; i < spans.length; ++i) { + var lines = spans[i].marker.lines; + var ix = indexOf(lines, line); + lines.splice(ix, 1); + } + line.markedSpans = null; + } + + function attachMarkedSpans(line, spans) { + if (!spans) return; + for (var i = 0; i < spans.length; ++i) + var marker = spans[i].marker.lines.push(line); + line.markedSpans = spans; + } // Line objects. These hold state related to a line, including // highlighting info (the styles array). - function Line(text, height) { + function Line(text, markedSpans, height) { this.text = text; this.height = height; + attachMarkedSpans(this, markedSpans); } - Line.inheritMarks = function(text, orig, height) { - var ln = new Line(text, height), mk = orig && orig.marked; - if (mk) { - for (var i = 0; i < mk.length; ++i) { - if (mk[i].to == null && mk[i].style) { - var newmk = ln.marked || (ln.marked = []), mark = mk[i]; - var nmark = mark.dup(); newmk.push(nmark); nmark.attach(ln); - } - } - } - return ln; - }; Line.prototype = { - // Replace a piece of a line, keeping the markers intact. - replace: function(from, to_, text) { - var mk = this.marked, to = to_ == null ? this.text.length : to_; - this.text = this.text.slice(0, from) + text + this.text.slice(to); - this.order = this.stateAfter = this.styles = null; - if (mk) { - var diff = text.length - (to - from); - for (var i = 0; i < mk.length; ++i) { - var mark = mk[i]; - mark.clipTo(from == null, from || 0, to_ == null, to, diff); - if (mark.isDead()) {mark.detach(this); mk.splice(i--, 1);} - } - } - }, - // Split a part off a line, keeping markers intact. - split: function(pos, textBefore) { - var mk = this.marked; - var taken = new Line(textBefore + this.text.slice(pos), this.height); - if (mk) { - for (var i = 0; i < mk.length; ++i) { - var mark = mk[i]; - var newmark = mark.split(pos, textBefore.length); - if (newmark) { - if (!taken.marked) taken.marked = []; - taken.marked.push(newmark); newmark.attach(taken); - if (newmark == mark) mk.splice(i--, 1); - } - } - } - return taken; - }, - append: function(line) { - var mylen = this.text.length, mk = line.marked, mymk = this.marked; - this.text += line.text; - this.order = this.order || line.order ? null : false; - this.styles = this.stateAfter = null; - if (mymk) { - for (var i = 0; i < mymk.length; ++i) - if (mymk[i].to == null) mymk[i].to = mylen; - } - if (mk && mk.length) { - if (!mymk) this.marked = mymk = []; - outer: for (var i = 0; i < mk.length; ++i) { - var mark = mk[i]; - if (!mark.from) { - for (var j = 0; j < mymk.length; ++j) { - var mymark = mymk[j]; - if (mymark.to == mylen && mymark.sameSet(mark)) { - mymark.to = mark.to == null ? null : mark.to + mylen; - if (mymark.isDead()) { - mymark.detach(this); - mk.splice(i--, 1); - } - continue outer; - } - } - } - mymk.push(mark); - mark.attach(this); - mark.from += mylen; - if (mark.to != null) mark.to += mylen; - } - } - }, - fixMarkEnds: function(other) { - var mk = this.marked, omk = other.marked; - if (!mk) return; - outer: for (var i = 0; i < mk.length; ++i) { - var mark = mk[i], close = mark.to == null; - if (close && omk) { - for (var j = 0; j < omk.length; ++j) { - var om = omk[j]; - if (!om.sameSet(mark) || om.from != null) continue; - if (mark.from == this.text.length && om.to == 0) { - omk.splice(j, 1); - mk.splice(i--, 1); - continue outer; - } else { - close = false; break; - } - } - } - if (close) mark.to = this.text.length; - } - }, - fixMarkStarts: function() { - var mk = this.marked; - if (!mk) return; - for (var i = 0; i < mk.length; ++i) - if (mk[i].from == null) mk[i].from = 0; - }, - addMark: function(mark) { - mark.attach(this); - if (this.marked == null) this.marked = []; - this.marked.push(mark); - this.marked.sort(function(a, b){return (a.from || 0) - (b.from || 0);}); + update: function(text, markedSpans, callbacks) { + this.text = text; + this.stateAfter = this.styles = null; + detachMarkedSpans(this); + attachMarkedSpans(this, markedSpans); + signalLater(this, "change", callbacks); }, // Run the given mode's parser over a line, update the styles // array, which contains alternating fragments of text and CSS @@ -2866,7 +2818,7 @@ window.CodeMirror = (function() { }; } - var st = this.styles, allText = this.text, marked = this.marked; + var st = this.styles, allText = this.text, marked = this.markedSpans; var len = allText.length; function styleToClass(style) { if (!style) return null; @@ -2882,13 +2834,14 @@ window.CodeMirror = (function() { span(str, styleToClass(style)); } } else { + marked.sort(function(a, b) { return a.from - b.from; }); var pos = 0, i = 0, text = "", style, sg = 0; var nextChange = marked[0].from || 0, marks = [], markpos = 0; var advanceMarks = function() { var m; while (markpos < marked.length && ((m = marked[markpos]).from == pos || m.from == null)) { - if (m.style != null) marks.push(m); + if (m.marker.style != null) marks.push(m); ++markpos; } nextChange = markpos < marked.length ? marked[markpos].from : Infinity; @@ -2908,7 +2861,7 @@ window.CodeMirror = (function() { var end = pos + text.length; var appliedStyle = style; for (var j = 0; j < marks.length; ++j) - appliedStyle = (appliedStyle ? appliedStyle + " " : "") + marks[j].style; + appliedStyle = (appliedStyle ? appliedStyle + " " : "") + marks[j].marker.style; span(end > upto ? text.slice(0, upto - pos) : text, appliedStyle); if (end >= upto) {text = text.slice(upto - pos); pos = upto; break;} pos = end; @@ -2921,8 +2874,7 @@ window.CodeMirror = (function() { }, cleanUp: function() { this.parent = null; - if (this.marked) - for (var i = 0, e = this.marked.length; i < e; ++i) this.marked[i].detach(this); + detachMarkedSpans(this); } }; diff --git a/test/test.js b/test/test.js index 54ab7ad2aa..3d2d27b287 100644 --- a/test/test.js +++ b/test/test.js @@ -262,13 +262,14 @@ testCM("markTextSingleLine", function(cm) { var r = cm.markText({line: 0, ch: 3}, {line: 0, ch: 6}, "foo"); cm.replaceRange(test.c, {line: 0, ch: test.a}, {line: 0, ch: test.b}); var f = r.find(); - eq(f.from && f.from.ch, test.f); eq(f.to && f.to.ch, test.t); + eq(f && f.from.ch, test.f); eq(f && f.to.ch, test.t); }); }); testCM("markTextMultiLine", function(cm) { function p(v) { return v && {line: v[0], ch: v[1]}; } forEach([{a: [0, 0], b: [0, 5], c: "", f: [0, 0], t: [2, 5]}, + {a: [0, 0], b: [0, 5], c: "foo\n", f: [1, 0], t: [3, 5]}, {a: [0, 1], b: [0, 10], c: "", f: [0, 1], t: [2, 5]}, {a: [0, 5], b: [0, 6], c: "x", f: [0, 6], t: [2, 5]}, {a: [0, 0], b: [1, 0], c: "", f: [0, 0], t: [1, 5]}, @@ -280,16 +281,33 @@ testCM("markTextMultiLine", function(cm) { {a: [1, 5], b: [2, 5], c: "", f: [0, 5], t: [1, 5]}, {a: [2, 0], b: [2, 3], c: "", f: [0, 5], t: [2, 2]}, {a: [2, 5], b: [3, 0], c: "a\nb", f: [0, 5], t: [2, 5]}, - {a: [2, 3], b: [3, 0], c: "x", f: [0, 5], t: [2, 4]}, + {a: [2, 3], b: [3, 0], c: "x", f: [0, 5], t: [2, 3]}, {a: [1, 1], b: [1, 9], c: "1\n2\n3", f: [0, 5], t: [4, 5]}], function(test) { cm.setValue("aaaaaaaaaa\nbbbbbbbbbb\ncccccccccc\ndddddddd\n"); - var r = cm.markText({line: 0, ch: 5}, {line: 2, ch: 5}, "foo"); + var r = cm.markText({line: 0, ch: 5}, {line: 2, ch: 5}, "CodeMirror-matchingbracket"); cm.replaceRange(test.c, p(test.a), p(test.b)); var f = r.find(); - eqPos(f.from, p(test.f)); eqPos(f.to, p(test.t)); + eqPos(f && f.from, p(test.f)); eqPos(f && f.to, p(test.t)); }); }); +testCM("markTextUndo", function(cm) { + var marker1 = cm.markText({line: 0, ch: 1}, {line: 0, ch: 3}, "CodeMirror-matchingbracket"); + var marker2 = cm.markText({line: 0, ch: 0}, {line: 2, ch: 1}, "CodeMirror-matchingbracket"); + var bookmark = cm.setBookmark({line: 1, ch: 5}); + cm.replaceRange("foo", {line: 0, ch: 2}); + cm.replaceRange("bar\baz\bug\n", {line: 2, ch: 0}, {line: 3, ch: 0}); + cm.setValue(""); + eq(marker1.find(), null); eq(marker2.find(), null); eq(bookmark.find(), null); + cm.undo(); + eqPos(bookmark.find(), {line: 1, ch: 5}); + cm.undo(); cm.undo(); + var m1Pos = marker1.find(), m2Pos = marker2.find(); + eqPos(m1Pos.from, {line: 0, ch: 1}); eqPos(m1Pos.to, {line: 0, ch: 3}); + eqPos(m2Pos.from, {line: 0, ch: 0}); eqPos(m2Pos.to, {line: 2, ch: 1}); + eqPos(bookmark.find(), {line: 1, ch: 5}); +}, {value: "1234\n56789\n00\n"}); + testCM("markClearBetween", function(cm) { cm.setValue("aaa\nbbb\nccc\nddd\n"); cm.markText({line: 0, ch: 0}, {line: 2}, "foo"); From f8c3a5e5b2dd8513837c2647aaf2235dd3ad5886 Mon Sep 17 00:00:00 2001 From: Marijn Haverbeke Date: Mon, 10 Sep 2012 12:15:12 +0200 Subject: [PATCH 0101/5780] Introduce a lst() function to reduce arr[arr.length-1] noise --- lib/codemirror.js | 28 +++++++++++++++------------- 1 file changed, 15 insertions(+), 13 deletions(-) diff --git a/lib/codemirror.js b/lib/codemirror.js index 61148f830e..793eea3a67 100644 --- a/lib/codemirror.js +++ b/lib/codemirror.js @@ -775,7 +775,7 @@ window.CodeMirror = (function() { history.addChange(from.line, newText.length, old); while (history.done.length > options.undoDepth) history.done.shift(); } - var lines = updateMarkedSpans(hlSpans(old[0]), hlSpans(old[old.length-1]), from.ch, to.ch, newText); + var lines = updateMarkedSpans(hlSpans(old[0]), hlSpans(lst(old)), from.ch, to.ch, newText); updateLinesNoUndo(from, to, lines, selFrom, selTo); } function unredoHelper(from, to) { @@ -787,7 +787,7 @@ window.CodeMirror = (function() { doc.iter(change.start, end, function(line) { replaced.push(newHL(line.text, line.markedSpans)); }); out.push({start: change.start, added: change.old.length, old: replaced}); var pos = {line: change.start + change.old.length - 1, - ch: editEnd(hlText(replaced[replaced.length-1]), hlText(change.old[change.old.length-1]))}; + ch: editEnd(hlText(lst(replaced)), hlText(lst(change.old)))}; updateLinesNoUndo({line: change.start, ch: 0}, {line: end - 1, ch: getLine(end-1).text.length}, change.old, pos, pos); } @@ -806,7 +806,7 @@ window.CodeMirror = (function() { }); var nlines = to.line - from.line, firstLine = getLine(from.line), lastLine = getLine(to.line); - var lastHL = lines[lines.length-1], th = textHeight(); + var lastHL = lst(lines), th = textHeight(); // First adjust the line structure if (from.ch == 0 && to.ch == 0 && hlText(lastHL) == "") { @@ -944,7 +944,7 @@ window.CodeMirror = (function() { var line = pos.line + code.length - (to.line - from.line) - 1; var ch = pos.ch; if (pos.line == to.line) - ch += code[code.length-1].length - (to.ch - (to.line == from.line ? from.ch : 0)); + ch += lst(code).length - (to.ch - (to.line == from.line ? from.ch : 0)); return {line: line, ch: ch}; } var end; @@ -962,7 +962,7 @@ window.CodeMirror = (function() { }); } function replaceRange1(code, from, to, computeSel) { - var endch = code.length == 1 ? code[0].length + from.ch : code[code.length-1].length; + var endch = code.length == 1 ? code[0].length + from.ch : lst(code).length; var newSel = computeSel({line: from.line + code.length - 1, ch: endch}); updateLines(from, to, code, newSel.from, newSel.to); } @@ -2617,7 +2617,7 @@ window.CodeMirror = (function() { var last = markedSpansAfter(oldLast, endCh); // Next, merge those two ends - var sameLine = newText.length == 1, offset = newText[newText.length-1].length + (sameLine ? startCh : 0); + var sameLine = newText.length == 1, offset = lst(newText).length + (sameLine ? startCh : 0); if (first) { // Fix up .to properties of first for (var i = 0; i < first.length; ++i) { @@ -2657,7 +2657,7 @@ window.CodeMirror = (function() { (gapMarkers || (gapMarkers = [])).push({from: null, to: null, marker: first[i].marker}); for (var i = 0; i < gap; ++i) newMarkers.push(newHL(newText[i+1], gapMarkers)); - newMarkers.push(newHL(newText[newText.length-1], last)); + newMarkers.push(newHL(lst(newText), last)); } return newMarkers; } @@ -3076,7 +3076,7 @@ window.CodeMirror = (function() { History.prototype = { addChange: function(start, added, old) { this.undone.length = 0; - var time = +new Date, cur = this.done[this.done.length - 1], last = cur && cur[cur.length - 1]; + var time = +new Date, cur = lst(this.done), last = cur && lst(cur); var dtime = time - this.time; if (this.compound && cur && !this.closed) { @@ -3236,10 +3236,12 @@ window.CodeMirror = (function() { var spaceStrs = [""]; function spaceStr(n) { while (spaceStrs.length <= n) - spaceStrs.push(spaceStrs[spaceStrs.length - 1] + " "); + spaceStrs.push(lst(spaceStrs) + " "); return spaceStrs[n]; } + function lst(arr) { return arr[arr.length-1]; } + function selectInput(node) { if (ios) { // Mobile Safari apparently has a bug where select() is broken. node.selectionStart = 0; @@ -3373,7 +3375,7 @@ window.CodeMirror = (function() { function lineRight(line) { var order = getOrder(line); if (!order) return line.text.length; - return bidiRight(order[order.length-1]); + return bidiRight(lst(order)); } function lineStart(line) { var order = getOrder(line); @@ -3597,11 +3599,11 @@ window.CodeMirror = (function() { order[0].from = m[0].length; order.unshift({from: 0, to: m[0].length, level: 0}); } - if (order[order.length-1].level == 1 && (m = str.match(/\s+$/))) { - order[order.length-1].to -= m[0].length; + if (lst(order).level == 1 && (m = str.match(/\s+$/))) { + lst(order).to -= m[0].length; order.push({from: len - m[0].length, to: len, level: 0}); } - if (order[0].level != order[order.length-1].level) + if (order[0].level != lst(order).level) order.push({from: len, to: len, level: order[0].level}); return order; From 256f2257d7793578368047f7bf56c7d89c4fe4b0 Mon Sep 17 00:00:00 2001 From: Marijn Haverbeke Date: Mon, 10 Sep 2012 13:33:01 +0200 Subject: [PATCH 0102/5780] Downcase package name in package.json --- package.json | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/package.json b/package.json index 8658354fc0..9a0d99c2e8 100644 --- a/package.json +++ b/package.json @@ -1,5 +1,5 @@ { - "name": "CodeMirror", + "name": "codemirror", "version":"2.33.0", "main": "codemirror.js", "description": "In-browser code editing made bearable", From 67365d2646551a0dcf9a18e2d1998686907233cd Mon Sep 17 00:00:00 2001 From: Marijn Haverbeke Date: Mon, 10 Sep 2012 16:42:34 +0200 Subject: [PATCH 0103/5780] Add a Common Lisp mode --- doc/compress.html | 1 + index.html | 1 + lib/codemirror.css | 2 +- mode/commonlisp/commonlisp.js | 98 ++++++++++++++++++++ mode/commonlisp/index.html | 165 ++++++++++++++++++++++++++++++++++ 5 files changed, 266 insertions(+), 1 deletion(-) create mode 100644 mode/commonlisp/commonlisp.js create mode 100644 mode/commonlisp/index.html diff --git a/doc/compress.html b/doc/compress.html index eb84a68915..da8b4e101d 100644 --- a/doc/compress.html +++ b/doc/compress.html @@ -62,6 +62,7 @@

    { } CodeMi + diff --git a/index.html b/index.html index 93d04cfde3..9d8818bfce 100644 --- a/index.html +++ b/index.html @@ -38,6 +38,7 @@

    Supported modes:

  • C, C++, C#
  • Clojure
  • CoffeeScript
  • +
  • Common Lisp
  • CSS
  • diff
  • ECL
  • diff --git a/lib/codemirror.css b/lib/codemirror.css index 9688339b78..95cd0c985a 100644 --- a/lib/codemirror.css +++ b/lib/codemirror.css @@ -80,7 +80,7 @@ .cm-s-default .cm-error {color: #f00;} .cm-s-default .cm-qualifier {color: #555;} .cm-s-default .cm-builtin {color: #30a;} -.cm-s-default .cm-bracket {color: #cc7;} +.cm-s-default .cm-bracket {color: #997;} .cm-s-default .cm-tag {color: #170;} .cm-s-default .cm-attribute {color: #00c;} .cm-s-default .cm-header {color: blue;} diff --git a/mode/commonlisp/commonlisp.js b/mode/commonlisp/commonlisp.js new file mode 100644 index 0000000000..b95a8686bb --- /dev/null +++ b/mode/commonlisp/commonlisp.js @@ -0,0 +1,98 @@ +CodeMirror.defineMode("commonlisp", function (config) { + var assumeBody = /^with|^def|^do|^prog|case$|^cond$|bind$|when$|unless$/; + var numLiteral = /^(?:[+\-]?(?:\d+|\d*\.\d+)(?:[efd][+\-]?\d+)?|[+\-]?\d+(?:\/[+\-]?\d+)?|#b[+\-]?[01]+|#o[+\-]?[0-7]+|#x[+\-]?[\da-f]+)/; + var symbol = /[^\s'`,@()\[\]";]/; + var type; + + function readSym(stream) { + while (ch = stream.next()) { + if (ch == "\\") stream.next(); + else if (!symbol.test(ch)) { stream.backUp(1); break; } + } + return stream.current(); + } + + function base(stream, state) { + if (stream.eatSpace()) {type = "ws"; return null;} + if (stream.match(numLiteral)) return "number"; + var ch = stream.next(); + if (ch == "\\") ch = stream.next(); + + if (ch == '"') return (state.tokenize = inString)(stream, state); + else if (ch == "(") { type = "open"; return "bracket"; } + else if (ch == ")" || ch == "]") { type = "close"; return "bracket"; } + else if (ch == ";") { stream.skipToEnd(); type = "ws"; return "comment"; } + else if (/['`,@]/.test(ch)) return null; + else if (ch == "|") { + if (stream.skipTo("|")) { stream.next(); return "symbol"; } + else { stream.skipToEnd(); return "error"; } + } else if (ch == "#") { + if (stream.eat("[")) { type = "open"; return "bracket"; } + else if (stream.eat(/[+\-=\.]/)) return null; + else if (stream.match(/^\d+#/)) return null; + else if (stream.eat("|")) return (state.tokenize = inComment)(stream, state); + else if (stream.eat(":")) { readSym(stream); return "meta"; } + else { stream.next(); return "error"; } + } else { + var name = readSym(stream); + if (name == ".") return null; + type = "symbol"; + if (name == "nil" || name == "t") return "atom"; + if (name.charAt(0) == ":") return "keyword"; + return "variable"; + } + } + + function inString(stream, state) { + var escaped = false, next; + while (next = stream.next()) { + if (next == '"' && !escaped) { state.tokenize = base; break; } + escaped = !escaped && next == "\\"; + } + return "string"; + } + + function inComment(stream, state) { + var next, last; + while (next = stream.next()) { + if (next == "#" && last == "|") { state.tokenize = base; break; } + last = next; + } + type = "ws"; + return "comment"; + } + + return { + startState: function () { + return {ctx: {prev: null, start: 0, indentTo: 0}, tokenize: base}; + }, + + token: function (stream, state) { + if (stream.sol() && typeof state.ctx.indentTo != "number") + state.ctx.indentTo = state.ctx.start + 1; + + type = null; + var style = state.tokenize(stream, state); + if (type != "ws") { + if (state.ctx.indentTo == null) { + if (type == "symbol" && assumeBody.test(stream.current())) + state.ctx.indentTo = state.ctx.start + config.indentUnit; + else + state.ctx.indentTo = "next"; + } else if (state.ctx.indentTo == "next") { + state.ctx.indentTo = stream.column(); + } + } + if (type == "open") state.ctx = {prev: state.ctx, start: stream.column(), indentTo: null}; + else if (type == "close") state.ctx = state.ctx.prev; + return style; + }, + + indent: function (state, textAfter) { + var i = state.ctx.indentTo; + return typeof i == "number" ? i : state.ctx.start + 1; + } + }; +}); + +CodeMirror.defineMIME("text/x-common-lisp", "commonlisp"); diff --git a/mode/commonlisp/index.html b/mode/commonlisp/index.html new file mode 100644 index 0000000000..f9766a844c --- /dev/null +++ b/mode/commonlisp/index.html @@ -0,0 +1,165 @@ + + + + + CodeMirror: Common Lisp mode + + + + + + + +

    CodeMirror: Common Lisp mode

    +
    + + +

    MIME types defined: text/x-common-lisp.

    + + + From 5a2cddfa44710fe8d1667aef9c2b52dd5e0fd2c0 Mon Sep 17 00:00:00 2001 From: Marijn Haverbeke Date: Mon, 10 Sep 2012 16:49:46 +0200 Subject: [PATCH 0104/5780] [commonlisp mode] Fix accidental global --- mode/commonlisp/commonlisp.js | 1 + 1 file changed, 1 insertion(+) diff --git a/mode/commonlisp/commonlisp.js b/mode/commonlisp/commonlisp.js index b95a8686bb..6f2d1d7af5 100644 --- a/mode/commonlisp/commonlisp.js +++ b/mode/commonlisp/commonlisp.js @@ -5,6 +5,7 @@ CodeMirror.defineMode("commonlisp", function (config) { var type; function readSym(stream) { + var ch; while (ch = stream.next()) { if (ch == "\\") stream.next(); else if (!symbol.test(ch)) { stream.backUp(1); break; } From 8f5eecf13efc7c91f4f9d3594933e2b7854630c5 Mon Sep 17 00:00:00 2001 From: Marijn Haverbeke Date: Mon, 10 Sep 2012 16:59:18 +0200 Subject: [PATCH 0105/5780] [commonlisp mode] Various small fixes --- mode/commonlisp/commonlisp.js | 14 ++++++++------ 1 file changed, 8 insertions(+), 6 deletions(-) diff --git a/mode/commonlisp/commonlisp.js b/mode/commonlisp/commonlisp.js index 6f2d1d7af5..7a56b61de5 100644 --- a/mode/commonlisp/commonlisp.js +++ b/mode/commonlisp/commonlisp.js @@ -28,18 +28,20 @@ CodeMirror.defineMode("commonlisp", function (config) { if (stream.skipTo("|")) { stream.next(); return "symbol"; } else { stream.skipToEnd(); return "error"; } } else if (ch == "#") { - if (stream.eat("[")) { type = "open"; return "bracket"; } - else if (stream.eat(/[+\-=\.]/)) return null; - else if (stream.match(/^\d+#/)) return null; - else if (stream.eat("|")) return (state.tokenize = inComment)(stream, state); - else if (stream.eat(":")) { readSym(stream); return "meta"; } - else { stream.next(); return "error"; } + var ch = stream.next(); + if (ch == "[") { type = "open"; return "bracket"; } + else if (/[+\-=\.']/.test(ch)) return null; + else if (/\d/.test(ch) && stream.match(/^\d*#/)) return null; + else if (ch == "|") return (state.tokenize = inComment)(stream, state); + else if (ch == ":") { readSym(stream); return "meta"; } + else return "error"; } else { var name = readSym(stream); if (name == ".") return null; type = "symbol"; if (name == "nil" || name == "t") return "atom"; if (name.charAt(0) == ":") return "keyword"; + if (name.charAt(0) == "&") return "variable-2"; return "variable"; } } From b8c595666f3db0746fd7db6506dd456ebffcb751 Mon Sep 17 00:00:00 2001 From: Marijn Haverbeke Date: Mon, 10 Sep 2012 17:05:20 +0200 Subject: [PATCH 0106/5780] Add node.js-capable runMode implementation --- lib/util/runmode-standalone.js | 105 +++++++++++++++++++++++++++++++++ 1 file changed, 105 insertions(+) create mode 100644 lib/util/runmode-standalone.js diff --git a/lib/util/runmode-standalone.js b/lib/util/runmode-standalone.js new file mode 100644 index 0000000000..789836b288 --- /dev/null +++ b/lib/util/runmode-standalone.js @@ -0,0 +1,105 @@ +/* Just enough of CodeMirror to run runMode under node.js */ + +function splitLines(string){ return string.split(/\r?\n|\r/); }; + +// Counts the column offset in a string, taking tabs into account. +// Used mostly to find indentation. +function countColumn(string, end) { + tabSize = 4; + if (end == null) { + end = string.search(/[^\s\u00a0]/); + if (end == -1) end = string.length; + } + for (var i = 0, n = 0; i < end; ++i) { + if (string.charAt(i) == "\t") n += tabSize - (n % tabSize); + else ++n; + } + return n; +} + +function StringStream(string) { + this.pos = this.start = 0; + this.string = string; +} +StringStream.prototype = { + eol: function() {return this.pos >= this.string.length;}, + sol: function() {return this.pos == 0;}, + peek: function() {return this.string.charAt(this.pos) || null;}, + next: function() { + if (this.pos < this.string.length) + return this.string.charAt(this.pos++); + }, + eat: function(match) { + var ch = this.string.charAt(this.pos); + if (typeof match == "string") var ok = ch == match; + else var ok = ch && (match.test ? match.test(ch) : match(ch)); + if (ok) {++this.pos; return ch;} + }, + eatWhile: function(match) { + var start = this.pos; + while (this.eat(match)){} + return this.pos > start; + }, + eatSpace: function() { + var start = this.pos; + while (/[\s\u00a0]/.test(this.string.charAt(this.pos))) ++this.pos; + return this.pos > start; + }, + skipToEnd: function() {this.pos = this.string.length;}, + skipTo: function(ch) { + var found = this.string.indexOf(ch, this.pos); + if (found > -1) {this.pos = found; return true;} + }, + backUp: function(n) {this.pos -= n;}, + column: function() {return countColumn(this.string, this.start);}, + indentation: function() {return countColumn(this.string);}, + match: function(pattern, consume, caseInsensitive) { + if (typeof pattern == "string") { + function cased(str) {return caseInsensitive ? str.toLowerCase() : str;} + if (cased(this.string).indexOf(cased(pattern), this.pos) == this.pos) { + if (consume !== false) this.pos += pattern.length; + return true; + } + } + else { + var match = this.string.slice(this.pos).match(pattern); + if (match && consume !== false) this.pos += match[0].length; + return match; + } + }, + current: function(){return this.string.slice(this.start, this.pos);} +}; +exports.StringStream = StringStream; + +exports.startState = function(mode, a1, a2) { + return mode.startState ? mode.startState(a1, a2) : true; +}; + +var modes = exports.modes = {}, mimeModes = exports.mimeModes = {}; +exports.defineMode = function(name, mode) { modes[name] = mode; }; +exports.defineMIME = function(mime, spec) { mimeModes[mime] = spec; }; +exports.getMode = function(options, spec) { + if (typeof spec == "string" && mimeModes.hasOwnProperty(spec)) + spec = mimeModes[spec]; + if (typeof spec == "string") + var mname = spec, config = {}; + else if (spec != null) + var mname = spec.name, config = spec; + var mfactory = modes[mname]; + if (!mfactory) throw new Error("Unknown mode: " + spec); + return mfactory(options, config || {}); +}; + +exports.runMode = function(string, modespec, callback) { + var mode = exports.getMode({indentUnit: 2}, modespec); + var lines = splitLines(string), state = exports.startState(mode); + for (var i = 0, e = lines.length; i < e; ++i) { + if (i) callback("\n"); + var stream = new exports.StringStream(lines[i]); + while (!stream.eol()) { + var style = mode.token(stream, state); + callback(stream.current(), style, i, stream.start); + stream.start = stream.pos; + } + } +}; From d46f252a1e3f8fc8f6c705019a13bf5570602013 Mon Sep 17 00:00:00 2001 From: Marijn Haverbeke Date: Mon, 10 Sep 2012 17:19:53 +0200 Subject: [PATCH 0107/5780] Further minimize runmode-standalone.js, fix failing build --- lib/util/runmode-standalone.js | 19 ++----------------- 1 file changed, 2 insertions(+), 17 deletions(-) diff --git a/lib/util/runmode-standalone.js b/lib/util/runmode-standalone.js index 789836b288..afdf044d8d 100644 --- a/lib/util/runmode-standalone.js +++ b/lib/util/runmode-standalone.js @@ -2,21 +2,6 @@ function splitLines(string){ return string.split(/\r?\n|\r/); }; -// Counts the column offset in a string, taking tabs into account. -// Used mostly to find indentation. -function countColumn(string, end) { - tabSize = 4; - if (end == null) { - end = string.search(/[^\s\u00a0]/); - if (end == -1) end = string.length; - } - for (var i = 0, n = 0; i < end; ++i) { - if (string.charAt(i) == "\t") n += tabSize - (n % tabSize); - else ++n; - } - return n; -} - function StringStream(string) { this.pos = this.start = 0; this.string = string; @@ -51,8 +36,8 @@ StringStream.prototype = { if (found > -1) {this.pos = found; return true;} }, backUp: function(n) {this.pos -= n;}, - column: function() {return countColumn(this.string, this.start);}, - indentation: function() {return countColumn(this.string);}, + column: function() {return this.start;}, + indentation: function() {return 0;}, match: function(pattern, consume, caseInsensitive) { if (typeof pattern == "string") { function cased(str) {return caseInsensitive ? str.toLowerCase() : str;} From 122f60475cbd0f4d0f4d37e60582f5b1bbaf1c64 Mon Sep 17 00:00:00 2001 From: Marijn Haverbeke Date: Tue, 11 Sep 2012 13:40:39 +0200 Subject: [PATCH 0108/5780] Add startStyle and endStyle options for markers Closes #819 --- doc/manual.html | 30 ++++++++++++++++++++---------- lib/codemirror.js | 14 +++++++++----- mode/xml/index.html | 2 +- 3 files changed, 30 insertions(+), 16 deletions(-) diff --git a/doc/manual.html b/doc/manual.html index 9d06987758..d54d9b71fc 100644 --- a/doc/manual.html +++ b/doc/manual.html @@ -643,16 +643,26 @@

    Programming API

    Can be used to mark a range of text with a specific CSS class name. from and to should be {line, ch} objects. The options - parameter is optional, and can be an object - with inclusiveLeft and inclusiveRight - properties, which determine whether typing at the left or right - of the marker will cause the new text to become part of the - marker (the default is false for both). The method will return an - object with two methods, clear(), which removes the - mark, and find(), which returns a {from, - to} (both document positions), indicating the current - position of the marked range, or undefined if the - marker is no longer in the document.
    + parameter is optional. When given, it should be an object that + may contain the following configuration options: +
    +
    inclusiveLeft
    Determines whether + text inserted on the left of the marker will end up inside + or outside of it.
    +
    inclusiveRight
    Like inclusiveLeft, + but for the right side.
    +
    startStyle
    Can be used to specify + an extra CSS class to be applied to the leftmost span that + is part of the marker.
    +
    endStyle
    Equivalent + to startStyle, but for the rightmost span.
    +
    + The method will return an object with two methods, + clear(), which removes the mark, + and find(), which returns a {from, to} + (both document positions), indicating the current position of + the marked range, or undefined if the marker is no + longer in the document.
    setBookmark(pos) → object
    Inserts a bookmark, a handle that follows the text around it diff --git a/lib/codemirror.js b/lib/codemirror.js index 793eea3a67..3fc7b206a7 100644 --- a/lib/codemirror.js +++ b/lib/codemirror.js @@ -1692,8 +1692,8 @@ window.CodeMirror = (function() { function markText(from, to, className, options) { from = clipPos(from); to = clipPos(to); var marker = new TextMarker("range", className); - if (options && options.inclusiveLeft) marker.inclusiveLeft = true; - if (options && options.inclusiveRight) marker.inclusiveRight = true; + if (options) for (var opt in options) if (options.hasOwnProperty(opt)) + marker[opt] = options[opt]; var curLine = from.line; doc.iter(curLine, to.line + 1, function(line) { var span = {from: curLine == from.line ? from.ch : null, @@ -2841,7 +2841,7 @@ window.CodeMirror = (function() { var m; while (markpos < marked.length && ((m = marked[markpos]).from == pos || m.from == null)) { - if (m.marker.style != null) marks.push(m); + if (m.marker.type == "range") marks.push(m); ++markpos; } nextChange = markpos < marked.length ? marked[markpos].from : Infinity; @@ -2860,8 +2860,12 @@ window.CodeMirror = (function() { if (text) { var end = pos + text.length; var appliedStyle = style; - for (var j = 0; j < marks.length; ++j) - appliedStyle = (appliedStyle ? appliedStyle + " " : "") + marks[j].marker.style; + for (var j = 0; j < marks.length; ++j) { + var mark = marks[j]; + appliedStyle = (appliedStyle ? appliedStyle + " " : "") + mark.marker.style; + if (mark.marker.endStyle && mark.to === Math.min(end, upto)) appliedStyle += " " + mark.marker.endStyle; + if (mark.marker.startStyle && mark.from === pos) appliedStyle += " " + mark.marker.startStyle; + } span(end > upto ? text.slice(0, upto - pos) : text, appliedStyle); if (end >= upto) {text = text.slice(upto - pos); pos = upto; break;} pos = end; diff --git a/mode/xml/index.html b/mode/xml/index.html index 9628d954c0..49a627de73 100644 --- a/mode/xml/index.html +++ b/mode/xml/index.html @@ -6,7 +6,7 @@ - + From a9b05dc0ec4559c5f491fad00ecbb44777343c4c Mon Sep 17 00:00:00 2001 From: Marijn Haverbeke Date: Tue, 11 Sep 2012 16:26:26 +0200 Subject: [PATCH 0109/5780] Remove leftover bracketHighlighted variable --- lib/codemirror.js | 7 ------- 1 file changed, 7 deletions(-) diff --git a/lib/codemirror.js b/lib/codemirror.js index 3fc7b206a7..3dbb969da3 100644 --- a/lib/codemirror.js +++ b/lib/codemirror.js @@ -296,9 +296,6 @@ window.CodeMirror = (function() { var delayedCallbacks, alignWidgets; // Current visible range (may be bigger than the view window). var displayOffset = 0, showingFrom = 0, showingTo = 0, lastSizeC = 0; - // bracketHighlighted is used to remember that a bracket has been - // marked. - var bracketHighlighted; // Tracks the maximum line length so that the horizontal scrollbar // can be kept static when scrolling. var maxLine = getLine(0), updateMaxLine = false, maxLineChanged = true; @@ -753,10 +750,6 @@ window.CodeMirror = (function() { if (focused) { signal(instance, "blur", instance); focused = false; - if (bracketHighlighted) - operation(function(){ - if (bracketHighlighted) { bracketHighlighted(); bracketHighlighted = null; } - })(); scroller.className = scroller.className.replace(" CodeMirror-focused", ""); } clearInterval(blinker); From 4cff3f1f2f9ecf5b45e80b9366c69af1daa9b74e Mon Sep 17 00:00:00 2001 From: Marijn Haverbeke Date: Wed, 12 Sep 2012 10:46:46 +0200 Subject: [PATCH 0110/5780] Add new state introspection mechanism for nested modes And move closetag over to it. This makes the code that gets the XML state out of the mode actually sound and extensible. Issue #820 --- doc/manual.html | 22 +++++++++ lib/codemirror.js | 16 ++++++- lib/util/closetag.js | 79 +++++++++++++++---------------- lib/util/multiplex.js | 6 ++- lib/util/overlay.js | 4 +- mode/htmlembedded/htmlembedded.js | 6 ++- mode/htmlmixed/htmlmixed.js | 13 ++--- mode/php/php.js | 10 ++-- 8 files changed, 98 insertions(+), 58 deletions(-) diff --git a/doc/manual.html b/doc/manual.html index d54d9b71fc..82298ea158 100644 --- a/doc/manual.html +++ b/doc/manual.html @@ -565,6 +565,11 @@

    Programming API

    getOption(option) → value
    Retrieves the current value of the given option for this editor instance.
    +
    getMode() → object
    +
    Gets the mode object for the editor. Note that this is + distinct from getOption("mode"), which gives you + the mode specification, rather than the resolved, instantiated + mode object.
    on(type, func)
    Register an event handler for the given event type (a @@ -1249,6 +1254,14 @@

    Writing CodeMirror Modes

    where mode is the mode that created the given state.

    +

    In a nested mode, it is recommended to add an + extra methods, innerMode which, given a state object, + returns a {state, mode} object with the inner mode + and its state for the current position. These are used by utility + scripts such as the autoformatter and + the tag closer to get context + information.

    +

    To make indentation work properly in a nested parser, it is advisable to give the startState method of modes that are intended to be nested an optional argument that provides the @@ -1267,6 +1280,15 @@

    Writing CodeMirror Modes

    mode, as in the mode option.

    +

    Sometimes, it is useful to add or override mode + object properties from external code. + The CodeMirror.extendMode can be used to add + properties to mode objects produced for a specific mode. Its first + argument is the name of the mode, its second an object that + specifies the properties that should be added. This is mostly + useful to add utilities that can later be looked + up getMode.

    +

    Contents

    diff --git a/lib/codemirror.js b/lib/codemirror.js index 3dbb969da3..c54fba7594 100644 --- a/lib/codemirror.js +++ b/lib/codemirror.js @@ -40,6 +40,7 @@ window.CodeMirror = (function() { optionHandlers[option](instance, value); }, getOption: function(option) {return options[option];}, + getMode: function() {return mode;}, undo: operation(undo), redo: operation(redo), indentLine: operation(function(n, dir) { @@ -2266,7 +2267,13 @@ window.CodeMirror = (function() { var spec = CodeMirror.resolveMode(spec); var mfactory = modes[spec.name]; if (!mfactory) return CodeMirror.getMode(options, "text/plain"); - return mfactory(options, spec); + var modeObj = mfactory(options, spec); + if (modeExtensions.hasOwnProperty(spec.name)) { + var exts = modeExtensions[spec.name]; + for (var prop in exts) if (exts.hasOwnProperty(prop)) modeObj[prop] = exts[prop]; + } + modeObj.name = spec.name; + return modeObj; }; CodeMirror.listModes = function() { var list = []; @@ -2291,6 +2298,13 @@ window.CodeMirror = (function() { optionHandlers[name] = handler; }; + var modeExtensions = CodeMirror.modeExtensions = {}; + CodeMirror.extendMode = function(mode, properties) { + var exts = modeExtensions.hasOwnProperty(mode) ? modeExtensions[mode] : (modeExtensions[mode] = {}); + for (var prop in properties) if (properties.hasOwnProperty(prop)) + exts[prop] = properties[prop]; + }; + var commands = CodeMirror.commands = { selectAll: function(cm) {cm.setSelection({line: 0, ch: 0}, {line: cm.lineCount() - 1});}, killLine: function(cm) { diff --git a/lib/util/closetag.js b/lib/util/closetag.js index 5b2a900139..83dc2c7e75 100644 --- a/lib/util/closetag.js +++ b/lib/util/closetag.js @@ -26,16 +26,15 @@ /** Array of tag names where an end tag is forbidden. */ CodeMirror.defaults['closeTagVoid'] = ['area', 'base', 'br', 'col', 'command', 'embed', 'hr', 'img', 'input', 'keygen', 'link', 'meta', 'param', 'source', 'track', 'wbr']; - function resolveMode(mode) { - if (typeof mode == "object") return mode.name; - if (mode.indexOf("/") > -1) return resolveMode(CodeMirror.mimeModes[mode]); - else return mode; + function innerState(cm, state) { + for (var mode = cm.getMode(); mode.innerMode;) { + var info = mode.innerMode(state); + mode = info.mode; + state = info.state; + } + if (mode.name == "xml") return state; } - function innerState(state) { - // htmlmixed uses .htmlState, PHP .html, XML just the top state object - return state.htmlState || state.html || state; - } /** * Call during key processing to close tags. Handles the key event if the tag is closed, otherwise throws CodeMirror.Pass. @@ -50,39 +49,34 @@ throw CodeMirror.Pass; } + /* + * Relevant structure of token: + * + * htmlmixed + * className + * state + * htmlState + * type + * tagName + * context + * tagName + * mode + * + * xml + * className + * state + * tagName + * type + */ - if (/^(xml|php|htmlmixed)$/.test(resolveMode(cm.getOption('mode')))) { - - /* - * Relevant structure of token: - * - * htmlmixed - * className - * state - * htmlState - * type - * tagName - * context - * tagName - * mode - * - * xml - * className - * state - * tagName - * type - */ - - var pos = cm.getCursor(); - var tok = cm.getTokenAt(pos); - var state = tok.state; - - if (state.mode && state.mode != 'html') { - throw CodeMirror.Pass; // With htmlmixed, we only care about the html sub-mode. - } + var pos = cm.getCursor(); + var tok = cm.getTokenAt(pos); + var state = innerState(cm, tok.state); + + if (state) { if (ch == '>') { - var type = innerState(state).type; + var type = state.type; if (tok.className == 'tag' && type == 'closeTag') { throw CodeMirror.Pass; // Don't process the '>' at the end of an end-tag. @@ -93,11 +87,12 @@ cm.setCursor(pos); tok = cm.getTokenAt(cm.getCursor()); - state = tok.state; - var type = innerState(state).type; + state = innerState(cm, tok.state); + if (!state) throw CodeMirror.Pass; + var type = state.type; if (tok.className == 'tag' && type != 'selfcloseTag') { - var tagName = innerState(state).tagName; + var tagName = state.tagName; if (tagName.length > 0 && shouldClose(cm, vd, tagName)) { insertEndTag(cm, indent, pos, tagName); } @@ -110,7 +105,7 @@ } else if (ch == '/') { if (tok.className == 'tag' && tok.string == '<') { - var ctx = innerState(state).context, tagName = ctx ? ctx.tagName : ''; + var ctx = state.context, tagName = ctx ? ctx.tagName : ''; if (tagName.length > 0) { completeEndTag(cm, pos, tagName); return; diff --git a/lib/util/multiplex.js b/lib/util/multiplex.js index b7c1838f62..214730839d 100644 --- a/lib/util/multiplex.js +++ b/lib/util/multiplex.js @@ -68,6 +68,10 @@ CodeMirror.multiplexingMode = function(outer /*, others */) { return mode.indent(state.innerActive ? state.inner : state.outer, textAfter); }, - electricChars: outer.electricChars + electricChars: outer.electricChars, + + innerMode: function(state) { + return state.inner ? {state: state.inner, mode: state.innerActive.mode} : {state: state.outer, mode: outer}; + } }; }; diff --git a/lib/util/overlay.js b/lib/util/overlay.js index 1d5df6c64c..c38d0ca6bb 100644 --- a/lib/util/overlay.js +++ b/lib/util/overlay.js @@ -47,6 +47,8 @@ CodeMirror.overlayMode = CodeMirror.overlayParser = function(base, overlay, comb indent: base.indent && function(state, textAfter) { return base.indent(state.base, textAfter); }, - electricChars: base.electricChars + electricChars: base.electricChars, + + innerMode: function(state) { return {state: state.base, mode: base}; } }; }; diff --git a/mode/htmlembedded/htmlembedded.js b/mode/htmlembedded/htmlembedded.js index a8a7e6e603..195d48e328 100644 --- a/mode/htmlembedded/htmlembedded.js +++ b/mode/htmlembedded/htmlembedded.js @@ -58,8 +58,12 @@ CodeMirror.defineMode("htmlembedded", function(config, parserConfig) { }; }, + electricChars: "/{}:", - electricChars: "/{}:" + innerMode: function(state) { + if (state.token == scriptingDispatch) return {state: state.scriptState, mode: scriptingMode}; + else return {state: state.htmlState, mode: htmlMixedMode}; + } }; }, "htmlmixed"); diff --git a/mode/htmlmixed/htmlmixed.js b/mode/htmlmixed/htmlmixed.js index 5f2fc238c9..4652848296 100644 --- a/mode/htmlmixed/htmlmixed.js +++ b/mode/htmlmixed/htmlmixed.js @@ -1,4 +1,4 @@ -CodeMirror.defineMode("htmlmixed", function(config, parserConfig) { +CodeMirror.defineMode("htmlmixed", function(config) { var htmlMode = CodeMirror.getMode(config, {name: "xml", htmlMode: true}); var jsMode = CodeMirror.getMode(config, "javascript"); var cssMode = CodeMirror.getMode(config, "css"); @@ -9,12 +9,10 @@ CodeMirror.defineMode("htmlmixed", function(config, parserConfig) { if (/^script$/i.test(state.htmlState.context.tagName)) { state.token = javascript; state.localState = jsMode.startState(htmlMode.indent(state.htmlState, "")); - state.mode = "javascript"; } else if (/^style$/i.test(state.htmlState.context.tagName)) { state.token = css; state.localState = cssMode.startState(htmlMode.indent(state.htmlState, "")); - state.mode = "css"; } } return style; @@ -33,7 +31,6 @@ CodeMirror.defineMode("htmlmixed", function(config, parserConfig) { if (stream.match(/^<\/\s*script\s*>/i, false)) { state.token = html; state.localState = null; - state.mode = "html"; return html(stream, state); } return maybeBackup(stream, /<\/\s*script\s*>/, @@ -43,7 +40,6 @@ CodeMirror.defineMode("htmlmixed", function(config, parserConfig) { if (stream.match(/^<\/\s*style\s*>/i, false)) { state.token = html; state.localState = null; - state.mode = "html"; return html(stream, state); } return maybeBackup(stream, /<\/\s*style\s*>/, @@ -76,7 +72,12 @@ CodeMirror.defineMode("htmlmixed", function(config, parserConfig) { return cssMode.indent(state.localState, textAfter); }, - electricChars: "/{}:" + electricChars: "/{}:", + + innerMode: function(state) { + var mode = state.token == html ? htmlMode : state.token == javascript ? jsMode : cssMode; + return {state: state.localState || state.htmlState, mode: mode}; + } }; }, "xml", "javascript", "css"); diff --git a/mode/php/php.js b/mode/php/php.js index dbe774fa59..b94317c749 100644 --- a/mode/php/php.js +++ b/mode/php/php.js @@ -56,14 +56,13 @@ var phpMode = CodeMirror.getMode(config, phpConfig); function dispatch(stream, state) { // TODO open PHP inside text/css - var isPHP = state.mode == "php"; + var isPHP = state.curMode == phpMode; if (stream.sol() && state.pending != '"') state.pending = null; if (state.curMode == htmlMode) { if (stream.match(/^<\?\w*/)) { state.curMode = phpMode; state.curState = state.php; state.curClose = "?>"; - state.mode = "php"; return "meta"; } if (state.pending == '"') { @@ -86,13 +85,11 @@ state.curMode = jsMode; state.curState = jsMode.startState(htmlMode.indent(state.curState, "")); state.curClose = /^<\/\s*script\s*>/i; - state.mode = "javascript"; } else if (/^style$/i.test(state.curState.context.tagName)) { state.curMode = cssMode; state.curState = cssMode.startState(htmlMode.indent(state.curState, "")); state.curClose = /^<\/\s*style\s*>/i; - state.mode = "css"; } } return style; @@ -101,7 +98,6 @@ state.curMode = htmlMode; state.curState = state.html; state.curClose = null; - state.mode = "html"; if (isPHP) return "meta"; else return dispatch(stream, state); } else { @@ -141,7 +137,9 @@ return state.curMode.indent(state.curState, textAfter); }, - electricChars: "/{}:" + electricChars: "/{}:", + + innerMode: function(state) { return {state: state.curState, mode: state.curMode}; } }; }, "xml", "clike", "javascript", "css"); CodeMirror.defineMIME("application/x-httpd-php", "php"); From 4c57f46eac03b4a612d3137ea0b06a3c37024ff0 Mon Sep 17 00:00:00 2001 From: Marijn Haverbeke Date: Wed, 12 Sep 2012 11:57:00 +0200 Subject: [PATCH 0111/5780] [util/formatting] Move over to new mode extension/introspection This makes the formatter easier to adjust to new mode, and cleans it up somewhat. --- doc/manual.html | 13 +- lib/codemirror.js | 8 + lib/util/closetag.js | 7 +- lib/util/formatting.js | 393 +++++++++++++++-------------------------- mode/gfm/gfm.js | 7 +- 5 files changed, 165 insertions(+), 263 deletions(-) diff --git a/doc/manual.html b/doc/manual.html index 82298ea158..ebff9573cb 100644 --- a/doc/manual.html +++ b/doc/manual.html @@ -1054,6 +1054,11 @@

    Add-ons

    Depends on the searchcursor add-on. Demo here.
    +
    formatting.js
    +
    Adds commentRange, autoIndentRange, + and autoFormatRange methods that, respectively, + comment (or uncomment), indent, or format (add line breaks) a + range of code. Demo here.
    closetag.js
    Provides utility functions for adding automatic tag closing to XML modes. See @@ -1258,9 +1263,11 @@

    Writing CodeMirror Modes

    extra methods, innerMode which, given a state object, returns a {state, mode} object with the inner mode and its state for the current position. These are used by utility - scripts such as the autoformatter and - the tag closer to get context - information.

    + scripts such as the autoformatter + and the tag closer to get context + information. Use the CodeMirror.innerMode helper + function to, starting from a mode and a state, recursively walk + down to the innermost mode and state.

    To make indentation work properly in a nested parser, it is advisable to give the startState method of modes that diff --git a/lib/codemirror.js b/lib/codemirror.js index c54fba7594..5ea98ba4f8 100644 --- a/lib/codemirror.js +++ b/lib/codemirror.js @@ -2518,6 +2518,14 @@ window.CodeMirror = (function() { return mode.startState ? mode.startState(a1, a2) : true; } CodeMirror.startState = startState; + CodeMirror.innerMode = function(mode, state) { + while (mode.innerMode) { + var info = mode.innerMode(state); + state = info.state; + mode = info.mode; + } + return info || {mode: mode, state: state}; + }; // The character stream used by a mode's parser. function StringStream(string, tabSize) { diff --git a/lib/util/closetag.js b/lib/util/closetag.js index 83dc2c7e75..5096678473 100644 --- a/lib/util/closetag.js +++ b/lib/util/closetag.js @@ -27,12 +27,7 @@ CodeMirror.defaults['closeTagVoid'] = ['area', 'base', 'br', 'col', 'command', 'embed', 'hr', 'img', 'input', 'keygen', 'link', 'meta', 'param', 'source', 'track', 'wbr']; function innerState(cm, state) { - for (var mode = cm.getMode(); mode.innerMode;) { - var info = mode.innerMode(state); - mode = info.mode; - state = info.state; - } - if (mode.name == "xml") return state; + return CodeMirror.innerMode(cm.getMode(), state).state; } diff --git a/lib/util/formatting.js b/lib/util/formatting.js index 22c943fb40..4375dd729a 100644 --- a/lib/util/formatting.js +++ b/lib/util/formatting.js @@ -1,110 +1,22 @@ // ============== Formatting extensions ============================ -// A common storage for all mode-specific formatting features -if (!CodeMirror.modeExtensions) CodeMirror.modeExtensions = {}; - -// Returns the extension of the editor's current mode -CodeMirror.defineExtension("getModeExt", function () { - var mname = CodeMirror.resolveMode(this.getOption("mode")).name; - var ext = CodeMirror.modeExtensions[mname]; - if (!ext) throw new Error("No extensions found for mode " + mname); - return ext; -}); - -// If the current mode is 'htmlmixed', returns the extension of a mode located at -// the specified position (can be htmlmixed, css or javascript). Otherwise, simply -// returns the extension of the editor's current mode. -CodeMirror.defineExtension("getModeExtAtPos", function (pos) { - var token = this.getTokenAt(pos); - if (token && token.state && token.state.mode) - return CodeMirror.modeExtensions[token.state.mode == "html" ? "htmlmixed" : token.state.mode]; - else - return this.getModeExt(); -}); - -// Comment/uncomment the specified range -CodeMirror.defineExtension("commentRange", function (isComment, from, to) { - var curMode = this.getModeExtAtPos(this.getCursor()); - if (isComment) { // Comment range - var commentedText = this.getRange(from, to); - this.replaceRange(curMode.commentStart + this.getRange(from, to) + curMode.commentEnd - , from, to); - if (from.line == to.line && from.ch == to.ch) { // An empty comment inserted - put cursor inside - this.setCursor(from.line, from.ch + curMode.commentStart.length); - } - } - else { // Uncomment range - var selText = this.getRange(from, to); - var startIndex = selText.indexOf(curMode.commentStart); - var endIndex = selText.lastIndexOf(curMode.commentEnd); - if (startIndex > -1 && endIndex > -1 && endIndex > startIndex) { - // Take string till comment start - selText = selText.substr(0, startIndex) - // From comment start till comment end - + selText.substring(startIndex + curMode.commentStart.length, endIndex) - // From comment end till string end - + selText.substr(endIndex + curMode.commentEnd.length); - } - this.replaceRange(selText, from, to); - } -}); - -// Applies automatic mode-aware indentation to the specified range -CodeMirror.defineExtension("autoIndentRange", function (from, to) { - var cmInstance = this; - this.operation(function () { - for (var i = from.line; i <= to.line; i++) { - cmInstance.indentLine(i, "smart"); +(function() { + // Define extensions for a few modes + CodeMirror.extendMode("css", { + commentStart: "/*", + commentEnd: "*/", + wordWrapChars: [";", "\\{", "\\}"], + autoFormatLineBreaks: function (text) { + return text.replace(new RegExp("(;|\\{|\\})([^\r\n])", "g"), "$1\n$2"); } }); -}); - -// Applies automatic formatting to the specified range -CodeMirror.defineExtension("autoFormatRange", function (from, to) { - var absStart = this.indexFromPos(from); - var absEnd = this.indexFromPos(to); - // Insert additional line breaks where necessary according to the - // mode's syntax - var res = this.getModeExt().autoFormatLineBreaks(this.getValue(), absStart, absEnd); - var cmInstance = this; - - // Replace and auto-indent the range - this.operation(function () { - cmInstance.replaceRange(res, from, to); - var startLine = cmInstance.posFromIndex(absStart).line; - var endLine = cmInstance.posFromIndex(absStart + res.length).line; - for (var i = startLine; i <= endLine; i++) { - cmInstance.indentLine(i, "smart"); - } - }); -}); - -// Define extensions for a few modes - -CodeMirror.modeExtensions["css"] = { - commentStart: "/*", - commentEnd: "*/", - wordWrapChars: [";", "\\{", "\\}"], - autoFormatLineBreaks: function (text, startPos, endPos) { - text = text.substring(startPos, endPos); - return text.replace(new RegExp("(;|\\{|\\})([^\r\n])", "g"), "$1\n$2"); - } -}; -CodeMirror.modeExtensions["javascript"] = { - commentStart: "/*", - commentEnd: "*/", - wordWrapChars: [";", "\\{", "\\}"], - - getNonBreakableBlocks: function (text) { - var nonBreakableRegexes = [ - new RegExp("for\\s*?\\(([\\s\\S]*?)\\)"), - new RegExp("\\\\\"([\\s\\S]*?)(\\\\\"|$)"), - new RegExp("\\\\\'([\\s\\S]*?)(\\\\\'|$)"), - new RegExp("'([\\s\\S]*?)('|$)"), - new RegExp("\"([\\s\\S]*?)(\"|$)"), - new RegExp("//.*([\r\n]|$)") - ]; - var nonBreakableBlocks = new Array(); + function jsNonBreakableBlocks(text) { + var nonBreakableRegexes = [/for\s*?\((.*?)\)/, + /\"(.*?)(\"|$)/, + /\'(.*?)(\'|$)/, + /\/\*(.*?)(\*\/|$)/, + /\/\/.*/]; + var nonBreakableBlocks = []; for (var i = 0; i < nonBreakableRegexes.length; i++) { var curPos = 0; while (curPos < text.length) { @@ -126,174 +38,149 @@ CodeMirror.modeExtensions["javascript"] = { }); return nonBreakableBlocks; - }, + } - autoFormatLineBreaks: function (text, startPos, endPos) { - text = text.substring(startPos, endPos); - var curPos = 0; - var reLinesSplitter = new RegExp("(;|\\{|\\})([^\r\n;])", "g"); - var nonBreakableBlocks = this.getNonBreakableBlocks(text); - if (nonBreakableBlocks != null) { - var res = ""; - for (var i = 0; i < nonBreakableBlocks.length; i++) { - if (nonBreakableBlocks[i].start > curPos) { // Break lines till the block - res += text.substring(curPos, nonBreakableBlocks[i].start).replace(reLinesSplitter, "$1\n$2"); - curPos = nonBreakableBlocks[i].start; - } - if (nonBreakableBlocks[i].start <= curPos - && nonBreakableBlocks[i].end >= curPos) { // Skip non-breakable block - res += text.substring(curPos, nonBreakableBlocks[i].end); - curPos = nonBreakableBlocks[i].end; + CodeMirror.extendMode("javascript", { + commentStart: "/*", + commentEnd: "*/", + wordWrapChars: [";", "\\{", "\\}"], + + autoFormatLineBreaks: function (text) { + var curPos = 0; + var reLinesSplitter = /(;|\{|\})([^\r\n;])/g; + var nonBreakableBlocks = jsNonBreakableBlocks(text); + if (nonBreakableBlocks != null) { + var res = ""; + for (var i = 0; i < nonBreakableBlocks.length; i++) { + if (nonBreakableBlocks[i].start > curPos) { // Break lines till the block + res += text.substring(curPos, nonBreakableBlocks[i].start).replace(reLinesSplitter, "$1\n$2"); + curPos = nonBreakableBlocks[i].start; + } + if (nonBreakableBlocks[i].start <= curPos + && nonBreakableBlocks[i].end >= curPos) { // Skip non-breakable block + res += text.substring(curPos, nonBreakableBlocks[i].end); + curPos = nonBreakableBlocks[i].end; + } } + if (curPos < text.length) + res += text.substr(curPos).replace(reLinesSplitter, "$1\n$2"); + return res; + } else { + return text.replace(reLinesSplitter, "$1\n$2"); } - if (curPos < text.length - 1) { - res += text.substr(curPos).replace(reLinesSplitter, "$1\n$2"); - } - return res; } - else { - return text.replace(reLinesSplitter, "$1\n$2"); - } - } -}; - -CodeMirror.modeExtensions["xml"] = { - commentStart: "", - wordWrapChars: [">"], + }); - autoFormatLineBreaks: function (text, startPos, endPos) { - text = text.substring(startPos, endPos); - var lines = text.split("\n"); - var reProcessedPortion = new RegExp("(^\\s*?<|^[^<]*?)(.+)(>\\s*?$|[^>]*?$)"); - var reOpenBrackets = new RegExp("<", "g"); - var reCloseBrackets = new RegExp("(>)([^\r\n])", "g"); - for (var i = 0; i < lines.length; i++) { - var mToProcess = lines[i].match(reProcessedPortion); - if (mToProcess != null && mToProcess.length > 3) { // The line starts with whitespaces and ends with whitespaces - lines[i] = mToProcess[1] + CodeMirror.extendMode("xml", { + commentStart: "", + wordWrapChars: [">"], + + autoFormatLineBreaks: function (text) { + var lines = text.split("\n"); + var reProcessedPortion = new RegExp("(^\\s*?<|^[^<]*?)(.+)(>\\s*?$|[^>]*?$)"); + var reOpenBrackets = new RegExp("<", "g"); + var reCloseBrackets = new RegExp("(>)([^\r\n])", "g"); + for (var i = 0; i < lines.length; i++) { + var mToProcess = lines[i].match(reProcessedPortion); + if (mToProcess != null && mToProcess.length > 3) { // The line starts with whitespaces and ends with whitespaces + lines[i] = mToProcess[1] + mToProcess[2].replace(reOpenBrackets, "\n$&").replace(reCloseBrackets, "$1\n$2") + mToProcess[3]; - continue; + continue; + } } + return lines.join("\n"); } + }); - return lines.join("\n"); + function localModeAt(cm, pos) { + return CodeMirror.innerMode(cm.getMode(), cm.getTokenAt(pos).state).mode; } -}; - -CodeMirror.modeExtensions["htmlmixed"] = { - commentStart: "", - wordWrapChars: [">", ";", "\\{", "\\}"], - getModeInfos: function (text, absPos) { - var modeInfos = new Array(); - modeInfos[0] = - { - pos: 0, - modeExt: CodeMirror.modeExtensions["xml"], - modeName: "xml" - }; - - var modeMatchers = new Array(); - modeMatchers[0] = - { - regex: new RegExp("]*>([\\s\\S]*?)(]*>|$)", "i"), - modeExt: CodeMirror.modeExtensions["css"], - modeName: "css" - }; - modeMatchers[1] = - { - regex: new RegExp("]*>([\\s\\S]*?)(]*>|$)", "i"), - modeExt: CodeMirror.modeExtensions["javascript"], - modeName: "javascript" - }; + function enumerateModesBetween(cm, line, start, end) { + var outer = cm.getMode(); + if (!outer.innerMode) return [{from: start, to: end, mode: outer}]; + var init = CodeMirror.innerMode(outer, cm.getTokenAt({line: line, ch: start}).state); + var state = init.state, mode = init.mode; + var found = [], stream = new CodeMirror.StringStream(cm.getLine(line)); + stream.pos = stream.start = start; + for (;;) { + outer.token(stream, state); + var cur = CodeMirror.innerMode(outer, state).mode; + if (curMode != mode) { + found.push({from: start, to: stream.pos, mode: mode}); + start = stream.pos; + mode = curMode; + } + if (stream.pos >= end) break; + stream.start = stream.pos; + } + if (start < end) found.push({from: start, to: end, mode: mode}); + return found; + } - var lastCharPos = (typeof (absPos) !== "undefined" ? absPos : text.length - 1); - // Detect modes for the entire text - for (var i = 0; i < modeMatchers.length; i++) { - var curPos = 0; - while (curPos <= lastCharPos) { - var m = text.substr(curPos).match(modeMatchers[i].regex); - if (m != null) { - if (m.length > 1 && m[1].length > 0) { - // Push block begin pos - var blockBegin = curPos + m.index + m[0].indexOf(m[1]); - modeInfos.push( - { - pos: blockBegin, - modeExt: modeMatchers[i].modeExt, - modeName: modeMatchers[i].modeName - }); - // Push block end pos - modeInfos.push( - { - pos: blockBegin + m[1].length, - modeExt: modeInfos[0].modeExt, - modeName: modeInfos[0].modeName - }); - curPos += m.index + m[0].length; - continue; - } - else { - curPos += m.index + Math.max(m[0].length, 1); - } - } - else { // No more matches - break; + // Comment/uncomment the specified range + CodeMirror.defineExtension("commentRange", function (isComment, from, to) { + var curMode = localModeAt(this, from); + this.operation(function() { + if (isComment) { // Comment range + this.replaceRange(curMode.commentEnd, to); + this.replaceRange(curMode.commentStart, from); + if (from.line == to.line && from.ch == to.ch) // An empty comment inserted - put cursor inside + this.setCursor(from.line, from.ch + curMode.commentStart.length); + } else { // Uncomment range + var selText = this.getRange(from, to); + var startIndex = selText.indexOf(curMode.commentStart); + var endIndex = selText.lastIndexOf(curMode.commentEnd); + if (startIndex > -1 && endIndex > -1 && endIndex > startIndex) { + // Take string till comment start + selText = selText.substr(0, startIndex) + // From comment start till comment end + + selText.substring(startIndex + curMode.commentStart.length, endIndex) + // From comment end till string end + + selText.substr(endIndex + curMode.commentEnd.length); } + this.replaceRange(selText, from, to); } - } - // Sort mode infos - modeInfos.sort(function sortModeInfo(a, b) { - return a.pos - b.pos; }); + }); - return modeInfos; - }, - - autoFormatLineBreaks: function (text, startPos, endPos) { - var modeInfos = this.getModeInfos(text); - var reBlockStartsWithNewline = new RegExp("^\\s*?\n"); - var reBlockEndsWithNewline = new RegExp("\n\\s*?$"); - var res = ""; - // Use modes info to break lines correspondingly - if (modeInfos.length > 1) { // Deal with multi-mode text - for (var i = 1; i <= modeInfos.length; i++) { - var selStart = modeInfos[i - 1].pos; - var selEnd = (i < modeInfos.length ? modeInfos[i].pos : endPos); + // Applies automatic mode-aware indentation to the specified range + CodeMirror.defineExtension("autoIndentRange", function (from, to) { + var cmInstance = this; + this.operation(function () { + for (var i = from.line; i <= to.line; i++) { + cmInstance.indentLine(i, "smart"); + } + }); + }); - if (selStart >= endPos) { // The block starts later than the needed fragment - break; + // Applies automatic formatting to the specified range + CodeMirror.defineExtension("autoFormatRange", function (from, to) { + var cm = this; + cm.operation(function () { + for (var cur = from.line, end = to.line; cur <= end; ++cur) { + var f = {line: cur, ch: cur == from.line ? from.ch : 0}; + var t = {line: cur, ch: cur == end ? to.ch : null}; + var modes = enumerateModesBetween(cm, cur, f.ch, t.ch), mangled = ""; + var text = cm.getRange(f, t); + for (var i = 0; i < modes.length; ++i) { + var part = modes.length > 1 ? text.slice(modes[i].from, modes[i].to) : text; + if (i) mangled += "\n"; + if (modes[i].mode.autoFormatLineBreaks) { + mangled += modes[i].mode.autoFormatLineBreaks(part); + } else mangled += text; } - if (selStart < startPos) { - if (selEnd <= startPos) { // The block starts earlier than the needed fragment - continue; - } - selStart = startPos; - } - if (selEnd > endPos) { - selEnd = endPos; - } - var textPortion = text.substring(selStart, selEnd); - if (modeInfos[i - 1].modeName != "xml") { // Starting a CSS or JavaScript block - if (!reBlockStartsWithNewline.test(textPortion) - && selStart > 0) { // The block does not start with a line break - textPortion = "\n" + textPortion; - } - if (!reBlockEndsWithNewline.test(textPortion) - && selEnd < text.length - 1) { // The block does not end with a line break - textPortion += "\n"; - } + if (mangled != text) { + for (var count = 0, pos = mangled.indexOf("\n"); pos != -1; pos = mangled.indexOf("\n", pos + 1), ++count) {} + cm.replaceRange(mangled, f, t); + cur += count; + end += count; } - res += modeInfos[i - 1].modeExt.autoFormatLineBreaks(textPortion); } - } - else { // Single-mode text - res = modeInfos[0].modeExt.autoFormatLineBreaks(text.substring(startPos, endPos)); - } - - return res; - } -}; + for (var cur = from.line + 1; cur <= end; ++cur) + cm.indentLine(cur, "smart"); + }); + }); +})(); diff --git a/mode/gfm/gfm.js b/mode/gfm/gfm.js index b83fbc683a..2bc273ab68 100644 --- a/mode/gfm/gfm.js +++ b/mode/gfm/gfm.js @@ -96,7 +96,7 @@ CodeMirror.defineMode("gfm", function(config, parserConfig) { }, copyState: function(state) { - return {token: state.token, mode: state.mode, mdState: CodeMirror.copyState(mdMode, state.mdState), + return {token: state.token, mdState: CodeMirror.copyState(mdMode, state.mdState), localMode: state.localMode, localState: state.localMode ? CodeMirror.copyState(state.localMode, state.localState) : null}; }, @@ -140,6 +140,11 @@ CodeMirror.defineMode("gfm", function(config, parserConfig) { } return state.token(stream, state); + }, + + innerMode: function(state) { + if (state.token == markdown) return {state: state.mdState, mode: mdMode}; + else return {state: state.localState, mode: state.localMode}; } }; }, "markdown"); From 17176f79cc7d87420c42fda37cb155f6924710da Mon Sep 17 00:00:00 2001 From: Marijn Haverbeke Date: Wed, 12 Sep 2012 12:32:00 +0200 Subject: [PATCH 0112/5780] [util/formatting] Fix a bunch of bugs That's what you get when you test with the published version, rather than the one you're actually editing. --- lib/util/formatting.js | 23 +++++++++++++++-------- 1 file changed, 15 insertions(+), 8 deletions(-) diff --git a/lib/util/formatting.js b/lib/util/formatting.js index 4375dd729a..ccd6db1b21 100644 --- a/lib/util/formatting.js +++ b/lib/util/formatting.js @@ -99,18 +99,24 @@ } function enumerateModesBetween(cm, line, start, end) { - var outer = cm.getMode(); + var outer = cm.getMode(), text = cm.getLine(line); + if (end == null) end = text.length; if (!outer.innerMode) return [{from: start, to: end, mode: outer}]; - var init = CodeMirror.innerMode(outer, cm.getTokenAt({line: line, ch: start}).state); - var state = init.state, mode = init.mode; - var found = [], stream = new CodeMirror.StringStream(cm.getLine(line)); + var state = cm.getTokenAt({line: line, ch: start}).state; + var mode = CodeMirror.innerMode(outer, state).mode; + var found = [], stream = new CodeMirror.StringStream(text); stream.pos = stream.start = start; for (;;) { outer.token(stream, state); - var cur = CodeMirror.innerMode(outer, state).mode; + var curMode = CodeMirror.innerMode(outer, state).mode; if (curMode != mode) { - found.push({from: start, to: stream.pos, mode: mode}); - start = stream.pos; + var cut = stream.start; + // Crappy heuristic to deal with the fact that a change in + // mode can occur both at the end and the start of a token, + // and we don't know which it was. + if (mode.name == "xml" && text.charAt(stream.pos - 1) == ">") cut = stream.pos; + found.push({from: start, to: cut, mode: mode}); + start = cut; mode = curMode; } if (stream.pos >= end) break; @@ -167,7 +173,7 @@ var text = cm.getRange(f, t); for (var i = 0; i < modes.length; ++i) { var part = modes.length > 1 ? text.slice(modes[i].from, modes[i].to) : text; - if (i) mangled += "\n"; + if (mangled) mangled += "\n"; if (modes[i].mode.autoFormatLineBreaks) { mangled += modes[i].mode.autoFormatLineBreaks(part); } else mangled += text; @@ -181,6 +187,7 @@ } for (var cur = from.line + 1; cur <= end; ++cur) cm.indentLine(cur, "smart"); + cm.setSelection(from, cm.getCursor(false)); }); }); })(); From 85d24de92b6b61b95421f0b28271c71375a29706 Mon Sep 17 00:00:00 2001 From: Marijn Haverbeke Date: Wed, 12 Sep 2012 20:22:15 +0200 Subject: [PATCH 0113/5780] Add NoTex to real-world uses --- index.html | 1 + 1 file changed, 1 insertion(+) diff --git a/index.html b/index.html index 9d8818bfce..3de16d3664 100644 --- a/index.html +++ b/index.html @@ -147,6 +147,7 @@

    Real-world uses:

  • CSSDeck (CSS showcase)
  • CKWNC (UML editor)
  • sketchPatch Livecodelab
  • +
  • NoTex (rST authoring)
  • From 022abb4081d65b8f38d1a25bf37ca69476736648 Mon Sep 17 00:00:00 2001 From: Marijn Haverbeke Date: Tue, 11 Sep 2012 15:27:10 +0200 Subject: [PATCH 0114/5780] Use a set of object, rather than a huge closure, to store state Huge refactor of lib/codemirror.js. Most code is now top-level (in the module wrapper function) rather than inside of the CodeMirror constructor sharing a huge amount of state. A CodeMirror instance now has (non-public) `display`, `view`, and `options` properties that describe its state. The display holds the editor's DOM structure and associated state, view represents a view on a document, containing `doc`, `sel`, `overwrite`, `scrollTop`, and `scrollLeft` properties. The `doc` itself contains its history. (It is not yet possible to switch views and documents in and out of an editor, but this does start clearing the way towards that.) --- doc/manual.html | 8 +- lib/codemirror.js | 4602 ++++++++++++++++++++++++--------------------- test/test.js | 9 +- 3 files changed, 2464 insertions(+), 2155 deletions(-) diff --git a/doc/manual.html b/doc/manual.html index ebff9573cb..fc94cdbb2e 100644 --- a/doc/manual.html +++ b/doc/manual.html @@ -125,9 +125,11 @@

    Configuration

    example {name: "javascript", json: true}). The demo pages for each mode contain information about what configuration parameters the mode supports. You can ask CodeMirror which modes - and MIME types are loaded with - the CodeMirror.listModes - and CodeMirror.listMIMEs functions.
    + and MIME types are by inspecting + the CodeMirror.modes + and CodeMirror.mimeModes objects. The first maps + mode names to their constructors, and the second maps MIME types + to mode specs.
    theme (string)
    The theme to style the editor with. You must make sure the diff --git a/lib/codemirror.js b/lib/codemirror.js index 5ea98ba4f8..94fa8366dc 100644 --- a/lib/codemirror.js +++ b/lib/codemirror.js @@ -5,1687 +5,2057 @@ // CodeMirror is the only global var we claim window.CodeMirror = (function() { "use strict"; - // This is the function that produces an editor instance. Its - // closure is used to store the editor state. - function CodeMirror(place, givenOptions) { - var history, overwrite = false; - - function isLine(l) {return l >= 0 && l < doc.size;} - // The instance object that we'll return. Mostly calls out to - // local functions in the CodeMirror function. Some do some extra - // range checking and/or clipping. operation is used to wrap the - // call so that changes it makes are tracked, and the display is - // updated afterwards. - var instance = { - getValue: getValue, - setValue: operation(setValue), - getSelection: getSelection, - replaceSelection: operation(replaceSelection), - focus: function(){window.focus(); focusInput(); onFocus(); fastPoll();}, - setOption: function(option, value) { - if (options[option] == value && option != "mode") return; - options[option] = value; - if (option == "mode" || option == "indentUnit") loadMode(); - else if (option == "readOnly" && value == "nocursor") {onBlur(); input.blur();} - else if (option == "readOnly" && !value) {resetInput(true);} - else if (option == "theme") themeChanged(); - else if (option == "lineWrapping") operation(wrappingChanged)(); - else if (option == "tabSize") updateDisplay(true); - else if (option == "keyMap") keyMapChanged(); - else if (option == "gutters" || option == "lineNumbers") setGuttersForLineNumbers(); - if (option == "lineNumbers" || option == "gutters" || option == "firstLineNumber" || - option == "theme" || option == "lineNumberFormatter") - guttersChanged(); - if (optionHandlers.hasOwnProperty(option)) - optionHandlers[option](instance, value); - }, - getOption: function(option) {return options[option];}, - getMode: function() {return mode;}, - undo: operation(undo), - redo: operation(redo), - indentLine: operation(function(n, dir) { - if (typeof dir != "string") { - if (dir == null) dir = options.smartIndent ? "smart" : "prev"; - else dir = dir ? "add" : "subtract"; - } - if (isLine(n)) indentLine(n, dir); - }), - indentSelection: operation(indentSelected), - historySize: function() {return {undo: history.done.length, redo: history.undone.length};}, - clearHistory: function() {history = new History();}, - setHistory: function(histData) { - history = new History(); - history.done = histData.done; - history.undone = histData.undone; - }, - getHistory: function() { - history.time = 0; - return {done: history.done.concat([]), undone: history.undone.concat([])}; - }, - getTokenAt: operation(function(pos) { - pos = clipPos(pos); - return getLine(pos.line).getTokenAt(mode, getStateBefore(pos.line), options.tabSize, pos.ch); - }), - getStateAfter: function(line) { - line = clipLine(line == null ? doc.size - 1: line); - return getStateBefore(line + 1); - }, - cursorCoords: function(start, mode) { - var pos; - if (start == null) start = sel.inverted; - if (typeof start == "object") pos = clipPos(start); - else pos = start ? sel.from : sel.to; - return cursorCoords(pos, mode || "page"); - }, - charCoords: function(pos, mode) { - return charCoords(clipPos(pos), mode || "page"); - }, - coordsChar: function(coords) { - var off = lineSpace.getBoundingClientRect(); - return coordsChar(coords.left - off.left, coords.top - off.top); - }, - markText: operation(markText), - setBookmark: setBookmark, - findMarksAt: findMarksAt, - setGutterMarker: operation(setGutterMarker), - clearGutter: operation(clearGutter), - setLineClass: operation(setLineClass), - addLineWidget: operation(addLineWidget), - removeLineWidget: operation(removeLineWidget), - foldLines: operation(foldLines), - unfoldLines: operation(unfoldLines), - lineInfo: lineInfo, - getViewport: function() { return {from: showingFrom, to: showingTo};}, - addWidget: function(pos, node, scroll, vert, horiz) { - pos = cursorCoords(clipPos(pos)); - var top = pos.top, left = pos.left; - node.style.position = "absolute"; - sizer.appendChild(node); - if (vert == "over") top = pos.top; - else if (vert == "near") { - var vspace = Math.max(wrapper.clientHeight, doc.height), - hspace = Math.max(sizer.clientWidth, lineSpace.clientWidth); - if (pos.bottom + node.offsetHeight > vspace && pos.top > node.offsetHeight) - top = pos.top - node.offsetHeight; - if (left + node.offsetWidth > hspace) - left = hspace - node.offsetWidth; - } - node.style.top = (top + paddingTop()) + "px"; - node.style.left = node.style.right = ""; - if (horiz == "right") { - left = sizer.clientWidth - node.offsetWidth; - node.style.right = "0px"; - } else { - if (horiz == "left") left = 0; - else if (horiz == "middle") left = (sizer.clientWidth - node.offsetWidth) / 2; - node.style.left = left + "px"; - } - if (scroll) - scrollIntoView(left, top, left + node.offsetWidth, top + node.offsetHeight); - }, - - lineCount: function() {return doc.size;}, - clipPos: clipPos, - getCursor: function(start) { - if (start == null) start = sel.inverted; - return copyPos(start ? sel.from : sel.to); - }, - somethingSelected: function() {return !posEq(sel.from, sel.to);}, - setCursor: operation(function(line, ch, user) { - if (ch == null && typeof line.line == "number") setCursor(line.line, line.ch, user); - else setCursor(line, ch, user); - }), - setSelection: operation(function(from, to, user) { - (user ? setSelectionUser : setSelection)(clipPos(from), clipPos(to || from)); - }), - getLine: function(line) {if (isLine(line)) return getLine(line).text;}, - getLineHandle: function(line) {if (isLine(line)) return getLine(line);}, - getLineNumber: function(line) {return lineNo(line);}, - setLine: operation(function(line, text) { - if (isLine(line)) replaceRange(text, {line: line, ch: 0}, {line: line, ch: getLine(line).text.length}); - }), - removeLine: operation(function(line) { - if (isLine(line)) replaceRange("", {line: line, ch: 0}, clipPos({line: line+1, ch: 0})); - }), - replaceRange: operation(replaceRange), - getRange: function(from, to, lineSep) {return getRange(clipPos(from), clipPos(to), lineSep);}, - - triggerOnKeyDown: operation(onKeyDown), - execCommand: function(cmd) {return commands[cmd](instance);}, - // Stuff used by commands, probably not much use to outside code. - moveH: operation(moveH), - deleteH: operation(deleteH), - moveV: operation(moveV), - toggleOverwrite: function() { - if(overwrite){ - overwrite = false; - cursor.className = cursor.className.replace(" CodeMirror-overwrite", ""); - } else { - overwrite = true; - cursor.className += " CodeMirror-overwrite"; - } - }, - - posFromIndex: function(off) { - var lineNo = 0, ch; - doc.iter(0, doc.size, function(line) { - var sz = line.text.length + 1; - if (sz > off) { ch = off; return true; } - off -= sz; - ++lineNo; - }); - return clipPos({line: lineNo, ch: ch}); - }, - indexFromPos: function (coords) { - if (coords.line < 0 || coords.ch < 0) return 0; - var index = coords.ch; - doc.iter(0, coords.line, function (line) { - index += line.text.length + 1; - }); - return index; - }, - scrollTo: function(x, y) { - if (x != null) scrollbarH.scrollLeft = scroller.scrollLeft = x; - if (y != null) scrollbarV.scrollTop = scroller.scrollTop = y; - updateDisplay([]); - }, - getScrollInfo: function() { - return {left: scroller.scrollLeft, top: scroller.scrollTop, - height: scroller.scrollHeight, width: scroller.scrollWidth}; - }, - setSize: function(width, height) { - function interpret(val) { - val = String(val); - return /^\d+$/.test(val) ? val + "px" : val; - } - if (width != null) wrapper.style.width = interpret(width); - if (height != null) wrapper.style.height = interpret(height); - instance.refresh(); - }, - on: function(type, f) {on(this, type, f);}, - off: function(type, f) {off(this, type, f);}, - - operation: function(f){return operation(f)();}, - compoundChange: function(f){return compoundChange(f);}, - refresh: function(){ - updateDisplay(true, null, lastScrollTop); - if (scrollbarV.scrollHeight > lastScrollTop) - scrollbarV.scrollTop = lastScrollTop; - }, - getInputField: function(){return input;}, - getWrapperElement: function(){return wrapper;}, - getScrollerElement: function(){return scroller;}, - getGutterElement: function(){return gutters;} - }; + // BROWSER SNIFFING + + // Crude, but necessary to handle a number of hard-to-feature detect + // bugs and behavior differences. + var gecko = /gecko\/\d{7}/i.test(navigator.userAgent); + var ie = /MSIE \d/.test(navigator.userAgent); + var ie_lt8 = /MSIE [1-7]\b/.test(navigator.userAgent); + var ie_lt9 = /MSIE [1-8]\b/.test(navigator.userAgent); + var quirksMode = ie && document.documentMode == 5; + var webkit = /WebKit\//.test(navigator.userAgent); + var chrome = /Chrome\//.test(navigator.userAgent); + var opera = /Opera\//.test(navigator.userAgent); + var safari = /Apple Computer/.test(navigator.vendor); + var khtml = /KHTML\//.test(navigator.userAgent); + var mac_geLion = /Mac OS X 10\D([7-9]|\d\d)\D/.test(navigator.userAgent); + + var ios = /AppleWebKit/.test(navigator.userAgent) && /Mobile\/\w+/.test(navigator.userAgent); + var mac = ios || /Mac/.test(navigator.platform); + var win = /Win/.test(navigator.platform); + + // CONSTRUCTOR + + function CodeMirror(place, options) { + if (!this || this == window) return new CodeMirror(place, options); + this.options = options = options || {}; // Determine effective options based on given values and defaults. - var options = {}, defaults = CodeMirror.defaults; - for (var opt in defaults) - if (defaults.hasOwnProperty(opt)) - options[opt] = (givenOptions && givenOptions.hasOwnProperty(opt) ? givenOptions : defaults)[opt]; + for (var opt in defaults) if (!options.hasOwnProperty(opt) && defaults.hasOwnProperty(opt)) + options[opt] = defaults[opt]; + setGuttersForLineNumbers(options); + + var display = this.display = makeDisplay(place); + display.wrapper.CodeMirror = this; + updateGutters(this); + themeChanged(this); + keyMapChanged(this); + if (options.tabindex != null) display.input.tabIndex = options.tabindex; + if (options.autofocus) focusInput(this); + if (options.lineWrapping) display.wrapper.className += " CodeMirror-wrap"; + + var doc = new BranchChunk([new LeafChunk([new Line("", null, textHeight(display))])]); + // frontier is the point up to which the content has been parsed, + doc.frontier = 0; + doc.highlight = new Delayed(); + doc.tabSize = options.tabSize; + // The selection. These are always maintained to point at valid + // positions. Inverted is used to remember that the user is + // selecting bottom-to-top. + this.view = { + doc: doc, + sel: {from: {line: 0, ch: 0}, to: {line: 0, ch: 0}, inverted: false, shift: false}, + scrollTop: 0, scrollLeft: 0, + overwrite: false, focused: false, + // Tracks the maximum line length so that + // the horizontal scrollbar can be kept + // static when scrolling. + maxLine: getLine(doc, 0), + maxLineChanged: false, + suppressEdits: false, + goalColumn: null + }; + loadMode(this); + + // Initialize the content. + this.setValue(options.value || ""); + doc.history = new History(); + + registerEventHandlers(this); + // IE throws unspecified error in certain cases, when + // trying to access activeElement before onload + var hasFocus; try { hasFocus = (document.activeElement == display.input); } catch(e) { } + if (hasFocus || options.autofocus) setTimeout(bind(onFocus, this), 20); + else onBlur(this); + + for (var opt in optionHandlers) + if (optionHandlers.propertyIsEnumerable(opt)) + optionHandlers[opt](this, options[opt]); + } + + // DISPLAY CONSTRUCTOR - var input = elt("textarea", null, null, "position: absolute; padding: 0; width: 1px; height: 1em; outline: none;"); + function makeDisplay(place) { + var d = {}; + var input = d.input = elt("textarea", null, null, "position: absolute; padding: 0; width: 1px; height: 1em; outline: none;"); input.setAttribute("wrap", "off"); input.setAttribute("autocorrect", "off"); input.setAttribute("autocapitalize", "off"); // Wraps and hides input textarea - var inputDiv = elt("div", [input], null, "overflow: hidden; position: relative; width: 3px; height: 0px;"); + d.inputDiv = elt("div", [input], null, "overflow: hidden; position: relative; width: 3px; height: 0px;"); // The actual fake scrollbars. - var scrollbarH = elt("div", [elt("div", null, null, "height: 1px")], "CodeMirror-hscrollbar"); - var scrollbarV = elt("div", [elt("div", null, null, "width: 1px")], "CodeMirror-vscrollbar"); - var scrollbarFiller = elt("div", null, "CodeMirror-scrollbar-filler"); + d.scrollbarH = elt("div", [elt("div", null, null, "height: 1px")], "CodeMirror-hscrollbar"); + d.scrollbarV = elt("div", [elt("div", null, null, "width: 1px")], "CodeMirror-vscrollbar"); + d.scrollbarFiller = elt("div", null, "CodeMirror-scrollbar-filler"); // DIVs containing the selection and the actual code - var lineDiv = elt("div"), selectionDiv = elt("div", null, null, "position: relative; z-index: 1"); + d.lineDiv = elt("div"); + d.selectionDiv = elt("div", null, null, "position: relative; z-index: 1"); // Blinky cursor, and element used to ensure cursor fits at the end of a line - var cursor = elt("pre", "\u00a0", "CodeMirror-cursor"); + d.cursor = elt("pre", "\u00a0", "CodeMirror-cursor"); // Secondary cursor, shown when on a 'jump' in bi-directional text - var otherCursor = elt("pre", "\u00a0", "CodeMirror-cursor CodeMirror-secondarycursor"); + d.otherCursor = elt("pre", "\u00a0", "CodeMirror-cursor CodeMirror-secondarycursor"); // Used to measure text size - var measure = elt("div", null, "CodeMirror-measure"); + d.measure = elt("div", null, "CodeMirror-measure"); // Wraps everything that needs to exist inside the vertically-padded coordinate system - var lineSpace = elt("div", [measure, cursor, otherCursor, selectionDiv, lineDiv], null, "position: relative"); + d.lineSpace = elt("div", [d.measure, d.cursor, d.otherCursor, d.selectionDiv, d.lineDiv], + null, "position: relative; outline: none"); // Moved around its parent to cover visible view - var mover = elt("div", [elt("div", [lineSpace], "CodeMirror-lines")], null, "position: relative"); - var gutters = elt("div", null, "CodeMirror-gutters"), lineGutter; + d.mover = elt("div", [elt("div", [d.lineSpace], "CodeMirror-lines")], null, "position: relative"); + d.gutters = elt("div", null, "CodeMirror-gutters"); + d.lineGutter = null; // Set to the height of the text, causes scrolling - var sizer = elt("div", [mover], "CodeMirror-sizer"); - // This is needed because behavior of elts with overflow: auto and padding is inconsistent across browsers - var heightForcer = elt("div", "\u00a0", null, "position: absolute; height: " + scrollerCutOff + "px"); + d.sizer = elt("div", [d.mover], "CodeMirror-sizer"); + // D is needed because behavior of elts with overflow: auto and padding is inconsistent across browsers + d.heightForcer = elt("div", "\u00a0", null, "position: absolute; height: " + scrollerCutOff + "px"); // Provides scrolling - var scroller = elt("div", [sizer, heightForcer], "CodeMirror-scroll"); - scroller.setAttribute("tabIndex", "-1"); + d.scroller = elt("div", [d.sizer, d.heightForcer], "CodeMirror-scroll"); + d.scroller.setAttribute("tabIndex", "-1"); // The element in which the editor lives. - var wrapper = elt("div", [gutters, inputDiv, scrollbarH, scrollbarV, scrollbarFiller, scroller], - "CodeMirror" + (options.lineWrapping ? " CodeMirror-wrap" : "")); + d.wrapper = elt("div", [d.gutters, d.inputDiv, d.scrollbarH, d.scrollbarV, + d.scrollbarFiller, d.scroller], "CodeMirror"); // Work around IE7 z-index bug - if (ie_lt8) gutters.style.zIndex = -1; - wrapper.CodeMirror = instance; - if (place.appendChild) place.appendChild(wrapper); else place(wrapper); + if (ie_lt8) d.gutters.style.zIndex = -1; + if (place.appendChild) place.appendChild(d.wrapper); else place(d.wrapper); - themeChanged(); keyMapChanged(); // Needed to hide big blue blinking cursor on Mobile Safari if (ios) input.style.width = "0px"; - if (!webkit) scroller.draggable = true; - lineSpace.style.outline = "none"; - if (options.tabindex != null) input.tabIndex = options.tabindex; - if (options.autofocus) focusInput(); - setGuttersForLineNumbers(); updateGutters(); + if (!webkit) d.scroller.draggable = true; // Needed to handle Tab key in KHTML - if (khtml) inputDiv.style.height = "1px", inputDiv.style.position = "absolute"; - + if (khtml) { d.inputDiv.style.height = "1px"; d.inputDiv.style.position = "absolute"; } // Need to set a minimum width to see the scrollbar on IE7 (but must not set it on IE8). - else if (ie_lt8) scrollbarH.style.minWidth = scrollbarV.style.minWidth = "18px"; - - // Delayed object wrap timeouts, making sure only one is active. blinker holds an interval. - var poll = new Delayed(), highlight = new Delayed(), blinker; + else if (ie_lt8) d.scrollbarH.style.minWidth = d.scrollbarV.style.minWidth = "18px"; - var measureLineMemo = [], measureLineMemoPos = 0; - // mode holds a mode API object. doc is the tree of Line objects, - // frontier is the point up to which the content has been parsed, - // and history the undo history (instance of History constructor). - var mode, doc = new BranchChunk([new LeafChunk([new Line("", null, textHeight())])]), frontier = 0, focused; - loadMode(); - // The selection. These are always maintained to point at valid - // positions. Inverted is used to remember that the user is - // selecting bottom-to-top. - var sel = {from: {line: 0, ch: 0}, to: {line: 0, ch: 0}, inverted: false}; - // Selection-related flags. shiftSelecting obviously tracks - // whether the user is holding shift. - var shiftSelecting, lastClick, lastDoubleClick, lastScrollTop = 0, lastScrollLeft = 0, draggingText, - suppressEdits = false; - // Variables used by startOperation/endOperation to track what - // happened during the operation. - var updateInput, userSelChange, changes, textChanged, selectionChanged; - var delayedCallbacks, alignWidgets; // Current visible range (may be bigger than the view window). - var displayOffset = 0, showingFrom = 0, showingTo = 0, lastSizeC = 0; - // Tracks the maximum line length so that the horizontal scrollbar - // can be kept static when scrolling. - var maxLine = getLine(0), updateMaxLine = false, maxLineChanged = true; - var pollingFast = false; // Ensures slowPoll doesn't cancel fastPoll - var goalColumn = null; + d.viewOffset = d.showingFrom = d.showingTo = d.lastSizeC = 0; + + // Used to only resize the line number gutter when necessary (when + // the amount of lines crosses a boundary that makes its width change) + d.lineNumWidth = d.lineNumChars = null; + // See readInput and resetInput + d.prevInput = ""; + // Set to true when a non-horizontal-scrolling widget is added. As + // an optimization, widget aligning is skipped when d is false. + d.alignWidgets = false; + // Flag that indicates whether we currently expect input to appear + // (after some event like 'keypress' or 'input') and are polling + // intensively. + d.pollingFast = false; + // Self-resetting timeout for the poller + d.poll = new Delayed(); + // True when a drag from the editor is active + d.draggingText = false; + + d.cachedCharWidth = d.cachedTextHeight = null; + d.measureLineCache = []; + d.measureLineCache.pos = 0; + + return d; + } - // Initialize the content. - operation(function(){setValue(options.value || ""); updateInput = false;})(); - history = new History(); - - // Register our event handlers. - on(scroller, "mousedown", operation(onMouseDown)); - on(gutters, "mousedown", operation(clickInGutter)); - on(scroller, "dblclick", operation(onDoubleClick)); - on(lineSpace, "selectstart", function(e) { - if (!mouseEventInWidget(e)) e_preventDefault(e); - }); - // Gecko browsers fire contextmenu *after* opening the menu, at - // which point we can't mess with it anymore. Context menu is - // handled in onMouseDown for Gecko. - if (!gecko) on(scroller, "contextmenu", onContextMenu); - on(scroller, "scroll", onScrollMain); - on(scrollbarH, "scroll", onScrollBarH); - on(scrollbarV, "scroll", onScrollBarV); - on(scrollbarH, "mousedown", function() {if (focused) setTimeout(focusInput, 0);}); - on(scrollbarV, "mousedown", function() {if (focused) setTimeout(focusInput, 0);}); - on(wrapper, "scroll", function() { wrapper.scrollTop = wrapper.scrollLeft = 0; }); - on(window, "resize", function resizeHandler() { - if (wrapper.parentNode) updateDisplay(true); - else off(window, "resize", resizeHandler); - }); - on(input, "keyup", operation(onKeyUp)); - on(input, "input", fastPoll); - on(input, "keydown", operation(onKeyDown)); - on(input, "keypress", operation(onKeyPress)); - on(input, "focus", onFocus); - on(input, "blur", onBlur); + // STATE UPDATES - function drag_(e) { - if (options.onDragEvent && options.onDragEvent(instance, addStop(e))) return; - e_stop(e); - } - if (options.dragDrop) { - on(scroller, "dragstart", onDragStart); - on(scroller, "dragenter", drag_); - on(scroller, "dragover", drag_); - on(scroller, "drop", operation(onDrop)); + // Used to get the editor into a consistent state again when options change. + + function loadMode(cm) { + var doc = cm.view.doc; + doc.mode = CodeMirror.getMode(cm.options, cm.options.mode); + doc.iter(0, doc.size, function(line) { line.stateAfter = null; }); + doc.frontier = 0; + startWorker(cm, 100); + } + + function wrappingChanged(cm) { + var doc = cm.view.doc; + if (cm.options.lineWrapping) { + cm.display.wrapper.className += " CodeMirror-wrap"; + var perLine = cm.display.wrapper.clientWidth / charWidth(cm.display) - 3; + doc.iter(0, doc.size, function(line) { + if (line.hidden) return; + var guess = Math.ceil(line.text.length / perLine) || 1; + if (guess != 1) updateLineHeight(line, guess); + }); + cm.display.sizer.style.minWidth = ""; + } else { + cm.display.wrapper.className = cm.display.wrapper.className.replace(" CodeMirror-wrap", ""); + computeMaxLength(cm.view); + doc.iter(0, doc.size, function(line) { + if (line.height != 1 && !line.hidden) updateLineHeight(line, 1); + }); } - on(scroller, "paste", function(){focusInput(); fastPoll();}); - on(input, "paste", fastPoll); - on(input, "cut", operation(function(){ - if (!options.readOnly) replaceSelection(""); - })); + regChange(cm, 0, doc.size); + } - // Needed to handle Tab key in KHTML - if (khtml) on(sizer, "mouseup", function() { - if (document.activeElement == input) input.blur(); - focusInput(); - }); + function keyMapChanged(cm) { + var style = keyMap[cm.options.keyMap].style; + cm.display.wrapper.className = cm.display.wrapper.className.replace(/\s*cm-keymap-\S+/g, "") + + (style ? " cm-keymap-" + style : ""); + } - // IE throws unspecified error in certain cases, when - // trying to access activeElement before onload - var hasFocus; try { hasFocus = (document.activeElement == input); } catch(e) { } - if (hasFocus || options.autofocus) setTimeout(onFocus, 20); - else onBlur(); + function themeChanged(cm) { + cm.display.wrapper.className = cm.display.wrapper.className.replace(/\s*cm-s-\S+/g, "") + + cm.options.theme.replace(/(^|\s)\s*/g, " cm-s-"); + } - function getLine(n) { return getLineAt(doc, n); } - function updateLineHeight(line, height) { - var diff = height - line.height; - for (var n = line; n; n = n.parent) n.height += diff; - } + function guttersChanged(cm) { + updateGutters(cm); + updateDisplay(cm, true); + } - function lineContent(line, wrapAt) { - if (!line.styles) - line.highlight(mode, line.stateAfter = getStateBefore(lineNo(line)), options.tabSize); - return line.getContent(options.tabSize, wrapAt, options.lineWrapping); + function updateGutters(cm) { + var gutters = cm.display.gutters, specs = cm.options.gutters; + removeChildren(gutters); + for (var i = 0; i < specs.length; ++i) { + var gutterClass = specs[i]; + var gElt = gutters.appendChild(elt("div", null, "CodeMirror-gutter " + gutterClass)); + if (gutterClass == "CodeMirror-linenumbers") { + cm.display.lineGutter = gElt; + gElt.style.width = (cm.display.lineNumWidth || 1) + "px"; + } } + gutters.style.display = i ? "" : "none"; + } - function setValue(code) { - var top = {line: 0, ch: 0}; - updateLines(top, {line: doc.size - 1, ch: getLine(doc.size-1).text.length}, - splitLines(code), top, top); - updateInput = true; - } - function getValue(lineSep) { - var text = []; - doc.iter(0, doc.size, function(line) { text.push(line.text); }); - return text.join(lineSep || "\n"); + function computeMaxLength(view) { + view.maxLine = getLine(view.doc, 0); view.maxLineChanged = true; + var maxLineLength = view.maxLine.text.length; + view.doc.iter(1, view.doc.size, function(line) { + var l = line.text; + if (!line.hidden && l.length > maxLineLength) { + maxLineLength = l.length; view.maxLine = line; + } + }); + } + + // Make sure the gutters options contains the element + // "CodeMirror-linenumbers" when the lineNumbers option is true. + function setGuttersForLineNumbers(options) { + var found = false; + for (var i = 0; i < options.gutters.length; ++i) { + if (options.gutters[i] == "CodeMirror-linenumbers") { + if (options.lineNumbers) found = true; + else options.gutters.splice(i--, 1); + } } + if (!found && options.lineNumbers) + options.gutters.push("CodeMirror-linenumbers"); + } - function compensateForHScroll() { - return scroller.getBoundingClientRect().left - sizer.getBoundingClientRect().left; + // SCROLLBARS + + // Re-synchronize the fake scrollbars with the actual size of the + // content. Optionally force a scrollTop. + function updateScrollbars(d /* display */, docHeight, scrollTop) { + d.sizer.style.minHeight = d.heightForcer.style.top = (docHeight + 2 * paddingTop(d)) + "px"; + var needsH = d.scroller.scrollWidth > d.scroller.clientWidth; + var needsV = d.scroller.scrollHeight > d.scroller.clientHeight; + if (needsV) { + d.scrollbarV.style.display = "block"; + d.scrollbarV.style.bottom = needsH ? scrollbarWidth(d.measure) + "px" : "0"; + d.scrollbarV.firstChild.style.height = + (d.scroller.scrollHeight - d.scroller.clientHeight + d.scrollbarV.clientHeight) + "px"; + if (scrollTop != null) { + d.scrollbarV.scrollTop = d.scroller.scrollTop = scrollTop; + // 'Nudge' the scrollbar to work around a Webkit bug where, + // in some situations, we'd end up with a scrollbar that + // reported its scrollTop (and looked) as expected, but + // *behaved* as if it was still in a previous state (i.e. + // couldn't scroll up, even though it appeared to be at the + // bottom). + if (webkit) setTimeout(function() { + if (d.scrollbarV.scrollTop != scrollTop) return; + d.scrollbarV.scrollTop = scrollTop + (scrollTop ? -1 : 1); + d.scrollbarV.scrollTop = scrollTop; + }, 0); + } + } else d.scrollbarV.style.display = ""; + if (needsH) { + d.scrollbarH.style.display = "block"; + d.scrollbarH.style.right = needsV ? scrollbarWidth(d.measure) + "px" : "0"; + d.scrollbarH.firstChild.style.width = + (d.scroller.scrollWidth - d.scroller.clientWidth + d.scrollbarH.clientWidth) + "px"; + } else d.scrollbarH.style.display = ""; + if (needsH && needsV) { + d.scrollbarFiller.style.display = "block"; + d.scrollbarFiller.style.height = d.scrollbarFiller.style.width = scrollbarWidth(d.measure) + "px"; + } else d.scrollbarFiller.style.display = ""; + + if (mac_geLion && scrollbarWidth(d.measure) === 0) + d.scrollbarV.style.minWidth = d.scrollbarH.style.minHeight = "12px"; + } + + function visibleLines(display, doc, scrollTop) { + var top = (scrollTop != null ? scrollTop : display.scroller.scrollTop) - paddingTop(display); + var fromHeight = Math.max(0, Math.floor(top)); + var toHeight = Math.ceil(top + display.wrapper.clientHeight); + return {from: lineAtHeight(doc, fromHeight), + to: lineAtHeight(doc, toHeight)}; + } + + // LINE NUMBERS + + function alignLineNumbers(display) { + if (display.alignWidgets) { + var margin = compensateForHScroll(display); + for (var n = display.lineDiv.firstChild; n; n = n.nextSibling) + for (var c = n.lastChild; c && c.widget; c = c.prevSibling) + if (c.widget.noHScroll) + c.style.marginLeft = (c.widget.coverGutter ? margin : margin + display.gutters.offsetWidth) + "px"; } - function lineNumberLeftPos() { - return compensateForHScroll() + lineGutter.offsetLeft; + if (!display.lineGutter) return; + var l = lineNumberLeftPos(display) + "px"; + for (var n = display.lineDiv.firstChild; n; n = n.nextSibling) + n.firstChild.style.left = l; + } + + function maybeUpdateLineNumberWidth(cm) { + if (!cm.options.lineNumbers) return false; + var doc = cm.view.doc, last = lineNumberFor(cm.options, doc.size - 1), display = cm.display; + if (last.length != display.lineNumChars) { + var test = display.measure.appendChild(elt("div", [elt("div", last)], + "CodeMirror-linenumber CodeMirror-gutter-elt")); + display.lineNumWidth = test.firstChild.offsetWidth; + display.lineNumChars = display.lineNumWidth ? last.length : -1; + display.lineGutter.style.width = display.lineNumWidth + "px"; + return true; } - function alignLineNumbers() { - if (alignWidgets) { - var margin = compensateForHScroll(); - for (var n = lineDiv.firstChild; n; n = n.nextSibling) - for (var c = n.lastChild; c && c.widget; c = c.prevSibling) - if (c.widget.noHScroll) - c.style.marginLeft = (c.widget.coverGutter ? margin : margin + gutters.offsetWidth) + "px"; - } - if (!options.lineNumbers) return; - var l = lineNumberLeftPos() + "px"; - for (var n = lineDiv.firstChild; n; n = n.nextSibling) - n.firstChild.style.left = l; + return false; + } + + function lineNumberFor(options, i) { + return String(options.lineNumberFormatter(i + options.firstLineNumber)); + } + function compensateForHScroll(display) { + return display.scroller.getBoundingClientRect().left - display.sizer.getBoundingClientRect().left; + } + function lineNumberLeftPos(display) { + return compensateForHScroll(display) + display.lineGutter.offsetLeft; + } + + // DISPLAY DRAWING + + function updateDisplay(cm, changes, scrollTop) { + var oldFrom = cm.display.showingFrom, oldTo = cm.display.showingTo; + var updated = updateDisplayInner(cm, changes, scrollTop); + if (updated) { + signalLater(cm, cm, "update", cm); + if (cm.display.showingFrom != oldFrom || cm.display.showingTo != oldTo) + signalLater(cm, cm, "update", cm, cm.display.showingFrom, cm.display.showingTo); } + updateSelection(cm); + updateScrollbars(cm.display, cm.view.doc.height, scrollTop); + return updated; + } - function onScrollBarH() { - if (scrollbarH.scrollLeft != lastScrollLeft) { - scroller.scrollLeft = lastScrollLeft = scrollbarH.scrollLeft; - alignLineNumbers(); + // Uses a set of changes plus the current scroll position to + // determine which DOM updates have to be made, and makes the + // updates. + function updateDisplayInner(cm, changes, scrollTop) { + var display = cm.display, doc = cm.view.doc; + if (!display.wrapper.clientWidth) { + display.showingFrom = display.showingTo = display.viewOffset = 0; + return; + } + + // Compute the new visible window + // If scrollTop is specified, use that to determine which lines + // to render instead of the current scrollbar position. + var visible = visibleLines(display, doc, scrollTop); + // Bail out if the visible area is already rendered and nothing changed. + if (changes !== true && changes.length == 0 && + visible.from > display.showingFrom && visible.to < display.showingTo) + return; + + if (changes && changes !== true && maybeUpdateLineNumberWidth(cm)) + changes = true; + display.sizer.style.marginLeft = display.scrollbarH.style.left = display.gutters.offsetWidth + "px"; + // Used to determine which lines need their line numbers updated + var positionsChangedFrom = changes === true ? 0 : Infinity; + if (cm.options.lineNumbers && changes && changes !== true) + for (var i = 0; i < changes.length; ++i) + if (changes[i].diff) { positionsChangedFrom = changes[i].from; break; } + + var from = Math.max(visible.from - 100, 0), to = Math.min(doc.size, visible.to + 100); + if (display.showingFrom < from && from - display.showingFrom < 20) from = display.showingFrom; + if (display.showingTo > to && display.showingTo - to < 20) to = Math.min(doc.size, display.showingTo); + + // Create a range of theoretically intact lines, and punch holes + // in that using the change info. + var intact = changes === true ? [] : + computeIntact([{from: display.showingFrom, to: display.showingTo, domStart: 0}], changes); + // Clip off the parts that won't be visible + var intactLines = 0; + for (var i = 0; i < intact.length; ++i) { + var range = intact[i]; + if (range.from < from) {range.domStart += (from - range.from); range.from = from;} + if (range.to > to) range.to = to; + if (range.from >= range.to) intact.splice(i--, 1); + else intactLines += range.to - range.from; + } + if (intactLines == to - from && from == display.showingFrom && to == display.showingTo) + return; + intact.sort(function(a, b) {return a.domStart - b.domStart;}); + + display.lineDiv.style.display = "none"; + patchDisplay(cm, from, to, intact, positionsChangedFrom); + display.lineDiv.style.display = ""; + + var different = from != display.showingFrom || to != display.showingTo || + display.lastSizeC != display.wrapper.clientHeight; + // This is just a bogus formula that detects when the editor is + // resized or the font size changes. + if (different) display.lastSizeC = display.wrapper.clientHeight; + display.showingFrom = from; display.showingTo = to; + display.viewOffset = heightAtLine(doc, from); + startWorker(cm, 100); + + // Since this is all rather error prone, it is honoured with the + // only assertion in the whole file. + if (display.lineDiv.childNodes.length != display.showingTo - display.showingFrom) + throw new Error("BAD PATCH! " + JSON.stringify(intact) + " size=" + (display.showingTo - display.showingFrom) + + " nodes=" + display.lineDiv.childNodes.length); + + // Update line heights for visible lines based on actual DOM + // sizes + var curNode = display.lineDiv.firstChild, heightChanged = false; + var relativeTo = curNode.offsetTop; + doc.iter(display.showingFrom, display.showingTo, function(line) { + // Work around bizarro IE7 bug where, sometimes, our curNode + // is magically replaced with a new node in the DOM, leaving + // us with a reference to an orphan (nextSibling-less) node. + if (!curNode) return; + if (!line.hidden) { + var end = curNode.offsetHeight + curNode.offsetTop; + var height = end - relativeTo, diff = line.height - height; + if (height < 2) height = textHeight(display); + relativeTo = end; + if (diff > .001 || diff < -.001) { + updateLineHeight(line, height); + heightChanged = true; + } } - } - function onScrollBarV() { - if (scrollbarV.scrollTop != lastScrollTop) { - scroller.scrollTop = lastScrollTop = scrollbarV.scrollTop; - updateDisplay([]); + curNode = curNode.nextSibling; + }); + + // Position the mover div to align with the current virtual scroll position + display.mover.style.top = display.viewOffset + "px"; + return true; + } + + function computeIntact(intact, changes) { + for (var i = 0, l = changes.length || 0; i < l; ++i) { + var change = changes[i], intact2 = [], diff = change.diff || 0; + for (var j = 0, l2 = intact.length; j < l2; ++j) { + var range = intact[j]; + if (change.to <= range.from && change.diff) + intact2.push({from: range.from + diff, to: range.to + diff, + domStart: range.domStart}); + else if (change.to <= range.from || change.from >= range.to) + intact2.push(range); + else { + if (change.from > range.from) + intact2.push({from: range.from, to: change.from, domStart: range.domStart}); + if (change.to < range.to) + intact2.push({from: change.to + diff, to: range.to + diff, + domStart: range.domStart + (change.to - range.from)}); + } } + intact = intact2; } + return intact; + } - function onScrollMain() { - if (scroller.scrollTop != lastScrollTop) { - scrollbarV.scrollTop = lastScrollTop = scroller.scrollTop; - updateDisplay([]); + function patchDisplay(cm, from, to, intact, updateNumbersFrom) { + function killNode(node) { + var tmp = node.nextSibling; + node.parentNode.removeChild(node); + return tmp; + } + var display = cm.display, lineNumbers = cm.options.lineNumbers; + var lineNumberPos = lineNumbers && lineNumberLeftPos(display); + // The first pass removes the DOM nodes that aren't intact. + if (!intact.length) removeChildren(display.lineDiv); + else { + var domPos = 0, curNode = display.lineDiv.firstChild, n; + for (var i = 0; i < intact.length; ++i) { + var cur = intact[i]; + while (cur.domStart > domPos) {curNode = killNode(curNode); domPos++;} + for (var j = cur.from, e = cur.to; j < e; ++j) { + if (lineNumbers && updateNumbersFrom <= j && curNode.firstChild) + setTextContent(curNode.firstChild, lineNumberFor(cm.options, j)); + curNode = curNode.nextSibling; domPos++; + } } - if (scroller.scrollLeft != lastScrollLeft) { - scrollbarH.scrollLeft = lastScrollLeft = scroller.scrollLeft; - alignLineNumbers(); + while (curNode) curNode = killNode(curNode); + } + // This pass fills in the lines that actually changed. + var nextIntact = intact.shift(), curNode = display.lineDiv.firstChild; + var j = from, gutterSpecs = cm.options.gutters; + cm.view.doc.iter(from, to, function(line) { + if (nextIntact && nextIntact.to == j) nextIntact = intact.shift(); + if (!nextIntact || nextIntact.from > j) { + if (line.hidden) var lineElement = elt("div"); + else { + var lineElement = lineContent(cm, line), markers = line.gutterMarkers; + if (line.className) lineElement.className = line.className; + // Lines with gutter elements or a background class need + // to be wrapped again, and have the extra elements added + // to the wrapper div + if (lineNumbers || markers || line.bgClassName || (line.widgets && line.widgets.length)) { + var inside = []; + if (lineNumbers) + inside.push(elt("div", lineNumberFor(cm.options, j), + "CodeMirror-linenumber CodeMirror-gutter-elt", + "left: " + lineNumberPos + "px; width: " + + display.lineNumWidth + "px")); + if (markers) + for (var k = 0; k < gutterSpecs.length; ++k) { + var id = gutterSpecs[k], found = markers.hasOwnProperty(id) && markers[id]; + if (found) { + var gutterElt = display.gutters.childNodes[k]; + inside.push(elt("div", [found], "CodeMirror-gutter-elt", "left: " + + (gutterElt.offsetLeft - display.gutters.offsetWidth) + + "px; width: " + gutterElt.clientWidth + "px")); + } + } + // Kludge to make sure the styled element lies behind the selection (by z-index) + if (line.bgClassName) + inside.push(elt("div", "\u00a0", line.bgClassName + " CodeMirror-linebackground")); + inside.push(lineElement); + if (line.widgets) + for (var i = 0, ws = line.widgets; i < ws.length; ++i) { + var widget = ws[i], node = elt("div", [widget.node], "CodeMirror-linewidget"); + node.widget = widget; + if (widget.noHScroll) + node.style.width = display.wrapper.clientWidth + "px"; + if (widget.coverGutter) { + node.style.zIndex = 5; + node.style.position = "relative"; + node.style.marginLeft = + (widget.noHScroll ? compensateForHScroll(display) : -display.gutters.offsetWidth) + "px"; + } + inside.push(node); + } + lineElement = elt("div", inside, null, "position: relative"); + if (ie_lt8) lineElement.style.zIndex = 2; + } + } + display.lineDiv.insertBefore(lineElement, curNode); + } else { + curNode = curNode.nextSibling; } - signal(instance, "scroll", instance); - } + ++j; + }); + } - function mouseEventInWidget(e) { - for (var n = e_target(e); n != wrapper; n = n.parentNode) - if (/\bCodeMirror-linewidget\b/.test(n.className) || - n.parentNode == sizer && n != mover) return true; - } + // SELECTION / CURSOR - function onMouseDown(e) { - setShift(e_prop(e, "shiftKey")); + function selHead(view) { + return view.sel.inverted ? view.sel.from : view.sel.to; + } - if (mouseEventInWidget(e)) { - if (!webkit) { - scroller.draggable = false; - setTimeout(function(){scroller.draggable = true;}, 100); - } - return; - } - if (clickInGutter(e)) return; - var start = posFromMouse(e); - - switch (e_button(e)) { - case 3: - if (gecko) onContextMenu(e); - return; - case 2: - if (start) setCursor(start.line, start.ch, true); - setTimeout(focusInput, 20); - e_preventDefault(e); - return; - } - // For button 1, if it was clicked inside the editor - // (posFromMouse returning non-null), we have to adjust the - // selection. - if (!start) {if (e_target(e) == scroller) e_preventDefault(e); return;} - - if (!focused) onFocus(); - - var now = +new Date, type = "single"; - if (lastDoubleClick && lastDoubleClick.time > now - 400 && posEq(lastDoubleClick.pos, start)) { - type = "triple"; - e_preventDefault(e); - setTimeout(focusInput, 20); - selectLine(start.line); - } else if (lastClick && lastClick.time > now - 400 && posEq(lastClick.pos, start)) { - type = "double"; - lastDoubleClick = {time: now, pos: start}; - e_preventDefault(e); - var word = findWordAt(start); - setSelectionUser(word.from, word.to); - } else { lastClick = {time: now, pos: start}; } - - var last = start, going; - if (options.dragDrop && dragAndDrop && !options.readOnly && !posEq(sel.from, sel.to) && - !posLess(start, sel.from) && !posLess(sel.to, start) && type == "single") { - var dragEnd = operation(function(e2) { - if (webkit) scroller.draggable = false; - draggingText = false; - off(document, "mouseup", dragEnd); - off(scroller, "drop", dragEnd); - if (Math.abs(e.clientX - e2.clientX) + Math.abs(e.clientY - e2.clientY) < 10) { - e_preventDefault(e2); - setCursor(start.line, start.ch, true); - focusInput(); + function updateSelection(cm) { + var headPos, display = cm.display, doc = cm.view.doc, sel = cm.view.sel; + if (posEq(sel.from, sel.to)) { // No selection, single cursor + var pos = headPos = cursorCoords(cm, sel.from, "div"); + display.cursor.style.left = pos.left + "px"; + display.cursor.style.top = pos.top + "px"; + display.cursor.style.height = (pos.bottom - pos.top) * .85 + "px"; + display.cursor.style.display = ""; + display.selectionDiv.style.display = "none"; + + if (pos.other) { + display.otherCursor.style.display = ""; + display.otherCursor.style.left = pos.other.left + "px"; + display.otherCursor.style.top = pos.other.top + "px"; + display.otherCursor.style.height = (pos.other.bottom - pos.other.top) * .85 + "px"; + } else { display.otherCursor.style.display = "none"; } + } else { + headPos = cursorCoords(cm, selHead(cm.view), "div"); + var fragment = document.createDocumentFragment(); + var clientWidth = display.lineSpace.clientWidth || display.lineSpace.offsetWidth; + var add = function(left, top, right, bottom) { + if (top < 0) top = 0; + var rstyle = quirksMode ? "width: " + (!right ? clientWidth : clientWidth - right - left) + "px" + : "right: " + right + "px"; + fragment.appendChild(elt("div", null, "CodeMirror-selected", "position: absolute; left: " + left + + "px; top: " + top + "px; " + rstyle + "; height: " + (bottom - top) + "px")); + }; + + var middleFrom = sel.from.line + 1, middleTo = sel.to.line - 1, sameLine = sel.from.line == sel.to.line; + var drawForLine = function(line, from, toArg, retTop) { + var lineObj = getLine(doc, line), lineLen = lineObj.text.length; + var coords = function(ch) { return charCoords(cm, {line: line, ch: ch}, "div", lineObj); }; + var rVal = retTop ? Infinity : -Infinity; + iterateBidiSections(getOrder(lineObj), from, toArg == null ? lineLen : toArg, function(from, to, dir) { + var leftPos = coords(dir == "rtl" ? to - 1 : from); + var rightPos = coords(dir == "rtl" ? from : to - 1); + var left = leftPos.left, right = rightPos.right; + if (rightPos.top - leftPos.top > 3) { // Different lines, draw top part + add(left, leftPos.top, 0, leftPos.bottom); + left = paddingLeft(display); + if (leftPos.bottom < rightPos.top) add(left, leftPos.bottom, 0, rightPos.top); } + if (toArg == null && to == lineLen) right = clientWidth; + rVal = retTop ? Math.min(rightPos.top, rVal) : Math.max(rightPos.bottom, rVal); + add(left, rightPos.top, clientWidth - right, rightPos.bottom); }); - // Let the drag handler handle this. - if (webkit) scroller.draggable = true; - draggingText = true; - // IE's approach to draggable - if (scroller.dragDrop) scroller.dragDrop(); - on(document, "mouseup", dragEnd); - on(scroller, "drop", dragEnd); - return; + return rVal; + }; + + var middleTop = Infinity, middleBot = -Infinity; + if (sel.from.ch || sameLine) + // Draw the first line of selection. + middleTop = drawForLine(sel.from.line, sel.from.ch, sameLine ? sel.to.ch : null); + else + // Simply include it in the middle block. + middleFrom = sel.from.line; + + if (!sameLine && sel.to.ch) + middleBot = drawForLine(sel.to.line, 0, sel.to.ch, true); + + if (middleFrom <= middleTo) { + // Draw the middle + var botLine = getLine(doc, middleTo), + bottom = charCoords(cm, {line: middleTo, ch: botLine.text.length}, "div", botLine); + // Kludge to try and prevent fetching coordinates twice if + // start end end are on same line. + var top = (middleFrom != middleTo || botLine.height > bottom.bottom - bottom.top) ? + charCoords(cm, {line: middleFrom, ch: 0}, "div") : bottom; + middleTop = Math.min(middleTop, top.top); + middleBot = Math.max(middleBot, bottom.bottom); + } + if (middleTop < middleBot) add(paddingLeft(display), middleTop, 0, middleBot); + + removeChildrenAndAdd(display.selectionDiv, fragment); + display.cursor.style.display = display.otherCursor.style.display = "none"; + display.selectionDiv.style.display = ""; + } + + // Move the hidden textarea near the cursor to prevent scrolling artifacts + var wrapOff = display.wrapper.getBoundingClientRect(), lineOff = display.lineDiv.getBoundingClientRect(); + display.inputDiv.style.top = Math.max(0, Math.min(display.wrapper.clientHeight - 10, + headPos.top + lineOff.top - wrapOff.top)) + "px"; + display.inputDiv.style.left = Math.max(0, Math.min(display.wrapper.clientWidth - 10, + headPos.left + lineOff.left - wrapOff.left)) + "px"; + } + + // Cursor-blinking + function restartBlink(cm) { + var display = cm.display; + clearInterval(display.blinker); + var on = true; + display.cursor.style.visibility = display.otherCursor.style.visibility = ""; + display.blinker = setInterval(function() { + display.cursor.style.visibility = display.otherCursor.style.visibility = (on = !on) ? "" : "hidden"; + }, cm.options.cursorBlinkRate); + } + + // HIGHLIGHT WORKER + + function startWorker(cm, time) { + if (cm.view.doc.frontier < cm.display.showingTo) + cm.view.doc.highlight.set(time, bind(highlightWorker, cm)); + } + + function highlightWorker(cm) { + var doc = cm.view.doc; + if (doc.frontier >= cm.display.showingTo) return; + var end = +new Date + cm.options.workTime; + var state = copyState(doc.mode, getStateBefore(doc, doc.frontier)); + var startFrontier = doc.frontier; + doc.iter(doc.frontier, cm.display.showingTo, function(line) { + if (doc.frontier >= cm.display.showingFrom) { // Visible + line.highlight(doc.mode, state, cm.options.tabSize); + line.stateAfter = copyState(doc.mode, state); + } else { + line.process(doc.mode, state, cm.options.tabSize); + line.stateAfter = doc.frontier % 5 == 0 ? copyState(doc.mode, state) : null; } - e_preventDefault(e); - if (type == "single") setCursor(start.line, start.ch, true); - - var startstart = sel.from, startend = sel.to; - - function doSelect(cur) { - if (type == "single") { - setSelectionUser(start, cur); - } else if (type == "double") { - var word = findWordAt(cur); - if (posLess(cur, startstart)) setSelectionUser(word.from, startend); - else setSelectionUser(startstart, word.to); - } else if (type == "triple") { - if (posLess(cur, startstart)) setSelectionUser(startend, clipPos({line: cur.line, ch: 0})); - else setSelectionUser(startstart, clipPos({line: cur.line + 1, ch: 0})); - } + ++doc.frontier; + if (+new Date > end) { + startWorker(cm, cm.options.workDelay); + return true; } + }); + if (cm.display.showingTo > startFrontier && doc.frontier >= cm.display.showingFrom) + operation(cm, function() {regChange(this, startFrontier, doc.frontier);})(); + } - function extend(e) { - var cur = posFromMouse(e, true); - if (cur && !posEq(cur, last)) { - if (!focused) onFocus(); - last = cur; - doSelect(cur); - updateInput = false; - var visible = visibleLines(); - if (cur.line >= visible.to || cur.line < visible.from) - going = setTimeout(operation(function(){extend(e);}), 150); - } + // Finds the line to start with when starting a parse. Tries to + // find a line with a stateAfter, so that it can start with a + // valid state. If that fails, it returns the line with the + // smallest indentation, which tends to need the least context to + // parse correctly. + function findStartLine(doc, n) { + var minindent, minline; + for (var search = n, lim = n - 40; search > lim; --search) { + if (search == 0) return 0; + var line = getLine(doc, search-1); + if (line.stateAfter) return search; + var indented = line.indentation(doc.tabSize); + if (minline == null || minindent > indented) { + minline = search - 1; + minindent = indented; + } + } + return minline; + } + + function getStateBefore(doc, n) { + var pos = findStartLine(doc, n), state = pos && getLine(doc, pos-1).stateAfter; + if (!state) state = startState(doc.mode); + else state = copyState(doc.mode, state); + doc.iter(pos, n, function(line) { + line.process(doc.mode, state, doc.tabSize); + line.stateAfter = (pos == n - 1 || pos % 5 == 0) ? copyState(doc.mode, state) : null; + }); + return state; + } + + // POSITION MEASUREMENT + + function paddingTop(display) {return display.lineSpace.offsetTop;} + function paddingLeft(display) { + var e = removeChildrenAndAdd(display.measure, elt("pre")).appendChild(elt("span", "x")); + return e.offsetLeft; + } + + function measureLine(cm, line, ch) { + var wrapping = cm.options.lineWrapping, display = cm.display; + // First look in the cache + var cache = cm.display.measureLineCache; + for (var i = 0; i < cache.length; ++i) { + var memo = cache[i]; + if (memo.ch == ch && memo.text == line.text && + (!wrapping || display.scroller.clientWidth == memo.width)) + return {top: memo.top, bottom: memo.bottom, left: memo.left, right: memo.right}; + } + + var atEnd = ch && ch == line.text.length; + var pre = lineContent(cm, line, atEnd ? ch - 1 : ch); + removeChildrenAndAdd(display.measure, pre); + var anchor = pre.anchor; + // We'll sample once at the top, once at the bottom of the line, + // to get the real line height (in case there tokens on the line + // with bigger fonts) + anchor.style.verticalAlign = "top"; + var left = anchor.offsetLeft, right = left + anchor.offsetWidth; + if (ie) { + // In IE, verticalAlign does not influence offsetTop, unless + // the element is an inline-block. Unfortunately, inline + // blocks have different wrapping behaviour, so we have to do + // some icky thing with inserting "Zero-Width No-Break Spaces" + // to compensate for wrapping artifacts. + anchor.style.display = "inline-block"; + if (wrapping && anchor.offsetLeft != left) { + anchor.parentNode.insertBefore(document.createTextNode("\ufeff"), anchor); + if (anchor.offsetLeft != left) + anchor.parentNode.insertBefore(document.createTextNode("\ufeff"), anchor.nextSibling); + if (anchor.offsetLeft != left) + anchor.parentNode.removeChild(anchor.previousSibling); + } + } + var top = Math.max(0, anchor.offsetTop); + anchor.style.verticalAlign = "bottom"; + var bottom = Math.min(anchor.offsetTop + anchor.offsetHeight, pre.offsetHeight); + if (atEnd) left = right; + + // Store result in the cache + var memo = {ch: ch, text: line.text, width: display.wrapper.clientWidth, + top: top, bottom: bottom, left: left, right: right}; + if (cache.length == 8) cache[++cache.pos % 8] = memo; + else cache.push(memo); + + return {top: top, bottom: bottom, left: left, right: right}; + } + + function intoCoordSystem(cm, pos, rect, context) { + if (context == "line") return rect; + var yOff = heightAtLine(cm.view.doc, pos.line) - (context == "div" ? cm.display.viewOffset : 0); + if (context == "page") { + var lOff = cm.display.lineSpace.getBoundingClientRect(); + yOff += lOff.top; rect.left += lOff.left; rect.right += lOff.right; + } + rect.top += yOff; rect.bottom += yOff; + return rect; + } + + function charCoords(cm, pos, context, lineObj) { + if (!lineObj) lineObj = getLine(cm.view.doc, pos.line); + return intoCoordSystem(cm, pos, measureLine(cm, lineObj, pos.ch), context); + } + + function cursorCoords(cm, pos, context, lineObj) { + lineObj = lineObj || getLine(cm.view.doc, pos.line); + function get(ch, right) { + var m = measureLine(cm, lineObj, ch); + if (right) m.left = m.right; else m.right = m.left; + return intoCoordSystem(cm, pos, m, context); + } + var order = getOrder(lineObj), ch = pos.ch; + if (!order) return get(ch); + var main, other, linedir = order[0].level; + for (var i = 0; i < order.length; ++i) { + var part = order[i], rtl = part.level % 2, nb, here; + if (part.from < ch && part.to > ch) return get(ch, rtl); + var left = rtl ? part.to : part.from, right = rtl ? part.from : part.to; + if (left == ch) { + // Opera and IE return bogus offsets and widths for edges + // where the direction flips, but only for the side with the + // lower level. So we try to use the side with the higher + // level. + if (i && part.level < (nb = order[i-1]).level) here = get(nb.level % 2 ? nb.from : nb.to - 1, true); + else here = get(rtl && part.from != part.to ? ch - 1 : ch); + if (rtl == linedir) main = here; else other = here; + } else if (right == ch) { + var nb = i < order.length - 1 && order[i+1]; + if (!rtl && nb && nb.from == nb.to) continue; + if (nb && part.level < nb.level) here = get(nb.level % 2 ? nb.to - 1 : nb.from); + else here = get(rtl ? ch : ch - 1, true); + if (rtl == linedir) main = here; else other = here; + } + } + if (linedir && !ch) other = get(order[0].to - 1); + if (!main) return other; + if (other) main.other = other; + return main; + } + + // Coords must be lineSpace-local + function coordsChar(cm, x, y) { + var display = cm.display, doc = cm.view.doc; + var cw = charWidth(display), heightPos = display.viewOffset + y; + if (heightPos < 0) return {line: 0, ch: 0}; + var lineNo = lineAtHeight(doc, heightPos); + if (lineNo >= doc.size) return {line: doc.size - 1, ch: getLine(doc, doc.size - 1).text.length}; + var lineObj = getLine(doc, lineNo); + if (!lineObj.text.length) return {line: lineNo, ch: 0}; + var tw = cm.options.lineWrapping, innerOff = tw ? heightPos - heightAtLine(doc, lineNo) : 0; + if (x < 0) x = 0; + var wrongLine = false; + function getX(ch) { + var sp = cursorCoords(cm, {line: lineNo, ch: ch}, "line", lineObj); + if (tw) { + wrongLine = true; + if (innerOff > sp.bottom) return Math.max(0, sp.left - display.wrapper.clientWidth); + else if (innerOff < sp.top) return sp.left + display.wrapper.clientWidth; + else wrongLine = false; + } + return sp.left; + } + var bidi = getOrder(lineObj), dist = lineObj.text.length; + var from = lineLeft(lineObj), fromX = 0, to = lineRight(lineObj), toX; + if (!bidi) { + // Guess a suitable upper bound for our search. + var estimated = Math.min(to, Math.ceil((x + Math.floor(innerOff / textHeight(display)) * + display.wrapper.clientWidth * .9) / cw)); + for (;;) { + var estX = getX(estimated); + if (estX <= x && estimated < to) estimated = Math.min(to, Math.ceil(estimated * 1.2)); + else {toX = estX; to = estimated; break;} + } + // Try to guess a suitable lower bound as well. + estimated = Math.floor(to * 0.8); estX = getX(estimated); + if (estX < x) {from = estimated; fromX = estX;} + dist = to - from; + } else toX = getX(to); + if (x > toX) return {line: lineNo, ch: to}; + // Do a binary search between these bounds. + for (;;) { + if (bidi ? to == from || to == moveVisually(lineObj, from, 1) : to - from <= 1) { + var after = x - fromX < toX - x, ch = after ? from : to; + while (isExtendingChar.test(lineObj.text.charAt(ch))) ++ch; + return {line: lineNo, ch: ch, after: after}; + } + var step = Math.ceil(dist / 2), middle = from + step; + if (bidi) { + middle = from; + for (var i = 0; i < step; ++i) middle = moveVisually(lineObj, middle, 1); + } + var middleX = getX(middle); + if (middleX > x) {to = middle; toX = middleX; if (wrongLine) toX += 1000; dist -= step;} + else {from = middle; fromX = middleX; dist = step;} + } + } + + var measureText; + function textHeight(display) { + if (display.cachedTextHeight != null) return display.cachedTextHeight; + if (measureText == null) { + measureText = elt("pre"); + // Measure a bunch of lines, for browsers that compute + // fractional heights. + for (var i = 0; i < 49; ++i) { + measureText.appendChild(document.createTextNode("x")); + measureText.appendChild(elt("br")); + } + measureText.appendChild(document.createTextNode("x")); + } + removeChildrenAndAdd(display.measure, measureText); + var height = measureText.offsetHeight / 50; + if (height > 3) display.cachedTextHeight = height; + removeChildren(display.measure); + return height || 1; + } + + function charWidth(display) { + if (display.cachedCharWidth != null) return display.cachedCharWidth; + var anchor = elt("span", "x"); + var pre = elt("pre", [anchor]); + removeChildrenAndAdd(display.measure, pre); + var width = anchor.offsetWidth; + if (width > 2) display.cachedCharWidth = width; + return width || 10; + } + + // OPERATIONS + + // Operations are used to wrap changes in such a way that each + // change won't have to update the cursor and display (which would + // be awkward, slow, and error-prone), but instead updates are + // batched and then all combined and executed at once. + + function startOperation(cm) { + if (cm.curOp) ++cm.curOp.depth; + else cm.curOp = { + // Nested operations delay update until the outermost one + // finishes. + depth: 1, + // An array of ranges of lines that have to be updated. See + // updateDisplay. + changes: [], + delayedCallbacks: [], + updateInput: null, + userSelChange: null, + textChanged: null, + selectionChanged: false, + updateMaxLine: false + }; + } + + function endOperation(cm) { + var op = cm.curOp; + if (--op.depth) return; + cm.curOp = null; + var view = cm.view, display = cm.display; + if (op.updateMaxLine) computeMaxLength(view); + if (view.maxLineChanged && !cm.options.lineWrapping) { + var width = measureLine(cm, view.maxLine, view.maxLine.text.length).left; + display.sizer.style.minWidth = (width + 3 + scrollerCutOff) + "px"; + view.maxLineChanged = false; + } + var newScrollPos, updated; + if (op.selectionChanged) { + var coords = cursorCoords(cm, selHead(view)); + newScrollPos = calculateScrollPos(display, coords.left, coords.top, coords.left, coords.bottom); + } + if (op.changes.length || newScrollPos && newScrollPos.scrollTop != null) + updated = updateDisplay(cm, op.changes, newScrollPos && newScrollPos.scrollTop); + if (!updated && op.selectionChanged) updateSelection(cm); + if (newScrollPos) scrollCursorIntoView(cm); + if (op.selectionChanged) restartBlink(cm); + + if (view.focused && (op.updateInput === true || (op.updateInput !== false && op.selectionChanged))) + resetInput(display, view, op.userSelChange); + + if (op.textChanged) + signal(cm, "change", cm, op.textChanged); + if (op.selectionChanged) signal(cm, "cursorActivity", cm); + for (var i = 0; i < op.delayedCallbacks.length; ++i) op.delayedCallbacks[i](cm); + } + + // Wraps a function in an operation. Returns the wrapped function. + function operation(cm1, f) { + return function() { + var cm = cm1 || this; + startOperation(cm); + try {var result = f.apply(cm, arguments);} + finally {endOperation(cm);} + return result; + }; + } + + function regChange(cm, from, to, lendiff) { + cm.curOp.changes.push({from: from, to: to, diff: lendiff}); + } + + function compoundChange(cm, f) { + var hist = cm.view.doc.history; + hist.startCompound(); + try { return f(); } finally { hist.endCompound(); } + } + + // INPUT HANDLING + + function slowPoll(cm) { + if (cm.view.pollingFast) return; + cm.display.poll.set(cm.options.pollInterval, function() { + readInput(cm); + if (cm.view.focused) slowPoll(cm); + }); + } + + function fastPoll(cm) { + var missed = false; + cm.display.pollingFast = true; + function p() { + var changed = readInput(cm); + if (!changed && !missed) {missed = true; cm.display.poll.set(60, p);} + else {cm.display.pollingFast = false; slowPoll(cm);} + } + cm.display.poll.set(20, p); + } + + // prevInput is a hack to work with IME. If we reset the textarea + // on every change, that breaks IME. So we look for changes + // compared to the previous content instead. (Modern browsers have + // events that indicate IME taking place, but these are not widely + // supported or compatible enough yet to rely on.) + function readInput(cm) { + var input = cm.display.input, prevInput = cm.display.prevInput, view = cm.view, sel = view.sel; + if (!view.focused || hasSelection(input) || cm.options.readOnly) return false; + var text = input.value; + if (text == prevInput) return false; + startOperation(cm); + view.sel.shift = null; + var same = 0, l = Math.min(prevInput.length, text.length); + while (same < l && prevInput[same] == text[same]) ++same; + if (same < prevInput.length) + sel.from = {line: sel.from.line, ch: sel.from.ch - (prevInput.length - same)}; + else if (view.overwrite && posEq(sel.from, sel.to)) + sel.to = {line: sel.to.line, ch: Math.min(getLine(doc, sel.to.line).text.length, sel.to.ch + (text.length - same))}; + cm.replaceSelection(text.slice(same), "end"); + if (text.length > 1000) { input.value = cm.display.prevInput = ""; } + else cm.display.prevInput = text; + endOperation(cm); + return true; + } + + function resetInput(display, view, user) { + if (!posEq(view.sel.from, view.sel.to)) { + display.prevInput = ""; + display.input.value = getSelection(view); + if (view.focused) selectInput(display.input); + } else if (user) display.prevInput = display.input.value = ""; + } + + function focusInput(cm) { + if (cm.options.readOnly != "nocursor") cm.display.input.focus(); + } + + // EVENT HANDLERS + + function registerEventHandlers(cm) { + var d = cm.display; + on(d.scroller, "mousedown", operation(cm, onMouseDown)); + on(d.gutters, "mousedown", operation(cm, clickInGutter)); + on(d.scroller, "dblclick", operation(cm, e_preventDefault)); + on(d.lineSpace, "selectstart", function(e) { + if (!mouseEventInWidget(d, e)) e_preventDefault(e); + }); + // Gecko browsers fire contextmenu *after* opening the menu, at + // which point we can't mess with it anymore. Context menu is + // handled in onMouseDown for Gecko. + if (!gecko) on(d.scroller, "contextmenu", function(e) {onContextMenu(cm, e);}); + + on(d.scroller, "scroll", function() { + if (d.scroller.scrollTop != cm.view.scrollTop) { + d.scrollbarV.scrollTop = cm.view.scrollTop = d.scroller.scrollTop; + updateDisplay(cm, []); + } + if (d.scroller.scrollLeft != cm.view.scrollLeft) { + d.scrollbarH.scrollLeft = cm.view.scrollLeft = d.scroller.scrollLeft; + alignLineNumbers(cm.display); + } + signal(cm, "scroll", cm); + }); + on(d.scrollbarV, "scroll", function() { + if (d.scrollbarV.scrollTop != cm.view.scrollTop) { + d.scroller.scrollTop = cm.view.scrollTop = d.scrollbarV.scrollTop; + updateDisplay(cm, []); + } + }); + on(d.scrollbarH, "scroll", function() { + if (d.scrollbarH.scrollLeft != cm.view.scrollLeft) { + d.scroller.scrollLeft = cm.view.scrollLeft = d.scrollbarH.scrollLeft; + alignLineNumbers(cm.display); } + }); - function done(e) { - clearTimeout(going); - var cur = posFromMouse(e); - if (cur) doSelect(cur); - e_preventDefault(e); - focusInput(); - updateInput = true; - off(document, "mousemove", move); - off(document, "mouseup", up); - } - - var move = operation(function(e) { - clearTimeout(going); - e_preventDefault(e); - if (!ie && !e_button(e)) done(e); - else extend(e); - }); - var up = operation(done); - on(document, "mousemove", move); - on(document, "mouseup", up); + function reFocus() { if (cm.view.focused) setTimeout(bind(focusInput, cm.display), 0); } + on(d.scrollbarH, "mousedown", reFocus); + on(d.scrollbarV, "mousedown", reFocus); + // Prevent wrapper from ever scrolling + on(d.wrapper, "scroll", function() { d.wrapper.scrollTop = d.wrapper.scrollLeft = 0; }); + on(window, "resize", function resizeHandler() { + // Might be a text scaling operation, clear size caches. + d.cachedCharWidth = d.cachedTextHeight = null; + d.measureLineCache.length = d.measureLineCache.pos = 0; + if (d.wrapper.parentNode) updateDisplay(cm, true); + else off(window, "resize", resizeHandler); + }); + + on(d.input, "keyup", operation(cm, function(e) { + if (cm.options.onKeyEvent && cm.options.onKeyEvent(cm, addStop(e))) return; + if (e_prop(e, "keyCode") == 16) cm.view.sel.shift = null; + })); + on(d.input, "input", bind(fastPoll, cm)); + on(d.input, "keydown", operation(cm, onKeyDown)); + on(d.input, "keypress", operation(cm, onKeyPress)); + on(d.input, "focus", bind(onFocus, cm)); + on(d.input, "blur", bind(onBlur, cm)); + + function drag_(e) { + if (options.onDragEvent && options.onDragEvent(cm, addStop(e))) return; + e_stop(e); + } + if (cm.options.dragDrop) { + on(d.scroller, "dragstart", function(e){onDragStart(cm, e);}); + on(d.scroller, "dragenter", drag_); + on(d.scroller, "dragover", drag_); + on(d.scroller, "drop", operation(cm, onDrop)); + } + on(d.scroller, "paste", function(){focusInput(cm); fastPoll(cm);}); + on(d.input, "paste", bind(fastPoll, cm)); + on(d.input, "cut", operation(cm, function(){ + if (!this.options.readOnly) cm.replaceSelection(""); + })); + + // Needed to handle Tab key in KHTML + if (khtml) on(d.sizer, "mouseup", function() { + if (document.activeElement == d.input) d.input.blur(); + focusInput(cm); + }); + } + + function mouseEventInWidget(display, e) { + for (var n = e_target(e); n != display.wrapper; n = n.parentNode) + if (/\bCodeMirror-linewidget\b/.test(n.className) || + n.parentNode == display.sizer && n != display.mover) return true; + } + + function posFromMouse(cm, e, liberal) { + var display = cm.display; + if (!liberal) { + var target = e_target(e); + if (target == display.scrollbarH || target == display.scrollbarH.firstChild || + target == display.scrollbarV || target == display.scrollbarV.firstChild || + target == display.scrollbarFiller) return null; + } + var x, y, space = display.lineSpace.getBoundingClientRect(); + // Fails unpredictably on IE[67] when mouse is dragged around quickly. + try { x = e.clientX; y = e.clientY; } catch (e) { return null; } + return coordsChar(cm, x - space.left, y - space.top); + } + + var lastClick, lastDoubleClick; + function onMouseDown(e) { + var cm = this, display = cm.display, view = cm.view, sel = view.sel, doc = view.doc; + setShift(cm.view, e_prop(e, "shiftKey")); + + if (mouseEventInWidget(display, e)) { + if (!webkit) { + display.scroller.draggable = false; + setTimeout(function(){display.scroller.draggable = true;}, 100); + } + return; } - function onDoubleClick(e) { + if (clickInGutter.call(cm, e)) return; + var start = posFromMouse(cm, e); + + switch (e_button(e)) { + case 3: + if (gecko) onContextMenu.call(cm, e); + return; + case 2: + if (start) setSelectionUser(cm, start, start); + setTimeout(bind(focusInput, display), 20); e_preventDefault(e); + return; } - function onDrop(e) { - if (options.onDragEvent && options.onDragEvent(instance, addStop(e))) return; + // For button 1, if it was clicked inside the editor + // (posFromMouse returning non-null), we have to adjust the + // selection. + if (!start) {if (e_target(e) == display.scroller) e_preventDefault(e); return;} + + if (!view.focused) onFocus(cm); + + var now = +new Date, type = "single"; + if (lastDoubleClick && lastDoubleClick.time > now - 400 && posEq(lastDoubleClick.pos, start)) { + type = "triple"; e_preventDefault(e); - var pos = posFromMouse(e, true), files = e.dataTransfer.files; - if (!pos || options.readOnly) return; - if (files && files.length && window.FileReader && window.File) { - var n = files.length, text = Array(n), read = 0; - var loadFile = function(file, i) { - var reader = new FileReader; - reader.onload = function() { - text[i] = reader.result; - if (++read == n) { - pos = clipPos(pos); - operation(function() { - var end = replaceRange(text.join(""), pos, pos); - setSelectionUser(pos, end); - })(); - } - }; - reader.readAsText(file); - }; - for (var i = 0; i < n; ++i) loadFile(files[i], i); - } else { - // Don't do a replace if the drop happened inside of the selected text. - if (draggingText && !(posLess(pos, sel.from) || posLess(sel.to, pos))) return; - try { - var text = e.dataTransfer.getData("Text"); - if (text) { - compoundChange(function() { - var curFrom = sel.from, curTo = sel.to; - setSelectionUser(pos, pos); - if (draggingText) replaceRange("", curFrom, curTo); - replaceSelection(text); - focusInput(); - }); + setTimeout(bind(focusInput, display), 20); + selectLine(cm, start.line); + } else if (lastClick && lastClick.time > now - 400 && posEq(lastClick.pos, start)) { + type = "double"; + lastDoubleClick = {time: now, pos: start}; + e_preventDefault(e); + var word = findWordAt(getLine(doc, start.line).text, start); + setSelectionUser(cm, word.from, word.to); + } else { lastClick = {time: now, pos: start}; } + + var last = start, going; + if (cm.options.dragDrop && dragAndDrop && !cm.options.readOnly && !posEq(sel.from, sel.to) && + !posLess(start, sel.from) && !posLess(sel.to, start) && type == "single") { + var dragEnd = operation(cm, function(e2) { + if (webkit) display.scroller.draggable = false; + view.draggingText = false; + off(document, "mouseup", dragEnd); + off(display.scroller, "drop", dragEnd); + if (Math.abs(e.clientX - e2.clientX) + Math.abs(e.clientY - e2.clientY) < 10) { + e_preventDefault(e2); + setSelectionUser(cm, start, start); + focusInput(cm); + } + }); + // Let the drag handler handle this. + if (webkit) display.scroller.draggable = true; + view.draggingText = true; + // IE's approach to draggable + if (display.scroller.dragDrop) display.scroller.dragDrop(); + on(document, "mouseup", dragEnd); + on(display.scroller, "drop", dragEnd); + return; + } + e_preventDefault(e); + if (type == "single") setSelectionUser(cm, start, start); + + var startstart = sel.from, startend = sel.to; + + function doSelect(cur) { + if (type == "single") { + setSelectionUser(cm, start, cur); + } else if (type == "double") { + var word = findWordAt(getLine(doc, cur.line).text, cur); + if (posLess(cur, startstart)) setSelectionUser(cm, word.from, startend); + else setSelectionUser(cm, startstart, word.to); + } else if (type == "triple") { + if (posLess(cur, startstart)) setSelectionUser(cm, startend, clipPos(doc, {line: cur.line, ch: 0})); + else setSelectionUser(cm, startstart, clipPos(doc, {line: cur.line + 1, ch: 0})); + } + } + + function extend(e) { + var cur = posFromMouse(cm, e, true); + if (cur && !posEq(cur, last)) { + if (!view.focused) onFocus(cm); + last = cur; + doSelect(cur); + cm.curOp.updateInput = false; + var visible = visibleLines(display, doc); + if (cur.line >= visible.to || cur.line < visible.from) + going = setTimeout(operation(cm, function(){extend(e);}), 150); + } + } + + function done(e) { + clearTimeout(going); + var cur = posFromMouse(cm, e); + if (cur) doSelect(cur); + e_preventDefault(e); + focusInput(cm); + cm.curOp.updateInput = true; + off(document, "mousemove", move); + off(document, "mouseup", up); + } + + var move = operation(cm, function(e) { + clearTimeout(going); + e_preventDefault(e); + if (!ie && !e_button(e)) done(e); + else extend(e); + }); + var up = operation(cm, done); + on(document, "mousemove", move); + on(document, "mouseup", up); + } + + function onDrop(e) { + var cm = this; + if (cm.options.onDragEvent && cm.options.onDragEvent(cm, addStop(e))) return; + e_preventDefault(e); + var pos = posFromMouse(cm, e, true), files = e.dataTransfer.files; + if (!pos || cm.options.readOnly) return; + if (files && files.length && window.FileReader && window.File) { + var n = files.length, text = Array(n), read = 0; + var loadFile = function(file, i) { + var reader = new FileReader; + reader.onload = function() { + text[i] = reader.result; + if (++read == n) { + pos = clipPos(doc, pos); + operation(cm, function() { + var end = replaceRange(cm, text.join(""), pos, pos); + setSelectionUser(cm, pos, end); + })(); } + }; + reader.readAsText(file); + }; + for (var i = 0; i < n; ++i) loadFile(files[i], i); + } else { + // Don't do a replace if the drop happened inside of the selected text. + if (cm.view.draggingText && !(posLess(pos, sel.from) || posLess(sel.to, pos))) return; + try { + var text = e.dataTransfer.getData("Text"); + if (text) { + compoundChange(function() { + var curFrom = sel.from, curTo = sel.to; + setSelectionUser(cm, pos, pos); + if (cm.view.draggingText) replaceRange(cm, "", curFrom, curTo); + cm.replaceSelection(text); + focusInput(cm); + }); } - catch(e){} - } - } - - function clickInGutter(e) { - try { var mX = e.clientX, mY = e.clientY; } - catch(e) { return false; } - - if (mX >= Math.floor(gutters.getBoundingClientRect().right)) return false; - if (hasHandler(instance, "gutterClick")) { - mY -= wrapper.getBoundingClientRect().top; - for (var i = 0; i < options.gutters.length; ++i) { - var g = gutters.childNodes[i]; - if (g && g.getBoundingClientRect().right >= mX) { - if (mY < lineDiv.offsetHeight) { - var line = lineAtHeight(doc, mY); - var gutter = options.gutters[i]; - signalLater(instance, "gutterClick", delayedCallbacks, instance, line, gutter, e); - } - break; + } + catch(e){} + } + } + + function clickInGutter(e) { + var cm = this, display = cm.display; + try { var mX = e.clientX, mY = e.clientY; } + catch(e) { return false; } + + if (mX >= Math.floor(display.gutters.getBoundingClientRect().right)) return false; + if (hasHandler(cm, "gutterClick")) { + mY -= display.wrapper.getBoundingClientRect().top; + for (var i = 0; i < cm.options.gutters.length; ++i) { + var g = display.gutters.childNodes[i]; + if (g && g.getBoundingClientRect().right >= mX) { + if (mY < display.lineDiv.offsetHeight) { + var line = lineAtHeight(cm.view.doc, mY); + var gutter = cm.options.gutters[i]; + signalLater(cm, cm, "gutterClick", cm, line, gutter, e); } + break; } } + } + e_preventDefault(e); + return true; + } + + function onDragStart(cm, e) { + var txt = cm.getSelection(); + e.dataTransfer.setData("Text", txt); + + // Use dummy image instead of default browsers image. + if (e.dataTransfer.setDragImage) + e.dataTransfer.setDragImage(elt('img'), 0, 0); + } + + function doHandleBinding(cm, bound, dropShift) { + if (typeof bound == "string") { + bound = commands[bound]; + if (!bound) return false; + } + var view = cm.view, prevShift = view.sel.shift; + try { + if (cm.options.readOnly) view.suppressEdits = true; + if (dropShift) view.sel.shift = null; + bound(cm); + } catch(e) { + if (e != Pass) throw e; + return false; + } finally { + view.sel.shift = prevShift; + view.suppressEdits = false; + } + return true; + } + + var maybeTransition; + function handleKeyBinding(cm, e) { + // Handle auto keymap transitions + var startMap = getKeyMap(cm.options.keyMap), next = startMap.auto; + clearTimeout(maybeTransition); + if (next && !isModifierKey(e)) maybeTransition = setTimeout(function() { + if (getKeyMap(cm.options.keyMap) == startMap) + cm.options.keyMap = (next.call ? next.call(null, cm) : next); + }, 50); + + var name = keyNames[e_prop(e, "keyCode")], handled = false; + if (name == null || e.altGraphKey) return false; + if (e_prop(e, "altKey")) name = "Alt-" + name; + if (e_prop(e, "ctrlKey")) name = "Ctrl-" + name; + if (e_prop(e, "metaKey")) name = "Cmd-" + name; + + var stopped = false; + function stop() { stopped = true; } + + if (e_prop(e, "shiftKey")) { + handled = lookupKey("Shift-" + name, cm.options.extraKeys, cm.options.keyMap, + function(b) {return doHandleBinding(cm, b, true);}, stop) + || lookupKey(name, cm.options.extraKeys, cm.options.keyMap, function(b) { + if (typeof b == "string" && /^go[A-Z]/.test(b)) return doHandleBinding(cm, b); + }, stop); + } else { + handled = lookupKey(name, cm.options.extraKeys, cm.options.keyMap, + function(b) { return doHandleBinding(cm, b); }, stop); + } + if (stopped) handled = false; + if (handled) { e_preventDefault(e); - return true; + restartBlink(cm); + if (ie) { e.oldKeyCode = e.keyCode; e.keyCode = 0; } } + return handled; + } - function onDragStart(e) { - var txt = getSelection(); - e.dataTransfer.setData("Text", txt); + function handleCharBinding(cm, e, ch) { + var handled = lookupKey("'" + ch + "'", cm.options.extraKeys, cm.options.keyMap, + function(b) { return doHandleBinding(cm, b, true); }); + if (handled) { + e_preventDefault(e); + restartBlink(cm); + } + return handled; + } - // Use dummy image instead of default browsers image. - if (e.dataTransfer.setDragImage) - e.dataTransfer.setDragImage(elt('img'), 0, 0); + var lastStoppedKey = null; + function onKeyDown(e) { + var cm = this; + if (!cm.view.focused) onFocus(cm); + if (ie && e.keyCode == 27) { e.returnValue = false; } + if (cm.display.pollingFast) { if (readInput(cm)) cm.display.pollingFast = false; } + if (cm.options.onKeyEvent && cm.options.onKeyEvent(cm, addStop(e))) return; + var code = e_prop(e, "keyCode"); + // IE does strange things with escape. + setShift(cm.view, code == 16 || e_prop(e, "shiftKey")); + // First give onKeyEvent option a chance to handle this. + var handled = handleKeyBinding(cm, e); + if (opera) { + lastStoppedKey = handled ? code : null; + // Opera has no cut event... we try to at least catch the key combo + if (!handled && code == 88 && e_prop(e, mac ? "metaKey" : "ctrlKey")) + cm.replaceSelection(""); } + } - function doHandleBinding(bound, dropShift) { - if (typeof bound == "string") { - bound = commands[bound]; - if (!bound) return false; - } - var prevShift = shiftSelecting; - try { - if (options.readOnly) suppressEdits = true; - if (dropShift) shiftSelecting = null; - bound(instance); - } catch(e) { - if (e != Pass) throw e; - return false; - } finally { - shiftSelecting = prevShift; - suppressEdits = false; - } - return true; + function onKeyPress(e) { + var cm = this; + if (cm.display.pollingFast) readInput(cm); + if (cm.options.onKeyEvent && options.onKeyEvent(cm, addStop(e))) return; + var keyCode = e_prop(e, "keyCode"), charCode = e_prop(e, "charCode"); + if (opera && keyCode == lastStoppedKey) {lastStoppedKey = null; e_preventDefault(e); return;} + if (((opera && (!e.which || e.which < 10)) || khtml) && handleKeyBinding(cm, e)) return; + var ch = String.fromCharCode(charCode == null ? keyCode : charCode); + if (this.options.electricChars && this.view.doc.mode.electricChars && + this.options.smartIndent && !this.options.readOnly && + this.view.doc.mode.electricChars.indexOf(ch) > -1) + setTimeout(operation(cm, function() {indentLine(cm, cm.view.sel.to.line, "smart");}), 75); + if (handleCharBinding(cm, e, ch)) return; + fastPoll(cm); + } + + function onFocus(cm) { + if (cm.options.readOnly == "nocursor") return; + if (!cm.view.focused) { + signal(cm, "focus", cm); + cm.view.focused = true; + if (cm.display.scroller.className.search(/\bCodeMirror-focused\b/) == -1) + cm.display.scroller.className += " CodeMirror-focused"; } - var maybeTransition; - function handleKeyBinding(e) { - // Handle auto keymap transitions - var startMap = getKeyMap(options.keyMap), next = startMap.auto; - clearTimeout(maybeTransition); - if (next && !isModifierKey(e)) maybeTransition = setTimeout(function() { - if (getKeyMap(options.keyMap) == startMap) { - options.keyMap = (next.call ? next.call(null, instance) : next); - } - }, 50); - - var name = keyNames[e_prop(e, "keyCode")], handled = false; - if (name == null || e.altGraphKey) return false; - if (e_prop(e, "altKey")) name = "Alt-" + name; - if (e_prop(e, "ctrlKey")) name = "Ctrl-" + name; - if (e_prop(e, "metaKey")) name = "Cmd-" + name; - - var stopped = false; - function stop() { stopped = true; } - - if (e_prop(e, "shiftKey")) { - handled = lookupKey("Shift-" + name, options.extraKeys, options.keyMap, - function(b) {return doHandleBinding(b, true);}, stop) - || lookupKey(name, options.extraKeys, options.keyMap, function(b) { - if (typeof b == "string" && /^go[A-Z]/.test(b)) return doHandleBinding(b); - }, stop); - } else { - handled = lookupKey(name, options.extraKeys, options.keyMap, doHandleBinding, stop); - } - if (stopped) handled = false; - if (handled) { - e_preventDefault(e); - restartBlink(); - if (ie) { e.oldKeyCode = e.keyCode; e.keyCode = 0; } - } - return handled; - } - function handleCharBinding(e, ch) { - var handled = lookupKey("'" + ch + "'", options.extraKeys, - options.keyMap, function(b) { return doHandleBinding(b, true); }); - if (handled) { - e_preventDefault(e); - restartBlink(); - } - return handled; - } - - var lastStoppedKey = null; - function onKeyDown(e) { - if (!focused) onFocus(); - if (ie && e.keyCode == 27) { e.returnValue = false; } - if (pollingFast) { if (readInput()) pollingFast = false; } - if (options.onKeyEvent && options.onKeyEvent(instance, addStop(e))) return; - var code = e_prop(e, "keyCode"); - // IE does strange things with escape. - setShift(code == 16 || e_prop(e, "shiftKey")); - // First give onKeyEvent option a chance to handle this. - var handled = handleKeyBinding(e); - if (opera) { - lastStoppedKey = handled ? code : null; - // Opera has no cut event... we try to at least catch the key combo - if (!handled && code == 88 && e_prop(e, mac ? "metaKey" : "ctrlKey")) - replaceSelection(""); - } - } - function onKeyPress(e) { - if (pollingFast) readInput(); - if (options.onKeyEvent && options.onKeyEvent(instance, addStop(e))) return; - var keyCode = e_prop(e, "keyCode"), charCode = e_prop(e, "charCode"); - if (opera && keyCode == lastStoppedKey) {lastStoppedKey = null; e_preventDefault(e); return;} - if (((opera && (!e.which || e.which < 10)) || khtml) && handleKeyBinding(e)) return; - var ch = String.fromCharCode(charCode == null ? keyCode : charCode); - if (options.electricChars && mode.electricChars && options.smartIndent && !options.readOnly) { - if (mode.electricChars.indexOf(ch) > -1) - setTimeout(operation(function() {indentLine(sel.to.line, "smart");}), 75); - } - if (handleCharBinding(e, ch)) return; - fastPoll(); - } - function onKeyUp(e) { - if (options.onKeyEvent && options.onKeyEvent(instance, addStop(e))) return; - if (e_prop(e, "keyCode") == 16) shiftSelecting = null; - } - - function onFocus() { - if (options.readOnly == "nocursor") return; - if (!focused) { - signal(instance, "focus", instance); - focused = true; - if (scroller.className.search(/\bCodeMirror-focused\b/) == -1) - scroller.className += " CodeMirror-focused"; - } - slowPoll(); - restartBlink(); - } - function onBlur() { - if (focused) { - signal(instance, "blur", instance); - focused = false; - scroller.className = scroller.className.replace(" CodeMirror-focused", ""); - } - clearInterval(blinker); - setTimeout(function() {if (!focused) shiftSelecting = null;}, 150); - } - - // Replace the range from from to to by the strings in newText. - // Afterwards, set the selection to selFrom, selTo. - function updateLines(from, to, newText, selFrom, selTo) { - if (suppressEdits) return; - var old = []; - doc.iter(from.line, to.line + 1, function(line) { - old.push(newHL(line.text, line.markedSpans)); + slowPoll(cm); + restartBlink(cm); + } + function onBlur(cm) { + if (cm.view.focused) { + signal(cm, "blur", cm); + cm.view.focused = false; + cm.display.scroller.className = cm.display.scroller.className.replace(" CodeMirror-focused", ""); + } + clearInterval(cm.display.blinker); + setTimeout(function() {if (!cm.view.focused) cm.view.sel.shift = null;}, 150); + } + + var detectingSelectAll; + function onContextMenu(cm, e) { + var display = cm.display, sel = cm.view.sel; + var pos = posFromMouse(cm, e), scrollPos = display.scroller.scrollTop; + if (!pos || opera) return; // Opera is difficult. + if (posEq(sel.from, sel.to) || posLess(pos, sel.from) || !posLess(pos, sel.to)) + operation(setSelection)(cm, pos, pos); + + var oldCSS = display.input.style.cssText; + display.inputDiv.style.position = "absolute"; + display.input.style.cssText = "position: fixed; width: 30px; height: 30px; top: " + (e.clientY - 5) + + "px; left: " + (e.clientX - 5) + "px; z-index: 1000; background: white; outline: none;" + + "border-width: 0; outline: none; overflow: hidden; opacity: .05; filter: alpha(opacity=5);"; + focusInput(cm); + resetInput(display, view, true); + // Adds "Select all" to context menu in FF + if (posEq(sel.from, sel.to)) display.input.value = display.prevInput = " "; + + function rehide() { + display.inputDiv.style.position = "relative"; + display.input.style.cssText = oldCSS; + if (ie_lt9) display.scrollbarV.scrollTop = display.scroller.scrollTop = scrollPos; + slowPoll(cm); + + // Try to detect the user choosing select-all + if (display.input.selectionStart != null) { + clearTimeout(detectingSelectAll); + var extval = display.input.value = " " + (posEq(sel.from, sel.to) ? "" : display.input.value), i = 0; + display.prevInput = " "; + display.input.selectionStart = 1; display.input.selectionEnd = extval.length; + detectingSelectAll = setTimeout(function poll(){ + if (display.prevInput == " " && display.input.selectionStart == 0) + operation(cm, commands.selectAll)(cm); + else if (i++ < 10) detectingSelectAll = setTimeout(poll, 500); + else resetInput(display, view); + }, 200); + } + } + + if (gecko) { + e_stop(e); + on(window, "mouseup", function mouseup() { + off(window, "mouseup", mouseup); + setTimeout(rehide, 20); }); - if (history) { - history.addChange(from.line, newText.length, old); - while (history.done.length > options.undoDepth) history.done.shift(); - } - var lines = updateMarkedSpans(hlSpans(old[0]), hlSpans(lst(old)), from.ch, to.ch, newText); - updateLinesNoUndo(from, to, lines, selFrom, selTo); - } - function unredoHelper(from, to) { - if (!from.length) return; - var set = from.pop(), out = []; - for (var i = set.length - 1; i >= 0; i -= 1) { - var change = set[i]; - var replaced = [], end = change.start + change.added; - doc.iter(change.start, end, function(line) { replaced.push(newHL(line.text, line.markedSpans)); }); - out.push({start: change.start, added: change.old.length, old: replaced}); - var pos = {line: change.start + change.old.length - 1, - ch: editEnd(hlText(lst(replaced)), hlText(lst(change.old)))}; - updateLinesNoUndo({line: change.start, ch: 0}, {line: end - 1, ch: getLine(end-1).text.length}, - change.old, pos, pos); - } - updateInput = true; - to.push(out); - } - function undo() {unredoHelper(history.done, history.undone);} - function redo() {unredoHelper(history.undone, history.done);} - - function updateLinesNoUndo(from, to, lines, selFrom, selTo) { - if (suppressEdits) return; - var recomputeMaxLength = false, maxLineLength = maxLine.text.length; - if (!options.lineWrapping) - doc.iter(from.line, to.line + 1, function(line) { - if (!line.hidden && line.text.length == maxLineLength) {recomputeMaxLength = true; return true;} - }); + } else { + setTimeout(rehide, 50); + } + } - var nlines = to.line - from.line, firstLine = getLine(from.line), lastLine = getLine(to.line); - var lastHL = lst(lines), th = textHeight(); + // UPDATING - // First adjust the line structure - if (from.ch == 0 && to.ch == 0 && hlText(lastHL) == "") { - // This is a whole-line replace. Treated specially to make - // sure line objects move the way they are supposed to. - var added = [], prevLine = null; - for (var i = 0, e = lines.length - 1; i < e; ++i) - added.push(new Line(hlText(lines[i]), hlSpans(lines[i]), th)); - lastLine.update(lastLine.text, hlSpans(lastHL), delayedCallbacks); - if (nlines) doc.remove(from.line, nlines, delayedCallbacks); - if (added.length) doc.insert(from.line, added); - } else if (firstLine == lastLine) { - if (lines.length == 1) { - firstLine.update(firstLine.text.slice(0, from.ch) + hlText(lines[0]) + firstLine.text.slice(to.ch), - hlSpans(lines[0]), delayedCallbacks); - } else { - for (var added = [], i = 1, e = lines.length - 1; i < e; ++i) - added.push(new Line(hlText(lines[i]), hlSpans(lines[i]), th)); - added.push(new Line(hlText(lastHL) + firstLine.text.slice(to.ch), hlSpans(lastHL), th)); - firstLine.update(firstLine.text.slice(0, from.ch) + hlText(lines[0]), hlSpans(lines[0]), delayedCallbacks); - doc.insert(from.line + 1, added); - } - } else if (lines.length == 1) { - firstLine.update(firstLine.text.slice(0, from.ch) + hlText(lines[0]) + lastLine.text.slice(to.ch), - hlSpans(lines[0]), delayedCallbacks); - doc.remove(from.line + 1, nlines, delayedCallbacks); + // Replace the range from from to to by the strings in newText. + // Afterwards, set the selection to selFrom, selTo. + function updateDoc(cm, from, to, newText, selFrom, selTo) { + var view = cm.view, doc = view.doc; + if (view.suppressEdits) return; + var old = []; + doc.iter(from.line, to.line + 1, function(line) { + old.push(newHL(line.text, line.markedSpans)); + }); + if (doc.history) { + doc.history.addChange(from.line, newText.length, old); + while (doc.history.done.length > cm.options.undoDepth) doc.history.done.shift(); + } + var lines = updateMarkedSpans(hlSpans(old[0]), hlSpans(lst(old)), from.ch, to.ch, newText); + updateDocNoUndo(cm, from, to, lines, selFrom, selTo); + } + function unredoHelper(cm, from, to) { + var doc = cm.view.doc; + if (!from.length) return; + var set = from.pop(), out = []; + for (var i = set.length - 1; i >= 0; i -= 1) { + var change = set[i]; + var replaced = [], end = change.start + change.added; + doc.iter(change.start, end, function(line) { replaced.push(newHL(line.text, line.markedSpans)); }); + out.push({start: change.start, added: change.old.length, old: replaced}); + var pos = {line: change.start + change.old.length - 1, + ch: editEnd(hlText(lst(replaced)), hlText(lst(change.old)))}; + updateDocNoUndo(cm, {line: change.start, ch: 0}, {line: end - 1, ch: getLine(doc, end-1).text.length}, + change.old, pos, pos); + } + cm.curOp.updateInput = true; + to.push(out); + } + function undo(cm) { + var hist = cm.view.doc.history; + unredoHelper + } + function redo(cm) { + var hist = cm.view.doc.history; + unredoHelper(cm, hist.undone, hist.done); + } + + function updateDocNoUndo(cm, from, to, lines, selFrom, selTo) { + var view = cm.view, doc = view.doc, display = cm.display; + if (view.suppressEdits) return; + var recomputeMaxLength = false, maxLineLength = view.maxLine.text.length; + if (!cm.options.lineWrapping) + doc.iter(from.line, to.line + 1, function(line) { + if (!line.hidden && line.text.length == maxLineLength) {recomputeMaxLength = true; return true;} + }); + + var nlines = to.line - from.line, firstLine = getLine(doc, from.line), lastLine = getLine(doc, to.line); + var lastHL = lst(lines), th = textHeight(display); + + // First adjust the line structure + if (from.ch == 0 && to.ch == 0 && hlText(lastHL) == "") { + // This is a whole-line replace. Treated specially to make + // sure line objects move the way they are supposed to. + var added = [], prevLine = null; + for (var i = 0, e = lines.length - 1; i < e; ++i) + added.push(new Line(hlText(lines[i]), hlSpans(lines[i]), th)); + lastLine.update(lastLine.text, hlSpans(lastHL), cm); + if (nlines) doc.remove(from.line, nlines, cm); + if (added.length) doc.insert(from.line, added); + } else if (firstLine == lastLine) { + if (lines.length == 1) { + firstLine.update(firstLine.text.slice(0, from.ch) + hlText(lines[0]) + firstLine.text.slice(to.ch), + hlSpans(lines[0]), cm); } else { - var added = []; - firstLine.update(firstLine.text.slice(0, from.ch) + hlText(lines[0]), hlSpans(lines[0]), - delayedCallbacks); - lastLine.update(hlText(lastHL) + lastLine.text.slice(to.ch), hlSpans(lastHL), delayedCallbacks); - for (var i = 1, e = lines.length - 1; i < e; ++i) + for (var added = [], i = 1, e = lines.length - 1; i < e; ++i) added.push(new Line(hlText(lines[i]), hlSpans(lines[i]), th)); - if (nlines > 1) doc.remove(from.line + 1, nlines - 1, delayedCallbacks); + added.push(new Line(hlText(lastHL) + firstLine.text.slice(to.ch), hlSpans(lastHL), th)); + firstLine.update(firstLine.text.slice(0, from.ch) + hlText(lines[0]), hlSpans(lines[0]), cm); doc.insert(from.line + 1, added); } - if (options.lineWrapping) { - var perLine = Math.max(5, scroller.clientWidth / charWidth() - 3); - doc.iter(from.line, from.line + lines.length, function(line) { - if (line.hidden) return; - var guess = (Math.ceil(line.text.length / perLine) || 1) * th; - if (guess != line.height) updateLineHeight(line, guess); - }); - } else { - doc.iter(from.line, from.line + lines.length, function(line) { - var l = line.text; - if (!line.hidden && l.length > maxLineLength) { - maxLine = line; maxLineLength = l.length; maxLineChanged = true; - recomputeMaxLength = false; - } - }); - if (recomputeMaxLength) updateMaxLine = true; - } - - // Adjust frontier, schedule worker - frontier = Math.min(frontier, from.line); - startWorker(400); - - var lendiff = lines.length - nlines - 1; - // Remember that these lines changed, for updating the display - changes.push({from: from.line, to: to.line + 1, diff: lendiff}); - if (options.onChange) { - // Normalize lines to contain only strings, since that's what - // the change event handler expects - for (var i = 0; i < lines.length; ++i) - if (typeof lines[i] != "string") lines[i] = lines[i].text; - var changeObj = {from: from, to: to, text: lines}; - if (textChanged) { - for (var cur = textChanged; cur.next; cur = cur.next) {} - cur.next = changeObj; - } else textChanged = changeObj; - } - - // Update the selection - setSelection(clipPos(selFrom), clipPos(selTo), true); - } - - function updateScrollbars(scrollTop) { - sizer.style.minHeight = heightForcer.style.top = (doc.height + 2 * paddingTop()) + "px"; - var needsH = scroller.scrollWidth > scroller.clientWidth; - var needsV = scroller.scrollHeight > scroller.clientHeight; - if (needsV) { - scrollbarV.style.display = "block"; - scrollbarV.style.bottom = needsH ? scrollbarWidth(measure) + "px" : "0"; - scrollbarV.firstChild.style.height = (scroller.scrollHeight - scroller.clientHeight + scrollbarV.clientHeight) + "px"; - if (scrollTop != null) { - scrollbarV.scrollTop = scroller.scrollTop = scrollTop; - // 'Nudge' the scrollbar to work around a Webkit bug where, - // in some situations, we'd end up with a scrollbar that - // reported its scrollTop (and looked) as expected, but - // *behaved* as if it was still in a previous state (i.e. - // couldn't scroll up, even though it appeared to be at the - // bottom). - if (webkit) setTimeout(function() { - if (scrollbarV.scrollTop != scrollTop) return; - scrollbarV.scrollTop = scrollTop + (scrollTop ? -1 : 1); - scrollbarV.scrollTop = scrollTop; - }, 0); - } - } else scrollbarV.style.display = ""; - if (needsH) { - scrollbarH.style.display = "block"; - scrollbarH.style.right = needsV ? scrollbarWidth(measure) + "px" : "0"; - scrollbarH.firstChild.style.width = (scroller.scrollWidth - scroller.clientWidth + scrollbarH.clientWidth) + "px"; - } else scrollbarH.style.display = ""; - if (needsH && needsV) { - scrollbarFiller.style.display = "block"; - scrollbarFiller.style.height = scrollbarFiller.style.width = scrollbarWidth(measure) + "px"; - } else scrollbarFiller.style.display = ""; - - if (mac_geLion && scrollbarWidth(measure) === 0) - scrollbarV.style.minWidth = scrollbarH.style.minHeight = "12px"; - } - - function computeMaxLength() { - maxLine = getLine(0); maxLineChanged = true; - var maxLineLength = maxLine.text.length; - doc.iter(1, doc.size, function(line) { + } else if (lines.length == 1) { + firstLine.update(firstLine.text.slice(0, from.ch) + hlText(lines[0]) + lastLine.text.slice(to.ch), + hlSpans(lines[0]), cm); + doc.remove(from.line + 1, nlines, cm); + } else { + var added = []; + firstLine.update(firstLine.text.slice(0, from.ch) + hlText(lines[0]), hlSpans(lines[0]), cm); + lastLine.update(hlText(lastHL) + lastLine.text.slice(to.ch), hlSpans(lastHL), cm); + for (var i = 1, e = lines.length - 1; i < e; ++i) + added.push(new Line(hlText(lines[i]), hlSpans(lines[i]), th)); + if (nlines > 1) doc.remove(from.line + 1, nlines - 1, cm); + doc.insert(from.line + 1, added); + } + + if (cm.options.lineWrapping) { + var perLine = Math.max(5, display.scroller.clientWidth / charWidth(display) - 3); + doc.iter(from.line, from.line + lines.length, function(line) { + if (line.hidden) return; + var guess = (Math.ceil(line.text.length / perLine) || 1) * th; + if (guess != line.height) updateLineHeight(line, guess); + }); + } else { + doc.iter(from.line, from.line + lines.length, function(line) { var l = line.text; if (!line.hidden && l.length > maxLineLength) { - maxLineLength = l.length; maxLine = line; + view.maxLine = line; maxLineLength = l.length; view.maxLineChanged = true; + recomputeMaxLength = false; } }); - updateMaxLine = false; - } - - function replaceRange(code, from, to) { - from = clipPos(from); - if (!to) to = from; else to = clipPos(to); - if (posLess(to, from)) { var tmp = to; to = from; from = tmp; } - code = splitLines(code); - function adjustPos(pos) { - if (posLess(pos, from)) return pos; - if (!posLess(to, pos)) return end; - var line = pos.line + code.length - (to.line - from.line) - 1; - var ch = pos.ch; - if (pos.line == to.line) - ch += lst(code).length - (to.ch - (to.line == from.line ? from.ch : 0)); - return {line: line, ch: ch}; - } - var end; - replaceRange1(code, from, to, function(end1) { - end = end1; - return {from: adjustPos(sel.from), to: adjustPos(sel.to)}; - }); - return end; - } - function replaceSelection(code, collapse) { - replaceRange1(splitLines(code), sel.from, sel.to, function(end) { - if (collapse == "end") return {from: end, to: end}; - else if (collapse == "start") return {from: sel.from, to: sel.from}; - else return {from: sel.from, to: end}; - }); - } - function replaceRange1(code, from, to, computeSel) { - var endch = code.length == 1 ? code[0].length + from.ch : lst(code).length; - var newSel = computeSel({line: from.line + code.length - 1, ch: endch}); - updateLines(from, to, code, newSel.from, newSel.to); - } + if (recomputeMaxLength) cm.curOp.updateMaxLine = true; + } + + // Adjust frontier, schedule worker + doc.frontier = Math.min(doc.frontier, from.line); + startWorker(cm, 400); + + var lendiff = lines.length - nlines - 1; + // Remember that these lines changed, for updating the display + regChange(cm, from.line, to.line + 1, lendiff); + if (hasHandler(cm, "change")) { + // Normalize lines to contain only strings, since that's what + // the change event handler expects + for (var i = 0; i < lines.length; ++i) + if (typeof lines[i] != "string") lines[i] = lines[i].text; + var changeObj = {from: from, to: to, text: lines}; + if (cm.curOp.textChanged) { + for (var cur = cm.operaton.textChanged; cur.next; cur = cur.next) {} + cur.next = changeObj; + } else cm.curOp.textChanged = changeObj; + } + + // Update the selection + setSelection(cm, clipPos(doc, selFrom), clipPos(doc, selTo), true); + } - function getRange(from, to, lineSep) { - var l1 = from.line, l2 = to.line; - if (l1 == l2) return getLine(l1).text.slice(from.ch, to.ch); - var code = [getLine(l1).text.slice(from.ch)]; - doc.iter(l1 + 1, l2, function(line) { code.push(line.text); }); - code.push(getLine(l2).text.slice(0, to.ch)); - return code.join(lineSep || "\n"); - } - function getSelection(lineSep) { - return getRange(sel.from, sel.to, lineSep); + function replaceRange(cm, code, from, to) { + if (!to) to = from; + if (posLess(to, from)) { var tmp = to; to = from; from = tmp; } + code = splitLines(code); + function adjustPos(pos) { + if (posLess(pos, from)) return pos; + if (!posLess(to, pos)) return end; + var line = pos.line + code.length - (to.line - from.line) - 1; + var ch = pos.ch; + if (pos.line == to.line) + ch += lst(code).length - (to.ch - (to.line == from.line ? from.ch : 0)); + return {line: line, ch: ch}; } + var end; + replaceRange1(cm, code, from, to, function(end1) { + end = end1; + return {from: adjustPos(cm.view.sel.from), to: adjustPos(cm.view.sel.to)}; + }); + return end; + } + function replaceRange1(cm, code, from, to, computeSel) { + var endch = code.length == 1 ? code[0].length + from.ch : lst(code).length; + var newSel = computeSel({line: from.line + code.length - 1, ch: endch}); + updateDoc(cm, from, to, code, newSel.from, newSel.to); + } - function slowPoll() { - if (pollingFast) return; - poll.set(options.pollInterval, function() { - readInput(); - if (focused) slowPoll(); - }); - } - function fastPoll() { - var missed = false; - pollingFast = true; - function p() { - var changed = readInput(); - if (!changed && !missed) {missed = true; poll.set(60, p);} - else {pollingFast = false; slowPoll();} - } - poll.set(20, p); - } - - // Previnput is a hack to work with IME. If we reset the textarea - // on every change, that breaks IME. So we look for changes - // compared to the previous content instead. (Modern browsers have - // events that indicate IME taking place, but these are not widely - // supported or compatible enough yet to rely on.) - var prevInput = ""; - function readInput() { - if (!focused || hasSelection(input) || options.readOnly) return false; - var text = input.value; - if (text == prevInput) return false; - if (!nestedOperation) startOperation(); - shiftSelecting = null; - var same = 0, l = Math.min(prevInput.length, text.length); - while (same < l && prevInput[same] == text[same]) ++same; - if (same < prevInput.length) - sel.from = {line: sel.from.line, ch: sel.from.ch - (prevInput.length - same)}; - else if (overwrite && posEq(sel.from, sel.to)) - sel.to = {line: sel.to.line, ch: Math.min(getLine(sel.to.line).text.length, sel.to.ch + (text.length - same))}; - replaceSelection(text.slice(same), "end"); - if (text.length > 1000) { input.value = prevInput = ""; } - else prevInput = text; - if (!nestedOperation) endOperation(); - return true; - } - function resetInput(user) { - if (!posEq(sel.from, sel.to)) { - prevInput = ""; - input.value = getSelection(); - if (focused) selectInput(input); - } else if (user) prevInput = input.value = ""; - } - - function focusInput() { - if (options.readOnly != "nocursor") input.focus(); - } - - function scrollCursorIntoView() { - var coords = cursorCoords(sel.inverted ? sel.from : sel.to); - scrollIntoView(coords.left, coords.top, coords.left, coords.bottom); - if (!focused) return; - var box = sizer.getBoundingClientRect(), doScroll = null; - if (coords.top + box.top < 0) doScroll = true; - else if (coords.bottom + box.top > (window.innerHeight || document.documentElement.clientHeight)) doScroll = false; - if (doScroll != null) { - var hidden = cursor.style.display == "none"; - if (hidden) { - cursor.style.display = ""; - cursor.style.left = coords.left + "px"; - cursor.style.top = (coords.top - displayOffset) + "px"; - } - cursor.scrollIntoView(doScroll); - if (hidden) cursor.style.display = "none"; - } - } - function scrollIntoView(x1, y1, x2, y2) { - var scrollPos = calculateScrollPos(x1, y1, x2, y2); - if (scrollPos.scrollLeft != null) {scrollbarH.scrollLeft = scroller.scrollLeft = scrollPos.scrollLeft;} - if (scrollPos.scrollTop != null) {scrollbarV.scrollTop = scroller.scrollTop = scrollPos.scrollTop;} - } - function calculateScrollPos(x1, y1, x2, y2) { - var pt = paddingTop(); - y1 += pt; y2 += pt; - var screen = scroller.clientHeight - scrollerCutOff, screentop = scroller.scrollTop, result = {}; - var docBottom = scroller.scrollHeight - scrollerCutOff; - var atTop = y1 < pt + 10, atBottom = y2 + pt > docBottom - 10; - if (y1 < screentop) result.scrollTop = atTop ? 0 : Math.max(0, y1); - else if (y2 > screentop + screen) result.scrollTop = (atBottom ? docBottom : y2 - screen); - - var screenw = scroller.clientWidth - scrollerCutOff, screenleft = scroller.scrollLeft; - x1 += gutters.offsetWidth; x2 += gutters.offsetWidth; - var gutterw = gutters.offsetWidth; - var atLeft = x1 < gutterw + 10; - if (x1 < screenleft + gutterw || atLeft) { - if (atLeft) x1 = 0; - result.scrollLeft = Math.max(0, x1 - 10 - gutterw); - } else if (x2 > screenw + screenleft - 3) { - result.scrollLeft = x2 + 10 - screenw; + // SELECTION + + function posEq(a, b) {return a.line == b.line && a.ch == b.ch;} + function posLess(a, b) {return a.line < b.line || (a.line == b.line && a.ch < b.ch);} + function copyPos(x) {return {line: x.line, ch: x.ch};} + + function clipLine(doc, n) {return Math.max(0, Math.min(n, doc.size-1));} + function clipPos(doc, pos) { + if (pos.line < 0) return {line: 0, ch: 0}; + if (pos.line >= doc.size) return {line: doc.size-1, ch: getLine(doc, doc.size-1).text.length}; + var ch = pos.ch, linelen = getLine(doc, pos.line).text.length; + if (ch == null || ch > linelen) return {line: pos.line, ch: linelen}; + else if (ch < 0) return {line: pos.line, ch: 0}; + else return pos; + } + function isLine(doc, l) {return l >= 0 && l < doc.size;} + + function setShift(view, val) { + if (val) view.sel.shift = view.sel.shift || selHead(view); + else view.sel.shift = null; + } + function setSelectionUser(cm, from, to) { + var view = cm.view, sh = view.sel.shift; + if (sh) { + sh = clipPos(view.doc, sh); + if (posLess(sh, from)) from = sh; + else if (posLess(to, sh)) to = sh; + } + setSelection(cm, from, to); + cm.curOp.userSelChange = true; + } + + // Update the selection. Last two args are only used by + // updateDoc, since they have to be expressed in the line + // numbers before the update. + function setSelection(cm, from, to, isChange) { + var sel = cm.view.sel, doc = cm.view.doc; + cm.view.goalColumn = null; + if (posEq(sel.from, from) && posEq(sel.to, to)) return; + if (posLess(to, from)) {var tmp = to; to = from; from = tmp;} + + // Skip over hidden lines. + if (isChange || from.line != sel.from.line) { + var from1 = skipHidden(doc, from, sel.from.line, sel.from.ch, cm); + // If there is no non-hidden line left, force visibility on current line + if (!from1) cm.unfoldLines(getLine(doc, from.line).hidden.id); + else from = from1; + } + if (isChange || to.line != sel.to.line) to = skipHidden(doc, to, sel.to.line, sel.to.ch, cm); + + if (posEq(from, to)) sel.inverted = false; + else if (posEq(from, sel.to)) sel.inverted = false; + else if (posEq(to, sel.from)) sel.inverted = true; + + if (cm.options.autoClearEmptyLines && posEq(sel.from, sel.to)) { + var head = selHead(view); + if (head.line != sel.from.line && sel.from.line < doc.size) { + var oldLine = getLine(doc, sel.from.line); + if (/^\s+$/.test(oldLine.text)) + setTimeout(operation(cm, function() { + if (oldLine.parent && /^\s+$/.test(oldLine.text)) { + var no = lineNo(oldLine); + replaceRange(cm, "", {line: no, ch: 0}, {line: no, ch: oldLine.text.length}); + } + }, 10)); } - return result; } - function visibleLines(scrollTop) { - var top = (scrollTop != null ? scrollTop : scroller.scrollTop) - paddingTop(); - var fromHeight = Math.max(0, Math.floor(top)); - var toHeight = Math.ceil(top + wrapper.clientHeight); - return {from: lineAtHeight(doc, fromHeight), - to: lineAtHeight(doc, toHeight)}; - } - // Uses a set of changes plus the current scroll position to - // determine which DOM updates have to be made, and makes the - // updates. - function updateDisplay(changes, suppressCallback, scrollTop) { - if (!wrapper.clientWidth) { - showingFrom = showingTo = displayOffset = 0; - return; - } - - // Compute the new visible window - // If scrollTop is specified, use that to determine which lines - // to render instead of the current scrollbar position. - var visible = visibleLines(scrollTop); - // Bail out if the visible area is already rendered and nothing changed. - if (changes !== true && changes.length == 0 && visible.from > showingFrom && visible.to < showingTo) { - updateScrollbars(scrollTop); - return; - } - if (changes && changes !== true && maybeUpdateLineNumberWidth()) - changes = true; - sizer.style.marginLeft = scrollbarH.style.left = gutters.offsetWidth + "px"; - // Used to determine which lines need their line numbers updated - var positionsChangedFrom = changes === true ? 0 : Infinity; - if (options.lineNumbers && changes && changes !== true) - for (var i = 0; i < changes.length; ++i) - if (changes[i].diff) { positionsChangedFrom = changes[i].from; break; } - - var from = Math.max(visible.from - 100, 0), to = Math.min(doc.size, visible.to + 100); - if (showingFrom < from && from - showingFrom < 20) from = showingFrom; - if (showingTo > to && showingTo - to < 20) to = Math.min(doc.size, showingTo); - - // Create a range of theoretically intact lines, and punch holes - // in that using the change info. - var intact = changes === true ? [] : - computeIntact([{from: showingFrom, to: showingTo, domStart: 0}], changes); - // Clip off the parts that won't be visible - var intactLines = 0; - for (var i = 0; i < intact.length; ++i) { - var range = intact[i]; - if (range.from < from) {range.domStart += (from - range.from); range.from = from;} - if (range.to > to) range.to = to; - if (range.from >= range.to) intact.splice(i--, 1); - else intactLines += range.to - range.from; - } - if (intactLines == to - from && from == showingFrom && to == showingTo) { - updateScrollbars(scrollTop); - return; - } - intact.sort(function(a, b) {return a.domStart - b.domStart;}); - - lineDiv.style.display = "none"; - patchDisplay(from, to, intact, positionsChangedFrom); - lineDiv.style.display = ""; - - var different = from != showingFrom || to != showingTo || lastSizeC != wrapper.clientHeight; - // This is just a bogus formula that detects when the editor is - // resized or the font size changes. - if (different) lastSizeC = wrapper.clientHeight; - if (from != showingFrom || to != showingTo) - signalLater(instance, "viewportChange", delayedCallbacks, instance, from, to); - showingFrom = from; showingTo = to; - displayOffset = heightAtLine(doc, from); - startWorker(100); - - // Since this is all rather error prone, it is honoured with the - // only assertion in the whole file. - if (lineDiv.childNodes.length != showingTo - showingFrom) - throw new Error("BAD PATCH! " + JSON.stringify(intact) + " size=" + (showingTo - showingFrom) + - " nodes=" + lineDiv.childNodes.length); - - // Update line heights for visible lines based on actual DOM - // sizes - var curNode = lineDiv.firstChild, heightChanged = false; - var relativeTo = curNode.offsetTop; - doc.iter(showingFrom, showingTo, function(line) { - // Work around bizarro IE7 bug where, sometimes, our curNode - // is magically replaced with a new node in the DOM, leaving - // us with a reference to an orphan (nextSibling-less) node. - if (!curNode) return; + sel.from = from; sel.to = to; + cm.curOp.selectionChanged = true; + } + + function skipHidden(doc, pos, oldLine, oldCh, allowUnfold) { + function getNonHidden(dir) { + var lNo = pos.line + dir, end = dir == 1 ? doc.size : -1; + while (lNo != end) { + var line = getLine(doc, lNo); if (!line.hidden) { - var end = curNode.offsetHeight + curNode.offsetTop; - var height = end - relativeTo, diff = line.height - height; - if (height < 2) height = textHeight(); - relativeTo = end; - if (diff > .001 || diff < -.001) { - updateLineHeight(line, height); - heightChanged = true; - } + var ch = pos.ch; + if (toEnd || ch > oldCh || ch > line.text.length) ch = line.text.length; + return {line: lNo, ch: ch}; } - curNode = curNode.nextSibling; - }); + lNo += dir; + } + } + var line = getLine(doc, pos.line); + while (allowUnfold && line.hidden && line.hidden.handle.unfoldOnEnter) + allowUnfold.unfoldLines(line.hidden.handle); + if (!line.hidden) return pos; + var toEnd = pos.ch == line.text.length && pos.ch != oldCh; + if (pos.line >= oldLine) return getNonHidden(1) || getNonHidden(-1); + else return getNonHidden(-1) || getNonHidden(1); + } - // Position the mover div to align with the current virtual scroll position - mover.style.top = displayOffset + "px"; - updateScrollbars(scrollTop); - updateSelection(); - if (!suppressCallback) signal(instance, "update", instance); - return true; + // SCROLLING + + function scrollCursorIntoView(cm) { + var view = cm.view, coords = cursorCoords(cm, selHead(view)); + scrollIntoView(cm.display, coords.left, coords.top, coords.left, coords.bottom); + if (!view.focused) return; + var display = cm.display, box = display.sizer.getBoundingClientRect(), doScroll = null; + if (coords.top + box.top < 0) doScroll = true; + else if (coords.bottom + box.top > (window.innerHeight || document.documentElement.clientHeight)) doScroll = false; + if (doScroll != null) { + var hidden = display.cursor.style.display == "none"; + if (hidden) { + display.cursor.style.display = ""; + display.cursor.style.left = coords.left + "px"; + display.cursor.style.top = (coords.top - display.viewOffset) + "px"; + } + display.cursor.scrollIntoView(doScroll); + if (hidden) display.cursor.style.display = "none"; } + } - function computeIntact(intact, changes) { - for (var i = 0, l = changes.length || 0; i < l; ++i) { - var change = changes[i], intact2 = [], diff = change.diff || 0; - for (var j = 0, l2 = intact.length; j < l2; ++j) { - var range = intact[j]; - if (change.to <= range.from && change.diff) - intact2.push({from: range.from + diff, to: range.to + diff, - domStart: range.domStart}); - else if (change.to <= range.from || change.from >= range.to) - intact2.push(range); - else { - if (change.from > range.from) - intact2.push({from: range.from, to: change.from, domStart: range.domStart}); - if (change.to < range.to) - intact2.push({from: change.to + diff, to: range.to + diff, - domStart: range.domStart + (change.to - range.from)}); - } - } - intact = intact2; - } - return intact; - } - - function lineNumberFor(i) { - return String(options.lineNumberFormatter(i + options.firstLineNumber)); - } - - function patchDisplay(from, to, intact, updateNumbersFrom) { - function killNode(node) { - var tmp = node.nextSibling; - node.parentNode.removeChild(node); - return tmp; - } - var lineNumbers = options.lineNumbers; - var lineNumberPos = lineNumbers && lineNumberLeftPos(); - // The first pass removes the DOM nodes that aren't intact. - if (!intact.length) removeChildren(lineDiv); - else { - var domPos = 0, curNode = lineDiv.firstChild, n; - for (var i = 0; i < intact.length; ++i) { - var cur = intact[i]; - while (cur.domStart > domPos) {curNode = killNode(curNode); domPos++;} - for (var j = cur.from, e = cur.to; j < e; ++j) { - if (lineNumbers && updateNumbersFrom <= j && curNode.firstChild) - setTextContent(curNode.firstChild, lineNumberFor(j)); - curNode = curNode.nextSibling; domPos++; - } - } - while (curNode) curNode = killNode(curNode); - } - // This pass fills in the lines that actually changed. - var nextIntact = intact.shift(), curNode = lineDiv.firstChild, j = from, gutterSpecs = options.gutters; - doc.iter(from, to, function(line) { - if (nextIntact && nextIntact.to == j) nextIntact = intact.shift(); - if (!nextIntact || nextIntact.from > j) { - if (line.hidden) var lineElement = elt("div"); - else { - var lineElement = lineContent(line), markers = line.gutterMarkers; - if (line.className) lineElement.className = line.className; - // Lines with gutter elements or a background class need - // to be wrapped again, and have the extra elements added - // to the wrapper div - if (lineNumbers || markers || line.bgClassName || (line.widgets && line.widgets.length)) { - var inside = []; - if (lineNumbers) - inside.push(elt("div", lineNumberFor(j), - "CodeMirror-linenumber CodeMirror-gutter-elt", - "left: " + lineNumberPos + "px; width: " - + currentLineNumberWidth + "px")); - if (markers) - for (var k = 0; k < gutterSpecs.length; ++k) { - var id = gutterSpecs[k], found = markers.hasOwnProperty(id) && markers[id]; - if (found) { - var gutterElt = gutters.childNodes[k]; - inside.push(elt("div", [found], "CodeMirror-gutter-elt", "left: " + (gutterElt.offsetLeft - gutters.offsetWidth) + - "px; width: " + gutterElt.clientWidth + "px")); - } - } - // Kludge to make sure the styled element lies behind the selection (by z-index) - if (line.bgClassName) - inside.push(elt("div", "\u00a0", line.bgClassName + " CodeMirror-linebackground")); - inside.push(lineElement); - if (line.widgets) - for (var i = 0, ws = line.widgets; i < ws.length; ++i) { - var widget = ws[i], node = elt("div", [widget.node], "CodeMirror-linewidget"); - node.widget = widget; - if (widget.noHScroll) - node.style.width = wrapper.clientWidth + "px"; - if (widget.coverGutter) { - node.style.zIndex = 5; - node.style.position = "relative"; - node.style.marginLeft = (widget.noHScroll ? compensateForHScroll() : -gutters.offsetWidth) + "px"; - } - inside.push(node); - } - lineElement = elt("div", inside, null, "position: relative"); - if (ie_lt8) lineElement.style.zIndex = 2; - } - } - lineDiv.insertBefore(lineElement, curNode); - } else { - curNode = curNode.nextSibling; - } - ++j; - }); + function scrollIntoView(display, x1, y1, x2, y2) { + var scrollPos = calculateScrollPos(display, x1, y1, x2, y2); + if (scrollPos.scrollLeft != null) {display.scrollbarH.scrollLeft = display.scroller.scrollLeft = scrollPos.scrollLeft;} + if (scrollPos.scrollTop != null) {display.scrollbarV.scrollTop = display.scroller.scrollTop = scrollPos.scrollTop;} + } + + function calculateScrollPos(display, x1, y1, x2, y2) { + var pt = paddingTop(display); + y1 += pt; y2 += pt; + var screen = display.scroller.clientHeight - scrollerCutOff, screentop = display.scroller.scrollTop, result = {}; + var docBottom = display.scroller.scrollHeight - scrollerCutOff; + var atTop = y1 < pt + 10, atBottom = y2 + pt > docBottom - 10; + if (y1 < screentop) result.scrollTop = atTop ? 0 : Math.max(0, y1); + else if (y2 > screentop + screen) result.scrollTop = (atBottom ? docBottom : y2 - screen); + + var screenw = display.scroller.clientWidth - scrollerCutOff, screenleft = display.scroller.scrollLeft; + x1 += display.gutters.offsetWidth; x2 += display.gutters.offsetWidth; + var gutterw = display.gutters.offsetWidth; + var atLeft = x1 < gutterw + 10; + if (x1 < screenleft + gutterw || atLeft) { + if (atLeft) x1 = 0; + result.scrollLeft = Math.max(0, x1 - 10 - gutterw); + } else if (x2 > screenw + screenleft - 3) { + result.scrollLeft = x2 + 10 - screenw; } + return result; + } - var currentLineNumberWidth, currentLineNumberChars; - function maybeUpdateLineNumberWidth() { - if (!options.lineNumbers) return false; - var last = lineNumberFor(doc.size - 1); - if (last.length != currentLineNumberChars) { - var test = measure.appendChild(elt("div", [elt("div", last)], "CodeMirror-linenumber CodeMirror-gutter-elt")); - currentLineNumberWidth = test.firstChild.offsetWidth; - currentLineNumberChars = currentLineNumberWidth ? last.length : -1; - lineGutter.style.width = currentLineNumberWidth + "px"; - return true; - } - return false; + // API UTILITIES + + function indentLine(cm, n, how) { + var doc = cm.view.doc; + if (!how) how = "add"; + if (how == "smart") { + if (!doc.mode.indent) how = "prev"; + else var state = getStateBefore(doc, n); } - function updateGutters() { - removeChildren(gutters); - for (var i = 0; i < options.gutters.length; ++i) { - var gutterClass = options.gutters[i]; - var gElt = gutters.appendChild(elt("div", null, "CodeMirror-gutter " + gutterClass)); - if (gutterClass == "CodeMirror-linenumbers") { - lineGutter = gElt; - gElt.style.width = (currentLineNumberWidth || 1) + "px"; - } - } - gutters.style.display = i ? "" : "none"; + var line = getLine(doc, n), curSpace = line.indentation(doc.tabSize), + curSpaceString = line.text.match(/^\s*/)[0], indentation; + if (how == "smart") { + indentation = doc.mode.indent(state, line.text.slice(curSpaceString.length), line.text); + if (indentation == Pass) how = "prev"; } - function guttersChanged() { - updateGutters(); - updateDisplay(true); + if (how == "prev") { + if (n) indentation = getLine(doc, n-1).indentation(doc.tabSize); + else indentation = 0; } - function setGuttersForLineNumbers() { - var found = false; - for (var i = 0; i < options.gutters.length; ++i) { - if (options.gutters[i] == "CodeMirror-linenumbers") { - if (options.lineNumbers) found = true; - else options.gutters.splice(i--, 1); - } - } - if (!found && options.lineNumbers) - options.gutters.push("CodeMirror-linenumbers"); - } - - function updateSelection() { - var headPos; - if (posEq(sel.from, sel.to)) { // No selection, single cursor - var pos = headPos = cursorCoords(sel.from, "div"); - cursor.style.left = pos.left + "px"; - cursor.style.top = pos.top + "px"; - cursor.style.height = (pos.bottom - pos.top) * .85 + "px"; - cursor.style.display = ""; - selectionDiv.style.display = "none"; - - if (pos.other) { - otherCursor.style.display = ""; - otherCursor.style.left = pos.other.left + "px"; - otherCursor.style.top = pos.other.top + "px"; - otherCursor.style.height = (pos.other.bottom - pos.other.top) * .85 + "px"; - } else { otherCursor.style.display = "none"; } - } else { - headPos = cursorCoords(sel.inverted ? sel.from : sel.to, "div"); - var fragment = document.createDocumentFragment(); - var clientWidth = lineSpace.clientWidth || lineSpace.offsetWidth; - var add = function(left, top, right, bottom) { - if (top < 0) top = 0; - var rstyle = quirksMode ? "width: " + (!right ? clientWidth : clientWidth - right - left) + "px" - : "right: " + right + "px"; - fragment.appendChild(elt("div", null, "CodeMirror-selected", "position: absolute; left: " + left + - "px; top: " + top + "px; " + rstyle + "; height: " + (bottom - top) + "px")); - }; + else if (how == "add") indentation = curSpace + cm.options.indentUnit; + else if (how == "subtract") indentation = curSpace - cm.options.indentUnit; + indentation = Math.max(0, indentation); + var diff = indentation - curSpace; - var middleFrom = sel.from.line + 1, middleTo = sel.to.line - 1, sameLine = sel.from.line == sel.to.line; - var drawForLine = function(line, from, toArg, retTop) { - var lineObj = getLine(line), lineLen = lineObj.text.length; - var coords = function(ch) { return charCoords({line: line, ch: ch}, "div", lineObj); }; - var rVal = retTop ? Infinity : -Infinity; - iterateBidiSections(getOrder(lineObj), from, toArg == null ? lineLen : toArg, function(from, to, dir) { - var leftPos = coords(dir == "rtl" ? to - 1 : from); - var rightPos = coords(dir == "rtl" ? from : to - 1); - var left = leftPos.left, right = rightPos.right; - if (rightPos.top - leftPos.top > 3) { // Different lines, draw top part - add(left, leftPos.top, 0, leftPos.bottom); - left = paddingLeft(); - if (leftPos.bottom < rightPos.top) add(left, leftPos.bottom, 0, rightPos.top); - } - if (toArg == null && to == lineLen) right = clientWidth; - rVal = retTop ? Math.min(rightPos.top, rVal) : Math.max(rightPos.bottom, rVal); - add(left, rightPos.top, clientWidth - right, rightPos.bottom); - }); - return rVal; - }; + var indentString = "", pos = 0; + if (cm.options.indentWithTabs) + for (var i = Math.floor(indentation / doc.tabSize); i; --i) {pos += doc.tabSize; indentString += "\t";} + if (pos < indentation) indentString += spaceStr(indentation - pos); - var middleTop = Infinity, middleBot = -Infinity; - if (sel.from.ch || sameLine) - // Draw the first line of selection. - middleTop = drawForLine(sel.from.line, sel.from.ch, sameLine ? sel.to.ch : null); - else - // Simply include it in the middle block. - middleFrom = sel.from.line; - - if (!sameLine && sel.to.ch) - middleBot = drawForLine(sel.to.line, 0, sel.to.ch, true); - - if (middleFrom <= middleTo) { - // Draw the middle - var botLine = getLine(middleTo), - bottom = charCoords({line: middleTo, ch: botLine.text.length}, "div", botLine); - // Kludge to try and prevent fetching coordinates twice if - // start end end are on same line. - var top = (middleFrom != middleTo || botLine.height > bottom.bottom - bottom.top) ? - charCoords({line: middleFrom, ch: 0}, "div") : bottom; - middleTop = Math.min(middleTop, top.top); - middleBot = Math.max(middleBot, bottom.bottom); - } - if (middleTop < middleBot) add(paddingLeft(), middleTop, 0, middleBot); - - removeChildrenAndAdd(selectionDiv, fragment); - cursor.style.display = otherCursor.style.display = "none"; - selectionDiv.style.display = ""; - } - - // Move the hidden textarea near the cursor to prevent scrolling artifacts - var wrapOff = wrapper.getBoundingClientRect(), lineOff = lineDiv.getBoundingClientRect(); - inputDiv.style.top = Math.max(0, Math.min(wrapper.clientHeight - 10, headPos.top + lineOff.top - wrapOff.top)) + "px"; - inputDiv.style.left = Math.max(0, Math.min(wrapper.clientWidth - 10, headPos.left + lineOff.left - wrapOff.left)) + "px"; - } - - function setShift(val) { - if (val) shiftSelecting = shiftSelecting || (sel.inverted ? sel.to : sel.from); - else shiftSelecting = null; - } - function setSelectionUser(from, to) { - var sh = shiftSelecting && clipPos(shiftSelecting); - if (sh) { - if (posLess(sh, from)) from = sh; - else if (posLess(to, sh)) to = sh; - } - setSelection(from, to); - userSelChange = true; - } - // Update the selection. Last two args are only used by - // updateLines, since they have to be expressed in the line - // numbers before the update. - function setSelection(from, to, isChange) { - goalColumn = null; - if (posEq(sel.from, from) && posEq(sel.to, to)) return; - if (posLess(to, from)) {var tmp = to; to = from; from = tmp;} - - // Skip over hidden lines. - if (isChange || from.line != sel.from.line) { - var from1 = skipHidden(from, sel.from.line, sel.from.ch, true); - // If there is no non-hidden line left, force visibility on current line - if (!from1) unfoldLines(getLine(from.line).hidden.id); - else from = from1; - } - if (isChange || to.line != sel.to.line) to = skipHidden(to, sel.to.line, sel.to.ch, true); - - if (posEq(from, to)) sel.inverted = false; - else if (posEq(from, sel.to)) sel.inverted = false; - else if (posEq(to, sel.from)) sel.inverted = true; - - if (options.autoClearEmptyLines && posEq(sel.from, sel.to)) { - var head = sel.inverted ? from : to; - if (head.line != sel.from.line && sel.from.line < doc.size) { - var oldLine = getLine(sel.from.line); - if (/^\s+$/.test(oldLine.text)) - setTimeout(operation(function() { - if (oldLine.parent && /^\s+$/.test(oldLine.text)) { - var no = lineNo(oldLine); - replaceRange("", {line: no, ch: 0}, {line: no, ch: oldLine.text.length}); - } - }, 10)); - } - } + if (indentString != curSpaceString) + replaceRange(cm, indentString, {line: n, ch: 0}, {line: n, ch: curSpaceString.length}); + } + + function changeLine(cm, handle, op) { + var no = handle, line = handle, doc = cm.view.doc; + if (typeof handle == "number") line = getLine(doc, clipLine(doc, handle)); + else no = lineNo(handle); + if (no == null) return null; + if (op(line, no)) regChange(cm, no, no + 1); + else return null; + return line; + } - sel.from = from; sel.to = to; - selectionChanged = true; + function findPosH(cm, dir, unit, visually) { + var doc = cm.view.doc, end = selHead(cm.view), line = end.line, ch = end.ch; + var lineObj = getLine(doc, line); + function findNextLine() { + for (var l = line + dir, e = dir < 0 ? -1 : doc.size; l != e; l += dir) { + var lo = getLine(doc, l); + if (!lo.hidden || !lo.hidden.handle.unfoldOnEneter) { line = l; lineObj = lo; return true; } + } + } + function moveOnce(boundToLine) { + var next = (visually ? moveVisually : moveLogically)(lineObj, ch, dir, true); + if (next == null) { + if (!boundToLine && findNextLine()) { + if (visually) ch = (dir < 0 ? lineRight : lineLeft)(lineObj); + else ch = dir < 0 ? lineObj.text.length : 0; + } else return false; + } else ch = next; + return true; } - function skipHidden(pos, oldLine, oldCh, allowUnfold) { - function getNonHidden(dir) { - var lNo = pos.line + dir, end = dir == 1 ? doc.size : -1; - while (lNo != end) { - var line = getLine(lNo); - if (!line.hidden) { - var ch = pos.ch; - if (toEnd || ch > oldCh || ch > line.text.length) ch = line.text.length; - return {line: lNo, ch: ch}; - } - lNo += dir; - } - } - var line = getLine(pos.line); - while (allowUnfold && line.hidden && line.hidden.handle.unfoldOnEnter) - unfoldLines(line.hidden.handle); - if (!line.hidden) return pos; - var toEnd = pos.ch == line.text.length && pos.ch != oldCh; - if (pos.line >= oldLine) return getNonHidden(1) || getNonHidden(-1); - else return getNonHidden(-1) || getNonHidden(1); - } - function setCursor(line, ch, user) { - var pos = clipPos({line: line, ch: ch || 0}); - (user ? setSelectionUser : setSelection)(pos, pos); - } - - function clipLine(n) {return Math.max(0, Math.min(n, doc.size-1));} - function clipPos(pos) { - if (pos.line < 0) return {line: 0, ch: 0}; - if (pos.line >= doc.size) return {line: doc.size-1, ch: getLine(doc.size-1).text.length}; - var ch = pos.ch, linelen = getLine(pos.line).text.length; - if (ch == null || ch > linelen) return {line: pos.line, ch: linelen}; - else if (ch < 0) return {line: pos.line, ch: 0}; - else return pos; - } - - function findPosH(dir, unit, visually) { - var end = sel.inverted ? sel.from : sel.to, line = end.line, ch = end.ch; - var lineObj = getLine(line); - function findNextLine() { - for (var l = line + dir, e = dir < 0 ? -1 : doc.size; l != e; l += dir) { - var lo = getLine(l); - if (!lo.hidden || !lo.hidden.handle.unfoldOnEneter) { line = l; lineObj = lo; return true; } - } - } - function moveOnce(boundToLine) { - var next = (visually ? moveVisually : moveLogically)(lineObj, ch, dir, true); - if (next == null) { - if (!boundToLine && findNextLine()) { - if (visually) ch = (dir < 0 ? lineRight : lineLeft)(lineObj); - else ch = dir < 0 ? lineObj.text.length : 0; - } else return false; - } else ch = next; - return true; - } - if (unit == "char") moveOnce(); - else if (unit == "column") moveOnce(true); - else if (unit == "word") { - var sawWord = false; - for (;;) { - if (dir < 0) if (!moveOnce()) break; - if (isWordChar(lineObj.text.charAt(ch))) sawWord = true; - else if (sawWord) {if (dir < 0) {dir = 1; moveOnce();} break;} - if (dir > 0) if (!moveOnce()) break; - } + if (unit == "char") moveOnce(); + else if (unit == "column") moveOnce(true); + else if (unit == "word") { + var sawWord = false; + for (;;) { + if (dir < 0) if (!moveOnce()) break; + if (isWordChar(lineObj.text.charAt(ch))) sawWord = true; + else if (sawWord) {if (dir < 0) {dir = 1; moveOnce();} break;} + if (dir > 0) if (!moveOnce()) break; } - return {line: line, ch: ch}; - } - function moveH(dir, unit) { - var pos = dir < 0 ? sel.from : sel.to; - if (shiftSelecting || posEq(sel.from, sel.to)) pos = findPosH(dir, unit, true); - setCursor(pos.line, pos.ch, true); - } - function deleteH(dir, unit) { - if (!posEq(sel.from, sel.to)) replaceRange("", sel.from, sel.to); - else replaceRange("", sel.from, findPosH(dir, unit, false)); - userSelChange = true; } - function moveV(dir, unit) { - var dist = 0, cur = sel.inverted ? sel.from : sel.to, pos = cursorCoords(cur, "div"); - var x = pos.left, y; - if (goalColumn != null) x = goalColumn; - if (unit == "page") { - var pageSize = Math.min(wrapper.clientHeight, window.innerHeight || document.documentElement.clientHeight); - y = pos.top + dir * pageSize; - } else if (unit == "line") { - y = dir > 0 ? pos.bottom + 3 : pos.top - 3; + return {line: line, ch: ch}; + } + + function findWordAt(line, pos) { + var start = pos.ch, end = pos.ch; + if (line) { + if (pos.after === false || end == line.length) --start; else ++end; + var startChar = line.charAt(start); + var check = isWordChar(startChar) ? isWordChar : + /\s/.test(startChar) ? function(ch) {return /\s/.test(ch);} : + function(ch) {return !/\s/.test(ch) && !isWordChar(ch);}; + while (start > 0 && check(line.charAt(start - 1))) --start; + while (end < line.length && check(line.charAt(end))) ++end; + } + return {from: {line: pos.line, ch: start}, to: {line: pos.line, ch: end}}; + } + + function selectLine(cm, line) { + setSelectionUser(cm, {line: line, ch: 0}, clipPos(cm.view.doc, {line: line + 1, ch: 0})); + } + + // PROTOTYPE + + // The publicly visible API. Note that operation(null, f) means + // 'wrap f in an operation, performed on its `this` parameter' + + CodeMirror.prototype = { + getValue: function(lineSep) { + var text = [], doc = this.view.doc; + doc.iter(0, doc.size, function(line) { text.push(line.text); }); + return text.join(lineSep || "\n"); + }, + + setValue: operation(null, function(code) { + var doc = this.view.doc, top = {line: 0, ch: 0}, lastLen = getLine(doc, doc.size-1).text.length; + updateDoc(this, top, {line: doc.size - 1, ch: lastLen}, splitLines(code), top, top); + this.curOp.updateInput = true; + }), + + getSelection: function(lineSep) { return this.getRange(this.view.sel.from, this.view.sel.to, lineSep); }, + + replaceSelection: operation(null, function(code, collapse) { + var sel = this.view.sel; + replaceRange1(this, splitLines(code), sel.from, sel.to, function(end) { + if (collapse == "end") return {from: end, to: end}; + else if (collapse == "start") return {from: sel.from, to: sel.from}; + else return {from: sel.from, to: end}; + }); + }), + + focus: function(){window.focus(); focusInput(this); onFocus(this); fastPoll(this);}, + + setOption: function(option, value) { + var options = this.options; + if (options[option] == value && option != "mode") return; + options[option] = value; + if (option == "mode" || option == "indentUnit") loadMode(this); + else if (option == "readOnly" && value == "nocursor") {onBlur(this); this.display.input.blur();} + else if (option == "readOnly" && !value) {resetInput(this.display, this.view, true);} + else if (option == "theme") themeChanged(this); + else if (option == "lineWrapping") operation(this, wrappingChanged)(this); + else if (option == "tabSize") {this.view.doc.tabSize = value; updateDisplay(this, true);} + else if (option == "keyMap") keyMapChanged(this); + else if (option == "gutters" || option == "lineNumbers") setGuttersForLineNumbers(this.options); + if (option == "lineNumbers" || option == "gutters" || option == "firstLineNumber" || + option == "theme" || option == "lineNumberFormatter") + guttersChanged(this); + if (optionHandlers.hasOwnProperty(option)) + optionHandlers[option](this, value); + }, + + getOption: function(option) {return this.options[option];}, + + getMode: function() {return this.view.doc.mode;}, + + undo: operation(null, function() { + var hist = this.view.doc.history; + unredoHelper(this, hist.done, hist.undone); + }), + redo: operation(null, function() { + var hist = this.view.doc.history; + unredoHelper(this, hist.undone, hist.done); + }), + + indentLine: operation(null, function(n, dir) { + if (typeof dir != "string") { + if (dir == null) dir = this.options.smartIndent ? "smart" : "prev"; + else dir = dir ? "add" : "subtract"; } - var target = coordsChar(x, y), line; - // Work around problem with moving 'through' line widgets - if (dir > 0 && target.line == cur.line && cur.line < doc.size - 1 && getLine(cur.line).widgets && - Math.abs(cursorCoords(target, "div").top - pos.top) < 2) - target = coordsChar(x, cursorCoords({line: cur.line + 1, ch: 0}, "div").top + 3); - else if (dir < 0 && cur.line > 0 && (line = getLine(target.line)).widgets && target.ch == line.text.length) - target = coordsChar(x, cursorCoords({line: target.line, ch: line.text.length}, "div").bottom - 3); - - if (unit == "page") scrollbarV.scrollTop += charCoords(target, "div").top - pos.top; - setCursor(target.line, target.ch, true); - goalColumn = x; - } - - function findWordAt(pos) { - var line = getLine(pos.line).text; - var start = pos.ch, end = pos.ch; - if (line) { - if (pos.after === false || end == line.length) --start; else ++end; - var startChar = line.charAt(start); - var check = isWordChar(startChar) ? isWordChar : - /\s/.test(startChar) ? function(ch) {return /\s/.test(ch);} : - function(ch) {return !/\s/.test(ch) && !isWordChar(ch);}; - while (start > 0 && check(line.charAt(start - 1))) --start; - while (end < line.length && check(line.charAt(end))) ++end; - } - return {from: {line: pos.line, ch: start}, to: {line: pos.line, ch: end}}; - } - function selectLine(line) { - setSelectionUser({line: line, ch: 0}, clipPos({line: line + 1, ch: 0})); - } - function indentSelected(mode) { - if (posEq(sel.from, sel.to)) return indentLine(sel.from.line, mode); + if (isLine(this.view.doc, n)) indentLine(this, n, dir); + }), + + indentSelection: operation(null, function(how) { + var sel = this.view.sel; + if (posEq(sel.from, sel.to)) return indentLine(this, sel.from.line, how); var e = sel.to.line - (sel.to.ch ? 0 : 1); - for (var i = sel.from.line; i <= e; ++i) indentLine(i, mode); - } - - function indentLine(n, how) { - if (!how) how = "add"; - if (how == "smart") { - if (!mode.indent) how = "prev"; - else var state = getStateBefore(n); - } - - var line = getLine(n), curSpace = line.indentation(options.tabSize), - curSpaceString = line.text.match(/^\s*/)[0], indentation; - if (how == "smart") { - indentation = mode.indent(state, line.text.slice(curSpaceString.length), line.text); - if (indentation == Pass) how = "prev"; - } - if (how == "prev") { - if (n) indentation = getLine(n-1).indentation(options.tabSize); - else indentation = 0; - } - else if (how == "add") indentation = curSpace + options.indentUnit; - else if (how == "subtract") indentation = curSpace - options.indentUnit; - indentation = Math.max(0, indentation); - var diff = indentation - curSpace; - - var indentString = "", pos = 0; - if (options.indentWithTabs) - for (var i = Math.floor(indentation / options.tabSize); i; --i) {pos += options.tabSize; indentString += "\t";} - if (pos < indentation) indentString += spaceStr(indentation - pos); - - if (indentString != curSpaceString) - replaceRange(indentString, {line: n, ch: 0}, {line: n, ch: curSpaceString.length}); - } - - function loadMode() { - mode = CodeMirror.getMode(options, options.mode); - doc.iter(0, doc.size, function(line) { line.stateAfter = null; }); - frontier = 0; - startWorker(100); - } - function wrappingChanged(from, to) { - if (options.lineWrapping) { - wrapper.className += " CodeMirror-wrap"; - var perLine = wrapper.clientWidth / charWidth() - 3; - doc.iter(0, doc.size, function(line) { - if (line.hidden) return; - var guess = Math.ceil(line.text.length / perLine) || 1; - if (guess != 1) updateLineHeight(line, guess); - }); - sizer.style.minWidth = ""; - } else { - wrapper.className = wrapper.className.replace(" CodeMirror-wrap", ""); - computeMaxLength(); - doc.iter(0, doc.size, function(line) { - if (line.height != 1 && !line.hidden) updateLineHeight(line, 1); - }); - } - changes.push({from: 0, to: doc.size}); - } - function themeChanged() { - wrapper.className = wrapper.className.replace(/\s*cm-s-\S+/g, "") + - options.theme.replace(/(^|\s)\s*/g, " cm-s-"); - } - function keyMapChanged() { - var style = keyMap[options.keyMap].style; - wrapper.className = wrapper.className.replace(/\s*cm-keymap-\S+/g, "") + - (style ? " cm-keymap-" + style : ""); - } + for (var i = sel.from.line; i <= e; ++i) indentLine(this, i, how); + }), - function TextMarker(type, style) { this.lines = []; this.type = type; if (style) this.style = style; } - TextMarker.prototype.clear = operation(function() { - var min = Infinity, max = -Infinity; - for (var i = 0; i < this.lines.length; ++i) { - var line = this.lines[i]; - var span = getMarkedSpanFor(line.markedSpans, this, true); - if (span.from != null || span.to != null) { - var lineN = lineNo(line); - min = Math.min(min, lineN); max = Math.max(max, lineN); - } - } - if (min != Infinity) - changes.push({from: min, to: max + 1}); - this.lines.length = 0; - }); - TextMarker.prototype.find = function() { - var from, to; - for (var i = 0; i < this.lines.length; ++i) { - var line = this.lines[i]; - var span = getMarkedSpanFor(line.markedSpans, this); - if (span.from != null || span.to != null) { - var found = lineNo(line); - if (span.from != null) from = {line: found, ch: span.from}; - if (span.to != null) to = {line: found, ch: span.to}; - } - } - if (this.type == "bookmark") return from; - return from && {from: from, to: to}; - }; + historySize: function() { + var hist = this.view.doc.history; + return {undo: hist.done.length, redo: hist.undone.length}; + }, - function markText(from, to, className, options) { - from = clipPos(from); to = clipPos(to); - var marker = new TextMarker("range", className); + clearHistory: function() {this.view.doc.history = new History();}, + + getHistory: function() { + var hist = this.view.doc.history; + hist.time = 0; + return {done: hist.done.concat([]), undone: hist.undone.concat([])}; + }, + + setHistory: function(histData) { + var hist = this.view.doc.history = new History(); + hist.done = histData.done; + hist.undone = histData.undone; + }, + + getTokenAt: function(pos) { + var doc = this.view.doc; + pos = clipPos(doc, pos); + return getLine(doc, pos.line).getTokenAt(doc.mode, getStateBefore(doc, pos.line), + this.options.tabSize, pos.ch); + }, + + getStateAfter: function(line) { + var doc = this.view.doc; + line = clipLine(doc, line == null ? doc.size - 1: line); + return getStateBefore(doc, line + 1); + }, + + cursorCoords: function(start, mode) { + var pos, sel = this.view.sel; + if (start == null) start = sel.inverted; + if (typeof start == "object") pos = clipPos(this.view.doc, start); + else pos = start ? sel.from : sel.to; + return cursorCoords(this, pos, mode || "page"); + }, + + charCoords: function(pos, mode) { + return charCoords(this, clipPos(this.view.doc, pos), mode || "page"); + }, + + coordsChar: function(coords) { + var off = this.display.lineSpace.getBoundingClientRect(); + return coordsChar(this, coords.left - off.left, coords.top - off.top); + }, + + markText: operation(null, function(from, to, className, options) { + var doc = this.view.doc; + from = clipPos(doc, from); to = clipPos(doc, to); + var marker = new TextMarker(this, "range", className); if (options) for (var opt in options) if (options.hasOwnProperty(opt)) marker[opt] = options[opt]; var curLine = from.line; @@ -1697,22 +2067,24 @@ window.CodeMirror = (function() { marker.lines.push(line); ++curLine; }); - changes.push({from: from.line, to: to.line + 1}); + regChange(this, from.line, to.line + 1); return marker; - } + }), - function setBookmark(pos) { - pos = clipPos(pos); - var marker = new TextMarker("bookmark"), line = getLine(pos.line); + setBookmark: function(pos) { + var doc = this.view.doc; + pos = clipPos(doc, pos); + var marker = new TextMarker(this, "bookmark"), line = getLine(doc, pos.line); var span = {from: pos.ch, to: pos.ch, marker: marker}; (line.markedSpans || (line.markedSpans = [])).push(span); marker.lines.push(line); return marker; - } + }, - function findMarksAt(pos) { - pos = clipPos(pos); - var markers = [], spans = getLine(pos.line).markedSpans; + findMarksAt: function(pos) { + var doc = this.view.doc; + pos = clipPos(doc, pos); + var markers = [], spans = getLine(doc, pos.line).markedSpans; if (spans) for (var i = 0; i < spans.length; ++i) { var span = spans[i]; if ((span.from == null || span.from <= pos.ch) && @@ -1720,88 +2092,81 @@ window.CodeMirror = (function() { markers.push(span.marker); } return markers; - } + }, - function isEmpty(obj) { - var c = 0; - for (var n in obj) if (obj.hasOwnProperty(n) && obj[n]) ++c; - return !c; - } - function setGutterMarker(line, gutterID, value) { - return changeLine(line, function(line) { + setGutterMarker: operation(null, function(line, gutterID, value) { + return changeLine(this, line, function(line) { var markers = line.gutterMarkers || (line.gutterMarkers = {}); markers[gutterID] = value; if (!value && isEmpty(markers)) line.gutterMarkers = null; return true; }); - } - function clearGutter(gutterID) { - var i = 0; + }), + + clearGutter: operation(null, function(gutterID) { + var i = 0, cm = this, doc = cm.view.doc; doc.iter(0, doc.size, function(line) { if (line.gutterMarkers && line.gutterMarkers[gutterID]) { line.gutterMarkers[gutterID] = null; - changes.push({from: i, to: i + 1}); + regChange(cm, i, i + 1); if (isEmpty(line.gutterMarkers)) line.gutterMarkers = null; } ++i; }); - } + }), - function changeLine(handle, op) { - var no = handle, line = handle; - if (typeof handle == "number") line = getLine(clipLine(handle)); - else no = lineNo(handle); - if (no == null) return null; - if (op(line, no)) changes.push({from: no, to: no + 1}); - else return null; - return line; - } - function setLineClass(handle, className, bgClassName) { - return changeLine(handle, function(line) { + setLineClass: operation(null, function(handle, className, bgClassName) { + return changeLine(this, handle, function(line) { if (line.className != className || line.bgClassName != bgClassName) { line.className = className; line.bgClassName = bgClassName; return true; } }); - } - function addLineWidget(handle, node, options) { + }), + + addLineWidget: operation(null, function addLineWidget(handle, node, options) { var widget = options || {}; widget.node = node; - if (widget.noHScroll) alignWidgets = true; - changeLine(handle, function(line) { + if (widget.noHScroll) this.display.alignWidgets = true; + changeLine(this, handle, function(line) { (line.widgets || (line.widgets = [])).push(widget); widget.line = line; return true; }); return widget; - } - function removeLineWidget(widget) { + }), + + removeLineWidget: operation(null, function(widget) { var ws = widget.line.widgets, no = lineNo(widget.line); if (no == null) return; for (var i = 0; i < ws.length; ++i) if (ws[i] == widget) ws.splice(i--, 1); - changes.push({from: no, to: no + 1}); - } + regChange(this, no, no + 1); + }), - function foldLines(from, to, unfoldOnEnter) { + foldLines: operation(null, function(from, to, unfoldOnEnter) { if (typeof from != "number") from = lineNo(from); if (typeof to != "number") to = lineNo(to); if (from > to) return; - var lines = [], handle = {lines: lines, unfoldOnEnter: unfoldOnEnter}; + var lines = [], handle = {lines: lines, unfoldOnEnter: unfoldOnEnter}, cm = this, view = cm.view, doc = view.doc; doc.iter(from, to, function(line) { lines.push(line); - if (!line.hidden && line.text.length == maxLine.text.length) updateMaxLine = true; + if (!line.hidden && line.text.length == cm.view.maxLine.text.length) + cm.curOp.updateMaxLine = true; line.hidden = {handle: handle, prev: line.hidden}; updateLineHeight(line, 0); }); - var selFrom = sel.from, selTo = sel.to; - if (selFrom.line >= from && selFrom.line < to) selFrom = skipHidden({line: selFrom.line, ch: 0}, selFrom.line, 0); - if (selTo.line >= from && selTo.line < to) selTo = skipHidden({line: selTo.line, ch: 0}, selTo.line, 0); - if (selFrom != sel.from || selTo != sel.to) setSelection(selFrom, selTo); - changes.push({from: from, to: to}); + var sel = view.sel, selFrom = sel.from, selTo = sel.to; + if (selFrom.line >= from && selFrom.line < to) + selFrom = skipHidden(doc, {line: selFrom.line, ch: 0}, selFrom.line, 0); + if (selTo.line >= from && selTo.line < to) + selTo = skipHidden(doc, {line: selTo.line, ch: 0}, selTo.line, 0); + if (selFrom != sel.from || selTo != sel.to) setSelection(this, selFrom, selTo); + regChange(cm, from, to); return handle; - } - function unfoldLines(handle) { + }), + + unfoldLines: operation(null, function(handle) { var from, to; for (var i = 0; i < handle.lines.length; ++i) { var line = handle.lines[i], hidden = line.hidden; @@ -1811,21 +2176,24 @@ window.CodeMirror = (function() { if (hidden && !line.hidden) { var no = lineNo(handle); from = Math.min(from, no); to = Math.max(to, no); - updateLineHeight(line, textHeight()); - if (line.text.length > maxLine.text.length) { maxLine = line; maxLineChanged = true; } + updateLineHeight(line, textHeight(this.display)); + if (line.text.length > this.view.maxLine.text.length) { + this.view.maxLine = line; + this.view.maxLineChanged = true; + } } } if (from != null) { - changes.push({from: from, to: to + 1}); - signalLater(handle, "unfold", delayedCallbacks); + regChange(this, from, to + 1); + signalLater(this, handle, "unfold"); } - } + }), - function lineInfo(line) { + lineInfo: function(line) { if (typeof line == "number") { - if (!isLine(line)) return null; + if (!isLine(this.view.doc, line)) return null; var n = line; - line = getLine(line); + line = getLine(this.view.doc, line); if (!line) return null; } else { var n = lineNo(line); @@ -1833,381 +2201,208 @@ window.CodeMirror = (function() { } return {line: n, handle: line, text: line.text, gutterMarkers: line.gutterMarkers, lineClass: line.className, bgClass: line.bgClassName, widgets: line.widgets}; - } + }, - function paddingLeft() { - var e = removeChildrenAndAdd(measure, elt("pre")).appendChild(elt("span", "x")); - return e.offsetLeft; - } - - function measureLine(line, ch) { - for (var i = 0; i < measureLineMemo.length; ++i) { - var memo = measureLineMemo[i]; - if (memo.ch == ch && memo.text == line.text && - (!options.lineWrapping || scroller.clientWidth == memo.width)) - return {top: memo.top, bottom: memo.bottom, left: memo.left, right: memo.right}; - } - var atEnd = ch && ch == line.text.length; - var pre = lineContent(line, atEnd ? ch - 1 : ch); - removeChildrenAndAdd(measure, pre); - var anchor = pre.anchor; - // We'll sample once at the top, once at the bottom of the line, - // to get the real line height (in case there tokens on the line - // with bigger fonts) - anchor.style.verticalAlign = "top"; - var left = anchor.offsetLeft, right = left + anchor.offsetWidth; - if (ie) { - // In IE, verticalAlign does not influence offsetTop, unless - // the element is an inline-block. Unfortunately, inline - // blocks have different wrapping behaviour, so we have to do - // some icky thing with inserting "Zero-Width No-Break Spaces" - // to compensate for wrapping artifacts. - anchor.style.display = "inline-block"; - if (options.lineWrapping && anchor.offsetLeft != left) { - anchor.parentNode.insertBefore(document.createTextNode("\ufeff"), anchor); - if (anchor.offsetLeft != left) - anchor.parentNode.insertBefore(document.createTextNode("\ufeff"), anchor.nextSibling); - if (anchor.offsetLeft != left) - anchor.parentNode.removeChild(anchor.previousSibling); - } - } - var top = Math.max(0, anchor.offsetTop); - anchor.style.verticalAlign = "bottom"; - var bottom = Math.min(anchor.offsetTop + anchor.offsetHeight, pre.offsetHeight); - if (atEnd) left = right; - - var memo = {ch: ch, text: line.text, width: wrapper.clientWidth, - top: top, bottom: bottom, left: left, right: right}; - if (measureLineMemo.length == 8) measureLineMemo[++measureLineMemoPos % 8] = memo; - else measureLineMemo.push(memo); - return {top: top, bottom: bottom, left: left, right: right}; - } - - function intoCoordSystem(pos, rect, context) { - if (context == "line") return rect; - var yOff = heightAtLine(doc, pos.line) - (context == "div" ? displayOffset : 0); - if (context == "page") { - var lOff = lineSpace.getBoundingClientRect(); - yOff += lOff.top; rect.left += lOff.left; rect.right += lOff.right; - } - rect.top += yOff; rect.bottom += yOff; - return rect; - } - - function charCoords(pos, context, lineObj) { - return intoCoordSystem(pos, measureLine(lineObj || getLine(pos.line), pos.ch), context); - } - - function cursorCoords(pos, context, lineObj) { - lineObj = lineObj || getLine(pos.line); - function get(ch, right) { - var m = measureLine(lineObj, ch); - if (right) m.left = m.right; else m.right = m.left; - return intoCoordSystem(pos, m, context); - } - var order = getOrder(lineObj), ch = pos.ch; - if (!order) return get(ch); - var main, other, linedir = order[0].level; - for (var i = 0; i < order.length; ++i) { - var part = order[i], rtl = part.level % 2, nb, here; - if (part.from < ch && part.to > ch) return get(ch, rtl); - var left = rtl ? part.to : part.from, right = rtl ? part.from : part.to; - if (left == ch) { - // Opera and IE return bogus offsets and widths for edges - // where the direction flips, but only for the side with the - // lower level. So we try to use the side with the higher - // level. - if (i && part.level < (nb = order[i-1]).level) here = get(nb.level % 2 ? nb.from : nb.to - 1, true); - else here = get(rtl && part.from != part.to ? ch - 1 : ch); - if (rtl == linedir) main = here; else other = here; - } else if (right == ch) { - var nb = i < order.length - 1 && order[i+1]; - if (!rtl && nb && nb.from == nb.to) continue; - if (nb && part.level < nb.level) here = get(nb.level % 2 ? nb.to - 1 : nb.from); - else here = get(rtl ? ch : ch - 1, true); - if (rtl == linedir) main = here; else other = here; - } - } - if (linedir && !ch) other = get(order[0].to - 1); - if (!main) return other; - if (other) main.other = other; - return main; - } - - // Coords must be lineSpace-local - function coordsChar(x, y) { - var cw = charWidth(), heightPos = displayOffset + y; - if (heightPos < 0) return {line: 0, ch: 0}; - var lineNo = lineAtHeight(doc, heightPos); - if (lineNo >= doc.size) return {line: doc.size - 1, ch: getLine(doc.size - 1).text.length}; - var lineObj = getLine(lineNo); - if (!lineObj.text.length) return {line: lineNo, ch: 0}; - var tw = options.lineWrapping, innerOff = tw ? heightPos - heightAtLine(doc, lineNo) : 0; - if (x < 0) x = 0; - var wrongLine = false; - function getX(ch) { - var sp = cursorCoords({line: lineNo, ch: ch}, "line", lineObj); - if (tw) { - wrongLine = true; - if (innerOff > sp.bottom) return Math.max(0, sp.left - wrapper.clientWidth); - else if (innerOff < sp.top) return sp.left + wrapper.clientWidth; - else wrongLine = false; - } - return sp.left; - } - var bidi = getOrder(lineObj), dist = lineObj.text.length; - var from = lineLeft(lineObj), fromX = 0, to = lineRight(lineObj), toX; - if (!bidi) { - // Guess a suitable upper bound for our search. - var estimated = Math.min(to, Math.ceil((x + Math.floor(innerOff / textHeight()) * wrapper.clientWidth * .9) / cw)); - for (;;) { - var estX = getX(estimated); - if (estX <= x && estimated < to) estimated = Math.min(to, Math.ceil(estimated * 1.2)); - else {toX = estX; to = estimated; break;} - } - // Try to guess a suitable lower bound as well. - estimated = Math.floor(to * 0.8); estX = getX(estimated); - if (estX < x) {from = estimated; fromX = estX;} - dist = to - from; - } else toX = getX(to); - if (x > toX) return {line: lineNo, ch: to}; - // Do a binary search between these bounds. - for (;;) { - if (bidi ? to == from || to == moveVisually(lineObj, from, 1) : to - from <= 1) { - var after = x - fromX < toX - x, ch = after ? from : to; - while (isExtendingChar.test(lineObj.text.charAt(ch))) ++ch; - return {line: lineNo, ch: ch, after: after}; - } - var step = Math.ceil(dist / 2), middle = from + step; - if (bidi) { - middle = from; - for (var i = 0; i < step; ++i) middle = moveVisually(lineObj, middle, 1); - } - var middleX = getX(middle); - if (middleX > x) {to = middle; toX = middleX; if (wrongLine) toX += 1000; dist -= step;} - else {from = middle; fromX = middleX; dist = step;} + getViewport: function() { return {from: this.display.showingFrom, to: this.display.showingTo};}, + + addWidget: function(pos, node, scroll, vert, horiz) { + var display = this.display; + pos = cursorCoords(this, clipPos(this.view.doc, pos)); + var top = pos.top, left = pos.left; + node.style.position = "absolute"; + display.sizer.appendChild(node); + if (vert == "over") top = pos.top; + else if (vert == "near") { + var vspace = Math.max(display.wrapper.clientHeight, this.view.doc.height), + hspace = Math.max(display.sizer.clientWidth, display.lineSpace.clientWidth); + if (pos.bottom + node.offsetHeight > vspace && pos.top > node.offsetHeight) + top = pos.top - node.offsetHeight; + if (left + node.offsetWidth > hspace) + left = hspace - node.offsetWidth; + } + node.style.top = (top + paddingTop(display)) + "px"; + node.style.left = node.style.right = ""; + if (horiz == "right") { + left = display.sizer.clientWidth - node.offsetWidth; + node.style.right = "0px"; + } else { + if (horiz == "left") left = 0; + else if (horiz == "middle") left = (display.sizer.clientWidth - node.offsetWidth) / 2; + node.style.left = left + "px"; } - } + if (scroll) + scrollIntoView(display, left, top, left + node.offsetWidth, top + node.offsetHeight); + }, - var cachedHeight, cachedHeightFor, measurePre; - function textHeight() { - if (measurePre == null) { - measurePre = elt("pre"); - for (var i = 0; i < 49; ++i) { - measurePre.appendChild(document.createTextNode("x")); - measurePre.appendChild(elt("br")); - } - measurePre.appendChild(document.createTextNode("x")); - } - var offsetHeight = lineDiv.clientHeight; - if (offsetHeight == cachedHeightFor) return cachedHeight; - cachedHeightFor = offsetHeight; - removeChildrenAndAdd(measure, measurePre.cloneNode(true)); - cachedHeight = measure.firstChild.offsetHeight / 50 || 1; - removeChildren(measure); - return cachedHeight; - } - var cachedWidth, cachedWidthFor = 0; - function charWidth() { - if (wrapper.clientWidth == cachedWidthFor) return cachedWidth; - cachedWidthFor = wrapper.clientWidth; - var anchor = elt("span", "x"); - var pre = elt("pre", [anchor]); - removeChildrenAndAdd(measure, pre); - return (cachedWidth = anchor.offsetWidth || 10); - } - function paddingTop() {return lineSpace.offsetTop;} - - function posFromMouse(e, liberal) { - if (!liberal) { - var target = e_target(e); - if (target == scrollbarH || target == scrollbarH.firstChild || - target == scrollbarV || target == scrollbarV.firstChild || - target == scrollbarFiller) return null; - } - var x, y, space = lineSpace.getBoundingClientRect(); - // Fails unpredictably on IE[67] when mouse is dragged around quickly. - try { x = e.clientX; y = e.clientY; } catch (e) { return null; } - return coordsChar(x - space.left, y - space.top); - } - var detectingSelectAll; - function onContextMenu(e) { - var pos = posFromMouse(e), scrollPos = scroller.scrollTop; - if (!pos || opera) return; // Opera is difficult. - if (posEq(sel.from, sel.to) || posLess(pos, sel.from) || !posLess(pos, sel.to)) - operation(setCursor)(pos.line, pos.ch); - - var oldCSS = input.style.cssText; - inputDiv.style.position = "absolute"; - input.style.cssText = "position: fixed; width: 30px; height: 30px; top: " + (e.clientY - 5) + - "px; left: " + (e.clientX - 5) + "px; z-index: 1000; background: white; outline: none;" + - "border-width: 0; outline: none; overflow: hidden; opacity: .05; filter: alpha(opacity=5);"; - focusInput(); - resetInput(true); - // Adds "Select all" to context menu in FF - if (posEq(sel.from, sel.to)) input.value = prevInput = " "; - - function rehide() { - inputDiv.style.position = "relative"; - input.style.cssText = oldCSS; - if (ie_lt9) scrollbarV.scrollTop = scroller.scrollTop = scrollPos; - slowPoll(); - - // Try to detect the user choosing select-all - if (input.selectionStart != null) { - clearTimeout(detectingSelectAll); - var extval = input.value = " " + (posEq(sel.from, sel.to) ? "" : input.value), i = 0; - prevInput = " "; - input.selectionStart = 1; input.selectionEnd = extval.length; - detectingSelectAll = setTimeout(function poll(){ - if (prevInput == " " && input.selectionStart == 0) - operation(commands.selectAll)(instance); - else if (i++ < 10) detectingSelectAll = setTimeout(poll, 500); - else resetInput(); - }, 200); - } - } + lineCount: function() {return this.view.doc.size;}, - if (gecko) { - e_stop(e); - on(window, "mouseup", function mouseup() { - off(window, "mouseup", mouseup); - setTimeout(rehide, 20); - }); - } else { - setTimeout(rehide, 50); - } - } - - // Cursor-blinking - function restartBlink() { - clearInterval(blinker); - var on = true; - cursor.style.visibility = otherCursor.style.visibility = ""; - blinker = setInterval(function() { - cursor.style.visibility = otherCursor.style.visibility = (on = !on) ? "" : "hidden"; - }, options.cursorBlinkRate); - } - - // Finds the line to start with when starting a parse. Tries to - // find a line with a stateAfter, so that it can start with a - // valid state. If that fails, it returns the line with the - // smallest indentation, which tends to need the least context to - // parse correctly. - function findStartLine(n) { - var minindent, minline; - for (var search = n, lim = n - 40; search > lim; --search) { - if (search == 0) return 0; - var line = getLine(search-1); - if (line.stateAfter) return search; - var indented = line.indentation(options.tabSize); - if (minline == null || minindent > indented) { - minline = search - 1; - minindent = indented; - } + clipPos: function(pos) {return clipPos(this.view.doc, pos);}, + + getCursor: function(start) { + var sel = this.view.sel; + if (start == null) start = sel.inverted; + return copyPos(start ? sel.from : sel.to); + }, + + somethingSelected: function() {return !posEq(this.view.sel.from, this.view.sel.to);}, + + setCursor: operation(null, function(line, ch, user) { + var pos = typeof line == "number" ? {line: line, ch: ch || 0} : line; + (user ? setSelectionUser : setSelection)(this, pos, pos); + }), + + setSelection: operation(null, function(from, to, user) { + var doc = this.view.doc; + (user ? setSelectionUser : setSelection)(this, clipPos(doc, from), clipPos(doc, to || from)); + }), + + getLine: function(line) {var l = this.getLineHandle(line); return l && l.text;}, + + getLineHandle: function(line) { + var doc = this.view.doc; + if (isLine(doc, line)) return getLine(doc, line); + }, + + getLineNumber: function(line) {return lineNo(line);}, + + setLine: operation(null, function(line, text) { + if (isLine(this.view.doc, line)) + replaceRange(this, text, {line: line, ch: 0}, {line: line, ch: getLine(this.view.doc, line).text.length}); + }), + + removeLine: operation(null, function(line) { + if (isLine(this.view.doc, line)) + replaceRange(this, "", {line: line, ch: 0}, clipPos(this.view.doc, {line: line+1, ch: 0})); + }), + + replaceRange: operation(null, function(code, from, to) { + var doc = this.view.doc; + from = clipPos(doc, from); + to = to ? clipPos(doc, to) : from; + return replaceRange(this, code, from, to); + }), + + getRange: function(from, to, lineSep) { + var doc = this.view.doc; + from = clipPos(doc, from); to = clipPos(doc, to); + var l1 = from.line, l2 = to.line; + if (l1 == l2) return getLine(doc, l1).text.slice(from.ch, to.ch); + var code = [getLine(doc, l1).text.slice(from.ch)]; + doc.iter(l1 + 1, l2, function(line) { code.push(line.text); }); + code.push(getLine(doc, l2).text.slice(0, to.ch)); + return code.join(lineSep || "\n"); + }, + + triggerOnKeyDown: operation(null, onKeyDown), + + execCommand: function(cmd) {return commands[cmd](this);}, + + // Stuff used by commands, probably not much use to outside code. + moveH: operation(null, function(dir, unit) { + var sel = this.view.sel, pos = dir < 0 ? sel.from : sel.to; + if (sel.shift || posEq(sel.from, sel.to)) pos = findPosH(this, dir, unit, true); + setSelectionUser(this, pos, pos); + }), + + deleteH: operation(null, function(dir, unit) { + var sel = this.view.sel; + if (!posEq(sel.from, sel.to)) replaceRange(cm, "", sel.from, sel.to); + else replaceRange(this, "", sel.from, findPosH(this, dir, unit, false)); + this.curOp.userSelChange = true; + }), + + moveV: operation(null, function(dir, unit) { + var view = this.view, doc = view.doc, display = this.display; + var dist = 0, cur = selHead(view), pos = cursorCoords(this, cur, "div"); + var x = pos.left, y; + if (view.goalColumn != null) x = view.goalColumn; + if (unit == "page") { + var pageSize = Math.min(display.wrapper.clientHeight, window.innerHeight || document.documentElement.clientHeight); + y = pos.top + dir * pageSize; + } else if (unit == "line") { + y = dir > 0 ? pos.bottom + 3 : pos.top - 3; } - return minline; - } - function getStateBefore(n) { - var pos = findStartLine(n), state = pos && getLine(pos-1).stateAfter; - if (!state) state = startState(mode); - else state = copyState(mode, state); - doc.iter(pos, n, function(line) { - line.process(mode, state, options.tabSize); - line.stateAfter = (pos == n - 1 || pos % 5 == 0) ? copyState(mode, state) : null; + var target = coordsChar(this, x, y), line; + // Work around problem with moving 'through' line widgets + if (dir > 0 && target.line == cur.line && cur.line < doc.size - 1 && getLine(doc, cur.line).widgets && + Math.abs(cursorCoords(this, target, "div").top - pos.top) < 2) + target = coordsChar(this, x, cursorCoords(this, {line: cur.line + 1, ch: 0}, "div").top + 3); + else if (dir < 0 && cur.line > 0 && (line = getLine(doc, target.line)).widgets && target.ch == line.text.length) + target = coordsChar(this, x, cursorCoords(this, {line: target.line, ch: line.text.length}, "div").bottom - 3); + + if (unit == "page") display.scrollbarV.scrollTop += charCoords(this, target, "div").top - pos.top; + setSelectionUser(this, target, target); + view.goalColumn = x; + }), + + toggleOverwrite: function() { + if (this.view.overwrite = !this.view.overwrite) + this.display.cursor.className += " CodeMirror-overwrite"; + else + this.display.cursor.className = this.display.cursor.className.replace(" CodeMirror-overwrite", ""); + }, + + posFromIndex: function(off) { + var lineNo = 0, ch, doc = this.view.doc; + doc.iter(0, doc.size, function(line) { + var sz = line.text.length + 1; + if (sz > off) { ch = off; return true; } + off -= sz; + ++lineNo; }); - return state; - } - function highlightWorker() { - if (frontier >= showingTo) return; - var end = +new Date + options.workTime, state = copyState(mode, getStateBefore(frontier)); - var startFrontier = frontier; - doc.iter(frontier, showingTo, function(line) { - if (frontier >= showingFrom) { // Visible - line.highlight(mode, state, options.tabSize); - line.stateAfter = copyState(mode, state); - } else { - line.process(mode, state, options.tabSize); - line.stateAfter = frontier % 5 == 0 ? copyState(mode, state) : null; - } - ++frontier; - if (+new Date > end) { - startWorker(options.workDelay); - return true; - } + return clipPos(doc, {line: lineNo, ch: ch}); + }, + indexFromPos: function (coords) { + if (coords.line < 0 || coords.ch < 0) return 0; + var index = coords.ch; + this.view.doc.iter(0, coords.line, function (line) { + index += line.text.length + 1; }); - if (showingTo > startFrontier && frontier >= showingFrom) - operation(function() {changes.push({from: startFrontier, to: frontier});})(); - } - function startWorker(time) { - if (frontier < showingTo) - highlight.set(time, highlightWorker); - } - - // Operations are used to wrap changes in such a way that each - // change won't have to update the cursor and display (which would - // be awkward, slow, and error-prone), but instead updates are - // batched and then all combined and executed at once. - function startOperation() { - updateInput = userSelChange = textChanged = null; - changes = []; selectionChanged = false; delayedCallbacks = []; - } - function endOperation() { - if (updateMaxLine) computeMaxLength(); - if (maxLineChanged && !options.lineWrapping) { - var width = measureLine(maxLine, maxLine.text.length).left; - sizer.style.minWidth = (width + 3 + scrollerCutOff) + "px"; - maxLineChanged = false; - } - var newScrollPos, updated; - if (selectionChanged) { - var coords = cursorCoords(sel.inverted ? sel.from : sel.to); - newScrollPos = calculateScrollPos(coords.left, coords.top, coords.left, coords.bottom); - } - if (changes.length || newScrollPos && newScrollPos.scrollTop != null) - updated = updateDisplay(changes, true, newScrollPos && newScrollPos.scrollTop); - if (!updated && selectionChanged) updateSelection(); - if (newScrollPos) scrollCursorIntoView(); - if (selectionChanged) restartBlink(); - - if (focused && (updateInput === true || (updateInput !== false && selectionChanged))) - resetInput(userSelChange); - - var sc = selectionChanged, cbs = delayedCallbacks; // these can be reset by callbacks - delayedCallbacks = null; - if (textChanged) - signal(instance, "change", instance, textChanged); - if (sc) signal(instance, "cursorActivity", instance); - for (var i = 0; i < cbs.length; ++i) cbs[i](instance); - if (updated) signal(instance, "update", instance); - } - var nestedOperation = 0; - function operation(f) { - return function() { - if (!nestedOperation++) startOperation(); - try {var result = f.apply(this, arguments);} - finally {if (!--nestedOperation) endOperation();} - return result; - }; - } + return index; + }, - function compoundChange(f) { - history.startCompound(); - try { return f(); } finally { history.endCompound(); } - } + scrollTo: function(x, y) { + if (x != null) this.display.scrollbarH.scrollLeft = this.display.scroller.scrollLeft = x; + if (y != null) this.display.scrollbarV.scrollTop = this.display.scroller.scrollTop = y; + updateDisplay(this, []); + }, + getScrollInfo: function() { + return {left: this.display.scroller.scrollLeft, top: this.display.scroller.scrollTop, + height: this.display.scroller.scrollHeight, width: this.display.scroller.scrollWidth}; + }, - for (var ext in extensions) - if (extensions.propertyIsEnumerable(ext) && - !instance.propertyIsEnumerable(ext)) - instance[ext] = extensions[ext]; - for (var opt in optionHandlers) - if (optionHandlers.propertyIsEnumerable(opt)) - optionHandlers[opt](instance, options[opt]); - return instance; - } // (end of function CodeMirror) + setSize: function(width, height) { + function interpret(val) { + val = String(val); + return /^\d+$/.test(val) ? val + "px" : val; + } + if (width != null) this.display.wrapper.style.width = interpret(width); + if (height != null) this.display.wrapper.style.height = interpret(height); + this.refresh(); + }, + + on: function(type, f) {on(this, type, f);}, + off: function(type, f) {off(this, type, f);}, + + operation: function(f){return operation(this, f)();}, + compoundChange: function(f){return compoundChange(this, f);}, + + refresh: function(){ + updateDisplay(this, true, this.view.scrollTop); + if (this.display.scrollbarV.scrollHeight > this.view.scrollTop) + this.display.scrollbarV.scrollTop = this.view.scrollTop; + }, + + getInputField: function(){return this.display.input;}, + getWrapperElement: function(){return this.display.wrapper;}, + getScrollerElement: function(){return this.display.scroller;}, + getGutterElement: function(){return this.display.gutters;} + }; + + // OPTION DEFAULTS // The default configuration options. - CodeMirror.defaults = { + var defaults = CodeMirror.defaults = { value: "", mode: null, theme: "default", @@ -2238,12 +2433,11 @@ window.CodeMirror = (function() { lineNumberFormatter: function(integer) { return integer; } }; - var ios = /AppleWebKit/.test(navigator.userAgent) && /Mobile\/\w+/.test(navigator.userAgent); - var mac = ios || /Mac/.test(navigator.platform); - var win = /Win/.test(navigator.platform); + // MODE DEFINITION AND QUERYING // Known modes, by name and by MIME var modes = CodeMirror.modes = {}, mimeModes = CodeMirror.mimeModes = {}; + CodeMirror.defineMode = function(name, mode) { if (!CodeMirror.defaults.mode && name != "null") CodeMirror.defaults.mode = name; if (arguments.length > 2) { @@ -2252,9 +2446,11 @@ window.CodeMirror = (function() { } modes[name] = mode; }; + CodeMirror.defineMIME = function(mime, spec) { mimeModes[mime] = spec; }; + CodeMirror.resolveMode = function(spec) { if (typeof spec == "string" && mimeModes.hasOwnProperty(spec)) spec = mimeModes[spec]; @@ -2263,6 +2459,7 @@ window.CodeMirror = (function() { if (typeof spec == "string") return {name: spec}; else return spec || {name: "null"}; }; + CodeMirror.getMode = function(options, spec) { var spec = CodeMirror.resolveMode(spec); var mfactory = modes[spec.name]; @@ -2275,41 +2472,69 @@ window.CodeMirror = (function() { modeObj.name = spec.name; return modeObj; }; - CodeMirror.listModes = function() { - var list = []; - for (var m in modes) - if (modes.propertyIsEnumerable(m)) list.push(m); - return list; + + CodeMirror.defineMode("null", function() { + return {token: function(stream) {stream.skipToEnd();}}; + }); + CodeMirror.defineMIME("text/plain", "null"); + + var modeExtensions = CodeMirror.modeExtensions = {}; + CodeMirror.extendMode = function(mode, properties) { + var exts = modeExtensions.hasOwnProperty(mode) ? modeExtensions[mode] : (modeExtensions[mode] = {}); + for (var prop in properties) if (properties.hasOwnProperty(prop)) + exts[prop] = properties[prop]; + }; + + // EXTENSIONS + + CodeMirror.defineExtension = function(name, func) { + CodeMirror.prototype[name] = func; }; - CodeMirror.listMIMEs = function() { - var list = []; - for (var m in mimeModes) - if (mimeModes.propertyIsEnumerable(m)) list.push({mime: m, mode: mimeModes[m]}); - return list; + var optionHandlers = CodeMirror.optionHandlers = {}; + CodeMirror.defineOption = function(name, deflt, handler) { + CodeMirror.defaults[name] = deflt; + optionHandlers[name] = handler; }; - var extensions = CodeMirror.extensions = {}; - CodeMirror.defineExtension = function(name, func) { - extensions[name] = func; - }; - var optionHandlers = CodeMirror.optionHandlers = {}; - CodeMirror.defineOption = function(name, deflt, handler) { - CodeMirror.defaults[name] = deflt; - optionHandlers[name] = handler; - }; + // MODE STATE HANDLING + + // Utility functions for working with state. Exported because modes + // sometimes need to do this. + function copyState(mode, state) { + if (state === true) return state; + if (mode.copyState) return mode.copyState(state); + var nstate = {}; + for (var n in state) { + var val = state[n]; + if (val instanceof Array) val = val.concat([]); + nstate[n] = val; + } + return nstate; + } + CodeMirror.copyState = copyState; + + function startState(mode, a1, a2) { + return mode.startState ? mode.startState(a1, a2) : true; + } + CodeMirror.startState = startState; - var modeExtensions = CodeMirror.modeExtensions = {}; - CodeMirror.extendMode = function(mode, properties) { - var exts = modeExtensions.hasOwnProperty(mode) ? modeExtensions[mode] : (modeExtensions[mode] = {}); - for (var prop in properties) if (properties.hasOwnProperty(prop)) - exts[prop] = properties[prop]; + CodeMirror.innerMode = function(mode, state) { + while (mode.innerMode) { + var info = mode.innerMode(state); + state = info.state; + mode = info.mode; + } + return info || {mode: mode, state: state}; }; + // STANDARD COMMANDS + var commands = CodeMirror.commands = { selectAll: function(cm) {cm.setSelection({line: 0, ch: 0}, {line: cm.lineCount() - 1});}, killLine: function(cm) { var from = cm.getCursor(true), to = cm.getCursor(false), sel = !posEq(from, to); - if (!sel && cm.getLine(from.line).length == from.ch) cm.replaceRange("", from, {line: from.line + 1, ch: 0}); + if (!sel && cm.getLine(from.line).length == from.ch) + cm.replaceRange("", from, {line: from.line + 1, ch: 0}); else cm.replaceRange("", from, sel ? to : {line: from.line}); }, deleteLine: function(cm) {var l = cm.getCursor().line; cm.replaceRange("", {line: l, ch: 0}, {line: l});}, @@ -2367,6 +2592,8 @@ window.CodeMirror = (function() { toggleOverwrite: function(cm) {cm.toggleOverwrite();} }; + // STANDARD KEYMAPS + var keyMap = CodeMirror.keyMap = {}; keyMap.basic = { "Left": "goCharLeft", "Right": "goCharRight", "Up": "goLineUp", "Down": "goLineDown", @@ -2402,10 +2629,13 @@ window.CodeMirror = (function() { "Alt-D": "delWordRight", "Alt-Backspace": "delWordLeft", "Ctrl-K": "killLine", "Ctrl-T": "transposeChars" }; + // KEYMAP DISPATCH + function getKeyMap(val) { if (typeof val == "string") return keyMap[val]; else return val; } + function lookupKey(name, extraMap, map, handle, stop) { function lookup(map) { map = getKeyMap(map); @@ -2436,6 +2666,8 @@ window.CodeMirror = (function() { return name == "Ctrl" || name == "Alt" || name == "Shift" || name == "Mod"; } + // FROMTEXTAREA + CodeMirror.fromTextArea = function(textarea, options) { if (!options) options = {}; options.value = textarea.value; @@ -2451,7 +2683,7 @@ window.CodeMirror = (function() { textarea.getAttribute("autofocus") != null && hasFocus == document.body; } - function save() {textarea.value = instance.getValue();} + function save() {textarea.value = cm.getValue();} if (textarea.form) { // Deplorable hack to make the submit method do the right thing. on(textarea.form, "submit", save); @@ -2467,14 +2699,14 @@ window.CodeMirror = (function() { } textarea.style.display = "none"; - var instance = CodeMirror(function(node) { + var cm = CodeMirror(function(node) { textarea.parentNode.insertBefore(node, textarea.nextSibling); }, options); - instance.save = save; - instance.getTextArea = function() { return textarea; }; - instance.toTextArea = function() { + cm.save = save; + cm.getTextArea = function() { return textarea; }; + cm.toTextArea = function() { save(); - textarea.parentNode.removeChild(instance.getWrapperElement()); + textarea.parentNode.removeChild(cm.getWrapperElement()); textarea.style.display = ""; if (textarea.form) { off(textarea.form, "submit", save); @@ -2482,50 +2714,13 @@ window.CodeMirror = (function() { textarea.form.submit = realSubmit; } }; - return instance; + return cm; }; - var gecko = /gecko\/\d{7}/i.test(navigator.userAgent); - var ie = /MSIE \d/.test(navigator.userAgent); - var ie_lt8 = /MSIE [1-7]\b/.test(navigator.userAgent); - var ie_lt9 = /MSIE [1-8]\b/.test(navigator.userAgent); - var quirksMode = ie && document.documentMode == 5; - var webkit = /WebKit\//.test(navigator.userAgent); - var chrome = /Chrome\//.test(navigator.userAgent); - var opera = /Opera\//.test(navigator.userAgent); - var safari = /Apple Computer/.test(navigator.vendor); - var khtml = /KHTML\//.test(navigator.userAgent); - var mac_geLion = /Mac OS X 10\D([7-9]|\d\d)\D/.test(navigator.userAgent); - - // Number of pixels added to scroller and sizer to hide scrollbar - var scrollerCutOff = 30; + // STRING STREAM - // Utility functions for working with state. Exported because modes - // sometimes need to do this. - function copyState(mode, state) { - if (state === true) return state; - if (mode.copyState) return mode.copyState(state); - var nstate = {}; - for (var n in state) { - var val = state[n]; - if (val instanceof Array) val = val.concat([]); - nstate[n] = val; - } - return nstate; - } - CodeMirror.copyState = copyState; - function startState(mode, a1, a2) { - return mode.startState ? mode.startState(a1, a2) : true; - } - CodeMirror.startState = startState; - CodeMirror.innerMode = function(mode, state) { - while (mode.innerMode) { - var info = mode.innerMode(state); - state = info.state; - mode = info.mode; - } - return info || {mode: mode, state: state}; - }; + // Fed to the mode parsers, provides helper functions to make + // parsers more succinct. // The character stream used by a mode's parser. function StringStream(string, tabSize) { @@ -2533,6 +2728,7 @@ window.CodeMirror = (function() { this.string = string; this.tabSize = tabSize || 8; } + StringStream.prototype = { eol: function() {return this.pos >= this.string.length;}, sol: function() {return this.pos == 0;}, @@ -2583,10 +2779,48 @@ window.CodeMirror = (function() { }; CodeMirror.StringStream = StringStream; - function MarkedSpan(from, to, marker) { - this.from = from; this.to = to; this.marker = marker; + // TEXTMARKERS + + function TextMarker(cm, type, style) { + this.lines = []; + this.type = type; + this.cm = cm; + if (style) this.style = style; } + TextMarker.prototype.clear = function() { + startOperation(this.cm); + var min = Infinity, max = -Infinity; + for (var i = 0; i < this.lines.length; ++i) { + var line = this.lines[i]; + var span = getMarkedSpanFor(line.markedSpans, this, true); + if (span.from != null || span.to != null) { + var lineN = lineNo(line); + min = Math.min(min, lineN); max = Math.max(max, lineN); + } + } + if (min != Infinity) regChange(this.cm, min, max + 1); + this.lines.length = 0; + endOperation(this.cm); + }; + + TextMarker.prototype.find = function() { + var from, to; + for (var i = 0; i < this.lines.length; ++i) { + var line = this.lines[i]; + var span = getMarkedSpanFor(line.markedSpans, this); + if (span.from != null || span.to != null) { + var found = lineNo(line); + if (span.from != null) from = {line: found, ch: span.from}; + if (span.to != null) to = {line: found, ch: span.to}; + } + } + if (this.type == "bookmark") return from; + return from && {from: from, to: to}; + }; + + // TEXTMARKER SPANS + function getMarkedSpanFor(spans, marker, del) { if (spans) for (var i = 0; i < spans.length; ++i) { var span = spans[i]; @@ -2701,6 +2935,8 @@ window.CodeMirror = (function() { line.markedSpans = spans; } + // LINE DATA STRUCTURE + // Line objects. These hold state related to a line, including // highlighting info (the styles array). function Line(text, markedSpans, height) { @@ -2708,14 +2944,16 @@ window.CodeMirror = (function() { this.height = height; attachMarkedSpans(this, markedSpans); } + Line.prototype = { - update: function(text, markedSpans, callbacks) { + update: function(text, markedSpans, cm) { this.text = text; this.stateAfter = this.styles = null; detachMarkedSpans(this); attachMarkedSpans(this, markedSpans); - signalLater(this, "change", callbacks); + signalLater(cm, this, "change"); }, + // Run the given mode's parser over a line, update the styles // array, which contains alternating fragments of text and CSS // classes. @@ -2738,6 +2976,9 @@ window.CodeMirror = (function() { } } }, + + // Lightweight form of highlight -- proceed over this line and + // update state, but don't save a style array. process: function(mode, state, tabSize) { var stream = new StringStream(this.text, tabSize); if (this.text == "" && mode.blankLine) mode.blankLine(state); @@ -2746,6 +2987,7 @@ window.CodeMirror = (function() { stream.start = stream.pos; } }, + // Fetch the parser token for a given character. Useful for hacks // that want to inspect the mode state (say, for completion). getTokenAt: function(mode, state, tabSize, ch) { @@ -2760,7 +3002,9 @@ window.CodeMirror = (function() { className: style || null, state: state}; }, + indentation: function(tabSize) {return countColumn(this.text, null, tabSize);}, + // Produces an HTML fragment for the line, taking selection, // marking, and highlighting into account. getContent: function(tabSize, wrapAt, compensateForWrapping) { @@ -2891,13 +3135,15 @@ window.CodeMirror = (function() { } return pre; }, + cleanUp: function() { this.parent = null; detachMarkedSpans(this); } }; - // Data structure that holds the sequence of lines. + // DOCUMENT DATA STRUCTURE + function LeafChunk(lines) { this.lines = lines; this.parent = null; @@ -2907,14 +3153,15 @@ window.CodeMirror = (function() { } this.height = height; } + LeafChunk.prototype = { chunkSize: function() { return this.lines.length; }, - remove: function(at, n, callbacks) { + remove: function(at, n, cm) { for (var i = at, e = at + n; i < e; ++i) { var line = this.lines[i]; this.height -= line.height; line.cleanUp(); - signalLater(line, "delete", callbacks); + signalLater(cm, line, "delete"); } this.lines.splice(at, n); }, @@ -2931,6 +3178,7 @@ window.CodeMirror = (function() { if (op(this.lines[at])) return true; } }; + function BranchChunk(children) { this.children = children; var size = 0, height = 0; @@ -2943,6 +3191,7 @@ window.CodeMirror = (function() { this.height = height; this.parent = null; } + BranchChunk.prototype = { chunkSize: function() { return this.size; }, remove: function(at, n, callbacks) { @@ -3030,7 +3279,14 @@ window.CodeMirror = (function() { } }; - function getLineAt(chunk, n) { + // LINE UTILITIES + + function lineDoc(line) { + for (var d = line.parent; d && d.parent; d = d.parent) {} + return d; + } + + function getLine(chunk, n) { while (!chunk.lines) { for (var i = 0;; ++i) { var child = chunk.children[i], sz = child.chunkSize(); @@ -3040,6 +3296,12 @@ window.CodeMirror = (function() { } return chunk.lines[n]; } + + function updateLineHeight(line, height) { + var diff = height - line.height; + for (var n = line; n; n = n.parent) n.height += diff; + } + function lineNo(line) { if (line.parent == null) return null; var cur = line.parent, no = indexOf(cur.lines, line); @@ -3051,6 +3313,7 @@ window.CodeMirror = (function() { } return no; } + function lineAtHeight(chunk, h) { var n = 0; outer: do { @@ -3069,6 +3332,7 @@ window.CodeMirror = (function() { } return n + i; } + function heightAtLine(chunk, n) { var h = 0; outer: do { @@ -3084,6 +3348,22 @@ window.CodeMirror = (function() { return h; } + function getOrder(line) { + var order = line.order; + if (order == null) order = line.order = bidiOrdering(line.text); + return order; + } + + function lineContent(cm, line, anchorAt) { + if (!line.styles) { + var doc = lineDoc(line); + line.highlight(doc.mode, line.stateAfter = getStateBefore(doc, lineNo(line)), cm.options.tabSize); + } + return line.getContent(cm.options.tabSize, anchorAt, cm.options.lineWrapping); + } + + // HISTORY + // The history object 'chunks' changes that are made close together // and at almost the same time into bigger undoable units. function History() { @@ -3122,6 +3402,8 @@ window.CodeMirror = (function() { } }; + // EVENT OPERATORS + function stopMethod() {e_stop(this);} // Ensure an event has a stop method. function addStop(event) { @@ -3161,6 +3443,8 @@ window.CodeMirror = (function() { return overridden ? e.override[prop] : e[prop]; } + // EVENT HANDLING + function on(emitter, type, f) { if (emitter.addEventListener) emitter.addEventListener(type, f, false); @@ -3172,6 +3456,7 @@ window.CodeMirror = (function() { arr.push(f); } } + function off(emitter, type, f) { if (emitter.removeEventListener) emitter.removeEventListener(type, f, false); @@ -3184,59 +3469,42 @@ window.CodeMirror = (function() { if (arr[i] == f) { arr.splice(i, 1); break; } } } + function signal(emitter, type /*, values...*/) { var arr = emitter._handlers && emitter._handlers[type]; if (!arr) return; var args = Array.prototype.slice.call(arguments, 2); for (var i = 0; i < arr.length; ++i) arr[i].apply(null, args); } - function signalLater(emitter, type, flist /*, values...*/) { + + function signalLater(cm, emitter, type /*, values...*/) { var arr = emitter._handlers && emitter._handlers[type]; if (!arr) return; - var args = Array.prototype.slice.call(arguments, 3); + var args = Array.prototype.slice.call(arguments, 3), flist = cm.curOp && cm.curOp.delayedCallbacks; function bnd(f) {return function(){f.apply(null, args);};}; for (var i = 0; i < arr.length; ++i) - if (flist) flist.push(bnd(arr[i])); else arr[i].apply(null, args); + if (flist) flist.push(bnd(arr[i])); + else arr[i].apply(null, args); } + function hasHandler(emitter, type) { var arr = emitter._handlers && emitter._handlers[type]; return arr && arr.length > 0; } - CodeMirror.on = on; CodeMirror.off = off; CodeMirror.signal = signal; - function Delayed() {this.id = null;} - Delayed.prototype = {set: function(ms, f) {clearTimeout(this.id); this.id = setTimeout(f, ms);}}; + CodeMirror.on = on; CodeMirror.off = off; CodeMirror.signal = signal; - var Pass = CodeMirror.Pass = {toString: function(){return "CodeMirror.Pass";}}; + // MISC UTILITIES - // Detect drag-and-drop - var dragAndDrop = function() { - // There is *some* kind of drag-and-drop support in IE6-8, but I - // couldn't get it to work yet. - if (ie_lt9) return false; - var div = elt('div'); - return "draggable" in div || "dragDrop" in div; - }(); + // Number of pixels added to scroller and sizer to hide scrollbar + var scrollerCutOff = 30; - // Feature-detect whether newlines in textareas are converted to \r\n - var lineSep = function () { - var te = elt("textarea"); - te.value = "foo\nbar"; - if (te.value.indexOf("\r") > -1) return "\r\n"; - return "\n"; - }(); + // Returned or thrown by various protocols to signal 'I'm not + // handling this'. + var Pass = CodeMirror.Pass = {toString: function(){return "CodeMirror.Pass";}}; - // For a reason I have yet to figure out, some browsers disallow - // word wrapping between certain characters *only* if a new inline - // element is started between them. This makes it hard to reliably - // measure the position of things, since that requires inserting an - // extra span. This terribly fragile set of regexps matches the - // character combinations that suffer from this phenomenon on the - // various browsers. - var spanAffectsWrapping = /^$/; // Won't match any two-character string - if (gecko) spanAffectsWrapping = /$'/; - else if (safari) spanAffectsWrapping = /\-[^ \-?]|\?[^ !'\"\),.\-\/:;\?\]\}]/; - else if (chrome) spanAffectsWrapping = /\-[^ \-\.?]|\?[^ \-\.?\]\}:;!'\"\),\/]|[\.!\"#&%\)*+,:;=>\]|\}~][\(\{\[<]|\$'/; + function Delayed() {this.id = null;} + Delayed.prototype = {set: function(ms, f) {clearTimeout(this.id); this.id = setTimeout(f, ms);}}; // Counts the column offset in a string, taking tabs into account. // Used mostly to find indentation. @@ -3268,10 +3536,41 @@ window.CodeMirror = (function() { } else node.select(); } - // Operations on {line, ch} objects. - function posEq(a, b) {return a.line == b.line && a.ch == b.ch;} - function posLess(a, b) {return a.line < b.line || (a.line == b.line && a.ch < b.ch);} - function copyPos(x) {return {line: x.line, ch: x.ch};} + // Used to position the cursor after an undo/redo by finding the + // last edited character. + function editEnd(from, to) { + if (!to) return 0; + if (!from) return to.length; + for (var i = from.length, j = to.length; i >= 0 && j >= 0; --i, --j) + if (from.charAt(i) != to.charAt(j)) break; + return j + 1; + } + + function indexOf(collection, elt) { + if (collection.indexOf) return collection.indexOf(elt); + for (var i = 0, e = collection.length; i < e; ++i) + if (collection[i] == elt) return i; + return -1; + } + + function bind(f) { + var args = Array.prototype.slice.call(arguments, 1); + return function(){return f.apply(null, args);}; + } + + function isWordChar(ch) { + return /\w/.test(ch) || ch.toUpperCase() != ch.toLowerCase(); + } + + function isEmpty(obj) { + var c = 0; + for (var n in obj) if (obj.hasOwnProperty(n) && obj[n]) ++c; + return !c; + } + + var isExtendingChar = /[\u0300-\u036F\u0483-\u0487\u0488-\u0489\u0591-\u05BD\u05BF\u05C1-\u05C2\u05C4-\u05C5\u05C7\u0610-\u061A\u064B-\u065F\u0670\u06D6-\u06DC\u06DF-\u06E4\u06E7-\u06E8\u06EA-\u06ED\uA66F\uA670-\uA672\uA674-\uA67D\uA69F]/; + + // DOM UTILITIES function elt(tag, content, className, style) { var e = document.createElement(tag); @@ -3281,13 +3580,16 @@ window.CodeMirror = (function() { else if (content) for (var i = 0; i < content.length; ++i) e.appendChild(content[i]); return e; } + function removeChildren(e) { e.innerHTML = ""; return e; } + function removeChildrenAndAdd(parent, e) { return removeChildren(parent).appendChild(e); } + function setTextContent(e, str) { if (ie_lt9) { e.innerHTML = ""; @@ -3295,6 +3597,37 @@ window.CodeMirror = (function() { } else e.textContent = str; } + // FEATURE DETECTION + + // Detect drag-and-drop + var dragAndDrop = function() { + // There is *some* kind of drag-and-drop support in IE6-8, but I + // couldn't get it to work yet. + if (ie_lt9) return false; + var div = elt('div'); + return "draggable" in div || "dragDrop" in div; + }(); + + // Feature-detect whether newlines in textareas are converted to \r\n + var lineSep = function () { + var te = elt("textarea"); + te.value = "foo\nbar"; + if (te.value.indexOf("\r") > -1) return "\r\n"; + return "\n"; + }(); + + // For a reason I have yet to figure out, some browsers disallow + // word wrapping between certain characters *only* if a new inline + // element is started between them. This makes it hard to reliably + // measure the position of things, since that requires inserting an + // extra span. This terribly fragile set of regexps matches the + // character combinations that suffer from this phenomenon on the + // various browsers. + var spanAffectsWrapping = /^$/; // Won't match any two-character string + if (gecko) spanAffectsWrapping = /$'/; + else if (safari) spanAffectsWrapping = /\-[^ \-?]|\?[^ !'\"\),.\-\/:;\?\]\}]/; + else if (chrome) spanAffectsWrapping = /\-[^ \-\.?]|\?[^ \-\.?\]\}:;!'\"\),\/]|[\.!\"#&%\)*+,:;=>\]|\}~][\(\{\[<]|\$'/; + var knownScrollbarWidth; function scrollbarWidth(measure) { if (knownScrollbarWidth != null) return knownScrollbarWidth; @@ -3305,26 +3638,6 @@ window.CodeMirror = (function() { return knownScrollbarWidth || 0; } - // Used to position the cursor after an undo/redo by finding the - // last edited character. - function editEnd(from, to) { - if (!to) return 0; - if (!from) return to.length; - for (var i = from.length, j = to.length; i >= 0 && j >= 0; --i, --j) - if (from.charAt(i) != to.charAt(j)) break; - return j + 1; - } - - function indexOf(collection, elt) { - if (collection.indexOf) return collection.indexOf(elt); - for (var i = 0, e = collection.length; i < e; ++i) - if (collection[i] == elt) return i; - return -1; - } - function isWordChar(ch) { - return /\w/.test(ch) || ch.toUpperCase() != ch.toLowerCase(); - } - // See if "".split is the broken IE version, if so, provide an // alternative way to split lines. var splitLines = "\n\nb".split(/\n/).length != 3 ? function(string) { @@ -3356,10 +3669,7 @@ window.CodeMirror = (function() { return range.compareEndPoints("StartToEnd", range) != 0; }; - CodeMirror.defineMode("null", function() { - return {token: function(stream) {stream.skipToEnd();}}; - }); - CodeMirror.defineMIME("text/plain", "null"); + // KEY NAMING var keyNames = {3: "Enter", 8: "Backspace", 9: "Tab", 13: "Enter", 16: "Shift", 17: "Ctrl", 18: "Alt", 19: "Pause", 20: "CapsLock", 27: "Esc", 32: "Space", 33: "PageUp", 34: "PageDown", 35: "End", @@ -3378,6 +3688,8 @@ window.CodeMirror = (function() { for (var i = 1; i <= 12; i++) keyNames[i + 111] = keyNames[i + 63235] = "F" + i; })(); + // BIDI HELPERS + function iterateBidiSections(order, from, to, f) { if (!order) return f(from, to, "ltr"); for (var i = 0; i < order.length; ++i) { @@ -3407,8 +3719,6 @@ window.CodeMirror = (function() { return order[0].level % 2 ? lineLeft(line) : lineRight(line); } - var isExtendingChar = /[\u0300-\u036F\u0483-\u0487\u0488-\u0489\u0591-\u05BD\u05BF\u05C1-\u05C2\u05C4-\u05C5\u05C7\u0610-\u061A\u064B-\u065F\u0670\u06D6-\u06DC\u06DF-\u06E4\u06E7-\u06E8\u06EA-\u06ED\uA66F\uA670-\uA672\uA674-\uA67D\uA69F]/; - // This is somewhat involved. It is needed in order to move // 'visually' through bi-directional text -- i.e., pressing left // should make the cursor go left, even when in RTL text. The @@ -3457,12 +3767,6 @@ window.CodeMirror = (function() { return target < 0 || target > line.text.length ? null : target; } - function getOrder(line) { - var order = line.order; - if (order == null) order = line.order = bidiOrdering(line.text); - return order; - } - // Bidirectional ordering algorithm // See http://unicode.org/reports/tr9/tr9-13.html for the algorithm // that this (partially) implements. @@ -3629,6 +3933,8 @@ window.CodeMirror = (function() { }; })(); + // THE END + CodeMirror.version = "3.0 B"; return CodeMirror; diff --git a/test/test.js b/test/test.js index 3d2d27b287..602c4e9be0 100644 --- a/test/test.js +++ b/test/test.js @@ -110,8 +110,8 @@ testCM("indent", function(cm) { }, {value: "if (x) {\nblah();\n}", indentUnit: 3, indentWithTabs: true, tabSize: 8}); test("core_defaults", function() { - var olddefaults = CodeMirror.defaults, defs = CodeMirror.defaults = {}; - for (var opt in olddefaults) defs[opt] = olddefaults[opt]; + var defsCopy = {}, defs = CodeMirror.defaults; + for (var opt in defs) defsCopy[opt] = defs[opt]; defs.indentUnit = 5; defs.value = "uu"; defs.enterMode = "keep"; @@ -126,7 +126,7 @@ test("core_defaults", function() { eq(cm.getInputField().tabIndex, 55); } finally { - CodeMirror.defaults = olddefaults; + for (var opt in defsCopy) defs[opt] = defsCopy[opt]; place.removeChild(cm.getWrapperElement()); } }); @@ -544,13 +544,14 @@ testCM("scrollVerticallyAndHorizontally", function(cm) { "bottom line visible"); }, {lineNumbers: true}); -testCM("moveV stuck", function(cm) { +testCM("moveVstuck", function(cm) { var lines = byClassName(cm.getWrapperElement(), "CodeMirror-lines")[0].firstChild, h0 = lines.offsetHeight; var val = "fooooooooooooooooooooooooo baaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaar\n"; cm.setValue(val); for (var w = 50;; w += 5) { cm.setSize(w); if (lines.offsetHeight <= 3 * h0) break; + if (w > 500) { return;} } cm.setCursor({line: 0, ch: val.length - 1}); cm.moveV(-1, "line"); From 0a706b399448ba422fd2d4b7f28d417ea4ac1ad8 Mon Sep 17 00:00:00 2001 From: Marijn Haverbeke Date: Thu, 13 Sep 2012 14:04:04 +0200 Subject: [PATCH 0115/5780] Fix tests --- lib/codemirror.js | 2 +- test/test.js | 1 - 2 files changed, 1 insertion(+), 2 deletions(-) diff --git a/lib/codemirror.js b/lib/codemirror.js index 94fa8366dc..9b870b892a 100644 --- a/lib/codemirror.js +++ b/lib/codemirror.js @@ -1566,7 +1566,7 @@ window.CodeMirror = (function() { } function undo(cm) { var hist = cm.view.doc.history; - unredoHelper + unredoHelper(cm, hist.done, hist.undone); } function redo(cm) { var hist = cm.view.doc.history; diff --git a/test/test.js b/test/test.js index 602c4e9be0..ef803ea06c 100644 --- a/test/test.js +++ b/test/test.js @@ -551,7 +551,6 @@ testCM("moveVstuck", function(cm) { for (var w = 50;; w += 5) { cm.setSize(w); if (lines.offsetHeight <= 3 * h0) break; - if (w > 500) { return;} } cm.setCursor({line: 0, ch: val.length - 1}); cm.moveV(-1, "line"); From 62cd5467563ac2252fe6ef5b6a79b943e7ea1c26 Mon Sep 17 00:00:00 2001 From: "Ahmad M. Zawawi" Date: Fri, 14 Sep 2012 00:33:26 -0700 Subject: [PATCH 0116/5780] Fixed delete selection regression --- lib/codemirror.js | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/lib/codemirror.js b/lib/codemirror.js index 9b870b892a..42334877c5 100644 --- a/lib/codemirror.js +++ b/lib/codemirror.js @@ -2306,7 +2306,7 @@ window.CodeMirror = (function() { deleteH: operation(null, function(dir, unit) { var sel = this.view.sel; - if (!posEq(sel.from, sel.to)) replaceRange(cm, "", sel.from, sel.to); + if (!posEq(sel.from, sel.to)) replaceRange(this, "", sel.from, sel.to); else replaceRange(this, "", sel.from, findPosH(this, dir, unit, false)); this.curOp.userSelChange = true; }), From b105536af56bdbc15a20793052227ebe90865495 Mon Sep 17 00:00:00 2001 From: Marijn Haverbeke Date: Fri, 14 Sep 2012 12:00:42 +0200 Subject: [PATCH 0117/5780] [tests] Fix handling of expected failures --- test/driver.js | 8 ++++++-- test/index.html | 3 ++- 2 files changed, 8 insertions(+), 3 deletions(-) diff --git a/test/driver.js b/test/driver.js index 43ffb1873e..7f9dfd0ae3 100644 --- a/test/driver.js +++ b/test/driver.js @@ -83,11 +83,11 @@ function runTests(callback) { } } } + var threw = false; try { var message = test.func(); - if (expFail) callback("fail", test.name, message); - else callback("ok", test.name, message); } catch(e) { + threw = true; if (expFail) callback("expected", test.name); else if (e instanceof Failure) callback("fail", test.name, e.message); else { @@ -95,6 +95,10 @@ function runTests(callback) { callback("error", test.name, e.toString() + (pos ? " (" + pos[1] + ":" + pos[2] + ")" : "")); } } + if (!threw) { + if (expFail) callback("fail", test.name, message || "expected failure, but succeeded"); + else callback("ok", test.name, message); + } if (!quit) { // Run next test var delay = 0; totalTime += (+new Date) - startTime; diff --git a/test/index.html b/test/index.html index 0e00720ce6..d3042dace1 100644 --- a/test/index.html +++ b/test/index.html @@ -22,6 +22,7 @@ font-weight: bold; white-space: pre; } + .CodeMirror { border: 1px solid black; } @@ -159,7 +160,7 @@

    CodeMirror: Test Suite

    } if (verbose && !customMessage) customMessage = message; setStatus(message, type); - if (customMessage.length > 0) { + if (customMessage && customMessage.length > 0) { addOutput(name, type, customMessage); } } From 39f27de9daf320f2bb10e9d4c5f1e790517d1f39 Mon Sep 17 00:00:00 2001 From: Marijn Haverbeke Date: Fri, 14 Sep 2012 12:00:59 +0200 Subject: [PATCH 0118/5780] Fix tests on IE7/8 --- lib/codemirror.js | 5 +++-- 1 file changed, 3 insertions(+), 2 deletions(-) diff --git a/lib/codemirror.js b/lib/codemirror.js index 42334877c5..2008d6ab92 100644 --- a/lib/codemirror.js +++ b/lib/codemirror.js @@ -29,7 +29,8 @@ window.CodeMirror = (function() { // CONSTRUCTOR function CodeMirror(place, options) { - if (!this || this == window) return new CodeMirror(place, options); + if (!(this instanceof CodeMirror)) return new CodeMirror(place, options, true); + this.options = options = options || {}; // Determine effective options based on given values and defaults. for (var opt in defaults) if (!options.hasOwnProperty(opt) && defaults.hasOwnProperty(opt)) @@ -123,7 +124,7 @@ window.CodeMirror = (function() { d.wrapper = elt("div", [d.gutters, d.inputDiv, d.scrollbarH, d.scrollbarV, d.scrollbarFiller, d.scroller], "CodeMirror"); // Work around IE7 z-index bug - if (ie_lt8) d.gutters.style.zIndex = -1; + if (ie_lt8) { d.gutters.style.zIndex = -1; d.scroller.style.paddingRight = 0; } if (place.appendChild) place.appendChild(d.wrapper); else place(d.wrapper); // Needed to hide big blue blinking cursor on Mobile Safari From 1541fce2197e9beb0aa7917478bae38a91e98823 Mon Sep 17 00:00:00 2001 From: Marijn Haverbeke Date: Fri, 14 Sep 2012 13:06:04 +0200 Subject: [PATCH 0119/5780] Rename del(Word|Char)(Left|Right) commands to (Before|After) Closes #808 --- keymap/vim.js | 12 ++++++------ lib/codemirror.js | 22 +++++++++++----------- 2 files changed, 17 insertions(+), 17 deletions(-) diff --git a/keymap/vim.js b/keymap/vim.js index b6a41850ef..429fb4e161 100644 --- a/keymap/vim.js +++ b/keymap/vim.js @@ -145,7 +145,7 @@ if (cur.line != cm.lineCount()) { CodeMirror.commands.goLineEnd(cm); cm.replaceSelection(" ", "end"); - CodeMirror.commands.delCharRight(cm); + CodeMirror.commands.delCharAfter(cm); } } function delTillMark(cm, cHar) { @@ -274,7 +274,7 @@ "S": function (cm) { countTimes(function (_cm) { - CodeMirror.commands.delCharRight(_cm); + CodeMirror.commands.delCharAfter(_cm); })(cm); enterInsertMode(cm); }, @@ -341,7 +341,7 @@ "Left": "goColumnLeft", "Right": "goColumnRight", "Down": "goLineDown", "Up": "goLineUp", "Backspace": "goCharLeft", "Space": "goCharRight", - "X": function(cm) {CodeMirror.commands.delCharRight(cm);}, + "X": function(cm) {CodeMirror.commands.delCharAfter(cm);}, "P": function(cm) { var cur = cm.getCursor().line; if (buf!= "") { @@ -349,7 +349,7 @@ cm.replaceRange(buf, cm.getCursor()); } }, - "Shift-X": function(cm) {CodeMirror.commands.delCharLeft(cm);}, + "Shift-X": function(cm) {CodeMirror.commands.delCharBefore(cm);}, "Shift-J": function(cm) {joinLineNext(cm);}, "Shift-P": function(cm) { var cur = cm.getCursor().line; @@ -434,14 +434,14 @@ CodeMirror.keyMap["vim-prefix-c"] = { "B": function (cm) { - countTimes("delWordLeft")(cm); + countTimes("delWordBefore")(cm); enterInsertMode(cm); }, "C": function (cm) { iterTimes(function (i, last) { CodeMirror.commands.deleteLine(cm); if (i) { - CodeMirror.commands.delCharRight(cm); + CodeMirror.commands.delCharAfter(cm); if (last) CodeMirror.commands.deleteLine(cm); } }); diff --git a/lib/codemirror.js b/lib/codemirror.js index 2008d6ab92..67bd91a7e9 100644 --- a/lib/codemirror.js +++ b/lib/codemirror.js @@ -8,7 +8,7 @@ window.CodeMirror = (function() { // BROWSER SNIFFING - // Crude, but necessary to handle a number of hard-to-feature detect + // Crude, but necessary to handle a number of hard-to-feature-detect // bugs and behavior differences. var gecko = /gecko\/\d{7}/i.test(navigator.userAgent); var ie = /MSIE \d/.test(navigator.userAgent); @@ -2568,10 +2568,10 @@ window.CodeMirror = (function() { goColumnRight: function(cm) {cm.moveH(1, "column");}, goWordLeft: function(cm) {cm.moveH(-1, "word");}, goWordRight: function(cm) {cm.moveH(1, "word");}, - delCharLeft: function(cm) {cm.deleteH(-1, "char");}, - delCharRight: function(cm) {cm.deleteH(1, "char");}, - delWordLeft: function(cm) {cm.deleteH(-1, "word");}, - delWordRight: function(cm) {cm.deleteH(1, "word");}, + delCharBefore: function(cm) {cm.deleteH(-1, "char");}, + delCharAfter: function(cm) {cm.deleteH(1, "char");}, + delWordBefore: function(cm) {cm.deleteH(-1, "word");}, + delWordAfter: function(cm) {cm.deleteH(1, "word");}, indentAuto: function(cm) {cm.indentSelection("smart");}, indentMore: function(cm) {cm.indentSelection("add");}, indentLess: function(cm) {cm.indentSelection("subtract");}, @@ -2599,7 +2599,7 @@ window.CodeMirror = (function() { keyMap.basic = { "Left": "goCharLeft", "Right": "goCharRight", "Up": "goLineUp", "Down": "goLineDown", "End": "goLineEnd", "Home": "goLineStartSmart", "PageUp": "goPageUp", "PageDown": "goPageDown", - "Delete": "delCharRight", "Backspace": "delCharLeft", "Tab": "defaultTab", "Shift-Tab": "indentAuto", + "Delete": "delCharAfter", "Backspace": "delCharBefore", "Tab": "defaultTab", "Shift-Tab": "indentAuto", "Enter": "newlineAndIndent", "Insert": "toggleOverwrite" }; // Note that the save and find-related commands aren't defined by @@ -2608,7 +2608,7 @@ window.CodeMirror = (function() { "Ctrl-A": "selectAll", "Ctrl-D": "deleteLine", "Ctrl-Z": "undo", "Shift-Ctrl-Z": "redo", "Ctrl-Y": "redo", "Ctrl-Home": "goDocStart", "Alt-Up": "goDocStart", "Ctrl-End": "goDocEnd", "Ctrl-Down": "goDocEnd", "Ctrl-Left": "goWordLeft", "Ctrl-Right": "goWordRight", "Alt-Left": "goLineStart", "Alt-Right": "goLineEnd", - "Ctrl-Backspace": "delWordLeft", "Ctrl-Delete": "delWordRight", "Ctrl-S": "save", "Ctrl-F": "find", + "Ctrl-Backspace": "delWordBefore", "Ctrl-Delete": "delWordAfter", "Ctrl-S": "save", "Ctrl-F": "find", "Ctrl-G": "findNext", "Shift-Ctrl-G": "findPrev", "Shift-Ctrl-F": "replace", "Shift-Ctrl-R": "replaceAll", "Ctrl-[": "indentLess", "Ctrl-]": "indentMore", fallthrough: "basic" @@ -2616,8 +2616,8 @@ window.CodeMirror = (function() { keyMap.macDefault = { "Cmd-A": "selectAll", "Cmd-D": "deleteLine", "Cmd-Z": "undo", "Shift-Cmd-Z": "redo", "Cmd-Y": "redo", "Cmd-Up": "goDocStart", "Cmd-End": "goDocEnd", "Cmd-Down": "goDocEnd", "Alt-Left": "goWordLeft", - "Alt-Right": "goWordRight", "Cmd-Left": "goLineStart", "Cmd-Right": "goLineEnd", "Alt-Backspace": "delWordLeft", - "Ctrl-Alt-Backspace": "delWordRight", "Alt-Delete": "delWordRight", "Cmd-S": "save", "Cmd-F": "find", + "Alt-Right": "goWordRight", "Cmd-Left": "goLineStart", "Cmd-Right": "goLineEnd", "Alt-Backspace": "delWordBefore", + "Ctrl-Alt-Backspace": "delWordAfter", "Alt-Delete": "delWordAfter", "Cmd-S": "save", "Cmd-F": "find", "Cmd-G": "findNext", "Shift-Cmd-G": "findPrev", "Cmd-Alt-F": "replace", "Shift-Cmd-Alt-F": "replaceAll", "Cmd-[": "indentLess", "Cmd-]": "indentMore", fallthrough: ["basic", "emacsy"] @@ -2626,8 +2626,8 @@ window.CodeMirror = (function() { keyMap.emacsy = { "Ctrl-F": "goCharRight", "Ctrl-B": "goCharLeft", "Ctrl-P": "goLineUp", "Ctrl-N": "goLineDown", "Alt-F": "goWordRight", "Alt-B": "goWordLeft", "Ctrl-A": "goLineStart", "Ctrl-E": "goLineEnd", - "Ctrl-V": "goPageUp", "Shift-Ctrl-V": "goPageDown", "Ctrl-D": "delCharRight", "Ctrl-H": "delCharLeft", - "Alt-D": "delWordRight", "Alt-Backspace": "delWordLeft", "Ctrl-K": "killLine", "Ctrl-T": "transposeChars" + "Ctrl-V": "goPageUp", "Shift-Ctrl-V": "goPageDown", "Ctrl-D": "delCharAfter", "Ctrl-H": "delCharBefore", + "Alt-D": "delWordAfter", "Alt-Backspace": "delWordBefore", "Ctrl-K": "killLine", "Ctrl-T": "transposeChars" }; // KEYMAP DISPATCH From dc69da1a2dfd4d931dfa939dc2f0eb1d99c88191 Mon Sep 17 00:00:00 2001 From: Marijn Haverbeke Date: Fri, 14 Sep 2012 13:12:02 +0200 Subject: [PATCH 0120/5780] Fix another bug introduced by the giant refactor --- lib/codemirror.js | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/lib/codemirror.js b/lib/codemirror.js index 67bd91a7e9..79448f18e5 100644 --- a/lib/codemirror.js +++ b/lib/codemirror.js @@ -1653,7 +1653,7 @@ window.CodeMirror = (function() { if (typeof lines[i] != "string") lines[i] = lines[i].text; var changeObj = {from: from, to: to, text: lines}; if (cm.curOp.textChanged) { - for (var cur = cm.operaton.textChanged; cur.next; cur = cur.next) {} + for (var cur = cm.curOp.textChanged; cur.next; cur = cur.next) {} cur.next = changeObj; } else cm.curOp.textChanged = changeObj; } From c4ff133ec1a48e00de8afce86f202b118fe9b5c3 Mon Sep 17 00:00:00 2001 From: Marijn Haverbeke Date: Fri, 14 Sep 2012 13:18:40 +0200 Subject: [PATCH 0121/5780] Add clientWidth and clientHeight to getScrollInfo Since these no longer follow directly from scroller.clientHeight/Width. Use these to ensure messages for current line are scrolled into view in demo/widget.html. Closes #802 --- demo/widget.html | 4 ++++ doc/manual.html | 7 ++++--- lib/codemirror.js | 6 ++++-- 3 files changed, 12 insertions(+), 5 deletions(-) diff --git a/demo/widget.html b/demo/widget.html index a2c0d3294c..9c40309be1 100644 --- a/demo/widget.html +++ b/demo/widget.html @@ -39,6 +39,10 @@

    CodeMirror: Inline Widget Demo

    widgets.push(editor.addLineWidget(err.line - 1, msg, {coverGutter: true, noHScroll: true})); } }); + var info = editor.getScrollInfo(); + var after = editor.charCoords({line: editor.getCursor().line + 1, ch: 0}, "local").top; + if (info.top + info.clientHeight < after) + editor.scrollTo(null, after - info.clientHeight + 3); } window.onload = function() { diff --git a/doc/manual.html b/doc/manual.html index fc94cdbb2e..c1ca348761 100644 --- a/doc/manual.html +++ b/doc/manual.html @@ -555,9 +555,10 @@

    Programming API

    arguments may be left as null or undefined to have no effect.
    getScrollInfo()
    -
    Get an {left, top, width, height} object that - represents the current scroll position and scrollable area size - of the editor.
    +
    Get an {left, top, width, height, clientWidth, + clientHeight} object that represents the current scroll + position, the size of the scrollable area, and the size of the + visible area (minus scrollbars).
    setOption(option, value)
    Change the configuration of the editor. option diff --git a/lib/codemirror.js b/lib/codemirror.js index 79448f18e5..e26973ee11 100644 --- a/lib/codemirror.js +++ b/lib/codemirror.js @@ -2368,8 +2368,10 @@ window.CodeMirror = (function() { updateDisplay(this, []); }, getScrollInfo: function() { - return {left: this.display.scroller.scrollLeft, top: this.display.scroller.scrollTop, - height: this.display.scroller.scrollHeight, width: this.display.scroller.scrollWidth}; + var scroller = this.display.scroller, co = scrollerCutOff; + return {left: scroller.scrollLeft, top: scroller.scrollTop, + height: scroller.scrollHeight - co, width: scroller.scrollWidth - co, + clientHeight: scroller.clientHeight - co, clientWidth: scroller.clientWidth - co}; }, setSize: function(width, height) { From 36f8cc37872a24d53aa7ccd5e9dafcbaca67e0be Mon Sep 17 00:00:00 2001 From: Marijn Haverbeke Date: Fri, 14 Sep 2012 13:56:35 +0200 Subject: [PATCH 0122/5780] Force scrolling when drag-select doesn't move selection Line widgets could make drag-select break before. Closes #800 --- lib/codemirror.js | 27 +++++++++++++++++++++------ 1 file changed, 21 insertions(+), 6 deletions(-) diff --git a/lib/codemirror.js b/lib/codemirror.js index e26973ee11..99118bb401 100644 --- a/lib/codemirror.js +++ b/lib/codemirror.js @@ -1204,7 +1204,7 @@ window.CodeMirror = (function() { setSelectionUser(cm, word.from, word.to); } else { lastClick = {time: now, pos: start}; } - var last = start, going; + var last = start; if (cm.options.dragDrop && dragAndDrop && !cm.options.readOnly && !posEq(sel.from, sel.to) && !posLess(start, sel.from) && !posLess(sel.to, start) && type == "single") { var dragEnd = operation(cm, function(e2) { @@ -1245,21 +1245,37 @@ window.CodeMirror = (function() { } } + var editorSize = display.wrapper.getBoundingClientRect(); + // Used to ensure timeout re-tries don't fire when another extend + // happened in the meantime (clearTimeout isn't reliable -- at + // least on Chrome, the timeouts still happen even when cleared, + // if the clear happens after their scheduled firing time). + var counter = 0; + function extend(e) { + var curCount = ++counter; var cur = posFromMouse(cm, e, true); - if (cur && !posEq(cur, last)) { + if (!cur) return; + if (!posEq(cur, last)) { if (!view.focused) onFocus(cm); last = cur; doSelect(cur); cm.curOp.updateInput = false; var visible = visibleLines(display, doc); if (cur.line >= visible.to || cur.line < visible.from) - going = setTimeout(operation(cm, function(){extend(e);}), 150); + setTimeout(operation(cm, function(){if (counter == curCount) extend(e);}), 150); + } else { + var outside = e.clientY < editorSize.top ? -20 : e.clientY > editorSize.bottom ? 20 : 0; + if (outside) setTimeout(operation(cm, function() { + if (counter != curCount) return; + display.scroller.scrollTop += outside; + extend(e); + }), 50); } } function done(e) { - clearTimeout(going); + counter = Infinity; var cur = posFromMouse(cm, e); if (cur) doSelect(cur); e_preventDefault(e); @@ -1268,9 +1284,8 @@ window.CodeMirror = (function() { off(document, "mousemove", move); off(document, "mouseup", up); } - + var move = operation(cm, function(e) { - clearTimeout(going); e_preventDefault(e); if (!ie && !e_button(e)) done(e); else extend(e); From 283c26f47f0b445dd818dcc0cf2ed3513ea82262 Mon Sep 17 00:00:00 2001 From: Marijn Haverbeke Date: Fri, 14 Sep 2012 15:04:56 +0200 Subject: [PATCH 0123/5780] [tests] Clean up and sanitize mode_test driver --- test/mode_test.js | 71 ++++++++++++++++++++--------------------------- 1 file changed, 30 insertions(+), 41 deletions(-) diff --git a/test/mode_test.js b/test/mode_test.js index 8d9df65e6c..f2459b432e 100644 --- a/test/mode_test.js +++ b/test/mode_test.js @@ -48,25 +48,24 @@ ModeTest.testMode = function(name, text, expected, modeName, modeOptions, expect } -ModeTest.compare = function (text, arguments, mode) { +ModeTest.compare = function (text, expected, mode) { var expectedOutput = []; - for (var i = 0; i < arguments.length; i += 2) { - arguments[i] = (arguments[i] != null ? arguments[i].split(' ').sort().join(' ') : arguments[i]); - expectedOutput.push([arguments[i],arguments[i + 1]]); + for (var i = 0; i < expected.length; i += 2) { + var sty = expected[i]; + if (sty && sty.indexOf(" ")) sty = sty.split(' ').sort().join(' '); + expectedOutput.push(sty, expected[i + 1]); } - var observedOutput = ModeTest.highlight(text, mode) + var observedOutput = ModeTest.highlight(text, mode); var pass, passStyle = ""; - if (expectedOutput.length > 0) { - pass = ModeTest.highlightOutputsEqual(expectedOutput, observedOutput); - passStyle = pass ? 'mt-pass' : 'mt-fail'; - ModeTest.passes += pass ? 1 : 0; - } + pass = ModeTest.highlightOutputsEqual(expectedOutput, observedOutput); + passStyle = pass ? 'mt-pass' : 'mt-fail'; + ModeTest.passes += pass ? 1 : 0; var s = ''; - if (pass || expectedOutput.length == 0) { + if (pass) { s += '
    '; s += '
    ' + ModeTest.htmlEscape(text) + '
    '; s += '
    '; @@ -102,36 +101,32 @@ ModeTest.highlight = function(string, mode) { var state = mode.startState() var lines = string.replace(/\r\n/g,'\n').split('\n'); - var output = []; + var st = [], pos = 0; for (var i = 0; i < lines.length; ++i) { - var line = lines[i]; + var line = lines[i], newLine = true; var stream = new CodeMirror.StringStream(line); if (line == "" && mode.blankLine) mode.blankLine(state); - var pos = 0; - var st = []; /* Start copied code from CodeMirror.highlight */ while (!stream.eol()) { var style = mode.token(stream, state), substr = stream.current(); + if (style && style.indexOf(" ") > -1) style = style.split(' ').sort().join(' '); + stream.start = stream.pos; - if (pos && st[pos-1] == style) { - st[pos-2] += substr; + if (pos && st[pos-2] == style && !newLine) { + st[pos-1] += substr; } else if (substr) { - st[pos++] = substr; st[pos++] = style; + st[pos++] = style; st[pos++] = substr; } // Give up when line is ridiculously long if (stream.pos > 5000) { - st[pos++] = this.text.slice(stream.pos); st[pos++] = null; + st[pos++] = null; st[pos++] = this.text.slice(stream.pos); break; } - } - /* End copied code from CodeMirror.highlight */ - for (var x = 0; x < st.length; x += 2) { - st[x + 1] = (st[x + 1] != null ? st[x + 1].split(' ').sort().join(' ') : st[x + 1]); - output.push([st[x + 1], st[x]]); + newLine = false; } } - return output; + return st; } /** @@ -144,14 +139,10 @@ ModeTest.highlight = function(string, mode) { * @return boolean; true iff outputs equal */ ModeTest.highlightOutputsEqual = function(o1, o2) { - var eq = (o1.length == o2.length); - if (eq) { - for (var j in o1) { - eq = eq && - o1[j].length == 2 && o1[j][0] == o2[j][0] && o1[j][1] == o2[j][1]; - } - } - return eq; + if (o1.length != o2.length) return false; + for (var i = 0; i < o1.length; ++i) + if (o1[i] != o2[i]) return false; + return true; } /** @@ -165,20 +156,18 @@ ModeTest.highlightOutputsEqual = function(o1, o2) { ModeTest.prettyPrintOutputTable = function(output) { var s = ''; s += ''; - for (var i = 0; i < output.length; ++i) { - var token = output[i]; + for (var i = 0; i < output.length; i += 2) { + var style = output[i], val = output[i+1]; s += ''; } s += ''; - for (var i = 0; i < output.length; ++i) { - var token = output[i]; - s += - ''; + for (var i = 0; i < output.length; i += 2) { + s += ''; } s += '
    ' + - '' + - ModeTest.htmlEscape(token[1]).replace(/ /g,'·') + + '' + + ModeTest.htmlEscape(val).replace(/ /g,'·') + '' + '
    ' + token[0] + '' + output[i] + '
    '; return s; From 7e00007c4a42dc1227584b1e18c095540d2b57b9 Mon Sep 17 00:00:00 2001 From: Marijn Haverbeke Date: Fri, 14 Sep 2012 15:06:40 +0200 Subject: [PATCH 0124/5780] Move XQuery tests to new framework They were completely broken due to really fragile use of html selectors. Should work much better now in test framework. Closes #796 --- mode/xquery/test.js | 77 ++++++++++++++++ mode/xquery/test/index.html | 27 ------ mode/xquery/test/testBase.js | 42 --------- mode/xquery/test/testEmptySequenceKeyword.js | 16 ---- mode/xquery/test/testMultiAttr.js | 16 ---- mode/xquery/test/testNamespaces.js | 91 ------------------- .../xquery/test/testProcessingInstructions.js | 16 ---- mode/xquery/test/testQuotes.js | 19 ---- test/index.html | 2 + 9 files changed, 79 insertions(+), 227 deletions(-) create mode 100644 mode/xquery/test.js delete mode 100644 mode/xquery/test/index.html delete mode 100644 mode/xquery/test/testBase.js delete mode 100644 mode/xquery/test/testEmptySequenceKeyword.js delete mode 100644 mode/xquery/test/testMultiAttr.js delete mode 100644 mode/xquery/test/testNamespaces.js delete mode 100644 mode/xquery/test/testProcessingInstructions.js delete mode 100644 mode/xquery/test/testQuotes.js diff --git a/mode/xquery/test.js b/mode/xquery/test.js new file mode 100644 index 0000000000..23ab3d1e3e --- /dev/null +++ b/mode/xquery/test.js @@ -0,0 +1,77 @@ +// Initiate ModeTest and set defaults +var MT = ModeTest; +MT.modeName = "xquery"; +MT.modeOptions = {}; + +MT.testMode("eviltest", + 'xquery version "1.0-ml";\ + (: this is\ + : a \ + "comment" :)\ + let $let := <x attr="value">"test"<func>function() $var {function()} {$var}</func></x>\ + let $joe:=1\ + return element element {\ + attribute attribute { 1 },\ + element test { 'a' }, \ + attribute foo { "bar" },\ + fn:doc()[ foo/@bar eq $let ],\ + //x } \ + \ + (: a more \'evil\' test :)\ + (: Modified Blakeley example (: with nested comment :) ... :)\ + declare private function local:declare() {()};\ + declare private function local:private() {()};\ + declare private function local:function() {()};\ + declare private function local:local() {()};\ + let $let := <let>let $let := "let"</let>\ + return element element {\ + attribute attribute { try { xdmp:version() } catch($e) { xdmp:log($e) } },\ + attribute fn:doc { "bar" castable as xs:string },\ + element text { text { "text" } },\ + fn:doc()[ child::eq/(@bar | attribute::attribute) eq $let ],\ + //fn:doc\ + }', ["keyword","xquery",null," ","keyword","version",null," ","variable",""1","keyword",".","atom","0","keyword","-","variable","ml"","def variable",";",null," ","comment","(: this is : a \"comment\" :)",null," ","keyword","let",null," ","variable","$let",null," ","keyword",":=",null," ","variable","<x",null," ","variable","attr","keyword","=","variable",""value">"test"<func>","def variable",";function","","()",null," ","variable","$var",null," ","","{","keyword","function","","()}",null," ","","{","variable","$var","","}","variable","<","keyword","/","variable","func><","keyword","/","variable","x>",null," ","keyword","let",null," ","variable","$joe","keyword",":=","atom","1",null," ","keyword","return",null," ","keyword","element",null," ","variable","element",null," ","","{",null," ","keyword","attribute",null," ","variable","attribute",null," ","","{",null," ","atom","1",null," ","","},",null," ","keyword","element",null," ","variable","test",null," ","","{",null," ","variable","'a'",null," ","","},",null," ","keyword","attribute",null," ","variable","foo",null," ","","{",null," ","variable",""bar"",null," ","","},",null," ","def variable","fn:doc","","()[",null," ","variable","foo","keyword","/","variable","@bar",null," ","keyword","eq",null," ","variable","$let",null," ","","],",null," ","keyword","//","variable","x",null," ","","}",null," ","comment","(: a more 'evil' test :)",null," ","comment","(: Modified Blakeley example (: with nested comment :) ... :)",null," ","keyword","declare",null," ","keyword","private",null," ","keyword","function",null," ","def variable","local:declare","","()",null," ","","{()}","variable",";",null," ","keyword","declare",null," ","keyword","private",null," ","keyword","function",null," ","def variable","local:private","","()",null," ","","{()}","variable",";",null," ","keyword","declare",null," ","keyword","private",null," ","keyword","function",null," ","def variable","local:function","","()",null," ","","{()}","variable",";",null," ","keyword","declare",null," ","keyword","private",null," ","keyword","function",null," ","def variable","local:local","","()",null," ","","{()}","variable",";",null," ","keyword","let",null," ","variable","$let",null," ","keyword",":=",null," ","variable","<let>let",null," ","variable","$let",null," ","keyword",":=",null," ","variable",""let"<","keyword","/let","variable",">",null," ","keyword","return",null," ","keyword","element",null," ","variable","element",null," ","","{",null," ","keyword","attribute",null," ","variable","attribute",null," ","","{",null," ","keyword","try",null," ","","{",null," ","def variable","xdmp:version","","()",null," ","","}",null," ","keyword","catch","","(","variable","$e","",")",null," ","","{",null," ","def variable","xdmp:log","","(","variable","$e","",")",null," ","","}",null," ","","},",null," ","keyword","attribute",null," ","variable","fn:doc",null," ","","{",null," ","variable",""bar"",null," ","variable","castable",null," ","keyword","as",null," ","atom","xs:string",null," ","","},",null," ","keyword","element",null," ","variable","text",null," ","","{",null," ","keyword","text",null," ","","{",null," ","variable",""text"",null," ","","}",null," ","","},",null," ","def variable","fn:doc","","()[",null," ","qualifier","child::","variable","eq","keyword","/","","(","variable","@bar",null," ","keyword","|",null," ","qualifier","attribute::","variable","attribute","",")",null," ","keyword","eq",null," ","variable","$let",null," ","","],",null," ","keyword","//","variable","fn:doc",null," ","","}"]); + +MT.testMode("testEmptySequenceKeyword", + '"foo" instance of empty-sequence()', + ["string","\"foo\"",null," ","keyword","instance",null," ","keyword","of",null," ","keyword","empty-sequence","","()"]); + + +MT.testMode("testMultiAttr", + '

    hello world

    ', + ["tag","

    ","variable","hello",null," ","variable","world","tag","

    "]); + +MT.testMode("test namespaced variable", + 'declare namespace e = "http://example.com/ANamespace";\ +declare variable $e:exampleComThisVarIsNotRecognized as element(*) external;', + ["keyword","declare",null," ","keyword","namespace",null," ","variable","e",null," ","keyword","=",null," ","string","\"http://example.com/ANamespace\"","variable",";declare",null," ","keyword","variable",null," ","variable","$e:exampleComThisVarIsNotRecognized",null," ","keyword","as",null," ","keyword","element","","(","keyword","*","",")",null," ","variable","external;"]); + +MT.testMode("test EQName variable", + 'declare variable $"http://www.example.com/ns/my":var := 12;\ +{$"http://www.example.com/ns/my":var}', + ["keyword","declare",null," ","keyword","variable",null," ","variable","$\"http://www.example.com/ns/my\":var",null," ","keyword",":=",null," ","atom","12","variable",";","tag","","","{","variable","$\"http://www.example.com/ns/my\":var","","}","tag",""]); + +MT.testMode("test EQName function", + 'declare function "http://www.example.com/ns/my":fn ($a as xs:integer) as xs:integer {\ + $a + 2\ +};\ +{"http://www.example.com/ns/my":fn(12)}', + ["keyword","declare",null," ","keyword","function",null," ","def variable","\"http://www.example.com/ns/my\":fn",null," ","","(","variable","$a",null," ","keyword","as",null," ","atom","xs:integer","",")",null," ","keyword","as",null," ","atom","xs:integer",null," ","","{",null," ","variable","$a",null," ","keyword","+",null," ","atom","2","","}","variable",";","tag","","","{","def variable","\"http://www.example.com/ns/my\":fn","","(","atom","12","",")}","tag",""]); + +MT.testMode("test EQName function with single quotes", + 'declare function \'http://www.example.com/ns/my\':fn ($a as xs:integer) as xs:integer {\ + $a + 2\ +};\ +{\'http://www.example.com/ns/my\':fn(12)}', + ["keyword","declare",null," ","keyword","function",null," ","def variable","'http://www.example.com/ns/my':fn",null," ","","(","variable","$a",null," ","keyword","as",null," ","atom","xs:integer","",")",null," ","keyword","as",null," ","atom","xs:integer",null," ","","{",null," ","variable","$a",null," ","keyword","+",null," ","atom","2","","}","variable",";","tag","","","{","def variable","'http://www.example.com/ns/my':fn","","(","atom","12","",")}","tag",""]); + +MT.testMode("testProcessingInstructions", + 'data() instance of xs:string', + ["def variable","data","","(","comment meta","","",")",null," ","keyword","instance",null," ","keyword","of",null," ","atom","xs:string"]); + +MT.testMode("testQuoteEscapeDouble", + 'let $rootfolder := "c:\\builds\\winnt\\HEAD\\qa\\scripts\\"\ +let $keysfolder := concat($rootfolder, "keys\\")\ +return\ +$keysfolder', + ["keyword","let",null," ","variable","$rootfolder",null," ","keyword",":=",null," ","string","\"c:\\builds\\winnt\\HEAD\\qa\\scripts\\\"","keyword","let",null," ","variable","$keysfolder",null," ","keyword",":=",null," ","def variable","concat","","(","variable","$rootfolder","",",",null," ","string","\"keys\\\"","",")","variable","return$keysfolder"]); diff --git a/mode/xquery/test/index.html b/mode/xquery/test/index.html deleted file mode 100644 index ba82e54f37..0000000000 --- a/mode/xquery/test/index.html +++ /dev/null @@ -1,27 +0,0 @@ - - - - - - - - - - - - - - - - - - -

    XQuery CodeMirror Mode

    -

    -

    -
      -
    -
    - - diff --git a/mode/xquery/test/testBase.js b/mode/xquery/test/testBase.js deleted file mode 100644 index d40e9eeabc..0000000000 --- a/mode/xquery/test/testBase.js +++ /dev/null @@ -1,42 +0,0 @@ - $(document).ready(function(){ - module("testBase"); - test("eviltest", function() { - expect(1); - - var input = 'xquery version "1.0-ml";\ - (: this is\ - : a \ - "comment" :)\ - let $let := <x attr="value">"test"<func>function() $var {function()} {$var}</func></x>\ - let $joe:=1\ - return element element {\ - attribute attribute { 1 },\ - element test { 'a' }, \ - attribute foo { "bar" },\ - fn:doc()[ foo/@bar eq $let ],\ - //x } \ - \ - (: a more \'evil\' test :)\ - (: Modified Blakeley example (: with nested comment :) ... :)\ - declare private function local:declare() {()};\ - declare private function local:private() {()};\ - declare private function local:function() {()};\ - declare private function local:local() {()};\ - let $let := <let>let $let := "let"</let>\ - return element element {\ - attribute attribute { try { xdmp:version() } catch($e) { xdmp:log($e) } },\ - attribute fn:doc { "bar" castable as xs:string },\ - element text { text { "text" } },\ - fn:doc()[ child::eq/(@bar | attribute::attribute) eq $let ],\ - //fn:doc\ - }'; - var expected = 'xquery version "1.0-ml"; (: this is : a "comment" :) let $let := <x attr="value">"test"<func>function() $var {function()} {$var}</func></x> let $joe:=1 return element element { attribute attribute { 1 }, element test { \'a\' }, attribute foo { "bar" }, fn:doc()[ foo/@bar eq $let ], //x } (: a more \'evil\' test :) (: Modified Blakeley example (: with nested comment :) ... :) declare private function local:declare() {()}; declare private function local:private() {()}; declare private function local:function() {()}; declare private function local:local() {()}; let $let := <let>let $let := "let"</let> return element element { attribute attribute { try { xdmp:version() } catch($e) { xdmp:log($e) } }, attribute fn:doc { "bar" castable as xs:string }, element text { text { "text" } }, fn:doc()[ child::eq/(@bar | attribute::attribute) eq $let ], //fn:doc }'; - - $("#sandbox").html(''); - var editor = CodeMirror.fromTextArea($("#editor")[0]); - var result = $(".CodeMirror-lines div div pre")[0].innerHTML; - - equal(result, expected); - $("#editor").html(""); - }); - }); diff --git a/mode/xquery/test/testEmptySequenceKeyword.js b/mode/xquery/test/testEmptySequenceKeyword.js deleted file mode 100644 index 39ed090500..0000000000 --- a/mode/xquery/test/testEmptySequenceKeyword.js +++ /dev/null @@ -1,16 +0,0 @@ -$(document).ready(function(){ - module("testEmptySequenceKeyword"); - test("testEmptySequenceKeyword", function() { - expect(1); - - var input = '"foo" instance of empty-sequence()'; - var expected = '"foo" instance of empty-sequence()'; - - $("#sandbox").html(''); - var editor = CodeMirror.fromTextArea($("#editor")[0]); - var result = $(".CodeMirror-lines div div pre")[0].innerHTML; - - equal(result, expected); - $("#editor").html(""); - }); -}); diff --git a/mode/xquery/test/testMultiAttr.js b/mode/xquery/test/testMultiAttr.js deleted file mode 100644 index 8e98c47d62..0000000000 --- a/mode/xquery/test/testMultiAttr.js +++ /dev/null @@ -1,16 +0,0 @@ - $(document).ready(function(){ - module("testMultiAttr"); - test("test1", function() { - expect(1); - - var expected = '<p a1="foo" a2="bar">hello world</p>'; - - $("#sandbox").html(''); - $("#editor").html('

    hello world

    '); - var editor = CodeMirror.fromTextArea($("#editor")[0]); - var result = $(".CodeMirror-lines div div pre")[0].innerHTML; - - equal(result, expected); - $("#editor").html(""); - }); - }); \ No newline at end of file diff --git a/mode/xquery/test/testNamespaces.js b/mode/xquery/test/testNamespaces.js deleted file mode 100644 index 4efea63e0d..0000000000 --- a/mode/xquery/test/testNamespaces.js +++ /dev/null @@ -1,91 +0,0 @@ -$(document).ready(function(){ - module("test namespaces"); - -// -------------------------------------------------------------------------------- -// this test is based on this: -//http://mbrevoort.github.com/CodeMirror2/#!exprSeqTypes/PrologExpr/VariableProlog/ExternalVariablesWith/K2-ExternalVariablesWith-10.xq -// -------------------------------------------------------------------------------- - test("test namespaced variable", function() { - expect(1); - - var input = 'declare namespace e = "http://example.com/ANamespace";\ -declare variable $e:exampleComThisVarIsNotRecognized as element(*) external;'; - - var expected = 'declare namespace e = "http://example.com/ANamespace";declare variable $e:exampleComThisVarIsNotRecognized as element(*) external;'; - - $("#sandbox").html(''); - var editor = CodeMirror.fromTextArea($("#editor")[0]); - var result = $(".CodeMirror-lines div div pre")[0].innerHTML; - - equal(result, expected); - $("#editor").html(""); - }); - - -// -------------------------------------------------------------------------------- -// this test is based on: -// http://mbrevoort.github.com/CodeMirror2/#!Basics/EQNames/eqname-002.xq -// -------------------------------------------------------------------------------- - test("test EQName variable", function() { - expect(1); - - var input = 'declare variable $"http://www.example.com/ns/my":var := 12;\ -{$"http://www.example.com/ns/my":var}'; - - var expected = 'declare variable $"http://www.example.com/ns/my":var := 12;<out>{$"http://www.example.com/ns/my":var}</out>'; - - $("#sandbox").html(''); - var editor = CodeMirror.fromTextArea($("#editor")[0]); - var result = $(".CodeMirror-lines div div pre")[0].innerHTML; - - equal(result, expected); - $("#editor").html(""); - }); - -// -------------------------------------------------------------------------------- -// this test is based on: -// http://mbrevoort.github.com/CodeMirror2/#!Basics/EQNames/eqname-003.xq -// -------------------------------------------------------------------------------- - test("test EQName function", function() { - expect(1); - - var input = 'declare function "http://www.example.com/ns/my":fn ($a as xs:integer) as xs:integer {\ - $a + 2\ -};\ -{"http://www.example.com/ns/my":fn(12)}'; - - var expected = 'declare function "http://www.example.com/ns/my":fn ($a as xs:integer) as xs:integer { $a + 2};<out>{"http://www.example.com/ns/my":fn(12)}</out>'; - - $("#sandbox").html(''); - var editor = CodeMirror.fromTextArea($("#editor")[0]); - var result = $(".CodeMirror-lines div div pre")[0].innerHTML; - - equal(result, expected); - $("#editor").html(""); - }); - -// -------------------------------------------------------------------------------- -// this test is based on: -// http://mbrevoort.github.com/CodeMirror2/#!Basics/EQNames/eqname-003.xq -// -------------------------------------------------------------------------------- - test("test EQName function with single quotes", function() { - expect(1); - - var input = 'declare function \'http://www.example.com/ns/my\':fn ($a as xs:integer) as xs:integer {\ - $a + 2\ -};\ -{\'http://www.example.com/ns/my\':fn(12)}'; - - var expected = 'declare function \'http://www.example.com/ns/my\':fn ($a as xs:integer) as xs:integer { $a + 2};<out>{\'http://www.example.com/ns/my\':fn(12)}</out>'; - - $("#sandbox").html(''); - var editor = CodeMirror.fromTextArea($("#editor")[0]); - var result = $(".CodeMirror-lines div div pre")[0].innerHTML; - - equal(result, expected); - $("#editor").html(""); - }); - -}); - - diff --git a/mode/xquery/test/testProcessingInstructions.js b/mode/xquery/test/testProcessingInstructions.js deleted file mode 100644 index 9b75305283..0000000000 --- a/mode/xquery/test/testProcessingInstructions.js +++ /dev/null @@ -1,16 +0,0 @@ -$(document).ready(function(){ - module("testProcessingInstructions"); - test("testProcessingInstructions", function() { - expect(1); - - var input = 'data() instance of xs:string'; - var expected = 'data(<?target content?>) instance of xs:string'; - - $("#sandbox").html(''); - var editor = CodeMirror.fromTextArea($("#editor")[0]); - var result = $(".CodeMirror-lines div div pre")[0].innerHTML; - - equal(result, expected); - $("#editor").html(""); - }); -}); diff --git a/mode/xquery/test/testQuotes.js b/mode/xquery/test/testQuotes.js deleted file mode 100644 index 79e5142d9f..0000000000 --- a/mode/xquery/test/testQuotes.js +++ /dev/null @@ -1,19 +0,0 @@ - $(document).ready(function(){ - module("testQuoteEscape"); - test("testQuoteEscapeDouble", function() { - expect(1); - - var input = 'let $rootfolder := "c:\\builds\\winnt\\HEAD\\qa\\scripts\\"\ -let $keysfolder := concat($rootfolder, "keys\\")\ -return\ -$keysfolder'; - var expected = 'let $rootfolder := "c:\\builds\\winnt\\HEAD\\qa\\scripts\\"let $keysfolder := concat($rootfolder, "keys\\")return$keysfolder'; - - $("#sandbox").html(''); - var editor = CodeMirror.fromTextArea($("#editor")[0]); - var result = $(".CodeMirror-lines div div pre")[0].innerHTML; - - equal(result, expected); - $("#editor").html(""); - }); - }); diff --git a/test/index.html b/test/index.html index d3042dace1..020b8100de 100644 --- a/test/index.html +++ b/test/index.html @@ -47,6 +47,8 @@

    CodeMirror: Test Suite

    + + diff --git a/lib/codemirror.js b/lib/codemirror.js index 1650a52807..f0d4991191 100644 --- a/lib/codemirror.js +++ b/lib/codemirror.js @@ -782,9 +782,12 @@ window.CodeMirror = (function() { return {top: top, bottom: bottom, left: left, right: right}; } + // Context is one of "line", "div" (display.lineDiv), "local"/null (editor), or "page" function intoCoordSystem(cm, pos, rect, context) { if (context == "line") return rect; - var yOff = heightAtLine(cm.view.doc, pos.line) - (context == "div" ? cm.display.viewOffset : 0); + if (!context) context = "local"; + var yOff = heightAtLine(cm.view.doc, pos.line); + if (context != "local") yOff -= cm.display.viewOffset; if (context == "page") { var lOff = cm.display.lineSpace.getBoundingClientRect(); yOff += lOff.top; rect.left += lOff.left; rect.right += lOff.right; From 7c2f744e4de946f48c1d22fa6edf098878e0379c Mon Sep 17 00:00:00 2001 From: Marijn Haverbeke Date: Thu, 20 Sep 2012 14:12:59 +0200 Subject: [PATCH 0149/5780] Use a className in demo/marker.html Should help trigger #836, and is cleaner anyway. --- demo/complete.html | 2 -- demo/marker.html | 3 ++- 2 files changed, 2 insertions(+), 3 deletions(-) diff --git a/demo/complete.html b/demo/complete.html index 76cfe60bba..13569edf92 100644 --- a/demo/complete.html +++ b/demo/complete.html @@ -10,8 +10,6 @@ -

    CodeMirror: Autocomplete demo

    diff --git a/demo/marker.html b/demo/marker.html index a76082dd47..f0981e4d5f 100644 --- a/demo/marker.html +++ b/demo/marker.html @@ -10,6 +10,7 @@ @@ -28,8 +29,8 @@

    CodeMirror: Breakpoint demo

    function makeMarker() { var marker = document.createElement("div"); - marker.style.color = "#822"; marker.innerHTML = "●"; + marker.className = "breakpoint"; return marker; } From 2038a1c65f0ca0e5849f17a6b6926eeef4da6b0a Mon Sep 17 00:00:00 2001 From: Marijn Haverbeke Date: Thu, 20 Sep 2012 14:23:01 +0200 Subject: [PATCH 0150/5780] Repair gutterclick event The line calculation was ignoring paddingTop and scrolling. --- lib/codemirror.js | 27 ++++++++++++++------------- 1 file changed, 14 insertions(+), 13 deletions(-) diff --git a/lib/codemirror.js b/lib/codemirror.js index f0d4991191..ae122b409f 100644 --- a/lib/codemirror.js +++ b/lib/codemirror.js @@ -1361,21 +1361,22 @@ window.CodeMirror = (function() { catch(e) { return false; } if (mX >= Math.floor(display.gutters.getBoundingClientRect().right)) return false; - if (hasHandler(cm, "gutterClick")) { - mY -= display.wrapper.getBoundingClientRect().top; - for (var i = 0; i < cm.options.gutters.length; ++i) { - var g = display.gutters.childNodes[i]; - if (g && g.getBoundingClientRect().right >= mX) { - if (mY < display.lineDiv.offsetHeight) { - var line = lineAtHeight(cm.view.doc, mY); - var gutter = cm.options.gutters[i]; - signalLater(cm, cm, "gutterClick", cm, line, gutter, e); - } - break; - } + e_preventDefault(e); + if (!hasHandler(cm, "gutterClick")) return true; + + var lineBox = display.lineDiv.getBoundingClientRect(); + if (mY > lineBox.bottom) return true; + mY -= lineBox.top - display.viewOffset; + + for (var i = 0; i < cm.options.gutters.length; ++i) { + var g = display.gutters.childNodes[i]; + if (g && g.getBoundingClientRect().right >= mX) { + var line = lineAtHeight(cm.view.doc, mY); + var gutter = cm.options.gutters[i]; + signalLater(cm, cm, "gutterClick", cm, line, gutter, e); + break; } } - e_preventDefault(e); return true; } From 81dbb5d8ebdf72c897a240bfe3d8d2b37511e378 Mon Sep 17 00:00:00 2001 From: Marijn Haverbeke Date: Thu, 20 Sep 2012 15:14:18 +0200 Subject: [PATCH 0151/5780] Increase getStateBefore's scan limit --- lib/codemirror.js | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/lib/codemirror.js b/lib/codemirror.js index ae122b409f..fd9778f422 100644 --- a/lib/codemirror.js +++ b/lib/codemirror.js @@ -700,7 +700,7 @@ window.CodeMirror = (function() { // parse correctly. function findStartLine(doc, n) { var minindent, minline; - for (var search = n, lim = n - 40; search > lim; --search) { + for (var search = n, lim = n - 100; search > lim; --search) { if (search == 0) return 0; var line = getLine(doc, search-1); if (line.stateAfter) return search; From 4c7fce053649912bf5bd17fbc8263591a8bc9266 Mon Sep 17 00:00:00 2001 From: Marijn Haverbeke Date: Thu, 20 Sep 2012 15:56:53 +0200 Subject: [PATCH 0152/5780] Add caveat to doc/internals.html --- doc/internals.html | 8 ++++++++ index.html | 7 +++++-- 2 files changed, 13 insertions(+), 2 deletions(-) diff --git a/doc/internals.html b/doc/internals.html index d859419864..9139528f3d 100644 --- a/doc/internals.html +++ b/doc/internals.html @@ -27,6 +27,14 @@

    { } CodeMi Date: March 2nd 2011 (updated November 13th 2011)

    +

    Caution: this text was written briefly after +version 2 was initially written. It no longer (even including the +update at the bottom) fully represents the current implementation. I'm +leaving it here as a historic document. For more up-to-date +information, look at the entries +tagged cm-internals +on my blog.

    +

    This is a followup to my Brutal Odyssey to the Dark Side of the DOM Tree story. That one describes the diff --git a/index.html b/index.html index ecd13d3c32..73006fcfff 100644 --- a/index.html +++ b/index.html @@ -179,9 +179,12 @@

    Documentation

    of how to use the editor, and then describes the API in detail.

    For those who want to learn more about the code, there is - an overview of the internals available. + a series of + posts on CodeMirror on my blog, and the + old overview of the editor + internals. The source code - itself is, for the most part, also well commented.

    + itself is, for the most part, also very readable.

    Support and bug reports

    From 52d76de91d4f0b0652d7e686443c6bf38196f0a3 Mon Sep 17 00:00:00 2001 From: Marijn Haverbeke Date: Tue, 25 Sep 2012 21:20:53 +0200 Subject: [PATCH 0153/5780] Fix git URL in doc/compress.html --- doc/compress.html | 50 +++++++++++++++++++++++------------------------ 1 file changed, 25 insertions(+), 25 deletions(-) diff --git a/doc/compress.html b/doc/compress.html index b5836dd1a7..d8235fc07c 100644 --- a/doc/compress.html +++ b/doc/compress.html @@ -30,30 +30,30 @@

    { } CodeMi

    Version:

    diff --git a/mode/gfm/test.js b/mode/gfm/test.js new file mode 100644 index 0000000000..3a261f8f77 --- /dev/null +++ b/mode/gfm/test.js @@ -0,0 +1,225 @@ +// Initiate ModeTest and set defaults +var MT = ModeTest; +MT.modeName = 'gfm'; +MT.modeOptions = {}; + +// Emphasis characters within a word +MT.testMode( + 'emInWordAsterisk', + 'foo*bar*hello', + [ + null, 'foo', + 'em', '*bar*', + null, 'hello' + ] +); +MT.testMode( + 'emInWordUnderscore', + 'foo_bar_hello', + [ + null, 'foo_bar_hello' + ] +); +MT.testMode( + 'emStrongUnderscore', + '___foo___ bar', + [ + 'strong', '__', + 'emstrong', '_foo__', + 'em', '_', + null, ' bar' + ] +); + +// Fenced code blocks +MT.testMode( + 'fencedCodeBlocks', + '```\nfoo\n\n```\nbar', + [ + 'comment', '```', + 'comment', 'foo', + 'comment', '```', + null, 'bar' + ] +); +// Fenced code block mode switching +MT.testMode( + 'fencedCodeBlockModeSwitching', + '```javascript\nfoo\n\n```\nbar', + [ + 'comment', '```javascript', + 'variable', 'foo', + 'comment', '```', + null, 'bar' + ] +); + +// SHA +MT.testMode( + 'SHA', + 'foo be6a8cc1c1ecfe9489fb51e4869af15a13fc2cd2 bar', + [ + null, 'foo ', + 'link', 'be6a8cc1c1ecfe9489fb51e4869af15a13fc2cd2', + null, ' bar' + ] +); +// GitHub highlights hashes 7-40 chars in length +MT.testMode( + 'shortSHA', + 'foo be6a8cc bar', + [ + null, 'foo ', + 'link', 'be6a8cc', + null, ' bar' + ] +); +// Invalid SHAs +// +// GitHub does not highlight hashes shorter than 7 chars +MT.testMode( + 'tooShortSHA', + 'foo be6a8c bar', + [ + null, 'foo be6a8c bar' + ] +); +// GitHub does not highlight hashes longer than 40 chars +MT.testMode( + 'longSHA', + 'foo be6a8cc1c1ecfe9489fb51e4869af15a13fc2cd22 bar', + [ + null, 'foo be6a8cc1c1ecfe9489fb51e4869af15a13fc2cd22 bar' + ] +); +MT.testMode( + 'badSHA', + 'foo be6a8cc1c1ecfe9489fb51e4869af15a13fc2cg2 bar', + [ + null, 'foo be6a8cc1c1ecfe9489fb51e4869af15a13fc2cg2 bar' + ] +); +// User@SHA +MT.testMode( + 'userSHA', + 'foo bar@be6a8cc1c1ecfe9489fb51e4869af15a13fc2cd2 hello', + [ + null, 'foo ', + 'link', 'bar@be6a8cc1c1ecfe9489fb51e4869af15a13fc2cd2', + null, ' hello' + ] +); +// User/Project@SHA +MT.testMode( + 'userProjectSHA', + 'foo bar/hello@be6a8cc1c1ecfe9489fb51e4869af15a13fc2cd2 world', + [ + null, 'foo ', + 'link', 'bar/hello@be6a8cc1c1ecfe9489fb51e4869af15a13fc2cd2', + null, ' world' + ] +); + +// #Num +MT.testMode( + 'num', + 'foo #1 bar', + [ + null, 'foo ', + 'link', '#1', + null, ' bar' + ] +); +// bad #Num +MT.testMode( + 'badNum', + 'foo #1bar hello', + [ + null, 'foo #1bar hello' + ] +); +// User#Num +MT.testMode( + 'userNum', + 'foo bar#1 hello', + [ + null, 'foo ', + 'link', 'bar#1', + null, ' hello' + ] +); +// User/Project#Num +MT.testMode( + 'userProjectNum', + 'foo bar/hello#1 world', + [ + null, 'foo ', + 'link', 'bar/hello#1', + null, ' world' + ] +); + +// Vanilla links +MT.testMode( + 'vanillaLink', + 'foo http://www.example.com/ bar', + [ + null, 'foo ', + 'link', 'http://www.example.com/', + null, ' bar' + ] +); +MT.testMode( + 'vanillaLinkPunctuation', + 'foo http://www.example.com/. bar', + [ + null, 'foo ', + 'link', 'http://www.example.com/', + null, '. bar' + ] +); +MT.testMode( + 'vanillaLinkExtension', + 'foo http://www.example.com/index.html bar', + [ + null, 'foo ', + 'link', 'http://www.example.com/index.html', + null, ' bar' + ] +); +// Not a link +MT.testMode( + 'notALink', + '```css\nfoo {color:black;}\n```http://www.example.com/', + [ + 'comment', '```css', + 'tag', 'foo', + null, ' {', + 'property', 'color', + 'operator', ':', + 'keyword', 'black', + null, ';}', + 'comment', '```', + 'link', 'http://www.example.com/' + ] +); +// Not a link +MT.testMode( + 'notALink', + '``foo `bar` http://www.example.com/`` hello', + [ + 'comment', '``foo `bar` http://www.example.com/``', + null, ' hello' + ] +); +// Not a link +MT.testMode( + 'notALink', + '`foo\nhttp://www.example.com/\n`foo\n\nhttp://www.example.com/', + [ + 'comment', '`foo', + 'link', 'http://www.example.com/', + 'comment', '`foo', + 'link', 'http://www.example.com/' + ] +); \ No newline at end of file diff --git a/mode/markdown/markdown.js b/mode/markdown/markdown.js index d3b05985d9..7e13e8217d 100644 --- a/mode/markdown/markdown.js +++ b/mode/markdown/markdown.js @@ -2,6 +2,51 @@ CodeMirror.defineMode("markdown", function(cmCfg, modeCfg) { var htmlFound = CodeMirror.mimeModes.hasOwnProperty("text/html"); var htmlMode = CodeMirror.getMode(cmCfg, htmlFound ? "text/html" : "text/plain"); + var aliases = { + html: "htmlmixed", + js: "javascript", + json: "application/json", + c: "text/x-csrc", + "c++": "text/x-c++src", + java: "text/x-java", + csharp: "text/x-csharp", + "c#": "text/x-csharp" + }; + + var getMode = (function () { + var i, modes = {}, mimes = {}, mime; + + var list = []; + for (var m in CodeMirror.modes) + if (CodeMirror.modes.propertyIsEnumerable(m)) list.push(m); + for (i = 0; i < list.length; i++) { + modes[list[i]] = list[i]; + } + var mimesList = []; + for (var m in CodeMirror.mimeModes) + if (CodeMirror.mimeModes.propertyIsEnumerable(m)) + mimesList.push({mime: m, mode: CodeMirror.mimeModes[m]}); + for (i = 0; i < mimesList.length; i++) { + mime = mimesList[i].mime; + mimes[mime] = mimesList[i].mime; + } + + for (var a in aliases) { + if (aliases[a] in modes || aliases[a] in mimes) + modes[a] = aliases[a]; + } + + return function (lang) { + return modes[lang] ? CodeMirror.getMode(cmCfg, modes[lang]) : null; + }; + }()); + + // Should underscores in words open/close em/strong? + if (modeCfg.underscoresBreakWords === undefined) + modeCfg.underscoresBreakWords = true; + + // Turn on fenced code blocks? ("```" to start/end) + if (modeCfg.fencedCodeBlocks === undefined) modeCfg.fencedCodeBlocks = false; var codeDepth = 0; var prevLineHasContent = false @@ -43,8 +88,6 @@ CodeMirror.defineMode("markdown", function(cmCfg, modeCfg) { function blankLine(state) { // Reset linkTitle state state.linkTitle = false; - // Reset CODE state - state.code = false; // Reset EM state state.em = false; // Reset STRONG state @@ -59,7 +102,6 @@ CodeMirror.defineMode("markdown", function(cmCfg, modeCfg) { } function blockNormal(stream, state) { - var match; if (state.list !== false && state.indentationDiff >= 0) { // Continued list if (state.indentationDiff < 4) { // Only adjust indentation if *not* a code block @@ -85,9 +127,15 @@ CodeMirror.defineMode("markdown", function(cmCfg, modeCfg) { return switchInline(stream, state, footnoteLink); } else if (stream.match(hrRE, true)) { return hr; - } else if (match = stream.match(ulRE, true) || stream.match(olRE, true)) { + } else if (stream.match(ulRE, true) || stream.match(olRE, true)) { state.indentation += 4; state.list = true; + } else if (modeCfg.fencedCodeBlocks && stream.match(/^```([\w+#]*)/, true)) { + // try switching mode + state.localMode = getMode(RegExp.$1); + if (state.localMode) state.localState = state.localMode.startState(); + switchBlock(stream, state, local); + return code; } return switchInline(stream, state, state.inline); @@ -107,6 +155,30 @@ CodeMirror.defineMode("markdown", function(cmCfg, modeCfg) { return style; } + function local(stream, state) { + if (stream.sol() && stream.match(/^```/, true)) { + state.localMode = state.localState = null; + state.f = inlineNormal; + state.block = blockNormal; + return code; + } else if (state.localMode) { + return state.localMode.token(stream, state.localState); + } else { + stream.skipToEnd(); + return code; + } + } + + function codeBlock(stream, state) { + if(stream.match(codeBlockRE, true)){ + state.f = inlineNormal; + state.block = blockNormal; + switchInline(stream, state, state.inline); + return code; + } + stream.skipToEnd(); + return code; + } // Inline function getType(state) { @@ -164,6 +236,7 @@ CodeMirror.defineMode("markdown", function(cmCfg, modeCfg) { } } + // If this block is changed, it may need to be updated in GFM mode if (ch === '`') { var t = getType(state); var before = stream.pos; @@ -227,8 +300,20 @@ CodeMirror.defineMode("markdown", function(cmCfg, modeCfg) { return "tag"; } + var ignoreUnderscore = false; + if (!modeCfg.underscoresBreakWords) { + if (ch === '_' && stream.peek() !== '_' && stream.match(/(\w)/, false)) { + var prevPos = stream.pos - 2; + if (prevPos >= 0) { + var prevCh = stream.string.charAt(prevPos); + if (prevCh !== '_' && prevCh.match(/(\w)/, false)) { + ignoreUnderscore = true; + } + } + } + } var t = getType(state); - if (ch === '*' || ch === '_') { + if (ch === '*' || (ch === '_' && !ignoreUnderscore)) { if (state.strong === ch && stream.eat(ch)) { // Remove STRONG state.strong = false; return t; @@ -344,6 +429,9 @@ CodeMirror.defineMode("markdown", function(cmCfg, modeCfg) { block: s.block, htmlState: CodeMirror.copyState(htmlMode, s.htmlState), indentation: s.indentation, + + localMode: s.localMode, + localState: s.localMode ? CodeMirror.copyState(s.localMode, s.localState) : null, inline: s.inline, text: s.text, @@ -372,6 +460,9 @@ CodeMirror.defineMode("markdown", function(cmCfg, modeCfg) { // Reset state.header state.header = false; + + // Reset state.code + state.code = false; state.f = state.block; var indentation = stream.match(/^\s*/, true)[0].replace(/\t/g, ' ').length; diff --git a/mode/markdown/test.js b/mode/markdown/test.js index ff3031f7d2..7572db510b 100644 --- a/mode/markdown/test.js +++ b/mode/markdown/test.js @@ -71,6 +71,17 @@ MT.testMode( ] ); +// Block code using single backtick (shouldn't work) +MT.testMode( + 'blockCodeSingleBacktick', + '`\nfoo\n`', + [ + 'comment', '`', + null, 'foo', + 'comment', '`' + ] +); + // Unclosed backticks // Instead of simply marking as CODE, it would be nice to have an // incomplete flag for CODE, that is styled slightly different. @@ -1252,4 +1263,4 @@ MT.testMode( [ null, '\\\\# foo' ] -); \ No newline at end of file +); diff --git a/test/index.html b/test/index.html index 020b8100de..7b48facedd 100644 --- a/test/index.html +++ b/test/index.html @@ -7,6 +7,7 @@ + @@ -45,6 +46,8 @@

    CodeMirror: Test Suite

    + + From 5d193fed8a144f7d8618f17a2de93bab350a293d Mon Sep 17 00:00:00 2001 From: Marijn Haverbeke Date: Wed, 3 Oct 2012 13:46:48 +0200 Subject: [PATCH 0179/5780] Fix "update" event being fired when "viewportChange" is meant --- lib/codemirror.js | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/lib/codemirror.js b/lib/codemirror.js index e4a7056129..c13562cebe 100644 --- a/lib/codemirror.js +++ b/lib/codemirror.js @@ -346,7 +346,7 @@ window.CodeMirror = (function() { if (updated) { signalLater(cm, cm, "update", cm); if (cm.display.showingFrom != oldFrom || cm.display.showingTo != oldTo) - signalLater(cm, cm, "update", cm, cm.display.showingFrom, cm.display.showingTo); + signalLater(cm, cm, "viewportChange", cm, cm.display.showingFrom, cm.display.showingTo); } updateSelection(cm); updateScrollbars(cm.display, cm.view.doc.height, scrollTop); From b4c9083fb3219ad1d986c1f8d7a2a05dec8a29d0 Mon Sep 17 00:00:00 2001 From: Marijn Haverbeke Date: Wed, 3 Oct 2012 13:55:04 +0200 Subject: [PATCH 0180/5780] Introduce the viewportMargin option --- doc/manual.html | 67 +++++++++++++++++++++++++++++------------------ lib/codemirror.js | 5 +++- 2 files changed, 46 insertions(+), 26 deletions(-) diff --git a/doc/manual.html b/doc/manual.html index f40bee2d3c..a510db827d 100644 --- a/doc/manual.html +++ b/doc/manual.html @@ -218,31 +218,6 @@

    Configuration

    simply true), focusing of the editor is also disallowed.

    -
    cursorBlinkRate (number)
    -
    Half-period in milliseconds used for cursor blinking. The default blink - rate is 530ms.
    - -
    cursorHeight (number)
    -
    Determines the height of the cursor. Default is 1, meaning - it spans the whole height of the line. For some fonts (and by - some tastes) a smaller height (for example 0.85), - which causes the cursor to not reach all the way to the bottom - of the line, looks better
    - -
    workTime, workDelay (number)
    -
    Highlighting is done by a pseudo background-thread that will - work for workTime milliseconds, and then use - timeout to sleep for workDelay milliseconds. The - defaults are 200 and 300, you can change these options to make - the highlighting more or less aggressive.
    - -
    pollInterval (number)
    -
    Indicates how quickly CodeMirror should poll its input - textarea for changes (when focused). Most input is captured by - events, but some things, like IME input on some browsers, don't - generate events that allow CodeMirror to properly detect it. - Thus, it polls. Default is 100 milliseconds.
    -
    undoDepth (integer)
    The maximum number of undo levels that the editor stores. Defaults to 40.
    @@ -260,7 +235,13 @@

    Configuration

    set to true when either the source textarea is focused, or it has an autofocus attribute and no other element is focused. + + +

    Below this a few more specialized, low-level options are + listed. These are only useful in very specific situations, you + might want to skip them the first time you read this manual.

    +
    dragDrop (boolean)
    Controls whether drag-and-drop is enabled. On by default.
    @@ -291,6 +272,42 @@

    Configuration

    its type property and only do something when it is keydown (or keypress for actions that need character data). + +
    cursorBlinkRate (number)
    +
    Half-period in milliseconds used for cursor blinking. The default blink + rate is 530ms.
    + +
    cursorHeight (number)
    +
    Determines the height of the cursor. Default is 1, meaning + it spans the whole height of the line. For some fonts (and by + some tastes) a smaller height (for example 0.85), + which causes the cursor to not reach all the way to the bottom + of the line, looks better
    + +
    workTime, workDelay (number)
    +
    Highlighting is done by a pseudo background-thread that will + work for workTime milliseconds, and then use + timeout to sleep for workDelay milliseconds. The + defaults are 200 and 300, you can change these options to make + the highlighting more or less aggressive.
    + +
    pollInterval (number)
    +
    Indicates how quickly CodeMirror should poll its input + textarea for changes (when focused). Most input is captured by + events, but some things, like IME input on some browsers, don't + generate events that allow CodeMirror to properly detect it. + Thus, it polls. Default is 100 milliseconds.
    + +
    viewportMargin (integer)
    +
    Specifies the amount of lines that are rendered above and + below the part of the document that's currently scrolled into + view. This affects the amount of updates needed when scrolling, + and the amount of work that such an update does. You should + usually leave it at its default, 100. Can be set + to Infinity to make sure the whole document is + always rendered, and thus the browser's text search works on it. + This will have bad effects on performance of big + documents.

    Events

    diff --git a/lib/codemirror.js b/lib/codemirror.js index c13562cebe..321bd1cd75 100644 --- a/lib/codemirror.js +++ b/lib/codemirror.js @@ -381,7 +381,8 @@ window.CodeMirror = (function() { for (var i = 0; i < changes.length; ++i) if (changes[i].diff) { positionsChangedFrom = changes[i].from; break; } - var from = Math.max(visible.from - 100, 0), to = Math.min(doc.size, visible.to + 100); + var from = Math.max(visible.from - cm.options.viewportMargin, 0); + var to = Math.min(doc.size, visible.to + cm.options.viewportMargin); if (display.showingFrom < from && from - display.showingFrom < 20) from = display.showingFrom; if (display.showingTo > to && display.showingTo - to < 20) to = Math.min(doc.size, display.showingTo); @@ -2019,6 +2020,7 @@ window.CodeMirror = (function() { else if (option == "keyMap") keyMapChanged(this); else if (option == "gutters" || option == "lineNumbers") setGuttersForLineNumbers(this.options); else if (option == "tabindex") this.display.input.tabIndex = value; + else if (option == "viewportMargin") this.refresh(); if (option == "lineNumbers" || option == "gutters" || option == "firstLineNumber" || option == "theme" || option == "lineNumberFormatter") guttersChanged(this); @@ -2492,6 +2494,7 @@ window.CodeMirror = (function() { workDelay: 200, pollInterval: 100, undoDepth: 40, + viewportMargin: 100, tabindex: null, autofocus: null, lineNumberFormatter: function(integer) { return integer; } From faf6d1e06ce571d58fe1f7e3846309a133f7bbae Mon Sep 17 00:00:00 2001 From: Marijn Haverbeke Date: Wed, 3 Oct 2012 14:01:38 +0200 Subject: [PATCH 0181/5780] Add test.html to .gitignore I tend to have some variety of it sitting in the root dir. --- .gitignore | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-) diff --git a/.gitignore b/.gitignore index 28b5e4144f..ea08f9d785 100644 --- a/.gitignore +++ b/.gitignore @@ -1,2 +1,3 @@ /node_modules -/npm-debug.log \ No newline at end of file +/npm-debug.log +test.html From adc7cdcf4a48bc0a343067b233c44ae8a9ca8ec4 Mon Sep 17 00:00:00 2001 From: Marijn Haverbeke Date: Wed, 3 Oct 2012 22:28:47 +0200 Subject: [PATCH 0182/5780] Fix regression due to being removed. Closes #868 --- mode/rst/rst.js | 12 +----------- 1 file changed, 1 insertion(+), 11 deletions(-) diff --git a/mode/rst/rst.js b/mode/rst/rst.js index c4ecdf4c75..7752e4b2a2 100644 --- a/mode/rst/rst.js +++ b/mode/rst/rst.js @@ -18,17 +18,7 @@ CodeMirror.defineMode('rst', function(config, options) { } function hasMode(mode) { - if (mode) { - var modes = CodeMirror.listModes(); - - for (var i in modes) { - if (modes[i] == mode) { - return true; - } - } - } - - return false; + return mode && CodeMirror.modes.hasOwnProperty(mode); } function getMode(mode) { From 49d96528e3a37b1aa9d93da4be374861ce7f4fc2 Mon Sep 17 00:00:00 2001 From: Brandon Frohs Date: Wed, 3 Oct 2012 09:44:09 -0400 Subject: [PATCH 0183/5780] Add note about mode dependency and links to highlighting tests. --- mode/gfm/index.html | 4 ++++ 1 file changed, 4 insertions(+) diff --git a/mode/gfm/index.html b/mode/gfm/index.html index adb851a408..0c75864d4c 100644 --- a/mode/gfm/index.html +++ b/mode/gfm/index.html @@ -62,5 +62,9 @@

    CodeMirror: GFM mode

    }); +

    Optionally depends on other modes for properly highlighted code blocks.

    + +

    Parsing/Highlighting Tests: normal, verbose.

    + From 6f8f652086bdb03c2a8baea0b9fb8c0b29afa7a9 Mon Sep 17 00:00:00 2001 From: Marijn Haverbeke Date: Thu, 4 Oct 2012 11:57:25 +0200 Subject: [PATCH 0184/5780] [simplehint util] Support completeSingle option To turn off the behavior where it'll always complete when only a single option is left. --- lib/util/simple-hint.js | 8 ++++++-- 1 file changed, 6 insertions(+), 2 deletions(-) diff --git a/lib/util/simple-hint.js b/lib/util/simple-hint.js index f42e787f07..04ae4af8b5 100644 --- a/lib/util/simple-hint.js +++ b/lib/util/simple-hint.js @@ -25,7 +25,10 @@ editor.replaceRange(str, result.from, result.to); } // When there is only one completion, use it directly. - if (completions.length == 1) {insert(completions[0]); return true;} + if (options.completeSingle && completions.length == 1) { + insert(completions[0]); + return true; + } // Build the select widget var complete = document.createElement("div"); @@ -92,6 +95,7 @@ }; CodeMirror.simpleHint.defaults = { closeOnBackspace: true, - closeOnTokenChange: false + closeOnTokenChange: false, + completeSingle: true }; })(); From d3ec92ee11333bdefed7909477752642e78ec7ce Mon Sep 17 00:00:00 2001 From: boomyjee Date: Thu, 4 Oct 2012 02:54:53 +0300 Subject: [PATCH 0185/5780] showWidgets parameter for foldLines function MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit There are cases when widget could be a different repsentation of code, so while folding the code  one may want to show widgets for hidden lines. I added showWidgets parameter to foldLines, if set it allows widgets for hidden lines to be shown. Here is a simple showcase where this functionality is needed: http://uxcandy.com/~boomyjee/dayside/plugins/teapot-copy/ --- lib/codemirror.js | 15 +++++++++------ 1 file changed, 9 insertions(+), 6 deletions(-) diff --git a/lib/codemirror.js b/lib/codemirror.js index 321bd1cd75..a67ec2d0f0 100644 --- a/lib/codemirror.js +++ b/lib/codemirror.js @@ -431,7 +431,8 @@ window.CodeMirror = (function() { // is magically replaced with a new node in the DOM, leaving // us with a reference to an orphan (nextSibling-less) node. if (!curNode) return; - if (!line.hidden) { + if (!line.hidden || (line.hidden.handle.showWidgets && line.widgets)) + { var end = curNode.offsetHeight + curNode.offsetTop; var height = end - relativeTo, diff = line.height - height; if (height < 2) height = textHeight(display); @@ -502,9 +503,11 @@ window.CodeMirror = (function() { cm.view.doc.iter(from, to, function(line) { if (nextIntact && nextIntact.to == j) nextIntact = intact.shift(); if (!nextIntact || nextIntact.from > j) { - if (line.hidden) var lineElement = elt("div"); - else { - var lineElement = lineContent(cm, line), markers = line.gutterMarkers; + if (line.hidden && (!line.hidden.handle.showWidgets || !line.widgets)) { + var lineElement = elt("div"); + } else { + var lineElement = line.hidden ? elt("div") : lineContent(cm, line) + var markers = line.gutterMarkers; if (line.className) lineElement.className = line.className; // Lines with gutter elements or a background class need // to be wrapped again, and have the extra elements added @@ -2207,11 +2210,11 @@ window.CodeMirror = (function() { regChange(this, no, no + 1); }), - foldLines: operation(null, function(from, to, unfoldOnEnter) { + foldLines: operation(null, function(from, to, unfoldOnEnter, showWidgets) { if (typeof from != "number") from = lineNo(from); if (typeof to != "number") to = lineNo(to); if (from > to) return; - var lines = [], handle = {lines: lines, unfoldOnEnter: unfoldOnEnter}, cm = this, view = cm.view, doc = view.doc; + var lines = [], handle = {lines: lines, unfoldOnEnter: unfoldOnEnter, showWidgets: showWidgets}, cm = this, view = cm.view, doc = view.doc; doc.iter(from, to, function(line) { lines.push(line); if (!line.hidden && line.text.length == cm.view.maxLine.text.length) From d0ae5f6925f49881f64fd691c3c3b04435f79913 Mon Sep 17 00:00:00 2001 From: Marijn Haverbeke Date: Thu, 4 Oct 2012 12:18:12 +0200 Subject: [PATCH 0186/5780] Change foldLines to take an options object --- doc/manual.html | 18 ++++++++++++------ lib/codemirror.js | 10 +++++----- lib/util/foldcode.js | 2 +- test/test.js | 4 ++-- 4 files changed, 20 insertions(+), 14 deletions(-) diff --git a/doc/manual.html b/doc/manual.html index a510db827d..67c7b23d41 100644 --- a/doc/manual.html +++ b/doc/manual.html @@ -762,18 +762,24 @@

    Programming API

    background (which lies behind the selection). Pass null to clear the classes for a line. -
    foldLines(from, to, unfoldOnEnter) → foldHandle
    +
    foldLines(from, to, options) → foldHandle
    Hide the given range of lines (to is non-inclusive). Hidden lines don't show up in the editor. Deleting a region around them does delete them, and copying a region around will include them in the copied text. Vertical - cursor movement will skip hidden lines. + cursor movement will skip hidden lines. The options + parameter is optional. When given, it should be an object + containing unfoldOnEnter + and/or showWidgets properties. When unfoldOnEnter is true, horizontal movement into the range will un-hide the range. When it is false, - horizonal cursor movement will skip it as if it isn't there. The - handle this function returns can be used to unfold the range, - and emits an unfold - event when unfolded. Folded ranges may overlap and nest.
    + horizonal cursor movement will skip it as if it isn't there. + When showWidgets is + true, line widgets in the folded + range will remain visible. The handle this function returns can + be used to unfold the range, and emits + an unfold event when + unfolded. Folded ranges may overlap and nest.
    unfoldLines(foldHandle)
    When given a handle as returned by foldLines, this will diff --git a/lib/codemirror.js b/lib/codemirror.js index a67ec2d0f0..036df85ce1 100644 --- a/lib/codemirror.js +++ b/lib/codemirror.js @@ -431,8 +431,7 @@ window.CodeMirror = (function() { // is magically replaced with a new node in the DOM, leaving // us with a reference to an orphan (nextSibling-less) node. if (!curNode) return; - if (!line.hidden || (line.hidden.handle.showWidgets && line.widgets)) - { + if (!line.hidden || (line.hidden.handle.showWidgets && line.widgets)) { var end = curNode.offsetHeight + curNode.offsetTop; var height = end - relativeTo, diff = line.height - height; if (height < 2) height = textHeight(display); @@ -1935,7 +1934,7 @@ window.CodeMirror = (function() { function findNextLine() { for (var l = line + dir, e = dir < 0 ? -1 : doc.size; l != e; l += dir) { var lo = getLine(doc, l); - if (!lo.hidden || !lo.hidden.handle.unfoldOnEneter) { line = l; lineObj = lo; return true; } + if (!lo.hidden || lo.hidden.handle.unfoldOnEnter) { line = l; lineObj = lo; return true; } } } function moveOnce(boundToLine) { @@ -2210,11 +2209,12 @@ window.CodeMirror = (function() { regChange(this, no, no + 1); }), - foldLines: operation(null, function(from, to, unfoldOnEnter, showWidgets) { + foldLines: operation(null, function(from, to, options) { if (typeof from != "number") from = lineNo(from); if (typeof to != "number") to = lineNo(to); if (from > to) return; - var lines = [], handle = {lines: lines, unfoldOnEnter: unfoldOnEnter, showWidgets: showWidgets}, cm = this, view = cm.view, doc = view.doc; + var handle = options || {}, lines = handle.lines = []; + var cm = this, view = cm.view, doc = view.doc; doc.iter(from, to, function(line) { lines.push(line); if (!line.hidden && line.text.length == cm.view.maxLine.text.length) diff --git a/lib/util/foldcode.js b/lib/util/foldcode.js index a0a6840d63..c8958efcb1 100644 --- a/lib/util/foldcode.js +++ b/lib/util/foldcode.js @@ -175,7 +175,7 @@ CodeMirror.newFoldFunction = function(rangeFinder, markText, hideEnd) { } else { var end = rangeFinder(cm, line, hideEnd); if (end == null) return; - var handle = cm.foldLines(line + 1, end, true); + var handle = cm.foldLines(line + 1, end, {unfoldOnEnter: true}); CodeMirror.on(handle, "unfold", function() { cm.setGutterMarker(l, "CodeMirror-folded", null); }); diff --git a/test/test.js b/test/test.js index df9576b6e7..e8e37f7661 100644 --- a/test/test.js +++ b/test/test.js @@ -460,13 +460,13 @@ testCM("hiddenLines", function(cm) { }); testCM("hiddenLinesAutoUnfold", function(cm) { - var folded = cm.foldLines(1, 3, true), unfolded = 0; + var folded = cm.foldLines(1, 3, {unfoldOnEnter: true}), unfolded = 0; CodeMirror.on(folded, "unfold", function() {unfolded++;}); cm.setCursor({line: 3, ch: 0}); eq(unfolded, 0); cm.execCommand("goCharLeft"); eq(unfolded, 1); - var folded = cm.foldLines(1, 3, true), unfolded = 0; + var folded = cm.foldLines(1, 3, {unfoldOnEnter: true}), unfolded = 0; CodeMirror.on(folded, "unfold", function() {unfolded++;}); eqPos(cm.getCursor(), {line: 3, ch: 0}); cm.setCursor({line: 0, ch: 3}); From 7ff8effe2eb3a1b397f95bb46294440bae15d760 Mon Sep 17 00:00:00 2001 From: Marijn Haverbeke Date: Thu, 4 Oct 2012 12:23:25 +0200 Subject: [PATCH 0187/5780] Missing semicolon --- lib/codemirror.js | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/lib/codemirror.js b/lib/codemirror.js index 036df85ce1..0010a2689f 100644 --- a/lib/codemirror.js +++ b/lib/codemirror.js @@ -505,7 +505,7 @@ window.CodeMirror = (function() { if (line.hidden && (!line.hidden.handle.showWidgets || !line.widgets)) { var lineElement = elt("div"); } else { - var lineElement = line.hidden ? elt("div") : lineContent(cm, line) + var lineElement = line.hidden ? elt("div") : lineContent(cm, line); var markers = line.gutterMarkers; if (line.className) lineElement.className = line.className; // Lines with gutter elements or a background class need From 2b8b5ed1a65266ff5c7ee41c4d77d4743ed4d25d Mon Sep 17 00:00:00 2001 From: Marijn Haverbeke Date: Thu, 4 Oct 2012 12:52:09 +0200 Subject: [PATCH 0188/5780] [keymap/vim] Stop using .forEach on arrays --- keymap/vim.js | 19 +++++++++---------- 1 file changed, 9 insertions(+), 10 deletions(-) diff --git a/keymap/vim.js b/keymap/vim.js index 429fb4e161..28ec4fd8cf 100644 --- a/keymap/vim.js +++ b/keymap/vim.js @@ -318,13 +318,12 @@ }; // standard mode switching - iterList(["d", "t", "T", "f", "F", "c", "r"], - function (ch) { - CodeMirror.keyMap.vim[toCombo(ch)] = function (cm) { - cm.setOption("keyMap", "vim-prefix-" + ch); - emptyBuffer(); - }; - }); + iterList(["d", "t", "T", "f", "F", "c", "r"], function (ch) { + CodeMirror.keyMap.vim[toCombo(ch)] = function (cm) { + cm.setOption("keyMap", "vim-prefix-" + ch); + emptyBuffer(); + }; + }); function addCountBindings(keyMap) { // Add bindings for number keys @@ -645,7 +644,7 @@ }; // Map our movement actions each operator and non-operational movement - motionList.forEach(function(key, index, array) { + iterList(motionList, function(key, index, array) { CodeMirror.keyMap['vim-prefix-d'][key] = function(cm) { // Get our selected range var start = cm.getCursor(); @@ -695,7 +694,7 @@ }); var nums = [1,2,3,4,5,6,7,8,9]; - nums.forEach(function(key, index, array) { + iterList(nums, function(key, index, array) { CodeMirror.keyMap['vim'][key] = function (cm) { reptTimes = (reptTimes * 10) + key; }; @@ -713,7 +712,7 @@ // Create our keymaps for each operator and make xa and xi where x is an operator // change to the corrosponding keymap var operators = ['d', 'y', 'c']; - operators.forEach(function(key, index, array) { + iterList(operators, function(key, index, array) { CodeMirror.keyMap['vim-prefix-'+key+'a'] = { auto: 'vim', nofallthrough: true, style: "fat-cursor" }; From e4f379326161525a269c8dfb6a23c09f202725a4 Mon Sep 17 00:00:00 2001 From: Marijn Haverbeke Date: Thu, 4 Oct 2012 13:09:58 +0200 Subject: [PATCH 0189/5780] Fail a test in IE7 And ensure we don't set cursor height to something negative. Closes #864 --- lib/codemirror.js | 2 +- test/test.js | 2 +- 2 files changed, 2 insertions(+), 2 deletions(-) diff --git a/lib/codemirror.js b/lib/codemirror.js index 0010a2689f..757b542b7f 100644 --- a/lib/codemirror.js +++ b/lib/codemirror.js @@ -582,7 +582,7 @@ window.CodeMirror = (function() { var pos = headPos = cursorCoords(cm, sel.from, "div"); display.cursor.style.left = pos.left + "px"; display.cursor.style.top = pos.top + "px"; - display.cursor.style.height = (pos.bottom - pos.top) * cm.options.cursorHeight + "px"; + display.cursor.style.height = Math.max(0, pos.bottom - pos.top) * cm.options.cursorHeight + "px"; display.cursor.style.display = ""; display.selectionDiv.style.display = "none"; diff --git a/test/test.js b/test/test.js index e8e37f7661..06b03ebee7 100644 --- a/test/test.js +++ b/test/test.js @@ -387,7 +387,7 @@ testCM("selectionPos", function(cm) { } } is(sawTop && sawBottom && sawMiddle, "all parts"); -}, null); +}, null, ie_lt8); testCM("restoreHistory", function(cm) { cm.setValue("abc\ndef"); From 15d910af901014f86b9a3c4adc86272ad9f0d6d9 Mon Sep 17 00:00:00 2001 From: Marijn Haverbeke Date: Thu, 4 Oct 2012 13:14:35 +0200 Subject: [PATCH 0190/5780] Set pre elements to overflow: visible Some sites set them to auto, which messes up our cursor (and probably more) --- lib/codemirror.css | 1 + 1 file changed, 1 insertion(+) diff --git a/lib/codemirror.css b/lib/codemirror.css index 3cce6998c2..9be4b00570 100644 --- a/lib/codemirror.css +++ b/lib/codemirror.css @@ -175,6 +175,7 @@ div.CodeMirror span.CodeMirror-nonmatchingbracket {color: #f22;} color: inherit; z-index: 2; position: relative; + overflow: visible; } .CodeMirror-wrap pre { word-wrap: break-word; From 7948d8cce3a827286146d8a2abbef29588f29ac7 Mon Sep 17 00:00:00 2001 From: Marijn Haverbeke Date: Mon, 15 Oct 2012 10:06:20 +0200 Subject: [PATCH 0191/5780] [util/simple-hint] Align completion dropdown with completed word Add alignWithWord option to be able to turn that off. --- lib/util/simple-hint.js | 5 +++-- 1 file changed, 3 insertions(+), 2 deletions(-) diff --git a/lib/util/simple-hint.js b/lib/util/simple-hint.js index 04ae4af8b5..d6f588e8bb 100644 --- a/lib/util/simple-hint.js +++ b/lib/util/simple-hint.js @@ -44,7 +44,7 @@ } sel.firstChild.selected = true; sel.size = Math.min(10, completions.length); - var pos = editor.cursorCoords(); + var pos = editor.cursorCoords(options.alignWithWord ? result.from : null); complete.style.left = pos.left + "px"; complete.style.top = pos.bottom + "px"; document.body.appendChild(complete); @@ -96,6 +96,7 @@ CodeMirror.simpleHint.defaults = { closeOnBackspace: true, closeOnTokenChange: false, - completeSingle: true + completeSingle: true, + alignWithWord: true }; })(); From 8531034a481274f95b050c1241e47cc1fb3021ad Mon Sep 17 00:00:00 2001 From: Marijn Haverbeke Date: Mon, 15 Oct 2012 11:31:32 +0200 Subject: [PATCH 0192/5780] Work around IE7 bogus clientWidth --- lib/codemirror.js | 2 +- test/test.js | 2 +- 2 files changed, 2 insertions(+), 2 deletions(-) diff --git a/lib/codemirror.js b/lib/codemirror.js index 757b542b7f..a985b36d41 100644 --- a/lib/codemirror.js +++ b/lib/codemirror.js @@ -595,7 +595,7 @@ window.CodeMirror = (function() { } else { headPos = cursorCoords(cm, selHead(cm.view), "div"); var fragment = document.createDocumentFragment(); - var clientWidth = display.lineSpace.clientWidth; + var clientWidth = display.lineSpace.offsetWidth; var add = function(left, top, width, bottom) { if (top < 0) top = 0; fragment.appendChild(elt("div", null, "CodeMirror-selected", "position: absolute; left: " + left + diff --git a/test/test.js b/test/test.js index 06b03ebee7..e8e37f7661 100644 --- a/test/test.js +++ b/test/test.js @@ -387,7 +387,7 @@ testCM("selectionPos", function(cm) { } } is(sawTop && sawBottom && sawMiddle, "all parts"); -}, null, ie_lt8); +}, null); testCM("restoreHistory", function(cm) { cm.setValue("abc\ndef"); From eae22b7c18509d8687ae02449855b0dd88c60392 Mon Sep 17 00:00:00 2001 From: Marijn Haverbeke Date: Mon, 15 Oct 2012 11:45:20 +0200 Subject: [PATCH 0193/5780] Reorder operation in measureLine to save one relayout --- lib/codemirror.js | 5 +++-- 1 file changed, 3 insertions(+), 2 deletions(-) diff --git a/lib/codemirror.js b/lib/codemirror.js index a985b36d41..a0ae5cbcfb 100644 --- a/lib/codemirror.js +++ b/lib/codemirror.js @@ -756,12 +756,13 @@ window.CodeMirror = (function() { var atEnd = ch && ch == line.text.length; var pre = lineContent(cm, line, atEnd ? ch - 1 : ch); removeChildrenAndAdd(display.measure, pre); - var anchor = pre.anchor, outer = display.lineDiv.getBoundingClientRect(); + var anchor = pre.anchor; // We'll sample once at the top, once at the bottom of the line, // to get the real line height (in case there tokens on the line // with bigger fonts) anchor.style.verticalAlign = "top"; - var box1 = anchor.getBoundingClientRect(), left = box1.left - outer.left, right = box1.right - outer.left; + var outer = display.lineDiv.getBoundingClientRect(), box1 = anchor.getBoundingClientRect(); + var left = box1.left - outer.left, right = box1.right - outer.left; if (ie) { var left1 = anchor.offsetLeft; // In IE, verticalAlign does not influence offsetTop, unless From 808cee080b43214dfe08b77e11db5577e845e9f0 Mon Sep 17 00:00:00 2001 From: Marijn Haverbeke Date: Mon, 15 Oct 2012 12:33:10 +0200 Subject: [PATCH 0194/5780] Fix drag/drop on IE9 Closes #857 --- lib/codemirror.js | 9 +++++++-- 1 file changed, 7 insertions(+), 2 deletions(-) diff --git a/lib/codemirror.js b/lib/codemirror.js index a0ae5cbcfb..100ee6c4ac 100644 --- a/lib/codemirror.js +++ b/lib/codemirror.js @@ -1258,7 +1258,7 @@ window.CodeMirror = (function() { }); // Let the drag handler handle this. if (webkit) display.scroller.draggable = true; - view.draggingText = true; + view.draggingText = dragEnd; // IE's approach to draggable if (display.scroller.dragDrop) display.scroller.dragDrop(); on(document, "mouseup", dragEnd); @@ -1356,7 +1356,11 @@ window.CodeMirror = (function() { for (var i = 0; i < n; ++i) loadFile(files[i], i); } else { // Don't do a replace if the drop happened inside of the selected text. - if (cm.view.draggingText && !(posLess(pos, cm.view.sel.from) || posLess(cm.view.sel.to, pos))) return; + if (cm.view.draggingText && !(posLess(pos, cm.view.sel.from) || posLess(cm.view.sel.to, pos))) { + cm.view.draggingText(e); + if (ie) setTimeout(bind(focusInput, cm), 50); + return; + } try { var text = e.dataTransfer.getData("Text"); if (text) { @@ -1366,6 +1370,7 @@ window.CodeMirror = (function() { if (cm.view.draggingText) replaceRange(cm, "", curFrom, curTo); cm.replaceSelection(text); focusInput(cm); + onFocus(cm); }); } } From cb7df4cc505bdf22a4ef3499bada6b1ec807e79f Mon Sep 17 00:00:00 2001 From: Marijn Haverbeke Date: Mon, 15 Oct 2012 12:43:05 +0200 Subject: [PATCH 0195/5780] Fix reload-while-focused corruption problem in IE9 --- lib/codemirror.js | 3 +++ 1 file changed, 3 insertions(+) diff --git a/lib/codemirror.js b/lib/codemirror.js index 100ee6c4ac..4874d29ef4 100644 --- a/lib/codemirror.js +++ b/lib/codemirror.js @@ -66,6 +66,9 @@ window.CodeMirror = (function() { // Initialize the content. this.setValue(options.value || ""); + // Override magic textarea content restore that IE sometimes does + // on our hidden textarea on reload + if (ie) setTimeout(bind(resetInput, this, true), 20); doc.history = new History(); registerEventHandlers(this); From a925cd2e30e31343e4379b41ab14e7f2e295d8dd Mon Sep 17 00:00:00 2001 From: Marijn Haverbeke Date: Mon, 15 Oct 2012 13:10:25 +0200 Subject: [PATCH 0196/5780] Factor out DOM building for line into separate function --- lib/codemirror.js | 135 +++++++++++++++++++++++----------------------- 1 file changed, 69 insertions(+), 66 deletions(-) diff --git a/lib/codemirror.js b/lib/codemirror.js index 4874d29ef4..13fe818c56 100644 --- a/lib/codemirror.js +++ b/lib/codemirror.js @@ -499,80 +499,83 @@ window.CodeMirror = (function() { } // This pass fills in the lines that actually changed. var nextIntact = intact.shift(), curNode = display.lineDiv.firstChild; - var j = from, gutterSpecs = cm.options.gutters; - var fixedPos = compensateForHScroll(display); + var j = from, fixedPos = compensateForHScroll(display); cm.view.doc.iter(from, to, function(line) { if (nextIntact && nextIntact.to == j) nextIntact = intact.shift(); - if (!nextIntact || nextIntact.from > j) { - if (line.hidden && (!line.hidden.handle.showWidgets || !line.widgets)) { - var lineElement = elt("div"); - } else { - var lineElement = line.hidden ? elt("div") : lineContent(cm, line); - var markers = line.gutterMarkers; - if (line.className) lineElement.className = line.className; - // Lines with gutter elements or a background class need - // to be wrapped again, and have the extra elements added - - // to the wrapper div - if (lineNumbers || markers || line.bgClassName || (line.widgets && line.widgets.length)) { - var wrap = elt("div", null, null, "position: relative"); - if (lineNumbers || markers) { - var gutterWrap = wrap.appendChild(elt("div", null, null, - "position: absolute; left: " + fixedPos + "px")); - wrap.alignable = [gutterWrap]; - if (lineNumbers) - gutterWrap.appendChild(elt("div", lineNumberFor(cm.options, j), - "CodeMirror-linenumber CodeMirror-gutter-elt", - "left: " + display.lineGutter.offsetLeft + "px; width: " - + display.lineNumInnerWidth + "px")); - if (markers) - for (var k = 0; k < gutterSpecs.length; ++k) { - var id = gutterSpecs[k], found = markers.hasOwnProperty(id) && markers[id]; - if (found) { - var gutterElt = display.gutters.childNodes[k]; - gutterWrap.appendChild(elt("div", [found], "CodeMirror-gutter-elt", "left: " + - gutterElt.offsetLeft + "px; width: " + gutterElt.clientWidth + "px")); - } - } - } - // Kludge to make sure the styled element lies behind the selection (by z-index) - if (line.bgClassName) - wrap.appendChild(elt("div", "\u00a0", line.bgClassName + " CodeMirror-linebackground")); - wrap.appendChild(lineElement); - if (line.widgets) - for (var i = 0, ws = line.widgets; i < ws.length; ++i) { - var widget = ws[i], node = elt("div", [widget.node], "CodeMirror-linewidget"); - node.widget = widget; - if (widget.noHScroll) { - (wrap.alignable || (wrap.alignable = [])).push(node); - var width = display.wrapper.clientWidth; - node.style.left = fixedPos + "px"; - if (!widget.coverGutter) { - width -= display.gutters.offsetWidth; - node.style.paddingLeft = display.gutters.offsetWidth + "px"; - } - node.style.width = width + "px"; - } - if (widget.coverGutter) { - node.style.zIndex = 5; - node.style.position = "relative"; - if (!widget.noHScroll) node.style.marginLeft = -display.gutters.offsetWidth + "px"; - } - wrap.appendChild(node); - } - lineElement = wrap; - if (ie_lt8) lineElement.style.zIndex = 2; - } - } - display.lineDiv.insertBefore(lineElement, curNode); - } else { + if (!nextIntact || nextIntact.from > j) + display.lineDiv.insertBefore(buildLineElement(cm, line, j, fixedPos), curNode); + else curNode = curNode.nextSibling; - } ++j; }); } + function buildLineElement(cm, line, lineNo, fixedPos) { + if (line.hidden && (!line.hidden.handle.showWidgets || !line.widgets)) + return elt("div"); + + var lineElement = line.hidden ? elt("div") : lineContent(cm, line); + var markers = line.gutterMarkers, display = cm.display; + if (line.className) lineElement.className = line.className; + + if (!cm.options.lineNumbers && !markers && !line.bgClassName && + (!line.widgets || !line.widgets.length)) return lineElement; + + // Lines with gutter elements or a background class need + // to be wrapped again, and have the extra elements added + // to the wrapper div + + var wrap = elt("div", null, null, "position: relative"); + if (cm.options.lineNumbers || markers) { + var gutterWrap = wrap.appendChild(elt("div", null, null, "position: absolute; left: " + + fixedPos + "px")); + wrap.alignable = [gutterWrap]; + if (cm.options.lineNumbers) + gutterWrap.appendChild(elt("div", lineNumberFor(cm.options, lineNo), + "CodeMirror-linenumber CodeMirror-gutter-elt", + "left: " + display.lineGutter.offsetLeft + "px; width: " + + display.lineNumInnerWidth + "px")); + if (markers) + for (var k = 0; k < cm.options.gutters.length; ++k) { + var id = cm.options.gutters[k], found = markers.hasOwnProperty(id) && markers[id]; + if (found) { + var gutterElt = display.gutters.childNodes[k]; + gutterWrap.appendChild(elt("div", [found], "CodeMirror-gutter-elt", "left: " + + gutterElt.offsetLeft + "px; width: " + gutterElt.clientWidth + "px")); + } + } + } + // Kludge to make sure the styled element lies behind the selection (by z-index) + if (line.bgClassName) + wrap.appendChild(elt("div", "\u00a0", line.bgClassName + " CodeMirror-linebackground")); + wrap.appendChild(lineElement); + if (line.widgets) + for (var i = 0, ws = line.widgets; i < ws.length; ++i) { + var widget = ws[i], node = elt("div", [widget.node], "CodeMirror-linewidget"); + node.widget = widget; + if (widget.noHScroll) { + (wrap.alignable || (wrap.alignable = [])).push(node); + var width = display.wrapper.clientWidth; + node.style.left = fixedPos + "px"; + if (!widget.coverGutter) { + width -= display.gutters.offsetWidth; + node.style.paddingLeft = display.gutters.offsetWidth + "px"; + } + node.style.width = width + "px"; + } + if (widget.coverGutter) { + node.style.zIndex = 5; + node.style.position = "relative"; + if (!widget.noHScroll) node.style.marginLeft = -display.gutters.offsetWidth + "px"; + } + wrap.appendChild(node); + } + + if (ie_lt8) wrap.style.zIndex = 2; + return wrap; + } + // SELECTION / CURSOR function selHead(view) { From 73336a64a5c24e23990adb15719044125763bda0 Mon Sep 17 00:00:00 2001 From: Marijn Haverbeke Date: Mon, 15 Oct 2012 13:27:22 +0200 Subject: [PATCH 0197/5780] Allow line widgets to be displayed above the line's text Closes #875 --- doc/manual.html | 3 +++ lib/codemirror.js | 26 ++++++++++++++++++++------ 2 files changed, 23 insertions(+), 6 deletions(-) diff --git a/doc/manual.html b/doc/manual.html index 67c7b23d41..79ab656322 100644 --- a/doc/manual.html +++ b/doc/manual.html @@ -836,6 +836,9 @@

    Programming API

    noHScroll (boolean)
    Whether the widget should stay fixed in the face of horizontal scrolling.
    +
    above (boolean)
    +
    Causes the widget to be placed above instead of below + the text of the line.
    Note that the widget node will become a descendant of nodes with CodeMirror-specific CSS classes, and those classes might in some diff --git a/lib/codemirror.js b/lib/codemirror.js index 13fe818c56..f53651087e 100644 --- a/lib/codemirror.js +++ b/lib/codemirror.js @@ -569,7 +569,10 @@ window.CodeMirror = (function() { node.style.position = "relative"; if (!widget.noHScroll) node.style.marginLeft = -display.gutters.offsetWidth + "px"; } - wrap.appendChild(node); + if (widget.above) + wrap.insertBefore(node, lineNumbers && !line.hidden ? gutterWrap : lineElement); + else + wrap.appendChild(node); } if (ie_lt8) wrap.style.zIndex = 2; @@ -800,7 +803,11 @@ window.CodeMirror = (function() { } // Context is one of "line", "div" (display.lineDiv), "local"/null (editor), or "page" - function intoCoordSystem(cm, pos, rect, context) { + function intoCoordSystem(cm, lineObj, pos, rect, context) { + if (lineObj.widgets) for (var i = 0; i < lineObj.widgets.length; ++i) if (lineObj.widgets[i].above) { + var size = lineObj.widgets[i].node.offsetHeight; + rect.top += size; rect.bottom += size; + } if (context == "line") return rect; if (!context) context = "local"; var yOff = heightAtLine(cm.view.doc, pos.line); @@ -817,7 +824,7 @@ window.CodeMirror = (function() { function charCoords(cm, pos, context, lineObj) { if (!lineObj) lineObj = getLine(cm.view.doc, pos.line); - return intoCoordSystem(cm, pos, measureLine(cm, lineObj, pos.ch), context); + return intoCoordSystem(cm, lineObj, pos, measureLine(cm, lineObj, pos.ch), context); } function cursorCoords(cm, pos, context, lineObj) { @@ -825,7 +832,7 @@ window.CodeMirror = (function() { function get(ch, right) { var m = measureLine(cm, lineObj, ch); if (right) m.left = m.right; else m.right = m.left; - return intoCoordSystem(cm, pos, m, context); + return intoCoordSystem(cm, lineObj, pos, m, context); } var order = getOrder(lineObj), ch = pos.ch; if (!order) return get(ch); @@ -2405,8 +2412,15 @@ window.CodeMirror = (function() { if (dir > 0 && target.line == cur.line && cur.line < doc.size - 1 && getLine(doc, cur.line).widgets && Math.abs(cursorCoords(this, target, "div").top - pos.top) < 2) target = coordsChar(this, x, cursorCoords(this, {line: cur.line + 1, ch: 0}, "div").top + 3); - else if (dir < 0 && cur.line > 0 && (line = getLine(doc, target.line)).widgets && target.ch == line.text.length) - target = coordsChar(this, x, cursorCoords(this, {line: target.line, ch: line.text.length}, "div").bottom - 3); + else if (dir < 0 && cur.line > 0 && (line = getLine(doc, target.line)).widgets) { + var above, below; + for (var i = 0; i < line.widgets.length; ++i) + if (line.widgets[i].above) above = true; else below = true; + if (below && target.ch == line.text.length) + target = coordsChar(this, x, cursorCoords(this, {line: target.line, ch: line.text.length}, "div").bottom - 3); + else if (above && posEq(target, cur)) + target = coordsChar(this, x, heightAtLine(doc, target.line) - 3); + } if (unit == "page") display.scrollbarV.scrollTop += charCoords(this, target, "div").top - pos.top; setSelectionUser(this, target, target); From 87cd5b3451e445ba21c19cc9b65a97f7a1ebb182 Mon Sep 17 00:00:00 2001 From: Brandon Frohs Date: Thu, 4 Oct 2012 23:09:02 -0400 Subject: [PATCH 0198/5780] [css] Fix class matching. This basically matches the CSS3 spec (except it doesn't match non-ascii characters). --- mode/css/css.js | 2 +- mode/css/test.js | 4 ++-- 2 files changed, 3 insertions(+), 3 deletions(-) diff --git a/mode/css/css.js b/mode/css/css.js index 5e3e233edb..87d5d7401e 100644 --- a/mode/css/css.js +++ b/mode/css/css.js @@ -228,7 +228,7 @@ CodeMirror.defineMode("css", function(config) { else if (/[,+>*\/]/.test(ch)) { return ret(null, "select-op"); } - else if (ch == "." && stream.match(/^\w+/)) { + else if (ch == "." && stream.match(/^-?[_a-z][_a-z0-9-]*/i)) { return ret("qualifier", type); } else if (ch == ":") { diff --git a/mode/css/test.js b/mode/css/test.js index 4e2d0e8e56..fd6a4b8aa8 100644 --- a/mode/css/test.js +++ b/mode/css/test.js @@ -217,9 +217,9 @@ MT.testMode( MT.testMode( 'classSelector', - '.foo { }', + '.foo-bar_hello { }', [ - 'qualifier', '.foo', + 'qualifier', '.foo-bar_hello', null, ' { }' ] ); From 91bfd16558f6683aba696c8499176741f49e142f Mon Sep 17 00:00:00 2001 From: Marijn Haverbeke Date: Mon, 15 Oct 2012 13:40:21 +0200 Subject: [PATCH 0199/5780] Place cursor elements after lines in DOM So that, even though they share a z-index, they overlay the text and marked spans. Closes #883 --- lib/codemirror.js | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/lib/codemirror.js b/lib/codemirror.js index f53651087e..9db44aa79e 100644 --- a/lib/codemirror.js +++ b/lib/codemirror.js @@ -105,7 +105,7 @@ window.CodeMirror = (function() { // Used to measure text size d.measure = elt("div", null, "CodeMirror-measure"); // Wraps everything that needs to exist inside the vertically-padded coordinate system - d.lineSpace = elt("div", [d.measure, d.cursor, d.otherCursor, d.selectionDiv, d.lineDiv], + d.lineSpace = elt("div", [d.measure, d.selectionDiv, d.lineDiv, d.cursor, d.otherCursor], null, "position: relative; outline: none"); // Moved around its parent to cover visible view d.mover = elt("div", [elt("div", [d.lineSpace], "CodeMirror-lines")], null, "position: relative"); From 0597b7a21803aad300d124f315dc2614dd1cf21d Mon Sep 17 00:00:00 2001 From: Marijn Haverbeke Date: Mon, 15 Oct 2012 14:19:35 +0200 Subject: [PATCH 0200/5780] Don't mutated makedSpans arrays And add marker changes to the undo history, so that un/redoing doesn't cause 'ghost' markers to appear. Closes #882 --- lib/codemirror.js | 47 +++++++++++++++++++++++++++++------------------ test/test.js | 20 ++++++++++++++------ 2 files changed, 43 insertions(+), 24 deletions(-) diff --git a/lib/codemirror.js b/lib/codemirror.js index 9db44aa79e..8588c8645f 100644 --- a/lib/codemirror.js +++ b/lib/codemirror.js @@ -2142,16 +2142,18 @@ window.CodeMirror = (function() { var marker = new TextMarker(this, "range", className); if (options) for (var opt in options) if (options.hasOwnProperty(opt)) marker[opt] = options[opt]; - var curLine = from.line; + var curLine = from.line, old = []; doc.iter(curLine, to.line + 1, function(line) { var span = {from: curLine == from.line ? from.ch : null, to: curLine == to.line ? to.ch : null, marker: marker}; - (line.markedSpans || (line.markedSpans = [])).push(span); + old.push(newHL(line.text, line.markedSpans)); + line.markedSpans = (line.markedSpans || []).concat([span]); marker.lines.push(line); ++curLine; }); regChange(this, from.line, to.line + 1); + doc.history.addChange(from.line, old.length, old, true); return marker; }), @@ -2159,9 +2161,11 @@ window.CodeMirror = (function() { var doc = this.view.doc; pos = clipPos(doc, pos); var marker = new TextMarker(this, "bookmark"), line = getLine(doc, pos.line); + var old = newHL(line.text, line.markedSpans); var span = {from: pos.ch, to: pos.ch, marker: marker}; - (line.markedSpans || (line.markedSpans = [])).push(span); + line.markedSpans = (line.markedSpans || []).concat([span]); marker.lines.push(line); + doc.history.addChange(pos.line, 1, [old]); return marker; }, @@ -2887,16 +2891,21 @@ window.CodeMirror = (function() { TextMarker.prototype.clear = function() { startOperation(this.cm); - var min = Infinity, max = -Infinity; + var min = null, max = null, seen = {}; for (var i = 0; i < this.lines.length; ++i) { - var line = this.lines[i]; - var span = getMarkedSpanFor(line.markedSpans, this, true); - if (span.from != null || span.to != null) { - var lineN = lineNo(line); - min = Math.min(min, lineN); max = Math.max(max, lineN); - } + var line = this.lines[i], lineN = lineNo(line); + seen[lineN] = newHL(line.text, line.markedSpans); + var span = getMarkedSpanFor(line.markedSpans, this); + if (span.from != null) min = lineN; + if (span.to != null) max = lineN; + line.markedSpans = removeMarkedSpan(line.markedSpans, span); + } + if (min != null) { + regChange(this.cm, min, max + 1); + var old = []; + for (var i = min; i <= max; ++i) old.push(seen[i]); + this.cm.view.doc.history.addChange(min, old.length, old, true); } - if (min != Infinity) regChange(this.cm, min, max + 1); this.lines.length = 0; endOperation(this.cm); }; @@ -2918,15 +2927,17 @@ window.CodeMirror = (function() { // TEXTMARKER SPANS - function getMarkedSpanFor(spans, marker, del) { + function getMarkedSpanFor(spans, marker) { if (spans) for (var i = 0; i < spans.length; ++i) { var span = spans[i]; - if (span.marker == marker) { - if (del) spans.splice(i, 1); - return span; - } + if (span.marker == marker) return span; } } + function removeMarkedSpan(spans, span) { + for (var r, i = 0; i < spans.lenght; ++i) + if (spans[i] == span) (r || (r = [])).push(spans[i]); + return r; + } function markedSpansBefore(old, startCh, endCh) { if (old) for (var i = 0, nw; i < old.length; ++i) { @@ -3471,12 +3482,12 @@ window.CodeMirror = (function() { this.closed = false; } History.prototype = { - addChange: function(start, added, old) { + addChange: function(start, added, old, minor) { this.undone.length = 0; var time = +new Date, cur = lst(this.done), last = cur && lst(cur); var dtime = time - this.time; - if (this.compound && cur && !this.closed) { + if (cur && !this.closed && (this.compound || minor)) { cur.push({start: start, added: added, old: old}); } else if (dtime > 400 || !last || this.closed || last.start > start + old.length || last.start + last.added < start) { diff --git a/test/test.js b/test/test.js index e8e37f7661..8da4d78889 100644 --- a/test/test.js +++ b/test/test.js @@ -293,20 +293,28 @@ testCM("markTextMultiLine", function(cm) { }); testCM("markTextUndo", function(cm) { - var marker1 = cm.markText({line: 0, ch: 1}, {line: 0, ch: 3}, "CodeMirror-matchingbracket"); - var marker2 = cm.markText({line: 0, ch: 0}, {line: 2, ch: 1}, "CodeMirror-matchingbracket"); - var bookmark = cm.setBookmark({line: 1, ch: 5}); - cm.replaceRange("foo", {line: 0, ch: 2}); - cm.replaceRange("bar\baz\bug\n", {line: 2, ch: 0}, {line: 3, ch: 0}); + var marker1, marker2, bookmark; + cm.compoundChange(function(){ + marker1 = cm.markText({line: 0, ch: 1}, {line: 0, ch: 3}, "CodeMirror-matchingbracket"); + marker2 = cm.markText({line: 0, ch: 0}, {line: 2, ch: 1}, "CodeMirror-matchingbracket"); + bookmark = cm.setBookmark({line: 1, ch: 5}); + }); + cm.compoundChange(function(){ + cm.replaceRange("foo", {line: 0, ch: 2}); + cm.replaceRange("bar\baz\bug\n", {line: 2, ch: 0}, {line: 3, ch: 0}); + }); cm.setValue(""); eq(marker1.find(), null); eq(marker2.find(), null); eq(bookmark.find(), null); cm.undo(); eqPos(bookmark.find(), {line: 1, ch: 5}); - cm.undo(); cm.undo(); + cm.undo(); var m1Pos = marker1.find(), m2Pos = marker2.find(); eqPos(m1Pos.from, {line: 0, ch: 1}); eqPos(m1Pos.to, {line: 0, ch: 3}); eqPos(m2Pos.from, {line: 0, ch: 0}); eqPos(m2Pos.to, {line: 2, ch: 1}); eqPos(bookmark.find(), {line: 1, ch: 5}); + marker1 = cm.markText({line: 0, ch: 0}, {line: 0, ch: 0}, "CodeMirror-matchingbracket"); + cm.undo(); + eq(marker1.find(), null); }, {value: "1234\n56789\n00\n"}); testCM("markClearBetween", function(cm) { From 98a927428498cb7e2b1c5a05ebc38ebe6b7cc87e Mon Sep 17 00:00:00 2001 From: Marijn Haverbeke Date: Mon, 15 Oct 2012 16:54:09 +0200 Subject: [PATCH 0201/5780] Try to catch scroll wheel events on an element that's not the main scroller By growing the vertical scrollbar to cover the whole editor when a wheel scroll is taking place. (This to avoid manually handling wheel events, for which there doesn't appear to be any way that feels native across browsers.) Closes #810 --- lib/codemirror.js | 35 +++++++++++++++++++++++++++++++++++ 1 file changed, 35 insertions(+) diff --git a/lib/codemirror.js b/lib/codemirror.js index 8588c8645f..f3ae57d21a 100644 --- a/lib/codemirror.js +++ b/lib/codemirror.js @@ -1124,6 +1124,10 @@ window.CodeMirror = (function() { d.scroller.scrollTop = cm.view.scrollTop = d.scrollbarV.scrollTop; updateDisplay(cm, []); } + if (d.scrollbarV.style.overflowX && d.scrollbarV.scrollLeft != cm.view.scrollLeft) { + d.scroller.scrollLeft = cm.view.scrollLeft = d.scrollbarV.scrollLeft; + alignVertically(cm.display); + } }); on(d.scrollbarH, "scroll", function() { if (d.scrollbarH.scrollLeft != cm.view.scrollLeft) { @@ -1132,6 +1136,37 @@ window.CodeMirror = (function() { } }); + // Kludge to prevent long wheel scrolls from causing flicker -- if + // they scroll the scroller div directly, we won't have a chance + // to update the content before the scroll takes place, which may + // cause blank screen area to come into view. This makes sure the + // vertical scrollbar grows to fill the whole editor during a + // wheel scroll. + var scrollbarReset = new Delayed(), active = false; + function growScrollbarV() { + var bar = d.scrollbarV; + if (!active) { + active = true; + if (bar.style.display == "none") return; + bar.style.left = d.gutters.offsetWidth + "px"; + if (d.scrollbarH.style.display == "block") { + bar.firstChild.style.width = (d.scroller.scrollWidth - d.scroller.clientWidth + bar.clientWidth) + "px"; + bar.scrollLeft = d.scrollbarH.scrollLeft; + bar.style.marginBottom = (-scrollbarWidth(d.measure)) + "px"; + bar.style.overflowX = "scroll"; + } + } + scrollbarReset.set(150, function() { + bar.style.left = bar.style.overflowX = + bar.firstChild.style.width = bar.style.marginBottom = ""; + active = false; + }); + } + on(d.scroller, "mousewheel", growScrollbarV); + on(d.scrollbarV, "mousewheel", growScrollbarV); + on(d.scroller, "DOMMouseScroll", growScrollbarV); + on(d.scrollbarV, "DOMMouseScroll", growScrollbarV); + function reFocus() { if (cm.view.focused) setTimeout(bind(focusInput, cm), 0); } on(d.scrollbarH, "mousedown", reFocus); on(d.scrollbarV, "mousedown", reFocus); From b31f73e98c36aca38b0af2b0d8f6a49905e4f8d4 Mon Sep 17 00:00:00 2001 From: Marijn Haverbeke Date: Mon, 15 Oct 2012 17:57:14 +0200 Subject: [PATCH 0202/5780] Slight scrolling optimization Issue #881 --- lib/codemirror.js | 17 +++++++---------- 1 file changed, 7 insertions(+), 10 deletions(-) diff --git a/lib/codemirror.js b/lib/codemirror.js index f3ae57d21a..8ab124f6f9 100644 --- a/lib/codemirror.js +++ b/lib/codemirror.js @@ -427,8 +427,7 @@ window.CodeMirror = (function() { // Update line heights for visible lines based on actual DOM // sizes - var curNode = display.lineDiv.firstChild, heightChanged = false; - var relativeTo = curNode.offsetTop; + var curNode = display.lineDiv.firstChild, relativeTo = curNode.offsetTop; doc.iter(display.showingFrom, display.showingTo, function(line) { // Work around bizarro IE7 bug where, sometimes, our curNode // is magically replaced with a new node in the DOM, leaving @@ -439,10 +438,8 @@ window.CodeMirror = (function() { var height = end - relativeTo, diff = line.height - height; if (height < 2) height = textHeight(display); relativeTo = end; - if (diff > .001 || diff < -.001) { + if (diff > .001 || diff < -.001) updateLineHeight(line, height); - heightChanged = true; - } } curNode = curNode.nextSibling; }); @@ -694,7 +691,7 @@ window.CodeMirror = (function() { var end = +new Date + cm.options.workTime; var state = copyState(doc.mode, getStateBefore(doc, doc.frontier)); var startFrontier = doc.frontier; - doc.iter(doc.frontier, cm.display.showingTo, function(line) { + doc.iter(doc.frontier, Math.min(doc.size, cm.display.showingTo + 500), function(line) { if (doc.frontier >= cm.display.showingFrom) { // Visible line.highlight(doc.mode, state, cm.options.tabSize); line.stateAfter = copyState(doc.mode, state); @@ -1110,8 +1107,8 @@ window.CodeMirror = (function() { on(d.scroller, "scroll", function() { if (d.scroller.scrollTop != cm.view.scrollTop) { - d.scrollbarV.scrollTop = cm.view.scrollTop = d.scroller.scrollTop; - updateDisplay(cm, []); + updateDisplay(cm, [], cm.view.scrollTop = d.scroller.scrollTop); + d.scrollbarV.scrollTop = cm.view.scrollTop; } if (d.scroller.scrollLeft != cm.view.scrollLeft) { d.scrollbarH.scrollLeft = cm.view.scrollLeft = d.scroller.scrollLeft; @@ -1121,8 +1118,8 @@ window.CodeMirror = (function() { }); on(d.scrollbarV, "scroll", function() { if (d.scrollbarV.scrollTop != cm.view.scrollTop) { - d.scroller.scrollTop = cm.view.scrollTop = d.scrollbarV.scrollTop; - updateDisplay(cm, []); + updateDisplay(cm, [], cm.view.scrollTop = d.scrollbarV.scrollTop); + d.scroller.scrollTop = cm.view.scrollTop; } if (d.scrollbarV.style.overflowX && d.scrollbarV.scrollLeft != cm.view.scrollLeft) { d.scroller.scrollLeft = cm.view.scrollLeft = d.scrollbarV.scrollLeft; From cd387d7018c4057a15ac036644024165000aa785 Mon Sep 17 00:00:00 2001 From: ks-ifware Date: Thu, 20 Sep 2012 17:03:02 -0300 Subject: [PATCH 0203/5780] Fixed multi-line Lua comment recognition Multi-line Lua comments start with "--[[", not "--[" --- mode/lua/lua.js | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/mode/lua/lua.js b/mode/lua/lua.js index 60e84a9264..97fb2c6f96 100644 --- a/mode/lua/lua.js +++ b/mode/lua/lua.js @@ -64,7 +64,7 @@ CodeMirror.defineMode("lua", function(config, parserConfig) { function normal(stream, state) { var ch = stream.next(); if (ch == "-" && stream.eat("-")) { - if (stream.eat("[")) + if (stream.eat("[") && stream.eat("[")) return (state.cur = bracketed(readBracket(stream), "comment"))(stream, state); stream.skipToEnd(); return "comment"; From 705b0a7561e4e9f43a001e2e2411577f2de2c87c Mon Sep 17 00:00:00 2001 From: Brandon Frohs Date: Sat, 13 Oct 2012 16:21:30 -0300 Subject: [PATCH 0204/5780] Fix typo in manual. --- doc/manual.html | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/doc/manual.html b/doc/manual.html index 79ab656322..119c5a0cea 100644 --- a/doc/manual.html +++ b/doc/manual.html @@ -1240,7 +1240,7 @@

    Writing CodeMirror Modes

    state.

    If you want your mode to provide smart indentation - (though the indentLine + (through the indentLine method and the indentAuto and newlineAndIndent commands, which keys can be bound to), you must define From 60192bb393fbb33ea0d9b370d47dbe7c6ef1d04d Mon Sep 17 00:00:00 2001 From: Marijn Haverbeke Date: Mon, 15 Oct 2012 21:46:15 +0200 Subject: [PATCH 0205/5780] Fix typo (lenght) --- lib/codemirror.js | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/lib/codemirror.js b/lib/codemirror.js index 8ab124f6f9..bcabff735c 100644 --- a/lib/codemirror.js +++ b/lib/codemirror.js @@ -2966,7 +2966,7 @@ window.CodeMirror = (function() { } } function removeMarkedSpan(spans, span) { - for (var r, i = 0; i < spans.lenght; ++i) + for (var r, i = 0; i < spans.length; ++i) if (spans[i] == span) (r || (r = [])).push(spans[i]); return r; } From fb582266ddf4d55a329702cec74b7794b3dd2b8f Mon Sep 17 00:00:00 2001 From: Marijn Haverbeke Date: Tue, 16 Oct 2012 10:46:30 +0200 Subject: [PATCH 0206/5780] More robust vertical movement Detect when new position falls outside of a line, and retry. Also replaces the kludgy widget compensation code. Closes #886 --- lib/codemirror.js | 40 ++++++++++++++-------------------------- 1 file changed, 14 insertions(+), 26 deletions(-) diff --git a/lib/codemirror.js b/lib/codemirror.js index bcabff735c..5f9beaf432 100644 --- a/lib/codemirror.js +++ b/lib/codemirror.js @@ -864,22 +864,20 @@ window.CodeMirror = (function() { function coordsChar(cm, x, y) { var display = cm.display, doc = cm.view.doc; var cw = charWidth(display), heightPos = display.viewOffset + y; - if (heightPos < 0) return {line: 0, ch: 0}; + if (heightPos < 0) return {line: 0, ch: 0, outside: true}; var lineNo = lineAtHeight(doc, heightPos); if (lineNo >= doc.size) return {line: doc.size - 1, ch: getLine(doc, doc.size - 1).text.length}; var lineObj = getLine(doc, lineNo); if (!lineObj.text.length) return {line: lineNo, ch: 0}; - var tw = cm.options.lineWrapping, innerOff = tw ? heightPos - heightAtLine(doc, lineNo) : 0; + var innerOff = heightPos - heightAtLine(doc, lineNo); if (x < 0) x = 0; - var wrongLine = false; + var wrongLine = false, cWidth = display.wrapper.clientWidth; function getX(ch) { var sp = cursorCoords(cm, {line: lineNo, ch: ch}, "line", lineObj); - if (tw) { - wrongLine = true; - if (innerOff > sp.bottom) return Math.max(0, sp.left - display.wrapper.clientWidth); - else if (innerOff < sp.top) return sp.left + display.wrapper.clientWidth; - else wrongLine = false; - } + wrongLine = true; + if (innerOff > sp.bottom) return Math.max(0, sp.left - cWidth); + else if (innerOff < sp.top) return sp.left + cWidth; + else wrongLine = false; return sp.left; } var bidi = getOrder(lineObj), dist = lineObj.text.length; @@ -898,13 +896,13 @@ window.CodeMirror = (function() { if (estX < x) {from = estimated; fromX = estX;} dist = to - from; } else toX = getX(to); - if (x > toX) return {line: lineNo, ch: to}; + if (x > toX) return {line: lineNo, ch: to, outside: wrongLine}; // Do a binary search between these bounds. for (;;) { if (bidi ? to == from || to == moveVisually(lineObj, from, 1) : to - from <= 1) { var after = x - fromX < toX - x, ch = after ? from : to; while (isExtendingChar.test(lineObj.text.charAt(ch))) ++ch; - return {line: lineNo, ch: ch, after: after}; + return {line: lineNo, ch: ch, after: after, outside: wrongLine}; } var step = Math.ceil(dist / 2), middle = from + step; if (bidi) { @@ -2443,21 +2441,11 @@ window.CodeMirror = (function() { } else if (unit == "line") { y = dir > 0 ? pos.bottom + 3 : pos.top - 3; } - var target = coordsChar(this, x, y), line; - // Work around problem with moving 'through' line widgets - if (dir > 0 && target.line == cur.line && cur.line < doc.size - 1 && getLine(doc, cur.line).widgets && - Math.abs(cursorCoords(this, target, "div").top - pos.top) < 2) - target = coordsChar(this, x, cursorCoords(this, {line: cur.line + 1, ch: 0}, "div").top + 3); - else if (dir < 0 && cur.line > 0 && (line = getLine(doc, target.line)).widgets) { - var above, below; - for (var i = 0; i < line.widgets.length; ++i) - if (line.widgets[i].above) above = true; else below = true; - if (below && target.ch == line.text.length) - target = coordsChar(this, x, cursorCoords(this, {line: target.line, ch: line.text.length}, "div").bottom - 3); - else if (above && posEq(target, cur)) - target = coordsChar(this, x, heightAtLine(doc, target.line) - 3); - } - + do { + var target = coordsChar(this, x, y); + y += dir * 5; + } while (target.outside && (dir < 0 ? y > 0 : y < doc.height)); + if (unit == "page") display.scrollbarV.scrollTop += charCoords(this, target, "div").top - pos.top; setSelectionUser(this, target, target); view.goalColumn = x; From c1e9f3fdc48ea67b762fbcd8905fd3cce78fec7d Mon Sep 17 00:00:00 2001 From: Marijn Haverbeke Date: Tue, 16 Oct 2012 11:08:36 +0200 Subject: [PATCH 0207/5780] Stop using methods on lines Simply use top-level functions + a plain object. --- lib/codemirror.js | 439 +++++++++++++++++++++++----------------------- 1 file changed, 219 insertions(+), 220 deletions(-) diff --git a/lib/codemirror.js b/lib/codemirror.js index 5f9beaf432..df3b2e3a82 100644 --- a/lib/codemirror.js +++ b/lib/codemirror.js @@ -41,7 +41,7 @@ window.CodeMirror = (function() { if (options.autofocus) focusInput(this); if (options.lineWrapping) display.wrapper.className += " CodeMirror-wrap"; - var doc = new BranchChunk([new LeafChunk([new Line("", null, textHeight(display))])]); + var doc = new BranchChunk([new LeafChunk([makeLine("", null, textHeight(display))])]); // frontier is the point up to which the content has been parsed, doc.frontier = 0; doc.highlight = new Delayed(); @@ -689,14 +689,14 @@ window.CodeMirror = (function() { var doc = cm.view.doc; if (doc.frontier >= cm.display.showingTo) return; var end = +new Date + cm.options.workTime; - var state = copyState(doc.mode, getStateBefore(doc, doc.frontier)); + var state = copyState(doc.mode, getStateBefore(cm, doc.frontier)); var startFrontier = doc.frontier; doc.iter(doc.frontier, Math.min(doc.size, cm.display.showingTo + 500), function(line) { if (doc.frontier >= cm.display.showingFrom) { // Visible - line.highlight(doc.mode, state, cm.options.tabSize); + highlightLine(cm, line, state); line.stateAfter = copyState(doc.mode, state); } else { - line.process(doc.mode, state, cm.options.tabSize); + processLine(cm, line, state); line.stateAfter = doc.frontier % 5 == 0 ? copyState(doc.mode, state) : null; } ++doc.frontier; @@ -714,13 +714,13 @@ window.CodeMirror = (function() { // valid state. If that fails, it returns the line with the // smallest indentation, which tends to need the least context to // parse correctly. - function findStartLine(doc, n) { - var minindent, minline; + function findStartLine(cm, n) { + var minindent, minline, doc = cm.view.doc; for (var search = n, lim = n - 100; search > lim; --search) { if (search == 0) return 0; var line = getLine(doc, search-1); if (line.stateAfter) return search; - var indented = line.indentation(doc.tabSize); + var indented = countColumn(line.text, null, cm.options.tabSize); if (minline == null || minindent > indented) { minline = search - 1; minindent = indented; @@ -729,12 +729,13 @@ window.CodeMirror = (function() { return minline; } - function getStateBefore(doc, n) { - var pos = findStartLine(doc, n), state = pos && getLine(doc, pos-1).stateAfter; + function getStateBefore(cm, n) { + var doc = cm.view.doc; + var pos = findStartLine(cm, n), state = pos && getLine(doc, pos-1).stateAfter; if (!state) state = startState(doc.mode); else state = copyState(doc.mode, state); doc.iter(pos, n, function(line) { - line.process(doc.mode, state, doc.tabSize); + processLine(cm, line, state); line.stateAfter = (pos == n - 1 || pos % 5 == 0) ? copyState(doc.mode, state) : null; }); return state; @@ -1692,31 +1693,31 @@ window.CodeMirror = (function() { // sure line objects move the way they are supposed to. var added = [], prevLine = null; for (var i = 0, e = lines.length - 1; i < e; ++i) - added.push(new Line(hlText(lines[i]), hlSpans(lines[i]), th)); - lastLine.update(lastLine.text, hlSpans(lastHL), cm); + added.push(makeLine(hlText(lines[i]), hlSpans(lines[i]), th)); + updateLine(cm, lastLine, lastLine.text, hlSpans(lastHL)); if (nlines) doc.remove(from.line, nlines, cm); if (added.length) doc.insert(from.line, added); } else if (firstLine == lastLine) { if (lines.length == 1) { - firstLine.update(firstLine.text.slice(0, from.ch) + hlText(lines[0]) + firstLine.text.slice(to.ch), - hlSpans(lines[0]), cm); + updateLine(cm, firstLine, firstLine.text.slice(0, from.ch) + hlText(lines[0]) + + firstLine.text.slice(to.ch), hlSpans(lines[0])); } else { for (var added = [], i = 1, e = lines.length - 1; i < e; ++i) - added.push(new Line(hlText(lines[i]), hlSpans(lines[i]), th)); - added.push(new Line(hlText(lastHL) + firstLine.text.slice(to.ch), hlSpans(lastHL), th)); - firstLine.update(firstLine.text.slice(0, from.ch) + hlText(lines[0]), hlSpans(lines[0]), cm); + added.push(makeLine(hlText(lines[i]), hlSpans(lines[i]), th)); + added.push(makeLine(hlText(lastHL) + firstLine.text.slice(to.ch), hlSpans(lastHL), th)); + updateLine(cm, firstLine, firstLine.text.slice(0, from.ch) + hlText(lines[0]), hlSpans(lines[0])); doc.insert(from.line + 1, added); } } else if (lines.length == 1) { - firstLine.update(firstLine.text.slice(0, from.ch) + hlText(lines[0]) + lastLine.text.slice(to.ch), - hlSpans(lines[0]), cm); + updateLine(cm, firstLine, firstLine.text.slice(0, from.ch) + hlText(lines[0]) + + lastLine.text.slice(to.ch), hlSpans(lines[0])); doc.remove(from.line + 1, nlines, cm); } else { var added = []; - firstLine.update(firstLine.text.slice(0, from.ch) + hlText(lines[0]), hlSpans(lines[0]), cm); - lastLine.update(hlText(lastHL) + lastLine.text.slice(to.ch), hlSpans(lastHL), cm); + updateLine(cm, firstLine, firstLine.text.slice(0, from.ch) + hlText(lines[0]), hlSpans(lines[0])); + updateLine(cm, lastLine, hlText(lastHL) + lastLine.text.slice(to.ch), hlSpans(lastHL)); for (var i = 1, e = lines.length - 1; i < e; ++i) - added.push(new Line(hlText(lines[i]), hlSpans(lines[i]), th)); + added.push(makeLine(hlText(lines[i]), hlSpans(lines[i]), th)); if (nlines > 1) doc.remove(from.line + 1, nlines - 1, cm); doc.insert(from.line + 1, added); } @@ -1939,17 +1940,17 @@ window.CodeMirror = (function() { if (!how) how = "add"; if (how == "smart") { if (!doc.mode.indent) how = "prev"; - else var state = getStateBefore(doc, n); + else var state = getStateBefore(cm, n); } - var line = getLine(doc, n), curSpace = line.indentation(doc.tabSize), - curSpaceString = line.text.match(/^\s*/)[0], indentation; + var line = getLine(doc, n), curSpace = countColumn(line.text, null, cm.options.tabSize); + var curSpaceString = line.text.match(/^\s*/)[0], indentation; if (how == "smart") { indentation = doc.mode.indent(state, line.text.slice(curSpaceString.length), line.text); if (indentation == Pass) how = "prev"; } if (how == "prev") { - if (n) indentation = getLine(doc, n-1).indentation(doc.tabSize); + if (n) indentation = countColumn(getLine(doc, n-1).text, null, cm.options.tabSize); else indentation = 0; } else if (how == "add") indentation = curSpace + cm.options.indentUnit; @@ -2139,14 +2140,14 @@ window.CodeMirror = (function() { getTokenAt: function(pos) { var doc = this.view.doc; pos = clipPos(doc, pos); - return getLine(doc, pos.line).getTokenAt(doc.mode, getStateBefore(doc, pos.line), + return getLine(doc, pos.line).getTokenAt(doc.mode, getStateBefore(this, pos.line), this.options.tabSize, pos.ch); }, getStateAfter: function(line) { var doc = this.view.doc; line = clipLine(doc, line == null ? doc.size - 1: line); - return getStateBefore(doc, line + 1); + return getStateBefore(this, line + 1); }, cursorCoords: function(start, mode) { @@ -3067,209 +3068,208 @@ window.CodeMirror = (function() { // Line objects. These hold state related to a line, including // highlighting info (the styles array). - function Line(text, markedSpans, height) { - this.text = text; - this.height = height; - attachMarkedSpans(this, markedSpans); + function makeLine(text, markedSpans, height) { + var line = {text: text, height: height}; + attachMarkedSpans(line, markedSpans); + return line; } - Line.prototype = { - update: function(text, markedSpans, cm) { - this.text = text; - this.stateAfter = this.styles = null; - if (this.order != null) this.order = null; - detachMarkedSpans(this); - attachMarkedSpans(this, markedSpans); - signalLater(cm, this, "change"); - }, - - // Run the given mode's parser over a line, update the styles - // array, which contains alternating fragments of text and CSS - // classes. - highlight: function(mode, state, tabSize) { - var stream = new StringStream(this.text, tabSize), st = this.styles || (this.styles = []); - var pos = st.length = 0; - if (this.text == "" && mode.blankLine) mode.blankLine(state); - while (!stream.eol()) { - var style = mode.token(stream, state), substr = stream.current(); - stream.start = stream.pos; - if (pos && st[pos-1] == style) { - st[pos-2] += substr; - } else if (substr) { - st[pos++] = substr; st[pos++] = style; - } - // Give up when line is ridiculously long - if (stream.pos > 5000) { - st[pos++] = this.text.slice(stream.pos); st[pos++] = null; - break; - } - } - }, - - // Lightweight form of highlight -- proceed over this line and - // update state, but don't save a style array. - process: function(mode, state, tabSize) { - var stream = new StringStream(this.text, tabSize); - if (this.text == "" && mode.blankLine) mode.blankLine(state); - while (!stream.eol() && stream.pos <= 5000) { - mode.token(stream, state); - stream.start = stream.pos; + function updateLine(cm, line, text, markedSpans) { + line.text = text; + line.stateAfter = line.styles = null; + if (line.order != null) line.order = null; + detachMarkedSpans(line); + attachMarkedSpans(line, markedSpans); + signalLater(cm, line, "change"); + } + + function cleanUpLine(line) { + line.parent = null; + detachMarkedSpans(line); + } + + // Run the given mode's parser over a line, update the styles + // array, which contains alternating fragments of text and CSS + // classes. + function highlightLine(cm, line, state) { + var mode = cm.view.doc.mode; + var stream = new StringStream(line.text, cm.options.tabSize), st = line.styles || (line.styles = []); + var pos = st.length = 0; + if (line.text == "" && mode.blankLine) mode.blankLine(state); + while (!stream.eol()) { + var style = mode.token(stream, state), substr = stream.current(); + stream.start = stream.pos; + if (pos && st[pos-1] == style) { + st[pos-2] += substr; + } else if (substr) { + st[pos++] = substr; st[pos++] = style; + } + // Give up when line is ridiculously long + if (stream.pos > 5000) { + st[pos++] = line.text.slice(stream.pos); st[pos++] = null; + break; } - }, - - // Fetch the parser token for a given character. Useful for hacks - // that want to inspect the mode state (say, for completion). - getTokenAt: function(mode, state, tabSize, ch) { - var txt = this.text, stream = new StringStream(txt, tabSize); - while (stream.pos < ch && !stream.eol()) { - stream.start = stream.pos; - var style = mode.token(stream, state); - } - return {start: stream.start, - end: stream.pos, - string: stream.current(), - className: style || null, - state: state}; - }, + } + } - indentation: function(tabSize) {return countColumn(this.text, null, tabSize);}, - - // Produces an HTML fragment for the line, taking selection, - // marking, and highlighting into account. - getContent: function(tabSize, wrapAt, compensateForWrapping) { - var first = true, col = 0, specials = /[\t\u0000-\u0019\u200b\u2028\u2029\uFEFF]/g; - var pre = elt("pre"); - function span_(text, style) { - if (!text) return; - // Work around a bug where, in some compat modes, IE ignores leading spaces - if (first && ie && text.charAt(0) == " ") text = "\u00a0" + text.slice(1); - first = false; - if (!specials.test(text)) { - col += text.length; - var content = document.createTextNode(text); - } else { - var content = document.createDocumentFragment(), pos = 0; - while (true) { - specials.lastIndex = pos; - var m = specials.exec(text); - var skipped = m ? m.index - pos : text.length - pos; - if (skipped) { - content.appendChild(document.createTextNode(text.slice(pos, pos + skipped))); - col += skipped; - } - if (!m) break; - pos += skipped + 1; - if (m[0] == "\t") { - var tabWidth = tabSize - col % tabSize; - content.appendChild(elt("span", spaceStr(tabWidth), "cm-tab")); - col += tabWidth; - } else { - var token = elt("span", "\u2022", "cm-invalidchar"); - token.title = "\\u" + m[0].charCodeAt(0).toString(16); - content.appendChild(token); - col += 1; - } + // Lightweight form of highlight -- proceed over this line and + // update state, but don't save a style array. + function processLine(cm, line, state) { + var mode = cm.view.doc.mode; + var stream = new StringStream(line.text, cm.options.tabSize); + if (line.text == "" && mode.blankLine) mode.blankLine(state); + while (!stream.eol() && stream.pos <= 5000) { + mode.token(stream, state); + stream.start = stream.pos; + } + } + + // Fetch the parser token for a given character. Useful for hacks + // that want to inspect the mode state (say, for completion). + function getTokenAt(cm, line, state, ch) { + var mode = cm.view.doc.mode; + var txt = line.text, stream = new StringStream(txt, cm.options.tabSize); + while (stream.pos < ch && !stream.eol()) { + stream.start = stream.pos; + var style = mode.token(stream, state); + } + return {start: stream.start, + end: stream.pos, + string: stream.current(), + className: style || null, + state: state}; + } + + // Produces an HTML fragment for the line, taking selection, + // marking, and highlighting into account. + function buildLineContent(line, tabSize, wrapAt, compensateForWrapping) { + var first = true, col = 0, specials = /[\t\u0000-\u0019\u200b\u2028\u2029\uFEFF]/g; + var pre = elt("pre"); + function span_(text, style) { + if (!text) return; + // Work around a bug where, in some compat modes, IE ignores leading spaces + if (first && ie && text.charAt(0) == " ") text = "\u00a0" + text.slice(1); + first = false; + if (!specials.test(text)) { + col += text.length; + var content = document.createTextNode(text); + } else { + var content = document.createDocumentFragment(), pos = 0; + while (true) { + specials.lastIndex = pos; + var m = specials.exec(text); + var skipped = m ? m.index - pos : text.length - pos; + if (skipped) { + content.appendChild(document.createTextNode(text.slice(pos, pos + skipped))); + col += skipped; } - } - if (style != null) return pre.appendChild(elt("span", [content], style)); - else return pre.appendChild(content); - } - var span = span_; - if (wrapAt != null) { - var outPos = 0; - span = function(text, style) { - var l = text.length; - if (wrapAt >= outPos && wrapAt < outPos + l) { - var cut = wrapAt - outPos; - if (wrapAt > outPos) { - span_(text.slice(0, cut), style); - // See comment at the definition of spanAffectsWrapping - if (compensateForWrapping && spanAffectsWrapping.test(text.slice(cut - 1, cut + 1))) - pre.appendChild(elt("wbr")); - } - if (cut + 1 == l) { - pre.anchor = span_(text.slice(cut), style || ""); - wrapAt--; - } else { - var end = cut + 1; - while (isExtendingChar.test(text.charAt(end))) ++end; - pre.anchor = span_(text.slice(cut, end), style || ""); - if (compensateForWrapping && spanAffectsWrapping.test(text.slice(cut, end + 1))) - pre.appendChild(elt("wbr")); - span_(text.slice(end), style); - } - outPos += l; + if (!m) break; + pos += skipped + 1; + if (m[0] == "\t") { + var tabWidth = tabSize - col % tabSize; + content.appendChild(elt("span", spaceStr(tabWidth), "cm-tab")); + col += tabWidth; } else { - outPos += l; - span_(text, style); + var token = elt("span", "\u2022", "cm-invalidchar"); + token.title = "\\u" + m[0].charCodeAt(0).toString(16); + content.appendChild(token); + col += 1; } - }; - } - - var st = this.styles, allText = this.text, marked = this.markedSpans; - var len = allText.length; - function styleToClass(style) { - if (!style) return null; - return "cm-" + style.replace(/ +/g, " cm-"); - } - if (!allText) { - span("\u00a0"); - } else if (!marked || !marked.length) { - for (var i = 0, ch = 0; ch < len; i+=2) { - var str = st[i], style = st[i+1], l = str.length; - if (ch + l > len) str = str.slice(0, len - ch); - ch += l; - span(str, styleToClass(style)); } - } else { - marked.sort(function(a, b) { return a.from - b.from; }); - var pos = 0, i = 0, text = "", style, sg = 0; - var nextChange = marked[0].from || 0, marks = [], markpos = 0; - var advanceMarks = function() { - var m; - while (markpos < marked.length && - ((m = marked[markpos]).from == pos || m.from == null)) { - if (m.marker.type == "range") marks.push(m); - ++markpos; + } + if (style != null) return pre.appendChild(elt("span", [content], style)); + else return pre.appendChild(content); + } + var span = span_; + if (wrapAt != null) { + var outPos = 0; + span = function(text, style) { + var l = text.length; + if (wrapAt >= outPos && wrapAt < outPos + l) { + var cut = wrapAt - outPos; + if (wrapAt > outPos) { + span_(text.slice(0, cut), style); + // See comment at the definition of spanAffectsWrapping + if (compensateForWrapping && spanAffectsWrapping.test(text.slice(cut - 1, cut + 1))) + pre.appendChild(elt("wbr")); } - nextChange = markpos < marked.length ? marked[markpos].from : Infinity; - for (var i = 0; i < marks.length; ++i) { - var to = marks[i].to; - if (to == null) to = Infinity; - if (to == pos) marks.splice(i--, 1); - else nextChange = Math.min(to, nextChange); + if (cut + 1 == l) { + pre.anchor = span_(text.slice(cut), style || ""); + wrapAt--; + } else { + var end = cut + 1; + while (isExtendingChar.test(text.charAt(end))) ++end; + pre.anchor = span_(text.slice(cut, end), style || ""); + if (compensateForWrapping && spanAffectsWrapping.test(text.slice(cut, end + 1))) + pre.appendChild(elt("wbr")); + span_(text.slice(end), style); } - }; - var m = 0; - while (pos < len) { - if (nextChange == pos) advanceMarks(); - var upto = Math.min(len, nextChange); - while (true) { - if (text) { - var end = pos + text.length; - var appliedStyle = style; - for (var j = 0; j < marks.length; ++j) { - var mark = marks[j]; - appliedStyle = (appliedStyle ? appliedStyle + " " : "") + mark.marker.style; - if (mark.marker.endStyle && mark.to === Math.min(end, upto)) appliedStyle += " " + mark.marker.endStyle; - if (mark.marker.startStyle && mark.from === pos) appliedStyle += " " + mark.marker.startStyle; - } - span(end > upto ? text.slice(0, upto - pos) : text, appliedStyle); - if (end >= upto) {text = text.slice(upto - pos); pos = upto; break;} - pos = end; + outPos += l; + } else { + outPos += l; + span_(text, style); + } + }; + } + + var st = line.styles, allText = line.text, marked = line.markedSpans; + var len = allText.length; + function styleToClass(style) { + if (!style) return null; + return "cm-" + style.replace(/ +/g, " cm-"); + } + if (!allText) { + span("\u00a0"); + } else if (!marked || !marked.length) { + for (var i = 0, ch = 0; ch < len; i+=2) { + var str = st[i], style = st[i+1], l = str.length; + if (ch + l > len) str = str.slice(0, len - ch); + ch += l; + span(str, styleToClass(style)); + } + } else { + marked.sort(function(a, b) { return a.from - b.from; }); + var pos = 0, i = 0, text = "", style, sg = 0; + var nextChange = marked[0].from || 0, marks = [], markpos = 0; + var advanceMarks = function() { + var m; + while (markpos < marked.length && + ((m = marked[markpos]).from == pos || m.from == null)) { + if (m.marker.type == "range") marks.push(m); + ++markpos; + } + nextChange = markpos < marked.length ? marked[markpos].from : Infinity; + for (var i = 0; i < marks.length; ++i) { + var to = marks[i].to; + if (to == null) to = Infinity; + if (to == pos) marks.splice(i--, 1); + else nextChange = Math.min(to, nextChange); + } + }; + var m = 0; + while (pos < len) { + if (nextChange == pos) advanceMarks(); + var upto = Math.min(len, nextChange); + while (true) { + if (text) { + var end = pos + text.length; + var appliedStyle = style; + for (var j = 0; j < marks.length; ++j) { + var mark = marks[j]; + appliedStyle = (appliedStyle ? appliedStyle + " " : "") + mark.marker.style; + if (mark.marker.endStyle && mark.to === Math.min(end, upto)) appliedStyle += " " + mark.marker.endStyle; + if (mark.marker.startStyle && mark.from === pos) appliedStyle += " " + mark.marker.startStyle; } - text = st[i++]; style = styleToClass(st[i++]); + span(end > upto ? text.slice(0, upto - pos) : text, appliedStyle); + if (end >= upto) {text = text.slice(upto - pos); pos = upto; break;} + pos = end; } + text = st[i++]; style = styleToClass(st[i++]); } } - return pre; - }, - - cleanUp: function() { - this.parent = null; - detachMarkedSpans(this); } - }; + return pre; + } // DOCUMENT DATA STRUCTURE @@ -3289,7 +3289,7 @@ window.CodeMirror = (function() { for (var i = at, e = at + n; i < e; ++i) { var line = this.lines[i]; this.height -= line.height; - line.cleanUp(); + cleanUpLine(line); signalLater(cm, line, "delete"); } this.lines.splice(at, n); @@ -3485,10 +3485,9 @@ window.CodeMirror = (function() { function lineContent(cm, line, anchorAt) { if (!line.styles) { - var doc = lineDoc(line); - line.highlight(doc.mode, line.stateAfter = getStateBefore(doc, lineNo(line)), cm.options.tabSize); + highlightLine(cm, line, line.stateAfter = getStateBefore(cm, lineNo(line))); } - return line.getContent(cm.options.tabSize, anchorAt, cm.options.lineWrapping); + return buildLineContent(line, cm.options.tabSize, anchorAt, cm.options.lineWrapping); } // HISTORY From f902ad1b134e690239d1f24653e426b7f4f5d443 Mon Sep 17 00:00:00 2001 From: Marijn Haverbeke Date: Tue, 16 Oct 2012 11:12:53 +0200 Subject: [PATCH 0208/5780] Stop using methods on history object --- lib/codemirror.js | 86 +++++++++++++++++++++-------------------------- 1 file changed, 39 insertions(+), 47 deletions(-) diff --git a/lib/codemirror.js b/lib/codemirror.js index df3b2e3a82..cf7b5b6616 100644 --- a/lib/codemirror.js +++ b/lib/codemirror.js @@ -69,7 +69,7 @@ window.CodeMirror = (function() { // Override magic textarea content restore that IE sometimes does // on our hidden textarea on reload if (ie) setTimeout(bind(resetInput, this, true), 20); - doc.history = new History(); + doc.history = makeHistory(); registerEventHandlers(this); // IE throws unspecified error in certain cases, when @@ -1017,12 +1017,6 @@ window.CodeMirror = (function() { cm.curOp.changes.push({from: from, to: to, diff: lendiff}); } - function compoundChange(cm, f) { - var hist = cm.view.doc.history; - hist.startCompound(); - try { return f(); } finally { hist.endCompound(); } - } - // INPUT HANDLING function slowPoll(cm) { @@ -1644,7 +1638,7 @@ window.CodeMirror = (function() { old.push(newHL(line.text, line.markedSpans)); }); if (doc.history) { - doc.history.addChange(from.line, newText.length, old); + addChange(doc.history, from.line, newText.length, old); while (doc.history.done.length > cm.options.undoDepth) doc.history.done.shift(); } var lines = updateMarkedSpans(hlSpans(old[0]), hlSpans(lst(old)), from.ch, to.ch, newText); @@ -2113,7 +2107,7 @@ window.CodeMirror = (function() { return {undo: hist.done.length, redo: hist.undone.length}; }, - clearHistory: function() {this.view.doc.history = new History();}, + clearHistory: function() {this.view.doc.history = makeHistory();}, getHistory: function() { var hist = this.view.doc.history; @@ -2132,7 +2126,7 @@ window.CodeMirror = (function() { }, setHistory: function(histData) { - var hist = this.view.doc.history = new History(); + var hist = this.view.doc.history = makeHistory(); hist.done = histData.done; hist.undone = histData.undone; }, @@ -2184,7 +2178,7 @@ window.CodeMirror = (function() { ++curLine; }); regChange(this, from.line, to.line + 1); - doc.history.addChange(from.line, old.length, old, true); + addChange(doc.history, from.line, old.length, old, true); return marker; }), @@ -2196,7 +2190,7 @@ window.CodeMirror = (function() { var span = {from: pos.ch, to: pos.ch, marker: marker}; line.markedSpans = (line.markedSpans || []).concat([span]); marker.lines.push(line); - doc.history.addChange(pos.line, 1, [old]); + addChange(doc.history, pos.line, 1, [old]); return marker; }, @@ -2925,7 +2919,7 @@ window.CodeMirror = (function() { regChange(this.cm, min, max + 1); var old = []; for (var i = min; i <= max; ++i) old.push(seen[i]); - this.cm.view.doc.history.addChange(min, old.length, old, true); + addChange(this.cm.view.doc.history, min, old.length, old, true); } this.lines.length = 0; endOperation(this.cm); @@ -3494,41 +3488,39 @@ window.CodeMirror = (function() { // The history object 'chunks' changes that are made close together // and at almost the same time into bigger undoable units. - function History() { - this.time = 0; - this.done = []; this.undone = []; - this.compound = 0; - this.closed = false; - } - History.prototype = { - addChange: function(start, added, old, minor) { - this.undone.length = 0; - var time = +new Date, cur = lst(this.done), last = cur && lst(cur); - var dtime = time - this.time; - - if (cur && !this.closed && (this.compound || minor)) { - cur.push({start: start, added: added, old: old}); - } else if (dtime > 400 || !last || this.closed || - last.start > start + old.length || last.start + last.added < start) { - this.done.push([{start: start, added: added, old: old}]); - this.closed = false; - } else { - var startBefore = Math.max(0, last.start - start), - endAfter = Math.max(0, (start + old.length) - (last.start + last.added)); - for (var i = startBefore; i > 0; --i) last.old.unshift(old[i - 1]); - for (var i = endAfter; i > 0; --i) last.old.push(old[old.length - i]); - if (startBefore) last.start = start; - last.added += added - (old.length - startBefore - endAfter); - } - this.time = time; - }, - startCompound: function() { - if (!this.compound++) this.closed = true; - }, - endCompound: function() { - if (!--this.compound) this.closed = true; + function makeHistory() { + return {time: 0, done: [], undone: [], + compound: 0, closed: false}; + } + + function addChange(history, start, added, old, minor) { + history.undone.length = 0; + var time = +new Date, cur = lst(history.done), last = cur && lst(cur); + var dtime = time - history.time; + + if (cur && !history.closed && (history.compound || minor)) { + cur.push({start: start, added: added, old: old}); + } else if (dtime > 400 || !last || history.closed || + last.start > start + old.length || last.start + last.added < start) { + history.done.push([{start: start, added: added, old: old}]); + history.closed = false; + } else { + var startBefore = Math.max(0, last.start - start), + endAfter = Math.max(0, (start + old.length) - (last.start + last.added)); + for (var i = startBefore; i > 0; --i) last.old.unshift(old[i - 1]); + for (var i = endAfter; i > 0; --i) last.old.push(old[old.length - i]); + if (startBefore) last.start = start; + last.added += added - (old.length - startBefore - endAfter); } - }; + history.time = time; + } + + function compoundChange(cm, f) { + var hist = cm.view.doc.history; + if (!hist.compound++) hist.closed = true; + try { return f(); } + finally { if (!--hist.compound) hist.closed = true; } + } // EVENT OPERATORS From 5f3e5541fc17918d5cc8ed85042525a49b1f2fbc Mon Sep 17 00:00:00 2001 From: Marijn Haverbeke Date: Tue, 16 Oct 2012 11:21:08 +0200 Subject: [PATCH 0209/5780] Move some properties from doc to view The reason for putting them in doc turned out to be misguided --- lib/codemirror.js | 109 +++++++++++++++++++++++----------------------- 1 file changed, 54 insertions(+), 55 deletions(-) diff --git a/lib/codemirror.js b/lib/codemirror.js index cf7b5b6616..9105f484a0 100644 --- a/lib/codemirror.js +++ b/lib/codemirror.js @@ -42,15 +42,13 @@ window.CodeMirror = (function() { if (options.lineWrapping) display.wrapper.className += " CodeMirror-wrap"; var doc = new BranchChunk([new LeafChunk([makeLine("", null, textHeight(display))])]); - // frontier is the point up to which the content has been parsed, - doc.frontier = 0; - doc.highlight = new Delayed(); - doc.tabSize = options.tabSize; // The selection. These are always maintained to point at valid // positions. Inverted is used to remember that the user is // selecting bottom-to-top. this.view = { doc: doc, + // frontier is the point up to which the content has been parsed, + frontier: 0, highlight: new Delayed(), sel: {from: {line: 0, ch: 0}, to: {line: 0, ch: 0}, inverted: false, shift: false}, scrollTop: 0, scrollLeft: 0, overwrite: false, focused: false, @@ -69,7 +67,7 @@ window.CodeMirror = (function() { // Override magic textarea content restore that IE sometimes does // on our hidden textarea on reload if (ie) setTimeout(bind(resetInput, this, true), 20); - doc.history = makeHistory(); + this.view.history = makeHistory(); registerEventHandlers(this); // IE throws unspecified error in certain cases, when @@ -174,9 +172,9 @@ window.CodeMirror = (function() { function loadMode(cm) { var doc = cm.view.doc; - doc.mode = CodeMirror.getMode(cm.options, cm.options.mode); + cm.view.mode = CodeMirror.getMode(cm.options, cm.options.mode); doc.iter(0, doc.size, function(line) { line.stateAfter = null; }); - doc.frontier = 0; + cm.view.frontier = 0; startWorker(cm, 100); } @@ -681,32 +679,32 @@ window.CodeMirror = (function() { // HIGHLIGHT WORKER function startWorker(cm, time) { - if (cm.view.doc.frontier < cm.display.showingTo) - cm.view.doc.highlight.set(time, bind(highlightWorker, cm)); + if (cm.view.frontier < cm.display.showingTo) + cm.view.highlight.set(time, bind(highlightWorker, cm)); } function highlightWorker(cm) { - var doc = cm.view.doc; - if (doc.frontier >= cm.display.showingTo) return; + var view = cm.view, doc = view.doc; + if (view.frontier >= cm.display.showingTo) return; var end = +new Date + cm.options.workTime; - var state = copyState(doc.mode, getStateBefore(cm, doc.frontier)); - var startFrontier = doc.frontier; - doc.iter(doc.frontier, Math.min(doc.size, cm.display.showingTo + 500), function(line) { - if (doc.frontier >= cm.display.showingFrom) { // Visible + var state = copyState(view.mode, getStateBefore(cm, view.frontier)); + var startFrontier = view.frontier; + doc.iter(view.frontier, Math.min(doc.size, cm.display.showingTo + 500), function(line) { + if (view.frontier >= cm.display.showingFrom) { // Visible highlightLine(cm, line, state); - line.stateAfter = copyState(doc.mode, state); + line.stateAfter = copyState(view.mode, state); } else { processLine(cm, line, state); - line.stateAfter = doc.frontier % 5 == 0 ? copyState(doc.mode, state) : null; + line.stateAfter = view.frontier % 5 == 0 ? copyState(view.mode, state) : null; } - ++doc.frontier; + ++view.frontier; if (+new Date > end) { startWorker(cm, cm.options.workDelay); return true; } }); - if (cm.display.showingTo > startFrontier && doc.frontier >= cm.display.showingFrom) - operation(cm, function() {regChange(this, startFrontier, doc.frontier);})(); + if (cm.display.showingTo > startFrontier && view.frontier >= cm.display.showingFrom) + operation(cm, function() {regChange(this, startFrontier, view.frontier);})(); } // Finds the line to start with when starting a parse. Tries to @@ -730,13 +728,13 @@ window.CodeMirror = (function() { } function getStateBefore(cm, n) { - var doc = cm.view.doc; - var pos = findStartLine(cm, n), state = pos && getLine(doc, pos-1).stateAfter; - if (!state) state = startState(doc.mode); - else state = copyState(doc.mode, state); - doc.iter(pos, n, function(line) { + var view = cm.view; + var pos = findStartLine(cm, n), state = pos && getLine(view.doc, pos-1).stateAfter; + if (!state) state = startState(view.mode); + else state = copyState(view.mode, state); + view.doc.iter(pos, n, function(line) { processLine(cm, line, state); - line.stateAfter = (pos == n - 1 || pos % 5 == 0) ? copyState(doc.mode, state) : null; + line.stateAfter = (pos == n - 1 || pos % 5 == 0) ? copyState(view.mode, state) : null; }); return state; } @@ -1547,9 +1545,9 @@ window.CodeMirror = (function() { if (opera && keyCode == lastStoppedKey) {lastStoppedKey = null; e_preventDefault(e); return;} if (((opera && (!e.which || e.which < 10)) || khtml) && handleKeyBinding(cm, e)) return; var ch = String.fromCharCode(charCode == null ? keyCode : charCode); - if (this.options.electricChars && this.view.doc.mode.electricChars && + if (this.options.electricChars && this.view.mode.electricChars && this.options.smartIndent && !this.options.readOnly && - this.view.doc.mode.electricChars.indexOf(ch) > -1) + this.view.mode.electricChars.indexOf(ch) > -1) setTimeout(operation(cm, function() {indentLine(cm, cm.view.sel.to.line, "smart");}), 75); if (handleCharBinding(cm, e, ch)) return; fastPoll(cm); @@ -1637,9 +1635,9 @@ window.CodeMirror = (function() { doc.iter(from.line, to.line + 1, function(line) { old.push(newHL(line.text, line.markedSpans)); }); - if (doc.history) { - addChange(doc.history, from.line, newText.length, old); - while (doc.history.done.length > cm.options.undoDepth) doc.history.done.shift(); + if (view.history) { + addChange(view.history, from.line, newText.length, old); + while (view.history.done.length > cm.options.undoDepth) view.history.done.shift(); } var lines = updateMarkedSpans(hlSpans(old[0]), hlSpans(lst(old)), from.ch, to.ch, newText); updateDocNoUndo(cm, from, to, lines, selFrom, selTo); @@ -1661,11 +1659,11 @@ window.CodeMirror = (function() { to.push(out); } function undo(cm) { - var hist = cm.view.doc.history; + var hist = cm.view.history; unredoHelper(cm, hist.done, hist.undone); } function redo(cm) { - var hist = cm.view.doc.history; + var hist = cm.view.history; unredoHelper(cm, hist.undone, hist.done); } @@ -1735,7 +1733,7 @@ window.CodeMirror = (function() { } // Adjust frontier, schedule worker - doc.frontier = Math.min(doc.frontier, from.line); + view.frontier = Math.min(view.frontier, from.line); startWorker(cm, 400); var lendiff = lines.length - nlines - 1; @@ -1933,18 +1931,19 @@ window.CodeMirror = (function() { var doc = cm.view.doc; if (!how) how = "add"; if (how == "smart") { - if (!doc.mode.indent) how = "prev"; + if (!cm.view.mode.indent) how = "prev"; else var state = getStateBefore(cm, n); } - var line = getLine(doc, n), curSpace = countColumn(line.text, null, cm.options.tabSize); + var tabSize = cm.options.tabSize; + var line = getLine(doc, n), curSpace = countColumn(line.text, null, tabSize); var curSpaceString = line.text.match(/^\s*/)[0], indentation; if (how == "smart") { - indentation = doc.mode.indent(state, line.text.slice(curSpaceString.length), line.text); + indentation = cm.view.mode.indent(state, line.text.slice(curSpaceString.length), line.text); if (indentation == Pass) how = "prev"; } if (how == "prev") { - if (n) indentation = countColumn(getLine(doc, n-1).text, null, cm.options.tabSize); + if (n) indentation = countColumn(getLine(doc, n-1).text, null, tabSize); else indentation = 0; } else if (how == "add") indentation = curSpace + cm.options.indentUnit; @@ -1954,7 +1953,7 @@ window.CodeMirror = (function() { var indentString = "", pos = 0; if (cm.options.indentWithTabs) - for (var i = Math.floor(indentation / doc.tabSize); i; --i) {pos += doc.tabSize; indentString += "\t";} + for (var i = Math.floor(indentation / tabSize); i; --i) {pos += tabSize; indentString += "\t";} if (pos < indentation) indentString += spaceStr(indentation - pos); if (indentString != curSpaceString) @@ -2062,7 +2061,7 @@ window.CodeMirror = (function() { else if (option == "readOnly" && !value) {resetInput(this, true);} else if (option == "theme") themeChanged(this); else if (option == "lineWrapping") operation(this, wrappingChanged)(this); - else if (option == "tabSize") {this.view.doc.tabSize = value; updateDisplay(this, true);} + else if (option == "tabSize") updateDisplay(this, true); else if (option == "keyMap") keyMapChanged(this); else if (option == "gutters" || option == "lineNumbers") setGuttersForLineNumbers(this.options); else if (option == "tabindex") this.display.input.tabIndex = value; @@ -2076,14 +2075,14 @@ window.CodeMirror = (function() { getOption: function(option) {return this.options[option];}, - getMode: function() {return this.view.doc.mode;}, + getMode: function() {return this.view.mode;}, undo: operation(null, function() { - var hist = this.view.doc.history; + var hist = this.view.history; unredoHelper(this, hist.done, hist.undone); }), redo: operation(null, function() { - var hist = this.view.doc.history; + var hist = this.view.history; unredoHelper(this, hist.undone, hist.done); }), @@ -2103,14 +2102,14 @@ window.CodeMirror = (function() { }), historySize: function() { - var hist = this.view.doc.history; + var hist = this.view.history; return {undo: hist.done.length, redo: hist.undone.length}; }, - clearHistory: function() {this.view.doc.history = makeHistory();}, + clearHistory: function() {this.view.history = makeHistory();}, getHistory: function() { - var hist = this.view.doc.history; + var hist = this.view.history; function cp(arr) { for (var i = 0, nw = [], nwelt; i < arr.length; ++i) { nw.push(nwelt = []); @@ -2126,7 +2125,7 @@ window.CodeMirror = (function() { }, setHistory: function(histData) { - var hist = this.view.doc.history = makeHistory(); + var hist = this.view.history = makeHistory(); hist.done = histData.done; hist.undone = histData.undone; }, @@ -2134,7 +2133,7 @@ window.CodeMirror = (function() { getTokenAt: function(pos) { var doc = this.view.doc; pos = clipPos(doc, pos); - return getLine(doc, pos.line).getTokenAt(doc.mode, getStateBefore(this, pos.line), + return getLine(doc, pos.line).getTokenAt(cm.view.mode, getStateBefore(this, pos.line), this.options.tabSize, pos.ch); }, @@ -2178,7 +2177,7 @@ window.CodeMirror = (function() { ++curLine; }); regChange(this, from.line, to.line + 1); - addChange(doc.history, from.line, old.length, old, true); + addChange(this.view.history, from.line, old.length, old, true); return marker; }), @@ -2190,7 +2189,7 @@ window.CodeMirror = (function() { var span = {from: pos.ch, to: pos.ch, marker: marker}; line.markedSpans = (line.markedSpans || []).concat([span]); marker.lines.push(line); - addChange(doc.history, pos.line, 1, [old]); + addChange(this.view.history, pos.line, 1, [old]); return marker; }, @@ -2919,7 +2918,7 @@ window.CodeMirror = (function() { regChange(this.cm, min, max + 1); var old = []; for (var i = min; i <= max; ++i) old.push(seen[i]); - addChange(this.cm.view.doc.history, min, old.length, old, true); + addChange(this.cm.view.history, min, old.length, old, true); } this.lines.length = 0; endOperation(this.cm); @@ -3086,7 +3085,7 @@ window.CodeMirror = (function() { // array, which contains alternating fragments of text and CSS // classes. function highlightLine(cm, line, state) { - var mode = cm.view.doc.mode; + var mode = cm.view.mode; var stream = new StringStream(line.text, cm.options.tabSize), st = line.styles || (line.styles = []); var pos = st.length = 0; if (line.text == "" && mode.blankLine) mode.blankLine(state); @@ -3109,7 +3108,7 @@ window.CodeMirror = (function() { // Lightweight form of highlight -- proceed over this line and // update state, but don't save a style array. function processLine(cm, line, state) { - var mode = cm.view.doc.mode; + var mode = cm.view.mode; var stream = new StringStream(line.text, cm.options.tabSize); if (line.text == "" && mode.blankLine) mode.blankLine(state); while (!stream.eol() && stream.pos <= 5000) { @@ -3121,7 +3120,7 @@ window.CodeMirror = (function() { // Fetch the parser token for a given character. Useful for hacks // that want to inspect the mode state (say, for completion). function getTokenAt(cm, line, state, ch) { - var mode = cm.view.doc.mode; + var mode = cm.view.mode; var txt = line.text, stream = new StringStream(txt, cm.options.tabSize); while (stream.pos < ch && !stream.eol()) { stream.start = stream.pos; @@ -3516,7 +3515,7 @@ window.CodeMirror = (function() { } function compoundChange(cm, f) { - var hist = cm.view.doc.history; + var hist = cm.view.history; if (!hist.compound++) hist.closed = true; try { return f(); } finally { if (!--hist.compound) hist.closed = true; } From 08f550bacb3a35819a9df7781adc6759cbb7a01a Mon Sep 17 00:00:00 2001 From: Marijn Haverbeke Date: Tue, 16 Oct 2012 11:25:37 +0200 Subject: [PATCH 0210/5780] Add a flattenSpans option --- doc/manual.html | 8 ++++++++ lib/codemirror.js | 5 +++-- 2 files changed, 11 insertions(+), 2 deletions(-) diff --git a/doc/manual.html b/doc/manual.html index 119c5a0cea..ad6a010158 100644 --- a/doc/manual.html +++ b/doc/manual.html @@ -298,6 +298,14 @@

    Configuration

    generate events that allow CodeMirror to properly detect it. Thus, it polls. Default is 100 milliseconds. +
    flattenSpans (boolean)
    +
    By default, CodeMirror will combine adjacent tokens into a + single span if they have the same class. This will result in a + simpler DOM tree, and thus perform better. With some kinds of + styling (such as rounded corners), this will change the way the + document looks. You can set this option to false to disable this + behavior.
    +
    viewportMargin (integer)
    Specifies the amount of lines that are rendered above and below the part of the document that's currently scrolled into diff --git a/lib/codemirror.js b/lib/codemirror.js index 9105f484a0..86197130be 100644 --- a/lib/codemirror.js +++ b/lib/codemirror.js @@ -2539,6 +2539,7 @@ window.CodeMirror = (function() { cursorHeight: 1, workTime: 100, workDelay: 200, + flattenSpans: true, pollInterval: 100, undoDepth: 40, viewportMargin: 100, @@ -3085,14 +3086,14 @@ window.CodeMirror = (function() { // array, which contains alternating fragments of text and CSS // classes. function highlightLine(cm, line, state) { - var mode = cm.view.mode; + var mode = cm.view.mode, flattenSpans = cm.options.flattenSpans; var stream = new StringStream(line.text, cm.options.tabSize), st = line.styles || (line.styles = []); var pos = st.length = 0; if (line.text == "" && mode.blankLine) mode.blankLine(state); while (!stream.eol()) { var style = mode.token(stream, state), substr = stream.current(); stream.start = stream.pos; - if (pos && st[pos-1] == style) { + if (flattenSpans && pos && st[pos-1] == style) { st[pos-2] += substr; } else if (substr) { st[pos++] = substr; st[pos++] = style; From e5588dcbb027fbb0d71b429356dcf83114a54963 Mon Sep 17 00:00:00 2001 From: shaund Date: Sun, 14 Oct 2012 16:52:43 -0700 Subject: [PATCH 0211/5780] fix fullscreen demo to use wrapper, not scroll --- demo/fullscreen.html | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/demo/fullscreen.html b/demo/fullscreen.html index cc3f3bd005..2709ebb4b5 100644 --- a/demo/fullscreen.html +++ b/demo/fullscreen.html @@ -126,7 +126,7 @@

    CodeMirror: Full Screen Editing

    CodeMirror.on(window, "resize", function() { var showing = document.body.getElementsByClassName("CodeMirror-fullscreen")[0]; if (!showing) return; - showing.CodeMirror.getScrollerElement().style.height = winHeight() + "px"; + showing.CodeMirror.getWrapperElement().style.height = winHeight() + "px"; }); var editor = CodeMirror.fromTextArea(document.getElementById("code"), { lineNumbers: true, From c3d9c9972e7f33a5dd72496a47222ff7a0d03af2 Mon Sep 17 00:00:00 2001 From: ComFreek Date: Mon, 15 Oct 2012 18:22:44 +0200 Subject: [PATCH 0212/5780] Merged JavaScript and TypeScript mode as marijnh suggested --- mode/javascript/index.html | 17 ++++++--- mode/javascript/javascript.js | 40 +++++++++++++++++++-- mode/javascript/typescript.html | 61 +++++++++++++++++++++++++++++++++ 3 files changed, 112 insertions(+), 6 deletions(-) create mode 100644 mode/javascript/typescript.html diff --git a/mode/javascript/index.html b/mode/javascript/index.html index 3d5b2ea80e..c52668fc0e 100644 --- a/mode/javascript/index.html +++ b/mode/javascript/index.html @@ -70,10 +70,19 @@

    CodeMirror: JavaScript mode

    }); -

    JavaScript mode supports a single configuration - option, json, which will set the mode to expect JSON - data rather than a JavaScript program.

    +

    + JavaScript mode supports a two configuration + options: +

      +
    • json which will set the mode to expect JSON data rather than a JavaScript program.
    • +
    • + typescript which will activate additional syntax highlighting and some other things for TypeScript code. +
      + Click here for the demo which also provides an extra theme: typescript.html +
    • +
    +

    -

    MIME types defined: text/javascript, application/json.

    +

    MIME types defined: text/javascript, application/json, text/typescript, application/typescript.

    diff --git a/mode/javascript/javascript.js b/mode/javascript/javascript.js index 5b377db7d1..cbb7bc46ca 100644 --- a/mode/javascript/javascript.js +++ b/mode/javascript/javascript.js @@ -1,6 +1,11 @@ -CodeMirror.defineMode("javascript", function(config, parserConfig) { +/** + * The TypeScript extensions are (C) Copyright 2012 by ComFreek + */ + +CodeMirror.defineMode("javascript", function (config, parserConfig) { var indentUnit = config.indentUnit; var jsonMode = parserConfig.json; + var isTS = parserConfig.typescript; // Tokenizer @@ -8,7 +13,8 @@ CodeMirror.defineMode("javascript", function(config, parserConfig) { function kw(type) {return {type: type, style: "keyword"};} var A = kw("keyword a"), B = kw("keyword b"), C = kw("keyword c"); var operator = kw("operator"), atom = {type: "atom", style: "atom"}; - return { + + var jsKeywords = { "if": A, "while": A, "with": A, "else": B, "do": B, "try": B, "finally": B, "return": C, "break": C, "continue": C, "new": C, "delete": C, "throw": C, "var": kw("var"), "const": kw("var"), "let": kw("var"), @@ -17,6 +23,34 @@ CodeMirror.defineMode("javascript", function(config, parserConfig) { "in": operator, "typeof": operator, "instanceof": operator, "true": atom, "false": atom, "null": atom, "undefined": atom, "NaN": atom, "Infinity": atom }; + + // Extend the 'normal' keywords with the TypeScript language extensions + if (isTS) { + var tsKeywords = { + // object-like things + "interface": kw("interface"), + "class": kw("class"), + "extends": kw("extends"), + "constructor": kw("constructor"), + + // scope modifiers + "public": kw("public"), + "private": kw("private"), + "protected": kw("protected"), + "static": kw("static"), + + "super": kw("super"), + + // types + "string": type, "number": type, "bool": type, "any": type + }; + + for (var attr in tsKeywords) { + jsKeywords[attr] = tsKeywords[attr]; + } + } + + return jsKeywords; }(); var isOperatorChar = /[+\-*&%=<>!?|]/; @@ -360,3 +394,5 @@ CodeMirror.defineMode("javascript", function(config, parserConfig) { CodeMirror.defineMIME("text/javascript", "javascript"); CodeMirror.defineMIME("application/json", {name: "javascript", json: true}); +CodeMirror.defineMIME("text/typescript", { name: "javascript", typescript: true }); +CodeMirror.defineMIME("application/typescript", { name: "javascript", typescript: true }); diff --git a/mode/javascript/typescript.html b/mode/javascript/typescript.html new file mode 100644 index 0000000000..ba7496b90c --- /dev/null +++ b/mode/javascript/typescript.html @@ -0,0 +1,61 @@ + + + + + CodeMirror: JavaScript mode using TypeScript extension + + + + + + + +

    CodeMirror: JavaScript mode using TypeScript extension

    + +
    + + + +

    + JavaScript mode supports a two configuration + options: +

      +
    • json which will set the mode to expect JSON data rather than a JavaScript program.
    • +
    • typescript which will activate additional syntax highlighting and some other things for TypeScript code.
    • +
    +

    + +

    + This sample also uses the TypeBox.css theme. +

    + +

    MIME types defined: text/javascript, application/json, text/typescript, application/typescript.

    + + From cb14cb6f220e271bce5c6fdea112aaf4e0a67a39 Mon Sep 17 00:00:00 2001 From: Marijn Haverbeke Date: Tue, 16 Oct 2012 11:50:21 +0200 Subject: [PATCH 0213/5780] Make typescript mode recognize type declarations --- mode/javascript/index.html | 4 +--- mode/javascript/javascript.js | 30 ++++++++++++++++++++++-------- mode/javascript/typescript.html | 19 +++---------------- 3 files changed, 26 insertions(+), 27 deletions(-) diff --git a/mode/javascript/index.html b/mode/javascript/index.html index c52668fc0e..c24413e565 100644 --- a/mode/javascript/index.html +++ b/mode/javascript/index.html @@ -76,9 +76,7 @@

    CodeMirror: JavaScript mode

    • json which will set the mode to expect JSON data rather than a JavaScript program.
    • - typescript which will activate additional syntax highlighting and some other things for TypeScript code. -
      - Click here for the demo which also provides an extra theme: typescript.html + typescript which will activate additional syntax highlighting and some other things for TypeScript code (demo).

    diff --git a/mode/javascript/javascript.js b/mode/javascript/javascript.js index cbb7bc46ca..eb258818b0 100644 --- a/mode/javascript/javascript.js +++ b/mode/javascript/javascript.js @@ -2,7 +2,9 @@ * The TypeScript extensions are (C) Copyright 2012 by ComFreek */ -CodeMirror.defineMode("javascript", function (config, parserConfig) { +// TODO actually recognize syntax of TypeScript constructs + +CodeMirror.defineMode("javascript", function(config, parserConfig) { var indentUnit = config.indentUnit; var jsonMode = parserConfig.json; var isTS = parserConfig.typescript; @@ -25,7 +27,8 @@ CodeMirror.defineMode("javascript", function (config, parserConfig) { }; // Extend the 'normal' keywords with the TypeScript language extensions - if (isTS) { + if (isTS) { + var type = {type: "variable", style: "variable-3"}; var tsKeywords = { // object-like things "interface": kw("interface"), @@ -42,12 +45,12 @@ CodeMirror.defineMode("javascript", function (config, parserConfig) { "super": kw("super"), // types - "string": type, "number": type, "bool": type, "any": type + "string": type, "number": type, "bool": type, "any": type }; - for (var attr in tsKeywords) { - jsKeywords[attr] = tsKeywords[attr]; - } + for (var attr in tsKeywords) { + jsKeywords[attr] = tsKeywords[attr]; + } } return jsKeywords; @@ -309,8 +312,19 @@ CodeMirror.defineMode("javascript", function (config, parserConfig) { if (type == "}") return cont(); return pass(statement, block); } + function maybetype(type) { + if (type == ":") return cont(typedef); + return pass(); + } + function typedef(type) { + if (type == "variable"){cx.marked = "variable-3"; return cont();} + return pass(); + } function vardef1(type, value) { - if (type == "variable"){register(value); return cont(vardef2);} + if (type == "variable") { + register(value); + return isTS ? cont(maybetype, vardef2) : cont(vardef2); + } return cont(); } function vardef2(type, value) { @@ -340,7 +354,7 @@ CodeMirror.defineMode("javascript", function (config, parserConfig) { if (type == "(") return cont(pushlex(")"), pushcontext, commasep(funarg, ")"), poplex, statement, popcontext); } function funarg(type, value) { - if (type == "variable") {register(value); return cont();} + if (type == "variable") {register(value); return isTS ? cont(maybetype) : cont();} } // Interface diff --git a/mode/javascript/typescript.html b/mode/javascript/typescript.html index ba7496b90c..58315e7ac7 100644 --- a/mode/javascript/typescript.html +++ b/mode/javascript/typescript.html @@ -2,7 +2,7 @@ - CodeMirror: JavaScript mode using TypeScript extension + CodeMirror: TypeScript mode @@ -10,7 +10,7 @@ -

    CodeMirror: JavaScript mode using TypeScript extension

    +

    CodeMirror: TypeScript mode

    +
    +
    + + + + +

    + + + diff --git a/index.html b/index.html index feb15121af..5e7257d03a 100644 --- a/index.html +++ b/index.html @@ -116,6 +116,7 @@

    Usage demos:

  • Vim keybindings
  • Automatic xml tag closing
  • Lazy mode loading
  • +
  • Document tree visualization
  • Real-world uses:

    From 579dcf142ed88c875f8d158e1f5e1bb69ef0d403 Mon Sep 17 00:00:00 2001 From: Marijn Haverbeke Date: Fri, 19 Oct 2012 11:34:07 +0200 Subject: [PATCH 0230/5780] [javascript mode] Handle semicolon-less code better Closes #898 --- mode/javascript/javascript.js | 15 ++++++++------- 1 file changed, 8 insertions(+), 7 deletions(-) diff --git a/mode/javascript/javascript.js b/mode/javascript/javascript.js index 2f00713866..0c67874e6f 100644 --- a/mode/javascript/javascript.js +++ b/mode/javascript/javascript.js @@ -99,7 +99,8 @@ CodeMirror.defineMode("javascript", function(config, parserConfig) { stream.skipToEnd(); return ret("comment", "comment"); } - else if (state.reAllowed) { + else if (state.lastType == "operator" || state.lastType == "keyword c" || + /^[\[{}\(,;:]$/.test(state.lastType)) { nextUntilUnescaped(stream, "/"); stream.eatWhile(/[gimy]/); // 'y' is "sticky" option in Mozilla return ret("regexp", "string-2"); @@ -120,7 +121,7 @@ CodeMirror.defineMode("javascript", function(config, parserConfig) { else { stream.eatWhile(/[\w\$_]/); var word = stream.current(), known = keywords.propertyIsEnumerable(word) && keywords[word]; - return (known && state.kwAllowed) ? ret(known.type, known.style, word) : + return (known && state.lastType != ".") ? ret(known.type, known.style, word) : ret("variable", "variable", word); } } @@ -359,8 +360,7 @@ CodeMirror.defineMode("javascript", function(config, parserConfig) { startState: function(basecolumn) { return { tokenize: jsTokenBase, - reAllowed: true, - kwAllowed: true, + lastType: null, cc: [], lexical: new JSLexical((basecolumn || 0) - indentUnit, 0, "block", false), localVars: parserConfig.localVars, @@ -378,8 +378,7 @@ CodeMirror.defineMode("javascript", function(config, parserConfig) { if (stream.eatSpace()) return null; var style = state.tokenize(stream, state); if (type == "comment") return style; - state.reAllowed = !!(type == "operator" || type == "keyword c" || type.match(/^[\[{}\(,;:]$/)); - state.kwAllowed = type != '.'; + state.lastType = type; return parseJS(state, style, type, content, stream); }, @@ -391,7 +390,9 @@ CodeMirror.defineMode("javascript", function(config, parserConfig) { var type = lexical.type, closing = firstChar == type; if (type == "vardef") return lexical.indented + 4; else if (type == "form" && firstChar == "{") return lexical.indented; - else if (type == "stat" || type == "form") return lexical.indented + indentUnit; + else if (type == "form") return lexical.indented + indentUnit; + else if (type == "stat") + return lexical.indented + (state.lastType == "operator" || state.lastType == "," ? indentUnit : 0); else if (lexical.info == "switch" && !closing) return lexical.indented + (/^(?:case|default)\b/.test(textAfter) ? indentUnit : 2 * indentUnit); else if (lexical.align) return lexical.column + (closing ? 0 : 1); From 0985a76566ab18537d4e459c7513adb6c53ade99 Mon Sep 17 00:00:00 2001 From: Marijn Haverbeke Date: Fri, 19 Oct 2012 11:39:47 +0200 Subject: [PATCH 0231/5780] [javascript mode] Also count on semicolon omittance in var lists Issue #898 --- mode/javascript/javascript.js | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/mode/javascript/javascript.js b/mode/javascript/javascript.js index 0c67874e6f..37f6f8737a 100644 --- a/mode/javascript/javascript.js +++ b/mode/javascript/javascript.js @@ -322,7 +322,7 @@ CodeMirror.defineMode("javascript", function(config, parserConfig) { register(value); return isTS ? cont(maybetype, vardef2) : cont(vardef2); } - return cont(); + return pass(); } function vardef2(type, value) { if (value == "=") return cont(expression, vardef2); @@ -388,7 +388,7 @@ CodeMirror.defineMode("javascript", function(config, parserConfig) { var firstChar = textAfter && textAfter.charAt(0), lexical = state.lexical; if (lexical.type == "stat" && firstChar == "}") lexical = lexical.prev; var type = lexical.type, closing = firstChar == type; - if (type == "vardef") return lexical.indented + 4; + if (type == "vardef") return lexical.indented + (state.lastType == "operator" || state.lastType == "," ? 4 : 0); else if (type == "form" && firstChar == "{") return lexical.indented; else if (type == "form") return lexical.indented + indentUnit; else if (type == "stat") From 954ffc14e9e35d8dc8187b5ec782dd50243e47d3 Mon Sep 17 00:00:00 2001 From: Marijn Haverbeke Date: Fri, 19 Oct 2012 22:20:53 +0200 Subject: [PATCH 0232/5780] Add expanded list of real-world uses as a separate page So that I don't have to say 'no' so often --- doc/realworld.html | 68 ++++++++++++++++++++++++++++++++++++++++++++++ index.html | 3 +- 2 files changed, 69 insertions(+), 2 deletions(-) create mode 100644 doc/realworld.html diff --git a/doc/realworld.html b/doc/realworld.html new file mode 100644 index 0000000000..c28e6444c6 --- /dev/null +++ b/doc/realworld.html @@ -0,0 +1,68 @@ + + + + + CodeMirror: Real-world uses + + + + + +

    { } CodeMirror

    + +
    + +
    +/* Real world uses,
    +   full list */
    +
    +
    + +

    Contact me if you'd like + your project to be added to this list.

    + + + + + diff --git a/index.html b/index.html index 5e7257d03a..3fdfe2eb2f 100644 --- a/index.html +++ b/index.html @@ -140,15 +140,14 @@

    Real-world uses:

  • ql.io (http API query helper)
  • Elm language examples
  • The File Tree (collab editor)
  • -
  • BlueGriffon (HTML editor)
  • JSHint (JS linter)
  • kl1p (paste service)
  • SQLFiddle (SQL playground)
  • Try Haxe (Haxe Playground)
  • CSSDeck (CSS showcase)
  • -
  • CKWNC (UML editor)
  • sketchPatch Livecodelab
  • NoTex (rST authoring)
  • +
  • More...
  • From 7c7e3aa0d5f95c333cde4e9de696c0799d9f6fc1 Mon Sep 17 00:00:00 2001 From: Marijn Haverbeke Date: Mon, 22 Oct 2012 10:10:27 +0200 Subject: [PATCH 0233/5780] Add more real-world uses --- doc/realworld.html | 4 ++++ 1 file changed, 4 insertions(+) diff --git a/doc/realworld.html b/doc/realworld.html index c28e6444c6..3441af377b 100644 --- a/doc/realworld.html +++ b/doc/realworld.html @@ -25,6 +25,8 @@

    { } CodeMi
  • Adobe Brackets (code editor)
  • BlueGriffon (HTML editor)
  • Cargo Collective (creative publishing platform)
  • +
  • Codebug (PHP Xdebug front-end)
  • +
  • CodeMirror2-GWT (Google Web Toolkit wrapper)
  • Codev (collaborative IDE)
  • Collaborative CodeMirror demo (CodeMirror + operational transforms)
  • CKWNC (UML editor)
  • @@ -33,12 +35,14 @@

    { } CodeMi
  • DbNinja (MySQL access interface)
  • Elm language examples
  • Eloquent JavaScript (book)
  • +
  • Fastfig (online computation/math tool)
  • FathomJS integration (slides with editors, again)
  • Go language tour
  • GitHub's Android app
  • Google Apps Script
  • Haxe (Haxe Playground)
  • Histone template engine playground
  • +
  • ICEcoder (web IDE)
  • Joomla plugin
  • jsbin.com (JS playground)
  • JSHint (JS linter)
  • From 0365519d25b02c4fc21f06aad31272ffa79e4d1c Mon Sep 17 00:00:00 2001 From: Marijn Haverbeke Date: Mon, 22 Oct 2012 10:26:53 +0200 Subject: [PATCH 0234/5780] Mark release 3.0beta2 --- doc/compress.html | 1 + doc/oldrelease.html | 51 ++++++++++++++++++++++++++++ index.html | 83 +++++++++++++++++---------------------------- lib/codemirror.js | 2 +- package.json | 2 +- 5 files changed, 86 insertions(+), 53 deletions(-) diff --git a/doc/compress.html b/doc/compress.html index d8235fc07c..36c5702ede 100644 --- a/doc/compress.html +++ b/doc/compress.html @@ -30,6 +30,7 @@

    { } CodeMi

    Version: + + + +

    MIME type defined: text/x-z80.

    + + diff --git a/mode/z80/z80.js b/mode/z80/z80.js new file mode 100644 index 0000000000..c026790dc7 --- /dev/null +++ b/mode/z80/z80.js @@ -0,0 +1,113 @@ +CodeMirror.defineMode('z80', function() +{ + var keywords1 = /^(exx?|(ld|cp|in)([di]r?)?|pop|push|ad[cd]|cpl|daa|dec|inc|neg|sbc|sub|and|bit|[cs]cf|x?or|res|set|r[lr]c?a?|r[lr]d|s[lr]a|srl|djnz|nop|rst|[de]i|halt|im|ot[di]r|out[di]?)\b/i; + var keywords2 = /^(call|j[pr]|ret[in]?)\b/i; + var keywords3 = /^b_?(call|jump)\b/i; + var variables1 = /^(af?|bc?|c|de?|e|hl?|l|i[xy]?|r|sp)\b/i; + var variables2 = /^(n?[zc]|p[oe]?|m)\b/i; + var errors = /^([hl][xy]|i[xy][hl]|slia|sll)\b/i; + var numbers = /^([\da-f]+h|[0-7]+o|[01]+b|\d+)\b/i; + + return {startState: function() + { + return {context: 0}; + }, token: function(stream, state) + { + if (!stream.column()) + state.context = 0; + + if (stream.eatSpace()) + return null; + + var w; + + if (stream.eatWhile(/\w/)) + { + w = stream.current(); + + if (stream.indentation()) + { + if (state.context == 1 && variables1.test(w)) + return 'variable-2'; + + if (state.context == 2 && variables2.test(w)) + return 'variable-3'; + + if (keywords1.test(w)) + { + state.context = 1; + return 'keyword'; + } + else if (keywords2.test(w)) + { + state.context = 2; + return 'keyword'; + } + else if (keywords3.test(w)) + { + state.context = 3; + return 'keyword'; + } + + if (errors.test(w)) + return 'error'; + } + else if (numbers.test(w)) + { + return 'number'; + } + else + { + return null; + } + } + else if (stream.eat(';')) + { + stream.skipToEnd(); + return 'comment'; + } + else if (stream.eat('"')) + { + while (w = stream.next()) + { + if (w == '"') + break; + + if (w == '\\') + stream.next(); + } + + return 'string'; + } + else if (stream.eat('\'')) + { + if (stream.match(/\\?.'/)) + return 'number'; + } + else if (stream.eat('.') || stream.sol() && stream.eat('#')) + { + state.context = 4; + + if (stream.eatWhile(/\w/)) + return 'def'; + } + else if (stream.eat('$')) + { + if (stream.eatWhile(/[\da-f]/i)) + return 'number'; + } + else if (stream.eat('%')) + { + if (stream.eatWhile(/[01]/)) + return 'number'; + } + else + { + stream.next(); + } + + return null; + }}; +}); + +CodeMirror.defineMIME("text/x-z80", "z80"); From 862140fbf81ca0ee27b906c6370c732681b6970b Mon Sep 17 00:00:00 2001 From: Marijn Haverbeke Date: Mon, 12 Nov 2012 10:03:50 +0100 Subject: [PATCH 0316/5780] Fix bug caused by recent Opera versions supporting cut/copy events The code assumed being on opera meant those events would never fire. Closes #952 --- lib/codemirror.js | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/lib/codemirror.js b/lib/codemirror.js index 32c6a96839..b4a67b261a 100644 --- a/lib/codemirror.js +++ b/lib/codemirror.js @@ -1651,7 +1651,7 @@ window.CodeMirror = (function() { if (opera) { lastStoppedKey = handled ? code : null; // Opera has no cut event... we try to at least catch the key combo - if (!handled && code == 88 && e_prop(e, mac ? "metaKey" : "ctrlKey")) + if (!handled && code == 88 && !hasCopyEvent && e_prop(e, mac ? "metaKey" : "ctrlKey")) cm.replaceSelection(""); } } From 1dfad49180c53f6eda29b9a1ee6de7d04400f98c Mon Sep 17 00:00:00 2001 From: Marijn Haverbeke Date: Mon, 12 Nov 2012 10:24:23 +0100 Subject: [PATCH 0317/5780] Remove ambiance-light theme, simply add an extra file that can be used to disable box-shadow Issue #974 --- theme/ambiance-light.css | 72 --------------------------------------- theme/ambiance-mobile.css | 6 ++++ theme/ambiance.css | 4 +-- 3 files changed, 8 insertions(+), 74 deletions(-) delete mode 100644 theme/ambiance-light.css create mode 100644 theme/ambiance-mobile.css diff --git a/theme/ambiance-light.css b/theme/ambiance-light.css deleted file mode 100644 index f835fb785e..0000000000 --- a/theme/ambiance-light.css +++ /dev/null @@ -1,72 +0,0 @@ -/* ambiance theme for code-mirror */ - -/* Color scheme */ - -.cm-s-ambiance .cm-keyword { color: #cda869; } -.cm-s-ambiance .cm-atom { color: #CF7EA9; } -.cm-s-ambiance .cm-number { color: #78CF8A; } -.cm-s-ambiance .cm-def { color: #aac6e3; } -.cm-s-ambiance .cm-variable { color: #ffb795; } -.cm-s-ambiance .cm-variable-2 { color: #eed1b3; } -.cm-s-ambiance .cm-variable-3 { color: #faded3; } -.cm-s-ambiance .cm-property { color: #eed1b3; } -.cm-s-ambiance .cm-operator {color: #fa8d6a;} -.cm-s-ambiance .cm-comment { color: #555; font-style:italic; } -.cm-s-ambiance .cm-string { color: #8f9d6a; } -.cm-s-ambiance .cm-string-2 { color: #9d937c; } -.cm-s-ambiance .cm-meta { color: #D2A8A1; } -.cm-s-ambiance .cm-error { color: #AF2018; } -.cm-s-ambiance .cm-qualifier { color: yellow; } -.cm-s-ambiance .cm-builtin { color: #9999cc; } -.cm-s-ambiance .cm-bracket { color: #24C2C7; } -.cm-s-ambiance .cm-tag { color: #fee4ff } -.cm-s-ambiance .cm-attribute { color: #9B859D; } -.cm-s-ambiance .cm-header {color: blue;} -.cm-s-ambiance .cm-quote { color: #24C2C7; } -.cm-s-ambiance .cm-hr { color: pink; } -.cm-s-ambiance .cm-link { color: #F4C20B; } -.cm-s-ambiance .cm-special { color: #FF9D00; } - -.cm-s-ambiance .CodeMirror-matchingbracket { color: #0f0; } -.cm-s-ambiance .CodeMirror-nonmatchingbracket { color: #f22; } - -.cm-s-ambiance .CodeMirror-selected { - background: rgba(255, 255, 255, 0.15); -} -.CodeMirror-focused .cm-s-ambiance .CodeMirror-selected { - background: rgba(255, 255, 255, 0.10); -} - -/* Editor styling */ - -.cm-s-ambiance { - line-height: 1.40em; - font-family: Monaco, Menlo,"Andale Mono","lucida console","Courier New",monospace !important; - color: #E6E1DC; - background-color: #202020; -} - -.cm-s-ambiance .CodeMirror-gutters { - background: #3D3D3D; - border-right: 1px solid #4D4D4D; - box-shadow: 0 10px 20px black; -} - -.cm-s-ambiance .CodeMirror-linenumber { - padding: 0 5px; - text-shadow: 0px 1px 1px #4d4d4d; - color: #222; -} - -.cm-s-ambiance .CodeMirror-lines .CodeMirror-cursor { - border-left: 1px solid #7991E8; -} - -.cm-s-ambiance .activeline { - background: none repeat scroll 0% 0% rgba(255, 255, 255, 0.031); -} - -.cm-s-ambiance, -.cm-s-ambiance .CodeMirror-gutters { - background-image: url("data:image/png;base64,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"); -} diff --git a/theme/ambiance-mobile.css b/theme/ambiance-mobile.css new file mode 100644 index 0000000000..35b3750c1b --- /dev/null +++ b/theme/ambiance-mobile.css @@ -0,0 +1,6 @@ +.cm-s-ambiance.CodeMirror { + -webkit-box-shadow: none; + -moz-box-shadow: none; + -o-box-shadow: none; + box-shadow: none; +} diff --git a/theme/ambiance.css b/theme/ambiance.css index 9902ecad70..c275442e5f 100644 --- a/theme/ambiance.css +++ b/theme/ambiance.css @@ -1,4 +1,4 @@ -/* ambiance theme for code-mirror */ +/* ambiance theme for codemirror */ /* Color scheme */ @@ -33,7 +33,7 @@ .cm-s-ambiance .CodeMirror-selected { background: rgba(255, 255, 255, 0.15); } -.CodeMirror-focused .cm-s-ambiance .CodeMirror-selected { +.cm-s-ambiance .CodeMirror-focused .CodeMirror-selected { background: rgba(255, 255, 255, 0.10); } From e8cf4aea811f1a4c192a2f5036a1e334b304b3e1 Mon Sep 17 00:00:00 2001 From: Mighty Guava Date: Sun, 11 Nov 2012 04:23:51 -0500 Subject: [PATCH 0318/5780] [vim keymap] Lots of fixes for navigation. Go to line with Shift+G works again. Updated navigation behavior to match closer to vim. dw/de at end of line will no longer delete newline. --- keymap/vim.js | 237 +++++++++++++++++++++++++++++++------------------- 1 file changed, 148 insertions(+), 89 deletions(-) diff --git a/keymap/vim.js b/keymap/vim.js index 28ec4fd8cf..24e8a41023 100644 --- a/keymap/vim.js +++ b/keymap/vim.js @@ -17,7 +17,7 @@ // Entering insert mode: // i, I, a, A, o, O // s -// ce, cb (without support for number of actions like c3e - TODO) +// ce, cb // cc // S, C TODO // cf, cF, ct, cT @@ -26,7 +26,7 @@ // x, X // J // dd, D -// de, db (without support for number of actions like d3e - TODO) +// de, db // df, dF, dt, dT // // Yanking and pasting: @@ -48,18 +48,25 @@ // (function() { - var count = ""; var sdir = "f"; var buf = ""; var yank = 0; var mark = []; - var reptTimes = 0; + var repeatCount = 0; + function isLine(cm, line) { return line >= 0 && line < cm.lineCount(); } function emptyBuffer() { buf = ""; } function pushInBuffer(str) { buf += str; } - function pushCountDigit(digit) { return function(cm) {count += digit;}; } - function popCount() { var i = parseInt(count, 10); count = ""; return i || 1; } + function pushRepeatCountDigit(digit) {return function(cm) {repeatCount = (repeatCount * 10) + digit}; } + function getCountOrOne() { + var i = repeatCount; + return i || 1; + } + function clearCount() { + repeatCount = 0; + } function iterTimes(func) { - for (var i = 0, c = popCount(); i < c; ++i) func(i, i == c - 1); + for (var i = 0, c = getCountOrOne(); i < c; ++i) func(i, i == c - 1); + clearCount(); } function countTimes(func) { if (typeof func == "string") func = CodeMirror.commands[func]; @@ -93,48 +100,104 @@ } var word = [/\w/, /[^\w\s]/], bigWord = [/\S/]; - function findWord(line, pos, dir, regexps) { - var stop = 0, next = -1; - if (dir > 0) { stop = line.length; next = 0; } - var start = stop, end = stop; - // Find bounds of next one. - outer: for (; pos != stop; pos += dir) { - for (var i = 0; i < regexps.length; ++i) { - if (regexps[i].test(line.charAt(pos + next))) { - start = pos; - for (; pos != stop; pos += dir) { - if (!regexps[i].test(line.charAt(pos + next))) break; + // Finds a word on the given line, and continue searching the next line if it can't find one. + function findWord(cm, lineNum, pos, dir, regexps) { + var line = cm.getLine(lineNum); + while (true) { + var stop = (dir > 0) ? line.length : -1; + var wordStart = stop, wordEnd = stop; + // Find bounds of next word. + for (; pos != stop; pos += dir) { + for (var i = 0; i < regexps.length; ++i) { + if (regexps[i].test(line.charAt(pos))) { + wordStart = pos; + // Advance to end of word. + for (; pos != stop && regexps[i].test(line.charAt(pos)); pos += dir) {} + wordEnd = (dir > 0) ? pos : pos + 1; + return { + from: Math.min(wordStart, wordEnd), + to: Math.max(wordStart, wordEnd), + line: lineNum}; } - end = pos; - break outer; } } + // Advance to next/prev line. + lineNum += dir; + if (!isLine(cm, lineNum)) return null; + line = cm.getLine(lineNum); + pos = (dir > 0) ? 0 : line.length; } - return {from: Math.min(start, end), to: Math.max(start, end)}; } - function moveToWord(cm, regexps, dir, times, where) { + /** + * @param {boolean} cm CodeMirror object. + * @param {regexp} regexps Regular expressions for word characters. + * @param {number} dir Direction, +/- 1. + * @param {number} times Number of times to advance word. + * @param {string} where Go to "start" or "end" of word, 'e' vs 'w'. + * @param {boolean} yank Whether we are finding words to yank. If true, + * do not go to the next line to look for the last word. This is to + * prevent deleting new line on 'dw' at the end of a line. + */ + function moveToWord(cm, regexps, dir, times, where, yank) { var cur = cm.getCursor(); - + if (yank) { + where = 'start'; + } for (var i = 0; i < times; i++) { - var line = cm.getLine(cur.line), startCh = cur.ch, word; + var startCh = cur.ch, startLine = cur.line, word; while (true) { - // If we're at start/end of line, start on prev/next respectivly - if (cur.ch == line.length && dir > 0) { - cur.line++; - cur.ch = 0; - line = cm.getLine(cur.line); - } else if (cur.ch == 0 && dir < 0) { - cur.line--; - cur.ch = line.length; - line = cm.getLine(cur.line); + // Search and advance. + word = findWord(cm, cur.line, cur.ch, dir, regexps); + if (word) { + if (yank && times == 1 && dir == 1 && cur.line != word.line) { + // Stop at end of line of last word. Don't want to delete line return + // for dw if the last deleted word is at the end of a line. + cur.ch = cm.getLine(cur.line).length; + break; + } else { + // Move to the word we just found. If by moving to the word we end up + // in the same spot, then move an extra character and search again. + cur.line = word.line; + if (dir > 0 && where == 'end') { + // 'e' + if (startCh != word.to - 1 || startLine != word.line) { + cur.ch = word.to - 1; + break; + } else { + cur.ch = word.to; + } + } else if (dir > 0 && where == 'start') { + // 'w' + if (startCh != word.from || startLine != word.line) { + cur.ch = word.from; + break; + } else { + cur.ch = word.to; + } + } else if (dir < 0 && where == 'end') { + // 'ge' + if (startCh != word.to || startLine != word.line) { + cur.ch = word.to; + break; + } else { + cur.ch = word.from - 1; + } + } else if (dir < 0 && where == 'start') { + // 'b' + if (startCh != word.from || startLine != word.line) { + cur.ch = word.from; + break; + } else { + cur.ch = word.from - 1; + } + } + } + } else { + // No more words to be found. Move to end of document. + for (; isLine(cm, cur.line + dir); cur.line += dir) {} + cur.ch = (dir > 0) ? cm.getLine(cur.line).length : 0; + break; } - if (!line) break; - - // On to the actual searching - word = findWord(line, cur.ch, dir, regexps); - cur.ch = word[where == "end" ? "to" : "from"]; - if (startCh == cur.ch && word.from != word.to) cur.ch = word[dir < 0 ? "from" : "to"]; - else break; } } return cur; @@ -220,7 +283,7 @@ function enterInsertMode(cm) { // enter insert mode: switch mode and cursor - popCount(); + clearCount(); cm.setOption("keyMap", "vim-insert"); } @@ -238,7 +301,8 @@ var map = CodeMirror.keyMap.vim = { // Pipe (|); TODO: should be *screen* chars, so need a util function to turn tabs into spaces? "'|'": function(cm) { - cm.setCursor(cm.getCursor().line, popCount() - 1, true); + cm.setCursor(cm.getCursor().line, getCountOrOne() - 1, true); + clearCount(); }, "A": function(cm) { cm.setCursor(cm.getCursor().line, cm.getCursor().ch+1, true); @@ -300,8 +364,8 @@ if (fn) sdir != "r" ? CodeMirror.commands.findPrev(cm) : fn.findNext(cm); }, "Shift-G": function(cm) { - count == "" ? cm.setCursor(cm.lineCount()) : cm.setCursor(parseInt(count, 10)-1); - popCount(); + (repeatCount == 0) ? cm.setCursor(cm.lineCount()) : cm.setCursor(repeatCount - 1); + clearCount(); CodeMirror.commands.goLineStart(cm); }, "':'": function(cm) { @@ -325,15 +389,6 @@ }; }); - function addCountBindings(keyMap) { - // Add bindings for number keys - keyMap["0"] = function(cm) { - count.length > 0 ? pushCountDigit("0")(cm) : CodeMirror.commands.goLineStart(cm); - }; - for (var i = 1; i < 10; ++i) keyMap[i] = pushCountDigit(i); - } - addCountBindings(CodeMirror.keyMap.vim); - // main num keymap // Add bindings that are influenced by number keys iterObj({ @@ -401,9 +456,12 @@ }); CodeMirror.keyMap["vim-prefix-g"] = { - "E": countTimes(function(cm) { cm.setCursor(moveToWord(cm, word, -1, 1, "start"));}), - "Shift-E": countTimes(function(cm) { cm.setCursor(moveToWord(cm, bigWord, -1, 1, "start"));}), - "G": function (cm) { cm.setCursor({line: 0, ch: cm.getCursor().ch});}, + "E": countTimes(function(cm) { cm.setCursor(moveToWord(cm, word, -1, 1, "end"));}), + "Shift-E": countTimes(function(cm) { cm.setCursor(moveToWord(cm, bigWord, -1, 1, "end"));}), + "G": function (cm) { + cm.setCursor({line: repeatCount - 1, ch: cm.getCursor().ch}); + clearCount(); + }, auto: "vim", nofallthrough: true, style: "fat-cursor" }; @@ -428,8 +486,6 @@ }, nofallthrough: true, style: "fat-cursor" }; - // FIXME - does not work for bindings like "d3e" - addCountBindings(CodeMirror.keyMap["vim-prefix-d"]); CodeMirror.keyMap["vim-prefix-c"] = { "B": function (cm) { @@ -593,10 +649,10 @@ var motionList = ['B', 'E', 'J', 'K', 'H', 'L', 'W', 'Shift-W', "'^'", "'$'", "'%'", 'Esc']; motions = { - 'B': function(cm, times) { return moveToWord(cm, word, -1, times); }, - 'Shift-B': function(cm, times) { return moveToWord(cm, bigWord, -1, times); }, - 'E': function(cm, times) { return moveToWord(cm, word, 1, times, 'end'); }, - 'Shift-E': function(cm, times) { return moveToWord(cm, bigWord, 1, times, 'end'); }, + 'B': function(cm, times, yank) { return moveToWord(cm, word, -1, times, 'start', yank); }, + 'Shift-B': function(cm, times, yank) { return moveToWord(cm, bigWord, -1, times, 'start', yank); }, + 'E': function(cm, times, yank) { return moveToWord(cm, word, 1, times, 'end', yank); }, + 'Shift-E': function(cm, times, yank) { return moveToWord(cm, bigWord, 1, times, 'end', yank); }, 'J': function(cm, times) { var cur = cm.getCursor(); return {line: cur.line+times, ch : cur.ch}; @@ -616,8 +672,8 @@ var cur = cm.getCursor(); return {line: cur.line, ch: cur.ch+times}; }, - 'W': function(cm, times) { return moveToWord(cm, word, 1, times); }, - 'Shift-W': function(cm, times) { return moveToWord(cm, bigWord, 1, times); }, + 'W': function(cm, times, yank) { return moveToWord(cm, word, 1, times, 'start', yank); }, + 'Shift-W': function(cm, times, yank) { return moveToWord(cm, bigWord, 1, times, 'start', yank); }, "'^'": function(cm, times) { var cur = cm.getCursor(); var line = cm.getLine(cur.line).split(''); @@ -637,7 +693,7 @@ "'%'": function(cm) { return findMatchedSymbol(cm, cm.getCursor()); }, "Esc" : function(cm) { cm.setOption('vim'); - reptTimes = 0; + repeatCount = 0; return cm.getCursor(); } @@ -648,7 +704,7 @@ CodeMirror.keyMap['vim-prefix-d'][key] = function(cm) { // Get our selected range var start = cm.getCursor(); - var end = motions[key](cm, reptTimes ? reptTimes : 1); + var end = motions[key](cm, repeatCount ? repeatCount : 1, true); // Set swap var if range is of negative length if ((start.line > end.line) || (start.line == end.line && start.ch > end.ch)) var swap = true; @@ -658,56 +714,59 @@ cm.replaceRange("", swap ? end : start, swap ? start : end); // And clean up - reptTimes = 0; + repeatCount = 0; cm.setOption("keyMap", "vim"); }; CodeMirror.keyMap['vim-prefix-c'][key] = function(cm) { var start = cm.getCursor(); - var end = motions[key](cm, reptTimes ? reptTimes : 1); + var end = motions[key](cm, repeatCount ? repeatCount : 1, true); if ((start.line > end.line) || (start.line == end.line && start.ch > end.ch)) var swap = true; pushInBuffer(cm.getRange(swap ? end : start, swap ? start : end)); cm.replaceRange("", swap ? end : start, swap ? start : end); - reptTimes = 0; + repeatCount = 0; cm.setOption('keyMap', 'vim-insert'); }; CodeMirror.keyMap['vim-prefix-y'][key] = function(cm) { var start = cm.getCursor(); - var end = motions[key](cm, reptTimes ? reptTimes : 1); + var end = motions[key](cm, repeatCount ? repeatCount : 1, true); if ((start.line > end.line) || (start.line == end.line && start.ch > end.ch)) var swap = true; pushInBuffer(cm.getRange(swap ? end : start, swap ? start : end)); - reptTimes = 0; + repeatCount = 0; cm.setOption("keyMap", "vim"); }; CodeMirror.keyMap['vim'][key] = function(cm) { - var cur = motions[key](cm, reptTimes ? reptTimes : 1); + var cur = motions[key](cm, repeatCount ? repeatCount : 1); cm.setCursor(cur.line, cur.ch); - reptTimes = 0; + repeatCount = 0; }; }); - var nums = [1,2,3,4,5,6,7,8,9]; - iterList(nums, function(key, index, array) { - CodeMirror.keyMap['vim'][key] = function (cm) { - reptTimes = (reptTimes * 10) + key; - }; - CodeMirror.keyMap['vim-prefix-d'][key] = function (cm) { - reptTimes = (reptTimes * 10) + key; - }; - CodeMirror.keyMap['vim-prefix-y'][key] = function (cm) { - reptTimes = (reptTimes * 10) + key; - }; - CodeMirror.keyMap['vim-prefix-c'][key] = function (cm) { - reptTimes = (reptTimes * 10) + key; + function addCountBindings(keyMapName) { + // Add bindings for number keys + keyMap = CodeMirror.keyMap[keyMapName]; + keyMap["0"] = function(cm) { + if (repeatCount > 0) { + pushRepeatCountDigit(0)(cm); + } else { + CodeMirror.commands.goLineStart(cm); + } }; - }); + for (var i = 1; i < 10; ++i) { + keyMap[i] = pushRepeatCountDigit(i); + } + } + addCountBindings('vim'); + addCountBindings('vim-prefix-d'); + addCountBindings('vim-prefix-y'); + addCountBindings('vim-prefix-c'); // Create our keymaps for each operator and make xa and xi where x is an operator // change to the corrosponding keymap @@ -721,12 +780,12 @@ }; CodeMirror.keyMap['vim-prefix-'+key]['A'] = function(cm) { - reptTimes = 0; + repeatCount = 0; cm.setOption('keyMap', 'vim-prefix-' + key + 'a'); }; CodeMirror.keyMap['vim-prefix-'+key]['I'] = function(cm) { - reptTimes = 0; + repeatCount = 0; cm.setOption('keyMap', 'vim-prefix-' + key + 'i'); }; }); From 94f2b6fc5ddd05a8f6e1570d4b60bfbcce9e0d23 Mon Sep 17 00:00:00 2001 From: Marijn Haverbeke Date: Mon, 12 Nov 2012 10:41:06 +0100 Subject: [PATCH 0319/5780] Extend zero-width-space replacement hack to use line-height 1 Closes #957 --- lib/codemirror.js | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/lib/codemirror.js b/lib/codemirror.js index b4a67b261a..8da2d2e34f 100644 --- a/lib/codemirror.js +++ b/lib/codemirror.js @@ -4096,7 +4096,7 @@ window.CodeMirror = (function() { zwspSupported = test.offsetWidth <= 1 && test.offsetHeight > 2; } if (zwspSupported) return elt("span", "\u200b"); - else return elt("span", "\u00a0", null, "display: inline-block; width: 1px; margin-right: -1px"); + else return elt("span", "\u00a0", null, "display: inline-block; width: 1px; margin-right: -1px; line-height: 1"); } // See if "".split is the broken IE version, if so, provide an From 15e7bbee1a07e3d1fe018b53d64431a96490aa7b Mon Sep 17 00:00:00 2001 From: Irakli Gozalishvili Date: Sun, 11 Nov 2012 13:59:04 -0800 Subject: [PATCH 0320/5780] Port solarized theme to code-mirror. --- demo/theme.html | 6 +- theme/solarized.css | 207 ++++++++++++++++++++++++++++++++++++++++++++ 2 files changed, 212 insertions(+), 1 deletion(-) create mode 100644 theme/solarized.css diff --git a/demo/theme.html b/demo/theme.html index f8471202fa..ecf1e125a6 100644 --- a/demo/theme.html +++ b/demo/theme.html @@ -18,6 +18,7 @@ + @@ -55,6 +56,8 @@

    CodeMirror: Theme demo

    + + @@ -69,7 +72,8 @@

    CodeMirror: Theme demo

    var theme = input.options[input.selectedIndex].innerHTML; editor.setOption("theme", theme); } - var choice = document.location.search && document.location.search.slice(1); + var choice = document.location.search && + decodeURIComponent(document.location.search.slice(1)); if (choice) { input.value = choice; editor.setOption("theme", choice); diff --git a/theme/solarized.css b/theme/solarized.css new file mode 100644 index 0000000000..06a6c7fa1b --- /dev/null +++ b/theme/solarized.css @@ -0,0 +1,207 @@ +/* +Solarized theme for code-mirror +http://ethanschoonover.com/solarized +*/ + +/* +Solarized color pallet +http://ethanschoonover.com/solarized/img/solarized-palette.png +*/ + +.solarized.base03 { color: #002b36; } +.solarized.base02 { color: #073642; } +.solarized.base01 { color: #586e75; } +.solarized.base00 { color: #657b83; } +.solarized.base0 { color: #839496; } +.solarized.base1 { color: #93a1a1; } +.solarized.base2 { color: #eee8d5; } +.solarized.base3 { color: #fdf6e3; } +.solarized.solar-yellow { color: #b58900; } +.solarized.solar-orange { color: #cb4b16; } +.solarized.solar-red { color: #dc322f; } +.solarized.solar-magenta { color: #d33682; } +.solarized.solar-violet { color: #6c71c4; } +.solarized.solar-blue { color: #268bd2; } +.solarized.solar-cyan { color: #2aa198; } +.solarized.solar-green { color: #859900; } + +/* Color scheme for code-mirror */ + +.cm-s-solarized { + line-height: 1.45em; + font-family: Menlo,Monaco,"Andale Mono","lucida console","Courier New",monospace !important; + color-profile: sRGB; + rendering-intent: auto; +} +.cm-s-solarized.cm-s-dark { + color: #839496; + background-color: #002b36; + text-shadow: #002b36 0 1px; +} +.cm-s-solarized.cm-s-light { + background-color: #fdf6e3; + color: #657b83; + text-shadow: #eee8d5 0 1px; +} + +.cm-s-solarized .CodeMirror-widget { + text-shadow: none; +} + + +.cm-s-solarized .cm-keyword { color: #cb4b16 } +.cm-s-solarized .cm-atom { color: #d33682; } +.cm-s-solarized .cm-number { color: #d33682; } +.cm-s-solarized .cm-def { color: #2aa198; } + +.cm-s-solarized .cm-variable { color: #268bd2; } +.cm-s-solarized .cm-variable-2 { color: #b58900; } +.cm-s-solarized .cm-variable-3 { color: #6c71c4; } + +.cm-s-solarized .cm-property { color: #2aa198; } +.cm-s-solarized .cm-operator {color: #6c71c4;} + +.cm-s-solarized .cm-comment { color: #586e75; font-style:italic; } + +.cm-s-solarized .cm-string { color: #859900; } +.cm-s-solarized .cm-string-2 { color: #b58900; } + +.cm-s-solarized .cm-meta { color: #859900; } +.cm-s-solarized .cm-error, +.cm-s-solarized .cm-invalidchar { + color: #586e75; + border-bottom: 1px dotted #dc322f; +} +.cm-s-solarized .cm-qualifier { color: #b58900; } +.cm-s-solarized .cm-builtin { color: #d33682; } +.cm-s-solarized .cm-bracket { color: #cb4b16; } +.cm-s-solarized .CodeMirror-matchingbracket { color: #859900; } +.cm-s-solarized .CodeMirror-nonmatchingbracket { color: #dc322f; } +.cm-s-solarized .cm-tag { color: #93a1a1 } +.cm-s-solarized .cm-attribute { color: #2aa198; } +.cm-s-solarized .cm-header { color: #586e75; } +.cm-s-solarized .cm-quote { color: #93a1a1; } +.cm-s-solarized .cm-hr { + color: transparent; + border-top: 1px solid #586e75; + display: block; +} +.cm-s-solarized .cm-link { color: #93a1a1; cursor: pointer; } +.cm-s-solarized .cm-special { color: #6c71c4; } +.cm-s-solarized .cm-em { + color: #999; + text-decoration: underline; + text-decoration-style: dotted; +} +.cm-s-solarized .cm-strong { color: #eee; } +.cm-s-solarized .cm-tab:before { + content: "➤"; /*visualize tab character*/ + color: #586e75; +} + +.cm-s-solarized.cm-s-dark .CodeMirror-focused .CodeMirror-selected { + background: #386774; + color: inherit; +} + +.cm-s-solarized.cm-s-dark ::selection { + background: #386774; + color: inherit; +} + +.cm-s-solarized.cm-s-dark .CodeMirror-selected { + background: #586e75; +} + +.cm-s-solarized.cm-s-light .CodeMirror-focused .CodeMirror-selected { + background: #eee8d5; + color: inherit; +} + +.cm-s-solarized.cm-s-light ::selection { + background: #eee8d5; + color: inherit; +} + +.cm-s-solarized.cm-s-light .CodeMirror-selected { + background: #93a1a1; +} + + + +/* Editor styling */ + + + +/* Little shadow on the view-port of the buffer view */ +.cm-s-solarized.CodeMirror { + -moz-box-shadow: inset 7px 0 12px -6px #000; + -webkit-box-shadow: inset 7px 0 12px -6px #000; + box-shadow: inset 7px 0 12px -6px #000; +} + +/* Gutter border and some shadow from it */ +.cm-s-solarized .CodeMirror-gutters { + padding: 0 15px 0 10px; + box-shadow: 0 10px 20px black; + border-right: 1px solid; +} + +/* Gutter colors and line number styling based of color scheme (dark / light) */ + +/* Dark */ +.cm-s-solarized.cm-s-dark .CodeMirror-gutters { + background-color: #073642; + border-color: #00232c; +} + +.cm-s-solarized.cm-s-dark .CodeMirror-linenumber { + text-shadow: #021014 0 -1px; +} + +/* Light */ +.cm-s-solarized.cm-s-light .CodeMirror-gutters { + background-color: #eee8d5; + border-color: #eee8d5; +} + +/* Common */ +.cm-s-solarized .CodeMirror-linenumber { + color: #586e75; +} + +.cm-s-solarized .CodeMirror-gutter .CodeMirror-gutter-text { + color: #586e75; +} + +.cm-s-solarized .CodeMirror-lines { + padding-left: 5px; +} + +.cm-s-solarized .CodeMirror-lines .CodeMirror-cursor { + border-left: 1px solid #819090; +} + +/* +Active line. Negative margin compensates left padding of the text in the +view-port +*/ +.cm-s-solarized .activeline { + margin-left: -20px; +} + +.cm-s-solarized.cm-s-dark .activeline { + background: rgba(255, 255, 255, 0.05); + +} +.cm-s-solarized.cm-s-light .activeline { + background: rgba(0, 0, 0, 0.05); +} + +/* +View-port and gutter both get little noise background to give it a real feel. +*/ +.cm-s-solarized.CodeMirror, +.cm-s-solarized .CodeMirror-gutters { + background-image: url("data:image/png;base64,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"); +} From fd2a7ab9f5f8fd96a58f5d057563e86cc9f0f4fb Mon Sep 17 00:00:00 2001 From: Marijn Haverbeke Date: Mon, 12 Nov 2012 11:35:25 +0100 Subject: [PATCH 0321/5780] Various IE fixes Deal with IE7's failure to compute proper offsets by switching line measurement to getBoundingClientRect (more precise anyway). Assume IE7 never supports zero-width spaces (it does just enough to throw off the feature detection, but not enough to actually help). Compensate for scrollHeight sometimes being smaller than content height in IE7 and 8. Closes #953 Closes #954 Closes #955 --- lib/codemirror.js | 20 +++++++++++--------- 1 file changed, 11 insertions(+), 9 deletions(-) diff --git a/lib/codemirror.js b/lib/codemirror.js index 8da2d2e34f..2a5f304152 100644 --- a/lib/codemirror.js +++ b/lib/codemirror.js @@ -289,14 +289,16 @@ window.CodeMirror = (function() { // Re-synchronize the fake scrollbars with the actual size of the // content. Optionally force a scrollTop. function updateScrollbars(d /* display */, docHeight) { - d.sizer.style.minHeight = d.heightForcer.style.top = (docHeight + 2 * paddingTop(d)) + "px"; + var totalHeight = docHeight + 2 * paddingTop(d); + d.sizer.style.minHeight = d.heightForcer.style.top = totalHeight + "px"; + var scrollHeight = Math.max(totalHeight, d.scroller.scrollHeight); var needsH = d.scroller.scrollWidth > d.scroller.clientWidth; - var needsV = d.scroller.scrollHeight > d.scroller.clientHeight; + var needsV = scrollHeight > d.scroller.clientHeight; if (needsV) { d.scrollbarV.style.display = "block"; d.scrollbarV.style.bottom = needsH ? scrollbarWidth(d.measure) + "px" : "0"; d.scrollbarV.firstChild.style.height = - (d.scroller.scrollHeight - d.scroller.clientHeight + d.scrollbarV.clientHeight) + "px"; + (scrollHeight - d.scroller.clientHeight + d.scrollbarV.clientHeight) + "px"; } else d.scrollbarV.style.display = ""; if (needsH) { d.scrollbarH.style.display = "block"; @@ -447,14 +449,12 @@ window.CodeMirror = (function() { display.showingFrom = from; display.showingTo = to; startWorker(cm, 100); - var lastHeight = display.lineDiv.offsetTop; for (var node = display.lineDiv.firstChild; node; node = node.nextSibling) { - var end = node.offsetHeight + node.offsetTop; - var height = end - lastHeight, diff = node.lineObj.height - height; + var box = node.getBoundingClientRect(); + var height = box.bottom - box.top, diff = node.lineObj.height - height; if (height < 2) height = textHeight(display); if (diff > .001 || diff < -.001) updateLineHeight(node.lineObj, height); - lastHeight = end; } display.viewOffset = heightAtLine(cm, getLine(doc, from)); // Position the mover div to align with the current virtual scroll position @@ -4093,10 +4093,12 @@ window.CodeMirror = (function() { var test = elt("span", "\u200b"); removeChildrenAndAdd(measure, elt("span", [test, document.createTextNode("x")])); if (measure.firstChild.offsetHeight != 0) - zwspSupported = test.offsetWidth <= 1 && test.offsetHeight > 2; + zwspSupported = test.offsetWidth <= 1 && test.offsetHeight > 2 && !ie_lt8; } if (zwspSupported) return elt("span", "\u200b"); - else return elt("span", "\u00a0", null, "display: inline-block; width: 1px; margin-right: -1px; line-height: 1"); + var span = elt("span", "\u00a0", null, "display: inline-block; width: 1px; margin-right: -1px"); + if (!ie_lt8) span.style.lineHeight = 1; + return span; } // See if "".split is the broken IE version, if so, provide an From ce4436ffae7eefbc8747a07e32bae4b893d8c719 Mon Sep 17 00:00:00 2001 From: Marijn Haverbeke Date: Mon, 12 Nov 2012 11:38:42 +0100 Subject: [PATCH 0322/5780] Remove line-height hack on zero-width elements It didn't work Issue #957 --- lib/codemirror.js | 4 +--- 1 file changed, 1 insertion(+), 3 deletions(-) diff --git a/lib/codemirror.js b/lib/codemirror.js index 2a5f304152..000eeeed19 100644 --- a/lib/codemirror.js +++ b/lib/codemirror.js @@ -4096,9 +4096,7 @@ window.CodeMirror = (function() { zwspSupported = test.offsetWidth <= 1 && test.offsetHeight > 2 && !ie_lt8; } if (zwspSupported) return elt("span", "\u200b"); - var span = elt("span", "\u00a0", null, "display: inline-block; width: 1px; margin-right: -1px"); - if (!ie_lt8) span.style.lineHeight = 1; - return span; + else return elt("span", "\u00a0", null, "display: inline-block; width: 1px; margin-right: -1px"); } // See if "".split is the broken IE version, if so, provide an From c6118dd99784216a8855daded13c243fe6d48cc6 Mon Sep 17 00:00:00 2001 From: Marijn Haverbeke Date: Mon, 12 Nov 2012 11:53:47 +0100 Subject: [PATCH 0323/5780] Reduce impact of zero-width element linespace variations In the (common) case where we simply want to measure an empty line, use a non-breaking-space since the empty line won't wrap anyway. Closes #957 --- lib/codemirror.js | 10 +++++++--- 1 file changed, 7 insertions(+), 3 deletions(-) diff --git a/lib/codemirror.js b/lib/codemirror.js index 000eeeed19..5ab7e6afe2 100644 --- a/lib/codemirror.js +++ b/lib/codemirror.js @@ -3432,8 +3432,9 @@ window.CodeMirror = (function() { } function lineContent(cm, realLine, measure) { - var merged, line = realLine, lineBefore, sawBefore; + var merged, line = realLine, lineBefore, sawBefore, simple = true; while (merged = collapsedSpanAtStart(line)) { + simple = false; line = getLine(cm.view.doc, merged.find().from.line); if (!lineBefore) lineBefore = line; } @@ -3454,11 +3455,14 @@ window.CodeMirror = (function() { } var next = insertLineContent(line, builder); sawBefore = line == lineBefore; - if (next) line = getLine(cm.view.doc, next.to.line); + if (next) { + line = getLine(cm.view.doc, next.to.line); + simple = false; + } } while (next); if (measure && !builder.addedOne) - measure[0] = builder.pre.appendChild(zeroWidthElement(cm.display.measure)); + measure[0] = builder.pre.appendChild(simple ? elt("span", "\u00a0") : zeroWidthElement(cm.display.measure)); if (!builder.pre.firstChild && !lineIsHidden(realLine)) builder.pre.appendChild(document.createTextNode("\u00a0")); From 3c6977472008ea2c490551793c4434cd2f453a53 Mon Sep 17 00:00:00 2001 From: Marijn Haverbeke Date: Mon, 12 Nov 2012 12:44:27 +0100 Subject: [PATCH 0324/5780] Move gutter background into scroller element Hopefully closes #791 (please test) --- lib/codemirror.js | 17 ++++++++--------- 1 file changed, 8 insertions(+), 9 deletions(-) diff --git a/lib/codemirror.js b/lib/codemirror.js index 5ab7e6afe2..433eb26442 100644 --- a/lib/codemirror.js +++ b/lib/codemirror.js @@ -117,17 +117,17 @@ window.CodeMirror = (function() { null, "position: relative; outline: none"); // Moved around its parent to cover visible view d.mover = elt("div", [elt("div", [d.lineSpace], "CodeMirror-lines")], null, "position: relative"); - d.gutters = elt("div", null, "CodeMirror-gutters"); - d.lineGutter = null; // Set to the height of the text, causes scrolling d.sizer = elt("div", [d.mover], "CodeMirror-sizer"); // D is needed because behavior of elts with overflow: auto and padding is inconsistent across browsers d.heightForcer = elt("div", "\u00a0", null, "position: absolute; height: " + scrollerCutOff + "px"); + d.gutters = elt("div", null, "CodeMirror-gutters"); + d.lineGutter = null; // Provides scrolling - d.scroller = elt("div", [d.sizer, d.heightForcer], "CodeMirror-scroll"); + d.scroller = elt("div", [d.sizer, d.heightForcer, d.gutters], "CodeMirror-scroll"); d.scroller.setAttribute("tabIndex", "-1"); // The element in which the editor lives. - d.wrapper = elt("div", [d.gutters, d.inputDiv, d.scrollbarH, d.scrollbarV, + d.wrapper = elt("div", [d.inputDiv, d.scrollbarH, d.scrollbarV, d.scrollbarFiller, d.scroller], "CodeMirror"); // Work around IE7 z-index bug if (ie_lt8) { d.gutters.style.zIndex = -1; d.scroller.style.paddingRight = 0; } @@ -1178,7 +1178,6 @@ window.CodeMirror = (function() { function registerEventHandlers(cm) { var d = cm.display; on(d.scroller, "mousedown", operation(cm, onMouseDown)); - on(d.gutters, "mousedown", operation(cm, clickInGutter)); on(d.scroller, "dblclick", operation(cm, e_preventDefault)); on(d.lineSpace, "selectstart", function(e) { if (!mouseEventInWidget(d, e)) e_preventDefault(e); @@ -1292,7 +1291,7 @@ window.CodeMirror = (function() { } return; } - if (clickInGutter.call(cm, e)) return; + if (clickInGutter(cm, e)) return; var start = posFromMouse(cm, e); switch (e_button(e)) { @@ -1460,11 +1459,11 @@ window.CodeMirror = (function() { } } - function clickInGutter(e) { - var cm = this, display = cm.display; + function clickInGutter(cm, e) { + var display = cm.display; try { var mX = e.clientX, mY = e.clientY; } catch(e) { return false; } - + if (mX >= Math.floor(display.gutters.getBoundingClientRect().right)) return false; e_preventDefault(e); if (!hasHandler(cm, "gutterClick")) return true; From db5e2d1137620849db9a5ab6cac6c5c048f86713 Mon Sep 17 00:00:00 2001 From: Marijn Haverbeke Date: Mon, 12 Nov 2012 12:44:37 +0100 Subject: [PATCH 0325/5780] Remove jsdares from real-world use list again --- doc/realworld.html | 1 - 1 file changed, 1 deletion(-) diff --git a/doc/realworld.html b/doc/realworld.html index ed6b8203f1..28ea162d16 100644 --- a/doc/realworld.html +++ b/doc/realworld.html @@ -47,7 +47,6 @@

    { } CodeMi
  • Joomla plugin
  • jQuery fundamentals (interactive tutorial)
  • jsbin.com (JS playground)
  • -
  • jsdares (interactive learning)
  • JSHint (JS linter)
  • kl1p (paste service)
  • Light Table (experimental IDE)
  • From ef071ab4605d4e6de6afdec298a5f0f2d8718fbc Mon Sep 17 00:00:00 2001 From: Marijn Haverbeke Date: Mon, 12 Nov 2012 14:50:08 +0100 Subject: [PATCH 0326/5780] Remove bounds estimation from coordsCharInner It's no longer useful (since searching within a line is fast), and apparently buggy. Issue #934 --- lib/codemirror.js | 18 +++--------------- 1 file changed, 3 insertions(+), 15 deletions(-) diff --git a/lib/codemirror.js b/lib/codemirror.js index 433eb26442..f0e2303319 100644 --- a/lib/codemirror.js +++ b/lib/codemirror.js @@ -966,21 +966,9 @@ window.CodeMirror = (function() { } var bidi = getOrder(lineObj), dist = lineObj.text.length; - var from = lineLeft(lineObj), to = lineRight(lineObj), fromX = paddingLeft(cm.display), toX; - if (!bidi) { - // Guess a suitable upper bound for our search. - var estimated = Math.min(to, Math.ceil((x + Math.floor(innerOff / textHeight(cm.display)) * - cWidth * .9) / charWidth(cm.display))); - for (;;) { - var estX = getX(estimated); - if (estX <= x && estimated < to) estimated = Math.min(to, Math.ceil(estimated * 1.2)); - else {toX = estX; to = estimated; break;} - } - // Try to guess a suitable lower bound as well. - estimated = Math.floor(to * 0.8); estX = getX(estimated); - if (estX < x) {from = estimated; fromX = estX;} - dist = to - from; - } else toX = getX(to); + var from = lineLeft(lineObj), to = lineRight(lineObj); + var fromX = paddingLeft(cm.display), toX = getX(to); + if (x > toX) return {line: lineNo, ch: to, outside: wrongLine}; // Do a binary search between these bounds. for (;;) { From 6199c06f9e7cbb6d1e76fb958add2c743f87f889 Mon Sep 17 00:00:00 2001 From: Marijn Haverbeke Date: Mon, 12 Nov 2012 17:12:42 +0100 Subject: [PATCH 0327/5780] Amend gutter patch to actually size gutter correctly --- lib/codemirror.js | 20 +++++++++----------- 1 file changed, 9 insertions(+), 11 deletions(-) diff --git a/lib/codemirror.js b/lib/codemirror.js index f0e2303319..864524c6b3 100644 --- a/lib/codemirror.js +++ b/lib/codemirror.js @@ -121,10 +121,13 @@ window.CodeMirror = (function() { d.sizer = elt("div", [d.mover], "CodeMirror-sizer"); // D is needed because behavior of elts with overflow: auto and padding is inconsistent across browsers d.heightForcer = elt("div", "\u00a0", null, "position: absolute; height: " + scrollerCutOff + "px"); + // Will contain the gutters, if any d.gutters = elt("div", null, "CodeMirror-gutters"); d.lineGutter = null; + // Helper element to properly size the gutter backgrounds + var scrollerInner = elt("div", [d.sizer, d.heightForcer, d.gutters], null, "position: relative; min-height: 100%"); // Provides scrolling - d.scroller = elt("div", [d.sizer, d.heightForcer, d.gutters], "CodeMirror-scroll"); + d.scroller = elt("div", [scrollerInner], "CodeMirror-scroll"); d.scroller.setAttribute("tabIndex", "-1"); // The element in which the editor lives. d.wrapper = elt("div", [d.inputDiv, d.scrollbarH, d.scrollbarV, @@ -198,7 +201,7 @@ window.CodeMirror = (function() { var guess = Math.ceil(line.text.length / perLine) || 1; if (guess != 1) updateLineHeight(line, guess * th); }); - cm.display.sizer.style.minWidth = ""; + cm.display.mover.style.minWidth = ""; } else { cm.display.wrapper.className = cm.display.wrapper.className.replace(" CodeMirror-wrap", ""); computeMaxLength(cm.view); @@ -328,10 +331,11 @@ window.CodeMirror = (function() { function alignVertically(display) { if (!display.alignWidgets && !display.gutters.firstChild) return; - var l = compensateForHScroll(display) + "px"; + var comp = compensateForHScroll(display), gutterW = display.gutters.offsetWidth, l = comp + "px"; for (var n = display.lineDiv.firstChild; n; n = n.nextSibling) if (n.alignable) { for (var i = 0, a = n.alignable; i < a.length; ++i) a[i].style.left = l; } + display.gutters.style.left = (comp + gutterW) + "px"; } function maybeUpdateLineNumberWidth(cm) { @@ -2057,14 +2061,8 @@ window.CodeMirror = (function() { function scrollIntoView(cm, x1, y1, x2, y2) { var scrollPos = calculateScrollPos(cm, x1, y1, x2, y2), display = cm.display; - if (scrollPos.scrollLeft != null) { - display.scrollbarH.scrollLeft = display.scroller.scrollLeft = scrollPos.scrollLeft; - cm.view.scrollLeft = display.scroller.scrollLeft; - } - if (scrollPos.scrollTop != null) { - display.scrollbarV.scrollTop = display.scroller.scrollTop = scrollPos.scrollTop; - cm.view.scrollTop = display.scroller.scrollTop; - } + if (scrollPos.scrollTop != null) setScrollTop(cm, scrollPos.scrollTop); + if (scrollPos.scrollLeft != null) setScrollLeft(cm, scrollPos.scrollLeft); } function calculateScrollPos(cm, x1, y1, x2, y2) { From a4bf5b9626429c493d8cf3f21c5af91fb2b50b0f Mon Sep 17 00:00:00 2001 From: Matt Sacks Date: Mon, 12 Nov 2012 08:02:53 -0800 Subject: [PATCH 0328/5780] Refactor the Shift-D keymapping for vim Before, Shift-D would delete the entire line with the vim keymapping set. Vim's binding for this deletes from the cursor up til the end of the line, so that's been fixed. Also, allow for count iterations on Shift-D. --- keymap/vim.js | 28 +++++++++++++++++++--------- 1 file changed, 19 insertions(+), 9 deletions(-) diff --git a/keymap/vim.js b/keymap/vim.js index 24e8a41023..70c85ce7a3 100644 --- a/keymap/vim.js +++ b/keymap/vim.js @@ -324,16 +324,26 @@ }, "G": function(cm) { cm.setOption("keyMap", "vim-prefix-g");}, "Shift-D": function(cm) { - // commented out verions works, but I left original, cause maybe - // I don't know vim enouth to see what it does - /* var cur = cm.getCursor(); - var f = {line: cur.line, ch: cur.ch}, t = {line: cur.line}; - pushInBuffer(cm.getRange(f, t)); - */ + var cursor = cm.getCursor(); + var lineN = cursor.line; + var line = cm.getLine(lineN); + cm.setLine(lineN, line.slice(0, cursor.ch)); + emptyBuffer(); - mark["Shift-D"] = cm.getCursor(false).line; - cm.setCursor(cm.getCursor(true).line); - delTillMark(cm,"Shift-D"); mark = []; + pushInBuffer(line.slice(cursor.ch)); + + if (repeatCount > 1) { + // we've already done it once + --repeatCount; + // the lines dissapear (ie, cursor stays on the same lineN), + // so only incremenet once + ++lineN; + + iterTimes(function(i) { + pushInBuffer(cm.getLine(lineN)); + cm.removeLine(lineN); + }); + } }, "S": function (cm) { From 543cbff64dd7cab0449ccb3cad484d227b403507 Mon Sep 17 00:00:00 2001 From: Marijn Haverbeke Date: Mon, 12 Nov 2012 17:17:38 +0100 Subject: [PATCH 0329/5780] Shhh, bot, it's okay --- lib/codemirror.js | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/lib/codemirror.js b/lib/codemirror.js index 864524c6b3..85a012e582 100644 --- a/lib/codemirror.js +++ b/lib/codemirror.js @@ -2060,7 +2060,7 @@ window.CodeMirror = (function() { } function scrollIntoView(cm, x1, y1, x2, y2) { - var scrollPos = calculateScrollPos(cm, x1, y1, x2, y2), display = cm.display; + var scrollPos = calculateScrollPos(cm, x1, y1, x2, y2); if (scrollPos.scrollTop != null) setScrollTop(cm, scrollPos.scrollTop); if (scrollPos.scrollLeft != null) setScrollLeft(cm, scrollPos.scrollLeft); } From 3d5f47c0e4282a474aa585a681e52fba385628f8 Mon Sep 17 00:00:00 2001 From: Marijn Haverbeke Date: Tue, 13 Nov 2012 17:36:57 +0100 Subject: [PATCH 0330/5780] Make insertTab, defaultTab, and newlineAndIndent commands mark their changes as input This helps the undo history notice that it may combine them. Issue #970 --- lib/codemirror.js | 14 +++++++------- 1 file changed, 7 insertions(+), 7 deletions(-) diff --git a/lib/codemirror.js b/lib/codemirror.js index 85a012e582..c897148250 100644 --- a/lib/codemirror.js +++ b/lib/codemirror.js @@ -1442,7 +1442,7 @@ window.CodeMirror = (function() { var curFrom = cm.view.sel.from, curTo = cm.view.sel.to; setSelectionUser(cm, pos, pos); if (cm.view.draggingText) replaceRange(cm, "", curFrom, curTo, "paste"); - cm.replaceSelection(text); + cm.replaceSelection(text, null, "paste"); focusInput(cm); onFocus(cm); } @@ -2205,9 +2205,9 @@ window.CodeMirror = (function() { getSelection: function(lineSep) { return this.getRange(this.view.sel.from, this.view.sel.to, lineSep); }, - replaceSelection: operation(null, function(code, collapse) { + replaceSelection: operation(null, function(code, collapse, origin) { var sel = this.view.sel; - updateDoc(this, sel.from, sel.to, splitLines(code), collapse || "around"); + updateDoc(this, sel.from, sel.to, splitLines(code), collapse || "around", origin); }), focus: function(){window.focus(); focusInput(this); onFocus(this); fastPoll(this);}, @@ -2828,10 +2828,10 @@ window.CodeMirror = (function() { indentAuto: function(cm) {cm.indentSelection("smart");}, indentMore: function(cm) {cm.indentSelection("add");}, indentLess: function(cm) {cm.indentSelection("subtract");}, - insertTab: function(cm) {cm.replaceSelection("\t", "end");}, + insertTab: function(cm) {cm.replaceSelection("\t", "end", "input");}, defaultTab: function(cm) { if (cm.somethingSelected()) cm.indentSelection("add"); - else cm.replaceSelection("\t", "end"); + else cm.replaceSelection("\t", "end", "input"); }, transposeChars: function(cm) { var cur = cm.getCursor(), line = cm.getLine(cur.line); @@ -2841,7 +2841,7 @@ window.CodeMirror = (function() { }, newlineAndIndent: function(cm) { operation(cm, function() { - cm.replaceSelection("\n", "end"); + cm.replaceSelection("\n", "end", "input"); cm.indentLine(cm.getCursor().line, null, true); })(); }, @@ -3815,7 +3815,7 @@ window.CodeMirror = (function() { if (cur && (history.lastOp == cm.curOp.id || history.lastOrigin == origin && (origin == "input" || origin == "delete") && - history.lastTime > time - 400)) { + history.lastTime > time - 600)) { // Merge this change into the last event var last = lst(cur.events); if (last.start > start + old.length || last.start + last.added < start) { From 6fcfb054f05330458982b73f07c3f1819d8aed22 Mon Sep 17 00:00:00 2001 From: Marijn Haverbeke Date: Tue, 13 Nov 2012 17:48:31 +0100 Subject: [PATCH 0331/5780] Don't use floats for gutters Opera will put them below each other, and mess up the layout. display: inline-block appears to work better. Closes #968 --- lib/codemirror.css | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/lib/codemirror.css b/lib/codemirror.css index ea0ecea592..4359dc9472 100644 --- a/lib/codemirror.css +++ b/lib/codemirror.css @@ -150,7 +150,7 @@ div.CodeMirror span.CodeMirror-nonmatchingbracket {color: #f22;} } .CodeMirror-gutter { height: 100%; - float: left; + display: inline-block; } .CodeMirror-gutter-elt { position: absolute; From b60d6ebb3eb7d25b60458cdc2882c33d1d074d00 Mon Sep 17 00:00:00 2001 From: Jan Keromnes Date: Tue, 13 Nov 2012 14:45:39 +0800 Subject: [PATCH 0332/5780] [vim] `mark` should be a hashmap, not an array --- keymap/vim.js | 6 +++--- 1 file changed, 3 insertions(+), 3 deletions(-) diff --git a/keymap/vim.js b/keymap/vim.js index 70c85ce7a3..2699f78269 100644 --- a/keymap/vim.js +++ b/keymap/vim.js @@ -51,7 +51,7 @@ var sdir = "f"; var buf = ""; var yank = 0; - var mark = []; + var mark = {}; var repeatCount = 0; function isLine(cm, line) { return line >= 0 && line < cm.lineCount(); } function emptyBuffer() { buf = ""; } @@ -352,13 +352,13 @@ })(cm); enterInsertMode(cm); }, - "M": function(cm) {cm.setOption("keyMap", "vim-prefix-m"); mark = [];}, + "M": function(cm) {cm.setOption("keyMap", "vim-prefix-m"); mark = {};}, "Y": function(cm) {cm.setOption("keyMap", "vim-prefix-y"); emptyBuffer(); yank = 0;}, "Shift-Y": function(cm) { emptyBuffer(); mark["Shift-D"] = cm.getCursor(false).line; cm.setCursor(cm.getCursor(true).line); - yankTillMark(cm,"Shift-D"); mark = []; + yankTillMark(cm,"Shift-D"); mark = {}; }, "/": function(cm) {var f = CodeMirror.commands.find; f && f(cm); sdir = "f";}, "'?'": function(cm) { From e02f25c5de6fac535913927824de2158a26feb65 Mon Sep 17 00:00:00 2001 From: Jan Keromnes Date: Tue, 13 Nov 2012 15:10:30 +0800 Subject: [PATCH 0333/5780] [vim] fix cursor jump in `Shift-Y` Also refactor `countTimes` and get rid of confusing global `yank`. --- keymap/vim.js | 25 +++++++++++-------------- 1 file changed, 11 insertions(+), 14 deletions(-) diff --git a/keymap/vim.js b/keymap/vim.js index 2699f78269..b5309b56e1 100644 --- a/keymap/vim.js +++ b/keymap/vim.js @@ -50,7 +50,6 @@ (function() { var sdir = "f"; var buf = ""; - var yank = 0; var mark = {}; var repeatCount = 0; function isLine(cm, line) { return line >= 0 && line < cm.lineCount(); } @@ -70,7 +69,7 @@ } function countTimes(func) { if (typeof func == "string") func = CodeMirror.commands[func]; - return function(cm) { iterTimes(function () { func(cm); }); }; + return function(cm) { iterTimes(function (i, last) { func(cm, i, last); }); }; } function iterObj(o, f) { @@ -220,7 +219,7 @@ var l = cm.getCursor().line, start = i > l ? l : i, end = i > l ? i : l; cm.setCursor(start); for (var c = start; c <= end; c++) { - pushInBuffer("\n"+cm.getLine(start)); + pushInBuffer("\n" + cm.getLine(start)); cm.removeLine(start); } } @@ -232,7 +231,7 @@ } var l = cm.getCursor().line, start = i > l ? l : i, end = i > l ? i : l; for (var c = start; c <= end; c++) { - pushInBuffer("\n"+cm.getLine(c)); + pushInBuffer("\n" + cm.getLine(c)); } cm.setCursor(start); } @@ -339,7 +338,7 @@ // so only incremenet once ++lineN; - iterTimes(function(i) { + iterTimes(function() { pushInBuffer(cm.getLine(lineN)); cm.removeLine(lineN); }); @@ -353,12 +352,10 @@ enterInsertMode(cm); }, "M": function(cm) {cm.setOption("keyMap", "vim-prefix-m"); mark = {};}, - "Y": function(cm) {cm.setOption("keyMap", "vim-prefix-y"); emptyBuffer(); yank = 0;}, + "Y": function(cm) {cm.setOption("keyMap", "vim-prefix-y"); emptyBuffer();}, "Shift-Y": function(cm) { emptyBuffer(); - mark["Shift-D"] = cm.getCursor(false).line; - cm.setCursor(cm.getCursor(true).line); - yankTillMark(cm,"Shift-D"); mark = {}; + iterTimes(function(i) { pushInBuffer("\n" + cm.getLine(cm.getCursor().line + i)); }); }, "/": function(cm) {var f = CodeMirror.commands.find; f && f(cm); sdir = "f";}, "'?'": function(cm) { @@ -477,7 +474,7 @@ CodeMirror.keyMap["vim-prefix-d"] = { "D": countTimes(function(cm) { - pushInBuffer("\n"+cm.getLine(cm.getCursor().line)); + pushInBuffer("\n" + cm.getLine(cm.getCursor().line)); cm.removeLine(cm.getCursor().line); cm.setOption("keyMap", "vim"); }), @@ -537,10 +534,10 @@ mark[m] = cm.getCursor().line; }; CodeMirror.keyMap["vim-prefix-d'"][m] = function(cm) { - delTillMark(cm,m); + delTillMark(cm, m); }; CodeMirror.keyMap["vim-prefix-y'"][m] = function(cm) { - yankTillMark(cm,m); + yankTillMark(cm, m); }; CodeMirror.keyMap["vim-prefix-r"][m] = function (cm) { var cur = cm.getCursor(); @@ -574,8 +571,8 @@ setupPrefixBindingForKey("Space"); CodeMirror.keyMap["vim-prefix-y"] = { - "Y": countTimes(function(cm) { - pushInBuffer("\n"+cm.getLine(cm.getCursor().line+yank)); yank++; + "Y": countTimes(function(cm, i, last) { + pushInBuffer("\n" + cm.getLine(cm.getCursor().line + i)); cm.setOption("keyMap", "vim"); }), "'": function(cm) {cm.setOption("keyMap", "vim-prefix-y'"); emptyBuffer();}, From da9fe672424ce56e7839e4edcd29bb09ae010aa9 Mon Sep 17 00:00:00 2001 From: Matt Sacks Date: Mon, 12 Nov 2012 09:23:11 -0800 Subject: [PATCH 0334/5780] Add text-objects ' and " Adds function findBeginningAndEnd to find identical symbols on the same line --- keymap/vim.js | 63 +++++++++++++++++++++++++++++++++++++++++++++++++-- 1 file changed, 61 insertions(+), 2 deletions(-) diff --git a/keymap/vim.js b/keymap/vim.js index b5309b56e1..4b012d1cdd 100644 --- a/keymap/vim.js +++ b/keymap/vim.js @@ -651,6 +651,63 @@ return {start: start, end: end}; } + // takes in a symbol and a cursor and tries to simulate text objects that have + // identical opening and closing symbols + // TODO support across multiple lines + function findBeginningAndEnd(cm, symb, inclusive) { + var cur = cm.getCursor(); + var line = cm.getLine(cur.line); + var chars = line.split(''); + var start = undefined; + var end = undefined; + var firstIndex = chars.indexOf(symb); + + // the decision tree is to always look backwards for the beginning first, + // but if the cursor is in front of the first instance of the symb, + // then move the cursor forward + if (cur.ch < firstIndex) { + cur.ch = firstIndex; + cm.setCursor(cur.line, firstIndex+1); + } + // otherwise if the cursor is currently on the closing symbol + else if (firstIndex < cur.ch && chars[cur.ch] == symb) { + end = cur.ch; // assign end to the current cursor + --cur.ch; // make sure to look backwards + } + + // if we're currently on the symbol, we've got a start + if (chars[cur.ch] == symb && end == null) + start = cur.ch + 1; // assign start to ahead of the cursor + else { + // go backwards to find the start + for (var i = cur.ch; i > -1 && start == null; i--) + if (chars[i] == symb) start = i + 1; + } + + // look forwards for the end symbol + if (start != null && end == null) { + for (var i = start, len = chars.length; i < len && end == null; i++) { + if (chars[i] == symb) end = i; + } + } + + // nothing found + // FIXME still enters insert mode + if (start == null || end == null) return { + start: cur, end: cur + }; + + // include the symbols + if (inclusive) { + --start; ++end; + } + + return { + start: {line: cur.line, ch: start}, + end: {line: cur.line, ch: end} + }; + } + // These are our motion commands to be used for navigation and selection with // certian other commands. All should return a cursor object. var motionList = ['B', 'E', 'J', 'K', 'H', 'L', 'W', 'Shift-W', "'^'", "'$'", "'%'", 'Esc']; @@ -805,7 +862,7 @@ // Create our text object functions. They work similar to motions but they // return a start cursor as well - var textObjectList = ['W', 'Shift-[', 'Shift-9', '[']; + var textObjectList = ['W', 'Shift-[', 'Shift-9', '[', "'", "Shift-'"]; var textObjects = { 'W': function(cm, inclusive) { var cur = cm.getCursor(); @@ -820,7 +877,9 @@ }, 'Shift-[': function(cm, inclusive) { return selectCompanionObject(cm, '}', inclusive); }, 'Shift-9': function(cm, inclusive) { return selectCompanionObject(cm, ')', inclusive); }, - '[': function(cm, inclusive) { return selectCompanionObject(cm, ']', inclusive); } + '[': function(cm, inclusive) { return selectCompanionObject(cm, ']', inclusive); }, + "'": function(cm, inclusive) { return findBeginningAndEnd(cm, "'", inclusive); }, + "Shift-'": function(cm, inclusive) { return findBeginningAndEnd(cm, '"', inclusive); } }; // One function to handle all operation upon text objects. Kinda funky but it works From cc684936604e9e11d4afe669f1ac1e7ce5ba6514 Mon Sep 17 00:00:00 2001 From: Marijn Haverbeke Date: Wed, 14 Nov 2012 08:11:20 +0100 Subject: [PATCH 0335/5780] Add Codepen to real-world uses --- doc/realworld.html | 1 + 1 file changed, 1 insertion(+) diff --git a/doc/realworld.html b/doc/realworld.html index 28ea162d16..3ed4637f57 100644 --- a/doc/realworld.html +++ b/doc/realworld.html @@ -27,6 +27,7 @@

    { } CodeMi
  • Cargo Collective (creative publishing platform)
  • Codebug (PHP Xdebug front-end)
  • CodeMirror2-GWT (Google Web Toolkit wrapper)
  • +
  • Codepen (gallery of animations)
  • Codev (collaborative IDE)
  • Collaborative CodeMirror demo (CodeMirror + operational transforms)
  • CKWNC (UML editor)
  • From 233d25c51deccca3115d5a1e2847b053eefa49eb Mon Sep 17 00:00:00 2001 From: Jan Keromnes Date: Wed, 14 Nov 2012 08:32:39 +0800 Subject: [PATCH 0336/5780] [vim] refactor motions --- keymap/vim.js | 88 ++++++++++++++++++++------------------------------- 1 file changed, 34 insertions(+), 54 deletions(-) diff --git a/keymap/vim.js b/keymap/vim.js index 4b012d1cdd..eaa4cf759c 100644 --- a/keymap/vim.js +++ b/keymap/vim.js @@ -399,9 +399,6 @@ // main num keymap // Add bindings that are influenced by number keys iterObj({ - "Left": "goColumnLeft", "Right": "goColumnRight", - "Down": "goLineDown", "Up": "goLineUp", "Backspace": "goCharLeft", - "Space": "goCharRight", "X": function(cm) {CodeMirror.commands.delCharAfter(cm);}, "P": function(cm) { var cur = cm.getCursor().line; @@ -708,67 +705,50 @@ }; } - // These are our motion commands to be used for navigation and selection with - // certian other commands. All should return a cursor object. - var motionList = ['B', 'E', 'J', 'K', 'H', 'L', 'W', 'Shift-W', "'^'", "'$'", "'%'", 'Esc']; - - motions = { - 'B': function(cm, times, yank) { return moveToWord(cm, word, -1, times, 'start', yank); }, - 'Shift-B': function(cm, times, yank) { return moveToWord(cm, bigWord, -1, times, 'start', yank); }, - 'E': function(cm, times, yank) { return moveToWord(cm, word, 1, times, 'end', yank); }, - 'Shift-E': function(cm, times, yank) { return moveToWord(cm, bigWord, 1, times, 'end', yank); }, - 'J': function(cm, times) { - var cur = cm.getCursor(); - return {line: cur.line+times, ch : cur.ch}; - }, - - 'K': function(cm, times) { - var cur = cm.getCursor(); - return {line: cur.line-times, ch: cur.ch}; - }, - - 'H': function(cm, times) { - var cur = cm.getCursor(); - return {line: cur.line, ch: cur.ch-times}; - }, + function offsetCursor(cm, line, ch) { + var cur = cm.getCursor(); return {line: cur.line + line, ch: cur.ch + ch}; + } - 'L': function(cm, times) { - var cur = cm.getCursor(); - return {line: cur.line, ch: cur.ch+times}; - }, - 'W': function(cm, times, yank) { return moveToWord(cm, word, 1, times, 'start', yank); }, - 'Shift-W': function(cm, times, yank) { return moveToWord(cm, bigWord, 1, times, 'start', yank); }, + // These are the motion commands we use for navigation and selection with + // certain other commands. All should return a cursor object. + var motions = { + "J": function(cm, times) { return offsetCursor(cm, times, 0); }, + "Down": function(cm, times) { return offsetCursor(cm, times, 0); }, + "K": function(cm, times) { return offsetCursor(cm, -times, 0); }, + "Up": function(cm, times) { return offsetCursor(cm, -times, 0); }, + "L": function(cm, times) { return offsetCursor(cm, 0, times); }, + "Right": function(cm, times) { return offsetCursor(cm, 0, times); }, + "Space": function(cm, times) { return offsetCursor(cm, 0, times); }, + "H": function(cm, times) { return offsetCursor(cm, 0, -times); }, + "Left": function(cm, times) { return offsetCursor(cm, 0, -times); }, + "Backspace": function(cm, times) { return offsetCursor(cm, 0, -times); }, + "B": function(cm, times, yank) { return moveToWord(cm, word, -1, times, 'start', yank); }, + "Shift-B": function(cm, times, yank) { return moveToWord(cm, bigWord, -1, times, 'start', yank); }, + "E": function(cm, times, yank) { return moveToWord(cm, word, 1, times, 'end', yank); }, + "Shift-E": function(cm, times, yank) { return moveToWord(cm, bigWord, 1, times, 'end', yank); }, + "W": function(cm, times, yank) { return moveToWord(cm, word, 1, times, 'start', yank); }, + "Shift-W": function(cm, times, yank) { return moveToWord(cm, bigWord, 1, times, 'start', yank); }, "'^'": function(cm, times) { - var cur = cm.getCursor(); - var line = cm.getLine(cur.line).split(''); - - // Empty line :o - if (line.length == 0) return cur; - - for (var index = 0; index < line.length; index++) { - if (line[index].match(/[^\s]/)) return {line: cur.line, ch: index}; + var cur = cm.getCursor(), line = cm.getLine(cur.line).split(''); + for (var i = 0; i < line.length; i++) { + if (line[i].match(/[^\s]/)) return {line: cur.line, ch: index}; } + return cur; }, "'$'": function(cm) { - var cur = cm.getCursor(); - var line = cm.getLine(cur.line); - return {line: cur.line, ch: line.length}; + var cur = cm.getCursor(), ch = cm.getLine(cur.line).length; + return {line: cur.line, ch: ch}; }, "'%'": function(cm) { return findMatchedSymbol(cm, cm.getCursor()); }, - "Esc" : function(cm) { - cm.setOption('vim'); - repeatCount = 0; - - return cm.getCursor(); - } + "Esc" : function(cm) { cm.setOption("keyMap", "vim"); repeatCount = 0; return cm.getCursor(); } }; // Map our movement actions each operator and non-operational movement - iterList(motionList, function(key, index, array) { + iterObj(motions, function(key, motion) { CodeMirror.keyMap['vim-prefix-d'][key] = function(cm) { // Get our selected range var start = cm.getCursor(); - var end = motions[key](cm, repeatCount ? repeatCount : 1, true); + var end = motion(cm, repeatCount ? repeatCount : 1, true); // Set swap var if range is of negative length if ((start.line > end.line) || (start.line == end.line && start.ch > end.ch)) var swap = true; @@ -784,7 +764,7 @@ CodeMirror.keyMap['vim-prefix-c'][key] = function(cm) { var start = cm.getCursor(); - var end = motions[key](cm, repeatCount ? repeatCount : 1, true); + var end = motion(cm, repeatCount ? repeatCount : 1, true); if ((start.line > end.line) || (start.line == end.line && start.ch > end.ch)) var swap = true; pushInBuffer(cm.getRange(swap ? end : start, swap ? start : end)); @@ -796,7 +776,7 @@ CodeMirror.keyMap['vim-prefix-y'][key] = function(cm) { var start = cm.getCursor(); - var end = motions[key](cm, repeatCount ? repeatCount : 1, true); + var end = motion(cm, repeatCount ? repeatCount : 1, true); if ((start.line > end.line) || (start.line == end.line && start.ch > end.ch)) var swap = true; pushInBuffer(cm.getRange(swap ? end : start, swap ? start : end)); @@ -806,7 +786,7 @@ }; CodeMirror.keyMap['vim'][key] = function(cm) { - var cur = motions[key](cm, repeatCount ? repeatCount : 1); + var cur = motion(cm, repeatCount ? repeatCount : 1); cm.setCursor(cur.line, cur.ch); repeatCount = 0; From 41faa75c991ca8e0dd4189ad9900d8c2aef7f293 Mon Sep 17 00:00:00 2001 From: Yunchi Luo Date: Wed, 14 Nov 2012 01:38:16 -0500 Subject: [PATCH 0337/5780] Fix off-by-one error for de. I accidentally introduced this fixing other off-by-one errors. Will seriously think about unit tests. --- keymap/vim.js | 4 ++++ 1 file changed, 4 insertions(+) diff --git a/keymap/vim.js b/keymap/vim.js index eaa4cf759c..b2069604c0 100644 --- a/keymap/vim.js +++ b/keymap/vim.js @@ -199,6 +199,10 @@ } } } + if (where == 'end' && yank) { + // Include the last character of the word for actions. + cur.ch++; + } return cur; } function joinLineNext(cm) { From dc5fcf12fba322f2d7323aa15ee93bd9a8676bc5 Mon Sep 17 00:00:00 2001 From: Marijn Haverbeke Date: Wed, 14 Nov 2012 10:30:00 +0100 Subject: [PATCH 0338/5780] Don't verify that typeof form.submit is function before replacing it That's apparently not a reliable thing to test for. --- lib/codemirror.js | 16 +++++++--------- 1 file changed, 7 insertions(+), 9 deletions(-) diff --git a/lib/codemirror.js b/lib/codemirror.js index c897148250..1e0233ab0f 100644 --- a/lib/codemirror.js +++ b/lib/codemirror.js @@ -2944,15 +2944,13 @@ window.CodeMirror = (function() { if (textarea.form) { // Deplorable hack to make the submit method do the right thing. on(textarea.form, "submit", save); - if (typeof textarea.form.submit == "function") { - var realSubmit = textarea.form.submit; - textarea.form.submit = function wrappedSubmit() { - save(); - textarea.form.submit = realSubmit; - textarea.form.submit(); - textarea.form.submit = wrappedSubmit; - }; - } + var realSubmit = textarea.form.submit; + textarea.form.submit = function wrappedSubmit() { + save(); + textarea.form.submit = realSubmit; + textarea.form.submit(); + textarea.form.submit = wrappedSubmit; + }; } textarea.style.display = "none"; From 679bd988d0bcad331d0196c3d39c6ab71938a00a Mon Sep 17 00:00:00 2001 From: Peter Kroon Date: Tue, 13 Nov 2012 19:20:08 +0100 Subject: [PATCH 0339/5780] CSS hack to get gutters to display properly in IE7 --- lib/codemirror.css | 3 +++ 1 file changed, 3 insertions(+) diff --git a/lib/codemirror.css b/lib/codemirror.css index 4359dc9472..d32f9c6026 100644 --- a/lib/codemirror.css +++ b/lib/codemirror.css @@ -151,6 +151,9 @@ div.CodeMirror span.CodeMirror-nonmatchingbracket {color: #f22;} .CodeMirror-gutter { height: 100%; display: inline-block; + /* Hack to make IE7 behave */ + *zoom:1; + *display:inline; } .CodeMirror-gutter-elt { position: absolute; From 37eb3540f69fecdd7548e5c3970cbbe5cc20ce22 Mon Sep 17 00:00:00 2001 From: Marijn Haverbeke Date: Wed, 14 Nov 2012 10:39:11 +0100 Subject: [PATCH 0340/5780] [htmlmixed mode] Fix typo bug --- mode/htmlmixed/htmlmixed.js | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/mode/htmlmixed/htmlmixed.js b/mode/htmlmixed/htmlmixed.js index 4652848296..9a15360dbd 100644 --- a/mode/htmlmixed/htmlmixed.js +++ b/mode/htmlmixed/htmlmixed.js @@ -22,7 +22,7 @@ CodeMirror.defineMode("htmlmixed", function(config) { var close = cur.search(pat), m; if (close > -1) stream.backUp(cur.length - close); else if (m = cur.match(/<\/?$/)) { - stream.backUp(cur[0].length); + stream.backUp(cur.length); if (!stream.match(pat, false)) stream.match(cur[0]); } return style; From 9ee9bc0acda22b8535724e8e0aff9331cb963852 Mon Sep 17 00:00:00 2001 From: Marijn Haverbeke Date: Thu, 15 Nov 2012 15:27:20 +0100 Subject: [PATCH 0341/5780] Add Jumpseller to real-world uses --- doc/realworld.html | 1 + 1 file changed, 1 insertion(+) diff --git a/doc/realworld.html b/doc/realworld.html index 3ed4637f57..a24fe2c3b3 100644 --- a/doc/realworld.html +++ b/doc/realworld.html @@ -49,6 +49,7 @@

    { } CodeMi
  • jQuery fundamentals (interactive tutorial)
  • jsbin.com (JS playground)
  • JSHint (JS linter)
  • +
  • Jumpseller (online store builder)
  • kl1p (paste service)
  • Light Table (experimental IDE)
  • Mergely (interactive diffing)
  • From 532c2fb64cc07cb226555296fcd1ebfe7a077fdb Mon Sep 17 00:00:00 2001 From: Marijn Haverbeke Date: Thu, 15 Nov 2012 16:35:37 +0100 Subject: [PATCH 0342/5780] Make the PHP mode depend on the HTML-mixed mode In order to not duplicate all its logic. Issue #751 --- mode/php/index.html | 1 + mode/php/php.js | 46 +++++++++++++-------------------------------- 2 files changed, 14 insertions(+), 33 deletions(-) diff --git a/mode/php/index.html b/mode/php/index.html index 9ac66ccb0f..1e4db2f639 100644 --- a/mode/php/index.html +++ b/mode/php/index.html @@ -6,6 +6,7 @@ + diff --git a/mode/php/php.js b/mode/php/php.js index 3e685bd575..c8ecb42e66 100644 --- a/mode/php/php.js +++ b/mode/php/php.js @@ -50,19 +50,16 @@ }; CodeMirror.defineMode("php", function(config, parserConfig) { - var htmlMode = CodeMirror.getMode(config, {name: "xml", htmlMode: true}); - var jsMode = CodeMirror.getMode(config, "javascript"); - var cssMode = CodeMirror.getMode(config, "css"); + var htmlMode = CodeMirror.getMode(config, "text/html"); var phpMode = CodeMirror.getMode(config, phpConfig); - function dispatch(stream, state) { // TODO open PHP inside text/css + function dispatch(stream, state) { var isPHP = state.curMode == phpMode; if (stream.sol() && state.pending != '"') state.pending = null; - if (state.curMode == htmlMode) { + if (!isPHP) { if (stream.match(/^<\?\w*/)) { state.curMode = phpMode; state.curState = state.php; - state.curClose = "?>"; return "meta"; } if (state.pending == '"') { @@ -80,51 +77,33 @@ if (style == "string" && /\"$/.test(cur) && !/\?>/.test(cur)) state.pending = '"'; else state.pending = {end: stream.pos, style: style}; stream.backUp(cur.length - openPHP); - } else if (style == "tag" && stream.current() == ">" && state.curState.context) { - if (/^script$/i.test(state.curState.context.tagName)) { - state.curMode = jsMode; - state.curState = jsMode.startState(htmlMode.indent(state.curState, "")); - state.curClose = /^<\/\s*script\s*>/i; - } - else if (/^style$/i.test(state.curState.context.tagName)) { - state.curMode = cssMode; - state.curState = cssMode.startState(htmlMode.indent(state.curState, "")); - state.curClose = /^<\/\s*style\s*>/i; - } } return style; - } else if ((!isPHP || state.php.tokenize == null) && - stream.match(state.curClose, isPHP)) { + } else if (isPHP && state.php.tokenize == null && stream.match("?>")) { state.curMode = htmlMode; state.curState = state.html; - state.curClose = null; - if (isPHP) return "meta"; - else return dispatch(stream, state); + return "meta"; } else { - return state.curMode.token(stream, state.curState); + return phpMode.token(stream, state.curState); } } return { startState: function() { - var html = htmlMode.startState(); + var html = CodeMirror.startState(htmlMode), php = CodeMirror.startState(phpMode); return {html: html, - php: phpMode.startState(), + php: php, curMode: parserConfig.startOpen ? phpMode : htmlMode, - curState: parserConfig.startOpen ? phpMode.startState() : html, - curClose: parserConfig.startOpen ? /^\?>/ : null, - mode: parserConfig.startOpen ? "php" : "html", + curState: parserConfig.startOpen ? php : html, pending: null}; }, copyState: function(state) { var html = state.html, htmlNew = CodeMirror.copyState(htmlMode, html), php = state.php, phpNew = CodeMirror.copyState(phpMode, php), cur; - if (state.curState == html) cur = htmlNew; - else if (state.curState == php) cur = phpNew; - else cur = CodeMirror.copyState(state.curMode, state.curState); + if (state.curMode == htmlMode) cur = htmlNew; + else cur = phpNew; return {html: htmlNew, php: phpNew, curMode: state.curMode, curState: cur, - curClose: state.curClose, mode: state.mode, pending: state.pending}; }, @@ -141,7 +120,8 @@ innerMode: function(state) { return {state: state.curState, mode: state.curMode}; } }; - }, "xml", "clike", "javascript", "css"); + }, "htmlmixed"); + CodeMirror.defineMIME("application/x-httpd-php", "php"); CodeMirror.defineMIME("application/x-httpd-php-open", {name: "php", startOpen: true}); CodeMirror.defineMIME("text/x-php", phpConfig); From e90e221d5dffe486cffd9b914a3302b942cb322e Mon Sep 17 00:00:00 2001 From: Marijn Haverbeke Date: Thu, 15 Nov 2012 16:44:47 +0100 Subject: [PATCH 0343/5780] Clear measurement cache when changing theme Closes #963 --- lib/codemirror.js | 19 ++++++++++++++----- 1 file changed, 14 insertions(+), 5 deletions(-) diff --git a/lib/codemirror.js b/lib/codemirror.js index 1e0233ab0f..d1ca7dc91c 100644 --- a/lib/codemirror.js +++ b/lib/codemirror.js @@ -166,7 +166,7 @@ window.CodeMirror = (function() { d.cachedCharWidth = d.cachedTextHeight = null; d.measureLineCache = []; - d.measureLineCache.pos = 0; + d.measureLineCachePos = 0; // Tracks when resetInput has punted to just putting a short // string instead of the (large) selection. @@ -836,7 +836,7 @@ window.CodeMirror = (function() { // Store result in the cache var memo = {text: line.text, width: display.scroller.clientWidth, markedSpans: line.markedSpans, measure: measure}; - if (cache.length == 16) cache[++cache.pos % 16] = memo; + if (cache.length == 16) cache[++display.measureLineCachePos % 16] = memo; else cache.push(memo); return measure; } @@ -872,6 +872,10 @@ window.CodeMirror = (function() { return data; } + function clearMeasureLineCache(cm) { + cm.display.measureLineCache.length = cm.display.measureLineCachePos = 0; + } + // Context is one of "line", "div" (display.lineDiv), "local"/null (editor), or "page" function intoCoordSystem(cm, lineObj, rect, context) { if (lineObj.widgets) for (var i = 0; i < lineObj.widgets.length; ++i) if (lineObj.widgets[i].above) { @@ -1202,7 +1206,7 @@ window.CodeMirror = (function() { on(window, "resize", function resizeHandler() { // Might be a text scaling operation, clear size caches. d.cachedCharWidth = d.cachedTextHeight = null; - d.measureLineCache.length = d.measureLineCache.pos = 0; + clearMeasureLineCache(cm); if (d.wrapper.parentNode) updateDisplay(cm, true); else off(window, "resize", resizeHandler); }); @@ -2602,7 +2606,7 @@ window.CodeMirror = (function() { operation: function(f){return operation(this, f)();}, refresh: function() { - this.display.measureLineCache.length = 0; + clearMeasureLineCache(this); updateDisplay(this, true, this.view.scrollTop); if (this.display.scrollbarV.scrollHeight > this.view.scrollTop) this.display.scrollbarV.scrollTop = this.view.scrollTop; @@ -2637,11 +2641,16 @@ window.CodeMirror = (function() { option("indentUnit", 2, loadMode, true); option("indentWithTabs", false); option("smartIndent", true); - option("tabSize", 4, function(cm) {loadMode(cm); updateDisplay(cm, true);}, true); + option("tabSize", 4, function(cm) { + loadMode(cm); + clearMeasureLineCache(cm); + updateDisplay(cm, true); + }, true); option("electricChars", true); option("autoClearEmptyLines", false); option("theme", "default", function(cm) { + clearMeasureLineCache(cm); themeChanged(cm); guttersChanged(cm); }, true); From 32abe85e698644231dba3badac86e886312461fd Mon Sep 17 00:00:00 2001 From: Marijn Haverbeke Date: Thu, 15 Nov 2012 16:50:02 +0100 Subject: [PATCH 0344/5780] [xml mode] Don't accept < characters not followed by tag name Closes #962 --- mode/xml/xml.js | 5 +++-- 1 file changed, 3 insertions(+), 2 deletions(-) diff --git a/mode/xml/xml.js b/mode/xml/xml.js index de3656c297..1f95ee2119 100644 --- a/mode/xml/xml.js +++ b/mode/xml/xml.js @@ -70,11 +70,12 @@ CodeMirror.defineMode("xml", function(config, parserConfig) { return "meta"; } else { - type = stream.eat("/") ? "closeTag" : "openTag"; - stream.eatSpace(); + var isClose = stream.eat("/"); tagName = ""; var c; while ((c = stream.eat(/[^\s\u00a0=<>\"\'\/?]/))) tagName += c; + if (!tagName) return "error"; + type = isClose ? "closeTag" : "openTag"; state.tokenize = inTag; return "tag"; } From 3c239efdbe29f1a7d332f16b0b52691548e080ec Mon Sep 17 00:00:00 2001 From: Marijn Haverbeke Date: Thu, 15 Nov 2012 17:44:47 +0100 Subject: [PATCH 0345/5780] Prevent creation of atomic range from always scrolling cursor into view By checking *first* whether it needs to be pushed, and *then* whether anything did actually change. Closes #975 --- lib/codemirror.js | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-) diff --git a/lib/codemirror.js b/lib/codemirror.js index d1ca7dc91c..7e7875ecdb 100644 --- a/lib/codemirror.js +++ b/lib/codemirror.js @@ -1960,7 +1960,6 @@ window.CodeMirror = (function() { cm.curOp.updateInput = true; var sel = cm.view.sel, doc = cm.view.doc; cm.view.goalColumn = null; - if (!checkAtomic && posEq(sel.from, from) && posEq(sel.to, to)) return; if (posLess(to, from)) {var tmp = to; to = from; from = tmp;} // Skip over atomic spans. @@ -1969,6 +1968,8 @@ window.CodeMirror = (function() { if (checkAtomic || !posEq(to, sel.to)) to = skipAtomic(cm, to, bias, checkAtomic != "push"); + if (posEq(sel.from, from) && posEq(sel.to, to)) return; + if (posEq(from, to)) sel.inverted = false; else if (posEq(from, sel.to)) sel.inverted = false; else if (posEq(to, sel.from)) sel.inverted = true; From 57dc36cb67275be9e26635e15269a825de8958f0 Mon Sep 17 00:00:00 2001 From: William Jamieson Date: Fri, 16 Nov 2012 14:57:30 +1100 Subject: [PATCH 0346/5780] Created a new twilight theme to match the Coda and Sublime twilight theme --- theme/twilight.css | 26 ++++++++++++++++++++++++++ 1 file changed, 26 insertions(+) create mode 100644 theme/twilight.css diff --git a/theme/twilight.css b/theme/twilight.css new file mode 100644 index 0000000000..9d14542c9c --- /dev/null +++ b/theme/twilight.css @@ -0,0 +1,26 @@ +.cm-s-twilight { background: #141414; color: #f7f7f7; } /**/ +.cm-s-twilight .CodeMirror-selected { background: #323232 !important; } /**/ + +.cm-s-twilight .CodeMirror-gutter { background: #222; border-right: 1px solid #aaa; } +.cm-s-twilight .CodeMirror-gutter-text { color: #aaa; } +.cm-s-twilight .CodeMirror-cursor { border-left: 1px solid white !important; } + +.cm-s-twilight .cm-keyword { color: #f9ee98; } /**/ +.cm-s-twilight .cm-atom { color: #FC0; } +.cm-s-twilight .cm-number { color: #ca7841; } /**/ +.cm-s-twilight .cm-def { color: #8DA6CE; } +.cm-s-twilight span.cm-variable-2, .cm-s-cobalt span.cm-tag { color: #607392; } /**/ +.cm-s-twilight span.cm-variable-3, .cm-s-cobalt span.cm-def { color: #607392; } /**/ +.cm-s-twilight .cm-operator { color: #cda869; } /**/ +.cm-s-twilight .cm-comment { color:#777; font-style:italic; font-weight:normal; } /**/ +.cm-s-twilight .cm-string { color:#8f9d6a; font-style:italic; } /**/ +.cm-s-twilight .cm-string-2 { color:#bd6b18 } /*?*/ +.cm-s-twilight .cm-meta { background-color:#141414; color:#f7f7f7; } /*?*/ +.cm-s-twilight .cm-error { border-bottom: 1px solid red; } +.cm-s-twilight .cm-builtin { color: #cda869; } /*?*/ +.cm-s-twilight .cm-tag { color: #997643; } /**/ +.cm-s-twilight .cm-attribute { color: #d6bb6d; } /*?*/ +.cm-s-twilight .cm-header { color: #FF6400; } +.cm-s-twilight .cm-hr { color: #AEAEAE; } +.cm-s-twilight .cm-link { color:#ad9361; font-style:italic; font-underline:none; } /**/ + From 85d69c85f82f08c29cb9de42ed223a21e8c90b15 Mon Sep 17 00:00:00 2001 From: Marijn Haverbeke Date: Fri, 16 Nov 2012 10:40:18 +0100 Subject: [PATCH 0347/5780] Integrate Twilight theme --- demo/theme.html | 2 ++ theme/twilight.css | 8 ++++---- 2 files changed, 6 insertions(+), 4 deletions(-) diff --git a/demo/theme.html b/demo/theme.html index ecf1e125a6..42a1b0cde7 100644 --- a/demo/theme.html +++ b/demo/theme.html @@ -19,6 +19,7 @@ + @@ -58,6 +59,7 @@

    CodeMirror: Theme demo

    + diff --git a/theme/twilight.css b/theme/twilight.css index 9d14542c9c..8e19db6ac8 100644 --- a/theme/twilight.css +++ b/theme/twilight.css @@ -1,16 +1,16 @@ .cm-s-twilight { background: #141414; color: #f7f7f7; } /**/ .cm-s-twilight .CodeMirror-selected { background: #323232 !important; } /**/ -.cm-s-twilight .CodeMirror-gutter { background: #222; border-right: 1px solid #aaa; } -.cm-s-twilight .CodeMirror-gutter-text { color: #aaa; } +.cm-s-twilight .CodeMirror-gutters { background: #222; border-right: 1px solid #aaa; } +.cm-s-twilight .CodeMirror-linenumber { color: #aaa; } .cm-s-twilight .CodeMirror-cursor { border-left: 1px solid white !important; } .cm-s-twilight .cm-keyword { color: #f9ee98; } /**/ .cm-s-twilight .cm-atom { color: #FC0; } .cm-s-twilight .cm-number { color: #ca7841; } /**/ .cm-s-twilight .cm-def { color: #8DA6CE; } -.cm-s-twilight span.cm-variable-2, .cm-s-cobalt span.cm-tag { color: #607392; } /**/ -.cm-s-twilight span.cm-variable-3, .cm-s-cobalt span.cm-def { color: #607392; } /**/ +.cm-s-twilight span.cm-variable-2, .cm-s-twilight span.cm-tag { color: #607392; } /**/ +.cm-s-twilight span.cm-variable-3, .cm-s-twilight span.cm-def { color: #607392; } /**/ .cm-s-twilight .cm-operator { color: #cda869; } /**/ .cm-s-twilight .cm-comment { color:#777; font-style:italic; font-weight:normal; } /**/ .cm-s-twilight .cm-string { color:#8f9d6a; font-style:italic; } /**/ From cecde4a28d00144da10f26b828cd611427356554 Mon Sep 17 00:00:00 2001 From: Kevin Sawicki Date: Thu, 15 Nov 2012 09:39:17 -0800 Subject: [PATCH 0348/5780] Use GitHub spelling consistently --- index.html | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/index.html b/index.html index 6f69d4f270..1511526461 100644 --- a/index.html +++ b/index.html @@ -54,7 +54,7 @@

    Supported modes:

  • Jinja2
  • LESS
  • Lua
  • -
  • Markdown (Github-flavour)
  • +
  • Markdown (GitHub-flavour)
  • MySQL
  • NTriples
  • OCaml
  • @@ -200,7 +200,7 @@

    Support and bug reports

    Though bug reports through e-mail are responded to, the preferred way to report bugs is to use - the Github + the GitHub issue tracker. Before reporting a bug, read these pointers. Also, the issue tracker is for bugs, not requests for help.

    From 55712411146d45133013c6ab2763eb9c1a73064b Mon Sep 17 00:00:00 2001 From: Kevin Sawicki Date: Thu, 15 Nov 2012 09:33:25 -0800 Subject: [PATCH 0349/5780] Specify markdown as dependency of gfm mode This ensures that markdown.js is loaded when gfm mode is loaded using CodeMirror.autoLoadMode --- mode/gfm/gfm.js | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/mode/gfm/gfm.js b/mode/gfm/gfm.js index ed06463673..6ff557f266 100644 --- a/mode/gfm/gfm.js +++ b/mode/gfm/gfm.js @@ -91,4 +91,4 @@ CodeMirror.defineMode("gfm", function(config) { fencedCodeBlocks: true }); return CodeMirror.overlayMode(CodeMirror.getMode(config, "gfmBase"), gfmOverlay); -}); +}, "markdown"); From 656b2714ded9554adecb164359d64562968b4c64 Mon Sep 17 00:00:00 2001 From: Marijn Haverbeke Date: Fri, 16 Nov 2012 11:48:10 +0100 Subject: [PATCH 0350/5780] 'Round' viewport start and end to the neares non-hidden line This prevents the offset computations from becoming confused when the border falls on a hidden line. Closes #978 --- lib/codemirror.js | 39 ++++++++++++++++++++++----------------- 1 file changed, 22 insertions(+), 17 deletions(-) diff --git a/lib/codemirror.js b/lib/codemirror.js index 7e7875ecdb..9f4b142e10 100644 --- a/lib/codemirror.js +++ b/lib/codemirror.js @@ -423,6 +423,10 @@ window.CodeMirror = (function() { var to = Math.min(doc.size, visible.to + cm.options.viewportMargin); if (display.showingFrom < from && from - display.showingFrom < 20) from = display.showingFrom; if (display.showingTo > to && display.showingTo - to < 20) to = Math.min(doc.size, display.showingTo); + if (sawCollapsedSpans) { + from = lineNo(visualLine(doc, getLine(doc, from))); + while (to < doc.size && lineIsHidden(getLine(doc, to))) ++to; + } // Create a range of theoretically intact lines, and punch holes // in that using the change info. @@ -1798,10 +1802,7 @@ window.CodeMirror = (function() { var nlines = to.line - from.line, firstLine = getLine(doc, from.line), lastLine = getLine(doc, to.line); var recomputeMaxLength = false, checkWidthStart = from.line; if (!cm.options.lineWrapping) { - for (var cur = firstLine, merged; merged = collapsedSpanAtStart(cur);) { - checkWidthStart = merged.find().from.line; - cur = getLine(doc, checkWidthStart); - } + checkWidthStart = lineNo(visualLine(doc, firstLine)); doc.iter(checkWidthStart, to.line + 1, function(line) { if (lineLength(doc, line) == view.maxLineLength) { recomputeMaxLength = true; @@ -3277,6 +3278,13 @@ window.CodeMirror = (function() { function collapsedSpanAtStart(line) { return collapsedSpanAt(line, -1); } function collapsedSpanAtEnd(line) { return collapsedSpanAt(line, line.text.length + 1); } + function visualLine(doc, line) { + var merged; + while (merged = collapsedSpanAtStart(line)) + line = getLine(doc, merged.find().from.line); + return line; + } + function lineIsHidden(line) { var sps = sawCollapsedSpans && line.markedSpans; if (sps) for (var sp, i = 0; i < sps.length; ++i) { @@ -3338,7 +3346,7 @@ window.CodeMirror = (function() { function makeLine(text, markedSpans, height) { var line = {text: text, height: height}; attachMarkedSpans(line, markedSpans); - if (markedSpans && collapsedSpanAtStart(line)) line.height = 0; + if (lineIsHidden(line)) line.height = 0; return line; } @@ -3348,8 +3356,7 @@ window.CodeMirror = (function() { if (line.order != null) line.order = null; detachMarkedSpans(line); attachMarkedSpans(line, markedSpans); - var hidden = collapsedSpanAtStart(line); - if (hidden) line.height = 0; + if (lineIsHidden(line)) line.height = 0; else if (!line.height) line.height = textHeight(cm.display); signalLater(cm, line, "change"); } @@ -3773,9 +3780,7 @@ window.CodeMirror = (function() { } function heightAtLine(cm, lineObj) { - var merged; - while (merged = collapsedSpanAtStart(lineObj)) - lineObj = getLine(cm.view.doc, merged.find().from.line); + lineObj = visualLine(cm.view.doc, lineObj); var h = 0, chunk = lineObj.parent; for (var i = 0; i < chunk.lines.length; ++i) { @@ -4174,13 +4179,13 @@ window.CodeMirror = (function() { return bidiRight(lst(order)); } - function lineStart(cm, lineNo) { - var merged, line; - while (merged = collapsedSpanAtStart(line = getLine(cm.view.doc, lineNo))) - lineNo = merged.find().from.line; - var order = getOrder(line); - var ch = !order ? 0 : order[0].level % 2 ? lineRight(line) : lineLeft(line); - return {line: lineNo, ch: ch}; + function lineStart(cm, lineN) { + var line = getLine(cm.view.doc, lineN); + var visual = visualLine(cm.view.doc, line); + if (visual != line) lineN = lineNo(visual); + var order = getOrder(visual); + var ch = !order ? 0 : order[0].level % 2 ? lineRight(visual) : lineLeft(visual); + return {line: lineN, ch: ch}; } function lineEnd(cm, lineNo) { var merged, line; From 1cda0595def66dd6592999f531eb212450d67053 Mon Sep 17 00:00:00 2001 From: Brandon Frohs Date: Mon, 12 Nov 2012 15:15:54 -0500 Subject: [PATCH 0351/5780] [gfm] Remove incorrect information Fenced code blocks *do* have syntax highlighting. --- mode/gfm/index.html | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/mode/gfm/index.html b/mode/gfm/index.html index 0c75864d4c..e4ea473571 100644 --- a/mode/gfm/index.html +++ b/mode/gfm/index.html @@ -33,7 +33,7 @@

    CodeMirror: GFM mode

    Underscores_are_allowed_between_words. -## Fenced code blocks (and syntax highlighting... someday) +## Fenced code blocks (and syntax highlighting) ```javascript for (var i = 0; i < items.length; i++) { From e210f54721d08128d7786c06764f465314c99c31 Mon Sep 17 00:00:00 2001 From: Marijn Haverbeke Date: Fri, 16 Nov 2012 12:02:13 +0100 Subject: [PATCH 0352/5780] Keep wheel scroll target nodes in DOM during the next update Issue #728 --- lib/codemirror.js | 35 +++++++++++++++++++++++------------ 1 file changed, 23 insertions(+), 12 deletions(-) diff --git a/lib/codemirror.js b/lib/codemirror.js index 9f4b142e10..cd09b90db4 100644 --- a/lib/codemirror.js +++ b/lib/codemirror.js @@ -457,7 +457,7 @@ window.CodeMirror = (function() { display.showingFrom = from; display.showingTo = to; startWorker(cm, 100); - for (var node = display.lineDiv.firstChild; node; node = node.nextSibling) { + for (var node = display.lineDiv.firstChild; node; node = node.nextSibling) if (node.lineObj) { var box = node.getBoundingClientRect(); var height = box.bottom - box.top, diff = node.lineObj.height - height; if (height < 2) height = textHeight(display); @@ -509,8 +509,21 @@ window.CodeMirror = (function() { var display = cm.display, lineNumbers = cm.options.lineNumbers; // IE does bad things to nodes when .innerHTML = "" is used on a parent // we still need widgets and markers intact to add back to the new content later - if (!intact.length && !ie) removeChildren(display.lineDiv); + if (!intact.length && !ie && (!safari || !cm.display.currentWheelTarget)) + removeChildren(display.lineDiv); var container = display.lineDiv, cur = container.firstChild; + + function rm(node) { + var next = node.nextSibling; + if (safari && mac && cm.display.currentWheelTarget == node) { + node.style.display = "none"; + node.lineObj = null; + } else { + container.removeChild(node); + } + return next; + } + var nextIntact = intact.shift(), lineNo = from; cm.view.doc.iter(from, to, function(line) { if (nextIntact && nextIntact.to == lineNo) nextIntact = intact.shift(); @@ -519,11 +532,7 @@ window.CodeMirror = (function() { } else if (nextIntact && nextIntact.from <= lineNo && nextIntact.to > lineNo) { // This line is intact. Skip to the actual node. Update its // line number if needed. - while (cur.lineObj != line) { - var tmp = cur.nextSibling; - container.removeChild(cur); - cur = tmp; - } + while (cur.lineObj != line) cur = rm(cur); if (lineNumbers && updateNumbersFrom <= lineNo && cur.lineNumber) setTextContent(cur.lineNumber, lineNumberFor(cm.options, lineNo)); cur = cur.nextSibling; @@ -535,11 +544,7 @@ window.CodeMirror = (function() { } ++lineNo; }); - while (cur) { - var tmp = cur.nextSibling; - container.removeChild(cur); - cur = tmp; - } + while (cur) cur = rm(cur); } function buildLineElement(cm, line, lineNo, dims) { @@ -1542,6 +1547,12 @@ window.CodeMirror = (function() { var top = cm.view.scrollTop, bot = top + cm.display.wrapper.clientHeight; if (pixels < 0) top = Math.max(0, top + pixels - 50); else bot = Math.min(cm.view.doc.height, bot + pixels + 50); + if (mac && safari) for (var cur = e.target; cur != scroll; cur = cur.parentNode) { + if (cur.lineObj) { + cm.display.currentWheelTarget = cur; + break; + } + } updateDisplay(cm, [], {top: top, bottom: bot}); } if (wheelSamples < 20) { From 41b4d53327086e51a4f4a561c4b0c8e7490ab10d Mon Sep 17 00:00:00 2001 From: Marijn Haverbeke Date: Sat, 17 Nov 2012 10:47:45 +0100 Subject: [PATCH 0353/5780] Apply hack e210f54721d for all webkit browsers on Mac Issue #728 --- lib/codemirror.js | 6 +++--- 1 file changed, 3 insertions(+), 3 deletions(-) diff --git a/lib/codemirror.js b/lib/codemirror.js index cd09b90db4..b5985da3c3 100644 --- a/lib/codemirror.js +++ b/lib/codemirror.js @@ -509,13 +509,13 @@ window.CodeMirror = (function() { var display = cm.display, lineNumbers = cm.options.lineNumbers; // IE does bad things to nodes when .innerHTML = "" is used on a parent // we still need widgets and markers intact to add back to the new content later - if (!intact.length && !ie && (!safari || !cm.display.currentWheelTarget)) + if (!intact.length && !ie && (!webkit || !cm.display.currentWheelTarget)) removeChildren(display.lineDiv); var container = display.lineDiv, cur = container.firstChild; function rm(node) { var next = node.nextSibling; - if (safari && mac && cm.display.currentWheelTarget == node) { + if (webkit && mac && cm.display.currentWheelTarget == node) { node.style.display = "none"; node.lineObj = null; } else { @@ -1547,7 +1547,7 @@ window.CodeMirror = (function() { var top = cm.view.scrollTop, bot = top + cm.display.wrapper.clientHeight; if (pixels < 0) top = Math.max(0, top + pixels - 50); else bot = Math.min(cm.view.doc.height, bot + pixels + 50); - if (mac && safari) for (var cur = e.target; cur != scroll; cur = cur.parentNode) { + if (mac && webkit) for (var cur = e.target; cur != scroll; cur = cur.parentNode) { if (cur.lineObj) { cm.display.currentWheelTarget = cur; break; From e20da488f5e50d56ca455e26b3c10984aa40ece4 Mon Sep 17 00:00:00 2001 From: Marijn Haverbeke Date: Mon, 19 Nov 2012 10:02:43 +0100 Subject: [PATCH 0354/5780] Remove apparently useless preventDefault in mousemove handler --- lib/codemirror.js | 1 - 1 file changed, 1 deletion(-) diff --git a/lib/codemirror.js b/lib/codemirror.js index b5985da3c3..9adfef9c5f 100644 --- a/lib/codemirror.js +++ b/lib/codemirror.js @@ -1410,7 +1410,6 @@ window.CodeMirror = (function() { } var move = operation(cm, function(e) { - e_preventDefault(e); if (!ie && !e_button(e)) done(e); else extend(e); }); From 120f8fc9db6229dc12f1a7c960a99e6c9ea03afe Mon Sep 17 00:00:00 2001 From: Chandra Sekhar Pydi Date: Sat, 17 Nov 2012 10:35:40 +0530 Subject: [PATCH 0355/5780] fix extra indentation caused by ';' in 'top'/'}' contexts. ';' after end of class definition is changing context to 'statement' from 'top' (or '}'). This results in subsequent lines to be indented with 2-spaces (extra). Fix is not to pushContext when curPunc is ';' and context.type is '} or 'top'. --- mode/clike/clike.js | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/mode/clike/clike.js b/mode/clike/clike.js index 715c522c0e..2bdd39a2ca 100644 --- a/mode/clike/clike.js +++ b/mode/clike/clike.js @@ -133,7 +133,7 @@ CodeMirror.defineMode("clike", function(config, parserConfig) { while (ctx.type == "statement") ctx = popContext(state); } else if (curPunc == ctx.type) popContext(state); - else if (ctx.type == "}" || ctx.type == "top" || (ctx.type == "statement" && curPunc == "newstatement")) + else if (((ctx.type == "}" || ctx.type == "top") && curPunc != ';') || (ctx.type == "statement" && curPunc == "newstatement")) pushContext(state, stream.column(), "statement"); state.startOfLine = false; return style; From eb29e4d0ca634d1bd15da4f3ca57d73e0fb76985 Mon Sep 17 00:00:00 2001 From: Marijn Haverbeke Date: Mon, 19 Nov 2012 11:09:47 +0100 Subject: [PATCH 0356/5780] Add continuecomment.js utility add-on --- doc/manual.html | 7 ++++++- lib/util/continuecomment.js | 36 ++++++++++++++++++++++++++++++++++++ mode/javascript/index.html | 4 +++- 3 files changed, 45 insertions(+), 2 deletions(-) create mode 100644 lib/util/continuecomment.js diff --git a/doc/manual.html b/doc/manual.html index 91c1bb6097..bc62d605b3 100644 --- a/doc/manual.html +++ b/doc/manual.html @@ -433,7 +433,7 @@

    Keymaps

    also contains the "default" keymap holding the default bindings.

    -

    The values of properties in keymaps can be either functions of +

    The values of properties in keymaps can be either functions of a single argument (the CodeMirror instance), strings, or false. Such strings refer to properties of the CodeMirror.commands object, which defines a number of @@ -1163,6 +1163,11 @@

    Add-ons

    which will ensure the given mode is loaded and cause the given editor instance to refresh its mode when the loading succeeded. See the demo.

    +
    continuecomment.js
    +
    Adds a command + called newlineAndIndentContinueComment that you can + bind Enter to in order to have the editor prefix + new lines inside C-like block comments with an asterisk.

    Writing CodeMirror Modes

    diff --git a/lib/util/continuecomment.js b/lib/util/continuecomment.js new file mode 100644 index 0000000000..5fbe0786c6 --- /dev/null +++ b/lib/util/continuecomment.js @@ -0,0 +1,36 @@ +(function() { + var modes = ["clike", "css", "javascript"]; + for (var i = 0; i < modes.length; ++i) + CodeMirror.extendMode(modes[i], {blockCommentStart: "/*", + blockCommentEnd: "*/", + blockCommentContinue: " * "}); + + CodeMirror.commands.newlineAndIndentContinueComment = function(cm) { + var pos = cm.getCursor(), token = cm.getTokenAt(pos); + var mode = CodeMirror.innerMode(cm.getMode(), token.state).mode; + var space; + + if (token.className == "comment" && mode.blockCommentStart) { + var end = token.string.indexOf(mode.blockCommentEnd); + var full = cm.getRange({line: pos.line, ch: 0}, {line: pos.line, ch: token.end}), found; + if (end != -1 && end == token.string.length - mode.blockCommentEnd.length) { + // Comment ended, don't continue it + } else if (token.string.indexOf(mode.blockCommentStart) == 0) { + space = full.slice(0, token.start); + if (!/^\s*$/.test(space)) { + space = ""; + for (var i = 0; i < token.start; ++i) space += " "; + } + } else if ((found = full.indexOf(mode.blockCommentContinue)) != -1 && + found + mode.blockCommentContinue.length > token.start && + /^\s*$/.test(full.slice(0, found))) { + space = full.slice(0, found); + } + } + + if (space != null) + cm.replaceSelection("\n" + space + mode.blockCommentContinue, "end"); + else + cm.execCommand("newlineAndIndent"); + }; +})(); diff --git a/mode/javascript/index.html b/mode/javascript/index.html index c24413e565..d81413c0a6 100644 --- a/mode/javascript/index.html +++ b/mode/javascript/index.html @@ -6,6 +6,7 @@ + @@ -66,7 +67,8 @@

    CodeMirror: JavaScript mode

    From 51095196a44ee7e82f5be4958b06a73a80a16111 Mon Sep 17 00:00:00 2001 From: Marijn Haverbeke Date: Mon, 19 Nov 2012 11:24:04 +0100 Subject: [PATCH 0357/5780] Use a 'type' property instead of 'className' in getTokenAt's return value className is misleading -- these are not the actual CSS classes. --- doc/manual.html | 5 +++-- lib/codemirror.js | 32 +++++++++++++++----------------- lib/util/closetag.js | 27 +++------------------------ lib/util/continuecomment.js | 2 +- lib/util/foldcode.js | 4 ++-- lib/util/javascript-hint.js | 22 +++++++++++----------- lib/util/matchbrackets.js | 4 ++-- lib/util/pig-hint.js | 4 ++-- lib/util/simple-hint.js | 2 +- 9 files changed, 40 insertions(+), 62 deletions(-) diff --git a/doc/manual.html b/doc/manual.html index bc62d605b3..3ad088d1a5 100644 --- a/doc/manual.html +++ b/doc/manual.html @@ -736,8 +736,9 @@

    Programming API

    start
    The character (on the given line) at which the token starts.
    end
    The character at which the token ends.
    string
    The token's string.
    -
    className
    The class the mode assigned - to the token. (Can be null when no class was assigned.)
    +
    type
    The token type the mode assigned + to the token, such as "keyword" + or "comment" (may also be null).
    state
    The mode's state at the end of this token.
    diff --git a/lib/codemirror.js b/lib/codemirror.js index 9adfef9c5f..da2cd3e173 100644 --- a/lib/codemirror.js +++ b/lib/codemirror.js @@ -2296,10 +2296,24 @@ window.CodeMirror = (function() { hist.undone = histData.undone; }, + // Fetch the parser token for a given character. Useful for hacks + // that want to inspect the mode state (say, for completion). getTokenAt: function(pos) { var doc = this.view.doc; pos = clipPos(doc, pos); - return getTokenAt(this, getLine(doc, pos.line), getStateBefore(this, pos.line), pos.ch); + var state = getStateBefore(this, pos.line), mode = this.view.mode; + var line = getLine(doc, pos.line); + var stream = new StringStream(line.text, this.options.tabSize); + while (stream.pos < pos.ch && !stream.eol()) { + stream.start = stream.pos; + var style = mode.token(stream, state); + } + return {start: stream.start, + end: stream.pos, + string: stream.current(), + className: style || null, // Deprecated, use 'type' instead + type: style || null, + state: state}; }, getStateAfter: function(line) { @@ -3418,22 +3432,6 @@ window.CodeMirror = (function() { } } - // Fetch the parser token for a given character. Useful for hacks - // that want to inspect the mode state (say, for completion). - function getTokenAt(cm, line, state, ch) { - var mode = cm.view.mode; - var txt = line.text, stream = new StringStream(txt, cm.options.tabSize); - while (stream.pos < ch && !stream.eol()) { - stream.start = stream.pos; - var style = mode.token(stream, state); - } - return {start: stream.start, - end: stream.pos, - string: stream.current(), - className: style || null, - state: state}; - } - var styleToClassCache = {}; function styleToClass(style) { if (!style) return null; diff --git a/lib/util/closetag.js b/lib/util/closetag.js index 5096678473..2c571dbd6a 100644 --- a/lib/util/closetag.js +++ b/lib/util/closetag.js @@ -43,27 +43,6 @@ if (!cm.getOption('closeTagEnabled')) { throw CodeMirror.Pass; } - - /* - * Relevant structure of token: - * - * htmlmixed - * className - * state - * htmlState - * type - * tagName - * context - * tagName - * mode - * - * xml - * className - * state - * tagName - * type - */ - var pos = cm.getCursor(); var tok = cm.getTokenAt(pos); var state = innerState(cm, tok.state); @@ -73,7 +52,7 @@ if (ch == '>') { var type = state.type; - if (tok.className == 'tag' && type == 'closeTag') { + if (tok.type == 'tag' && type == 'closeTag') { throw CodeMirror.Pass; // Don't process the '>' at the end of an end-tag. } @@ -86,7 +65,7 @@ if (!state) throw CodeMirror.Pass; var type = state.type; - if (tok.className == 'tag' && type != 'selfcloseTag') { + if (tok.type == 'tag' && type != 'selfcloseTag') { var tagName = state.tagName; if (tagName.length > 0 && shouldClose(cm, vd, tagName)) { insertEndTag(cm, indent, pos, tagName); @@ -99,7 +78,7 @@ cm.replaceSelection(""); } else if (ch == '/') { - if (tok.className == 'tag' && tok.string == '<') { + if (tok.type == 'tag' && tok.string == '<') { var ctx = state.context, tagName = ctx ? ctx.tagName : ''; if (tagName.length > 0) { completeEndTag(cm, pos, tagName); diff --git a/lib/util/continuecomment.js b/lib/util/continuecomment.js index 5fbe0786c6..dac83a81f6 100644 --- a/lib/util/continuecomment.js +++ b/lib/util/continuecomment.js @@ -10,7 +10,7 @@ var mode = CodeMirror.innerMode(cm.getMode(), token.state).mode; var space; - if (token.className == "comment" && mode.blockCommentStart) { + if (token.type == "comment" && mode.blockCommentStart) { var end = token.string.indexOf(mode.blockCommentEnd); var full = cm.getRange({line: pos.line, ch: 0}, {line: pos.line, ch: token.end}), found; if (end != -1 && end == token.string.length - mode.blockCommentEnd.length) { diff --git a/lib/util/foldcode.js b/lib/util/foldcode.js index c5273ad3cd..407bac2a5e 100644 --- a/lib/util/foldcode.js +++ b/lib/util/foldcode.js @@ -111,7 +111,7 @@ CodeMirror.braceRangeFinder = function(cm, start) { for (;;) { var found = lineText.lastIndexOf("{", at); if (found < start.ch) break; - tokenType = cm.getTokenAt({line: line, ch: found}).className; + tokenType = cm.getTokenAt({line: line, ch: found}).type; if (!/^(comment|string)/.test(tokenType)) { startChar = found; break; } at = found - 1; } @@ -125,7 +125,7 @@ CodeMirror.braceRangeFinder = function(cm, start) { if (nextClose < 0) nextClose = text.length; pos = Math.min(nextOpen, nextClose); if (pos == text.length) break; - if (cm.getTokenAt({line: i, ch: pos + 1}).className == tokenType) { + if (cm.getTokenAt({line: i, ch: pos + 1}).type == tokenType) { if (pos == nextOpen) ++count; else if (!--count) { end = i; endCh = pos; break outer; } } diff --git a/lib/util/javascript-hint.js b/lib/util/javascript-hint.js index ff15adb3ac..22edf5f3fb 100644 --- a/lib/util/javascript-hint.js +++ b/lib/util/javascript-hint.js @@ -22,10 +22,10 @@ // If it's not a 'word-style' token, ignore the token. if (!/^[\w$_]*$/.test(token.string)) { token = tprop = {start: cur.ch, end: cur.ch, string: "", state: token.state, - className: token.string == "." ? "property" : null}; + type: token.string == "." ? "property" : null}; } // If it is a property, find out what it is a property of. - while (tprop.className == "property") { + while (tprop.type == "property") { tprop = getToken(editor, {line: cur.line, ch: tprop.start}); if (tprop.string != ".") return; tprop = getToken(editor, {line: cur.line, ch: tprop.start}); @@ -40,9 +40,9 @@ } } while (level > 0); tprop = getToken(editor, {line: cur.line, ch: tprop.start}); - if (tprop.className == 'variable') - tprop.className = 'function'; - else return; // no clue + if (tprop.type == 'variable') + tprop.type = 'function'; + else return; // no clue } if (!context) var context = []; context.push(tprop); @@ -65,10 +65,10 @@ if (cur.ch == token.start + 1 && token.string.charAt(0) == '.') { token.end = token.start; token.string = '.'; - token.className = "property"; + token.type = "property"; } else if (/^\.[\w$_]*$/.test(token.string)) { - token.className = "property"; + token.type = "property"; token.start++; token.string = token.string.replace(/\./, ''); } @@ -105,13 +105,13 @@ // If this is a property, see if it belongs to some object we can // find in the current environment. var obj = context.pop(), base; - if (obj.className == "variable") + if (obj.type == "variable") base = window[obj.string]; - else if (obj.className == "string") + else if (obj.type == "string") base = ""; - else if (obj.className == "atom") + else if (obj.type == "atom") base = 1; - else if (obj.className == "function") { + else if (obj.type == "function") { if (window.jQuery != null && (obj.string == '$' || obj.string == 'jQuery') && (typeof window.jQuery == 'function')) base = window.jQuery(); diff --git a/lib/util/matchbrackets.js b/lib/util/matchbrackets.js index c77d9676bf..2df2fbb35f 100644 --- a/lib/util/matchbrackets.js +++ b/lib/util/matchbrackets.js @@ -5,7 +5,7 @@ var match = (pos >= 0 && matching[line.text.charAt(pos)]) || matching[line.text.charAt(++pos)]; if (!match) return null; var forward = match.charAt(1) == ">", d = forward ? 1 : -1; - var style = cm.getTokenAt({line: cur.line, ch: pos + 1}).className; + var style = cm.getTokenAt({line: cur.line, ch: pos + 1}).type; var stack = [line.text.charAt(pos)], re = /[(){}[\]]/; function scan(line, lineNo, start) { @@ -14,7 +14,7 @@ if (start != null) pos = start + d; for (; pos != end; pos += d) { var ch = line.text.charAt(pos); - if (re.test(ch) && cm.getTokenAt({line: lineNo, ch: pos + 1}).className == style) { + if (re.test(ch) && cm.getTokenAt({line: lineNo, ch: pos + 1}).type == style) { var match = matching[ch]; if (match.charAt(1) == ">" == forward) stack.push(ch); else if (stack.pop() != match.charAt(0)) return {pos: pos, match: false}; diff --git a/lib/util/pig-hint.js b/lib/util/pig-hint.js index f5c2049b5a..149b468158 100644 --- a/lib/util/pig-hint.js +++ b/lib/util/pig-hint.js @@ -103,9 +103,9 @@ // find in the current environment. var obj = context.pop(), base; - if (obj.className == "pig-word") + if (obj.type == "variable") base = obj.string; - else if(obj.className == "pig-type") + else if(obj.type == "variable-3") base = ":" + obj.string; while (base != null && context.length) diff --git a/lib/util/simple-hint.js b/lib/util/simple-hint.js index d6f588e8bb..f08f311e2f 100644 --- a/lib/util/simple-hint.js +++ b/lib/util/simple-hint.js @@ -14,7 +14,7 @@ // Don't show completions if token has changed and the option is set. if (options.closeOnTokenChange && previousToken != null && - (tempToken.start != previousToken.start || tempToken.className != previousToken.className)) { + (tempToken.start != previousToken.start || tempToken.type != previousToken.type)) { return; } From 3a24dd087d5558d8780411d7c2aecd42d7eac0a2 Mon Sep 17 00:00:00 2001 From: Marijn Haverbeke Date: Mon, 19 Nov 2012 11:34:51 +0100 Subject: [PATCH 0358/5780] Work around overlapping boundingClientRects in IE7 --- lib/codemirror.js | 14 +++++++++++--- 1 file changed, 11 insertions(+), 3 deletions(-) diff --git a/lib/codemirror.js b/lib/codemirror.js index da2cd3e173..312b799b2a 100644 --- a/lib/codemirror.js +++ b/lib/codemirror.js @@ -457,9 +457,17 @@ window.CodeMirror = (function() { display.showingFrom = from; display.showingTo = to; startWorker(cm, 100); - for (var node = display.lineDiv.firstChild; node; node = node.nextSibling) if (node.lineObj) { - var box = node.getBoundingClientRect(); - var height = box.bottom - box.top, diff = node.lineObj.height - height; + var prevBottom = display.lineDiv.offsetTop; + for (var node = display.lineDiv.firstChild, height; node; node = node.nextSibling) if (node.lineObj) { + if (ie_lt8) { + var bot = node.offsetTop + node.offsetHeight; + height = bot - prevBottom; + prevBottom = bot; + } else { + var box = node.getBoundingClientRect(); + height = box.bottom - box.top; + } + var diff = node.lineObj.height - height; if (height < 2) height = textHeight(display); if (diff > .001 || diff < -.001) updateLineHeight(node.lineObj, height); From 6f6daeceb11d8a4ac661453f01809b8fc555c127 Mon Sep 17 00:00:00 2001 From: Marijn Haverbeke Date: Mon, 19 Nov 2012 12:08:51 +0100 Subject: [PATCH 0359/5780] Fail some tests on IE7 again I give up. --- lib/codemirror.js | 14 ++++++++------ test/test.js | 4 ++-- 2 files changed, 10 insertions(+), 8 deletions(-) diff --git a/lib/codemirror.js b/lib/codemirror.js index 312b799b2a..303822a9ee 100644 --- a/lib/codemirror.js +++ b/lib/codemirror.js @@ -2988,12 +2988,14 @@ window.CodeMirror = (function() { // Deplorable hack to make the submit method do the right thing. on(textarea.form, "submit", save); var realSubmit = textarea.form.submit; - textarea.form.submit = function wrappedSubmit() { - save(); - textarea.form.submit = realSubmit; - textarea.form.submit(); - textarea.form.submit = wrappedSubmit; - }; + try { + textarea.form.submit = function wrappedSubmit() { + save(); + textarea.form.submit = realSubmit; + textarea.form.submit(); + textarea.form.submit = wrappedSubmit; + }; + } catch(e) {} } textarea.style.display = "none"; diff --git a/test/test.js b/test/test.js index aeb5c831d0..35f1507596 100644 --- a/test/test.js +++ b/test/test.js @@ -678,7 +678,7 @@ testCM("measureEndOfLine", function(cm) { is(endPos.left > w - 20, "not at right"); endPos = cm.charCoords({line: 0, ch: 18}); eqPos(cm.coordsChar({left: endPos.left, top: endPos.top + 5}), {line: 0, ch: 18}); -}, {mode: "text/html", value: "", lineWrapping: true}); +}, {mode: "text/html", value: "", lineWrapping: true}, ie_lt8); testCM("scrollVerticallyAndHorizontally", function(cm) { cm.setSize(100, 100); @@ -703,7 +703,7 @@ testCM("moveVstuck", function(cm) { cm.setCursor({line: 0, ch: val.length - 1}); cm.moveV(-1, "line"); eqPos(cm.getCursor(), {line: 0, ch: 26}); -}, {lineWrapping: true}); +}, {lineWrapping: true}, ie_lt8); testCM("clickTab", function(cm) { var p0 = cm.charCoords({line: 0, ch: 0}); From a78b4f2e4a6a483761b246cc8d2dd100c72dcd9f Mon Sep 17 00:00:00 2001 From: Marijn Haverbeke Date: Tue, 20 Nov 2012 09:44:44 +0100 Subject: [PATCH 0360/5780] Try to pre-align gutter and widgets on horizontal mouse scroll (And fix a really poor function name.) Issue #986 --- lib/codemirror.js | 13 ++++++++++--- 1 file changed, 10 insertions(+), 3 deletions(-) diff --git a/lib/codemirror.js b/lib/codemirror.js index 303822a9ee..99e5a44dc5 100644 --- a/lib/codemirror.js +++ b/lib/codemirror.js @@ -329,9 +329,12 @@ window.CodeMirror = (function() { // LINE NUMBERS - function alignVertically(display) { + function alignHorizontally(display, targetScrollLeft) { if (!display.alignWidgets && !display.gutters.firstChild) return; - var comp = compensateForHScroll(display), gutterW = display.gutters.offsetWidth, l = comp + "px"; + var comp = compensateForHScroll(display); + if (targetScrollLeft != null) + comp += display.scroller.scrollLeft - targetScrollLeft; + var gutterW = display.gutters.offsetWidth, l = comp + "px"; for (var n = display.lineDiv.firstChild; n; n = n.nextSibling) if (n.alignable) { for (var i = 0, a = n.alignable; i < a.length; ++i) a[i].style.left = l; } @@ -1518,7 +1521,7 @@ window.CodeMirror = (function() { cm.view.scrollLeft = val; if (cm.display.scroller.scrollLeft != val) cm.display.scroller.scrollLeft = val; if (cm.display.scrollbarH.scrollLeft != val) cm.display.scrollbarH.scrollLeft = val; - alignVertically(cm.display); + alignHorizontally(cm.display); } // Since the delta values reported on mouse wheel events are @@ -1562,6 +1565,10 @@ window.CodeMirror = (function() { } updateDisplay(cm, [], {top: top, bottom: bot}); } + if (dx && wheelPixelsPerUnit != null) { + var target = Math.max(0, Math.min(scroll.scrollWidth - scroll.clientWidth, dx * wheelPixelsPerUnit + scroll.scrollLeft)); + if (target != scroll.scrollLeft) alignHorizontally(cm.display, target); + } if (wheelSamples < 20) { if (wheelStartX == null) { wheelStartX = scroll.scrollLeft; wheelStartY = scroll.scrollTop; From 9b14ecb7eaa8ef142d29bf8c44f9c24a0484076c Mon Sep 17 00:00:00 2001 From: Marijn Haverbeke Date: Tue, 20 Nov 2012 15:43:49 +0100 Subject: [PATCH 0361/5780] Make compression helper output help info when not given any files --- bin/compress | 2 ++ 1 file changed, 2 insertions(+) diff --git a/bin/compress b/bin/compress index 925eee2c37..de86ca1f69 100755 --- a/bin/compress +++ b/bin/compress @@ -63,6 +63,8 @@ function walk(dir) { walk("lib/"); walk("mode/"); +if (!blob) help(false); + if (files.length) { console.log("Some speficied files were not found: " + files.map(function(a){return a.name;}).join(", ")); From 73730b3a5e4a58bcd0ba3c461e87f948f96a3ead Mon Sep 17 00:00:00 2001 From: Marijn Haverbeke Date: Tue, 20 Nov 2012 16:14:38 +0100 Subject: [PATCH 0362/5780] Mark release 3.0rc1 --- doc/compress.html | 3 +++ index.html | 46 ++++++++++++++++++++++++++++++++++++---------- lib/codemirror.js | 2 +- package.json | 2 +- 4 files changed, 41 insertions(+), 12 deletions(-) diff --git a/doc/compress.html b/doc/compress.html index cdb339deac..df0d8a7acb 100644 --- a/doc/compress.html +++ b/doc/compress.html @@ -30,8 +30,11 @@

    { } CodeMi

    Version: + + + +

    MIME types defined: message/http.

    + + From 4f7df9b5eb071546563263510dedc8659ed071a7 Mon Sep 17 00:00:00 2001 From: Marijn Haverbeke Date: Wed, 28 Nov 2012 10:58:28 +0100 Subject: [PATCH 0389/5780] Add 'negative' and 'positive' token styles --- lib/codemirror.css | 2 ++ mode/diff/diff.js | 4 ++-- 2 files changed, 4 insertions(+), 2 deletions(-) diff --git a/lib/codemirror.css b/lib/codemirror.css index d32f9c6026..bf995f4866 100644 --- a/lib/codemirror.css +++ b/lib/codemirror.css @@ -86,6 +86,8 @@ .cm-s-default .cm-hr {color: #999;} .cm-s-default .cm-link {color: #00c;} +.cm-negative {color: #d44;} +.cm-positive {color: #292;} .cm-header, .cm-strong {font-weight: bold;} .cm-em {font-style: italic;} .cm-emstrong {font-style: italic; font-weight: bold;} diff --git a/mode/diff/diff.js b/mode/diff/diff.js index 3402f3b331..9a0d90ea55 100644 --- a/mode/diff/diff.js +++ b/mode/diff/diff.js @@ -1,8 +1,8 @@ CodeMirror.defineMode("diff", function() { var TOKEN_NAMES = { - '+': 'tag', - '-': 'string', + '+': 'positive', + '-': 'negative', '@': 'meta' }; From 16ffe00798df3f3e71a57c22107c045f79445ff8 Mon Sep 17 00:00:00 2001 From: Marijn Haverbeke Date: Wed, 28 Nov 2012 10:58:50 +0100 Subject: [PATCH 0390/5780] Integrate HTTP mode --- doc/compress.html | 1 + index.html | 2 +- mode/http/http.js | 98 +++++++++++++++++++++++++++ mode/{httpmessage => http}/index.html | 6 +- mode/httpmessage/httpmessage.js | 72 -------------------- 5 files changed, 103 insertions(+), 76 deletions(-) create mode 100644 mode/http/http.js rename mode/{httpmessage => http}/index.html (87%) delete mode 100644 mode/httpmessage/httpmessage.js diff --git a/doc/compress.html b/doc/compress.html index df0d8a7acb..8c78b99b05 100644 --- a/doc/compress.html +++ b/doc/compress.html @@ -80,6 +80,7 @@

    { } CodeMi + diff --git a/index.html b/index.html index f3e12c32db..9028f791a2 100644 --- a/index.html +++ b/index.html @@ -49,7 +49,7 @@

    Supported modes:

  • Haxe
  • HTML embedded scripts
  • HTML mixed-mode
  • -
  • HTTP message
  • +
  • HTTP
  • Java
  • JavaScript
  • Jinja2
  • diff --git a/mode/http/http.js b/mode/http/http.js new file mode 100644 index 0000000000..f861fbb609 --- /dev/null +++ b/mode/http/http.js @@ -0,0 +1,98 @@ +CodeMirror.defineMode("http", function() { + function failFirstLine(stream, state) { + stream.skipToEnd(); + state.cur = header; + return "error"; + } + + function start(stream, state) { + if (stream.match(/^HTTP\/\d\.\d/)) { + state.cur = responseStatusCode; + return "keyword"; + } else if (stream.match(/^[A-Z]+/) && /[ \t]/.test(stream.peek())) { + state.cur = requestPath; + return "keyword"; + } else { + return failFirstLine(stream, state); + } + } + + function responseStatusCode(stream, state) { + var code = stream.match(/^\d+/); + if (!code) return failFirstLine(stream, state); + + state.cur = responseStatusText; + var status = Number(code[0]); + if (status >= 100 && status < 200) { + return "positive informational"; + } else if (status >= 200 && status < 300) { + return "positive success"; + } else if (status >= 300 && status < 400) { + return "positive redirect"; + } else if (status >= 400 && status < 500) { + return "negative client-error"; + } else if (status >= 500 && status < 600) { + return "negative server-error"; + } else { + return "error"; + } + } + + function responseStatusText(stream, state) { + stream.skipToEnd(); + state.cur = header; + return null; + } + + function requestPath(stream, state) { + stream.eatWhile(/\S/); + state.cur = requestProtocol; + return "string-2"; + } + + function requestProtocol(stream, state) { + if (stream.match(/^HTTP\/\d\.\d$/)) { + state.cur = header; + return "keyword"; + } else { + return failFirstLine(stream, state); + } + } + + function header(stream, state) { + if (stream.sol() && !stream.eat(/[ \t]/)) { + if (stream.match(/^.*?:/)) { + return "atom"; + } else { + stream.skipToEnd(); + return "error"; + } + } else { + stream.skipToEnd(); + return "string"; + } + } + + function body(stream, state) { + stream.skipToEnd(); + return null; + } + + return { + token: function(stream, state) { + var cur = state.cur; + if (cur != header && cur != body && stream.eatSpace()) return null; + return cur(stream, state); + }, + + blankLine: function(state) { + state.cur = body; + }, + + startState: function() { + return {cur: start}; + } + }; +}); + +CodeMirror.defineMIME("message/http", "http"); diff --git a/mode/httpmessage/index.html b/mode/http/index.html similarity index 87% rename from mode/httpmessage/index.html rename to mode/http/index.html index 97ef86086f..124eb84f95 100644 --- a/mode/httpmessage/index.html +++ b/mode/http/index.html @@ -2,15 +2,15 @@ - CodeMirror: HTTP message mode + CodeMirror: HTTP mode - + -

    CodeMirror: HTTP message mode

    +

    CodeMirror: HTTP mode

    - - - - - + + + + + CodeMirror: Close-Tag Demo + + + + + + + + + + + + +

    Close-Tag Demo

    +
      +
    • Type an html tag. When you type '>' or '/', the tag will auto-close/complete. Block-level tags will indent.
    • +
    • There are options for disabling tag closing or customizing the list of tags to indent.
    • +
    • Works with "text/html" (based on htmlmixed.js or xml.js) mode.
    • +
    • View source for key binding details.
    • +
    + +
    + + + + diff --git a/lib/util/closetag.js b/lib/util/closetag.js index 78271730de..b6249a260b 100644 --- a/lib/util/closetag.js +++ b/lib/util/closetag.js @@ -1,144 +1,145 @@ -/** - * Tag-closer extension for CodeMirror. - * - * This extension adds a "closeTag" utility function that can be used with key bindings to - * insert a matching end tag after the ">" character of a start tag has been typed. It can - * also complete " - * Contributed under the same license terms as CodeMirror. - */ -(function() { - /** Option that allows tag closing behavior to be toggled. Default is true. */ - CodeMirror.defaults['closeTagEnabled'] = true; - - /** Array of tag names to add indentation after the start tag for. Default is the list of block-level html tags. */ - CodeMirror.defaults['closeTagIndent'] = ['applet', 'blockquote', 'body', 'button', 'div', 'dl', 'fieldset', 'form', 'frameset', 'h1', 'h2', 'h3', 'h4', 'h5', 'h6', 'head', 'html', 'iframe', 'layer', 'legend', 'object', 'ol', 'p', 'select', 'table', 'ul']; - - /** Array of tag names where an end tag is forbidden. */ - CodeMirror.defaults['closeTagVoid'] = ['area', 'base', 'br', 'col', 'command', 'embed', 'hr', 'img', 'input', 'keygen', 'link', 'meta', 'param', 'source', 'track', 'wbr']; - - function innerXMLState(cm, state) { - var inner = CodeMirror.innerMode(cm.getMode(), state); - if (inner.mode.name == "xml") return inner.state; - } - - - /** - * Call during key processing to close tags. Handles the key event if the tag is closed, otherwise throws CodeMirror.Pass. - * - cm: The editor instance. - * - ch: The character being processed. - * - indent: Optional. An array of tag names to indent when closing. Omit or pass true to use the default indentation tag list defined in the 'closeTagIndent' option. - * Pass false to disable indentation. Pass an array to override the default list of tag names. - * - vd: Optional. An array of tag names that should not be closed. Omit to use the default void (end tag forbidden) tag list defined in the 'closeTagVoid' option. Ignored in xml mode. - */ - CodeMirror.defineExtension("closeTag", function(cm, ch, indent, vd) { - if (!cm.getOption('closeTagEnabled')) { - throw CodeMirror.Pass; - } - var pos = cm.getCursor(); - var tok = cm.getTokenAt(pos); - var state = innerXMLState(cm, tok.state); - - if (state) { - - if (ch == '>') { - var type = state.type; - - if (tok.type == 'tag' && type == 'closeTag') { - throw CodeMirror.Pass; // Don't process the '>' at the end of an end-tag. - } - - cm.replaceSelection('>'); // Mode state won't update until we finish the tag. - pos = {line: pos.line, ch: pos.ch + 1}; - cm.setCursor(pos); - - tok = cm.getTokenAt(cm.getCursor()); - state = innerXMLState(cm, tok.state); - if (!state) throw CodeMirror.Pass; - var type = state.type; - - if (tok.type == 'tag' && type != 'selfcloseTag') { - var tagName = state.tagName; - if (tagName.length > 0 && shouldClose(cm, vd, tagName)) { - insertEndTag(cm, indent, pos, tagName); - } - return; - } - - // Undo the '>' insert and allow cm to handle the key instead. - cm.setSelection({line: pos.line, ch: pos.ch - 1}, pos); - cm.replaceSelection(""); - - } else if (ch == '/') { - if (tok.type == 'tag' && tok.string == '<') { - var ctx = state.context, tagName = ctx ? ctx.tagName : ''; - if (tagName.length > 0) { - completeEndTag(cm, pos, tagName); - return; - } - } - } - - } - - throw CodeMirror.Pass; // Bubble if not handled - }); - - function insertEndTag(cm, indent, pos, tagName) { - if (shouldIndent(cm, indent, tagName)) { - cm.replaceSelection('\n\n', 'end'); - cm.indentLine(pos.line + 1); - cm.indentLine(pos.line + 2); - cm.setCursor({line: pos.line + 1, ch: cm.getLine(pos.line + 1).length}); - } else { - cm.replaceSelection(''); - cm.setCursor(pos); - } - } - - function shouldIndent(cm, indent, tagName) { - if (typeof indent == 'undefined' || indent == null || indent == true) { - indent = cm.getOption('closeTagIndent'); - } - if (!indent) { - indent = []; - } - return indexOf(indent, tagName.toLowerCase()) != -1; - } - - function shouldClose(cm, vd, tagName) { - if (cm.getOption('mode') == 'xml') { - return true; // always close xml tags - } - if (typeof vd == 'undefined' || vd == null) { - vd = cm.getOption('closeTagVoid'); - } - if (!vd) { - vd = []; - } - return indexOf(vd, tagName.toLowerCase()) == -1; - } - - // C&P from codemirror.js...would be nice if this were visible to utilities. - function indexOf(collection, elt) { - if (collection.indexOf) return collection.indexOf(elt); - for (var i = 0, e = collection.length; i < e; ++i) - if (collection[i] == elt) return i; - return -1; - } - - function completeEndTag(cm, pos, tagName) { - cm.replaceSelection('/' + tagName + '>'); - cm.setCursor({line: pos.line, ch: pos.ch + tagName.length + 2 }); - } - -})(); +/** + * Tag-closer extension for CodeMirror. + * + * This extension adds a "closeTag" utility function that can be used with key bindings to + * insert a matching end tag after the ">" character of a start tag has been typed. It can + * also complete " + * Contributed under the same license terms as CodeMirror. + */ + +(function() { + /** Option that allows tag closing behavior to be toggled. Default is true. */ + CodeMirror.defaults['closeTagEnabled'] = true; + + /** Array of tag names to add indentation after the start tag for. Default is the list of block-level html tags. */ + CodeMirror.defaults['closeTagIndent'] = ['applet', 'blockquote', 'body', 'button', 'div', 'dl', 'fieldset', 'form', 'frameset', 'h1', 'h2', 'h3', 'h4', 'h5', 'h6', 'head', 'html', 'iframe', 'layer', 'legend', 'object', 'ol', 'p', 'select', 'table', 'ul']; + + /** Array of tag names where an end tag is forbidden. */ + CodeMirror.defaults['closeTagVoid'] = ['area', 'base', 'br', 'col', 'command', 'embed', 'hr', 'img', 'input', 'keygen', 'link', 'meta', 'param', 'source', 'track', 'wbr']; + + function innerXMLState(cm, state) { + var inner = CodeMirror.innerMode(cm.getMode(), state); + if (inner.mode.name == "xml") return inner.state; + } + + + /** + * Call during key processing to close tags. Handles the key event if the tag is closed, otherwise throws CodeMirror.Pass. + * - cm: The editor instance. + * - ch: The character being processed. + * - indent: Optional. An array of tag names to indent when closing. Omit or pass true to use the default indentation tag list defined in the 'closeTagIndent' option. + * Pass false to disable indentation. Pass an array to override the default list of tag names. + * - vd: Optional. An array of tag names that should not be closed. Omit to use the default void (end tag forbidden) tag list defined in the 'closeTagVoid' option. Ignored in xml mode. + */ + CodeMirror.defineExtension("closeTag", function(cm, ch, indent, vd) { + if (!cm.getOption('closeTagEnabled')) { + throw CodeMirror.Pass; + } + var pos = cm.getCursor(); + var tok = cm.getTokenAt(pos); + var state = innerXMLState(cm, tok.state); + + if (state) { + + if (ch == '>') { + var type = state.type; + + if (tok.type == 'tag' && type == 'closeTag') { + throw CodeMirror.Pass; // Don't process the '>' at the end of an end-tag. + } + + cm.replaceSelection('>'); // Mode state won't update until we finish the tag. + pos = {line: pos.line, ch: pos.ch + 1}; + cm.setCursor(pos); + + tok = cm.getTokenAt(cm.getCursor()); + state = innerXMLState(cm, tok.state); + if (!state) throw CodeMirror.Pass; + var type = state.type; + + if (tok.type == 'tag' && type != 'selfcloseTag') { + var tagName = state.tagName; + if (tagName.length > 0 && shouldClose(cm, vd, tagName)) { + insertEndTag(cm, indent, pos, tagName); + } + return; + } + + // Undo the '>' insert and allow cm to handle the key instead. + cm.setSelection({line: pos.line, ch: pos.ch - 1}, pos); + cm.replaceSelection(""); + + } else if (ch == '/') { + if (tok.type == 'tag' && tok.string == '<') { + var ctx = state.context, tagName = ctx ? ctx.tagName : ''; + if (tagName.length > 0) { + completeEndTag(cm, pos, tagName); + return; + } + } + } + + } + + throw CodeMirror.Pass; // Bubble if not handled + }); + + function insertEndTag(cm, indent, pos, tagName) { + if (shouldIndent(cm, indent, tagName)) { + cm.replaceSelection('\n\n', 'end'); + cm.indentLine(pos.line + 1); + cm.indentLine(pos.line + 2); + cm.setCursor({line: pos.line + 1, ch: cm.getLine(pos.line + 1).length}); + } else { + cm.replaceSelection(''); + cm.setCursor(pos); + } + } + + function shouldIndent(cm, indent, tagName) { + if (typeof indent == 'undefined' || indent == null || indent == true) { + indent = cm.getOption('closeTagIndent'); + } + if (!indent) { + indent = []; + } + return indexOf(indent, tagName.toLowerCase()) != -1; + } + + function shouldClose(cm, vd, tagName) { + if (cm.getOption('mode') == 'xml') { + return true; // always close xml tags + } + if (typeof vd == 'undefined' || vd == null) { + vd = cm.getOption('closeTagVoid'); + } + if (!vd) { + vd = []; + } + return indexOf(vd, tagName.toLowerCase()) == -1; + } + + // C&P from codemirror.js...would be nice if this were visible to utilities. + function indexOf(collection, elt) { + if (collection.indexOf) return collection.indexOf(elt); + for (var i = 0, e = collection.length; i < e; ++i) + if (collection[i] == elt) return i; + return -1; + } + + function completeEndTag(cm, pos, tagName) { + cm.replaceSelection('/' + tagName + '>'); + cm.setCursor({line: pos.line, ch: pos.ch + tagName.length + 2 }); + } + +})(); From 4052fbf381d94c919c7dd854c9affd99c50e2429 Mon Sep 17 00:00:00 2001 From: Marijn Haverbeke Date: Thu, 29 Nov 2012 12:33:01 +0100 Subject: [PATCH 0399/5780] [util/closetag] Rewrite using defineOption Closes #995 --- demo/closetag.html | 5 +- doc/manual.html | 5 +- lib/codemirror.js | 8 +- lib/util/closetag.js | 182 +++++++++++++++---------------------------- mode/xml/xml.js | 10 ++- test/test.js | 6 ++ 6 files changed, 84 insertions(+), 132 deletions(-) diff --git a/demo/closetag.html b/demo/closetag.html index c8e8ce7d1f..c33d10875f 100644 --- a/demo/closetag.html +++ b/demo/closetag.html @@ -30,10 +30,7 @@

    Close-Tag Demo

    diff --git a/doc/manual.html b/doc/manual.html index 6eeddec15f..1f3691576f 100644 --- a/doc/manual.html +++ b/doc/manual.html @@ -663,7 +663,10 @@

    Programming API

    than those added later.
    removeKeyMap(map)
    Disable a keymap added - with addKeyMap.
    + with addKeyMap. Either + pass in the keymap object itself, or a string, which will be + compared against the name property of the active + keymaps.
    on(type, func)
    Register an event handler for the given event type (a diff --git a/lib/codemirror.js b/lib/codemirror.js index 362b1202aa..bdab0e60b2 100644 --- a/lib/codemirror.js +++ b/lib/codemirror.js @@ -2312,8 +2312,12 @@ window.CodeMirror = (function() { }, removeKeyMap: function(map) { - var i = indexOf(this.view.keyMaps, map); - if (i > -1) this.view.keyMaps.splice(i, 1); + var maps = this.view.keyMaps; + for (var i = 0; i < maps.length; ++i) + if ((typeof map == "string" ? maps[i].name : maps[i]) == map) { + maps.splice(i, 1); + return true; + } }, undo: operation(null, function() {unredoHelper(this, "undo");}), diff --git a/lib/util/closetag.js b/lib/util/closetag.js index b6249a260b..1e62f88bf0 100644 --- a/lib/util/closetag.js +++ b/lib/util/closetag.js @@ -1,145 +1,85 @@ /** * Tag-closer extension for CodeMirror. * - * This extension adds a "closeTag" utility function that can be used with key bindings to - * insert a matching end tag after the ">" character of a start tag has been typed. It can - * also complete "' of an opening + * tag is typed. + * `dontCloseTags` (default is empty tags for HTML, none for XML) + * An array of tag names that should not be autoclosed. + * `indentTags` (default is block tags for HTML, none for XML) + * An array of tag names that should, when opened, cause a + * blank line to be added inside the tag, and the blank line and + * closing line to be indented. * * See demos/closetag.html for a usage example. - * - * @author Nathan Williams - * Contributed under the same license terms as CodeMirror. */ (function() { - /** Option that allows tag closing behavior to be toggled. Default is true. */ - CodeMirror.defaults['closeTagEnabled'] = true; - - /** Array of tag names to add indentation after the start tag for. Default is the list of block-level html tags. */ - CodeMirror.defaults['closeTagIndent'] = ['applet', 'blockquote', 'body', 'button', 'div', 'dl', 'fieldset', 'form', 'frameset', 'h1', 'h2', 'h3', 'h4', 'h5', 'h6', 'head', 'html', 'iframe', 'layer', 'legend', 'object', 'ol', 'p', 'select', 'table', 'ul']; - - /** Array of tag names where an end tag is forbidden. */ - CodeMirror.defaults['closeTagVoid'] = ['area', 'base', 'br', 'col', 'command', 'embed', 'hr', 'img', 'input', 'keygen', 'link', 'meta', 'param', 'source', 'track', 'wbr']; + CodeMirror.defineOption("autoCloseTags", false, function(cm, val, old) { + if (val && (old == CodeMirror.Init || !old)) { + var map = {name: "autoCloseTags"}; + if (typeof val != "object" || val.whenClosing) + map["'/'"] = function(cm) { autoCloseTag(cm, '/'); } + if (typeof val != "object" || val.whenOpening) + map["'>'"] = function(cm) { autoCloseTag(cm, '>'); } + cm.addKeyMap(map); + } else if (!val && (old != CodeMirror.Init && old)) { + cm.removeKeyMap("autoCloseTags"); + } + }); - function innerXMLState(cm, state) { - var inner = CodeMirror.innerMode(cm.getMode(), state); - if (inner.mode.name == "xml") return inner.state; - } + var htmlDontClose = ["area", "base", "br", "col", "command", "embed", "hr", "img", "input", "keygen", "link", "meta", "param", + "source", "track", "wbr"]; + var htmlIndent = ["applet", "blockquote", "body", "button", "div", "dl", "fieldset", "form", "frameset", "h1", "h2", "h3", "h4", + "h5", "h6", "head", "html", "iframe", "layer", "legend", "object", "ol", "p", "select", "table", "ul"]; + function autoCloseTag(cm, ch) { + var pos = cm.getCursor(), tok = cm.getTokenAt(pos); + var inner = CodeMirror.innerMode(cm.getMode(), tok.state), state = inner.state; + if (inner.mode.name != "xml") throw CodeMirror.Pass; - /** - * Call during key processing to close tags. Handles the key event if the tag is closed, otherwise throws CodeMirror.Pass. - * - cm: The editor instance. - * - ch: The character being processed. - * - indent: Optional. An array of tag names to indent when closing. Omit or pass true to use the default indentation tag list defined in the 'closeTagIndent' option. - * Pass false to disable indentation. Pass an array to override the default list of tag names. - * - vd: Optional. An array of tag names that should not be closed. Omit to use the default void (end tag forbidden) tag list defined in the 'closeTagVoid' option. Ignored in xml mode. - */ - CodeMirror.defineExtension("closeTag", function(cm, ch, indent, vd) { - if (!cm.getOption('closeTagEnabled')) { - throw CodeMirror.Pass; - } - var pos = cm.getCursor(); - var tok = cm.getTokenAt(pos); - var state = innerXMLState(cm, tok.state); + var opt = cm.getOption("autoCloseTags"), html = inner.mode.configuration == "html"; + var dontCloseTags = (typeof opt == "object" && opt.dontCloseTags) || (html && htmlDontClose); + var indentTags = (typeof opt == "object" && opt.indentTags) || (html && htmlIndent); - if (state) { - - if (ch == '>') { - var type = state.type; - - if (tok.type == 'tag' && type == 'closeTag') { - throw CodeMirror.Pass; // Don't process the '>' at the end of an end-tag. - } - - cm.replaceSelection('>'); // Mode state won't update until we finish the tag. - pos = {line: pos.line, ch: pos.ch + 1}; - cm.setCursor(pos); - - tok = cm.getTokenAt(cm.getCursor()); - state = innerXMLState(cm, tok.state); - if (!state) throw CodeMirror.Pass; - var type = state.type; + if (ch == ">" && state.tagName) { + var tagName = state.tagName; + if (tok.end > pos.ch) tagName = tagName.slice(0, tagName.length - tok.end + pos.ch); + var lowerTagName = tagName.toLowerCase(); + // Don't process the '>' at the end of an end-tag or self-closing tag + if (tok.type == "tag" && state.type == "closeTag" || + /\/\s*$/.test(tok.string) || + dontCloseTags && indexOf(dontCloseTags, lowerTagName) > -1) + throw CodeMirror.Pass; - if (tok.type == 'tag' && type != 'selfcloseTag') { - var tagName = state.tagName; - if (tagName.length > 0 && shouldClose(cm, vd, tagName)) { - insertEndTag(cm, indent, pos, tagName); - } - return; - } - - // Undo the '>' insert and allow cm to handle the key instead. - cm.setSelection({line: pos.line, ch: pos.ch - 1}, pos); - cm.replaceSelection(""); - - } else if (ch == '/') { - if (tok.type == 'tag' && tok.string == '<') { - var ctx = state.context, tagName = ctx ? ctx.tagName : ''; - if (tagName.length > 0) { - completeEndTag(cm, pos, tagName); - return; - } - } + var doIndent = indentTags && indexOf(indentTags, lowerTagName) > -1; + cm.replaceSelection(">" + (doIndent ? "\n\n" : "") + "", + doIndent ? {line: pos.line + 1, ch: 0} : {line: pos.line, ch: pos.ch + 1}); + if (doIndent) { + cm.indentLine(pos.line + 1); + cm.indentLine(pos.line + 2); } - - } - - throw CodeMirror.Pass; // Bubble if not handled - }); - - function insertEndTag(cm, indent, pos, tagName) { - if (shouldIndent(cm, indent, tagName)) { - cm.replaceSelection('\n\n', 'end'); - cm.indentLine(pos.line + 1); - cm.indentLine(pos.line + 2); - cm.setCursor({line: pos.line + 1, ch: cm.getLine(pos.line + 1).length}); - } else { - cm.replaceSelection(''); - cm.setCursor(pos); + return; + } else if (ch == "/" && tok.type == "tag" && tok.string == "<") { + var tagName = state.context && state.context.tagName; + if (tagName) cm.replaceSelection("/" + tagName + ">", "end"); + return; } + throw CodeMirror.Pass; } - - function shouldIndent(cm, indent, tagName) { - if (typeof indent == 'undefined' || indent == null || indent == true) { - indent = cm.getOption('closeTagIndent'); - } - if (!indent) { - indent = []; - } - return indexOf(indent, tagName.toLowerCase()) != -1; - } - - function shouldClose(cm, vd, tagName) { - if (cm.getOption('mode') == 'xml') { - return true; // always close xml tags - } - if (typeof vd == 'undefined' || vd == null) { - vd = cm.getOption('closeTagVoid'); - } - if (!vd) { - vd = []; - } - return indexOf(vd, tagName.toLowerCase()) == -1; - } - - // C&P from codemirror.js...would be nice if this were visible to utilities. + function indexOf(collection, elt) { if (collection.indexOf) return collection.indexOf(elt); for (var i = 0, e = collection.length; i < e; ++i) if (collection[i] == elt) return i; return -1; } - - function completeEndTag(cm, pos, tagName) { - cm.replaceSelection('/' + tagName + '>'); - cm.setCursor({line: pos.line, ch: pos.ch + tagName.length + 2 }); - } - })(); diff --git a/mode/xml/xml.js b/mode/xml/xml.js index ae7c725070..7b11fd6746 100644 --- a/mode/xml/xml.js +++ b/mode/xml/xml.js @@ -211,14 +211,16 @@ CodeMirror.defineMode("xml", function(config, parserConfig) { } function endtag(startOfLine) { return function(type) { + var tagName = curState.tagName; + curState.tagName = null; if (type == "selfcloseTag" || - (type == "endTag" && Kludges.autoSelfClosers.hasOwnProperty(curState.tagName.toLowerCase()))) { - maybePopContext(curState.tagName.toLowerCase()); + (type == "endTag" && Kludges.autoSelfClosers.hasOwnProperty(tagName.toLowerCase()))) { + maybePopContext(tagName.toLowerCase()); return cont(); } if (type == "endTag") { - maybePopContext(curState.tagName.toLowerCase()); - pushContext(curState.tagName, startOfLine); + maybePopContext(tagName.toLowerCase()); + pushContext(tagName, startOfLine); return cont(); } return cont(); diff --git a/test/test.js b/test/test.js index fe20355eb9..8173121e17 100644 --- a/test/test.js +++ b/test/test.js @@ -1158,4 +1158,10 @@ testCM("addKeyMap", function(cm) { sendKey(39); eq(test, 12); eqPos(cm.getCursor(), {line: 0, ch: 2}); + cm.addKeyMap({Right: function() { test = 55; }, name: "mymap"}); + sendKey(39); + eq(test, 55); + cm.removeKeyMap("mymap"); + sendKey(39); + eqPos(cm.getCursor(), {line: 0, ch: 3}); }, {value: "abc"}); From 7214c383cb3991092ca8f183e52b2f2bca2db5bb Mon Sep 17 00:00:00 2001 From: Marijn Haverbeke Date: Thu, 29 Nov 2012 12:40:33 +0100 Subject: [PATCH 0400/5780] Add missing semicolons --- lib/util/closetag.js | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/lib/util/closetag.js b/lib/util/closetag.js index 1e62f88bf0..7320c17184 100644 --- a/lib/util/closetag.js +++ b/lib/util/closetag.js @@ -27,9 +27,9 @@ if (val && (old == CodeMirror.Init || !old)) { var map = {name: "autoCloseTags"}; if (typeof val != "object" || val.whenClosing) - map["'/'"] = function(cm) { autoCloseTag(cm, '/'); } + map["'/'"] = function(cm) { autoCloseTag(cm, '/'); }; if (typeof val != "object" || val.whenOpening) - map["'>'"] = function(cm) { autoCloseTag(cm, '>'); } + map["'>'"] = function(cm) { autoCloseTag(cm, '>'); }; cm.addKeyMap(map); } else if (!val && (old != CodeMirror.Init && old)) { cm.removeKeyMap("autoCloseTags"); From d97a00f3774128889db71b092a9068efbcdb3b89 Mon Sep 17 00:00:00 2001 From: Yunchi Luo Date: Wed, 14 Nov 2012 01:39:13 -0500 Subject: [PATCH 0401/5780] [vim keymap] Full rewrite See discussion in https://github.com/marijnh/CodeMirror/pull/984 --- demo/vim.html | 3 +- keymap/vim.js | 2359 +++++++++++++++++++++++++++++----------------- test/index.html | 2 + test/vim_test.js | 433 +++++++++ 4 files changed, 1953 insertions(+), 844 deletions(-) create mode 100644 test/vim_test.js diff --git a/demo/vim.html b/demo/vim.html index 6813cb37fb..b3c0e194a6 100644 --- a/demo/vim.html +++ b/demo/vim.html @@ -45,7 +45,8 @@

    CodeMirror: Vim bindings demo

    var editor = CodeMirror.fromTextArea(document.getElementById("code"), { lineNumbers: true, mode: "text/x-csrc", - keyMap: "vim" + keyMap: "vim", + showCursorWhenSelecting: true }); diff --git a/keymap/vim.js b/keymap/vim.js index adb09c39c1..596753bff0 100644 --- a/keymap/vim.js +++ b/keymap/vim.js @@ -1,904 +1,1577 @@ -// Supported keybindings: -// -// Cursor movement: -// h, j, k, l -// e, E, w, W, b, B -// Ctrl-f, Ctrl-b -// Ctrl-n, Ctrl-p -// $, ^, 0 -// G -// ge, gE -// gg -// f, F, t, T -// Ctrl-o, Ctrl-i TODO (FIXME - Ctrl-O wont work in Chrome) -// /, ?, n, N TODO (does not work) -// #, * TODO -// -// Entering insert mode: -// i, I, a, A, o, O -// s -// ce, cb -// cc -// S, C TODO -// cf, cF, ct, cT -// -// Leaving insert mode: -// Esc, Ctrl-[, Ctrl-C -// -// Deleting text: -// x, X -// J -// dd, D -// de, db -// df, dF, dt, dT -// -// Yanking and pasting: -// yy, Y -// p, P -// p' TODO - test -// y' TODO - test -// m TODO - test -// -// Changing text in place: -// ~ -// r -// -// Visual mode: -// v, V TODO -// -// Misc: -// . TODO -// +// Reimplementation of vim keybindings +// word1 +/** + * Supported keybindings: + * + * Motion: + * h, j, k, l + * e, E, w, W, b, B, ge, gE + * f, F, t, T + * $, ^, 0 + * gg, G + * % + * ', ` + * + * Operator: + * d, y, c + * dd, yy, cc + * g~, g~g~ + * >, <, >>, << + * + * Operator-Motion: + * x, X, D, Y, ~ + * + * Action: + * a, i, s, A, I, S, o, O + * J + * u, Ctrl-r + * m + * r + * + * Modes: + * ESC - leave insert mode, visual mode, and clear input state. + * Ctrl-[, Ctrl-c - same as ESC. + * + * Registers: unamed, -, a-z, A-Z, 0-9 + * (Does not respect the special case for number registers when delete + * operator is made with these commands: %, (, ), , /, ?, n, N, {, } ) + * TODO: Implement the remaining registers. + * Marks: a-z, A-Z, and 0-9 + * TODO: Implement the remaining special marks. They have more complex + * behavior. + * + * Code structure: + * 1. Default keymap + * 2. Variable declarations and short basic helpers + * 3. Instance (External API) implementation + * 4. Internal state tracking objects (input state, counter) implementation + * and instanstiation + * 5. Key handler (the main command dispatcher) implementation + * 6. Motion, operator, and action implementations + * 7. Helper functions for the key handler, motions, operators, and actions + * 8. Set up Vim to work as a keymap for CodeMirror. + * + */ (function() { - var sdir = "f"; - var buf = ""; - var mark = {}; - var repeatCount = 0; - function isLine(cm, line) { return line >= 0 && line < cm.lineCount(); } - function emptyBuffer() { buf = ""; } - function pushInBuffer(str) { buf += str; } - function pushRepeatCountDigit(digit) {return function(cm) {repeatCount = (repeatCount * 10) + digit}; } - function getCountOrOne() { - var i = repeatCount; - return i || 1; - } - function clearCount() { - repeatCount = 0; - } - function iterTimes(func) { - for (var i = 0, c = getCountOrOne(); i < c; ++i) func(i, i == c - 1); - clearCount(); - } - function countTimes(func) { - if (typeof func == "string") func = CodeMirror.commands[func]; - return function(cm) { iterTimes(function (i, last) { func(cm, i, last); }); }; - } - - function iterObj(o, f) { - for (var prop in o) if (o.hasOwnProperty(prop)) f(prop, o[prop]); - } - function iterList(l, f) { - for (var i = 0; i < l.length; ++i) f(l[i]); - } - function toLetter(ch) { - // T -> t, Shift-T -> T, '*' -> *, "Space" -> " " - if (ch.slice(0, 6) == "Shift-") { - return ch.slice(0, 1); - } else { - if (ch == "Space") return " "; - if (ch.length == 3 && ch[0] == "'" && ch[2] == "'") return ch[1]; - return ch.toLowerCase(); - } - } - var SPECIAL_SYMBOLS = "~`!@#$%^&*()_-+=[{}]\\|/?.,<>:;\"\'1234567890"; - function toCombo(ch) { - // t -> T, T -> Shift-T, * -> '*', " " -> "Space" - if (ch == " ") return "Space"; - var specialIdx = SPECIAL_SYMBOLS.indexOf(ch); - if (specialIdx != -1) return "'" + ch + "'"; - if (ch.toLowerCase() == ch) return ch.toUpperCase(); - return "Shift-" + ch.toUpperCase(); - } - - var word = [/\w/, /[^\w\s]/], bigWord = [/\S/]; - // Finds a word on the given line, and continue searching the next line if it can't find one. - function findWord(cm, lineNum, pos, dir, regexps) { - var line = cm.getLine(lineNum); - while (true) { - var stop = (dir > 0) ? line.length : -1; - var wordStart = stop, wordEnd = stop; - // Find bounds of next word. - for (; pos != stop; pos += dir) { - for (var i = 0; i < regexps.length; ++i) { - if (regexps[i].test(line.charAt(pos))) { - wordStart = pos; - // Advance to end of word. - for (; pos != stop && regexps[i].test(line.charAt(pos)); pos += dir) {} - wordEnd = (dir > 0) ? pos : pos + 1; - return { - from: Math.min(wordStart, wordEnd), - to: Math.max(wordStart, wordEnd), - line: lineNum}; - } - } - } - // Advance to next/prev line. - lineNum += dir; - if (!isLine(cm, lineNum)) return null; - line = cm.getLine(lineNum); - pos = (dir > 0) ? 0 : line.length; - } - } - /** - * @param {boolean} cm CodeMirror object. - * @param {regexp} regexps Regular expressions for word characters. - * @param {number} dir Direction, +/- 1. - * @param {number} times Number of times to advance word. - * @param {string} where Go to "start" or "end" of word, 'e' vs 'w'. - * @param {boolean} yank Whether we are finding words to yank. If true, - * do not go to the next line to look for the last word. This is to - * prevent deleting new line on 'dw' at the end of a line. - */ - function moveToWord(cm, regexps, dir, times, where, yank) { - var cur = cm.getCursor(); - if (yank) { - where = 'start'; - } - for (var i = 0; i < times; i++) { - var startCh = cur.ch, startLine = cur.line, word; - while (true) { - // Search and advance. - word = findWord(cm, cur.line, cur.ch, dir, regexps); - if (word) { - if (yank && times == 1 && dir == 1 && cur.line != word.line) { - // Stop at end of line of last word. Don't want to delete line return - // for dw if the last deleted word is at the end of a line. - cur.ch = cm.getLine(cur.line).length; - break; + 'use strict'; + + var defaultKeymap = [ + // Key to key mapping. This goes first to make it possible to override + // existing mappings. + { keys: ['Left'], type: 'keyToKey', toKeys: ['h'] }, + { keys: ['Right'], type: 'keyToKey', toKeys: ['l'] }, + { keys: ['Up'], type: 'keyToKey', toKeys: ['k'] }, + { keys: ['Down'], type: 'keyToKey', toKeys: ['j'] }, + { keys: ['Space'], type: 'keyToKey', toKeys: ['l'] }, + { keys: ['Backspace'], type: 'keyToKey', toKeys: ['h'] }, + { keys: ['Ctrl-Space'], type: 'keyToKey', toKeys: ['W'] }, + { keys: ['Ctrl-Backspace'], type: 'keyToKey', toKeys: ['B'] }, + { keys: ['Shift-Space'], type: 'keyToKey', toKeys: ['w'] }, + { keys: ['Shift-Backspace'], type: 'keyToKey', toKeys: ['b'] }, + { keys: ['Ctrl-n'], type: 'keyToKey', toKeys: ['j'] }, + { keys: ['Ctrl-p'], type: 'keyToKey', toKeys: ['k'] }, + { keys: ['Ctrl-['], type: 'keyToKey', toKeys: ['Esc'] }, + { keys: ['Ctrl-c'], type: 'keyToKey', toKeys: ['Esc'] }, + // Motions + { keys: ['h'], type: 'motion', + motion: 'moveByCharacters', + motionArgs: { forward: false }}, + { keys: ['l'], type: 'motion', + motion: 'moveByCharacters', + motionArgs: { forward: true }}, + { keys: ['j'], type: 'motion', + motion: 'moveByLines', + motionArgs: { forward: true, linewise: true }}, + { keys: ['k'], type: 'motion', + motion: 'moveByLines', + motionArgs: { forward: false, linewise: true }}, + { keys: ['w'], type: 'motion', + motion: 'moveByWords', + motionArgs: { forward: true, wordEnd: false }}, + { keys: ['W'], type: 'motion', + motion: 'moveByWords', + motionArgs: { forward: true, wordEnd: false, bigWord: true }}, + { keys: ['e'], type: 'motion', + motion: 'moveByWords', + motionArgs: { forward: true, wordEnd: true, inclusive: true }}, + { keys: ['E'], type: 'motion', + motion: 'moveByWords', + motionArgs: { forward: true, wordEnd: true, bigWord: true, + inclusive: true }}, + { keys: ['b'], type: 'motion', + motion: 'moveByWords', + motionArgs: { forward: false, wordEnd: false }}, + { keys: ['B'], type: 'motion', + motion: 'moveByWords', + motionArgs: { forward: false, wordEnd: false, bigWord: true }}, + { keys: ['g', 'e'], type: 'motion', + motion: 'moveByWords', + motionArgs: { forward: false, wordEnd: true, inclusive: true }}, + { keys: ['g', 'E'], type: 'motion', + motion: 'moveByWords', + motionArgs: { forward: false, wordEnd: true, bigWord: true, + inclusive: true }}, + { keys: ['Ctrl-f'], type: 'motion', + motion: 'moveByPage', motionArgs: { forward: true }}, + { keys: ['Ctrl-b'], type: 'motion', + motion: 'moveByPage', motionArgs: { forward: false }}, + { keys: ['g', 'g'], type: 'motion', + motion: 'moveToLineOrEdgeOfDocument', + motionArgs: { forward: false, explicitRepeat: true }}, + { keys: ['G'], type: 'motion', + motion: 'moveToLineOrEdgeOfDocument', + motionArgs: { forward: true, explicitRepeat: true }}, + { keys: ['0'], type: 'motion', motion: 'moveToStartOfLine' }, + { keys: ['^'], type: 'motion', + motion: 'moveToFirstNonWhiteSpaceCharacter' }, + { keys: ['$'], type: 'motion', motion: 'moveToEol' }, + { keys: ['%'], type: 'motion', + motion: 'moveToMatchedSymbol', + motionArgs: { inclusive: true }}, + { keys: ['f', 'character'], type: 'motion', + motion: 'moveToCharacter', + motionArgs: { forward: true , inclusive: true }}, + { keys: ['F', 'character'], type: 'motion', + motion: 'moveToCharacter', + motionArgs: { forward: false }}, + { keys: ['t', 'character'], type: 'motion', + motion: 'moveTillCharacter', + motionArgs: { forward: true, inclusive: true }}, + { keys: ['T', 'character'], type: 'motion', + motion: 'moveTillCharacter', + motionArgs: { forward: false }}, + { keys: ['\'', 'character'], type: 'motion', motion: 'goToMark' }, + { keys: ['`', 'character'], type: 'motion', motion: 'goToMark' }, + // Operators + { keys: ['d'], type: 'operator', operator: 'delete' }, + { keys: ['y'], type: 'operator', operator: 'yank' }, + { keys: ['c'], type: 'operator', operator: 'change', + operatorArgs: { enterInsertMode: true } }, + { keys: ['>'], type: 'operator', operator: 'indent', + operatorArgs: { indentRight: true }}, + { keys: ['<'], type: 'operator', operator: 'indent', + operatorArgs: { indentRight: false }}, + { keys: ['g', '~'], type: 'operator', operator: 'swapcase' }, + // Operator-Motion dual commands + { keys: ['x'], type: 'operatorMotion', operator: 'delete', + motion: 'moveByCharacters', motionArgs: { forward: true }, + operatorMotionArgs: { visualLine: false }}, + { keys: ['X'], type: 'operatorMotion', operator: 'delete', + motion: 'moveByCharacters', motionArgs: { forward: false }, + operatorMotionArgs: { visualLine: true }}, + { keys: ['D'], type: 'operatorMotion', operator: 'delete', + motion: 'moveToEol' , operatorMotionArgs: { visualLine: true }}, + { keys: ['Y'], type: 'operatorMotion', operator: 'yank', + motion: 'moveToEol' , operatorMotionArgs: { visualLine: true }}, + { keys: ['~'], type: 'operatorMotion', operator: 'swapcase', + motion: 'moveByCharacters', motionArgs: { forward: true }}, + // Actions + { keys: ['a'], type: 'action', action: 'enterInsertMode', + motion: 'moveByCharacters', + motionArgs: { forward: true, noRepeat: true }}, + { keys: ['A'], type: 'action', action: 'enterInsertMode', + motion: 'moveToEol' }, + { keys: ['i'], type: 'action', action: 'enterInsertMode' }, + { keys: ['I'], type: 'action', action: 'enterInsertMode', + motion: 'moveToFirstNonWhiteSpaceCharacter' }, + { keys: ['s'], type: 'action', action: 'enterInsertMode', + motion: 'moveByCharacters', motionArgs: { forward: true }, + operator: 'delete' }, + { keys: ['S'], type: 'action', action: 'enterInsertMode', + motion: 'moveByLines', + motionArgs: { forward: true, linewise: true, explicitRepeat: true }, + operator: 'delete' }, + { keys: ['o'], type: 'action', action: 'newLineAndEnterInsertMode', + actionArgs: { after: true }}, + { keys: ['O'], type: 'action', action: 'newLineAndEnterInsertMode', + actionArgs: { after: false }}, + { keys: ['v'], type: 'action', action: 'toggleVisualMode' }, + { keys: ['V'], type: 'action', action: 'toggleVisualMode', + actionArgs: { linewise: true }}, + { keys: ['J'], type: 'action', action: 'joinLines' }, + { keys: ['p'], type: 'action', action: 'paste', + actionArgs: { after: true }}, + { keys: ['P'], type: 'action', action: 'paste', + actionArgs: { after: false }}, + { keys: ['r', 'character'], type: 'action', action: 'replace' }, + { keys: ['u'], type: 'action', action: 'undo' }, + { keys: ['Ctrl-r'], type: 'action', action: 'redo' }, + { keys: ['m', 'character'], type: 'action', action: 'setMark' }, + { keys: ['\"', 'character'], type: 'action', action: 'setRegister' }, + // Text object motions + { keys: ['a', 'character'], type: 'motion', + motion: 'textObjectManipulation' }, + { keys: ['i', 'character'], type: 'motion', + motion: 'textObjectManipulation', + motionArgs: { textObjectInner: true }} + ]; + + var Vim = function() { + var alphabetRegex = /[A-Za-z]/; + var numberRegex = /[\d]/; + var whiteSpaceRegex = /\s/; + var wordRegexp = [(/\w/), (/[^\w\s]/)], bigWordRegexp = [(/\S/)]; + function makeKeyRange(start, size) { + var keys = []; + for (var i = start; i < start + size; i++) { + keys.push(String.fromCharCode(i)); + } + return keys; + } + var upperCaseAlphabet = makeKeyRange(65, 26); + var lowerCaseAlphabet = makeKeyRange(97, 26); + var numbers = makeKeyRange(48, 10); + var SPECIAL_SYMBOLS = '~`!@#$%^&*()_-+=[{}]\\|/?.,<>:;\"\''; + var specialSymbols = SPECIAL_SYMBOLS.split(''); + var specialKeys = ['Left', 'Right', 'Up', 'Down', 'Space', 'Backspace', + 'Esc']; + var validMarks = upperCaseAlphabet.concat(lowerCaseAlphabet).concat( + numbers); + var validRegisters = upperCaseAlphabet.concat(lowerCaseAlphabet).concat( + numbers).concat('-\"'.split('')); + + function isAlphabet(k) { + return alphabetRegex.test(k); + } + function isLine(cm, line) { + return line >= 0 && line < cm.lineCount(); + } + function isLowerCase(k) { + return (/^[a-z]$/).test(k); + } + function isMatchableSymbol(k) { + return '()[]{}'.indexOf(k) != -1; + } + function isNumber(k) { + return numberRegex.test(k); + } + function isUpperCase(k) { + return (/^[A-Z]$/).test(k); + } + function isAlphanumeric(k) { + return (/^[a-zA-Z-0-9]/).test(k); + } + function isWhiteSpace(k) { + return whiteSpaceRegex.test(k); + } + function isWhiteSpaceString(k) { + return (/^\s*$/).test(k); + } + function inRangeInclusive(x, start, end) { + return x >= start && x <= end; + } + function inArray(val, arr) { + for (var i = 0; i < arr.length; i++) { + if (arr[i] == val) { return true; } + } + return false; + } + + var vimApi= { + addKeyMap: function() { + // Add user defined key bindings. + // TODO: Implement this. + }, + buildKeyMap: function() { + // TODO: Convert keymap into dictionary format for fast lookup. + }, + // Initializes vim state variable on the CodeMirror object. Should only be + // called lazily by handleKey or for testing. + maybeInitState: function(cm) { + if (!cm.vimState) { + // Store instance state in the CodeMirror object. + cm.vimState = { + inputState: new InputState(), + marks: {}, + registerController: new RegisterController({}), + visualMode: false, + // If we are in visual line mode. No effect if visualMode is false. + visualLine: false + }; + } + }, + // This is the outermost function called by CodeMirror, after keys have + // been mapped to their Vim equivalents. + handleKey: function(cm, key) { + this.maybeInitState(cm); + var vim = cm.vimState; + if (key == 'Esc') { + // Clear input state and get back to normal mode. + vim.inputState.reset(); + if (vim.visualMode) { + exitVisualMode(cm, vim); + } + return; + } + if (vim.visualMode && + cursorEqual(cm.getCursor('head'), cm.getCursor('anchor'))) { + // The selection was cleared. Exit visual mode. + exitVisualMode(cm, vim); + } + if (key != '0' || (key == '0' && vim.inputState.getRepeat() === 0)) { + // Have to special case 0 since it's both a motion and a number. + var command = commandDispatcher.matchCommand(key, defaultKeymap, + vim); + } + if (!command && isNumber(key)) { + // Increment count unless count is 0 and key is 0. + vim.inputState.pushRepeatDigit(key); + return; + } + if (command) { + if (command.type == 'keyToKey') { + // TODO: prevent infinite recursion. + for (var i = 0; i < command.toKeys.length; i++) { + this.handleKey(cm, command.toKeys[i]); + } } else { - // Move to the word we just found. If by moving to the word we end up - // in the same spot, then move an extra character and search again. - cur.line = word.line; - if (dir > 0 && where == 'end') { - // 'e' - if (startCh != word.to - 1 || startLine != word.line) { - cur.ch = word.to - 1; - break; - } else { - cur.ch = word.to; + commandDispatcher.processCommand(cm, vim, command); + } + } + } + }; + + // Represents the current input state. + function InputState() { + this.reset(); + } + InputState.prototype.reset = function() { + this.prefixRepeat = []; + this.motionRepeat = []; + + this.operator = null; + this.operatorArgs = null; + this.motion = null; + this.motionArgs = null; + this.keyBuffer = []; // For matching multi-key commands. + this.registerName = null; // Defaults to the unamed register. + }; + InputState.prototype.pushRepeatDigit = function(n) { + if (!this.operator) { + this.prefixRepeat = this.prefixRepeat.concat(n); + } else { + this.motionRepeat = this.motionRepeat.concat(n); + } + }; + InputState.prototype.getRepeat = function() { + var repeat = 0; + if (this.prefixRepeat.length > 0 || this.motionRepeat.length > 0) { + repeat = 1; + if (this.prefixRepeat.length > 0) { + repeat *= parseInt(this.prefixRepeat.join(''), 10); + } + if (this.motionRepeat.length > 0) { + repeat *= parseInt(this.motionRepeat.join(''), 10); + } + } + return repeat; + }; + + function Register() { + this.clear(); + } + Register.prototype = { + set: function(text, linewise) { + this.text = text; + this.linewise = !!linewise; + }, + append: function(text, linewise) { + // if this register has ever been set to linewise, use linewise. + if (linewise || this.linewise) { + this.text += '\n' + text; + this.linewise = true; + } else { + this.text += text; + } + }, + clear: function() { + this.text = ''; + this.linewise = false; + } + }; + + function RegisterController(registers) { + this.registers = registers; + this.lastUpdatedRegisterName = null; + this.unamedRegister = registers['\"'] = new Register(); + } + RegisterController.prototype = { + pushText: function(registerName, operator, text, linewise) { + // Lowercase and uppercase registers refer to the same register. + // Uppercase just means append. + var append = isUpperCase(registerName); + var register = this.isValidRegister(registerName) ? + this.getRegister(registerName) : null; + if (register && + registerName.toLowerCase() != this.lastUpdatedRegisterName) { + // Switched registers, clear the unamed register. + this.lastUpdatedRegisterName = registerName.toLowerCase(); + this.unamedRegister.set('', false); + } else { + this.lastUpdatedRegisterName = null; + } + // The unamed register always has the same value as the last used + // register. + if (append) { + if (register) { + register.append(text, linewise); + } + this.unamedRegister.append(text, linewise); + } else { + if (register) { + register.set(text, linewise); + } + this.unamedRegister.set(text, linewise); + } + if (!register) { + // These only happen if no register was explicitly specified. + if (operator == 'yank') { + // The 0 register contains the text from the most recent yank. + this.getRegisterInternal('0').set(text, linewise); + } else if (operator == 'delete' || operator == 'change') { + if (text.indexOf('\n') == -1) { + // Delete less than 1 line. Update the small delete register. + this.getRegisterInternal('-').set(text, linewise); + } else { + // Shift down the contents of the numbered registers and put the + // deleted text into register 1. + for (var i = 9; i >= 2; i--) { + var from = this.getRegisterInternal('' + (i - 1)); + this.registers['' + i] = from; } - } else if (dir > 0 && where == 'start') { - // 'w' - if (startCh != word.from || startLine != word.line) { - cur.ch = word.from; - break; - } else { - cur.ch = word.to; + this.registers['1'] = new Register(); + this.registers['1'].set(text, linewise); + } + } + } + }, + getRegister: function(name) { + if (!this.isValidRegister(name)) { + return this.unamedRegister; + } + return this.getRegisterInternal(name); + }, + getRegisterInternal: function(name) { + if (!name) { + return null; + } + name = name.toLowerCase(); + if (!this.registers[name]) { + this.registers[name] = new Register(); + } + return this.registers[name]; + }, + isValidRegister: function(name) { + return name && inArray(name, validRegisters); + } + }; + + var commandDispatcher = { + matchCommand: function(key, keyMap, vim) { + var inputState = vim.inputState; + var keys = inputState.keyBuffer.concat(key); + for (var i = 0; i < keyMap.length; i++) { + var command = keyMap[i]; + if (matchKeysPartial(keys, command.keys)) { + if (keys.length < command.keys.length) { + // Matches part of a multi-key command. Buffer and wait for next + // stroke. + inputState.keyBuffer.push(key); + return null; + } else { + if (inputState.operator && command.type == 'action') { + // Ignore matched action commands after an operator. Operators + // only operate on motions. This check is really for text + // objects since aW, a[ etcs conflicts with a. + continue; } - } else if (dir < 0 && where == 'end') { - // 'ge' - if (startCh != word.to || startLine != word.line) { - cur.ch = word.to; - break; - } else { - cur.ch = word.from - 1; + // Matches whole comand. Return the command. + if (command.keys[keys.length - 1] == 'character') { + inputState.selectedCharacter = keys[keys.length - 1]; } - } else if (dir < 0 && where == 'start') { - // 'b' - if (startCh != word.from || startLine != word.line) { - cur.ch = word.from; - break; + inputState.keyBuffer = []; + return command; + } + } + } + // Clear the buffer since there are no partial matches. + inputState.keyBuffer = []; + return null; + }, + processCommand: function(cm, vim, command) { + switch (command.type) { + case 'motion': + this.processMotion(cm, vim, command); + break; + case 'operator': + this.processOperator(cm, vim, command); + break; + case 'operatorMotion': + this.processOperatorMotion(cm, vim, command); + break; + case 'action': + this.processAction(cm, vim, command); + break; + default: + break; + } + }, + processMotion: function(cm, vim, command) { + vim.inputState.motion = command.motion; + vim.inputState.motionArgs = copyArgs(command.motionArgs); + this.evalInput(cm, vim); + }, + processOperator: function(cm, vim, command) { + var inputState = vim.inputState; + if (inputState.operator) { + if (inputState.operator == command.operator) { + // Typing an operator twice like 'dd' makes the operator operate + // linewise + inputState.motion = 'expandToLine'; + inputState.motionArgs = { linewise: true }; + this.evalInput(cm, vim); + return; + } else { + // 2 different operators in a row doesn't make sense. + inputState.reset(); + } + } + inputState.operator = command.operator; + inputState.operatorArgs = copyArgs(command.operatorArgs); + if (vim.visualMode) { + // Operating on a selection in visual mode. We don't need a motion. + this.evalInput(cm, vim); + } + }, + processOperatorMotion: function(cm, vim, command) { + var visualMode = vim.visualMode; + var operatorMotionArgs = copyArgs(command.operatorMotionArgs); + if (operatorMotionArgs) { + // Operator motions may have special behavior in visual mode. + if (visualMode && operatorMotionArgs.visualLine) { + vim.visualLine = true; + } + } + this.processOperator(cm, vim, command); + if (!visualMode) { + this.processMotion(cm, vim, command); + } + }, + processAction: function(cm, vim, command) { + var inputState = vim.inputState; + var repeat = inputState.getRepeat(); + var repeatIsExplicit = !!repeat; + var actionArgs = copyArgs(command.actionArgs) || {}; + if (inputState.selectedCharacter) { + actionArgs.selectedCharacter = inputState.selectedCharacter; + } + // Actions may or may not have motions and operators. Do these first. + if (command.operator) { + this.processOperator(cm, vim, command); + } + if (command.motion) { + this.processMotion(cm, vim, command); + } + if (command.motion || command.operator) { + this.evalInput(cm, vim); + } + actionArgs.repeat = repeat || 1; + actionArgs.repeatIsExplicit = repeatIsExplicit; + actionArgs.registerName = inputState.registerName; + inputState.reset(); + actions[command.action](cm, actionArgs, vim); + }, + evalInput: function(cm, vim) { + // If the motion comand is set, execute both the operator and motion. + // Otherwise return. + var inputState = vim.inputState; + var motion = inputState.motion; + var motionArgs = inputState.motionArgs || {}; + var operator = inputState.operator; + var operatorArgs = inputState.operatorArgs || {}; + var registerName = inputState.registerName; + var selectionEnd = cm.getCursor('head'); + var selectionStart = cm.getCursor('anchor'); + // The difference between cur and selection cursors are that cur is + // being operated on and ignores that there is a selection. + var curStart = copyCursor(selectionEnd); + var curEnd; + var repeat = inputState.getRepeat(); + if (repeat > 0 && motionArgs.explicitRepeat) { + motionArgs.repeatIsExplicit = true; + } else if (motionArgs.noRepeat || + (!motionArgs.explicitRepeat && repeat === 0)) { + repeat = 1; + motionArgs.repeatIsExplicit = false; + } + if (inputState.selectedCharacter) { + // If there is a character input, stick it in all of the arg arrays. + motionArgs.selectedCharacter = operatorArgs.selectedCharacter = + inputState.selectedCharacter; + } + motionArgs.repeat = repeat; + inputState.reset(); + if (motion) { + var motionResult = motions[motion](cm, motionArgs, vim); + if (!motionResult) { + return; + } + if (motionResult instanceof Array) { + curStart = motionResult[0]; + curEnd = motionResult[1]; + } else { + curEnd = motionResult; + } + // TODO: Handle null returns from motion commands better. + if (!curEnd) { + curEnd = { ch: curStart.ch, line: curStart.line }; + } + if (vim.visualMode) { + // Check if the selection crossed over itself. Will need to shift + // the start point if that happened. + if (cursorIsBefore(selectionStart, selectionEnd) && + (cursorEqual(selectionStart, curEnd) || + cursorIsBefore(curEnd, selectionStart))) { + // The end of the selection has moved from after the start to + // before the start. We will shift the start right by 1. + selectionStart.ch += 1; + } else if (cursorIsBefore(selectionEnd, selectionStart) && + (cursorEqual(selectionStart, curEnd) || + cursorIsBefore(selectionStart, curEnd))) { + // The opposite happened. We will shift the start left by 1. + selectionStart.ch -= 1; + } + selectionEnd = curEnd; + if (vim.visualLine) { + if (cursorIsBefore(selectionStart, selectionEnd)) { + selectionStart.ch = 0; + selectionEnd.ch = lineLength(cm, selectionEnd.line); } else { - cur.ch = word.from - 1; + selectionEnd.ch = 0; + selectionStart.ch = lineLength(cm, selectionStart.line); } } + // Need to set the cursor to clear the selection. Otherwise, + // CodeMirror can't figure out that we changed directions... + cm.setCursor(selectionStart); + cm.setSelection(selectionStart, selectionEnd); + } else { + cm.setCursor(curEnd.line, curEnd.ch); } - } else { - // No more words to be found. Move to end of document. - for (; isLine(cm, cur.line + dir); cur.line += dir) {} - cur.ch = (dir > 0) ? cm.getLine(cur.line).length : 0; - break; - } - } - } - if (where == 'end' && yank) { - // Include the last character of the word for actions. - cur.ch++; - } - return cur; - } - function joinLineNext(cm) { - var cur = cm.getCursor(), ch = cur.ch, line = cm.getLine(cur.line); - CodeMirror.commands.goLineEnd(cm); - if (cur.line != cm.lineCount()) { - CodeMirror.commands.goLineEnd(cm); - cm.replaceSelection(" ", "end"); - CodeMirror.commands.delCharAfter(cm); - } - } - function delTillMark(cm, cHar) { - var i = mark[cHar]; - if (i === undefined) { - // console.log("Mark not set"); // TODO - show in status bar - return; - } - var l = cm.getCursor().line, start = i > l ? l : i, end = i > l ? i : l; - cm.setCursor(start); - for (var c = start; c <= end; c++) { - pushInBuffer("\n" + cm.getLine(start)); - cm.removeLine(start); - } - } - function yankTillMark(cm, cHar) { - var i = mark[cHar]; - if (i === undefined) { - // console.log("Mark not set"); // TODO - show in status bar - return; - } - var l = cm.getCursor().line, start = i > l ? l : i, end = i > l ? i : l; - for (var c = start; c <= end; c++) { - pushInBuffer("\n" + cm.getLine(c)); - } - cm.setCursor(start); - } - function goLineStartText(cm) { - // Go to the start of the line where the text begins, or the end for whitespace-only lines - var cur = cm.getCursor(), firstNonWS = cm.getLine(cur.line).search(/\S/); - cm.setCursor(cur.line, firstNonWS == -1 ? line.length : firstNonWS); - } - - function charIdxInLine(cm, cHar, motion_options) { - // Search for cHar in line. - // motion_options: {forward, inclusive} - // If inclusive = true, include it too. - // If forward = true, search forward, else search backwards. - // If char is not found on this line, do nothing - var cur = cm.getCursor(), line = cm.getLine(cur.line), idx; - var ch = toLetter(cHar), mo = motion_options; - if (mo.forward) { - idx = line.indexOf(ch, cur.ch + 1); - if (idx != -1 && mo.inclusive) idx += 1; - } else { - idx = line.lastIndexOf(ch, cur.ch); - if (idx != -1 && !mo.inclusive) idx += 1; - } - return idx; - } - - function moveTillChar(cm, cHar, motion_options) { - // Move to cHar in line, as found by charIdxInLine. - var idx = charIdxInLine(cm, cHar, motion_options), cur = cm.getCursor(); - if (idx != -1) cm.setCursor({line: cur.line, ch: idx}); - } - - function delTillChar(cm, cHar, motion_options) { - // delete text in this line, untill cHar is met, - // as found by charIdxInLine. - // If char is not found on this line, do nothing - var idx = charIdxInLine(cm, cHar, motion_options); - var cur = cm.getCursor(); - if (idx !== -1) { - if (motion_options.forward) { - cm.replaceRange("", {line: cur.line, ch: cur.ch}, {line: cur.line, ch: idx}); - } else { - cm.replaceRange("", {line: cur.line, ch: idx}, {line: cur.line, ch: cur.ch}); - } - } - } - - function enterInsertMode(cm) { - // enter insert mode: switch mode and cursor - clearCount(); - cm.setOption("keyMap", "vim-insert"); - } - - function dialog(cm, text, shortText, f) { - if (cm.openDialog) cm.openDialog(text, f); - else f(prompt(shortText, "")); - } - function showAlert(cm, text) { - var esc = text.replace(/[<&]/, function(ch) { return ch == "<" ? "<" : "&"; }); - if (cm.openDialog) cm.openDialog(esc + " "); - else alert(text); - } - - // main keymap - var map = CodeMirror.keyMap.vim = { - // Pipe (|); TODO: should be *screen* chars, so need a util function to turn tabs into spaces? - "'|'": function(cm) { - cm.setCursor(cm.getCursor().line, getCountOrOne() - 1); - clearCount(); - }, - "A": function(cm) { - cm.setCursor(cm.getCursor().line, cm.getCursor().ch+1); - enterInsertMode(cm); - }, - "Shift-A": function(cm) { CodeMirror.commands.goLineEnd(cm); enterInsertMode(cm);}, - "I": function(cm) { enterInsertMode(cm);}, - "Shift-I": function(cm) { goLineStartText(cm); enterInsertMode(cm);}, - "O": function(cm) { - CodeMirror.commands.goLineEnd(cm); - CodeMirror.commands.newlineAndIndent(cm); - enterInsertMode(cm); - }, - "Shift-O": function(cm) { - CodeMirror.commands.goLineStart(cm); - cm.replaceSelection("\n", "start"); - cm.indentLine(cm.getCursor().line); - enterInsertMode(cm); - }, - "G": function(cm) { cm.setOption("keyMap", "vim-prefix-g");}, - "Shift-D": function(cm) { - var cursor = cm.getCursor(); - var lineN = cursor.line; - var line = cm.getLine(lineN); - cm.setLine(lineN, line.slice(0, cursor.ch)); - - emptyBuffer(); - pushInBuffer(line.slice(cursor.ch)); - - if (repeatCount > 1) { - // we've already done it once - --repeatCount; - // the lines dissapear (ie, cursor stays on the same lineN), - // so only incremenet once - ++lineN; - - iterTimes(function() { - pushInBuffer(cm.getLine(lineN)); - cm.removeLine(lineN); - }); - } - }, - - "S": function (cm) { - countTimes(function (_cm) { - CodeMirror.commands.delCharAfter(_cm); - })(cm); - enterInsertMode(cm); - }, - "M": function(cm) {cm.setOption("keyMap", "vim-prefix-m"); mark = {};}, - "Y": function(cm) {cm.setOption("keyMap", "vim-prefix-y"); emptyBuffer();}, - "Shift-Y": function(cm) { - emptyBuffer(); - iterTimes(function(i) { pushInBuffer("\n" + cm.getLine(cm.getCursor().line + i)); }); - }, - "/": function(cm) {var f = CodeMirror.commands.find; f && f(cm); sdir = "f";}, - "'?'": function(cm) { - var f = CodeMirror.commands.find; - if (f) { f(cm); CodeMirror.commands.findPrev(cm); sdir = "r"; } - }, - "N": function(cm) { - var fn = CodeMirror.commands.findNext; - if (fn) sdir != "r" ? fn(cm) : CodeMirror.commands.findPrev(cm); - }, - "Shift-N": function(cm) { - var fn = CodeMirror.commands.findNext; - if (fn) sdir != "r" ? CodeMirror.commands.findPrev(cm) : fn.findNext(cm); - }, - "Shift-G": function(cm) { - (repeatCount == 0) ? cm.setCursor(cm.lineCount()) : cm.setCursor(repeatCount - 1); - clearCount(); - CodeMirror.commands.goLineStart(cm); - }, - "':'": function(cm) { - var exModeDialog = ': '; - dialog(cm, exModeDialog, ':', function(command) { - if (command.match(/^\d+$/)) { - cm.setCursor(command - 1, cm.getCursor().ch); - } else { - showAlert(cm, "Bad command: " + command); } - }); - }, - nofallthrough: true, style: "fat-cursor" - }; - // standard mode switching - iterList(["d", "t", "T", "f", "F", "c", "r"], function (ch) { - CodeMirror.keyMap.vim[toCombo(ch)] = function (cm) { - cm.setOption("keyMap", "vim-prefix-" + ch); - emptyBuffer(); + if (operator) { + operatorArgs.repeat = repeat; // Indent in visual mode needs this. + if (vim.visualMode) { + curStart = selectionStart; + curEnd = selectionEnd; + motionArgs.inclusive = true; + } + // Swap start and end if motion was backward. + if (cursorIsBefore(curEnd, curStart)) { + var tmp = curStart; + curStart = curEnd; + curEnd = tmp; + var inverted = true; + } + if (motionArgs.inclusive && !(vim.visualMode && inverted)) { + // Move the selection end one to the right to include the last + // character. + curEnd.ch++; + } + var linewise = motionArgs.linewise || + (vim.visualMode && vim.visualLine); + if (linewise) { + // Expand selection to entire line. + expandSelectionToLine(cm, curStart, curEnd); + } else if (motionArgs.forward) { + // Clip to trailing newlines only if we the motion goes forward. + clipToLine(cm, curStart, curEnd); + } + operatorArgs.registerName = registerName; + // Keep track of linewise as it affects how paste and change behave. + operatorArgs.linewise = linewise; + operators[operator](cm, operatorArgs, vim, curStart, + curEnd); + if (vim.visualMode) { + exitVisualMode(cm, vim); + } + if (operatorArgs.enterInsertMode) { + actions.enterInsertMode(cm); + } + } + } }; - }); - - // main num keymap - // Add bindings that are influenced by number keys - iterObj({ - "X": function(cm) {CodeMirror.commands.delCharAfter(cm);}, - "P": function(cm) { - var cur = cm.getCursor().line; - if (buf!= "") { - if (buf[0] == "\n") CodeMirror.commands.goLineEnd(cm); - cm.replaceRange(buf, cm.getCursor()); - } - }, - "Shift-X": function(cm) {CodeMirror.commands.delCharBefore(cm);}, - "Shift-J": function(cm) {joinLineNext(cm);}, - "Shift-P": function(cm) { - var cur = cm.getCursor().line; - if (buf!= "") { - CodeMirror.commands.goLineUp(cm); - CodeMirror.commands.goLineEnd(cm); - cm.replaceSelection(buf, "end"); - } - cm.setCursor(cur+1); - }, - "'~'": function(cm) { - var cur = cm.getCursor(), cHar = cm.getRange({line: cur.line, ch: cur.ch}, {line: cur.line, ch: cur.ch+1}); - cHar = cHar != cHar.toLowerCase() ? cHar.toLowerCase() : cHar.toUpperCase(); - cm.replaceRange(cHar, {line: cur.line, ch: cur.ch}, {line: cur.line, ch: cur.ch+1}); - cm.setCursor(cur.line, cur.ch+1); - }, - "Ctrl-B": function(cm) {CodeMirror.commands.goPageUp(cm);}, - "Ctrl-F": function(cm) {CodeMirror.commands.goPageDown(cm);}, - "Ctrl-P": "goLineUp", "Ctrl-N": "goLineDown", - "U": "undo", "Ctrl-R": "redo" - }, function(key, cmd) { map[key] = countTimes(cmd); }); - - // empty key maps - iterList([ - "vim-prefix-d'", - "vim-prefix-y'", - "vim-prefix-df", - "vim-prefix-dF", - "vim-prefix-dt", - "vim-prefix-dT", - "vim-prefix-c", - "vim-prefix-cf", - "vim-prefix-cF", - "vim-prefix-ct", - "vim-prefix-cT", - "vim-prefix-", - "vim-prefix-f", - "vim-prefix-F", - "vim-prefix-t", - "vim-prefix-T", - "vim-prefix-r", - "vim-prefix-m" - ], - function (prefix) { - CodeMirror.keyMap[prefix] = { - auto: "vim", - nofallthrough: true, - style: "fat-cursor" - }; - }); - - CodeMirror.keyMap["vim-prefix-g"] = { - "E": countTimes(function(cm) { cm.setCursor(moveToWord(cm, word, -1, 1, "end"));}), - "Shift-E": countTimes(function(cm) { cm.setCursor(moveToWord(cm, bigWord, -1, 1, "end"));}), - "G": function (cm) { - cm.setCursor({line: repeatCount - 1, ch: cm.getCursor().ch}); - clearCount(); - }, - auto: "vim", nofallthrough: true, style: "fat-cursor" - }; - CodeMirror.keyMap["vim-prefix-d"] = { - "D": countTimes(function(cm) { - pushInBuffer("\n" + cm.getLine(cm.getCursor().line)); - cm.removeLine(cm.getCursor().line); - cm.setOption("keyMap", "vim"); - }), - "'": function(cm) { - cm.setOption("keyMap", "vim-prefix-d'"); - emptyBuffer(); - }, - "B": function(cm) { - var cur = cm.getCursor(); - var line = cm.getLine(cur.line); - var index = line.lastIndexOf(" ", cur.ch); - - pushInBuffer(line.substring(index, cur.ch)); - cm.replaceRange("", {line: cur.line, ch: index}, cur); - cm.setOption("keyMap", "vim"); - }, - nofallthrough: true, style: "fat-cursor" - }; - - CodeMirror.keyMap["vim-prefix-c"] = { - "B": function (cm) { - countTimes("delWordBefore")(cm); - enterInsertMode(cm); - }, - "C": function (cm) { - iterTimes(function (i, last) { - CodeMirror.commands.deleteLine(cm); - if (i) { - CodeMirror.commands.delCharAfter(cm); - if (last) CodeMirror.commands.deleteLine(cm); - } - }); - enterInsertMode(cm); - }, - nofallthrough: true, style: "fat-cursor" - }; - - iterList(["vim-prefix-d", "vim-prefix-c", "vim-prefix-"], function (prefix) { - iterList(["f", "F", "T", "t"], - function (ch) { - CodeMirror.keyMap[prefix][toCombo(ch)] = function (cm) { - cm.setOption("keyMap", prefix + ch); - emptyBuffer(); - }; - }); - }); - - var MOTION_OPTIONS = { - "t": {inclusive: false, forward: true}, - "f": {inclusive: true, forward: true}, - "T": {inclusive: false, forward: false}, - "F": {inclusive: true, forward: false} - }; - - function setupPrefixBindingForKey(m) { - CodeMirror.keyMap["vim-prefix-m"][m] = function(cm) { - mark[m] = cm.getCursor().line; + /** + * typedef {Object{line:number,ch:number}} Cursor An object containing the + * position of the cursor. + */ + // All of the functions below return Cursor objects. + var motions = { + expandToLine: function(cm, motionArgs) { + // Expands forward to end of line, and then to next line if repeat is > 1. + // Does not handle backward motion! + var cursor = cm.getCursor(); + var endLine = Math.min(cm.lineCount(), + cursor.line + motionArgs.repeat - 1); + return { line: endLine, ch: lineLength(cm, endLine) }; + }, + goToMark: function(cm, motionArgs, vim) { + var mark = vim.marks[motionArgs.selectedCharacter]; + if (mark) { + return mark.find(); + } + return null; + }, + moveByCharacters: function(cm, motionArgs) { + var cursor = cm.getCursor(); + var line = cm.getLine(cursor.line); + var repeat = motionArgs.repeat; + if (motionArgs.forward) { + return { line: cursor.line, + ch: Math.min(line.length, cursor.ch + repeat) }; + } else { + return { line: cursor.line, ch: Math.max(0, cursor.ch - repeat) }; + } + }, + moveByLines: function(cm, motionArgs) { + var cursor = cm.getCursor(); + var repeat = motionArgs.repeat; + if (motionArgs.forward) { + return { line: Math.min(cm.lineCount(), cursor.line + repeat), + ch: cursor.ch }; + } else { + return { line: Math.max(0, cursor.line - repeat), ch: cursor.ch }; + } + }, + moveByPage: function(cm, motionArgs) { + // CodeMirror only exposes functions that move the cursor page down, so + // doing this bad hack to move the cursor and move it back. evalInput will + // move the cursor to where it should be in the end. + // TODO: Consider making motions move the cursor by default, so as to not + // need this ugliness. But it might make visual mode hard. + var curStart = cm.getCursor(); + var repeat = motionArgs.repeat; + cm.moveV(motionArgs.forward ? repeat : (-1 * repeat), 'page'); + var curEnd = cm.getCursor(); + cm.setCursor(curStart); + return curEnd; + }, + moveByWords: function(cm, motionArgs) { + return moveToWord(cm, motionArgs.repeat, !!motionArgs.forward, + !!motionArgs.wordEnd, !!motionArgs.bigWord); + }, + moveTillCharacter: function(cm, motionArgs) { + var repeat = motionArgs.repeat; + var curEnd = moveToCharacter(cm, repeat, motionArgs.forward, + motionArgs.selectedCharacter); + if (motionArgs.forward) { + curEnd.ch--; + } + else { + curEnd.ch++; + } + return curEnd; + }, + moveToCharacter: function(cm, motionArgs) { + var repeat = motionArgs.repeat; + return moveToCharacter(cm, repeat, motionArgs.forward, + motionArgs.selectedCharacter); + }, + moveToEol: function(cm, motionArgs) { + var cursor = cm.getCursor(); + var line = Math.min(cursor.line + motionArgs.repeat - 1, + cm.lineCount()); + return { line: line, ch: cm.getLine(line).length }; + }, + moveToFirstNonWhiteSpaceCharacter: function(cm) { + // Go to the start of the line where the text begins, or the end for + // whitespace-only lines + var cursor = cm.getCursor(); + var line = cm.getLine(cursor.line); + return { line: cursor.line, + ch: findFirstNonWhiteSpaceCharacter(cm.getLine(cursor.line)), + user: true }; + }, + moveToMatchedSymbol: function(cm, motionArgs) { + var cursor = cm.getCursor(); + var symbol = cm.getLine(cursor.line).charAt(cursor.ch); + if (isMatchableSymbol(symbol)) { + return findMatchedSymbol(cm, cm.getCursor(), motionArgs.symbol); + } else { + return cursor; + } + }, + moveToStartOfLine: function(cm) { + var cursor = cm.getCursor(); + return { line: cursor.line, ch: 0 }; + }, + moveToLineOrEdgeOfDocument: function(cm, motionArgs) { + var lineNum = motionArgs.forward ? cm.lineCount() - 1 : 0; + if (motionArgs.repeatIsExplicit) { + lineNum = Math.max(0, Math.min( + motionArgs.repeat - 1, + cm.lineCount() - 1)); + } + return { line: lineNum, + ch: findFirstNonWhiteSpaceCharacter(cm.getLine(lineNum)), + user: true }; + }, + textObjectManipulation: function(cm, motionArgs) { + var character = motionArgs.selectedCharacter; + // Inclusive is the difference between a and i + // TODO: Instead of using the additional text object map to perform text + // object operations, merge the map into the defaultKeyMap and use + // motionArgs to define behavior. Define separate entries for 'aw', + // 'iw', 'a[', 'i[', etc. + var inclusive = !motionArgs.textObjectInner; + if (!textObjects[character]) { + // No text object defined for this, don't move. + return null; + } + var tmp = textObjects[character](cm, inclusive); + var start = tmp.start; + var end = tmp.end; + return [start, end]; + } }; - CodeMirror.keyMap["vim-prefix-d'"][m] = function(cm) { - delTillMark(cm, m); + + var operators = { + change: function(cm, operatorArgs, vim, curStart, curEnd) { + vim.registerController.pushText(operatorArgs.registerName, 'change', + cm.getRange(curStart, curEnd), operatorArgs.linewise); + if (operatorArgs.linewise) { + // Insert an additional newline so that insert mode can start there. + // curEnd should be on the first character of the new line. + cm.replaceRange('\n', curStart, curEnd); + } else { + cm.replaceRange('', curStart, curEnd); + } + cm.setCursor(curStart); + }, + // delete is a javascript keyword. + 'delete': function(cm, operatorArgs, vim, curStart, curEnd) { + vim.registerController.pushText(operatorArgs.registerName, 'delete', + cm.getRange(curStart, curEnd), operatorArgs.linewise); + cm.replaceRange('', curStart, curEnd); + }, + indent: function(cm, operatorArgs, vim, curStart, curEnd) { + var startLine = curStart.line; + var endLine = curEnd.line; + // In visual mode, n> shifts the selection right n times, instead of + // shifting n lines right once. + var repeat = (vim.visualMode) ? operatorArgs.repeat : 1; + if (operatorArgs.linewise) { + // The only way to delete a newline is to delete until the start of + // the next line, so in linewise mode evalInput will include the next + // line. We don't want this in indent, so we go back a line. + endLine--; + } + for (var i = startLine; i <= endLine; i++) { + for (var j = 0; j < repeat; j++) { + cm.indentLine(i, operatorArgs.indentRight); + } + } + cm.setCursor(curStart); + cm.setCursor(motions.moveToFirstNonWhiteSpaceCharacter(cm)); + }, + swapcase: function(cm, operatorArgs, vim, curStart, curEnd) { + var toSwap = cm.getRange(curStart, curEnd); + var swapped = ''; + for (var i = 0; i < toSwap.length; i++) { + var character = toSwap[i]; + swapped += isUpperCase(character) ? character.toLowerCase() : + character.toUpperCase(); + } + cm.replaceRange(swapped, curStart, curEnd); + }, + yank: function(cm, operatorArgs, vim, curStart, curEnd) { + vim.registerController.pushText(operatorArgs.registerName, 'yank', + cm.getRange(curStart, curEnd), operatorArgs.linewise); + } }; - CodeMirror.keyMap["vim-prefix-y'"][m] = function(cm) { - yankTillMark(cm, m); + + var actions = { + enterInsertMode: function(cm) { + cm.setOption('keyMap', 'vim-insert'); + }, + toggleVisualMode: function(cm, actionArgs, vim) { + var repeat = actionArgs.repeat; + var curStart = cm.getCursor(); + var curEnd; + vim.visualLine = !!actionArgs.linewise; + // TODO: The repeat should actually select number of characters/lines + // equal to the repeat times the size of the previous visual + // operation. + if (!vim.visualMode) { + vim.visualMode = true; + if (vim.visualLine) { + curStart.ch = 0; + curEnd = { + line: Math.min(curStart.line + repeat - 1, cm.lineCount()), + ch: lineLength(cm, curStart.line) + }; + } else { + curEnd = { + line: curStart.line, + ch: Math.min(curStart.ch + repeat, + lineLength(cm, curStart.line)) + }; + } + // Make the initial selection. + if (!actionArgs.repeatIsExplicit && !vim.visualLine) { + // This is a strange case. Here the implicit repeat is 1. The + // following commands lets the cursor hover over the 1 character + // selection. + cm.setCursor(curEnd); + cm.setSelection(curEnd, curStart); + } else { + cm.setSelection(curStart, curEnd); + } + } else { + exitVisualMode(cm, vim); + } + }, + joinLines: function(cm, actionArgs, vim) { + if (vim.visualMode) { + var curStart = cm.getCursor('anchor'); + var curEnd = cm.getCursor('head'); + curEnd.ch = lineLength(cm, curEnd.line) - 1; + } else { + // Repeat is the number of lines to join. Minimum 2 lines. + var repeat = Math.max(actionArgs.repeat, 2); + var curStart = cm.getCursor(); + var lineNumEnd = Math.min(curStart.line + repeat - 1, + cm.lineCount() - 1); + var curEnd = { line: lineNumEnd, + ch: lineLength(cm, lineNumEnd) - 1 }; + } + var text = cm.getRange(curStart, curEnd).replace(/\n\s*/g, ' '); + cm.replaceRange(text, curStart, curEnd); + cm.setCursor(curStart); + }, + newLineAndEnterInsertMode: function(cm, actionArgs) { + var insertAt = cm.getCursor(); + insertAt.ch = 0; + insertAt.line = (actionArgs.after) ? insertAt.line + 1 : insertAt.line; + cm.replaceRange('\n', insertAt); + cm.setCursor(insertAt); + this.enterInsertMode(cm); + }, + paste: function(cm, actionArgs, vim) { + var cur = cm.getCursor(); + var register = vim.registerController.getRegister( + actionArgs.registerName); + if (!register.text) { return; } + for (var text = '', i = 0; i < actionArgs.repeat; i++) { + text += register.text; + } + var curChEnd = 0; + var linewise = register.linewise; + if (linewise) { + cur.line += actionArgs.after ? 1 : 0; + cur.ch = 0; + curChEnd = 0; + } else { + cur.ch += actionArgs.after ? 1 : 0; + curChEnd = cur.ch; + } + // Set cursor in the right place to let CodeMirror handle moving it. + cm.setCursor(cur.line, curChEnd); + cm.replaceRange(text, cur); + // Now fine tune the cursor to where we want it. + if (linewise) { + cm.setCursor(cm.getCursor().line - 1, 0); + } + else { + cur = cm.getCursor(); + cm.setCursor(cur.line, cur.ch - 1); + } + }, + undo: function(cm, actionArgs) { + repeatFn(cm, CodeMirror.commands.undo, actionArgs.repeat)(); + }, + redo: function(cm, actionArgs) { + repeatFn(cm, CodeMirror.commands.redo, actionArgs.repeat)(); + }, + setRegister: function(cm, actionArgs, vim) { + vim.inputState.registerName = actionArgs.selectedCharacter; + }, + setMark: function(cm, actionArgs, vim) { + var markName = actionArgs.selectedCharacter; + if (!inArray(markName, validMarks)) { + return; + } + if (vim.marks[markName]) { + vim.marks[markName].clear(); + } + vim.marks[markName] = cm.setBookmark(cm.getCursor()); + }, + replace: function(cm, actionArgs) { + var replaceWith = actionArgs.selectedCharacter; + var curStart = cm.getCursor(); + var line = cm.getLine(curStart.line); + var replaceTo = curStart.ch + actionArgs.repeat; + if (replaceTo > line.length) { + return; + } + var curEnd = { line: curStart.line, ch: replaceTo }; + var replaceWithStr = ''; + for (var i = 0; i < curEnd.ch - curStart.ch; i++) { + replaceWithStr += replaceWith; + } + cm.replaceRange(replaceWithStr, curStart, curEnd); + } }; - CodeMirror.keyMap["vim-prefix-r"][m] = function (cm) { - var cur = cm.getCursor(); - cm.replaceRange(toLetter(m), - {line: cur.line, ch: cur.ch}, - {line: cur.line, ch: cur.ch + 1}); - CodeMirror.commands.goColumnLeft(cm); + + var textObjects = { + // TODO: lots of possible exceptions that can be thrown here. Try da( + // outside of a () block. + // TODO: implement text objects for the reverse like }. Should just be + // an additional mapping after moving to the defaultKeyMap. + 'w': function(cm, inclusive) { + return expandToWord(cm, inclusive, true /** forward */, + false /** bigWord */); + }, + 'W': function(cm, inclusive) { + return expandToWord(cm, inclusive, + true /** forward */, true /** bigWord */); + }, + '{': function(cm, inclusive) { + return selectCompanionObject(cm, '}', inclusive); + }, + '(': function(cm, inclusive) { + return selectCompanionObject(cm, ')', inclusive); + }, + '[': function(cm, inclusive) { + return selectCompanionObject(cm, ']', inclusive); + }, + '\'': function(cm, inclusive) { + return findBeginningAndEnd(cm, "'", inclusive); + }, + '\"': function(cm, inclusive) { + return findBeginningAndEnd(cm, '"', inclusive); + } }; - // all commands, related to motions till char in line - iterObj(MOTION_OPTIONS, function (ch, options) { - CodeMirror.keyMap["vim-prefix-" + ch][m] = function(cm) { - moveTillChar(cm, m, options); - }; - CodeMirror.keyMap["vim-prefix-d" + ch][m] = function(cm) { - delTillChar(cm, m, options); - }; - CodeMirror.keyMap["vim-prefix-c" + ch][m] = function(cm) { - delTillChar(cm, m, options); - enterInsertMode(cm); - }; - }); - } - for (var i = 65; i < 65 + 26; i++) { // uppercase alphabet char codes - var ch = String.fromCharCode(i); - setupPrefixBindingForKey(toCombo(ch)); - setupPrefixBindingForKey(toCombo(ch.toLowerCase())); - } - for (var i = 0; i < SPECIAL_SYMBOLS.length; ++i) { - setupPrefixBindingForKey(toCombo(SPECIAL_SYMBOLS.charAt(i))); - } - setupPrefixBindingForKey("Space"); - - CodeMirror.keyMap["vim-prefix-y"] = { - "Y": countTimes(function(cm, i, last) { - pushInBuffer("\n" + cm.getLine(cm.getCursor().line + i)); - cm.setOption("keyMap", "vim"); - }), - "'": function(cm) {cm.setOption("keyMap", "vim-prefix-y'"); emptyBuffer();}, - nofallthrough: true, style: "fat-cursor" - }; - CodeMirror.keyMap["vim-insert"] = { - // TODO: override navigation keys so that Esc will cancel automatic indentation from o, O, i_ - "Esc": function(cm) { - cm.setCursor(cm.getCursor().line, cm.getCursor().ch-1); - cm.setOption("keyMap", "vim"); - }, - "Ctrl-N": "autocomplete", - "Ctrl-P": "autocomplete", - fallthrough: ["default"] - }; - // Ctrl-[ is synonymous with in vim, and many other places - // http://vimdoc.sourceforge.net/htmldoc/insert.html#i_CTRL-C - // CTRL-C Quit insert mode, go back to Normal mode. - CodeMirror.keyMap["vim-insert"]['Ctrl-C'] = CodeMirror.keyMap["vim-insert"]['Ctrl-['] = CodeMirror.keyMap["vim-insert"]['Esc']; - - function findMatchedSymbol(cm, cur, symb) { - var line = cur.line; - var symb = symb ? symb : cm.getLine(line)[cur.ch]; - - // Are we at the opening or closing char - var forwards = ['(', '[', '{'].indexOf(symb) != -1; - - var reverseSymb = (function(sym) { - switch (sym) { - case '(' : return ')'; - case '[' : return ']'; - case '{' : return '}'; - case ')' : return '('; - case ']' : return '['; - case '}' : return '{'; - default : return null; - } - })(symb); - - // Couldn't find a matching symbol, abort - if (reverseSymb == null) return cur; - - // Tracking our imbalance in open/closing symbols. An opening symbol wii be - // the first thing we pick up if moving forward, this isn't true moving backwards - var disBal = forwards ? 0 : 1; - - while (true) { - if (line == cur.line) { - // First pass, do some special stuff - var currLine = forwards ? cm.getLine(line).substr(cur.ch).split('') : cm.getLine(line).substr(0,cur.ch).split('').reverse(); + // Merge arguments in place, for overriding arguments. + function mergeArgs(to, from) { + for (var prop in from) { + if (from.hasOwnProperty(prop)) { + to[prop] = from[prop]; + } + } + } + function copyArgs(args) { + var ret = {}; + for (var prop in args) { + if (args.hasOwnProperty(prop)) { + ret[prop] = args[prop]; + } + } + return ret; + } + function arrayEq(a1, a2) { + if (a1.length != a2.length) return false; + for (var i = 0; i < a1.length; i++) { + if (a1[i] != a2[i]) { + return false; + } + } + return true; + } + function matchKeysPartial(pressed, mapped) { + for (var i = 0; i < pressed.length; i++) { + // 'character' means any character. For mark, register commads, etc. + if (pressed[i] != mapped[i] && mapped[i] != 'character') { + return false; + } + } + return true; + } + function arrayIsSubsetFromBeginning(small, big) { + for (var i = 0; i < small.length; i++) { + if (small[i] != big[i]) { + return false; + } + } + return true; + } + function repeatFn(cm, fn, repeat) { + return function() { + for (var i = 0; i < repeat; i++) { + fn(cm); + } + }; + } + function copyCursor(cur) { + return { line: cur.line, ch: cur.ch, user: cur.user }; + } + function cursorEqual(cur1, cur2) { + return cur1.ch == cur2.ch && cur1.line == cur2.line; + } + function cursorIsBefore(cur1, cur2) { + if (cur1.line < cur2.line) { + return true; + } else if (cur1.line == cur2.line && cur1.ch < cur2.ch) { + return true; } else { - var currLine = forwards ? cm.getLine(line).split('') : cm.getLine(line).split('').reverse(); + return false; } + } + function lineLength(cm, lineNum) { + return cm.getLine(lineNum).length; + } - for (var index = 0; index < currLine.length; index++) { - if (currLine[index] == symb) disBal++; - else if (currLine[index] == reverseSymb) disBal--; + function exitVisualMode(cm, vim) { + vim.visualMode = false; + vim.visualLine = false; + var selectionStart = cm.getCursor('anchor'); + var selectionEnd = cm.getCursor('head'); + if (!cursorEqual(selectionStart, selectionEnd)) { + // Clear the selection and set the cursor only if the selection has not + // already been cleared. Otherwise we risk moving the cursor somewhere + // it's not supposed to be. + cm.setCursor(selectionEnd); + } + } - if (disBal == 0) { - if (forwards && cur.line == line) return {line: line, ch: index + cur.ch}; - else if (forwards) return {line: line, ch: index}; - else return {line: line, ch: currLine.length - index - 1 }; - } + // Remove any trailing newlines from the selection. For + // example, with the caret at the start of the last word on the line, + // 'dw' should word, but not the newline, while 'w' should advance the + // caret to the first character of the next line. + function clipToLine(cm, curStart, curEnd) { + var selection = cm.getRange(curStart, curEnd); + var lines = selection.split('\n'); + if (lines.length > 1 && isWhiteSpaceString(lines.pop())) { + curEnd.line--; + curEnd.ch = cm.getLine(curEnd.line).length; } + } - if (forwards) line++; - else line--; + // Expand the selection to line ends. + function expandSelectionToLine(cm, curStart, curEnd) { + curStart.ch = 0; + curEnd.ch = 0; + curEnd.line++; } - } - function selectCompanionObject(cm, revSymb, inclusive) { - var cur = cm.getCursor(); + function findFirstNonWhiteSpaceCharacter(text) { + var firstNonWS = text.search(/\S/); + return firstNonWS == -1 ? text.length : firstNonWS; + } - var end = findMatchedSymbol(cm, cur, revSymb); - var start = findMatchedSymbol(cm, end); - start.ch += inclusive ? 1 : 0; - end.ch += inclusive ? 0 : 1; + function expandToWord(cm, inclusive, forward, bigWord) { + var cur = cm.getCursor(); + var line = cm.getLine(cur.line); - return {start: start, end: end}; - } + var line_to_char = new String(line.substring(0, cur.ch)); + // TODO: Case when small word is matching symbols does not work right with + // the current regexLastIndexOf check. + var start = regexLastIndexOf(line_to_char, + (!bigWord) ? /[^a-zA-Z0-9]/ : /\s/) + 1; + var end = motions.moveByWords(cm, { repeat: 1, forward: true, + wordEnd: true, bigWord: bigWord }); + end.ch += inclusive ? 1 : 0 ; + return {start: {line: cur.line, ch: start}, end: end }; + } - // takes in a symbol and a cursor and tries to simulate text objects that have - // identical opening and closing symbols - // TODO support across multiple lines - function findBeginningAndEnd(cm, symb, inclusive) { - var cur = cm.getCursor(); - var line = cm.getLine(cur.line); - var chars = line.split(''); - var start = undefined; - var end = undefined; - var firstIndex = chars.indexOf(symb); + /* + * Returns the boundaries of the next word. If the cursor in the middle of the + * word, then returns the boundaries of the current word, starting at the + * cursor. If the cursor is at the start/end of a word, and we are going + * forward/backward, respectively, find the boundaries of the next word. + * + * @param {CodeMirror} cm CodeMirror object. + * @param {Cursor} cur The cursor position. + * @param {boolean} forward True to search forward. False to search backward. + * @param {boolean} bigWord True if punctuation count as part of the word. + * False if only [a-zA-Z0-9] characters count as part of the word. + * @return {Object{from:number, to:number, line: number}} The boundaries of + * the word, or null if there are no more words. + */ + // TODO: Treat empty lines (with no whitespace) as words. + function findWord(cm, cur, forward, bigWord) { + var lineNum = cur.line; + var pos = cur.ch; + var line = cm.getLine(lineNum); + var dir = forward ? 1 : -1; + var regexps = bigWord ? bigWordRegexp : wordRegexp; - // the decision tree is to always look backwards for the beginning first, - // but if the cursor is in front of the first instance of the symb, - // then move the cursor forward - if (cur.ch < firstIndex) { - cur.ch = firstIndex; - cm.setCursor(cur.line, firstIndex+1); + while (true) { + var stop = (dir > 0) ? line.length : -1; + var wordStart = stop, wordEnd = stop; + // Find bounds of next word. + while (pos != stop) { + var foundWord = false; + for (var i = 0; i < regexps.length && !foundWord; ++i) { + if (regexps[i].test(line.charAt(pos))) { + wordStart = pos; + // Advance to end of word. + while (pos != stop && regexps[i].test(line.charAt(pos))) { + pos += dir; + } + wordEnd = pos; + foundWord = wordStart != wordEnd; + if (wordStart == cur.ch && lineNum == cur.line && + wordEnd == wordStart + dir) { + // We started at the end of a word. Find the next one. + continue; + } else { + return { + from: Math.min(wordStart, wordEnd + 1), + to: Math.max(wordStart, wordEnd), + line: lineNum}; + } + } + } + if (!foundWord) { + pos += dir; + } + } + // Advance to next/prev line. + lineNum += dir; + if (!isLine(cm, lineNum)) { + return null; + } + line = cm.getLine(lineNum); + pos = (dir > 0) ? 0 : line.length; + } + // Should never get here. + throw 'The impossible happened.'; } - // otherwise if the cursor is currently on the closing symbol - else if (firstIndex < cur.ch && chars[cur.ch] == symb) { - end = cur.ch; // assign end to the current cursor - --cur.ch; // make sure to look backwards + + /** + * @param {CodeMirror} cm CodeMirror object. + * @param {int} repeat Number of words to move past. + * @param {boolean} forward True to search forward. False to search backward. + * @param {boolean} wordEnd True to move to end of word. False to move to + * beginning of word. + * @param {boolean} bigWord True if punctuation count as part of the word. + * False if only alphabet characters count as part of the word. + * @return {Cursor} The position the cursor should move to. + */ + function moveToWord(cm, repeat, forward, wordEnd, bigWord) { + var cur = cm.getCursor(); + for (var i = 0; i < repeat; i++) { + var startCh = cur.ch, startLine = cur.line, word; + var movedToNextWord = false; + while (!movedToNextWord) { + // Search and advance. + word = findWord(cm, cur, forward, bigWord); + movedToNextWord = true; + if (word) { + // Move to the word we just found. If by moving to the word we end up + // in the same spot, then move an extra character and search again. + cur.line = word.line; + if (forward && wordEnd) { + // 'e' + cur.ch = word.to - 1; + } else if (forward && !wordEnd) { + // 'w' + if (inRangeInclusive(cur.ch, word.from, word.to) && + word.line == startLine) { + // Still on the same word. Go to the next one. + movedToNextWord = false; + cur.ch = word.to - 1; + } else { + cur.ch = word.from; + } + } else if (!forward && wordEnd) { + // 'ge' + if (inRangeInclusive(cur.ch, word.from, word.to) && + word.line == startLine) { + // still on the same word. Go to the next one. + movedToNextWord = false; + cur.ch = word.from; + } else { + cur.ch = word.to; + } + } else if (!forward && !wordEnd) { + // 'b' + cur.ch = word.from; + } + } else { + // No more words to be found. Move to end of line. + return { line: cur.line, ch: lineLength(cm, cur.line) }; + } + } + } + return cur; } - // if we're currently on the symbol, we've got a start - if (chars[cur.ch] == symb && end == null) - start = cur.ch + 1; // assign start to ahead of the cursor - else { - // go backwards to find the start - for (var i = cur.ch; i > -1 && start == null; i--) - if (chars[i] == symb) start = i + 1; + function moveToCharacter(cm, repeat, forward, character) { + var cur = cm.getCursor(); + var start = cur.ch; + for (var i = 0; i < repeat; i ++) { + var line = cm.getLine(cur.line); + var idx = charIdxInLine(start, line, character, forward, true); + if (idx == -1) { + return cur; + } + start = idx; + } + return { line: cm.getCursor().line, + ch: idx }; } - // look forwards for the end symbol - if (start != null && end == null) { - for (var i = start, len = chars.length; i < len && end == null; i++) { - if (chars[i] == symb) end = i; + function charIdxInLine(start, line, character, forward, includeChar) { + // Search for char in line. + // motion_options: {forward, includeChar} + // If includeChar = true, include it too. + // If forward = true, search forward, else search backwards. + // If char is not found on this line, do nothing + var idx; + if (forward) { + idx = line.indexOf(character, start + 1); + if (idx != -1 && !includeChar) { + idx -= 1; + } + } else { + idx = line.lastIndexOf(character, start - 1); + if (idx != -1 && !includeChar) { + idx += 1; + } } + return idx; } - // nothing found - // FIXME still enters insert mode - if (start == null || end == null) return { - start: cur, end: cur - }; + function findMatchedSymbol(cm, cur, symb) { + var line = cur.line; + symb = symb ? symb : cm.getLine(line)[cur.ch]; + + // Are we at the opening or closing char + var forwards = (['(', '[', '{'].indexOf(symb) != -1); + + var reverseSymb = (function(sym) { + switch (sym) { + case '(' : return ')'; + case '[' : return ']'; + case '{' : return '}'; + case ')' : return '('; + case ']' : return '['; + case '}' : return '{'; + default : return null; + } + })(symb); - // include the symbols - if (inclusive) { - --start; ++end; - } + // Couldn't find a matching symbol, abort + if (!reverseSymb) return cur; - return { - start: {line: cur.line, ch: start}, - end: {line: cur.line, ch: end} - }; - } - - function offsetCursor(cm, line, ch) { - var cur = cm.getCursor(); return {line: cur.line + line, ch: cur.ch + ch}; - } - - // These are the motion commands we use for navigation and selection with - // certain other commands. All should return a cursor object. - var motions = { - "J": function(cm, times) { return offsetCursor(cm, times, 0); }, - "Down": function(cm, times) { return offsetCursor(cm, times, 0); }, - "K": function(cm, times) { return offsetCursor(cm, -times, 0); }, - "Up": function(cm, times) { return offsetCursor(cm, -times, 0); }, - "L": function(cm, times) { return offsetCursor(cm, 0, times); }, - "Right": function(cm, times) { return offsetCursor(cm, 0, times); }, - "Space": function(cm, times) { return offsetCursor(cm, 0, times); }, - "H": function(cm, times) { return offsetCursor(cm, 0, -times); }, - "Left": function(cm, times) { return offsetCursor(cm, 0, -times); }, - "Backspace": function(cm, times) { return offsetCursor(cm, 0, -times); }, - "B": function(cm, times, yank) { return moveToWord(cm, word, -1, times, 'start', yank); }, - "Shift-B": function(cm, times, yank) { return moveToWord(cm, bigWord, -1, times, 'start', yank); }, - "E": function(cm, times, yank) { return moveToWord(cm, word, 1, times, 'end', yank); }, - "Shift-E": function(cm, times, yank) { return moveToWord(cm, bigWord, 1, times, 'end', yank); }, - "W": function(cm, times, yank) { return moveToWord(cm, word, 1, times, 'start', yank); }, - "Shift-W": function(cm, times, yank) { return moveToWord(cm, bigWord, 1, times, 'start', yank); }, - "'^'": function(cm, times) { - var cur = cm.getCursor(), line = cm.getLine(cur.line).split(''); - for (var i = 0; i < line.length; i++) { - if (line[i].match(/[^\s]/)) return {line: cur.line, ch: index}; + // Tracking our imbalance in open/closing symbols. An opening symbol will + // be the first thing we pick up if moving forward, this isn't true moving + // backwards + var disBal = forwards ? 0 : 1; + + var currLine; + while (true) { + if (line == cur.line) { + // First pass, do some special stuff + currLine = forwards ? cm.getLine(line).substr(cur.ch).split('') : + cm.getLine(line).substr(0,cur.ch).split('').reverse(); + } else { + currLine = forwards ? cm.getLine(line).split('') : + cm.getLine(line).split('').reverse(); + } + + for (var index = 0; index < currLine.length; index++) { + if (currLine[index] == symb) { + disBal++; + } else if (currLine[index] == reverseSymb) { + disBal--; + } + + if (disBal === 0) { + if (forwards && cur.line == line) { + return { line: line, ch: index + cur.ch}; + } else if (forwards) { + return { line: line, ch: index}; + } else { + return {line: line, ch: currLine.length - index - 1 }; + } + } + } + + if (forwards) { + line++; + } else { + line--; + } } return cur; - }, - "'$'": function(cm) { - var cur = cm.getCursor(), ch = cm.getLine(cur.line).length; - return {line: cur.line, ch: ch}; - }, - "'%'": function(cm) { return findMatchedSymbol(cm, cm.getCursor()); }, - "Esc" : function(cm) { cm.setOption("keyMap", "vim"); repeatCount = 0; return cm.getCursor(); } - }; + } - // Map our movement actions each operator and non-operational movement - iterObj(motions, function(key, motion) { - CodeMirror.keyMap['vim-prefix-d'][key] = function(cm) { - // Get our selected range - var start = cm.getCursor(); - var end = motion(cm, repeatCount ? repeatCount : 1, true); + function selectCompanionObject(cm, revSymb, inclusive) { + var cur = cm.getCursor(); - // Set swap var if range is of negative length - if ((start.line > end.line) || (start.line == end.line && start.ch > end.ch)) var swap = true; + var end = findMatchedSymbol(cm, cur, revSymb); + var start = findMatchedSymbol(cm, end); + start.ch += inclusive ? 1 : 0; + end.ch += inclusive ? 0 : 1; - // Take action, switching start and end if swap var is set - pushInBuffer(cm.getRange(swap ? end : start, swap ? start : end)); - cm.replaceRange("", swap ? end : start, swap ? start : end); + return {start: start, end: end}; + } - // And clean up - repeatCount = 0; - cm.setOption("keyMap", "vim"); - }; + function regexLastIndexOf(string, pattern, startIndex) { + for (var i = !startIndex ? string.length : startIndex; + i >= 0; --i) { + if (pattern.test(string.charAt(i))) { + return i; + } + } + return -1; + } - CodeMirror.keyMap['vim-prefix-c'][key] = function(cm) { - var start = cm.getCursor(); - var end = motion(cm, repeatCount ? repeatCount : 1, true); + // takes in a symbol and a cursor and tries to simulate text objects that have + // identical opening and closing symbols + // TODO support across multiple lines + function findBeginningAndEnd(cm, symb, inclusive) { + var cur = cm.getCursor(); + var line = cm.getLine(cur.line); + var chars = line.split(''); + var start = undefined; + var end = undefined; + var firstIndex = chars.indexOf(symb); + + // the decision tree is to always look backwards for the beginning first, + // but if the cursor is in front of the first instance of the symb, + // then move the cursor forward + if (cur.ch < firstIndex) { + cur.ch = firstIndex; + // Why is this line even here??? + // cm.setCursor(cur.line, firstIndex+1); + } + // otherwise if the cursor is currently on the closing symbol + else if (firstIndex < cur.ch && chars[cur.ch] == symb) { + end = cur.ch; // assign end to the current cursor + --cur.ch; // make sure to look backwards + } - if ((start.line > end.line) || (start.line == end.line && start.ch > end.ch)) var swap = true; - pushInBuffer(cm.getRange(swap ? end : start, swap ? start : end)); - cm.replaceRange("", swap ? end : start, swap ? start : end); + // if we're currently on the symbol, we've got a start + if (chars[cur.ch] == symb && !end) { + start = cur.ch + 1; // assign start to ahead of the cursor + } else { + // go backwards to find the start + for (var i = cur.ch; i > -1 && !start; i--) { + if (chars[i] == symb) { + start = i + 1; + } + } + } - repeatCount = 0; - cm.setOption('keyMap', 'vim-insert'); - }; + // look forwards for the end symbol + if (start && !end) { + for (var i = start, len = chars.length; i < len && !end; i++) { + if (chars[i] == symb) { + end = i; + } + } + } - CodeMirror.keyMap['vim-prefix-y'][key] = function(cm) { - var start = cm.getCursor(); - var end = motion(cm, repeatCount ? repeatCount : 1, true); + // nothing found + // FIXME still enters insert mode + if (!start || !end) return { + start: cur, end: cur + }; - if ((start.line > end.line) || (start.line == end.line && start.ch > end.ch)) var swap = true; - pushInBuffer(cm.getRange(swap ? end : start, swap ? start : end)); + // include the symbols + if (inclusive) { + --start; ++end; + } - repeatCount = 0; - cm.setOption("keyMap", "vim"); - }; + return { + start: {line: cur.line, ch: start}, + end: {line: cur.line, ch: end} + }; + } - CodeMirror.keyMap['vim'][key] = function(cm) { - var cur = motion(cm, repeatCount ? repeatCount : 1); - cm.setCursor(cur.line, cur.ch); + function buildVimKeyMap() { + /** + * Handle the raw key event from CodeMirror. Translate the + * Shift + key modifier to the resulting letter, while preserving other + * modifers. + */ + // TODO: Figure out a way to catch capslock. + function handleKeyEvent_(cm, key, modifier) { + if (isUpperCase(key)) { + // Convert to lower case if shift is not the modifier since the key + // we get from CodeMirror is always upper case. + if (modifier == 'Shift') { + modifier = null; + } + else { + key = key.toLowerCase(); + } + } + if (modifier) { + // Vim will parse modifier+key combination as a single key. + key = modifier + '-' + key; + } + vim.handleKey(cm, key); + } - repeatCount = 0; - }; - }); - - function addCountBindings(keyMapName) { - // Add bindings for number keys - keyMap = CodeMirror.keyMap[keyMapName]; - keyMap["0"] = function(cm) { - if (repeatCount > 0) { - pushRepeatCountDigit(0)(cm); - } else { - CodeMirror.commands.goLineStart(cm); + // Closure to bind CodeMirror, key, modifier. + function keyMapper(key, modifier) { + return function(cm) { + handleKeyEvent_(cm, key, modifier); + }; } - }; - for (var i = 1; i < 10; ++i) { - keyMap[i] = pushRepeatCountDigit(i); - } - } - addCountBindings('vim'); - addCountBindings('vim-prefix-d'); - addCountBindings('vim-prefix-y'); - addCountBindings('vim-prefix-c'); - - // Create our keymaps for each operator and make xa and xi where x is an operator - // change to the corrosponding keymap - var operators = ['d', 'y', 'c']; - iterList(operators, function(key, index, array) { - CodeMirror.keyMap['vim-prefix-'+key+'a'] = { - auto: 'vim', nofallthrough: true, style: "fat-cursor" - }; - CodeMirror.keyMap['vim-prefix-'+key+'i'] = { - auto: 'vim', nofallthrough: true, style: "fat-cursor" - }; - CodeMirror.keyMap['vim-prefix-'+key]['A'] = function(cm) { - repeatCount = 0; - cm.setOption('keyMap', 'vim-prefix-' + key + 'a'); - }; + var modifiers = ['Shift', 'Ctrl']; + var keyMap = { + 'nofallthrough': true, + 'style': 'fat-cursor' + }; + function bindKeys(keys, modifier) { + for (var i = 0; i < keys.length; i++) { + var key = keys[i]; + if (!modifier && inArray(key, specialSymbols)) { + // Wrap special symbols with '' because that's how CodeMirror binds + // them. + key = "'" + key + "'"; + } + if (modifier) { + keyMap[modifier + '-' + key] = keyMapper(keys[i], modifier); + } else { + keyMap[key] = keyMapper(keys[i]); + } + } + } + bindKeys(upperCaseAlphabet); + bindKeys(upperCaseAlphabet, 'Shift'); + bindKeys(upperCaseAlphabet, 'Ctrl'); + bindKeys(specialSymbols); + bindKeys(specialSymbols, 'Ctrl'); + bindKeys(numbers); + bindKeys(numbers, 'Ctrl'); + bindKeys(specialKeys); + bindKeys(specialKeys, 'Ctrl'); + return keyMap; + } + CodeMirror.keyMap.vim = buildVimKeyMap(); - CodeMirror.keyMap['vim-prefix-'+key]['I'] = function(cm) { - repeatCount = 0; - cm.setOption('keyMap', 'vim-prefix-' + key + 'i'); - }; - }); - - function regexLastIndexOf(string, pattern, startIndex) { - for (var i = startIndex == null ? string.length : startIndex; i >= 0; --i) - if (pattern.test(string.charAt(i))) return i; - return -1; - } - - // Create our text object functions. They work similar to motions but they - // return a start cursor as well - var textObjectList = ['W', 'Shift-[', 'Shift-9', '[', "'", "Shift-'"]; - var textObjects = { - 'W': function(cm, inclusive) { - var cur = cm.getCursor(); - var line = cm.getLine(cur.line); + function exitInsertMode(cm) { + cm.setCursor(cm.getCursor().line, cm.getCursor().ch-1, true); + cm.setOption('keyMap', 'vim'); + } - var line_to_char = new String(line.substring(0, cur.ch)); - var start = regexLastIndexOf(line_to_char, /[^a-zA-Z0-9]/) + 1; - var end = motions["E"](cm, 1) ; + CodeMirror.keyMap['vim-insert'] = { + // TODO: override navigation keys so that Esc will cancel automatic + // indentation from o, O, i_ + 'Esc': exitInsertMode, + 'Ctrl-[': exitInsertMode, + 'Ctrl-C': exitInsertMode, + 'Ctrl-N': 'autocomplete', + 'Ctrl-P': 'autocomplete', + fallthrough: ['default'] + }; - end.ch += inclusive ? 1 : 0 ; - return {start: {line: cur.line, ch: start}, end: end }; - }, - 'Shift-[': function(cm, inclusive) { return selectCompanionObject(cm, '}', inclusive); }, - 'Shift-9': function(cm, inclusive) { return selectCompanionObject(cm, ')', inclusive); }, - '[': function(cm, inclusive) { return selectCompanionObject(cm, ']', inclusive); }, - "'": function(cm, inclusive) { return findBeginningAndEnd(cm, "'", inclusive); }, - "Shift-'": function(cm, inclusive) { return findBeginningAndEnd(cm, '"', inclusive); } + return vimApi; }; - - // One function to handle all operation upon text objects. Kinda funky but it works - // better than rewriting this code six times - function textObjectManipulation(cm, object, remove, insert, inclusive) { - // Object is the text object, delete object if remove is true, enter insert - // mode if insert is true, inclusive is the difference between a and i - var tmp = textObjects[object](cm, inclusive); - var start = tmp.start; - var end = tmp.end; - - if ((start.line > end.line) || (start.line == end.line && start.ch > end.ch)) var swap = true ; - - pushInBuffer(cm.getRange(swap ? end : start, swap ? start : end)); - if (remove) cm.replaceRange("", swap ? end : start, swap ? start : end); - if (insert) cm.setOption('keyMap', 'vim-insert'); - } - - // And finally build the keymaps up from the text objects - for (var i = 0; i < textObjectList.length; ++i) { - var object = textObjectList[i]; - (function(object) { - CodeMirror.keyMap['vim-prefix-di'][object] = function(cm) { textObjectManipulation(cm, object, true, false, false); }; - CodeMirror.keyMap['vim-prefix-da'][object] = function(cm) { textObjectManipulation(cm, object, true, false, true); }; - CodeMirror.keyMap['vim-prefix-yi'][object] = function(cm) { textObjectManipulation(cm, object, false, false, false); }; - CodeMirror.keyMap['vim-prefix-ya'][object] = function(cm) { textObjectManipulation(cm, object, false, false, true); }; - CodeMirror.keyMap['vim-prefix-ci'][object] = function(cm) { textObjectManipulation(cm, object, true, true, false); }; - CodeMirror.keyMap['vim-prefix-ca'][object] = function(cm) { textObjectManipulation(cm, object, true, true, true); }; - })(object) - } -})(); + // Initialize Vim and make it available as an API. + var vim = Vim(); + CodeMirror.Vim = vim; +} +)(); diff --git a/test/index.html b/test/index.html index 7b48facedd..7261ec001d 100644 --- a/test/index.html +++ b/test/index.html @@ -10,6 +10,7 @@ + @@ -328,7 +329,8 @@

    CodeMirror: Markdown mode

    var editor = CodeMirror.fromTextArea(document.getElementById("code"), { mode: 'markdown', lineNumbers: true, - theme: "default" + theme: "default", + extraKeys: {"Enter": "newlineAndIndentContinueMarkdownList"} }); From 8c349de6caa3a6db4ed378aad5220d8b0ee37ed8 Mon Sep 17 00:00:00 2001 From: Marijn Haverbeke Date: Mon, 3 Dec 2012 11:39:00 +0100 Subject: [PATCH 0420/5780] Add Graphit to real-world uses --- doc/realworld.html | 1 + 1 file changed, 1 insertion(+) diff --git a/doc/realworld.html b/doc/realworld.html index b5d2daa98f..2f0ab91634 100644 --- a/doc/realworld.html +++ b/doc/realworld.html @@ -42,6 +42,7 @@

    { } CodeMi
  • Go language tour
  • GitHub's Android app
  • Google Apps Script
  • +
  • Graphit (function graphing)
  • Haxe (Haxe Playground)
  • HaxPad (editor for Win RT)
  • Histone template engine playground
  • From 6361b3a9d6a6102bb10125f8e7da721c97a282f2 Mon Sep 17 00:00:00 2001 From: Marijn Haverbeke Date: Mon, 3 Dec 2012 11:41:22 +0100 Subject: [PATCH 0421/5780] Remove unused variable --- lib/util/continuelist.js | 1 - 1 file changed, 1 deletion(-) diff --git a/lib/util/continuelist.js b/lib/util/continuelist.js index 8a2b8653e2..33b343bb53 100644 --- a/lib/util/continuelist.js +++ b/lib/util/continuelist.js @@ -1,7 +1,6 @@ (function() { CodeMirror.commands.newlineAndIndentContinueMarkdownList = function(cm) { var pos = cm.getCursor(), token = cm.getTokenAt(pos); - var mode = CodeMirror.innerMode(cm.getMode(), token.state).mode; var space; if (token.className == "string") { var full = cm.getRange({line: pos.line, ch: 0}, {line: pos.line, ch: token.end}); From b7c968bdd5fdfb40998c2e8ef86512c110f58355 Mon Sep 17 00:00:00 2001 From: Marijn Haverbeke Date: Mon, 3 Dec 2012 15:17:37 +0100 Subject: [PATCH 0422/5780] In mouse handler, don't assume document doesn't change between mousedown and mouseup --- lib/codemirror.js | 11 ++++++++--- 1 file changed, 8 insertions(+), 3 deletions(-) diff --git a/lib/codemirror.js b/lib/codemirror.js index 9ab41a86f6..73c9a2867e 100644 --- a/lib/codemirror.js +++ b/lib/codemirror.js @@ -1398,14 +1398,19 @@ window.CodeMirror = (function() { return; } e_preventDefault(e); - if (type == "single") extendSelection(cm, start); + if (type == "single") extendSelection(cm, clipPos(doc, start)); var startstart = sel.from, startend = sel.to; function doSelect(cur) { if (type == "single") { - extendSelection(cm, start, cur); - } else if (type == "double") { + extendSelection(cm, clipPos(doc, start), cur); + return; + } + + startstart = clipPos(doc, startstart); + startend = clipPos(doc, startend); + if (type == "double") { var word = findWordAt(getLine(doc, cur.line).text, cur); if (posLess(cur, startstart)) extendSelection(cm, word.from, startend); else extendSelection(cm, startstart, word.to); From 994b28e83df5a185ad1010f53e0f56979d489727 Mon Sep 17 00:00:00 2001 From: Yunchi Luo Date: Mon, 3 Dec 2012 21:03:39 -0500 Subject: [PATCH 0423/5780] Clean up to meet JSHint compliance. Various other style fixes. Updates to comments. --- keymap/vim.js | 126 +++++++++++++++++++++++++++----------------------- 1 file changed, 69 insertions(+), 57 deletions(-) diff --git a/keymap/vim.js b/keymap/vim.js index d97caae9f5..cf8a1a02b8 100644 --- a/keymap/vim.js +++ b/keymap/vim.js @@ -1,5 +1,3 @@ -// Reimplementation of vim keybindings -// word1 /** * Supported keybindings: * @@ -50,7 +48,6 @@ * 6. Motion, operator, and action implementations * 7. Helper functions for the key handler, motions, operators, and actions * 8. Set up Vim to work as a keymap for CodeMirror. - * */ (function() { @@ -285,7 +282,9 @@ } function inArray(val, arr) { for (var i = 0; i < arr.length; i++) { - if (arr[i] == val) { return true; } + if (arr[i] == val) { + return true; + } } return false; } @@ -640,7 +639,7 @@ var forward = command.searchArgs.forward; getSearchState(cm).reversed = !forward; var promptPrefix = (forward) ? '/' : '?'; - var handleQuery = function(query) { + function handleQuery(query) { updateSearchQuery(cm, query); commandDispatcher.processMotion(cm, vim, { type: 'motion', @@ -795,8 +794,8 @@ // All of the functions below return Cursor objects. var motions = { expandToLine: function(cm, motionArgs) { - // Expands forward to end of line, and then to next line if repeat is > 1. - // Does not handle backward motion! + // Expands forward to end of line, and then to next line if repeat is + // >1. Does not handle backward motion! var cur = cm.getCursor(); return clipCursorToContent(cm, { line: cur.line + motionArgs.repeat - 1, ch: Infinity }); @@ -843,10 +842,8 @@ }, moveByPage: function(cm, motionArgs) { // CodeMirror only exposes functions that move the cursor page down, so - // doing this bad hack to move the cursor and move it back. evalInput will - // move the cursor to where it should be in the end. - // TODO: Consider making motions move the cursor by default, so as to not - // need this ugliness. But it might make visual mode hard. + // doing this bad hack to move the cursor and move it back. evalInput + // will move the cursor to where it should be in the end. var curStart = cm.getCursor(); var repeat = motionArgs.repeat; cm.moveV((motionArgs.forward ? repeat : -repeat), 'page'); @@ -889,7 +886,7 @@ var cursor = cm.getCursor(); var line = cm.getLine(cursor.line); return { line: cursor.line, - ch: findFirstNonWhiteSpaceCharacter(cm.getLine(cursor.line)) }; + ch: findFirstNonWhiteSpaceCharacter(cm.getLine(cursor.line)) }; }, moveToMatchedSymbol: function(cm, motionArgs) { var cursor = cm.getCursor(); @@ -1031,8 +1028,8 @@ }); } else { curEnd = clipCursorToContent(cm, { - line: curStart.line, - ch: curStart.ch + repeat + line: curStart.line, + ch: curStart.ch + repeat }); } // Make the initial selection. @@ -1060,7 +1057,7 @@ var repeat = Math.max(actionArgs.repeat, 2); curStart = cm.getCursor(); curEnd = clipCursorToContent(cm, { line: curStart.line + repeat - 1, - ch: Infinity }); + ch: Infinity }); } var finalCh = 0; cm.operation(function() { @@ -1078,9 +1075,9 @@ }, newLineAndEnterInsertMode: function(cm, actionArgs) { var insertAt = cm.getCursor(); - if (insertAt.line == 0 && !actionArgs.after) { + if (insertAt.line === 0 && !actionArgs.after) { // Special case for inserting newline before start of document. - cm.replaceRange('\n', makeCursor(0, 0)); + cm.replaceRange('\n', { line: 0, ch: 0 }); cm.setCursor(0, 0); } else { insertAt.line = (actionArgs.after) ? insertAt.line : @@ -1097,7 +1094,9 @@ var cur = cm.getCursor(); var register = vim.registerController.getRegister( actionArgs.registerName); - if (!register.text) { return; } + if (!register.text) { + return; + } for (var text = '', i = 0; i < actionArgs.repeat; i++) { text += register.text; } @@ -1233,7 +1232,9 @@ return { line: cur.line + offsetLine, ch: cur.ch + offsetCh }; } function arrayEq(a1, a2) { - if (a1.length != a2.length) return false; + if (a1.length != a2.length) { + return false; + } for (var i = 0; i < a1.length; i++) { if (a1[i] != a2[i]) { return false; @@ -1317,8 +1318,8 @@ } function findFirstNonWhiteSpaceCharacter(text) { - var firstNonWS = text.search(/\S/); - return firstNonWS == -1 ? text.length : firstNonWS; + var firstNonWS = text.search(/\S/); + return firstNonWS == -1 ? text.length : firstNonWS; } function expandToWord(cm, inclusive, forward, bigWord) { @@ -1337,14 +1338,15 @@ } /* - * Returns the boundaries of the next word. If the cursor in the middle of the - * word, then returns the boundaries of the current word, starting at the - * cursor. If the cursor is at the start/end of a word, and we are going + * Returns the boundaries of the next word. If the cursor in the middle of + * the word, then returns the boundaries of the current word, starting at + * the cursor. If the cursor is at the start/end of a word, and we are going * forward/backward, respectively, find the boundaries of the next word. * * @param {CodeMirror} cm CodeMirror object. * @param {Cursor} cur The cursor position. - * @param {boolean} forward True to search forward. False to search backward. + * @param {boolean} forward True to search forward. False to search + * backward. * @param {boolean} bigWord True if punctuation count as part of the word. * False if only [a-zA-Z0-9] characters count as part of the word. * @return {Object{from:number, to:number, line: number}} The boundaries of @@ -1379,9 +1381,9 @@ continue; } else { return { - from: Math.min(wordStart, wordEnd + 1), - to: Math.max(wordStart, wordEnd), - line: lineNum}; + from: Math.min(wordStart, wordEnd + 1), + to: Math.max(wordStart, wordEnd), + line: lineNum }; } } } @@ -1404,7 +1406,8 @@ /** * @param {CodeMirror} cm CodeMirror object. * @param {int} repeat Number of words to move past. - * @param {boolean} forward True to search forward. False to search backward. + * @param {boolean} forward True to search forward. False to search + * backward. * @param {boolean} wordEnd True to move to end of word. False to move to * beginning of word. * @param {boolean} bigWord True if punctuation count as part of the word. @@ -1421,8 +1424,9 @@ word = findWord(cm, cur, forward, bigWord); movedToNextWord = true; if (word) { - // Move to the word we just found. If by moving to the word we end up - // in the same spot, then move an extra character and search again. + // Move to the word we just found. If by moving to the word we end + // up in the same spot, then move an extra character and search + // again. cur.line = word.line; if (forward && wordEnd) { // 'e' @@ -1526,8 +1530,8 @@ var depth = 1, nextCh = symb, index = cur.ch, lineText = cm.getLine(line); // Simple search for closing paren--just count openings and closings till // we find our match - // TODO: use info from CodeMirror to ignore closing brackets in comments and - // quotes, etc. + // TODO: use info from CodeMirror to ignore closing brackets in comments + // and quotes, etc. while (nextCh && depth > 0) { index += increment; nextCh = lineText.charAt(index); @@ -1538,9 +1542,9 @@ nextCh = lineText.charAt(index); } if (nextCh === symb) { - depth++; + depth++; } else if (nextCh === reverseSymb) { - depth--; + depth--; } } @@ -1571,8 +1575,8 @@ return -1; } - // takes in a symbol and a cursor and tries to simulate text objects that have - // identical opening and closing symbols + // Takes in a symbol and a cursor and tries to simulate text objects that + // have identical opening and closing symbols // TODO support across multiple lines function findBeginningAndEnd(cm, symb, inclusive) { var cur = cm.getCursor(); @@ -1617,10 +1621,9 @@ } // nothing found - // FIXME still enters insert mode - if (!start || !end) return { - start: cur, end: cur - }; + if (!start || !end) { + return { start: cur, end: cur }; + } // include the symbols if (inclusive) { @@ -1653,26 +1656,25 @@ } function parseQuery(cm, query) { // First try to extract regex + flags from the input. If no flags found, - // extract just the regex. IE does not accept flags directly defined in the - // regex string in the form /regex/flags + // extract just the regex. IE does not accept flags directly defined in + // the regex string in the form /regex/flags var match = query.match(/^(.*)\/(.*)$/); var insensitive = false; - var regexp; + var query_regex; if (match) { insensitive = (match[2].indexOf('i') != -1); - regexp = match[1]; + query_regex = match[1]; } else { - regexp = query; + query_regex = query; } // Heuristic: if the query string is all lowercase, do a case-insensitive // search. - insensitive |= (/^[^A-Z]*$/).test(regexp); - var query; + insensitive = insensitive || (/^[^A-Z]*$/).test(query_regex); try { - query = new RegExp(regexp, insensitive ? 'i' : undefined); - return query; + var regexp = new RegExp(query_regex, insensitive ? 'i' : undefined); + return regexp; } catch (e) { - showConfirm(cm, 'Invalid regex: ' + regexp); + showConfirm(cm, 'Invalid regex: ' + query_regex); } } function showConfirm(cm, text) { @@ -1693,7 +1695,7 @@ if (desc) { raw += ''; raw += desc; - raw += '' + raw += ''; } return raw; } @@ -1718,16 +1720,24 @@ function updateSearchQuery(cm, query) { cm.operation(function() { var state = getSearchState(cm); - if (!query) return; + if (!query) { + return; + } var newQuery = parseQuery(cm, query); - if (regexEqual(newQuery, state.query)) return; + if (regexEqual(newQuery, state.query)) { + return; + } clearSearch(cm); state.query = newQuery; - if (!state.query) return; + if (!state.query) { + return; + } if (cm.lineCount() < 2000) { // This is too expensive on big documents. - for (var cursor = cm.getSearchCursor(state.query); cursor.findNext();) + for (var cursor = cm.getSearchCursor(state.query); + cursor.findNext();) { state.marked.push(cm.markText(cursor.from(), cursor.to(), 'CodeMirror-searching')); + } } }); } @@ -1750,7 +1760,9 @@ // around and try again. cursor = cm.getSearchCursor(state.query, (prev) ? { line: cm.lineCount() - 1} : {line: 0, ch: 0} ); - if (!cursor.find(prev)) return; + if (!cursor.find(prev)) { + return; + } } } return cursor.from(); From bfd2465baffceb9cdc944a278205b704f50dc4e7 Mon Sep 17 00:00:00 2001 From: Yunchi Luo Date: Mon, 3 Dec 2012 22:26:34 -0500 Subject: [PATCH 0424/5780] [vim keymap] Rewrite expandWordUnderCursor. --- keymap/vim.js | 74 +++++++++++++++++++++++++++++++++++++----------- test/vim_test.js | 12 ++++++++ 2 files changed, 70 insertions(+), 16 deletions(-) diff --git a/keymap/vim.js b/keymap/vim.js index cf8a1a02b8..b8c5ac3794 100644 --- a/keymap/vim.js +++ b/keymap/vim.js @@ -269,7 +269,7 @@ return (/^[A-Z]$/).test(k); } function isAlphanumeric(k) { - return (/^[a-zA-Z0-9]/).test(k); + return (/^[\w]$/).test(k); } function isWhiteSpace(k) { return whiteSpaceRegex.test(k); @@ -651,12 +651,13 @@ showPrompt(cm, handleQuery, promptPrefix, searchPromptDesc); break; case 'wordUnderCursor': - var word = expandToWord(cm, false /** inclusive */, - true /** forward */, false /** bigWord */); + var word = expandWordUnderCursor(cm, false /** inclusive */, + true /** forward */, false /** bigWord */, + true /** noSymbol */); var query = cm.getLine(word.start.line).substring(word.start.ch, word.end.ch + 1); - // Use \b (word boundary) to simulate \< \> in Vim. query = '\\b' + query + '\\b'; + cm.setCursor(word.start); handleQuery(query); break; } @@ -1175,11 +1176,11 @@ // TODO: implement text objects for the reverse like }. Should just be // an additional mapping after moving to the defaultKeyMap. 'w': function(cm, inclusive) { - return expandToWord(cm, inclusive, true /** forward */, + return expandWordUnderCursor(cm, inclusive, true /** forward */, false /** bigWord */); }, 'W': function(cm, inclusive) { - return expandToWord(cm, inclusive, + return expandWordUnderCursor(cm, inclusive, true /** forward */, true /** bigWord */); }, '{': function(cm, inclusive) { @@ -1283,6 +1284,12 @@ function lineLength(cm, lineNum) { return cm.getLine(lineNum).length; } + function reverse(s){ + return s.split("").reverse().join(""); + } + function escapeRegex(s) { + return s.replace(/([.?*+\^$\[\]\\(){}|\-])/g, "\\$1"); + } function exitVisualMode(cm, vim) { vim.visualMode = false; @@ -1322,19 +1329,54 @@ return firstNonWS == -1 ? text.length : firstNonWS; } - function expandToWord(cm, inclusive, forward, bigWord) { + function expandWordUnderCursor(cm, inclusive, forward, bigWord, noSymbol) { var cur = cm.getCursor(); var line = cm.getLine(cur.line); + var idx = cur.ch; + + // Seek to first word or non-whitespace character, depending on if + // noSymbol is true. + var textAfterIdx = line.substring(idx); + var firstMatchedChar; + if (noSymbol) { + firstMatchedChar = textAfterIdx.search(/\w/); + } else { + firstMatchedChar = textAfterIdx.search(/\S/); + } + if (firstMatchedChar == -1) { + return null; + } + idx += firstMatchedChar; + textAfterIdx = line.substring(idx); + var textBeforeIdx = line.substring(0, idx); + + var matchRegex; + // Greedy matchers for the "word" we are trying to expand. + if (bigWord) { + matchRegex = /^\S+/; + } else { + if ((/\w/).test(line.charAt(idx))) { + matchRegex = /^\w+/; + } else { + matchRegex = /^[^\w\s]+/; + } + } + + var wordAfterRegex = matchRegex.exec(textAfterIdx); + var wordStart = idx; + var wordEnd = idx + wordAfterRegex[0].length - 1; + // TODO: Find a better way to do this. It will be slow on very long lines. + var wordBeforeRegex = matchRegex.exec(reverse(textBeforeIdx)); + if (wordBeforeRegex) { + wordStart -= wordBeforeRegex[0].length; + } + + if (inclusive) { + wordEnd++; + } - var line_to_char = line.substring(0, cur.ch); - // TODO: Case when small word is matching symbols does not work right with - // the current regexLastIndexOf check. - var start = regexLastIndexOf(line_to_char, - (!bigWord) ? /[^a-zA-Z0-9]/ : /\s/) + 1; - var end = motions.moveByWords(cm, { repeat: 1, forward: true, - wordEnd: true, bigWord: bigWord }); - end.ch += inclusive ? 1 : 0; - return { start: { line: cur.line, ch: start }, end: end }; + return { start: { line: cur.line, ch: wordStart }, + end: { line: cur.line, ch: wordEnd }}; } /* diff --git a/test/vim_test.js b/test/vim_test.js index 19b5543555..2d7067ee22 100644 --- a/test/vim_test.js +++ b/test/vim_test.js @@ -736,3 +736,15 @@ testVim('#', function(cm, vim, helpers) { helpers.doKeys('2', '#'); helpers.assertCursorAt(makeCursor(0, 22)); }, { value: 'nomatch match nomatch match \nnomatch Match' }); +testVim('*_seek', function(cm, vim, helpers) { + // Should skip over space and symbols. + cm.setCursor(0, 3); + helpers.doKeys('*'); + helpers.assertCursorAt(makeCursor(0, 22)); +}, { value: ' := match nomatch match \nnomatch Match' }); +testVim('#', function(cm, vim, helpers) { + // Should skip over space and symbols. + cm.setCursor(0, 3); + helpers.doKeys('#'); + helpers.assertCursorAt(makeCursor(1, 8)); +}, { value: ' := match nomatch match \nnomatch Match' }); From 9f59a5338b82872cc2386ad33ad6e2ffee45aef5 Mon Sep 17 00:00:00 2001 From: Yunchi Luo Date: Mon, 3 Dec 2012 22:49:48 -0500 Subject: [PATCH 0425/5780] [vim keymap] Fix a,x,s,d/y/c l at end of line --- keymap/vim.js | 20 ++++++++++---------- test/vim_test.js | 16 ++++++++++++++++ 2 files changed, 26 insertions(+), 10 deletions(-) diff --git a/keymap/vim.js b/keymap/vim.js index b8c5ac3794..9cae1ffc58 100644 --- a/keymap/vim.js +++ b/keymap/vim.js @@ -184,8 +184,7 @@ motion: 'moveByCharacters', motionArgs: { forward: true }}, // Actions { keys: ['a'], type: 'action', action: 'enterInsertMode', - motion: 'moveByCharacters', - motionArgs: { forward: true, noRepeat: true }}, + actionArgs: { insertAt: 'charAfter' }}, { keys: ['A'], type: 'action', action: 'enterInsertMode', actionArgs: { insertAt: 'eol' }}, { keys: ['i'], type: 'action', action: 'enterInsertMode' }, @@ -739,6 +738,7 @@ cm.setCursor(selectionStart); cm.setSelection(selectionStart, selectionEnd); } else if (!operator) { + curEnd = clipCursorToContent(cm, curEnd); cm.setCursor(curEnd.line, curEnd.ch); } } @@ -798,8 +798,7 @@ // Expands forward to end of line, and then to next line if repeat is // >1. Does not handle backward motion! var cur = cm.getCursor(); - return clipCursorToContent(cm, { line: cur.line + motionArgs.repeat - 1, - ch: Infinity }); + return { line: cur.line + motionArgs.repeat - 1, ch: Infinity }; }, findNext: function(cm, motionArgs, vim) { return findNext(cm, false /** prev */, motionArgs.repeat); @@ -818,7 +817,7 @@ var cur = cm.getCursor(); var repeat = motionArgs.repeat; var ch = motionArgs.forward ? cur.ch + repeat : cur.ch - repeat; - return clipCursorToContent(cm, { line: cur.line, ch: ch }); + return { line: cur.line, ch: ch }; }, moveByLines: function(cm, motionArgs, vim) { var endCh = cm.getCursor().ch; @@ -839,7 +838,7 @@ var cur = cm.getCursor(); var repeat = motionArgs.repeat; var line = motionArgs.forward ? cur.line + repeat : cur.line - repeat; - return clipCursorToContent(cm, { line: line, ch: endCh }); + return { line: line, ch: endCh }; }, moveByPage: function(cm, motionArgs) { // CodeMirror only exposes functions that move the cursor page down, so @@ -878,8 +877,7 @@ moveToEol: function(cm, motionArgs, vim) { var cur = cm.getCursor(); vim.lastHPos = Infinity; - return clipCursorToContent(cm, { line: cur.line + motionArgs.repeat - 1, - ch: Infinity }); + return { line: cur.line + motionArgs.repeat - 1, ch: Infinity }; }, moveToFirstNonWhiteSpaceCharacter: function(cm) { // Go to the start of the line where the text begins, or the end for @@ -907,8 +905,8 @@ if (motionArgs.repeatIsExplicit) { lineNum = motionArgs.repeat - 1; } - return clipCursorToContent(cm, { line: lineNum, - ch: findFirstNonWhiteSpaceCharacter(cm.getLine(lineNum)) }); + return { line: lineNum, + ch: findFirstNonWhiteSpaceCharacter(cm.getLine(lineNum)) }; }, textObjectManipulation: function(cm, motionArgs) { var character = motionArgs.selectedCharacter; @@ -1008,6 +1006,8 @@ var cursor = cm.getCursor(); cursor = { line: cursor.line, ch: lineLength(cm, cursor.line) }; cm.setCursor(cursor); + } else if (insertAt == 'charAfter') { + cm.setCursor(offsetCursor(cm.getCursor(), 0, 1)); } cm.setOption('keyMap', 'vim-insert'); }, diff --git a/test/vim_test.js b/test/vim_test.js index 2d7067ee22..c90fd2e1b4 100644 --- a/test/vim_test.js +++ b/test/vim_test.js @@ -254,6 +254,16 @@ testVim('dl', function(cm, vim, helpers) { is(!register.linewise); eqPos(curStart, cm.getCursor()); }, { value: ' word1 ' }); +testVim('dl_eol', function(cm, vim, helpers) { + var curStart = makeCursor(0, 6); + cm.setCursor(curStart); + helpers.doKeys('d', 'l'); + eq(' word1', cm.getValue()); + var register = vim.registerController.getRegister(); + eq(' ', register.text); + is(!register.linewise); + helpers.assertCursorAt(makeCursor(0, 6)); +}, { value: ' word1 ' }); testVim('dl_repeat', function(cm, vim, helpers) { var curStart = makeCursor(0, 0); cm.setCursor(curStart); @@ -586,6 +596,12 @@ testVim('a', function(cm, vim, helpers) { eqPos(makeCursor(0, 2), cm.getCursor()); eq('vim-insert', cm.getOption('keyMap')); }); +testVim('a_eol', function(cm, vim, helpers) { + cm.setCursor(0, lines[0].length - 1); + helpers.doKeys('a'); + helpers.assertCursorAt(makeCursor(0, lines[0].length)); + eq('vim-insert', cm.getOption('keyMap')); +}); testVim('i', function(cm, vim, helpers) { cm.setCursor(0, 1); helpers.doKeys('i'); From 5dc92890a81e48b362531762a82995b590e5c5a4 Mon Sep 17 00:00:00 2001 From: Yunchi Luo Date: Mon, 3 Dec 2012 23:38:41 -0500 Subject: [PATCH 0426/5780] [vim keymap] Fix a few edge cases --- keymap/vim.js | 11 +++++++++-- test/vim_test.js | 35 ++++++++++++++++++++++++++++++++++- 2 files changed, 43 insertions(+), 3 deletions(-) diff --git a/keymap/vim.js b/keymap/vim.js index 9cae1ffc58..833e035401 100644 --- a/keymap/vim.js +++ b/keymap/vim.js @@ -838,6 +838,9 @@ var cur = cm.getCursor(); var repeat = motionArgs.repeat; var line = motionArgs.forward ? cur.line + repeat : cur.line - repeat; + if (line < 0 || line > cm.lineCount() - 1) { + return null; + } return { line: line, ch: endCh }; }, moveByPage: function(cm, motionArgs) { @@ -1498,8 +1501,12 @@ cur.ch = word.from; } } else { - // No more words to be found. Move to end of line. - return { line: cur.line, ch: lineLength(cm, cur.line) }; + // No more words to be found. Move to the end. + if (forward) { + return { line: cur.line, ch: lineLength(cm, cur.line) }; + } else { + return { line: cur.line, ch: 0 }; + } } } } diff --git a/test/vim_test.js b/test/vim_test.js index c90fd2e1b4..b5e77867c0 100644 --- a/test/vim_test.js +++ b/test/vim_test.js @@ -26,6 +26,8 @@ var lines = (function() { } return ret; })(); +var endOfDocument = makeCursor(lines.length - 1, + lines[lines.length - 1].length); var wordLine = lines[0]; var bigWordLine = lines[1]; var charLine = lines[2]; @@ -112,7 +114,13 @@ function testVim(name, run, opts, expectedFail) { } } function assertCursorAtFn(cm) { - return function(pos) { + return function(line, ch) { + var pos; + if (ch == null && typeof line.line == 'number') { + pos = line; + } else { + pos = makeCursor(line, ch); + } eqPos(pos, cm.getCursor()); } } @@ -179,17 +187,22 @@ testMotion('k_repeat', ['2', 'k'], makeCursor(0, 4), makeCursor(2, 4)); testMotion('w', 'w', word1.start); testMotion('w_repeat', ['2', 'w'], word2.start); testMotion('w_wrap', ['w'], word3.start, word2.start); +testMotion('w_endOfDocument', 'w', endOfDocument, endOfDocument); testMotion('W', 'W', bigWord1.start); testMotion('W_repeat', ['2', 'W'], bigWord3.start, bigWord1.start); testMotion('e', 'e', word1.end); testMotion('e_repeat', ['2', 'e'], word2.end); testMotion('e_wrap', 'e', word3.end, word2.end); +testMotion('e_endOfDocument', 'e', endOfDocument, endOfDocument); testMotion('b', 'b', word3.start, word3.end); testMotion('b_repeat', ['2', 'b'], word2.start, word3.end); testMotion('b_wrap', 'b', word2.start, word3.start); +testMotion('b_startOfDocument', 'b', makeCursor(0, 0), makeCursor(0, 0)); testMotion('ge', ['g', 'e'], word2.end, word3.end); testMotion('ge_repeat', ['2', 'g', 'e'], word1.end, word3.start); testMotion('ge_wrap', ['g', 'e'], word2.end, word3.start); +testMotion('ge_startOfDocument', ['g', 'e'], makeCursor(0, 0), + makeCursor(0, 0)); testMotion('gg', ['g', 'g'], makeCursor(lines[0].line, lines[0].textStart), makeCursor(3, 1)); testMotion('gg_repeat', ['3', 'g', 'g'], @@ -294,6 +307,16 @@ testVim('dj', function(cm, vim, helpers) { is(register.linewise); eqPos(makeCursor(0, 1), cm.getCursor()); }, { value: ' word1\nword2\n word3' }); +testVim('dj_end_of_document', function(cm, vim, helpers) { + var curStart = makeCursor(0, 3); + cm.setCursor(curStart); + helpers.doKeys('d', 'j'); + eq(' word1 ', cm.getValue()); + var register = vim.registerController.getRegister(); + eq('', register.text); + is(!register.linewise); + helpers.assertCursorAt(0, 3); +}, { value: ' word1 ' }); testVim('dk', function(cm, vim, helpers) { var curStart = makeCursor(1, 3); cm.setCursor(curStart); @@ -304,6 +327,16 @@ testVim('dk', function(cm, vim, helpers) { is(register.linewise); eqPos(makeCursor(0, 1), cm.getCursor()); }, { value: ' word1\nword2\n word3' }); +testVim('dk_start_of_document', function(cm, vim, helpers) { + var curStart = makeCursor(0, 3); + cm.setCursor(curStart); + helpers.doKeys('d', 'k'); + eq(' word1 ', cm.getValue()); + var register = vim.registerController.getRegister(); + eq('', register.text); + is(!register.linewise); + helpers.assertCursorAt(0, 3); +}, { value: ' word1 ' }); testVim('dw_space', function(cm, vim, helpers) { var curStart = makeCursor(0, 0); cm.setCursor(curStart); From 03c4853f6934d180d2bb0269212b78731de66f76 Mon Sep 17 00:00:00 2001 From: Matt Pass Date: Mon, 3 Dec 2012 16:30:37 +0000 Subject: [PATCH 0427/5780] Postfixed main theme classes where BG/FG set Added .CodeMirror to end of class names so that under CodeMirror v3, the authors intended BG/FG is used rather than the parent styling seen --- theme/ambiance.css | 4 ++-- theme/blackboard.css | 2 +- theme/cobalt.css | 2 +- theme/erlang-dark.css | 2 +- theme/lesser-dark.css | 2 +- theme/monokai.css | 2 +- theme/night.css | 2 +- theme/rubyblue.css | 2 +- theme/twilight.css | 2 +- theme/vibrant-ink.css | 2 +- theme/xq-dark.css | 2 +- 11 files changed, 12 insertions(+), 12 deletions(-) diff --git a/theme/ambiance.css b/theme/ambiance.css index c275442e5f..beec553851 100644 --- a/theme/ambiance.css +++ b/theme/ambiance.css @@ -39,7 +39,7 @@ /* Editor styling */ -.cm-s-ambiance { +.cm-s-ambiance.CodeMirror { line-height: 1.40em; font-family: Monaco, Menlo,"Andale Mono","lucida console","Courier New",monospace !important; color: #E6E1DC; @@ -70,7 +70,7 @@ background: none repeat scroll 0% 0% rgba(255, 255, 255, 0.031); } -.cm-s-ambiance, +.cm-s-ambiance.CodeMirror, .cm-s-ambiance .CodeMirror-gutters { background-image: url("data:image/png;base64,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"); } diff --git a/theme/blackboard.css b/theme/blackboard.css index 6e7bab7d03..f2bde690c8 100644 --- a/theme/blackboard.css +++ b/theme/blackboard.css @@ -1,6 +1,6 @@ /* Port of TextMate's Blackboard theme */ -.cm-s-blackboard { background: #0C1021; color: #F8F8F8; } +.cm-s-blackboard.CodeMirror { background: #0C1021; color: #F8F8F8; } .cm-s-blackboard .CodeMirror-selected { background: #253B76 !important; } .cm-s-blackboard .CodeMirror-gutters { background: #0C1021; border-right: 0; } .cm-s-blackboard .CodeMirror-linenumber { color: #888; } diff --git a/theme/cobalt.css b/theme/cobalt.css index daf0abe366..6095799f36 100644 --- a/theme/cobalt.css +++ b/theme/cobalt.css @@ -1,4 +1,4 @@ -.cm-s-cobalt { background: #002240; color: white; } +.cm-s-cobalt.CodeMirror { background: #002240; color: white; } .cm-s-cobalt div.CodeMirror-selected { background: #b36539 !important; } .cm-s-cobalt .CodeMirror-gutters { background: #002240; border-right: 1px solid #aaa; } .cm-s-cobalt .CodeMirror-linenumber { color: #d0d0d0; } diff --git a/theme/erlang-dark.css b/theme/erlang-dark.css index d6a6d377db..ea9c26c44d 100644 --- a/theme/erlang-dark.css +++ b/theme/erlang-dark.css @@ -1,4 +1,4 @@ -.cm-s-erlang-dark { background: #002240; color: white; } +.cm-s-erlang-dark.CodeMirror { background: #002240; color: white; } .cm-s-erlang-dark div.CodeMirror-selected { background: #b36539 !important; } .cm-s-erlang-dark .CodeMirror-gutters { background: #002240; border-right: 1px solid #aaa; } .cm-s-erlang-dark .CodeMirror-linenumber { color: #d0d0d0; } diff --git a/theme/lesser-dark.css b/theme/lesser-dark.css index 509b1e804e..67f71ad727 100644 --- a/theme/lesser-dark.css +++ b/theme/lesser-dark.css @@ -9,7 +9,7 @@ Ported to CodeMirror by Peter Kroon font-family: 'Bitstream Vera Sans Mono', 'DejaVu Sans Mono', 'Monaco', Courier, monospace !important; } -.cm-s-lesser-dark { background: #262626; color: #EBEFE7; text-shadow: 0 -1px 1px #262626; } +.cm-s-lesser-dark.CodeMirror { background: #262626; color: #EBEFE7; text-shadow: 0 -1px 1px #262626; } .cm-s-lesser-dark div.CodeMirror-selected {background: #45443B !important;} /* 33322B*/ .cm-s-lesser-dark .CodeMirror-cursor { border-left: 1px solid white !important; } .cm-s-lesser-dark pre { padding: 0 8px; }/*editable code holder*/ diff --git a/theme/monokai.css b/theme/monokai.css index 89692f5d6a..a0b3c7c0af 100644 --- a/theme/monokai.css +++ b/theme/monokai.css @@ -1,6 +1,6 @@ /* Based on Sublime Text's Monokai theme */ -.cm-s-monokai {background: #272822; color: #f8f8f2;} +.cm-s-monokai.CodeMirror {background: #272822; color: #f8f8f2;} .cm-s-monokai div.CodeMirror-selected {background: #49483E !important;} .cm-s-monokai .CodeMirror-gutters {background: #272822; border-right: 0px;} .cm-s-monokai .CodeMirror-linenumber {color: #d0d0d0;} diff --git a/theme/night.css b/theme/night.css index b403e54dd6..8804a399a1 100644 --- a/theme/night.css +++ b/theme/night.css @@ -1,6 +1,6 @@ /* Loosely based on the Midnight Textmate theme */ -.cm-s-night { background: #0a001f; color: #f8f8f8; } +.cm-s-night.CodeMirror { background: #0a001f; color: #f8f8f8; } .cm-s-night div.CodeMirror-selected { background: #447 !important; } .cm-s-night .CodeMirror-gutters { background: #0a001f; border-right: 1px solid #aaa; } .cm-s-night .CodeMirror-linenumber { color: #f8f8f8; } diff --git a/theme/rubyblue.css b/theme/rubyblue.css index 47e0b69e08..8817de07bf 100644 --- a/theme/rubyblue.css +++ b/theme/rubyblue.css @@ -1,6 +1,6 @@ .cm-s-rubyblue { font:13px/1.4em Trebuchet, Verdana, sans-serif; } /* - customized editor font - */ -.cm-s-rubyblue { background: #112435; color: white; } +.cm-s-rubyblue.CodeMirror { background: #112435; color: white; } .cm-s-rubyblue div.CodeMirror-selected { background: #38566F !important; } .cm-s-rubyblue .CodeMirror-gutters { background: #1F4661; border-right: 7px solid #3E7087; } .cm-s-rubyblue .CodeMirror-linenumber { color: white; } diff --git a/theme/twilight.css b/theme/twilight.css index 1d151dbd9c..fd8944ba8d 100644 --- a/theme/twilight.css +++ b/theme/twilight.css @@ -1,4 +1,4 @@ -.cm-s-twilight { background: #141414; color: #f7f7f7; } /**/ +.cm-s-twilight.CodeMirror { background: #141414; color: #f7f7f7; } /**/ .cm-s-twilight .CodeMirror-selected { background: #323232 !important; } /**/ .cm-s-twilight .CodeMirror-gutters { background: #222; border-right: 1px solid #aaa; } diff --git a/theme/vibrant-ink.css b/theme/vibrant-ink.css index 9a6fd4df49..22024a489a 100644 --- a/theme/vibrant-ink.css +++ b/theme/vibrant-ink.css @@ -1,6 +1,6 @@ /* Taken from the popular Visual Studio Vibrant Ink Schema */ -.cm-s-vibrant-ink { background: black; color: white; } +.cm-s-vibrant-ink.CodeMirror { background: black; color: white; } .cm-s-vibrant-ink .CodeMirror-selected { background: #35493c !important; } .cm-s-vibrant-ink .CodeMirror-gutters { background: #002240; border-right: 1px solid #aaa; } diff --git a/theme/xq-dark.css b/theme/xq-dark.css index 5add87be70..fd9bb12abc 100644 --- a/theme/xq-dark.css +++ b/theme/xq-dark.css @@ -20,7 +20,7 @@ LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. */ -.cm-s-xq-dark { background: #0a001f; color: #f8f8f8; } +.cm-s-xq-dark.CodeMirror { background: #0a001f; color: #f8f8f8; } .cm-s-xq-dark span.CodeMirror-selected { background: #a8f !important; } .cm-s-xq-dark .CodeMirror-gutters { background: #0a001f; border-right: 1px solid #aaa; } .cm-s-xq-dark .CodeMirror-linenumber { color: #f8f8f8; } From 5e11eadb63b0002d985d17d75042f6673b47f9e9 Mon Sep 17 00:00:00 2001 From: James Campos Date: Mon, 3 Dec 2012 02:46:54 -0800 Subject: [PATCH 0428/5780] [vim][v3] fix search highlighting `markText` changed parameters from v2 to v3 --- keymap/vim.js | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/keymap/vim.js b/keymap/vim.js index 833e035401..58ccbc3745 100644 --- a/keymap/vim.js +++ b/keymap/vim.js @@ -1785,7 +1785,7 @@ for (var cursor = cm.getSearchCursor(state.query); cursor.findNext();) { state.marked.push(cm.markText(cursor.from(), cursor.to(), - 'CodeMirror-searching')); + { className: 'CodeMirror-searching' })); } } }); From c3312da21fa07077a8d081b8c93ca97b5448d703 Mon Sep 17 00:00:00 2001 From: Marijn Haverbeke Date: Tue, 4 Dec 2012 11:30:12 +0100 Subject: [PATCH 0429/5780] Don't mutate cursor style for blinking when it is invisible Closes #1044 --- lib/codemirror.js | 1 + 1 file changed, 1 insertion(+) diff --git a/lib/codemirror.js b/lib/codemirror.js index 73c9a2867e..c7154252fb 100644 --- a/lib/codemirror.js +++ b/lib/codemirror.js @@ -752,6 +752,7 @@ window.CodeMirror = (function() { var on = true; display.cursor.style.visibility = display.otherCursor.style.visibility = ""; display.blinker = setInterval(function() { + if (!display.cursor.offsetHeight) return; display.cursor.style.visibility = display.otherCursor.style.visibility = (on = !on) ? "" : "hidden"; }, cm.options.cursorBlinkRate); } From 8bcede98c421e0aa084dc224d49a8494e06c8cd1 Mon Sep 17 00:00:00 2001 From: Marijn Haverbeke Date: Tue, 4 Dec 2012 11:53:19 +0100 Subject: [PATCH 0430/5780] Improve cache clearing (again) Issue #1042 --- lib/codemirror.js | 13 +++++++------ 1 file changed, 7 insertions(+), 6 deletions(-) diff --git a/lib/codemirror.js b/lib/codemirror.js index c7154252fb..33e13e562e 100644 --- a/lib/codemirror.js +++ b/lib/codemirror.js @@ -214,7 +214,7 @@ window.CodeMirror = (function() { }); } regChange(cm, 0, doc.size); - clearMeasureLineCache(cm); + clearCaches(cm); setTimeout(bind(updateScrollbars, cm.display), 100); } @@ -227,6 +227,7 @@ window.CodeMirror = (function() { function themeChanged(cm) { cm.display.wrapper.className = cm.display.wrapper.className.replace(/\s*cm-s-\S+/g, "") + cm.options.theme.replace(/(^|\s)\s*/g, " cm-s-"); + clearCaches(cm); } function guttersChanged(cm) { @@ -924,8 +925,9 @@ window.CodeMirror = (function() { return data; } - function clearMeasureLineCache(cm) { + function clearCaches(cm) { cm.display.measureLineCache.length = cm.display.measureLineCachePos = 0; + cm.display.cachedCharWidth = cm.display.cachedTextHeight = null; } // Context is one of "line", "div" (display.lineDiv), "local"/null (editor), or "page" @@ -1260,7 +1262,7 @@ window.CodeMirror = (function() { on(window, "resize", function resizeHandler() { // Might be a text scaling operation, clear size caches. d.cachedCharWidth = d.cachedTextHeight = null; - clearMeasureLineCache(cm); + clearCaches(cm); if (d.wrapper.parentNode) updateDisplay(cm, true); else off(window, "resize", resizeHandler); }); @@ -2729,7 +2731,7 @@ window.CodeMirror = (function() { operation: function(f){return operation(this, f)();}, refresh: function() { - clearMeasureLineCache(this); + clearCaches(this); if (this.display.scroller.scrollHeight > this.view.scrollTop) this.display.scrollbarV.scrollTop = this.display.scroller.scrollTop = this.view.scrollTop; updateDisplay(this, true); @@ -2766,13 +2768,12 @@ window.CodeMirror = (function() { option("smartIndent", true); option("tabSize", 4, function(cm) { loadMode(cm); - clearMeasureLineCache(cm); + clearCaches(cm); updateDisplay(cm, true); }, true); option("electricChars", true); option("theme", "default", function(cm) { - clearMeasureLineCache(cm); themeChanged(cm); guttersChanged(cm); }, true); From 42041769ecc52af08ac4fadf513aabca78706157 Mon Sep 17 00:00:00 2001 From: Marijn Haverbeke Date: Tue, 4 Dec 2012 12:14:33 +0100 Subject: [PATCH 0431/5780] Allow multiple iterations when scrolling a position into view New computed heights for intermediate lines might cause the first attempt to not actually scroll the desited position into view. Closes #1043 Closes #1042 --- lib/codemirror.js | 25 +++++++++++++++++++++---- 1 file changed, 21 insertions(+), 4 deletions(-) diff --git a/lib/codemirror.js b/lib/codemirror.js index 33e13e562e..a45a149b3c 100644 --- a/lib/codemirror.js +++ b/lib/codemirror.js @@ -2132,8 +2132,8 @@ window.CodeMirror = (function() { // SCROLLING function scrollCursorIntoView(cm) { - var view = cm.view, coords = cursorCoords(cm, view.sel.head); - scrollIntoView(cm, coords.left, coords.top, coords.left, coords.bottom); + var view = cm.view; + var coords = scrollPosIntoView(cm, view.sel.head); if (!view.focused) return; var display = cm.display, box = display.sizer.getBoundingClientRect(), doScroll = null; if (coords.top + box.top < 0) doScroll = true; @@ -2150,6 +2150,23 @@ window.CodeMirror = (function() { } } + function scrollPosIntoView(cm, pos) { + for (;;) { + var changed = false, coords = cursorCoords(cm, pos); + var scrollPos = calculateScrollPos(cm, coords.left, coords.top, coords.left, coords.bottom); + var startTop = cm.view.scrollTop, startLeft = cm.view.scrollLeft; + if (scrollPos.scrollTop != null) { + setScrollTop(cm, scrollPos.scrollTop); + if (Math.abs(cm.view.scrollTop - startTop) > 1) changed = true; + } + if (scrollPos.scrollLeft != null) { + setScrollLeft(cm, scrollPos.scrollLeft); + if (Math.abs(cm.view.scrollLeft - startLeft) > 1) changed = true; + } + if (!changed) return coords; + } + } + function scrollIntoView(cm, x1, y1, x2, y2) { var scrollPos = calculateScrollPos(cm, x1, y1, x2, y2); if (scrollPos.scrollTop != null) setScrollTop(cm, scrollPos.scrollTop); @@ -2711,9 +2728,9 @@ window.CodeMirror = (function() { }, scrollIntoView: function(pos) { + if (typeof pos == "number") pos = {line: pos, ch: 0}; pos = pos ? clipPos(this.view.doc, pos) : this.view.sel.head; - var coords = cursorCoords(this, pos); - scrollIntoView(this, coords.left, coords.top, coords.left, coords.bottom); + scrollPosIntoView(this, pos); }, setSize: function(width, height) { From 8cdacf4fc2caeb1a6c8cb161f65a9e3a05440fe6 Mon Sep 17 00:00:00 2001 From: Marijn Haverbeke Date: Wed, 5 Dec 2012 11:53:23 +0100 Subject: [PATCH 0432/5780] Invalidate max line length when calling .refresh() Closes #1049 --- lib/codemirror.js | 1 + 1 file changed, 1 insertion(+) diff --git a/lib/codemirror.js b/lib/codemirror.js index a45a149b3c..1ccb31a4c1 100644 --- a/lib/codemirror.js +++ b/lib/codemirror.js @@ -928,6 +928,7 @@ window.CodeMirror = (function() { function clearCaches(cm) { cm.display.measureLineCache.length = cm.display.measureLineCachePos = 0; cm.display.cachedCharWidth = cm.display.cachedTextHeight = null; + cm.view.maxLineChanged = true; } // Context is one of "line", "div" (display.lineDiv), "local"/null (editor), or "page" From a05aca9bb3981b136689253809a766454a12ecd2 Mon Sep 17 00:00:00 2001 From: Marijn Haverbeke Date: Wed, 5 Dec 2012 12:01:36 +0100 Subject: [PATCH 0433/5780] [htmlembedded mode] Don't directly call startState and indent on unknown mode It may not provide these methods. Closes #1051 --- mode/htmlembedded/htmlembedded.js | 6 +++--- 1 file changed, 3 insertions(+), 3 deletions(-) diff --git a/mode/htmlembedded/htmlembedded.js b/mode/htmlembedded/htmlembedded.js index b7888689f1..e183d67467 100644 --- a/mode/htmlembedded/htmlembedded.js +++ b/mode/htmlembedded/htmlembedded.js @@ -34,8 +34,8 @@ CodeMirror.defineMode("htmlembedded", function(config, parserConfig) { htmlMixedMode = htmlMixedMode || CodeMirror.getMode(config, "htmlmixed"); return { token : parserConfig.startOpen ? scriptingDispatch : htmlDispatch, - htmlState : htmlMixedMode.startState(), - scriptState : scriptingMode.startState() + htmlState : CodeMirror.startState(htmlMixedMode), + scriptState : CodeMirror.startState(scriptingMode) }; }, @@ -46,7 +46,7 @@ CodeMirror.defineMode("htmlembedded", function(config, parserConfig) { indent: function(state, textAfter) { if (state.token == htmlDispatch) return htmlMixedMode.indent(state.htmlState, textAfter); - else + else if (scriptingMode.indent) return scriptingMode.indent(state.scriptState, textAfter); }, From f7fe5cdc4d3e6568d8286caf17b8a75ba0776a20 Mon Sep 17 00:00:00 2001 From: Marijn Haverbeke Date: Wed, 5 Dec 2012 12:04:34 +0100 Subject: [PATCH 0434/5780] [css mode] Include leading space in string token for non-quoted url values Issue #1026 --- mode/css/css.js | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/mode/css/css.js b/mode/css/css.js index e1fc70b0cb..f4ce9f9d77 100644 --- a/mode/css/css.js +++ b/mode/css/css.js @@ -352,7 +352,7 @@ CodeMirror.defineMode("css", function(config) { // sequence of selectors: // One or more of the named type of selector chained with commas. - if (stream.eatSpace()) return null; + if (state.tokenize == tokenBase && stream.eatSpace()) return null; var style = state.tokenize(stream, state); // Changing style returned based on context From 821cfe1b0f0823906ae296ae3556d21df8ddd601 Mon Sep 17 00:00:00 2001 From: Marijn Haverbeke Date: Wed, 5 Dec 2012 13:56:08 +0100 Subject: [PATCH 0435/5780] Don't call dataTransfer.setDragImage on Sarari It seems to cause Safari 6.0.2 to follow a null pointer and crash. --- lib/codemirror.js | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-) diff --git a/lib/codemirror.js b/lib/codemirror.js index 1ccb31a4c1..d11b0b6040 100644 --- a/lib/codemirror.js +++ b/lib/codemirror.js @@ -1546,7 +1546,8 @@ window.CodeMirror = (function() { e.dataTransfer.setData("Text", txt); // Use dummy image instead of default browsers image. - if (e.dataTransfer.setDragImage) + // Recent Safari (~6.0.2) have a tendency to segfault when this happens, so we don't do it there. + if (e.dataTransfer.setDragImage && !safari) e.dataTransfer.setDragImage(elt('img'), 0, 0); } From 17cffdf6407de1e8fe533d4db740471327a8b717 Mon Sep 17 00:00:00 2001 From: Marijn Haverbeke Date: Wed, 5 Dec 2012 15:33:55 +0100 Subject: [PATCH 0436/5780] Fix typo in manual --- doc/manual.html | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/doc/manual.html b/doc/manual.html index 9eef7f3b4a..bb544cdcbd 100644 --- a/doc/manual.html +++ b/doc/manual.html @@ -1223,7 +1223,7 @@

    Writing CodeMirror Modes

    CodeMirror. This function takes two arguments. The first should be the name of the mode, for which you should use a lowercase string, preferably one that is also the name of the files that define the - mode (i.e. "xml" is defined xml.js). The + mode (i.e. "xml" is defined in xml.js). The second argument should be a function that, given a CodeMirror configuration object (the thing passed to the CodeMirror function) and an optional mode From 041bb7d1f4e342a4b9809f3542084a34b7a710bd Mon Sep 17 00:00:00 2001 From: Yunchi Luo Date: Wed, 5 Dec 2012 15:26:13 -0500 Subject: [PATCH 0437/5780] [vim keymap] Fix yank and paste between files --- demo/vim.html | 10 ++++++ keymap/vim.js | 36 ++++++++++++++++----- test/vim_test.js | 82 +++++++++++++++++++++++++----------------------- 3 files changed, 82 insertions(+), 46 deletions(-) diff --git a/demo/vim.html b/demo/vim.html index 2108b54616..d2a143acf1 100644 --- a/demo/vim.html +++ b/demo/vim.html @@ -35,6 +35,10 @@

    CodeMirror: Vim bindings demo

    } +
    +

    The vim keybindings are enabled by including keymap/vim.js and setting the keyMap option to "vim". Because @@ -49,6 +53,12 @@

    CodeMirror: Vim bindings demo

    keyMap: "vim", showCursorWhenSelecting: true }); + var editor2 = CodeMirror.fromTextArea(document.getElementById("code2"), { + lineNumbers: true, + mode: "text/x-csrc", + keyMap: "vim", + showCursorWhenSelecting: true + }); diff --git a/keymap/vim.js b/keymap/vim.js index 58ccbc3745..da48b1c83f 100644 --- a/keymap/vim.js +++ b/keymap/vim.js @@ -287,6 +287,17 @@ } return false; } + + // Global Vim state. Call getVimGlobalState to get and initialize. + var vimGlobalState; + function getVimGlobalState() { + if (!vimGlobalState) { + vimGlobalState = { + registerController: new RegisterController({}) + } + } + return vimGlobalState; + } function getVimState(cm) { if (!cm.vimState) { // Store instance state in the CodeMirror object. @@ -302,7 +313,6 @@ // executed in between. lastMotion: null, marks: {}, - registerController: new RegisterController({}), visualMode: false, // If we are in visual line mode. No effect if visualMode is false. visualLine: false @@ -319,6 +329,15 @@ buildKeyMap: function() { // TODO: Convert keymap into dictionary format for fast lookup. }, + // Testing hook, though it might be useful to expose the register + // controller anyways. + getRegisterController: function() { + return getVimGlobalState().registerController; + }, + // Testing hook. + _clearVimGlobalState: function() { + vimGlobalState = null; + }, // Initializes vim state variable on the CodeMirror object. Should only be // called lazily by handleKey or for testing. maybeInitState: function(cm) { @@ -932,8 +951,9 @@ var operators = { change: function(cm, operatorArgs, vim, curStart, curEnd) { - vim.registerController.pushText(operatorArgs.registerName, 'change', - cm.getRange(curStart, curEnd), operatorArgs.linewise); + getVimGlobalState().registerController.pushText( + operatorArgs.registerName, 'change', cm.getRange(curStart, curEnd), + operatorArgs.linewise); if (operatorArgs.linewise) { // Delete starting at the first nonwhitespace character of the first // line, instead of from the start of the first line. This way we get @@ -954,8 +974,9 @@ }, // delete is a javascript keyword. 'delete': function(cm, operatorArgs, vim, curStart, curEnd) { - vim.registerController.pushText(operatorArgs.registerName, 'delete', - cm.getRange(curStart, curEnd), operatorArgs.linewise); + getVimGlobalState().registerController.pushText( + operatorArgs.registerName, 'delete', cm.getRange(curStart, curEnd), + operatorArgs.linewise); cm.replaceRange('', curStart, curEnd); if (operatorArgs.linewise) { cm.setCursor(motions.moveToFirstNonWhiteSpaceCharacter(cm)); @@ -995,7 +1016,8 @@ cm.setCursor(curOriginal); }, yank: function(cm, operatorArgs, vim, curStart, curEnd, curOriginal) { - vim.registerController.pushText(operatorArgs.registerName, 'yank', + getVimGlobalState().registerController.pushText( + operatorArgs.registerName, 'yank', cm.getRange(curStart, curEnd), operatorArgs.linewise); cm.setCursor(curOriginal); } @@ -1096,7 +1118,7 @@ }, paste: function(cm, actionArgs, vim) { var cur = cm.getCursor(); - var register = vim.registerController.getRegister( + var register = getVimGlobalState().registerController.getRegister( actionArgs.registerName); if (!register.text) { return; diff --git a/test/vim_test.js b/test/vim_test.js index b5e77867c0..ebdcfe2192 100644 --- a/test/vim_test.js +++ b/test/vim_test.js @@ -132,8 +132,12 @@ function testVim(name, run, opts, expectedFail) { var helpers = { doKeys: doKeysFn(cm), assertCursorAt: assertCursorAtFn(cm), - fakeOpenDialog: fakeOpenDialog + fakeOpenDialog: fakeOpenDialog, + getRegisterController: function() { + return CodeMirror.Vim.getRegisterController(); + } } + CodeMirror.Vim._clearVimGlobalState(); var successful = false; try { run(cm, vim, helpers); @@ -262,7 +266,7 @@ testVim('dl', function(cm, vim, helpers) { cm.setCursor(curStart); helpers.doKeys('d', 'l'); eq('word1 ', cm.getValue()); - var register = vim.registerController.getRegister(); + var register = helpers.getRegisterController().getRegister(); eq(' ', register.text); is(!register.linewise); eqPos(curStart, cm.getCursor()); @@ -272,7 +276,7 @@ testVim('dl_eol', function(cm, vim, helpers) { cm.setCursor(curStart); helpers.doKeys('d', 'l'); eq(' word1', cm.getValue()); - var register = vim.registerController.getRegister(); + var register = helpers.getRegisterController().getRegister(); eq(' ', register.text); is(!register.linewise); helpers.assertCursorAt(makeCursor(0, 6)); @@ -282,7 +286,7 @@ testVim('dl_repeat', function(cm, vim, helpers) { cm.setCursor(curStart); helpers.doKeys('2', 'd', 'l'); eq('ord1 ', cm.getValue()); - var register = vim.registerController.getRegister(); + var register = helpers.getRegisterController().getRegister(); eq(' w', register.text); is(!register.linewise); eqPos(curStart, cm.getCursor()); @@ -292,7 +296,7 @@ testVim('dh', function(cm, vim, helpers) { cm.setCursor(curStart); helpers.doKeys('d', 'h'); eq(' wrd1 ', cm.getValue()); - var register = vim.registerController.getRegister(); + var register = helpers.getRegisterController().getRegister(); eq('o', register.text); is(!register.linewise); eqPos(offsetCursor(curStart, 0 , -1), cm.getCursor()); @@ -302,7 +306,7 @@ testVim('dj', function(cm, vim, helpers) { cm.setCursor(curStart); helpers.doKeys('d', 'j'); eq(' word3', cm.getValue()); - var register = vim.registerController.getRegister(); + var register = helpers.getRegisterController().getRegister(); eq(' word1\nword2\n', register.text); is(register.linewise); eqPos(makeCursor(0, 1), cm.getCursor()); @@ -312,7 +316,7 @@ testVim('dj_end_of_document', function(cm, vim, helpers) { cm.setCursor(curStart); helpers.doKeys('d', 'j'); eq(' word1 ', cm.getValue()); - var register = vim.registerController.getRegister(); + var register = helpers.getRegisterController().getRegister(); eq('', register.text); is(!register.linewise); helpers.assertCursorAt(0, 3); @@ -322,7 +326,7 @@ testVim('dk', function(cm, vim, helpers) { cm.setCursor(curStart); helpers.doKeys('d', 'k'); eq(' word3', cm.getValue()); - var register = vim.registerController.getRegister(); + var register = helpers.getRegisterController().getRegister(); eq(' word1\nword2\n', register.text); is(register.linewise); eqPos(makeCursor(0, 1), cm.getCursor()); @@ -332,7 +336,7 @@ testVim('dk_start_of_document', function(cm, vim, helpers) { cm.setCursor(curStart); helpers.doKeys('d', 'k'); eq(' word1 ', cm.getValue()); - var register = vim.registerController.getRegister(); + var register = helpers.getRegisterController().getRegister(); eq('', register.text); is(!register.linewise); helpers.assertCursorAt(0, 3); @@ -342,7 +346,7 @@ testVim('dw_space', function(cm, vim, helpers) { cm.setCursor(curStart); helpers.doKeys('d', 'w'); eq('word1 ', cm.getValue()); - var register = vim.registerController.getRegister(); + var register = helpers.getRegisterController().getRegister(); eq(' ', register.text); is(!register.linewise); eqPos(curStart, cm.getCursor()); @@ -352,7 +356,7 @@ testVim('dw_word', function(cm, vim, helpers) { cm.setCursor(curStart); helpers.doKeys('d', 'w'); eq(' word2', cm.getValue()); - var register = vim.registerController.getRegister(); + var register = helpers.getRegisterController().getRegister(); eq('word1 ', register.text); is(!register.linewise); eqPos(curStart, cm.getCursor()); @@ -364,7 +368,7 @@ testVim('dw_only_word', function(cm, vim, helpers) { cm.setCursor(curStart); helpers.doKeys('d', 'w'); eq(' ', cm.getValue()); - var register = vim.registerController.getRegister(); + var register = helpers.getRegisterController().getRegister(); eq('word1 ', register.text); is(!register.linewise); eqPos(curStart, cm.getCursor()); @@ -376,7 +380,7 @@ testVim('dw_eol', function(cm, vim, helpers) { cm.setCursor(curStart); helpers.doKeys('d', 'w'); eq(' \nword2', cm.getValue()); - var register = vim.registerController.getRegister(); + var register = helpers.getRegisterController().getRegister(); eq('word1', register.text); is(!register.linewise); eqPos(curStart, cm.getCursor()); @@ -388,7 +392,7 @@ testVim('dw_repeat', function(cm, vim, helpers) { cm.setCursor(curStart); helpers.doKeys('d', '2', 'w'); eq(' ', cm.getValue()); - var register = vim.registerController.getRegister(); + var register = helpers.getRegisterController().getRegister(); eq('word1\nword2', register.text); is(!register.linewise); eqPos(curStart, cm.getCursor()); @@ -400,7 +404,7 @@ testVim('d_inclusive', function(cm, vim, helpers) { cm.setCursor(curStart); helpers.doKeys('d', 'e'); eq(' ', cm.getValue()); - var register = vim.registerController.getRegister(); + var register = helpers.getRegisterController().getRegister(); eq('word1', register.text); is(!register.linewise); eqPos(curStart, cm.getCursor()); @@ -410,7 +414,7 @@ testVim('d_reverse', function(cm, vim, helpers) { cm.setCursor(1, 0); helpers.doKeys('d', 'b'); eq(' word2 ', cm.getValue()); - var register = vim.registerController.getRegister(); + var register = helpers.getRegisterController().getRegister(); eq('word1\n', register.text); is(!register.linewise); eqPos(makeCursor(0, 1), cm.getCursor()); @@ -422,7 +426,7 @@ testVim('dd', function(cm, vim, helpers) { var expectedLineCount = cm.lineCount() - 1; helpers.doKeys('d', 'd'); eq(expectedLineCount, cm.lineCount()); - var register = vim.registerController.getRegister(); + var register = helpers.getRegisterController().getRegister(); eq(expectedBuffer, register.text); is(register.linewise); eqPos(makeCursor(0, lines[1].textStart), cm.getCursor()); @@ -434,7 +438,7 @@ testVim('dd_prefix_repeat', function(cm, vim, helpers) { var expectedLineCount = cm.lineCount() - 2; helpers.doKeys('2', 'd', 'd'); eq(expectedLineCount, cm.lineCount()); - var register = vim.registerController.getRegister(); + var register = helpers.getRegisterController().getRegister(); eq(expectedBuffer, register.text); is(register.linewise); eqPos(makeCursor(0, lines[2].textStart), cm.getCursor()); @@ -446,7 +450,7 @@ testVim('dd_motion_repeat', function(cm, vim, helpers) { var expectedLineCount = cm.lineCount() - 2; helpers.doKeys('d', '2', 'd'); eq(expectedLineCount, cm.lineCount()); - var register = vim.registerController.getRegister(); + var register = helpers.getRegisterController().getRegister(); eq(expectedBuffer, register.text); is(register.linewise); eqPos(makeCursor(0, lines[2].textStart), cm.getCursor()); @@ -458,7 +462,7 @@ testVim('dd_multiply_repeat', function(cm, vim, helpers) { var expectedLineCount = cm.lineCount() - 6; helpers.doKeys('2', 'd', '3', 'd'); eq(expectedLineCount, cm.lineCount()); - var register = vim.registerController.getRegister(); + var register = helpers.getRegisterController().getRegister(); eq(expectedBuffer, register.text); is(register.linewise); eqPos(makeCursor(0, lines[6].textStart), cm.getCursor()); @@ -472,7 +476,7 @@ testVim('yw_repeat', function(cm, vim, helpers) { cm.setCursor(curStart); helpers.doKeys('y', '2', 'w'); eq(' word1\nword2', cm.getValue()); - var register = vim.registerController.getRegister(); + var register = helpers.getRegisterController().getRegister(); eq('word1\nword2', register.text); is(!register.linewise); eqPos(curStart, cm.getCursor()); @@ -485,7 +489,7 @@ testVim('yy_multiply_repeat', function(cm, vim, helpers) { var expectedLineCount = cm.lineCount(); helpers.doKeys('2', 'y', '3', 'y'); eq(expectedLineCount, cm.lineCount()); - var register = vim.registerController.getRegister(); + var register = helpers.getRegisterController().getRegister(); eq(expectedBuffer, register.text); is(register.linewise); eqPos(curStart, cm.getCursor()); @@ -500,7 +504,7 @@ testVim('cw_repeat', function(cm, vim, helpers) { cm.setCursor(curStart); helpers.doKeys('c', '2', 'w'); eq(' ', cm.getValue()); - var register = vim.registerController.getRegister(); + var register = helpers.getRegisterController().getRegister(); eq('word1\nword2', register.text); is(!register.linewise); eqPos(curStart, cm.getCursor()); @@ -513,7 +517,7 @@ testVim('cc_multiply_repeat', function(cm, vim, helpers) { var expectedLineCount = cm.lineCount() - 5; helpers.doKeys('2', 'c', '3', 'c'); eq(expectedLineCount, cm.lineCount()); - var register = vim.registerController.getRegister(); + var register = helpers.getRegisterController().getRegister(); eq(expectedBuffer, register.text); is(register.linewise); eqPos(makeCursor(0, lines[0].textStart), cm.getCursor()); @@ -527,7 +531,7 @@ testVim('g~w_repeat', function(cm, vim, helpers) { cm.setCursor(curStart); helpers.doKeys('g', '~', '2', 'w'); eq(' WORD1\nWORD2', cm.getValue()); - var register = vim.registerController.getRegister(); + var register = helpers.getRegisterController().getRegister(); eq('', register.text); is(!register.linewise); eqPos(curStart, cm.getCursor()); @@ -539,7 +543,7 @@ testVim('g~g~', function(cm, vim, helpers) { var expectedValue = cm.getValue().toUpperCase(); helpers.doKeys('2', 'g', '~', '3', 'g', '~'); eq(expectedValue, cm.getValue()); - var register = vim.registerController.getRegister(); + var register = helpers.getRegisterController().getRegister(); eq('', register.text); is(!register.linewise); eqPos(curStart, cm.getCursor()); @@ -550,7 +554,7 @@ testVim('>{motion}', function(cm, vim, helpers) { var expectedValue = ' word1\n word2\nword3 '; helpers.doKeys('>', 'k'); eq(expectedValue, cm.getValue()); - var register = vim.registerController.getRegister(); + var register = helpers.getRegisterController().getRegister(); eq('', register.text); is(!register.linewise); eqPos(makeCursor(0, 3), cm.getCursor()); @@ -561,7 +565,7 @@ testVim('>>', function(cm, vim, helpers) { var expectedValue = ' word1\n word2\nword3 '; helpers.doKeys('2', '>', '>'); eq(expectedValue, cm.getValue()); - var register = vim.registerController.getRegister(); + var register = helpers.getRegisterController().getRegister(); eq('', register.text); is(!register.linewise); eqPos(makeCursor(0, 3), cm.getCursor()); @@ -572,7 +576,7 @@ testVim('<{motion}', function(cm, vim, helpers) { var expectedValue = ' word1\nword2\nword3 '; helpers.doKeys('<', 'k'); eq(expectedValue, cm.getValue()); - var register = vim.registerController.getRegister(); + var register = helpers.getRegisterController().getRegister(); eq('', register.text); is(!register.linewise); eqPos(makeCursor(0, 1), cm.getCursor()); @@ -583,7 +587,7 @@ testVim('<<', function(cm, vim, helpers) { var expectedValue = ' word1\nword2\nword3 '; helpers.doKeys('2', '<', '<'); eq(expectedValue, cm.getValue()); - var register = vim.registerController.getRegister(); + var register = helpers.getRegisterController().getRegister(); eq('', register.text); is(!register.linewise); eqPos(makeCursor(0, 1), cm.getCursor()); @@ -595,7 +599,7 @@ testVim('D', function(cm, vim, helpers) { cm.setCursor(curStart); helpers.doKeys('D'); eq(' wo\nword2\n word3', cm.getValue()); - var register = vim.registerController.getRegister(); + var register = helpers.getRegisterController().getRegister(); eq('rd1', register.text); is(!register.linewise); eqPos(makeCursor(0, 3), cm.getCursor()); @@ -605,7 +609,7 @@ testVim('C', function(cm, vim, helpers) { cm.setCursor(curStart); helpers.doKeys('C'); eq(' wo\nword2\n word3', cm.getValue()); - var register = vim.registerController.getRegister(); + var register = helpers.getRegisterController().getRegister(); eq('rd1', register.text); is(!register.linewise); eqPos(makeCursor(0, 3), cm.getCursor()); @@ -616,7 +620,7 @@ testVim('Y', function(cm, vim, helpers) { cm.setCursor(curStart); helpers.doKeys('Y'); eq(' word1\nword2\n word3', cm.getValue()); - var register = vim.registerController.getRegister(); + var register = helpers.getRegisterController().getRegister(); eq('rd1', register.text); is(!register.linewise); eqPos(makeCursor(0, 3), cm.getCursor()); @@ -683,42 +687,42 @@ testVim('J_repeat', function(cm, vim, helpers) { }, { value: 'word1 \n word2\nword3\n word4' }); testVim('p', function(cm, vim, helpers) { cm.setCursor(0, 1); - vim.registerController.pushText('"', 'yank', 'abc\ndef', false); + helpers.getRegisterController().pushText('"', 'yank', 'abc\ndef', false); helpers.doKeys('p'); eq('__abc\ndef_', cm.getValue()); eqPos(makeCursor(1, 2), cm.getCursor()); }, { value: '___' }); testVim('p_register', function(cm, vim, helpers) { cm.setCursor(0, 1); - vim.registerController.getRegister('a').set('abc\ndef', false); + helpers.getRegisterController().getRegister('a').set('abc\ndef', false); helpers.doKeys('"', 'a', 'p'); eq('__abc\ndef_', cm.getValue()); eqPos(makeCursor(1, 2), cm.getCursor()); }, { value: '___' }); testVim('p_wrong_register', function(cm, vim, helpers) { cm.setCursor(0, 1); - vim.registerController.getRegister('a').set('abc\ndef', false); + helpers.getRegisterController().getRegister('a').set('abc\ndef', false); helpers.doKeys('p'); eq('___', cm.getValue()); eqPos(makeCursor(0, 1), cm.getCursor()); }, { value: '___' }); testVim('p_line', function(cm, vim, helpers) { cm.setCursor(0, 1); - vim.registerController.pushText('"', 'yank', ' a\nd\n', true); + helpers.getRegisterController().pushText('"', 'yank', ' a\nd\n', true); helpers.doKeys('2', 'p'); eq('___\n a\nd\n a\nd', cm.getValue()); eqPos(makeCursor(1, 2), cm.getCursor()); }, { value: '___' }); testVim('P', function(cm, vim, helpers) { cm.setCursor(0, 1); - vim.registerController.pushText('"', 'yank', 'abc\ndef', false); + helpers.getRegisterController().pushText('"', 'yank', 'abc\ndef', false); helpers.doKeys('P'); eq('_abc\ndef__', cm.getValue()); eqPos(makeCursor(1, 3), cm.getCursor()); }, { value: '___' }); testVim('P_line', function(cm, vim, helpers) { cm.setCursor(0, 1); - vim.registerController.pushText('"', 'yank', ' a\nd\n', true); + helpers.getRegisterController().pushText('"', 'yank', ' a\nd\n', true); helpers.doKeys('2', 'P'); eq(' a\nd\n a\nd\n___', cm.getValue()); eqPos(makeCursor(0, 2), cm.getCursor()); From 623b84f4f3838af4bb28efcdc885765352ca7517 Mon Sep 17 00:00:00 2001 From: Yunchi Luo Date: Thu, 6 Dec 2012 21:00:17 -0500 Subject: [PATCH 0438/5780] [vim keymap] Put underscores after variable name for private variables --- keymap/vim.js | 8 ++++---- test/vim_test.js | 2 +- 2 files changed, 5 insertions(+), 5 deletions(-) diff --git a/keymap/vim.js b/keymap/vim.js index da48b1c83f..6e4b590ed3 100644 --- a/keymap/vim.js +++ b/keymap/vim.js @@ -335,7 +335,7 @@ return getVimGlobalState().registerController; }, // Testing hook. - _clearVimGlobalState: function() { + clearVimGlobalState_: function() { vimGlobalState = null; }, // Initializes vim state variable on the CodeMirror object. Should only be @@ -486,7 +486,7 @@ } else { // Shift down the contents of the numbered registers and put the // deleted text into register 1. - this._shiftNumericRegisters(); + this.shiftNumericRegisters_(); this.registers['1'] = new Register(text, linewise); } break; @@ -523,7 +523,7 @@ isValidRegister: function(name) { return name && inArray(name, validRegisters); }, - _shiftNumericRegisters: function() { + shiftNumericRegisters_: function() { for (var i = 9; i >= 2; i--) { this.registers[i] = this.getRegister('' + (i - 1)); } @@ -1715,7 +1715,7 @@ } function getSearchState(cm) { var vim = getVimState(cm); - return vim._searchState || (vim._searchState = new SearchState()); + return vim.searchState_ || (vim.searchState_ = new SearchState()); } function dialog(cm, text, shortText, callback) { if (cm.openDialog) { diff --git a/test/vim_test.js b/test/vim_test.js index ebdcfe2192..5fe1ab2cf9 100644 --- a/test/vim_test.js +++ b/test/vim_test.js @@ -137,7 +137,7 @@ function testVim(name, run, opts, expectedFail) { return CodeMirror.Vim.getRegisterController(); } } - CodeMirror.Vim._clearVimGlobalState(); + CodeMirror.Vim.clearVimGlobalState_(); var successful = false; try { run(cm, vim, helpers); From 016396a506a5d56f74ffbe06441f666b6a4b1f06 Mon Sep 17 00:00:00 2001 From: Yunchi Luo Date: Thu, 6 Dec 2012 21:57:03 -0500 Subject: [PATCH 0439/5780] [vim keymap] Make query same across cm instances. Update marks on findNext --- keymap/vim.js | 100 +++++++++++++++++++++++++++++++++-------------- test/vim_test.js | 10 +++-- 2 files changed, 77 insertions(+), 33 deletions(-) diff --git a/keymap/vim.js b/keymap/vim.js index 6e4b590ed3..b6b7d39444 100644 --- a/keymap/vim.js +++ b/keymap/vim.js @@ -207,7 +207,7 @@ { keys: ['Ctrl-r'], type: 'action', action: 'redo' }, { keys: ['m', 'character'], type: 'action', action: 'setMark' }, { keys: ['\"', 'character'], type: 'action', action: 'setRegister' }, - { keys: [',', '/'], type: 'action', action: 'clearSearch' }, + { keys: [',', '/'], type: 'action', action: 'clearSearchHighlight' }, // Text object motions { keys: ['a', 'character'], type: 'motion', motion: 'textObjectManipulation' }, @@ -293,6 +293,10 @@ function getVimGlobalState() { if (!vimGlobalState) { vimGlobalState = { + // The current search query. + searchQuery: null, + // Whether we are searching backwards. + searchIsReversed: false, registerController: new RegisterController({}) } } @@ -655,7 +659,7 @@ return; } var forward = command.searchArgs.forward; - getSearchState(cm).reversed = !forward; + getSearchState(cm).setReversed(!forward); var promptPrefix = (forward) ? '/' : '?'; function handleQuery(query) { updateSearchQuery(cm, query); @@ -672,6 +676,9 @@ var word = expandWordUnderCursor(cm, false /** inclusive */, true /** forward */, false /** bigWord */, true /** noSymbol */); + if (!word) { + return; + } var query = cm.getLine(word.start.line).substring(word.start.ch, word.end.ch + 1); query = '\\b' + query + '\\b'; @@ -1024,7 +1031,7 @@ }; var actions = { - clearSearch: clearSearch, + clearSearchHighlight: clearSearchHighlight, enterInsertMode: function(cm, actionArgs) { var insertAt = (actionArgs) ? actionArgs.insertAt : null; if (insertAt == 'eol') { @@ -1709,10 +1716,29 @@ // Search functions function SearchState() { - this.query = null; - this.marked = []; - this.reversed = false; + // Highlighted text that match the query. + this.marked = null; } + SearchState.prototype = { + getQuery: function() { + return getVimGlobalState().query; + }, + setQuery: function(query) { + getVimGlobalState().query = query; + }, + getMarked: function() { + return this.marked; + }, + setMarked: function(marked) { + this.marked = marked; + }, + isReversed: function() { + return getVimGlobalState().isReversed; + }, + setReversed: function(reversed) { + getVimGlobalState().isReversed = reversed; + } + }; function getSearchState(cm) { var vim = getVimState(cm); return vim.searchState_ || (vim.searchState_ = new SearchState()); @@ -1788,48 +1814,59 @@ } return(false); } - function updateSearchQuery(cm, query) { + function updateSearchQuery(cm, rawQuery) { cm.operation(function() { var state = getSearchState(cm); - if (!query) { + if (!rawQuery) { return; } - var newQuery = parseQuery(cm, query); - if (regexEqual(newQuery, state.query)) { + var query = parseQuery(cm, rawQuery); + if (regexEqual(query, state.getQuery())) { return; } - clearSearch(cm); - state.query = newQuery; - if (!state.query) { + if (!query) { return; } - if (cm.lineCount() < 2000) { // This is too expensive on big documents. - for (var cursor = cm.getSearchCursor(state.query); - cursor.findNext();) { - state.marked.push(cm.markText(cursor.from(), cursor.to(), - { className: 'CodeMirror-searching' })); - } - } + clearSearchHighlight(cm); + highlightSearchMatches(cm, query); + state.setQuery(query); }); } + function highlightSearchMatches(cm, query) { + // TODO: Highlight only text inside the viewport. Highlighting everything + // is inefficient and expensive. + if (cm.lineCount() < 2000) { // This is too expensive on big documents. + var marked = []; + for (var cursor = cm.getSearchCursor(query); + cursor.findNext();) { + marked.push(cm.markText(cursor.from(), cursor.to(), + { className: 'CodeMirror-searching' })); + } + getSearchState(cm).setMarked(marked); + } + } function findNext(cm, prev, repeat) { return cm.operation(function() { var state = getSearchState(cm); - if (!state.query) { + var query = state.getQuery(); + if (!query) { return; } + if (!state.getMarked()) { + highlightSearchMatches(cm, query); + } var pos = cm.getCursor(); // If search is initiated with ? instead of /, negate direction. - prev = (state.reversed) ? !prev : prev; + prev = (state.isReversed()) ? !prev : prev; if (!prev) { pos.ch += 1; } - var cursor = cm.getSearchCursor(state.query, pos); + var cursor = cm.getSearchCursor(query, pos); for (var i = 0; i < repeat; i++) { if (!cursor.find(prev)) { // SearchCursor may have returned null because it hit EOF, wrap // around and try again. - cursor = cm.getSearchCursor(state.query, + cursor = cm.getSearchCursor(query, (prev) ? { line: cm.lineCount() - 1} : {line: 0, ch: 0} ); if (!cursor.find(prev)) { return; @@ -1838,17 +1875,20 @@ } return cursor.from(); });} - function clearSearch(cm) { + function clearSearchHighlight(cm) { cm.operation(function() { var state = getSearchState(cm); - if (!state.query) { + if (!state.getQuery()) { + return; + } + var marked = state.getMarked(); + if (!marked) { return; } - state.query = null; - for (var i = 0; i < state.marked.length; ++i) { - state.marked[i].clear(); + for (var i = 0; i < marked.length; ++i) { + marked[i].clear(); } - state.marked.length = 0; + state.setMarked(null); });} function buildVimKeyMap() { diff --git a/test/vim_test.js b/test/vim_test.js index 5fe1ab2cf9..c2b8bb7441 100644 --- a/test/vim_test.js +++ b/test/vim_test.js @@ -764,12 +764,11 @@ testVim('? and n/N', function(cm, vim, helpers) { helpers.doKeys('2', '?'); helpers.assertCursorAt(makeCursor(0, 11)); }, { value: 'match nope match \n nope Match' }); -testVim(',/ clearSearch', function(cm, vim, helpers) { +testVim(',/ clearSearchHighlight', function(cm, vim, helpers) { cm.openDialog = helpers.fakeOpenDialog('match'); helpers.doKeys('?'); - var origPos = cm.getCursor(); helpers.doKeys(',', '/', 'n'); - helpers.assertCursorAt(origPos); + helpers.assertCursorAt(0, 11); }, { value: 'match nope match \n nope Match' }); testVim('*', function(cm, vim, helpers) { cm.setCursor(0, 9); @@ -780,6 +779,11 @@ testVim('*', function(cm, vim, helpers) { helpers.doKeys('2', '*'); helpers.assertCursorAt(makeCursor(1, 8)); }, { value: 'nomatch match nomatch match \nnomatch Match' }); +testVim('*_no_word', function(cm, vim, helpers) { + cm.setCursor(0, 0); + helpers.doKeys('*'); + helpers.assertCursorAt(0, 0); +}, { value: ' \n match \n' }); testVim('#', function(cm, vim, helpers) { cm.setCursor(0, 9); helpers.doKeys('#'); From dff894df670d063619c3147a7380829e9dfa0555 Mon Sep 17 00:00:00 2001 From: James Campos Date: Thu, 6 Dec 2012 06:57:13 -0800 Subject: [PATCH 0440/5780] chmod -x mode/properties/* --- mode/properties/index.html | 0 mode/properties/properties.js | 0 2 files changed, 0 insertions(+), 0 deletions(-) mode change 100755 => 100644 mode/properties/index.html mode change 100755 => 100644 mode/properties/properties.js diff --git a/mode/properties/index.html b/mode/properties/index.html old mode 100755 new mode 100644 diff --git a/mode/properties/properties.js b/mode/properties/properties.js old mode 100755 new mode 100644 From c59d7b2ce51d2dfd34887af83919e1e3da87c0a5 Mon Sep 17 00:00:00 2001 From: Yunchi Luo Date: Wed, 5 Dec 2012 21:41:54 -0500 Subject: [PATCH 0441/5780] [vim keymap] Improvements to visual mode. - Fix bug where visual mode cannot be entered from EOL. - When already in characterwise visual mode, pressing Shift-V now enters linewise visual mode instead of exiting visual mode. - Mouse selection in normal mode enables visual mode. --- keymap/vim.js | 44 ++++++++++++++++++++++++++++++++++---------- 1 file changed, 34 insertions(+), 10 deletions(-) diff --git a/keymap/vim.js b/keymap/vim.js index b6b7d39444..124a871d33 100644 --- a/keymap/vim.js +++ b/keymap/vim.js @@ -365,6 +365,11 @@ // The selection was cleared. Exit visual mode. exitVisualMode(cm, vim); } + if (!vim.visualMode && + !cursorEqual(cm.getCursor('head'), cm.getCursor('anchor'))) { + vim.visualMode = true; + vim.visualLine = false; + } if (key != '0' || (key == '0' && vim.inputState.getRepeat() === 0)) { // Have to special case 0 since it's both a motion and a number. command = commandDispatcher.matchCommand(key, defaultKeymap, vim); @@ -1047,23 +1052,23 @@ var repeat = actionArgs.repeat; var curStart = cm.getCursor(); var curEnd; - vim.visualLine = !!actionArgs.linewise; // TODO: The repeat should actually select number of characters/lines // equal to the repeat times the size of the previous visual // operation. if (!vim.visualMode) { vim.visualMode = true; + vim.visualLine = !!actionArgs.linewise; if (vim.visualLine) { curStart.ch = 0; curEnd = clipCursorToContent(cm, { line: curStart.line + repeat - 1, ch: lineLength(cm, curStart.line) - }); + }, true /** includeLineBreak */); } else { curEnd = clipCursorToContent(cm, { line: curStart.line, ch: curStart.ch + repeat - }); + }, true /** includeLineBreak */); } // Make the initial selection. if (!actionArgs.repeatIsExplicit && !vim.visualLine) { @@ -1076,7 +1081,20 @@ cm.setSelection(curStart, curEnd); } } else { - exitVisualMode(cm, vim); + if (!vim.visualLine && actionArgs.linewise) { + // Shift-V pressed in characterwise visual mode. Switch to linewise + // visual mode instead of exiting visual mode. + vim.visualLine = true; + curStart = cm.getCursor('anchor'); + curEnd = cm.getCursor('head'); + curStart.ch = cursorIsBefore(curStart, curEnd) ? 0 : + lineLength(cm, curStart.line); + curEnd.ch = cursorIsBefore(curStart, curEnd) ? + lineLength(cm, curEnd.line) : 0; + cm.setSelection(curStart, curEnd); + } else { + exitVisualMode(cm, vim); + } } }, joinLines: function(cm, actionArgs, vim) { @@ -1236,12 +1254,18 @@ * Below are miscellaneous utility functions used by vim.js */ - // Clips cursor @cur to ensure - // that 0 <= cur.ch < line length and - // 0 <= cur.line < total number of lines - function clipCursorToContent(cm, cur) { + /** + * Clips cursor to ensure that: + * 0 <= cur.ch < lineLength + * AND + * 0 <= cur.line < lineCount + * If includeLineBreak is true, then allow cur.ch == lineLength. + */ + function clipCursorToContent(cm, cur, includeLineBreak) { var line = Math.min(Math.max(0, cur.line), cm.lineCount() - 1); - var ch = Math.min(Math.max(0, cur.ch), lineLength(cm, line) - 1); + var maxCh = lineLength(cm, line) - 1; + maxCh = (includeLineBreak) ? maxCh + 1 : maxCh; + var ch = Math.min(Math.max(0, cur.ch), maxCh); return { line: line, ch: ch }; } // Merge arguments in place, for overriding arguments. @@ -1332,7 +1356,7 @@ // Clear the selection and set the cursor only if the selection has not // already been cleared. Otherwise we risk moving the cursor somewhere // it's not supposed to be. - cm.setCursor(selectionEnd); + cm.setCursor(clipCursorToContent(cm, selectionEnd)); } } From a7c53fdd9f4df13693a49d04aea7f398406a7e8d Mon Sep 17 00:00:00 2001 From: Marijn Haverbeke Date: Fri, 7 Dec 2012 21:07:58 +0100 Subject: [PATCH 0442/5780] Don't assume textarea.form will stay the same in fromTextArea Closes #1056 --- lib/codemirror.js | 10 +++++----- 1 file changed, 5 insertions(+), 5 deletions(-) diff --git a/lib/codemirror.js b/lib/codemirror.js index d11b0b6040..27a8b83ae6 100644 --- a/lib/codemirror.js +++ b/lib/codemirror.js @@ -3101,13 +3101,13 @@ window.CodeMirror = (function() { if (textarea.form) { // Deplorable hack to make the submit method do the right thing. on(textarea.form, "submit", save); - var realSubmit = textarea.form.submit; + var form = textarea.form, realSubmit = form.submit; try { - textarea.form.submit = function wrappedSubmit() { + form.submit = function wrappedSubmit() { save(); - textarea.form.submit = realSubmit; - textarea.form.submit(); - textarea.form.submit = wrappedSubmit; + form.submit = realSubmit; + form.submit(); + form.submit = wrappedSubmit; }; } catch(e) {} } From 99ed2b4f2676553422d24fecb4ea2e0b3953130e Mon Sep 17 00:00:00 2001 From: Marijn Haverbeke Date: Mon, 10 Dec 2012 10:30:02 +0100 Subject: [PATCH 0443/5780] Add examples to v3 upgrade guide --- doc/upgrade_v3.html | 57 ++++++++++++++++++++++++++++++++++++++++++--- 1 file changed, 54 insertions(+), 3 deletions(-) diff --git a/doc/upgrade_v3.html b/doc/upgrade_v3.html index f9a9553e36..3af3ee750d 100644 --- a/doc/upgrade_v3.html +++ b/doc/upgrade_v3.html @@ -35,9 +35,9 @@

    DOM structure

    structure of the editor has changed quite a lot, mostly to implement a new scrolling model.

    -

    Editor height is now set on the outer wrapper (CSS -class CodeMirror) element, not on the scroller -(CodeMirror-scroll) element.

    +

    Editor height is now set on the outer wrapper element (CSS +class CodeMirror), not on the scroller element +(CodeMirror-scroll).

    Other nodes were moved, dropped, and added. If you have any code that makes assumptions about the internal DOM structure of the editor, @@ -64,6 +64,22 @@

    Gutter model

    The fixedGutter option was removed (since it is now the only behavior).

    +
    +<style>
    +  /* Define a gutter style */
    +  .note-gutter { width: 3em; background: cyan; }
    +</style>
    +<script>
    +  // Create an instance with two gutters -- line numbers and notes
    +  var cm = new CodeMirror(document.body, {
    +    gutters: ["note-gutter", "CodeMirror-linenumbers"],
    +    lineNumbers: true
    +  });
    +  // Add a note to line 0
    +  cm.setGutterMarker(0, "note-gutter", document.createTextNode("hi"));
    +</script>
    +
    +

    Event handling

    Most of the onXYZ options have been removed. The same @@ -78,6 +94,12 @@

    Event handling

    which act more as hooks than as event handlers, are still there in their old form.)

    +
    +cm.on("change", function(cm, change) {
    +  console.log("something changed! (" + change.origin + ")");
    +});
    +
    +

    markText method arguments

    The markText method @@ -86,6 +108,15 @@

    markText method arguments

    takes the CSS class name as a separate argument, but makes it an optional field in the options object instead.

    +
    +// Style first ten lines, and forbid the cursor from entering them
    +cm.markText({line: 0, ch: 0}, {line: 10, ch: 0}, {
    +  className: "magic-text",
    +  inclusiveLeft: true,
    +  atomic: true
    +});
    +
    +

    Line folding

    The interface for hiding lines has been @@ -95,6 +126,19 @@

    Line folding

    The folding script has been updated to use the new interface, and should now be more robust.

    +
    +// Fold a range, replacing it with the text "??"
    +var range = cm.markText({line: 4, ch: 2}, {line: 8, ch: 1}, {
    +  replacedWith: document.createTextNode("??"),
    +  // Auto-unfold when cursor moves into the range
    +  clearOnEnter: true
    +});
    +// Get notified when auto-unfolding
    +CodeMirror.on(range, "clear", function() {
    +  console.log("boom");
    +});
    +
    +

    Line CSS classes

    The setLineClass method has been replaced @@ -102,6 +146,13 @@

    Line CSS classes

    and removeLineClass, which allow more modular control over the classes attached to a line.

    +
    +var marked = cm.addLineClass(10, "background", "highlighted-line");
    +setTimeout(function() {
    +  cm.removeLineClass(marked, "background", "highlighted-line");
    +});
    +
    +

    Position properties

    All methods that take or return objects that represent screen From 40e5bae3fcc0b58cb241ac301b08669820fbe647 Mon Sep 17 00:00:00 2001 From: Marijn Haverbeke Date: Mon, 10 Dec 2012 11:12:21 +0100 Subject: [PATCH 0444/5780] [util/runmode] Move from innerHTML to document.create* To work around IE innerHTML bugs --- lib/util/runmode.js | 39 +++++++++++++++++++-------------------- 1 file changed, 19 insertions(+), 20 deletions(-) diff --git a/lib/util/runmode.js b/lib/util/runmode.js index 327976badf..3e1bed7361 100644 --- a/lib/util/runmode.js +++ b/lib/util/runmode.js @@ -1,43 +1,44 @@ CodeMirror.runMode = function(string, modespec, callback, options) { - function esc(str) { - return str.replace(/[<&]/g, function(ch) { return ch == "<" ? "<" : "&"; }); - } - var mode = CodeMirror.getMode(CodeMirror.defaults, modespec); - var isNode = callback.nodeType == 1; - var tabSize = (options && options.tabSize) || CodeMirror.defaults.tabSize; - if (isNode) { - var node = callback, accum = [], col = 0; + + if (callback.nodeType == 1) { + var tabSize = (options && options.tabSize) || CodeMirror.defaults.tabSize; + var node = callback, col = 0; + node.innerHTML = ""; callback = function(text, style) { if (text == "\n") { - accum.push("
    "); + node.appendChild(document.createElement("br")); col = 0; return; } - var escaped = ""; - // HTML-escape and replace tabs + var content = ""; + // replace tabs for (var pos = 0;;) { var idx = text.indexOf("\t", pos); if (idx == -1) { - escaped += esc(text.slice(pos)); + content += text.slice(pos); col += text.length - pos; break; } else { col += idx - pos; - escaped += esc(text.slice(pos, idx)); + content += text.slice(pos, idx); var size = tabSize - col % tabSize; col += size; - for (var i = 0; i < size; ++i) escaped += " "; + for (var i = 0; i < size; ++i) content += " "; pos = idx + 1; } } - if (style) - accum.push("" + escaped + ""); - else - accum.push(escaped); + if (style) { + var sp = node.appendChild(document.createElement("span")); + sp.className = "cm-" + style.replace(/ +/g, " cm-"); + sp.appendChild(document.createTextNode(content)); + } else { + node.appendChild(document.createTextNode(content)); + } }; } + var lines = CodeMirror.splitLines(string), state = CodeMirror.startState(mode); for (var i = 0, e = lines.length; i < e; ++i) { if (i) callback("\n"); @@ -48,6 +49,4 @@ CodeMirror.runMode = function(string, modespec, callback, options) { stream.start = stream.pos; } } - if (isNode) - node.innerHTML = accum.join(""); }; From 4bfa0b594f912045579b5700e7043cdfdeb9cc9b Mon Sep 17 00:00:00 2001 From: Marijn Haverbeke Date: Mon, 10 Dec 2012 11:13:10 +0100 Subject: [PATCH 0445/5780] Add a syntax highlighting shim for PRE tags, use in manual --- doc/manual.html | 20 +++++++++++++++----- doc/upgrade_v3.html | 19 ++++++++++++++----- lib/util/colorize.js | 29 +++++++++++++++++++++++++++++ 3 files changed, 58 insertions(+), 10 deletions(-) create mode 100644 lib/util/colorize.js diff --git a/doc/manual.html b/doc/manual.html index bb544cdcbd..811acaee45 100644 --- a/doc/manual.html +++ b/doc/manual.html @@ -6,6 +6,14 @@ + + + + + + + + @@ -48,21 +56,21 @@

    Basic Usage

    (See the compression helper for an easy way to combine scripts.) For example:

    -
    <script src="lib/codemirror.js"></script>
    +    
    <script src="lib/codemirror.js"></script>
     <link rel="stylesheet" href="../lib/codemirror.css">
     <script src="mode/javascript/javascript.js"></script>

    Having done this, an editor instance can be created like this:

    -
    var myCodeMirror = CodeMirror(document.body);
    +
    var myCodeMirror = CodeMirror(document.body);

    The editor will be appended to the document body, will start empty, and will use the mode that we loaded. To have more control over the new editor, a configuration object can be passed to CodeMirror as a second argument:

    -
    var myCodeMirror = CodeMirror(document.body, {
    +    
    var myCodeMirror = CodeMirror(document.body, {
       value: "function myScript(){return 100;}\n",
       mode:  "javascript"
     });
    @@ -80,7 +88,7 @@

    Basic Usage

    document somewhere. This could be used to, for example, replace a textarea with a real editor:

    -
    var myCodeMirror = CodeMirror(function(elt) {
    +    
    var myCodeMirror = CodeMirror(function(elt) {
       myTextArea.parentNode.replaceChild(elt, myTextArea);
     }, {value: myTextArea.value});
    @@ -88,7 +96,7 @@

    Basic Usage

    CodeMirror, the library provides a much more powerful shortcut:

    -
    var myCodeMirror = CodeMirror.fromTextArea(myTextArea);
    +
    var myCodeMirror = CodeMirror.fromTextArea(myTextArea);

    This will, among other things, ensure that the textarea's value is updated with the editor's contents when the form (if it is part @@ -1449,5 +1457,7 @@

    Contents

     
    + + diff --git a/doc/upgrade_v3.html b/doc/upgrade_v3.html index 3af3ee750d..eaaffec17f 100644 --- a/doc/upgrade_v3.html +++ b/doc/upgrade_v3.html @@ -5,6 +5,14 @@ CodeMirror: Upgrading to v3 + + + + + + + + @@ -64,7 +72,7 @@

    Gutter model

    The fixedGutter option was removed (since it is now the only behavior).

    -
    +
     <style>
       /* Define a gutter style */
       .note-gutter { width: 3em; background: cyan; }
    @@ -94,7 +102,7 @@ 

    Event handling

    which act more as hooks than as event handlers, are still there in their old form.)

    -
    +
     cm.on("change", function(cm, change) {
       console.log("something changed! (" + change.origin + ")");
     });
    @@ -108,7 +116,7 @@ 

    markText method arguments

    takes the CSS class name as a separate argument, but makes it an optional field in the options object instead.

    -
    +
     // Style first ten lines, and forbid the cursor from entering them
     cm.markText({line: 0, ch: 0}, {line: 10, ch: 0}, {
       className: "magic-text",
    @@ -126,7 +134,7 @@ 

    Line folding

    The folding script has been updated to use the new interface, and should now be more robust.

    -
    +
     // Fold a range, replacing it with the text "??"
     var range = cm.markText({line: 4, ch: 2}, {line: 8, ch: 1}, {
       replacedWith: document.createTextNode("??"),
    @@ -146,7 +154,7 @@ 

    Line CSS classes

    and removeLineClass, which allow more modular control over the classes attached to a line.

    -
    +
     var marked = cm.addLineClass(10, "background", "highlighted-line");
     setTimeout(function() {
       cm.removeLineClass(marked, "background", "highlighted-line");
    @@ -214,5 +222,6 @@ 

    Contents

    + diff --git a/lib/util/colorize.js b/lib/util/colorize.js new file mode 100644 index 0000000000..62286d21e4 --- /dev/null +++ b/lib/util/colorize.js @@ -0,0 +1,29 @@ +CodeMirror.colorize = (function() { + + var isBlock = /^(p|li|div|h\\d|pre|blockquote|td)$/; + + function textContent(node, out) { + if (node.nodeType == 3) return out.push(node.nodeValue); + for (var ch = node.firstChild; ch; ch = ch.nextSibling) { + textContent(ch, out); + if (isBlock.test(node.nodeType)) out.push("\n"); + } + } + + return function(collection, defaultMode) { + if (!collection) collection = document.body.getElementsByTagName("pre"); + + for (var i = 0; i < collection.length; ++i) { + var node = collection[i]; + var mode = node.getAttribute("data-lang") || defaultMode; + if (!mode) continue; + + var text = []; + textContent(node, text); + node.innerHTML = ""; + CodeMirror.runMode(text.join(""), mode, node); + + node.className += " cm-s-default"; + } + }; +})(); From 3e088ab300484b3a1880f22cf7b13892b6ae2715 Mon Sep 17 00:00:00 2001 From: Marijn Haverbeke Date: Mon, 10 Dec 2012 11:31:34 +0100 Subject: [PATCH 0446/5780] [clike mode] Add statementIndentUnit config option To specify the amount that continued statements have to be indented. Closes #1053 --- mode/clike/clike.js | 8 ++++++-- 1 file changed, 6 insertions(+), 2 deletions(-) diff --git a/mode/clike/clike.js b/mode/clike/clike.js index 2b0c64dc5f..4d1484a708 100644 --- a/mode/clike/clike.js +++ b/mode/clike/clike.js @@ -1,5 +1,6 @@ CodeMirror.defineMode("clike", function(config, parserConfig) { var indentUnit = config.indentUnit, + statementIndentUnit = parserConfig.statementIndentUnit || indentUnit, keywords = parserConfig.keywords || {}, builtin = parserConfig.builtin || {}, blockKeywords = parserConfig.blockKeywords || {}, @@ -89,7 +90,10 @@ CodeMirror.defineMode("clike", function(config, parserConfig) { this.prev = prev; } function pushContext(state, col, type) { - return state.context = new Context(state.indented, col, type, null, state.context); + var indent = state.indented; + if (state.context && state.context.type == "statement") + indent = state.context.indented; + return state.context = new Context(indent, col, type, null, state.context); } function popContext(state) { var t = state.context.type; @@ -144,7 +148,7 @@ CodeMirror.defineMode("clike", function(config, parserConfig) { var ctx = state.context, firstChar = textAfter && textAfter.charAt(0); if (ctx.type == "statement" && firstChar == "}") ctx = ctx.prev; var closing = firstChar == ctx.type; - if (ctx.type == "statement") return ctx.indented + (firstChar == "{" ? 0 : indentUnit); + if (ctx.type == "statement") return ctx.indented + (firstChar == "{" ? 0 : statementIndentUnit); else if (ctx.align) return ctx.column + (closing ? 0 : 1); else return ctx.indented + (closing ? 0 : indentUnit); }, From 0682373fb8ea950d1e44a2e4af5b60631f7248a7 Mon Sep 17 00:00:00 2001 From: Yunchi Luo Date: Sat, 8 Dec 2012 13:45:40 -0500 Subject: [PATCH 0447/5780] Add an option to show dialog at the bottom. --- lib/util/dialog.css | 19 ++++++++++++------- lib/util/dialog.js | 23 ++++++++++++++--------- 2 files changed, 26 insertions(+), 16 deletions(-) diff --git a/lib/util/dialog.css b/lib/util/dialog.css index 8c4f847920..2e7c0fc9b8 100644 --- a/lib/util/dialog.css +++ b/lib/util/dialog.css @@ -1,18 +1,23 @@ .CodeMirror-dialog { - position: relative; -} - -.CodeMirror-dialog > div { position: absolute; - top: 0; left: 0; right: 0; + left: 0; right: 0; background: white; - border-bottom: 1px solid #eee; z-index: 15; padding: .1em .8em; overflow: hidden; color: #333; } +.CodeMirror-dialog-top { + border-bottom: 1px solid #eee; + top: 0; +} + +.CodeMirror-dialog-bottom { + border-top: 1px solid #eee; + bottom: 0; +} + .CodeMirror-dialog input { border: none; outline: none; @@ -24,4 +29,4 @@ .CodeMirror-dialog button { font-size: 70%; -} \ No newline at end of file +} diff --git a/lib/util/dialog.js b/lib/util/dialog.js index 1d4751623b..845a3ea946 100644 --- a/lib/util/dialog.js +++ b/lib/util/dialog.js @@ -1,16 +1,21 @@ // Open simple dialogs on top of an editor. Relies on dialog.css. (function() { - function dialogDiv(cm, template) { + function dialogDiv(cm, template, bottom) { var wrap = cm.getWrapperElement(); - var dialog = wrap.insertBefore(document.createElement("div"), wrap.firstChild); - dialog.className = "CodeMirror-dialog"; - dialog.innerHTML = '
    ' + template + '
    '; + var dialog; + dialog = wrap.appendChild(document.createElement("div")); + if (bottom) { + dialog.className = "CodeMirror-dialog CodeMirror-dialog-bottom"; + } else { + dialog.className = "CodeMirror-dialog CodeMirror-dialog-top"; + } + dialog.innerHTML = template; return dialog; } - CodeMirror.defineExtension("openDialog", function(template, callback) { - var dialog = dialogDiv(this, template); + CodeMirror.defineExtension("openDialog", function(template, callback, bottom) { + var dialog = dialogDiv(this, template, bottom); var closed = false, me = this; function close() { if (closed) return; @@ -40,8 +45,8 @@ return close; }); - CodeMirror.defineExtension("openConfirm", function(template, callbacks) { - var dialog = dialogDiv(this, template); + CodeMirror.defineExtension("openConfirm", function(template, callbacks, bottom) { + var dialog = dialogDiv(this, template, bottom); var buttons = dialog.getElementsByTagName("button"); var closed = false, me = this, blurring = 1; function close() { @@ -67,4 +72,4 @@ CodeMirror.on(b, "focus", function() { ++blurring; }); } }); -})(); \ No newline at end of file +})(); From 38d0c3240ee3f1d7db98fb233ed22c048c4c16c5 Mon Sep 17 00:00:00 2001 From: Yunchi Luo Date: Sat, 8 Dec 2012 13:47:18 -0500 Subject: [PATCH 0448/5780] [vim keymap] Move dialog to bottom of editor --- keymap/vim.js | 9 +++++---- 1 file changed, 5 insertions(+), 4 deletions(-) diff --git a/keymap/vim.js b/keymap/vim.js index 124a871d33..27c97a304e 100644 --- a/keymap/vim.js +++ b/keymap/vim.js @@ -1769,7 +1769,7 @@ } function dialog(cm, text, shortText, callback) { if (cm.openDialog) { - cm.openDialog(text, callback); + cm.openDialog(text, callback, true /** bottom */); } else { callback(prompt(shortText, "")); @@ -1801,7 +1801,8 @@ function showConfirm(cm, text) { if (cm.openConfirm) { cm.openConfirm('' + text + - ' ', function() {}); + ' ', function() {}, + true /** bottom */); } else { alert(text); } @@ -1809,9 +1810,9 @@ function makePrompt(prefix, desc) { var raw = ''; if (prefix) { - raw += prefix; + raw += '' + prefix + ''; } - raw += ' ' + + raw += ' ' + ''; if (desc) { raw += ''; From 0a83f784c5e635abc6c77fbc9a97f1e690c58a2f Mon Sep 17 00:00:00 2001 From: Marijn Haverbeke Date: Mon, 10 Dec 2012 11:39:35 +0100 Subject: [PATCH 0449/5780] Make dialog methods take an option object instead of a boolean parameter Issue #1065 --- keymap/vim.js | 4 ++-- lib/util/dialog.js | 8 ++++---- 2 files changed, 6 insertions(+), 6 deletions(-) diff --git a/keymap/vim.js b/keymap/vim.js index 27c97a304e..659a6658e9 100644 --- a/keymap/vim.js +++ b/keymap/vim.js @@ -1769,7 +1769,7 @@ } function dialog(cm, text, shortText, callback) { if (cm.openDialog) { - cm.openDialog(text, callback, true /** bottom */); + cm.openDialog(text, callback, {bottom: true}); } else { callback(prompt(shortText, "")); @@ -1802,7 +1802,7 @@ if (cm.openConfirm) { cm.openConfirm('' + text + ' ', function() {}, - true /** bottom */); + {bottom: true}); } else { alert(text); } diff --git a/lib/util/dialog.js b/lib/util/dialog.js index 845a3ea946..380b80455c 100644 --- a/lib/util/dialog.js +++ b/lib/util/dialog.js @@ -14,8 +14,8 @@ return dialog; } - CodeMirror.defineExtension("openDialog", function(template, callback, bottom) { - var dialog = dialogDiv(this, template, bottom); + CodeMirror.defineExtension("openDialog", function(template, callback, options) { + var dialog = dialogDiv(this, template, options && options.bottom); var closed = false, me = this; function close() { if (closed) return; @@ -45,8 +45,8 @@ return close; }); - CodeMirror.defineExtension("openConfirm", function(template, callbacks, bottom) { - var dialog = dialogDiv(this, template, bottom); + CodeMirror.defineExtension("openConfirm", function(template, callbacks, options) { + var dialog = dialogDiv(this, template, options && options.bottom); var buttons = dialog.getElementsByTagName("button"); var closed = false, me = this, blurring = 1; function close() { From 6da60252625bd1a24be688834d11c6f5205434ad Mon Sep 17 00:00:00 2001 From: Yunchi Luo Date: Sat, 8 Dec 2012 13:21:12 -0500 Subject: [PATCH 0450/5780] [vim keymap] Improvements to * and #. * and # will fall back to non-blank characters if no keyword is found. --- keymap/vim.js | 91 +++++++++++++++++++++++++++++++++++------------- test/vim_test.js | 5 +++ 2 files changed, 72 insertions(+), 24 deletions(-) diff --git a/keymap/vim.js b/keymap/vim.js index 659a6658e9..aae8a29e44 100644 --- a/keymap/vim.js +++ b/keymap/vim.js @@ -298,7 +298,7 @@ // Whether we are searching backwards. searchIsReversed: false, registerController: new RegisterController({}) - } + }; } return vimGlobalState; } @@ -666,29 +666,43 @@ var forward = command.searchArgs.forward; getSearchState(cm).setReversed(!forward); var promptPrefix = (forward) ? '/' : '?'; - function handleQuery(query) { - updateSearchQuery(cm, query); + function handleQuery(query, ignoreCase, smartCase) { + updateSearchQuery(cm, query, ignoreCase, smartCase); commandDispatcher.processMotion(cm, vim, { type: 'motion', motion: 'findNext' }); } + function onPromptClose(query) { + handleQuery(query, true /** ignoreCase */, true /** smartCase */); + } switch (command.searchArgs.querySrc) { case 'prompt': - showPrompt(cm, handleQuery, promptPrefix, searchPromptDesc); + showPrompt(cm, onPromptClose, promptPrefix, searchPromptDesc); break; case 'wordUnderCursor': var word = expandWordUnderCursor(cm, false /** inclusive */, true /** forward */, false /** bigWord */, true /** noSymbol */); + var isKeyword = true; + if (!word) { + word = expandWordUnderCursor(cm, false /** inclusive */, + true /** forward */, false /** bigWord */, + false /** noSymbol */); + isKeyword = false; + } if (!word) { return; } var query = cm.getLine(word.start.line).substring(word.start.ch, word.end.ch + 1); - query = '\\b' + query + '\\b'; + if (isKeyword) { + query = '\\b' + query + '\\b'; + } else { + query = escapeRegex(query); + } cm.setCursor(word.start); - handleQuery(query); + handleQuery(query, true /** ignoreCase */, false /** smartCase */); break; } }, @@ -1344,7 +1358,7 @@ return s.split("").reverse().join(""); } function escapeRegex(s) { - return s.replace(/([.?*+\^$\[\]\\(){}|\-])/g, "\\$1"); + return s.replace(/([.?*+$\[\]\/\\(){}|\-])/g, "\\$1"); } function exitVisualMode(cm, vim) { @@ -1775,27 +1789,56 @@ callback(prompt(shortText, "")); } } - function parseQuery(cm, query) { + function findUnescapedSlashes(str) { + var escapeNextChar = false; + var slashes = []; + for (var i = 0; i < str.length; i++) { + var c = str.charAt(i); + if (!escapeNextChar && c == '/') { + slashes.push(i); + } + escapeNextChar = (c == '\\'); + } + return slashes; + } + /** + * Extract the regular expression from the query and return a Regexp object. + * Returns null if the query is blank. + * If ignoreCase is passed in, the Regexp object will have the 'i' flag set. + * If smartCase is passed in, and the query contains upper case letters, + * then ignoreCase is overridden, and the 'i' flag will not be set. + * If the query contains the /i in the flag part of the regular expression, + * then both ignoreCase and smartCase are ignored, and 'i' will be passed + * through to the Regex object. + */ + function parseQuery(cm, query, ignoreCase, smartCase) { // First try to extract regex + flags from the input. If no flags found, // extract just the regex. IE does not accept flags directly defined in // the regex string in the form /regex/flags - var match = query.match(/^(.*)\/(.*)$/); - var insensitive = false; - var query_regex; - if (match) { - insensitive = (match[2].indexOf('i') != -1); - query_regex = match[1]; + var slashes = findUnescapedSlashes(query); + var regexPart; + var forceIgnoreCase; + if (!slashes.length) { + // Query looks like 'regexp' + regexPart = query; } else { - query_regex = query; + // Query looks like 'regexp/...' + regexPart = query.substring(0, slashes[0]); + var flagsPart = query.substring(slashes[0]); + forceIgnoreCase = (flagsPart.indexOf('i') != -1); + } + if (!regexPart) { + return null; + } + if (smartCase) { + ignoreCase = (/^[^A-Z]*$/).test(regexPart); } - // Heuristic: if the query string is all lowercase, do a case-insensitive - // search. - insensitive = insensitive || (/^[^A-Z]*$/).test(query_regex); try { - var regexp = new RegExp(query_regex, insensitive ? 'i' : undefined); + var regexp = new RegExp(regexPart, + (ignoreCase || forceIgnoreCase) ? 'i' : undefined); return regexp; } catch (e) { - showConfirm(cm, 'Invalid regex: ' + query_regex); + showConfirm(cm, 'Invalid regex: ' + regexPart); } } function showConfirm(cm, text) { @@ -1839,17 +1882,17 @@ } return(false); } - function updateSearchQuery(cm, rawQuery) { + function updateSearchQuery(cm, rawQuery, ignoreCase, smartCase) { cm.operation(function() { var state = getSearchState(cm); if (!rawQuery) { return; } - var query = parseQuery(cm, rawQuery); - if (regexEqual(query, state.getQuery())) { + var query = parseQuery(cm, rawQuery, !!ignoreCase, !!smartCase); + if (!query) { return; } - if (!query) { + if (regexEqual(query, state.getQuery())) { return; } clearSearchHighlight(cm); diff --git a/test/vim_test.js b/test/vim_test.js index c2b8bb7441..f8c13d64a4 100644 --- a/test/vim_test.js +++ b/test/vim_test.js @@ -784,6 +784,11 @@ testVim('*_no_word', function(cm, vim, helpers) { helpers.doKeys('*'); helpers.assertCursorAt(0, 0); }, { value: ' \n match \n' }); +testVim('*_symbol', function(cm, vim, helpers) { + cm.setCursor(0, 0); + helpers.doKeys('*'); + helpers.assertCursorAt(1, 0); +}, { value: ' /}\n/} match \n' }); testVim('#', function(cm, vim, helpers) { cm.setCursor(0, 9); helpers.doKeys('#'); From fb4b667fb4c1b61cac86cfd6344ed70dce25571b Mon Sep 17 00:00:00 2001 From: Yunchi Luo Date: Sat, 8 Dec 2012 17:29:20 -0500 Subject: [PATCH 0451/5780] [vim keymap] Rudimentary Ex command implementation --- keymap/vim.js | 130 ++++++++++++++++++++++++++++++++++++++++++++++++-- 1 file changed, 126 insertions(+), 4 deletions(-) diff --git a/keymap/vim.js b/keymap/vim.js index aae8a29e44..b400606a41 100644 --- a/keymap/vim.js +++ b/keymap/vim.js @@ -121,10 +121,10 @@ motion: 'moveByPage', motionArgs: { forward: false }}, { keys: ['g', 'g'], type: 'motion', motion: 'moveToLineOrEdgeOfDocument', - motionArgs: { forward: false, explicitRepeat: true }}, + motionArgs: { forward: false, explicitRepeat: true, linewise: true }}, { keys: ['G'], type: 'motion', motion: 'moveToLineOrEdgeOfDocument', - motionArgs: { forward: true, explicitRepeat: true }}, + motionArgs: { forward: true, explicitRepeat: true, linewise: true }}, { keys: ['0'], type: 'motion', motion: 'moveToStartOfLine' }, { keys: ['^'], type: 'motion', motion: 'moveToFirstNonWhiteSpaceCharacter' }, @@ -222,7 +222,9 @@ { keys: ['*'], type: 'search', searchArgs: { forward: true, querySrc: 'wordUnderCursor' }}, { keys: ['#'], type: 'search', - searchArgs: { forward: false, querySrc: 'wordUnderCursor' }} + searchArgs: { forward: false, querySrc: 'wordUnderCursor' }}, + // Ex command + { keys: [':'], type: 'ex' }, ]; var Vim = function() { @@ -588,6 +590,8 @@ case 'search': this.processSearch(cm, vim, command); break; + case 'ex': + this.processEx(cm, vim, command); default: break; } @@ -706,6 +710,12 @@ break; } }, + processEx: function(cm, vim, command) { + function onPromptClose(input) { + exCommandDispatcher.processCommand(cm, input); + } + showPrompt(cm, onPromptClose, ':'); + }, evalInput: function(cm, vim) { // If the motion comand is set, execute both the operator and motion. // Otherwise return. @@ -722,7 +732,14 @@ var curStart = copyCursor(selectionEnd); var curOriginal = copyCursor(curStart); var curEnd; - var repeat = inputState.getRepeat(); + var repeat; + if (motionArgs.repeat !== undefined) { + // If motionArgs specifies a repeat, that takes precedence over the + // input state's repeat. Used by Ex mode and can be user defined. + repeat = inputState.motionArgs.repeat; + } else { + repeat = inputState.getRepeat(); + } if (repeat > 0 && motionArgs.explicitRepeat) { motionArgs.repeatIsExplicit = true; } else if (motionArgs.noRepeat || @@ -1395,6 +1412,9 @@ } function findFirstNonWhiteSpaceCharacter(text) { + if (!text) { + return 0; + } var firstNonWS = text.search(/\S/); return firstNonWS == -1 ? text.length : firstNonWS; } @@ -1959,6 +1979,108 @@ state.setMarked(null); });} + // Ex command handling + // Care must be taken when adding to the default Ex command map. For any + // pair of commands that have a shared prefix, at least one of their + // shortNames must not match the prefix of the other command. + var defaultExCommandMap = [ + { name: 'write', shortName: 'w' }, + { name: 'undo', shortName: 'u' }, + { name: 'redo', shortName: 'red' } + ]; + var ExCommandDispatcher = function() { + this.buildCommandMap_(); + }; + ExCommandDispatcher.prototype = { + processCommand: function(cm, input) { + var parsedCommand = this.parseInput_(input); + var commandName; + if (!parsedCommand.commandName) { + // If only a line range is defined, move to the line. + if (parsedCommand.line !== undefined) { + commandName = 'move'; + } + } else { + var command = this.matchCommand_(parsedCommand.commandName); + if (command) { + commandName = command.name; + } + } + if (!commandName) { + showConfirm(cm, 'Not an editor command: ' + input); + return; + } + exCommands[commandName](cm, parsedCommand); + }, + parseInput_: function(input) { + var result = {}; + // Trim preceding ':'. + var colons = (/^:+/).exec(input); + if (colons) { + input = input.substring(colons[0].length); + } + + // Parse range. + var numberMatch = (/^(\d+)/).exec(input); + if (numberMatch) { + result['line'] = parseInt(numberMatch[1]); + input = input.substring(numberMatch[0].length); + } + // Parse command name. + var commandMatch = (/(\w+)/).exec(input); + if (commandMatch) { + result['commandName'] = commandMatch[1]; + } + return result; + }, + matchCommand_: function(commandName) { + // Return the command in the command map that matches the shortest + // prefix of the passed in command name. The match is guaranteed to be + // unambiguous if the defaultExCommandMap's shortNames are set up + // correctly. (see @code{defaultExCommandMap}). + for (var i = 1; i <= commandName.length; i++) { + var prefix = commandName.substring(0, i); + if (this.commandMap_[prefix]) { + var command = this.commandMap_[prefix]; + if (command.name.indexOf(commandName) === 0) { + return command; + } + } + } + return null; + }, + buildCommandMap_: function() { + this.commandMap_ = {}; + for (var i = 0; i < defaultExCommandMap.length; i++) { + var command = defaultExCommandMap[i]; + this.commandMap_[command.shortName] = command; + } + } + }; + + var exCommands = { + move: function(cm, args) { + commandDispatcher.processMotion(cm, getVimState(cm), { + motion: 'moveToLineOrEdgeOfDocument', + motionArgs: { forward: false, explicitRepeat: true, + linewise: true, repeat: args.line }}); + }, + redo: CodeMirror.commands.redo, + undo: CodeMirror.commands.undo, + write: function(cm) { + if (CodeMirror.commands.save) { + // If a save command is defined, call it. + CodeMirror.commands.save(cm); + } else { + // Saves to text area if no save command is defined. + cm.save(); + } + } + }; + + var exCommandDispatcher = new ExCommandDispatcher(); + + // Register Vim with CodeMirror function buildVimKeyMap() { /** * Handle the raw key event from CodeMirror. Translate the From 2f3d73602b08fd9b12afdcc8855cee8a8193e471 Mon Sep 17 00:00:00 2001 From: Yunchi Luo Date: Sun, 9 Dec 2012 19:54:50 -0500 Subject: [PATCH 0452/5780] [vim keymap] Add basic :map command --- keymap/vim.js | 131 ++++++++++++++++++++++++++++++++++++++++---------- 1 file changed, 106 insertions(+), 25 deletions(-) diff --git a/keymap/vim.js b/keymap/vim.js index b400606a41..e1e957acdc 100644 --- a/keymap/vim.js +++ b/keymap/vim.js @@ -224,7 +224,7 @@ { keys: ['#'], type: 'search', searchArgs: { forward: false, querySrc: 'wordUnderCursor' }}, // Ex command - { keys: [':'], type: 'ex' }, + { keys: [':'], type: 'ex' } ]; var Vim = function() { @@ -328,10 +328,6 @@ } var vimApi= { - addKeyMap: function() { - // Add user defined key bindings. - // TODO: Implement this. - }, buildKeyMap: function() { // TODO: Convert keymap into dictionary format for fast lookup. }, @@ -344,6 +340,10 @@ clearVimGlobalState_: function() { vimGlobalState = null; }, + map: function(lhs, rhs) { + // Add user defined key bindings. + exCommandDispatcher.map(lhs, rhs); + }, // Initializes vim state variable on the CodeMirror object. Should only be // called lazily by handleKey or for testing. maybeInitState: function(cm) { @@ -591,7 +591,9 @@ this.processSearch(cm, vim, command); break; case 'ex': + case 'keyToEx': this.processEx(cm, vim, command); + break; default: break; } @@ -714,7 +716,12 @@ function onPromptClose(input) { exCommandDispatcher.processCommand(cm, input); } - showPrompt(cm, onPromptClose, ':'); + if (command.type == 'keyToEx') { + // Handle user defined Ex to Ex mappings + exCommandDispatcher.processCommand(cm, command.exArgs.input); + } else { + showPrompt(cm, onPromptClose, ':'); + } }, evalInput: function(cm, vim) { // If the motion comand is set, execute both the operator and motion. @@ -1374,6 +1381,13 @@ function reverse(s){ return s.split("").reverse().join(""); } + function trim(s) { + if (s.trim) { + return s.trim(); + } else { + return s.replace(/^\s+|\s+$/g, ''); + } + } function escapeRegex(s) { return s.replace(/([.?*+$\[\]\/\\(){}|\-])/g, "\\$1"); } @@ -1984,53 +1998,76 @@ // pair of commands that have a shared prefix, at least one of their // shortNames must not match the prefix of the other command. var defaultExCommandMap = [ - { name: 'write', shortName: 'w' }, - { name: 'undo', shortName: 'u' }, - { name: 'redo', shortName: 'red' } + { name: 'map', type: 'builtIn' }, + { name: 'write', shortName: 'w', type: 'builtIn' }, + { name: 'undo', shortName: 'u', type: 'builtIn' }, + { name: 'redo', shortName: 'red', type: 'builtIn' } ]; var ExCommandDispatcher = function() { this.buildCommandMap_(); }; ExCommandDispatcher.prototype = { processCommand: function(cm, input) { - var parsedCommand = this.parseInput_(input); + var params = this.parseInput_(input); var commandName; - if (!parsedCommand.commandName) { + if (!params.commandName) { // If only a line range is defined, move to the line. - if (parsedCommand.line !== undefined) { + if (params.line !== undefined) { commandName = 'move'; } } else { - var command = this.matchCommand_(parsedCommand.commandName); + var command = this.matchCommand_(params.commandName); if (command) { commandName = command.name; + if (command.type == 'exToKey') { + // Handle Ex to Key mapping. + for (var i = 0; i < command.toKeys.length; i++) { + vim.handleKey(cm, command.toKeys[i]); + } + return; + } else if (command.type == 'exToEx') { + // Handle Ex to Ex mapping. + this.processCommand(cm, command.toInput); + return; + } } } if (!commandName) { - showConfirm(cm, 'Not an editor command: ' + input); + showConfirm(cm, 'Not an editor command ":' + input + '"'); return; } - exCommands[commandName](cm, parsedCommand); + exCommands[commandName](cm, params); }, parseInput_: function(input) { var result = {}; + result.input = input; + var idx = 0; // Trim preceding ':'. var colons = (/^:+/).exec(input); if (colons) { - input = input.substring(colons[0].length); + idx += colons[0].length; } // Parse range. - var numberMatch = (/^(\d+)/).exec(input); + var numberMatch = (/^(\d+)/).exec(input.substring(idx)); if (numberMatch) { - result['line'] = parseInt(numberMatch[1]); - input = input.substring(numberMatch[0].length); + result.line = parseInt(numberMatch[1], 10); + idx += numberMatch[0].length; } + // Parse command name. - var commandMatch = (/(\w+)/).exec(input); + var commandMatch = (/^(\w+)/).exec(input.substring(idx)); if (commandMatch) { - result['commandName'] = commandMatch[1]; + result.commandName = commandMatch[1]; + idx += commandMatch[1].length; + } + + // Parse command-line arguments + var args = trim(input.substring(idx)).split(/\s+/); + if (args.length && args[0]) { + result.commandArgs = args; } + return result; }, matchCommand_: function(commandName) { @@ -2038,7 +2075,7 @@ // prefix of the passed in command name. The match is guaranteed to be // unambiguous if the defaultExCommandMap's shortNames are set up // correctly. (see @code{defaultExCommandMap}). - for (var i = 1; i <= commandName.length; i++) { + for (var i = commandName.length; i > 0; i--) { var prefix = commandName.substring(0, i); if (this.commandMap_[prefix]) { var command = this.commandMap_[prefix]; @@ -2053,17 +2090,61 @@ this.commandMap_ = {}; for (var i = 0; i < defaultExCommandMap.length; i++) { var command = defaultExCommandMap[i]; - this.commandMap_[command.shortName] = command; + var key = command.shortName || command.name; + this.commandMap_[key] = command; + } + }, + map: function(lhs, rhs) { + if (lhs.charAt(0) == ':') { + var commandName = lhs.substring(1); + if (rhs.charAt(0) == ':') { + // Ex to Ex mapping + this.commandMap_[commandName] = { + name: commandName, + type: 'exToEx', + toInput: rhs.substring(1) + }; + } else { + // Ex to key mapping + this.commandMap_[commandName] = { + name: commandName, + type: 'exToKey', + toKeys: rhs + }; + } + } else { + if (rhs.charAt(0) == ':') { + // Key to Ex mapping. + defaultKeymap.unshift({ + keys: lhs.split(''), + type: 'keyToEx', + exArgs: { input: rhs.substring(1) }}); + } else { + // Key to key mapping + defaultKeymap.unshift({ + keys: lhs.split(''), type: 'keyToKey', toKeys: rhs.split('') + }); + } } } }; var exCommands = { - move: function(cm, args) { + map: function(cm, params) { + var mapArgs = params.commandArgs; + if (!mapArgs || mapArgs.length < 2) { + if (cm) { + showConfirm(cm, 'Invalid mapping: ' + params.input); + } + return; + } + exCommandDispatcher.map(mapArgs[0], mapArgs[1], cm); + }, + move: function(cm, params) { commandDispatcher.processMotion(cm, getVimState(cm), { motion: 'moveToLineOrEdgeOfDocument', motionArgs: { forward: false, explicitRepeat: true, - linewise: true, repeat: args.line }}); + linewise: true, repeat: params.line }}); }, redo: CodeMirror.commands.redo, undo: CodeMirror.commands.undo, From ee4b750b272a54ba101042b8f4e72d559ae02d69 Mon Sep 17 00:00:00 2001 From: Yunchi Luo Date: Sun, 9 Dec 2012 20:51:28 -0500 Subject: [PATCH 0453/5780] [vim keymap] Add Vim key notation support --- keymap/vim.js | 47 ++++++++++++++++++++++++++++++++++++++++++++--- 1 file changed, 44 insertions(+), 3 deletions(-) diff --git a/keymap/vim.js b/keymap/vim.js index e1e957acdc..aa1856518b 100644 --- a/keymap/vim.js +++ b/keymap/vim.js @@ -2109,26 +2109,67 @@ this.commandMap_[commandName] = { name: commandName, type: 'exToKey', - toKeys: rhs + toKeys: parseKeyString(rhs) }; } } else { if (rhs.charAt(0) == ':') { // Key to Ex mapping. defaultKeymap.unshift({ - keys: lhs.split(''), + keys: parseKeyString(lhs), type: 'keyToEx', exArgs: { input: rhs.substring(1) }}); } else { // Key to key mapping defaultKeymap.unshift({ - keys: lhs.split(''), type: 'keyToKey', toKeys: rhs.split('') + keys: parseKeyString(lhs), + type: 'keyToKey', + toKeys: parseKeyString(rhs) }); } } } }; + // Converts a key string sequence of the form abd into Vim's + // keymap representation. + function parseKeyString(str) { + var idx = 0; + var keys = []; + while (idx < str.length) { + if (str.charAt(idx) != '<') { + keys.push(str.charAt(idx)); + idx++; + continue; + } + // Vim key notation here means desktop Vim key-notation. + // See :help key-notation in desktop Vim. + var vimKeyNotationStart = ++idx; + while (str.charAt(idx++) != '>') {} + var vimKeyNotation = str.substring(vimKeyNotationStart, idx - 1); + var match = (/^C-(.+)$/).exec(vimKeyNotation); + if (match) { + var key; + switch (match[1]) { + case 'BS': + key = 'Backspace'; + break; + case 'CR': + key = 'Enter'; + break; + case 'Del': + key = 'Delete'; + break; + default: + key = match[1]; + break; + } + keys.push('Ctrl-' + key); + } + } + return keys; + } + var exCommands = { map: function(cm, params) { var mapArgs = params.commandArgs; From a9c40d02c569768754401e55e13ee0ead9005646 Mon Sep 17 00:00:00 2001 From: Maximilian Hils Date: Sat, 8 Dec 2012 19:18:03 +0100 Subject: [PATCH 0454/5780] Simple Hint: pass through given options --- lib/util/simple-hint.js | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/lib/util/simple-hint.js b/lib/util/simple-hint.js index f08f311e2f..1565bd4785 100644 --- a/lib/util/simple-hint.js +++ b/lib/util/simple-hint.js @@ -18,7 +18,7 @@ return; } - var result = getHints(editor); + var result = getHints(editor, givenOptions); if (!result || !result.list.length) return; var completions = result.list; function insert(str) { From 5497f6881806a74592ed4e0b08c7000e822fcb18 Mon Sep 17 00:00:00 2001 From: Marijn Haverbeke Date: Mon, 10 Dec 2012 11:56:09 +0100 Subject: [PATCH 0455/5780] [javascript hinter] Support additionalContext option Issue #1066 --- lib/util/javascript-hint.js | 27 +++++++++++++++------------ 1 file changed, 15 insertions(+), 12 deletions(-) diff --git a/lib/util/javascript-hint.js b/lib/util/javascript-hint.js index 22edf5f3fb..07caba8766 100644 --- a/lib/util/javascript-hint.js +++ b/lib/util/javascript-hint.js @@ -16,7 +16,7 @@ return arr.indexOf(item) != -1; } - function scriptHint(editor, keywords, getToken) { + function scriptHint(editor, keywords, getToken, options) { // Find the token at the cursor var cur = editor.getCursor(), token = getToken(editor, cur), tprop = token; // If it's not a 'word-style' token, ignore the token. @@ -47,14 +47,15 @@ if (!context) var context = []; context.push(tprop); } - return {list: getCompletions(token, context, keywords), + return {list: getCompletions(token, context, keywords, options), from: {line: cur.line, ch: token.start}, to: {line: cur.line, ch: token.end}}; } - CodeMirror.javascriptHint = function(editor) { + CodeMirror.javascriptHint = function(editor, options) { return scriptHint(editor, javascriptKeywords, - function (e, cur) {return e.getTokenAt(cur);}); + function (e, cur) {return e.getTokenAt(cur);}, + options); }; function getCoffeeScriptToken(editor, cur) { @@ -75,8 +76,8 @@ return token; } - CodeMirror.coffeescriptHint = function(editor) { - return scriptHint(editor, coffeescriptKeywords, getCoffeeScriptToken); + CodeMirror.coffeescriptHint = function(editor, options) { + return scriptHint(editor, coffeescriptKeywords, getCoffeeScriptToken, options); }; var stringProps = ("charAt charCodeAt indexOf lastIndexOf substring substr slice trim trimLeft trimRight " + @@ -89,7 +90,7 @@ var coffeescriptKeywords = ("and break catch class continue delete do else extends false finally for " + "if in instanceof isnt new no not null of off on or return switch then throw true try typeof until void while with yes").split(" "); - function getCompletions(token, context, keywords) { + function getCompletions(token, context, keywords, options) { var found = [], start = token.string; function maybeAdd(str) { if (str.indexOf(start) == 0 && !arrayContains(found, str)) found.push(str); @@ -105,13 +106,15 @@ // If this is a property, see if it belongs to some object we can // find in the current environment. var obj = context.pop(), base; - if (obj.type == "variable") - base = window[obj.string]; - else if (obj.type == "string") + if (obj.type == "variable") { + if (options && options.additionalContext) + base = options.additionalContext[obj.string]; + base = base || window[obj.string]; + } else if (obj.type == "string") { base = ""; - else if (obj.type == "atom") + } else if (obj.type == "atom") { base = 1; - else if (obj.type == "function") { + } else if (obj.type == "function") { if (window.jQuery != null && (obj.string == '$' || obj.string == 'jQuery') && (typeof window.jQuery == 'function')) base = window.jQuery(); From aa347a319fe938f1aed0f27439649ff24c6d2565 Mon Sep 17 00:00:00 2001 From: Marijn Haverbeke Date: Mon, 10 Dec 2012 12:07:39 +0100 Subject: [PATCH 0456/5780] Don't autofocus on mobile devices --- lib/codemirror.js | 6 ++++-- 1 file changed, 4 insertions(+), 2 deletions(-) diff --git a/lib/codemirror.js b/lib/codemirror.js index 27a8b83ae6..8e1b6b8c58 100644 --- a/lib/codemirror.js +++ b/lib/codemirror.js @@ -21,6 +21,8 @@ window.CodeMirror = (function() { var phantom = /PhantomJS/.test(navigator.userAgent); var ios = /AppleWebKit/.test(navigator.userAgent) && /Mobile\/\w+/.test(navigator.userAgent); + // This is woefully incomplete. Suggestions for alternative methods welcome. + var mobile = ios || /Android|webOS|BlackBerry|Opera Mini|IEMobile/i.test(navigator.userAgent); var mac = ios || /Mac/.test(navigator.platform); // Optimize some code when these features are not used @@ -40,7 +42,7 @@ window.CodeMirror = (function() { var display = this.display = makeDisplay(place); display.wrapper.CodeMirror = this; updateGutters(this); - if (options.autofocus) focusInput(this); + if (options.autofocus && !mobile) focusInput(this); this.view = makeView(new BranchChunk([new LeafChunk([makeLine("", null, textHeight(display))])])); this.nextOpId = 0; @@ -60,7 +62,7 @@ window.CodeMirror = (function() { // IE throws unspecified error in certain cases, when // trying to access activeElement before onload var hasFocus; try { hasFocus = (document.activeElement == display.input); } catch(e) { } - if (hasFocus || options.autofocus) setTimeout(bind(onFocus, this), 20); + if (hasFocus || (options.autofocus && !mobile)) setTimeout(bind(onFocus, this), 20); else onBlur(this); operation(this, function() { From 02fdac636a8833bcee99ad6d27d56c444f22c304 Mon Sep 17 00:00:00 2001 From: Brandon Frohs Date: Mon, 10 Dec 2012 00:21:16 -0500 Subject: [PATCH 0457/5780] Add contributing guidelines See https://github.com/blog/1184-contributing-guidelines --- CONTRIBUTING.md | 80 +++++++++++++++++++++++++++++++++++++++++++++++++ README.md | 5 ++-- 2 files changed, 83 insertions(+), 2 deletions(-) create mode 100644 CONTRIBUTING.md diff --git a/CONTRIBUTING.md b/CONTRIBUTING.md new file mode 100644 index 0000000000..d3e5c5d525 --- /dev/null +++ b/CONTRIBUTING.md @@ -0,0 +1,80 @@ +# How to contribute + +- [Getting help](#getting-help-) +- [Submitting bug reports](#submitting-bug-reports-) +- [Contributing code](#contributing-code-) + +## Getting help [^](#how-to-contribute) + +Community discussion, questions, and informal bug reporting is done on the +[CodeMirror Google group](http://groups.google.com/group/codemirror). + +## Submitting bug reports [^](#how-to-contribute) + +The preferred way to report bugs is to use the +[GitHub issue tracker](http://github.com/marijnh/CodeMirror/issues). Before +reporting a bug, read these pointers. + +**Note:** The issue tracker is for *bugs*, not requests for help. Questions +should be asked on the +[CodeMirror Google group](http://groups.google.com/group/codemirror) instead. + +### Reporting bugs effectively + +- CodeMirror is maintained by volunteers. They don't owe you anything, so be + polite. Reports with an indignant or belligerent tone tend to be moved to the + bottom of the pile. + +- Include information about **the browser in which the problem occurred**. Even + if you tested several browsers, and the problem occurred in all of them, + mention this fact in the bug report. Also include browser version numbers and + the operating system that you're on. + +- Mention which release of CodeMirror you're using. Preferably, try also with + the current development snapshot, to ensure the problem has not already been + fixed. + +- Mention very precisely what went wrong. "X is broken" is not a good bug + report. What did you expect to happen? What happened instead? Describe the + exact steps a maintainer has to take to make the problem occur. We can not + fix something that we can not observe. + +- If the problem can not be reproduced in any of the demos included in the + CodeMirror distribution, please provide an HTML document that demonstrates + the problem. The best way to do this is to go to + [jsbin.com](http://jsbin.com/ihunin/edit), enter it there, press save, and + include the resulting link in your bug report. + +## Contributing code [^](#how-to-contribute) + +- Make sure you have a [GitHub Account](https://github.com/signup/free) +- Fork [CodeMirror](https://github.com/marijnh/CodeMirror/) + ([how to fork a repo](https://help.github.com/articles/fork-a-repo)) +- Make your changes + - If your change affects highlighting for one of the modes, please [add (or + change) tests](#adding-mode-highlighting-tests) for the changes. If the mode + doesn't already have highlighting tests, you *aren't* required to add any. +- Test your changes + -Visit `/path-to-code/test/index.html` to test your code. *All tests should + pass*. +- Submit a pull request +([how to create a pull request](https://help.github.com/articles/fork-a-repo)) + +### Adding mode highlighting tests + +- Create a `test.js` file in the corresponding mode directory + ([example](https://github.com/marijnh/CodeMirror/blob/master/mode/markdown/test.js)) +- Add script tags to `/test/index.html` to include the formatting code and + as well as the tests. +- Run the tests! + +### Code formatting standards + +- 2 spaces (no tabs) +- Wrap to 80 characters when possible (unless it affects readability negatively) +- No trailing whitespace + - Blank lines should be indented as if there *is* text on them +- Spacing + - `function someFunction(someVar, someOtherVar) {` + - `if (someVar === true) doThis(someVar, someOtherVar);` + - `if (!someVar || someOtherVar === 0) {` diff --git a/README.md b/README.md index 8ed9871a46..3e87272fd8 100644 --- a/README.md +++ b/README.md @@ -4,5 +4,6 @@ CodeMirror is a JavaScript component that provides a code editor in the browser. When a mode is available for the language you are coding in, it will color your code, and optionally help with indentation. -The project page is http://codemirror.net -The manual is at http://codemirror.net/doc/manual.html +The project page is http://codemirror.net +The manual is at http://codemirror.net/doc/manual.html +The contributing guidelines are in the CONTRIBUTING.md file From ecfe3d55df4b211439914d23afbfcf43fce03026 Mon Sep 17 00:00:00 2001 From: Marijn Haverbeke Date: Mon, 10 Dec 2012 12:20:47 +0100 Subject: [PATCH 0458/5780] Modify CONTRIBUTING.md Don't emphasize mode test suites and coding standards so much, they are not a big issue. --- CONTRIBUTING.md | 36 +++++++++++++----------------------- 1 file changed, 13 insertions(+), 23 deletions(-) diff --git a/CONTRIBUTING.md b/CONTRIBUTING.md index d3e5c5d525..afc18373cc 100644 --- a/CONTRIBUTING.md +++ b/CONTRIBUTING.md @@ -51,30 +51,20 @@ should be asked on the - Fork [CodeMirror](https://github.com/marijnh/CodeMirror/) ([how to fork a repo](https://help.github.com/articles/fork-a-repo)) - Make your changes - - If your change affects highlighting for one of the modes, please [add (or - change) tests](#adding-mode-highlighting-tests) for the changes. If the mode - doesn't already have highlighting tests, you *aren't* required to add any. -- Test your changes - -Visit `/path-to-code/test/index.html` to test your code. *All tests should - pass*. +- If your changes are easy to test or likely to regress, add tests. + Tests for the core go into `test/test.js`, some modes have their own + test suite under `mode/XXX/test.js`. Feel free to add new test + suites to modes that don't have one yet (be sure to link the new + tests into `test/index.html`). +- Make sure all tests pass. Visit `test/index.html` in your browser to + run them. - Submit a pull request ([how to create a pull request](https://help.github.com/articles/fork-a-repo)) -### Adding mode highlighting tests +### Coding standards -- Create a `test.js` file in the corresponding mode directory - ([example](https://github.com/marijnh/CodeMirror/blob/master/mode/markdown/test.js)) -- Add script tags to `/test/index.html` to include the formatting code and - as well as the tests. -- Run the tests! - -### Code formatting standards - -- 2 spaces (no tabs) -- Wrap to 80 characters when possible (unless it affects readability negatively) -- No trailing whitespace - - Blank lines should be indented as if there *is* text on them -- Spacing - - `function someFunction(someVar, someOtherVar) {` - - `if (someVar === true) doThis(someVar, someOtherVar);` - - `if (!someVar || someOtherVar === 0) {` +- 2 spaces per indentation level, no tabs. +- Include semicolons after statements. +- Note that the linter (`test/lint/lint.js`) which is run after each + commit complains about unused variables and functions. Prefix their + names with an underscore to muffle it. From d9b8b19a5284a5a51befa6ff3e4ee4efaf440293 Mon Sep 17 00:00:00 2001 From: Brandon Frohs Date: Mon, 10 Dec 2012 00:01:09 -0500 Subject: [PATCH 0459/5780] Force correct line endings in text files --- .gitattributes | 8 ++++++++ 1 file changed, 8 insertions(+) create mode 100644 .gitattributes diff --git a/.gitattributes b/.gitattributes new file mode 100644 index 0000000000..f8bdd60f49 --- /dev/null +++ b/.gitattributes @@ -0,0 +1,8 @@ +*.txt text +*.js text +*.html text +*.md text +*.json text +*.yml text +*.css text +*.svg text From 7033793a94caed124b96191849e6d94dea4d72df Mon Sep 17 00:00:00 2001 From: Brandon Frohs Date: Mon, 10 Dec 2012 00:23:13 -0500 Subject: [PATCH 0460/5780] Normalize line endings --- mode/ecl/ecl.js | 384 ++++++++++++++++++++-------------------- mode/ecl/index.html | 78 ++++---- mode/verilog/verilog.js | 364 ++++++++++++++++++------------------- 3 files changed, 413 insertions(+), 413 deletions(-) diff --git a/mode/ecl/ecl.js b/mode/ecl/ecl.js index b1c8088655..3ee7a681de 100644 --- a/mode/ecl/ecl.js +++ b/mode/ecl/ecl.js @@ -1,192 +1,192 @@ -CodeMirror.defineMode("ecl", function(config) { - - function words(str) { - var obj = {}, words = str.split(" "); - for (var i = 0; i < words.length; ++i) obj[words[i]] = true; - return obj; - } - - function metaHook(stream, state) { - if (!state.startOfLine) return false; - stream.skipToEnd(); - return "meta"; - } - - var indentUnit = config.indentUnit; - var keyword = words("abs acos allnodes ascii asin asstring atan atan2 ave case choose choosen choosesets clustersize combine correlation cos cosh count covariance cron dataset dedup define denormalize distribute distributed distribution ebcdic enth error evaluate event eventextra eventname exists exp failcode failmessage fetch fromunicode getisvalid global graph group hash hash32 hash64 hashcrc hashmd5 having if index intformat isvalid iterate join keyunicode length library limit ln local log loop map matched matchlength matchposition matchtext matchunicode max merge mergejoin min nolocal nonempty normalize parse pipe power preload process project pull random range rank ranked realformat recordof regexfind regexreplace regroup rejected rollup round roundup row rowdiff sample set sin sinh sizeof soapcall sort sorted sqrt stepped stored sum table tan tanh thisnode topn tounicode transfer trim truncate typeof ungroup unicodeorder variance which workunit xmldecode xmlencode xmltext xmlunicode"); - var variable = words("apply assert build buildindex evaluate fail keydiff keypatch loadxml nothor notify output parallel sequential soapcall wait"); - var variable_2 = words("__compressed__ all and any as atmost before beginc++ best between case const counter csv descend encrypt end endc++ endmacro except exclusive expire export extend false few first flat from full function group header heading hole ifblock import in interface joined keep keyed last left limit load local locale lookup macro many maxcount maxlength min skew module named nocase noroot noscan nosort not of only opt or outer overwrite packed partition penalty physicallength pipe quote record relationship repeat return right scan self separator service shared skew skip sql store terminator thor threshold token transform trim true type unicodeorder unsorted validate virtual whole wild within xml xpath"); - var variable_3 = words("ascii big_endian boolean data decimal ebcdic integer pattern qstring real record rule set of string token udecimal unicode unsigned varstring varunicode"); - var builtin = words("checkpoint deprecated failcode failmessage failure global independent onwarning persist priority recovery stored success wait when"); - var blockKeywords = words("catch class do else finally for if switch try while"); - var atoms = words("true false null"); - var hooks = {"#": metaHook}; - var multiLineStrings; - var isOperatorChar = /[+\-*&%=<>!?|\/]/; - - var curPunc; - - function tokenBase(stream, state) { - var ch = stream.next(); - if (hooks[ch]) { - var result = hooks[ch](stream, state); - if (result !== false) return result; - } - if (ch == '"' || ch == "'") { - state.tokenize = tokenString(ch); - return state.tokenize(stream, state); - } - if (/[\[\]{}\(\),;\:\.]/.test(ch)) { - curPunc = ch; - return null; - } - if (/\d/.test(ch)) { - stream.eatWhile(/[\w\.]/); - return "number"; - } - if (ch == "/") { - if (stream.eat("*")) { - state.tokenize = tokenComment; - return tokenComment(stream, state); - } - if (stream.eat("/")) { - stream.skipToEnd(); - return "comment"; - } - } - if (isOperatorChar.test(ch)) { - stream.eatWhile(isOperatorChar); - return "operator"; - } - stream.eatWhile(/[\w\$_]/); - var cur = stream.current().toLowerCase(); - if (keyword.propertyIsEnumerable(cur)) { - if (blockKeywords.propertyIsEnumerable(cur)) curPunc = "newstatement"; - return "keyword"; - } else if (variable.propertyIsEnumerable(cur)) { - if (blockKeywords.propertyIsEnumerable(cur)) curPunc = "newstatement"; - return "variable"; - } else if (variable_2.propertyIsEnumerable(cur)) { - if (blockKeywords.propertyIsEnumerable(cur)) curPunc = "newstatement"; - return "variable-2"; - } else if (variable_3.propertyIsEnumerable(cur)) { - if (blockKeywords.propertyIsEnumerable(cur)) curPunc = "newstatement"; - return "variable-3"; - } else if (builtin.propertyIsEnumerable(cur)) { - if (blockKeywords.propertyIsEnumerable(cur)) curPunc = "newstatement"; - return "builtin"; - } else { //Data types are of from KEYWORD## - var i = cur.length - 1; - while(i >= 0 && (!isNaN(cur[i]) || cur[i] == '_')) - --i; - - if (i > 0) { - var cur2 = cur.substr(0, i + 1); - if (variable_3.propertyIsEnumerable(cur2)) { - if (blockKeywords.propertyIsEnumerable(cur2)) curPunc = "newstatement"; - return "variable-3"; - } - } - } - if (atoms.propertyIsEnumerable(cur)) return "atom"; - return null; - } - - function tokenString(quote) { - return function(stream, state) { - var escaped = false, next, end = false; - while ((next = stream.next()) != null) { - if (next == quote && !escaped) {end = true; break;} - escaped = !escaped && next == "\\"; - } - if (end || !(escaped || multiLineStrings)) - state.tokenize = tokenBase; - return "string"; - }; - } - - function tokenComment(stream, state) { - var maybeEnd = false, ch; - while (ch = stream.next()) { - if (ch == "/" && maybeEnd) { - state.tokenize = tokenBase; - break; - } - maybeEnd = (ch == "*"); - } - return "comment"; - } - - function Context(indented, column, type, align, prev) { - this.indented = indented; - this.column = column; - this.type = type; - this.align = align; - this.prev = prev; - } - function pushContext(state, col, type) { - return state.context = new Context(state.indented, col, type, null, state.context); - } - function popContext(state) { - var t = state.context.type; - if (t == ")" || t == "]" || t == "}") - state.indented = state.context.indented; - return state.context = state.context.prev; - } - - // Interface - - return { - startState: function(basecolumn) { - return { - tokenize: null, - context: new Context((basecolumn || 0) - indentUnit, 0, "top", false), - indented: 0, - startOfLine: true - }; - }, - - token: function(stream, state) { - var ctx = state.context; - if (stream.sol()) { - if (ctx.align == null) ctx.align = false; - state.indented = stream.indentation(); - state.startOfLine = true; - } - if (stream.eatSpace()) return null; - curPunc = null; - var style = (state.tokenize || tokenBase)(stream, state); - if (style == "comment" || style == "meta") return style; - if (ctx.align == null) ctx.align = true; - - if ((curPunc == ";" || curPunc == ":") && ctx.type == "statement") popContext(state); - else if (curPunc == "{") pushContext(state, stream.column(), "}"); - else if (curPunc == "[") pushContext(state, stream.column(), "]"); - else if (curPunc == "(") pushContext(state, stream.column(), ")"); - else if (curPunc == "}") { - while (ctx.type == "statement") ctx = popContext(state); - if (ctx.type == "}") ctx = popContext(state); - while (ctx.type == "statement") ctx = popContext(state); - } - else if (curPunc == ctx.type) popContext(state); - else if (ctx.type == "}" || ctx.type == "top" || (ctx.type == "statement" && curPunc == "newstatement")) - pushContext(state, stream.column(), "statement"); - state.startOfLine = false; - return style; - }, - - indent: function(state, textAfter) { - if (state.tokenize != tokenBase && state.tokenize != null) return 0; - var ctx = state.context, firstChar = textAfter && textAfter.charAt(0); - if (ctx.type == "statement" && firstChar == "}") ctx = ctx.prev; - var closing = firstChar == ctx.type; - if (ctx.type == "statement") return ctx.indented + (firstChar == "{" ? 0 : indentUnit); - else if (ctx.align) return ctx.column + (closing ? 0 : 1); - else return ctx.indented + (closing ? 0 : indentUnit); - }, - - electricChars: "{}" - }; -}); - -CodeMirror.defineMIME("text/x-ecl", "ecl"); +CodeMirror.defineMode("ecl", function(config) { + + function words(str) { + var obj = {}, words = str.split(" "); + for (var i = 0; i < words.length; ++i) obj[words[i]] = true; + return obj; + } + + function metaHook(stream, state) { + if (!state.startOfLine) return false; + stream.skipToEnd(); + return "meta"; + } + + var indentUnit = config.indentUnit; + var keyword = words("abs acos allnodes ascii asin asstring atan atan2 ave case choose choosen choosesets clustersize combine correlation cos cosh count covariance cron dataset dedup define denormalize distribute distributed distribution ebcdic enth error evaluate event eventextra eventname exists exp failcode failmessage fetch fromunicode getisvalid global graph group hash hash32 hash64 hashcrc hashmd5 having if index intformat isvalid iterate join keyunicode length library limit ln local log loop map matched matchlength matchposition matchtext matchunicode max merge mergejoin min nolocal nonempty normalize parse pipe power preload process project pull random range rank ranked realformat recordof regexfind regexreplace regroup rejected rollup round roundup row rowdiff sample set sin sinh sizeof soapcall sort sorted sqrt stepped stored sum table tan tanh thisnode topn tounicode transfer trim truncate typeof ungroup unicodeorder variance which workunit xmldecode xmlencode xmltext xmlunicode"); + var variable = words("apply assert build buildindex evaluate fail keydiff keypatch loadxml nothor notify output parallel sequential soapcall wait"); + var variable_2 = words("__compressed__ all and any as atmost before beginc++ best between case const counter csv descend encrypt end endc++ endmacro except exclusive expire export extend false few first flat from full function group header heading hole ifblock import in interface joined keep keyed last left limit load local locale lookup macro many maxcount maxlength min skew module named nocase noroot noscan nosort not of only opt or outer overwrite packed partition penalty physicallength pipe quote record relationship repeat return right scan self separator service shared skew skip sql store terminator thor threshold token transform trim true type unicodeorder unsorted validate virtual whole wild within xml xpath"); + var variable_3 = words("ascii big_endian boolean data decimal ebcdic integer pattern qstring real record rule set of string token udecimal unicode unsigned varstring varunicode"); + var builtin = words("checkpoint deprecated failcode failmessage failure global independent onwarning persist priority recovery stored success wait when"); + var blockKeywords = words("catch class do else finally for if switch try while"); + var atoms = words("true false null"); + var hooks = {"#": metaHook}; + var multiLineStrings; + var isOperatorChar = /[+\-*&%=<>!?|\/]/; + + var curPunc; + + function tokenBase(stream, state) { + var ch = stream.next(); + if (hooks[ch]) { + var result = hooks[ch](stream, state); + if (result !== false) return result; + } + if (ch == '"' || ch == "'") { + state.tokenize = tokenString(ch); + return state.tokenize(stream, state); + } + if (/[\[\]{}\(\),;\:\.]/.test(ch)) { + curPunc = ch; + return null; + } + if (/\d/.test(ch)) { + stream.eatWhile(/[\w\.]/); + return "number"; + } + if (ch == "/") { + if (stream.eat("*")) { + state.tokenize = tokenComment; + return tokenComment(stream, state); + } + if (stream.eat("/")) { + stream.skipToEnd(); + return "comment"; + } + } + if (isOperatorChar.test(ch)) { + stream.eatWhile(isOperatorChar); + return "operator"; + } + stream.eatWhile(/[\w\$_]/); + var cur = stream.current().toLowerCase(); + if (keyword.propertyIsEnumerable(cur)) { + if (blockKeywords.propertyIsEnumerable(cur)) curPunc = "newstatement"; + return "keyword"; + } else if (variable.propertyIsEnumerable(cur)) { + if (blockKeywords.propertyIsEnumerable(cur)) curPunc = "newstatement"; + return "variable"; + } else if (variable_2.propertyIsEnumerable(cur)) { + if (blockKeywords.propertyIsEnumerable(cur)) curPunc = "newstatement"; + return "variable-2"; + } else if (variable_3.propertyIsEnumerable(cur)) { + if (blockKeywords.propertyIsEnumerable(cur)) curPunc = "newstatement"; + return "variable-3"; + } else if (builtin.propertyIsEnumerable(cur)) { + if (blockKeywords.propertyIsEnumerable(cur)) curPunc = "newstatement"; + return "builtin"; + } else { //Data types are of from KEYWORD## + var i = cur.length - 1; + while(i >= 0 && (!isNaN(cur[i]) || cur[i] == '_')) + --i; + + if (i > 0) { + var cur2 = cur.substr(0, i + 1); + if (variable_3.propertyIsEnumerable(cur2)) { + if (blockKeywords.propertyIsEnumerable(cur2)) curPunc = "newstatement"; + return "variable-3"; + } + } + } + if (atoms.propertyIsEnumerable(cur)) return "atom"; + return null; + } + + function tokenString(quote) { + return function(stream, state) { + var escaped = false, next, end = false; + while ((next = stream.next()) != null) { + if (next == quote && !escaped) {end = true; break;} + escaped = !escaped && next == "\\"; + } + if (end || !(escaped || multiLineStrings)) + state.tokenize = tokenBase; + return "string"; + }; + } + + function tokenComment(stream, state) { + var maybeEnd = false, ch; + while (ch = stream.next()) { + if (ch == "/" && maybeEnd) { + state.tokenize = tokenBase; + break; + } + maybeEnd = (ch == "*"); + } + return "comment"; + } + + function Context(indented, column, type, align, prev) { + this.indented = indented; + this.column = column; + this.type = type; + this.align = align; + this.prev = prev; + } + function pushContext(state, col, type) { + return state.context = new Context(state.indented, col, type, null, state.context); + } + function popContext(state) { + var t = state.context.type; + if (t == ")" || t == "]" || t == "}") + state.indented = state.context.indented; + return state.context = state.context.prev; + } + + // Interface + + return { + startState: function(basecolumn) { + return { + tokenize: null, + context: new Context((basecolumn || 0) - indentUnit, 0, "top", false), + indented: 0, + startOfLine: true + }; + }, + + token: function(stream, state) { + var ctx = state.context; + if (stream.sol()) { + if (ctx.align == null) ctx.align = false; + state.indented = stream.indentation(); + state.startOfLine = true; + } + if (stream.eatSpace()) return null; + curPunc = null; + var style = (state.tokenize || tokenBase)(stream, state); + if (style == "comment" || style == "meta") return style; + if (ctx.align == null) ctx.align = true; + + if ((curPunc == ";" || curPunc == ":") && ctx.type == "statement") popContext(state); + else if (curPunc == "{") pushContext(state, stream.column(), "}"); + else if (curPunc == "[") pushContext(state, stream.column(), "]"); + else if (curPunc == "(") pushContext(state, stream.column(), ")"); + else if (curPunc == "}") { + while (ctx.type == "statement") ctx = popContext(state); + if (ctx.type == "}") ctx = popContext(state); + while (ctx.type == "statement") ctx = popContext(state); + } + else if (curPunc == ctx.type) popContext(state); + else if (ctx.type == "}" || ctx.type == "top" || (ctx.type == "statement" && curPunc == "newstatement")) + pushContext(state, stream.column(), "statement"); + state.startOfLine = false; + return style; + }, + + indent: function(state, textAfter) { + if (state.tokenize != tokenBase && state.tokenize != null) return 0; + var ctx = state.context, firstChar = textAfter && textAfter.charAt(0); + if (ctx.type == "statement" && firstChar == "}") ctx = ctx.prev; + var closing = firstChar == ctx.type; + if (ctx.type == "statement") return ctx.indented + (firstChar == "{" ? 0 : indentUnit); + else if (ctx.align) return ctx.column + (closing ? 0 : 1); + else return ctx.indented + (closing ? 0 : indentUnit); + }, + + electricChars: "{}" + }; +}); + +CodeMirror.defineMIME("text/x-ecl", "ecl"); diff --git a/mode/ecl/index.html b/mode/ecl/index.html index 77006ae6e6..0ba88c3995 100644 --- a/mode/ecl/index.html +++ b/mode/ecl/index.html @@ -1,39 +1,39 @@ - - - - CodeMirror: ECL mode - - - - - - - -

    CodeMirror: ECL mode

    -
    - - -

    Based on CodeMirror's clike mode. For more information see HPCC Systems web site.

    -

    MIME types defined: text/x-ecl.

    - - - + + + + CodeMirror: ECL mode + + + + + + + +

    CodeMirror: ECL mode

    +
    + + +

    Based on CodeMirror's clike mode. For more information see HPCC Systems web site.

    +

    MIME types defined: text/x-ecl.

    + + + diff --git a/mode/verilog/verilog.js b/mode/verilog/verilog.js index 230e31d2dc..708de23f49 100644 --- a/mode/verilog/verilog.js +++ b/mode/verilog/verilog.js @@ -1,182 +1,182 @@ -CodeMirror.defineMode("verilog", function(config, parserConfig) { - var indentUnit = config.indentUnit, - keywords = parserConfig.keywords || {}, - blockKeywords = parserConfig.blockKeywords || {}, - atoms = parserConfig.atoms || {}, - hooks = parserConfig.hooks || {}, - multiLineStrings = parserConfig.multiLineStrings; - var isOperatorChar = /[&|~> Date: Mon, 10 Dec 2012 12:33:27 +0100 Subject: [PATCH 0461/5780] Fix call to updateScrollBars to pass doc height --- lib/codemirror.js | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/lib/codemirror.js b/lib/codemirror.js index 8e1b6b8c58..1cdac59250 100644 --- a/lib/codemirror.js +++ b/lib/codemirror.js @@ -217,7 +217,7 @@ window.CodeMirror = (function() { } regChange(cm, 0, doc.size); clearCaches(cm); - setTimeout(bind(updateScrollbars, cm.display), 100); + setTimeout(function(){updateScrollbars(cm.display, cm.view.doc.height);}, 100); } function keyMapChanged(cm) { From fcbe3fd3875e25a4e3b5084364fc78c393413500 Mon Sep 17 00:00:00 2001 From: Marijn Haverbeke Date: Mon, 10 Dec 2012 12:34:19 +0100 Subject: [PATCH 0462/5780] Bump supported version of FF to 3 2 doesn't have getBoundingClientRect, which we use extensively. Closes #1062 --- index.html | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/index.html b/index.html index 4dbc0659f0..b510b5f706 100644 --- a/index.html +++ b/index.html @@ -215,7 +215,7 @@

    Supported browsers

    The following desktop browsers are able to run CodeMirror:

    @@ -148,7 +148,7 @@

    { } CodeMi and Rubyblue themes.
  • Add setBookmark method.
  • Move some of the demo code into reusable components - under lib/util.
  • + under lib/util.
  • Make screen-coord-finding code faster and more reliable.
  • Fix drag-and-drop in Firefox.
  • Improve support for IME.
  • diff --git a/doc/upgrade_v3.html b/doc/upgrade_v3.html index eaaffec17f..7e8a6b61ae 100644 --- a/doc/upgrade_v3.html +++ b/doc/upgrade_v3.html @@ -7,8 +7,8 @@ - - + + @@ -174,9 +174,9 @@

    Position properties

    Bracket matching no longer in core

    -

    The matchBrackets +

    The matchBrackets option is no longer defined in the core editor. -Load lib/util/matchbrackets.js to enable it.

    +Load addon/edit/matchbrackets.js to enable it.

    Mode management

    diff --git a/index.html b/index.html index 174bc27fd3..e921f0f300 100644 --- a/index.html +++ b/index.html @@ -329,7 +329,7 @@

    Releases

  • Add defaultTextHeight method.
  • Various extensions to the vim keymap.
  • Make PHP mode build on mixed HTML mode.
  • -
  • Add comment-continuing add-on.
  • +
  • Add comment-continuing add-on.
  • Full list of patches.
  • diff --git a/mode/clike/index.html b/mode/clike/index.html index ca10c4bc23..5f90394d95 100644 --- a/mode/clike/index.html +++ b/mode/clike/index.html @@ -5,7 +5,7 @@ CodeMirror: C-like mode - + diff --git a/mode/clike/scala.html b/mode/clike/scala.html index 39115967ab..f3c7eea498 100644 --- a/mode/clike/scala.html +++ b/mode/clike/scala.html @@ -6,7 +6,7 @@ - + diff --git a/mode/erlang/index.html b/mode/erlang/index.html index f6bee8f723..fd21521c88 100644 --- a/mode/erlang/index.html +++ b/mode/erlang/index.html @@ -5,7 +5,7 @@ CodeMirror: Erlang mode - + diff --git a/mode/gfm/index.html b/mode/gfm/index.html index e4ea473571..b5bdb8888f 100644 --- a/mode/gfm/index.html +++ b/mode/gfm/index.html @@ -5,7 +5,7 @@ CodeMirror: GFM mode - + diff --git a/mode/go/index.html b/mode/go/index.html index 1a9ef53852..8a6aafca29 100644 --- a/mode/go/index.html +++ b/mode/go/index.html @@ -6,7 +6,7 @@ - + diff --git a/mode/groovy/index.html b/mode/groovy/index.html index d0d76bfa90..3d39595774 100644 --- a/mode/groovy/index.html +++ b/mode/groovy/index.html @@ -5,7 +5,7 @@ CodeMirror: Groovy mode - + diff --git a/mode/haskell/index.html b/mode/haskell/index.html index b304a27682..56307b8a95 100644 --- a/mode/haskell/index.html +++ b/mode/haskell/index.html @@ -5,7 +5,7 @@ CodeMirror: Haskell mode - + diff --git a/mode/javascript/index.html b/mode/javascript/index.html index d81413c0a6..9222ddf170 100644 --- a/mode/javascript/index.html +++ b/mode/javascript/index.html @@ -5,8 +5,8 @@ CodeMirror: JavaScript mode - - + + diff --git a/mode/less/index.html b/mode/less/index.html index 7f27cf30e9..78c1e53074 100644 --- a/mode/less/index.html +++ b/mode/less/index.html @@ -5,7 +5,7 @@ CodeMirror: LESS mode - + diff --git a/mode/lua/index.html b/mode/lua/index.html index df83f9b47d..a0a42d91c4 100644 --- a/mode/lua/index.html +++ b/mode/lua/index.html @@ -4,7 +4,7 @@ CodeMirror: Lua mode - + diff --git a/mode/markdown/index.html b/mode/markdown/index.html index 5d7452f1e8..6f97b10e73 100644 --- a/mode/markdown/index.html +++ b/mode/markdown/index.html @@ -5,7 +5,7 @@ CodeMirror: Markdown mode - + diff --git a/mode/ocaml/index.html b/mode/ocaml/index.html index 962fa29eb1..c10a84fade 100644 --- a/mode/ocaml/index.html +++ b/mode/ocaml/index.html @@ -10,7 +10,7 @@ - +

    CodeMirror: OCaml mode

    diff --git a/mode/php/index.html b/mode/php/index.html index 4b21c1900a..3d4c336ce0 100644 --- a/mode/php/index.html +++ b/mode/php/index.html @@ -5,7 +5,7 @@ CodeMirror: PHP mode - + diff --git a/mode/python/index.html b/mode/python/index.html index 7a26d278db..c1df119485 100644 --- a/mode/python/index.html +++ b/mode/python/index.html @@ -5,7 +5,7 @@ CodeMirror: Python mode - + diff --git a/mode/ruby/index.html b/mode/ruby/index.html index f226289d7d..64cfe5ef34 100644 --- a/mode/ruby/index.html +++ b/mode/ruby/index.html @@ -5,7 +5,7 @@ CodeMirror: Ruby mode - + diff --git a/mode/shell/index.html b/mode/shell/index.html index 0827053cee..9a2ef7c4cd 100644 --- a/mode/shell/index.html +++ b/mode/shell/index.html @@ -10,7 +10,7 @@ - +

    CodeMirror: Shell mode

    diff --git a/mode/smalltalk/index.html b/mode/smalltalk/index.html index 690b560fd7..b7aebdb7fb 100644 --- a/mode/smalltalk/index.html +++ b/mode/smalltalk/index.html @@ -5,7 +5,7 @@ CodeMirror: Smalltalk mode - + diff --git a/mode/tiddlywiki/index.html b/mode/tiddlywiki/index.html index 89ae85892b..848f33a592 100644 --- a/mode/tiddlywiki/index.html +++ b/mode/tiddlywiki/index.html @@ -5,7 +5,7 @@ CodeMirror: TiddlyWiki mode - + diff --git a/mode/vb/index.html b/mode/vb/index.html index 1670c7d05b..74dd5e8161 100644 --- a/mode/vb/index.html +++ b/mode/vb/index.html @@ -12,7 +12,7 @@ .CodeMirror-scroll { overflow-x: auto; overflow-y: hidden;} .CodeMirror pre { font-family: Inconsolata; font-size: 14px} - +

    CodeMirror: VB.NET mode

    diff --git a/test/index.html b/test/index.html index 9634ac23e8..4bb4a77be5 100644 --- a/test/index.html +++ b/test/index.html @@ -7,8 +7,8 @@ - - + + diff --git a/test/run.js b/test/run.js index 8c7649a17d..5588d6f388 100755 --- a/test/run.js +++ b/test/run.js @@ -4,6 +4,7 @@ var lint = require("./lint/lint"); lint.checkDir("mode"); lint.checkDir("lib"); +lint.checkDir("addon"); var ok = lint.success(); From 9e7501bb10582e1f1155d318bd1c39ee97a1da38 Mon Sep 17 00:00:00 2001 From: Marijn Haverbeke Date: Tue, 8 Jan 2013 14:40:29 +0100 Subject: [PATCH 0559/5780] Fix links in full mode list Closes #1144 --- doc/modes.html | 112 ++++++++++++++++++++++++------------------------- 1 file changed, 56 insertions(+), 56 deletions(-) diff --git a/doc/modes.html b/doc/modes.html index 48fdf88273..7c072971c2 100644 --- a/doc/modes.html +++ b/doc/modes.html @@ -23,62 +23,62 @@

    { } CodeMi
    From 21380af6901c76011548d2fd2c7a2befad99f41f Mon Sep 17 00:00:00 2001 From: Peter Kroon Date: Tue, 8 Jan 2013 15:04:26 +0100 Subject: [PATCH 0560/5780] Update LICENSE Set correct year in license. --- LICENSE | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/LICENSE b/LICENSE index 3916e96b2d..482d55eb73 100644 --- a/LICENSE +++ b/LICENSE @@ -1,4 +1,4 @@ -Copyright (C) 2012 by Marijn Haverbeke +Copyright (C) 2013 by Marijn Haverbeke Permission is hereby granted, free of charge, to any person obtaining a copy of this software and associated documentation files (the "Software"), to deal From ee071463e9de587abcf2ae47fa4bd59b770e2384 Mon Sep 17 00:00:00 2001 From: Marijn Haverbeke Date: Thu, 10 Jan 2013 11:02:37 +0100 Subject: [PATCH 0561/5780] Check, after updating display, if viewport is really filled Due to changed line heights, the rendered content might not reach to the viewport bottom. Issue #1147 --- lib/codemirror.js | 3 +++ 1 file changed, 3 insertions(+) diff --git a/lib/codemirror.js b/lib/codemirror.js index 5a87212632..9cce59d93f 100644 --- a/lib/codemirror.js +++ b/lib/codemirror.js @@ -503,6 +503,9 @@ window.CodeMirror = (function() { display.viewOffset = heightAtLine(cm, getLine(doc, from)); // Position the mover div to align with the current virtual scroll position display.mover.style.top = display.viewOffset + "px"; + + if (visibleLines(display, doc, viewPort).to >= to) + updateDisplayInner(cm, [], viewPort); return true; } From db1b28207d5b8b799d7202cf47bb9ece1c0afb3c Mon Sep 17 00:00:00 2001 From: Marijn Haverbeke Date: Thu, 10 Jan 2013 11:44:05 +0100 Subject: [PATCH 0562/5780] Make line widget handles objects with methods. Handle widget height better Closes #1147 Widget handles now have .clear() and .changed() methods. The second informs CodeMirror that the widget changed, and causes it to recompute the line height for the widget's line. Fixes some sloppiness in the tracking of widget heights. --- doc/manual.html | 15 +++++---- lib/codemirror.js | 80 ++++++++++++++++++++++++++++++++++++----------- 2 files changed, 70 insertions(+), 25 deletions(-) diff --git a/doc/manual.html b/doc/manual.html index 8f9574b871..150ef05353 100644 --- a/doc/manual.html +++ b/doc/manual.html @@ -978,12 +978,15 @@

    Programming API

    CodeMirror-specific CSS classes, and those classes might in some cases affect it. This method returns an object that represents the widget placement. It'll have a line property - pointing at the line handle that it is associated with, and it - can be passed to removeLineWidget to remove the - widget. - -
    removeLineWidget(widget)
    -
    Removes the given line widget.
    + pointing at the line handle that it is associated with, and the following methods: +
    +
    clear()
    Removes the widget.
    +
    changed()
    Call + this if you made some change to the widget's DOM node that + might affect its height. It'll force CodeMirror to update + the height of the line that contains the widget.
    +
    +
    posFromIndex(index) → object
    Calculates and returns a {line, ch} object for a diff --git a/lib/codemirror.js b/lib/codemirror.js index 9cce59d93f..975c8d23b9 100644 --- a/lib/codemirror.js +++ b/lib/codemirror.js @@ -497,8 +497,12 @@ window.CodeMirror = (function() { } var diff = node.lineObj.height - height; if (height < 2) height = textHeight(display); - if (diff > .001 || diff < -.001) + if (diff > .001 || diff < -.001) { updateLineHeight(node.lineObj, height); + var widgets = node.lineObj.widgets; + if (widgets) for (var i = 0; i < widgets.length; ++i) + widgets[i].height = widgets[i].node.offsetHeight; + } } display.viewOffset = heightAtLine(cm, getLine(doc, from)); // Position the mover div to align with the current virtual scroll position @@ -978,7 +982,7 @@ window.CodeMirror = (function() { // Context is one of "line", "div" (display.lineDiv), "local"/null (editor), or "page" function intoCoordSystem(cm, lineObj, rect, context) { if (lineObj.widgets) for (var i = 0; i < lineObj.widgets.length; ++i) if (lineObj.widgets[i].above) { - var size = lineObj.widgets[i].node.offsetHeight; + var size = widgetHeight(lineObj.widgets[i]); rect.top += size; rect.bottom += size; } if (context == "line") return rect; @@ -2597,25 +2601,10 @@ window.CodeMirror = (function() { }), addLineWidget: operation(null, function(handle, node, options) { - var widget = options || {}; - widget.node = node; - if (widget.noHScroll) this.display.alignWidgets = true; - changeLine(this, handle, function(line) { - (line.widgets || (line.widgets = [])).push(widget); - widget.line = line; - return true; - }); - return widget; + return addLineWidget(this, handle, node, options); }), - removeLineWidget: operation(null, function(widget) { - var ws = widget.line.widgets, no = lineNo(widget.line); - if (no == null || !ws) return; - for (var i = 0; i < ws.length; ++i) if (ws[i] == widget) ws.splice(i--, 1); - var newHeight = widget.node.offsetHeight ? widget.line.height - widget.node.offsetHeight : textHeight(this.display); - updateLineHeight(widget.line, newHeight); - regChange(this, no, no + 1); - }), + removeLineWidget: function(widget) { widget.remove(); }, lineInfo: function(line) { if (typeof line == "number") { @@ -3587,6 +3576,59 @@ window.CodeMirror = (function() { line.markedSpans = spans; } + // LINE WIDGETS + + var LineWidget = CodeMirror.LineWidget = function(cm, node, options) { + for (var opt in options) if (options.hasOwnProperty(opt)) + this[opt] = options[opt]; + this.cm = cm; + this.node = node; + }; + function widgetOperation(f) { + return function() { + startOperation(this.cm); + try {var result = f.apply(this, arguments);} + finally {endOperation(this.cm);} + return result; + }; + } + LineWidget.prototype.clear = widgetOperation(function() { + var ws = this.line.widgets, no = lineNo(this.line); + if (no == null || !ws) return; + for (var i = 0; i < ws.length; ++i) if (ws[i] == this) ws.splice(i--, 1); + updateLineHeight(this.line, Math.max(0, this.line.height - widgetHeight(this))); + regChange(this.cm, no, no + 1); + }); + LineWidget.prototype.changed = widgetOperation(function() { + var oldH = this.height; + this.height = null; + var diff = widgetHeight(this) - oldH; + if (!diff) return; + updateLineHeight(this.line, this.line.height + diff); + var no = lineNo(this.line); + regChange(this.cm, no, no + 1); + }); + + function widgetHeight(widget) { + if (widget.height != null) return widget.height; + if (!widget.node.parentNode) + removeChildrenAndAdd(widget.cm.display.measure, elt("div", [widget.node], null, "position: relative")); + return widget.height = widget.node.offsetHeight; + } + + function addLineWidget(cm, handle, node, options) { + var widget = new LineWidget(cm, node, options); + if (widget.noHScroll) cm.display.alignWidgets = true; + changeLine(cm, handle, function(line) { + (line.widgets || (line.widgets = [])).push(widget); + widget.line = line; + if (!lineIsHidden(line) || widget.showIfHidden) + updateLineHeight(line, line.height + widgetHeight(widget)); + return true; + }); + return widget; + } + // LINE DATA STRUCTURE // Line objects. These hold state related to a line, including From 5af6bb0f1572ea4e0e0d65f85bdfec5dc493a390 Mon Sep 17 00:00:00 2001 From: Marijn Haverbeke Date: Thu, 10 Jan 2013 11:57:34 +0100 Subject: [PATCH 0563/5780] Don't set innerHTML in IE It damages parent-child relationships in subnodes, which we might still need later. --- lib/codemirror.js | 9 ++++----- 1 file changed, 4 insertions(+), 5 deletions(-) diff --git a/lib/codemirror.js b/lib/codemirror.js index 975c8d23b9..32f11a7afa 100644 --- a/lib/codemirror.js +++ b/lib/codemirror.js @@ -550,9 +550,7 @@ window.CodeMirror = (function() { function patchDisplay(cm, from, to, intact, updateNumbersFrom) { var dims = getDimensions(cm); var display = cm.display, lineNumbers = cm.options.lineNumbers; - // IE does bad things to nodes when .innerHTML = "" is used on a parent - // we still need widgets and markers intact to add back to the new content later - if (!intact.length && !ie && (!webkit || !cm.display.currentWheelTarget)) + if (!intact.length && (!webkit || !cm.display.currentWheelTarget)) removeChildren(display.lineDiv); var container = display.lineDiv, cur = container.firstChild; @@ -1114,7 +1112,6 @@ window.CodeMirror = (function() { } removeChildrenAndAdd(display.measure, measureText); var height = measureText.offsetHeight / 50; - display.measure.removeChild(measureText); if (height > 3) display.cachedTextHeight = height; removeChildren(display.measure); return height || 1; @@ -4354,7 +4351,9 @@ window.CodeMirror = (function() { } function removeChildren(e) { - e.innerHTML = ""; + // IE will break all parent-child relations in subnodes when setting innerHTML + if (!ie) e.innerHTML = ""; + else while (e.firstChild) e.removeChild(e.firstChild); return e; } From 7777dce07c3acc9c14cf6ea867e2ee1e6b3f381c Mon Sep 17 00:00:00 2001 From: Marijn Haverbeke Date: Thu, 10 Jan 2013 12:14:19 +0100 Subject: [PATCH 0564/5780] Make sure widgets are added to vertical scroll size Closes #1148 --- lib/codemirror.js | 8 ++++++-- 1 file changed, 6 insertions(+), 2 deletions(-) diff --git a/lib/codemirror.js b/lib/codemirror.js index 32f11a7afa..4456181cc6 100644 --- a/lib/codemirror.js +++ b/lib/codemirror.js @@ -3608,7 +3608,7 @@ window.CodeMirror = (function() { function widgetHeight(widget) { if (widget.height != null) return widget.height; - if (!widget.node.parentNode) + if (!widget.node.parentNode || widget.node.parentNode.nodeType != 1) removeChildrenAndAdd(widget.cm.display.measure, elt("div", [widget.node], null, "position: relative")); return widget.height = widget.node.offsetHeight; } @@ -3619,8 +3619,12 @@ window.CodeMirror = (function() { changeLine(cm, handle, function(line) { (line.widgets || (line.widgets = [])).push(widget); widget.line = line; - if (!lineIsHidden(line) || widget.showIfHidden) + if (!lineIsHidden(line) || widget.showIfHidden) { + var aboveVisible = heightAtLine(cm, line) < cm.display.scroller.scrollTop; updateLineHeight(line, line.height + widgetHeight(widget)); + if (aboveVisible) + setTimeout(function() {cm.display.scroller.scrollTop += widget.height;}); + } return true; }); return widget; From d8009de4f3a7f88379784023cf2c8f9098a811d6 Mon Sep 17 00:00:00 2001 From: Rahul Date: Thu, 10 Jan 2013 19:00:45 +0100 Subject: [PATCH 0565/5780] Correct spelling from "givne" to "given". --- doc/manual.html | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/doc/manual.html b/doc/manual.html index 150ef05353..14d3a3249f 100644 --- a/doc/manual.html +++ b/doc/manual.html @@ -620,7 +620,7 @@

    Programming API

    Set the selection range. anchor and head should be {line, ch} objects. head defaults to anchor when - not givne.
    + not given.
    getLine(n) → string
    Get the content of line n.
    From ece10c7208da8f36001f3ff02a86d0bd6612c0bb Mon Sep 17 00:00:00 2001 From: Narciso Jaramillo Date: Thu, 10 Jan 2013 11:54:55 -0800 Subject: [PATCH 0566/5780] Make temporary wheel measurement vars per-instance --- lib/codemirror.js | 24 ++++++++++++------------ 1 file changed, 12 insertions(+), 12 deletions(-) diff --git a/lib/codemirror.js b/lib/codemirror.js index 4456181cc6..45adfdce27 100644 --- a/lib/codemirror.js +++ b/lib/codemirror.js @@ -1637,7 +1637,7 @@ window.CodeMirror = (function() { // is that it gives us a chance to update the display before the // actual scrolling happens, reducing flickering. - var wheelSamples = 0, wheelDX, wheelDY, wheelStartX, wheelStartY, wheelPixelsPerUnit = null; + var wheelSamples = 0, wheelPixelsPerUnit = null; // Fill in a browser-detected starting value on browsers where we // know one. These don't have to be accurate -- the result of them // being wrong would just be a slight flicker on the first wheel @@ -1678,7 +1678,7 @@ window.CodeMirror = (function() { setScrollTop(cm, Math.max(0, Math.min(scroll.scrollTop + dy * wheelPixelsPerUnit, scroll.scrollHeight - scroll.clientHeight))); setScrollLeft(cm, Math.max(0, Math.min(scroll.scrollLeft + dx * wheelPixelsPerUnit, scroll.scrollWidth - scroll.clientWidth))); e_preventDefault(e); - wheelStartX = null; // Abort measurement, if in progress + cm.wheelStartX = null; // Abort measurement, if in progress return; } @@ -1691,22 +1691,22 @@ window.CodeMirror = (function() { } if (wheelSamples < 20) { - if (wheelStartX == null) { - wheelStartX = scroll.scrollLeft; wheelStartY = scroll.scrollTop; - wheelDX = dx; wheelDY = dy; + if (cm.wheelStartX == null) { + cm.wheelStartX = scroll.scrollLeft; cm.wheelStartY = scroll.scrollTop; + cm.wheelDX = dx; cm.wheelDY = dy; setTimeout(function() { - if (wheelStartX == null) return; - var movedX = scroll.scrollLeft - wheelStartX; - var movedY = scroll.scrollTop - wheelStartY; - var sample = (movedY && wheelDY && movedY / wheelDY) || - (movedX && wheelDX && movedX / wheelDX); - wheelStartX = wheelStartY = null; + if (cm.wheelStartX == null) return; + var movedX = scroll.scrollLeft - cm.wheelStartX; + var movedY = scroll.scrollTop - cm.wheelStartY; + var sample = (movedY && cm.wheelDY && movedY / cm.wheelDY) || + (movedX && cm.wheelDX && movedX / cm.wheelDX); + cm.wheelStartX = cm.wheelStartY = null; if (!sample) return; wheelPixelsPerUnit = (wheelPixelsPerUnit * wheelSamples + sample) / (wheelSamples + 1); ++wheelSamples; }, 200); } else { - wheelDX += dx; wheelDY += dy; + cm.wheelDX += dx; cm.wheelDY += dy; } } } From 7f64ae4887cfc093cee6cbe4d3739235dd883e1f Mon Sep 17 00:00:00 2001 From: Marijn Haverbeke Date: Fri, 11 Jan 2013 10:41:37 +0100 Subject: [PATCH 0567/5780] Attach wheel measuring properties to display, not directly to CM instance Issue #1151 --- lib/codemirror.js | 29 ++++++++++++++++------------- 1 file changed, 16 insertions(+), 13 deletions(-) diff --git a/lib/codemirror.js b/lib/codemirror.js index 45adfdce27..da551ffad8 100644 --- a/lib/codemirror.js +++ b/lib/codemirror.js @@ -166,6 +166,9 @@ window.CodeMirror = (function() { // detected d.pasteIncoming = false; + // Used for measuring wheel scrolling granularity + d.wheelDX = d.wheelDY = d.wheelStartX = d.wheelStartY = null; + return d; } @@ -1666,7 +1669,7 @@ window.CodeMirror = (function() { } } - var scroll = cm.display.scroller; + var display = cm.display, scroll = display.scroller; // On some browsers, horizontal scrolling will cause redraws to // happen before the gutter has been realigned, causing it to // wriggle around in a most unseemly way. When we have an @@ -1678,35 +1681,35 @@ window.CodeMirror = (function() { setScrollTop(cm, Math.max(0, Math.min(scroll.scrollTop + dy * wheelPixelsPerUnit, scroll.scrollHeight - scroll.clientHeight))); setScrollLeft(cm, Math.max(0, Math.min(scroll.scrollLeft + dx * wheelPixelsPerUnit, scroll.scrollWidth - scroll.clientWidth))); e_preventDefault(e); - cm.wheelStartX = null; // Abort measurement, if in progress + display.wheelStartX = null; // Abort measurement, if in progress return; } if (dy && wheelPixelsPerUnit != null) { var pixels = dy * wheelPixelsPerUnit; - var top = cm.view.scrollTop, bot = top + cm.display.wrapper.clientHeight; + var top = cm.view.scrollTop, bot = top + display.wrapper.clientHeight; if (pixels < 0) top = Math.max(0, top + pixels - 50); else bot = Math.min(cm.view.doc.height, bot + pixels + 50); updateDisplay(cm, [], {top: top, bottom: bot}); } if (wheelSamples < 20) { - if (cm.wheelStartX == null) { - cm.wheelStartX = scroll.scrollLeft; cm.wheelStartY = scroll.scrollTop; - cm.wheelDX = dx; cm.wheelDY = dy; + if (display.wheelStartX == null) { + display.wheelStartX = scroll.scrollLeft; display.wheelStartY = scroll.scrollTop; + display.wheelDX = dx; display.wheelDY = dy; setTimeout(function() { - if (cm.wheelStartX == null) return; - var movedX = scroll.scrollLeft - cm.wheelStartX; - var movedY = scroll.scrollTop - cm.wheelStartY; - var sample = (movedY && cm.wheelDY && movedY / cm.wheelDY) || - (movedX && cm.wheelDX && movedX / cm.wheelDX); - cm.wheelStartX = cm.wheelStartY = null; + if (display.wheelStartX == null) return; + var movedX = scroll.scrollLeft - display.wheelStartX; + var movedY = scroll.scrollTop - display.wheelStartY; + var sample = (movedY && display.wheelDY && movedY / display.wheelDY) || + (movedX && display.wheelDX && movedX / display.wheelDX); + display.wheelStartX = display.wheelStartY = null; if (!sample) return; wheelPixelsPerUnit = (wheelPixelsPerUnit * wheelSamples + sample) / (wheelSamples + 1); ++wheelSamples; }, 200); } else { - cm.wheelDX += dx; cm.wheelDY += dy; + display.wheelDX += dx; display.wheelDY += dy; } } } From 2cf2913ceb1d394a2aa8190a448dd993112d71c1 Mon Sep 17 00:00:00 2001 From: Jason San Jose Date: Thu, 10 Jan 2013 13:53:38 -0800 Subject: [PATCH 0568/5780] widget.remove() should be widget.clear() --- lib/codemirror.js | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/lib/codemirror.js b/lib/codemirror.js index da551ffad8..4ce1cd0b8f 100644 --- a/lib/codemirror.js +++ b/lib/codemirror.js @@ -2604,7 +2604,7 @@ window.CodeMirror = (function() { return addLineWidget(this, handle, node, options); }), - removeLineWidget: function(widget) { widget.remove(); }, + removeLineWidget: function(widget) { widget.clear(); }, lineInfo: function(line) { if (typeof line == "number") { From 67084c2b924908099bc7df1220a03a1f7ace72b4 Mon Sep 17 00:00:00 2001 From: Marijn Haverbeke Date: Fri, 11 Jan 2013 11:10:31 +0100 Subject: [PATCH 0569/5780] Add realtime.io to real-world uses --- doc/realworld.html | 1 + 1 file changed, 1 insertion(+) diff --git a/doc/realworld.html b/doc/realworld.html index 6d95f5ba5b..71d9929c63 100644 --- a/doc/realworld.html +++ b/doc/realworld.html @@ -67,6 +67,7 @@

    { } CodeMi
  • QiYun web app platform
  • Qt+Webkit integration (building a desktop CodeMirror app)
  • Rascal (tiny computer)
  • +
  • RealTime.io (Internet-of-Things infrastructure)
  • sketchPatch Livecodelab
  • Skulpt (in-browser Python environment)
  • SolidShops (hosted e-commerce platform)
  • From 2e3c49ab092125337b4b11c68ce85d563169d3b4 Mon Sep 17 00:00:00 2001 From: Marijn Haverbeke Date: Fri, 11 Jan 2013 21:38:51 +0100 Subject: [PATCH 0570/5780] Remove improperly copy-pasted piece in mode/sieve/LICENSE --- mode/sieve/LICENSE | 4 ---- 1 file changed, 4 deletions(-) diff --git a/mode/sieve/LICENSE b/mode/sieve/LICENSE index 24e4c94c09..8a74612cba 100644 --- a/mode/sieve/LICENSE +++ b/mode/sieve/LICENSE @@ -17,7 +17,3 @@ AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. - -Please note that some subdirectories of the CodeMirror distribution -include their own LICENSE files, and are released under different -licences. From d9a86095ae7a42968ba9dc8eeacbf9e159127375 Mon Sep 17 00:00:00 2001 From: Marijn Haverbeke Date: Sat, 12 Jan 2013 12:09:13 +0100 Subject: [PATCH 0571/5780] [test/mode_test.js] Remove some obsolete cruft --- test/mode_test.js | 33 ++++----------------------------- 1 file changed, 4 insertions(+), 29 deletions(-) diff --git a/test/mode_test.js b/test/mode_test.js index f2459b432e..06270aa31e 100644 --- a/test/mode_test.js +++ b/test/mode_test.js @@ -9,12 +9,8 @@ ModeTest = {}; ModeTest.modeOptions = {}; ModeTest.modeName = CodeMirror.defaults.mode; -/* keep track of results for printSummary */ -ModeTest.testCount = 0; -ModeTest.passes = 0; - /** - * Run a test; prettyprints the results using document.write(). + * Run a test. * * @param name Name of test * @param text String to highlight. @@ -24,8 +20,6 @@ ModeTest.passes = 0; * @param expectedFail */ ModeTest.testMode = function(name, text, expected, modeName, modeOptions, expectedFail) { - ModeTest.testCount += 1; - if (!modeName) modeName = ModeTest.modeName; if (!modeOptions) modeOptions = ModeTest.modeOptions; @@ -62,12 +56,11 @@ ModeTest.compare = function (text, expected, mode) { var pass, passStyle = ""; pass = ModeTest.highlightOutputsEqual(expectedOutput, observedOutput); passStyle = pass ? 'mt-pass' : 'mt-fail'; - ModeTest.passes += pass ? 1 : 0; var s = ''; if (pass) { s += '
    '; - s += '
    ' + ModeTest.htmlEscape(text) + '
    '; + s += '
    ' + text + '
    '; s += '
    '; s += ModeTest.prettyPrintOutputTable(observedOutput); s += '
    '; @@ -75,7 +68,7 @@ ModeTest.compare = function (text, expected, mode) { return s; } else { s += '
    '; - s += '
    ' + ModeTest.htmlEscape(text) + '
    '; + s += '
    ' + text + '
    '; s += '
    '; s += 'expected:'; s += ModeTest.prettyPrintOutputTable(expectedOutput); @@ -161,7 +154,7 @@ ModeTest.prettyPrintOutputTable = function(output) { s += '' + '' + - ModeTest.htmlEscape(val).replace(/ /g,'·') + + val.replace(/ /g,'\xb7') + '' + ''; } @@ -172,21 +165,3 @@ ModeTest.prettyPrintOutputTable = function(output) { s += ''; return s; } - -/** - * Print how many tests have run so far and how many of those passed. - */ -ModeTest.printSummary = function() { - ModeTest.runTests(ModeTest.displayTest); - document.write(ModeTest.passes + ' passes for ' + ModeTest.testCount + ' tests'); -} - -/** - * Basic HTML escaping. - */ -ModeTest.htmlEscape = function(str) { - str = str.toString(); - return str.replace(/[<&]/g, - function(str) {return str == "&" ? "&" : "<";}); -} - From a2ad87f27d021a95f661ac9b106e1f79c6c86a1a Mon Sep 17 00:00:00 2001 From: Marijn Haverbeke Date: Sat, 12 Jan 2013 14:07:08 +0100 Subject: [PATCH 0572/5780] [tests] Overhaul mode testing framework Tests are now specified in a less redundant format, and should be easier to read and write. --- mode/css/test.js | 547 ++---------- mode/gfm/test.js | 309 ++----- mode/markdown/test.js | 1870 +++++++++++++---------------------------- mode/stex/test.js | 447 +++------- mode/xquery/test.js | 121 ++- mode/xquery/xquery.js | 16 +- test/mode_test.js | 309 +++---- 7 files changed, 1072 insertions(+), 2547 deletions(-) diff --git a/mode/css/test.js b/mode/css/test.js index fd6a4b8aa8..4336ddabdf 100644 --- a/mode/css/test.js +++ b/mode/css/test.js @@ -1,501 +1,106 @@ -// Initiate ModeTest and set defaults -var MT = ModeTest; -MT.modeName = 'css'; -MT.modeOptions = {}; +(function() { + var mode = CodeMirror.getMode({tabSize: 4}, "css"); + function MT(name) { test.mode(name, mode, Array.prototype.slice.call(arguments, 2)); } -// Requires at least one media query -MT.testMode( - 'atMediaEmpty', - '@media { }', - [ - 'def', '@media', - null, ' ', - 'error', '{', - null, ' }' - ] -); + // Requires at least one media query + MT("atMediaEmpty", + "[def @media] [error {] }"); -MT.testMode( - 'atMediaMultiple', - '@media not screen and (color), not print and (color) { }', - [ - 'def', '@media', - null, ' ', - 'keyword', 'not', - null, ' ', - 'attribute', 'screen', - null, ' ', - 'operator', 'and', - null, ' (', - 'property', 'color', - null, '), ', - 'keyword', 'not', - null, ' ', - 'attribute', 'print', - null, ' ', - 'operator', 'and', - null, ' (', - 'property', 'color', - null, ') { }' - ] -); + MT("atMediaMultiple", + "[def @media] [keyword not] [attribute screen] [operator and] ([property color]), [keyword not] [attribute print] [operator and] ([property color]) { }"); -MT.testMode( - 'atMediaCheckStack', - '@media screen { } foo { }', - [ - 'def', '@media', - null, ' ', - 'attribute', 'screen', - null, ' { } ', - 'tag', 'foo', - null, ' { }' - ] -); + MT("atMediaCheckStack", + "[def @media] [attribute screen] { } [tag foo] { }"); -MT.testMode( - 'atMediaCheckStack', - '@media screen (color) { } foo { }', - [ - 'def', '@media', - null, ' ', - 'attribute', 'screen', - null, ' (', - 'property', 'color', - null, ') { } ', - 'tag', 'foo', - null, ' { }' - ] -); + MT("atMediaCheckStack", + "[def @media] [attribute screen] ([property color]) { } [tag foo] { }"); -MT.testMode( - 'atMediaCheckStackInvalidAttribute', - '@media foobarhello { } foo { }', - [ - 'def', '@media', - null, ' ', - 'attribute error', 'foobarhello', - null, ' { } ', - 'tag', 'foo', - null, ' { }' - ] -); + MT("atMediaCheckStackInvalidAttribute", + "[def @media] [attribute&error foobarhello] { } [tag foo] { }"); -// Error, because "and" is only allowed immediately preceding a media expression -MT.testMode( - 'atMediaInvalidAttribute', - '@media foobarhello { }', - [ - 'def', '@media', - null, ' ', - 'attribute error', 'foobarhello', - null, ' { }' - ] -); + // Error, because "and" is only allowed immediately preceding a media expression + MT("atMediaInvalidAttribute", + "[def @media] [attribute&error foobarhello] { }"); -// Error, because "and" is only allowed immediately preceding a media expression -MT.testMode( - 'atMediaInvalidAnd', - '@media and screen { }', - [ - 'def', '@media', - null, ' ', - 'error', 'and', - null, ' ', - 'attribute', 'screen', - null, ' { }' - ] -); + // Error, because "and" is only allowed immediately preceding a media expression + MT("atMediaInvalidAnd", + "[def @media] [error and] [attribute screen] { }"); -// Error, because "not" is only allowed as the first item in each media query -MT.testMode( - 'atMediaInvalidNot', - '@media screen not (not) { }', - [ - 'def', '@media', - null, ' ', - 'attribute', 'screen', - null, ' ', - 'error', 'not', - null, ' (', - 'error', 'not', - null, ') { }' - ] -); + // Error, because "not" is only allowed as the first item in each media query + MT("atMediaInvalidNot", + "[def @media] [attribute screen] [error not] ([error not]) { }"); -// Error, because "only" is only allowed as the first item in each media query -MT.testMode( - 'atMediaInvalidOnly', - '@media screen only (only) { }', - [ - 'def', '@media', - null, ' ', - 'attribute', 'screen', - null, ' ', - 'error', 'only', - null, ' (', - 'error', 'only', - null, ') { }' - ] -); + // Error, because "only" is only allowed as the first item in each media query + MT("atMediaInvalidOnly", + "[def @media] [attribute screen] [error only] ([error only]) { }"); -// Error, because "foobarhello" is neither a known type or property, but -// property was expected (after "and"), and it should be in parenthese. -MT.testMode( - 'atMediaUnknownType', - '@media screen and foobarhello { }', - [ - 'def', '@media', - null, ' ', - 'attribute', 'screen', - null, ' ', - 'operator', 'and', - null, ' ', - 'error', 'foobarhello', - null, ' { }' - ] -); + // Error, because "foobarhello" is neither a known type or property, but + // property was expected (after "and"), and it should be in parenthese. + MT("atMediaUnknownType", + "[def @media] [attribute screen] [operator and] [error foobarhello] { }"); -// Error, because "color" is not a known type, but is a known property, and -// should be in parentheses. -MT.testMode( - 'atMediaInvalidType', - '@media screen and color { }', - [ - 'def', '@media', - null, ' ', - 'attribute', 'screen', - null, ' ', - 'operator', 'and', - null, ' ', - 'error', 'color', - null, ' { }' - ] -); + // Error, because "color" is not a known type, but is a known property, and + // should be in parentheses. + MT("atMediaInvalidType", + "[def @media] [attribute screen] [operator and] [error color] { }"); -// Error, because "print" is not a known property, but is a known type, -// and should not be in parenthese. -MT.testMode( - 'atMediaInvalidProperty', - '@media screen and (print) { }', - [ - 'def', '@media', - null, ' ', - 'attribute', 'screen', - null, ' ', - 'operator', 'and', - null, ' (', - 'error', 'print', - null, ') { }' - ] -); + // Error, because "print" is not a known property, but is a known type, + // and should not be in parenthese. + MT("atMediaInvalidProperty", + "[def @media] [attribute screen] [operator and] ([error print]) { }"); -// Soft error, because "foobarhello" is not a known property or type. -MT.testMode( - 'atMediaUnknownProperty', - '@media screen and (foobarhello) { }', - [ - 'def', '@media', - null, ' ', - 'attribute', 'screen', - null, ' ', - 'operator', 'and', - null, ' (', - 'property error', 'foobarhello', - null, ') { }' - ] -); + // Soft error, because "foobarhello" is not a known property or type. + MT("atMediaUnknownProperty", + "[def @media] [attribute screen] [operator and] ([property&error foobarhello]) { }"); -MT.testMode( - 'tagSelector', - 'foo { }', - [ - 'tag', 'foo', - null, ' { }' - ] -); + MT("tagSelector", + "[tag foo] { }"); -MT.testMode( - 'classSelector', - '.foo-bar_hello { }', - [ - 'qualifier', '.foo-bar_hello', - null, ' { }' - ] -); + MT("classSelector", + "[qualifier .foo-bar_hello] { }"); -MT.testMode( - 'idSelector', - '#foo { #foo }', - [ - 'builtin', '#foo', - null, ' { ', - 'error', '#foo', - null, ' }' - ] -); + MT("idSelector", + "[builtin #foo] { [error #foo] }"); -MT.testMode( - 'tagSelectorUnclosed', - 'foo { margin: 0 } bar { }', - [ - 'tag', 'foo', - null, ' { ', - 'property', 'margin', - 'operator', ':', - null, ' ', - 'number', '0', - null, ' } ', - 'tag', 'bar', - null, ' { }' - ] -); + MT("tagSelectorUnclosed", + "[tag foo] { [property margin][operator :] [number 0] } [tag bar] { }"); -MT.testMode( - 'tagStringNoQuotes', - 'foo { font-family: hello world; }', - [ - 'tag', 'foo', - null, ' { ', - 'property', 'font-family', - 'operator', ':', - null, ' ', - 'variable-2', 'hello', - null, ' ', - 'variable-2', 'world', - null, '; }' - ] -); + MT("tagStringNoQuotes", + "[tag foo] { [property font-family][operator :] [variable-2 hello] [variable-2 world]; }"); -MT.testMode( - 'tagStringDouble', - 'foo { font-family: "hello world"; }', - [ - 'tag', 'foo', - null, ' { ', - 'property', 'font-family', - 'operator', ':', - null, ' ', - 'string', '"hello world"', - null, '; }' - ] -); + MT("tagStringDouble", + "[tag foo] { [property font-family][operator :] [string \"hello world\"]; }"); -MT.testMode( - 'tagStringSingle', - 'foo { font-family: \'hello world\'; }', - [ - 'tag', 'foo', - null, ' { ', - 'property', 'font-family', - 'operator', ':', - null, ' ', - 'string', '\'hello world\'', - null, '; }' - ] -); + MT("tagStringSingle", + "[tag foo] { [property font-family][operator :] [string 'hello world']; }"); -MT.testMode( - 'tagColorKeyword', - 'foo { color: black; }', - [ - 'tag', 'foo', - null, ' { ', - 'property', 'color', - 'operator', ':', - null, ' ', - 'keyword', 'black', - null, '; }' - ] -); + MT("tagColorKeyword", + "[tag foo] { [property color][operator :] [keyword black]; }"); -MT.testMode( - 'tagColorHex3', - 'foo { background: #fff; }', - [ - 'tag', 'foo', - null, ' { ', - 'property', 'background', - 'operator', ':', - null, ' ', - 'atom', '#fff', - null, '; }' - ] -); + MT("tagColorHex3", + "[tag foo] { [property background][operator :] [atom #fff]; }"); -MT.testMode( - 'tagColorHex6', - 'foo { background: #ffffff; }', - [ - 'tag', 'foo', - null, ' { ', - 'property', 'background', - 'operator', ':', - null, ' ', - 'atom', '#ffffff', - null, '; }' - ] -); + MT("tagColorHex6", + "[tag foo] { [property background][operator :] [atom #ffffff]; }"); -MT.testMode( - 'tagColorHex4', - 'foo { background: #ffff; }', - [ - 'tag', 'foo', - null, ' { ', - 'property', 'background', - 'operator', ':', - null, ' ', - 'atom error', '#ffff', - null, '; }' - ] -); + MT("tagColorHex4", + "[tag foo] { [property background][operator :] [atom&error #ffff]; }"); -MT.testMode( - 'tagColorHexInvalid', - 'foo { background: #ffg; }', - [ - 'tag', 'foo', - null, ' { ', - 'property', 'background', - 'operator', ':', - null, ' ', - 'atom error', '#ffg', - null, '; }' - ] -); + MT("tagColorHexInvalid", + "[tag foo] { [property background][operator :] [atom&error #ffg]; }"); -MT.testMode( - 'tagNegativeNumber', - 'foo { margin: -5px; }', - [ - 'tag', 'foo', - null, ' { ', - 'property', 'margin', - 'operator', ':', - null, ' ', - 'number', '-5px', - null, '; }' - ] -); + MT("tagNegativeNumber", + "[tag foo] { [property margin][operator :] [number -5px]; }"); -MT.testMode( - 'tagPositiveNumber', - 'foo { padding: 5px; }', - [ - 'tag', 'foo', - null, ' { ', - 'property', 'padding', - 'operator', ':', - null, ' ', - 'number', '5px', - null, '; }' - ] -); + MT("tagPositiveNumber", + "[tag foo] { [property padding][operator :] [number 5px]; }"); -MT.testMode( - 'tagVendor', - 'foo { -foo-box-sizing: -foo-border-box; }', - [ - 'tag', 'foo', - null, ' { ', - 'meta', '-foo-', - 'property', 'box-sizing', - 'operator', ':', - null, ' ', - 'meta', '-foo-', - 'string-2', 'border-box', - null, '; }' - ] -); + MT("tagVendor", + "[tag foo] { [meta -foo-][property box-sizing][operator :] [meta -foo-][string-2 border-box]; }"); -MT.testMode( - 'tagBogusProperty', - 'foo { barhelloworld: 0; }', - [ - 'tag', 'foo', - null, ' { ', - 'property error', 'barhelloworld', - 'operator', ':', - null, ' ', - 'number', '0', - null, '; }' - ] -); + MT("tagBogusProperty", + "[tag foo] { [property&error barhelloworld][operator :] [number 0]; }"); -MT.testMode( - 'tagTwoProperties', - 'foo { margin: 0; padding: 0; }', - [ - 'tag', 'foo', - null, ' { ', - 'property', 'margin', - 'operator', ':', - null, ' ', - 'number', '0', - null, '; ', - 'property', 'padding', - 'operator', ':', - null, ' ', - 'number', '0', - null, '; }' - ] -); -// -//MT.testMode( -// 'tagClass', -// '@media only screen and (min-width: 500px), print {foo.bar#hello { color: black !important; background: #f00; margin: -5px; padding: 5px; -foo-box-sizing: border-box; } /* world */}', -// [ -// 'def', '@media', -// null, ' ', -// 'keyword', 'only', -// null, ' ', -// 'attribute', 'screen', -// null, ' ', -// 'operator', 'and', -// null, ' ', -// 'bracket', '(', -// 'property', 'min-width', -// 'operator', ':', -// null, ' ', -// 'number', '500px', -// 'bracket', ')', -// null, ', ', -// 'attribute', 'print', -// null, ' {', -// 'tag', 'foo', -// 'qualifier', '.bar', -// 'header', '#hello', -// null, ' { ', -// 'property', 'color', -// 'operator', ':', -// null, ' ', -// 'keyword', 'black', -// null, ' ', -// 'keyword', '!important', -// null, '; ', -// 'property', 'background', -// 'operator', ':', -// null, ' ', -// 'atom', '#f00', -// null, '; ', -// 'property', 'padding', -// 'operator', ':', -// null, ' ', -// 'number', '5px', -// null, '; ', -// 'property', 'margin', -// 'operator', ':', -// null, ' ', -// 'number', '-5px', -// null, '; ', -// 'meta', '-foo-', -// 'property', 'box-sizing', -// 'operator', ':', -// null, ' ', -// 'string-2', 'border-box', -// null, '; } ', -// 'comment', '/* world */', -// null, '}' -// ] -//); \ No newline at end of file + MT("tagTwoProperties", + "[tag foo] { [property margin][operator :] [number 0]; [property padding][operator :] [number 0]; }"); +})(); diff --git a/mode/gfm/test.js b/mode/gfm/test.js index 3a261f8f77..cf97df66a5 100644 --- a/mode/gfm/test.js +++ b/mode/gfm/test.js @@ -1,225 +1,84 @@ -// Initiate ModeTest and set defaults -var MT = ModeTest; -MT.modeName = 'gfm'; -MT.modeOptions = {}; - -// Emphasis characters within a word -MT.testMode( - 'emInWordAsterisk', - 'foo*bar*hello', - [ - null, 'foo', - 'em', '*bar*', - null, 'hello' - ] -); -MT.testMode( - 'emInWordUnderscore', - 'foo_bar_hello', - [ - null, 'foo_bar_hello' - ] -); -MT.testMode( - 'emStrongUnderscore', - '___foo___ bar', - [ - 'strong', '__', - 'emstrong', '_foo__', - 'em', '_', - null, ' bar' - ] -); - -// Fenced code blocks -MT.testMode( - 'fencedCodeBlocks', - '```\nfoo\n\n```\nbar', - [ - 'comment', '```', - 'comment', 'foo', - 'comment', '```', - null, 'bar' - ] -); -// Fenced code block mode switching -MT.testMode( - 'fencedCodeBlockModeSwitching', - '```javascript\nfoo\n\n```\nbar', - [ - 'comment', '```javascript', - 'variable', 'foo', - 'comment', '```', - null, 'bar' - ] -); - -// SHA -MT.testMode( - 'SHA', - 'foo be6a8cc1c1ecfe9489fb51e4869af15a13fc2cd2 bar', - [ - null, 'foo ', - 'link', 'be6a8cc1c1ecfe9489fb51e4869af15a13fc2cd2', - null, ' bar' - ] -); -// GitHub highlights hashes 7-40 chars in length -MT.testMode( - 'shortSHA', - 'foo be6a8cc bar', - [ - null, 'foo ', - 'link', 'be6a8cc', - null, ' bar' - ] -); -// Invalid SHAs -// -// GitHub does not highlight hashes shorter than 7 chars -MT.testMode( - 'tooShortSHA', - 'foo be6a8c bar', - [ - null, 'foo be6a8c bar' - ] -); -// GitHub does not highlight hashes longer than 40 chars -MT.testMode( - 'longSHA', - 'foo be6a8cc1c1ecfe9489fb51e4869af15a13fc2cd22 bar', - [ - null, 'foo be6a8cc1c1ecfe9489fb51e4869af15a13fc2cd22 bar' - ] -); -MT.testMode( - 'badSHA', - 'foo be6a8cc1c1ecfe9489fb51e4869af15a13fc2cg2 bar', - [ - null, 'foo be6a8cc1c1ecfe9489fb51e4869af15a13fc2cg2 bar' - ] -); -// User@SHA -MT.testMode( - 'userSHA', - 'foo bar@be6a8cc1c1ecfe9489fb51e4869af15a13fc2cd2 hello', - [ - null, 'foo ', - 'link', 'bar@be6a8cc1c1ecfe9489fb51e4869af15a13fc2cd2', - null, ' hello' - ] -); -// User/Project@SHA -MT.testMode( - 'userProjectSHA', - 'foo bar/hello@be6a8cc1c1ecfe9489fb51e4869af15a13fc2cd2 world', - [ - null, 'foo ', - 'link', 'bar/hello@be6a8cc1c1ecfe9489fb51e4869af15a13fc2cd2', - null, ' world' - ] -); - -// #Num -MT.testMode( - 'num', - 'foo #1 bar', - [ - null, 'foo ', - 'link', '#1', - null, ' bar' - ] -); -// bad #Num -MT.testMode( - 'badNum', - 'foo #1bar hello', - [ - null, 'foo #1bar hello' - ] -); -// User#Num -MT.testMode( - 'userNum', - 'foo bar#1 hello', - [ - null, 'foo ', - 'link', 'bar#1', - null, ' hello' - ] -); -// User/Project#Num -MT.testMode( - 'userProjectNum', - 'foo bar/hello#1 world', - [ - null, 'foo ', - 'link', 'bar/hello#1', - null, ' world' - ] -); - -// Vanilla links -MT.testMode( - 'vanillaLink', - 'foo http://www.example.com/ bar', - [ - null, 'foo ', - 'link', 'http://www.example.com/', - null, ' bar' - ] -); -MT.testMode( - 'vanillaLinkPunctuation', - 'foo http://www.example.com/. bar', - [ - null, 'foo ', - 'link', 'http://www.example.com/', - null, '. bar' - ] -); -MT.testMode( - 'vanillaLinkExtension', - 'foo http://www.example.com/index.html bar', - [ - null, 'foo ', - 'link', 'http://www.example.com/index.html', - null, ' bar' - ] -); -// Not a link -MT.testMode( - 'notALink', - '```css\nfoo {color:black;}\n```http://www.example.com/', - [ - 'comment', '```css', - 'tag', 'foo', - null, ' {', - 'property', 'color', - 'operator', ':', - 'keyword', 'black', - null, ';}', - 'comment', '```', - 'link', 'http://www.example.com/' - ] -); -// Not a link -MT.testMode( - 'notALink', - '``foo `bar` http://www.example.com/`` hello', - [ - 'comment', '``foo `bar` http://www.example.com/``', - null, ' hello' - ] -); -// Not a link -MT.testMode( - 'notALink', - '`foo\nhttp://www.example.com/\n`foo\n\nhttp://www.example.com/', - [ - 'comment', '`foo', - 'link', 'http://www.example.com/', - 'comment', '`foo', - 'link', 'http://www.example.com/' - ] -); \ No newline at end of file +(function() { + var mode = CodeMirror.getMode({tabSize: 4}, "gfm"); + function MT(name) { test.mode(name, mode, Array.prototype.slice.call(arguments, 1)); } + + MT("emInWordAsterisk", + "foo[em *bar*]hello"); + + MT("emInWordUnderscore", + "foo_bar_hello"); + + MT("emStrongUnderscore", + "[strong __][emstrong _foo__][em _] bar"); + + MT("fencedCodeBlocks", + "[comment ```]", + "[comment foo]", + "", + "[comment ```]", + "bar"); + + MT("fencedCodeBlockModeSwitching", + "[comment ```javascript]", + "[variable foo]", + "", + "[comment ```]", + "bar"); + + MT("SHA", + "foo [link be6a8cc1c1ecfe9489fb51e4869af15a13fc2cd2] bar"); + + MT("shortSHA", + "foo [link be6a8cc] bar"); + + MT("tooShortSHA", + "foo be6a8c bar"); + + MT("longSHA", + "foo be6a8cc1c1ecfe9489fb51e4869af15a13fc2cd22 bar"); + + MT("badSHA", + "foo be6a8cc1c1ecfe9489fb51e4869af15a13fc2cg2 bar"); + + MT("userSHA", + "foo [link bar@be6a8cc1c1ecfe9489fb51e4869af15a13fc2cd2] hello"); + + MT("userProjectSHA", + "foo [link bar/hello@be6a8cc1c1ecfe9489fb51e4869af15a13fc2cd2] world"); + + MT("num", + "foo [link #1] bar"); + + MT("badNum", + "foo #1bar hello"); + + MT("userNum", + "foo [link bar#1] hello"); + + MT("userProjectNum", + "foo [link bar/hello#1] world"); + + MT("vanillaLink", + "foo [link http://www.example.com/] bar"); + + MT("vanillaLinkPunctuation", + "foo [link http://www.example.com/]. bar"); + + MT("vanillaLinkExtension", + "foo [link http://www.example.com/index.html] bar"); + + MT("notALink", + "[comment ```css]", + "[tag foo] {[property color][operator :][keyword black];}", + "[comment ```][link http://www.example.com/]"); + + MT("notALink", + "[comment ``foo `bar` http://www.example.com/``] hello"); + + MT("notALink", + "[comment `foo]", + "[link http://www.example.com/]", + "[comment `foo]", + "", + "[link http://www.example.com/]"); +})(); diff --git a/mode/markdown/test.js b/mode/markdown/test.js index 2e06707c11..eaa7343a20 100644 --- a/mode/markdown/test.js +++ b/mode/markdown/test.js @@ -1,1291 +1,579 @@ -// Initiate ModeTest and set defaults -var MT = ModeTest; -MT.modeName = 'markdown'; -MT.modeOptions = {}; - -MT.testMode( - 'plainText', - 'foo', - [ - null, 'foo' - ] -); - -// Code blocks using 4 spaces (regardless of CodeMirror.tabSize value) -MT.testMode( - 'codeBlocksUsing4Spaces', - ' foo', - [ - null, ' ', - 'comment', 'foo' - ] -); -// Code blocks using 4 spaces with internal indentation -MT.testMode( - 'codeBlocksUsing4SpacesIndentation', - ' bar\n hello\n world\n foo\nbar', - [ - null, ' ', - 'comment', 'bar', - null, ' ', - 'comment', 'hello', - null, ' ', - 'comment', 'world', - null, ' ', - 'comment', 'foo', - null, 'bar' - ] -); -// Code blocks using 4 spaces with internal indentation -MT.testMode( - 'codeBlocksUsing4SpacesIndentation', - ' foo\n bar\n hello\n world', - [ - null, ' foo', - null, ' ', - 'comment', 'bar', - null, ' ', - 'comment', 'hello', - null, ' ', - 'comment', 'world' - ] -); - -// Code blocks using 1 tab (regardless of CodeMirror.indentWithTabs value) -MT.testMode( - 'codeBlocksUsing1Tab', - '\tfoo', - [ - null, '\t', - 'comment', 'foo' - ] -); - -// Inline code using backticks -MT.testMode( - 'inlineCodeUsingBackticks', - 'foo `bar`', - [ - null, 'foo ', - 'comment', '`bar`' - ] -); - -// Block code using single backtick (shouldn't work) -MT.testMode( - 'blockCodeSingleBacktick', - '`\nfoo\n`', - [ - 'comment', '`', - null, 'foo', - 'comment', '`' - ] -); - -// Unclosed backticks -// Instead of simply marking as CODE, it would be nice to have an -// incomplete flag for CODE, that is styled slightly different. -MT.testMode( - 'unclosedBackticks', - 'foo `bar', - [ - null, 'foo ', - 'comment', '`bar' - ] -); - -// Per documentation: "To include a literal backtick character within a -// code span, you can use multiple backticks as the opening and closing -// delimiters" -MT.testMode( - 'doubleBackticks', - '``foo ` bar``', - [ - 'comment', '``foo ` bar``' - ] -); - -// Tests based on Dingus -// http://daringfireball.net/projects/markdown/dingus -// -// Multiple backticks within an inline code block -MT.testMode( - 'consecutiveBackticks', - '`foo```bar`', - [ - 'comment', '`foo```bar`' - ] -); -// Multiple backticks within an inline code block with a second code block -MT.testMode( - 'consecutiveBackticks', - '`foo```bar` hello `world`', - [ - 'comment', '`foo```bar`', - null, ' hello ', - 'comment', '`world`' - ] -); -// Unclosed with several different groups of backticks -MT.testMode( - 'unclosedBackticks', - '``foo ``` bar` hello', - [ - 'comment', '``foo ``` bar` hello' - ] -); -// Closed with several different groups of backticks -MT.testMode( - 'closedBackticks', - '``foo ``` bar` hello`` world', - [ - 'comment', '``foo ``` bar` hello``', - null, ' world' - ] -); - -// atx headers -// http://daringfireball.net/projects/markdown/syntax#header -// -// H1 -MT.testMode( - 'atxH1', - '# foo', - [ - 'header', '# foo' - ] -); -// H2 -MT.testMode( - 'atxH2', - '## foo', - [ - 'header', '## foo' - ] -); -// H3 -MT.testMode( - 'atxH3', - '### foo', - [ - 'header', '### foo' - ] -); -// H4 -MT.testMode( - 'atxH4', - '#### foo', - [ - 'header', '#### foo' - ] -); -// H5 -MT.testMode( - 'atxH5', - '##### foo', - [ - 'header', '##### foo' - ] -); -// H6 -MT.testMode( - 'atxH6', - '###### foo', - [ - 'header', '###### foo' - ] -); -// H6 - 7x '#' should still be H6, per Dingus -// http://daringfireball.net/projects/markdown/dingus -MT.testMode( - 'atxH6NotH7', - '####### foo', - [ - 'header', '####### foo' - ] -); - -// Setext headers - H1, H2 -// Per documentation, "Any number of underlining =’s or -’s will work." -// http://daringfireball.net/projects/markdown/syntax#header -// Ideally, the text would be marked as `header` as well, but this is -// not really feasible at the moment. So, instead, we're testing against -// what works today, to avoid any regressions. -// -// Check if single underlining = works -MT.testMode( - 'setextH1', - 'foo\n=', - [ - null, 'foo', - 'header', '=' - ] -); -// Check if 3+ ='s work -MT.testMode( - 'setextH1', - 'foo\n===', - [ - null, 'foo', - 'header', '===' - ] -); -// Check if single underlining - works -MT.testMode( - 'setextH2', - 'foo\n-', - [ - null, 'foo', - 'header', '-' - ] -); -// Check if 3+ -'s work -MT.testMode( - 'setextH2', - 'foo\n---', - [ - null, 'foo', - 'header', '---' - ] -); - -// Single-line blockquote with trailing space -MT.testMode( - 'blockquoteSpace', - '> foo', - [ - 'quote', '> foo' - ] -); - -// Single-line blockquote -MT.testMode( - 'blockquoteNoSpace', - '>foo', - [ - 'quote', '>foo' - ] -); - -// Single-line blockquote followed by normal paragraph -MT.testMode( - 'blockquoteThenParagraph', - '>foo\n\nbar', - [ - 'quote', '>foo', - null, 'bar' - ] -); - -// Multi-line blockquote (lazy mode) -MT.testMode( - 'multiBlockquoteLazy', - '>foo\nbar', - [ - 'quote', '>foo', - 'quote', 'bar' - ] -); - -// Multi-line blockquote followed by normal paragraph (lazy mode) -MT.testMode( - 'multiBlockquoteLazyThenParagraph', - '>foo\nbar\n\nhello', - [ - 'quote', '>foo', - 'quote', 'bar', - null, 'hello' - ] -); - -// Multi-line blockquote (non-lazy mode) -MT.testMode( - 'multiBlockquote', - '>foo\n>bar', - [ - 'quote', '>foo', - 'quote', '>bar' - ] -); - -// Multi-line blockquote followed by normal paragraph (non-lazy mode) -MT.testMode( - 'multiBlockquoteThenParagraph', - '>foo\n>bar\n\nhello', - [ - 'quote', '>foo', - 'quote', '>bar', - null, 'hello' - ] -); - -// Check list types -MT.testMode( - 'listAsterisk', - '* foo\n* bar', - [ - 'string', '* foo', - 'string', '* bar' - ] -); -MT.testMode( - 'listPlus', - '+ foo\n+ bar', - [ - 'string', '+ foo', - 'string', '+ bar' - ] -); -MT.testMode( - 'listDash', - '- foo\n- bar', - [ - 'string', '- foo', - 'string', '- bar' - ] -); -MT.testMode( - 'listNumber', - '1. foo\n2. bar', - [ - 'string', '1. foo', - 'string', '2. bar' - ] -); - -// Formatting in lists (*) -MT.testMode( - 'listAsteriskFormatting', - '* *foo* bar\n\n* **foo** bar\n\n* ***foo*** bar\n\n* `foo` bar', - [ - 'string', '* ', - 'string em', '*foo*', - 'string', ' bar', - 'string', '* ', - 'string strong', '**foo**', - 'string', ' bar', - 'string', '* ', - 'string strong', '**', - 'string emstrong', '*foo**', - 'string em', '*', - 'string', ' bar', - 'string', '* ', - 'string comment', '`foo`', - 'string', ' bar' - ] -); -// Formatting in lists (+) -MT.testMode( - 'listPlusFormatting', - '+ *foo* bar\n\n+ **foo** bar\n\n+ ***foo*** bar\n\n+ `foo` bar', - [ - 'string', '+ ', - 'string em', '*foo*', - 'string', ' bar', - 'string', '+ ', - 'string strong', '**foo**', - 'string', ' bar', - 'string', '+ ', - 'string strong', '**', - 'string emstrong', '*foo**', - 'string em', '*', - 'string', ' bar', - 'string', '+ ', - 'string comment', '`foo`', - 'string', ' bar' - ] -); -// Formatting in lists (-) -MT.testMode( - 'listDashFormatting', - '- *foo* bar\n\n- **foo** bar\n\n- ***foo*** bar\n\n- `foo` bar', - [ - 'string', '- ', - 'string em', '*foo*', - 'string', ' bar', - 'string', '- ', - 'string strong', '**foo**', - 'string', ' bar', - 'string', '- ', - 'string strong', '**', - 'string emstrong', '*foo**', - 'string em', '*', - 'string', ' bar', - 'string', '- ', - 'string comment', '`foo`', - 'string', ' bar' - ] -); -// Formatting in lists (1.) -MT.testMode( - 'listNumberFormatting', - '1. *foo* bar\n\n2. **foo** bar\n\n3. ***foo*** bar\n\n4. `foo` bar', - [ - 'string', '1. ', - 'string em', '*foo*', - 'string', ' bar', - 'string', '2. ', - 'string strong', '**foo**', - 'string', ' bar', - 'string', '3. ', - 'string strong', '**', - 'string emstrong', '*foo**', - 'string em', '*', - 'string', ' bar', - 'string', '4. ', - 'string comment', '`foo`', - 'string', ' bar' - ] -); - -// Paragraph lists -MT.testMode( - 'listParagraph', - '* foo\n\n* bar', - [ - 'string', '* foo', - 'string', '* bar' - ] -); - -// Multi-paragraph lists -// -// 4 spaces -MT.testMode( - 'listMultiParagraph', - '* foo\n\n* bar\n\n hello', - [ - 'string', '* foo', - 'string', '* bar', - null, ' ', - 'string', 'hello' - ] -); -// 4 spaces, extra blank lines (should still be list, per Dingus) -MT.testMode( - 'listMultiParagraphExtra', - '* foo\n\n* bar\n\n\n hello', - [ - 'string', '* foo', - 'string', '* bar', - null, ' ', - 'string', 'hello' - ] -); -// 4 spaces, plus 1 space (should still be list, per Dingus) -MT.testMode( - 'listMultiParagraphExtraSpace', - '* foo\n\n* bar\n\n hello\n\n world', - [ - 'string', '* foo', - 'string', '* bar', - null, ' ', - 'string', 'hello', - null, ' ', - 'string', 'world' - ] -); -// 1 tab -MT.testMode( - 'listTab', - '* foo\n\n* bar\n\n\thello', - [ - 'string', '* foo', - 'string', '* bar', - null, '\t', - 'string', 'hello' - ] -); -// No indent -MT.testMode( - 'listNoIndent', - '* foo\n\n* bar\n\nhello', - [ - 'string', '* foo', - 'string', '* bar', - null, 'hello' - ] -); -// Blockquote -MT.testMode( - 'blockquote', - '* foo\n\n* bar\n\n > hello', - [ - 'string', '* foo', - 'string', '* bar', - null, ' ', - 'string quote', '> hello' - ] -); -// Code block -MT.testMode( - 'blockquoteCode', - '* foo\n\n* bar\n\n > hello\n\n world', - [ - 'string', '* foo', - 'string', '* bar', - null, ' ', - 'comment', '> hello', - null, ' ', - 'string', 'world' - ] -); -// Code block followed by text -MT.testMode( - 'blockquoteCodeText', - '* foo\n\n bar\n\n hello\n\n world', - [ - 'string', '* foo', - null, ' ', - 'string', 'bar', - null, ' ', - 'comment', 'hello', - null, ' ', - 'string', 'world' - ] -); - -// Nested list -// -// * -MT.testMode( - 'listAsteriskNested', - '* foo\n\n * bar', - [ - 'string', '* foo', - null, ' ', - 'string', '* bar' - ] -); -// + -MT.testMode( - 'listPlusNested', - '+ foo\n\n + bar', - [ - 'string', '+ foo', - null, ' ', - 'string', '+ bar' - ] -); -// - -MT.testMode( - 'listDashNested', - '- foo\n\n - bar', - [ - 'string', '- foo', - null, ' ', - 'string', '- bar' - ] -); -// 1. -MT.testMode( - 'listNumberNested', - '1. foo\n\n 2. bar', - [ - 'string', '1. foo', - null, ' ', - 'string', '2. bar' - ] -); -// Mixed -MT.testMode( - 'listMixed', - '* foo\n\n + bar\n\n - hello\n\n 1. world', - [ - 'string', '* foo', - null, ' ', - 'string', '+ bar', - null, ' ', - 'string', '- hello', - null, ' ', - 'string', '1. world' - ] -); -// Blockquote -MT.testMode( - 'listBlockquote', - '* foo\n\n + bar\n\n > hello', - [ - 'string', '* foo', - null, ' ', - 'string', '+ bar', - null, ' ', - 'quote string', '> hello' - ] -); -// Code -MT.testMode( - 'listCode', - '* foo\n\n + bar\n\n hello', - [ - 'string', '* foo', - null, ' ', - 'string', '+ bar', - null, ' ', - 'comment', 'hello' - ] -); -// Code with internal indentation -MT.testMode( - 'listCodeIndentation', - '* foo\n\n bar\n hello\n world\n foo\n bar', - [ - 'string', '* foo', - null, ' ', - 'comment', 'bar', - null, ' ', - 'comment', 'hello', - null, ' ', - 'comment', 'world', - null, ' ', - 'comment', 'foo', - null, ' ', - 'string', 'bar' - ] -); -// Code followed by text -MT.testMode( - 'listCodeText', - '* foo\n\n bar\n\nhello', - [ - 'string', '* foo', - null, ' ', - 'comment', 'bar', - null, 'hello' - ] -); - -// Following tests directly from official Markdown documentation -// http://daringfireball.net/projects/markdown/syntax#hr -MT.testMode( - 'hrSpace', - '* * *', - [ - 'hr', '* * *' - ] -); - -MT.testMode( - 'hr', - '***', - [ - 'hr', '***' - ] -); - -MT.testMode( - 'hrLong', - '*****', - [ - 'hr', '*****' - ] -); - -MT.testMode( - 'hrSpaceDash', - '- - -', - [ - 'hr', '- - -' - ] -); - -MT.testMode( - 'hrDashLong', - '---------------------------------------', - [ - 'hr', '---------------------------------------' - ] -); - -// Inline link with title -MT.testMode( - 'linkTitle', - '[foo](http://example.com/ "bar") hello', - [ - 'link', '[foo]', - 'string', '(http://example.com/ "bar")', - null, ' hello' - ] -); - -// Inline link without title -MT.testMode( - 'linkNoTitle', - '[foo](http://example.com/) bar', - [ - 'link', '[foo]', - 'string', '(http://example.com/)', - null, ' bar' - ] -); - -// Inline link with image -MT.testMode( - 'linkImage', - '[![foo](http://example.com/)](http://example.com/) bar', - [ - 'link', '[', - 'tag', '![foo]', - 'string', '(http://example.com/)', - 'link', ']', - 'string', '(http://example.com/)', - null, ' bar' - ] -); - -// Inline link with Em -MT.testMode( - 'linkEm', - '[*foo*](http://example.com/) bar', - [ - 'link', '[', - 'link em', '*foo*', - 'link', ']', - 'string', '(http://example.com/)', - null, ' bar' - ] -); - -// Inline link with Strong -MT.testMode( - 'linkStrong', - '[**foo**](http://example.com/) bar', - [ - 'link', '[', - 'link strong', '**foo**', - 'link', ']', - 'string', '(http://example.com/)', - null, ' bar' - ] -); - -// Inline link with EmStrong -MT.testMode( - 'linkEmStrong', - '[***foo***](http://example.com/) bar', - [ - 'link', '[', - 'link strong', '**', - 'link emstrong', '*foo**', - 'link em', '*', - 'link', ']', - 'string', '(http://example.com/)', - null, ' bar' - ] -); - -// Image with title -MT.testMode( - 'imageTitle', - '![foo](http://example.com/ "bar") hello', - [ - 'tag', '![foo]', - 'string', '(http://example.com/ "bar")', - null, ' hello' - ] -); - -// Image without title -MT.testMode( - 'imageNoTitle', - '![foo](http://example.com/) bar', - [ - 'tag', '![foo]', - 'string', '(http://example.com/)', - null, ' bar' - ] -); - -// Image with asterisks -MT.testMode( - 'imageAsterisks', - '![*foo*](http://example.com/) bar', - [ - 'tag', '![*foo*]', - 'string', '(http://example.com/)', - null, ' bar' - ] -); - -// Not a link. Should be normal text due to square brackets being used -// regularly in text, especially in quoted material, and no space is allowed -// between square brackets and parentheses (per Dingus). -MT.testMode( - 'notALink', - '[foo] (bar)', - [ - null, '[foo] (bar)' - ] -); - -// Reference-style links -MT.testMode( - 'linkReference', - '[foo][bar] hello', - [ - 'link', '[foo]', - 'string', '[bar]', - null, ' hello' - ] -); -// Reference-style links with Em -MT.testMode( - 'linkReferenceEm', - '[*foo*][bar] hello', - [ - 'link', '[', - 'link em', '*foo*', - 'link', ']', - 'string', '[bar]', - null, ' hello' - ] -); -// Reference-style links with Strong -MT.testMode( - 'linkReferenceStrong', - '[**foo**][bar] hello', - [ - 'link', '[', - 'link strong', '**foo**', - 'link', ']', - 'string', '[bar]', - null, ' hello' - ] -); -// Reference-style links with EmStrong -MT.testMode( - 'linkReferenceEmStrong', - '[***foo***][bar] hello', - [ - 'link', '[', - 'link strong', '**', - 'link emstrong', '*foo**', - 'link em', '*', - 'link', ']', - 'string', '[bar]', - null, ' hello' - ] -); - -// Reference-style links with optional space separator (per docuentation) -// "You can optionally use a space to separate the sets of brackets" -MT.testMode( - 'linkReferenceSpace', - '[foo] [bar] hello', - [ - 'link', '[foo]', - null, ' ', - 'string', '[bar]', - null, ' hello' - ] -); -// Should only allow a single space ("...use *a* space...") -MT.testMode( - 'linkReferenceDoubleSpace', - '[foo] [bar] hello', - [ - null, '[foo] [bar] hello' - ] -); - -// Reference-style links with implicit link name -MT.testMode( - 'linkImplicit', - '[foo][] hello', - [ - 'link', '[foo]', - 'string', '[]', - null, ' hello' - ] -); - -// @todo It would be nice if, at some point, the document was actually -// checked to see if the referenced link exists - -// Link label, for reference-style links (taken from documentation) -// -// No title -MT.testMode( - 'labelNoTitle', - '[foo]: http://example.com/', - [ - 'link', '[foo]:', - null, ' ', - 'string', 'http://example.com/' - ] -); -// Indented -MT.testMode( - 'labelIndented', - ' [foo]: http://example.com/', - [ - null, ' ', - 'link', '[foo]:', - null, ' ', - 'string', 'http://example.com/' - ] -); -// Space in ID and title -MT.testMode( - 'labelSpaceTitle', - '[foo bar]: http://example.com/ "hello"', - [ - 'link', '[foo bar]:', - null, ' ', - 'string', 'http://example.com/ "hello"' - ] -); -// Double title -MT.testMode( - 'labelDoubleTitle', - '[foo bar]: http://example.com/ "hello" "world"', - [ - 'link', '[foo bar]:', - null, ' ', - 'string', 'http://example.com/ "hello"', - null, ' "world"' - ] -); -// Double quotes around title -MT.testMode( - 'labelTitleDoubleQuotes', - '[foo]: http://example.com/ "bar"', - [ - 'link', '[foo]:', - null, ' ', - 'string', 'http://example.com/ "bar"' - ] -); -// Single quotes around title -MT.testMode( - 'labelTitleSingleQuotes', - '[foo]: http://example.com/ \'bar\'', - [ - 'link', '[foo]:', - null, ' ', - 'string', 'http://example.com/ \'bar\'' - ] -); -// Parentheses around title -MT.testMode( - 'labelTitleParenthese', - '[foo]: http://example.com/ (bar)', - [ - 'link', '[foo]:', - null, ' ', - 'string', 'http://example.com/ (bar)' - ] -); -// Invalid title -MT.testMode( - 'labelTitleInvalid', - '[foo]: http://example.com/ bar', - [ - 'link', '[foo]:', - null, ' ', - 'string', 'http://example.com/', - null, ' bar' - ] -); -// Angle brackets around URL -MT.testMode( - 'labelLinkAngleBrackets', - '[foo]: "bar"', - [ - 'link', '[foo]:', - null, ' ', - 'string', ' "bar"' - ] -); -// Title on next line per documentation (double quotes) -MT.testMode( - 'labelTitleNextDoubleQuotes', - '[foo]: http://example.com/\n"bar" hello', - [ - 'link', '[foo]:', - null, ' ', - 'string', 'http://example.com/', - 'string', '"bar"', - null, ' hello' - ] -); -// Title on next line per documentation (single quotes) -MT.testMode( - 'labelTitleNextSingleQuotes', - '[foo]: http://example.com/\n\'bar\' hello', - [ - 'link', '[foo]:', - null, ' ', - 'string', 'http://example.com/', - 'string', '\'bar\'', - null, ' hello' - ] -); -// Title on next line per documentation (parentheses) -MT.testMode( - 'labelTitleNextParenthese', - '[foo]: http://example.com/\n(bar) hello', - [ - 'link', '[foo]:', - null, ' ', - 'string', 'http://example.com/', - 'string', '(bar)', - null, ' hello' - ] -); -// Title on next line per documentation (mixed) -MT.testMode( - 'labelTitleNextMixed', - '[foo]: http://example.com/\n(bar" hello', - [ - 'link', '[foo]:', - null, ' ', - 'string', 'http://example.com/', - null, '(bar" hello' - ] -); - -// Automatic links -MT.testMode( - 'linkWeb', - ' foo', - [ - 'link', '', - null, ' foo' - ] -); - -// Automatic email links -MT.testMode( - 'linkEmail', - ' foo', - [ - 'link', '', - null, ' foo' - ] -); - -// Single asterisk -MT.testMode( - 'emAsterisk', - '*foo* bar', - [ - 'em', '*foo*', - null, ' bar' - ] -); - -// Single underscore -MT.testMode( - 'emUnderscore', - '_foo_ bar', - [ - 'em', '_foo_', - null, ' bar' - ] -); - -// Emphasis characters within a word -MT.testMode( - 'emInWordAsterisk', - 'foo*bar*hello', - [ - null, 'foo', - 'em', '*bar*', - null, 'hello' - ] -); -MT.testMode( - 'emInWordUnderscore', - 'foo_bar_hello', - [ - null, 'foo', - 'em', '_bar_', - null, 'hello' - ] -); -// Per documentation: "...surround an * or _ with spaces, it’ll be -// treated as a literal asterisk or underscore." -// -// Inside EM -MT.testMode( - 'emEscapedBySpaceIn', - 'foo _bar _ hello_ world', - [ - null, 'foo ', - 'em', '_bar _ hello_', - null, ' world' - ] -); -// Outside EM -MT.testMode( - 'emEscapedBySpaceOut', - 'foo _ bar_hello_world', - [ - null, 'foo _ bar', - 'em', '_hello_', - null, 'world' - ] -); - -// Unclosed emphasis characters -// Instead of simply marking as EM / STRONG, it would be nice to have an -// incomplete flag for EM and STRONG, that is styled slightly different. -MT.testMode( - 'emIncompleteAsterisk', - 'foo *bar', - [ - null, 'foo ', - 'em', '*bar' - ] -); -MT.testMode( - 'emIncompleteUnderscore', - 'foo _bar', - [ - null, 'foo ', - 'em', '_bar' - ] -); - -// Double asterisk -MT.testMode( - 'strongAsterisk', - '**foo** bar', - [ - 'strong', '**foo**', - null, ' bar' - ] -); - -// Double underscore -MT.testMode( - 'strongUnderscore', - '__foo__ bar', - [ - 'strong', '__foo__', - null, ' bar' - ] -); - -// Triple asterisk -MT.testMode( - 'emStrongAsterisk', - '*foo**bar*hello** world', - [ - 'em', '*foo', - 'emstrong', '**bar*', - 'strong', 'hello**', - null, ' world' - ] -); - -// Triple underscore -MT.testMode( - 'emStrongUnderscore', - '_foo__bar_hello__ world', - [ - 'em', '_foo', - 'emstrong', '__bar_', - 'strong', 'hello__', - null, ' world' - ] -); - -// Triple mixed -// "...same character must be used to open and close an emphasis span."" -MT.testMode( - 'emStrongMixed', - '_foo**bar*hello__ world', - [ - 'em', '_foo', - 'emstrong', '**bar*hello__ world' - ] -); - -MT.testMode( - 'emStrongMixed', - '*foo__bar_hello** world', - [ - 'em', '*foo', - 'emstrong', '__bar_hello** world' - ] -); - -// These characters should be escaped: -// \ backslash -// ` backtick -// * asterisk -// _ underscore -// {} curly braces -// [] square brackets -// () parentheses -// # hash mark -// + plus sign -// - minus sign (hyphen) -// . dot -// ! exclamation mark -// -// Backtick (code) -MT.testMode( - 'escapeBacktick', - 'foo \\`bar\\`', - [ - null, 'foo \\`bar\\`' - ] -); -MT.testMode( - 'doubleEscapeBacktick', - 'foo \\\\`bar\\\\`', - [ - null, 'foo \\\\', - 'comment', '`bar\\\\`' - ] -); -// Asterisk (em) -MT.testMode( - 'escapeAsterisk', - 'foo \\*bar\\*', - [ - null, 'foo \\*bar\\*' - ] -); -MT.testMode( - 'doubleEscapeAsterisk', - 'foo \\\\*bar\\\\*', - [ - null, 'foo \\\\', - 'em', '*bar\\\\*' - ] -); -// Underscore (em) -MT.testMode( - 'escapeUnderscore', - 'foo \\_bar\\_', - [ - null, 'foo \\_bar\\_' - ] -); -MT.testMode( - 'doubleEscapeUnderscore', - 'foo \\\\_bar\\\\_', - [ - null, 'foo \\\\', - 'em', '_bar\\\\_' - ] -); -// Hash mark (headers) -MT.testMode( - 'escapeHash', - '\\# foo', - [ - null, '\\# foo' - ] -); -MT.testMode( - 'doubleEscapeHash', - '\\\\# foo', - [ - null, '\\\\# foo' - ] -); +(function() { + var mode = CodeMirror.getMode({tabSize: 4}, "markdown"); + function MT(name) { test.mode(name, mode, Array.prototype.slice.call(arguments, 1)); } + + MT("plainText", + "foo"); + + // Code blocks using 4 spaces (regardless of CodeMirror.tabSize value) + MT("codeBlocksUsing4Spaces", + " [comment foo]"); + + // Code blocks using 4 spaces with internal indentation + MT("codeBlocksUsing4SpacesIndentation", + " [comment bar]", + " [comment hello]", + " [comment world]", + " [comment foo]", + "bar"); + + // Code blocks using 4 spaces with internal indentation + MT("codeBlocksUsing4SpacesIndentation", + " foo", + " [comment bar]", + " [comment hello]", + " [comment world]"); + + // Code blocks using 1 tab (regardless of CodeMirror.indentWithTabs value) + MT("codeBlocksUsing1Tab", + "\t[comment foo]"); + + // Inline code using backticks + MT("inlineCodeUsingBackticks", + "foo [comment `bar`]"); + + // Block code using single backtick (shouldn't work) + MT("blockCodeSingleBacktick", + "[comment `]", + "foo", + "[comment `]"); + + // Unclosed backticks + // Instead of simply marking as CODE, it would be nice to have an + // incomplete flag for CODE, that is styled slightly different. + MT("unclosedBackticks", + "foo [comment `bar]"); + + // Per documentation: "To include a literal backtick character within a + // code span, you can use multiple backticks as the opening and closing + // delimiters" + MT("doubleBackticks", + "[comment ``foo ` bar``]"); + + // Tests based on Dingus + // http://daringfireball.net/projects/markdown/dingus + // + // Multiple backticks within an inline code block + MT("consecutiveBackticks", + "[comment `foo```bar`]"); + + // Multiple backticks within an inline code block with a second code block + MT("consecutiveBackticks", + "[comment `foo```bar`] hello [comment `world`]"); + + // Unclosed with several different groups of backticks + MT("unclosedBackticks", + "[comment ``foo ``` bar` hello]"); + + // Closed with several different groups of backticks + MT("closedBackticks", + "[comment ``foo ``` bar` hello``] world"); + + // atx headers + // http://daringfireball.net/projects/markdown/syntax#header + + MT("atxH1", + "[header # foo]"); + + MT("atxH2", + "[header ## foo]"); + + MT("atxH3", + "[header ### foo]"); + + MT("atxH4", + "[header #### foo]"); + + MT("atxH5", + "[header ##### foo]"); + + MT("atxH6", + "[header ###### foo]"); + + // H6 - 7x '#' should still be H6, per Dingus + // http://daringfireball.net/projects/markdown/dingus + MT("atxH6NotH7", + "[header ####### foo]"); + + // Setext headers - H1, H2 + // Per documentation, "Any number of underlining =’s or -’s will work." + // http://daringfireball.net/projects/markdown/syntax#header + // Ideally, the text would be marked as `header` as well, but this is + // not really feasible at the moment. So, instead, we're testing against + // what works today, to avoid any regressions. + // + // Check if single underlining = works + MT("setextH1", + "foo", + "[header =]"); + + // Check if 3+ ='s work + MT("setextH1", + "foo", + "[header ===]"); + + // Check if single underlining - works + MT("setextH2", + "foo", + "[header -]"); + + // Check if 3+ -'s work + MT("setextH2", + "foo", + "[header ---]"); + + // Single-line blockquote with trailing space + MT("blockquoteSpace", + "[quote > foo]"); + + // Single-line blockquote + MT("blockquoteNoSpace", + "[quote >foo]"); + + // Single-line blockquote followed by normal paragraph + MT("blockquoteThenParagraph", + "[quote >foo]", + "", + "bar"); + + // Multi-line blockquote (lazy mode) + MT("multiBlockquoteLazy", + "[quote >foo]", + "[quote bar]"); + + // Multi-line blockquote followed by normal paragraph (lazy mode) + MT("multiBlockquoteLazyThenParagraph", + "[quote >foo]", + "[quote bar]", + "", + "hello"); + + // Multi-line blockquote (non-lazy mode) + MT("multiBlockquote", + "[quote >foo]", + "[quote >bar]"); + + // Multi-line blockquote followed by normal paragraph (non-lazy mode) + MT("multiBlockquoteThenParagraph", + "[quote >foo]", + "[quote >bar]", + "", + "hello"); + + // Check list types + + MT("listAsterisk", + "[string * foo]", + "[string * bar]"); + + MT("listPlus", + "[string + foo]", + "[string + bar]"); + + MT("listDash", + "[string - foo]", + "[string - bar]"); + + MT("listNumber", + "[string 1. foo]", + "[string 2. bar]"); + + // Formatting in lists (*) + MT("listAsteriskFormatting", + "[string * ][string&em *foo*][string bar]", + "[string * ][string&strong **foo**][string bar]", + "[string * ][string&strong **][string&emstrong *foo**][string&em *][string bar]", + "[string * ][string&comment `foo`][string bar]"); + + // Formatting in lists (+) + MT("listPlusFormatting", + "[string + ][string&em *foo*][string bar]", + "[string + ][string&strong **foo**][string bar]", + "[string + ][string&strong **][string&emstrong *foo**][string&em *][string bar]", + "[string + ][string&comment `foo`][string bar]"); + + // Formatting in lists (-) + MT("listDashFormatting", + "[string - ][string&em *foo*][string bar]", + "[string - ][string&strong **foo**][string bar]", + "[string - ][string&strong **][string&emstrong *foo**][string&em *][string bar]", + "[string - ][string&comment `foo`][string bar]"); + + // Formatting in lists (1.) + MT("listNumberFormatting", + "[string 1. ][string&em *foo*][string bar]", + "[string 2. ][string&strong **foo**][string bar]", + "[string 3. ][string&strong **][string&emstrong *foo**][string&em *][string bar]", + "[string 4. ][string&comment `foo`][string bar]"); + + // Paragraph lists + MT("listParagraph", + "[string * foo]", + "", + "[string * bar]"); + + // Multi-paragraph lists + // + // 4 spaces + MT("listMultiParagraph", + "[string * foo]", + "", + "[string * bar]", + "", + " [string hello]"); + + // 4 spaces, extra blank lines (should still be list, per Dingus) + MT("listMultiParagraphExtra", + "[string * foo]", + "", + "[string * bar]", + "", + " [string hello]"); + + // 4 spaces, plus 1 space (should still be list, per Dingus) + MT("listMultiParagraphExtraSpace", + "[string * foo]", + "", + "[string * bar]", + "", + " [string hello]", + "", + " [string world]"); + + // 1 tab + MT("listTab", + "[string * foo]", + "", + "[string * bar]", + "", + "\t[string hello]"); + + // No indent + MT("listNoIndent", + "[string * foo]", + "", + "[string * bar]", + "", + "hello"); + + // Blockquote + MT("blockquote", + "[string * foo]", + "", + "[string * bar]", + "", + " [string"e > hello]"); + + // Code block + MT("blockquoteCode", + "[string * foo]", + "", + "[string * bar]", + "", + " [comment > hello]", + "", + " [string world]"); + + // Code block followed by text + MT("blockquoteCodeText", + "[string * foo]", + "", + " [string bar]", + "", + " [comment hello]", + "", + " [string world]"); + + // Nested list + + MT("listAsteriskNested", + "[string * foo]", + "", + " [string * bar]"); + + MT("listPlusNested", + "[string + foo]", + "", + " [string + bar]"); + + MT("listDashNested", + "[string - foo]", + "", + " [string - bar]"); + + MT("listNumberNested", + "[string 1. foo]", + "", + " [string 2. bar]"); + + MT("listMixed", + "[string * foo]", + "", + " [string + bar]", + "", + " [string - hello]", + "", + " [string 1. world]"); + + MT("listBlockquote", + "[string * foo]", + "", + " [string + bar]", + "", + " [quote&string > hello]"); + + MT("listCode", + "[string * foo]", + "", + " [string + bar]", + "", + " [comment hello]"); + + // Code with internal indentation + MT("listCodeIndentation", + "[string * foo]", + "", + " [comment bar]", + " [comment hello]", + " [comment world]", + " [comment foo]", + " [string bar]"); + + // Code followed by text + MT("listCodeText", + "[string * foo]", + "", + " [comment bar]", + "", + "hello"); + + // Following tests directly from official Markdown documentation + // http://daringfireball.net/projects/markdown/syntax#hr + + MT("hrSpace", + "[hr * * *]"); + + MT("hr", + "[hr ***]"); + + MT("hrLong", + "[hr *****]"); + + MT("hrSpaceDash", + "[hr - - -]"); + + MT("hrDashLong", + "[hr ---------------------------------------]"); + + // Inline link with title + MT("linkTitle", + "[link [[foo]]][string (http://example.com/ \"bar\")] hello"); + + // Inline link without title + MT("linkNoTitle", + "[link [[foo]]][string (http://example.com/)] bar"); + + // Inline link with image + MT("linkImage", + "[link [[][tag ![[foo]]][string (http://example.com/)][link ]]][string (http://example.com/)] bar"); + + // Inline link with Em + MT("linkEm", + "[link [[][link&em *foo*][link ]]][string (http://example.com/)] bar"); + + // Inline link with Strong + MT("linkStrong", + "[link [[][link&strong **foo**][link ]]][string (http://example.com/)] bar"); + + // Inline link with EmStrong + MT("linkEmStrong", + "[link [[][link&strong **][link&emstrong *foo**][link&em *][link ]]][string (http://example.com/)] bar"); + + // Image with title + MT("imageTitle", + "[tag ![[foo]]][string (http://example.com/ \"bar\")] hello"); + + // Image without title + MT("imageNoTitle", + "[tag ![[foo]]][string (http://example.com/)] bar"); + + // Image with asterisks + MT("imageAsterisks", + "[tag ![[*foo*]]][string (http://example.com/)] bar"); + + // Not a link. Should be normal text due to square brackets being used + // regularly in text, especially in quoted material, and no space is allowed + // between square brackets and parentheses (per Dingus). + MT("notALink", + "[[foo]] (bar)"); + + // Reference-style links + MT("linkReference", + "[link [[foo]]][string [[bar]]] hello"); + + // Reference-style links with Em + MT("linkReferenceEm", + "[link [[][link&em *foo*][link ]]][string [[bar]]] hello"); + + // Reference-style links with Strong + MT("linkReferenceStrong", + "[link [[][link&strong **foo**][link ]]][string [[bar]]] hello"); + + // Reference-style links with EmStrong + MT("linkReferenceEmStrong", + "[link [[][link&strong **][link&emstrong *foo**][link&em *][link ]]][string [[bar]]] hello"); + + // Reference-style links with optional space separator (per docuentation) + // "You can optionally use a space to separate the sets of brackets" + MT("linkReferenceSpace", + "[link [[foo]]] [string [[bar]]] hello"); + + // Should only allow a single space ("...use *a* space...") + MT("linkReferenceDoubleSpace", + "[[foo]] [[bar]] hello"); + + // Reference-style links with implicit link name + MT("linkImplicit", + "[link [[foo]]][string [[]]] hello"); + + // @todo It would be nice if, at some point, the document was actually + // checked to see if the referenced link exists + + // Link label, for reference-style links (taken from documentation) + + MT("labelNoTitle", + "[link [[foo]]:] [string http://example.com/]"); + + MT("labelIndented", + " [link [[foo]]:] [string http://example.com/]"); + + MT("labelSpaceTitle", + "[link [[foo bar]]:] [string http://example.com/ \"hello\"]"); + + MT("labelDoubleTitle", + "[link [[foo bar]]:] [string http://example.com/ \"hello\"] \"world\""); + + MT("labelTitleDoubleQuotes", + "[link [[foo]]:] [string http://example.com/ \"bar\"]"); + + MT("labelTitleSingleQuotes", + "[link [[foo]]:] [string http://example.com/ 'bar']"); + + MT("labelTitleParenthese", + "[link [[foo]]:] [string http://example.com/ (bar)]"); + + MT("labelTitleInvalid", + "[link [[foo]]:] [string http://example.com/] bar"); + + MT("labelLinkAngleBrackets", + "[link [[foo]]:] [string \"bar\"]"); + + MT("labelTitleNextDoubleQuotes", + "[link [[foo]]:] [string http://example.com/]", + "[string \"bar\"] hello"); + + MT("labelTitleNextSingleQuotes", + "[link [[foo]]:] [string http://example.com/]", + "[string 'bar'] hello"); + + MT("labelTitleNextParenthese", + "[link [[foo]]:] [string http://example.com/]", + "[string (bar)] hello"); + + MT("labelTitleNextMixed", + "[link [[foo]]:] [string http://example.com/]", + "(bar\" hello"); + + MT("linkWeb", + "[link ] foo"); + + MT("linkEmail", + "[link ] foo"); + + MT("emAsterisk", + "[em *foo*] bar"); + + MT("emUnderscore", + "[em _foo_] bar"); + + MT("emInWordAsterisk", + "foo[em *bar*]hello"); + + MT("emInWordUnderscore", + "foo[em _bar_]hello"); + + // Per documentation: "...surround an * or _ with spaces, it’ll be + // treated as a literal asterisk or underscore." + + MT("emEscapedBySpaceIn", + "foo [em _bar _ hello_] world"); + + MT("emEscapedBySpaceOut", + "foo _ bar[em _hello_]world"); + + // Unclosed emphasis characters + // Instead of simply marking as EM / STRONG, it would be nice to have an + // incomplete flag for EM and STRONG, that is styled slightly different. + MT("emIncompleteAsterisk", + "foo [em *bar]"); + + MT("emIncompleteUnderscore", + "foo [em _bar]"); + + MT("strongAsterisk", + "[strong **foo**] bar"); + + MT("strongUnderscore", + "[strong __foo__] bar"); + + MT("emStrongAsterisk", + "[em *foo][emstrong **bar*][strong hello**] world"); + + MT("emStrongUnderscore", + "[em _foo][emstrong __bar_][strong hello__] world"); + + // "...same character must be used to open and close an emphasis span."" + MT("emStrongMixed", + "[em _foo][emstrong **bar*hello__ world]"); + + MT("emStrongMixed", + "[em *foo][emstrong __bar_hello** world]"); + + // These characters should be escaped: + // \ backslash + // ` backtick + // * asterisk + // _ underscore + // {} curly braces + // [] square brackets + // () parentheses + // # hash mark + // + plus sign + // - minus sign (hyphen) + // . dot + // ! exclamation mark + + MT("escapeBacktick", + "foo \\`bar\\`"); + + MT("doubleEscapeBacktick", + "foo \\\\[comment `bar\\\\`]"); + + MT("escapeAsterisk", + "foo \\*bar\\*"); + + MT("doubleEscapeAsterisk", + "foo \\\\[em *bar\\\\*]"); + + MT("escapeUnderscore", + "foo \\_bar\\_"); + + MT("doubleEscapeUnderscore", + "foo \\\\[em _bar\\\\_]"); + + MT("escapeHash", + "\\# foo"); + + MT("doubleEscapeHash", + "\\\\# foo"); +})(); diff --git a/mode/stex/test.js b/mode/stex/test.js index c5a34f3d8d..e7e9c29c1e 100644 --- a/mode/stex/test.js +++ b/mode/stex/test.js @@ -1,343 +1,104 @@ -var MT = ModeTest; -MT.modeName = 'stex'; -MT.modeOptions = {}; - -MT.testMode( - 'word', - 'foo', - [ - null, 'foo' - ] -); - -MT.testMode( - 'twoWords', - 'foo bar', - [ - null, 'foo bar' - ] -); - -MT.testMode( - 'beginEndDocument', - '\\begin{document}\n\\end{document}', - [ - 'tag', '\\begin', - 'bracket', '{', - 'atom', 'document', - 'bracket', '}', - 'tag', '\\end', - 'bracket', '{', - 'atom', 'document', - 'bracket', '}' - ] -); - -MT.testMode( - 'beginEndEquation', - '\\begin{equation}\n E=mc^2\n\\end{equation}', - [ - 'tag', '\\begin', - 'bracket', '{', - 'atom', 'equation', - 'bracket', '}', - null, ' E=mc^2', - 'tag', '\\end', - 'bracket', '{', - 'atom', 'equation', - 'bracket', '}' - ] -); - -MT.testMode( - 'beginModule', - '\\begin{module}[]', - [ - 'tag', '\\begin', - 'bracket', '{', - 'atom', 'module', - 'bracket', '}[]' - ] -); - -MT.testMode( - 'beginModuleId', - '\\begin{module}[id=bbt-size]', - [ - 'tag', '\\begin', - 'bracket', '{', - 'atom', 'module', - 'bracket', '}[', - null, 'id=bbt-size', - 'bracket', ']' - ] -); - -MT.testMode( - 'importModule', - '\\importmodule[b-b-t]{b-b-t}', - [ - 'tag', '\\importmodule', - 'bracket', '[', - 'string', 'b-b-t', - 'bracket', ']{', - 'builtin', 'b-b-t', - 'bracket', '}' - ] -); - -MT.testMode( - 'importModulePath', - '\\importmodule[\\KWARCslides{dmath/en/cardinality}]{card}', - [ - 'tag', '\\importmodule', - 'bracket', '[', - 'tag', '\\KWARCslides', - 'bracket', '{', - 'string', 'dmath/en/cardinality', - 'bracket', '}]{', - 'builtin', 'card', - 'bracket', '}' - ] -); - -MT.testMode( - 'psForPDF', - '\\PSforPDF[1]{#1}', // could treat #1 specially - [ - 'tag', '\\PSforPDF', - 'bracket', '[', - 'atom', '1', - 'bracket', ']{', - null, '#1', - 'bracket', '}' - ] -); - -MT.testMode( - 'comment', - '% foo', - [ - 'comment', '% foo' - ] -); - -MT.testMode( - 'tagComment', - '\\item% bar', - [ - 'tag', '\\item', - 'comment', '% bar' - ] -); - -MT.testMode( - 'commentTag', - ' % \\item', - [ - null, ' ', - 'comment', '% \\item' - ] -); - -MT.testMode( - 'commentLineBreak', - '%\nfoo', - [ - 'comment', '%', - null, 'foo' - ] -); - -MT.testMode( - 'tagErrorCurly', - '\\begin}{', - [ - 'tag', '\\begin', - 'error', '}', - 'bracket', '{' - ] -); - -MT.testMode( - 'tagErrorSquare', - '\\item]{', - [ - 'tag', '\\item', - 'error', ']', - 'bracket', '{' - ] -); - -MT.testMode( - 'commentCurly', - '% }', - [ - 'comment', '% }' - ] -); - -MT.testMode( - 'tagHash', - 'the \\# key', - [ - null, 'the ', - 'tag', '\\#', - null, ' key' - ] -); - -MT.testMode( - 'tagNumber', - 'a \\$5 stetson', - [ - null, 'a ', - 'tag', '\\$', - 'atom', 5, - null, ' stetson' - ] -); - -MT.testMode( - 'tagPercent', - '100\\% beef', - [ - 'atom', '100', - 'tag', '\\%', - null, ' beef' - ] -); - -MT.testMode( - 'tagAmpersand', - 'L \\& N', - [ - null, 'L ', - 'tag', '\\&', - null, ' N' - ] -); - -MT.testMode( - 'tagUnderscore', - 'foo\\_bar', - [ - null, 'foo', - 'tag', '\\_', - null, 'bar' - ] -); - -MT.testMode( - 'tagBracketOpen', - '\\emph{\\{}', - [ - 'tag', '\\emph', - 'bracket', '{', - 'tag', '\\{', - 'bracket', '}' - ] -); - -MT.testMode( - 'tagBracketClose', - '\\emph{\\}}', - [ - 'tag', '\\emph', - 'bracket', '{', - 'tag', '\\}', - 'bracket', '}' - ] -); - -MT.testMode( - 'tagLetterNumber', - 'section \\S1', - [ - null, 'section ', - 'tag', '\\S', - 'atom', '1' - ] -); - -MT.testMode( - 'textTagNumber', - 'para \\P2', - [ - null, 'para ', - 'tag', '\\P', - 'atom', '2' - ] -); - -MT.testMode( - 'thinspace', - 'x\\,y', // thinspace - [ - null, 'x', - 'tag', '\\,', - null, 'y' - ] -); - -MT.testMode( - 'thickspace', - 'x\\;y', // thickspace - [ - null, 'x', - 'tag', '\\;', - null, 'y' - ] -); - -MT.testMode( - 'negativeThinspace', - 'x\\!y', // negative thinspace - [ - null, 'x', - 'tag', '\\!', - null, 'y' - ] -); - -MT.testMode( - 'periodNotSentence', - 'J.\\ L.\\ is', // period not ending a sentence - [ - null, 'J.\\ L.\\ is' - ] -); // maybe could be better - -MT.testMode( - 'periodSentence', - 'X\\@. The', // period ending a sentence - [ - null, 'X', - 'tag', '\\@', - null, '. The' - ] -); - -MT.testMode( - 'italicCorrection', - '{\\em If\\/} I', // italic correction - [ - 'bracket', '{', - 'tag', '\\em', - null, ' If', - 'tag', '\\/', - 'bracket', '}', - null, ' I' - ] -); - -MT.testMode( - 'tagBracket', - '\\newcommand{\\pop}', - [ - 'tag', '\\newcommand', - 'bracket', '{', - 'tag', '\\pop', - 'bracket', '}' - ] -); \ No newline at end of file +(function() { + var mode = CodeMirror.getMode({tabSize: 4}, "stex"); + function MT(name) { test.mode(name, mode, Array.prototype.slice.call(arguments, 1)); } + + MT("word", + "foo"); + + MT("twoWords", + "foo bar"); + + MT("beginEndDocument", + "[tag \\begin][bracket {][atom document][bracket }]", + "[tag \\end][bracket {][atom document][bracket }]"); + + MT("beginEndEquation", + "[tag \\begin][bracket {][atom equation][bracket }]", + " E=mc^2", + "[tag \\end][bracket {][atom equation][bracket }]"); + + MT("beginModule", + "[tag \\begin][bracket {][atom module][bracket }[[]]]"); + + MT("beginModuleId", + "[tag \\begin][bracket {][atom module][bracket }[[]id=bbt-size[bracket ]]]"); + + MT("importModule", + "[tag \\importmodule][bracket [[][string b-b-t][bracket ]]{][builtin b-b-t][bracket }]"); + + MT("importModulePath", + "[tag \\importmodule][bracket [[][tag \\KWARCslides][bracket {][string dmath/en/cardinality][bracket }]]{][builtin card][bracket }]"); + + MT("psForPDF", + "[tag \\PSforPDF][bracket [[][atom 1][bracket ]]{]#1[bracket }]"); + + MT("comment", + "[comment % foo]"); + + MT("tagComment", + "[tag \\item][comment % bar]"); + + MT("commentTag", + " [comment % \\item]"); + + MT("commentLineBreak", + "[comment %]", + "foo"); + + MT("tagErrorCurly", + "[tag \\begin][error }][bracket {]"); + + MT("tagErrorSquare", + "[tag \\item][error ]]][bracket {]"); + + MT("commentCurly", + "[comment % }]"); + + MT("tagHash", + "the [tag \\#] key"); + + MT("tagNumber", + "a [tag \\$][atom 5] stetson"); + + MT("tagPercent", + "[atom 100][tag \\%] beef"); + + MT("tagAmpersand", + "L [tag \\&] N"); + + MT("tagUnderscore", + "foo[tag \\_]bar"); + + MT("tagBracketOpen", + "[tag \\emph][bracket {][tag \\{][bracket }]"); + + MT("tagBracketClose", + "[tag \\emph][bracket {][tag \\}][bracket }]"); + + MT("tagLetterNumber", + "section [tag \\S][atom 1]"); + + MT("textTagNumber", + "para [tag \\P][atom 2]"); + + MT("thinspace", + "x[tag \\,]y"); + + MT("thickspace", + "x[tag \\;]y"); + + MT("negativeThinspace", + "x[tag \\!]y"); + + MT("periodNotSentence", + "J.\\ L.\\ is"); + + MT("periodSentence", + "X[tag \\@]. The"); + + MT("italicCorrection", + "[bracket {][tag \\em] If[tag \\/][bracket }] I"); + + MT("tagBracket", + "[tag \\newcommand][bracket {][tag \\pop][bracket }]"); +})(); diff --git a/mode/xquery/test.js b/mode/xquery/test.js index 23ab3d1e3e..41719dd169 100644 --- a/mode/xquery/test.js +++ b/mode/xquery/test.js @@ -1,77 +1,64 @@ -// Initiate ModeTest and set defaults -var MT = ModeTest; -MT.modeName = "xquery"; -MT.modeOptions = {}; +// Don't take these too seriously -- the expected results appear to be +// based on the results of actual runs without any serious manual +// verification. If a change you made causes them to fail, the test is +// as likely to wrong as the code. -MT.testMode("eviltest", - 'xquery version "1.0-ml";\ - (: this is\ - : a \ - "comment" :)\ - let $let := <x attr="value">"test"<func>function() $var {function()} {$var}</func></x>\ - let $joe:=1\ - return element element {\ - attribute attribute { 1 },\ - element test { 'a' }, \ - attribute foo { "bar" },\ - fn:doc()[ foo/@bar eq $let ],\ - //x } \ - \ - (: a more \'evil\' test :)\ - (: Modified Blakeley example (: with nested comment :) ... :)\ - declare private function local:declare() {()};\ - declare private function local:private() {()};\ - declare private function local:function() {()};\ - declare private function local:local() {()};\ - let $let := <let>let $let := "let"</let>\ - return element element {\ - attribute attribute { try { xdmp:version() } catch($e) { xdmp:log($e) } },\ - attribute fn:doc { "bar" castable as xs:string },\ - element text { text { "text" } },\ - fn:doc()[ child::eq/(@bar | attribute::attribute) eq $let ],\ - //fn:doc\ - }', ["keyword","xquery",null," ","keyword","version",null," ","variable",""1","keyword",".","atom","0","keyword","-","variable","ml"","def variable",";",null," ","comment","(: this is : a \"comment\" :)",null," ","keyword","let",null," ","variable","$let",null," ","keyword",":=",null," ","variable","<x",null," ","variable","attr","keyword","=","variable",""value">"test"<func>","def variable",";function","","()",null," ","variable","$var",null," ","","{","keyword","function","","()}",null," ","","{","variable","$var","","}","variable","<","keyword","/","variable","func><","keyword","/","variable","x>",null," ","keyword","let",null," ","variable","$joe","keyword",":=","atom","1",null," ","keyword","return",null," ","keyword","element",null," ","variable","element",null," ","","{",null," ","keyword","attribute",null," ","variable","attribute",null," ","","{",null," ","atom","1",null," ","","},",null," ","keyword","element",null," ","variable","test",null," ","","{",null," ","variable","'a'",null," ","","},",null," ","keyword","attribute",null," ","variable","foo",null," ","","{",null," ","variable",""bar"",null," ","","},",null," ","def variable","fn:doc","","()[",null," ","variable","foo","keyword","/","variable","@bar",null," ","keyword","eq",null," ","variable","$let",null," ","","],",null," ","keyword","//","variable","x",null," ","","}",null," ","comment","(: a more 'evil' test :)",null," ","comment","(: Modified Blakeley example (: with nested comment :) ... :)",null," ","keyword","declare",null," ","keyword","private",null," ","keyword","function",null," ","def variable","local:declare","","()",null," ","","{()}","variable",";",null," ","keyword","declare",null," ","keyword","private",null," ","keyword","function",null," ","def variable","local:private","","()",null," ","","{()}","variable",";",null," ","keyword","declare",null," ","keyword","private",null," ","keyword","function",null," ","def variable","local:function","","()",null," ","","{()}","variable",";",null," ","keyword","declare",null," ","keyword","private",null," ","keyword","function",null," ","def variable","local:local","","()",null," ","","{()}","variable",";",null," ","keyword","let",null," ","variable","$let",null," ","keyword",":=",null," ","variable","<let>let",null," ","variable","$let",null," ","keyword",":=",null," ","variable",""let"<","keyword","/let","variable",">",null," ","keyword","return",null," ","keyword","element",null," ","variable","element",null," ","","{",null," ","keyword","attribute",null," ","variable","attribute",null," ","","{",null," ","keyword","try",null," ","","{",null," ","def variable","xdmp:version","","()",null," ","","}",null," ","keyword","catch","","(","variable","$e","",")",null," ","","{",null," ","def variable","xdmp:log","","(","variable","$e","",")",null," ","","}",null," ","","},",null," ","keyword","attribute",null," ","variable","fn:doc",null," ","","{",null," ","variable",""bar"",null," ","variable","castable",null," ","keyword","as",null," ","atom","xs:string",null," ","","},",null," ","keyword","element",null," ","variable","text",null," ","","{",null," ","keyword","text",null," ","","{",null," ","variable",""text"",null," ","","}",null," ","","},",null," ","def variable","fn:doc","","()[",null," ","qualifier","child::","variable","eq","keyword","/","","(","variable","@bar",null," ","keyword","|",null," ","qualifier","attribute::","variable","attribute","",")",null," ","keyword","eq",null," ","variable","$let",null," ","","],",null," ","keyword","//","variable","fn:doc",null," ","","}"]); +(function() { + var mode = CodeMirror.getMode({tabSize: 4}, "xquery"); + function MT(name) { test.mode(name, mode, Array.prototype.slice.call(arguments, 1)); } -MT.testMode("testEmptySequenceKeyword", - '"foo" instance of empty-sequence()', - ["string","\"foo\"",null," ","keyword","instance",null," ","keyword","of",null," ","keyword","empty-sequence","","()"]); + MT("eviltest", + "[keyword xquery] [keyword version] [variable "1][keyword .][atom 0][keyword -][variable ml"][def&variable ;] [comment (: this is : a \"comment\" :)]", + " [keyword let] [variable $let] [keyword :=] [variable <x] [variable attr][keyword =][variable "value">"test"<func>][def&variable ;function]() [variable $var] {[keyword function]()} {[variable $var]}[variable <][keyword /][variable func><][keyword /][variable x>]", + " [keyword let] [variable $joe][keyword :=][atom 1]", + " [keyword return] [keyword element] [variable element] {", + " [keyword attribute] [variable attribute] { [atom 1] },", + " [keyword element] [variable test] { [variable 'a'] }, [keyword attribute] [variable foo] { [variable "bar"] },", + " [def&variable fn:doc]()[[ [variable foo][keyword /][variable @bar] [keyword eq] [variable $let] ]],", + " [keyword //][variable x] } [comment (: a more 'evil' test :)]", + " [comment (: Modified Blakeley example (: with nested comment :) ... :)]", + " [keyword declare] [keyword private] [keyword function] [def&variable local:declare]() {()}[variable ;]", + " [keyword declare] [keyword private] [keyword function] [def&variable local:private]() {()}[variable ;]", + " [keyword declare] [keyword private] [keyword function] [def&variable local:function]() {()}[variable ;]", + " [keyword declare] [keyword private] [keyword function] [def&variable local:local]() {()}[variable ;]", + " [keyword let] [variable $let] [keyword :=] [variable <let>let] [variable $let] [keyword :=] [variable "let"<][keyword /let][variable >]", + " [keyword return] [keyword element] [variable element] {", + " [keyword attribute] [variable attribute] { [keyword try] { [def&variable xdmp:version]() } [keyword catch]([variable $e]) { [def&variable xdmp:log]([variable $e]) } },", + " [keyword attribute] [variable fn:doc] { [variable "bar"] [variable castable] [keyword as] [atom xs:string] },", + " [keyword element] [variable text] { [keyword text] { [variable "text"] } },", + " [def&variable fn:doc]()[[ [qualifier child::][variable eq][keyword /]([variable @bar] [keyword |] [qualifier attribute::][variable attribute]) [keyword eq] [variable $let] ]],", + " [keyword //][variable fn:doc]", + " }"); + MT("testEmptySequenceKeyword", + "[string \"foo\"] [keyword instance] [keyword of] [keyword empty-sequence]()"); -MT.testMode("testMultiAttr", - '

    hello world

    ', - ["tag","

    ","variable","hello",null," ","variable","world","tag","

    "]); + MT("testMultiAttr", + "[tag

    ][variable hello] [variable world][tag

    ]"); -MT.testMode("test namespaced variable", - 'declare namespace e = "http://example.com/ANamespace";\ -declare variable $e:exampleComThisVarIsNotRecognized as element(*) external;', - ["keyword","declare",null," ","keyword","namespace",null," ","variable","e",null," ","keyword","=",null," ","string","\"http://example.com/ANamespace\"","variable",";declare",null," ","keyword","variable",null," ","variable","$e:exampleComThisVarIsNotRecognized",null," ","keyword","as",null," ","keyword","element","","(","keyword","*","",")",null," ","variable","external;"]); + MT("test namespaced variable", + "[keyword declare] [keyword namespace] [variable e] [keyword =] [string \"http://example.com/ANamespace\"][variable ;declare] [keyword variable] [variable $e:exampleComThisVarIsNotRecognized] [keyword as] [keyword element]([keyword *]) [variable external;]"); -MT.testMode("test EQName variable", - 'declare variable $"http://www.example.com/ns/my":var := 12;\ -{$"http://www.example.com/ns/my":var}', - ["keyword","declare",null," ","keyword","variable",null," ","variable","$\"http://www.example.com/ns/my\":var",null," ","keyword",":=",null," ","atom","12","variable",";","tag","","","{","variable","$\"http://www.example.com/ns/my\":var","","}","tag",""]); + MT("test EQName variable", + "[keyword declare] [keyword variable] [variable $\"http://www.example.com/ns/my\":var] [keyword :=] [atom 12][variable ;]", + "[tag ]{[variable $\"http://www.example.com/ns/my\":var]}[tag ]"); -MT.testMode("test EQName function", - 'declare function "http://www.example.com/ns/my":fn ($a as xs:integer) as xs:integer {\ - $a + 2\ -};\ -{"http://www.example.com/ns/my":fn(12)}', - ["keyword","declare",null," ","keyword","function",null," ","def variable","\"http://www.example.com/ns/my\":fn",null," ","","(","variable","$a",null," ","keyword","as",null," ","atom","xs:integer","",")",null," ","keyword","as",null," ","atom","xs:integer",null," ","","{",null," ","variable","$a",null," ","keyword","+",null," ","atom","2","","}","variable",";","tag","","","{","def variable","\"http://www.example.com/ns/my\":fn","","(","atom","12","",")}","tag",""]); + MT("test EQName function", + "[keyword declare] [keyword function] [def&variable \"http://www.example.com/ns/my\":fn] ([variable $a] [keyword as] [atom xs:integer]) [keyword as] [atom xs:integer] {", + " [variable $a] [keyword +] [atom 2]", + "}[variable ;]", + "[tag ]{[def&variable \"http://www.example.com/ns/my\":fn]([atom 12])}[tag ]"); -MT.testMode("test EQName function with single quotes", - 'declare function \'http://www.example.com/ns/my\':fn ($a as xs:integer) as xs:integer {\ - $a + 2\ -};\ -{\'http://www.example.com/ns/my\':fn(12)}', - ["keyword","declare",null," ","keyword","function",null," ","def variable","'http://www.example.com/ns/my':fn",null," ","","(","variable","$a",null," ","keyword","as",null," ","atom","xs:integer","",")",null," ","keyword","as",null," ","atom","xs:integer",null," ","","{",null," ","variable","$a",null," ","keyword","+",null," ","atom","2","","}","variable",";","tag","","","{","def variable","'http://www.example.com/ns/my':fn","","(","atom","12","",")}","tag",""]); + MT("test EQName function with single quotes", + "[keyword declare] [keyword function] [def&variable 'http://www.example.com/ns/my':fn] ([variable $a] [keyword as] [atom xs:integer]) [keyword as] [atom xs:integer] {", + " [variable $a] [keyword +] [atom 2]", + "}[variable ;]", + "[tag ]{[def&variable 'http://www.example.com/ns/my':fn]([atom 12])}[tag ]"); -MT.testMode("testProcessingInstructions", - 'data() instance of xs:string', - ["def variable","data","","(","comment meta","","",")",null," ","keyword","instance",null," ","keyword","of",null," ","atom","xs:string"]); + MT("testProcessingInstructions", + "[def&variable data]([comment&meta ]) [keyword instance] [keyword of] [atom xs:string]"); -MT.testMode("testQuoteEscapeDouble", - 'let $rootfolder := "c:\\builds\\winnt\\HEAD\\qa\\scripts\\"\ -let $keysfolder := concat($rootfolder, "keys\\")\ -return\ -$keysfolder', - ["keyword","let",null," ","variable","$rootfolder",null," ","keyword",":=",null," ","string","\"c:\\builds\\winnt\\HEAD\\qa\\scripts\\\"","keyword","let",null," ","variable","$keysfolder",null," ","keyword",":=",null," ","def variable","concat","","(","variable","$rootfolder","",",",null," ","string","\"keys\\\"","",")","variable","return$keysfolder"]); + MT("testQuoteEscapeDouble", + "[keyword let] [variable $rootfolder] [keyword :=] [string \"c:\\builds\\winnt\\HEAD\\qa\\scripts\\\"]", + "[keyword let] [variable $keysfolder] [keyword :=] [def&variable concat]([variable $rootfolder], [string \"keys\\\"])"); +})(); diff --git a/mode/xquery/xquery.js b/mode/xquery/xquery.js index e4231d1069..d5c859e00a 100644 --- a/mode/xquery/xquery.js +++ b/mode/xquery/xquery.js @@ -33,7 +33,7 @@ CodeMirror.defineMode("xquery", function() { , C = kw("keyword c") , operator = kw("operator") , atom = {type: "atom", style: "atom"} - , punctuation = {type: "punctuation", style: ""} + , punctuation = {type: "punctuation", style: null} , qualifier = {type: "axis_specifier", style: "qualifier"}; // kwObj is what is return from this function at the end @@ -121,12 +121,12 @@ CodeMirror.defineMode("xquery", function() { // start code block else if(ch == "{") { pushStateStack(state,{ type: "codeblock"}); - return ret("", ""); + return ret("", null); } // end code block else if(ch == "}") { popStateStack(state); - return ret("", ""); + return ret("", null); } // if we're in an XML block else if(isInXmlBlock(state)) { @@ -163,22 +163,22 @@ CodeMirror.defineMode("xquery", function() { // open paren else if(ch === "(") { pushStateStack(state, { type: "paren"}); - return ret("", ""); + return ret("", null); } // close paren else if(ch === ")") { popStateStack(state); - return ret("", ""); + return ret("", null); } // open paren else if(ch === "[") { pushStateStack(state, { type: "bracket"}); - return ret("", ""); + return ret("", null); } // close paren else if(ch === "]") { popStateStack(state); - return ret("", ""); + return ret("", null); } else { var known = keywords.propertyIsEnumerable(ch) && keywords[ch]; @@ -342,7 +342,7 @@ CodeMirror.defineMode("xquery", function() { return ret("tag", "tag"); } if(ch == "=") - return ret("", ""); + return ret("", null); // quoted string if (ch == '"' || ch == "'") return chain(stream, state, tokenString(ch, tokenAttribute)); diff --git a/test/mode_test.js b/test/mode_test.js index 06270aa31e..51e51b9525 100644 --- a/test/mode_test.js +++ b/test/mode_test.js @@ -2,166 +2,191 @@ * Helper to test CodeMirror highlighting modes. It pretty prints output of the * highlighter and can check against expected styles. * - * See test.html in the stex mode for examples. - */ -ModeTest = {}; - -ModeTest.modeOptions = {}; -ModeTest.modeName = CodeMirror.defaults.mode; - -/** - * Run a test. - * - * @param name Name of test - * @param text String to highlight. - * @param expected Expected styles and tokens: Array(style, token, [style, token,...]) - * @param modeName - * @param modeOptions - * @param expectedFail + * Mode tests are registered by calling test.mode(testName, mode, + * tokens), where mode is a mode object as returned by + * CodeMirror.getMode, and tokens is an array of lines that make up + * the test. + * + * These lines are strings, in which styled stretches of code are + * enclosed in brackets `[]`, and prefixed by their style. For + * example, `[keyword if]`. Brackets in the code itself must be + * duplicated to prevent them from being interpreted as token + * boundaries. For example `a[[i]]` for `a[i]`. If a token has + * multiple styles, the styles must be separated by ampersands, for + * example `[tag&error ]`. + * + * See the test.js files in the css, markdown, gfm, and stex mode + * directories for examples. */ -ModeTest.testMode = function(name, text, expected, modeName, modeOptions, expectedFail) { - if (!modeName) modeName = ModeTest.modeName; - - if (!modeOptions) modeOptions = ModeTest.modeOptions; - - var mode = CodeMirror.getMode(modeOptions, modeName); - - if (expected.length < 0) { - throw "must have text for test (" + name + ")"; +(function() { + function findSingle(str, pos, ch) { + for (;;) { + var found = str.indexOf(ch, pos); + if (found == -1) return null; + if (str.charAt(found + 1) != ch) return found; + pos = found + 2; + } } - if (expected.length % 2 != 0) { - throw "must have text for test (" + name + ") plus expected (style, token) pairs"; + + var styleName = /[\w&-_]+/g; + function parseTokens(strs) { + var tokens = [], plain = ""; + for (var i = 0; i < strs.length; ++i) { + if (i) plain += "\n"; + var str = strs[i], pos = 0; + while (pos < str.length) { + var style = null, text; + if (str.charAt(pos) == "[" && str.charAt(pos+1) != "[") { + styleName.lastIndex = pos + 1; + var m = styleName.exec(str); + style = m[0].replace(/&/g, " "); + var textStart = pos + style.length + 2; + var end = findSingle(str, textStart, "]"); + if (end == null) throw new Error("Unterminated token at " + pos + " in '" + str + "'" + style); + text = str.slice(textStart, end); + pos = end + 1; + } else { + var end = findSingle(str, pos, "["); + if (end == null) end = str.length; + text = str.slice(pos, end); + pos = end; + } + text = text.replace(/\[\[|\]\]/g, function(s) {return s.charAt(0);}); + tokens.push(style, text); + plain += text; + } + } + return {tokens: tokens, plain: plain}; } - return test( - modeName + "_" + name, - function(){ - return ModeTest.compare(text, expected, mode); - }, - expectedFail - ); - -} -ModeTest.compare = function (text, expected, mode) { + test.mode = function(name, mode, tokens) { + var data = parseTokens(tokens); + return test(mode.name + "_" + name, function() { + return compare(data.plain, data.tokens, mode); + }); + }; - var expectedOutput = []; - for (var i = 0; i < expected.length; i += 2) { - var sty = expected[i]; - if (sty && sty.indexOf(" ")) sty = sty.split(' ').sort().join(' '); - expectedOutput.push(sty, expected[i + 1]); - } + function compare(text, expected, mode) { - var observedOutput = ModeTest.highlight(text, mode); + var expectedOutput = []; + for (var i = 0; i < expected.length; i += 2) { + var sty = expected[i]; + if (sty && sty.indexOf(" ")) sty = sty.split(' ').sort().join(' '); + expectedOutput.push(sty, expected[i + 1]); + } - var pass, passStyle = ""; - pass = ModeTest.highlightOutputsEqual(expectedOutput, observedOutput); - passStyle = pass ? 'mt-pass' : 'mt-fail'; + var observedOutput = highlight(text, mode); - var s = ''; - if (pass) { - s += '
    '; - s += '
    ' + text + '
    '; - s += '
    '; - s += ModeTest.prettyPrintOutputTable(observedOutput); - s += '
    '; - s += '
    '; - return s; - } else { - s += '
    '; - s += '
    ' + text + '
    '; - s += '
    '; - s += 'expected:'; - s += ModeTest.prettyPrintOutputTable(expectedOutput); - s += 'observed:'; - s += ModeTest.prettyPrintOutputTable(observedOutput); - s += '
    '; - s += '
    '; - throw s; + var pass, passStyle = ""; + pass = highlightOutputsEqual(expectedOutput, observedOutput); + passStyle = pass ? 'mt-pass' : 'mt-fail'; + + var s = ''; + if (pass) { + s += '
    '; + s += '
    ' + text + '
    '; + s += '
    '; + s += prettyPrintOutputTable(observedOutput); + s += '
    '; + s += '
    '; + return s; + } else { + s += '
    '; + s += '
    ' + text + '
    '; + s += '
    '; + s += 'expected:'; + s += prettyPrintOutputTable(expectedOutput); + s += 'observed:'; + s += prettyPrintOutputTable(observedOutput); + s += '
    '; + s += '
    '; + throw s; + } } -} -/** - * Emulation of CodeMirror's internal highlight routine for testing. Multi-line - * input is supported. - * - * @param string to highlight - * - * @param mode the mode that will do the actual highlighting - * - * @return array of [style, token] pairs - */ -ModeTest.highlight = function(string, mode) { - var state = mode.startState() + /** + * Emulation of CodeMirror's internal highlight routine for testing. Multi-line + * input is supported. + * + * @param string to highlight + * + * @param mode the mode that will do the actual highlighting + * + * @return array of [style, token] pairs + */ + function highlight(string, mode) { + var state = mode.startState() - var lines = string.replace(/\r\n/g,'\n').split('\n'); - var st = [], pos = 0; - for (var i = 0; i < lines.length; ++i) { - var line = lines[i], newLine = true; - var stream = new CodeMirror.StringStream(line); - if (line == "" && mode.blankLine) mode.blankLine(state); - /* Start copied code from CodeMirror.highlight */ - while (!stream.eol()) { - var style = mode.token(stream, state), substr = stream.current(); - if (style && style.indexOf(" ") > -1) style = style.split(' ').sort().join(' '); + var lines = string.replace(/\r\n/g,'\n').split('\n'); + var st = [], pos = 0; + for (var i = 0; i < lines.length; ++i) { + var line = lines[i], newLine = true; + var stream = new CodeMirror.StringStream(line); + if (line == "" && mode.blankLine) mode.blankLine(state); + /* Start copied code from CodeMirror.highlight */ + while (!stream.eol()) { + var style = mode.token(stream, state), substr = stream.current(); + if (style && style.indexOf(" ") > -1) style = style.split(' ').sort().join(' '); - stream.start = stream.pos; - if (pos && st[pos-2] == style && !newLine) { - st[pos-1] += substr; - } else if (substr) { - st[pos++] = style; st[pos++] = substr; - } - // Give up when line is ridiculously long - if (stream.pos > 5000) { - st[pos++] = null; st[pos++] = this.text.slice(stream.pos); - break; + stream.start = stream.pos; + if (pos && st[pos-2] == style && !newLine) { + st[pos-1] += substr; + } else if (substr) { + st[pos++] = style; st[pos++] = substr; + } + // Give up when line is ridiculously long + if (stream.pos > 5000) { + st[pos++] = null; st[pos++] = this.text.slice(stream.pos); + break; + } + newLine = false; } - newLine = false; } - } - return st; -} + return st; + } -/** - * Compare two arrays of output from ModeTest.highlight. - * - * @param o1 array of [style, token] pairs - * - * @param o2 array of [style, token] pairs - * - * @return boolean; true iff outputs equal - */ -ModeTest.highlightOutputsEqual = function(o1, o2) { - if (o1.length != o2.length) return false; - for (var i = 0; i < o1.length; ++i) - if (o1[i] != o2[i]) return false; - return true; -} + /** + * Compare two arrays of output from highlight. + * + * @param o1 array of [style, token] pairs + * + * @param o2 array of [style, token] pairs + * + * @return boolean; true iff outputs equal + */ + function highlightOutputsEqual(o1, o2) { + if (o1.length != o2.length) return false; + for (var i = 0; i < o1.length; ++i) + if (o1[i] != o2[i]) return false; + return true; + } -/** - * Print tokens and corresponding styles in a table. Spaces in the token are - * replaced with 'interpunct' dots (·). - * - * @param output array of [style, token] pairs - * - * @return html string - */ -ModeTest.prettyPrintOutputTable = function(output) { - var s = ''; - s += ''; - for (var i = 0; i < output.length; i += 2) { - var style = output[i], val = output[i+1]; - s += + /** + * Print tokens and corresponding styles in a table. Spaces in the token are + * replaced with 'interpunct' dots (·). + * + * @param output array of [style, token] pairs + * + * @return html string + */ + function prettyPrintOutputTable(output) { + var s = '
    '; + s += ''; + for (var i = 0; i < output.length; i += 2) { + var style = output[i], val = output[i+1]; + s += ''; - } - s += ''; - for (var i = 0; i < output.length; i += 2) { - s += ''; + ''; + } + s += ''; + for (var i = 0; i < output.length; i += 2) { + s += ''; + } + s += '
    ' + '' + - val.replace(/ /g,'\xb7') + + val.replace(/ /g,'\xb7') + '' + - '
    ' + output[i] + '
    ' + output[i] + '
    '; + return s; } - s += ''; - return s; -} +})(); From 03f8aaed47b947fe0edfe73c29889d49483fd88f Mon Sep 17 00:00:00 2001 From: Marijn Haverbeke Date: Sat, 12 Jan 2013 14:22:16 +0100 Subject: [PATCH 0573/5780] Make the tester accurately report total test count Even when a subset is selected. --- test/driver.js | 24 ++++++++++++++---------- test/index.html | 4 +--- 2 files changed, 15 insertions(+), 13 deletions(-) diff --git a/test/driver.js b/test/driver.js index 6306991328..6d480e9460 100644 --- a/test/driver.js +++ b/test/driver.js @@ -48,10 +48,6 @@ function runTests(callback) { } if (debug.length < 1) { debug = null; - } else { - if (totalTests > debug.length) { - totalTests = debug.length; - } } } var totalTime = 0; @@ -67,7 +63,7 @@ function runTests(callback) { // Remove from array for reporting incorrect tests later debug.splice(debugIndex, 1); } else { - var wildcardName = test.name.split("_").shift() + "_*"; + var wildcardName = test.name.split("_")[0] + "_*"; debugIndex = indexOf(debug, wildcardName); if (debugIndex !== -1) { // Remove from array for reporting incorrect tests later @@ -75,11 +71,7 @@ function runTests(callback) { debugUsed.push(wildcardName); } else { debugIndex = indexOf(debugUsed, wildcardName); - if (debugIndex !== -1) { - totalTests++; - } else { - return step(i + 1); - } + if (debugIndex == -1) return step(i + 1); } } } @@ -132,3 +124,15 @@ function eqPos(a, b, msg) { function is(a, msg) { if (!a) throw new Failure(label("assertion failed", msg)); } + +function countTests() { + if (!debug) return tests.length; + var sum = 0; + for (var i = 0; i < tests.length; ++i) { + var name = tests[i].name; + if (indexOf(debug, name) != -1 || + indexOf(debug, name.split("_")[0] + "_*") != -1) + ++sum; + } + return sum; +} \ No newline at end of file diff --git a/test/index.html b/test/index.html index 4bb4a77be5..d4cec017f6 100644 --- a/test/index.html +++ b/test/index.html @@ -107,7 +107,7 @@

    CodeMirror: Test Suite

    bad = ""; verbose = false; debugUsed = Array(); - totalTests = tests.length; + totalTests = countTests(); progressTotal.nodeValue = " of " + totalTests; progressRan.nodeValue = count; output.innerHTML = ''; @@ -141,8 +141,6 @@

    CodeMirror: Test Suite

    var message = "???"; if (type != "done") ++count; progress.style.width = (count * (progress.parentNode.clientWidth - 2) / totalTests) + "px"; - progressTotal.nodeValue = " of " + totalTests + - (debugUsed.length && type != "done" ? "+" : ""); progressRan.nodeValue = count; if (type == "ok") { message = "Test '" + name + "' succeeded"; From 8416706c40c6c7dda5356f3e7f88365d6b13ea5a Mon Sep 17 00:00:00 2001 From: Marijn Haverbeke Date: Mon, 14 Jan 2013 08:27:28 +0100 Subject: [PATCH 0574/5780] Add MIHTool to real-world uses --- doc/realworld.html | 1 + 1 file changed, 1 insertion(+) diff --git a/doc/realworld.html b/doc/realworld.html index 71d9929c63..870c28d312 100644 --- a/doc/realworld.html +++ b/doc/realworld.html @@ -57,6 +57,7 @@

    { } CodeMi
  • kl1p (paste service)
  • Light Table (experimental IDE)
  • Mergely (interactive diffing)
  • +
  • MIHTool (iOS web-app debugging tool)
  • My2ndGeneration (social coding)
  • NoTex (rST authoring)
  • ORG (z80 assembly IDE)
  • From 2165c76e6e8620298b3695e112bdecb61437e2a0 Mon Sep 17 00:00:00 2001 From: Marijn Haverbeke Date: Mon, 14 Jan 2013 09:05:50 +0100 Subject: [PATCH 0575/5780] Reinstate the fixedGutter option --- doc/manual.html | 5 +++++ lib/codemirror.js | 16 +++++++++++----- 2 files changed, 16 insertions(+), 5 deletions(-) diff --git a/doc/manual.html b/doc/manual.html index 14d3a3249f..5bf582ca0d 100644 --- a/doc/manual.html +++ b/doc/manual.html @@ -222,6 +222,11 @@

    Configuration

    names are the keys passed to setGutterMarker. +
    fixedGutter (boolean)
    +
    Determines whether the gutter scrolls along with the content + horizontally (false) or whether it stays fixed during horizontal + scrolling (true, the default).
    +
    readOnly (boolean)
    This disables editing of the editor content by the user. If the special value "nocursor" is given (instead of diff --git a/lib/codemirror.js b/lib/codemirror.js index 4ce1cd0b8f..a771a1e643 100644 --- a/lib/codemirror.js +++ b/lib/codemirror.js @@ -358,13 +358,14 @@ window.CodeMirror = (function() { function alignHorizontally(cm) { var display = cm.display; - if (!display.alignWidgets && !display.gutters.firstChild) return; + if (!display.alignWidgets && (!display.gutters.firstChild || !cm.options.fixedGutter)) return; var comp = compensateForHScroll(display) - display.scroller.scrollLeft + cm.view.scrollLeft; var gutterW = display.gutters.offsetWidth, l = comp + "px"; for (var n = display.lineDiv.firstChild; n; n = n.nextSibling) if (n.alignable) { for (var i = 0, a = n.alignable; i < a.length; ++i) a[i].style.left = l; } - display.gutters.style.left = (comp + gutterW) + "px"; + if (cm.options.fixedGutter) + display.gutters.style.left = (comp + gutterW) + "px"; } function maybeUpdateLineNumberWidth(cm) { @@ -428,7 +429,8 @@ window.CodeMirror = (function() { if (changes && maybeUpdateLineNumberWidth(cm)) changes = true; - display.sizer.style.marginLeft = display.scrollbarH.style.left = display.gutters.offsetWidth + "px"; + var gutterW = display.sizer.style.marginLeft = display.gutters.offsetWidth + "px"; + display.scrollbarH.style.left = cm.options.fixedGutter ? gutterW : "0"; // When merged lines are present, the line that needs to be // redrawn might not be the one that was changed. @@ -616,8 +618,8 @@ window.CodeMirror = (function() { var wrap = elt("div", null, line.wrapClass, "position: relative"); if (cm.options.lineNumbers || markers) { var gutterWrap = wrap.appendChild(elt("div", null, null, "position: absolute; left: " + - dims.fixedPos + "px")); - wrap.alignable = [gutterWrap]; + (cm.options.fixedGutter ? dims.fixedPos : -dims.gutterTotalWidth) + "px")); + if (cm.options.fixedGutter) wrap.alignable = [gutterWrap]; if (cm.options.lineNumbers && (!markers || !markers["CodeMirror-linenumbers"])) wrap.lineNumber = gutterWrap.appendChild( elt("div", lineNumberFor(cm.options, lineNo), @@ -2883,6 +2885,10 @@ window.CodeMirror = (function() { setGuttersForLineNumbers(cm.options); guttersChanged(cm); }, true); + option("fixedGutter", true, function(cm, val) { + cm.display.gutters.style.left = val ? compensateForHScroll(cm.display) + "px" : "0"; + cm.refresh(); + }, true); option("lineNumbers", false, function(cm) { setGuttersForLineNumbers(cm.options); guttersChanged(cm); From c606a14d0e6da16dd4a11ac2596293a9d475f902 Mon Sep 17 00:00:00 2001 From: Marijn Haverbeke Date: Mon, 14 Jan 2013 14:10:02 +0100 Subject: [PATCH 0576/5780] Use document.documentMode when detecting IE versions Closes #1149 --- lib/codemirror.js | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/lib/codemirror.js b/lib/codemirror.js index a771a1e643..d0c0e83511 100644 --- a/lib/codemirror.js +++ b/lib/codemirror.js @@ -8,8 +8,8 @@ window.CodeMirror = (function() { // bugs and behavior differences. var gecko = /gecko\/\d/i.test(navigator.userAgent); var ie = /MSIE \d/.test(navigator.userAgent); - var ie_lt8 = /MSIE [1-7]\b/.test(navigator.userAgent); - var ie_lt9 = /MSIE [1-8]\b/.test(navigator.userAgent); + var ie_lt8 = ie && (document.documentMode == null || document.documentMode < 8); + var ie_lt9 = ie && (document.documentMode == null || document.documentMode < 9); var webkit = /WebKit\//.test(navigator.userAgent); var qtwebkit = webkit && /Qt\/\d+\.\d+/.test(navigator.userAgent); var chrome = /Chrome\//.test(navigator.userAgent); From 4857377f478821925d74c8f6a13f56ade1755d7c Mon Sep 17 00:00:00 2001 From: sonson Date: Mon, 14 Jan 2013 14:15:33 +0100 Subject: [PATCH 0577/5780] Support Scala in GFM mode. --- mode/markdown/markdown.js | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-) diff --git a/mode/markdown/markdown.js b/mode/markdown/markdown.js index 531c2b5469..63de81e6ee 100644 --- a/mode/markdown/markdown.js +++ b/mode/markdown/markdown.js @@ -10,7 +10,8 @@ CodeMirror.defineMode("markdown", function(cmCfg, modeCfg) { "c++": "text/x-c++src", java: "text/x-java", csharp: "text/x-csharp", - "c#": "text/x-csharp" + "c#": "text/x-csharp", + scala: "text/x-scala" }; var getMode = (function () { From 8eca1bc713f923088371254e49ddb31d08002cf6 Mon Sep 17 00:00:00 2001 From: Akeksandr Motsjonov Date: Sat, 12 Jan 2013 18:14:22 +0200 Subject: [PATCH 0578/5780] SearchCursor.matches() doesn't work properly if query is /.*/ issue #1155 --- addon/search/searchcursor.js | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/addon/search/searchcursor.js b/addon/search/searchcursor.js index 58fed74d00..5684690d1f 100644 --- a/addon/search/searchcursor.js +++ b/addon/search/searchcursor.js @@ -30,7 +30,7 @@ var line = cm.getLine(pos.line), match = query.exec(line), start = match && match.index; } - if (match) + if (match && match[0]) return {from: {line: pos.line, ch: start}, to: {line: pos.line, ch: start + match[0].length}, match: match}; From 1960b0d378270098d37aab223ff29237fa0b0372 Mon Sep 17 00:00:00 2001 From: Marijn Haverbeke Date: Mon, 14 Jan 2013 14:37:24 +0100 Subject: [PATCH 0579/5780] Guard against search cursors with a query of "" They'd exhibit the same infinite traversal behavior mentioned in #1155 --- addon/search/searchcursor.js | 25 ++++++++++++++++--------- 1 file changed, 16 insertions(+), 9 deletions(-) diff --git a/addon/search/searchcursor.js b/addon/search/searchcursor.js index 5684690d1f..b7e7a79054 100644 --- a/addon/search/searchcursor.js +++ b/addon/search/searchcursor.js @@ -40,15 +40,21 @@ var fold = caseFold ? function(str){return str.toLowerCase();} : function(str){return str;}; var target = query.split("\n"); // Different methods for single-line and multi-line queries - if (target.length == 1) - this.matches = function(reverse, pos) { - var line = fold(cm.getLine(pos.line)), len = query.length, match; - if (reverse ? (pos.ch >= len && (match = line.lastIndexOf(query, pos.ch - len)) != -1) - : (match = line.indexOf(query, pos.ch)) != -1) - return {from: {line: pos.line, ch: match}, - to: {line: pos.line, ch: match + len}}; - }; - else + if (target.length == 1) { + if (!query.length) { + // Empty string would match anything and never progress, so + // we define it to match nothing instead. + this.matches = function() {}; + } else { + this.matches = function(reverse, pos) { + var line = fold(cm.getLine(pos.line)), len = query.length, match; + if (reverse ? (pos.ch >= len && (match = line.lastIndexOf(query, pos.ch - len)) != -1) + : (match = line.indexOf(query, pos.ch)) != -1) + return {from: {line: pos.line, ch: match}, + to: {line: pos.line, ch: match + len}}; + }; + } + } else { this.matches = function(reverse, pos) { var ln = pos.line, idx = (reverse ? target.length - 1 : 0), match = target[idx], line = fold(cm.getLine(ln)); var offsetA = (reverse ? line.indexOf(match) + match.length : line.lastIndexOf(match)); @@ -70,6 +76,7 @@ return {from: reverse ? end : start, to: reverse ? start : end}; } }; + } } } From b1b1dd55281ccb33130c5f3df99472f1a76469c2 Mon Sep 17 00:00:00 2001 From: santec Date: Tue, 15 Jan 2013 23:52:23 +0100 Subject: [PATCH 0580/5780] Improve Mode SQL * mysql & mariadb: ** .21 syntax ** .table_name syntax ** added keywords * MIMEs define their operatorChars * MIMEs have a support prop * use spaces as tab in index.html for consistency * some return false -> null * more comments --- mode/sql/index.html | 93 ++++++++++++++++++++++++--------------------- mode/sql/sql.js | 49 +++++++++++++++++------- 2 files changed, 85 insertions(+), 57 deletions(-) diff --git a/mode/sql/index.html b/mode/sql/index.html index 36fc2238ab..7ec938a121 100644 --- a/mode/sql/index.html +++ b/mode/sql/index.html @@ -1,47 +1,49 @@ - - - SQL Mode for CodeMirror - - - - - - - - -

    SQL Mode for CodeMirror

    - - - -

    MIME types defined: - text/x-sql, - text/x-mysql, - text/x-mariadb, - text/x-plsql. -

    - + +

    MIME types defined: + text/x-sql, + text/x-mysql, + text/x-mariadb, + text/x-plsql. +

    +

    + Tests: + normal, + verbose. +

    + diff --git a/mode/sql/sql.js b/mode/sql/sql.js index dc163dd8d5..2a16882aea 100644 --- a/mode/sql/sql.js +++ b/mode/sql/sql.js @@ -5,13 +5,15 @@ CodeMirror.defineMode("sql", function(config, parserConfig) { atoms = parserConfig.atoms || {"false": true, "true": true, "null": true}, builtin = parserConfig.builtin || {}, keywords = parserConfig.keywords, - operatorChars = /^[*+\-%<>!=&|~^]/, + operatorChars = parserConfig.operatorChars || /^[*+\-%<>!=&|~^]/, + support = parserConfig.support || {}, hooks = parserConfig.hooks || {}, dateSQL = parserConfig.dateSQL || {"date" : true, "time" : true, "timestamp" : true}; - + function tokenBase(stream, state) { var ch = stream.next(); + // call hooks from the mime type if (hooks[ch]) { var result = hooks[ch](stream, state); if (result !== false) return result; @@ -36,7 +38,8 @@ CodeMirror.defineMode("sql", function(config, parserConfig) { // strings state.tokenize = tokenLiteral(ch); return state.tokenize(stream, state); - } else if (/^[\(\),\.;\[\]]/.test(ch)) { + } else if (/^[\(\),\;\[\]]/.test(ch)) { + // no highlightning return null; } else if (ch == "#" || (ch == "-" && stream.eat("-") && stream.eat(" "))) { // 1-line comments @@ -46,9 +49,19 @@ CodeMirror.defineMode("sql", function(config, parserConfig) { // multi-line comments state.tokenize = tokenComment; return state.tokenize(stream, state); + } else if (ch == ".") { + // .1 for 0.1 + if (stream.match(/^[0-9eE]+/) && support.zerolessFloat == true) { + return "number"; + } + // .table_name (ODBC) + if (stream.match(/^[a-zA-Z_]+/) && support.ODBCdotTable == true) { + return "variable-2"; + } } else if (operatorChars.test(ch)) { + // operators stream.eatWhile(operatorChars); - return false; + return null; } else if (ch == '{' && (stream.match(/^( )*(d|D|t|T|ts|TS)( )*'[^']*'( )*}/) || stream.match(/^( )*(d|D|t|T|ts|TS)( )*"[^"]*"( )*}/))) { // dates (weird ODBC syntax) @@ -63,10 +76,11 @@ CodeMirror.defineMode("sql", function(config, parserConfig) { if (builtin.hasOwnProperty(word)) return "builtin"; if (keywords.hasOwnProperty(word)) return "keyword"; if (client.hasOwnProperty(word)) return "string-2"; - return "variable"; + return null; } } + // 'string', with char specified in quote escaped by '\' function tokenLiteral(quote) { return function(stream, state) { var escaped = false, ch; @@ -149,15 +163,15 @@ CodeMirror.defineMode("sql", function(config, parserConfig) { (function() { "use strict"; - + + // `identifier` function hookIdentifier(stream) { var escaped = false, ch; - while ((ch = stream.next()) != null) { if (ch == "`" && !escaped) return "variable-2"; escaped = !escaped && ch == "`"; } - return false; + return null; } // variable token @@ -182,13 +196,13 @@ CodeMirror.defineMode("sql", function(config, parserConfig) { } else if (stream.match(/^[0-9a-zA-Z$\.\_]+/)) { return "variable-2"; } - return false; + return null; }; // short client keyword token function hookClient(stream) { // \g, etc - return stream.match(/^[a-zA-Z]\b/) ? "variable-2" : false; + return stream.match(/^[a-zA-Z]\b/) ? "variable-2" : null; } var sqlKeywords = "alter and as asc between by count create delete desc distinct drop from having in insert into is join like not on or order select set table union update values where "; @@ -204,16 +218,20 @@ CodeMirror.defineMode("sql", function(config, parserConfig) { keywords: set(sqlKeywords + "begin"), builtin: set("bool boolean bit blob enum long longblob longtext medium mediumblob mediumint mediumtext time timestamp tinyblob tinyint tinytext text bigint int int1 int2 int3 int4 int8 integer float float4 float8 double char varbinary varchar varcharacter precision real date datetime year unsigned signed decimal numeric"), atoms: set("false true null unknown"), - dateSQL: set("date time timestamp") + operatorChars: /^[*+\-%<>!=]/, + dateSQL: set("date time timestamp"), + support: set("ODBCdotTable") }); CodeMirror.defineMIME("text/x-mysql", { name: "sql", client: set("charset clear connect edit ego exit go help nopager notee nowarning pager print prompt quit rehash source status system tee"), keywords: set(sqlKeywords + "accessible action add after algorithm all analyze asensitive at authors auto_increment autocommit avg avg_row_length before binary binlog both btree cache call cascade cascaded case catalog_name chain change changed character check checkpoint checksum class_origin client_statistics close coalesce code collate collation collations column columns comment commit committed completion concurrent condition connection consistent constraint contains continue contributors convert cross current_date current_time current_timestamp current_user cursor data database databases day_hour day_microsecond day_minute day_second deallocate dec declare default delay_key_write delayed delimiter des_key_file describe deterministic dev_pop dev_samp deviance directory disable discard distinctrow div dual dumpfile each elseif enable enclosed end ends engine engines enum errors escape escaped even event events every execute exists exit explain extended fast fetch field fields first flush for force foreign found_rows full fulltext function general global grant grants group groupby_concat handler hash help high_priority hosts hour_microsecond hour_minute hour_second if ignore ignore_server_ids import index index_statistics infile inner innodb inout insensitive insert_method interval invoker isolation iterate key keys kill language last leading leave left level limit linear lines list load local localtime localtimestamp lock logs low_priority master master_heartbeat_period master_ssl_verify_server_cert masters match max max_rows maxvalue message_text middleint migrate min min_rows minute_microsecond minute_second mod mode modifies modify mutex mysql_errno natural next no no_write_to_binlog offline offset one online open optimize option optionally out outer outfile pack_keys parser partition partitions password phase plugin plugins prepare preserve prev primary privileges procedure processlist profile profiles purge query quick range read read_write reads real rebuild recover references regexp relaylog release remove rename reorganize repair repeatable replace require resignal restrict resume return returns revoke right rlike rollback rollup row row_format rtree savepoint schedule schema schema_name schemas second_microsecond security sensitive separator serializable server session share show signal slave slow smallint snapshot spatial specific sql sql_big_result sql_buffer_result sql_cache sql_calc_found_rows sql_no_cache sql_small_result sqlexception sqlstate sqlwarning ssl start starting starts status std stddev stddev_pop stddev_samp storage straight_join subclass_origin sum suspend table_name table_statistics tables tablespace temporary terminated to trailing transaction trigger triggers truncate uncommitted undo unique unlock upgrade usage use use_frm user user_resources user_statistics using utc_date utc_time utc_timestamp value variables varying view views warnings when while with work write xa xor year_month zerofill begin do then else loop repeat"), - builtin: set("bool boolean bit blob enum long longblob longtext medium mediumblob mediumint mediumtext time timestamp tinyblob tinyint tinytext text bigint int int1 int2 int3 int4 int8 integer float float4 float8 double char varbinary varchar varcharacter precision date datetime year unsigned signed decimal numeric"), + builtin: set("bool boolean bit blob decimal double enum float long longblob longtext medium mediumblob mediumint mediumtext time timestamp tinyblob tinyint tinytext text bigint int int1 int2 int3 int4 int8 integer float float4 float8 double char varbinary varchar varcharacter precision date datetime year unsigned signed numeric"), atoms: set("false true null unknown"), + operatorChars: /^[*+\-%<>!=&|^]/, dateSQL: set("date time timestamp"), + support: set("ODBCdotTable zerolessFloat"), hooks: { "@": hookVar, "`": hookIdentifier, @@ -225,9 +243,11 @@ CodeMirror.defineMode("sql", function(config, parserConfig) { name: "sql", client: set("charset clear connect edit ego exit go help nopager notee nowarning pager print prompt quit rehash source status system tee"), keywords: set(sqlKeywords + "accessible action add after algorithm all always analyze asensitive at authors auto_increment autocommit avg avg_row_length before binary binlog both btree cache call cascade cascaded case catalog_name chain change changed character check checkpoint checksum class_origin client_statistics close coalesce code collate collation collations column columns comment commit committed completion concurrent condition connection consistent constraint contains continue contributors convert cross current_date current_time current_timestamp current_user cursor data database databases day_hour day_microsecond day_minute day_second deallocate dec declare default delay_key_write delayed delimiter des_key_file describe deterministic dev_pop dev_samp deviance directory disable discard distinctrow div dual dumpfile each elseif enable enclosed end ends engine engines enum errors escape escaped even event events every execute exists exit explain extended fast fetch field fields first flush for force foreign found_rows full fulltext function general generated global grant grants group groupby_concat handler hash help high_priority hosts hour_microsecond hour_minute hour_second if ignore ignore_server_ids import index index_statistics infile inner innodb inout insensitive insert_method interval invoker isolation iterate key keys kill language last leading leave left level limit linear lines list load local localtime localtimestamp lock logs low_priority master master_heartbeat_period master_ssl_verify_server_cert masters match max max_rows maxvalue message_text middleint migrate min min_rows minute_microsecond minute_second mod mode modifies modify mutex mysql_errno natural next no no_write_to_binlog offline offset one online open optimize option optionally out outer outfile pack_keys parser partition partitions password persistent phase plugin plugins prepare preserve prev primary privileges procedure processlist profile profiles purge query quick range read read_write reads real rebuild recover references regexp relaylog release remove rename reorganize repair repeatable replace require resignal restrict resume return returns revoke right rlike rollback rollup row row_format rtree savepoint schedule schema schema_name schemas second_microsecond security sensitive separator serializable server session share show signal slave slow smallint snapshot spatial specific sql sql_big_result sql_buffer_result sql_cache sql_calc_found_rows sql_no_cache sql_small_result sqlexception sqlstate sqlwarning ssl start starting starts status std stddev stddev_pop stddev_samp storage straight_join subclass_origin sum suspend table_name table_statistics tables tablespace temporary terminated to trailing transaction trigger triggers truncate uncommitted undo unique unlock upgrade usage use use_frm user user_resources user_statistics using utc_date utc_time utc_timestamp value variables varying view views virtual warnings when while with work write xa xor year_month zerofill begin do then else loop repeat"), - builtin: set("bool boolean bit blob enum long longblob longtext medium mediumblob mediumint mediumtext time timestamp tinyblob tinyint tinytext text bigint int int1 int2 int3 int4 int8 integer float float4 float8 double char varbinary varchar varcharacter precision date datetime year unsigned signed decimal numeric"), + builtin: set("bool boolean bit blob decimal double enum float long longblob longtext medium mediumblob mediumint mediumtext time timestamp tinyblob tinyint tinytext text bigint int int1 int2 int3 int4 int8 integer float float4 float8 double char varbinary varchar varcharacter precision date datetime year unsigned signed numeric"), atoms: set("false true null unknown"), + operatorChars: /^[*+\-%<>!=&|^]/, dateSQL: set("date time timestamp"), + support: set("ODBCdotTable zerolessFloat"), hooks: { "@": hookVar, "`": hookIdentifier, @@ -242,6 +262,7 @@ CodeMirror.defineMode("sql", function(config, parserConfig) { keywords: set("abort accept access add all alter and any array arraylen as asc assert assign at attributes audit authorization avg base_table begin between binary_integer body boolean by case cast char char_base check close cluster clusters colauth column comment commit compress connect connected constant constraint crash create current currval cursor data_base database date dba deallocate debugoff debugon decimal declare default definition delay delete desc digits dispose distinct do drop else elsif enable end entry escape exception exception_init exchange exclusive exists exit external fast fetch file for force form from function generic goto grant group having identified if immediate in increment index indexes indicator initial initrans insert interface intersect into is key level library like limited local lock log logging long loop master maxextents maxtrans member minextents minus mislabel mode modify multiset new next no noaudit nocompress nologging noparallel not nowait number_base object of off offline on online only open option or order out package parallel partition pctfree pctincrease pctused pls_integer positive positiven pragma primary prior private privileges procedure public raise range raw read rebuild record ref references refresh release rename replace resource restrict return returning reverse revoke rollback row rowid rowlabel rownum rows run savepoint schema segment select separate session set share snapshot some space split sql start statement storage subtype successful synonym tabauth table tables tablespace task terminate then to trigger truncate type union unique unlimited unrecoverable unusable update use using validate value values variable view views when whenever where while with work"), functions: set("abs acos add_months ascii asin atan atan2 average bfilename ceil chartorowid chr concat convert cos cosh count decode deref dual dump dup_val_on_index empty error exp false floor found glb greatest hextoraw initcap instr instrb isopen last_day least lenght lenghtb ln lower lpad ltrim lub make_ref max min mod months_between new_time next_day nextval nls_charset_decl_len nls_charset_id nls_charset_name nls_initcap nls_lower nls_sort nls_upper nlssort no_data_found notfound null nvl others power rawtohex reftohex round rowcount rowidtochar rpad rtrim sign sin sinh soundex sqlcode sqlerrm sqrt stddev substr substrb sum sysdate tan tanh to_char to_date to_label to_multi_byte to_number to_single_byte translate true trunc uid upper user userenv variance vsize"), builtin: set("bfile blob character clob dec float int integer mlslabel natural naturaln nchar nclob number numeric nvarchar2 real rowtype signtype smallint string varchar varchar2"), - dateSQL: set("date time timestamp"), + operatorChars: /^[*+\-%<>!=~]/, + dateSQL: set("date time timestamp") }); }()); From 9e4a3cd381c97d705da665082c6495c3e048a95a Mon Sep 17 00:00:00 2001 From: Marijn Haverbeke Date: Wed, 16 Jan 2013 11:07:44 +0100 Subject: [PATCH 0581/5780] Fix bug in lineIsHidden test It would occasionally mark lines as hidden that were needed to display parts of other lines. Issue #1161 --- lib/codemirror.js | 22 ++++++++++++++++------ test/test.js | 14 ++++++++++++++ 2 files changed, 30 insertions(+), 6 deletions(-) diff --git a/lib/codemirror.js b/lib/codemirror.js index d0c0e83511..d9cf6571f8 100644 --- a/lib/codemirror.js +++ b/lib/codemirror.js @@ -1057,8 +1057,9 @@ window.CodeMirror = (function() { var lineObj = getLine(doc, lineNo); var found = coordsCharInner(cm, lineObj, lineNo, x, y); var merged = collapsedSpanAtEnd(lineObj); - if (merged && found.ch == lineRight(lineObj)) - lineNo = merged.find().to.line; + var mergedPos = merged && merged.find(); + if (merged && found.ch >= mergedPos.from.ch) + lineNo = mergedPos.to.line; else return found; } @@ -3360,10 +3361,11 @@ window.CodeMirror = (function() { else updateLineHeight(line, 0); } addMarkedSpan(line, span); - if (marker.collapsed && curLine == from.line && lineIsHidden(line)) - updateLineHeight(line, 0); ++curLine; }); + if (marker.collapsed) doc.iter(from.line, to.line + 1, function(line) { + if (lineIsHidden(line)) updateLineHeight(line, 0); + }); if (marker.readOnly) { sawReadOnlySpans = true; @@ -3538,9 +3540,12 @@ window.CodeMirror = (function() { return true; } } - window.lineIsHidden = lineIsHidden; function lineIsHiddenInner(line, span) { - if (span.to == null || span.marker.inclusiveRight && span.to == line.text.length) + if (span.to == null) { + var end = span.marker.find().to, endLine = getLine(lineDoc(line), end.line); + return lineIsHiddenInner(endLine, getMarkedSpanFor(endLine.markedSpans, span.marker)); + } + if (span.marker.inclusiveRight && span.to == line.text.length) return true; for (var sp, i = 0; i < line.markedSpans.length; ++i) { sp = line.markedSpans[i]; @@ -4083,6 +4088,11 @@ window.CodeMirror = (function() { return no; } + function lineDoc(line) { + for (var d = line.parent; d.parent; d = d.parent) {} + return d; + } + function lineAtHeight(chunk, h) { var n = 0; outer: do { diff --git a/test/test.js b/test/test.js index 6eea43b5ac..b71114f319 100644 --- a/test/test.js +++ b/test/test.js @@ -543,6 +543,20 @@ testCM("collapsedLines", function(cm) { eq(cleared, 1); }); +testCM("collapsedRangeCoordsChar", function(cm) { + var pos_1_3 = cm.charCoords({line: 1, ch: 3}); + var opts = {collapsed: true, inclusiveLeft: true, inclusiveRight: true}; + var m1 = cm.markText({line: 0, ch: 0}, {line: 2, ch: 0}, opts); + eqPos(cm.coordsChar(pos_1_3), {line: 3, ch: 3}); + m1.clear(); + var m1 = cm.markText({line: 0, ch: 0}, {line: 1, ch: 1}, opts); + var m2 = cm.markText({line: 1, ch: 1}, {line: 2, ch: 0}, opts); + eqPos(cm.coordsChar(pos_1_3), {line: 3, ch: 3}); + m1.clear(); m2.clear(); + var m1 = cm.markText({line: 0, ch: 0}, {line: 1, ch: 6}, opts); + eqPos(cm.coordsChar(pos_1_3), {line: 3, ch: 3}); +}, {value: "123456\nabcdef\nghijkl\nmnopqr\n"}); + testCM("hiddenLinesAutoUnfold", function(cm) { var range = foldLines(cm, 1, 3, true), cleared = 0; CodeMirror.on(range, "clear", function() {cleared++;}); From d9d9eb7d74d4ad62f2c85b1238121628434fd029 Mon Sep 17 00:00:00 2001 From: Marijn Haverbeke Date: Wed, 16 Jan 2013 11:35:04 +0100 Subject: [PATCH 0582/5780] Update maxLine when adding or clearing a collapsed range Closes #1161 --- lib/codemirror.js | 15 +++++++++++++-- 1 file changed, 13 insertions(+), 2 deletions(-) diff --git a/lib/codemirror.js b/lib/codemirror.js index d9cf6571f8..f0f5ff3eec 100644 --- a/lib/codemirror.js +++ b/lib/codemirror.js @@ -1967,7 +1967,7 @@ window.CodeMirror = (function() { if (!cm.options.lineWrapping) { checkWidthStart = lineNo(visualLine(doc, firstLine)); doc.iter(checkWidthStart, to.line + 1, function(line) { - if (lineLength(doc, line) == view.maxLineLength) { + if (line == view.maxLine) { recomputeMaxLength = true; return true; } @@ -3288,7 +3288,7 @@ window.CodeMirror = (function() { TextMarker.prototype.clear = function() { if (this.explicitlyCleared) return; startOperation(this.cm); - var min = null, max = null; + var view = this.cm.view, min = null, max = null; for (var i = 0; i < this.lines.length; ++i) { var line = this.lines[i]; var span = getMarkedSpanFor(line.markedSpans, this); @@ -3299,6 +3299,15 @@ window.CodeMirror = (function() { else if (this.collapsed && !lineIsHidden(line)) updateLineHeight(line, textHeight(this.cm.display)); } + if (this.collapsed && !this.cm.options.lineWrapping) for (var i = 0; i < this.lines.length; ++i) { + var visual = visualLine(view.doc, this.lines[i]), len = lineLength(view.doc, visual); + if (len > view.maxLineLength) { + view.maxLine = visual; + view.maxLineLength = len; + view.maxLineChanged = true; + } + } + if (min != null) regChange(this.cm, min, max + 1); this.lines.length = 0; this.explicitlyCleared = true; @@ -3351,6 +3360,8 @@ window.CodeMirror = (function() { var curLine = from.line, size = 0, collapsedAtStart, collapsedAtEnd; doc.iter(curLine, to.line + 1, function(line) { + if (marker.collapsed && !cm.options.lineWrapping && visualLine(doc, line) == cm.view.maxLine) + cm.curOp.updateMaxLine = true; var span = {from: null, to: null, marker: marker}; size += line.text.length; if (curLine == from.line) {span.from = from.ch; size -= from.ch;} From 169947226d85270ee3c7e1136e3877c9584723ca Mon Sep 17 00:00:00 2001 From: Marijn Haverbeke Date: Wed, 16 Jan 2013 11:48:43 +0100 Subject: [PATCH 0583/5780] Adjust test to work in Phantom.js --- test/test.js | 1 + 1 file changed, 1 insertion(+) diff --git a/test/test.js b/test/test.js index b71114f319..b4405d8b96 100644 --- a/test/test.js +++ b/test/test.js @@ -545,6 +545,7 @@ testCM("collapsedLines", function(cm) { testCM("collapsedRangeCoordsChar", function(cm) { var pos_1_3 = cm.charCoords({line: 1, ch: 3}); + pos_1_3.left += 2; pos_1_3.top += 2; var opts = {collapsed: true, inclusiveLeft: true, inclusiveRight: true}; var m1 = cm.markText({line: 0, ch: 0}, {line: 2, ch: 0}, opts); eqPos(cm.coordsChar(pos_1_3), {line: 3, ch: 3}); From 2dafec9d8fdd6f72eaccff3df1fa1d5afc655238 Mon Sep 17 00:00:00 2001 From: Marijn Haverbeke Date: Wed, 16 Jan 2013 13:20:18 +0100 Subject: [PATCH 0584/5780] [gfm mode] Remove link to nonexistent stylesheet --- mode/gfm/index.html | 1 - 1 file changed, 1 deletion(-) diff --git a/mode/gfm/index.html b/mode/gfm/index.html index b5bdb8888f..f413db19ef 100644 --- a/mode/gfm/index.html +++ b/mode/gfm/index.html @@ -16,7 +16,6 @@ - From 1c41c417f222483692a4f70af971a745632e8ae7 Mon Sep 17 00:00:00 2001 From: Marijn Haverbeke Date: Wed, 16 Jan 2013 14:59:24 +0100 Subject: [PATCH 0585/5780] [gfm mode] Simplify URL regexp, since it's crashing Chrome Issue #1160 --- mode/gfm/gfm.js | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-) diff --git a/mode/gfm/gfm.js b/mode/gfm/gfm.js index 6ff557f266..1aec9f8c43 100644 --- a/mode/gfm/gfm.js +++ b/mode/gfm/gfm.js @@ -75,9 +75,10 @@ CodeMirror.defineMode("gfm", function(config) { return "link"; } } - if (stream.match(/^((?:[a-z][\w-]+:(?:\/{1,3}|[a-z0-9%])|www\d{0,3}[.]|[a-z0-9.\-]+[.][a-z]{2,4}\/)(?:[^\s()<>]+|\(([^\s()<>]+|(\([^\s()<>]+\)))*\))+(?:\(([^\s()<>]+|(\([^\s()<>]+\)))*\)|[^\s`!()\[\]{};:'".,<>?«»“”‘’]))/i)) { + if (stream.match(/^((?:[a-z][\w-]+:(?:\/{1,3}|[a-z0-9%])|www\d{0,3}[.]|[a-z0-9.\-]+[.][a-z]{2,4}\/)(?:[^\s()<>]+|\([^\s()<>]*\))+(?:\([^\s()<>]*\)|[^\s`!()\[\]{};:'".,<>?«»“”‘’]))/i)) { // URLs // Taken from http://daringfireball.net/2010/07/improved_regex_for_matching_urls + // And then (issue #1160) simplified to make it not crash the Chrome Regexp engine return "link"; } stream.next(); From 7fc76ca55ad72ab2ec6c8a792f240bd508d337fe Mon Sep 17 00:00:00 2001 From: shaun gilchrist Date: Mon, 21 Jan 2013 10:00:55 +0100 Subject: [PATCH 0586/5780] Add APL mode --- mode/apl/apl.js | 160 ++++++++++++++++++++++++++++++++++++++++++++ mode/apl/index.html | 61 +++++++++++++++++ 2 files changed, 221 insertions(+) create mode 100644 mode/apl/apl.js create mode 100644 mode/apl/index.html diff --git a/mode/apl/apl.js b/mode/apl/apl.js new file mode 100644 index 0000000000..4accd3f7ce --- /dev/null +++ b/mode/apl/apl.js @@ -0,0 +1,160 @@ +CodeMirror.defineMode("apl", function(config, parserConfig) { + var builtInOps = { + ".": "innerProduct", + "\\": "scan", + "/": "reduce", + "⌿": "reduce1Axis", + "⍀": "scan1Axis", + "¨": "each", + "⍣": "power" + }; + var builtInFuncs = { + "+": ["conjugate", "add"], + "−": ["negate", "subtract"], + "×": ["signOf", "multiply"], + "÷": ["reciprocal", "divide"], + "⌈": ["ceiling", "greaterOf"], + "⌊": ["floor", "lesserOf"], + "∣": ["absolute", "residue"], + "⍳": ["indexGenerate", "indexOf"], + "?": ["roll", "deal"], + "⋆": ["exponentiate", "toThePowerOf"], + "⍟": ["naturalLog", "logToTheBase"], + "○": ["piTimes", "circularFuncs"], + "!": ["factorial", "binomial"], + "⌹": ["matrixInverse", "matrixDivide"], + "<": [null, "lessThan"], + "≤": [null, "lessThanOrEqual"], + "=": [null, "equals"], + ">": [null, "greaterThan"], + "≥": [null, "greaterThanOrEqual"], + "≠": [null, "notEqual"], + "≡": ["depth", "match"], + "≢": [null, "notMatch"], + "∈": ["enlist", "membership"], + "⍷": [null, "find"], + "∪": ["unique", "union"], + "∩": [null, "intersection"], + "∼": ["not", "without"], + "∨": [null, "or"], + "∧": [null, "and"], + "⍱": [null, "nor"], + "⍲": [null, "nand"], + "⍴": ["shapeOf", "reshape"], + ",": ["ravel", "catenate"], + "⍪": [null, "firstAxisCatenate"], + "⌽": ["reverse", "rotate"], + "⊖": ["axis1Reverse", "axis1Rotate"], + "⍉": ["transpose", null], + "↑": ["first", "take"], + "↓": [null, "drop"], + "⊂": ["enclose", "partitionWithAxis"], + "⊃": ["diclose", "pick"], + "⌷": [null, "index"], + "⍋": ["gradeUp", null], + "⍒": ["gradeDown", null], + "⊤": ["encode", null], + "⊥": ["decode", null], + "⍕": ["format", "formatByExample"], + "⍎": ["execute", null], + "⊣": ["stop", "left"], + "⊢": ["pass", "right"] + }; + + var isOperator = /[\.\/⌿⍀¨⍣]/; + var isNiladic = /⍬/; + var isFunction = /[\+−×÷⌈⌊∣⍳\?⋆⍟○!⌹<≤=>≥≠≡≢∈⍷∪∩∼∨∧⍱⍲⍴,⍪⌽⊖⍉↑↓⊂⊃⌷⍋⍒⊤⊥⍕⍎⊣⊢]/; + var isArrow = /←/; + var isComment = /[⍝#].*$/; + + var stringEater = function(type) { + var prev; + prev = false; + return function(c) { + prev = c; + if (c === type) { + return prev === "\\"; + } + return true; + }; + }; + return { + startState: function() { + return { + prev: false, + func: false, + op: false, + string: false, + escape: false + }; + }, + token: function(stream, state) { + var ch, funcName, word; + if (stream.eatSpace()) { + return null; + } + ch = stream.next(); + if (ch === '"' || ch === "'") { + stream.eatWhile(stringEater(ch)); + stream.next(); + state.prev = true; + return "string"; + } + if (/[\[{\(]/.test(ch)) { + state.prev = false; + return null; + } + if (/[\]}\)]/.test(ch)) { + state.prev = true; + return null; + } + if (isNiladic.test(ch)) { + state.prev = false; + return "niladic"; + } + if (/[¯\d]/.test(ch)) { + if (state.func) { + state.func = false; + state.prev = false; + } else { + state.prev = true; + } + stream.eatWhile(/[\w\.]/); + return "number"; + } + if (isOperator.test(ch)) { + return "operator apl-" + builtInOps[ch]; + } + if (isArrow.test(ch)) { + return "apl-arrow"; + } + if (isFunction.test(ch)) { + funcName = "apl-"; + if (builtInFuncs[ch] != null) { + if (state.prev) { + funcName += builtInFuncs[ch][1]; + } else { + funcName += builtInFuncs[ch][0]; + } + } + state.func = true; + state.prev = false; + return "function " + funcName; + } + if (isComment.test(ch)) { + stream.skipToEnd(); + return "comment"; + } + if (ch === "∘" && stream.peek() === ".") { + stream.next(); + return "function jot-dot"; + } + stream.eatWhile(/[\w\$_]/); + word = stream.current(); + state.prev = true; + return "keyword"; + } + }; +}); + +CodeMirror.defineMIME("text/apl", "apl"); diff --git a/mode/apl/index.html b/mode/apl/index.html new file mode 100644 index 0000000000..119ff17f19 --- /dev/null +++ b/mode/apl/index.html @@ -0,0 +1,61 @@ + + + + + CodeMirror: APL mode + + + + + + + + +

    CodeMirror: APL mode

    + +
    + + + +

    Simple mode that tries to handle APL as well as it can.

    +

    It attempts to label functions/operators based upon + monadic/dyadic usage (but this is far from fully fleshed out). + This means there are meaningful classnames so hover states can + have popups etc.

    + +

    MIME types defined: text/apl (APL code)

    + + From 180e75271b9562e8f5f59e56590a431d4fe99427 Mon Sep 17 00:00:00 2001 From: Marijn Haverbeke Date: Mon, 21 Jan 2013 10:03:04 +0100 Subject: [PATCH 0587/5780] Integrate APL mode --- doc/compress.html | 1 + doc/modes.html | 1 + mode/apl/apl.js | 2 +- mode/meta.js | 11 ++++++----- 4 files changed, 9 insertions(+), 6 deletions(-) diff --git a/doc/compress.html b/doc/compress.html index 2315838c80..313735f234 100644 --- a/doc/compress.html +++ b/doc/compress.html @@ -68,6 +68,7 @@

    { } CodeMi + diff --git a/doc/modes.html b/doc/modes.html index 7c072971c2..877227492f 100644 --- a/doc/modes.html +++ b/doc/modes.html @@ -23,6 +23,7 @@

    { } CodeMi
      +
    • APL
    • Asterisk dialplan
    • C, C++, C#
    • Clojure
    • diff --git a/mode/apl/apl.js b/mode/apl/apl.js index 4accd3f7ce..5c23af85da 100644 --- a/mode/apl/apl.js +++ b/mode/apl/apl.js @@ -1,4 +1,4 @@ -CodeMirror.defineMode("apl", function(config, parserConfig) { +CodeMirror.defineMode("apl", function() { var builtInOps = { ".": "innerProduct", "\\": "scan", diff --git a/mode/meta.js b/mode/meta.js index f670a569a6..ad212f9663 100644 --- a/mode/meta.js +++ b/mode/meta.js @@ -1,9 +1,10 @@ CodeMirror.modeInfo = [ - {name: 'Asterisk', mime: 'x-asterisk', mode: 'asterisk'}, - {name: 'C', mime: 'x-csrc', mode: 'clike'}, - {name: 'C++', mime: 'x-c++src', mode: 'clike'}, - {name: 'Java', mime: 'x-java', mode: 'clike'}, - {name: 'C#', mime: 'x-csharp', mode: 'clike'}, + {name: 'APL', mime: 'text/apl', mode: 'apl'}, + {name: 'Asterisk', mime: 'text/x-asterisk', mode: 'asterisk'}, + {name: 'C', mime: 'text/x-csrc', mode: 'clike'}, + {name: 'C++', mime: 'text/x-c++src', mode: 'clike'}, + {name: 'Java', mime: 'text/x-java', mode: 'clike'}, + {name: 'C#', mime: 'text/x-csharp', mode: 'clike'}, {name: 'Scala', mime: 'text/x-scala', mode: 'clike'}, {name: 'Clojure', mime: 'text/x-clojure', mode: 'clojure'}, {name: 'CoffeeScript', mime: 'text/x-coffeescript', mode: 'coffeescript'}, From 2f37f66531abd6f0d20bfdf7819e0a7f345f12ce Mon Sep 17 00:00:00 2001 From: Marijn Haverbeke Date: Mon, 21 Jan 2013 10:28:11 +0100 Subject: [PATCH 0588/5780] Properly restore horizonal scroll position on refresh() calls Issue #1164 --- lib/codemirror.js | 7 +++++-- 1 file changed, 5 insertions(+), 2 deletions(-) diff --git a/lib/codemirror.js b/lib/codemirror.js index f0f5ff3eec..487ed90d69 100644 --- a/lib/codemirror.js +++ b/lib/codemirror.js @@ -2829,8 +2829,11 @@ window.CodeMirror = (function() { refresh: function() { clearCaches(this); - if (this.display.scroller.scrollHeight > this.view.scrollTop) - this.display.scrollbarV.scrollTop = this.display.scroller.scrollTop = this.view.scrollTop; + var sTop = this.view.scrollTop, sLeft = this.view.scrollLeft; + if (this.display.scroller.scrollHeight > sTop) + this.display.scrollbarV.scrollTop = this.display.scroller.scrollTop = sTop; + if (this.display.scroller.scrollWidth > sLeft) + this.display.scrollbarH.scrollLeft = this.display.scroller.scrollLeft = sLeft; updateDisplay(this, true); }, From 48c2e8e454035c1849a1cbcce4503b564a4f26e9 Mon Sep 17 00:00:00 2001 From: Marijn Haverbeke Date: Mon, 21 Jan 2013 10:47:06 +0100 Subject: [PATCH 0589/5780] Fix workaround for webkit bug in rendering of overlong text nodes Issue #1163 --- lib/codemirror.js | 5 +++-- 1 file changed, 3 insertions(+), 2 deletions(-) diff --git a/lib/codemirror.js b/lib/codemirror.js index 487ed90d69..08a33030be 100644 --- a/lib/codemirror.js +++ b/lib/codemirror.js @@ -565,7 +565,7 @@ window.CodeMirror = (function() { node.style.display = "none"; node.lineObj = null; } else { - container.removeChild(node); + node.parentNode.removeChild(node); } return next; } @@ -3697,8 +3697,9 @@ window.CodeMirror = (function() { while (!stream.eol()) { var style = mode.token(stream, state); if (stream.pos > 5000) { + flattenSpans = false; // Webkit seems to refuse to render text nodes longer than 57444 characters - stream.pos = Math.min(text.length, stream.pos + 50000); + stream.pos = Math.min(text.length, stream.start + 50000); style = null; } var substr = stream.current(); From 83889141142bf897bfcce164dfa4c871232a3b6d Mon Sep 17 00:00:00 2001 From: Peter Kroon Date: Fri, 18 Jan 2013 13:09:40 +0100 Subject: [PATCH 0590/5780] Add Opera Mobile UA to the mobile list Add Opera Mobile UA to the mobile list. --- lib/codemirror.js | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/lib/codemirror.js b/lib/codemirror.js index 08a33030be..308d0ecb82 100644 --- a/lib/codemirror.js +++ b/lib/codemirror.js @@ -22,7 +22,7 @@ window.CodeMirror = (function() { var ios = /AppleWebKit/.test(navigator.userAgent) && /Mobile\/\w+/.test(navigator.userAgent); // This is woefully incomplete. Suggestions for alternative methods welcome. - var mobile = ios || /Android|webOS|BlackBerry|Opera Mini|IEMobile/i.test(navigator.userAgent); + var mobile = ios || /Android|webOS|BlackBerry|Opera Mini|Opera Mobi|IEMobile/i.test(navigator.userAgent); var mac = ios || /Mac/.test(navigator.platform); var windows = /windows/i.test(navigator.platform); From a14e1e91df89d5058191b324a2ed59fcca8e8d95 Mon Sep 17 00:00:00 2001 From: Marijn Haverbeke Date: Mon, 21 Jan 2013 11:07:02 +0100 Subject: [PATCH 0591/5780] Make sure the editor is re-focused after a no-op drag Closes #1168 --- lib/codemirror.js | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-) diff --git a/lib/codemirror.js b/lib/codemirror.js index 308d0ecb82..6f8f09b1f3 100644 --- a/lib/codemirror.js +++ b/lib/codemirror.js @@ -1560,7 +1560,8 @@ window.CodeMirror = (function() { // Don't do a replace if the drop happened inside of the selected text. if (cm.view.draggingText && !(posLess(pos, cm.view.sel.from) || posLess(cm.view.sel.to, pos))) { cm.view.draggingText(e); - if (ie) setTimeout(bind(focusInput, cm), 50); + // Ensure the editor is re-focused + setTimeout(bind(focusInput, cm), 20); return; } try { From e4c42b562d94dfe3f22c31ba9db70c10fa5dbd7f Mon Sep 17 00:00:00 2001 From: Marijn Haverbeke Date: Mon, 21 Jan 2013 11:24:59 +0100 Subject: [PATCH 0592/5780] [formatting addon] Don't line-break around inline tags --- addon/format/formatting.js | 10 ++++++++-- demo/formatting.html | 2 +- 2 files changed, 9 insertions(+), 3 deletions(-) diff --git a/addon/format/formatting.js b/addon/format/formatting.js index 25ee1531a8..88b84307f6 100644 --- a/addon/format/formatting.js +++ b/addon/format/formatting.js @@ -22,11 +22,17 @@ } }); + var inlineElements = /^(a|abbr|acronym|area|base|bdo|big|br|button|caption|cite|code|col|colgroup|dd|del|dfn|em|frame|hr|iframe|img|input|ins|kbd|label|legend|link|map|object|optgroup|option|param|q|samp|script|select|small|span|strong|sub|sup|textarea|tt|var)$/; + CodeMirror.extendMode("xml", { commentStart: "", - newlineAfterToken: function(type, content, textAfter) { - return type == "tag" && />$/.test(content) || /^$/.test(content) && state.context) || + /^CodeMirror: Formatting demo

    function test(c){ for (var i = 0; i < 10; i++){ process("a.b();c = null;", 300);} } -
    test 1
    test 2
    +
    test 1
    test 2
    From e653b1b87fe9e39b1a0eef3f87d0108d19d02acc Mon Sep 17 00:00:00 2001 From: Adam King Date: Sun, 20 Jan 2013 18:34:26 -0500 Subject: [PATCH 0593/5780] Fix brace indenting for clojure maps. --- mode/clojure/clojure.js | 6 +++--- 1 file changed, 3 insertions(+), 3 deletions(-) diff --git a/mode/clojure/clojure.js b/mode/clojure/clojure.js index 3fdabd263a..5d90edb8a5 100644 --- a/mode/clojure/clojure.js +++ b/mode/clojure/clojure.js @@ -142,7 +142,7 @@ CodeMirror.defineMode("clojure", function () { returnType = COMMENT; } else if (isNumber(ch,stream)){ returnType = NUMBER; - } else if (ch == "(" || ch == "[") { + } else if (ch == "(" || ch == "[" || ch == "{" ) { var keyWord = '', indentTemp = stream.column(), letter; /** Either @@ -172,9 +172,9 @@ CodeMirror.defineMode("clojure", function () { stream.backUp(stream.current().length - 1); // undo all the eating returnType = BRACKET; - } else if (ch == ")" || ch == "]") { + } else if (ch == ")" || ch == "]" || ch == "}") { returnType = BRACKET; - if (state.indentStack != null && state.indentStack.type == (ch == ")" ? "(" : "[")) { + if (state.indentStack != null && state.indentStack.type == (ch == ")" ? "(" : (ch == "]" ? "[" :"{"))) { popStack(state); } } else if ( ch == ":" ) { From f2d2938724a81b17c26e8f5017e48c253791b9ff Mon Sep 17 00:00:00 2001 From: Peter Kroon Date: Fri, 18 Jan 2013 15:51:21 +0100 Subject: [PATCH 0594/5780] Append image to wrapper Append image to wrapper. No more appending to document. Works with multiple editors. Tested: 3 editors beneath each other on 1 page. --- lib/codemirror.js | 12 +++++++++++- 1 file changed, 11 insertions(+), 1 deletion(-) diff --git a/lib/codemirror.js b/lib/codemirror.js index 6f8f09b1f3..39df6944c2 100644 --- a/lib/codemirror.js +++ b/lib/codemirror.js @@ -168,6 +168,15 @@ window.CodeMirror = (function() { // Used for measuring wheel scrolling granularity d.wheelDX = d.wheelDY = d.wheelStartX = d.wheelStartY = null; + + //opera setDragImage hack + if (opera) { + var img = elt('img'); + img.width = '1'; + img.height = '1'; + img.setAttribute('style','position:fixed; top:0; left:0; right:0'); + d.wrapper.appendChild(img); + } return d; } @@ -1612,8 +1621,9 @@ window.CodeMirror = (function() { // Use dummy image instead of default browsers image. // Recent Safari (~6.0.2) have a tendency to segfault when this happens, so we don't do it there. + var img = opera ? cm.display.wrapper.getElementsByTagName('img')[0] : elt('img'); if (e.dataTransfer.setDragImage && !safari) - e.dataTransfer.setDragImage(elt('img'), 0, 0); + e.dataTransfer.setDragImage(img, 0, 0); } function setScrollTop(cm, val) { From 4797771d9028f825bc93ce7ce5e0b3f2dc382ca9 Mon Sep 17 00:00:00 2001 From: Marijn Haverbeke Date: Mon, 21 Jan 2013 12:39:59 +0100 Subject: [PATCH 0595/5780] Clean up Opera setDragImage patch Issue #1166 --- lib/codemirror.js | 21 ++++++++++----------- 1 file changed, 10 insertions(+), 11 deletions(-) diff --git a/lib/codemirror.js b/lib/codemirror.js index 39df6944c2..27d1132d49 100644 --- a/lib/codemirror.js +++ b/lib/codemirror.js @@ -169,15 +169,6 @@ window.CodeMirror = (function() { // Used for measuring wheel scrolling granularity d.wheelDX = d.wheelDY = d.wheelStartX = d.wheelStartY = null; - //opera setDragImage hack - if (opera) { - var img = elt('img'); - img.width = '1'; - img.height = '1'; - img.setAttribute('style','position:fixed; top:0; left:0; right:0'); - d.wrapper.appendChild(img); - } - return d; } @@ -1621,9 +1612,17 @@ window.CodeMirror = (function() { // Use dummy image instead of default browsers image. // Recent Safari (~6.0.2) have a tendency to segfault when this happens, so we don't do it there. - var img = opera ? cm.display.wrapper.getElementsByTagName('img')[0] : elt('img'); - if (e.dataTransfer.setDragImage && !safari) + if (e.dataTransfer.setDragImage && !safari) { + var img = elt("img", null, null, "position: fixed; left: 0; top: 0;"); + if (opera) { + img.width = img.height = 1; + cm.display.wrapper.appendChild(img); + // Force a relayout, or Opera won't use our image for some obscure reason + img._top = img.offsetTop; + } e.dataTransfer.setDragImage(img, 0, 0); + if (opera) img.parentNode.removeChild(img); + } } function setScrollTop(cm, val) { From 32abd31833b7e9f18b8929f5996bf7ae1f00d99f Mon Sep 17 00:00:00 2001 From: Marijn Haverbeke Date: Mon, 21 Jan 2013 13:25:23 +0100 Subject: [PATCH 0596/5780] Mark release 3.01 --- doc/compress.html | 6 ++---- doc/oldrelease.html | 18 ++++++++++++++++++ index.html | 44 +++++++++++++++++++++++++------------------ lib/codemirror.js | 2 +- mode/mysql/index.html | 2 ++ mode/plsql/index.html | 2 ++ package.json | 2 +- 7 files changed, 52 insertions(+), 24 deletions(-) diff --git a/doc/compress.html b/doc/compress.html index 313735f234..94f293c725 100644 --- a/doc/compress.html +++ b/doc/compress.html @@ -30,11 +30,9 @@

    { } CodeMi

    Version: +     +

    +
    +
    + Select buffer: +     +
    + + + +

    Demonstration of + using linked documents + to provide a split view on a document, and + using swapDoc + to use a single editor to display multiple documents.

    + + + diff --git a/keymap/vim.js b/keymap/vim.js index ec85c28228..fb11848a11 100644 --- a/keymap/vim.js +++ b/keymap/vim.js @@ -810,9 +810,6 @@ selectionStart.ch = lineLength(cm, selectionStart.line); } } - // Need to set the cursor to clear the selection. Otherwise, - // CodeMirror can't figure out that we changed directions... - cm.setCursor(selectionStart); cm.setSelection(selectionStart, selectionEnd); updateMark(cm, vim, '<', cursorIsBefore(selectionStart, selectionEnd) ? selectionStart diff --git a/lib/codemirror.js b/lib/codemirror.js index 61c72a5ae0..58cbc0da14 100644 --- a/lib/codemirror.js +++ b/lib/codemirror.js @@ -45,24 +45,32 @@ window.CodeMirror = (function() { options[opt] = defaults[opt]; setGuttersForLineNumbers(options); - var display = this.display = makeDisplay(place); + var docStart = typeof options.value == "string" ? 0 : options.value.first; + var display = this.display = makeDisplay(place, docStart); display.wrapper.CodeMirror = this; updateGutters(this); if (options.autofocus && !mobile) focusInput(this); - this.view = makeView(new BranchChunk([new LeafChunk([makeLine("", null, textHeight(display))])])); + this.state = {keyMaps: [], + overlays: [], + modeGen: 0, + overwrite: false, focused: false, + suppressEdits: false, pasteIncoming: false, + draggingText: false, + highlight: new Delayed()}; + this.nextOpId = 0; - loadMode(this); themeChanged(this); if (options.lineWrapping) this.display.wrapper.className += " CodeMirror-wrap"; - // Initialize the content. - this.setValue(options.value || ""); + var doc = options.value; + if (typeof doc == "string") doc = new Doc(options.value, options.mode); + operation(this, attachDoc)(this, doc); + // Override magic textarea content restore that IE sometimes does // on our hidden textarea on reload if (ie) setTimeout(bind(resetInput, this, true), 20); - this.view.history = makeHistory(); registerEventHandlers(this); // IE throws unspecified error in certain cases, when @@ -81,7 +89,7 @@ window.CodeMirror = (function() { // DISPLAY CONSTRUCTOR - function makeDisplay(place) { + function makeDisplay(place, docStart) { var d = {}; var input = d.input = elt("textarea", null, null, "position: absolute; padding: 0; width: 1px; height: 1em; outline: none;"); if (webkit) input.style.width = "1000px"; @@ -135,7 +143,8 @@ window.CodeMirror = (function() { else if (ie_lt8) d.scrollbarH.style.minWidth = d.scrollbarV.style.minWidth = "18px"; // Current visible range (may be bigger than the view window). - d.viewOffset = d.showingFrom = d.showingTo = d.lastSizeC = 0; + d.viewOffset = d.lastSizeC = 0; + d.showingFrom = d.showingTo = docStart; // Used to only resize the line number gutter when necessary (when // the amount of lines crosses a boundary that makes its width change) @@ -162,9 +171,11 @@ window.CodeMirror = (function() { // string instead of the (large) selection. d.inaccurateSelection = false; - // Used to adjust overwrite behaviour when a paste has been - // detected - d.pasteIncoming = false; + // Tracks the maximum line length so that the horizontal scrollbar + // can be kept static when scrolling. + d.maxLine = null; + d.maxLineLength = 0; + d.maxLineChanged = false; // Used for measuring wheel scrolling granularity d.wheelDX = d.wheelDY = d.wheelStartX = d.wheelStartY = null; @@ -172,70 +183,55 @@ window.CodeMirror = (function() { return d; } - // VIEW CONSTRUCTOR - - function makeView(doc) { - var selPos = {line: 0, ch: 0}; - return { - doc: doc, - // frontier is the point up to which the content has been parsed, - frontier: 0, highlight: new Delayed(), - sel: {from: selPos, to: selPos, head: selPos, anchor: selPos, shift: false, extend: false}, - scrollTop: 0, scrollLeft: 0, - overwrite: false, focused: false, - // Tracks the maximum line length so that - // the horizontal scrollbar can be kept - // static when scrolling. - maxLine: getLine(doc, 0), - maxLineLength: 0, - maxLineChanged: false, - suppressEdits: false, - goalColumn: null, - cantEdit: false, - keyMaps: [], - overlays: [], - modeGen: 0 - }; - } - // STATE UPDATES // Used to get the editor into a consistent state again when options change. function loadMode(cm) { - var doc = cm.view.doc; - cm.view.mode = CodeMirror.getMode(cm.options, cm.options.mode); - doc.iter(0, doc.size, function(line) { + cm.doc.mode = CodeMirror.getMode(cm.options, cm.doc.modeOption); + cm.doc.iter(function(line) { if (line.stateAfter) line.stateAfter = null; if (line.styles) line.styles = null; }); - cm.view.frontier = 0; + cm.doc.frontier = cm.doc.first; startWorker(cm, 100); - cm.view.modeGen++; - if (cm.curOp) regChange(cm, 0, doc.size); + cm.state.modeGen++; + if (cm.curOp) regChange(cm); } function wrappingChanged(cm) { - var doc = cm.view.doc, th = textHeight(cm.display); if (cm.options.lineWrapping) { cm.display.wrapper.className += " CodeMirror-wrap"; - var perLine = cm.display.scroller.clientWidth / charWidth(cm.display) - 3; - doc.iter(0, doc.size, function(line) { - if (line.height == 0) return; - var guess = Math.ceil(line.text.length / perLine) || 1; - if (guess != 1) updateLineHeight(line, guess * th); - }); cm.display.sizer.style.minWidth = ""; } else { cm.display.wrapper.className = cm.display.wrapper.className.replace(" CodeMirror-wrap", ""); - computeMaxLength(cm.view); - doc.iter(0, doc.size, function(line) { - if (line.height != 0) updateLineHeight(line, th); - }); + computeMaxLength(cm); } - regChange(cm, 0, doc.size); + estimateLineHeights(cm); + regChange(cm); clearCaches(cm); - setTimeout(function(){updateScrollbars(cm.display, cm.view.doc.height);}, 100); + setTimeout(function(){updateScrollbars(cm.display, cm.doc.height);}, 100); + } + + function estimateHeight(cm) { + var th = textHeight(cm.display), wrapping = cm.options.lineWrapping; + var perLine = wrapping && Math.max(5, cm.display.scroller.clientWidth / charWidth(cm.display) - 3); + return function(line) { + if (lineIsHidden(line)) + return 0; + else if (wrapping) + return (Math.ceil(line.text.length / perLine) || 1) * th; + else + return th; + }; + } + + function estimateLineHeights(cm) { + var doc = cm.doc, est = estimateHeight(cm); + doc.iter(function(line) { + var estHeight = est(line); + if (estHeight != line.height) updateLineHeight(line, estHeight); + }); } function keyMapChanged(cm) { @@ -287,15 +283,16 @@ window.CodeMirror = (function() { return len; } - function computeMaxLength(view) { - view.maxLine = getLine(view.doc, 0); - view.maxLineLength = lineLength(view.doc, view.maxLine); - view.maxLineChanged = true; - view.doc.iter(1, view.doc.size, function(line) { - var len = lineLength(view.doc, line); - if (len > view.maxLineLength) { - view.maxLineLength = len; - view.maxLine = line; + function computeMaxLength(cm) { + var d = cm.display, doc = cm.doc; + d.maxLine = getLine(doc, doc.first); + d.maxLineLength = lineLength(doc, d.maxLine); + d.maxLineChanged = true; + doc.iter(function(line) { + var len = lineLength(doc, line); + if (len > d.maxLineLength) { + d.maxLineLength = len; + d.maxLine = line; } }); } @@ -359,7 +356,7 @@ window.CodeMirror = (function() { function alignHorizontally(cm) { var display = cm.display; if (!display.alignWidgets && (!display.gutters.firstChild || !cm.options.fixedGutter)) return; - var comp = compensateForHScroll(display) - display.scroller.scrollLeft + cm.view.scrollLeft; + var comp = compensateForHScroll(display) - display.scroller.scrollLeft + cm.doc.scrollLeft; var gutterW = display.gutters.offsetWidth, l = comp + "px"; for (var n = display.lineDiv.firstChild; n; n = n.nextSibling) if (n.alignable) { for (var i = 0, a = n.alignable; i < a.length; ++i) a[i].style.left = l; @@ -370,7 +367,7 @@ window.CodeMirror = (function() { function maybeUpdateLineNumberWidth(cm) { if (!cm.options.lineNumbers) return false; - var doc = cm.view.doc, last = lineNumberFor(cm.options, doc.size - 1), display = cm.display; + var doc = cm.doc, last = lineNumberFor(cm.options, doc.first + doc.size - 1), display = cm.display; if (last.length != display.lineNumChars) { var test = display.measure.appendChild(elt("div", [elt("div", last)], "CodeMirror-linenumber CodeMirror-gutter-elt")); @@ -398,12 +395,12 @@ window.CodeMirror = (function() { var oldFrom = cm.display.showingFrom, oldTo = cm.display.showingTo; var updated = updateDisplayInner(cm, changes, viewPort); if (updated) { - signalLater(cm, cm, "update", cm); + signalLater(cm, "update", cm); if (cm.display.showingFrom != oldFrom || cm.display.showingTo != oldTo) - signalLater(cm, cm, "viewportChange", cm, cm.display.showingFrom, cm.display.showingTo); + signalLater(cm, "viewportChange", cm, cm.display.showingFrom, cm.display.showingTo); } updateSelection(cm); - updateScrollbars(cm.display, cm.view.doc.height); + updateScrollbars(cm.display, cm.doc.height); return updated; } @@ -412,9 +409,10 @@ window.CodeMirror = (function() { // determine which DOM updates have to be made, and makes the // updates. function updateDisplayInner(cm, changes, viewPort) { - var display = cm.display, doc = cm.view.doc; + var display = cm.display, doc = cm.doc; if (!display.wrapper.clientWidth) { - display.showingFrom = display.showingTo = display.viewOffset = 0; + display.showingFrom = display.showingTo = doc.first; + display.viewOffset = 0; return; } @@ -445,24 +443,26 @@ window.CodeMirror = (function() { } // Used to determine which lines need their line numbers updated - var positionsChangedFrom = changes === true ? 0 : Infinity; + var positionsChangedFrom = changes === true ? doc.first : Infinity; if (cm.options.lineNumbers && changes && changes !== true) for (var i = 0; i < changes.length; ++i) if (changes[i].diff) { positionsChangedFrom = changes[i].from; break; } - var from = Math.max(visible.from - cm.options.viewportMargin, 0); - var to = Math.min(doc.size, visible.to + cm.options.viewportMargin); - if (display.showingFrom < from && from - display.showingFrom < 20) from = display.showingFrom; - if (display.showingTo > to && display.showingTo - to < 20) to = Math.min(doc.size, display.showingTo); + var end = doc.first + doc.size; + var from = Math.max(visible.from - cm.options.viewportMargin, doc.first); + var to = Math.min(end, visible.to + cm.options.viewportMargin); + if (display.showingFrom < from && from - display.showingFrom < 20) from = Math.max(doc.first, display.showingFrom); + if (display.showingTo > to && display.showingTo - to < 20) to = Math.min(end, display.showingTo); if (sawCollapsedSpans) { from = lineNo(visualLine(doc, getLine(doc, from))); - while (to < doc.size && lineIsHidden(getLine(doc, to))) ++to; + while (to < end && lineIsHidden(getLine(doc, to))) ++to; } // Create a range of theoretically intact lines, and punch holes // in that using the change info. var intact = changes === true ? [] : computeIntact([{from: display.showingFrom, to: display.showingTo}], changes); + // Clip off the parts that won't be visible var intactLines = 0; for (var i = 0; i < intact.length; ++i) { @@ -570,9 +570,9 @@ window.CodeMirror = (function() { return next; } - var nextIntact = intact.shift(), lineNo = from; - cm.view.doc.iter(from, to, function(line) { - if (nextIntact && nextIntact.to == lineNo) nextIntact = intact.shift(); + var nextIntact = intact.shift(), lineN = from; + cm.doc.iter(from, to, function(line) { + if (nextIntact && nextIntact.to == lineN) nextIntact = intact.shift(); if (lineIsHidden(line)) { if (line.height != 0) updateLineHeight(line, 0); if (line.widgets && cur.previousSibling) for (var i = 0; i < line.widgets.length; ++i) @@ -586,20 +586,20 @@ window.CodeMirror = (function() { } prev.appendChild(buildLineWidget(line.widgets[i], prev, dims)); } - } else if (nextIntact && nextIntact.from <= lineNo && nextIntact.to > lineNo) { + } else if (nextIntact && nextIntact.from <= lineN && nextIntact.to > lineN) { // This line is intact. Skip to the actual node. Update its // line number if needed. while (cur.lineObj != line) cur = rm(cur); - if (lineNumbers && updateNumbersFrom <= lineNo && cur.lineNumber) - setTextContent(cur.lineNumber, lineNumberFor(cm.options, lineNo)); + if (lineNumbers && updateNumbersFrom <= lineN && cur.lineNumber) + setTextContent(cur.lineNumber, lineNumberFor(cm.options, lineN)); cur = cur.nextSibling; } else { // This line needs to be generated. - var lineNode = buildLineElement(cm, line, lineNo, dims); + var lineNode = buildLineElement(cm, line, lineN, dims); container.insertBefore(lineNode, cur); lineNode.lineObj = line; } - ++lineNo; + ++lineN; }); while (cur) cur = rm(cur); } @@ -674,7 +674,7 @@ window.CodeMirror = (function() { function updateSelection(cm) { var display = cm.display; - var collapsed = posEq(cm.view.sel.from, cm.view.sel.to); + var collapsed = posEq(cm.doc.sel.from, cm.doc.sel.to); if (collapsed || cm.options.showCursorWhenSelecting) updateSelectionCursor(cm); else @@ -685,7 +685,7 @@ window.CodeMirror = (function() { display.selectionDiv.style.display = "none"; // Move the hidden textarea near the cursor to prevent scrolling artifacts - var headPos = cursorCoords(cm, cm.view.sel.head, "div"); + var headPos = cursorCoords(cm, cm.doc.sel.head, "div"); var wrapOff = display.wrapper.getBoundingClientRect(), lineOff = display.lineDiv.getBoundingClientRect(); display.inputDiv.style.top = Math.max(0, Math.min(display.wrapper.clientHeight - 10, headPos.top + lineOff.top - wrapOff.top)) + "px"; @@ -695,7 +695,7 @@ window.CodeMirror = (function() { // No selection, plain cursor function updateSelectionCursor(cm) { - var display = cm.display, pos = cursorCoords(cm, cm.view.sel.head, "div"); + var display = cm.display, pos = cursorCoords(cm, cm.doc.sel.head, "div"); display.cursor.style.left = pos.left + "px"; display.cursor.style.top = pos.top + "px"; display.cursor.style.height = Math.max(0, pos.bottom - pos.top) * cm.options.cursorHeight + "px"; @@ -711,7 +711,7 @@ window.CodeMirror = (function() { // Highlight selection function updateSelectionRange(cm) { - var display = cm.display, doc = cm.view.doc, sel = cm.view.sel; + var display = cm.display, doc = cm.doc, sel = cm.doc.sel; var fragment = document.createDocumentFragment(); var clientWidth = display.lineSpace.offsetWidth, pl = paddingLeft(cm.display); @@ -804,33 +804,33 @@ window.CodeMirror = (function() { // HIGHLIGHT WORKER function startWorker(cm, time) { - if (cm.view.mode.startState && cm.view.frontier < cm.display.showingTo) - cm.view.highlight.set(time, bind(highlightWorker, cm)); + if (cm.doc.mode.startState && cm.doc.frontier < cm.display.showingTo) + cm.state.highlight.set(time, bind(highlightWorker, cm)); } function highlightWorker(cm) { - var view = cm.view, doc = view.doc; - if (view.frontier >= cm.display.showingTo) return; + var doc = cm.doc; + if (doc.frontier < doc.first) doc.frontier = doc.first; + if (doc.frontier >= cm.display.showingTo) return; var end = +new Date + cm.options.workTime; - var state = copyState(view.mode, getStateBefore(cm, view.frontier)); + var state = copyState(doc.mode, getStateBefore(cm, doc.frontier)); var changed = [], prevChange; - doc.iter(view.frontier, Math.min(doc.size, cm.display.showingTo + 500), function(line) { - if (view.frontier >= cm.display.showingFrom) { // Visible + doc.iter(doc.frontier, Math.min(doc.first + doc.size, cm.display.showingTo + 500), function(line) { + if (doc.frontier >= cm.display.showingFrom) { // Visible var oldStyles = line.styles; line.styles = highlightLine(cm, line, state); var ischange = !oldStyles || oldStyles.length != line.styles.length; - for (var i = 0; !ischange && i < oldStyles.length; ++i) - ischange = oldStyles[i] != line.styles[i]; + for (var i = 0; !ischange && i < oldStyles.length; ++i) ischange = oldStyles[i] != line.styles[i]; if (ischange) { - if (prevChange && prevChange.end == view.frontier) prevChange.end++; - else changed.push(prevChange = {start: view.frontier, end: view.frontier + 1}); + if (prevChange && prevChange.end == doc.frontier) prevChange.end++; + else changed.push(prevChange = {start: doc.frontier, end: doc.frontier + 1}); } - line.stateAfter = copyState(view.mode, state); + line.stateAfter = copyState(doc.mode, state); } else { processLine(cm, line, state); - line.stateAfter = view.frontier % 5 == 0 ? copyState(view.mode, state) : null; + line.stateAfter = doc.frontier % 5 == 0 ? copyState(doc.mode, state) : null; } - ++view.frontier; + ++doc.frontier; if (+new Date > end) { startWorker(cm, cm.options.workDelay); return true; @@ -849,10 +849,10 @@ window.CodeMirror = (function() { // smallest indentation, which tends to need the least context to // parse correctly. function findStartLine(cm, n) { - var minindent, minline, doc = cm.view.doc; + var minindent, minline, doc = cm.doc; for (var search = n, lim = n - 100; search > lim; --search) { - if (search == 0) return 0; - var line = getLine(doc, search-1); + if (search <= doc.first) return doc.first; + var line = getLine(doc, search - 1); if (line.stateAfter) return search; var indented = countColumn(line.text, null, cm.options.tabSize); if (minline == null || minindent > indented) { @@ -864,15 +864,15 @@ window.CodeMirror = (function() { } function getStateBefore(cm, n) { - var view = cm.view; - if (!view.mode.startState) return true; - var pos = findStartLine(cm, n), state = pos && getLine(view.doc, pos-1).stateAfter; - if (!state) state = startState(view.mode); - else state = copyState(view.mode, state); - view.doc.iter(pos, n, function(line) { + var doc = cm.doc, display = cm.display; + if (!doc.mode.startState) return true; + var pos = findStartLine(cm, n), state = pos > doc.first && getLine(doc, pos-1).stateAfter; + if (!state) state = startState(doc.mode); + else state = copyState(doc.mode, state); + doc.iter(pos, n, function(line) { processLine(cm, line, state); - var save = pos == n - 1 || pos % 5 == 0 || pos >= view.showingFrom && pos < view.showingTo; - line.stateAfter = save ? copyState(view.mode, state) : null; + var save = pos == n - 1 || pos % 5 == 0 || pos >= display.showingFrom && pos < display.showingTo; + line.stateAfter = save ? copyState(doc.mode, state) : null; ++pos; }); return state; @@ -979,7 +979,7 @@ window.CodeMirror = (function() { function clearCaches(cm) { cm.display.measureLineCache.length = cm.display.measureLineCachePos = 0; cm.display.cachedCharWidth = cm.display.cachedTextHeight = null; - cm.view.maxLineChanged = true; + cm.display.maxLineChanged = true; } // Context is one of "line", "div" (display.lineDiv), "local"/null (editor), or "page" @@ -1003,12 +1003,12 @@ window.CodeMirror = (function() { } function charCoords(cm, pos, context, lineObj) { - if (!lineObj) lineObj = getLine(cm.view.doc, pos.line); + if (!lineObj) lineObj = getLine(cm.doc, pos.line); return intoCoordSystem(cm, lineObj, measureChar(cm, lineObj, pos.ch), context); } function cursorCoords(cm, pos, context, lineObj, measurement) { - lineObj = lineObj || getLine(cm.view.doc, pos.line); + lineObj = lineObj || getLine(cm.doc, pos.line); if (!measurement) measurement = measureLine(cm, lineObj); function get(ch, right) { var m = measureChar(cm, lineObj, ch, measurement); @@ -1046,11 +1046,12 @@ window.CodeMirror = (function() { // Coords must be lineSpace-local function coordsChar(cm, x, y) { - var doc = cm.view.doc; + var doc = cm.doc; y += cm.display.viewOffset; - if (y < 0) return {line: 0, ch: 0, outside: true}; - var lineNo = lineAtHeight(doc, y); - if (lineNo >= doc.size) return {line: doc.size - 1, ch: getLine(doc, doc.size - 1).text.length}; + if (y < 0) return {line: doc.first, ch: 0, outside: true}; + var lineNo = lineAtHeight(doc, y), last = doc.first + doc.size - 1; + if (lineNo > last) + return {line: doc.size - 1, ch: getLine(doc, last).text.length}; if (x < 0) x = 0; for (;;) { @@ -1141,80 +1142,106 @@ window.CodeMirror = (function() { // batched and then all combined and executed at once. function startOperation(cm) { - if (cm.curOp) ++cm.curOp.depth; - else cm.curOp = { - // Nested operations delay update until the outermost one - // finishes. - depth: 1, + cm.curOp = { // An array of ranges of lines that have to be updated. See // updateDisplay. changes: [], - delayedCallbacks: [], updateInput: null, userSelChange: null, textChanged: null, selectionChanged: false, updateMaxLine: false, + updateScrollPos: false, id: ++cm.nextOpId }; + if (!delayedCallbackDepth++) delayedCallbacks = []; } function endOperation(cm) { - var op = cm.curOp; - if (--op.depth) return; + var op = cm.curOp, doc = cm.doc, delayed; + if (!--delayedCallbackDepth) { + delayed = delayedCallbacks; + delayedCallbacks = null; + } cm.curOp = null; - var view = cm.view, display = cm.display; - if (op.updateMaxLine) computeMaxLength(view); - if (view.maxLineChanged && !cm.options.lineWrapping) { - var width = measureChar(cm, view.maxLine, view.maxLine.text.length).right; + var display = cm.display; + if (op.updateMaxLine) computeMaxLength(cm); + if (cm.display.maxLineChanged && !cm.options.lineWrapping) { + var width = measureChar(cm, cm.display.maxLine, cm.display.maxLine.text.length).right; display.sizer.style.minWidth = (width + 3 + scrollerCutOff) + "px"; - view.maxLineChanged = false; + cm.display.maxLineChanged = false; var maxScrollLeft = Math.max(0, display.sizer.offsetLeft + display.sizer.offsetWidth - display.scroller.clientWidth); - if (maxScrollLeft < view.scrollLeft) + if (maxScrollLeft < doc.scrollLeft && !op.updateScrollPos) setScrollLeft(cm, Math.min(display.scroller.scrollLeft, maxScrollLeft), true); } var newScrollPos, updated; - if (op.selectionChanged) { - var coords = cursorCoords(cm, view.sel.head); + if (op.updateScrollPos) { + newScrollPos = {scrollTop: doc.scrollTop, scrollLeft: doc.scrollLeft}; + } else if (op.selectionChanged) { + var coords = cursorCoords(cm, doc.sel.head); newScrollPos = calculateScrollPos(cm, coords.left, coords.top, coords.left, coords.bottom); } if (op.changes.length || newScrollPos && newScrollPos.scrollTop != null) updated = updateDisplay(cm, op.changes, newScrollPos && newScrollPos.scrollTop); if (!updated && op.selectionChanged) updateSelection(cm); - if (newScrollPos) scrollCursorIntoView(cm); + if (op.updateScrollPos) { + cm.display.scroller.scrollTop = doc.scrollTop; + cm.display.scroller.scrollLeft = doc.scrollLeft; + alignHorizontally(cm); + } else if (newScrollPos) { + scrollCursorIntoView(cm); + } if (op.selectionChanged) restartBlink(cm); - if (view.focused && op.updateInput) + if (cm.state.focused && op.updateInput) resetInput(cm, op.userSelChange); if (op.textChanged) signal(cm, "change", cm, op.textChanged); if (op.selectionChanged) signal(cm, "cursorActivity", cm); - for (var i = 0; i < op.delayedCallbacks.length; ++i) op.delayedCallbacks[i](cm); + if (delayed) for (var i = 0; i < delayed.length; ++i) delayed[i](cm); } // Wraps a function in an operation. Returns the wrapped function. function operation(cm1, f) { return function() { - var cm = cm1 || this; - startOperation(cm); - try {var result = f.apply(cm, arguments);} - finally {endOperation(cm);} + var cm = cm1 || this, mustEnd; + if (mustEnd = !cm.curOp) startOperation(cm); + try { var result = f.apply(cm, arguments); } + finally { if (mustEnd) endOperation(cm); } + return result; + }; + } + function docOperation(f) { + return function() { + var mustEnd, result; + if (mustEnd = this.cm && !this.cm.curOp) startOperation(this.cm); + try { result = f.apply(this, arguments); } + finally { if (mustEnd) endOperation(this.cm); } return result; }; } + function runInOp(cm, f) { + var mustEnd, result; + if (mustEnd = !cm.curOp) startOperation(cm); + try { result = f(); } + finally { if (mustEnd) endOperation(cm); } + return result; + } function regChange(cm, from, to, lendiff) { + if (from == null) from = cm.doc.first; + if (to == null) to = cm.doc.first + cm.doc.size; cm.curOp.changes.push({from: from, to: to, diff: lendiff}); } // INPUT HANDLING function slowPoll(cm) { - if (cm.view.pollingFast) return; + if (cm.display.pollingFast) return; cm.display.poll.set(cm.options.pollInterval, function() { readInput(cm); - if (cm.view.focused) slowPoll(cm); + if (cm.state.focused) slowPoll(cm); }); } @@ -1235,39 +1262,41 @@ window.CodeMirror = (function() { // events that indicate IME taking place, but these are not widely // supported or compatible enough yet to rely on.) function readInput(cm) { - var input = cm.display.input, prevInput = cm.display.prevInput, view = cm.view, sel = view.sel; - if (!view.focused || hasSelection(input) || isReadOnly(cm)) return false; + var input = cm.display.input, prevInput = cm.display.prevInput, doc = cm.doc, sel = doc.sel; + if (!cm.state.focused || hasSelection(input) || isReadOnly(cm)) return false; var text = input.value; if (text == prevInput && posEq(sel.from, sel.to)) return false; - startOperation(cm); - view.sel.shift = false; + var mustStart = !cm.curOp; + if (mustStart) startOperation(cm); + sel.shift = false; var same = 0, l = Math.min(prevInput.length, text.length); while (same < l && prevInput[same] == text[same]) ++same; var from = sel.from, to = sel.to; if (same < prevInput.length) from = {line: from.line, ch: from.ch - (prevInput.length - same)}; - else if (view.overwrite && posEq(from, to) && !cm.display.pasteIncoming) - to = {line: to.line, ch: Math.min(getLine(cm.view.doc, to.line).text.length, to.ch + (text.length - same))}; + else if (cm.state.overwrite && posEq(from, to) && !cm.state.pasteIncoming) + to = {line: to.line, ch: Math.min(getLine(doc, to.line).text.length, to.ch + (text.length - same))}; var updateInput = cm.curOp.updateInput; - updateDoc(cm, from, to, splitLines(text.slice(same)), "end", - cm.display.pasteIncoming ? "paste" : "input", {from: from, to: to}); + makeChange(cm.doc, {from: from, to: to, text: splitLines(text.slice(same)), + origin: cm.state.pasteIncoming ? "paste" : "input"}, "end"); + cm.curOp.updateInput = updateInput; if (text.length > 1000) input.value = cm.display.prevInput = ""; else cm.display.prevInput = text; - endOperation(cm); - cm.display.pasteIncoming = false; + if (mustStart) endOperation(cm); + cm.state.pasteIncoming = false; return true; } function resetInput(cm, user) { - var view = cm.view, minimal, selected; - if (!posEq(view.sel.from, view.sel.to)) { + var minimal, selected, doc = cm.doc; + if (!posEq(doc.sel.from, doc.sel.to)) { cm.display.prevInput = ""; minimal = hasCopyEvent && - (view.sel.to.line - view.sel.from.line > 100 || (selected = cm.getSelection()).length > 1000); + (doc.sel.to.line - doc.sel.from.line > 100 || (selected = cm.getSelection()).length > 1000); if (minimal) cm.display.input.value = "-"; else cm.display.input.value = selected || cm.getSelection(); - if (view.focused) selectInput(cm.display.input); + if (cm.state.focused) selectInput(cm.display.input); } else if (user) cm.display.prevInput = cm.display.input.value = ""; cm.display.inaccurateSelection = minimal; } @@ -1278,7 +1307,7 @@ window.CodeMirror = (function() { } function isReadOnly(cm) { - return cm.options.readOnly || cm.view.cantEdit; + return cm.options.readOnly || cm.doc.cantEdit; } // EVENT HANDLERS @@ -1310,7 +1339,7 @@ window.CodeMirror = (function() { on(d.scroller, "mousewheel", function(e){onScrollWheel(cm, e);}); on(d.scroller, "DOMMouseScroll", function(e){onScrollWheel(cm, e);}); - function reFocus() { if (cm.view.focused) setTimeout(bind(focusInput, cm), 0); } + function reFocus() { if (cm.state.focused) setTimeout(bind(focusInput, cm), 0); } on(d.scrollbarH, "mousedown", reFocus); on(d.scrollbarV, "mousedown", reFocus); // Prevent wrapper from ever scrolling @@ -1325,7 +1354,7 @@ window.CodeMirror = (function() { on(d.input, "keyup", operation(cm, function(e) { if (cm.options.onKeyEvent && cm.options.onKeyEvent(cm, addStop(e))) return; - if (e_prop(e, "keyCode") == 16) cm.view.sel.shift = false; + if (e_prop(e, "keyCode") == 16) cm.doc.sel.shift = false; })); on(d.input, "input", bind(fastPoll, cm)); on(d.input, "keydown", operation(cm, onKeyDown)); @@ -1349,7 +1378,7 @@ window.CodeMirror = (function() { fastPoll(cm); }); on(d.input, "paste", function() { - d.pasteIncoming = true; + cm.state.pasteIncoming = true; fastPoll(cm); }); @@ -1395,7 +1424,7 @@ window.CodeMirror = (function() { var lastClick, lastDoubleClick; function onMouseDown(e) { - var cm = this, display = cm.display, view = cm.view, sel = view.sel, doc = view.doc; + var cm = this, display = cm.display, doc = cm.doc, sel = doc.sel; sel.shift = e_prop(e, "shiftKey"); if (eventInWidget(display, e)) { @@ -1413,7 +1442,7 @@ window.CodeMirror = (function() { if (gecko) onContextMenu.call(cm, cm, e); return; case 2: - if (start) extendSelection(cm, start); + if (start) extendSelection(cm.doc, start); setTimeout(bind(focusInput, cm), 20); e_preventDefault(e); return; @@ -1423,7 +1452,7 @@ window.CodeMirror = (function() { // selection. if (!start) {if (e_target(e) == display.scroller) e_preventDefault(e); return;} - if (!view.focused) onFocus(cm); + if (!cm.state.focused) onFocus(cm); var now = +new Date, type = "single"; if (lastDoubleClick && lastDoubleClick.time > now - 400 && posEq(lastDoubleClick.pos, start)) { @@ -1436,7 +1465,7 @@ window.CodeMirror = (function() { lastDoubleClick = {time: now, pos: start}; e_preventDefault(e); var word = findWordAt(getLine(doc, start.line).text, start); - extendSelection(cm, word.from, word.to); + extendSelection(cm.doc, word.from, word.to); } else { lastClick = {time: now, pos: start}; } var last = start; @@ -1444,18 +1473,18 @@ window.CodeMirror = (function() { !posLess(start, sel.from) && !posLess(sel.to, start) && type == "single") { var dragEnd = operation(cm, function(e2) { if (webkit) display.scroller.draggable = false; - view.draggingText = false; + cm.state.draggingText = false; off(document, "mouseup", dragEnd); off(display.scroller, "drop", dragEnd); if (Math.abs(e.clientX - e2.clientX) + Math.abs(e.clientY - e2.clientY) < 10) { e_preventDefault(e2); - extendSelection(cm, start); + extendSelection(cm.doc, start); focusInput(cm); } }); // Let the drag handler handle this. if (webkit) display.scroller.draggable = true; - view.draggingText = dragEnd; + cm.state.draggingText = dragEnd; // IE's approach to draggable if (display.scroller.dragDrop) display.scroller.dragDrop(); on(document, "mouseup", dragEnd); @@ -1463,13 +1492,13 @@ window.CodeMirror = (function() { return; } e_preventDefault(e); - if (type == "single") extendSelection(cm, clipPos(doc, start)); + if (type == "single") extendSelection(cm.doc, clipPos(doc, start)); var startstart = sel.from, startend = sel.to; function doSelect(cur) { if (type == "single") { - extendSelection(cm, clipPos(doc, start), cur); + extendSelection(cm.doc, clipPos(doc, start), cur); return; } @@ -1477,11 +1506,11 @@ window.CodeMirror = (function() { startend = clipPos(doc, startend); if (type == "double") { var word = findWordAt(getLine(doc, cur.line).text, cur); - if (posLess(cur, startstart)) extendSelection(cm, word.from, startend); - else extendSelection(cm, startstart, word.to); + if (posLess(cur, startstart)) extendSelection(cm.doc, word.from, startend); + else extendSelection(cm.doc, startstart, word.to); } else if (type == "triple") { - if (posLess(cur, startstart)) extendSelection(cm, startend, clipPos(doc, {line: cur.line, ch: 0})); - else extendSelection(cm, startstart, clipPos(doc, {line: cur.line + 1, ch: 0})); + if (posLess(cur, startstart)) extendSelection(cm.doc, startend, clipPos(doc, {line: cur.line, ch: 0})); + else extendSelection(cm.doc, startstart, clipPos(doc, {line: cur.line + 1, ch: 0})); } } @@ -1497,7 +1526,7 @@ window.CodeMirror = (function() { var cur = posFromMouse(cm, e, true); if (!cur) return; if (!posEq(cur, last)) { - if (!view.focused) onFocus(cm); + if (!cm.state.focused) onFocus(cm); last = cur; doSelect(cur); var visible = visibleLines(display, doc); @@ -1546,11 +1575,8 @@ window.CodeMirror = (function() { reader.onload = function() { text[i] = reader.result; if (++read == n) { - pos = clipPos(cm.view.doc, pos); - operation(cm, function() { - var end = replaceRange(cm, text.join(""), pos, pos, "paste"); - setSelection(cm, pos, end); - })(); + pos = clipPos(cm.doc, pos); + replaceRange(cm.doc, text.join(""), pos, "around", "paste"); } }; reader.readAsText(file); @@ -1558,8 +1584,8 @@ window.CodeMirror = (function() { for (var i = 0; i < n; ++i) loadFile(files[i], i); } else { // Don't do a replace if the drop happened inside of the selected text. - if (cm.view.draggingText && !(posLess(pos, cm.view.sel.from) || posLess(cm.view.sel.to, pos))) { - cm.view.draggingText(e); + if (cm.state.draggingText && !(posLess(pos, cm.doc.sel.from) || posLess(cm.doc.sel.to, pos))) { + cm.state.draggingText(e); // Ensure the editor is re-focused setTimeout(bind(focusInput, cm), 20); return; @@ -1567,9 +1593,9 @@ window.CodeMirror = (function() { try { var text = e.dataTransfer.getData("Text"); if (text) { - var curFrom = cm.view.sel.from, curTo = cm.view.sel.to; - setSelection(cm, pos, pos); - if (cm.view.draggingText) replaceRange(cm, "", curFrom, curTo, "paste"); + var curFrom = cm.doc.sel.from, curTo = cm.doc.sel.to; + setSelection(cm.doc, pos, pos); + if (cm.state.draggingText) replaceRange(cm.doc, "", curFrom, curTo, "paste"); cm.replaceSelection(text, null, "paste"); focusInput(cm); onFocus(cm); @@ -1595,9 +1621,9 @@ window.CodeMirror = (function() { for (var i = 0; i < cm.options.gutters.length; ++i) { var g = display.gutters.childNodes[i]; if (g && g.getBoundingClientRect().right >= mX) { - var line = lineAtHeight(cm.view.doc, mY); + var line = lineAtHeight(cm.doc, mY); var gutter = cm.options.gutters[i]; - signalLater(cm, cm, "gutterClick", cm, line, gutter, e); + signalLater(cm, "gutterClick", cm, line, gutter, e); break; } } @@ -1626,17 +1652,17 @@ window.CodeMirror = (function() { } function setScrollTop(cm, val) { - if (Math.abs(cm.view.scrollTop - val) < 2) return; - cm.view.scrollTop = val; + if (Math.abs(cm.doc.scrollTop - val) < 2) return; + cm.doc.scrollTop = val; if (!gecko) updateDisplay(cm, [], val); if (cm.display.scroller.scrollTop != val) cm.display.scroller.scrollTop = val; if (cm.display.scrollbarV.scrollTop != val) cm.display.scrollbarV.scrollTop = val; if (gecko) updateDisplay(cm, []); } function setScrollLeft(cm, val, isScroller) { - if (isScroller ? val == cm.view.scrollLeft : Math.abs(cm.view.scrollLeft - val) < 2) return; + if (isScroller ? val == cm.doc.scrollLeft : Math.abs(cm.doc.scrollLeft - val) < 2) return; val = Math.min(val, cm.display.scroller.scrollWidth - cm.display.scroller.clientWidth); - cm.view.scrollLeft = val; + cm.doc.scrollLeft = val; alignHorizontally(cm); if (cm.display.scroller.scrollLeft != val) cm.display.scroller.scrollLeft = val; if (cm.display.scrollbarH.scrollLeft != val) cm.display.scrollbarH.scrollLeft = val; @@ -1700,9 +1726,9 @@ window.CodeMirror = (function() { if (dy && wheelPixelsPerUnit != null) { var pixels = dy * wheelPixelsPerUnit; - var top = cm.view.scrollTop, bot = top + display.wrapper.clientHeight; + var top = cm.doc.scrollTop, bot = top + display.wrapper.clientHeight; if (pixels < 0) top = Math.max(0, top + pixels - 50); - else bot = Math.min(cm.view.doc.height, bot + pixels + 50); + else bot = Math.min(cm.doc.height, bot + pixels + 50); updateDisplay(cm, [], {top: top, bottom: bot}); } @@ -1735,23 +1761,23 @@ window.CodeMirror = (function() { // Ensure previous input has been read, so that the handler sees a // consistent view of the document if (cm.display.pollingFast && readInput(cm)) cm.display.pollingFast = false; - var view = cm.view, prevShift = view.sel.shift; + var doc = cm.doc, prevShift = doc.sel.shift; try { - if (isReadOnly(cm)) view.suppressEdits = true; - if (dropShift) view.sel.shift = false; + if (isReadOnly(cm)) cm.state.suppressEdits = true; + if (dropShift) doc.sel.shift = false; bound(cm); } catch(e) { if (e != Pass) throw e; return false; } finally { - view.sel.shift = prevShift; - view.suppressEdits = false; + doc.sel.shift = prevShift; + cm.state.suppressEdits = false; } return true; } function allKeyMaps(cm) { - var maps = cm.view.keyMaps.slice(0); + var maps = cm.state.keyMaps.slice(0); maps.push(cm.options.keyMap); if (cm.options.extraKeys) maps.unshift(cm.options.extraKeys); return maps; @@ -1809,12 +1835,12 @@ window.CodeMirror = (function() { var lastStoppedKey = null; function onKeyDown(e) { var cm = this; - if (!cm.view.focused) onFocus(cm); + if (!cm.state.focused) onFocus(cm); if (ie && e.keyCode == 27) { e.returnValue = false; } if (cm.options.onKeyEvent && cm.options.onKeyEvent(cm, addStop(e))) return; var code = e_prop(e, "keyCode"); // IE does strange things with escape. - cm.view.sel.shift = code == 16 || e_prop(e, "shiftKey"); + cm.doc.sel.shift = code == 16 || e_prop(e, "shiftKey"); // First give onKeyEvent option a chance to handle this. var handled = handleKeyBinding(cm, e); if (opera) { @@ -1832,19 +1858,19 @@ window.CodeMirror = (function() { if (opera && keyCode == lastStoppedKey) {lastStoppedKey = null; e_preventDefault(e); return;} if (((opera && (!e.which || e.which < 10)) || khtml) && handleKeyBinding(cm, e)) return; var ch = String.fromCharCode(charCode == null ? keyCode : charCode); - if (this.options.electricChars && this.view.mode.electricChars && + if (this.options.electricChars && this.doc.mode.electricChars && this.options.smartIndent && !isReadOnly(this) && - this.view.mode.electricChars.indexOf(ch) > -1) - setTimeout(operation(cm, function() {indentLine(cm, cm.view.sel.to.line, "smart");}), 75); + this.doc.mode.electricChars.indexOf(ch) > -1) + setTimeout(operation(cm, function() {indentLine(cm, cm.doc.sel.to.line, "smart");}), 75); if (handleCharBinding(cm, e, ch)) return; fastPoll(cm); } function onFocus(cm) { if (cm.options.readOnly == "nocursor") return; - if (!cm.view.focused) { + if (!cm.state.focused) { signal(cm, "focus", cm); - cm.view.focused = true; + cm.state.focused = true; if (cm.display.scroller.className.search(/\bCodeMirror-focused\b/) == -1) cm.display.scroller.className += " CodeMirror-focused"; resetInput(cm, true); @@ -1853,25 +1879,24 @@ window.CodeMirror = (function() { restartBlink(cm); } function onBlur(cm) { - if (cm.view.focused) { + if (cm.state.focused) { signal(cm, "blur", cm); - cm.view.focused = false; + cm.state.focused = false; cm.display.scroller.className = cm.display.scroller.className.replace(" CodeMirror-focused", ""); } clearInterval(cm.display.blinker); - setTimeout(function() {if (!cm.view.focused) cm.view.sel.shift = false;}, 150); + setTimeout(function() {if (!cm.state.focused) cm.doc.sel.shift = false;}, 150); } var detectingSelectAll; function onContextMenu(cm, e) { - var display = cm.display; + var display = cm.display, sel = cm.doc.sel; if (eventInWidget(display, e)) return; - - var sel = cm.view.sel; + var pos = posFromMouse(cm, e), scrollPos = display.scroller.scrollTop; if (!pos || opera) return; // Opera is difficult. if (posEq(sel.from, sel.to) || posLess(pos, sel.from) || !posLess(pos, sel.to)) - operation(cm, setSelection)(cm, pos, pos); + operation(cm, setSelection)(cm.doc, pos, pos); var oldCSS = display.input.style.cssText; display.inputDiv.style.position = "absolute"; @@ -1917,124 +1942,150 @@ window.CodeMirror = (function() { // UPDATING - // Replace the range from from to to by the strings in newText. - // Afterwards, set the selection to selFrom, selTo. - function updateDoc(cm, from, to, newText, selUpdate, origin) { + function changeEnd(change) { + return {line: change.from.line + change.text.length - 1, + ch: lst(change.text).length + (change.text.length == 1 ? change.from.ch : 0)}; + } + + // Hint can be null|"end"|"start"|"around"|{anchor,head} + function computeSelAfterChange(sel, change, hint) { + if (hint && typeof hint == "object") return hint; // Assumed to be {from, to} object + if (hint == "start") return {anchor: change.from, head: change.from}; + + var end = changeEnd(change); + if (hint == "around") return {anchor: change.from, head: end}; + if (hint == "end") return {anchor: end, head: end}; + + // hint is null, leave the selection alone as much as possible + var adjustPos = function(pos) { + if (posLess(pos, change.from)) return pos; + if (!posLess(change.to, pos)) return end; + + var line = pos.line + change.text.length - (change.to.line - change.from.line) - 1, ch = pos.ch; + if (pos.line == change.to.line) ch += end.ch - change.to.ch; + return {line: line, ch: ch}; + }; + return {anchor: adjustPos(sel.anchor), head: adjustPos(sel.head)}; + } + + // Replace the range from from to to by the strings in replacement. + // change is a {from, to, text [, origin]} object + function makeChange(doc, change, selUpdate, ignoreReadOnly) { + if (doc.cm) { + if (!doc.cm.curOp) return operation(doc.cm, makeChange)(doc, change, selUpdate, ignoreReadOnly); + if (doc.cm.state.suppressEdits) return; + } + // Possibly split or suppress the update based on the presence // of read-only spans in its range. - var split = sawReadOnlySpans && - removeReadOnlyRanges(cm.view.doc, from, to); + var split = sawReadOnlySpans && !ignoreReadOnly && removeReadOnlyRanges(doc, change.from, change.to); if (split) { for (var i = split.length - 1; i >= 1; --i) - updateDocInner(cm, split[i].from, split[i].to, [""], origin); + makeChangeNoReadonly(doc, {from: split[i].from, to: split[i].to, text: [""]}); if (split.length) - return updateDocInner(cm, split[0].from, split[0].to, newText, selUpdate, origin); + makeChangeNoReadonly(doc, {from: split[0].from, to: split[0].to, text: change.text}, selUpdate); } else { - return updateDocInner(cm, from, to, newText, selUpdate, origin); + makeChangeNoReadonly(doc, change, selUpdate); } } - function updateDocInner(cm, from, to, newText, selUpdate, origin) { - if (cm.view.suppressEdits) return; + function makeChangeNoReadonly(doc, change, selUpdate) { + var selAfter = computeSelAfterChange(doc.sel, change, selUpdate); + addToHistory(doc, change, selAfter, doc.cm ? doc.cm.curOp.id : NaN); - var view = cm.view, doc = view.doc, old = []; - doc.iter(from.line, to.line + 1, function(line) { - old.push(newHL(line.text, line.markedSpans)); + makeChangeSingleDoc(doc, change, selAfter, stretchSpansOverChange(doc, change)); + + linkedDocs(doc, function(doc, sharedHist) { + if (!sharedHist) rebaseHist(doc.history, change); + makeChangeSingleDoc(doc, change, null, stretchSpansOverChange(doc, change)); }); - var startSelFrom = view.sel.from, startSelTo = view.sel.to; - var lines = updateMarkedSpans(hlSpans(old[0]), hlSpans(lst(old)), from.ch, to.ch, newText); - var retval = updateDocNoUndo(cm, from, to, lines, selUpdate, origin); - if (view.history) addChange(cm, from.line, newText.length, old, origin, - startSelFrom, startSelTo, view.sel.from, view.sel.to); - return retval; - } - - function unredoHelper(cm, type) { - var doc = cm.view.doc, hist = cm.view.history; - var set = (type == "undo" ? hist.done : hist.undone).pop(); - if (!set) return; - var anti = {events: [], fromBefore: set.fromAfter, toBefore: set.toAfter, - fromAfter: set.fromBefore, toAfter: set.toBefore}; - for (var i = set.events.length - 1; i >= 0; i -= 1) { - hist.dirtyCounter += type == "undo" ? -1 : 1; - var change = set.events[i]; - var replaced = [], end = change.start + change.added; - doc.iter(change.start, end, function(line) { replaced.push(newHL(line.text, line.markedSpans)); }); - anti.events.push({start: change.start, added: change.old.length, old: replaced}); - var selPos = i ? null : {from: set.fromBefore, to: set.toBefore}; - updateDocNoUndo(cm, {line: change.start, ch: 0}, {line: end - 1, ch: getLine(doc, end-1).text.length}, - change.old, selPos, type); - } + } + + function makeChangeFromHistory(doc, type) { + var hist = doc.history; + var event = (type == "undo" ? hist.done : hist.undone).pop(); + if (!event) return; + hist.dirtyCounter += type == "undo" ? -1 : 1; + + var anti = {changes: [], anchorBefore: event.anchorAfter, headBefore: event.headAfter, + anchorAfter: event.anchorBefore, headAfter: event.headBefore}; (type == "undo" ? hist.undone : hist.done).push(anti); + + for (var i = event.changes.length - 1; i >= 0; --i) { + var change = event.changes[i]; + change.origin = type; + anti.changes.push(historyChangeFromChange(doc, change)); + + var after = i ? computeSelAfterChange(doc.sel, change, null) + : {anchor: event.anchorBefore, head: event.headBefore}; + makeChangeSingleDoc(doc, change, after, getOldSpans(doc, change)); + + linkedDocs(doc, function(doc, sharedHist) { + if (!sharedHist) rebaseHist(doc.history, change); + makeChangeSingleDoc(doc, change, null, getOldSpans(doc, change)); + }); + } + } + + function shiftDoc(doc, distance) { + function shiftPos(pos) {return {line: pos.line + distance, ch: pos.ch};} + doc.first += distance; + if (doc.cm) regChange(doc.cm, doc.first, doc.first, distance); + doc.sel.head = shiftPos(doc.sel.head); doc.sel.anchor = shiftPos(doc.sel.anchor); + doc.sel.from = shiftPos(doc.sel.from); doc.sel.to = shiftPos(doc.sel.to); + } + + function makeChangeSingleDoc(doc, change, selAfter, spans) { + if (doc.cm && !doc.cm.curOp) + return operation(doc.cm, makeChangeSingleDoc)(doc, change, selAfter, spans); + + if (change.to.line < doc.first) { + shiftDoc(doc, change.text.length - 1 - (change.to.line - change.from.line)); + return; + } + if (change.from.line > doc.lastLine()) return; + + // Clip the change to the size of this doc + if (change.from.line < doc.first) { + var shift = change.text.length - 1 - (doc.first - change.from.line); + shiftDoc(doc, shift); + change = {from: {line: doc.first, ch: 0}, to: {line: change.to.line + shift, ch: change.to.ch}, + text: [lst(change.text)], origin: change.origin}; + } + var last = doc.lastLine(); + if (change.to.line > last) { + change = {from: change.from, to: {line: last, ch: getLine(doc, last).text.length}, + text: [change.text[0]], origin: change.origin}; + } + + if (!selAfter) selAfter = computeSelAfterChange(doc.sel, change, null); + if (doc.cm) makeChangeSingleDocInEditor(doc.cm, change, spans, selAfter); + else updateDoc(doc, change, spans, selAfter); } - function updateDocNoUndo(cm, from, to, lines, selUpdate, origin) { - var view = cm.view, doc = view.doc, display = cm.display; - if (view.suppressEdits) return; + function makeChangeSingleDocInEditor(cm, change, spans, selAfter) { + var doc = cm.doc, display = cm.display, from = change.from, to = change.to; - var nlines = to.line - from.line, firstLine = getLine(doc, from.line), lastLine = getLine(doc, to.line); var recomputeMaxLength = false, checkWidthStart = from.line; if (!cm.options.lineWrapping) { - checkWidthStart = lineNo(visualLine(doc, firstLine)); + checkWidthStart = lineNo(visualLine(doc, getLine(doc, from.line))); doc.iter(checkWidthStart, to.line + 1, function(line) { - if (line == view.maxLine) { + if (line == display.maxLine) { recomputeMaxLength = true; return true; } }); } - var lastHL = lst(lines), th = textHeight(display); + updateDoc(doc, change, spans, selAfter, estimateHeight(cm)); - // First adjust the line structure - if (from.ch == 0 && to.ch == 0 && hlText(lastHL) == "") { - // This is a whole-line replace. Treated specially to make - // sure line objects move the way they are supposed to. - var added = []; - for (var i = 0, e = lines.length - 1; i < e; ++i) - added.push(makeLine(hlText(lines[i]), hlSpans(lines[i]), th)); - updateLine(cm, lastLine, lastLine.text, hlSpans(lastHL)); - if (nlines) doc.remove(from.line, nlines, cm); - if (added.length) doc.insert(from.line, added); - } else if (firstLine == lastLine) { - if (lines.length == 1) { - updateLine(cm, firstLine, firstLine.text.slice(0, from.ch) + hlText(lines[0]) + - firstLine.text.slice(to.ch), hlSpans(lines[0])); - } else { - for (var added = [], i = 1, e = lines.length - 1; i < e; ++i) - added.push(makeLine(hlText(lines[i]), hlSpans(lines[i]), th)); - added.push(makeLine(hlText(lastHL) + firstLine.text.slice(to.ch), hlSpans(lastHL), th)); - updateLine(cm, firstLine, firstLine.text.slice(0, from.ch) + hlText(lines[0]), hlSpans(lines[0])); - doc.insert(from.line + 1, added); - } - } else if (lines.length == 1) { - updateLine(cm, firstLine, firstLine.text.slice(0, from.ch) + hlText(lines[0]) + - lastLine.text.slice(to.ch), hlSpans(lines[0])); - doc.remove(from.line + 1, nlines, cm); - } else { - var added = []; - updateLine(cm, firstLine, firstLine.text.slice(0, from.ch) + hlText(lines[0]), hlSpans(lines[0])); - updateLine(cm, lastLine, hlText(lastHL) + lastLine.text.slice(to.ch), hlSpans(lastHL)); - for (var i = 1, e = lines.length - 1; i < e; ++i) - added.push(makeLine(hlText(lines[i]), hlSpans(lines[i]), th)); - if (nlines > 1) doc.remove(from.line + 1, nlines - 1, cm); - doc.insert(from.line + 1, added); - } - - if (cm.options.lineWrapping) { - var perLine = Math.max(5, display.scroller.clientWidth / charWidth(display) - 3); - doc.iter(from.line, from.line + lines.length, function(line) { - if (line.height == 0) return; - var guess = (Math.ceil(line.text.length / perLine) || 1) * th; - if (guess != line.height) updateLineHeight(line, guess); - }); - } else { - doc.iter(checkWidthStart, from.line + lines.length, function(line) { + if (!cm.options.lineWrapping) { + doc.iter(checkWidthStart, from.line + change.text.length, function(line) { var len = lineLength(doc, line); - if (len > view.maxLineLength) { - view.maxLine = line; - view.maxLineLength = len; - view.maxLineChanged = true; + if (len > display.maxLineLength) { + display.maxLine = line; + display.maxLineLength = len; + display.maxLineChanged = true; recomputeMaxLength = false; } }); @@ -2042,57 +2093,26 @@ window.CodeMirror = (function() { } // Adjust frontier, schedule worker - view.frontier = Math.min(view.frontier, from.line); + doc.frontier = Math.min(doc.frontier, from.line); startWorker(cm, 400); - var lendiff = lines.length - nlines - 1; + var lendiff = change.text.length - (to.line - from.line) - 1; // Remember that these lines changed, for updating the display regChange(cm, from.line, to.line + 1, lendiff); if (hasHandler(cm, "change")) { - // Normalize lines to contain only strings, since that's what - // the change event handler expects - for (var i = 0; i < lines.length; ++i) - if (typeof lines[i] != "string") lines[i] = lines[i].text; - var changeObj = {from: from, to: to, text: lines, origin: origin}; + var changeObj = {from: from, to: to, text: change.text, origin: origin}; if (cm.curOp.textChanged) { for (var cur = cm.curOp.textChanged; cur.next; cur = cur.next) {} cur.next = changeObj; } else cm.curOp.textChanged = changeObj; } - - // Update the selection - var newSelFrom, newSelTo, end = {line: from.line + lines.length - 1, - ch: hlText(lastHL).length + (lines.length == 1 ? from.ch : 0)}; - if (selUpdate && typeof selUpdate != "string") { - if (selUpdate.from) { newSelFrom = selUpdate.from; newSelTo = selUpdate.to; } - else newSelFrom = newSelTo = selUpdate; - } else if (selUpdate == "end") { - newSelFrom = newSelTo = end; - } else if (selUpdate == "start") { - newSelFrom = newSelTo = from; - } else if (selUpdate == "around") { - newSelFrom = from; newSelTo = end; - } else { - var adjustPos = function(pos) { - if (posLess(pos, from)) return pos; - if (!posLess(to, pos)) return end; - var line = pos.line + lendiff; - var ch = pos.ch; - if (pos.line == to.line) - ch += hlText(lastHL).length - (to.ch - (to.line == from.line ? from.ch : 0)); - return {line: line, ch: ch}; - }; - newSelFrom = adjustPos(view.sel.from); - newSelTo = adjustPos(view.sel.to); - } - setSelection(cm, newSelFrom, newSelTo, null, true); - return end; } - function replaceRange(cm, code, from, to, origin) { + function replaceRange(doc, code, from, to, origin) { if (!to) to = from; if (posLess(to, from)) { var tmp = to; to = from; from = tmp; } - return updateDoc(cm, from, to, splitLines(code), null, origin); + if (typeof code == "string") code = splitLines(code); + makeChange(doc, {from: from, to: to, text: code, origin: origin}, null); } // SELECTION @@ -2101,23 +2121,23 @@ window.CodeMirror = (function() { function posLess(a, b) {return a.line < b.line || (a.line == b.line && a.ch < b.ch);} function copyPos(x) {return {line: x.line, ch: x.ch};} - function clipLine(doc, n) {return Math.max(0, Math.min(n, doc.size-1));} + function clipLine(doc, n) {return Math.max(doc.first, Math.min(n, doc.first + doc.size - 1));} function clipPos(doc, pos) { - if (pos.line < 0) return {line: 0, ch: 0}; - if (pos.line >= doc.size) return {line: doc.size-1, ch: getLine(doc, doc.size-1).text.length}; + if (pos.line < doc.first) return {line: doc.first, ch: 0}; + var last = doc.first + doc.size - 1; + if (pos.line > last) return {line: last, ch: getLine(doc, last).text.length}; var ch = pos.ch, linelen = getLine(doc, pos.line).text.length; if (ch == null || ch > linelen) return {line: pos.line, ch: linelen}; else if (ch < 0) return {line: pos.line, ch: 0}; else return pos; } - function isLine(doc, l) {return l >= 0 && l < doc.size;} + function isLine(doc, l) {return l >= doc.first && l < doc.first + doc.size;} // If shift is held, this will move the selection anchor. Otherwise, // it'll set the whole selection. - function extendSelection(cm, pos, other, bias) { - var sel = cm.view.sel; - if (sel.shift || sel.extend) { - var anchor = sel.anchor; + function extendSelection(doc, pos, other, bias) { + if (doc.sel.shift || doc.sel.extend) { + var anchor = doc.sel.anchor; if (other) { var posBefore = posLess(pos, anchor); if (posBefore != posLess(other, anchor)) { @@ -2127,24 +2147,24 @@ window.CodeMirror = (function() { pos = other; } } - setSelection(cm, anchor, pos, bias); + setSelection(doc, anchor, pos, bias); } else { - setSelection(cm, pos, other || pos, bias); + setSelection(doc, pos, other || pos, bias); } - cm.curOp.userSelChange = true; + if (doc.cm) doc.cm.curOp.userSelChange = true; } // Update the selection. Last two args are only used by // updateDoc, since they have to be expressed in the line // numbers before the update. - function setSelection(cm, anchor, head, bias, checkAtomic) { - cm.view.goalColumn = null; - var sel = cm.view.sel; + function setSelection(doc, anchor, head, bias, checkAtomic) { + var sel = doc.sel; + sel.goalColumn = null; // Skip over atomic spans. if (checkAtomic || !posEq(anchor, sel.anchor)) - anchor = skipAtomic(cm, anchor, bias, checkAtomic != "push"); + anchor = skipAtomic(doc, anchor, bias, checkAtomic != "push"); if (checkAtomic || !posEq(head, sel.head)) - head = skipAtomic(cm, head, bias, checkAtomic != "push"); + head = skipAtomic(doc, head, bias, checkAtomic != "push"); if (posEq(sel.anchor, anchor) && posEq(sel.head, head)) return; @@ -2153,18 +2173,18 @@ window.CodeMirror = (function() { sel.from = inv ? head : anchor; sel.to = inv ? anchor : head; - cm.curOp.updateInput = true; - cm.curOp.selectionChanged = true; + if (doc.cm) + doc.cm.curOp.updateInput = doc.cm.curOp.selectionChanged = true; } function reCheckSelection(cm) { - setSelection(cm, cm.view.sel.from, cm.view.sel.to, null, "push"); + setSelection(cm.doc, cm.doc.sel.from, cm.doc.sel.to, null, "push"); } - function skipAtomic(cm, pos, bias, mayClear) { - var doc = cm.view.doc, flipped = false, curPos = pos; + function skipAtomic(doc, pos, bias, mayClear) { + var flipped = false, curPos = pos; var dir = bias || 1; - cm.view.cantEdit = false; + doc.cantEdit = false; search: for (;;) { var line = getLine(doc, curPos.line), toClear; if (line.markedSpans) { @@ -2180,20 +2200,20 @@ window.CodeMirror = (function() { if (posEq(newPos, curPos)) { newPos.ch += dir; if (newPos.ch < 0) { - if (newPos.line) newPos = clipPos(doc, {line: newPos.line - 1}); + if (newPos.line > doc.first) newPos = clipPos(doc, {line: newPos.line - 1}); else newPos = null; } else if (newPos.ch > line.text.length) { - if (newPos.line < doc.size - 1) newPos = {line: newPos.line + 1, ch: 0}; + if (newPos.line < doc.first + doc.size - 1) newPos = {line: newPos.line + 1, ch: 0}; else newPos = null; } if (!newPos) { if (flipped) { // Driven in a corner -- no valid cursor position found at all // -- try again *with* clearing, if we didn't already - if (!mayClear) return skipAtomic(cm, pos, bias, true); + if (!mayClear) return skipAtomic(doc, pos, bias, true); // Otherwise, turn off editing until further notice, and return the start of the doc - cm.view.cantEdit = true; - return {line: 0, ch: 0}; + doc.cantEdit = true; + return {line: doc.first, ch: 0}; } flipped = true; newPos = pos; dir = -dir; } @@ -2211,9 +2231,8 @@ window.CodeMirror = (function() { // SCROLLING function scrollCursorIntoView(cm) { - var view = cm.view; - var coords = scrollPosIntoView(cm, view.sel.head); - if (!view.focused) return; + var coords = scrollPosIntoView(cm, cm.doc.sel.head); + if (!cm.state.focused) return; var display = cm.display, box = display.sizer.getBoundingClientRect(), doScroll = null; if (coords.top + box.top < 0) doScroll = true; else if (coords.bottom + box.top > (window.innerHeight || document.documentElement.clientHeight)) doScroll = false; @@ -2233,14 +2252,14 @@ window.CodeMirror = (function() { for (;;) { var changed = false, coords = cursorCoords(cm, pos); var scrollPos = calculateScrollPos(cm, coords.left, coords.top, coords.left, coords.bottom); - var startTop = cm.view.scrollTop, startLeft = cm.view.scrollLeft; + var startTop = cm.doc.scrollTop, startLeft = cm.doc.scrollLeft; if (scrollPos.scrollTop != null) { setScrollTop(cm, scrollPos.scrollTop); - if (Math.abs(cm.view.scrollTop - startTop) > 1) changed = true; + if (Math.abs(cm.doc.scrollTop - startTop) > 1) changed = true; } if (scrollPos.scrollLeft != null) { setScrollLeft(cm, scrollPos.scrollLeft); - if (Math.abs(cm.view.scrollLeft - startLeft) > 1) changed = true; + if (Math.abs(cm.doc.scrollLeft - startLeft) > 1) changed = true; } if (!changed) return coords; } @@ -2256,7 +2275,7 @@ window.CodeMirror = (function() { var display = cm.display, pt = paddingTop(display); y1 += pt; y2 += pt; var screen = display.scroller.clientHeight - scrollerCutOff, screentop = display.scroller.scrollTop, result = {}; - var docBottom = cm.view.doc.height + 2 * pt; + var docBottom = cm.doc.height + 2 * pt; var atTop = y1 < pt + 10, atBottom = y2 + pt > docBottom - 10; if (y1 < screentop) result.scrollTop = atTop ? 0 : Math.max(0, y1); else if (y2 > screentop + screen) result.scrollTop = (atBottom ? docBottom : y2) - screen; @@ -2277,10 +2296,10 @@ window.CodeMirror = (function() { // API UTILITIES function indentLine(cm, n, how, aggressive) { - var doc = cm.view.doc; + var doc = cm.doc; if (!how) how = "add"; if (how == "smart") { - if (!cm.view.mode.indent) how = "prev"; + if (!cm.doc.mode.indent) how = "prev"; else var state = getStateBefore(cm, n); } @@ -2288,18 +2307,20 @@ window.CodeMirror = (function() { var line = getLine(doc, n), curSpace = countColumn(line.text, null, tabSize); var curSpaceString = line.text.match(/^\s*/)[0], indentation; if (how == "smart") { - indentation = cm.view.mode.indent(state, line.text.slice(curSpaceString.length), line.text); + indentation = cm.doc.mode.indent(state, line.text.slice(curSpaceString.length), line.text); if (indentation == Pass) { if (!aggressive) return; how = "prev"; } } if (how == "prev") { - if (n) indentation = countColumn(getLine(doc, n-1).text, null, tabSize); + if (n > doc.first) indentation = countColumn(getLine(doc, n-1).text, null, tabSize); else indentation = 0; + } else if (how == "add") { + indentation = curSpace + cm.options.indentUnit; + } else if (how == "subtract") { + indentation = curSpace - cm.options.indentUnit; } - else if (how == "add") indentation = curSpace + cm.options.indentUnit; - else if (how == "subtract") indentation = curSpace - cm.options.indentUnit; indentation = Math.max(0, indentation); var indentString = "", pos = 0; @@ -2308,12 +2329,12 @@ window.CodeMirror = (function() { if (pos < indentation) indentString += spaceStr(indentation - pos); if (indentString != curSpaceString) - replaceRange(cm, indentString, {line: n, ch: 0}, {line: n, ch: curSpaceString.length}, "input"); + replaceRange(cm.doc, indentString, {line: n, ch: 0}, {line: n, ch: curSpaceString.length}, "input"); line.stateAfter = null; } function changeLine(cm, handle, op) { - var no = handle, line = handle, doc = cm.view.doc; + var no = handle, line = handle, doc = cm.doc; if (typeof handle == "number") line = getLine(doc, clipLine(doc, handle)); else no = lineNo(handle); if (no == null) return null; @@ -2322,12 +2343,12 @@ window.CodeMirror = (function() { return line; } - function findPosH(cm, dir, unit, visually) { - var doc = cm.view.doc, end = cm.view.sel.head, line = end.line, ch = end.ch; + function findPosH(doc, dir, unit, visually) { + var end = doc.sel.head, line = end.line, ch = end.ch; var lineObj = getLine(doc, line); function findNextLine() { var l = line + dir; - if (l < 0 || l == doc.size) return false; + if (l < doc.first || l >= doc.first + doc.size) return false; line = l; return lineObj = getLine(doc, l); } @@ -2352,7 +2373,7 @@ window.CodeMirror = (function() { if (dir > 0) if (!moveOnce()) break; } } - return skipAtomic(cm, {line: line, ch: ch}, dir, true); + return skipAtomic(doc, {line: line, ch: ch}, dir, true); } function findWordAt(line, pos) { @@ -2370,7 +2391,7 @@ window.CodeMirror = (function() { } function selectLine(cm, line) { - extendSelection(cm, {line: line, ch: 0}, clipPos(cm.view.doc, {line: line + 1, ch: 0})); + extendSelection(cm.doc, {line: line, ch: 0}, clipPos(cm.doc, {line: line + 1, ch: 0})); } // PROTOTYPE @@ -2379,24 +2400,6 @@ window.CodeMirror = (function() { // 'wrap f in an operation, performed on its `this` parameter' CodeMirror.prototype = { - getValue: function(lineSep) { - var text = [], doc = this.view.doc; - doc.iter(0, doc.size, function(line) { text.push(line.text); }); - return text.join(lineSep || "\n"); - }, - - setValue: operation(null, function(code) { - var doc = this.view.doc, top = {line: 0, ch: 0}, lastLen = getLine(doc, doc.size-1).text.length; - updateDocInner(this, top, {line: doc.size - 1, ch: lastLen}, splitLines(code), top, top, "setValue"); - }), - - getSelection: function(lineSep) { return this.getRange(this.view.sel.from, this.view.sel.to, lineSep); }, - - replaceSelection: operation(null, function(code, collapse, origin) { - var sel = this.view.sel; - updateDoc(this, sel.from, sel.to, splitLines(code), collapse || "around", origin); - }), - focus: function(){window.focus(); focusInput(this); onFocus(this); fastPoll(this);}, setOption: function(option, value) { @@ -2408,15 +2411,14 @@ window.CodeMirror = (function() { }, getOption: function(option) {return this.options[option];}, - - getMode: function() {return this.view.mode;}, + getMode: function() {return this.doc.mode;}, + getDoc: function() {return this.doc;}, addKeyMap: function(map) { - this.view.keyMaps.push(map); + this.state.keyMaps.push(map); }, - removeKeyMap: function(map) { - var maps = this.view.keyMaps; + var maps = this.state.keyMaps; for (var i = 0; i < maps.length; ++i) if ((typeof map == "string" ? maps[i].name : maps[i]) == map) { maps.splice(i, 1); @@ -2427,84 +2429,42 @@ window.CodeMirror = (function() { addOverlay: operation(null, function(spec, options) { var mode = spec.token ? spec : CodeMirror.getMode(this.options, spec); if (mode.startState) throw new Error("Overlays may not be stateful."); - this.view.overlays.push({mode: mode, modeSpec: spec, opaque: options && options.opaque}); - this.view.modeGen++; - regChange(this, 0, this.view.doc.size); + this.state.overlays.push({mode: mode, modeSpec: spec, opaque: options && options.opaque}); + this.state.modeGen++; + regChange(this); }), removeOverlay: operation(null, function(spec) { - var overlays = this.view.overlays; + var overlays = this.state.overlays; for (var i = 0; i < overlays.length; ++i) { if (overlays[i].modeSpec == spec) { overlays.splice(i, 1); - this.view.modeGen++; - regChange(this, 0, this.view.doc.size); + this.state.modeGen++; + regChange(this); return; } } }), - undo: operation(null, function() {unredoHelper(this, "undo");}), - redo: operation(null, function() {unredoHelper(this, "redo");}), - indentLine: operation(null, function(n, dir, aggressive) { if (typeof dir != "string") { if (dir == null) dir = this.options.smartIndent ? "smart" : "prev"; else dir = dir ? "add" : "subtract"; } - if (isLine(this.view.doc, n)) indentLine(this, n, dir, aggressive); + if (isLine(this.doc, n)) indentLine(this, n, dir, aggressive); }), - indentSelection: operation(null, function(how) { - var sel = this.view.sel; + var sel = this.doc.sel; if (posEq(sel.from, sel.to)) return indentLine(this, sel.from.line, how); var e = sel.to.line - (sel.to.ch ? 0 : 1); for (var i = sel.from.line; i <= e; ++i) indentLine(this, i, how); }), - historySize: function() { - var hist = this.view.history; - return {undo: hist.done.length, redo: hist.undone.length}; - }, - - clearHistory: function() {this.view.history = makeHistory();}, - - markClean: function() { - this.view.history.dirtyCounter = 0; - this.view.history.lastOp = this.view.history.lastOrigin = null; - }, - - isClean: function () {return this.view.history.dirtyCounter == 0;}, - - getHistory: function() { - var hist = this.view.history; - function cp(arr) { - for (var i = 0, nw = [], nwelt; i < arr.length; ++i) { - var set = arr[i]; - nw.push({events: nwelt = [], fromBefore: set.fromBefore, toBefore: set.toBefore, - fromAfter: set.fromAfter, toAfter: set.toAfter}); - for (var j = 0, elt = set.events; j < elt.length; ++j) { - var old = [], cur = elt[j]; - nwelt.push({start: cur.start, added: cur.added, old: old}); - for (var k = 0; k < cur.old.length; ++k) old.push(hlText(cur.old[k])); - } - } - return nw; - } - return {done: cp(hist.done), undone: cp(hist.undone)}; - }, - - setHistory: function(histData) { - var hist = this.view.history = makeHistory(); - hist.done = histData.done; - hist.undone = histData.undone; - }, - // Fetch the parser token for a given character. Useful for hacks // that want to inspect the mode state (say, for completion). getTokenAt: function(pos) { - var doc = this.view.doc; + var doc = this.doc; pos = clipPos(doc, pos); - var state = getStateBefore(this, pos.line), mode = this.view.mode; + var state = getStateBefore(this, pos.line), mode = this.doc.mode; var line = getLine(doc, pos.line); var stream = new StringStream(line.text, this.options.tabSize); while (stream.pos < pos.ch && !stream.eol()) { @@ -2520,21 +2480,21 @@ window.CodeMirror = (function() { }, getStateAfter: function(line) { - var doc = this.view.doc; - line = clipLine(doc, line == null ? doc.size - 1: line); + var doc = this.doc; + line = clipLine(doc, line == null ? doc.first + doc.size - 1: line); return getStateBefore(this, line + 1); }, cursorCoords: function(start, mode) { - var pos, sel = this.view.sel; + var pos, sel = this.doc.sel; if (start == null) pos = sel.head; - else if (typeof start == "object") pos = clipPos(this.view.doc, start); + else if (typeof start == "object") pos = clipPos(this.doc, start); else pos = start ? sel.from : sel.to; return cursorCoords(this, pos, mode || "page"); }, charCoords: function(pos, mode) { - return charCoords(this, clipPos(this.view.doc, pos), mode || "page"); + return charCoords(this, clipPos(this.doc, pos), mode || "page"); }, coordsChar: function(coords) { @@ -2544,29 +2504,6 @@ window.CodeMirror = (function() { defaultTextHeight: function() { return textHeight(this.display); }, - markText: operation(null, function(from, to, options) { - return markText(this, clipPos(this.view.doc, from), clipPos(this.view.doc, to), - options, "range"); - }), - - setBookmark: operation(null, function(pos, widget) { - pos = clipPos(this.view.doc, pos); - return markText(this, pos, pos, widget ? {replacedWith: widget} : {}, "bookmark"); - }), - - findMarksAt: function(pos) { - var doc = this.view.doc; - pos = clipPos(doc, pos); - var markers = [], spans = getLine(doc, pos.line).markedSpans; - if (spans) for (var i = 0; i < spans.length; ++i) { - var span = spans[i]; - if ((span.from == null || span.from <= pos.ch) && - (span.to == null || span.to >= pos.ch)) - markers.push(span.marker); - } - return markers; - }, - setGutterMarker: operation(null, function(line, gutterID, value) { return changeLine(this, line, function(line) { var markers = line.gutterMarkers || (line.gutterMarkers = {}); @@ -2577,8 +2514,8 @@ window.CodeMirror = (function() { }), clearGutter: operation(null, function(gutterID) { - var i = 0, cm = this, doc = cm.view.doc; - doc.iter(0, doc.size, function(line) { + var cm = this, doc = cm.doc, i = doc.first; + doc.iter(function(line) { if (line.gutterMarkers && line.gutterMarkers[gutterID]) { line.gutterMarkers[gutterID] = null; regChange(cm, i, i + 1); @@ -2621,9 +2558,9 @@ window.CodeMirror = (function() { lineInfo: function(line) { if (typeof line == "number") { - if (!isLine(this.view.doc, line)) return null; + if (!isLine(this.doc, line)) return null; var n = line; - line = getLine(this.view.doc, line); + line = getLine(this.doc, line); if (!line) return null; } else { var n = lineNo(line); @@ -2638,13 +2575,13 @@ window.CodeMirror = (function() { addWidget: function(pos, node, scroll, vert, horiz) { var display = this.display; - pos = cursorCoords(this, clipPos(this.view.doc, pos)); + pos = cursorCoords(this, clipPos(this.doc, pos)); var top = pos.top, left = pos.left; node.style.position = "absolute"; display.sizer.appendChild(node); if (vert == "over") top = pos.top; else if (vert == "near") { - var vspace = Math.max(display.wrapper.clientHeight, this.view.doc.height), + var vspace = Math.max(display.wrapper.clientHeight, this.doc.height), hspace = Math.max(display.sizer.clientWidth, display.lineSpace.clientWidth); if (pos.bottom + node.offsetHeight > vspace && pos.top > node.offsetHeight) top = pos.top - node.offsetHeight; @@ -2665,100 +2602,30 @@ window.CodeMirror = (function() { scrollIntoView(this, left, top, left + node.offsetWidth, top + node.offsetHeight); }, - lineCount: function() {return this.view.doc.size;}, - - clipPos: function(pos) {return clipPos(this.view.doc, pos);}, - - getCursor: function(start) { - var sel = this.view.sel, pos; - if (start == null || start == "head") pos = sel.head; - else if (start == "anchor") pos = sel.anchor; - else if (start == "end" || start === false) pos = sel.to; - else pos = sel.from; - return copyPos(pos); - }, - - somethingSelected: function() {return !posEq(this.view.sel.from, this.view.sel.to);}, - - setCursor: operation(null, function(line, ch, extend) { - var pos = clipPos(this.view.doc, typeof line == "number" ? {line: line, ch: ch || 0} : line); - if (extend) extendSelection(this, pos); - else setSelection(this, pos, pos); - }), - - setSelection: operation(null, function(anchor, head) { - var doc = this.view.doc; - setSelection(this, clipPos(doc, anchor), clipPos(doc, head || anchor)); - }), - - extendSelection: operation(null, function(from, to) { - var doc = this.view.doc; - extendSelection(this, clipPos(doc, from), to && clipPos(doc, to)); - }), - - setExtending: function(val) {this.view.sel.extend = val;}, - - getLine: function(line) {var l = this.getLineHandle(line); return l && l.text;}, - - getLineHandle: function(line) { - var doc = this.view.doc; - if (isLine(doc, line)) return getLine(doc, line); - }, - - getLineNumber: function(line) {return lineNo(line);}, - - setLine: operation(null, function(line, text) { - if (isLine(this.view.doc, line)) - replaceRange(this, text, {line: line, ch: 0}, {line: line, ch: getLine(this.view.doc, line).text.length}); - }), - - removeLine: operation(null, function(line) { - if (isLine(this.view.doc, line)) - replaceRange(this, "", {line: line, ch: 0}, clipPos(this.view.doc, {line: line+1, ch: 0})); - }), - - replaceRange: operation(null, function(code, from, to) { - var doc = this.view.doc; - from = clipPos(doc, from); - to = to ? clipPos(doc, to) : from; - return replaceRange(this, code, from, to); - }), - - getRange: function(from, to, lineSep) { - var doc = this.view.doc; - from = clipPos(doc, from); to = clipPos(doc, to); - var l1 = from.line, l2 = to.line; - if (l1 == l2) return getLine(doc, l1).text.slice(from.ch, to.ch); - var code = [getLine(doc, l1).text.slice(from.ch)]; - doc.iter(l1 + 1, l2, function(line) { code.push(line.text); }); - code.push(getLine(doc, l2).text.slice(0, to.ch)); - return code.join(lineSep || "\n"); - }, - triggerOnKeyDown: operation(null, onKeyDown), execCommand: function(cmd) {return commands[cmd](this);}, // Stuff used by commands, probably not much use to outside code. moveH: operation(null, function(dir, unit) { - var sel = this.view.sel, pos = dir < 0 ? sel.from : sel.to; + var sel = this.doc.sel, pos = dir < 0 ? sel.from : sel.to; if (sel.shift || sel.extend || posEq(sel.from, sel.to)) - pos = findPosH(this, dir, unit, this.options.rtlMoveVisually); - extendSelection(this, pos, pos, dir); + pos = findPosH(this.doc, dir, unit, this.options.rtlMoveVisually); + extendSelection(this.doc, pos, pos, dir); }), deleteH: operation(null, function(dir, unit) { - var sel = this.view.sel; - if (!posEq(sel.from, sel.to)) replaceRange(this, "", sel.from, sel.to, "delete"); - else replaceRange(this, "", sel.from, findPosH(this, dir, unit, false), "delete"); + var sel = this.doc.sel; + if (!posEq(sel.from, sel.to)) replaceRange(this.doc, "", sel.from, sel.to, "delete"); + else replaceRange(this.doc, "", sel.from, findPosH(this.doc, dir, unit, false), "delete"); this.curOp.userSelChange = true; }), moveV: operation(null, function(dir, unit) { - var view = this.view, doc = view.doc, display = this.display; - var cur = view.sel.head, pos = cursorCoords(this, cur, "div"); + var doc = this.doc, display = this.display; + var cur = doc.sel.head, pos = cursorCoords(this, cur, "div"); var x = pos.left, y; - if (view.goalColumn != null) x = view.goalColumn; + if (doc.sel.goalColumn != null) x = doc.sel.goalColumn; if (unit == "page") { var pageSize = Math.min(display.wrapper.clientHeight, window.innerHeight || document.documentElement.clientHeight); y = pos.top + dir * pageSize; @@ -2771,36 +2638,17 @@ window.CodeMirror = (function() { } while (target.outside && (dir < 0 ? y > 0 : y < doc.height)); if (unit == "page") display.scrollbarV.scrollTop += charCoords(this, target, "div").top - pos.top; - extendSelection(this, target, target, dir); - view.goalColumn = x; + extendSelection(this.doc, target, target, dir); + doc.sel.goalColumn = x; }), toggleOverwrite: function() { - if (this.view.overwrite = !this.view.overwrite) + if (this.state.overwrite = !this.state.overwrite) this.display.cursor.className += " CodeMirror-overwrite"; else this.display.cursor.className = this.display.cursor.className.replace(" CodeMirror-overwrite", ""); }, - posFromIndex: function(off) { - var lineNo = 0, ch, doc = this.view.doc; - doc.iter(0, doc.size, function(line) { - var sz = line.text.length + 1; - if (sz > off) { ch = off; return true; } - off -= sz; - ++lineNo; - }); - return clipPos(doc, {line: lineNo, ch: ch}); - }, - indexFromPos: function (coords) { - coords = clipPos(this.view.doc, coords); - var index = coords.ch; - this.view.doc.iter(0, coords.line, function (line) { - index += line.text.length + 1; - }); - return index; - }, - scrollTo: function(x, y) { if (x != null) this.display.scrollbarH.scrollLeft = this.display.scroller.scrollLeft = x; if (y != null) this.display.scrollbarV.scrollTop = this.display.scroller.scrollTop = y; @@ -2816,7 +2664,7 @@ window.CodeMirror = (function() { scrollIntoView: function(pos) { if (typeof pos == "number") pos = {line: pos, ch: 0}; if (!pos || pos.line != null) { - pos = pos ? clipPos(this.view.doc, pos) : this.view.sel.head; + pos = pos ? clipPos(this.doc, pos) : this.doc.sel.head; scrollPosIntoView(this, pos); } else { scrollIntoView(this, pos.left, pos.top, pos.right, pos.bottom); @@ -2835,11 +2683,11 @@ window.CodeMirror = (function() { on: function(type, f) {on(this, type, f);}, off: function(type, f) {off(this, type, f);}, - operation: function(f){return operation(this, f)();}, + operation: function(f){return runInOp(this, f);}, refresh: function() { clearCaches(this); - var sTop = this.view.scrollTop, sLeft = this.view.scrollLeft; + var sTop = this.doc.scrollTop, sLeft = this.doc.scrollLeft; if (this.display.scroller.scrollHeight > sTop) this.display.scrollbarV.scrollTop = this.display.scroller.scrollTop = sTop; if (this.display.scroller.scrollWidth > sLeft) @@ -2847,6 +2695,15 @@ window.CodeMirror = (function() { updateDisplay(this, true); }, + swapDoc: operation(null, function(doc) { + var old = this.doc; + old.cm = null; + attachDoc(this, doc); + clearCaches(this); + this.curOp.updateScrollPos = true; + return old; + }), + getInputField: function(){return this.display.input;}, getWrapperElement: function(){return this.display.wrapper;}, getScrollerElement: function(){return this.display.scroller;}, @@ -2870,8 +2727,13 @@ window.CodeMirror = (function() { // These two are, on init, called from the constructor because they // have to be initialized before the editor can start at all. - option("value", "", function(cm, val) {cm.setValue(val);}, true); - option("mode", null, loadMode, true); + option("value", "", function(cm, val) { + cm.setValue(val); + }, true); + option("mode", null, function(cm, val) { + cm.doc.modeOption = val; + loadMode(cm); + }, true); option("indentUnit", 2, loadMode, true); option("indentWithTabs", false); @@ -2923,7 +2785,7 @@ window.CodeMirror = (function() { option("workDelay", 100); option("flattenSpans", true); option("pollInterval", 100); - option("undoDepth", 40); + option("undoDepth", 40, function(cm, val){cm.doc.history.undoDepth = val;}); option("viewportMargin", 10, function(cm){cm.refresh();}, true); option("tabindex", null, function(cm, val) { @@ -3032,7 +2894,7 @@ window.CodeMirror = (function() { // STANDARD COMMANDS var commands = CodeMirror.commands = { - selectAll: function(cm) {cm.setSelection({line: 0, ch: 0}, {line: cm.lineCount() - 1});}, + selectAll: function(cm) {cm.setSelection({line: cm.firstLine(), ch: 0}, {line: cm.lastLine()});}, killLine: function(cm) { var from = cm.getCursor(true), to = cm.getCursor(false), sel = !posEq(from, to); if (!sel && cm.getLine(from.line).length == from.ch) @@ -3045,8 +2907,8 @@ window.CodeMirror = (function() { }, undo: function(cm) {cm.undo();}, redo: function(cm) {cm.redo();}, - goDocStart: function(cm) {cm.extendSelection({line: 0, ch: 0});}, - goDocEnd: function(cm) {cm.extendSelection({line: cm.lineCount() - 1});}, + goDocStart: function(cm) {cm.extendSelection({line: cm.firstLine(), ch: 0});}, + goDocEnd: function(cm) {cm.extendSelection({line: cm.lastLine()});}, goLineStart: function(cm) { cm.extendSelection(lineStart(cm, cm.getCursor().line)); }, @@ -3291,17 +3153,18 @@ window.CodeMirror = (function() { // TEXTMARKERS - function TextMarker(cm, type) { + function TextMarker(doc, type) { this.lines = []; this.type = type; - this.cm = cm; + this.doc = doc; } CodeMirror.TextMarker = TextMarker; TextMarker.prototype.clear = function() { if (this.explicitlyCleared) return; - startOperation(this.cm); - var view = this.cm.view, min = null, max = null; + var cm = this.doc.cm, mustStart = cm && !cm.curOp; + if (mustStart) startOperation(cm); + var min = null, max = null; for (var i = 0; i < this.lines.length; ++i) { var line = this.lines[i]; var span = getMarkedSpanFor(line.markedSpans, this); @@ -3309,27 +3172,27 @@ window.CodeMirror = (function() { line.markedSpans = removeMarkedSpan(line.markedSpans, span); if (span.from != null) min = lineNo(line); - else if (this.collapsed && !lineIsHidden(line)) - updateLineHeight(line, textHeight(this.cm.display)); - } - if (this.collapsed && !this.cm.options.lineWrapping) for (var i = 0; i < this.lines.length; ++i) { - var visual = visualLine(view.doc, this.lines[i]), len = lineLength(view.doc, visual); - if (len > view.maxLineLength) { - view.maxLine = visual; - view.maxLineLength = len; - view.maxLineChanged = true; + else if (this.collapsed && !lineIsHidden(line) && cm) + updateLineHeight(line, textHeight(cm.display)); + } + if (cm && this.collapsed && !cm.options.lineWrapping) for (var i = 0; i < this.lines.length; ++i) { + var visual = visualLine(cm.doc, this.lines[i]), len = lineLength(cm.doc, visual); + if (len > cm.display.maxLineLength) { + cm.display.maxLine = visual; + cm.display.maxLineLength = len; + cm.display.maxLineChanged = true; } } - if (min != null) regChange(this.cm, min, max + 1); + if (min != null && cm) regChange(cm, min, max + 1); this.lines.length = 0; this.explicitlyCleared = true; - if (this.collapsed && this.cm.view.cantEdit) { - this.cm.view.cantEdit = false; - reCheckSelection(this.cm); + if (this.collapsed && this.doc.cantEdit) { + this.doc.cantEdit = false; + if (cm) reCheckSelection(cm); } - endOperation(this.cm); - signalLater(this.cm, this, "clear"); + if (mustStart) endOperation(cm); + signalLater(this, "clear"); }; TextMarker.prototype.find = function() { @@ -3359,9 +3222,8 @@ window.CodeMirror = (function() { startStyle: this.startStyle, endStyle: this.endStyle}; }; - function markText(cm, from, to, options, type) { - var doc = cm.view.doc; - var marker = new TextMarker(cm, type); + function markText(doc, from, to, options, type) { + var marker = new TextMarker(doc, type); if (type == "range" && !posLess(from, to)) return marker; if (options) for (var opt in options) if (options.hasOwnProperty(opt)) marker[opt] = options[opt]; @@ -3371,10 +3233,10 @@ window.CodeMirror = (function() { } if (marker.collapsed) sawCollapsedSpans = true; - var curLine = from.line, size = 0, collapsedAtStart, collapsedAtEnd; + var curLine = from.line, size = 0, collapsedAtStart, collapsedAtEnd, cm = doc.cm, updateMaxLine; doc.iter(curLine, to.line + 1, function(line) { - if (marker.collapsed && !cm.options.lineWrapping && visualLine(doc, line) == cm.view.maxLine) - cm.curOp.updateMaxLine = true; + if (cm && marker.collapsed && !cm.options.lineWrapping && visualLine(doc, line) == cm.display.maxLine) + updateMaxLine = true; var span = {from: null, to: null, marker: marker}; size += line.text.length; if (curLine == from.line) {span.from = from.ch; size -= from.ch;} @@ -3393,8 +3255,8 @@ window.CodeMirror = (function() { if (marker.readOnly) { sawReadOnlySpans = true; - if (cm.view.history.done.length || cm.view.history.undone.length) - cm.clearHistory(); + if (doc.history.done.length || doc.history.undone.length) + doc.clearHistory(); } if (marker.collapsed) { if (collapsedAtStart != collapsedAtEnd) @@ -3402,9 +3264,12 @@ window.CodeMirror = (function() { marker.size = size; marker.atomic = true; } - if (marker.className || marker.startStyle || marker.endStyle || marker.collapsed) - regChange(cm, from.line, to.line + 1); - if (marker.atomic) reCheckSelection(cm); + if (cm) runInOp(cm, function() { + if (updateMaxLine) cm.curOp.updateMaxLine = true; + if (marker.className || marker.startStyle || marker.endStyle || marker.collapsed) + regChange(cm, from.line, to.line + 1); + if (marker.atomic) reCheckSelection(cm); + }); return marker; } @@ -3454,14 +3319,18 @@ window.CodeMirror = (function() { return nw; } - function updateMarkedSpans(oldFirst, oldLast, startCh, endCh, newText) { - if (!oldFirst && !oldLast) return newText; + function stretchSpansOverChange(doc, change) { + var oldFirst = isLine(doc, change.from.line) && getLine(doc, change.from.line).markedSpans; + var oldLast = isLine(doc, change.to.line) && getLine(doc, change.to.line).markedSpans; + if (!oldFirst && !oldLast) return null; + + var startCh = change.from.ch, endCh = change.to.ch; // Get the spans that 'stick out' on both sides var first = markedSpansBefore(oldFirst, startCh); var last = markedSpansAfter(oldLast, startCh, endCh); // Next, merge those two ends - var sameLine = newText.length == 1, offset = lst(newText).length + (sameLine ? startCh : 0); + var sameLine = change.text.length == 1, offset = lst(change.text).length + (sameLine ? startCh : 0); if (first) { // Fix up .to properties of first for (var i = 0; i < first.length; ++i) { @@ -3491,17 +3360,17 @@ window.CodeMirror = (function() { } } - var newMarkers = [newHL(newText[0], first)]; + var newMarkers = [first]; if (!sameLine) { // Fill gap with whole-line-spans - var gap = newText.length - 2, gapMarkers; + var gap = change.text.length - 2, gapMarkers; if (gap > 0 && first) for (var i = 0; i < first.length; ++i) if (first[i].to == null) (gapMarkers || (gapMarkers = [])).push({from: null, to: null, marker: first[i].marker}); for (var i = 0; i < gap; ++i) - newMarkers.push(newHL(newText[i+1], gapMarkers)); - newMarkers.push(newHL(lst(newText), last)); + newMarkers.push(gapMarkers); + newMarkers.push(last); } return newMarkers; } @@ -3579,20 +3448,6 @@ window.CodeMirror = (function() { } } - // hl stands for history-line, a data structure that can be either a - // string (line without markers) or a {text, markedSpans} object. - function hlText(val) { return typeof val == "string" ? val : val.text; } - function hlSpans(val) { - if (typeof val == "string") return null; - var spans = val.markedSpans, out = null; - for (var i = 0; i < spans.length; ++i) { - if (spans[i].marker.explicitlyCleared) { if (!out) out = spans.slice(0, i); } - else if (out) out.push(spans[i]); - } - return !out ? spans : out.length ? out : null; - } - function newHL(text, spans) { return spans ? {text: text, markedSpans: spans} : text; } - function detachMarkedSpans(line) { var spans = line.markedSpans; if (!spans) return; @@ -3672,23 +3527,23 @@ window.CodeMirror = (function() { // Line objects. These hold state related to a line, including // highlighting info (the styles array). - function makeLine(text, markedSpans, height) { - var line = {text: text, height: height}; + function makeLine(text, markedSpans, estimateHeight) { + var line = {text: text}; attachMarkedSpans(line, markedSpans); - if (lineIsHidden(line)) line.height = 0; + line.height = estimateHeight ? estimateHeight(line) : 1; return line; } - function updateLine(cm, line, text, markedSpans) { + function updateLine(line, text, markedSpans, estimateHeight) { line.text = text; if (line.stateAfter) line.stateAfter = null; if (line.styles) line.styles = null; if (line.order != null) line.order = null; detachMarkedSpans(line); attachMarkedSpans(line, markedSpans); - if (lineIsHidden(line)) line.height = 0; - else if (!line.height) line.height = textHeight(cm.display); - signalLater(cm, line, "change"); + var estHeight = estimateHeight ? estimateHeight(line) : 1; + if (estHeight != line.height) updateLineHeight(line, estHeight); + signalLater(line, "change"); } function cleanUpLine(line) { @@ -3725,13 +3580,13 @@ window.CodeMirror = (function() { function highlightLine(cm, line, state) { // A styles array always starts with a number identifying the // mode/overlays that it is based on (for easy invalidation). - var st = [cm.view.modeGen]; + var st = [cm.state.modeGen]; // Compute the base array of styles - runMode(cm, line.text, cm.view.mode, state, function(txt, style) {st.push(txt, style);}); + runMode(cm, line.text, cm.doc.mode, state, function(txt, style) {st.push(txt, style);}); // Run overlays, adjust style array. - for (var o = 0; o < cm.view.overlays.length; ++o) { - var overlay = cm.view.overlays[o], i = 1; + for (var o = 0; o < cm.state.overlays.length; ++o) { + var overlay = cm.state.overlays[o], i = 1; runMode(cm, line.text, overlay.mode, true, function(txt, style) { var start = i, len = txt.length; // Ensure there's a token end at the current position, and that i points at it @@ -3762,7 +3617,7 @@ window.CodeMirror = (function() { } function getLineStyles(cm, line) { - if (!line.styles || line.styles[0] != cm.view.modeGen) + if (!line.styles || line.styles[0] != cm.state.modeGen) line.styles = highlightLine(cm, line, line.stateAfter = getStateBefore(cm, lineNo(line))); return line.styles; } @@ -3770,7 +3625,7 @@ window.CodeMirror = (function() { // Lightweight form of highlight -- proceed over this line and // update state, but don't save a style array. function processLine(cm, line, state) { - var mode = cm.view.mode; + var mode = cm.doc.mode; var stream = new StringStream(line.text, cm.options.tabSize); if (line.text == "" && mode.blankLine) mode.blankLine(state); while (!stream.eol() && stream.pos <= 5000) { @@ -3790,7 +3645,7 @@ window.CodeMirror = (function() { var merged, line = realLine, lineBefore, sawBefore, simple = true; while (merged = collapsedSpanAtStart(line)) { simple = false; - line = getLine(cm.view.doc, merged.find().from.line); + line = getLine(cm.doc, merged.find().from.line); if (!lineBefore) lineBefore = line; } @@ -3809,7 +3664,7 @@ window.CodeMirror = (function() { var next = insertLineContent(line, builder, getLineStyles(cm, line)); sawBefore = line == lineBefore; if (next) { - line = getLine(cm.view.doc, next.to.line); + line = getLine(cm.doc, next.to.line); simple = false; } } while (next); @@ -3948,6 +3803,49 @@ window.CodeMirror = (function() { // DOCUMENT DATA STRUCTURE + function updateDoc(doc, change, markedSpans, selAfter, estimateHeight) { + function spansFor(n) {return markedSpans ? markedSpans[n] : null;} + + var from = change.from, to = change.to, text = change.text; + var firstLine = getLine(doc, from.line), lastLine = getLine(doc, to.line); + var lastText = lst(text), lastSpans = spansFor(text.length - 1), nlines = to.line - from.line; + + // First adjust the line structure + if (from.ch == 0 && to.ch == 0 && lastText == "") { + // This is a whole-line replace. Treated specially to make + // sure line objects move the way they are supposed to. + for (var i = 0, e = text.length - 1, added = []; i < e; ++i) + added.push(makeLine(text[i], spansFor(i), estimateHeight)); + updateLine(lastLine, lastLine.text, lastSpans, estimateHeight); + if (nlines) doc.remove(from.line, nlines); + if (added.length) doc.insert(from.line, added); + } else if (firstLine == lastLine) { + if (text.length == 1) { + updateLine(firstLine, firstLine.text.slice(0, from.ch) + lastText + firstLine.text.slice(to.ch), + lastSpans, estimateHeight); + } else { + for (var added = [], i = 1, e = text.length - 1; i < e; ++i) + added.push(makeLine(text[i], spansFor(i), estimateHeight)); + added.push(makeLine(lastText + firstLine.text.slice(to.ch), lastSpans, estimateHeight)); + updateLine(firstLine, firstLine.text.slice(0, from.ch) + text[0], spansFor(0), estimateHeight); + doc.insert(from.line + 1, added); + } + } else if (text.length == 1) { + updateLine(firstLine, firstLine.text.slice(0, from.ch) + text[0] + lastLine.text.slice(to.ch), + spansFor(0), estimateHeight); + doc.remove(from.line + 1, nlines); + } else { + updateLine(firstLine, firstLine.text.slice(0, from.ch) + text[0], spansFor(0), estimateHeight); + updateLine(lastLine, lastText + lastLine.text.slice(to.ch), lastSpans, estimateHeight); + for (var i = 1, e = text.length - 1, added = []; i < e; ++i) + added.push(makeLine(text[i], spansFor(i), estimateHeight)); + if (nlines > 1) doc.remove(from.line + 1, nlines - 1); + doc.insert(from.line + 1, added); + } + + setSelection(doc, selAfter.anchor, selAfter.head, null, true); + } + function LeafChunk(lines) { this.lines = lines; this.parent = null; @@ -3960,19 +3858,19 @@ window.CodeMirror = (function() { LeafChunk.prototype = { chunkSize: function() { return this.lines.length; }, - remove: function(at, n, cm) { + removeInner: function(at, n) { for (var i = at, e = at + n; i < e; ++i) { var line = this.lines[i]; this.height -= line.height; cleanUpLine(line); - signalLater(cm, line, "delete"); + signalLater(line, "delete"); } this.lines.splice(at, n); }, collapse: function(lines) { lines.splice.apply(lines, [lines.length, 0].concat(this.lines)); }, - insertHeight: function(at, lines, height) { + insertInner: function(at, lines, height) { this.height += height; this.lines = this.lines.slice(0, at).concat(lines).concat(this.lines.slice(at)); for (var i = 0, e = lines.length; i < e; ++i) lines[i].parent = this; @@ -3998,13 +3896,13 @@ window.CodeMirror = (function() { BranchChunk.prototype = { chunkSize: function() { return this.size; }, - remove: function(at, n, callbacks) { + removeInner: function(at, n) { this.size -= n; for (var i = 0; i < this.children.length; ++i) { var child = this.children[i], sz = child.chunkSize(); if (at < sz) { var rm = Math.min(n, sz - at), oldHeight = child.height; - child.remove(at, rm, callbacks); + child.removeInner(at, rm); this.height -= oldHeight - child.height; if (sz == rm) { this.children.splice(i--, 1); child.parent = null; } if ((n -= rm) == 0) break; @@ -4021,18 +3919,13 @@ window.CodeMirror = (function() { collapse: function(lines) { for (var i = 0, e = this.children.length; i < e; ++i) this.children[i].collapse(lines); }, - insert: function(at, lines) { - var height = 0; - for (var i = 0, e = lines.length; i < e; ++i) height += lines[i].height; - this.insertHeight(at, lines, height); - }, - insertHeight: function(at, lines, height) { + insertInner: function(at, lines, height) { this.size += lines.length; this.height += height; for (var i = 0, e = this.children.length; i < e; ++i) { var child = this.children[i], sz = child.chunkSize(); if (at <= sz) { - child.insertHeight(at, lines, height); + child.insertInner(at, lines, height); if (child.lines && child.lines.length > 50) { while (child.lines.length > 50) { var spilled = child.lines.splice(child.lines.length - 25, 25); @@ -4069,7 +3962,6 @@ window.CodeMirror = (function() { } while (me.children.length > 10); me.parent.maybeSpill(); }, - iter: function(from, to, op) { this.iterN(from, to - from, op); }, iterN: function(at, n, op) { for (var i = 0, e = this.children.length; i < e; ++i) { var child = this.children[i], sz = child.chunkSize(); @@ -4083,9 +3975,257 @@ window.CodeMirror = (function() { } }; + var nextDocId = 0; + var Doc = CodeMirror.Doc = function Doc(text, mode, firstLine) { + if (!(this instanceof Doc)) return new Doc(text, mode, firstLine); + if (firstLine == null) firstLine = 0; + + BranchChunk.call(this, [new LeafChunk([makeLine("", null)])]); + this.first = firstLine; + this.scrollTop = this.scrollLeft = 0; + this.cantEdit = false; + this.history = makeHistory(); + this.frontier = firstLine; + var start = {line: firstLine, ch: 0}; + this.sel = {from: start, to: start, head: start, anchor: start, shift: false, extend: false, goalColumn: null}; + this.id = ++nextDocId; + this.modeOption = mode; + + if (typeof text == "string") text = splitLines(text); + updateDoc(this, {from: start, to: start, text: text}, null, {head: start, anchor: start}); + }; + + Doc.prototype = createObj(BranchChunk.prototype, { + iter: function(from, to, op) { + if (op) this.iterN(from - this.first, to - (from - this.first), op); + else this.iterN(this.first, this.first + this.size, from); + }, + + insert: function(at, lines) { + var height = 0; + for (var i = 0, e = lines.length; i < e; ++i) height += lines[i].height; + this.insertInner(at - this.first, lines, height); + }, + remove: function(at, n) { this.removeInner(at - this.first, n); }, + + getValue: function(lineSep) { + var lines = getLines(this, this.first, this.first + this.size); + if (lineSep === false) return lines; + return lines.join(lineSep || "\n"); + }, + setValue: function(code) { + var top = {line: this.first, ch: 0}, last = this.first + this.size - 1; + makeChange(this, {from: top, to: {line: last, ch: getLine(this, last).text.length}, + text: splitLines(code), origin: "setValue"}, + {head: top, anchor: top}, true); + }, + replaceRange: function(code, from, to) { + from = clipPos(this, from); + to = to ? clipPos(this, to) : from; + replaceRange(this, code, from, to); + }, + getRange: function(from, to, lineSep) { + var lines = getBetween(this, clipPos(this, from), clipPos(this, to)); + if (lineSep === false) return lines; + return lines.join(lineSep || "\n"); + }, + + getLine: function(line) {var l = this.getLineHandle(line); return l && l.text;}, + setLine: function(line, text) { + if (isLine(this, line)) + replaceRange(this, text, {line: line, ch: 0}, {line: line, ch: getLine(this, line).text.length}); + }, + removeLine: function(line) { + if (isLine(this, line)) + replaceRange(this, "", {line: line, ch: 0}, clipPos(this, {line: line+1, ch: 0})); + }, + + getLineHandle: function(line) {if (isLine(this, line)) return getLine(this, line);}, + getLineNumber: function(line) {return lineNo(line);}, + + lineCount: function() {return this.size;}, + firstLine: function() {return this.first;}, + lastLine: function() {return this.first + this.size - 1;}, + + clipPos: function(pos) {return clipPos(this, pos);}, + + getCursor: function(start) { + var sel = this.sel, pos; + if (start == null || start == "head") pos = sel.head; + else if (start == "anchor") pos = sel.anchor; + else if (start == "end" || start === false) pos = sel.to; + else pos = sel.from; + return copyPos(pos); + }, + somethingSelected: function() {return !posEq(this.sel.head, this.sel.anchor);}, + + setCursor: docOperation(function(line, ch, extend) { + var pos = clipPos(this, typeof line == "number" ? {line: line, ch: ch || 0} : line); + if (extend) extendSelection(this, pos); + else setSelection(this, pos, pos); + }), + setSelection: docOperation(function(anchor, head) { + setSelection(this, clipPos(this, anchor), clipPos(this, head || anchor)); + }), + extendSelection: docOperation(function(from, to) { + extendSelection(this, clipPos(this, from), to && clipPos(this, to)); + }), + + getSelection: function(lineSep) {return this.getRange(this.sel.from, this.sel.to, lineSep);}, + replaceSelection: function(code, collapse, origin) { + makeChange(this, {from: this.sel.from, to: this.sel.to, text: splitLines(code), origin: origin}, collapse || "around"); + }, + undo: function() {makeChangeFromHistory(this, "undo");}, + redo: function() {makeChangeFromHistory(this, "redo");}, + + setExtending: function(val) {this.sel.extend = val;}, + + historySize: function() { + var hist = this.history; + return {undo: hist.done.length, redo: hist.undone.length}; + }, + clearHistory: function() {this.history = makeHistory();}, + + markClean: function() { + this.history.dirtyCounter = 0; + this.history.lastOp = this.history.lastOrigin = null; + }, + isClean: function () {return this.history.dirtyCounter == 0;}, + + getHistory: function() { + return {done: copyHistoryArray(this.history.done), + undone: copyHistoryArray(this.history.undone)}; + }, + setHistory: function(histData) { + var hist = this.history = makeHistory(); + hist.done = histData.done.slice(0); + hist.undone = histData.undone.slice(0); + }, + + markText: function(from, to, options) { + return markText(this, clipPos(this, from), clipPos(this, to), options, "range"); + }, + setBookmark: function(pos, widget) { + pos = clipPos(this, pos); + return markText(this, pos, pos, widget ? {replacedWith: widget} : {}, "bookmark"); + }, + findMarksAt: function(pos) { + pos = clipPos(this, pos); + var markers = [], spans = getLine(this, pos.line).markedSpans; + if (spans) for (var i = 0; i < spans.length; ++i) { + var span = spans[i]; + if ((span.from == null || span.from <= pos.ch) && + (span.to == null || span.to >= pos.ch)) + markers.push(span.marker); + } + return markers; + }, + + posFromIndex: function(off) { + var ch, lineNo = this.first; + this.iter(function(line) { + var sz = line.text.length + 1; + if (sz > off) { ch = off; return true; } + off -= sz; + ++lineNo; + }); + return clipPos(this, {line: lineNo, ch: ch}); + }, + indexFromPos: function (coords) { + coords = clipPos(this, coords); + var index = coords.ch; + if (coords.line < this.first || coords.ch < 0) return 0; + this.iter(this.first, coords.line, function (line) { + index += line.text.length + 1; + }); + return index; + }, + + copy: function(copyHistory) { + var doc = new Doc(getLines(this, this.first, this.first + this.size), this.modeOption, this.first); + doc.scrollTop = this.scrollTop; doc.scrollLeft = this.scrollLeft; + doc.sel = {from: this.sel.from, to: this.sel.to, head: this.sel.head, anchor: this.sel.anchor, + shift: this.sel.shift, extend: false, goalColumn: this.sel.goalColumn}; + if (copyHistory) { + doc.history.undoDepth = this.history.undoDepth; + doc.setHistory(this.getHistory()); + } + return doc; + }, + + linkedDoc: function(options) { + if (!options) options = {}; + var from = this.first, to = this.first + this.size; + if (options.from != null && options.from > from) from = options.from; + if (options.to != null && options.to < to) to = options.to; + var copy = new Doc(getLines(this, from, to), options.mode || this.modeOption, from); + if (options.sharedHist) copy.history = this.history; + (this.linked || (this.linked = [])).push({doc: copy, sharedHist: options.sharedHist}); + copy.linked = [{doc: this, isParent: true, sharedHist: options.sharedHist}]; + return copy; + }, + unlinkDoc: function(other) { + if (other instanceof CodeMirror) other = other.doc; + if (this.linked) for (var i = 0; i < this.linked.length; ++i) { + var link = this.linked[i]; + if (link.doc != other) continue; + this.linked.splice(i, 1); + other.unlinkDoc(this); + break; + } + // If the histories were shared, split them again + if (other.history == this.history) { + var splitIds = [other.id]; + linkedDocs(other, function(doc) {splitIds.push(doc.id);}, true); + other.history = makeHistory(); + other.history.done = copyHistoryArray(this.history.done, splitIds); + other.history.undone = copyHistoryArray(this.history.undone, splitIds); + } + }, + iterLinkedDocs: function(f) {linkedDocs(this, f);}, + + getEditor: function() {return this.cm;} + }); + + // The Doc methods that should be available on CodeMirror instances + var toDelegate = ("setValue getValue getSelection replaceSelection undo redo historySize clearHistory markClean isClean " + + "getHistory setHistory markText setBookmark findMarksAt lineCount firstLine lastLine clipPos getCursor " + + "somethingSelected setCursor setSelection extendSelection setExtending getLine setLine removeLine " + + "replaceRange getRange getLineHandle getLineNumber posFromIndex indexFromPos linkedDoc unlinkDoc iterLinkedDocs").split(" "); + for (var i = 0; i < toDelegate.length; ++i) (function(method) { + var target = Doc.prototype[method]; + CodeMirror.prototype[method] = function() {return target.apply(this.doc, arguments);}; + })(toDelegate[i]); + + function linkedDocs(doc, f, sharedHistOnly) { + function propagate(doc, skip, sharedHist) { + if (doc.linked) for (var i = 0; i < doc.linked.length; ++i) { + var rel = doc.linked[i]; + if (rel.doc == skip) continue; + var shared = sharedHist && rel.sharedHist; + if (sharedHistOnly && !shared) continue; + f(rel.doc, shared); + propagate(rel.doc, doc, shared); + } + } + propagate(doc, null, true); + } + + function attachDoc(cm, doc) { + if (doc.cm) throw new Error("This document is already in use."); + cm.doc = doc; + doc.cm = cm; + estimateLineHeights(cm); + loadMode(cm); + if (!cm.options.lineWrapping) computeMaxLength(cm); + cm.options.mode = doc.modeOption; + regChange(cm); + } + // LINE UTILITIES function getLine(chunk, n) { + n -= chunk.first; while (!chunk.lines) { for (var i = 0;; ++i) { var child = chunk.children[i], sz = child.chunkSize(); @@ -4096,6 +4236,23 @@ window.CodeMirror = (function() { return chunk.lines[n]; } + function getBetween(doc, start, end) { + var out = [], n = start.line; + doc.iter(start.line, end.line + 1, function(line) { + var text = line.text; + if (n == end.line) text = text.slice(0, end.ch); + if (n == start.line) text = text.slice(start.ch); + out.push(text); + ++n; + }); + return out; + } + function getLines(doc, from, to) { + var out = []; + doc.iter(from, to, function(line) { out.push(line.text); }); + return out; + } + function updateLineHeight(line, height) { var diff = height - line.height; for (var n = line; n; n = n.parent) n.height += diff; @@ -4110,7 +4267,7 @@ window.CodeMirror = (function() { no += chunk.children[i].chunkSize(); } } - return no; + return no + cur.first; } function lineDoc(line) { @@ -4119,7 +4276,7 @@ window.CodeMirror = (function() { } function lineAtHeight(chunk, h) { - var n = 0; + var n = chunk.first; outer: do { for (var i = 0, e = chunk.children.length; i < e; ++i) { var child = chunk.children[i], ch = child.height; @@ -4138,7 +4295,7 @@ window.CodeMirror = (function() { } function heightAtLine(cm, lineObj) { - lineObj = visualLine(cm.view.doc, lineObj); + lineObj = visualLine(cm.doc, lineObj); var h = 0, chunk = lineObj.parent; for (var i = 0; i < chunk.lines.length; ++i) { @@ -4169,7 +4326,7 @@ window.CodeMirror = (function() { // Arrays of history events. Doing something adds an event to // done and clears undo. Undoing moves events from done to // undone, redoing moves them in the other direction. - done: [], undone: [], + done: [], undone: [], undoDepth: Infinity, // Used to track when changes can be merged into a single undo // event lastTime: 0, lastOp: null, lastOrigin: null, @@ -4178,47 +4335,157 @@ window.CodeMirror = (function() { }; } - function addChange(cm, start, added, old, origin, fromBefore, toBefore, fromAfter, toAfter) { - var history = cm.view.history; - history.undone.length = 0; - var time = +new Date, cur = lst(history.done); - + function attachLocalSpans(doc, change, from, to) { + var existing = change["spans_" + doc.id], n = 0; + doc.iter(Math.max(doc.first, from), Math.min(doc.first + doc.size, to), function(line) { + if (line.markedSpans) + (existing || (existing = change["spans_" + doc.id] = {}))[n] = line.markedSpans; + ++n; + }); + } + + function historyChangeFromChange(doc, change) { + var histChange = {from: change.from, to: changeEnd(change), text: getBetween(doc, change.from, change.to)}; + attachLocalSpans(doc, histChange, change.from.line, change.to.line + 1); + linkedDocs(doc, function(doc) {attachLocalSpans(doc, histChange, change.from.line, change.to.line + 1);}, true); + return histChange; + } + + function addToHistory(doc, change, selAfter, opId) { + var hist = doc.history; + hist.undone.length = 0; + var time = +new Date, cur = lst(hist.done); + if (cur && - (history.lastOp == cm.curOp.id || - history.lastOrigin == origin && (origin == "input" || origin == "delete") && - history.lastTime > time - 600)) { + (hist.lastOp == opId || + hist.lastOrigin == change.origin && (change.origin == "input" || change.origin == "delete") && + hist.lastTime > time - 600)) { // Merge this change into the last event - var last = lst(cur.events); - if (last.start > start + old.length || last.start + last.added < start) { - // Doesn't intersect with last sub-event, add new sub-event - cur.events.push({start: start, added: added, old: old}); + var last = lst(cur.changes); + if (posEq(change.from, change.to) && posEq(change.from, last.to)) { + // Optimized case for simple insertion -- don't want to add + // new changesets for every character typed + last.to = changeEnd(change); } else { - // Patch up the last sub-event - var startBefore = Math.max(0, last.start - start), - endAfter = Math.max(0, (start + old.length) - (last.start + last.added)); - for (var i = startBefore; i > 0; --i) last.old.unshift(old[i - 1]); - for (var i = endAfter; i > 0; --i) last.old.push(old[old.length - i]); - if (startBefore) last.start = start; - last.added += added - (old.length - startBefore - endAfter); + // Add new sub-event + cur.changes.push(historyChangeFromChange(doc, change)); } - cur.fromAfter = fromAfter; cur.toAfter = toAfter; + cur.anchorAfter = selAfter.anchor; cur.headAfter = selAfter.head; } else { // Can not be merged, start a new event. - cur = {events: [{start: start, added: added, old: old}], - fromBefore: fromBefore, toBefore: toBefore, fromAfter: fromAfter, toAfter: toAfter}; - history.done.push(cur); - while (history.done.length > cm.options.undoDepth) - history.done.shift(); - if (history.dirtyCounter < 0) - // The user has made a change after undoing past the last clean state. - // We can never get back to a clean state now until markClean() is called. - history.dirtyCounter = NaN; + cur = {changes: [historyChangeFromChange(doc, change)], + anchorBefore: doc.sel.anchor, headBefore: doc.sel.head, + anchorAfter: selAfter.anchor, headAfter: selAfter.head}; + hist.done.push(cur); + while (hist.done.length > hist.undoDepth) + hist.done.shift(); + if (hist.dirtyCounter < 0) + // The user has made a change after undoing past the last clean state. + // We can never get back to a clean state now until markClean() is called. + hist.dirtyCounter = NaN; else - history.dirtyCounter++; + hist.dirtyCounter++; } - history.lastTime = time; - history.lastOp = cm.curOp.id; - history.lastOrigin = origin; + hist.lastTime = time; + hist.lastOp = opId; + hist.lastOrigin = change.origin; + } + + function removeClearedSpans(spans) { + if (!spans) return null; + for (var i = 0, out; i < spans.length; ++i) { + if (spans[i].marker.explicitlyCleared) { if (!out) out = spans.slice(0, i); } + else if (out) out.push(spans[i]); + } + return !out ? spans : out.length ? out : null; + } + + // FIXME this goes wrong when + // - making a change on line n + // - setting a marker on line n outside of change + // - undoing the change (will wrongly clear the marker) + // needs an algorithm for selectively merging the undone ranges into + // the current surrounding + function getOldSpans(doc, change) { + var found = change["spans_" + doc.id]; + if (!found) return null; + for (var i = 0, nw = []; i < change.text.length; ++i) + nw.push(removeClearedSpans(found[i])); + return nw; + } + + // Used both to provide a JSON-safe object in .getHistory, and, when + // detaching a document, to split the history in two + function copyHistoryArray(events, newGroup) { + for (var i = 0, copy = []; i < events.length; ++i) { + var event = events[i], changes = event.changes, newChanges = []; + copy.push({changes: newChanges, anchorBefore: event.anchorBefore, headBefore: event.headBefore, + anchorAfter: event.anchorAfter, headAfter: event.headAfter}); + for (var j = 0; j < changes.length; ++j) { + var change = changes[j], m; + newChanges.push({from: change.from, to: change.to, text: change.text}); + if (newGroup) for (var prop in change) if (m = prop.match(/^spans_(\d+)$/)) { + if (indexOf(newGroup, Number(m[1])) > -1) { + lst(newChanges)[prop] = change[prop]; + delete change[prop]; + } + } + } + } + return copy; + } + + // Rebasing/resetting history to deal with externally-sourced changes + + function rebaseHistSel(pos, from, to, diff) { + if (to < pos.line) { + pos.line += diff; + } else if (from < pos.line) { + pos.line = from; + pos.ch = 0; + } + } + + // Tries to rebase an array of history events given a change in the + // document. If the change touches the same lines as the event, the + // event, and everything 'behind' it, is discarded. If the change is + // before the event, the event's positions are updated. Uses a + // copy-on-write scheme for the positions, to avoid having to + // reallocate them all on every rebase, but also avoid problems with + // shared position objects being unsafely updated. + function rebaseHistArray(array, from, to, diff) { + for (var i = 0; i < array.length; ++i) { + var sub = array[i], ok = true; + for (var j = 0; j < sub.changes.length; ++j) { + var cur = sub.changes[j]; + if (!sub.copied) { cur.from = copyPos(cur.from); cur.to = copyPos(cur.to); } + if (to < cur.from.line) { + cur.from.line += diff; + cur.to.line += diff; + } else if (from <= cur.to.line) { + ok = false; + break; + } + } + if (!sub.copied) { + sub.anchorBefore = copyPos(sub.anchorBefore); sub.headBefore = copyPos(sub.headBefore); + sub.anchorAfter = copyPos(sub.anchorAfter); sub.readAfter = copyPos(sub.headAfter); + sub.copied = true; + } + if (!ok) { + array.splice(0, i + 1); + i = 0; + } else { + rebaseHistSel(sub.anchorBefore); rebaseHistSel(sub.headBefore); + rebaseHistSel(sub.anchorAfter); rebaseHistSel(sub.headAfter); + } + } + } + + function rebaseHist(hist, change) { + var from = change.from.line, to = change.to.line, diff = change.text.length - (to - from) - 1; + rebaseHistArray(hist.done, from, to, diff); + rebaseHistArray(hist.undone, from, to, diff); } // EVENT OPERATORS @@ -4296,13 +4563,14 @@ window.CodeMirror = (function() { for (var i = 0; i < arr.length; ++i) arr[i].apply(null, args); } - function signalLater(cm, emitter, type /*, values...*/) { + var delayedCallbacks, delayedCallbackDepth = 0; + function signalLater(emitter, type /*, values...*/) { var arr = emitter._handlers && emitter._handlers[type]; if (!arr) return; - var args = Array.prototype.slice.call(arguments, 3), flist = cm.curOp && cm.curOp.delayedCallbacks; + var args = Array.prototype.slice.call(arguments, 3); function bnd(f) {return function(){f.apply(null, args);};}; for (var i = 0; i < arr.length; ++i) - if (flist) flist.push(bnd(arr[i])); + if (delayedCallbacks) delayedCallbacks.push(bnd(arr[i])); else arr[i].apply(null, args); } @@ -4363,6 +4631,15 @@ window.CodeMirror = (function() { return -1; } + function createObj(base, props) { + if (!base) return; + createObj.prototype = base; + var inst = new createObj(); + if (props) for (var n in props) if (props.hasOwnProperty(n)) + inst[n] = props[n]; + return inst; + } + function emptyArray(size) { for (var a = [], i = 0; i < size; ++i) a.push(undefined); return a; @@ -4540,8 +4817,8 @@ window.CodeMirror = (function() { } function lineStart(cm, lineN) { - var line = getLine(cm.view.doc, lineN); - var visual = visualLine(cm.view.doc, line); + var line = getLine(cm.doc, lineN); + var visual = visualLine(cm.doc, line); if (visual != line) lineN = lineNo(visual); var order = getOrder(visual); var ch = !order ? 0 : order[0].level % 2 ? lineRight(visual) : lineLeft(visual); @@ -4549,7 +4826,7 @@ window.CodeMirror = (function() { } function lineEnd(cm, lineNo) { var merged, line; - while (merged = collapsedSpanAtEnd(line = getLine(cm.view.doc, lineNo))) + while (merged = collapsedSpanAtEnd(line = getLine(cm.doc, lineNo))) lineNo = merged.find().to.line; var order = getOrder(line); var ch = !order ? line.text.length : order[0].level % 2 ? lineLeft(line) : lineRight(line); diff --git a/test/doc_test.js b/test/doc_test.js new file mode 100644 index 0000000000..567a577ac4 --- /dev/null +++ b/test/doc_test.js @@ -0,0 +1,290 @@ +(function() { + // A minilanguage for instantiating linked CodeMirror instances and Docs + function instantiateSpec(spec, place, opts) { + var names = {}, pos = 0, l = spec.length, editors = []; + while (spec) { + var m = spec.match(/^(\w+)(\*?)(?:='([^\']*)'|<(~?)(\w+)(?:\/(\d+)-(\d+))?)\s*/); + var name = m[1], isDoc = m[2], cur; + if (m[3]) { + cur = isDoc ? CodeMirror.Doc(m[3]) : CodeMirror(place, clone(opts, {value: m[3]})); + } else { + var other = m[5]; + if (!names.hasOwnProperty(other)) { + names[other] = editors.length; + editors.push(CodeMirror(place, opts)); + } + var doc = editors[names[other]].linkedDoc({ + sharedHist: !m[4], + from: m[6] && Number(m[6]), + to: m[7] && Number(m[7]) + }); + cur = isDoc ? doc : CodeMirror(place, clone(opts, {value: doc})); + } + names[name] = editors.length; + editors.push(cur); + spec = spec.slice(m[0].length); + } + return editors; + } + + function clone(obj, props) { + if (!obj) return; + clone.prototype = obj; + var inst = new clone(); + if (props) for (var n in props) if (props.hasOwnProperty(n)) + inst[n] = props[n]; + return inst; + } + + function eqAll(val) { + var end = arguments.length, msg = null; + if (typeof arguments[end-1] == "string") + msg = arguments[--end]; + if (i == end) throw new Error("No editors provided to eqAll"); + for (var i = 1; i < end; ++i) + eq(arguments[i].getValue(), val, msg) + } + + function testDoc(name, spec, run, opts) { + if (!opts) opts = {}; + + return test("doc_" + name, function() { + var place = document.getElementById("testground"); + var editors = instantiateSpec(spec, place, opts); + var successful = false; + + try { + run.apply(null, editors); + successful = true; + } finally { + if ((debug && !successful) || verbose) { + place.style.visibility = ""; + } else { + for (var i = 0; i < editors.length; ++i) + if (editors[i] instanceof CodeMirror) + place.removeChild(editors[i].getWrapperElement()); + } + } + }); + } + + function testBasic(a, b) { + eqAll("x", a, b); + a.setValue("hey"); + eqAll("hey", a, b); + b.setValue("wow"); + eqAll("wow", a, b); + a.replaceRange("u\nv\nw", {line: 0, ch: 3}); + b.replaceRange("i", {line: 0, ch: 4}); + b.replaceRange("j", {line: 2, ch: 1}); + eqAll("wowui\nv\nwj", a, b); + } + + testDoc("basic", "A='x' B 0, "not at left"); + is(pos.top > 0, "not at top"); + }); + + testDoc("copyDoc", "A='u'", function(a) { + var copy = a.getDoc().copy(true); + a.setValue("foo"); + copy.setValue("bar"); + var old = a.swapDoc(copy); + eq(a.getValue(), "bar"); + a.undo(); + eq(a.getValue(), "u"); + a.swapDoc(old); + eq(a.getValue(), "foo"); + eq(old.historySize().undo, 1); + eq(old.copy(false).historySize().undo, 0); + }); + + testDoc("docKeepsMode", "A='1+1'", function(a) { + var other = CodeMirror.Doc("hi", "text/x-markdown"); + a.setOption("mode", "text/javascript"); + var old = a.swapDoc(other); + eq(a.getOption("mode"), "text/x-markdown"); + eq(a.getMode().name, "markdown"); + a.swapDoc(old); + eq(a.getOption("mode"), "text/javascript"); + eq(a.getMode().name, "javascript"); + }); + + testDoc("subview", "A='1\n2\n3\n4\n5' B<~A/1-3", function(a, b) { + eq(b.getValue(), "2\n3"); + eq(b.firstLine(), 1); + b.setCursor({line: 4}); + eqPos(b.getCursor(), {line: 2, ch: 1}); + a.replaceRange("-1\n0\n", {line: 0, ch: 0}); + eq(b.firstLine(), 3); + eqPos(b.getCursor(), {line: 4, ch: 1}); + a.undo(); + eqPos(b.getCursor(), {line: 2, ch: 1}); + b.replaceRange("oyoy\n", {line: 2, ch: 0}); + eq(a.getValue(), "1\n2\noyoy\n3\n4\n5"); + b.undo(); + eq(a.getValue(), "1\n2\n3\n4\n5"); + }); + + testDoc("subviewEditOnBoundary", "A='11\n22\n33\n44\n55' B<~A/1-4", function(a, b) { + a.replaceRange("x\nyy\nz", {line: 0, ch: 1}, {line: 2, ch: 1}); + eq(b.firstLine(), 2); + eq(b.lineCount(), 2); + eq(b.getValue(), "z3\n44"); + a.replaceRange("q\nrr\ns", {line: 3, ch: 1}, {line: 4, ch: 1}); + eq(b.firstLine(), 2); + eq(b.getValue(), "z3\n4q"); + eq(a.getValue(), "1x\nyy\nz3\n4q\nrr\ns5"); + a.execCommand("selectAll"); + a.replaceSelection("!"); + eqAll("!", a, b); + }); +})(); diff --git a/test/driver.js b/test/driver.js index 6d480e9460..aba427702e 100644 --- a/test/driver.js +++ b/test/driver.js @@ -117,8 +117,8 @@ function eq(a, b, msg) { function eqPos(a, b, msg) { function str(p) { return "{line:" + p.line + ",ch:" + p.ch + "}"; } if (a == b) return; - if (a == null) throw new Failure(label("comparing null to " + str(b))); - if (b == null) throw new Failure(label("comparing " + str(a) + " to null")); + if (a == null) throw new Failure(label("comparing null to " + str(b), msg)); + if (b == null) throw new Failure(label("comparing " + str(a) + " to null", msg)); if (a.line != b.line || a.ch != b.ch) throw new Failure(label(str(a) + " != " + str(b), msg)); } function is(a, msg) { @@ -135,4 +135,4 @@ function countTests() { ++sum; } return sum; -} \ No newline at end of file +} diff --git a/test/index.html b/test/index.html index d4cec017f6..24bdf999d8 100644 --- a/test/index.html +++ b/test/index.html @@ -52,6 +52,7 @@

    CodeMirror: Test Suite

    + @@ -65,12 +66,8 @@

    CodeMirror: Test Suite

    From f861359412e902d3b635726623a530691878baaf Mon Sep 17 00:00:00 2001 From: Ice White Date: Wed, 23 Jan 2013 14:24:33 +0800 Subject: [PATCH 0620/5780] tiddlwiki => tiddlywiki --- mode/meta.js | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/mode/meta.js b/mode/meta.js index ad212f9663..d6296c332f 100644 --- a/mode/meta.js +++ b/mode/meta.js @@ -57,7 +57,7 @@ CodeMirror.modeInfo = [ {name: 'PL/SQL', mime: 'text/x-plsql', mode: 'sql'}, {name: 'sTeX', mime: 'text/x-stex', mode: 'stex'}, {name: 'LaTeX', mime: 'text/x-latex', mode: 'stex'}, - {name: 'TiddlyWiki ', mime: 'text/x-tiddlywiki', mode: 'tiddlwiki'}, + {name: 'TiddlyWiki ', mime: 'text/x-tiddlywiki', mode: 'tiddlywiki'}, {name: 'Tiki wiki', mime: 'text/tiki', mode: 'tiki'}, {name: 'VB.NET', mime: 'text/x-vb', mode: 'vb'}, {name: 'VBScript', mime: 'text/vbscript', mode: 'vbscript'}, From 56f1f15a7ee3fc1711aeb3a326a4c30887fff729 Mon Sep 17 00:00:00 2001 From: Marijn Haverbeke Date: Wed, 23 Jan 2013 12:54:56 +0100 Subject: [PATCH 0621/5780] Add "hide"/"unhide" events for markers These fire when the marker is removed/resurrected by editing and undo operations. Closes #1093 --- doc/manual.html | 6 ++++++ lib/codemirror.js | 38 +++++++++++++++++++++++++++++--------- 2 files changed, 35 insertions(+), 9 deletions(-) diff --git a/doc/manual.html b/doc/manual.html index 93271c51ca..da2a6580c2 100644 --- a/doc/manual.html +++ b/doc/manual.html @@ -423,6 +423,12 @@

    Events

    be fired once per handle. Note that deleting the range through text editing does not fire this event, because an undo action might bring the range back into existence. +
    "hide" ()
    +
    Fired when the last part of the marker is removed from the + document by editing operations.
    +
    "unhide" ()
    +
    Fired when, after the marker was removed by editing, a undo + operation brought the marker back.

    Line widgets, returned diff --git a/lib/codemirror.js b/lib/codemirror.js index cee61ac586..2575cd9b62 100644 --- a/lib/codemirror.js +++ b/lib/codemirror.js @@ -1241,6 +1241,12 @@ window.CodeMirror = (function() { if (cm.state.focused && op.updateInput) resetInput(cm, op.userSelChange); + var hidden = op.maybeHiddenMarkers, unhidden = op.maybeUnhiddenMarkers; + if (hidden) for (var i = 0; i < hidden.length; ++i) + if (!hidden[i].lines.length) signal(hidden[i], "hide"); + if (unhidden) for (var i = 0; i < unhidden.length; ++i) + if (unhidden[i].lines.length) signal(unhidden[i], "unhide"); + if (op.textChanged) signal(cm, "change", cm, op.textChanged); if (op.selectionChanged) signal(cm, "cursorActivity", cm); @@ -3269,8 +3275,25 @@ window.CodeMirror = (function() { startStyle: this.startStyle, endStyle: this.endStyle}; }; + TextMarker.prototype.attachLine = function(line) { + if (!this.lines.length && this.doc.cm) { + var op = this.doc.cm.curOp; + if (!op.maybeHiddenMarkers || indexOf(op.maybeHiddenMarkers, this) == -1) + (op.maybeUnhiddenMarkers || (op.maybeUnhiddenMarkers = [])).push(this); + } + this.lines.push(line); + }; + TextMarker.prototype.detachLine = function(line) { + this.lines.splice(indexOf(this.lines, line), 1); + if (!this.lines.length && this.doc.cm) { + var op = this.doc.cm.curOp; + (op.maybeHiddenMarkers || (op.maybeHiddenMarkers = [])).push(this); + } + }; + function markText(doc, from, to, options, type) { if (options && options.shared) return markTextShared(doc, from, to, options, type); + if (doc.cm && !doc.cm.curOp) return operation(doc.cm, markText)(doc, from, to, options, type); var marker = new TextMarker(doc, type); if (type == "range" && !posLess(from, to)) return marker; @@ -3312,12 +3335,12 @@ window.CodeMirror = (function() { marker.size = size; marker.atomic = true; } - if (cm) runInOp(cm, function() { + if (cm) { if (updateMaxLine) cm.curOp.updateMaxLine = true; if (marker.className || marker.startStyle || marker.endStyle || marker.collapsed) regChange(cm, from.line, to.line + 1); if (marker.atomic) reCheckSelection(cm); - }); + } return marker; } @@ -3377,7 +3400,7 @@ window.CodeMirror = (function() { } function addMarkedSpan(line, span) { line.markedSpans = line.markedSpans ? line.markedSpans.concat([span]) : [span]; - span.marker.lines.push(line); + span.marker.attachLine(line); } function markedSpansBefore(old, startCh) { @@ -3562,18 +3585,15 @@ window.CodeMirror = (function() { function detachMarkedSpans(line) { var spans = line.markedSpans; if (!spans) return; - for (var i = 0; i < spans.length; ++i) { - var lines = spans[i].marker.lines; - var ix = indexOf(lines, line); - lines.splice(ix, 1); - } + for (var i = 0; i < spans.length; ++i) + spans[i].marker.detachLine(line); line.markedSpans = null; } function attachMarkedSpans(line, spans) { if (!spans) return; for (var i = 0; i < spans.length; ++i) - spans[i].marker.lines.push(line); + spans[i].marker.attachLine(line); line.markedSpans = spans; } From b6149ccbab7282d9929eb48e86de77b4c612d019 Mon Sep 17 00:00:00 2001 From: Marijn Haverbeke Date: Wed, 23 Jan 2013 14:48:20 +0100 Subject: [PATCH 0622/5780] Add show-hint add-on to replace simple-hint --- addon/hint/show-hint.css | 37 ++++++++++++++ addon/hint/show-hint.js | 101 +++++++++++++++++++++++++++++++++++++++ demo/complete.html | 6 +-- doc/manual.html | 11 ++++- 4 files changed, 150 insertions(+), 5 deletions(-) create mode 100644 addon/hint/show-hint.css create mode 100644 addon/hint/show-hint.js diff --git a/addon/hint/show-hint.css b/addon/hint/show-hint.css new file mode 100644 index 0000000000..55661c9c0c --- /dev/null +++ b/addon/hint/show-hint.css @@ -0,0 +1,37 @@ +.CodeMirror-hints { + position: absolute; + z-index: 10; + overflow: hidden; + list-style: none; + + margin: 0; + padding: 2px; + + -webkit-box-shadow: 2px 3px 5px rgba(0,0,0,.2); + -moz-box-shadow: 2px 3px 5px rgba(0,0,0,.2); + box-shadow: 2px 3px 5px rgba(0,0,0,.2); + border-radius: 3px; + + background: white; + font-size: 90%; + font-family: monospace; + + max-height: 20em; + overflow-y: auto; +} + +.CodeMirror-hint { + margin: 0; + padding: 0 4px; + border-radius: 2px; + max-width: 19em; + overflow: hidden; + white-space: pre; + color: black; + cursor: pointer; +} + +.CodeMirror-hint-active { + background: #08f; + color: white; +} diff --git a/addon/hint/show-hint.js b/addon/hint/show-hint.js new file mode 100644 index 0000000000..dc381d8510 --- /dev/null +++ b/addon/hint/show-hint.js @@ -0,0 +1,101 @@ +CodeMirror.showHint = function(cm, getHints, options) { + if (!options) options = {}; + + function collectHints(previousToken) { + // We want a single cursor position. + if (cm.somethingSelected()) return; + + var token = cm.getTokenAt(cm.getCursor()); + + // Don't show completions if token has changed + if (previousToken != null && + (token.start != previousToken.start || token.type != previousToken.type)) + return; + + var result = getHints(cm, options); + if (!result || !result.list.length) return; + var completions = result.list; + // When there is only one completion, use it directly. + if (!previousToken && options.completeSingle !== false && completions.length == 1) { + cm.replaceRange(completions[0], result.from, result.to); + return true; + } + + // Build the select widget + var hints = document.createElement("ul"), selectedHint = 0; + hints.className = "CodeMirror-hints"; + for (var i = 0; i < completions.length; ++i) { + var elt = hints.appendChild(document.createElement("li")); + elt.className = "CodeMirror-hint" + (i ? "" : " CodeMirror-hint-active"); + elt.appendChild(document.createTextNode(completions[i])); + elt.hintId = i; + } + var pos = cm.cursorCoords(options.alignWithWord !== false ? result.from : null); + hints.style.left = pos.left + "px"; + hints.style.top = pos.bottom + "px"; + document.body.appendChild(hints); + + // If we're at the edge of the screen, then we want the menu to appear on the left of the cursor. + var winW = window.innerWidth || Math.max(document.body.offsetWidth, document.documentElement.offsetWidth); + if (winW - pos.left < hints.clientWidth) + hints.style.left = (pos.left - sel.clientWidth) + "px"; + + function changeActive(i) { + if (i < 0 || i >= completions.length || selectedHint == i) return; + hints.childNodes[selectedHint].className = "CodeMirror-hint"; + var node = hints.childNodes[selectedHint = i]; + node.className = "CodeMirror-hint CodeMirror-hint-active"; + if (node.offsetTop < hints.scrollTop) + hints.scrollTop = node.offsetTop - 3; + else if (node.offsetTop + node.offsetHeight > hints.scrollTop + hints.clientHeight) + hints.scrollTop = node.offsetTop + node.offsetHeight - hints.clientHeight + 3; + } + + var ourMap = { + Up: function() {changeActive(selectedHint - 1);}, + Down: function() {changeActive(selectedHint + 1);}, + Enter: pick, + Tab: pick, + Esc: close + }; + if (options.customKeys) for (var key in options.customKeys) if (options.customKeys.hasOwnProperty(key)) { + var val = options.customKeys[key]; + if (/^(Up|Down|Enter|Esc)$/.test(key)) val = ourMap[val]; + ourMap[key] = val; + } + + cm.addKeyMap(ourMap); + cm.on("cursorActivity", cursorActivity); + CodeMirror.on(hints, "dblclick", function(e) { + var t = e.target || e.srcElement; + if (t.hintId != null) {selectedHint = t.hintId; pick();} + setTimeout(function(){cm.focus();}, 20); + }); + CodeMirror.on(hints, "click", function(e) { + var t = e.target || e.srcElement; + if (t.hintId != null) changeActive(t.hintId); + setTimeout(function(){cm.focus();}, 20); + }); + + var done = false, once; + function close() { + if (done) return; + done = true; + clearTimeout(once); + hints.parentNode.removeChild(hints); + cm.removeKeyMap(ourMap); + cm.off("cursorActivity", cursorActivity); + } + function pick() { + cm.replaceRange(completions[selectedHint], result.from, result.to); + close(); + } + var once; + function cursorActivity() { + clearTimeout(once); + once = setTimeout(function(){close(); collectHints(token);}, 70); + } + return true; + } + return collectHints(); +}; diff --git a/demo/complete.html b/demo/complete.html index 2efbd3d622..c94f6b40d6 100644 --- a/demo/complete.html +++ b/demo/complete.html @@ -5,8 +5,8 @@ CodeMirror: Autocomplete Demo - - + + @@ -59,7 +59,7 @@

    CodeMirror: Autocomplete demo

    - - + + @@ -23,7 +23,7 @@

    CodeMirror: XML Autocomplete demo

    Type '<' or space inside tag or press ctrl-space to activate autocompletion. See - the code (here + the code (here and here) to figure out how it works.

    @@ -57,14 +57,22 @@

    CodeMirror: XML Autocomplete demo

    'x-three' ]; + CodeMirror.commands.autocomplete = function(cm) { + CodeMirror.showHint(cm, CodeMirror.xmlHint); + } + function passAndHint(cm) { + setTimeout(function() {cm.execCommand("autocomplete");}, 100); + throw CodeMirror.Pass; + } + var editor = CodeMirror.fromTextArea(document.getElementById("code"), { value: '', mode: 'text/html', lineNumbers: true, extraKeys: { - "' '": function(cm) { CodeMirror.xmlHint(cm, ' '); }, - "'<'": function(cm) { CodeMirror.xmlHint(cm, '<'); }, - "Ctrl-Space": function(cm) { CodeMirror.xmlHint(cm, ''); } + "' '": passAndHint, + "'<'": passAndHint, + "Ctrl-Space": "autocomplete" }, autoCloseTags: true }); From e8c7955c4139844786ead37a0a2eb8ba4f7204af Mon Sep 17 00:00:00 2001 From: Marijn Haverbeke Date: Wed, 23 Jan 2013 19:26:29 +0100 Subject: [PATCH 0624/5780] Fix bug in the way event handlers are called Issue #1178 --- lib/codemirror.js | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/lib/codemirror.js b/lib/codemirror.js index 2575cd9b62..d4d575537f 100644 --- a/lib/codemirror.js +++ b/lib/codemirror.js @@ -4696,7 +4696,7 @@ window.CodeMirror = (function() { function signalLater(emitter, type /*, values...*/) { var arr = emitter._handlers && emitter._handlers[type]; if (!arr) return; - var args = Array.prototype.slice.call(arguments, 3); + var args = Array.prototype.slice.call(arguments, 2); function bnd(f) {return function(){f.apply(null, args);};}; for (var i = 0; i < arr.length; ++i) if (delayedCallbacks) delayedCallbacks.push(bnd(arr[i])); From 8a5aee5462586630df455caf9067759c8d35b7d1 Mon Sep 17 00:00:00 2001 From: Marijn Haverbeke Date: Fri, 25 Jan 2013 13:06:40 +0100 Subject: [PATCH 0625/5780] Add Code Monster & Code Maven to real-world uses --- doc/realworld.html | 1 + 1 file changed, 1 insertion(+) diff --git a/doc/realworld.html b/doc/realworld.html index 870c28d312..13eea24b1b 100644 --- a/doc/realworld.html +++ b/doc/realworld.html @@ -28,6 +28,7 @@

    { } CodeMi
  • Cargo Collective (creative publishing platform)
  • Codebug (PHP Xdebug front-end)
  • CodeMirror2-GWT (Google Web Toolkit wrapper)
  • +
  • Code Monster & Code Maven (learning environment)
  • Codepen (gallery of animations)
  • Code School (online tech learning environment)
  • Codev (collaborative IDE)
  • From 7525611d361d451ed67fb1861707fff2a3ec5aff Mon Sep 17 00:00:00 2001 From: Jochen Berger Date: Thu, 24 Jan 2013 08:12:41 +0100 Subject: [PATCH 0626/5780] when checking whether the wrapper element is still part of the DOM, check all the way up to the document body --- lib/codemirror.js | 6 +++++- 1 file changed, 5 insertions(+), 1 deletion(-) diff --git a/lib/codemirror.js b/lib/codemirror.js index d4d575537f..2de52a094f 100644 --- a/lib/codemirror.js +++ b/lib/codemirror.js @@ -1396,10 +1396,14 @@ window.CodeMirror = (function() { // Prevent wrapper from ever scrolling on(d.wrapper, "scroll", function() { d.wrapper.scrollTop = d.wrapper.scrollLeft = 0; }); on(window, "resize", function resizeHandler() { + var currentParent = d.wrapper.parentNode; // Might be a text scaling operation, clear size caches. d.cachedCharWidth = d.cachedTextHeight = null; clearCaches(cm); - if (d.wrapper.parentNode) runInOp(cm, bind(regChange, cm)); + while (currentParent !== document.body && currentParent !== null) { + currentParent = currentParent.parentNode; + } + if (currentParent === document.body) runInOp(cm, bind(regChange, cm)); else off(window, "resize", resizeHandler); }); From 02088b8ff92f05f0448faa16e65191622a266476 Mon Sep 17 00:00:00 2001 From: Marijn Haverbeke Date: Fri, 25 Jan 2013 13:09:27 +0100 Subject: [PATCH 0627/5780] Fix memory leak The window.onresize handlers registered for each editor would prevent them from ever getting collected. --- lib/codemirror.js | 23 +++++++++++++++-------- 1 file changed, 15 insertions(+), 8 deletions(-) diff --git a/lib/codemirror.js b/lib/codemirror.js index 2de52a094f..4f7b06535b 100644 --- a/lib/codemirror.js +++ b/lib/codemirror.js @@ -1395,17 +1395,24 @@ window.CodeMirror = (function() { on(d.scrollbarV, "mousedown", reFocus); // Prevent wrapper from ever scrolling on(d.wrapper, "scroll", function() { d.wrapper.scrollTop = d.wrapper.scrollLeft = 0; }); - on(window, "resize", function resizeHandler() { - var currentParent = d.wrapper.parentNode; + + if (!window.registered) window.registered = 0; + ++window.registered; + function onResize() { // Might be a text scaling operation, clear size caches. d.cachedCharWidth = d.cachedTextHeight = null; clearCaches(cm); - while (currentParent !== document.body && currentParent !== null) { - currentParent = currentParent.parentNode; - } - if (currentParent === document.body) runInOp(cm, bind(regChange, cm)); - else off(window, "resize", resizeHandler); - }); + runInOp(cm, bind(regChange, cm)); + } + on(window, "resize", onResize); + // Above handler holds on to the editor and its data structures. + // Here we poll to unregister it when the editor is no longer in + // the document, so that it can be garbage-collected. + setTimeout(function unregister() { + for (var p = d.wrapper.parentNode; p && p != document.body; p = p.parentNode) {} + if (p) setTimeout(unregister, 5000); + else {--window.registered; off(window, "resize", onResize);} + }, 5000); on(d.input, "keyup", operation(cm, function(e) { if (cm.options.onKeyEvent && cm.options.onKeyEvent(cm, addStop(e))) return; From 335ac34e73556d32780540f5b0caaef04cd6aec0 Mon Sep 17 00:00:00 2001 From: Marijn Haverbeke Date: Fri, 25 Jan 2013 13:46:46 +0100 Subject: [PATCH 0628/5780] Note release 3.02 --- doc/compress.html | 1 + index.html | 6 ++++++ lib/codemirror.js | 2 +- package.json | 2 +- 4 files changed, 9 insertions(+), 2 deletions(-) diff --git a/doc/compress.html b/doc/compress.html index 94f293c725..aa94d70dd6 100644 --- a/doc/compress.html +++ b/doc/compress.html @@ -30,6 +30,7 @@

    { } CodeMi

    Version: + + + +

    Simple addon to easily mark (and style) selected text.

    + + + diff --git a/demo/matchhighlighter.html b/demo/matchhighlighter.html index ea5944ce33..dd59af36c6 100644 --- a/demo/matchhighlighter.html +++ b/demo/matchhighlighter.html @@ -11,9 +11,9 @@ @@ -26,11 +26,11 @@

    CodeMirror: Match Highlighter Demo

    -

    Highlight matches of selected text on select

    +

    Search and highlight occurences of the selected text.

    From 4388df166fa5c7e0535d41292f4bbf97324a8d13 Mon Sep 17 00:00:00 2001 From: Marijn Haverbeke Date: Wed, 6 Feb 2013 10:01:00 +0100 Subject: [PATCH 0673/5780] Clean up mark-selection add-on --- addon/selection/mark-selection.js | 50 ++++++++++++------------------- demo/markselection.html | 11 ++++--- 2 files changed, 24 insertions(+), 37 deletions(-) diff --git a/addon/selection/mark-selection.js b/addon/selection/mark-selection.js index 755f3ed1f9..7342135545 100644 --- a/addon/selection/mark-selection.js +++ b/addon/selection/mark-selection.js @@ -1,38 +1,26 @@ // Because sometimes you need to mark the selected *text*. -// Use by attaching the following function call to the cursorActivity event: - //myCodeMirror.markSelection(); -// To clear the selection mark, run: - //myCodeMirror.markSelection.clear(myCodeMirror); -// The selection will be marked with `.CodeMirror-selection`. -"use strict"; +// +// Adds an option 'styleSelectedText' which, when enabled, gives +// selected text the CSS class "CodeMirror-selectedtext". (function() { - function lessthan(a, b) { - if (a.line < b.line) return true; - if (a.line == b.line) return a.ch < b.ch; - } + "use strict"; + + CodeMirror.defineOption("styleSelectedText", false, function(cm, val, old) { + var prev = old && old != CodeMirror.Init; + if (val && !prev) + cm.on("cursorActivity", updateSelectedText); + else if (!val && prev) + cm.off("cursorActivity", updateSelectedText); + }); - function clear(cm) { + function updateSelectedText(cm) { if (cm._selectionMark) cm._selectionMark.clear(); + + if (cm.somethingSelected()) + cm._selectionMark = cm.markText(cm.getCursor("start"), cm.getCursor("end"), + {className: "CodeMirror-selectedtext"}); + else + cm._selectionMark = null; } - - function markSelection(minChars, className) { - var cm = this; - clear(cm); - if (!cm.somethingSelected()) return; - - var start = cm.getCursor('anchor') - , end = cm.getCursor(); - - if (lessthan(end, start)) { - var i = end; - end = start; - start = i; - } - - cm._selectionMark = cm.markText(start, end, {className:'CodeMirror-selection'}); - } - - markSelection.clear = clear; - CodeMirror.defineExtension("markSelection", markSelection); })(); diff --git a/demo/markselection.html b/demo/markselection.html index 9a7d64ea1a..e1c054869f 100644 --- a/demo/markselection.html +++ b/demo/markselection.html @@ -11,9 +11,8 @@ @@ -25,9 +24,9 @@

    CodeMirror: Mark Selection Demo

    behind the text, you'll need something like this to change its colour. From 5f7171ed556b571bab3cd0e248ecff25497baec6 Mon Sep 17 00:00:00 2001 From: Marijn Haverbeke Date: Wed, 6 Feb 2013 10:18:43 +0100 Subject: [PATCH 0674/5780] [match-highlighter addon] Rewrite to use defineOption and an overlay --- addon/search/match-highlighter.js | 89 +++++++++++++++++-------------- demo/matchhighlighter.html | 10 ++-- 2 files changed, 53 insertions(+), 46 deletions(-) diff --git a/addon/search/match-highlighter.js b/addon/search/match-highlighter.js index b8e7e3e388..c6e35cd97e 100644 --- a/addon/search/match-highlighter.js +++ b/addon/search/match-highlighter.js @@ -1,53 +1,60 @@ -// Define match-highlighter commands. Depends on searchcursor.js -// Use by attaching the following function call to the cursorActivity event: -// myCodeMirror.matchHighlight([minChars], [className]); -// And including a CSS rules for `span.CodeMirror-matchhighlight` (also -// optionally a separate one for `.CodeMirror-focused` -- see demo matchhighlighter.html). -// To clear all marks, run: -// myCodeMirror.matchHighlight.clear(myCodeMirror); +// Highlighting text that matches the selection +// +// Defines an option highlightSelectionMatches, which, when enabled, +// will style strings that match the selection throughout the +// document. +// +// The option can be set to true to simply enable it, or to a +// {minChars, style} object to explicitly configure it. minChars is +// the minimum amount of characters that should be selected for the +// behavior to occur, and style is the token style to apply to the +// matches. This will be prefixed by "cm-" to create an actual CSS +// class name. (function() { var DEFAULT_MIN_CHARS = 2; - var DEFAULT_CLASS_NAME = "CodeMirror-matchhighlight"; + var DEFAULT_TOKEN_STYLE = "matchhighlight"; - function MatchHighlightState() { - this.marked = []; + function State(options) { + this.minChars = typeof options == "object" && options.minChars || DEFAULT_MIN_CHARS; + this.style = typeof options == "object" && options.style || DEFAULT_TOKEN_STYLE; + this.overlay = null; } - function getMatchHighlightState(cm) { - return cm._matchHighlightState || (cm._matchHighlightState = new MatchHighlightState()); - } - - function clearMarks(state) { - for (var i = 0; i < state.marked.length; ++i) - state.marked[i].clear(); - state.marked = []; - } - - function markDocument(cm, minChars, className) { - var state = getMatchHighlightState(cm); - clearMarks(state); - // If not enough chars selected, don't search - if (cm.somethingSelected() && cm.getSelection().replace(/^\s+|\s+$/g, "").length < minChars) return; - // This is too expensive on big documents - if (cm.lineCount() > 2000) return; - var query = cm.getSelection(); + CodeMirror.defineOption("highlightSelectionMatches", false, function(cm, val, old) { + var prev = old && old != CodeMirror.Init; + if (val && !prev) { + cm._matchHighlightState = new State(val); + cm.on("cursorActivity", highlightMatches); + } else if (!val && prev) { + var over = cm._matchHighlightState.overlay; + if (over) cm.removeOverlay(over); + cm._matchHighlightState = null; + cm.off("cursorActivity", highlightMatches); + } + }); + + function highlightMatches(cm) { cm.operation(function() { - for (var cursor = cm.getSearchCursor(query); cursor.findNext();) { - // Only apply matchhighlight to the matches other than the one actually selected - if (cursor.from().line !== cm.getCursor(true).line || cursor.from().ch !== cm.getCursor(true).ch) - state.marked.push(cm.markText(cursor.from(), cursor.to(), {className: className})); + var state = cm._matchHighlightState; + if (state.overlay) { + cm.removeOverlay(state.overlay); + state.overlay = null; } + + if (!cm.somethingSelected()) return; + var selection = cm.getSelection().replace(/^\s+|\s+$/g, ""); + if (selection.length < state.minChars) return; + + cm.addOverlay(state.overlay = makeOverlay(selection, state.style)); }); } - var plugin = function(minChars, className) { - if (typeof minChars === 'undefined') minChars = DEFAULT_MIN_CHARS; - if (typeof className === 'undefined') className = DEFAULT_CLASS_NAME; - markDocument(this, minChars, className); - }; - plugin.clear = function(cm) { - clearMarks(getMatchHighlightState(cm)); - }; - CodeMirror.defineExtension("matchHighlight", plugin); + function makeOverlay(query, style) { + return {token: function(stream) { + if (stream.match(query)) return style; + stream.next(); + stream.skipTo(query.charAt(0)) || stream.skipToEnd(); + }}; + } })(); diff --git a/demo/matchhighlighter.html b/demo/matchhighlighter.html index dd59af36c6..7fd1b2b792 100644 --- a/demo/matchhighlighter.html +++ b/demo/matchhighlighter.html @@ -12,8 +12,8 @@ @@ -24,9 +24,9 @@

    CodeMirror: Match Highlighter Demo

    Give it a try! No more hardToSpotVars. From 31d2e736203d7fc163d30983c1310707d7419bea Mon Sep 17 00:00:00 2001 From: Marijn Haverbeke Date: Wed, 6 Feb 2013 10:21:19 +0100 Subject: [PATCH 0675/5780] [match-highlighter addon] Update documentation --- doc/manual.html | 12 ++++-------- 1 file changed, 4 insertions(+), 8 deletions(-) diff --git a/doc/manual.html b/doc/manual.html index cf29ba7bbb..632e010ca1 100644 --- a/doc/manual.html +++ b/doc/manual.html @@ -1412,14 +1412,10 @@

    Add-ons

    python-hint.js
    Defines CodeMirror.pythonHint, a hinter for Python code.
    match-highlighter.js
    -
    Adds a matchHighlight method to CodeMirror - instances that can be called (typically from - a cursorActivity - handler) to highlight all instances of a currently selected word - with the a classname given as a first argument to the method. - Depends on - the searchcursor - add-on. Demo here.
    +
    Adds a highlightSelectionMatches option that + can be enabled to highlight all instances of a currently + selected word. + Demo here.
    formatting.js
    Adds commentRange, autoIndentRange, and autoFormatRange methods that, respectively, From c98271e6a0f0692610e02154029d16a71ecfff2e Mon Sep 17 00:00:00 2001 From: Marijn Haverbeke Date: Wed, 6 Feb 2013 10:45:34 +0100 Subject: [PATCH 0676/5780] Repair context menu on IE9 --- lib/codemirror.js | 11 ++++++----- 1 file changed, 6 insertions(+), 5 deletions(-) diff --git a/lib/codemirror.js b/lib/codemirror.js index 19f4421530..53a4ebff6f 100644 --- a/lib/codemirror.js +++ b/lib/codemirror.js @@ -30,6 +30,7 @@ window.CodeMirror = (function() { if (opera_version) opera_version = Number(opera_version[1]); // Some browsers use the wrong event properties to signal cmd/ctrl on OS X var flipCtrlCmd = mac && (qtwebkit || opera && (opera_version == null || opera_version < 12.11)); + var captureMiddleClick = gecko || (ie && !ie_lt9); // Optimize some code when these features are not used var sawReadOnlySpans = false, sawCollapsedSpans = false; @@ -1385,7 +1386,7 @@ window.CodeMirror = (function() { // Gecko browsers fire contextmenu *after* opening the menu, at // which point we can't mess with it anymore. Context menu is // handled in onMouseDown for Gecko. - if (!gecko) on(d.scroller, "contextmenu", function(e) {onContextMenu(cm, e);}); + if (captureMiddleClick) on(d.scroller, "contextmenu", function(e) {onContextMenu(cm, e);}); on(d.scroller, "scroll", function() { setScrollTop(cm, d.scroller.scrollTop); @@ -1512,7 +1513,7 @@ window.CodeMirror = (function() { switch (e_button(e)) { case 3: - if (gecko) onContextMenu.call(cm, cm, e); + if (captureMiddleClick) onContextMenu.call(cm, cm, e); return; case 2: if (start) extendSelection(cm.doc, start); @@ -1968,7 +1969,7 @@ window.CodeMirror = (function() { display.inputDiv.style.position = "absolute"; display.input.style.cssText = "position: fixed; width: 30px; height: 30px; top: " + (e.clientY - 5) + "px; left: " + (e.clientX - 5) + "px; z-index: 1000; background: white; outline: none;" + - "border-width: 0; outline: none; overflow: hidden; opacity: .05; filter: alpha(opacity=5);"; + "border-width: 0; outline: none; overflow: hidden; opacity: .05; -ms-opacity: .05; filter: alpha(opacity=5);"; focusInput(cm); resetInput(cm, true); // Adds "Select all" to context menu in FF @@ -1981,7 +1982,7 @@ window.CodeMirror = (function() { slowPoll(cm); // Try to detect the user choosing select-all - if (display.input.selectionStart != null) { + if (display.input.selectionStart != null && (!ie || ie_lt9)) { clearTimeout(detectingSelectAll); var extval = display.input.value = " " + (posEq(sel.from, sel.to) ? "" : display.input.value), i = 0; display.prevInput = " "; @@ -1996,7 +1997,7 @@ window.CodeMirror = (function() { } } - if (gecko) { + if (captureMiddleClick) { e_stop(e); var mouseup = function() { off(window, "mouseup", mouseup); From 712522647fa7adb39d670529975b25f0af3a2d37 Mon Sep 17 00:00:00 2001 From: Marijn Haverbeke Date: Wed, 6 Feb 2013 14:28:59 +0100 Subject: [PATCH 0677/5780] Use a Pos constructor instead of repeating {line, ch} literals This helps performance on v8 (in microbenchmarks) and is less noisy. Regular literals are still accepted, the constructor is only used to create objects, doesn't attach any methods. --- addon/edit/closetag.js | 2 +- addon/edit/continuecomment.js | 2 +- addon/edit/matchbrackets.js | 12 +- addon/fold/collapserange.js | 2 +- addon/fold/foldcode.js | 18 +- addon/hint/javascript-hint.js | 14 +- addon/hint/pig-hint.js | 4 +- addon/hint/python-hint.js | 4 +- addon/hint/xml-hint.js | 4 +- addon/search/search.js | 2 +- addon/search/searchcursor.js | 24 +- lib/codemirror.js | 114 +++---- test/doc_test.js | 104 +++---- test/test.js | 552 +++++++++++++++++----------------- 14 files changed, 437 insertions(+), 421 deletions(-) diff --git a/addon/edit/closetag.js b/addon/edit/closetag.js index 653fc0b216..44bbca7c6f 100644 --- a/addon/edit/closetag.js +++ b/addon/edit/closetag.js @@ -61,7 +61,7 @@ throw CodeMirror.Pass; var doIndent = indentTags && indexOf(indentTags, lowerTagName) > -1; - var curPos = doIndent ? {line: pos.line + 1, ch: 0} : {line: pos.line, ch: pos.ch + 1}; + var curPos = doIndent ? CodeMirror.Pos(pos.line + 1, 0) : CodeMirror.Pos(pos.line, pos.ch + 1); cm.replaceSelection(">" + (doIndent ? "\n\n" : "") + "", {head: curPos, anchor: curPos}); if (doIndent) { diff --git a/addon/edit/continuecomment.js b/addon/edit/continuecomment.js index dac83a81f6..0def40008f 100644 --- a/addon/edit/continuecomment.js +++ b/addon/edit/continuecomment.js @@ -12,7 +12,7 @@ if (token.type == "comment" && mode.blockCommentStart) { var end = token.string.indexOf(mode.blockCommentEnd); - var full = cm.getRange({line: pos.line, ch: 0}, {line: pos.line, ch: token.end}), found; + var full = cm.getRange(CodeMirro.Pos(pos.line, 0), CodeMirror.Pos(pos.line, token.end)), found; if (end != -1 && end == token.string.length - mode.blockCommentEnd.length) { // Comment ended, don't continue it } else if (token.string.indexOf(mode.blockCommentStart) == 0) { diff --git a/addon/edit/matchbrackets.js b/addon/edit/matchbrackets.js index a5d0998b07..d8ae0787a8 100644 --- a/addon/edit/matchbrackets.js +++ b/addon/edit/matchbrackets.js @@ -2,13 +2,15 @@ var ie_lt8 = /MSIE \d/.test(navigator.userAgent) && (document.documentMode == null || document.documentMode < 8); + var Pos = CodeMirror.Pos; + var matching = {"(": ")>", ")": "(<", "[": "]>", "]": "[<", "{": "}>", "}": "{<"}; function findMatchingBracket(cm) { var cur = cm.getCursor(), line = cm.getLineHandle(cur.line), pos = cur.ch - 1; var match = (pos >= 0 && matching[line.text.charAt(pos)]) || matching[line.text.charAt(++pos)]; if (!match) return null; var forward = match.charAt(1) == ">", d = forward ? 1 : -1; - var style = cm.getTokenAt({line: cur.line, ch: pos + 1}).type; + var style = cm.getTokenAt(Pos(cur.line, pos + 1)).type; var stack = [line.text.charAt(pos)], re = /[(){}[\]]/; function scan(line, lineNo, start) { @@ -17,7 +19,7 @@ if (start != null) pos = start + d; for (; pos != end; pos += d) { var ch = line.text.charAt(pos); - if (re.test(ch) && cm.getTokenAt({line: lineNo, ch: pos + 1}).type == style) { + if (re.test(ch) && cm.getTokenAt(Pos(lineNo, pos + 1)).type == style) { var match = matching[ch]; if (match.charAt(1) == ">" == forward) stack.push(ch); else if (stack.pop() != match.charAt(0)) return {pos: pos, match: false}; @@ -30,16 +32,16 @@ else found = scan(cm.getLineHandle(i), i); if (found) break; } - return {from: {line: cur.line, ch: pos}, to: found && {line: i, ch: found.pos}, match: found && found.match}; + return {from: Pos(cur.line, pos), to: found && Pos(i, found.pos), match: found && found.match}; } function matchBrackets(cm, autoclear) { var found = findMatchingBracket(cm); if (!found) return; var style = found.match ? "CodeMirror-matchingbracket" : "CodeMirror-nonmatchingbracket"; - var one = cm.markText(found.from, {line: found.from.line, ch: found.from.ch + 1}, + var one = cm.markText(found.from, Pos(found.from.line, found.from.ch + 1), {className: style}); - var two = found.to && cm.markText(found.to, {line: found.to.line, ch: found.to.ch + 1}, + var two = found.to && cm.markText(found.to, Pos(found.to.line, found.to.ch + 1), {className: style}); // Kludge to work around the IE bug from issue #1193, where text // input stops going to the textare whever this fires. diff --git a/addon/fold/collapserange.js b/addon/fold/collapserange.js index ebcdda2755..e2c013734d 100644 --- a/addon/fold/collapserange.js +++ b/addon/fold/collapserange.js @@ -33,7 +33,7 @@ var from = Math.min(old, line), to = Math.max(old, line); if (from != to) { // Finish this fold - var fold = cm.markText({line: from + 1, ch: 0}, {line: to - 1}, { + var fold = cm.markText(CodeMirror.Pos(from + 1, 0), {line: to - 1}, { collapsed: true, inclusiveLeft: true, inclusiveRight: true, diff --git a/addon/fold/foldcode.js b/addon/fold/foldcode.js index 12e5d1d7b4..90019762fc 100644 --- a/addon/fold/foldcode.js +++ b/addon/fold/foldcode.js @@ -95,8 +95,8 @@ CodeMirror.tagRangeFinder = function(cm, start) { depth--; else depth++; - if (!depth) return {from: {line: start.line, ch: gtPos + 1}, - to: {line: l, ch: match.index}}; + if (!depth) return {from: CodeMirror.Pos(start.line, gtPos + 1), + to: CodeMirror.Pos(l, match.index)}; } } l++; @@ -111,7 +111,7 @@ CodeMirror.braceRangeFinder = function(cm, start) { for (;;) { var found = lineText.lastIndexOf("{", at); if (found < start.ch) break; - tokenType = cm.getTokenAt({line: line, ch: found + 1}).type; + tokenType = cm.getTokenAt(CodeMirror.Pos(line, found + 1)).type; if (!/^(comment|string)/.test(tokenType)) { startChar = found; break; } at = found - 1; } @@ -125,7 +125,7 @@ CodeMirror.braceRangeFinder = function(cm, start) { if (nextClose < 0) nextClose = text.length; pos = Math.min(nextOpen, nextClose); if (pos == text.length) break; - if (cm.getTokenAt({line: i, ch: pos + 1}).type == tokenType) { + if (cm.getTokenAt(CodeMirror.Pos(i, pos + 1)).type == tokenType) { if (pos == nextOpen) ++count; else if (!--count) { end = i; endCh = pos; break outer; } } @@ -133,8 +133,8 @@ CodeMirror.braceRangeFinder = function(cm, start) { } } if (end == null || end == line + 1) return; - return {from: {line: line, ch: startChar + 1}, - to: {line: end, ch: endCh}}; + return {from: CodeMirror.Pos(line, startChar + 1), + to: CodeMirror.Pos(end, endCh)}; }; CodeMirror.indentRangeFinder = function(cm, start) { @@ -144,8 +144,8 @@ CodeMirror.indentRangeFinder = function(cm, start) { var curLine = cm.getLine(i); if (CodeMirror.countColumn(curLine, null, tabSize) < myIndent && CodeMirror.countColumn(cm.getLine(i-1), null, tabSize) > myIndent) - return {from: {line: start.line, ch: firstLine.length}, - to: {line: i, ch: curLine.length}}; + return {from: CodeMirror.Pos(start.line, firstLine.length), + to: CodeMirror.Pos(i, curLine.length)}; } }; @@ -159,7 +159,7 @@ CodeMirror.newFoldFunction = function(rangeFinder, widget) { } return function(cm, pos) { - if (typeof pos == "number") pos = {line: pos, ch: 0}; + if (typeof pos == "number") pos = CodeMirror.Pos(pos, 0); var range = rangeFinder(cm, pos); if (!range) return; diff --git a/addon/hint/javascript-hint.js b/addon/hint/javascript-hint.js index d44ff2968d..fe2240af1c 100644 --- a/addon/hint/javascript-hint.js +++ b/addon/hint/javascript-hint.js @@ -1,4 +1,6 @@ (function () { + var Pos = CodeMirror.Pos; + function forEach(arr, f) { for (var i = 0, e = arr.length; i < e; ++i) f(arr[i]); } @@ -26,20 +28,20 @@ } // If it is a property, find out what it is a property of. while (tprop.type == "property") { - tprop = getToken(editor, {line: cur.line, ch: tprop.start}); + tprop = getToken(editor, Pos(cur.line, tprop.start)); if (tprop.string != ".") return; - tprop = getToken(editor, {line: cur.line, ch: tprop.start}); + tprop = getToken(editor, Pos(cur.line, tprop.start)); if (tprop.string == ')') { var level = 1; do { - tprop = getToken(editor, {line: cur.line, ch: tprop.start}); + tprop = getToken(editor, Pos(cur.line, tprop.start)); switch (tprop.string) { case ')': level++; break; case '(': level--; break; default: break; } } while (level > 0); - tprop = getToken(editor, {line: cur.line, ch: tprop.start}); + tprop = getToken(editor, Pos(cur.line, tprop.start)); if (tprop.type.indexOf("variable") === 0) tprop.type = "function"; else return; // no clue @@ -48,8 +50,8 @@ context.push(tprop); } return {list: getCompletions(token, context, keywords, options), - from: {line: cur.line, ch: token.start}, - to: {line: cur.line, ch: token.end}}; + from: Pos(cur.line, token.start), + to: Pos(cur.line, token.end)}; } CodeMirror.javascriptHint = function(editor, options) { diff --git a/addon/hint/pig-hint.js b/addon/hint/pig-hint.js index 149b468158..9847996be9 100644 --- a/addon/hint/pig-hint.js +++ b/addon/hint/pig-hint.js @@ -37,8 +37,8 @@ } return {list: completionList, - from: {line: cur.line, ch: token.start}, - to: {line: cur.line, ch: token.end}}; + from: CodeMirror.Pos(cur.line, token.start), + to: CodeMirror.Pos(cur.line, token.end)}; } CodeMirror.pigHint = function(editor) { diff --git a/addon/hint/python-hint.js b/addon/hint/python-hint.js index 56b077b3fb..60221b89ec 100644 --- a/addon/hint/python-hint.js +++ b/addon/hint/python-hint.js @@ -37,8 +37,8 @@ } return {list: completionList, - from: {line: cur.line, ch: token.start}, - to: {line: cur.line, ch: token.end}}; + from: CodeMirror.Pos(cur.line, token.start), + to: CodeMirror.Pos(cur.line, token.end)}; } CodeMirror.pythonHint = function(editor) { diff --git a/addon/hint/xml-hint.js b/addon/hint/xml-hint.js index 354eb297f3..1609aafd40 100644 --- a/addon/hint/xml-hint.js +++ b/addon/hint/xml-hint.js @@ -8,7 +8,7 @@ if (cursor.ch > 0) { - var text = cm.getRange({line: 0, ch: 0}, cursor); + var text = cm.getRange(CodeMirror.Pos(0, 0), cursor); var typed = ''; var simbol = ''; for(var i = text.length - 1; i >= 0; i--) { @@ -38,7 +38,7 @@ return { list: hints, - from: { line: cursor.line, ch: cursor.ch - typed.length }, + from: CodeMirror.Pos(cursor.line, cursor.ch - typed.length), to: cursor }; } diff --git a/addon/search/search.js b/addon/search/search.js index d8d866b04d..6331b86555 100644 --- a/addon/search/search.js +++ b/addon/search/search.js @@ -66,7 +66,7 @@ var state = getSearchState(cm); var cursor = getSearchCursor(cm, state.query, rev ? state.posFrom : state.posTo); if (!cursor.find(rev)) { - cursor = getSearchCursor(cm, state.query, rev ? {line: cm.lineCount() - 1} : {line: 0, ch: 0}); + cursor = getSearchCursor(cm, state.query, rev ? CodeMirror.Pos(cm.lastLine()) : CodeMirror.Pos(cm.firstLine(), 0)); if (!cursor.find(rev)) return; } cm.setSelection(cursor.from(), cursor.to()); diff --git a/addon/search/searchcursor.js b/addon/search/searchcursor.js index 5ccf7723bb..e6554ce56b 100644 --- a/addon/search/searchcursor.js +++ b/addon/search/searchcursor.js @@ -1,9 +1,11 @@ (function(){ + var Pos = CodeMirror.Pos; + function SearchCursor(cm, query, pos, caseFold) { this.atOccurrence = false; this.cm = cm; if (caseFold == null && typeof query == "string") caseFold = false; - pos = pos ? cm.clipPos(pos) : {line: 0, ch: 0}; + pos = pos ? cm.clipPos(pos) : Pos(0, 0); this.pos = {from: pos, to: pos}; // The matches method is filled in based on the type of query. @@ -31,8 +33,8 @@ start = match && match.index; } if (match && match[0]) - return {from: {line: pos.line, ch: start}, - to: {line: pos.line, ch: start + match[0].length}, + return {from: Pos(pos.line, start), + to: Pos(pos.line, start + match[0].length), match: match}; }; } else { // String query @@ -50,8 +52,8 @@ var line = fold(cm.getLine(pos.line)), len = query.length, match; if (reverse ? (pos.ch >= len && (match = line.lastIndexOf(query, pos.ch - len)) != -1) : (match = line.indexOf(query, pos.ch)) != -1) - return {from: {line: pos.line, ch: match}, - to: {line: pos.line, ch: match + len}}; + return {from: Pos(pos.line, match), + to: Pos(pos.line, match + len)}; }; } } else { @@ -72,7 +74,7 @@ var offsetB = (reverse ? line.lastIndexOf(match) : line.indexOf(match) + match.length); if (reverse ? offsetB != line.length - match.length : offsetB != match.length) return; - var start = {line: pos.line, ch: offsetA}, end = {line: ln, ch: offsetB}; + var start = Pos(pos.line, offsetA), end = Pos(ln, offsetB); return {from: reverse ? end : start, to: reverse ? start : end}; } }; @@ -87,7 +89,7 @@ find: function(reverse) { var self = this, pos = this.cm.clipPos(reverse ? this.pos.from : this.pos.to); function savePosAndFail(line) { - var pos = {line: line, ch: 0}; + var pos = Pos(line, 0); self.pos = {from: pos, to: pos}; self.atOccurrence = false; return false; @@ -101,12 +103,12 @@ } if (reverse) { if (!pos.line) return savePosAndFail(0); - pos = {line: pos.line-1, ch: this.cm.getLine(pos.line-1).length}; + pos = Pos(pos.line-1, this.cm.getLine(pos.line-1).length); } else { var maxLine = this.cm.lineCount(); if (pos.line == maxLine - 1) return savePosAndFail(maxLine); - pos = {line: pos.line+1, ch: 0}; + pos = Pos(pos.line + 1, 0); } } }, @@ -118,8 +120,8 @@ if (!this.atOccurrence) return; var lines = CodeMirror.splitLines(newText); this.cm.replaceRange(lines, this.pos.from, this.pos.to); - this.pos.to = {line: this.pos.from.line + lines.length - 1, - ch: lines[lines.length - 1].length + (lines.length == 1 ? this.pos.from.ch : 0)}; + this.pos.to = Pos(this.pos.from.line + lines.length - 1, + lines[lines.length - 1].length + (lines.length == 1 ? this.pos.from.ch : 0)); } }; diff --git a/lib/codemirror.js b/lib/codemirror.js index 53a4ebff6f..e9e485570e 100644 --- a/lib/codemirror.js +++ b/lib/codemirror.js @@ -773,7 +773,7 @@ window.CodeMirror = (function() { var lineObj = getLine(doc, line); var lineLen = lineObj.text.length, rVal = retTop ? Infinity : -Infinity; function coords(ch) { - return charCoords(cm, {line: line, ch: ch}, "div", lineObj); + return charCoords(cm, Pos(line, ch), "div", lineObj); } iterateBidiSections(getOrder(lineObj), fromArg || 0, toArg == null ? lineLen : toArg, function(from, to, dir) { @@ -1109,7 +1109,7 @@ window.CodeMirror = (function() { if (y < 0) return {line: doc.first, ch: 0, outside: true}; var lineNo = lineAtHeight(doc, y), last = doc.first + doc.size - 1; if (lineNo > last) - return {line: doc.size - 1, ch: getLine(doc, last).text.length}; + return Pos(doc.size - 1, getLine(doc, last).text.length); if (x < 0) x = 0; for (;;) { @@ -1130,7 +1130,7 @@ window.CodeMirror = (function() { var measurement = measureLine(cm, lineObj); function getX(ch) { - var sp = cursorCoords(cm, {line: lineNo, ch: ch}, "line", + var sp = cursorCoords(cm, Pos(lineNo, ch), "line", lineObj, measurement); wrongLine = true; if (innerOff > sp.bottom) return Math.max(0, sp.left - cWidth); @@ -1337,9 +1337,9 @@ window.CodeMirror = (function() { while (same < l && prevInput[same] == text[same]) ++same; var from = sel.from, to = sel.to; if (same < prevInput.length) - from = {line: from.line, ch: from.ch - (prevInput.length - same)}; + from = Pos(from.line, from.ch - (prevInput.length - same)); else if (cm.state.overwrite && posEq(from, to) && !cm.state.pasteIncoming) - to = {line: to.line, ch: Math.min(getLine(doc, to.line).text.length, to.ch + (text.length - same))}; + to = Pos(to.line, Math.min(getLine(doc, to.line).text.length, to.ch + (text.length - same))); var updateInput = cm.curOp.updateInput; makeChange(cm.doc, {from: from, to: to, text: splitLines(text.slice(same)), origin: cm.state.pasteIncoming ? "paste" : "+input"}, "end"); @@ -1583,8 +1583,8 @@ window.CodeMirror = (function() { if (posLess(cur, startstart)) extendSelection(cm.doc, word.from, startend); else extendSelection(cm.doc, startstart, word.to); } else if (type == "triple") { - if (posLess(cur, startstart)) extendSelection(cm.doc, startend, clipPos(doc, {line: cur.line, ch: 0})); - else extendSelection(cm.doc, startstart, clipPos(doc, {line: cur.line + 1, ch: 0})); + if (posLess(cur, startstart)) extendSelection(cm.doc, startend, clipPos(doc, Pos(cur.line, 0))); + else extendSelection(cm.doc, startstart, clipPos(doc, Pos(cur.line + 1, 0))); } } @@ -2012,8 +2012,8 @@ window.CodeMirror = (function() { // UPDATING function changeEnd(change) { - return {line: change.from.line + change.text.length - 1, - ch: lst(change.text).length + (change.text.length == 1 ? change.from.ch : 0)}; + return Pos(change.from.line + change.text.length - 1, + lst(change.text).length + (change.text.length == 1 ? change.from.ch : 0)); } // Hint can be null|"end"|"start"|"around"|{anchor,head} @@ -2032,7 +2032,7 @@ window.CodeMirror = (function() { var line = pos.line + change.text.length - (change.to.line - change.from.line) - 1, ch = pos.ch; if (pos.line == change.to.line) ch += end.ch - change.to.ch; - return {line: line, ch: ch}; + return Pos(line, ch); }; return {anchor: adjustPos(sel.anchor), head: adjustPos(sel.head)}; } @@ -2105,7 +2105,7 @@ window.CodeMirror = (function() { } function shiftDoc(doc, distance) { - function shiftPos(pos) {return {line: pos.line + distance, ch: pos.ch};} + function shiftPos(pos) {return Pos(pos.line + distance, pos.ch);} doc.first += distance; if (doc.cm) regChange(doc.cm, doc.first, doc.first, distance); doc.sel.head = shiftPos(doc.sel.head); doc.sel.anchor = shiftPos(doc.sel.anchor); @@ -2126,12 +2126,12 @@ window.CodeMirror = (function() { if (change.from.line < doc.first) { var shift = change.text.length - 1 - (doc.first - change.from.line); shiftDoc(doc, shift); - change = {from: {line: doc.first, ch: 0}, to: {line: change.to.line + shift, ch: change.to.ch}, + change = {from: Pos(doc.first, 0), to: Pos(change.to.line + shift, change.to.ch), text: [lst(change.text)], origin: change.origin}; } var last = doc.lastLine(); if (change.to.line > last) { - change = {from: change.from, to: {line: last, ch: getLine(doc, last).text.length}, + change = {from: change.from, to: Pos(last, getLine(doc, last).text.length), text: [change.text[0]], origin: change.origin}; } @@ -2192,20 +2192,28 @@ window.CodeMirror = (function() { makeChange(doc, {from: from, to: to, text: code, origin: origin}, null); } - // SELECTION + // POSITION OBJECT + + function Pos(line, ch) { + if (!(this instanceof Pos)) return new Pos(line, ch); + this.line = line; this.ch = ch; + } + CodeMirror.Pos = Pos; function posEq(a, b) {return a.line == b.line && a.ch == b.ch;} function posLess(a, b) {return a.line < b.line || (a.line == b.line && a.ch < b.ch);} - function copyPos(x) {return {line: x.line, ch: x.ch};} + function copyPos(x) {return Pos(x.line, x.ch);} + + // SELECTION function clipLine(doc, n) {return Math.max(doc.first, Math.min(n, doc.first + doc.size - 1));} function clipPos(doc, pos) { - if (pos.line < doc.first) return {line: doc.first, ch: 0}; + if (pos.line < doc.first) return Pos(doc.first, 0); var last = doc.first + doc.size - 1; - if (pos.line > last) return {line: last, ch: getLine(doc, last).text.length}; + if (pos.line > last) return Pos(last, getLine(doc, last).text.length); var ch = pos.ch, linelen = getLine(doc, pos.line).text.length; - if (ch == null || ch > linelen) return {line: pos.line, ch: linelen}; - else if (ch < 0) return {line: pos.line, ch: 0}; + if (ch == null || ch > linelen) return Pos(pos.line, linelen); + else if (ch < 0) return Pos(pos.line, 0); else return pos; } function isLine(doc, l) {return l >= doc.first && l < doc.first + doc.size;} @@ -2277,10 +2285,10 @@ window.CodeMirror = (function() { if (posEq(newPos, curPos)) { newPos.ch += dir; if (newPos.ch < 0) { - if (newPos.line > doc.first) newPos = clipPos(doc, {line: newPos.line - 1}); + if (newPos.line > doc.first) newPos = clipPos(doc, Pos(newPos.line - 1)); else newPos = null; } else if (newPos.ch > line.text.length) { - if (newPos.line < doc.first + doc.size - 1) newPos = {line: newPos.line + 1, ch: 0}; + if (newPos.line < doc.first + doc.size - 1) newPos = Pos(newPos.line + 1, 0); else newPos = null; } if (!newPos) { @@ -2290,7 +2298,7 @@ window.CodeMirror = (function() { if (!mayClear) return skipAtomic(doc, pos, bias, true); // Otherwise, turn off editing until further notice, and return the start of the doc doc.cantEdit = true; - return {line: doc.first, ch: 0}; + return Pos(doc.first, 0); } flipped = true; newPos = pos; dir = -dir; } @@ -2406,7 +2414,7 @@ window.CodeMirror = (function() { if (pos < indentation) indentString += spaceStr(indentation - pos); if (indentString != curSpaceString) - replaceRange(cm.doc, indentString, {line: n, ch: 0}, {line: n, ch: curSpaceString.length}, "+input"); + replaceRange(cm.doc, indentString, Pos(n, 0), Pos(n, curSpaceString.length), "+input"); line.stateAfter = null; } @@ -2450,7 +2458,7 @@ window.CodeMirror = (function() { if (dir > 0) if (!moveOnce()) break; } } - return skipAtomic(doc, {line: line, ch: ch}, dir, true); + return skipAtomic(doc, Pos(line, ch), dir, true); } function findWordAt(line, pos) { @@ -2464,11 +2472,11 @@ window.CodeMirror = (function() { while (start > 0 && check(line.charAt(start - 1))) --start; while (end < line.length && check(line.charAt(end))) ++end; } - return {from: {line: pos.line, ch: start}, to: {line: pos.line, ch: end}}; + return {from: Pos(pos.line, start), to: Pos(pos.line, end)}; } function selectLine(cm, line) { - extendSelection(cm.doc, {line: line, ch: 0}, clipPos(cm.doc, {line: line + 1, ch: 0})); + extendSelection(cm.doc, Pos(line, 0), clipPos(cm.doc, Pos(line + 1, 0))); } // PROTOTYPE @@ -2742,7 +2750,7 @@ window.CodeMirror = (function() { }, scrollIntoView: function(pos) { - if (typeof pos == "number") pos = {line: pos, ch: 0}; + if (typeof pos == "number") pos = Pos(pos, 0); if (!pos || pos.line != null) { pos = pos ? clipPos(this.doc, pos) : this.doc.sel.head; scrollPosIntoView(this, pos); @@ -2969,21 +2977,21 @@ window.CodeMirror = (function() { // STANDARD COMMANDS var commands = CodeMirror.commands = { - selectAll: function(cm) {cm.setSelection({line: cm.firstLine(), ch: 0}, {line: cm.lastLine()});}, + selectAll: function(cm) {cm.setSelection(Pos(cm.firstLine(), 0), Pos(cm.lastLine()));}, killLine: function(cm) { var from = cm.getCursor(true), to = cm.getCursor(false), sel = !posEq(from, to); if (!sel && cm.getLine(from.line).length == from.ch) - cm.replaceRange("", from, {line: from.line + 1, ch: 0}, "+delete"); - else cm.replaceRange("", from, sel ? to : {line: from.line}, "+delete"); + cm.replaceRange("", from, Pos(from.line + 1, 0), "+delete"); + else cm.replaceRange("", from, sel ? to : Pos(from.line), "+delete"); }, deleteLine: function(cm) { var l = cm.getCursor().line; - cm.replaceRange("", {line: l, ch: 0}, {line: l}, "+delete"); + cm.replaceRange("", Pos(l, 0), Pos(l), "+delete"); }, undo: function(cm) {cm.undo();}, redo: function(cm) {cm.redo();}, - goDocStart: function(cm) {cm.extendSelection({line: cm.firstLine(), ch: 0});}, - goDocEnd: function(cm) {cm.extendSelection({line: cm.lastLine()});}, + goDocStart: function(cm) {cm.extendSelection(Pos(cm.firstLine(), 0));}, + goDocEnd: function(cm) {cm.extendSelection(Pos(cm.lastLine()));}, goLineStart: function(cm) { cm.extendSelection(lineStart(cm, cm.getCursor().line)); }, @@ -2994,7 +3002,7 @@ window.CodeMirror = (function() { if (!order || order[0].level == 0) { var firstNonWS = Math.max(0, line.text.search(/\S/)); var inWS = cur.line == start.line && cur.ch <= firstNonWS && cur.ch; - cm.extendSelection({line: start.line, ch: inWS ? 0 : firstNonWS}); + cm.extendSelection(Pos(start.line, inWS ? 0 : firstNonWS)); } else cm.extendSelection(start); }, goLineEnd: function(cm) { @@ -3026,7 +3034,7 @@ window.CodeMirror = (function() { var cur = cm.getCursor(), line = cm.getLine(cur.line); if (cur.ch > 0 && cur.ch < line.length - 1) cm.replaceRange(line.charAt(cur.ch) + line.charAt(cur.ch - 1), - {line: cur.line, ch: cur.ch - 1}, {line: cur.line, ch: cur.ch + 1}); + Pos(cur.line, cur.ch - 1), Pos(cur.line, cur.ch + 1)); }, newlineAndIndent: function(cm) { operation(cm, function() { @@ -3290,8 +3298,8 @@ window.CodeMirror = (function() { var span = getMarkedSpanFor(line.markedSpans, this); if (span.from != null || span.to != null) { var found = lineNo(line); - if (span.from != null) from = {line: found, ch: span.from}; - if (span.to != null) to = {line: found, ch: span.to}; + if (span.from != null) from = Pos(found, span.from); + if (span.to != null) to = Pos(found, span.to); } } if (this.type == "bookmark") return from; @@ -3558,14 +3566,14 @@ window.CodeMirror = (function() { for (var i = 0; i < markers.length; ++i) { var mk = markers[i], m = mk.find(); if (mk.inclusiveLeft) { - m.from = {line: m.from.line, ch: m.from.ch - 1}; + m.from = Pos(m.from.line, m.from.ch - 1); if (m.from.ch < 0 && m.from.line > doc.first) - m.from = {line: m.from.line - 1, ch: getLine(doc, m.from.line - 1).text.length}; + m.from = Pos(m.from.line - 1, getLine(doc, m.from.line - 1).text.length); } if (mk.inclusiveRight) { - m.to = {line: m.to.line, ch: m.to.ch + 1}; + m.to = Pos(m.to.line, m.to.ch + 1); if (m.to.line < doc.first + doc.size - 1 && m.to.ch > getLine(doc, m.to.line).text.length) - m.to = {line: m.to.line + 1, ch: 0}; + m.to = Pos(m.to.line + 1, 0); } for (var j = 0; j < parts.length; ++j) { var p = parts[j]; @@ -4165,7 +4173,7 @@ window.CodeMirror = (function() { this.cantEdit = false; this.history = makeHistory(); this.frontier = firstLine; - var start = {line: firstLine, ch: 0}; + var start = Pos(firstLine, 0); this.sel = {from: start, to: start, head: start, anchor: start, shift: false, extend: false, goalColumn: null}; this.id = ++nextDocId; this.modeOption = mode; @@ -4193,8 +4201,8 @@ window.CodeMirror = (function() { return lines.join(lineSep || "\n"); }, setValue: function(code) { - var top = {line: this.first, ch: 0}, last = this.first + this.size - 1; - makeChange(this, {from: top, to: {line: last, ch: getLine(this, last).text.length}, + var top = Pos(this.first, 0), last = this.first + this.size - 1; + makeChange(this, {from: top, to: Pos(last, getLine(this, last).text.length), text: splitLines(code), origin: "setValue"}, {head: top, anchor: top}, true); }, @@ -4212,11 +4220,11 @@ window.CodeMirror = (function() { getLine: function(line) {var l = this.getLineHandle(line); return l && l.text;}, setLine: function(line, text) { if (isLine(this, line)) - replaceRange(this, text, {line: line, ch: 0}, {line: line, ch: getLine(this, line).text.length}); + replaceRange(this, text, Pos(line, 0), clipPos(this, Pos(line))); }, removeLine: function(line) { if (isLine(this, line)) - replaceRange(this, "", {line: line, ch: 0}, clipPos(this, {line: line+1, ch: 0})); + replaceRange(this, "", Pos(line, 0), clipPos(this, Pos(line + 1, 0))); }, getLineHandle: function(line) {if (isLine(this, line)) return getLine(this, line);}, @@ -4239,7 +4247,7 @@ window.CodeMirror = (function() { somethingSelected: function() {return !posEq(this.sel.head, this.sel.anchor);}, setCursor: docOperation(function(line, ch, extend) { - var pos = clipPos(this, typeof line == "number" ? {line: line, ch: ch || 0} : line); + var pos = clipPos(this, typeof line == "number" ? Pos(line, ch || 0) : line); if (extend) extendSelection(this, pos); else setSelection(this, pos, pos); }), @@ -4310,7 +4318,7 @@ window.CodeMirror = (function() { off -= sz; ++lineNo; }); - return clipPos(this, {line: lineNo, ch: ch}); + return clipPos(this, Pos(lineNo, ch)); }, indexFromPos: function (coords) { coords = clipPos(this, coords); @@ -4999,15 +5007,15 @@ window.CodeMirror = (function() { if (visual != line) lineN = lineNo(visual); var order = getOrder(visual); var ch = !order ? 0 : order[0].level % 2 ? lineRight(visual) : lineLeft(visual); - return {line: lineN, ch: ch}; + return Pos(lineN, ch); } - function lineEnd(cm, lineNo) { + function lineEnd(cm, lineN) { var merged, line; - while (merged = collapsedSpanAtEnd(line = getLine(cm.doc, lineNo))) - lineNo = merged.find().to.line; + while (merged = collapsedSpanAtEnd(line = getLine(cm.doc, lineN))) + lineN = merged.find().to.line; var order = getOrder(line); var ch = !order ? line.text.length : order[0].level % 2 ? lineLeft(line) : lineRight(line); - return {line: lineNo, ch: ch}; + return Pos(lineN, ch); } // This is somewhat involved. It is needed in order to move diff --git a/test/doc_test.js b/test/doc_test.js index 20c910f5fa..6609230dc0 100644 --- a/test/doc_test.js +++ b/test/doc_test.js @@ -76,9 +76,9 @@ eqAll("hey", a, b); b.setValue("wow"); eqAll("wow", a, b); - a.replaceRange("u\nv\nw", {line: 0, ch: 3}); - b.replaceRange("i", {line: 0, ch: 4}); - b.replaceRange("j", {line: 2, ch: 1}); + a.replaceRange("u\nv\nw", Pos(0, 3)); + b.replaceRange("i", Pos(0, 4)); + b.replaceRange("j", Pos(2, 1)); eqAll("wowui\nv\nwj", a, b); } @@ -86,9 +86,9 @@ testDoc("basicSeparate", "A='x' B<~A", testBasic); testDoc("sharedHist", "A='ab\ncd\nef' B top2.top); eq(top.left, top2.left); }); @@ -210,9 +212,9 @@ testCM("coordsChar", function(cm) { for (var ch = 0; ch <= 35; ch += 5) { for (var line = 0; line < 70; line += 5) { cm.setCursor(line, ch); - var coords = cm.charCoords({line: line, ch: ch}); + var coords = cm.charCoords(Pos(line, ch)); var pos = cm.coordsChar({left: coords.left, top: coords.top + 5}); - eqPos(pos, {line: line, ch: ch}); + eqPos(pos, Pos(line, ch)); } } }); @@ -259,8 +261,8 @@ testCM("undo", function(cm) { cm.clearHistory(); eq(cm.historySize().undo, 0); for (var i = 0; i < 20; ++i) { - cm.replaceRange("a", {line: 0, ch: 0}); - cm.replaceRange("b", {line: 3, ch: 0}); + cm.replaceRange("a", Pos(0, 0)); + cm.replaceRange("b", Pos(3, 0)); } eq(cm.historySize().undo, 40); for (var i = 0; i < 40; ++i) @@ -270,9 +272,9 @@ testCM("undo", function(cm) { }, {value: "abc"}); testCM("undoDepth", function(cm) { - cm.replaceRange("d", {line: 0}); - cm.replaceRange("e", {line: 0}); - cm.replaceRange("f", {line: 0}); + cm.replaceRange("d", Pos(0)); + cm.replaceRange("e", Pos(0)); + cm.replaceRange("f", Pos(0)); cm.undo(); cm.undo(); cm.undo(); eq(cm.getValue(), "abcd"); }, {value: "abc", undoDepth: 2}); @@ -284,31 +286,31 @@ testCM("undoDoesntClearValue", function(cm) { testCM("undoMultiLine", function(cm) { cm.operation(function() { - cm.replaceRange("x", {line:0, ch: 0}); - cm.replaceRange("y", {line:1, ch: 0}); + cm.replaceRange("x", Pos(0, 0)); + cm.replaceRange("y", Pos(1, 0)); }); cm.undo(); eq(cm.getValue(), "abc\ndef\nghi"); cm.operation(function() { - cm.replaceRange("y", {line:1, ch: 0}); - cm.replaceRange("x", {line:0, ch: 0}); + cm.replaceRange("y", Pos(1, 0)); + cm.replaceRange("x", Pos(0, 0)); }); cm.undo(); eq(cm.getValue(), "abc\ndef\nghi"); cm.operation(function() { - cm.replaceRange("y", {line:2, ch: 0}); - cm.replaceRange("x", {line:1, ch: 0}); - cm.replaceRange("z", {line:2, ch: 0}); + cm.replaceRange("y", Pos(2, 0)); + cm.replaceRange("x", Pos(1, 0)); + cm.replaceRange("z", Pos(2, 0)); }); cm.undo(); eq(cm.getValue(), "abc\ndef\nghi", 3); }, {value: "abc\ndef\nghi"}); testCM("undoComposite", function(cm) { - cm.replaceRange("y", {line: 1}); + cm.replaceRange("y", Pos(1)); cm.operation(function() { - cm.replaceRange("x", {line: 0}); - cm.replaceRange("z", {line: 2}); + cm.replaceRange("x", Pos(0)); + cm.replaceRange("z", Pos(2)); }); eq(cm.getValue(), "ax\nby\ncz\n"); cm.undo(); @@ -320,16 +322,16 @@ testCM("undoComposite", function(cm) { }, {value: "a\nb\nc\n"}); testCM("undoSelection", function(cm) { - cm.setSelection({line: 0, ch: 2}, {line: 0, ch: 4}); + cm.setSelection(Pos(0, 2), Pos(0, 4)); cm.replaceSelection(""); - cm.setCursor({line: 1, ch: 0}); + cm.setCursor(Pos(1, 0)); cm.undo(); - eqPos(cm.getCursor(true), {line: 0, ch: 2}); - eqPos(cm.getCursor(false), {line: 0, ch: 4}); - cm.setCursor({line: 1, ch: 0}); + eqPos(cm.getCursor(true), Pos(0, 2)); + eqPos(cm.getCursor(false), Pos(0, 4)); + cm.setCursor(Pos(1, 0)); cm.redo(); - eqPos(cm.getCursor(true), {line: 0, ch: 2}); - eqPos(cm.getCursor(false), {line: 0, ch: 2}); + eqPos(cm.getCursor(true), Pos(0, 2)); + eqPos(cm.getCursor(false), Pos(0, 2)); }, {value: "abcdefgh\n"}); testCM("markTextSingleLine", function(cm) { @@ -348,15 +350,15 @@ testCM("markTextSingleLine", function(cm) { {a: 6, b: 6, c: "a", f: 3, t: 6}, {a: 8, b: 9, c: "", f: 3, t: 6}], function(test) { cm.setValue("1234567890"); - var r = cm.markText({line: 0, ch: 3}, {line: 0, ch: 6}, {className: "foo"}); - cm.replaceRange(test.c, {line: 0, ch: test.a}, {line: 0, ch: test.b}); + var r = cm.markText(Pos(0, 3), Pos(0, 6), {className: "foo"}); + cm.replaceRange(test.c, Pos(0, test.a), Pos(0, test.b)); var f = r.find(); eq(f && f.from.ch, test.f); eq(f && f.to.ch, test.t); }); }); testCM("markTextMultiLine", function(cm) { - function p(v) { return v && {line: v[0], ch: v[1]}; } + function p(v) { return v && Pos(v[0], v[1]); } forEach([{a: [0, 0], b: [0, 5], c: "", f: [0, 0], t: [2, 5]}, {a: [0, 0], b: [0, 5], c: "foo\n", f: [1, 0], t: [3, 5]}, {a: [0, 1], b: [0, 10], c: "", f: [0, 1], t: [2, 5]}, @@ -373,7 +375,7 @@ testCM("markTextMultiLine", function(cm) { {a: [2, 3], b: [3, 0], c: "x", f: [0, 5], t: [2, 3]}, {a: [1, 1], b: [1, 9], c: "1\n2\n3", f: [0, 5], t: [4, 5]}], function(test) { cm.setValue("aaaaaaaaaa\nbbbbbbbbbb\ncccccccccc\ndddddddd\n"); - var r = cm.markText({line: 0, ch: 5}, {line: 2, ch: 5}, + var r = cm.markText(Pos(0, 5), Pos(2, 5), {className: "CodeMirror-matchingbracket"}); cm.replaceRange(test.c, p(test.a), p(test.b)); var f = r.find(); @@ -383,68 +385,68 @@ testCM("markTextMultiLine", function(cm) { testCM("markTextUndo", function(cm) { var marker1, marker2, bookmark; - marker1 = cm.markText({line: 0, ch: 1}, {line: 0, ch: 3}, + marker1 = cm.markText(Pos(0, 1), Pos(0, 3), {className: "CodeMirror-matchingbracket"}); - marker2 = cm.markText({line: 0, ch: 0}, {line: 2, ch: 1}, + marker2 = cm.markText(Pos(0, 0), Pos(2, 1), {className: "CodeMirror-matchingbracket"}); - bookmark = cm.setBookmark({line: 1, ch: 5}); + bookmark = cm.setBookmark(Pos(1, 5)); cm.operation(function(){ - cm.replaceRange("foo", {line: 0, ch: 2}); - cm.replaceRange("bar\nbaz\nbug\n", {line: 2, ch: 0}, {line: 3, ch: 0}); + cm.replaceRange("foo", Pos(0, 2)); + cm.replaceRange("bar\nbaz\nbug\n", Pos(2, 0), Pos(3, 0)); }); var v1 = cm.getValue(); cm.setValue(""); eq(marker1.find(), null); eq(marker2.find(), null); eq(bookmark.find(), null); cm.undo(); - eqPos(bookmark.find(), {line: 1, ch: 5}, "still there"); + eqPos(bookmark.find(), Pos(1, 5), "still there"); cm.undo(); var m1Pos = marker1.find(), m2Pos = marker2.find(); - eqPos(m1Pos.from, {line: 0, ch: 1}); eqPos(m1Pos.to, {line: 0, ch: 3}); - eqPos(m2Pos.from, {line: 0, ch: 0}); eqPos(m2Pos.to, {line: 2, ch: 1}); - eqPos(bookmark.find(), {line: 1, ch: 5}); + eqPos(m1Pos.from, Pos(0, 1)); eqPos(m1Pos.to, Pos(0, 3)); + eqPos(m2Pos.from, Pos(0, 0)); eqPos(m2Pos.to, Pos(2, 1)); + eqPos(bookmark.find(), Pos(1, 5)); cm.redo(); cm.redo(); eq(bookmark.find(), null); cm.undo(); - eqPos(bookmark.find(), {line: 1, ch: 5}); + eqPos(bookmark.find(), Pos(1, 5)); eq(cm.getValue(), v1); }, {value: "1234\n56789\n00\n"}); testCM("markTextStayGone", function(cm) { - var m1 = cm.markText({line: 0, ch: 0}, {line: 0, ch: 1}); - cm.replaceRange("hi", {line: 0, ch: 2}); + var m1 = cm.markText(Pos(0, 0), Pos(0, 1)); + cm.replaceRange("hi", Pos(0, 2)); m1.clear(); cm.undo(); eq(m1.find(), null); }, {value: "hello"}); testCM("undoPreservesNewMarks", function(cm) { - cm.markText({line: 0, ch: 3}, {line: 0, ch: 4}); - cm.markText({line: 1, ch: 1}, {line: 1, ch: 3}); - cm.replaceRange("", {line: 0, ch: 3}, {line: 3, ch: 1}); - var mBefore = cm.markText({line: 0, ch: 0}, {line: 0, ch: 1}); - var mAfter = cm.markText({line: 0, ch: 5}, {line: 0, ch: 6}); - var mAround = cm.markText({line: 0, ch: 2}, {line: 0, ch: 4}); + cm.markText(Pos(0, 3), Pos(0, 4)); + cm.markText(Pos(1, 1), Pos(1, 3)); + cm.replaceRange("", Pos(0, 3), Pos(3, 1)); + var mBefore = cm.markText(Pos(0, 0), Pos(0, 1)); + var mAfter = cm.markText(Pos(0, 5), Pos(0, 6)); + var mAround = cm.markText(Pos(0, 2), Pos(0, 4)); cm.undo(); - eqPos(mBefore.find().from, {line: 0, ch: 0}); - eqPos(mBefore.find().to, {line: 0, ch: 1}); - eqPos(mAfter.find().from, {line: 3, ch: 3}); - eqPos(mAfter.find().to, {line: 3, ch: 4}); - eqPos(mAround.find().from, {line: 0, ch: 2}); - eqPos(mAround.find().to, {line: 3, ch: 2}); - var found = cm.findMarksAt({line: 2, ch: 2}); + eqPos(mBefore.find().from, Pos(0, 0)); + eqPos(mBefore.find().to, Pos(0, 1)); + eqPos(mAfter.find().from, Pos(3, 3)); + eqPos(mAfter.find().to, Pos(3, 4)); + eqPos(mAround.find().from, Pos(0, 2)); + eqPos(mAround.find().to, Pos(3, 2)); + var found = cm.findMarksAt(Pos(2, 2)); eq(found.length, 1); eq(found[0], mAround); }, {value: "aaaa\nbbbb\ncccc\ndddd"}); testCM("markClearBetween", function(cm) { cm.setValue("aaa\nbbb\nccc\nddd\n"); - cm.markText({line: 0, ch: 0}, {line: 2}); - cm.replaceRange("aaa\nbbb\nccc", {line: 0, ch: 0}, {line: 2}); - eq(cm.findMarksAt({line: 1, ch: 1}).length, 0); + cm.markText(Pos(0, 0), Pos(2)); + cm.replaceRange("aaa\nbbb\nccc", Pos(0, 0), Pos(2)); + eq(cm.findMarksAt(Pos(1, 1)).length, 0); }); testCM("bookmark", function(cm) { - function p(v) { return v && {line: v[0], ch: v[1]}; } + function p(v) { return v && Pos(v[0], v[1]); } forEach([{a: [1, 0], b: [1, 1], c: "", d: [1, 4]}, {a: [1, 1], b: [1, 1], c: "xx", d: [1, 7]}, {a: [1, 4], b: [1, 5], c: "ab", d: [1, 6]}, @@ -454,25 +456,25 @@ testCM("bookmark", function(cm) { {a: [1, 4], b: [1, 4], c: "\n\n", d: [3, 1]}, {bm: [1, 9], a: [1, 1], b: [1, 1], c: "\n", d: [2, 8]}], function(test) { cm.setValue("1234567890\n1234567890\n1234567890"); - var b = cm.setBookmark(p(test.bm) || {line: 1, ch: 5}); + var b = cm.setBookmark(p(test.bm) || Pos(1, 5)); cm.replaceRange(test.c, p(test.a), p(test.b)); eqPos(b.find(), p(test.d)); }); }); testCM("bookmarkInsertLeft", function(cm) { - var br = cm.setBookmark({line: 0, ch: 2}, {insertLeft: false}); - var bl = cm.setBookmark({line: 0, ch: 2}, {insertLeft: true}); - cm.setCursor({line: 0, ch: 2}); + var br = cm.setBookmark(Pos(0, 2), {insertLeft: false}); + var bl = cm.setBookmark(Pos(0, 2), {insertLeft: true}); + cm.setCursor(Pos(0, 2)); cm.replaceSelection("hi"); - eqPos(br.find(), {line: 0, ch: 2}); - eqPos(bl.find(), {line: 0, ch: 4}); - cm.replaceRange("", {line: 0, ch: 4}, {line: 0, ch: 5}); - cm.replaceRange("", {line: 0, ch: 2}, {line: 0, ch: 4}); - cm.replaceRange("", {line: 0, ch: 1}, {line: 0, ch: 2}); + eqPos(br.find(), Pos(0, 2)); + eqPos(bl.find(), Pos(0, 4)); + cm.replaceRange("", Pos(0, 4), Pos(0, 5)); + cm.replaceRange("", Pos(0, 2), Pos(0, 4)); + cm.replaceRange("", Pos(0, 1), Pos(0, 2)); // Verify that deleting next to bookmarks doesn't kill them - eqPos(br.find(), {line: 0, ch: 1}); - eqPos(bl.find(), {line: 0, ch: 1}); + eqPos(br.find(), Pos(0, 1)); + eqPos(bl.find(), Pos(0, 1)); }, {value: "abcdef"}); testCM("bug577", function(cm) { @@ -485,14 +487,14 @@ testCM("bug577", function(cm) { testCM("scrollSnap", function(cm) { cm.setSize(100, 100); addDoc(cm, 200, 200); - cm.setCursor({line: 100, ch: 180}); + cm.setCursor(Pos(100, 180)); var info = cm.getScrollInfo(); is(info.left > 0 && info.top > 0); - cm.setCursor({line: 0, ch: 0}); + cm.setCursor(Pos(0, 0)); info = cm.getScrollInfo(); is(info.left == 0 && info.top == 0, "scrolled clean to top"); - cm.setCursor({line: 100, ch: 180}); - cm.setCursor({line: 199, ch: 0}); + cm.setCursor(Pos(100, 180)); + cm.setCursor(Pos(199, 0)); info = cm.getScrollInfo(); is(info.left == 0 && info.top + 2 > info.height - cm.getScrollerElement().clientHeight, "scrolled clean to bottom"); }); @@ -500,9 +502,9 @@ testCM("scrollSnap", function(cm) { testCM("selectionPos", function(cm) { cm.setSize(100, 100); addDoc(cm, 200, 100); - cm.setSelection({line: 1, ch: 100}, {line: 98, ch: 100}); - var lineWidth = cm.charCoords({line: 0, ch: 200}, "local").left; - var lineHeight = (cm.charCoords({line: 99}).top - cm.charCoords({line: 0}).top) / 100; + cm.setSelection(Pos(1, 100), Pos(98, 100)); + var lineWidth = cm.charCoords(Pos(0, 200), "local").left; + var lineHeight = (cm.charCoords(Pos(99)).top - cm.charCoords(Pos(0)).top) / 100; cm.scrollTo(0, 0); var selElt = byClassName(cm.getWrapperElement(), "CodeMirror-selected"); var outer = cm.getWrapperElement().getBoundingClientRect(); @@ -584,7 +586,7 @@ testCM("setSize", function(cm) { }); function foldLines(cm, start, end, autoClear) { - return cm.markText({line: start, ch: 0}, {line: end - 1}, { + return cm.markText(Pos(start, 0), Pos(end - 1), { inclusiveLeft: true, inclusiveRight: true, collapsed: true, @@ -596,47 +598,47 @@ testCM("collapsedLines", function(cm) { addDoc(cm, 4, 10); var range = foldLines(cm, 4, 5), cleared = 0; CodeMirror.on(range, "clear", function() {cleared++;}); - cm.setCursor({line: 3, ch: 0}); + cm.setCursor(Pos(3, 0)); CodeMirror.commands.goLineDown(cm); - eqPos(cm.getCursor(), {line: 5, ch: 0}); + eqPos(cm.getCursor(), Pos(5, 0)); cm.setLine(3, "abcdefg"); - cm.setCursor({line: 3, ch: 6}); + cm.setCursor(Pos(3, 6)); CodeMirror.commands.goLineDown(cm); - eqPos(cm.getCursor(), {line: 5, ch: 4}); + eqPos(cm.getCursor(), Pos(5, 4)); cm.setLine(3, "ab"); - cm.setCursor({line: 3, ch: 2}); + cm.setCursor(Pos(3, 2)); CodeMirror.commands.goLineDown(cm); - eqPos(cm.getCursor(), {line: 5, ch: 2}); + eqPos(cm.getCursor(), Pos(5, 2)); range.clear(); range.clear(); eq(cleared, 1); }); testCM("collapsedRangeCoordsChar", function(cm) { - var pos_1_3 = cm.charCoords({line: 1, ch: 3}); + var pos_1_3 = cm.charCoords(Pos(1, 3)); pos_1_3.left += 2; pos_1_3.top += 2; var opts = {collapsed: true, inclusiveLeft: true, inclusiveRight: true}; - var m1 = cm.markText({line: 0, ch: 0}, {line: 2, ch: 0}, opts); - eqPos(cm.coordsChar(pos_1_3), {line: 3, ch: 3}); + var m1 = cm.markText(Pos(0, 0), Pos(2, 0), opts); + eqPos(cm.coordsChar(pos_1_3), Pos(3, 3)); m1.clear(); - var m1 = cm.markText({line: 0, ch: 0}, {line: 1, ch: 1}, opts); - var m2 = cm.markText({line: 1, ch: 1}, {line: 2, ch: 0}, opts); - eqPos(cm.coordsChar(pos_1_3), {line: 3, ch: 3}); + var m1 = cm.markText(Pos(0, 0), Pos(1, 1), opts); + var m2 = cm.markText(Pos(1, 1), Pos(2, 0), opts); + eqPos(cm.coordsChar(pos_1_3), Pos(3, 3)); m1.clear(); m2.clear(); - var m1 = cm.markText({line: 0, ch: 0}, {line: 1, ch: 6}, opts); - eqPos(cm.coordsChar(pos_1_3), {line: 3, ch: 3}); + var m1 = cm.markText(Pos(0, 0), Pos(1, 6), opts); + eqPos(cm.coordsChar(pos_1_3), Pos(3, 3)); }, {value: "123456\nabcdef\nghijkl\nmnopqr\n"}); testCM("hiddenLinesAutoUnfold", function(cm) { var range = foldLines(cm, 1, 3, true), cleared = 0; CodeMirror.on(range, "clear", function() {cleared++;}); - cm.setCursor({line: 3, ch: 0}); + cm.setCursor(Pos(3, 0)); eq(cleared, 0); cm.execCommand("goCharLeft"); eq(cleared, 1); range = foldLines(cm, 1, 3, true); CodeMirror.on(range, "clear", function() {cleared++;}); - eqPos(cm.getCursor(), {line: 3, ch: 0}); - cm.setCursor({line: 0, ch: 3}); + eqPos(cm.getCursor(), Pos(3, 0)); + cm.setCursor(Pos(0, 3)); cm.execCommand("goCharRight"); eq(cleared, 2); }, {value: "abc\ndef\nghi\njkl"}); @@ -646,8 +648,8 @@ testCM("hiddenLinesSelectAll", function(cm) { // Issue #484 foldLines(cm, 0, 10); foldLines(cm, 11, 20); CodeMirror.commands.selectAll(cm); - eqPos(cm.getCursor(true), {line: 10, ch: 0}); - eqPos(cm.getCursor(false), {line: 10, ch: 4}); + eqPos(cm.getCursor(true), Pos(10, 0)); + eqPos(cm.getCursor(false), Pos(10, 4)); }); @@ -668,18 +670,18 @@ testCM("everythingFolded", function(cm) { testCM("structuredFold", function(cm) { addDoc(cm, 4, 8); - var range = cm.markText({line: 1, ch: 2}, {line: 6, ch: 2}, { + var range = cm.markText(Pos(1, 2), Pos(6, 2), { replacedWith: document.createTextNode("Q") }); cm.setCursor(0, 3); CodeMirror.commands.goLineDown(cm); - eqPos(cm.getCursor(), {line: 6, ch: 2}); + eqPos(cm.getCursor(), Pos(6, 2)); CodeMirror.commands.goCharLeft(cm); - eqPos(cm.getCursor(), {line: 1, ch: 2}); + eqPos(cm.getCursor(), Pos(1, 2)); CodeMirror.commands.delCharAfter(cm); eq(cm.getValue(), "xxxx\nxxxx\nxxxx"); addDoc(cm, 4, 8); - range = cm.markText({line: 1, ch: 2}, {line: 6, ch: 2}, { + range = cm.markText(Pos(1, 2), Pos(6, 2), { replacedWith: document.createTextNode("x"), clearOnEnter: true }); @@ -687,70 +689,70 @@ testCM("structuredFold", function(cm) { CodeMirror.on(range, "clear", function(){++cleared;}); cm.setCursor(0, 3); CodeMirror.commands.goLineDown(cm); - eqPos(cm.getCursor(), {line: 6, ch: 2}); + eqPos(cm.getCursor(), Pos(6, 2)); CodeMirror.commands.goCharLeft(cm); - eqPos(cm.getCursor(), {line: 6, ch: 1}); + eqPos(cm.getCursor(), Pos(6, 1)); eq(cleared, 1); range.clear(); eq(cleared, 1); - range = cm.markText({line: 1, ch: 2}, {line: 6, ch: 2}, { + range = cm.markText(Pos(1, 2), Pos(6, 2), { replacedWith: document.createTextNode("Q"), clearOnEnter: true }); range.clear(); cm.setCursor(1, 2); CodeMirror.commands.goCharRight(cm); - eqPos(cm.getCursor(), {line: 1, ch: 3}); + eqPos(cm.getCursor(), Pos(1, 3)); }, null); testCM("nestedFold", function(cm) { addDoc(cm, 10, 3); function fold(ll, cl, lr, cr) { - return cm.markText({line: ll, ch: cl}, {line: lr, ch: cr}, {collapsed: true}); + return cm.markText(Pos(ll, cl), Pos(lr, cr), {collapsed: true}); } var inner1 = fold(0, 6, 1, 3), inner2 = fold(0, 2, 1, 8), outer = fold(0, 1, 2, 3), inner0 = fold(0, 5, 0, 6); cm.setCursor(0, 1); CodeMirror.commands.goCharRight(cm); - eqPos(cm.getCursor(), {line: 2, ch: 3}); + eqPos(cm.getCursor(), Pos(2, 3)); inner0.clear(); CodeMirror.commands.goCharLeft(cm); - eqPos(cm.getCursor(), {line: 0, ch: 1}); + eqPos(cm.getCursor(), Pos(0, 1)); outer.clear(); CodeMirror.commands.goCharRight(cm); - eqPos(cm.getCursor(), {line: 0, ch: 2}); + eqPos(cm.getCursor(), Pos(0, 2)); CodeMirror.commands.goCharRight(cm); - eqPos(cm.getCursor(), {line: 1, ch: 8}); + eqPos(cm.getCursor(), Pos(1, 8)); inner2.clear(); CodeMirror.commands.goCharLeft(cm); - eqPos(cm.getCursor(), {line: 1, ch: 7}); + eqPos(cm.getCursor(), Pos(1, 7)); cm.setCursor(0, 5); CodeMirror.commands.goCharRight(cm); - eqPos(cm.getCursor(), {line: 0, ch: 6}); + eqPos(cm.getCursor(), Pos(0, 6)); CodeMirror.commands.goCharRight(cm); - eqPos(cm.getCursor(), {line: 1, ch: 3}); + eqPos(cm.getCursor(), Pos(1, 3)); }); testCM("badNestedFold", function(cm) { addDoc(cm, 4, 4); - cm.markText({line: 0, ch: 2}, {line: 3, ch: 2}, {collapsed: true}); + cm.markText(Pos(0, 2), Pos(3, 2), {collapsed: true}); var caught; - try {cm.markText({line: 0, ch: 1}, {line: 0, ch: 3}, {collapsed: true});} + try {cm.markText(Pos(0, 1), Pos(0, 3), {collapsed: true});} catch(e) {caught = e;} is(caught instanceof Error, "no error"); is(/overlap/i.test(caught.message), "wrong error"); }); testCM("inlineWidget", function(cm) { - var w = cm.setBookmark({line: 0, ch: 2}, {widget: document.createTextNode("uu")}); + var w = cm.setBookmark(Pos(0, 2), {widget: document.createTextNode("uu")}); cm.setCursor(0, 2); CodeMirror.commands.goLineDown(cm); - eqPos(cm.getCursor(), {line: 1, ch: 4}); + eqPos(cm.getCursor(), Pos(1, 4)); cm.setCursor(0, 2); cm.replaceSelection("hi"); - eqPos(w.find(), {line: 0, ch: 2}); + eqPos(w.find(), Pos(0, 2)); cm.setCursor(0, 1); cm.replaceSelection("ay"); - eqPos(w.find(), {line: 0, ch: 4}); + eqPos(w.find(), Pos(0, 4)); eq(cm.getLine(0), "uayuhiuu"); }, {value: "uuuu\nuuuuuu"}); @@ -760,7 +762,7 @@ testCM("wrappingAndResizing", function(cm) { var wrap = cm.getWrapperElement(), h0 = wrap.offsetHeight; var doc = "xxx xxx xxx xxx xxx"; cm.setValue(doc); - for (var step = 10, w = cm.charCoords({line: 0, ch: 18}, "div").right;; w += step) { + for (var step = 10, w = cm.charCoords(Pos(0, 18), "div").right;; w += step) { cm.setSize(w); if (wrap.offsetHeight <= h0 * (opera_lt10 ? 1.2 : 1.5)) { if (step == 10) { w -= 10; step = 1; } @@ -769,7 +771,7 @@ testCM("wrappingAndResizing", function(cm) { } // Ensure that putting the cursor at the end of the maximally long // line doesn't cause wrapping to happen. - cm.setCursor({line: 0, ch: doc.length}); + cm.setCursor(Pos(0, doc.length)); eq(wrap.offsetHeight, h0); cm.replaceSelection("x"); is(wrap.offsetHeight > h0, "wrapping happens"); @@ -777,9 +779,9 @@ testCM("wrappingAndResizing", function(cm) { // almost-wrapped lines, go over it so that a scrollbar appears. cm.setValue(doc + "\n" + doc + "\n"); cm.getScrollerElement().style.maxHeight = "100px"; - cm.replaceRange("\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n!\n", {line: 2, ch: 0}); - forEach([{line: 0, ch: doc.length}, {line: 0, ch: doc.length - 1}, - {line: 0, ch: 0}, {line: 1, ch: doc.length}, {line: 1, ch: doc.length - 1}], + cm.replaceRange("\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n!\n", Pos(2, 0)); + forEach([Pos(0, doc.length), Pos(0, doc.length - 1), + Pos(0, 0), Pos(1, doc.length), Pos(1, doc.length - 1)], function(pos) { var coords = cm.charCoords(pos); eqPos(pos, cm.coordsChar({left: coords.left + 2, top: coords.top + 5})); @@ -790,7 +792,7 @@ testCM("measureEndOfLine", function(cm) { cm.setSize(null, "auto"); var inner = byClassName(cm.getWrapperElement(), "CodeMirror-lines")[0].firstChild; var lh = inner.offsetHeight; - for (var step = 10, w = cm.charCoords({line: 0, ch: 7}, "div").right;; w += step) { + for (var step = 10, w = cm.charCoords(Pos(0, 7), "div").right;; w += step) { cm.setSize(w); if (inner.offsetHeight < 2.5 * lh) { if (step == 10) { w -= 10; step = 1; } @@ -798,11 +800,11 @@ testCM("measureEndOfLine", function(cm) { } } cm.setValue(cm.getValue() + "\n\n"); - var endPos = cm.charCoords({line: 0, ch: 18}, "local"); + var endPos = cm.charCoords(Pos(0, 18), "local"); is(endPos.top > lh * .8, "not at top"); is(endPos.left > w - 20, "not at right"); - endPos = cm.charCoords({line: 0, ch: 18}); - eqPos(cm.coordsChar({left: endPos.left, top: endPos.top + 5}), {line: 0, ch: 18}); + endPos = cm.charCoords(Pos(0, 18)); + eqPos(cm.coordsChar({left: endPos.left, top: endPos.top + 5}), Pos(0, 18)); }, {mode: "text/html", value: "", lineWrapping: true}, ie_lt8 || opera_lt10); testCM("scrollVerticallyAndHorizontally", function(cm) { @@ -821,19 +823,19 @@ testCM("moveVstuck", function(cm) { var lines = byClassName(cm.getWrapperElement(), "CodeMirror-lines")[0].firstChild, h0 = lines.offsetHeight; var val = "fooooooooooooooooooooooooo baaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaar\n"; cm.setValue(val); - for (var w = cm.charCoords({line: 0, ch: 26}, "div").right * 2.8;; w += 5) { + for (var w = cm.charCoords(Pos(0, 26), "div").right * 2.8;; w += 5) { cm.setSize(w); if (lines.offsetHeight <= 3.5 * h0) break; } - cm.setCursor({line: 0, ch: val.length - 1}); + cm.setCursor(Pos(0, val.length - 1)); cm.moveV(-1, "line"); - eqPos(cm.getCursor(), {line: 0, ch: 26}); + eqPos(cm.getCursor(), Pos(0, 26)); }, {lineWrapping: true}, ie_lt8 || opera_lt10); testCM("clickTab", function(cm) { - var p0 = cm.charCoords({line: 0, ch: 0}); - eqPos(cm.coordsChar({left: p0.left + 5, top: p0.top + 5}), {line: 0, ch: 0}); - eqPos(cm.coordsChar({left: p0.right - 5, top: p0.top + 5}), {line: 0, ch: 1}); + var p0 = cm.charCoords(Pos(0, 0)); + eqPos(cm.coordsChar({left: p0.left + 5, top: p0.top + 5}), Pos(0, 0)); + eqPos(cm.coordsChar({left: p0.right - 5, top: p0.top + 5}), Pos(0, 1)); }, {value: "\t\n\n", lineWrapping: true, tabSize: 8}); testCM("verticalScroll", function(cm) { @@ -885,76 +887,76 @@ testCM("extraKeys", function(cm) { testCM("wordMovementCommands", function(cm) { cm.execCommand("goWordLeft"); - eqPos(cm.getCursor(), {line: 0, ch: 0}); + eqPos(cm.getCursor(), Pos(0, 0)); cm.execCommand("goWordRight"); cm.execCommand("goWordRight"); - eqPos(cm.getCursor(), {line: 0, ch: 7}); + eqPos(cm.getCursor(), Pos(0, 7)); cm.execCommand("goWordLeft"); - eqPos(cm.getCursor(), {line: 0, ch: 5}); + eqPos(cm.getCursor(), Pos(0, 5)); cm.execCommand("goWordRight"); cm.execCommand("goWordRight"); - eqPos(cm.getCursor(), {line: 0, ch: 12}); + eqPos(cm.getCursor(), Pos(0, 12)); cm.execCommand("goWordLeft"); - eqPos(cm.getCursor(), {line: 0, ch: 9}); + eqPos(cm.getCursor(), Pos(0, 9)); cm.execCommand("goWordRight"); cm.execCommand("goWordRight"); cm.execCommand("goWordRight"); - eqPos(cm.getCursor(), {line: 1, ch: 1}); + eqPos(cm.getCursor(), Pos(1, 1)); cm.execCommand("goWordRight"); - eqPos(cm.getCursor(), {line: 1, ch: 9}); + eqPos(cm.getCursor(), Pos(1, 9)); cm.execCommand("goWordRight"); - eqPos(cm.getCursor(), {line: 1, ch: 13}); + eqPos(cm.getCursor(), Pos(1, 13)); cm.execCommand("goWordRight"); cm.execCommand("goWordRight"); - eqPos(cm.getCursor(), {line: 2, ch: 0}); + eqPos(cm.getCursor(), Pos(2, 0)); }, {value: "this is (the) firstline.\na foo12\u00e9\u00f8\u00d7bar\n"}); testCM("charMovementCommands", function(cm) { cm.execCommand("goCharLeft"); cm.execCommand("goColumnLeft"); - eqPos(cm.getCursor(), {line: 0, ch: 0}); + eqPos(cm.getCursor(), Pos(0, 0)); cm.execCommand("goCharRight"); cm.execCommand("goCharRight"); - eqPos(cm.getCursor(), {line: 0, ch: 2}); - cm.setCursor({line: 1, ch: 0}); + eqPos(cm.getCursor(), Pos(0, 2)); + cm.setCursor(Pos(1, 0)); cm.execCommand("goColumnLeft"); - eqPos(cm.getCursor(), {line: 1, ch: 0}); + eqPos(cm.getCursor(), Pos(1, 0)); cm.execCommand("goCharLeft"); - eqPos(cm.getCursor(), {line: 0, ch: 5}); + eqPos(cm.getCursor(), Pos(0, 5)); cm.execCommand("goColumnRight"); - eqPos(cm.getCursor(), {line: 0, ch: 5}); + eqPos(cm.getCursor(), Pos(0, 5)); cm.execCommand("goCharRight"); - eqPos(cm.getCursor(), {line: 1, ch: 0}); + eqPos(cm.getCursor(), Pos(1, 0)); cm.execCommand("goLineEnd"); - eqPos(cm.getCursor(), {line: 1, ch: 5}); + eqPos(cm.getCursor(), Pos(1, 5)); cm.execCommand("goLineStartSmart"); - eqPos(cm.getCursor(), {line: 1, ch: 1}); + eqPos(cm.getCursor(), Pos(1, 1)); cm.execCommand("goLineStartSmart"); - eqPos(cm.getCursor(), {line: 1, ch: 0}); - cm.setCursor({line: 2, ch: 0}); + eqPos(cm.getCursor(), Pos(1, 0)); + cm.setCursor(Pos(2, 0)); cm.execCommand("goCharRight"); cm.execCommand("goColumnRight"); - eqPos(cm.getCursor(), {line: 2, ch: 0}); + eqPos(cm.getCursor(), Pos(2, 0)); }, {value: "line1\n ine2\n"}); testCM("verticalMovementCommands", function(cm) { cm.execCommand("goLineUp"); - eqPos(cm.getCursor(), {line: 0, ch: 0}); + eqPos(cm.getCursor(), Pos(0, 0)); cm.execCommand("goLineDown"); if (!phantom) // This fails in PhantomJS, though not in a real Webkit - eqPos(cm.getCursor(), {line: 1, ch: 0}); - cm.setCursor({line: 1, ch: 12}); + eqPos(cm.getCursor(), Pos(1, 0)); + cm.setCursor(Pos(1, 12)); cm.execCommand("goLineDown"); - eqPos(cm.getCursor(), {line: 2, ch: 5}); + eqPos(cm.getCursor(), Pos(2, 5)); cm.execCommand("goLineDown"); - eqPos(cm.getCursor(), {line: 3, ch: 0}); + eqPos(cm.getCursor(), Pos(3, 0)); cm.execCommand("goLineUp"); - eqPos(cm.getCursor(), {line: 2, ch: 5}); + eqPos(cm.getCursor(), Pos(2, 5)); cm.execCommand("goLineUp"); - eqPos(cm.getCursor(), {line: 1, ch: 12}); + eqPos(cm.getCursor(), Pos(1, 12)); cm.execCommand("goPageDown"); - eqPos(cm.getCursor(), {line: 5, ch: 0}); + eqPos(cm.getCursor(), Pos(5, 0)); cm.execCommand("goPageDown"); cm.execCommand("goLineDown"); - eqPos(cm.getCursor(), {line: 5, ch: 0}); + eqPos(cm.getCursor(), Pos(5, 0)); cm.execCommand("goPageUp"); - eqPos(cm.getCursor(), {line: 0, ch: 0}); + eqPos(cm.getCursor(), Pos(0, 0)); }, {value: "line1\nlong long line2\nline3\n\nline5\n"}); testCM("verticalMovementCommandsWrapping", function(cm) { cm.setSize(120); - cm.setCursor({line: 0, ch: 5}); + cm.setCursor(Pos(0, 5)); cm.execCommand("goLineDown"); eq(cm.getCursor().line, 0); is(cm.getCursor().ch > 5, "moved beyond wrap"); @@ -993,25 +995,25 @@ testCM("rtlMovement", function(cm) { // Verify that updating a line clears its bidi ordering testCM("bidiUpdate", function(cm) { - cm.setCursor({line: 0, ch: 2}); + cm.setCursor(Pos(0, 2)); cm.replaceSelection("خحج", "start"); cm.execCommand("goCharRight"); - eqPos(cm.getCursor(), {line: 0, ch: 4}); + eqPos(cm.getCursor(), Pos(0, 4)); }, {value: "abcd\n"}); testCM("movebyTextUnit", function(cm) { cm.setValue("בְּרֵאשִ\ńéée\n"); cm.execCommand("goLineEnd"); for (var i = 0; i < 4; ++i) cm.execCommand("goCharRight"); - eqPos(cm.getCursor(), {line: 0, ch: 0}); + eqPos(cm.getCursor(), Pos(0, 0)); cm.execCommand("goCharRight"); - eqPos(cm.getCursor(), {line: 1, ch: 0}); + eqPos(cm.getCursor(), Pos(1, 0)); cm.execCommand("goCharRight"); cm.execCommand("goCharRight"); - eqPos(cm.getCursor(), {line: 1, ch: 3}); + eqPos(cm.getCursor(), Pos(1, 3)); cm.execCommand("goCharRight"); cm.execCommand("goCharRight"); - eqPos(cm.getCursor(), {line: 1, ch: 6}); + eqPos(cm.getCursor(), Pos(1, 6)); }); testCM("lineChangeEvents", function(cm) { @@ -1025,10 +1027,10 @@ testCM("lineChangeEvents", function(cm) { return function() {log.push("ch " + i);}; }(i)); } - cm.replaceRange("x", {line: 0, ch: 1}); - cm.replaceRange("xy", {line: 1, ch: 1}, {line: 2}); - cm.replaceRange("foo\nbar", {line: 0, ch: 1}); - cm.replaceRange("", {line: 0, ch: 0}, {line: cm.lineCount()}); + cm.replaceRange("x", Pos(0, 1)); + cm.replaceRange("xy", Pos(1, 1), Pos(2)); + cm.replaceRange("foo\nbar", Pos(0, 1)); + cm.replaceRange("", Pos(0, 0), Pos(cm.lineCount())); eq(log.length, want.length, "same length"); for (var i = 0; i < log.length; ++i) eq(log[i], want[i]); @@ -1036,23 +1038,23 @@ testCM("lineChangeEvents", function(cm) { testCM("scrollEntirelyToRight", function(cm) { addDoc(cm, 500, 2); - cm.setCursor({line: 0, ch: 500}); + cm.setCursor(Pos(0, 500)); var wrap = cm.getWrapperElement(), cur = byClassName(wrap, "CodeMirror-cursor")[0]; is(wrap.getBoundingClientRect().right > cur.getBoundingClientRect().left); }); testCM("lineWidgets", function(cm) { addDoc(cm, 500, 3); - var last = cm.charCoords({line: 2, ch: 0}); + var last = cm.charCoords(Pos(2, 0)); var node = document.createElement("div"); node.innerHTML = "hi"; var widget = cm.addLineWidget(1, node); - is(last.top < cm.charCoords({line: 2, ch: 0}).top, "took up space"); - cm.setCursor({line: 1, ch: 1}); + is(last.top < cm.charCoords(Pos(2, 0)).top, "took up space"); + cm.setCursor(Pos(1, 1)); cm.execCommand("goLineDown"); - eqPos(cm.getCursor(), {line: 2, ch: 1}); + eqPos(cm.getCursor(), Pos(2, 1)); cm.execCommand("goLineUp"); - eqPos(cm.getCursor(), {line: 1, ch: 1}); + eqPos(cm.getCursor(), Pos(1, 1)); }); testCM("lineWidgetFocus", function(cm) { @@ -1064,7 +1066,7 @@ testCM("lineWidgetFocus", function(cm) { var widget = cm.addLineWidget(1, node); node.focus(); eq(document.activeElement, node); - cm.replaceRange("new stuff", {line: 1, ch: 0}); + cm.replaceRange("new stuff", Pos(1, 0)); eq(document.activeElement, node); } finally { place.className = ""; @@ -1075,7 +1077,7 @@ testCM("getLineNumber", function(cm) { addDoc(cm, 2, 20); var h1 = cm.getLineHandle(1); eq(cm.getLineNumber(h1), 1); - cm.replaceRange("hi\nbye\n", {line: 0, ch: 0}); + cm.replaceRange("hi\nbye\n", Pos(0, 0)); eq(cm.getLineNumber(h1), 3); cm.setValue(""); eq(cm.getLineNumber(h1), null); @@ -1088,27 +1090,27 @@ testCM("jumpTheGap", function(cm) { cm.setSize("200px", null); cm.getWrapperElement().style.lineHeight = 2; cm.refresh(); - cm.setCursor({line: 0, ch: 1}); + cm.setCursor(Pos(0, 1)); cm.execCommand("goLineDown"); - eqPos(cm.getCursor(), {line: 1, ch: 1}); + eqPos(cm.getCursor(), Pos(1, 1)); cm.execCommand("goLineDown"); - eqPos(cm.getCursor(), {line: 2, ch: 1}); + eqPos(cm.getCursor(), Pos(2, 1)); cm.execCommand("goLineDown"); eq(cm.getCursor().line, 2); is(cm.getCursor().ch > 1); cm.execCommand("goLineUp"); - eqPos(cm.getCursor(), {line: 2, ch: 1}); + eqPos(cm.getCursor(), Pos(2, 1)); cm.execCommand("goLineUp"); - eqPos(cm.getCursor(), {line: 1, ch: 1}); + eqPos(cm.getCursor(), Pos(1, 1)); var node = document.createElement("div"); node.innerHTML = "hi"; node.style.height = "30px"; cm.addLineWidget(0, node); cm.addLineWidget(1, node.cloneNode(true), {above: true}); - cm.setCursor({line: 0, ch: 2}); + cm.setCursor(Pos(0, 2)); cm.execCommand("goLineDown"); - eqPos(cm.getCursor(), {line: 1, ch: 2}); + eqPos(cm.getCursor(), Pos(1, 2)); cm.execCommand("goLineUp"); - eqPos(cm.getCursor(), {line: 0, ch: 2}); + eqPos(cm.getCursor(), Pos(0, 2)); }, {lineWrapping: true, value: "abc\ndef\nghi\njkl\n"}); testCM("addLineClass", function(cm) { @@ -1143,82 +1145,82 @@ testCM("addLineClass", function(cm) { testCM("atomicMarker", function(cm) { addDoc(cm, 10, 10); function atom(ll, cl, lr, cr, li, ri) { - return cm.markText({line: ll, ch: cl}, {line: lr, ch: cr}, + return cm.markText(Pos(ll, cl), Pos(lr, cr), {atomic: true, inclusiveLeft: li, inclusiveRight: ri}); } var m = atom(0, 1, 0, 5); - cm.setCursor({line: 0, ch: 1}); + cm.setCursor(Pos(0, 1)); cm.execCommand("goCharRight"); - eqPos(cm.getCursor(), {line: 0, ch: 5}); + eqPos(cm.getCursor(), Pos(0, 5)); cm.execCommand("goCharLeft"); - eqPos(cm.getCursor(), {line: 0, ch: 1}); + eqPos(cm.getCursor(), Pos(0, 1)); m.clear(); m = atom(0, 0, 0, 5, true); - eqPos(cm.getCursor(), {line: 0, ch: 5}, "pushed out"); + eqPos(cm.getCursor(), Pos(0, 5), "pushed out"); cm.execCommand("goCharLeft"); - eqPos(cm.getCursor(), {line: 0, ch: 5}); + eqPos(cm.getCursor(), Pos(0, 5)); m.clear(); m = atom(8, 4, 9, 10, false, true); - cm.setCursor({line: 9, ch: 8}); - eqPos(cm.getCursor(), {line: 8, ch: 4}, "set"); + cm.setCursor(Pos(9, 8)); + eqPos(cm.getCursor(), Pos(8, 4), "set"); cm.execCommand("goCharRight"); - eqPos(cm.getCursor(), {line: 8, ch: 4}, "char right"); + eqPos(cm.getCursor(), Pos(8, 4), "char right"); cm.execCommand("goLineDown"); - eqPos(cm.getCursor(), {line: 8, ch: 4}, "line down"); + eqPos(cm.getCursor(), Pos(8, 4), "line down"); cm.execCommand("goCharLeft"); - eqPos(cm.getCursor(), {line: 8, ch: 3}); + eqPos(cm.getCursor(), Pos(8, 3)); m.clear(); m = atom(1, 1, 3, 8); - cm.setCursor({line: 2, ch: 0}); - eqPos(cm.getCursor(), {line: 3, ch: 8}); + cm.setCursor(Pos(2, 0)); + eqPos(cm.getCursor(), Pos(3, 8)); cm.execCommand("goCharLeft"); - eqPos(cm.getCursor(), {line: 1, ch: 1}); + eqPos(cm.getCursor(), Pos(1, 1)); cm.execCommand("goCharRight"); - eqPos(cm.getCursor(), {line: 3, ch: 8}); + eqPos(cm.getCursor(), Pos(3, 8)); cm.execCommand("goLineUp"); - eqPos(cm.getCursor(), {line: 1, ch: 1}); + eqPos(cm.getCursor(), Pos(1, 1)); cm.execCommand("goLineDown"); - eqPos(cm.getCursor(), {line: 3, ch: 8}); + eqPos(cm.getCursor(), Pos(3, 8)); cm.execCommand("delCharBefore"); eq(cm.getValue().length, 80, "del chunk"); m = atom(3, 0, 5, 5); - cm.setCursor({line: 3, ch: 0}); + cm.setCursor(Pos(3, 0)); cm.execCommand("delWordAfter"); eq(cm.getValue().length, 53, "del chunk"); }); testCM("readOnlyMarker", function(cm) { function mark(ll, cl, lr, cr, at) { - return cm.markText({line: ll, ch: cl}, {line: lr, ch: cr}, + return cm.markText(Pos(ll, cl), Pos(lr, cr), {readOnly: true, atomic: at}); } var m = mark(0, 1, 0, 4); - cm.setCursor({line: 0, ch: 2}); + cm.setCursor(Pos(0, 2)); cm.replaceSelection("hi", "end"); - eqPos(cm.getCursor(), {line: 0, ch: 2}); + eqPos(cm.getCursor(), Pos(0, 2)); eq(cm.getLine(0), "abcde"); cm.execCommand("selectAll"); cm.replaceSelection("oops"); eq(cm.getValue(), "oopsbcd"); cm.undo(); - eqPos(m.find().from, {line: 0, ch: 1}); - eqPos(m.find().to, {line: 0, ch: 4}); + eqPos(m.find().from, Pos(0, 1)); + eqPos(m.find().to, Pos(0, 4)); m.clear(); - cm.setCursor({line: 0, ch: 2}); + cm.setCursor(Pos(0, 2)); cm.replaceSelection("hi"); eq(cm.getLine(0), "abhicde"); - eqPos(cm.getCursor(), {line: 0, ch: 4}); + eqPos(cm.getCursor(), Pos(0, 4)); m = mark(0, 2, 2, 2, true); - cm.setSelection({line: 1, ch: 1}, {line: 2, ch: 4}); + cm.setSelection(Pos(1, 1), Pos(2, 4)); cm.replaceSelection("t", "end"); - eqPos(cm.getCursor(), {line: 2, ch: 3}); + eqPos(cm.getCursor(), Pos(2, 3)); eq(cm.getLine(2), "klto"); cm.execCommand("goCharLeft"); cm.execCommand("goCharLeft"); - eqPos(cm.getCursor(), {line: 0, ch: 2}); - cm.setSelection({line: 0, ch: 1}, {line: 0, ch: 3}); + eqPos(cm.getCursor(), Pos(0, 2)); + cm.setSelection(Pos(0, 1), Pos(0, 3)); cm.replaceSelection("xx"); - eqPos(cm.getCursor(), {line: 0, ch: 3}); + eqPos(cm.getCursor(), Pos(0, 3)); eq(cm.getLine(0), "axxhicde"); }, {value: "abcde\nfghij\nklmno\n"}); @@ -1247,12 +1249,12 @@ testCM("addKeyMap", function(cm) { } sendKey(39); - eqPos(cm.getCursor(), {line: 0, ch: 1}); + eqPos(cm.getCursor(), Pos(0, 1)); var test = 0; var map1 = {Right: function() { ++test; }}, map2 = {Right: function() { test += 10; }} cm.addKeyMap(map1); sendKey(39); - eqPos(cm.getCursor(), {line: 0, ch: 1}); + eqPos(cm.getCursor(), Pos(0, 1)); eq(test, 1); cm.addKeyMap(map2); sendKey(39); @@ -1263,11 +1265,11 @@ testCM("addKeyMap", function(cm) { cm.removeKeyMap(map2); sendKey(39); eq(test, 12); - eqPos(cm.getCursor(), {line: 0, ch: 2}); + eqPos(cm.getCursor(), Pos(0, 2)); cm.addKeyMap({Right: function() { test = 55; }, name: "mymap"}); sendKey(39); eq(test, 55); cm.removeKeyMap("mymap"); sendKey(39); - eqPos(cm.getCursor(), {line: 0, ch: 3}); + eqPos(cm.getCursor(), Pos(0, 3)); }, {value: "abc"}); From 8026d5cd5fbf82018008e5b3c1934a2714b8a1af Mon Sep 17 00:00:00 2001 From: Marijn Haverbeke Date: Wed, 6 Feb 2013 14:33:34 +0100 Subject: [PATCH 0678/5780] [show-hint addon] Support PageUp/PageDown/Home/End keys when dialog is active --- addon/hint/show-hint.css | 1 + addon/hint/show-hint.js | 8 ++++++++ 2 files changed, 9 insertions(+) diff --git a/addon/hint/show-hint.css b/addon/hint/show-hint.css index 55661c9c0c..8a4ff052e3 100644 --- a/addon/hint/show-hint.css +++ b/addon/hint/show-hint.css @@ -11,6 +11,7 @@ -moz-box-shadow: 2px 3px 5px rgba(0,0,0,.2); box-shadow: 2px 3px 5px rgba(0,0,0,.2); border-radius: 3px; + border: 1px solid silver; background: white; font-size: 90%; diff --git a/addon/hint/show-hint.js b/addon/hint/show-hint.js index dc381d8510..38dbbf5335 100644 --- a/addon/hint/show-hint.js +++ b/addon/hint/show-hint.js @@ -51,9 +51,17 @@ CodeMirror.showHint = function(cm, getHints, options) { hints.scrollTop = node.offsetTop + node.offsetHeight - hints.clientHeight + 3; } + function screenAmount() { + return Math.floor(hints.clientHeight / hints.firstChild.offsetHeight) || 1; + } + var ourMap = { Up: function() {changeActive(selectedHint - 1);}, Down: function() {changeActive(selectedHint + 1);}, + PageUp: function() {changeActive(selectedHint - screenAmount());}, + PageDown: function() {changeActive(selectedHint + screenAmount());}, + Home: function() {changeActive(0);}, + End: function() {changeActive(completions.length - 1);}, Enter: pick, Tab: pick, Esc: close From 3694587040daf791c13c7d41cdc7ce350385c7c8 Mon Sep 17 00:00:00 2001 From: Marijn Haverbeke Date: Wed, 6 Feb 2013 14:35:31 +0100 Subject: [PATCH 0679/5780] [show-hint addon] Don't close hinter when typing first letter of word --- addon/hint/show-hint.js | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/addon/hint/show-hint.js b/addon/hint/show-hint.js index 38dbbf5335..c1347a9421 100644 --- a/addon/hint/show-hint.js +++ b/addon/hint/show-hint.js @@ -8,7 +8,7 @@ CodeMirror.showHint = function(cm, getHints, options) { var token = cm.getTokenAt(cm.getCursor()); // Don't show completions if token has changed - if (previousToken != null && + if (previousToken != null && /\w/.test(previousToken.string) && (token.start != previousToken.start || token.type != previousToken.type)) return; From 1648e9d7b2e2fd4979f941f6c9a5ee6673432bb5 Mon Sep 17 00:00:00 2001 From: Xavier Mendez Date: Wed, 6 Feb 2013 14:07:01 +0100 Subject: [PATCH 0680/5780] Roll up the active-line addon! --- addon/selection/active-line.js | 39 ++++++++++++++++++++++++++++++++++ demo/activeline.html | 12 +++-------- 2 files changed, 42 insertions(+), 9 deletions(-) create mode 100644 addon/selection/active-line.js diff --git a/addon/selection/active-line.js b/addon/selection/active-line.js new file mode 100644 index 0000000000..988a0ffbf7 --- /dev/null +++ b/addon/selection/active-line.js @@ -0,0 +1,39 @@ +// Because sometimes you need to style the cursor's line. +// +// Adds an option 'styleActiveLine' which, when enabled, gives the +// active line's wrapping
    the CSS class "CodeMirror-activeline", +// and gives its background
    the class "CodeMirror-activeline-background". + +(function() { + "use strict"; + var WRAP_CLASS = "CodeMirror-activeline"; + var BACK_CLASS = "CodeMirror-activeline-background"; + + CodeMirror.defineOption("styleActiveLine", false, function(cm, val, old) { + var prev = old && old != CodeMirror.Init; + if (val && !prev) { + updateActiveLine(cm); + cm.on("cursorActivity", updateActiveLine); + } else if (!val && prev) { + cm.off("cursorActivity", updateActiveLine); + clearActiveLine(cm); + delete cm._activeLine; + } + }); + + function clearActiveLine(cm) { + if ("_activeLine" in cm) { + cm.removeLineClass(cm._activeLine, "wrap", WRAP_CLASS); + cm.removeLineClass(cm._activeLine, "background", BACK_CLASS); + } + } + + function updateActiveLine(cm) { + var line = cm.getLineHandle(cm.getCursor().line); + if (cm._activeLine == line) return; + clearActiveLine(cm); + cm.addLineClass(line, "wrap", WRAP_CLASS); + cm.addLineClass(line, "background", BACK_CLASS); + cm._activeLine = line; + } +})(); diff --git a/demo/activeline.html b/demo/activeline.html index 457d69cad8..b0ea9b9070 100644 --- a/demo/activeline.html +++ b/demo/activeline.html @@ -6,11 +6,12 @@ + @@ -57,17 +58,10 @@

    CodeMirror: Active Line Demo

    Styling the current cursor line.

    From 9bf595221d5e35cc4a89dd2b6b09d0d707b9ad11 Mon Sep 17 00:00:00 2001 From: Xavier Mendez Date: Wed, 6 Feb 2013 14:30:08 +0100 Subject: [PATCH 0681/5780] Fix some issues on mark-selection - When mark-selection is deactivated the mark was not removed. - When mark-selection is [re]activated it immediately calls updateSelectedText() to set the mark if there's already a selection. - In addition to removing the mark, the handle is completely deleted to leave the CodeMirror instance in a clean state, and avoid re-deleting the mark if it's activated again. --- addon/selection/mark-selection.js | 14 +++++++++++--- 1 file changed, 11 insertions(+), 3 deletions(-) diff --git a/addon/selection/mark-selection.js b/addon/selection/mark-selection.js index 7342135545..d7ff30c9a9 100644 --- a/addon/selection/mark-selection.js +++ b/addon/selection/mark-selection.js @@ -8,14 +8,22 @@ CodeMirror.defineOption("styleSelectedText", false, function(cm, val, old) { var prev = old && old != CodeMirror.Init; - if (val && !prev) + if (val && !prev) { + updateSelectedText(cm); cm.on("cursorActivity", updateSelectedText); - else if (!val && prev) + } else if (!val && prev) { cm.off("cursorActivity", updateSelectedText); + clearSelectedText(cm); + delete cm._selectionMark; + } }); - function updateSelectedText(cm) { + function clearSelectedText(cm) { if (cm._selectionMark) cm._selectionMark.clear(); + } + + function updateSelectedText(cm) { + clearSelectedText(cm); if (cm.somethingSelected()) cm._selectionMark = cm.markText(cm.getCursor("start"), cm.getCursor("end"), From c708ad73e67521b0d880b02df95c4f026dac28bf Mon Sep 17 00:00:00 2001 From: Xavier Mendez Date: Wed, 6 Feb 2013 14:30:30 +0100 Subject: [PATCH 0682/5780] Talk about mark-selection and active-line on the manual. --- doc/manual.html | 10 ++++++++++ 1 file changed, 10 insertions(+) diff --git a/doc/manual.html b/doc/manual.html index 632e010ca1..9dcc68abc8 100644 --- a/doc/manual.html +++ b/doc/manual.html @@ -1416,6 +1416,16 @@

    Add-ons

    can be enabled to highlight all instances of a currently selected word. Demo here.
    +
    mark-selection.js
    +
    Causes the selected text to be marked with the CSS class + CodeMirror-selectedtext when the styleSelectedText option + is enabled. Useful to change the colour of the selection (in addition to the background), + like in this demo.
    +
    active-line.js
    +
    Defines a styleActiveLine option that, when enabled, + gives the wrapper of the active line the class CodeMirror-activeline, + and adds a background with the class CodeMirror-activeline-background. + is enabled. See the demo.
    formatting.js
    Adds commentRange, autoIndentRange, and autoFormatRange methods that, respectively, From 510edf43e05abb9e87cefeec873092c17c211c83 Mon Sep 17 00:00:00 2001 From: Marijn Haverbeke Date: Wed, 6 Feb 2013 16:12:51 +0100 Subject: [PATCH 0683/5780] Add a bi-directional text demo Closes #1180 --- demo/bidi.html | 61 ++++++++++++++++++++++++++++++++++++++++++++++++++ index.html | 1 + 2 files changed, 62 insertions(+) create mode 100644 demo/bidi.html diff --git a/demo/bidi.html b/demo/bidi.html new file mode 100644 index 0000000000..47feb8c5aa --- /dev/null +++ b/demo/bidi.html @@ -0,0 +1,61 @@ + + + + + CodeMirror: Bi-directional Text Demo + + + + + + + + +

    CodeMirror: Bi-directional Text Demo

    + +
    + + + +

    Demonstration of bi-directional text support. See + the related + blog post for more background.

    + + + diff --git a/index.html b/index.html index f423ebac6f..477cec3d64 100644 --- a/index.html +++ b/index.html @@ -93,6 +93,7 @@

    Usage demos:

  • Autocompletion (XML)
  • Search/replace
  • Code folding
  • +
  • Bi-directional text
  • Line widgets (via JSHint)
  • Split view
  • Mode overlays
  • From 5810dd040d3d71c4366b463dc991f7bb5042462c Mon Sep 17 00:00:00 2001 From: Marijn Haverbeke Date: Thu, 7 Feb 2013 11:19:10 +0100 Subject: [PATCH 0684/5780] Sanitize logic that prevents editing in readOnly ranges It was really quite misguided before. --- lib/codemirror.js | 18 +++++------------- 1 file changed, 5 insertions(+), 13 deletions(-) diff --git a/lib/codemirror.js b/lib/codemirror.js index e9e485570e..8dfcff0f09 100644 --- a/lib/codemirror.js +++ b/lib/codemirror.js @@ -3565,22 +3565,14 @@ window.CodeMirror = (function() { var parts = [{from: from, to: to}]; for (var i = 0; i < markers.length; ++i) { var mk = markers[i], m = mk.find(); - if (mk.inclusiveLeft) { - m.from = Pos(m.from.line, m.from.ch - 1); - if (m.from.ch < 0 && m.from.line > doc.first) - m.from = Pos(m.from.line - 1, getLine(doc, m.from.line - 1).text.length); - } - if (mk.inclusiveRight) { - m.to = Pos(m.to.line, m.to.ch + 1); - if (m.to.line < doc.first + doc.size - 1 && m.to.ch > getLine(doc, m.to.line).text.length) - m.to = Pos(m.to.line + 1, 0); - } for (var j = 0; j < parts.length; ++j) { var p = parts[j]; - if (!posLess(m.from, p.to) || posLess(m.to, p.from)) continue; + if (posLess(p.to, m.from) || posLess(m.to, p.from)) continue; var newParts = [j, 1]; - if (posLess(p.from, m.from)) newParts.push({from: p.from, to: m.from}); - if (posLess(m.to, p.to)) newParts.push({from: m.to, to: p.to}); + if (posLess(p.from, m.from) || !mk.inclusiveLeft && posEq(p.from, m.from)) + newParts.push({from: p.from, to: m.from}); + if (posLess(m.to, p.to) || !mk.inclusiveRight && posEq(p.to, m.to)) + newParts.push({from: m.to, to: p.to}); parts.splice.apply(parts, newParts); j += newParts.length - 1; } From 9ca7b0773b88fe071d7287e6df2c70fcded9e868 Mon Sep 17 00:00:00 2001 From: Marijn Haverbeke Date: Thu, 7 Feb 2013 11:38:23 +0100 Subject: [PATCH 0685/5780] [show-hint addon] Improve heuristic for keeping or closing the pop-up --- addon/hint/show-hint.js | 19 ++++++++++--------- 1 file changed, 10 insertions(+), 9 deletions(-) diff --git a/addon/hint/show-hint.js b/addon/hint/show-hint.js index c1347a9421..89ea66795f 100644 --- a/addon/hint/show-hint.js +++ b/addon/hint/show-hint.js @@ -1,22 +1,17 @@ CodeMirror.showHint = function(cm, getHints, options) { if (!options) options = {}; - function collectHints(previousToken) { + function collectHints(startCh, continued) { // We want a single cursor position. if (cm.somethingSelected()) return; var token = cm.getTokenAt(cm.getCursor()); - // Don't show completions if token has changed - if (previousToken != null && /\w/.test(previousToken.string) && - (token.start != previousToken.start || token.type != previousToken.type)) - return; - var result = getHints(cm, options); if (!result || !result.list.length) return; var completions = result.list; // When there is only one completion, use it directly. - if (!previousToken && options.completeSingle !== false && completions.length == 1) { + if (!continued && options.completeSingle !== false && completions.length == 1) { cm.replaceRange(completions[0], result.from, result.to); return true; } @@ -98,10 +93,16 @@ CodeMirror.showHint = function(cm, getHints, options) { cm.replaceRange(completions[selectedHint], result.from, result.to); close(); } - var once; + var once, lastPos = cm.getCursor(), lastLen = cm.getLine(lastPos.line).length; function cursorActivity() { clearTimeout(once); - once = setTimeout(function(){close(); collectHints(token);}, 70); + + var pos = cm.getCursor(), len = cm.getLine(pos.line).length, start = startCh || lastPos.ch; + if (pos.line != lastPos.line || len - pos.ch != lastLen - lastPos.ch || + pos.ch < start || cm.somethingSelected()) + close(); + else + once = setTimeout(function(){close(); collectHints(start, true);}, 70); } return true; } From b3c2ecdd68cc22b656853ac3ba9a0e76ae3ba008 Mon Sep 17 00:00:00 2001 From: Marijn Haverbeke Date: Thu, 7 Feb 2013 11:40:49 +0100 Subject: [PATCH 0686/5780] [show-hint addon] Remove unused variable --- addon/hint/show-hint.js | 2 -- 1 file changed, 2 deletions(-) diff --git a/addon/hint/show-hint.js b/addon/hint/show-hint.js index 89ea66795f..2c1cba850b 100644 --- a/addon/hint/show-hint.js +++ b/addon/hint/show-hint.js @@ -5,8 +5,6 @@ CodeMirror.showHint = function(cm, getHints, options) { // We want a single cursor position. if (cm.somethingSelected()) return; - var token = cm.getTokenAt(cm.getCursor()); - var result = getHints(cm, options); if (!result || !result.list.length) return; var completions = result.list; From b37017c20f6dc5f6f3b2d1d52aefa31a4ab3e305 Mon Sep 17 00:00:00 2001 From: Marijn Haverbeke Date: Thu, 7 Feb 2013 16:29:04 +0100 Subject: [PATCH 0687/5780] [show-hint addon] More careful positioning of pop-up --- addon/hint/show-hint.js | 22 ++++++++++++++++++++-- 1 file changed, 20 insertions(+), 2 deletions(-) diff --git a/addon/hint/show-hint.js b/addon/hint/show-hint.js index 2c1cba850b..30c8df3cd8 100644 --- a/addon/hint/show-hint.js +++ b/addon/hint/show-hint.js @@ -30,8 +30,26 @@ CodeMirror.showHint = function(cm, getHints, options) { // If we're at the edge of the screen, then we want the menu to appear on the left of the cursor. var winW = window.innerWidth || Math.max(document.body.offsetWidth, document.documentElement.offsetWidth); - if (winW - pos.left < hints.clientWidth) - hints.style.left = (pos.left - sel.clientWidth) + "px"; + var winH = window.innerHeight || Math.max(document.body.offsetHeight, document.documentElement.offsetHeight); + var box = hints.getBoundingClientRect(); + var overlapX = box.right - winW, overlapY = box.bottom - winH; + if (overlapX > 0) { + if (box.right - box.left > winW) { + hints.style.width = (winW - 5) + "px"; + overlapX -= (box.right - box.left) - winW; + } + hints.style.left = (pos.left - overlapX) + "px"; + } + if (overlapY > 0) { + var height = box.bottom - box.top; + if (box.top - (pos.bottom - pos.top) - height > 0) { + overlapY = height + (pos.bottom - pos.top); + } else if (height > winH) { + hints.style.height = (winH - 5) + "px"; + overlapY -= height - winH; + } + hints.style.top = (pos.bottom - overlapY) + "px"; + } function changeActive(i) { if (i < 0 || i >= completions.length || selectedHint == i) return; From 89df69dec96976f1d7d7929a7e69c91e7ba64871 Mon Sep 17 00:00:00 2001 From: Marijn Haverbeke Date: Thu, 7 Feb 2013 16:30:55 +0100 Subject: [PATCH 0688/5780] [show-hint addon] Close pop-up on blur, make page-up work when near top of list --- addon/hint/show-hint.js | 5 ++++- 1 file changed, 4 insertions(+), 1 deletion(-) diff --git a/addon/hint/show-hint.js b/addon/hint/show-hint.js index 30c8df3cd8..eb9cfa924f 100644 --- a/addon/hint/show-hint.js +++ b/addon/hint/show-hint.js @@ -52,7 +52,8 @@ CodeMirror.showHint = function(cm, getHints, options) { } function changeActive(i) { - if (i < 0 || i >= completions.length || selectedHint == i) return; + i = Math.max(0, Math.min(i, completions.length - 1)); + if (selectedHint == i) return; hints.childNodes[selectedHint].className = "CodeMirror-hint"; var node = hints.childNodes[selectedHint = i]; node.className = "CodeMirror-hint CodeMirror-hint-active"; @@ -85,6 +86,7 @@ CodeMirror.showHint = function(cm, getHints, options) { cm.addKeyMap(ourMap); cm.on("cursorActivity", cursorActivity); + cm.on("blur", close); CodeMirror.on(hints, "dblclick", function(e) { var t = e.target || e.srcElement; if (t.hintId != null) {selectedHint = t.hintId; pick();} @@ -104,6 +106,7 @@ CodeMirror.showHint = function(cm, getHints, options) { hints.parentNode.removeChild(hints); cm.removeKeyMap(ourMap); cm.off("cursorActivity", cursorActivity); + cm.off("blur", close); } function pick() { cm.replaceRange(completions[selectedHint], result.from, result.to); From ae179ddd1fe0562ae9f46ddf212d7fad67cc317e Mon Sep 17 00:00:00 2001 From: Narciso Jaramillo Date: Thu, 7 Feb 2013 14:57:53 -0800 Subject: [PATCH 0689/5780] Wrap undo/redo in an operation --- lib/codemirror.js | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/lib/codemirror.js b/lib/codemirror.js index 8dfcff0f09..cc2f33416f 100644 --- a/lib/codemirror.js +++ b/lib/codemirror.js @@ -4254,8 +4254,8 @@ window.CodeMirror = (function() { replaceSelection: function(code, collapse, origin) { makeChange(this, {from: this.sel.from, to: this.sel.to, text: splitLines(code), origin: origin}, collapse || "around"); }, - undo: function() {makeChangeFromHistory(this, "undo");}, - redo: function() {makeChangeFromHistory(this, "redo");}, + undo: docOperation(function() {makeChangeFromHistory(this, "undo");}), + redo: docOperation(function() {makeChangeFromHistory(this, "redo");}), setExtending: function(val) {this.sel.extend = val;}, From 70809f8de1ca27d22e7703575edc936da85dd96c Mon Sep 17 00:00:00 2001 From: Randy Edmunds Date: Thu, 7 Feb 2013 13:32:00 -0800 Subject: [PATCH 0690/5780] [htmlmixed mode] support for x-javascript and x-ecmascript script types --- mode/htmlmixed/htmlmixed.js | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/mode/htmlmixed/htmlmixed.js b/mode/htmlmixed/htmlmixed.js index 44063b3551..7c1fbefcea 100644 --- a/mode/htmlmixed/htmlmixed.js +++ b/mode/htmlmixed/htmlmixed.js @@ -11,7 +11,7 @@ CodeMirror.defineMode("htmlmixed", function(config) { // Script block: mode to change to depends on type attribute var scriptType = stream.string.slice(Math.max(0, stream.pos - 100), stream.pos).match(/\btype\s*=\s*("[^"]+"|'[^']+'|\S+)[^<]*$/i); scriptType = scriptType && scriptType[1]; - if (!scriptType || scriptType.match(/(text|application)\/(java|ecma)script/i)) { + if (!scriptType || scriptType.match(/(text|application)\/(x-)?(java|ecma)script/i)) { state.token = javascript; state.localMode = jsMode; state.localState = jsMode.startState(htmlMode.indent(state.htmlState, "")); From 3b37d7ad2bf08cfbcc294dc8861b3a21034c03b3 Mon Sep 17 00:00:00 2001 From: Marijn Haverbeke Date: Fri, 8 Feb 2013 09:47:20 +0100 Subject: [PATCH 0691/5780] Remove reference to buildLineWidget It was still being called for showIfHidden widgets, even though it no longer exists. Closes #1225 --- lib/codemirror.js | 18 ++++++++++-------- 1 file changed, 10 insertions(+), 8 deletions(-) diff --git a/lib/codemirror.js b/lib/codemirror.js index cc2f33416f..05ebcfebe0 100644 --- a/lib/codemirror.js +++ b/lib/codemirror.js @@ -590,7 +590,8 @@ window.CodeMirror = (function() { wrap.appendChild(prev); prev = wrap; } - prev.appendChild(buildLineWidget(line.widgets[i], prev, dims)); + var wnode = prev.appendChild(elt("div", [line.widgets[i].node], "CodeMirror-linewidget")); + positionLineWidget(line.widgets[i], wnode, prev, dims); } } else if (nextIntact && nextIntact.from <= lineN && nextIntact.to > lineN) { // This line is intact. Skip to the actual node. Update its @@ -644,7 +645,7 @@ window.CodeMirror = (function() { var widget = line.widgets[i], isFirst = false; if (!widget.above) { isFirst = first; first = false; } if (widget.node == n.firstChild) { - positionLineWidget(n, reuse, dims); + positionLineWidget(widget, n, reuse, dims); ++widgetsSeen; if (isFirst) reuse.insertBefore(lineElement, n); break; @@ -687,8 +688,7 @@ window.CodeMirror = (function() { if (ie_lt8) wrap.style.zIndex = 2; if (line.widgets && wrap != reuse) for (var i = 0, ws = line.widgets; i < ws.length; ++i) { var widget = ws[i], node = elt("div", [widget.node], "CodeMirror-linewidget"); - node.widget = widget; - positionLineWidget(node, wrap, dims); + positionLineWidget(widget, node, wrap, dims); if (widget.above) wrap.insertBefore(node, cm.options.lineNumbers && line.height != 0 ? gutterWrap : lineElement); else @@ -698,8 +698,7 @@ window.CodeMirror = (function() { return wrap; } - function positionLineWidget(node, wrap, dims) { - var widget = node.widget; + function positionLineWidget(widget, node, wrap, dims) { if (widget.noHScroll) { (wrap.alignable || (wrap.alignable = [])).push(node); var width = dims.wrapperWidth; @@ -1027,8 +1026,11 @@ window.CodeMirror = (function() { var vr = cur.top; cur.top = vranges[vr]; cur.bottom = vranges[vr+1]; } - if (!cm.options.lineWrapping) - data.width = pre.lastChild.getBoundingClientRect().right - outer.left; + if (!cm.options.lineWrapping) { + var last = pre.lastChild; + if (last.nodeType == 3) last = pre.appendChild(elt("span", "\u200b")); + data.width = last.getBoundingClientRect().right - outer.left; + } return data; } From 1c8a36e5beeb42ec21124b22fb57d32431affb9a Mon Sep 17 00:00:00 2001 From: Marijn Haverbeke Date: Fri, 8 Feb 2013 10:03:05 +0100 Subject: [PATCH 0692/5780] Fix delayed firing of event handlers The mechanism that was supposed to batch handlers during an operation and fire them afterwards was broken -- handlers triggered during endOperation were fired immediately. Should be better now. Closes #1226 --- lib/codemirror.js | 23 ++++++++++++----------- 1 file changed, 12 insertions(+), 11 deletions(-) diff --git a/lib/codemirror.js b/lib/codemirror.js index 05ebcfebe0..d31422324b 100644 --- a/lib/codemirror.js +++ b/lib/codemirror.js @@ -1218,18 +1218,14 @@ window.CodeMirror = (function() { } function endOperation(cm) { - var op = cm.curOp, doc = cm.doc, delayed; - if (!--delayedCallbackDepth) { - delayed = delayedCallbacks; - delayedCallbacks = null; - } + var op = cm.curOp, doc = cm.doc, display = cm.display; cm.curOp = null; - var display = cm.display; + if (op.updateMaxLine) computeMaxLength(cm); - if (cm.display.maxLineChanged && !cm.options.lineWrapping) { - var width = measureLine(cm, cm.display.maxLine).width; + if (display.maxLineChanged && !cm.options.lineWrapping) { + var width = measureLine(cm, display.maxLine).width; display.sizer.style.minWidth = Math.max(0, width + 3 + scrollerCutOff) + "px"; - cm.display.maxLineChanged = false; + display.maxLineChanged = false; var maxScrollLeft = Math.max(0, display.sizer.offsetLeft + display.sizer.offsetWidth - display.scroller.clientWidth); if (maxScrollLeft < doc.scrollLeft && !op.updateScrollPos) setScrollLeft(cm, Math.min(display.scroller.scrollLeft, maxScrollLeft), true); @@ -1245,8 +1241,8 @@ window.CodeMirror = (function() { updated = updateDisplay(cm, op.changes, newScrollPos && newScrollPos.scrollTop); if (!updated && op.selectionChanged) updateSelection(cm); if (op.updateScrollPos) { - cm.display.scroller.scrollTop = cm.display.scrollbarV.scrollTop = doc.scrollTop = newScrollPos.scrollTop; - cm.display.scroller.scrollLeft = cm.display.scrollbarH.scrollLeft = doc.scrollLeft = newScrollPos.scrollLeft; + display.scroller.scrollTop = display.scrollbarV.scrollTop = doc.scrollTop = newScrollPos.scrollTop; + display.scroller.scrollLeft = display.scrollbarH.scrollLeft = doc.scrollLeft = newScrollPos.scrollLeft; alignHorizontally(cm); } else if (newScrollPos) { scrollCursorIntoView(cm); @@ -1262,6 +1258,11 @@ window.CodeMirror = (function() { if (unhidden) for (var i = 0; i < unhidden.length; ++i) if (unhidden[i].lines.length) signal(unhidden[i], "unhide"); + var delayed; + if (!--delayedCallbackDepth) { + delayed = delayedCallbacks; + delayedCallbacks = null; + } if (op.textChanged) signal(cm, "change", cm, op.textChanged); if (op.selectionChanged) signal(cm, "cursorActivity", cm); From ba5dc823ef47816b8d7a155960c4272bd103bc05 Mon Sep 17 00:00:00 2001 From: Page Date: Fri, 8 Feb 2013 15:44:52 +0000 Subject: [PATCH 0693/5780] Update the docs to match the current default of 10 for viewportMargin. --- doc/manual.html | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/doc/manual.html b/doc/manual.html index 9dcc68abc8..c3e4181df9 100644 --- a/doc/manual.html +++ b/doc/manual.html @@ -331,7 +331,7 @@

    Configuration

    below the part of the document that's currently scrolled into view. This affects the amount of updates needed when scrolling, and the amount of work that such an update does. You should - usually leave it at its default, 100. Can be set + usually leave it at its default, 10. Can be set to Infinity to make sure the whole document is always rendered, and thus the browser's text search works on it. This will have bad effects on performance of big From 96c4963bd7d5a45bbe034601c9bff33cace8a256 Mon Sep 17 00:00:00 2001 From: Narciso Jaramillo Date: Fri, 8 Feb 2013 09:56:44 -0800 Subject: [PATCH 0694/5780] Fix recently introduced typo --- addon/edit/continuecomment.js | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/addon/edit/continuecomment.js b/addon/edit/continuecomment.js index 0def40008f..4ee0b48015 100644 --- a/addon/edit/continuecomment.js +++ b/addon/edit/continuecomment.js @@ -12,7 +12,7 @@ if (token.type == "comment" && mode.blockCommentStart) { var end = token.string.indexOf(mode.blockCommentEnd); - var full = cm.getRange(CodeMirro.Pos(pos.line, 0), CodeMirror.Pos(pos.line, token.end)), found; + var full = cm.getRange(CodeMirror.Pos(pos.line, 0), CodeMirror.Pos(pos.line, token.end)), found; if (end != -1 && end == token.string.length - mode.blockCommentEnd.length) { // Comment ended, don't continue it } else if (token.string.indexOf(mode.blockCommentStart) == 0) { From 9cfdd2a2c8e9420532c171056ddbc6db7cfb0a4c Mon Sep 17 00:00:00 2001 From: Glenn Ruehle Date: Fri, 8 Feb 2013 12:39:43 -0800 Subject: [PATCH 0695/5780] Repair context menu. --- lib/codemirror.js | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/lib/codemirror.js b/lib/codemirror.js index d31422324b..acabd861dc 100644 --- a/lib/codemirror.js +++ b/lib/codemirror.js @@ -1389,7 +1389,7 @@ window.CodeMirror = (function() { // Gecko browsers fire contextmenu *after* opening the menu, at // which point we can't mess with it anymore. Context menu is // handled in onMouseDown for Gecko. - if (captureMiddleClick) on(d.scroller, "contextmenu", function(e) {onContextMenu(cm, e);}); + if (!captureMiddleClick) on(d.scroller, "contextmenu", function(e) {onContextMenu(cm, e);}); on(d.scroller, "scroll", function() { setScrollTop(cm, d.scroller.scrollTop); From d7bdb71478b8ae7f9ac838d3828a127b538f0cfe Mon Sep 17 00:00:00 2001 From: Brian Sletten Date: Sun, 10 Feb 2013 19:54:18 -0800 Subject: [PATCH 0696/5780] Added support for Turtle RDF serialization --- index.html | 1 + mode/turtle/index.html | 39 +++++++++++ mode/turtle/turtle.js | 145 +++++++++++++++++++++++++++++++++++++++++ 3 files changed, 185 insertions(+) create mode 100644 mode/turtle/index.html create mode 100644 mode/turtle/turtle.js diff --git a/index.html b/index.html index 477cec3d64..91e25c8fe1 100644 --- a/index.html +++ b/index.html @@ -74,6 +74,7 @@

    Supported modes:

  • Smarty
  • SQL (several dialects)
  • SPARQL
  • +
  • Turtle
  • sTeX, LaTeX
  • VB.NET
  • VBScript
  • diff --git a/mode/turtle/index.html b/mode/turtle/index.html new file mode 100644 index 0000000000..8e2264e49d --- /dev/null +++ b/mode/turtle/index.html @@ -0,0 +1,39 @@ + + + + CodeMirror: Turtle mode + + + + + + + + +

    CodeMirror: Turtle mode

    +
    + + +

    MIME types defined: text/turtle.

    + + + diff --git a/mode/turtle/turtle.js b/mode/turtle/turtle.js new file mode 100644 index 0000000000..2f1d70cd9a --- /dev/null +++ b/mode/turtle/turtle.js @@ -0,0 +1,145 @@ +CodeMirror.defineMode("turtle", function(config) { + var indentUnit = config.indentUnit; + var curPunc; + + function wordRegexp(words) { + return new RegExp("^(?:" + words.join("|") + ")$", "i"); + } + var ops = wordRegexp([]); + var keywords = wordRegexp(["@prefix", "@base", "a"]); + var operatorChars = /[*+\-<>=&|]/; + + function tokenBase(stream, state) { + var ch = stream.next(); + curPunc = null; + if (ch == "<" && !stream.match(/^[\s\u00a0=]/, false)) { + stream.match(/^[^\s\u00a0>]*>?/); + return "atom"; + } + else if (ch == "\"" || ch == "'") { + state.tokenize = tokenLiteral(ch); + return state.tokenize(stream, state); + } + else if (/[{}\(\),\.;\[\]]/.test(ch)) { + curPunc = ch; + return null; + } + else if (ch == "#") { + stream.skipToEnd(); + return "comment"; + } + else if (operatorChars.test(ch)) { + stream.eatWhile(operatorChars); + return null; + } + else if (ch == ":") { + return "operator"; + } else { + stream.eatWhile(/[_\w\d]/); + if(stream.peek() == ":") { + return "variable-3"; + } else { + var word = stream.current(); + + if(keywords.test(word)) { + return "meta"; + } + + if(ch >= "A" && ch <= "Z") { + return "comment"; + } else { + return "keyword"; + } + } + var word = stream.current(); + if (ops.test(word)) + return null; + else if (keywords.test(word)) + return "meta"; + else + return "variable"; + } + } + + function tokenLiteral(quote) { + return function(stream, state) { + var escaped = false, ch; + while ((ch = stream.next()) != null) { + if (ch == quote && !escaped) { + state.tokenize = tokenBase; + break; + } + escaped = !escaped && ch == "\\"; + } + return "string"; + }; + } + + function pushContext(state, type, col) { + state.context = {prev: state.context, indent: state.indent, col: col, type: type}; + } + function popContext(state) { + state.indent = state.context.indent; + state.context = state.context.prev; + } + + return { + startState: function(base) { + return {tokenize: tokenBase, + context: null, + indent: 0, + col: 0}; + }, + + token: function(stream, state) { + if (stream.sol()) { + if (state.context && state.context.align == null) state.context.align = false; + state.indent = stream.indentation(); + } + if (stream.eatSpace()) return null; + var style = state.tokenize(stream, state); + + if (style != "comment" && state.context && state.context.align == null && state.context.type != "pattern") { + state.context.align = true; + } + + if (curPunc == "(") pushContext(state, ")", stream.column()); + else if (curPunc == "[") pushContext(state, "]", stream.column()); + else if (curPunc == "{") pushContext(state, "}", stream.column()); + else if (/[\]\}\)]/.test(curPunc)) { + while (state.context && state.context.type == "pattern") popContext(state); + if (state.context && curPunc == state.context.type) popContext(state); + } + else if (curPunc == "." && state.context && state.context.type == "pattern") popContext(state); + else if (/atom|string|variable/.test(style) && state.context) { + if (/[\}\]]/.test(state.context.type)) + pushContext(state, "pattern", stream.column()); + else if (state.context.type == "pattern" && !state.context.align) { + state.context.align = true; + state.context.col = stream.column(); + } + } + + return style; + }, + + indent: function(state, textAfter) { + var firstChar = textAfter && textAfter.charAt(0); + var context = state.context; + if (/[\]\}]/.test(firstChar)) + while (context && context.type == "pattern") context = context.prev; + + var closing = context && firstChar == context.type; + if (!context) + return 0; + else if (context.type == "pattern") + return context.col; + else if (context.align) + return context.col + (closing ? 0 : 1); + else + return context.indent + (closing ? 0 : indentUnit); + } + }; +}); + +CodeMirror.defineMIME("text/turtle", "turtle"); From 4ccc65e116fed1c97b12073e6fce9654541d2216 Mon Sep 17 00:00:00 2001 From: Marijn Haverbeke Date: Mon, 11 Feb 2013 10:09:48 +0100 Subject: [PATCH 0697/5780] Integrate Turtle mode --- doc/compress.html | 1 + doc/modes.html | 1 + index.html | 1 - mode/turtle/index.html | 4 ++-- 4 files changed, 4 insertions(+), 3 deletions(-) diff --git a/doc/compress.html b/doc/compress.html index aa94d70dd6..f2e7634c9f 100644 --- a/doc/compress.html +++ b/doc/compress.html @@ -118,6 +118,7 @@

    { } CodeMi + diff --git a/doc/modes.html b/doc/modes.html index 877227492f..8132770cfa 100644 --- a/doc/modes.html +++ b/doc/modes.html @@ -72,6 +72,7 @@

    { } CodeMi
  • sTeX, LaTeX
  • Tiddlywiki
  • Tiki wiki
  • +
  • Turtle
  • VB.NET
  • VBScript
  • Velocity
  • diff --git a/index.html b/index.html index 91e25c8fe1..477cec3d64 100644 --- a/index.html +++ b/index.html @@ -74,7 +74,6 @@

    Supported modes:

  • Smarty
  • SQL (several dialects)
  • SPARQL
  • -
  • Turtle
  • sTeX, LaTeX
  • VB.NET
  • VBScript
  • diff --git a/mode/turtle/index.html b/mode/turtle/index.html index 8e2264e49d..5e56e5755c 100644 --- a/mode/turtle/index.html +++ b/mode/turtle/index.html @@ -1,13 +1,13 @@ + CodeMirror: Turtle mode - - +

    CodeMirror: Turtle mode

    From 0cddeeda43c578f3d4623b98c7abfabf350be7e0 Mon Sep 17 00:00:00 2001 From: Marijn Haverbeke Date: Mon, 11 Feb 2013 10:25:03 +0100 Subject: [PATCH 0698/5780] Fix bug in measuring surrogate pair characters Splitting them into two spans apparently prevents them from rendering at all (not unexpectedly). --- lib/codemirror.js | 20 +++++++++++++------- 1 file changed, 13 insertions(+), 7 deletions(-) diff --git a/lib/codemirror.js b/lib/codemirror.js index acabd861dc..b8e48f5117 100644 --- a/lib/codemirror.js +++ b/lib/codemirror.js @@ -3900,13 +3900,19 @@ window.CodeMirror = (function() { function buildTokenMeasure(builder, text, style, startStyle, endStyle) { for (var i = 0; i < text.length; ++i) { - if (i && i < text.length && - builder.cm.options.lineWrapping && - spanAffectsWrapping.test(text.slice(i - 1, i + 1))) + var ch = text.charAt(i), start = i == 0; + if (ch >= "\ud800" && ch < "\udbff" && i < text.length - 1) { + ch = text.slice(i, i + 2); + ++i; + } else if (i && i < text.length && + builder.cm.options.lineWrapping && + spanAffectsWrapping.test(text.slice(i - 1, i + 1))) { builder.pre.appendChild(elt("wbr")); - builder.measure[builder.pos++] = - buildToken(builder, text.charAt(i), style, - i == 0 && startStyle, i == text.length - 1 && endStyle); + } + builder.measure[builder.pos] = + buildToken(builder, ch, style, + start && startStyle, i == text.length - 1 && endStyle); + builder.pos += ch.length; } if (text.length) builder.addedOne = true; } @@ -4842,7 +4848,7 @@ window.CodeMirror = (function() { return true; } - var isExtendingChar = /[\u0300-\u036F\u0483-\u0487\u0488-\u0489\u0591-\u05BD\u05BF\u05C1-\u05C2\u05C4-\u05C5\u05C7\u0610-\u061A\u064B-\u065F\u0670\u06D6-\u06DC\u06DF-\u06E4\u06E7-\u06E8\u06EA-\u06ED\uA66F\uA670-\uA672\uA674-\uA67D\uA69F]/; + var isExtendingChar = /[\u0300-\u036F\u0483-\u0487\u0488-\u0489\u0591-\u05BD\u05BF\u05C1-\u05C2\u05C4-\u05C5\u05C7\u0610-\u061A\u064B-\u065F\u0670\u06D6-\u06DC\u06DF-\u06E4\u06E7-\u06E8\u06EA-\u06ED\uA66F\uA670-\uA672\uA674-\uA67D\uA69F\udc00-\udfff]/; // DOM UTILITIES From 5d529152486169222cfd1d891648d9470f4d45c1 Mon Sep 17 00:00:00 2001 From: Marijn Haverbeke Date: Mon, 11 Feb 2013 10:26:11 +0100 Subject: [PATCH 0699/5780] Remove useless (always true) test --- lib/codemirror.js | 3 +-- 1 file changed, 1 insertion(+), 2 deletions(-) diff --git a/lib/codemirror.js b/lib/codemirror.js index b8e48f5117..2bdb654149 100644 --- a/lib/codemirror.js +++ b/lib/codemirror.js @@ -3904,8 +3904,7 @@ window.CodeMirror = (function() { if (ch >= "\ud800" && ch < "\udbff" && i < text.length - 1) { ch = text.slice(i, i + 2); ++i; - } else if (i && i < text.length && - builder.cm.options.lineWrapping && + } else if (i && builder.cm.options.lineWrapping && spanAffectsWrapping.test(text.slice(i - 1, i + 1))) { builder.pre.appendChild(elt("wbr")); } From efc041477e83263ebba18760aebd2a2aebca80c6 Mon Sep 17 00:00:00 2001 From: Marijn Haverbeke Date: Mon, 11 Feb 2013 10:26:39 +0100 Subject: [PATCH 0700/5780] [turtle mode] Remove unused argument --- mode/turtle/turtle.js | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/mode/turtle/turtle.js b/mode/turtle/turtle.js index 2f1d70cd9a..5c7c28eb02 100644 --- a/mode/turtle/turtle.js +++ b/mode/turtle/turtle.js @@ -84,7 +84,7 @@ CodeMirror.defineMode("turtle", function(config) { } return { - startState: function(base) { + startState: function() { return {tokenize: tokenBase, context: null, indent: 0, From 8d1668b5f03a04c93e1aaaa7dbffdeea0c4b7cf2 Mon Sep 17 00:00:00 2001 From: Marijn Haverbeke Date: Mon, 11 Feb 2013 10:52:02 +0100 Subject: [PATCH 0701/5780] [matchhighlighter demo] Use a pseudo-underline background instead of a color Closes #1232 --- demo/matchhighlighter.html | 8 +++++--- 1 file changed, 5 insertions(+), 3 deletions(-) diff --git a/demo/matchhighlighter.html b/demo/matchhighlighter.html index 7fd1b2b792..c574fbae81 100644 --- a/demo/matchhighlighter.html +++ b/demo/matchhighlighter.html @@ -11,9 +11,11 @@ From d6a0c0daf00e7d1a920cb766dd359f1739c7d08c Mon Sep 17 00:00:00 2001 From: Marijn Haverbeke Date: Mon, 11 Feb 2013 11:00:44 +0100 Subject: [PATCH 0702/5780] Use a helper for getBoundingClientRect, allow this helper to be overwritten --- lib/codemirror.js | 33 +++++++++++++++++++-------------- 1 file changed, 19 insertions(+), 14 deletions(-) diff --git a/lib/codemirror.js b/lib/codemirror.js index 2bdb654149..96b71fc0e4 100644 --- a/lib/codemirror.js +++ b/lib/codemirror.js @@ -387,7 +387,7 @@ window.CodeMirror = (function() { return String(options.lineNumberFormatter(i + options.firstLineNumber)); } function compensateForHScroll(display) { - return display.scroller.getBoundingClientRect().left - display.sizer.getBoundingClientRect().left; + return getRect(display.scroller).left - getRect(display.sizer).left; } // DISPLAY DRAWING @@ -499,7 +499,7 @@ window.CodeMirror = (function() { height = bot - prevBottom; prevBottom = bot; } else { - var box = node.getBoundingClientRect(); + var box = getRect(node); height = box.bottom - box.top; } var diff = node.lineObj.height - height; @@ -732,7 +732,7 @@ window.CodeMirror = (function() { // Move the hidden textarea near the cursor to prevent scrolling artifacts var headPos = cursorCoords(cm, cm.doc.sel.head, "div"); - var wrapOff = display.wrapper.getBoundingClientRect(), lineOff = display.lineDiv.getBoundingClientRect(); + var wrapOff = getRect(display.wrapper), lineOff = getRect(display.lineDiv); display.inputDiv.style.top = Math.max(0, Math.min(display.wrapper.clientHeight - 10, headPos.top + lineOff.top - wrapOff.top)) + "px"; display.inputDiv.style.left = Math.max(0, Math.min(display.wrapper.clientWidth - 10, @@ -998,7 +998,7 @@ window.CodeMirror = (function() { removeChildrenAndAdd(display.measure, pre); - var outer = display.lineDiv.getBoundingClientRect(); + var outer = getRect(display.lineDiv); var vranges = [], data = emptyArray(line.text.length), maxBot = pre.offsetHeight; // Work around an IE7/8 bug where it will sometimes have randomly // replaced our pre with a clone at this point. @@ -1006,7 +1006,7 @@ window.CodeMirror = (function() { removeChildrenAndAdd(display.measure, pre); for (var i = 0, cur; i < measure.length; ++i) if (cur = measure[i]) { - var size = cur.getBoundingClientRect(); + var size = getRect(cur); var top = Math.max(0, size.top - outer.top), bot = Math.min(size.bottom - outer.top, maxBot); for (var j = 0; j < vranges.length; j += 2) { var rtop = vranges[j], rbot = vranges[j+1]; @@ -1029,7 +1029,7 @@ window.CodeMirror = (function() { if (!cm.options.lineWrapping) { var last = pre.lastChild; if (last.nodeType == 3) last = pre.appendChild(elt("span", "\u200b")); - data.width = last.getBoundingClientRect().right - outer.left; + data.width = getRect(last).right - outer.left; } return data; @@ -1053,7 +1053,7 @@ window.CodeMirror = (function() { var yOff = heightAtLine(cm, lineObj); if (context != "local") yOff -= cm.display.viewOffset; if (context == "page") { - var lOff = cm.display.lineSpace.getBoundingClientRect(); + var lOff = getRect(cm.display.lineSpace); yOff += lOff.top + (window.pageYOffset || (document.documentElement || document.body).scrollTop); var xOff = lOff.left + (window.pageXOffset || (document.documentElement || document.body).scrollLeft); rect.left += xOff; rect.right += xOff; @@ -1493,7 +1493,7 @@ window.CodeMirror = (function() { target == display.scrollbarV || target == display.scrollbarV.firstChild || target == display.scrollbarFiller) return null; } - var x, y, space = display.lineSpace.getBoundingClientRect(); + var x, y, space = getRect(display.lineSpace); // Fails unpredictably on IE[67] when mouse is dragged around quickly. try { x = e.clientX; y = e.clientY; } catch (e) { return null; } return coordsChar(cm, x - space.left, y - space.top); @@ -1591,7 +1591,7 @@ window.CodeMirror = (function() { } } - var editorSize = display.wrapper.getBoundingClientRect(); + var editorSize = getRect(display.wrapper); // Used to ensure timeout re-tries don't fire when another extend // happened in the meantime (clearTimeout isn't reliable -- at // least on Chrome, the timeouts still happen even when cleared, @@ -1687,17 +1687,17 @@ window.CodeMirror = (function() { try { var mX = e.clientX, mY = e.clientY; } catch(e) { return false; } - if (mX >= Math.floor(display.gutters.getBoundingClientRect().right)) return false; + if (mX >= Math.floor(getRect(display.gutters).right)) return false; e_preventDefault(e); if (!hasHandler(cm, "gutterClick")) return true; - var lineBox = display.lineDiv.getBoundingClientRect(); + var lineBox = getRect(display.lineDiv); if (mY > lineBox.bottom) return true; mY -= lineBox.top - display.viewOffset; for (var i = 0; i < cm.options.gutters.length; ++i) { var g = display.gutters.childNodes[i]; - if (g && g.getBoundingClientRect().right >= mX) { + if (g && getRect(g).right >= mX) { var line = lineAtHeight(cm.doc, mY); var gutter = cm.options.gutters[i]; signalLater(cm, "gutterClick", cm, line, gutter, e); @@ -2321,7 +2321,7 @@ window.CodeMirror = (function() { function scrollCursorIntoView(cm) { var coords = scrollPosIntoView(cm, cm.doc.sel.head); if (!cm.state.focused) return; - var display = cm.display, box = display.sizer.getBoundingClientRect(), doScroll = null; + var display = cm.display, box = getRect(display.sizer), doScroll = null; if (coords.top + box.top < 0) doScroll = true; else if (coords.bottom + box.top > (window.innerHeight || document.documentElement.clientHeight)) doScroll = false; if (doScroll != null && !phantom) { @@ -2585,7 +2585,7 @@ window.CodeMirror = (function() { }, coordsChar: function(coords) { - var off = this.display.lineSpace.getBoundingClientRect(); + var off = getRect(this.display.lineSpace); var scrollY = window.pageYOffset || (document.documentElement || document.body).scrollTop; var scrollX = window.pageXOffset || (document.documentElement || document.body).scrollLeft; return coordsChar(this, coords.left - off.left - scrollX, coords.top - off.top - scrollY); @@ -4878,6 +4878,11 @@ window.CodeMirror = (function() { } else e.textContent = str; } + function getRect(node) { + return node.getBoundingClientRect(); + } + CodeMirror.replaceGetRect = function(f) { getRect = f; }; + // FEATURE DETECTION // Detect drag-and-drop From e0186cdf976015469a596b4f0ee7938e9bc5d567 Mon Sep 17 00:00:00 2001 From: Marijn Haverbeke Date: Mon, 11 Feb 2013 11:04:57 +0100 Subject: [PATCH 0703/5780] Add two missing addons to compression helper --- doc/compress.html | 2 ++ 1 file changed, 2 insertions(+) diff --git a/doc/compress.html b/doc/compress.html index f2e7634c9f..1bec338058 100644 --- a/doc/compress.html +++ b/doc/compress.html @@ -152,6 +152,8 @@

    { } CodeMi + + From 3ae0106197c6866a822e818e841b650164cff33f Mon Sep 17 00:00:00 2001 From: Marijn Haverbeke Date: Mon, 11 Feb 2013 14:01:08 +0100 Subject: [PATCH 0704/5780] Fix problem where the gutter won't span to bottom of horizontally scrollable document --- lib/codemirror.css | 1 + 1 file changed, 1 insertion(+) diff --git a/lib/codemirror.css b/lib/codemirror.css index 1ceb0800dc..6e34d15578 100644 --- a/lib/codemirror.css +++ b/lib/codemirror.css @@ -148,6 +148,7 @@ div.CodeMirror span.CodeMirror-nonmatchingbracket {color: #f22;} .CodeMirror-gutters { position: absolute; left: 0; top: 0; height: 100%; + padding-bottom: 30px; z-index: 3; } .CodeMirror-gutter { From 77fbf4abec6b9e568393a70726a9a637064545ca Mon Sep 17 00:00:00 2001 From: Marijn Haverbeke Date: Mon, 11 Feb 2013 14:01:55 +0100 Subject: [PATCH 0705/5780] Use a less weird construct in createObj helper --- lib/codemirror.js | 6 +++--- 1 file changed, 3 insertions(+), 3 deletions(-) diff --git a/lib/codemirror.js b/lib/codemirror.js index 96b71fc0e4..c0e19c1b4f 100644 --- a/lib/codemirror.js +++ b/lib/codemirror.js @@ -4813,9 +4813,9 @@ window.CodeMirror = (function() { } function createObj(base, props) { - if (!base) return; - createObj.prototype = base; - var inst = new createObj(); + function Obj() {} + Obj.prototype = base; + var inst = new Obj(); if (props) copyObj(props, inst); return inst; } From 7b16cbcd8ba4ed5dbefe7d987b1114f278b6784e Mon Sep 17 00:00:00 2001 From: Marijn Haverbeke Date: Mon, 11 Feb 2013 14:10:35 +0100 Subject: [PATCH 0706/5780] Change key handling prototol to allow returning Pass, rather than throwing it This to make dev tools show useful backtraces when an error is raised inside a key handler. Once we've caught it, the backtrace is truncated. --- addon/edit/closetag.js | 10 +++++----- demo/xmlcomplete.html | 2 +- doc/manual.html | 10 ++++++---- lib/codemirror.js | 12 ++++-------- 4 files changed, 16 insertions(+), 18 deletions(-) diff --git a/addon/edit/closetag.js b/addon/edit/closetag.js index 44bbca7c6f..46a5f4c76c 100644 --- a/addon/edit/closetag.js +++ b/addon/edit/closetag.js @@ -27,9 +27,9 @@ if (val && (old == CodeMirror.Init || !old)) { var map = {name: "autoCloseTags"}; if (typeof val != "object" || val.whenClosing) - map["'/'"] = function(cm) { autoCloseTag(cm, '/'); }; + map["'/'"] = function(cm) { return autoCloseTag(cm, '/'); }; if (typeof val != "object" || val.whenOpening) - map["'>'"] = function(cm) { autoCloseTag(cm, '>'); }; + map["'>'"] = function(cm) { return autoCloseTag(cm, '>'); }; cm.addKeyMap(map); } else if (!val && (old != CodeMirror.Init && old)) { cm.removeKeyMap("autoCloseTags"); @@ -44,7 +44,7 @@ function autoCloseTag(cm, ch) { var pos = cm.getCursor(), tok = cm.getTokenAt(pos); var inner = CodeMirror.innerMode(cm.getMode(), tok.state), state = inner.state; - if (inner.mode.name != "xml") throw CodeMirror.Pass; + if (inner.mode.name != "xml") return CodeMirror.Pass; var opt = cm.getOption("autoCloseTags"), html = inner.mode.configuration == "html"; var dontCloseTags = (typeof opt == "object" && opt.dontCloseTags) || (html && htmlDontClose); @@ -58,7 +58,7 @@ if (tok.type == "tag" && state.type == "closeTag" || /\/\s*$/.test(tok.string) || dontCloseTags && indexOf(dontCloseTags, lowerTagName) > -1) - throw CodeMirror.Pass; + return CodeMirror.Pass; var doIndent = indentTags && indexOf(indentTags, lowerTagName) > -1; var curPos = doIndent ? CodeMirror.Pos(pos.line + 1, 0) : CodeMirror.Pos(pos.line, pos.ch + 1); @@ -74,7 +74,7 @@ if (tagName) cm.replaceSelection("/" + tagName + ">", "end"); return; } - throw CodeMirror.Pass; + return CodeMirror.Pass; } function indexOf(collection, elt) { diff --git a/demo/xmlcomplete.html b/demo/xmlcomplete.html index 34a1b0152f..28a50638f7 100644 --- a/demo/xmlcomplete.html +++ b/demo/xmlcomplete.html @@ -62,7 +62,7 @@

    CodeMirror: XML Autocomplete demo

    } function passAndHint(cm) { setTimeout(function() {cm.execCommand("autocomplete");}, 100); - throw CodeMirror.Pass; + return CodeMirror.Pass; } var editor = CodeMirror.fromTextArea(document.getElementById("code"), { diff --git a/doc/manual.html b/doc/manual.html index c3e4181df9..a7039d3b98 100644 --- a/doc/manual.html +++ b/doc/manual.html @@ -481,7 +481,7 @@

    Keymaps

    common commands that are used by the default keybindings, and maps them to functions. If the property is set to false, CodeMirror leaves handling of the key up to the browser. A key - handler function may throw CodeMirror.Pass to indicate + handler function may return CodeMirror.Pass to indicate that it has decided not to handle the key, and other handlers (or the default behavior) should be given a turn.

    @@ -1595,8 +1595,8 @@

    Writing CodeMirror Modes

    If you want your mode to provide smart indentation (through the indentLine method and the indentAuto - and newlineAndIndent commands, which keys can be - bound to), you must define + and newlineAndIndent commands, to which keys can be + bound), you must define an indent(state, textAfter) method on your mode object.

    @@ -1605,7 +1605,9 @@

    Writing CodeMirror Modes

    the text on the line that is being indented, and return an integer, the amount of spaces to indent. It should usually take the indentUnit - option into account.

    + option into account. An indentation method may + return CodeMirror.Pass to indicate that it + could not come up with a precise indentation.

    Finally, a mode may define an electricChars property, which should hold a string diff --git a/lib/codemirror.js b/lib/codemirror.js index c0e19c1b4f..beb1d99832 100644 --- a/lib/codemirror.js +++ b/lib/codemirror.js @@ -1838,16 +1838,16 @@ window.CodeMirror = (function() { // Ensure previous input has been read, so that the handler sees a // consistent view of the document if (cm.display.pollingFast && readInput(cm)) cm.display.pollingFast = false; - var doc = cm.doc, prevShift = doc.sel.shift; + var doc = cm.doc, prevShift = doc.sel.shift, done = false; try { if (isReadOnly(cm)) cm.state.suppressEdits = true; if (dropShift) doc.sel.shift = false; - bound(cm); + done = bound(cm) != Pass; } finally { doc.sel.shift = prevShift; cm.state.suppressEdits = false; } - return true; + return done; } function allKeyMaps(cm) { @@ -3097,11 +3097,7 @@ window.CodeMirror = (function() { map = getKeyMap(map); var found = map[name]; if (found === false) return "stop"; - if (found != null) { - try { var done = handle(found); } - catch (e) { if (e != Pass) throw e; } - if (done) return true; - } + if (found != null && handle(found)) return true; if (map.nofallthrough) return "stop"; var fallthrough = map.fallthrough; From 72ca55233967e1d92ee54fb792d548b651be54a6 Mon Sep 17 00:00:00 2001 From: Marijn Haverbeke Date: Mon, 11 Feb 2013 15:47:21 +0100 Subject: [PATCH 0707/5780] [variableheight demo] Fix inconsistent text/markup --- demo/variableheight.html | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/demo/variableheight.html b/demo/variableheight.html index 8523027368..b00f7e4542 100644 --- a/demo/variableheight.html +++ b/demo/variableheight.html @@ -18,7 +18,7 @@

    CodeMirror: Variable Height Demo

    -

    The HTML mixed mode depends on the XML, JavaScript, and CSS modes.

    +

    It takes an optional mode configuration + option, scriptTypes, which can be used to add custom + behavior for specific <script type="..."> tags. If + given, it should hold an array of {matches, mode} + objects, where matches is a string or regexp that + matches the script type, and mode is + either null, for script types that should stay in + HTML mode, or a mode + spec corresponding to the mode that should be used for the + script.

    +

    MIME types defined: text/html (redefined, only takes effect if you load this parser after the XML parser).

    From 5f528b5928286a95d5e60d498ecd4f75ff85075e Mon Sep 17 00:00:00 2001 From: Marijn Haverbeke Date: Fri, 15 Feb 2013 10:30:26 +0100 Subject: [PATCH 0718/5780] Add Mongo MapReduce WebBrowser to real-world uses --- doc/realworld.html | 1 + 1 file changed, 1 insertion(+) diff --git a/doc/realworld.html b/doc/realworld.html index 60db83ac74..118ce10b6a 100644 --- a/doc/realworld.html +++ b/doc/realworld.html @@ -62,6 +62,7 @@

    { } CodeMi
  • Light Table (experimental IDE)
  • Mergely (interactive diffing)
  • MIHTool (iOS web-app debugging tool)
  • +
  • Mongo MapReduce WebBrowser
  • My2ndGeneration (social coding)
  • NoTex (rST authoring)
  • ORG (z80 assembly IDE)
  • From 1ff3c7ea500bc8b8f9e2f6ed5ae262c484023b11 Mon Sep 17 00:00:00 2001 From: Marijn Haverbeke Date: Fri, 15 Feb 2013 11:11:26 +0100 Subject: [PATCH 0719/5780] Make sure operation ids aren't reused between editors Issue #1249 --- lib/codemirror.js | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/lib/codemirror.js b/lib/codemirror.js index 32f2ac7d08..4d360aca68 100644 --- a/lib/codemirror.js +++ b/lib/codemirror.js @@ -60,7 +60,6 @@ window.CodeMirror = (function() { draggingText: false, highlight: new Delayed()}; - this.nextOpId = 0; themeChanged(this); if (options.lineWrapping) this.display.wrapper.className += " CodeMirror-wrap"; @@ -1201,6 +1200,7 @@ window.CodeMirror = (function() { // be awkward, slow, and error-prone), but instead updates are // batched and then all combined and executed at once. + var nextOpId = 0; function startOperation(cm) { cm.curOp = { // An array of ranges of lines that have to be updated. See @@ -1212,7 +1212,7 @@ window.CodeMirror = (function() { selectionChanged: false, updateMaxLine: false, updateScrollPos: false, - id: ++cm.nextOpId + id: ++nextOpId }; if (!delayedCallbackDepth++) delayedCallbacks = []; } From 15328c7adb8bebd957b228cf80959fa285e715e6 Mon Sep 17 00:00:00 2001 From: Marijn Haverbeke Date: Fri, 15 Feb 2013 11:24:32 +0100 Subject: [PATCH 0720/5780] [htmlmixed mode] Fix bug in matching of script types Closes #1248 --- mode/htmlmixed/htmlmixed.js | 1 + 1 file changed, 1 insertion(+) diff --git a/mode/htmlmixed/htmlmixed.js b/mode/htmlmixed/htmlmixed.js index 05c2e8ac7e..f8a0d5d18a 100644 --- a/mode/htmlmixed/htmlmixed.js +++ b/mode/htmlmixed/htmlmixed.js @@ -19,6 +19,7 @@ CodeMirror.defineMode("htmlmixed", function(config, parserConfig) { // Script block: mode to change to depends on type attribute var scriptType = stream.string.slice(Math.max(0, stream.pos - 100), stream.pos).match(/\btype\s*=\s*("[^"]+"|'[^']+'|\S+)[^<]*$/i); scriptType = scriptType ? scriptType[1] : ""; + if (scriptType && /[\"\']/.test(scriptType.charAt(0))) scriptType = scriptType.slice(1, scriptType.length - 1); for (var i = 0; i < scriptTypes.length; ++i) { var tp = scriptTypes[i]; if (typeof tp.matches == "string" ? scriptType == tp.matches : tp.matches.test(scriptType)) { From f7c3944acd608bc2e6a7ffa673d4658dc08119ed Mon Sep 17 00:00:00 2001 From: Marijn Haverbeke Date: Fri, 15 Feb 2013 12:18:01 +0100 Subject: [PATCH 0721/5780] Factor findPosH into an exported interface. Add tests. --- doc/manual.html | 14 ++++++++++++++ lib/codemirror.js | 33 ++++++++++++++++++++++++--------- test/test.js | 21 +++++++++++++++++++++ 3 files changed, 59 insertions(+), 9 deletions(-) diff --git a/doc/manual.html b/doc/manual.html index c154942014..23ef10abac 100644 --- a/doc/manual.html +++ b/doc/manual.html @@ -736,6 +736,20 @@

    Cursor and selection methods

    the shift key, in that it will cause cursor movement and calls to extendSelection to leave the selection anchor in place.

    + +
    cm.findPosH(start, amount, unit, visually) → object
    +
    Used to find the target position for horizontal cursor + motion. start is a {line, ch} + object, amount an integer (may be negative), + and unit one of the + string "char", "column", + or "word". Will return a position that is produced + by moving amount times the distance specified + by unit. When visually is true, motion + in right-to-left text will be visual rather than logical. When + the motion was clipped by hitting the end or start of the + document, the returned value will have a hitSide + property set to true.

    Configuration methods

    diff --git a/lib/codemirror.js b/lib/codemirror.js index 4d360aca68..5030fb70f8 100644 --- a/lib/codemirror.js +++ b/lib/codemirror.js @@ -2440,12 +2440,13 @@ window.CodeMirror = (function() { return line; } - function findPosH(doc, dir, unit, visually) { - var end = doc.sel.head, line = end.line, ch = end.ch; + function findPosH(doc, pos, dir, unit, visually) { + var line = pos.line, ch = pos.ch; var lineObj = getLine(doc, line); + var possible = true; function findNextLine() { var l = line + dir; - if (l < doc.first || l >= doc.first + doc.size) return false; + if (l < doc.first || l >= doc.first + doc.size) return (possible = false); line = l; return lineObj = getLine(doc, l); } @@ -2455,10 +2456,11 @@ window.CodeMirror = (function() { if (!boundToLine && findNextLine()) { if (visually) ch = (dir < 0 ? lineRight : lineLeft)(lineObj); else ch = dir < 0 ? lineObj.text.length : 0; - } else return false; + } else return (possible = false); } else ch = next; return true; } + if (unit == "char") moveOnce(); else if (unit == "column") moveOnce(true); else if (unit == "word") { @@ -2470,7 +2472,9 @@ window.CodeMirror = (function() { if (dir > 0) if (!moveOnce()) break; } } - return skipAtomic(doc, Pos(line, ch), dir, true); + var result = skipAtomic(doc, Pos(line, ch), dir, true); + if (!possible) result.hitSide = true; + return result; } function findWordAt(line, pos) { @@ -2708,18 +2712,29 @@ window.CodeMirror = (function() { execCommand: function(cmd) {return commands[cmd](this);}, - // Stuff used by commands, probably not much use to outside code. + findPosH: function(from, amount, unit, visually) { + var dir = 1; + if (amount < 0) { dir = -1; amount = -amount; } + for (var i = 0, cur = clipPos(this.doc, from); i < amount; ++i) { + cur = findPosH(this.doc, cur, dir, unit, visually); + if (cur.hitSide) break; + } + return cur; + }, + moveH: operation(null, function(dir, unit) { - var sel = this.doc.sel, pos = dir < 0 ? sel.from : sel.to; + var sel = this.doc.sel, pos; if (sel.shift || sel.extend || posEq(sel.from, sel.to)) - pos = findPosH(this.doc, dir, unit, this.options.rtlMoveVisually); + pos = findPosH(this.doc, sel.head, dir, unit, this.options.rtlMoveVisually); + else + pos = dir < 0 ? sel.from : sel.to; extendSelection(this.doc, pos, pos, dir); }), deleteH: operation(null, function(dir, unit) { var sel = this.doc.sel; if (!posEq(sel.from, sel.to)) replaceRange(this.doc, "", sel.from, sel.to, "+delete"); - else replaceRange(this.doc, "", sel.from, findPosH(this.doc, dir, unit, false), "+delete"); + else replaceRange(this.doc, "", sel.from, findPosH(this.doc, sel.head, dir, unit, false), "+delete"); this.curOp.userSelChange = true; }), diff --git a/test/test.js b/test/test.js index 3a3dfac2d7..871c095d7d 100644 --- a/test/test.js +++ b/test/test.js @@ -1273,3 +1273,24 @@ testCM("addKeyMap", function(cm) { sendKey(39); eqPos(cm.getCursor(), Pos(0, 3)); }, {value: "abc"}); + +testCM("findPosH", function(cm) { + forEach([{from: Pos(0, 0), to: Pos(0, 1), by: 1}, + {from: Pos(0, 0), to: Pos(0, 0), by: -1, hitSide: true}, + {from: Pos(0, 0), to: Pos(0, 4), by: 1, unit: "word"}, + {from: Pos(0, 0), to: Pos(0, 8), by: 2, unit: "word"}, + {from: Pos(0, 0), to: Pos(2, 0), by: 20, unit: "word", hitSide: true}, + {from: Pos(0, 7), to: Pos(0, 5), by: -1, unit: "word"}, + {from: Pos(0, 4), to: Pos(0, 8), by: 1, unit: "word"}, + {from: Pos(1, 0), to: Pos(1, 18), by: 3, unit: "word"}, + {from: Pos(1, 22), to: Pos(1, 5), by: -3, unit: "word"}, + {from: Pos(1, 15), to: Pos(1, 10), by: -5}, + {from: Pos(1, 15), to: Pos(1, 10), by: -5, unit: "column"}, + {from: Pos(1, 15), to: Pos(1, 0), by: -50, unit: "column", hitSide: true}, + {from: Pos(1, 15), to: Pos(1, 24), by: 50, unit: "column", hitSide: true}, + {from: Pos(1, 15), to: Pos(2, 0), by: 50, hitSide: true}], function(t) { + var r = cm.findPosH(t.from, t.by, t.unit || "char"); + eqPos(r, t.to); + eq(!!r.hitSide, !!t.hitSide); + }); +}, {value: "line one\nline two.something.other\n"}); From 6638d16c0e44b238df42b780d4f967f4af62afa7 Mon Sep 17 00:00:00 2001 From: Marijn Haverbeke Date: Fri, 15 Feb 2013 12:53:46 +0100 Subject: [PATCH 0722/5780] Export and document findPosV Issue #1235 --- doc/manual.html | 6 ++++++ lib/codemirror.js | 53 +++++++++++++++++++++++++++++++++-------------- 2 files changed, 43 insertions(+), 16 deletions(-) diff --git a/doc/manual.html b/doc/manual.html index 23ef10abac..04cadcb37b 100644 --- a/doc/manual.html +++ b/doc/manual.html @@ -750,6 +750,12 @@

    Cursor and selection methods

    the motion was clipped by hitting the end or start of the document, the returned value will have a hitSide property set to true. +
    cm.findPosV(start, amount, unit) → object
    +
    Similar to findPosH, + but used for vertical motion. unit may + be "line" or "page". The other + arguments and the returned value have the same interpretation as + they have in findPosH.

    Configuration methods

    diff --git a/lib/codemirror.js b/lib/codemirror.js index 5030fb70f8..3dfad34da0 100644 --- a/lib/codemirror.js +++ b/lib/codemirror.js @@ -2477,6 +2477,23 @@ window.CodeMirror = (function() { return result; } + function findPosV(cm, pos, dir, unit) { + var doc = cm.doc, possible = true, x = pos.left, y; + if (unit == "page") { + var pageSize = Math.min(cm.display.wrapper.clientHeight, window.innerHeight || document.documentElement.clientHeight); + y = pos.top + dir * pageSize; + } else if (unit == "line") { + y = dir > 0 ? pos.bottom + 3 : pos.top - 3; + } + for (;;) { + var target = coordsChar(cm, x, y); + if (!target.outside) break; + if (dir < 0 ? y <= 0 : y >= doc.height) { target.hitSide = true; break; } + y += dir * 5; + } + return target; + } + function findWordAt(line, pos) { var start = pos.ch, end = pos.ch; if (line) { @@ -2738,25 +2755,29 @@ window.CodeMirror = (function() { this.curOp.userSelChange = true; }), - moveV: operation(null, function(dir, unit) { - var doc = this.doc, display = this.display; - var cur = doc.sel.head, pos = cursorCoords(this, cur, "div"); - var x = pos.left, y; - if (doc.sel.goalColumn != null) x = doc.sel.goalColumn; - if (unit == "page") { - var pageSize = Math.min(display.wrapper.clientHeight, window.innerHeight || document.documentElement.clientHeight); - y = pos.top + dir * pageSize; - } else if (unit == "line") { - y = dir > 0 ? pos.bottom + 3 : pos.top - 3; + findPosV: function(from, amount, unit) { + var dir = 1, x = null; + if (amount < 0) { dir = -1; amount = -amount; } + for (var i = 0, cur = clipPos(this.doc, from); i < amount; ++i) { + var coords = cursorCoords(this, cur, "div"); + if (x == null) x = coords.left; + else coords.left = x; + cur = findPosV(this, coords, dir, unit); + if (cur.hitSide) break; } - do { - var target = coordsChar(this, x, y); - y += dir * 5; - } while (target.outside && (dir < 0 ? y > 0 : y < doc.height)); + return cur; + }, + + moveV: operation(null, function(dir, unit) { + var sel = this.doc.sel; + var pos = cursorCoords(this, sel.head, "div"); + if (sel.goalColumn != null) pos.left = sel.goalColumn; + var target = findPosV(this, pos, dir, unit); - if (unit == "page") display.scrollbarV.scrollTop += charCoords(this, target, "div").top - pos.top; + if (unit == "page") + this.display.scrollbarV.scrollTop += charCoords(this, target, "div").top - pos.top; extendSelection(this.doc, target, target, dir); - doc.sel.goalColumn = x; + sel.goalColumn = pos.left; }), toggleOverwrite: function() { From f3d0bbbeac9d12d876ecace7fa5d2c640e904d54 Mon Sep 17 00:00:00 2001 From: Marijn Haverbeke Date: Fri, 15 Feb 2013 13:00:41 +0100 Subject: [PATCH 0723/5780] Use Pos constructor for all internal positions --- lib/codemirror.js | 16 ++++++++++++---- 1 file changed, 12 insertions(+), 4 deletions(-) diff --git a/lib/codemirror.js b/lib/codemirror.js index 3dfad34da0..2544846320 100644 --- a/lib/codemirror.js +++ b/lib/codemirror.js @@ -1103,11 +1103,17 @@ window.CodeMirror = (function() { return main; } + function PosMaybeOutside(line, ch, outside) { + var pos = new Pos(line, ch); + if (outside) pos.outside = true; + return pos; + } + // Coords must be lineSpace-local function coordsChar(cm, x, y) { var doc = cm.doc; y += cm.display.viewOffset; - if (y < 0) return {line: doc.first, ch: 0, outside: true}; + if (y < 0) return PosMaybeOutside(doc.first, 0, true); var lineNo = lineAtHeight(doc, y), last = doc.first + doc.size - 1; if (lineNo > last) return Pos(doc.size - 1, getLine(doc, last).text.length); @@ -1144,13 +1150,15 @@ window.CodeMirror = (function() { var from = lineLeft(lineObj), to = lineRight(lineObj); var fromX = getX(from), fromOutside = wrongLine, toX = getX(to), toOutside = wrongLine; - if (x > toX) return {line: lineNo, ch: to, outside: toOutside}; + if (x > toX) return PosMaybeOutside(lineNo, to, toOutside); // Do a binary search between these bounds. for (;;) { if (bidi ? to == from || to == moveVisually(lineObj, from, 1) : to - from <= 1) { var after = x - fromX < toX - x, ch = after ? from : to; while (isExtendingChar.test(lineObj.text.charAt(ch))) ++ch; - return {line: lineNo, ch: ch, after: after, outside: after ? fromOutside : toOutside}; + var pos = PosMaybeOutside(lineNo, ch, after ? fromOutside : toOutside); + pos.after = after; + return pos; } var step = Math.ceil(dist / 2), middle = from + step; if (bidi) { @@ -2478,7 +2486,7 @@ window.CodeMirror = (function() { } function findPosV(cm, pos, dir, unit) { - var doc = cm.doc, possible = true, x = pos.left, y; + var doc = cm.doc, x = pos.left, y; if (unit == "page") { var pageSize = Math.min(cm.display.wrapper.clientHeight, window.innerHeight || document.documentElement.clientHeight); y = pos.top + dir * pageSize; From bdb776e154f8a12e22063aab8d8fd15df918d1f1 Mon Sep 17 00:00:00 2001 From: Marijn Haverbeke Date: Fri, 15 Feb 2013 13:03:03 +0100 Subject: [PATCH 0724/5780] [manual] Make description of getCursor less confusing --- doc/manual.html | 17 ++++++++--------- 1 file changed, 8 insertions(+), 9 deletions(-) diff --git a/doc/manual.html b/doc/manual.html index 04cadcb37b..79ca16f681 100644 --- a/doc/manual.html +++ b/doc/manual.html @@ -701,15 +701,14 @@

    Cursor and selection methods

    Replace the selection with the given string.
    doc.getCursor(start) → object
    -
    start is a boolean indicating whether the start - or the end of the selection must be retrieved. If it is not - given, the current cursor pos, i.e. the side of the selection - that would move if you pressed an arrow key, is chosen. - Alternatively, you can pass one of the - strings "start", "end", "head" - (same as not passing anything), or "anchor" (the - opposite). A {line, ch} object will be - returned.
    +
    start is a an optional string indicating which + end of the selection to return. It may + be "start", "end", "head" + (the side of the selection that moves when you press + shift+arrow), or "anchor" (the fixed side of the + selection). Omitting the argument is the same as + passing "head". A {line, ch} object + will be returned.
    doc.somethingSelected() → boolean
    Return true if any text is selected.
    doc.setCursor(pos)
    From 5a26cb068559360082e324cd94af30d6113500bd Mon Sep 17 00:00:00 2001 From: Marijn Haverbeke Date: Fri, 15 Feb 2013 13:35:21 +0100 Subject: [PATCH 0725/5780] Add beforeChange event Issue #1244 --- doc/manual.html | 24 ++++++++++++++++++++++++ lib/codemirror.js | 27 +++++++++++++++++++++++++++ test/test.js | 18 ++++++++++++++++++ 3 files changed, 69 insertions(+) diff --git a/doc/manual.html b/doc/manual.html index 79ca16f681..a40143e4f6 100644 --- a/doc/manual.html +++ b/doc/manual.html @@ -365,6 +365,26 @@

    Events

    a next property pointing to another change object (which may point to another, etc). +
    "beforeChange" (instance, change)
    +
    This event is fired before a change is applied, and its + handler may choose to modify or cancel the change. + The change object + has from, to, and text + properties, as with + the "change" event, but + never a next property, since this is fired for each + individual change, and not batched per operation. It also + has update(from, to, text) + and cancel() methods, which may be used to modify + or cancel the change. All three arguments to update + are optional, and can be left off to leave the existing value + for that field intact. Note: you may not do + anything from a "beforeChange" handler that would + cause changes to the document or its visualization. Doing so + will, since this handler is called directly from the bowels of + the CodeMirror implementation, probably cause the editor to + become corrupted.
    +
    "cursorActivity" (instance)
    Will be fired when the cursor or selection moves, or any change is made to the editor content.
    @@ -410,6 +430,10 @@

    Events

    document change events are not batched (whereas editor change events are). +
    "beforeChange" (doc, change)
    +
    See the description of the + same event on editor instances.
    +
    "cursorActivity" (doc)
    Fired whenever the cursor or selection in this document changes.
    diff --git a/lib/codemirror.js b/lib/codemirror.js index 2544846320..a95d6f4bff 100644 --- a/lib/codemirror.js +++ b/lib/codemirror.js @@ -2055,6 +2055,28 @@ window.CodeMirror = (function() { return {anchor: adjustPos(sel.anchor), head: adjustPos(sel.head)}; } + function filterChange(doc, change) { + var obj = { + canceled: false, + from: change.from, + to: change.to, + text: change.text, + origin: change.origin, + update: function(from, to, text, origin) { + if (from) this.from = clipPos(doc, from); + if (to) this.to = clipPos(doc.to); + if (text) this.text = text; + if (origin !== undefined) this.origin = origin; + }, + cancel: function() { this.canceled = true; } + }; + signal(doc, "beforeChange", doc, obj); + if (doc.cm) signal(doc.cm, "beforeChange", doc.cm, obj); + + if (obj.canceled) return null; + return {from: obj.from, to: obj.to, text: obj.text, origin: obj.origin}; + } + // Replace the range from from to to by the strings in replacement. // change is a {from, to, text [, origin]} object function makeChange(doc, change, selUpdate, ignoreReadOnly) { @@ -2063,6 +2085,11 @@ window.CodeMirror = (function() { if (doc.cm.state.suppressEdits) return; } + if (hasHandler(doc, "beforeChange") || doc.cm && hasHandler(doc.cm, "beforeChange")) { + change = filterChange(doc, change); + if (!change) return; + } + // Possibly split or suppress the update based on the presence // of read-only spans in its range. var split = sawReadOnlySpans && !ignoreReadOnly && removeReadOnlyRanges(doc, change.from, change.to); diff --git a/test/test.js b/test/test.js index 871c095d7d..79e8a9bca1 100644 --- a/test/test.js +++ b/test/test.js @@ -1294,3 +1294,21 @@ testCM("findPosH", function(cm) { eq(!!r.hitSide, !!t.hitSide); }); }, {value: "line one\nline two.something.other\n"}); + +testCM("beforeChange", function(cm) { + cm.on("beforeChange", function(cm, change) { + var text = []; + for (var i = 0; i < change.text.length; ++i) + text.push(change.text[i].replace(/\s/g, "_")); + change.update(null, null, text); + }); + cm.setValue("hello, i am a\nnew document\n"); + eq(cm.getValue(), "hello,_i_am_a\nnew_document\n"); + CodeMirror.on(cm.getDoc(), "beforeChange", function(doc, change) { + if (change.from.line == 0) change.cancel(); + }); + cm.setValue("oops"); // Canceled + eq(cm.getValue(), "hello,_i_am_a\nnew_document\n"); + cm.replaceRange("hey hey hey", Pos(1, 0), Pos(2, 0)); + eq(cm.getValue(), "hello,_i_am_a\nhey_hey_hey"); +}, {value: "abcdefghijk"}); From b1df9d5efaa9aab345df1d0e5a4ccefba32a1954 Mon Sep 17 00:00:00 2001 From: Marijn Haverbeke Date: Fri, 15 Feb 2013 13:55:07 +0100 Subject: [PATCH 0726/5780] Properly set 'outside' property when fetching a position below the document --- lib/codemirror.js | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/lib/codemirror.js b/lib/codemirror.js index a95d6f4bff..d480f40168 100644 --- a/lib/codemirror.js +++ b/lib/codemirror.js @@ -1116,7 +1116,7 @@ window.CodeMirror = (function() { if (y < 0) return PosMaybeOutside(doc.first, 0, true); var lineNo = lineAtHeight(doc, y), last = doc.first + doc.size - 1; if (lineNo > last) - return Pos(doc.size - 1, getLine(doc, last).text.length); + return PosMaybeOutside(doc.size - 1, getLine(doc, last).text.length, true); if (x < 0) x = 0; for (;;) { From c430dec1f0ddbda6d9344d06dbaf52389968cec2 Mon Sep 17 00:00:00 2001 From: Marijn Haverbeke Date: Fri, 15 Feb 2013 13:56:54 +0100 Subject: [PATCH 0727/5780] Allow passing in a goalColumn to findPosV --- lib/codemirror.js | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/lib/codemirror.js b/lib/codemirror.js index d480f40168..c42b2c2983 100644 --- a/lib/codemirror.js +++ b/lib/codemirror.js @@ -2790,8 +2790,8 @@ window.CodeMirror = (function() { this.curOp.userSelChange = true; }), - findPosV: function(from, amount, unit) { - var dir = 1, x = null; + findPosV: function(from, amount, unit, goalColumn) { + var dir = 1, x = goalColumn; if (amount < 0) { dir = -1; amount = -amount; } for (var i = 0, cur = clipPos(this.doc, from); i < amount; ++i) { var coords = cursorCoords(this, cur, "div"); From 1ccc8a088fd640dc277f48b6f076eef77361e69c Mon Sep 17 00:00:00 2001 From: "angelo.zerr@gmail.com" Date: Fri, 15 Feb 2013 14:33:35 +0100 Subject: [PATCH 0728/5780] Close Bracket Feature. --- addon/edit/closebracket.js | 39 ++++++++++++++++++++++++++++++++++++++ demo/closebracket.html | 31 ++++++++++++++++++++++++++++++ 2 files changed, 70 insertions(+) create mode 100644 addon/edit/closebracket.js create mode 100644 demo/closebracket.html diff --git a/addon/edit/closebracket.js b/addon/edit/closebracket.js new file mode 100644 index 0000000000..bd29f12792 --- /dev/null +++ b/addon/edit/closebracket.js @@ -0,0 +1,39 @@ +(function() { + var DEFAULT_BRACKETS = [ "()", "[]", "{}", "''", "\"\"" ]; + CodeMirror.defineOption("autoCloseBrackets", false, function(cm, val, old) { + if (val && (old == CodeMirror.Init || !old)) { + var map = { + name : "autoCloseBrackets" + }; + var brackets = val.brackets || val.brackets || DEFAULT_BRACKETS; + for ( var i = 0; i < brackets.length; i++) { + var bracket = brackets[i]; + var startBracket = bracket.charAt(0); + var endBracket = bracket.substring(1, bracket.length); + attachAutoCloseBracket(startBracket, endBracket, map) + } + cm.addKeyMap(map); + } else if (!val && (old != CodeMirror.Init && old)) { + cm.removeKeyMap("autoCloseBrackets"); + } + }); + + function attachAutoCloseBracket(startBracket, endBracket, map) { + map["'" + startBracket + "'"] = function(cm) { + autoCloseBracket(cm, startBracket, endBracket); + }; + }; + + function autoCloseBracket(cm, startBracket, endBracket) { + +// if (cm.somethingSelected()) { +// alert('e'); +// //cm.replaceSelection("[#{cm.getSelection()}]") +// //CodeMirror.commands["goCharRight"](cm) +// } else { + cm.replaceRange(endBracket, cm.getCursor(false)) + CodeMirror.commands["goCharLeft"](cm) + //} + throw CodeMirror.Pass; + } +})(); diff --git a/demo/closebracket.html b/demo/closebracket.html new file mode 100644 index 0000000000..59b9144593 --- /dev/null +++ b/demo/closebracket.html @@ -0,0 +1,31 @@ + + + + + CodeMirror: Close-Tag Demo + + + + + + + + + +

    Close-Bracket Demo

    +
      +
    • Type a bracket like '[', '(', '{', '"', or ''' and it will close the bracket and set the cursor on the middle.
    • +
    + +
    + + + + From 085ab5180bdbcb154f903928996e04283aecc338 Mon Sep 17 00:00:00 2001 From: Marijn Haverbeke Date: Fri, 15 Feb 2013 16:21:55 +0100 Subject: [PATCH 0729/5780] [closebrackets addon] Clean up and integrate --- addon/edit/closebracket.js | 39 ------------------------ addon/edit/closebrackets.js | 29 ++++++++++++++++++ demo/closebracket.html | 31 ------------------- demo/closebrackets.html | 59 +++++++++++++++++++++++++++++++++++++ doc/compress.html | 1 + doc/manual.html | 7 +++++ 6 files changed, 96 insertions(+), 70 deletions(-) delete mode 100644 addon/edit/closebracket.js create mode 100644 addon/edit/closebrackets.js delete mode 100644 demo/closebracket.html create mode 100644 demo/closebrackets.html diff --git a/addon/edit/closebracket.js b/addon/edit/closebracket.js deleted file mode 100644 index bd29f12792..0000000000 --- a/addon/edit/closebracket.js +++ /dev/null @@ -1,39 +0,0 @@ -(function() { - var DEFAULT_BRACKETS = [ "()", "[]", "{}", "''", "\"\"" ]; - CodeMirror.defineOption("autoCloseBrackets", false, function(cm, val, old) { - if (val && (old == CodeMirror.Init || !old)) { - var map = { - name : "autoCloseBrackets" - }; - var brackets = val.brackets || val.brackets || DEFAULT_BRACKETS; - for ( var i = 0; i < brackets.length; i++) { - var bracket = brackets[i]; - var startBracket = bracket.charAt(0); - var endBracket = bracket.substring(1, bracket.length); - attachAutoCloseBracket(startBracket, endBracket, map) - } - cm.addKeyMap(map); - } else if (!val && (old != CodeMirror.Init && old)) { - cm.removeKeyMap("autoCloseBrackets"); - } - }); - - function attachAutoCloseBracket(startBracket, endBracket, map) { - map["'" + startBracket + "'"] = function(cm) { - autoCloseBracket(cm, startBracket, endBracket); - }; - }; - - function autoCloseBracket(cm, startBracket, endBracket) { - -// if (cm.somethingSelected()) { -// alert('e'); -// //cm.replaceSelection("[#{cm.getSelection()}]") -// //CodeMirror.commands["goCharRight"](cm) -// } else { - cm.replaceRange(endBracket, cm.getCursor(false)) - CodeMirror.commands["goCharLeft"](cm) - //} - throw CodeMirror.Pass; - } -})(); diff --git a/addon/edit/closebrackets.js b/addon/edit/closebrackets.js new file mode 100644 index 0000000000..09dc7de3aa --- /dev/null +++ b/addon/edit/closebrackets.js @@ -0,0 +1,29 @@ +(function() { + var DEFAULT_BRACKETS = "()[]{}''\"\""; + + CodeMirror.defineOption("autoCloseBrackets", false, function(cm, val, old) { + var wasOn = old && old != CodeMirror.Init; + if (val && !wasOn) + cm.addKeyMap(buildKeymap(typeof val == "string" ? val : DEFAULT_BRACKETS)); + else if (!val && wasOn) + cm.removeKeyMap("autoCloseBrackets"); + }); + + function buildKeymap(pairs) { + var map = {name : "autoCloseBrackets"}; + for (var i = 0; i < pairs.length; i += 2) (function(left, right) { + function maybeOverwrite(cm) { + var cur = cm.getCursor(), ahead = cm.getRange(cur, CodeMirror.Pos(cur.line, cur.ch + 1)); + if (ahead != right) return CodeMirror.Pass; + else cm.execCommand("goCharRight"); + } + map["'" + left + "'"] = function(cm) { + if (left == right && maybeOverwrite(cm) != CodeMirror.Pass) return; + var cur = cm.getCursor(), ahead = CodeMirror.Pos(cur.line, cur.ch + 1); + cm.replaceSelection(left + right, {head: ahead, anchor: ahead}); + }; + if (left != right) map["'" + right + "'"] = maybeOverwrite; + })(pairs.charAt(i), pairs.charAt(i + 1)); + return map; + } +})(); diff --git a/demo/closebracket.html b/demo/closebracket.html deleted file mode 100644 index 59b9144593..0000000000 --- a/demo/closebracket.html +++ /dev/null @@ -1,31 +0,0 @@ - - - - - CodeMirror: Close-Tag Demo - - - - - - - - - -

    Close-Bracket Demo

    -
      -
    • Type a bracket like '[', '(', '{', '"', or ''' and it will close the bracket and set the cursor on the middle.
    • -
    - -
    - - - - diff --git a/demo/closebrackets.html b/demo/closebrackets.html new file mode 100644 index 0000000000..58ba0330a1 --- /dev/null +++ b/demo/closebrackets.html @@ -0,0 +1,59 @@ + + + + + CodeMirror: Closebrackets Demo + + + + + + + + + +

    CodeMirror: Closebrackets Demo

    + +

    Type a bracket like '[', '(', '{', '"', or ''' + and the addon + will auto-close it. Type the closing variant when directly in + front of a matching character and it will overwrite it.

    + +
    + + + + diff --git a/doc/compress.html b/doc/compress.html index 1bec338058..b06062abf0 100644 --- a/doc/compress.html +++ b/doc/compress.html @@ -134,6 +134,7 @@

    { } CodeMi + diff --git a/doc/manual.html b/doc/manual.html index a40143e4f6..d7a3e87d05 100644 --- a/doc/manual.html +++ b/doc/manual.html @@ -1389,6 +1389,13 @@

    Add-ons

    once, and a method findMatchingBracket that can be used to run the bracket-finding algorithm that this uses internally. +
    closebrackets.js
    +
    Defines an option autoCloseBrackets that will + auto-close brackets and quotes when typed. By default, it'll + auto-close ()[]{}''"", but you can pass it a + string similar to that (containing pairs of matching characters) + to customize it. Demo + here.
    foldcode.js
    Helps with code folding. See the demo for an example. From 3eae20a249335fd13c7db7feacf7140893c01dd8 Mon Sep 17 00:00:00 2001 From: Metatheos Date: Mon, 11 Feb 2013 16:56:06 +0100 Subject: [PATCH 0730/5780] [vim mode] Added gj/gk, :noh, defineEx(). Better :{num}, <{Key}> handling - gj/gk for moveByDisplayLines motion, allows vertical movement within wrapped lines - :noh[lsearch] is now an ex-command - new ex-commands can be defined through api, using CodeMirror.Vim.defineEx() - :{number} should now move to line with {number} on the gutter - parseKeyString now also accepts keys like (in addition to ) --- keymap/vim.js | 127 +++++++++++++++++++++++++++++++---------------- test/vim_test.js | 59 ++++++++++++++++++++-- 2 files changed, 139 insertions(+), 47 deletions(-) diff --git a/keymap/vim.js b/keymap/vim.js index f8fe5ab4bf..e0e18cc5b4 100644 --- a/keymap/vim.js +++ b/keymap/vim.js @@ -3,6 +3,7 @@ * * Motion: * h, j, k, l + * gj, gk * e, E, w, W, b, B, ge, gE * f, F, t, T * $, ^, 0 @@ -90,6 +91,12 @@ { keys: ['k'], type: 'motion', motion: 'moveByLines', motionArgs: { forward: false, linewise: true }}, + { keys: ['g','j'], type: 'motion', + motion: 'moveByDisplayLines', + motionArgs: { forward: true, linewise: true }}, + { keys: ['g','k'], type: 'motion', + motion: 'moveByDisplayLines', + motionArgs: { forward: false, linewise: true }}, { keys: ['w'], type: 'motion', motion: 'moveByWords', motionArgs: { forward: true, wordEnd: false }}, @@ -212,7 +219,6 @@ { keys: ['Ctrl-r'], type: 'action', action: 'redo' }, { keys: ['m', 'character'], type: 'action', action: 'setMark' }, { keys: ['\"', 'character'], type: 'action', action: 'setRegister' }, - { keys: [',', '/'], type: 'action', action: 'clearSearchHighlight' }, { keys: ['z', 'z'], type: 'action', action: 'scrollToCursor', actionArgs: { position: 'center' }}, { keys: ['z', '.'], type: 'action', action: 'scrollToCursor', @@ -275,7 +281,7 @@ return alphabetRegex.test(k); } function isLine(cm, line) { - return line >= 0 && line < cm.lineCount(); + return line >= cm.firstLine() && line <= cm.lastLine(); } function isLowerCase(k) { return (/^[a-z]$/).test(k); @@ -335,6 +341,8 @@ // cursor should go back to its horizontal position on the longer // line if it can. This is to keep track of the horizontal position. lastHPos: -1, + // Doing the same with screen-position for gj/gk + lastHSPos: -1, // The last motion command run. Cleared if a non-motion command gets // executed in between. lastMotion: null, @@ -364,6 +372,12 @@ // Add user defined key bindings. exCommandDispatcher.map(lhs, rhs); }, + defineEx: function(name, prefix, func){ + if (name.indexOf(prefix) === 0) { + exCommands[name]=func; + exCommandDispatcher.commandMap_[prefix]={name:name, shortName:prefix, type:'api'}; + }else throw new Error("(Vim.defineEx) \""+prefix+"\" is not a prefix of \""+name+"\", command not registered"); + }, // Initializes vim state variable on the CodeMirror object. Should only be // called lazily by handleKey or for testing. maybeInitState: function(cm) { @@ -916,7 +930,8 @@ return { line: cur.line, ch: ch }; }, moveByLines: function(cm, motionArgs, vim) { - var endCh = cm.getCursor().ch; + var cur = cm.getCursor(); + var endCh = cur.ch; // Depending what our last motion was, we may want to do different // things. If our last motion was moving vertically, we want to // preserve the HPos from our last horizontal move. If our last motion @@ -924,6 +939,7 @@ // the end of the line, etc. switch (vim.lastMotion) { case this.moveByLines: + case this.moveByDisplayLines: case this.moveToColumn: case this.moveToEol: endCh = vim.lastHPos; @@ -931,14 +947,31 @@ default: vim.lastHPos = endCh; } - var cur = cm.getCursor(); var repeat = motionArgs.repeat; var line = motionArgs.forward ? cur.line + repeat : cur.line - repeat; - if (line < 0 || line > cm.lineCount() - 1) { + if (line < cm.firstLine() || line > cm.lastLine() ) { return null; } + vim.lastHSPos = cm.charCoords({line:line, ch:endCh},"div").left; return { line: line, ch: endCh }; }, + moveByDisplayLines: function(cm, motionArgs, vim) { + var cur = cm.getCursor(); + switch (vim.lastMotion) { + case this.moveByDisplayLines: + case this.moveByLines: + case this.moveToColumn: + case this.moveToEol: + break; + default: + vim.lastHSPos = cm.charCoords(cur,"div").left; + } + var repeat = motionArgs.repeat; + var res=cm.findPosV(cur,(motionArgs.forward ? repeat : -repeat),"line",vim.lastHSPos); + if(res.hitSide)return null; + vim.lastHPos = res.ch; + return res; + }, moveByPage: function(cm, motionArgs) { // CodeMirror only exposes functions that move the cursor page down, so // doing this bad hack to move the cursor and move it back. evalInput @@ -955,12 +988,12 @@ var repeat = motionArgs.repeat; var inc = motionArgs.forward ? 1 : -1; for (var i = 0; i < repeat; i++) { - if ((!motionArgs.forward && line === 0) || - (motionArgs.forward && line == cm.lineCount() - 1)) { + if ((!motionArgs.forward && line === cm.firstLine() ) || + (motionArgs.forward && line == cm.lastLine())) { break; } line += inc; - while (line !== 0 && line != cm.lineCount - 1 && cm.getLine(line)) { + while (line !== cm.firstLine() && line != cm.lastLine() && cm.getLine(line)) { line += inc; } } @@ -987,12 +1020,17 @@ var repeat = motionArgs.repeat; // repeat is equivalent to which column we want to move to! vim.lastHPos = repeat - 1; + vim.lastHSPos = cm.charCoords(cm.getCursor(),"div").left; return moveToColumn(cm, repeat); }, moveToEol: function(cm, motionArgs, vim) { var cur = cm.getCursor(); vim.lastHPos = Infinity; - return { line: cur.line + motionArgs.repeat - 1, ch: Infinity }; + var retval={ line: cur.line + motionArgs.repeat - 1, ch: Infinity } + var end=cm.clipPos(retval); + end.ch--; + vim.lastHSPos = cm.charCoords(end,"div").left; + return retval; }, moveToFirstNonWhiteSpaceCharacter: function(cm) { // Go to the start of the line where the text begins, or the end for @@ -1016,9 +1054,9 @@ return { line: cursor.line, ch: 0 }; }, moveToLineOrEdgeOfDocument: function(cm, motionArgs) { - var lineNum = motionArgs.forward ? cm.lineCount() - 1 : 0; + var lineNum = motionArgs.forward ? cm.lastLine() : cm.firstLine(); if (motionArgs.repeatIsExplicit) { - lineNum = motionArgs.repeat - 1; + lineNum = motionArgs.repeat - cm.getOption('firstLineNumber'); } return { line: lineNum, ch: findFirstNonWhiteSpaceCharacter(cm.getLine(lineNum)) }; @@ -1117,7 +1155,6 @@ }; var actions = { - clearSearchHighlight: clearSearchHighlight, scrollToCursor: function(cm, actionArgs) { var lineNum = cm.getCursor().line; var heightProp = window.getComputedStyle(cm.getScrollerElement()). @@ -1234,10 +1271,10 @@ }, newLineAndEnterInsertMode: function(cm, actionArgs) { var insertAt = cm.getCursor(); - if (insertAt.line === 0 && !actionArgs.after) { + if (insertAt.line === cm.firstLine() && !actionArgs.after) { // Special case for inserting newline before start of document. - cm.replaceRange('\n', { line: 0, ch: 0 }); - cm.setCursor(0, 0); + cm.replaceRange('\n', { line: cm.firstLine(), ch: 0 }); + cm.setCursor(cm.firstLine(), 0); } else { insertAt.line = (actionArgs.after) ? insertAt.line : insertAt.line - 1; @@ -1357,14 +1394,11 @@ */ /** - * Clips cursor to ensure that: - * 0 <= cur.ch < lineLength - * AND - * 0 <= cur.line < lineCount + * Clips cursor to ensure that line is within the buffer's range * If includeLineBreak is true, then allow cur.ch == lineLength. */ function clipCursorToContent(cm, cur, includeLineBreak) { - var line = Math.min(Math.max(0, cur.line), cm.lineCount() - 1); + var line = Math.min(Math.max(cm.firstLine(), cur.line), cm.lastLine() ); var maxCh = lineLength(cm, line) - 1; maxCh = (includeLineBreak) ? maxCh + 1 : maxCh; var ch = Math.min(Math.max(0, cur.ch), maxCh); @@ -2091,7 +2125,7 @@ // SearchCursor may have returned null because it hit EOF, wrap // around and try again. cursor = cm.getSearchCursor(query, - (prev) ? { line: cm.lineCount() - 1} : {line: 0, ch: 0} ); + (prev) ? { line: cm.lastLine() } : {line: cm.firstLine(), ch: 0} ); if (!cursor.find(prev)) { return; } @@ -2156,7 +2190,8 @@ { name: 'write', shortName: 'w', type: 'builtIn' }, { name: 'undo', shortName: 'u', type: 'builtIn' }, { name: 'redo', shortName: 'red', type: 'builtIn' }, - { name: 'substitute', shortName: 's', type: 'builtIn'} + { name: 'substitute', shortName: 's', type: 'builtIn'}, + { name: 'nohlsearch', shortName: 'noh', type: 'builtIn'} ]; Vim.ExCommandDispatcher = function() { this.buildCommandMap_(); @@ -2206,8 +2241,8 @@ inputStream.eatWhile(':'); // Parse range. if (inputStream.eat('%')) { - result.line = 0; - result.lineEnd = cm.lineCount() - 1; + result.line = cm.firstLine(); + result.lineEnd = cm.lastLine(); } else { result.line = this.parseLineSpec_(cm, inputStream); if (result.line !== undefined && inputStream.eat(',')) { @@ -2234,7 +2269,7 @@ case '.': return cm.getCursor().line; case '$': - return cm.lineCount() - 1; + return cm.lastLine(); case '\'': var mark = getVimState(cm).marks[inputStream.next()]; if (mark && mark.find()) { @@ -2337,25 +2372,28 @@ var vimKeyNotationStart = ++idx; while (str.charAt(idx++) != '>') {} var vimKeyNotation = str.substring(vimKeyNotationStart, idx - 1); + var mod=''; var match = (/^C-(.+)$/).exec(vimKeyNotation); if (match) { - var key; - switch (match[1]) { - case 'BS': - key = 'Backspace'; - break; - case 'CR': - key = 'Enter'; - break; - case 'Del': - key = 'Delete'; - break; - default: - key = match[1]; - break; - } - keys.push('Ctrl-' + key); + mod='Ctrl-'; + vimKeyNotation=match[1]; + } + var key; + switch (vimKeyNotation) { + case 'BS': + key = 'Backspace'; + break; + case 'CR': + key = 'Enter'; + break; + case 'Del': + key = 'Delete'; + break; + default: + key = vimKeyNotation; + break; } + keys.push(mod + key); } return keys; } @@ -2375,7 +2413,7 @@ commandDispatcher.processMotion(cm, getVimState(cm), { motion: 'moveToLineOrEdgeOfDocument', motionArgs: { forward: false, explicitRepeat: true, - linewise: true, repeat: params.line }}); + linewise: true, repeat: params.line+1 }}); }, substitute: function(cm, params) { var argString = params.argString; @@ -2410,7 +2448,7 @@ } var state = getSearchState(cm); var query = state.getQuery(); - var lineStart = params.line || 0; + var lineStart = params.line || cm.firstLine(); var lineEnd = params.lineEnd || lineStart; if (count) { lineStart = lineEnd; @@ -2447,6 +2485,9 @@ // Saves to text area if no save command is defined. cm.save(); } + }, + nohlsearch: function(cm) { + clearSearchHighlight(cm); } }; diff --git a/test/vim_test.js b/test/vim_test.js index d91abe5ad0..52d9ea3909 100644 --- a/test/vim_test.js +++ b/test/vim_test.js @@ -87,7 +87,6 @@ function copyCursor(cur) { function testVim(name, run, opts, expectedFail) { var vimOpts = { lineNumbers: true, - mode: 'text/x-csrc', keyMap: 'vim', showCursorWhenSelecting: true, value: code @@ -266,6 +265,41 @@ testVim('Changing lines after Eol operation', function(cm, vim, helpers) { // same place we were at on line 1 helpers.assertCursorAt({ line: 5, ch: lines[2].length - 2 }); }); +//making sure gj and gk recover from clipping +testVim('gj_gk_clipping', function(cm,vim,helpers){ + cm.setCursor(0, 1); + helpers.doKeys('g','j','g','j'); + helpers.assertCursorAt(2, 1); + helpers.doKeys('g','k','g','k'); + helpers.assertCursorAt(0, 1); +},{value: 'line 1\n\nline 2'}); +//testing a mix of j/k and gj/gk +testVim('j_k_and_gj_gk', function(cm,vim,helpers){ + cm.setSize(120); + cm.setCursor(0, 0); + //go to the last character on the first line + helpers.doKeys('$'); + //move up/down on the column within the wrapped line + //side-effect: cursor is not locked to eol anymore + helpers.doKeys('g','k'); + var cur=cm.getCursor(); + eq(cur.line,0); + is((cur.ch<176),'gk didn\'t move cursor back (1)'); + helpers.doKeys('g','j'); + helpers.assertCursorAt(0, 176); + //should move to character 177 on line 2 (j/k preserve character index within line) + helpers.doKeys('j'); + //due to different line wrapping, the cursor can be on a different screen-x now + //gj and gk preserve screen-x on movement, much like moveV + helpers.doKeys('3','g','k'); + cur=cm.getCursor(); + eq(cur.line,1); + is((cur.ch<176),'gk didn\'t move cursor back (2)'); + helpers.doKeys('g','j','2','g','j'); + //should return to the same character-index + helpers.doKeys('k'); + helpers.assertCursorAt(0, 176); +},{ lineWrapping:true, value: 'This line is intentially long to test movement of gj and gk over wrapped lines. I will start on the end of this line, then make a step up and back to set the origin for j and k.\nThis line is supposed to be even longer than the previous. I will jump here and make another wiggle with gj and gk, before I jump back to the line above. Both wiggles should not change my cursor\'s target character but both j/k and gj/gk change each other\'s reference position.'}); testVim('}', function(cm, vim, helpers) { cm.setCursor(0, 0); helpers.doKeys('}'); @@ -827,11 +861,14 @@ testVim('? and n/N', function(cm, vim, helpers) { helpers.doKeys('2', '?'); helpers.assertCursorAt(0, 11); }, { value: 'match nope match \n nope Match' }); -testVim(',/ clearSearchHighlight', function(cm, vim, helpers) { +//:noh should clear highlighting of search-results but allow to resume search through n +testVim('noh_clearSearchHighlight', function(cm, vim, helpers) { cm.openDialog = helpers.fakeOpenDialog('match'); helpers.doKeys('?'); - helpers.doKeys(',', '/', 'n'); - helpers.assertCursorAt(0, 11); + helpers.doEx('noh'); + eq(vim.searchState_.getOverlay(),null,'match-highlighting wasn\'t cleared'); + helpers.doKeys('n'); + helpers.assertCursorAt(0, 11,'can\'t resume search after clearing highlighting'); }, { value: 'match nope match \n nope Match' }); testVim('*', function(cm, vim, helpers) { cm.setCursor(0, 9); @@ -987,6 +1024,20 @@ testVim('ex_map_key2ex', function(cm, vim, helpers) { eq(written, true); eq(actualCm, cm); }); +// Testing registration of functions as ex-commands and mapping to -keys +testVim('ex_api_test', function(cm, vim, helpers) { + var res=false; + var val='from'; + CodeMirror.Vim.defineEx('extest','ext',function(cm,params){ + if(params.args)val=params.args[0]; + else res=true; + }); + helpers.doEx(':ext to'); + eq(val,'to','Defining ex-command failed'); + CodeMirror.Vim.map('',':ext'); + helpers.doKeys('Ctrl-Enter','Space'); + is(res,'Mapping to key failed'); +}); // For now, this test needs to be last because it messes up : for future tests. testVim('ex_map_key2key_from_colon', function(cm, vim, helpers) { helpers.doEx('map : x'); From c3b97cb59684cf8af012bb6b6eb19139bdf45e01 Mon Sep 17 00:00:00 2001 From: Marijn Haverbeke Date: Fri, 15 Feb 2013 16:38:35 +0100 Subject: [PATCH 0731/5780] [closebrackets addon] Fix bug in cursor placement --- addon/edit/closebrackets.js | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/addon/edit/closebrackets.js b/addon/edit/closebrackets.js index 09dc7de3aa..9ca4ce1f5c 100644 --- a/addon/edit/closebrackets.js +++ b/addon/edit/closebrackets.js @@ -19,7 +19,7 @@ } map["'" + left + "'"] = function(cm) { if (left == right && maybeOverwrite(cm) != CodeMirror.Pass) return; - var cur = cm.getCursor(), ahead = CodeMirror.Pos(cur.line, cur.ch + 1); + var cur = cm.getCursor("start"), ahead = CodeMirror.Pos(cur.line, cur.ch + 1); cm.replaceSelection(left + right, {head: ahead, anchor: ahead}); }; if (left != right) map["'" + right + "'"] = maybeOverwrite; From f1c4978de0deb562329ef9f53ee556fe72a9891f Mon Sep 17 00:00:00 2001 From: Marijn Haverbeke Date: Fri, 15 Feb 2013 17:17:33 +0100 Subject: [PATCH 0732/5780] Take care that selection updates in changes don't create invalid selections --- lib/codemirror.js | 36 +++++++++++++++++++++++++++++------- 1 file changed, 29 insertions(+), 7 deletions(-) diff --git a/lib/codemirror.js b/lib/codemirror.js index c42b2c2983..0b5ac78e67 100644 --- a/lib/codemirror.js +++ b/lib/codemirror.js @@ -2034,9 +2034,28 @@ window.CodeMirror = (function() { lst(change.text).length + (change.text.length == 1 ? change.from.ch : 0)); } + // Make sure a position will be valid after the given change. + function clipPostChange(doc, change, pos) { + if (!posLess(change.from, pos)) return clipPos(doc, pos); + var diff = (change.text.length - 1) - (change.to.line - change.from.line); + if (pos.line > change.to.line + diff) { + var preLine = pos.line - diff, lastLine = doc.first + doc.size - 1; + if (preLine > lastLine) return Pos(lastLine, getLine(doc, lastLine).text.length); + return clipToLen(pos, getLine(doc, preLine).text.length); + } + if (pos.line == change.to.line + diff) + return clipToLen(pos, lst(change.text).length + (change.text.length == 1 ? change.from.ch : 0) + + getLine(doc, change.to.line).text.length - change.to.ch); + var inside = pos.line - change.from.line; + return clipToLen(pos, change.text[inside].length + (inside ? 0 : change.from.ch)); + } + // Hint can be null|"end"|"start"|"around"|{anchor,head} - function computeSelAfterChange(sel, change, hint) { - if (hint && typeof hint == "object") return hint; // Assumed to be {from, to} object + function computeSelAfterChange(doc, change, hint) { + if (hint && typeof hint == "object") // Assumed to be {anchor, head} object + return {anchor: clipPostChange(doc, change, hint.anchor), + head: clipPostChange(doc, change, hint.head)}; + if (hint == "start") return {anchor: change.from, head: change.from}; var end = changeEnd(change); @@ -2052,7 +2071,7 @@ window.CodeMirror = (function() { if (pos.line == change.to.line) ch += end.ch - change.to.ch; return Pos(line, ch); }; - return {anchor: adjustPos(sel.anchor), head: adjustPos(sel.head)}; + return {anchor: adjustPos(doc.sel.anchor), head: adjustPos(doc.sel.head)}; } function filterChange(doc, change) { @@ -2104,7 +2123,7 @@ window.CodeMirror = (function() { } function makeChangeNoReadonly(doc, change, selUpdate) { - var selAfter = computeSelAfterChange(doc.sel, change, selUpdate); + var selAfter = computeSelAfterChange(doc, change, selUpdate); addToHistory(doc, change, selAfter, doc.cm ? doc.cm.curOp.id : NaN); makeChangeSingleDoc(doc, change, selAfter, stretchSpansOverChange(doc, change)); @@ -2134,7 +2153,7 @@ window.CodeMirror = (function() { change.origin = type; anti.changes.push(historyChangeFromChange(doc, change)); - var after = i ? computeSelAfterChange(doc.sel, change, null) + var after = i ? computeSelAfterChange(doc, change, null) : {anchor: event.anchorBefore, head: event.headBefore}; makeChangeSingleDoc(doc, change, after, mergeOldSpans(doc, change)); var rebased = []; @@ -2180,7 +2199,7 @@ window.CodeMirror = (function() { text: [change.text[0]], origin: change.origin}; } - if (!selAfter) selAfter = computeSelAfterChange(doc.sel, change, null); + if (!selAfter) selAfter = computeSelAfterChange(doc, change, null); if (doc.cm) makeChangeSingleDocInEditor(doc.cm, change, spans, selAfter); else updateDoc(doc, change, spans, selAfter); } @@ -2256,7 +2275,10 @@ window.CodeMirror = (function() { if (pos.line < doc.first) return Pos(doc.first, 0); var last = doc.first + doc.size - 1; if (pos.line > last) return Pos(last, getLine(doc, last).text.length); - var ch = pos.ch, linelen = getLine(doc, pos.line).text.length; + return clipToLen(pos, getLine(doc, pos.line).text.length); + } + function clipToLen(pos, linelen) { + var ch = pos.ch; if (ch == null || ch > linelen) return Pos(pos.line, linelen); else if (ch < 0) return Pos(pos.line, 0); else return pos; From 6bbcad10ed4c7f9d04372bfaa2bbfc43c8cdcf07 Mon Sep 17 00:00:00 2001 From: Marijn Haverbeke Date: Fri, 15 Feb 2013 17:22:51 +0100 Subject: [PATCH 0733/5780] Remove e_prop hack I don't think anyone is using it anymore, and it was a kludge to begin with. --- lib/codemirror.js | 33 +++++++++++++-------------------- 1 file changed, 13 insertions(+), 20 deletions(-) diff --git a/lib/codemirror.js b/lib/codemirror.js index 0b5ac78e67..731920c614 100644 --- a/lib/codemirror.js +++ b/lib/codemirror.js @@ -1446,7 +1446,7 @@ window.CodeMirror = (function() { on(d.input, "keyup", operation(cm, function(e) { if (cm.options.onKeyEvent && cm.options.onKeyEvent(cm, addStop(e))) return; - if (e_prop(e, "keyCode") == 16) cm.doc.sel.shift = false; + if (e.keyCode == 16) cm.doc.sel.shift = false; })); on(d.input, "input", bind(fastPoll, cm)); on(d.input, "keydown", operation(cm, onKeyDown)); @@ -1517,7 +1517,7 @@ window.CodeMirror = (function() { var lastClick, lastDoubleClick; function onMouseDown(e) { var cm = this, display = cm.display, doc = cm.doc, sel = doc.sel; - sel.shift = e_prop(e, "shiftKey"); + sel.shift = e.shiftKey; if (eventInWidget(display, e)) { if (!webkit) { @@ -1886,7 +1886,7 @@ window.CodeMirror = (function() { if (!name) return false; var keymaps = allKeyMaps(cm); - if (e_prop(e, "shiftKey")) { + if (e.shiftKey) { // First try to resolve full name (including 'Shift-'). Failing // that, see if there is a cursor-motion command (starting with // 'go') bound to the keyname without 'Shift-'. @@ -1923,15 +1923,15 @@ window.CodeMirror = (function() { if (!cm.state.focused) onFocus(cm); if (ie && e.keyCode == 27) { e.returnValue = false; } if (cm.options.onKeyEvent && cm.options.onKeyEvent(cm, addStop(e))) return; - var code = e_prop(e, "keyCode"); + var code = e.keyCode; // IE does strange things with escape. - cm.doc.sel.shift = code == 16 || e_prop(e, "shiftKey"); + cm.doc.sel.shift = code == 16 || e.shiftKey; // First give onKeyEvent option a chance to handle this. var handled = handleKeyBinding(cm, e); if (opera) { lastStoppedKey = handled ? code : null; // Opera has no cut event... we try to at least catch the key combo - if (!handled && code == 88 && !hasCopyEvent && e_prop(e, mac ? "metaKey" : "ctrlKey")) + if (!handled && code == 88 && !hasCopyEvent && (mac ? e.metaKey : e.ctrlKey)) cm.replaceSelection(""); } } @@ -1939,7 +1939,7 @@ window.CodeMirror = (function() { function onKeyPress(e) { var cm = this; if (cm.options.onKeyEvent && cm.options.onKeyEvent(cm, addStop(e))) return; - var keyCode = e_prop(e, "keyCode"), charCode = e_prop(e, "charCode"); + var keyCode = e.keyCode, charCode = e.charCode; if (opera && keyCode == lastStoppedKey) {lastStoppedKey = null; e_preventDefault(e); return;} if (((opera && (!e.which || e.which < 10)) || khtml) && handleKeyBinding(cm, e)) return; var ch = String.fromCharCode(charCode == null ? keyCode : charCode); @@ -3219,16 +3219,16 @@ window.CodeMirror = (function() { } } function isModifierKey(event) { - var name = keyNames[e_prop(event, "keyCode")]; + var name = keyNames[event.keyCode]; return name == "Ctrl" || name == "Alt" || name == "Shift" || name == "Mod"; } function keyName(event, noShift) { - var name = keyNames[e_prop(event, "keyCode")]; + var name = keyNames[event.keyCode]; if (name == null || event.altGraphKey) return false; - if (e_prop(event, "altKey")) name = "Alt-" + name; - if (e_prop(event, flipCtrlCmd ? "metaKey" : "ctrlKey")) name = "Ctrl-" + name; - if (e_prop(event, flipCtrlCmd ? "ctrlKey" : "metaKey")) name = "Cmd-" + name; - if (!noShift && e_prop(event, "shiftKey")) name = "Shift-" + name; + if (event.altKey) name = "Alt-" + name; + if (flipCtrlCmd ? event.metaKey : event.ctrlKey) name = "Ctrl-" + name; + if (flipCtrlCmd ? event.ctrlKey : event.metaKey) name = "Cmd-" + name; + if (!noShift && event.shiftKey) name = "Shift-" + name; return name; } CodeMirror.lookupKey = lookupKey; @@ -4799,13 +4799,6 @@ window.CodeMirror = (function() { return b; } - // Allow 3rd-party code to override event properties by adding an override - // object to an event object. - function e_prop(e, prop) { - var overridden = e.override && e.override.hasOwnProperty(prop); - return overridden ? e.override[prop] : e[prop]; - } - // EVENT HANDLING function on(emitter, type, f) { From fa7d0e90eb6dd770a39535e63803397b66b9bd43 Mon Sep 17 00:00:00 2001 From: Marijn Haverbeke Date: Fri, 15 Feb 2013 21:24:15 +0100 Subject: [PATCH 0734/5780] Fix typo in beforeChange event implementation --- lib/codemirror.js | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/lib/codemirror.js b/lib/codemirror.js index 731920c614..a0f900a7b9 100644 --- a/lib/codemirror.js +++ b/lib/codemirror.js @@ -2083,7 +2083,7 @@ window.CodeMirror = (function() { origin: change.origin, update: function(from, to, text, origin) { if (from) this.from = clipPos(doc, from); - if (to) this.to = clipPos(doc.to); + if (to) this.to = clipPos(doc, to); if (text) this.text = text; if (origin !== undefined) this.origin = origin; }, From b76d6989db6adb72370f2fafbab8f1742d684bac Mon Sep 17 00:00:00 2001 From: Marijn Haverbeke Date: Mon, 18 Feb 2013 09:06:50 +0100 Subject: [PATCH 0735/5780] Fix ignorance about doc.first in coordsChar Closes #1254 --- lib/codemirror.js | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/lib/codemirror.js b/lib/codemirror.js index a0f900a7b9..fbfefde622 100644 --- a/lib/codemirror.js +++ b/lib/codemirror.js @@ -1116,7 +1116,7 @@ window.CodeMirror = (function() { if (y < 0) return PosMaybeOutside(doc.first, 0, true); var lineNo = lineAtHeight(doc, y), last = doc.first + doc.size - 1; if (lineNo > last) - return PosMaybeOutside(doc.size - 1, getLine(doc, last).text.length, true); + return PosMaybeOutside(doc.first + doc.size - 1, getLine(doc, last).text.length, true); if (x < 0) x = 0; for (;;) { From e12fbcbda5026a2e42d63b1076698c422bd8775c Mon Sep 17 00:00:00 2001 From: Charles Skelton Date: Mon, 18 Feb 2013 09:13:30 +0100 Subject: [PATCH 0736/5780] Add a Q mode --- doc/compress.html | 1 + doc/modes.html | 1 + mode/q/index.html | 131 ++++++++++++++++++++++++++++++++++++++++++++++ mode/q/q.js | 124 +++++++++++++++++++++++++++++++++++++++++++ 4 files changed, 257 insertions(+) create mode 100644 mode/q/index.html create mode 100644 mode/q/q.js diff --git a/doc/compress.html b/doc/compress.html index b06062abf0..0f5926fe60 100644 --- a/doc/compress.html +++ b/doc/compress.html @@ -100,6 +100,7 @@

    { } CodeMi + diff --git a/doc/modes.html b/doc/modes.html index 8132770cfa..7d273cbb75 100644 --- a/doc/modes.html +++ b/doc/modes.html @@ -55,6 +55,7 @@

    { } CodeMi
  • Pig Latin
  • Properties files
  • Python
  • +
  • Q
  • R
  • RPM spec and changelog
  • reStructuredText
  • diff --git a/mode/q/index.html b/mode/q/index.html new file mode 100644 index 0000000000..303ec1d3ad --- /dev/null +++ b/mode/q/index.html @@ -0,0 +1,131 @@ + + + + + CodeMirror: Q mode + + + + + + + + +

    CodeMirror: Q mode

    + +
    + + + +

    MIME type defined: text/x-q.

    + + diff --git a/mode/q/q.js b/mode/q/q.js new file mode 100644 index 0000000000..8f339bc3c6 --- /dev/null +++ b/mode/q/q.js @@ -0,0 +1,124 @@ +CodeMirror.defineMode("q",function(config){ + var indentUnit=config.indentUnit, + curPunc, + keywords=buildRE(["abs","acos","aj","aj0","all","and","any","asc","asin","asof","atan","attr","avg","avgs","bin","by","ceiling","cols","cor","cos","count","cov","cross","csv","cut","delete","deltas","desc","dev","differ","distinct","div","do","each","ej","enlist","eval","except","exec","exit","exp","fby","fills","first","fkeys","flip","floor","from","get","getenv","group","gtime","hclose","hcount","hdel","hopen","hsym","iasc","idesc","if","ij","in","insert","inter","inv","key","keys","last","like","list","lj","load","log","lower","lsq","ltime","ltrim","mavg","max","maxs","mcount","md5","mdev","med","meta","min","mins","mmax","mmin","mmu","mod","msum","neg","next","not","null","or","over","parse","peach","pj","plist","prd","prds","prev","prior","rand","rank","ratios","raze","read0","read1","reciprocal","reverse","rload","rotate","rsave","rtrim","save","scan","select","set","setenv","show","signum","sin","sqrt","ss","ssr","string","sublist","sum","sums","sv","system","tables","tan","til","trim","txf","type","uj","ungroup","union","update","upper","upsert","value","var","view","views","vs","wavg","where","where","while","within","wj","wj1","wsum","xasc","xbar","xcol","xcols","xdesc","xexp","xgroup","xkey","xlog","xprev","xrank"]), + E=/[|/&^!+:\\\-*%$=~#;@><,?_\'\"\[\(\]\)\s{}]/; + function buildRE(w){return new RegExp("^("+w.join("|")+")$");} + function tokenBase(stream,state){ + var sol=stream.sol(),c=stream.next(); + curPunc=null; + if(sol) + if(c=="/") + return(state.tokenize=tokenLineComment)(stream,state); + else if(c=="\\"){ + if(stream.eol()||/\s/.test(stream.peek())) + return stream.skipToEnd(),/^\\\s*$/.test(stream.current())?(state.tokenize=tokenCommentToEOF)(stream, state):state.tokenize=tokenBase,"comment"; + else + return state.tokenize=tokenBase,"builtin"; + } + if(/\s/.test(c)) + return stream.peek()=="/"?(stream.skipToEnd(),"comment"):"whitespace"; + if(c=='"') + return(state.tokenize=tokenString)(stream,state); + if(c=='`') + return stream.eatWhile(/[A-Z|a-z|\d|_|:|\/|\.]/),"symbol"; + if(("."==c&&/\d/.test(stream.peek()))||/\d/.test(c)){ + var t=null; + stream.backUp(1); + if(stream.match(/^\d{4}\.\d{2}(m|\.\d{2}([D|T](\d{2}(:\d{2}(:\d{2}(\.\d{1,9})?)?)?)?)?)/) + || stream.match(/^\d+D(\d{2}(:\d{2}(:\d{2}(\.\d{1,9})?)?)?)/) + || stream.match(/^\d{2}:\d{2}(:\d{2}(\.\d{1,9})?)?/) + || stream.match(/^\d+[ptuv]{1}/)) + t="temporal"; + else if(stream.match(/^0[NwW]{1}/) + || stream.match(/^0x[\d|a-f|A-F]*/) + || stream.match(/^[0|1]+[b]{1}/) + || stream.match(/^\d+[chijn]{1}/) + || stream.match(/-?\d*(\.\d*)?(e[+\-]?\d+)?(e|f)?/)) + t="number"; + return(t&&(!(c=stream.peek())||E.test(c)))?t:(stream.next(),"error"); + } + if(/[A-Z|a-z]|\./.test(c)) + return stream.eatWhile(/[A-Z|a-z|\.|_|\d]/),keywords.test(stream.current())?"keyword":"variable"; + if(/[|/&^!+:\\\-*%$=~#;@><\.,?_\']/.test(c)) + return null; + if(/[{}\(\[\]\)]/.test(c)) + return null; + return"error"; + } + function tokenLineComment(stream,state){ + return stream.skipToEnd(),/\/\s*$/.test(stream.current())?(state.tokenize=tokenBlockComment)(stream,state):(state.tokenize=tokenBase),"comment"; + } + function tokenBlockComment(stream,state){ + var f=stream.sol()&&stream.peek()=="\\"; + stream.skipToEnd(); + if(f&&/^\\\s*$/.test(stream.current())) + state.tokenize=tokenBase; + return"comment"; + } + function tokenCommentToEOF(stream,state){return stream.skipToEnd(),"comment";} + function tokenString(stream,state){ + var escaped=false,next,end=false; + while((next=stream.next())){ + if(next=="\""&&!escaped){end=true;break;} + escaped=!escaped&&next=="\\"; + } + if(end)state.tokenize=tokenBase; + return"string"; + } + function pushContext(state,type,col){state.context={prev:state.context,indent:state.indent,col:col,type:type};} + function popContext(state){state.indent=state.context.indent;state.context=state.context.prev;} + return{ + startState:function(base){ + return{tokenize:tokenBase, + context:null, + indent:0, + col:0}; + }, + token:function(stream,state){ + if(stream.sol()){ + if(state.context&&state.context.align==null) + state.context.align=false; + state.indent=stream.indentation(); + } + //if (stream.eatSpace()) return null; + var style=state.tokenize(stream,state); + if(style!="comment"&&state.context&&state.context.align==null&&state.context.type!="pattern"){ + state.context.align=true; + } + if(curPunc=="(")pushContext(state,")",stream.column()); + else if(curPunc=="[")pushContext(state,"]",stream.column()); + else if(curPunc=="{")pushContext(state,"}",stream.column()); + else if(/[\]\}\)]/.test(curPunc)){ + while(state.context&&state.context.type=="pattern")popContext(state); + if(state.context&&curPunc==state.context.type)popContext(state); + } + else if(curPunc=="."&&state.context&&state.context.type=="pattern")popContext(state); + else if(/atom|string|variable/.test(style)&&state.context){ + if(/[\}\]]/.test(state.context.type)) + pushContext(state,"pattern",stream.column()); + else if(state.context.type=="pattern"&&!state.context.align){ + state.context.align=true; + state.context.col=stream.column(); + } + } + return style; + }, + indent:function(state,textAfter){ + var firstChar=textAfter&&textAfter.charAt(0); + var context=state.context; + if(/[\]\}]/.test(firstChar)) + while (context&&context.type=="pattern")context=context.prev; + var closing=context&&firstChar==context.type; + if(!context) + return 0; + else if(context.type=="pattern") + return context.col; + else if(context.align) + return context.col+(closing?0:1); + else + return context.indent+(closing?0:indentUnit); + } + }; +}); +CodeMirror.defineMIME("text/x-q","q"); From 853093b7743f3435bf3dd18dab276114bb981682 Mon Sep 17 00:00:00 2001 From: Marijn Haverbeke Date: Mon, 18 Feb 2013 09:33:33 +0100 Subject: [PATCH 0737/5780] Fix bug in document iteration over subviews Closes #1252 --- lib/codemirror.js | 2 +- test/doc_test.js | 7 +++++++ 2 files changed, 8 insertions(+), 1 deletion(-) diff --git a/lib/codemirror.js b/lib/codemirror.js index fbfefde622..60d1da2d8a 100644 --- a/lib/codemirror.js +++ b/lib/codemirror.js @@ -4283,7 +4283,7 @@ window.CodeMirror = (function() { Doc.prototype = createObj(BranchChunk.prototype, { iter: function(from, to, op) { - if (op) this.iterN(from - this.first, to - (from - this.first), op); + if (op) this.iterN(from - this.first, to - from, op); else this.iterN(this.first, this.first + this.size, from); }, diff --git a/test/doc_test.js b/test/doc_test.js index bebdfb091f..3e04e155b3 100644 --- a/test/doc_test.js +++ b/test/doc_test.js @@ -319,4 +319,11 @@ eq(mark.find(), null); eq(cleared, 1); }); + + testDoc("undoInSubview", "A='line 0\nline 1\nline 2\nline 3\nline 4' B Date: Mon, 18 Feb 2013 10:18:01 +0100 Subject: [PATCH 0738/5780] Reduce intact ranges, rather than extending change ranges, for collapsed spans This way, we don't have to shuffle coordinate systems. Closes #1255 --- lib/codemirror.js | 31 +++++++++++++++++-------------- 1 file changed, 17 insertions(+), 14 deletions(-) diff --git a/lib/codemirror.js b/lib/codemirror.js index 60d1da2d8a..fe2cdd605a 100644 --- a/lib/codemirror.js +++ b/lib/codemirror.js @@ -430,18 +430,6 @@ window.CodeMirror = (function() { var gutterW = display.sizer.style.marginLeft = display.gutters.offsetWidth + "px"; display.scrollbarH.style.left = cm.options.fixedGutter ? gutterW : "0"; - // When merged lines are present, the line that needs to be - // redrawn might not be the one that was changed. - if (sawCollapsedSpans) - for (var i = 0; i < changes.length; ++i) { - var ch = changes[i], merged; - while (merged = collapsedSpanAtStart(getLine(doc, ch.from))) { - var from = merged.find().from.line; - if (ch.diff) ch.diff -= ch.from - from; - ch.from = from; - } - } - // Used to determine which lines need their line numbers updated var positionsChangedFrom = Infinity; if (cm.options.lineNumbers) @@ -460,7 +448,22 @@ window.CodeMirror = (function() { // Create a range of theoretically intact lines, and punch holes // in that using the change info. - var intact = computeIntact([{from: display.showingFrom, to: display.showingTo}], changes); + var intact = [{from: Math.max(display.showingFrom, doc.first), + to: Math.min(display.showingTo, end)}]; + if (intact[0].from >= intact[0].to) intact = []; + else intact = computeIntact(intact, changes); + // When merged lines are present, we might have to reduce the + // intact ranges because changes in continued fragments of the + // intact lines do require the lines to be redrawn. + if (sawCollapsedSpans) + for (var i = 0; i < intact.length; ++i) { + var range = intact[i], merged; + while (merged = collapsedSpanAtEnd(getLine(doc, range.to - 1))) { + var newTo = merged.find().from.line; + if (newTo > range.from) range.to = newTo; + else { intact.splice(i--, 1); break; } + } + } // Clip off the parts that won't be visible var intactLines = 0; @@ -512,7 +515,7 @@ window.CodeMirror = (function() { } updateViewOffset(cm); - if (visibleLines(display, doc, viewPort).to >= to) + if (visibleLines(display, doc, viewPort).to > to) updateDisplayInner(cm, [], viewPort); return true; } From f2e3f3c525c30f92200ee86ccee9e0a6e8095fe0 Mon Sep 17 00:00:00 2001 From: Marijn Haverbeke Date: Mon, 18 Feb 2013 10:27:02 +0100 Subject: [PATCH 0739/5780] [q mode] Remove unused variables --- mode/q/q.js | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/mode/q/q.js b/mode/q/q.js index 8f339bc3c6..6fc4e65a9c 100644 --- a/mode/q/q.js +++ b/mode/q/q.js @@ -56,7 +56,7 @@ CodeMirror.defineMode("q",function(config){ state.tokenize=tokenBase; return"comment"; } - function tokenCommentToEOF(stream,state){return stream.skipToEnd(),"comment";} + function tokenCommentToEOF(stream){return stream.skipToEnd(),"comment";} function tokenString(stream,state){ var escaped=false,next,end=false; while((next=stream.next())){ @@ -69,7 +69,7 @@ CodeMirror.defineMode("q",function(config){ function pushContext(state,type,col){state.context={prev:state.context,indent:state.indent,col:col,type:type};} function popContext(state){state.indent=state.context.indent;state.context=state.context.prev;} return{ - startState:function(base){ + startState:function(){ return{tokenize:tokenBase, context:null, indent:0, From 8a2d4668bfbcc1f00495cc1bb369d550686dcfc6 Mon Sep 17 00:00:00 2001 From: Narciso Jaramillo Date: Mon, 11 Feb 2013 17:30:37 -0800 Subject: [PATCH 0740/5780] Avoid scrolling to cursor position in a non-visible editor --- lib/codemirror.js | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/lib/codemirror.js b/lib/codemirror.js index fe2cdd605a..6a8ab07cfd 100644 --- a/lib/codemirror.js +++ b/lib/codemirror.js @@ -1244,7 +1244,7 @@ window.CodeMirror = (function() { var newScrollPos, updated; if (op.updateScrollPos) { newScrollPos = op.updateScrollPos; - } else if (op.selectionChanged) { + } else if (op.selectionChanged && display.scroller.clientHeight) { // don't rescroll if not visible var coords = cursorCoords(cm, doc.sel.head); newScrollPos = calculateScrollPos(cm, coords.left, coords.top, coords.left, coords.bottom); } From 1605b5557b37d5b1ef38df313904045f61af37b9 Mon Sep 17 00:00:00 2001 From: Marijn Haverbeke Date: Mon, 18 Feb 2013 10:39:54 +0100 Subject: [PATCH 0741/5780] Demote formatting.js to non-supported module status --- demo/formatting.html | 7 +++++++ doc/manual.html | 5 ----- index.html | 1 - 3 files changed, 7 insertions(+), 6 deletions(-) diff --git a/demo/formatting.html b/demo/formatting.html index e2dec35548..6a05794b20 100644 --- a/demo/formatting.html +++ b/demo/formatting.html @@ -24,6 +24,13 @@

    CodeMirror: Formatting demo

    +

    Note: The formatting addon receives a fair + amount of bug requests. I (the maintainer of CodeMirror) do not + intend to spend time on improving it. Pull requests (if clean and + intelligent) are welcome, but you should see this code as a + proof-of-concept (using CodeMirror's mode tokenizers to help + format code), not a finished, robust module.

    +
    + + +

    This demo runs JSHint over the code +in the editor and use CodeMirror Lint to display the warnings that JSHint comes up with.

    + + diff --git a/demo/jsonlint.html b/demo/jsonlint.html new file mode 100644 index 0000000000..39e90b266a --- /dev/null +++ b/demo/jsonlint.html @@ -0,0 +1,75 @@ + + + + + CodeMirror: JSON Lint Demo + + + + + + + + + + + + + + + +

    CodeMirror: JSON Lint Demo

    + + + + +

    This demo runs JSONLint over the code +in the editor and use CodeMirror Lint to display the warnings that JSONLint comes up with.

    + + diff --git a/lib/codemirror-lint.css b/lib/codemirror-lint.css new file mode 100644 index 0000000000..c251c973ed --- /dev/null +++ b/lib/codemirror-lint.css @@ -0,0 +1,85 @@ +.annotationRange { + background-position: left bottom; + background-repeat: repeat-x; +} + +.annotationRange.error { + background-image: + url("data:image/png;base64,iVBORw0KGgoAAAANSUhEUgAAAAQAAAADCAYAAAC09K7GAAAAAXNSR0IArs4c6QAAAAZiS0dEAP8A/wD/oL2nkwAAAAlwSFlzAAALEwAACxMBAJqcGAAAAAd0SU1FB9sJDw4cOCW1/KIAAAAZdEVYdENvbW1lbnQAQ3JlYXRlZCB3aXRoIEdJTVBXgQ4XAAAAHElEQVQI12NggIL/DAz/GdA5/xkY/qPKMDAwAADLZwf5rvm+LQAAAABJRU5ErkJggg==") + ; +} + +.annotationRange.warning { + /* images/squiggly_warning.png */ + background-image: url("data:image/png;base64,iVBORw0KGgoAAAANSUhEUgAAAAQAAAADCAYAAAC09K7GAAAAAXNSR0IArs4c6QAAAAZiS0dEAP8A/wD/oL2nkwAAAAlwSFlzAAALEwAACxMBAJqcGAAAAAd0SU1FB9sJFhQXEbhTg7YAAAAZdEVYdENvbW1lbnQAQ3JlYXRlZCB3aXRoIEdJTVBXgQ4XAAAAMklEQVQI12NkgIIvJ3QXMjAwdDN+OaEbysDA4MPAwNDNwMCwiOHLCd1zX07o6kBVGQEAKBANtobskNMAAAAASUVORK5CYII="); +} + +.annotationHTML { + background-position: center center; + background-repeat: no-repeat; + cursor: pointer; + display: inline-block; + height: 16px; + vertical-align: middle; + width: 16px; +} + +.annotationHTML.error { + background-image: + url("data:image/gif;base64,R0lGODlhEAAQANUAAPVvcvWHiPVucvRuc+ttcfV6f91KVN5LU99PV/FZY/JhaM4oN84pONE4Rd1ATfJLWutVYPRgbdxpcsgWKMgZKs4lNfE/UvE/U+artcpdSc5uXveimslHPuBhW/eJhfV5efaCgO2CgP+/v+PExP///////wAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAACH5BAEAACUALAAAAAAQABAAAAZ+wJJwSCwaScgkySgkjTQZTkYzWhadnE5oE+pwqkSshwQqkzxfa4kkQXxEpA9J9EFI1KQGQQBAigYCBA14ExEWF0gXihETeA0QD3AkD5QQg0NsDnAJmwkOd5gYFSQKpXAFDBhqaxgLBwQBBAapq00YEg0UDRKqTGtKSL7Cw8JBADs=") + ; +} + +.annotationHTML.warning { + /* images/warning.gif */ + background-image: url("data:image/gif;base64,R0lGODlhEAAQANUAAP7bc//egf/ij/7ijv/jl/7kl//mnv7lnv/uwf7CTP7DTf7DT/7IW//Na/7Na//NbP7QdP/dmbltAIJNAF03AMSAJMSCLKqASa2DS6uBSquCSrGHTq6ETbCHT7WKUrKIUcCVXL+UXMOYX8GWXsSZYMiib6+ETbOIUcOXX86uhd3Muf///wAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAACH5BAEAACsALAAAAAAQABAAAAZowJVwSCwaj0ihikRSJYcoBEL0XKlGkcjImQQhJBREKFnyICoThKeE/AAW6AXgdPyUAgrLJBEo0YsbAQyDhAEdRRwDDw8OaA4NDQImRBgFEJdglxAEGEQZKQcHBqOkKRpFF6mqq1WtrUEAOw=="); +} + +/* Styles for the overview ruler */ +.annotationOverview { + cursor: pointer; + border-radius: 2px; + left: 2px; + width: 8px; +} +.annotationOverview.error { + background-color: lightcoral; + border: 1px solid darkred; +} +.annotationOverview.warning { + background-color: Gold; + border: 1px solid black; +} + +.annotationHTML.overlay { + background-image: url("data:image/png;base64,iVBORw0KGgoAAAANSUhEUgAAAAcAAAAHCAYAAADEUlfTAAAAAXNSR0IArs4c6QAAAAZiS0dEAAAAAAAA+UO7fwAAAAlwSFlzAAALEwAACxMBAJqcGAAAAAd0SU1FB9sJEAQvB2JVdrAAAAAdaVRYdENvbW1lbnQAAAAAAENyZWF0ZWQgd2l0aCBHSU1QZC5lBwAAAD1JREFUCNdtjkESADAEAzemf69f66HMqGlOIhYiFRFRtSQBWAY7mzx+EDTL6sSgb1jTk7Q87rxyqe37fXsAa78gLyZnRgEAAAAASUVORK5CYII="); + background-position: right bottom; + position: relative; + top: -16px; +} + +.CodeMirror-lints { + width: 16px; +} + +.textviewTooltip { + background-color: infobackground; + border: 1px solid black; + border-radius: 4px 4px 4px 4px; + color: infotext; + font-family: monospace; + font-size: 10pt; + overflow: hidden; + padding: 2px; + position: fixed; + white-space: pre; + z-index: 100; +} + +#ttcont { + //display: block; + //padding: 2px 12px 3px 7px; + margin-left: 5px; +// background: #666; +// color: #fff; +} \ No newline at end of file From 50ab187f4ea611f32d396ada23fc680a26cafb0e Mon Sep 17 00:00:00 2001 From: Marijn Haverbeke Date: Mon, 18 Feb 2013 11:13:52 +0100 Subject: [PATCH 0743/5780] [lint addon] Start integrating - Link to external jsonlint.js (don't want dependencies in repo if I can help it) - Use a single demo file (prevent demo/ from becoming a huge mess) - Make whitespace and newlines conform to other files, fix linter errors --- addon/lint/javascript-lint.js | 267 ++++++----- addon/lint/json-lint.js | 34 +- addon/lint/jsonlint.js | 432 ------------------ addon/lint/lint.css | 81 ++++ addon/lint/lint.js | 565 ++++++++++++------------ demo/jsonlint.html | 75 ---- demo/{javascriptlint.html => lint.html} | 64 ++- doc/compress.html | 3 + lib/codemirror-lint.css | 85 ---- 9 files changed, 563 insertions(+), 1043 deletions(-) delete mode 100644 addon/lint/jsonlint.js create mode 100644 addon/lint/lint.css delete mode 100644 demo/jsonlint.html rename demo/{javascriptlint.html => lint.html} (60%) delete mode 100644 lib/codemirror-lint.css diff --git a/addon/lint/javascript-lint.js b/addon/lint/javascript-lint.js index cc108fa4d7..e3f84b857a 100644 --- a/addon/lint/javascript-lint.js +++ b/addon/lint/javascript-lint.js @@ -1,144 +1,137 @@ (function() { - var localConfig, defaults, defaultIndent, bogus, warnings, errors; - - localConfig = {}; - - defaults = [ "undef", "newcap", "smarttabs" ]; - defaultIndent = 3; - - bogus = [ "Dangerous comment" ]; - - warnings = [ [ "Expected '{'", - "Statement body should be inside '{ }' braces." ] ]; - - errors = [ "Missing semicolon", "Extra comma", "Missing property name", - "Unmatched ", " and instead saw", " is not defined", - "Unclosed string", "Stopping, unable to continue" ]; - - CodeMirror.javascriptValidator = function(contents, collector, cm) { - var positionalAdjustment = 1; - var result = jshint(contents); - var errors = result.errors; - if (errors) { - parseErrors(errors, positionalAdjustment, collector); - } - }; - - function jshint(contents) { - JSHINT(contents/* , localConfig.options, localConfig.options.predef */); - return JSHINT.data(); - } - - function cleanup(error) { - // All problems are warnings by default - fixWith(error, warnings, "warning", true); - fixWith(error, errors, "error"); - - return isBogus(error) ? null : error; - } - - function fixWith(error, fixes, severity, force) { - var description, fix, find, replace, found; - - description = error.description; - - for ( var i = 0; i < fixes.length; i++) { - fix = fixes[i]; - find = (typeof fix === "string" ? fix : fix[0]); - replace = (typeof fix === "string" ? null : fix[1]); - found = description.indexOf(find) !== -1; - - if (force || found) { - error.severity = severity; - } - if (found && replace) { - error.description = replace; - } - } + var bogus = [ "Dangerous comment" ]; + + var warnings = [ [ "Expected '{'", + "Statement body should be inside '{ }' braces." ] ]; + + var errors = [ "Missing semicolon", "Extra comma", "Missing property name", + "Unmatched ", " and instead saw", " is not defined", + "Unclosed string", "Stopping, unable to continue" ]; + + CodeMirror.javascriptValidator = function(contents, collector) { + var positionalAdjustment = 1; + var result = jshint(contents); + var errors = result.errors; + if (errors) { + parseErrors(errors, positionalAdjustment, collector); + } + }; + + function jshint(contents) { + JSHINT(contents/* , localConfig.options, localConfig.options.predef */); + return JSHINT.data(); + } + + function cleanup(error) { + // All problems are warnings by default + fixWith(error, warnings, "warning", true); + fixWith(error, errors, "error"); + + return isBogus(error) ? null : error; + } + + function fixWith(error, fixes, severity, force) { + var description, fix, find, replace, found; + + description = error.description; + + for ( var i = 0; i < fixes.length; i++) { + fix = fixes[i]; + find = (typeof fix === "string" ? fix : fix[0]); + replace = (typeof fix === "string" ? null : fix[1]); + found = description.indexOf(find) !== -1; + + if (force || found) { + error.severity = severity; + } + if (found && replace) { + error.description = replace; + } + } + } + + function isBogus(error) { + var description = error.description; + for ( var i = 0; i < bogus.length; i++) { + if (description.indexOf(bogus[i]) !== -1) { + return true; + } + } + return false; + } + + function parseErrors(errors, positionalAdjustment, collector) { + for ( var i = 0; i < errors.length; i++) { + var error = errors[i]; + if (error) { + var linetabpositions, index; + + linetabpositions = []; + + // This next block is to fix a problem in jshint. Jshint + // replaces + // all tabs with spaces then performs some checks. The error + // positions (character/space) are then reported incorrectly, + // not taking the replacement step into account. Here we look + // at the evidence line and try to adjust the character position + // to the correct value. + if (error.evidence) { + // Tab positions are computed once per line and cached + var tabpositions = linetabpositions[error.line]; + if (!tabpositions) { + var evidence = error.evidence; + tabpositions = []; + // ugggh phantomjs does not like this + // forEachChar(evidence, function(item, index) { + Array.prototype.forEach.call(evidence, function(item, + index) { + if (item === '\t') { + // First col is 1 (not 0) to match error + // positions + tabpositions.push(index + 1); + } + }); + linetabpositions[error.line] = tabpositions; + } + if (tabpositions.length > 0) { + var pos = error.character; + tabpositions.forEach(function(tabposition) { + if (pos > tabposition) { + pos -= positionalAdjustment; + } + }); + error.character = pos; + } } - function isBogus(error) { - var description = error.description; - for ( var i = 0; i < bogus.length; i++) { - if (description.indexOf(bogus[i]) !== -1) { - return true; - } - } - return false; + var start = error.character - 1, end = start + 1; + if (error.evidence) { + index = error.evidence.substring(start).search(/.\b/); + if (index > -1) { + end += index; + } } - function parseErrors(errors, positionalAdjustment, collector) { - for ( var i = 0; i < errors.length; i++) { - error = errors[i]; - if (error) { - var linetabpositions, index; - - linetabpositions = []; - - // This next block is to fix a problem in jshint. Jshint - // replaces - // all tabs with spaces then performs some checks. The error - // positions (character/space) are then reported incorrectly, - // not taking the replacement step into account. Here we look - // at the evidence line and try to adjust the character position - // to the correct value. - if (error.evidence) { - // Tab positions are computed once per line and cached - var tabpositions = linetabpositions[error.line]; - if (!tabpositions) { - var evidence = error.evidence; - tabpositions = []; - // ugggh phantomjs does not like this - // forEachChar(evidence, function(item, index) { - Array.prototype.forEach.call(evidence, function(item, - index) { - if (item === '\t') { - // First col is 1 (not 0) to match error - // positions - tabpositions.push(index + 1); - } - }); - linetabpositions[error.line] = tabpositions; - } - if (tabpositions.length > 0) { - var pos = error.character; - tabpositions.forEach(function(tabposition) { - if (pos > tabposition) { - pos -= positionalAdjustment; - } - }); - error.character = pos; - } - } - - var start = error.character - 1, end = start + 1; - if (error.evidence) { - index = error.evidence.substring(start).search(/.\b/); - if (index > -1) { - end += index; - } - } - - // Convert to format expected by validation service - error.description = error.reason;// + "(jshint)"; - error.start = error.character; - error.end = end; - error = cleanup(error); - - if (error) { - var severity = error.severity; - var lineStart = error.line - 1; - var charStart = start; - var lineEnd = error.line - 1; - var charEnd = end; - var description = error.description; - - collector.addAnnotation(severity, lineStart, charStart, - lineEnd, charEnd, description); - - } - } - } + // Convert to format expected by validation service + error.description = error.reason;// + "(jshint)"; + error.start = error.character; + error.end = end; + error = cleanup(error); + + if (error) { + var severity = error.severity; + var lineStart = error.line - 1; + var charStart = start; + var lineEnd = error.line - 1; + var charEnd = end; + var description = error.description; + + collector.addAnnotation(severity, lineStart, charStart, + lineEnd, charEnd, description); + } -})(); \ No newline at end of file + } + } + } +})(); diff --git a/addon/lint/json-lint.js b/addon/lint/json-lint.js index 2aebcdc7b9..d3c4c756ee 100644 --- a/addon/lint/json-lint.js +++ b/addon/lint/json-lint.js @@ -1,24 +1,20 @@ (function() { - CodeMirror.jsonValidator = function(contents, collector, cm) { - try { - jsonlint.parseError = function(str, hash) { + CodeMirror.jsonValidator = function(contents, collector) { + jsonlint.parseError = function(str, hash) { + var severity = 'error'; + var loc = hash.loc; + var lineStart = loc.first_line - 1; + var lineEnd = hash.line;// loc.last_line - 1; + var charStart = loc.first_column; + var charEnd = loc.last_column; - var severity = 'error'; - var loc = hash.loc; - var lineStart = loc.first_line - 1; - var lineEnd = hash.line;// loc.last_line - 1; - var charStart = loc.first_column - var charEnd = loc.last_column; + collector.addAnnotation(severity, lineStart, charStart, + lineEnd, charEnd, str); - collector.addAnnotation(severity, lineStart, charStart, - lineEnd, charEnd, str); + }; + try { jsonlint.parse(contents); } + catch(e) {} + }; - }; - jsonlint.parse(contents); - } catch (parseException) { - //alert(parseException); - } - }; - -})(); \ No newline at end of file +})(); diff --git a/addon/lint/jsonlint.js b/addon/lint/jsonlint.js deleted file mode 100644 index d72145473c..0000000000 --- a/addon/lint/jsonlint.js +++ /dev/null @@ -1,432 +0,0 @@ -/* Jison generated parser */ -var jsonlint = (function(){ -var parser = {trace: function trace() { }, -yy: {}, -symbols_: {"error":2,"JSONString":3,"STRING":4,"JSONNumber":5,"NUMBER":6,"JSONNullLiteral":7,"NULL":8,"JSONBooleanLiteral":9,"TRUE":10,"FALSE":11,"JSONText":12,"JSONValue":13,"EOF":14,"JSONObject":15,"JSONArray":16,"{":17,"}":18,"JSONMemberList":19,"JSONMember":20,":":21,",":22,"[":23,"]":24,"JSONElementList":25,"$accept":0,"$end":1}, -terminals_: {2:"error",4:"STRING",6:"NUMBER",8:"NULL",10:"TRUE",11:"FALSE",14:"EOF",17:"{",18:"}",21:":",22:",",23:"[",24:"]"}, -productions_: [0,[3,1],[5,1],[7,1],[9,1],[9,1],[12,2],[13,1],[13,1],[13,1],[13,1],[13,1],[13,1],[15,2],[15,3],[20,3],[19,1],[19,3],[16,2],[16,3],[25,1],[25,3]], -performAction: function anonymous(yytext,yyleng,yylineno,yy,yystate,$$,_$) { - -var $0 = $$.length - 1; -switch (yystate) { -case 1: // replace escaped characters with actual character - this.$ = yytext.replace(/\\(\\|")/g, "$"+"1") - .replace(/\\n/g,'\n') - .replace(/\\r/g,'\r') - .replace(/\\t/g,'\t') - .replace(/\\v/g,'\v') - .replace(/\\f/g,'\f') - .replace(/\\b/g,'\b'); - -break; -case 2:this.$ = Number(yytext); -break; -case 3:this.$ = null; -break; -case 4:this.$ = true; -break; -case 5:this.$ = false; -break; -case 6:return this.$ = $$[$0-1]; -break; -case 13:this.$ = {}; -break; -case 14:this.$ = $$[$0-1]; -break; -case 15:this.$ = [$$[$0-2], $$[$0]]; -break; -case 16:this.$ = {}; this.$[$$[$0][0]] = $$[$0][1]; -break; -case 17:this.$ = $$[$0-2]; $$[$0-2][$$[$0][0]] = $$[$0][1]; -break; -case 18:this.$ = []; -break; -case 19:this.$ = $$[$0-1]; -break; -case 20:this.$ = [$$[$0]]; -break; -case 21:this.$ = $$[$0-2]; $$[$0-2].push($$[$0]); -break; -} -}, -table: [{3:5,4:[1,12],5:6,6:[1,13],7:3,8:[1,9],9:4,10:[1,10],11:[1,11],12:1,13:2,15:7,16:8,17:[1,14],23:[1,15]},{1:[3]},{14:[1,16]},{14:[2,7],18:[2,7],22:[2,7],24:[2,7]},{14:[2,8],18:[2,8],22:[2,8],24:[2,8]},{14:[2,9],18:[2,9],22:[2,9],24:[2,9]},{14:[2,10],18:[2,10],22:[2,10],24:[2,10]},{14:[2,11],18:[2,11],22:[2,11],24:[2,11]},{14:[2,12],18:[2,12],22:[2,12],24:[2,12]},{14:[2,3],18:[2,3],22:[2,3],24:[2,3]},{14:[2,4],18:[2,4],22:[2,4],24:[2,4]},{14:[2,5],18:[2,5],22:[2,5],24:[2,5]},{14:[2,1],18:[2,1],21:[2,1],22:[2,1],24:[2,1]},{14:[2,2],18:[2,2],22:[2,2],24:[2,2]},{3:20,4:[1,12],18:[1,17],19:18,20:19},{3:5,4:[1,12],5:6,6:[1,13],7:3,8:[1,9],9:4,10:[1,10],11:[1,11],13:23,15:7,16:8,17:[1,14],23:[1,15],24:[1,21],25:22},{1:[2,6]},{14:[2,13],18:[2,13],22:[2,13],24:[2,13]},{18:[1,24],22:[1,25]},{18:[2,16],22:[2,16]},{21:[1,26]},{14:[2,18],18:[2,18],22:[2,18],24:[2,18]},{22:[1,28],24:[1,27]},{22:[2,20],24:[2,20]},{14:[2,14],18:[2,14],22:[2,14],24:[2,14]},{3:20,4:[1,12],20:29},{3:5,4:[1,12],5:6,6:[1,13],7:3,8:[1,9],9:4,10:[1,10],11:[1,11],13:30,15:7,16:8,17:[1,14],23:[1,15]},{14:[2,19],18:[2,19],22:[2,19],24:[2,19]},{3:5,4:[1,12],5:6,6:[1,13],7:3,8:[1,9],9:4,10:[1,10],11:[1,11],13:31,15:7,16:8,17:[1,14],23:[1,15]},{18:[2,17],22:[2,17]},{18:[2,15],22:[2,15]},{22:[2,21],24:[2,21]}], -defaultActions: {16:[2,6]}, -parseError: function parseError(str, hash) { - throw new Error(str); -}, -parse: function parse(input) { - var self = this, - stack = [0], - vstack = [null], // semantic value stack - lstack = [], // location stack - table = this.table, - yytext = '', - yylineno = 0, - yyleng = 0, - recovering = 0, - TERROR = 2, - EOF = 1; - - //this.reductionCount = this.shiftCount = 0; - - this.lexer.setInput(input); - this.lexer.yy = this.yy; - this.yy.lexer = this.lexer; - if (typeof this.lexer.yylloc == 'undefined') - this.lexer.yylloc = {}; - var yyloc = this.lexer.yylloc; - lstack.push(yyloc); - - if (typeof this.yy.parseError === 'function') - this.parseError = this.yy.parseError; - - function popStack (n) { - stack.length = stack.length - 2*n; - vstack.length = vstack.length - n; - lstack.length = lstack.length - n; - } - - function lex() { - var token; - token = self.lexer.lex() || 1; // $end = 1 - // if token isn't its numeric value, convert - if (typeof token !== 'number') { - token = self.symbols_[token] || token; - } - return token; - } - - var symbol, preErrorSymbol, state, action, a, r, yyval={},p,len,newState, expected; - while (true) { - // retreive state number from top of stack - state = stack[stack.length-1]; - - // use default actions if available - if (this.defaultActions[state]) { - action = this.defaultActions[state]; - } else { - if (symbol == null) - symbol = lex(); - // read action for current state and first input - action = table[state] && table[state][symbol]; - } - - // handle parse error - _handle_error: - if (typeof action === 'undefined' || !action.length || !action[0]) { - - if (!recovering) { - // Report error - expected = []; - for (p in table[state]) if (this.terminals_[p] && p > 2) { - expected.push("'"+this.terminals_[p]+"'"); - } - var errStr = ''; - if (this.lexer.showPosition) { - errStr = 'Parse error on line '+(yylineno+1)+":\n"+this.lexer.showPosition()+"\nExpecting "+expected.join(', ') + ", got '" + this.terminals_[symbol]+ "'"; - } else { - errStr = 'Parse error on line '+(yylineno+1)+": Unexpected " + - (symbol == 1 /*EOF*/ ? "end of input" : - ("'"+(this.terminals_[symbol] || symbol)+"'")); - } - this.parseError(errStr, - {text: this.lexer.match, token: this.terminals_[symbol] || symbol, line: this.lexer.yylineno, loc: yyloc, expected: expected}); - } - - // just recovered from another error - if (recovering == 3) { - if (symbol == EOF) { - throw new Error(errStr || 'Parsing halted.'); - } - - // discard current lookahead and grab another - yyleng = this.lexer.yyleng; - yytext = this.lexer.yytext; - yylineno = this.lexer.yylineno; - yyloc = this.lexer.yylloc; - symbol = lex(); - } - - // try to recover from error - while (1) { - // check for error recovery rule in this state - if ((TERROR.toString()) in table[state]) { - break; - } - if (state == 0) { - throw new Error(errStr || 'Parsing halted.'); - } - popStack(1); - state = stack[stack.length-1]; - } - - preErrorSymbol = symbol; // save the lookahead token - symbol = TERROR; // insert generic error symbol as new lookahead - state = stack[stack.length-1]; - action = table[state] && table[state][TERROR]; - recovering = 3; // allow 3 real symbols to be shifted before reporting a new error - } - - // this shouldn't happen, unless resolve defaults are off - if (action[0] instanceof Array && action.length > 1) { - throw new Error('Parse Error: multiple actions possible at state: '+state+', token: '+symbol); - } - - switch (action[0]) { - - case 1: // shift - //this.shiftCount++; - - stack.push(symbol); - vstack.push(this.lexer.yytext); - lstack.push(this.lexer.yylloc); - stack.push(action[1]); // push state - symbol = null; - if (!preErrorSymbol) { // normal execution/no error - yyleng = this.lexer.yyleng; - yytext = this.lexer.yytext; - yylineno = this.lexer.yylineno; - yyloc = this.lexer.yylloc; - if (recovering > 0) - recovering--; - } else { // error just occurred, resume old lookahead f/ before error - symbol = preErrorSymbol; - preErrorSymbol = null; - } - break; - - case 2: // reduce - //this.reductionCount++; - - len = this.productions_[action[1]][1]; - - // perform semantic action - yyval.$ = vstack[vstack.length-len]; // default to $$ = $1 - // default location, uses first token for firsts, last for lasts - yyval._$ = { - first_line: lstack[lstack.length-(len||1)].first_line, - last_line: lstack[lstack.length-1].last_line, - first_column: lstack[lstack.length-(len||1)].first_column, - last_column: lstack[lstack.length-1].last_column - }; - r = this.performAction.call(yyval, yytext, yyleng, yylineno, this.yy, action[1], vstack, lstack); - - if (typeof r !== 'undefined') { - return r; - } - - // pop off stack - if (len) { - stack = stack.slice(0,-1*len*2); - vstack = vstack.slice(0, -1*len); - lstack = lstack.slice(0, -1*len); - } - - stack.push(this.productions_[action[1]][0]); // push nonterminal (reduce) - vstack.push(yyval.$); - lstack.push(yyval._$); - // goto new state = table[STATE][NONTERMINAL] - newState = table[stack[stack.length-2]][stack[stack.length-1]]; - stack.push(newState); - break; - - case 3: // accept - return true; - } - - } - - return true; -}}; -/* Jison generated lexer */ -var lexer = (function(){ -var lexer = ({EOF:1, -parseError:function parseError(str, hash) { - if (this.yy.parseError) { - this.yy.parseError(str, hash); - } else { - throw new Error(str); - } - }, -setInput:function (input) { - this._input = input; - this._more = this._less = this.done = false; - this.yylineno = this.yyleng = 0; - this.yytext = this.matched = this.match = ''; - this.conditionStack = ['INITIAL']; - this.yylloc = {first_line:1,first_column:0,last_line:1,last_column:0}; - return this; - }, -input:function () { - var ch = this._input[0]; - this.yytext+=ch; - this.yyleng++; - this.match+=ch; - this.matched+=ch; - var lines = ch.match(/\n/); - if (lines) this.yylineno++; - this._input = this._input.slice(1); - return ch; - }, -unput:function (ch) { - this._input = ch + this._input; - return this; - }, -more:function () { - this._more = true; - return this; - }, -less:function (n) { - this._input = this.match.slice(n) + this._input; - }, -pastInput:function () { - var past = this.matched.substr(0, this.matched.length - this.match.length); - return (past.length > 20 ? '...':'') + past.substr(-20).replace(/\n/g, ""); - }, -upcomingInput:function () { - var next = this.match; - if (next.length < 20) { - next += this._input.substr(0, 20-next.length); - } - return (next.substr(0,20)+(next.length > 20 ? '...':'')).replace(/\n/g, ""); - }, -showPosition:function () { - var pre = this.pastInput(); - var c = new Array(pre.length + 1).join("-"); - return pre + this.upcomingInput() + "\n" + c+"^"; - }, -next:function () { - if (this.done) { - return this.EOF; - } - if (!this._input) this.done = true; - - var token, - match, - tempMatch, - index, - col, - lines; - if (!this._more) { - this.yytext = ''; - this.match = ''; - } - var rules = this._currentRules(); - for (var i=0;i < rules.length; i++) { - tempMatch = this._input.match(this.rules[rules[i]]); - if (tempMatch && (!match || tempMatch[0].length > match[0].length)) { - match = tempMatch; - index = i; - if (!this.options.flex) break; - } - } - if (match) { - lines = match[0].match(/\n.*/g); - if (lines) this.yylineno += lines.length; - this.yylloc = {first_line: this.yylloc.last_line, - last_line: this.yylineno+1, - first_column: this.yylloc.last_column, - last_column: lines ? lines[lines.length-1].length-1 : this.yylloc.last_column + match[0].length} - this.yytext += match[0]; - this.match += match[0]; - this.yyleng = this.yytext.length; - this._more = false; - this._input = this._input.slice(match[0].length); - this.matched += match[0]; - token = this.performAction.call(this, this.yy, this, rules[index],this.conditionStack[this.conditionStack.length-1]); - if (this.done && this._input) this.done = false; - if (token) return token; - else return; - } - if (this._input === "") { - return this.EOF; - } else { - this.parseError('Lexical error on line '+(this.yylineno+1)+'. Unrecognized text.\n'+this.showPosition(), - {text: "", token: null, line: this.yylineno}); - } - }, -lex:function lex() { - var r = this.next(); - if (typeof r !== 'undefined') { - return r; - } else { - return this.lex(); - } - }, -begin:function begin(condition) { - this.conditionStack.push(condition); - }, -popState:function popState() { - return this.conditionStack.pop(); - }, -_currentRules:function _currentRules() { - return this.conditions[this.conditionStack[this.conditionStack.length-1]].rules; - }, -topState:function () { - return this.conditionStack[this.conditionStack.length-2]; - }, -pushState:function begin(condition) { - this.begin(condition); - }}); -lexer.options = {}; -lexer.performAction = function anonymous(yy,yy_,$avoiding_name_collisions,YY_START) { - -var YYSTATE=YY_START -switch($avoiding_name_collisions) { -case 0:/* skip whitespace */ -break; -case 1:return 6 -break; -case 2:yy_.yytext = yy_.yytext.substr(1,yy_.yyleng-2); return 4 -break; -case 3:return 17 -break; -case 4:return 18 -break; -case 5:return 23 -break; -case 6:return 24 -break; -case 7:return 22 -break; -case 8:return 21 -break; -case 9:return 10 -break; -case 10:return 11 -break; -case 11:return 8 -break; -case 12:return 14 -break; -case 13:return 'INVALID' -break; -} -}; -lexer.rules = [/^(?:\s+)/,/^(?:(-?([0-9]|[1-9][0-9]+))(\.[0-9]+)?([eE][-+]?[0-9]+)?\b)/,/^(?:"(?:\\[\\"bfnrt/]|\\u[a-fA-F0-9]{4}|[^\\\0-\x09\x0a-\x1f"])*")/,/^(?:\{)/,/^(?:\})/,/^(?:\[)/,/^(?:\])/,/^(?:,)/,/^(?::)/,/^(?:true\b)/,/^(?:false\b)/,/^(?:null\b)/,/^(?:$)/,/^(?:.)/]; -lexer.conditions = {"INITIAL":{"rules":[0,1,2,3,4,5,6,7,8,9,10,11,12,13],"inclusive":true}}; - - -; -return lexer;})() -parser.lexer = lexer; -return parser; -})(); -if (typeof require !== 'undefined' && typeof exports !== 'undefined') { -exports.parser = jsonlint; -exports.parse = function () { return jsonlint.parse.apply(jsonlint, arguments); } -exports.main = function commonjsMain(args) { - if (!args[1]) - throw new Error('Usage: '+args[0]+' FILE'); - if (typeof process !== 'undefined') { - var source = require('fs').readFileSync(require('path').join(process.cwd(), args[1]), "utf8"); - } else { - var cwd = require("file").path(require("file").cwd()); - var source = cwd.join(args[1]).read({charset: "utf-8"}); - } - return exports.parser.parse(source); -} -if (typeof module !== 'undefined' && require.main === module) { - exports.main(typeof process !== 'undefined' ? process.argv.slice(1) : require("system").args); -} -} \ No newline at end of file diff --git a/addon/lint/lint.css b/addon/lint/lint.css new file mode 100644 index 0000000000..98e883266f --- /dev/null +++ b/addon/lint/lint.css @@ -0,0 +1,81 @@ +.annotationRange { + background-position: left bottom; + background-repeat: repeat-x; +} + +.annotationRange.error { + background-image: + url("data:image/png;base64,iVBORw0KGgoAAAANSUhEUgAAAAQAAAADCAYAAAC09K7GAAAAAXNSR0IArs4c6QAAAAZiS0dEAP8A/wD/oL2nkwAAAAlwSFlzAAALEwAACxMBAJqcGAAAAAd0SU1FB9sJDw4cOCW1/KIAAAAZdEVYdENvbW1lbnQAQ3JlYXRlZCB3aXRoIEdJTVBXgQ4XAAAAHElEQVQI12NggIL/DAz/GdA5/xkY/qPKMDAwAADLZwf5rvm+LQAAAABJRU5ErkJggg==") + ; +} + +.annotationRange.warning { + /* images/squiggly_warning.png */ + background-image: url("data:image/png;base64,iVBORw0KGgoAAAANSUhEUgAAAAQAAAADCAYAAAC09K7GAAAAAXNSR0IArs4c6QAAAAZiS0dEAP8A/wD/oL2nkwAAAAlwSFlzAAALEwAACxMBAJqcGAAAAAd0SU1FB9sJFhQXEbhTg7YAAAAZdEVYdENvbW1lbnQAQ3JlYXRlZCB3aXRoIEdJTVBXgQ4XAAAAMklEQVQI12NkgIIvJ3QXMjAwdDN+OaEbysDA4MPAwNDNwMCwiOHLCd1zX07o6kBVGQEAKBANtobskNMAAAAASUVORK5CYII="); +} + +.annotationHTML { + background-position: center center; + background-repeat: no-repeat; + cursor: pointer; + display: inline-block; + height: 16px; + vertical-align: middle; + width: 16px; +} + +.annotationHTML.error { + background-image: + url("data:image/gif;base64,R0lGODlhEAAQANUAAPVvcvWHiPVucvRuc+ttcfV6f91KVN5LU99PV/FZY/JhaM4oN84pONE4Rd1ATfJLWutVYPRgbdxpcsgWKMgZKs4lNfE/UvE/U+artcpdSc5uXveimslHPuBhW/eJhfV5efaCgO2CgP+/v+PExP///////wAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAACH5BAEAACUALAAAAAAQABAAAAZ+wJJwSCwaScgkySgkjTQZTkYzWhadnE5oE+pwqkSshwQqkzxfa4kkQXxEpA9J9EFI1KQGQQBAigYCBA14ExEWF0gXihETeA0QD3AkD5QQg0NsDnAJmwkOd5gYFSQKpXAFDBhqaxgLBwQBBAapq00YEg0UDRKqTGtKSL7Cw8JBADs=") + ; +} + +.annotationHTML.warning { + /* images/warning.gif */ + background-image: url("data:image/gif;base64,R0lGODlhEAAQANUAAP7bc//egf/ij/7ijv/jl/7kl//mnv7lnv/uwf7CTP7DTf7DT/7IW//Na/7Na//NbP7QdP/dmbltAIJNAF03AMSAJMSCLKqASa2DS6uBSquCSrGHTq6ETbCHT7WKUrKIUcCVXL+UXMOYX8GWXsSZYMiib6+ETbOIUcOXX86uhd3Muf///wAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAACH5BAEAACsALAAAAAAQABAAAAZowJVwSCwaj0ihikRSJYcoBEL0XKlGkcjImQQhJBREKFnyICoThKeE/AAW6AXgdPyUAgrLJBEo0YsbAQyDhAEdRRwDDw8OaA4NDQImRBgFEJdglxAEGEQZKQcHBqOkKRpFF6mqq1WtrUEAOw=="); +} + +/* Styles for the overview ruler */ +.annotationOverview { + cursor: pointer; + border-radius: 2px; + left: 2px; + width: 8px; +} +.annotationOverview.error { + background-color: lightcoral; + border: 1px solid darkred; +} +.annotationOverview.warning { + background-color: Gold; + border: 1px solid black; +} + +.annotationHTML.overlay { + background-image: url("data:image/png;base64,iVBORw0KGgoAAAANSUhEUgAAAAcAAAAHCAYAAADEUlfTAAAAAXNSR0IArs4c6QAAAAZiS0dEAAAAAAAA+UO7fwAAAAlwSFlzAAALEwAACxMBAJqcGAAAAAd0SU1FB9sJEAQvB2JVdrAAAAAdaVRYdENvbW1lbnQAAAAAAENyZWF0ZWQgd2l0aCBHSU1QZC5lBwAAAD1JREFUCNdtjkESADAEAzemf69f66HMqGlOIhYiFRFRtSQBWAY7mzx+EDTL6sSgb1jTk7Q87rxyqe37fXsAa78gLyZnRgEAAAAASUVORK5CYII="); + background-position: right bottom; + position: relative; + top: -16px; +} + +.CodeMirror-lints { + width: 16px; +} + +.textviewTooltip { + background-color: infobackground; + border: 1px solid black; + border-radius: 4px 4px 4px 4px; + color: infotext; + font-family: monospace; + font-size: 10pt; + overflow: hidden; + padding: 2px; + position: fixed; + white-space: pre; + z-index: 100; +} + +#ttcont { + margin-left: 5px; +} diff --git a/addon/lint/lint.js b/addon/lint/lint.js index 82adebdd36..a2713efcce 100644 --- a/addon/lint/lint.js +++ b/addon/lint/lint.js @@ -1,283 +1,282 @@ -CodeMirror.validate = function(cm, collectAnnotations, options) { - if (!options) - options = {}; - - var GUTTER_ID = "CodeMirror-lints"; - - // see - // http://sixrevisions.com/tutorials/javascript_tutorial/create_lightweight_javascript_tooltip/ - var tooltip = function() { - var id = 'tt'; - var top = 3; - var left = 3; - var maxw = 600; - var speed = 10; - var timer = 20; - var endalpha = 95; - var alpha = 0; - //var tt, t, c, b, h; - var tt, c, h; - var ie = document.all ? true : false; - return { - show : function(v, w, severity) { - if (tt == null) { - tt = document.createElement('div'); - tt.setAttribute('id', id); - tt.className = 'textviewTooltip'; - //t = document.createElement('div'); - //t.setAttribute('id', id + 'top'); - c = document.createElement('div'); - c.setAttribute('id', id + 'cont'); - //b = document.createElement('div'); - //b.setAttribute('id', id + 'bot'); - //tt.appendChild(t); - tt.appendChild(c); - //tt.appendChild(b); - document.body.appendChild(tt); - tt.style.opacity = 0; - tt.style.filter = 'alpha(opacity=0)'; - document.onmousemove = this.pos; - } - tt.style.display = 'block'; - c.innerHTML = v; - tt.style.width = w ? w + 'px' : 'auto'; - if (!w && ie) { - //t.style.display = 'none'; - //b.style.display = 'none'; - tt.style.width = tt.offsetWidth; - //t.style.display = 'block'; - //b.style.display = 'block'; - } - if (tt.offsetWidth > maxw) { - tt.style.width = maxw + 'px' - } - h = parseInt(tt.offsetHeight) + top; - clearInterval(tt.timer); - tt.timer = setInterval(function() { - tooltip.fade(1) - }, timer); - }, - pos : function(e) { - var u = ie ? event.clientY + document.documentElement.scrollTop - : e.pageY; - var l = ie ? event.clientX - + document.documentElement.scrollLeft : e.pageX; - tt.style.top = (u - h) + 'px'; - tt.style.left = (l + left) + 'px'; - }, - fade : function(d) { - var a = alpha; - if ((a != endalpha && d == 1) || (a != 0 && d == -1)) { - var i = speed; - if (endalpha - a < speed && d == 1) { - i = endalpha - a; - } else if (alpha < speed && d == -1) { - i = a; - } - alpha = a + (i * d); - tt.style.opacity = alpha * .01; - tt.style.filter = 'alpha(opacity=' + alpha + ')'; - } else { - clearInterval(tt.timer); - if (d == -1) { - tt.style.display = 'none' - } else if (d == 1) { - tt.style.opacity = 1; - tt.style.filter = 'alpha(opacity=100)'; - } - } - }, - hide : function() { - clearInterval(tt.timer); - tt.timer = setInterval(function() { - tooltip.fade(-1) - }, timer); - } - }; - }(); - - function SyntaxErrorHighlightState() { - this.marked = []; - } - function getSyntaxErrorHighlightState(cm) { - return cm._syntaxErrorHighlightState - || (cm._syntaxErrorHighlightState = new SyntaxErrorHighlightState()); - } - - function clearMarks(cm, gutterID) { - cm.clearGutter(gutterID); - var state = getSyntaxErrorHighlightState(cm); - for ( var i = 0; i < state.marked.length; ++i) - state.marked[i].clear(); - state.marked = []; - } - - function makeMarker(tooltipLabel, maxSeverity, multiple) { - - var marker = document.createElement("div"); - marker.onmouseover = function() { - tooltip.show(tooltipLabel, null, maxSeverity); - }; - marker.onmouseout = function() { - tooltip.hide(); - }; - marker.className = 'annotation ' + maxSeverity; - - var markerHTML= document.createElement("div"); - markerHTML.className = 'annotationHTML ' + maxSeverity; - marker.appendChild(markerHTML); - - if (multiple) { - var markerHTML= document.createElement("div"); - markerHTML.className = 'annotationHTML overlay'; - marker.appendChild(markerHTML); - } - - return marker; - } - - var AnnotationsCollector = function() { - this.linesNumber = []; - this.linesAnnotations = []; - }; - - AnnotationsCollector.prototype.addAnnotation = function(severity, - lineStart, charStart, lineEnd, charEnd, description) { - - var annotation = { - "severity" : severity, - "lineStart" : lineStart, - "charStart" : charStart, - "lineEnd" : lineEnd, - "charEnd" : charEnd, - "description" : description - } - var annotations = this.linesAnnotations[lineStart]; - if (!annotations) { - annotations = []; - this.linesAnnotations[lineStart] = annotations; - this.linesNumber.push(lineStart); - } - annotations.push(annotation); - }; - - function getMaxSeverity(severity1, severity2) { - // TODO : should be improved to extend severity (today just error and - // warning are managed) - if (severity1 == 'error') { - return severity1; - } - return severity2; - } - - function getTooltipLabel(content, multiple) { - var label = '
    '; - if (multiple) { - label += '
    Multiple Annotations
    '; - } - label += '
    '; - label += content; - label += '
    '; - label += '
    '; - return label; - } - - function getTooltipAnnotationLabel(description, severity) { - var label = '
    '; - label += '
    '; - label += '
    '; - label += ' '; - label += description; - label += ''; - label += '
    '; - return label; - } - - function makeMarkText(severity, lineStart, charStart, lineEnd, charEnd, annotationLabel) { - var markClass = 'annotationRange ' + severity; - var mark = cm.markText({ - line : lineStart, - ch : charStart - }, { - line : lineEnd, - ch : charEnd - }, { - className : markClass, - readOnly : false - }); - - // save mark text in an array which is used to clean mark - var state = getSyntaxErrorHighlightState(cm); - state.marked.push(mark); - - var tooltipLabel = getTooltipLabel(annotationLabel, false); - // TODO : add tooltip for mark text. How to manage that??? - // I would like do like this - /*mark.onmouseover = function() { - tooltip.show(tooltipLabel, null, severity); - }; - mark.onmouseout = function() { - tooltip.hide(); - };*/ - } - - function markDocument() { - var gutterID = GUTTER_ID; - clearMarks(cm, gutterID); - - var collector = new AnnotationsCollector(); - - var contents = (options.getContents ? options.getContents() : cm - .getValue()); - collectAnnotations(contents, collector, cm); - - var linesNumber = collector.linesNumber; - // Loop for each lines number which contains problems - for ( var i = 0; i < linesNumber.length; i++) { - var line = linesNumber[i]; - var annotations = collector.linesAnnotations[line]; - - // loop for each annotations for the current line to - // - create a markText for each annotations - // - create a tooltip with the list of annotations - - var maxSeverity = null; - var markerTooltipLabel = ''; - var multiple = annotations.length > 1; - - // create a markText for each annotations - for ( var j = 0; j < annotations.length; j++) { - var annotation = annotations[j]; - if (options.formatAnnotation) { - annotation = options.formatAnnotation(annotation); - } - - // Create mark text - - var severity = annotation.severity; - var lineStart = annotation.lineStart; - var charStart = annotation.charStart; - var lineEnd = annotation.lineEnd; - var charEnd = annotation.charEnd; - var description = annotation.description; - - var annotationLabel = getTooltipAnnotationLabel(description, severity); - makeMarkText(severity, lineStart, charStart, lineEnd, charEnd, annotationLabel); - - markerTooltipLabel+=annotationLabel; - maxSeverity = getMaxSeverity(maxSeverity, severity); - } - markerTooltipLabel = getTooltipLabel(markerTooltipLabel, multiple); - - - // create a tooltip with the list of annotations - var marker = makeMarker(markerTooltipLabel, maxSeverity, multiple); - - var info = cm.lineInfo(line); - cm.setGutterMarker(line, gutterID, info.gutterMarkers ? null - : marker); - } - } - - return markDocument(); -}; +CodeMirror.validate = function(cm, collectAnnotations, options) { + if (!options) + options = {}; + + var GUTTER_ID = "CodeMirror-lints"; + + // see + // http://sixrevisions.com/tutorials/javascript_tutorial/create_lightweight_javascript_tooltip/ + var tooltip = function() { + var id = 'tt'; + var top = 3; + var left = 3; + var maxw = 600; + var speed = 10; + var timer = 20; + var endalpha = 95; + var alpha = 0; + var tt, c, h; + var ie = document.all ? true : false; + return { + show : function(v, w) { + if (tt == null) { + tt = document.createElement('div'); + tt.setAttribute('id', id); + tt.className = 'textviewTooltip'; + //t = document.createElement('div'); + //t.setAttribute('id', id + 'top'); + c = document.createElement('div'); + c.setAttribute('id', id + 'cont'); + //b = document.createElement('div'); + //b.setAttribute('id', id + 'bot'); + //tt.appendChild(t); + tt.appendChild(c); + //tt.appendChild(b); + document.body.appendChild(tt); + tt.style.opacity = 0; + tt.style.filter = 'alpha(opacity=0)'; + document.onmousemove = this.pos; + } + tt.style.display = 'block'; + c.innerHTML = v; + tt.style.width = w ? w + 'px' : 'auto'; + if (!w && ie) { + //t.style.display = 'none'; + //b.style.display = 'none'; + tt.style.width = tt.offsetWidth; + //t.style.display = 'block'; + //b.style.display = 'block'; + } + if (tt.offsetWidth > maxw) { + tt.style.width = maxw + 'px'; + } + h = parseInt(tt.offsetHeight) + top; + clearInterval(tt.timer); + tt.timer = setInterval(function() { + tooltip.fade(1); + }, timer); + }, + pos : function(e) { + var u = ie ? event.clientY + document.documentElement.scrollTop + : e.pageY; + var l = ie ? event.clientX + + document.documentElement.scrollLeft : e.pageX; + tt.style.top = (u - h) + 'px'; + tt.style.left = (l + left) + 'px'; + }, + fade : function(d) { + var a = alpha; + if ((a != endalpha && d == 1) || (a != 0 && d == -1)) { + var i = speed; + if (endalpha - a < speed && d == 1) { + i = endalpha - a; + } else if (alpha < speed && d == -1) { + i = a; + } + alpha = a + (i * d); + tt.style.opacity = alpha * .01; + tt.style.filter = 'alpha(opacity=' + alpha + ')'; + } else { + clearInterval(tt.timer); + if (d == -1) { + tt.style.display = 'none'; + } else if (d == 1) { + tt.style.opacity = 1; + tt.style.filter = 'alpha(opacity=100)'; + } + } + }, + hide : function() { + clearInterval(tt.timer); + tt.timer = setInterval(function() { + tooltip.fade(-1); + }, timer); + } + }; + }(); + + function SyntaxErrorHighlightState() { + this.marked = []; + } + function getSyntaxErrorHighlightState(cm) { + return cm._syntaxErrorHighlightState + || (cm._syntaxErrorHighlightState = new SyntaxErrorHighlightState()); + } + + function clearMarks(cm, gutterID) { + cm.clearGutter(gutterID); + var state = getSyntaxErrorHighlightState(cm); + for ( var i = 0; i < state.marked.length; ++i) + state.marked[i].clear(); + state.marked = []; + } + + function makeMarker(tooltipLabel, maxSeverity, multiple) { + + var marker = document.createElement("div"); + marker.onmouseover = function() { + tooltip.show(tooltipLabel, null, maxSeverity); + }; + marker.onmouseout = function() { + tooltip.hide(); + }; + marker.className = 'annotation ' + maxSeverity; + + var markerHTML= document.createElement("div"); + markerHTML.className = 'annotationHTML ' + maxSeverity; + marker.appendChild(markerHTML); + + if (multiple) { + var markerHTML= document.createElement("div"); + markerHTML.className = 'annotationHTML overlay'; + marker.appendChild(markerHTML); + } + + return marker; + } + + var AnnotationsCollector = function() { + this.linesNumber = []; + this.linesAnnotations = []; + }; + + AnnotationsCollector.prototype.addAnnotation = function(severity, + lineStart, charStart, lineEnd, charEnd, description) { + + var annotation = { + "severity" : severity, + "lineStart" : lineStart, + "charStart" : charStart, + "lineEnd" : lineEnd, + "charEnd" : charEnd, + "description" : description + }; + var annotations = this.linesAnnotations[lineStart]; + if (!annotations) { + annotations = []; + this.linesAnnotations[lineStart] = annotations; + this.linesNumber.push(lineStart); + } + annotations.push(annotation); + }; + + function getMaxSeverity(severity1, severity2) { + // TODO : should be improved to extend severity (today just error and + // warning are managed) + if (severity1 == 'error') { + return severity1; + } + return severity2; + } + + function getTooltipLabel(content, multiple) { + var label = '
    '; + if (multiple) { + label += '
    Multiple Annotations
    '; + } + label += '
    '; + label += content; + label += '
    '; + label += '
    '; + return label; + } + + function getTooltipAnnotationLabel(description, severity) { + var label = '
    '; + label += '
    '; + label += '
    '; + label += ' '; + label += description; + label += ''; + label += '
    '; + return label; + } + + function makeMarkText(severity, lineStart, charStart, lineEnd, charEnd, annotationLabel) { + var markClass = 'annotationRange ' + severity; + var mark = cm.markText({ + line : lineStart, + ch : charStart + }, { + line : lineEnd, + ch : charEnd + }, { + className : markClass, + readOnly : false + }); + + // save mark text in an array which is used to clean mark + var state = getSyntaxErrorHighlightState(cm); + state.marked.push(mark); + + var _tooltipLabel = getTooltipLabel(annotationLabel, false); + // TODO : add tooltip for mark text. How to manage that??? + // I would like do like this + /*mark.onmouseover = function() { + tooltip.show(tooltipLabel, null, severity); + }; + mark.onmouseout = function() { + tooltip.hide(); + };*/ + } + + function markDocument() { + var gutterID = GUTTER_ID; + clearMarks(cm, gutterID); + + var collector = new AnnotationsCollector(); + + var contents = (options.getContents ? options.getContents() : cm + .getValue()); + collectAnnotations(contents, collector, cm); + + var linesNumber = collector.linesNumber; + // Loop for each lines number which contains problems + for ( var i = 0; i < linesNumber.length; i++) { + var line = linesNumber[i]; + var annotations = collector.linesAnnotations[line]; + + // loop for each annotations for the current line to + // - create a markText for each annotations + // - create a tooltip with the list of annotations + + var maxSeverity = null; + var markerTooltipLabel = ''; + var multiple = annotations.length > 1; + + // create a markText for each annotations + for ( var j = 0; j < annotations.length; j++) { + var annotation = annotations[j]; + if (options.formatAnnotation) { + annotation = options.formatAnnotation(annotation); + } + + // Create mark text + + var severity = annotation.severity; + var lineStart = annotation.lineStart; + var charStart = annotation.charStart; + var lineEnd = annotation.lineEnd; + var charEnd = annotation.charEnd; + var description = annotation.description; + + var annotationLabel = getTooltipAnnotationLabel(description, severity); + makeMarkText(severity, lineStart, charStart, lineEnd, charEnd, annotationLabel); + + markerTooltipLabel+=annotationLabel; + maxSeverity = getMaxSeverity(maxSeverity, severity); + } + markerTooltipLabel = getTooltipLabel(markerTooltipLabel, multiple); + + + // create a tooltip with the list of annotations + var marker = makeMarker(markerTooltipLabel, maxSeverity, multiple); + + var info = cm.lineInfo(line); + cm.setGutterMarker(line, gutterID, info.gutterMarkers ? null + : marker); + } + } + + return markDocument(); +}; diff --git a/demo/jsonlint.html b/demo/jsonlint.html deleted file mode 100644 index 39e90b266a..0000000000 --- a/demo/jsonlint.html +++ /dev/null @@ -1,75 +0,0 @@ - - - - - CodeMirror: JSON Lint Demo - - - - - - - - - - - - - - - -

    CodeMirror: JSON Lint Demo

    - - - - -

    This demo runs JSONLint over the code -in the editor and use CodeMirror Lint to display the warnings that JSONLint comes up with.

    - - diff --git a/demo/javascriptlint.html b/demo/lint.html similarity index 60% rename from demo/javascriptlint.html rename to demo/lint.html index de89fbb465..35f2156623 100644 --- a/demo/javascriptlint.html +++ b/demo/lint.html @@ -2,25 +2,26 @@ - CodeMirror: Javascript Lint Demo + CodeMirror: Linter Demo + - - - - + + + + -

    CodeMirror: Javascript Lint Demo

    +

    CodeMirror: Linter Demo

    - +

    + +

    -

    This demo runs JSHint over the code -in the editor and use CodeMirror Lint to display the warnings that JSHint comes up with.

    + diff --git a/doc/compress.html b/doc/compress.html index 0f5926fe60..521e6e865a 100644 --- a/doc/compress.html +++ b/doc/compress.html @@ -156,6 +156,9 @@

    { } CodeMi + + + diff --git a/lib/codemirror-lint.css b/lib/codemirror-lint.css deleted file mode 100644 index c251c973ed..0000000000 --- a/lib/codemirror-lint.css +++ /dev/null @@ -1,85 +0,0 @@ -.annotationRange { - background-position: left bottom; - background-repeat: repeat-x; -} - -.annotationRange.error { - background-image: - url("data:image/png;base64,iVBORw0KGgoAAAANSUhEUgAAAAQAAAADCAYAAAC09K7GAAAAAXNSR0IArs4c6QAAAAZiS0dEAP8A/wD/oL2nkwAAAAlwSFlzAAALEwAACxMBAJqcGAAAAAd0SU1FB9sJDw4cOCW1/KIAAAAZdEVYdENvbW1lbnQAQ3JlYXRlZCB3aXRoIEdJTVBXgQ4XAAAAHElEQVQI12NggIL/DAz/GdA5/xkY/qPKMDAwAADLZwf5rvm+LQAAAABJRU5ErkJggg==") - ; -} - -.annotationRange.warning { - /* images/squiggly_warning.png */ - background-image: url("data:image/png;base64,iVBORw0KGgoAAAANSUhEUgAAAAQAAAADCAYAAAC09K7GAAAAAXNSR0IArs4c6QAAAAZiS0dEAP8A/wD/oL2nkwAAAAlwSFlzAAALEwAACxMBAJqcGAAAAAd0SU1FB9sJFhQXEbhTg7YAAAAZdEVYdENvbW1lbnQAQ3JlYXRlZCB3aXRoIEdJTVBXgQ4XAAAAMklEQVQI12NkgIIvJ3QXMjAwdDN+OaEbysDA4MPAwNDNwMCwiOHLCd1zX07o6kBVGQEAKBANtobskNMAAAAASUVORK5CYII="); -} - -.annotationHTML { - background-position: center center; - background-repeat: no-repeat; - cursor: pointer; - display: inline-block; - height: 16px; - vertical-align: middle; - width: 16px; -} - -.annotationHTML.error { - background-image: - url("data:image/gif;base64,R0lGODlhEAAQANUAAPVvcvWHiPVucvRuc+ttcfV6f91KVN5LU99PV/FZY/JhaM4oN84pONE4Rd1ATfJLWutVYPRgbdxpcsgWKMgZKs4lNfE/UvE/U+artcpdSc5uXveimslHPuBhW/eJhfV5efaCgO2CgP+/v+PExP///////wAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAACH5BAEAACUALAAAAAAQABAAAAZ+wJJwSCwaScgkySgkjTQZTkYzWhadnE5oE+pwqkSshwQqkzxfa4kkQXxEpA9J9EFI1KQGQQBAigYCBA14ExEWF0gXihETeA0QD3AkD5QQg0NsDnAJmwkOd5gYFSQKpXAFDBhqaxgLBwQBBAapq00YEg0UDRKqTGtKSL7Cw8JBADs=") - ; -} - -.annotationHTML.warning { - /* images/warning.gif */ - background-image: url("data:image/gif;base64,R0lGODlhEAAQANUAAP7bc//egf/ij/7ijv/jl/7kl//mnv7lnv/uwf7CTP7DTf7DT/7IW//Na/7Na//NbP7QdP/dmbltAIJNAF03AMSAJMSCLKqASa2DS6uBSquCSrGHTq6ETbCHT7WKUrKIUcCVXL+UXMOYX8GWXsSZYMiib6+ETbOIUcOXX86uhd3Muf///wAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAACH5BAEAACsALAAAAAAQABAAAAZowJVwSCwaj0ihikRSJYcoBEL0XKlGkcjImQQhJBREKFnyICoThKeE/AAW6AXgdPyUAgrLJBEo0YsbAQyDhAEdRRwDDw8OaA4NDQImRBgFEJdglxAEGEQZKQcHBqOkKRpFF6mqq1WtrUEAOw=="); -} - -/* Styles for the overview ruler */ -.annotationOverview { - cursor: pointer; - border-radius: 2px; - left: 2px; - width: 8px; -} -.annotationOverview.error { - background-color: lightcoral; - border: 1px solid darkred; -} -.annotationOverview.warning { - background-color: Gold; - border: 1px solid black; -} - -.annotationHTML.overlay { - background-image: url("data:image/png;base64,iVBORw0KGgoAAAANSUhEUgAAAAcAAAAHCAYAAADEUlfTAAAAAXNSR0IArs4c6QAAAAZiS0dEAAAAAAAA+UO7fwAAAAlwSFlzAAALEwAACxMBAJqcGAAAAAd0SU1FB9sJEAQvB2JVdrAAAAAdaVRYdENvbW1lbnQAAAAAAENyZWF0ZWQgd2l0aCBHSU1QZC5lBwAAAD1JREFUCNdtjkESADAEAzemf69f66HMqGlOIhYiFRFRtSQBWAY7mzx+EDTL6sSgb1jTk7Q87rxyqe37fXsAa78gLyZnRgEAAAAASUVORK5CYII="); - background-position: right bottom; - position: relative; - top: -16px; -} - -.CodeMirror-lints { - width: 16px; -} - -.textviewTooltip { - background-color: infobackground; - border: 1px solid black; - border-radius: 4px 4px 4px 4px; - color: infotext; - font-family: monospace; - font-size: 10pt; - overflow: hidden; - padding: 2px; - position: fixed; - white-space: pre; - z-index: 100; -} - -#ttcont { - //display: block; - //padding: 2px 12px 3px 7px; - margin-left: 5px; -// background: #666; -// color: #fff; -} \ No newline at end of file From 0bcee7c52d7813f2f53c87ee8ece004ec2f1de34 Mon Sep 17 00:00:00 2001 From: Marijn Haverbeke Date: Mon, 18 Feb 2013 12:25:37 +0100 Subject: [PATCH 0744/5780] WIP --- addon/lint/json-lint.js | 32 ++-- addon/lint/lint.css | 71 ++++----- addon/lint/lint.js | 319 ++++++++++------------------------------ demo/lint.html | 8 +- 4 files changed, 133 insertions(+), 297 deletions(-) diff --git a/addon/lint/json-lint.js b/addon/lint/json-lint.js index d3c4c756ee..bbab508e98 100644 --- a/addon/lint/json-lint.js +++ b/addon/lint/json-lint.js @@ -1,20 +1,14 @@ -(function() { - - CodeMirror.jsonValidator = function(contents, collector) { - jsonlint.parseError = function(str, hash) { - var severity = 'error'; - var loc = hash.loc; - var lineStart = loc.first_line - 1; - var lineEnd = hash.line;// loc.last_line - 1; - var charStart = loc.first_column; - var charEnd = loc.last_column; - - collector.addAnnotation(severity, lineStart, charStart, - lineEnd, charEnd, str); - - }; - try { jsonlint.parse(contents); } - catch(e) {} +// Depends on jsonlint.js from https://github.com/zaach/jsonlint + +CodeMirror.jsonValidator = function(cm) { + var found = []; + jsonlint.parseError = function(str, hash) { + var loc = hash.loc; + found.push({from: CodeMirror.Pos(loc.first_line - 1, loc.first_column), + to: CodeMirror.Pos(loc.last_line - 1, loc.last_column), + message: str}); }; - -})(); + try { jsonlint.parse(cm.getValue()); } + catch(e) {} + return found; +}; diff --git a/addon/lint/lint.css b/addon/lint/lint.css index 98e883266f..d508f5cb78 100644 --- a/addon/lint/lint.css +++ b/addon/lint/lint.css @@ -1,41 +1,63 @@ -.annotationRange { +/* The lint marker gutter */ +.CodeMirror-lint-markers { + width: 16px; +} + +.CodeMirror-lint-tooltip { + background-color: infobackground; + border: 1px solid black; + border-radius: 4px 4px 4px 4px; + color: infotext; + font-family: monospace; + font-size: 10pt; + overflow: hidden; + padding: 2px 5px; + position: fixed; + white-space: pre; + z-index: 100; + max-width: 600px; + opacity: 0; + transition: opacity .4s; + -moz-transition: opacity .4s; + -webkit-transition: opacity .4s; + -o-transition: opacity .4s; + -ms-transition: opacity .4s; +} + +.CodeMirror-lint { background-position: left bottom; background-repeat: repeat-x; } -.annotationRange.error { +.CodeMirror-lint-error { background-image: url("data:image/png;base64,iVBORw0KGgoAAAANSUhEUgAAAAQAAAADCAYAAAC09K7GAAAAAXNSR0IArs4c6QAAAAZiS0dEAP8A/wD/oL2nkwAAAAlwSFlzAAALEwAACxMBAJqcGAAAAAd0SU1FB9sJDw4cOCW1/KIAAAAZdEVYdENvbW1lbnQAQ3JlYXRlZCB3aXRoIEdJTVBXgQ4XAAAAHElEQVQI12NggIL/DAz/GdA5/xkY/qPKMDAwAADLZwf5rvm+LQAAAABJRU5ErkJggg==") ; } -.annotationRange.warning { +.CodeMirror-lint-warning { /* images/squiggly_warning.png */ background-image: url("data:image/png;base64,iVBORw0KGgoAAAANSUhEUgAAAAQAAAADCAYAAAC09K7GAAAAAXNSR0IArs4c6QAAAAZiS0dEAP8A/wD/oL2nkwAAAAlwSFlzAAALEwAACxMBAJqcGAAAAAd0SU1FB9sJFhQXEbhTg7YAAAAZdEVYdENvbW1lbnQAQ3JlYXRlZCB3aXRoIEdJTVBXgQ4XAAAAMklEQVQI12NkgIIvJ3QXMjAwdDN+OaEbysDA4MPAwNDNwMCwiOHLCd1zX07o6kBVGQEAKBANtobskNMAAAAASUVORK5CYII="); } -.annotationHTML { +.CodeMirror-lint-marker-error, .CodeMirror-lint-marker-warning { background-position: center center; background-repeat: no-repeat; cursor: pointer; display: inline-block; height: 16px; - vertical-align: middle; width: 16px; + vertical-align: middle; } -.annotationHTML.error { - background-image: - url("data:image/gif;base64,R0lGODlhEAAQANUAAPVvcvWHiPVucvRuc+ttcfV6f91KVN5LU99PV/FZY/JhaM4oN84pONE4Rd1ATfJLWutVYPRgbdxpcsgWKMgZKs4lNfE/UvE/U+artcpdSc5uXveimslHPuBhW/eJhfV5efaCgO2CgP+/v+PExP///////wAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAACH5BAEAACUALAAAAAAQABAAAAZ+wJJwSCwaScgkySgkjTQZTkYzWhadnE5oE+pwqkSshwQqkzxfa4kkQXxEpA9J9EFI1KQGQQBAigYCBA14ExEWF0gXihETeA0QD3AkD5QQg0NsDnAJmwkOd5gYFSQKpXAFDBhqaxgLBwQBBAapq00YEg0UDRKqTGtKSL7Cw8JBADs=") - ; +.CodeMirror-lint-marker-error { + background-image: url("data:image/gif;base64,R0lGODlhEAAQANUAAPVvcvWHiPVucvRuc+ttcfV6f91KVN5LU99PV/FZY/JhaM4oN84pONE4Rd1ATfJLWutVYPRgbdxpcsgWKMgZKs4lNfE/UvE/U+artcpdSc5uXveimslHPuBhW/eJhfV5efaCgO2CgP+/v+PExP///////wAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAACH5BAEAACUALAAAAAAQABAAAAZ+wJJwSCwaScgkySgkjTQZTkYzWhadnE5oE+pwqkSshwQqkzxfa4kkQXxEpA9J9EFI1KQGQQBAigYCBA14ExEWF0gXihETeA0QD3AkD5QQg0NsDnAJmwkOd5gYFSQKpXAFDBhqaxgLBwQBBAapq00YEg0UDRKqTGtKSL7Cw8JBADs="); } -.annotationHTML.warning { - /* images/warning.gif */ +.CodeMirror-lint-marker-warning { background-image: url("data:image/gif;base64,R0lGODlhEAAQANUAAP7bc//egf/ij/7ijv/jl/7kl//mnv7lnv/uwf7CTP7DTf7DT/7IW//Na/7Na//NbP7QdP/dmbltAIJNAF03AMSAJMSCLKqASa2DS6uBSquCSrGHTq6ETbCHT7WKUrKIUcCVXL+UXMOYX8GWXsSZYMiib6+ETbOIUcOXX86uhd3Muf///wAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAACH5BAEAACsALAAAAAAQABAAAAZowJVwSCwaj0ihikRSJYcoBEL0XKlGkcjImQQhJBREKFnyICoThKeE/AAW6AXgdPyUAgrLJBEo0YsbAQyDhAEdRRwDDw8OaA4NDQImRBgFEJdglxAEGEQZKQcHBqOkKRpFF6mqq1WtrUEAOw=="); } - -/* Styles for the overview ruler */ +/* Styles for the overview ruler .annotationOverview { cursor: pointer; border-radius: 2px; @@ -57,25 +79,4 @@ position: relative; top: -16px; } - -.CodeMirror-lints { - width: 16px; -} - -.textviewTooltip { - background-color: infobackground; - border: 1px solid black; - border-radius: 4px 4px 4px 4px; - color: infotext; - font-family: monospace; - font-size: 10pt; - overflow: hidden; - padding: 2px; - position: fixed; - white-space: pre; - z-index: 100; -} - -#ttcont { - margin-left: 5px; -} +*/ \ No newline at end of file diff --git a/addon/lint/lint.js b/addon/lint/lint.js index a2713efcce..8db5d4624b 100644 --- a/addon/lint/lint.js +++ b/addon/lint/lint.js @@ -1,282 +1,123 @@ -CodeMirror.validate = function(cm, collectAnnotations, options) { +CodeMirror.validate = function(cm, getAnnotations, options) { if (!options) options = {}; - var GUTTER_ID = "CodeMirror-lints"; + var GUTTER_ID = "CodeMirror-lint-markers"; + var SEVERITIES = /^(?:error|warning)$/; - // see - // http://sixrevisions.com/tutorials/javascript_tutorial/create_lightweight_javascript_tooltip/ var tooltip = function() { - var id = 'tt'; - var top = 3; - var left = 3; - var maxw = 600; - var speed = 10; - var timer = 20; - var endalpha = 95; - var alpha = 0; - var tt, c, h; - var ie = document.all ? true : false; + var tt; return { - show : function(v, w) { + show: function(nodes, e) { if (tt == null) { - tt = document.createElement('div'); - tt.setAttribute('id', id); - tt.className = 'textviewTooltip'; - //t = document.createElement('div'); - //t.setAttribute('id', id + 'top'); - c = document.createElement('div'); - c.setAttribute('id', id + 'cont'); - //b = document.createElement('div'); - //b.setAttribute('id', id + 'bot'); - //tt.appendChild(t); - tt.appendChild(c); - //tt.appendChild(b); + tt = document.createElement("div"); + tt.className = "CodeMirror-lint-tooltip"; document.body.appendChild(tt); - tt.style.opacity = 0; - tt.style.filter = 'alpha(opacity=0)'; - document.onmousemove = this.pos; + CodeMirror.on(document, "mousemove", this.pos); + this.pos(e); } - tt.style.display = 'block'; - c.innerHTML = v; - tt.style.width = w ? w + 'px' : 'auto'; - if (!w && ie) { - //t.style.display = 'none'; - //b.style.display = 'none'; - tt.style.width = tt.offsetWidth; - //t.style.display = 'block'; - //b.style.display = 'block'; - } - if (tt.offsetWidth > maxw) { - tt.style.width = maxw + 'px'; - } - h = parseInt(tt.offsetHeight) + top; - clearInterval(tt.timer); - tt.timer = setInterval(function() { - tooltip.fade(1); - }, timer); + tt.style.display = "block"; + tt.appendChild(nodes); + this.fade(true); }, - pos : function(e) { - var u = ie ? event.clientY + document.documentElement.scrollTop - : e.pageY; - var l = ie ? event.clientX - + document.documentElement.scrollLeft : e.pageX; - tt.style.top = (u - h) + 'px'; - tt.style.left = (l + left) + 'px'; + pos: function(e) { + if (tt.style.display == "none") return; + + var u = event.pageY, l = event.pageX; + if (u == null) { + u = event.clientY + document.documentElement.scrollTop; + l = event.clientX + document.documentElement.scrollLeft; + } + tt.style.top = (u - tt.offsetHeight - 5) + "px"; + tt.style.left = (l + 5) + "px"; }, - fade : function(d) { - var a = alpha; - if ((a != endalpha && d == 1) || (a != 0 && d == -1)) { - var i = speed; - if (endalpha - a < speed && d == 1) { - i = endalpha - a; - } else if (alpha < speed && d == -1) { - i = a; - } - alpha = a + (i * d); - tt.style.opacity = alpha * .01; - tt.style.filter = 'alpha(opacity=' + alpha + ')'; - } else { - clearInterval(tt.timer); - if (d == -1) { - tt.style.display = 'none'; - } else if (d == 1) { - tt.style.opacity = 1; - tt.style.filter = 'alpha(opacity=100)'; - } - } + fade: function(show) { + if (!show) setTimeout(function() { + if (tt.style.opacity == "0") tt.style.display = "none"; + }, 600); + else tt.style.display = ""; + tt.style.opacity = show ? 1 : 0; }, - hide : function() { - clearInterval(tt.timer); - tt.timer = setInterval(function() { - tooltip.fade(-1); - }, timer); - } + hide: function() { this.fade(false); } }; }(); - function SyntaxErrorHighlightState() { + function LintState() { this.marked = []; } - function getSyntaxErrorHighlightState(cm) { - return cm._syntaxErrorHighlightState - || (cm._syntaxErrorHighlightState = new SyntaxErrorHighlightState()); + function getLintState(cm) { + return cm._lintState || (cm._lintState = new LintState()); } - function clearMarks(cm, gutterID) { - cm.clearGutter(gutterID); - var state = getSyntaxErrorHighlightState(cm); - for ( var i = 0; i < state.marked.length; ++i) + function clearMarks(cm, state) { + if (options.gutterMarkers) cm.clearGutter(GUTTER_ID); + for (var i = 0; i < state.marked.length; ++i) state.marked[i].clear(); - state.marked = []; + state.marked.length = 0; } - function makeMarker(tooltipLabel, maxSeverity, multiple) { - + function makeMarker(labels, severity) { var marker = document.createElement("div"); - marker.onmouseover = function() { - tooltip.show(tooltipLabel, null, maxSeverity); - }; - marker.onmouseout = function() { + marker.className = "CodeMirror-lint-marker-" + severity; + + CodeMirror.on(marker, "mouseover", function(e) { + tooltip.show(labels, e); + }); + CodeMirror.on(marker, "mouseout", function(e) { tooltip.hide(); - }; - marker.className = 'annotation ' + maxSeverity; - - var markerHTML= document.createElement("div"); - markerHTML.className = 'annotationHTML ' + maxSeverity; - marker.appendChild(markerHTML); - - if (multiple) { - var markerHTML= document.createElement("div"); - markerHTML.className = 'annotationHTML overlay'; - marker.appendChild(markerHTML); - } - + }); + return marker; } - var AnnotationsCollector = function() { - this.linesNumber = []; - this.linesAnnotations = []; - }; - - AnnotationsCollector.prototype.addAnnotation = function(severity, - lineStart, charStart, lineEnd, charEnd, description) { - - var annotation = { - "severity" : severity, - "lineStart" : lineStart, - "charStart" : charStart, - "lineEnd" : lineEnd, - "charEnd" : charEnd, - "description" : description - }; - var annotations = this.linesAnnotations[lineStart]; - if (!annotations) { - annotations = []; - this.linesAnnotations[lineStart] = annotations; - this.linesNumber.push(lineStart); - } - annotations.push(annotation); - }; - - function getMaxSeverity(severity1, severity2) { - // TODO : should be improved to extend severity (today just error and - // warning are managed) - if (severity1 == 'error') { - return severity1; - } - return severity2; + function getMaxSeverity(a, b) { + if (a == "error") return a; + else return b; } - - function getTooltipLabel(content, multiple) { - var label = '
    '; - if (multiple) { - label += '
    Multiple Annotations
    '; + + function groupByLine(annotations) { + var lines = []; + for (var i = 0; i < annotations.length; ++i) { + var ann = annotations[i], line = ann.from.line; + (lines[line] || (lines[line] = [])).push(ann); } - label += '
    '; - label += content; - label += '
    '; - label += '
    '; - return label; - } - - function getTooltipAnnotationLabel(description, severity) { - var label = '
    '; - label += '
    '; - label += '
    '; - label += ' '; - label += description; - label += ''; - label += '
    '; - return label; - } - - function makeMarkText(severity, lineStart, charStart, lineEnd, charEnd, annotationLabel) { - var markClass = 'annotationRange ' + severity; - var mark = cm.markText({ - line : lineStart, - ch : charStart - }, { - line : lineEnd, - ch : charEnd - }, { - className : markClass, - readOnly : false - }); - - // save mark text in an array which is used to clean mark - var state = getSyntaxErrorHighlightState(cm); - state.marked.push(mark); - - var _tooltipLabel = getTooltipLabel(annotationLabel, false); - // TODO : add tooltip for mark text. How to manage that??? - // I would like do like this - /*mark.onmouseover = function() { - tooltip.show(tooltipLabel, null, severity); - }; - mark.onmouseout = function() { - tooltip.hide(); - };*/ + return lines; } - function markDocument() { - var gutterID = GUTTER_ID; - clearMarks(cm, gutterID); + function markDocument(cm) { + var state = getLintState(cm); + clearMarks(cm, state); - var collector = new AnnotationsCollector(); + var annotations = groupByLine(getAnnotations(cm)); - var contents = (options.getContents ? options.getContents() : cm - .getValue()); - collectAnnotations(contents, collector, cm); + for (var line = 0; line < annotations.length; ++line) { + var anns = annotations[line]; + if (!anns) continue; - var linesNumber = collector.linesNumber; - // Loop for each lines number which contains problems - for ( var i = 0; i < linesNumber.length; i++) { - var line = linesNumber[i]; - var annotations = collector.linesAnnotations[line]; - - // loop for each annotations for the current line to - // - create a markText for each annotations - // - create a tooltip with the list of annotations + var maxSeverity = null, tipLabel = document.createDocumentFragment(); - var maxSeverity = null; - var markerTooltipLabel = ''; - var multiple = annotations.length > 1; - - // create a markText for each annotations - for ( var j = 0; j < annotations.length; j++) { - var annotation = annotations[j]; - if (options.formatAnnotation) { - annotation = options.formatAnnotation(annotation); - } + for (var i = 0; i < anns.length; ++i) { + var ann = anns[i]; + var severity = ann.severity; + if (!SEVERITIES.test(severity)) severity = "error"; + maxSeverity = getMaxSeverity(maxSeverity, severity); - // Create mark text - - var severity = annotation.severity; - var lineStart = annotation.lineStart; - var charStart = annotation.charStart; - var lineEnd = annotation.lineEnd; - var charEnd = annotation.charEnd; - var description = annotation.description; + if (options.formatAnnotation) ann = options.formatAnnotation(ann); - var annotationLabel = getTooltipAnnotationLabel(description, severity); - makeMarkText(severity, lineStart, charStart, lineEnd, charEnd, annotationLabel); - - markerTooltipLabel+=annotationLabel; - maxSeverity = getMaxSeverity(maxSeverity, severity); - } - markerTooltipLabel = getTooltipLabel(markerTooltipLabel, multiple); + var label = document.createElement("div"); + label.className = "CodeMirror-hint-message-" + severity; + label.appendChild(document.createTextNode(ann.message)); + tipLabel.appendChild(label); - - // create a tooltip with the list of annotations - var marker = makeMarker(markerTooltipLabel, maxSeverity, multiple); + if (ann.to) state.marked.push(cm.markText(ann.from, ann.to, { + className: "CodeMirror-lint CodeMirror-lint-" + severity + })); + } - var info = cm.lineInfo(line); - cm.setGutterMarker(line, gutterID, info.gutterMarkers ? null - : marker); + if (options.gutterMarkers !== false) + cm.setGutterMarker(i, GUTTER_ID, makeMarker(tipLabel, maxSeverity)); } } - return markDocument(); + return markDocument(cm); }; diff --git a/demo/lint.html b/demo/lint.html index 35f2156623..1b66c7f468 100644 --- a/demo/lint.html +++ b/demo/lint.html @@ -77,9 +77,9 @@

    CodeMirror: Linter Demo

    var editor = CodeMirror.fromTextArea(document.getElementById("code-js"), { lineNumbers: true, mode: "javascript", - gutters: ["CodeMirror-lints"] + gutters: ["CodeMirror-lint-markers"] }); - +/* var validate = function() { CodeMirror.validate(editor, CodeMirror.javascriptValidator); }; @@ -89,13 +89,13 @@

    CodeMirror: Linter Demo

    waiting = setTimeout(validate, 500); }); - setTimeout(validate, 100); + setTimeout(validate, 100);*/ var editor2 = CodeMirror.fromTextArea(document.getElementById("code-json"), { lineNumbers: true, mode: "application/json", - gutters: ["CodeMirror-lints"] + gutters: ["CodeMirror-lint-markers"] }); var validate2 = function() { From 5a5d7c570008a8b18cc95b5ad3096bba46f4ade0 Mon Sep 17 00:00:00 2001 From: Marijn Haverbeke Date: Mon, 18 Feb 2013 14:18:59 +0100 Subject: [PATCH 0745/5780] [linter addon] Rewrite. Make tooltips on text work --- addon/lint/javascript-lint.js | 40 +++---- addon/lint/lint.css | 26 +++-- addon/lint/lint.js | 189 +++++++++++++++++++++------------- demo/lint.html | 39 ++----- 4 files changed, 158 insertions(+), 136 deletions(-) diff --git a/addon/lint/javascript-lint.js b/addon/lint/javascript-lint.js index e3f84b857a..2200bba3e4 100644 --- a/addon/lint/javascript-lint.js +++ b/addon/lint/javascript-lint.js @@ -9,20 +9,13 @@ "Unmatched ", " and instead saw", " is not defined", "Unclosed string", "Stopping, unable to continue" ]; - CodeMirror.javascriptValidator = function(contents, collector) { - var positionalAdjustment = 1; - var result = jshint(contents); - var errors = result.errors; - if (errors) { - parseErrors(errors, positionalAdjustment, collector); - } + CodeMirror.javascriptValidator = function(cm) { + JSHINT(cm.getValue()); + var errors = JSHINT.data().errors, result = []; + if (errors) parseErrors(errors, result); + return result; }; - function jshint(contents) { - JSHINT(contents/* , localConfig.options, localConfig.options.predef */); - return JSHINT.data(); - } - function cleanup(error) { // All problems are warnings by default fixWith(error, warnings, "warning", true); @@ -61,7 +54,7 @@ return false; } - function parseErrors(errors, positionalAdjustment, collector) { + function parseErrors(errors, output) { for ( var i = 0; i < errors.length; i++) { var error = errors[i]; if (error) { @@ -97,9 +90,7 @@ if (tabpositions.length > 0) { var pos = error.character; tabpositions.forEach(function(tabposition) { - if (pos > tabposition) { - pos -= positionalAdjustment; - } + if (pos > tabposition) pos -= 1; }); error.character = pos; } @@ -119,18 +110,11 @@ error.end = end; error = cleanup(error); - if (error) { - var severity = error.severity; - var lineStart = error.line - 1; - var charStart = start; - var lineEnd = error.line - 1; - var charEnd = end; - var description = error.description; - - collector.addAnnotation(severity, lineStart, charStart, - lineEnd, charEnd, description); - - } + if (error) + output.push({message: error.description, + severity: error.severity, + from: CodeMirror.Pos(error.line - 1, start), + to: CodeMirror.Pos(error.line - 1, end)}); } } } diff --git a/addon/lint/lint.css b/addon/lint/lint.css index d508f5cb78..4fd72e774a 100644 --- a/addon/lint/lint.css +++ b/addon/lint/lint.css @@ -24,19 +24,18 @@ -ms-transition: opacity .4s; } -.CodeMirror-lint { +.CodeMirror-lint-span-error, .CodeMirror-lint-span-warning { background-position: left bottom; background-repeat: repeat-x; } -.CodeMirror-lint-error { +.CodeMirror-lint-span-error { background-image: url("data:image/png;base64,iVBORw0KGgoAAAANSUhEUgAAAAQAAAADCAYAAAC09K7GAAAAAXNSR0IArs4c6QAAAAZiS0dEAP8A/wD/oL2nkwAAAAlwSFlzAAALEwAACxMBAJqcGAAAAAd0SU1FB9sJDw4cOCW1/KIAAAAZdEVYdENvbW1lbnQAQ3JlYXRlZCB3aXRoIEdJTVBXgQ4XAAAAHElEQVQI12NggIL/DAz/GdA5/xkY/qPKMDAwAADLZwf5rvm+LQAAAABJRU5ErkJggg==") ; } -.CodeMirror-lint-warning { - /* images/squiggly_warning.png */ +.CodeMirror-lint-span-warning { background-image: url("data:image/png;base64,iVBORw0KGgoAAAANSUhEUgAAAAQAAAADCAYAAAC09K7GAAAAAXNSR0IArs4c6QAAAAZiS0dEAP8A/wD/oL2nkwAAAAlwSFlzAAALEwAACxMBAJqcGAAAAAd0SU1FB9sJFhQXEbhTg7YAAAAZdEVYdENvbW1lbnQAQ3JlYXRlZCB3aXRoIEdJTVBXgQ4XAAAAMklEQVQI12NkgIIvJ3QXMjAwdDN+OaEbysDA4MPAwNDNwMCwiOHLCd1zX07o6kBVGQEAKBANtobskNMAAAAASUVORK5CYII="); } @@ -48,15 +47,30 @@ height: 16px; width: 16px; vertical-align: middle; + position: relative; } -.CodeMirror-lint-marker-error { +.CodeMirror-lint-message-error, .CodeMirror-lint-message-warning { + padding-left: 18px; + background-position: top left; + background-repeat: no-repeat; +} + +.CodeMirror-lint-marker-error, .CodeMirror-lint-message-error { background-image: url("data:image/gif;base64,R0lGODlhEAAQANUAAPVvcvWHiPVucvRuc+ttcfV6f91KVN5LU99PV/FZY/JhaM4oN84pONE4Rd1ATfJLWutVYPRgbdxpcsgWKMgZKs4lNfE/UvE/U+artcpdSc5uXveimslHPuBhW/eJhfV5efaCgO2CgP+/v+PExP///////wAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAACH5BAEAACUALAAAAAAQABAAAAZ+wJJwSCwaScgkySgkjTQZTkYzWhadnE5oE+pwqkSshwQqkzxfa4kkQXxEpA9J9EFI1KQGQQBAigYCBA14ExEWF0gXihETeA0QD3AkD5QQg0NsDnAJmwkOd5gYFSQKpXAFDBhqaxgLBwQBBAapq00YEg0UDRKqTGtKSL7Cw8JBADs="); } -.CodeMirror-lint-marker-warning { +.CodeMirror-lint-marker-warning, .CodeMirror-lint-message-warning { background-image: url("data:image/gif;base64,R0lGODlhEAAQANUAAP7bc//egf/ij/7ijv/jl/7kl//mnv7lnv/uwf7CTP7DTf7DT/7IW//Na/7Na//NbP7QdP/dmbltAIJNAF03AMSAJMSCLKqASa2DS6uBSquCSrGHTq6ETbCHT7WKUrKIUcCVXL+UXMOYX8GWXsSZYMiib6+ETbOIUcOXX86uhd3Muf///wAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAACH5BAEAACsALAAAAAAQABAAAAZowJVwSCwaj0ihikRSJYcoBEL0XKlGkcjImQQhJBREKFnyICoThKeE/AAW6AXgdPyUAgrLJBEo0YsbAQyDhAEdRRwDDw8OaA4NDQImRBgFEJdglxAEGEQZKQcHBqOkKRpFF6mqq1WtrUEAOw=="); } + +.CodeMirror-lint-marker-multiple { + background-image: url("data:image/png;base64,iVBORw0KGgoAAAANSUhEUgAAAAcAAAAHCAYAAADEUlfTAAAAAXNSR0IArs4c6QAAAAZiS0dEAAAAAAAA+UO7fwAAAAlwSFlzAAALEwAACxMBAJqcGAAAAAd0SU1FB9sJEAQvB2JVdrAAAAAdaVRYdENvbW1lbnQAAAAAAENyZWF0ZWQgd2l0aCBHSU1QZC5lBwAAAD1JREFUCNdtjkESADAEAzemf69f66HMqGlOIhYiFRFRtSQBWAY7mzx+EDTL6sSgb1jTk7Q87rxyqe37fXsAa78gLyZnRgEAAAAASUVORK5CYII="); + background-repeat: no-repeat; + background-position: right bottom; + width: 100%; height: 100%; +} + /* Styles for the overview ruler .annotationOverview { cursor: pointer; diff --git a/addon/lint/lint.js b/addon/lint/lint.js index 8db5d4624b..d59478aab0 100644 --- a/addon/lint/lint.js +++ b/addon/lint/lint.js @@ -1,71 +1,66 @@ -CodeMirror.validate = function(cm, getAnnotations, options) { - if (!options) - options = {}; - +CodeMirror.validate = (function() { var GUTTER_ID = "CodeMirror-lint-markers"; var SEVERITIES = /^(?:error|warning)$/; - var tooltip = function() { - var tt; - return { - show: function(nodes, e) { - if (tt == null) { - tt = document.createElement("div"); - tt.className = "CodeMirror-lint-tooltip"; - document.body.appendChild(tt); - CodeMirror.on(document, "mousemove", this.pos); - this.pos(e); - } - tt.style.display = "block"; - tt.appendChild(nodes); - this.fade(true); - }, - pos: function(e) { - if (tt.style.display == "none") return; - - var u = event.pageY, l = event.pageX; - if (u == null) { - u = event.clientY + document.documentElement.scrollTop; - l = event.clientX + document.documentElement.scrollLeft; - } - tt.style.top = (u - tt.offsetHeight - 5) + "px"; - tt.style.left = (l + 5) + "px"; - }, - fade: function(show) { - if (!show) setTimeout(function() { - if (tt.style.opacity == "0") tt.style.display = "none"; - }, 600); - else tt.style.display = ""; - tt.style.opacity = show ? 1 : 0; - }, - hide: function() { this.fade(false); } - }; - }(); - - function LintState() { + function showTooltip(e, content) { + var tt = document.createElement("div"); + tt.className = "CodeMirror-lint-tooltip"; + tt.appendChild(content.cloneNode(true)); + document.body.appendChild(tt); + + function position(e) { + if (!tt.parentNode) return CodeMirror.off(document, "mousemove", position); + tt.style.top = (e.clientY - tt.offsetHeight - 5) + "px"; + tt.style.left = (e.clientX + 5) + "px"; + } + CodeMirror.on(document, "mousemove", position); + position(e); + tt.style.opacity = 1; + return tt; + } + function rm(elt) { + if (elt.parentNode) elt.parentNode.removeChild(elt); + } + function hideTooltip(tt) { + if (!tt.parentNode) return; + if (tt.style.opacity == null) rm(tt); + tt.style.opacity = 0; + setTimeout(function() { rm(tt); }, 600); + } + + function LintState(cm, options, hasGutter) { this.marked = []; + this.options = options; + this.timeout = null; + this.hasGutter = hasGutter; + this.onMouseOver = function(e) { onMouseOver(cm, e); }; } - function getLintState(cm) { - return cm._lintState || (cm._lintState = new LintState()); + + function parseOptions(options) { + if (options instanceof Function) return {getAnnotations: options}; + else if (!options || !options.getAnnotations) throw new Error("Required option 'getAnnotations' missing (lint addon)"); + return options; } - function clearMarks(cm, state) { - if (options.gutterMarkers) cm.clearGutter(GUTTER_ID); + function clearMarks(cm) { + var state = cm._lintState; + if (state.hasGutter) cm.clearGutter(GUTTER_ID); for (var i = 0; i < state.marked.length; ++i) state.marked[i].clear(); state.marked.length = 0; } - function makeMarker(labels, severity) { - var marker = document.createElement("div"); + function makeMarker(labels, severity, multiple) { + var marker = document.createElement("div"), inner = marker; marker.className = "CodeMirror-lint-marker-" + severity; + if (multiple) { + inner = marker.appendChild(document.createElement("div")); + inner.className = "CodeMirror-lint-marker-multiple"; + } - CodeMirror.on(marker, "mouseover", function(e) { - tooltip.show(labels, e); - }); - CodeMirror.on(marker, "mouseout", function(e) { - tooltip.hide(); - }); + var tooltip; + CodeMirror.on(inner, "mouseover", function(e) { tooltip = showTooltip(e, labels); }); + CodeMirror.on(inner, "mouseout", function() { if (tooltip) hideTooltip(tooltip); }); return marker; } @@ -84,17 +79,27 @@ CodeMirror.validate = function(cm, getAnnotations, options) { return lines; } - function markDocument(cm) { - var state = getLintState(cm); - clearMarks(cm, state); + function annotationTooltip(ann) { + var severity = ann.severity; + if (!SEVERITIES.test(severity)) severity = "error"; + var tip = document.createElement("div"); + tip.className = "CodeMirror-lint-message-" + severity; + tip.appendChild(document.createTextNode(ann.message)); + return tip; + } + + function updateLinting(cm) { + clearMarks(cm); + var state = cm._lintState, options = state.options; - var annotations = groupByLine(getAnnotations(cm)); + var annotations = groupByLine(options.getAnnotations(cm)); for (var line = 0; line < annotations.length; ++line) { var anns = annotations[line]; if (!anns) continue; - var maxSeverity = null, tipLabel = document.createDocumentFragment(); + var maxSeverity = null; + var tipLabel = state.hasGutter && document.createDocumentFragment(); for (var i = 0; i < anns.length; ++i) { var ann = anns[i]; @@ -103,21 +108,67 @@ CodeMirror.validate = function(cm, getAnnotations, options) { maxSeverity = getMaxSeverity(maxSeverity, severity); if (options.formatAnnotation) ann = options.formatAnnotation(ann); - - var label = document.createElement("div"); - label.className = "CodeMirror-hint-message-" + severity; - label.appendChild(document.createTextNode(ann.message)); - tipLabel.appendChild(label); + if (state.hasGutter) tipLabel.appendChild(annotationTooltip(ann)); if (ann.to) state.marked.push(cm.markText(ann.from, ann.to, { - className: "CodeMirror-lint CodeMirror-lint-" + severity + className: "CodeMirror-lint-span-" + severity, + __annotation: ann })); } - if (options.gutterMarkers !== false) - cm.setGutterMarker(i, GUTTER_ID, makeMarker(tipLabel, maxSeverity)); + if (state.hasGutter) + cm.setGutterMarker(line, GUTTER_ID, makeMarker(tipLabel, maxSeverity, anns.length > 1)); } } - return markDocument(cm); -}; + function onChange(cm) { + var state = cm._lintState; + clearTimeout(state.timeout); + state.timeout = setTimeout(function(){updateLinting(cm);}, state.options.delay || 500); + } + + function popupSpanTooltip(ann, e) { + var tooltip = showTooltip(e, annotationTooltip(ann)); + var target = e.target || e.srcElement; + CodeMirror.on(target, "mouseout", hide); + function hide() { + CodeMirror.off(target, "mouseout", hide); + hideTooltip(tooltip); + } + } + + // When the mouseover fires, the cursor might not actually be over + // the character itself yet. These pairs of x,y offsets are used to + // probe a few nearby points when no suitable marked range is found. + var nearby = [0, 0, 0, 5, 0, -5, 5, 0, -5, 0]; + + function onMouseOver(cm, e) { + if (!/\bCodeMirror-lint-span-/.test((e.target || e.srcElement).className)) return; + for (var i = 0; i < nearby.length; i += 2) { + var spans = cm.findMarksAt(cm.coordsChar({left: e.clientX + nearby[i], + top: e.clientY + nearby[i + 1]})); + for (var j = 0; j < spans.length; ++j) { + var span = spans[j], ann = span.__annotation; + if (ann) return popupSpanTooltip(ann, e); + } + } + } + + CodeMirror.defineOption("lintWith", false, function(cm, val, old) { + if (old && old != CodeMirror.Init) { + clearMarks(cm); + cm.off("change", onChange); + CodeMirror.off(cm.getWrapperElement(), "mouseover", cm._lintState.onMouseOver); + delete cm._lintState; + } + + if (val) { + var gutters = cm.getOption("gutters"), hasLintGutter = false; + for (var i = 0; i < gutters.length; ++i) if (gutters[i] == GUTTER_ID) hasLintGutter = true; + var state = cm._lintState = new LintState(cm, parseOptions(val), hasLintGutter); + cm.on("change", onChange); + CodeMirror.on(cm.getWrapperElement(), "mouseover", state.onMouseOver); + updateLinting(cm); + } + }); +})(); diff --git a/demo/lint.html b/demo/lint.html index 1b66c7f468..ece8b1cef7 100644 --- a/demo/lint.html +++ b/demo/lint.html @@ -50,8 +50,7 @@

    CodeMirror: Linter Demo

    From 85893eca60510784d8122824c8e1152fe5546118 Mon Sep 17 00:00:00 2001 From: Matthias BUSSONNIER Date: Sun, 17 Feb 2013 12:36:07 +0100 Subject: [PATCH 0746/5780] [python mode] Make tokenizer regexps configurable --- mode/python/index.html | 11 +++++++++++ mode/python/python.js | 14 +++++++------- 2 files changed, 18 insertions(+), 7 deletions(-) diff --git a/mode/python/index.html b/mode/python/index.html index c1df119485..4244c6fc5a 100644 --- a/mode/python/index.html +++ b/mode/python/index.html @@ -118,6 +118,17 @@

    Configuration Options:

  • version - 2/3 - The version of Python to recognize. Default is 2.
  • 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.
  • +

    Advanced Configuration Options:

    +

    Usefull for superset of python syntax like Enthought enaml, IPython magics and questionmark help

    +
      +
    • singleOperators - RegEx - Regular Expression for single operator matching, default :
      ^[\\+\\-\\*/%&|\\^~<>!]
    • +
    • singleDelimiters - RegEx - Regular Expression for single delimiter matching, default :
      ^[\\(\\)\\[\\]\\{\\}@,:`=;\\.]
    • +
    • doubleOperators - RegEx - Regular Expression for double operators matching, default :
      ^((==)|(!=)|(<=)|(>=)|(<>)|(<<)|(>>)|(//)|(\\*\\*))
    • +
    • doubleDelimiters - RegEx - Regular Expressoin for double delimiters matching, default :
      ^((\\+=)|(\\-=)|(\\*=)|(%=)|(/=)|(&=)|(\\|=)|(\\^=))
    • +
    • tripleDelimiters - RegEx - Regular Expression for triple delimiters matching, default :
      ^((//=)|(>>=)|(<<=)|(\\*\\*=))
    • +
    • identifiers - RegEx - Regular Expression for identifier, default :
      ^[_A-Za-z][_A-Za-z0-9]*
    • +
    +

    MIME types defined: text/x-python.

    diff --git a/mode/python/python.js b/mode/python/python.js index a12e326dc8..951aaf8197 100644 --- a/mode/python/python.js +++ b/mode/python/python.js @@ -1,16 +1,16 @@ CodeMirror.defineMode("python", function(conf, parserConf) { var ERRORCLASS = 'error'; - + function wordRegexp(words) { return new RegExp("^((" + words.join(")|(") + "))\\b"); } - var singleOperators = new RegExp("^[\\+\\-\\*/%&|\\^~<>!]"); - var singleDelimiters = new RegExp('^[\\(\\)\\[\\]\\{\\}@,:`=;\\.]'); - var doubleOperators = new RegExp("^((==)|(!=)|(<=)|(>=)|(<>)|(<<)|(>>)|(//)|(\\*\\*))"); - var doubleDelimiters = new RegExp("^((\\+=)|(\\-=)|(\\*=)|(%=)|(/=)|(&=)|(\\|=)|(\\^=))"); - var tripleDelimiters = new RegExp("^((//=)|(>>=)|(<<=)|(\\*\\*=))"); - var identifiers = new RegExp("^[_A-Za-z][_A-Za-z0-9]*"); + var singleOperators = parserConf.singleOperators || new RegExp("^[\\+\\-\\*/%&|\\^~<>!]"); + var singleDelimiters = parserConf.singleDelimiters || new RegExp('^[\\(\\)\\[\\]\\{\\}@,:`=;\\.]'); + var doubleOperators = parserConf.doubleOperators || new RegExp("^((==)|(!=)|(<=)|(>=)|(<>)|(<<)|(>>)|(//)|(\\*\\*))"); + var doubleDelimiters = parserConf.doubleDelimiters || new RegExp("^((\\+=)|(\\-=)|(\\*=)|(%=)|(/=)|(&=)|(\\|=)|(\\^=))"); + var tripleDelimiters = parserConf.tripleDelimiters || new RegExp("^((//=)|(>>=)|(<<=)|(\\*\\*=))"); + var identifiers = parserConf.identifiers|| new RegExp("^[_A-Za-z][_A-Za-z0-9]*"); var wordOperators = wordRegexp(['and', 'or', 'not', 'is', 'in']); var commonkeywords = ['as', 'assert', 'break', 'class', 'continue', From f92ea27a83e048e63bfa8045c1c9711d09fb4ad0 Mon Sep 17 00:00:00 2001 From: Marijn Haverbeke Date: Mon, 18 Feb 2013 16:08:30 +0100 Subject: [PATCH 0747/5780] [lint addon] Pass code instead of editor instance to lint providers --- addon/lint/javascript-lint.js | 4 ++-- addon/lint/json-lint.js | 4 ++-- addon/lint/lint.js | 2 +- 3 files changed, 5 insertions(+), 5 deletions(-) diff --git a/addon/lint/javascript-lint.js b/addon/lint/javascript-lint.js index 2200bba3e4..0d8ec32ed0 100644 --- a/addon/lint/javascript-lint.js +++ b/addon/lint/javascript-lint.js @@ -9,8 +9,8 @@ "Unmatched ", " and instead saw", " is not defined", "Unclosed string", "Stopping, unable to continue" ]; - CodeMirror.javascriptValidator = function(cm) { - JSHINT(cm.getValue()); + CodeMirror.javascriptValidator = function(text) { + JSHINT(text); var errors = JSHINT.data().errors, result = []; if (errors) parseErrors(errors, result); return result; diff --git a/addon/lint/json-lint.js b/addon/lint/json-lint.js index bbab508e98..42b36abb39 100644 --- a/addon/lint/json-lint.js +++ b/addon/lint/json-lint.js @@ -1,6 +1,6 @@ // Depends on jsonlint.js from https://github.com/zaach/jsonlint -CodeMirror.jsonValidator = function(cm) { +CodeMirror.jsonValidator = function(text) { var found = []; jsonlint.parseError = function(str, hash) { var loc = hash.loc; @@ -8,7 +8,7 @@ CodeMirror.jsonValidator = function(cm) { to: CodeMirror.Pos(loc.last_line - 1, loc.last_column), message: str}); }; - try { jsonlint.parse(cm.getValue()); } + try { jsonlint.parse(text); } catch(e) {} return found; }; diff --git a/addon/lint/lint.js b/addon/lint/lint.js index d59478aab0..39aee495d6 100644 --- a/addon/lint/lint.js +++ b/addon/lint/lint.js @@ -92,7 +92,7 @@ CodeMirror.validate = (function() { clearMarks(cm); var state = cm._lintState, options = state.options; - var annotations = groupByLine(options.getAnnotations(cm)); + var annotations = groupByLine(options.getAnnotations(cm.getValue())); for (var line = 0; line < annotations.length; ++line) { var anns = annotations[line]; From a3d7282704e9adda2e317291a8067211f553d9c7 Mon Sep 17 00:00:00 2001 From: Marijn Haverbeke Date: Tue, 19 Feb 2013 09:53:43 +0100 Subject: [PATCH 0748/5780] [real-world uses] Update Farabi's description --- doc/realworld.html | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/doc/realworld.html b/doc/realworld.html index 118ce10b6a..6388a4034e 100644 --- a/doc/realworld.html +++ b/doc/realworld.html @@ -43,7 +43,7 @@

    { } CodeMi
  • Elm language examples
  • Eloquent JavaScript (book)
  • Fastfig (online computation/math tool)
  • -
  • Farabi (Perl editor)
  • +
  • Farabi (modern Perl IDE)
  • FathomJS integration (slides with editors, again)
  • Go language tour
  • GitHub's Android app
  • From 017805885ede3df22140ad270679201a4cc0d5c0 Mon Sep 17 00:00:00 2001 From: Marijn Haverbeke Date: Tue, 19 Feb 2013 12:38:12 +0100 Subject: [PATCH 0749/5780] [javascript mode] Add 'property' style to string/number propnames in object literals --- mode/javascript/javascript.js | 1 + 1 file changed, 1 insertion(+) diff --git a/mode/javascript/javascript.js b/mode/javascript/javascript.js index c734555047..d41536f77a 100644 --- a/mode/javascript/javascript.js +++ b/mode/javascript/javascript.js @@ -302,6 +302,7 @@ CodeMirror.defineMode("javascript", function(config, parserConfig) { } function objprop(type) { if (type == "variable") cx.marked = "property"; + else if (type == "number" || type == "string") cx.marked = type + " property"; if (atomicTypes.hasOwnProperty(type)) return cont(expect(":"), expression); } function commasep(what, end) { From 21c2450d0b85eec83e52788e73ce5b3e84a626ed Mon Sep 17 00:00:00 2001 From: Marijn Haverbeke Date: Wed, 20 Feb 2013 13:50:02 +0100 Subject: [PATCH 0750/5780] Fix unclosed tag on front page Closes #1265 --- index.html | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/index.html b/index.html index 3e9725083e..073e577ba2 100644 --- a/index.html +++ b/index.html @@ -95,7 +95,7 @@

    Usage demos:

  • Code folding
  • Bi-directional text
  • Line widgets (via JSHint)
  • -
  • Split view
  • +
  • Split view
  • Mode overlays
  • Mode multiplexer
  • HTML editor with preview
  • From 0560bdf780638ac956bf9a6cf8af49648861bfb0 Mon Sep 17 00:00:00 2001 From: Marijn Haverbeke Date: Wed, 20 Feb 2013 13:59:17 +0100 Subject: [PATCH 0751/5780] [show-hint addon] Make hint window behave halfway proper when scrolling the editor Closes #1264 --- addon/hint/show-hint.js | 26 ++++++++++++++++++++------ 1 file changed, 20 insertions(+), 6 deletions(-) diff --git a/addon/hint/show-hint.js b/addon/hint/show-hint.js index ccc062b2d7..1d581e5291 100644 --- a/addon/hint/show-hint.js +++ b/addon/hint/show-hint.js @@ -31,8 +31,9 @@ CodeMirror.showHint = function(cm, getHints, options) { elt.hintId = i; } var pos = cm.cursorCoords(options.alignWithWord !== false ? data.from : null); - hints.style.left = pos.left + "px"; - hints.style.top = pos.bottom + "px"; + var left = pos.left, top = pos.bottom; + hints.style.left = left + "px"; + hints.style.top = top + "px"; document.body.appendChild(hints); // If we're at the edge of the screen, then we want the menu to appear on the left of the cursor. @@ -45,7 +46,7 @@ CodeMirror.showHint = function(cm, getHints, options) { hints.style.width = (winW - 5) + "px"; overlapX -= (box.right - box.left) - winW; } - hints.style.left = (pos.left - overlapX) + "px"; + hints.style.left = (left = pos.left - overlapX) + "px"; } if (overlapY > 0) { var height = box.bottom - box.top; @@ -55,7 +56,7 @@ CodeMirror.showHint = function(cm, getHints, options) { hints.style.height = (winH - 5) + "px"; overlapY -= height - winH; } - hints.style.top = (pos.bottom - overlapY) + "px"; + hints.style.top = (top = pos.bottom - overlapY) + "px"; } function changeActive(i) { @@ -93,7 +94,18 @@ CodeMirror.showHint = function(cm, getHints, options) { cm.addKeyMap(ourMap); cm.on("cursorActivity", cursorActivity); - cm.on("blur", close); + var closingOnBlur; + function onBlur(){ closingOnBlur = setTimeout(close, 100); }; + function onFocus(){ clearTimeout(closingOnBlur); }; + cm.on("blur", onBlur); + cm.on("focus", onFocus); + var startScroll = cm.getScrollInfo(); + function onScroll() { + var curScroll = cm.getScrollInfo(); + hints.style.top = (top + startScroll.top - curScroll.top) + "px"; + hints.style.left = (left + startScroll.left - curScroll.left) + "px"; + } + cm.on("scroll", onScroll); CodeMirror.on(hints, "dblclick", function(e) { var t = e.target || e.srcElement; if (t.hintId != null) {selectedHint = t.hintId; pick();} @@ -113,7 +125,9 @@ CodeMirror.showHint = function(cm, getHints, options) { hints.parentNode.removeChild(hints); cm.removeKeyMap(ourMap); cm.off("cursorActivity", cursorActivity); - cm.off("blur", close); + cm.off("blur", onBlur); + cm.off("focus", onFocus); + cm.off("scroll", onScroll); } function pick() { cm.replaceRange(completions[selectedHint], data.from, data.to); From 1eb6cc12decc227d8b8ea7767b14153986b816bd Mon Sep 17 00:00:00 2001 From: Marijn Haverbeke Date: Wed, 20 Feb 2013 17:19:44 +0100 Subject: [PATCH 0752/5780] Fix selection drawing in non-left-aligned text --- lib/codemirror.js | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/lib/codemirror.js b/lib/codemirror.js index 6a8ab07cfd..706f7980ed 100644 --- a/lib/codemirror.js +++ b/lib/codemirror.js @@ -930,7 +930,7 @@ window.CodeMirror = (function() { function paddingTop(display) {return display.lineSpace.offsetTop;} function paddingLeft(display) { - var e = removeChildrenAndAdd(display.measure, elt("pre")).appendChild(elt("span", "x")); + var e = removeChildrenAndAdd(display.measure, elt("pre", null, null, "text-align: left")).appendChild(elt("span", "x")); return e.offsetLeft; } From 16bd84ef120ba1632cc0b630d09f3ad4900cf501 Mon Sep 17 00:00:00 2001 From: Marijn Haverbeke Date: Wed, 20 Feb 2013 17:30:02 +0100 Subject: [PATCH 0753/5780] Add a getAllMarks method --- doc/manual.html | 2 ++ lib/codemirror.js | 9 +++++++++ test/test.js | 12 ++++++++++++ 3 files changed, 23 insertions(+) diff --git a/doc/manual.html b/doc/manual.html index 56f822bba1..0abaedabd9 100644 --- a/doc/manual.html +++ b/doc/manual.html @@ -1025,6 +1025,8 @@

    Text-marking methods

    doc.findMarksAt(pos) → array
    Returns an array of all the bookmarks and marked ranges present at the given position.
    +
    doc.getAllMarks() → array
    +
    Returns an array containing all marked ranges in the document.

    Widget, gutter, and decoration methods

    diff --git a/lib/codemirror.js b/lib/codemirror.js index 706f7980ed..fb589c9090 100644 --- a/lib/codemirror.js +++ b/lib/codemirror.js @@ -4411,6 +4411,15 @@ window.CodeMirror = (function() { } return markers; }, + getAllMarks: function() { + var markers = []; + this.iter(function(line) { + var sps = line.markedSpans; + if (sps) for (var i = 0; i < sps.length; ++i) + if (sps[i].from != null) markers.push(sps[i].marker); + }); + return markers; + }, posFromIndex: function(off) { var ch, lineNo = this.first; diff --git a/test/test.js b/test/test.js index 79e8a9bca1..11d5c3f5fb 100644 --- a/test/test.js +++ b/test/test.js @@ -477,6 +477,18 @@ testCM("bookmarkInsertLeft", function(cm) { eqPos(bl.find(), Pos(0, 1)); }, {value: "abcdef"}); +testCM("getAllMarks", function(cm) { + addDoc(cm, 10, 10); + var m1 = cm.setBookmark(Pos(0, 2)); + var m2 = cm.markText(Pos(0, 2), Pos(3, 2)); + var m3 = cm.markText(Pos(1, 2), Pos(1, 8)); + var m4 = cm.markText(Pos(8, 0), Pos(9, 0)); + eq(cm.getAllMarks().length, 4); + m1.clear(); + m3.clear(); + eq(cm.getAllMarks().length, 2); +}); + testCM("bug577", function(cm) { cm.setValue("a\nb"); cm.clearHistory(); From 25ad8f62cf8b4f6b6b9de04fe95385f9af05f971 Mon Sep 17 00:00:00 2001 From: Marijn Haverbeke Date: Wed, 20 Feb 2013 17:57:24 +0100 Subject: [PATCH 0754/5780] Make marker widgets inline blocks --- lib/codemirror.css | 4 ++++ 1 file changed, 4 insertions(+) diff --git a/lib/codemirror.css b/lib/codemirror.css index 6e34d15578..8de0b1908e 100644 --- a/lib/codemirror.css +++ b/lib/codemirror.css @@ -200,6 +200,10 @@ div.CodeMirror span.CodeMirror-nonmatchingbracket {color: #f22;} overflow: auto; } +.CodeMirror-widget { + display: inline-block; +} + .CodeMirror-wrap .CodeMirror-scroll { overflow-x: hidden; } From 33f92ebec2993f750d55ba5159bcf1fcc01eeacf Mon Sep 17 00:00:00 2001 From: Marijn Haverbeke Date: Wed, 20 Feb 2013 17:58:55 +0100 Subject: [PATCH 0755/5780] Add null/plain text mode to mode/meta.js Closes #1267 --- mode/meta.js | 1 + 1 file changed, 1 insertion(+) diff --git a/mode/meta.js b/mode/meta.js index d6296c332f..5d2ee0ffe9 100644 --- a/mode/meta.js +++ b/mode/meta.js @@ -38,6 +38,7 @@ CodeMirror.modeInfo = [ {name: 'PHP', mime: 'text/x-php', mode: 'php'}, {name: 'PHP(HTML)', mime: 'application/x-httpd-php', mode: 'php'}, {name: 'Pig', mime: 'text/x-pig', mode: 'pig'}, + {name: 'Plain Text', mime: 'text/plain', mode: 'null'}, {name: 'Properties files', mime: 'text/x-properties', mode: 'clike'}, {name: 'Python', mime: 'text/x-python', mode: 'python'}, {name: 'R', mime: 'text/x-rsrc', mode: 'r'}, From a65e98fbc80a23db584e23e323acfc5a5d934632 Mon Sep 17 00:00:00 2001 From: Marijn Haverbeke Date: Thu, 21 Feb 2013 12:26:06 +0100 Subject: [PATCH 0756/5780] Add beforeSelectionChange event Issue #1262 --- doc/manual.html | 17 +++++++++++++++++ lib/codemirror.js | 14 ++++++++++++++ test/test.js | 19 +++++++++++++++++++ 3 files changed, 50 insertions(+) diff --git a/doc/manual.html b/doc/manual.html index 0abaedabd9..3ef0456184 100644 --- a/doc/manual.html +++ b/doc/manual.html @@ -389,6 +389,18 @@

    Events

    Will be fired when the cursor or selection moves, or any change is made to the editor content.
    +
    "beforeSelectionChange" (instance, selection)
    +
    This event is fired before the selection is moved. Its + handler may modify the resulting selection head and anchor. + The selection parameter is an object + with head and anchor properties + holding {line, ch} objects, which the handler can + read and update. Handlers for this event have the same + restriction + as "beforeChange" + handlers — they should not do anything to directly update the + state of the editor.
    +
    "viewportChange" (instance, from, to)
    Fires whenever the view port of the editor changes (due to scrolling, editing, or any other @@ -437,6 +449,11 @@

    Events

    "cursorActivity" (doc)
    Fired whenever the cursor or selection in this document changes.
    + +
    "beforeSelectionChange" (doc, selection)
    +
    Equivalent to + the event by the same + name as fired on editor instances.

    Line handles (as returned by, for diff --git a/lib/codemirror.js b/lib/codemirror.js index fb589c9090..328b2c14d7 100644 --- a/lib/codemirror.js +++ b/lib/codemirror.js @@ -2309,10 +2309,24 @@ window.CodeMirror = (function() { if (doc.cm) doc.cm.curOp.userSelChange = true; } + function filterSelectionChange(doc, anchor, head) { + var obj = {anchor: anchor, head: head}; + signal(doc, "beforeSelectionChange", doc, obj); + if (doc.cm) signal(doc.cm, "beforeSelectionChange", doc.cm, obj); + obj.anchor = clipPos(doc, obj.anchor); obj.head = clipPos(doc, obj.head); + return obj; + } + // Update the selection. Last two args are only used by // updateDoc, since they have to be expressed in the line // numbers before the update. function setSelection(doc, anchor, head, bias, checkAtomic) { + if (!checkAtomic && hasHandler(doc, "beforeSelectionChange") || doc.cm && hasHandler(doc.cm, "beforeSelectionChange")) { + var filtered = filterSelectionChange(doc, anchor, head); + head = filtered.head; + anchor = filtered.anchor; + } + var sel = doc.sel; sel.goalColumn = null; // Skip over atomic spans. diff --git a/test/test.js b/test/test.js index 11d5c3f5fb..4dbbf2a758 100644 --- a/test/test.js +++ b/test/test.js @@ -1324,3 +1324,22 @@ testCM("beforeChange", function(cm) { cm.replaceRange("hey hey hey", Pos(1, 0), Pos(2, 0)); eq(cm.getValue(), "hello,_i_am_a\nhey_hey_hey"); }, {value: "abcdefghijk"}); + +testCM("beforeSelectionChange", function(cm) { + function notAtEnd(cm, pos) { + var len = cm.getLine(pos.line).length; + if (!len || pos.ch == len) return Pos(pos.line, pos.ch - 1); + return pos; + } + cm.on("beforeSelectionChange", function(cm, sel) { + sel.head = notAtEnd(cm, sel.head); + sel.anchor = notAtEnd(cm, sel.anchor); + }); + + addDoc(cm, 10, 10); + cm.execCommand("goLineEnd"); + eqPos(cm.getCursor(), Pos(0, 9)); + cm.execCommand("selectAll"); + eqPos(cm.getCursor("start"), Pos(0, 0)); + eqPos(cm.getCursor("end"), Pos(9, 9)); +}); \ No newline at end of file From c69ce2ee8ad52bb5e9ddab6f4b2be76466e39b1f Mon Sep 17 00:00:00 2001 From: Marijn Haverbeke Date: Thu, 21 Feb 2013 13:09:31 +0100 Subject: [PATCH 0757/5780] [htmlmixed mode] Make detection of opening script/style tags more robust Closes #1271 --- mode/htmlmixed/htmlmixed.js | 37 ++++++++++++++++++------------------- 1 file changed, 18 insertions(+), 19 deletions(-) diff --git a/mode/htmlmixed/htmlmixed.js b/mode/htmlmixed/htmlmixed.js index f8a0d5d18a..ec0c21d24a 100644 --- a/mode/htmlmixed/htmlmixed.js +++ b/mode/htmlmixed/htmlmixed.js @@ -13,29 +13,28 @@ CodeMirror.defineMode("htmlmixed", function(config, parserConfig) { mode: CodeMirror.getMode(config, "text/plain")}); function html(stream, state) { + var tagName = state.htmlState.tagName; var style = htmlMode.token(stream, state.htmlState); - if (/(?:^|\s)tag(?:\s|$)/.test(style) && stream.current() == ">" && state.htmlState.context) { - if (/^script$/i.test(state.htmlState.context.tagName)) { - // Script block: mode to change to depends on type attribute - var scriptType = stream.string.slice(Math.max(0, stream.pos - 100), stream.pos).match(/\btype\s*=\s*("[^"]+"|'[^']+'|\S+)[^<]*$/i); - scriptType = scriptType ? scriptType[1] : ""; - if (scriptType && /[\"\']/.test(scriptType.charAt(0))) scriptType = scriptType.slice(1, scriptType.length - 1); - for (var i = 0; i < scriptTypes.length; ++i) { - var tp = scriptTypes[i]; - if (typeof tp.matches == "string" ? scriptType == tp.matches : tp.matches.test(scriptType)) { - if (tp.mode) { - state.token = script; - state.localMode = tp.mode; - state.localState = tp.mode.startState && tp.mode.startState(htmlMode.indent(state.htmlState, "")); - } - break; + if (tagName == "script" && /\btag\b/.test(style) && stream.current() == ">") { + // Script block: mode to change to depends on type attribute + var scriptType = stream.string.slice(Math.max(0, stream.pos - 100), stream.pos).match(/\btype\s*=\s*("[^"]+"|'[^']+'|\S+)[^<]*$/i); + scriptType = scriptType ? scriptType[1] : ""; + if (scriptType && /[\"\']/.test(scriptType.charAt(0))) scriptType = scriptType.slice(1, scriptType.length - 1); + for (var i = 0; i < scriptTypes.length; ++i) { + var tp = scriptTypes[i]; + if (typeof tp.matches == "string" ? scriptType == tp.matches : tp.matches.test(scriptType)) { + if (tp.mode) { + state.token = script; + state.localMode = tp.mode; + state.localState = tp.mode.startState && tp.mode.startState(htmlMode.indent(state.htmlState, "")); } + break; } - } else if (/^style$/i.test(state.htmlState.context.tagName)) { - state.token = css; - state.localMode = cssMode; - state.localState = cssMode.startState(htmlMode.indent(state.htmlState, "")); } + } else if (tagName == "style" && /\btag\b/.test(style) && stream.current() == ">") { + state.token = css; + state.localMode = cssMode; + state.localState = cssMode.startState(htmlMode.indent(state.htmlState, "")); } return style; } From 7ce264fdfe78a70694b999c45d1b4a06df3469e8 Mon Sep 17 00:00:00 2001 From: Marijn Haverbeke Date: Thu, 21 Feb 2013 13:18:10 +0100 Subject: [PATCH 0758/5780] [show-hint addon] Close hinting popup when scrolled out of view --- addon/hint/show-hint.js | 10 +++++++--- 1 file changed, 7 insertions(+), 3 deletions(-) diff --git a/addon/hint/show-hint.js b/addon/hint/show-hint.js index 1d581e5291..2eb206d1fd 100644 --- a/addon/hint/show-hint.js +++ b/addon/hint/show-hint.js @@ -31,7 +31,7 @@ CodeMirror.showHint = function(cm, getHints, options) { elt.hintId = i; } var pos = cm.cursorCoords(options.alignWithWord !== false ? data.from : null); - var left = pos.left, top = pos.bottom; + var left = pos.left, top = pos.bottom, below = true; hints.style.left = left + "px"; hints.style.top = top + "px"; document.body.appendChild(hints); @@ -52,6 +52,7 @@ CodeMirror.showHint = function(cm, getHints, options) { var height = box.bottom - box.top; if (box.top - (pos.bottom - pos.top) - height > 0) { overlapY = height + (pos.bottom - pos.top); + below = false; } else if (height > winH) { hints.style.height = (winH - 5) + "px"; overlapY -= height - winH; @@ -101,8 +102,11 @@ CodeMirror.showHint = function(cm, getHints, options) { cm.on("focus", onFocus); var startScroll = cm.getScrollInfo(); function onScroll() { - var curScroll = cm.getScrollInfo(); - hints.style.top = (top + startScroll.top - curScroll.top) + "px"; + var curScroll = cm.getScrollInfo(), editor = cm.getWrapperElement().getBoundingClientRect(); + var newTop = top + startScroll.top - curScroll.top, point = newTop; + if (!below) point += hints.offsetHeight; + if (point <= editor.top || point >= editor.bottom) return close(); + hints.style.top = newTop + "px"; hints.style.left = (left + startScroll.left - curScroll.left) + "px"; } cm.on("scroll", onScroll); From 7db06ab9f635946f32977406597d6593e3b708e7 Mon Sep 17 00:00:00 2001 From: "angelo.zerr@gmail.com" Date: Thu, 21 Feb 2013 11:57:32 +0100 Subject: [PATCH 0759/5780] [lint addon] Add an option run an asynchronous linter --- addon/lint/lint.js | 16 ++++++++++++---- 1 file changed, 12 insertions(+), 4 deletions(-) diff --git a/addon/lint/lint.js b/addon/lint/lint.js index 39aee495d6..f8aa3365df 100644 --- a/addon/lint/lint.js +++ b/addon/lint/lint.js @@ -88,11 +88,19 @@ CodeMirror.validate = (function() { return tip; } - function updateLinting(cm) { + function startLinting(cm) { + var state = cm._lintState, options = state.options; + if (options.async) + options.getAnnotations(cm, updateLinting, options); + else + updateLinting(cm, options.getAnnotations(cm.getValue())); + } + + function updateLinting(cm, annotationsNotSorted) { clearMarks(cm); var state = cm._lintState, options = state.options; - var annotations = groupByLine(options.getAnnotations(cm.getValue())); + var annotations = groupByLine(annotationsNotSorted); for (var line = 0; line < annotations.length; ++line) { var anns = annotations[line]; @@ -124,7 +132,7 @@ CodeMirror.validate = (function() { function onChange(cm) { var state = cm._lintState; clearTimeout(state.timeout); - state.timeout = setTimeout(function(){updateLinting(cm);}, state.options.delay || 500); + state.timeout = setTimeout(function(){startLinting(cm);}, state.options.delay || 500); } function popupSpanTooltip(ann, e) { @@ -168,7 +176,7 @@ CodeMirror.validate = (function() { var state = cm._lintState = new LintState(cm, parseOptions(val), hasLintGutter); cm.on("change", onChange); CodeMirror.on(cm.getWrapperElement(), "mouseover", state.onMouseOver); - updateLinting(cm); + startLinting(cm); } }); })(); From 9e5599dd6bfae3ebed670cffbd523838a75264ed Mon Sep 17 00:00:00 2001 From: Marijn Haverbeke Date: Thu, 21 Feb 2013 14:54:55 +0100 Subject: [PATCH 0760/5780] Add a kludge to work around poor dimension measurements in bidi text in IE Closes #1129 --- lib/codemirror.js | 26 +++++++++++++++++++++----- test/test.js | 2 +- 2 files changed, 22 insertions(+), 6 deletions(-) diff --git a/lib/codemirror.js b/lib/codemirror.js index 328b2c14d7..3fb5ed9f73 100644 --- a/lib/codemirror.js +++ b/lib/codemirror.js @@ -1022,7 +1022,9 @@ window.CodeMirror = (function() { } } if (j == vranges.length) vranges.push(top, bot); - data[i] = {left: size.left - outer.left, right: size.right - outer.left, top: j}; + var right = size.right; + if (cur.measureRight) right = getRect(cur.measureRight).left; + data[i] = {left: size.left - outer.left, right: right - outer.left, top: j}; } for (var i = 0, cur; i < data.length; ++i) if (cur = data[i]) { var vr = cur.top; @@ -1085,10 +1087,9 @@ window.CodeMirror = (function() { if (part.from < ch && part.to > ch) return get(ch, rtl); var left = rtl ? part.to : part.from, right = rtl ? part.from : part.to; if (left == ch) { - // Opera and IE return bogus offsets and widths for edges - // where the direction flips, but only for the side with the - // lower level. So we try to use the side with the higher - // level. + // IE returns bogus offsets and widths for edges where the + // direction flips, but only for the side with the lower + // level. So we try to use the side with the higher level. if (i && part.level < (nb = order[i-1]).level) here = get(nb.level % 2 ? nb.from : nb.to - 1, true); else here = get(rtl && part.from != part.to ? ch - 1 : ch); if (rtl == linedir) main = here; else other = here; @@ -3971,6 +3972,21 @@ window.CodeMirror = (function() { if (!builder.pre.firstChild && !lineIsHidden(cm.doc, realLine)) builder.pre.appendChild(document.createTextNode("\u00a0")); + var order; + // Work around problem with the reported dimensions of single-char + // direction spans on IE (issue #1129). See also the comment in + // cursorCoords. + if (measure && ie && (order = getOrder(line))) { + var l = order.length - 1; + if (order[l].from == order[l].to) --l; + var last = order[l], prev = order[l - 1]; + if (last.from + 1 == last.to && prev && last.level < prev.level) { + var span = measure[builder.pos - 1]; + if (span) span.parentNode.insertBefore(span.measureRight = zeroWidthElement(cm.display.measure), + span.nextSibling); + } + } + return builder.pre; } diff --git a/test/test.js b/test/test.js index 4dbbf2a758..80770ef422 100644 --- a/test/test.js +++ b/test/test.js @@ -984,7 +984,7 @@ testCM("verticalMovementCommandsWrapping", function(cm) { testCM("rtlMovement", function(cm) { forEach(["خحج", "خحabcخحج", "abخحخحجcd", "abخde", "abخح2342خ1حج", "خ1ح2خح3حxج", - "خحcd", "1خحcd", "abcdeح1ج", "خمرحبها مها!"], function(line) { + "خحcd", "1خحcd", "abcdeح1ج", "خمرحبها مها!", "foobarر"], function(line) { var inv = line.charAt(0) == "خ"; cm.setValue(line + "\n"); cm.execCommand(inv ? "goLineEnd" : "goLineStart"); var cursor = byClassName(cm.getWrapperElement(), "CodeMirror-cursor")[0]; From 69fa5fd886cb6cc77ff5df40bc15c194996cc215 Mon Sep 17 00:00:00 2001 From: Marijn Haverbeke Date: Thu, 21 Feb 2013 15:09:38 +0100 Subject: [PATCH 0761/5780] Clean up documentation for addons, mention lint addon --- doc/manual.html | 63 +++++++++++++++++++++++-------------------------- 1 file changed, 30 insertions(+), 33 deletions(-) diff --git a/doc/manual.html b/doc/manual.html index 3ef0456184..0071d39a4f 100644 --- a/doc/manual.html +++ b/doc/manual.html @@ -1357,14 +1357,14 @@

    Add-ons

    functionality. In brief, they are:

    -
    dialog.js
    +
    dialog/dialog.js
    Provides a very simple way to query users for text input. Adds an openDialog method to CodeMirror instances, which can be called with an HTML fragment that provides the prompt (should include an input tag), and a callback function that is called when text has been entered. Depends on addon/dialog/dialog.css.
    -
    searchcursor.js
    +
    search/searchcursor.js
    Adds the getSearchCursor(query, start, caseFold) → cursor method to CodeMirror instances, which can be used to implement search/replace functionality. query @@ -1393,14 +1393,14 @@

    Add-ons

    replacement.

    - +
    Implements the search commands. CodeMirror has keys bound to these by default, but will not do anything with them unless an implementation is provided. Depends on searchcursor.js, and will make use of openDialog when available to make prompting for search queries less ugly.
    -
    matchbrackets.js
    +
    edit/matchbrackets.js
    Defines an option matchBrackets which, when set to true, causes matching brackets to be highlighted whenever the cursor is next to them. It also adds a @@ -1408,14 +1408,14 @@

    Add-ons

    once, and a method findMatchingBracket that can be used to run the bracket-finding algorithm that this uses internally.
    -
    closebrackets.js
    +
    edit/closebrackets.js
    Defines an option autoCloseBrackets that will auto-close brackets and quotes when typed. By default, it'll auto-close ()[]{}''"", but you can pass it a string similar to that (containing pairs of matching characters) to customize it. Demo here.
    -
    foldcode.js
    +
    fold/foldcode.js
    Helps with code folding. See the demo for an example. Call CodeMirror.newFoldFunction with a range-finder @@ -1431,12 +1431,12 @@

    Add-ons

    where indentation determines block structure (Python, Haskell), and CodeMirror.tagRangeFinder, for XML-style languages.
    -
    collapserange.js
    +
    fold/collapserange.js
    Another approach to folding. See demo. Allows the user to select a range to fold by clicking in the gutter.
    -
    runmode.js
    +
    runmode/runmode.js
    Can be used to run a CodeMirror mode over text without actually opening an editor instance. See the demo for an example. @@ -1445,7 +1445,7 @@

    Add-ons

    (without including all of CodeMirror) and for running under node.js.
    -
    overlay.js
    +
    mode/overlay.js
    Mode combinator that can be used to extend a mode with an 'overlay' — a secondary mode is run over the stream, along with the base mode, and can color specific pieces of text without @@ -1453,7 +1453,7 @@

    Add-ons

    Defines CodeMirror.overlayMode, which is used to create such a mode. See this demo for a detailed example.
    -
    multiplex.js
    +
    mode/multiplex.js
    Mode combinator that can be used to easily 'multiplex' between several modes. Defines CodeMirror.multiplexingMode which, when @@ -1472,7 +1472,7 @@

    Add-ons

    see the content between the delimiters. See this demo for an example.
    -
    show-hint.js
    +
    hint/show-hint.js
    Provides a framework for showing autocompletion hints. Defines CodeMirror.showHint, which takes a CodeMirror instance and a hinting function, and pops up a widget @@ -1482,44 +1482,41 @@

    Add-ons

    is an array of strings (the completions), and from and to give the start and end of the token that is being completed. Depends - on addon/hint/show-hint.css. - See the demo for an + on addon/hint/show-hint.css. See the other files in + the addon/hint for + hint sources for various languages. Check + out the demo for an example.
    -
    simple-hint.js
    -
    Defines CodeMirror.simpleHint, which is - compatible with showHint. Deprecated, - use showHint instead. Depends - on addon/hint/simple-hint.css.
    -
    javascript-hint.js
    -
    Defines CodeMirror.javascriptHint - and CodeMirror.coffeescriptHint, which are simple - hinting functions for the JavaScript and CoffeeScript - modes.
    -
    xml-hint.js
    -
    Defines CodeMirror.xmlHint, a hinting function - for XML (which requires a schema to be defined).
    -
    python-hint.js
    -
    Defines CodeMirror.pythonHint, a hinter for Python code.
    match-highlighter.js
    Adds a highlightSelectionMatches option that can be enabled to highlight all instances of a currently selected word. Demo here.
    -
    mark-selection.js
    +
    lint/lint.js
    +
    Defines an interface component for showing linting warnings, + with pluggable warning sources + (see json-lint.js + and javascript-lint.js + in the same directory). Defines a lintWith option + that can be set to a warning source (for + example CodeMirror.javascriptValidator). Depends + on addon/lint/lint.css. A demo can be + found here.
    +
    selection/mark-selection.js
    Causes the selected text to be marked with the CSS class CodeMirror-selectedtext when the styleSelectedText option is enabled. Useful to change the colour of the selection (in addition to the background), like in this demo.
    -
    active-line.js
    +
    selection/active-line.js
    Defines a styleActiveLine option that, when enabled, gives the wrapper of the active line the class CodeMirror-activeline, and adds a background with the class CodeMirror-activeline-background. is enabled. See the demo.
    -
    closetag.js
    +
    edit/closetag.js
    Provides utility functions for adding automatic tag closing to XML modes. See the demo.
    -
    loadmode.js
    +
    mode/loadmode.js
    Defines a CodeMirror.requireMode(modename, callback) function that will try to load a given mode and call the callback when it succeeded. You'll have to @@ -1531,7 +1528,7 @@

    Add-ons

    which will ensure the given mode is loaded and cause the given editor instance to refresh its mode when the loading succeeded. See the demo.
    -
    continuecomment.js
    +
    edit/continuecomment.js
    Adds a command called newlineAndIndentContinueComment that you can bind Enter to in order to have the editor prefix From 545aeb74f6800dcaf6acead1bfc14c4eef99c9eb Mon Sep 17 00:00:00 2001 From: Marijn Haverbeke Date: Thu, 21 Feb 2013 15:39:07 +0100 Subject: [PATCH 0762/5780] Mark release 3.1 --- doc/compress.html | 1 + doc/oldrelease.html | 37 ++++++++++++++++++++++ index.html | 76 ++++++++++++++++++++++----------------------- lib/codemirror.js | 2 +- package.json | 2 +- 5 files changed, 78 insertions(+), 40 deletions(-) diff --git a/doc/compress.html b/doc/compress.html index 521e6e865a..7f3bc7f7ab 100644 --- a/doc/compress.html +++ b/doc/compress.html @@ -30,6 +30,7 @@

    { } CodeMi

    Version: From d597e278cd4dade42095d82ebb0f2023c4bc8337 Mon Sep 17 00:00:00 2001 From: Marijn Haverbeke Date: Thu, 28 Feb 2013 09:42:06 +0100 Subject: [PATCH 0789/5780] Try to make page-up + page-down place the cursor back in its old place Closes #1291 --- lib/codemirror.js | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/lib/codemirror.js b/lib/codemirror.js index ac43d0b97f..30fbce9670 100644 --- a/lib/codemirror.js +++ b/lib/codemirror.js @@ -2591,7 +2591,7 @@ window.CodeMirror = (function() { var doc = cm.doc, x = pos.left, y; if (unit == "page") { var pageSize = Math.min(cm.display.wrapper.clientHeight, window.innerHeight || document.documentElement.clientHeight); - y = pos.top + dir * (pageSize - textHeight(cm.display)); + y = pos.top + dir * (pageSize - (dir < 0 ? 1.5 : .5) * textHeight(cm.display)); } else if (unit == "line") { y = dir > 0 ? pos.bottom + 3 : pos.top - 3; } From 9b17e93f7a71abdb569cceabd8d942d5ef6b7183 Mon Sep 17 00:00:00 2001 From: Marijn Haverbeke Date: Thu, 28 Feb 2013 09:56:59 +0100 Subject: [PATCH 0790/5780] Add leaveSubmitMethodAlone option to disable form submit hack --- lib/codemirror.js | 22 ++++++++++++---------- 1 file changed, 12 insertions(+), 10 deletions(-) diff --git a/lib/codemirror.js b/lib/codemirror.js index 30fbce9670..3137d231c4 100644 --- a/lib/codemirror.js +++ b/lib/codemirror.js @@ -3306,17 +3306,19 @@ window.CodeMirror = (function() { function save() {textarea.value = cm.getValue();} if (textarea.form) { - // Deplorable hack to make the submit method do the right thing. on(textarea.form, "submit", save); - var form = textarea.form, realSubmit = form.submit; - try { - var wrappedSubmit = form.submit = function() { - save(); - form.submit = realSubmit; - form.submit(); - form.submit = wrappedSubmit; - }; - } catch(e) {} + // Deplorable hack to make the submit method do the right thing. + if (!options.leaveSubmitMethodAlone) { + var form = textarea.form, realSubmit = form.submit; + try { + var wrappedSubmit = form.submit = function() { + save(); + form.submit = realSubmit; + form.submit(); + form.submit = wrappedSubmit; + }; + } catch(e) {} + } } textarea.style.display = "none"; From 1344052d63dafe5cdefc36206e5325ed21ebc988 Mon Sep 17 00:00:00 2001 From: Brandon Frohs Date: Thu, 28 Feb 2013 12:32:18 -0500 Subject: [PATCH 0791/5780] [gfm] Add support for task lists. Closes #1165. --- mode/gfm/gfm.js | 1 + mode/gfm/index.html | 5 +++++ mode/gfm/test.js | 28 ++++++++++++++++++++++++++++ mode/markdown/markdown.js | 26 ++++++++++++++++++++++++++ mode/markdown/test.js | 11 +++++++++++ 5 files changed, 71 insertions(+) diff --git a/mode/gfm/gfm.js b/mode/gfm/gfm.js index 1aec9f8c43..1179b53dc6 100644 --- a/mode/gfm/gfm.js +++ b/mode/gfm/gfm.js @@ -89,6 +89,7 @@ CodeMirror.defineMode("gfm", function(config) { CodeMirror.defineMIME("gfmBase", { name: "markdown", underscoresBreakWords: false, + taskLists: true, fencedCodeBlocks: true }); return CodeMirror.overlayMode(CodeMirror.getMode(config, "gfmBase"), gfmOverlay); diff --git a/mode/gfm/index.html b/mode/gfm/index.html index f413db19ef..826a96d2de 100644 --- a/mode/gfm/index.html +++ b/mode/gfm/index.html @@ -40,6 +40,11 @@

    CodeMirror: GFM mode

    } ``` +## Task Lists + +- [ ] Incomplete task list item +- [x] **Completed** task list item + ## A bit of GitHub spice * SHA: be6a8cc1c1ecfe9489fb51e4869af15a13fc2cd2 diff --git a/mode/gfm/test.js b/mode/gfm/test.js index de29f5889a..3ccaec5010 100644 --- a/mode/gfm/test.js +++ b/mode/gfm/test.js @@ -25,6 +25,34 @@ "[comment ```]", "bar"); + MT("taskListAsterisk", + "[variable-2 * []] foo]", // Invalid; must have space or x between [] + "[variable-2 * [ ]]bar]", // Invalid; must have space after ] + "[variable-2 * [x]]hello]", // Invalid; must have space after ] + "[variable-2 * ][meta [ ]]][variable-2 [world]]]", // Valid; tests reference style links + " [variable-3 * ][property [x]]][variable-3 foo]"); // Valid; can be nested + + MT("taskListPlus", + "[variable-2 + []] foo]", // Invalid; must have space or x between [] + "[variable-2 + [ ]]bar]", // Invalid; must have space after ] + "[variable-2 + [x]]hello]", // Invalid; must have space after ] + "[variable-2 + ][meta [ ]]][variable-2 [world]]]", // Valid; tests reference style links + " [variable-3 + ][property [x]]][variable-3 foo]"); // Valid; can be nested + + MT("taskListDash", + "[variable-2 - []] foo]", // Invalid; must have space or x between [] + "[variable-2 - [ ]]bar]", // Invalid; must have space after ] + "[variable-2 - [x]]hello]", // Invalid; must have space after ] + "[variable-2 - ][meta [ ]]][variable-2 [world]]]", // Valid; tests reference style links + " [variable-3 - ][property [x]]][variable-3 foo]"); // Valid; can be nested + + MT("taskListNumber", + "[variable-2 1. []] foo]", // Invalid; must have space or x between [] + "[variable-2 2. [ ]]bar]", // Invalid; must have space after ] + "[variable-2 3. [x]]hello]", // Invalid; must have space after ] + "[variable-2 4. ][meta [ ]]][variable-2 [world]]]", // Valid; tests reference style links + " [variable-3 1. ][property [x]]][variable-3 foo]"); // Valid; can be nested + MT("SHA", "foo [link be6a8cc1c1ecfe9489fb51e4869af15a13fc2cd2] bar"); diff --git a/mode/markdown/markdown.js b/mode/markdown/markdown.js index ba6d4d7fc4..2de56ca739 100644 --- a/mode/markdown/markdown.js +++ b/mode/markdown/markdown.js @@ -49,6 +49,9 @@ CodeMirror.defineMode("markdown", function(cmCfg, modeCfg) { // Turn on fenced code blocks? ("```" to start/end) if (modeCfg.fencedCodeBlocks === undefined) modeCfg.fencedCodeBlocks = false; + // Turn on task lists? ("- [ ] " and "- [x] ") + if (modeCfg.taskLists === undefined) modeCfg.taskLists = false; + var codeDepth = 0; var prevLineHasContent = false , thisLineHasContent = false; @@ -72,6 +75,7 @@ CodeMirror.defineMode("markdown", function(cmCfg, modeCfg) { var hrRE = /^([*\-=_])(?:\s*\1){2,}\s*$/ , ulRE = /^[*\-+]\s+/ , olRE = /^[0-9]+\.\s+/ + , taskListRE = /^\[(x| )\](?=\s)/ // Must follow ulRE or olRE , headerRE = /^(?:\={1,}|-{1,})$/ , textRE = /^[^!\[\]*_\\<>` "'(]+/; @@ -144,6 +148,9 @@ CodeMirror.defineMode("markdown", function(cmCfg, modeCfg) { state.indentation += 4; state.list = true; state.listDepth++; + if (modeCfg.taskLists && stream.match(taskListRE, false)) { + state.taskList = true; + } } else if (modeCfg.fencedCodeBlocks && stream.match(/^```([\w+#]*)/, true)) { // try switching mode state.localMode = getMode(RegExp.$1); @@ -187,6 +194,9 @@ CodeMirror.defineMode("markdown", function(cmCfg, modeCfg) { function getType(state) { var styles = []; + if (state.taskOpen) { return "meta"; } + if (state.taskClosed) { return "property"; } + if (state.strong) { styles.push(strong); } if (state.em) { styles.push(em); } @@ -227,6 +237,17 @@ CodeMirror.defineMode("markdown", function(cmCfg, modeCfg) { return getType(state); } + if (state.taskList) { + var taskOpen = stream.match(taskListRE, true)[1] !== "x"; + if (taskOpen) state.taskOpen = true; + else state.taskClosed = true; + state.taskList = false; + return getType(state); + } + + state.taskOpen = false; + state.taskClosed = false; + var ch = stream.next(); if (ch === '\\') { @@ -428,6 +449,7 @@ CodeMirror.defineMode("markdown", function(cmCfg, modeCfg) { em: false, strong: false, header: false, + taskList: false, list: false, listDepth: 0, quote: 0 @@ -451,6 +473,7 @@ CodeMirror.defineMode("markdown", function(cmCfg, modeCfg) { em: s.em, strong: s.strong, header: s.header, + taskList: s.taskList, list: s.list, listDepth: s.listDepth, quote: s.quote, @@ -474,6 +497,9 @@ CodeMirror.defineMode("markdown", function(cmCfg, modeCfg) { // Reset state.header state.header = false; + // Reset state.taskList + state.taskList = false; + // Reset state.code state.code = false; diff --git a/mode/markdown/test.js b/mode/markdown/test.js index e527354e45..835f6cf6db 100644 --- a/mode/markdown/test.js +++ b/mode/markdown/test.js @@ -610,4 +610,15 @@ MT("doubleEscapeHash", "\\\\# foo"); + + + // Tests to make sure GFM-specific things aren't getting through + + MT("taskList", + "[variable-2 * [ ]] bar]"); + + MT("fencedCodeBlocks", + "[comment ```]", + "foo", + "[comment ```]"); })(); From 66fee2cc00a81db9eff5cddf816475fa25090311 Mon Sep 17 00:00:00 2001 From: Brandon Frohs Date: Thu, 28 Feb 2013 13:02:20 -0500 Subject: [PATCH 0792/5780] [markdown] Fix list highlighting. Previously, lists following other content weren't always highlighted. --- mode/markdown/markdown.js | 25 +++++++++++++------------ mode/markdown/test.js | 12 ++++++++++++ 2 files changed, 25 insertions(+), 12 deletions(-) diff --git a/mode/markdown/markdown.js b/mode/markdown/markdown.js index 2de56ca739..be43082280 100644 --- a/mode/markdown/markdown.js +++ b/mode/markdown/markdown.js @@ -53,8 +53,6 @@ CodeMirror.defineMode("markdown", function(cmCfg, modeCfg) { if (modeCfg.taskLists === undefined) modeCfg.taskLists = false; var codeDepth = 0; - var prevLineHasContent = false - , thisLineHasContent = false; var header = 'header' , code = 'comment' @@ -105,6 +103,8 @@ CodeMirror.defineMode("markdown", function(cmCfg, modeCfg) { state.f = inlineNormal; state.block = blockNormal; } + // Mark this line as blank + state.thisLineHasContent = false; return null; } @@ -130,7 +130,7 @@ CodeMirror.defineMode("markdown", function(cmCfg, modeCfg) { return code; } else if (stream.eatSpace()) { return null; - } else if (stream.peek() === '#' || (prevLineHasContent && stream.match(headerRE)) ) { + } else if (stream.peek() === '#' || (state.prevLineHasContent && stream.match(headerRE)) ) { state.header = true; } else if (stream.eat('>')) { state.indentation++; @@ -144,7 +144,7 @@ CodeMirror.defineMode("markdown", function(cmCfg, modeCfg) { return switchInline(stream, state, footnoteLink); } else if (stream.match(hrRE, true)) { return hr; - } else if ((!prevLineHasContent || prevLineIsList) && (stream.match(ulRE, true) || stream.match(olRE, true))) { + } else if ((!state.prevLineHasContent || prevLineIsList) && (stream.match(ulRE, true) || stream.match(olRE, true))) { state.indentation += 4; state.list = true; state.listDepth++; @@ -432,11 +432,12 @@ CodeMirror.defineMode("markdown", function(cmCfg, modeCfg) { return { startState: function() { - prevLineHasContent = false; - thisLineHasContent = false; return { f: blockNormal, + prevLineHasContent: false, + thisLineHasContent: false, + block: blockNormal, htmlState: CodeMirror.startState(htmlMode), indentation: 0, @@ -460,6 +461,9 @@ CodeMirror.defineMode("markdown", function(cmCfg, modeCfg) { return { f: s.f, + prevLineHasContent: s.prevLineHasContent, + thisLineHasContent: s.thisLineHasContent, + block: s.block, htmlState: CodeMirror.copyState(htmlMode, s.htmlState), indentation: s.indentation, @@ -484,14 +488,11 @@ CodeMirror.defineMode("markdown", function(cmCfg, modeCfg) { token: function(stream, state) { if (stream.sol()) { if (stream.match(/^\s*$/, true)) { - prevLineHasContent = false; + state.prevLineHasContent = false; return blankLine(state); } else { - if(thisLineHasContent){ - prevLineHasContent = true; - thisLineHasContent = false; - } - thisLineHasContent = true; + state.prevLineHasContent = state.thisLineHasContent; + state.thisLineHasContent = true; } // Reset state.header diff --git a/mode/markdown/test.js b/mode/markdown/test.js index 835f6cf6db..cfc229bc3f 100644 --- a/mode/markdown/test.js +++ b/mode/markdown/test.js @@ -174,18 +174,30 @@ // Check list types MT("listAsterisk", + "foo", + "bar", + "", "[variable-2 * foo]", "[variable-2 * bar]"); MT("listPlus", + "foo", + "bar", + "", "[variable-2 + foo]", "[variable-2 + bar]"); MT("listDash", + "foo", + "bar", + "", "[variable-2 - foo]", "[variable-2 - bar]"); MT("listNumber", + "foo", + "bar", + "", "[variable-2 1. foo]", "[variable-2 2. bar]"); From b444f799e1db2eb0d38bdf82af1071e452fc1a22 Mon Sep 17 00:00:00 2001 From: Ford_Lawnmower Date: Wed, 27 Feb 2013 11:55:42 -0500 Subject: [PATCH 0793/5780] Mode tcl added. --- mode/tcl/index.html | 130 +++++++++++++++++++++++++++++++++++++++++++ mode/tcl/tcl.js | 131 ++++++++++++++++++++++++++++++++++++++++++++ 2 files changed, 261 insertions(+) create mode 100644 mode/tcl/index.html create mode 100644 mode/tcl/tcl.js diff --git a/mode/tcl/index.html b/mode/tcl/index.html new file mode 100644 index 0000000000..ea72331269 --- /dev/null +++ b/mode/tcl/index.html @@ -0,0 +1,130 @@ + + + + + CodeMirror: tcl mode + + + + + + + + +

    CodeMirror: tcl mode

    +
    + + +

    MIME types defined: text/tcl.

    + + + diff --git a/mode/tcl/tcl.js b/mode/tcl/tcl.js new file mode 100644 index 0000000000..4defa8d8c5 --- /dev/null +++ b/mode/tcl/tcl.js @@ -0,0 +1,131 @@ +//tcl mode by Ford_Lawnmower :: Based on Velocity mode by Steve O'Hara +CodeMirror.defineMode("tcl", function() { + function parseWords(str) { + var obj = {}, words = str.split(" "); + for (var i = 0; i < words.length; ++i) obj[words[i]] = true; + return obj; + } + var keywords = parseWords("Tcl safe after append array auto_execok auto_import auto_load " + + "auto_mkindex auto_mkindex_old auto_qualify auto_reset bgerror " + + "binary break catch cd close concat continue dde eof encoding error " + + "eval exec exit expr fblocked fconfigure fcopy file fileevent filename " + + "filename flush for foreach format gets glob global history http if " + + "incr info interp join lappend lindex linsert list llength load lrange " + + "lreplace lsearch lset lsort memory msgcat namespace open package parray " + + "pid pkg::create pkg_mkIndex proc puts pwd re_syntax read regex regexp " + + "registry regsub rename resource return scan seek set socket source split " + + "string subst switch tcl_endOfWord tcl_findLibrary tcl_startOfNextWord " + + "tcl_wordBreakAfter tcl_startOfPreviousWord tcl_wordBreakBefore tcltest " + + "tclvars tell time trace unknown unset update uplevel upvar variable " + + "vwait"); + var functions = parseWords("if elseif else and not or eq ne in ni for foreach while switch"); + var isOperatorChar = /[+\-*&%=<>!?^\/\|]/; + function chain(stream, state, f) { + state.tokenize = f; + return f(stream, state); + } + function tokenBase(stream, state) { + var beforeParams = state.beforeParams; + state.beforeParams = false; + var ch = stream.next(); + if ((ch == '"' || ch == "'") && state.inParams) + return chain(stream, state, tokenString(ch)); + else if (/[\[\]{}\(\),;\.]/.test(ch)) { + if (ch == "(" && beforeParams) state.inParams = true; + else if (ch == ")") state.inParams = false; + return null; + } + else if (/\d/.test(ch)) { + stream.eatWhile(/[\w\.]/); + return "number"; + } + else if (ch == "#" && stream.eat("*")) { + return chain(stream, state, tokenComment); + } + else if (ch == "#" && stream.match(/ *\[ *\[/)) { + return chain(stream, state, tokenUnparsed); + } + else if (ch == "#" && stream.eat("#")) { + stream.skipToEnd(); + return "comment"; + } + else if (ch == '"') { + stream.skipTo(/"/) + return "comment"; + } + else if (ch == "$") { + stream.eatWhile(/[$_a-z0-9A-Z\.{:]/); + stream.eatWhile(/}/) + state.beforeParams = true; + return "builtin"; + } + else if (isOperatorChar.test(ch)) { + stream.eatWhile(isOperatorChar); + return "comment"; + } + else { + stream.eatWhile(/[\w\$_{}]/); + var word = stream.current().toLowerCase(); + if (keywords && keywords.propertyIsEnumerable(word)) + return "keyword"; + if (functions && functions.propertyIsEnumerable(word)) { + state.beforeParams = true; + return "keyword"; + } + return null; + } + } + function tokenString(quote) { + return function(stream, state) { + var escaped = false, next, end = false; + while ((next = stream.next()) != null) { + if (next == quote && !escaped) { + end = true; + break; + } + escaped = !escaped && next == "\\"; + } + if (end) state.tokenize = tokenBase; + return "string"; + }; + } + function tokenComment(stream, state) { + var maybeEnd = false, ch; + while (ch = stream.next()) { + if (ch == "#" && maybeEnd) { + state.tokenize = tokenBase; + break; + } + maybeEnd = (ch == "*"); + } + return "comment"; + } + function tokenUnparsed(stream, state) { + var maybeEnd = 0, ch; + while (ch = stream.next()) { + if (ch == "#" && maybeEnd == 2) { + state.tokenize = tokenBase; + break; + } + if (ch == "]") + maybeEnd++; + else if (ch != " ") + maybeEnd = 0; + } + return "meta"; + } + return { + startState: function() { + return { + tokenize: tokenBase, + beforeParams: false, + inParams: false + }; + }, + token: function(stream, state) { + if (stream.eatSpace()) return null; + return state.tokenize(stream, state); + } + }; +}); +CodeMirror.defineMIME("text/tcl", "tcl"); From d5e9746db834fa2478dfa0d4a73ea8087e5e02d5 Mon Sep 17 00:00:00 2001 From: Ford_Lawnmower Date: Wed, 27 Feb 2013 12:29:06 -0500 Subject: [PATCH 0794/5780] Added mirc mode. --- mode/mirc/index.html | 149 +++++++++++++++++++++++++++++++++ mode/mirc/mirc.js | 191 +++++++++++++++++++++++++++++++++++++++++++ 2 files changed, 340 insertions(+) create mode 100644 mode/mirc/index.html create mode 100644 mode/mirc/mirc.js diff --git a/mode/mirc/index.html b/mode/mirc/index.html new file mode 100644 index 0000000000..8784374fe4 --- /dev/null +++ b/mode/mirc/index.html @@ -0,0 +1,149 @@ + + + + + CodeMirror: mIRC mode + + + + + + + + +

    CodeMirror: mIRC mode

    +
    + + +

    MIME types defined: text/mirc.

    + + + diff --git a/mode/mirc/mirc.js b/mode/mirc/mirc.js new file mode 100644 index 0000000000..632e8a3076 --- /dev/null +++ b/mode/mirc/mirc.js @@ -0,0 +1,191 @@ +//mIRC mode by Ford_Lawnmower :: Based on Velocity mode by Steve O'Hara +CodeMirror.defineMIME("text/mirc", "mirc"); +CodeMirror.defineMode("mirc", function() { + function parseWords(str) { + var obj = {}, words = str.split(" "); + for (var i = 0; i < words.length; ++i) obj[words[i]] = true; + return obj; + } + var specials = parseWords("$! $$ $& $? $+ $abook $abs $active $activecid " + + "$activewid $address $addtok $agent $agentname $agentstat $agentver " + + "$alias $and $anick $ansi2mirc $aop $appactive $appstate $asc $asctime " + + "$asin $atan $avoice $away $awaymsg $awaytime $banmask $base $bfind " + + "$binoff $biton $bnick $bvar $bytes $calc $cb $cd $ceil $chan $chanmodes " + + "$chantypes $chat $chr $cid $clevel $click $cmdbox $cmdline $cnick $color " + + "$com $comcall $comchan $comerr $compact $compress $comval $cos $count " + + "$cr $crc $creq $crlf $ctime $ctimer $ctrlenter $date $day $daylight " + + "$dbuh $dbuw $dccignore $dccport $dde $ddename $debug $decode $decompress " + + "$deltok $devent $dialog $did $didreg $didtok $didwm $disk $dlevel $dll " + + "$dllcall $dname $dns $duration $ebeeps $editbox $emailaddr $encode $error " + + "$eval $event $exist $feof $ferr $fgetc $file $filename $filtered $finddir " + + "$finddirn $findfile $findfilen $findtok $fline $floor $fopen $fread $fserve " + + "$fulladdress $fulldate $fullname $fullscreen $get $getdir $getdot $gettok $gmt " + + "$group $halted $hash $height $hfind $hget $highlight $hnick $hotline " + + "$hotlinepos $ial $ialchan $ibl $idle $iel $ifmatch $ignore $iif $iil " + + "$inelipse $ini $inmidi $inpaste $inpoly $input $inrect $inroundrect " + + "$insong $instok $int $inwave $ip $isalias $isbit $isdde $isdir $isfile " + + "$isid $islower $istok $isupper $keychar $keyrpt $keyval $knick $lactive " + + "$lactivecid $lactivewid $left $len $level $lf $line $lines $link $lock " + + "$lock $locked $log $logstamp $logstampfmt $longfn $longip $lower $ltimer " + + "$maddress $mask $matchkey $matchtok $md5 $me $menu $menubar $menucontext " + + "$menutype $mid $middir $mircdir $mircexe $mircini $mklogfn $mnick $mode " + + "$modefirst $modelast $modespl $mouse $msfile $network $newnick $nick $nofile " + + "$nopath $noqt $not $notags $notify $null $numeric $numok $oline $onpoly " + + "$opnick $or $ord $os $passivedcc $pic $play $pnick $port $portable $portfree " + + "$pos $prefix $prop $protect $puttok $qt $query $rand $r $rawmsg $read $readomo " + + "$readn $regex $regml $regsub $regsubex $remove $remtok $replace $replacex " + + "$reptok $result $rgb $right $round $scid $scon $script $scriptdir $scriptline " + + "$sdir $send $server $serverip $sfile $sha1 $shortfn $show $signal $sin " + + "$site $sline $snick $snicks $snotify $sock $sockbr $sockerr $sockname " + + "$sorttok $sound $sqrt $ssl $sreq $sslready $status $strip $str $stripped " + + "$syle $submenu $switchbar $tan $target $ticks $time $timer $timestamp " + + "$timestampfmt $timezone $tip $titlebar $toolbar $treebar $trust $ulevel " + + "$ulist $upper $uptime $url $usermode $v1 $v2 $var $vcmd $vcmdstat $vcmdver " + + "$version $vnick $vol $wid $width $wildsite $wildtok $window $wrap $xor"); + var keywords = parseWords("abook ajinvite alias aline ame amsg anick aop auser autojoin avoice " + + "away background ban bcopy beep bread break breplace bset btrunc bunset bwrite " + + "channel clear clearall cline clipboard close cnick color comclose comopen " + + "comreg continue copy creq ctcpreply ctcps dcc dccserver dde ddeserver " + + "debug dec describe dialog did didtok disable disconnect dlevel dline dll " + + "dns dqwindow drawcopy drawdot drawfill drawline drawpic drawrect drawreplace " + + "drawrot drawsave drawscroll drawtext ebeeps echo editbox emailaddr enable " + + "events exit fclose filter findtext finger firewall flash flist flood flush " + + "flushini font fopen fseek fsend fserve fullname fwrite ghide gload gmove " + + "gopts goto gplay gpoint gqreq groups gshow gsize gstop gtalk gunload hadd " + + "halt haltdef hdec hdel help hfree hinc hload hmake hop hsave ial ialclear " + + "ialmark identd if ignore iline inc invite iuser join kick linesep links list " + + "load loadbuf localinfo log mdi me menubar mkdir mnick mode msg nick noop notice " + + "notify omsg onotice part partall pdcc perform play playctrl pop protect pvoice " + + "qme qmsg query queryn quit raw reload remini remote remove rename renwin " + + "reseterror resetidle return rlevel rline rmdir run ruser save savebuf saveini " + + "say scid scon server set showmirc signam sline sockaccept sockclose socklist " + + "socklisten sockmark sockopen sockpause sockread sockrename sockudp sockwrite " + + "sound speak splay sreq strip switchbar timer timestamp titlebar tnick tokenize " + + "toolbar topic tray treebar ulist unload unset unsetall updatenl url uwho " + + "var vcadd vcmd vcrem vol while whois window winhelp write writeint if isalnum " + + "isalpha isaop isavoice isban ischan ishop isignore isin isincs isletter islower " + + "isnotify isnum ison isop isprotect isreg isupper isvoice iswm iswmcs " + + "elseif else goto menu nicklist status title icon size option text edit " + + "button check radio box scroll list combo link tab item"); + var functions = parseWords("if elseif else and not or eq ne in ni for foreach while switch"); + var isOperatorChar = /[+\-*&%=<>!?^\/\|]/; + function chain(stream, state, f) { + state.tokenize = f; + return f(stream, state); + } + function tokenBase(stream, state) { + var beforeParams = state.beforeParams; + state.beforeParams = false; + var ch = stream.next(); + if (/[\[\]{}\(\),\.]/.test(ch)) { + if (ch == "(" && beforeParams) state.inParams = true; + else if (ch == ")") state.inParams = false; + return null; + } + else if (/\d/.test(ch)) { + stream.eatWhile(/[\w\.]/); + return "number"; + } + else if (ch == "\\") { + stream.eat("\\"); + stream.eat(/./) + return "number" + } + else if (ch == "/" && stream.eat("*")) { + return chain(stream, state, tokenComment); + } + else if (ch == ";" && stream.match(/ *\( *\(/)) { + return chain(stream, state, tokenUnparsed); + } + else if (ch == ";" && !state.inParams) { + stream.skipToEnd(); + return "comment"; + } + else if (ch == '"') { + stream.eat(/"/); + return "keyword"; + } + else if (ch == "$") { + stream.eatWhile(/[$_a-z0-9A-Z\.:]/); + if (specials && specials.propertyIsEnumerable(stream.current().toLowerCase())) { + return "keyword" + } + else { + state.beforeParams = true; + return "builtin"; + } + } + else if (ch == "%") { + stream.eatWhile(/[^,^\s^\(^\)]/); + state.beforeParams = true; + return "string"; + } + else if (isOperatorChar.test(ch)) { + stream.eatWhile(isOperatorChar); + return "operator"; + } + else { + stream.eatWhile(/[\w\$_{}]/); + var word = stream.current().toLowerCase(); + if (keywords && keywords.propertyIsEnumerable(word)) + return "keyword"; + if (functions && functions.propertyIsEnumerable(word)) { + state.beforeParams = true; + return "keyword"; + } + return null; + } + } + function tokenString(quote) { + return function(stream, state) { + var escaped = false, next, end = false; + while ((next = stream.next()) != null) { + if (next == quote && !escaped) { + end = true; + break; + } + escaped = !escaped && next == "\\"; + } + if (end) state.tokenize = tokenBase; + return "string"; + }; + } + function tokenComment(stream, state) { + var maybeEnd = false, ch; + while (ch = stream.next()) { + if (ch == "/" && maybeEnd) { + state.tokenize = tokenBase; + break; + } + maybeEnd = (ch == "*"); + } + return "comment"; + } + function tokenUnparsed(stream, state) { + var maybeEnd = 0, ch; + while (ch = stream.next()) { + if (ch == ";" && maybeEnd == 2) { + state.tokenize = tokenBase; + break; + } + if (ch == ")") + maybeEnd++; + else if (ch != " ") + maybeEnd = 0; + } + return "meta"; + } + return { + startState: function() { + return { + tokenize: tokenBase, + beforeParams: false, + inParams: false + }; + }, + token: function(stream, state) { + if (stream.eatSpace()) return null; + return state.tokenize(stream, state); + } + }; +}); From 90092d10eb03609d1401f1efe87a36b571c35fb1 Mon Sep 17 00:00:00 2001 From: Marijn Haverbeke Date: Fri, 1 Mar 2013 09:27:24 +0100 Subject: [PATCH 0795/5780] [mirc mode] Integrate. --- doc/compress.html | 1 + doc/modes.html | 1 + mode/meta.js | 1 + mode/mirc/index.html | 24 ++-- mode/mirc/mirc.js | 324 +++++++++++++++++++++---------------------- 5 files changed, 170 insertions(+), 181 deletions(-) diff --git a/doc/compress.html b/doc/compress.html index 7f3bc7f7ab..d9e0843686 100644 --- a/doc/compress.html +++ b/doc/compress.html @@ -91,6 +91,7 @@

    { } CodeMi + diff --git a/doc/modes.html b/doc/modes.html index 7d273cbb75..642c7c1d7d 100644 --- a/doc/modes.html +++ b/doc/modes.html @@ -47,6 +47,7 @@

    { } CodeMi
  • LESS
  • Lua
  • Markdown (GitHub-flavour)
  • +
  • mIRC
  • NTriples
  • OCaml
  • Pascal
  • diff --git a/mode/meta.js b/mode/meta.js index 5d2ee0ffe9..99586bc30b 100644 --- a/mode/meta.js +++ b/mode/meta.js @@ -31,6 +31,7 @@ CodeMirror.modeInfo = [ {name: 'LESS', mime: 'text/x-less', mode: 'less'}, {name: 'Lua', mime: 'text/x-lua', mode: 'lua'}, {name: 'Markdown (GitHub-flavour)', mime: 'text/x-markdown', mode: 'markdown'}, + {name: 'mIRC', mime: 'text/mirc', mode: 'mirc'}, {name: 'NTriples', mime: 'text/n-triples', mode: 'ntriples'}, {name: 'OCaml', mime: 'text/x-ocaml', mode: 'ocaml'}, {name: 'Pascal', mime: 'text/x-pascal', mode: 'pascal'}, diff --git a/mode/mirc/index.html b/mode/mirc/index.html index 8784374fe4..0ff5ec9a20 100644 --- a/mode/mirc/index.html +++ b/mode/mirc/index.html @@ -5,7 +5,7 @@ CodeMirror: mIRC mode - + @@ -48,7 +48,7 @@

    CodeMirror: mIRC mode

    } else { hadd -m NickNames $2 $+(~,$1,~,$iif($3,$+($3,~))) - } + } } alias -l Fix.All.MindUser { var %Fix.Count = $hfind(NickNames,/[^~]+[0-9]{4}~/,0,r).data @@ -77,10 +77,10 @@

    CodeMirror: mIRC mode

    } menu status,channel { - - .AKA + .AKA ..AKA Search Dialog:dialog $iif($dialog(AKA_Search),-v,-m) AKA_Search AKA_Search - ..Clean All Records:Fix.All.Minduser - - + ..Clean All Records:Fix.All.Minduser + - } dialog AKA_Search { title "AKA Search Engine" @@ -105,7 +105,7 @@

    CodeMirror: mIRC mode

    did -a $dname 6 $hfind(NickNames,%search,%matches,w). [ $+ [ %type ] ] dec %matches } - did -c $dname 6 1 + did -c $dname 6 1 } elseif ($did == 7) && ($did($dname,6).seltext) { echo -ga $AKALogo 07 $mid($replace($hget(NickNames,$v1),$chr(126),$chr(44)),2,-1) } elseif ($did == 9) && ($did($dname,6).seltext) { clipboard $mid($v1,$pos($v1,*,1)) } @@ -114,20 +114,20 @@

    CodeMirror: mIRC mode

    if (!$hget(NickNames)) { hmake NickNames 10 } if ($isfile(NickNames.hsh)) { hload NickNames NickNames.hsh } } -On *:Exit: { if ($hget(NickNames)) { hsave NickNames NickNames.hsh } } +On *:Exit: { if ($hget(NickNames)) { hsave NickNames NickNames.hsh } } On *:Disconnect: { if ($hget(NickNames)) { hsave NickNames NickNames.hsh } } On *:Unload: { hfree NickNames } alias -l ialupdateCheck { inc -z $+(%,ialupdateCheck,$network) $calc($nick($1,0) / 4) - ;If your ial is already being updated on join .who $1 out. + ;If your ial is already being updated on join .who $1 out. ;If you are using /names to update ial you will still need this line. .who $1 } -Raw 352:*: { - if ($($+(%,ialupdateCheck,$network),2)) haltdef - NickNamesAdd $6 $+($network,$address($6,2)) +Raw 352:*: { + if ($($+(%,ialupdateCheck,$network),2)) haltdef + NickNamesAdd $6 $+($network,$address($6,2)) } -Raw 315:*: { +Raw 315:*: { if ($($+(%,ialupdateCheck,$network),2)) haltdef } diff --git a/mode/mirc/mirc.js b/mode/mirc/mirc.js index 632e8a3076..f76f782764 100644 --- a/mode/mirc/mirc.js +++ b/mode/mirc/mirc.js @@ -7,185 +7,171 @@ CodeMirror.defineMode("mirc", function() { return obj; } var specials = parseWords("$! $$ $& $? $+ $abook $abs $active $activecid " + - "$activewid $address $addtok $agent $agentname $agentstat $agentver " + - "$alias $and $anick $ansi2mirc $aop $appactive $appstate $asc $asctime " + - "$asin $atan $avoice $away $awaymsg $awaytime $banmask $base $bfind " + - "$binoff $biton $bnick $bvar $bytes $calc $cb $cd $ceil $chan $chanmodes " + - "$chantypes $chat $chr $cid $clevel $click $cmdbox $cmdline $cnick $color " + - "$com $comcall $comchan $comerr $compact $compress $comval $cos $count " + - "$cr $crc $creq $crlf $ctime $ctimer $ctrlenter $date $day $daylight " + - "$dbuh $dbuw $dccignore $dccport $dde $ddename $debug $decode $decompress " + - "$deltok $devent $dialog $did $didreg $didtok $didwm $disk $dlevel $dll " + - "$dllcall $dname $dns $duration $ebeeps $editbox $emailaddr $encode $error " + - "$eval $event $exist $feof $ferr $fgetc $file $filename $filtered $finddir " + - "$finddirn $findfile $findfilen $findtok $fline $floor $fopen $fread $fserve " + - "$fulladdress $fulldate $fullname $fullscreen $get $getdir $getdot $gettok $gmt " + - "$group $halted $hash $height $hfind $hget $highlight $hnick $hotline " + - "$hotlinepos $ial $ialchan $ibl $idle $iel $ifmatch $ignore $iif $iil " + - "$inelipse $ini $inmidi $inpaste $inpoly $input $inrect $inroundrect " + - "$insong $instok $int $inwave $ip $isalias $isbit $isdde $isdir $isfile " + - "$isid $islower $istok $isupper $keychar $keyrpt $keyval $knick $lactive " + - "$lactivecid $lactivewid $left $len $level $lf $line $lines $link $lock " + - "$lock $locked $log $logstamp $logstampfmt $longfn $longip $lower $ltimer " + - "$maddress $mask $matchkey $matchtok $md5 $me $menu $menubar $menucontext " + - "$menutype $mid $middir $mircdir $mircexe $mircini $mklogfn $mnick $mode " + - "$modefirst $modelast $modespl $mouse $msfile $network $newnick $nick $nofile " + - "$nopath $noqt $not $notags $notify $null $numeric $numok $oline $onpoly " + - "$opnick $or $ord $os $passivedcc $pic $play $pnick $port $portable $portfree " + - "$pos $prefix $prop $protect $puttok $qt $query $rand $r $rawmsg $read $readomo " + - "$readn $regex $regml $regsub $regsubex $remove $remtok $replace $replacex " + - "$reptok $result $rgb $right $round $scid $scon $script $scriptdir $scriptline " + - "$sdir $send $server $serverip $sfile $sha1 $shortfn $show $signal $sin " + - "$site $sline $snick $snicks $snotify $sock $sockbr $sockerr $sockname " + - "$sorttok $sound $sqrt $ssl $sreq $sslready $status $strip $str $stripped " + - "$syle $submenu $switchbar $tan $target $ticks $time $timer $timestamp " + - "$timestampfmt $timezone $tip $titlebar $toolbar $treebar $trust $ulevel " + - "$ulist $upper $uptime $url $usermode $v1 $v2 $var $vcmd $vcmdstat $vcmdver " + - "$version $vnick $vol $wid $width $wildsite $wildtok $window $wrap $xor"); + "$activewid $address $addtok $agent $agentname $agentstat $agentver " + + "$alias $and $anick $ansi2mirc $aop $appactive $appstate $asc $asctime " + + "$asin $atan $avoice $away $awaymsg $awaytime $banmask $base $bfind " + + "$binoff $biton $bnick $bvar $bytes $calc $cb $cd $ceil $chan $chanmodes " + + "$chantypes $chat $chr $cid $clevel $click $cmdbox $cmdline $cnick $color " + + "$com $comcall $comchan $comerr $compact $compress $comval $cos $count " + + "$cr $crc $creq $crlf $ctime $ctimer $ctrlenter $date $day $daylight " + + "$dbuh $dbuw $dccignore $dccport $dde $ddename $debug $decode $decompress " + + "$deltok $devent $dialog $did $didreg $didtok $didwm $disk $dlevel $dll " + + "$dllcall $dname $dns $duration $ebeeps $editbox $emailaddr $encode $error " + + "$eval $event $exist $feof $ferr $fgetc $file $filename $filtered $finddir " + + "$finddirn $findfile $findfilen $findtok $fline $floor $fopen $fread $fserve " + + "$fulladdress $fulldate $fullname $fullscreen $get $getdir $getdot $gettok $gmt " + + "$group $halted $hash $height $hfind $hget $highlight $hnick $hotline " + + "$hotlinepos $ial $ialchan $ibl $idle $iel $ifmatch $ignore $iif $iil " + + "$inelipse $ini $inmidi $inpaste $inpoly $input $inrect $inroundrect " + + "$insong $instok $int $inwave $ip $isalias $isbit $isdde $isdir $isfile " + + "$isid $islower $istok $isupper $keychar $keyrpt $keyval $knick $lactive " + + "$lactivecid $lactivewid $left $len $level $lf $line $lines $link $lock " + + "$lock $locked $log $logstamp $logstampfmt $longfn $longip $lower $ltimer " + + "$maddress $mask $matchkey $matchtok $md5 $me $menu $menubar $menucontext " + + "$menutype $mid $middir $mircdir $mircexe $mircini $mklogfn $mnick $mode " + + "$modefirst $modelast $modespl $mouse $msfile $network $newnick $nick $nofile " + + "$nopath $noqt $not $notags $notify $null $numeric $numok $oline $onpoly " + + "$opnick $or $ord $os $passivedcc $pic $play $pnick $port $portable $portfree " + + "$pos $prefix $prop $protect $puttok $qt $query $rand $r $rawmsg $read $readomo " + + "$readn $regex $regml $regsub $regsubex $remove $remtok $replace $replacex " + + "$reptok $result $rgb $right $round $scid $scon $script $scriptdir $scriptline " + + "$sdir $send $server $serverip $sfile $sha1 $shortfn $show $signal $sin " + + "$site $sline $snick $snicks $snotify $sock $sockbr $sockerr $sockname " + + "$sorttok $sound $sqrt $ssl $sreq $sslready $status $strip $str $stripped " + + "$syle $submenu $switchbar $tan $target $ticks $time $timer $timestamp " + + "$timestampfmt $timezone $tip $titlebar $toolbar $treebar $trust $ulevel " + + "$ulist $upper $uptime $url $usermode $v1 $v2 $var $vcmd $vcmdstat $vcmdver " + + "$version $vnick $vol $wid $width $wildsite $wildtok $window $wrap $xor"); var keywords = parseWords("abook ajinvite alias aline ame amsg anick aop auser autojoin avoice " + - "away background ban bcopy beep bread break breplace bset btrunc bunset bwrite " + - "channel clear clearall cline clipboard close cnick color comclose comopen " + - "comreg continue copy creq ctcpreply ctcps dcc dccserver dde ddeserver " + - "debug dec describe dialog did didtok disable disconnect dlevel dline dll " + - "dns dqwindow drawcopy drawdot drawfill drawline drawpic drawrect drawreplace " + - "drawrot drawsave drawscroll drawtext ebeeps echo editbox emailaddr enable " + - "events exit fclose filter findtext finger firewall flash flist flood flush " + - "flushini font fopen fseek fsend fserve fullname fwrite ghide gload gmove " + - "gopts goto gplay gpoint gqreq groups gshow gsize gstop gtalk gunload hadd " + - "halt haltdef hdec hdel help hfree hinc hload hmake hop hsave ial ialclear " + - "ialmark identd if ignore iline inc invite iuser join kick linesep links list " + - "load loadbuf localinfo log mdi me menubar mkdir mnick mode msg nick noop notice " + - "notify omsg onotice part partall pdcc perform play playctrl pop protect pvoice " + - "qme qmsg query queryn quit raw reload remini remote remove rename renwin " + - "reseterror resetidle return rlevel rline rmdir run ruser save savebuf saveini " + - "say scid scon server set showmirc signam sline sockaccept sockclose socklist " + - "socklisten sockmark sockopen sockpause sockread sockrename sockudp sockwrite " + - "sound speak splay sreq strip switchbar timer timestamp titlebar tnick tokenize " + - "toolbar topic tray treebar ulist unload unset unsetall updatenl url uwho " + - "var vcadd vcmd vcrem vol while whois window winhelp write writeint if isalnum " + - "isalpha isaop isavoice isban ischan ishop isignore isin isincs isletter islower " + - "isnotify isnum ison isop isprotect isreg isupper isvoice iswm iswmcs " + - "elseif else goto menu nicklist status title icon size option text edit " + - "button check radio box scroll list combo link tab item"); - var functions = parseWords("if elseif else and not or eq ne in ni for foreach while switch"); - var isOperatorChar = /[+\-*&%=<>!?^\/\|]/; - function chain(stream, state, f) { - state.tokenize = f; - return f(stream, state); + "away background ban bcopy beep bread break breplace bset btrunc bunset bwrite " + + "channel clear clearall cline clipboard close cnick color comclose comopen " + + "comreg continue copy creq ctcpreply ctcps dcc dccserver dde ddeserver " + + "debug dec describe dialog did didtok disable disconnect dlevel dline dll " + + "dns dqwindow drawcopy drawdot drawfill drawline drawpic drawrect drawreplace " + + "drawrot drawsave drawscroll drawtext ebeeps echo editbox emailaddr enable " + + "events exit fclose filter findtext finger firewall flash flist flood flush " + + "flushini font fopen fseek fsend fserve fullname fwrite ghide gload gmove " + + "gopts goto gplay gpoint gqreq groups gshow gsize gstop gtalk gunload hadd " + + "halt haltdef hdec hdel help hfree hinc hload hmake hop hsave ial ialclear " + + "ialmark identd if ignore iline inc invite iuser join kick linesep links list " + + "load loadbuf localinfo log mdi me menubar mkdir mnick mode msg nick noop notice " + + "notify omsg onotice part partall pdcc perform play playctrl pop protect pvoice " + + "qme qmsg query queryn quit raw reload remini remote remove rename renwin " + + "reseterror resetidle return rlevel rline rmdir run ruser save savebuf saveini " + + "say scid scon server set showmirc signam sline sockaccept sockclose socklist " + + "socklisten sockmark sockopen sockpause sockread sockrename sockudp sockwrite " + + "sound speak splay sreq strip switchbar timer timestamp titlebar tnick tokenize " + + "toolbar topic tray treebar ulist unload unset unsetall updatenl url uwho " + + "var vcadd vcmd vcrem vol while whois window winhelp write writeint if isalnum " + + "isalpha isaop isavoice isban ischan ishop isignore isin isincs isletter islower " + + "isnotify isnum ison isop isprotect isreg isupper isvoice iswm iswmcs " + + "elseif else goto menu nicklist status title icon size option text edit " + + "button check radio box scroll list combo link tab item"); + var functions = parseWords("if elseif else and not or eq ne in ni for foreach while switch"); + var isOperatorChar = /[+\-*&%=<>!?^\/\|]/; + function chain(stream, state, f) { + state.tokenize = f; + return f(stream, state); + } + function tokenBase(stream, state) { + var beforeParams = state.beforeParams; + state.beforeParams = false; + var ch = stream.next(); + if (/[\[\]{}\(\),\.]/.test(ch)) { + if (ch == "(" && beforeParams) state.inParams = true; + else if (ch == ")") state.inParams = false; + return null; } - function tokenBase(stream, state) { - var beforeParams = state.beforeParams; - state.beforeParams = false; - var ch = stream.next(); - if (/[\[\]{}\(\),\.]/.test(ch)) { - if (ch == "(" && beforeParams) state.inParams = true; - else if (ch == ")") state.inParams = false; - return null; - } - else if (/\d/.test(ch)) { - stream.eatWhile(/[\w\.]/); - return "number"; - } - else if (ch == "\\") { - stream.eat("\\"); - stream.eat(/./) - return "number" - } - else if (ch == "/" && stream.eat("*")) { - return chain(stream, state, tokenComment); - } - else if (ch == ";" && stream.match(/ *\( *\(/)) { - return chain(stream, state, tokenUnparsed); - } - else if (ch == ";" && !state.inParams) { - stream.skipToEnd(); - return "comment"; - } - else if (ch == '"') { - stream.eat(/"/); - return "keyword"; - } - else if (ch == "$") { - stream.eatWhile(/[$_a-z0-9A-Z\.:]/); - if (specials && specials.propertyIsEnumerable(stream.current().toLowerCase())) { - return "keyword" - } - else { - state.beforeParams = true; - return "builtin"; - } - } - else if (ch == "%") { - stream.eatWhile(/[^,^\s^\(^\)]/); - state.beforeParams = true; - return "string"; - } - else if (isOperatorChar.test(ch)) { - stream.eatWhile(isOperatorChar); - return "operator"; + else if (/\d/.test(ch)) { + stream.eatWhile(/[\w\.]/); + return "number"; + } + else if (ch == "\\") { + stream.eat("\\"); + stream.eat(/./); + return "number"; + } + else if (ch == "/" && stream.eat("*")) { + return chain(stream, state, tokenComment); + } + else if (ch == ";" && stream.match(/ *\( *\(/)) { + return chain(stream, state, tokenUnparsed); + } + else if (ch == ";" && !state.inParams) { + stream.skipToEnd(); + return "comment"; + } + else if (ch == '"') { + stream.eat(/"/); + return "keyword"; + } + else if (ch == "$") { + stream.eatWhile(/[$_a-z0-9A-Z\.:]/); + if (specials && specials.propertyIsEnumerable(stream.current().toLowerCase())) { + return "keyword"; } else { - stream.eatWhile(/[\w\$_{}]/); - var word = stream.current().toLowerCase(); - if (keywords && keywords.propertyIsEnumerable(word)) - return "keyword"; - if (functions && functions.propertyIsEnumerable(word)) { - state.beforeParams = true; - return "keyword"; - } - return null; + state.beforeParams = true; + return "builtin"; } } - function tokenString(quote) { - return function(stream, state) { - var escaped = false, next, end = false; - while ((next = stream.next()) != null) { - if (next == quote && !escaped) { - end = true; - break; - } - escaped = !escaped && next == "\\"; - } - if (end) state.tokenize = tokenBase; - return "string"; - }; + else if (ch == "%") { + stream.eatWhile(/[^,^\s^\(^\)]/); + state.beforeParams = true; + return "string"; + } + else if (isOperatorChar.test(ch)) { + stream.eatWhile(isOperatorChar); + return "operator"; } - function tokenComment(stream, state) { - var maybeEnd = false, ch; - while (ch = stream.next()) { - if (ch == "/" && maybeEnd) { - state.tokenize = tokenBase; - break; - } - maybeEnd = (ch == "*"); + else { + stream.eatWhile(/[\w\$_{}]/); + var word = stream.current().toLowerCase(); + if (keywords && keywords.propertyIsEnumerable(word)) + return "keyword"; + if (functions && functions.propertyIsEnumerable(word)) { + state.beforeParams = true; + return "keyword"; } - return "comment"; + return null; } - function tokenUnparsed(stream, state) { - var maybeEnd = 0, ch; - while (ch = stream.next()) { - if (ch == ";" && maybeEnd == 2) { - state.tokenize = tokenBase; - break; - } - if (ch == ")") - maybeEnd++; - else if (ch != " ") - maybeEnd = 0; + } + function tokenComment(stream, state) { + var maybeEnd = false, ch; + while (ch = stream.next()) { + if (ch == "/" && maybeEnd) { + state.tokenize = tokenBase; + break; } - return "meta"; + maybeEnd = (ch == "*"); } - return { - startState: function() { - return { - tokenize: tokenBase, - beforeParams: false, - inParams: false - }; - }, - token: function(stream, state) { - if (stream.eatSpace()) return null; - return state.tokenize(stream, state); + return "comment"; + } + function tokenUnparsed(stream, state) { + var maybeEnd = 0, ch; + while (ch = stream.next()) { + if (ch == ";" && maybeEnd == 2) { + state.tokenize = tokenBase; + break; } - }; + if (ch == ")") + maybeEnd++; + else if (ch != " ") + maybeEnd = 0; + } + return "meta"; + } + return { + startState: function() { + return { + tokenize: tokenBase, + beforeParams: false, + inParams: false + }; + }, + token: function(stream, state) { + if (stream.eatSpace()) return null; + return state.tokenize(stream, state); + } + }; }); From 6ff549aa5aae0b1598fa88e984ac793d33e5f69b Mon Sep 17 00:00:00 2001 From: Marijn Haverbeke Date: Fri, 1 Mar 2013 09:32:24 +0100 Subject: [PATCH 0796/5780] [tcl mode] Integrate --- doc/compress.html | 1 + doc/modes.html | 1 + index.html | 1 + mode/meta.js | 1 + mode/tcl/index.html | 17 ++++++++--------- mode/tcl/tcl.js | 32 ++++++++++++++++---------------- 6 files changed, 28 insertions(+), 25 deletions(-) diff --git a/doc/compress.html b/doc/compress.html index d9e0843686..a92f36396c 100644 --- a/doc/compress.html +++ b/doc/compress.html @@ -119,6 +119,7 @@

    { } CodeMi + diff --git a/doc/modes.html b/doc/modes.html index 642c7c1d7d..927ef27dca 100644 --- a/doc/modes.html +++ b/doc/modes.html @@ -72,6 +72,7 @@

    { } CodeMi
  • SQL (several dialects)
  • SPARQL
  • sTeX, LaTeX
  • +
  • Tcl
  • Tiddlywiki
  • Tiki wiki
  • Turtle
  • diff --git a/index.html b/index.html index a853e2ce11..59ebc87b70 100644 --- a/index.html +++ b/index.html @@ -75,6 +75,7 @@

    Supported modes:

  • SQL (several dialects)
  • SPARQL
  • sTeX, LaTeX
  • +
  • Tcl
  • VB.NET
  • VBScript
  • Velocity
  • diff --git a/mode/meta.js b/mode/meta.js index 99586bc30b..7ac3f181d0 100644 --- a/mode/meta.js +++ b/mode/meta.js @@ -59,6 +59,7 @@ CodeMirror.modeInfo = [ {name: 'PL/SQL', mime: 'text/x-plsql', mode: 'sql'}, {name: 'sTeX', mime: 'text/x-stex', mode: 'stex'}, {name: 'LaTeX', mime: 'text/x-latex', mode: 'stex'}, + {name: 'Tcl', mime: 'text/x-tcl', mode: 'tcl'}, {name: 'TiddlyWiki ', mime: 'text/x-tiddlywiki', mode: 'tiddlywiki'}, {name: 'Tiki wiki', mime: 'text/tiki', mode: 'tiki'}, {name: 'VB.NET', mime: 'text/x-vb', mode: 'vb'}, diff --git a/mode/tcl/index.html b/mode/tcl/index.html index ea72331269..e2e42a04be 100644 --- a/mode/tcl/index.html +++ b/mode/tcl/index.html @@ -2,16 +2,15 @@ - CodeMirror: tcl mode + CodeMirror: Tcl mode - -

    CodeMirror: tcl mode

    +

    CodeMirror: Tcl mode

    -

    MIME types defined: text/tcl.

    +

    MIME types defined: text/x-tcl.

    diff --git a/mode/tcl/tcl.js b/mode/tcl/tcl.js index 4defa8d8c5..58ca709e4c 100644 --- a/mode/tcl/tcl.js +++ b/mode/tcl/tcl.js @@ -6,17 +6,17 @@ CodeMirror.defineMode("tcl", function() { return obj; } var keywords = parseWords("Tcl safe after append array auto_execok auto_import auto_load " + - "auto_mkindex auto_mkindex_old auto_qualify auto_reset bgerror " + - "binary break catch cd close concat continue dde eof encoding error " + - "eval exec exit expr fblocked fconfigure fcopy file fileevent filename " + - "filename flush for foreach format gets glob global history http if " + - "incr info interp join lappend lindex linsert list llength load lrange " + - "lreplace lsearch lset lsort memory msgcat namespace open package parray " + - "pid pkg::create pkg_mkIndex proc puts pwd re_syntax read regex regexp " + - "registry regsub rename resource return scan seek set socket source split " + - "string subst switch tcl_endOfWord tcl_findLibrary tcl_startOfNextWord " + - "tcl_wordBreakAfter tcl_startOfPreviousWord tcl_wordBreakBefore tcltest " + - "tclvars tell time trace unknown unset update uplevel upvar variable " + + "auto_mkindex auto_mkindex_old auto_qualify auto_reset bgerror " + + "binary break catch cd close concat continue dde eof encoding error " + + "eval exec exit expr fblocked fconfigure fcopy file fileevent filename " + + "filename flush for foreach format gets glob global history http if " + + "incr info interp join lappend lindex linsert list llength load lrange " + + "lreplace lsearch lset lsort memory msgcat namespace open package parray " + + "pid pkg::create pkg_mkIndex proc puts pwd re_syntax read regex regexp " + + "registry regsub rename resource return scan seek set socket source split " + + "string subst switch tcl_endOfWord tcl_findLibrary tcl_startOfNextWord " + + "tcl_wordBreakAfter tcl_startOfPreviousWord tcl_wordBreakBefore tcltest " + + "tclvars tell time trace unknown unset update uplevel upvar variable " + "vwait"); var functions = parseWords("if elseif else and not or eq ne in ni for foreach while switch"); var isOperatorChar = /[+\-*&%=<>!?^\/\|]/; @@ -50,13 +50,13 @@ CodeMirror.defineMode("tcl", function() { return "comment"; } else if (ch == '"') { - stream.skipTo(/"/) - return "comment"; + stream.skipTo(/"/); + return "comment"; } else if (ch == "$") { stream.eatWhile(/[$_a-z0-9A-Z\.{:]/); - stream.eatWhile(/}/) - state.beforeParams = true; + stream.eatWhile(/}/); + state.beforeParams = true; return "builtin"; } else if (isOperatorChar.test(ch)) { @@ -128,4 +128,4 @@ CodeMirror.defineMode("tcl", function() { } }; }); -CodeMirror.defineMIME("text/tcl", "tcl"); +CodeMirror.defineMIME("text/x-tcl", "tcl"); From 73e32fea5729a9f461ebfccfa0d3f8a122bb8bb7 Mon Sep 17 00:00:00 2001 From: John Connor Date: Thu, 28 Feb 2013 19:59:47 -0500 Subject: [PATCH 0797/5780] Added delmarks command to vim key bindings. Functionality and error messages match the latest vim 7.0 release. --- keymap/vim.js | 64 ++++++++++++++++++++++++++++- test/vim_test.js | 104 +++++++++++++++++++++++++++++++++++++++++++++++ 2 files changed, 167 insertions(+), 1 deletion(-) diff --git a/keymap/vim.js b/keymap/vim.js index 38bfa1d482..57e90e09df 100644 --- a/keymap/vim.js +++ b/keymap/vim.js @@ -2236,7 +2236,8 @@ { name: 'undo', shortName: 'u', type: 'builtIn' }, { name: 'redo', shortName: 'red', type: 'builtIn' }, { name: 'substitute', shortName: 's', type: 'builtIn'}, - { name: 'nohlsearch', shortName: 'noh', type: 'builtIn'} + { name: 'nohlsearch', shortName: 'noh', type: 'builtIn'}, + { name: 'delmarks', shortName: 'delm', type: 'builtin'} ]; Vim.ExCommandDispatcher = function() { this.buildCommandMap_(); @@ -2533,6 +2534,67 @@ }, nohlsearch: function(cm) { clearSearchHighlight(cm); + }, + delmarks: function(cm, params) { + if (!params.argString || !params.argString.trim()) { + showConfirm(cm, 'Argument required'); + return; + } + + var state = getVimState(cm); + var stream = new CodeMirror.StringStream(params.argString.trim()); + while (!stream.eol()) { + stream.eatSpace(); + + // Record the streams position at the beginning of the loop for use + // in error messages. + var count = stream.pos; + + if (!stream.match(/[a-zA-Z]/, false)) { + showConfirm(cm, 'Invalid argument: ' + params.argString.substring(count)); + return; + } + + var sym = stream.next(); + // Check if this symbol is part of a range + if (stream.match('-', true)) { + // This symbol is part of a range. + + // The range must terminate at an alphabetic character. + if (!stream.match(/[a-zA-Z]/, false)) { + showConfirm(cm, 'Invalid argument: ' + params.argString.substring(count)); + return; + } + + var startMark = sym; + var finishMark = stream.next(); + // The range must terminate at an alphabetic character which + // shares the same case as the start of the range. + if (isLowerCase(startMark) && isLowerCase(finishMark) || + isUpperCase(startMark) && isUpperCase(finishMark)) { + var start = startMark.charCodeAt(0); + var finish = finishMark.charCodeAt(0); + if (start >= finish) { + showConfirm(cm, 'Invalid argument: ' + params.argString.substring(count)); + return; + } + + // Because marks are always ASCII values, and we have + // determined that they are the same case, we can use + // their char codes to iterate through the defined range. + for (var j = 0; j <= finish - start; j++) { + var mark = String.fromCharCode(start + j); + delete state.marks[mark]; + } + } else { + showConfirm(cm, 'Invalid argument: ' + startMark + "-"); + return; + } + } else { + // This symbol is a valid mark, and is not part of a range. + delete state.marks[sym]; + } + } } }; diff --git a/test/vim_test.js b/test/vim_test.js index b9827e1f07..00638b570d 100644 --- a/test/vim_test.js +++ b/test/vim_test.js @@ -812,6 +812,110 @@ testVim('mark', function(cm, vim, helpers) { helpers.doKeys('`', 't'); helpers.assertCursorAt(2, 2); }); +testVim('delmark_single', function(cm, vim, helpers) { + cm.setCursor(1, 2); + helpers.doKeys('m', 't'); + helpers.doEx('delmarks t'); + cm.setCursor(0, 0); + helpers.doKeys('`', 't'); + helpers.assertCursorAt(0, 0); +}); +testVim('delmark_range', function(cm, vim, helpers) { + cm.setCursor(1, 2); + helpers.doKeys('m', 'a'); + cm.setCursor(2, 2); + helpers.doKeys('m', 'b'); + cm.setCursor(3, 2); + helpers.doKeys('m', 'c'); + cm.setCursor(4, 2); + helpers.doKeys('m', 'd'); + cm.setCursor(5, 2); + helpers.doKeys('m', 'e'); + helpers.doEx('delmarks b-d'); + cm.setCursor(0, 0); + helpers.doKeys('`', 'a'); + helpers.assertCursorAt(1, 2); + helpers.doKeys('`', 'b'); + helpers.assertCursorAt(1, 2); + helpers.doKeys('`', 'c'); + helpers.assertCursorAt(1, 2); + helpers.doKeys('`', 'd'); + helpers.assertCursorAt(1, 2); + helpers.doKeys('`', 'e'); + helpers.assertCursorAt(5, 2); +}); +testVim('delmark_multi', function(cm, vim, helpers) { + cm.setCursor(1, 2); + helpers.doKeys('m', 'a'); + cm.setCursor(2, 2); + helpers.doKeys('m', 'b'); + cm.setCursor(3, 2); + helpers.doKeys('m', 'c'); + cm.setCursor(4, 2); + helpers.doKeys('m', 'd'); + cm.setCursor(5, 2); + helpers.doKeys('m', 'e'); + helpers.doEx('delmarks bcd'); + cm.setCursor(0, 0); + helpers.doKeys('`', 'a'); + helpers.assertCursorAt(1, 2); + helpers.doKeys('`', 'b'); + helpers.assertCursorAt(1, 2); + helpers.doKeys('`', 'c'); + helpers.assertCursorAt(1, 2); + helpers.doKeys('`', 'd'); + helpers.assertCursorAt(1, 2); + helpers.doKeys('`', 'e'); + helpers.assertCursorAt(5, 2); +}); +testVim('delmark_multi_space', function(cm, vim, helpers) { + cm.setCursor(1, 2); + helpers.doKeys('m', 'a'); + cm.setCursor(2, 2); + helpers.doKeys('m', 'b'); + cm.setCursor(3, 2); + helpers.doKeys('m', 'c'); + cm.setCursor(4, 2); + helpers.doKeys('m', 'd'); + cm.setCursor(5, 2); + helpers.doKeys('m', 'e'); + helpers.doEx('delmarks b c d'); + cm.setCursor(0, 0); + helpers.doKeys('`', 'a'); + helpers.assertCursorAt(1, 2); + helpers.doKeys('`', 'b'); + helpers.assertCursorAt(1, 2); + helpers.doKeys('`', 'c'); + helpers.assertCursorAt(1, 2); + helpers.doKeys('`', 'd'); + helpers.assertCursorAt(1, 2); + helpers.doKeys('`', 'e'); + helpers.assertCursorAt(5, 2); +}); +testVim('delmark_all', function(cm, vim, helpers) { + cm.setCursor(1, 2); + helpers.doKeys('m', 'a'); + cm.setCursor(2, 2); + helpers.doKeys('m', 'b'); + cm.setCursor(3, 2); + helpers.doKeys('m', 'c'); + cm.setCursor(4, 2); + helpers.doKeys('m', 'd'); + cm.setCursor(5, 2); + helpers.doKeys('m', 'e'); + helpers.doEx('delmarks a b-de'); + cm.setCursor(0, 0); + helpers.doKeys('`', 'a'); + helpers.assertCursorAt(0, 0); + helpers.doKeys('`', 'b'); + helpers.assertCursorAt(0, 0); + helpers.doKeys('`', 'c'); + helpers.assertCursorAt(0, 0); + helpers.doKeys('`', 'd'); + helpers.assertCursorAt(0, 0); + helpers.doKeys('`', 'e'); + helpers.assertCursorAt(0, 0); +}); testVim('visual', function(cm, vim, helpers) { helpers.doKeys('l', 'v', 'l', 'l'); helpers.assertCursorAt(0, 3); From a3ff09d80ad536f6ebd6b2b284158f9603bad57d Mon Sep 17 00:00:00 2001 From: David Pathakjee Date: Thu, 28 Feb 2013 22:44:12 -0600 Subject: [PATCH 0798/5780] Replaced builtins with updated list for 1.4. Generated by: (->> 'clojure.core ns-publics (map first) sort (clojure.string/join " ")) --- mode/clojure/clojure.js | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/mode/clojure/clojure.js b/mode/clojure/clojure.js index 5d90edb8a5..5dcb489d38 100644 --- a/mode/clojure/clojure.js +++ b/mode/clojure/clojure.js @@ -19,7 +19,7 @@ CodeMirror.defineMode("clojure", function () { "defn defn- def def- defonce defmulti defmethod defmacro defstruct deftype defprotocol defrecord defproject deftest slice defalias defhinted defmacro- defn-memo defnk defnk defonce- defunbound defunbound- defvar defvar- let letfn do case cond condp for loop recur when when-not when-let when-first if if-let if-not . .. -> ->> doto and or dosync doseq dotimes dorun doall load import unimport ns in-ns refer try catch finally throw with-open with-local-vars binding gen-class gen-and-load-class gen-and-save-class handler-case handle"); var builtins = makeKeywords( - "* *1 *2 *3 *agent* *allow-unresolved-vars* *assert *clojure-version* *command-line-args* *compile-files* *compile-path* *e *err* *file* *flush-on-newline* *in* *macro-meta* *math-context* *ns* *out* *print-dup* *print-length* *print-level* *print-meta* *print-readably* *read-eval* *source-path* *use-context-classloader* *warn-on-reflection* + - / < <= = == > >= accessor aclone agent agent-errors aget alength alias all-ns alter alter-meta! alter-var-root amap ancestors and apply areduce array-map aset aset-boolean aset-byte aset-char aset-double aset-float aset-int aset-long aset-short assert assoc assoc! assoc-in associative? atom await await-for await1 bases bean bigdec bigint binding bit-and bit-and-not bit-clear bit-flip bit-not bit-or bit-set bit-shift-left bit-shift-right bit-test bit-xor boolean boolean-array booleans bound-fn bound-fn* butlast byte byte-array bytes case cast char char-array char-escape-string char-name-string char? chars chunk chunk-append chunk-buffer chunk-cons chunk-first chunk-next chunk-rest chunked-seq? class class? clear-agent-errors clojure-version coll? comment commute comp comparator compare compare-and-set! compile complement concat cond condp conj conj! cons constantly construct-proxy contains? count counted? create-ns create-struct cycle dec decimal? declare definline defmacro defmethod defmulti defn defn- defonce defstruct delay delay? deliver deref derive descendants destructure disj disj! dissoc dissoc! distinct distinct? doall doc dorun doseq dosync dotimes doto double double-array doubles drop drop-last drop-while empty empty? ensure enumeration-seq eval even? every? extend extend-protocol extend-type extends? extenders false? ffirst file-seq filter find find-doc find-ns find-var first float float-array float? floats flush fn fn? fnext for force format future future-call future-cancel future-cancelled? future-done? future? gen-class gen-interface gensym get get-in get-method get-proxy-class get-thread-bindings get-validator hash hash-map hash-set identical? identity if-let if-not ifn? import in-ns inc init-proxy instance? int int-array integer? interleave intern interpose into into-array ints io! isa? iterate iterator-seq juxt key keys keyword keyword? last lazy-cat lazy-seq let letfn line-seq list list* list? load load-file load-reader load-string loaded-libs locking long long-array longs loop macroexpand macroexpand-1 make-array make-hierarchy map map? mapcat max max-key memfn memoize merge merge-with meta method-sig methods min min-key mod name namespace neg? newline next nfirst nil? nnext not not-any? not-empty not-every? not= ns ns-aliases ns-imports ns-interns ns-map ns-name ns-publics ns-refers ns-resolve ns-unalias ns-unmap nth nthnext num number? odd? or parents partial partition pcalls peek persistent! pmap pop pop! pop-thread-bindings pos? pr pr-str prefer-method prefers primitives-classnames print print-ctor print-doc print-dup print-method print-namespace-doc print-simple print-special-doc print-str printf println println-str prn prn-str promise proxy proxy-call-with-super proxy-mappings proxy-name proxy-super push-thread-bindings pvalues quot rand rand-int range ratio? rational? rationalize re-find re-groups re-matcher re-matches re-pattern re-seq read read-line read-string reify reduce ref ref-history-count ref-max-history ref-min-history ref-set refer refer-clojure release-pending-sends rem remove remove-method remove-ns repeat repeatedly replace replicate require reset! reset-meta! resolve rest resultset-seq reverse reversible? rseq rsubseq satisfies? second select-keys send send-off seq seq? seque sequence sequential? set set-validator! set? short short-array shorts shutdown-agents slurp some sort sort-by sorted-map sorted-map-by sorted-set sorted-set-by sorted? special-form-anchor special-symbol? split-at split-with str stream? string? struct struct-map subs subseq subvec supers swap! symbol symbol? sync syntax-symbol-anchor take take-last take-nth take-while test the-ns time to-array to-array-2d trampoline transient tree-seq true? type unchecked-add unchecked-dec unchecked-divide unchecked-inc unchecked-multiply unchecked-negate unchecked-remainder unchecked-subtract underive unquote unquote-splicing update-in update-proxy use val vals var-get var-set var? vary-meta vec vector vector? when when-first when-let when-not while with-bindings with-bindings* with-in-str with-loading-context with-local-vars with-meta with-open with-out-str with-precision xml-seq"); + "* *' *1 *2 *3 *agent* *allow-unresolved-vars* *assert* *clojure-version* *command-line-args* *compile-files* *compile-path* *compiler-options* *data-readers* *e *err* *file* *flush-on-newline* *fn-loader* *in* *math-context* *ns* *out* *print-dup* *print-length* *print-level* *print-meta* *print-readably* *read-eval* *source-path* *unchecked-math* *use-context-classloader* *verbose-defrecords* *warn-on-reflection* + +' - -' -> ->> ->ArrayChunk ->Vec ->VecNode ->VecSeq -cache-protocol-fn -reset-methods .. / < <= = == > >= EMPTY-NODE accessor aclone add-classpath add-watch agent agent-error agent-errors aget alength alias all-ns alter alter-meta! alter-var-root amap ancestors and apply areduce array-map aset aset-boolean aset-byte aset-char aset-double aset-float aset-int aset-long aset-short assert assoc assoc! assoc-in associative? atom await await-for await1 bases bean bigdec bigint biginteger binding bit-and bit-and-not bit-clear bit-flip bit-not bit-or bit-set bit-shift-left bit-shift-right bit-test bit-xor boolean boolean-array booleans bound-fn bound-fn* bound? butlast byte byte-array bytes case cast char char-array char-escape-string char-name-string char? chars chunk chunk-append chunk-buffer chunk-cons chunk-first chunk-next chunk-rest chunked-seq? class class? clear-agent-errors clojure-version coll? comment commute comp comparator compare compare-and-set! compile complement concat cond condp conj conj! cons constantly construct-proxy contains? count counted? create-ns create-struct cycle dec dec' decimal? declare default-data-readers definline definterface defmacro defmethod defmulti defn defn- defonce defprotocol defrecord defstruct deftype delay delay? deliver denominator deref derive descendants destructure disj disj! dissoc dissoc! distinct distinct? doall dorun doseq dosync dotimes doto double double-array doubles drop drop-last drop-while empty empty? ensure enumeration-seq error-handler error-mode eval even? every-pred every? ex-data ex-info extend extend-protocol extend-type extenders extends? false? ffirst file-seq filter filterv find find-keyword find-ns find-protocol-impl find-protocol-method find-var first flatten float float-array float? floats flush fn fn? fnext fnil for force format frequencies future future-call future-cancel future-cancelled? future-done? future? gen-class gen-interface gensym get get-in get-method get-proxy-class get-thread-bindings get-validator group-by hash hash-combine hash-map hash-set identical? identity if-let if-not ifn? import in-ns inc inc' init-proxy instance? int int-array integer? interleave intern interpose into into-array ints io! isa? iterate iterator-seq juxt keep keep-indexed key keys keyword keyword? last lazy-cat lazy-seq let letfn line-seq list list* list? load load-file load-reader load-string loaded-libs locking long long-array longs loop macroexpand macroexpand-1 make-array make-hierarchy map map-indexed map? mapcat mapv max max-key memfn memoize merge merge-with meta method-sig methods min min-key mod munge name namespace namespace-munge neg? newline next nfirst nil? nnext not not-any? not-empty not-every? not= ns ns-aliases ns-imports ns-interns ns-map ns-name ns-publics ns-refers ns-resolve ns-unalias ns-unmap nth nthnext nthrest num number? numerator object-array odd? or parents partial partition partition-all partition-by pcalls peek persistent! pmap pop pop! pop-thread-bindings pos? pr pr-str prefer-method prefers primitives-classnames print print-ctor print-dup print-method print-simple print-str printf println println-str prn prn-str promise proxy proxy-call-with-super proxy-mappings proxy-name proxy-super push-thread-bindings pvalues quot rand rand-int rand-nth range ratio? rational? rationalize re-find re-groups re-matcher re-matches re-pattern re-seq read read-line read-string realized? reduce reduce-kv reductions ref ref-history-count ref-max-history ref-min-history ref-set refer refer-clojure reify release-pending-sends rem remove remove-all-methods remove-method remove-ns remove-watch repeat repeatedly replace replicate require reset! reset-meta! resolve rest restart-agent resultset-seq reverse reversible? rseq rsubseq satisfies? second select-keys send send-off seq seq? seque sequence sequential? set set-error-handler! set-error-mode! set-validator! set? short short-array shorts shuffle shutdown-agents slurp some some-fn sort sort-by sorted-map sorted-map-by sorted-set sorted-set-by sorted? special-symbol? spit split-at split-with str string? struct struct-map subs subseq subvec supers swap! symbol symbol? sync take take-last take-nth take-while test the-ns thread-bound? time to-array to-array-2d trampoline transient tree-seq true? type unchecked-add unchecked-add-int unchecked-byte unchecked-char unchecked-dec unchecked-dec-int unchecked-divide-int unchecked-double unchecked-float unchecked-inc unchecked-inc-int unchecked-int unchecked-long unchecked-multiply unchecked-multiply-int unchecked-negate unchecked-negate-int unchecked-remainder-int unchecked-short unchecked-subtract unchecked-subtract-int underive unquote unquote-splicing update-in update-proxy use val vals var-get var-set var? vary-meta vec vector vector-of vector? when when-first when-let when-not while with-bindings with-bindings* with-in-str with-loading-context with-local-vars with-meta with-open with-out-str with-precision with-redefs with-redefs-fn xml-seq zero? zipmap"); var indentKeys = makeKeywords( // Built-ins From 3e3f3939c8992af4be7f6009eb2646652ff2ab7c Mon Sep 17 00:00:00 2001 From: Joseph Pecoraro Date: Fri, 1 Mar 2013 12:04:24 -0800 Subject: [PATCH 0799/5780] Improve performance of StringStream with long lines Fixes Issue #1301 StringStreams.prototype.column was always calculating the column offset for a position by walking from index 0 in the string to the current start position. That is factorial in performance and gets noticeably slower with long lines. StringStreams only ever advance forward. So when determining the column we can cache the value for a previous position on the line. Then the next time column is called, we only need to update the cache for the small portion that the Stream advanced. This way we only ever walk the stream once. --- lib/codemirror.js | 13 ++++++++++--- 1 file changed, 10 insertions(+), 3 deletions(-) diff --git a/lib/codemirror.js b/lib/codemirror.js index 3137d231c4..8c429c6b87 100644 --- a/lib/codemirror.js +++ b/lib/codemirror.js @@ -3350,6 +3350,7 @@ window.CodeMirror = (function() { this.pos = this.start = 0; this.string = string; this.tabSize = tabSize || 8; + this.lastColumnPos = this.lastColumnValue = 0; } StringStream.prototype = { @@ -3382,7 +3383,13 @@ window.CodeMirror = (function() { if (found > -1) {this.pos = found; return true;} }, backUp: function(n) {this.pos -= n;}, - column: function() {return countColumn(this.string, this.start, this.tabSize);}, + column: function() { + if (this.lastColumnPos < this.start) { + this.lastColumnValue = countColumn(this.string, this.start, this.tabSize, this.lastColumnPos, this.lastColumnValue); + this.lastColumnPos = this.start; + } + return this.lastColumnValue; + }, indentation: function() {return countColumn(this.string, null, this.tabSize);}, match: function(pattern, consume, caseInsensitive) { if (typeof pattern == "string") { @@ -4960,12 +4967,12 @@ window.CodeMirror = (function() { // Counts the column offset in a string, taking tabs into account. // Used mostly to find indentation. - function countColumn(string, end, tabSize) { + function countColumn(string, end, tabSize, startIndex, startValue) { if (end == null) { end = string.search(/[^\s\u00a0]/); if (end == -1) end = string.length; } - for (var i = 0, n = 0; i < end; ++i) { + for (var i = startIndex || 0, n = startValue || 0; i < end; ++i) { if (string.charAt(i) == "\t") n += tabSize - (n % tabSize); else ++n; } From f97e5a6e30cda984d2e4e97205895c487eb69c9c Mon Sep 17 00:00:00 2001 From: Brandon Frohs Date: Fri, 1 Mar 2013 12:38:50 -0500 Subject: [PATCH 0800/5780] Fix typo in manual. --- doc/manual.html | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/doc/manual.html b/doc/manual.html index 5812b48b1b..8a5f8f4723 100644 --- a/doc/manual.html +++ b/doc/manual.html @@ -1210,7 +1210,7 @@

    Sizing, scrolling and positioning methods

    Mode, state, and token-related methods

    When writing language-aware functionality, it can often be - useful to hook into the knowledge thate the CodeMirror language + useful to hook into the knowledge that the CodeMirror language mode has. See the section on modes for a more detailed description of how these work.

    From c3f655b5311ba5e54af9198cd2bcde3e16e65817 Mon Sep 17 00:00:00 2001 From: Joseph Pecoraro Date: Fri, 1 Mar 2013 13:23:19 -0800 Subject: [PATCH 0801/5780] Improve performance of StringStream with long lines (match) StringString.prototype.match() may do a lot of unnecessary work: - If it is caseInsensitive it was calling toLowerCase on the entire string. - It did an indexOf search through the string for a pattern, but only cared if the string started with that pattern. Because a string pattern has a fixed length, we only need to run toLowerCase on a small substring of the stream, and we only compare this small substring to the pattern. --- lib/codemirror.js | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-) diff --git a/lib/codemirror.js b/lib/codemirror.js index 8c429c6b87..23be905deb 100644 --- a/lib/codemirror.js +++ b/lib/codemirror.js @@ -3394,7 +3394,8 @@ window.CodeMirror = (function() { match: function(pattern, consume, caseInsensitive) { if (typeof pattern == "string") { var cased = function(str) {return caseInsensitive ? str.toLowerCase() : str;}; - if (cased(this.string).indexOf(cased(pattern), this.pos) == this.pos) { + var substr = this.string.substr(this.pos, pattern.length); + if (cased(substr) == cased(pattern)) { if (consume !== false) this.pos += pattern.length; return true; } From 136265718e5c96abd5c6db306f3c4c236688ad41 Mon Sep 17 00:00:00 2001 From: Yunchi Luo Date: Sat, 2 Mar 2013 11:16:08 -0500 Subject: [PATCH 0802/5780] [vim] Clean up v2 references --- keymap/vim.js | 72 +++++++++------------------------------------------ 1 file changed, 12 insertions(+), 60 deletions(-) diff --git a/keymap/vim.js b/keymap/vim.js index 57e90e09df..358814292d 100644 --- a/keymap/vim.js +++ b/keymap/vim.js @@ -1945,10 +1945,7 @@ } // Search functions - function SearchState() { - // Highlighted text that match the query. - this.marked = null; - } + function SearchState() {} SearchState.prototype = { getQuery: function() { return getVimGlobalState().query; @@ -1956,12 +1953,6 @@ setQuery: function(query) { getVimGlobalState().query = query; }, - getMarked: function() { - return this.marked; - }, - setMarked: function(marked) { - this.marked = marked; - }, getOverlay: function() { return this.searchOverlay; }, @@ -2094,7 +2085,6 @@ if (regexEqual(query, state.getQuery())) { return; } - clearSearchHighlight(cm); highlightSearchMatches(cm, query); state.setQuery(query); }); @@ -2124,28 +2114,14 @@ }; } function highlightSearchMatches(cm, query) { - if (cm.addOverlay) { - var overlay = getSearchState(cm).getOverlay(); - if (!overlay || query != overlay.query) { - if (overlay) { - cm.removeOverlay(overlay); - } - overlay = searchOverlay(query); - cm.addOverlay(overlay); - getSearchState(cm).setOverlay(overlay); - } - } else { - // TODO: Highlight only text inside the viewport. Highlighting everything - // is inefficient and expensive. - if (cm.lineCount() < 2000) { // This is too expensive on big documents. - var marked = []; - for (var cursor = cm.getSearchCursor(query); - cursor.findNext();) { - marked.push(cm.markText(cursor.from(), cursor.to(), - { className: 'cm-searching' })); - } - getSearchState(cm).setMarked(marked); + var overlay = getSearchState(cm).getOverlay(); + if (!overlay || query != overlay.query) { + if (overlay) { + cm.removeOverlay(overlay); } + overlay = searchOverlay(query); + cm.addOverlay(overlay); + getSearchState(cm).setOverlay(overlay); } } function findNext(cm, prev, repeat) { @@ -2155,9 +2131,7 @@ if (!query) { return; } - if (!state.getMarked()) { - highlightSearchMatches(cm, query); - } + highlightSearchMatches(cm, query); var pos = cm.getCursor(); // If search is initiated with ? instead of /, negate direction. prev = (state.isReversed()) ? !prev : prev; @@ -2179,25 +2153,8 @@ return cursor.from(); });} function clearSearchHighlight(cm) { - if (cm.addOverlay) { - cm.removeOverlay(getSearchState(cm).getOverlay()); - getSearchState(cm).setOverlay(null); - } else { - cm.operation(function() { - var state = getSearchState(cm); - if (!state.getQuery()) { - return; - } - var marked = state.getMarked(); - if (!marked) { - return; - } - for (var i = 0; i < marked.length; ++i) { - marked[i].clear(); - } - state.setMarked(null); - }); - } + cm.removeOverlay(getSearchState(cm).getOverlay()); + getSearchState(cm).setOverlay(null); } /** * Check if pos is in the specified range, INCLUSIVE. @@ -2514,12 +2471,7 @@ exitVisualMode(cm, vim); } } - if (cm.compoundChange) { - // Only exists in v2 - cm.compoundChange(doReplace); - } else { - cm.operation(doReplace); - } + cm.operation(doReplace); }, redo: CodeMirror.commands.redo, undo: CodeMirror.commands.undo, From 8a2068fdc69c0fdb4f4343ea8cacfbc4fa9e515e Mon Sep 17 00:00:00 2001 From: Yunchi Luo Date: Sat, 2 Mar 2013 14:33:55 -0500 Subject: [PATCH 0803/5780] [vim] Incremental search --- addon/dialog/dialog.js | 4 +++ keymap/vim.js | 82 ++++++++++++++++++++++++++++-------------- 2 files changed, 60 insertions(+), 26 deletions(-) diff --git a/addon/dialog/dialog.js b/addon/dialog/dialog.js index e113f7c9b4..51fdc6443f 100644 --- a/addon/dialog/dialog.js +++ b/addon/dialog/dialog.js @@ -25,6 +25,7 @@ var inp = dialog.getElementsByTagName("input")[0], button; if (inp) { CodeMirror.on(inp, "keydown", function(e) { + if (options && options.onKeyDown && options.onKeyDown(e, inp.value)) { return; } if (e.keyCode == 13 || e.keyCode == 27) { CodeMirror.e_stop(e); close(); @@ -32,6 +33,9 @@ if (e.keyCode == 13) callback(inp.value); } }); + if (options && options.onKeyUp) { + CodeMirror.on(inp, "keyup", function(e) {options.onKeyUp(e, inp.value);}); + } if (options && options.value) inp.value = options.value; inp.focus(); CodeMirror.on(inp, "blur", close); diff --git a/keymap/vim.js b/keymap/vim.js index 358814292d..2f230db8e1 100644 --- a/keymap/vim.js +++ b/keymap/vim.js @@ -182,8 +182,10 @@ { keys: ['<'], type: 'operator', operator: 'indent', operatorArgs: { indentRight: false }}, { keys: ['g', '~'], type: 'operator', operator: 'swapcase' }, - { keys: ['n'], type: 'motion', motion: 'findNext' }, - { keys: ['N'], type: 'motion', motion: 'findPrev' }, + { keys: ['n'], type: 'motion', motion: 'findNext', + motionArgs: { forward: true }}, + { keys: ['N'], type: 'motion', motion: 'findNext', + motionArgs: { forward: false }}, // Operator-Motion dual commands { keys: ['x'], type: 'operatorMotion', operator: 'delete', motion: 'moveByCharacters', motionArgs: { forward: true }, @@ -727,19 +729,45 @@ var forward = command.searchArgs.forward; getSearchState(cm).setReversed(!forward); var promptPrefix = (forward) ? '/' : '?'; + var originalQuery = getSearchState(cm).getQuery(); + var originalPos = cm.getCursor(); function handleQuery(query, ignoreCase, smartCase) { updateSearchQuery(cm, query, ignoreCase, smartCase); commandDispatcher.processMotion(cm, vim, { type: 'motion', - motion: 'findNext' + motion: 'findNext', + motionArgs: { forward: true } }); } function onPromptClose(query) { + cm.scrollIntoView(originalPos); handleQuery(query, true /** ignoreCase */, true /** smartCase */); } + function onPromptKeyUp(e, query) { + if (query) { + updateSearchQuery(cm, query, true /** ignoreCase */, true /** smartCase */); + cm.scrollIntoView(findNext(cm, forward, query)); + } else { + clearSearchHighlight(cm); + cm.scrollIntoView(originalPos); + } + } + function onPromptKeyDown(e, query) { + if (CodeMirror.keyName(e) == 'Esc') { + updateSearchQuery(cm, originalQuery); + clearSearchHighlight(cm); + cm.scrollIntoView(originalPos); + } + } switch (command.searchArgs.querySrc) { case 'prompt': - showPrompt(cm, onPromptClose, promptPrefix, searchPromptDesc); + showPrompt(cm, { + onClose: onPromptClose, + prefix: promptPrefix, + desc: searchPromptDesc, + onKeyUp: onPromptKeyUp, + onKeyDown: onPromptKeyDown + }); break; case 'wordUnderCursor': var word = expandWordUnderCursor(cm, false /** inclusive */, @@ -776,9 +804,9 @@ exCommandDispatcher.processCommand(cm, command.exArgs.input); } else { if (vim.visualMode) { - showPrompt(cm, onPromptClose, ':', undefined, '\'<,\'>'); + showPrompt(cm, { onClose: onPromptClose, prefix: ':', value: '\'<,\'>' }); } else { - showPrompt(cm, onPromptClose, ':'); + showPrompt(cm, { onClose: onPromptClose, prefix: ':' }); } } }, @@ -932,10 +960,16 @@ return { line: cur.line + motionArgs.repeat - 1, ch: Infinity }; }, findNext: function(cm, motionArgs, vim) { - return findNext(cm, false /** prev */, motionArgs.repeat); - }, - findPrev: function(cm, motionArgs, vim) { - return findNext(cm, true /** prev */, motionArgs.repeat); + var state = getSearchState(cm); + var query = state.getQuery(); + if (!query) { + return; + } + var prev = !motionArgs.forward; + // If search is initiated with ? instead of /, negate direction. + prev = (state.isReversed()) ? !prev : prev; + highlightSearchMatches(cm, query); + return findNext(cm, prev/** prev */, query, motionArgs.repeat); }, goToMark: function(cm, motionArgs, vim) { var mark = vim.marks[motionArgs.selectedCharacter]; @@ -1970,9 +2004,10 @@ var vim = getVimState(cm); return vim.searchState_ || (vim.searchState_ = new SearchState()); } - function dialog(cm, text, shortText, callback, initialValue) { + function dialog(cm, template, shortText, onClose, options) { if (cm.openDialog) { - cm.openDialog(text, callback, { bottom: true, value: initialValue }); + cm.openDialog(template, onClose, { bottom: true, value: options.value, + onKeyDown: options.onKeyDown, onKeyUp: options.onKeyUp }); } else { callback(prompt(shortText, "")); @@ -2001,6 +2036,8 @@ * through to the Regex object. */ function parseQuery(cm, query, ignoreCase, smartCase) { + // Check if the query is already a regex. + if (query instanceof RegExp) { return query; } // First try to extract regex + flags from the input. If no flags found, // extract just the regex. IE does not accept flags directly defined in // the regex string in the form /regex/flags @@ -2054,10 +2091,10 @@ return raw; } var searchPromptDesc = '(Javascript regexp)'; - function showPrompt(cm, onPromptClose, prefix, desc, initialValue) { - var shortText = (prefix || '') + ' ' + (desc || ''); - dialog(cm, makePrompt(prefix, desc), shortText, onPromptClose, - initialValue); + function showPrompt(cm, options) { + var shortText = (options.prefix || '') + ' ' + (options.desc || ''); + var prompt = makePrompt(options.prefix, options.desc); + dialog(cm, prompt, shortText, options.onClose, options); } function regexEqual(r1, r2) { if (r1 instanceof RegExp && r2 instanceof RegExp) { @@ -2082,10 +2119,10 @@ if (!query) { return; } + highlightSearchMatches(cm, query); if (regexEqual(query, state.getQuery())) { return; } - highlightSearchMatches(cm, query); state.setQuery(query); }); } @@ -2124,17 +2161,10 @@ getSearchState(cm).setOverlay(overlay); } } - function findNext(cm, prev, repeat) { + function findNext(cm, prev, query, repeat) { + if (repeat === undefined) { repeat = 1; } return cm.operation(function() { - var state = getSearchState(cm); - var query = state.getQuery(); - if (!query) { - return; - } - highlightSearchMatches(cm, query); var pos = cm.getCursor(); - // If search is initiated with ? instead of /, negate direction. - prev = (state.isReversed()) ? !prev : prev; if (!prev) { pos.ch += 1; } From f4fcea92fa4375189bb6f66ccb3359729cca24aa Mon Sep 17 00:00:00 2001 From: Yunchi Luo Date: Sat, 2 Mar 2013 14:45:59 -0500 Subject: [PATCH 0804/5780] [vim] Ctrl-C and Ctrl-[ exit dialog --- addon/dialog/dialog.js | 4 ++-- keymap/vim.js | 23 +++++++++++++++++++---- 2 files changed, 21 insertions(+), 6 deletions(-) diff --git a/addon/dialog/dialog.js b/addon/dialog/dialog.js index 51fdc6443f..71e2287447 100644 --- a/addon/dialog/dialog.js +++ b/addon/dialog/dialog.js @@ -25,7 +25,7 @@ var inp = dialog.getElementsByTagName("input")[0], button; if (inp) { CodeMirror.on(inp, "keydown", function(e) { - if (options && options.onKeyDown && options.onKeyDown(e, inp.value)) { return; } + if (options && options.onKeyDown && options.onKeyDown(e, inp.value, close)) { return; } if (e.keyCode == 13 || e.keyCode == 27) { CodeMirror.e_stop(e); close(); @@ -34,7 +34,7 @@ } }); if (options && options.onKeyUp) { - CodeMirror.on(inp, "keyup", function(e) {options.onKeyUp(e, inp.value);}); + CodeMirror.on(inp, "keyup", function(e) {options.onKeyUp(e, inp.value, close);}); } if (options && options.value) inp.value = options.value; inp.focus(); diff --git a/keymap/vim.js b/keymap/vim.js index 2f230db8e1..a04f4d3fef 100644 --- a/keymap/vim.js +++ b/keymap/vim.js @@ -752,11 +752,16 @@ cm.scrollIntoView(originalPos); } } - function onPromptKeyDown(e, query) { - if (CodeMirror.keyName(e) == 'Esc') { + function onPromptKeyDown(e, query, close) { + var keyName = CodeMirror.keyName(e); + if (keyName == 'Esc' || keyName == 'Ctrl-C' || keyName == 'Ctrl-[') { updateSearchQuery(cm, originalQuery); clearSearchHighlight(cm); cm.scrollIntoView(originalPos); + + CodeMirror.e_stop(e); + close(); + cm.focus(); } } switch (command.searchArgs.querySrc) { @@ -799,14 +804,24 @@ function onPromptClose(input) { exCommandDispatcher.processCommand(cm, input); } + function onPromptKeyDown(e, input, close) { + var keyName = CodeMirror.keyName(e); + if (keyName == 'Esc' || keyName == 'Ctrl-C' || keyName == 'Ctrl-[') { + CodeMirror.e_stop(e); + close(); + cm.focus(); + } + } if (command.type == 'keyToEx') { // Handle user defined Ex to Ex mappings exCommandDispatcher.processCommand(cm, command.exArgs.input); } else { if (vim.visualMode) { - showPrompt(cm, { onClose: onPromptClose, prefix: ':', value: '\'<,\'>' }); + showPrompt(cm, { onClose: onPromptClose, prefix: ':', value: '\'<,\'>', + onKeyDown: onPromptKeyDown}); } else { - showPrompt(cm, { onClose: onPromptClose, prefix: ':' }); + showPrompt(cm, { onClose: onPromptClose, prefix: ':', + onKeyDown: onPromptKeyDown}); } } }, From 6db15190fee156acf5428a44563f901cba8ec21f Mon Sep 17 00:00:00 2001 From: Yunchi Luo Date: Sat, 2 Mar 2013 12:40:23 -0500 Subject: [PATCH 0805/5780] [vim] Initial implementation of . --- keymap/vim.js | 43 +++++++++++++++++++++++++++++++------------ test/vim_test.js | 35 +++++++++++++++++++++++++---------- 2 files changed, 56 insertions(+), 22 deletions(-) diff --git a/keymap/vim.js b/keymap/vim.js index a04f4d3fef..b7006d850c 100644 --- a/keymap/vim.js +++ b/keymap/vim.js @@ -245,6 +245,7 @@ { keys: ['z', 'b'], type: 'action', action: 'scrollToCursor', actionArgs: { position: 'bottom' }, motion: 'moveToFirstNonWhiteSpaceCharacter' }, + { keys: ['.'], type: 'action', action: 'repeatLastEdit' }, // Text object motions { keys: ['a', 'character'], type: 'motion', motion: 'textObjectManipulation' }, @@ -401,7 +402,7 @@ var vim = getVimState(cm); if (key == 'Esc') { // Clear input state and get back to normal mode. - vim.inputState.reset(); + vim.inputState = new InputState(); if (vim.visualMode) { exitVisualMode(cm, vim); } @@ -441,9 +442,6 @@ // Represents the current input state. function InputState() { - this.reset(); - } - InputState.prototype.reset = function() { this.prefixRepeat = []; this.motionRepeat = []; @@ -453,7 +451,7 @@ this.motionArgs = null; this.keyBuffer = []; // For matching multi-key commands. this.registerName = null; // Defaults to the unamed register. - }; + } InputState.prototype.pushRepeatDigit = function(n) { if (!this.operator) { this.prefixRepeat = this.prefixRepeat.concat(n); @@ -631,6 +629,7 @@ return null; }, processCommand: function(cm, vim, command) { + vim.inputState.repeatOverride = command.repeatOverride; switch (command.type) { case 'motion': this.processMotion(cm, vim, command); @@ -672,7 +671,7 @@ return; } else { // 2 different operators in a row doesn't make sense. - inputState.reset(); + vim.inputState = new InputState(); } } inputState.operator = command.operator; @@ -717,7 +716,7 @@ actionArgs.repeat = repeat || 1; actionArgs.repeatIsExplicit = repeatIsExplicit; actionArgs.registerName = inputState.registerName; - inputState.reset(); + vim.inputState = new InputState(); vim.lastMotion = null, actions[command.action](cm, actionArgs, vim); }, @@ -842,10 +841,13 @@ var curOriginal = copyCursor(curStart); var curEnd; var repeat; - if (motionArgs.repeat !== undefined) { - // If motionArgs specifies a repeat, that takes precedence over the + if (operator) { + this.recordLastEdit(cm, vim, inputState); + } + if (inputState.repeatOverride !== undefined) { + // If repeatOverride is specified, that takes precedence over the // input state's repeat. Used by Ex mode and can be user defined. - repeat = inputState.motionArgs.repeat; + repeat = inputState.repeatOverride; } else { repeat = inputState.getRepeat(); } @@ -862,7 +864,7 @@ inputState.selectedCharacter; } motionArgs.repeat = repeat; - inputState.reset(); + vim.inputState = new InputState(); if (motion) { var motionResult = motions[motion](cm, motionArgs, vim); vim.lastMotion = motions[motion]; @@ -959,6 +961,9 @@ actions.enterInsertMode(cm); } } + }, + recordLastEdit: function(cm, vim, inputState) { + vim.lastEdit = inputState; } }; @@ -1450,6 +1455,19 @@ cm.setCursor(offsetCursor(curEnd, 0, -1)); } } + }, + repeatLastEdit: function(cm, actionArgs, vim) { + // TODO: Make this repeat insert mode changes. + var lastEdit = vim.lastEdit; + if (lastEdit) { + if (actionArgs.repeat && actionArgs.repeatIsExplicit) { + vim.lastEdit.repeatOverride = actionArgs.repeat; + } + var currentInputState = vim.inputState; + vim.inputState = vim.lastEdit; + commandDispatcher.evalInput(cm, vim); + vim.inputState = currentInputState; + } } }; @@ -2461,7 +2479,8 @@ commandDispatcher.processMotion(cm, getVimState(cm), { motion: 'moveToLineOrEdgeOfDocument', motionArgs: { forward: false, explicitRepeat: true, - linewise: true, repeat: params.line+1 }}); + linewise: true }, + repeatOverride: params.line+1}); }, substitute: function(cm, params) { var argString = params.argString; diff --git a/test/vim_test.js b/test/vim_test.js index 00638b570d..4764c639b5 100644 --- a/test/vim_test.js +++ b/test/vim_test.js @@ -54,7 +54,8 @@ var bigWord3 = { var bigWord4 = { start: { line: bigWordLine.line, ch: bigWord1.end.ch + 3 }, end: { line: bigWordLine.line, ch: bigWord1.end.ch + 7 } -} +}; + var oChars = [ { line: charLine.line, ch: 1 }, { line: charLine.line, ch: 3 }, { line: charLine.line, ch: 7 } ]; @@ -971,15 +972,6 @@ testVim('? and n/N', function(cm, vim, helpers) { helpers.doKeys('2', '?'); helpers.assertCursorAt(0, 11); }, { value: 'match nope match \n nope Match' }); -//:noh should clear highlighting of search-results but allow to resume search through n -testVim('noh_clearSearchHighlight', function(cm, vim, helpers) { - cm.openDialog = helpers.fakeOpenDialog('match'); - helpers.doKeys('?'); - helpers.doEx('noh'); - eq(vim.searchState_.getOverlay(),null,'match-highlighting wasn\'t cleared'); - helpers.doKeys('n'); - helpers.assertCursorAt(0, 11,'can\'t resume search after clearing highlighting'); -}, { value: 'match nope match \n nope Match' }); testVim('*', function(cm, vim, helpers) { cm.setCursor(0, 9); helpers.doKeys('*'); @@ -1020,6 +1012,20 @@ testVim('#', function(cm, vim, helpers) { helpers.doKeys('#'); helpers.assertCursorAt(1, 8); }, { value: ' := match nomatch match \nnomatch Match' }); +testVim('.', function(cm, vim, helpers) { + cm.setCursor(0, 0); + helpers.doKeys('2', 'd', 'w'); + helpers.doKeys('.'); + eq('5 6', cm.getValue()); +}, { value: '1 2 3 4 5 6'}); +testVim('._repeat', function(cm, vim, helpers) { + cm.setCursor(0, 0); + helpers.doKeys('2', 'd', 'w'); + helpers.doKeys('3', '.'); + eq('6', cm.getValue()); +}, { value: '1 2 3 4 5 6'}); + +// Ex mode tests testVim('ex_write', function(cm, vim, helpers) { var tmp = CodeMirror.commands.save; var written; @@ -1084,6 +1090,15 @@ testVim('ex_substitute_count_with_range', function(cm, vim, helpers) { helpers.doEx('1,3s/\\d/0/ 3'); eq('1\n2\n0\n0', cm.getValue()); }, { value: '1\n2\n3\n4' }); +//:noh should clear highlighting of search-results but allow to resume search through n +testVim('ex_noh_clearSearchHighlight', function(cm, vim, helpers) { + cm.openDialog = helpers.fakeOpenDialog('match'); + helpers.doKeys('?'); + helpers.doEx('noh'); + eq(vim.searchState_.getOverlay(),null,'match-highlighting wasn\'t cleared'); + helpers.doKeys('n'); + helpers.assertCursorAt(0, 11,'can\'t resume search after clearing highlighting'); +}, { value: 'match nope match \n nope Match' }); // TODO: Reset key maps after each test. testVim('ex_map_key2key', function(cm, vim, helpers) { helpers.doEx('map a x'); From 6e250cef478e35f9e27a7f09987d20a699013643 Mon Sep 17 00:00:00 2001 From: David Pathakjee Date: Sat, 2 Mar 2013 14:58:46 -0600 Subject: [PATCH 0806/5780] Update for release of Clojure 1.5.0. --- mode/clojure/clojure.js | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/mode/clojure/clojure.js b/mode/clojure/clojure.js index 5dcb489d38..8b9614cc20 100644 --- a/mode/clojure/clojure.js +++ b/mode/clojure/clojure.js @@ -19,7 +19,7 @@ CodeMirror.defineMode("clojure", function () { "defn defn- def def- defonce defmulti defmethod defmacro defstruct deftype defprotocol defrecord defproject deftest slice defalias defhinted defmacro- defn-memo defnk defnk defonce- defunbound defunbound- defvar defvar- let letfn do case cond condp for loop recur when when-not when-let when-first if if-let if-not . .. -> ->> doto and or dosync doseq dotimes dorun doall load import unimport ns in-ns refer try catch finally throw with-open with-local-vars binding gen-class gen-and-load-class gen-and-save-class handler-case handle"); var builtins = makeKeywords( - "* *' *1 *2 *3 *agent* *allow-unresolved-vars* *assert* *clojure-version* *command-line-args* *compile-files* *compile-path* *compiler-options* *data-readers* *e *err* *file* *flush-on-newline* *fn-loader* *in* *math-context* *ns* *out* *print-dup* *print-length* *print-level* *print-meta* *print-readably* *read-eval* *source-path* *unchecked-math* *use-context-classloader* *verbose-defrecords* *warn-on-reflection* + +' - -' -> ->> ->ArrayChunk ->Vec ->VecNode ->VecSeq -cache-protocol-fn -reset-methods .. / < <= = == > >= EMPTY-NODE accessor aclone add-classpath add-watch agent agent-error agent-errors aget alength alias all-ns alter alter-meta! alter-var-root amap ancestors and apply areduce array-map aset aset-boolean aset-byte aset-char aset-double aset-float aset-int aset-long aset-short assert assoc assoc! assoc-in associative? atom await await-for await1 bases bean bigdec bigint biginteger binding bit-and bit-and-not bit-clear bit-flip bit-not bit-or bit-set bit-shift-left bit-shift-right bit-test bit-xor boolean boolean-array booleans bound-fn bound-fn* bound? butlast byte byte-array bytes case cast char char-array char-escape-string char-name-string char? chars chunk chunk-append chunk-buffer chunk-cons chunk-first chunk-next chunk-rest chunked-seq? class class? clear-agent-errors clojure-version coll? comment commute comp comparator compare compare-and-set! compile complement concat cond condp conj conj! cons constantly construct-proxy contains? count counted? create-ns create-struct cycle dec dec' decimal? declare default-data-readers definline definterface defmacro defmethod defmulti defn defn- defonce defprotocol defrecord defstruct deftype delay delay? deliver denominator deref derive descendants destructure disj disj! dissoc dissoc! distinct distinct? doall dorun doseq dosync dotimes doto double double-array doubles drop drop-last drop-while empty empty? ensure enumeration-seq error-handler error-mode eval even? every-pred every? ex-data ex-info extend extend-protocol extend-type extenders extends? false? ffirst file-seq filter filterv find find-keyword find-ns find-protocol-impl find-protocol-method find-var first flatten float float-array float? floats flush fn fn? fnext fnil for force format frequencies future future-call future-cancel future-cancelled? future-done? future? gen-class gen-interface gensym get get-in get-method get-proxy-class get-thread-bindings get-validator group-by hash hash-combine hash-map hash-set identical? identity if-let if-not ifn? import in-ns inc inc' init-proxy instance? int int-array integer? interleave intern interpose into into-array ints io! isa? iterate iterator-seq juxt keep keep-indexed key keys keyword keyword? last lazy-cat lazy-seq let letfn line-seq list list* list? load load-file load-reader load-string loaded-libs locking long long-array longs loop macroexpand macroexpand-1 make-array make-hierarchy map map-indexed map? mapcat mapv max max-key memfn memoize merge merge-with meta method-sig methods min min-key mod munge name namespace namespace-munge neg? newline next nfirst nil? nnext not not-any? not-empty not-every? not= ns ns-aliases ns-imports ns-interns ns-map ns-name ns-publics ns-refers ns-resolve ns-unalias ns-unmap nth nthnext nthrest num number? numerator object-array odd? or parents partial partition partition-all partition-by pcalls peek persistent! pmap pop pop! pop-thread-bindings pos? pr pr-str prefer-method prefers primitives-classnames print print-ctor print-dup print-method print-simple print-str printf println println-str prn prn-str promise proxy proxy-call-with-super proxy-mappings proxy-name proxy-super push-thread-bindings pvalues quot rand rand-int rand-nth range ratio? rational? rationalize re-find re-groups re-matcher re-matches re-pattern re-seq read read-line read-string realized? reduce reduce-kv reductions ref ref-history-count ref-max-history ref-min-history ref-set refer refer-clojure reify release-pending-sends rem remove remove-all-methods remove-method remove-ns remove-watch repeat repeatedly replace replicate require reset! reset-meta! resolve rest restart-agent resultset-seq reverse reversible? rseq rsubseq satisfies? second select-keys send send-off seq seq? seque sequence sequential? set set-error-handler! set-error-mode! set-validator! set? short short-array shorts shuffle shutdown-agents slurp some some-fn sort sort-by sorted-map sorted-map-by sorted-set sorted-set-by sorted? special-symbol? spit split-at split-with str string? struct struct-map subs subseq subvec supers swap! symbol symbol? sync take take-last take-nth take-while test the-ns thread-bound? time to-array to-array-2d trampoline transient tree-seq true? type unchecked-add unchecked-add-int unchecked-byte unchecked-char unchecked-dec unchecked-dec-int unchecked-divide-int unchecked-double unchecked-float unchecked-inc unchecked-inc-int unchecked-int unchecked-long unchecked-multiply unchecked-multiply-int unchecked-negate unchecked-negate-int unchecked-remainder-int unchecked-short unchecked-subtract unchecked-subtract-int underive unquote unquote-splicing update-in update-proxy use val vals var-get var-set var? vary-meta vec vector vector-of vector? when when-first when-let when-not while with-bindings with-bindings* with-in-str with-loading-context with-local-vars with-meta with-open with-out-str with-precision with-redefs with-redefs-fn xml-seq zero? zipmap"); + "* *' *1 *2 *3 *agent* *allow-unresolved-vars* *assert* *clojure-version* *command-line-args* *compile-files* *compile-path* *compiler-options* *data-readers* *e *err* *file* *flush-on-newline* *fn-loader* *in* *math-context* *ns* *out* *print-dup* *print-length* *print-level* *print-meta* *print-readably* *read-eval* *source-path* *unchecked-math* *use-context-classloader* *verbose-defrecords* *warn-on-reflection* + +' - -' -> ->> ->ArrayChunk ->Vec ->VecNode ->VecSeq -cache-protocol-fn -reset-methods .. / < <= = == > >= EMPTY-NODE accessor aclone add-classpath add-watch agent agent-error agent-errors aget alength alias all-ns alter alter-meta! alter-var-root amap ancestors and apply areduce array-map aset aset-boolean aset-byte aset-char aset-double aset-float aset-int aset-long aset-short assert assoc assoc! assoc-in associative? atom await await-for await1 bases bean bigdec bigint biginteger binding bit-and bit-and-not bit-clear bit-flip bit-not bit-or bit-set bit-shift-left bit-shift-right bit-test bit-xor boolean boolean-array booleans bound-fn bound-fn* bound? butlast byte byte-array bytes case cast char char-array char-escape-string char-name-string char? chars chunk chunk-append chunk-buffer chunk-cons chunk-first chunk-next chunk-rest chunked-seq? class class? clear-agent-errors clojure-version coll? comment commute comp comparator compare compare-and-set! compile complement concat cond condp conj conj! cons constantly construct-proxy contains? count counted? create-ns create-struct cycle dec dec' decimal? declare default-data-readers definline definterface defmacro defmethod defmulti defn defn- defonce defprotocol defrecord defstruct deftype delay delay? deliver denominator deref derive descendants destructure disj disj! dissoc dissoc! distinct distinct? doall dorun doseq dosync dotimes doto double double-array doubles drop drop-last drop-while empty empty? ensure enumeration-seq error-handler error-mode eval even? every-pred every? ex-data ex-info extend extend-protocol extend-type extenders extends? false? ffirst file-seq filter filterv find find-keyword find-ns find-protocol-impl find-protocol-method find-var first flatten float float-array float? floats flush fn fn? fnext fnil for force format frequencies future future-call future-cancel future-cancelled? future-done? future? gen-class gen-interface gensym get get-in get-method get-proxy-class get-thread-bindings get-validator group-by hash hash-combine hash-map hash-set identical? identity if-let if-not ifn? import in-ns inc inc' init-proxy instance? int int-array integer? interleave intern interpose into into-array ints io! isa? iterate iterator-seq juxt keep keep-indexed key keys keyword keyword? last lazy-cat lazy-seq let letfn line-seq list list* list? load load-file load-reader load-string loaded-libs locking long long-array longs loop macroexpand macroexpand-1 make-array make-hierarchy map map-indexed map? mapcat mapv max max-key memfn memoize merge merge-with meta method-sig methods min min-key mod munge name namespace namespace-munge neg? newline next nfirst nil? nnext not not-any? not-empty not-every? not= ns ns-aliases ns-imports ns-interns ns-map ns-name ns-publics ns-refers ns-resolve ns-unalias ns-unmap nth nthnext nthrest num number? numerator object-array odd? or parents partial partition partition-all partition-by pcalls peek persistent! pmap pop pop! pop-thread-bindings pos? pr pr-str prefer-method prefers primitives-classnames print print-ctor print-dup print-method print-simple print-str printf println println-str prn prn-str promise proxy proxy-call-with-super proxy-mappings proxy-name proxy-super push-thread-bindings pvalues quot rand rand-int rand-nth range ratio? rational? rationalize re-find re-groups re-matcher re-matches re-pattern re-seq read read-line read-string realized? reduce reduce-kv reductions ref ref-history-count ref-max-history ref-min-history ref-set refer refer-clojure reify release-pending-sends rem remove remove-all-methods remove-method remove-ns remove-watch repeat repeatedly replace replicate require reset! reset-meta! resolve rest restart-agent resultset-seq reverse reversible? rseq rsubseq satisfies? second select-keys send send-off seq seq? seque sequence sequential? set set-error-handler! set-error-mode! set-validator! set? short short-array shorts shuffle shutdown-agents slurp some some-fn sort sort-by sorted-map sorted-map-by sorted-set sorted-set-by sorted? special-symbol? spit split-at split-with str string? struct struct-map subs subseq subvec supers swap! symbol symbol? sync take take-last take-nth take-while test the-ns thread-bound? time to-array to-array-2d trampoline transient tree-seq true? type unchecked-add unchecked-add-int unchecked-byte unchecked-char unchecked-dec unchecked-dec-int unchecked-divide-int unchecked-double unchecked-float unchecked-inc unchecked-inc-int unchecked-int unchecked-long unchecked-multiply unchecked-multiply-int unchecked-negate unchecked-negate-int unchecked-remainder-int unchecked-short unchecked-subtract unchecked-subtract-int underive unquote unquote-splicing update-in update-proxy use val vals var-get var-set var? vary-meta vec vector vector-of vector? when when-first when-let when-not while with-bindings with-bindings* with-in-str with-loading-context with-local-vars with-meta with-open with-out-str with-precision with-redefs with-redefs-fn xml-seq zero? zipmap *default-data-reader-fn* as-> cond-> cond->> reduced reduced? send-via set-agent-send-executor! set-agent-send-off-executor! some-> some->>"); var indentKeys = makeKeywords( // Built-ins From 0442d11707009e1f1cc9cba3a321a237bf1b6e10 Mon Sep 17 00:00:00 2001 From: Marijn Haverbeke Date: Sun, 3 Mar 2013 21:40:38 +0100 Subject: [PATCH 0807/5780] Clip updated scroll positions to a sane range --- lib/codemirror.js | 5 +++-- 1 file changed, 3 insertions(+), 2 deletions(-) diff --git a/lib/codemirror.js b/lib/codemirror.js index 23be905deb..0b8382749c 100644 --- a/lib/codemirror.js +++ b/lib/codemirror.js @@ -2496,8 +2496,9 @@ window.CodeMirror = (function() { function addToScrollPos(cm, left, top) { var pos = cm.curOp.updateScrollPos || (cm.curOp.updateScrollPos = {scrollLeft: cm.doc.scrollLeft, scrollTop: cm.doc.scrollTop}); - pos.scrollTop += top; - pos.scrollLeft += left; + var scroll = cm.display.scroller; + pos.scrollTop = Math.max(0, Math.min(scroll.scrollHeight - scroll.clientHeight, pos.scrollTop + top)); + pos.scrollLeft = Math.max(0, Math.min(scroll.scrollWidth - scroll.clientWidth, pos.scrollLeft + left)); } // API UTILITIES From 6bb8b11860d294782d7c08849dd71b5b9615539a Mon Sep 17 00:00:00 2001 From: Joseph Pecoraro Date: Sat, 2 Mar 2013 17:51:47 -0800 Subject: [PATCH 0808/5780] [javascript mode] Recognize bitwise operators '~' and '^' This was causing "1^2" to tokenize as: (number, "1"), (variable, "^2). By tokenizing these bitwise operators as operators CodeMirror can correctly syntax highlight the 2 as a number. Likewise the number in "~0". --- mode/javascript/javascript.js | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/mode/javascript/javascript.js b/mode/javascript/javascript.js index d41536f77a..b6d4e6b451 100644 --- a/mode/javascript/javascript.js +++ b/mode/javascript/javascript.js @@ -52,7 +52,7 @@ CodeMirror.defineMode("javascript", function(config, parserConfig) { return jsKeywords; }(); - var isOperatorChar = /[+\-*&%=<>!?|]/; + var isOperatorChar = /[+\-*&%=<>!?|~^]/; function chain(stream, state, f) { state.tokenize = f; From 8ce322a1afb0d2fea153e78ed396341fe2966030 Mon Sep 17 00:00:00 2001 From: Thaddee Tyl Date: Sun, 3 Mar 2013 00:16:32 +0100 Subject: [PATCH 0809/5780] Give example of the extraKeys option. Issues #988, #742, #612, etc. show that CodeMirror users tend to: 1. Not find how to make the Tab key insert spaces instead of a tab, 2. Miss how to use extraKeys to easily solve the issue. Adding an example helps them find a solution immediately. --- doc/manual.html | 11 +++++++++++ 1 file changed, 11 insertions(+) diff --git a/doc/manual.html b/doc/manual.html index 8a5f8f4723..1739b741e4 100644 --- a/doc/manual.html +++ b/doc/manual.html @@ -523,6 +523,17 @@

    Keymaps

    for example, Shift-Ctrl-Space would be a valid key identifier.

    +

    Common example: map the Tab key to insert spaces instead of a tab + character.

    + +
    +{
    +  Tab: function(cm) {
    +    var spaces = Array(cm.getOption("indentUnit") + 1).join(" ");
    +    cm.replaceSelection(spaces, "end", "+input");
    +  }
    +}
    +

    Alternatively, a character can be specified directly by surrounding it in single quotes, for example '$' or 'q'. Due to limitations in the way browsers fire From b06dbb217e39cb065752b99b4004a4ec47496538 Mon Sep 17 00:00:00 2001 From: Marijn Haverbeke Date: Mon, 4 Mar 2013 11:58:59 +0100 Subject: [PATCH 0810/5780] Give heightForcer a width to make sure it actually forces height --- lib/codemirror.js | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/lib/codemirror.js b/lib/codemirror.js index 0b8382749c..8fc2dabb13 100644 --- a/lib/codemirror.js +++ b/lib/codemirror.js @@ -118,7 +118,7 @@ window.CodeMirror = (function() { // Set to the height of the text, causes scrolling d.sizer = elt("div", [d.mover], "CodeMirror-sizer"); // D is needed because behavior of elts with overflow: auto and padding is inconsistent across browsers - d.heightForcer = elt("div", null, null, "position: absolute; height: " + scrollerCutOff + "px"); + d.heightForcer = elt("div", null, null, "position: absolute; height: " + scrollerCutOff + "px; width: 1px;"); // Will contain the gutters, if any d.gutters = elt("div", null, "CodeMirror-gutters"); d.lineGutter = null; From 4b53a77c2a54baa1887e0edb80afaa048491328e Mon Sep 17 00:00:00 2001 From: Marijn Haverbeke Date: Tue, 5 Mar 2013 10:23:22 +0100 Subject: [PATCH 0811/5780] Add the soft-hyphen character to the list of non-printing chars Closes #1310 --- lib/codemirror.js | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/lib/codemirror.js b/lib/codemirror.js index 8fc2dabb13..442ba8a62f 100644 --- a/lib/codemirror.js +++ b/lib/codemirror.js @@ -4034,7 +4034,7 @@ window.CodeMirror = (function() { return builder.pre; } - var tokenSpecialChars = /[\t\u0000-\u0019\u200b\u2028\u2029\uFEFF]/g; + var tokenSpecialChars = /[\t\u0000-\u0019\u00ad\u200b\u2028\u2029\uFEFF]/g; function buildToken(builder, text, style, startStyle, endStyle) { if (!text) return; if (!tokenSpecialChars.test(text)) { From 9565019853ed6d2b90ffb160a5f0e848fcbcc2f7 Mon Sep 17 00:00:00 2001 From: Yunchi Luo Date: Mon, 4 Mar 2013 21:14:14 -0500 Subject: [PATCH 0812/5780] [vim] Fix handling of invalid search regexps. --- keymap/vim.js | 39 +++++++++++++++++++++++++-------------- 1 file changed, 25 insertions(+), 14 deletions(-) diff --git a/keymap/vim.js b/keymap/vim.js index b7006d850c..5a3469453e 100644 --- a/keymap/vim.js +++ b/keymap/vim.js @@ -731,7 +731,12 @@ var originalQuery = getSearchState(cm).getQuery(); var originalPos = cm.getCursor(); function handleQuery(query, ignoreCase, smartCase) { - updateSearchQuery(cm, query, ignoreCase, smartCase); + try { + updateSearchQuery(cm, query, ignoreCase, smartCase); + } catch (e) { + showConfirm(cm, 'Invalid regex: ' + regexPart); + return; + } commandDispatcher.processMotion(cm, vim, { type: 'motion', motion: 'findNext', @@ -744,8 +749,13 @@ } function onPromptKeyUp(e, query) { if (query) { - updateSearchQuery(cm, query, true /** ignoreCase */, true /** smartCase */); - cm.scrollIntoView(findNext(cm, forward, query)); + try { + updateSearchQuery(cm, query, true /** ignoreCase */, + true /** smartCase */); + cm.scrollIntoView(findNext(cm, forward, query)); + } catch (e) { + // Swallow bad regexes for incremental search. + } } else { clearSearchHighlight(cm); cm.scrollIntoView(originalPos); @@ -2092,13 +2102,9 @@ if (smartCase) { ignoreCase = (/^[^A-Z]*$/).test(regexPart); } - try { - var regexp = new RegExp(regexPart, - (ignoreCase || forceIgnoreCase) ? 'i' : undefined); - return regexp; - } catch (e) { - showConfirm(cm, 'Invalid regex: ' + regexPart); - } + var regexp = new RegExp(regexPart, + (ignoreCase || forceIgnoreCase) ? 'i' : undefined); + return regexp; } function showConfirm(cm, text) { if (cm.openConfirm) { @@ -2510,8 +2516,13 @@ if (regexPart) { // If regex part is empty, then use the previous query. Otherwise use // the regex part as the new query. - updateSearchQuery(cm, regexPart, true /** ignoreCase */, - true /** smartCase */); + try { + updateSearchQuery(cm, regexPart, true /** ignoreCase */, + true /** smartCase */); + } catch (e) { + showConfirm(cm, 'Invalid regex: ' + regexPart); + return; + } } var state = getSearchState(cm); var query = state.getQuery(); @@ -2586,7 +2597,7 @@ var finishMark = stream.next(); // The range must terminate at an alphabetic character which // shares the same case as the start of the range. - if (isLowerCase(startMark) && isLowerCase(finishMark) || + if (isLowerCase(startMark) && isLowerCase(finishMark) || isUpperCase(startMark) && isUpperCase(finishMark)) { var start = startMark.charCodeAt(0); var finish = finishMark.charCodeAt(0); @@ -2599,7 +2610,7 @@ // determined that they are the same case, we can use // their char codes to iterate through the defined range. for (var j = 0; j <= finish - start; j++) { - var mark = String.fromCharCode(start + j); + var mark = String.fromCharCode(start + j); delete state.marks[mark]; } } else { From 1171acb083d681a6d657c67058341c3e9b483ae7 Mon Sep 17 00:00:00 2001 From: "Daniel, Dao Quang Minh" Date: Tue, 5 Mar 2013 02:49:58 +0800 Subject: [PATCH 0813/5780] Make CSS mode configurable, use it to implement SCSS mode. --- mode/css/css.js | 575 +++++++++++++++++++++++++----------------- mode/css/scss.html | 142 +++++++++++ mode/css/scss_test.js | 77 ++++++ mode/css/test.js | 5 +- test/index.html | 1 + 5 files changed, 562 insertions(+), 238 deletions(-) create mode 100644 mode/css/scss.html create mode 100644 mode/css/scss_test.js diff --git a/mode/css/css.js b/mode/css/css.js index 37ef76c941..7cdd18b3fc 100644 --- a/mode/css/css.js +++ b/mode/css/css.js @@ -1,204 +1,25 @@ -CodeMirror.defineMode("css", function(config) { - var indentUnit = config.indentUnit, type; - - var atMediaTypes = keySet([ - "all", "aural", "braille", "handheld", "print", "projection", "screen", - "tty", "tv", "embossed" - ]); - - var atMediaFeatures = keySet([ - "width", "min-width", "max-width", "height", "min-height", "max-height", - "device-width", "min-device-width", "max-device-width", "device-height", - "min-device-height", "max-device-height", "aspect-ratio", - "min-aspect-ratio", "max-aspect-ratio", "device-aspect-ratio", - "min-device-aspect-ratio", "max-device-aspect-ratio", "color", "min-color", - "max-color", "color-index", "min-color-index", "max-color-index", - "monochrome", "min-monochrome", "max-monochrome", "resolution", - "min-resolution", "max-resolution", "scan", "grid" - ]); - - var propertyKeywords = keySet([ - "align-content", "align-items", "align-self", "alignment-adjust", - "alignment-baseline", "anchor-point", "animation", "animation-delay", - "animation-direction", "animation-duration", "animation-iteration-count", - "animation-name", "animation-play-state", "animation-timing-function", - "appearance", "azimuth", "backface-visibility", "background", - "background-attachment", "background-clip", "background-color", - "background-image", "background-origin", "background-position", - "background-repeat", "background-size", "baseline-shift", "binding", - "bleed", "bookmark-label", "bookmark-level", "bookmark-state", - "bookmark-target", "border", "border-bottom", "border-bottom-color", - "border-bottom-left-radius", "border-bottom-right-radius", - "border-bottom-style", "border-bottom-width", "border-collapse", - "border-color", "border-image", "border-image-outset", - "border-image-repeat", "border-image-slice", "border-image-source", - "border-image-width", "border-left", "border-left-color", - "border-left-style", "border-left-width", "border-radius", "border-right", - "border-right-color", "border-right-style", "border-right-width", - "border-spacing", "border-style", "border-top", "border-top-color", - "border-top-left-radius", "border-top-right-radius", "border-top-style", - "border-top-width", "border-width", "bottom", "box-decoration-break", - "box-shadow", "box-sizing", "break-after", "break-before", "break-inside", - "caption-side", "clear", "clip", "color", "color-profile", "column-count", - "column-fill", "column-gap", "column-rule", "column-rule-color", - "column-rule-style", "column-rule-width", "column-span", "column-width", - "columns", "content", "counter-increment", "counter-reset", "crop", "cue", - "cue-after", "cue-before", "cursor", "direction", "display", - "dominant-baseline", "drop-initial-after-adjust", - "drop-initial-after-align", "drop-initial-before-adjust", - "drop-initial-before-align", "drop-initial-size", "drop-initial-value", - "elevation", "empty-cells", "fit", "fit-position", "flex", "flex-basis", - "flex-direction", "flex-flow", "flex-grow", "flex-shrink", "flex-wrap", - "float", "float-offset", "font", "font-feature-settings", "font-family", - "font-kerning", "font-language-override", "font-size", "font-size-adjust", - "font-stretch", "font-style", "font-synthesis", "font-variant", - "font-variant-alternates", "font-variant-caps", "font-variant-east-asian", - "font-variant-ligatures", "font-variant-numeric", "font-variant-position", - "font-weight", "grid-cell", "grid-column", "grid-column-align", - "grid-column-sizing", "grid-column-span", "grid-columns", "grid-flow", - "grid-row", "grid-row-align", "grid-row-sizing", "grid-row-span", - "grid-rows", "grid-template", "hanging-punctuation", "height", "hyphens", - "icon", "image-orientation", "image-rendering", "image-resolution", - "inline-box-align", "justify-content", "left", "letter-spacing", - "line-break", "line-height", "line-stacking", "line-stacking-ruby", - "line-stacking-shift", "line-stacking-strategy", "list-style", - "list-style-image", "list-style-position", "list-style-type", "margin", - "margin-bottom", "margin-left", "margin-right", "margin-top", - "marker-offset", "marks", "marquee-direction", "marquee-loop", - "marquee-play-count", "marquee-speed", "marquee-style", "max-height", - "max-width", "min-height", "min-width", "move-to", "nav-down", "nav-index", - "nav-left", "nav-right", "nav-up", "opacity", "order", "orphans", "outline", - "outline-color", "outline-offset", "outline-style", "outline-width", - "overflow", "overflow-style", "overflow-wrap", "overflow-x", "overflow-y", - "padding", "padding-bottom", "padding-left", "padding-right", "padding-top", - "page", "page-break-after", "page-break-before", "page-break-inside", - "page-policy", "pause", "pause-after", "pause-before", "perspective", - "perspective-origin", "pitch", "pitch-range", "play-during", "position", - "presentation-level", "punctuation-trim", "quotes", "rendering-intent", - "resize", "rest", "rest-after", "rest-before", "richness", "right", - "rotation", "rotation-point", "ruby-align", "ruby-overhang", - "ruby-position", "ruby-span", "size", "speak", "speak-as", "speak-header", - "speak-numeral", "speak-punctuation", "speech-rate", "stress", "string-set", - "tab-size", "table-layout", "target", "target-name", "target-new", - "target-position", "text-align", "text-align-last", "text-decoration", - "text-decoration-color", "text-decoration-line", "text-decoration-skip", - "text-decoration-style", "text-emphasis", "text-emphasis-color", - "text-emphasis-position", "text-emphasis-style", "text-height", - "text-indent", "text-justify", "text-outline", "text-shadow", - "text-space-collapse", "text-transform", "text-underline-position", - "text-wrap", "top", "transform", "transform-origin", "transform-style", - "transition", "transition-delay", "transition-duration", - "transition-property", "transition-timing-function", "unicode-bidi", - "vertical-align", "visibility", "voice-balance", "voice-duration", - "voice-family", "voice-pitch", "voice-range", "voice-rate", "voice-stress", - "voice-volume", "volume", "white-space", "widows", "width", "word-break", - "word-spacing", "word-wrap", "z-index" - ]); - - var colorKeywords = keySet([ - "black", "silver", "gray", "white", "maroon", "red", "purple", "fuchsia", - "green", "lime", "olive", "yellow", "navy", "blue", "teal", "aqua" - ]); - - var valueKeywords = keySet([ - "above", "absolute", "activeborder", "activecaption", "afar", - "after-white-space", "ahead", "alias", "all", "all-scroll", "alternate", - "always", "amharic", "amharic-abegede", "antialiased", "appworkspace", - "arabic-indic", "armenian", "asterisks", "auto", "avoid", "background", - "backwards", "baseline", "below", "bidi-override", "binary", "bengali", - "blink", "block", "block-axis", "bold", "bolder", "border", "border-box", - "both", "bottom", "break-all", "break-word", "button", "button-bevel", - "buttonface", "buttonhighlight", "buttonshadow", "buttontext", "cambodian", - "capitalize", "caps-lock-indicator", "caption", "captiontext", "caret", - "cell", "center", "checkbox", "circle", "cjk-earthly-branch", - "cjk-heavenly-stem", "cjk-ideographic", "clear", "clip", "close-quote", - "col-resize", "collapse", "compact", "condensed", "contain", "content", - "content-box", "context-menu", "continuous", "copy", "cover", "crop", - "cross", "crosshair", "currentcolor", "cursive", "dashed", "decimal", - "decimal-leading-zero", "default", "default-button", "destination-atop", - "destination-in", "destination-out", "destination-over", "devanagari", - "disc", "discard", "document", "dot-dash", "dot-dot-dash", "dotted", - "double", "down", "e-resize", "ease", "ease-in", "ease-in-out", "ease-out", - "element", "ellipsis", "embed", "end", "ethiopic", "ethiopic-abegede", - "ethiopic-abegede-am-et", "ethiopic-abegede-gez", "ethiopic-abegede-ti-er", - "ethiopic-abegede-ti-et", "ethiopic-halehame-aa-er", - "ethiopic-halehame-aa-et", "ethiopic-halehame-am-et", - "ethiopic-halehame-gez", "ethiopic-halehame-om-et", - "ethiopic-halehame-sid-et", "ethiopic-halehame-so-et", - "ethiopic-halehame-ti-er", "ethiopic-halehame-ti-et", - "ethiopic-halehame-tig", "ew-resize", "expanded", "extra-condensed", - "extra-expanded", "fantasy", "fast", "fill", "fixed", "flat", "footnotes", - "forwards", "from", "geometricPrecision", "georgian", "graytext", "groove", - "gujarati", "gurmukhi", "hand", "hangul", "hangul-consonant", "hebrew", - "help", "hidden", "hide", "higher", "highlight", "highlighttext", - "hiragana", "hiragana-iroha", "horizontal", "hsl", "hsla", "icon", "ignore", - "inactiveborder", "inactivecaption", "inactivecaptiontext", "infinite", - "infobackground", "infotext", "inherit", "initial", "inline", "inline-axis", - "inline-block", "inline-table", "inset", "inside", "intrinsic", "invert", - "italic", "justify", "kannada", "katakana", "katakana-iroha", "khmer", - "landscape", "lao", "large", "larger", "left", "level", "lighter", - "line-through", "linear", "lines", "list-item", "listbox", "listitem", - "local", "logical", "loud", "lower", "lower-alpha", "lower-armenian", - "lower-greek", "lower-hexadecimal", "lower-latin", "lower-norwegian", - "lower-roman", "lowercase", "ltr", "malayalam", "match", - "media-controls-background", "media-current-time-display", - "media-fullscreen-button", "media-mute-button", "media-play-button", - "media-return-to-realtime-button", "media-rewind-button", - "media-seek-back-button", "media-seek-forward-button", "media-slider", - "media-sliderthumb", "media-time-remaining-display", "media-volume-slider", - "media-volume-slider-container", "media-volume-sliderthumb", "medium", - "menu", "menulist", "menulist-button", "menulist-text", - "menulist-textfield", "menutext", "message-box", "middle", "min-intrinsic", - "mix", "mongolian", "monospace", "move", "multiple", "myanmar", "n-resize", - "narrower", "navy", "ne-resize", "nesw-resize", "no-close-quote", "no-drop", - "no-open-quote", "no-repeat", "none", "normal", "not-allowed", "nowrap", - "ns-resize", "nw-resize", "nwse-resize", "oblique", "octal", "open-quote", - "optimizeLegibility", "optimizeSpeed", "oriya", "oromo", "outset", - "outside", "overlay", "overline", "padding", "padding-box", "painted", - "paused", "persian", "plus-darker", "plus-lighter", "pointer", "portrait", - "pre", "pre-line", "pre-wrap", "preserve-3d", "progress", "push-button", - "radio", "read-only", "read-write", "read-write-plaintext-only", "relative", - "repeat", "repeat-x", "repeat-y", "reset", "reverse", "rgb", "rgba", - "ridge", "right", "round", "row-resize", "rtl", "run-in", "running", - "s-resize", "sans-serif", "scroll", "scrollbar", "se-resize", "searchfield", - "searchfield-cancel-button", "searchfield-decoration", - "searchfield-results-button", "searchfield-results-decoration", - "semi-condensed", "semi-expanded", "separate", "serif", "show", "sidama", - "single", "skip-white-space", "slide", "slider-horizontal", - "slider-vertical", "sliderthumb-horizontal", "sliderthumb-vertical", "slow", - "small", "small-caps", "small-caption", "smaller", "solid", "somali", - "source-atop", "source-in", "source-out", "source-over", "space", "square", - "square-button", "start", "static", "status-bar", "stretch", "stroke", - "sub", "subpixel-antialiased", "super", "sw-resize", "table", - "table-caption", "table-cell", "table-column", "table-column-group", - "table-footer-group", "table-header-group", "table-row", "table-row-group", - "telugu", "text", "text-bottom", "text-top", "textarea", "textfield", "thai", - "thick", "thin", "threeddarkshadow", "threedface", "threedhighlight", - "threedlightshadow", "threedshadow", "tibetan", "tigre", "tigrinya-er", - "tigrinya-er-abegede", "tigrinya-et", "tigrinya-et-abegede", "to", "top", - "transparent", "ultra-condensed", "ultra-expanded", "underline", "up", - "upper-alpha", "upper-armenian", "upper-greek", "upper-hexadecimal", - "upper-latin", "upper-norwegian", "upper-roman", "uppercase", "urdu", "url", - "vertical", "vertical-text", "visible", "visibleFill", "visiblePainted", - "visibleStroke", "visual", "w-resize", "wait", "wave", "white", "wider", - "window", "windowframe", "windowtext", "x-large", "x-small", "xor", - "xx-large", "xx-small", "yellow" - ]); +CodeMirror.defineMode("css", function(config, parserConfig) { + "use strict"; + var indentUnit = config.indentUnit, + hooks = parserConfig.hooks || {}, + atMediaTypes = parserConfig.atMediaTypes || {}, + atMediaFeatures = parserConfig.atMediaFeatures || {}, + propertyKeywords = parserConfig.propertyKeywords || {}, + colorKeywords = parserConfig.colorKeywords || {}, + valueKeywords = parserConfig.valueKeywords || {}, + allowNested = !!parserConfig.allowNested, + type = null; - function keySet(array) { var keys = {}; for (var i = 0; i < array.length; ++i) keys[array[i]] = true; return keys; } - function ret(style, tp) {type = tp; return style;} + function ret(style, tp) { type = tp; return style; } function tokenBase(stream, state) { var ch = stream.next(); - if (ch == "@") {stream.eatWhile(/[\w\\\-]/); return ret("def", stream.current());} - else if (ch == "/" && stream.eat("*")) { - state.tokenize = tokenCComment; - return tokenCComment(stream, state); - } - else if (ch == "<" && stream.eat("!")) { - state.tokenize = tokenSGMLComment; - return tokenSGMLComment(stream, state); + if (hooks[ch]) { + // result[0] is style and result[1] is type + var result = hooks[ch](stream, state); + if (result !== false) return ret(result[0], result[1]); } + if (ch == "@") {stream.eatWhile(/[\w\\\-]/); return ret("def", stream.current());} else if (ch == "=") ret(null, "compare"); else if ((ch == "~" || ch == "|") && stream.eat("=")) return ret(null, "compare"); else if (ch == "\"" || ch == "'") { @@ -248,30 +69,6 @@ CodeMirror.defineMode("css", function(config) { } } - function tokenCComment(stream, state) { - var maybeEnd = false, ch; - while ((ch = stream.next()) != null) { - if (maybeEnd && ch == "/") { - state.tokenize = tokenBase; - break; - } - maybeEnd = (ch == "*"); - } - return ret("comment", "comment"); - } - - function tokenSGMLComment(stream, state) { - var dashes = 0, ch; - while ((ch = stream.next()) != null) { - if (dashes >= 2 && ch == ">") { - state.tokenize = tokenBase; - break; - } - dashes = (ch == "-") ? dashes + 1 : 0; - } - return ret("comment", "comment"); - } - function tokenString(quote, nonInclusive) { return function(stream, state) { var escaped = false, ch; @@ -305,59 +102,63 @@ CodeMirror.defineMode("css", function(config) { }, token: function(stream, state) { - + // Use these terms when applicable (see http://www.xanthir.com/blog/b4E50) - // + // // rule** or **ruleset: // A selector + braces combo, or an at-rule. - // + // // declaration block: // A sequence of declarations. - // + // // declaration: // A property + colon + value combo. - // + // // property value: // The entire value of a property. - // + // // component value: // A single piece of a property value. Like the 5px in // text-shadow: 0 0 5px blue;. Can also refer to things that are // multiple terms, like the 1-4 terms that make up the background-size // portion of the background shorthand. - // + // // term: // The basic unit of author-facing CSS, like a single number (5), // dimension (5px), string ("foo"), or function. Officially defined // by the CSS 2.1 grammar (look for the 'term' production) - // - // + // + // // simple selector: // A single atomic selector, like a type selector, an attr selector, a // class selector, etc. - // + // // compound selector: // One or more simple selectors without a combinator. div.example is // compound, div > .example is not. - // + // // complex selector: // One or more compound selectors chained with combinators. - // + // // combinator: // The parts of selectors that express relationships. There are four // currently - the space (descendant combinator), the greater-than // bracket (child combinator), the plus sign (next sibling combinator), // and the tilda (following sibling combinator). - // + // // sequence of selectors: // One or more of the named type of selector chained with commas. + state.tokenize = state.tokenize || tokenBase; if (state.tokenize == tokenBase && stream.eatSpace()) return null; var style = state.tokenize(stream, state); // Changing style returned based on context var context = state.stack[state.stack.length-1]; - if (style == "property") { + if (style == "variable") { + if (type == "variable-definition") state.stack.push("propertyValue"); + return "variable-2"; + } else if (style == "property") { if (context == "propertyValue"){ if (valueKeywords[stream.current()]) { style = "string-2"; @@ -370,6 +171,18 @@ CodeMirror.defineMode("css", function(config) { if (!propertyKeywords[stream.current()]) { style += " error"; } + } else if (context == "block") { + // if a value is present in both property, value, or color, the order + // of preference is property -> color -> value + if (propertyKeywords[stream.current()]) { + style = "property"; + } else if (colorKeywords[stream.current()]) { + style = "keyword"; + } else if (valueKeywords[stream.current()]) { + style = "string-2"; + } else { + style = "tag"; + } } else if (!context || context == "@media{") { style = "tag"; } else if (context == "@media") { @@ -434,19 +247,25 @@ CodeMirror.defineMode("css", function(config) { state.stack.pop(); state.stack[state.stack.length-1] = "@media{"; } - else state.stack.push("rule"); + else { + var newContext = allowNested ? "block" : "rule"; + state.stack.push(newContext); + } } else if (type == "}") { + var lastState = state.stack[state.stack.length - 1]; + if (lastState == "interpolation") style = "operator"; state.stack.pop(); if (context == "propertyValue") state.stack.pop(); } + else if (type == "interpolation") state.stack.push("interpolation"); else if (type == "@media") state.stack.push("@media"); else if (context == "@media" && /\b(keyword|attribute)\b/.test(style)) state.stack.push("@mediaType"); else if (context == "@mediaType" && stream.current() == ",") state.stack.pop(); else if (context == "@mediaType" && type == "(") state.stack.push("@mediaType("); else if (context == "@mediaType(" && type == ")") state.stack.pop(); - else if (context == "rule" && type == ":") state.stack.push("propertyValue"); + else if ((context == "rule" || context == "block") && type == ":") state.stack.push("propertyValue"); else if (context == "propertyValue" && type == ";") state.stack.pop(); return style; }, @@ -462,4 +281,286 @@ CodeMirror.defineMode("css", function(config) { }; }); -CodeMirror.defineMIME("text/css", "css"); +(function() { + function mimes(ms, mode) { + for (var i = 0; i < ms.length; ++i) { + CodeMirror.defineMIME(ms[i], mode); + } + } + + function keySet(array) { + var keys = {}; + for (var i = 0; i < array.length; ++i) { + keys[array[i]] = true; + } + return keys; + } + + var atMediaTypes = [ + "all", "aural", "braille", "handheld", "print", "projection", "screen", + "tty", "tv", "embossed" + ]; + + var atMediaFeatures = [ + "width", "min-width", "max-width", "height", "min-height", "max-height", + "device-width", "min-device-width", "max-device-width", "device-height", + "min-device-height", "max-device-height", "aspect-ratio", + "min-aspect-ratio", "max-aspect-ratio", "device-aspect-ratio", + "min-device-aspect-ratio", "max-device-aspect-ratio", "color", "min-color", + "max-color", "color-index", "min-color-index", "max-color-index", + "monochrome", "min-monochrome", "max-monochrome", "resolution", + "min-resolution", "max-resolution", "scan", "grid" + ]; + + var propertyKeywords = [ + "align-content", "align-items", "align-self", "alignment-adjust", + "alignment-baseline", "anchor-point", "animation", "animation-delay", + "animation-direction", "animation-duration", "animation-iteration-count", + "animation-name", "animation-play-state", "animation-timing-function", + "appearance", "azimuth", "backface-visibility", "background", + "background-attachment", "background-clip", "background-color", + "background-image", "background-origin", "background-position", + "background-repeat", "background-size", "baseline-shift", "binding", + "bleed", "bookmark-label", "bookmark-level", "bookmark-state", + "bookmark-target", "border", "border-bottom", "border-bottom-color", + "border-bottom-left-radius", "border-bottom-right-radius", + "border-bottom-style", "border-bottom-width", "border-collapse", + "border-color", "border-image", "border-image-outset", + "border-image-repeat", "border-image-slice", "border-image-source", + "border-image-width", "border-left", "border-left-color", + "border-left-style", "border-left-width", "border-radius", "border-right", + "border-right-color", "border-right-style", "border-right-width", + "border-spacing", "border-style", "border-top", "border-top-color", + "border-top-left-radius", "border-top-right-radius", "border-top-style", + "border-top-width", "border-width", "bottom", "box-decoration-break", + "box-shadow", "box-sizing", "break-after", "break-before", "break-inside", + "caption-side", "clear", "clip", "color", "color-profile", "column-count", + "column-fill", "column-gap", "column-rule", "column-rule-color", + "column-rule-style", "column-rule-width", "column-span", "column-width", + "columns", "content", "counter-increment", "counter-reset", "crop", "cue", + "cue-after", "cue-before", "cursor", "direction", "display", + "dominant-baseline", "drop-initial-after-adjust", + "drop-initial-after-align", "drop-initial-before-adjust", + "drop-initial-before-align", "drop-initial-size", "drop-initial-value", + "elevation", "empty-cells", "fit", "fit-position", "flex", "flex-basis", + "flex-direction", "flex-flow", "flex-grow", "flex-shrink", "flex-wrap", + "float", "float-offset", "font", "font-feature-settings", "font-family", + "font-kerning", "font-language-override", "font-size", "font-size-adjust", + "font-stretch", "font-style", "font-synthesis", "font-variant", + "font-variant-alternates", "font-variant-caps", "font-variant-east-asian", + "font-variant-ligatures", "font-variant-numeric", "font-variant-position", + "font-weight", "grid-cell", "grid-column", "grid-column-align", + "grid-column-sizing", "grid-column-span", "grid-columns", "grid-flow", + "grid-row", "grid-row-align", "grid-row-sizing", "grid-row-span", + "grid-rows", "grid-template", "hanging-punctuation", "height", "hyphens", + "icon", "image-orientation", "image-rendering", "image-resolution", + "inline-box-align", "justify-content", "left", "letter-spacing", + "line-break", "line-height", "line-stacking", "line-stacking-ruby", + "line-stacking-shift", "line-stacking-strategy", "list-style", + "list-style-image", "list-style-position", "list-style-type", "margin", + "margin-bottom", "margin-left", "margin-right", "margin-top", + "marker-offset", "marks", "marquee-direction", "marquee-loop", + "marquee-play-count", "marquee-speed", "marquee-style", "max-height", + "max-width", "min-height", "min-width", "move-to", "nav-down", "nav-index", + "nav-left", "nav-right", "nav-up", "opacity", "order", "orphans", "outline", + "outline-color", "outline-offset", "outline-style", "outline-width", + "overflow", "overflow-style", "overflow-wrap", "overflow-x", "overflow-y", + "padding", "padding-bottom", "padding-left", "padding-right", "padding-top", + "page", "page-break-after", "page-break-before", "page-break-inside", + "page-policy", "pause", "pause-after", "pause-before", "perspective", + "perspective-origin", "pitch", "pitch-range", "play-during", "position", + "presentation-level", "punctuation-trim", "quotes", "rendering-intent", + "resize", "rest", "rest-after", "rest-before", "richness", "right", + "rotation", "rotation-point", "ruby-align", "ruby-overhang", + "ruby-position", "ruby-span", "size", "speak", "speak-as", "speak-header", + "speak-numeral", "speak-punctuation", "speech-rate", "stress", "string-set", + "tab-size", "table-layout", "target", "target-name", "target-new", + "target-position", "text-align", "text-align-last", "text-decoration", + "text-decoration-color", "text-decoration-line", "text-decoration-skip", + "text-decoration-style", "text-emphasis", "text-emphasis-color", + "text-emphasis-position", "text-emphasis-style", "text-height", + "text-indent", "text-justify", "text-outline", "text-shadow", + "text-space-collapse", "text-transform", "text-underline-position", + "text-wrap", "top", "transform", "transform-origin", "transform-style", + "transition", "transition-delay", "transition-duration", + "transition-property", "transition-timing-function", "unicode-bidi", + "vertical-align", "visibility", "voice-balance", "voice-duration", + "voice-family", "voice-pitch", "voice-range", "voice-rate", "voice-stress", + "voice-volume", "volume", "white-space", "widows", "width", "word-break", + "word-spacing", "word-wrap", "z-index" + ]; + + var colorKeywords = [ + "black", "silver", "gray", "white", "maroon", "red", "purple", "fuchsia", + "green", "lime", "olive", "yellow", "navy", "blue", "teal", "aqua" + ]; + + var valueKeywords = [ + "above", "absolute", "activeborder", "activecaption", "afar", + "after-white-space", "ahead", "alias", "all", "all-scroll", "alternate", + "always", "amharic", "amharic-abegede", "antialiased", "appworkspace", + "arabic-indic", "armenian", "asterisks", "auto", "avoid", "background", + "backwards", "baseline", "below", "bidi-override", "binary", "bengali", + "blink", "block", "block-axis", "bold", "bolder", "border", "border-box", + "both", "bottom", "break-all", "break-word", "button", "button-bevel", + "buttonface", "buttonhighlight", "buttonshadow", "buttontext", "cambodian", + "capitalize", "caps-lock-indicator", "caption", "captiontext", "caret", + "cell", "center", "checkbox", "circle", "cjk-earthly-branch", + "cjk-heavenly-stem", "cjk-ideographic", "clear", "clip", "close-quote", + "col-resize", "collapse", "compact", "condensed", "contain", "content", + "content-box", "context-menu", "continuous", "copy", "cover", "crop", + "cross", "crosshair", "currentcolor", "cursive", "dashed", "decimal", + "decimal-leading-zero", "default", "default-button", "destination-atop", + "destination-in", "destination-out", "destination-over", "devanagari", + "disc", "discard", "document", "dot-dash", "dot-dot-dash", "dotted", + "double", "down", "e-resize", "ease", "ease-in", "ease-in-out", "ease-out", + "element", "ellipsis", "embed", "end", "ethiopic", "ethiopic-abegede", + "ethiopic-abegede-am-et", "ethiopic-abegede-gez", "ethiopic-abegede-ti-er", + "ethiopic-abegede-ti-et", "ethiopic-halehame-aa-er", + "ethiopic-halehame-aa-et", "ethiopic-halehame-am-et", + "ethiopic-halehame-gez", "ethiopic-halehame-om-et", + "ethiopic-halehame-sid-et", "ethiopic-halehame-so-et", + "ethiopic-halehame-ti-er", "ethiopic-halehame-ti-et", + "ethiopic-halehame-tig", "ew-resize", "expanded", "extra-condensed", + "extra-expanded", "fantasy", "fast", "fill", "fixed", "flat", "footnotes", + "forwards", "from", "geometricPrecision", "georgian", "graytext", "groove", + "gujarati", "gurmukhi", "hand", "hangul", "hangul-consonant", "hebrew", + "help", "hidden", "hide", "higher", "highlight", "highlighttext", + "hiragana", "hiragana-iroha", "horizontal", "hsl", "hsla", "icon", "ignore", + "inactiveborder", "inactivecaption", "inactivecaptiontext", "infinite", + "infobackground", "infotext", "inherit", "initial", "inline", "inline-axis", + "inline-block", "inline-table", "inset", "inside", "intrinsic", "invert", + "italic", "justify", "kannada", "katakana", "katakana-iroha", "khmer", + "landscape", "lao", "large", "larger", "left", "level", "lighter", + "line-through", "linear", "lines", "list-item", "listbox", "listitem", + "local", "logical", "loud", "lower", "lower-alpha", "lower-armenian", + "lower-greek", "lower-hexadecimal", "lower-latin", "lower-norwegian", + "lower-roman", "lowercase", "ltr", "malayalam", "match", + "media-controls-background", "media-current-time-display", + "media-fullscreen-button", "media-mute-button", "media-play-button", + "media-return-to-realtime-button", "media-rewind-button", + "media-seek-back-button", "media-seek-forward-button", "media-slider", + "media-sliderthumb", "media-time-remaining-display", "media-volume-slider", + "media-volume-slider-container", "media-volume-sliderthumb", "medium", + "menu", "menulist", "menulist-button", "menulist-text", + "menulist-textfield", "menutext", "message-box", "middle", "min-intrinsic", + "mix", "mongolian", "monospace", "move", "multiple", "myanmar", "n-resize", + "narrower", "navy", "ne-resize", "nesw-resize", "no-close-quote", "no-drop", + "no-open-quote", "no-repeat", "none", "normal", "not-allowed", "nowrap", + "ns-resize", "nw-resize", "nwse-resize", "oblique", "octal", "open-quote", + "optimizeLegibility", "optimizeSpeed", "oriya", "oromo", "outset", + "outside", "overlay", "overline", "padding", "padding-box", "painted", + "paused", "persian", "plus-darker", "plus-lighter", "pointer", "portrait", + "pre", "pre-line", "pre-wrap", "preserve-3d", "progress", "push-button", + "radio", "read-only", "read-write", "read-write-plaintext-only", "relative", + "repeat", "repeat-x", "repeat-y", "reset", "reverse", "rgb", "rgba", + "ridge", "right", "round", "row-resize", "rtl", "run-in", "running", + "s-resize", "sans-serif", "scroll", "scrollbar", "se-resize", "searchfield", + "searchfield-cancel-button", "searchfield-decoration", + "searchfield-results-button", "searchfield-results-decoration", + "semi-condensed", "semi-expanded", "separate", "serif", "show", "sidama", + "single", "skip-white-space", "slide", "slider-horizontal", + "slider-vertical", "sliderthumb-horizontal", "sliderthumb-vertical", "slow", + "small", "small-caps", "small-caption", "smaller", "solid", "somali", + "source-atop", "source-in", "source-out", "source-over", "space", "square", + "square-button", "start", "static", "status-bar", "stretch", "stroke", + "sub", "subpixel-antialiased", "super", "sw-resize", "table", + "table-caption", "table-cell", "table-column", "table-column-group", + "table-footer-group", "table-header-group", "table-row", "table-row-group", + "telugu", "text", "text-bottom", "text-top", "textarea", "textfield", "thai", + "thick", "thin", "threeddarkshadow", "threedface", "threedhighlight", + "threedlightshadow", "threedshadow", "tibetan", "tigre", "tigrinya-er", + "tigrinya-er-abegede", "tigrinya-et", "tigrinya-et-abegede", "to", "top", + "transparent", "ultra-condensed", "ultra-expanded", "underline", "up", + "upper-alpha", "upper-armenian", "upper-greek", "upper-hexadecimal", + "upper-latin", "upper-norwegian", "upper-roman", "uppercase", "urdu", "url", + "vertical", "vertical-text", "visible", "visibleFill", "visiblePainted", + "visibleStroke", "visual", "w-resize", "wait", "wave", "white", "wider", + "window", "windowframe", "windowtext", "x-large", "x-small", "xor", + "xx-large", "xx-small", "yellow" + ]; + + function tokenCComment(stream, state) { + var maybeEnd = false, ch; + while ((ch = stream.next()) != null) { + if (maybeEnd && ch == "/") { + state.tokenize = null; + break; + } + maybeEnd = (ch == "*"); + } + return ["comment", "comment"]; + } + + mimes(['text/css', 'css'], { + atMediaTypes: keySet(atMediaTypes), + atMediaFeatures: keySet(atMediaFeatures), + propertyKeywords: keySet(propertyKeywords), + colorKeywords: keySet(colorKeywords), + valueKeywords: keySet(valueKeywords), + hooks: { + "<": function(stream, state) { + function tokenSGMLComment(stream, state) { + var dashes = 0, ch; + while ((ch = stream.next()) != null) { + if (dashes >= 2 && ch == ">") { + state.tokenize = null; + break; + } + dashes = (ch == "-") ? dashes + 1 : 0; + } + return ["comment", "comment"]; + } + if (stream.eat("!")) { + state.tokenize = tokenSGMLComment; + return tokenSGMLComment(stream, state); + } + }, + '/': function(stream, state) { + if (stream.eat("*")) { + state.tokenize = tokenCComment; + return tokenCComment(stream, state); + } + } + }, + name: "css" + }); + + mimes(['text/x-scss', 'scss'], { + atMediaTypes: keySet(atMediaTypes), + atMediaFeatures: keySet(atMediaFeatures), + propertyKeywords: keySet(propertyKeywords), + colorKeywords: keySet(colorKeywords), + valueKeywords: keySet(valueKeywords), + allowNested: true, + hooks: { + "$": function(stream) { + stream.match(/^[\w-]+/); + if (stream.peek() == ':') { + return ["variable", "variable-definition"]; + } + return ["variable", "variable"]; + }, + '/': function(stream, state) { + if (stream.eat('/')) { + stream.skipToEnd(); + return ['comment', 'comment']; + } else if (stream.eat('*')) { + state.tokenize = tokenCComment; + return tokenCComment(stream, state); + } else { + return ["operator", "operator"]; + } + }, + '#': function(stream) { + if (stream.eat('{')) { + return ["operator", "interpolation"]; + } else { + stream.eatWhile(/[\w\\\-]/); + return ["atom", "hash"]; + } + } + }, + name: "css" + }); +})(); diff --git a/mode/css/scss.html b/mode/css/scss.html new file mode 100644 index 0000000000..b67d7ff28a --- /dev/null +++ b/mode/css/scss.html @@ -0,0 +1,142 @@ + + + + + CodeMirror: SCSS mode + + + + + + + +

    CodeMirror: SCSS mode

    +
    + + +

    MIME types defined: text/scss.

    + +

    Parsing/Highlighting Tests: normal, verbose.

    + + + diff --git a/mode/css/scss_test.js b/mode/css/scss_test.js new file mode 100644 index 0000000000..a296e56fe6 --- /dev/null +++ b/mode/css/scss_test.js @@ -0,0 +1,77 @@ +(function() { + var mode = CodeMirror.getMode({tabSize: 4}, "text/x-scss"); + function MT(name) { test.mode(name, mode, Array.prototype.slice.call(arguments, 1)); } + + MT('url_with_quotation', + "[tag foo] { [property background][operator :][string-2 url]([string test.jpg]) }"); + + MT('url_with_double_quotes', + "[tag foo] { [property background][operator :][string-2 url]([string \"test.jpg\"]) }"); + + MT('url_with_single_quotes', + "[tag foo] { [property background][operator :][string-2 url]([string \'test.jpg\']) }"); + + MT('string', + "[def @import] [string \"compass/css3\"]"); + + MT('important_keyword', + "[tag foo] { [property background][operator :][string-2 url]([string \'test.jpg\']) [keyword !important] }"); + + MT('variable', + "[variable-2 $blue][operator :][atom #333]"); + + MT('variable_as_attribute', + "[tag foo] { [property color][operator :][variable-2 $blue] }"); + + MT('numbers', + "[tag foo] { [property padding][operator :][number 10px] [number 10] [number 10em] [number 8in] }"); + + MT('number_percentage', + "[tag foo] { [property width][operator :][number 80%] }"); + + MT('selector', + "[builtin #hello][qualifier .world]{}"); + + MT('singleline_comment', + "[comment // this is a comment]"); + + MT('multiline_comment', + "[comment /*foobar*/]"); + + MT('attribute_with_hyphen', + "[tag foo] { [property font-size][operator :][number 10px] }"); + + MT('string_after_attribute', + "[tag foo] { [property content][operator :][string \"::\"] }"); + + MT('directives', + "[def @include] [qualifier .mixin]"); + + MT('basic_structure', + "[tag p] { [property background][operator :][keyword red]; }"); + + MT('nested_structure', + "[tag p] { [tag a] { [property color][operator :][keyword red]; } }"); + + MT('mixin', + "[def @mixin] [tag table-base] {}"); + + MT('number_without_semicolon', + "[tag p] {[property width][operator :][number 12]}", + "[tag a] {[property color][operator :][keyword red];}"); + + MT('atom_in_nested_block', + "[tag p] { [tag a] { [property color][operator :][atom #000]; } }"); + + MT('interpolation_in_property', + "[tag foo] { [operator #{][variable-2 $hello][operator }:][atom #000]; }"); + + MT('interpolation_in_selector', + "[tag foo][operator #{][variable-2 $hello][operator }] { [property color][operator :][atom #000]; }"); + + MT('interpolation_error', + "[tag foo][operator #{][error foo][operator }] { [property color][operator :][atom #000]; }"); + + MT("divide_operator", + "[tag foo] { [property width][operator :][number 4] [operator /] [number 2] }"); +})(); diff --git a/mode/css/test.js b/mode/css/test.js index 4336ddabdf..0f68deb1c4 100644 --- a/mode/css/test.js +++ b/mode/css/test.js @@ -1,6 +1,6 @@ (function() { var mode = CodeMirror.getMode({tabSize: 4}, "css"); - function MT(name) { test.mode(name, mode, Array.prototype.slice.call(arguments, 2)); } + function MT(name) { test.mode(name, mode, Array.prototype.slice.call(arguments, 1)); } // Requires at least one media query MT("atMediaEmpty", @@ -103,4 +103,7 @@ MT("tagTwoProperties", "[tag foo] { [property margin][operator :] [number 0]; [property padding][operator :] [number 0]; }"); + + MT("commentSGML", + "[comment ]"); })(); diff --git a/test/index.html b/test/index.html index 24bdf999d8..0a0da5bd87 100644 --- a/test/index.html +++ b/test/index.html @@ -56,6 +56,7 @@

    CodeMirror: Test Suite

    + From eb13244c5afdc86cd3803c6f83619bda5f53d287 Mon Sep 17 00:00:00 2001 From: Marijn Haverbeke Date: Tue, 5 Mar 2013 10:56:56 +0100 Subject: [PATCH 0814/5780] [scss mode] Integrate --- doc/modes.html | 1 + mode/css/css.js | 78 +++++++++++++++++++++---------------------- mode/css/scss_test.js | 2 +- mode/meta.js | 1 + test/mode_test.js | 4 +-- 5 files changed, 44 insertions(+), 42 deletions(-) diff --git a/doc/modes.html b/doc/modes.html index 927ef27dca..c3d16059ac 100644 --- a/doc/modes.html +++ b/doc/modes.html @@ -65,6 +65,7 @@

    { } CodeMi
  • Sass
  • Scala
  • Scheme
  • +
  • SCSS
  • Shell
  • Sieve
  • Smalltalk
  • diff --git a/mode/css/css.js b/mode/css/css.js index 7cdd18b3fc..058ae42036 100644 --- a/mode/css/css.js +++ b/mode/css/css.js @@ -1,5 +1,10 @@ -CodeMirror.defineMode("css", function(config, parserConfig) { +CodeMirror.defineMode("css", function(config) { + return CodeMirror.getMode(config, "text/css"); +}); + +CodeMirror.defineMode("css-base", function(config, parserConfig) { "use strict"; + var indentUnit = config.indentUnit, hooks = parserConfig.hooks || {}, atMediaTypes = parserConfig.atMediaTypes || {}, @@ -282,12 +287,6 @@ CodeMirror.defineMode("css", function(config, parserConfig) { }); (function() { - function mimes(ms, mode) { - for (var i = 0; i < ms.length; ++i) { - CodeMirror.defineMIME(ms[i], mode); - } - } - function keySet(array) { var keys = {}; for (var i = 0; i < array.length; ++i) { @@ -296,12 +295,12 @@ CodeMirror.defineMode("css", function(config, parserConfig) { return keys; } - var atMediaTypes = [ + var atMediaTypes = keySet([ "all", "aural", "braille", "handheld", "print", "projection", "screen", "tty", "tv", "embossed" - ]; + ]); - var atMediaFeatures = [ + var atMediaFeatures = keySet([ "width", "min-width", "max-width", "height", "min-height", "max-height", "device-width", "min-device-width", "max-device-width", "device-height", "min-device-height", "max-device-height", "aspect-ratio", @@ -310,9 +309,9 @@ CodeMirror.defineMode("css", function(config, parserConfig) { "max-color", "color-index", "min-color-index", "max-color-index", "monochrome", "min-monochrome", "max-monochrome", "resolution", "min-resolution", "max-resolution", "scan", "grid" - ]; + ]); - var propertyKeywords = [ + var propertyKeywords = keySet([ "align-content", "align-items", "align-self", "alignment-adjust", "alignment-baseline", "anchor-point", "animation", "animation-delay", "animation-direction", "animation-duration", "animation-iteration-count", @@ -388,14 +387,14 @@ CodeMirror.defineMode("css", function(config, parserConfig) { "voice-family", "voice-pitch", "voice-range", "voice-rate", "voice-stress", "voice-volume", "volume", "white-space", "widows", "width", "word-break", "word-spacing", "word-wrap", "z-index" - ]; + ]); - var colorKeywords = [ + var colorKeywords = keySet([ "black", "silver", "gray", "white", "maroon", "red", "purple", "fuchsia", "green", "lime", "olive", "yellow", "navy", "blue", "teal", "aqua" - ]; + ]); - var valueKeywords = [ + var valueKeywords = keySet([ "above", "absolute", "activeborder", "activecaption", "afar", "after-white-space", "ahead", "alias", "all", "all-scroll", "alternate", "always", "amharic", "amharic-abegede", "antialiased", "appworkspace", @@ -478,7 +477,7 @@ CodeMirror.defineMode("css", function(config, parserConfig) { "visibleStroke", "visual", "w-resize", "wait", "wave", "white", "wider", "window", "windowframe", "windowtext", "x-large", "x-small", "xor", "xx-large", "xx-small", "yellow" - ]; + ]); function tokenCComment(stream, state) { var maybeEnd = false, ch; @@ -492,12 +491,12 @@ CodeMirror.defineMode("css", function(config, parserConfig) { return ["comment", "comment"]; } - mimes(['text/css', 'css'], { - atMediaTypes: keySet(atMediaTypes), - atMediaFeatures: keySet(atMediaFeatures), - propertyKeywords: keySet(propertyKeywords), - colorKeywords: keySet(colorKeywords), - valueKeywords: keySet(valueKeywords), + CodeMirror.defineMIME("text/css", { + atMediaTypes: atMediaTypes, + atMediaFeatures: atMediaFeatures, + propertyKeywords: propertyKeywords, + colorKeywords: colorKeywords, + valueKeywords: valueKeywords, hooks: { "<": function(stream, state) { function tokenSGMLComment(stream, state) { @@ -516,44 +515,45 @@ CodeMirror.defineMode("css", function(config, parserConfig) { return tokenSGMLComment(stream, state); } }, - '/': function(stream, state) { + "/": function(stream, state) { if (stream.eat("*")) { state.tokenize = tokenCComment; return tokenCComment(stream, state); } + return false; } }, - name: "css" + name: "css-base" }); - mimes(['text/x-scss', 'scss'], { - atMediaTypes: keySet(atMediaTypes), - atMediaFeatures: keySet(atMediaFeatures), - propertyKeywords: keySet(propertyKeywords), - colorKeywords: keySet(colorKeywords), - valueKeywords: keySet(valueKeywords), + CodeMirror.defineMIME("text/x-scss", { + atMediaTypes: atMediaTypes, + atMediaFeatures: atMediaFeatures, + propertyKeywords: propertyKeywords, + colorKeywords: colorKeywords, + valueKeywords: valueKeywords, allowNested: true, hooks: { "$": function(stream) { stream.match(/^[\w-]+/); - if (stream.peek() == ':') { + if (stream.peek() == ":") { return ["variable", "variable-definition"]; } return ["variable", "variable"]; }, - '/': function(stream, state) { - if (stream.eat('/')) { + "/": function(stream, state) { + if (stream.eat("/")) { stream.skipToEnd(); - return ['comment', 'comment']; - } else if (stream.eat('*')) { + return ["comment", "comment"]; + } else if (stream.eat("*")) { state.tokenize = tokenCComment; return tokenCComment(stream, state); } else { return ["operator", "operator"]; } }, - '#': function(stream) { - if (stream.eat('{')) { + "#": function(stream) { + if (stream.eat("{")) { return ["operator", "interpolation"]; } else { stream.eatWhile(/[\w\\\-]/); @@ -561,6 +561,6 @@ CodeMirror.defineMode("css", function(config, parserConfig) { } } }, - name: "css" + name: "css-base" }); })(); diff --git a/mode/css/scss_test.js b/mode/css/scss_test.js index a296e56fe6..330b0a6a5b 100644 --- a/mode/css/scss_test.js +++ b/mode/css/scss_test.js @@ -1,6 +1,6 @@ (function() { var mode = CodeMirror.getMode({tabSize: 4}, "text/x-scss"); - function MT(name) { test.mode(name, mode, Array.prototype.slice.call(arguments, 1)); } + function MT(name) { test.mode(name, mode, Array.prototype.slice.call(arguments, 1), "scss"); } MT('url_with_quotation', "[tag foo] { [property background][operator :][string-2 url]([string test.jpg]) }"); diff --git a/mode/meta.js b/mode/meta.js index 7ac3f181d0..cf8a7ec3ea 100644 --- a/mode/meta.js +++ b/mode/meta.js @@ -48,6 +48,7 @@ CodeMirror.modeInfo = [ {name: 'Rust', mime: 'text/x-rustsrc', mode: 'rust'}, {name: 'Sass', mime: 'text/x-sass', mode: 'sass'}, {name: 'Scheme', mime: 'text/x-scheme', mode: 'scheme'}, + {name: 'SCSS', mime: 'text/x-scss', mode: 'css'}, {name: 'Shell', mime: 'text/x-sh', mode: 'shell'}, {name: 'Sieve', mime: 'application/sieve', mode: 'sieve'}, {name: 'Smalltalk', mime: 'text/x-stsrc', mode: 'smalltalk'}, diff --git a/test/mode_test.js b/test/mode_test.js index 51e51b9525..79a7752492 100644 --- a/test/mode_test.js +++ b/test/mode_test.js @@ -59,9 +59,9 @@ return {tokens: tokens, plain: plain}; } - test.mode = function(name, mode, tokens) { + test.mode = function(name, mode, tokens, modeName) { var data = parseTokens(tokens); - return test(mode.name + "_" + name, function() { + return test((modeName || mode.name) + "_" + name, function() { return compare(data.plain, data.tokens, mode); }); }; From bfea2717d18bca74944b36d08d8984177a05b9c4 Mon Sep 17 00:00:00 2001 From: Tom MacWright Date: Tue, 5 Mar 2013 22:04:55 -0500 Subject: [PATCH 0815/5780] Correct path of package from codemirror.js to lib/codemirror.js Ran into this when trying to include CodeMirror from Browserify - the path of `codemirror.js` doesn't point to the necessary `lib/codemirror.js` file. --- package.json | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/package.json b/package.json index 2799b0a5e3..eb8f2ba07d 100644 --- a/package.json +++ b/package.json @@ -1,7 +1,7 @@ { "name": "codemirror", "version":"3.10.01", - "main": "codemirror.js", + "main": "lib/codemirror.js", "description": "In-browser code editing made bearable", "licenses": [{"type": "MIT", "url": "http://codemirror.net/LICENSE"}], From c65ee3ed62c60cedcd02a314187619587caf0881 Mon Sep 17 00:00:00 2001 From: flack Date: Tue, 5 Mar 2013 15:56:31 +0100 Subject: [PATCH 0816/5780] Fix 1-2 pixel jumps when brackets are highlighted With the eclipse theme, if you move your cursor on a bracket and have the matching bracket option enabled, spans with borders are inserted. They push the rest of the line to the right by the amount of the border width, which looks very crappy. Using outline instead of border fixes this --- theme/eclipse.css | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/theme/eclipse.css b/theme/eclipse.css index 47d66a012e..4807e45156 100644 --- a/theme/eclipse.css +++ b/theme/eclipse.css @@ -20,6 +20,6 @@ .cm-s-eclipse span.cm-link {color: #219;} .cm-s-eclipse .CodeMirror-matchingbracket { - border:1px solid grey; + outline:1px solid grey; color:black !important;; } From ec8438df11c0207dd3fed27817fc934803f88d1f Mon Sep 17 00:00:00 2001 From: Chris Coyier Date: Fri, 1 Mar 2013 08:36:28 -0800 Subject: [PATCH 0817/5780] iOS keyboard opening fix --- lib/codemirror.js | 4 ++++ 1 file changed, 4 insertions(+) diff --git a/lib/codemirror.js b/lib/codemirror.js index 442ba8a62f..801ddb07c3 100644 --- a/lib/codemirror.js +++ b/lib/codemirror.js @@ -91,10 +91,14 @@ window.CodeMirror = (function() { function makeDisplay(place, docStart) { var d = {}; + var input = d.input = elt("textarea", null, null, "position: absolute; padding: 0; width: 1px; height: 1em; outline: none;"); if (webkit) input.style.width = "1000px"; else input.setAttribute("wrap", "off"); + // if border: 0; -- iOS fails to open keyboard (issue #1287) + if (ios) input.style.border = "1px solid black"; input.setAttribute("autocorrect", "off"); input.setAttribute("autocapitalize", "off"); + // Wraps and hides input textarea d.inputDiv = elt("div", [input], null, "overflow: hidden; position: relative; width: 3px; height: 0px;"); // The actual fake scrollbars. From d07f7a9c8340a9e2a4943e947fb483733c90c87b Mon Sep 17 00:00:00 2001 From: Marijn Haverbeke Date: Wed, 6 Mar 2013 08:26:39 +0100 Subject: [PATCH 0818/5780] M-x delete-trailing-whitespace (lib/codemirror.js) --- lib/codemirror.js | 32 ++++++++++++++++---------------- 1 file changed, 16 insertions(+), 16 deletions(-) diff --git a/lib/codemirror.js b/lib/codemirror.js index 801ddb07c3..10888bac1f 100644 --- a/lib/codemirror.js +++ b/lib/codemirror.js @@ -39,7 +39,7 @@ window.CodeMirror = (function() { function CodeMirror(place, options) { if (!(this instanceof CodeMirror)) return new CodeMirror(place, options); - + this.options = options = options || {}; // Determine effective options based on given values and defaults. for (var opt in defaults) if (!options.hasOwnProperty(opt) && defaults.hasOwnProperty(opt)) @@ -183,7 +183,7 @@ window.CodeMirror = (function() { // Used for measuring wheel scrolling granularity d.wheelDX = d.wheelDY = d.wheelStartX = d.wheelStartY = null; - + return d; } @@ -328,7 +328,7 @@ window.CodeMirror = (function() { if (needsV) { d.scrollbarV.style.display = "block"; d.scrollbarV.style.bottom = needsH ? scrollbarWidth(d.measure) + "px" : "0"; - d.scrollbarV.firstChild.style.height = + d.scrollbarV.firstChild.style.height = (scrollHeight - d.scroller.clientHeight + d.scrollbarV.clientHeight) + "px"; } else d.scrollbarV.style.display = ""; if (needsH) { @@ -638,7 +638,7 @@ window.CodeMirror = (function() { // Lines with gutter elements, widgets or a background class need // to be wrapped again, and have the extra elements added to the // wrapper div - + if (reuse) { reuse.alignable = null; var isOk = true, widgetsSeen = 0; @@ -931,7 +931,7 @@ window.CodeMirror = (function() { } // POSITION MEASUREMENT - + function paddingTop(display) {return display.lineSpace.offsetTop;} function paddingLeft(display) { var e = removeChildrenAndAdd(display.measure, elt("pre", null, null, "text-align: left")).appendChild(elt("span", "x")); @@ -941,7 +941,7 @@ window.CodeMirror = (function() { function measureChar(cm, line, ch, data) { var dir = -1; data = data || measureLine(cm, line); - + for (var pos = ch;; pos += dir) { var r = data[pos]; if (r) break; @@ -1385,7 +1385,7 @@ window.CodeMirror = (function() { var updateInput = cm.curOp.updateInput; makeChange(cm.doc, {from: from, to: to, text: splitLines(text.slice(same)), origin: cm.state.pasteIncoming ? "paste" : "+input"}, "end"); - + cm.curOp.updateInput = updateInput; if (text.length > 1000) input.value = cm.display.prevInput = ""; else cm.display.prevInput = text; @@ -1465,7 +1465,7 @@ window.CodeMirror = (function() { for (var p = d.wrapper.parentNode; p && p != document.body; p = p.parentNode) {} if (p) setTimeout(unregister, 5000); else off(window, "resize", onResize); - } + } setTimeout(unregister, 5000); on(d.input, "keyup", operation(cm, function(e) { @@ -1490,7 +1490,7 @@ window.CodeMirror = (function() { } on(d.scroller, "paste", function(e){ if (eventInWidget(d, e)) return; - focusInput(cm); + focusInput(cm); fastPoll(cm); }); on(d.input, "paste", function() { @@ -1748,7 +1748,7 @@ window.CodeMirror = (function() { function onDragStart(cm, e) { if (eventInWidget(cm.display, e)) return; - + var txt = cm.getSelection(); e.dataTransfer.setData("Text", txt); @@ -2032,7 +2032,7 @@ window.CodeMirror = (function() { if (ie_lt9) display.scrollbarV.scrollTop = display.scroller.scrollTop = scrollPos; slowPoll(cm); - // Try to detect the user choosing select-all + // Try to detect the user choosing select-all if (display.input.selectionStart != null && (!ie || ie_lt9)) { clearTimeout(detectingSelectAll); var extval = display.input.value = " " + (posEq(sel.from, sel.to) ? "" : display.input.value), i = 0; @@ -2090,7 +2090,7 @@ window.CodeMirror = (function() { head: clipPostChange(doc, change, hint.head)}; if (hint == "start") return {anchor: change.from, head: change.from}; - + var end = changeEnd(change); if (hint == "around") return {anchor: change.from, head: end}; if (hint == "end") return {anchor: end, head: end}; @@ -3018,7 +3018,7 @@ window.CodeMirror = (function() { option("firstLineNumber", 1, guttersChanged, true); option("lineNumberFormatter", function(integer) {return integer;}, guttersChanged, true); option("showCursorWhenSelecting", false, updateSelection, true); - + option("readOnly", false, function(cm, val) { if (val == "nocursor") {onBlur(cm); cm.display.input.blur();} else if (!val) resetInput(cm, true); @@ -4353,7 +4353,7 @@ window.CodeMirror = (function() { var Doc = CodeMirror.Doc = function(text, mode, firstLine) { if (!(this instanceof Doc)) return new Doc(text, mode, firstLine); if (firstLine == null) firstLine = 0; - + BranchChunk.call(this, [new LeafChunk([makeLine("", null)])]); this.first = firstLine; this.scrollTop = this.scrollLeft = 0; @@ -4465,7 +4465,7 @@ window.CodeMirror = (function() { this.history.lastOp = this.history.lastOrigin = null; }, isClean: function () {return this.history.dirtyCounter == 0;}, - + getHistory: function() { return {done: copyHistoryArray(this.history.done), undone: copyHistoryArray(this.history.undone)}; @@ -4760,7 +4760,7 @@ window.CodeMirror = (function() { while (hist.done.length > hist.undoDepth) hist.done.shift(); if (hist.dirtyCounter < 0) - // The user has made a change after undoing past the last clean state. + // The user has made a change after undoing past the last clean state. // We can never get back to a clean state now until markClean() is called. hist.dirtyCounter = NaN; else From f60fb21c0ede7d44cbd4050305a9ee770e8e632a Mon Sep 17 00:00:00 2001 From: Marijn Haverbeke Date: Wed, 6 Mar 2013 08:33:04 +0100 Subject: [PATCH 0819/5780] Make cursor sit above text, give fat cursor a specific z-index to sit behind it Issue #1317 --- lib/codemirror.css | 10 +++------- 1 file changed, 3 insertions(+), 7 deletions(-) diff --git a/lib/codemirror.css b/lib/codemirror.css index 9359cd2996..e36fbeb88f 100644 --- a/lib/codemirror.css +++ b/lib/codemirror.css @@ -41,6 +41,7 @@ .CodeMirror div.CodeMirror-cursor { border-left: 1px solid black; + z-index: 3; } /* Shown when moving in bi-directional text */ .CodeMirror div.CodeMirror-secondarycursor { @@ -49,13 +50,8 @@ .CodeMirror.cm-keymap-fat-cursor div.CodeMirror-cursor { width: auto; border: 0; - background: transparent; - background: rgba(0, 200, 0, .4); - filter: progid:DXImageTransform.Microsoft.gradient(startColorstr=#6600c800, endColorstr=#4c00c800); -} -/* Kludge to turn off filter in ie9+, which also accepts rgba */ -.CodeMirror.cm-keymap-fat-cursor div.CodeMirror-cursor:not(#nonsense_id) { - filter: progid:DXImageTransform.Microsoft.gradient(enabled=false); + background: #7e7; + z-index: 1; } /* Can style cursor different in overwrite (non-insert) mode */ .CodeMirror div.CodeMirror-cursor.CodeMirror-overwrite {} From d57f4cdcad4c2056d5a41bdc41de6caf0f24b9e8 Mon Sep 17 00:00:00 2001 From: Miguel Castillo Date: Wed, 6 Mar 2013 09:03:24 -0500 Subject: [PATCH 0820/5780] [javascript mode] Make 'this' a keyword --- mode/javascript/javascript.js | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-) diff --git a/mode/javascript/javascript.js b/mode/javascript/javascript.js index b6d4e6b451..8c25070408 100644 --- a/mode/javascript/javascript.js +++ b/mode/javascript/javascript.js @@ -19,7 +19,8 @@ CodeMirror.defineMode("javascript", function(config, parserConfig) { "function": kw("function"), "catch": kw("catch"), "for": kw("for"), "switch": kw("switch"), "case": kw("case"), "default": kw("default"), "in": operator, "typeof": operator, "instanceof": operator, - "true": atom, "false": atom, "null": atom, "undefined": atom, "NaN": atom, "Infinity": atom + "true": atom, "false": atom, "null": atom, "undefined": atom, "NaN": atom, "Infinity": atom, + "this": kw("this") }; // Extend the 'normal' keywords with the TypeScript language extensions From 6ad9552d0aa1cfccd12a458af7b3ff40ec7af676 Mon Sep 17 00:00:00 2001 From: Marijn Haverbeke Date: Thu, 7 Mar 2013 09:01:02 +0100 Subject: [PATCH 0821/5780] [javascript mode] Make parser aware of 'this' keyword --- mode/javascript/javascript.js | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/mode/javascript/javascript.js b/mode/javascript/javascript.js index 8c25070408..fcb9ffc22c 100644 --- a/mode/javascript/javascript.js +++ b/mode/javascript/javascript.js @@ -149,7 +149,7 @@ CodeMirror.defineMode("javascript", function(config, parserConfig) { // Parser - var atomicTypes = {"atom": true, "number": true, "variable": true, "string": true, "regexp": true}; + var atomicTypes = {"atom": true, "number": true, "variable": true, "string": true, "regexp": true, "this": true}; function JSLexical(indented, column, type, align, prev, info) { this.indented = indented; From 55fc0ac71e820418a75d52238c2bab3533dddbbb Mon Sep 17 00:00:00 2001 From: edsharp Date: Wed, 6 Mar 2013 10:01:34 +0000 Subject: [PATCH 0822/5780] [xml-hint addon] Recognize multi-line tags MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Allows correct hinting even when the tag is multi-line by achieving a dot matches all effect by using [\s\S] instead. --- addon/hint/xml-hint.js | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/addon/hint/xml-hint.js b/addon/hint/xml-hint.js index 1609aafd40..42eab6f3b7 100644 --- a/addon/hint/xml-hint.js +++ b/addon/hint/xml-hint.js @@ -50,7 +50,7 @@ if(text.length >= 0) { - var regex = new RegExp('<([^!?][^\\s/>]*).*?>', 'g'); + var regex = new RegExp('<([^!?][^\\s/>]*)[\\s\\S]*?>', 'g'); var matches = []; var match; From 3a359da33aeed970f2621823fa3a5a80db025d98 Mon Sep 17 00:00:00 2001 From: Hasan Karahan Date: Wed, 6 Mar 2013 17:43:41 +0500 Subject: [PATCH 0823/5780] Fixed minor bug: HTTP path with commas now properly highlighted --- mode/rst/rst.js | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/mode/rst/rst.js b/mode/rst/rst.js index abbd79a93b..5fed967a67 100644 --- a/mode/rst/rst.js +++ b/mode/rst/rst.js @@ -496,7 +496,7 @@ CodeMirror.defineMode('rst', function (config, options) { var rx_uri_protocol = "[Hh][Tt][Tt][Pp][Ss]?://"; var rx_uri_domain = "(?:[\\d\\w.-]+)\\.(?:\\w{2,6})"; - var rx_uri_path = "(?:/[\\d\\w\\#\\%\\&\\-\\.\\/\\:\\=\\?\\~]+)*"; + var rx_uri_path = "(?:/[\\d\\w\\#\\%\\&\\-\\.\\,\\/\\:\\=\\?\\~]+)*"; var rx_uri = new RegExp("^" + rx_uri_protocol + rx_uri_domain + rx_uri_path ); From 15600db901199b4e04125e2627a1c7e8bc425983 Mon Sep 17 00:00:00 2001 From: Marijn Haverbeke Date: Thu, 7 Mar 2013 09:10:46 +0100 Subject: [PATCH 0824/5780] [css mode] Fix bug introduced by recent refactoring The state.tokenize property could return an array, but the token method didn't count on that. Closes #1326 --- mode/css/css.js | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-) diff --git a/mode/css/css.js b/mode/css/css.js index 058ae42036..00d605a69d 100644 --- a/mode/css/css.js +++ b/mode/css/css.js @@ -22,7 +22,7 @@ CodeMirror.defineMode("css-base", function(config, parserConfig) { if (hooks[ch]) { // result[0] is style and result[1] is type var result = hooks[ch](stream, state); - if (result !== false) return ret(result[0], result[1]); + if (result !== false) return result; } if (ch == "@") {stream.eatWhile(/[\w\\\-]/); return ret("def", stream.current());} else if (ch == "=") ret(null, "compare"); @@ -157,6 +157,7 @@ CodeMirror.defineMode("css-base", function(config, parserConfig) { state.tokenize = state.tokenize || tokenBase; if (state.tokenize == tokenBase && stream.eatSpace()) return null; var style = state.tokenize(stream, state); + if (style && typeof style != "string") style = ret(style[0], style[1]); // Changing style returned based on context var context = state.stack[state.stack.length-1]; From 41c156f44903bded870bee8261d5b77bea4c5f10 Mon Sep 17 00:00:00 2001 From: Marijn Haverbeke Date: Thu, 7 Mar 2013 09:22:01 +0100 Subject: [PATCH 0825/5780] Make sure shared marker's widgets are cloned --- lib/codemirror.js | 2 ++ 1 file changed, 2 insertions(+) diff --git a/lib/codemirror.js b/lib/codemirror.js index 10888bac1f..a95d2bcb51 100644 --- a/lib/codemirror.js +++ b/lib/codemirror.js @@ -3587,7 +3587,9 @@ window.CodeMirror = (function() { options = copyObj(options); options.shared = false; var markers = [markText(doc, from, to, options, type)], primary = markers[0]; + var widget = options.replacedWith; linkedDocs(doc, function(doc) { + if (widget) options.replacedWith = widget.cloneNode(true); markers.push(markText(doc, clipPos(doc, from), clipPos(doc, to), options, type)); for (var i = 0; i < doc.linked.length; ++i) if (doc.linked[i].isParent) return; From e5df8cc3ae1bde4b547bea879c78154189ebe890 Mon Sep 17 00:00:00 2001 From: Marijn Haverbeke Date: Thu, 7 Mar 2013 16:00:54 +0100 Subject: [PATCH 0826/5780] Fix spanAffectsWrapping for new Safari versions --- demo/spanaffectswrapping_shim.html | 73 ++++++++++++++++++++++++++++++ lib/codemirror.js | 4 +- 2 files changed, 75 insertions(+), 2 deletions(-) create mode 100644 demo/spanaffectswrapping_shim.html diff --git a/demo/spanaffectswrapping_shim.html b/demo/spanaffectswrapping_shim.html new file mode 100644 index 0000000000..2d43eec05b --- /dev/null +++ b/demo/spanaffectswrapping_shim.html @@ -0,0 +1,73 @@ + + + + + CodeMirror: Automatically derive odd wrapping behavior for your browser + + + +

    CodeMirror: odd wrapping shim

    + +

    This is a hack to automatically derive + a spanAffectsWrapping regexp for a browser. See the + comments above that variable + in lib/codemirror.js + for some more details.

    + +
    +
    
    +
    +    
    +  
    +
    diff --git a/lib/codemirror.js b/lib/codemirror.js
    index a95d2bcb51..562ed009ee 100644
    --- a/lib/codemirror.js
    +++ b/lib/codemirror.js
    @@ -5102,8 +5102,8 @@ window.CodeMirror = (function() {
       // various browsers.
       var spanAffectsWrapping = /^$/; // Won't match any two-character string
       if (gecko) spanAffectsWrapping = /$'/;
    -  else if (safari) spanAffectsWrapping = /\-[^ \-?]|\?[^ !'\"\),.\-\/:;\?\]\}]/;
    -  else if (chrome) spanAffectsWrapping = /\-[^ \-\.?]|\?[^ \-\.?\]\}:;!'\"\),\/]|[\.!\"#&%\)*+,:;=>\]|\}~][\(\{\[<]|\$'/;
    +  else if (safari && !/Version\/([6-9]|\d\d)\b/.test(navigator.userAgent)) spanAffectsWrapping = /\-[^ \-?]|\?[^ !'\"\),.\-\/:;\?\]\}]/;
    +  else if (webkit) spanAffectsWrapping = /[~!#%&*)=+}\]|\"\.>,][({[<]|-[^\-?\.]|\?[^!)\-}\]\'\"\/?\.,]/;
     
       var knownScrollbarWidth;
       function scrollbarWidth(measure) {
    
    From 04d8fc6e48b9214a08d806e0fe8acbe7b119045c Mon Sep 17 00:00:00 2001
    From: Marijn Haverbeke 
    Date: Thu, 7 Mar 2013 17:23:15 +0100
    Subject: [PATCH 0827/5780] Another tweak to the spanAffectsWrapping regexp
    
    ---
     demo/spanaffectswrapping_shim.html | 2 +-
     lib/codemirror.js                  | 2 +-
     2 files changed, 2 insertions(+), 2 deletions(-)
    
    diff --git a/demo/spanaffectswrapping_shim.html b/demo/spanaffectswrapping_shim.html
    index 2d43eec05b..733db067ff 100644
    --- a/demo/spanaffectswrapping_shim.html
    +++ b/demo/spanaffectswrapping_shim.html
    @@ -19,7 +19,7 @@ 

    CodeMirror: odd wrapping shim

    + + + + + + +

    CodeMirror: Indented wrapped line demo

    + +
    + +

    This page uses a hack on top of the "renderLine" + event to make wrapped text line up with the base indentation of + the line.

    + + + + + diff --git a/demo/visibletabs.html b/demo/visibletabs.html index ddc12fb46a..109d1a6b08 100644 --- a/demo/visibletabs.html +++ b/demo/visibletabs.html @@ -37,7 +37,7 @@

    CodeMirror: Visible tabs demo

    Tabs inside the editor are spans with the -class cm-tab, and can be styled. +class cm-tab, and can be styled.

    + + + + + + +

    CodeMirror: LiveScript mode

    +
    + + +

    MIME types defined: text/x-livescript.

    + +

    The LiveScript mode was written by Kenneth Bentley (license).

    + + + diff --git a/mode/livescript/livescript.js b/mode/livescript/livescript.js new file mode 100644 index 0000000000..96d8813334 --- /dev/null +++ b/mode/livescript/livescript.js @@ -0,0 +1,265 @@ +/** + * Link to the project's GitHub page: + * https://github.com/duralog/CodeMirror + */ +var indenter, identifier, keywordend, stringfill, Rules, idx, r, i$, len$, i, rr; +CodeMirror.defineMode('livescript', function(conf){ + var tokenBase, external; + tokenBase = function(stream, state){ + var next_rule, nr, i$, len$, r, m; + if (next_rule = state.next || 'start') { + state.next = state.next; + if (Array.isArray(nr = Rules[next_rule])) { + for (i$ = 0, len$ = nr.length; i$ < len$; ++i$) { + r = nr[i$]; + if (r.regex && (m = stream.match(r.regex))) { + state.next = r.next; + return r.token; + } + } + stream.next(); + return 'error'; + } + if (stream.match(r = Rules[next_rule])) { + if (r.regex && stream.match(r.regex)) { + state.next = r.next; + return r.token; + } else { + stream.next(); + return 'error'; + } + } + } + stream.next(); + return 'error'; + }; + external = { + startState: function(basecolumn){ + return { + next: 'start', + lastToken: null + }; + }, + token: function(stream, state){ + var style; + style = tokenBase(stream, state); + state.lastToken = { + style: style, + indent: stream.indentation(), + content: stream.current() + }; + return style.replace(/\./g, ' '); + }, + indent: function(state, textAfter){ + var indentation; + indentation = state.lastToken.indent; + if (state.lastToken.content.match(indenter)) { + indentation += 2; + } + return indentation; + } + }; + return external; +}); +indenter = RegExp('(?:[({[=:]|[-~]>|\\b(?:e(?:lse|xport)|d(?:o|efault)|t(?:ry|hen)|finally|import(?:\\s*all)?|const|var|let|new|catch(?:\\s*' + identifier + ')?))\\s*$'); +identifier = '(?![\\d\\s])[$\\w\\xAA-\\uFFDC](?:(?!\\s)[$\\w\\xAA-\\uFFDC]|-[A-Za-z])*'; +keywordend = '(?![$\\w]|-[A-Za-z]|\\s*:(?![:=]))'; +stringfill = { + token: 'string', + regex: '.+' +}; +Rules = { + start: [ + { + token: 'comment.doc', + regex: '/\\*', + next: 'comment' + }, { + token: 'comment', + regex: '#.*' + }, { + token: 'keyword', + regex: '(?:t(?:h(?:is|row|en)|ry|ypeof!?)|c(?:on(?:tinue|st)|a(?:se|tch)|lass)|i(?:n(?:stanceof)?|mp(?:ort(?:\\s+all)?|lements)|[fs])|d(?:e(?:fault|lete|bugger)|o)|f(?:or(?:\\s+own)?|inally|unction)|s(?:uper|witch)|e(?:lse|x(?:tends|port)|val)|a(?:nd|rguments)|n(?:ew|ot)|un(?:less|til)|w(?:hile|ith)|o[fr]|return|break|let|var|loop)' + keywordend + }, { + token: 'constant.language', + regex: '(?:true|false|yes|no|on|off|null|void|undefined)' + keywordend + }, { + token: 'invalid.illegal', + regex: '(?:p(?:ackage|r(?:ivate|otected)|ublic)|i(?:mplements|nterface)|enum|static|yield)' + keywordend + }, { + token: 'language.support.class', + regex: '(?:R(?:e(?:gExp|ferenceError)|angeError)|S(?:tring|yntaxError)|E(?:rror|valError)|Array|Boolean|Date|Function|Number|Object|TypeError|URIError)' + keywordend + }, { + token: 'language.support.function', + regex: '(?:is(?:NaN|Finite)|parse(?:Int|Float)|Math|JSON|(?:en|de)codeURI(?:Component)?)' + keywordend + }, { + token: 'variable.language', + regex: '(?:t(?:hat|il|o)|f(?:rom|allthrough)|it|by|e)' + keywordend + }, { + token: 'identifier', + regex: identifier + '\\s*:(?![:=])' + }, { + token: 'variable', + regex: identifier + }, { + token: 'keyword.operator', + regex: '(?:\\.{3}|\\s+\\?)' + }, { + token: 'keyword.variable', + regex: '(?:@+|::|\\.\\.)', + next: 'key' + }, { + token: 'keyword.operator', + regex: '\\.\\s*', + next: 'key' + }, { + token: 'string', + regex: '\\\\\\S[^\\s,;)}\\]]*' + }, { + token: 'string.doc', + regex: '\'\'\'', + next: 'qdoc' + }, { + token: 'string.doc', + regex: '"""', + next: 'qqdoc' + }, { + token: 'string', + regex: '\'', + next: 'qstring' + }, { + token: 'string', + regex: '"', + next: 'qqstring' + }, { + token: 'string', + regex: '`', + next: 'js' + }, { + token: 'string', + regex: '<\\[', + next: 'words' + }, { + token: 'string.regex', + regex: '//', + next: 'heregex' + }, { + token: 'string.regex', + regex: '\\/(?:[^[\\/\\n\\\\]*(?:(?:\\\\.|\\[[^\\]\\n\\\\]*(?:\\\\.[^\\]\\n\\\\]*)*\\])[^[\\/\\n\\\\]*)*)\\/[gimy$]{0,4}', + next: 'key' + }, { + token: 'constant.numeric', + regex: '(?:0x[\\da-fA-F][\\da-fA-F_]*|(?:[2-9]|[12]\\d|3[0-6])r[\\da-zA-Z][\\da-zA-Z_]*|(?:\\d[\\d_]*(?:\\.\\d[\\d_]*)?|\\.\\d[\\d_]*)(?:e[+-]?\\d[\\d_]*)?[\\w$]*)' + }, { + token: 'lparen', + regex: '[({[]' + }, { + token: 'rparen', + regex: '[)}\\]]', + next: 'key' + }, { + token: 'keyword.operator', + regex: '\\S+' + }, { + token: 'text', + regex: '\\s+' + } + ], + heregex: [ + { + token: 'string.regex', + regex: '.*?//[gimy$?]{0,4}', + next: 'start' + }, { + token: 'string.regex', + regex: '\\s*#{' + }, { + token: 'comment.regex', + regex: '\\s+(?:#.*)?' + }, { + token: 'string.regex', + regex: '\\S+' + } + ], + key: [ + { + token: 'keyword.operator', + regex: '[.?@!]+' + }, { + token: 'identifier', + regex: identifier, + next: 'start' + }, { + token: 'text', + regex: '.', + next: 'start' + } + ], + comment: [ + { + token: 'comment.doc', + regex: '.*?\\*/', + next: 'start' + }, { + token: 'comment.doc', + regex: '.+' + } + ], + qdoc: [ + { + token: 'string', + regex: ".*?'''", + next: 'key' + }, stringfill + ], + qqdoc: [ + { + token: 'string', + regex: '.*?"""', + next: 'key' + }, stringfill + ], + qstring: [ + { + token: 'string', + regex: '[^\\\\\']*(?:\\\\.[^\\\\\']*)*\'', + next: 'key' + }, stringfill + ], + qqstring: [ + { + token: 'string', + regex: '[^\\\\"]*(?:\\\\.[^\\\\"]*)*"', + next: 'key' + }, stringfill + ], + js: [ + { + token: 'string', + regex: '[^\\\\`]*(?:\\\\.[^\\\\`]*)*`', + next: 'key' + }, stringfill + ], + words: [ + { + token: 'string', + regex: '.*?\\]>', + next: 'key' + }, stringfill + ] +}; +for (idx in Rules) { + r = Rules[idx]; + if (Array.isArray(r)) { + for (i$ = 0, len$ = r.length; i$ < len$; ++i$) { + i = i$; + rr = r[i$]; + if (rr.regex) { + Rules[idx][i].regex = new RegExp('^' + rr.regex); + } + } + } else if (r.regex) { + Rules[idx].regex = new RegExp('^' + r.regex); + } +} +CodeMirror.defineMIME('text/x-livescript', 'livescript'); diff --git a/mode/livescript/livescript.ls b/mode/livescript/livescript.ls new file mode 100644 index 0000000000..065242312a --- /dev/null +++ b/mode/livescript/livescript.ls @@ -0,0 +1,266 @@ +/** + * Link to the project's GitHub page: + * https://github.com/duralog/CodeMirror + */ +CodeMirror.defineMode 'livescript', (conf) -> + tokenBase = (stream, state) -> + #indent = + if next_rule = state.next or \start + state.next = state.next + if Array.isArray nr = Rules[next_rule] + for r in nr + if r.regex and m = stream.match r.regex + state.next = r.next + return r.token + stream.next! + return \error + if stream.match r = Rules[next_rule] + if r.regex and stream.match r.regex + state.next = r.next + return r.token + else + stream.next! + return \error + stream.next! + return 'error' + external = { + startState: (basecolumn) -> + { + next: \start + lastToken: null + } + token: (stream, state) -> + style = tokenBase stream, state #tokenLexer stream, state + state.lastToken = { + style: style + indent: stream.indentation! + content: stream.current! + } + style.replace /\./g, ' ' + indent: (state, textAfter) -> + # XXX this won't work with backcalls + indentation = state.lastToken.indent + if state.lastToken.content.match indenter then indentation += 2 + return indentation + } + external + +### Highlight Rules +# taken from mode-ls.ls + +indenter = // (? + : [({[=:] + | [-~]> + | \b (?: e(?:lse|xport) | d(?:o|efault) | t(?:ry|hen) | finally | + import (?:\s* all)? | const | var | + let | new | catch (?:\s* #identifier)? ) + ) \s* $ // + +identifier = /(?![\d\s])[$\w\xAA-\uFFDC](?:(?!\s)[$\w\xAA-\uFFDC]|-[A-Za-z])*/$ +keywordend = /(?![$\w]|-[A-Za-z]|\s*:(?![:=]))/$ +stringfill = token: \string, regex: '.+' + +Rules = + start: + * token: \comment.doc + regex: '/\\*' + next : \comment + + * token: \comment + regex: '#.*' + + * token: \keyword + regex: //(? + :t(?:h(?:is|row|en)|ry|ypeof!?) + |c(?:on(?:tinue|st)|a(?:se|tch)|lass) + |i(?:n(?:stanceof)?|mp(?:ort(?:\s+all)?|lements)|[fs]) + |d(?:e(?:fault|lete|bugger)|o) + |f(?:or(?:\s+own)?|inally|unction) + |s(?:uper|witch) + |e(?:lse|x(?:tends|port)|val) + |a(?:nd|rguments) + |n(?:ew|ot) + |un(?:less|til) + |w(?:hile|ith) + |o[fr]|return|break|let|var|loop + )//$ + keywordend + + * token: \constant.language + regex: '(?:true|false|yes|no|on|off|null|void|undefined)' + keywordend + + * token: \invalid.illegal + regex: '(? + :p(?:ackage|r(?:ivate|otected)|ublic) + |i(?:mplements|nterface) + |enum|static|yield + )' + keywordend + + * token: \language.support.class + regex: '(? + :R(?:e(?:gExp|ferenceError)|angeError) + |S(?:tring|yntaxError) + |E(?:rror|valError) + |Array|Boolean|Date|Function|Number|Object|TypeError|URIError + )' + keywordend + + * token: \language.support.function + regex: '(? + :is(?:NaN|Finite) + |parse(?:Int|Float) + |Math|JSON + |(?:en|de)codeURI(?:Component)? + )' + keywordend + + * token: \variable.language + regex: '(?:t(?:hat|il|o)|f(?:rom|allthrough)|it|by|e)' + keywordend + + * token: \identifier + regex: identifier + /\s*:(?![:=])/$ + + * token: \variable + regex: identifier + + * token: \keyword.operator + regex: /(?:\.{3}|\s+\?)/$ + + * token: \keyword.variable + regex: /(?:@+|::|\.\.)/$ + next : \key + + * token: \keyword.operator + regex: /\.\s*/$ + next : \key + + * token: \string + regex: /\\\S[^\s,;)}\]]*/$ + + * token: \string.doc + regex: \''' + next : \qdoc + + * token: \string.doc + regex: \""" + next : \qqdoc + + * token: \string + regex: \' + next : \qstring + + * token: \string + regex: \" + next : \qqstring + + * token: \string + regex: \` + next : \js + + * token: \string + regex: '<\\[' + next : \words + + * token: \string.regex + regex: \// + next : \heregex + + * token: \string.regex + regex: // + /(?: [^ [ / \n \\ ]* + (?: (?: \\. + | \[ [^\]\n\\]* (?:\\.[^\]\n\\]*)* \] + ) [^ [ / \n \\ ]* + )* + )/ [gimy$]{0,4} + //$ + next : \key + + * token: \constant.numeric + regex: '(?:0x[\\da-fA-F][\\da-fA-F_]* + |(?:[2-9]|[12]\\d|3[0-6])r[\\da-zA-Z][\\da-zA-Z_]* + |(?:\\d[\\d_]*(?:\\.\\d[\\d_]*)?|\\.\\d[\\d_]*) + (?:e[+-]?\\d[\\d_]*)?[\\w$]*)' + + * token: \lparen + regex: '[({[]' + + * token: \rparen + regex: '[)}\\]]' + next : \key + + * token: \keyword.operator + regex: \\\S+ + + * token: \text + regex: \\\s+ + + heregex: + * token: \string.regex + regex: '.*?//[gimy$?]{0,4}' + next : \start + * token: \string.regex + regex: '\\s*#{' + * token: \comment.regex + regex: '\\s+(?:#.*)?' + * token: \string.regex + regex: '\\S+' + + key: + * token: \keyword.operator + regex: '[.?@!]+' + * token: \identifier + regex: identifier + next : \start + * token: \text + regex: '.' + next : \start + + comment: + * token: \comment.doc + regex: '.*?\\*/' + next : \start + * token: \comment.doc + regex: '.+' + + qdoc: + token: \string + regex: ".*?'''" + next : \key + stringfill + + qqdoc: + token: \string + regex: '.*?"""' + next : \key + stringfill + + qstring: + token: \string + regex: /[^\\']*(?:\\.[^\\']*)*'/$ + next : \key + stringfill + + qqstring: + token: \string + regex: /[^\\"]*(?:\\.[^\\"]*)*"/$ + next : \key + stringfill + + js: + token: \string + regex: /[^\\`]*(?:\\.[^\\`]*)*`/$ + next : \key + stringfill + + words: + token: \string + regex: '.*?\\]>' + next : \key + stringfill + +# for optimization, precompile the regexps +for idx, r of Rules + if Array.isArray r + for rr, i in r + if rr.regex then Rules[idx][i].regex = new RegExp '^'+rr.regex + else if r.regex then Rules[idx].regex = new RegExp '^'+r.regex + +CodeMirror.defineMIME 'text/x-livescript', 'livescript' From ed2e51993575f0629df3240804416232c4805dbe Mon Sep 17 00:00:00 2001 From: Marijn Haverbeke Date: Mon, 11 Mar 2013 12:07:11 +0100 Subject: [PATCH 0839/5780] [livescript mode] Integrate --- doc/compress.html | 1 + doc/modes.html | 1 + mode/livescript/livescript.js | 496 +++++++++++++++++----------------- mode/meta.js | 1 + 4 files changed, 252 insertions(+), 247 deletions(-) diff --git a/doc/compress.html b/doc/compress.html index a92f36396c..22b8362fc1 100644 --- a/doc/compress.html +++ b/doc/compress.html @@ -89,6 +89,7 @@

    { } CodeMi + diff --git a/doc/modes.html b/doc/modes.html index c3d16059ac..f8080a46e8 100644 --- a/doc/modes.html +++ b/doc/modes.html @@ -45,6 +45,7 @@

    { } CodeMi
  • JavaScript
  • Jinja2
  • LESS
  • +
  • LiveScript
  • Lua
  • Markdown (GitHub-flavour)
  • mIRC
  • diff --git a/mode/livescript/livescript.js b/mode/livescript/livescript.js index 96d8813334..c000324b8c 100644 --- a/mode/livescript/livescript.js +++ b/mode/livescript/livescript.js @@ -2,264 +2,266 @@ * Link to the project's GitHub page: * https://github.com/duralog/CodeMirror */ -var indenter, identifier, keywordend, stringfill, Rules, idx, r, i$, len$, i, rr; -CodeMirror.defineMode('livescript', function(conf){ - var tokenBase, external; - tokenBase = function(stream, state){ - var next_rule, nr, i$, len$, r, m; - if (next_rule = state.next || 'start') { - state.next = state.next; - if (Array.isArray(nr = Rules[next_rule])) { - for (i$ = 0, len$ = nr.length; i$ < len$; ++i$) { - r = nr[i$]; - if (r.regex && (m = stream.match(r.regex))) { +(function() { + CodeMirror.defineMode('livescript', function(){ + var tokenBase, external; + tokenBase = function(stream, state){ + var next_rule, nr, i$, len$, r, m; + if (next_rule = state.next || 'start') { + state.next = state.next; + if (Array.isArray(nr = Rules[next_rule])) { + for (i$ = 0, len$ = nr.length; i$ < len$; ++i$) { + r = nr[i$]; + if (r.regex && (m = stream.match(r.regex))) { + state.next = r.next; + return r.token; + } + } + stream.next(); + return 'error'; + } + if (stream.match(r = Rules[next_rule])) { + if (r.regex && stream.match(r.regex)) { state.next = r.next; return r.token; + } else { + stream.next(); + return 'error'; } } - stream.next(); - return 'error'; } - if (stream.match(r = Rules[next_rule])) { - if (r.regex && stream.match(r.regex)) { - state.next = r.next; - return r.token; - } else { - stream.next(); - return 'error'; + stream.next(); + return 'error'; + }; + external = { + startState: function(){ + return { + next: 'start', + lastToken: null + }; + }, + token: function(stream, state){ + var style; + style = tokenBase(stream, state); + state.lastToken = { + style: style, + indent: stream.indentation(), + content: stream.current() + }; + return style.replace(/\./g, ' '); + }, + indent: function(state){ + var indentation; + indentation = state.lastToken.indent; + if (state.lastToken.content.match(indenter)) { + indentation += 2; } + return indentation; } - } - stream.next(); - return 'error'; + }; + return external; + }); + + var identifier = '(?![\\d\\s])[$\\w\\xAA-\\uFFDC](?:(?!\\s)[$\\w\\xAA-\\uFFDC]|-[A-Za-z])*'; + var indenter = RegExp('(?:[({[=:]|[-~]>|\\b(?:e(?:lse|xport)|d(?:o|efault)|t(?:ry|hen)|finally|import(?:\\s*all)?|const|var|let|new|catch(?:\\s*' + identifier + ')?))\\s*$'); + var keywordend = '(?![$\\w]|-[A-Za-z]|\\s*:(?![:=]))'; + var stringfill = { + token: 'string', + regex: '.+' }; - external = { - startState: function(basecolumn){ - return { - next: 'start', - lastToken: null - }; - }, - token: function(stream, state){ - var style; - style = tokenBase(stream, state); - state.lastToken = { - style: style, - indent: stream.indentation(), - content: stream.current() - }; - return style.replace(/\./g, ' '); - }, - indent: function(state, textAfter){ - var indentation; - indentation = state.lastToken.indent; - if (state.lastToken.content.match(indenter)) { - indentation += 2; + var Rules = { + start: [ + { + token: 'comment.doc', + regex: '/\\*', + next: 'comment' + }, { + token: 'comment', + regex: '#.*' + }, { + token: 'keyword', + regex: '(?:t(?:h(?:is|row|en)|ry|ypeof!?)|c(?:on(?:tinue|st)|a(?:se|tch)|lass)|i(?:n(?:stanceof)?|mp(?:ort(?:\\s+all)?|lements)|[fs])|d(?:e(?:fault|lete|bugger)|o)|f(?:or(?:\\s+own)?|inally|unction)|s(?:uper|witch)|e(?:lse|x(?:tends|port)|val)|a(?:nd|rguments)|n(?:ew|ot)|un(?:less|til)|w(?:hile|ith)|o[fr]|return|break|let|var|loop)' + keywordend + }, { + token: 'constant.language', + regex: '(?:true|false|yes|no|on|off|null|void|undefined)' + keywordend + }, { + token: 'invalid.illegal', + regex: '(?:p(?:ackage|r(?:ivate|otected)|ublic)|i(?:mplements|nterface)|enum|static|yield)' + keywordend + }, { + token: 'language.support.class', + regex: '(?:R(?:e(?:gExp|ferenceError)|angeError)|S(?:tring|yntaxError)|E(?:rror|valError)|Array|Boolean|Date|Function|Number|Object|TypeError|URIError)' + keywordend + }, { + token: 'language.support.function', + regex: '(?:is(?:NaN|Finite)|parse(?:Int|Float)|Math|JSON|(?:en|de)codeURI(?:Component)?)' + keywordend + }, { + token: 'variable.language', + regex: '(?:t(?:hat|il|o)|f(?:rom|allthrough)|it|by|e)' + keywordend + }, { + token: 'identifier', + regex: identifier + '\\s*:(?![:=])' + }, { + token: 'variable', + regex: identifier + }, { + token: 'keyword.operator', + regex: '(?:\\.{3}|\\s+\\?)' + }, { + token: 'keyword.variable', + regex: '(?:@+|::|\\.\\.)', + next: 'key' + }, { + token: 'keyword.operator', + regex: '\\.\\s*', + next: 'key' + }, { + token: 'string', + regex: '\\\\\\S[^\\s,;)}\\]]*' + }, { + token: 'string.doc', + regex: '\'\'\'', + next: 'qdoc' + }, { + token: 'string.doc', + regex: '"""', + next: 'qqdoc' + }, { + token: 'string', + regex: '\'', + next: 'qstring' + }, { + token: 'string', + regex: '"', + next: 'qqstring' + }, { + token: 'string', + regex: '`', + next: 'js' + }, { + token: 'string', + regex: '<\\[', + next: 'words' + }, { + token: 'string.regex', + regex: '//', + next: 'heregex' + }, { + token: 'string.regex', + regex: '\\/(?:[^[\\/\\n\\\\]*(?:(?:\\\\.|\\[[^\\]\\n\\\\]*(?:\\\\.[^\\]\\n\\\\]*)*\\])[^[\\/\\n\\\\]*)*)\\/[gimy$]{0,4}', + next: 'key' + }, { + token: 'constant.numeric', + regex: '(?:0x[\\da-fA-F][\\da-fA-F_]*|(?:[2-9]|[12]\\d|3[0-6])r[\\da-zA-Z][\\da-zA-Z_]*|(?:\\d[\\d_]*(?:\\.\\d[\\d_]*)?|\\.\\d[\\d_]*)(?:e[+-]?\\d[\\d_]*)?[\\w$]*)' + }, { + token: 'lparen', + regex: '[({[]' + }, { + token: 'rparen', + regex: '[)}\\]]', + next: 'key' + }, { + token: 'keyword.operator', + regex: '\\S+' + }, { + token: 'text', + regex: '\\s+' } - return indentation; - } + ], + heregex: [ + { + token: 'string.regex', + regex: '.*?//[gimy$?]{0,4}', + next: 'start' + }, { + token: 'string.regex', + regex: '\\s*#{' + }, { + token: 'comment.regex', + regex: '\\s+(?:#.*)?' + }, { + token: 'string.regex', + regex: '\\S+' + } + ], + key: [ + { + token: 'keyword.operator', + regex: '[.?@!]+' + }, { + token: 'identifier', + regex: identifier, + next: 'start' + }, { + token: 'text', + regex: '.', + next: 'start' + } + ], + comment: [ + { + token: 'comment.doc', + regex: '.*?\\*/', + next: 'start' + }, { + token: 'comment.doc', + regex: '.+' + } + ], + qdoc: [ + { + token: 'string', + regex: ".*?'''", + next: 'key' + }, stringfill + ], + qqdoc: [ + { + token: 'string', + regex: '.*?"""', + next: 'key' + }, stringfill + ], + qstring: [ + { + token: 'string', + regex: '[^\\\\\']*(?:\\\\.[^\\\\\']*)*\'', + next: 'key' + }, stringfill + ], + qqstring: [ + { + token: 'string', + regex: '[^\\\\"]*(?:\\\\.[^\\\\"]*)*"', + next: 'key' + }, stringfill + ], + js: [ + { + token: 'string', + regex: '[^\\\\`]*(?:\\\\.[^\\\\`]*)*`', + next: 'key' + }, stringfill + ], + words: [ + { + token: 'string', + regex: '.*?\\]>', + next: 'key' + }, stringfill + ] }; - return external; -}); -indenter = RegExp('(?:[({[=:]|[-~]>|\\b(?:e(?:lse|xport)|d(?:o|efault)|t(?:ry|hen)|finally|import(?:\\s*all)?|const|var|let|new|catch(?:\\s*' + identifier + ')?))\\s*$'); -identifier = '(?![\\d\\s])[$\\w\\xAA-\\uFFDC](?:(?!\\s)[$\\w\\xAA-\\uFFDC]|-[A-Za-z])*'; -keywordend = '(?![$\\w]|-[A-Za-z]|\\s*:(?![:=]))'; -stringfill = { - token: 'string', - regex: '.+' -}; -Rules = { - start: [ - { - token: 'comment.doc', - regex: '/\\*', - next: 'comment' - }, { - token: 'comment', - regex: '#.*' - }, { - token: 'keyword', - regex: '(?:t(?:h(?:is|row|en)|ry|ypeof!?)|c(?:on(?:tinue|st)|a(?:se|tch)|lass)|i(?:n(?:stanceof)?|mp(?:ort(?:\\s+all)?|lements)|[fs])|d(?:e(?:fault|lete|bugger)|o)|f(?:or(?:\\s+own)?|inally|unction)|s(?:uper|witch)|e(?:lse|x(?:tends|port)|val)|a(?:nd|rguments)|n(?:ew|ot)|un(?:less|til)|w(?:hile|ith)|o[fr]|return|break|let|var|loop)' + keywordend - }, { - token: 'constant.language', - regex: '(?:true|false|yes|no|on|off|null|void|undefined)' + keywordend - }, { - token: 'invalid.illegal', - regex: '(?:p(?:ackage|r(?:ivate|otected)|ublic)|i(?:mplements|nterface)|enum|static|yield)' + keywordend - }, { - token: 'language.support.class', - regex: '(?:R(?:e(?:gExp|ferenceError)|angeError)|S(?:tring|yntaxError)|E(?:rror|valError)|Array|Boolean|Date|Function|Number|Object|TypeError|URIError)' + keywordend - }, { - token: 'language.support.function', - regex: '(?:is(?:NaN|Finite)|parse(?:Int|Float)|Math|JSON|(?:en|de)codeURI(?:Component)?)' + keywordend - }, { - token: 'variable.language', - regex: '(?:t(?:hat|il|o)|f(?:rom|allthrough)|it|by|e)' + keywordend - }, { - token: 'identifier', - regex: identifier + '\\s*:(?![:=])' - }, { - token: 'variable', - regex: identifier - }, { - token: 'keyword.operator', - regex: '(?:\\.{3}|\\s+\\?)' - }, { - token: 'keyword.variable', - regex: '(?:@+|::|\\.\\.)', - next: 'key' - }, { - token: 'keyword.operator', - regex: '\\.\\s*', - next: 'key' - }, { - token: 'string', - regex: '\\\\\\S[^\\s,;)}\\]]*' - }, { - token: 'string.doc', - regex: '\'\'\'', - next: 'qdoc' - }, { - token: 'string.doc', - regex: '"""', - next: 'qqdoc' - }, { - token: 'string', - regex: '\'', - next: 'qstring' - }, { - token: 'string', - regex: '"', - next: 'qqstring' - }, { - token: 'string', - regex: '`', - next: 'js' - }, { - token: 'string', - regex: '<\\[', - next: 'words' - }, { - token: 'string.regex', - regex: '//', - next: 'heregex' - }, { - token: 'string.regex', - regex: '\\/(?:[^[\\/\\n\\\\]*(?:(?:\\\\.|\\[[^\\]\\n\\\\]*(?:\\\\.[^\\]\\n\\\\]*)*\\])[^[\\/\\n\\\\]*)*)\\/[gimy$]{0,4}', - next: 'key' - }, { - token: 'constant.numeric', - regex: '(?:0x[\\da-fA-F][\\da-fA-F_]*|(?:[2-9]|[12]\\d|3[0-6])r[\\da-zA-Z][\\da-zA-Z_]*|(?:\\d[\\d_]*(?:\\.\\d[\\d_]*)?|\\.\\d[\\d_]*)(?:e[+-]?\\d[\\d_]*)?[\\w$]*)' - }, { - token: 'lparen', - regex: '[({[]' - }, { - token: 'rparen', - regex: '[)}\\]]', - next: 'key' - }, { - token: 'keyword.operator', - regex: '\\S+' - }, { - token: 'text', - regex: '\\s+' - } - ], - heregex: [ - { - token: 'string.regex', - regex: '.*?//[gimy$?]{0,4}', - next: 'start' - }, { - token: 'string.regex', - regex: '\\s*#{' - }, { - token: 'comment.regex', - regex: '\\s+(?:#.*)?' - }, { - token: 'string.regex', - regex: '\\S+' - } - ], - key: [ - { - token: 'keyword.operator', - regex: '[.?@!]+' - }, { - token: 'identifier', - regex: identifier, - next: 'start' - }, { - token: 'text', - regex: '.', - next: 'start' - } - ], - comment: [ - { - token: 'comment.doc', - regex: '.*?\\*/', - next: 'start' - }, { - token: 'comment.doc', - regex: '.+' - } - ], - qdoc: [ - { - token: 'string', - regex: ".*?'''", - next: 'key' - }, stringfill - ], - qqdoc: [ - { - token: 'string', - regex: '.*?"""', - next: 'key' - }, stringfill - ], - qstring: [ - { - token: 'string', - regex: '[^\\\\\']*(?:\\\\.[^\\\\\']*)*\'', - next: 'key' - }, stringfill - ], - qqstring: [ - { - token: 'string', - regex: '[^\\\\"]*(?:\\\\.[^\\\\"]*)*"', - next: 'key' - }, stringfill - ], - js: [ - { - token: 'string', - regex: '[^\\\\`]*(?:\\\\.[^\\\\`]*)*`', - next: 'key' - }, stringfill - ], - words: [ - { - token: 'string', - regex: '.*?\\]>', - next: 'key' - }, stringfill - ] -}; -for (idx in Rules) { - r = Rules[idx]; - if (Array.isArray(r)) { - for (i$ = 0, len$ = r.length; i$ < len$; ++i$) { - i = i$; - rr = r[i$]; - if (rr.regex) { - Rules[idx][i].regex = new RegExp('^' + rr.regex); + for (var idx in Rules) { + var r = Rules[idx]; + if (Array.isArray(r)) { + for (var i = 0, len = r.length; i < len; ++i) { + var rr = r[i]; + if (rr.regex) { + Rules[idx][i].regex = new RegExp('^' + rr.regex); + } } + } else if (r.regex) { + Rules[idx].regex = new RegExp('^' + r.regex); } - } else if (r.regex) { - Rules[idx].regex = new RegExp('^' + r.regex); } -} +})(); + CodeMirror.defineMIME('text/x-livescript', 'livescript'); diff --git a/mode/meta.js b/mode/meta.js index cf8a7ec3ea..7ca7333b2c 100644 --- a/mode/meta.js +++ b/mode/meta.js @@ -29,6 +29,7 @@ CodeMirror.modeInfo = [ {name: 'TypeScript', mime: 'application/typescript', mode: 'javascript'}, {name: 'Jinja2', mime: 'jinja2', mode: 'jinja2'}, {name: 'LESS', mime: 'text/x-less', mode: 'less'}, + {name: 'LiveScript', mime: 'text/x-livescript', mode: 'livescript'}, {name: 'Lua', mime: 'text/x-lua', mode: 'lua'}, {name: 'Markdown (GitHub-flavour)', mime: 'text/x-markdown', mode: 'markdown'}, {name: 'mIRC', mime: 'text/mirc', mode: 'mirc'}, From d55a02aea32d4000b768f59bc1d873e01492dcee Mon Sep 17 00:00:00 2001 From: Marijn Haverbeke Date: Mon, 11 Mar 2013 12:14:55 +0100 Subject: [PATCH 0840/5780] Set viewportMargin to Infinity in the autoresize demo --- demo/resize.html | 7 +++++-- 1 file changed, 5 insertions(+), 2 deletions(-) diff --git a/demo/resize.html b/demo/resize.html index ddd4e56695..7badc2bfbb 100644 --- a/demo/resize.html +++ b/demo/resize.html @@ -33,12 +33,15 @@

    CodeMirror: Autoresize demo

    } -

    By setting a few CSS properties, CodeMirror can be made to +

    By setting a few CSS properties, and giving +the viewportMargin +a value of Infinity, CodeMirror can be made to automatically resize to fit its content.

    From 6752696cfc534c6422870fe1eadaa375fdc0bd7a Mon Sep 17 00:00:00 2001 From: satchmorun Date: Mon, 11 Mar 2013 09:15:07 -0500 Subject: [PATCH 0841/5780] [addon continuelist.js] Make the listiness check Just a small nit. Without the list check, `getStateAfter` looks like it will always be true. --- addon/edit/continuelist.js | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/addon/edit/continuelist.js b/addon/edit/continuelist.js index 907eb5fdcd..fb1fc38ba2 100644 --- a/addon/edit/continuelist.js +++ b/addon/edit/continuelist.js @@ -6,7 +6,7 @@ CodeMirror.commands.newlineAndIndentContinueMarkdownList = function(cm) { var pos = cm.getCursor(), - inList = cm.getStateAfter(pos.line), + inList = cm.getStateAfter(pos.line).list, match; if (!inList || !(match = cm.getLine(pos.line).match(listRE))) { From 262bf5264501cac42ac3d2fc957c1b80ef13894c Mon Sep 17 00:00:00 2001 From: satchmorun Date: Mon, 11 Mar 2013 15:35:09 -0500 Subject: [PATCH 0842/5780] [addon closebrackets] Add smart backspace behavior When any specified pair is empty, backspacing will also delete the closing bracket. --- addon/edit/closebrackets.js | 22 +++++++++++++++++++++- demo/closebrackets.html | 8 ++++++-- 2 files changed, 27 insertions(+), 3 deletions(-) diff --git a/addon/edit/closebrackets.js b/addon/edit/closebrackets.js index 9ca4ce1f5c..f512a54553 100644 --- a/addon/edit/closebrackets.js +++ b/addon/edit/closebrackets.js @@ -10,7 +10,10 @@ }); function buildKeymap(pairs) { - var map = {name : "autoCloseBrackets"}; + var map = { + name : "autoCloseBrackets", + Backspace: buildBackspacer(pairs) + }; for (var i = 0; i < pairs.length; i += 2) (function(left, right) { function maybeOverwrite(cm) { var cur = cm.getCursor(), ahead = cm.getRange(cur, CodeMirror.Pos(cur.line, cur.ch + 1)); @@ -26,4 +29,21 @@ })(pairs.charAt(i), pairs.charAt(i + 1)); return map; } + + function buildBackspacer(pairs) { + var pairmap = {}; + + for (var i = 0; i < pairs.length; i += 2) + pairmap[pairs.charAt(i)] = pairs.charAt(i + 1); + + return function(cm) { + var cur = cm.getCursor(), + from = CodeMirror.Pos(cur.line, cur.ch - 1), + to = CodeMirror.Pos(cur.line, cur.ch + 1), + str = cm.getRange(from, to); + + if (pairmap[str.charAt(0)] !== str.charAt(1)) return CodeMirror.Pass; + cm.replaceRange('', from, to); + }; + } })(); diff --git a/demo/closebrackets.html b/demo/closebrackets.html index 58ba0330a1..47304ea889 100644 --- a/demo/closebrackets.html +++ b/demo/closebrackets.html @@ -13,14 +13,18 @@ - +

    CodeMirror: Closebrackets Demo

    - +

    Type a bracket like '[', '(', '{', '"', or ''' and the addon will auto-close it. Type the closing variant when directly in front of a matching character and it will overwrite it.

    +

    If you backspace over a starting bracket while inside empty brackets + (e.g. {|}), it will delete the closing bracket for you.

    + +
    + +

    The placeholder + plug-in adds an option placeholder that can be set to + make text appear in the editor when it is empty and not focused. + If the source textarea has a placeholder attribute, + it will automatically be inherited.

    + + + + + diff --git a/doc/manual.html b/doc/manual.html index b6e1c8670d..1e84ca9313 100644 --- a/doc/manual.html +++ b/doc/manual.html @@ -1549,6 +1549,12 @@

    Add-ons

    called newlineAndIndentContinueComment that you can bind Enter to in order to have the editor prefix new lines inside C-like block comments with an asterisk.

    +
    display/placeholder.js
    +
    Adds a placeholder option that can be used to + make text appear in the editor when it is empty and not focused. + Also gives the editor a CodeMirror-empty CSS class + whenever it doesn't contain any text. + See the demo.

    Writing CodeMirror Modes

    diff --git a/lib/codemirror.js b/lib/codemirror.js index 60a91a291b..e485219c18 100644 --- a/lib/codemirror.js +++ b/lib/codemirror.js @@ -3312,6 +3312,8 @@ window.CodeMirror = (function() { options.value = textarea.value; if (!options.tabindex && textarea.tabindex) options.tabindex = textarea.tabindex; + if (!options.placeholder && textarea.placeholder) + options.placeholder = textarea.placeholder; // Set autofocus to true if this textarea is focused, or if it has // autofocus and no other element is focused. if (options.autofocus == null) { From f936a703e1606122d61ba81299a3726cc014d042 Mon Sep 17 00:00:00 2001 From: Marijn Haverbeke Date: Tue, 12 Mar 2013 10:20:58 +0100 Subject: [PATCH 0846/5780] Remove unused variable --- addon/display/placeholder.js | 2 -- 1 file changed, 2 deletions(-) diff --git a/addon/display/placeholder.js b/addon/display/placeholder.js index 66c2f1e16b..97f6d15fcc 100644 --- a/addon/display/placeholder.js +++ b/addon/display/placeholder.js @@ -1,6 +1,4 @@ (function() { - var placeholder; - CodeMirror.defineOption("placeholder", "", function(cm, val, old) { var prev = old && old != CodeMirror.Init; if (val && !prev) { From 5e881b9276f166c0e6c0640a12b7c4adc52f5b91 Mon Sep 17 00:00:00 2001 From: Marijn Haverbeke Date: Wed, 13 Mar 2013 17:48:49 +0100 Subject: [PATCH 0847/5780] Set a default background and color Closes #1346 --- lib/codemirror.css | 2 ++ 1 file changed, 2 insertions(+) diff --git a/lib/codemirror.css b/lib/codemirror.css index e36fbeb88f..913a1a9c3d 100644 --- a/lib/codemirror.css +++ b/lib/codemirror.css @@ -102,6 +102,8 @@ div.CodeMirror span.CodeMirror-nonmatchingbracket {color: #f22;} line-height: 1; position: relative; overflow: hidden; + background: white; + color: black; } .CodeMirror-scroll { From 165b65af88cfcaa8d74f9c0bd51cbb535964352a Mon Sep 17 00:00:00 2001 From: Marijn Haverbeke Date: Wed, 13 Mar 2013 17:58:46 +0100 Subject: [PATCH 0848/5780] [closetag addon] Fix bug in self-closing tags typed directly in front of word Closes #1349 --- addon/edit/closetag.js | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/addon/edit/closetag.js b/addon/edit/closetag.js index 46a5f4c76c..5d6131c38b 100644 --- a/addon/edit/closetag.js +++ b/addon/edit/closetag.js @@ -55,8 +55,8 @@ if (tok.end > pos.ch) tagName = tagName.slice(0, tagName.length - tok.end + pos.ch); var lowerTagName = tagName.toLowerCase(); // Don't process the '>' at the end of an end-tag or self-closing tag - if (tok.type == "tag" && state.type == "closeTag" || - /\/\s*$/.test(tok.string) || + console.log(tok.string); + if (tok.type == "tag" && state.type == "closeTag" || tok.string.indexOf("/") > -1 || dontCloseTags && indexOf(dontCloseTags, lowerTagName) > -1) return CodeMirror.Pass; From 6220a2c75911c542ba4ed1dbd6fdc50bca4d0a97 Mon Sep 17 00:00:00 2001 From: Marijn Haverbeke Date: Wed, 13 Mar 2013 17:59:14 +0100 Subject: [PATCH 0849/5780] Remove a debug statement --- addon/edit/closetag.js | 1 - 1 file changed, 1 deletion(-) diff --git a/addon/edit/closetag.js b/addon/edit/closetag.js index 5d6131c38b..54fa195463 100644 --- a/addon/edit/closetag.js +++ b/addon/edit/closetag.js @@ -55,7 +55,6 @@ if (tok.end > pos.ch) tagName = tagName.slice(0, tagName.length - tok.end + pos.ch); var lowerTagName = tagName.toLowerCase(); // Don't process the '>' at the end of an end-tag or self-closing tag - console.log(tok.string); if (tok.type == "tag" && state.type == "closeTag" || tok.string.indexOf("/") > -1 || dontCloseTags && indexOf(dontCloseTags, lowerTagName) > -1) return CodeMirror.Pass; From 1d12a3823366eda72ff40e20effc094633d4f1f1 Mon Sep 17 00:00:00 2001 From: Narciso Jaramillo Date: Tue, 12 Mar 2013 17:06:56 -0700 Subject: [PATCH 0850/5780] Don't try to scroll if editor isn't visible --- lib/codemirror.js | 12 +++++++----- 1 file changed, 7 insertions(+), 5 deletions(-) diff --git a/lib/codemirror.js b/lib/codemirror.js index e485219c18..65126e0587 100644 --- a/lib/codemirror.js +++ b/lib/codemirror.js @@ -1432,15 +1432,17 @@ window.CodeMirror = (function() { if (!captureMiddleClick) on(d.scroller, "contextmenu", function(e) {onContextMenu(cm, e);}); on(d.scroller, "scroll", function() { - setScrollTop(cm, d.scroller.scrollTop); - setScrollLeft(cm, d.scroller.scrollLeft, true); - signal(cm, "scroll", cm); + if (d.scroller.clientHeight) { + setScrollTop(cm, d.scroller.scrollTop); + setScrollLeft(cm, d.scroller.scrollLeft, true); + signal(cm, "scroll", cm); + } }); on(d.scrollbarV, "scroll", function() { - setScrollTop(cm, d.scrollbarV.scrollTop); + if (d.scroller.clientHeight) setScrollTop(cm, d.scrollbarV.scrollTop); }); on(d.scrollbarH, "scroll", function() { - setScrollLeft(cm, d.scrollbarH.scrollLeft); + if (d.scroller.clientHeight) setScrollLeft(cm, d.scrollbarH.scrollLeft); }); on(d.scroller, "mousewheel", function(e){onScrollWheel(cm, e);}); From 50621941b536805b2c62cb0fb5e1bc8a98e8877d Mon Sep 17 00:00:00 2001 From: Marijn Haverbeke Date: Fri, 15 Mar 2013 10:44:30 +0100 Subject: [PATCH 0851/5780] Make sure doc.scrollTop is accurate post-display-update Closes #1351 --- lib/codemirror.js | 1 + 1 file changed, 1 insertion(+) diff --git a/lib/codemirror.js b/lib/codemirror.js index 65126e0587..d04ff42cc7 100644 --- a/lib/codemirror.js +++ b/lib/codemirror.js @@ -405,6 +405,7 @@ window.CodeMirror = (function() { } updateSelection(cm); updateScrollbars(cm.display, cm.doc.height); + cm.doc.scrollTop = cm.display.scroller.scrollTop; return updated; } From db6242871c860c62f7c987edd032be2797378caf Mon Sep 17 00:00:00 2001 From: Andrey Lushnikov Date: Wed, 13 Mar 2013 18:24:23 +0400 Subject: [PATCH 0852/5780] Improve closebrackets.js addon - Do not insert closing bracket if a spacechar or closing bracket follows cursor - Do not handle backspace if selection is not empty - Do not overwrite anything if selection is not empty - If selection is not empty and open bracket is inserted - surround selection with brackets. --- addon/edit/closebrackets.js | 19 ++++++++++++++++--- 1 file changed, 16 insertions(+), 3 deletions(-) diff --git a/addon/edit/closebrackets.js b/addon/edit/closebrackets.js index 1ce5ba0511..b46caca02a 100644 --- a/addon/edit/closebrackets.js +++ b/addon/edit/closebrackets.js @@ -1,5 +1,6 @@ (function() { var DEFAULT_BRACKETS = "()[]{}''\"\""; + var SPACE_CHAR_REGEX = /\s/; CodeMirror.defineOption("autoCloseBrackets", false, function(cm, val, old) { var wasOn = old && old != CodeMirror.Init; @@ -13,6 +14,7 @@ var map = { name : "autoCloseBrackets", Backspace: function(cm) { + if (cm.somethingSelected()) return CodeMirror.Pass; var cur = cm.getCursor(), line = cm.getLine(cur.line); if (cur.ch && cur.ch < line.length && pairs.indexOf(line.slice(cur.ch - 1, cur.ch + 1)) % 2 == 0) @@ -21,16 +23,27 @@ return CodeMirror.Pass; } }; + var closingBrackets = []; for (var i = 0; i < pairs.length; i += 2) (function(left, right) { + if (left != right) closingBrackets.push(right); + function surround(cm) { + var selection = cm.getSelection(); + cm.replaceSelection(left + selection + right); + } function maybeOverwrite(cm) { var cur = cm.getCursor(), ahead = cm.getRange(cur, CodeMirror.Pos(cur.line, cur.ch + 1)); - if (ahead != right) return CodeMirror.Pass; + if (ahead != right || cm.somethingSelected()) return CodeMirror.Pass; else cm.execCommand("goCharRight"); } map["'" + left + "'"] = function(cm) { + if (cm.somethingSelected()) return surround(cm); if (left == right && maybeOverwrite(cm) != CodeMirror.Pass) return; - var cur = cm.getCursor("start"), ahead = CodeMirror.Pos(cur.line, cur.ch + 1); - cm.replaceSelection(left + right, {head: ahead, anchor: ahead}); + var cur = cm.getCursor(), ahead = CodeMirror.Pos(cur.line, cur.ch + 1); + var line = cm.getLine(cur.line), nextChar = line.charAt(cur.ch); + if (line.length == cur.ch || closingBrackets.indexOf(nextChar) >= 0 || SPACE_CHAR_REGEX.test(nextChar)) + cm.replaceSelection(left + right, {head: ahead, anchor: ahead}); + else + return CodeMirror.Pass; }; if (left != right) map["'" + right + "'"] = maybeOverwrite; })(pairs.charAt(i), pairs.charAt(i + 1)); From 9338ba490fa5c73d3a9d47839b88565456d9084d Mon Sep 17 00:00:00 2001 From: Marijn Haverbeke Date: Fri, 15 Mar 2013 11:38:15 +0100 Subject: [PATCH 0853/5780] Revise patch 50621941b536805b2c62cb0fb5e1bc8a98e8877d The previous solution broke test doc_docKeepsScroll. --- lib/codemirror.js | 5 +++-- 1 file changed, 3 insertions(+), 2 deletions(-) diff --git a/lib/codemirror.js b/lib/codemirror.js index d04ff42cc7..cab11e813b 100644 --- a/lib/codemirror.js +++ b/lib/codemirror.js @@ -405,7 +405,6 @@ window.CodeMirror = (function() { } updateSelection(cm); updateScrollbars(cm.display, cm.doc.height); - cm.doc.scrollTop = cm.display.scroller.scrollTop; return updated; } @@ -1271,8 +1270,10 @@ window.CodeMirror = (function() { var coords = cursorCoords(cm, doc.sel.head); newScrollPos = calculateScrollPos(cm, coords.left, coords.top, coords.left, coords.bottom); } - if (op.changes.length || newScrollPos && newScrollPos.scrollTop != null) + if (op.changes.length || newScrollPos && newScrollPos.scrollTop != null) { updated = updateDisplay(cm, op.changes, newScrollPos && newScrollPos.scrollTop); + if (cm.display.scroller.offsetHeight) cm.doc.scrollTop = cm.display.scroller.scrollTop; + } if (!updated && op.selectionChanged) updateSelection(cm); if (op.updateScrollPos) { display.scroller.scrollTop = display.scrollbarV.scrollTop = doc.scrollTop = newScrollPos.scrollTop; From 02b0f47d504eb96788f566b150f8ebc95cddf443 Mon Sep 17 00:00:00 2001 From: Marijn Haverbeke Date: Fri, 15 Mar 2013 11:42:44 +0100 Subject: [PATCH 0854/5780] [placeholder addon] Fix corner case --- addon/display/placeholder.js | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/addon/display/placeholder.js b/addon/display/placeholder.js index 97f6d15fcc..f85f2df127 100644 --- a/addon/display/placeholder.js +++ b/addon/display/placeholder.js @@ -15,7 +15,7 @@ wrapper.className = wrapper.className.replace(" CodeMirror-empty", ""); } - if (val && !cm.hasFocus()) setPlaceholder(cm); + if (val && !cm.hasFocus()) onBlur(cm); }); function clearPlaceholder(cm) { From 4a4f1a873076da0b6caa5dbfe01d1fdce762cae0 Mon Sep 17 00:00:00 2001 From: Marijn Haverbeke Date: Fri, 15 Mar 2013 11:55:34 +0100 Subject: [PATCH 0855/5780] Revert "Make the default by-word movement stop at word boundaries, rather than word end" This reverts commit dd919a2bcf93a59b2ab28aa766c50fe6e5d9d239. --- lib/codemirror.js | 27 +++++++++------------------ 1 file changed, 9 insertions(+), 18 deletions(-) diff --git a/lib/codemirror.js b/lib/codemirror.js index cab11e813b..cb5a781138 100644 --- a/lib/codemirror.js +++ b/lib/codemirror.js @@ -2584,20 +2584,13 @@ window.CodeMirror = (function() { if (unit == "char") moveOnce(); else if (unit == "column") moveOnce(true); - else if (unit == "word" || unit == "wordBoundary") { - var sawWord = false, boundary = unit == "wordBoundary", cur = null; + else if (unit == "word") { + var sawWord = false; for (;;) { - if (dir < 0 && !moveOnce()) break; - var wordChar = isWordChar(lineObj.text.charAt(ch)), done = false; - if (boundary) { - if (cur == null) cur = wordChar; - else done = cur != wordChar; - } else { - if (wordChar) sawWord = true; - else done = sawWord; - } - if (done) {if (dir < 0) {dir = 1; moveOnce();} break;} - if (dir > 0 && !moveOnce()) break; + if (dir < 0) if (!moveOnce()) break; + if (isWordChar(lineObj.text.charAt(ch))) sawWord = true; + else if (sawWord) {if (dir < 0) {dir = 1; moveOnce();} break;} + if (dir > 0) if (!moveOnce()) break; } } var result = skipAtomic(doc, Pos(line, ch), dir, true); @@ -3195,8 +3188,6 @@ window.CodeMirror = (function() { goColumnRight: function(cm) {cm.moveH(1, "column");}, goWordLeft: function(cm) {cm.moveH(-1, "word");}, goWordRight: function(cm) {cm.moveH(1, "word");}, - goWordBoundaryLeft: function(cm) {cm.moveH(-1, "wordBoundary");}, - goWordBoundaryRight: function(cm) {cm.moveH(1, "wordBoundary");}, delCharBefore: function(cm) {cm.deleteH(-1, "char");}, delCharAfter: function(cm) {cm.deleteH(1, "char");}, delWordBefore: function(cm) {cm.deleteH(-1, "word");}, @@ -3238,7 +3229,7 @@ window.CodeMirror = (function() { keyMap.pcDefault = { "Ctrl-A": "selectAll", "Ctrl-D": "deleteLine", "Ctrl-Z": "undo", "Shift-Ctrl-Z": "redo", "Ctrl-Y": "redo", "Ctrl-Home": "goDocStart", "Alt-Up": "goDocStart", "Ctrl-End": "goDocEnd", "Ctrl-Down": "goDocEnd", - "Ctrl-Left": "goWordBoundaryLeft", "Ctrl-Right": "goWordBoundaryRight", "Alt-Left": "goLineStart", "Alt-Right": "goLineEnd", + "Ctrl-Left": "goWordLeft", "Ctrl-Right": "goWordRight", "Alt-Left": "goLineStart", "Alt-Right": "goLineEnd", "Ctrl-Backspace": "delWordBefore", "Ctrl-Delete": "delWordAfter", "Ctrl-S": "save", "Ctrl-F": "find", "Ctrl-G": "findNext", "Shift-Ctrl-G": "findPrev", "Shift-Ctrl-F": "replace", "Shift-Ctrl-R": "replaceAll", "Ctrl-[": "indentLess", "Ctrl-]": "indentMore", @@ -3246,8 +3237,8 @@ window.CodeMirror = (function() { }; keyMap.macDefault = { "Cmd-A": "selectAll", "Cmd-D": "deleteLine", "Cmd-Z": "undo", "Shift-Cmd-Z": "redo", "Cmd-Y": "redo", - "Cmd-Up": "goDocStart", "Cmd-End": "goDocEnd", "Cmd-Down": "goDocEnd", "Alt-Left": "goWordBoundaryLeft", - "Alt-Right": "goWordBoundaryRight", "Cmd-Left": "goLineStart", "Cmd-Right": "goLineEnd", "Alt-Backspace": "delWordBefore", + "Cmd-Up": "goDocStart", "Cmd-End": "goDocEnd", "Cmd-Down": "goDocEnd", "Alt-Left": "goWordLeft", + "Alt-Right": "goWordRight", "Cmd-Left": "goLineStart", "Cmd-Right": "goLineEnd", "Alt-Backspace": "delWordBefore", "Ctrl-Alt-Backspace": "delWordAfter", "Alt-Delete": "delWordAfter", "Cmd-S": "save", "Cmd-F": "find", "Cmd-G": "findNext", "Shift-Cmd-G": "findPrev", "Cmd-Alt-F": "replace", "Shift-Cmd-Alt-F": "replaceAll", "Cmd-[": "indentLess", "Cmd-]": "indentMore", From 047e0f513122e62a42e7341f61b9ae08c030f532 Mon Sep 17 00:00:00 2001 From: Marijn Haverbeke Date: Fri, 15 Mar 2013 12:19:19 +0100 Subject: [PATCH 0856/5780] By-word movement change, take 2: count groups of punctuation as words Adds goGroupLeft/Right, delGroupLeft/Right as alternative to word-based motion/deletion. Binds ctrl-left/right/del/backspace to these commands. Also makes word and group-based motion stop at line boundaries. --- lib/codemirror.js | 42 +++++++++++++++++++++++++++--------------- 1 file changed, 27 insertions(+), 15 deletions(-) diff --git a/lib/codemirror.js b/lib/codemirror.js index cb5a781138..86889e32be 100644 --- a/lib/codemirror.js +++ b/lib/codemirror.js @@ -2584,13 +2584,21 @@ window.CodeMirror = (function() { if (unit == "char") moveOnce(); else if (unit == "column") moveOnce(true); - else if (unit == "word") { - var sawWord = false; - for (;;) { - if (dir < 0) if (!moveOnce()) break; - if (isWordChar(lineObj.text.charAt(ch))) sawWord = true; - else if (sawWord) {if (dir < 0) {dir = 1; moveOnce();} break;} - if (dir > 0) if (!moveOnce()) break; + else if (unit == "word" || unit == "group") { + var sawType = null, group = unit == "group"; + for (var first = true;; first = false) { + if (dir < 0 && !moveOnce(!first)) break; + var cur = lineObj.text.charAt(ch) || "\n"; + var type = isWordChar(cur) ? "w" + : !group ? null + : /\s/.test(cur) ? null + : "p"; + if (sawType && sawType != type) { + if (dir < 0) {dir = 1; moveOnce();} + break; + } + if (type) sawType = type; + if (dir > 0 && !moveOnce(!first)) break; } } var result = skipAtomic(doc, Pos(line, ch), dir, true); @@ -2620,9 +2628,9 @@ window.CodeMirror = (function() { if (line) { if (pos.after === false || end == line.length) --start; else ++end; var startChar = line.charAt(start); - var check = isWordChar(startChar) ? isWordChar : - /\s/.test(startChar) ? function(ch) {return /\s/.test(ch);} : - function(ch) {return !/\s/.test(ch) && !isWordChar(ch);}; + var check = isWordChar(startChar) ? isWordChar + : /\s/.test(startChar) ? function(ch) {return /\s/.test(ch);} + : function(ch) {return !/\s/.test(ch) && !isWordChar(ch);}; while (start > 0 && check(line.charAt(start - 1))) --start; while (end < line.length && check(line.charAt(end))) ++end; } @@ -3187,11 +3195,15 @@ window.CodeMirror = (function() { goColumnLeft: function(cm) {cm.moveH(-1, "column");}, goColumnRight: function(cm) {cm.moveH(1, "column");}, goWordLeft: function(cm) {cm.moveH(-1, "word");}, + goGroupRight: function(cm) {cm.moveH(1, "group");}, + goGroupLeft: function(cm) {cm.moveH(-1, "group");}, goWordRight: function(cm) {cm.moveH(1, "word");}, delCharBefore: function(cm) {cm.deleteH(-1, "char");}, delCharAfter: function(cm) {cm.deleteH(1, "char");}, delWordBefore: function(cm) {cm.deleteH(-1, "word");}, delWordAfter: function(cm) {cm.deleteH(1, "word");}, + delGroupBefore: function(cm) {cm.deleteH(-1, "group");}, + delGroupAfter: function(cm) {cm.deleteH(1, "group");}, indentAuto: function(cm) {cm.indentSelection("smart");}, indentMore: function(cm) {cm.indentSelection("add");}, indentLess: function(cm) {cm.indentSelection("subtract");}, @@ -3229,17 +3241,17 @@ window.CodeMirror = (function() { keyMap.pcDefault = { "Ctrl-A": "selectAll", "Ctrl-D": "deleteLine", "Ctrl-Z": "undo", "Shift-Ctrl-Z": "redo", "Ctrl-Y": "redo", "Ctrl-Home": "goDocStart", "Alt-Up": "goDocStart", "Ctrl-End": "goDocEnd", "Ctrl-Down": "goDocEnd", - "Ctrl-Left": "goWordLeft", "Ctrl-Right": "goWordRight", "Alt-Left": "goLineStart", "Alt-Right": "goLineEnd", - "Ctrl-Backspace": "delWordBefore", "Ctrl-Delete": "delWordAfter", "Ctrl-S": "save", "Ctrl-F": "find", + "Ctrl-Left": "goGroupLeft", "Ctrl-Right": "goGroupRight", "Alt-Left": "goLineStart", "Alt-Right": "goLineEnd", + "Ctrl-Backspace": "delGroupBefore", "Ctrl-Delete": "delGroupAfter", "Ctrl-S": "save", "Ctrl-F": "find", "Ctrl-G": "findNext", "Shift-Ctrl-G": "findPrev", "Shift-Ctrl-F": "replace", "Shift-Ctrl-R": "replaceAll", "Ctrl-[": "indentLess", "Ctrl-]": "indentMore", fallthrough: "basic" }; keyMap.macDefault = { "Cmd-A": "selectAll", "Cmd-D": "deleteLine", "Cmd-Z": "undo", "Shift-Cmd-Z": "redo", "Cmd-Y": "redo", - "Cmd-Up": "goDocStart", "Cmd-End": "goDocEnd", "Cmd-Down": "goDocEnd", "Alt-Left": "goWordLeft", - "Alt-Right": "goWordRight", "Cmd-Left": "goLineStart", "Cmd-Right": "goLineEnd", "Alt-Backspace": "delWordBefore", - "Ctrl-Alt-Backspace": "delWordAfter", "Alt-Delete": "delWordAfter", "Cmd-S": "save", "Cmd-F": "find", + "Cmd-Up": "goDocStart", "Cmd-End": "goDocEnd", "Cmd-Down": "goDocEnd", "Alt-Left": "goGroupLeft", + "Alt-Right": "goGroupRight", "Cmd-Left": "goLineStart", "Cmd-Right": "goLineEnd", "Alt-Backspace": "delGroupBefore", + "Ctrl-Alt-Backspace": "delGroupAfter", "Alt-Delete": "delGroupAfter", "Cmd-S": "save", "Cmd-F": "find", "Cmd-G": "findNext", "Shift-Cmd-G": "findPrev", "Cmd-Alt-F": "replace", "Shift-Cmd-Alt-F": "replaceAll", "Cmd-[": "indentLess", "Cmd-]": "indentMore", fallthrough: ["basic", "emacsy"] From d77ad465da4bf87d62829b5339d5451b3904da5d Mon Sep 17 00:00:00 2001 From: Marijn Haverbeke Date: Fri, 15 Mar 2013 12:31:53 +0100 Subject: [PATCH 0857/5780] Add tests for by-group motion --- test/test.js | 34 +++++++++++++++++++++++++++++++--- 1 file changed, 31 insertions(+), 3 deletions(-) diff --git a/test/test.js b/test/test.js index 51631feb1c..3225b0498a 100644 --- a/test/test.js +++ b/test/test.js @@ -910,8 +910,8 @@ testCM("wordMovementCommands", function(cm) { cm.execCommand("goWordLeft"); eqPos(cm.getCursor(), Pos(0, 9)); cm.execCommand("goWordRight"); cm.execCommand("goWordRight"); cm.execCommand("goWordRight"); - eqPos(cm.getCursor(), Pos(1, 1)); - cm.execCommand("goWordRight"); + eqPos(cm.getCursor(), Pos(0, 24)); + cm.execCommand("goWordRight"); cm.execCommand("goWordRight"); eqPos(cm.getCursor(), Pos(1, 9)); cm.execCommand("goWordRight"); eqPos(cm.getCursor(), Pos(1, 13)); @@ -919,6 +919,34 @@ testCM("wordMovementCommands", function(cm) { eqPos(cm.getCursor(), Pos(2, 0)); }, {value: "this is (the) firstline.\na foo12\u00e9\u00f8\u00d7bar\n"}); +testCM("groupMovementCommands", function(cm) { + cm.execCommand("goGroupLeft"); + eqPos(cm.getCursor(), Pos(0, 0)); + cm.execCommand("goGroupRight"); + eqPos(cm.getCursor(), Pos(0, 4)); + cm.execCommand("goGroupRight"); + eqPos(cm.getCursor(), Pos(0, 7)); + cm.execCommand("goGroupRight"); + eqPos(cm.getCursor(), Pos(0, 10)); + cm.execCommand("goGroupLeft"); + eqPos(cm.getCursor(), Pos(0, 7)); + cm.execCommand("goGroupRight"); cm.execCommand("goGroupRight"); cm.execCommand("goGroupRight"); + eqPos(cm.getCursor(), Pos(0, 15)); + cm.setCursor(Pos(0, 17)); + cm.execCommand("goGroupLeft"); + eqPos(cm.getCursor(), Pos(0, 16)); + cm.execCommand("goGroupLeft"); + eqPos(cm.getCursor(), Pos(0, 14)); + cm.execCommand("goGroupRight"); cm.execCommand("goGroupRight"); + eqPos(cm.getCursor(), Pos(0, 20)); + cm.execCommand("goGroupRight"); + eqPos(cm.getCursor(), Pos(1, 5)); + cm.execCommand("goGroupLeft"); cm.execCommand("goGroupLeft"); + eqPos(cm.getCursor(), Pos(1, 0)); + cm.execCommand("goGroupLeft"); + eqPos(cm.getCursor(), Pos(0, 16)); +}, {value: "booo ba---quux. ffff\n abc d"}); + testCM("charMovementCommands", function(cm) { cm.execCommand("goCharLeft"); cm.execCommand("goColumnLeft"); eqPos(cm.getCursor(), Pos(0, 0)); @@ -1344,4 +1372,4 @@ testCM("beforeSelectionChange", function(cm) { cm.execCommand("selectAll"); eqPos(cm.getCursor("start"), Pos(0, 0)); eqPos(cm.getCursor("end"), Pos(9, 9)); -}); \ No newline at end of file +}); From 79d1c6ee34813328e1bd438b23b74f4e3af86557 Mon Sep 17 00:00:00 2001 From: Marijn Haverbeke Date: Fri, 15 Mar 2013 13:00:26 +0100 Subject: [PATCH 0858/5780] Add a removed property to the info passed to change event handlers Issue #1358 --- doc/manual.html | 8 +++++--- lib/codemirror.js | 19 ++++++++++++------- 2 files changed, 17 insertions(+), 10 deletions(-) diff --git a/doc/manual.html b/doc/manual.html index 1e84ca9313..80dd59ba5a 100644 --- a/doc/manual.html +++ b/doc/manual.html @@ -352,7 +352,7 @@

    Events

    "change" (instance, changeObj)
    Fires every time the content of the editor is changed. - The changeObj is a {from, to, text, + The changeObj is a {from, to, text, removed, next} object containing information about the changes that occurred as second argument. from and to are the positions (in the pre-change @@ -360,8 +360,10 @@

    Events

    example, it might be {ch:0, line:18} if the position is at the beginning of line #19). text is an array of strings representing the text that replaced the - changed range (split by line). If multiple changes happened - during a single operation, the object will have + changed range (split by line). removed is the text + that used to be between from and to, + which is overwritten by this change. If multiple changes + happened during a single operation, the object will have a next property pointing to another change object (which may point to another, etc).
    diff --git a/lib/codemirror.js b/lib/codemirror.js index 86889e32be..0e66bae7bd 100644 --- a/lib/codemirror.js +++ b/lib/codemirror.js @@ -2256,6 +2256,18 @@ window.CodeMirror = (function() { }); } + if (hasHandler(cm, "change")) { + var changeObj = {from: from, to: to, + text: change.text, + removed: getBetween(doc, from, to), + origin: change.origin}; + + if (cm.curOp.textChanged) { + for (var cur = cm.curOp.textChanged; cur.next; cur = cur.next) {} + cur.next = changeObj; + } else cm.curOp.textChanged = changeObj; + } + updateDoc(doc, change, spans, selAfter, estimateHeight(cm)); if (!cm.options.lineWrapping) { @@ -2278,13 +2290,6 @@ window.CodeMirror = (function() { var lendiff = change.text.length - (to.line - from.line) - 1; // Remember that these lines changed, for updating the display regChange(cm, from.line, to.line + 1, lendiff); - if (hasHandler(cm, "change")) { - var changeObj = {from: from, to: to, text: change.text, origin: change.origin}; - if (cm.curOp.textChanged) { - for (var cur = cm.curOp.textChanged; cur.next; cur = cur.next) {} - cur.next = changeObj; - } else cm.curOp.textChanged = changeObj; - } } function replaceRange(doc, code, from, to, origin) { From 5dad49618755398fc5c5af115961497a17df5d54 Mon Sep 17 00:00:00 2001 From: Michael Lehenbauer Date: Fri, 15 Mar 2013 13:01:19 +0100 Subject: [PATCH 0859/5780] Add test for change.removed property --- test/test.js | 25 +++++++++++++++++++++++++ 1 file changed, 25 insertions(+) diff --git a/test/test.js b/test/test.js index 3225b0498a..d3e78f80bb 100644 --- a/test/test.js +++ b/test/test.js @@ -1373,3 +1373,28 @@ testCM("beforeSelectionChange", function(cm) { eqPos(cm.getCursor("start"), Pos(0, 0)); eqPos(cm.getCursor("end"), Pos(9, 9)); }); + +testCM("change_removedText", function(cm) { + cm.setValue("abc\ndef"); + + var removedText; + cm.on("change", function(cm, change) { + removedText = [change.removed, change.next && change.next.removed]; + }); + + cm.operation(function() { + cm.replaceRange("xyz", Pos(0, 0), Pos(1,1)); + cm.replaceRange("123", Pos(0,0)); + }); + + eq(removedText[0].join("\n"), "abc\nd"); + eq(removedText[1].join("\n"), ""); + + cm.undo(); + eq(removedText[0].join("\n"), "123"); + eq(removedText[1].join("\n"), "xyz"); + + cm.redo(); + eq(removedText[0].join("\n"), "abc\nd"); + eq(removedText[1].join("\n"), ""); +}); From f0dbe85b3143a451b11b6f4fd7e5e33824b77c5b Mon Sep 17 00:00:00 2001 From: Marijn Haverbeke Date: Fri, 15 Mar 2013 13:10:16 +0100 Subject: [PATCH 0860/5780] Pass change event to line handle's change handlers --- doc/manual.html | 7 +++++-- lib/codemirror.js | 19 ++++++++++--------- 2 files changed, 15 insertions(+), 11 deletions(-) diff --git a/doc/manual.html b/doc/manual.html index 80dd59ba5a..e16c255c9c 100644 --- a/doc/manual.html +++ b/doc/manual.html @@ -468,9 +468,12 @@

    Events

    is associated with the start of the line. Mostly useful when you need to find out when your gutter markers on a given line are removed. -
    "change" ()
    +
    "change" (line, changeObj)
    Fires when the line's text content is changed in any way - (but the line is not deleted outright).
    + (but the line is not deleted outright). The change + object is similar to the one passed + to change event on the editor + object.

    Marked range handles, as returned diff --git a/lib/codemirror.js b/lib/codemirror.js index 0e66bae7bd..772b4807c9 100644 --- a/lib/codemirror.js +++ b/lib/codemirror.js @@ -3917,7 +3917,6 @@ window.CodeMirror = (function() { attachMarkedSpans(line, markedSpans); var estHeight = estimateHeight ? estimateHeight(line) : 1; if (estHeight != line.height) updateLineHeight(line, estHeight); - signalLater(line, "change"); } function cleanUpLine(line) { @@ -4208,6 +4207,10 @@ window.CodeMirror = (function() { function updateDoc(doc, change, markedSpans, selAfter, estimateHeight) { function spansFor(n) {return markedSpans ? markedSpans[n] : null;} + function update(line, text, spans) { + updateLine(line, text, spans, estimateHeight); + signalLater(line, "change", line, change); + } var from = change.from, to = change.to, text = change.text; var firstLine = getLine(doc, from.line), lastLine = getLine(doc, to.line); @@ -4219,27 +4222,25 @@ window.CodeMirror = (function() { // sure line objects move the way they are supposed to. for (var i = 0, e = text.length - 1, added = []; i < e; ++i) added.push(makeLine(text[i], spansFor(i), estimateHeight)); - updateLine(lastLine, lastLine.text, lastSpans, estimateHeight); + update(lastLine, lastLine.text, lastSpans); if (nlines) doc.remove(from.line, nlines); if (added.length) doc.insert(from.line, added); } else if (firstLine == lastLine) { if (text.length == 1) { - updateLine(firstLine, firstLine.text.slice(0, from.ch) + lastText + firstLine.text.slice(to.ch), - lastSpans, estimateHeight); + update(firstLine, firstLine.text.slice(0, from.ch) + lastText + firstLine.text.slice(to.ch), lastSpans); } else { for (var added = [], i = 1, e = text.length - 1; i < e; ++i) added.push(makeLine(text[i], spansFor(i), estimateHeight)); added.push(makeLine(lastText + firstLine.text.slice(to.ch), lastSpans, estimateHeight)); - updateLine(firstLine, firstLine.text.slice(0, from.ch) + text[0], spansFor(0), estimateHeight); + update(firstLine, firstLine.text.slice(0, from.ch) + text[0], spansFor(0)); doc.insert(from.line + 1, added); } } else if (text.length == 1) { - updateLine(firstLine, firstLine.text.slice(0, from.ch) + text[0] + lastLine.text.slice(to.ch), - spansFor(0), estimateHeight); + update(firstLine, firstLine.text.slice(0, from.ch) + text[0] + lastLine.text.slice(to.ch), spansFor(0)); doc.remove(from.line + 1, nlines); } else { - updateLine(firstLine, firstLine.text.slice(0, from.ch) + text[0], spansFor(0), estimateHeight); - updateLine(lastLine, lastText + lastLine.text.slice(to.ch), lastSpans, estimateHeight); + update(firstLine, firstLine.text.slice(0, from.ch) + text[0], spansFor(0)); + update(lastLine, lastText + lastLine.text.slice(to.ch), lastSpans); for (var i = 1, e = text.length - 1, added = []; i < e; ++i) added.push(makeLine(text[i], spansFor(i), estimateHeight)); if (nlines > 1) doc.remove(from.line + 1, nlines - 1); From be5560dfcddb49e6ce3cdcdddceec2cf0fea4d1b Mon Sep 17 00:00:00 2001 From: Marijn Haverbeke Date: Fri, 15 Mar 2013 13:12:19 +0100 Subject: [PATCH 0861/5780] Make sure change objects passed to doc and line objs also have removed prop --- lib/codemirror.js | 25 +++++++++++++------------ 1 file changed, 13 insertions(+), 12 deletions(-) diff --git a/lib/codemirror.js b/lib/codemirror.js index 772b4807c9..99f711ce1e 100644 --- a/lib/codemirror.js +++ b/lib/codemirror.js @@ -2237,6 +2237,8 @@ window.CodeMirror = (function() { text: [change.text[0]], origin: change.origin}; } + change.removed = getBetween(doc, change.from, change.to); + if (!selAfter) selAfter = computeSelAfterChange(doc, change, null); if (doc.cm) makeChangeSingleDocInEditor(doc.cm, change, spans, selAfter); else updateDoc(doc, change, spans, selAfter); @@ -2256,18 +2258,6 @@ window.CodeMirror = (function() { }); } - if (hasHandler(cm, "change")) { - var changeObj = {from: from, to: to, - text: change.text, - removed: getBetween(doc, from, to), - origin: change.origin}; - - if (cm.curOp.textChanged) { - for (var cur = cm.curOp.textChanged; cur.next; cur = cur.next) {} - cur.next = changeObj; - } else cm.curOp.textChanged = changeObj; - } - updateDoc(doc, change, spans, selAfter, estimateHeight(cm)); if (!cm.options.lineWrapping) { @@ -2290,6 +2280,17 @@ window.CodeMirror = (function() { var lendiff = change.text.length - (to.line - from.line) - 1; // Remember that these lines changed, for updating the display regChange(cm, from.line, to.line + 1, lendiff); + + if (hasHandler(cm, "change")) { + var changeObj = {from: from, to: to, + text: change.text, + removed: change.removed, + origin: change.origin}; + if (cm.curOp.textChanged) { + for (var cur = cm.curOp.textChanged; cur.next; cur = cur.next) {} + cur.next = changeObj; + } else cm.curOp.textChanged = changeObj; + } } function replaceRange(doc, code, from, to, origin) { From c5554d5f42776b629b4a786dba43170baf16874a Mon Sep 17 00:00:00 2001 From: "angelo.zerr@gmail.com" Date: Fri, 15 Mar 2013 13:21:29 +0100 Subject: [PATCH 0862/5780] Add xq-light theme --- demo/theme.html | 2 ++ theme/xq-light.css | 43 +++++++++++++++++++++++++++++++++++++++++++ 2 files changed, 45 insertions(+) create mode 100644 theme/xq-light.css diff --git a/demo/theme.html b/demo/theme.html index 42a1b0cde7..046d1fc3e1 100644 --- a/demo/theme.html +++ b/demo/theme.html @@ -15,6 +15,7 @@ + @@ -62,6 +63,7 @@

    CodeMirror: Theme demo

    +

    diff --git a/theme/xq-light.css b/theme/xq-light.css new file mode 100644 index 0000000000..08784d58c4 --- /dev/null +++ b/theme/xq-light.css @@ -0,0 +1,43 @@ +/* +Copyright (C) 2011 by MarkLogic Corporation +Author: Mike Brevoort + +Permission is hereby granted, free of charge, to any person obtaining a copy +of this software and associated documentation files (the "Software"), to deal +in the Software without restriction, including without limitation the rights +to use, copy, modify, merge, publish, distribute, sublicense, and/or sell +copies of the Software, and to permit persons to whom the Software is +furnished to do so, subject to the following conditions: + +The above copyright notice and this permission notice shall be included in +all copies or substantial portions of the Software. + +THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR +IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, +FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE +AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER +LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, +OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN +THE SOFTWARE. +*/ +.cm-s-xq-light span.cm-keyword {line-height: 1em; font-weight: bold; color: #5A5CAD; } +.cm-s-xq-light span.cm-atom {color: #6C8CD5;} +.cm-s-xq-light span.cm-number {color: #164;} +.cm-s-xq-light span.cm-def {text-decoration:underline;} +.cm-s-xq-light span.cm-variable {color: black; } +.cm-s-xq-light span.cm-variable-2 {color:black;} +.cm-s-xq-light span.cm-variable-3 {color: black; } +.cm-s-xq-light span.cm-property {} +.cm-s-xq-light span.cm-operator {} +.cm-s-xq-light span.cm-comment {color: #0080FF; font-style: italic;} +.cm-s-xq-light span.cm-string {color: red;} +.cm-s-xq-light span.cm-meta {color: yellow;} +.cm-s-xq-light span.cm-error {color: #f00;} +.cm-s-xq-light span.cm-qualifier {color: grey} +.cm-s-xq-light span.cm-builtin {color: #7EA656;} +.cm-s-xq-light span.cm-bracket {color: #cc7;} +.cm-s-xq-light span.cm-tag {color: #3F7F7F;} +.cm-s-xq-light span.cm-attribute {color: #7F007F;} + +.cm-s-xq-light .CodeMirror-activeline-background {background: #e8f2ff !important;} +.cm-s-xq-light .CodeMirror-matchingbracket {border:1px solid grey;color:black !important;background:yellow;} \ No newline at end of file From fb5791135f848e9af921ee44a07fd13e6130eb04 Mon Sep 17 00:00:00 2001 From: Marijn Haverbeke Date: Fri, 15 Mar 2013 13:22:38 +0100 Subject: [PATCH 0863/5780] Add .tern-* to .gitignore --- .gitignore | 1 + 1 file changed, 1 insertion(+) diff --git a/.gitignore b/.gitignore index ea08f9d785..bc20ab58d5 100644 --- a/.gitignore +++ b/.gitignore @@ -1,3 +1,4 @@ /node_modules /npm-debug.log test.html +.tern-* From 8be65a4e5fa69070d77d215f06aa5f06b19ddc86 Mon Sep 17 00:00:00 2001 From: "angelo.zerr@gmail.com" Date: Fri, 15 Mar 2013 13:44:56 +0100 Subject: [PATCH 0864/5780] [lint addon] Support onUpdateLinting callback --- addon/lint/lint.js | 1 + 1 file changed, 1 insertion(+) diff --git a/addon/lint/lint.js b/addon/lint/lint.js index f8aa3365df..dd838e7ea5 100644 --- a/addon/lint/lint.js +++ b/addon/lint/lint.js @@ -127,6 +127,7 @@ CodeMirror.validate = (function() { if (state.hasGutter) cm.setGutterMarker(line, GUTTER_ID, makeMarker(tipLabel, maxSeverity, anns.length > 1)); } + if (options.onUpdateLinting) options.onUpdateLinting(annotationsNotSorted, annotations, cm); } function onChange(cm) { From 0a8b204d70ff3a2d0dde80d814cf56a5e139e5b2 Mon Sep 17 00:00:00 2001 From: Marijn Haverbeke Date: Fri, 15 Mar 2013 13:56:01 +0100 Subject: [PATCH 0865/5780] [javascript-lint addon] Make it possible to pass options to JSHint --- addon/lint/javascript-lint.js | 8 +++++++- 1 file changed, 7 insertions(+), 1 deletion(-) diff --git a/addon/lint/javascript-lint.js b/addon/lint/javascript-lint.js index 0d8ec32ed0..2da0e55438 100644 --- a/addon/lint/javascript-lint.js +++ b/addon/lint/javascript-lint.js @@ -9,13 +9,19 @@ "Unmatched ", " and instead saw", " is not defined", "Unclosed string", "Stopping, unable to continue" ]; - CodeMirror.javascriptValidator = function(text) { + function validator(options, text) { JSHINT(text); var errors = JSHINT.data().errors, result = []; if (errors) parseErrors(errors, result); return result; + } + + CodeMirror.javascriptValidatorWithOptions = function(options) { + return function(text) { return validator(options, text); }; }; + CodeMirror.javascriptValidator = CodeMirror.javascriptValidatorWithOptions(null); + function cleanup(error) { // All problems are warnings by default fixWith(error, warnings, "warning", true); From 804e62ba8963ccfaea34032c3af57bb7e36d1156 Mon Sep 17 00:00:00 2001 From: Marijn Haverbeke Date: Fri, 15 Mar 2013 13:59:11 +0100 Subject: [PATCH 0866/5780] Mistake in patch 0a8b204d70ff3a2d0dde80d814cf56a5e139e5b2 --- addon/lint/javascript-lint.js | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/addon/lint/javascript-lint.js b/addon/lint/javascript-lint.js index 2da0e55438..db9ff6b224 100644 --- a/addon/lint/javascript-lint.js +++ b/addon/lint/javascript-lint.js @@ -10,7 +10,7 @@ "Unclosed string", "Stopping, unable to continue" ]; function validator(options, text) { - JSHINT(text); + JSHINT(text, options); var errors = JSHINT.data().errors, result = []; if (errors) parseErrors(errors, result); return result; From ef896f9531ceac49c174eb8f84c7123b419e2702 Mon Sep 17 00:00:00 2001 From: Marijn Haverbeke Date: Fri, 15 Mar 2013 14:58:22 +0100 Subject: [PATCH 0867/5780] [folding addon] Rewrite tagRangeFinder Closes #1360 --- addon/fold/foldcode.js | 142 +++++++++++++++-------------------------- 1 file changed, 50 insertions(+), 92 deletions(-) diff --git a/addon/fold/foldcode.js b/addon/fold/foldcode.js index 90019762fc..bdc641d3c6 100644 --- a/addon/fold/foldcode.js +++ b/addon/fold/foldcode.js @@ -1,109 +1,67 @@ -// the tagRangeFinder function is -// Copyright (C) 2011 by Daniel Glazman -// released under the MIT license (../../LICENSE) like the rest of CodeMirror -CodeMirror.tagRangeFinder = function(cm, start) { +CodeMirror.tagRangeFinder = (function() { var nameStartChar = "A-Z_a-z\\u00C0-\\u00D6\\u00D8-\\u00F6\\u00F8-\\u02FF\\u0370-\\u037D\\u037F-\\u1FFF\\u200C-\\u200D\\u2070-\\u218F\\u2C00-\\u2FEF\\u3001-\\uD7FF\\uF900-\\uFDCF\\uFDF0-\\uFFFD"; var nameChar = nameStartChar + "\-\:\.0-9\\u00B7\\u0300-\\u036F\\u203F-\\u2040"; - var xmlNAMERegExp = new RegExp("^[" + nameStartChar + "][" + nameChar + "]*"); + var xmlTagStart = new RegExp("<(/?)([" + nameStartChar + "][" + nameChar + "]*)", "g"); - var lineText = cm.getLine(start.line); - var found = false; - var tag = null; - var pos = start.ch; - while (!found) { - pos = lineText.indexOf("<", pos); - if (-1 == pos) // no tag on line - return; - if (pos + 1 < lineText.length && lineText[pos + 1] == "/") { // closing tag - pos++; - continue; - } - // ok we seem to have a start tag - if (!lineText.substr(pos + 1).match(xmlNAMERegExp)) { // not a tag name... - pos++; - continue; + return function(cm, start) { + var line = start.line, ch = start.ch, lineText = cm.getLine(line); + + function nextLine() { + if (line >= cm.lastLine()) return; + ch = 0; + lineText = cm.getLine(++line); + return true; } - var gtPos = lineText.indexOf(">", pos + 1); - if (-1 == gtPos) { // end of start tag not in line - var l = start.line + 1; - var foundGt = false; - var lastLine = cm.lineCount(); - while (l < lastLine && !foundGt) { - var lt = cm.getLine(l); - gtPos = lt.indexOf(">"); - if (-1 != gtPos) { // found a > - foundGt = true; - var slash = lt.lastIndexOf("/", gtPos); - if (-1 != slash && slash < gtPos) { - var str = lineText.substr(slash, gtPos - slash + 1); - if (!str.match( /\/\s*\>/ )) // yep, that's the end of empty tag - return; - } - } - l++; + function toTagEnd() { + for (;;) { + var gt = lineText.indexOf(">", ch); + if (gt == -1) { if (nextLine()) continue; else return; } + var lastSlash = lineText.lastIndexOf("/", gt); + var selfClose = lastSlash > -1 && /^\s*$/.test(lineText.slice(lastSlash + 1, gt)); + ch = gt + 1; + return selfClose ? "selfClose" : "regular"; } - found = true; } - else { - var slashPos = lineText.lastIndexOf("/", gtPos); - if (-1 == slashPos) { // cannot be empty tag - found = true; - // don't continue - } - else { // empty tag? - // check if really empty tag - var str = lineText.substr(slashPos, gtPos - slashPos + 1); - if (!str.match( /\/\s*\>/ )) { // finally not empty - found = true; - // don't continue - } + function toNextTag() { + for (;;) { + xmlTagStart.lastIndex = ch; + var found = xmlTagStart.exec(lineText); + if (!found) { if (nextLine()) continue; else return; } + ch = found.index + found[0].length; + return found; } } - if (found) { - var subLine = lineText.substr(pos + 1); - tag = subLine.match(xmlNAMERegExp); - if (tag) { - // we have an element name, wooohooo ! - tag = tag[0]; - // do we have the close tag on same line ??? - if (-1 != lineText.indexOf("", pos)) // yep - { - found = false; - } - // we don't, so we have a candidate... + + var stack = [], startCh; + for (;;) { + var openTag = toNextTag(), end; + if (!openTag || line != start.line || !(end = toTagEnd())) return; + if (!openTag[1] && end != "selfClose") { + stack.push(openTag[2]); + startCh = ch; + break; } - else - found = false; } - if (!found) - pos++; - } - if (found) { - var startTag = "(\\<\\/" + tag + "\\>)|(\\<" + tag + "\\>)|(\\<" + tag + "\\s)|(\\<" + tag + "$)"; - var startTagRegExp = new RegExp(startTag); - var endTag = ""; - var depth = 1; - var l = start.line + 1; - var lastLine = cm.lineCount(); - while (l < lastLine) { - lineText = cm.getLine(l); - var match = lineText.match(startTagRegExp); - if (match) { - for (var i = 0; i < match.length; i++) { - if (match[i] == endTag) - depth--; - else - depth++; - if (!depth) return {from: CodeMirror.Pos(start.line, gtPos + 1), - to: CodeMirror.Pos(l, match.index)}; + for (;;) { + var next = toNextTag(), end, tagLine = line, tagCh = ch - (next ? next[0].length : 0); + if (!next || !(end = toTagEnd())) return; + if (end == "selfClose") continue; + if (next[1]) { // closing tag + for (var i = stack.length - 1; i >= 0; --i) if (stack[i] == next[2]) { + stack.length = i; + break; } + if (!stack.length) return { + from: CodeMirror.Pos(start.line, startCh), + to: CodeMirror.Pos(tagLine, tagCh) + }; + } else { // opening tag + stack.push(next[2]); } - l++; } - return; - } -}; + }; +})(); CodeMirror.braceRangeFinder = function(cm, start) { var line = start.line, lineText = cm.getLine(line); From 00bcbcde2e0571ed4e635b34593f4a126d62c4b1 Mon Sep 17 00:00:00 2001 From: Yunchi Luo Date: Tue, 12 Mar 2013 02:49:02 -0400 Subject: [PATCH 0868/5780] [vim] Make search highlighting respect ^ (start of line) --- keymap/vim.js | 67 ++++++++++++++++++++++++++++++--------------------- 1 file changed, 40 insertions(+), 27 deletions(-) diff --git a/keymap/vim.js b/keymap/vim.js index 5a3469453e..e5c1121532 100644 --- a/keymap/vim.js +++ b/keymap/vim.js @@ -729,7 +729,7 @@ getSearchState(cm).setReversed(!forward); var promptPrefix = (forward) ? '/' : '?'; var originalQuery = getSearchState(cm).getQuery(); - var originalPos = cm.getCursor(); + var originalScrollPos = cm.getScrollInfo(); function handleQuery(query, ignoreCase, smartCase) { try { updateSearchQuery(cm, query, ignoreCase, smartCase); @@ -744,21 +744,22 @@ }); } function onPromptClose(query) { - cm.scrollIntoView(originalPos); + cm.scrollTo(originalScrollPos.left, originalScrollPos.top); handleQuery(query, true /** ignoreCase */, true /** smartCase */); } function onPromptKeyUp(e, query) { - if (query) { - try { - updateSearchQuery(cm, query, true /** ignoreCase */, - true /** smartCase */); - cm.scrollIntoView(findNext(cm, forward, query)); - } catch (e) { - // Swallow bad regexes for incremental search. - } + var parsedQuery; + try { + parsedQuery = updateSearchQuery(cm, query, + true /** ignoreCase */, true /** smartCase */) + } catch (e) { + // Swallow bad regexes for incremental search. + } + if (parsedQuery) { + cm.scrollIntoView(findNext(cm, !forward, parsedQuery), 30); } else { clearSearchHighlight(cm); - cm.scrollIntoView(originalPos); + cm.scrollTo(originalScrollPos.left, originalScrollPos.top); } } function onPromptKeyDown(e, query, close) { @@ -766,7 +767,7 @@ if (keyName == 'Esc' || keyName == 'Ctrl-C' || keyName == 'Ctrl-[') { updateSearchQuery(cm, originalQuery); clearSearchHighlight(cm); - cm.scrollIntoView(originalPos); + cm.scrollTo(originalScrollPos.left, originalScrollPos.top); CodeMirror.e_stop(e); close(); @@ -2148,28 +2149,40 @@ } return(false); } + // Returns true if the query is valid. function updateSearchQuery(cm, rawQuery, ignoreCase, smartCase) { - cm.operation(function() { - var state = getSearchState(cm); - if (!rawQuery) { - return; - } - var query = parseQuery(cm, rawQuery, !!ignoreCase, !!smartCase); - if (!query) { - return; - } - highlightSearchMatches(cm, query); - if (regexEqual(query, state.getQuery())) { - return; - } - state.setQuery(query); - }); + if (!rawQuery) { + return; + } + var state = getSearchState(cm); + var query = parseQuery(cm, rawQuery, !!ignoreCase, !!smartCase); + if (!query) { + return; + } + highlightSearchMatches(cm, query); + if (regexEqual(query, state.getQuery())) { + return query; + } + state.setQuery(query); + return query; } function searchOverlay(query) { + if (query.source.charAt(0) == '^') { + var matchSol = true; + } return { token: function(stream) { + if (matchSol && !stream.sol()) { + stream.skipToEnd(); + return; + } var match = stream.match(query, false); if (match) { + if (match[0].length == 0) { + // Matched empty string, skip to next. + stream.next(); + return; + } if (!stream.sol()) { // Backtrack 1 to match \b stream.backUp(1); From a36f4e941143996eee30a96c9042e4fd52650f63 Mon Sep 17 00:00:00 2001 From: Marijn Haverbeke Date: Fri, 15 Mar 2013 15:12:29 +0100 Subject: [PATCH 0869/5780] [searchcursor addon] Fix reverse searching of regexps Closes #1364 --- addon/search/searchcursor.js | 17 ++++++++--------- 1 file changed, 8 insertions(+), 9 deletions(-) diff --git a/addon/search/searchcursor.js b/addon/search/searchcursor.js index e6554ce56b..0d2d874103 100644 --- a/addon/search/searchcursor.js +++ b/addon/search/searchcursor.js @@ -17,16 +17,15 @@ this.matches = function(reverse, pos) { if (reverse) { query.lastIndex = 0; - var line = cm.getLine(pos.line).slice(0, pos.ch), match = query.exec(line), start = 0; - while (match) { - start += match.index + 1; - line = line.slice(start); - query.lastIndex = 0; - var newmatch = query.exec(line); - if (newmatch) match = newmatch; - else break; + var line = cm.getLine(pos.line).slice(0, pos.ch), cutOff = 0, match, start; + for (;;) { + query.lastIndex = cutOff; + var newMatch = query.exec(line); + if (!newMatch) break; + match = newMatch; + start = cutOff; + cutOff = match.index + 1; } - start--; } else { query.lastIndex = pos.ch; var line = cm.getLine(pos.line), match = query.exec(line), From 7b09d40c8999ede663ff1b27ba627e424678f386 Mon Sep 17 00:00:00 2001 From: Jason Date: Thu, 14 Mar 2013 23:38:33 -0700 Subject: [PATCH 0870/5780] Added syntax highlighting for Math modes --- mode/stex/stex.js | 313 ++++++++++++++++++++++++++++------------------ mode/stex/test.js | 13 ++ 2 files changed, 205 insertions(+), 121 deletions(-) diff --git a/mode/stex/stex.js b/mode/stex/stex.js index 42ed82c669..318b63acd4 100644 --- a/mode/stex/stex.js +++ b/mode/stex/stex.js @@ -3,95 +3,93 @@ * Licence: MIT */ -CodeMirror.defineMode("stex", function() -{ +CodeMirror.defineMode("stex", function() { + "use strict"; + function pushCommand(state, command) { - state.cmdState.push(command); + state.cmdState.push(command); } - function peekCommand(state) { - if (state.cmdState.length>0) - return state.cmdState[state.cmdState.length-1]; - else - return null; + function peekCommand(state) { + if (state.cmdState.length > 0) { + return state.cmdState[state.cmdState.length - 1]; + } else { + return null; + } } function popCommand(state) { - if (state.cmdState.length>0) { - var plug = state.cmdState.pop(); - plug.closeBracket(); - } + var plug = state.cmdState.pop(); + if (plug) { + plug.closeBracket(); + } } - function applyMostPowerful(state) { - var context = state.cmdState; - for (var i = context.length - 1; i >= 0; i--) { - var plug = context[i]; - if (plug.name=="DEFAULT") - continue; - return plug.styleIdentifier(); - } - return null; + // returns the non-default plugin closest to the end of the list + function getMostPowerful(state) { + var context = state.cmdState; + for (var i = context.length - 1; i >= 0; i--) { + var plug = context[i]; + if (plug.name == "DEFAULT") { + continue; + } + return plug; + } + return { styleIdentifier: function() { return null; } }; } - function addPluginPattern(pluginName, cmdStyle, brackets, styles) { - return function () { - this.name=pluginName; - this.bracketNo = 0; - this.style=cmdStyle; - this.styles = styles; - this.brackets = brackets; - - this.styleIdentifier = function() { - if (this.bracketNo<=this.styles.length) - return this.styles[this.bracketNo-1]; - else - return null; - }; - this.openBracket = function() { - this.bracketNo++; - return "bracket"; - }; - this.closeBracket = function() {}; - }; + function addPluginPattern(pluginName, cmdStyle, styles) { + return function () { + this.name = pluginName; + this.bracketNo = 0; + this.style = cmdStyle; + this.styles = styles; + this.argument = null; // \begin and \end have arguments that follow. These are stored in the plugin + + this.styleIdentifier = function() { + return this.styles[this.bracketNo - 1] || null; + }; + this.openBracket = function() { + this.bracketNo++; + return "bracket"; + }; + this.closeBracket = function() {}; + }; } - var plugins = new Array(); - - plugins["importmodule"] = addPluginPattern("importmodule", "tag", "{[", ["string", "builtin"]); - plugins["documentclass"] = addPluginPattern("documentclass", "tag", "{[", ["", "atom"]); - plugins["usepackage"] = addPluginPattern("documentclass", "tag", "[", ["atom"]); - plugins["begin"] = addPluginPattern("documentclass", "tag", "[", ["atom"]); - plugins["end"] = addPluginPattern("documentclass", "tag", "[", ["atom"]); + var plugins = {}; + + plugins["importmodule"] = addPluginPattern("importmodule", "tag", ["string", "builtin"]); + plugins["documentclass"] = addPluginPattern("documentclass", "tag", ["", "atom"]); + plugins["usepackage"] = addPluginPattern("usepackage", "tag", ["atom"]); + plugins["begin"] = addPluginPattern("begin", "tag", ["atom"]); + plugins["end"] = addPluginPattern("end", "tag", ["atom"]); plugins["DEFAULT"] = function () { - this.name="DEFAULT"; - this.style="tag"; + this.name = "DEFAULT"; + this.style = "tag"; - this.styleIdentifier = this.openBracket = this.closeBracket = function() {}; + this.styleIdentifier = this.openBracket = this.closeBracket = function() {}; }; function setState(state, f) { - state.f = f; + state.f = f; } + // called when in a normal (no environment) context function normal(source, state) { - if (source.match(/^\\[a-zA-Z@]+/)) { - var cmdName = source.current(); - cmdName = cmdName.substr(1, cmdName.length-1); - var plug; - if (plugins.hasOwnProperty(cmdName)) { - plug = plugins[cmdName]; - } else { - plug = plugins["DEFAULT"]; - } - plug = new plug(); - pushCommand(state, plug); - setState(state, beginParams); - return plug.style; - } + var plug; + // Do we look like '\command' ? If so, attempt to apply the plugin 'command' + if (source.match(/^\\[a-zA-Z@]+/)) { + var cmdName = source.current().slice(1); + plug = plugins[cmdName] || plugins["DEFAULT"]; + plug = new plug(); + pushCommand(state, plug); + setState(state, beginParams); + return plug.style; + } - // escape characters + // escape characters if (source.match(/^\\[$&%#{}_]/)) { return "tag"; } @@ -101,74 +99,147 @@ CodeMirror.defineMode("stex", function() return "tag"; } - var ch = source.next(); - if (ch == "%") { + // find if we're starting various math modes + if (source.match("\\[")) { + setState(state, function(source, state){ return inMathMode(source, state, "\\]"); }); + return "keyword"; + } + if (source.match("$$")) { + setState(state, function(source, state){ return inMathMode(source, state, "$$"); }); + return "keyword"; + } + if (source.match("$")) { + setState(state, function(source, state){ return inMathMode(source, state, "$"); }); + return "keyword"; + } + + var ch = source.next(); + if (ch == "%") { // special case: % at end of its own line; stay in same state if (!source.eol()) { setState(state, inCComment); } - return "comment"; - } - else if (ch=='}' || ch==']') { - plug = peekCommand(state); - if (plug) { - plug.closeBracket(ch); - setState(state, beginParams); - } else - return "error"; - return "bracket"; - } else if (ch=='{' || ch=='[') { - plug = plugins["DEFAULT"]; - plug = new plug(); - pushCommand(state, plug); - return "bracket"; - } - else if (/\d/.test(ch)) { - source.eatWhile(/[\w.%]/); - return "atom"; - } - else { - source.eatWhile(/[\w-_]/); - return applyMostPowerful(state); - } + return "comment"; + } + else if (ch == '}' || ch == ']') { + plug = peekCommand(state); + if (plug) { + plug.closeBracket(ch); + setState(state, beginParams); + } else { + return "error"; + } + return "bracket"; + } else if (ch == '{' || ch == '[') { + plug = plugins["DEFAULT"]; + plug = new plug(); + pushCommand(state, plug); + return "bracket"; + } + else if (/\d/.test(ch)) { + source.eatWhile(/[\w.%]/); + return "atom"; + } + else { + source.eatWhile(/[\w\-_]/); + plug = getMostPowerful(state); + if (plug.name == 'begin') { + plug.argument = source.current(); + } + return plug.styleIdentifier(); + } } function inCComment(source, state) { - source.skipToEnd(); - setState(state, normal); - return "comment"; + source.skipToEnd(); + setState(state, normal); + return "comment"; + } + + function inMathMode(source, state, endModeSeq) { + if (source.eatSpace()) { + return null; + } + if (source.match(endModeSeq)) { + setState(state, normal); + return "keyword"; + } + if (source.match(/^\\[a-zA-Z@]+/)) { + return "tag"; + } + if (source.match(/^[a-zA-Z]+/)) { + return "variable-2"; + } + // escape characters + if (source.match(/^\\[$&%#{}_]/)) { + return "tag"; + } + // white space control characters + if (source.match(/^\\[,;!\/]/)) { + return "tag"; + } + // special math-mode characters + if (source.match(/^[\^_&]/)) { + return "tag"; + } + // non-special characters + if (source.match(/^[+\-<>|=,\/@!*:;'"`~#?]/)) { + return null; + } + if (source.match(/^(\d+\.\d*|\d*\.\d+|\d+)/)) { + return "number"; + } + var ch = source.next(); + if (ch == "{" || ch == "}" || ch == "[" || ch == "]" || ch == "(" || ch == ")") { + return "bracket"; + } + + // eat comments here, because inCComment returns us to normal state! + if (ch == "%") { + if (!source.eol()) { + source.skipToEnd(); + } + return "comment"; + } + return "error"; } function beginParams(source, state) { - var ch = source.peek(); - if (ch == '{' || ch == '[') { - var lastPlug = peekCommand(state); - lastPlug.openBracket(ch); - source.eat(ch); - setState(state, normal); - return "bracket"; - } - if (/[ \t\r]/.test(ch)) { - source.eat(ch); - return null; - } - setState(state, normal); - lastPlug = peekCommand(state); - if (lastPlug) { - popCommand(state); - } + var ch = source.peek(), lastPlug; + if (ch == '{' || ch == '[') { + lastPlug = peekCommand(state); + lastPlug.openBracket(ch); + source.eat(ch); + setState(state, normal); + return "bracket"; + } + if (/[ \t\r]/.test(ch)) { + source.eat(ch); + return null; + } + setState(state, normal); + popCommand(state); + return normal(source, state); } return { - startState: function() { return { f:normal, cmdState:[] }; }, - copyState: function(s) { return { f: s.f, cmdState: s.cmdState.slice(0, s.cmdState.length) }; }, - - token: function(stream, state) { - var t = state.f(stream, state); - return t; - } - }; + startState: function() { + return { + cmdState: [], + f: normal + }; + }, + copyState: function(s) { + return { + cmdState: s.cmdState.slice(), + f: s.f + }; + }, + token: function(stream, state) { + return state.f(stream, state); + } + }; }); CodeMirror.defineMIME("text/x-stex", "stex"); diff --git a/mode/stex/test.js b/mode/stex/test.js index e7e9c29c1e..727a7db07e 100644 --- a/mode/stex/test.js +++ b/mode/stex/test.js @@ -101,4 +101,17 @@ MT("tagBracket", "[tag \\newcommand][bracket {][tag \\pop][bracket }]"); + + MT("inlineMathTagFollowedByNumber", + "[keyword $][tag \\pi][number 2][keyword $]"); + + MT("inlineMath", + "[keyword $][number 3][variable-2 x][tag ^][number 2.45]-[tag \\sqrt][bracket {][tag \\$\\alpha][bracket }] = [number 2][keyword $] other text"); + + MT("displayMath", + "More [keyword $$]\t[variable-2 S][tag ^][variable-2 n][tag \\sum] [variable-2 i][keyword $$] other text"); + + MT("mathWithComment", + "[keyword $][variable-2 x] [comment % $]", + "[variable-2 y][keyword $] other text"); })(); From a9b6656e26ce591394de398f9ea4dbe628bc0c64 Mon Sep 17 00:00:00 2001 From: Marijn Haverbeke Date: Fri, 15 Mar 2013 15:45:21 +0100 Subject: [PATCH 0871/5780] Add a second parameter to coordsChar to determine the origin --- doc/manual.html | 11 +++++++---- lib/codemirror.js | 28 +++++++++++++++++++++++----- 2 files changed, 30 insertions(+), 9 deletions(-) diff --git a/doc/manual.html b/doc/manual.html index e16c255c9c..6fee9965eb 100644 --- a/doc/manual.html +++ b/doc/manual.html @@ -1205,10 +1205,13 @@

    Sizing, scrolling and positioning methods

    it'll give the size of the whole character, rather than just the position that the cursor would have when it would sit at that position. -
    cm.coordsChar(object) → pos
    -
    Given an {left, top} object (in page coordinates), - returns the {line, ch} position that corresponds to - it.
    +
    cm.coordsChar(object, mode) → pos
    +
    Given an {left, top} object, returns + the {line, ch} position that corresponds to it. The + optional mode parameter determines relative to what + the coordinates are interpreted. It may + be "window", "page" (the default), + or "local".
    cm.defaultTextHeight() → number
    Returns the line height of the default font for the editor.
    diff --git a/lib/codemirror.js b/lib/codemirror.js index 99f711ce1e..1baf27e308 100644 --- a/lib/codemirror.js +++ b/lib/codemirror.js @@ -1087,6 +1087,26 @@ window.CodeMirror = (function() { return rect; } + // Context may be "window", "page", "div", or "local"/null + // Result is in local coords + function fromCoordSystem(cm, coords, context) { + if (context == "div") return coords; + var left = coords.left, top = coords.top; + if (context == "page") { + left -= window.pageXOffset || (document.documentElement || document.body).scrollLeft; + top -= window.pageYOffset || (document.documentElement || document.body).scrollTop; + } + var lineSpaceBox = getRect(cm.display.lineSpace); + left -= lineSpaceBox.left; + top -= lineSpaceBox.top; + if (context == "local" || !context) { + var editorBox = getRect(cm.display.wrapper); + left -= editorBox.left; + top -= editorBox.top; + } + return {left: left, top: top}; + } + function charCoords(cm, pos, context, lineObj) { if (!lineObj) lineObj = getLine(cm.doc, pos.line); return intoCoordSystem(cm, lineObj, measureChar(cm, lineObj, pos.ch), context); @@ -2749,11 +2769,9 @@ window.CodeMirror = (function() { return charCoords(this, clipPos(this.doc, pos), mode || "page"); }, - coordsChar: function(coords) { - var off = getRect(this.display.lineSpace); - var scrollY = window.pageYOffset || (document.documentElement || document.body).scrollTop; - var scrollX = window.pageXOffset || (document.documentElement || document.body).scrollLeft; - return coordsChar(this, coords.left - off.left - scrollX, coords.top - off.top - scrollY); + coordsChar: function(coords, mode) { + coords = fromCoordSystem(this, coords, mode || "page"); + return coordsChar(this, coords.left, coords.top); }, defaultTextHeight: function() { return textHeight(this.display); }, From e8419a8e661b6bafa65a0404520fd3927d3ffea3 Mon Sep 17 00:00:00 2001 From: Marijn Haverbeke Date: Fri, 15 Mar 2013 15:52:52 +0100 Subject: [PATCH 0872/5780] Add goLineLeft and goLineRight commands For line-end/start navigation that stops when a line wraps. --- lib/codemirror.js | 10 +++++++++- 1 file changed, 9 insertions(+), 1 deletion(-) diff --git a/lib/codemirror.js b/lib/codemirror.js index 1baf27e308..a3743b4cf6 100644 --- a/lib/codemirror.js +++ b/lib/codemirror.js @@ -1158,7 +1158,7 @@ window.CodeMirror = (function() { function coordsChar(cm, x, y) { var doc = cm.doc; y += cm.display.viewOffset; - if (y < 0) return PosMaybeOutside(doc.first, 0, true); + if (y < 0) y = 0; var lineNo = lineAtHeight(doc, y), last = doc.first + doc.size - 1; if (lineNo > last) return PosMaybeOutside(doc.first + doc.size - 1, getLine(doc, last).text.length, true); @@ -3210,6 +3210,14 @@ window.CodeMirror = (function() { goLineEnd: function(cm) { cm.extendSelection(lineEnd(cm, cm.getCursor().line)); }, + goLineRight: function(cm) { + var top = cm.charCoords(cm.getCursor(), "div").top + 5; + cm.extendSelection(cm.coordsChar({left: cm.display.lineDiv.offsetWidth + 100, top: top}, "div")); + }, + goLineLeft: function(cm) { + var top = cm.charCoords(cm.getCursor(), "div").top + 5; + cm.extendSelection(cm.coordsChar({left: 0, top: top}, "div")); + }, goLineUp: function(cm) {cm.moveV(-1, "line");}, goLineDown: function(cm) {cm.moveV(1, "line");}, goPageUp: function(cm) {cm.moveV(-1, "page");}, From a4cda3dbb7b7c508843181c6d1a77e70d57e4ab5 Mon Sep 17 00:00:00 2001 From: Marijn Haverbeke Date: Fri, 15 Mar 2013 16:13:02 +0100 Subject: [PATCH 0873/5780] Fix mistake in a36f4e941143996eee30a96c9042e4fd52650f63 Issue #1364 --- addon/search/searchcursor.js | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/addon/search/searchcursor.js b/addon/search/searchcursor.js index 0d2d874103..fd134636e3 100644 --- a/addon/search/searchcursor.js +++ b/addon/search/searchcursor.js @@ -23,7 +23,7 @@ var newMatch = query.exec(line); if (!newMatch) break; match = newMatch; - start = cutOff; + start = match.index; cutOff = match.index + 1; } } else { From 5eac0a248a1b1000295dfe4972e60848c02ec166 Mon Sep 17 00:00:00 2001 From: Marijn Haverbeke Date: Fri, 15 Mar 2013 16:16:32 +0100 Subject: [PATCH 0874/5780] Fix bad change from e8419a8e661b6bafa65a0404520fd3927d3ffea3 --- lib/codemirror.js | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/lib/codemirror.js b/lib/codemirror.js index a3743b4cf6..cd604c1433 100644 --- a/lib/codemirror.js +++ b/lib/codemirror.js @@ -1158,7 +1158,7 @@ window.CodeMirror = (function() { function coordsChar(cm, x, y) { var doc = cm.doc; y += cm.display.viewOffset; - if (y < 0) y = 0; + if (y < 0) return PosMaybeOutside(doc.first, 0, true); var lineNo = lineAtHeight(doc, y), last = doc.first + doc.size - 1; if (lineNo > last) return PosMaybeOutside(doc.first + doc.size - 1, getLine(doc, last).text.length, true); From 037993fbf02483d19a47334d1bbdfeb9b6237ceb Mon Sep 17 00:00:00 2001 From: Marijn Haverbeke Date: Fri, 15 Mar 2013 17:06:33 +0100 Subject: [PATCH 0875/5780] Kludge to force trailing space on a wrapped line to be rendered in Webkit and IE Closes #1362 --- lib/codemirror.js | 14 ++++++++++++++ 1 file changed, 14 insertions(+) diff --git a/lib/codemirror.js b/lib/codemirror.js index cd604c1433..3a40c06ffe 100644 --- a/lib/codemirror.js +++ b/lib/codemirror.js @@ -4058,6 +4058,8 @@ window.CodeMirror = (function() { builder.measure = line == realLine && measure; builder.pos = 0; builder.addToken = builder.measure ? buildTokenMeasure : buildToken; + if ((ie || webkit) && cm.getOption("lineWrapping")) + builder.addToken = buildTokenSplitSpaces(builder.addToken); if (measure && sawBefore && line != realLine && !builder.addedOne) { measure[0] = builder.pre.appendChild(zeroWidthElement(cm.display.measure)); builder.addedOne = true; @@ -4158,6 +4160,18 @@ window.CodeMirror = (function() { if (text.length) builder.addedOne = true; } + function buildTokenSplitSpaces(inner) { + function split(old) { + var out = " "; + for (var i = 0; i < old.length - 2; ++i) out += i % 2 ? " " : "\u00a0"; + out += " "; + return out; + } + return function(builder, text, style, startStyle, endStyle) { + return inner(builder, text.replace(/ {3,}/, split), style, startStyle, endStyle); + }; + } + function buildCollapsedSpan(builder, size, widget) { if (widget) { if (!builder.display) widget = widget.cloneNode(true); From fbefd524e9079db7e01c95bc36985443559bc927 Mon Sep 17 00:00:00 2001 From: Marijn Haverbeke Date: Fri, 15 Mar 2013 17:31:21 +0100 Subject: [PATCH 0876/5780] [xml mode] Fix indentation bug when attributes are contd on indented line Closes #1366 --- mode/xml/xml.js | 12 +++++++----- 1 file changed, 7 insertions(+), 5 deletions(-) diff --git a/mode/xml/xml.js b/mode/xml/xml.js index 7b11fd6746..f08f9f5679 100644 --- a/mode/xml/xml.js +++ b/mode/xml/xml.js @@ -165,7 +165,7 @@ CodeMirror.defineMode("xml", function(config, parserConfig) { }; } - var curState, setStyle; + var curState, curStream, setStyle; function pass() { for (var i = arguments.length - 1; i >= 0; i--) curState.cc.push(arguments[i]); } @@ -191,6 +191,7 @@ CodeMirror.defineMode("xml", function(config, parserConfig) { function element(type) { if (type == "openTag") { curState.tagName = tagName; + curState.tagStart = curStream.column(); return cont(attributes, endtag(curState.startOfLine)); } else if (type == "closeTag") { var err = false; @@ -212,7 +213,7 @@ CodeMirror.defineMode("xml", function(config, parserConfig) { function endtag(startOfLine) { return function(type) { var tagName = curState.tagName; - curState.tagName = null; + curState.tagName = curState.tagStart = null; if (type == "selfcloseTag" || (type == "endTag" && Kludges.autoSelfClosers.hasOwnProperty(tagName.toLowerCase()))) { maybePopContext(tagName.toLowerCase()); @@ -274,11 +275,11 @@ CodeMirror.defineMode("xml", function(config, parserConfig) { return { startState: function() { - return {tokenize: inText, cc: [], indented: 0, startOfLine: true, tagName: null, context: null}; + return {tokenize: inText, cc: [], indented: 0, startOfLine: true, tagName: null, tagStart: null, context: null}; }, token: function(stream, state) { - if (stream.sol()) { + if (!state.tagName && stream.sol()) { state.startOfLine = true; state.indented = stream.indentation(); } @@ -288,7 +289,7 @@ CodeMirror.defineMode("xml", function(config, parserConfig) { var style = state.tokenize(stream, state); state.type = type; if ((style || type) && style != "comment") { - curState = state; + curState = state; curStream = stream; while (true) { var comb = state.cc.pop() || element; if (comb(type || style)) break; @@ -303,6 +304,7 @@ CodeMirror.defineMode("xml", function(config, parserConfig) { if ((state.tokenize != inTag && state.tokenize != inText) || context && context.noIndent) return fullLine ? fullLine.match(/^(\s*)/)[0].length : 0; + if (state.tagName) return state.tagStart + indentUnit; if (alignCDATA && / Date: Mon, 18 Mar 2013 10:21:27 +0100 Subject: [PATCH 0877/5780] [sql mode] Work when not given a configuration Closes #1374 --- mode/sql/sql.js | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/mode/sql/sql.js b/mode/sql/sql.js index 2a16882aea..510110b307 100644 --- a/mode/sql/sql.js +++ b/mode/sql/sql.js @@ -4,7 +4,7 @@ CodeMirror.defineMode("sql", function(config, parserConfig) { var client = parserConfig.client || {}, atoms = parserConfig.atoms || {"false": true, "true": true, "null": true}, builtin = parserConfig.builtin || {}, - keywords = parserConfig.keywords, + keywords = parserConfig.keywords || {}, operatorChars = parserConfig.operatorChars || /^[*+\-%<>!=&|~^]/, support = parserConfig.support || {}, hooks = parserConfig.hooks || {}, From fdacdc1ca6507d7aef1310730a263b8f03fdd5d7 Mon Sep 17 00:00:00 2001 From: Marijn Haverbeke Date: Mon, 18 Mar 2013 11:41:39 +0100 Subject: [PATCH 0878/5780] Signal a beforeCursorEnter event on marked ranges Closes #1368 --- doc/manual.html | 5 +++++ lib/codemirror.js | 18 +++++++++++------- 2 files changed, 16 insertions(+), 7 deletions(-) diff --git a/doc/manual.html b/doc/manual.html index 6fee9965eb..0dc390082c 100644 --- a/doc/manual.html +++ b/doc/manual.html @@ -482,6 +482,11 @@

    Events

    following events:

    +
    "beforeCursorEnter" ()
    +
    Fired when the cursor enters the marked range. From this + event handler, the editor state may be inspected + but not modified, with the exception that the range on + which the event fires may be cleared.
    "clear" ()
    Fired when the range is cleared, either through cursor movement in combination diff --git a/lib/codemirror.js b/lib/codemirror.js index 3a40c06ffe..3b964669e1 100644 --- a/lib/codemirror.js +++ b/lib/codemirror.js @@ -2418,16 +2418,20 @@ window.CodeMirror = (function() { var dir = bias || 1; doc.cantEdit = false; search: for (;;) { - var line = getLine(doc, curPos.line), toClear; + var line = getLine(doc, curPos.line); if (line.markedSpans) { for (var i = 0; i < line.markedSpans.length; ++i) { var sp = line.markedSpans[i], m = sp.marker; if ((sp.from == null || (m.inclusiveLeft ? sp.from <= curPos.ch : sp.from < curPos.ch)) && (sp.to == null || (m.inclusiveRight ? sp.to >= curPos.ch : sp.to > curPos.ch))) { - if (mayClear && m.clearOnEnter) { - (toClear || (toClear = [])).push(m); - continue; - } else if (!m.atomic) continue; + if (mayClear) { + signal(m, "beforeCursorEnter"); + if (m.explicitlyCleared) { + if (!line.markedSpans) break; + else {--i; continue;} + } + } + if (!m.atomic) continue; var newPos = m.find()[dir < 0 ? "from" : "to"]; if (posEq(newPos, curPos)) { newPos.ch += dir; @@ -2454,7 +2458,6 @@ window.CodeMirror = (function() { continue search; } } - if (toClear) for (var i = 0; i < toClear.length; ++i) toClear[i].clear(); } return curPos; } @@ -3534,7 +3537,6 @@ window.CodeMirror = (function() { inclusiveLeft: this.inclusiveLeft, inclusiveRight: this.inclusiveRight, atomic: this.atomic, collapsed: this.collapsed, - clearOnEnter: this.clearOnEnter, replacedWith: copyWidget ? repl && repl.cloneNode(true) : repl, readOnly: this.readOnly, startStyle: this.startStyle, endStyle: this.endStyle}; @@ -3589,6 +3591,8 @@ window.CodeMirror = (function() { if (lineIsHidden(doc, line)) updateLineHeight(line, 0); }); + if (marker.clearOnEnter) on(marker, "beforeCursorEnter", function() { marker.clear(); }); + if (marker.readOnly) { sawReadOnlySpans = true; if (doc.history.done.length || doc.history.undone.length) From 60876156245226aafc9a5479e41120cb3cef8f32 Mon Sep 17 00:00:00 2001 From: Marijn Haverbeke Date: Mon, 18 Mar 2013 11:44:26 +0100 Subject: [PATCH 0879/5780] Prevent undo/redo from working in readOnly mode Closes #1369 --- lib/codemirror.js | 2 ++ 1 file changed, 2 insertions(+) diff --git a/lib/codemirror.js b/lib/codemirror.js index 3b964669e1..fb04e6150e 100644 --- a/lib/codemirror.js +++ b/lib/codemirror.js @@ -2197,6 +2197,8 @@ window.CodeMirror = (function() { } function makeChangeFromHistory(doc, type) { + if (doc.cm && doc.cm.state.suppressEdits) return; + var hist = doc.history; var event = (type == "undo" ? hist.done : hist.undone).pop(); if (!event) return; From a10cd333b96636ec1ba993f2da63261945ac8772 Mon Sep 17 00:00:00 2001 From: John Connor Date: Fri, 1 Mar 2013 12:22:59 -0500 Subject: [PATCH 0880/5780] Vim keybindings for jump to next and prev marks. The functionality should be identical to vim 7. In normal mode "[`" takes you to the next lowercase mark, "]`" takes you to the previous. "['" and "]'" work similarly but just take you to the line of the mark, not the column. --- keymap/vim.js | 48 +++++++++++++++++ test/vim_test.js | 132 +++++++++++++++++++++++++++++++++++++++++++++++ 2 files changed, 180 insertions(+) diff --git a/keymap/vim.js b/keymap/vim.js index e5c1121532..af964a666d 100644 --- a/keymap/vim.js +++ b/keymap/vim.js @@ -169,6 +169,10 @@ motionArgs: { forward: false }}, { keys: ['\'', 'character'], type: 'motion', motion: 'goToMark' }, { keys: ['`', 'character'], type: 'motion', motion: 'goToMark' }, + { keys: [']', '`',], type: 'motion', motion: 'jumpToMark', motionArgs: { forward: true } }, + { keys: ['[', '`',], type: 'motion', motion: 'jumpToMark', motionArgs: { forward: false } }, + { keys: [']', '\''], type: 'motion', motion: 'jumpToMark', motionArgs: { forward: true, linewise: true } }, + { keys: ['[', '\''], type: 'motion', motion: 'jumpToMark', motionArgs: { forward: false, linewise: true } }, { keys: ['|'], type: 'motion', motion: 'moveToColumn', motionArgs: { }}, @@ -1009,6 +1013,44 @@ } return null; }, + jumpToMark: function(cm, motionArgs, vim) { + var best = cm.getCursor(); + for (var i = 0; i < motionArgs.repeat; i++) { + var cursor = best; + for (var key in vim.marks) { + if (!isLowerCase(key)) { + continue; + } + var mark = vim.marks[key].find(); + var isWrongDirection = (motionArgs.forward) ? + cursorIsBefore(mark, cursor) : cursorIsBefore(cursor, mark) + + if (isWrongDirection) { + continue; + } + if (motionArgs.linewise && (mark.line == cursor.line)) { + continue; + } + + var equal = cursorEqual(cursor, best); + var between = (motionArgs.forward) ? + cusrorIsBetween(cursor, mark, best) : + cusrorIsBetween(best, mark, cursor); + + if (equal || between) { + best = mark; + } + } + } + + if (motionArgs.linewise) { + // Vim places the cursor on the first non-whitespace character of + // the line if there is one, else it places the cursor at the end + // of the line, regardless of whether a mark was found. + best.ch = findFirstNonWhiteSpaceCharacter(cm.getLine(best.line)); + } + return best; + }, moveByCharacters: function(cm, motionArgs) { var cur = cm.getCursor(); var repeat = motionArgs.repeat; @@ -1596,6 +1638,12 @@ } return false; } + function cusrorIsBetween(cur1, cur2, cur3) { + // returns true if cur2 is between cur1 and cur3. + var cur1before2 = cursorIsBefore(cur1, cur2); + var cur2before3 = cursorIsBefore(cur2, cur3); + return cur1before2 && cur2before3; + } function lineLength(cm, lineNum) { return cm.getLine(lineNum).length; } diff --git a/test/vim_test.js b/test/vim_test.js index 4764c639b5..c3b23a3b4a 100644 --- a/test/vim_test.js +++ b/test/vim_test.js @@ -813,6 +813,138 @@ testVim('mark', function(cm, vim, helpers) { helpers.doKeys('`', 't'); helpers.assertCursorAt(2, 2); }); +testVim('jumpToMark_next', function(cm, vim, helpers) { + cm.setCursor(2, 2); + helpers.doKeys('m', 't'); + cm.setCursor(0, 0); + helpers.doKeys(']', '`'); + helpers.assertCursorAt(2, 2); + cm.setCursor(0, 0); + helpers.doKeys(']', '\''); + helpers.assertCursorAt(2, 0); +}); +testVim('jumpToMark_next_repeat', function(cm, vim, helpers) { + cm.setCursor(2, 2); + helpers.doKeys('m', 'a'); + cm.setCursor(3, 2); + helpers.doKeys('m', 'b'); + cm.setCursor(4, 2); + helpers.doKeys('m', 'c'); + cm.setCursor(0, 0); + helpers.doKeys('2', ']', '`'); + helpers.assertCursorAt(3, 2); + cm.setCursor(0, 0); + helpers.doKeys('2', ']', '\''); + helpers.assertCursorAt(3, 1); +}); +testVim('jumpToMark_next_sameline', function(cm, vim, helpers) { + cm.setCursor(2, 0); + helpers.doKeys('m', 'a'); + cm.setCursor(2, 4); + helpers.doKeys('m', 'b'); + cm.setCursor(2, 2); + helpers.doKeys(']', '`'); + helpers.assertCursorAt(2, 4); +}); +testVim('jumpToMark_next_onlyprev', function(cm, vim, helpers) { + cm.setCursor(2, 0); + helpers.doKeys('m', 'a'); + cm.setCursor(4, 0); + helpers.doKeys(']', '`'); + helpers.assertCursorAt(4, 0); +}); +testVim('jumpToMark_next_nomark', function(cm, vim, helpers) { + cm.setCursor(2, 2); + helpers.doKeys(']', '`'); + helpers.assertCursorAt(2, 2); + helpers.doKeys(']', '\''); + helpers.assertCursorAt(2, 0); +}); +testVim('jumpToMark_next_linewise_over', function(cm, vim, helpers) { + cm.setCursor(2, 2); + helpers.doKeys('m', 'a'); + cm.setCursor(3, 4); + helpers.doKeys('m', 'b'); + cm.setCursor(2, 1); + helpers.doKeys(']', '\''); + helpers.assertCursorAt(3, 1); +}); +testVim('jumpToMark_next_action', function(cm, vim, helpers) { + cm.setCursor(2, 2); + helpers.doKeys('m', 't'); + cm.setCursor(0, 0); + helpers.doKeys('d', ']', '`'); + helpers.assertCursorAt(0, 0); + var actual = cm.getLine(0); + var expected = 'pop pop 0 1 2 3 4'; + eq(actual, expected, "Deleting while jumping to the next mark failed."); +}); +testVim('jumpToMark_next_line_action', function(cm, vim, helpers) { + cm.setCursor(2, 2); + helpers.doKeys('m', 't'); + cm.setCursor(0, 0); + helpers.doKeys('d', ']', '\''); + helpers.assertCursorAt(0, 1); + var actual = cm.getLine(0); + var expected = ' (a) [b] {c} ' + eq(actual, expected, "Deleting while jumping to the next mark line failed."); +}); +testVim('jumpToMark_prev', function(cm, vim, helpers) { + cm.setCursor(2, 2); + helpers.doKeys('m', 't'); + cm.setCursor(4, 0); + helpers.doKeys('[', '`'); + helpers.assertCursorAt(2, 2); + cm.setCursor(4, 0); + helpers.doKeys('[', '\''); + helpers.assertCursorAt(2, 0); +}); +testVim('jumpToMark_prev_repeat', function(cm, vim, helpers) { + cm.setCursor(2, 2); + helpers.doKeys('m', 'a'); + cm.setCursor(3, 2); + helpers.doKeys('m', 'b'); + cm.setCursor(4, 2); + helpers.doKeys('m', 'c'); + cm.setCursor(5, 0); + helpers.doKeys('2', '[', '`'); + helpers.assertCursorAt(3, 2); + cm.setCursor(5, 0); + helpers.doKeys('2', '[', '\''); + helpers.assertCursorAt(3, 1); +}); +testVim('jumpToMark_prev_sameline', function(cm, vim, helpers) { + cm.setCursor(2, 0); + helpers.doKeys('m', 'a'); + cm.setCursor(2, 4); + helpers.doKeys('m', 'b'); + cm.setCursor(2, 2); + helpers.doKeys('[', '`'); + helpers.assertCursorAt(2, 0); +}); +testVim('jumpToMark_prev_onlynext', function(cm, vim, helpers) { + cm.setCursor(4, 4); + helpers.doKeys('m', 'a'); + cm.setCursor(2, 0); + helpers.doKeys('[', '`'); + helpers.assertCursorAt(2, 0); +}); +testVim('jumpToMark_prev_nomark', function(cm, vim, helpers) { + cm.setCursor(2, 2); + helpers.doKeys('[', '`'); + helpers.assertCursorAt(2, 2); + helpers.doKeys('[', '\''); + helpers.assertCursorAt(2, 0); +}); +testVim('jumpToMark_prev_linewise_over', function(cm, vim, helpers) { + cm.setCursor(2, 2); + helpers.doKeys('m', 'a'); + cm.setCursor(3, 4); + helpers.doKeys('m', 'b'); + cm.setCursor(3, 6); + helpers.doKeys('[', '\''); + helpers.assertCursorAt(2, 0); +}); testVim('delmark_single', function(cm, vim, helpers) { cm.setCursor(1, 2); helpers.doKeys('m', 't'); From 965dc47c24c6b95eb191e77be85fee88e75cd67c Mon Sep 17 00:00:00 2001 From: Drew Hintz Date: Sat, 16 Mar 2013 00:40:49 -0700 Subject: [PATCH 0881/5780] remove old Opera-specific CSS properties where Opera supports the standard properties. --- lib/codemirror.css | 2 +- theme/ambiance-mobile.css | 1 - theme/ambiance.css | 1 - 3 files changed, 1 insertion(+), 3 deletions(-) diff --git a/lib/codemirror.css b/lib/codemirror.css index 913a1a9c3d..bab16bc2c9 100644 --- a/lib/codemirror.css +++ b/lib/codemirror.css @@ -166,7 +166,7 @@ div.CodeMirror span.CodeMirror-nonmatchingbracket {color: #f22;} } .CodeMirror pre { /* Reset some styles that the rest of the page might have set */ - -moz-border-radius: 0; -webkit-border-radius: 0; -o-border-radius: 0; border-radius: 0; + -moz-border-radius: 0; -webkit-border-radius: 0; border-radius: 0; border-width: 0; background: transparent; font-family: inherit; diff --git a/theme/ambiance-mobile.css b/theme/ambiance-mobile.css index 35b3750c1b..88d332e1a7 100644 --- a/theme/ambiance-mobile.css +++ b/theme/ambiance-mobile.css @@ -1,6 +1,5 @@ .cm-s-ambiance.CodeMirror { -webkit-box-shadow: none; -moz-box-shadow: none; - -o-box-shadow: none; box-shadow: none; } diff --git a/theme/ambiance.css b/theme/ambiance.css index beec553851..0185426f01 100644 --- a/theme/ambiance.css +++ b/theme/ambiance.css @@ -46,7 +46,6 @@ background-color: #202020; -webkit-box-shadow: inset 0 0 10px black; -moz-box-shadow: inset 0 0 10px black; - -o-box-shadow: inset 0 0 10px black; box-shadow: inset 0 0 10px black; } From 5f591a013e11c9a00bd439e6494b1c17d7d2813b Mon Sep 17 00:00:00 2001 From: Nikita Beloglazov Date: Sat, 16 Mar 2013 13:15:40 +0300 Subject: [PATCH 0882/5780] Add character higlighting to clojure mode. --- mode/clojure/clojure.js | 19 ++++++++++++++++++- mode/clojure/index.html | 23 ++++++++++++++++------- 2 files changed, 34 insertions(+), 8 deletions(-) diff --git a/mode/clojure/clojure.js b/mode/clojure/clojure.js index 8b9614cc20..658ff140e0 100644 --- a/mode/clojure/clojure.js +++ b/mode/clojure/clojure.js @@ -3,7 +3,7 @@ * Branched from CodeMirror's Scheme mode (by Koh Zi Han, based on implementation by Koh Zi Chun) */ CodeMirror.defineMode("clojure", function () { - var BUILTIN = "builtin", COMMENT = "comment", STRING = "string", + var BUILTIN = "builtin", COMMENT = "comment", STRING = "string", CHARACTER = "string-2", ATOM = "atom", NUMBER = "number", BRACKET = "bracket", KEYWORD = "keyword"; var INDENT_WORD_SKIP = 2; @@ -95,6 +95,20 @@ CodeMirror.defineMode("clojure", function () { return false; } + // Eat character that starts after backslash \ + function eatCharacter(stream) { + var first = stream.next(); + // Read special literals: backspace, newline, space, return. + // Just read all lowercase letters. + if (first.match(/[a-z]/) && stream.match(/[a-z]+/, true)) { + return; + } + // Read unicode character: \u1000 \uA0a1 + if (first === "u") { + stream.match(/[0-9a-z]{4}/i, true); + } + } + return { startState: function () { return { @@ -135,6 +149,9 @@ CodeMirror.defineMode("clojure", function () { if (ch == "\"") { state.mode = "string"; returnType = STRING; + } else if (ch == "\\") { + eatCharacter(stream); + returnType = CHARACTER; } else if (ch == "'" && !( tests.digit_or_colon.test(stream.peek()) )) { returnType = ATOM; } else if (ch == ";") { // comment diff --git a/mode/clojure/index.html b/mode/clojure/index.html index bce0473530..bfe6fc955a 100644 --- a/mode/clojure/index.html +++ b/mode/clojure/index.html @@ -21,13 +21,13 @@

    CodeMirror: Clojure mode

    ;; Core game of life's algorithm functions -(defn neighbours +(defn neighbours "Given a cell's coordinates, returns the coordinates of its neighbours." [[x y]] (for [dx [-1 0 1] dy (if (zero? dx) [-1 1] [-1 0 1])] [(+ dx x) (+ dy y)])) -(defn step +(defn step "Given a set of living cells, computes the new set of living cells." [cells] (set (for [[cell n] (frequencies (mapcat neighbours cells)) @@ -36,14 +36,14 @@

    CodeMirror: Clojure mode

    ;; Utility methods for displaying game on a text terminal -(defn print-board +(defn print-board "Prints a board on *out*, representing a step in the game." [board w h] (doseq [x (range (inc w)) y (range (inc h))] - (if (= y 0) (print "\n")) + (if (= y 0) (print "\n")) (print (if (board [x y]) "[X]" " . ")))) -(defn display-grids +(defn display-grids "Prints a squence of boards on *out*, representing several steps." [grids w h] (doseq [board grids] @@ -52,11 +52,20 @@

    CodeMirror: Clojure mode

    ;; Launches an example board -(def +(def ^{:doc "board represents the initial set of living cells"} board #{[2 1] [2 2] [2 3]}) -(display-grids (take 3 (iterate step board)) 5 5) +(display-grids (take 3 (iterate step board)) 5 5) + +;; Let's play with characters +(println \1 \a \# \\ + \" \( \newline + \} \" \space + \tab \return \backspace + \u1000 \uAaAa \u9F9F) + + From 8cf1f1a50ac99e59e45f68c9b683b34b3de70d9a Mon Sep 17 00:00:00 2001 From: Marijn Haverbeke Date: Mon, 18 Mar 2013 13:54:18 +0100 Subject: [PATCH 0883/5780] [xml mode] Support config parameter for indentation in wrapped tags Closes #1375 --- mode/xml/xml.js | 4 +++- 1 file changed, 3 insertions(+), 1 deletion(-) diff --git a/mode/xml/xml.js b/mode/xml/xml.js index f08f9f5679..a68b033841 100644 --- a/mode/xml/xml.js +++ b/mode/xml/xml.js @@ -1,5 +1,7 @@ CodeMirror.defineMode("xml", function(config, parserConfig) { var indentUnit = config.indentUnit; + var multilineTagIndentFactor = parserConfig.multilineTagIndentFactor || 1; + var Kludges = parserConfig.htmlMode ? { autoSelfClosers: {'area': true, 'base': true, 'br': true, 'col': true, 'command': true, 'embed': true, 'frame': true, 'hr': true, 'img': true, 'input': true, @@ -304,7 +306,7 @@ CodeMirror.defineMode("xml", function(config, parserConfig) { if ((state.tokenize != inTag && state.tokenize != inText) || context && context.noIndent) return fullLine ? fullLine.match(/^(\s*)/)[0].length : 0; - if (state.tagName) return state.tagStart + indentUnit; + if (state.tagName) return state.tagStart + indentUnit * multilineTagIndentFactor; if (alignCDATA && / Date: Mon, 18 Mar 2013 14:28:01 +0100 Subject: [PATCH 0884/5780] Use a simpler, faster approach in removeChildren Closes #1376 --- lib/codemirror.js | 5 ++--- 1 file changed, 2 insertions(+), 3 deletions(-) diff --git a/lib/codemirror.js b/lib/codemirror.js index fb04e6150e..a8f7778451 100644 --- a/lib/codemirror.js +++ b/lib/codemirror.js @@ -5136,9 +5136,8 @@ window.CodeMirror = (function() { } function removeChildren(e) { - // IE will break all parent-child relations in subnodes when setting innerHTML - if (!ie) e.innerHTML = ""; - else while (e.firstChild) e.removeChild(e.firstChild); + for (var count = e.childNodes.length; count > 0; --count) + e.removeChild(e.firstChild); return e; } From d8a583e913f9ccfb516e9b891b48b4d05d635c4a Mon Sep 17 00:00:00 2001 From: Marijn Haverbeke Date: Mon, 18 Mar 2013 16:18:49 +0100 Subject: [PATCH 0885/5780] Don't leave text with newlines in the textarea There's no multi-line-unit IME that I'm aware of. Issue #1231 --- lib/codemirror.js | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/lib/codemirror.js b/lib/codemirror.js index a8f7778451..5b844010b2 100644 --- a/lib/codemirror.js +++ b/lib/codemirror.js @@ -1410,7 +1410,7 @@ window.CodeMirror = (function() { origin: cm.state.pasteIncoming ? "paste" : "+input"}, "end"); cm.curOp.updateInput = updateInput; - if (text.length > 1000) input.value = cm.display.prevInput = ""; + if (text.length > 1000 || text.indexOf("\n") > -1) input.value = cm.display.prevInput = ""; else cm.display.prevInput = text; if (withOp) endOperation(cm); cm.state.pasteIncoming = false; From 20ea151001909161cdd940571450b8c1426bc001 Mon Sep 17 00:00:00 2001 From: Marijn Haverbeke Date: Tue, 19 Mar 2013 09:59:06 +0100 Subject: [PATCH 0886/5780] Give extraKeys a lower prec than addKeyMap keys, add bottom arg to addKeyMap --- doc/manual.html | 20 +++++++++++--------- lib/codemirror.js | 6 +++--- test/test.js | 2 +- 3 files changed, 15 insertions(+), 13 deletions(-) diff --git a/doc/manual.html b/doc/manual.html index 0dc390082c..db192e08d1 100644 --- a/doc/manual.html +++ b/doc/manual.html @@ -834,16 +834,18 @@

    Configuration methods

    Retrieves the current value of the given option for this editor instance.
    -
    cm.addKeyMap(map)
    -
    Attach an additional keymap to the editor. This is mostly - useful for add-ons that need to register some key handlers - without trampling on +
    cm.addKeyMap(map, bottom)
    +
    Attach an additional keymap to the + editor. This is mostly useful for add-ons that need to register + some key handlers without trampling on the extraKeys - option. Maps added in this way have a lower precedence - than extraKeys, a higher precedence than the - base keyMap, and - between them, the maps added earlier have a higher precedence - than those added later.
    + option. Maps added in this way have a higher precedence than + the extraKeys + and keyMap options, + and between them, the maps added earlier have a lower precedence + than those added later, unless the bottom argument + was passed, in which case they end up below other keymaps added + with this method.
    cm.removeKeyMap(map)
    Disable a keymap added with addKeyMap. Either diff --git a/lib/codemirror.js b/lib/codemirror.js index 5b844010b2..6f475f8b54 100644 --- a/lib/codemirror.js +++ b/lib/codemirror.js @@ -1925,8 +1925,8 @@ window.CodeMirror = (function() { function allKeyMaps(cm) { var maps = cm.state.keyMaps.slice(0); + if (cm.options.extraKeys) maps.push(cm.options.extraKeys); maps.push(cm.options.keyMap); - if (cm.options.extraKeys) maps.unshift(cm.options.extraKeys); return maps; } @@ -2691,8 +2691,8 @@ window.CodeMirror = (function() { getOption: function(option) {return this.options[option];}, getDoc: function() {return this.doc;}, - addKeyMap: function(map) { - this.state.keyMaps.push(map); + addKeyMap: function(map, bottom) { + this.state.keyMaps[bottom ? "push" : "unshift"](map); }, removeKeyMap: function(map) { var maps = this.state.keyMaps; diff --git a/test/test.js b/test/test.js index d3e78f80bb..96f37353aa 100644 --- a/test/test.js +++ b/test/test.js @@ -1298,7 +1298,7 @@ testCM("addKeyMap", function(cm) { sendKey(39); eqPos(cm.getCursor(), Pos(0, 1)); eq(test, 1); - cm.addKeyMap(map2); + cm.addKeyMap(map2, true); sendKey(39); eq(test, 2); cm.removeKeyMap(map1); From 14b8063dd83bb745e1b506c26fce80ca597a9913 Mon Sep 17 00:00:00 2001 From: Marijn Haverbeke Date: Tue, 19 Mar 2013 10:09:31 +0100 Subject: [PATCH 0887/5780] Revise interface to continueComment It's better to have it only take effect when in a comment, so that Enter's regular behavior isn't overridden in other situations. --- addon/edit/continuecomment.js | 14 +++++++++++--- doc/manual.html | 9 +++++---- mode/javascript/index.html | 2 +- 3 files changed, 17 insertions(+), 8 deletions(-) diff --git a/addon/edit/continuecomment.js b/addon/edit/continuecomment.js index 4ee0b48015..308026229f 100644 --- a/addon/edit/continuecomment.js +++ b/addon/edit/continuecomment.js @@ -5,7 +5,7 @@ blockCommentEnd: "*/", blockCommentContinue: " * "}); - CodeMirror.commands.newlineAndIndentContinueComment = function(cm) { + function continueComment(cm) { var pos = cm.getCursor(), token = cm.getTokenAt(pos); var mode = CodeMirror.innerMode(cm.getMode(), token.state).mode; var space; @@ -31,6 +31,14 @@ if (space != null) cm.replaceSelection("\n" + space + mode.blockCommentContinue, "end"); else - cm.execCommand("newlineAndIndent"); - }; + return CodeMirror.Pass; + } + + CodeMirror.defineOption("continueComments", null, function(cm, val, prev) { + if (prev && prev != CodeMirror.Init) + cm.removeKeyMap("continueComment"); + var map = {name: "continueComment"}; + map[typeof val == "string" ? val : "Enter"] = continueComment; + cm.addKeyMap(map); + }); })(); diff --git a/doc/manual.html b/doc/manual.html index db192e08d1..d8cd2db783 100644 --- a/doc/manual.html +++ b/doc/manual.html @@ -1560,10 +1560,11 @@

    Add-ons

    editor instance to refresh its mode when the loading succeeded. See the demo.
    edit/continuecomment.js
    -
    Adds a command - called newlineAndIndentContinueComment that you can - bind Enter to in order to have the editor prefix - new lines inside C-like block comments with an asterisk.
    +
    Adds an continueComments option, which can be + set to true to have the editor prefix new lines inside C-like + block comments with an asterisk when Enter is pressed. It can + also be set to a string in order to bind this functionality to a + specific key..
    display/placeholder.js
    Adds a placeholder option that can be used to make text appear in the editor when it is empty and not focused. diff --git a/mode/javascript/index.html b/mode/javascript/index.html index 9222ddf170..cca7d04eaf 100644 --- a/mode/javascript/index.html +++ b/mode/javascript/index.html @@ -68,7 +68,7 @@

    CodeMirror: JavaScript mode

    var editor = CodeMirror.fromTextArea(document.getElementById("code"), { lineNumbers: true, matchBrackets: true, - extraKeys: {"Enter": "newlineAndIndentContinueComment"} + continueComments: "Enter" }); From 962cabe4a3a30da1536c8a4a06cdd9ac22e5acfb Mon Sep 17 00:00:00 2001 From: Drew Hintz Date: Sat, 16 Mar 2013 01:07:30 -0700 Subject: [PATCH 0888/5780] use 3 character color codes instead of 6 where appropriate --- mode/tiki/tiki.css | 4 ++-- theme/erlang-dark.css | 10 +++++----- 2 files changed, 7 insertions(+), 7 deletions(-) diff --git a/mode/tiki/tiki.css b/mode/tiki/tiki.css index e3c3c0fd2d..0dbc3ea0e0 100644 --- a/mode/tiki/tiki.css +++ b/mode/tiki/tiki.css @@ -1,6 +1,6 @@ .cm-tw-syntaxerror { - color: #FFFFFF; - background-color: #990000; + color: #FFF; + background-color: #900; } .cm-tw-deleted { diff --git a/theme/erlang-dark.css b/theme/erlang-dark.css index ea9c26c44d..cf5bf2bd6e 100644 --- a/theme/erlang-dark.css +++ b/theme/erlang-dark.css @@ -7,15 +7,15 @@ .cm-s-erlang-dark span.cm-atom { color: #845dc4; } .cm-s-erlang-dark span.cm-attribute { color: #ff80e1; } .cm-s-erlang-dark span.cm-bracket { color: #ff9d00; } -.cm-s-erlang-dark span.cm-builtin { color: #eeaaaa; } -.cm-s-erlang-dark span.cm-comment { color: #7777ff; } -.cm-s-erlang-dark span.cm-def { color: #ee77aa; } +.cm-s-erlang-dark span.cm-builtin { color: #eaa; } +.cm-s-erlang-dark span.cm-comment { color: #77f; } +.cm-s-erlang-dark span.cm-def { color: #e7a; } .cm-s-erlang-dark span.cm-error { color: #9d1e15; } .cm-s-erlang-dark span.cm-keyword { color: #ffee80; } .cm-s-erlang-dark span.cm-meta { color: #50fefe; } .cm-s-erlang-dark span.cm-number { color: #ffd0d0; } -.cm-s-erlang-dark span.cm-operator { color: #dd1111; } +.cm-s-erlang-dark span.cm-operator { color: #d11; } .cm-s-erlang-dark span.cm-string { color: #3ad900; } .cm-s-erlang-dark span.cm-tag { color: #9effff; } .cm-s-erlang-dark span.cm-variable { color: #50fe50; } -.cm-s-erlang-dark span.cm-variable-2 { color: #ee00ee; } +.cm-s-erlang-dark span.cm-variable-2 { color: #e0e; } From 2faf0349770b2ad02321814244595a5a89b81b6f Mon Sep 17 00:00:00 2001 From: Juan Benavides Romero Date: Thu, 7 Mar 2013 10:02:13 +0100 Subject: [PATCH 0889/5780] standard html5 code completation --- addon/hint/html-hint.js | 586 ++++++++++++++++++++++++++++++++++++++++ 1 file changed, 586 insertions(+) create mode 100755 addon/hint/html-hint.js diff --git a/addon/hint/html-hint.js b/addon/hint/html-hint.js new file mode 100755 index 0000000000..0e43e79c93 --- /dev/null +++ b/addon/hint/html-hint.js @@ -0,0 +1,586 @@ +(function () { + function scriptHint(editor, htmlStructure, getToken) { + var cur = editor.getCursor(); + var token = getToken(editor, cur); + var keywords = []; + var i = 0; + var j = 0; + var k = 0; + var from = {line: cur.line, ch: cur.ch}; + var to = {line: cur.line, ch: cur.ch}; + var flagClean = true; + + text = editor.getRange({line: 0, ch: 0}, cur); + + var open = text.lastIndexOf('<'); + var close = text.lastIndexOf('>'); + var tokenString = token.string.replace("<",""); + + if(open > close) { + var last = editor.getRange({line: cur.line, ch: cur.ch - 1}, cur); + if(last == "<") { + for(i = 0; i < htmlStructure.length; i++) { + keywords.push(htmlStructure[i].tag); + } + from.ch = token.start + 1; + } else { + var counter = 0; + var found = function(token, type, position) { + counter++; + if(counter > 50) return; + if(token.type == type) { + return token; + } else { + position.ch = token.start; + var newToken = editor.getTokenAt(position); + return found(newToken, type, position); + } + }; + + var nodeToken = found(token, "tag", {line: cur.line, ch: cur.ch}); + var node = nodeToken.string.substring(1); + + if(token.type === null && token.string.trim() === "") { + for(i = 0; i < htmlStructure.length; i++) { + if(htmlStructure[i].tag == node) { + for(j = 0; j < htmlStructure[i].attr.length; j++) { + keywords.push(htmlStructure[i].attr[j].key + "=\"\" "); + } + + for(k = 0; k < globalAttributes.length; k++) { + keywords.push(globalAttributes[k].key + "=\"\" "); + } + } + } + } else if(token.type == "string") { + tokenString = tokenString.substring(1, tokenString.length - 1); + var attributeToken = found(token, "attribute", {line: cur.line, ch: cur.ch}); + var attribute = attributeToken.string; + + for(i = 0; i < htmlStructure.length; i++) { + if(htmlStructure[i].tag == node) { + for(j = 0; j < htmlStructure[i].attr.length; j++) { + if(htmlStructure[i].attr[j].key == attribute) { + for(k = 0; k < htmlStructure[i].attr[j].values.length; k++) { + keywords.push(htmlStructure[i].attr[j].values[k]); + } + } + } + + for(j = 0; j < globalAttributes.length; j++) { + if(globalAttributes[j].key == attribute) { + for(k = 0; k < globalAttributes[j].values.length; k++) { + keywords.push(globalAttributes[j].values[k]); + } + } + } + } + } + from.ch = token.start + 1; + } else if(token.type == "attribute") { + for(i = 0; i < htmlStructure.length; i++) { + if(htmlStructure[i].tag == node) { + for(j = 0; j < htmlStructure[i].attr.length; j++) { + keywords.push(htmlStructure[i].attr[j].key + "=\"\" "); + } + + for(k = 0; k < globalAttributes.length; k++) { + keywords.push(globalAttributes[k].key + "=\"\" "); + } + } + } + from.ch = token.start; + } else if(token.type == "tag") { + for(i = 0; i < htmlStructure.length; i++) { + keywords.push(htmlStructure[i].tag); + } + + from.ch = token.start + 1; + } + } + } else { + for(i = 0; i < htmlStructure.length; i++) { + keywords.push("<" + htmlStructure[i].tag); + } + + tokenString = ("<" + tokenString).trim(); + from.ch = token.start; + } + + if(flagClean === true && tokenString.trim() === "") { + flagClean = false; + } + + if(flagClean) { + keywords = cleanResults(tokenString, keywords); + } + + return {list: keywords, from: from, to: to}; + } + + + var cleanResults = function(text, keywords) { + var results = []; + var i = 0; + + for(i = 0; i < keywords.length; i++) { + if(keywords[i].substring(0, text.length) == text) { + results.push(keywords[i]); + } + } + + return results; + }; + + + var htmlStructure = [ + {tag: '!DOCTYPE', attr: []}, + {tag: 'a', attr: [ + {key: 'href', values: ["#"]}, + {key: 'target', values: ["_blank","_self","_top","_parent"]}, + {key: 'ping', values: [""]}, + {key: 'media', values: ["#"]}, + {key: 'hreflang', values: ["en","es"]}, + {key: 'type', values: []} + ]}, + {tag: 'abbr', attr: []}, + {tag: 'acronym', attr: []}, + {tag: 'address', attr: []}, + {tag: 'applet', attr: []}, + {tag: 'area', attr: [ + {key: 'alt', values: [""]}, + {key: 'coords', values: ["rect: left, top, right, bottom","circle: center-x, center-y, radius","poly: x1, y1, x2, y2, ..."]}, + {key: 'shape', values: ["default","rect","circle","poly"]}, + {key: 'href', values: ["#"]}, + {key: 'target', values: ["#"]}, + {key: 'ping', values: []}, + {key: 'media', values: []}, + {key: 'hreflang', values: []}, + {key: 'type', values: []} + + ]}, + {tag: 'article', attr: []}, + {tag: 'aside', attr: []}, + {tag: 'audio', attr: [ + {key: 'src', values: []}, + {key: 'crossorigin', values: ["anonymous","use-credentials"]}, + {key: 'preload', values: ["none","metadata","auto"]}, + {key: 'autoplay', values: ["","autoplay"]}, + {key: 'mediagroup', values: []}, + {key: 'loop', values: ["","loop"]}, + {key: 'controls', values: ["","controls"]} + ]}, + {tag: 'b', attr: []}, + {tag: 'base', attr: [ + {key: 'href', values: ["#"]}, + {key: 'target', values: ["_blank","_self","_top","_parent"]} + ]}, + {tag: 'basefont', attr: []}, + {tag: 'bdi', attr: []}, + {tag: 'bdo', attr: []}, + {tag: 'big', attr: []}, + {tag: 'blockquote', attr: [ + {key: 'cite', values: ["http://"]} + ]}, + {tag: 'body', attr: []}, + {tag: 'br', attr: []}, + {tag: 'button', attr: [ + {key: 'autofocus', values: ["","autofocus"]}, + {key: 'disabled', values: ["","disabled"]}, + {key: 'form', values: []}, + {key: 'formaction', values: []}, + {key: 'formenctype', values: ["application/x-www-form-urlencoded","multipart/form-data","text/plain"]}, + {key: 'formmethod', values: ["get","post","put","delete"]}, + {key: 'formnovalidate', values: ["","novalidate"]}, + {key: 'formtarget', values: ["_blank","_self","_top","_parent"]}, + {key: 'name', values: []}, + {key: 'type', values: ["submit","reset","button"]}, + {key: 'value', values: []} + ]}, + {tag: 'canvas', attr: [ + {key: 'width', values: []}, + {key: 'height', values: []} + ]}, + {tag: 'caption', attr: []}, + {tag: 'center', attr: []}, + {tag: 'cite', attr: []}, + {tag: 'code', attr: []}, + {tag: 'col', attr: [ + {key: 'span', values: []} + ]}, + {tag: 'colgroup', attr: [ + {key: 'span', values: []} + ]}, + {tag: 'command', attr: [ + {key: 'type', values: ["command","checkbox","radio"]}, + {key: 'label', values: []}, + {key: 'icon', values: []}, + {key: 'disabled', values: ["","disabled"]}, + {key: 'checked', values: ["","checked"]}, + {key: 'radiogroup', values: []}, + {key: 'command', values: []}, + {key: 'title', values: []} + ]}, + {tag: 'data', attr: [ + {key: 'value', values: []} + ]}, + {tag: 'datagrid', attr: [ + {key: 'disabled', values: ["","disabled"]}, + {key: 'multiple', values: ["","multiple"]} + ]}, + {tag: 'datalist', attr: [ + {key: 'data', values: []} + ]}, + {tag: 'dd', attr: []}, + {tag: 'del', attr: [ + {key: 'cite', values: []}, + {key: 'datetime', values: []} + ]}, + {tag: 'details', attr: [ + {key: 'open', values: ["","open"]} + ]}, + {tag: 'dfn', attr: []}, + {tag: 'dir', attr: []}, + {tag: 'div', attr: [ + {key: 'id', values: []}, + {key: 'class', values: []}, + {key: 'style', values: []} + ]}, + {tag: 'dl', attr: []}, + {tag: 'dt', attr: []}, + {tag: 'em', attr: []}, + {tag: 'embed', attr: [ + {key: 'src', values: []}, + {key: 'type', values: []}, + {key: 'width', values: []}, + {key: 'height', values: []} + ]}, + {tag: 'eventsource', attr: [ + {key: 'src', values: []} + ]}, + {tag: 'fieldset', attr: [ + {key: 'disabled', values: ["","disabled"]}, + {key: 'form', values: []}, + {key: 'name', values: []} + ]}, + {tag: 'figcaption', attr: []}, + {tag: 'figure', attr: []}, + {tag: 'font', attr: []}, + {tag: 'footer', attr: []}, + {tag: 'form', attr: [ + {key: 'accept-charset', values: ["UNKNOWN","utf-8"]}, + {key: 'action', values: []}, + {key: 'autocomplete', values: ["on","off"]}, + {key: 'enctype', values: ["application/x-www-form-urlencoded","multipart/form-data","text/plain"]}, + {key: 'method', values: ["get","post","put","delete","dialog"]}, + {key: 'name', values: []}, + {key: 'novalidate', values: ["","novalidate"]}, + {key: 'target', values: ["_blank","_self","_top","_parent"]} + ]}, + {tag: 'frame', attr: []}, + {tag: 'frameset', attr: []}, + {tag: 'h1', attr: []}, + {tag: 'h2', attr: []}, + {tag: 'h3', attr: []}, + {tag: 'h4', attr: []}, + {tag: 'h5', attr: []}, + {tag: 'h6', attr: []}, + {tag: 'head', attr: []}, + {tag: 'header', attr: []}, + {tag: 'hgroup', attr: []}, + {tag: 'hr', attr: []}, + {tag: 'html', attr: [ + {key: 'manifest', values: []} + ]}, + {tag: 'i', attr: []}, + {tag: 'iframe', attr: [ + {key: 'src', values: []}, + {key: 'srcdoc', values: []}, + {key: 'name', values: []}, + {key: 'sandbox', values: ["allow-top-navigation","allow-same-origin","allow-forms","allow-scripts"]}, + {key: 'seamless', values: ["","seamless"]}, + {key: 'width', values: []}, + {key: 'height', values: []} + ]}, + {tag: 'img', attr: [ + {key: 'alt', values: []}, + {key: 'src', values: []}, + {key: 'crossorigin', values: ["anonymous","use-credentials"]}, + {key: 'ismap', values: []}, + {key: 'usemap', values: []}, + {key: 'width', values: []}, + {key: 'height', values: []} + ]}, + {tag: 'input', attr: [ + {key: 'accept', values: ["audio/*","video/*","image/*"]}, + {key: 'alt', values: []}, + {key: 'autocomplete', values: ["on","off"]}, + {key: 'autofocus', values: ["","autofocus"]}, + {key: 'checked', values: ["","checked"]}, + {key: 'disabled', values: ["","disabled"]}, + {key: 'dirname', values: []}, + {key: 'form', values: []}, + {key: 'formaction', values: []}, + {key: 'formenctype', values: ["application/x-www-form-urlencoded","multipart/form-data","text/plain"]}, + {key: 'formmethod', values: ["get","post","put","delete"]}, + {key: 'formnovalidate', values: ["","novalidate"]}, + {key: 'formtarget', values: ["_blank","_self","_top","_parent"]}, + {key: 'height', values: []}, + {key: 'list', values: []}, + {key: 'max', values: []}, + {key: 'maxlength', values: []}, + {key: 'min', values: []}, + {key: 'multiple', values: ["","multiple"]}, + {key: 'name', values: []}, + {key: 'pattern', values: []}, + {key: 'placeholder', values: []}, + {key: 'readonly', values: ["","readonly"]}, + {key: 'required', values: ["","required"]}, + {key: 'size', values: []}, + {key: 'src', values: []}, + {key: 'step', values: []}, + {key: 'type', values: [ + "hidden","text","search","tel","url","email","password","datetime","date","month","week","time","datetime-local", + "number","range","color","checkbox","radio","file","submit","image","reset","button" + ]}, + {key: 'value', values: []}, + {key: 'width', values: []} + ]}, + {tag: 'ins', attr: [ + {key: 'cite', values: []}, + {key: 'datetime', values: []} + ]}, + {tag: 'kbd', attr: []}, + {tag: 'keygen', attr: [ + {key: 'autofocus', values: ["","autofocus"]}, + {key: 'challenge', values: []}, + {key: 'disabled', values: ["","disabled"]}, + {key: 'form', values: []}, + {key: 'keytype', values: ["RSA"]}, + {key: 'name', values: []} + ]}, + {tag: 'label', attr: [ + {key: 'for', values: []}, + {key: 'form', values: []} + ]}, + {tag: 'legend', attr: []}, + {tag: 'li', attr: [ + {key: 'value', values: []} + ]}, + {tag: 'link', attr: [ + {key: 'href', values: []}, + {key: 'hreflang', values: ["en","es"]}, + {key: 'media', values: [ + "all","screen","print","embossed","braille","handheld","print","projection","screen","tty","tv","speech","3d-glasses", + "resolution [>][<][=] [X]dpi","resolution [>][<][=] [X]dpcm","device-aspect-ratio: 16/9","device-aspect-ratio: 4/3", + "device-aspect-ratio: 32/18","device-aspect-ratio: 1280/720","device-aspect-ratio: 2560/1440","orientation:portrait", + "orientation:landscape","device-height: [X]px","device-width: [X]px","-webkit-min-device-pixel-ratio: 2" + ]}, + {key: 'type', values: []}, + {key: 'sizes', values: ["all","16x16","16x16 32x32","16x16 32x32 64x64"]} + ]}, + {tag: 'map', attr: [ + {key: 'name', values: []} + ]}, + {tag: 'mark', attr: []}, + {tag: 'menu', attr: [ + {key: 'type', values: ["list","context","toolbar"]}, + {key: 'label', values: []} + ]}, + {tag: 'meta', attr: [ + {key: 'charset', attr: ["utf-8"]}, + {key: 'name', attr: ["viewport","application-name","author","description","generator","keywords"]}, + {key: 'content', attr: ["","width=device-width","initial-scale=1, maximum-scale=1, minimun-scale=1, user-scale=no"]}, + {key: 'http-equiv', attr: ["content-language","content-type","default-style","refresh"]} + ]}, + {tag: 'meter', attr: [ + {key: 'value', values: []}, + {key: 'min', values: []}, + {key: 'low', values: []}, + {key: 'high', values: []}, + {key: 'max', values: []}, + {key: 'optimum', values: []} + ]}, + {tag: 'nav', attr: []}, + {tag: 'noframes', attr: []}, + {tag: 'noscript', attr: []}, + {tag: 'object', attr: [ + {key: 'data', values: []}, + {key: 'type', values: []}, + {key: 'typemustmatch', values: ["","typemustmatch"]}, + {key: 'name', values: []}, + {key: 'usemap', values: []}, + {key: 'form', values: []}, + {key: 'width', values: []}, + {key: 'height', values: []} + ]}, + {tag: 'ol', attr: [ + {key: 'reversed', values: ["", "reversed"]}, + {key: 'start', values: []}, + {key: 'type', values: ["1","a","A","i","I"]} + ]}, + {tag: 'optgroup', attr: [ + {key: 'disabled', values: ["","disabled"]}, + {key: 'label', values: []} + ]}, + {tag: 'option', attr: [ + {key: 'disabled', values: ["", "disabled"]}, + {key: 'label', values: []}, + {key: 'selected', values: ["", "selected"]}, + {key: 'value', values: []} + ]}, + {tag: 'output', attr: [ + {key: 'for', values: []}, + {key: 'form', values: []}, + {key: 'name', values: []} + ]}, + {tag: 'p', attr: []}, + {tag: 'param', attr: [ + {key: 'name', values: []}, + {key: 'value', values: []} + ]}, + {tag: 'pre', attr: []}, + {tag: 'progress', attr: [ + {key: 'value', values: []}, + {key: 'max', values: []} + ]}, + {tag: 'q', attr: [ + {key: 'cite', values: []} + ]}, + {tag: 'rp', attr: []}, + {tag: 'rt', attr: []}, + {tag: 'ruby', attr: []}, + {tag: 's', attr: []}, + {tag: 'samp', attr: []}, + {tag: 'script', attr: [ + {key: 'type', values: ["text/javascript"]}, + {key: 'src', values: []}, + {key: 'async', values: ["","async"]}, + {key: 'defer', values: ["","defer"]}, + {key: 'charset', values: ["utf-8"]} + ]}, + {tag: 'section', attr: []}, + {tag: 'select', attr: [ + {key: 'autofocus', values: ["", "autofocus"]}, + {key: 'disabled', values: ["", "disabled"]}, + {key: 'form', values: []}, + {key: 'multiple', values: ["", "multiple"]}, + {key: 'name', values: []}, + {key: 'size', values: []} + ]}, + {tag: 'small', attr: []}, + {tag: 'source', attr: [ + {key: 'src', values: []}, + {key: 'type', values: []}, + {key: 'media', values: []} + ]}, + {tag: 'span', attr: []}, + {tag: 'strike', attr: []}, + {tag: 'strong', attr: []}, + {tag: 'style', attr: [ + {key: 'type', values: ["text/css"]}, + {key: 'media', values: ["all","braille","print","projection","screen","speech"]}, + {key: 'scoped', values: []} + ]}, + {tag: 'sub', attr: []}, + {tag: 'summary', attr: []}, + {tag: 'sup', attr: []}, + {tag: 'table', attr: [ + {key: 'border', values: []} + ]}, + {tag: 'tbody', attr: []}, + {tag: 'td', attr: [ + {key: 'colspan', values: []}, + {key: 'rowspan', values: []}, + {key: 'headers', values: []} + ]}, + {tag: 'textarea', attr: [ + {key: 'autofocus', values: ["","autofocus"]}, + {key: 'disabled', values: ["","disabled"]}, + {key: 'dirname', values: []}, + {key: 'form', values: []}, + {key: 'maxlength', values: []}, + {key: 'name', values: []}, + {key: 'placeholder', values: []}, + {key: 'readonly', values: ["","readonly"]}, + {key: 'required', values: ["","required"]}, + {key: 'rows', values: []}, + {key: 'cols', values: []}, + {key: 'wrap', values: ["soft","hard"]} + ]}, + {tag: 'tfoot', attr: []}, + {tag: 'th', attr: [ + {key: 'colspan', values: []}, + {key: 'rowspan', values: []}, + {key: 'headers', values: []}, + {key: 'scope', values: ["row","col","rowgroup","colgroup"]} + ]}, + {tag: 'thead', attr: []}, + {tag: 'time', attr: [ + {key: 'datetime', values: []} + ]}, + {tag: 'title', attr: []}, + {tag: 'tr', attr: []}, + {tag: 'track', attr: [ + {key: 'kind', values: ["subtitles","captions","descriptions","chapters","metadata"]}, + {key: 'src', values: []}, + {key: 'srclang', values: ["en","es"]}, + {key: 'label', values: []}, + {key: 'default', values: []} + ]}, + {tag: 'tt', attr: []}, + {tag: 'u', attr: []}, + {tag: 'ul', attr: []}, + {tag: 'var', attr: []}, + {tag: 'video', attr: [ + {key: "src", values: []}, + {key: "crossorigin", values: ["anonymous","use-credentials"]}, + {key: "poster", values: []}, + {key: "preload", values: ["auto","metadata","none"]}, + {key: "autoplay", values: ["","autoplay"]}, + {key: "mediagroup", values: ["movie"]}, + {key: "loop", values: ["","loop"]}, + {key: "muted", values: ["","muted"]}, + {key: "controls", values: ["","controls"]}, + {key: "width", values: []}, + {key: "height", values: []} + ]}, + {tag: 'wbr', attr: []} + ]; + + + + var globalAttributes = [ + {key: "accesskey", values: ["a","b","c","d","e","f","g","h","i","j","k","l","m","n","o","p","q","r","s","t","u","v","w","x","y","z","0","1","2","3","4","5","6","7","8","9"]}, + {key: "class", values: []}, + {key: "contenteditable", values: ["true", "false"]}, + {key: "contextmenu", values: []}, + {key: "dir", values: ["ltr","rtl","auto"]}, + {key: "draggable", values: ["true","false","auto"]}, + {key: "dropzone", values: ["copy","move","link","string:","file:"]}, + {key: "hidden", values: ["hidden"]}, + {key: "id", values: []}, + {key: "inert", values: ["inert"]}, + {key: "itemid", values: []}, + {key: "itemprop", values: []}, + {key: "itemref", values: []}, + {key: "itemscope", values: ["itemscope"]}, + {key: "itemtype", values: []}, + {key: "lang", values: ["en","es"]}, + {key: "spellcheck", values: ["true","false"]}, + {key: "style", values: []}, + {key: "tabindex", values: ["1","2","3","4","5","6","7","8","9"]}, + {key: "title", values: []}, + {key: "translate", values: ["yes","no"]}, + {key: "onclick", values: []}, + {key: 'rel', values: ["stylesheet","alternate","author","bookmark","help","license","next","nofollow","noreferrer","prefetch","prev","search","tag"]} + ]; + + + CodeMirror.htmlHint = function(editor) { + if(String.prototype.trim == undefined) { + String.prototype.trim=function(){return this.replace(/^\s+|\s+$/g, '');}; + } + return scriptHint(editor, htmlStructure, function (e, cur) { return e.getTokenAt(cur); }); + }; +})(); \ No newline at end of file From bbd6439b84df0bb52c80beeb6923f3b9a53bf3df Mon Sep 17 00:00:00 2001 From: Juan Benavides Romero Date: Thu, 7 Mar 2013 10:06:32 +0100 Subject: [PATCH 0890/5780] Change tabs for 2 spaces --- addon/hint/html-hint.js | 1154 +++++++++++++++++++-------------------- 1 file changed, 577 insertions(+), 577 deletions(-) diff --git a/addon/hint/html-hint.js b/addon/hint/html-hint.js index 0e43e79c93..4becb34f18 100755 --- a/addon/hint/html-hint.js +++ b/addon/hint/html-hint.js @@ -1,586 +1,586 @@ (function () { - function scriptHint(editor, htmlStructure, getToken) { - var cur = editor.getCursor(); - var token = getToken(editor, cur); - var keywords = []; - var i = 0; - var j = 0; - var k = 0; - var from = {line: cur.line, ch: cur.ch}; - var to = {line: cur.line, ch: cur.ch}; - var flagClean = true; - - text = editor.getRange({line: 0, ch: 0}, cur); - - var open = text.lastIndexOf('<'); + function scriptHint(editor, htmlStructure, getToken) { + var cur = editor.getCursor(); + var token = getToken(editor, cur); + var keywords = []; + var i = 0; + var j = 0; + var k = 0; + var from = {line: cur.line, ch: cur.ch}; + var to = {line: cur.line, ch: cur.ch}; + var flagClean = true; + + text = editor.getRange({line: 0, ch: 0}, cur); + + var open = text.lastIndexOf('<'); var close = text.lastIndexOf('>'); - var tokenString = token.string.replace("<",""); - - if(open > close) { - var last = editor.getRange({line: cur.line, ch: cur.ch - 1}, cur); - if(last == "<") { - for(i = 0; i < htmlStructure.length; i++) { - keywords.push(htmlStructure[i].tag); - } - from.ch = token.start + 1; - } else { - var counter = 0; - var found = function(token, type, position) { - counter++; - if(counter > 50) return; - if(token.type == type) { - return token; - } else { - position.ch = token.start; - var newToken = editor.getTokenAt(position); - return found(newToken, type, position); - } - }; + var tokenString = token.string.replace("<",""); + + if(open > close) { + var last = editor.getRange({line: cur.line, ch: cur.ch - 1}, cur); + if(last == "<") { + for(i = 0; i < htmlStructure.length; i++) { + keywords.push(htmlStructure[i].tag); + } + from.ch = token.start + 1; + } else { + var counter = 0; + var found = function(token, type, position) { + counter++; + if(counter > 50) return; + if(token.type == type) { + return token; + } else { + position.ch = token.start; + var newToken = editor.getTokenAt(position); + return found(newToken, type, position); + } + }; - var nodeToken = found(token, "tag", {line: cur.line, ch: cur.ch}); - var node = nodeToken.string.substring(1); - - if(token.type === null && token.string.trim() === "") { - for(i = 0; i < htmlStructure.length; i++) { - if(htmlStructure[i].tag == node) { - for(j = 0; j < htmlStructure[i].attr.length; j++) { - keywords.push(htmlStructure[i].attr[j].key + "=\"\" "); - } - - for(k = 0; k < globalAttributes.length; k++) { - keywords.push(globalAttributes[k].key + "=\"\" "); - } - } - } - } else if(token.type == "string") { - tokenString = tokenString.substring(1, tokenString.length - 1); - var attributeToken = found(token, "attribute", {line: cur.line, ch: cur.ch}); - var attribute = attributeToken.string; - - for(i = 0; i < htmlStructure.length; i++) { - if(htmlStructure[i].tag == node) { - for(j = 0; j < htmlStructure[i].attr.length; j++) { - if(htmlStructure[i].attr[j].key == attribute) { - for(k = 0; k < htmlStructure[i].attr[j].values.length; k++) { - keywords.push(htmlStructure[i].attr[j].values[k]); - } - } - } - - for(j = 0; j < globalAttributes.length; j++) { - if(globalAttributes[j].key == attribute) { - for(k = 0; k < globalAttributes[j].values.length; k++) { - keywords.push(globalAttributes[j].values[k]); - } - } - } - } - } - from.ch = token.start + 1; - } else if(token.type == "attribute") { - for(i = 0; i < htmlStructure.length; i++) { - if(htmlStructure[i].tag == node) { - for(j = 0; j < htmlStructure[i].attr.length; j++) { - keywords.push(htmlStructure[i].attr[j].key + "=\"\" "); - } - - for(k = 0; k < globalAttributes.length; k++) { - keywords.push(globalAttributes[k].key + "=\"\" "); - } - } - } - from.ch = token.start; - } else if(token.type == "tag") { - for(i = 0; i < htmlStructure.length; i++) { - keywords.push(htmlStructure[i].tag); - } - - from.ch = token.start + 1; - } - } - } else { - for(i = 0; i < htmlStructure.length; i++) { - keywords.push("<" + htmlStructure[i].tag); - } - - tokenString = ("<" + tokenString).trim(); - from.ch = token.start; - } - - if(flagClean === true && tokenString.trim() === "") { - flagClean = false; - } - - if(flagClean) { - keywords = cleanResults(tokenString, keywords); - } + var nodeToken = found(token, "tag", {line: cur.line, ch: cur.ch}); + var node = nodeToken.string.substring(1); + + if(token.type === null && token.string.trim() === "") { + for(i = 0; i < htmlStructure.length; i++) { + if(htmlStructure[i].tag == node) { + for(j = 0; j < htmlStructure[i].attr.length; j++) { + keywords.push(htmlStructure[i].attr[j].key + "=\"\" "); + } + + for(k = 0; k < globalAttributes.length; k++) { + keywords.push(globalAttributes[k].key + "=\"\" "); + } + } + } + } else if(token.type == "string") { + tokenString = tokenString.substring(1, tokenString.length - 1); + var attributeToken = found(token, "attribute", {line: cur.line, ch: cur.ch}); + var attribute = attributeToken.string; + + for(i = 0; i < htmlStructure.length; i++) { + if(htmlStructure[i].tag == node) { + for(j = 0; j < htmlStructure[i].attr.length; j++) { + if(htmlStructure[i].attr[j].key == attribute) { + for(k = 0; k < htmlStructure[i].attr[j].values.length; k++) { + keywords.push(htmlStructure[i].attr[j].values[k]); + } + } + } + + for(j = 0; j < globalAttributes.length; j++) { + if(globalAttributes[j].key == attribute) { + for(k = 0; k < globalAttributes[j].values.length; k++) { + keywords.push(globalAttributes[j].values[k]); + } + } + } + } + } + from.ch = token.start + 1; + } else if(token.type == "attribute") { + for(i = 0; i < htmlStructure.length; i++) { + if(htmlStructure[i].tag == node) { + for(j = 0; j < htmlStructure[i].attr.length; j++) { + keywords.push(htmlStructure[i].attr[j].key + "=\"\" "); + } + + for(k = 0; k < globalAttributes.length; k++) { + keywords.push(globalAttributes[k].key + "=\"\" "); + } + } + } + from.ch = token.start; + } else if(token.type == "tag") { + for(i = 0; i < htmlStructure.length; i++) { + keywords.push(htmlStructure[i].tag); + } + + from.ch = token.start + 1; + } + } + } else { + for(i = 0; i < htmlStructure.length; i++) { + keywords.push("<" + htmlStructure[i].tag); + } + + tokenString = ("<" + tokenString).trim(); + from.ch = token.start; + } + + if(flagClean === true && tokenString.trim() === "") { + flagClean = false; + } + + if(flagClean) { + keywords = cleanResults(tokenString, keywords); + } - return {list: keywords, from: from, to: to}; - } - - - var cleanResults = function(text, keywords) { - var results = []; - var i = 0; + return {list: keywords, from: from, to: to}; + } + + + var cleanResults = function(text, keywords) { + var results = []; + var i = 0; - for(i = 0; i < keywords.length; i++) { - if(keywords[i].substring(0, text.length) == text) { - results.push(keywords[i]); - } - } - - return results; - }; - - - var htmlStructure = [ - {tag: '!DOCTYPE', attr: []}, - {tag: 'a', attr: [ - {key: 'href', values: ["#"]}, - {key: 'target', values: ["_blank","_self","_top","_parent"]}, - {key: 'ping', values: [""]}, - {key: 'media', values: ["#"]}, - {key: 'hreflang', values: ["en","es"]}, - {key: 'type', values: []} - ]}, - {tag: 'abbr', attr: []}, - {tag: 'acronym', attr: []}, - {tag: 'address', attr: []}, - {tag: 'applet', attr: []}, - {tag: 'area', attr: [ - {key: 'alt', values: [""]}, - {key: 'coords', values: ["rect: left, top, right, bottom","circle: center-x, center-y, radius","poly: x1, y1, x2, y2, ..."]}, - {key: 'shape', values: ["default","rect","circle","poly"]}, - {key: 'href', values: ["#"]}, - {key: 'target', values: ["#"]}, - {key: 'ping', values: []}, - {key: 'media', values: []}, - {key: 'hreflang', values: []}, - {key: 'type', values: []} - - ]}, - {tag: 'article', attr: []}, - {tag: 'aside', attr: []}, - {tag: 'audio', attr: [ - {key: 'src', values: []}, - {key: 'crossorigin', values: ["anonymous","use-credentials"]}, - {key: 'preload', values: ["none","metadata","auto"]}, - {key: 'autoplay', values: ["","autoplay"]}, - {key: 'mediagroup', values: []}, - {key: 'loop', values: ["","loop"]}, - {key: 'controls', values: ["","controls"]} - ]}, - {tag: 'b', attr: []}, - {tag: 'base', attr: [ - {key: 'href', values: ["#"]}, - {key: 'target', values: ["_blank","_self","_top","_parent"]} - ]}, - {tag: 'basefont', attr: []}, - {tag: 'bdi', attr: []}, - {tag: 'bdo', attr: []}, - {tag: 'big', attr: []}, - {tag: 'blockquote', attr: [ - {key: 'cite', values: ["http://"]} - ]}, - {tag: 'body', attr: []}, - {tag: 'br', attr: []}, - {tag: 'button', attr: [ - {key: 'autofocus', values: ["","autofocus"]}, - {key: 'disabled', values: ["","disabled"]}, - {key: 'form', values: []}, - {key: 'formaction', values: []}, - {key: 'formenctype', values: ["application/x-www-form-urlencoded","multipart/form-data","text/plain"]}, - {key: 'formmethod', values: ["get","post","put","delete"]}, - {key: 'formnovalidate', values: ["","novalidate"]}, - {key: 'formtarget', values: ["_blank","_self","_top","_parent"]}, - {key: 'name', values: []}, - {key: 'type', values: ["submit","reset","button"]}, - {key: 'value', values: []} - ]}, - {tag: 'canvas', attr: [ - {key: 'width', values: []}, - {key: 'height', values: []} - ]}, - {tag: 'caption', attr: []}, - {tag: 'center', attr: []}, - {tag: 'cite', attr: []}, - {tag: 'code', attr: []}, - {tag: 'col', attr: [ - {key: 'span', values: []} - ]}, - {tag: 'colgroup', attr: [ - {key: 'span', values: []} - ]}, - {tag: 'command', attr: [ - {key: 'type', values: ["command","checkbox","radio"]}, - {key: 'label', values: []}, - {key: 'icon', values: []}, - {key: 'disabled', values: ["","disabled"]}, - {key: 'checked', values: ["","checked"]}, - {key: 'radiogroup', values: []}, - {key: 'command', values: []}, - {key: 'title', values: []} - ]}, - {tag: 'data', attr: [ - {key: 'value', values: []} - ]}, - {tag: 'datagrid', attr: [ - {key: 'disabled', values: ["","disabled"]}, - {key: 'multiple', values: ["","multiple"]} - ]}, - {tag: 'datalist', attr: [ - {key: 'data', values: []} - ]}, - {tag: 'dd', attr: []}, - {tag: 'del', attr: [ - {key: 'cite', values: []}, - {key: 'datetime', values: []} - ]}, - {tag: 'details', attr: [ - {key: 'open', values: ["","open"]} - ]}, - {tag: 'dfn', attr: []}, - {tag: 'dir', attr: []}, - {tag: 'div', attr: [ - {key: 'id', values: []}, - {key: 'class', values: []}, - {key: 'style', values: []} - ]}, - {tag: 'dl', attr: []}, - {tag: 'dt', attr: []}, - {tag: 'em', attr: []}, - {tag: 'embed', attr: [ - {key: 'src', values: []}, - {key: 'type', values: []}, - {key: 'width', values: []}, - {key: 'height', values: []} - ]}, - {tag: 'eventsource', attr: [ - {key: 'src', values: []} - ]}, - {tag: 'fieldset', attr: [ - {key: 'disabled', values: ["","disabled"]}, - {key: 'form', values: []}, - {key: 'name', values: []} - ]}, - {tag: 'figcaption', attr: []}, - {tag: 'figure', attr: []}, - {tag: 'font', attr: []}, - {tag: 'footer', attr: []}, - {tag: 'form', attr: [ - {key: 'accept-charset', values: ["UNKNOWN","utf-8"]}, - {key: 'action', values: []}, - {key: 'autocomplete', values: ["on","off"]}, - {key: 'enctype', values: ["application/x-www-form-urlencoded","multipart/form-data","text/plain"]}, - {key: 'method', values: ["get","post","put","delete","dialog"]}, - {key: 'name', values: []}, - {key: 'novalidate', values: ["","novalidate"]}, - {key: 'target', values: ["_blank","_self","_top","_parent"]} - ]}, - {tag: 'frame', attr: []}, - {tag: 'frameset', attr: []}, - {tag: 'h1', attr: []}, - {tag: 'h2', attr: []}, - {tag: 'h3', attr: []}, - {tag: 'h4', attr: []}, - {tag: 'h5', attr: []}, - {tag: 'h6', attr: []}, - {tag: 'head', attr: []}, - {tag: 'header', attr: []}, - {tag: 'hgroup', attr: []}, - {tag: 'hr', attr: []}, - {tag: 'html', attr: [ - {key: 'manifest', values: []} - ]}, - {tag: 'i', attr: []}, - {tag: 'iframe', attr: [ - {key: 'src', values: []}, - {key: 'srcdoc', values: []}, - {key: 'name', values: []}, - {key: 'sandbox', values: ["allow-top-navigation","allow-same-origin","allow-forms","allow-scripts"]}, - {key: 'seamless', values: ["","seamless"]}, - {key: 'width', values: []}, - {key: 'height', values: []} - ]}, - {tag: 'img', attr: [ - {key: 'alt', values: []}, - {key: 'src', values: []}, - {key: 'crossorigin', values: ["anonymous","use-credentials"]}, - {key: 'ismap', values: []}, - {key: 'usemap', values: []}, - {key: 'width', values: []}, - {key: 'height', values: []} - ]}, - {tag: 'input', attr: [ - {key: 'accept', values: ["audio/*","video/*","image/*"]}, - {key: 'alt', values: []}, - {key: 'autocomplete', values: ["on","off"]}, - {key: 'autofocus', values: ["","autofocus"]}, - {key: 'checked', values: ["","checked"]}, - {key: 'disabled', values: ["","disabled"]}, - {key: 'dirname', values: []}, - {key: 'form', values: []}, - {key: 'formaction', values: []}, - {key: 'formenctype', values: ["application/x-www-form-urlencoded","multipart/form-data","text/plain"]}, - {key: 'formmethod', values: ["get","post","put","delete"]}, - {key: 'formnovalidate', values: ["","novalidate"]}, - {key: 'formtarget', values: ["_blank","_self","_top","_parent"]}, - {key: 'height', values: []}, - {key: 'list', values: []}, - {key: 'max', values: []}, - {key: 'maxlength', values: []}, - {key: 'min', values: []}, - {key: 'multiple', values: ["","multiple"]}, - {key: 'name', values: []}, - {key: 'pattern', values: []}, - {key: 'placeholder', values: []}, - {key: 'readonly', values: ["","readonly"]}, - {key: 'required', values: ["","required"]}, - {key: 'size', values: []}, - {key: 'src', values: []}, - {key: 'step', values: []}, - {key: 'type', values: [ - "hidden","text","search","tel","url","email","password","datetime","date","month","week","time","datetime-local", - "number","range","color","checkbox","radio","file","submit","image","reset","button" - ]}, - {key: 'value', values: []}, - {key: 'width', values: []} - ]}, - {tag: 'ins', attr: [ - {key: 'cite', values: []}, - {key: 'datetime', values: []} - ]}, - {tag: 'kbd', attr: []}, - {tag: 'keygen', attr: [ - {key: 'autofocus', values: ["","autofocus"]}, - {key: 'challenge', values: []}, - {key: 'disabled', values: ["","disabled"]}, - {key: 'form', values: []}, - {key: 'keytype', values: ["RSA"]}, - {key: 'name', values: []} - ]}, - {tag: 'label', attr: [ - {key: 'for', values: []}, - {key: 'form', values: []} - ]}, - {tag: 'legend', attr: []}, - {tag: 'li', attr: [ - {key: 'value', values: []} - ]}, - {tag: 'link', attr: [ - {key: 'href', values: []}, - {key: 'hreflang', values: ["en","es"]}, - {key: 'media', values: [ - "all","screen","print","embossed","braille","handheld","print","projection","screen","tty","tv","speech","3d-glasses", - "resolution [>][<][=] [X]dpi","resolution [>][<][=] [X]dpcm","device-aspect-ratio: 16/9","device-aspect-ratio: 4/3", - "device-aspect-ratio: 32/18","device-aspect-ratio: 1280/720","device-aspect-ratio: 2560/1440","orientation:portrait", - "orientation:landscape","device-height: [X]px","device-width: [X]px","-webkit-min-device-pixel-ratio: 2" - ]}, - {key: 'type', values: []}, - {key: 'sizes', values: ["all","16x16","16x16 32x32","16x16 32x32 64x64"]} - ]}, - {tag: 'map', attr: [ - {key: 'name', values: []} - ]}, - {tag: 'mark', attr: []}, - {tag: 'menu', attr: [ - {key: 'type', values: ["list","context","toolbar"]}, - {key: 'label', values: []} - ]}, - {tag: 'meta', attr: [ - {key: 'charset', attr: ["utf-8"]}, - {key: 'name', attr: ["viewport","application-name","author","description","generator","keywords"]}, - {key: 'content', attr: ["","width=device-width","initial-scale=1, maximum-scale=1, minimun-scale=1, user-scale=no"]}, - {key: 'http-equiv', attr: ["content-language","content-type","default-style","refresh"]} - ]}, - {tag: 'meter', attr: [ - {key: 'value', values: []}, - {key: 'min', values: []}, - {key: 'low', values: []}, - {key: 'high', values: []}, - {key: 'max', values: []}, - {key: 'optimum', values: []} - ]}, - {tag: 'nav', attr: []}, - {tag: 'noframes', attr: []}, - {tag: 'noscript', attr: []}, - {tag: 'object', attr: [ - {key: 'data', values: []}, - {key: 'type', values: []}, - {key: 'typemustmatch', values: ["","typemustmatch"]}, - {key: 'name', values: []}, - {key: 'usemap', values: []}, - {key: 'form', values: []}, - {key: 'width', values: []}, - {key: 'height', values: []} - ]}, - {tag: 'ol', attr: [ - {key: 'reversed', values: ["", "reversed"]}, - {key: 'start', values: []}, - {key: 'type', values: ["1","a","A","i","I"]} - ]}, - {tag: 'optgroup', attr: [ - {key: 'disabled', values: ["","disabled"]}, - {key: 'label', values: []} - ]}, - {tag: 'option', attr: [ - {key: 'disabled', values: ["", "disabled"]}, - {key: 'label', values: []}, - {key: 'selected', values: ["", "selected"]}, - {key: 'value', values: []} - ]}, - {tag: 'output', attr: [ - {key: 'for', values: []}, - {key: 'form', values: []}, - {key: 'name', values: []} - ]}, - {tag: 'p', attr: []}, - {tag: 'param', attr: [ - {key: 'name', values: []}, - {key: 'value', values: []} - ]}, - {tag: 'pre', attr: []}, - {tag: 'progress', attr: [ - {key: 'value', values: []}, - {key: 'max', values: []} - ]}, - {tag: 'q', attr: [ - {key: 'cite', values: []} - ]}, - {tag: 'rp', attr: []}, - {tag: 'rt', attr: []}, - {tag: 'ruby', attr: []}, - {tag: 's', attr: []}, - {tag: 'samp', attr: []}, - {tag: 'script', attr: [ - {key: 'type', values: ["text/javascript"]}, - {key: 'src', values: []}, - {key: 'async', values: ["","async"]}, - {key: 'defer', values: ["","defer"]}, - {key: 'charset', values: ["utf-8"]} - ]}, - {tag: 'section', attr: []}, - {tag: 'select', attr: [ - {key: 'autofocus', values: ["", "autofocus"]}, - {key: 'disabled', values: ["", "disabled"]}, - {key: 'form', values: []}, - {key: 'multiple', values: ["", "multiple"]}, - {key: 'name', values: []}, - {key: 'size', values: []} - ]}, - {tag: 'small', attr: []}, - {tag: 'source', attr: [ - {key: 'src', values: []}, - {key: 'type', values: []}, - {key: 'media', values: []} - ]}, - {tag: 'span', attr: []}, - {tag: 'strike', attr: []}, - {tag: 'strong', attr: []}, - {tag: 'style', attr: [ - {key: 'type', values: ["text/css"]}, - {key: 'media', values: ["all","braille","print","projection","screen","speech"]}, - {key: 'scoped', values: []} - ]}, - {tag: 'sub', attr: []}, - {tag: 'summary', attr: []}, - {tag: 'sup', attr: []}, - {tag: 'table', attr: [ - {key: 'border', values: []} - ]}, - {tag: 'tbody', attr: []}, - {tag: 'td', attr: [ - {key: 'colspan', values: []}, - {key: 'rowspan', values: []}, - {key: 'headers', values: []} - ]}, - {tag: 'textarea', attr: [ - {key: 'autofocus', values: ["","autofocus"]}, - {key: 'disabled', values: ["","disabled"]}, - {key: 'dirname', values: []}, - {key: 'form', values: []}, - {key: 'maxlength', values: []}, - {key: 'name', values: []}, - {key: 'placeholder', values: []}, - {key: 'readonly', values: ["","readonly"]}, - {key: 'required', values: ["","required"]}, - {key: 'rows', values: []}, - {key: 'cols', values: []}, - {key: 'wrap', values: ["soft","hard"]} - ]}, - {tag: 'tfoot', attr: []}, - {tag: 'th', attr: [ - {key: 'colspan', values: []}, - {key: 'rowspan', values: []}, - {key: 'headers', values: []}, - {key: 'scope', values: ["row","col","rowgroup","colgroup"]} - ]}, - {tag: 'thead', attr: []}, - {tag: 'time', attr: [ - {key: 'datetime', values: []} - ]}, - {tag: 'title', attr: []}, - {tag: 'tr', attr: []}, - {tag: 'track', attr: [ - {key: 'kind', values: ["subtitles","captions","descriptions","chapters","metadata"]}, - {key: 'src', values: []}, - {key: 'srclang', values: ["en","es"]}, - {key: 'label', values: []}, - {key: 'default', values: []} - ]}, - {tag: 'tt', attr: []}, - {tag: 'u', attr: []}, - {tag: 'ul', attr: []}, - {tag: 'var', attr: []}, - {tag: 'video', attr: [ - {key: "src", values: []}, - {key: "crossorigin", values: ["anonymous","use-credentials"]}, - {key: "poster", values: []}, - {key: "preload", values: ["auto","metadata","none"]}, - {key: "autoplay", values: ["","autoplay"]}, - {key: "mediagroup", values: ["movie"]}, - {key: "loop", values: ["","loop"]}, - {key: "muted", values: ["","muted"]}, - {key: "controls", values: ["","controls"]}, - {key: "width", values: []}, - {key: "height", values: []} - ]}, - {tag: 'wbr', attr: []} - ]; - - - - var globalAttributes = [ - {key: "accesskey", values: ["a","b","c","d","e","f","g","h","i","j","k","l","m","n","o","p","q","r","s","t","u","v","w","x","y","z","0","1","2","3","4","5","6","7","8","9"]}, - {key: "class", values: []}, - {key: "contenteditable", values: ["true", "false"]}, - {key: "contextmenu", values: []}, - {key: "dir", values: ["ltr","rtl","auto"]}, - {key: "draggable", values: ["true","false","auto"]}, - {key: "dropzone", values: ["copy","move","link","string:","file:"]}, - {key: "hidden", values: ["hidden"]}, - {key: "id", values: []}, - {key: "inert", values: ["inert"]}, - {key: "itemid", values: []}, - {key: "itemprop", values: []}, - {key: "itemref", values: []}, - {key: "itemscope", values: ["itemscope"]}, - {key: "itemtype", values: []}, - {key: "lang", values: ["en","es"]}, - {key: "spellcheck", values: ["true","false"]}, - {key: "style", values: []}, - {key: "tabindex", values: ["1","2","3","4","5","6","7","8","9"]}, - {key: "title", values: []}, - {key: "translate", values: ["yes","no"]}, - {key: "onclick", values: []}, - {key: 'rel', values: ["stylesheet","alternate","author","bookmark","help","license","next","nofollow","noreferrer","prefetch","prev","search","tag"]} - ]; - - - CodeMirror.htmlHint = function(editor) { + for(i = 0; i < keywords.length; i++) { + if(keywords[i].substring(0, text.length) == text) { + results.push(keywords[i]); + } + } + + return results; + }; + + + var htmlStructure = [ + {tag: '!DOCTYPE', attr: []}, + {tag: 'a', attr: [ + {key: 'href', values: ["#"]}, + {key: 'target', values: ["_blank","_self","_top","_parent"]}, + {key: 'ping', values: [""]}, + {key: 'media', values: ["#"]}, + {key: 'hreflang', values: ["en","es"]}, + {key: 'type', values: []} + ]}, + {tag: 'abbr', attr: []}, + {tag: 'acronym', attr: []}, + {tag: 'address', attr: []}, + {tag: 'applet', attr: []}, + {tag: 'area', attr: [ + {key: 'alt', values: [""]}, + {key: 'coords', values: ["rect: left, top, right, bottom","circle: center-x, center-y, radius","poly: x1, y1, x2, y2, ..."]}, + {key: 'shape', values: ["default","rect","circle","poly"]}, + {key: 'href', values: ["#"]}, + {key: 'target', values: ["#"]}, + {key: 'ping', values: []}, + {key: 'media', values: []}, + {key: 'hreflang', values: []}, + {key: 'type', values: []} + + ]}, + {tag: 'article', attr: []}, + {tag: 'aside', attr: []}, + {tag: 'audio', attr: [ + {key: 'src', values: []}, + {key: 'crossorigin', values: ["anonymous","use-credentials"]}, + {key: 'preload', values: ["none","metadata","auto"]}, + {key: 'autoplay', values: ["","autoplay"]}, + {key: 'mediagroup', values: []}, + {key: 'loop', values: ["","loop"]}, + {key: 'controls', values: ["","controls"]} + ]}, + {tag: 'b', attr: []}, + {tag: 'base', attr: [ + {key: 'href', values: ["#"]}, + {key: 'target', values: ["_blank","_self","_top","_parent"]} + ]}, + {tag: 'basefont', attr: []}, + {tag: 'bdi', attr: []}, + {tag: 'bdo', attr: []}, + {tag: 'big', attr: []}, + {tag: 'blockquote', attr: [ + {key: 'cite', values: ["http://"]} + ]}, + {tag: 'body', attr: []}, + {tag: 'br', attr: []}, + {tag: 'button', attr: [ + {key: 'autofocus', values: ["","autofocus"]}, + {key: 'disabled', values: ["","disabled"]}, + {key: 'form', values: []}, + {key: 'formaction', values: []}, + {key: 'formenctype', values: ["application/x-www-form-urlencoded","multipart/form-data","text/plain"]}, + {key: 'formmethod', values: ["get","post","put","delete"]}, + {key: 'formnovalidate', values: ["","novalidate"]}, + {key: 'formtarget', values: ["_blank","_self","_top","_parent"]}, + {key: 'name', values: []}, + {key: 'type', values: ["submit","reset","button"]}, + {key: 'value', values: []} + ]}, + {tag: 'canvas', attr: [ + {key: 'width', values: []}, + {key: 'height', values: []} + ]}, + {tag: 'caption', attr: []}, + {tag: 'center', attr: []}, + {tag: 'cite', attr: []}, + {tag: 'code', attr: []}, + {tag: 'col', attr: [ + {key: 'span', values: []} + ]}, + {tag: 'colgroup', attr: [ + {key: 'span', values: []} + ]}, + {tag: 'command', attr: [ + {key: 'type', values: ["command","checkbox","radio"]}, + {key: 'label', values: []}, + {key: 'icon', values: []}, + {key: 'disabled', values: ["","disabled"]}, + {key: 'checked', values: ["","checked"]}, + {key: 'radiogroup', values: []}, + {key: 'command', values: []}, + {key: 'title', values: []} + ]}, + {tag: 'data', attr: [ + {key: 'value', values: []} + ]}, + {tag: 'datagrid', attr: [ + {key: 'disabled', values: ["","disabled"]}, + {key: 'multiple', values: ["","multiple"]} + ]}, + {tag: 'datalist', attr: [ + {key: 'data', values: []} + ]}, + {tag: 'dd', attr: []}, + {tag: 'del', attr: [ + {key: 'cite', values: []}, + {key: 'datetime', values: []} + ]}, + {tag: 'details', attr: [ + {key: 'open', values: ["","open"]} + ]}, + {tag: 'dfn', attr: []}, + {tag: 'dir', attr: []}, + {tag: 'div', attr: [ + {key: 'id', values: []}, + {key: 'class', values: []}, + {key: 'style', values: []} + ]}, + {tag: 'dl', attr: []}, + {tag: 'dt', attr: []}, + {tag: 'em', attr: []}, + {tag: 'embed', attr: [ + {key: 'src', values: []}, + {key: 'type', values: []}, + {key: 'width', values: []}, + {key: 'height', values: []} + ]}, + {tag: 'eventsource', attr: [ + {key: 'src', values: []} + ]}, + {tag: 'fieldset', attr: [ + {key: 'disabled', values: ["","disabled"]}, + {key: 'form', values: []}, + {key: 'name', values: []} + ]}, + {tag: 'figcaption', attr: []}, + {tag: 'figure', attr: []}, + {tag: 'font', attr: []}, + {tag: 'footer', attr: []}, + {tag: 'form', attr: [ + {key: 'accept-charset', values: ["UNKNOWN","utf-8"]}, + {key: 'action', values: []}, + {key: 'autocomplete', values: ["on","off"]}, + {key: 'enctype', values: ["application/x-www-form-urlencoded","multipart/form-data","text/plain"]}, + {key: 'method', values: ["get","post","put","delete","dialog"]}, + {key: 'name', values: []}, + {key: 'novalidate', values: ["","novalidate"]}, + {key: 'target', values: ["_blank","_self","_top","_parent"]} + ]}, + {tag: 'frame', attr: []}, + {tag: 'frameset', attr: []}, + {tag: 'h1', attr: []}, + {tag: 'h2', attr: []}, + {tag: 'h3', attr: []}, + {tag: 'h4', attr: []}, + {tag: 'h5', attr: []}, + {tag: 'h6', attr: []}, + {tag: 'head', attr: []}, + {tag: 'header', attr: []}, + {tag: 'hgroup', attr: []}, + {tag: 'hr', attr: []}, + {tag: 'html', attr: [ + {key: 'manifest', values: []} + ]}, + {tag: 'i', attr: []}, + {tag: 'iframe', attr: [ + {key: 'src', values: []}, + {key: 'srcdoc', values: []}, + {key: 'name', values: []}, + {key: 'sandbox', values: ["allow-top-navigation","allow-same-origin","allow-forms","allow-scripts"]}, + {key: 'seamless', values: ["","seamless"]}, + {key: 'width', values: []}, + {key: 'height', values: []} + ]}, + {tag: 'img', attr: [ + {key: 'alt', values: []}, + {key: 'src', values: []}, + {key: 'crossorigin', values: ["anonymous","use-credentials"]}, + {key: 'ismap', values: []}, + {key: 'usemap', values: []}, + {key: 'width', values: []}, + {key: 'height', values: []} + ]}, + {tag: 'input', attr: [ + {key: 'accept', values: ["audio/*","video/*","image/*"]}, + {key: 'alt', values: []}, + {key: 'autocomplete', values: ["on","off"]}, + {key: 'autofocus', values: ["","autofocus"]}, + {key: 'checked', values: ["","checked"]}, + {key: 'disabled', values: ["","disabled"]}, + {key: 'dirname', values: []}, + {key: 'form', values: []}, + {key: 'formaction', values: []}, + {key: 'formenctype', values: ["application/x-www-form-urlencoded","multipart/form-data","text/plain"]}, + {key: 'formmethod', values: ["get","post","put","delete"]}, + {key: 'formnovalidate', values: ["","novalidate"]}, + {key: 'formtarget', values: ["_blank","_self","_top","_parent"]}, + {key: 'height', values: []}, + {key: 'list', values: []}, + {key: 'max', values: []}, + {key: 'maxlength', values: []}, + {key: 'min', values: []}, + {key: 'multiple', values: ["","multiple"]}, + {key: 'name', values: []}, + {key: 'pattern', values: []}, + {key: 'placeholder', values: []}, + {key: 'readonly', values: ["","readonly"]}, + {key: 'required', values: ["","required"]}, + {key: 'size', values: []}, + {key: 'src', values: []}, + {key: 'step', values: []}, + {key: 'type', values: [ + "hidden","text","search","tel","url","email","password","datetime","date","month","week","time","datetime-local", + "number","range","color","checkbox","radio","file","submit","image","reset","button" + ]}, + {key: 'value', values: []}, + {key: 'width', values: []} + ]}, + {tag: 'ins', attr: [ + {key: 'cite', values: []}, + {key: 'datetime', values: []} + ]}, + {tag: 'kbd', attr: []}, + {tag: 'keygen', attr: [ + {key: 'autofocus', values: ["","autofocus"]}, + {key: 'challenge', values: []}, + {key: 'disabled', values: ["","disabled"]}, + {key: 'form', values: []}, + {key: 'keytype', values: ["RSA"]}, + {key: 'name', values: []} + ]}, + {tag: 'label', attr: [ + {key: 'for', values: []}, + {key: 'form', values: []} + ]}, + {tag: 'legend', attr: []}, + {tag: 'li', attr: [ + {key: 'value', values: []} + ]}, + {tag: 'link', attr: [ + {key: 'href', values: []}, + {key: 'hreflang', values: ["en","es"]}, + {key: 'media', values: [ + "all","screen","print","embossed","braille","handheld","print","projection","screen","tty","tv","speech","3d-glasses", + "resolution [>][<][=] [X]dpi","resolution [>][<][=] [X]dpcm","device-aspect-ratio: 16/9","device-aspect-ratio: 4/3", + "device-aspect-ratio: 32/18","device-aspect-ratio: 1280/720","device-aspect-ratio: 2560/1440","orientation:portrait", + "orientation:landscape","device-height: [X]px","device-width: [X]px","-webkit-min-device-pixel-ratio: 2" + ]}, + {key: 'type', values: []}, + {key: 'sizes', values: ["all","16x16","16x16 32x32","16x16 32x32 64x64"]} + ]}, + {tag: 'map', attr: [ + {key: 'name', values: []} + ]}, + {tag: 'mark', attr: []}, + {tag: 'menu', attr: [ + {key: 'type', values: ["list","context","toolbar"]}, + {key: 'label', values: []} + ]}, + {tag: 'meta', attr: [ + {key: 'charset', attr: ["utf-8"]}, + {key: 'name', attr: ["viewport","application-name","author","description","generator","keywords"]}, + {key: 'content', attr: ["","width=device-width","initial-scale=1, maximum-scale=1, minimun-scale=1, user-scale=no"]}, + {key: 'http-equiv', attr: ["content-language","content-type","default-style","refresh"]} + ]}, + {tag: 'meter', attr: [ + {key: 'value', values: []}, + {key: 'min', values: []}, + {key: 'low', values: []}, + {key: 'high', values: []}, + {key: 'max', values: []}, + {key: 'optimum', values: []} + ]}, + {tag: 'nav', attr: []}, + {tag: 'noframes', attr: []}, + {tag: 'noscript', attr: []}, + {tag: 'object', attr: [ + {key: 'data', values: []}, + {key: 'type', values: []}, + {key: 'typemustmatch', values: ["","typemustmatch"]}, + {key: 'name', values: []}, + {key: 'usemap', values: []}, + {key: 'form', values: []}, + {key: 'width', values: []}, + {key: 'height', values: []} + ]}, + {tag: 'ol', attr: [ + {key: 'reversed', values: ["", "reversed"]}, + {key: 'start', values: []}, + {key: 'type', values: ["1","a","A","i","I"]} + ]}, + {tag: 'optgroup', attr: [ + {key: 'disabled', values: ["","disabled"]}, + {key: 'label', values: []} + ]}, + {tag: 'option', attr: [ + {key: 'disabled', values: ["", "disabled"]}, + {key: 'label', values: []}, + {key: 'selected', values: ["", "selected"]}, + {key: 'value', values: []} + ]}, + {tag: 'output', attr: [ + {key: 'for', values: []}, + {key: 'form', values: []}, + {key: 'name', values: []} + ]}, + {tag: 'p', attr: []}, + {tag: 'param', attr: [ + {key: 'name', values: []}, + {key: 'value', values: []} + ]}, + {tag: 'pre', attr: []}, + {tag: 'progress', attr: [ + {key: 'value', values: []}, + {key: 'max', values: []} + ]}, + {tag: 'q', attr: [ + {key: 'cite', values: []} + ]}, + {tag: 'rp', attr: []}, + {tag: 'rt', attr: []}, + {tag: 'ruby', attr: []}, + {tag: 's', attr: []}, + {tag: 'samp', attr: []}, + {tag: 'script', attr: [ + {key: 'type', values: ["text/javascript"]}, + {key: 'src', values: []}, + {key: 'async', values: ["","async"]}, + {key: 'defer', values: ["","defer"]}, + {key: 'charset', values: ["utf-8"]} + ]}, + {tag: 'section', attr: []}, + {tag: 'select', attr: [ + {key: 'autofocus', values: ["", "autofocus"]}, + {key: 'disabled', values: ["", "disabled"]}, + {key: 'form', values: []}, + {key: 'multiple', values: ["", "multiple"]}, + {key: 'name', values: []}, + {key: 'size', values: []} + ]}, + {tag: 'small', attr: []}, + {tag: 'source', attr: [ + {key: 'src', values: []}, + {key: 'type', values: []}, + {key: 'media', values: []} + ]}, + {tag: 'span', attr: []}, + {tag: 'strike', attr: []}, + {tag: 'strong', attr: []}, + {tag: 'style', attr: [ + {key: 'type', values: ["text/css"]}, + {key: 'media', values: ["all","braille","print","projection","screen","speech"]}, + {key: 'scoped', values: []} + ]}, + {tag: 'sub', attr: []}, + {tag: 'summary', attr: []}, + {tag: 'sup', attr: []}, + {tag: 'table', attr: [ + {key: 'border', values: []} + ]}, + {tag: 'tbody', attr: []}, + {tag: 'td', attr: [ + {key: 'colspan', values: []}, + {key: 'rowspan', values: []}, + {key: 'headers', values: []} + ]}, + {tag: 'textarea', attr: [ + {key: 'autofocus', values: ["","autofocus"]}, + {key: 'disabled', values: ["","disabled"]}, + {key: 'dirname', values: []}, + {key: 'form', values: []}, + {key: 'maxlength', values: []}, + {key: 'name', values: []}, + {key: 'placeholder', values: []}, + {key: 'readonly', values: ["","readonly"]}, + {key: 'required', values: ["","required"]}, + {key: 'rows', values: []}, + {key: 'cols', values: []}, + {key: 'wrap', values: ["soft","hard"]} + ]}, + {tag: 'tfoot', attr: []}, + {tag: 'th', attr: [ + {key: 'colspan', values: []}, + {key: 'rowspan', values: []}, + {key: 'headers', values: []}, + {key: 'scope', values: ["row","col","rowgroup","colgroup"]} + ]}, + {tag: 'thead', attr: []}, + {tag: 'time', attr: [ + {key: 'datetime', values: []} + ]}, + {tag: 'title', attr: []}, + {tag: 'tr', attr: []}, + {tag: 'track', attr: [ + {key: 'kind', values: ["subtitles","captions","descriptions","chapters","metadata"]}, + {key: 'src', values: []}, + {key: 'srclang', values: ["en","es"]}, + {key: 'label', values: []}, + {key: 'default', values: []} + ]}, + {tag: 'tt', attr: []}, + {tag: 'u', attr: []}, + {tag: 'ul', attr: []}, + {tag: 'var', attr: []}, + {tag: 'video', attr: [ + {key: "src", values: []}, + {key: "crossorigin", values: ["anonymous","use-credentials"]}, + {key: "poster", values: []}, + {key: "preload", values: ["auto","metadata","none"]}, + {key: "autoplay", values: ["","autoplay"]}, + {key: "mediagroup", values: ["movie"]}, + {key: "loop", values: ["","loop"]}, + {key: "muted", values: ["","muted"]}, + {key: "controls", values: ["","controls"]}, + {key: "width", values: []}, + {key: "height", values: []} + ]}, + {tag: 'wbr', attr: []} + ]; + + + + var globalAttributes = [ + {key: "accesskey", values: ["a","b","c","d","e","f","g","h","i","j","k","l","m","n","o","p","q","r","s","t","u","v","w","x","y","z","0","1","2","3","4","5","6","7","8","9"]}, + {key: "class", values: []}, + {key: "contenteditable", values: ["true", "false"]}, + {key: "contextmenu", values: []}, + {key: "dir", values: ["ltr","rtl","auto"]}, + {key: "draggable", values: ["true","false","auto"]}, + {key: "dropzone", values: ["copy","move","link","string:","file:"]}, + {key: "hidden", values: ["hidden"]}, + {key: "id", values: []}, + {key: "inert", values: ["inert"]}, + {key: "itemid", values: []}, + {key: "itemprop", values: []}, + {key: "itemref", values: []}, + {key: "itemscope", values: ["itemscope"]}, + {key: "itemtype", values: []}, + {key: "lang", values: ["en","es"]}, + {key: "spellcheck", values: ["true","false"]}, + {key: "style", values: []}, + {key: "tabindex", values: ["1","2","3","4","5","6","7","8","9"]}, + {key: "title", values: []}, + {key: "translate", values: ["yes","no"]}, + {key: "onclick", values: []}, + {key: 'rel', values: ["stylesheet","alternate","author","bookmark","help","license","next","nofollow","noreferrer","prefetch","prev","search","tag"]} + ]; + + + CodeMirror.htmlHint = function(editor) { if(String.prototype.trim == undefined) { String.prototype.trim=function(){return this.replace(/^\s+|\s+$/g, '');}; } - return scriptHint(editor, htmlStructure, function (e, cur) { return e.getTokenAt(cur); }); - }; + return scriptHint(editor, htmlStructure, function (e, cur) { return e.getTokenAt(cur); }); + }; })(); \ No newline at end of file From 52e691cb1198cce8b9966d99ea006c4ea19ed8c3 Mon Sep 17 00:00:00 2001 From: Juan Benavides Romero Date: Thu, 7 Mar 2013 17:57:54 +0100 Subject: [PATCH 0891/5780] Added a demo page for html5 autocomplete --- demo/html5complete.html | 92 +++++++++++++++++++++++++++++++++++++++++ 1 file changed, 92 insertions(+) create mode 100644 demo/html5complete.html diff --git a/demo/html5complete.html b/demo/html5complete.html new file mode 100644 index 0000000000..5091354ae9 --- /dev/null +++ b/demo/html5complete.html @@ -0,0 +1,92 @@ + + + + + CodeMirror: Close-Tag Demo + + + + + + + + + + + + + + + + +

    HTML5 code completation demo

    +
      +
    • Type an html tag. If you press Ctrl+Space a hint panel show the code suggest. You can type to autocomplete tags, attributes if your cursor are inner a tag or attribute values if your cursor are inner a attribute value.
    • +
    + +
    + + + From 92b6fa34b6ea6912eb4295dbee111412769c8569 Mon Sep 17 00:00:00 2001 From: Marijn Haverbeke Date: Tue, 19 Mar 2013 10:26:41 +0100 Subject: [PATCH 0892/5780] [html-hint addon] Integrate --- addon/hint/html-hint.js | 70 +++++++++++++++++++---------------------- doc/compress.html | 1 + 2 files changed, 34 insertions(+), 37 deletions(-) diff --git a/addon/hint/html-hint.js b/addon/hint/html-hint.js index 4becb34f18..8b5dc6f002 100755 --- a/addon/hint/html-hint.js +++ b/addon/hint/html-hint.js @@ -1,5 +1,5 @@ (function () { - function scriptHint(editor, htmlStructure, getToken) { + function htmlHint(editor, htmlStructure, getToken) { var cur = editor.getCursor(); var token = getToken(editor, cur); var keywords = []; @@ -9,13 +9,13 @@ var from = {line: cur.line, ch: cur.ch}; var to = {line: cur.line, ch: cur.ch}; var flagClean = true; - - text = editor.getRange({line: 0, ch: 0}, cur); - + + var text = editor.getRange({line: 0, ch: 0}, cur); + var open = text.lastIndexOf('<'); - var close = text.lastIndexOf('>'); + var close = text.lastIndexOf('>'); var tokenString = token.string.replace("<",""); - + if(open > close) { var last = editor.getRange({line: cur.line, ch: cur.ch - 1}, cur); if(last == "<") { @@ -29,24 +29,24 @@ counter++; if(counter > 50) return; if(token.type == type) { - return token; + return token; } else { position.ch = token.start; var newToken = editor.getTokenAt(position); return found(newToken, type, position); } }; - + var nodeToken = found(token, "tag", {line: cur.line, ch: cur.ch}); var node = nodeToken.string.substring(1); - + if(token.type === null && token.string.trim() === "") { for(i = 0; i < htmlStructure.length; i++) { if(htmlStructure[i].tag == node) { for(j = 0; j < htmlStructure[i].attr.length; j++) { keywords.push(htmlStructure[i].attr[j].key + "=\"\" "); } - + for(k = 0; k < globalAttributes.length; k++) { keywords.push(globalAttributes[k].key + "=\"\" "); } @@ -56,7 +56,7 @@ tokenString = tokenString.substring(1, tokenString.length - 1); var attributeToken = found(token, "attribute", {line: cur.line, ch: cur.ch}); var attribute = attributeToken.string; - + for(i = 0; i < htmlStructure.length; i++) { if(htmlStructure[i].tag == node) { for(j = 0; j < htmlStructure[i].attr.length; j++) { @@ -66,7 +66,7 @@ } } } - + for(j = 0; j < globalAttributes.length; j++) { if(globalAttributes[j].key == attribute) { for(k = 0; k < globalAttributes[j].values.length; k++) { @@ -83,7 +83,7 @@ for(j = 0; j < htmlStructure[i].attr.length; j++) { keywords.push(htmlStructure[i].attr[j].key + "=\"\" "); } - + for(k = 0; k < globalAttributes.length; k++) { keywords.push(globalAttributes[k].key + "=\"\" "); } @@ -94,7 +94,7 @@ for(i = 0; i < htmlStructure.length; i++) { keywords.push(htmlStructure[i].tag); } - + from.ch = token.start + 1; } } @@ -102,37 +102,36 @@ for(i = 0; i < htmlStructure.length; i++) { keywords.push("<" + htmlStructure[i].tag); } - + tokenString = ("<" + tokenString).trim(); from.ch = token.start; } - + if(flagClean === true && tokenString.trim() === "") { - flagClean = false; + flagClean = false; } - + if(flagClean) { keywords = cleanResults(tokenString, keywords); } - + return {list: keywords, from: from, to: to}; } - - + + var cleanResults = function(text, keywords) { var results = []; var i = 0; - + for(i = 0; i < keywords.length; i++) { if(keywords[i].substring(0, text.length) == text) { - results.push(keywords[i]); + results.push(keywords[i]); } } - + return results; }; - - + var htmlStructure = [ {tag: '!DOCTYPE', attr: []}, {tag: 'a', attr: [ @@ -157,7 +156,7 @@ {key: 'media', values: []}, {key: 'hreflang', values: []}, {key: 'type', values: []} - + ]}, {tag: 'article', attr: []}, {tag: 'aside', attr: []}, @@ -547,9 +546,7 @@ ]}, {tag: 'wbr', attr: []} ]; - - - + var globalAttributes = [ {key: "accesskey", values: ["a","b","c","d","e","f","g","h","i","j","k","l","m","n","o","p","q","r","s","t","u","v","w","x","y","z","0","1","2","3","4","5","6","7","8","9"]}, {key: "class", values: []}, @@ -575,12 +572,11 @@ {key: "onclick", values: []}, {key: 'rel', values: ["stylesheet","alternate","author","bookmark","help","license","next","nofollow","noreferrer","prefetch","prev","search","tag"]} ]; - - + CodeMirror.htmlHint = function(editor) { - if(String.prototype.trim == undefined) { - String.prototype.trim=function(){return this.replace(/^\s+|\s+$/g, '');}; - } - return scriptHint(editor, htmlStructure, function (e, cur) { return e.getTokenAt(cur); }); + if(String.prototype.trim == undefined) { + String.prototype.trim=function(){return this.replace(/^\s+|\s+$/g, '');}; + } + return htmlHint(editor, htmlStructure, function (e, cur) { return e.getTokenAt(cur); }); }; -})(); \ No newline at end of file +})(); diff --git a/doc/compress.html b/doc/compress.html index 22b8362fc1..c559730e90 100644 --- a/doc/compress.html +++ b/doc/compress.html @@ -146,6 +146,7 @@

    { } CodeMi + From 3c07efc9bd12472a2d10ece99e68d4cb2ce61162 Mon Sep 17 00:00:00 2001 From: Marijn Haverbeke Date: Tue, 19 Mar 2013 10:38:56 +0100 Subject: [PATCH 0893/5780] [compression helper] Fix copy-paste mistake --- doc/compress.html | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/doc/compress.html b/doc/compress.html index c559730e90..71ae09d149 100644 --- a/doc/compress.html +++ b/doc/compress.html @@ -146,7 +146,7 @@

    { } CodeMi - + From df76d4ecfd4adbe884eeab81a003875e1b8cc9ad Mon Sep 17 00:00:00 2001 From: "Daniel, Dao Quang Minh" Date: Wed, 20 Mar 2013 09:33:42 +0800 Subject: [PATCH 0894/5780] [css mode] fix navy and yellow not hightlighted as color --- mode/css/css.js | 4 ++-- mode/css/test.js | 6 +++++- 2 files changed, 7 insertions(+), 3 deletions(-) diff --git a/mode/css/css.js b/mode/css/css.js index 00d605a69d..7fc5387ffe 100644 --- a/mode/css/css.js +++ b/mode/css/css.js @@ -445,7 +445,7 @@ CodeMirror.defineMode("css-base", function(config, parserConfig) { "menu", "menulist", "menulist-button", "menulist-text", "menulist-textfield", "menutext", "message-box", "middle", "min-intrinsic", "mix", "mongolian", "monospace", "move", "multiple", "myanmar", "n-resize", - "narrower", "navy", "ne-resize", "nesw-resize", "no-close-quote", "no-drop", + "narrower", "ne-resize", "nesw-resize", "no-close-quote", "no-drop", "no-open-quote", "no-repeat", "none", "normal", "not-allowed", "nowrap", "ns-resize", "nw-resize", "nwse-resize", "oblique", "octal", "open-quote", "optimizeLegibility", "optimizeSpeed", "oriya", "oromo", "outset", @@ -477,7 +477,7 @@ CodeMirror.defineMode("css-base", function(config, parserConfig) { "vertical", "vertical-text", "visible", "visibleFill", "visiblePainted", "visibleStroke", "visual", "w-resize", "wait", "wave", "white", "wider", "window", "windowframe", "windowtext", "x-large", "x-small", "xor", - "xx-large", "xx-small", "yellow" + "xx-large", "xx-small" ]); function tokenCComment(stream, state) { diff --git a/mode/css/test.js b/mode/css/test.js index 0f68deb1c4..97dd0a8a34 100644 --- a/mode/css/test.js +++ b/mode/css/test.js @@ -75,7 +75,11 @@ "[tag foo] { [property font-family][operator :] [string 'hello world']; }"); MT("tagColorKeyword", - "[tag foo] { [property color][operator :] [keyword black]; }"); + "[tag foo] {" + + "[property color][operator :] [keyword black];" + + "[property color][operator :] [keyword navy];" + + "[property color][operator :] [keyword yellow];" + + "}"); MT("tagColorHex3", "[tag foo] { [property background][operator :] [atom #fff]; }"); From 6abe503b62f4347d80afdbe2cc3f0a3f019d403d Mon Sep 17 00:00:00 2001 From: "Daniel, Dao Quang Minh" Date: Wed, 20 Mar 2013 09:23:02 +0800 Subject: [PATCH 0895/5780] [scss mode] fix nested id selector not highlighted properly --- mode/css/css.js | 2 +- mode/css/scss.html | 3 +++ mode/css/scss_test.js | 3 +++ 3 files changed, 7 insertions(+), 1 deletion(-) diff --git a/mode/css/css.js b/mode/css/css.js index 7fc5387ffe..1ef72b517d 100644 --- a/mode/css/css.js +++ b/mode/css/css.js @@ -234,7 +234,7 @@ CodeMirror.defineMode("css-base", function(config, parserConfig) { style = "error"; } } else if (style == "atom") { - if(!context || context == "@media{") { + if(!context || context == "@media{" || context == "block") { style = "builtin"; } else if (context == "propertyValue") { if (!/^#([0-9a-fA-f]{3}|[0-9a-fA-f]{6})$/.test(stream.current())) { diff --git a/mode/css/scss.html b/mode/css/scss.html index b67d7ff28a..b90cbe876c 100644 --- a/mode/css/scss.html +++ b/mode/css/scss.html @@ -21,6 +21,9 @@

    CodeMirror: SCSS mode

    $margin: 16px; .content-navigation { + #nested { + background-color: black; + } border-color: $blue; color: darken($blue, 9%); diff --git a/mode/css/scss_test.js b/mode/css/scss_test.js index 330b0a6a5b..996dc78849 100644 --- a/mode/css/scss_test.js +++ b/mode/css/scss_test.js @@ -74,4 +74,7 @@ MT("divide_operator", "[tag foo] { [property width][operator :][number 4] [operator /] [number 2] }"); + + MT('nested_structure_with_id_selector', + "[tag p] { [builtin #hello] { [property color][operator :][keyword red]; } }"); })(); From 192b2c2b9216a8063504334732536aa293e55657 Mon Sep 17 00:00:00 2001 From: Marijn Haverbeke Date: Wed, 20 Mar 2013 07:33:46 +0100 Subject: [PATCH 0896/5780] Make tab elements inline blocks Closes #1380 --- lib/codemirror.css | 2 ++ 1 file changed, 2 insertions(+) diff --git a/lib/codemirror.css b/lib/codemirror.css index bab16bc2c9..411166bc72 100644 --- a/lib/codemirror.css +++ b/lib/codemirror.css @@ -56,6 +56,8 @@ /* Can style cursor different in overwrite (non-insert) mode */ .CodeMirror div.CodeMirror-cursor.CodeMirror-overwrite {} +.cm-tab { display: inline-block; } + /* DEFAULT THEME */ .cm-s-default .cm-keyword {color: #708;} From 740366d3a2aba3c6d1a3a1affd50ed4d08f1ed05 Mon Sep 17 00:00:00 2001 From: Yunchi Luo Date: Tue, 19 Mar 2013 21:22:29 -0400 Subject: [PATCH 0897/5780] [vim] Fix :num and add unit test --- keymap/vim.js | 3 ++- test/vim_test.js | 5 +++++ 2 files changed, 7 insertions(+), 1 deletion(-) diff --git a/keymap/vim.js b/keymap/vim.js index af964a666d..77b696d93b 100644 --- a/keymap/vim.js +++ b/keymap/vim.js @@ -2543,7 +2543,8 @@ exCommandDispatcher.map(mapArgs[0], mapArgs[1], cm); }, move: function(cm, params) { - commandDispatcher.processMotion(cm, getVimState(cm), { + commandDispatcher.processCommand(cm, getVimState(cm), { + type: 'motion', motion: 'moveToLineOrEdgeOfDocument', motionArgs: { forward: false, explicitRepeat: true, linewise: true }, diff --git a/test/vim_test.js b/test/vim_test.js index c3b23a3b4a..f91be02f9e 100644 --- a/test/vim_test.js +++ b/test/vim_test.js @@ -1158,6 +1158,11 @@ testVim('._repeat', function(cm, vim, helpers) { }, { value: '1 2 3 4 5 6'}); // Ex mode tests +testVim('ex_go_to_line', function(cm, vim, helpers) { + cm.setCursor(0, 0); + helpers.doEx('4'); + helpers.assertCursorAt(3, 0); +}, { value: 'a\nb\nc\nd\ne\n'}); testVim('ex_write', function(cm, vim, helpers) { var tmp = CodeMirror.commands.save; var written; From 4a745fe6680d104ff4c4347617a5964dcb01c46b Mon Sep 17 00:00:00 2001 From: Marijn Haverbeke Date: Wed, 20 Mar 2013 11:25:32 +0100 Subject: [PATCH 0898/5780] Work around element size rounding issue on OS X Also fixes a problem where individual gutter backgrounds would not fill the space next to the horiz scrollbar. --- lib/codemirror.css | 2 ++ 1 file changed, 2 insertions(+) diff --git a/lib/codemirror.css b/lib/codemirror.css index 411166bc72..4e126154f6 100644 --- a/lib/codemirror.css +++ b/lib/codemirror.css @@ -152,6 +152,8 @@ div.CodeMirror span.CodeMirror-nonmatchingbracket {color: #f22;} } .CodeMirror-gutter { height: 100%; + padding-bottom: 30px; + margin-bottom: -32px; display: inline-block; /* Hack to make IE7 behave */ *zoom:1; From bac7936e9e26c4f1cb26cd99e3a680555a32338c Mon Sep 17 00:00:00 2001 From: Marijn Haverbeke Date: Wed, 20 Mar 2013 11:41:01 +0100 Subject: [PATCH 0899/5780] Drop the formatting addon It's flaky, and I don't want to support it. Everybody: feel free to take the code, put it in some other repo and maintain it. --- addon/format/formatting.js | 114 ------------------------------------- demo/formatting.html | 89 ----------------------------- doc/compress.html | 1 - doc/manual.html | 9 ++- 4 files changed, 4 insertions(+), 209 deletions(-) delete mode 100644 addon/format/formatting.js delete mode 100644 demo/formatting.html diff --git a/addon/format/formatting.js b/addon/format/formatting.js deleted file mode 100644 index 88b84307f6..0000000000 --- a/addon/format/formatting.js +++ /dev/null @@ -1,114 +0,0 @@ -(function() { - - CodeMirror.extendMode("css", { - commentStart: "/*", - commentEnd: "*/", - newlineAfterToken: function(_type, content) { - return /^[;{}]$/.test(content); - } - }); - - CodeMirror.extendMode("javascript", { - commentStart: "/*", - commentEnd: "*/", - // FIXME semicolons inside of for - newlineAfterToken: function(_type, content, textAfter, state) { - if (this.jsonMode) { - return /^[\[,{]$/.test(content) || /^}/.test(textAfter); - } else { - if (content == ";" && state.lexical && state.lexical.type == ")") return false; - return /^[;{}]$/.test(content) && !/^;/.test(textAfter); - } - } - }); - - var inlineElements = /^(a|abbr|acronym|area|base|bdo|big|br|button|caption|cite|code|col|colgroup|dd|del|dfn|em|frame|hr|iframe|img|input|ins|kbd|label|legend|link|map|object|optgroup|option|param|q|samp|script|select|small|span|strong|sub|sup|textarea|tt|var)$/; - - CodeMirror.extendMode("xml", { - commentStart: "", - newlineAfterToken: function(type, content, textAfter, state) { - var inline = false; - if (this.configuration == "html") - inline = state.context ? inlineElements.test(state.context.tagName) : false; - return !inline && ((type == "tag" && />$/.test(content) && state.context) || - /^ -1 && endIndex > -1 && endIndex > startIndex) { - // Take string till comment start - selText = selText.substr(0, startIndex) - // From comment start till comment end - + selText.substring(startIndex + curMode.commentStart.length, endIndex) - // From comment end till string end - + selText.substr(endIndex + curMode.commentEnd.length); - } - cm.replaceRange(selText, from, to); - } - }); - }); - - // Applies automatic mode-aware indentation to the specified range - CodeMirror.defineExtension("autoIndentRange", function (from, to) { - var cmInstance = this; - this.operation(function () { - for (var i = from.line; i <= to.line; i++) { - cmInstance.indentLine(i, "smart"); - } - }); - }); - - // Applies automatic formatting to the specified range - CodeMirror.defineExtension("autoFormatRange", function (from, to) { - var cm = this; - var outer = cm.getMode(), text = cm.getRange(from, to).split("\n"); - var state = CodeMirror.copyState(outer, cm.getTokenAt(from).state); - var tabSize = cm.getOption("tabSize"); - - var out = "", lines = 0, atSol = from.ch == 0; - function newline() { - out += "\n"; - atSol = true; - ++lines; - } - - for (var i = 0; i < text.length; ++i) { - var stream = new CodeMirror.StringStream(text[i], tabSize); - while (!stream.eol()) { - var inner = CodeMirror.innerMode(outer, state); - var style = outer.token(stream, state), cur = stream.current(); - stream.start = stream.pos; - if (!atSol || /\S/.test(cur)) { - out += cur; - atSol = false; - } - if (!atSol && inner.mode.newlineAfterToken && - inner.mode.newlineAfterToken(style, cur, stream.string.slice(stream.pos) || text[i+1] || "", inner.state)) - newline(); - } - if (!stream.pos && outer.blankLine) outer.blankLine(state); - if (!atSol && i < text.length - 1) newline(); - } - - cm.operation(function () { - cm.replaceRange(out, from, to); - for (var cur = from.line + 1, end = from.line + lines; cur <= end; ++cur) - cm.indentLine(cur, "smart"); - cm.setSelection(from, cm.getCursor(false)); - }); - }); -})(); diff --git a/demo/formatting.html b/demo/formatting.html deleted file mode 100644 index 6a05794b20..0000000000 --- a/demo/formatting.html +++ /dev/null @@ -1,89 +0,0 @@ - - - - - CodeMirror: Formatting Demo - - - - - - - - - - - - -

    CodeMirror: Formatting demo

    - -

    Note: The formatting addon receives a fair - amount of bug requests. I (the maintainer of CodeMirror) do not - intend to spend time on improving it. Pull requests (if clean and - intelligent) are welcome, but you should see this code as a - proof-of-concept (using CodeMirror's mode tokenizers to help - format code), not a finished, robust module.

    - -
    - -

    Select a piece of code and click one of the links below to apply automatic formatting to the selected text or comment/uncomment the selected text. Note that the formatting behavior depends on the current block's mode. - - - - - - -
    - - Autoformat Selected - - - - Comment Selected - - - - Uncomment Selected - -
    -

    - - - - diff --git a/doc/compress.html b/doc/compress.html index 71ae09d149..fc6b225a90 100644 --- a/doc/compress.html +++ b/doc/compress.html @@ -142,7 +142,6 @@

    { } CodeMi - diff --git a/doc/manual.html b/doc/manual.html index d8cd2db783..2f2d064a9e 100644 --- a/doc/manual.html +++ b/doc/manual.html @@ -1761,11 +1761,10 @@

    Writing CodeMirror Modes

    extra methods, innerMode which, given a state object, returns a {state, mode} object with the inner mode and its state for the current position. These are used by utility - scripts such as the
    autoformatter - and the tag closer to get context - information. Use the CodeMirror.innerMode helper - function to, starting from a mode and a state, recursively walk - down to the innermost mode and state.

    + scripts such as the tag closer to + get context information. Use the CodeMirror.innerMode + helper function to, starting from a mode and a state, recursively + walk down to the innermost mode and state.

    To make indentation work properly in a nested parser, it is advisable to give the startState method of modes that From 478119ff5f61f06387cd1e477408009aa4c35de2 Mon Sep 17 00:00:00 2001 From: Marijn Haverbeke Date: Wed, 20 Mar 2013 11:42:44 +0100 Subject: [PATCH 0900/5780] Remove mysql and plsql modes Use the generic sql mode, which has MIME types for these dialects. --- doc/compress.html | 2 - mode/meta.js | 2 - mode/mysql/index.html | 43 --------- mode/mysql/mysql.js | 203 --------------------------------------- mode/plsql/index.html | 64 ------------- mode/plsql/plsql.js | 216 ------------------------------------------ 6 files changed, 530 deletions(-) delete mode 100644 mode/mysql/index.html delete mode 100644 mode/mysql/mysql.js delete mode 100644 mode/plsql/index.html delete mode 100644 mode/plsql/plsql.js diff --git a/doc/compress.html b/doc/compress.html index fc6b225a90..aefa37aa4a 100644 --- a/doc/compress.html +++ b/doc/compress.html @@ -93,14 +93,12 @@

    { } CodeMi - - diff --git a/mode/meta.js b/mode/meta.js index 7ca7333b2c..e01ea30d40 100644 --- a/mode/meta.js +++ b/mode/meta.js @@ -56,9 +56,7 @@ CodeMirror.modeInfo = [ {name: 'Smarty', mime: 'text/x-smarty', mode: 'smarty'}, {name: 'SPARQL', mime: 'application/x-sparql-query', mode: 'sparql'}, {name: 'SQL', mime: 'text/x-sql', mode: 'sql'}, - {name: 'MySQL', mime: 'text/x-mysql', mode: 'sql'}, {name: 'MariaDB', mime: 'text/x-mariadb', mode: 'sql'}, - {name: 'PL/SQL', mime: 'text/x-plsql', mode: 'sql'}, {name: 'sTeX', mime: 'text/x-stex', mode: 'stex'}, {name: 'LaTeX', mime: 'text/x-latex', mode: 'stex'}, {name: 'Tcl', mime: 'text/x-tcl', mode: 'tcl'}, diff --git a/mode/mysql/index.html b/mode/mysql/index.html deleted file mode 100644 index 41aeed12d8..0000000000 --- a/mode/mysql/index.html +++ /dev/null @@ -1,43 +0,0 @@ - - - - - CodeMirror: MySQL mode - - - - - - - -

    CodeMirror: MySQL mode

    -
    - - -

    !! This mode is deprecated in favor of the generic SQL mode (which can be configured to handle MySQL).

    - -

    MIME types defined: text/x-mysql.

    - - - diff --git a/mode/mysql/mysql.js b/mode/mysql/mysql.js deleted file mode 100644 index 69d3f9952b..0000000000 --- a/mode/mysql/mysql.js +++ /dev/null @@ -1,203 +0,0 @@ -/* - * MySQL Mode for CodeMirror 2 by MySQL-Tools - * @author James Thorne (partydroid) - * @link http://github.com/partydroid/MySQL-Tools - * @link http://mysqltools.org - * @version 02/Jan/2012 -*/ -CodeMirror.defineMode("mysql", function(config) { - var indentUnit = config.indentUnit; - var curPunc; - - function wordRegexp(words) { - return new RegExp("^(?:" + words.join("|") + ")$", "i"); - } - var ops = wordRegexp(["str", "lang", "langmatches", "datatype", "bound", "sameterm", "isiri", "isuri", - "isblank", "isliteral", "union", "a"]); - var keywords = wordRegexp([ - ('ACCESSIBLE'),('ALTER'),('AS'),('BEFORE'),('BINARY'),('BY'),('CASE'),('CHARACTER'),('COLUMN'),('CONTINUE'),('CROSS'),('CURRENT_TIMESTAMP'),('DATABASE'),('DAY_MICROSECOND'),('DEC'),('DEFAULT'), - ('DESC'),('DISTINCT'),('DOUBLE'),('EACH'),('ENCLOSED'),('EXIT'),('FETCH'),('FLOAT8'),('FOREIGN'),('GRANT'),('HIGH_PRIORITY'),('HOUR_SECOND'),('IN'),('INNER'),('INSERT'),('INT2'),('INT8'), - ('INTO'),('JOIN'),('KILL'),('LEFT'),('LINEAR'),('LOCALTIME'),('LONG'),('LOOP'),('MATCH'),('MEDIUMTEXT'),('MINUTE_SECOND'),('NATURAL'),('NULL'),('OPTIMIZE'),('OR'),('OUTER'),('PRIMARY'), - ('RANGE'),('READ_WRITE'),('REGEXP'),('REPEAT'),('RESTRICT'),('RIGHT'),('SCHEMAS'),('SENSITIVE'),('SHOW'),('SPECIFIC'),('SQLSTATE'),('SQL_CALC_FOUND_ROWS'),('STARTING'),('TERMINATED'), - ('TINYINT'),('TRAILING'),('UNDO'),('UNLOCK'),('USAGE'),('UTC_DATE'),('VALUES'),('VARCHARACTER'),('WHERE'),('WRITE'),('ZEROFILL'),('ALL'),('AND'),('ASENSITIVE'),('BIGINT'),('BOTH'),('CASCADE'), - ('CHAR'),('COLLATE'),('CONSTRAINT'),('CREATE'),('CURRENT_TIME'),('CURSOR'),('DAY_HOUR'),('DAY_SECOND'),('DECLARE'),('DELETE'),('DETERMINISTIC'),('DIV'),('DUAL'),('ELSEIF'),('EXISTS'),('FALSE'), - ('FLOAT4'),('FORCE'),('FULLTEXT'),('HAVING'),('HOUR_MINUTE'),('IGNORE'),('INFILE'),('INSENSITIVE'),('INT1'),('INT4'),('INTERVAL'),('ITERATE'),('KEYS'),('LEAVE'),('LIMIT'),('LOAD'),('LOCK'), - ('LONGTEXT'),('MASTER_SSL_VERIFY_SERVER_CERT'),('MEDIUMINT'),('MINUTE_MICROSECOND'),('MODIFIES'),('NO_WRITE_TO_BINLOG'),('ON'),('OPTIONALLY'),('OUT'),('PRECISION'),('PURGE'),('READS'), - ('REFERENCES'),('RENAME'),('REQUIRE'),('REVOKE'),('SCHEMA'),('SELECT'),('SET'),('SPATIAL'),('SQLEXCEPTION'),('SQL_BIG_RESULT'),('SSL'),('TABLE'),('TINYBLOB'),('TO'),('TRUE'),('UNIQUE'), - ('UPDATE'),('USING'),('UTC_TIMESTAMP'),('VARCHAR'),('WHEN'),('WITH'),('YEAR_MONTH'),('ADD'),('ANALYZE'),('ASC'),('BETWEEN'),('BLOB'),('CALL'),('CHANGE'),('CHECK'),('CONDITION'),('CONVERT'), - ('CURRENT_DATE'),('CURRENT_USER'),('DATABASES'),('DAY_MINUTE'),('DECIMAL'),('DELAYED'),('DESCRIBE'),('DISTINCTROW'),('DROP'),('ELSE'),('ESCAPED'),('EXPLAIN'),('FLOAT'),('FOR'),('FROM'), - ('GROUP'),('HOUR_MICROSECOND'),('IF'),('INDEX'),('INOUT'),('INT'),('INT3'),('INTEGER'),('IS'),('KEY'),('LEADING'),('LIKE'),('LINES'),('LOCALTIMESTAMP'),('LONGBLOB'),('LOW_PRIORITY'), - ('MEDIUMBLOB'),('MIDDLEINT'),('MOD'),('NOT'),('NUMERIC'),('OPTION'),('ORDER'),('OUTFILE'),('PROCEDURE'),('READ'),('REAL'),('RELEASE'),('REPLACE'),('RETURN'),('RLIKE'),('SECOND_MICROSECOND'), - ('SEPARATOR'),('SMALLINT'),('SQL'),('SQLWARNING'),('SQL_SMALL_RESULT'),('STRAIGHT_JOIN'),('THEN'),('TINYTEXT'),('TRIGGER'),('UNION'),('UNSIGNED'),('USE'),('UTC_TIME'),('VARBINARY'),('VARYING'), - ('WHILE'),('XOR'),('FULL'),('COLUMNS'),('MIN'),('MAX'),('STDEV'),('COUNT') - ]); - var operatorChars = /[*+\-<>=&|]/; - - function tokenBase(stream, state) { - var ch = stream.next(); - curPunc = null; - if (ch == "$" || ch == "?") { - stream.match(/^[\w\d]*/); - return "variable-2"; - } - else if (ch == "<" && !stream.match(/^[\s\u00a0=]/, false)) { - stream.match(/^[^\s\u00a0>]*>?/); - return "atom"; - } - else if (ch == "\"" || ch == "'") { - state.tokenize = tokenLiteral(ch); - return state.tokenize(stream, state); - } - else if (ch == "`") { - state.tokenize = tokenOpLiteral(ch); - return state.tokenize(stream, state); - } - else if (/[{}\(\),\.;\[\]]/.test(ch)) { - curPunc = ch; - return null; - } - else if (ch == "-" && stream.eat("-")) { - stream.skipToEnd(); - return "comment"; - } - else if (ch == "/" && stream.eat("*")) { - state.tokenize = tokenComment; - return state.tokenize(stream, state); - } - else if (operatorChars.test(ch)) { - stream.eatWhile(operatorChars); - return null; - } - else if (ch == ":") { - stream.eatWhile(/[\w\d\._\-]/); - return "atom"; - } - else { - stream.eatWhile(/[_\w\d]/); - if (stream.eat(":")) { - stream.eatWhile(/[\w\d_\-]/); - return "atom"; - } - var word = stream.current(); - if (ops.test(word)) - return null; - else if (keywords.test(word)) - return "keyword"; - else - return "variable"; - } - } - - function tokenLiteral(quote) { - return function(stream, state) { - var escaped = false, ch; - while ((ch = stream.next()) != null) { - if (ch == quote && !escaped) { - state.tokenize = tokenBase; - break; - } - escaped = !escaped && ch == "\\"; - } - return "string"; - }; - } - - function tokenOpLiteral(quote) { - return function(stream, state) { - var escaped = false, ch; - while ((ch = stream.next()) != null) { - if (ch == quote && !escaped) { - state.tokenize = tokenBase; - break; - } - escaped = !escaped && ch == "\\"; - } - return "variable-2"; - }; - } - - function tokenComment(stream, state) { - for (;;) { - if (stream.skipTo("*")) { - stream.next(); - if (stream.eat("/")) { - state.tokenize = tokenBase; - break; - } - } else { - stream.skipToEnd(); - break; - } - } - return "comment"; - } - - - function pushContext(state, type, col) { - state.context = {prev: state.context, indent: state.indent, col: col, type: type}; - } - function popContext(state) { - state.indent = state.context.indent; - state.context = state.context.prev; - } - - return { - startState: function() { - return {tokenize: tokenBase, - context: null, - indent: 0, - col: 0}; - }, - - token: function(stream, state) { - if (stream.sol()) { - if (state.context && state.context.align == null) state.context.align = false; - state.indent = stream.indentation(); - } - if (stream.eatSpace()) return null; - var style = state.tokenize(stream, state); - - if (style != "comment" && state.context && state.context.align == null && state.context.type != "pattern") { - state.context.align = true; - } - - if (curPunc == "(") pushContext(state, ")", stream.column()); - else if (curPunc == "[") pushContext(state, "]", stream.column()); - else if (curPunc == "{") pushContext(state, "}", stream.column()); - else if (/[\]\}\)]/.test(curPunc)) { - while (state.context && state.context.type == "pattern") popContext(state); - if (state.context && curPunc == state.context.type) popContext(state); - } - else if (curPunc == "." && state.context && state.context.type == "pattern") popContext(state); - else if (/atom|string|variable/.test(style) && state.context) { - if (/[\}\]]/.test(state.context.type)) - pushContext(state, "pattern", stream.column()); - else if (state.context.type == "pattern" && !state.context.align) { - state.context.align = true; - state.context.col = stream.column(); - } - } - - return style; - }, - - indent: function(state, textAfter) { - var firstChar = textAfter && textAfter.charAt(0); - var context = state.context; - if (/[\]\}]/.test(firstChar)) - while (context && context.type == "pattern") context = context.prev; - - var closing = context && firstChar == context.type; - if (!context) - return 0; - else if (context.type == "pattern") - return context.col; - else if (context.align) - return context.col + (closing ? 0 : 1); - else - return context.indent + (closing ? 0 : indentUnit); - } - }; -}); - -CodeMirror.defineMIME("text/x-mysql", "mysql"); diff --git a/mode/plsql/index.html b/mode/plsql/index.html deleted file mode 100644 index 52e60710d5..0000000000 --- a/mode/plsql/index.html +++ /dev/null @@ -1,64 +0,0 @@ - - - - - CodeMirror: Oracle PL/SQL mode - - - - - - - -

    CodeMirror: Oracle PL/SQL mode

    - -
    - - - -

    !! This mode is deprecated in favor of the generic SQL mode (which can be configured to handle PL/SQL).

    - -

    - Simple mode that handles Oracle PL/SQL language (and Oracle SQL, of course). -

    - -

    MIME type defined: text/x-plsql - (PLSQL code) - diff --git a/mode/plsql/plsql.js b/mode/plsql/plsql.js deleted file mode 100644 index df119baee1..0000000000 --- a/mode/plsql/plsql.js +++ /dev/null @@ -1,216 +0,0 @@ -CodeMirror.defineMode("plsql", function(_config, parserConfig) { - var keywords = parserConfig.keywords, - functions = parserConfig.functions, - types = parserConfig.types, - sqlplus = parserConfig.sqlplus, - multiLineStrings = parserConfig.multiLineStrings; - var isOperatorChar = /[+\-*&%=<>!?:\/|]/; - function chain(stream, state, f) { - state.tokenize = f; - return f(stream, state); - } - - var type; - function ret(tp, style) { - type = tp; - return style; - } - - function tokenBase(stream, state) { - var ch = stream.next(); - // start of string? - if (ch == '"' || ch == "'") - return chain(stream, state, tokenString(ch)); - // is it one of the special signs []{}().,;? Seperator? - else if (/[\[\]{}\(\),;\.]/.test(ch)) - return ret(ch); - // start of a number value? - else if (/\d/.test(ch)) { - stream.eatWhile(/[\w\.]/); - return ret("number", "number"); - } - // multi line comment or simple operator? - else if (ch == "/") { - if (stream.eat("*")) { - return chain(stream, state, tokenComment); - } - else { - stream.eatWhile(isOperatorChar); - return ret("operator", "operator"); - } - } - // single line comment or simple operator? - else if (ch == "-") { - if (stream.eat("-")) { - stream.skipToEnd(); - return ret("comment", "comment"); - } - else { - stream.eatWhile(isOperatorChar); - return ret("operator", "operator"); - } - } - // pl/sql variable? - else if (ch == "@" || ch == "$") { - stream.eatWhile(/[\w\d\$_]/); - return ret("word", "variable"); - } - // is it a operator? - else if (isOperatorChar.test(ch)) { - stream.eatWhile(isOperatorChar); - return ret("operator", "operator"); - } - else { - // get the whole word - stream.eatWhile(/[\w\$_]/); - // is it one of the listed keywords? - if (keywords && keywords.propertyIsEnumerable(stream.current().toLowerCase())) return ret("keyword", "keyword"); - // is it one of the listed functions? - if (functions && functions.propertyIsEnumerable(stream.current().toLowerCase())) return ret("keyword", "builtin"); - // is it one of the listed types? - if (types && types.propertyIsEnumerable(stream.current().toLowerCase())) return ret("keyword", "variable-2"); - // is it one of the listed sqlplus keywords? - if (sqlplus && sqlplus.propertyIsEnumerable(stream.current().toLowerCase())) return ret("keyword", "variable-3"); - // default: just a "variable" - return ret("word", "variable"); - } - } - - function tokenString(quote) { - return function(stream, state) { - var escaped = false, next, end = false; - while ((next = stream.next()) != null) { - if (next == quote && !escaped) {end = true; break;} - escaped = !escaped && next == "\\"; - } - if (end || !(escaped || multiLineStrings)) - state.tokenize = tokenBase; - return ret("string", "plsql-string"); - }; - } - - function tokenComment(stream, state) { - var maybeEnd = false, ch; - while (ch = stream.next()) { - if (ch == "/" && maybeEnd) { - state.tokenize = tokenBase; - break; - } - maybeEnd = (ch == "*"); - } - return ret("comment", "plsql-comment"); - } - - // Interface - - return { - startState: function() { - return { - tokenize: tokenBase, - startOfLine: true - }; - }, - - token: function(stream, state) { - if (stream.eatSpace()) return null; - var style = state.tokenize(stream, state); - return style; - } - }; -}); - -(function() { - function keywords(str) { - var obj = {}, words = str.split(" "); - for (var i = 0; i < words.length; ++i) obj[words[i]] = true; - return obj; - } - var cKeywords = "abort accept access add all alter and any array arraylen as asc assert assign at attributes audit " + - "authorization avg " + - "base_table begin between binary_integer body boolean by " + - "case cast char char_base check close cluster clusters colauth column comment commit compress connect " + - "connected constant constraint crash create current currval cursor " + - "data_base database date dba deallocate debugoff debugon decimal declare default definition delay delete " + - "desc digits dispose distinct do drop " + - "else elsif enable end entry escape exception exception_init exchange exclusive exists exit external " + - "fast fetch file for force form from function " + - "generic goto grant group " + - "having " + - "identified if immediate in increment index indexes indicator initial initrans insert interface intersect " + - "into is " + - "key " + - "level library like limited local lock log logging long loop " + - "master maxextents maxtrans member minextents minus mislabel mode modify multiset " + - "new next no noaudit nocompress nologging noparallel not nowait number_base " + - "object of off offline on online only open option or order out " + - "package parallel partition pctfree pctincrease pctused pls_integer positive positiven pragma primary prior " + - "private privileges procedure public " + - "raise range raw read rebuild record ref references refresh release rename replace resource restrict return " + - "returning reverse revoke rollback row rowid rowlabel rownum rows run " + - "savepoint schema segment select separate session set share snapshot some space split sql start statement " + - "storage subtype successful synonym " + - "tabauth table tables tablespace task terminate then to trigger truncate type " + - "union unique unlimited unrecoverable unusable update use using " + - "validate value values variable view views " + - "when whenever where while with work"; - - var cFunctions = "abs acos add_months ascii asin atan atan2 average " + - "bfilename " + - "ceil chartorowid chr concat convert cos cosh count " + - "decode deref dual dump dup_val_on_index " + - "empty error exp " + - "false floor found " + - "glb greatest " + - "hextoraw " + - "initcap instr instrb isopen " + - "last_day least lenght lenghtb ln lower lpad ltrim lub " + - "make_ref max min mod months_between " + - "new_time next_day nextval nls_charset_decl_len nls_charset_id nls_charset_name nls_initcap nls_lower " + - "nls_sort nls_upper nlssort no_data_found notfound null nvl " + - "others " + - "power " + - "rawtohex reftohex round rowcount rowidtochar rpad rtrim " + - "sign sin sinh soundex sqlcode sqlerrm sqrt stddev substr substrb sum sysdate " + - "tan tanh to_char to_date to_label to_multi_byte to_number to_single_byte translate true trunc " + - "uid upper user userenv " + - "variance vsize"; - - var cTypes = "bfile blob " + - "character clob " + - "dec " + - "float " + - "int integer " + - "mlslabel " + - "natural naturaln nchar nclob number numeric nvarchar2 " + - "real rowtype " + - "signtype smallint string " + - "varchar varchar2"; - - var cSqlplus = "appinfo arraysize autocommit autoprint autorecovery autotrace " + - "blockterminator break btitle " + - "cmdsep colsep compatibility compute concat copycommit copytypecheck " + - "define describe " + - "echo editfile embedded escape exec execute " + - "feedback flagger flush " + - "heading headsep " + - "instance " + - "linesize lno loboffset logsource long longchunksize " + - "markup " + - "native newpage numformat numwidth " + - "pagesize pause pno " + - "recsep recsepchar release repfooter repheader " + - "serveroutput shiftinout show showmode size spool sqlblanklines sqlcase sqlcode sqlcontinue sqlnumber " + - "sqlpluscompatibility sqlprefix sqlprompt sqlterminator suffix " + - "tab term termout time timing trimout trimspool ttitle " + - "underline " + - "verify version " + - "wrap"; - - CodeMirror.defineMIME("text/x-plsql", { - name: "plsql", - keywords: keywords(cKeywords), - functions: keywords(cFunctions), - types: keywords(cTypes), - sqlplus: keywords(cSqlplus) - }); -}()); From 68b2994f012fbb15450b8dfd6bc3c061a42b3491 Mon Sep 17 00:00:00 2001 From: Marijn Haverbeke Date: Wed, 20 Mar 2013 11:47:49 +0100 Subject: [PATCH 0901/5780] Split folding functions into their own files --- addon/fold/brace-fold.js | 31 +++++++++++ addon/fold/foldcode.js | 109 -------------------------------------- addon/fold/indent-fold.js | 11 ++++ addon/fold/xml-fold.js | 64 ++++++++++++++++++++++ demo/folding.html | 4 +- doc/compress.html | 3 ++ doc/manual.html | 9 ++-- 7 files changed, 117 insertions(+), 114 deletions(-) create mode 100644 addon/fold/brace-fold.js create mode 100644 addon/fold/indent-fold.js create mode 100644 addon/fold/xml-fold.js diff --git a/addon/fold/brace-fold.js b/addon/fold/brace-fold.js new file mode 100644 index 0000000000..aad6e0141e --- /dev/null +++ b/addon/fold/brace-fold.js @@ -0,0 +1,31 @@ +CodeMirror.braceRangeFinder = function(cm, start) { + var line = start.line, lineText = cm.getLine(line); + var at = lineText.length, startChar, tokenType; + for (;;) { + var found = lineText.lastIndexOf("{", at); + if (found < start.ch) break; + tokenType = cm.getTokenAt(CodeMirror.Pos(line, found + 1)).type; + if (!/^(comment|string)/.test(tokenType)) { startChar = found; break; } + at = found - 1; + } + if (startChar == null || lineText.lastIndexOf("}") > startChar) return; + var count = 1, lastLine = cm.lineCount(), end, endCh; + outer: for (var i = line + 1; i < lastLine; ++i) { + var text = cm.getLine(i), pos = 0; + for (;;) { + var nextOpen = text.indexOf("{", pos), nextClose = text.indexOf("}", pos); + if (nextOpen < 0) nextOpen = text.length; + if (nextClose < 0) nextClose = text.length; + pos = Math.min(nextOpen, nextClose); + if (pos == text.length) break; + if (cm.getTokenAt(CodeMirror.Pos(i, pos + 1)).type == tokenType) { + if (pos == nextOpen) ++count; + else if (!--count) { end = i; endCh = pos; break outer; } + } + ++pos; + } + } + if (end == null || end == line + 1) return; + return {from: CodeMirror.Pos(line, startChar + 1), + to: CodeMirror.Pos(end, endCh)}; +}; diff --git a/addon/fold/foldcode.js b/addon/fold/foldcode.js index bdc641d3c6..b8b4b0da9e 100644 --- a/addon/fold/foldcode.js +++ b/addon/fold/foldcode.js @@ -1,112 +1,3 @@ -CodeMirror.tagRangeFinder = (function() { - var nameStartChar = "A-Z_a-z\\u00C0-\\u00D6\\u00D8-\\u00F6\\u00F8-\\u02FF\\u0370-\\u037D\\u037F-\\u1FFF\\u200C-\\u200D\\u2070-\\u218F\\u2C00-\\u2FEF\\u3001-\\uD7FF\\uF900-\\uFDCF\\uFDF0-\\uFFFD"; - var nameChar = nameStartChar + "\-\:\.0-9\\u00B7\\u0300-\\u036F\\u203F-\\u2040"; - var xmlTagStart = new RegExp("<(/?)([" + nameStartChar + "][" + nameChar + "]*)", "g"); - - return function(cm, start) { - var line = start.line, ch = start.ch, lineText = cm.getLine(line); - - function nextLine() { - if (line >= cm.lastLine()) return; - ch = 0; - lineText = cm.getLine(++line); - return true; - } - function toTagEnd() { - for (;;) { - var gt = lineText.indexOf(">", ch); - if (gt == -1) { if (nextLine()) continue; else return; } - var lastSlash = lineText.lastIndexOf("/", gt); - var selfClose = lastSlash > -1 && /^\s*$/.test(lineText.slice(lastSlash + 1, gt)); - ch = gt + 1; - return selfClose ? "selfClose" : "regular"; - } - } - function toNextTag() { - for (;;) { - xmlTagStart.lastIndex = ch; - var found = xmlTagStart.exec(lineText); - if (!found) { if (nextLine()) continue; else return; } - ch = found.index + found[0].length; - return found; - } - } - - var stack = [], startCh; - for (;;) { - var openTag = toNextTag(), end; - if (!openTag || line != start.line || !(end = toTagEnd())) return; - if (!openTag[1] && end != "selfClose") { - stack.push(openTag[2]); - startCh = ch; - break; - } - } - - for (;;) { - var next = toNextTag(), end, tagLine = line, tagCh = ch - (next ? next[0].length : 0); - if (!next || !(end = toTagEnd())) return; - if (end == "selfClose") continue; - if (next[1]) { // closing tag - for (var i = stack.length - 1; i >= 0; --i) if (stack[i] == next[2]) { - stack.length = i; - break; - } - if (!stack.length) return { - from: CodeMirror.Pos(start.line, startCh), - to: CodeMirror.Pos(tagLine, tagCh) - }; - } else { // opening tag - stack.push(next[2]); - } - } - }; -})(); - -CodeMirror.braceRangeFinder = function(cm, start) { - var line = start.line, lineText = cm.getLine(line); - var at = lineText.length, startChar, tokenType; - for (;;) { - var found = lineText.lastIndexOf("{", at); - if (found < start.ch) break; - tokenType = cm.getTokenAt(CodeMirror.Pos(line, found + 1)).type; - if (!/^(comment|string)/.test(tokenType)) { startChar = found; break; } - at = found - 1; - } - if (startChar == null || lineText.lastIndexOf("}") > startChar) return; - var count = 1, lastLine = cm.lineCount(), end, endCh; - outer: for (var i = line + 1; i < lastLine; ++i) { - var text = cm.getLine(i), pos = 0; - for (;;) { - var nextOpen = text.indexOf("{", pos), nextClose = text.indexOf("}", pos); - if (nextOpen < 0) nextOpen = text.length; - if (nextClose < 0) nextClose = text.length; - pos = Math.min(nextOpen, nextClose); - if (pos == text.length) break; - if (cm.getTokenAt(CodeMirror.Pos(i, pos + 1)).type == tokenType) { - if (pos == nextOpen) ++count; - else if (!--count) { end = i; endCh = pos; break outer; } - } - ++pos; - } - } - if (end == null || end == line + 1) return; - return {from: CodeMirror.Pos(line, startChar + 1), - to: CodeMirror.Pos(end, endCh)}; -}; - -CodeMirror.indentRangeFinder = function(cm, start) { - var tabSize = cm.getOption("tabSize"), firstLine = cm.getLine(start.line); - var myIndent = CodeMirror.countColumn(firstLine, null, tabSize); - for (var i = start.line + 1, end = cm.lineCount(); i < end; ++i) { - var curLine = cm.getLine(i); - if (CodeMirror.countColumn(curLine, null, tabSize) < myIndent && - CodeMirror.countColumn(cm.getLine(i-1), null, tabSize) > myIndent) - return {from: CodeMirror.Pos(start.line, firstLine.length), - to: CodeMirror.Pos(i, curLine.length)}; - } -}; - CodeMirror.newFoldFunction = function(rangeFinder, widget) { if (widget == null) widget = "\u2194"; if (typeof widget == "string") { diff --git a/addon/fold/indent-fold.js b/addon/fold/indent-fold.js new file mode 100644 index 0000000000..94a0a1ffae --- /dev/null +++ b/addon/fold/indent-fold.js @@ -0,0 +1,11 @@ +CodeMirror.indentRangeFinder = function(cm, start) { + var tabSize = cm.getOption("tabSize"), firstLine = cm.getLine(start.line); + var myIndent = CodeMirror.countColumn(firstLine, null, tabSize); + for (var i = start.line + 1, end = cm.lineCount(); i < end; ++i) { + var curLine = cm.getLine(i); + if (CodeMirror.countColumn(curLine, null, tabSize) < myIndent && + CodeMirror.countColumn(cm.getLine(i-1), null, tabSize) > myIndent) + return {from: CodeMirror.Pos(start.line, firstLine.length), + to: CodeMirror.Pos(i, curLine.length)}; + } +}; diff --git a/addon/fold/xml-fold.js b/addon/fold/xml-fold.js new file mode 100644 index 0000000000..79c524d485 --- /dev/null +++ b/addon/fold/xml-fold.js @@ -0,0 +1,64 @@ +CodeMirror.tagRangeFinder = (function() { + var nameStartChar = "A-Z_a-z\\u00C0-\\u00D6\\u00D8-\\u00F6\\u00F8-\\u02FF\\u0370-\\u037D\\u037F-\\u1FFF\\u200C-\\u200D\\u2070-\\u218F\\u2C00-\\u2FEF\\u3001-\\uD7FF\\uF900-\\uFDCF\\uFDF0-\\uFFFD"; + var nameChar = nameStartChar + "\-\:\.0-9\\u00B7\\u0300-\\u036F\\u203F-\\u2040"; + var xmlTagStart = new RegExp("<(/?)([" + nameStartChar + "][" + nameChar + "]*)", "g"); + + return function(cm, start) { + var line = start.line, ch = start.ch, lineText = cm.getLine(line); + + function nextLine() { + if (line >= cm.lastLine()) return; + ch = 0; + lineText = cm.getLine(++line); + return true; + } + function toTagEnd() { + for (;;) { + var gt = lineText.indexOf(">", ch); + if (gt == -1) { if (nextLine()) continue; else return; } + var lastSlash = lineText.lastIndexOf("/", gt); + var selfClose = lastSlash > -1 && /^\s*$/.test(lineText.slice(lastSlash + 1, gt)); + ch = gt + 1; + return selfClose ? "selfClose" : "regular"; + } + } + function toNextTag() { + for (;;) { + xmlTagStart.lastIndex = ch; + var found = xmlTagStart.exec(lineText); + if (!found) { if (nextLine()) continue; else return; } + ch = found.index + found[0].length; + return found; + } + } + + var stack = [], startCh; + for (;;) { + var openTag = toNextTag(), end; + if (!openTag || line != start.line || !(end = toTagEnd())) return; + if (!openTag[1] && end != "selfClose") { + stack.push(openTag[2]); + startCh = ch; + break; + } + } + + for (;;) { + var next = toNextTag(), end, tagLine = line, tagCh = ch - (next ? next[0].length : 0); + if (!next || !(end = toTagEnd())) return; + if (end == "selfClose") continue; + if (next[1]) { // closing tag + for (var i = stack.length - 1; i >= 0; --i) if (stack[i] == next[2]) { + stack.length = i; + break; + } + if (!stack.length) return { + from: CodeMirror.Pos(start.line, startCh), + to: CodeMirror.Pos(tagLine, tagCh) + }; + } else { // opening tag + stack.push(next[2]); + } + } + }; +})(); diff --git a/demo/folding.html b/demo/folding.html index f28e5af469..cd0417aac6 100644 --- a/demo/folding.html +++ b/demo/folding.html @@ -6,6 +6,8 @@ + + @@ -27,7 +29,7 @@

    CodeMirror: Code Folding Demo

    Demonstration of code folding using the code in foldcode.js. Press ctrl-q or click on the gutter to fold a block, again - to unfold.
    Try the Range Colapse demo as well.

    + to unfold.

    JavaScript:
    HTML:
    diff --git a/doc/compress.html b/doc/compress.html index aefa37aa4a..70f0ef0c45 100644 --- a/doc/compress.html +++ b/doc/compress.html @@ -139,6 +139,9 @@

    { } CodeMi + + + diff --git a/doc/manual.html b/doc/manual.html index 2f2d064a9e..819396f8e2 100644 --- a/doc/manual.html +++ b/doc/manual.html @@ -1454,10 +1454,11 @@

    Add-ons

    a CodeMirror instance and a line number, attempt to fold or unfold the block starting at the given line. A range-finder is a language-specific function that also takes an instance and a - line number, and returns an range to be folded, or null if - no block is started on that line. This file - provides CodeMirror.braceRangeFinder, which finds - blocks in brace languages (JavaScript, C, Java, + line number, and returns an range to be folded, or null if no + block is started on that line. There are files in + the
    addon/fold/ + directory providing CodeMirror.braceRangeFinder, + which finds blocks in brace languages (JavaScript, C, Java, etc), CodeMirror.indentRangeFinder, for languages where indentation determines block structure (Python, Haskell), and CodeMirror.tagRangeFinder, for XML-style From e72589ddff4028a07a2f1e6050f1695feafb820e Mon Sep 17 00:00:00 2001 From: Marijn Haverbeke Date: Wed, 20 Mar 2013 11:48:44 +0100 Subject: [PATCH 0902/5780] Remove the collapserange addon Too specialized. Out of scope. --- addon/fold/collapserange.js | 68 ----------------------- demo/collapserange.html | 104 ------------------------------------ doc/compress.html | 1 - doc/manual.html | 5 -- 4 files changed, 178 deletions(-) delete mode 100644 addon/fold/collapserange.js delete mode 100644 demo/collapserange.html diff --git a/addon/fold/collapserange.js b/addon/fold/collapserange.js deleted file mode 100644 index e2c013734d..0000000000 --- a/addon/fold/collapserange.js +++ /dev/null @@ -1,68 +0,0 @@ -(function() { - CodeMirror.defineOption("collapseRange", false, function(cm, val, old) { - var wasOn = old && old != CodeMirror.Init; - if (val && !wasOn) - enableRangeCollapsing(cm); - else if (!val && wasOn) - disableRangeCollapsing(cm); - }); - - var gutterClass = "CodeMirror-collapserange"; - - function enableRangeCollapsing(cm) { - cm.on("gutterClick", gutterClick); - cm.setOption("gutters", (cm.getOption("gutters") || []).concat([gutterClass])); - } - - function disableRangeCollapsing(cm) { - cm.rangeCollapseStart = null; - cm.off("gutterClick", gutterClick); - var gutters = cm.getOption("gutters"); - for (var i = 0; i < gutters.length && gutters[i] != gutterClass; ++i) {} - cm.setOption("gutters", gutters.slice(0, i).concat(gutters.slice(i + 1))); - } - - function gutterClick(cm, line, gutter) { - if (gutter != gutterClass) return; - - var start = cm.rangeCollapseStart; - if (start) { - var old = cm.getLineNumber(start); - cm.setGutterMarker(start, gutterClass, null); - cm.rangeCollapseStart = null; - var from = Math.min(old, line), to = Math.max(old, line); - if (from != to) { - // Finish this fold - var fold = cm.markText(CodeMirror.Pos(from + 1, 0), {line: to - 1}, { - collapsed: true, - inclusiveLeft: true, - inclusiveRight: true, - clearOnEnter: true - }); - var clear = function() { - cm.setGutterMarker(topLine, gutterClass, null); - cm.setGutterMarker(botLine, gutterClass, null); - fold.clear(); - }; - var topLine = cm.setGutterMarker(from, gutterClass, makeMarker(true, true, clear)); - var botLine = cm.setGutterMarker(to, gutterClass, makeMarker(false, true, clear)); - CodeMirror.on(fold, "clear", clear); - - return; - } - } - - // Start a new fold - cm.rangeCollapseStart = cm.setGutterMarker(line, gutterClass, makeMarker(true, false)); - } - - function makeMarker(isTop, isFinished, handler) { - var node = document.createElement("div"); - node.innerHTML = isTop ? "\u25bc" : "\u25b2"; - if (!isFinished) node.style.color = "red"; - node.style.fontSize = "85%"; - node.style.cursor = "pointer"; - if (handler) CodeMirror.on(node, "mousedown", handler); - return node; - } -})(); diff --git a/demo/collapserange.html b/demo/collapserange.html deleted file mode 100644 index 5a6df0d257..0000000000 --- a/demo/collapserange.html +++ /dev/null @@ -1,104 +0,0 @@ - - - - - CodeMirror: Range Collapsing Demo - - - - - - - - - -

    CodeMirror: Range Collapsing Demo

    - - - - - -

    Click on the right side of the gutter, then click again below, - the code between will collapse. Click on either arrow to expand. - To use, simply include the collapserange.js file and - set collapseRange: true in options.

    - - - diff --git a/doc/compress.html b/doc/compress.html index 70f0ef0c45..458365bf32 100644 --- a/doc/compress.html +++ b/doc/compress.html @@ -142,7 +142,6 @@

    { } CodeMi - diff --git a/doc/manual.html b/doc/manual.html index 819396f8e2..9fdd7c0a40 100644 --- a/doc/manual.html +++ b/doc/manual.html @@ -1463,11 +1463,6 @@

    Add-ons

    where indentation determines block structure (Python, Haskell), and CodeMirror.tagRangeFinder, for XML-style languages.

    -
    fold/collapserange.js
    -
    Another approach to - folding. See demo. - Allows the user to select a range to fold by clicking in the - gutter.
    runmode/runmode.js
    Can be used to run a CodeMirror mode over text without actually opening an editor instance. From 7658b31c14988264d6169d3dea66185132ae94ac Mon Sep 17 00:00:00 2001 From: Marijn Haverbeke Date: Wed, 20 Mar 2013 11:57:03 +0100 Subject: [PATCH 0903/5780] Remove simple-hint addon Use show-hint, which is much more solid and configurable. --- addon/hint/simple-hint.css | 16 ------ addon/hint/simple-hint.js | 102 ------------------------------------- doc/compress.html | 1 - 3 files changed, 119 deletions(-) delete mode 100644 addon/hint/simple-hint.css delete mode 100644 addon/hint/simple-hint.js diff --git a/addon/hint/simple-hint.css b/addon/hint/simple-hint.css deleted file mode 100644 index 4387cb9411..0000000000 --- a/addon/hint/simple-hint.css +++ /dev/null @@ -1,16 +0,0 @@ -.CodeMirror-completions { - position: absolute; - z-index: 10; - overflow: hidden; - -webkit-box-shadow: 2px 3px 5px rgba(0,0,0,.2); - -moz-box-shadow: 2px 3px 5px rgba(0,0,0,.2); - box-shadow: 2px 3px 5px rgba(0,0,0,.2); -} -.CodeMirror-completions select { - background: #fafafa; - outline: none; - border: none; - padding: 0; - margin: 0; - font-family: monospace; -} diff --git a/addon/hint/simple-hint.js b/addon/hint/simple-hint.js deleted file mode 100644 index 1565bd4785..0000000000 --- a/addon/hint/simple-hint.js +++ /dev/null @@ -1,102 +0,0 @@ -(function() { - CodeMirror.simpleHint = function(editor, getHints, givenOptions) { - // Determine effective options based on given values and defaults. - var options = {}, defaults = CodeMirror.simpleHint.defaults; - for (var opt in defaults) - if (defaults.hasOwnProperty(opt)) - options[opt] = (givenOptions && givenOptions.hasOwnProperty(opt) ? givenOptions : defaults)[opt]; - - function collectHints(previousToken) { - // We want a single cursor position. - if (editor.somethingSelected()) return; - - var tempToken = editor.getTokenAt(editor.getCursor()); - - // Don't show completions if token has changed and the option is set. - if (options.closeOnTokenChange && previousToken != null && - (tempToken.start != previousToken.start || tempToken.type != previousToken.type)) { - return; - } - - var result = getHints(editor, givenOptions); - if (!result || !result.list.length) return; - var completions = result.list; - function insert(str) { - editor.replaceRange(str, result.from, result.to); - } - // When there is only one completion, use it directly. - if (options.completeSingle && completions.length == 1) { - insert(completions[0]); - return true; - } - - // Build the select widget - var complete = document.createElement("div"); - complete.className = "CodeMirror-completions"; - var sel = complete.appendChild(document.createElement("select")); - // Opera doesn't move the selection when pressing up/down in a - // multi-select, but it does properly support the size property on - // single-selects, so no multi-select is necessary. - if (!window.opera) sel.multiple = true; - for (var i = 0; i < completions.length; ++i) { - var opt = sel.appendChild(document.createElement("option")); - opt.appendChild(document.createTextNode(completions[i])); - } - sel.firstChild.selected = true; - sel.size = Math.min(10, completions.length); - var pos = editor.cursorCoords(options.alignWithWord ? result.from : null); - complete.style.left = pos.left + "px"; - complete.style.top = pos.bottom + "px"; - document.body.appendChild(complete); - // If we're at the edge of the screen, then we want the menu to appear on the left of the cursor. - var winW = window.innerWidth || Math.max(document.body.offsetWidth, document.documentElement.offsetWidth); - if(winW - pos.left < sel.clientWidth) - complete.style.left = (pos.left - sel.clientWidth) + "px"; - // Hack to hide the scrollbar. - if (completions.length <= 10) - complete.style.width = (sel.clientWidth - 1) + "px"; - - var done = false; - function close() { - if (done) return; - done = true; - complete.parentNode.removeChild(complete); - } - function pick() { - insert(completions[sel.selectedIndex]); - close(); - setTimeout(function(){editor.focus();}, 50); - } - CodeMirror.on(sel, "blur", close); - CodeMirror.on(sel, "keydown", function(event) { - var code = event.keyCode; - // Enter - if (code == 13) {CodeMirror.e_stop(event); pick();} - // Escape - else if (code == 27) {CodeMirror.e_stop(event); close(); editor.focus();} - else if (code != 38 && code != 40 && code != 33 && code != 34 && !CodeMirror.isModifierKey(event)) { - close(); editor.focus(); - // Pass the event to the CodeMirror instance so that it can handle things like backspace properly. - editor.triggerOnKeyDown(event); - // Don't show completions if the code is backspace and the option is set. - if (!options.closeOnBackspace || code != 8) { - setTimeout(function(){collectHints(tempToken);}, 50); - } - } - }); - CodeMirror.on(sel, "dblclick", pick); - - sel.focus(); - // Opera sometimes ignores focusing a freshly created node - if (window.opera) setTimeout(function(){if (!done) sel.focus();}, 100); - return true; - } - return collectHints(); - }; - CodeMirror.simpleHint.defaults = { - closeOnBackspace: true, - closeOnTokenChange: false, - completeSingle: true, - alignWithWord: true - }; -})(); diff --git a/doc/compress.html b/doc/compress.html index 458365bf32..7a43622a20 100644 --- a/doc/compress.html +++ b/doc/compress.html @@ -142,7 +142,6 @@

    { } CodeMi - From e34c62a607e6fd28ae2f9976fc3ae718b567f021 Mon Sep 17 00:00:00 2001 From: Marijn Haverbeke Date: Wed, 20 Mar 2013 12:17:49 +0100 Subject: [PATCH 0904/5780] Mark release 3.11 --- doc/compress.html | 1 + doc/manual.html | 12 ++++++++++++ doc/oldrelease.html | 16 ++++++++++++++++ index.html | 41 +++++++++++++++++++++++++---------------- lib/codemirror.js | 2 +- package.json | 2 +- 6 files changed, 56 insertions(+), 18 deletions(-) diff --git a/doc/compress.html b/doc/compress.html index 7a43622a20..06ed4970ad 100644 --- a/doc/compress.html +++ b/doc/compress.html @@ -30,6 +30,7 @@

    { } CodeMi

    Version: + + + + +

    Handles AT&T assembler syntax (more specifically this handles + the GNU Assembler (gas) syntax.) + It takes a single optional configuration parameter: + architecture, which can be one of "ARM", + "ARMv6" or "x86". + Including the parameter adds syntax for the registers and special + directives for the supplied architecture. + +

    MIME types defined: text/x-gas

    + + diff --git a/mode/s/s.js b/mode/s/s.js new file mode 100644 index 0000000000..ae83c3efa0 --- /dev/null +++ b/mode/s/s.js @@ -0,0 +1,326 @@ +CodeMirror.defineMode("s", function(config, parserConfig) { + 'use strict'; + + // If an architecture is specified, its initialization function may + // populate this array with custom parsing functions which will be + // tried in the event that the standard functions do not find a match. + var custom = []; + + // The symbol used to start a line comment changes based on the target + // architecture. + // If no architecture is pased in "parserConfig" then only multiline + // comments will have syntax support. + var lineCommentStartSymbol = ""; + + // These directives are architecture independent. + // Machine specific directives should go in their respective + // architecture initialization function. + // Reference: + // http://sourceware.org/binutils/docs/as/Pseudo-Ops.html#Pseudo-Ops + var directives = { + ".abort" : "builtin", + ".align" : "builtin", + ".altmacro" : "builtin", + ".ascii" : "builtin", + ".asciz" : "builtin", + ".balign" : "builtin", + ".balignw" : "builtin", + ".balignl" : "builtin", + ".bundle_align_mode" : "builtin", + ".bundle_lock" : "builtin", + ".bundle_unlock" : "builtin", + ".byte" : "builtin", + ".cfi_startproc" : "builtin", + ".comm" : "builtin", + ".data" : "builtin", + ".def" : "builtin", + ".desc" : "builtin", + ".dim" : "builtin", + ".double" : "builtin", + ".eject" : "builtin", + ".else" : "builtin", + ".elseif" : "builtin", + ".end" : "builtin", + ".endef" : "builtin", + ".endfunc" : "builtin", + ".endif" : "builtin", + ".equ" : "builtin", + ".equiv" : "builtin", + ".eqv" : "builtin", + ".err" : "builtin", + ".error" : "builtin", + ".exitm" : "builtin", + ".extern" : "builtin", + ".fail" : "builtin", + ".file" : "builtin", + ".fill" : "builtin", + ".float" : "builtin", + ".func" : "builtin", + ".global" : "builtin", + ".gnu_attribute" : "builtin", + ".hidden" : "builtin", + ".hword" : "builtin", + ".ident" : "builtin", + ".if" : "builtin", + ".incbin" : "builtin", + ".include" : "builtin", + ".int" : "builtin", + ".internal" : "builtin", + ".irp" : "builtin", + ".irpc" : "builtin", + ".lcomm" : "builtin", + ".lflags" : "builtin", + ".line" : "builtin", + ".linkonce" : "builtin", + ".list" : "builtin", + ".ln" : "builtin", + ".loc" : "builtin", + ".loc_mark_labels" : "builtin", + ".local" : "builtin", + ".long" : "builtin", + ".macro" : "builtin", + ".mri" : "builtin", + ".noaltmacro" : "builtin", + ".nolist" : "builtin", + ".octa" : "builtin", + ".offset" : "builtin", + ".org" : "builtin", + ".p2align" : "builtin", + ".popsection" : "builtin", + ".previous" : "builtin", + ".print" : "builtin", + ".protected" : "builtin", + ".psize" : "builtin", + ".purgem" : "builtin", + ".pushsection" : "builtin", + ".quad" : "builtin", + ".reloc" : "builtin", + ".rept" : "builtin", + ".sbttl" : "builtin", + ".scl" : "builtin", + ".section" : "builtin", + ".set" : "builtin", + ".short" : "builtin", + ".single" : "builtin", + ".size" : "builtin", + ".skip" : "builtin", + ".sleb128" : "builtin", + ".space" : "builtin", + ".stab" : "builtin", + ".string" : "builtin", + ".struct" : "builtin", + ".subsection" : "builtin", + ".symver" : "builtin", + ".tag" : "builtin", + ".text" : "builtin", + ".title" : "builtin", + ".type" : "builtin", + ".uleb128" : "builtin", + ".val" : "builtin", + ".version" : "builtin", + ".vtable_entry" : "builtin", + ".vtable_inherit" : "builtin", + ".warning" : "builtin", + ".weak" : "builtin", + ".weakref" : "builtin", + ".word" : "builtin", + }; + + var registers = {}; + + function x86(config, parserConfig) { + lineCommentStartSymbol = "#"; + + registers.ax = "variable"; + registers.eax = "variable-2"; + registers.rax = "variable-3"; + + registers.bx = "variable"; + registers.ebx = "variable-2"; + registers.rbx = "variable-3"; + + registers.cx = "variable"; + registers.ecx = "variable-2"; + registers.rcx = "variable-3"; + + registers.dx = "variable"; + registers.edx = "variable-2"; + registers.rdx = "variable-3"; + + registers.si = "variable"; + registers.esi = "variable-2"; + registers.rsi = "variable-3"; + + registers.di = "variable"; + registers.edi = "variable-2"; + registers.rdi = "variable-3"; + + registers.sp = "variable"; + registers.esp = "variable-2"; + registers.rsp = "variable-3"; + + registers.bp = "variable"; + registers.ebp = "variable-2"; + registers.rbp = "variable-3"; + + registers.ip = "variable"; + registers.eip = "variable-2"; + registers.rip = "variable-3"; + + registers.cs = "keyword"; + registers.ds = "keyword"; + registers.ss = "keyword"; + registers.es = "keyword"; + registers.fs = "keyword"; + registers.gs = "keyword"; + } + + function armv6(config, parserConfig) { + // Reference: + // http://infocenter.arm.com/help/topic/com.arm.doc.qrc0001l/QRC0001_UAL.pdf + // http://infocenter.arm.com/help/topic/com.arm.doc.ddi0301h/DDI0301H_arm1176jzfs_r0p7_trm.pdf + lineCommentStartSymbol = "@"; + directives.syntax = "builtin"; + + registers.r0 = "variable"; + registers.r1 = "variable"; + registers.r2 = "variable"; + registers.r3 = "variable"; + registers.r4 = "variable"; + registers.r5 = "variable"; + registers.r6 = "variable"; + registers.r7 = "variable"; + registers.r8 = "variable"; + registers.r9 = "variable"; + registers.r10 = "variable"; + registers.r11 = "variable"; + registers.r12 = "variable"; + + registers.sp = "variable-2"; + registers.lr = "variable-2"; + registers.pc = "variable-2"; + registers.r13 = registers.sp; + registers.r14 = registers.lr; + registers.r15 = registers.pc; + + custom.push(function(ch, stream, state) { + if (ch === '#') { + stream.eatWhile(/\w/); + return "number"; + } + }); + } + + var arch = parserConfig.architecture.toLowerCase(); + if (arch === "x86") { + x86(config, parserConfig); + } else if (arch === "arm" || arch === "armv6") { + armv6(config, parserConfig); + } + + function nextUntilUnescaped(stream, end) { + var escaped = false, next; + while ((next = stream.next()) !== null) { + if (next === end && !escaped) { + return false; + } + escaped = !escaped && next === "\\"; + } + return escaped; + } + + function clikeComment(stream, state) { + var maybeEnd = false, ch; + while ((ch = stream.next()) !== null) { + if (ch === "/" && maybeEnd) { + state.tokenize = null; + break; + } + maybeEnd = (ch === "*"); + } + return "comment"; + } + + return { + startState: function(basecolumn) { + return { + tokenize: null, + }; + }, + + token: function(stream, state) { + if (state.tokenize) { + return state.tokenize(stream, state); + } + + if (stream.eatSpace()) { + return null; + } + + var style, cur, ch = stream.next(); + + if (ch === "/") { + if (stream.eat("*")) { + state.tokenize = clikeComment; + return clikeComment(stream, state); + } + } + + if (ch === lineCommentStartSymbol) { + stream.skipToEnd(); + return "comment"; + } + + if (ch === '"') { + nextUntilUnescaped(stream, '"'); + return "string"; + } + + if (ch === '.') { + stream.eatWhile(/\w/); + cur = stream.current().toLowerCase(); + style = directives[cur]; + return style || null; + } + + if (ch === '=') { + stream.eatWhile(/\w/); + return "tag"; + } + + if (ch === '{') { + return "braket"; + } + + if (ch === '}') { + return "braket"; + } + + if (/\d/.test(ch)) { + if (ch === "0" && stream.eat("x")) { + stream.eatWhile(/[0-9a-fA-F]/); + return "number"; + } + stream.eatWhile(/\d/); + return "number"; + } + + if (/\w/.test(ch)) { + stream.eatWhile(/\w/); + if (stream.eat(":")) { + return 'tag'; + } + cur = stream.current().toLowerCase(); + style = registers[cur]; + return style || null; + } + + for (var i = 0; i < custom.length; i++) { + style = custom[i](ch, stream, state); + if (style) { + return style; + } + } + }, + } +}); From 6de5ea6cd8c8986c6b1aa96e6291a3bf31d3ee78 Mon Sep 17 00:00:00 2001 From: Marijn Haverbeke Date: Mon, 25 Mar 2013 10:41:02 +0100 Subject: [PATCH 0918/5780] [gas mode] Fix overly precise comparisons --- mode/s/s.js | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/mode/s/s.js b/mode/s/s.js index ae83c3efa0..1edfb7acdc 100644 --- a/mode/s/s.js +++ b/mode/s/s.js @@ -220,7 +220,7 @@ CodeMirror.defineMode("s", function(config, parserConfig) { function nextUntilUnescaped(stream, end) { var escaped = false, next; - while ((next = stream.next()) !== null) { + while ((next = stream.next()) != null) { if (next === end && !escaped) { return false; } @@ -231,7 +231,7 @@ CodeMirror.defineMode("s", function(config, parserConfig) { function clikeComment(stream, state) { var maybeEnd = false, ch; - while ((ch = stream.next()) !== null) { + while ((ch = stream.next()) != null) { if (ch === "/" && maybeEnd) { state.tokenize = null; break; From eaea95295e24c6cc36983872e63ba04be1ec438e Mon Sep 17 00:00:00 2001 From: Marijn Haverbeke Date: Mon, 25 Mar 2013 10:41:21 +0100 Subject: [PATCH 0919/5780] [gas mode] Untabify --- mode/s/s.js | 640 ++++++++++++++++++++++++++-------------------------- 1 file changed, 320 insertions(+), 320 deletions(-) diff --git a/mode/s/s.js b/mode/s/s.js index 1edfb7acdc..4a5dc2972d 100644 --- a/mode/s/s.js +++ b/mode/s/s.js @@ -1,326 +1,326 @@ CodeMirror.defineMode("s", function(config, parserConfig) { - 'use strict'; - - // If an architecture is specified, its initialization function may - // populate this array with custom parsing functions which will be - // tried in the event that the standard functions do not find a match. - var custom = []; - - // The symbol used to start a line comment changes based on the target - // architecture. - // If no architecture is pased in "parserConfig" then only multiline - // comments will have syntax support. - var lineCommentStartSymbol = ""; - - // These directives are architecture independent. - // Machine specific directives should go in their respective - // architecture initialization function. - // Reference: - // http://sourceware.org/binutils/docs/as/Pseudo-Ops.html#Pseudo-Ops - var directives = { - ".abort" : "builtin", - ".align" : "builtin", - ".altmacro" : "builtin", - ".ascii" : "builtin", - ".asciz" : "builtin", - ".balign" : "builtin", - ".balignw" : "builtin", - ".balignl" : "builtin", - ".bundle_align_mode" : "builtin", - ".bundle_lock" : "builtin", - ".bundle_unlock" : "builtin", - ".byte" : "builtin", - ".cfi_startproc" : "builtin", - ".comm" : "builtin", - ".data" : "builtin", - ".def" : "builtin", - ".desc" : "builtin", - ".dim" : "builtin", - ".double" : "builtin", - ".eject" : "builtin", - ".else" : "builtin", - ".elseif" : "builtin", - ".end" : "builtin", - ".endef" : "builtin", - ".endfunc" : "builtin", - ".endif" : "builtin", - ".equ" : "builtin", - ".equiv" : "builtin", - ".eqv" : "builtin", - ".err" : "builtin", - ".error" : "builtin", - ".exitm" : "builtin", - ".extern" : "builtin", - ".fail" : "builtin", - ".file" : "builtin", - ".fill" : "builtin", - ".float" : "builtin", - ".func" : "builtin", - ".global" : "builtin", - ".gnu_attribute" : "builtin", - ".hidden" : "builtin", - ".hword" : "builtin", - ".ident" : "builtin", - ".if" : "builtin", - ".incbin" : "builtin", - ".include" : "builtin", - ".int" : "builtin", - ".internal" : "builtin", - ".irp" : "builtin", - ".irpc" : "builtin", - ".lcomm" : "builtin", - ".lflags" : "builtin", - ".line" : "builtin", - ".linkonce" : "builtin", - ".list" : "builtin", - ".ln" : "builtin", - ".loc" : "builtin", - ".loc_mark_labels" : "builtin", - ".local" : "builtin", - ".long" : "builtin", - ".macro" : "builtin", - ".mri" : "builtin", - ".noaltmacro" : "builtin", - ".nolist" : "builtin", - ".octa" : "builtin", - ".offset" : "builtin", - ".org" : "builtin", - ".p2align" : "builtin", - ".popsection" : "builtin", - ".previous" : "builtin", - ".print" : "builtin", - ".protected" : "builtin", - ".psize" : "builtin", - ".purgem" : "builtin", - ".pushsection" : "builtin", - ".quad" : "builtin", - ".reloc" : "builtin", - ".rept" : "builtin", - ".sbttl" : "builtin", - ".scl" : "builtin", - ".section" : "builtin", - ".set" : "builtin", - ".short" : "builtin", - ".single" : "builtin", - ".size" : "builtin", - ".skip" : "builtin", - ".sleb128" : "builtin", - ".space" : "builtin", - ".stab" : "builtin", - ".string" : "builtin", - ".struct" : "builtin", - ".subsection" : "builtin", - ".symver" : "builtin", - ".tag" : "builtin", - ".text" : "builtin", - ".title" : "builtin", - ".type" : "builtin", - ".uleb128" : "builtin", - ".val" : "builtin", - ".version" : "builtin", - ".vtable_entry" : "builtin", - ".vtable_inherit" : "builtin", - ".warning" : "builtin", - ".weak" : "builtin", - ".weakref" : "builtin", - ".word" : "builtin", - }; - - var registers = {}; - - function x86(config, parserConfig) { - lineCommentStartSymbol = "#"; - - registers.ax = "variable"; - registers.eax = "variable-2"; - registers.rax = "variable-3"; - - registers.bx = "variable"; - registers.ebx = "variable-2"; - registers.rbx = "variable-3"; - - registers.cx = "variable"; - registers.ecx = "variable-2"; - registers.rcx = "variable-3"; - - registers.dx = "variable"; - registers.edx = "variable-2"; - registers.rdx = "variable-3"; - - registers.si = "variable"; - registers.esi = "variable-2"; - registers.rsi = "variable-3"; - - registers.di = "variable"; - registers.edi = "variable-2"; - registers.rdi = "variable-3"; - - registers.sp = "variable"; - registers.esp = "variable-2"; - registers.rsp = "variable-3"; - - registers.bp = "variable"; - registers.ebp = "variable-2"; - registers.rbp = "variable-3"; - - registers.ip = "variable"; - registers.eip = "variable-2"; - registers.rip = "variable-3"; - - registers.cs = "keyword"; - registers.ds = "keyword"; - registers.ss = "keyword"; - registers.es = "keyword"; - registers.fs = "keyword"; - registers.gs = "keyword"; + 'use strict'; + + // If an architecture is specified, its initialization function may + // populate this array with custom parsing functions which will be + // tried in the event that the standard functions do not find a match. + var custom = []; + + // The symbol used to start a line comment changes based on the target + // architecture. + // If no architecture is pased in "parserConfig" then only multiline + // comments will have syntax support. + var lineCommentStartSymbol = ""; + + // These directives are architecture independent. + // Machine specific directives should go in their respective + // architecture initialization function. + // Reference: + // http://sourceware.org/binutils/docs/as/Pseudo-Ops.html#Pseudo-Ops + var directives = { + ".abort" : "builtin", + ".align" : "builtin", + ".altmacro" : "builtin", + ".ascii" : "builtin", + ".asciz" : "builtin", + ".balign" : "builtin", + ".balignw" : "builtin", + ".balignl" : "builtin", + ".bundle_align_mode" : "builtin", + ".bundle_lock" : "builtin", + ".bundle_unlock" : "builtin", + ".byte" : "builtin", + ".cfi_startproc" : "builtin", + ".comm" : "builtin", + ".data" : "builtin", + ".def" : "builtin", + ".desc" : "builtin", + ".dim" : "builtin", + ".double" : "builtin", + ".eject" : "builtin", + ".else" : "builtin", + ".elseif" : "builtin", + ".end" : "builtin", + ".endef" : "builtin", + ".endfunc" : "builtin", + ".endif" : "builtin", + ".equ" : "builtin", + ".equiv" : "builtin", + ".eqv" : "builtin", + ".err" : "builtin", + ".error" : "builtin", + ".exitm" : "builtin", + ".extern" : "builtin", + ".fail" : "builtin", + ".file" : "builtin", + ".fill" : "builtin", + ".float" : "builtin", + ".func" : "builtin", + ".global" : "builtin", + ".gnu_attribute" : "builtin", + ".hidden" : "builtin", + ".hword" : "builtin", + ".ident" : "builtin", + ".if" : "builtin", + ".incbin" : "builtin", + ".include" : "builtin", + ".int" : "builtin", + ".internal" : "builtin", + ".irp" : "builtin", + ".irpc" : "builtin", + ".lcomm" : "builtin", + ".lflags" : "builtin", + ".line" : "builtin", + ".linkonce" : "builtin", + ".list" : "builtin", + ".ln" : "builtin", + ".loc" : "builtin", + ".loc_mark_labels" : "builtin", + ".local" : "builtin", + ".long" : "builtin", + ".macro" : "builtin", + ".mri" : "builtin", + ".noaltmacro" : "builtin", + ".nolist" : "builtin", + ".octa" : "builtin", + ".offset" : "builtin", + ".org" : "builtin", + ".p2align" : "builtin", + ".popsection" : "builtin", + ".previous" : "builtin", + ".print" : "builtin", + ".protected" : "builtin", + ".psize" : "builtin", + ".purgem" : "builtin", + ".pushsection" : "builtin", + ".quad" : "builtin", + ".reloc" : "builtin", + ".rept" : "builtin", + ".sbttl" : "builtin", + ".scl" : "builtin", + ".section" : "builtin", + ".set" : "builtin", + ".short" : "builtin", + ".single" : "builtin", + ".size" : "builtin", + ".skip" : "builtin", + ".sleb128" : "builtin", + ".space" : "builtin", + ".stab" : "builtin", + ".string" : "builtin", + ".struct" : "builtin", + ".subsection" : "builtin", + ".symver" : "builtin", + ".tag" : "builtin", + ".text" : "builtin", + ".title" : "builtin", + ".type" : "builtin", + ".uleb128" : "builtin", + ".val" : "builtin", + ".version" : "builtin", + ".vtable_entry" : "builtin", + ".vtable_inherit" : "builtin", + ".warning" : "builtin", + ".weak" : "builtin", + ".weakref" : "builtin", + ".word" : "builtin", + }; + + var registers = {}; + + function x86(config, parserConfig) { + lineCommentStartSymbol = "#"; + + registers.ax = "variable"; + registers.eax = "variable-2"; + registers.rax = "variable-3"; + + registers.bx = "variable"; + registers.ebx = "variable-2"; + registers.rbx = "variable-3"; + + registers.cx = "variable"; + registers.ecx = "variable-2"; + registers.rcx = "variable-3"; + + registers.dx = "variable"; + registers.edx = "variable-2"; + registers.rdx = "variable-3"; + + registers.si = "variable"; + registers.esi = "variable-2"; + registers.rsi = "variable-3"; + + registers.di = "variable"; + registers.edi = "variable-2"; + registers.rdi = "variable-3"; + + registers.sp = "variable"; + registers.esp = "variable-2"; + registers.rsp = "variable-3"; + + registers.bp = "variable"; + registers.ebp = "variable-2"; + registers.rbp = "variable-3"; + + registers.ip = "variable"; + registers.eip = "variable-2"; + registers.rip = "variable-3"; + + registers.cs = "keyword"; + registers.ds = "keyword"; + registers.ss = "keyword"; + registers.es = "keyword"; + registers.fs = "keyword"; + registers.gs = "keyword"; + } + + function armv6(config, parserConfig) { + // Reference: + // http://infocenter.arm.com/help/topic/com.arm.doc.qrc0001l/QRC0001_UAL.pdf + // http://infocenter.arm.com/help/topic/com.arm.doc.ddi0301h/DDI0301H_arm1176jzfs_r0p7_trm.pdf + lineCommentStartSymbol = "@"; + directives.syntax = "builtin"; + + registers.r0 = "variable"; + registers.r1 = "variable"; + registers.r2 = "variable"; + registers.r3 = "variable"; + registers.r4 = "variable"; + registers.r5 = "variable"; + registers.r6 = "variable"; + registers.r7 = "variable"; + registers.r8 = "variable"; + registers.r9 = "variable"; + registers.r10 = "variable"; + registers.r11 = "variable"; + registers.r12 = "variable"; + + registers.sp = "variable-2"; + registers.lr = "variable-2"; + registers.pc = "variable-2"; + registers.r13 = registers.sp; + registers.r14 = registers.lr; + registers.r15 = registers.pc; + + custom.push(function(ch, stream, state) { + if (ch === '#') { + stream.eatWhile(/\w/); + return "number"; + } + }); + } + + var arch = parserConfig.architecture.toLowerCase(); + if (arch === "x86") { + x86(config, parserConfig); + } else if (arch === "arm" || arch === "armv6") { + armv6(config, parserConfig); + } + + function nextUntilUnescaped(stream, end) { + var escaped = false, next; + while ((next = stream.next()) != null) { + if (next === end && !escaped) { + return false; + } + escaped = !escaped && next === "\\"; + } + return escaped; + } + + function clikeComment(stream, state) { + var maybeEnd = false, ch; + while ((ch = stream.next()) != null) { + if (ch === "/" && maybeEnd) { + state.tokenize = null; + break; + } + maybeEnd = (ch === "*"); + } + return "comment"; + } + + return { + startState: function(basecolumn) { + return { + tokenize: null, + }; + }, + + token: function(stream, state) { + if (state.tokenize) { + return state.tokenize(stream, state); + } + + if (stream.eatSpace()) { + return null; + } + + var style, cur, ch = stream.next(); + + if (ch === "/") { + if (stream.eat("*")) { + state.tokenize = clikeComment; + return clikeComment(stream, state); } - - function armv6(config, parserConfig) { - // Reference: - // http://infocenter.arm.com/help/topic/com.arm.doc.qrc0001l/QRC0001_UAL.pdf - // http://infocenter.arm.com/help/topic/com.arm.doc.ddi0301h/DDI0301H_arm1176jzfs_r0p7_trm.pdf - lineCommentStartSymbol = "@"; - directives.syntax = "builtin"; - - registers.r0 = "variable"; - registers.r1 = "variable"; - registers.r2 = "variable"; - registers.r3 = "variable"; - registers.r4 = "variable"; - registers.r5 = "variable"; - registers.r6 = "variable"; - registers.r7 = "variable"; - registers.r8 = "variable"; - registers.r9 = "variable"; - registers.r10 = "variable"; - registers.r11 = "variable"; - registers.r12 = "variable"; - - registers.sp = "variable-2"; - registers.lr = "variable-2"; - registers.pc = "variable-2"; - registers.r13 = registers.sp; - registers.r14 = registers.lr; - registers.r15 = registers.pc; - - custom.push(function(ch, stream, state) { - if (ch === '#') { - stream.eatWhile(/\w/); - return "number"; - } - }); - } - - var arch = parserConfig.architecture.toLowerCase(); - if (arch === "x86") { - x86(config, parserConfig); - } else if (arch === "arm" || arch === "armv6") { - armv6(config, parserConfig); + } + + if (ch === lineCommentStartSymbol) { + stream.skipToEnd(); + return "comment"; + } + + if (ch === '"') { + nextUntilUnescaped(stream, '"'); + return "string"; + } + + if (ch === '.') { + stream.eatWhile(/\w/); + cur = stream.current().toLowerCase(); + style = directives[cur]; + return style || null; + } + + if (ch === '=') { + stream.eatWhile(/\w/); + return "tag"; + } + + if (ch === '{') { + return "braket"; + } + + if (ch === '}') { + return "braket"; + } + + if (/\d/.test(ch)) { + if (ch === "0" && stream.eat("x")) { + stream.eatWhile(/[0-9a-fA-F]/); + return "number"; } - - function nextUntilUnescaped(stream, end) { - var escaped = false, next; - while ((next = stream.next()) != null) { - if (next === end && !escaped) { - return false; - } - escaped = !escaped && next === "\\"; - } - return escaped; + stream.eatWhile(/\d/); + return "number"; + } + + if (/\w/.test(ch)) { + stream.eatWhile(/\w/); + if (stream.eat(":")) { + return 'tag'; } - - function clikeComment(stream, state) { - var maybeEnd = false, ch; - while ((ch = stream.next()) != null) { - if (ch === "/" && maybeEnd) { - state.tokenize = null; - break; - } - maybeEnd = (ch === "*"); - } - return "comment"; - } - - return { - startState: function(basecolumn) { - return { - tokenize: null, - }; - }, - - token: function(stream, state) { - if (state.tokenize) { - return state.tokenize(stream, state); - } - - if (stream.eatSpace()) { - return null; - } - - var style, cur, ch = stream.next(); - - if (ch === "/") { - if (stream.eat("*")) { - state.tokenize = clikeComment; - return clikeComment(stream, state); - } - } - - if (ch === lineCommentStartSymbol) { - stream.skipToEnd(); - return "comment"; - } - - if (ch === '"') { - nextUntilUnescaped(stream, '"'); - return "string"; - } - - if (ch === '.') { - stream.eatWhile(/\w/); - cur = stream.current().toLowerCase(); - style = directives[cur]; - return style || null; - } - - if (ch === '=') { - stream.eatWhile(/\w/); - return "tag"; - } - - if (ch === '{') { - return "braket"; - } - - if (ch === '}') { - return "braket"; - } - - if (/\d/.test(ch)) { - if (ch === "0" && stream.eat("x")) { - stream.eatWhile(/[0-9a-fA-F]/); - return "number"; - } - stream.eatWhile(/\d/); - return "number"; - } - - if (/\w/.test(ch)) { - stream.eatWhile(/\w/); - if (stream.eat(":")) { - return 'tag'; - } - cur = stream.current().toLowerCase(); - style = registers[cur]; - return style || null; - } - - for (var i = 0; i < custom.length; i++) { - style = custom[i](ch, stream, state); - if (style) { - return style; - } - } - }, + cur = stream.current().toLowerCase(); + style = registers[cur]; + return style || null; + } + + for (var i = 0; i < custom.length; i++) { + style = custom[i](ch, stream, state); + if (style) { + return style; } + } + }, + } }); From db7666a1e2f82625e42e1fe9c10c2f510cf5bf5b Mon Sep 17 00:00:00 2001 From: Marijn Haverbeke Date: Mon, 25 Mar 2013 10:46:47 +0100 Subject: [PATCH 0920/5780] [gas mode] Integrate --- doc/compress.html | 1 + doc/modes.html | 1 + mode/{s/s.js => gas/gas.js} | 18 +++++++++--------- mode/{s => gas}/index.html | 4 ++-- mode/meta.js | 2 +- 5 files changed, 14 insertions(+), 12 deletions(-) rename mode/{s/s.js => gas/gas.js} (96%) rename mode/{s => gas}/index.html (93%) diff --git a/doc/compress.html b/doc/compress.html index 7f14fc78b2..a100f9b965 100644 --- a/doc/compress.html +++ b/doc/compress.html @@ -80,6 +80,7 @@

    { } CodeMi + diff --git a/doc/modes.html b/doc/modes.html index f8080a46e8..d016aca8ee 100644 --- a/doc/modes.html +++ b/doc/modes.html @@ -34,6 +34,7 @@

    { } CodeMi
  • diff
  • ECL
  • Erlang
  • +
  • Gas (AT&T-style assembly)
  • Go
  • Groovy
  • Haskell
  • diff --git a/mode/s/s.js b/mode/gas/gas.js similarity index 96% rename from mode/s/s.js rename to mode/gas/gas.js index 4a5dc2972d..52620c6f08 100644 --- a/mode/s/s.js +++ b/mode/gas/gas.js @@ -1,4 +1,4 @@ -CodeMirror.defineMode("s", function(config, parserConfig) { +CodeMirror.defineMode("gas", function(_config, parserConfig) { 'use strict'; // If an architecture is specified, its initialization function may @@ -123,12 +123,12 @@ CodeMirror.defineMode("s", function(config, parserConfig) { ".warning" : "builtin", ".weak" : "builtin", ".weakref" : "builtin", - ".word" : "builtin", + ".word" : "builtin" }; var registers = {}; - function x86(config, parserConfig) { + function x86(_config, _parserConfig) { lineCommentStartSymbol = "#"; registers.ax = "variable"; @@ -175,7 +175,7 @@ CodeMirror.defineMode("s", function(config, parserConfig) { registers.gs = "keyword"; } - function armv6(config, parserConfig) { + function armv6(_config, _parserConfig) { // Reference: // http://infocenter.arm.com/help/topic/com.arm.doc.qrc0001l/QRC0001_UAL.pdf // http://infocenter.arm.com/help/topic/com.arm.doc.ddi0301h/DDI0301H_arm1176jzfs_r0p7_trm.pdf @@ -203,7 +203,7 @@ CodeMirror.defineMode("s", function(config, parserConfig) { registers.r14 = registers.lr; registers.r15 = registers.pc; - custom.push(function(ch, stream, state) { + custom.push(function(ch, stream) { if (ch === '#') { stream.eatWhile(/\w/); return "number"; @@ -242,9 +242,9 @@ CodeMirror.defineMode("s", function(config, parserConfig) { } return { - startState: function(basecolumn) { + startState: function() { return { - tokenize: null, + tokenize: null }; }, @@ -321,6 +321,6 @@ CodeMirror.defineMode("s", function(config, parserConfig) { return style; } } - }, - } + } + }; }); diff --git a/mode/s/index.html b/mode/gas/index.html similarity index 93% rename from mode/s/index.html rename to mode/gas/index.html index 8c55cb155a..7684bc18df 100644 --- a/mode/s/index.html +++ b/mode/gas/index.html @@ -5,7 +5,7 @@ CodeMirror: Gas mode - + @@ -40,7 +40,7 @@

    CodeMirror: Gas mode

    diff --git a/mode/meta.js b/mode/meta.js index 4bc03e67a7..87000c4aa2 100644 --- a/mode/meta.js +++ b/mode/meta.js @@ -14,7 +14,7 @@ CodeMirror.modeInfo = [ {name: 'diff', mime: 'text/x-diff', mode: 'diff'}, {name: 'ECL', mime: 'text/x-ecl', mode: 'ecl'}, {name: 'Erlang', mime: 'text/x-erlang', mode: 'erlang'}, - {name: 'Gas', mime: 'text/x-gas', mode: 's'}, + {name: 'Gas', mime: 'text/x-gas', mode: 'gas'}, {name: 'GitHub Flavored Markdown', mode: 'gfm'}, {name: 'GO', mime: 'text/x-go', mode: 'go'}, {name: 'Groovy', mime: 'text/x-groovy', mode: 'groovy'}, From f365868e814fda6680ab630764b2bd89b74cc802 Mon Sep 17 00:00:00 2001 From: Ansel Santosa Date: Thu, 21 Mar 2013 11:01:29 -0700 Subject: [PATCH 0921/5780] Prevent crashing on IE when in iframes "Unspecified error" on document.activeElement when running in an iframe in IE. --- lib/codemirror.js | 7 +++++-- 1 file changed, 5 insertions(+), 2 deletions(-) diff --git a/lib/codemirror.js b/lib/codemirror.js index d06456bf91..2098f4adeb 100644 --- a/lib/codemirror.js +++ b/lib/codemirror.js @@ -484,11 +484,14 @@ window.CodeMirror = (function() { } intact.sort(function(a, b) {return a.from - b.from;}); - var focused = document.activeElement; + // Avoid crashing on IE's "unspecified error" when in iframes + try { + var focused = document.activeElement; + } catch(e) {} if (intactLines < (to - from) * .7) display.lineDiv.style.display = "none"; patchDisplay(cm, from, to, intact, positionsChangedFrom); display.lineDiv.style.display = ""; - if (document.activeElement != focused && focused.offsetHeight) focused.focus(); + if (focused && document.activeElement != focused && focused.offsetHeight) focused.focus(); var different = from != display.showingFrom || to != display.showingTo || display.lastSizeC != display.wrapper.clientHeight; From f12685dc6ec10b2c0f81f1a0f9061d9d04839d6d Mon Sep 17 00:00:00 2001 From: Marijn Haverbeke Date: Mon, 25 Mar 2013 10:54:50 +0100 Subject: [PATCH 0922/5780] Make text inside hidden textarea tiny To prevent it from pushing the scroll position of the wrapper. --- lib/codemirror.js | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/lib/codemirror.js b/lib/codemirror.js index 2098f4adeb..32067c1a11 100644 --- a/lib/codemirror.js +++ b/lib/codemirror.js @@ -92,7 +92,7 @@ window.CodeMirror = (function() { function makeDisplay(place, docStart) { var d = {}; - var input = d.input = elt("textarea", null, null, "position: absolute; padding: 0; width: 1px; height: 1em; outline: none;"); + var input = d.input = elt("textarea", null, null, "position: absolute; padding: 0; width: 1px; height: 1em; outline: none; font-size: 4px;"); if (webkit) input.style.width = "1000px"; else input.setAttribute("wrap", "off"); // if border: 0; -- iOS fails to open keyboard (issue #1287) @@ -742,7 +742,7 @@ window.CodeMirror = (function() { // Move the hidden textarea near the cursor to prevent scrolling artifacts var headPos = cursorCoords(cm, cm.doc.sel.head, "div"); var wrapOff = getRect(display.wrapper), lineOff = getRect(display.lineDiv); - display.inputDiv.style.top = Math.max(0, Math.min(display.wrapper.clientHeight - 20, + display.inputDiv.style.top = Math.max(0, Math.min(display.wrapper.clientHeight - 10, headPos.top + lineOff.top - wrapOff.top)) + "px"; display.inputDiv.style.left = Math.max(0, Math.min(display.wrapper.clientWidth - 10, headPos.left + lineOff.left - wrapOff.left)) + "px"; From d155c412be65acb31c6605f65b407f47408778ba Mon Sep 17 00:00:00 2001 From: Marijn Haverbeke Date: Mon, 25 Mar 2013 17:25:46 +0100 Subject: [PATCH 0923/5780] More careful checking of viewport coverage after updateDisplayInner Closes #1391 --- lib/codemirror.js | 31 ++++++++++++++++--------------- 1 file changed, 16 insertions(+), 15 deletions(-) diff --git a/lib/codemirror.js b/lib/codemirror.js index 32067c1a11..1de32f1468 100644 --- a/lib/codemirror.js +++ b/lib/codemirror.js @@ -396,15 +396,22 @@ window.CodeMirror = (function() { // DISPLAY DRAWING function updateDisplay(cm, changes, viewPort) { - var oldFrom = cm.display.showingFrom, oldTo = cm.display.showingTo; - var updated = updateDisplayInner(cm, changes, viewPort); - if (updated) { - signalLater(cm, "update", cm); - if (cm.display.showingFrom != oldFrom || cm.display.showingTo != oldTo) - signalLater(cm, "viewportChange", cm, cm.display.showingFrom, cm.display.showingTo); + var oldFrom = cm.display.showingFrom, oldTo = cm.display.showingTo, updated; + for (;;) { + var visible = visibleLines(cm.display, cm.doc, viewPort); + if (updateDisplayInner(cm, changes, visible)) { + updated = true; + signalLater(cm, "update", cm); + if (cm.display.showingFrom != oldFrom || cm.display.showingTo != oldTo) + signalLater(cm, "viewportChange", cm, cm.display.showingFrom, cm.display.showingTo); + } else break; + updateSelection(cm); + updateScrollbars(cm.display, cm.doc.height); + + var newVisible = visibleLines(cm.display, cm.doc, viewPort); + if (newVisible.from >= cm.display.showingFrom && newVisible.to <= cm.display.showingTo) + break; } - updateSelection(cm); - updateScrollbars(cm.display, cm.doc.height); return updated; } @@ -412,7 +419,7 @@ window.CodeMirror = (function() { // Uses a set of changes plus the current scroll position to // determine which DOM updates have to be made, and makes the // updates. - function updateDisplayInner(cm, changes, viewPort) { + function updateDisplayInner(cm, changes, visible) { var display = cm.display, doc = cm.doc; if (!display.wrapper.clientWidth) { display.showingFrom = display.showingTo = doc.first; @@ -420,10 +427,6 @@ window.CodeMirror = (function() { return; } - // Compute the new visible window - // If scrollTop is specified, use that to determine which lines - // to render instead of the current scrollbar position. - var visible = visibleLines(display, doc, viewPort); // Bail out if the visible area is already rendered and nothing changed. if (changes.length == 0 && visible.from > display.showingFrom && visible.to < display.showingTo) @@ -522,8 +525,6 @@ window.CodeMirror = (function() { } updateViewOffset(cm); - if (visibleLines(display, doc, viewPort).to > to) - updateDisplayInner(cm, [], viewPort); return true; } From 84b8fb36bfe2252cdc4412b49319580617b9dc2a Mon Sep 17 00:00:00 2001 From: Tarmil Date: Mon, 25 Mar 2013 17:07:21 +0100 Subject: [PATCH 0924/5780] Fix multiplex close on blank line. --- addon/mode/multiplex.js | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/addon/mode/multiplex.js b/addon/mode/multiplex.js index e77ff2a9c3..459a918ea5 100644 --- a/addon/mode/multiplex.js +++ b/addon/mode/multiplex.js @@ -81,7 +81,7 @@ CodeMirror.multiplexingMode = function(outer /*, others */) { state.inner = CodeMirror.startState(other.mode, mode.indent ? mode.indent(state.outer, "") : 0); } } - } else if (mode.close === "\n") { + } else if (state.innerActive.close === "\n") { state.innerActive = state.inner = null; } }, From 8197a87bb2c09c8b2e742306101d8f346aa921dd Mon Sep 17 00:00:00 2001 From: Marijn Haverbeke Date: Tue, 26 Mar 2013 12:02:27 +0100 Subject: [PATCH 0925/5780] [gas mode] Fix undefined var error --- lib/codemirror.js | 2 +- mode/gas/gas.js | 8 ++++---- 2 files changed, 5 insertions(+), 5 deletions(-) diff --git a/lib/codemirror.js b/lib/codemirror.js index 1de32f1468..4c3ff3a853 100644 --- a/lib/codemirror.js +++ b/lib/codemirror.js @@ -1067,7 +1067,7 @@ window.CodeMirror = (function() { function clearCaches(cm) { cm.display.measureLineCache.length = cm.display.measureLineCachePos = 0; cm.display.cachedCharWidth = cm.display.cachedTextHeight = null; - cm.display.maxLineChanged = true; + if (!cm.options.lineWrapping) cm.display.maxLineChanged = true; cm.display.lineNumChars = null; } diff --git a/mode/gas/gas.js b/mode/gas/gas.js index 52620c6f08..a03382b61f 100644 --- a/mode/gas/gas.js +++ b/mode/gas/gas.js @@ -128,7 +128,7 @@ CodeMirror.defineMode("gas", function(_config, parserConfig) { var registers = {}; - function x86(_config, _parserConfig) { + function x86(_parserConfig) { lineCommentStartSymbol = "#"; registers.ax = "variable"; @@ -175,7 +175,7 @@ CodeMirror.defineMode("gas", function(_config, parserConfig) { registers.gs = "keyword"; } - function armv6(_config, _parserConfig) { + function armv6(_parserConfig) { // Reference: // http://infocenter.arm.com/help/topic/com.arm.doc.qrc0001l/QRC0001_UAL.pdf // http://infocenter.arm.com/help/topic/com.arm.doc.ddi0301h/DDI0301H_arm1176jzfs_r0p7_trm.pdf @@ -213,9 +213,9 @@ CodeMirror.defineMode("gas", function(_config, parserConfig) { var arch = parserConfig.architecture.toLowerCase(); if (arch === "x86") { - x86(config, parserConfig); + x86(parserConfig); } else if (arch === "arm" || arch === "armv6") { - armv6(config, parserConfig); + armv6(parserConfig); } function nextUntilUnescaped(stream, end) { From 1b150c9a4fd607508f5c6aa5e301f4f3ab7b393b Mon Sep 17 00:00:00 2001 From: Marijn Haverbeke Date: Tue, 26 Mar 2013 17:08:31 +0100 Subject: [PATCH 0926/5780] Prevent bogus error messages when a mode throws an error --- lib/codemirror.js | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/lib/codemirror.js b/lib/codemirror.js index 4c3ff3a853..652f86c8ad 100644 --- a/lib/codemirror.js +++ b/lib/codemirror.js @@ -1279,7 +1279,7 @@ window.CodeMirror = (function() { cm.curOp = null; if (op.updateMaxLine) computeMaxLength(cm); - if (display.maxLineChanged && !cm.options.lineWrapping) { + if (display.maxLineChanged && !cm.options.lineWrapping && display.maxLine) { var width = measureLineWidth(cm, display.maxLine); display.sizer.style.minWidth = Math.max(0, width + 3 + scrollerCutOff) + "px"; display.maxLineChanged = false; From ca79fa2066b956bc290abfcee2be6224cbed9a5b Mon Sep 17 00:00:00 2001 From: Marijn Haverbeke Date: Wed, 27 Mar 2013 10:30:42 +0100 Subject: [PATCH 0927/5780] Add maxHighlightLength option --- doc/manual.html | 7 +++++++ lib/codemirror.js | 10 ++++++---- 2 files changed, 13 insertions(+), 4 deletions(-) diff --git a/doc/manual.html b/doc/manual.html index f1a157cf18..4a601f2709 100644 --- a/doc/manual.html +++ b/doc/manual.html @@ -326,6 +326,13 @@

    Configuration

    document looks. You can set this option to false to disable this behavior.

    +
    maxHighlightLength (number)
    +
    When highlighting long lines, in order to stay responsive, + the editor will give up and simply style the rest of the line as + plain text when it reaches a certain position. The default is + 10000. You can set this to Infinity to turn off + this behavior.
    +
    viewportMargin (integer)
    Specifies the amount of lines that are rendered above and below the part of the document that's currently scrolled into diff --git a/lib/codemirror.js b/lib/codemirror.js index 652f86c8ad..e298ce43ba 100644 --- a/lib/codemirror.js +++ b/lib/codemirror.js @@ -3087,6 +3087,7 @@ window.CodeMirror = (function() { option("pollInterval", 100); option("undoDepth", 40, function(cm, val){cm.doc.history.undoDepth = val;}); option("viewportMargin", 10, function(cm){cm.refresh();}, true); + option("maxHighlightLength", 10000, function(cm){loadMode(cm); cm.refresh();}, true); option("tabindex", null, function(cm, val) { cm.display.input.tabIndex = val || ""; @@ -3978,15 +3979,16 @@ window.CodeMirror = (function() { var flattenSpans = mode.flattenSpans; if (flattenSpans == null) flattenSpans = cm.options.flattenSpans; var curText = "", curStyle = null; - var stream = new StringStream(text, cm.options.tabSize); + var stream = new StringStream(text, cm.options.tabSize), style; if (text == "" && mode.blankLine) mode.blankLine(state); while (!stream.eol()) { - var style = mode.token(stream, state); - if (stream.pos > 5000) { + if (stream.pos > cm.options.maxHighlightLength) { flattenSpans = false; // Webkit seems to refuse to render text nodes longer than 57444 characters stream.pos = Math.min(text.length, stream.start + 50000); style = null; + } else { + style = mode.token(stream, state); } var substr = stream.current(); stream.start = stream.pos; @@ -4049,7 +4051,7 @@ window.CodeMirror = (function() { var mode = cm.doc.mode; var stream = new StringStream(line.text, cm.options.tabSize); if (line.text == "" && mode.blankLine) mode.blankLine(state); - while (!stream.eol() && stream.pos <= 5000) { + while (!stream.eol() && stream.pos <= cm.options.maxHighlightLength) { mode.token(stream, state); stream.start = stream.pos; } From 9e62ecad1520e91cbfa70c997066ce41ba0e0890 Mon Sep 17 00:00:00 2001 From: Marijn Haverbeke Date: Wed, 27 Mar 2013 12:58:20 +0100 Subject: [PATCH 0928/5780] Reset hidden textarea when swapping an editor's document --- lib/codemirror.js | 1 + 1 file changed, 1 insertion(+) diff --git a/lib/codemirror.js b/lib/codemirror.js index e298ce43ba..882b92b313 100644 --- a/lib/codemirror.js +++ b/lib/codemirror.js @@ -3000,6 +3000,7 @@ window.CodeMirror = (function() { old.cm = null; attachDoc(this, doc); clearCaches(this); + resetInput(this, true); updateScrollPos(this, doc.scrollLeft, doc.scrollTop); return old; }), From 72d639dcfedf19de97a26b0778977d28f980b81a Mon Sep 17 00:00:00 2001 From: Marijn Haverbeke Date: Wed, 27 Mar 2013 16:28:28 +0100 Subject: [PATCH 0929/5780] Add moveInputWithCursor option --- lib/codemirror.js | 17 +++++++++++------ 1 file changed, 11 insertions(+), 6 deletions(-) diff --git a/lib/codemirror.js b/lib/codemirror.js index 882b92b313..71febb999f 100644 --- a/lib/codemirror.js +++ b/lib/codemirror.js @@ -741,12 +741,14 @@ window.CodeMirror = (function() { display.selectionDiv.style.display = "none"; // Move the hidden textarea near the cursor to prevent scrolling artifacts - var headPos = cursorCoords(cm, cm.doc.sel.head, "div"); - var wrapOff = getRect(display.wrapper), lineOff = getRect(display.lineDiv); - display.inputDiv.style.top = Math.max(0, Math.min(display.wrapper.clientHeight - 10, - headPos.top + lineOff.top - wrapOff.top)) + "px"; - display.inputDiv.style.left = Math.max(0, Math.min(display.wrapper.clientWidth - 10, - headPos.left + lineOff.left - wrapOff.left)) + "px"; + if (cm.options.moveInputWithCursor) { + var headPos = cursorCoords(cm, cm.doc.sel.head, "div"); + var wrapOff = getRect(display.wrapper), lineOff = getRect(display.lineDiv); + display.inputDiv.style.top = Math.max(0, Math.min(display.wrapper.clientHeight - 10, + headPos.top + lineOff.top - wrapOff.top)) + "px"; + display.inputDiv.style.left = Math.max(0, Math.min(display.wrapper.clientWidth - 10, + headPos.left + lineOff.left - wrapOff.left)) + "px"; + } } // No selection, plain cursor @@ -3089,6 +3091,9 @@ window.CodeMirror = (function() { option("undoDepth", 40, function(cm, val){cm.doc.history.undoDepth = val;}); option("viewportMargin", 10, function(cm){cm.refresh();}, true); option("maxHighlightLength", 10000, function(cm){loadMode(cm); cm.refresh();}, true); + option("moveInputWithCursor", true, function(cm, val) { + if (!val) cm.display.inputDiv.style.top = cm.display.inputDiv.style.left = 0; + }); option("tabindex", null, function(cm, val) { cm.display.input.tabIndex = val || ""; From f4abf68f68f9f073d67c64c5db1205d73bef7476 Mon Sep 17 00:00:00 2001 From: Narciso Jaramillo Date: Wed, 27 Mar 2013 16:08:50 -0700 Subject: [PATCH 0930/5780] Use empty changes array when calling updateDisplayInner() subsequent times --- lib/codemirror.js | 1 + 1 file changed, 1 insertion(+) diff --git a/lib/codemirror.js b/lib/codemirror.js index 71febb999f..ead5afc66a 100644 --- a/lib/codemirror.js +++ b/lib/codemirror.js @@ -411,6 +411,7 @@ window.CodeMirror = (function() { var newVisible = visibleLines(cm.display, cm.doc, viewPort); if (newVisible.from >= cm.display.showingFrom && newVisible.to <= cm.display.showingTo) break; + changes = []; } return updated; From 85d19c4d89c87caab98cfb1001af8df76eb311a1 Mon Sep 17 00:00:00 2001 From: Marijn Haverbeke Date: Thu, 28 Mar 2013 13:43:18 +0100 Subject: [PATCH 0931/5780] Make scrollIntoView work properly when called inside an operation Closes #1398 --- lib/codemirror.js | 21 ++++++++++++++------- 1 file changed, 14 insertions(+), 7 deletions(-) diff --git a/lib/codemirror.js b/lib/codemirror.js index ead5afc66a..49447bf909 100644 --- a/lib/codemirror.js +++ b/lib/codemirror.js @@ -1306,6 +1306,8 @@ window.CodeMirror = (function() { display.scroller.scrollTop = display.scrollbarV.scrollTop = doc.scrollTop = newScrollPos.scrollTop; display.scroller.scrollLeft = display.scrollbarH.scrollLeft = doc.scrollLeft = newScrollPos.scrollLeft; alignHorizontally(cm); + if (op.scrollToPos) + scrollPosIntoView(cm, clipPos(cm.doc, op.scrollToPos), op.scrollToPosMargin); } else if (newScrollPos) { scrollCursorIntoView(cm); } @@ -2544,7 +2546,8 @@ window.CodeMirror = (function() { } function updateScrollPos(cm, left, top) { - cm.curOp.updateScrollPos = {scrollLeft: left, scrollTop: top}; + cm.curOp.updateScrollPos = {scrollLeft: left == null ? cm.doc.scrollLeft : left, + scrollTop: top == null ? cm.doc.scrollTop : top}; } function addToScrollPos(cm, left, top) { @@ -2968,15 +2971,19 @@ window.CodeMirror = (function() { clientHeight: scroller.clientHeight - co, clientWidth: scroller.clientWidth - co}; }, - scrollIntoView: function(pos, margin) { + scrollIntoView: operation(null, function(pos, margin) { if (typeof pos == "number") pos = Pos(pos, 0); + if (!margin) margin = 0; + var coords = pos; + if (!pos || pos.line != null) { - pos = pos ? clipPos(this.doc, pos) : this.doc.sel.head; - scrollPosIntoView(this, pos, margin); - } else { - scrollIntoView(this, pos.left, pos.top - margin, pos.right, pos.bottom + margin); + this.curOp.scrollToPos = pos ? clipPos(this.doc, pos) : this.doc.sel.head; + this.curOp.scrollToPosMargin = margin; + coords = cursorCoords(this, this.curOp.scrollToPos); } - }, + var sPos = calculateScrollPos(this, coords.left, coords.top - margin, coords.right, coords.bottom + margin); + updateScrollPos(this, sPos.scrollLeft, sPos.scrollTop); + }), setSize: function(width, height) { function interpret(val) { From 4c8daad391161f074ebb16923caf6d4ad4431a18 Mon Sep 17 00:00:00 2001 From: Marijn Haverbeke Date: Thu, 28 Mar 2013 13:45:56 +0100 Subject: [PATCH 0932/5780] Don't blink the cursor when the editor is not focused. Saves on layout computations. Closes #1044 --- lib/codemirror.js | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/lib/codemirror.js b/lib/codemirror.js index 49447bf909..bf464a6306 100644 --- a/lib/codemirror.js +++ b/lib/codemirror.js @@ -855,7 +855,7 @@ window.CodeMirror = (function() { var on = true; display.cursor.style.visibility = display.otherCursor.style.visibility = ""; display.blinker = setInterval(function() { - if (!display.cursor.offsetHeight) return; + if (!cm.state.focused) return; display.cursor.style.visibility = display.otherCursor.style.visibility = (on = !on) ? "" : "hidden"; }, cm.options.cursorBlinkRate); } From 8f8072a86323196631c1966bf11075824e6d714e Mon Sep 17 00:00:00 2001 From: Marijn Haverbeke Date: Thu, 28 Mar 2013 13:54:16 +0100 Subject: [PATCH 0933/5780] Don't run the cursor-blinking timeout at all when not focused --- lib/codemirror.js | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/lib/codemirror.js b/lib/codemirror.js index bf464a6306..c71c80b88f 100644 --- a/lib/codemirror.js +++ b/lib/codemirror.js @@ -850,12 +850,12 @@ window.CodeMirror = (function() { // Cursor-blinking function restartBlink(cm) { + if (!cm.state.focused) return; var display = cm.display; clearInterval(display.blinker); var on = true; display.cursor.style.visibility = display.otherCursor.style.visibility = ""; display.blinker = setInterval(function() { - if (!cm.state.focused) return; display.cursor.style.visibility = display.otherCursor.style.visibility = (on = !on) ? "" : "hidden"; }, cm.options.cursorBlinkRate); } From ea3b07f5d3a0dfadb7f97af614a5fe86afa80b50 Mon Sep 17 00:00:00 2001 From: Marijn Haverbeke Date: Thu, 28 Mar 2013 13:59:20 +0100 Subject: [PATCH 0934/5780] Add XQuery tester to real-world uses --- doc/realworld.html | 1 + 1 file changed, 1 insertion(+) diff --git a/doc/realworld.html b/doc/realworld.html index 4f0dec346d..d1769559f7 100644 --- a/doc/realworld.html +++ b/doc/realworld.html @@ -90,6 +90,7 @@

    { } CodeMi
  • WeScheme (learning tool)
  • WordPress plugin
  • XOSide (online editor)
  • +
  • XQuery tester
  • From 56def7dbd341a1ccfddf0573a77fe3602c7f8f81 Mon Sep 17 00:00:00 2001 From: Eric Allam Date: Mon, 11 Mar 2013 23:30:48 -0400 Subject: [PATCH 0935/5780] Don't treat whitespace as an error, instead just eat the whitespace (fixes #1331) --- mode/sass/sass.js | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/mode/sass/sass.js b/mode/sass/sass.js index 9aed75a1af..e5c82e4e1b 100644 --- a/mode/sass/sass.js +++ b/mode/sass/sass.js @@ -284,7 +284,7 @@ CodeMirror.defineMode("sass", function(config) { // If we haven't returned by now, we move 1 character // and return an error stream.next(); - return 'error'; + return null; }; var tokenLexer = function(stream, state) { @@ -302,7 +302,7 @@ CodeMirror.defineMode("sass", function(config) { indent(state); } - if (style !== "error"){ + if (style !== null){ var startOfToken = stream.pos - current.length; var withCurrentIndent = startOfToken + (config.indentUnit * state.indentCount); From 3989667893b5e4e8909f53a619ca5f616b8a054d Mon Sep 17 00:00:00 2001 From: Marijn Haverbeke Date: Tue, 2 Apr 2013 09:21:12 +0200 Subject: [PATCH 0936/5780] [javascript mode] Add call tracking, as used by Tern --- mode/javascript/javascript.js | 8 ++++++-- 1 file changed, 6 insertions(+), 2 deletions(-) diff --git a/mode/javascript/javascript.js b/mode/javascript/javascript.js index ff6bb5d803..84df5052fa 100644 --- a/mode/javascript/javascript.js +++ b/mode/javascript/javascript.js @@ -290,7 +290,7 @@ CodeMirror.defineMode("javascript", function(config, parserConfig) { return cont(expression); } if (type == ";") return; - if (type == "(") return cont(pushlex(")"), commasep(expression, ")"), poplex, maybeoperator); + if (type == "(") return cont(pushlex(")", "call"), commasep(expression, ")"), poplex, maybeoperator); if (type == ".") return cont(property, maybeoperator); if (type == "[") return cont(pushlex("]"), expression, expect("]"), poplex, maybeoperator); } @@ -318,7 +318,11 @@ CodeMirror.defineMode("javascript", function(config, parserConfig) { } function commasep(what, end) { function proceed(type) { - if (type == ",") return cont(what, proceed); + if (type == ",") { + var lex = cx.state.lexical; + if (lex.info == "call") lex.pos = (lex.pos || 0) + 1; + return cont(what, proceed); + } if (type == end) return cont(); return cont(expect(end)); } From 9ed1a68835adf2667a00b1963bc8c4a277a053d3 Mon Sep 17 00:00:00 2001 From: Mike Diaz Date: Thu, 28 Mar 2013 09:34:58 -0700 Subject: [PATCH 0937/5780] add option to disable lint tooltips --- addon/lint/lint.js | 21 +++++++++++++-------- 1 file changed, 13 insertions(+), 8 deletions(-) diff --git a/addon/lint/lint.js b/addon/lint/lint.js index dd838e7ea5..1fa5aa985e 100644 --- a/addon/lint/lint.js +++ b/addon/lint/lint.js @@ -50,7 +50,7 @@ CodeMirror.validate = (function() { state.marked.length = 0; } - function makeMarker(labels, severity, multiple) { + function makeMarker(labels, severity, multiple, tooltips) { var marker = document.createElement("div"), inner = marker; marker.className = "CodeMirror-lint-marker-" + severity; if (multiple) { @@ -58,9 +58,11 @@ CodeMirror.validate = (function() { inner.className = "CodeMirror-lint-marker-multiple"; } - var tooltip; - CodeMirror.on(inner, "mouseover", function(e) { tooltip = showTooltip(e, labels); }); - CodeMirror.on(inner, "mouseout", function() { if (tooltip) hideTooltip(tooltip); }); + if (tooltips != false) { + var tooltip; + CodeMirror.on(inner, "mouseover", function(e) { tooltip = showTooltip(e, labels); }); + CodeMirror.on(inner, "mouseout", function() { if (tooltip) hideTooltip(tooltip); }); + } return marker; } @@ -95,7 +97,7 @@ CodeMirror.validate = (function() { else updateLinting(cm, options.getAnnotations(cm.getValue())); } - + function updateLinting(cm, annotationsNotSorted) { clearMarks(cm); var state = cm._lintState, options = state.options; @@ -125,7 +127,8 @@ CodeMirror.validate = (function() { } if (state.hasGutter) - cm.setGutterMarker(line, GUTTER_ID, makeMarker(tipLabel, maxSeverity, anns.length > 1)); + cm.setGutterMarker(line, GUTTER_ID, makeMarker(tipLabel, maxSeverity, anns.length > 1, + state.options.tooltips)); } if (options.onUpdateLinting) options.onUpdateLinting(annotationsNotSorted, annotations, cm); } @@ -170,13 +173,15 @@ CodeMirror.validate = (function() { CodeMirror.off(cm.getWrapperElement(), "mouseover", cm._lintState.onMouseOver); delete cm._lintState; } - + if (val) { var gutters = cm.getOption("gutters"), hasLintGutter = false; for (var i = 0; i < gutters.length; ++i) if (gutters[i] == GUTTER_ID) hasLintGutter = true; var state = cm._lintState = new LintState(cm, parseOptions(val), hasLintGutter); cm.on("change", onChange); - CodeMirror.on(cm.getWrapperElement(), "mouseover", state.onMouseOver); + if (state.options.tooltips != false) + CodeMirror.on(cm.getWrapperElement(), "mouseover", state.onMouseOver); + startLinting(cm); } }); From ddaa7e8057a1a191d827a84e0be99ab7e99816d9 Mon Sep 17 00:00:00 2001 From: Marijn Haverbeke Date: Tue, 2 Apr 2013 11:13:24 +0200 Subject: [PATCH 0938/5780] [javascript mode] Support statementIndent option Closes #1403 --- mode/javascript/index.html | 12 ++++++++---- mode/javascript/javascript.js | 5 +++++ 2 files changed, 13 insertions(+), 4 deletions(-) diff --git a/mode/javascript/index.html b/mode/javascript/index.html index cca7d04eaf..dd0ca220d8 100644 --- a/mode/javascript/index.html +++ b/mode/javascript/index.html @@ -76,10 +76,14 @@

    CodeMirror: JavaScript mode

    JavaScript mode supports a two configuration options:
      -
    • json which will set the mode to expect JSON data rather than a JavaScript program.
    • -
    • - typescript which will activate additional syntax highlighting and some other things for TypeScript code (demo). -
    • +
    • json which will set the mode to expect JSON + data rather than a JavaScript program.
    • +
    • typescript which will activate additional + syntax highlighting and some other things for TypeScript code + (demo).
    • +
    • statementIndent which (given a number) will + determine the amount of indentation to use for statements + continued on a new line.

    diff --git a/mode/javascript/javascript.js b/mode/javascript/javascript.js index 84df5052fa..bfbf78378b 100644 --- a/mode/javascript/javascript.js +++ b/mode/javascript/javascript.js @@ -415,6 +415,11 @@ CodeMirror.defineMode("javascript", function(config, parserConfig) { var firstChar = textAfter && textAfter.charAt(0), lexical = state.lexical; if (lexical.type == "stat" && firstChar == "}") lexical = lexical.prev; var type = lexical.type, closing = firstChar == type; + if (parserConfig.statementIndent != null) { + if (type == ")" && lexical.prev && lexical.prev.type == "stat") lexical = lexical.prev; + if (lexical.type == "stat") return lexical.indented + parserConfig.statementIndent; + } + if (type == "vardef") return lexical.indented + (state.lastType == "operator" || state.lastType == "," ? 4 : 0); else if (type == "form" && firstChar == "{") return lexical.indented; else if (type == "form") return lexical.indented + indentUnit; From 6a33096e6021fa5c48abf2867e7b68569168e24a Mon Sep 17 00:00:00 2001 From: Marijn Haverbeke Date: Tue, 2 Apr 2013 11:30:57 +0200 Subject: [PATCH 0939/5780] Fix another case where updateDisplay might not update the entire visible area Closes #1404 --- lib/codemirror.js | 10 +++++++--- 1 file changed, 7 insertions(+), 3 deletions(-) diff --git a/lib/codemirror.js b/lib/codemirror.js index c71c80b88f..493bd71b08 100644 --- a/lib/codemirror.js +++ b/lib/codemirror.js @@ -397,8 +397,8 @@ window.CodeMirror = (function() { function updateDisplay(cm, changes, viewPort) { var oldFrom = cm.display.showingFrom, oldTo = cm.display.showingTo, updated; + var visible = visibleLines(cm.display, cm.doc, viewPort); for (;;) { - var visible = visibleLines(cm.display, cm.doc, viewPort); if (updateDisplayInner(cm, changes, visible)) { updated = true; signalLater(cm, "update", cm); @@ -408,8 +408,12 @@ window.CodeMirror = (function() { updateSelection(cm); updateScrollbars(cm.display, cm.doc.height); - var newVisible = visibleLines(cm.display, cm.doc, viewPort); - if (newVisible.from >= cm.display.showingFrom && newVisible.to <= cm.display.showingTo) + // Clip forced viewport to actual scrollable area + if (viewPort) + viewPort = Math.min(cm.display.scroller.scrollHeight - cm.display.scroller.clientHeight, + typeof viewPort == "number" ? viewPort : viewPort.top); + visible = visibleLines(cm.display, cm.doc, viewPort); + if (visible.from >= cm.display.showingFrom && visible.to <= cm.display.showingTo) break; changes = []; } From e7dda847b3a265c5778ca66308da468e307a52d9 Mon Sep 17 00:00:00 2001 From: Joseph Pecoraro Date: Thu, 28 Mar 2013 15:39:11 -0700 Subject: [PATCH 0940/5780] Remove tabs and trailing whitespace in a few modes --- mode/less/less.js | 46 +++++++++++++++++++++++----------------------- mode/sql/sql.js | 36 ++++++++++++++++++------------------ 2 files changed, 41 insertions(+), 41 deletions(-) diff --git a/mode/less/less.js b/mode/less/less.js index 70cd5c937b..4f6214363a 100644 --- a/mode/less/less.js +++ b/mode/less/less.js @@ -41,7 +41,7 @@ CodeMirror.defineMode("less", function(config) { }else{ if(type == "string" || type == "(")return ret("string", "string"); if(state.stack[state.stack.length-1] != undefined)return ret(null, ch); - stream.eatWhile(/[\a-zA-Z0-9\-_.\s]/); + stream.eatWhile(/[\a-zA-Z0-9\-_.\s]/); if( /\/|\)|#/.test(stream.peek() || (stream.eatSpace() && stream.peek() == ")")) || stream.eol() )return ret("string", "string"); // let url(/images/logo.png) without quotes return as string } } @@ -58,7 +58,7 @@ CodeMirror.defineMode("less", function(config) { return ret(null, "select-op"); } else if (/[;{}:\[\]()~\|]/.test(ch)) { - if(ch == ":"){ + if(ch == ":"){ stream.eatWhile(/[a-z\\\-]/); if( selectors.test(stream.current()) ){ return ret("tag", "tag"); @@ -69,7 +69,7 @@ CodeMirror.defineMode("less", function(config) { if( selectors.test(stream.current().substring(1)) )return ret("tag", "tag"); return ret(null, ch); }else{ - return ret(null, ch); + return ret(null, ch); } }else if(ch == "~"){ if(type == "r")return ret("string", "string"); @@ -77,7 +77,7 @@ CodeMirror.defineMode("less", function(config) { return ret(null, ch); } } - else if (ch == ".") { + else if (ch == ".") { if(type == "(" || type == "string")return ret("string", "string"); // allow url(../image.png) stream.eatWhile(/[\a-zA-Z0-9\-_]/); if(stream.peek() == " ")stream.eatSpace(); @@ -106,7 +106,7 @@ CodeMirror.defineMode("less", function(config) { else return ret("number", "unit"); }else{//when not a valid hexvalue in the current stream e.g. #footer stream.eatWhile(/[\w\\\-]/); - return ret("atom", "tag"); + return ret("atom", "tag"); } }else{//when not a valid hexvalue length stream.eatWhile(/[\w\\\-]/); @@ -126,14 +126,14 @@ CodeMirror.defineMode("less", function(config) { return ret("string", "string"); }else if(stream.peek() == "<" || stream.peek() == ">"){ return ret("tag", "tag"); - }else if( /\(/.test(stream.peek()) ){ + }else if( /\(/.test(stream.peek()) ){ return ret(null, ch); }else if (stream.peek() == "/" && state.stack[state.stack.length-1] != undefined){ // url(dir/center/image.png) return ret("string", "string"); }else if( stream.current().match(/\-\d|\-.\d/) ){ // match e.g.: -5px -0.4 etc... only colorize the minus sign //commment out these 2 comment if you want the minus sign to be parsed as null -500px //stream.backUp(stream.current().length-1); - //return ret(null, ch); //console.log( stream.current() ); + //return ret(null, ch); //console.log( stream.current() ); return ret("number", "unit"); }else if( inTagsArray(stream.current().toLowerCase()) ){ // match html tags return ret("tag", "tag"); @@ -156,22 +156,22 @@ CodeMirror.defineMode("less", function(config) { stream.next(); var t_v = stream.peek() == ":" ? true : false; if(!t_v){ - var old_pos = stream.pos; - var sc = stream.current().length; - stream.eatWhile(/[a-z\\\-]/); - var new_pos = stream.pos; - if(stream.current().substring(sc-1).match(selectors) != null){ - stream.backUp(new_pos-(old_pos-1)); - return ret("tag", "tag"); - } else stream.backUp(new_pos-(old_pos-1)); - }else{ - stream.backUp(1); - } - if(t_v)return ret("tag", "tag"); else return ret("variable", "variable"); - }else{ - return ret("variable", "variable"); + var old_pos = stream.pos; + var sc = stream.current().length; + stream.eatWhile(/[a-z\\\-]/); + var new_pos = stream.pos; + if(stream.current().substring(sc-1).match(selectors) != null){ + stream.backUp(new_pos-(old_pos-1)); + return ret("tag", "tag"); + } else stream.backUp(new_pos-(old_pos-1)); + }else{ + stream.backUp(1); + } + if(t_v)return ret("tag", "tag"); else return ret("variable", "variable"); + }else{ + return ret("variable", "variable"); } - } + } } function tokenSComment(stream, state) { // SComment = Slash comment @@ -218,7 +218,7 @@ CodeMirror.defineMode("less", function(config) { } return { - startState: function(base) { + startState: function(base) { return {tokenize: tokenBase, baseIndent: base || 0, stack: []}; diff --git a/mode/sql/sql.js b/mode/sql/sql.js index 510110b307..78f8d7fbad 100644 --- a/mode/sql/sql.js +++ b/mode/sql/sql.js @@ -20,11 +20,11 @@ CodeMirror.defineMode("sql", function(config, parserConfig) { } if ((ch == "0" && stream.match(/^[xX][0-9a-fA-F]+/)) - || (ch == "x" || ch == "X") && stream.match(/^'[0-9a-fA-F]+'/)) { + || (ch == "x" || ch == "X") && stream.match(/^'[0-9a-fA-F]+'/)) { // hex return "number"; } else if (((ch == "b" || ch == "B") && stream.match(/^'[01]+'/)) - || (ch == "0" && stream.match(/^b[01]+/))) { + || (ch == "0" && stream.match(/^b[01]+/))) { // bitstring return "number"; } else if (ch.charCodeAt(0) > 47 && ch.charCodeAt(0) < 58) { @@ -43,7 +43,7 @@ CodeMirror.defineMode("sql", function(config, parserConfig) { return null; } else if (ch == "#" || (ch == "-" && stream.eat("-") && stream.eat(" "))) { // 1-line comments - stream.skipToEnd(); + stream.skipToEnd(); return "comment"; } else if (ch == "/" && stream.eat("*")) { // multi-line comments @@ -85,11 +85,11 @@ CodeMirror.defineMode("sql", function(config, parserConfig) { return function(stream, state) { var escaped = false, ch; while ((ch = stream.next()) != null) { - if (ch == quote && !escaped) { - state.tokenize = tokenBase; - break; - } - escaped = !escaped && ch == "\\"; + if (ch == quote && !escaped) { + state.tokenize = tokenBase; + break; + } + escaped = !escaped && ch == "\\"; } return "string"; }; @@ -97,14 +97,14 @@ CodeMirror.defineMode("sql", function(config, parserConfig) { function tokenComment(stream, state) { while (true) { if (stream.skipTo("*")) { - stream.next(); - if (stream.eat("/")) { - state.tokenize = tokenBase; - break; - } + stream.next(); + if (stream.eat("/")) { + state.tokenize = tokenBase; + break; + } } else { - stream.skipToEnd(); - break; + stream.skipToEnd(); + break; } } return "comment"; @@ -131,8 +131,8 @@ CodeMirror.defineMode("sql", function(config, parserConfig) { token: function(stream, state) { if (stream.sol()) { - if (state.context && state.context.align == null) - state.context.align = false; + if (state.context && state.context.align == null) + state.context.align = false; } if (stream.eatSpace()) return null; @@ -140,7 +140,7 @@ CodeMirror.defineMode("sql", function(config, parserConfig) { if (style == "comment") return style; if (state.context && state.context.align == null) - state.context.align = true; + state.context.align = true; var tok = stream.current(); if (tok == "(") From e0e42aa58c27f7c88e4b9d4f267d9fac2e285af1 Mon Sep 17 00:00:00 2001 From: Marijn Haverbeke Date: Tue, 2 Apr 2013 11:39:48 +0200 Subject: [PATCH 0941/5780] [linter] Disallow tabs in the whole project --- test/lint/lint.js | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/test/lint/lint.js b/test/lint/lint.js index b1b9381dd2..60b63a2d15 100644 --- a/test/lint/lint.js +++ b/test/lint/lint.js @@ -20,7 +20,7 @@ var scopePasser = walk.make({ function checkFile(fileName) { var file = fs.readFileSync(fileName, "utf8"); - var badChar = file.match(/[\x00-\x08\x0b\x0c\x0e-\x19\uFEFF]/); + var badChar = file.match(/[\x00-\x08\x0b\x0c\x0e-\x19\uFEFF\t]/); if (badChar) fail("Undesirable character " + badChar[0].charCodeAt(0) + " at position " + badChar.index, {source: fileName}); From 2024df3c0726a01d98d2e4842471a11b054420ac Mon Sep 17 00:00:00 2001 From: Marijn Haverbeke Date: Tue, 2 Apr 2013 11:39:54 +0200 Subject: [PATCH 0942/5780] Remove a whole lot of tabs --- addon/hint/javascript-hint.js | 8 +- addon/lint/javascript-lint.js | 118 +-- addon/lint/lint.js | 12 +- mode/ecl/ecl.js | 24 +- mode/gas/gas.js | 82 +- mode/lua/lua.js | 14 +- mode/mirc/mirc.js | 114 +-- mode/perl/perl.js | 1582 ++++++++++++++++----------------- mode/pig/pig.js | 326 +++---- mode/smalltalk/smalltalk.js | 258 +++--- mode/smarty/smarty.js | 6 +- mode/tiddlywiki/tiddlywiki.js | 690 +++++++------- mode/tiki/tiki.js | 595 +++++++------ mode/turtle/turtle.js | 28 +- mode/vbscript/vbscript.js | 8 +- mode/yaml/yaml.js | 180 ++-- mode/z80/z80.js | 192 ++-- 17 files changed, 2104 insertions(+), 2133 deletions(-) diff --git a/addon/hint/javascript-hint.js b/addon/hint/javascript-hint.js index 99e4eadc35..e3366147e3 100644 --- a/addon/hint/javascript-hint.js +++ b/addon/hint/javascript-hint.js @@ -4,7 +4,7 @@ function forEach(arr, f) { for (var i = 0, e = arr.length; i < e; ++i) f(arr[i]); } - + function arrayContains(arr, item) { if (!Array.prototype.indexOf) { var i = arr.length; @@ -44,9 +44,9 @@ } } while (level > 0); tprop = getToken(editor, Pos(cur.line, tprop.start)); - if (tprop.type.indexOf("variable") === 0) - tprop.type = "function"; - else return; // no clue + if (tprop.type.indexOf("variable") === 0) + tprop.type = "function"; + else return; // no clue } if (!context) var context = []; context.push(tprop); diff --git a/addon/lint/javascript-lint.js b/addon/lint/javascript-lint.js index db9ff6b224..9a815c824e 100644 --- a/addon/lint/javascript-lint.js +++ b/addon/lint/javascript-lint.js @@ -3,11 +3,11 @@ var bogus = [ "Dangerous comment" ]; var warnings = [ [ "Expected '{'", - "Statement body should be inside '{ }' braces." ] ]; + "Statement body should be inside '{ }' braces." ] ]; var errors = [ "Missing semicolon", "Extra comma", "Missing property name", - "Unmatched ", " and instead saw", " is not defined", - "Unclosed string", "Stopping, unable to continue" ]; + "Unmatched ", " and instead saw", " is not defined", + "Unclosed string", "Stopping, unable to continue" ]; function validator(options, text) { JSHINT(text, options); @@ -42,10 +42,10 @@ found = description.indexOf(find) !== -1; if (force || found) { - error.severity = severity; + error.severity = severity; } if (found && replace) { - error.description = replace; + error.description = replace; } } } @@ -54,7 +54,7 @@ var description = error.description; for ( var i = 0; i < bogus.length; i++) { if (description.indexOf(bogus[i]) !== -1) { - return true; + return true; } } return false; @@ -64,59 +64,59 @@ for ( var i = 0; i < errors.length; i++) { var error = errors[i]; if (error) { - var linetabpositions, index; - - linetabpositions = []; - - // This next block is to fix a problem in jshint. Jshint - // replaces - // all tabs with spaces then performs some checks. The error - // positions (character/space) are then reported incorrectly, - // not taking the replacement step into account. Here we look - // at the evidence line and try to adjust the character position - // to the correct value. - if (error.evidence) { - // Tab positions are computed once per line and cached - var tabpositions = linetabpositions[error.line]; - if (!tabpositions) { - var evidence = error.evidence; - tabpositions = []; - // ugggh phantomjs does not like this - // forEachChar(evidence, function(item, index) { - Array.prototype.forEach.call(evidence, function(item, - index) { - if (item === '\t') { - // First col is 1 (not 0) to match error - // positions - tabpositions.push(index + 1); - } - }); - linetabpositions[error.line] = tabpositions; - } - if (tabpositions.length > 0) { - var pos = error.character; - tabpositions.forEach(function(tabposition) { - if (pos > tabposition) pos -= 1; - }); - error.character = pos; - } - } - - var start = error.character - 1, end = start + 1; - if (error.evidence) { - index = error.evidence.substring(start).search(/.\b/); - if (index > -1) { - end += index; - } - } - - // Convert to format expected by validation service - error.description = error.reason;// + "(jshint)"; - error.start = error.character; - error.end = end; - error = cleanup(error); - - if (error) + var linetabpositions, index; + + linetabpositions = []; + + // This next block is to fix a problem in jshint. Jshint + // replaces + // all tabs with spaces then performs some checks. The error + // positions (character/space) are then reported incorrectly, + // not taking the replacement step into account. Here we look + // at the evidence line and try to adjust the character position + // to the correct value. + if (error.evidence) { + // Tab positions are computed once per line and cached + var tabpositions = linetabpositions[error.line]; + if (!tabpositions) { + var evidence = error.evidence; + tabpositions = []; + // ugggh phantomjs does not like this + // forEachChar(evidence, function(item, index) { + Array.prototype.forEach.call(evidence, function(item, + index) { + if (item === '\t') { + // First col is 1 (not 0) to match error + // positions + tabpositions.push(index + 1); + } + }); + linetabpositions[error.line] = tabpositions; + } + if (tabpositions.length > 0) { + var pos = error.character; + tabpositions.forEach(function(tabposition) { + if (pos > tabposition) pos -= 1; + }); + error.character = pos; + } + } + + var start = error.character - 1, end = start + 1; + if (error.evidence) { + index = error.evidence.substring(start).search(/.\b/); + if (index > -1) { + end += index; + } + } + + // Convert to format expected by validation service + error.description = error.reason;// + "(jshint)"; + error.start = error.character; + error.end = end; + error = cleanup(error); + + if (error) output.push({message: error.description, severity: error.severity, from: CodeMirror.Pos(error.line - 1, start), diff --git a/addon/lint/lint.js b/addon/lint/lint.js index 1fa5aa985e..d8961ac3f6 100644 --- a/addon/lint/lint.js +++ b/addon/lint/lint.js @@ -91,11 +91,11 @@ CodeMirror.validate = (function() { } function startLinting(cm) { - var state = cm._lintState, options = state.options; - if (options.async) - options.getAnnotations(cm, updateLinting, options); - else - updateLinting(cm, options.getAnnotations(cm.getValue())); + var state = cm._lintState, options = state.options; + if (options.async) + options.getAnnotations(cm, updateLinting, options); + else + updateLinting(cm, options.getAnnotations(cm.getValue())); } function updateLinting(cm, annotationsNotSorted) { @@ -117,7 +117,7 @@ CodeMirror.validate = (function() { if (!SEVERITIES.test(severity)) severity = "error"; maxSeverity = getMaxSeverity(maxSeverity, severity); - if (options.formatAnnotation) ann = options.formatAnnotation(ann); + if (options.formatAnnotation) ann = options.formatAnnotation(ann); if (state.hasGutter) tipLabel.appendChild(annotationTooltip(ann)); if (ann.to) state.marked.push(cm.markText(ann.from, ann.to, { diff --git a/mode/ecl/ecl.js b/mode/ecl/ecl.js index 3ee7a681de..7601b189a0 100644 --- a/mode/ecl/ecl.js +++ b/mode/ecl/ecl.js @@ -75,18 +75,18 @@ CodeMirror.defineMode("ecl", function(config) { } else if (builtin.propertyIsEnumerable(cur)) { if (blockKeywords.propertyIsEnumerable(cur)) curPunc = "newstatement"; return "builtin"; - } else { //Data types are of from KEYWORD## - var i = cur.length - 1; - while(i >= 0 && (!isNaN(cur[i]) || cur[i] == '_')) - --i; - - if (i > 0) { - var cur2 = cur.substr(0, i + 1); - if (variable_3.propertyIsEnumerable(cur2)) { - if (blockKeywords.propertyIsEnumerable(cur2)) curPunc = "newstatement"; - return "variable-3"; - } - } + } else { //Data types are of from KEYWORD## + var i = cur.length - 1; + while(i >= 0 && (!isNaN(cur[i]) || cur[i] == '_')) + --i; + + if (i > 0) { + var cur2 = cur.substr(0, i + 1); + if (variable_3.propertyIsEnumerable(cur2)) { + if (blockKeywords.propertyIsEnumerable(cur2)) curPunc = "newstatement"; + return "variable-3"; + } + } } if (atoms.propertyIsEnumerable(cur)) return "atom"; return null; diff --git a/mode/gas/gas.js b/mode/gas/gas.js index a03382b61f..5ef4bb04ed 100644 --- a/mode/gas/gas.js +++ b/mode/gas/gas.js @@ -205,8 +205,8 @@ CodeMirror.defineMode("gas", function(_config, parserConfig) { custom.push(function(ch, stream) { if (ch === '#') { - stream.eatWhile(/\w/); - return "number"; + stream.eatWhile(/\w/); + return "number"; } }); } @@ -222,7 +222,7 @@ CodeMirror.defineMode("gas", function(_config, parserConfig) { var escaped = false, next; while ((next = stream.next()) != null) { if (next === end && !escaped) { - return false; + return false; } escaped = !escaped && next === "\\"; } @@ -233,8 +233,8 @@ CodeMirror.defineMode("gas", function(_config, parserConfig) { var maybeEnd = false, ch; while ((ch = stream.next()) != null) { if (ch === "/" && maybeEnd) { - state.tokenize = null; - break; + state.tokenize = null; + break; } maybeEnd = (ch === "*"); } @@ -244,82 +244,82 @@ CodeMirror.defineMode("gas", function(_config, parserConfig) { return { startState: function() { return { - tokenize: null + tokenize: null }; }, token: function(stream, state) { if (state.tokenize) { - return state.tokenize(stream, state); + return state.tokenize(stream, state); } if (stream.eatSpace()) { - return null; + return null; } var style, cur, ch = stream.next(); if (ch === "/") { - if (stream.eat("*")) { - state.tokenize = clikeComment; - return clikeComment(stream, state); - } + if (stream.eat("*")) { + state.tokenize = clikeComment; + return clikeComment(stream, state); + } } if (ch === lineCommentStartSymbol) { - stream.skipToEnd(); - return "comment"; + stream.skipToEnd(); + return "comment"; } if (ch === '"') { - nextUntilUnescaped(stream, '"'); - return "string"; + nextUntilUnescaped(stream, '"'); + return "string"; } if (ch === '.') { - stream.eatWhile(/\w/); - cur = stream.current().toLowerCase(); - style = directives[cur]; - return style || null; + stream.eatWhile(/\w/); + cur = stream.current().toLowerCase(); + style = directives[cur]; + return style || null; } if (ch === '=') { - stream.eatWhile(/\w/); - return "tag"; + stream.eatWhile(/\w/); + return "tag"; } if (ch === '{') { - return "braket"; + return "braket"; } if (ch === '}') { - return "braket"; + return "braket"; } if (/\d/.test(ch)) { - if (ch === "0" && stream.eat("x")) { - stream.eatWhile(/[0-9a-fA-F]/); - return "number"; - } - stream.eatWhile(/\d/); - return "number"; + if (ch === "0" && stream.eat("x")) { + stream.eatWhile(/[0-9a-fA-F]/); + return "number"; + } + stream.eatWhile(/\d/); + return "number"; } if (/\w/.test(ch)) { - stream.eatWhile(/\w/); - if (stream.eat(":")) { - return 'tag'; - } - cur = stream.current().toLowerCase(); - style = registers[cur]; - return style || null; + stream.eatWhile(/\w/); + if (stream.eat(":")) { + return 'tag'; + } + cur = stream.current().toLowerCase(); + style = registers[cur]; + return style || null; } for (var i = 0; i < custom.length; i++) { - style = custom[i](ch, stream, state); - if (style) { - return style; - } + style = custom[i](ch, stream, state); + if (style) { + return style; + } } } }; diff --git a/mode/lua/lua.js b/mode/lua/lua.js index 97fb2c6f96..90761e2941 100644 --- a/mode/lua/lua.js +++ b/mode/lua/lua.js @@ -1,7 +1,7 @@ // LUA mode. Ported to CodeMirror 2 from Franciszek Wawrzak's // CodeMirror 1 mode. // highlights keywords, strings, comments (no leveling supported! ("[==[")), tokens, basic indenting - + CodeMirror.defineMode("lua", function(config, parserConfig) { var indentUnit = config.indentUnit; @@ -12,7 +12,7 @@ CodeMirror.defineMode("lua", function(config, parserConfig) { return new RegExp("^(?:" + words.join("|") + ")$", "i"); } var specials = wordRE(parserConfig.specials || []); - + // long list of standard functions from lua manual var builtins = wordRE([ "_G","_VERSION","assert","collectgarbage","dofile","error","getfenv","getmetatable","ipairs","load", @@ -47,8 +47,8 @@ CodeMirror.defineMode("lua", function(config, parserConfig) { "table.concat","table.insert","table.maxn","table.remove","table.sort" ]); var keywords = wordRE(["and","break","elseif","false","nil","not","or","return", - "true","function", "end", "if", "then", "else", "do", - "while", "repeat", "until", "for", "in", "local" ]); + "true","function", "end", "if", "then", "else", "do", + "while", "repeat", "until", "for", "in", "local" ]); var indentTokens = wordRE(["function", "if","repeat","do", "\\(", "{"]); var dedentTokens = wordRE(["end", "until", "\\)", "}"]); @@ -68,7 +68,7 @@ CodeMirror.defineMode("lua", function(config, parserConfig) { return (state.cur = bracketed(readBracket(stream), "comment"))(stream, state); stream.skipToEnd(); return "comment"; - } + } if (ch == "\"" || ch == "'") return (state.cur = string(ch))(stream, state); if (ch == "[" && /[\[=]/.test(stream.peek())) @@ -108,7 +108,7 @@ CodeMirror.defineMode("lua", function(config, parserConfig) { return "string"; }; } - + return { startState: function(basecol) { return {basecol: basecol || 0, indentDepth: 0, cur: normal}; @@ -121,7 +121,7 @@ CodeMirror.defineMode("lua", function(config, parserConfig) { if (style == "variable") { if (keywords.test(word)) style = "keyword"; else if (builtins.test(word)) style = "builtin"; - else if (specials.test(word)) style = "variable-2"; + else if (specials.test(word)) style = "variable-2"; } if ((style != "comment") && (style != "string")){ if (indentTokens.test(word)) ++state.indentDepth; diff --git a/mode/mirc/mirc.js b/mode/mirc/mirc.js index f76f782764..fc88bc56fb 100644 --- a/mode/mirc/mirc.js +++ b/mode/mirc/mirc.js @@ -8,65 +8,65 @@ CodeMirror.defineMode("mirc", function() { } var specials = parseWords("$! $$ $& $? $+ $abook $abs $active $activecid " + "$activewid $address $addtok $agent $agentname $agentstat $agentver " + - "$alias $and $anick $ansi2mirc $aop $appactive $appstate $asc $asctime " + - "$asin $atan $avoice $away $awaymsg $awaytime $banmask $base $bfind " + - "$binoff $biton $bnick $bvar $bytes $calc $cb $cd $ceil $chan $chanmodes " + - "$chantypes $chat $chr $cid $clevel $click $cmdbox $cmdline $cnick $color " + - "$com $comcall $comchan $comerr $compact $compress $comval $cos $count " + - "$cr $crc $creq $crlf $ctime $ctimer $ctrlenter $date $day $daylight " + - "$dbuh $dbuw $dccignore $dccport $dde $ddename $debug $decode $decompress " + - "$deltok $devent $dialog $did $didreg $didtok $didwm $disk $dlevel $dll " + - "$dllcall $dname $dns $duration $ebeeps $editbox $emailaddr $encode $error " + - "$eval $event $exist $feof $ferr $fgetc $file $filename $filtered $finddir " + - "$finddirn $findfile $findfilen $findtok $fline $floor $fopen $fread $fserve " + - "$fulladdress $fulldate $fullname $fullscreen $get $getdir $getdot $gettok $gmt " + - "$group $halted $hash $height $hfind $hget $highlight $hnick $hotline " + - "$hotlinepos $ial $ialchan $ibl $idle $iel $ifmatch $ignore $iif $iil " + - "$inelipse $ini $inmidi $inpaste $inpoly $input $inrect $inroundrect " + - "$insong $instok $int $inwave $ip $isalias $isbit $isdde $isdir $isfile " + - "$isid $islower $istok $isupper $keychar $keyrpt $keyval $knick $lactive " + - "$lactivecid $lactivewid $left $len $level $lf $line $lines $link $lock " + - "$lock $locked $log $logstamp $logstampfmt $longfn $longip $lower $ltimer " + - "$maddress $mask $matchkey $matchtok $md5 $me $menu $menubar $menucontext " + - "$menutype $mid $middir $mircdir $mircexe $mircini $mklogfn $mnick $mode " + - "$modefirst $modelast $modespl $mouse $msfile $network $newnick $nick $nofile " + - "$nopath $noqt $not $notags $notify $null $numeric $numok $oline $onpoly " + - "$opnick $or $ord $os $passivedcc $pic $play $pnick $port $portable $portfree " + - "$pos $prefix $prop $protect $puttok $qt $query $rand $r $rawmsg $read $readomo " + - "$readn $regex $regml $regsub $regsubex $remove $remtok $replace $replacex " + - "$reptok $result $rgb $right $round $scid $scon $script $scriptdir $scriptline " + - "$sdir $send $server $serverip $sfile $sha1 $shortfn $show $signal $sin " + - "$site $sline $snick $snicks $snotify $sock $sockbr $sockerr $sockname " + - "$sorttok $sound $sqrt $ssl $sreq $sslready $status $strip $str $stripped " + - "$syle $submenu $switchbar $tan $target $ticks $time $timer $timestamp " + - "$timestampfmt $timezone $tip $titlebar $toolbar $treebar $trust $ulevel " + - "$ulist $upper $uptime $url $usermode $v1 $v2 $var $vcmd $vcmdstat $vcmdver " + - "$version $vnick $vol $wid $width $wildsite $wildtok $window $wrap $xor"); + "$alias $and $anick $ansi2mirc $aop $appactive $appstate $asc $asctime " + + "$asin $atan $avoice $away $awaymsg $awaytime $banmask $base $bfind " + + "$binoff $biton $bnick $bvar $bytes $calc $cb $cd $ceil $chan $chanmodes " + + "$chantypes $chat $chr $cid $clevel $click $cmdbox $cmdline $cnick $color " + + "$com $comcall $comchan $comerr $compact $compress $comval $cos $count " + + "$cr $crc $creq $crlf $ctime $ctimer $ctrlenter $date $day $daylight " + + "$dbuh $dbuw $dccignore $dccport $dde $ddename $debug $decode $decompress " + + "$deltok $devent $dialog $did $didreg $didtok $didwm $disk $dlevel $dll " + + "$dllcall $dname $dns $duration $ebeeps $editbox $emailaddr $encode $error " + + "$eval $event $exist $feof $ferr $fgetc $file $filename $filtered $finddir " + + "$finddirn $findfile $findfilen $findtok $fline $floor $fopen $fread $fserve " + + "$fulladdress $fulldate $fullname $fullscreen $get $getdir $getdot $gettok $gmt " + + "$group $halted $hash $height $hfind $hget $highlight $hnick $hotline " + + "$hotlinepos $ial $ialchan $ibl $idle $iel $ifmatch $ignore $iif $iil " + + "$inelipse $ini $inmidi $inpaste $inpoly $input $inrect $inroundrect " + + "$insong $instok $int $inwave $ip $isalias $isbit $isdde $isdir $isfile " + + "$isid $islower $istok $isupper $keychar $keyrpt $keyval $knick $lactive " + + "$lactivecid $lactivewid $left $len $level $lf $line $lines $link $lock " + + "$lock $locked $log $logstamp $logstampfmt $longfn $longip $lower $ltimer " + + "$maddress $mask $matchkey $matchtok $md5 $me $menu $menubar $menucontext " + + "$menutype $mid $middir $mircdir $mircexe $mircini $mklogfn $mnick $mode " + + "$modefirst $modelast $modespl $mouse $msfile $network $newnick $nick $nofile " + + "$nopath $noqt $not $notags $notify $null $numeric $numok $oline $onpoly " + + "$opnick $or $ord $os $passivedcc $pic $play $pnick $port $portable $portfree " + + "$pos $prefix $prop $protect $puttok $qt $query $rand $r $rawmsg $read $readomo " + + "$readn $regex $regml $regsub $regsubex $remove $remtok $replace $replacex " + + "$reptok $result $rgb $right $round $scid $scon $script $scriptdir $scriptline " + + "$sdir $send $server $serverip $sfile $sha1 $shortfn $show $signal $sin " + + "$site $sline $snick $snicks $snotify $sock $sockbr $sockerr $sockname " + + "$sorttok $sound $sqrt $ssl $sreq $sslready $status $strip $str $stripped " + + "$syle $submenu $switchbar $tan $target $ticks $time $timer $timestamp " + + "$timestampfmt $timezone $tip $titlebar $toolbar $treebar $trust $ulevel " + + "$ulist $upper $uptime $url $usermode $v1 $v2 $var $vcmd $vcmdstat $vcmdver " + + "$version $vnick $vol $wid $width $wildsite $wildtok $window $wrap $xor"); var keywords = parseWords("abook ajinvite alias aline ame amsg anick aop auser autojoin avoice " + "away background ban bcopy beep bread break breplace bset btrunc bunset bwrite " + - "channel clear clearall cline clipboard close cnick color comclose comopen " + - "comreg continue copy creq ctcpreply ctcps dcc dccserver dde ddeserver " + - "debug dec describe dialog did didtok disable disconnect dlevel dline dll " + - "dns dqwindow drawcopy drawdot drawfill drawline drawpic drawrect drawreplace " + - "drawrot drawsave drawscroll drawtext ebeeps echo editbox emailaddr enable " + - "events exit fclose filter findtext finger firewall flash flist flood flush " + - "flushini font fopen fseek fsend fserve fullname fwrite ghide gload gmove " + - "gopts goto gplay gpoint gqreq groups gshow gsize gstop gtalk gunload hadd " + - "halt haltdef hdec hdel help hfree hinc hload hmake hop hsave ial ialclear " + - "ialmark identd if ignore iline inc invite iuser join kick linesep links list " + - "load loadbuf localinfo log mdi me menubar mkdir mnick mode msg nick noop notice " + - "notify omsg onotice part partall pdcc perform play playctrl pop protect pvoice " + - "qme qmsg query queryn quit raw reload remini remote remove rename renwin " + - "reseterror resetidle return rlevel rline rmdir run ruser save savebuf saveini " + - "say scid scon server set showmirc signam sline sockaccept sockclose socklist " + - "socklisten sockmark sockopen sockpause sockread sockrename sockudp sockwrite " + - "sound speak splay sreq strip switchbar timer timestamp titlebar tnick tokenize " + - "toolbar topic tray treebar ulist unload unset unsetall updatenl url uwho " + - "var vcadd vcmd vcrem vol while whois window winhelp write writeint if isalnum " + - "isalpha isaop isavoice isban ischan ishop isignore isin isincs isletter islower " + - "isnotify isnum ison isop isprotect isreg isupper isvoice iswm iswmcs " + - "elseif else goto menu nicklist status title icon size option text edit " + - "button check radio box scroll list combo link tab item"); + "channel clear clearall cline clipboard close cnick color comclose comopen " + + "comreg continue copy creq ctcpreply ctcps dcc dccserver dde ddeserver " + + "debug dec describe dialog did didtok disable disconnect dlevel dline dll " + + "dns dqwindow drawcopy drawdot drawfill drawline drawpic drawrect drawreplace " + + "drawrot drawsave drawscroll drawtext ebeeps echo editbox emailaddr enable " + + "events exit fclose filter findtext finger firewall flash flist flood flush " + + "flushini font fopen fseek fsend fserve fullname fwrite ghide gload gmove " + + "gopts goto gplay gpoint gqreq groups gshow gsize gstop gtalk gunload hadd " + + "halt haltdef hdec hdel help hfree hinc hload hmake hop hsave ial ialclear " + + "ialmark identd if ignore iline inc invite iuser join kick linesep links list " + + "load loadbuf localinfo log mdi me menubar mkdir mnick mode msg nick noop notice " + + "notify omsg onotice part partall pdcc perform play playctrl pop protect pvoice " + + "qme qmsg query queryn quit raw reload remini remote remove rename renwin " + + "reseterror resetidle return rlevel rline rmdir run ruser save savebuf saveini " + + "say scid scon server set showmirc signam sline sockaccept sockclose socklist " + + "socklisten sockmark sockopen sockpause sockread sockrename sockudp sockwrite " + + "sound speak splay sreq strip switchbar timer timestamp titlebar tnick tokenize " + + "toolbar topic tray treebar ulist unload unset unsetall updatenl url uwho " + + "var vcadd vcmd vcrem vol while whois window winhelp write writeint if isalnum " + + "isalpha isaop isavoice isban ischan ishop isignore isin isincs isletter islower " + + "isnotify isnum ison isop isprotect isreg isupper isvoice iswm iswmcs " + + "elseif else goto menu nicklist status title icon size option text edit " + + "button check radio box scroll list combo link tab item"); var functions = parseWords("if elseif else and not or eq ne in ni for foreach while switch"); var isOperatorChar = /[+\-*&%=<>!?^\/\|]/; function chain(stream, state, f) { diff --git a/mode/perl/perl.js b/mode/perl/perl.js index 52361c6107..5954b1a61c 100644 --- a/mode/perl/perl.js +++ b/mode/perl/perl.js @@ -1,816 +1,816 @@ // CodeMirror2 mode/perl/perl.js (text/x-perl) beta 0.10 (2011-11-08) // This is a part of CodeMirror from https://github.com/sabaca/CodeMirror_mode_perl (mail@sabaca.com) CodeMirror.defineMode("perl",function(){ - // http://perldoc.perl.org - var PERL={ // null - magic touch - // 1 - keyword - // 2 - def - // 3 - atom - // 4 - operator - // 5 - variable-2 (predefined) - // [x,y] - x=1,2,3; y=must be defined if x{...} - // PERL operators - '->' : 4, - '++' : 4, - '--' : 4, - '**' : 4, - // ! ~ \ and unary + and - - '=~' : 4, - '!~' : 4, - '*' : 4, - '/' : 4, - '%' : 4, - 'x' : 4, - '+' : 4, - '-' : 4, - '.' : 4, - '<<' : 4, - '>>' : 4, - // named unary operators - '<' : 4, - '>' : 4, - '<=' : 4, - '>=' : 4, - 'lt' : 4, - 'gt' : 4, - 'le' : 4, - 'ge' : 4, - '==' : 4, - '!=' : 4, - '<=>' : 4, - 'eq' : 4, - 'ne' : 4, - 'cmp' : 4, - '~~' : 4, - '&' : 4, - '|' : 4, - '^' : 4, - '&&' : 4, - '||' : 4, - '//' : 4, - '..' : 4, - '...' : 4, - '?' : 4, - ':' : 4, - '=' : 4, - '+=' : 4, - '-=' : 4, - '*=' : 4, // etc. ??? - ',' : 4, - '=>' : 4, - '::' : 4, - // list operators (rightward) - 'not' : 4, - 'and' : 4, - 'or' : 4, - 'xor' : 4, - // PERL predefined variables (I know, what this is a paranoid idea, but may be needed for people, who learn PERL, and for me as well, ...and may be for you?;) - 'BEGIN' : [5,1], - 'END' : [5,1], - 'PRINT' : [5,1], - 'PRINTF' : [5,1], - 'GETC' : [5,1], - 'READ' : [5,1], - 'READLINE' : [5,1], - 'DESTROY' : [5,1], - 'TIE' : [5,1], - 'TIEHANDLE' : [5,1], - 'UNTIE' : [5,1], - 'STDIN' : 5, - 'STDIN_TOP' : 5, - 'STDOUT' : 5, - 'STDOUT_TOP' : 5, - 'STDERR' : 5, - 'STDERR_TOP' : 5, - '$ARG' : 5, - '$_' : 5, - '@ARG' : 5, - '@_' : 5, - '$LIST_SEPARATOR' : 5, - '$"' : 5, - '$PROCESS_ID' : 5, - '$PID' : 5, - '$$' : 5, - '$REAL_GROUP_ID' : 5, - '$GID' : 5, - '$(' : 5, - '$EFFECTIVE_GROUP_ID' : 5, - '$EGID' : 5, - '$)' : 5, - '$PROGRAM_NAME' : 5, - '$0' : 5, - '$SUBSCRIPT_SEPARATOR' : 5, - '$SUBSEP' : 5, - '$;' : 5, - '$REAL_USER_ID' : 5, - '$UID' : 5, - '$<' : 5, - '$EFFECTIVE_USER_ID' : 5, - '$EUID' : 5, - '$>' : 5, - '$a' : 5, - '$b' : 5, - '$COMPILING' : 5, - '$^C' : 5, - '$DEBUGGING' : 5, - '$^D' : 5, - '${^ENCODING}' : 5, - '$ENV' : 5, - '%ENV' : 5, - '$SYSTEM_FD_MAX' : 5, - '$^F' : 5, - '@F' : 5, - '${^GLOBAL_PHASE}' : 5, - '$^H' : 5, - '%^H' : 5, - '@INC' : 5, - '%INC' : 5, - '$INPLACE_EDIT' : 5, - '$^I' : 5, - '$^M' : 5, - '$OSNAME' : 5, - '$^O' : 5, - '${^OPEN}' : 5, - '$PERLDB' : 5, - '$^P' : 5, - '$SIG' : 5, - '%SIG' : 5, - '$BASETIME' : 5, - '$^T' : 5, - '${^TAINT}' : 5, - '${^UNICODE}' : 5, - '${^UTF8CACHE}' : 5, - '${^UTF8LOCALE}' : 5, - '$PERL_VERSION' : 5, - '$^V' : 5, - '${^WIN32_SLOPPY_STAT}' : 5, - '$EXECUTABLE_NAME' : 5, - '$^X' : 5, - '$1' : 5, // - regexp $1, $2... - '$MATCH' : 5, - '$&' : 5, - '${^MATCH}' : 5, - '$PREMATCH' : 5, - '$`' : 5, - '${^PREMATCH}' : 5, - '$POSTMATCH' : 5, - "$'" : 5, - '${^POSTMATCH}' : 5, - '$LAST_PAREN_MATCH' : 5, - '$+' : 5, - '$LAST_SUBMATCH_RESULT' : 5, - '$^N' : 5, - '@LAST_MATCH_END' : 5, - '@+' : 5, - '%LAST_PAREN_MATCH' : 5, - '%+' : 5, - '@LAST_MATCH_START' : 5, - '@-' : 5, - '%LAST_MATCH_START' : 5, - '%-' : 5, - '$LAST_REGEXP_CODE_RESULT' : 5, - '$^R' : 5, - '${^RE_DEBUG_FLAGS}' : 5, - '${^RE_TRIE_MAXBUF}' : 5, - '$ARGV' : 5, - '@ARGV' : 5, - 'ARGV' : 5, - 'ARGVOUT' : 5, - '$OUTPUT_FIELD_SEPARATOR' : 5, - '$OFS' : 5, - '$,' : 5, - '$INPUT_LINE_NUMBER' : 5, - '$NR' : 5, - '$.' : 5, - '$INPUT_RECORD_SEPARATOR' : 5, - '$RS' : 5, - '$/' : 5, - '$OUTPUT_RECORD_SEPARATOR' : 5, - '$ORS' : 5, - '$\\' : 5, - '$OUTPUT_AUTOFLUSH' : 5, - '$|' : 5, - '$ACCUMULATOR' : 5, - '$^A' : 5, - '$FORMAT_FORMFEED' : 5, - '$^L' : 5, - '$FORMAT_PAGE_NUMBER' : 5, - '$%' : 5, - '$FORMAT_LINES_LEFT' : 5, - '$-' : 5, - '$FORMAT_LINE_BREAK_CHARACTERS' : 5, - '$:' : 5, - '$FORMAT_LINES_PER_PAGE' : 5, - '$=' : 5, - '$FORMAT_TOP_NAME' : 5, - '$^' : 5, - '$FORMAT_NAME' : 5, - '$~' : 5, - '${^CHILD_ERROR_NATIVE}' : 5, - '$EXTENDED_OS_ERROR' : 5, - '$^E' : 5, - '$EXCEPTIONS_BEING_CAUGHT' : 5, - '$^S' : 5, - '$WARNING' : 5, - '$^W' : 5, - '${^WARNING_BITS}' : 5, - '$OS_ERROR' : 5, - '$ERRNO' : 5, - '$!' : 5, - '%OS_ERROR' : 5, - '%ERRNO' : 5, - '%!' : 5, - '$CHILD_ERROR' : 5, - '$?' : 5, - '$EVAL_ERROR' : 5, - '$@' : 5, - '$OFMT' : 5, - '$#' : 5, - '$*' : 5, - '$ARRAY_BASE' : 5, - '$[' : 5, - '$OLD_PERL_VERSION' : 5, - '$]' : 5, - // PERL blocks - 'if' :[1,1], - elsif :[1,1], - 'else' :[1,1], - 'while' :[1,1], - unless :[1,1], - 'for' :[1,1], - foreach :[1,1], - // PERL functions - 'abs' :1, // - absolute value function - accept :1, // - accept an incoming socket connect - alarm :1, // - schedule a SIGALRM - 'atan2' :1, // - arctangent of Y/X in the range -PI to PI - bind :1, // - binds an address to a socket - binmode :1, // - prepare binary files for I/O - bless :1, // - create an object - bootstrap :1, // - 'break' :1, // - break out of a "given" block - caller :1, // - get context of the current subroutine call - chdir :1, // - change your current working directory - chmod :1, // - changes the permissions on a list of files - chomp :1, // - remove a trailing record separator from a string - chop :1, // - remove the last character from a string - chown :1, // - change the owership on a list of files - chr :1, // - get character this number represents - chroot :1, // - make directory new root for path lookups - close :1, // - close file (or pipe or socket) handle - closedir :1, // - close directory handle - connect :1, // - connect to a remote socket - 'continue' :[1,1], // - optional trailing block in a while or foreach - 'cos' :1, // - cosine function - crypt :1, // - one-way passwd-style encryption - dbmclose :1, // - breaks binding on a tied dbm file - dbmopen :1, // - create binding on a tied dbm file - 'default' :1, // - defined :1, // - test whether a value, variable, or function is defined - 'delete' :1, // - deletes a value from a hash - die :1, // - raise an exception or bail out - 'do' :1, // - turn a BLOCK into a TERM - dump :1, // - create an immediate core dump - each :1, // - retrieve the next key/value pair from a hash - endgrent :1, // - be done using group file - endhostent :1, // - be done using hosts file - endnetent :1, // - be done using networks file - endprotoent :1, // - be done using protocols file - endpwent :1, // - be done using passwd file - endservent :1, // - be done using services file - eof :1, // - test a filehandle for its end - 'eval' :1, // - catch exceptions or compile and run code - 'exec' :1, // - abandon this program to run another - exists :1, // - test whether a hash key is present - exit :1, // - terminate this program - 'exp' :1, // - raise I to a power - fcntl :1, // - file control system call - fileno :1, // - return file descriptor from filehandle - flock :1, // - lock an entire file with an advisory lock - fork :1, // - create a new process just like this one - format :1, // - declare a picture format with use by the write() function - formline :1, // - internal function used for formats - getc :1, // - get the next character from the filehandle - getgrent :1, // - get next group record - getgrgid :1, // - get group record given group user ID - getgrnam :1, // - get group record given group name - gethostbyaddr :1, // - get host record given its address - gethostbyname :1, // - get host record given name - gethostent :1, // - get next hosts record - getlogin :1, // - return who logged in at this tty - getnetbyaddr :1, // - get network record given its address - getnetbyname :1, // - get networks record given name - getnetent :1, // - get next networks record - getpeername :1, // - find the other end of a socket connection - getpgrp :1, // - get process group - getppid :1, // - get parent process ID - getpriority :1, // - get current nice value - getprotobyname :1, // - get protocol record given name - getprotobynumber :1, // - get protocol record numeric protocol - getprotoent :1, // - get next protocols record - getpwent :1, // - get next passwd record - getpwnam :1, // - get passwd record given user login name - getpwuid :1, // - get passwd record given user ID - getservbyname :1, // - get services record given its name - getservbyport :1, // - get services record given numeric port - getservent :1, // - get next services record - getsockname :1, // - retrieve the sockaddr for a given socket - getsockopt :1, // - get socket options on a given socket - given :1, // - glob :1, // - expand filenames using wildcards - gmtime :1, // - convert UNIX time into record or string using Greenwich time - 'goto' :1, // - create spaghetti code - grep :1, // - locate elements in a list test true against a given criterion - hex :1, // - convert a string to a hexadecimal number - 'import' :1, // - patch a module's namespace into your own - index :1, // - find a substring within a string - 'int' :1, // - get the integer portion of a number - ioctl :1, // - system-dependent device control system call - 'join' :1, // - join a list into a string using a separator - keys :1, // - retrieve list of indices from a hash - kill :1, // - send a signal to a process or process group - last :1, // - exit a block prematurely - lc :1, // - return lower-case version of a string - lcfirst :1, // - return a string with just the next letter in lower case - length :1, // - return the number of bytes in a string - 'link' :1, // - create a hard link in the filesytem - listen :1, // - register your socket as a server - local : 2, // - create a temporary value for a global variable (dynamic scoping) - localtime :1, // - convert UNIX time into record or string using local time - lock :1, // - get a thread lock on a variable, subroutine, or method - 'log' :1, // - retrieve the natural logarithm for a number - lstat :1, // - stat a symbolic link - m :null, // - match a string with a regular expression pattern - map :1, // - apply a change to a list to get back a new list with the changes - mkdir :1, // - create a directory - msgctl :1, // - SysV IPC message control operations - msgget :1, // - get SysV IPC message queue - msgrcv :1, // - receive a SysV IPC message from a message queue - msgsnd :1, // - send a SysV IPC message to a message queue - my : 2, // - declare and assign a local variable (lexical scoping) - 'new' :1, // - next :1, // - iterate a block prematurely - no :1, // - unimport some module symbols or semantics at compile time - oct :1, // - convert a string to an octal number - open :1, // - open a file, pipe, or descriptor - opendir :1, // - open a directory - ord :1, // - find a character's numeric representation - our : 2, // - declare and assign a package variable (lexical scoping) - pack :1, // - convert a list into a binary representation - 'package' :1, // - declare a separate global namespace - pipe :1, // - open a pair of connected filehandles - pop :1, // - remove the last element from an array and return it - pos :1, // - find or set the offset for the last/next m//g search - print :1, // - output a list to a filehandle - printf :1, // - output a formatted list to a filehandle - prototype :1, // - get the prototype (if any) of a subroutine - push :1, // - append one or more elements to an array - q :null, // - singly quote a string - qq :null, // - doubly quote a string - qr :null, // - Compile pattern - quotemeta :null, // - quote regular expression magic characters - qw :null, // - quote a list of words - qx :null, // - backquote quote a string - rand :1, // - retrieve the next pseudorandom number - read :1, // - fixed-length buffered input from a filehandle - readdir :1, // - get a directory from a directory handle - readline :1, // - fetch a record from a file - readlink :1, // - determine where a symbolic link is pointing - readpipe :1, // - execute a system command and collect standard output - recv :1, // - receive a message over a Socket - redo :1, // - start this loop iteration over again - ref :1, // - find out the type of thing being referenced - rename :1, // - change a filename - require :1, // - load in external functions from a library at runtime - reset :1, // - clear all variables of a given name - 'return' :1, // - get out of a function early - reverse :1, // - flip a string or a list - rewinddir :1, // - reset directory handle - rindex :1, // - right-to-left substring search - rmdir :1, // - remove a directory - s :null, // - replace a pattern with a string - say :1, // - print with newline - scalar :1, // - force a scalar context - seek :1, // - reposition file pointer for random-access I/O - seekdir :1, // - reposition directory pointer - select :1, // - reset default output or do I/O multiplexing - semctl :1, // - SysV semaphore control operations - semget :1, // - get set of SysV semaphores - semop :1, // - SysV semaphore operations - send :1, // - send a message over a socket - setgrent :1, // - prepare group file for use - sethostent :1, // - prepare hosts file for use - setnetent :1, // - prepare networks file for use - setpgrp :1, // - set the process group of a process - setpriority :1, // - set a process's nice value - setprotoent :1, // - prepare protocols file for use - setpwent :1, // - prepare passwd file for use - setservent :1, // - prepare services file for use - setsockopt :1, // - set some socket options - shift :1, // - remove the first element of an array, and return it - shmctl :1, // - SysV shared memory operations - shmget :1, // - get SysV shared memory segment identifier - shmread :1, // - read SysV shared memory - shmwrite :1, // - write SysV shared memory - shutdown :1, // - close down just half of a socket connection - 'sin' :1, // - return the sine of a number - sleep :1, // - block for some number of seconds - socket :1, // - create a socket - socketpair :1, // - create a pair of sockets - 'sort' :1, // - sort a list of values - splice :1, // - add or remove elements anywhere in an array - 'split' :1, // - split up a string using a regexp delimiter - sprintf :1, // - formatted print into a string - 'sqrt' :1, // - square root function - srand :1, // - seed the random number generator - stat :1, // - get a file's status information - state :1, // - declare and assign a state variable (persistent lexical scoping) - study :1, // - optimize input data for repeated searches - 'sub' :1, // - declare a subroutine, possibly anonymously - 'substr' :1, // - get or alter a portion of a stirng - symlink :1, // - create a symbolic link to a file - syscall :1, // - execute an arbitrary system call - sysopen :1, // - open a file, pipe, or descriptor - sysread :1, // - fixed-length unbuffered input from a filehandle - sysseek :1, // - position I/O pointer on handle used with sysread and syswrite - system :1, // - run a separate program - syswrite :1, // - fixed-length unbuffered output to a filehandle - tell :1, // - get current seekpointer on a filehandle - telldir :1, // - get current seekpointer on a directory handle - tie :1, // - bind a variable to an object class - tied :1, // - get a reference to the object underlying a tied variable - time :1, // - return number of seconds since 1970 - times :1, // - return elapsed time for self and child processes - tr :null, // - transliterate a string - truncate :1, // - shorten a file - uc :1, // - return upper-case version of a string - ucfirst :1, // - return a string with just the next letter in upper case - umask :1, // - set file creation mode mask - undef :1, // - remove a variable or function definition - unlink :1, // - remove one link to a file - unpack :1, // - convert binary structure into normal perl variables - unshift :1, // - prepend more elements to the beginning of a list - untie :1, // - break a tie binding to a variable - use :1, // - load in a module at compile time - utime :1, // - set a file's last access and modify times - values :1, // - return a list of the values in a hash - vec :1, // - test or set particular bits in a string - wait :1, // - wait for any child process to die - waitpid :1, // - wait for a particular child process to die - wantarray :1, // - get void vs scalar vs list context of current subroutine call - warn :1, // - print debugging info - when :1, // - write :1, // - print a picture record - y :null}; // - transliterate a string + // http://perldoc.perl.org + var PERL={ // null - magic touch + // 1 - keyword + // 2 - def + // 3 - atom + // 4 - operator + // 5 - variable-2 (predefined) + // [x,y] - x=1,2,3; y=must be defined if x{...} + // PERL operators + '->' : 4, + '++' : 4, + '--' : 4, + '**' : 4, + // ! ~ \ and unary + and - + '=~' : 4, + '!~' : 4, + '*' : 4, + '/' : 4, + '%' : 4, + 'x' : 4, + '+' : 4, + '-' : 4, + '.' : 4, + '<<' : 4, + '>>' : 4, + // named unary operators + '<' : 4, + '>' : 4, + '<=' : 4, + '>=' : 4, + 'lt' : 4, + 'gt' : 4, + 'le' : 4, + 'ge' : 4, + '==' : 4, + '!=' : 4, + '<=>' : 4, + 'eq' : 4, + 'ne' : 4, + 'cmp' : 4, + '~~' : 4, + '&' : 4, + '|' : 4, + '^' : 4, + '&&' : 4, + '||' : 4, + '//' : 4, + '..' : 4, + '...' : 4, + '?' : 4, + ':' : 4, + '=' : 4, + '+=' : 4, + '-=' : 4, + '*=' : 4, // etc. ??? + ',' : 4, + '=>' : 4, + '::' : 4, + // list operators (rightward) + 'not' : 4, + 'and' : 4, + 'or' : 4, + 'xor' : 4, + // PERL predefined variables (I know, what this is a paranoid idea, but may be needed for people, who learn PERL, and for me as well, ...and may be for you?;) + 'BEGIN' : [5,1], + 'END' : [5,1], + 'PRINT' : [5,1], + 'PRINTF' : [5,1], + 'GETC' : [5,1], + 'READ' : [5,1], + 'READLINE' : [5,1], + 'DESTROY' : [5,1], + 'TIE' : [5,1], + 'TIEHANDLE' : [5,1], + 'UNTIE' : [5,1], + 'STDIN' : 5, + 'STDIN_TOP' : 5, + 'STDOUT' : 5, + 'STDOUT_TOP' : 5, + 'STDERR' : 5, + 'STDERR_TOP' : 5, + '$ARG' : 5, + '$_' : 5, + '@ARG' : 5, + '@_' : 5, + '$LIST_SEPARATOR' : 5, + '$"' : 5, + '$PROCESS_ID' : 5, + '$PID' : 5, + '$$' : 5, + '$REAL_GROUP_ID' : 5, + '$GID' : 5, + '$(' : 5, + '$EFFECTIVE_GROUP_ID' : 5, + '$EGID' : 5, + '$)' : 5, + '$PROGRAM_NAME' : 5, + '$0' : 5, + '$SUBSCRIPT_SEPARATOR' : 5, + '$SUBSEP' : 5, + '$;' : 5, + '$REAL_USER_ID' : 5, + '$UID' : 5, + '$<' : 5, + '$EFFECTIVE_USER_ID' : 5, + '$EUID' : 5, + '$>' : 5, + '$a' : 5, + '$b' : 5, + '$COMPILING' : 5, + '$^C' : 5, + '$DEBUGGING' : 5, + '$^D' : 5, + '${^ENCODING}' : 5, + '$ENV' : 5, + '%ENV' : 5, + '$SYSTEM_FD_MAX' : 5, + '$^F' : 5, + '@F' : 5, + '${^GLOBAL_PHASE}' : 5, + '$^H' : 5, + '%^H' : 5, + '@INC' : 5, + '%INC' : 5, + '$INPLACE_EDIT' : 5, + '$^I' : 5, + '$^M' : 5, + '$OSNAME' : 5, + '$^O' : 5, + '${^OPEN}' : 5, + '$PERLDB' : 5, + '$^P' : 5, + '$SIG' : 5, + '%SIG' : 5, + '$BASETIME' : 5, + '$^T' : 5, + '${^TAINT}' : 5, + '${^UNICODE}' : 5, + '${^UTF8CACHE}' : 5, + '${^UTF8LOCALE}' : 5, + '$PERL_VERSION' : 5, + '$^V' : 5, + '${^WIN32_SLOPPY_STAT}' : 5, + '$EXECUTABLE_NAME' : 5, + '$^X' : 5, + '$1' : 5, // - regexp $1, $2... + '$MATCH' : 5, + '$&' : 5, + '${^MATCH}' : 5, + '$PREMATCH' : 5, + '$`' : 5, + '${^PREMATCH}' : 5, + '$POSTMATCH' : 5, + "$'" : 5, + '${^POSTMATCH}' : 5, + '$LAST_PAREN_MATCH' : 5, + '$+' : 5, + '$LAST_SUBMATCH_RESULT' : 5, + '$^N' : 5, + '@LAST_MATCH_END' : 5, + '@+' : 5, + '%LAST_PAREN_MATCH' : 5, + '%+' : 5, + '@LAST_MATCH_START' : 5, + '@-' : 5, + '%LAST_MATCH_START' : 5, + '%-' : 5, + '$LAST_REGEXP_CODE_RESULT' : 5, + '$^R' : 5, + '${^RE_DEBUG_FLAGS}' : 5, + '${^RE_TRIE_MAXBUF}' : 5, + '$ARGV' : 5, + '@ARGV' : 5, + 'ARGV' : 5, + 'ARGVOUT' : 5, + '$OUTPUT_FIELD_SEPARATOR' : 5, + '$OFS' : 5, + '$,' : 5, + '$INPUT_LINE_NUMBER' : 5, + '$NR' : 5, + '$.' : 5, + '$INPUT_RECORD_SEPARATOR' : 5, + '$RS' : 5, + '$/' : 5, + '$OUTPUT_RECORD_SEPARATOR' : 5, + '$ORS' : 5, + '$\\' : 5, + '$OUTPUT_AUTOFLUSH' : 5, + '$|' : 5, + '$ACCUMULATOR' : 5, + '$^A' : 5, + '$FORMAT_FORMFEED' : 5, + '$^L' : 5, + '$FORMAT_PAGE_NUMBER' : 5, + '$%' : 5, + '$FORMAT_LINES_LEFT' : 5, + '$-' : 5, + '$FORMAT_LINE_BREAK_CHARACTERS' : 5, + '$:' : 5, + '$FORMAT_LINES_PER_PAGE' : 5, + '$=' : 5, + '$FORMAT_TOP_NAME' : 5, + '$^' : 5, + '$FORMAT_NAME' : 5, + '$~' : 5, + '${^CHILD_ERROR_NATIVE}' : 5, + '$EXTENDED_OS_ERROR' : 5, + '$^E' : 5, + '$EXCEPTIONS_BEING_CAUGHT' : 5, + '$^S' : 5, + '$WARNING' : 5, + '$^W' : 5, + '${^WARNING_BITS}' : 5, + '$OS_ERROR' : 5, + '$ERRNO' : 5, + '$!' : 5, + '%OS_ERROR' : 5, + '%ERRNO' : 5, + '%!' : 5, + '$CHILD_ERROR' : 5, + '$?' : 5, + '$EVAL_ERROR' : 5, + '$@' : 5, + '$OFMT' : 5, + '$#' : 5, + '$*' : 5, + '$ARRAY_BASE' : 5, + '$[' : 5, + '$OLD_PERL_VERSION' : 5, + '$]' : 5, + // PERL blocks + 'if' :[1,1], + elsif :[1,1], + 'else' :[1,1], + 'while' :[1,1], + unless :[1,1], + 'for' :[1,1], + foreach :[1,1], + // PERL functions + 'abs' :1, // - absolute value function + accept :1, // - accept an incoming socket connect + alarm :1, // - schedule a SIGALRM + 'atan2' :1, // - arctangent of Y/X in the range -PI to PI + bind :1, // - binds an address to a socket + binmode :1, // - prepare binary files for I/O + bless :1, // - create an object + bootstrap :1, // + 'break' :1, // - break out of a "given" block + caller :1, // - get context of the current subroutine call + chdir :1, // - change your current working directory + chmod :1, // - changes the permissions on a list of files + chomp :1, // - remove a trailing record separator from a string + chop :1, // - remove the last character from a string + chown :1, // - change the owership on a list of files + chr :1, // - get character this number represents + chroot :1, // - make directory new root for path lookups + close :1, // - close file (or pipe or socket) handle + closedir :1, // - close directory handle + connect :1, // - connect to a remote socket + 'continue' :[1,1], // - optional trailing block in a while or foreach + 'cos' :1, // - cosine function + crypt :1, // - one-way passwd-style encryption + dbmclose :1, // - breaks binding on a tied dbm file + dbmopen :1, // - create binding on a tied dbm file + 'default' :1, // + defined :1, // - test whether a value, variable, or function is defined + 'delete' :1, // - deletes a value from a hash + die :1, // - raise an exception or bail out + 'do' :1, // - turn a BLOCK into a TERM + dump :1, // - create an immediate core dump + each :1, // - retrieve the next key/value pair from a hash + endgrent :1, // - be done using group file + endhostent :1, // - be done using hosts file + endnetent :1, // - be done using networks file + endprotoent :1, // - be done using protocols file + endpwent :1, // - be done using passwd file + endservent :1, // - be done using services file + eof :1, // - test a filehandle for its end + 'eval' :1, // - catch exceptions or compile and run code + 'exec' :1, // - abandon this program to run another + exists :1, // - test whether a hash key is present + exit :1, // - terminate this program + 'exp' :1, // - raise I to a power + fcntl :1, // - file control system call + fileno :1, // - return file descriptor from filehandle + flock :1, // - lock an entire file with an advisory lock + fork :1, // - create a new process just like this one + format :1, // - declare a picture format with use by the write() function + formline :1, // - internal function used for formats + getc :1, // - get the next character from the filehandle + getgrent :1, // - get next group record + getgrgid :1, // - get group record given group user ID + getgrnam :1, // - get group record given group name + gethostbyaddr :1, // - get host record given its address + gethostbyname :1, // - get host record given name + gethostent :1, // - get next hosts record + getlogin :1, // - return who logged in at this tty + getnetbyaddr :1, // - get network record given its address + getnetbyname :1, // - get networks record given name + getnetent :1, // - get next networks record + getpeername :1, // - find the other end of a socket connection + getpgrp :1, // - get process group + getppid :1, // - get parent process ID + getpriority :1, // - get current nice value + getprotobyname :1, // - get protocol record given name + getprotobynumber :1, // - get protocol record numeric protocol + getprotoent :1, // - get next protocols record + getpwent :1, // - get next passwd record + getpwnam :1, // - get passwd record given user login name + getpwuid :1, // - get passwd record given user ID + getservbyname :1, // - get services record given its name + getservbyport :1, // - get services record given numeric port + getservent :1, // - get next services record + getsockname :1, // - retrieve the sockaddr for a given socket + getsockopt :1, // - get socket options on a given socket + given :1, // + glob :1, // - expand filenames using wildcards + gmtime :1, // - convert UNIX time into record or string using Greenwich time + 'goto' :1, // - create spaghetti code + grep :1, // - locate elements in a list test true against a given criterion + hex :1, // - convert a string to a hexadecimal number + 'import' :1, // - patch a module's namespace into your own + index :1, // - find a substring within a string + 'int' :1, // - get the integer portion of a number + ioctl :1, // - system-dependent device control system call + 'join' :1, // - join a list into a string using a separator + keys :1, // - retrieve list of indices from a hash + kill :1, // - send a signal to a process or process group + last :1, // - exit a block prematurely + lc :1, // - return lower-case version of a string + lcfirst :1, // - return a string with just the next letter in lower case + length :1, // - return the number of bytes in a string + 'link' :1, // - create a hard link in the filesytem + listen :1, // - register your socket as a server + local : 2, // - create a temporary value for a global variable (dynamic scoping) + localtime :1, // - convert UNIX time into record or string using local time + lock :1, // - get a thread lock on a variable, subroutine, or method + 'log' :1, // - retrieve the natural logarithm for a number + lstat :1, // - stat a symbolic link + m :null, // - match a string with a regular expression pattern + map :1, // - apply a change to a list to get back a new list with the changes + mkdir :1, // - create a directory + msgctl :1, // - SysV IPC message control operations + msgget :1, // - get SysV IPC message queue + msgrcv :1, // - receive a SysV IPC message from a message queue + msgsnd :1, // - send a SysV IPC message to a message queue + my : 2, // - declare and assign a local variable (lexical scoping) + 'new' :1, // + next :1, // - iterate a block prematurely + no :1, // - unimport some module symbols or semantics at compile time + oct :1, // - convert a string to an octal number + open :1, // - open a file, pipe, or descriptor + opendir :1, // - open a directory + ord :1, // - find a character's numeric representation + our : 2, // - declare and assign a package variable (lexical scoping) + pack :1, // - convert a list into a binary representation + 'package' :1, // - declare a separate global namespace + pipe :1, // - open a pair of connected filehandles + pop :1, // - remove the last element from an array and return it + pos :1, // - find or set the offset for the last/next m//g search + print :1, // - output a list to a filehandle + printf :1, // - output a formatted list to a filehandle + prototype :1, // - get the prototype (if any) of a subroutine + push :1, // - append one or more elements to an array + q :null, // - singly quote a string + qq :null, // - doubly quote a string + qr :null, // - Compile pattern + quotemeta :null, // - quote regular expression magic characters + qw :null, // - quote a list of words + qx :null, // - backquote quote a string + rand :1, // - retrieve the next pseudorandom number + read :1, // - fixed-length buffered input from a filehandle + readdir :1, // - get a directory from a directory handle + readline :1, // - fetch a record from a file + readlink :1, // - determine where a symbolic link is pointing + readpipe :1, // - execute a system command and collect standard output + recv :1, // - receive a message over a Socket + redo :1, // - start this loop iteration over again + ref :1, // - find out the type of thing being referenced + rename :1, // - change a filename + require :1, // - load in external functions from a library at runtime + reset :1, // - clear all variables of a given name + 'return' :1, // - get out of a function early + reverse :1, // - flip a string or a list + rewinddir :1, // - reset directory handle + rindex :1, // - right-to-left substring search + rmdir :1, // - remove a directory + s :null, // - replace a pattern with a string + say :1, // - print with newline + scalar :1, // - force a scalar context + seek :1, // - reposition file pointer for random-access I/O + seekdir :1, // - reposition directory pointer + select :1, // - reset default output or do I/O multiplexing + semctl :1, // - SysV semaphore control operations + semget :1, // - get set of SysV semaphores + semop :1, // - SysV semaphore operations + send :1, // - send a message over a socket + setgrent :1, // - prepare group file for use + sethostent :1, // - prepare hosts file for use + setnetent :1, // - prepare networks file for use + setpgrp :1, // - set the process group of a process + setpriority :1, // - set a process's nice value + setprotoent :1, // - prepare protocols file for use + setpwent :1, // - prepare passwd file for use + setservent :1, // - prepare services file for use + setsockopt :1, // - set some socket options + shift :1, // - remove the first element of an array, and return it + shmctl :1, // - SysV shared memory operations + shmget :1, // - get SysV shared memory segment identifier + shmread :1, // - read SysV shared memory + shmwrite :1, // - write SysV shared memory + shutdown :1, // - close down just half of a socket connection + 'sin' :1, // - return the sine of a number + sleep :1, // - block for some number of seconds + socket :1, // - create a socket + socketpair :1, // - create a pair of sockets + 'sort' :1, // - sort a list of values + splice :1, // - add or remove elements anywhere in an array + 'split' :1, // - split up a string using a regexp delimiter + sprintf :1, // - formatted print into a string + 'sqrt' :1, // - square root function + srand :1, // - seed the random number generator + stat :1, // - get a file's status information + state :1, // - declare and assign a state variable (persistent lexical scoping) + study :1, // - optimize input data for repeated searches + 'sub' :1, // - declare a subroutine, possibly anonymously + 'substr' :1, // - get or alter a portion of a stirng + symlink :1, // - create a symbolic link to a file + syscall :1, // - execute an arbitrary system call + sysopen :1, // - open a file, pipe, or descriptor + sysread :1, // - fixed-length unbuffered input from a filehandle + sysseek :1, // - position I/O pointer on handle used with sysread and syswrite + system :1, // - run a separate program + syswrite :1, // - fixed-length unbuffered output to a filehandle + tell :1, // - get current seekpointer on a filehandle + telldir :1, // - get current seekpointer on a directory handle + tie :1, // - bind a variable to an object class + tied :1, // - get a reference to the object underlying a tied variable + time :1, // - return number of seconds since 1970 + times :1, // - return elapsed time for self and child processes + tr :null, // - transliterate a string + truncate :1, // - shorten a file + uc :1, // - return upper-case version of a string + ucfirst :1, // - return a string with just the next letter in upper case + umask :1, // - set file creation mode mask + undef :1, // - remove a variable or function definition + unlink :1, // - remove one link to a file + unpack :1, // - convert binary structure into normal perl variables + unshift :1, // - prepend more elements to the beginning of a list + untie :1, // - break a tie binding to a variable + use :1, // - load in a module at compile time + utime :1, // - set a file's last access and modify times + values :1, // - return a list of the values in a hash + vec :1, // - test or set particular bits in a string + wait :1, // - wait for any child process to die + waitpid :1, // - wait for a particular child process to die + wantarray :1, // - get void vs scalar vs list context of current subroutine call + warn :1, // - print debugging info + when :1, // + write :1, // - print a picture record + y :null}; // - transliterate a string - var RXstyle="string-2"; - var RXmodifiers=/[goseximacplud]/; // NOTE: "m", "s", "y" and "tr" need to correct real modifiers for each regexp type + var RXstyle="string-2"; + var RXmodifiers=/[goseximacplud]/; // NOTE: "m", "s", "y" and "tr" need to correct real modifiers for each regexp type - function tokenChain(stream,state,chain,style,tail){ // NOTE: chain.length > 2 is not working now (it's for s[...][...]geos;) - state.chain=null; // 12 3tail - state.style=null; - state.tail=null; - state.tokenize=function(stream,state){ - var e=false,c,i=0; - while(c=stream.next()){ - if(c===chain[i]&&!e){ - if(chain[++i]!==undefined){ - state.chain=chain[i]; - state.style=style; - state.tail=tail;} - else if(tail) - stream.eatWhile(tail); - state.tokenize=tokenPerl; - return style;} - e=!e&&c=="\\";} - return style;}; - return state.tokenize(stream,state);} + function tokenChain(stream,state,chain,style,tail){ // NOTE: chain.length > 2 is not working now (it's for s[...][...]geos;) + state.chain=null; // 12 3tail + state.style=null; + state.tail=null; + state.tokenize=function(stream,state){ + var e=false,c,i=0; + while(c=stream.next()){ + if(c===chain[i]&&!e){ + if(chain[++i]!==undefined){ + state.chain=chain[i]; + state.style=style; + state.tail=tail;} + else if(tail) + stream.eatWhile(tail); + state.tokenize=tokenPerl; + return style;} + e=!e&&c=="\\";} + return style;}; + return state.tokenize(stream,state);} - function tokenSOMETHING(stream,state,string){ - state.tokenize=function(stream,state){ - if(stream.string==string) - state.tokenize=tokenPerl; - stream.skipToEnd(); - return "string";}; - return state.tokenize(stream,state);} + function tokenSOMETHING(stream,state,string){ + state.tokenize=function(stream,state){ + if(stream.string==string) + state.tokenize=tokenPerl; + stream.skipToEnd(); + return "string";}; + return state.tokenize(stream,state);} - function tokenPerl(stream,state){ - if(stream.eatSpace()) - return null; - if(state.chain) - return tokenChain(stream,state,state.chain,state.style,state.tail); - if(stream.match(/^\-?[\d\.]/,false)) - if(stream.match(/^(\-?(\d*\.\d+(e[+-]?\d+)?|\d+\.\d*)|0x[\da-fA-F]+|0b[01]+|\d+(e[+-]?\d+)?)/)) - return 'number'; - if(stream.match(/^<<(?=\w)/)){ // NOTE: <"],RXstyle,RXmodifiers);} - if(/[\^'"!~\/]/.test(c)){ - stream.eatSuffix(1); - return tokenChain(stream,state,[stream.eat(c)],RXstyle,RXmodifiers);}} - else if(c=="q"){ - c=stream.look(1); - if(c=="("){ - stream.eatSuffix(2); - return tokenChain(stream,state,[")"],"string");} - if(c=="["){ - stream.eatSuffix(2); - return tokenChain(stream,state,["]"],"string");} - if(c=="{"){ - stream.eatSuffix(2); - return tokenChain(stream,state,["}"],"string");} - if(c=="<"){ - stream.eatSuffix(2); - return tokenChain(stream,state,[">"],"string");} - if(/[\^'"!~\/]/.test(c)){ - stream.eatSuffix(1); - return tokenChain(stream,state,[stream.eat(c)],"string");}} - else if(c=="w"){ - c=stream.look(1); - if(c=="("){ - stream.eatSuffix(2); - return tokenChain(stream,state,[")"],"bracket");} - if(c=="["){ - stream.eatSuffix(2); - return tokenChain(stream,state,["]"],"bracket");} - if(c=="{"){ - stream.eatSuffix(2); - return tokenChain(stream,state,["}"],"bracket");} - if(c=="<"){ - stream.eatSuffix(2); - return tokenChain(stream,state,[">"],"bracket");} - if(/[\^'"!~\/]/.test(c)){ - stream.eatSuffix(1); - return tokenChain(stream,state,[stream.eat(c)],"bracket");}} - else if(c=="r"){ - c=stream.look(1); - if(c=="("){ - stream.eatSuffix(2); - return tokenChain(stream,state,[")"],RXstyle,RXmodifiers);} - if(c=="["){ - stream.eatSuffix(2); - return tokenChain(stream,state,["]"],RXstyle,RXmodifiers);} - if(c=="{"){ - stream.eatSuffix(2); - return tokenChain(stream,state,["}"],RXstyle,RXmodifiers);} - if(c=="<"){ - stream.eatSuffix(2); - return tokenChain(stream,state,[">"],RXstyle,RXmodifiers);} - if(/[\^'"!~\/]/.test(c)){ - stream.eatSuffix(1); - return tokenChain(stream,state,[stream.eat(c)],RXstyle,RXmodifiers);}} - else if(/[\^'"!~\/(\[{<]/.test(c)){ - if(c=="("){ - stream.eatSuffix(1); - return tokenChain(stream,state,[")"],"string");} - if(c=="["){ - stream.eatSuffix(1); - return tokenChain(stream,state,["]"],"string");} - if(c=="{"){ - stream.eatSuffix(1); - return tokenChain(stream,state,["}"],"string");} - if(c=="<"){ - stream.eatSuffix(1); - return tokenChain(stream,state,[">"],"string");} - if(/[\^'"!~\/]/.test(c)){ - return tokenChain(stream,state,[stream.eat(c)],"string");}}}} - if(ch=="m"){ - var c=stream.look(-2); - if(!(c&&/\w/.test(c))){ - c=stream.eat(/[(\[{<\^'"!~\/]/); - if(c){ - if(/[\^'"!~\/]/.test(c)){ - return tokenChain(stream,state,[c],RXstyle,RXmodifiers);} - if(c=="("){ - return tokenChain(stream,state,[")"],RXstyle,RXmodifiers);} - if(c=="["){ - return tokenChain(stream,state,["]"],RXstyle,RXmodifiers);} - if(c=="{"){ - return tokenChain(stream,state,["}"],RXstyle,RXmodifiers);} - if(c=="<"){ - return tokenChain(stream,state,[">"],RXstyle,RXmodifiers);}}}} - if(ch=="s"){ - var c=/[\/>\]})\w]/.test(stream.look(-2)); - if(!c){ - c=stream.eat(/[(\[{<\^'"!~\/]/); - if(c){ - if(c=="[") - return tokenChain(stream,state,["]","]"],RXstyle,RXmodifiers); - if(c=="{") - return tokenChain(stream,state,["}","}"],RXstyle,RXmodifiers); - if(c=="<") - return tokenChain(stream,state,[">",">"],RXstyle,RXmodifiers); - if(c=="(") - return tokenChain(stream,state,[")",")"],RXstyle,RXmodifiers); - return tokenChain(stream,state,[c,c],RXstyle,RXmodifiers);}}} - if(ch=="y"){ - var c=/[\/>\]})\w]/.test(stream.look(-2)); - if(!c){ - c=stream.eat(/[(\[{<\^'"!~\/]/); - if(c){ - if(c=="[") - return tokenChain(stream,state,["]","]"],RXstyle,RXmodifiers); - if(c=="{") - return tokenChain(stream,state,["}","}"],RXstyle,RXmodifiers); - if(c=="<") - return tokenChain(stream,state,[">",">"],RXstyle,RXmodifiers); - if(c=="(") - return tokenChain(stream,state,[")",")"],RXstyle,RXmodifiers); - return tokenChain(stream,state,[c,c],RXstyle,RXmodifiers);}}} - if(ch=="t"){ - var c=/[\/>\]})\w]/.test(stream.look(-2)); - if(!c){ - c=stream.eat("r");if(c){ - c=stream.eat(/[(\[{<\^'"!~\/]/); - if(c){ - if(c=="[") - return tokenChain(stream,state,["]","]"],RXstyle,RXmodifiers); - if(c=="{") - return tokenChain(stream,state,["}","}"],RXstyle,RXmodifiers); - if(c=="<") - return tokenChain(stream,state,[">",">"],RXstyle,RXmodifiers); - if(c=="(") - return tokenChain(stream,state,[")",")"],RXstyle,RXmodifiers); - return tokenChain(stream,state,[c,c],RXstyle,RXmodifiers);}}}} - if(ch=="`"){ - return tokenChain(stream,state,[ch],"variable-2");} - if(ch=="/"){ - if(!/~\s*$/.test(stream.prefix())) - return "operator"; - else - return tokenChain(stream,state,[ch],RXstyle,RXmodifiers);} - if(ch=="$"){ - var p=stream.pos; - if(stream.eatWhile(/\d/)||stream.eat("{")&&stream.eatWhile(/\d/)&&stream.eat("}")) - return "variable-2"; - else - stream.pos=p;} - if(/[$@%]/.test(ch)){ - var p=stream.pos; - if(stream.eat("^")&&stream.eat(/[A-Z]/)||!/[@$%&]/.test(stream.look(-2))&&stream.eat(/[=|\\\-#?@;:&`~\^!\[\]*'"$+.,\/<>()]/)){ - var c=stream.current(); - if(PERL[c]) - return "variable-2";} - stream.pos=p;} - if(/[$@%&]/.test(ch)){ - if(stream.eatWhile(/[\w$\[\]]/)||stream.eat("{")&&stream.eatWhile(/[\w$\[\]]/)&&stream.eat("}")){ - var c=stream.current(); - if(PERL[c]) - return "variable-2"; - else - return "variable";}} - if(ch=="#"){ - if(stream.look(-2)!="$"){ - stream.skipToEnd(); - return "comment";}} - if(/[:+\-\^*$&%@=<>!?|\/~\.]/.test(ch)){ - var p=stream.pos; - stream.eatWhile(/[:+\-\^*$&%@=<>!?|\/~\.]/); - if(PERL[stream.current()]) - return "operator"; - else - stream.pos=p;} - if(ch=="_"){ - if(stream.pos==1){ - if(stream.suffix(6)=="_END__"){ - return tokenChain(stream,state,['\0'],"comment");} - else if(stream.suffix(7)=="_DATA__"){ - return tokenChain(stream,state,['\0'],"variable-2");} - else if(stream.suffix(7)=="_C__"){ - return tokenChain(stream,state,['\0'],"string");}}} - if(/\w/.test(ch)){ - var p=stream.pos; - if(stream.look(-2)=="{"&&(stream.look(0)=="}"||stream.eatWhile(/\w/)&&stream.look(0)=="}")) - return "string"; - else - stream.pos=p;} - if(/[A-Z]/.test(ch)){ - var l=stream.look(-2); - var p=stream.pos; - stream.eatWhile(/[A-Z_]/); - if(/[\da-z]/.test(stream.look(0))){ - stream.pos=p;} - else{ - var c=PERL[stream.current()]; - if(!c) - return "meta"; - if(c[1]) - c=c[0]; - if(l!=":"){ - if(c==1) - return "keyword"; - else if(c==2) - return "def"; - else if(c==3) - return "atom"; - else if(c==4) - return "operator"; - else if(c==5) - return "variable-2"; - else - return "meta";} - else - return "meta";}} - if(/[a-zA-Z_]/.test(ch)){ - var l=stream.look(-2); - stream.eatWhile(/\w/); - var c=PERL[stream.current()]; - if(!c) - return "meta"; - if(c[1]) - c=c[0]; - if(l!=":"){ - if(c==1) - return "keyword"; - else if(c==2) - return "def"; - else if(c==3) - return "atom"; - else if(c==4) - return "operator"; - else if(c==5) - return "variable-2"; - else - return "meta";} - else - return "meta";} - return null;} + function tokenPerl(stream,state){ + if(stream.eatSpace()) + return null; + if(state.chain) + return tokenChain(stream,state,state.chain,state.style,state.tail); + if(stream.match(/^\-?[\d\.]/,false)) + if(stream.match(/^(\-?(\d*\.\d+(e[+-]?\d+)?|\d+\.\d*)|0x[\da-fA-F]+|0b[01]+|\d+(e[+-]?\d+)?)/)) + return 'number'; + if(stream.match(/^<<(?=\w)/)){ // NOTE: <"],RXstyle,RXmodifiers);} + if(/[\^'"!~\/]/.test(c)){ + stream.eatSuffix(1); + return tokenChain(stream,state,[stream.eat(c)],RXstyle,RXmodifiers);}} + else if(c=="q"){ + c=stream.look(1); + if(c=="("){ + stream.eatSuffix(2); + return tokenChain(stream,state,[")"],"string");} + if(c=="["){ + stream.eatSuffix(2); + return tokenChain(stream,state,["]"],"string");} + if(c=="{"){ +stream.eatSuffix(2); + return tokenChain(stream,state,["}"],"string");} + if(c=="<"){ + stream.eatSuffix(2); + return tokenChain(stream,state,[">"],"string");} + if(/[\^'"!~\/]/.test(c)){ + stream.eatSuffix(1); + return tokenChain(stream,state,[stream.eat(c)],"string");}} + else if(c=="w"){ + c=stream.look(1); + if(c=="("){ + stream.eatSuffix(2); + return tokenChain(stream,state,[")"],"bracket");} + if(c=="["){ + stream.eatSuffix(2); + return tokenChain(stream,state,["]"],"bracket");} + if(c=="{"){ + stream.eatSuffix(2); + return tokenChain(stream,state,["}"],"bracket");} + if(c=="<"){ + stream.eatSuffix(2); + return tokenChain(stream,state,[">"],"bracket");} + if(/[\^'"!~\/]/.test(c)){ + stream.eatSuffix(1); + return tokenChain(stream,state,[stream.eat(c)],"bracket");}} + else if(c=="r"){ + c=stream.look(1); + if(c=="("){ + stream.eatSuffix(2); + return tokenChain(stream,state,[")"],RXstyle,RXmodifiers);} + if(c=="["){ + stream.eatSuffix(2); + return tokenChain(stream,state,["]"],RXstyle,RXmodifiers);} + if(c=="{"){ + stream.eatSuffix(2); + return tokenChain(stream,state,["}"],RXstyle,RXmodifiers);} + if(c=="<"){ + stream.eatSuffix(2); + return tokenChain(stream,state,[">"],RXstyle,RXmodifiers);} + if(/[\^'"!~\/]/.test(c)){ + stream.eatSuffix(1); + return tokenChain(stream,state,[stream.eat(c)],RXstyle,RXmodifiers);}} + else if(/[\^'"!~\/(\[{<]/.test(c)){ + if(c=="("){ + stream.eatSuffix(1); + return tokenChain(stream,state,[")"],"string");} + if(c=="["){ + stream.eatSuffix(1); + return tokenChain(stream,state,["]"],"string");} + if(c=="{"){ + stream.eatSuffix(1); + return tokenChain(stream,state,["}"],"string");} + if(c=="<"){ + stream.eatSuffix(1); + return tokenChain(stream,state,[">"],"string");} + if(/[\^'"!~\/]/.test(c)){ + return tokenChain(stream,state,[stream.eat(c)],"string");}}}} + if(ch=="m"){ + var c=stream.look(-2); + if(!(c&&/\w/.test(c))){ + c=stream.eat(/[(\[{<\^'"!~\/]/); + if(c){ + if(/[\^'"!~\/]/.test(c)){ + return tokenChain(stream,state,[c],RXstyle,RXmodifiers);} + if(c=="("){ + return tokenChain(stream,state,[")"],RXstyle,RXmodifiers);} + if(c=="["){ + return tokenChain(stream,state,["]"],RXstyle,RXmodifiers);} + if(c=="{"){ + return tokenChain(stream,state,["}"],RXstyle,RXmodifiers);} + if(c=="<"){ + return tokenChain(stream,state,[">"],RXstyle,RXmodifiers);}}}} + if(ch=="s"){ + var c=/[\/>\]})\w]/.test(stream.look(-2)); + if(!c){ + c=stream.eat(/[(\[{<\^'"!~\/]/); + if(c){ + if(c=="[") + return tokenChain(stream,state,["]","]"],RXstyle,RXmodifiers); + if(c=="{") + return tokenChain(stream,state,["}","}"],RXstyle,RXmodifiers); + if(c=="<") + return tokenChain(stream,state,[">",">"],RXstyle,RXmodifiers); + if(c=="(") + return tokenChain(stream,state,[")",")"],RXstyle,RXmodifiers); + return tokenChain(stream,state,[c,c],RXstyle,RXmodifiers);}}} + if(ch=="y"){ + var c=/[\/>\]})\w]/.test(stream.look(-2)); + if(!c){ + c=stream.eat(/[(\[{<\^'"!~\/]/); + if(c){ + if(c=="[") + return tokenChain(stream,state,["]","]"],RXstyle,RXmodifiers); + if(c=="{") + return tokenChain(stream,state,["}","}"],RXstyle,RXmodifiers); + if(c=="<") + return tokenChain(stream,state,[">",">"],RXstyle,RXmodifiers); + if(c=="(") + return tokenChain(stream,state,[")",")"],RXstyle,RXmodifiers); + return tokenChain(stream,state,[c,c],RXstyle,RXmodifiers);}}} + if(ch=="t"){ + var c=/[\/>\]})\w]/.test(stream.look(-2)); + if(!c){ + c=stream.eat("r");if(c){ + c=stream.eat(/[(\[{<\^'"!~\/]/); + if(c){ + if(c=="[") + return tokenChain(stream,state,["]","]"],RXstyle,RXmodifiers); + if(c=="{") + return tokenChain(stream,state,["}","}"],RXstyle,RXmodifiers); + if(c=="<") + return tokenChain(stream,state,[">",">"],RXstyle,RXmodifiers); + if(c=="(") + return tokenChain(stream,state,[")",")"],RXstyle,RXmodifiers); + return tokenChain(stream,state,[c,c],RXstyle,RXmodifiers);}}}} + if(ch=="`"){ + return tokenChain(stream,state,[ch],"variable-2");} + if(ch=="/"){ + if(!/~\s*$/.test(stream.prefix())) + return "operator"; + else + return tokenChain(stream,state,[ch],RXstyle,RXmodifiers);} + if(ch=="$"){ + var p=stream.pos; + if(stream.eatWhile(/\d/)||stream.eat("{")&&stream.eatWhile(/\d/)&&stream.eat("}")) + return "variable-2"; + else + stream.pos=p;} + if(/[$@%]/.test(ch)){ + var p=stream.pos; + if(stream.eat("^")&&stream.eat(/[A-Z]/)||!/[@$%&]/.test(stream.look(-2))&&stream.eat(/[=|\\\-#?@;:&`~\^!\[\]*'"$+.,\/<>()]/)){ + var c=stream.current(); + if(PERL[c]) + return "variable-2";} + stream.pos=p;} + if(/[$@%&]/.test(ch)){ + if(stream.eatWhile(/[\w$\[\]]/)||stream.eat("{")&&stream.eatWhile(/[\w$\[\]]/)&&stream.eat("}")){ + var c=stream.current(); + if(PERL[c]) + return "variable-2"; + else + return "variable";}} + if(ch=="#"){ + if(stream.look(-2)!="$"){ + stream.skipToEnd(); + return "comment";}} + if(/[:+\-\^*$&%@=<>!?|\/~\.]/.test(ch)){ + var p=stream.pos; + stream.eatWhile(/[:+\-\^*$&%@=<>!?|\/~\.]/); + if(PERL[stream.current()]) + return "operator"; + else + stream.pos=p;} + if(ch=="_"){ + if(stream.pos==1){ + if(stream.suffix(6)=="_END__"){ + return tokenChain(stream,state,['\0'],"comment");} + else if(stream.suffix(7)=="_DATA__"){ + return tokenChain(stream,state,['\0'],"variable-2");} + else if(stream.suffix(7)=="_C__"){ + return tokenChain(stream,state,['\0'],"string");}}} + if(/\w/.test(ch)){ + var p=stream.pos; + if(stream.look(-2)=="{"&&(stream.look(0)=="}"||stream.eatWhile(/\w/)&&stream.look(0)=="}")) + return "string"; + else + stream.pos=p;} + if(/[A-Z]/.test(ch)){ + var l=stream.look(-2); + var p=stream.pos; + stream.eatWhile(/[A-Z_]/); + if(/[\da-z]/.test(stream.look(0))){ + stream.pos=p;} + else{ + var c=PERL[stream.current()]; + if(!c) + return "meta"; + if(c[1]) + c=c[0]; + if(l!=":"){ + if(c==1) + return "keyword"; + else if(c==2) + return "def"; + else if(c==3) + return "atom"; + else if(c==4) + return "operator"; + else if(c==5) + return "variable-2"; + else + return "meta";} + else + return "meta";}} + if(/[a-zA-Z_]/.test(ch)){ + var l=stream.look(-2); + stream.eatWhile(/\w/); + var c=PERL[stream.current()]; + if(!c) + return "meta"; + if(c[1]) + c=c[0]; + if(l!=":"){ + if(c==1) + return "keyword"; + else if(c==2) + return "def"; + else if(c==3) + return "atom"; + else if(c==4) + return "operator"; + else if(c==5) + return "variable-2"; + else + return "meta";} + else + return "meta";} + return null;} - return{ - startState:function(){ - return{ - tokenize:tokenPerl, - chain:null, - style:null, - tail:null};}, - token:function(stream,state){ - return (state.tokenize||tokenPerl)(stream,state);}, - electricChars:"{}"};}); + return{ + startState:function(){ + return{ + tokenize:tokenPerl, + chain:null, + style:null, + tail:null};}, + token:function(stream,state){ + return (state.tokenize||tokenPerl)(stream,state);}, + electricChars:"{}"};}); CodeMirror.defineMIME("text/x-perl", "perl"); // it's like "peek", but need for look-ahead or look-behind if index < 0 CodeMirror.StringStream.prototype.look=function(c){ - return this.string.charAt(this.pos+(c||0));}; + return this.string.charAt(this.pos+(c||0));}; // return a part of prefix of current stream from current position CodeMirror.StringStream.prototype.prefix=function(c){ - if(c){ - var x=this.pos-c; - return this.string.substr((x>=0?x:0),c);} - else{ - return this.string.substr(0,this.pos-1);}}; + if(c){ + var x=this.pos-c; + return this.string.substr((x>=0?x:0),c);} + else{ + return this.string.substr(0,this.pos-1);}}; // return a part of suffix of current stream from current position CodeMirror.StringStream.prototype.suffix=function(c){ - var y=this.string.length; - var x=y-this.pos+1; - return this.string.substr(this.pos,(c&&c=(y=this.string.length-1)) - this.pos=y; - else - this.pos=x;}; + var x=this.pos+c; + var y; + if(x<=0) + this.pos=0; + else if(x>=(y=this.string.length-1)) + this.pos=y; + else + this.pos=x;}; diff --git a/mode/pig/pig.js b/mode/pig/pig.js index f8818a9b66..c2f611a1a0 100644 --- a/mode/pig/pig.js +++ b/mode/pig/pig.js @@ -1,171 +1,171 @@ /* - * Pig Latin Mode for CodeMirror 2 - * @author Prasanth Jayachandran - * @link https://github.com/prasanthj/pig-codemirror-2 + * Pig Latin Mode for CodeMirror 2 + * @author Prasanth Jayachandran + * @link https://github.com/prasanthj/pig-codemirror-2 * This implementation is adapted from PL/SQL mode in CodeMirror 2. -*/ + */ CodeMirror.defineMode("pig", function(_config, parserConfig) { - var keywords = parserConfig.keywords, - builtins = parserConfig.builtins, - types = parserConfig.types, - multiLineStrings = parserConfig.multiLineStrings; - - var isOperatorChar = /[*+\-%<>=&?:\/!|]/; - - function chain(stream, state, f) { - state.tokenize = f; - return f(stream, state); - } - - var type; - function ret(tp, style) { - type = tp; - return style; - } - - function tokenComment(stream, state) { - var isEnd = false; - var ch; - while(ch = stream.next()) { - if(ch == "/" && isEnd) { - state.tokenize = tokenBase; - break; - } - isEnd = (ch == "*"); - } - return ret("comment", "comment"); - } - - function tokenString(quote) { - return function(stream, state) { - var escaped = false, next, end = false; - while((next = stream.next()) != null) { - if (next == quote && !escaped) { - end = true; break; - } - escaped = !escaped && next == "\\"; - } - if (end || !(escaped || multiLineStrings)) - state.tokenize = tokenBase; - return ret("string", "error"); - }; - } - - function tokenBase(stream, state) { - var ch = stream.next(); - - // is a start of string? - if (ch == '"' || ch == "'") - return chain(stream, state, tokenString(ch)); - // is it one of the special chars - else if(/[\[\]{}\(\),;\.]/.test(ch)) - return ret(ch); - // is it a number? - else if(/\d/.test(ch)) { - stream.eatWhile(/[\w\.]/); - return ret("number", "number"); - } - // multi line comment or operator - else if (ch == "/") { - if (stream.eat("*")) { - return chain(stream, state, tokenComment); - } - else { - stream.eatWhile(isOperatorChar); - return ret("operator", "operator"); - } - } - // single line comment or operator - else if (ch=="-") { - if(stream.eat("-")){ - stream.skipToEnd(); - return ret("comment", "comment"); - } - else { - stream.eatWhile(isOperatorChar); - return ret("operator", "operator"); - } - } - // is it an operator - else if (isOperatorChar.test(ch)) { - stream.eatWhile(isOperatorChar); - return ret("operator", "operator"); - } - else { - // get the while word - stream.eatWhile(/[\w\$_]/); - // is it one of the listed keywords? - if (keywords && keywords.propertyIsEnumerable(stream.current().toUpperCase())) { - if (stream.eat(")") || stream.eat(".")) { - //keywords can be used as variables like flatten(group), group.$0 etc.. - } - else { - return ("keyword", "keyword"); - } - } - // is it one of the builtin functions? - if (builtins && builtins.propertyIsEnumerable(stream.current().toUpperCase())) - { - return ("keyword", "variable-2"); - } - // is it one of the listed types? - if (types && types.propertyIsEnumerable(stream.current().toUpperCase())) - return ("keyword", "variable-3"); - // default is a 'variable' - return ret("variable", "pig-word"); - } - } - - // Interface - return { - startState: function() { - return { - tokenize: tokenBase, - startOfLine: true - }; - }, - - token: function(stream, state) { - if(stream.eatSpace()) return null; - var style = state.tokenize(stream, state); - return style; - } - }; + var keywords = parserConfig.keywords, + builtins = parserConfig.builtins, + types = parserConfig.types, + multiLineStrings = parserConfig.multiLineStrings; + + var isOperatorChar = /[*+\-%<>=&?:\/!|]/; + + function chain(stream, state, f) { + state.tokenize = f; + return f(stream, state); + } + + var type; + function ret(tp, style) { + type = tp; + return style; + } + + function tokenComment(stream, state) { + var isEnd = false; + var ch; + while(ch = stream.next()) { + if(ch == "/" && isEnd) { + state.tokenize = tokenBase; + break; + } + isEnd = (ch == "*"); + } + return ret("comment", "comment"); + } + + function tokenString(quote) { + return function(stream, state) { + var escaped = false, next, end = false; + while((next = stream.next()) != null) { + if (next == quote && !escaped) { + end = true; break; + } + escaped = !escaped && next == "\\"; + } + if (end || !(escaped || multiLineStrings)) + state.tokenize = tokenBase; + return ret("string", "error"); + }; + } + + function tokenBase(stream, state) { + var ch = stream.next(); + + // is a start of string? + if (ch == '"' || ch == "'") + return chain(stream, state, tokenString(ch)); + // is it one of the special chars + else if(/[\[\]{}\(\),;\.]/.test(ch)) + return ret(ch); + // is it a number? + else if(/\d/.test(ch)) { + stream.eatWhile(/[\w\.]/); + return ret("number", "number"); + } + // multi line comment or operator + else if (ch == "/") { + if (stream.eat("*")) { + return chain(stream, state, tokenComment); + } + else { + stream.eatWhile(isOperatorChar); + return ret("operator", "operator"); + } + } + // single line comment or operator + else if (ch=="-") { + if(stream.eat("-")){ + stream.skipToEnd(); + return ret("comment", "comment"); + } + else { + stream.eatWhile(isOperatorChar); + return ret("operator", "operator"); + } + } + // is it an operator + else if (isOperatorChar.test(ch)) { + stream.eatWhile(isOperatorChar); + return ret("operator", "operator"); + } + else { + // get the while word + stream.eatWhile(/[\w\$_]/); + // is it one of the listed keywords? + if (keywords && keywords.propertyIsEnumerable(stream.current().toUpperCase())) { + if (stream.eat(")") || stream.eat(".")) { + //keywords can be used as variables like flatten(group), group.$0 etc.. + } + else { + return ("keyword", "keyword"); + } + } + // is it one of the builtin functions? + if (builtins && builtins.propertyIsEnumerable(stream.current().toUpperCase())) + { + return ("keyword", "variable-2"); + } + // is it one of the listed types? + if (types && types.propertyIsEnumerable(stream.current().toUpperCase())) + return ("keyword", "variable-3"); + // default is a 'variable' + return ret("variable", "pig-word"); + } + } + + // Interface + return { + startState: function() { + return { + tokenize: tokenBase, + startOfLine: true + }; + }, + + token: function(stream, state) { + if(stream.eatSpace()) return null; + var style = state.tokenize(stream, state); + return style; + } + }; }); (function() { - function keywords(str) { - var obj = {}, words = str.split(" "); - for (var i = 0; i < words.length; ++i) obj[words[i]] = true; - return obj; - } + function keywords(str) { + var obj = {}, words = str.split(" "); + for (var i = 0; i < words.length; ++i) obj[words[i]] = true; + return obj; + } + + // builtin funcs taken from trunk revision 1303237 + var pBuiltins = "ABS ACOS ARITY ASIN ATAN AVG BAGSIZE BINSTORAGE BLOOM BUILDBLOOM CBRT CEIL " + + "CONCAT COR COS COSH COUNT COUNT_STAR COV CONSTANTSIZE CUBEDIMENSIONS DIFF DISTINCT DOUBLEABS " + + "DOUBLEAVG DOUBLEBASE DOUBLEMAX DOUBLEMIN DOUBLEROUND DOUBLESUM EXP FLOOR FLOATABS FLOATAVG " + + "FLOATMAX FLOATMIN FLOATROUND FLOATSUM GENERICINVOKER INDEXOF INTABS INTAVG INTMAX INTMIN " + + "INTSUM INVOKEFORDOUBLE INVOKEFORFLOAT INVOKEFORINT INVOKEFORLONG INVOKEFORSTRING INVOKER " + + "ISEMPTY JSONLOADER JSONMETADATA JSONSTORAGE LAST_INDEX_OF LCFIRST LOG LOG10 LOWER LONGABS " + + "LONGAVG LONGMAX LONGMIN LONGSUM MAX MIN MAPSIZE MONITOREDUDF NONDETERMINISTIC OUTPUTSCHEMA " + + "PIGSTORAGE PIGSTREAMING RANDOM REGEX_EXTRACT REGEX_EXTRACT_ALL REPLACE ROUND SIN SINH SIZE " + + "SQRT STRSPLIT SUBSTRING SUM STRINGCONCAT STRINGMAX STRINGMIN STRINGSIZE TAN TANH TOBAG " + + "TOKENIZE TOMAP TOP TOTUPLE TRIM TEXTLOADER TUPLESIZE UCFIRST UPPER UTF8STORAGECONVERTER "; + + // taken from QueryLexer.g + var pKeywords = "VOID IMPORT RETURNS DEFINE LOAD FILTER FOREACH ORDER CUBE DISTINCT COGROUP " + + "JOIN CROSS UNION SPLIT INTO IF OTHERWISE ALL AS BY USING INNER OUTER ONSCHEMA PARALLEL " + + "PARTITION GROUP AND OR NOT GENERATE FLATTEN ASC DESC IS STREAM THROUGH STORE MAPREDUCE " + + "SHIP CACHE INPUT OUTPUT STDERROR STDIN STDOUT LIMIT SAMPLE LEFT RIGHT FULL EQ GT LT GTE LTE " + + "NEQ MATCHES TRUE FALSE "; + + // data types + var pTypes = "BOOLEAN INT LONG FLOAT DOUBLE CHARARRAY BYTEARRAY BAG TUPLE MAP "; - // builtin funcs taken from trunk revision 1303237 - var pBuiltins = "ABS ACOS ARITY ASIN ATAN AVG BAGSIZE BINSTORAGE BLOOM BUILDBLOOM CBRT CEIL " - + "CONCAT COR COS COSH COUNT COUNT_STAR COV CONSTANTSIZE CUBEDIMENSIONS DIFF DISTINCT DOUBLEABS " - + "DOUBLEAVG DOUBLEBASE DOUBLEMAX DOUBLEMIN DOUBLEROUND DOUBLESUM EXP FLOOR FLOATABS FLOATAVG " - + "FLOATMAX FLOATMIN FLOATROUND FLOATSUM GENERICINVOKER INDEXOF INTABS INTAVG INTMAX INTMIN " - + "INTSUM INVOKEFORDOUBLE INVOKEFORFLOAT INVOKEFORINT INVOKEFORLONG INVOKEFORSTRING INVOKER " - + "ISEMPTY JSONLOADER JSONMETADATA JSONSTORAGE LAST_INDEX_OF LCFIRST LOG LOG10 LOWER LONGABS " - + "LONGAVG LONGMAX LONGMIN LONGSUM MAX MIN MAPSIZE MONITOREDUDF NONDETERMINISTIC OUTPUTSCHEMA " - + "PIGSTORAGE PIGSTREAMING RANDOM REGEX_EXTRACT REGEX_EXTRACT_ALL REPLACE ROUND SIN SINH SIZE " - + "SQRT STRSPLIT SUBSTRING SUM STRINGCONCAT STRINGMAX STRINGMIN STRINGSIZE TAN TANH TOBAG " - + "TOKENIZE TOMAP TOP TOTUPLE TRIM TEXTLOADER TUPLESIZE UCFIRST UPPER UTF8STORAGECONVERTER "; - - // taken from QueryLexer.g - var pKeywords = "VOID IMPORT RETURNS DEFINE LOAD FILTER FOREACH ORDER CUBE DISTINCT COGROUP " - + "JOIN CROSS UNION SPLIT INTO IF OTHERWISE ALL AS BY USING INNER OUTER ONSCHEMA PARALLEL " - + "PARTITION GROUP AND OR NOT GENERATE FLATTEN ASC DESC IS STREAM THROUGH STORE MAPREDUCE " - + "SHIP CACHE INPUT OUTPUT STDERROR STDIN STDOUT LIMIT SAMPLE LEFT RIGHT FULL EQ GT LT GTE LTE " - + "NEQ MATCHES TRUE FALSE "; - - // data types - var pTypes = "BOOLEAN INT LONG FLOAT DOUBLE CHARARRAY BYTEARRAY BAG TUPLE MAP "; - - CodeMirror.defineMIME("text/x-pig", { - name: "pig", - builtins: keywords(pBuiltins), - keywords: keywords(pKeywords), - types: keywords(pTypes) - }); + CodeMirror.defineMIME("text/x-pig", { + name: "pig", + builtins: keywords(pBuiltins), + keywords: keywords(pKeywords), + types: keywords(pTypes) + }); }()); diff --git a/mode/smalltalk/smalltalk.js b/mode/smalltalk/smalltalk.js index 33ea11eae7..f8c9026bb3 100644 --- a/mode/smalltalk/smalltalk.js +++ b/mode/smalltalk/smalltalk.js @@ -1,139 +1,139 @@ CodeMirror.defineMode('smalltalk', function(config) { - var specialChars = /[+\-/\\*~<>=@%|&?!.:;^]/; - var keywords = /true|false|nil|self|super|thisContext/; - - var Context = function(tokenizer, parent) { - this.next = tokenizer; - this.parent = parent; - }; - - var Token = function(name, context, eos) { - this.name = name; - this.context = context; - this.eos = eos; - }; - - var State = function() { - this.context = new Context(next, null); - this.expectVariable = true; - this.indentation = 0; - this.userIndentationDelta = 0; - }; - - State.prototype.userIndent = function(indentation) { - this.userIndentationDelta = indentation > 0 ? (indentation / config.indentUnit - this.indentation) : 0; - }; - - var next = function(stream, context, state) { - var token = new Token(null, context, false); - var aChar = stream.next(); - - if (aChar === '"') { - token = nextComment(stream, new Context(nextComment, context)); - - } else if (aChar === '\'') { - token = nextString(stream, new Context(nextString, context)); - - } else if (aChar === '#') { - stream.eatWhile(/[^ .]/); - token.name = 'string-2'; - - } else if (aChar === '$') { - stream.eatWhile(/[^ ]/); - token.name = 'string-2'; - - } else if (aChar === '|' && state.expectVariable) { - token.context = new Context(nextTemporaries, context); - - } else if (/[\[\]{}()]/.test(aChar)) { - token.name = 'bracket'; - token.eos = /[\[{(]/.test(aChar); - - if (aChar === '[') { - state.indentation++; - } else if (aChar === ']') { - state.indentation = Math.max(0, state.indentation - 1); - } - - } else if (specialChars.test(aChar)) { - stream.eatWhile(specialChars); - token.name = 'operator'; - token.eos = aChar !== ';'; // ; cascaded message expression - - } else if (/\d/.test(aChar)) { - stream.eatWhile(/[\w\d]/); - token.name = 'number'; - - } else if (/[\w_]/.test(aChar)) { - stream.eatWhile(/[\w\d_]/); - token.name = state.expectVariable ? (keywords.test(stream.current()) ? 'keyword' : 'variable') : null; - - } else { - token.eos = state.expectVariable; - } - - return token; - }; - - var nextComment = function(stream, context) { - stream.eatWhile(/[^"]/); - return new Token('comment', stream.eat('"') ? context.parent : context, true); - }; - - var nextString = function(stream, context) { - stream.eatWhile(/[^']/); - return new Token('string', stream.eat('\'') ? context.parent : context, false); - }; - - var nextTemporaries = function(stream, context) { - var token = new Token(null, context, false); - var aChar = stream.next(); - - if (aChar === '|') { - token.context = context.parent; - token.eos = true; - - } else { - stream.eatWhile(/[^|]/); - token.name = 'variable'; - } - - return token; - }; - - return { - startState: function() { - return new State; - }, - - token: function(stream, state) { - state.userIndent(stream.indentation()); - - if (stream.eatSpace()) { - return null; - } + var specialChars = /[+\-\/\\*~<>=@%|&?!.:;^]/; + var keywords = /true|false|nil|self|super|thisContext/; + + var Context = function(tokenizer, parent) { + this.next = tokenizer; + this.parent = parent; + }; + + var Token = function(name, context, eos) { + this.name = name; + this.context = context; + this.eos = eos; + }; + + var State = function() { + this.context = new Context(next, null); + this.expectVariable = true; + this.indentation = 0; + this.userIndentationDelta = 0; + }; + + State.prototype.userIndent = function(indentation) { + this.userIndentationDelta = indentation > 0 ? (indentation / config.indentUnit - this.indentation) : 0; + }; + + var next = function(stream, context, state) { + var token = new Token(null, context, false); + var aChar = stream.next(); + + if (aChar === '"') { + token = nextComment(stream, new Context(nextComment, context)); + + } else if (aChar === '\'') { + token = nextString(stream, new Context(nextString, context)); + + } else if (aChar === '#') { + stream.eatWhile(/[^ .]/); + token.name = 'string-2'; + + } else if (aChar === '$') { + stream.eatWhile(/[^ ]/); + token.name = 'string-2'; + + } else if (aChar === '|' && state.expectVariable) { + token.context = new Context(nextTemporaries, context); + + } else if (/[\[\]{}()]/.test(aChar)) { + token.name = 'bracket'; + token.eos = /[\[{(]/.test(aChar); + + if (aChar === '[') { + state.indentation++; + } else if (aChar === ']') { + state.indentation = Math.max(0, state.indentation - 1); + } + + } else if (specialChars.test(aChar)) { + stream.eatWhile(specialChars); + token.name = 'operator'; + token.eos = aChar !== ';'; // ; cascaded message expression + + } else if (/\d/.test(aChar)) { + stream.eatWhile(/[\w\d]/); + token.name = 'number'; + + } else if (/[\w_]/.test(aChar)) { + stream.eatWhile(/[\w\d_]/); + token.name = state.expectVariable ? (keywords.test(stream.current()) ? 'keyword' : 'variable') : null; + + } else { + token.eos = state.expectVariable; + } + + return token; + }; + + var nextComment = function(stream, context) { + stream.eatWhile(/[^"]/); + return new Token('comment', stream.eat('"') ? context.parent : context, true); + }; + + var nextString = function(stream, context) { + stream.eatWhile(/[^']/); + return new Token('string', stream.eat('\'') ? context.parent : context, false); + }; + + var nextTemporaries = function(stream, context) { + var token = new Token(null, context, false); + var aChar = stream.next(); + + if (aChar === '|') { + token.context = context.parent; + token.eos = true; + + } else { + stream.eatWhile(/[^|]/); + token.name = 'variable'; + } + + return token; + }; + + return { + startState: function() { + return new State; + }, + + token: function(stream, state) { + state.userIndent(stream.indentation()); + + if (stream.eatSpace()) { + return null; + } - var token = state.context.next(stream, state.context, state); - state.context = token.context; - state.expectVariable = token.eos; + var token = state.context.next(stream, state.context, state); + state.context = token.context; + state.expectVariable = token.eos; - state.lastToken = token; - return token.name; - }, + state.lastToken = token; + return token.name; + }, - blankLine: function(state) { - state.userIndent(0); - }, + blankLine: function(state) { + state.userIndent(0); + }, - indent: function(state, textAfter) { - var i = state.context.next === next && textAfter && textAfter.charAt(0) === ']' ? -1 : state.userIndentationDelta; - return (state.indentation + i) * config.indentUnit; - }, + indent: function(state, textAfter) { + var i = state.context.next === next && textAfter && textAfter.charAt(0) === ']' ? -1 : state.userIndentationDelta; + return (state.indentation + i) * config.indentUnit; + }, - electricChars: ']' - }; + electricChars: ']' + }; }); -CodeMirror.defineMIME('text/x-stsrc', {name: 'smalltalk'}); \ No newline at end of file +CodeMirror.defineMIME('text/x-stsrc', {name: 'smalltalk'}); diff --git a/mode/smarty/smarty.js b/mode/smarty/smarty.js index 9ee1e4851c..7d7e62f86e 100644 --- a/mode/smarty/smarty.js +++ b/mode/smarty/smarty.js @@ -88,7 +88,7 @@ CodeMirror.defineMode("smarty", function(config) { var str = ""; if (ch != "/") { - str += ch; + str += ch; } var c = ""; while ((c = stream.eat(regs.validIdentifier))) { @@ -101,7 +101,7 @@ CodeMirror.defineMode("smarty", function(config) { } } if (/\s/.test(ch)) { - return null; + return null; } return ret("tag", "tag"); } @@ -145,4 +145,4 @@ CodeMirror.defineMode("smarty", function(config) { }; }); -CodeMirror.defineMIME("text/x-smarty", "smarty"); \ No newline at end of file +CodeMirror.defineMIME("text/x-smarty", "smarty"); diff --git a/mode/tiddlywiki/tiddlywiki.js b/mode/tiddlywiki/tiddlywiki.js index 0d506ee608..24a24786bc 100644 --- a/mode/tiddlywiki/tiddlywiki.js +++ b/mode/tiddlywiki/tiddlywiki.js @@ -1,352 +1,352 @@ /*** -|''Name''|tiddlywiki.js| -|''Description''|Enables TiddlyWikiy syntax highlighting using CodeMirror| -|''Author''|PMario| -|''Version''|0.1.7| -|''Status''|''stable''| -|''Source''|[[GitHub|https://github.com/pmario/CodeMirror2/blob/tw-syntax/mode/tiddlywiki]]| -|''Documentation''|http://codemirror.tiddlyspace.com/| -|''License''|[[MIT License|http://www.opensource.org/licenses/mit-license.php]]| -|''CoreVersion''|2.5.0| -|''Requires''|codemirror.js| -|''Keywords''|syntax highlighting color code mirror codemirror| -! Info -CoreVersion parameter is needed for TiddlyWiki only! + |''Name''|tiddlywiki.js| + |''Description''|Enables TiddlyWikiy syntax highlighting using CodeMirror| + |''Author''|PMario| + |''Version''|0.1.7| + |''Status''|''stable''| + |''Source''|[[GitHub|https://github.com/pmario/CodeMirror2/blob/tw-syntax/mode/tiddlywiki]]| + |''Documentation''|http://codemirror.tiddlyspace.com/| + |''License''|[[MIT License|http://www.opensource.org/licenses/mit-license.php]]| + |''CoreVersion''|2.5.0| + |''Requires''|codemirror.js| + |''Keywords''|syntax highlighting color code mirror codemirror| + ! Info + CoreVersion parameter is needed for TiddlyWiki only! ***/ //{{{ CodeMirror.defineMode("tiddlywiki", function () { - // Tokenizer - var textwords = {}; - - var keywords = function () { - function kw(type) { - return { type: type, style: "macro"}; - } - return { - "allTags": kw('allTags'), "closeAll": kw('closeAll'), "list": kw('list'), - "newJournal": kw('newJournal'), "newTiddler": kw('newTiddler'), - "permaview": kw('permaview'), "saveChanges": kw('saveChanges'), - "search": kw('search'), "slider": kw('slider'), "tabs": kw('tabs'), - "tag": kw('tag'), "tagging": kw('tagging'), "tags": kw('tags'), - "tiddler": kw('tiddler'), "timeline": kw('timeline'), - "today": kw('today'), "version": kw('version'), "option": kw('option'), - - "with": kw('with'), - "filter": kw('filter') - }; - }(); - - var isSpaceName = /[\w_\-]/i, - reHR = /^\-\-\-\-+$/, //
    - reWikiCommentStart = /^\/\*\*\*$/, // /*** - reWikiCommentStop = /^\*\*\*\/$/, // ***/ - reBlockQuote = /^<<<$/, - - reJsCodeStart = /^\/\/\{\{\{$/, // //{{{ js block start - reJsCodeStop = /^\/\/\}\}\}$/, // //}}} js stop - reXmlCodeStart = /^$/, // xml block start - reXmlCodeStop = /^$/, // xml stop - - reCodeBlockStart = /^\{\{\{$/, // {{{ TW text div block start - reCodeBlockStop = /^\}\}\}$/, // }}} TW text stop - - reUntilCodeStop = /.*?\}\}\}/; - - function chain(stream, state, f) { - state.tokenize = f; - return f(stream, state); - } - - // Used as scratch variables to communicate multiple values without - // consing up tons of objects. - var type, content; - - function ret(tp, style, cont) { - type = tp; - content = cont; - return style; - } - - function jsTokenBase(stream, state) { - var sol = stream.sol(), ch; - - state.block = false; // indicates the start of a code block. - - ch = stream.peek(); // don't eat, to make matching simpler - - // check start of blocks - if (sol && /[<\/\*{}\-]/.test(ch)) { - if (stream.match(reCodeBlockStart)) { - state.block = true; - return chain(stream, state, twTokenCode); - } - if (stream.match(reBlockQuote)) { - return ret('quote', 'quote'); - } - if (stream.match(reWikiCommentStart) || stream.match(reWikiCommentStop)) { - return ret('code', 'comment'); - } - if (stream.match(reJsCodeStart) || stream.match(reJsCodeStop) || stream.match(reXmlCodeStart) || stream.match(reXmlCodeStop)) { - return ret('code', 'comment'); - } - if (stream.match(reHR)) { - return ret('hr', 'hr'); - } - } // sol - ch = stream.next(); - - if (sol && /[\/\*!#;:>|]/.test(ch)) { - if (ch == "!") { // tw header - stream.skipToEnd(); - return ret("header", "header"); - } - if (ch == "*") { // tw list - stream.eatWhile('*'); - return ret("list", "comment"); - } - if (ch == "#") { // tw numbered list - stream.eatWhile('#'); - return ret("list", "comment"); - } - if (ch == ";") { // definition list, term - stream.eatWhile(';'); - return ret("list", "comment"); - } - if (ch == ":") { // definition list, description - stream.eatWhile(':'); - return ret("list", "comment"); - } - if (ch == ">") { // single line quote - stream.eatWhile(">"); - return ret("quote", "quote"); - } - if (ch == '|') { - return ret('table', 'header'); - } - } - - if (ch == '{' && stream.match(/\{\{/)) { - return chain(stream, state, twTokenCode); - } - - // rudimentary html:// file:// link matching. TW knows much more ... - if (/[hf]/i.test(ch)) { - if (/[ti]/i.test(stream.peek()) && stream.match(/\b(ttps?|tp|ile):\/\/[\-A-Z0-9+&@#\/%?=~_|$!:,.;]*[A-Z0-9+&@#\/%=~_|$]/i)) { - return ret("link", "link"); - } - } - // just a little string indicator, don't want to have the whole string covered - if (ch == '"') { - return ret('string', 'string'); - } - if (ch == '~') { // _no_ CamelCase indicator should be bold - return ret('text', 'brace'); - } - if (/[\[\]]/.test(ch)) { // check for [[..]] - if (stream.peek() == ch) { - stream.next(); - return ret('brace', 'brace'); - } - } - if (ch == "@") { // check for space link. TODO fix @@...@@ highlighting - stream.eatWhile(isSpaceName); - return ret("link", "link"); - } - if (/\d/.test(ch)) { // numbers - stream.eatWhile(/\d/); - return ret("number", "number"); - } - if (ch == "/") { // tw invisible comment - if (stream.eat("%")) { - return chain(stream, state, twTokenComment); - } - else if (stream.eat("/")) { // - return chain(stream, state, twTokenEm); - } - } - if (ch == "_") { // tw underline - if (stream.eat("_")) { - return chain(stream, state, twTokenUnderline); - } - } - // strikethrough and mdash handling - if (ch == "-") { - if (stream.eat("-")) { - // if strikethrough looks ugly, change CSS. - if (stream.peek() != ' ') - return chain(stream, state, twTokenStrike); - // mdash - if (stream.peek() == ' ') - return ret('text', 'brace'); - } - } - if (ch == "'") { // tw bold - if (stream.eat("'")) { - return chain(stream, state, twTokenStrong); - } - } - if (ch == "<") { // tw macro - if (stream.eat("<")) { - return chain(stream, state, twTokenMacro); - } - } - else { - return ret(ch); - } - - // core macro handling - stream.eatWhile(/[\w\$_]/); - var word = stream.current(), - known = textwords.propertyIsEnumerable(word) && textwords[word]; - - return known ? ret(known.type, known.style, word) : ret("text", null, word); - - } // jsTokenBase() - - // tw invisible comment - function twTokenComment(stream, state) { - var maybeEnd = false, - ch; - while (ch = stream.next()) { - if (ch == "/" && maybeEnd) { - state.tokenize = jsTokenBase; - break; - } - maybeEnd = (ch == "%"); - } - return ret("comment", "comment"); - } - - // tw strong / bold - function twTokenStrong(stream, state) { - var maybeEnd = false, - ch; - while (ch = stream.next()) { - if (ch == "'" && maybeEnd) { - state.tokenize = jsTokenBase; - break; - } - maybeEnd = (ch == "'"); - } - return ret("text", "strong"); - } - - // tw code - function twTokenCode(stream, state) { - var ch, sb = state.block; - - if (sb && stream.current()) { - return ret("code", "comment"); - } - - if (!sb && stream.match(reUntilCodeStop)) { - state.tokenize = jsTokenBase; - return ret("code", "comment"); - } - - if (sb && stream.sol() && stream.match(reCodeBlockStop)) { - state.tokenize = jsTokenBase; - return ret("code", "comment"); - } - - ch = stream.next(); - return (sb) ? ret("code", "comment") : ret("code", "comment"); - } - - // tw em / italic - function twTokenEm(stream, state) { - var maybeEnd = false, - ch; - while (ch = stream.next()) { - if (ch == "/" && maybeEnd) { - state.tokenize = jsTokenBase; - break; - } - maybeEnd = (ch == "/"); - } - return ret("text", "em"); - } - - // tw underlined text - function twTokenUnderline(stream, state) { - var maybeEnd = false, - ch; - while (ch = stream.next()) { - if (ch == "_" && maybeEnd) { - state.tokenize = jsTokenBase; - break; - } - maybeEnd = (ch == "_"); - } - return ret("text", "underlined"); - } - - // tw strike through text looks ugly - // change CSS if needed - function twTokenStrike(stream, state) { - var maybeEnd = false, ch; - - while (ch = stream.next()) { - if (ch == "-" && maybeEnd) { - state.tokenize = jsTokenBase; - break; - } - maybeEnd = (ch == "-"); - } - return ret("text", "strikethrough"); - } - - // macro - function twTokenMacro(stream, state) { - var ch, word, known; - - if (stream.current() == '<<') { - return ret('brace', 'macro'); - } - - ch = stream.next(); - if (!ch) { - state.tokenize = jsTokenBase; - return ret(ch); - } - if (ch == ">") { - if (stream.peek() == '>') { - stream.next(); - state.tokenize = jsTokenBase; - return ret("brace", "macro"); - } - } - - stream.eatWhile(/[\w\$_]/); - word = stream.current(); - known = keywords.propertyIsEnumerable(word) && keywords[word]; - - if (known) { - return ret(known.type, known.style, word); - } - else { - return ret("macro", null, word); - } - } - - // Interface - return { - startState: function () { - return { - tokenize: jsTokenBase, - indented: 0, - level: 0 - }; - }, - - token: function (stream, state) { - if (stream.eatSpace()) return null; - var style = state.tokenize(stream, state); - return style; - }, - - electricChars: "" - }; + // Tokenizer + var textwords = {}; + + var keywords = function () { + function kw(type) { + return { type: type, style: "macro"}; + } + return { + "allTags": kw('allTags'), "closeAll": kw('closeAll'), "list": kw('list'), + "newJournal": kw('newJournal'), "newTiddler": kw('newTiddler'), + "permaview": kw('permaview'), "saveChanges": kw('saveChanges'), + "search": kw('search'), "slider": kw('slider'), "tabs": kw('tabs'), + "tag": kw('tag'), "tagging": kw('tagging'), "tags": kw('tags'), + "tiddler": kw('tiddler'), "timeline": kw('timeline'), + "today": kw('today'), "version": kw('version'), "option": kw('option'), + + "with": kw('with'), + "filter": kw('filter') + }; + }(); + + var isSpaceName = /[\w_\-]/i, + reHR = /^\-\-\-\-+$/, //
    + reWikiCommentStart = /^\/\*\*\*$/, // /*** + reWikiCommentStop = /^\*\*\*\/$/, // ***/ + reBlockQuote = /^<<<$/, + + reJsCodeStart = /^\/\/\{\{\{$/, // //{{{ js block start + reJsCodeStop = /^\/\/\}\}\}$/, // //}}} js stop + reXmlCodeStart = /^$/, // xml block start + reXmlCodeStop = /^$/, // xml stop + + reCodeBlockStart = /^\{\{\{$/, // {{{ TW text div block start + reCodeBlockStop = /^\}\}\}$/, // }}} TW text stop + + reUntilCodeStop = /.*?\}\}\}/; + + function chain(stream, state, f) { + state.tokenize = f; + return f(stream, state); + } + + // Used as scratch variables to communicate multiple values without + // consing up tons of objects. + var type, content; + + function ret(tp, style, cont) { + type = tp; + content = cont; + return style; + } + + function jsTokenBase(stream, state) { + var sol = stream.sol(), ch; + + state.block = false; // indicates the start of a code block. + + ch = stream.peek(); // don't eat, to make matching simpler + + // check start of blocks + if (sol && /[<\/\*{}\-]/.test(ch)) { + if (stream.match(reCodeBlockStart)) { + state.block = true; + return chain(stream, state, twTokenCode); + } + if (stream.match(reBlockQuote)) { + return ret('quote', 'quote'); + } + if (stream.match(reWikiCommentStart) || stream.match(reWikiCommentStop)) { + return ret('code', 'comment'); + } + if (stream.match(reJsCodeStart) || stream.match(reJsCodeStop) || stream.match(reXmlCodeStart) || stream.match(reXmlCodeStop)) { + return ret('code', 'comment'); + } + if (stream.match(reHR)) { + return ret('hr', 'hr'); + } + } // sol + ch = stream.next(); + + if (sol && /[\/\*!#;:>|]/.test(ch)) { + if (ch == "!") { // tw header + stream.skipToEnd(); + return ret("header", "header"); + } + if (ch == "*") { // tw list + stream.eatWhile('*'); + return ret("list", "comment"); + } + if (ch == "#") { // tw numbered list + stream.eatWhile('#'); + return ret("list", "comment"); + } + if (ch == ";") { // definition list, term + stream.eatWhile(';'); + return ret("list", "comment"); + } + if (ch == ":") { // definition list, description + stream.eatWhile(':'); + return ret("list", "comment"); + } + if (ch == ">") { // single line quote + stream.eatWhile(">"); + return ret("quote", "quote"); + } + if (ch == '|') { + return ret('table', 'header'); + } + } + + if (ch == '{' && stream.match(/\{\{/)) { + return chain(stream, state, twTokenCode); + } + + // rudimentary html:// file:// link matching. TW knows much more ... + if (/[hf]/i.test(ch)) { + if (/[ti]/i.test(stream.peek()) && stream.match(/\b(ttps?|tp|ile):\/\/[\-A-Z0-9+&@#\/%?=~_|$!:,.;]*[A-Z0-9+&@#\/%=~_|$]/i)) { + return ret("link", "link"); + } + } + // just a little string indicator, don't want to have the whole string covered + if (ch == '"') { + return ret('string', 'string'); + } + if (ch == '~') { // _no_ CamelCase indicator should be bold + return ret('text', 'brace'); + } + if (/[\[\]]/.test(ch)) { // check for [[..]] + if (stream.peek() == ch) { + stream.next(); + return ret('brace', 'brace'); + } + } + if (ch == "@") { // check for space link. TODO fix @@...@@ highlighting + stream.eatWhile(isSpaceName); + return ret("link", "link"); + } + if (/\d/.test(ch)) { // numbers + stream.eatWhile(/\d/); + return ret("number", "number"); + } + if (ch == "/") { // tw invisible comment + if (stream.eat("%")) { + return chain(stream, state, twTokenComment); + } + else if (stream.eat("/")) { // + return chain(stream, state, twTokenEm); + } + } + if (ch == "_") { // tw underline + if (stream.eat("_")) { + return chain(stream, state, twTokenUnderline); + } + } + // strikethrough and mdash handling + if (ch == "-") { + if (stream.eat("-")) { + // if strikethrough looks ugly, change CSS. + if (stream.peek() != ' ') + return chain(stream, state, twTokenStrike); + // mdash + if (stream.peek() == ' ') + return ret('text', 'brace'); + } + } + if (ch == "'") { // tw bold + if (stream.eat("'")) { + return chain(stream, state, twTokenStrong); + } + } + if (ch == "<") { // tw macro + if (stream.eat("<")) { + return chain(stream, state, twTokenMacro); + } + } + else { + return ret(ch); + } + + // core macro handling + stream.eatWhile(/[\w\$_]/); + var word = stream.current(), + known = textwords.propertyIsEnumerable(word) && textwords[word]; + + return known ? ret(known.type, known.style, word) : ret("text", null, word); + + } // jsTokenBase() + + // tw invisible comment + function twTokenComment(stream, state) { + var maybeEnd = false, + ch; + while (ch = stream.next()) { + if (ch == "/" && maybeEnd) { + state.tokenize = jsTokenBase; + break; + } + maybeEnd = (ch == "%"); + } + return ret("comment", "comment"); + } + + // tw strong / bold + function twTokenStrong(stream, state) { + var maybeEnd = false, + ch; + while (ch = stream.next()) { + if (ch == "'" && maybeEnd) { + state.tokenize = jsTokenBase; + break; + } + maybeEnd = (ch == "'"); + } + return ret("text", "strong"); + } + + // tw code + function twTokenCode(stream, state) { + var ch, sb = state.block; + + if (sb && stream.current()) { + return ret("code", "comment"); + } + + if (!sb && stream.match(reUntilCodeStop)) { + state.tokenize = jsTokenBase; + return ret("code", "comment"); + } + + if (sb && stream.sol() && stream.match(reCodeBlockStop)) { + state.tokenize = jsTokenBase; + return ret("code", "comment"); + } + + ch = stream.next(); + return (sb) ? ret("code", "comment") : ret("code", "comment"); + } + + // tw em / italic + function twTokenEm(stream, state) { + var maybeEnd = false, + ch; + while (ch = stream.next()) { + if (ch == "/" && maybeEnd) { + state.tokenize = jsTokenBase; + break; + } + maybeEnd = (ch == "/"); + } + return ret("text", "em"); + } + + // tw underlined text + function twTokenUnderline(stream, state) { + var maybeEnd = false, + ch; + while (ch = stream.next()) { + if (ch == "_" && maybeEnd) { + state.tokenize = jsTokenBase; + break; + } + maybeEnd = (ch == "_"); + } + return ret("text", "underlined"); + } + + // tw strike through text looks ugly + // change CSS if needed + function twTokenStrike(stream, state) { + var maybeEnd = false, ch; + + while (ch = stream.next()) { + if (ch == "-" && maybeEnd) { + state.tokenize = jsTokenBase; + break; + } + maybeEnd = (ch == "-"); + } + return ret("text", "strikethrough"); + } + + // macro + function twTokenMacro(stream, state) { + var ch, word, known; + + if (stream.current() == '<<') { + return ret('brace', 'macro'); + } + + ch = stream.next(); + if (!ch) { + state.tokenize = jsTokenBase; + return ret(ch); + } + if (ch == ">") { + if (stream.peek() == '>') { + stream.next(); + state.tokenize = jsTokenBase; + return ret("brace", "macro"); + } + } + + stream.eatWhile(/[\w\$_]/); + word = stream.current(); + known = keywords.propertyIsEnumerable(word) && keywords[word]; + + if (known) { + return ret(known.type, known.style, word); + } + else { + return ret("macro", null, word); + } + } + + // Interface + return { + startState: function () { + return { + tokenize: jsTokenBase, + indented: 0, + level: 0 + }; + }, + + token: function (stream, state) { + if (stream.eatSpace()) return null; + var style = state.tokenize(stream, state); + return style; + }, + + electricChars: "" + }; }); CodeMirror.defineMIME("text/x-tiddlywiki", "tiddlywiki"); diff --git a/mode/tiki/tiki.js b/mode/tiki/tiki.js index 81e87ab213..e789163dc7 100644 --- a/mode/tiki/tiki.js +++ b/mode/tiki/tiki.js @@ -1,309 +1,308 @@ CodeMirror.defineMode('tiki', function(config) { - function inBlock(style, terminator, returnTokenizer) { - return function(stream, state) { - while (!stream.eol()) { - if (stream.match(terminator)) { - state.tokenize = inText; - break; - } - stream.next(); - } - - if (returnTokenizer) state.tokenize = returnTokenizer; - - return style; - }; - } - - function inLine(style) { - return function(stream, state) { - while(!stream.eol()) { - stream.next(); - } - state.tokenize = inText; - return style; - }; - } - - function inText(stream, state) { - function chain(parser) { - state.tokenize = parser; - return parser(stream, state); - } - - var sol = stream.sol(); - var ch = stream.next(); - - //non start of line - switch (ch) { //switch is generally much faster than if, so it is used here - case "{": //plugin - stream.eat("/"); - stream.eatSpace(); - var tagName = ""; - var c; - while ((c = stream.eat(/[^\s\u00a0=\"\'\/?(}]/))) tagName += c; - state.tokenize = inPlugin; - return "tag"; - break; - case "_": //bold - if (stream.eat("_")) { - return chain(inBlock("strong", "__", inText)); - } - break; - case "'": //italics - if (stream.eat("'")) { - // Italic text - return chain(inBlock("em", "''", inText)); - } - break; - case "(":// Wiki Link - if (stream.eat("(")) { - return chain(inBlock("variable-2", "))", inText)); - } - break; - case "[":// Weblink - return chain(inBlock("variable-3", "]", inText)); - break; - case "|": //table - if (stream.eat("|")) { - return chain(inBlock("comment", "||")); - } - break; - case "-": - if (stream.eat("=")) {//titleBar - return chain(inBlock("header string", "=-", inText)); - } else if (stream.eat("-")) {//deleted - return chain(inBlock("error tw-deleted", "--", inText)); - } - break; - case "=": //underline - if (stream.match("==")) { - return chain(inBlock("tw-underline", "===", inText)); - } - break; - case ":": - if (stream.eat(":")) { - return chain(inBlock("comment", "::")); - } - break; - case "^": //box - return chain(inBlock("tw-box", "^")); - break; - case "~": //np - if (stream.match("np~")) { - return chain(inBlock("meta", "~/np~")); - } - break; - } - - //start of line types - if (sol) { - switch (ch) { - case "!": //header at start of line - if (stream.match('!!!!!')) { - return chain(inLine("header string")); - } else if (stream.match('!!!!')) { - return chain(inLine("header string")); - } else if (stream.match('!!!')) { - return chain(inLine("header string")); - } else if (stream.match('!!')) { - return chain(inLine("header string")); - } else { - return chain(inLine("header string")); - } - break; - case "*": //unordered list line item, or
  • at start of line - case "#": //ordered list line item, or
  • at start of line - case "+": //ordered list line item, or
  • at start of line - return chain(inLine("tw-listitem bracket")); - break; - } - } - - //stream.eatWhile(/[&{]/); was eating up plugins, turned off to act less like html and more like tiki - return null; - } - - var indentUnit = config.indentUnit; + function inBlock(style, terminator, returnTokenizer) { + return function(stream, state) { + while (!stream.eol()) { + if (stream.match(terminator)) { + state.tokenize = inText; + break; + } + stream.next(); + } - // Return variables for tokenizers - var pluginName, type; - function inPlugin(stream, state) { - var ch = stream.next(); - var peek = stream.peek(); - - if (ch == "}") { - state.tokenize = inText; - //type = ch == ")" ? "endPlugin" : "selfclosePlugin"; inPlugin - return "tag"; - } else if (ch == "(" || ch == ")") { - return "bracket"; - } else if (ch == "=") { - type = "equals"; - - if (peek == ">") { - ch = stream.next(); - peek = stream.peek(); - } - - //here we detect values directly after equal character with no quotes - if (!/[\'\"]/.test(peek)) { - state.tokenize = inAttributeNoQuote(); - } - //end detect values - - return "operator"; - } else if (/[\'\"]/.test(ch)) { - state.tokenize = inAttribute(ch); - return state.tokenize(stream, state); - } else { - stream.eatWhile(/[^\s\u00a0=\"\'\/?]/); - return "keyword"; - } - } + if (returnTokenizer) state.tokenize = returnTokenizer; - function inAttribute(quote) { - return function(stream, state) { - while (!stream.eol()) { - if (stream.next() == quote) { - state.tokenize = inPlugin; - break; - } - } - return "string"; - }; - } - - function inAttributeNoQuote() { - return function(stream, state) { - while (!stream.eol()) { - var ch = stream.next(); - var peek = stream.peek(); - if (ch == " " || ch == "," || /[ )}]/.test(peek)) { - state.tokenize = inPlugin; - break; - } - } - return "string"; - }; - } + return style; + }; + } - var curState, setStyle; - function pass() { - for (var i = arguments.length - 1; i >= 0; i--) curState.cc.push(arguments[i]); - } - - function cont() { - pass.apply(null, arguments); - return true; - } + function inLine(style) { + return function(stream, state) { + while(!stream.eol()) { + stream.next(); + } + state.tokenize = inText; + return style; + }; + } - function pushContext(pluginName, startOfLine) { - var noIndent = curState.context && curState.context.noIndent; - curState.context = { - prev: curState.context, - pluginName: pluginName, - indent: curState.indented, - startOfLine: startOfLine, - noIndent: noIndent - }; - } - - function popContext() { - if (curState.context) curState.context = curState.context.prev; - } + function inText(stream, state) { + function chain(parser) { + state.tokenize = parser; + return parser(stream, state); + } - function element(type) { - if (type == "openPlugin") {curState.pluginName = pluginName; return cont(attributes, endplugin(curState.startOfLine));} - else if (type == "closePlugin") { - var err = false; - if (curState.context) { - err = curState.context.pluginName != pluginName; - popContext(); - } else { - err = true; - } - if (err) setStyle = "error"; - return cont(endcloseplugin(err)); - } - else if (type == "string") { - if (!curState.context || curState.context.name != "!cdata") pushContext("!cdata"); - if (curState.tokenize == inText) popContext(); - return cont(); - } - else return cont(); - } - - function endplugin(startOfLine) { - return function(type) { - if ( - type == "selfclosePlugin" || - type == "endPlugin" - ) - return cont(); - if (type == "endPlugin") {pushContext(curState.pluginName, startOfLine); return cont();} - return cont(); - }; - } - - function endcloseplugin(err) { - return function(type) { - if (err) setStyle = "error"; - if (type == "endPlugin") return cont(); - return pass(); - }; - } + var sol = stream.sol(); + var ch = stream.next(); - function attributes(type) { - if (type == "keyword") {setStyle = "attribute"; return cont(attributes);} - if (type == "equals") return cont(attvalue, attributes); - return pass(); - } - function attvalue(type) { - if (type == "keyword") {setStyle = "string"; return cont();} - if (type == "string") return cont(attvaluemaybe); - return pass(); - } - function attvaluemaybe(type) { - if (type == "string") return cont(attvaluemaybe); - else return pass(); - } - return { - startState: function() { - return {tokenize: inText, cc: [], indented: 0, startOfLine: true, pluginName: null, context: null}; - }, - token: function(stream, state) { - if (stream.sol()) { - state.startOfLine = true; - state.indented = stream.indentation(); - } - if (stream.eatSpace()) return null; + //non start of line + switch (ch) { //switch is generally much faster than if, so it is used here + case "{": //plugin + stream.eat("/"); + stream.eatSpace(); + var tagName = ""; + var c; + while ((c = stream.eat(/[^\s\u00a0=\"\'\/?(}]/))) tagName += c; + state.tokenize = inPlugin; + return "tag"; + break; + case "_": //bold + if (stream.eat("_")) { + return chain(inBlock("strong", "__", inText)); + } + break; + case "'": //italics + if (stream.eat("'")) { + // Italic text + return chain(inBlock("em", "''", inText)); + } + break; + case "(":// Wiki Link + if (stream.eat("(")) { + return chain(inBlock("variable-2", "))", inText)); + } + break; + case "[":// Weblink + return chain(inBlock("variable-3", "]", inText)); + break; + case "|": //table + if (stream.eat("|")) { + return chain(inBlock("comment", "||")); + } + break; + case "-": + if (stream.eat("=")) {//titleBar + return chain(inBlock("header string", "=-", inText)); + } else if (stream.eat("-")) {//deleted + return chain(inBlock("error tw-deleted", "--", inText)); + } + break; + case "=": //underline + if (stream.match("==")) { + return chain(inBlock("tw-underline", "===", inText)); + } + break; + case ":": + if (stream.eat(":")) { + return chain(inBlock("comment", "::")); + } + break; + case "^": //box + return chain(inBlock("tw-box", "^")); + break; + case "~": //np + if (stream.match("np~")) { + return chain(inBlock("meta", "~/np~")); + } + break; + } - setStyle = type = pluginName = null; - var style = state.tokenize(stream, state); - if ((style || type) && style != "comment") { - curState = state; - while (true) { - var comb = state.cc.pop() || element; - if (comb(type || style)) break; - } - } - state.startOfLine = false; - return setStyle || style; - }, - indent: function(state, textAfter) { - var context = state.context; - if (context && context.noIndent) return 0; - if (context && /^{\//.test(textAfter)) - context = context.prev; - while (context && !context.startOfLine) - context = context.prev; - if (context) return context.indent + indentUnit; - else return 0; - }, - electricChars: "/" - }; + //start of line types + if (sol) { + switch (ch) { + case "!": //header at start of line + if (stream.match('!!!!!')) { + return chain(inLine("header string")); + } else if (stream.match('!!!!')) { + return chain(inLine("header string")); + } else if (stream.match('!!!')) { + return chain(inLine("header string")); + } else if (stream.match('!!')) { + return chain(inLine("header string")); + } else { + return chain(inLine("header string")); + } + break; + case "*": //unordered list line item, or
  • at start of line + case "#": //ordered list line item, or
  • at start of line + case "+": //ordered list line item, or
  • at start of line + return chain(inLine("tw-listitem bracket")); + break; + } + } + + //stream.eatWhile(/[&{]/); was eating up plugins, turned off to act less like html and more like tiki + return null; + } + + var indentUnit = config.indentUnit; + + // Return variables for tokenizers + var pluginName, type; + function inPlugin(stream, state) { + var ch = stream.next(); + var peek = stream.peek(); + + if (ch == "}") { + state.tokenize = inText; + //type = ch == ")" ? "endPlugin" : "selfclosePlugin"; inPlugin + return "tag"; + } else if (ch == "(" || ch == ")") { + return "bracket"; + } else if (ch == "=") { + type = "equals"; + + if (peek == ">") { + ch = stream.next(); + peek = stream.peek(); + } + + //here we detect values directly after equal character with no quotes + if (!/[\'\"]/.test(peek)) { + state.tokenize = inAttributeNoQuote(); + } + //end detect values + + return "operator"; + } else if (/[\'\"]/.test(ch)) { + state.tokenize = inAttribute(ch); + return state.tokenize(stream, state); + } else { + stream.eatWhile(/[^\s\u00a0=\"\'\/?]/); + return "keyword"; + } + } + + function inAttribute(quote) { + return function(stream, state) { + while (!stream.eol()) { + if (stream.next() == quote) { + state.tokenize = inPlugin; + break; + } + } + return "string"; + }; + } + + function inAttributeNoQuote() { + return function(stream, state) { + while (!stream.eol()) { + var ch = stream.next(); + var peek = stream.peek(); + if (ch == " " || ch == "," || /[ )}]/.test(peek)) { + state.tokenize = inPlugin; + break; + } + } + return "string"; +}; + } + +var curState, setStyle; +function pass() { + for (var i = arguments.length - 1; i >= 0; i--) curState.cc.push(arguments[i]); +} + +function cont() { + pass.apply(null, arguments); + return true; +} + +function pushContext(pluginName, startOfLine) { + var noIndent = curState.context && curState.context.noIndent; + curState.context = { + prev: curState.context, + pluginName: pluginName, + indent: curState.indented, + startOfLine: startOfLine, + noIndent: noIndent + }; +} + +function popContext() { + if (curState.context) curState.context = curState.context.prev; +} + +function element(type) { + if (type == "openPlugin") {curState.pluginName = pluginName; return cont(attributes, endplugin(curState.startOfLine));} + else if (type == "closePlugin") { + var err = false; + if (curState.context) { + err = curState.context.pluginName != pluginName; + popContext(); + } else { + err = true; + } + if (err) setStyle = "error"; + return cont(endcloseplugin(err)); + } + else if (type == "string") { + if (!curState.context || curState.context.name != "!cdata") pushContext("!cdata"); + if (curState.tokenize == inText) popContext(); + return cont(); + } + else return cont(); +} + +function endplugin(startOfLine) { + return function(type) { + if ( + type == "selfclosePlugin" || + type == "endPlugin" + ) + return cont(); + if (type == "endPlugin") {pushContext(curState.pluginName, startOfLine); return cont();} + return cont(); + }; +} + +function endcloseplugin(err) { + return function(type) { + if (err) setStyle = "error"; + if (type == "endPlugin") return cont(); + return pass(); + }; +} + +function attributes(type) { + if (type == "keyword") {setStyle = "attribute"; return cont(attributes);} + if (type == "equals") return cont(attvalue, attributes); + return pass(); +} +function attvalue(type) { + if (type == "keyword") {setStyle = "string"; return cont();} + if (type == "string") return cont(attvaluemaybe); + return pass(); +} +function attvaluemaybe(type) { + if (type == "string") return cont(attvaluemaybe); + else return pass(); +} +return { + startState: function() { + return {tokenize: inText, cc: [], indented: 0, startOfLine: true, pluginName: null, context: null}; + }, + token: function(stream, state) { + if (stream.sol()) { + state.startOfLine = true; + state.indented = stream.indentation(); + } + if (stream.eatSpace()) return null; + + setStyle = type = pluginName = null; + var style = state.tokenize(stream, state); + if ((style || type) && style != "comment") { + curState = state; + while (true) { + var comb = state.cc.pop() || element; + if (comb(type || style)) break; + } + } + state.startOfLine = false; + return setStyle || style; + }, + indent: function(state, textAfter) { + var context = state.context; + if (context && context.noIndent) return 0; + if (context && /^{\//.test(textAfter)) + context = context.prev; + while (context && !context.startOfLine) + context = context.prev; + if (context) return context.indent + indentUnit; + else return 0; + }, + electricChars: "/" + }; }); -//I figure, why not CodeMirror.defineMIME("text/tiki", "tiki"); diff --git a/mode/turtle/turtle.js b/mode/turtle/turtle.js index 5c7c28eb02..e118bfbced 100644 --- a/mode/turtle/turtle.js +++ b/mode/turtle/turtle.js @@ -33,23 +33,23 @@ CodeMirror.defineMode("turtle", function(config) { return null; } else if (ch == ":") { - return "operator"; - } else { + return "operator"; + } else { stream.eatWhile(/[_\w\d]/); if(stream.peek() == ":") { return "variable-3"; } else { - var word = stream.current(); - - if(keywords.test(word)) { - return "meta"; - } - - if(ch >= "A" && ch <= "Z") { - return "comment"; - } else { - return "keyword"; - } + var word = stream.current(); + + if(keywords.test(word)) { + return "meta"; + } + + if(ch >= "A" && ch <= "Z") { + return "comment"; + } else { + return "keyword"; + } } var word = stream.current(); if (ops.test(word)) @@ -119,7 +119,7 @@ CodeMirror.defineMode("turtle", function(config) { state.context.col = stream.column(); } } - + return style; }, diff --git a/mode/vbscript/vbscript.js b/mode/vbscript/vbscript.js index 65d6c21276..3e1eedc7db 100644 --- a/mode/vbscript/vbscript.js +++ b/mode/vbscript/vbscript.js @@ -6,12 +6,12 @@ CodeMirror.defineMode("vbscript", function() { if (stream.eatSpace()) return null; var ch = stream.next(); if (ch == "'") { - stream.skipToEnd(); - return "comment"; + stream.skipToEnd(); + return "comment"; } if (ch == '"') { - stream.skipTo('"'); - return "string"; + stream.skipTo('"'); + return "string"; } if (/\w/.test(ch)) { diff --git a/mode/yaml/yaml.js b/mode/yaml/yaml.js index 59e2641a01..bdd303dfed 100644 --- a/mode/yaml/yaml.js +++ b/mode/yaml/yaml.js @@ -1,95 +1,95 @@ CodeMirror.defineMode("yaml", function() { - - var cons = ['true', 'false', 'on', 'off', 'yes', 'no']; - var keywordRegex = new RegExp("\\b(("+cons.join(")|(")+"))$", 'i'); - - return { - token: function(stream, state) { - var ch = stream.peek(); - var esc = state.escaped; - state.escaped = false; - /* comments */ - if (ch == "#") { stream.skipToEnd(); return "comment"; } - if (state.literal && stream.indentation() > state.keyCol) { - stream.skipToEnd(); return "string"; - } else if (state.literal) { state.literal = false; } - if (stream.sol()) { - state.keyCol = 0; - state.pair = false; - state.pairStart = false; - /* document start */ - if(stream.match(/---/)) { return "def"; } - /* document end */ - if (stream.match(/\.\.\./)) { return "def"; } - /* array list item */ - if (stream.match(/\s*-\s+/)) { return 'meta'; } - } - /* pairs (associative arrays) -> key */ - if (!state.pair && stream.match(/^\s*([a-z0-9\._-])+(?=\s*:)/i)) { - state.pair = true; - state.keyCol = stream.indentation(); - return "atom"; - } - if (state.pair && stream.match(/^:\s*/)) { state.pairStart = true; return 'meta'; } - - /* inline pairs/lists */ - if (stream.match(/^(\{|\}|\[|\])/)) { - if (ch == '{') - state.inlinePairs++; - else if (ch == '}') - state.inlinePairs--; - else if (ch == '[') - state.inlineList++; - else - state.inlineList--; - return 'meta'; - } - - /* list seperator */ - if (state.inlineList > 0 && !esc && ch == ',') { - stream.next(); - return 'meta'; - } - /* pairs seperator */ - if (state.inlinePairs > 0 && !esc && ch == ',') { - state.keyCol = 0; - state.pair = false; - state.pairStart = false; - stream.next(); - return 'meta'; - } - - /* start of value of a pair */ - if (state.pairStart) { - /* block literals */ - if (stream.match(/^\s*(\||\>)\s*/)) { state.literal = true; return 'meta'; }; - /* references */ - if (stream.match(/^\s*(\&|\*)[a-z0-9\._-]+\b/i)) { return 'variable-2'; } - /* numbers */ - if (state.inlinePairs == 0 && stream.match(/^\s*-?[0-9\.\,]+\s?$/)) { return 'number'; } - if (state.inlinePairs > 0 && stream.match(/^\s*-?[0-9\.\,]+\s?(?=(,|}))/)) { return 'number'; } - /* keywords */ - if (stream.match(keywordRegex)) { return 'keyword'; } - } - /* nothing found, continue */ - state.pairStart = false; - state.escaped = (ch == '\\'); - stream.next(); - return null; - }, - startState: function() { - return { - pair: false, - pairStart: false, - keyCol: 0, - inlinePairs: 0, - inlineList: 0, - literal: false, - escaped: false - }; - } - }; + var cons = ['true', 'false', 'on', 'off', 'yes', 'no']; + var keywordRegex = new RegExp("\\b(("+cons.join(")|(")+"))$", 'i'); + + return { + token: function(stream, state) { + var ch = stream.peek(); + var esc = state.escaped; + state.escaped = false; + /* comments */ + if (ch == "#") { stream.skipToEnd(); return "comment"; } + if (state.literal && stream.indentation() > state.keyCol) { + stream.skipToEnd(); return "string"; + } else if (state.literal) { state.literal = false; } + if (stream.sol()) { + state.keyCol = 0; + state.pair = false; + state.pairStart = false; + /* document start */ + if(stream.match(/---/)) { return "def"; } + /* document end */ + if (stream.match(/\.\.\./)) { return "def"; } + /* array list item */ + if (stream.match(/\s*-\s+/)) { return 'meta'; } + } + /* pairs (associative arrays) -> key */ + if (!state.pair && stream.match(/^\s*([a-z0-9\._-])+(?=\s*:)/i)) { + state.pair = true; + state.keyCol = stream.indentation(); + return "atom"; + } + if (state.pair && stream.match(/^:\s*/)) { state.pairStart = true; return 'meta'; } + + /* inline pairs/lists */ + if (stream.match(/^(\{|\}|\[|\])/)) { + if (ch == '{') + state.inlinePairs++; + else if (ch == '}') + state.inlinePairs--; + else if (ch == '[') + state.inlineList++; + else + state.inlineList--; + return 'meta'; + } + + /* list seperator */ + if (state.inlineList > 0 && !esc && ch == ',') { + stream.next(); + return 'meta'; + } + /* pairs seperator */ + if (state.inlinePairs > 0 && !esc && ch == ',') { + state.keyCol = 0; + state.pair = false; + state.pairStart = false; + stream.next(); + return 'meta'; + } + + /* start of value of a pair */ + if (state.pairStart) { + /* block literals */ + if (stream.match(/^\s*(\||\>)\s*/)) { state.literal = true; return 'meta'; }; + /* references */ + if (stream.match(/^\s*(\&|\*)[a-z0-9\._-]+\b/i)) { return 'variable-2'; } + /* numbers */ + if (state.inlinePairs == 0 && stream.match(/^\s*-?[0-9\.\,]+\s?$/)) { return 'number'; } + if (state.inlinePairs > 0 && stream.match(/^\s*-?[0-9\.\,]+\s?(?=(,|}))/)) { return 'number'; } + /* keywords */ + if (stream.match(keywordRegex)) { return 'keyword'; } + } + + /* nothing found, continue */ + state.pairStart = false; + state.escaped = (ch == '\\'); + stream.next(); + return null; + }, + startState: function() { + return { + pair: false, + pairStart: false, + keyCol: 0, + inlinePairs: 0, + inlineList: 0, + literal: false, + escaped: false + }; + } + }; }); CodeMirror.defineMIME("text/x-yaml", "yaml"); diff --git a/mode/z80/z80.js b/mode/z80/z80.js index c026790dc7..ff43d32b52 100644 --- a/mode/z80/z80.js +++ b/mode/z80/z80.js @@ -1,113 +1,85 @@ -CodeMirror.defineMode('z80', function() -{ - var keywords1 = /^(exx?|(ld|cp|in)([di]r?)?|pop|push|ad[cd]|cpl|daa|dec|inc|neg|sbc|sub|and|bit|[cs]cf|x?or|res|set|r[lr]c?a?|r[lr]d|s[lr]a|srl|djnz|nop|rst|[de]i|halt|im|ot[di]r|out[di]?)\b/i; - var keywords2 = /^(call|j[pr]|ret[in]?)\b/i; - var keywords3 = /^b_?(call|jump)\b/i; - var variables1 = /^(af?|bc?|c|de?|e|hl?|l|i[xy]?|r|sp)\b/i; - var variables2 = /^(n?[zc]|p[oe]?|m)\b/i; - var errors = /^([hl][xy]|i[xy][hl]|slia|sll)\b/i; - var numbers = /^([\da-f]+h|[0-7]+o|[01]+b|\d+)\b/i; - - return {startState: function() - { - return {context: 0}; - }, token: function(stream, state) - { - if (!stream.column()) - state.context = 0; - - if (stream.eatSpace()) - return null; - - var w; - - if (stream.eatWhile(/\w/)) - { - w = stream.current(); - - if (stream.indentation()) - { - if (state.context == 1 && variables1.test(w)) - return 'variable-2'; - - if (state.context == 2 && variables2.test(w)) - return 'variable-3'; - - if (keywords1.test(w)) - { - state.context = 1; - return 'keyword'; - } - else if (keywords2.test(w)) - { - state.context = 2; - return 'keyword'; - } - else if (keywords3.test(w)) - { - state.context = 3; - return 'keyword'; - } - - if (errors.test(w)) - return 'error'; - } - else if (numbers.test(w)) - { - return 'number'; - } - else - { - return null; - } - } - else if (stream.eat(';')) - { - stream.skipToEnd(); - return 'comment'; - } - else if (stream.eat('"')) - { - while (w = stream.next()) - { - if (w == '"') - break; - - if (w == '\\') - stream.next(); - } - - return 'string'; - } - else if (stream.eat('\'')) - { - if (stream.match(/\\?.'/)) - return 'number'; - } - else if (stream.eat('.') || stream.sol() && stream.eat('#')) - { - state.context = 4; - - if (stream.eatWhile(/\w/)) - return 'def'; - } - else if (stream.eat('$')) - { - if (stream.eatWhile(/[\da-f]/i)) - return 'number'; - } - else if (stream.eat('%')) - { - if (stream.eatWhile(/[01]/)) - return 'number'; - } - else - { - stream.next(); - } - - return null; - }}; +CodeMirror.defineMode('z80', function() { + var keywords1 = /^(exx?|(ld|cp|in)([di]r?)?|pop|push|ad[cd]|cpl|daa|dec|inc|neg|sbc|sub|and|bit|[cs]cf|x?or|res|set|r[lr]c?a?|r[lr]d|s[lr]a|srl|djnz|nop|rst|[de]i|halt|im|ot[di]r|out[di]?)\b/i; + var keywords2 = /^(call|j[pr]|ret[in]?)\b/i; + var keywords3 = /^b_?(call|jump)\b/i; + var variables1 = /^(af?|bc?|c|de?|e|hl?|l|i[xy]?|r|sp)\b/i; + var variables2 = /^(n?[zc]|p[oe]?|m)\b/i; + var errors = /^([hl][xy]|i[xy][hl]|slia|sll)\b/i; + var numbers = /^([\da-f]+h|[0-7]+o|[01]+b|\d+)\b/i; + + return { + startState: function() { + return {context: 0}; + }, + token: function(stream, state) { + if (!stream.column()) + state.context = 0; + + if (stream.eatSpace()) + return null; + + var w; + + if (stream.eatWhile(/\w/)) { + w = stream.current(); + + if (stream.indentation()) { + if (state.context == 1 && variables1.test(w)) + return 'variable-2'; + + if (state.context == 2 && variables2.test(w)) + return 'variable-3'; + + if (keywords1.test(w)) { + state.context = 1; + return 'keyword'; + } else if (keywords2.test(w)) { + state.context = 2; + return 'keyword'; + } else if (keywords3.test(w)) { + state.context = 3; + return 'keyword'; + } + + if (errors.test(w)) + return 'error'; + } else if (numbers.test(w)) { + return 'number'; + } else { + return null; + } + } else if (stream.eat(';')) { + stream.skipToEnd(); + return 'comment'; + } else if (stream.eat('"')) { + while (w = stream.next()) { + if (w == '"') + break; + + if (w == '\\') + stream.next(); + } + return 'string'; + } else if (stream.eat('\'')) { + if (stream.match(/\\?.'/)) + return 'number'; + } else if (stream.eat('.') || stream.sol() && stream.eat('#')) { + state.context = 4; + + if (stream.eatWhile(/\w/)) + return 'def'; + } else if (stream.eat('$')) { + if (stream.eatWhile(/[\da-f]/i)) + return 'number'; + } else if (stream.eat('%')) { + if (stream.eatWhile(/[01]/)) + return 'number'; + } else { + stream.next(); + } + return null; + } + }; }); CodeMirror.defineMIME("text/x-z80", "z80"); From 86c6b6afd965e36ed71562111ad573433a8c8b6f Mon Sep 17 00:00:00 2001 From: Marijn Haverbeke Date: Tue, 2 Apr 2013 11:53:13 +0200 Subject: [PATCH 0943/5780] Make the linter complain about trailing whitespace --- test/lint/lint.js | 16 ++++++++++------ 1 file changed, 10 insertions(+), 6 deletions(-) diff --git a/test/lint/lint.js b/test/lint/lint.js index 60b63a2d15..5f808922f2 100644 --- a/test/lint/lint.js +++ b/test/lint/lint.js @@ -19,11 +19,15 @@ var scopePasser = walk.make({ }); function checkFile(fileName) { - var file = fs.readFileSync(fileName, "utf8"); - var badChar = file.match(/[\x00-\x08\x0b\x0c\x0e-\x19\uFEFF\t]/); - if (badChar) - fail("Undesirable character " + badChar[0].charCodeAt(0) + " at position " + badChar.index, - {source: fileName}); + var file = fs.readFileSync(fileName, "utf8"), notAllowed; + if (notAllowed = file.match(/[\x00-\x08\x0b\x0c\x0e-\x19\uFEFF\t]|[ \t]\n/)) { + var msg; + if (notAllowed[0] == "\t") msg = "Found tab character"; + else if (notAllowed[0].indexOf("\n") > -1) msg = "Trailing whitespace"; + else msg = "Undesirable character " + notAllowed[0].charCodeAt(0); + var info = acorn.getLineInfo(file, notAllowed.index); + fail(msg + " at line " + info.line + ", column " + info.column, {source: fileName}); + } try { var parsed = acorn.parse(file, { @@ -91,7 +95,7 @@ function checkFile(fileName) { var failed = false; function fail(msg, pos) { if (pos.start) msg += " (" + pos.start.line + ":" + pos.start.column + ")"; - console.log(pos.source.match(/[^\/]+$/)[0] + ": " + msg); + console.log(pos.source + ": " + msg); failed = true; } From 2700c8cdbffb861508911f4af15e5fc159902c8d Mon Sep 17 00:00:00 2001 From: Marijn Haverbeke Date: Tue, 2 Apr 2013 11:53:23 +0200 Subject: [PATCH 0944/5780] Remove a whole lot of trailing whitespace --- addon/edit/matchbrackets.js | 2 +- addon/hint/pig-hint.js | 34 ++++----- addon/mode/multiplex.js | 2 +- addon/mode/overlay.js | 4 +- addon/search/match-highlighter.js | 2 +- addon/selection/active-line.js | 2 +- mode/asterisk/asterisk.js | 12 ++-- mode/clike/clike.js | 28 ++++---- mode/clojure/clojure.js | 2 +- mode/coffeescript/coffeescript.js | 2 +- mode/erlang/erlang.js | 2 +- mode/gas/gas.js | 2 +- mode/haskell/haskell.js | 52 +++++++------- mode/haxe/haxe.js | 30 ++++---- mode/htmlembedded/htmlembedded.js | 12 ++-- mode/javascript/javascript.js | 8 +-- mode/jinja2/jinja2.js | 4 +- mode/less/less.js | 30 ++++---- mode/markdown/markdown.js | 78 ++++++++++----------- mode/markdown/test.js | 22 +++--- mode/ntriples/ntriples.js | 44 ++++++------ mode/ocaml/ocaml.js | 2 +- mode/python/python.js | 48 ++++++------- mode/q/q.js | 2 +- mode/shell/shell.js | 2 +- mode/sieve/sieve.js | 20 +++--- mode/sparql/sparql.js | 2 +- mode/sql/sql.js | 4 +- mode/tcl/tcl.js | 2 +- mode/vb/vb.js | 61 ++++++++--------- mode/xml/xml.js | 2 +- mode/xquery/xquery.js | 110 +++++++++++++++--------------- 32 files changed, 314 insertions(+), 315 deletions(-) diff --git a/addon/edit/matchbrackets.js b/addon/edit/matchbrackets.js index f4925b7256..72bca57091 100644 --- a/addon/edit/matchbrackets.js +++ b/addon/edit/matchbrackets.js @@ -3,7 +3,7 @@ (document.documentMode == null || document.documentMode < 8); var Pos = CodeMirror.Pos; - // Disable brace matching in long lines, since it'll cause hugely slow updates + // Disable brace matching in long lines, since it'll cause hugely slow updates var maxLineLen = 1000; var matching = {"(": ")>", ")": "(<", "[": "]>", "]": "[<", "{": "}>", "}": "{<"}; diff --git a/addon/hint/pig-hint.js b/addon/hint/pig-hint.js index 9847996be9..d831ccfe93 100644 --- a/addon/hint/pig-hint.js +++ b/addon/hint/pig-hint.js @@ -2,7 +2,7 @@ function forEach(arr, f) { for (var i = 0, e = arr.length; i < e; ++i) f(arr[i]); } - + function arrayContains(arr, item) { if (!Array.prototype.indexOf) { var i = arr.length; @@ -25,11 +25,11 @@ token = tprop = {start: cur.ch, end: cur.ch, string: "", state: token.state, className: token.string == ":" ? "pig-type" : null}; } - + if (!context) var context = []; context.push(tprop); - - var completionList = getCompletions(token, context); + + var completionList = getCompletions(token, context); completionList = completionList.sort(); //prevent autocomplete for last word, instead show dropdown with one word if(completionList.length == 1) { @@ -40,24 +40,24 @@ from: CodeMirror.Pos(cur.line, token.start), to: CodeMirror.Pos(cur.line, token.end)}; } - + CodeMirror.pigHint = function(editor) { return scriptHint(editor, pigKeywordsU, function (e, cur) {return e.getTokenAt(cur);}); }; - + var pigKeywords = "VOID IMPORT RETURNS DEFINE LOAD FILTER FOREACH ORDER CUBE DISTINCT COGROUP " + "JOIN CROSS UNION SPLIT INTO IF OTHERWISE ALL AS BY USING INNER OUTER ONSCHEMA PARALLEL " + "PARTITION GROUP AND OR NOT GENERATE FLATTEN ASC DESC IS STREAM THROUGH STORE MAPREDUCE " - + "SHIP CACHE INPUT OUTPUT STDERROR STDIN STDOUT LIMIT SAMPLE LEFT RIGHT FULL EQ GT LT GTE LTE " + + "SHIP CACHE INPUT OUTPUT STDERROR STDIN STDOUT LIMIT SAMPLE LEFT RIGHT FULL EQ GT LT GTE LTE " + "NEQ MATCHES TRUE FALSE"; var pigKeywordsU = pigKeywords.split(" "); var pigKeywordsL = pigKeywords.toLowerCase().split(" "); - + var pigTypes = "BOOLEAN INT LONG FLOAT DOUBLE CHARARRAY BYTEARRAY BAG TUPLE MAP"; var pigTypesU = pigTypes.split(" "); var pigTypesL = pigTypes.toLowerCase().split(" "); - - var pigBuiltins = "ABS ACOS ARITY ASIN ATAN AVG BAGSIZE BINSTORAGE BLOOM BUILDBLOOM CBRT CEIL " + + var pigBuiltins = "ABS ACOS ARITY ASIN ATAN AVG BAGSIZE BINSTORAGE BLOOM BUILDBLOOM CBRT CEIL " + "CONCAT COR COS COSH COUNT COUNT_STAR COV CONSTANTSIZE CUBEDIMENSIONS DIFF DISTINCT DOUBLEABS " + "DOUBLEAVG DOUBLEBASE DOUBLEMAX DOUBLEMIN DOUBLEROUND DOUBLESUM EXP FLOOR FLOATABS FLOATAVG " + "FLOATMAX FLOATMIN FLOATROUND FLOATSUM GENERICINVOKER INDEXOF INTABS INTAVG INTMAX INTMIN " @@ -66,9 +66,9 @@ + "LONGAVG LONGMAX LONGMIN LONGSUM MAX MIN MAPSIZE MONITOREDUDF NONDETERMINISTIC OUTPUTSCHEMA " + "PIGSTORAGE PIGSTREAMING RANDOM REGEX_EXTRACT REGEX_EXTRACT_ALL REPLACE ROUND SIN SINH SIZE " + "SQRT STRSPLIT SUBSTRING SUM STRINGCONCAT STRINGMAX STRINGMIN STRINGSIZE TAN TANH TOBAG " - + "TOKENIZE TOMAP TOP TOTUPLE TRIM TEXTLOADER TUPLESIZE UCFIRST UPPER UTF8STORAGECONVERTER"; - var pigBuiltinsU = pigBuiltins.split(" ").join("() ").split(" "); - var pigBuiltinsL = pigBuiltins.toLowerCase().split(" ").join("() ").split(" "); + + "TOKENIZE TOMAP TOP TOTUPLE TRIM TEXTLOADER TUPLESIZE UCFIRST UPPER UTF8STORAGECONVERTER"; + var pigBuiltinsU = pigBuiltins.split(" ").join("() ").split(" "); + var pigBuiltinsL = pigBuiltins.toLowerCase().split(" ").join("() ").split(" "); var pigBuiltinsC = ("BagSize BinStorage Bloom BuildBloom ConstantSize CubeDimensions DoubleAbs " + "DoubleAvg DoubleBase DoubleMax DoubleMin DoubleRound DoubleSum FloatAbs FloatAvg FloatMax " + "FloatMin FloatRound FloatSum GenericInvoker IntAbs IntAvg IntMax IntMin IntSum " @@ -76,13 +76,13 @@ + "IsEmpty JsonLoader JsonMetadata JsonStorage LongAbs LongAvg LongMax LongMin LongSum MapSize " + "MonitoredUDF Nondeterministic OutputSchema PigStorage PigStreaming StringConcat StringMax " + "StringMin StringSize TextLoader TupleSize Utf8StorageConverter").split(" ").join("() ").split(" "); - + function getCompletions(token, context) { var found = [], start = token.string; function maybeAdd(str) { if (str.indexOf(start) == 0 && !arrayContains(found, str)) found.push(str); } - + function gatherCompletions(obj) { if(obj == ":") { forEach(pigTypesL, maybeAdd); @@ -103,11 +103,11 @@ // find in the current environment. var obj = context.pop(), base; - if (obj.type == "variable") + if (obj.type == "variable") base = obj.string; else if(obj.type == "variable-3") base = ":" + obj.string; - + while (base != null && context.length) base = base[context.pop().string]; if (base != null) gatherCompletions(base); diff --git a/addon/mode/multiplex.js b/addon/mode/multiplex.js index 459a918ea5..3ff3a929ed 100644 --- a/addon/mode/multiplex.js +++ b/addon/mode/multiplex.js @@ -61,7 +61,7 @@ CodeMirror.multiplexingMode = function(outer /*, others */) { return innerToken; } }, - + indent: function(state, textAfter) { var mode = state.innerActive ? state.innerActive.mode : outer; if (!mode.indent) return CodeMirror.Pass; diff --git a/addon/mode/overlay.js b/addon/mode/overlay.js index fba38987bb..b7928a7bbf 100644 --- a/addon/mode/overlay.js +++ b/addon/mode/overlay.js @@ -43,14 +43,14 @@ CodeMirror.overlayMode = CodeMirror.overlayParser = function(base, overlay, comb if (state.baseCur != null && combine) return state.baseCur + " " + state.overlayCur; else return state.overlayCur; }, - + indent: base.indent && function(state, textAfter) { return base.indent(state.base, textAfter); }, electricChars: base.electricChars, innerMode: function(state) { return {state: state.base, mode: base}; }, - + blankLine: function(state) { if (base.blankLine) base.blankLine(state.base); if (overlay.blankLine) overlay.blankLine(state.overlay); diff --git a/addon/search/match-highlighter.js b/addon/search/match-highlighter.js index c6e35cd97e..14c1dab5b6 100644 --- a/addon/search/match-highlighter.js +++ b/addon/search/match-highlighter.js @@ -14,7 +14,7 @@ (function() { var DEFAULT_MIN_CHARS = 2; var DEFAULT_TOKEN_STYLE = "matchhighlight"; - + function State(options) { this.minChars = typeof options == "object" && options.minChars || DEFAULT_MIN_CHARS; this.style = typeof options == "object" && options.style || DEFAULT_TOKEN_STYLE; diff --git a/addon/selection/active-line.js b/addon/selection/active-line.js index 988a0ffbf7..211de0fefd 100644 --- a/addon/selection/active-line.js +++ b/addon/selection/active-line.js @@ -20,7 +20,7 @@ delete cm._activeLine; } }); - + function clearActiveLine(cm) { if ("_activeLine" in cm) { cm.removeLineClass(cm._activeLine, "wrap", WRAP_CLASS); diff --git a/mode/asterisk/asterisk.js b/mode/asterisk/asterisk.js index d491857e92..60b689d1d5 100644 --- a/mode/asterisk/asterisk.js +++ b/mode/asterisk/asterisk.js @@ -8,7 +8,7 @@ * Created: 05/17/2012 09:20:25 PM * Revision: none * - * Author: Stas Kobzar (stas@modulis.ca), + * Author: Stas Kobzar (stas@modulis.ca), * Company: Modulis.ca Inc. * * ===================================================================================== @@ -99,9 +99,9 @@ CodeMirror.defineMode("asterisk", function() { state.extenStart = true; switch(cur) { case 'same': state.extenSame = true; break; - case 'include': - case 'switch': - case 'ignorepat': + case 'include': + case 'switch': + case 'ignorepat': state.extenInclude = true;break; default:break; } @@ -121,7 +121,7 @@ CodeMirror.defineMode("asterisk", function() { }; }, token: function(stream, state) { - + var cur = ''; var ch = ''; if(stream.eatSpace()) return null; @@ -174,7 +174,7 @@ CodeMirror.defineMode("asterisk", function() { } else{ return basicToken(stream,state); } - + return null; } }; diff --git a/mode/clike/clike.js b/mode/clike/clike.js index 1b350aeb8c..c14d7b4006 100644 --- a/mode/clike/clike.js +++ b/mode/clike/clike.js @@ -222,7 +222,7 @@ CodeMirror.defineMode("clike", function(config, parserConfig) { }); CodeMirror.defineMIME("text/x-java", { name: "clike", - keywords: words("abstract assert boolean break byte case catch char class const continue default " + + keywords: words("abstract assert boolean break byte case catch char class const continue default " + "do double else enum extends final finally float for goto if implements import " + "instanceof int interface long native new package private protected public " + "return short static strictfp super switch synchronized this throw throws transient " + @@ -238,12 +238,12 @@ CodeMirror.defineMode("clike", function(config, parserConfig) { }); CodeMirror.defineMIME("text/x-csharp", { name: "clike", - keywords: words("abstract as base break case catch checked class const continue" + - " default delegate do else enum event explicit extern finally fixed for" + - " foreach goto if implicit in interface internal is lock namespace new" + - " operator out override params private protected public readonly ref return sealed" + - " sizeof stackalloc static struct switch this throw try typeof unchecked" + - " unsafe using virtual void volatile while add alias ascending descending dynamic from get" + + keywords: words("abstract as base break case catch checked class const continue" + + " default delegate do else enum event explicit extern finally fixed for" + + " foreach goto if implicit in interface internal is lock namespace new" + + " operator out override params private protected public readonly ref return sealed" + + " sizeof stackalloc static struct switch this throw try typeof unchecked" + + " unsafe using virtual void volatile while add alias ascending descending dynamic from get" + " global group into join let orderby partial remove select set value var yield"), blockKeywords: words("catch class do else finally for foreach if struct switch try while"), builtin: words("Boolean Byte Char DateTime DateTimeOffset Decimal Double" + @@ -265,30 +265,30 @@ CodeMirror.defineMode("clike", function(config, parserConfig) { CodeMirror.defineMIME("text/x-scala", { name: "clike", keywords: words( - + /* scala */ "abstract case catch class def do else extends false final finally for forSome if " + "implicit import lazy match new null object override package private protected return " + "sealed super this throw trait try trye type val var while with yield _ : = => <- <: " + "<% >: # @ " + - + /* package scala */ "assert assume require print println printf readLine readBoolean readByte readShort " + "readChar readInt readLong readFloat readDouble " + - + "AnyVal App Application Array BufferedIterator BigDecimal BigInt Char Console Either " + "Enumeration Equiv Error Exception Fractional Function IndexedSeq Integral Iterable " + "Iterator List Map Numeric Nil NotNull Option Ordered Ordering PartialFunction PartialOrdering " + "Product Proxy Range Responder Seq Serializable Set Specializable Stream StringBuilder " + "StringContext Symbol Throwable Traversable TraversableOnce Tuple Unit Vector :: #:: " + - - /* package java.lang */ + + /* package java.lang */ "Boolean Byte Character CharSequence Class ClassLoader Cloneable Comparable " + "Compiler Double Exception Float Integer Long Math Number Object Package Pair Process " + "Runtime Runnable SecurityManager Short StackTraceElement StrictMath String " + "StringBuffer System Thread ThreadGroup ThreadLocal Throwable Triple Void" - - + + ), blockKeywords: words("catch class do else finally for forSome if match switch try while"), atoms: words("true false null"), diff --git a/mode/clojure/clojure.js b/mode/clojure/clojure.js index 658ff140e0..7751212632 100644 --- a/mode/clojure/clojure.js +++ b/mode/clojure/clojure.js @@ -14,7 +14,7 @@ CodeMirror.defineMode("clojure", function () { } var atoms = makeKeywords("true false nil"); - + var keywords = makeKeywords( "defn defn- def def- defonce defmulti defmethod defmacro defstruct deftype defprotocol defrecord defproject deftest slice defalias defhinted defmacro- defn-memo defnk defnk defonce- defunbound defunbound- defvar defvar- let letfn do case cond condp for loop recur when when-not when-let when-first if if-let if-not . .. -> ->> doto and or dosync doseq dotimes dorun doall load import unimport ns in-ns refer try catch finally throw with-open with-local-vars binding gen-class gen-and-load-class gen-and-save-class handler-case handle"); diff --git a/mode/coffeescript/coffeescript.js b/mode/coffeescript/coffeescript.js index 4d925037ce..cf1cf4f246 100644 --- a/mode/coffeescript/coffeescript.js +++ b/mode/coffeescript/coffeescript.js @@ -158,7 +158,7 @@ CodeMirror.defineMode('coffeescript', function(conf) { if (stream.match(identifiers)) { return 'variable'; } - + if (stream.match(properties)) { return 'property'; } diff --git a/mode/erlang/erlang.js b/mode/erlang/erlang.js index accf24e670..029b8e5561 100644 --- a/mode/erlang/erlang.js +++ b/mode/erlang/erlang.js @@ -247,7 +247,7 @@ CodeMirror.defineMode("erlang", function(cmCfg) { return rval(state,stream,"function"); } } - return rval(state,stream,"atom"); + return rval(state,stream,"atom"); } // number diff --git a/mode/gas/gas.js b/mode/gas/gas.js index 5ef4bb04ed..6604f01820 100644 --- a/mode/gas/gas.js +++ b/mode/gas/gas.js @@ -241,7 +241,7 @@ CodeMirror.defineMode("gas", function(_config, parserConfig) { return "comment"; } - return { + return { startState: function() { return { tokenize: null diff --git a/mode/haskell/haskell.js b/mode/haskell/haskell.js index 71235f4ee9..faec08dc33 100644 --- a/mode/haskell/haskell.js +++ b/mode/haskell/haskell.js @@ -4,7 +4,7 @@ CodeMirror.defineMode("haskell", function() { setState(f); return f(source, setState); } - + // These should all be Unicode extended, as per the Haskell 2010 report var smallRE = /[a-z_]/; var largeRE = /[A-Z]/; @@ -15,12 +15,12 @@ CodeMirror.defineMode("haskell", function() { var symbolRE = /[-!#$%&*+.\/<=>?@\\^|~:]/; var specialRE = /[(),;[\]`{}]/; var whiteCharRE = /[ \t\v\f]/; // newlines are handled in tokenizer - + function normal(source, setState) { if (source.eatWhile(whiteCharRE)) { return null; } - + var ch = source.next(); if (specialRE.test(ch)) { if (ch == '{' && source.eat('-')) { @@ -32,7 +32,7 @@ CodeMirror.defineMode("haskell", function() { } return null; } - + if (ch == '\'') { if (source.eat('\\')) { source.next(); // should handle other escapes here @@ -45,11 +45,11 @@ CodeMirror.defineMode("haskell", function() { } return "error"; } - + if (ch == '"') { return switchState(source, setState, stringLiteral); } - + if (largeRE.test(ch)) { source.eatWhile(idRE); if (source.eat('.')) { @@ -57,12 +57,12 @@ CodeMirror.defineMode("haskell", function() { } return "variable-2"; } - + if (smallRE.test(ch)) { source.eatWhile(idRE); return "variable"; } - + if (digitRE.test(ch)) { if (ch == '0') { if (source.eat(/[xX]/)) { @@ -87,7 +87,7 @@ CodeMirror.defineMode("haskell", function() { } return t; } - + if (symbolRE.test(ch)) { if (ch == '-' && source.eat(/-/)) { source.eatWhile(/-/); @@ -101,12 +101,12 @@ CodeMirror.defineMode("haskell", function() { t = "variable-2"; } source.eatWhile(symbolRE); - return t; + return t; } - + return "error"; } - + function ncomment(type, nest) { if (nest == 0) { return normal; @@ -130,7 +130,7 @@ CodeMirror.defineMode("haskell", function() { return type; }; } - + function stringLiteral(source, setState) { while (!source.eol()) { var ch = source.next(); @@ -153,7 +153,7 @@ CodeMirror.defineMode("haskell", function() { setState(normal); return "error"; } - + function stringGap(source, setState) { if (source.eat('\\')) { return switchState(source, setState, stringLiteral); @@ -162,8 +162,8 @@ CodeMirror.defineMode("haskell", function() { setState(normal); return "error"; } - - + + var wellKnownWords = (function() { var wkw = {}; function setType(t) { @@ -172,19 +172,19 @@ CodeMirror.defineMode("haskell", function() { wkw[arguments[i]] = t; }; } - + setType("keyword")( "case", "class", "data", "default", "deriving", "do", "else", "foreign", "if", "import", "in", "infix", "infixl", "infixr", "instance", "let", "module", "newtype", "of", "then", "type", "where", "_"); - + setType("keyword")( "\.\.", ":", "::", "=", "\\", "\"", "<-", "->", "@", "~", "=>"); - + setType("builtin")( "!!", "$!", "$", "&&", "+", "++", "-", ".", "/", "/=", "<", "<=", "=<<", "==", ">", ">=", ">>", ">>=", "^", "^^", "||", "*", "**"); - + setType("builtin")( "Bool", "Bounded", "Char", "Double", "EQ", "Either", "Enum", "Eq", "False", "FilePath", "Float", "Floating", "Fractional", "Functor", "GT", @@ -192,7 +192,7 @@ CodeMirror.defineMode("haskell", function() { "Maybe", "Monad", "Nothing", "Num", "Ord", "Ordering", "Rational", "Read", "ReadS", "Real", "RealFloat", "RealFrac", "Right", "Show", "ShowS", "String", "True"); - + setType("builtin")( "abs", "acos", "acosh", "all", "and", "any", "appendFile", "asTypeOf", "asin", "asinh", "atan", "atan2", "atanh", "break", "catch", "ceiling", @@ -220,16 +220,16 @@ CodeMirror.defineMode("haskell", function() { "toRational", "truncate", "uncurry", "undefined", "unlines", "until", "unwords", "unzip", "unzip3", "userError", "words", "writeFile", "zip", "zip3", "zipWith", "zipWith3"); - + return wkw; })(); - - - + + + return { startState: function () { return { f: normal }; }, copyState: function (s) { return { f: s.f }; }, - + token: function(stream, state) { var t = state.f(stream, function(s) { state.f = s; }); var w = stream.current(); diff --git a/mode/haxe/haxe.js b/mode/haxe/haxe.js index 786fe92d06..28f9b00ec3 100644 --- a/mode/haxe/haxe.js +++ b/mode/haxe/haxe.js @@ -1,6 +1,6 @@ CodeMirror.defineMode("haxe", function(config, parserConfig) { var indentUnit = config.indentUnit; - + // Tokenizer var keywords = function(){ @@ -12,10 +12,10 @@ CodeMirror.defineMode("haxe", function(config, parserConfig) { "if": A, "while": A, "else": B, "do": B, "try": B, "return": C, "break": C, "continue": C, "new": C, "throw": C, "var": kw("var"), "inline":attribute, "static": attribute, "using":kw("import"), - "public": attribute, "private": attribute, "cast": kw("cast"), "import": kw("import"), "macro": kw("macro"), + "public": attribute, "private": attribute, "cast": kw("cast"), "import": kw("import"), "macro": kw("macro"), "function": kw("function"), "catch": kw("catch"), "untyped": kw("untyped"), "callback": kw("cb"), "for": kw("for"), "switch": kw("switch"), "case": kw("case"), "default": kw("default"), - "in": operator, "never": kw("property_access"), "trace":kw("trace"), + "in": operator, "never": kw("property_access"), "trace":kw("trace"), "class": type, "enum":type, "interface":type, "typedef":type, "extends":type, "implements":type, "dynamic":type, "true": atom, "false": atom, "null": atom }; @@ -55,14 +55,14 @@ CodeMirror.defineMode("haxe", function(config, parserConfig) { else if (ch == "0" && stream.eat(/x/i)) { stream.eatWhile(/[\da-f]/i); return ret("number", "number"); - } + } else if (/\d/.test(ch) || ch == "-" && stream.eat(/\d/)) { stream.match(/^\d*(?:\.\d*)?(?:[eE][+\-]?\d+)?/); return ret("number", "number"); } else if (state.reAllowed && (ch == "~" && stream.eat(/\//))) { nextUntilUnescaped(stream, "/"); - stream.eatWhile(/[gimsu]/); + stream.eatWhile(/[gimsu]/); return ret("regexp", "string-2"); } else if (ch == "/") { @@ -146,13 +146,13 @@ CodeMirror.defineMode("haxe", function(config, parserConfig) { for (var v = state.localVars; v; v = v.next) if (v.name == varname) return true; } - + function parseHaxe(state, style, type, content, stream) { var cc = state.cc; // Communicate our context to the combinators. // (Less wasteful than consing up a hundred closures on every call.) cx.state = state; cx.stream = stream; cx.marked = null, cx.cc = cc; - + if (!state.lexical.hasOwnProperty("align")) state.lexical.align = true; @@ -168,7 +168,7 @@ CodeMirror.defineMode("haxe", function(config, parserConfig) { } } } - + function imported(state, typename) { if (/[a-z]/.test(typename.charAt(0))) @@ -177,8 +177,8 @@ CodeMirror.defineMode("haxe", function(config, parserConfig) { for (var i = 0; i/i; - + //inner modes var scriptingMode, htmlMixedMode; - + //tokenizer when in html mode function htmlDispatch(stream, state) { if (stream.match(scriptStartRegex, false)) { @@ -32,7 +32,7 @@ CodeMirror.defineMode("htmlembedded", function(config, parserConfig) { startState: function() { scriptingMode = scriptingMode || CodeMirror.getMode(config, parserConfig.scriptingModeSpec); htmlMixedMode = htmlMixedMode || CodeMirror.getMode(config, "htmlmixed"); - return { + return { token : parserConfig.startOpen ? scriptingDispatch : htmlDispatch, htmlState : CodeMirror.startState(htmlMixedMode), scriptState : CodeMirror.startState(scriptingMode) @@ -49,7 +49,7 @@ CodeMirror.defineMode("htmlembedded", function(config, parserConfig) { else if (scriptingMode.indent) return scriptingMode.indent(state.scriptState, textAfter); }, - + copyState: function(state) { return { token : state.token, @@ -57,7 +57,7 @@ CodeMirror.defineMode("htmlembedded", function(config, parserConfig) { scriptState : CodeMirror.copyState(scriptingMode, state.scriptState) }; }, - + electricChars: "/{}:", innerMode: function(state) { diff --git a/mode/javascript/javascript.js b/mode/javascript/javascript.js index bfbf78378b..6f7c33c2ac 100644 --- a/mode/javascript/javascript.js +++ b/mode/javascript/javascript.js @@ -11,7 +11,7 @@ CodeMirror.defineMode("javascript", function(config, parserConfig) { function kw(type) {return {type: type, style: "keyword"};} var A = kw("keyword a"), B = kw("keyword b"), C = kw("keyword c"); var operator = kw("operator"), atom = {type: "atom", style: "atom"}; - + var jsKeywords = { "if": A, "while": A, "with": A, "else": B, "do": B, "try": B, "finally": B, "return": C, "break": C, "continue": C, "new": C, "delete": C, "throw": C, @@ -87,7 +87,7 @@ CodeMirror.defineMode("javascript", function(config, parserConfig) { else if (ch == "0" && stream.eat(/x/i)) { stream.eatWhile(/[\da-f]/i); return ret("number", "number"); - } + } else if (/\d/.test(ch) || ch == "-" && stream.eat(/\d/)) { stream.match(/^\d*(?:\.\d*)?(?:[eE][+\-]?\d+)?/); return ret("number", "number"); @@ -170,7 +170,7 @@ CodeMirror.defineMode("javascript", function(config, parserConfig) { // Communicate our context to the combinators. // (Less wasteful than consing up a hundred closures on every call.) cx.state = state; cx.stream = stream; cx.marked = null, cx.cc = cc; - + if (!state.lexical.hasOwnProperty("align")) state.lexical.align = true; @@ -282,7 +282,7 @@ CodeMirror.defineMode("javascript", function(config, parserConfig) { if (type.match(/[;\}\)\],]/)) return pass(); return pass(expression); } - + function maybeoperator(type, value) { if (type == "operator") { if (/\+\+|--/.test(value)) return cont(maybeoperator); diff --git a/mode/jinja2/jinja2.js b/mode/jinja2/jinja2.js index 1472d398f2..16b06c48ef 100644 --- a/mode/jinja2/jinja2.js +++ b/mode/jinja2/jinja2.js @@ -1,5 +1,5 @@ CodeMirror.defineMode("jinja2", function() { - var keywords = ["block", "endblock", "for", "endfor", "in", "true", "false", + var keywords = ["block", "endblock", "for", "endfor", "in", "true", "false", "loop", "none", "self", "super", "if", "as", "not", "and", "else", "import", "with", "without", "context"]; keywords = new RegExp("^((" + keywords.join(")|(") + "))\\b"); @@ -38,5 +38,5 @@ CodeMirror.defineMode("jinja2", function() { token: function (stream, state) { return state.tokenize(stream, state); } - }; + }; }); diff --git a/mode/less/less.js b/mode/less/less.js index 4f6214363a..6df4790995 100644 --- a/mode/less/less.js +++ b/mode/less/less.js @@ -9,16 +9,16 @@ CodeMirror.defineMode("less", function(config) { function ret(style, tp) {type = tp; return style;} //html tags var tags = "a abbr acronym address applet area article aside audio b base basefont bdi bdo big blockquote body br button canvas caption cite code col colgroup command datalist dd del details dfn dir div dl dt em embed fieldset figcaption figure font footer form frame frameset h1 h2 h3 h4 h5 h6 head header hgroup hr html i iframe img input ins keygen kbd label legend li link map mark menu meta meter nav noframes noscript object ol optgroup option output p param pre progress q rp rt ruby s samp script section select small source span strike strong style sub summary sup table tbody td textarea tfoot th thead time title tr track tt u ul var video wbr".split(' '); - + function inTagsArray(val){ for(var i=0; i= 0) { // Continued list if (state.indentationDiff < 4) { // Only adjust indentation if *not* a code block @@ -123,7 +123,7 @@ CodeMirror.defineMode("markdown", function(cmCfg, modeCfg) { state.list = false; state.listDepth = 0; } - + if (state.indentationDiff >= 4) { state.indentation -= 4; stream.skipToEnd(); @@ -158,7 +158,7 @@ CodeMirror.defineMode("markdown", function(cmCfg, modeCfg) { switchBlock(stream, state, local); return code; } - + return switchInline(stream, state, state.inline); } @@ -193,17 +193,17 @@ CodeMirror.defineMode("markdown", function(cmCfg, modeCfg) { // Inline function getType(state) { var styles = []; - + if (state.taskOpen) { return "meta"; } if (state.taskClosed) { return "property"; } - + if (state.strong) { styles.push(strong); } if (state.em) { styles.push(em); } - + if (state.linkText) { styles.push(linktext); } - + if (state.code) { styles.push(code); } - + if (state.header) { styles.push(header); } if (state.quote) { styles.push(state.quote % 2 ? quote1 : quote2); } if (state.list !== false) { @@ -224,19 +224,19 @@ CodeMirror.defineMode("markdown", function(cmCfg, modeCfg) { if (stream.match(textRE, true)) { return getType(state); } - return undefined; + return undefined; } function inlineNormal(stream, state) { var style = state.text(stream, state); if (typeof style !== 'undefined') return style; - + if (state.list) { // List marker (*, +, -, 1., etc) state.list = null; return getType(state); } - + if (state.taskList) { var taskOpen = stream.match(taskListRE, true)[1] !== "x"; if (taskOpen) state.taskOpen = true; @@ -244,17 +244,17 @@ CodeMirror.defineMode("markdown", function(cmCfg, modeCfg) { state.taskList = false; return getType(state); } - + state.taskOpen = false; state.taskClosed = false; - + var ch = stream.next(); - + if (ch === '\\') { stream.next(); return getType(state); } - + // Matches link titles present on next line if (state.linkTitle) { state.linkTitle = false; @@ -268,7 +268,7 @@ CodeMirror.defineMode("markdown", function(cmCfg, modeCfg) { return linkhref; } } - + // If this block is changed, it may need to be updated in GFM mode if (ch === '`') { var t = getType(state); @@ -289,7 +289,7 @@ CodeMirror.defineMode("markdown", function(cmCfg, modeCfg) { } else if (state.code) { return getType(state); } - + if (ch === '!' && stream.match(/\[[^\]]*\] ?(?:\(|\[)/, false)) { stream.match(/\[[^\]]*\]/); state.inline = state.f = linkHref; @@ -307,15 +307,15 @@ CodeMirror.defineMode("markdown", function(cmCfg, modeCfg) { state.inline = state.f = linkHref; return type; } - + if (ch === '<' && stream.match(/^(https?|ftps?):\/\/(?:[^\\>]|\\.)+>/, true)) { return switchInline(stream, state, inlineElement(linkinline, '>')); } - + if (ch === '<' && stream.match(/^[^> \\]+@(?:[^\\>]|\\.)+>/, true)) { return switchInline(stream, state, inlineElement(linkemail, '>')); } - + if (ch === '<' && stream.match(/^\w/, false)) { if (stream.string.indexOf(">")!=-1) { var atts = stream.string.substring(1,stream.string.indexOf(">")); @@ -326,12 +326,12 @@ CodeMirror.defineMode("markdown", function(cmCfg, modeCfg) { stream.backUp(1); return switchBlock(stream, state, htmlBlock); } - + if (ch === '<' && stream.match(/^\/\w*?>/)) { state.md_inside = false; return "tag"; } - + var ignoreUnderscore = false; if (!modeCfg.underscoresBreakWords) { if (ch === '_' && stream.peek() !== '_' && stream.match(/(\w)/, false)) { @@ -368,7 +368,7 @@ CodeMirror.defineMode("markdown", function(cmCfg, modeCfg) { } } } - + return getType(state); } @@ -414,7 +414,7 @@ CodeMirror.defineMode("markdown", function(cmCfg, modeCfg) { if (!savedInlineRE[endChar]) { // Escape endChar for RegExp (taken from http://stackoverflow.com/a/494122/526741) endChar = (endChar+'').replace(/([.?*+^$[\]\\(){}|-])/g, "\\$1"); - // Match any non-endChar, escaped character, as well as the closing + // Match any non-endChar, escaped character, as well as the closing // endChar. savedInlineRE[endChar] = new RegExp('^(?:[^\\\\]|\\\\.)*?(' + endChar + ')'); } @@ -434,17 +434,17 @@ CodeMirror.defineMode("markdown", function(cmCfg, modeCfg) { startState: function() { return { f: blockNormal, - + prevLineHasContent: false, thisLineHasContent: false, - + block: blockNormal, htmlState: CodeMirror.startState(htmlMode), indentation: 0, - + inline: inlineNormal, text: handleText, - + linkText: false, linkTitle: false, em: false, @@ -460,17 +460,17 @@ CodeMirror.defineMode("markdown", function(cmCfg, modeCfg) { copyState: function(s) { return { f: s.f, - + prevLineHasContent: s.prevLineHasContent, thisLineHasContent: s.thisLineHasContent, - + block: s.block, htmlState: CodeMirror.copyState(htmlMode, s.htmlState), indentation: s.indentation, - + localMode: s.localMode, localState: s.localMode ? CodeMirror.copyState(s.localMode, s.localState) : null, - + inline: s.inline, text: s.text, linkTitle: s.linkTitle, @@ -497,10 +497,10 @@ CodeMirror.defineMode("markdown", function(cmCfg, modeCfg) { // Reset state.header state.header = false; - + // Reset state.taskList state.taskList = false; - + // Reset state.code state.code = false; diff --git a/mode/markdown/test.js b/mode/markdown/test.js index cfc229bc3f..c451412fee 100644 --- a/mode/markdown/test.js +++ b/mode/markdown/test.js @@ -39,20 +39,20 @@ "[comment `]"); // Unclosed backticks - // Instead of simply marking as CODE, it would be nice to have an + // Instead of simply marking as CODE, it would be nice to have an // incomplete flag for CODE, that is styled slightly different. MT("unclosedBackticks", "foo [comment `bar]"); - // Per documentation: "To include a literal backtick character within a - // code span, you can use multiple backticks as the opening and closing + // Per documentation: "To include a literal backtick character within a + // code span, you can use multiple backticks as the opening and closing // delimiters" MT("doubleBackticks", "[comment ``foo ` bar``]"); // Tests based on Dingus // http://daringfireball.net/projects/markdown/dingus - // + // // Multiple backticks within an inline code block MT("consecutiveBackticks", "[comment `foo```bar`]"); @@ -98,10 +98,10 @@ // Setext headers - H1, H2 // Per documentation, "Any number of underlining =’s or -’s will work." // http://daringfireball.net/projects/markdown/syntax#header - // Ideally, the text would be marked as `header` as well, but this is - // not really feasible at the moment. So, instead, we're testing against + // Ideally, the text would be marked as `header` as well, but this is + // not really feasible at the moment. So, instead, we're testing against // what works today, to avoid any regressions. - // + // // Check if single underlining = works MT("setextH1", "foo", @@ -368,7 +368,7 @@ " [comment world]", " [comment foo]", " [variable-2 bar]"); - + // List nesting edge cases MT("listNested", "[variable-2 * foo]", @@ -548,7 +548,7 @@ MT("emInWordUnderscore", "foo[em _bar_]hello"); - // Per documentation: "...surround an * or _ with spaces, it’ll be + // Per documentation: "...surround an * or _ with spaces, it’ll be // treated as a literal asterisk or underscore." MT("emEscapedBySpaceIn", @@ -558,7 +558,7 @@ "foo _ bar[em _hello_]world"); // Unclosed emphasis characters - // Instead of simply marking as EM / STRONG, it would be nice to have an + // Instead of simply marking as EM / STRONG, it would be nice to have an // incomplete flag for EM and STRONG, that is styled slightly different. MT("emIncompleteAsterisk", "foo [em *bar]"); @@ -625,7 +625,7 @@ // Tests to make sure GFM-specific things aren't getting through - + MT("taskList", "[variable-2 * [ ]] bar]"); diff --git a/mode/ntriples/ntriples.js b/mode/ntriples/ntriples.js index ced8645169..ed0cee34b0 100644 --- a/mode/ntriples/ntriples.js +++ b/mode/ntriples/ntriples.js @@ -1,22 +1,22 @@ /********************************************************** -* This script provides syntax highlighting support for +* This script provides syntax highlighting support for * the Ntriples format. -* Ntriples format specification: +* Ntriples format specification: * http://www.w3.org/TR/rdf-testcases/#ntriples ***********************************************************/ -/* +/* The following expression defines the defined ASF grammar transitions. pre_subject -> { ( writing_subject_uri | writing_bnode_uri ) - -> pre_predicate - -> writing_predicate_uri - -> pre_object - -> writing_object_uri | writing_object_bnode | - ( - writing_object_literal + -> pre_predicate + -> writing_predicate_uri + -> pre_object + -> writing_object_uri | writing_object_bnode | + ( + writing_object_literal -> writing_literal_lang | writing_literal_type ) -> post_object @@ -25,7 +25,7 @@ -> ERROR } */ -CodeMirror.defineMode("ntriples", function() { +CodeMirror.defineMode("ntriples", function() { var Location = { PRE_SUBJECT : 0, @@ -45,7 +45,7 @@ CodeMirror.defineMode("ntriples", function() { function transitState(currState, c) { var currLocation = currState.location; var ret; - + // Opening. if (currLocation == Location.PRE_SUBJECT && c == '<') ret = Location.WRITING_SUB_URI; else if(currLocation == Location.PRE_SUBJECT && c == '_') ret = Location.WRITING_BNODE_URI; @@ -53,7 +53,7 @@ CodeMirror.defineMode("ntriples", function() { else if(currLocation == Location.PRE_OBJ && c == '<') ret = Location.WRITING_OBJ_URI; else if(currLocation == Location.PRE_OBJ && c == '_') ret = Location.WRITING_OBJ_BNODE; else if(currLocation == Location.PRE_OBJ && c == '"') ret = Location.WRITING_OBJ_LITERAL; - + // Closing. else if(currLocation == Location.WRITING_SUB_URI && c == '>') ret = Location.PRE_PRED; else if(currLocation == Location.WRITING_BNODE_URI && c == ' ') ret = Location.PRE_PRED; @@ -63,33 +63,33 @@ CodeMirror.defineMode("ntriples", function() { else if(currLocation == Location.WRITING_OBJ_LITERAL && c == '"') ret = Location.POST_OBJ; else if(currLocation == Location.WRITING_LIT_LANG && c == ' ') ret = Location.POST_OBJ; else if(currLocation == Location.WRITING_LIT_TYPE && c == '>') ret = Location.POST_OBJ; - + // Closing typed and language literal. else if(currLocation == Location.WRITING_OBJ_LITERAL && c == '@') ret = Location.WRITING_LIT_LANG; else if(currLocation == Location.WRITING_OBJ_LITERAL && c == '^') ret = Location.WRITING_LIT_TYPE; // Spaces. - else if( c == ' ' && + else if( c == ' ' && ( - currLocation == Location.PRE_SUBJECT || - currLocation == Location.PRE_PRED || - currLocation == Location.PRE_OBJ || + currLocation == Location.PRE_SUBJECT || + currLocation == Location.PRE_PRED || + currLocation == Location.PRE_OBJ || currLocation == Location.POST_OBJ ) ) ret = currLocation; - + // Reset. - else if(currLocation == Location.POST_OBJ && c == '.') ret = Location.PRE_SUBJECT; - + else if(currLocation == Location.POST_OBJ && c == '.') ret = Location.PRE_SUBJECT; + // Error else ret = Location.ERROR; - + currState.location=ret; } return { startState: function() { - return { + return { location : Location.PRE_SUBJECT, uris : [], anchors : [], diff --git a/mode/ocaml/ocaml.js b/mode/ocaml/ocaml.js index 2ce3fb8f0c..7dc3d283b2 100644 --- a/mode/ocaml/ocaml.js +++ b/mode/ocaml/ocaml.js @@ -109,5 +109,5 @@ CodeMirror.defineMode('ocaml', function() { } }; }); - + CodeMirror.defineMIME('text/x-ocaml', 'ocaml'); diff --git a/mode/python/python.js b/mode/python/python.js index 951aaf8197..d69ef8389b 100644 --- a/mode/python/python.js +++ b/mode/python/python.js @@ -4,7 +4,7 @@ CodeMirror.defineMode("python", function(conf, parserConf) { function wordRegexp(words) { return new RegExp("^((" + words.join(")|(") + "))\\b"); } - + var singleOperators = parserConf.singleOperators || new RegExp("^[\\+\\-\\*/%&|\\^~<>!]"); var singleDelimiters = parserConf.singleDelimiters || new RegExp('^[\\(\\)\\[\\]\\{\\}@,:`=;\\.]'); var doubleOperators = parserConf.doubleOperators || new RegExp("^((==)|(!=)|(<=)|(>=)|(<>)|(<<)|(>>)|(//)|(\\*\\*))"); @@ -72,15 +72,15 @@ CodeMirror.defineMode("python", function(conf, parserConf) { if (stream.eatSpace()) { return null; } - + var ch = stream.peek(); - + // Handle Comments if (ch === '#') { stream.skipToEnd(); return 'comment'; } - + // Handle Number Literals if (stream.match(/^[0-9\.]/, false)) { var floatLiteral = false; @@ -116,13 +116,13 @@ CodeMirror.defineMode("python", function(conf, parserConf) { return 'number'; } } - + // Handle Strings if (stream.match(stringPrefixes)) { state.tokenize = tokenStringFactory(stream.current()); return state.tokenize(stream, state); } - + // Handle operators and Delimiters if (stream.match(tripleDelimiters) || stream.match(doubleDelimiters)) { return null; @@ -135,31 +135,31 @@ CodeMirror.defineMode("python", function(conf, parserConf) { if (stream.match(singleDelimiters)) { return null; } - + if (stream.match(keywords)) { return 'keyword'; } - + if (stream.match(builtins)) { return 'builtin'; } - + if (stream.match(identifiers)) { return 'variable'; } - + // Handle non-detected items stream.next(); return ERRORCLASS; } - + function tokenStringFactory(delimiter) { while ('rub'.indexOf(delimiter.charAt(0).toLowerCase()) >= 0) { delimiter = delimiter.substr(1); } var singleline = delimiter.length == 1; var OUTCLASS = 'string'; - + function tokenString(stream, state) { while (!stream.eol()) { stream.eatWhile(/[^'"\\]/); @@ -187,7 +187,7 @@ CodeMirror.defineMode("python", function(conf, parserConf) { tokenString.isString = true; return tokenString; } - + function indent(stream, state, type) { type = type || 'py'; var indentUnit = 0; @@ -210,7 +210,7 @@ CodeMirror.defineMode("python", function(conf, parserConf) { type: type }); } - + function dedent(stream, state, type) { type = type || 'py'; if (state.scopes.length == 1) return; @@ -259,7 +259,7 @@ CodeMirror.defineMode("python", function(conf, parserConf) { } return style; } - + // Handle decorators if (current === '@') { return stream.match(identifiers, false) ? 'meta' : ERRORCLASS; @@ -269,7 +269,7 @@ CodeMirror.defineMode("python", function(conf, parserConf) { && state.lastToken === 'meta') { style = 'meta'; } - + // Handle scope changes. if (current === 'pass' || current === 'return') { state.dedent += 1; @@ -298,7 +298,7 @@ CodeMirror.defineMode("python", function(conf, parserConf) { if (state.scopes.length > 1) state.scopes.shift(); state.dedent -= 1; } - + return style; } @@ -312,27 +312,27 @@ CodeMirror.defineMode("python", function(conf, parserConf) { dedent: 0 }; }, - + token: function(stream, state) { var style = tokenLexer(stream, state); - + state.lastToken = style; - + if (stream.eol() && stream.lambda) { state.lambda = false; } - + return style; }, - + indent: function(state) { if (state.tokenize != tokenBase) { return state.tokenize.isString ? CodeMirror.Pass : 0; } - + return state.scopes[0].offset; } - + }; return external; }); diff --git a/mode/q/q.js b/mode/q/q.js index 6fc4e65a9c..56017e30ad 100644 --- a/mode/q/q.js +++ b/mode/q/q.js @@ -17,7 +17,7 @@ CodeMirror.defineMode("q",function(config){ return state.tokenize=tokenBase,"builtin"; } if(/\s/.test(c)) - return stream.peek()=="/"?(stream.skipToEnd(),"comment"):"whitespace"; + return stream.peek()=="/"?(stream.skipToEnd(),"comment"):"whitespace"; if(c=='"') return(state.tokenize=tokenString)(stream,state); if(c=='`') diff --git a/mode/shell/shell.js b/mode/shell/shell.js index 9ce139b411..ed09284f29 100644 --- a/mode/shell/shell.js +++ b/mode/shell/shell.js @@ -114,5 +114,5 @@ CodeMirror.defineMode('shell', function() { } }; }); - + CodeMirror.defineMIME('text/x-sh', 'shell'); diff --git a/mode/sieve/sieve.js b/mode/sieve/sieve.js index b1bba53e05..8ca2a4cb82 100644 --- a/mode/sieve/sieve.js +++ b/mode/sieve/sieve.js @@ -31,7 +31,7 @@ CodeMirror.defineMode("sieve", function(config) { state.tokenize = tokenString(ch); return state.tokenize(stream, state); } - + if (ch == "(") { state._indent.push("("); // add virtual angel wings so that editor behaves... @@ -44,24 +44,24 @@ CodeMirror.defineMode("sieve", function(config) { state._indent.push("{"); return null; } - + if (ch == ")") { state._indent.pop(); - state._indent.pop(); + state._indent.pop(); } if (ch === "}") { state._indent.pop(); return null; } - + if (ch == ",") return null; - + if (ch == ";") return null; - - + + if (/[{}\(\),;]/.test(ch)) return null; @@ -97,7 +97,7 @@ CodeMirror.defineMode("sieve", function(config) { if (atoms.propertyIsEnumerable(cur)) return "atom"; - + return null; } @@ -169,10 +169,10 @@ CodeMirror.defineMode("sieve", function(config) { var length = state._indent.length; if (_textAfter && (_textAfter[0] == "}")) length--; - + if (length <0) length = 0; - + return length * indentUnit; }, diff --git a/mode/sparql/sparql.js b/mode/sparql/sparql.js index 0b367b2a80..f7237698d3 100644 --- a/mode/sparql/sparql.js +++ b/mode/sparql/sparql.js @@ -117,7 +117,7 @@ CodeMirror.defineMode("sparql", function(config) { state.context.col = stream.column(); } } - + return style; }, diff --git a/mode/sql/sql.js b/mode/sql/sql.js index 78f8d7fbad..15f65920ca 100644 --- a/mode/sql/sql.js +++ b/mode/sql/sql.js @@ -9,7 +9,7 @@ CodeMirror.defineMode("sql", function(config, parserConfig) { support = parserConfig.support || {}, hooks = parserConfig.hooks || {}, dateSQL = parserConfig.dateSQL || {"date" : true, "time" : true, "timestamp" : true}; - + function tokenBase(stream, state) { var ch = stream.next(); @@ -163,7 +163,7 @@ CodeMirror.defineMode("sql", function(config, parserConfig) { (function() { "use strict"; - + // `identifier` function hookIdentifier(stream) { var escaped = false, ch; diff --git a/mode/tcl/tcl.js b/mode/tcl/tcl.js index 58ca709e4c..ed2c697217 100644 --- a/mode/tcl/tcl.js +++ b/mode/tcl/tcl.js @@ -56,7 +56,7 @@ CodeMirror.defineMode("tcl", function() { else if (ch == "$") { stream.eatWhile(/[$_a-z0-9A-Z\.{:]/); stream.eatWhile(/}/); - state.beforeParams = true; + state.beforeParams = true; return "builtin"; } else if (isOperatorChar.test(ch)) { diff --git a/mode/vb/vb.js b/mode/vb/vb.js index 764c07101a..27b2271951 100644 --- a/mode/vb/vb.js +++ b/mode/vb/vb.js @@ -1,10 +1,10 @@ CodeMirror.defineMode("vb", function(conf, parserConf) { var ERRORCLASS = 'error'; - + function wordRegexp(words) { return new RegExp("^((" + words.join(")|(") + "))\\b", "i"); } - + var singleOperators = new RegExp("^[\\+\\-\\*/%&\\\\|\\^~<>!]"); var singleDelimiters = new RegExp('^[\\(\\)\\[\\]\\{\\}@,:`=;\\.]'); var doubleOperators = new RegExp("^((==)|(<>)|(<=)|(>=)|(<>)|(<<)|(>>)|(//)|(\\*\\*))"); @@ -15,9 +15,9 @@ CodeMirror.defineMode("vb", function(conf, parserConf) { var openingKeywords = ['class','module', 'sub','enum','select','while','if','function', 'get','set','property', 'try']; var middleKeywords = ['else','elseif','case', 'catch']; var endKeywords = ['next','loop']; - + var wordOperators = wordRegexp(['and', 'or', 'not', 'xor', 'in']); - var commonkeywords = ['as', 'dim', 'break', 'continue','optional', 'then', 'until', + var commonkeywords = ['as', 'dim', 'break', 'continue','optional', 'then', 'until', 'goto', 'byval','byref','new','handles','property', 'return', 'const','private', 'protected', 'friend', 'public', 'shared', 'static', 'true','false']; var commontypes = ['integer','string','double','decimal','boolean','short','char', 'float','single']; @@ -34,13 +34,13 @@ CodeMirror.defineMode("vb", function(conf, parserConf) { var indentInfo = null; - + function indent(_stream, state) { state.currentIndent++; } - + function dedent(_stream, state) { state.currentIndent--; } @@ -49,16 +49,16 @@ CodeMirror.defineMode("vb", function(conf, parserConf) { if (stream.eatSpace()) { return null; } - + var ch = stream.peek(); - + // Handle Comments if (ch === "'") { stream.skipToEnd(); return 'comment'; } - - + + // Handle Number Literals if (stream.match(/^((&H)|(&O))?[0-9\.a-f]/i, false)) { var floatLiteral = false; @@ -66,7 +66,7 @@ CodeMirror.defineMode("vb", function(conf, parserConf) { if (stream.match(/^\d*\.\d+F?/i)) { floatLiteral = true; } else if (stream.match(/^\d+\.\d*F?/)) { floatLiteral = true; } else if (stream.match(/^\.\d+F?/)) { floatLiteral = true; } - + if (floatLiteral) { // Float literals may be "imaginary" stream.eat(/J/i); @@ -93,13 +93,13 @@ CodeMirror.defineMode("vb", function(conf, parserConf) { return 'number'; } } - + // Handle Strings if (stream.match(stringPrefixes)) { state.tokenize = tokenStringFactory(stream.current()); return state.tokenize(stream, state); } - + // Handle operators and Delimiters if (stream.match(tripleDelimiters) || stream.match(doubleDelimiters)) { return null; @@ -137,28 +137,28 @@ CodeMirror.defineMode("vb", function(conf, parserConf) { dedent(stream,state); return 'keyword'; } - + if (stream.match(types)) { return 'keyword'; } - + if (stream.match(keywords)) { return 'keyword'; } - + if (stream.match(identifiers)) { return 'variable'; } - + // Handle non-detected items stream.next(); return ERRORCLASS; } - + function tokenStringFactory(delimiter) { var singleline = delimiter.length == 1; var OUTCLASS = 'string'; - + return function(stream, state) { while (!stream.eol()) { stream.eatWhile(/[^'"]/); @@ -179,7 +179,7 @@ CodeMirror.defineMode("vb", function(conf, parserConf) { return OUTCLASS; }; } - + function tokenLexer(stream, state) { var style = state.tokenize(stream, state); @@ -195,8 +195,8 @@ CodeMirror.defineMode("vb", function(conf, parserConf) { return ERRORCLASS; } } - - + + var delimiter_index = '[({'.indexOf(current); if (delimiter_index !== -1) { indent(stream, state ); @@ -212,7 +212,7 @@ CodeMirror.defineMode("vb", function(conf, parserConf) { return ERRORCLASS; } } - + return style; } @@ -229,7 +229,7 @@ CodeMirror.defineMode("vb", function(conf, parserConf) { }; }, - + token: function(stream, state) { if (stream.sol()) { state.currentIndent += state.nextLineIndent; @@ -237,24 +237,23 @@ CodeMirror.defineMode("vb", function(conf, parserConf) { state.doInCurrentLine = 0; } var style = tokenLexer(stream, state); - + state.lastToken = {style:style, content: stream.current()}; - - - + + + return style; }, - + indent: function(state, textAfter) { var trueText = textAfter.replace(/^\s+|\s+$/g, '') ; if (trueText.match(closing) || trueText.match(doubleClosing) || trueText.match(middle)) return conf.indentUnit*(state.currentIndent-1); if(state.currentIndent < 0) return 0; return state.currentIndent * conf.indentUnit; } - + }; return external; }); CodeMirror.defineMIME("text/x-vb", "vb"); - diff --git a/mode/xml/xml.js b/mode/xml/xml.js index a68b033841..440343bbe7 100644 --- a/mode/xml/xml.js +++ b/mode/xml/xml.js @@ -86,7 +86,7 @@ CodeMirror.defineMode("xml", function(config, parserConfig) { var ok; if (stream.eat("#")) { if (stream.eat("x")) { - ok = stream.eatWhile(/[a-fA-F\d]/) && stream.eat(";"); + ok = stream.eatWhile(/[a-fA-F\d]/) && stream.eat(";"); } else { ok = stream.eatWhile(/[\d]/) && stream.eat(";"); } diff --git a/mode/xquery/xquery.js b/mode/xquery/xquery.js index d5c859e00a..95decc11b7 100644 --- a/mode/xquery/xquery.js +++ b/mode/xquery/xquery.js @@ -35,18 +35,18 @@ CodeMirror.defineMode("xquery", function() { , atom = {type: "atom", style: "atom"} , punctuation = {type: "punctuation", style: null} , qualifier = {type: "axis_specifier", style: "qualifier"}; - + // kwObj is what is return from this function at the end var kwObj = { 'if': A, 'switch': A, 'while': A, 'for': A, 'else': B, 'then': B, 'try': B, 'finally': B, 'catch': B, - 'element': C, 'attribute': C, 'let': C, 'implements': C, 'import': C, 'module': C, 'namespace': C, - 'return': C, 'super': C, 'this': C, 'throws': C, 'where': C, 'private': C, + 'element': C, 'attribute': C, 'let': C, 'implements': C, 'import': C, 'module': C, 'namespace': C, + 'return': C, 'super': C, 'this': C, 'throws': C, 'where': C, 'private': C, ',': punctuation, 'null': atom, 'fn:false()': atom, 'fn:true()': atom }; - - // a list of 'basic' keywords. For each add a property to kwObj with the value of + + // a list of 'basic' keywords. For each add a property to kwObj with the value of // {type: basic[i], style: "keyword"} e.g. 'after' --> {type: "after", style: "keyword"} var basic = ['after','ancestor','ancestor-or-self','and','as','ascending','assert','attribute','before', 'by','case','cast','child','comment','declare','default','define','descendant','descendant-or-self', @@ -57,20 +57,20 @@ CodeMirror.defineMode("xquery", function() { 'self','some','sortby','stable','text','then','to','treat','typeswitch','union','variable','version','where', 'xquery', 'empty-sequence']; for(var i=0, l=basic.length; i < l; i++) { kwObj[basic[i]] = kw(basic[i]);}; - - // a list of types. For each add a property to kwObj with the value of + + // a list of types. For each add a property to kwObj with the value of // {type: "atom", style: "atom"} - var types = ['xs:string', 'xs:float', 'xs:decimal', 'xs:double', 'xs:integer', 'xs:boolean', 'xs:date', 'xs:dateTime', - 'xs:time', 'xs:duration', 'xs:dayTimeDuration', 'xs:time', 'xs:yearMonthDuration', 'numeric', 'xs:hexBinary', + var types = ['xs:string', 'xs:float', 'xs:decimal', 'xs:double', 'xs:integer', 'xs:boolean', 'xs:date', 'xs:dateTime', + 'xs:time', 'xs:duration', 'xs:dayTimeDuration', 'xs:time', 'xs:yearMonthDuration', 'numeric', 'xs:hexBinary', 'xs:base64Binary', 'xs:anyURI', 'xs:QName', 'xs:byte','xs:boolean','xs:anyURI','xf:yearMonthDuration']; for(var i=0, l=types.length; i < l; i++) { kwObj[types[i]] = atom;}; - + // each operator will add a property to kwObj with value of {type: "operator", style: "keyword"} var operators = ['eq', 'ne', 'lt', 'le', 'gt', 'ge', ':=', '=', '>', '>=', '<', '<=', '.', '|', '?', 'and', 'or', 'div', 'idiv', 'mod', '*', '/', '+', '-']; for(var i=0, l=operators.length; i < l; i++) { kwObj[operators[i]] = operator;}; - + // each axis_specifiers will add a property to kwObj with value of {type: "axis_specifier", style: "qualifier"} - var axis_specifiers = ["self::", "attribute::", "child::", "descendant::", "descendant-or-self::", "parent::", + var axis_specifiers = ["self::", "attribute::", "child::", "descendant::", "descendant-or-self::", "parent::", "ancestor::", "ancestor-or-self::", "following::", "preceding::", "following-sibling::", "preceding-sibling::"]; for(var i=0, l=axis_specifiers.length; i < l; i++) { kwObj[axis_specifiers[i]] = qualifier; }; @@ -80,42 +80,42 @@ CodeMirror.defineMode("xquery", function() { // Used as scratch variables to communicate multiple values without // consing up tons of objects. var type, content; - + function ret(tp, style, cont) { type = tp; content = cont; return style; } - + function chain(stream, state, f) { state.tokenize = f; return f(stream, state); } - + // the primary mode tokenizer function tokenBase(stream, state) { - var ch = stream.next(), + var ch = stream.next(), mightBeFunction = false, isEQName = isEQNameAhead(stream); - + // an XML tag (if not in some sub, chained tokenizer) if (ch == "<") { if(stream.match("!--", true)) return chain(stream, state, tokenXMLComment); - + if(stream.match("![CDATA", false)) { state.tokenize = tokenCDATA; return ret("tag", "tag"); } - + if(stream.match("?", false)) { return chain(stream, state, tokenPreProcessing); } - + var isclose = stream.eat("/"); stream.eatSpace(); var tagName = "", c; while ((c = stream.eat(/[^\s\u00a0=<>\"\'\/?]/))) tagName += c; - + return chain(stream, state, tokenTag(tagName, isclose)); } // start code block @@ -136,7 +136,7 @@ CodeMirror.defineMode("xquery", function() { popStateStack(state); return ret("tag", "tag"); } - else + else return ret("word", "variable"); } // if a number @@ -186,13 +186,13 @@ CodeMirror.defineMode("xquery", function() { // if there's a EQName ahead, consume the rest of the string portion, it's likely a function if(isEQName && ch === '\"') while(stream.next() !== '"'){} if(isEQName && ch === '\'') while(stream.next() !== '\''){} - + // gobble up a word if the character is not known if(!known) stream.eatWhile(/[\w\$_-]/); - + // gobble a colon in the case that is a lib func type call fn:doc var foundColon = stream.eat(":"); - + // if there's not a second colon, gobble another word. Otherwise, it's probably an axis specifier // which should get matched as a keyword if(!stream.eat(":") && foundColon) { @@ -205,27 +205,27 @@ CodeMirror.defineMode("xquery", function() { // is the word a keyword? var word = stream.current(); known = keywords.propertyIsEnumerable(word) && keywords[word]; - - // if we think it's a function call but not yet known, + + // if we think it's a function call but not yet known, // set style to variable for now for lack of something better if(mightBeFunction && !known) known = {type: "function_call", style: "variable def"}; - + // if the previous word was element, attribute, axis specifier, this word should be the name of that if(isInXmlConstructor(state)) { popStateStack(state); return ret("word", "variable", word); } - // as previously checked, if the word is element,attribute, axis specifier, call it an "xmlconstructor" and + // as previously checked, if the word is element,attribute, axis specifier, call it an "xmlconstructor" and // push the stack so we know to look for it on the next word if(word == "element" || word == "attribute" || known.type == "axis_specifier") pushStateStack(state, {type: "xmlconstructor"}); - + // if the word is known, return the details of that else just call this a generic 'word' return known ? ret(known.type, known.style, word) : ret("word", "variable", word); } } - // handle comments, including nested + // handle comments, including nested function tokenComment(stream, state) { var maybeEnd = false, maybeNested = false, nestedCount = 0, ch; while (ch = stream.next()) { @@ -243,7 +243,7 @@ CodeMirror.defineMode("xquery", function() { maybeEnd = (ch == ":"); maybeNested = (ch == "("); } - + return ret("comment", "comment"); } @@ -264,10 +264,10 @@ CodeMirror.defineMode("xquery", function() { // if we're in a string and in an XML block, allow an embedded code block if(stream.match("{", false) && isInXmlAttributeBlock(state)) { state.tokenize = tokenBase; - return ret("string", "string"); + return ret("string", "string"); } - + while (ch = stream.next()) { if (ch == quote) { popStateStack(state); @@ -278,16 +278,16 @@ CodeMirror.defineMode("xquery", function() { // if we're in a string and in an XML block, allow an embedded code block in an attribute if(stream.match("{", false) && isInXmlAttributeBlock(state)) { state.tokenize = tokenBase; - return ret("string", "string"); + return ret("string", "string"); } } } - + return ret("string", "string"); }; } - + // tokenizer for variables function tokenVariable(stream, state) { var isVariableChar = /[\w\$_-]/; @@ -304,7 +304,7 @@ CodeMirror.defineMode("xquery", function() { state.tokenize = tokenBase; return ret("variable", "variable"); } - + // tokenizer for XML tags function tokenTag(name, isclose) { return function(stream, state) { @@ -322,7 +322,7 @@ CodeMirror.defineMode("xquery", function() { return ret("tag", "tag"); } else { - state.tokenize = tokenBase; + state.tokenize = tokenBase; } return ret("tag", "tag"); }; @@ -331,7 +331,7 @@ CodeMirror.defineMode("xquery", function() { // tokenizer for XML attributes function tokenAttribute(stream, state) { var ch = stream.next(); - + if(ch == "/" && stream.eat(">")) { if(isInXmlAttributeBlock(state)) popStateStack(state); if(isInXmlBlock(state)) popStateStack(state); @@ -347,7 +347,7 @@ CodeMirror.defineMode("xquery", function() { if (ch == '"' || ch == "'") return chain(stream, state, tokenString(ch, tokenAttribute)); - if(!isInXmlAttributeBlock(state)) + if(!isInXmlAttributeBlock(state)) pushStateStack(state, { type: "attribute", name: name, tokenize: tokenAttribute}); stream.eat(/[a-zA-Z_:]/); @@ -357,18 +357,18 @@ CodeMirror.defineMode("xquery", function() { // the case where the attribute has not value and the tag was closed if(stream.match(">", false) || stream.match("/", false)) { popStateStack(state); - state.tokenize = tokenBase; + state.tokenize = tokenBase; } return ret("attribute", "attribute"); } - - // handle comments, including nested + + // handle comments, including nested function tokenXMLComment(stream, state) { var ch; while (ch = stream.next()) { if (ch == "-" && stream.match("->", true)) { - state.tokenize = tokenBase; + state.tokenize = tokenBase; return ret("comment", "comment"); } } @@ -380,7 +380,7 @@ CodeMirror.defineMode("xquery", function() { var ch; while (ch = stream.next()) { if (ch == "]" && stream.match("]", true)) { - state.tokenize = tokenBase; + state.tokenize = tokenBase; return ret("comment", "comment"); } } @@ -391,20 +391,20 @@ CodeMirror.defineMode("xquery", function() { var ch; while (ch = stream.next()) { if (ch == "?" && stream.match(">", true)) { - state.tokenize = tokenBase; + state.tokenize = tokenBase; return ret("comment", "comment meta"); } } } - - + + // functions to test the current context of the state function isInXmlBlock(state) { return isIn(state, "tag"); } function isInXmlAttributeBlock(state) { return isIn(state, "attribute"); } function isInXmlConstructor(state) { return isIn(state, "xmlconstructor"); } function isInString(state) { return isIn(state, "string"); } - function isEQNameAhead(stream) { + function isEQNameAhead(stream) { // assume we've already eaten a quote (") if(stream.current() === '"') return stream.match(/^[^\"]+\"\:/, false); @@ -413,21 +413,21 @@ CodeMirror.defineMode("xquery", function() { else return false; } - + function isIn(state, type) { - return (state.stack.length && state.stack[state.stack.length - 1].type == type); + return (state.stack.length && state.stack[state.stack.length - 1].type == type); } - + function pushStateStack(state, newState) { state.stack.push(newState); } - + function popStateStack(state) { state.stack.pop(); var reinstateTokenize = state.stack.length && state.stack[state.stack.length-1].tokenize; state.tokenize = reinstateTokenize || tokenBase; } - + // the interface for the mode API return { startState: function() { From d8a762c4276e7b330d3d0051e2b6a0dc063af287 Mon Sep 17 00:00:00 2001 From: Marijn Haverbeke Date: Tue, 2 Apr 2013 12:04:11 +0200 Subject: [PATCH 0945/5780] Add a bin/lint for easy running of the linter --- CONTRIBUTING.md | 8 +++++--- bin/lint | 11 +++++++++++ 2 files changed, 16 insertions(+), 3 deletions(-) create mode 100755 bin/lint diff --git a/CONTRIBUTING.md b/CONTRIBUTING.md index afc18373cc..4e040ff193 100644 --- a/CONTRIBUTING.md +++ b/CONTRIBUTING.md @@ -56,6 +56,8 @@ should be asked on the test suite under `mode/XXX/test.js`. Feel free to add new test suites to modes that don't have one yet (be sure to link the new tests into `test/index.html`). +- Follow the general code style of the rest of the project (see + below). Run `bin/lint` to verify that the linter is happy. - Make sure all tests pass. Visit `test/index.html` in your browser to run them. - Submit a pull request @@ -65,6 +67,6 @@ should be asked on the - 2 spaces per indentation level, no tabs. - Include semicolons after statements. -- Note that the linter (`test/lint/lint.js`) which is run after each - commit complains about unused variables and functions. Prefix their - names with an underscore to muffle it. +- Note that the linter (`bin/lint`) which is run after each commit + complains about unused variables and functions. Prefix their names + with an underscore to muffle it. diff --git a/bin/lint b/bin/lint new file mode 100755 index 0000000000..3b6338691e --- /dev/null +++ b/bin/lint @@ -0,0 +1,11 @@ +#!/usr/bin/env node + +var lint = require("../test/lint/lint"); + +process.chdir(__dirname.slice(0, __dirname.lastIndexOf("/"))); + +lint.checkDir("mode"); +lint.checkDir("lib"); +lint.checkDir("addon"); + +process.exit(lint.success() ? 0 : 1); From 164bc920784320383f98916fd9a6eabe1f6f81a7 Mon Sep 17 00:00:00 2001 From: Hocdoc Date: Fri, 29 Mar 2013 11:08:36 +0100 Subject: [PATCH 0946/5780] Remove double semicolon from eclipse theme. The double semicolon prevents LESS to compile the CSS file. --- theme/eclipse.css | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/theme/eclipse.css b/theme/eclipse.css index 4807e45156..4137bbe26e 100644 --- a/theme/eclipse.css +++ b/theme/eclipse.css @@ -21,5 +21,5 @@ .cm-s-eclipse .CodeMirror-matchingbracket { outline:1px solid grey; - color:black !important;; + color:black !important; } From ab6d9193ea512ab84e1f7936cf25c5239feb78e3 Mon Sep 17 00:00:00 2001 From: Marijn Haverbeke Date: Tue, 2 Apr 2013 12:09:51 +0200 Subject: [PATCH 0947/5780] Leave selection positions at start of changed range alone --- lib/codemirror.js | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/lib/codemirror.js b/lib/codemirror.js index 493bd71b08..ef7a9c2fdb 100644 --- a/lib/codemirror.js +++ b/lib/codemirror.js @@ -2140,7 +2140,7 @@ window.CodeMirror = (function() { // hint is null, leave the selection alone as much as possible var adjustPos = function(pos) { - if (posLess(pos, change.from)) return pos; + if (!posLess(change.from, pos)) return pos; if (!posLess(change.to, pos)) return end; var line = pos.line + change.text.length - (change.to.line - change.from.line) - 1, ch = pos.ch; From f15a14eed1d658957e756fa7dce13c80d0698b51 Mon Sep 17 00:00:00 2001 From: Marijn Haverbeke Date: Tue, 2 Apr 2013 12:14:42 +0200 Subject: [PATCH 0948/5780] Disable another test on Phantom --- test/test.js | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/test/test.js b/test/test.js index 96f37353aa..a334c5a59c 100644 --- a/test/test.js +++ b/test/test.js @@ -858,7 +858,7 @@ testCM("verticalScroll", function(cm) { cm.setLine(0, "aaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaah"); is(sc.scrollWidth > baseWidth, "scrollbar present"); cm.setLine(0, "foo"); - eq(sc.scrollWidth, baseWidth, "scrollbar gone"); + if (!phantom) eq(sc.scrollWidth, baseWidth, "scrollbar gone"); cm.setLine(0, "aaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaah"); cm.setLine(1, "bbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbh"); is(sc.scrollWidth > baseWidth, "present again"); From 6cf4289de7e5295bd8dc5ee796d0e2bce8f4cbb1 Mon Sep 17 00:00:00 2001 From: Marijn Haverbeke Date: Tue, 2 Apr 2013 12:30:26 +0200 Subject: [PATCH 0949/5780] Make calculateScrollPos behave sanely when object doesn't fit into view --- lib/codemirror.js | 9 +++++++-- 1 file changed, 7 insertions(+), 2 deletions(-) diff --git a/lib/codemirror.js b/lib/codemirror.js index ef7a9c2fdb..8f950f3f0a 100644 --- a/lib/codemirror.js +++ b/lib/codemirror.js @@ -2530,11 +2530,16 @@ window.CodeMirror = (function() { function calculateScrollPos(cm, x1, y1, x2, y2) { var display = cm.display, pt = paddingTop(display); y1 += pt; y2 += pt; + if (y1 < 0) y1 = 0; var screen = display.scroller.clientHeight - scrollerCutOff, screentop = display.scroller.scrollTop, result = {}; var docBottom = cm.doc.height + paddingVert(display); var atTop = y1 < pt + 10, atBottom = y2 + pt > docBottom - 10; - if (y1 < screentop) result.scrollTop = atTop ? 0 : Math.max(0, y1); - else if (y2 > screentop + screen) result.scrollTop = (atBottom ? docBottom : y2) - screen; + if (y1 < screentop) { + result.scrollTop = atTop ? 0 : y1; + } else if (y2 > screentop + screen) { + var newTop = Math.min(y1, (atBottom ? docBottom : y2) - screen); + if (newTop != screentop) result.scrollTop = newTop; + } var screenw = display.scroller.clientWidth - scrollerCutOff, screenleft = display.scroller.scrollLeft; x1 += display.gutters.offsetWidth; x2 += display.gutters.offsetWidth; From 00d0c0b2eb98ebcd4c1a043f5f3aeb43fff9a8bb Mon Sep 17 00:00:00 2001 From: Marijn Haverbeke Date: Tue, 2 Apr 2013 12:32:37 +0200 Subject: [PATCH 0950/5780] Add Code per Node to real-world uses --- doc/realworld.html | 1 + 1 file changed, 1 insertion(+) diff --git a/doc/realworld.html b/doc/realworld.html index d1769559f7..6bebfebc18 100644 --- a/doc/realworld.html +++ b/doc/realworld.html @@ -27,6 +27,7 @@

    { } CodeMi
  • APEye (tool for testing & documenting APIs)
  • BlueGriffon (HTML editor)
  • Cargo Collective (creative publishing platform)
  • +
  • Code per Node (Drupal module)
  • Codebug (PHP Xdebug front-end)
  • CodeMirror movie (scripted editing demos)
  • CodeMirror2-GWT (Google Web Toolkit wrapper)
  • From 4d63c15295b15c54af8fb2ac6193e81d5366d46b Mon Sep 17 00:00:00 2001 From: Marijn Haverbeke Date: Tue, 2 Apr 2013 12:44:05 +0200 Subject: [PATCH 0951/5780] [matchbrackets addon] Skip scanning of huge lines, allow passing of config options Issue #1408 --- addon/edit/matchbrackets.js | 21 ++++++++++++++------- 1 file changed, 14 insertions(+), 7 deletions(-) diff --git a/addon/edit/matchbrackets.js b/addon/edit/matchbrackets.js index 72bca57091..167eece3d5 100644 --- a/addon/edit/matchbrackets.js +++ b/addon/edit/matchbrackets.js @@ -3,11 +3,11 @@ (document.documentMode == null || document.documentMode < 8); var Pos = CodeMirror.Pos; - // Disable brace matching in long lines, since it'll cause hugely slow updates - var maxLineLen = 1000; var matching = {"(": ")>", ")": "(<", "[": "]>", "]": "[<", "{": "}>", "}": "{<"}; function findMatchingBracket(cm) { + var maxScanLen = cm.state._matchBrackets.maxScanLineLength || 10000; + var cur = cm.getCursor(), line = cm.getLineHandle(cur.line), pos = cur.ch - 1; var match = (pos >= 0 && matching[line.text.charAt(pos)]) || matching[line.text.charAt(++pos)]; if (!match) return null; @@ -18,6 +18,7 @@ function scan(line, lineNo, start) { if (!line.text) return; var pos = forward ? 0 : line.text.length - 1, end = forward ? line.text.length : -1; + if (line.text.length > maxScanLen) return null; if (start != null) pos = start + d; for (; pos != end; pos += d) { var ch = line.text.charAt(pos); @@ -38,9 +39,11 @@ } function matchBrackets(cm, autoclear) { + // Disable brace matching in long lines, since it'll cause hugely slow updates + var maxHighlightLen = cm.state._matchBrackets.maxHighlightLineLength || 1000; var found = findMatchingBracket(cm); - if (!found || cm.getLine(found.from.line).length > maxLineLen || - found.to && cm.getLine(found.to.line).length > maxLineLen) + if (!found || cm.getLine(found.from.line).length > maxHighlightLen || + found.to && cm.getLine(found.to.line).length > maxHighlightLen) return; var style = found.match ? "CodeMirror-matchingbracket" : "CodeMirror-nonmatchingbracket"; @@ -64,9 +67,13 @@ }); } - CodeMirror.defineOption("matchBrackets", false, function(cm, val) { - if (val) cm.on("cursorActivity", doMatchBrackets); - else cm.off("cursorActivity", doMatchBrackets); + CodeMirror.defineOption("matchBrackets", false, function(cm, val, old) { + if (old && old != CodeMirror.Init) + cm.off("cursorActivity", doMatchBrackets); + if (val) { + cm.state._matchBrackets = typeof val == "object" ? val : {}; + cm.on("cursorActivity", doMatchBrackets); + } }); CodeMirror.defineExtension("matchBrackets", function() {matchBrackets(this, true);}); From bcade67000aace1fefc7d5712ec8ed608a6bed16 Mon Sep 17 00:00:00 2001 From: Marijn Haverbeke Date: Tue, 2 Apr 2013 12:49:41 +0200 Subject: [PATCH 0952/5780] [matchbrackets addon] Don't fetch tokens on long lines Without a bunch of more involved code, it has quadratic complexity, which gets slow rather fast. Closes #1408 --- addon/edit/matchbrackets.js | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-) diff --git a/addon/edit/matchbrackets.js b/addon/edit/matchbrackets.js index 167eece3d5..e4ff914c6b 100644 --- a/addon/edit/matchbrackets.js +++ b/addon/edit/matchbrackets.js @@ -19,10 +19,11 @@ if (!line.text) return; var pos = forward ? 0 : line.text.length - 1, end = forward ? line.text.length : -1; if (line.text.length > maxScanLen) return null; + var checkTokenStyles = line.text.length < 1000; if (start != null) pos = start + d; for (; pos != end; pos += d) { var ch = line.text.charAt(pos); - if (re.test(ch) && cm.getTokenAt(Pos(lineNo, pos + 1)).type == style) { + if (re.test(ch) && (!checkTokenStyles || cm.getTokenAt(Pos(lineNo, pos + 1)).type == style)) { var match = matching[ch]; if (match.charAt(1) == ">" == forward) stack.push(ch); else if (stack.pop() != match.charAt(0)) return {pos: pos, match: false}; From 5f25446d8f5fad8604d7c87bf4d0663d2fd6d134 Mon Sep 17 00:00:00 2001 From: Hasan Karahan Date: Sat, 30 Mar 2013 16:02:29 +0500 Subject: [PATCH 0953/5780] [rst-mode] Fixed strong, emphasis & literal rST rST does not allow mixing strong with simple emphasis, further it requires the stars to have a preceding and trailing whitespaces. Same for literal text; e.g. **strong** is correct, but **strong* is not, or *emphasis* is correct, but *emphasis** is not. --- mode/rst/rst.js | 6 +++--- 1 file changed, 3 insertions(+), 3 deletions(-) diff --git a/mode/rst/rst.js b/mode/rst/rst.js index 5fed967a67..8de9d75415 100644 --- a/mode/rst/rst.js +++ b/mode/rst/rst.js @@ -501,9 +501,9 @@ CodeMirror.defineMode('rst', function (config, options) { rx_uri_protocol + rx_uri_domain + rx_uri_path ); - var rx_strong = /^\*\*[^\*\s](?:[^\*]*[^\*\s])?\*\*/; - var rx_emphasis = /^\*[^\*\s](?:[^\*]*[^\*\s])?\*/; - var rx_literal = /^``[^`\s](?:[^`]*[^`\s])``/; + var rx_strong = /^\*\*[^\*\s](?:[^\*]*[^\*\s])?\*\*(\s+|$)/; + var rx_emphasis = /^[^\*]\*[^\*\s](?:[^\*]*[^\*\s])?\*(\s+|$)/; + var rx_literal = /^``[^`\s](?:[^`]*[^`\s])``(\s+|$)/; var rx_number = /^(?:[\d]+(?:[\.,]\d+)*)/; var rx_positive = /^(?:\s\+[\d]+(?:[\.,]\d+)*)/; From 6139ad9d45da4c786a7958fdc2eb33198903a102 Mon Sep 17 00:00:00 2001 From: Marijn Haverbeke Date: Tue, 2 Apr 2013 13:11:19 +0200 Subject: [PATCH 0954/5780] Ensure cursorActivity event is fired when the cursor is in changed range Closes #1411 --- lib/codemirror.js | 9 +++++++-- 1 file changed, 7 insertions(+), 2 deletions(-) diff --git a/lib/codemirror.js b/lib/codemirror.js index 8f950f3f0a..e1c79976c6 100644 --- a/lib/codemirror.js +++ b/lib/codemirror.js @@ -1274,6 +1274,7 @@ window.CodeMirror = (function() { userSelChange: null, textChanged: null, selectionChanged: false, + cursorActivity: false, updateMaxLine: false, updateScrollPos: false, id: ++nextOpId @@ -1333,7 +1334,7 @@ window.CodeMirror = (function() { } if (op.textChanged) signal(cm, "change", cm, op.textChanged); - if (op.selectionChanged) signal(cm, "cursorActivity", cm); + if (op.cursorActivity) signal(cm, "cursorActivity", cm); if (delayed) for (var i = 0; i < delayed.length; ++i) delayed[i](); } @@ -2298,6 +2299,9 @@ window.CodeMirror = (function() { }); } + if (!posLess(doc.sel.head, change.from) && !posLess(change.to, doc.sel.head)) + cm.curOp.cursorActivity = true; + updateDoc(doc, change, spans, selAfter, estimateHeight(cm)); if (!cm.options.lineWrapping) { @@ -2424,7 +2428,8 @@ window.CodeMirror = (function() { sel.to = inv ? anchor : head; if (doc.cm) - doc.cm.curOp.updateInput = doc.cm.curOp.selectionChanged = true; + doc.cm.curOp.updateInput = doc.cm.curOp.selectionChanged = + doc.cm.curOp.cursorActivity = true; signalLater(doc, "cursorActivity", doc); } From 01c0c777a3a5f617d89b0759ad45c35d1ec9f6f8 Mon Sep 17 00:00:00 2001 From: Andy Li Date: Sun, 31 Mar 2013 22:52:40 +0800 Subject: [PATCH 0955/5780] Fix various HTML mistakes in docs --- doc/compress.html | 2 +- doc/internals.html | 2 +- doc/manual.html | 1 + doc/oldrelease.html | 4 ++-- 4 files changed, 5 insertions(+), 4 deletions(-) diff --git a/doc/compress.html b/doc/compress.html index a100f9b965..cd4d41bac8 100644 --- a/doc/compress.html +++ b/doc/compress.html @@ -170,7 +170,7 @@

    { } CodeMi -

    +

    with UglifyJS diff --git a/doc/internals.html b/doc/internals.html index 9139528f3d..4336ba4d1c 100644 --- a/doc/internals.html +++ b/doc/internals.html @@ -50,7 +50,7 @@

    { } CodeMi integrated into about a thousand systems by now. The most prominent one, since a few weeks, being Google -code's project hosting. It works, and it's being used widely. +code's project hosting. It works, and it's being used widely.

    Still, I did not start replacing it because I was bored. CodeMirror 1 was heavily reliant on designMode diff --git a/doc/manual.html b/doc/manual.html index 4a601f2709..5244fce777 100644 --- a/doc/manual.html +++ b/doc/manual.html @@ -1177,6 +1177,7 @@

    Widget, gutter, and decoration methods

    the height of the line that contains the widget.

  • +

    Sizing, scrolling and positioning methods

    diff --git a/doc/oldrelease.html b/doc/oldrelease.html index f97a65a8cb..b51e93c328 100644 --- a/doc/oldrelease.html +++ b/doc/oldrelease.html @@ -335,7 +335,7 @@

    { } CodeMi faster, smaller, simpler to use, and less dependent on browser quirks. See this and this - for more information. + for more information.

    28-03-2011: Version 1.0:

    22-02-2011: Version 2.0 beta 2:

    -

    Somewhat more mature API, lots of bugs shaken out. +

    Somewhat more mature API, lots of bugs shaken out.

    17-02-2011: Version 0.94:

      From f0d055b1d4972d5a8347a3b654cb7842e742eb76 Mon Sep 17 00:00:00 2001 From: Yunchi Luo Date: Tue, 2 Apr 2013 00:58:37 -0400 Subject: [PATCH 0956/5780] Implement custom scrolling Ctrl-D/U. --- keymap/vim.js | 38 +++++++++++++++++++++++++++++++++++++- test/vim_test.js | 27 +++++++++++++++++++++++++++ 2 files changed, 64 insertions(+), 1 deletion(-) diff --git a/keymap/vim.js b/keymap/vim.js index 77b696d93b..ba0e0942c1 100644 --- a/keymap/vim.js +++ b/keymap/vim.js @@ -131,6 +131,12 @@ motion: 'moveByPage', motionArgs: { forward: true }}, { keys: ['Ctrl-b'], type: 'motion', motion: 'moveByPage', motionArgs: { forward: false }}, + { keys: ['Ctrl-d'], type: 'motion', + motion: 'moveByScroll', + motionArgs: { forward: true, explicitRepeat: true }}, + { keys: ['Ctrl-u'], type: 'motion', + motion: 'moveByScroll', + motionArgs: { forward: false, explicitRepeat: true }}, { keys: ['g', 'g'], type: 'motion', motion: 'moveToLineOrEdgeOfDocument', motionArgs: { forward: false, explicitRepeat: true, linewise: true }}, @@ -1068,6 +1074,7 @@ switch (vim.lastMotion) { case this.moveByLines: case this.moveByDisplayLines: + case this.moveByScroll: case this.moveToColumn: case this.moveToEol: endCh = vim.lastHPos; @@ -1091,6 +1098,7 @@ var cur = cm.getCursor(); switch (vim.lastMotion) { case this.moveByDisplayLines: + case this.moveByScroll: case this.moveByLines: case this.moveToColumn: case this.moveToEol: @@ -1100,7 +1108,17 @@ } var repeat = motionArgs.repeat; var res=cm.findPosV(cur,(motionArgs.forward ? repeat : -repeat),"line",vim.lastHSPos); - if(res.hitSide)return null; + if (res.hitSide) { + if (motionArgs.forward) { + var lastCharCoords = cm.charCoords(res, 'div'); + var goalCoords = { top: lastCharCoords.top + 8, left: vim.lastHSPos }; + var res = cm.coordsChar(goalCoords, 'div'); + } else { + var resCoords = cm.charCoords({ line: cm.firstLine(), ch: 0}, 'div'); + resCoords.left = vim.lastHSPos; + res = cm.coordsChar(resCoords, 'div'); + } + } vim.lastHPos = res.ch; return res; }, @@ -1131,6 +1149,24 @@ } return { line: line, ch: 0 }; }, + moveByScroll: function(cm, motionArgs, vim) { + var globalState = getVimGlobalState(); + var scrollbox = cm.getScrollInfo(); + var curEnd = null; + var repeat = motionArgs.repeat; + if (!repeat) { + repeat = scrollbox.clientHeight / (2 * cm.defaultTextHeight()); + } + var orig = cm.charCoords(cm.getCursor(), 'local'); + motionArgs.repeat = repeat; + var curEnd = motions.moveByDisplayLines(cm, motionArgs, vim); + if (!curEnd) { + return null; + } + var dest = cm.charCoords(curEnd, 'local'); + cm.scrollTo(null, scrollbox.top + dest.top - orig.top); + return curEnd; + }, moveByWords: function(cm, motionArgs) { return moveToWord(cm, motionArgs.repeat, !!motionArgs.forward, !!motionArgs.wordEnd, !!motionArgs.bigWord); diff --git a/test/vim_test.js b/test/vim_test.js index f91be02f9e..fb0254a9ba 100644 --- a/test/vim_test.js +++ b/test/vim_test.js @@ -304,6 +304,33 @@ testVim('j_k_and_gj_gk', function(cm,vim,helpers){ helpers.doKeys('k'); helpers.assertCursorAt(0, 176); },{ lineWrapping:true, value: 'This line is intentially long to test movement of gj and gk over wrapped lines. I will start on the end of this line, then make a step up and back to set the origin for j and k.\nThis line is supposed to be even longer than the previous. I will jump here and make another wiggle with gj and gk, before I jump back to the line above. Both wiggles should not change my cursor\'s target character but both j/k and gj/gk change each other\'s reference position.'}); +testVim('gj_gk', function(cm, vim, helpers) { + cm.setSize(120); + // Test top of document edge case. + cm.setCursor(0, 4); + helpers.doKeys('g', 'j'); + helpers.doKeys('10', 'g', 'k'); + helpers.assertCursorAt(0, 4); + + // Test moving down preserves column position. + helpers.doKeys('g', 'j'); + var pos1 = cm.getCursor(); + var expectedPos2 = { line: 0, ch: (pos1.ch - 4) * 2 + 4}; + helpers.doKeys('g', 'j'); + helpers.assertCursorAt(expectedPos2); + + // Move to the last character + cm.setCursor(0, 0); + // Move left to reset HSPos + helpers.doKeys('h'); + // Test bottom of document edge case. + helpers.doKeys('100', 'g', 'j'); + var endingPos = cm.getCursor(); + is(endingPos != 0, 'gj should not be on wrapped line 0'); + var topLeftCharCoords = cm.charCoords(makeCursor(0, 0)); + var endingCharCoords = cm.charCoords(endingPos); + is(topLeftCharCoords.left == endingCharCoords.left, 'gj should end up on column 0'); +},{ lineNumbers: false, lineWrapping:true, value: 'Thislineisintentiallylongtotestmovementofgjandgkoverwrappedlines.' }); testVim('}', function(cm, vim, helpers) { cm.setCursor(0, 0); helpers.doKeys('}'); From 37456d086e9da30b21493f20b901f0e890f31590 Mon Sep 17 00:00:00 2001 From: Marijn Haverbeke Date: Tue, 2 Apr 2013 13:21:14 +0200 Subject: [PATCH 0957/5780] Disable another test that fails on Phantom but not on real Webkit --- test/vim_test.js | 1 + 1 file changed, 1 insertion(+) diff --git a/test/vim_test.js b/test/vim_test.js index fb0254a9ba..bc04b7fc70 100644 --- a/test/vim_test.js +++ b/test/vim_test.js @@ -305,6 +305,7 @@ testVim('j_k_and_gj_gk', function(cm,vim,helpers){ helpers.assertCursorAt(0, 176); },{ lineWrapping:true, value: 'This line is intentially long to test movement of gj and gk over wrapped lines. I will start on the end of this line, then make a step up and back to set the origin for j and k.\nThis line is supposed to be even longer than the previous. I will jump here and make another wiggle with gj and gk, before I jump back to the line above. Both wiggles should not change my cursor\'s target character but both j/k and gj/gk change each other\'s reference position.'}); testVim('gj_gk', function(cm, vim, helpers) { + if (phantom) return; cm.setSize(120); // Test top of document edge case. cm.setCursor(0, 4); From 11805dda45b8432d852831ae3bf874c83e037d8b Mon Sep 17 00:00:00 2001 From: Marijn Haverbeke Date: Tue, 2 Apr 2013 23:40:45 +0200 Subject: [PATCH 0958/5780] Fix newlineAndIndent command Closes #1415 --- lib/codemirror.js | 6 ++++-- 1 file changed, 4 insertions(+), 2 deletions(-) diff --git a/lib/codemirror.js b/lib/codemirror.js index e1c79976c6..e4171e82a5 100644 --- a/lib/codemirror.js +++ b/lib/codemirror.js @@ -2609,6 +2609,7 @@ window.CodeMirror = (function() { if (indentString != curSpaceString) replaceRange(cm.doc, indentString, Pos(n, 0), Pos(n, curSpaceString.length), "+input"); line.stateAfter = null; + return indentString.length; } function changeLine(cm, handle, op) { @@ -2756,7 +2757,7 @@ window.CodeMirror = (function() { if (dir == null) dir = this.options.smartIndent ? "smart" : "prev"; else dir = dir ? "add" : "subtract"; } - if (isLine(this.doc, n)) indentLine(this, n, dir, aggressive); + if (isLine(this.doc, n)) return indentLine(this, n, dir, aggressive); }), indentSelection: operation(null, function(how) { var sel = this.doc.sel; @@ -3296,7 +3297,8 @@ window.CodeMirror = (function() { newlineAndIndent: function(cm) { operation(cm, function() { cm.replaceSelection("\n", "end", "+input"); - cm.indentLine(cm.getCursor().line, null, true); + var line = cm.getCursor().line, indented = cm.indentLine(line, null, true); + if (indented) cm.setCursor(line, indented); })(); }, toggleOverwrite: function(cm) {cm.toggleOverwrite();} From 28b85eef53741aae9c9acaaee104bd18bf981a9f Mon Sep 17 00:00:00 2001 From: Marijn Haverbeke Date: Wed, 3 Apr 2013 08:52:54 +0200 Subject: [PATCH 0959/5780] Document second argument to replaceSelection --- doc/manual.html | 9 +++++++-- 1 file changed, 7 insertions(+), 2 deletions(-) diff --git a/doc/manual.html b/doc/manual.html index 5244fce777..fd1a49f360 100644 --- a/doc/manual.html +++ b/doc/manual.html @@ -773,8 +773,13 @@

      Cursor and selection methods

      doc.getSelection() → string
      Get the currently selected code.
      -
      doc.replaceSelection(string)
      -
      Replace the selection with the given string.
      +
      doc.replaceSelection(string, collapse)
      +
      Replace the selection with the given string. By default, the + new selection will span the inserted text. The + optional collapse argument can be used to change + this—passing "start" or "end" will + collapse the selection to the start or end of the inserted + text.
      doc.getCursor(start) → object
      start is a an optional string indicating which From 90209f8cb05f815adc94b0b67a36c18c9eacbaa1 Mon Sep 17 00:00:00 2001 From: Marijn Haverbeke Date: Wed, 3 Apr 2013 09:05:36 +0200 Subject: [PATCH 0960/5780] [javascript mode] Handle non-typical for-initializers properly Closes #1418 --- mode/javascript/javascript.js | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/mode/javascript/javascript.js b/mode/javascript/javascript.js index 6f7c33c2ac..b2df95f639 100644 --- a/mode/javascript/javascript.js +++ b/mode/javascript/javascript.js @@ -358,7 +358,7 @@ CodeMirror.defineMode("javascript", function(config, parserConfig) { if (type == "var") return cont(vardef1, expect(";"), forspec2); if (type == ";") return cont(forspec2); if (type == "variable") return cont(formaybein); - return cont(forspec2); + return pass(expression, expect(";"), forspec2); } function formaybein(_type, value) { if (value == "in") return cont(expression); From c1b7ea430aca76aaad9ce1c79990086f10a14c04 Mon Sep 17 00:00:00 2001 From: Albert Xing Date: Tue, 2 Apr 2013 16:26:20 -0700 Subject: [PATCH 0961/5780] [closebrackets addon] Do not auto-close single apostrophes in comments Make exception for auto-closing single apostrophes in comments, as per adobe/brackets#3246 --- addon/edit/closebrackets.js | 11 ++++++++--- 1 file changed, 8 insertions(+), 3 deletions(-) diff --git a/addon/edit/closebrackets.js b/addon/edit/closebrackets.js index b46caca02a..5e78ba2f03 100644 --- a/addon/edit/closebrackets.js +++ b/addon/edit/closebrackets.js @@ -17,7 +17,7 @@ if (cm.somethingSelected()) return CodeMirror.Pass; var cur = cm.getCursor(), line = cm.getLine(cur.line); if (cur.ch && cur.ch < line.length && - pairs.indexOf(line.slice(cur.ch - 1, cur.ch + 1)) % 2 == 0) + pairs.indexOf(line.slice(cur.ch - 1, cur.ch + 1)) % 2 == 0) cm.replaceRange("", CodeMirror.Pos(cur.line, cur.ch - 1), CodeMirror.Pos(cur.line, cur.ch + 1)); else return CodeMirror.Pass; @@ -27,8 +27,8 @@ for (var i = 0; i < pairs.length; i += 2) (function(left, right) { if (left != right) closingBrackets.push(right); function surround(cm) { - var selection = cm.getSelection(); - cm.replaceSelection(left + selection + right); + var selection = cm.getSelection(); + cm.replaceSelection(left + selection + right); } function maybeOverwrite(cm) { var cur = cm.getCursor(), ahead = cm.getRange(cur, CodeMirror.Pos(cur.line, cur.ch + 1)); @@ -36,6 +36,11 @@ else cm.execCommand("goCharRight"); } map["'" + left + "'"] = function(cm) { + var type = cm.getTokenAt(cm.getCursor()).type; + if (left === "'" && type === "comment") { + cm.replaceSelection("'", {head: ahead, anchor: ahead}); + return; + } if (cm.somethingSelected()) return surround(cm); if (left == right && maybeOverwrite(cm) != CodeMirror.Pass) return; var cur = cm.getCursor(), ahead = CodeMirror.Pos(cur.line, cur.ch + 1); From de0d4ebad64dd07695e23421e577e4219980cc40 Mon Sep 17 00:00:00 2001 From: Marijn Haverbeke Date: Wed, 3 Apr 2013 09:15:30 +0200 Subject: [PATCH 0962/5780] [closebrackets addon] Clean up c1b7ea430aca76aaad9ce1c79990086f10a14c04 --- addon/edit/closebrackets.js | 7 ++----- 1 file changed, 2 insertions(+), 5 deletions(-) diff --git a/addon/edit/closebrackets.js b/addon/edit/closebrackets.js index 5e78ba2f03..43902aee68 100644 --- a/addon/edit/closebrackets.js +++ b/addon/edit/closebrackets.js @@ -36,11 +36,8 @@ else cm.execCommand("goCharRight"); } map["'" + left + "'"] = function(cm) { - var type = cm.getTokenAt(cm.getCursor()).type; - if (left === "'" && type === "comment") { - cm.replaceSelection("'", {head: ahead, anchor: ahead}); - return; - } + if (left == "'" && cm.getTokenAt(cm.getCursor()).type == "comment") + return CodeMirror.Pass; if (cm.somethingSelected()) return surround(cm); if (left == right && maybeOverwrite(cm) != CodeMirror.Pass) return; var cur = cm.getCursor(), ahead = CodeMirror.Pos(cur.line, cur.ch + 1); From f50582c497c21507f029f919ecd4d6ca99434de3 Mon Sep 17 00:00:00 2001 From: Marijn Haverbeke Date: Wed, 3 Apr 2013 16:44:05 +0200 Subject: [PATCH 0963/5780] Use a slightly more complicated heuristic for adjusting cursor pos at start of change Issue #1415 --- lib/codemirror.js | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/lib/codemirror.js b/lib/codemirror.js index e4171e82a5..ac8b41a0ae 100644 --- a/lib/codemirror.js +++ b/lib/codemirror.js @@ -2141,7 +2141,7 @@ window.CodeMirror = (function() { // hint is null, leave the selection alone as much as possible var adjustPos = function(pos) { - if (!posLess(change.from, pos)) return pos; + if (posEq(change.from, change.to) ? posLess(pos, change.from) : !posLess(change.from, pos)) return pos; if (!posLess(change.to, pos)) return end; var line = pos.line + change.text.length - (change.to.line - change.from.line) - 1, ch = pos.ch; From c984988179f8093528eac125b1cdd4f5e67ee7da Mon Sep 17 00:00:00 2001 From: Marijn Haverbeke Date: Thu, 4 Apr 2013 10:45:30 +0200 Subject: [PATCH 0964/5780] [javascript-hint addon] Fix undefined variable bug --- addon/hint/javascript-hint.js | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/addon/hint/javascript-hint.js b/addon/hint/javascript-hint.js index e3366147e3..b961c5abe9 100644 --- a/addon/hint/javascript-hint.js +++ b/addon/hint/javascript-hint.js @@ -25,7 +25,7 @@ // If it's not a 'word-style' token, ignore the token. if (!/^[\w$_]*$/.test(token.string)) { - token = tprop = {start: cur.ch, end: cur.ch, string: "", state: state, + token = tprop = {start: cur.ch, end: cur.ch, string: "", state: token.state, type: token.string == "." ? "property" : null}; } // If it is a property, find out what it is a property of. From 58e59e3acf651a3f32a58333e5f6a0412c819eb9 Mon Sep 17 00:00:00 2001 From: Marijn Haverbeke Date: Thu, 4 Apr 2013 10:56:45 +0200 Subject: [PATCH 0965/5780] [javascript mode] Another fix to parsing for-loop specs Issue #1418 --- mode/javascript/javascript.js | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/mode/javascript/javascript.js b/mode/javascript/javascript.js index b2df95f639..89f1220369 100644 --- a/mode/javascript/javascript.js +++ b/mode/javascript/javascript.js @@ -367,7 +367,7 @@ CodeMirror.defineMode("javascript", function(config, parserConfig) { function forspec2(type, value) { if (type == ";") return cont(forspec3); if (value == "in") return cont(expression); - return cont(expression, expect(";"), forspec3); + return pass(expression, expect(";"), forspec3); } function forspec3(type) { if (type != ")") cont(expression); From 17d836adebf1ddbbeb9635b4f1e55690c1935876 Mon Sep 17 00:00:00 2001 From: Andy Li Date: Thu, 4 Apr 2013 03:26:50 +0800 Subject: [PATCH 0966/5780] Standardized the type format used in manual. --- doc/manual.html | 373 ++++++++++++++++++++++++++---------------------- 1 file changed, 200 insertions(+), 173 deletions(-) diff --git a/doc/manual.html b/doc/manual.html index fd1a49f360..fbf30573d2 100644 --- a/doc/manual.html +++ b/doc/manual.html @@ -119,11 +119,11 @@

      Configuration

      These are the supported options:

      -
      value (string or Doc)
      +
      value: string|CodeMirror.Doc
      The starting value of the editor. Can be a string, or a document object.
      -
      mode (string or object)
      +
      mode: string|object
      The mode to use. When not given, this will default to the first mode that was loaded. It may be a string, which either simply names the mode or is @@ -140,7 +140,7 @@

      Configuration

      mode names to their constructors, and the second maps MIME types to mode specs.
      -
      theme (string)
      +
      theme: string
      The theme to style the editor with. You must make sure the CSS file defining the corresponding .cm-s-[name] styles is loaded (see @@ -152,29 +152,29 @@

      Configuration

      the cm-s-foo and the cm-s-bar classes to the editor.
      -
      indentUnit (integer)
      +
      indentUnit: integer
      How many spaces a block (whatever that means in the edited language) should be indented. The default is 2.
      -
      smartIndent (boolean)
      +
      smartIndent: boolean
      Whether to use the context-sensitive indentation that the mode provides (or just indent the same as the line before). Defaults to true.
      -
      tabSize (integer)
      +
      tabSize: integer
      The width of a tab character. Defaults to 4.
      -
      indentWithTabs (boolean)
      +
      indentWithTabs: boolean
      Whether, when indenting, the first N*tabSize spaces should be replaced by N tabs. Default is false.
      -
      electricChars (boolean)
      +
      electricChars: boolean
      Configures whether the editor should re-indent the current line when a character is typed that might change its proper indentation (only works if the mode supports indentation). Default is true.
      -
      rtlMoveVisually (boolean)
      +
      rtlMoveVisually: boolean
      Determines whether horizontal cursor movement through right-to-left (Arabic, Hebrew) text is visual (pressing the left arrow moves the cursor left) or logical (pressing the left arrow @@ -182,7 +182,7 @@

      Configuration

      right in right-to-left text). The default is false on Windows, and true on other platforms.
      -
      keyMap (string)
      +
      keyMap: string
      Configures the keymap to use. The default is "default", which is the only keymap defined in codemirror.js itself. Extra keymaps are found in @@ -190,28 +190,28 @@

      Configuration

      the section on keymaps for more information.
      -
      extraKeys (object)
      +
      extraKeys: object
      Can be used to specify extra keybindings for the editor, alongside the ones defined by keyMap. Should be either null, or a valid keymap value.
      -
      lineWrapping (boolean)
      +
      lineWrapping: boolean
      Whether CodeMirror should scroll or wrap for long lines. Defaults to false (scroll).
      -
      lineNumbers (boolean)
      +
      lineNumbers: boolean
      Whether to show line numbers to the left of the editor.
      -
      firstLineNumber (integer)
      +
      firstLineNumber: integer
      At which number to start counting lines. Default is 1.
      -
      lineNumberFormatter (function)
      +
      lineNumberFormatter: (line: integer) → string
      A function used to format line numbers. The function is passed the line number, and should return a string that will be shown in the gutter.
      -
      gutters (array)
      +
      gutters: array<string>
      Can be used to add extra gutters (beyond or instead of the line number gutter). Should be an array of CSS class names, each of which defines a width (and optionally a @@ -223,31 +223,31 @@

      Configuration

      names are the keys passed to setGutterMarker.
      -
      fixedGutter (boolean)
      +
      fixedGutter: boolean
      Determines whether the gutter scrolls along with the content horizontally (false) or whether it stays fixed during horizontal scrolling (true, the default).
      -
      readOnly (boolean)
      +
      readOnly: boolean|string
      This disables editing of the editor content by the user. If the special value "nocursor" is given (instead of simply true), focusing of the editor is also disallowed.
      -
      showCursorWhenSelecting (boolean)
      +
      showCursorWhenSelecting: boolean
      Whether the cursor should be drawn when a selection is active. Defaults to false.
      -
      undoDepth (integer)
      +
      undoDepth: integer
      The maximum number of undo levels that the editor stores. Defaults to 40.
      -
      tabindex (integer)
      +
      tabindex: integer
      The tab index to assign to the editor. If not given, no tab index will be assigned.
      -
      autofocus (boolean)
      +
      autofocus: boolean
      Can be used to make CodeMirror focus itself on initialization. Defaults to off. When fromTextArea is @@ -262,10 +262,10 @@

      Configuration

      might want to skip them the first time you read this manual.

      -
      dragDrop (boolean)
      +
      dragDrop: boolean
      Controls whether drag-and-drop is enabled. On by default.
      -
      onDragEvent (function)
      +
      onDragEvent: (instance: CodeMirror, event: Event) → boolean
      When given, this will be called when the editor is handling a dragenter, dragover, or drop event. It will be passed the editor instance @@ -274,7 +274,7 @@

      Configuration

      return true to indicate that CodeMirror should not do anything further.
      -
      onKeyEvent (function)
      +
      onKeyEvent: (instance: CodeMirror, event: Event) → boolean
      This provides a rather low-level hook into CodeMirror's key handling. If provided, this function will be called on every keydown, keyup, @@ -293,32 +293,35 @@

      Configuration

      is keydown (or keypress for actions that need character data).
      -
      cursorBlinkRate (number)
      +
      cursorBlinkRate: number
      Half-period in milliseconds used for cursor blinking. The default blink rate is 530ms.
      -
      cursorHeight (number)
      +
      cursorHeight: number
      Determines the height of the cursor. Default is 1, meaning it spans the whole height of the line. For some fonts (and by some tastes) a smaller height (for example 0.85), which causes the cursor to not reach all the way to the bottom of the line, looks better
      -
      workTime, workDelay (number)
      +
      workTime: number
      Highlighting is done by a pseudo background-thread that will work for workTime milliseconds, and then use timeout to sleep for workDelay milliseconds. The defaults are 200 and 300, you can change these options to make the highlighting more or less aggressive.
      -
      pollInterval (number)
      +
      workDelay: number
      +
      See workTime.
      + +
      pollInterval: number
      Indicates how quickly CodeMirror should poll its input textarea for changes (when focused). Most input is captured by events, but some things, like IME input on some browsers, don't generate events that allow CodeMirror to properly detect it. Thus, it polls. Default is 100 milliseconds.
      -
      flattenSpans (boolean)
      +
      flattenSpans: boolean
      By default, CodeMirror will combine adjacent tokens into a single span if they have the same class. This will result in a simpler DOM tree, and thus perform better. With some kinds of @@ -326,14 +329,14 @@

      Configuration

      document looks. You can set this option to false to disable this behavior.
      -
      maxHighlightLength (number)
      +
      maxHighlightLength: number
      When highlighting long lines, in order to stay responsive, the editor will give up and simply style the rest of the line as plain text when it reaches a certain position. The default is 10000. You can set this to Infinity to turn off this behavior.
      -
      viewportMargin (integer)
      +
      viewportMargin: integer
      Specifies the amount of lines that are rendered above and below the part of the document that's currently scrolled into view. This affects the amount of updates needed when scrolling, @@ -357,7 +360,8 @@

      Events

      refers to the editor instance.

      -
      "change" (instance, changeObj)
      +
      "change" (instance: CodeMirror, changeObj: { from: {line, ch}, to: {line, ch}, text: array<string>, removed: string, + ?next:changeObj })
      Fires every time the content of the editor is changed. The changeObj is a {from, to, text, removed, next} object containing information about the changes @@ -374,7 +378,7 @@

      Events

      a next property pointing to another change object (which may point to another, etc).
      -
      "beforeChange" (instance, change)
      +
      "beforeChange" (instance: CodeMirror, change: { from: {line, ch}, to: {line, ch}, text: array<string>, removed: string, update: (?from: {line, ch}, ?to: {line, ch}, ?text: array<string>) → void, cancel: () → void })
      This event is fired before a change is applied, and its handler may choose to modify or cancel the change. The change object @@ -394,11 +398,11 @@

      Events

      the CodeMirror implementation, probably cause the editor to become corrupted.
      -
      "cursorActivity" (instance)
      +
      "cursorActivity" (instance: CodeMirror)
      Will be fired when the cursor or selection moves, or any change is made to the editor content.
      -
      "beforeSelectionChange" (instance, selection)
      +
      "beforeSelectionChange" (instance: CodeMirror, selection: { head: {line, ch}, anchor: {line, ch} })
      This event is fired before the selection is moved. Its handler may modify the resulting selection head and anchor. The selection parameter is an object @@ -410,13 +414,13 @@

      Events

      handlers — they should not do anything to directly update the state of the editor.
      -
      "viewportChange" (instance, from, to)
      +
      "viewportChange" (instance: CodeMirror, from: number, to: number)
      Fires whenever the view port of the editor changes (due to scrolling, editing, or any other factor). The from and to arguments give the new start and end of the viewport.
      -
      "gutterClick" (instance, line, gutter, clickEvent)
      +
      "gutterClick" (instance: CodeMirror, line: integer, gutter: string, clickEvent: Event)
      Fires when the editor gutter (the line-number area) is clicked. Will pass the editor instance as first argument, the (zero-based) number of the line that was clicked as second @@ -424,16 +428,19 @@

      Events

      argument, and the raw mousedown event object as fourth argument.
      -
      "focus", "blur" (instance)
      -
      These fire whenever the editor is focused or unfocused.
      +
      "focus" (instance: CodeMirror)
      +
      Fires whenever the editor is focused.
      + +
      "blur" (instance: CodeMirror)
      +
      Fires whenever the editor is unfocused.
      -
      "scroll" (instance)
      +
      "scroll" (instance: CodeMirror)
      Fires when the editor is scrolled.
      -
      "update" (instance)
      +
      "update" (instance: CodeMirror)
      Will be fired whenever CodeMirror updates its DOM display.
      -
      "renderLine" (instance, line, element)
      +
      "renderLine" (instance: CodeMirror, line: integer, element: Element)
      Fired whenever a line is (re-)rendered to the DOM. Fired right after the DOM element is built, before it is added to the document. The handler may mess with the style of @@ -449,7 +456,7 @@

      Events

      following events:

      -
      "change" (doc, changeObj)
      +
      "change" (doc: CodeMirror.Doc, changeObj: { from: {line, ch}, to: {line, ch}, text: array<string>, removed: string })
      Fired whenever a change occurs to the document. changeObj has a similar type as the object passed to the @@ -458,15 +465,15 @@

      Events

      document change events are not batched (whereas editor change events are).
      -
      "beforeChange" (doc, change)
      +
      "beforeChange" (doc: CodeMirror.Doc, change: { from: {line, ch}, to: {line, ch}, text: array<string>, removed: string })
      See the description of the same event on editor instances.
      -
      "cursorActivity" (doc)
      +
      "cursorActivity" (doc: CodeMirror.Doc)
      Fired whenever the cursor or selection in this document changes.
      -
      "beforeSelectionChange" (doc, selection)
      +
      "beforeSelectionChange" (doc: CodeMirror.Doc, selection: { head: {line, ch}, anchor: {line, ch} })
      Equivalent to the event by the same name as fired on editor instances.
      @@ -482,7 +489,7 @@

      Events

      is associated with the start of the line. Mostly useful when you need to find out when your gutter markers on a given line are removed.
      -
      "change" (line, changeObj)
      +
      "change" (line: LineHandle, changeObj: { from: {line, ch}, to: {line, ch}, text: array<string>, removed: string })
      Fires when the line's text content is changed in any way (but the line is not deleted outright). The change object is similar to the one passed @@ -490,7 +497,7 @@

      Events

      object.
      -

      Marked range handles, as returned +

      Marked range handles (CodeMirror.TextMarker), as returned by markText and setBookmark, emit the following events:

      @@ -517,7 +524,7 @@

      Events

      operation brought the marker back.
      -

      Line widgets, returned +

      Line widgets (CodeMirror.LineWidget), returned by addLineWidget, fire these events:

      @@ -704,49 +711,55 @@

      Programming API

      Content manipulation methods

      -
      doc.getValue() → string
      +
      doc.getValue(?seperator: string): string
      Get the current editor content. You can pass it an optional argument to specify the string to be used to separate lines (defaults to "\n").
      -
      doc.setValue(string)
      +
      doc.setValue(content: string): void
      Set the editor content.
      -
      doc.getRange(from, to) → string
      +
      doc.getRange(from: {line, ch}, to: {line, ch}, ?seperator: string): string
      Get the text between the given points in the editor, which should be {line, ch} objects. An optional third argument can be given to indicate the line separator string to use (defaults to "\n").
      -
      doc.replaceRange(string, from, to)
      +
      doc.replaceRange(replacement: string, from: {line, ch}, to: {line, ch}): void
      Replace the part of the document between from and to with the given string. from and to must be {line, ch} objects. to can be left off to simply insert the string at position from.
      -
      doc.getLine(n) → string
      +
      doc.getLine(n: integer): string
      Get the content of line n.
      -
      doc.setLine(n, text)
      +
      doc.setLine(n: integer, text: string): void
      Set the content of line n.
      -
      doc.removeLine(n)
      +
      doc.removeLine(n: integer): void
      Remove the given line from the document.
      -
      doc.lineCount() → number
      +
      doc.lineCount(): integer
      Get the number of lines in the editor.
      -
      doc.firstLine() → number
      -
      doc.lastLine() → number
      -
      Get the first and last lines of the editor. This will - usually be zero and doc.lineCount() - 1 respectively, - but for linked sub-views, +
      doc.firstLine(): integer
      +
      Get the first line of the editor. This will + usually be zero but for linked sub-views, or documents instantiated with a non-zero first line, it might return other values.
      +
      doc.lastLine(): integer
      +
      Get the last line of the editor. This will + usually be doc.lineCount() - 1, + but for linked sub-views, + it might return other values.
      -
      doc.getLineHandle(num) → lineHandle
      +
      doc.getLineHandle(num: integer): LineHandle
      Fetches the line handle for the given line number.
      -
      doc.getLineNumber(handle) → integer
      +
      doc.getLineNumber(handle: LineHandle): integer
      Given a line handle, returns the current position of that line (or null when it is no longer in the document).
      -
      doc.eachLine(f) | doc.eachLine(start, end, f)
      +
      + doc.eachLine(f: (line:LineHandle) → void): void
      + doc.eachLine(start: integer, end: integer, f: (line:LineHandle) → void): void +
      Iterate over the whole document, or if start and end line numbers are given, the range from start up to (not including) end, @@ -757,12 +770,12 @@

      Content manipulation methods

      a text property containing the line's content (as a string).
      -
      doc.markClean()
      +
      doc.markClean(): void
      Set the editor content as 'clean', a flag that it will retain until it is edited, and which will be set again when such an edit is undone again. Useful to track whether the content needs to be saved.
      -
      doc.isClean() → boolean
      +
      doc.isClean(): boolean
      Returns whether the document is currently clean (not modified since initialization or the last call to markClean).
      @@ -771,9 +784,9 @@

      Content manipulation methods

      Cursor and selection methods

      -
      doc.getSelection() → string
      +
      doc.getSelection(): string
      Get the currently selected code.
      -
      doc.replaceSelection(string, collapse)
      +
      doc.replaceSelection(replacement: string, ?collapse: string): void
      Replace the selection with the given string. By default, the new selection will span the inserted text. The optional collapse argument can be used to change @@ -781,7 +794,7 @@

      Cursor and selection methods

      collapse the selection to the start or end of the inserted text.
      -
      doc.getCursor(start) → object
      +
      doc.getCursor(?start: string): {line, ch}
      start is a an optional string indicating which end of the selection to return. It may be "start", "end", "head" @@ -790,18 +803,18 @@

      Cursor and selection methods

      selection). Omitting the argument is the same as passing "head". A {line, ch} object will be returned.
      -
      doc.somethingSelected() → boolean
      +
      doc.somethingSelected(): boolean
      Return true if any text is selected.
      -
      doc.setCursor(pos)
      +
      doc.setCursor(pos: {line, ch}): void
      Set the cursor position. You can either pass a single {line, ch} object, or the line and the character as two separate parameters.
      -
      doc.setSelection(anchor, head)
      +
      doc.setSelection(anchor: {line, ch}, head: {line, ch}): void
      Set the selection range. anchor and head should be {line, ch} objects. head defaults to anchor when not given.
      -
      doc.extendSelection(pos, pos2)
      +
      doc.extendSelection(pos: {line, ch}, ?pos2: {line, ch}): void
      Similar to setSelection, but will, if shift is held or @@ -811,16 +824,16 @@

      Cursor and selection methods

      ensure a region (for example a word or paragraph) will end up selected (in addition to whatever lies between that region and the current anchor).
      -
      doc.setExtending(bool)
      +
      doc.setExtending(value: boolean): void
      Sets or clears the 'extending' flag, which acts similar to the shift key, in that it will cause cursor movement and calls to extendSelection to leave the selection anchor in place.
      -
      cm.hasFocus() → bool
      +
      cm.hasFocus(): boolean
      Tells you whether the editor currently has focus.
      -
      cm.findPosH(start, amount, unit, visually) → object
      +
      cm.findPosH(start: {line, ch}, amount: integer, unit: string, visually: boolean): {line, ch, ?hitSide: boolean}
      Used to find the target position for horizontal cursor motion. start is a {line, ch} object, amount an integer (may be negative), @@ -833,7 +846,7 @@

      Cursor and selection methods

      the motion was clipped by hitting the end or start of the document, the returned value will have a hitSide property set to true.
      -
      cm.findPosV(start, amount, unit) → object
      +
      cm.findPosV(start: {line, ch}, amount: integer, unit: string): {line, ch, ?hitSide: boolean}
      Similar to findPosH, but used for vertical motion. unit may be "line" or "page". The other @@ -844,16 +857,16 @@

      Cursor and selection methods

      Configuration methods

      -
      cm.setOption(option, value)
      +
      cm.setOption(option: string, value: any): void
      Change the configuration of the editor. option should the name of an option, and value should be a valid value for that option.
      -
      cm.getOption(option) → value
      +
      cm.getOption(option: string) → any
      Retrieves the current value of the given option for this editor instance.
      -
      cm.addKeyMap(map, bottom)
      +
      cm.addKeyMap(map: object, bottom: boolean): void
      Attach an additional keymap to the editor. This is mostly useful for add-ons that need to register some key handlers without trampling on @@ -865,14 +878,14 @@

      Configuration methods

      than those added later, unless the bottom argument was passed, in which case they end up below other keymaps added with this method.
      -
      cm.removeKeyMap(map)
      +
      cm.removeKeyMap(map: object): void
      Disable a keymap added with addKeyMap. Either pass in the keymap object itself, or a string, which will be compared against the name property of the active keymaps.
      -
      cm.addOverlay(mode, options)
      +
      cm.addOverlay(mode: string|object, ?options: object): void
      Enable a highlighting overlay. This is a stateless mini-mode that can be used to add extra highlighting. For example, the search add-on uses it to @@ -886,18 +899,18 @@

      Configuration methods

      allow the overlay styling, when not null, to override the styling of the base mode entirely, instead of the two being applied together.
      -
      cm.removeOverlay(mode)
      +
      cm.removeOverlay(mode: string|object): void
      Pass this the exact argument passed for the mode parameter to addOverlay to remove an overlay again.
      -
      cm.on(type, func)
      +
      cm.on(type: string, func: (...args) → void): void
      Register an event handler for the given event type (a string) on the editor instance. There is also a CodeMirror.on(object, type, func) version that allows registering of events on any object.
      -
      cm.off(type, func)
      +
      cm.off(type: string, func: (...args) → void): void
      Remove an event handler on the editor instance. An equivalent CodeMirror.off(object, type, func) also exists.
      @@ -916,50 +929,53 @@

      Document management methods

      it start at a line number other than 0, respectively.

      -
      cm.getDoc() → doc
      +
      cm.getDoc(): CodeMirror.Doc
      Retrieve the currently active document from an editor.
      -
      doc.getEditor() → editor
      +
      doc.getEditor(): CodeMirror
      Retrieve the editor associated with a document. May return null.
      -
      cm.swapDoc(doc) → doc
      +
      cm.swapDoc(doc: CodeMirror.Doc): CodeMirror.Doc
      Attach a new document to the editor. Returns the old document, which is now no longer associated with an editor.
      -
      doc.copy(copyHistory) → doc
      +
      doc.copy(copyHistory: boolean): CodeMirror.Doc
      Create an identical copy of the given doc. When copyHistory is true, the history will also be copied. Can not be called directly on an editor.
      -
      doc.linkedDoc(options) → doc
      +
      doc.linkedDoc(options: object): CodeMirror.Doc
      Create a new document that's linked to the target document. Linked documents will stay in sync (changes to one are also applied to the other) until unlinked. These are the options that are supported:
      -
      sharedHist (boolean)
      +
      sharedHist: boolean
      When turned on, the linked copy will share an undo history with the original. Thus, something done in one of the two can be undone in the other, and vice versa.
      -
      from, to (integer)
      +
      + from: integer
      + to: integer +
      Can be given to make the new document a subview of the original. Subviews only show a given range of lines. Note that line coordinates inside the subview will be consistent with those of the parent, so that for example a subview starting at line 10 will refer to its first line as line 10, not 0.
      -
      mode (mode spec)
      +
      mode: string|object
      By default, the new document inherits the mode of the parent. This option can be set to a mode spec to give it a different mode.
      -
      doc.unlinkDoc(doc)
      +
      doc.unlinkDoc(doc: CodeMirror.Doc): void
      Break the link between two documents. After calling this, changes will no longer propagate between the documents, and, if they had a shared history, the history will become separate.
      -
      doc.iterLinkedDocs(function)
      +
      doc.iterLinkedDocs(function: (doc: CodeMirror.Doc, sharedHist: boolean) → void): void
      Will call the given function for all documents linked to the target document. It will be passed two arguments, the linked document and a boolean indicating whether that document shares history @@ -969,20 +985,20 @@

      Document management methods

      History-related methods

      -
      doc.undo()
      +
      doc.undo(): void
      Undo one edit (if any undo events are stored).
      -
      doc.redo()
      +
      doc.redo(): void
      Redo one undone edit.
      -
      doc.historySize() → object
      +
      doc.historySize(): { undo: integer, redo: integer }
      Returns an object with {undo, redo} properties, both of which hold integers, indicating the amount of stored undo and redo operations.
      -
      doc.clearHistory()
      +
      doc.clearHistory(): void
      Clears the editor's undo history.
      -
      doc.getHistory() → object
      +
      doc.getHistory(): object
      Get a (JSON-serializeable) representation of the undo history.
      -
      doc.setHistory(object)
      +
      doc.setHistory(history: object): void
      Replace the editor's undo history with the one provided, which must be a value as returned by getHistory. Note that @@ -994,31 +1010,33 @@

      History-related methods

      Text-marking methods

      -
      doc.markText(from, to, options) → object
      +
      doc.markText(from: {line, ch}, to: {line, ch}, ?options: object): CodeMirror.TextMarker
      Can be used to mark a range of text with a specific CSS class name. from and to should be {line, ch} objects. The options parameter is optional. When given, it should be an object that may contain the following configuration options:
      -
      className (string)
      +
      className: string
      Assigns a CSS class to the marked stretch of text.
      -
      inclusiveLeft (boolean)
      Determines whether +
      inclusiveLeft: boolean
      +
      Determines whether text inserted on the left of the marker will end up inside or outside of it.
      -
      inclusiveRight (boolean)
      Like inclusiveLeft, +
      inclusiveRight: boolean
      +
      Like inclusiveLeft, but for the right side.
      -
      atomic (boolean)
      +
      atomic: boolean
      Atomic ranges act as a single unit when cursor movement is concerned—i.e. it is impossible to place the cursor inside of them. In atomic ranges, inclusiveLeft and inclusiveRight have a different meaning—they will prevent the cursor from being placed respectively directly before and directly after the range.
      -
      collapsed (boolean)
      +
      collapsed: boolean
      Collapsed ranges do not show up in the display. Setting a range to be collapsed will automatically make it atomic.
      -
      clearOnEnter (boolean)
      +
      clearOnEnter: boolean
      When enabled, will cause the mark to clear itself whenever the cursor enters its range. This is mostly useful for text-replacement widgets that need to 'snap open' when the @@ -1026,11 +1044,11 @@

      Text-marking methods

      the "clear" event fired on the range handle can be used to be notified when this happens.
      -
      replacedWith (dom node)
      +
      replacedWith: Element
      Use a given node to display this range. Implies both collapsed and atomic. The given DOM node must be an inline element (as opposed to a block element).
      -
      readOnly
      +
      readOnly: boolean|string
      A read-only span can, as long as it is not cleared, not be modified except by calling setValue to reset @@ -1039,12 +1057,12 @@

      Text-marking methods

      existing undo events being partially nullified by read-only spans would corrupt the history (in the current implementation).
      -
      startStyle
      Can be used to specify +
      startStyle: string
      Can be used to specify an extra CSS class to be applied to the leftmost span that is part of the marker.
      -
      endStyle
      Equivalent +
      endStyle: string
      Equivalent to startStyle, but for the rightmost span.
      -
      shared
      When the +
      shared: boolean
      When the target document is linked to other documents, you can set shared to true to make the marker appear in all documents. By default, a marker appears @@ -1065,7 +1083,7 @@

      Text-marking methods

      the replacedWith option, if any.
      -
      doc.setBookmark(pos, options) → object
      +
      doc.setBookmark(pos: {line, ch}, ?options: object): CodeMirror.TextMarker
      Inserts a bookmark, a handle that follows the text around it as it is being edited, at the given position. A bookmark has two methods find() and clear(). The first @@ -1074,27 +1092,27 @@

      Text-marking methods

      The options argument is optional. If given, the following properties are recognized:
      -
      widget
      Can be used to display a DOM +
      widget: Element
      Can be used to display a DOM node at the current location of the bookmark (analogous to the replacedWith option to markText).
      -
      insertLeft
      By default, text typed +
      insertLeft: boolean
      By default, text typed when the cursor is on top of the bookmark will end up to the right of the bookmark. Set this option to true to make it go to the left instead.
      -
      doc.findMarksAt(pos) → array
      +
      doc.findMarksAt(pos: {line, ch}): array<CodeMirror.TextMarker>
      Returns an array of all the bookmarks and marked ranges present at the given position.
      -
      doc.getAllMarks() → array
      +
      doc.getAllMarks(): array<CodeMirror.TextMarker>
      Returns an array containing all marked ranges in the document.

      Widget, gutter, and decoration methods

      -
      cm.setGutterMarker(line, gutterID, value) → lineHandle
      +
      cm.setGutterMarker(line: integer|LineHandle, gutterID: string, value: Element): LineHandle
      Sets the gutter marker for the given gutter (identified by its CSS class, see the gutters option) @@ -1103,11 +1121,11 @@

      Widget, gutter, and decoration methods

      will be shown in the specified gutter next to the specified line.
      -
      cm.clearGutter(gutterID)
      +
      cm.clearGutter(gutterID: string): void
      Remove all gutter markers in the gutter with the given ID.
      -
      cm.addLineClass(line, where, class) → lineHandle
      +
      cm.addLineClass(line: integer|LineHandle, where: string, class: string): LineHandle
      Set a CSS class name for the given line. line can be a number or a line handle. where determines to which element this class should be applied, can can be one @@ -1118,7 +1136,7 @@

      Widget, gutter, and decoration methods

      gutter elements). class should be the name of the class to apply.
      -
      cm.removeLineClass(line, where, class) → lineHandle
      +
      cm.removeLineClass(line: integer|LineHandle, where: string, class: string): LineHandle
      Remove a CSS class from a line. line can be a line handle or number. where should be one of "text", "background", @@ -1127,7 +1145,7 @@

      Widget, gutter, and decoration methods

      can be left off to remove all classes for the specified node, or be a string to remove only a specific class.
      -
      cm.lineInfo(line) → object
      +
      cm.lineInfo(line: integer|LineHandle): {line: integer, handle: LineHandle, text: string, gutterMarkers: object<Element>, textClass: string, bgClass: string, wrapClass: string, widgets: array<CodeMirror.LineWidget>}
      Returns the line number, text content, and marker status of the given line, which can be either a number or a line handle. The returned object has the structure {line, handle, text, @@ -1138,7 +1156,7 @@

      Widget, gutter, and decoration methods

      line, and the various class properties refer to classes added with addLineClass.
      -
      cm.addWidget(pos, node, scrollIntoView)
      +
      cm.addWidget(pos: {line, ch}, node: Element, scrollIntoView: boolean): void
      Puts node, which should be an absolutely positioned DOM node, into the editor, positioned right below the given {line, ch} position. @@ -1147,7 +1165,7 @@

      Widget, gutter, and decoration methods

      widget again, simply use DOM methods (move it somewhere else, or call removeChild on its parent).
      -
      cm.addLineWidget(line, node, options) → object
      +
      cm.addLineWidget(line: integer|LineHandle, node: Element, ?options: object): CodeMirror.LineWidget
      Adds a line widget, an element shown below a line, spanning the whole of the editor's width, and moving the lines below it downwards. line should be either an integer or a @@ -1157,15 +1175,15 @@

      Widget, gutter, and decoration methods

      the widget. The following options are supported (all default to false):
      -
      coverGutter (boolean)
      +
      coverGutter: boolean
      Whether the widget should cover the gutter.
      -
      noHScroll (boolean)
      +
      noHScroll: boolean
      Whether the widget should stay fixed in the face of horizontal scrolling.
      -
      above (boolean)
      +
      above: boolean
      Causes the widget to be placed above instead of below the text of the line.
      -
      showIfHidden (boolean)
      +
      showIfHidden: boolean
      When true, will cause the widget to be rendered even if the line it is associated with is hidden.
      @@ -1175,8 +1193,8 @@

      Widget, gutter, and decoration methods

      the widget placement. It'll have a line property pointing at the line handle that it is associated with, and the following methods:
      -
      clear()
      Removes the widget.
      -
      changed()
      Call +
      clear(): void
      Removes the widget.
      +
      changed(): void
      Call this if you made some change to the widget's DOM node that might affect its height. It'll force CodeMirror to update the height of the line that contains the widget.
      @@ -1187,7 +1205,7 @@

      Widget, gutter, and decoration methods

      Sizing, scrolling and positioning methods

      -
      cm.setSize(width, height)
      +
      cm.setSize(width: number|string, height: number|string): void
      Programatically set the size of the editor (overriding the applicable CSS rules). width and height height @@ -1196,16 +1214,16 @@

      Sizing, scrolling and positioning methods

      pass null for either of them to indicate that that dimension should not be changed.
      -
      cm.scrollTo(x, y)
      +
      cm.scrollTo(x: number, y: number): void
      Scroll the editor to a given (pixel) position. Both arguments may be left as null or undefined to have no effect.
      -
      cm.getScrollInfo()
      +
      cm.getScrollInfo(): {left: number, top: number, width: number, height: number, clientWidth: number, clientHeight: number}
      Get an {left, top, width, height, clientWidth, clientHeight} object that represents the current scroll position, the size of the scrollable area, and the size of the visible area (minus scrollbars).
      -
      cm.scrollIntoView(pos, margin)
      +
      cm.scrollIntoView(pos: {line, ch}|{left: number, top: number, right: number, bottom: number}, ?margin: number): void
      Scrolls the given element into view. pos may be either a {line, ch} position, referring to a given character, null, to refer to the cursor, or @@ -1214,7 +1232,7 @@

      Sizing, scrolling and positioning methods

      optional. When given, it indicates the amount of pixels around the given area that should be made visible as well.
      -
      cm.cursorCoords(where, mode) → object
      +
      cm.cursorCoords(where: boolean|{line, ch}, mode: string): {left: number, top: number, bottom: number}
      Returns an {left, top, bottom} object containing the coordinates of the cursor position. If mode is "local", they will be @@ -1225,29 +1243,29 @@

      Sizing, scrolling and positioning methods

      end (false) of the selection, or, if a {line, ch} object is given, it specifies the precise position at which you want to measure.
      -
      cm.charCoords(pos, mode) → object
      +
      cm.charCoords(pos: {line, ch}, mode: string): {left: number, top: number, bottom: number}
      Returns the position and dimensions of an arbitrary character. pos should be a {line, ch} object. This differs from cursorCoords in that it'll give the size of the whole character, rather than just the position that the cursor would have when it would sit at that position.
      -
      cm.coordsChar(object, mode) → pos
      +
      cm.coordsChar(object: {left: number, top: number}, ?mode: string): {line, ch}
      Given an {left, top} object, returns the {line, ch} position that corresponds to it. The optional mode parameter determines relative to what the coordinates are interpreted. It may be "window", "page" (the default), or "local".
      -
      cm.defaultTextHeight() → number
      +
      cm.defaultTextHeight(): number
      Returns the line height of the default font for the editor.
      -
      cm.defaultCharWidth() → number
      +
      cm.defaultCharWidth(): number
      Returns the pixel width of an 'x' in the default font for the editor. (Note that for non-monospace fonts, this is mostly useless, and even for monospace fonts, non-ascii characters might have a different width).
      -
      cm.getViewport() → object
      +
      cm.getViewport(): {from: number, to: number}
      Returns a {from, to} object indicating the start (inclusive) and end (exclusive) of the currently rendered part of the document. In big documents, when most content is @@ -1256,7 +1274,7 @@

      Sizing, scrolling and positioning methods

      the viewportChange event.
      -
      cm.refresh()
      +
      cm.refresh(): void
      If your code does something to change the size of the editor element (window resizes are already listened for), or unhides it, you should probably follow up by calling this method to @@ -1271,13 +1289,13 @@

      Mode, state, and token-related methods

      more detailed description of how these work.

      -
      doc.getMode() → object
      +
      doc.getMode(): object
      Gets the mode object for the editor. Note that this is distinct from getOption("mode"), which gives you the mode specification, rather than the resolved, instantiated mode object.
      -
      cm.getTokenAt(pos) → object
      +
      cm.getTokenAt(pos: {line, ch}): { start: string, end: string, string: string, type: string, state: string }
      Retrieves information about the token the current mode found before the given position (a {line, ch} object). The returned object has the following properties: @@ -1291,7 +1309,7 @@

      Mode, state, and token-related methods

      state
      The mode's state at the end of this token.
      -
      cm.getStateAfter(line) → state
      +
      cm.getStateAfter(?line: integer): object
      Returns the mode's parser state, if any, at the end of the given line number. If no line number is given, the state at the end of the document is returned. This can be useful for storing @@ -1302,7 +1320,7 @@

      Mode, state, and token-related methods

      Miscellaneous methods

      -
      cm.operation(func) → result
      +
      cm.operation(func: () → any): any
      CodeMirror internally buffers changes and only updates its DOM structure after it has finished performing some operation. If you need to perform a lot of operations on a CodeMirror @@ -1312,7 +1330,7 @@

      Miscellaneous methods

      lot faster. The return value from this method will be the return value of your function.
      -
      cm.indentLine(line, dir)
      +
      cm.indentLine(line: integer|LineHandle, ?dir: string): void
      Adjust the indentation of the given line. The second argument (which defaults to "smart") may be one of:
      @@ -1328,28 +1346,28 @@

      Miscellaneous methods

      Reduce the indentation of the line.
      -
      doc.posFromIndex(index) → object
      +
      doc.posFromIndex(index: integer): {line, ch}
      Calculates and returns a {line, ch} object for a zero-based index who's value is relative to the start of the editor's text. If the index is out of range of the text then the returned object is clipped to start or end of the text respectively.
      -
      doc.indexFromPos(object) → number
      +
      doc.indexFromPos(object: {line, ch}): integer
      The reverse of posFromIndex.
      -
      cm.focus()
      +
      cm.focus(): void
      Give the editor focus.
      -
      cm.getInputField() → textarea
      +
      cm.getInputField(): TextAreaElement
      Returns the hidden textarea used to read input.
      -
      cm.getWrapperElement() → node
      +
      cm.getWrapperElement(): Element
      Returns the DOM node that represents the editor, and controls its size. Remove this from your tree to delete an editor instance.
      -
      cm.getScrollerElement() → node
      +
      cm.getScrollerElement(): Element
      Returns the DOM node that is responsible for the scrolling of the editor.
      -
      cm.getGutterElement() → node
      +
      cm.getGutterElement(): Element
      Fetches the DOM node that contains the editor gutters.
      @@ -1432,18 +1450,24 @@

      Add-ons

      relevant when matching a string. It will cause the search to be case-insensitive. A search cursor has the following methods:
      -
      findNext(), findPrevious() → boolean
      +
      + findNext(): boolean
      + findPrevious(): boolean +
      Search forward or backward from the current position. The return value indicates whether a match was found. If matching a regular expression, the return value will be the array returned by the match method, in case you want to extract matched groups.
      -
      from(), to() → object
      +
      + from(): {line, ch}
      + to(): {line, ch} +
      These are only valid when the last call to findNext or findPrevious did not return false. They will return {line, ch} objects pointing at the start and end of the match.
      -
      replace(text)
      +
      replace(text: string): void
      Replaces the currently found match with the given text and adjusts the cursor position to reflect the replacement.
      @@ -1651,42 +1675,45 @@

      Writing CodeMirror Modes

      the following API:

      -
      eol() → boolean
      +
      eol(): boolean
      Returns true only if the stream is at the end of the line.
      -
      sol() → boolean
      +
      sol(): boolean
      Returns true only if the stream is at the start of the line.
      -
      peek() → character
      +
      peek(): string
      Returns the next character in the stream without advancing it. Will return an null at the end of the line.
      -
      next() → character
      +
      next(): string
      Returns the next character in the stream and advances it. Also returns null when no more characters are available.
      -
      eat(match) → character
      +
      eat(match: string|regexp|(char:string) → boolean): string
      match can be a character, a regular expression, or a function that takes a character and returns a boolean. If the next character in the stream 'matches' the given argument, it is consumed and returned. Otherwise, undefined is returned.
      -
      eatWhile(match) → boolean
      +
      eatWhile(match: string|regexp|(char:string) → boolean): boolean
      Repeatedly calls eat with the given argument, until it fails. Returns true if any characters were eaten.
      -
      eatSpace() → boolean
      +
      eatSpace(): boolean
      Shortcut for eatWhile when matching white-space.
      -
      skipToEnd()
      +
      skipToEnd(): void
      Moves the position to the end of the line.
      -
      skipTo(ch) → boolean
      +
      skipTo(ch: string): boolean
      Skips to the next occurrence of the given character, if found on the current line (doesn't advance the stream if the character does not occur on the line). Returns true if the character was found.
      -
      match(pattern, consume, caseFold) → boolean
      +
      + match(pattern: string, ?consume: boolean, ?caseFold: boolean): boolean
      + match(pattern: regexp, ?consume: boolean): array<string> +
      Act like a multi-character eat—if consume is true or not given—or a look-ahead that doesn't update the stream @@ -1698,18 +1725,18 @@

      Writing CodeMirror Modes

      returned by match, in case you need to extract matched groups.
      -
      backUp(n)
      +
      backUp(n: integer): void
      Backs up the stream n characters. Backing it up further than the start of the current token will cause things to break, so be careful.
      -
      column() → integer
      +
      column(): integer
      Returns the column (taking into account tabs) at which the current token starts.
      -
      indentation() → integer
      +
      indentation(): integer
      Tells you how far the current line has been indented, in spaces. Corrects for tab characters.
      -
      current() → string
      +
      current(): string
      Get the string between the start of the current token and the current stream position.
      From 7f17d0857889003b0c6f885cb246b88e848fbaf4 Mon Sep 17 00:00:00 2001 From: Andy Li Date: Thu, 4 Apr 2013 04:20:00 +0800 Subject: [PATCH 0967/5780] Manual: Constructor method is added to miscellaneous, static members are put inside a
      . --- doc/manual.html | 104 ++++++++++++++++++++++++++---------------------- 1 file changed, 57 insertions(+), 47 deletions(-) diff --git a/doc/manual.html b/doc/manual.html index fbf30573d2..fc0eab5e33 100644 --- a/doc/manual.html +++ b/doc/manual.html @@ -431,7 +431,7 @@

      Events

      "focus" (instance: CodeMirror)
      Fires whenever the editor is focused.
      -
      "blur" (instance: CodeMirror)
      +
      "blur" (instance: CodeMirror)
      Fires whenever the editor is unfocused.
      "scroll" (instance: CodeMirror)
      @@ -1320,6 +1320,9 @@

      Mode, state, and token-related methods

      Miscellaneous methods

      +
      CodeMirror(elt:Element|(elt:Element) → void, ?config: object): void
      +
      The constructor. See Basic Usage.
      +
      cm.operation(func: () → any): any
      CodeMirror internally buffers changes and only updates its DOM structure after it has finished performing some operation. @@ -1372,57 +1375,64 @@

      Miscellaneous methods

      Static properties

      - -

      The CodeMirror object itself provides - several useful properties. Firstly, its version - property contains a string that indicates the version of the - library. For releases, this simply - contains "major.minor" (for - example "2.33". For beta versions, " B" - (space, capital B) is added at the end of the string, for - development snapshots, " +" (space, plus) is - added.

      - -

      The CodeMirror.fromTextArea - method provides another way to initialize an editor. It takes a - textarea DOM node as first argument and an optional configuration - object as second. It will replace the textarea with a CodeMirror - instance, and wire up the form of that textarea (if any) to make - sure the editor contents are put into the textarea when the form - is submitted. A CodeMirror instance created this way has three - additional methods:

      +

      The CodeMirror object itself provides + several useful properties.

      -
      cm.save()
      -
      Copy the content of the editor into the textarea.
      +
      CodeMirror.version: string
      +
      It contains a string that indicates the version of the + library. For releases, this simply + contains "major.minor" (for + example "2.33". For beta versions, " B" + (space, capital B) is added at the end of the string, for + development snapshots, " +" (space, plus) is + added.
      + +
      CodeMirror.fromTextArea(textArea: TextAreaElement, ?config: object)
      +
      + The method provides another way to initialize an editor. It takes a + textarea DOM node as first argument and an optional configuration + object as second. It will replace the textarea with a CodeMirror + instance, and wire up the form of that textarea (if any) to make + sure the editor contents are put into the textarea when the form + is submitted. A CodeMirror instance created this way has three + additional methods: +
      +
      cm.save(): void
      +
      Copy the content of the editor into the textarea.
      -
      cm.toTextArea()
      -
      Remove the editor, and restore the original textarea (with - the editor's current content).
      +
      cm.toTextArea(): void
      +
      Remove the editor, and restore the original textarea (with + the editor's current content).
      -
      cm.getTextArea() → textarea
      -
      Returns the textarea that the instance was based on.
      -
      +
      cm.getTextArea(): TextAreaElement
      +
      Returns the textarea that the instance was based on.
      +
      +
      -

      If you want to define extra methods in terms - of the CodeMirror API, it is possible to - use CodeMirror.defineExtension(name, value). This - will cause the given value (usually a method) to be added to all - CodeMirror instances created from then on.

      - -

      Similarly, CodeMirror.defineOption(name, - default, updateFunc) can be used to define new options for - CodeMirror. The updateFunc will be called with the - editor instance and the new value when an editor is initialized, - and whenever the option is modified - through setOption.

      - -

      If your extention just needs to run some - code whenever a CodeMirror instance is initialized, - use CodeMirror.defineInitHook. Give it a function as - its only argument, and from then on, that function will be called - (with the instance as argument) whenever a new CodeMirror instance - is initialized.

      +
      CodeMirror.defineExtension(name: string, value: any): void
      +
      If you want to define extra methods in terms + of the CodeMirror API, it is possible to + use defineExtension. This + will cause the given value (usually a method) to be added to all + CodeMirror instances created from then on.
      + +
      CodeMirror.defineOption(name: string, + default: any, updateFunc: (instance: CodeMirror, value: any, setOption: boolean) → void): void
      +
      Similarly, defineOption can be used to define new options for + CodeMirror. The updateFunc will be called with the + editor instance and the new value when an editor is initialized, + and whenever the option is modified + through setOption.
      + +
      CodeMirror.defineInitHook(func: (instance: CodeMirror) → void): void
      +
      If your extention just needs to run some + code whenever a CodeMirror instance is initialized, + use CodeMirror.defineInitHook. Give it a function as + its only argument, and from then on, that function will be called + (with the instance as argument) whenever a new CodeMirror instance + is initialized.
      +

      Add-ons

      From 7683ff625be45ebea5a020da61ce1e019b6c5717 Mon Sep 17 00:00:00 2001 From: Marijn Haverbeke Date: Thu, 4 Apr 2013 11:20:05 +0200 Subject: [PATCH 0968/5780] Go over manual changes, try to preserve readability --- doc/manual.html | 247 ++++++++++++++++++++++++------------------------ 1 file changed, 123 insertions(+), 124 deletions(-) diff --git a/doc/manual.html b/doc/manual.html index fc0eab5e33..d805185f15 100644 --- a/doc/manual.html +++ b/doc/manual.html @@ -206,7 +206,7 @@

      Configuration

      firstLineNumber: integer
      At which number to start counting lines. Default is 1.
      -
      lineNumberFormatter: (line: integer) → string
      +
      lineNumberFormatter: function(line: integer) → string
      A function used to format line numbers. The function is passed the line number, and should return a string that will be shown in the gutter.
      @@ -265,7 +265,7 @@

      Configuration

      dragDrop: boolean
      Controls whether drag-and-drop is enabled. On by default.
      -
      onDragEvent: (instance: CodeMirror, event: Event) → boolean
      +
      onDragEvent: function(instance: CodeMirror, event: Event) → boolean
      When given, this will be called when the editor is handling a dragenter, dragover, or drop event. It will be passed the editor instance @@ -274,7 +274,7 @@

      Configuration

      return true to indicate that CodeMirror should not do anything further.
      -
      onKeyEvent: (instance: CodeMirror, event: Event) → boolean
      +
      onKeyEvent: function(instance: CodeMirror, event: Event) → boolean
      This provides a rather low-level hook into CodeMirror's key handling. If provided, this function will be called on every keydown, keyup, @@ -304,7 +304,7 @@

      Configuration

      which causes the cursor to not reach all the way to the bottom of the line, looks better
      -
      workTime: number
      +
      workTime, workDelay: number
      Highlighting is done by a pseudo background-thread that will work for workTime milliseconds, and then use timeout to sleep for workDelay milliseconds. The @@ -360,8 +360,7 @@

      Events

      refers to the editor instance.

      -
      "change" (instance: CodeMirror, changeObj: { from: {line, ch}, to: {line, ch}, text: array<string>, removed: string, - ?next:changeObj })
      +
      "change" (instance: CodeMirror, changeObj: object)
      Fires every time the content of the editor is changed. The changeObj is a {from, to, text, removed, next} object containing information about the changes @@ -378,10 +377,10 @@

      Events

      a next property pointing to another change object (which may point to another, etc).
      -
      "beforeChange" (instance: CodeMirror, change: { from: {line, ch}, to: {line, ch}, text: array<string>, removed: string, update: (?from: {line, ch}, ?to: {line, ch}, ?text: array<string>) → void, cancel: () → void })
      +
      "beforeChange" (instance: CodeMirror, changeObj: object)
      This event is fired before a change is applied, and its handler may choose to modify or cancel the change. - The change object + The changeObj object has from, to, and text properties, as with the "change" event, but @@ -402,7 +401,7 @@

      Events

      Will be fired when the cursor or selection moves, or any change is made to the editor content.
      -
      "beforeSelectionChange" (instance: CodeMirror, selection: { head: {line, ch}, anchor: {line, ch} })
      +
      "beforeSelectionChange" (instance: CodeMirror, selection: {head, anchor})
      This event is fired before the selection is moved. Its handler may modify the resulting selection head and anchor. The selection parameter is an object @@ -456,7 +455,7 @@

      Events

      following events:

      -
      "change" (doc: CodeMirror.Doc, changeObj: { from: {line, ch}, to: {line, ch}, text: array<string>, removed: string })
      +
      "change" (doc: CodeMirror.Doc, changeObj: object)
      Fired whenever a change occurs to the document. changeObj has a similar type as the object passed to the @@ -465,7 +464,7 @@

      Events

      document change events are not batched (whereas editor change events are).
      -
      "beforeChange" (doc: CodeMirror.Doc, change: { from: {line, ch}, to: {line, ch}, text: array<string>, removed: string })
      +
      "beforeChange" (doc: CodeMirror.Doc, change: object)
      See the description of the same event on editor instances.
      @@ -473,7 +472,7 @@

      Events

      Fired whenever the cursor or selection in this document changes.
      -
      "beforeSelectionChange" (doc: CodeMirror.Doc, selection: { head: {line, ch}, anchor: {line, ch} })
      +
      "beforeSelectionChange" (doc: CodeMirror.Doc, selection: {head, anchor})
      Equivalent to the event by the same name as fired on editor instances.
      @@ -489,7 +488,7 @@

      Events

      is associated with the start of the line. Mostly useful when you need to find out when your gutter markers on a given line are removed.
      -
      "change" (line: LineHandle, changeObj: { from: {line, ch}, to: {line, ch}, text: array<string>, removed: string })
      +
      "change" (line: LineHandle, changeObj: object)
      Fires when the line's text content is changed in any way (but the line is not deleted outright). The change object is similar to the one passed @@ -711,54 +710,54 @@

      Programming API

      Content manipulation methods

      -
      doc.getValue(?seperator: string): string
      +
      doc.getValue(?seperator: string) → string
      Get the current editor content. You can pass it an optional argument to specify the string to be used to separate lines (defaults to "\n").
      -
      doc.setValue(content: string): void
      +
      doc.setValue(content: string)
      Set the editor content.
      -
      doc.getRange(from: {line, ch}, to: {line, ch}, ?seperator: string): string
      +
      doc.getRange(from: {line, ch}, to: {line, ch}, ?seperator: string) → string
      Get the text between the given points in the editor, which should be {line, ch} objects. An optional third argument can be given to indicate the line separator string to use (defaults to "\n").
      -
      doc.replaceRange(replacement: string, from: {line, ch}, to: {line, ch}): void
      +
      doc.replaceRange(replacement: string, from: {line, ch}, to: {line, ch})
      Replace the part of the document between from and to with the given string. from and to must be {line, ch} objects. to can be left off to simply insert the string at position from.
      -
      doc.getLine(n: integer): string
      +
      doc.getLine(n: integer) → string
      Get the content of line n.
      -
      doc.setLine(n: integer, text: string): void
      +
      doc.setLine(n: integer, text: string)
      Set the content of line n.
      -
      doc.removeLine(n: integer): void
      +
      doc.removeLine(n: integer)
      Remove the given line from the document.
      -
      doc.lineCount(): integer
      +
      doc.lineCount() → integer
      Get the number of lines in the editor.
      -
      doc.firstLine(): integer
      +
      doc.firstLine() → integer
      Get the first line of the editor. This will usually be zero but for linked sub-views, or documents instantiated with a non-zero first line, it might return other values.
      -
      doc.lastLine(): integer
      +
      doc.lastLine() → integer
      Get the last line of the editor. This will usually be doc.lineCount() - 1, but for linked sub-views, it might return other values.
      -
      doc.getLineHandle(num: integer): LineHandle
      +
      doc.getLineHandle(num: integer) → LineHandle
      Fetches the line handle for the given line number.
      -
      doc.getLineNumber(handle: LineHandle): integer
      +
      doc.getLineNumber(handle: LineHandle) → integer
      Given a line handle, returns the current position of that line (or null when it is no longer in the document).
      - doc.eachLine(f: (line:LineHandle) → void): void
      - doc.eachLine(start: integer, end: integer, f: (line:LineHandle) → void): void + doc.eachLine(f: (line: LineHandle))
      + doc.eachLine(start: integer, end: integer, f: (line: LineHandle))
      Iterate over the whole document, or if start and end line numbers are given, the range @@ -770,12 +769,12 @@

      Content manipulation methods

      a text property containing the line's content (as a string).
      -
      doc.markClean(): void
      +
      doc.markClean()
      Set the editor content as 'clean', a flag that it will retain until it is edited, and which will be set again when such an edit is undone again. Useful to track whether the content needs to be saved.
      -
      doc.isClean(): boolean
      +
      doc.isClean() → boolean
      Returns whether the document is currently clean (not modified since initialization or the last call to markClean).
      @@ -784,9 +783,9 @@

      Content manipulation methods

      Cursor and selection methods

      -
      doc.getSelection(): string
      +
      doc.getSelection() → string
      Get the currently selected code.
      -
      doc.replaceSelection(replacement: string, ?collapse: string): void
      +
      doc.replaceSelection(replacement: string, ?collapse: string)
      Replace the selection with the given string. By default, the new selection will span the inserted text. The optional collapse argument can be used to change @@ -794,7 +793,7 @@

      Cursor and selection methods

      collapse the selection to the start or end of the inserted text.
      -
      doc.getCursor(?start: string): {line, ch}
      +
      doc.getCursor(?start: string) → {line, ch}
      start is a an optional string indicating which end of the selection to return. It may be "start", "end", "head" @@ -803,18 +802,18 @@

      Cursor and selection methods

      selection). Omitting the argument is the same as passing "head". A {line, ch} object will be returned.
      -
      doc.somethingSelected(): boolean
      +
      doc.somethingSelected() → boolean
      Return true if any text is selected.
      -
      doc.setCursor(pos: {line, ch}): void
      +
      doc.setCursor(pos: {line, ch})
      Set the cursor position. You can either pass a single {line, ch} object, or the line and the character as two separate parameters.
      -
      doc.setSelection(anchor: {line, ch}, head: {line, ch}): void
      +
      doc.setSelection(anchor: {line, ch}, head: {line, ch})
      Set the selection range. anchor and head should be {line, ch} objects. head defaults to anchor when not given.
      -
      doc.extendSelection(pos: {line, ch}, ?pos2: {line, ch}): void
      +
      doc.extendSelection(from: {line, ch}, ?to: {line, ch})
      Similar to setSelection, but will, if shift is held or @@ -824,16 +823,16 @@

      Cursor and selection methods

      ensure a region (for example a word or paragraph) will end up selected (in addition to whatever lies between that region and the current anchor).
      -
      doc.setExtending(value: boolean): void
      +
      doc.setExtending(value: boolean)
      Sets or clears the 'extending' flag, which acts similar to the shift key, in that it will cause cursor movement and calls to extendSelection to leave the selection anchor in place.
      -
      cm.hasFocus(): boolean
      +
      cm.hasFocus() → boolean
      Tells you whether the editor currently has focus.
      -
      cm.findPosH(start: {line, ch}, amount: integer, unit: string, visually: boolean): {line, ch, ?hitSide: boolean}
      +
      cm.findPosH(start: {line, ch}, amount: integer, unit: string, visually: boolean) → {line, ch, ?hitSide: boolean}
      Used to find the target position for horizontal cursor motion. start is a {line, ch} object, amount an integer (may be negative), @@ -846,7 +845,7 @@

      Cursor and selection methods

      the motion was clipped by hitting the end or start of the document, the returned value will have a hitSide property set to true.
      -
      cm.findPosV(start: {line, ch}, amount: integer, unit: string): {line, ch, ?hitSide: boolean}
      +
      cm.findPosV(start: {line, ch}, amount: integer, unit: string) → {line, ch, ?hitSide: boolean}
      Similar to findPosH, but used for vertical motion. unit may be "line" or "page". The other @@ -857,7 +856,7 @@

      Cursor and selection methods

      Configuration methods

      -
      cm.setOption(option: string, value: any): void
      +
      cm.setOption(option: string, value: any)
      Change the configuration of the editor. option should the name of an option, and value should be a valid value for that @@ -866,7 +865,7 @@

      Configuration methods

      Retrieves the current value of the given option for this editor instance.
      -
      cm.addKeyMap(map: object, bottom: boolean): void
      +
      cm.addKeyMap(map: object, bottom: boolean)
      Attach an additional keymap to the editor. This is mostly useful for add-ons that need to register some key handlers without trampling on @@ -878,14 +877,14 @@

      Configuration methods

      than those added later, unless the bottom argument was passed, in which case they end up below other keymaps added with this method.
      -
      cm.removeKeyMap(map: object): void
      +
      cm.removeKeyMap(map: object)
      Disable a keymap added with addKeyMap. Either pass in the keymap object itself, or a string, which will be compared against the name property of the active keymaps.
      -
      cm.addOverlay(mode: string|object, ?options: object): void
      +
      cm.addOverlay(mode: string|object, ?options: object)
      Enable a highlighting overlay. This is a stateless mini-mode that can be used to add extra highlighting. For example, the search add-on uses it to @@ -899,18 +898,18 @@

      Configuration methods

      allow the overlay styling, when not null, to override the styling of the base mode entirely, instead of the two being applied together.
      -
      cm.removeOverlay(mode: string|object): void
      +
      cm.removeOverlay(mode: string|object)
      Pass this the exact argument passed for the mode parameter to addOverlay to remove an overlay again.
      -
      cm.on(type: string, func: (...args) → void): void
      +
      cm.on(type: string, func: (...args))
      Register an event handler for the given event type (a string) on the editor instance. There is also a CodeMirror.on(object, type, func) version that allows registering of events on any object.
      -
      cm.off(type: string, func: (...args) → void): void
      +
      cm.off(type: string, func: (...args))
      Remove an event handler on the editor instance. An equivalent CodeMirror.off(object, type, func) also exists.
      @@ -929,22 +928,22 @@

      Document management methods

      it start at a line number other than 0, respectively.

      -
      cm.getDoc(): CodeMirror.Doc
      +
      cm.getDoc() → CodeMirror.Doc
      Retrieve the currently active document from an editor.
      -
      doc.getEditor(): CodeMirror
      +
      doc.getEditor() → CodeMirror
      Retrieve the editor associated with a document. May return null.
      -
      cm.swapDoc(doc: CodeMirror.Doc): CodeMirror.Doc
      +
      cm.swapDoc(doc: CodeMirror.Doc) → CodeMirror.Doc
      Attach a new document to the editor. Returns the old document, which is now no longer associated with an editor.
      -
      doc.copy(copyHistory: boolean): CodeMirror.Doc
      +
      doc.copy(copyHistory: boolean) → CodeMirror.Doc
      Create an identical copy of the given doc. When copyHistory is true, the history will also be copied. Can not be called directly on an editor.
      -
      doc.linkedDoc(options: object): CodeMirror.Doc
      +
      doc.linkedDoc(options: object) → CodeMirror.Doc
      Create a new document that's linked to the target document. Linked documents will stay in sync (changes to one are also applied to the other) until unlinked. @@ -970,12 +969,12 @@

      Document management methods

      a mode spec to give it a different mode.
      -
      doc.unlinkDoc(doc: CodeMirror.Doc): void
      +
      doc.unlinkDoc(doc: CodeMirror.Doc)
      Break the link between two documents. After calling this, changes will no longer propagate between the documents, and, if they had a shared history, the history will become separate.
      -
      doc.iterLinkedDocs(function: (doc: CodeMirror.Doc, sharedHist: boolean) → void): void
      +
      doc.iterLinkedDocs(function: (doc: CodeMirror.Doc, sharedHist: boolean))
      Will call the given function for all documents linked to the target document. It will be passed two arguments, the linked document and a boolean indicating whether that document shares history @@ -985,20 +984,20 @@

      Document management methods

      History-related methods

      -
      doc.undo(): void
      +
      doc.undo()
      Undo one edit (if any undo events are stored).
      -
      doc.redo(): void
      +
      doc.redo()
      Redo one undone edit.
      -
      doc.historySize(): { undo: integer, redo: integer }
      +
      doc.historySize() → {undo: integer, redo: integer}
      Returns an object with {undo, redo} properties, both of which hold integers, indicating the amount of stored undo and redo operations.
      -
      doc.clearHistory(): void
      +
      doc.clearHistory()
      Clears the editor's undo history.
      -
      doc.getHistory(): object
      +
      doc.getHistory() → object
      Get a (JSON-serializeable) representation of the undo history.
      -
      doc.setHistory(history: object): void
      +
      doc.setHistory(history: object)
      Replace the editor's undo history with the one provided, which must be a value as returned by getHistory. Note that @@ -1010,7 +1009,7 @@

      History-related methods

      Text-marking methods

      -
      doc.markText(from: {line, ch}, to: {line, ch}, ?options: object): CodeMirror.TextMarker
      +
      doc.markText(from: {line, ch}, to: {line, ch}, ?options: object) → CodeMirror.TextMarker
      Can be used to mark a range of text with a specific CSS class name. from and to should be {line, ch} objects. The options @@ -1083,7 +1082,7 @@

      Text-marking methods

      the replacedWith option, if any.
      -
      doc.setBookmark(pos: {line, ch}, ?options: object): CodeMirror.TextMarker
      +
      doc.setBookmark(pos: {line, ch}, ?options: object) → CodeMirror.TextMarker
      Inserts a bookmark, a handle that follows the text around it as it is being edited, at the given position. A bookmark has two methods find() and clear(). The first @@ -1102,17 +1101,17 @@

      Text-marking methods

      to the left instead.
      -
      doc.findMarksAt(pos: {line, ch}): array<CodeMirror.TextMarker>
      +
      doc.findMarksAt(pos: {line, ch}) → array<CodeMirror.TextMarker>
      Returns an array of all the bookmarks and marked ranges present at the given position.
      -
      doc.getAllMarks(): array<CodeMirror.TextMarker>
      +
      doc.getAllMarks() → array<CodeMirror.TextMarker>
      Returns an array containing all marked ranges in the document.

      Widget, gutter, and decoration methods

      -
      cm.setGutterMarker(line: integer|LineHandle, gutterID: string, value: Element): LineHandle
      +
      cm.setGutterMarker(line: integer|LineHandle, gutterID: string, value: Element) → LineHandle
      Sets the gutter marker for the given gutter (identified by its CSS class, see the gutters option) @@ -1121,11 +1120,11 @@

      Widget, gutter, and decoration methods

      will be shown in the specified gutter next to the specified line.
      -
      cm.clearGutter(gutterID: string): void
      +
      cm.clearGutter(gutterID: string)
      Remove all gutter markers in the gutter with the given ID.
      -
      cm.addLineClass(line: integer|LineHandle, where: string, class: string): LineHandle
      +
      cm.addLineClass(line: integer|LineHandle, where: string, class: string) → LineHandle
      Set a CSS class name for the given line. line can be a number or a line handle. where determines to which element this class should be applied, can can be one @@ -1136,7 +1135,7 @@

      Widget, gutter, and decoration methods

      gutter elements). class should be the name of the class to apply.
      -
      cm.removeLineClass(line: integer|LineHandle, where: string, class: string): LineHandle
      +
      cm.removeLineClass(line: integer|LineHandle, where: string, class: string) → LineHandle
      Remove a CSS class from a line. line can be a line handle or number. where should be one of "text", "background", @@ -1145,7 +1144,7 @@

      Widget, gutter, and decoration methods

      can be left off to remove all classes for the specified node, or be a string to remove only a specific class.
      -
      cm.lineInfo(line: integer|LineHandle): {line: integer, handle: LineHandle, text: string, gutterMarkers: object<Element>, textClass: string, bgClass: string, wrapClass: string, widgets: array<CodeMirror.LineWidget>}
      +
      cm.lineInfo(line: integer|LineHandle) → {line: integer, handle: LineHandle, text: string, gutterMarkers: object<Element>, textClass: string, bgClass: string, wrapClass: string, widgets: array<CodeMirror.LineWidget>}
      Returns the line number, text content, and marker status of the given line, which can be either a number or a line handle. The returned object has the structure {line, handle, text, @@ -1156,7 +1155,7 @@

      Widget, gutter, and decoration methods

      line, and the various class properties refer to classes added with addLineClass.
      -
      cm.addWidget(pos: {line, ch}, node: Element, scrollIntoView: boolean): void
      +
      cm.addWidget(pos: {line, ch}, node: Element, scrollIntoView: boolean)
      Puts node, which should be an absolutely positioned DOM node, into the editor, positioned right below the given {line, ch} position. @@ -1165,7 +1164,7 @@

      Widget, gutter, and decoration methods

      widget again, simply use DOM methods (move it somewhere else, or call removeChild on its parent).
      -
      cm.addLineWidget(line: integer|LineHandle, node: Element, ?options: object): CodeMirror.LineWidget
      +
      cm.addLineWidget(line: integer|LineHandle, node: Element, ?options: object) → CodeMirror.LineWidget
      Adds a line widget, an element shown below a line, spanning the whole of the editor's width, and moving the lines below it downwards. line should be either an integer or a @@ -1173,7 +1172,7 @@

      Widget, gutter, and decoration methods

      will be displayed below the given line. options, when given, should be an object that configures the behavior of the widget. The following options are supported (all default to - false): + false) →
      coverGutter: boolean
      Whether the widget should cover the gutter.
      @@ -1193,8 +1192,8 @@

      Widget, gutter, and decoration methods

      the widget placement. It'll have a line property pointing at the line handle that it is associated with, and the following methods:
      -
      clear(): void
      Removes the widget.
      -
      changed(): void
      Call +
      clear()
      Removes the widget.
      +
      changed()
      Call this if you made some change to the widget's DOM node that might affect its height. It'll force CodeMirror to update the height of the line that contains the widget.
      @@ -1205,7 +1204,7 @@

      Widget, gutter, and decoration methods

      Sizing, scrolling and positioning methods

      -
      cm.setSize(width: number|string, height: number|string): void
      +
      cm.setSize(width: number|string, height: number|string)
      Programatically set the size of the editor (overriding the applicable CSS rules). width and height height @@ -1214,16 +1213,16 @@

      Sizing, scrolling and positioning methods

      pass null for either of them to indicate that that dimension should not be changed.
      -
      cm.scrollTo(x: number, y: number): void
      +
      cm.scrollTo(x: number, y: number)
      Scroll the editor to a given (pixel) position. Both arguments may be left as null or undefined to have no effect.
      -
      cm.getScrollInfo(): {left: number, top: number, width: number, height: number, clientWidth: number, clientHeight: number}
      +
      cm.getScrollInfo() → {left, top, width, height, clientWidth, clientHeight}
      Get an {left, top, width, height, clientWidth, clientHeight} object that represents the current scroll position, the size of the scrollable area, and the size of the visible area (minus scrollbars).
      -
      cm.scrollIntoView(pos: {line, ch}|{left: number, top: number, right: number, bottom: number}, ?margin: number): void
      +
      cm.scrollIntoView(pos: {line, ch}|{left, top, right, bottom}, ?margin: number)
      Scrolls the given element into view. pos may be either a {line, ch} position, referring to a given character, null, to refer to the cursor, or @@ -1232,7 +1231,7 @@

      Sizing, scrolling and positioning methods

      optional. When given, it indicates the amount of pixels around the given area that should be made visible as well.
      -
      cm.cursorCoords(where: boolean|{line, ch}, mode: string): {left: number, top: number, bottom: number}
      +
      cm.cursorCoords(where: boolean|{line, ch}, mode: string) → {left, top, bottom}
      Returns an {left, top, bottom} object containing the coordinates of the cursor position. If mode is "local", they will be @@ -1243,29 +1242,29 @@

      Sizing, scrolling and positioning methods

      end (false) of the selection, or, if a {line, ch} object is given, it specifies the precise position at which you want to measure.
      -
      cm.charCoords(pos: {line, ch}, mode: string): {left: number, top: number, bottom: number}
      +
      cm.charCoords(pos: {line, ch}, mode: string) → {left, right, top, bottom}
      Returns the position and dimensions of an arbitrary character. pos should be a {line, ch} object. This differs from cursorCoords in that it'll give the size of the whole character, rather than just the position that the cursor would have when it would sit at that position.
      -
      cm.coordsChar(object: {left: number, top: number}, ?mode: string): {line, ch}
      +
      cm.coordsChar(object: {left, top}, ?mode: string) → {line, ch}
      Given an {left, top} object, returns the {line, ch} position that corresponds to it. The optional mode parameter determines relative to what the coordinates are interpreted. It may be "window", "page" (the default), or "local".
      -
      cm.defaultTextHeight(): number
      +
      cm.defaultTextHeight() → number
      Returns the line height of the default font for the editor.
      -
      cm.defaultCharWidth(): number
      +
      cm.defaultCharWidth() → number
      Returns the pixel width of an 'x' in the default font for the editor. (Note that for non-monospace fonts, this is mostly useless, and even for monospace fonts, non-ascii characters might have a different width).
      -
      cm.getViewport(): {from: number, to: number}
      +
      cm.getViewport() → {from: number, to: number}
      Returns a {from, to} object indicating the start (inclusive) and end (exclusive) of the currently rendered part of the document. In big documents, when most content is @@ -1274,7 +1273,7 @@

      Sizing, scrolling and positioning methods

      the viewportChange event.
      -
      cm.refresh(): void
      +
      cm.refresh()
      If your code does something to change the size of the editor element (window resizes are already listened for), or unhides it, you should probably follow up by calling this method to @@ -1289,13 +1288,13 @@

      Mode, state, and token-related methods

      more detailed description of how these work.

      -
      doc.getMode(): object
      +
      doc.getMode() → object
      Gets the mode object for the editor. Note that this is distinct from getOption("mode"), which gives you the mode specification, rather than the resolved, instantiated mode object.
      -
      cm.getTokenAt(pos: {line, ch}): { start: string, end: string, string: string, type: string, state: string }
      +
      cm.getTokenAt(pos: {line, ch}) → object
      Retrieves information about the token the current mode found before the given position (a {line, ch} object). The returned object has the following properties: @@ -1309,7 +1308,7 @@

      Mode, state, and token-related methods

      state
      The mode's state at the end of this token.
      -
      cm.getStateAfter(?line: integer): object
      +
      cm.getStateAfter(?line: integer) → object
      Returns the mode's parser state, if any, at the end of the given line number. If no line number is given, the state at the end of the document is returned. This can be useful for storing @@ -1320,10 +1319,10 @@

      Mode, state, and token-related methods

      Miscellaneous methods

      -
      CodeMirror(elt:Element|(elt:Element) → void, ?config: object): void
      +
      CodeMirror(elt: Element|function(elt: Element), ?config: object)
      The constructor. See Basic Usage.
      -
      cm.operation(func: () → any): any
      +
      cm.operation(func: () → any) → any
      CodeMirror internally buffers changes and only updates its DOM structure after it has finished performing some operation. If you need to perform a lot of operations on a CodeMirror @@ -1333,7 +1332,7 @@

      Miscellaneous methods

      lot faster. The return value from this method will be the return value of your function.
      -
      cm.indentLine(line: integer|LineHandle, ?dir: string): void
      +
      cm.indentLine(line: integer|LineHandle, ?dir: string)
      Adjust the indentation of the given line. The second argument (which defaults to "smart") may be one of:
      @@ -1349,28 +1348,28 @@

      Miscellaneous methods

      Reduce the indentation of the line.
      -
      doc.posFromIndex(index: integer): {line, ch}
      +
      doc.posFromIndex(index: integer) → {line, ch}
      Calculates and returns a {line, ch} object for a zero-based index who's value is relative to the start of the editor's text. If the index is out of range of the text then the returned object is clipped to start or end of the text respectively.
      -
      doc.indexFromPos(object: {line, ch}): integer
      +
      doc.indexFromPos(object: {line, ch}) → integer
      The reverse of posFromIndex.
      -
      cm.focus(): void
      +
      cm.focus()
      Give the editor focus.
      -
      cm.getInputField(): TextAreaElement
      +
      cm.getInputField() → TextAreaElement
      Returns the hidden textarea used to read input.
      -
      cm.getWrapperElement(): Element
      +
      cm.getWrapperElement() → Element
      Returns the DOM node that represents the editor, and controls its size. Remove this from your tree to delete an editor instance.
      -
      cm.getScrollerElement(): Element
      +
      cm.getScrollerElement() → Element
      Returns the DOM node that is responsible for the scrolling of the editor.
      -
      cm.getGutterElement(): Element
      +
      cm.getGutterElement() → Element
      Fetches the DOM node that contains the editor gutters.
      @@ -1398,19 +1397,19 @@

      Static properties

      is submitted. A CodeMirror instance created this way has three additional methods:
      -
      cm.save(): void
      +
      cm.save()
      Copy the content of the editor into the textarea.
      -
      cm.toTextArea(): void
      +
      cm.toTextArea()
      Remove the editor, and restore the original textarea (with the editor's current content).
      -
      cm.getTextArea(): TextAreaElement
      +
      cm.getTextArea() → TextAreaElement
      Returns the textarea that the instance was based on.
      -
      CodeMirror.defineExtension(name: string, value: any): void
      +
      CodeMirror.defineExtension(name: string, value: any)
      If you want to define extra methods in terms of the CodeMirror API, it is possible to use defineExtension. This @@ -1418,14 +1417,14 @@

      Static properties

      CodeMirror instances created from then on.
      CodeMirror.defineOption(name: string, - default: any, updateFunc: (instance: CodeMirror, value: any, setOption: boolean) → void): void
      + default: any, updateFunc: function)
      Similarly, defineOption can be used to define new options for CodeMirror. The updateFunc will be called with the editor instance and the new value when an editor is initialized, and whenever the option is modified through setOption.
      -
      CodeMirror.defineInitHook(func: (instance: CodeMirror) → void): void
      +
      CodeMirror.defineInitHook(func: function)
      If your extention just needs to run some code whenever a CodeMirror instance is initialized, use CodeMirror.defineInitHook. Give it a function as @@ -1461,8 +1460,8 @@

      Add-ons

      case-insensitive. A search cursor has the following methods:
      - findNext(): boolean
      - findPrevious(): boolean + findNext() → boolean
      + findPrevious() → boolean
      Search forward or backward from the current position. The return value indicates whether a match was found. If @@ -1470,14 +1469,14 @@

      Add-ons

      array returned by the match method, in case you want to extract matched groups.
      - from(): {line, ch}
      - to(): {line, ch} + from() → {line, ch}
      + to() → {line, ch}
      These are only valid when the last call to findNext or findPrevious did not return false. They will return {line, ch} objects pointing at the start and end of the match.
      -
      replace(text: string): void
      +
      replace(text: string)
      Replaces the currently found match with the given text and adjusts the cursor position to reflect the replacement.
      @@ -1685,44 +1684,44 @@

      Writing CodeMirror Modes

      the following API:

      -
      eol(): boolean
      +
      eol() → boolean
      Returns true only if the stream is at the end of the line.
      -
      sol(): boolean
      +
      sol() → boolean
      Returns true only if the stream is at the start of the line.
      -
      peek(): string
      +
      peek() → string
      Returns the next character in the stream without advancing it. Will return an null at the end of the line.
      -
      next(): string
      +
      next() → string
      Returns the next character in the stream and advances it. Also returns null when no more characters are available.
      -
      eat(match: string|regexp|(char:string) → boolean): string
      +
      eat(match: string|regexp|function(char: string) → boolean) → string
      match can be a character, a regular expression, or a function that takes a character and returns a boolean. If the next character in the stream 'matches' the given argument, it is consumed and returned. Otherwise, undefined is returned.
      -
      eatWhile(match: string|regexp|(char:string) → boolean): boolean
      +
      eatWhile(match: string|regexp|function(char: string) → boolean) → boolean
      Repeatedly calls eat with the given argument, until it fails. Returns true if any characters were eaten.
      -
      eatSpace(): boolean
      +
      eatSpace() → boolean
      Shortcut for eatWhile when matching white-space.
      -
      skipToEnd(): void
      +
      skipToEnd()
      Moves the position to the end of the line.
      -
      skipTo(ch: string): boolean
      +
      skipTo(ch: string) → boolean
      Skips to the next occurrence of the given character, if found on the current line (doesn't advance the stream if the character does not occur on the line). Returns true if the character was found.
      - match(pattern: string, ?consume: boolean, ?caseFold: boolean): boolean
      - match(pattern: regexp, ?consume: boolean): array<string> + match(pattern: string, ?consume: boolean, ?caseFold: boolean) → boolean
      + match(pattern: regexp, ?consume: boolean) → array<string>
      Act like a multi-character eat—if consume is true @@ -1735,18 +1734,18 @@

      Writing CodeMirror Modes

      returned by match, in case you need to extract matched groups.
      -
      backUp(n: integer): void
      +
      backUp(n: integer)
      Backs up the stream n characters. Backing it up further than the start of the current token will cause things to break, so be careful.
      -
      column(): integer
      +
      column() → integer
      Returns the column (taking into account tabs) at which the current token starts.
      -
      indentation(): integer
      +
      indentation() → integer
      Tells you how far the current line has been indented, in spaces. Corrects for tab characters.
      -
      current(): string
      +
      current() → string
      Get the string between the start of the current token and the current stream position.
      From 1cf23b4647b6864dac96debc4994284472fabad5 Mon Sep 17 00:00:00 2001 From: Marijn Haverbeke Date: Thu, 4 Apr 2013 11:35:04 +0200 Subject: [PATCH 0969/5780] Do some styling in an attempt to make manual more readable --- doc/manual.html | 437 ++++++++++++++++++++++++------------------------ 1 file changed, 215 insertions(+), 222 deletions(-) diff --git a/doc/manual.html b/doc/manual.html index d805185f15..45ea1a9225 100644 --- a/doc/manual.html +++ b/doc/manual.html @@ -1,10 +1,10 @@ - + CodeMirror: User Manual - - + + @@ -14,13 +14,16 @@ +

      { } CodeMirror

      - +
       /* User manual and
          reference guide */
      @@ -119,11 +122,11 @@ 

      Configuration

      These are the supported options:

      -
      value: string|CodeMirror.Doc
      +
      value: string|CodeMirror.Doc
      The starting value of the editor. Can be a string, or a document object.
      -
      mode: string|object
      +
      mode: string|object
      The mode to use. When not given, this will default to the first mode that was loaded. It may be a string, which either simply names the mode or is @@ -140,7 +143,7 @@

      Configuration

      mode names to their constructors, and the second maps MIME types to mode specs.
      -
      theme: string
      +
      theme: string
      The theme to style the editor with. You must make sure the CSS file defining the corresponding .cm-s-[name] styles is loaded (see @@ -152,29 +155,29 @@

      Configuration

      the cm-s-foo and the cm-s-bar classes to the editor.
      -
      indentUnit: integer
      +
      indentUnit: integer
      How many spaces a block (whatever that means in the edited language) should be indented. The default is 2.
      -
      smartIndent: boolean
      +
      smartIndent: boolean
      Whether to use the context-sensitive indentation that the mode provides (or just indent the same as the line before). Defaults to true.
      -
      tabSize: integer
      +
      tabSize: integer
      The width of a tab character. Defaults to 4.
      -
      indentWithTabs: boolean
      +
      indentWithTabs: boolean
      Whether, when indenting, the first N*tabSize spaces should be replaced by N tabs. Default is false.
      -
      electricChars: boolean
      +
      electricChars: boolean
      Configures whether the editor should re-indent the current line when a character is typed that might change its proper indentation (only works if the mode supports indentation). Default is true.
      -
      rtlMoveVisually: boolean
      +
      rtlMoveVisually: boolean
      Determines whether horizontal cursor movement through right-to-left (Arabic, Hebrew) text is visual (pressing the left arrow moves the cursor left) or logical (pressing the left arrow @@ -182,7 +185,7 @@

      Configuration

      right in right-to-left text). The default is false on Windows, and true on other platforms.
      -
      keyMap: string
      +
      keyMap: string
      Configures the keymap to use. The default is "default", which is the only keymap defined in codemirror.js itself. Extra keymaps are found in @@ -190,28 +193,28 @@

      Configuration

      the section on keymaps for more information.
      -
      extraKeys: object
      +
      extraKeys: object
      Can be used to specify extra keybindings for the editor, alongside the ones defined by keyMap. Should be either null, or a valid keymap value.
      -
      lineWrapping: boolean
      +
      lineWrapping: boolean
      Whether CodeMirror should scroll or wrap for long lines. Defaults to false (scroll).
      -
      lineNumbers: boolean
      +
      lineNumbers: boolean
      Whether to show line numbers to the left of the editor.
      -
      firstLineNumber: integer
      +
      firstLineNumber: integer
      At which number to start counting lines. Default is 1.
      -
      lineNumberFormatter: function(line: integer) → string
      +
      lineNumberFormatter: function(line: integer) → string
      A function used to format line numbers. The function is passed the line number, and should return a string that will be shown in the gutter.
      -
      gutters: array<string>
      +
      gutters: array<string>
      Can be used to add extra gutters (beyond or instead of the line number gutter). Should be an array of CSS class names, each of which defines a width (and optionally a @@ -223,31 +226,31 @@

      Configuration

      names are the keys passed to setGutterMarker.
      -
      fixedGutter: boolean
      +
      fixedGutter: boolean
      Determines whether the gutter scrolls along with the content horizontally (false) or whether it stays fixed during horizontal scrolling (true, the default).
      -
      readOnly: boolean|string
      +
      readOnly: boolean|string
      This disables editing of the editor content by the user. If the special value "nocursor" is given (instead of simply true), focusing of the editor is also disallowed.
      -
      showCursorWhenSelecting: boolean
      +
      showCursorWhenSelecting: boolean
      Whether the cursor should be drawn when a selection is active. Defaults to false.
      -
      undoDepth: integer
      +
      undoDepth: integer
      The maximum number of undo levels that the editor stores. Defaults to 40.
      -
      tabindex: integer
      +
      tabindex: integer
      The tab index to assign to the editor. If not given, no tab index will be assigned.
      -
      autofocus: boolean
      +
      autofocus: boolean
      Can be used to make CodeMirror focus itself on initialization. Defaults to off. When fromTextArea is @@ -262,10 +265,10 @@

      Configuration

      might want to skip them the first time you read this manual.

      -
      dragDrop: boolean
      +
      dragDrop: boolean
      Controls whether drag-and-drop is enabled. On by default.
      -
      onDragEvent: function(instance: CodeMirror, event: Event) → boolean
      +
      onDragEvent: function(instance: CodeMirror, event: Event) → boolean
      When given, this will be called when the editor is handling a dragenter, dragover, or drop event. It will be passed the editor instance @@ -274,7 +277,7 @@

      Configuration

      return true to indicate that CodeMirror should not do anything further.
      -
      onKeyEvent: function(instance: CodeMirror, event: Event) → boolean
      +
      onKeyEvent: function(instance: CodeMirror, event: Event) → boolean
      This provides a rather low-level hook into CodeMirror's key handling. If provided, this function will be called on every keydown, keyup, @@ -293,35 +296,35 @@

      Configuration

      is keydown (or keypress for actions that need character data).
      -
      cursorBlinkRate: number
      +
      cursorBlinkRate: number
      Half-period in milliseconds used for cursor blinking. The default blink rate is 530ms.
      -
      cursorHeight: number
      +
      cursorHeight: number
      Determines the height of the cursor. Default is 1, meaning it spans the whole height of the line. For some fonts (and by some tastes) a smaller height (for example 0.85), which causes the cursor to not reach all the way to the bottom of the line, looks better
      -
      workTime, workDelay: number
      +
      workTime, workDelay: number
      Highlighting is done by a pseudo background-thread that will work for workTime milliseconds, and then use timeout to sleep for workDelay milliseconds. The defaults are 200 and 300, you can change these options to make the highlighting more or less aggressive.
      -
      workDelay: number
      +
      workDelay: number
      See workTime.
      -
      pollInterval: number
      +
      pollInterval: number
      Indicates how quickly CodeMirror should poll its input textarea for changes (when focused). Most input is captured by events, but some things, like IME input on some browsers, don't generate events that allow CodeMirror to properly detect it. Thus, it polls. Default is 100 milliseconds.
      -
      flattenSpans: boolean
      +
      flattenSpans: boolean
      By default, CodeMirror will combine adjacent tokens into a single span if they have the same class. This will result in a simpler DOM tree, and thus perform better. With some kinds of @@ -329,14 +332,14 @@

      Configuration

      document looks. You can set this option to false to disable this behavior.
      -
      maxHighlightLength: number
      +
      maxHighlightLength: number
      When highlighting long lines, in order to stay responsive, the editor will give up and simply style the rest of the line as plain text when it reaches a certain position. The default is 10000. You can set this to Infinity to turn off this behavior.
      -
      viewportMargin: integer
      +
      viewportMargin: integer
      Specifies the amount of lines that are rendered above and below the part of the document that's currently scrolled into view. This affects the amount of updates needed when scrolling, @@ -360,7 +363,7 @@

      Events

      refers to the editor instance.

      -
      "change" (instance: CodeMirror, changeObj: object)
      +
      "change" (instance: CodeMirror, changeObj: object)
      Fires every time the content of the editor is changed. The changeObj is a {from, to, text, removed, next} object containing information about the changes @@ -377,7 +380,7 @@

      Events

      a next property pointing to another change object (which may point to another, etc).
      -
      "beforeChange" (instance: CodeMirror, changeObj: object)
      +
      "beforeChange" (instance: CodeMirror, changeObj: object)
      This event is fired before a change is applied, and its handler may choose to modify or cancel the change. The changeObj object @@ -397,11 +400,11 @@

      Events

      the CodeMirror implementation, probably cause the editor to become corrupted.
      -
      "cursorActivity" (instance: CodeMirror)
      +
      "cursorActivity" (instance: CodeMirror)
      Will be fired when the cursor or selection moves, or any change is made to the editor content.
      -
      "beforeSelectionChange" (instance: CodeMirror, selection: {head, anchor})
      +
      "beforeSelectionChange" (instance: CodeMirror, selection: {head, anchor})
      This event is fired before the selection is moved. Its handler may modify the resulting selection head and anchor. The selection parameter is an object @@ -413,13 +416,13 @@

      Events

      handlers — they should not do anything to directly update the state of the editor.
      -
      "viewportChange" (instance: CodeMirror, from: number, to: number)
      +
      "viewportChange" (instance: CodeMirror, from: number, to: number)
      Fires whenever the view port of the editor changes (due to scrolling, editing, or any other factor). The from and to arguments give the new start and end of the viewport.
      -
      "gutterClick" (instance: CodeMirror, line: integer, gutter: string, clickEvent: Event)
      +
      "gutterClick" (instance: CodeMirror, line: integer, gutter: string, clickEvent: Event)
      Fires when the editor gutter (the line-number area) is clicked. Will pass the editor instance as first argument, the (zero-based) number of the line that was clicked as second @@ -427,19 +430,19 @@

      Events

      argument, and the raw mousedown event object as fourth argument.
      -
      "focus" (instance: CodeMirror)
      +
      "focus" (instance: CodeMirror)
      Fires whenever the editor is focused.
      -
      "blur" (instance: CodeMirror)
      +
      "blur" (instance: CodeMirror)
      Fires whenever the editor is unfocused.
      -
      "scroll" (instance: CodeMirror)
      +
      "scroll" (instance: CodeMirror)
      Fires when the editor is scrolled.
      -
      "update" (instance: CodeMirror)
      +
      "update" (instance: CodeMirror)
      Will be fired whenever CodeMirror updates its DOM display.
      -
      "renderLine" (instance: CodeMirror, line: integer, element: Element)
      +
      "renderLine" (instance: CodeMirror, line: integer, element: Element)
      Fired whenever a line is (re-)rendered to the DOM. Fired right after the DOM element is built, before it is added to the document. The handler may mess with the style of @@ -455,7 +458,7 @@

      Events

      following events:

      -
      "change" (doc: CodeMirror.Doc, changeObj: object)
      +
      "change" (doc: CodeMirror.Doc, changeObj: object)
      Fired whenever a change occurs to the document. changeObj has a similar type as the object passed to the @@ -464,15 +467,15 @@

      Events

      document change events are not batched (whereas editor change events are).
      -
      "beforeChange" (doc: CodeMirror.Doc, change: object)
      +
      "beforeChange" (doc: CodeMirror.Doc, change: object)
      See the description of the same event on editor instances.
      -
      "cursorActivity" (doc: CodeMirror.Doc)
      +
      "cursorActivity" (doc: CodeMirror.Doc)
      Fired whenever the cursor or selection in this document changes.
      -
      "beforeSelectionChange" (doc: CodeMirror.Doc, selection: {head, anchor})
      +
      "beforeSelectionChange" (doc: CodeMirror.Doc, selection: {head, anchor})
      Equivalent to the event by the same name as fired on editor instances.
      @@ -483,12 +486,12 @@

      Events

      support these events:

      -
      "delete" ()
      +
      "delete" ()
      Will be fired when the line object is deleted. A line object is associated with the start of the line. Mostly useful when you need to find out when your gutter markers on a given line are removed.
      -
      "change" (line: LineHandle, changeObj: object)
      +
      "change" (line: LineHandle, changeObj: object)
      Fires when the line's text content is changed in any way (but the line is not deleted outright). The change object is similar to the one passed @@ -502,12 +505,12 @@

      Events

      following events:

      -
      "beforeCursorEnter" ()
      +
      "beforeCursorEnter" ()
      Fired when the cursor enters the marked range. From this event handler, the editor state may be inspected but not modified, with the exception that the range on which the event fires may be cleared.
      -
      "clear" ()
      +
      "clear" ()
      Fired when the range is cleared, either through cursor movement in combination with clearOnEnter @@ -515,10 +518,10 @@

      Events

      be fired once per handle. Note that deleting the range through text editing does not fire this event, because an undo action might bring the range back into existence.
      -
      "hide" ()
      +
      "hide" ()
      Fired when the last part of the marker is removed from the document by editing operations.
      -
      "unhide" ()
      +
      "unhide" ()
      Fired when, after the marker was removed by editing, a undo operation brought the marker back.
      @@ -528,7 +531,7 @@

      Events

      these events:

      -
      "redraw" ()
      +
      "redraw" ()
      Fired whenever the editor re-adds the widget to the DOM. This will happen once right after the widget is added (if it is scrolled into view), and then again whenever it is scrolled out @@ -622,13 +625,13 @@

      Customized Styling

      this file serve the following roles:

      -
      CodeMirror
      +
      CodeMirror
      The outer element of the editor. This should be used for the editor width, height, borders and positioning. Can also be used to set styles that should hold for everything inside the editor (such as font and font size), or to set a background.
      -
      CodeMirror-scroll
      +
      CodeMirror-scroll
      Whether the editor scrolls (overflow: auto + fixed height). By default, it does. Setting the CodeMirror class to have height: @@ -637,40 +640,40 @@

      Customized Styling

      to resize to fit its content.
      -
      CodeMirror-focused
      +
      CodeMirror-focused
      Whenever the editor is focused, the top element gets this class. This is used to hide the cursor and give the selection a different color when the editor is not focused.
      -
      CodeMirror-gutters
      +
      CodeMirror-gutters
      This is the backdrop for all gutters. Use it to set the default gutter background color, and optionally add a border on the right of the gutters.
      -
      CodeMirror-linenumbers
      +
      CodeMirror-linenumbers
      Use this for giving a background or width to the line number gutter.
      -
      CodeMirror-linenumber
      +
      CodeMirror-linenumber
      Used to style the actual individual line numbers. These won't be children of the CodeMirror-linenumbers (plural) element, but rather will be absolutely positioned to overlay it. Use this to set alignment and text properties for the line numbers.
      -
      CodeMirror-lines
      +
      CodeMirror-lines
      The visible lines. This is where you specify vertical padding for the editor content.
      -
      CodeMirror-cursor
      +
      CodeMirror-cursor
      The cursor is a block element that is absolutely positioned. You can make it look whichever way you want.
      -
      CodeMirror-selected
      +
      CodeMirror-selected
      The selection is represented by span elements with this class.
      -
      CodeMirror-matchingbracket, +
      CodeMirror-matchingbracket, CodeMirror-nonmatchingbracket
      These are used to style matched (or unmatched) brackets.
      @@ -710,55 +713,53 @@

      Programming API

      Content manipulation methods

      -
      doc.getValue(?seperator: string) → string
      +
      doc.getValue(?seperator: string) → string
      Get the current editor content. You can pass it an optional argument to specify the string to be used to separate lines (defaults to "\n").
      -
      doc.setValue(content: string)
      +
      doc.setValue(content: string)
      Set the editor content.
      -
      doc.getRange(from: {line, ch}, to: {line, ch}, ?seperator: string) → string
      +
      doc.getRange(from: {line, ch}, to: {line, ch}, ?seperator: string) → string
      Get the text between the given points in the editor, which should be {line, ch} objects. An optional third argument can be given to indicate the line separator string to use (defaults to "\n").
      -
      doc.replaceRange(replacement: string, from: {line, ch}, to: {line, ch})
      +
      doc.replaceRange(replacement: string, from: {line, ch}, to: {line, ch})
      Replace the part of the document between from and to with the given string. from and to must be {line, ch} objects. to can be left off to simply insert the string at position from.
      -
      doc.getLine(n: integer) → string
      +
      doc.getLine(n: integer) → string
      Get the content of line n.
      -
      doc.setLine(n: integer, text: string)
      +
      doc.setLine(n: integer, text: string)
      Set the content of line n.
      -
      doc.removeLine(n: integer)
      +
      doc.removeLine(n: integer)
      Remove the given line from the document.
      -
      doc.lineCount() → integer
      +
      doc.lineCount() → integer
      Get the number of lines in the editor.
      -
      doc.firstLine() → integer
      +
      doc.firstLine() → integer
      Get the first line of the editor. This will usually be zero but for linked sub-views, or documents instantiated with a non-zero first line, it might return other values.
      -
      doc.lastLine() → integer
      +
      doc.lastLine() → integer
      Get the last line of the editor. This will usually be doc.lineCount() - 1, but for linked sub-views, it might return other values.
      -
      doc.getLineHandle(num: integer) → LineHandle
      +
      doc.getLineHandle(num: integer) → LineHandle
      Fetches the line handle for the given line number.
      -
      doc.getLineNumber(handle: LineHandle) → integer
      +
      doc.getLineNumber(handle: LineHandle) → integer
      Given a line handle, returns the current position of that line (or null when it is no longer in the document).
      -
      - doc.eachLine(f: (line: LineHandle))
      - doc.eachLine(start: integer, end: integer, f: (line: LineHandle)) -
      +
      doc.eachLine(f: (line: LineHandle))
      +
      doc.eachLine(start: integer, end: integer, f: (line: LineHandle))
      Iterate over the whole document, or if start and end line numbers are given, the range from start up to (not including) end, @@ -769,12 +770,12 @@

      Content manipulation methods

      a text property containing the line's content (as a string).
      -
      doc.markClean()
      +
      doc.markClean()
      Set the editor content as 'clean', a flag that it will retain until it is edited, and which will be set again when such an edit is undone again. Useful to track whether the content needs to be saved.
      -
      doc.isClean() → boolean
      +
      doc.isClean() → boolean
      Returns whether the document is currently clean (not modified since initialization or the last call to markClean).
      @@ -783,9 +784,9 @@

      Content manipulation methods

      Cursor and selection methods

      -
      doc.getSelection() → string
      +
      doc.getSelection() → string
      Get the currently selected code.
      -
      doc.replaceSelection(replacement: string, ?collapse: string)
      +
      doc.replaceSelection(replacement: string, ?collapse: string)
      Replace the selection with the given string. By default, the new selection will span the inserted text. The optional collapse argument can be used to change @@ -793,7 +794,7 @@

      Cursor and selection methods

      collapse the selection to the start or end of the inserted text.
      -
      doc.getCursor(?start: string) → {line, ch}
      +
      doc.getCursor(?start: string) → {line, ch}
      start is a an optional string indicating which end of the selection to return. It may be "start", "end", "head" @@ -802,18 +803,18 @@

      Cursor and selection methods

      selection). Omitting the argument is the same as passing "head". A {line, ch} object will be returned.
      -
      doc.somethingSelected() → boolean
      +
      doc.somethingSelected() → boolean
      Return true if any text is selected.
      -
      doc.setCursor(pos: {line, ch})
      +
      doc.setCursor(pos: {line, ch})
      Set the cursor position. You can either pass a single {line, ch} object, or the line and the character as two separate parameters.
      -
      doc.setSelection(anchor: {line, ch}, head: {line, ch})
      +
      doc.setSelection(anchor: {line, ch}, head: {line, ch})
      Set the selection range. anchor and head should be {line, ch} objects. head defaults to anchor when not given.
      -
      doc.extendSelection(from: {line, ch}, ?to: {line, ch})
      +
      doc.extendSelection(from: {line, ch}, ?to: {line, ch})
      Similar to setSelection, but will, if shift is held or @@ -823,16 +824,16 @@

      Cursor and selection methods

      ensure a region (for example a word or paragraph) will end up selected (in addition to whatever lies between that region and the current anchor).
      -
      doc.setExtending(value: boolean)
      +
      doc.setExtending(value: boolean)
      Sets or clears the 'extending' flag, which acts similar to the shift key, in that it will cause cursor movement and calls to extendSelection to leave the selection anchor in place.
      -
      cm.hasFocus() → boolean
      +
      cm.hasFocus() → boolean
      Tells you whether the editor currently has focus.
      -
      cm.findPosH(start: {line, ch}, amount: integer, unit: string, visually: boolean) → {line, ch, ?hitSide: boolean}
      +
      cm.findPosH(start: {line, ch}, amount: integer, unit: string, visually: boolean) → {line, ch, ?hitSide: boolean}
      Used to find the target position for horizontal cursor motion. start is a {line, ch} object, amount an integer (may be negative), @@ -845,7 +846,7 @@

      Cursor and selection methods

      the motion was clipped by hitting the end or start of the document, the returned value will have a hitSide property set to true.
      -
      cm.findPosV(start: {line, ch}, amount: integer, unit: string) → {line, ch, ?hitSide: boolean}
      +
      cm.findPosV(start: {line, ch}, amount: integer, unit: string) → {line, ch, ?hitSide: boolean}
      Similar to findPosH, but used for vertical motion. unit may be "line" or "page". The other @@ -856,16 +857,16 @@

      Cursor and selection methods

      Configuration methods

      -
      cm.setOption(option: string, value: any)
      +
      cm.setOption(option: string, value: any)
      Change the configuration of the editor. option should the name of an option, and value should be a valid value for that option.
      -
      cm.getOption(option: string) → any
      +
      cm.getOption(option: string) → any
      Retrieves the current value of the given option for this editor instance.
      -
      cm.addKeyMap(map: object, bottom: boolean)
      +
      cm.addKeyMap(map: object, bottom: boolean)
      Attach an additional keymap to the editor. This is mostly useful for add-ons that need to register some key handlers without trampling on @@ -877,14 +878,14 @@

      Configuration methods

      than those added later, unless the bottom argument was passed, in which case they end up below other keymaps added with this method.
      -
      cm.removeKeyMap(map: object)
      +
      cm.removeKeyMap(map: object)
      Disable a keymap added with addKeyMap. Either pass in the keymap object itself, or a string, which will be compared against the name property of the active keymaps.
      -
      cm.addOverlay(mode: string|object, ?options: object)
      +
      cm.addOverlay(mode: string|object, ?options: object)
      Enable a highlighting overlay. This is a stateless mini-mode that can be used to add extra highlighting. For example, the search add-on uses it to @@ -898,18 +899,18 @@

      Configuration methods

      allow the overlay styling, when not null, to override the styling of the base mode entirely, instead of the two being applied together.
      -
      cm.removeOverlay(mode: string|object)
      +
      cm.removeOverlay(mode: string|object)
      Pass this the exact argument passed for the mode parameter to addOverlay to remove an overlay again.
      -
      cm.on(type: string, func: (...args))
      +
      cm.on(type: string, func: (...args))
      Register an event handler for the given event type (a string) on the editor instance. There is also a CodeMirror.on(object, type, func) version that allows registering of events on any object.
      -
      cm.off(type: string, func: (...args))
      +
      cm.off(type: string, func: (...args))
      Remove an event handler on the editor instance. An equivalent CodeMirror.off(object, type, func) also exists.
      @@ -928,53 +929,51 @@

      Document management methods

      it start at a line number other than 0, respectively.

      -
      cm.getDoc() → CodeMirror.Doc
      +
      cm.getDoc() → Doc
      Retrieve the currently active document from an editor.
      -
      doc.getEditor() → CodeMirror
      +
      doc.getEditor() → CodeMirror
      Retrieve the editor associated with a document. May return null.
      -
      cm.swapDoc(doc: CodeMirror.Doc) → CodeMirror.Doc
      +
      cm.swapDoc(doc: CodeMirror.Doc) → Doc
      Attach a new document to the editor. Returns the old document, which is now no longer associated with an editor.
      -
      doc.copy(copyHistory: boolean) → CodeMirror.Doc
      +
      doc.copy(copyHistory: boolean) → Doc
      Create an identical copy of the given doc. When copyHistory is true, the history will also be copied. Can not be called directly on an editor.
      -
      doc.linkedDoc(options: object) → CodeMirror.Doc
      +
      doc.linkedDoc(options: object) → Doc
      Create a new document that's linked to the target document. Linked documents will stay in sync (changes to one are also applied to the other) until unlinked. These are the options that are supported:
      -
      sharedHist: boolean
      +
      sharedHist: boolean
      When turned on, the linked copy will share an undo history with the original. Thus, something done in one of the two can be undone in the other, and vice versa.
      -
      - from: integer
      - to: integer -
      +
      from: integer
      +
      to: integer
      Can be given to make the new document a subview of the original. Subviews only show a given range of lines. Note that line coordinates inside the subview will be consistent with those of the parent, so that for example a subview starting at line 10 will refer to its first line as line 10, not 0.
      -
      mode: string|object
      +
      mode: string|object
      By default, the new document inherits the mode of the parent. This option can be set to a mode spec to give it a different mode.
      -
      doc.unlinkDoc(doc: CodeMirror.Doc)
      +
      doc.unlinkDoc(doc: CodeMirror.Doc)
      Break the link between two documents. After calling this, changes will no longer propagate between the documents, and, if they had a shared history, the history will become separate.
      -
      doc.iterLinkedDocs(function: (doc: CodeMirror.Doc, sharedHist: boolean))
      +
      doc.iterLinkedDocs(function: (doc: CodeMirror.Doc, sharedHist: boolean))
      Will call the given function for all documents linked to the target document. It will be passed two arguments, the linked document and a boolean indicating whether that document shares history @@ -984,20 +983,20 @@

      Document management methods

      History-related methods

      -
      doc.undo()
      +
      doc.undo()
      Undo one edit (if any undo events are stored).
      -
      doc.redo()
      +
      doc.redo()
      Redo one undone edit.
      -
      doc.historySize() → {undo: integer, redo: integer}
      +
      doc.historySize() → {undo: integer, redo: integer}
      Returns an object with {undo, redo} properties, both of which hold integers, indicating the amount of stored undo and redo operations.
      -
      doc.clearHistory()
      +
      doc.clearHistory()
      Clears the editor's undo history.
      -
      doc.getHistory() → object
      +
      doc.getHistory() → object
      Get a (JSON-serializeable) representation of the undo history.
      -
      doc.setHistory(history: object)
      +
      doc.setHistory(history: object)
      Replace the editor's undo history with the one provided, which must be a value as returned by getHistory. Note that @@ -1009,33 +1008,33 @@

      History-related methods

      Text-marking methods

      -
      doc.markText(from: {line, ch}, to: {line, ch}, ?options: object) → CodeMirror.TextMarker
      +
      doc.markText(from: {line, ch}, to: {line, ch}, ?options: object) → TextMarker
      Can be used to mark a range of text with a specific CSS class name. from and to should be {line, ch} objects. The options parameter is optional. When given, it should be an object that may contain the following configuration options:
      -
      className: string
      +
      className: string
      Assigns a CSS class to the marked stretch of text.
      -
      inclusiveLeft: boolean
      +
      inclusiveLeft: boolean
      Determines whether text inserted on the left of the marker will end up inside or outside of it.
      -
      inclusiveRight: boolean
      +
      inclusiveRight: boolean
      Like inclusiveLeft, but for the right side.
      -
      atomic: boolean
      +
      atomic: boolean
      Atomic ranges act as a single unit when cursor movement is concerned—i.e. it is impossible to place the cursor inside of them. In atomic ranges, inclusiveLeft and inclusiveRight have a different meaning—they will prevent the cursor from being placed respectively directly before and directly after the range.
      -
      collapsed: boolean
      +
      collapsed: boolean
      Collapsed ranges do not show up in the display. Setting a range to be collapsed will automatically make it atomic.
      -
      clearOnEnter: boolean
      +
      clearOnEnter: boolean
      When enabled, will cause the mark to clear itself whenever the cursor enters its range. This is mostly useful for text-replacement widgets that need to 'snap open' when the @@ -1043,11 +1042,11 @@

      Text-marking methods

      the "clear" event fired on the range handle can be used to be notified when this happens.
      -
      replacedWith: Element
      +
      replacedWith: Element
      Use a given node to display this range. Implies both collapsed and atomic. The given DOM node must be an inline element (as opposed to a block element).
      -
      readOnly: boolean|string
      +
      readOnly: boolean|string
      A read-only span can, as long as it is not cleared, not be modified except by calling setValue to reset @@ -1056,12 +1055,12 @@

      Text-marking methods

      existing undo events being partially nullified by read-only spans would corrupt the history (in the current implementation).
      -
      startStyle: string
      Can be used to specify +
      startStyle: string
      Can be used to specify an extra CSS class to be applied to the leftmost span that is part of the marker.
      -
      endStyle: string
      Equivalent +
      endStyle: string
      Equivalent to startStyle, but for the rightmost span.
      -
      shared: boolean
      When the +
      shared: boolean
      When the target document is linked to other documents, you can set shared to true to make the marker appear in all documents. By default, a marker appears @@ -1082,7 +1081,7 @@

      Text-marking methods

      the replacedWith option, if any.
      -
      doc.setBookmark(pos: {line, ch}, ?options: object) → CodeMirror.TextMarker
      +
      doc.setBookmark(pos: {line, ch}, ?options: object) → TextMarker
      Inserts a bookmark, a handle that follows the text around it as it is being edited, at the given position. A bookmark has two methods find() and clear(). The first @@ -1091,27 +1090,27 @@

      Text-marking methods

      The options argument is optional. If given, the following properties are recognized:
      -
      widget: Element
      Can be used to display a DOM +
      widget: Element
      Can be used to display a DOM node at the current location of the bookmark (analogous to the replacedWith option to markText).
      -
      insertLeft: boolean
      By default, text typed +
      insertLeft: boolean
      By default, text typed when the cursor is on top of the bookmark will end up to the right of the bookmark. Set this option to true to make it go to the left instead.
      -
      doc.findMarksAt(pos: {line, ch}) → array<CodeMirror.TextMarker>
      +
      doc.findMarksAt(pos: {line, ch}) → array<TextMarker>
      Returns an array of all the bookmarks and marked ranges present at the given position.
      -
      doc.getAllMarks() → array<CodeMirror.TextMarker>
      +
      doc.getAllMarks() → array<TextMarker>
      Returns an array containing all marked ranges in the document.

      Widget, gutter, and decoration methods

      -
      cm.setGutterMarker(line: integer|LineHandle, gutterID: string, value: Element) → LineHandle
      +
      cm.setGutterMarker(line: integer|LineHandle, gutterID: string, value: Element) → LineHandle
      Sets the gutter marker for the given gutter (identified by its CSS class, see the gutters option) @@ -1120,11 +1119,11 @@

      Widget, gutter, and decoration methods

      will be shown in the specified gutter next to the specified line.
      -
      cm.clearGutter(gutterID: string)
      +
      cm.clearGutter(gutterID: string)
      Remove all gutter markers in the gutter with the given ID.
      -
      cm.addLineClass(line: integer|LineHandle, where: string, class: string) → LineHandle
      +
      cm.addLineClass(line: integer|LineHandle, where: string, class: string) → LineHandle
      Set a CSS class name for the given line. line can be a number or a line handle. where determines to which element this class should be applied, can can be one @@ -1135,7 +1134,7 @@

      Widget, gutter, and decoration methods

      gutter elements). class should be the name of the class to apply.
      -
      cm.removeLineClass(line: integer|LineHandle, where: string, class: string) → LineHandle
      +
      cm.removeLineClass(line: integer|LineHandle, where: string, class: string) → LineHandle
      Remove a CSS class from a line. line can be a line handle or number. where should be one of "text", "background", @@ -1144,7 +1143,7 @@

      Widget, gutter, and decoration methods

      can be left off to remove all classes for the specified node, or be a string to remove only a specific class.
      -
      cm.lineInfo(line: integer|LineHandle) → {line: integer, handle: LineHandle, text: string, gutterMarkers: object<Element>, textClass: string, bgClass: string, wrapClass: string, widgets: array<CodeMirror.LineWidget>}
      +
      cm.lineInfo(line: integer|LineHandle) → object
      Returns the line number, text content, and marker status of the given line, which can be either a number or a line handle. The returned object has the structure {line, handle, text, @@ -1155,7 +1154,7 @@

      Widget, gutter, and decoration methods

      line, and the various class properties refer to classes added with addLineClass.
      -
      cm.addWidget(pos: {line, ch}, node: Element, scrollIntoView: boolean)
      +
      cm.addWidget(pos: {line, ch}, node: Element, scrollIntoView: boolean)
      Puts node, which should be an absolutely positioned DOM node, into the editor, positioned right below the given {line, ch} position. @@ -1164,7 +1163,7 @@

      Widget, gutter, and decoration methods

      widget again, simply use DOM methods (move it somewhere else, or call removeChild on its parent).
      -
      cm.addLineWidget(line: integer|LineHandle, node: Element, ?options: object) → CodeMirror.LineWidget
      +
      cm.addLineWidget(line: integer|LineHandle, node: Element, ?options: object) → LineWidget
      Adds a line widget, an element shown below a line, spanning the whole of the editor's width, and moving the lines below it downwards. line should be either an integer or a @@ -1174,15 +1173,15 @@

      Widget, gutter, and decoration methods

      the widget. The following options are supported (all default to false) →
      -
      coverGutter: boolean
      +
      coverGutter: boolean
      Whether the widget should cover the gutter.
      -
      noHScroll: boolean
      +
      noHScroll: boolean
      Whether the widget should stay fixed in the face of horizontal scrolling.
      -
      above: boolean
      +
      above: boolean
      Causes the widget to be placed above instead of below the text of the line.
      -
      showIfHidden: boolean
      +
      showIfHidden: boolean
      When true, will cause the widget to be rendered even if the line it is associated with is hidden.
      @@ -1192,8 +1191,8 @@

      Widget, gutter, and decoration methods

      the widget placement. It'll have a line property pointing at the line handle that it is associated with, and the following methods:
      -
      clear()
      Removes the widget.
      -
      changed()
      Call +
      clear()
      Removes the widget.
      +
      changed()
      Call this if you made some change to the widget's DOM node that might affect its height. It'll force CodeMirror to update the height of the line that contains the widget.
      @@ -1204,7 +1203,7 @@

      Widget, gutter, and decoration methods

      Sizing, scrolling and positioning methods

      -
      cm.setSize(width: number|string, height: number|string)
      +
      cm.setSize(width: number|string, height: number|string)
      Programatically set the size of the editor (overriding the applicable CSS rules). width and height height @@ -1213,16 +1212,16 @@

      Sizing, scrolling and positioning methods

      pass null for either of them to indicate that that dimension should not be changed.
      -
      cm.scrollTo(x: number, y: number)
      +
      cm.scrollTo(x: number, y: number)
      Scroll the editor to a given (pixel) position. Both arguments may be left as null or undefined to have no effect.
      -
      cm.getScrollInfo() → {left, top, width, height, clientWidth, clientHeight}
      +
      cm.getScrollInfo() → {left, top, width, height, clientWidth, clientHeight}
      Get an {left, top, width, height, clientWidth, clientHeight} object that represents the current scroll position, the size of the scrollable area, and the size of the visible area (minus scrollbars).
      -
      cm.scrollIntoView(pos: {line, ch}|{left, top, right, bottom}, ?margin: number)
      +
      cm.scrollIntoView(pos: {line, ch}|{left, top, right, bottom}, ?margin: number)
      Scrolls the given element into view. pos may be either a {line, ch} position, referring to a given character, null, to refer to the cursor, or @@ -1231,7 +1230,7 @@

      Sizing, scrolling and positioning methods

      optional. When given, it indicates the amount of pixels around the given area that should be made visible as well.
      -
      cm.cursorCoords(where: boolean|{line, ch}, mode: string) → {left, top, bottom}
      +
      cm.cursorCoords(where: boolean|{line, ch}, mode: string) → {left, top, bottom}
      Returns an {left, top, bottom} object containing the coordinates of the cursor position. If mode is "local", they will be @@ -1242,29 +1241,29 @@

      Sizing, scrolling and positioning methods

      end (false) of the selection, or, if a {line, ch} object is given, it specifies the precise position at which you want to measure.
      -
      cm.charCoords(pos: {line, ch}, mode: string) → {left, right, top, bottom}
      +
      cm.charCoords(pos: {line, ch}, mode: string) → {left, right, top, bottom}
      Returns the position and dimensions of an arbitrary character. pos should be a {line, ch} object. This differs from cursorCoords in that it'll give the size of the whole character, rather than just the position that the cursor would have when it would sit at that position.
      -
      cm.coordsChar(object: {left, top}, ?mode: string) → {line, ch}
      +
      cm.coordsChar(object: {left, top}, ?mode: string) → {line, ch}
      Given an {left, top} object, returns the {line, ch} position that corresponds to it. The optional mode parameter determines relative to what the coordinates are interpreted. It may be "window", "page" (the default), or "local".
      -
      cm.defaultTextHeight() → number
      +
      cm.defaultTextHeight() → number
      Returns the line height of the default font for the editor.
      -
      cm.defaultCharWidth() → number
      +
      cm.defaultCharWidth() → number
      Returns the pixel width of an 'x' in the default font for the editor. (Note that for non-monospace fonts, this is mostly useless, and even for monospace fonts, non-ascii characters might have a different width).
      -
      cm.getViewport() → {from: number, to: number}
      +
      cm.getViewport() → {from: number, to: number}
      Returns a {from, to} object indicating the start (inclusive) and end (exclusive) of the currently rendered part of the document. In big documents, when most content is @@ -1273,7 +1272,7 @@

      Sizing, scrolling and positioning methods

      the viewportChange event.
      -
      cm.refresh()
      +
      cm.refresh()
      If your code does something to change the size of the editor element (window resizes are already listened for), or unhides it, you should probably follow up by calling this method to @@ -1288,27 +1287,27 @@

      Mode, state, and token-related methods

      more detailed description of how these work.

      -
      doc.getMode() → object
      +
      doc.getMode() → object
      Gets the mode object for the editor. Note that this is distinct from getOption("mode"), which gives you the mode specification, rather than the resolved, instantiated mode object.
      -
      cm.getTokenAt(pos: {line, ch}) → object
      +
      cm.getTokenAt(pos: {line, ch}) → object
      Retrieves information about the token the current mode found before the given position (a {line, ch} object). The returned object has the following properties:
      -
      start
      The character (on the given line) at which the token starts.
      -
      end
      The character at which the token ends.
      -
      string
      The token's string.
      -
      type
      The token type the mode assigned +
      start
      The character (on the given line) at which the token starts.
      +
      end
      The character at which the token ends.
      +
      string
      The token's string.
      +
      type
      The token type the mode assigned to the token, such as "keyword" or "comment" (may also be null).
      -
      state
      The mode's state at the end of this token.
      +
      state
      The mode's state at the end of this token.
      -
      cm.getStateAfter(?line: integer) → object
      +
      cm.getStateAfter(?line: integer) → object
      Returns the mode's parser state, if any, at the end of the given line number. If no line number is given, the state at the end of the document is returned. This can be useful for storing @@ -1319,10 +1318,10 @@

      Mode, state, and token-related methods

      Miscellaneous methods

      -
      CodeMirror(elt: Element|function(elt: Element), ?config: object)
      +
      CodeMirror(elt: Element|function(elt: Element), ?config: object)
      The constructor. See Basic Usage.
      -
      cm.operation(func: () → any) → any
      +
      cm.operation(func: () → any) → any
      CodeMirror internally buffers changes and only updates its DOM structure after it has finished performing some operation. If you need to perform a lot of operations on a CodeMirror @@ -1332,44 +1331,44 @@

      Miscellaneous methods

      lot faster. The return value from this method will be the return value of your function.
      -
      cm.indentLine(line: integer|LineHandle, ?dir: string)
      +
      cm.indentLine(line: integer|LineHandle, ?dir: string)
      Adjust the indentation of the given line. The second argument (which defaults to "smart") may be one of:
      -
      "prev"
      +
      "prev"
      Base indentation on the indentation of the previous line.
      -
      "smart"
      +
      "smart"
      Use the mode's smart indentation if available, behave like "prev" otherwise.
      -
      "add"
      +
      "add"
      Increase the indentation of the line by one indent unit.
      -
      "subtract"
      +
      "subtract"
      Reduce the indentation of the line.
      -
      doc.posFromIndex(index: integer) → {line, ch}
      +
      doc.posFromIndex(index: integer) → {line, ch}
      Calculates and returns a {line, ch} object for a zero-based index who's value is relative to the start of the editor's text. If the index is out of range of the text then the returned object is clipped to start or end of the text respectively.
      -
      doc.indexFromPos(object: {line, ch}) → integer
      +
      doc.indexFromPos(object: {line, ch}) → integer
      The reverse of posFromIndex.
      -
      cm.focus()
      +
      cm.focus()
      Give the editor focus.
      -
      cm.getInputField() → TextAreaElement
      +
      cm.getInputField() → TextAreaElement
      Returns the hidden textarea used to read input.
      -
      cm.getWrapperElement() → Element
      +
      cm.getWrapperElement() → Element
      Returns the DOM node that represents the editor, and controls its size. Remove this from your tree to delete an editor instance.
      -
      cm.getScrollerElement() → Element
      +
      cm.getScrollerElement() → Element
      Returns the DOM node that is responsible for the scrolling of the editor.
      -
      cm.getGutterElement() → Element
      +
      cm.getGutterElement() → Element
      Fetches the DOM node that contains the editor gutters.
      @@ -1378,7 +1377,7 @@

      Static properties

      several useful properties.

      -
      CodeMirror.version: string
      +
      CodeMirror.version: string
      It contains a string that indicates the version of the library. For releases, this simply contains "major.minor" (for @@ -1387,7 +1386,7 @@

      Static properties

      development snapshots, " +" (space, plus) is added.
      -
      CodeMirror.fromTextArea(textArea: TextAreaElement, ?config: object)
      +
      CodeMirror.fromTextArea(textArea: TextAreaElement, ?config: object)
      The method provides another way to initialize an editor. It takes a textarea DOM node as first argument and an optional configuration @@ -1397,26 +1396,26 @@

      Static properties

      is submitted. A CodeMirror instance created this way has three additional methods:
      -
      cm.save()
      +
      cm.save()
      Copy the content of the editor into the textarea.
      -
      cm.toTextArea()
      +
      cm.toTextArea()
      Remove the editor, and restore the original textarea (with the editor's current content).
      -
      cm.getTextArea() → TextAreaElement
      +
      cm.getTextArea() → TextAreaElement
      Returns the textarea that the instance was based on.
      -
      CodeMirror.defineExtension(name: string, value: any)
      +
      CodeMirror.defineExtension(name: string, value: any)
      If you want to define extra methods in terms of the CodeMirror API, it is possible to use defineExtension. This will cause the given value (usually a method) to be added to all CodeMirror instances created from then on.
      -
      CodeMirror.defineOption(name: string, +
      CodeMirror.defineOption(name: string, default: any, updateFunc: function)
      Similarly, defineOption can be used to define new options for CodeMirror. The updateFunc will be called with the @@ -1424,7 +1423,7 @@

      Static properties

      and whenever the option is modified through setOption.
      -
      CodeMirror.defineInitHook(func: function)
      +
      CodeMirror.defineInitHook(func: function)
      If your extention just needs to run some code whenever a CodeMirror instance is initialized, use CodeMirror.defineInitHook. Give it a function as @@ -1459,24 +1458,20 @@

      Add-ons

      relevant when matching a string. It will cause the search to be case-insensitive. A search cursor has the following methods:
      -
      - findNext() → boolean
      - findPrevious() → boolean -
      +
      findNext() → boolean
      +
      findPrevious() → boolean
      Search forward or backward from the current position. The return value indicates whether a match was found. If matching a regular expression, the return value will be the array returned by the match method, in case you want to extract matched groups.
      -
      - from() → {line, ch}
      - to() → {line, ch} -
      +
      from() → {line, ch}
      +
      to() → {line, ch}
      These are only valid when the last call to findNext or findPrevious did not return false. They will return {line, ch} objects pointing at the start and end of the match.
      -
      replace(text: string)
      +
      replace(text: string)
      Replaces the currently found match with the given text and adjusts the cursor position to reflect the replacement.
      @@ -1684,45 +1679,43 @@

      Writing CodeMirror Modes

      the following API:

      -
      eol() → boolean
      +
      eol() → boolean
      Returns true only if the stream is at the end of the line.
      -
      sol() → boolean
      +
      sol() → boolean
      Returns true only if the stream is at the start of the line.
      -
      peek() → string
      +
      peek() → string
      Returns the next character in the stream without advancing it. Will return an null at the end of the line.
      -
      next() → string
      +
      next() → string
      Returns the next character in the stream and advances it. Also returns null when no more characters are available.
      -
      eat(match: string|regexp|function(char: string) → boolean) → string
      +
      eat(match: string|regexp|function(char: string) → boolean) → string
      match can be a character, a regular expression, or a function that takes a character and returns a boolean. If the next character in the stream 'matches' the given argument, it is consumed and returned. Otherwise, undefined is returned.
      -
      eatWhile(match: string|regexp|function(char: string) → boolean) → boolean
      +
      eatWhile(match: string|regexp|function(char: string) → boolean) → boolean
      Repeatedly calls eat with the given argument, until it fails. Returns true if any characters were eaten.
      -
      eatSpace() → boolean
      +
      eatSpace() → boolean
      Shortcut for eatWhile when matching white-space.
      -
      skipToEnd()
      +
      skipToEnd()
      Moves the position to the end of the line.
      -
      skipTo(ch: string) → boolean
      +
      skipTo(ch: string) → boolean
      Skips to the next occurrence of the given character, if found on the current line (doesn't advance the stream if the character does not occur on the line). Returns true if the character was found.
      -
      - match(pattern: string, ?consume: boolean, ?caseFold: boolean) → boolean
      - match(pattern: regexp, ?consume: boolean) → array<string> -
      +
      match(pattern: string, ?consume: boolean, ?caseFold: boolean) → boolean
      +
      match(pattern: regexp, ?consume: boolean) → array<string>
      Act like a multi-character eat—if consume is true or not given—or a look-ahead that doesn't update the stream @@ -1734,18 +1727,18 @@

      Writing CodeMirror Modes

      returned by match, in case you need to extract matched groups.
      -
      backUp(n: integer)
      +
      backUp(n: integer)
      Backs up the stream n characters. Backing it up further than the start of the current token will cause things to break, so be careful.
      -
      column() → integer
      +
      column() → integer
      Returns the column (taking into account tabs) at which the current token starts.
      -
      indentation() → integer
      +
      indentation() → integer
      Tells you how far the current line has been indented, in spaces. Corrects for tab characters.
      -
      current() → string
      +
      current() → string
      Get the string between the start of the current token and the current stream position.
      From 20cbf22edfefcf06892907120b779a228e0030ab Mon Sep 17 00:00:00 2001 From: Marijn Haverbeke Date: Thu, 4 Apr 2013 11:46:15 +0200 Subject: [PATCH 0970/5780] Revert changes to cursor adjustment during replaceRange Issue #1415 --- lib/codemirror.js | 8 +++----- 1 file changed, 3 insertions(+), 5 deletions(-) diff --git a/lib/codemirror.js b/lib/codemirror.js index ac8b41a0ae..9a96cd6986 100644 --- a/lib/codemirror.js +++ b/lib/codemirror.js @@ -2141,7 +2141,7 @@ window.CodeMirror = (function() { // hint is null, leave the selection alone as much as possible var adjustPos = function(pos) { - if (posEq(change.from, change.to) ? posLess(pos, change.from) : !posLess(change.from, pos)) return pos; + if (posLess(pos, change.from)) return pos; if (!posLess(change.to, pos)) return end; var line = pos.line + change.text.length - (change.to.line - change.from.line) - 1, ch = pos.ch; @@ -2609,7 +2609,6 @@ window.CodeMirror = (function() { if (indentString != curSpaceString) replaceRange(cm.doc, indentString, Pos(n, 0), Pos(n, curSpaceString.length), "+input"); line.stateAfter = null; - return indentString.length; } function changeLine(cm, handle, op) { @@ -2757,7 +2756,7 @@ window.CodeMirror = (function() { if (dir == null) dir = this.options.smartIndent ? "smart" : "prev"; else dir = dir ? "add" : "subtract"; } - if (isLine(this.doc, n)) return indentLine(this, n, dir, aggressive); + if (isLine(this.doc, n)) indentLine(this, n, dir, aggressive); }), indentSelection: operation(null, function(how) { var sel = this.doc.sel; @@ -3297,8 +3296,7 @@ window.CodeMirror = (function() { newlineAndIndent: function(cm) { operation(cm, function() { cm.replaceSelection("\n", "end", "+input"); - var line = cm.getCursor().line, indented = cm.indentLine(line, null, true); - if (indented) cm.setCursor(line, indented); + cm.indentLine(cm.getCursor().line, null, true); })(); }, toggleOverwrite: function(cm) {cm.toggleOverwrite();} From c59f87245d0ce230713646c7857b0a9497c287fd Mon Sep 17 00:00:00 2001 From: lynschinzer Date: Thu, 4 Apr 2013 20:28:40 +0200 Subject: [PATCH 0971/5780] Add Midnight theme --- demo/theme.html | 2 ++ theme/midnight.css | 52 ++++++++++++++++++++++++++++++++++++++++++++++ 2 files changed, 54 insertions(+) create mode 100644 theme/midnight.css diff --git a/demo/theme.html b/demo/theme.html index 046d1fc3e1..0f979f73aa 100644 --- a/demo/theme.html +++ b/demo/theme.html @@ -21,6 +21,7 @@ + @@ -54,6 +55,7 @@

      CodeMirror: Theme demo

      + diff --git a/theme/midnight.css b/theme/midnight.css new file mode 100644 index 0000000000..e567625c6c --- /dev/null +++ b/theme/midnight.css @@ -0,0 +1,52 @@ +/* Based on the theme at http://bonsaiden.github.com/JavaScript-Garden */ + +/**/ +.breakpoints {width: .8em;} +.breakpoint { color: #822; } + +/**/ +span.CodeMirror-matchhighlight { background: #494949 } +.CodeMirror-focused span.CodeMirror-matchhighlight { background: #314D67; !important } + +/**/ +.activeline {background: #253540 !important;} + +.cm-s-midnight.CodeMirror { + background: #0F192A; + color: #D1EDFF; +} + +.CodeMirror {border-top: 1px solid black; border-bottom: 1px solid black;} + +.cm-s-midnight div.CodeMirror-selected {background: #314D67 !important;} +.cm-s-midnight .CodeMirror-gutters {background: #0F192A; border-right: 1px solid;} +.cm-s-midnight .CodeMirror-linenumber {color: #D0D0D0;} +.cm-s-midnight .CodeMirror-cursor { + border-left: 1px solid #F8F8F0 !important; +} + +.cm-s-midnight span.cm-comment {color: #428BDD;} +.cm-s-midnight span.cm-atom {color: #AE81FF;} +.cm-s-midnight span.cm-number {color: #D1EDFF;} + +.cm-s-midnight span.cm-property, .cm-s-tropicaleve span.cm-attribute {color: #A6E22E;} +.cm-s-midnight span.cm-keyword {color: #E83737;} +.cm-s-midnight span.cm-string {color: #1DC116;} + +.cm-s-midnight span.cm-variable {color: #FFAA3E;} +.cm-s-midnight span.cm-variable-2 {color: #FFAA3E;} +.cm-s-midnight span.cm-def {color: #4DD;} +.cm-s-midnight span.cm-error {background: #F92672; color: #F8F8F0;} +.cm-s-midnight span.cm-bracket {color: #D1EDFF;} +.cm-s-midnight span.cm-tag {color: #008;} +.cm-s-midnight span.cm-link {color: #AE81FF;} + +.cm-s-midnight .CodeMirror-matchingbracket { + text-decoration: underline; + color: white !important; +} + +.typ { color: #FFAA3E; } +.atn { color: #606; } +.atv { color: #080; } +.dec { color: #606; } From d1942cc0ab010c34f683fd14c193418646fdedbc Mon Sep 17 00:00:00 2001 From: Liam Newman Date: Thu, 4 Apr 2013 12:58:39 -0700 Subject: [PATCH 0972/5780] Add npm module badge Simple badge for current npm module --- README.md | 4 +++- 1 file changed, 3 insertions(+), 1 deletion(-) diff --git a/README.md b/README.md index 976584e3b9..61f6b64525 100644 --- a/README.md +++ b/README.md @@ -1,4 +1,6 @@ -# CodeMirror [![Build Status](https://secure.travis-ci.org/marijnh/CodeMirror.png?branch=master)](http://travis-ci.org/marijnh/CodeMirror) +# CodeMirror +[![Build Status](https://secure.travis-ci.org/marijnh/CodeMirror.png?branch=master)](http://travis-ci.org/marijnh/CodeMirror) +[![NPM version](https://badge.fury.io/js/codemirror.png)](http://badge.fury.io/js/codemirror) CodeMirror is a JavaScript component that provides a code editor in the browser. When a mode is available for the language you are coding From 1520c653e9b68f5991c4d22a83d8534412f983e8 Mon Sep 17 00:00:00 2001 From: Ian Wehrman Date: Thu, 4 Apr 2013 16:58:02 -0700 Subject: [PATCH 0973/5780] Prevent the latter part of hyphenated terms from being identified as attributes. --- mode/shell/shell.js | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/mode/shell/shell.js b/mode/shell/shell.js index ed09284f29..abfd214454 100644 --- a/mode/shell/shell.js +++ b/mode/shell/shell.js @@ -57,7 +57,7 @@ CodeMirror.defineMode('shell', function() { return 'number'; } } - stream.eatWhile(/\w/); + stream.eatWhile(/[\w-]/); var cur = stream.current(); if (stream.peek() === '=' && /\w+/.test(cur)) return 'def'; return words.hasOwnProperty(cur) ? words[cur] : null; From 7b8f79618da67ceae5e1f32f43da86ccce8eb398 Mon Sep 17 00:00:00 2001 From: Marijn Haverbeke Date: Sun, 7 Apr 2013 13:07:55 +0200 Subject: [PATCH 0974/5780] [show-hint addon] Support onSelect and onClose callbacks --- addon/hint/show-hint.js | 8 ++++++-- 1 file changed, 6 insertions(+), 2 deletions(-) diff --git a/addon/hint/show-hint.js b/addon/hint/show-hint.js index b3be8ef5ea..d132aa75d3 100644 --- a/addon/hint/show-hint.js +++ b/addon/hint/show-hint.js @@ -29,6 +29,7 @@ CodeMirror.showHint = function(cm, getHints, options) { // When there is only one completion, use it directly. if (!continued && options.completeSingle !== false && completions.length == 1) { pickCompletion(cm, data, completions[0]); + if (data.onClose) data.onClose(); return true; } @@ -85,6 +86,7 @@ CodeMirror.showHint = function(cm, getHints, options) { hints.scrollTop = node.offsetTop - 3; else if (node.offsetTop + node.offsetHeight > hints.scrollTop + hints.clientHeight) hints.scrollTop = node.offsetTop + node.offsetHeight - hints.clientHeight + 3; + if (data.onSelect) data.onSelect(completions[selectedHint]); } function screenAmount() { @@ -138,7 +140,7 @@ CodeMirror.showHint = function(cm, getHints, options) { }); var done = false, once; - function close() { + function close(willContinue) { if (done) return; done = true; clearTimeout(once); @@ -148,6 +150,7 @@ CodeMirror.showHint = function(cm, getHints, options) { cm.off("blur", onBlur); cm.off("focus", onFocus); cm.off("scroll", onScroll); + if (!willContinue && data.onClose) data.onClose(); } function pick() { pickCompletion(cm, data, completions[selectedHint]); @@ -163,8 +166,9 @@ CodeMirror.showHint = function(cm, getHints, options) { (pos.ch && closeOn.test(line.charAt(pos.ch - 1)))) close(); else - once = setTimeout(function(){close(); continued = true; startHinting();}, 70); + once = setTimeout(function(){close(true); continued = true; startHinting();}, 70); } + if (data.onSelect) data.onSelect(completions[0]); return true; } From cf52a93edfc15519d1cb4f488b473fec49a1f5ee Mon Sep 17 00:00:00 2001 From: lynschinzer Date: Fri, 5 Apr 2013 19:29:58 +0200 Subject: [PATCH 0975/5780] [vim keymap] Add ; , commands for reapting f t F T motions --- keymap/vim.js | 31 ++++++++++++++++++++++++++- test/vim_test.js | 56 ++++++++++++++++++++++++++++++++++++++++++++++++ 2 files changed, 86 insertions(+), 1 deletion(-) diff --git a/keymap/vim.js b/keymap/vim.js index ba0e0942c1..26c3c7d4b9 100644 --- a/keymap/vim.js +++ b/keymap/vim.js @@ -173,6 +173,10 @@ { keys: ['T', 'character'], type: 'motion', motion: 'moveTillCharacter', motionArgs: { forward: false }}, + { keys: [';'], type: 'motion', motion: 'repeatLastCharacterSearch', + motionArgs: { forward: true }}, + { keys: [','], type: 'motion', motion: 'repeatLastCharacterSearch', + motionArgs: { forward: false }}, { keys: ['\'', 'character'], type: 'motion', motion: 'goToMark' }, { keys: ['`', 'character'], type: 'motion', motion: 'goToMark' }, { keys: [']', '`',], type: 'motion', motion: 'jumpToMark', motionArgs: { forward: true } }, @@ -347,6 +351,8 @@ searchQuery: null, // Whether we are searching backwards. searchIsReversed: false, + // Recording latest f, t, F or T motion command. + lastChararacterSearch: {increment:0, forward:true, selectedCharacter:''}, registerController: new RegisterController({}) }; } @@ -1175,13 +1181,15 @@ var repeat = motionArgs.repeat; var curEnd = moveToCharacter(cm, repeat, motionArgs.forward, motionArgs.selectedCharacter); - if(!curEnd)return cm.getCursor(); var increment = motionArgs.forward ? -1 : 1; + recordLastCharacterSearch(increment, motionArgs); + if(!curEnd)return cm.getCursor(); curEnd.ch += increment; return curEnd; }, moveToCharacter: function(cm, motionArgs) { var repeat = motionArgs.repeat; + recordLastCharacterSearch(0, motionArgs); return moveToCharacter(cm, repeat, motionArgs.forward, motionArgs.selectedCharacter) || cm.getCursor(); }, @@ -1245,6 +1253,20 @@ var start = tmp.start; var end = tmp.end; return [start, end]; + }, + repeatLastCharacterSearch: function(cm, motionArgs) { + var lastSearch = getVimGlobalState().lastChararacterSearch; + var repeat = motionArgs.repeat; + var forward = motionArgs.forward === lastSearch.forward; + var increment = (lastSearch.increment ? 1 : 0) * (forward ? -1 : 1); + cm.moveH(-increment, 'char'); + var curEnd = moveToCharacter(cm, repeat, forward, lastSearch.selectedCharacter); + if (!curEnd) { + cm.moveH(increment, 'char') + return cm.getCursor(); + } + curEnd.ch += increment; + return curEnd; } }; @@ -1788,6 +1810,13 @@ end: { line: cur.line, ch: wordEnd }}; } + function recordLastCharacterSearch(increment, args) { + var vimGlobalState = getVimGlobalState(); + vimGlobalState.lastChararacterSearch.increment = increment; + vimGlobalState.lastChararacterSearch.forward = args.forward; + vimGlobalState.lastChararacterSearch.selectedCharacter = args.selectedCharacter; + } + /* * Returns the boundaries of the next word. If the cursor in the middle of * the word, then returns the boundaries of the current word, starting at diff --git a/test/vim_test.js b/test/vim_test.js index bc04b7fc70..ea3ea4d55b 100644 --- a/test/vim_test.js +++ b/test/vim_test.js @@ -1184,6 +1184,62 @@ testVim('._repeat', function(cm, vim, helpers) { helpers.doKeys('3', '.'); eq('6', cm.getValue()); }, { value: '1 2 3 4 5 6'}); +testVim('f;', function(cm, vim, helpers) { + cm.setCursor(0, 0); + helpers.doKeys('f', 'x'); + helpers.doKeys(';'); + helpers.doKeys('2', ';'); + eq(9, cm.getCursor().ch); +}, { value: '01x3xx678x'}); +testVim('F;', function(cm, vim, helpers) { + cm.setCursor(0, 8); + helpers.doKeys('F', 'x'); + helpers.doKeys(';'); + helpers.doKeys('2', ';'); + eq(2, cm.getCursor().ch); +}, { value: '01x3xx6x8x'}); +testVim('t;', function(cm, vim, helpers) { + cm.setCursor(0, 0); + helpers.doKeys('t', 'x'); + helpers.doKeys(';'); + helpers.doKeys('2', ';'); + eq(8, cm.getCursor().ch); +}, { value: '01x3xx678x'}); +testVim('T;', function(cm, vim, helpers) { + cm.setCursor(0, 9); + helpers.doKeys('T', 'x'); + helpers.doKeys(';'); + helpers.doKeys('2', ';'); + eq(2, cm.getCursor().ch); +}, { value: '0xx3xx678x'}); +testVim('f,', function(cm, vim, helpers) { + cm.setCursor(0, 6); + helpers.doKeys('f', 'x'); + helpers.doKeys(','); + helpers.doKeys('2', ','); + eq(2, cm.getCursor().ch); +}, { value: '01x3xx678x'}); +testVim('F,', function(cm, vim, helpers) { + cm.setCursor(0, 3); + helpers.doKeys('F', 'x'); + helpers.doKeys(','); + helpers.doKeys('2', ','); + eq(9, cm.getCursor().ch); +}, { value: '01x3xx678x'}); +testVim('t,', function(cm, vim, helpers) { + cm.setCursor(0, 6); + helpers.doKeys('t', 'x'); + helpers.doKeys(','); + helpers.doKeys('2', ','); + eq(3, cm.getCursor().ch); +}, { value: '01x3xx678x'}); +testVim('T,', function(cm, vim, helpers) { + cm.setCursor(0, 4); + helpers.doKeys('T', 'x'); + helpers.doKeys(','); + helpers.doKeys('2', ','); + eq(8, cm.getCursor().ch); +}, { value: '01x3xx67xx'}); // Ex mode tests testVim('ex_go_to_line', function(cm, vim, helpers) { From 3bc89192dd221818f0c9e819876023d324b4758e Mon Sep 17 00:00:00 2001 From: Golevka Date: Sat, 6 Apr 2013 21:31:59 +0800 Subject: [PATCH 0976/5780] [closetag addon] close tags with attributes containing slashes (such as path or text/javascript) --- addon/edit/closetag.js | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-) diff --git a/addon/edit/closetag.js b/addon/edit/closetag.js index 54fa195463..454dfea5e6 100644 --- a/addon/edit/closetag.js +++ b/addon/edit/closetag.js @@ -55,7 +55,8 @@ if (tok.end > pos.ch) tagName = tagName.slice(0, tagName.length - tok.end + pos.ch); var lowerTagName = tagName.toLowerCase(); // Don't process the '>' at the end of an end-tag or self-closing tag - if (tok.type == "tag" && state.type == "closeTag" || tok.string.indexOf("/") > -1 || + if (tok.type == "tag" && state.type == "closeTag" || + tok.string.indexOf("/") == (tok.string.length - 1) || // match something like dontCloseTags && indexOf(dontCloseTags, lowerTagName) > -1) return CodeMirror.Pass; From 02ea26c0d0e4cf29ec05490a80d6ee3f79f5c3ef Mon Sep 17 00:00:00 2001 From: Marijn Haverbeke Date: Sun, 7 Apr 2013 13:18:42 +0200 Subject: [PATCH 0977/5780] [javascript mode] Tokenize leading whitespace in block comments as comment Closes #1431 --- mode/javascript/javascript.js | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/mode/javascript/javascript.js b/mode/javascript/javascript.js index 89f1220369..cf3a5f003c 100644 --- a/mode/javascript/javascript.js +++ b/mode/javascript/javascript.js @@ -402,7 +402,7 @@ CodeMirror.defineMode("javascript", function(config, parserConfig) { state.lexical.align = false; state.indented = stream.indentation(); } - if (stream.eatSpace()) return null; + if (state.tokenize != jsTokenComment && stream.eatSpace()) return null; var style = state.tokenize(stream, state); if (type == "comment") return style; state.lastType = type; From fcf6f33052dcaf98def7318a8a6d20365ed88912 Mon Sep 17 00:00:00 2001 From: Marijn Haverbeke Date: Mon, 8 Apr 2013 11:08:17 +0200 Subject: [PATCH 0978/5780] [javascript mode] Properly handle commas Closes #1433 --- mode/javascript/javascript.js | 37 +++++++++++++++++++++++------------ 1 file changed, 24 insertions(+), 13 deletions(-) diff --git a/mode/javascript/javascript.js b/mode/javascript/javascript.js index cf3a5f003c..229fb2dc0a 100644 --- a/mode/javascript/javascript.js +++ b/mode/javascript/javascript.js @@ -269,13 +269,19 @@ CodeMirror.defineMode("javascript", function(config, parserConfig) { return pass(pushlex("stat"), expression, expect(";"), poplex); } function expression(type) { - if (atomicTypes.hasOwnProperty(type)) return cont(maybeoperator); + return expressionInner(type, maybeoperatorComma); + } + function expressionNoComma(type) { + return expressionInner(type, maybeoperatorNoComma); + } + function expressionInner(type, maybeop) { + if (atomicTypes.hasOwnProperty(type)) return cont(maybeop); if (type == "function") return cont(functiondef); if (type == "keyword c") return cont(maybeexpression); - if (type == "(") return cont(pushlex(")"), maybeexpression, expect(")"), poplex, maybeoperator); + if (type == "(") return cont(pushlex(")"), maybeexpression, expect(")"), poplex, maybeop); if (type == "operator") return cont(expression); - if (type == "[") return cont(pushlex("]"), commasep(expression, "]"), poplex, maybeoperator); - if (type == "{") return cont(pushlex("}"), commasep(objprop, "}"), poplex, maybeoperator); + if (type == "[") return cont(pushlex("]"), commasep(expressionNoComma, "]"), poplex, maybeop); + if (type == "{") return cont(pushlex("}"), commasep(objprop, "}"), poplex, maybeop); return cont(); } function maybeexpression(type) { @@ -283,20 +289,25 @@ CodeMirror.defineMode("javascript", function(config, parserConfig) { return pass(expression); } - function maybeoperator(type, value) { + function maybeoperatorComma(type, value) { + if (type == ",") return cont(expression); + return maybeoperatorNoComma(type, value, maybeoperatorComma); + } + function maybeoperatorNoComma(type, value, me) { + if (!me) me = maybeoperatorNoComma; if (type == "operator") { - if (/\+\+|--/.test(value)) return cont(maybeoperator); + if (/\+\+|--/.test(value)) return cont(me); if (value == "?") return cont(expression, expect(":"), expression); return cont(expression); } if (type == ";") return; - if (type == "(") return cont(pushlex(")", "call"), commasep(expression, ")"), poplex, maybeoperator); - if (type == ".") return cont(property, maybeoperator); - if (type == "[") return cont(pushlex("]"), expression, expect("]"), poplex, maybeoperator); + if (type == "(") return cont(pushlex(")", "call"), commasep(expressionNoComma, ")"), poplex, me); + if (type == ".") return cont(property, me); + if (type == "[") return cont(pushlex("]"), expression, expect("]"), poplex, me); } function maybelabel(type) { if (type == ":") return cont(poplex, statement); - return pass(maybeoperator, expect(";"), poplex); + return pass(maybeoperatorComma, expect(";"), poplex); } function property(type) { if (type == "variable") {cx.marked = "property"; return cont();} @@ -308,7 +319,7 @@ CodeMirror.defineMode("javascript", function(config, parserConfig) { } else if (type == "number" || type == "string") { cx.marked = type + " property"; } - if (atomicTypes.hasOwnProperty(type)) return cont(expect(":"), expression); + if (atomicTypes.hasOwnProperty(type)) return cont(expect(":"), expressionNoComma); } function getterSetter(type) { if (type == ":") return cont(expression); @@ -351,7 +362,7 @@ CodeMirror.defineMode("javascript", function(config, parserConfig) { return pass(); } function vardef2(type, value) { - if (value == "=") return cont(expression, vardef2); + if (value == "=") return cont(expressionNoComma, vardef2); if (type == ",") return cont(vardef1); } function forspec1(type) { @@ -362,7 +373,7 @@ CodeMirror.defineMode("javascript", function(config, parserConfig) { } function formaybein(_type, value) { if (value == "in") return cont(expression); - return cont(maybeoperator, forspec2); + return cont(maybeoperatorComma, forspec2); } function forspec2(type, value) { if (type == ";") return cont(forspec3); From 6e44713cdb6966cde5b76e783697b3ec53a6bc32 Mon Sep 17 00:00:00 2001 From: Marijn Haverbeke Date: Mon, 8 Apr 2013 11:54:48 +0200 Subject: [PATCH 0979/5780] Fix distance-tracking in coordsChar on bidi text --- lib/codemirror.js | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/lib/codemirror.js b/lib/codemirror.js index 9a96cd6986..3ef882832d 100644 --- a/lib/codemirror.js +++ b/lib/codemirror.js @@ -1222,8 +1222,8 @@ window.CodeMirror = (function() { for (var i = 0; i < step; ++i) middle = moveVisually(lineObj, middle, 1); } var middleX = getX(middle); - if (middleX > x) {to = middle; toX = middleX; if (toOutside = wrongLine) toX += 1000; dist -= step;} - else {from = middle; fromX = middleX; fromOutside = wrongLine; dist = step;} + if (middleX > x) {to = middle; toX = middleX; if (toOutside = wrongLine) toX += 1000; dist = step;} + else {from = middle; fromX = middleX; fromOutside = wrongLine; dist -= step;} } } From b6d456db96f7e671980bc68916ddd904b384dc2b Mon Sep 17 00:00:00 2001 From: Marijn Haverbeke Date: Mon, 8 Apr 2013 12:05:08 +0200 Subject: [PATCH 0980/5780] Add historyEventDelay option --- doc/manual.html | 5 +++++ lib/codemirror.js | 4 +++- 2 files changed, 8 insertions(+), 1 deletion(-) diff --git a/doc/manual.html b/doc/manual.html index 45ea1a9225..295ae003cb 100644 --- a/doc/manual.html +++ b/doc/manual.html @@ -245,6 +245,11 @@

      Configuration

      The maximum number of undo levels that the editor stores. Defaults to 40.
      +
      historyEventDelay: integer
      +
      The period of inactivity (in milliseconds) that will cause a + new history event to be started when typing or deleting. + Defaults to 500.
      +
      tabindex: integer
      The tab index to assign to the editor. If not given, no tab index diff --git a/lib/codemirror.js b/lib/codemirror.js index 3ef882832d..6d15dbdc4a 100644 --- a/lib/codemirror.js +++ b/lib/codemirror.js @@ -3111,6 +3111,7 @@ window.CodeMirror = (function() { option("flattenSpans", true); option("pollInterval", 100); option("undoDepth", 40, function(cm, val){cm.doc.history.undoDepth = val;}); + option("historyEventDelay", 500); option("viewportMargin", 10, function(cm){cm.refresh();}, true); option("maxHighlightLength", 10000, function(cm){loadMode(cm); cm.refresh();}, true); option("moveInputWithCursor", true, function(cm, val) { @@ -4861,7 +4862,8 @@ window.CodeMirror = (function() { if (cur && (hist.lastOp == opId || hist.lastOrigin == change.origin && change.origin && - ((change.origin.charAt(0) == "+" && hist.lastTime > time - 600) || change.origin.charAt(0) == "*"))) { + ((change.origin.charAt(0) == "+" && doc.cm && hist.lastTime > time - doc.cm.options.historyEventDelay) || + change.origin.charAt(0) == "*"))) { // Merge this change into the last event var last = lst(cur.changes); if (posEq(change.from, change.to) && posEq(change.from, last.to)) { From 4c53964e532e271cbd3b0461f56d2eec4a406578 Mon Sep 17 00:00:00 2001 From: Marijn Haverbeke Date: Mon, 8 Apr 2013 12:13:25 +0200 Subject: [PATCH 0981/5780] [javascript mode] Correct indentation for nested, brace-less if/else statements Closes #1436 --- mode/javascript/javascript.js | 12 +++++++++++- 1 file changed, 11 insertions(+), 1 deletion(-) diff --git a/mode/javascript/javascript.js b/mode/javascript/javascript.js index 229fb2dc0a..f501174ee0 100644 --- a/mode/javascript/javascript.js +++ b/mode/javascript/javascript.js @@ -13,7 +13,7 @@ CodeMirror.defineMode("javascript", function(config, parserConfig) { var operator = kw("operator"), atom = {type: "atom", style: "atom"}; var jsKeywords = { - "if": A, "while": A, "with": A, "else": B, "do": B, "try": B, "finally": B, + "if": kw("if"), "while": A, "with": A, "else": B, "do": B, "try": B, "finally": B, "return": C, "break": C, "continue": C, "new": C, "delete": C, "throw": C, "var": kw("var"), "const": kw("var"), "let": kw("var"), "function": kw("function"), "catch": kw("catch"), @@ -256,6 +256,7 @@ CodeMirror.defineMode("javascript", function(config, parserConfig) { if (type == "keyword b") return cont(pushlex("form"), statement, poplex); if (type == "{") return cont(pushlex("}"), block, poplex); if (type == ";") return cont(); + if (type == "if") return cont(pushlex("form"), expression, statement, poplex, maybeelse(cx.state.indented)); if (type == "function") return cont(functiondef); if (type == "for") return cont(pushlex("form"), expect("("), pushlex(")"), forspec1, expect(")"), poplex, statement, poplex); @@ -365,6 +366,15 @@ CodeMirror.defineMode("javascript", function(config, parserConfig) { if (value == "=") return cont(expressionNoComma, vardef2); if (type == ",") return cont(vardef1); } + function maybeelse(indent) { + return function(type, value) { + if (type == "keyword b" && value == "else") { + cx.state.lexical = new JSLexical(indent, 0, "form", null, cx.state.lexical); + return cont(expression, statement, poplex); + } + return pass(); + }; + } function forspec1(type) { if (type == "var") return cont(vardef1, expect(";"), forspec2); if (type == ";") return cont(forspec2); From f35feec32c45f79ec9be111124e068190144823a Mon Sep 17 00:00:00 2001 From: Marijn Haverbeke Date: Tue, 9 Apr 2013 08:15:30 +0200 Subject: [PATCH 0982/5780] [show-hint addon] Clean up code that handles custom keymap Issue #1440 --- addon/hint/show-hint.js | 15 +++++++++------ 1 file changed, 9 insertions(+), 6 deletions(-) diff --git a/addon/hint/show-hint.js b/addon/hint/show-hint.js index d132aa75d3..0c29e793ab 100644 --- a/addon/hint/show-hint.js +++ b/addon/hint/show-hint.js @@ -93,7 +93,7 @@ CodeMirror.showHint = function(cm, getHints, options) { return Math.floor(hints.clientHeight / hints.firstChild.offsetHeight) || 1; } - var ourMap = { + var ourMap, baseMap = { Up: function() {changeActive(selectedHint - 1);}, Down: function() {changeActive(selectedHint + 1);}, PageUp: function() {changeActive(selectedHint - screenAmount());}, @@ -104,11 +104,14 @@ CodeMirror.showHint = function(cm, getHints, options) { Tab: pick, Esc: close }; - if (options.customKeys) for (var key in options.customKeys) if (options.customKeys.hasOwnProperty(key)) { - var val = options.customKeys[key]; - if (/^(Up|Down|Enter|Esc)$/.test(key)) val = ourMap[val]; - ourMap[key] = val; - } + if (options.customKeys) { + ourMap = {}; + for (var key in options.customKeys) if (options.customKeys.hasOwnProperty(key)) { + var val = options.customKeys[key]; + if (baseMap.hasOwnProperty(val)) val = baseMap[val]; + ourMap[key] = val; + } + } else ourMap = baseMap; cm.addKeyMap(ourMap); cm.on("cursorActivity", cursorActivity); From 8dae6abc0d5a188e2954679b85a969cd8fa1edb4 Mon Sep 17 00:00:00 2001 From: Marijn Haverbeke Date: Tue, 9 Apr 2013 08:20:58 +0200 Subject: [PATCH 0983/5780] [show-hint addon] Make sure onClose is actually called Issue #1425 --- addon/hint/show-hint.js | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/addon/hint/show-hint.js b/addon/hint/show-hint.js index 0c29e793ab..55c58b3b2b 100644 --- a/addon/hint/show-hint.js +++ b/addon/hint/show-hint.js @@ -153,7 +153,7 @@ CodeMirror.showHint = function(cm, getHints, options) { cm.off("blur", onBlur); cm.off("focus", onFocus); cm.off("scroll", onScroll); - if (!willContinue && data.onClose) data.onClose(); + if (willContinue !== true && data.onClose) data.onClose(); } function pick() { pickCompletion(cm, data, completions[selectedHint]); From 71cd784305159dddbd4184792b6238d0f8a2bd1e Mon Sep 17 00:00:00 2001 From: Marijn Haverbeke Date: Tue, 9 Apr 2013 08:25:11 +0200 Subject: [PATCH 0984/5780] [show-hint addon] Pass option node as second argument to onSelect --- addon/hint/show-hint.js | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/addon/hint/show-hint.js b/addon/hint/show-hint.js index 55c58b3b2b..cbaa043571 100644 --- a/addon/hint/show-hint.js +++ b/addon/hint/show-hint.js @@ -86,7 +86,7 @@ CodeMirror.showHint = function(cm, getHints, options) { hints.scrollTop = node.offsetTop - 3; else if (node.offsetTop + node.offsetHeight > hints.scrollTop + hints.clientHeight) hints.scrollTop = node.offsetTop + node.offsetHeight - hints.clientHeight + 3; - if (data.onSelect) data.onSelect(completions[selectedHint]); + if (data.onSelect) data.onSelect(completions[selectedHint], node); } function screenAmount() { @@ -171,7 +171,7 @@ CodeMirror.showHint = function(cm, getHints, options) { else once = setTimeout(function(){close(true); continued = true; startHinting();}, 70); } - if (data.onSelect) data.onSelect(completions[0]); + if (data.onSelect) data.onSelect(completions[0], hints.firstChild); return true; } From ca2551bd077e5e921871fd936d6bde18a3b7e95b Mon Sep 17 00:00:00 2001 From: Marijn Haverbeke Date: Tue, 9 Apr 2013 09:01:48 +0200 Subject: [PATCH 0985/5780] [javascript mode] Don't expect an expression after else Issue #1441 --- mode/javascript/javascript.js | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/mode/javascript/javascript.js b/mode/javascript/javascript.js index f501174ee0..c138b6fc63 100644 --- a/mode/javascript/javascript.js +++ b/mode/javascript/javascript.js @@ -370,7 +370,7 @@ CodeMirror.defineMode("javascript", function(config, parserConfig) { return function(type, value) { if (type == "keyword b" && value == "else") { cx.state.lexical = new JSLexical(indent, 0, "form", null, cx.state.lexical); - return cont(expression, statement, poplex); + return cont(statement, poplex); } return pass(); }; From 3d046a4bae8382858ba046e9e4a899bca815a6f6 Mon Sep 17 00:00:00 2001 From: Marijn Haverbeke Date: Tue, 9 Apr 2013 09:06:50 +0200 Subject: [PATCH 0986/5780] Handle dblclick events specifically on IE Apparently, canceling the initial mousedown will stop the second mousedown from firing, but still fire the dblclick event. --- lib/codemirror.js | 11 ++++++++++- 1 file changed, 10 insertions(+), 1 deletion(-) diff --git a/lib/codemirror.js b/lib/codemirror.js index 6d15dbdc4a..c4fd1af1d1 100644 --- a/lib/codemirror.js +++ b/lib/codemirror.js @@ -1458,7 +1458,16 @@ window.CodeMirror = (function() { function registerEventHandlers(cm) { var d = cm.display; on(d.scroller, "mousedown", operation(cm, onMouseDown)); - on(d.scroller, "dblclick", operation(cm, e_preventDefault)); + if (ie) + on(d.scroller, "dblclick", operation(cm, function(e) { + var pos = posFromMouse(cm, e); + if (!pos || clickInGutter(cm, e) || eventInWidget(cm.display, e)) return; + e_preventDefault(e); + var word = findWordAt(getLine(cm.doc, pos.line).text, pos); + extendSelection(cm.doc, word.from, word.to); + })); + else + on(d.scroller, "dblclick", e_preventDefault); on(d.lineSpace, "selectstart", function(e) { if (!eventInWidget(d, e)) e_preventDefault(e); }); From e3f7c466557955e54298a56d61897c8baac7d2ac Mon Sep 17 00:00:00 2001 From: Marijn Haverbeke Date: Tue, 9 Apr 2013 15:56:37 +0200 Subject: [PATCH 0987/5780] Add Firepad to real-world uses --- doc/realworld.html | 1 + 1 file changed, 1 insertion(+) diff --git a/doc/realworld.html b/doc/realworld.html index 6bebfebc18..cf90ea2555 100644 --- a/doc/realworld.html +++ b/doc/realworld.html @@ -48,6 +48,7 @@

      { } CodeMi
    • Fastfig (online computation/math tool)
    • Farabi (modern Perl IDE)
    • FathomJS integration (slides with editors, again)
    • +
    • Firepad (collaborative text editor)
    • Go language tour
    • GitHub's Android app
    • Google Apps Script
    • From 90983502df989731608647c457dd170add29b78d Mon Sep 17 00:00:00 2001 From: Marijn Haverbeke Date: Tue, 9 Apr 2013 17:41:04 +0200 Subject: [PATCH 0988/5780] Add defineDocExtension function --- doc/manual.html | 15 ++++++++++----- lib/codemirror.js | 4 +++- 2 files changed, 13 insertions(+), 6 deletions(-) diff --git a/doc/manual.html b/doc/manual.html index 295ae003cb..49dd5883d5 100644 --- a/doc/manual.html +++ b/doc/manual.html @@ -1414,11 +1414,16 @@

      Static properties

      CodeMirror.defineExtension(name: string, value: any)
      -
      If you want to define extra methods in terms - of the CodeMirror API, it is possible to - use defineExtension. This - will cause the given value (usually a method) to be added to all - CodeMirror instances created from then on.
      +
      If you want to define extra methods in terms of the + CodeMirror API, it is possible to + use defineExtension. This will cause the given + value (usually a method) to be added to all CodeMirror instances + created from then on.
      + +
      CodeMirror.defineDocExtension(name: string, value: any)
      +
      Like defineExtension, + but the method will be added to the interface + for Doc objects instead.
      CodeMirror.defineOption(name: string, default: any, updateFunc: function)
      diff --git a/lib/codemirror.js b/lib/codemirror.js index c4fd1af1d1..bf5c9e69c6 100644 --- a/lib/codemirror.js +++ b/lib/codemirror.js @@ -3192,7 +3192,9 @@ window.CodeMirror = (function() { CodeMirror.defineExtension = function(name, func) { CodeMirror.prototype[name] = func; }; - + CodeMirror.defineDocExtension = function(name, func) { + Doc.prototype[name] = func; + } CodeMirror.defineOption = option; var initHooks = []; From 8d14e586f448d9e50e981472886bfa61001f06ff Mon Sep 17 00:00:00 2001 From: Marijn Haverbeke Date: Tue, 9 Apr 2013 19:00:14 +0200 Subject: [PATCH 0989/5780] [searchcursor addon] Make search cursors work on docs --- addon/search/searchcursor.js | 29 ++++++++++++++++------------- 1 file changed, 16 insertions(+), 13 deletions(-) diff --git a/addon/search/searchcursor.js b/addon/search/searchcursor.js index fd134636e3..a590e84443 100644 --- a/addon/search/searchcursor.js +++ b/addon/search/searchcursor.js @@ -1,11 +1,11 @@ (function(){ var Pos = CodeMirror.Pos; - function SearchCursor(cm, query, pos, caseFold) { - this.atOccurrence = false; this.cm = cm; + function SearchCursor(doc, query, pos, caseFold) { + this.atOccurrence = false; this.doc = doc; if (caseFold == null && typeof query == "string") caseFold = false; - pos = pos ? cm.clipPos(pos) : Pos(0, 0); + pos = pos ? doc.clipPos(pos) : Pos(0, 0); this.pos = {from: pos, to: pos}; // The matches method is filled in based on the type of query. @@ -17,7 +17,7 @@ this.matches = function(reverse, pos) { if (reverse) { query.lastIndex = 0; - var line = cm.getLine(pos.line).slice(0, pos.ch), cutOff = 0, match, start; + var line = doc.getLine(pos.line).slice(0, pos.ch), cutOff = 0, match, start; for (;;) { query.lastIndex = cutOff; var newMatch = query.exec(line); @@ -28,7 +28,7 @@ } } else { query.lastIndex = pos.ch; - var line = cm.getLine(pos.line), match = query.exec(line), + var line = doc.getLine(pos.line), match = query.exec(line), start = match && match.index; } if (match && match[0]) @@ -48,7 +48,7 @@ this.matches = function() {}; } else { this.matches = function(reverse, pos) { - var line = fold(cm.getLine(pos.line)), len = query.length, match; + var line = fold(doc.getLine(pos.line)), len = query.length, match; if (reverse ? (pos.ch >= len && (match = line.lastIndexOf(query, pos.ch - len)) != -1) : (match = line.indexOf(query, pos.ch)) != -1) return {from: Pos(pos.line, match), @@ -57,14 +57,14 @@ } } else { this.matches = function(reverse, pos) { - var ln = pos.line, idx = (reverse ? target.length - 1 : 0), match = target[idx], line = fold(cm.getLine(ln)); + var ln = pos.line, idx = (reverse ? target.length - 1 : 0), match = target[idx], line = fold(doc.getLine(ln)); var offsetA = (reverse ? line.indexOf(match) + match.length : line.lastIndexOf(match)); if (reverse ? offsetA >= pos.ch || offsetA != match.length : offsetA <= pos.ch || offsetA != line.length - match.length) return; for (;;) { - if (reverse ? !ln : ln == cm.lineCount() - 1) return; - line = fold(cm.getLine(ln += reverse ? -1 : 1)); + if (reverse ? !ln : ln == doc.lineCount() - 1) return; + line = fold(doc.getLine(ln += reverse ? -1 : 1)); match = target[reverse ? --idx : ++idx]; if (idx > 0 && idx < target.length - 1) { if (line != match) return; @@ -86,7 +86,7 @@ findPrevious: function() {return this.find(true);}, find: function(reverse) { - var self = this, pos = this.cm.clipPos(reverse ? this.pos.from : this.pos.to); + var self = this, pos = this.doc.clipPos(reverse ? this.pos.from : this.pos.to); function savePosAndFail(line) { var pos = Pos(line, 0); self.pos = {from: pos, to: pos}; @@ -102,10 +102,10 @@ } if (reverse) { if (!pos.line) return savePosAndFail(0); - pos = Pos(pos.line-1, this.cm.getLine(pos.line-1).length); + pos = Pos(pos.line-1, this.doc.getLine(pos.line-1).length); } else { - var maxLine = this.cm.lineCount(); + var maxLine = this.doc.lineCount(); if (pos.line == maxLine - 1) return savePosAndFail(maxLine); pos = Pos(pos.line + 1, 0); } @@ -118,13 +118,16 @@ replace: function(newText) { if (!this.atOccurrence) return; var lines = CodeMirror.splitLines(newText); - this.cm.replaceRange(lines, this.pos.from, this.pos.to); + this.doc.replaceRange(lines, this.pos.from, this.pos.to); this.pos.to = Pos(this.pos.from.line + lines.length - 1, lines[lines.length - 1].length + (lines.length == 1 ? this.pos.from.ch : 0)); } }; CodeMirror.defineExtension("getSearchCursor", function(query, pos, caseFold) { + return new SearchCursor(this.doc, query, pos, caseFold); + }); + CodeMirror.defineDocExtension("getSearchCursor", function(query, pos, caseFold) { return new SearchCursor(this, query, pos, caseFold); }); })(); From db8f1842bb0176053c9ba03a0f943577b3c76241 Mon Sep 17 00:00:00 2001 From: Marijn Haverbeke Date: Tue, 9 Apr 2013 21:24:26 +0200 Subject: [PATCH 0990/5780] Add missing semicolon Shush, linter. --- lib/codemirror.js | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/lib/codemirror.js b/lib/codemirror.js index bf5c9e69c6..abae2882a2 100644 --- a/lib/codemirror.js +++ b/lib/codemirror.js @@ -3194,7 +3194,7 @@ window.CodeMirror = (function() { }; CodeMirror.defineDocExtension = function(name, func) { Doc.prototype[name] = func; - } + }; CodeMirror.defineOption = option; var initHooks = []; From cc90184e2da39b8d5a3fe2b3dc36c0f14f4f8d22 Mon Sep 17 00:00:00 2001 From: Marijn Haverbeke Date: Wed, 10 Apr 2013 09:39:06 +0200 Subject: [PATCH 0991/5780] Prevent selection-drawing code from calling charCoords with ch: -1 --- lib/codemirror.js | 13 ++++++++++--- 1 file changed, 10 insertions(+), 3 deletions(-) diff --git a/lib/codemirror.js b/lib/codemirror.js index abae2882a2..5f253de552 100644 --- a/lib/codemirror.js +++ b/lib/codemirror.js @@ -793,9 +793,16 @@ window.CodeMirror = (function() { } iterateBidiSections(getOrder(lineObj), fromArg || 0, toArg == null ? lineLen : toArg, function(from, to, dir) { - var leftPos = coords(dir == "rtl" ? to - 1 : from); - var rightPos = coords(dir == "rtl" ? from : to - 1); - var left = leftPos.left, right = rightPos.right; + var leftPos = coords(from), rightPos, left, right; + if (from == to) { + rightPos = leftPos; + left = right = leftPos.left; + } else { + rightPos = coords(to - 1); + if (dir == "rtl") { var tmp = leftPos; leftPos = rightPos; rightPos = tmp; } + left = leftPos.left; + right = rightPos.right; + } if (rightPos.top - leftPos.top > 3) { // Different lines, draw top part add(left, leftPos.top, null, leftPos.bottom); left = pl; From 8698e629d49c203f451250ae1fc30f6b8e0d1af8 Mon Sep 17 00:00:00 2001 From: Marijn Haverbeke Date: Wed, 10 Apr 2013 09:42:29 +0200 Subject: [PATCH 0992/5780] Add Blogger's template editor to real-world uses --- doc/realworld.html | 1 + 1 file changed, 1 insertion(+) diff --git a/doc/realworld.html b/doc/realworld.html index cf90ea2555..576a003de0 100644 --- a/doc/realworld.html +++ b/doc/realworld.html @@ -25,6 +25,7 @@

      { } CodeMi
    • Adobe Brackets (code editor)
    • Amber (JavaScript-based Smalltalk system)
    • APEye (tool for testing & documenting APIs)
    • +
    • Blogger's template editor
    • BlueGriffon (HTML editor)
    • Cargo Collective (creative publishing platform)
    • Code per Node (Drupal module)
    • From ac8b53b737da172697b68fae0398981a7fe5ad15 Mon Sep 17 00:00:00 2001 From: lynschinzer Date: Wed, 10 Apr 2013 20:53:15 +0200 Subject: [PATCH 0993/5780] [vim keymap] Fix ;, behavior for d c y operators --- keymap/vim.js | 1 + test/vim_test.js | 132 +++++++++++++++++++++++++++++++++++++++++++++++ 2 files changed, 133 insertions(+) diff --git a/keymap/vim.js b/keymap/vim.js index 26c3c7d4b9..83885bb6c1 100644 --- a/keymap/vim.js +++ b/keymap/vim.js @@ -1260,6 +1260,7 @@ var forward = motionArgs.forward === lastSearch.forward; var increment = (lastSearch.increment ? 1 : 0) * (forward ? -1 : 1); cm.moveH(-increment, 'char'); + motionArgs.inclusive = forward ? true : false; var curEnd = moveToCharacter(cm, repeat, forward, lastSearch.selectedCharacter); if (!curEnd) { cm.moveH(increment, 'char') diff --git a/test/vim_test.js b/test/vim_test.js index ea3ea4d55b..09f6e4b541 100644 --- a/test/vim_test.js +++ b/test/vim_test.js @@ -1240,6 +1240,138 @@ testVim('T,', function(cm, vim, helpers) { helpers.doKeys('2', ','); eq(8, cm.getCursor().ch); }, { value: '01x3xx67xx'}); +testVim('fd,;', function(cm, vim, helpers) { + cm.setCursor(0, 0); + helpers.doKeys('f', '4'); + cm.setCursor(0, 0); + helpers.doKeys('d', ';'); + eq('56789', cm.getValue()); + helpers.doKeys('u'); + cm.setCursor(0, 9); + helpers.doKeys('d', ','); + eq('01239', cm.getValue()); +}, { value: '0123456789'}); +testVim('Fd,;', function(cm, vim, helpers) { + cm.setCursor(0, 9); + helpers.doKeys('F', '4'); + cm.setCursor(0, 9); + helpers.doKeys('d', ';'); + eq('01239', cm.getValue()); + helpers.doKeys('u'); + cm.setCursor(0, 0); + helpers.doKeys('d', ','); + eq('56789', cm.getValue()); +}, { value: '0123456789'}); +testVim('td,;', function(cm, vim, helpers) { + cm.setCursor(0, 0); + helpers.doKeys('t', '4'); + cm.setCursor(0, 0); + helpers.doKeys('d', ';'); + eq('456789', cm.getValue()); + helpers.doKeys('u'); + cm.setCursor(0, 9); + helpers.doKeys('d', ','); + eq('012349', cm.getValue()); +}, { value: '0123456789'}); +testVim('Td,;', function(cm, vim, helpers) { + cm.setCursor(0, 9); + helpers.doKeys('T', '4'); + cm.setCursor(0, 9); + helpers.doKeys('d', ';'); + eq('012349', cm.getValue()); + helpers.doKeys('u'); + cm.setCursor(0, 0); + helpers.doKeys('d', ','); + eq('456789', cm.getValue()); +}, { value: '0123456789'}); +testVim('fc,;', function(cm, vim, helpers) { + cm.setCursor(0, 0); + helpers.doKeys('f', '4'); + cm.setCursor(0, 0); + helpers.doKeys('c', ';', 'Esc'); + eq('56789', cm.getValue()); + helpers.doKeys('u'); + cm.setCursor(0, 9); + helpers.doKeys('c', ','); + eq('01239', cm.getValue()); +}, { value: '0123456789'}); +testVim('Fc,;', function(cm, vim, helpers) { + cm.setCursor(0, 9); + helpers.doKeys('F', '4'); + cm.setCursor(0, 9); + helpers.doKeys('c', ';', 'Esc'); + eq('01239', cm.getValue()); + helpers.doKeys('u'); + cm.setCursor(0, 0); + helpers.doKeys('c', ','); + eq('56789', cm.getValue()); +}, { value: '0123456789'}); +testVim('tc,;', function(cm, vim, helpers) { + cm.setCursor(0, 0); + helpers.doKeys('t', '4'); + cm.setCursor(0, 0); + helpers.doKeys('c', ';', 'Esc'); + eq('456789', cm.getValue()); + helpers.doKeys('u'); + cm.setCursor(0, 9); + helpers.doKeys('c', ','); + eq('012349', cm.getValue()); +}, { value: '0123456789'}); +testVim('Tc,;', function(cm, vim, helpers) { + cm.setCursor(0, 9); + helpers.doKeys('T', '4'); + cm.setCursor(0, 9); + helpers.doKeys('c', ';', 'Esc'); + eq('012349', cm.getValue()); + helpers.doKeys('u'); + cm.setCursor(0, 0); + helpers.doKeys('c', ','); + eq('456789', cm.getValue()); +}, { value: '0123456789'}); +testVim('fy,;', function(cm, vim, helpers) { + cm.setCursor(0, 0); + helpers.doKeys('f', '4'); + cm.setCursor(0, 0); + helpers.doKeys('y', ';', 'P'); + eq('012340123456789', cm.getValue()); + helpers.doKeys('u'); + cm.setCursor(0, 9); + helpers.doKeys('y', ',', 'P'); + eq('012345678456789', cm.getValue()); +}, { value: '0123456789'}); +testVim('Fy,;', function(cm, vim, helpers) { + cm.setCursor(0, 9); + helpers.doKeys('F', '4'); + cm.setCursor(0, 9); + helpers.doKeys('y', ';', 'p'); + eq('012345678945678', cm.getValue()); + helpers.doKeys('u'); + cm.setCursor(0, 0); + helpers.doKeys('y', ',', 'P'); + eq('012340123456789', cm.getValue()); +}, { value: '0123456789'}); +testVim('ty,;', function(cm, vim, helpers) { + cm.setCursor(0, 0); + helpers.doKeys('t', '4'); + cm.setCursor(0, 0); + helpers.doKeys('y', ';', 'P'); + eq('01230123456789', cm.getValue()); + helpers.doKeys('u'); + cm.setCursor(0, 9); + helpers.doKeys('y', ',', 'p'); + eq('01234567895678', cm.getValue()); +}, { value: '0123456789'}); +testVim('Ty,;', function(cm, vim, helpers) { + cm.setCursor(0, 9); + helpers.doKeys('T', '4'); + cm.setCursor(0, 9); + helpers.doKeys('y', ';', 'p'); + eq('01234567895678', cm.getValue()); + helpers.doKeys('u'); + cm.setCursor(0, 0); + helpers.doKeys('y', ',', 'P'); + eq('01230123456789', cm.getValue()); +}, { value: '0123456789'}); // Ex mode tests testVim('ex_go_to_line', function(cm, vim, helpers) { From 0ef9137286cd6c806a030605925d29b0ef645138 Mon Sep 17 00:00:00 2001 From: lynschinzer Date: Tue, 9 Apr 2013 09:58:43 +0200 Subject: [PATCH 0994/5780] [vim keymap] Add Ctrl-a Ctrl-x to increment/decrement numerical tokens --- keymap/vim.js | 32 ++++++++++++++++++++++++++++++++ test/vim_test.js | 29 +++++++++++++++++++++++++++++ 2 files changed, 61 insertions(+) diff --git a/keymap/vim.js b/keymap/vim.js index 83885bb6c1..66679c0e4d 100644 --- a/keymap/vim.js +++ b/keymap/vim.js @@ -260,6 +260,10 @@ actionArgs: { position: 'bottom' }, motion: 'moveToFirstNonWhiteSpaceCharacter' }, { keys: ['.'], type: 'action', action: 'repeatLastEdit' }, + { keys: ['Ctrl-a'], type: 'action', action: 'incrementNumberToken', + actionArgs: {increase: true, backtrack: false}}, + { keys: ['Ctrl-x'], type: 'action', action: 'incrementNumberToken', + actionArgs: {increase: false, backtrack: false}}, // Text object motions { keys: ['a', 'character'], type: 'motion', motion: 'textObjectManipulation' }, @@ -1568,6 +1572,34 @@ } } }, + incrementNumberToken: function(cm, actionArgs, vim) { + var cur = cm.getCursor(); + var lineStr = cm.getLine(cur.line); + var re = /-?\d+/g; + var match; + var start; + var end; + var numberStr; + var token; + while ((match = re.exec(lineStr)) !== null) { + token = match[0]; + start = match.index; + end = start + token.length; + if(cur.ch < end)break; + } + if(!actionArgs.backtrack && (end <= cur.ch))return; + if (token) { + var increment = actionArgs.increase ? 1 : -1; + var number = parseInt(token) + (increment * actionArgs.repeat); + var from = {ch:start, line:cur.line}; + var to = {ch:end, line:cur.line}; + numberStr = number.toString(); + cm.replaceRange(numberStr, from, to); + } else { + return; + } + cm.setCursor({line: cur.line, ch: start + numberStr.length - 1}); + }, repeatLastEdit: function(cm, actionArgs, vim) { // TODO: Make this repeat insert mode changes. var lastEdit = vim.lastEdit; diff --git a/test/vim_test.js b/test/vim_test.js index 09f6e4b541..7c080c46ca 100644 --- a/test/vim_test.js +++ b/test/vim_test.js @@ -722,6 +722,35 @@ testVim('Y', function(cm, vim, helpers) { }, { value: ' word1\nword2\n word3' }); // Action tests +testVim('ctrl-a', function(cm, vim, helpers) { + cm.setCursor(0, 0); + helpers.doKeys('Ctrl-a'); + eq('-9', cm.getValue()); + helpers.assertCursorAt(0, 1); + helpers.doKeys('2','Ctrl-a'); + eq('-7', cm.getValue()); +}, {value: '-10'}); +testVim('ctrl-x', function(cm, vim, helpers) { + cm.setCursor(0, 0); + helpers.doKeys('Ctrl-x'); + eq('-1', cm.getValue()); + helpers.assertCursorAt(0, 1); + helpers.doKeys('2','Ctrl-x'); + eq('-3', cm.getValue()); +}, {value: '0'}); +testVim('Ctrl-x/Ctrl-a search forward', function(cm, vim, helpers) { + ['Ctrl-x', 'Ctrl-a'].forEach(function(key) { + cm.setCursor(0, 0); + helpers.doKeys(key); + helpers.assertCursorAt(0, 5); + helpers.doKeys('l'); + helpers.doKeys(key); + helpers.assertCursorAt(0, 10); + cm.setCursor(0, 11); + helpers.doKeys(key); + helpers.assertCursorAt(0, 11); + }); +}, {value: '__jmp1 jmp2 jmp'}); testVim('a', function(cm, vim, helpers) { cm.setCursor(0, 1); helpers.doKeys('a'); From 890e32805b592e7a39633675d9f52808f0362893 Mon Sep 17 00:00:00 2001 From: Jason Grout Date: Thu, 11 Apr 2013 08:22:24 -0500 Subject: [PATCH 0995/5780] Fix typos in markText docs --- doc/manual.html | 6 +++--- 1 file changed, 3 insertions(+), 3 deletions(-) diff --git a/doc/manual.html b/doc/manual.html index 49dd5883d5..adfba1bf15 100644 --- a/doc/manual.html +++ b/doc/manual.html @@ -1043,8 +1043,8 @@

      Text-marking methods

      When enabled, will cause the mark to clear itself whenever the cursor enters its range. This is mostly useful for text-replacement widgets that need to 'snap open' when the - user tries to edit them. A - the "clear" event + user tries to edit them. The + "clear" event fired on the range handle can be used to be notified when this happens.
      replacedWith: Element
      @@ -1081,7 +1081,7 @@

      Text-marking methods

      marker is no longer in the document, and finally getOptions(copyWidget), which returns an object representing the options for the marker. - If copyWidget is given an true, it will clone the + If copyWidget is given true, it will clone the value of the replacedWith option, if any.

      From 25fa0241d90436e469d7bd49b2b3e2d81222d970 Mon Sep 17 00:00:00 2001 From: Marijn Haverbeke Date: Fri, 12 Apr 2013 09:36:56 +0200 Subject: [PATCH 0996/5780] [show-hint addon] Support displayText property for completions --- addon/hint/show-hint.js | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/addon/hint/show-hint.js b/addon/hint/show-hint.js index cbaa043571..58acf728c6 100644 --- a/addon/hint/show-hint.js +++ b/addon/hint/show-hint.js @@ -42,7 +42,7 @@ CodeMirror.showHint = function(cm, getHints, options) { if (completion.className != null) className = completion.className + " " + className; elt.className = className; if (completion.render) completion.render(elt, data, completion); - else elt.appendChild(document.createTextNode(getText(completion))); + else elt.appendChild(document.createTextNode(completion.displayText || getText(completion))); elt.hintId = i; } var pos = cm.cursorCoords(options.alignWithWord !== false ? data.from : null); From 20647247ed7116d09bde12d88ee624c31c5d579d Mon Sep 17 00:00:00 2001 From: Marijn Haverbeke Date: Fri, 12 Apr 2013 21:19:18 +0200 Subject: [PATCH 0997/5780] Add Flattr link to front page --- index.html | 5 +++-- 1 file changed, 3 insertions(+), 2 deletions(-) diff --git a/index.html b/index.html index 9b250b63d0..8b76987f2d 100644 --- a/index.html +++ b/index.html @@ -262,8 +262,9 @@

      Support CodeMirror

      (Paypal, bank, or - Gittip) + class="quasilink">bank, + Gittip, or + Flattr)
    • Purchase commercial support
    From c519feae3458881d7dc4258df7079c5464c971fe Mon Sep 17 00:00:00 2001 From: santec Date: Mon, 15 Apr 2013 09:59:28 +0200 Subject: [PATCH 0998/5780] [sql mode] Add some keywords to mariadb and mysql modes --- mode/sql/sql.js | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/mode/sql/sql.js b/mode/sql/sql.js index 15f65920ca..4ff847f32c 100644 --- a/mode/sql/sql.js +++ b/mode/sql/sql.js @@ -226,7 +226,7 @@ CodeMirror.defineMode("sql", function(config, parserConfig) { CodeMirror.defineMIME("text/x-mysql", { name: "sql", client: set("charset clear connect edit ego exit go help nopager notee nowarning pager print prompt quit rehash source status system tee"), - keywords: set(sqlKeywords + "accessible action add after algorithm all analyze asensitive at authors auto_increment autocommit avg avg_row_length before binary binlog both btree cache call cascade cascaded case catalog_name chain change changed character check checkpoint checksum class_origin client_statistics close coalesce code collate collation collations column columns comment commit committed completion concurrent condition connection consistent constraint contains continue contributors convert cross current_date current_time current_timestamp current_user cursor data database databases day_hour day_microsecond day_minute day_second deallocate dec declare default delay_key_write delayed delimiter des_key_file describe deterministic dev_pop dev_samp deviance directory disable discard distinctrow div dual dumpfile each elseif enable enclosed end ends engine engines enum errors escape escaped even event events every execute exists exit explain extended fast fetch field fields first flush for force foreign found_rows full fulltext function general global grant grants group groupby_concat handler hash help high_priority hosts hour_microsecond hour_minute hour_second if ignore ignore_server_ids import index index_statistics infile inner innodb inout insensitive insert_method interval invoker isolation iterate key keys kill language last leading leave left level limit linear lines list load local localtime localtimestamp lock logs low_priority master master_heartbeat_period master_ssl_verify_server_cert masters match max max_rows maxvalue message_text middleint migrate min min_rows minute_microsecond minute_second mod mode modifies modify mutex mysql_errno natural next no no_write_to_binlog offline offset one online open optimize option optionally out outer outfile pack_keys parser partition partitions password phase plugin plugins prepare preserve prev primary privileges procedure processlist profile profiles purge query quick range read read_write reads real rebuild recover references regexp relaylog release remove rename reorganize repair repeatable replace require resignal restrict resume return returns revoke right rlike rollback rollup row row_format rtree savepoint schedule schema schema_name schemas second_microsecond security sensitive separator serializable server session share show signal slave slow smallint snapshot spatial specific sql sql_big_result sql_buffer_result sql_cache sql_calc_found_rows sql_no_cache sql_small_result sqlexception sqlstate sqlwarning ssl start starting starts status std stddev stddev_pop stddev_samp storage straight_join subclass_origin sum suspend table_name table_statistics tables tablespace temporary terminated to trailing transaction trigger triggers truncate uncommitted undo unique unlock upgrade usage use use_frm user user_resources user_statistics using utc_date utc_time utc_timestamp value variables varying view views warnings when while with work write xa xor year_month zerofill begin do then else loop repeat"), + keywords: set(sqlKeywords + "accessible action add after algorithm all analyze asensitive at authors auto_increment autocommit avg avg_row_length before binary binlog both btree cache call cascade cascaded case catalog_name chain change changed character check checkpoint checksum class_origin client_statistics close coalesce code collate collation collations column columns comment commit committed completion concurrent condition connection consistent constraint contains continue contributors convert cross current_date current_time current_timestamp current_user cursor data database databases day_hour day_microsecond day_minute day_second deallocate dec declare default delay_key_write delayed delimiter des_key_file describe deterministic dev_pop dev_samp deviance directory disable discard distinctrow div dual dumpfile each elseif enable enclosed end ends engine engines enum errors escape escaped even event events every execute exists exit explain extended fast fetch field fields first flush for force foreign found_rows full fulltext function general global grant grants group groupby_concat handler hash help high_priority hosts hour_microsecond hour_minute hour_second if ignore ignore_server_ids import index index_statistics infile inner innodb inout insensitive insert_method install interval invoker isolation iterate key keys kill language last leading leave left level limit linear lines list load local localtime localtimestamp lock logs low_priority master master_heartbeat_period master_ssl_verify_server_cert masters match max max_rows maxvalue message_text middleint migrate min min_rows minute_microsecond minute_second mod mode modifies modify mutex mysql_errno natural next no no_write_to_binlog offline offset one online open optimize option optionally out outer outfile pack_keys parser partition partitions password phase plugin plugins prepare preserve prev primary privileges procedure processlist profile profiles purge query quick range read read_write reads real rebuild recover references regexp relaylog release remove rename reorganize repair repeatable replace require resignal restrict resume return returns revoke right rlike rollback rollup row row_format rtree savepoint schedule schema schema_name schemas second_microsecond security sensitive separator serializable server session share show signal slave slow smallint snapshot soname spatial specific sql sql_big_result sql_buffer_result sql_cache sql_calc_found_rows sql_no_cache sql_small_result sqlexception sqlstate sqlwarning ssl start starting starts status std stddev stddev_pop stddev_samp storage straight_join subclass_origin sum suspend table_name table_statistics tables tablespace temporary terminated to trailing transaction trigger triggers truncate uncommitted undo uninstall unique unlock upgrade usage use use_frm user user_resources user_statistics using utc_date utc_time utc_timestamp value variables varying view views warnings when while with work write xa xor year_month zerofill begin do then else loop repeat"), builtin: set("bool boolean bit blob decimal double enum float long longblob longtext medium mediumblob mediumint mediumtext time timestamp tinyblob tinyint tinytext text bigint int int1 int2 int3 int4 int8 integer float float4 float8 double char varbinary varchar varcharacter precision date datetime year unsigned signed numeric"), atoms: set("false true null unknown"), operatorChars: /^[*+\-%<>!=&|^]/, @@ -242,7 +242,7 @@ CodeMirror.defineMode("sql", function(config, parserConfig) { CodeMirror.defineMIME("text/x-mariadb", { name: "sql", client: set("charset clear connect edit ego exit go help nopager notee nowarning pager print prompt quit rehash source status system tee"), - keywords: set(sqlKeywords + "accessible action add after algorithm all always analyze asensitive at authors auto_increment autocommit avg avg_row_length before binary binlog both btree cache call cascade cascaded case catalog_name chain change changed character check checkpoint checksum class_origin client_statistics close coalesce code collate collation collations column columns comment commit committed completion concurrent condition connection consistent constraint contains continue contributors convert cross current_date current_time current_timestamp current_user cursor data database databases day_hour day_microsecond day_minute day_second deallocate dec declare default delay_key_write delayed delimiter des_key_file describe deterministic dev_pop dev_samp deviance directory disable discard distinctrow div dual dumpfile each elseif enable enclosed end ends engine engines enum errors escape escaped even event events every execute exists exit explain extended fast fetch field fields first flush for force foreign found_rows full fulltext function general generated global grant grants group groupby_concat handler hash help high_priority hosts hour_microsecond hour_minute hour_second if ignore ignore_server_ids import index index_statistics infile inner innodb inout insensitive insert_method interval invoker isolation iterate key keys kill language last leading leave left level limit linear lines list load local localtime localtimestamp lock logs low_priority master master_heartbeat_period master_ssl_verify_server_cert masters match max max_rows maxvalue message_text middleint migrate min min_rows minute_microsecond minute_second mod mode modifies modify mutex mysql_errno natural next no no_write_to_binlog offline offset one online open optimize option optionally out outer outfile pack_keys parser partition partitions password persistent phase plugin plugins prepare preserve prev primary privileges procedure processlist profile profiles purge query quick range read read_write reads real rebuild recover references regexp relaylog release remove rename reorganize repair repeatable replace require resignal restrict resume return returns revoke right rlike rollback rollup row row_format rtree savepoint schedule schema schema_name schemas second_microsecond security sensitive separator serializable server session share show signal slave slow smallint snapshot spatial specific sql sql_big_result sql_buffer_result sql_cache sql_calc_found_rows sql_no_cache sql_small_result sqlexception sqlstate sqlwarning ssl start starting starts status std stddev stddev_pop stddev_samp storage straight_join subclass_origin sum suspend table_name table_statistics tables tablespace temporary terminated to trailing transaction trigger triggers truncate uncommitted undo unique unlock upgrade usage use use_frm user user_resources user_statistics using utc_date utc_time utc_timestamp value variables varying view views virtual warnings when while with work write xa xor year_month zerofill begin do then else loop repeat"), + keywords: set(sqlKeywords + "accessible action add after algorithm all always analyze asensitive at authors auto_increment autocommit avg avg_row_length before binary binlog both btree cache call cascade cascaded case catalog_name chain change changed character check checkpoint checksum class_origin client_statistics close coalesce code collate collation collations column columns comment commit committed completion concurrent condition connection consistent constraint contains continue contributors convert cross current_date current_time current_timestamp current_user cursor data database databases day_hour day_microsecond day_minute day_second deallocate dec declare default delay_key_write delayed delimiter des_key_file describe deterministic dev_pop dev_samp deviance directory disable discard distinctrow div dual dumpfile each elseif enable enclosed end ends engine engines enum errors escape escaped even event events every execute exists exit explain extended fast fetch field fields first flush for force foreign found_rows full fulltext function general generated global grant grants group groupby_concat handler hard hash help high_priority hosts hour_microsecond hour_minute hour_second if ignore ignore_server_ids import index index_statistics infile inner innodb inout insensitive insert_method install interval invoker isolation iterate key keys kill language last leading leave left level limit linear lines list load local localtime localtimestamp lock logs low_priority master master_heartbeat_period master_ssl_verify_server_cert masters match max max_rows maxvalue message_text middleint migrate min min_rows minute_microsecond minute_second mod mode modifies modify mutex mysql_errno natural next no no_write_to_binlog offline offset one online open optimize option optionally out outer outfile pack_keys parser partition partitions password persistent phase plugin plugins prepare preserve prev primary privileges procedure processlist profile profiles purge query quick range read read_write reads real rebuild recover references regexp relaylog release remove rename reorganize repair repeatable replace require resignal restrict resume return returns revoke right rlike rollback rollup row row_format rtree savepoint schedule schema schema_name schemas second_microsecond security sensitive separator serializable server session share show signal slave slow smallint snapshot soft soname spatial specific sql sql_big_result sql_buffer_result sql_cache sql_calc_found_rows sql_no_cache sql_small_result sqlexception sqlstate sqlwarning ssl start starting starts status std stddev stddev_pop stddev_samp storage straight_join subclass_origin sum suspend table_name table_statistics tables tablespace temporary terminated to trailing transaction trigger triggers truncate uncommitted undo uninstall unique unlock upgrade usage use use_frm user user_resources user_statistics using utc_date utc_time utc_timestamp value variables varying view views virtual warnings when while with work write xa xor year_month zerofill begin do then else loop repeat"), builtin: set("bool boolean bit blob decimal double enum float long longblob longtext medium mediumblob mediumint mediumtext time timestamp tinyblob tinyint tinytext text bigint int int1 int2 int3 int4 int8 integer float float4 float8 double char varbinary varchar varcharacter precision date datetime year unsigned signed numeric"), atoms: set("false true null unknown"), operatorChars: /^[*+\-%<>!=&|^]/, From 58216885e00cec655b389eec00827ab66525a42a Mon Sep 17 00:00:00 2001 From: Marijn Haverbeke Date: Mon, 15 Apr 2013 11:16:00 +0200 Subject: [PATCH 0999/5780] Try to guard against IE9/10 sneakily resetting textarea selection on drag (And possibly in other situations.) Issue #1240 --- lib/codemirror.js | 19 +++++++++++-------- 1 file changed, 11 insertions(+), 8 deletions(-) diff --git a/lib/codemirror.js b/lib/codemirror.js index 5f253de552..98d7355024 100644 --- a/lib/codemirror.js +++ b/lib/codemirror.js @@ -1409,18 +1409,16 @@ window.CodeMirror = (function() { if (!cm.state.focused || hasSelection(input) || isReadOnly(cm)) return false; var text = input.value; if (text == prevInput && posEq(sel.from, sel.to)) return false; - // IE enjoys randomly deselecting our input's text when - // re-focusing. If the selection is gone but the cursor is at the - // start of the input, that's probably what happened. - if (ie && text && input.selectionStart === 0) { + if (ie && !ie_lt9 && cm.display.inputHasSelection === text) { resetInput(cm, true); return false; } + var withOp = !cm.curOp; if (withOp) startOperation(cm); sel.shift = false; var same = 0, l = Math.min(prevInput.length, text.length); - while (same < l && prevInput[same] == text[same]) ++same; + while (same < l && prevInput.charCodeAt(same) == text.charCodeAt(same)) ++same; var from = sel.from, to = sel.to; if (same < prevInput.length) from = Pos(from.line, from.ch - (prevInput.length - same)); @@ -1444,10 +1442,14 @@ window.CodeMirror = (function() { cm.display.prevInput = ""; minimal = hasCopyEvent && (doc.sel.to.line - doc.sel.from.line > 100 || (selected = cm.getSelection()).length > 1000); - if (minimal) cm.display.input.value = "-"; - else cm.display.input.value = selected || cm.getSelection(); + var content = minimal ? "-" : selected || cm.getSelection(); + cm.display.input.value = content; if (cm.state.focused) selectInput(cm.display.input); - } else if (user) cm.display.prevInput = cm.display.input.value = ""; + if (ie && !ie_lt9) cm.display.inputHasSelection = content; + } else if (user) { + cm.display.prevInput = cm.display.input.value = ""; + if (ie && !ie_lt9) cm.display.inputHasSelection = null; + } cm.display.inaccurateSelection = minimal; } @@ -2040,6 +2042,7 @@ window.CodeMirror = (function() { this.doc.mode.electricChars.indexOf(ch) > -1) setTimeout(operation(cm, function() {indentLine(cm, cm.doc.sel.to.line, "smart");}), 75); if (handleCharBinding(cm, e, ch)) return; + if (ie && !ie_lt9) cm.display.inputHasSelection = null; fastPoll(cm); } From 0ee523d47a6cb9b8f4be16425383435c0fb9745b Mon Sep 17 00:00:00 2001 From: Marijn Haverbeke Date: Mon, 15 Apr 2013 11:23:42 +0200 Subject: [PATCH 1000/5780] Stop 'phantom' after-the-fact drag starts in IE10 Closes #1445 --- lib/codemirror.js | 3 +-- 1 file changed, 1 insertion(+), 2 deletions(-) diff --git a/lib/codemirror.js b/lib/codemirror.js index 98d7355024..161ac997e0 100644 --- a/lib/codemirror.js +++ b/lib/codemirror.js @@ -164,8 +164,6 @@ window.CodeMirror = (function() { d.pollingFast = false; // Self-resetting timeout for the poller d.poll = new Delayed(); - // True when a drag from the editor is active - d.draggingText = false; d.cachedCharWidth = d.cachedTextHeight = null; d.measureLineCache = []; @@ -1804,6 +1802,7 @@ window.CodeMirror = (function() { } function onDragStart(cm, e) { + if (ie && !cm.state.draggingText) { e_stop(e); return; } if (eventInWidget(cm.display, e)) return; var txt = cm.getSelection(); From c9c213ed1abadf00a1620f303c0a125a22050454 Mon Sep 17 00:00:00 2001 From: Marijn Haverbeke Date: Mon, 15 Apr 2013 11:32:48 +0200 Subject: [PATCH 1001/5780] Prevent duplicate selection updates from mouse clicks Closes #1450 --- lib/codemirror.js | 5 ++++- 1 file changed, 4 insertions(+), 1 deletion(-) diff --git a/lib/codemirror.js b/lib/codemirror.js index 161ac997e0..57e16806ab 100644 --- a/lib/codemirror.js +++ b/lib/codemirror.js @@ -1665,9 +1665,12 @@ window.CodeMirror = (function() { e_preventDefault(e); if (type == "single") extendSelection(cm.doc, clipPos(doc, start)); - var startstart = sel.from, startend = sel.to; + var startstart = sel.from, startend = sel.to, lastPos = start; function doSelect(cur) { + if (posEq(lastPos, cur)) return; + lastPos = cur; + if (type == "single") { extendSelection(cm.doc, clipPos(doc, start), cur); return; From 046f2a336303137973c45a6bbe88fe85cb7cfd07 Mon Sep 17 00:00:00 2001 From: lynschinzer Date: Thu, 11 Apr 2013 17:49:58 +0200 Subject: [PATCH 1002/5780] [vim keymap] Add HML cursor motions --- keymap/vim.js | 31 +++++++++++++++++++++++++++++++ test/vim_test.js | 18 ++++++++++++++++++ 2 files changed, 49 insertions(+) diff --git a/keymap/vim.js b/keymap/vim.js index 66679c0e4d..01550449c5 100644 --- a/keymap/vim.js +++ b/keymap/vim.js @@ -79,6 +79,15 @@ { keys: ['PageUp'], type: 'keyToKey', toKeys: ['Ctrl-b'] }, { keys: ['PageDown'], type: 'keyToKey', toKeys: ['Ctrl-f'] }, // Motions + { keys: ['H'], type: 'motion', + motion: 'moveToTopLine', + motionArgs: { linewise: true }}, + { keys: ['M'], type: 'motion', + motion: 'moveToMiddleLine', + motionArgs: { linewise: true }}, + { keys: ['L'], type: 'motion', + motion: 'moveToBottomLine', + motionArgs: { linewise: true }}, { keys: ['h'], type: 'motion', motion: 'moveByCharacters', motionArgs: { forward: false }}, @@ -1004,6 +1013,19 @@ */ // All of the functions below return Cursor objects. var motions = { + moveToTopLine: function(cm, motionArgs) { + var line = getUserVisibleLines(cm).top + motionArgs.repeat -1; + return { line: line, ch: findFirstNonWhiteSpaceCharacter(cm.getLine(line)) }; + }, + moveToMiddleLine: function(cm) { + var range = getUserVisibleLines(cm); + var line = Math.floor((range.top + range.bottom) * 0.5); + return { line: line, ch: findFirstNonWhiteSpaceCharacter(cm.getLine(line)) }; + }, + moveToBottomLine: function(cm, motionArgs) { + var line = getUserVisibleLines(cm).bottom - motionArgs.repeat +1; + return { line: line, ch: findFirstNonWhiteSpaceCharacter(cm.getLine(line)) }; + }, expandToLine: function(cm, motionArgs) { // Expands forward to end of line, and then to next line if repeat is // >1. Does not handle backward motion! @@ -2410,6 +2432,15 @@ } } } + function getUserVisibleLines(cm) { + var scrollInfo = cm.getScrollInfo(); + var occludeTorleranceTop = 6; + var occludeTorleranceBottom = 10; + var from = cm.coordsChar({left:0, top: occludeTorleranceTop}, 'local'); + var bottomY = scrollInfo.clientHeight - occludeTorleranceBottom; + var to = cm.coordsChar({left:0, top: bottomY}, 'local'); + return {top: from.line, bottom: to.line}; + } // Ex command handling // Care must be taken when adding to the default Ex command map. For any diff --git a/test/vim_test.js b/test/vim_test.js index 7c080c46ca..103259a9ef 100644 --- a/test/vim_test.js +++ b/test/vim_test.js @@ -1401,6 +1401,24 @@ testVim('Ty,;', function(cm, vim, helpers) { helpers.doKeys('y', ',', 'P'); eq('01230123456789', cm.getValue()); }, { value: '0123456789'}); +testVim('HML', function(cm, vim, helpers) { + cm.setSize(600, 400); + cm.setCursor(120, 0); + helpers.doKeys('H'); + helpers.assertCursorAt(90, 2); + helpers.doKeys('L'); + helpers.assertCursorAt(119, 4); + helpers.doKeys('M'); + helpers.assertCursorAt(104,4); +}, { value: (function(){ + var upperLines = new Array(100); + var lowerLines = new Array(100); + var upper = ' xx\n'; + var lower = ' xx\n'; + upper = upperLines.join(upper); + lower = upperLines.join(lower); + return upper + lower; +})()}); // Ex mode tests testVim('ex_go_to_line', function(cm, vim, helpers) { From 09eba2305ffa044c091d0fff931a18d1e84531d9 Mon Sep 17 00:00:00 2001 From: Marijn Haverbeke Date: Mon, 15 Apr 2013 11:48:10 +0200 Subject: [PATCH 1003/5780] [javascript mode] Make 'foo++ / 10' parse correctly (not a regexp) Issue #1451 --- mode/javascript/javascript.js | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/mode/javascript/javascript.js b/mode/javascript/javascript.js index c138b6fc63..08c1cb1f4f 100644 --- a/mode/javascript/javascript.js +++ b/mode/javascript/javascript.js @@ -426,7 +426,7 @@ CodeMirror.defineMode("javascript", function(config, parserConfig) { if (state.tokenize != jsTokenComment && stream.eatSpace()) return null; var style = state.tokenize(stream, state); if (type == "comment") return style; - state.lastType = type; + state.lastType = type == "operator" && (content == "++" || content == "--") ? "incdec" : type; return parseJS(state, style, type, content, stream); }, From b7db11ae8e6c4c62cd6589780f116183328a737d Mon Sep 17 00:00:00 2001 From: lynschinzer Date: Sun, 14 Apr 2013 21:07:58 +0200 Subject: [PATCH 1004/5780] [vim keymap] Add [] motions --- keymap/vim.js | 138 +++++++++++++++++++++++++++++++++++++++++++++-- test/vim_test.js | 116 +++++++++++++++++++++++++++++++++++++++ 2 files changed, 250 insertions(+), 4 deletions(-) diff --git a/keymap/vim.js b/keymap/vim.js index 01550449c5..499601b5b0 100644 --- a/keymap/vim.js +++ b/keymap/vim.js @@ -192,6 +192,12 @@ { keys: ['[', '`',], type: 'motion', motion: 'jumpToMark', motionArgs: { forward: false } }, { keys: [']', '\''], type: 'motion', motion: 'jumpToMark', motionArgs: { forward: true, linewise: true } }, { keys: ['[', '\''], type: 'motion', motion: 'jumpToMark', motionArgs: { forward: false, linewise: true } }, + { keys: [']', 'character'], type: 'motion', + motion: 'moveToSymbol', + motionArgs: { forward: true}}, + { keys: ['[', 'character'], type: 'motion', + motion: 'moveToSymbol', + motionArgs: { forward: false}}, { keys: ['|'], type: 'motion', motion: 'moveToColumn', motionArgs: { }}, @@ -1219,6 +1225,11 @@ return moveToCharacter(cm, repeat, motionArgs.forward, motionArgs.selectedCharacter) || cm.getCursor(); }, + moveToSymbol: function(cm, motionArgs) { + var repeat = motionArgs.repeat; + return findSymbol(cm, repeat, motionArgs.forward, + motionArgs.selectedCharacter) || cm.getCursor(); + }, moveToColumn: function(cm, motionArgs, vim) { var repeat = motionArgs.repeat; // repeat is equivalent to which column we want to move to! @@ -1872,6 +1883,127 @@ vimGlobalState.lastChararacterSearch.selectedCharacter = args.selectedCharacter; } + var symbolToMode = { + '(': 'bracket', ')': 'bracket', '{': 'bracket', '}': 'bracket', + '[': 'section', ']': 'section', + '*': 'comment', '/': 'comment', + 'm': 'method', 'M': 'method', + '#': 'preprocess' + }; + var findSymbolModes = { + bracket: { + isComplete: function(state) { + if (state.nextCh === state.symb) { + state.depth++; + if(state.depth >= 1)return true; + } else if (state.nextCh === state.reverseSymb) { + state.depth--; + } + return false; + } + }, + section: { + init: function(state) { + state.curMoveThrough = true; + state.symb = (state.forward ? ']' : '[') === state.symb ? '{' : '}'; + }, + isComplete: function(state) { + return state.index === 0 && state.nextCh === state.symb; + } + }, + comment: { + isComplete: function(state) { + var found = state.lastCh === '*' && state.nextCh === '/'; + state.lastCh = state.nextCh; + return found; + } + }, + // TODO: The original Vim implementation only operates on level 1 and 2. + // The current implementation doesn't check for code block level and + // therefore it operates on any levels. + method: { + init: function(state) { + state.symb = (state.symb === 'm' ? '{' : '}'); + state.reverseSymb = state.symb === '{' ? '}' : '{'; + }, + isComplete: function(state) { + if(state.nextCh === state.symb)return true; + return false; + } + }, + preprocess: { + init: function(state) { + state.index = 0; + }, + isComplete: function(state) { + if (state.nextCh === '#') { + var token = state.lineText.match(/#(\w+)/)[1]; + if (token === 'endif') { + if (state.forward && state.depth === 0) { + return true; + } + state.depth++; + } else if (token === 'if') { + if (!state.forward && state.depth === 0) { + return true; + } + state.depth--; + } + if(token === 'else' && state.depth === 0)return true; + } + return false; + } + } + }; + function findSymbol(cm, repeat, forward, symb) { + var cur = cm.getCursor(); + var increment = forward ? 1 : -1; + var endLine = forward ? cm.lineCount() : -1; + var curCh = cur.ch; + var line = cur.line; + var lineText = cm.getLine(line); + var state = { + lineText: lineText, + nextCh: lineText.charAt(curCh), + lastCh: null, + index: curCh, + symb: symb, + reverseSymb: (forward ? { ')': '(', '}': '{' } : { '(': ')', '{': '}' })[symb], + forward: forward, + depth: 0, + curMoveThrough: false + }; + var mode = symbolToMode[symb]; + if(!mode)return cur; + var init = findSymbolModes[mode].init; + var isComplete = findSymbolModes[mode].isComplete; + if(init)init(state); + while (line !== endLine && repeat) { + state.index += increment; + state.nextCh = state.lineText.charAt(state.index); + if (!state.nextCh) { + line += increment; + state.lineText = cm.getLine(line) || ''; + if (increment > 0) { + state.index = 0; + } else { + var lineLen = state.lineText.length; + state.index = (lineLen > 0) ? (lineLen-1) : 0; + } + state.nextCh = state.lineText.charAt(state.index); + } + if (isComplete(state)) { + cur.line = line; + cur.ch = state.index; + repeat--; + } + } + if (state.nextCh || state.curMoveThrough) { + return { line: line, ch: state.index }; + } + return cur; + } + /* * Returns the boundaries of the next word. If the cursor in the middle of * the word, then returns the boundaries of the current word, starting at @@ -2060,9 +2192,6 @@ var line = cur.line; symb = symb ? symb : cm.getLine(line).charAt(cur.ch); - // Are we at the opening or closing char - var forwards = inArray(symb, ['(', '[', '{']); - var reverseSymb = ({ '(': ')', ')': '(', '[': ']', ']': '[', @@ -2076,12 +2205,13 @@ // set our increment to move forward (+1) or backwards (-1) // depending on which bracket we're matching var increment = ({'(': 1, '{': 1, '[': 1})[symb] || -1; + var endLine = increment === 1 ? cm.lineCount() : -1; var depth = 1, nextCh = symb, index = cur.ch, lineText = cm.getLine(line); // Simple search for closing paren--just count openings and closings till // we find our match // TODO: use info from CodeMirror to ignore closing brackets in comments // and quotes, etc. - while (nextCh && depth > 0) { + while (line !== endLine && depth > 0) { index += increment; nextCh = lineText.charAt(index); if (!nextCh) { diff --git a/test/vim_test.js b/test/vim_test.js index 103259a9ef..7d4dde8585 100644 --- a/test/vim_test.js +++ b/test/vim_test.js @@ -1419,6 +1419,122 @@ testVim('HML', function(cm, vim, helpers) { lower = upperLines.join(lower); return upper + lower; })()}); +var squareBracketMotionSandbox = ''+ + '({\n'+//0 + ' ({\n'+//11 + ' /*comment {\n'+//2 + ' */(\n'+//3 + '#else \n'+//4 + ' /* )\n'+//5 + '#if }\n'+//6 + ' )}*/\n'+//7 + ')}\n'+//8 + '{}\n'+//9 + '#else {{\n'+//10 + '{}\n'+//11 + '}\n'+//12 + '{\n'+//13 + '#endif\n'+//14 + '}\n'+//15 + '}\n'+//16 + '#else';//17 +testVim('[[, ]]', function(cm, vim, helpers) { + cm.setCursor(0, 0); + helpers.doKeys(']', ']'); + helpers.assertCursorAt(9,0); + helpers.doKeys('2', ']', ']'); + helpers.assertCursorAt(13,0); + helpers.doKeys(']', ']'); + helpers.assertCursorAt(17,0); + helpers.doKeys('[', '['); + helpers.assertCursorAt(13,0); + helpers.doKeys('2', '[', '['); + helpers.assertCursorAt(9,0); + helpers.doKeys('[', '['); + helpers.assertCursorAt(0,0); +}, { value: squareBracketMotionSandbox}); +testVim('[], ][', function(cm, vim, helpers) { + cm.setCursor(0, 0); + helpers.doKeys(']', '['); + helpers.assertCursorAt(12,0); + helpers.doKeys('2', ']', '['); + helpers.assertCursorAt(16,0); + helpers.doKeys(']', '['); + helpers.assertCursorAt(17,0); + helpers.doKeys('[', ']'); + helpers.assertCursorAt(16,0); + helpers.doKeys('2', '[', ']'); + helpers.assertCursorAt(12,0); + helpers.doKeys('[', ']'); + helpers.assertCursorAt(0,0); +}, { value: squareBracketMotionSandbox}); +testVim('[{, ]}', function(cm, vim, helpers) { + cm.setCursor(4, 10); + helpers.doKeys('[', '{'); + helpers.assertCursorAt(2,12); + helpers.doKeys('2', '[', '{'); + helpers.assertCursorAt(0,1); + cm.setCursor(4, 10); + helpers.doKeys(']', '}'); + helpers.assertCursorAt(6,11); + helpers.doKeys('2', ']', '}'); + helpers.assertCursorAt(8,1); + cm.setCursor(0,1); + helpers.doKeys(']', '}'); + helpers.assertCursorAt(8,1); + helpers.doKeys('[', '{'); + helpers.assertCursorAt(0,1); +}, { value: squareBracketMotionSandbox}); +testVim('[(, ])', function(cm, vim, helpers) { + cm.setCursor(4, 10); + helpers.doKeys('[', '('); + helpers.assertCursorAt(3,14); + helpers.doKeys('2', '[', '('); + helpers.assertCursorAt(0,0); + cm.setCursor(4, 10); + helpers.doKeys(']', ')'); + helpers.assertCursorAt(5,11); + helpers.doKeys('2', ']', ')'); + helpers.assertCursorAt(8,0); + helpers.doKeys('[', '('); + helpers.assertCursorAt(0,0); + helpers.doKeys(']', ')'); + helpers.assertCursorAt(8,0); +}, { value: squareBracketMotionSandbox}); +testVim('[*, ]*, [/, ]/', function(cm, vim, helpers) { + ['*', '/'].forEach(function(key){ + cm.setCursor(7, 0); + helpers.doKeys('2', '[', key); + helpers.assertCursorAt(2,2); + helpers.doKeys('2', ']', key); + helpers.assertCursorAt(7,5); + }); +}, { value: squareBracketMotionSandbox}); +testVim('[#, ]#', function(cm, vim, helpers) { + cm.setCursor(10, 3); + helpers.doKeys('2', '[', '#'); + helpers.assertCursorAt(4,0); + helpers.doKeys('5', ']', '#'); + helpers.assertCursorAt(17,0); + cm.setCursor(10, 3); + helpers.doKeys(']', '#'); + helpers.assertCursorAt(14,0); +}, { value: squareBracketMotionSandbox}); +testVim('[m, ]m, [M, ]M', function(cm, vim, helpers) { + cm.setCursor(11, 0); + helpers.doKeys('[', 'm'); + helpers.assertCursorAt(10,7); + helpers.doKeys('4', '[', 'm'); + helpers.assertCursorAt(1,3); + helpers.doKeys('5', ']', 'm'); + helpers.assertCursorAt(11,0); + helpers.doKeys('[', 'M'); + helpers.assertCursorAt(9,1); + helpers.doKeys('3', ']', 'M'); + helpers.assertCursorAt(15,0); + helpers.doKeys('5', '[', 'M'); + helpers.assertCursorAt(7,3); +}, { value: squareBracketMotionSandbox}); // Ex mode tests testVim('ex_go_to_line', function(cm, vim, helpers) { From 50c4d2c321cf5692a2a882e76e92d0d0f3a422d3 Mon Sep 17 00:00:00 2001 From: Marijn Haverbeke Date: Tue, 16 Apr 2013 09:16:18 +0200 Subject: [PATCH 1005/5780] [brace-fold addon] Fix infinite loop when finding opening brace --- addon/fold/brace-fold.js | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/addon/fold/brace-fold.js b/addon/fold/brace-fold.js index aad6e0141e..dc78883f35 100644 --- a/addon/fold/brace-fold.js +++ b/addon/fold/brace-fold.js @@ -1,7 +1,7 @@ CodeMirror.braceRangeFinder = function(cm, start) { var line = start.line, lineText = cm.getLine(line); var at = lineText.length, startChar, tokenType; - for (;;) { + for (; at > 0;) { var found = lineText.lastIndexOf("{", at); if (found < start.ch) break; tokenType = cm.getTokenAt(CodeMirror.Pos(line, found + 1)).type; From 6f45a030ccfba22f93097b06b06a7b3c599f8662 Mon Sep 17 00:00:00 2001 From: Marijn Haverbeke Date: Fri, 19 Apr 2013 15:06:18 +0200 Subject: [PATCH 1006/5780] Fix inaccurate type info in docs Closes #1459 --- doc/manual.html | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/doc/manual.html b/doc/manual.html index adfba1bf15..b5dda00c59 100644 --- a/doc/manual.html +++ b/doc/manual.html @@ -1336,7 +1336,7 @@

    Miscellaneous methods

    lot faster. The return value from this method will be the return value of your function. -
    cm.indentLine(line: integer|LineHandle, ?dir: string)
    +
    cm.indentLine(line: integer, ?dir: string)
    Adjust the indentation of the given line. The second argument (which defaults to "smart") may be one of:
    From e370099b99ee18f23e0669f86ff071fd97343258 Mon Sep 17 00:00:00 2001 From: Marijn Haverbeke Date: Fri, 19 Apr 2013 15:19:14 +0200 Subject: [PATCH 1007/5780] [clojure mode] Fix allowed symbol characters Closes #1460 --- mode/clojure/clojure.js | 7 +++---- 1 file changed, 3 insertions(+), 4 deletions(-) diff --git a/mode/clojure/clojure.js b/mode/clojure/clojure.js index 7751212632..fae675477d 100644 --- a/mode/clojure/clojure.js +++ b/mode/clojure/clojure.js @@ -44,8 +44,7 @@ CodeMirror.defineMode("clojure", function () { sign: /[+-]/, exponent: /e/i, keyword_char: /[^\s\(\[\;\)\]]/, - basic: /[\w\$_\-]/, - lang_keyword: /[\w*+!\-_?:\/]/ + symbol: /[\w*+!\-\._?:\/]/ }; function stateStack(indent, type, prev) { // represents a state stack object @@ -195,10 +194,10 @@ CodeMirror.defineMode("clojure", function () { popStack(state); } } else if ( ch == ":" ) { - stream.eatWhile(tests.lang_keyword); + stream.eatWhile(tests.symbol); return ATOM; } else { - stream.eatWhile(tests.basic); + stream.eatWhile(tests.symbol); if (keywords && keywords.propertyIsEnumerable(stream.current())) { returnType = KEYWORD; From c7b802ae3c0b9240ef9c732e1e22ec0ce554407b Mon Sep 17 00:00:00 2001 From: Marijn Haverbeke Date: Fri, 19 Apr 2013 15:31:02 +0200 Subject: [PATCH 1008/5780] [lint addon] Don't try to display tooltip above top of document --- addon/lint/lint.js | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/addon/lint/lint.js b/addon/lint/lint.js index d8961ac3f6..6c5519ab56 100644 --- a/addon/lint/lint.js +++ b/addon/lint/lint.js @@ -10,7 +10,7 @@ CodeMirror.validate = (function() { function position(e) { if (!tt.parentNode) return CodeMirror.off(document, "mousemove", position); - tt.style.top = (e.clientY - tt.offsetHeight - 5) + "px"; + tt.style.top = Math.max(0, e.clientY - tt.offsetHeight - 5) + "px"; tt.style.left = (e.clientX + 5) + "px"; } CodeMirror.on(document, "mousemove", position); From 5a0e4eeafc8f03164e19c291be7964f4f1e39321 Mon Sep 17 00:00:00 2001 From: Marijn Haverbeke Date: Fri, 19 Apr 2013 15:54:17 +0200 Subject: [PATCH 1009/5780] [lint addon] Make sure tooltips are always hidden when their ref node is removed --- addon/lint/lint.js | 33 +++++++++++++++++++++------------ 1 file changed, 21 insertions(+), 12 deletions(-) diff --git a/addon/lint/lint.js b/addon/lint/lint.js index 6c5519ab56..231eb2f567 100644 --- a/addon/lint/lint.js +++ b/addon/lint/lint.js @@ -15,7 +15,7 @@ CodeMirror.validate = (function() { } CodeMirror.on(document, "mousemove", position); position(e); - tt.style.opacity = 1; + if (tt.style.opacity != null) tt.style.opacity = 1; return tt; } function rm(elt) { @@ -28,6 +28,22 @@ CodeMirror.validate = (function() { setTimeout(function() { rm(tt); }, 600); } + function showTooltipFor(e, content, node) { + var tooltip = showTooltip(e, content); + function hide() { + CodeMirror.off(node, "mouseout", hide); + if (tooltip) { hideTooltip(tooltip); tooltip = null; } + } + var poll = setInterval(function() { + if (tooltip) for (var n = node;; n = n.parentNode) { + if (n == document.body) return; + if (!n) { hide(); break; } + } + if (!tooltip) return clearInterval(poll); + }, 400); + CodeMirror.on(node, "mouseout", hide); + } + function LintState(cm, options, hasGutter) { this.marked = []; this.options = options; @@ -58,11 +74,9 @@ CodeMirror.validate = (function() { inner.className = "CodeMirror-lint-marker-multiple"; } - if (tooltips != false) { - var tooltip; - CodeMirror.on(inner, "mouseover", function(e) { tooltip = showTooltip(e, labels); }); - CodeMirror.on(inner, "mouseout", function() { if (tooltip) hideTooltip(tooltip); }); - } + if (tooltips != false) CodeMirror.on(inner, "mouseover", function(e) { + showTooltipFor(e, labels, inner); + }); return marker; } @@ -140,13 +154,8 @@ CodeMirror.validate = (function() { } function popupSpanTooltip(ann, e) { - var tooltip = showTooltip(e, annotationTooltip(ann)); var target = e.target || e.srcElement; - CodeMirror.on(target, "mouseout", hide); - function hide() { - CodeMirror.off(target, "mouseout", hide); - hideTooltip(tooltip); - } + showTooltipFor(e, annotationTooltip(ann), target); } // When the mouseover fires, the cursor might not actually be over From 9c349a57f221c166c4b50d8fe1f169de06ecb5e4 Mon Sep 17 00:00:00 2001 From: Andrey Lushnikov Date: Mon, 15 Apr 2013 13:42:29 +0400 Subject: [PATCH 1010/5780] show-hint addon to emit "hintShowed" and "hintClosed" events --- addon/hint/show-hint.js | 2 ++ 1 file changed, 2 insertions(+) diff --git a/addon/hint/show-hint.js b/addon/hint/show-hint.js index 58acf728c6..0a5b1eb33f 100644 --- a/addon/hint/show-hint.js +++ b/addon/hint/show-hint.js @@ -50,6 +50,7 @@ CodeMirror.showHint = function(cm, getHints, options) { hints.style.left = left + "px"; hints.style.top = top + "px"; document.body.appendChild(hints); + CodeMirror.signal(cm, "hintShowed"); // If we're at the edge of the screen, then we want the menu to appear on the left of the cursor. var winW = window.innerWidth || Math.max(document.body.offsetWidth, document.documentElement.offsetWidth); @@ -154,6 +155,7 @@ CodeMirror.showHint = function(cm, getHints, options) { cm.off("focus", onFocus); cm.off("scroll", onScroll); if (willContinue !== true && data.onClose) data.onClose(); + CodeMirror.signal(cm, "hintClosed"); } function pick() { pickCompletion(cm, data, completions[selectedHint]); From 14465a501b4cd398c9cff0ae2cb96c0e739035a8 Mon Sep 17 00:00:00 2001 From: Marijn Haverbeke Date: Fri, 19 Apr 2013 16:04:26 +0200 Subject: [PATCH 1011/5780] [show-hint addon] Use events to notify hint source on open/close/select Issue #1454 --- addon/hint/show-hint.js | 11 +++++------ 1 file changed, 5 insertions(+), 6 deletions(-) diff --git a/addon/hint/show-hint.js b/addon/hint/show-hint.js index 0a5b1eb33f..2c54dcd77e 100644 --- a/addon/hint/show-hint.js +++ b/addon/hint/show-hint.js @@ -29,7 +29,7 @@ CodeMirror.showHint = function(cm, getHints, options) { // When there is only one completion, use it directly. if (!continued && options.completeSingle !== false && completions.length == 1) { pickCompletion(cm, data, completions[0]); - if (data.onClose) data.onClose(); + CodeMirror.signal(data, "close"); return true; } @@ -50,7 +50,7 @@ CodeMirror.showHint = function(cm, getHints, options) { hints.style.left = left + "px"; hints.style.top = top + "px"; document.body.appendChild(hints); - CodeMirror.signal(cm, "hintShowed"); + CodeMirror.signal(data, "shown"); // If we're at the edge of the screen, then we want the menu to appear on the left of the cursor. var winW = window.innerWidth || Math.max(document.body.offsetWidth, document.documentElement.offsetWidth); @@ -87,7 +87,7 @@ CodeMirror.showHint = function(cm, getHints, options) { hints.scrollTop = node.offsetTop - 3; else if (node.offsetTop + node.offsetHeight > hints.scrollTop + hints.clientHeight) hints.scrollTop = node.offsetTop + node.offsetHeight - hints.clientHeight + 3; - if (data.onSelect) data.onSelect(completions[selectedHint], node); + CodeMirror.signal(data, "select", completions[selectedHint], node); } function screenAmount() { @@ -154,8 +154,7 @@ CodeMirror.showHint = function(cm, getHints, options) { cm.off("blur", onBlur); cm.off("focus", onFocus); cm.off("scroll", onScroll); - if (willContinue !== true && data.onClose) data.onClose(); - CodeMirror.signal(cm, "hintClosed"); + if (willContinue !== true) CodeMirror.signal(data, "close"); } function pick() { pickCompletion(cm, data, completions[selectedHint]); @@ -173,7 +172,7 @@ CodeMirror.showHint = function(cm, getHints, options) { else once = setTimeout(function(){close(true); continued = true; startHinting();}, 70); } - if (data.onSelect) data.onSelect(completions[0], hints.firstChild); + CodeMirror.signal(data, "select", completions[0], hints.firstChild); return true; } From e6bc191c01c23b5a0313069a09030c502f73faf9 Mon Sep 17 00:00:00 2001 From: Marijn Haverbeke Date: Fri, 19 Apr 2013 16:50:14 +0200 Subject: [PATCH 1012/5780] Document addToHistory option to markText --- doc/manual.html | 6 +++++- 1 file changed, 5 insertions(+), 1 deletion(-) diff --git a/doc/manual.html b/doc/manual.html index b5dda00c59..a69a5cb92b 100644 --- a/doc/manual.html +++ b/doc/manual.html @@ -1051,7 +1051,7 @@

    Text-marking methods

    Use a given node to display this range. Implies both collapsed and atomic. The given DOM node must be an inline element (as opposed to a block element).
    -
    readOnly: boolean|string
    +
    readOnly: boolean
    A read-only span can, as long as it is not cleared, not be modified except by calling setValue to reset @@ -1060,6 +1060,10 @@

    Text-marking methods

    existing undo events being partially nullified by read-only spans would corrupt the history (in the current implementation).
    +
    addToHistory: boolean
    +
    When set to true (default is false), adding this marker + will create an event in the undo history that can be + individually undone (clearing the marker).
    startStyle: string
    Can be used to specify an extra CSS class to be applied to the leftmost span that is part of the marker.
    From 36e4c894aeee094c2f8d2ef884162fb50c570306 Mon Sep 17 00:00:00 2001 From: Marijn Haverbeke Date: Fri, 19 Apr 2013 16:58:51 +0200 Subject: [PATCH 1013/5780] Mark release 3.12 --- doc/compress.html | 1 + index.html | 14 ++++++++++++++ lib/codemirror.js | 2 +- package.json | 2 +- 4 files changed, 17 insertions(+), 2 deletions(-) diff --git a/doc/compress.html b/doc/compress.html index cd4d41bac8..f1fa218def 100644 --- a/doc/compress.html +++ b/doc/compress.html @@ -30,6 +30,7 @@

    { } CodeMi

    Version: + + + + + + + + + + + + + + + + + + + + Select Font Size + + + + +

    + + + + From 1ecf154e62ff931f77894e966995bd19b3db0105 Mon Sep 17 00:00:00 2001 From: Marijn Haverbeke Date: Mon, 22 Apr 2013 12:58:00 +0200 Subject: [PATCH 1018/5780] [cobol mode] Integrate Issue #1465 --- doc/compress.html | 1 + doc/modes.html | 1 + mode/cobol/cobol.js | 459 +++++++++++++++++++++--------------------- mode/cobol/index.html | 118 +++++------ mode/meta.js | 1 + 5 files changed, 291 insertions(+), 289 deletions(-) diff --git a/doc/compress.html b/doc/compress.html index f1fa218def..c51b2833e0 100644 --- a/doc/compress.html +++ b/doc/compress.html @@ -73,6 +73,7 @@

    { } CodeMi + diff --git a/doc/modes.html b/doc/modes.html index d016aca8ee..ac476bef78 100644 --- a/doc/modes.html +++ b/doc/modes.html @@ -27,6 +27,7 @@

    { } CodeMi
  • Asterisk dialplan
  • C, C++, C#
  • Clojure
  • +
  • COBOL
  • CoffeeScript
  • Common Lisp
  • CSS
  • diff --git a/mode/cobol/cobol.js b/mode/cobol/cobol.js index 8f45e3bbd8..d92491dde8 100644 --- a/mode/cobol/cobol.js +++ b/mode/cobol/cobol.js @@ -1,241 +1,240 @@ /** * Author: Gautam Mehta - * Branched from CodeMirror's Scheme mode + * Branched from CodeMirror's Scheme mode */ CodeMirror.defineMode("cobol", function () { - var BUILTIN = "builtin", COMMENT = "comment", STRING = "string", CHARACTER = "string-2", - ATOM = "atom", NUMBER = "number", BRACKET = "bracket", KEYWORD = "keyword", MODTAG = "header", - COBOLLINENUM = "def", PERIOD = "link"; - function makeKeywords(str) { - var obj = {}, words = str.split(" "); - for (var i = 0; i < words.length; ++i) obj[words[i]] = true; - return obj; + var BUILTIN = "builtin", COMMENT = "comment", STRING = "string", + ATOM = "atom", NUMBER = "number", KEYWORD = "keyword", MODTAG = "header", + COBOLLINENUM = "def", PERIOD = "link"; + function makeKeywords(str) { + var obj = {}, words = str.split(" "); + for (var i = 0; i < words.length; ++i) obj[words[i]] = true; + return obj; + } + var atoms = makeKeywords("TRUE FALSE ZEROES ZEROS ZERO SPACES SPACE LOW-VALUE LOW-VALUES "); + var keywords = makeKeywords( + "ACCEPT ACCESS ACQUIRE ADD ADDRESS " + + "ADVANCING AFTER ALIAS ALL ALPHABET " + + "ALPHABETIC ALPHABETIC-LOWER ALPHABETIC-UPPER ALPHANUMERIC ALPHANUMERIC-EDITED " + + "ALSO ALTER ALTERNATE AND ANY " + + "ARE AREA AREAS ARITHMETIC ASCENDING " + + "ASSIGN AT ATTRIBUTE AUTHOR AUTO " + + "AUTO-SKIP AUTOMATIC B-AND B-EXOR B-LESS " + + "B-NOT B-OR BACKGROUND-COLOR BACKGROUND-COLOUR BEEP " + + "BEFORE BELL BINARY BIT BITS " + + "BLANK BLINK BLOCK BOOLEAN BOTTOM " + + "BY CALL CANCEL CD CF " + + "CH CHARACTER CHARACTERS CLASS CLOCK-UNITS " + + "CLOSE COBOL CODE CODE-SET COL " + + "COLLATING COLUMN COMMA COMMIT COMMITMENT " + + "COMMON COMMUNICATION COMP COMP-0 COMP-1 " + + "COMP-2 COMP-3 COMP-4 COMP-5 COMP-6 " + + "COMP-7 COMP-8 COMP-9 COMPUTATIONAL COMPUTATIONAL-0 " + + "COMPUTATIONAL-1 COMPUTATIONAL-2 COMPUTATIONAL-3 COMPUTATIONAL-4 COMPUTATIONAL-5 " + + "COMPUTATIONAL-6 COMPUTATIONAL-7 COMPUTATIONAL-8 COMPUTATIONAL-9 COMPUTE " + + "CONFIGURATION CONNECT CONSOLE CONTAINED CONTAINS " + + "CONTENT CONTINUE CONTROL CONTROL-AREA CONTROLS " + + "CONVERTING COPY CORR CORRESPONDING COUNT " + + "CRT CRT-UNDER CURRENCY CURRENT CURSOR " + + "DATA DATE DATE-COMPILED DATE-WRITTEN DAY " + + "DAY-OF-WEEK DB DB-ACCESS-CONTROL-KEY DB-DATA-NAME DB-EXCEPTION " + + "DB-FORMAT-NAME DB-RECORD-NAME DB-SET-NAME DB-STATUS DBCS " + + "DBCS-EDITED DE DEBUG-CONTENTS DEBUG-ITEM DEBUG-LINE " + + "DEBUG-NAME DEBUG-SUB-1 DEBUG-SUB-2 DEBUG-SUB-3 DEBUGGING " + + "DECIMAL-POINT DECLARATIVES DEFAULT DELETE DELIMITED " + + "DELIMITER DEPENDING DESCENDING DESCRIBED DESTINATION " + + "DETAIL DISABLE DISCONNECT DISPLAY DISPLAY-1 " + + "DISPLAY-2 DISPLAY-3 DISPLAY-4 DISPLAY-5 DISPLAY-6 " + + "DISPLAY-7 DISPLAY-8 DISPLAY-9 DIVIDE DIVISION " + + "DOWN DROP DUPLICATE DUPLICATES DYNAMIC " + + "EBCDIC EGI EJECT ELSE EMI " + + "EMPTY EMPTY-CHECK ENABLE END END. END-ACCEPT END-ACCEPT. " + + "END-ADD END-CALL END-COMPUTE END-DELETE END-DISPLAY " + + "END-DIVIDE END-EVALUATE END-IF END-INVOKE END-MULTIPLY " + + "END-OF-PAGE END-PERFORM END-READ END-RECEIVE END-RETURN " + + "END-REWRITE END-SEARCH END-START END-STRING END-SUBTRACT " + + "END-UNSTRING END-WRITE END-XML ENTER ENTRY " + + "ENVIRONMENT EOP EQUAL EQUALS ERASE " + + "ERROR ESI EVALUATE EVERY EXCEEDS " + + "EXCEPTION EXCLUSIVE EXIT EXTEND EXTERNAL " + + "EXTERNALLY-DESCRIBED-KEY FD FETCH FILE FILE-CONTROL " + + "FILE-STREAM FILES FILLER FINAL FIND " + + "FINISH FIRST FOOTING FOR FOREGROUND-COLOR " + + "FOREGROUND-COLOUR FORMAT FREE FROM FULL " + + "FUNCTION GENERATE GET GIVING GLOBAL " + + "GO GOBACK GREATER GROUP HEADING " + + "HIGH-VALUE HIGH-VALUES HIGHLIGHT I-O I-O-CONTROL " + + "ID IDENTIFICATION IF IN INDEX " + + "INDEX-1 INDEX-2 INDEX-3 INDEX-4 INDEX-5 " + + "INDEX-6 INDEX-7 INDEX-8 INDEX-9 INDEXED " + + "INDIC INDICATE INDICATOR INDICATORS INITIAL " + + "INITIALIZE INITIATE INPUT INPUT-OUTPUT INSPECT " + + "INSTALLATION INTO INVALID INVOKE IS " + + "JUST JUSTIFIED KANJI KEEP KEY " + + "LABEL LAST LD LEADING LEFT " + + "LEFT-JUSTIFY LENGTH LENGTH-CHECK LESS LIBRARY " + + "LIKE LIMIT LIMITS LINAGE LINAGE-COUNTER " + + "LINE LINE-COUNTER LINES LINKAGE LOCAL-STORAGE " + + "LOCALE LOCALLY LOCK " + + "MEMBER MEMORY MERGE MESSAGE METACLASS " + + "MODE MODIFIED MODIFY MODULES MOVE " + + "MULTIPLE MULTIPLY NATIONAL NATIVE NEGATIVE " + + "NEXT NO NO-ECHO NONE NOT " + + "NULL NULL-KEY-MAP NULL-MAP NULLS NUMBER " + + "NUMERIC NUMERIC-EDITED OBJECT OBJECT-COMPUTER OCCURS " + + "OF OFF OMITTED ON ONLY " + + "OPEN OPTIONAL OR ORDER ORGANIZATION " + + "OTHER OUTPUT OVERFLOW OWNER PACKED-DECIMAL " + + "PADDING PAGE PAGE-COUNTER PARSE PERFORM " + + "PF PH PIC PICTURE PLUS " + + "POINTER POSITION POSITIVE PREFIX PRESENT " + + "PRINTING PRIOR PROCEDURE PROCEDURE-POINTER PROCEDURES " + + "PROCEED PROCESS PROCESSING PROGRAM PROGRAM-ID " + + "PROMPT PROTECTED PURGE QUEUE QUOTE " + + "QUOTES RANDOM RD READ READY " + + "REALM RECEIVE RECONNECT RECORD RECORD-NAME " + + "RECORDS RECURSIVE REDEFINES REEL REFERENCE " + + "REFERENCE-MONITOR REFERENCES RELATION RELATIVE RELEASE " + + "REMAINDER REMOVAL RENAMES REPEATED REPLACE " + + "REPLACING REPORT REPORTING REPORTS REPOSITORY " + + "REQUIRED RERUN RESERVE RESET RETAINING " + + "RETRIEVAL RETURN RETURN-CODE RETURNING REVERSE-VIDEO " + + "REVERSED REWIND REWRITE RF RH " + + "RIGHT RIGHT-JUSTIFY ROLLBACK ROLLING ROUNDED " + + "RUN SAME SCREEN SD SEARCH " + + "SECTION SECURE SECURITY SEGMENT SEGMENT-LIMIT " + + "SELECT SEND SENTENCE SEPARATE SEQUENCE " + + "SEQUENTIAL SET SHARED SIGN SIZE " + + "SKIP1 SKIP2 SKIP3 SORT SORT-MERGE " + + "SORT-RETURN SOURCE SOURCE-COMPUTER SPACE-FILL " + + "SPECIAL-NAMES STANDARD STANDARD-1 STANDARD-2 " + + "START STARTING STATUS STOP STORE " + + "STRING SUB-QUEUE-1 SUB-QUEUE-2 SUB-QUEUE-3 SUB-SCHEMA " + + "SUBFILE SUBSTITUTE SUBTRACT SUM SUPPRESS " + + "SYMBOLIC SYNC SYNCHRONIZED SYSIN SYSOUT " + + "TABLE TALLYING TAPE TENANT TERMINAL " + + "TERMINATE TEST TEXT THAN THEN " + + "THROUGH THRU TIME TIMES TITLE " + + "TO TOP TRAILING TRAILING-SIGN TRANSACTION " + + "TYPE TYPEDEF UNDERLINE UNEQUAL UNIT " + + "UNSTRING UNTIL UP UPDATE UPON " + + "USAGE USAGE-MODE USE USING VALID " + + "VALIDATE VALUE VALUES VARYING VLR " + + "WAIT WHEN WHEN-COMPILED WITH WITHIN " + + "WORDS WORKING-STORAGE WRITE XML XML-CODE " + + "XML-EVENT XML-NTEXT XML-TEXT ZERO ZERO-FILL " ); + + var builtins = makeKeywords("- * ** / + < <= = > >= "); + var tests = { + digit: /\d/, + digit_or_colon: /[\d:]/, + hex: /[0-9a-f]/i, + sign: /[+-]/, + exponent: /e/i, + keyword_char: /[^\s\(\[\;\)\]]/, + symbol: /[\w*+\-]/ + }; + function isNumber(ch, stream){ + // hex + if ( ch === '0' && stream.eat(/x/i) ) { + stream.eatWhile(tests.hex); + return true; } - var atoms = makeKeywords("TRUE FALSE ZEROES ZEROS ZERO SPACES SPACE LOW-VALUE LOW-VALUES "); - var keywords = makeKeywords( - "ACCEPT ACCESS ACQUIRE ADD ADDRESS " + - "ADVANCING AFTER ALIAS ALL ALPHABET " + - "ALPHABETIC ALPHABETIC-LOWER ALPHABETIC-UPPER ALPHANUMERIC ALPHANUMERIC-EDITED " + - "ALSO ALTER ALTERNATE AND ANY " + - "ARE AREA AREAS ARITHMETIC ASCENDING " + - "ASSIGN AT ATTRIBUTE AUTHOR AUTO " + - "AUTO-SKIP AUTOMATIC B-AND B-EXOR B-LESS " + - "B-NOT B-OR BACKGROUND-COLOR BACKGROUND-COLOUR BEEP " + - "BEFORE BELL BINARY BIT BITS " + - "BLANK BLINK BLOCK BOOLEAN BOTTOM " + - "BY CALL CANCEL CD CF " + - "CH CHARACTER CHARACTERS CLASS CLOCK-UNITS " + - "CLOSE COBOL CODE CODE-SET COL " + - "COLLATING COLUMN COMMA COMMIT COMMITMENT " + - "COMMON COMMUNICATION COMP COMP-0 COMP-1 " + - "COMP-2 COMP-3 COMP-4 COMP-5 COMP-6 " + - "COMP-7 COMP-8 COMP-9 COMPUTATIONAL COMPUTATIONAL-0 " + - "COMPUTATIONAL-1 COMPUTATIONAL-2 COMPUTATIONAL-3 COMPUTATIONAL-4 COMPUTATIONAL-5 " + - "COMPUTATIONAL-6 COMPUTATIONAL-7 COMPUTATIONAL-8 COMPUTATIONAL-9 COMPUTE " + - "CONFIGURATION CONNECT CONSOLE CONTAINED CONTAINS " + - "CONTENT CONTINUE CONTROL CONTROL-AREA CONTROLS " + - "CONVERTING COPY CORR CORRESPONDING COUNT " + - "CRT CRT-UNDER CURRENCY CURRENT CURSOR " + - "DATA DATE DATE-COMPILED DATE-WRITTEN DAY " + - "DAY-OF-WEEK DB DB-ACCESS-CONTROL-KEY DB-DATA-NAME DB-EXCEPTION " + - "DB-FORMAT-NAME DB-RECORD-NAME DB-SET-NAME DB-STATUS DBCS " + - "DBCS-EDITED DE DEBUG-CONTENTS DEBUG-ITEM DEBUG-LINE " + - "DEBUG-NAME DEBUG-SUB-1 DEBUG-SUB-2 DEBUG-SUB-3 DEBUGGING " + - "DECIMAL-POINT DECLARATIVES DEFAULT DELETE DELIMITED " + - "DELIMITER DEPENDING DESCENDING DESCRIBED DESTINATION " + - "DETAIL DISABLE DISCONNECT DISPLAY DISPLAY-1 " + - "DISPLAY-2 DISPLAY-3 DISPLAY-4 DISPLAY-5 DISPLAY-6 " + - "DISPLAY-7 DISPLAY-8 DISPLAY-9 DIVIDE DIVISION " + - "DOWN DROP DUPLICATE DUPLICATES DYNAMIC " + - "EBCDIC EGI EJECT ELSE EMI " + - "EMPTY EMPTY-CHECK ENABLE END END. END-ACCEPT END-ACCEPT. " + - "END-ADD END-CALL END-COMPUTE END-DELETE END-DISPLAY " + - "END-DIVIDE END-EVALUATE END-IF END-INVOKE END-MULTIPLY " + - "END-OF-PAGE END-PERFORM END-READ END-RECEIVE END-RETURN " + - "END-REWRITE END-SEARCH END-START END-STRING END-SUBTRACT " + - "END-UNSTRING END-WRITE END-XML ENTER ENTRY " + - "ENVIRONMENT EOP EQUAL EQUALS ERASE " + - "ERROR ESI EVALUATE EVERY EXCEEDS " + - "EXCEPTION EXCLUSIVE EXIT EXTEND EXTERNAL " + - "EXTERNALLY-DESCRIBED-KEY FD FETCH FILE FILE-CONTROL " + - "FILE-STREAM FILES FILLER FINAL FIND " + - "FINISH FIRST FOOTING FOR FOREGROUND-COLOR " + - "FOREGROUND-COLOUR FORMAT FREE FROM FULL " + - "FUNCTION GENERATE GET GIVING GLOBAL " + - "GO GOBACK GREATER GROUP HEADING " + - "HIGH-VALUE HIGH-VALUES HIGHLIGHT I-O I-O-CONTROL " + - "ID IDENTIFICATION IF IN INDEX " + - "INDEX-1 INDEX-2 INDEX-3 INDEX-4 INDEX-5 " + - "INDEX-6 INDEX-7 INDEX-8 INDEX-9 INDEXED " + - "INDIC INDICATE INDICATOR INDICATORS INITIAL " + - "INITIALIZE INITIATE INPUT INPUT-OUTPUT INSPECT " + - "INSTALLATION INTO INVALID INVOKE IS " + - "JUST JUSTIFIED KANJI KEEP KEY " + - "LABEL LAST LD LEADING LEFT " + - "LEFT-JUSTIFY LENGTH LENGTH-CHECK LESS LIBRARY " + - "LIKE LIMIT LIMITS LINAGE LINAGE-COUNTER " + - "LINE LINE-COUNTER LINES LINKAGE LOCAL-STORAGE " + - "LOCALE LOCALLY LOCK " + - "MEMBER MEMORY MERGE MESSAGE METACLASS " + - "MODE MODIFIED MODIFY MODULES MOVE " + - "MULTIPLE MULTIPLY NATIONAL NATIVE NEGATIVE " + - "NEXT NO NO-ECHO NONE NOT " + - "NULL NULL-KEY-MAP NULL-MAP NULLS NUMBER " + - "NUMERIC NUMERIC-EDITED OBJECT OBJECT-COMPUTER OCCURS " + - "OF OFF OMITTED ON ONLY " + - "OPEN OPTIONAL OR ORDER ORGANIZATION " + - "OTHER OUTPUT OVERFLOW OWNER PACKED-DECIMAL " + - "PADDING PAGE PAGE-COUNTER PARSE PERFORM " + - "PF PH PIC PICTURE PLUS " + - "POINTER POSITION POSITIVE PREFIX PRESENT " + - "PRINTING PRIOR PROCEDURE PROCEDURE-POINTER PROCEDURES " + - "PROCEED PROCESS PROCESSING PROGRAM PROGRAM-ID " + - "PROMPT PROTECTED PURGE QUEUE QUOTE " + - "QUOTES RANDOM RD READ READY " + - "REALM RECEIVE RECONNECT RECORD RECORD-NAME " + - "RECORDS RECURSIVE REDEFINES REEL REFERENCE " + - "REFERENCE-MONITOR REFERENCES RELATION RELATIVE RELEASE " + - "REMAINDER REMOVAL RENAMES REPEATED REPLACE " + - "REPLACING REPORT REPORTING REPORTS REPOSITORY " + - "REQUIRED RERUN RESERVE RESET RETAINING " + - "RETRIEVAL RETURN RETURN-CODE RETURNING REVERSE-VIDEO " + - "REVERSED REWIND REWRITE RF RH " + - "RIGHT RIGHT-JUSTIFY ROLLBACK ROLLING ROUNDED " + - "RUN SAME SCREEN SD SEARCH " + - "SECTION SECURE SECURITY SEGMENT SEGMENT-LIMIT " + - "SELECT SEND SENTENCE SEPARATE SEQUENCE " + - "SEQUENTIAL SET SHARED SIGN SIZE " + - "SKIP1 SKIP2 SKIP3 SORT SORT-MERGE " + - "SORT-RETURN SOURCE SOURCE-COMPUTER SPACE-FILL " + - "SPECIAL-NAMES STANDARD STANDARD-1 STANDARD-2 " + - "START STARTING STATUS STOP STORE " + - "STRING SUB-QUEUE-1 SUB-QUEUE-2 SUB-QUEUE-3 SUB-SCHEMA " + - "SUBFILE SUBSTITUTE SUBTRACT SUM SUPPRESS " + - "SYMBOLIC SYNC SYNCHRONIZED SYSIN SYSOUT " + - "TABLE TALLYING TAPE TENANT TERMINAL " + - "TERMINATE TEST TEXT THAN THEN " + - "THROUGH THRU TIME TIMES TITLE " + - "TO TOP TRAILING TRAILING-SIGN TRANSACTION " + - "TYPE TYPEDEF UNDERLINE UNEQUAL UNIT " + - "UNSTRING UNTIL UP UPDATE UPON " + - "USAGE USAGE-MODE USE USING VALID " + - "VALIDATE VALUE VALUES VARYING VLR " + - "WAIT WHEN WHEN-COMPILED WITH WITHIN " + - "WORDS WORKING-STORAGE WRITE XML XML-CODE " + - "XML-EVENT XML-NTEXT XML-TEXT ZERO ZERO-FILL " ); - - var builtins = makeKeywords("- * ** / + < <= = > >= "); - var indentKeys = makeKeywords(""); - var tests = { - digit: /\d/, - digit_or_colon: /[\d:]/, - hex: /[0-9a-f]/i, - sign: /[+-]/, - exponent: /e/i, - keyword_char: /[^\s\(\[\;\)\]]/, - symbol: /[\w*+\-]/ - }; - function isNumber(ch, stream){ - // hex - if ( ch === '0' && stream.eat(/x/i) ) { - stream.eatWhile(tests.hex); - return true; - } - // leading sign - if ( ( ch == '+' || ch == '-' ) && ( tests.digit.test(stream.peek()) ) ) { - stream.eat(tests.sign); - ch = stream.next(); + // leading sign + if ( ( ch == '+' || ch == '-' ) && ( tests.digit.test(stream.peek()) ) ) { + stream.eat(tests.sign); + ch = stream.next(); + } + if ( tests.digit.test(ch) ) { + stream.eat(ch); + stream.eatWhile(tests.digit); + if ( '.' == stream.peek()) { + stream.eat('.'); + stream.eatWhile(tests.digit); + } + if ( stream.eat(tests.exponent) ) { + stream.eat(tests.sign); + stream.eatWhile(tests.digit); + } + return true; + } + return false; + } + return { + startState: function () { + return { + indentStack: null, + indentation: 0, + mode: false + }; + }, + token: function (stream, state) { + if (state.indentStack == null && stream.sol()) { + // update indentation, but only if indentStack is empty + state.indentation = 6 ; //stream.indentation(); + } + // skip spaces + if (stream.eatSpace()) { + return null; + } + var returnType = null; + switch(state.mode){ + case "string": // multi-line string parsing mode + var next = false; + while ((next = stream.next()) != null) { + if (next == "\"" || next == "\'") { + state.mode = false; + break; + } } - if ( tests.digit.test(ch) ) { - stream.eat(ch); - stream.eatWhile(tests.digit); - if ( '.' == stream.peek()) { - stream.eat('.'); - stream.eatWhile(tests.digit); + returnType = STRING; // continue on in string mode + break; + default: // default parsing mode + var ch = stream.next(); + var col = stream.column(); + if (col >= 0 && col <= 5) { + returnType = COBOLLINENUM; + } else if (col >= 72 && col <= 79) { + stream.skipToEnd(); + returnType = MODTAG; + } else if (ch == "*" && col == 6) { // comment + stream.skipToEnd(); // rest of the line is a comment + returnType = COMMENT; + } else if (ch == "\"" || ch == "\'") { + state.mode = "string"; + returnType = STRING; + } else if (ch == "'" && !( tests.digit_or_colon.test(stream.peek()) )) { + returnType = ATOM; + } else if (ch == ".") { + returnType = PERIOD; + } else if (isNumber(ch,stream)){ + returnType = NUMBER; + } else { + if (stream.current().match(tests.symbol)) { + while (col < 71) { + if (stream.eat(tests.symbol) === undefined) { + break; + } else { + col++; + } } - if ( stream.eat(tests.exponent) ) { - stream.eat(tests.sign); - stream.eatWhile(tests.digit); - } - return true; + } + if (keywords && keywords.propertyIsEnumerable(stream.current().toUpperCase())) { + returnType = KEYWORD; + } else if (builtins && builtins.propertyIsEnumerable(stream.current().toUpperCase())) { + returnType = BUILTIN; + } else if (atoms && atoms.propertyIsEnumerable(stream.current().toUpperCase())) { + returnType = ATOM; + } else returnType = null; } - return false; + } + return returnType; + }, + indent: function (state) { + if (state.indentStack == null) return state.indentation; + return state.indentStack.indent; } - return { - startState: function () { - return { - indentStack: null, - indentation: 0, - mode: false - }; - }, - token: function (stream, state) { - if (state.indentStack == null && stream.sol()) { - // update indentation, but only if indentStack is empty - state.indentation = 6 ; //stream.indentation(); - } - // skip spaces - if (stream.eatSpace()) { - return null; - } - var returnType = null; - switch(state.mode){ - case "string": // multi-line string parsing mode - var next = false; - while ((next = stream.next()) != null) { - if (next == "\"" || next == "\'") { - state.mode = false; - break; - } - } - returnType = STRING; // continue on in string mode - break; - default: // default parsing mode - var ch = stream.next(); - var col = stream.column(); - if (col >= 0 && col <= 5) { - returnType = COBOLLINENUM; - } else if (col >= 72 && col <= 79) { - stream.skipToEnd(); - returnType = MODTAG; - } else if (ch == "*" && col == 6) { // comment - stream.skipToEnd(); // rest of the line is a comment - returnType = COMMENT; - } else if (ch == "\"" || ch == "\'") { - state.mode = "string"; - returnType = STRING; - } else if (ch == "'" && !( tests.digit_or_colon.test(stream.peek()) )) { - returnType = ATOM; - } else if (ch == ".") { - returnType = PERIOD; - } else if (isNumber(ch,stream)){ - returnType = NUMBER; - } else { - if (stream.current().match(tests.symbol)) { - while (col < 71) { - if (stream.eat(tests.symbol) === undefined) { - break; - } else { - col++; - } - } - } - if (keywords && keywords.propertyIsEnumerable(stream.current().toUpperCase())) { - returnType = KEYWORD; - } else if (builtins && builtins.propertyIsEnumerable(stream.current().toUpperCase())) { - returnType = BUILTIN; - } else if (atoms && atoms.propertyIsEnumerable(stream.current().toUpperCase())) { - returnType = ATOM; - } else returnType = null; - } - } - return returnType; - }, - indent: function (state) { - if (state.indentStack == null) return state.indentation; - return state.indentStack.indent; - } - }; + }; }); -CodeMirror.defineMIME("text/cbl", "cobol"); +CodeMirror.defineMIME("text/x-cobol", "cobol"); diff --git a/mode/cobol/index.html b/mode/cobol/index.html index c66a0e8619..71cc2fa151 100644 --- a/mode/cobol/index.html +++ b/mode/cobol/index.html @@ -7,7 +7,7 @@ - + @@ -24,42 +24,42 @@ - - - - - + + + + + + .CodeMirror { + border: 1px solid #eee; + font-size : 20px; + height : auto !important; + } + .CodeMirror-activeline-background {background: #555555 !important;} + -

    Select Theme Select Font Size + + + + + + + + + + + + + + + + + + + + Select Font Size + + +

    MIME types defined: text/x-haml.

    + +

    Parsing/Highlighting Tests: normal, verbose.

    + + + diff --git a/mode/haml/test.js b/mode/haml/test.js new file mode 100644 index 0000000000..b7178d40fb --- /dev/null +++ b/mode/haml/test.js @@ -0,0 +1,94 @@ +(function() { + var mode = CodeMirror.getMode({tabSize: 4}, "haml"); + function MT(name) { test.mode(name, mode, Array.prototype.slice.call(arguments, 1)); } + + // Requires at least one media query + MT("elementName", + "[tag %h1] Hey There"); + + MT("oneElementPerLine", + "[tag %h1] Hey There %h2"); + + MT("idSelector", + "[tag %h1][attribute #test] Hey There"); + + MT("classSelector", + "[tag %h1][attribute .hello] Hey There"); + + MT("docType", + "[tag !!! XML]"); + + MT("comment", + "[comment / Hello WORLD]"); + + MT("notComment", + "[tag %h1] This is not a / comment "); + + MT("attributes", + "[tag %a]([variable title][operator =][string \"test\"]){[atom :title] [operator =>] [string \"test\"]}"); + + MT("htmlCode", + "[tag

    ]Title[tag

    ]"); + + MT("rubyBlock", + "[operator =][variable-2 @item]"); + + MT("selectorRubyBlock", + "[tag %a.selector=] [variable-2 @item]"); + + MT("nestedRubyBlock", + "[tag %a]", + " [operator =][variable puts] [string \"test\"]"); + + MT("multilinePlaintext", + "[tag %p]", + " Hello,", + " World"); + + MT("multilineRuby", + "[tag %p]", + " [comment -# this is a comment]", + " [comment and this is a comment too]", + " Date/Time", + " [operator -] [variable now] [operator =] [tag DateTime][operator .][variable now]", + " [tag %strong=] [variable now]", + " [operator -] [keyword if] [variable now] [operator >] [tag DateTime][operator .][variable parse]([string \"December 31, 2006\"])", + " [operator =][string \"Happy\"]", + " [operator =][string \"Belated\"]", + " [operator =][string \"Birthday\"]"); + + MT("multilineComment", + "[comment /]", + " [comment Multiline]", + " [comment Comment]"); + + MT("hamlComment", + "[comment -# this is a comment]"); + + MT("multilineHamlComment", + "[comment -# this is a comment]", + " [comment and this is a comment too]"); + + MT("multilineHTMLComment", + "[comment ]"); + + MT("hamlAfterRubyTag", + "[attribute .block]", + " [tag %strong=] [variable now]", + " [attribute .test]", + " [operator =][variable now]", + " [attribute .right]"); + + MT("stretchedRuby", + "[operator =] [variable puts] [string \"Hello\"],", + " [string \"World\"]"); + + MT("interpolationInHashAttribute", + //"[tag %div]{[atom :id] [operator =>] [string \"#{][variable test][string }_#{][variable ting][string }\"]} test"); + "[tag %div]{[atom :id] [operator =>] [string \"#{][variable test][string }_#{][variable ting][string }\"]} test"); + + MT("interpolationInHTMLAttribute", + "[tag %div]([variable title][operator =][string \"#{][variable test][string }_#{][variable ting]()[string }\"]) Test"); +})(); diff --git a/test/index.html b/test/index.html index 0a0da5bd87..d397e8f7a2 100644 --- a/test/index.html +++ b/test/index.html @@ -57,6 +57,11 @@

    CodeMirror: Test Suite

    + + + + + @@ -84,7 +89,7 @@

    CodeMirror: Test Suite

    running = false, // Flag that states tests are running quit = false, // Flag to quit tests ASAP verbose = false; // Adds message for *every* test to output - + function runHarness(){ if (running) { quit = true; @@ -115,7 +120,7 @@

    CodeMirror: Test Suite

    ""; runTests(displayTest); } - + function setStatus(message, className, force){ if (quit && !force) return; if (!message) throw("must provide message"); From ea302c4a0a8fd8a784cfb21464ee5c0b3e0b45c1 Mon Sep 17 00:00:00 2001 From: Marijn Haverbeke Date: Mon, 29 Apr 2013 10:18:09 +0200 Subject: [PATCH 1028/5780] [haml mode] Integrate --- doc/compress.html | 1 + doc/modes.html | 1 + 2 files changed, 2 insertions(+) diff --git a/doc/compress.html b/doc/compress.html index c51b2833e0..9ed4773e8e 100644 --- a/doc/compress.html +++ b/doc/compress.html @@ -85,6 +85,7 @@

    { } CodeMi + diff --git a/doc/modes.html b/doc/modes.html index ac476bef78..af798aa774 100644 --- a/doc/modes.html +++ b/doc/modes.html @@ -38,6 +38,7 @@

    { } CodeMi
  • Gas (AT&T-style assembly)
  • Go
  • Groovy
  • +
  • HAML
  • Haskell
  • Haxe
  • HTML embedded scripts
  • From de9092bc99669da084fa99a16ad6ee010594dd92 Mon Sep 17 00:00:00 2001 From: Marijn Haverbeke Date: Mon, 29 Apr 2013 10:22:42 +0200 Subject: [PATCH 1029/5780] Remove TextMarker.getOptions It's hardly useful, error-prone, and too clunky. --- doc/manual.html | 12 +++--------- lib/codemirror.js | 16 ---------------- 2 files changed, 3 insertions(+), 25 deletions(-) diff --git a/doc/manual.html b/doc/manual.html index d098421442..a92a6bc92a 100644 --- a/doc/manual.html +++ b/doc/manual.html @@ -1104,18 +1104,12 @@

    Text-marking methods

    The method will return an object that represents the marker (with constuctor CodeMirror.TextMarker), which - exposes three methods: - clear(), to remove the mark, + exposes two methods: + clear(), to remove the mark, and find(), which returns a {from, to} object (both holding document positions), indicating the current position of the marked range, or undefined if the - marker is no longer in the document, and - finally getOptions(copyWidget), which returns an - object representing the options for the marker. - If copyWidget is given true, it will clone the - value of - the replacedWith - option, if any.
    + marker is no longer in the document.
    doc.setBookmark(pos: {line, ch}, ?options: object) → TextMarker
    Inserts a bookmark, a handle that follows the text around it diff --git a/lib/codemirror.js b/lib/codemirror.js index b2acbe0736..11817d7c6e 100644 --- a/lib/codemirror.js +++ b/lib/codemirror.js @@ -3597,17 +3597,6 @@ window.CodeMirror = (function() { return from && {from: from, to: to}; }; - TextMarker.prototype.getOptions = function(copyWidget) { - var repl = this.replacedWith; - return {className: this.className, - inclusiveLeft: this.inclusiveLeft, inclusiveRight: this.inclusiveRight, - atomic: this.atomic, - collapsed: this.collapsed, - replacedWith: copyWidget ? repl && repl.cloneNode(true) : repl, - readOnly: this.readOnly, - startStyle: this.startStyle, endStyle: this.endStyle}; - }; - TextMarker.prototype.attachLine = function(line) { if (!this.lines.length && this.doc.cm) { var op = this.doc.cm.curOp; @@ -3705,11 +3694,6 @@ window.CodeMirror = (function() { SharedTextMarker.prototype.find = function() { return this.primary.find(); }; - SharedTextMarker.prototype.getOptions = function(copyWidget) { - var inner = this.primary.getOptions(copyWidget); - inner.shared = true; - return inner; - }; function markTextShared(doc, from, to, options, type) { options = copyObj(options); From ca3707b67ed60d866452c31cd1310cace4bb822c Mon Sep 17 00:00:00 2001 From: Marijn Haverbeke Date: Mon, 29 Apr 2013 10:47:05 +0200 Subject: [PATCH 1030/5780] Add TextMarker.changed method --- doc/manual.html | 17 +++++++++++------ lib/codemirror.js | 45 ++++++++++++++++++++++++++++++++------------- 2 files changed, 43 insertions(+), 19 deletions(-) diff --git a/doc/manual.html b/doc/manual.html index a92a6bc92a..6f47472050 100644 --- a/doc/manual.html +++ b/doc/manual.html @@ -1104,12 +1104,17 @@

    Text-marking methods

    The method will return an object that represents the marker (with constuctor CodeMirror.TextMarker), which - exposes two methods: - clear(), to remove the mark, and - find(), which returns a {from, to} - object (both holding document positions), indicating the current - position of the marked range, or undefined if the - marker is no longer in the document.
    + exposes three methods: + clear(), to remove the mark, + find(), which returns + a {from, to} object (both holding document + positions), indicating the current position of the marked range, + or undefined if the marker is no longer in the + document, and finally changed(), + which you can call if you've done something that might change + the size of the marker (for example changing the content of + a replacedWith + node), and want to cheaply update the display.
    doc.setBookmark(pos: {line, ch}, ?options: object) → TextMarker
    Inserts a bookmark, a handle that follows the text around it diff --git a/lib/codemirror.js b/lib/codemirror.js index 11817d7c6e..7563e8804e 100644 --- a/lib/codemirror.js +++ b/lib/codemirror.js @@ -976,23 +976,28 @@ window.CodeMirror = (function() { if (memo.text == line.text && memo.markedSpans == line.markedSpans && cm.display.scroller.clientWidth == memo.width && memo.classes == line.textClass + "|" + line.bgClass + "|" + line.wrapClass) - return memo.measure; + return memo; } } + function clearCachedMeasurement(cm, line) { + var exists = findCachedMeasurement(cm, line); + if (exists) exists.text = exists.measure = exists.markedSpans = null; + } + function measureLine(cm, line) { // First look in the cache - var measure = findCachedMeasurement(cm, line); - if (!measure) { - // Failing that, recompute and store result in cache - measure = measureLineInner(cm, line); - var cache = cm.display.measureLineCache; - var memo = {text: line.text, width: cm.display.scroller.clientWidth, - markedSpans: line.markedSpans, measure: measure, - classes: line.textClass + "|" + line.bgClass + "|" + line.wrapClass}; - if (cache.length == 16) cache[++cm.display.measureLineCachePos % 16] = memo; - else cache.push(memo); - } + var cached = findCachedMeasurement(cm, line); + if (cached) return cached.measure; + + // Failing that, recompute and store result in cache + var measure = measureLineInner(cm, line); + var cache = cm.display.measureLineCache; + var memo = {text: line.text, width: cm.display.scroller.clientWidth, + markedSpans: line.markedSpans, measure: measure, + classes: line.textClass + "|" + line.bgClass + "|" + line.wrapClass}; + if (cache.length == 16) cache[++cm.display.measureLineCachePos % 16] = memo; + else cache.push(memo); return measure; } @@ -1068,7 +1073,7 @@ window.CodeMirror = (function() { if (sp.collapsed && (sp.to == null || sp.to == line.text.length)) hasBadSpan = true; } var cached = !hasBadSpan && findCachedMeasurement(cm, line); - if (cached) return measureChar(cm, line, line.text.length, cached).right; + if (cached) return measureChar(cm, line, line.text.length, cached.measure).right; var pre = lineContent(cm, line); var end = pre.appendChild(zeroWidthElement(cm.display.measure)); @@ -3597,6 +3602,20 @@ window.CodeMirror = (function() { return from && {from: from, to: to}; }; + TextMarker.prototype.changed = function() { + var pos = this.find(), cm = this.doc.cm; + if (!pos || !cm) return; + var line = getLine(this.doc, pos.from.line); + clearCachedMeasurement(cm, line); + if (pos.from.line >= cm.display.showingFrom && pos.from.line < cm.display.showingTo) { + for (var node = cm.display.lineDiv.firstChild; node; node = node.nextSibling) if (node.lineObj == line) { + if (node.offsetHeight != line.height) updateLineHeight(line, node.offsetHeight); + break; + } + runInOp(cm, function() { cm.curOp.selectionChanged = true; }); + } + }; + TextMarker.prototype.attachLine = function(line) { if (!this.lines.length && this.doc.cm) { var op = this.doc.cm.curOp; From c690083563f86739dc3d271516916d88b04370ed Mon Sep 17 00:00:00 2001 From: Marijn Haverbeke Date: Mon, 29 Apr 2013 14:21:38 +0200 Subject: [PATCH 1031/5780] [sass mode] Remove hard-coded lists of html tags and pseudo-elements Issue #1466 --- mode/sass/sass.js | 33 ++++++++------------------------- 1 file changed, 8 insertions(+), 25 deletions(-) diff --git a/mode/sass/sass.js b/mode/sass/sass.js index e5c82e4e1b..1c13e9b415 100644 --- a/mode/sass/sass.js +++ b/mode/sass/sass.js @@ -3,24 +3,13 @@ CodeMirror.defineMode("sass", function(config) { return new RegExp("^" + words.join("|")); }; - var tags = ["&", "a","abbr","acronym","address","applet","area","article","aside","audio","b","base","basefont","bdi","bdo","big","blockquote","body","br","button","canvas","caption","cite","code","col","colgroup","command","datalist","dd","del","details","dfn","dir","div","dl","dt","em","embed","fieldset","figcaption","figure","font","footer","form","frame","frameset","h1","h2","h3","h4","h5","h6","head","header","hgroup","hr","html","i","iframe","img","input","ins","keygen","kbd","label","legend","li","link","map","mark","menu","meta","meter","nav","noframes","noscript","object","ol","optgroup","option","output","p","param","pre","progress","q","rp","rt","ruby","s","samp","script","section","select","small","source","span","strike","strong","style","sub","summary","sup","table","tbody","td","textarea","tfoot","th","thead","time","title","tr","track","tt","u","ul","var","video","wbr"]; var keywords = ["true", "false", "null", "auto"]; var keywordsRegexp = new RegExp("^" + keywords.join("|")); var operators = ["\\(", "\\)", "=", ">", "<", "==", ">=", "<=", "\\+", "-", "\\!=", "/", "\\*", "%", "and", "or", "not"]; var opRegexp = tokenRegexp(operators); - function htmlTag(val){ - for(var i=0; i Date: Mon, 29 Apr 2013 14:23:28 +0200 Subject: [PATCH 1032/5780] [sass mode] Remove unused variable --- mode/sass/sass.js | 2 -- 1 file changed, 2 deletions(-) diff --git a/mode/sass/sass.js b/mode/sass/sass.js index 1c13e9b415..8758b25189 100644 --- a/mode/sass/sass.js +++ b/mode/sass/sass.js @@ -251,8 +251,6 @@ CodeMirror.defineMode("sass", function(config) { // atoms if (stream.eatWhile(/[\w-&]/)){ - - var current = stream.current(); // matches a property definition if (stream.peek() === ":" && !stream.match(pseudoElementsRegexp, false)) return "property"; From a9654a5f9add00d5ba74dd5a04b0e9d989daf8b3 Mon Sep 17 00:00:00 2001 From: Ivan Kurnosov Date: Tue, 30 Apr 2013 15:23:28 +1200 Subject: [PATCH 1033/5780] Updated sql's hookIdentifier to properly match escaped ` --- mode/sql/sql.js | 5 ++--- 1 file changed, 2 insertions(+), 3 deletions(-) diff --git a/mode/sql/sql.js b/mode/sql/sql.js index 4ff847f32c..0ce2176da6 100644 --- a/mode/sql/sql.js +++ b/mode/sql/sql.js @@ -166,10 +166,9 @@ CodeMirror.defineMode("sql", function(config, parserConfig) { // `identifier` function hookIdentifier(stream) { - var escaped = false, ch; + var ch; while ((ch = stream.next()) != null) { - if (ch == "`" && !escaped) return "variable-2"; - escaped = !escaped && ch == "`"; + if (ch == "`" && !stream.eat("`")) return "variable-2"; } return null; } From 9069b222ce48e8d5155ef10700f41e2c40cc5668 Mon Sep 17 00:00:00 2001 From: Marijn Haverbeke Date: Wed, 1 May 2013 12:05:32 +0200 Subject: [PATCH 1034/5780] Preserve Doc.prototype.constructor property --- lib/codemirror.js | 1 + 1 file changed, 1 insertion(+) diff --git a/lib/codemirror.js b/lib/codemirror.js index 7563e8804e..af89a0e755 100644 --- a/lib/codemirror.js +++ b/lib/codemirror.js @@ -4520,6 +4520,7 @@ window.CodeMirror = (function() { }; Doc.prototype = createObj(BranchChunk.prototype, { + constructor: Doc, iter: function(from, to, op) { if (op) this.iterN(from - this.first, to - from, op); else this.iterN(this.first, this.first + this.size, from); From f57ab844957d50ebff3652b9dd7f629f1d56e97e Mon Sep 17 00:00:00 2001 From: Irakli Gozalishvili Date: Tue, 30 Apr 2013 13:36:03 +0400 Subject: [PATCH 1035/5780] Preserve constructor property on the editor instance. In some cases it's more convenient to access `CodeMirror` from the instance itself, but the way prototype was defined made it impossible. --- lib/codemirror.js | 1 + 1 file changed, 1 insertion(+) diff --git a/lib/codemirror.js b/lib/codemirror.js index af89a0e755..4f0823b73a 100644 --- a/lib/codemirror.js +++ b/lib/codemirror.js @@ -2733,6 +2733,7 @@ window.CodeMirror = (function() { // 'wrap f in an operation, performed on its `this` parameter' CodeMirror.prototype = { + constructor: CodeMirror, focus: function(){window.focus(); focusInput(this); onFocus(this); fastPoll(this);}, setOption: function(option, value) { From 0d8a43a05e331c1cb1881e8f11b970141fde23dd Mon Sep 17 00:00:00 2001 From: Marijn Haverbeke Date: Fri, 3 May 2013 08:58:25 +0200 Subject: [PATCH 1036/5780] [markdown mode] Directly use xml mode, rather than relying on text/html binding Since the mode uses the internals of the xml mode state object, it will break if another mode defines text/html (i.e. htmlmixed). Closes #1468 --- mode/markdown/markdown.js | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/mode/markdown/markdown.js b/mode/markdown/markdown.js index c0be49460b..d93d55590e 100644 --- a/mode/markdown/markdown.js +++ b/mode/markdown/markdown.js @@ -1,7 +1,7 @@ CodeMirror.defineMode("markdown", function(cmCfg, modeCfg) { - var htmlFound = CodeMirror.mimeModes.hasOwnProperty("text/html"); - var htmlMode = CodeMirror.getMode(cmCfg, htmlFound ? "text/html" : "text/plain"); + var htmlFound = CodeMirror.modes.hasOwnProperty("xml"); + var htmlMode = CodeMirror.getMode(cmCfg, htmlFound ? {name: "xml", htmlMode: true} : "text/plain"); var aliases = { html: "htmlmixed", js: "javascript", From de983c613392d5f886e0142fc0a6c8ab9a81832e Mon Sep 17 00:00:00 2001 From: Randy Edmunds Date: Tue, 30 Apr 2013 19:43:42 -0700 Subject: [PATCH 1037/5780] [css mode] Add @import context --- mode/css/css.js | 4 ++++ 1 file changed, 4 insertions(+) diff --git a/mode/css/css.js b/mode/css/css.js index 1ef72b517d..8b1aca280e 100644 --- a/mode/css/css.js +++ b/mode/css/css.js @@ -230,6 +230,8 @@ CodeMirror.defineMode("css-base", function(config, parserConfig) { } else { style += " error"; } + } else if (context == "@import") { + style = "tag"; } else { style = "error"; } @@ -266,6 +268,7 @@ CodeMirror.defineMode("css-base", function(config, parserConfig) { } else if (type == "interpolation") state.stack.push("interpolation"); else if (type == "@media") state.stack.push("@media"); + else if (type == "@import") state.stack.push("@import"); else if (context == "@media" && /\b(keyword|attribute)\b/.test(style)) state.stack.push("@mediaType"); else if (context == "@mediaType" && stream.current() == ",") state.stack.pop(); @@ -273,6 +276,7 @@ CodeMirror.defineMode("css-base", function(config, parserConfig) { else if (context == "@mediaType(" && type == ")") state.stack.pop(); else if ((context == "rule" || context == "block") && type == ":") state.stack.push("propertyValue"); else if (context == "propertyValue" && type == ";") state.stack.pop(); + else if (context == "@import" && type == ";") state.stack.pop(); return style; }, From 85fb7c309f6b8329d2b317b6123084d2dd801a1a Mon Sep 17 00:00:00 2001 From: Marijn Haverbeke Date: Fri, 3 May 2013 09:10:30 +0200 Subject: [PATCH 1038/5780] [sql mode] Don't consider '.e' a number Closes #1489 --- mode/sql/index.html | 1 - mode/sql/sql.js | 2 +- 2 files changed, 1 insertion(+), 2 deletions(-) diff --git a/mode/sql/index.html b/mode/sql/index.html index 7ec938a121..8a2495ca24 100644 --- a/mode/sql/index.html +++ b/mode/sql/index.html @@ -6,7 +6,6 @@ - @@ -68,7 +69,8 @@

    CodeMirror: JavaScript mode

    var editor = CodeMirror.fromTextArea(document.getElementById("code"), { lineNumbers: true, matchBrackets: true, - continueComments: "Enter" + continueComments: "Enter", + extraKeys: {"Ctrl-Q": "toggleComment"} }); diff --git a/mode/javascript/javascript.js b/mode/javascript/javascript.js index 87856a89f3..fabe1c42b9 100644 --- a/mode/javascript/javascript.js +++ b/mode/javascript/javascript.js @@ -453,6 +453,9 @@ CodeMirror.defineMode("javascript", function(config, parserConfig) { }, electricChars: ":{}", + blockCommentStart: jsonMode ? null : "/*", + blockCommentEnd: jsonMode ? null : "*/", + lineComment: jsonMode ? null : "//", jsonMode: jsonMode }; diff --git a/mode/lua/lua.js b/mode/lua/lua.js index 90761e2941..b8deaa2575 100644 --- a/mode/lua/lua.js +++ b/mode/lua/lua.js @@ -133,7 +133,11 @@ CodeMirror.defineMode("lua", function(config, parserConfig) { indent: function(state, textAfter) { var closing = dedentPartial.test(textAfter); return state.basecol + indentUnit * (state.indentDepth - (closing ? 1 : 0)); - } + }, + + lineComment: "--", + blockCommentStart: "--[[", + blockCommentEnd: "]]" }; }); diff --git a/mode/ocaml/ocaml.js b/mode/ocaml/ocaml.js index 7dc3d283b2..32cbc0b7ba 100644 --- a/mode/ocaml/ocaml.js +++ b/mode/ocaml/ocaml.js @@ -106,7 +106,10 @@ CodeMirror.defineMode('ocaml', function() { token: function(stream, state) { if (stream.eatSpace()) return null; return state.tokenize(stream, state); - } + }, + + blockCommentStart: "(*", + blockCommentEnd: "*)" }; }); diff --git a/mode/php/php.js b/mode/php/php.js index 56497ed436..fa0db5b1fe 100644 --- a/mode/php/php.js +++ b/mode/php/php.js @@ -118,6 +118,9 @@ }, electricChars: "/{}:", + blockCommentStart: "/*", + blockCommentEnd: "*/", + lineComment: "//", innerMode: function(state) { return {state: state.curState, mode: state.curMode}; } }; diff --git a/mode/python/python.js b/mode/python/python.js index d69ef8389b..b623972b88 100644 --- a/mode/python/python.js +++ b/mode/python/python.js @@ -331,8 +331,9 @@ CodeMirror.defineMode("python", function(conf, parserConf) { } return state.scopes[0].offset; - } + }, + lineComment: "#" }; return external; }); diff --git a/mode/ruby/ruby.js b/mode/ruby/ruby.js index d106a542db..883244b1c7 100644 --- a/mode/ruby/ruby.js +++ b/mode/ruby/ruby.js @@ -186,8 +186,9 @@ CodeMirror.defineMode("ruby", function(config) { return ct.indented + (closing ? 0 : config.indentUnit) + (state.continuedLine ? config.indentUnit : 0); }, - electricChars: "}de" // enD and rescuE + electricChars: "}de", // enD and rescuE + lineComment: "#" }; }); diff --git a/mode/rust/rust.js b/mode/rust/rust.js index ea3005c360..7bee489b47 100644 --- a/mode/rust/rust.js +++ b/mode/rust/rust.js @@ -425,7 +425,10 @@ CodeMirror.defineMode("rust", function() { return lexical.indented + (closing ? 0 : (lexical.info == "alt" ? altIndentUnit : indentUnit)); }, - electricChars: "{}" + electricChars: "{}", + blockCommentStart: "/*", + blockCommentEnd: "*/", + lineComment: "//" }; }); diff --git a/mode/scheme/scheme.js b/mode/scheme/scheme.js index 2ed0a24cd8..c5990ae92c 100644 --- a/mode/scheme/scheme.js +++ b/mode/scheme/scheme.js @@ -223,7 +223,9 @@ CodeMirror.defineMode("scheme", function () { indent: function (state) { if (state.indentStack == null) return state.indentation; return state.indentStack.indent; - } + }, + + lineComment: ";;" }; }); diff --git a/mode/xml/xml.js b/mode/xml/xml.js index 440343bbe7..b04248c6c6 100644 --- a/mode/xml/xml.js +++ b/mode/xml/xml.js @@ -317,6 +317,8 @@ CodeMirror.defineMode("xml", function(config, parserConfig) { }, electricChars: "/", + blockCommentStart: "", configuration: parserConfig.htmlMode ? "html" : "xml" }; diff --git a/test/comment_test.js b/test/comment_test.js new file mode 100644 index 0000000000..311bdb5f49 --- /dev/null +++ b/test/comment_test.js @@ -0,0 +1,46 @@ +namespace = "comment_"; + +(function() { + function test(name, mode, run, before, after) { + return testCM(name, function(cm) { + run(cm); + eq(cm.getValue(), after); + }, {value: before, mode: mode}); + } + + var simpleProg = "function foo() {\n return bar;\n}"; + + test("block", "javascript", function(cm) { + cm.blockComment(Pos(0, 3), Pos(3, 0), {blockCommentLead: " *"}); + }, simpleProg + "\n", "/* function foo() {\n * return bar;\n * }\n */"); + + test("blockToggle", "javascript", function(cm) { + cm.blockComment(Pos(0, 3), Pos(2, 0), {blockCommentLead: " *"}); + cm.uncomment(Pos(0, 3), Pos(2, 0), {blockCommentLead: " *"}); + }, simpleProg, simpleProg); + + test("line", "javascript", function(cm) { + cm.lineComment(Pos(1, 1), Pos(1, 1)); + }, simpleProg, "function foo() {\n// return bar;\n}"); + + test("lineToggle", "javascript", function(cm) { + cm.lineComment(Pos(0, 0), Pos(2, 1)); + cm.uncomment(Pos(0, 0), Pos(2, 1)); + }, simpleProg, simpleProg); + + test("fallbackToBlock", "css", function(cm) { + cm.lineComment(Pos(0, 0), Pos(2, 1)); + }, "html {\n border: none;\n}", "/* html {\n border: none;\n} */"); + + test("fallbackToLine", "ruby", function(cm) { + cm.blockComment(Pos(0, 0), Pos(1)); + }, "def blah()\n return hah\n", "# def blah()\n# return hah\n"); + + test("commentRange", "javascript", function(cm) { + cm.blockComment(Pos(1, 2), Pos(1, 13), {fullLines: false}); + }, simpleProg, "function foo() {\n /*return bar;*/\n}"); + + test("indented", "javascript", function(cm) { + cm.lineComment(Pos(1, 0), Pos(2), {indent: true}); + }, simpleProg, "function foo() {\n // return bar;\n // }"); +})(); diff --git a/test/driver.js b/test/driver.js index aba427702e..ae79c328fe 100644 --- a/test/driver.js +++ b/test/driver.js @@ -23,8 +23,9 @@ function test(name, run, expectedFail) { tests.push({name: name, func: run, expectedFail: expectedFail}); return name; } +var namespace = ""; function testCM(name, run, opts, expectedFail) { - return test("core_" + name, function() { + return test(namespace + name, function() { var place = document.getElementById("testground"), cm = CodeMirror(place, opts); var successful = false; try { diff --git a/test/index.html b/test/index.html index d397e8f7a2..816cef5948 100644 --- a/test/index.html +++ b/test/index.html @@ -9,6 +9,7 @@ + @@ -53,6 +54,7 @@

    CodeMirror: Test Suite

    + diff --git a/test/test.js b/test/test.js index 93d899a66e..637f6e79ca 100644 --- a/test/test.js +++ b/test/test.js @@ -32,6 +32,8 @@ var opera_version = opera && navigator.userAgent.match(/Version\/(\d+\.\d+)/); if (opera_version) opera_version = Number(opera_version); var opera_lt10 = opera && (!opera_version || opera_version < 10); +namespace = "core_"; + test("core_fromTextArea", function() { var te = document.getElementById("code"); te.value = "CONTENT"; From 3095862234bed78cb2dda2581ffe17cad5878a63 Mon Sep 17 00:00:00 2001 From: Marijn Haverbeke Date: Mon, 13 May 2013 13:53:18 +0200 Subject: [PATCH 1078/5780] [comment addon] Fix lint errors --- addon/comment/comment.js | 5 ++--- addon/lint/lint.js | 10 +++++----- 2 files changed, 7 insertions(+), 8 deletions(-) diff --git a/addon/comment/comment.js b/addon/comment/comment.js index 2963268a72..ae42b6bcd5 100644 --- a/addon/comment/comment.js +++ b/addon/comment/comment.js @@ -1,6 +1,6 @@ (function() { "use strict"; - + var noOptions = {}; var nonWS = /[^\s\u00a0]/; var Pos = CodeMirror.Pos; @@ -34,7 +34,6 @@ self.operation(function() { if (options.indent) { - var baseCol = CodeMirror.countColumn(firstLine, null, tabSize); var baseString = firstLine.slice(0, firstNonWS(firstLine)); for (var i = from.line; i < end; ++i) { var line = self.getLine(i), cut = baseString.length; @@ -112,7 +111,7 @@ }); return true; } - + // Try block comments var startString = options.blockCommentStart || mode.blockCommentStart; var endString = options.blockCommentEnd || mode.blockCommentEnd; diff --git a/addon/lint/lint.js b/addon/lint/lint.js index ca05da76ef..29fd8d9c3c 100644 --- a/addon/lint/lint.js +++ b/addon/lint/lint.js @@ -105,11 +105,11 @@ CodeMirror.validate = (function() { } function startLinting(cm) { - var state = cm._lintState, options = state.options; - if (options.async) - options.getAnnotations(cm, updateLinting, options); - else - updateLinting(cm, options.getAnnotations(cm.getValue())); + var state = cm._lintState, options = state.options; + if (options.async) + options.getAnnotations(cm, updateLinting, options); + else + updateLinting(cm, options.getAnnotations(cm.getValue())); } function updateLinting(cm, annotationsNotSorted) { From 669c8dd45baff0193e3ac78c325c5d903af74c15 Mon Sep 17 00:00:00 2001 From: Marijn Haverbeke Date: Mon, 13 May 2013 14:05:30 +0200 Subject: [PATCH 1079/5780] [comment addon] Another lint fix --- addon/comment/comment.js | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/addon/comment/comment.js b/addon/comment/comment.js index ae42b6bcd5..4f590f2870 100644 --- a/addon/comment/comment.js +++ b/addon/comment/comment.js @@ -26,7 +26,7 @@ } return; } - var tabSize = self.getOption("tabSize"), firstLine = self.getLine(from.line); + var firstLine = self.getLine(from.line); if (firstLine == null) return; var end = Math.min(to.ch != 0 || to.line == from.line ? to.line + 1 : to.line, self.lastLine() + 1); var pad = options.padding == null ? " " : options.padding; From 373cb16763574e3d1d31847f2fb911472d06114d Mon Sep 17 00:00:00 2001 From: Marijn Haverbeke Date: Mon, 13 May 2013 14:11:59 +0200 Subject: [PATCH 1080/5780] Fix context menu select-all in IE9+ Closes #1522 --- lib/codemirror.js | 16 +++++++++++----- 1 file changed, 11 insertions(+), 5 deletions(-) diff --git a/lib/codemirror.js b/lib/codemirror.js index 699f55a4c5..fec1d582d9 100644 --- a/lib/codemirror.js +++ b/lib/codemirror.js @@ -2097,6 +2097,13 @@ window.CodeMirror = (function() { // Adds "Select all" to context menu in FF if (posEq(sel.from, sel.to)) display.input.value = display.prevInput = " "; + function prepareSelectAllHack() { + if (display.input.selectionStart != null) { + var extval = display.input.value = " " + (posEq(sel.from, sel.to) ? "" : display.input.value); + display.prevInput = " "; + display.input.selectionStart = 1; display.input.selectionEnd = extval.length; + } + } function rehide() { display.inputDiv.style.position = "relative"; display.input.style.cssText = oldCSS; @@ -2104,12 +2111,10 @@ window.CodeMirror = (function() { slowPoll(cm); // Try to detect the user choosing select-all - if (display.input.selectionStart != null && (!ie || ie_lt9)) { + if (display.input.selectionStart != null) { + if (!ie || ie_lt9) prepareSelectAllHack(); clearTimeout(detectingSelectAll); - var extval = display.input.value = " " + (posEq(sel.from, sel.to) ? "" : display.input.value), i = 0; - display.prevInput = " "; - display.input.selectionStart = 1; display.input.selectionEnd = extval.length; - var poll = function(){ + var i = 0, poll = function(){ if (display.prevInput == " " && display.input.selectionStart == 0) operation(cm, commands.selectAll)(cm); else if (i++ < 10) detectingSelectAll = setTimeout(poll, 500); @@ -2119,6 +2124,7 @@ window.CodeMirror = (function() { } } + if (ie && !ie_lt9) prepareSelectAllHack(); if (captureMiddleClick) { e_stop(e); var mouseup = function() { From fb6df35b2e9c79d6fe4b834d92b8f7ed0407f385 Mon Sep 17 00:00:00 2001 From: Marijn Haverbeke Date: Mon, 13 May 2013 16:27:06 +0200 Subject: [PATCH 1081/5780] [less mode] Remove fixed list of tags It makes no sense. Issue #1466 --- mode/less/less.js | 8 -------- 1 file changed, 8 deletions(-) diff --git a/mode/less/less.js b/mode/less/less.js index 6df4790995..09f510e032 100644 --- a/mode/less/less.js +++ b/mode/less/less.js @@ -7,12 +7,6 @@ CodeMirror.defineMode("less", function(config) { var indentUnit = config.indentUnit, type; function ret(style, tp) {type = tp; return style;} - //html tags - var tags = "a abbr acronym address applet area article aside audio b base basefont bdi bdo big blockquote body br button canvas caption cite code col colgroup command datalist dd del details dfn dir div dl dt em embed fieldset figcaption figure font footer form frame frameset h1 h2 h3 h4 h5 h6 head header hgroup hr html i iframe img input ins keygen kbd label legend li link map mark menu meta meter nav noframes noscript object ol optgroup option output p param pre progress q rp rt ruby s samp script section select small source span strike strong style sub summary sup table tbody td textarea tfoot th thead time title tr track tt u ul var video wbr".split(' '); - - function inTagsArray(val){ - for(var i=0; i Date: Mon, 13 May 2013 22:43:29 +0200 Subject: [PATCH 1082/5780] [vim keymap] Add actionArgs for command 'i' 'I' --- keymap/vim.js | 11 +++++++---- 1 file changed, 7 insertions(+), 4 deletions(-) diff --git a/keymap/vim.js b/keymap/vim.js index 48276836ca..4d7833f14e 100644 --- a/keymap/vim.js +++ b/keymap/vim.js @@ -245,9 +245,10 @@ actionArgs: { insertAt: 'charAfter' }}, { keys: ['A'], type: 'action', action: 'enterInsertMode', actionArgs: { insertAt: 'eol' }}, - { keys: ['i'], type: 'action', action: 'enterInsertMode' }, + { keys: ['i'], type: 'action', action: 'enterInsertMode', + actionArgs: { insertAt: 'inplace' }}, { keys: ['I'], type: 'action', action: 'enterInsertMode', - motion: 'moveToFirstNonWhiteSpaceCharacter' }, + actionArgs: { insertAt: 'firstNonBlank' }}, { keys: ['o'], type: 'action', action: 'newLineAndEnterInsertMode', actionArgs: { after: true }}, { keys: ['O'], type: 'action', action: 'newLineAndEnterInsertMode', @@ -368,7 +369,7 @@ return false; } - var circularJumpList = function() { + var createCircularJumpList = function() { var size = 100; var pointer = -1; var head = 0; @@ -442,7 +443,7 @@ searchQuery: null, // Whether we are searching backwards. searchIsReversed: false, - jumpList: circularJumpList(), + jumpList: createCircularJumpList(), // Recording latest f, t, F or T motion command. lastChararacterSearch: {increment:0, forward:true, selectedCharacter:''}, registerController: new RegisterController({}) @@ -1528,6 +1529,8 @@ cm.setCursor(cursor); } else if (insertAt == 'charAfter') { cm.setCursor(offsetCursor(cm.getCursor(), 0, 1)); + } else if (insertAt == 'firstNonBlank') { ++ cm.setCursor(motions.moveToFirstNonWhiteSpaceCharacter(cm)); } cm.setOption('keyMap', 'vim-insert'); }, From 1f3e26ab6869f0a3de97f5cf01e37c93e54f4a7f Mon Sep 17 00:00:00 2001 From: Marijn Haverbeke Date: Tue, 14 May 2013 10:51:09 +0200 Subject: [PATCH 1083/5780] [vim keymap] Remove stray plus character Issue #1525 --- keymap/vim.js | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/keymap/vim.js b/keymap/vim.js index 4d7833f14e..3640e65be8 100644 --- a/keymap/vim.js +++ b/keymap/vim.js @@ -1530,7 +1530,7 @@ } else if (insertAt == 'charAfter') { cm.setCursor(offsetCursor(cm.getCursor(), 0, 1)); } else if (insertAt == 'firstNonBlank') { -+ cm.setCursor(motions.moveToFirstNonWhiteSpaceCharacter(cm)); + cm.setCursor(motions.moveToFirstNonWhiteSpaceCharacter(cm)); } cm.setOption('keyMap', 'vim-insert'); }, From b5e0401e4424084e39cd3762feb284c01cc9caf6 Mon Sep 17 00:00:00 2001 From: Marijn Haverbeke Date: Tue, 14 May 2013 11:13:46 +0200 Subject: [PATCH 1084/5780] Allow mode spec objects to have mime types as name Other properties will be added/overridden in the config object for the mime type. --- lib/codemirror.js | 9 +++++++-- 1 file changed, 7 insertions(+), 2 deletions(-) diff --git a/lib/codemirror.js b/lib/codemirror.js index fec1d582d9..15a092f9c4 100644 --- a/lib/codemirror.js +++ b/lib/codemirror.js @@ -3180,10 +3180,15 @@ window.CodeMirror = (function() { }; CodeMirror.resolveMode = function(spec) { - if (typeof spec == "string" && mimeModes.hasOwnProperty(spec)) + if (typeof spec == "string" && mimeModes.hasOwnProperty(spec)) { spec = mimeModes[spec]; - else if (typeof spec == "string" && /^[\w\-]+\/[\w\-]+\+xml$/.test(spec)) + } else if (typeof spec.name == "string" && mimeModes.hasOwnProperty(spec.name)) { + var found = mimeModes[spec.name]; + spec = createObj(found, spec); + spec.name = found.name; + } else if (typeof spec == "string" && /^[\w\-]+\/[\w\-]+\+xml$/.test(spec)) { return CodeMirror.resolveMode("application/xml"); + } if (typeof spec == "string") return {name: spec}; else return spec || {name: "null"}; }; From c3cb802df2e001bc0b2d1b78766a603a48ec2d1b Mon Sep 17 00:00:00 2001 From: Marijn Haverbeke Date: Tue, 14 May 2013 11:18:16 +0200 Subject: [PATCH 1085/5780] [clike mode] Fix interaction of dontAlignCalls and statementIndentUnit Closes #1518 --- mode/clike/clike.js | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/mode/clike/clike.js b/mode/clike/clike.js index 83bf8019f2..09621bc08d 100644 --- a/mode/clike/clike.js +++ b/mode/clike/clike.js @@ -150,8 +150,8 @@ CodeMirror.defineMode("clike", function(config, parserConfig) { if (ctx.type == "statement" && firstChar == "}") ctx = ctx.prev; var closing = firstChar == ctx.type; if (ctx.type == "statement") return ctx.indented + (firstChar == "{" ? 0 : statementIndentUnit); - else if (dontAlignCalls && ctx.type == ")" && !closing) return ctx.indented + statementIndentUnit; - else if (ctx.align) return ctx.column + (closing ? 0 : 1); + else if (ctx.align && (!dontAlignCalls || ctx.type != ")")) return ctx.column + (closing ? 0 : 1); + else if (ctx.type == ")" && !closing) return ctx.indented + statementIndentUnit; else return ctx.indented + (closing ? 0 : indentUnit); }, From 00504d8a36c1cd4cb5699601bc49ca1012c2fe78 Mon Sep 17 00:00:00 2001 From: Marijn Haverbeke Date: Tue, 14 May 2013 11:25:48 +0200 Subject: [PATCH 1086/5780] Don't crash when getMode is passed null --- lib/codemirror.js | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/lib/codemirror.js b/lib/codemirror.js index 15a092f9c4..7f5f672adb 100644 --- a/lib/codemirror.js +++ b/lib/codemirror.js @@ -3182,7 +3182,7 @@ window.CodeMirror = (function() { CodeMirror.resolveMode = function(spec) { if (typeof spec == "string" && mimeModes.hasOwnProperty(spec)) { spec = mimeModes[spec]; - } else if (typeof spec.name == "string" && mimeModes.hasOwnProperty(spec.name)) { + } else if (spec && typeof spec.name == "string" && mimeModes.hasOwnProperty(spec.name)) { var found = mimeModes[spec.name]; spec = createObj(found, spec); spec.name = found.name; From ea523decb8bd3562bfaae4891903c46e9c190f87 Mon Sep 17 00:00:00 2001 From: Marijn Haverbeke Date: Wed, 15 May 2013 13:03:59 +0200 Subject: [PATCH 1087/5780] Shuffle real-world uses on front page, add Bitbucket and JSFiddle --- doc/realworld.html | 2 ++ index.html | 39 ++++++++++++++++++++------------------- 2 files changed, 22 insertions(+), 19 deletions(-) diff --git a/doc/realworld.html b/doc/realworld.html index c3f2c814fc..941d926dbe 100644 --- a/doc/realworld.html +++ b/doc/realworld.html @@ -25,6 +25,7 @@

    { } CodeMi
  • Adobe Brackets (code editor)
  • Amber (JavaScript-based Smalltalk system)
  • APEye (tool for testing & documenting APIs)
  • +
  • Bitbucket (code hosting)
  • Blogger's template editor
  • BlueGriffon (HTML editor)
  • Cargo Collective (creative publishing platform)
  • @@ -66,6 +67,7 @@

    { } CodeMi
  • Joomla plugin
  • jQuery fundamentals (interactive tutorial)
  • jsbin.com (JS playground)
  • +
  • jsfiddle.com (another JS playground)
  • JSHint (JS linter)
  • Jumpseller (online store builder)
  • kl1p (paste service)
  • diff --git a/index.html b/index.html index 1dba41984b..a0d3d5b48f 100644 --- a/index.html +++ b/index.html @@ -120,28 +120,29 @@

    Usage demos:

    Real-world uses:

    From 36fe7d5b4a3ffdd804933a323a45f23ae0885e20 Mon Sep 17 00:00:00 2001 From: lynschinzer Date: Tue, 14 May 2013 23:16:39 +0200 Subject: [PATCH 1088/5780] [vim keymap] Change defaultKeymap notation format --- keymap/vim.js | 108 ++++++++++++++++++----------------------------- test/vim_test.js | 62 +++++++++++++-------------- 2 files changed, 73 insertions(+), 97 deletions(-) diff --git a/keymap/vim.js b/keymap/vim.js index 3640e65be8..9c0197faaa 100644 --- a/keymap/vim.js +++ b/keymap/vim.js @@ -58,26 +58,26 @@ var defaultKeymap = [ // Key to key mapping. This goes first to make it possible to override // existing mappings. - { keys: ['Left'], type: 'keyToKey', toKeys: ['h'] }, - { keys: ['Right'], type: 'keyToKey', toKeys: ['l'] }, - { keys: ['Up'], type: 'keyToKey', toKeys: ['k'] }, - { keys: ['Down'], type: 'keyToKey', toKeys: ['j'] }, - { keys: ['Space'], type: 'keyToKey', toKeys: ['l'] }, - { keys: ['Backspace'], type: 'keyToKey', toKeys: ['h'] }, - { keys: ['Ctrl-Space'], type: 'keyToKey', toKeys: ['W'] }, - { keys: ['Ctrl-Backspace'], type: 'keyToKey', toKeys: ['B'] }, - { keys: ['Shift-Space'], type: 'keyToKey', toKeys: ['w'] }, - { keys: ['Shift-Backspace'], type: 'keyToKey', toKeys: ['b'] }, - { keys: ['Ctrl-n'], type: 'keyToKey', toKeys: ['j'] }, - { keys: ['Ctrl-p'], type: 'keyToKey', toKeys: ['k'] }, - { keys: ['Ctrl-['], type: 'keyToKey', toKeys: ['Esc'] }, - { keys: ['Ctrl-c'], type: 'keyToKey', toKeys: ['Esc'] }, + { keys: [''], type: 'keyToKey', toKeys: ['h'] }, + { keys: [''], type: 'keyToKey', toKeys: ['l'] }, + { keys: [''], type: 'keyToKey', toKeys: ['k'] }, + { keys: [''], type: 'keyToKey', toKeys: ['j'] }, + { keys: [''], type: 'keyToKey', toKeys: ['l'] }, + { keys: [''], type: 'keyToKey', toKeys: ['h'] }, + { keys: [''], type: 'keyToKey', toKeys: ['W'] }, + { keys: [''], type: 'keyToKey', toKeys: ['B'] }, + { keys: [''], type: 'keyToKey', toKeys: ['w'] }, + { keys: [''], type: 'keyToKey', toKeys: ['b'] }, + { keys: [''], type: 'keyToKey', toKeys: ['j'] }, + { keys: [''], type: 'keyToKey', toKeys: ['k'] }, + { keys: ['C-['], type: 'keyToKey', toKeys: [''] }, + { keys: [''], type: 'keyToKey', toKeys: [''] }, { keys: ['s'], type: 'keyToKey', toKeys: ['c', 'l'] }, { keys: ['S'], type: 'keyToKey', toKeys: ['c', 'c'] }, - { keys: ['Home'], type: 'keyToKey', toKeys: ['0'] }, - { keys: ['End'], type: 'keyToKey', toKeys: ['$'] }, - { keys: ['PageUp'], type: 'keyToKey', toKeys: ['Ctrl-b'] }, - { keys: ['PageDown'], type: 'keyToKey', toKeys: ['Ctrl-f'] }, + { keys: [''], type: 'keyToKey', toKeys: ['0'] }, + { keys: [''], type: 'keyToKey', toKeys: ['$'] }, + { keys: [''], type: 'keyToKey', toKeys: [''] }, + { keys: [''], type: 'keyToKey', toKeys: [''] }, // Motions { keys: ['H'], type: 'motion', motion: 'moveToTopLine', @@ -136,14 +136,14 @@ motionArgs: { forward: false, toJumplist: true }}, { keys: ['}'], type: 'motion', motion: 'moveByParagraph', motionArgs: { forward: true, toJumplist: true }}, - { keys: ['Ctrl-f'], type: 'motion', + { keys: [''], type: 'motion', motion: 'moveByPage', motionArgs: { forward: true }}, - { keys: ['Ctrl-b'], type: 'motion', + { keys: [''], type: 'motion', motion: 'moveByPage', motionArgs: { forward: false }}, - { keys: ['Ctrl-d'], type: 'motion', + { keys: [''], type: 'motion', motion: 'moveByScroll', motionArgs: { forward: true, explicitRepeat: true }}, - { keys: ['Ctrl-u'], type: 'motion', + { keys: [''], type: 'motion', motion: 'moveByScroll', motionArgs: { forward: false, explicitRepeat: true }}, { keys: ['g', 'g'], type: 'motion', @@ -237,9 +237,9 @@ { keys: ['~'], type: 'operatorMotion', operator: 'swapcase', motion: 'moveByCharacters', motionArgs: { forward: true }}, // Actions - { keys: ['Ctrl-i'], type: 'action', action: 'jumpListWalk', + { keys: [''], type: 'action', action: 'jumpListWalk', actionArgs: { forward: true }}, - { keys: ['Ctrl-o'], type: 'action', action: 'jumpListWalk', + { keys: [''], type: 'action', action: 'jumpListWalk', actionArgs: { forward: false }}, { keys: ['a'], type: 'action', action: 'enterInsertMode', actionArgs: { insertAt: 'charAfter' }}, @@ -264,7 +264,7 @@ { keys: ['r', 'character'], type: 'action', action: 'replace' }, { keys: ['R'], type: 'action', action: 'enterReplaceMode' }, { keys: ['u'], type: 'action', action: 'undo' }, - { keys: ['Ctrl-r'], type: 'action', action: 'redo' }, + { keys: [''], type: 'action', action: 'redo' }, { keys: ['m', 'character'], type: 'action', action: 'setMark' }, { keys: ['\"', 'character'], type: 'action', action: 'setRegister' }, { keys: ['z', 'z'], type: 'action', action: 'scrollToCursor', @@ -274,7 +274,7 @@ motion: 'moveToFirstNonWhiteSpaceCharacter' }, { keys: ['z', 't'], type: 'action', action: 'scrollToCursor', actionArgs: { position: 'top' }}, - { keys: ['z', 'Enter'], type: 'action', action: 'scrollToCursor', + { keys: ['z', ''], type: 'action', action: 'scrollToCursor', actionArgs: { position: 'top' }, motion: 'moveToFirstNonWhiteSpaceCharacter' }, { keys: ['z', '-'], type: 'action', action: 'scrollToCursor', @@ -283,9 +283,9 @@ actionArgs: { position: 'bottom' }, motion: 'moveToFirstNonWhiteSpaceCharacter' }, { keys: ['.'], type: 'action', action: 'repeatLastEdit' }, - { keys: ['Ctrl-a'], type: 'action', action: 'incrementNumberToken', + { keys: [''], type: 'action', action: 'incrementNumberToken', actionArgs: {increase: true, backtrack: false}}, - { keys: ['Ctrl-x'], type: 'action', action: 'incrementNumberToken', + { keys: [''], type: 'action', action: 'incrementNumberToken', actionArgs: {increase: false, backtrack: false}}, // Text object motions { keys: ['a', 'character'], type: 'motion', @@ -509,7 +509,7 @@ handleKey: function(cm, key) { var command; var vim = getVimState(cm); - if (key == 'Esc') { + if (key == '') { // Clear input state and get back to normal mode. vim.inputState = new InputState(); if (vim.visualMode) { @@ -717,10 +717,10 @@ inputState.selectedCharacter = keys[keys.length - 1]; if(inputState.selectedCharacter.length>1){ switch(inputState.selectedCharacter){ - case "Enter": + case "": inputState.selectedCharacter='\n'; break; - case "Space": + case "": inputState.selectedCharacter=' '; break; default: @@ -2930,41 +2930,14 @@ // Converts a key string sequence of the form abd into Vim's // keymap representation. function parseKeyString(str) { - var idx = 0; + var key, match; var keys = []; - while (idx < str.length) { - if (str.charAt(idx) != '<') { - keys.push(str.charAt(idx)); - idx++; - continue; - } - // Vim key notation here means desktop Vim key-notation. - // See :help key-notation in desktop Vim. - var vimKeyNotationStart = ++idx; - while (str.charAt(idx++) != '>') {} - var vimKeyNotation = str.substring(vimKeyNotationStart, idx - 1); - var mod=''; - var match = (/^C-(.+)$/).exec(vimKeyNotation); - if (match) { - mod='Ctrl-'; - vimKeyNotation=match[1]; - } - var key; - switch (vimKeyNotation) { - case 'BS': - key = 'Backspace'; - break; - case 'CR': - key = 'Enter'; - break; - case 'Del': - key = 'Delete'; - break; - default: - key = vimKeyNotation; - break; - } - keys.push(mod + key); + while (str) { + match = (/<\w+-.+?>|<\w+>|./).exec(str); + if(match === null)break; + key = match[0]; + str = str.substring(match.index + key.length); + keys.push(key); } return keys; } @@ -3148,8 +3121,11 @@ } if (modifier) { // Vim will parse modifier+key combination as a single key. - key = modifier + '-' + key; + key = modifier.charAt(0) + '-' + key; } + var specialKey = ({Enter:'CR',Backspace:'BS',Delete:'Del'})[key]; + key = specialKey ? specialKey : key; + key = key.length > 1 ? '<'+ key + '>' : key; vim.handleKey(cm, key); } diff --git a/test/vim_test.js b/test/vim_test.js index f2304b99cf..83bbec9ac6 100644 --- a/test/vim_test.js +++ b/test/vim_test.js @@ -190,32 +190,32 @@ function testJumplist(name, keys, endPos, startPos, dialog) { helpers.assertCursorAt(endPos); }, {value: jumplistScene}); }; -testJumplist('jumplist_H', ['H', 'Ctrl-o'], [5,2], [5,2]); -testJumplist('jumplist_M', ['M', 'Ctrl-o'], [2,2], [2,2]); -testJumplist('jumplist_L', ['L', 'Ctrl-o'], [2,2], [2,2]); -testJumplist('jumplist_[[', ['[', '[', 'Ctrl-o'], [5,2], [5,2]); -testJumplist('jumplist_]]', [']', ']', 'Ctrl-o'], [2,2], [2,2]); -testJumplist('jumplist_G', ['G', 'Ctrl-o'], [5,2], [5,2]); -testJumplist('jumplist_gg', ['g', 'g', 'Ctrl-o'], [5,2], [5,2]); -testJumplist('jumplist_%', ['%', 'Ctrl-o'], [1,5], [1,5]); -testJumplist('jumplist_{', ['{', 'Ctrl-o'], [1,5], [1,5]); -testJumplist('jumplist_}', ['}', 'Ctrl-o'], [1,5], [1,5]); -testJumplist('jumplist_\'', ['m', 'a', 'h', '\'', 'a', 'h', 'Ctrl-i'], [1,5], [1,5]); -testJumplist('jumplist_`', ['m', 'a', 'h', '`', 'a', 'h', 'Ctrl-i'], [1,5], [1,5]); -testJumplist('jumplist_*_cachedCursor', ['*', 'Ctrl-o'], [1,3], [1,3]); -testJumplist('jumplist_#_cachedCursor', ['#', 'Ctrl-o'], [1,3], [1,3]); -testJumplist('jumplist_n', ['#', 'n', 'Ctrl-o'], [1,1], [2,3]); -testJumplist('jumplist_N', ['#', 'N', 'Ctrl-o'], [1,1], [2,3]); -testJumplist('jumplist_repeat_', ['*', '*', '*', '3', 'Ctrl-o'], [2,3], [2,3]); -testJumplist('jumplist_repeat_', ['*', '*', '*', '3', 'Ctrl-o', '2', 'Ctrl-i'], [5,0], [2,3]); -testJumplist('jumplist_repeated_motion', ['3', '*', 'Ctrl-o'], [2,3], [2,3]); -testJumplist('jumplist_/', ['/', 'Ctrl-o'], [2,3], [2,3], 'dialog'); -testJumplist('jumplist_?', ['?', 'Ctrl-o'], [2,3], [2,3], 'dialog'); +testJumplist('jumplist_H', ['H', ''], [5,2], [5,2]); +testJumplist('jumplist_M', ['M', ''], [2,2], [2,2]); +testJumplist('jumplist_L', ['L', ''], [2,2], [2,2]); +testJumplist('jumplist_[[', ['[', '[', ''], [5,2], [5,2]); +testJumplist('jumplist_]]', [']', ']', ''], [2,2], [2,2]); +testJumplist('jumplist_G', ['G', ''], [5,2], [5,2]); +testJumplist('jumplist_gg', ['g', 'g', ''], [5,2], [5,2]); +testJumplist('jumplist_%', ['%', ''], [1,5], [1,5]); +testJumplist('jumplist_{', ['{', ''], [1,5], [1,5]); +testJumplist('jumplist_}', ['}', ''], [1,5], [1,5]); +testJumplist('jumplist_\'', ['m', 'a', 'h', '\'', 'a', 'h', ''], [1,5], [1,5]); +testJumplist('jumplist_`', ['m', 'a', 'h', '`', 'a', 'h', ''], [1,5], [1,5]); +testJumplist('jumplist_*_cachedCursor', ['*', ''], [1,3], [1,3]); +testJumplist('jumplist_#_cachedCursor', ['#', ''], [1,3], [1,3]); +testJumplist('jumplist_n', ['#', 'n', ''], [1,1], [2,3]); +testJumplist('jumplist_N', ['#', 'N', ''], [1,1], [2,3]); +testJumplist('jumplist_repeat_', ['*', '*', '*', '3', ''], [2,3], [2,3]); +testJumplist('jumplist_repeat_', ['*', '*', '*', '3', '', '2', ''], [5,0], [2,3]); +testJumplist('jumplist_repeated_motion', ['3', '*', ''], [2,3], [2,3]); +testJumplist('jumplist_/', ['/', ''], [2,3], [2,3], 'dialog'); +testJumplist('jumplist_?', ['?', ''], [2,3], [2,3], 'dialog'); testJumplist('jumplist_skip_delted_mark', - ['*', 'n', 'n', 'k', 'd', 'k', 'Ctrl-o', 'Ctrl-o', 'Ctrl-o'], + ['*', 'n', 'n', 'k', 'd', 'k', '', '', ''], [0,2], [0,2]); testJumplist('jumplist_skip_delted_mark', - ['*', 'n', 'n', 'k', 'd', 'k', 'Ctrl-o', 'Ctrl-i', 'Ctrl-i'], + ['*', 'n', 'n', 'k', 'd', 'k', '', '', ''], [1,0], [0,2]); /** * @param name Name of the test @@ -970,22 +970,22 @@ testVim('Y', function(cm, vim, helpers) { // Action tests testVim('ctrl-a', function(cm, vim, helpers) { cm.setCursor(0, 0); - helpers.doKeys('Ctrl-a'); + helpers.doKeys(''); eq('-9', cm.getValue()); helpers.assertCursorAt(0, 1); - helpers.doKeys('2','Ctrl-a'); + helpers.doKeys('2',''); eq('-7', cm.getValue()); }, {value: '-10'}); testVim('ctrl-x', function(cm, vim, helpers) { cm.setCursor(0, 0); - helpers.doKeys('Ctrl-x'); + helpers.doKeys(''); eq('-1', cm.getValue()); helpers.assertCursorAt(0, 1); - helpers.doKeys('2','Ctrl-x'); + helpers.doKeys('2',''); eq('-3', cm.getValue()); }, {value: '0'}); -testVim('Ctrl-x/Ctrl-a search forward', function(cm, vim, helpers) { - ['Ctrl-x', 'Ctrl-a'].forEach(function(key) { +testVim('/ search forward', function(cm, vim, helpers) { + ['', ''].forEach(function(key) { cm.setCursor(0, 0); helpers.doKeys(key); helpers.assertCursorAt(0, 5); @@ -1103,7 +1103,7 @@ testVim('r', function(cm, vim, helpers) { eq('wuuuet\nanother', cm.getValue(),'3r failed'); helpers.assertCursorAt(0, 3); cm.setCursor(0, 4); - helpers.doKeys('v', 'j', 'h', 'r', 'Space'); + helpers.doKeys('v', 'j', 'h', 'r', ''); eq('wuuu \n her', cm.getValue(),'Replacing selection by space-characters failed'); }, { value: 'wordet\nanother' }); testVim('R', function(cm, vim, helpers) { @@ -2001,7 +2001,7 @@ testVim('ex_api_test', function(cm, vim, helpers) { helpers.doEx(':ext to'); eq(val,'to','Defining ex-command failed'); CodeMirror.Vim.map('',':ext'); - helpers.doKeys('Ctrl-Enter','Space'); + helpers.doKeys('',''); is(res,'Mapping to key failed'); }); // For now, this test needs to be last because it messes up : for future tests. From 9a8282fd1d310b3bf61d101b230b4766a933688c Mon Sep 17 00:00:00 2001 From: Alex Piggott Date: Wed, 15 May 2013 13:14:57 +0200 Subject: [PATCH 1089/5780] [brace-fold addon] Also handle square brackets --- addon/fold/brace-fold.js | 12 +++++++++--- 1 file changed, 9 insertions(+), 3 deletions(-) diff --git a/addon/fold/brace-fold.js b/addon/fold/brace-fold.js index dc78883f35..efdffb877c 100644 --- a/addon/fold/brace-fold.js +++ b/addon/fold/brace-fold.js @@ -3,17 +3,23 @@ CodeMirror.braceRangeFinder = function(cm, start) { var at = lineText.length, startChar, tokenType; for (; at > 0;) { var found = lineText.lastIndexOf("{", at); - if (found < start.ch) break; + var startToken = '{', endToken = '}'; + if (found < start.ch) { + found = lineText.lastIndexOf("[", at); + if (found < start.ch) break; + startToken = '['; endToken = ']'; + } + tokenType = cm.getTokenAt(CodeMirror.Pos(line, found + 1)).type; if (!/^(comment|string)/.test(tokenType)) { startChar = found; break; } at = found - 1; } - if (startChar == null || lineText.lastIndexOf("}") > startChar) return; + if (startChar == null || lineText.lastIndexOf(startToken) > startChar) return; var count = 1, lastLine = cm.lineCount(), end, endCh; outer: for (var i = line + 1; i < lastLine; ++i) { var text = cm.getLine(i), pos = 0; for (;;) { - var nextOpen = text.indexOf("{", pos), nextClose = text.indexOf("}", pos); + var nextOpen = text.indexOf(startToken, pos), nextClose = text.indexOf(endToken, pos); if (nextOpen < 0) nextOpen = text.length; if (nextClose < 0) nextClose = text.length; pos = Math.min(nextOpen, nextClose); From 6b7e2953febaba158e6128b7a3d07b7b02c88636 Mon Sep 17 00:00:00 2001 From: Andrey Lushnikov Date: Mon, 13 May 2013 19:12:09 +0400 Subject: [PATCH 1090/5780] mark-selection addon improvement Addresses issue #1484 and speeds up mark-selection add-on by maintaining text selection coverage with small chunks of text marks. --- addon/selection/mark-selection.js | 135 +++++++++++++++++++++++++++--- 1 file changed, 125 insertions(+), 10 deletions(-) diff --git a/addon/selection/mark-selection.js b/addon/selection/mark-selection.js index d7ff30c9a9..80eb9def0f 100644 --- a/addon/selection/mark-selection.js +++ b/addon/selection/mark-selection.js @@ -13,22 +13,137 @@ cm.on("cursorActivity", updateSelectedText); } else if (!val && prev) { cm.off("cursorActivity", updateSelectedText); - clearSelectedText(cm); - delete cm._selectionMark; + removeTextMarks(cm); } }); - function clearSelectedText(cm) { - if (cm._selectionMark) cm._selectionMark.clear(); + function cmp(pos1, pos2) { + return pos1.line !== pos2.line ? pos1.line - pos2.line : pos1.ch - pos2.ch; + } + + function removeTextMarks(cm) { + if (cm._selectionMarks) { + for(var i = 0; i < cm._selectionMarks.length; ++i) { + cm._selectionMarks[i].clear(); + } + } + cm._selectionMarks = null; + } + + function markText(cm, start, end) { + if (cmp(start, end) === 0) + return; + var opt = {className: "CodeMirror-selectedtext"}; + cm._selectionMarks.push(cmp(start, end) <= 0 ? cm.markText(start, end, opt) : cm.markText(end, start, opt)); + } + + var CHUNK_SIZE = 8; + var Pos = CodeMirror.Pos; + + function coverRange(cm, anchor, head) { + if (Math.abs(anchor.line - head.line) < CHUNK_SIZE) { + markText(cm, anchor, head); + return; + } + if (head.line > anchor.line) { + markText(cm, anchor, Pos(anchor.line + CHUNK_SIZE, 0)); + for(var line = anchor.line + CHUNK_SIZE; line + CHUNK_SIZE < head.line; line += CHUNK_SIZE) + markText(cm, Pos(line, 0), Pos(line + CHUNK_SIZE, 0)); + markText(cm, Pos(line, 0), head); + } else { + markText(cm, anchor, Pos(anchor.line - CHUNK_SIZE + 1, 0)); + for(var line = anchor.line - CHUNK_SIZE + 1; line - CHUNK_SIZE > head.line; line -= CHUNK_SIZE) + markText(cm, Pos(line, 0), Pos(line - CHUNK_SIZE, 0)); + markText(cm, Pos(line, 0), head); + } + } + + function createInitialCoverage(cm) { + var anchor = cm.getCursor("anchor"); + var head = cm.getCursor("head"); + cm._selectionMarks = []; + coverRange(cm, anchor, head); + } + + function getCoveredRange(cm) { + var first = cm._selectionMarks[0].find(); + var last = cm._selectionMarks[cm._selectionMarks.length - 1].find(); + if (!first || !last) + return null; + var reversed = cmp(first.from, last.to) >= 0; + return reversed ? {anchor: first.to, head: last.from, reversed: reversed} : {anchor: first.from, head: last.to, reversed: reversed}; + } + + function incrementalCoverageUpdate(cm) { + var anchor = cm.getCursor("anchor"); + var head = cm.getCursor("head"); + var reversed = cmp(head, anchor) < 0; + var lastSelection = getCoveredRange(cm); + // if the anchor of selection moved or selection changed direction - remove everything and construct from scratch. + if (!lastSelection || cmp(anchor, lastSelection.anchor) !== 0 || (reversed ^ lastSelection.reversed)) { + removeTextMarks(cm); + createInitialCoverage(cm); + return; + } + // fast return if nothing changed + if (cmp(head, lastSelection.head) === 0) + return; + + // if only column changed then update last text mark + if (head.line === lastSelection.head.line) { + var lastMark = cm._selectionMarks.pop(); + var position = lastMark.find(); + lastMark.clear(); + markText(cm, reversed ? position.to : position.from, head); + return; + } + + if (!reversed) { + // if selection shrinks + if (head.line < lastSelection.head.line) { + var textMark, + position; + do { + textMark = cm._selectionMarks.pop(); + position = textMark.find(); + textMark.clear(); + } while (cm._selectionMarks.length && position.from.line >= head.line); + markText(cm, position.from, head); + } else { + var textMark = cm._selectionMarks.pop(); + var position = textMark.find(); + textMark.clear(); + coverRange(cm, position.from, head); + } + } else { + // selection getting smaller + if (head.line > lastSelection.head.line) { + var textMark, + position; + do { + textMark = cm._selectionMarks.pop(); + position = textMark.find(); + textMark.clear(); + } while (cm._selectionMarks.length && position.to.line <= head.line); + markText(cm, position.to, head); + } else { + var textMark = cm._selectionMarks.pop(); + var position = textMark.find(); + coverRange(cm, position.to, head); + textMark.clear(); + } + } } function updateSelectedText(cm) { - clearSelectedText(cm); + cm.operation(function() { + if (!cm.somethingSelected()) + return removeTextMarks(cm); + + if (!cm._selectionMarks || !cm._selectionMarks.length) + return createInitialCoverage(cm); - if (cm.somethingSelected()) - cm._selectionMark = cm.markText(cm.getCursor("start"), cm.getCursor("end"), - {className: "CodeMirror-selectedtext"}); - else - cm._selectionMark = null; + incrementalCoverageUpdate(cm); + }); } })(); From 5a5f9f4e25b1a88de413fbca295881beacd5b82a Mon Sep 17 00:00:00 2001 From: Marijn Haverbeke Date: Thu, 16 May 2013 11:32:46 +0200 Subject: [PATCH 1091/5780] [mark-selection addon] Simplify Issue #1523 --- addon/selection/mark-selection.js | 183 ++++++++++++------------------ 1 file changed, 71 insertions(+), 112 deletions(-) diff --git a/addon/selection/mark-selection.js b/addon/selection/mark-selection.js index 80eb9def0f..c97776e492 100644 --- a/addon/selection/mark-selection.js +++ b/addon/selection/mark-selection.js @@ -1,7 +1,8 @@ // Because sometimes you need to mark the selected *text*. // // Adds an option 'styleSelectedText' which, when enabled, gives -// selected text the CSS class "CodeMirror-selectedtext". +// selected text the CSS class given as option value, or +// "CodeMirror-selectedtext" when the value is not a string. (function() { "use strict"; @@ -9,141 +10,99 @@ CodeMirror.defineOption("styleSelectedText", false, function(cm, val, old) { var prev = old && old != CodeMirror.Init; if (val && !prev) { - updateSelectedText(cm); - cm.on("cursorActivity", updateSelectedText); + cm.state.markedSelection = []; + cm.state.markedSelectionStyle = typeof val == "string" ? val : "CodeMirror-selectedtext"; + reset(cm); + cm.on("cursorActivity", onCursorActivity); + cm.on("change", onChange); } else if (!val && prev) { - cm.off("cursorActivity", updateSelectedText); - removeTextMarks(cm); + cm.off("cursorActivity", onCursorActivity); + cm.off("change", onChange); + clear(cm); + cm.state.markedSelection = cm.state.markedSelectionStyle = null; } }); - function cmp(pos1, pos2) { - return pos1.line !== pos2.line ? pos1.line - pos2.line : pos1.ch - pos2.ch; - } - - function removeTextMarks(cm) { - if (cm._selectionMarks) { - for(var i = 0; i < cm._selectionMarks.length; ++i) { - cm._selectionMarks[i].clear(); - } - } - cm._selectionMarks = null; + function onCursorActivity(cm) { + cm.operation(function() { update(cm); }); } - function markText(cm, start, end) { - if (cmp(start, end) === 0) - return; - var opt = {className: "CodeMirror-selectedtext"}; - cm._selectionMarks.push(cmp(start, end) <= 0 ? cm.markText(start, end, opt) : cm.markText(end, start, opt)); + function onChange(cm) { + if (cm.state.markedSelection.length) + cm.operation(function() { clear(cm); }); } var CHUNK_SIZE = 8; var Pos = CodeMirror.Pos; - function coverRange(cm, anchor, head) { - if (Math.abs(anchor.line - head.line) < CHUNK_SIZE) { - markText(cm, anchor, head); - return; - } - if (head.line > anchor.line) { - markText(cm, anchor, Pos(anchor.line + CHUNK_SIZE, 0)); - for(var line = anchor.line + CHUNK_SIZE; line + CHUNK_SIZE < head.line; line += CHUNK_SIZE) - markText(cm, Pos(line, 0), Pos(line + CHUNK_SIZE, 0)); - markText(cm, Pos(line, 0), head); - } else { - markText(cm, anchor, Pos(anchor.line - CHUNK_SIZE + 1, 0)); - for(var line = anchor.line - CHUNK_SIZE + 1; line - CHUNK_SIZE > head.line; line -= CHUNK_SIZE) - markText(cm, Pos(line, 0), Pos(line - CHUNK_SIZE, 0)); - markText(cm, Pos(line, 0), head); + function cmp(pos1, pos2) { + return pos1.line - pos2.line || pos1.ch - pos2.ch; + } + + function coverRange(cm, from, to, addAt) { + if (cmp(from, to) == 0) return; + var array = cm.state.markedSelection; + var cls = cm.state.markedSelectionStyle; + for (var line = from.line;;) { + var start = line == from.line ? from : Pos(line, 0); + var endLine = line + CHUNK_SIZE, atEnd = endLine >= to.line; + var end = atEnd ? to : Pos(endLine, 0); + var mark = cm.markText(start, end, {className: cls}); + if (addAt == null) array.push(mark); + else array.splice(addAt++, 0, mark); + if (atEnd) break; + line = endLine; } } - function createInitialCoverage(cm) { - var anchor = cm.getCursor("anchor"); - var head = cm.getCursor("head"); - cm._selectionMarks = []; - coverRange(cm, anchor, head); + function clear(cm) { + var array = cm.state.markedSelection; + for (var i = 0; i < array.length; ++i) array[i].clear(); + array.length = 0; } - function getCoveredRange(cm) { - var first = cm._selectionMarks[0].find(); - var last = cm._selectionMarks[cm._selectionMarks.length - 1].find(); - if (!first || !last) - return null; - var reversed = cmp(first.from, last.to) >= 0; - return reversed ? {anchor: first.to, head: last.from, reversed: reversed} : {anchor: first.from, head: last.to, reversed: reversed}; + function reset(cm) { + clear(cm); + var from = cm.getCursor("start"), to = cm.getCursor("end"); + coverRange(cm, from, to); } - function incrementalCoverageUpdate(cm) { - var anchor = cm.getCursor("anchor"); - var head = cm.getCursor("head"); - var reversed = cmp(head, anchor) < 0; - var lastSelection = getCoveredRange(cm); - // if the anchor of selection moved or selection changed direction - remove everything and construct from scratch. - if (!lastSelection || cmp(anchor, lastSelection.anchor) !== 0 || (reversed ^ lastSelection.reversed)) { - removeTextMarks(cm); - createInitialCoverage(cm); - return; - } - // fast return if nothing changed - if (cmp(head, lastSelection.head) === 0) - return; + function update(cm) { + var from = cm.getCursor("start"), to = cm.getCursor("end"); + if (cmp(from, to) == 0) return clear(cm); - // if only column changed then update last text mark - if (head.line === lastSelection.head.line) { - var lastMark = cm._selectionMarks.pop(); - var position = lastMark.find(); - lastMark.clear(); - markText(cm, reversed ? position.to : position.from, head); - return; - } + var array = cm.state.markedSelection; + if (!array.length) return coverRange(cm, from, to); + + var coverStart = array[0].find(), coverEnd = array[array.length - 1].find(); + if (!coverStart || !coverEnd || to.line - from.line < CHUNK_SIZE || + cmp(from, coverEnd.to) >= 0 || cmp(to, coverStart.from) <= 0) + return reset(cm); - if (!reversed) { - // if selection shrinks - if (head.line < lastSelection.head.line) { - var textMark, - position; - do { - textMark = cm._selectionMarks.pop(); - position = textMark.find(); - textMark.clear(); - } while (cm._selectionMarks.length && position.from.line >= head.line); - markText(cm, position.from, head); + while (cmp(from, coverStart.from) > 0) { + array.shift().clear(); + coverStart = array[0].find(); + } + if (cmp(from, coverStart.from) < 0) { + if (coverStart.to.line - from.line < CHUNK_SIZE) { + array.shift().clear(); + coverRange(cm, from, coverStart.to, 0); } else { - var textMark = cm._selectionMarks.pop(); - var position = textMark.find(); - textMark.clear(); - coverRange(cm, position.from, head); + coverRange(cm, from, coverStart.from, 0); } - } else { - // selection getting smaller - if (head.line > lastSelection.head.line) { - var textMark, - position; - do { - textMark = cm._selectionMarks.pop(); - position = textMark.find(); - textMark.clear(); - } while (cm._selectionMarks.length && position.to.line <= head.line); - markText(cm, position.to, head); + } + + while (cmp(to, coverEnd.to) < 0) { + array.pop().clear(); + coverEnd = array[array.length - 1].find(); + } + if (cmp(to, coverEnd.to) > 0) { + if (to.line - coverEnd.from.line < CHUNK_SIZE) { + array.pop().clear(); + coverRange(cm, coverEnd.from, to); } else { - var textMark = cm._selectionMarks.pop(); - var position = textMark.find(); - coverRange(cm, position.to, head); - textMark.clear(); + coverRange(cm, coverEnd.to, to); } } } - - function updateSelectedText(cm) { - cm.operation(function() { - if (!cm.somethingSelected()) - return removeTextMarks(cm); - - if (!cm._selectionMarks || !cm._selectionMarks.length) - return createInitialCoverage(cm); - - incrementalCoverageUpdate(cm); - }); - } })(); From eccea9beaad51a34006430b446892d9a5333e543 Mon Sep 17 00:00:00 2001 From: Marijn Haverbeke Date: Thu, 16 May 2013 11:39:14 +0200 Subject: [PATCH 1092/5780] Move addon state objects into cm.state Rather than polluting the top-level of the editor object --- addon/display/placeholder.js | 8 ++++---- addon/lint/lint.js | 14 +++++++------- addon/search/match-highlighter.js | 8 ++++---- addon/search/search.js | 2 +- addon/selection/active-line.js | 12 ++++++------ 5 files changed, 22 insertions(+), 22 deletions(-) diff --git a/addon/display/placeholder.js b/addon/display/placeholder.js index f85f2df127..18f9dff3ab 100644 --- a/addon/display/placeholder.js +++ b/addon/display/placeholder.js @@ -19,14 +19,14 @@ }); function clearPlaceholder(cm) { - if (cm._placeholder) { - cm._placeholder.parentNode.removeChild(cm._placeholder); - cm._placeholder = null; + if (cm.state.placeholder) { + cm.state.placeholder.parentNode.removeChild(cm.state.placeholder); + cm.state.placeholder = null; } } function setPlaceholder(cm) { clearPlaceholder(cm); - var elt = cm._placeholder = document.createElement("pre"); + var elt = cm.state.placeholder = document.createElement("pre"); elt.style.cssText = "height: 0; overflow: visible"; elt.className = "CodeMirror-placeholder"; elt.appendChild(document.createTextNode(cm.getOption("placeholder"))); diff --git a/addon/lint/lint.js b/addon/lint/lint.js index 29fd8d9c3c..2e7cea1925 100644 --- a/addon/lint/lint.js +++ b/addon/lint/lint.js @@ -59,7 +59,7 @@ CodeMirror.validate = (function() { } function clearMarks(cm) { - var state = cm._lintState; + var state = cm.state.lint; if (state.hasGutter) cm.clearGutter(GUTTER_ID); for (var i = 0; i < state.marked.length; ++i) state.marked[i].clear(); @@ -105,7 +105,7 @@ CodeMirror.validate = (function() { } function startLinting(cm) { - var state = cm._lintState, options = state.options; + var state = cm.state.lint, options = state.options; if (options.async) options.getAnnotations(cm, updateLinting, options); else @@ -114,7 +114,7 @@ CodeMirror.validate = (function() { function updateLinting(cm, annotationsNotSorted) { clearMarks(cm); - var state = cm._lintState, options = state.options; + var state = cm.state.lint, options = state.options; var annotations = groupByLine(annotationsNotSorted); @@ -148,7 +148,7 @@ CodeMirror.validate = (function() { } function onChange(cm) { - var state = cm._lintState; + var state = cm.state.lint; clearTimeout(state.timeout); state.timeout = setTimeout(function(){startLinting(cm);}, state.options.delay || 500); } @@ -179,14 +179,14 @@ CodeMirror.validate = (function() { if (old && old != CodeMirror.Init) { clearMarks(cm); cm.off("change", onChange); - CodeMirror.off(cm.getWrapperElement(), "mouseover", cm._lintState.onMouseOver); - delete cm._lintState; + CodeMirror.off(cm.getWrapperElement(), "mouseover", cm.state.lint.onMouseOver); + delete cm.state.lint; } if (val) { var gutters = cm.getOption("gutters"), hasLintGutter = false; for (var i = 0; i < gutters.length; ++i) if (gutters[i] == GUTTER_ID) hasLintGutter = true; - var state = cm._lintState = new LintState(cm, parseOptions(val), hasLintGutter); + var state = cm.state.lint = new LintState(cm, parseOptions(val), hasLintGutter); cm.on("change", onChange); if (state.options.tooltips != false) CodeMirror.on(cm.getWrapperElement(), "mouseover", state.onMouseOver); diff --git a/addon/search/match-highlighter.js b/addon/search/match-highlighter.js index 14c1dab5b6..0800f4c638 100644 --- a/addon/search/match-highlighter.js +++ b/addon/search/match-highlighter.js @@ -24,19 +24,19 @@ CodeMirror.defineOption("highlightSelectionMatches", false, function(cm, val, old) { var prev = old && old != CodeMirror.Init; if (val && !prev) { - cm._matchHighlightState = new State(val); + cm.state.matchHighlighter = new State(val); cm.on("cursorActivity", highlightMatches); } else if (!val && prev) { - var over = cm._matchHighlightState.overlay; + var over = cm.state.matchHighlighter.overlay; if (over) cm.removeOverlay(over); - cm._matchHighlightState = null; + cm.state.matchHighlighter = null; cm.off("cursorActivity", highlightMatches); } }); function highlightMatches(cm) { cm.operation(function() { - var state = cm._matchHighlightState; + var state = cm.state.matchHighlighter; if (state.overlay) { cm.removeOverlay(state.overlay); state.overlay = null; diff --git a/addon/search/search.js b/addon/search/search.js index 6331b86555..eb9ab8bede 100644 --- a/addon/search/search.js +++ b/addon/search/search.js @@ -27,7 +27,7 @@ this.overlay = null; } function getSearchState(cm) { - return cm._searchState || (cm._searchState = new SearchState()); + return cm.state.search || (cm.state.search = new SearchState()); } function getSearchCursor(cm, query, pos) { // Heuristic: if the query string is all lowercase, do a case insensitive search. diff --git a/addon/selection/active-line.js b/addon/selection/active-line.js index 211de0fefd..65fab6f162 100644 --- a/addon/selection/active-line.js +++ b/addon/selection/active-line.js @@ -17,23 +17,23 @@ } else if (!val && prev) { cm.off("cursorActivity", updateActiveLine); clearActiveLine(cm); - delete cm._activeLine; + delete cm.state.activeLine; } }); function clearActiveLine(cm) { - if ("_activeLine" in cm) { - cm.removeLineClass(cm._activeLine, "wrap", WRAP_CLASS); - cm.removeLineClass(cm._activeLine, "background", BACK_CLASS); + if ("activeLine" in cm.state) { + cm.removeLineClass(cm.state.activeLine, "wrap", WRAP_CLASS); + cm.removeLineClass(cm.state.activeLine, "background", BACK_CLASS); } } function updateActiveLine(cm) { var line = cm.getLineHandle(cm.getCursor().line); - if (cm._activeLine == line) return; + if (cm.state.activeLine == line) return; clearActiveLine(cm); cm.addLineClass(line, "wrap", WRAP_CLASS); cm.addLineClass(line, "background", BACK_CLASS); - cm._activeLine = line; + cm.state.activeLine = line; } })(); From 6286d9db04d604a12f660c9b3fc0ce66929abb6b Mon Sep 17 00:00:00 2001 From: lynschinzer Date: Tue, 14 May 2013 00:41:49 +0200 Subject: [PATCH 1093/5780] [vim keymap] Add macro support for normal mode [vim keymap] Remove duplicated @ logic [vim keymap] Remove q swapping [vim keymap] Recover mis-deleted line from 3-way merge conflict [vim keymap] "q" repeatable by count [vim keymap] Remove deprecated lookup tables [vim keymap] Robustify if condition [vim keymap] Revise logKey logic [vim keymap] IE8 compatible forEach [vim keymap] Transparentize hidden states [vim keymap] Minor fixup [vim keymap] Eliminate globals/Remove unused function/Add tests --- keymap/vim.js | 101 ++++++++++++++++++++++++++++++++++++++++++++++- test/index.html | 1 + test/vim_test.js | 16 ++++++++ 3 files changed, 117 insertions(+), 1 deletion(-) diff --git a/keymap/vim.js b/keymap/vim.js index 9c0197faaa..fd8167e098 100644 --- a/keymap/vim.js +++ b/keymap/vim.js @@ -262,6 +262,8 @@ { keys: ['P'], type: 'action', action: 'paste', actionArgs: { after: false }}, { keys: ['r', 'character'], type: 'action', action: 'replace' }, + { keys: ['@', 'character'], type: 'action', action: 'replayMacro' }, + { keys: ['q', 'character'], type: 'action', action: 'enterMacroRecordMode' }, { keys: ['R'], type: 'action', action: 'enterReplaceMode' }, { keys: ['u'], type: 'action', action: 'undo' }, { keys: [''], type: 'action', action: 'redo' }, @@ -411,7 +413,7 @@ } var mark = buffer[(size + pointer) % size]; // skip marks that are temporarily removed from text buffer - if (!mark.find()) { + if (mark && !mark.find()) { var inc = offset > 0 ? 1 : -1; var newCur; var oldCur = cm.getCursor(); @@ -434,6 +436,26 @@ move: move }; }; + + var createMacroState = function() { + return { + macroKeyBuffer: [], + latestRegister: undefined, + enteredMacroMode: undefined, + isMacroPlaying: false, + toggle: function(cm, registerName) { + if (this.enteredMacroMode) { //onExit + this.enteredMacroMode(); // close dialog + this.enteredMacroMode = undefined; + } else { //onEnter + this.latestRegister = registerName; + this.enteredMacroMode = cm.openDialog( + '(recording)['+registerName+']', null, {bottom:true}); + } + } + } + } + // Global Vim state. Call getVimGlobalState to get and initialize. var vimGlobalState; function getVimGlobalState() { @@ -444,6 +466,7 @@ // Whether we are searching backwards. searchIsReversed: false, jumpList: createCircularJumpList(), + macroModeState: createMacroState(), // Recording latest f, t, F or T motion command. lastChararacterSearch: {increment:0, forward:true, selectedCharacter:''}, registerController: new RegisterController({}) @@ -509,6 +532,14 @@ handleKey: function(cm, key) { var command; var vim = getVimState(cm); + var macroModeState = getVimGlobalState().macroModeState; + if (macroModeState.enteredMacroMode) { + if (key == 'q') { + actions.exitMacroRecordMode(); + return; + } + logKey(macroModeState, key); + } if (key == '') { // Clear input state and get back to normal mode. vim.inputState = new InputState(); @@ -671,6 +702,9 @@ this.unamedRegister.set(text, linewise); } }, + setRegisterText: function(name, text, linewise) { + this.getRegister(name).set(text, linewise); + }, // Gets the register named @name. If one of @name doesn't already exist, // create it. If @name is invalid, return the unamedRegister. getRegister: function(name) { @@ -1521,6 +1555,30 @@ // view into the right place. cm.scrollIntoView(); }, + replayMacro: function(cm, actionArgs) { + var registerName = actionArgs.selectedCharacter; + var repeat = actionArgs.repeat; + var macroModeState = getVimGlobalState().macroModeState; + if (registerName == '@') { + registerName = macroModeState.latestRegister; + } + var keyBuffer = parseRegisterToKeyBuffer(macroModeState, registerName); + while(repeat--){ + executeMacroKeyBuffer(cm, macroModeState, keyBuffer); + } + }, + exitMacroRecordMode: function(cm, actionArgs) { + var macroModeState = getVimGlobalState().macroModeState; + macroModeState.toggle(); + parseKeyBufferToRegister(macroModeState.latestRegister, + macroModeState.macroKeyBuffer); + }, + enterMacroRecordMode: function(cm, actionArgs) { + var macroModeState = getVimGlobalState().macroModeState; + var registerName = actionArgs.selectedCharacter; + macroModeState.toggle(cm, registerName); + emptyMacroKeyBuffer(macroModeState); + }, enterInsertMode: function(cm, actionArgs) { var insertAt = (actionArgs) ? actionArgs.insertAt : null; if (insertAt == 'eol') { @@ -3190,6 +3248,47 @@ fallthrough: ['default'] }; + function parseRegisterToKeyBuffer(macroModeState, registerName) { + var match, key; + var register = getVimGlobalState().registerController.getRegister(registerName); + var text = register.toString(); + var macroKeyBuffer = macroModeState.macroKeyBuffer; + emptyMacroKeyBuffer(macroModeState); + do { + match = text.match(/<\w+-.+>|<\w+>|.|\n/); + if(match === null)break; + key = match[0]; + text = text.substring(match.index + key.length); + macroKeyBuffer.push(key); + } while (text); + return macroKeyBuffer; + } + + function parseKeyBufferToRegister(registerName, keyBuffer) { + var text = keyBuffer.join(''); + getVimGlobalState().registerController.setRegisterText(registerName, text); + } + + function emptyMacroKeyBuffer(macroModeState) { + if(macroModeState.isMacroPlaying)return; + var macroKeyBuffer = macroModeState.macroKeyBuffer; + macroKeyBuffer.length = 0; + } + + function executeMacroKeyBuffer(cm, macroModeState, keyBuffer) { + macroModeState.isMacroPlaying = true; + for (var i = 0, len = keyBuffer.length; i < len; i++) { + CodeMirror.Vim.handleKey(cm, keyBuffer[i]); + }; + macroModeState.isMacroPlaying = false; + } + + function logKey(macroModeState, key) { + if(macroModeState.isMacroPlaying)return; + var macroKeyBuffer = macroModeState.macroKeyBuffer; + macroKeyBuffer.push(key); + } + function exitReplaceMode(cm) { cm.toggleOverwrite(); cm.setCursor(cm.getCursor().line, cm.getCursor().ch-1, true); diff --git a/test/index.html b/test/index.html index 816cef5948..3eb691576a 100644 --- a/test/index.html +++ b/test/index.html @@ -9,6 +9,7 @@ + diff --git a/test/vim_test.js b/test/vim_test.js index 83bbec9ac6..ff376c6dc2 100644 --- a/test/vim_test.js +++ b/test/vim_test.js @@ -169,6 +169,22 @@ function testVim(name, run, opts, expectedFail) { } }, expectedFail); }; +testVim('qq@q', function(cm, vim, helpers) { + cm.setCursor(0, 0); + helpers.doKeys('q', 'q', 'l', 'l', 'q'); + helpers.assertCursorAt(0,2); + helpers.doKeys('@', 'q'); + helpers.assertCursorAt(0,4); +}, { value: ' '}); +testVim('@@', function(cm, vim, helpers) { + cm.setCursor(0, 0); + helpers.doKeys('q', 'q', 'l', 'l', 'q'); + helpers.assertCursorAt(0,2); + helpers.doKeys('@', 'q'); + helpers.assertCursorAt(0,4); + helpers.doKeys('@', '@'); + helpers.assertCursorAt(0,6); +}, { value: ' '}); var jumplistScene = ''+ 'word\n'+ '(word)\n'+ From 4206fd03bb0c6c7a4f0eea002d777e3839ca420e Mon Sep 17 00:00:00 2001 From: lynschinzer Date: Thu, 16 May 2013 11:00:25 +0200 Subject: [PATCH 1094/5780] [vim keymap] Fix zz/zt/zb --- keymap/vim.js | 23 ++++++++++------------- test/vim_test.js | 39 +++++++++++++++++++++++++++++++++++---- 2 files changed, 45 insertions(+), 17 deletions(-) diff --git a/keymap/vim.js b/keymap/vim.js index fd8167e098..ac6d29c465 100644 --- a/keymap/vim.js +++ b/keymap/vim.js @@ -1538,22 +1538,19 @@ }, scrollToCursor: function(cm, actionArgs) { var lineNum = cm.getCursor().line; - var heightProp = window.getComputedStyle(cm.getScrollerElement()). - getPropertyValue('height'); - var height = parseInt(heightProp); - var y = cm.charCoords({line: lineNum, ch: 0}, "local").top; - var halfHeight = parseInt(height) / 2; + var charCoords = cm.charCoords({line: lineNum, ch: 0}, "local"); + var height = cm.getScrollInfo().clientHeight; + var y = charCoords.top; + var lineHeight = charCoords.bottom - y; switch (actionArgs.position) { - case 'center': y = y - (height / 2) + 10; - break; - case 'bottom': y = y - height; - break; - case 'top': break; + case 'center': y = y - (height / 2) + lineHeight; + break; + case 'bottom': y = y - height + lineHeight*1.4; + break; + case 'top': y = y + lineHeight*0.4; + break; } cm.scrollTo(null, y); - // The calculations are slightly off, use scrollIntoView to nudge the - // view into the right place. - cm.scrollIntoView(); }, replayMacro: function(cm, actionArgs) { var registerName = actionArgs.selectedCharacter; diff --git a/test/vim_test.js b/test/vim_test.js index ff376c6dc2..69714079de 100644 --- a/test/vim_test.js +++ b/test/vim_test.js @@ -1752,14 +1752,45 @@ testVim('HML', function(cm, vim, helpers) { helpers.doKeys('M'); helpers.assertCursorAt(103,4); }, { value: (function(){ - var upperLines = new Array(100); - var lowerLines = new Array(100); + var lines = new Array(100); var upper = ' xx\n'; var lower = ' xx\n'; - upper = upperLines.join(upper); - lower = upperLines.join(lower); + upper = lines.join(upper); + lower = lines.join(lower); return upper + lower; })()}); + +var zVals = ['zb','zz','zt','z-','z.','z'].map(function(e, idx){ + var lineNum = 250; + var lines = 35; + testVim(e, function(cm, vim, helpers) { + var k1 = e[0]; + var k2 = e.substring(1); + var textHeight = cm.defaultTextHeight(); + cm.setSize(600, lines*textHeight); + cm.setCursor(lineNum, 0); + helpers.doKeys(k1, k2); + zVals[idx] = cm.getScrollInfo().top; + }, { value: (function(){ + return new Array(500).join('\n'); + })()}); +}); +testVim('zb', function(cm, vim, helpers){ + eq(zVals[2], zVals[5]); +}); + var squareBracketMotionSandbox = ''+ '({\n'+//0 ' ({\n'+//11 From 3788103b7b94f5960c3d3380097f7bb332daabad Mon Sep 17 00:00:00 2001 From: Marijn Haverbeke Date: Thu, 16 May 2013 14:29:53 +0200 Subject: [PATCH 1095/5780] [real-world uses] Add Upsource --- doc/realworld.html | 1 + 1 file changed, 1 insertion(+) diff --git a/doc/realworld.html b/doc/realworld.html index 941d926dbe..0c51cc79cd 100644 --- a/doc/realworld.html +++ b/doc/realworld.html @@ -98,6 +98,7 @@

    { } CodeMi
  • Tumblr code highlighting shim
  • TurboPY (web publishing framework)
  • UmpleOnline (model-oriented programming tool)
  • +
  • Upsource (code viewer)
  • WebGL playground
  • WeScheme (learning tool)
  • WordPress plugin
  • From 7b8ed766a8a545a6bedc594a34f466ebe260baf7 Mon Sep 17 00:00:00 2001 From: Marijn Haverbeke Date: Thu, 16 May 2013 15:40:25 +0200 Subject: [PATCH 1096/5780] [css mode] Recognize SVG properties Closes #1529 --- mode/css/css.js | 12 +++++++++++- 1 file changed, 11 insertions(+), 1 deletion(-) diff --git a/mode/css/css.js b/mode/css/css.js index f4ee021ed4..b9953c1767 100644 --- a/mode/css/css.js +++ b/mode/css/css.js @@ -392,7 +392,17 @@ CodeMirror.defineMode("css-base", function(config, parserConfig) { "vertical-align", "visibility", "voice-balance", "voice-duration", "voice-family", "voice-pitch", "voice-range", "voice-rate", "voice-stress", "voice-volume", "volume", "white-space", "widows", "width", "word-break", - "word-spacing", "word-wrap", "z-index" + "word-spacing", "word-wrap", "z-index", + // SVG-specific + "clip-path", "clip-rule", "mask", "enable-background", "filter", "flood-color", + "flood-opacity", "lighting-color", "stop-color", "stop-opacity", "pointer-events", + "color-interpolation", "color-interpolation-filters", "color-profile", + "color-rendering", "fill", "fill-opacity", "fill-rule", "image-rendering", + "marker", "marker-end", "marker-mid", "marker-start", "shape-rendering", "stroke", + "stroke-dasharray", "stroke-dashoffset", "stroke-linecap", "stroke-linejoin", + "stroke-miterlimit", "stroke-opacity", "stroke-width", "text-rendering", + "baseline-shift", "dominant-baseline", "glyph-orientation-horizontal", + "glyph-orientation-vertical", "kerning", "text-anchor", "writing-mode" ]); var colorKeywords = keySet([ From 18c08f6efcc3dd646e2f1d7e32575cbe2940d39a Mon Sep 17 00:00:00 2001 From: Marijn Haverbeke Date: Thu, 16 May 2013 15:50:22 +0200 Subject: [PATCH 1097/5780] [real-world uses] Better link for Upsource --- doc/realworld.html | 2 +- index.html | 2 +- 2 files changed, 2 insertions(+), 2 deletions(-) diff --git a/doc/realworld.html b/doc/realworld.html index 0c51cc79cd..75507842dd 100644 --- a/doc/realworld.html +++ b/doc/realworld.html @@ -98,7 +98,7 @@

    { } CodeMi
  • Tumblr code highlighting shim
  • TurboPY (web publishing framework)
  • UmpleOnline (model-oriented programming tool)
  • -
  • Upsource (code viewer)
  • +
  • Upsource (code viewer)
  • WebGL playground
  • WeScheme (learning tool)
  • WordPress plugin
  • diff --git a/index.html b/index.html index a0d3d5b48f..f5244742ea 100644 --- a/index.html +++ b/index.html @@ -129,6 +129,7 @@

    Real-world uses:

  • Eloquent JavaScript
  • Emmet
  • Prose.io
  • +
  • Upsource
  • Paper.js
  • Codev
  • Tributary
  • @@ -138,7 +139,6 @@

    Real-world uses:

  • The File Tree
  • JSHint
  • SQLFiddle
  • -
  • Try Haxe
  • CSSDeck
  • sketchPatch Livecodelab
  • NoTex
  • From fc38d5f9aaa946b2bce6458d07fb48a279021bd0 Mon Sep 17 00:00:00 2001 From: lynschinzer Date: Thu, 16 May 2013 14:55:05 +0200 Subject: [PATCH 1098/5780] [vim keymap] Improve code structure for later usage --- keymap/vim.js | 33 ++++++++++++++++----------------- 1 file changed, 16 insertions(+), 17 deletions(-) diff --git a/keymap/vim.js b/keymap/vim.js index ac6d29c465..42e6ecaac0 100644 --- a/keymap/vim.js +++ b/keymap/vim.js @@ -3163,36 +3163,37 @@ * modifers. */ // TODO: Figure out a way to catch capslock. - function handleKeyEvent_(cm, key, modifier) { - if (isUpperCase(key)) { + function cmKeyToVimKey(key, modifier) { + var vimKey = key; + if (isUpperCase(vimKey)) { // Convert to lower case if shift is not the modifier since the key // we get from CodeMirror is always upper case. if (modifier == 'Shift') { modifier = null; } else { - key = key.toLowerCase(); + vimKey = vimKey.toLowerCase(); } } if (modifier) { // Vim will parse modifier+key combination as a single key. - key = modifier.charAt(0) + '-' + key; + vimKey = modifier.charAt(0) + '-' + vimKey; } - var specialKey = ({Enter:'CR',Backspace:'BS',Delete:'Del'})[key]; - key = specialKey ? specialKey : key; - key = key.length > 1 ? '<'+ key + '>' : key; - vim.handleKey(cm, key); + var specialKey = ({Enter:'CR',Backspace:'BS',Delete:'Del'})[vimKey]; + vimKey = specialKey ? specialKey : vimKey; + vimKey = vimKey.length > 1 ? '<'+ vimKey + '>' : vimKey; + return vimKey; } // Closure to bind CodeMirror, key, modifier. - function keyMapper(key, modifier) { + function keyMapper(vimKey) { return function(cm) { - handleKeyEvent_(cm, key, modifier); + vim.handleKey(cm, vimKey); }; } var modifiers = ['Shift', 'Ctrl']; - var keyMap = { + var cmToVimKeymap = { 'nofallthrough': true, 'style': 'fat-cursor' }; @@ -3204,11 +3205,9 @@ // them. key = "'" + key + "'"; } - if (modifier) { - keyMap[modifier + '-' + key] = keyMapper(keys[i], modifier); - } else { - keyMap[key] = keyMapper(keys[i]); - } + var vimKey = cmKeyToVimKey(keys[i], modifier); + var cmKey = modifier ? modifier + '-' + key : key; + cmToVimKeymap[cmKey] = keyMapper(vimKey); } } bindKeys(upperCaseAlphabet); @@ -3220,7 +3219,7 @@ bindKeys(numbers, 'Ctrl'); bindKeys(specialKeys); bindKeys(specialKeys, 'Ctrl'); - return keyMap; + return cmToVimKeymap; } CodeMirror.keyMap.vim = buildVimKeyMap(); From 20f7dc8c6f62fdcce602ec00c63d48d319c6a79a Mon Sep 17 00:00:00 2001 From: Marijn Haverbeke Date: Mon, 20 May 2013 00:02:15 +0200 Subject: [PATCH 1099/5780] [real-world uses] Add snippets.pro --- doc/realworld.html | 1 + 1 file changed, 1 insertion(+) diff --git a/doc/realworld.html b/doc/realworld.html index 75507842dd..f90b0f5a13 100644 --- a/doc/realworld.html +++ b/doc/realworld.html @@ -90,6 +90,7 @@

    { } CodeMi
  • RealTime.io (Internet-of-Things infrastructure)
  • sketchPatch Livecodelab
  • Skulpt (in-browser Python environment)
  • +
  • Snippets.pro (code snippet sharing)
  • SolidShops (hosted e-commerce platform)
  • SQLFiddle (SQL playground)
  • The File Tree (collab editor)
  • From 14f3188c42f5c368a5a760cbd46510e00d8e337f Mon Sep 17 00:00:00 2001 From: Marijn Haverbeke Date: Mon, 20 May 2013 14:48:45 +0200 Subject: [PATCH 1100/5780] More involved span-affects-wrapping test on Webkit A dash is only handled specially when preceded by a word character. Issue #1531 --- demo/spanaffectswrapping_shim.html | 11 ++++++----- lib/codemirror.js | 24 +++++++++++++++++------- 2 files changed, 23 insertions(+), 12 deletions(-) diff --git a/demo/spanaffectswrapping_shim.html b/demo/spanaffectswrapping_shim.html index 733db067ff..14b6c8d82a 100644 --- a/demo/spanaffectswrapping_shim.html +++ b/demo/spanaffectswrapping_shim.html @@ -14,23 +14,24 @@

    CodeMirror: odd wrapping shim

    in lib/codemirror.js for some more details.

    -
    +
    foooo,-10br
    
     
         
         
    +    
         
         
         
    @@ -73,6 +74,7 @@ 

    CodeMirror: Test Suite

    + -

    A plain text/Smarty mode which allows for custom delimiter tags (defaults to { and }).

    +
    + +

    Smarty 3

    + + + + + + +

    A plain text/Smarty version 2 or 3 mode, which allows for custom delimiter tags.

    MIME types defined: text/x-smarty

    diff --git a/mode/smarty/smarty.js b/mode/smarty/smarty.js index 7d7e62f86e..00c1df5aa8 100644 --- a/mode/smarty/smarty.js +++ b/mode/smarty/smarty.js @@ -1,140 +1,186 @@ +/** + * Smarty 2 and 3 mode. + */ CodeMirror.defineMode("smarty", function(config) { - var keyFuncs = ["debug", "extends", "function", "include", "literal"]; + "use strict"; + + // our default settings; check to see if they're overridden + var settings = { + rightDelimiter: '}', + leftDelimiter: '{', + smartyVersion: 2 // for backward compatibility + }; + if (config.hasOwnProperty("leftDelimiter")) { + settings.leftDelimiter = config.leftDelimiter; + } + if (config.hasOwnProperty("rightDelimiter")) { + settings.rightDelimiter = config.rightDelimiter; + } + if (config.hasOwnProperty("smartyVersion") && config.smartyVersion === 3) { + settings.smartyVersion = 3; + } + + var keyFunctions = ["debug", "extends", "function", "include", "literal"]; var last; var regs = { operatorChars: /[+\-*&%=<>!?]/, - validIdentifier: /[a-zA-Z0-9\_]/, - stringChar: /[\'\"]/ + validIdentifier: /[a-zA-Z0-9_]/, + stringChar: /['"]/ }; - var leftDelim = (typeof config.mode.leftDelimiter != 'undefined') ? config.mode.leftDelimiter : "{"; - var rightDelim = (typeof config.mode.rightDelimiter != 'undefined') ? config.mode.rightDelimiter : "}"; - function ret(style, lst) { last = lst; return style; } - - function tokenizer(stream, state) { - function chain(parser) { + var helpers = { + continue: function(style, lastType) { + last = lastType; + return style; + }, + chain: function(stream, state, parser) { state.tokenize = parser; return parser(stream, state); } + }; - if (stream.match(leftDelim, true)) { - if (stream.eat("*")) { - return chain(inBlock("comment", "*" + rightDelim)); - } - else { - state.tokenize = inSmarty; - return "tag"; - } - } - else { - // I'd like to do an eatWhile() here, but I can't get it to eat only up to the rightDelim string/char - stream.next(); - return null; - } - } - function inSmarty(stream, state) { - if (stream.match(rightDelim, true)) { - state.tokenize = tokenizer; - return ret("tag", null); - } + // our various parsers + var parsers = { - var ch = stream.next(); - if (ch == "$") { - stream.eatWhile(regs.validIdentifier); - return ret("variable-2", "variable"); - } - else if (ch == ".") { - return ret("operator", "property"); - } - else if (regs.stringChar.test(ch)) { - state.tokenize = inAttribute(ch); - return ret("string", "string"); - } - else if (regs.operatorChars.test(ch)) { - stream.eatWhile(regs.operatorChars); - return ret("operator", "operator"); - } - else if (ch == "[" || ch == "]") { - return ret("bracket", "bracket"); - } - else if (/\d/.test(ch)) { - stream.eatWhile(/\d/); - return ret("number", "number"); - } - else { - if (state.last == "variable") { - if (ch == "@") { - stream.eatWhile(regs.validIdentifier); - return ret("property", "property"); + // the main tokenizer + tokenizer: function(stream, state) { + if (stream.match(settings.leftDelimiter, true)) { + if (stream.eat("*")) { + return helpers.chain(stream, state, parsers.inBlock("comment", "*" + settings.rightDelimiter)); + } else { + // Smarty 3 allows { and } surrounded by whitespace to NOT slip into Smarty mode + state.depth++; + var isEol = stream.eol(); + var isFollowedByWhitespace = /\s/.test(stream.peek()); + if (settings.smartyVersion === 3 && settings.leftDelimiter === "{" && (isEol || isFollowedByWhitespace)) { + state.depth--; + return null; + } else { + state.tokenize = parsers.smarty; + last = "startTag"; + return "tag"; + } } - else if (ch == "|") { - stream.eatWhile(regs.validIdentifier); - return ret("qualifier", "modifier"); - } - } - else if (state.last == "whitespace") { - stream.eatWhile(regs.validIdentifier); - return ret("attribute", "modifier"); - } - else if (state.last == "property") { - stream.eatWhile(regs.validIdentifier); - return ret("property", null); - } - else if (/\s/.test(ch)) { - last = "whitespace"; + } else { + stream.next(); return null; } + }, - var str = ""; - if (ch != "/") { - str += ch; - } - var c = ""; - while ((c = stream.eat(regs.validIdentifier))) { - str += c; - } - var i, j; - for (i=0, j=keyFuncs.length; i Date: Thu, 23 May 2013 12:06:16 +0200 Subject: [PATCH 1114/5780] [trailingspace addon] Add --- addon/edit/trailingspace.js | 15 ++++++++++++++ demo/trailingspace.html | 39 +++++++++++++++++++++++++++++++++++++ doc/manual.html | 15 ++++++++++---- lib/codemirror.js | 3 ++- 4 files changed, 67 insertions(+), 5 deletions(-) create mode 100644 addon/edit/trailingspace.js create mode 100644 demo/trailingspace.html diff --git a/addon/edit/trailingspace.js b/addon/edit/trailingspace.js new file mode 100644 index 0000000000..f6bb02645d --- /dev/null +++ b/addon/edit/trailingspace.js @@ -0,0 +1,15 @@ +CodeMirror.defineOption("showTrailingSpace", false, function(cm, val, prev) { + if (prev == CodeMirror.Init) prev = false; + if (prev && !val) + cm.removeOverlay("trailingspace"); + else if (!prev && val) + cm.addOverlay({ + token: function(stream) { + for (var l = stream.string.length, i = l; i && /\s/.test(stream.string.charAt(i - 1)); --i) {} + if (i > stream.pos) { stream.pos = i; return null; } + stream.pos = l; + return "trailingspace"; + }, + name: "trailingspace" + }); +}); diff --git a/demo/trailingspace.html b/demo/trailingspace.html new file mode 100644 index 0000000000..ca74152eca --- /dev/null +++ b/demo/trailingspace.html @@ -0,0 +1,39 @@ + + + + + CodeMirror: Trailing Whitespace Demo + + + + + + + + +

    CodeMirror: Trailing Whitespace Demo

    + +
    + + + +

    Uses +the trailingspace +addon to highlight trailing whitespace.

    + + + diff --git a/doc/manual.html b/doc/manual.html index 5d029aebc1..33625b577c 100644 --- a/doc/manual.html +++ b/doc/manual.html @@ -939,10 +939,10 @@

    Configuration methods

    override the styling of the base mode entirely, instead of the two being applied together.

    cm.removeOverlay(mode: string|object)
    -
    Pass this the exact argument passed for - the mode parameter - to addOverlay to remove - an overlay again.
    +
    Pass this the exact value passed for the mode + parameter to addOverlay, + or a string that corresponds to the name propery of + that value, to remove an overlay again.
    cm.on(type: string, func: (...args))
    Register an event handler for the given event type (a @@ -1558,6 +1558,13 @@

    Add-ons

    to customize it. Demo here.
    +
    edit/trailingspace.js
    +
    Adds an option showTrailingSpace which, when + enabled, adds the CSS class cm-trailingspace to + stretches of whitespace at the end of lines. + The demo has a nice + squiggly underline style for this class.
    +
    comment/comment.js
    Addon for commenting and uncommenting code. Adds three methods to CodeMirror instances: diff --git a/lib/codemirror.js b/lib/codemirror.js index 1b124a432e..a2324e7679 100644 --- a/lib/codemirror.js +++ b/lib/codemirror.js @@ -2780,7 +2780,8 @@ window.CodeMirror = (function() { removeOverlay: operation(null, function(spec) { var overlays = this.state.overlays; for (var i = 0; i < overlays.length; ++i) { - if (overlays[i].modeSpec == spec) { + var cur = overlays[i].modeSpec; + if (cur == spec || typeof spec == "string" && cur.name == spec) { overlays.splice(i, 1); this.state.modeGen++; regChange(this); From 2daf724ed578726f2e37ef22dc8eebebd44b2a64 Mon Sep 17 00:00:00 2001 From: Marijn Haverbeke Date: Thu, 23 May 2013 13:11:07 +0200 Subject: [PATCH 1115/5780] [smarty mode] Fix lint errors --- mode/smarty/smarty.js | 34 +++++++++++++++++----------------- 1 file changed, 17 insertions(+), 17 deletions(-) diff --git a/mode/smarty/smarty.js b/mode/smarty/smarty.js index 00c1df5aa8..856f7bb3be 100644 --- a/mode/smarty/smarty.js +++ b/mode/smarty/smarty.js @@ -29,7 +29,7 @@ CodeMirror.defineMode("smarty", function(config) { }; var helpers = { - continue: function(style, lastType) { + cont: function(style, lastType) { last = lastType; return style; }, @@ -79,47 +79,47 @@ CodeMirror.defineMode("smarty", function(config) { } else { state.tokenize = parsers.tokenizer; } - return helpers.continue("tag", null); + return helpers.cont("tag", null); } if (stream.match(settings.leftDelimiter, true)) { state.depth++; - return helpers.continue("tag", "startTag"); + return helpers.cont("tag", "startTag"); } var ch = stream.next(); if (ch == "$") { stream.eatWhile(regs.validIdentifier); - return helpers.continue("variable-2", "variable"); + return helpers.cont("variable-2", "variable"); } else if (ch == ".") { - return helpers.continue("operator", "property"); + return helpers.cont("operator", "property"); } else if (regs.stringChar.test(ch)) { state.tokenize = parsers.inAttribute(ch); - return helpers.continue("string", "string"); + return helpers.cont("string", "string"); } else if (regs.operatorChars.test(ch)) { stream.eatWhile(regs.operatorChars); - return helpers.continue("operator", "operator"); + return helpers.cont("operator", "operator"); } else if (ch == "[" || ch == "]") { - return helpers.continue("bracket", "bracket"); + return helpers.cont("bracket", "bracket"); } else if (/\d/.test(ch)) { stream.eatWhile(/\d/); - return helpers.continue("number", "number"); + return helpers.cont("number", "number"); } else { if (state.last == "variable") { if (ch == "@") { stream.eatWhile(regs.validIdentifier); - return helpers.continue("property", "property"); + return helpers.cont("property", "property"); } else if (ch == "|") { stream.eatWhile(regs.validIdentifier); - return helpers.continue("qualifier", "modifier"); + return helpers.cont("qualifier", "modifier"); } } else if (state.last == "whitespace") { stream.eatWhile(regs.validIdentifier); - return helpers.continue("attribute", "modifier"); + return helpers.cont("attribute", "modifier"); } if (state.last == "property") { stream.eatWhile(regs.validIdentifier); - return helpers.continue("property", null); + return helpers.cont("property", null); } else if (/\s/.test(ch)) { last = "whitespace"; return null; @@ -129,19 +129,19 @@ CodeMirror.defineMode("smarty", function(config) { if (ch != "/") { str += ch; } - var c = null; + var c = null; while (c = stream.eat(regs.validIdentifier)) { str += c; } for (var i=0, j=keyFunctions.length; i Date: Fri, 24 May 2013 08:31:56 +0200 Subject: [PATCH 1116/5780] Remove unneeded cursor set at end of drag (The mousemove events should already have taken care of that.) Issue #1488 --- lib/codemirror.js | 2 -- 1 file changed, 2 deletions(-) diff --git a/lib/codemirror.js b/lib/codemirror.js index a2324e7679..f21f76a34e 100644 --- a/lib/codemirror.js +++ b/lib/codemirror.js @@ -1728,8 +1728,6 @@ window.CodeMirror = (function() { function done(e) { counter = Infinity; - var cur = posFromMouse(cm, e); - if (cur) doSelect(cur); e_preventDefault(e); focusInput(cm); off(document, "mousemove", move); From 6edd771495c6a333166b7d22a5ebe0e0df47483e Mon Sep 17 00:00:00 2001 From: Marijn Haverbeke Date: Fri, 24 May 2013 09:22:39 +0200 Subject: [PATCH 1117/5780] Also fire beforeChange for undo/redo changes But disable its update method in that case. Issue #1539 --- doc/manual.html | 27 ++++++++++++++++----------- lib/codemirror.js | 23 +++++++++++++++-------- test/test.js | 13 +++++++++++++ 3 files changed, 44 insertions(+), 19 deletions(-) diff --git a/doc/manual.html b/doc/manual.html index 33625b577c..6fa58c6e30 100644 --- a/doc/manual.html +++ b/doc/manual.html @@ -406,17 +406,22 @@

    Events

    properties, as with the "change" event, but never a next property, since this is fired for each - individual change, and not batched per operation. It also - has update(from, to, text) - and cancel() methods, which may be used to modify - or cancel the change. All three arguments to update - are optional, and can be left off to leave the existing value - for that field intact. Note: you may not do - anything from a "beforeChange" handler that would - cause changes to the document or its visualization. Doing so - will, since this handler is called directly from the bowels of - the CodeMirror implementation, probably cause the editor to - become corrupted.
    + individual change, and not batched per operation. It also has + a cancel() method, which can be called to cancel + the change, and, if the change isn't coming + from an undo or redo event, an update(from, to, + text) method, which may be used to modify the change. + Undo or redo changes can't be modified, because they hold some + metainformation for restoring old marked ranges that is only + valid for that specific change. All three arguments + to update are optional, and can be left off to + leave the existing value for that field + intact. Note: you may not do anything from + a "beforeChange" handler that would cause changes + to the document or its visualization. Doing so will, since this + handler is called directly from the bowels of the CodeMirror + implementation, probably cause the editor to become + corrupted.
    "cursorActivity" (instance: CodeMirror)
    Will be fired when the cursor or selection moves, or any diff --git a/lib/codemirror.js b/lib/codemirror.js index f21f76a34e..b92b3a9bbc 100644 --- a/lib/codemirror.js +++ b/lib/codemirror.js @@ -2186,21 +2186,21 @@ window.CodeMirror = (function() { return {anchor: adjustPos(doc.sel.anchor), head: adjustPos(doc.sel.head)}; } - function filterChange(doc, change) { + function filterChange(doc, change, update) { var obj = { canceled: false, from: change.from, to: change.to, text: change.text, origin: change.origin, - update: function(from, to, text, origin) { - if (from) this.from = clipPos(doc, from); - if (to) this.to = clipPos(doc, to); - if (text) this.text = text; - if (origin !== undefined) this.origin = origin; - }, cancel: function() { this.canceled = true; } }; + if (update) obj.update = function(from, to, text, origin) { + if (from) this.from = clipPos(doc, from); + if (to) this.to = clipPos(doc, to); + if (text) this.text = text; + if (origin !== undefined) this.origin = origin; + }; signal(doc, "beforeChange", doc, obj); if (doc.cm) signal(doc.cm, "beforeChange", doc.cm, obj); @@ -2217,7 +2217,7 @@ window.CodeMirror = (function() { } if (hasHandler(doc, "beforeChange") || doc.cm && hasHandler(doc.cm, "beforeChange")) { - change = filterChange(doc, change); + change = filterChange(doc, change, true); if (!change) return; } @@ -2262,9 +2262,16 @@ window.CodeMirror = (function() { anchorAfter: event.anchorBefore, headAfter: event.headBefore}; (type == "undo" ? hist.undone : hist.done).push(anti); + var filter = hasHandler(doc, "beforeChange") || doc.cm && hasHandler(doc.cm, "beforeChange"); + for (var i = event.changes.length - 1; i >= 0; --i) { var change = event.changes[i]; change.origin = type; + if (filter && !filterChange(doc, change, false)) { + (type == "undo" ? hist.done : hist.undone).length = 0; + return; + } + anti.changes.push(historyChangeFromChange(doc, change)); var after = i ? computeSelAfterChange(doc, change, null) diff --git a/test/test.js b/test/test.js index 637f6e79ca..d42a76bd41 100644 --- a/test/test.js +++ b/test/test.js @@ -1358,6 +1358,19 @@ testCM("beforeChange", function(cm) { eq(cm.getValue(), "hello,_i_am_a\nhey_hey_hey"); }, {value: "abcdefghijk"}); +testCM("beforeChangeUndo", function(cm) { + cm.setLine(0, "hi"); + cm.setLine(0, "bye"); + eq(cm.historySize().undo, 2); + cm.on("beforeChange", function(cm, change) { + is(!change.update); + change.cancel(); + }); + cm.undo(); + eq(cm.historySize().undo, 0); + eq(cm.getValue(), "bye\ntwo"); +}, {value: "one\ntwo"}); + testCM("beforeSelectionChange", function(cm) { function notAtEnd(cm, pos) { var len = cm.getLine(pos.line).length; From 83fdc1e77cc078029504de549b2eef8e1bb83f12 Mon Sep 17 00:00:00 2001 From: Marijn Haverbeke Date: Sat, 25 May 2013 06:59:11 +0200 Subject: [PATCH 1118/5780] Fix patch d07c5471ca649b78366b4c10c3ef698504a454a6 The argument order to Delayed.set was wrong, and we actually want to ensure that the resize fires at least every 100ms during a resize, so that the display doesn't lag too much. --- lib/codemirror.js | 7 ++++--- 1 file changed, 4 insertions(+), 3 deletions(-) diff --git a/lib/codemirror.js b/lib/codemirror.js index b92b3a9bbc..aa0de5de43 100644 --- a/lib/codemirror.js +++ b/lib/codemirror.js @@ -1513,14 +1513,15 @@ window.CodeMirror = (function() { // Prevent wrapper from ever scrolling on(d.wrapper, "scroll", function() { d.wrapper.scrollTop = d.wrapper.scrollLeft = 0; }); - var resizeTimer = new Delayed(); + var resizeTimer; function onResize() { - resizeTimer.set(function() { + if (resizeTimer == null) resizeTimer = setTimeout(function() { + resizeTimer = null; // Might be a text scaling operation, clear size caches. d.cachedCharWidth = d.cachedTextHeight = null; clearCaches(cm); runInOp(cm, bind(regChange, cm)); - }, 200); + }, 100); } on(window, "resize", onResize); // Above handler holds on to the editor and its data structures. From 8e6b9f5e15d16f388fa15f88a930013a87f6c304 Mon Sep 17 00:00:00 2001 From: Ben Keen Date: Fri, 24 May 2013 22:11:59 -0700 Subject: [PATCH 1119/5780] [smarty mode] bugfixes - escaped quotes in strings now displayed properly - whitespace no longer affects qualifiers - parentheses properly highlighted --- mode/smarty/smarty.js | 13 ++++++++++++- 1 file changed, 12 insertions(+), 1 deletion(-) diff --git a/mode/smarty/smarty.js b/mode/smarty/smarty.js index 856f7bb3be..826c2b966c 100644 --- a/mode/smarty/smarty.js +++ b/mode/smarty/smarty.js @@ -91,6 +91,8 @@ CodeMirror.defineMode("smarty", function(config) { if (ch == "$") { stream.eatWhile(regs.validIdentifier); return helpers.cont("variable-2", "variable"); + } else if (ch == "|") { + return helpers.cont("operator", "pipe"); } else if (ch == ".") { return helpers.cont("operator", "property"); } else if (regs.stringChar.test(ch)) { @@ -101,6 +103,8 @@ CodeMirror.defineMode("smarty", function(config) { return helpers.cont("operator", "operator"); } else if (ch == "[" || ch == "]") { return helpers.cont("bracket", "bracket"); + } else if (ch == "(" || ch == ")") { + return helpers.cont("bracket", "operator"); } else if (/\d/.test(ch)) { stream.eatWhile(/\d/); return helpers.cont("number", "number"); @@ -114,6 +118,9 @@ CodeMirror.defineMode("smarty", function(config) { stream.eatWhile(regs.validIdentifier); return helpers.cont("qualifier", "modifier"); } + } else if (state.last == "pipe") { + stream.eatWhile(regs.validIdentifier); + return helpers.cont("qualifier", "modifier"); } else if (state.last == "whitespace") { stream.eatWhile(regs.validIdentifier); return helpers.cont("attribute", "modifier"); @@ -147,11 +154,15 @@ CodeMirror.defineMode("smarty", function(config) { inAttribute: function(quote) { return function(stream, state) { + var prevChar = null; + var currChar = null; while (!stream.eol()) { - if (stream.next() == quote) { + currChar = stream.peek(); + if (stream.next() == quote && prevChar !== '\\') { state.tokenize = parsers.smarty; break; } + prevChar = currChar; } return "string"; }; From 3f203d7b83d58a701ee9c980408fe1aab018795d Mon Sep 17 00:00:00 2001 From: John Connor Date: Sat, 25 May 2013 13:41:25 -0400 Subject: [PATCH 1120/5780] [vim keymap] 'dd' now handles last line corretly. Current behavior: ``` word1 word2 ``` If the cursor is on the last line and 'dd' is executed, the buffer does not change. Expected behavior: ``` word1 word2 ``` --- keymap/vim.js | 10 ++++++++++ test/vim_test.js | 7 +++++++ 2 files changed, 17 insertions(+) diff --git a/keymap/vim.js b/keymap/vim.js index 532e640a1f..51b93aec30 100644 --- a/keymap/vim.js +++ b/keymap/vim.js @@ -663,6 +663,9 @@ } RegisterController.prototype = { pushText: function(registerName, operator, text, linewise) { + if (linewise && text.charAt(0) == '\n') { + text = text.slice(1) + '\n'; + } // Lowercase and uppercase registers refer to the same register. // Uppercase just means append. var register = this.isValidRegister(registerName) ? @@ -1476,6 +1479,13 @@ }, // delete is a javascript keyword. 'delete': function(cm, operatorArgs, vim, curStart, curEnd) { + // If the ending line is past the last line, inclusive, instead of + // including the trailing \n, include the \n before the starting line + if (operatorArgs.linewise && + curEnd.line > cm.lastLine() && curStart.line > cm.firstLine()) { + curStart.line--; + curStart.ch = lineLength(cm, curStart.line); + } getVimGlobalState().registerController.pushText( operatorArgs.registerName, 'delete', cm.getRange(curStart, curEnd), operatorArgs.linewise); diff --git a/test/vim_test.js b/test/vim_test.js index 69714079de..f3b53508f6 100644 --- a/test/vim_test.js +++ b/test/vim_test.js @@ -782,6 +782,13 @@ testVim('dd_multiply_repeat', function(cm, vim, helpers) { is(register.linewise); helpers.assertCursorAt(0, lines[6].textStart); }); +testVim('dd_lastline', function(cm, vim, helpers) { + cm.setCursor(cm.lineCount(), 0); + var expectedLineCount = cm.lineCount() - 1; + helpers.doKeys('d', 'd'); + eq(expectedLineCount, cm.lineCount()); + helpers.assertCursorAt(cm.lineCount() - 1, 0); +}); // Yank commands should behave the exact same as d commands, expect that nothing // gets deleted. testVim('yw_repeat', function(cm, vim, helpers) { From 4c2fa0767a4168250bde05015ced6ec31f697686 Mon Sep 17 00:00:00 2001 From: Marijn Haverbeke Date: Mon, 27 May 2013 12:01:19 +0200 Subject: [PATCH 1121/5780] Update Webkit spanAffectsWrapping hack to recognize some common Unicode punctuation Issue #1219 --- lib/codemirror.js | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/lib/codemirror.js b/lib/codemirror.js index aa0de5de43..a68417598c 100644 --- a/lib/codemirror.js +++ b/lib/codemirror.js @@ -5287,7 +5287,7 @@ window.CodeMirror = (function() { spanAffectsWrapping = function(str, i) { if (i > 1 && str.charCodeAt(i - 1) == 45 && /\w/.test(str.charAt(i - 2)) && /[^\-?\.]/.test(str.charAt(i))) return true; - return /[~!#%&*)=+}\]|\"\.>,:;][({[<]|\?[\w~`@#$%\^&*(_=+{[|><]/.test(str.slice(i - 1, i + 1)); + return /[~!#%&*)=+}\]|\"\.>,:;][({[<]|-[^\-?\.\u2010-\u201f\u2026]|\?[\w~`@#$%\^&*(_=+{[|><]|…[\w~`@#$%\^&*(_=+{[><]/.test(str.slice(i - 1, i + 1)); }; var knownScrollbarWidth; From 9a50176800dfd9f4a5cdc19564a26381e71ed96f Mon Sep 17 00:00:00 2001 From: Marijn Haverbeke Date: Tue, 28 May 2013 08:17:47 +0200 Subject: [PATCH 1122/5780] [comment addon] Fix bug in uncommenting line-commented blocks (http://stackoverflow.com/questions/16702574/codemirror-comment-js-addon-wont-uncomment-in-ruby-mode) --- addon/comment/comment.js | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/addon/comment/comment.js b/addon/comment/comment.js index 4f590f2870..b25bd96f22 100644 --- a/addon/comment/comment.js +++ b/addon/comment/comment.js @@ -97,7 +97,7 @@ var line = self.getLine(i); var found = line.indexOf(lineString); if (found == -1 && (i != end || i == start) && nonWS.test(line)) break lineComment; - if (i != start && nonWS.test(line.slice(0, found))) break lineComment; + if (i != start && found > -1 && nonWS.test(line.slice(0, found))) break lineComment; lines.push(line); } self.operation(function() { From 8aaac4445a317fff82ab314e9dd6f6d8404c8ff7 Mon Sep 17 00:00:00 2001 From: Marijn Haverbeke Date: Tue, 28 May 2013 10:06:37 +0200 Subject: [PATCH 1123/5780] [xml mode] Make else indentation style correspond to rest of project --- mode/xml/xml.js | 32 ++++++++++++++------------------ 1 file changed, 14 insertions(+), 18 deletions(-) diff --git a/mode/xml/xml.js b/mode/xml/xml.js index b04248c6c6..ae71c64130 100644 --- a/mode/xml/xml.js +++ b/mode/xml/xml.js @@ -58,20 +58,19 @@ CodeMirror.defineMode("xml", function(config, parserConfig) { if (stream.eat("[")) { if (stream.match("CDATA[")) return chain(inBlock("atom", "]]>")); else return null; - } - else if (stream.match("--")) return chain(inBlock("comment", "-->")); - else if (stream.match("DOCTYPE", true, true)) { + } else if (stream.match("--")) { + return chain(inBlock("comment", "-->")); + } else if (stream.match("DOCTYPE", true, true)) { stream.eatWhile(/[\w\._\-]/); return chain(doctype(1)); + } else { + return null; } - else return null; - } - else if (stream.eat("?")) { + } else if (stream.eat("?")) { stream.eatWhile(/[\w\._\-]/); state.tokenize = inBlock("meta", "?>"); return "meta"; - } - else { + } else { var isClose = stream.eat("/"); tagName = ""; var c; @@ -81,8 +80,7 @@ CodeMirror.defineMode("xml", function(config, parserConfig) { state.tokenize = inTag; return "tag"; } - } - else if (ch == "&") { + } else if (ch == "&") { var ok; if (stream.eat("#")) { if (stream.eat("x")) { @@ -94,8 +92,7 @@ CodeMirror.defineMode("xml", function(config, parserConfig) { ok = stream.eatWhile(/[\w\.\-:]/) && stream.eat(";"); } return ok ? "atom" : "error"; - } - else { + } else { stream.eatWhile(/[^&<]/); return null; } @@ -107,16 +104,15 @@ CodeMirror.defineMode("xml", function(config, parserConfig) { state.tokenize = inText; type = ch == ">" ? "endTag" : "selfcloseTag"; return "tag"; - } - else if (ch == "=") { + } else if (ch == "=") { type = "equals"; return null; - } - else if (/[\'\"]/.test(ch)) { + } else if (ch == "<") { + return "error"; + } else if (/[\'\"]/.test(ch)) { state.tokenize = inAttribute(ch); return state.tokenize(stream, state); - } - else { + } else { stream.eatWhile(/[^\s\u00a0=<>\"\']/); return "word"; } From a85d89c082cb5764ad137b6d1a3d79cffbb7f275 Mon Sep 17 00:00:00 2001 From: Marijn Haverbeke Date: Tue, 28 May 2013 13:49:18 +0200 Subject: [PATCH 1124/5780] [xml-hint addon] Rewrite from scratch Using some of the ideas from the html5 hinter. Can now use a much richer source of hinting information, and also completes properties and property values. --- addon/hint/show-hint.js | 13 +++- addon/hint/xml-hint.js | 168 +++++++++++++--------------------------- demo/xmlcomplete.html | 119 ++++++++++++++++------------ doc/manual.html | 33 +++++++- 4 files changed, 164 insertions(+), 169 deletions(-) diff --git a/addon/hint/show-hint.js b/addon/hint/show-hint.js index 2c54dcd77e..504fda786b 100644 --- a/addon/hint/show-hint.js +++ b/addon/hint/show-hint.js @@ -1,7 +1,7 @@ CodeMirror.showHint = function(cm, getHints, options) { if (!options) options = {}; var startCh = cm.getCursor().ch, continued = false; - var closeOn = options.closeCharacters || /[\s()\[\]{};:]/; + var closeOn = options.closeCharacters || /[\s()\[\]{};:>]/; function startHinting() { // We want a single cursor position. @@ -114,6 +114,7 @@ CodeMirror.showHint = function(cm, getHints, options) { } } else ourMap = baseMap; + cm.state.completionActive = true; cm.addKeyMap(ourMap); cm.on("cursorActivity", cursorActivity); var closingOnBlur; @@ -154,7 +155,10 @@ CodeMirror.showHint = function(cm, getHints, options) { cm.off("blur", onBlur); cm.off("focus", onFocus); cm.off("scroll", onScroll); - if (willContinue !== true) CodeMirror.signal(data, "close"); + if (willContinue !== true) { + CodeMirror.signal(data, "close"); + cm.state.completionActive = false; + } } function pick() { pickCompletion(cm, data, completions[selectedHint]); @@ -169,8 +173,9 @@ CodeMirror.showHint = function(cm, getHints, options) { pos.ch < startCh || cm.somethingSelected() || (pos.ch && closeOn.test(line.charAt(pos.ch - 1)))) close(); - else - once = setTimeout(function(){close(true); continued = true; startHinting();}, 70); + else { + once = setTimeout(function(){close(true); continued = true; startHinting();}, 170); + } } CodeMirror.signal(data, "select", completions[0], hints.firstChild); return true; diff --git a/addon/hint/xml-hint.js b/addon/hint/xml-hint.js index 42eab6f3b7..bc26a09c6d 100644 --- a/addon/hint/xml-hint.js +++ b/addon/hint/xml-hint.js @@ -1,118 +1,60 @@ (function() { - - CodeMirror.xmlHints = []; - - CodeMirror.xmlHint = function(cm) { - - var cursor = cm.getCursor(); - - if (cursor.ch > 0) { - - var text = cm.getRange(CodeMirror.Pos(0, 0), cursor); - var typed = ''; - var simbol = ''; - for(var i = text.length - 1; i >= 0; i--) { - if(text[i] == ' ' || text[i] == '<') { - simbol = text[i]; - break; - } - else { - typed = text[i] + typed; - } - } - - text = text.slice(0, text.length - typed.length); - - var path = getActiveElement(text) + simbol; - var hints = CodeMirror.xmlHints[path]; - - if(typeof hints === 'undefined') - hints = ['']; - else { - hints = hints.slice(0); - for (var i = hints.length - 1; i >= 0; i--) { - if(hints[i].indexOf(typed) != 0) - hints.splice(i, 1); - } - } - - return { - list: hints, - from: CodeMirror.Pos(cursor.line, cursor.ch - typed.length), - to: cursor - }; + "use strict"; + + var Pos = CodeMirror.Pos; + + CodeMirror.xmlHint = function(cm, options) { + var tags = options && options.schemaInfo; + if (!tags) return; + var cur = cm.getCursor(), token = cm.getTokenAt(cur); + var inner = CodeMirror.innerMode(cm.getMode(), token.state); + if (inner.mode.name != "xml") return; + var result = [], replaceToken = false, prefix; + var isTag = token.string.charAt(0) == "<"; + if (!inner.state.tagName || isTag) { // Tag completion + if (isTag) { + prefix = token.string.slice(1); + replaceToken = true; + } + var cx = inner.state.context, curTag = cx && tags[cx.tagName]; + var childList = cx ? curTag && curTag.children : tags["!top"]; + if (childList) { + for (var i = 0; i < childList.length; ++i) if (!prefix || childList[i].indexOf(prefix) == 0) + result.push("<" + childList[i]); + } else { + for (var name in tags) if (tags.hasOwnProperty(name) && name != "!top" && (!prefix || name.indexOf(prefix) == 0)) + result.push("<" + name); + } + if (cx && (!prefix || ("/" + cx.tagName).indexOf(prefix) == 0)) + result.push(""); + } else { + // Attribute completion + var curTag = tags[inner.state.tagName], attrs = curTag && curTag.attrs; + if (!attrs) return; + if (token.type == "string" || token.string == "=") { // A value + var before = cm.getRange(Pos(cur.line, Math.max(0, cur.ch - 60)), + Pos(cur.line, token.type == "string" ? token.start : token.end)); + var atName = before.match(/([^\s\u00a0=<>\"\']+)=$/), atValues; + if (!atName || !attrs.hasOwnProperty(atName[1]) || !(atValues = attrs[atName[1]])) return; + if (token.type == "string") { + prefix = token.string.charAt(0) == '"' ? token.string.slice(1) : token.string; + replaceToken = true; } - }; - - var getActiveElement = function(text) { - - var element = ''; - - if(text.length >= 0) { - - var regex = new RegExp('<([^!?][^\\s/>]*)[\\s\\S]*?>', 'g'); - - var matches = []; - var match; - while ((match = regex.exec(text)) != null) { - matches.push({ - tag: match[1], - selfclose: (match[0].slice(match[0].length - 2) === '/>') - }); - } - - for (var i = matches.length - 1, skip = 0; i >= 0; i--) { - - var item = matches[i]; - - if (item.tag[0] == '/') - { - skip++; - } - else if (item.selfclose == false) - { - if (skip > 0) - { - skip--; - } - else - { - element = '<' + item.tag + '>' + element; - } - } - } - - element += getOpenTag(text); + for (var i = 0; i < atValues.length; ++i) if (!prefix || atValues[i].indexOf(prefix) == 0) + result.push('"' + atValues[i] + '"'); + } else { // An attribute name + if (token.type == "attribute") { + prefix = token.string; + replaceToken = true; } - - return element; + for (var attr in attrs) if (attrs.hasOwnProperty(attr) && (!prefix || attr.indexOf(prefix) == 0)) + result.push(attr); + } + } + return { + list: result, + from: replaceToken ? Pos(cur.line, token.start) : cur, + to: replaceToken ? Pos(cur.line, token.end) : cur }; - - var getOpenTag = function(text) { - - var open = text.lastIndexOf('<'); - var close = text.lastIndexOf('>'); - - if (close < open) - { - text = text.slice(open); - - if(text != '<') { - - var space = text.indexOf(' '); - if(space < 0) - space = text.indexOf('\t'); - if(space < 0) - space = text.indexOf('\n'); - - if (space < 0) - space = text.length; - - return text.slice(0, space); - } - } - - return ''; - }; - + }; })(); diff --git a/demo/xmlcomplete.html b/demo/xmlcomplete.html index 28a50638f7..a03b4c5fcc 100644 --- a/demo/xmlcomplete.html +++ b/demo/xmlcomplete.html @@ -7,75 +7,96 @@ -

    CodeMirror: XML Autocomplete demo

    -
    +
    -

    Type '<' or space inside tag or - press ctrl-space to activate autocompletion. See - the code (here - and here) to figure out how - it works.

    +

    Press ctrl-space, or type a '<' character to + activate autocompletion. This demo defines a simple schema that + guides completion. The schema can be customized—see + the manual.

    diff --git a/doc/manual.html b/doc/manual.html index 6fa58c6e30..07e72c7b63 100644 --- a/doc/manual.html +++ b/doc/manual.html @@ -1682,12 +1682,39 @@

    Add-ons

    is an array of strings (the completions), and from and to give the start and end of the token that is being completed. Depends - on addon/hint/show-hint.css. See the other files in - the addon/hint for - hint sources for various languages. Check + on addon/hint/show-hint.css. Check out the demo for an example.
    +
    hint/javascript-hint.js
    +
    Defines a simple hinting function for JavaScript + (CodeMirror.javascriptHint) and CoffeeScript + (CodeMirror.coffeescriptHint) code. This will + simply use the JavaScript environment that the editor runs in as + a source of information about objects and their properties.
    + +
    hint/xml-hint.js
    +
    Defines CodeMirror.xmlHint, which produces + hints for XML tagnames, attribute names, and attribute values, + guided by a schemaInfo option (a property of the + second argument passed to the hinting function, or the third + argument passed to CodeMirror.showHint).
    The + schema info should be an object mapping tag names to information + about these tags, with optionally a "!top" property + containing a list of the names of valid top-level tags. The + values of the properties should be objects with optional + properties children (an array of valid child + element names, omit to simply allow all tags to appear) + and attrs (an object mapping attribute names + to null for free-form attributes, and an array of + valid values for restricted + attributes). Demo + here.
    + +
    hint/python-hint.js
    +
    A very simple hinting function for Python code. + Defines CodeMirror.pythonHint.
    +
    match-highlighter.js
    Adds a highlightSelectionMatches option that can be enabled to highlight all instances of a currently From d23d0589aae8d98f236ef0eb0e29b435fcad3b9f Mon Sep 17 00:00:00 2001 From: Marijn Haverbeke Date: Tue, 28 May 2013 15:06:36 +0200 Subject: [PATCH 1125/5780] [html-hint addon] Rewrite to reuse xml-hint instead of own hinting function --- addon/hint/html-hint.js | 895 +++++++++++++++------------------------- demo/html5complete.html | 76 +--- doc/manual.html | 11 + 3 files changed, 349 insertions(+), 633 deletions(-) diff --git a/addon/hint/html-hint.js b/addon/hint/html-hint.js index 8b5dc6f002..23238df054 100755 --- a/addon/hint/html-hint.js +++ b/addon/hint/html-hint.js @@ -1,582 +1,335 @@ (function () { - function htmlHint(editor, htmlStructure, getToken) { - var cur = editor.getCursor(); - var token = getToken(editor, cur); - var keywords = []; - var i = 0; - var j = 0; - var k = 0; - var from = {line: cur.line, ch: cur.ch}; - var to = {line: cur.line, ch: cur.ch}; - var flagClean = true; + var langs = "ab aa af ak sq am ar an hy as av ae ay az bm ba eu be bn bh bi bs br bg my ca ch ce ny zh cv kw co cr hr cs da dv nl dz en eo et ee fo fj fi fr ff gl ka de el gn gu ht ha he hz hi ho hu ia id ie ga ig ik io is it iu ja jv kl kn kr ks kk km ki rw ky kv kg ko ku kj la lb lg li ln lo lt lu lv gv mk mg ms ml mt mi mr mh mn na nv nb nd ne ng nn no ii nr oc oj cu om or os pa pi fa pl ps pt qu rm rn ro ru sa sc sd se sm sg sr gd sn si sk sl so st es su sw ss sv ta te tg th ti bo tk tl tn to tr ts tt tw ty ug uk ur uz ve vi vo wa cy wo fy xh yi yo za zu".split(" "); + var targets = ["_blank", "_self", "_top", "_parent"]; + var charsets = ["ascii", "utf-8", "utf-16", "latin1", "latin1"]; + var methods = ["get", "post", "put", "delete"]; + var encs = ["application/x-www-form-urlencoded", "multipart/form-data", "text/plain"]; + var media = ["all", "screen", "print", "embossed", "braille", "handheld", "print", "projection", "screen", "tty", "tv", "speech", + "3d-glasses", "resolution [>][<][=] [X]", "device-aspect-ratio: X/Y", "orientation:portrait", + "orientation:landscape", "device-height: [X]", "device-width: [X]"]; + var s = { attrs: {} }; // Simple tag, reused for a whole lot of tags - var text = editor.getRange({line: 0, ch: 0}, cur); - - var open = text.lastIndexOf('<'); - var close = text.lastIndexOf('>'); - var tokenString = token.string.replace("<",""); - - if(open > close) { - var last = editor.getRange({line: cur.line, ch: cur.ch - 1}, cur); - if(last == "<") { - for(i = 0; i < htmlStructure.length; i++) { - keywords.push(htmlStructure[i].tag); - } - from.ch = token.start + 1; - } else { - var counter = 0; - var found = function(token, type, position) { - counter++; - if(counter > 50) return; - if(token.type == type) { - return token; - } else { - position.ch = token.start; - var newToken = editor.getTokenAt(position); - return found(newToken, type, position); - } - }; - - var nodeToken = found(token, "tag", {line: cur.line, ch: cur.ch}); - var node = nodeToken.string.substring(1); - - if(token.type === null && token.string.trim() === "") { - for(i = 0; i < htmlStructure.length; i++) { - if(htmlStructure[i].tag == node) { - for(j = 0; j < htmlStructure[i].attr.length; j++) { - keywords.push(htmlStructure[i].attr[j].key + "=\"\" "); - } - - for(k = 0; k < globalAttributes.length; k++) { - keywords.push(globalAttributes[k].key + "=\"\" "); - } - } - } - } else if(token.type == "string") { - tokenString = tokenString.substring(1, tokenString.length - 1); - var attributeToken = found(token, "attribute", {line: cur.line, ch: cur.ch}); - var attribute = attributeToken.string; - - for(i = 0; i < htmlStructure.length; i++) { - if(htmlStructure[i].tag == node) { - for(j = 0; j < htmlStructure[i].attr.length; j++) { - if(htmlStructure[i].attr[j].key == attribute) { - for(k = 0; k < htmlStructure[i].attr[j].values.length; k++) { - keywords.push(htmlStructure[i].attr[j].values[k]); - } - } - } - - for(j = 0; j < globalAttributes.length; j++) { - if(globalAttributes[j].key == attribute) { - for(k = 0; k < globalAttributes[j].values.length; k++) { - keywords.push(globalAttributes[j].values[k]); - } - } - } - } - } - from.ch = token.start + 1; - } else if(token.type == "attribute") { - for(i = 0; i < htmlStructure.length; i++) { - if(htmlStructure[i].tag == node) { - for(j = 0; j < htmlStructure[i].attr.length; j++) { - keywords.push(htmlStructure[i].attr[j].key + "=\"\" "); - } - - for(k = 0; k < globalAttributes.length; k++) { - keywords.push(globalAttributes[k].key + "=\"\" "); - } - } - } - from.ch = token.start; - } else if(token.type == "tag") { - for(i = 0; i < htmlStructure.length; i++) { - keywords.push(htmlStructure[i].tag); - } - - from.ch = token.start + 1; - } + var data = { + a: { + attrs: { + href: null, ping: null, type: null, + media: media, + target: targets, + hreflang: langs } - } else { - for(i = 0; i < htmlStructure.length; i++) { - keywords.push("<" + htmlStructure[i].tag); + }, + abbr: s, + acronym: s, + address: s, + applet: s, + area: { + attrs: { + alt: null, coords: null, href: null, target: null, ping: null, + media: media, hreflang: langs, type: null, + shape: ["default", "rect", "circle", "poly"] } - - tokenString = ("<" + tokenString).trim(); - from.ch = token.start; - } - - if(flagClean === true && tokenString.trim() === "") { - flagClean = false; - } - - if(flagClean) { - keywords = cleanResults(tokenString, keywords); - } - - return {list: keywords, from: from, to: to}; - } - - - var cleanResults = function(text, keywords) { - var results = []; - var i = 0; - - for(i = 0; i < keywords.length; i++) { - if(keywords[i].substring(0, text.length) == text) { - results.push(keywords[i]); + }, + article: s, + aside: s, + audio: { + attrs: { + src: null, mediagroup: null, + crossorigin: ["anonymous", "use-credentials"], + preload: ["none", "metadata", "auto"], + autoplay: ["", "autoplay"], + loop: ["", "loop"], + controls: ["", "controls"] } - } - - return results; + }, + b: s, + base: { attrs: { href: null, target: targets } }, + basefont: s, + bdi: s, + bdo: s, + big: s, + blockquote: { attrs: { cite: null } }, + body: s, + br: s, + button: { + attrs: { + form: null, formaction: null, name: null, value: null, + autofocus: ["", "autofocus"], + disabled: ["", "autofocus"], + formenctype: encs, + formmethod: methods, + formnovalidate: ["", "novalidate"], + formtarget: targets, + type: ["submit", "reset", "button"] + } + }, + canvas: { attrs: { width: null, height: null } }, + caption: s, + center: s, + cite: s, + code: s, + col: { attrs: { span: null } }, + colgroup: { attrs: { span: null } }, + command: { + attrs: { + type: ["command", "checkbox", "radio"], + label: null, icon: null, radiogroup: null, command: null, title: null, + disabled: ["", "disabled"], + checked: ["", "checked"] + } + }, + data: { attrs: { value: null } }, + datagrid: { attrs: { disabled: ["", "disabled"], multiple: ["", "multiple"] } }, + datalist: { attrs: { data: null } }, + dd: s, + del: { attrs: { cite: null, datetime: null } }, + details: { attrs: { open: ["", "open"] } }, + dfn: s, + dir: s, + div: s, + dl: s, + dt: s, + em: s, + embed: { attrs: { src: null, type: null, width: null, height: null } }, + eventsource: { attrs: { src: null } }, + fieldset: { attrs: { disabled: ["", "disabled"], form: null, name: null } }, + figcaption: s, + figure: s, + font: s, + footer: s, + form: { + attrs: { + action: null, name: null, + "accept-charset": charsets, + autocomplete: ["on", "off"], + enctype: encs, + method: methods, + novalidate: ["", "novalidate"], + target: targets + } + }, + frame: s, + frameset: s, + h1: s, h2: s, h3: s, h4: s, h5: s, h6: s, + head: { + attrs: {}, + children: ["title", "base", "link", "style", "meta", "script", "noscript", "command"] + }, + header: s, + hgroup: s, + hr: s, + html: { + attrs: { manifest: null }, + children: ["head", "body"] + }, + i: s, + iframe: { + attrs: { + src: null, srcdoc: null, name: null, width: null, height: null, + sandbox: ["allow-top-navigation", "allow-same-origin", "allow-forms", "allow-scripts"], + seamless: ["", "seamless"] + } + }, + img: { + attrs: { + alt: null, src: null, ismap: null, usemap: null, width: null, height: null, + crossorigin: ["anonymous", "use-credentials"] + } + }, + input: { + attrs: { + alt: null, dirname: null, form: null, formaction: null, + height: null, list: null, max: null, maxlength: null, min: null, + name: null, pattern: null, placeholder: null, size: null, src: null, + step: null, value: null, width: null, + accept: ["audio/*", "video/*", "image/*"], + autocomplete: ["on", "off"], + autofocus: ["", "autofocus"], + checked: ["", "checked"], + disabled: ["", "disabled"], + formenctype: encs, + formmethod: methods, + formnovalidate: ["", "novalidate"], + formtarget: targets, + multiple: ["", "multiple"], + readonly: ["", "readonly"], + required: ["", "required"], + type: ["hidden", "text", "search", "tel", "url", "email", "password", "datetime", "date", "month", + "week", "time", "datetime-local", "number", "range", "color", "checkbox", "radio", + "file", "submit", "image", "reset", "button"] + } + }, + ins: { attrs: { cite: null, datetime: null } }, + kbd: s, + keygen: { + attrs: { + challenge: null, form: null, name: null, + autofocus: ["", "autofocus"], + disabled: ["", "disabled"], + keytype: ["RSA"] + } + }, + label: { attrs: { "for": null, form: null } }, + legend: s, + li: { attrs: { value: null } }, + link: { + attrs: { + href: null, type: null, + hreflang: langs, + media: media, + sizes: ["all", "16x16", "16x16 32x32", "16x16 32x32 64x64"] + } + }, + map: { attrs: { name: null } }, + mark: s, + menu: { attrs: { label: null, type: ["list", "context", "toolbar"] } }, + meta: { + attrs: { + content: null, + charset: charsets, + name: ["viewport", "application-name", "author", "description", "generator", "keywords"], + "http-equiv": ["content-language", "content-type", "default-style", "refresh"] + } + }, + meter: { attrs: { value: null, min: null, low: null, high: null, max: null, optimum: null } }, + nav: s, + noframes: s, + noscript: s, + object: { + attrs: { + data: null, type: null, name: null, usemap: null, form: null, width: null, height: null, + typemustmatch: ["", "typemustmatch"] + } + }, + ol: { attrs: { reversed: ["", "reversed"], start: null, type: ["1", "a", "A", "i", "I"] } }, + optgroup: { attrs: { disabled: ["", "disabled"], label: null } }, + option: { attrs: { disabled: ["", "disabled"], label: null, selected: ["", "selected"], value: null } }, + output: { attrs: { "for": null, form: null, name: null } }, + p: s, + param: { attrs: { name: null, value: null } }, + pre: s, + progress: { attrs: { value: null, max: null } }, + q: { attrs: { cite: null } }, + rp: s, + rt: s, + ruby: s, + s: s, + samp: s, + script: { + attrs: { + type: ["text/javascript"], + src: null, + async: ["", "async"], + defer: ["", "defer"], + charset: charsets + } + }, + section: s, + select: { + attrs: { + form: null, name: null, size: null, + autofocus: ["", "autofocus"], + disabled: ["", "disabled"], + multiple: ["", "multiple"] + } + }, + small: s, + source: { attrs: { src: null, type: null, media: null } }, + span: s, + strike: s, + strong: s, + style: { + attrs: { + type: ["text/css"], + media: media, + scoped: null + } + }, + sub: s, + summary: s, + sup: s, + table: s, + tbody: s, + td: { attrs: { colspan: null, rowspan: null, headers: null } }, + textarea: { + attrs: { + dirname: null, form: null, maxlength: null, name: null, placeholder: null, + rows: null, cols: null, + autofocus: ["", "autofocus"], + disabled: ["", "disabled"], + readonly: ["", "readonly"], + required: ["", "required"], + wrap: ["soft", "hard"] + } + }, + tfoot: s, + th: { attrs: { colspan: null, rowspan: null, headers: null, scope: ["row", "col", "rowgroup", "colgroup"] } }, + thead: s, + time: { attrs: { datetime: null } }, + title: s, + tr: s, + track: { + attrs: { + src: null, label: null, "default": null, + kind: ["subtitles", "captions", "descriptions", "chapters", "metadata"], + srclang: langs + } + }, + tt: s, + u: s, + ul: s, + "var": s, + video: { + attrs: { + src: null, poster: null, width: null, height: null, + crossorigin: ["anonymous", "use-credentials"], + preload: ["auto", "metadata", "none"], + autoplay: ["", "autoplay"], + mediagroup: ["movie"], + muted: ["", "muted"], + controls: ["", "controls"] + } + }, + wbr: s }; - var htmlStructure = [ - {tag: '!DOCTYPE', attr: []}, - {tag: 'a', attr: [ - {key: 'href', values: ["#"]}, - {key: 'target', values: ["_blank","_self","_top","_parent"]}, - {key: 'ping', values: [""]}, - {key: 'media', values: ["#"]}, - {key: 'hreflang', values: ["en","es"]}, - {key: 'type', values: []} - ]}, - {tag: 'abbr', attr: []}, - {tag: 'acronym', attr: []}, - {tag: 'address', attr: []}, - {tag: 'applet', attr: []}, - {tag: 'area', attr: [ - {key: 'alt', values: [""]}, - {key: 'coords', values: ["rect: left, top, right, bottom","circle: center-x, center-y, radius","poly: x1, y1, x2, y2, ..."]}, - {key: 'shape', values: ["default","rect","circle","poly"]}, - {key: 'href', values: ["#"]}, - {key: 'target', values: ["#"]}, - {key: 'ping', values: []}, - {key: 'media', values: []}, - {key: 'hreflang', values: []}, - {key: 'type', values: []} - - ]}, - {tag: 'article', attr: []}, - {tag: 'aside', attr: []}, - {tag: 'audio', attr: [ - {key: 'src', values: []}, - {key: 'crossorigin', values: ["anonymous","use-credentials"]}, - {key: 'preload', values: ["none","metadata","auto"]}, - {key: 'autoplay', values: ["","autoplay"]}, - {key: 'mediagroup', values: []}, - {key: 'loop', values: ["","loop"]}, - {key: 'controls', values: ["","controls"]} - ]}, - {tag: 'b', attr: []}, - {tag: 'base', attr: [ - {key: 'href', values: ["#"]}, - {key: 'target', values: ["_blank","_self","_top","_parent"]} - ]}, - {tag: 'basefont', attr: []}, - {tag: 'bdi', attr: []}, - {tag: 'bdo', attr: []}, - {tag: 'big', attr: []}, - {tag: 'blockquote', attr: [ - {key: 'cite', values: ["http://"]} - ]}, - {tag: 'body', attr: []}, - {tag: 'br', attr: []}, - {tag: 'button', attr: [ - {key: 'autofocus', values: ["","autofocus"]}, - {key: 'disabled', values: ["","disabled"]}, - {key: 'form', values: []}, - {key: 'formaction', values: []}, - {key: 'formenctype', values: ["application/x-www-form-urlencoded","multipart/form-data","text/plain"]}, - {key: 'formmethod', values: ["get","post","put","delete"]}, - {key: 'formnovalidate', values: ["","novalidate"]}, - {key: 'formtarget', values: ["_blank","_self","_top","_parent"]}, - {key: 'name', values: []}, - {key: 'type', values: ["submit","reset","button"]}, - {key: 'value', values: []} - ]}, - {tag: 'canvas', attr: [ - {key: 'width', values: []}, - {key: 'height', values: []} - ]}, - {tag: 'caption', attr: []}, - {tag: 'center', attr: []}, - {tag: 'cite', attr: []}, - {tag: 'code', attr: []}, - {tag: 'col', attr: [ - {key: 'span', values: []} - ]}, - {tag: 'colgroup', attr: [ - {key: 'span', values: []} - ]}, - {tag: 'command', attr: [ - {key: 'type', values: ["command","checkbox","radio"]}, - {key: 'label', values: []}, - {key: 'icon', values: []}, - {key: 'disabled', values: ["","disabled"]}, - {key: 'checked', values: ["","checked"]}, - {key: 'radiogroup', values: []}, - {key: 'command', values: []}, - {key: 'title', values: []} - ]}, - {tag: 'data', attr: [ - {key: 'value', values: []} - ]}, - {tag: 'datagrid', attr: [ - {key: 'disabled', values: ["","disabled"]}, - {key: 'multiple', values: ["","multiple"]} - ]}, - {tag: 'datalist', attr: [ - {key: 'data', values: []} - ]}, - {tag: 'dd', attr: []}, - {tag: 'del', attr: [ - {key: 'cite', values: []}, - {key: 'datetime', values: []} - ]}, - {tag: 'details', attr: [ - {key: 'open', values: ["","open"]} - ]}, - {tag: 'dfn', attr: []}, - {tag: 'dir', attr: []}, - {tag: 'div', attr: [ - {key: 'id', values: []}, - {key: 'class', values: []}, - {key: 'style', values: []} - ]}, - {tag: 'dl', attr: []}, - {tag: 'dt', attr: []}, - {tag: 'em', attr: []}, - {tag: 'embed', attr: [ - {key: 'src', values: []}, - {key: 'type', values: []}, - {key: 'width', values: []}, - {key: 'height', values: []} - ]}, - {tag: 'eventsource', attr: [ - {key: 'src', values: []} - ]}, - {tag: 'fieldset', attr: [ - {key: 'disabled', values: ["","disabled"]}, - {key: 'form', values: []}, - {key: 'name', values: []} - ]}, - {tag: 'figcaption', attr: []}, - {tag: 'figure', attr: []}, - {tag: 'font', attr: []}, - {tag: 'footer', attr: []}, - {tag: 'form', attr: [ - {key: 'accept-charset', values: ["UNKNOWN","utf-8"]}, - {key: 'action', values: []}, - {key: 'autocomplete', values: ["on","off"]}, - {key: 'enctype', values: ["application/x-www-form-urlencoded","multipart/form-data","text/plain"]}, - {key: 'method', values: ["get","post","put","delete","dialog"]}, - {key: 'name', values: []}, - {key: 'novalidate', values: ["","novalidate"]}, - {key: 'target', values: ["_blank","_self","_top","_parent"]} - ]}, - {tag: 'frame', attr: []}, - {tag: 'frameset', attr: []}, - {tag: 'h1', attr: []}, - {tag: 'h2', attr: []}, - {tag: 'h3', attr: []}, - {tag: 'h4', attr: []}, - {tag: 'h5', attr: []}, - {tag: 'h6', attr: []}, - {tag: 'head', attr: []}, - {tag: 'header', attr: []}, - {tag: 'hgroup', attr: []}, - {tag: 'hr', attr: []}, - {tag: 'html', attr: [ - {key: 'manifest', values: []} - ]}, - {tag: 'i', attr: []}, - {tag: 'iframe', attr: [ - {key: 'src', values: []}, - {key: 'srcdoc', values: []}, - {key: 'name', values: []}, - {key: 'sandbox', values: ["allow-top-navigation","allow-same-origin","allow-forms","allow-scripts"]}, - {key: 'seamless', values: ["","seamless"]}, - {key: 'width', values: []}, - {key: 'height', values: []} - ]}, - {tag: 'img', attr: [ - {key: 'alt', values: []}, - {key: 'src', values: []}, - {key: 'crossorigin', values: ["anonymous","use-credentials"]}, - {key: 'ismap', values: []}, - {key: 'usemap', values: []}, - {key: 'width', values: []}, - {key: 'height', values: []} - ]}, - {tag: 'input', attr: [ - {key: 'accept', values: ["audio/*","video/*","image/*"]}, - {key: 'alt', values: []}, - {key: 'autocomplete', values: ["on","off"]}, - {key: 'autofocus', values: ["","autofocus"]}, - {key: 'checked', values: ["","checked"]}, - {key: 'disabled', values: ["","disabled"]}, - {key: 'dirname', values: []}, - {key: 'form', values: []}, - {key: 'formaction', values: []}, - {key: 'formenctype', values: ["application/x-www-form-urlencoded","multipart/form-data","text/plain"]}, - {key: 'formmethod', values: ["get","post","put","delete"]}, - {key: 'formnovalidate', values: ["","novalidate"]}, - {key: 'formtarget', values: ["_blank","_self","_top","_parent"]}, - {key: 'height', values: []}, - {key: 'list', values: []}, - {key: 'max', values: []}, - {key: 'maxlength', values: []}, - {key: 'min', values: []}, - {key: 'multiple', values: ["","multiple"]}, - {key: 'name', values: []}, - {key: 'pattern', values: []}, - {key: 'placeholder', values: []}, - {key: 'readonly', values: ["","readonly"]}, - {key: 'required', values: ["","required"]}, - {key: 'size', values: []}, - {key: 'src', values: []}, - {key: 'step', values: []}, - {key: 'type', values: [ - "hidden","text","search","tel","url","email","password","datetime","date","month","week","time","datetime-local", - "number","range","color","checkbox","radio","file","submit","image","reset","button" - ]}, - {key: 'value', values: []}, - {key: 'width', values: []} - ]}, - {tag: 'ins', attr: [ - {key: 'cite', values: []}, - {key: 'datetime', values: []} - ]}, - {tag: 'kbd', attr: []}, - {tag: 'keygen', attr: [ - {key: 'autofocus', values: ["","autofocus"]}, - {key: 'challenge', values: []}, - {key: 'disabled', values: ["","disabled"]}, - {key: 'form', values: []}, - {key: 'keytype', values: ["RSA"]}, - {key: 'name', values: []} - ]}, - {tag: 'label', attr: [ - {key: 'for', values: []}, - {key: 'form', values: []} - ]}, - {tag: 'legend', attr: []}, - {tag: 'li', attr: [ - {key: 'value', values: []} - ]}, - {tag: 'link', attr: [ - {key: 'href', values: []}, - {key: 'hreflang', values: ["en","es"]}, - {key: 'media', values: [ - "all","screen","print","embossed","braille","handheld","print","projection","screen","tty","tv","speech","3d-glasses", - "resolution [>][<][=] [X]dpi","resolution [>][<][=] [X]dpcm","device-aspect-ratio: 16/9","device-aspect-ratio: 4/3", - "device-aspect-ratio: 32/18","device-aspect-ratio: 1280/720","device-aspect-ratio: 2560/1440","orientation:portrait", - "orientation:landscape","device-height: [X]px","device-width: [X]px","-webkit-min-device-pixel-ratio: 2" - ]}, - {key: 'type', values: []}, - {key: 'sizes', values: ["all","16x16","16x16 32x32","16x16 32x32 64x64"]} - ]}, - {tag: 'map', attr: [ - {key: 'name', values: []} - ]}, - {tag: 'mark', attr: []}, - {tag: 'menu', attr: [ - {key: 'type', values: ["list","context","toolbar"]}, - {key: 'label', values: []} - ]}, - {tag: 'meta', attr: [ - {key: 'charset', attr: ["utf-8"]}, - {key: 'name', attr: ["viewport","application-name","author","description","generator","keywords"]}, - {key: 'content', attr: ["","width=device-width","initial-scale=1, maximum-scale=1, minimun-scale=1, user-scale=no"]}, - {key: 'http-equiv', attr: ["content-language","content-type","default-style","refresh"]} - ]}, - {tag: 'meter', attr: [ - {key: 'value', values: []}, - {key: 'min', values: []}, - {key: 'low', values: []}, - {key: 'high', values: []}, - {key: 'max', values: []}, - {key: 'optimum', values: []} - ]}, - {tag: 'nav', attr: []}, - {tag: 'noframes', attr: []}, - {tag: 'noscript', attr: []}, - {tag: 'object', attr: [ - {key: 'data', values: []}, - {key: 'type', values: []}, - {key: 'typemustmatch', values: ["","typemustmatch"]}, - {key: 'name', values: []}, - {key: 'usemap', values: []}, - {key: 'form', values: []}, - {key: 'width', values: []}, - {key: 'height', values: []} - ]}, - {tag: 'ol', attr: [ - {key: 'reversed', values: ["", "reversed"]}, - {key: 'start', values: []}, - {key: 'type', values: ["1","a","A","i","I"]} - ]}, - {tag: 'optgroup', attr: [ - {key: 'disabled', values: ["","disabled"]}, - {key: 'label', values: []} - ]}, - {tag: 'option', attr: [ - {key: 'disabled', values: ["", "disabled"]}, - {key: 'label', values: []}, - {key: 'selected', values: ["", "selected"]}, - {key: 'value', values: []} - ]}, - {tag: 'output', attr: [ - {key: 'for', values: []}, - {key: 'form', values: []}, - {key: 'name', values: []} - ]}, - {tag: 'p', attr: []}, - {tag: 'param', attr: [ - {key: 'name', values: []}, - {key: 'value', values: []} - ]}, - {tag: 'pre', attr: []}, - {tag: 'progress', attr: [ - {key: 'value', values: []}, - {key: 'max', values: []} - ]}, - {tag: 'q', attr: [ - {key: 'cite', values: []} - ]}, - {tag: 'rp', attr: []}, - {tag: 'rt', attr: []}, - {tag: 'ruby', attr: []}, - {tag: 's', attr: []}, - {tag: 'samp', attr: []}, - {tag: 'script', attr: [ - {key: 'type', values: ["text/javascript"]}, - {key: 'src', values: []}, - {key: 'async', values: ["","async"]}, - {key: 'defer', values: ["","defer"]}, - {key: 'charset', values: ["utf-8"]} - ]}, - {tag: 'section', attr: []}, - {tag: 'select', attr: [ - {key: 'autofocus', values: ["", "autofocus"]}, - {key: 'disabled', values: ["", "disabled"]}, - {key: 'form', values: []}, - {key: 'multiple', values: ["", "multiple"]}, - {key: 'name', values: []}, - {key: 'size', values: []} - ]}, - {tag: 'small', attr: []}, - {tag: 'source', attr: [ - {key: 'src', values: []}, - {key: 'type', values: []}, - {key: 'media', values: []} - ]}, - {tag: 'span', attr: []}, - {tag: 'strike', attr: []}, - {tag: 'strong', attr: []}, - {tag: 'style', attr: [ - {key: 'type', values: ["text/css"]}, - {key: 'media', values: ["all","braille","print","projection","screen","speech"]}, - {key: 'scoped', values: []} - ]}, - {tag: 'sub', attr: []}, - {tag: 'summary', attr: []}, - {tag: 'sup', attr: []}, - {tag: 'table', attr: [ - {key: 'border', values: []} - ]}, - {tag: 'tbody', attr: []}, - {tag: 'td', attr: [ - {key: 'colspan', values: []}, - {key: 'rowspan', values: []}, - {key: 'headers', values: []} - ]}, - {tag: 'textarea', attr: [ - {key: 'autofocus', values: ["","autofocus"]}, - {key: 'disabled', values: ["","disabled"]}, - {key: 'dirname', values: []}, - {key: 'form', values: []}, - {key: 'maxlength', values: []}, - {key: 'name', values: []}, - {key: 'placeholder', values: []}, - {key: 'readonly', values: ["","readonly"]}, - {key: 'required', values: ["","required"]}, - {key: 'rows', values: []}, - {key: 'cols', values: []}, - {key: 'wrap', values: ["soft","hard"]} - ]}, - {tag: 'tfoot', attr: []}, - {tag: 'th', attr: [ - {key: 'colspan', values: []}, - {key: 'rowspan', values: []}, - {key: 'headers', values: []}, - {key: 'scope', values: ["row","col","rowgroup","colgroup"]} - ]}, - {tag: 'thead', attr: []}, - {tag: 'time', attr: [ - {key: 'datetime', values: []} - ]}, - {tag: 'title', attr: []}, - {tag: 'tr', attr: []}, - {tag: 'track', attr: [ - {key: 'kind', values: ["subtitles","captions","descriptions","chapters","metadata"]}, - {key: 'src', values: []}, - {key: 'srclang', values: ["en","es"]}, - {key: 'label', values: []}, - {key: 'default', values: []} - ]}, - {tag: 'tt', attr: []}, - {tag: 'u', attr: []}, - {tag: 'ul', attr: []}, - {tag: 'var', attr: []}, - {tag: 'video', attr: [ - {key: "src", values: []}, - {key: "crossorigin", values: ["anonymous","use-credentials"]}, - {key: "poster", values: []}, - {key: "preload", values: ["auto","metadata","none"]}, - {key: "autoplay", values: ["","autoplay"]}, - {key: "mediagroup", values: ["movie"]}, - {key: "loop", values: ["","loop"]}, - {key: "muted", values: ["","muted"]}, - {key: "controls", values: ["","controls"]}, - {key: "width", values: []}, - {key: "height", values: []} - ]}, - {tag: 'wbr', attr: []} - ]; + var globalAttrs = { + accesskey: ["a", "b", "c", "d", "e", "f", "g", "h", "i", "j", "k", "l", "m", "n", "o", "p", "q", "r", "s", "t", "u", "v", "w", "x", "y", "z", "0", "1", "2", "3", "4", "5", "6", "7", "8", "9"], + "class": null, + contenteditable: ["true", "false"], + contextmenu: null, + dir: ["ltr", "rtl", "auto"], + draggable: ["true", "false", "auto"], + dropzone: ["copy", "move", "link", "string:", "file:"], + hidden: ["hidden"], + id: null, + inert: ["inert"], + itemid: null, + itemprop: null, + itemref: null, + itemscope: ["itemscope"], + itemtype: null, + lang: ["en", "es"], + spellcheck: ["true", "false"], + style: null, + tabindex: ["1", "2", "3", "4", "5", "6", "7", "8", "9"], + title: null, + translate: ["yes", "no"], + onclick: null, + rel: ["stylesheet", "alternate", "author", "bookmark", "help", "license", "next", "nofollow", "noreferrer", "prefetch", "prev", "search", "tag"] + }; + function populate(obj) { + for (var attr in globalAttrs) if (globalAttrs.hasOwnProperty(attr)) + obj.attrs[attr] = globalAttrs[attr]; + } - var globalAttributes = [ - {key: "accesskey", values: ["a","b","c","d","e","f","g","h","i","j","k","l","m","n","o","p","q","r","s","t","u","v","w","x","y","z","0","1","2","3","4","5","6","7","8","9"]}, - {key: "class", values: []}, - {key: "contenteditable", values: ["true", "false"]}, - {key: "contextmenu", values: []}, - {key: "dir", values: ["ltr","rtl","auto"]}, - {key: "draggable", values: ["true","false","auto"]}, - {key: "dropzone", values: ["copy","move","link","string:","file:"]}, - {key: "hidden", values: ["hidden"]}, - {key: "id", values: []}, - {key: "inert", values: ["inert"]}, - {key: "itemid", values: []}, - {key: "itemprop", values: []}, - {key: "itemref", values: []}, - {key: "itemscope", values: ["itemscope"]}, - {key: "itemtype", values: []}, - {key: "lang", values: ["en","es"]}, - {key: "spellcheck", values: ["true","false"]}, - {key: "style", values: []}, - {key: "tabindex", values: ["1","2","3","4","5","6","7","8","9"]}, - {key: "title", values: []}, - {key: "translate", values: ["yes","no"]}, - {key: "onclick", values: []}, - {key: 'rel', values: ["stylesheet","alternate","author","bookmark","help","license","next","nofollow","noreferrer","prefetch","prev","search","tag"]} - ]; + populate(s); + for (var tag in data) if (data.hasOwnProperty(tag) && data[tag] != s) + populate(data[tag]); - CodeMirror.htmlHint = function(editor) { - if(String.prototype.trim == undefined) { - String.prototype.trim=function(){return this.replace(/^\s+|\s+$/g, '');}; - } - return htmlHint(editor, htmlStructure, function (e, cur) { return e.getTokenAt(cur); }); + CodeMirror.htmlSchema = data; + CodeMirror.htmlHint = function(cm, options) { + var local = {schemaInfo: data}; + if (options) for (var opt in options) local[opt] = options[opt]; + return CodeMirror.xmlHint(cm, local); }; })(); diff --git a/demo/html5complete.html b/demo/html5complete.html index 5091354ae9..5b2dd0de1d 100644 --- a/demo/html5complete.html +++ b/demo/html5complete.html @@ -2,12 +2,12 @@ - CodeMirror: Close-Tag Demo + CodeMirror: HTML completion demo - + @@ -21,72 +21,24 @@ -

    HTML5 code completation demo

    -
      -
    • Type an html tag. If you press Ctrl+Space a hint panel show the code suggest. You can type to autocomplete tags, attributes if your cursor are inner a tag or attribute values if your cursor are inner a attribute value.
    • -
    +

    HTML completion demo

    +

    Shows the XML completer + parameterized with information about the tags in HTML. + Press ctrl-space to activate completion.

    -
    diff --git a/doc/manual.html b/doc/manual.html index 07e72c7b63..3bfc3d0e91 100644 --- a/doc/manual.html +++ b/doc/manual.html @@ -1711,6 +1711,17 @@

    Add-ons

    attributes). Demo here.
    +
    hint/html-hint.js
    +
    Provides schema info to + the xml-hint addon for HTML + documents. Defines a schema + object CodeMirror.htmlSchema that you can pass to + as a schemaInfo option, and + a CodeMirror.htmlHint hinting function that + automatically calls CodeMirror.xmlHint with this + schema data. See + the demo.
    +
    hint/python-hint.js
    A very simple hinting function for Python code. Defines CodeMirror.pythonHint.
    From a22787fa013b6027d8c2ec94b75124a9c19dbb7b Mon Sep 17 00:00:00 2001 From: Marijn Haverbeke Date: Tue, 28 May 2013 19:35:29 +0200 Subject: [PATCH 1126/5780] Work around IE bug where drag events get fired twice Closes #1551 --- lib/codemirror.js | 57 ++++++++++++++++++++++++++--------------------- 1 file changed, 31 insertions(+), 26 deletions(-) diff --git a/lib/codemirror.js b/lib/codemirror.js index a68417598c..37f7a1100d 100644 --- a/lib/codemirror.js +++ b/lib/codemirror.js @@ -1744,11 +1744,41 @@ window.CodeMirror = (function() { on(document, "mouseup", up); } + function clickInGutter(cm, e) { + var display = cm.display; + try { var mX = e.clientX, mY = e.clientY; } + catch(e) { return false; } + + if (mX >= Math.floor(getRect(display.gutters).right)) return false; + e_preventDefault(e); + if (!hasHandler(cm, "gutterClick")) return true; + + var lineBox = getRect(display.lineDiv); + if (mY > lineBox.bottom) return true; + mY -= lineBox.top - display.viewOffset; + + for (var i = 0; i < cm.options.gutters.length; ++i) { + var g = display.gutters.childNodes[i]; + if (g && getRect(g).right >= mX) { + var line = lineAtHeight(cm.doc, mY); + var gutter = cm.options.gutters[i]; + signalLater(cm, "gutterClick", cm, line, gutter, e); + break; + } + } + return true; + } + + // Kludge to work around strange IE behavior where it'll sometimes + // re-fire a series of drag-related events right after the drop (#1551) + var lastDrop = 0; + function onDrop(e) { var cm = this; if (eventInWidget(cm.display, e) || (cm.options.onDragEvent && cm.options.onDragEvent(cm, addStop(e)))) return; e_preventDefault(e); + if (ie) lastDrop = +new Date; var pos = posFromMouse(cm, e, true), files = e.dataTransfer.files; if (!pos || isReadOnly(cm)) return; if (files && files.length && window.FileReader && window.File) { @@ -1788,33 +1818,8 @@ window.CodeMirror = (function() { } } - function clickInGutter(cm, e) { - var display = cm.display; - try { var mX = e.clientX, mY = e.clientY; } - catch(e) { return false; } - - if (mX >= Math.floor(getRect(display.gutters).right)) return false; - e_preventDefault(e); - if (!hasHandler(cm, "gutterClick")) return true; - - var lineBox = getRect(display.lineDiv); - if (mY > lineBox.bottom) return true; - mY -= lineBox.top - display.viewOffset; - - for (var i = 0; i < cm.options.gutters.length; ++i) { - var g = display.gutters.childNodes[i]; - if (g && getRect(g).right >= mX) { - var line = lineAtHeight(cm.doc, mY); - var gutter = cm.options.gutters[i]; - signalLater(cm, "gutterClick", cm, line, gutter, e); - break; - } - } - return true; - } - function onDragStart(cm, e) { - if (ie && !cm.state.draggingText) { e_stop(e); return; } + if (ie && (!cm.state.draggingText || +new Date - lastDrop < 100)) { e_stop(e); return; } if (eventInWidget(cm.display, e)) return; var txt = cm.getSelection(); From c71f96fbf30af73c19c8453ea32cfc9d2d053975 Mon Sep 17 00:00:00 2001 From: Marijn Haverbeke Date: Tue, 28 May 2013 19:59:48 +0200 Subject: [PATCH 1127/5780] [javascript mode] More comma-handling tweaks Issue #1443 --- mode/javascript/javascript.js | 17 +++++++++++------ 1 file changed, 11 insertions(+), 6 deletions(-) diff --git a/mode/javascript/javascript.js b/mode/javascript/javascript.js index fabe1c42b9..ab76a57e81 100644 --- a/mode/javascript/javascript.js +++ b/mode/javascript/javascript.js @@ -270,17 +270,18 @@ CodeMirror.defineMode("javascript", function(config, parserConfig) { return pass(pushlex("stat"), expression, expect(";"), poplex); } function expression(type) { - return expressionInner(type, maybeoperatorComma); + return expressionInner(type, false); } function expressionNoComma(type) { - return expressionInner(type, maybeoperatorNoComma); + return expressionInner(type, true); } - function expressionInner(type, maybeop) { + function expressionInner(type, noComma) { + var maybeop = noComma ? maybeoperatorNoComma : maybeoperatorComma; if (atomicTypes.hasOwnProperty(type)) return cont(maybeop); if (type == "function") return cont(functiondef); - if (type == "keyword c") return cont(maybeexpression); + if (type == "keyword c") return cont(noComma ? maybeexpressionNoComma : maybeexpression); if (type == "(") return cont(pushlex(")"), maybeexpression, expect(")"), poplex, maybeop); - if (type == "operator") return cont(expression); + if (type == "operator") return cont(noComma ? expressionNoComma : expression); if (type == "[") return cont(pushlex("]"), commasep(expressionNoComma, "]"), poplex, maybeop); if (type == "{") return cont(pushlex("}"), commasep(objprop, "}"), poplex, maybeop); return cont(); @@ -289,9 +290,13 @@ CodeMirror.defineMode("javascript", function(config, parserConfig) { if (type.match(/[;\}\)\],]/)) return pass(); return pass(expression); } + function maybeexpressionNoComma(type) { + if (type.match(/[;\}\)\],]/)) return pass(); + return pass(expressionNoComma); + } function maybeoperatorComma(type, value) { - if (type == ",") return pass(); + if (type == ",") return cont(expression); return maybeoperatorNoComma(type, value, maybeoperatorComma); } function maybeoperatorNoComma(type, value, me) { From a828fa125508eb7c6e59894af20ff5918811da37 Mon Sep 17 00:00:00 2001 From: Marijn Haverbeke Date: Tue, 28 May 2013 21:57:40 +0200 Subject: [PATCH 1128/5780] [xml-hint demo] Fix minor bug, add sponsor link --- demo/xmlcomplete.html | 5 +++++ 1 file changed, 5 insertions(+) diff --git a/demo/xmlcomplete.html b/demo/xmlcomplete.html index a03b4c5fcc..5363b39611 100644 --- a/demo/xmlcomplete.html +++ b/demo/xmlcomplete.html @@ -25,6 +25,10 @@

    CodeMirror: XML Autocomplete demo

    guides completion. The schema can be customized—see the manual.

    +

    Development of the xml-hint addon was kindly + sponsored + by www.xperiment.mobi.

    + From ba5554ddeaae8e6525d7ea196ec8da3ba1dac6a3 Mon Sep 17 00:00:00 2001 From: Marijn Haverbeke Date: Wed, 29 May 2013 10:05:15 +0200 Subject: [PATCH 1132/5780] Fix partial-updating of lines with line widgets above them Issue #1554 --- lib/codemirror.js | 8 ++++---- 1 file changed, 4 insertions(+), 4 deletions(-) diff --git a/lib/codemirror.js b/lib/codemirror.js index 0cbfbb1917..0865b55889 100644 --- a/lib/codemirror.js +++ b/lib/codemirror.js @@ -656,25 +656,25 @@ window.CodeMirror = (function() { if (reuse) { reuse.alignable = null; - var isOk = true, widgetsSeen = 0; + var isOk = true, widgetsSeen = 0, insertBefore = null; for (var n = reuse.firstChild, next; n; n = next) { next = n.nextSibling; if (!/\bCodeMirror-linewidget\b/.test(n.className)) { reuse.removeChild(n); } else { for (var i = 0, first = true; i < line.widgets.length; ++i) { - var widget = line.widgets[i], isFirst = false; - if (!widget.above) { isFirst = first; first = false; } + var widget = line.widgets[i]; + if (!widget.above) { insertBefore = n; first = false; } if (widget.node == n.firstChild) { positionLineWidget(widget, n, reuse, dims); ++widgetsSeen; - if (isFirst) reuse.insertBefore(lineElement, n); break; } } if (i == line.widgets.length) { isOk = false; break; } } } + reuse.insertBefore(lineElement, insertBefore); if (isOk && widgetsSeen == line.widgets.length) { wrap = reuse; reuse.className = line.wrapClass || ""; From 3328ed0ebc31182f3853d6564d45bcabcc8c1806 Mon Sep 17 00:00:00 2001 From: Marijn Haverbeke Date: Thu, 30 May 2013 12:14:24 +0200 Subject: [PATCH 1133/5780] Reset scrollbarwidth cache when window is resized Zooming can produce different measurements. --- lib/codemirror.js | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/lib/codemirror.js b/lib/codemirror.js index 0865b55889..96a84d3d89 100644 --- a/lib/codemirror.js +++ b/lib/codemirror.js @@ -1518,7 +1518,7 @@ window.CodeMirror = (function() { if (resizeTimer == null) resizeTimer = setTimeout(function() { resizeTimer = null; // Might be a text scaling operation, clear size caches. - d.cachedCharWidth = d.cachedTextHeight = null; + d.cachedCharWidth = d.cachedTextHeight = knownScrollbarWidth = null; clearCaches(cm); runInOp(cm, bind(regChange, cm)); }, 100); From 0bfab07985e5734a2d8eb364d9d12d4ddcfe1719 Mon Sep 17 00:00:00 2001 From: MinRK Date: Thu, 23 May 2013 12:41:39 -0700 Subject: [PATCH 1134/5780] forgive one pixel in scrollbars Sometimes I find that CodeMirror adds scrollbars inappropriately (most often due to browser zoom). Digging around suggested that it is some non-integers resulting in rounding up the measured size, and I haven't been able to find a case where the two values differ by more than one pixel, so simply setting the threshold one pixel higher seems to address the issue. This PR forgives one pixel in the size comparison before drawing the scrollbars. --- lib/codemirror.js | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/lib/codemirror.js b/lib/codemirror.js index 96a84d3d89..717ac6f4d5 100644 --- a/lib/codemirror.js +++ b/lib/codemirror.js @@ -323,8 +323,8 @@ window.CodeMirror = (function() { d.sizer.style.minHeight = d.heightForcer.style.top = totalHeight + "px"; d.gutters.style.height = Math.max(totalHeight, d.scroller.clientHeight - scrollerCutOff) + "px"; var scrollHeight = Math.max(totalHeight, d.scroller.scrollHeight); - var needsH = d.scroller.scrollWidth > d.scroller.clientWidth; - var needsV = scrollHeight > d.scroller.clientHeight; + var needsH = d.scroller.scrollWidth > (d.scroller.clientWidth + 1); + var needsV = scrollHeight > (d.scroller.clientHeight + 1); if (needsV) { d.scrollbarV.style.display = "block"; d.scrollbarV.style.bottom = needsH ? scrollbarWidth(d.measure) + "px" : "0"; From cd6540cb3b6d6e5de55eab2c4b692cbe768d8e97 Mon Sep 17 00:00:00 2001 From: Marijn Haverbeke Date: Thu, 30 May 2013 12:36:11 +0200 Subject: [PATCH 1135/5780] Add handleMouseEvents option to markText and addLineWidget --- doc/manual.html | 11 +++++++++++ lib/codemirror.js | 17 ++++++++++------- 2 files changed, 21 insertions(+), 7 deletions(-) diff --git a/doc/manual.html b/doc/manual.html index 3bfc3d0e91..2b3874aaa5 100644 --- a/doc/manual.html +++ b/doc/manual.html @@ -1090,6 +1090,12 @@

    Text-marking methods

    Use a given node to display this range. Implies both collapsed and atomic. The given DOM node must be an inline element (as opposed to a block element).
    +
    handleMouseEvents: boolean
    +
    When replacedWith is given, this determines + whether the editor will capture mouse and drag events + occurring in this widget. Default is false—the events will be + left alone for the default browser handler, or specific + handlers on the widget, to capture.
    readOnly: boolean
    A read-only span can, as long as it is not cleared, not be modified except by @@ -1231,6 +1237,11 @@

    Widget, gutter, and decoration methods

    showIfHidden: boolean
    When true, will cause the widget to be rendered even if the line it is associated with is hidden.
    +
    handleMouseEvents: boolean
    +
    Determines whether the editor will capture mouse and + drag events occurring in this widget. Default is false—the + events will be left alone for the default browser handler, + or specific handlers on the widget, to capture.
    Note that the widget node will become a descendant of nodes with CodeMirror-specific CSS classes, and those classes might in some diff --git a/lib/codemirror.js b/lib/codemirror.js index 717ac6f4d5..9b6b2e1ecd 100644 --- a/lib/codemirror.js +++ b/lib/codemirror.js @@ -602,8 +602,9 @@ window.CodeMirror = (function() { if (nextIntact && nextIntact.to == lineN) nextIntact = intact.shift(); if (lineIsHidden(cm.doc, line)) { if (line.height != 0) updateLineHeight(line, 0); - if (line.widgets && cur.previousSibling) for (var i = 0; i < line.widgets.length; ++i) - if (line.widgets[i].showIfHidden) { + if (line.widgets && cur.previousSibling) for (var i = 0; i < line.widgets.length; ++i) { + var w = line.widgets[i]; + if (w.showIfHidden) { var prev = cur.previousSibling; if (/pre/i.test(prev.nodeName)) { var wrap = elt("div", null, null, "position: relative"); @@ -611,9 +612,11 @@ window.CodeMirror = (function() { wrap.appendChild(prev); prev = wrap; } - var wnode = prev.appendChild(elt("div", [line.widgets[i].node], "CodeMirror-linewidget")); - positionLineWidget(line.widgets[i], wnode, prev, dims); + var wnode = prev.appendChild(elt("div", [w.node], "CodeMirror-linewidget")); + if (!w.handleMouseEvents) wnode.ignoreEvents = true; + positionLineWidget(w, wnode, prev, dims); } + } } else if (nextIntact && nextIntact.from <= lineN && nextIntact.to > lineN) { // This line is intact. Skip to the actual node. Update its // line number if needed. @@ -709,6 +712,7 @@ window.CodeMirror = (function() { if (ie_lt8) wrap.style.zIndex = 2; if (line.widgets && wrap != reuse) for (var i = 0, ws = line.widgets; i < ws.length; ++i) { var widget = ws[i], node = elt("div", [widget.node], "CodeMirror-linewidget"); + if (!widget.handleMouseEvents) node.ignoreEvents = true; positionLineWidget(widget, node, wrap, dims); if (widget.above) wrap.insertBefore(node, cm.options.lineNumbers && line.height != 0 ? gutterWrap : lineElement); @@ -1584,9 +1588,7 @@ window.CodeMirror = (function() { function eventInWidget(display, e) { for (var n = e_target(e); n != display.wrapper; n = n.parentNode) { - if (!n) return true; - if (/\bCodeMirror-(?:line)?widget\b/.test(n.className) || - n.parentNode == display.sizer && n != display.mover) return true; + if (!n || n.ignoreEvents || n.parentNode == display.sizer && n != display.mover) return true; } } @@ -3664,6 +3666,7 @@ window.CodeMirror = (function() { if (marker.replacedWith) { marker.collapsed = true; marker.replacedWith = elt("span", [marker.replacedWith], "CodeMirror-widget"); + if (!options.handleMouseEvents) marker.replacedWith.ignoreEvents = true; } if (marker.collapsed) sawCollapsedSpans = true; From 41aeb8a512bf2fe7cf52e75465e1701b9124e753 Mon Sep 17 00:00:00 2001 From: Marijn Haverbeke Date: Thu, 30 May 2013 13:32:05 +0200 Subject: [PATCH 1136/5780] Take top padding into account in "local" coordinate space, fix bug in fromCoordSystem Issue #1557 --- lib/codemirror.js | 18 +++++++++--------- test/test.js | 17 ++++++++++------- 2 files changed, 19 insertions(+), 16 deletions(-) diff --git a/lib/codemirror.js b/lib/codemirror.js index 9b6b2e1ecd..6b452d699d 100644 --- a/lib/codemirror.js +++ b/lib/codemirror.js @@ -1109,7 +1109,8 @@ window.CodeMirror = (function() { if (context == "line") return rect; if (!context) context = "local"; var yOff = heightAtLine(cm, lineObj); - if (context != "local") yOff -= cm.display.viewOffset; + if (context == "local") yOff += paddingTop(cm.display); + else yOff -= cm.display.viewOffset; if (context == "page") { var lOff = getRect(cm.display.lineSpace); yOff += lOff.top + (window.pageYOffset || (document.documentElement || document.body).scrollTop); @@ -1125,19 +1126,18 @@ window.CodeMirror = (function() { function fromCoordSystem(cm, coords, context) { if (context == "div") return coords; var left = coords.left, top = coords.top; + // First move into "page" coordinate system if (context == "page") { left -= window.pageXOffset || (document.documentElement || document.body).scrollLeft; top -= window.pageYOffset || (document.documentElement || document.body).scrollTop; + } else if (context == "local" || !context) { + var localBox = getRect(cm.display.sizer); + left += localBox.left; + top += localBox.top; } + var lineSpaceBox = getRect(cm.display.lineSpace); - left -= lineSpaceBox.left; - top -= lineSpaceBox.top; - if (context == "local" || !context) { - var editorBox = getRect(cm.display.wrapper); - left += editorBox.left; - top += editorBox.top; - } - return {left: left, top: top}; + return {left: left - lineSpaceBox.left, top: top - lineSpaceBox.top}; } function charCoords(cm, pos, context, lineObj) { diff --git a/test/test.js b/test/test.js index d42a76bd41..ec309107bf 100644 --- a/test/test.js +++ b/test/test.js @@ -211,15 +211,18 @@ testCM("coords", function(cm) { testCM("coordsChar", function(cm) { addDoc(cm, 35, 70); - for (var ch = 0; ch <= 35; ch += 5) { - for (var line = 0; line < 70; line += 5) { - cm.setCursor(line, ch); - var coords = cm.charCoords(Pos(line, ch)); - var pos = cm.coordsChar({left: coords.left, top: coords.top + 5}); - eqPos(pos, Pos(line, ch)); + for (var i = 0; i < 2; ++i) { + var sys = i ? "local" : "page"; + for (var ch = 0; ch <= 35; ch += 5) { + for (var line = 0; line < 70; line += 5) { + cm.setCursor(line, ch); + var coords = cm.charCoords(Pos(line, ch), sys); + var pos = cm.coordsChar({left: coords.left + 1, top: coords.top + 1}, sys); + eqPos(pos, Pos(line, ch)); + } } } -}); +}, {lineNumbers: true}); testCM("posFromIndex", function(cm) { cm.setValue( From b800af364642d1e053cfb3e6f2c196bdd1bc1cd0 Mon Sep 17 00:00:00 2001 From: Marijn Haverbeke Date: Thu, 30 May 2013 13:39:07 +0200 Subject: [PATCH 1137/5780] Add a lineAtHeight method Issue #1556 --- doc/manual.html | 7 ++++++- lib/codemirror.js | 5 +++++ 2 files changed, 11 insertions(+), 1 deletion(-) diff --git a/doc/manual.html b/doc/manual.html index 2b3874aaa5..2d78dbe454 100644 --- a/doc/manual.html +++ b/doc/manual.html @@ -1299,7 +1299,7 @@

    Sizing, scrolling and positioning methods

    end (false) of the selection, or, if a {line, ch} object is given, it specifies the precise position at which you want to measure. -
    cm.charCoords(pos: {line, ch}, mode: string) → {left, right, top, bottom}
    +
    cm.charCoords(pos: {line, ch}, ?mode: string) → {left, right, top, bottom}
    Returns the position and dimensions of an arbitrary character. pos should be a {line, ch} object. This differs from cursorCoords in that @@ -1313,6 +1313,11 @@

    Sizing, scrolling and positioning methods

    the coordinates are interpreted. It may be "window", "page" (the default), or "local".
    +
    cm.lineAtHeight(height: number, ?mode: string) → number
    +
    Computes the line at the given pixel + height. mode can be one of the same strings + that coordsChar + accepts.
    cm.defaultTextHeight() → number
    Returns the line height of the default font for the editor.
    cm.defaultCharWidth() → number
    diff --git a/lib/codemirror.js b/lib/codemirror.js index 6b452d699d..191804ccc0 100644 --- a/lib/codemirror.js +++ b/lib/codemirror.js @@ -2851,6 +2851,11 @@ window.CodeMirror = (function() { return coordsChar(this, coords.left, coords.top); }, + lineAtHeight: function(height, mode) { + height = fromCoordSystem(this, {top: height, left: 0}, mode || "page").top; + return lineAtHeight(this.doc, height + this.display.viewOffset); + }, + defaultTextHeight: function() { return textHeight(this.display); }, defaultCharWidth: function() { return charWidth(this.display); }, From fb6df7fb3bb796eb26185e9dda894b0b868f92aa Mon Sep 17 00:00:00 2001 From: Marijn Haverbeke Date: Thu, 30 May 2013 13:47:45 +0200 Subject: [PATCH 1138/5780] [vim keymap] Fix reliance on bug in coordsChar --- keymap/vim.js | 8 ++++---- 1 file changed, 4 insertions(+), 4 deletions(-) diff --git a/keymap/vim.js b/keymap/vim.js index 51b93aec30..d29f39cac4 100644 --- a/keymap/vim.js +++ b/keymap/vim.js @@ -2811,10 +2811,10 @@ } function getUserVisibleLines(cm) { var scrollInfo = cm.getScrollInfo(); - var occludeTorleranceTop = 6; - var occludeTorleranceBottom = 10; - var from = cm.coordsChar({left:0, top: occludeTorleranceTop}, 'local'); - var bottomY = scrollInfo.clientHeight - occludeTorleranceBottom; + var occludeToleranceTop = 6; + var occludeToleranceBottom = 10; + var from = cm.coordsChar({left:0, top: occludeToleranceTop + scrollInfo.top}, 'local'); + var bottomY = scrollInfo.clientHeight - occludeToleranceBottom + scrollInfo.top; var to = cm.coordsChar({left:0, top: bottomY}, 'local'); return {top: from.line, bottom: to.line}; } From a8088496ad582499fe7fb9a22b1f6ae66f6b88bd Mon Sep 17 00:00:00 2001 From: Marijn Haverbeke Date: Thu, 30 May 2013 14:09:01 +0200 Subject: [PATCH 1139/5780] [closebrackets addon] Add 'explode' feature Issue #1552 --- addon/edit/closebrackets.js | 40 ++++++++++++++++++++++++++++++------- doc/manual.html | 12 +++++++---- 2 files changed, 41 insertions(+), 11 deletions(-) diff --git a/addon/edit/closebrackets.js b/addon/edit/closebrackets.js index 2abc8c5fe6..32819516fb 100644 --- a/addon/edit/closebrackets.js +++ b/addon/edit/closebrackets.js @@ -1,23 +1,36 @@ (function() { var DEFAULT_BRACKETS = "()[]{}''\"\""; + var DEFAULT_EXPLODE_ON_ENTER = "[]{}"; var SPACE_CHAR_REGEX = /\s/; CodeMirror.defineOption("autoCloseBrackets", false, function(cm, val, old) { - var wasOn = old && old != CodeMirror.Init; - if (val && !wasOn) - cm.addKeyMap(buildKeymap(typeof val == "string" ? val : DEFAULT_BRACKETS)); - else if (!val && wasOn) + if (old != CodeMirror.Init && old) cm.removeKeyMap("autoCloseBrackets"); + if (!val) return; + var pairs = DEFAULT_BRACKETS, explode = DEFAULT_EXPLODE_ON_ENTER; + if (typeof val == "string") pairs = val; + else if (typeof val == "object") { + if (val.pairs != null) pairs = val.pairs; + if (val.explode != null) explode = val.explode; + } + var map = buildKeymap(pairs); + if (explode) map.Enter = buildExplodeHandler(explode); + cm.addKeyMap(map); }); + function charsAround(cm, pos) { + var str = cm.getRange(CodeMirror.Pos(pos.line, pos.ch - 1), + CodeMirror.Pos(pos.line, pos.ch + 1)); + return str.length == 2 ? str : null; + } + function buildKeymap(pairs) { var map = { name : "autoCloseBrackets", Backspace: function(cm) { if (cm.somethingSelected()) return CodeMirror.Pass; - var cur = cm.getCursor(), line = cm.getLine(cur.line); - if (cur.ch && cur.ch < line.length && - pairs.indexOf(line.slice(cur.ch - 1, cur.ch + 1)) % 2 == 0) + var cur = cm.getCursor(), around = charsAround(cm, cur); + if (around && pairs.indexOf(line.slice(cur.ch - 1, cur.ch + 1)) % 2 == 0) cm.replaceRange("", CodeMirror.Pos(cur.line, cur.ch - 1), CodeMirror.Pos(cur.line, cur.ch + 1)); else return CodeMirror.Pass; @@ -51,4 +64,17 @@ })(pairs.charAt(i), pairs.charAt(i + 1)); return map; } + + function buildExplodeHandler(pairs) { + return function(cm) { + var cur = cm.getCursor(), around = charsAround(cm, cur); + if (!around || pairs.indexOf(around) % 2 != 0) return CodeMirror.Pass; + cm.operation(function() { + var newPos = CodeMirror.Pos(cur.line + 1, 0); + cm.replaceSelection("\n\n", {anchor: newPos, head: newPos}, "+input"); + cm.indentLine(cur.line + 1, null, true); + cm.indentLine(cur.line + 2, null, true); + }); + }; + } })(); diff --git a/doc/manual.html b/doc/manual.html index 2d78dbe454..cc9a1d8b86 100644 --- a/doc/manual.html +++ b/doc/manual.html @@ -1574,10 +1574,14 @@

    Add-ons

    edit/closebrackets.js
    Defines an option autoCloseBrackets that will auto-close brackets and quotes when typed. By default, it'll - auto-close ()[]{}''"", but you can pass it a - string similar to that (containing pairs of matching characters) - to customize it. Demo - here.
    + auto-close ()[]{}''"", but you can pass it a string + similar to that (containing pairs of matching characters), or an + object with pairs and + optionally explode properties to customize + it. explode should be a similar string that gives + the pairs of characters that, when enter is pressed between + them, should have the second character also moved to its own + line. Demo here.
    edit/trailingspace.js
    Adds an option showTrailingSpace which, when From a092d987583dbc677deae277707037ef23c1476f Mon Sep 17 00:00:00 2001 From: Marijn Haverbeke Date: Thu, 30 May 2013 14:35:21 +0200 Subject: [PATCH 1140/5780] [show-hint addon] More complete documentation --- doc/manual.html | 69 ++++++++++++++++++++++++++++++++++++++++++++----- 1 file changed, 62 insertions(+), 7 deletions(-) diff --git a/doc/manual.html b/doc/manual.html index cc9a1d8b86..ffa7c6f09c 100644 --- a/doc/manual.html +++ b/doc/manual.html @@ -1695,14 +1695,69 @@

    Add-ons

    hint/show-hint.js
    Provides a framework for showing autocompletion hints. Defines CodeMirror.showHint, which takes a - CodeMirror instance and a hinting function, and pops up a widget - that allows the user to select a completion. Hinting functions - are function that take an editor instance, and return + CodeMirror instance, a hinting function, and optionally an + options object, and pops up a widget that allows the user to + select a completion. Hinting functions are function that take an + editor instance and an optional options object, and return a {list, from, to} object, where list - is an array of strings (the completions), and from - and to give the start and end of the token that is - being completed. Depends - on addon/hint/show-hint.css. Check + is an array of strings or objects (the completions), + and from and to give the start and end + of the token that is being completed. When completions aren't + simple strings, they should be objects with the folowing + properties: +
    +
    text: string
    +
    The completion text. This is the only required + property.
    +
    displayText: string
    +
    The text that should be displayed in the menu.
    +
    className: string
    +
    A CSS class name to apply to the completion's line in the + menu.
    +
    render: fn(Element, self, data)
    +
    A method used to create the DOM structure for showing the + completion by appending it to its first argument.
    +
    hint: fn(CodeMirror, self, data)
    +
    A method used to actually apply the completion, instead of + the default behavior.
    +
    + The plugin understands the following options (the options object + will also be passed along to the hinting function, which may + understand additional options): +
    +
    async: boolean
    +
    When set to true, the hinting function's signature should + be (cm, callback, ?options), and the completion + interface will only be popped up when the hinting function + calls the callback, passing it the object holding the + completions.
    +
    completeSingle: boolean
    +
    Determines whether, when only a single completion is + available, it is completed without showing the dialog. + Defaults to true.
    +
    alignWithWord: boolean
    +
    Whether the pop-up should be horizontally aligned with the + start of the word (true, default), or with the cursor (false).
    +
    customKeys: keymap
    +
    Allows you to provide a custom keymap of keys to be active + when the pop-up is active. To bind a key to a behavior that is + already present in the default keymap, bind it to the name of + the key in the default keymap (for example "Up").
    +
    + The following events will be fired on the completions object + during completion: +
    +
    "shown" ()
    +
    Fired when the pop-up is shown.
    +
    "select" (completion, Element)
    +
    Fired when a completion is selected. Passed the completion + value (string or object) and the DOM node that represents it + in the menu.
    +
    "close" ()
    +
    Fired when the completion is finished.
    +
    + This addon depends styles + from addon/hint/show-hint.css. Check out the demo for an example.
    From 2e62847db235b3e63a7d75026fb2d6e25e5f798b Mon Sep 17 00:00:00 2001 From: Marijn Haverbeke Date: Thu, 30 May 2013 14:47:55 +0200 Subject: [PATCH 1141/5780] [show-hint addon] Make sure to clear state.completionActive when no match is found --- addon/hint/show-hint.js | 13 +++++++++---- 1 file changed, 9 insertions(+), 4 deletions(-) diff --git a/addon/hint/show-hint.js b/addon/hint/show-hint.js index 504fda786b..a0661e4622 100644 --- a/addon/hint/show-hint.js +++ b/addon/hint/show-hint.js @@ -24,12 +24,17 @@ CodeMirror.showHint = function(cm, getHints, options) { } function showHints(data) { - if (!data || !data.list.length) return; + if (!data || !data.list.length) { + if (continued) { + cm.state.completionActive = false; + CodeMirror.signal(data, "close"); + } + return; + } + var completions = data.list; - // When there is only one completion, use it directly. - if (!continued && options.completeSingle !== false && completions.length == 1) { + if (!continued && options.completeSingle != false && completions.length == 1) { pickCompletion(cm, data, completions[0]); - CodeMirror.signal(data, "close"); return true; } From 8955ce4817a754674594144b64aa1bac59c227d8 Mon Sep 17 00:00:00 2001 From: Marijn Haverbeke Date: Thu, 30 May 2013 15:16:50 +0200 Subject: [PATCH 1142/5780] Add support for "window" coordinate system to charCoords/cursorCoords --- lib/codemirror.js | 13 ++++++++----- 1 file changed, 8 insertions(+), 5 deletions(-) diff --git a/lib/codemirror.js b/lib/codemirror.js index 191804ccc0..a5fc2f2ed3 100644 --- a/lib/codemirror.js +++ b/lib/codemirror.js @@ -1100,6 +1100,9 @@ window.CodeMirror = (function() { cm.display.lineNumChars = null; } + function pageScrollX() { return window.pageXOffset || (document.documentElement || document.body).scrollLeft; } + function pageScrollY() { return window.pageYOffset || (document.documentElement || document.body).scrollTop; } + // Context is one of "line", "div" (display.lineDiv), "local"/null (editor), or "page" function intoCoordSystem(cm, lineObj, rect, context) { if (lineObj.widgets) for (var i = 0; i < lineObj.widgets.length; ++i) if (lineObj.widgets[i].above) { @@ -1111,10 +1114,10 @@ window.CodeMirror = (function() { var yOff = heightAtLine(cm, lineObj); if (context == "local") yOff += paddingTop(cm.display); else yOff -= cm.display.viewOffset; - if (context == "page") { + if (context == "page" || context == "window") { var lOff = getRect(cm.display.lineSpace); - yOff += lOff.top + (window.pageYOffset || (document.documentElement || document.body).scrollTop); - var xOff = lOff.left + (window.pageXOffset || (document.documentElement || document.body).scrollLeft); + yOff += lOff.top + (context == "window" ? 0 : pageScrollY()); + var xOff = lOff.left + (context == "window" ? 0 : pageScrollX()); rect.left += xOff; rect.right += xOff; } rect.top += yOff; rect.bottom += yOff; @@ -1128,8 +1131,8 @@ window.CodeMirror = (function() { var left = coords.left, top = coords.top; // First move into "page" coordinate system if (context == "page") { - left -= window.pageXOffset || (document.documentElement || document.body).scrollLeft; - top -= window.pageYOffset || (document.documentElement || document.body).scrollTop; + left -= pageScrollX(); + top -= pageScrollY(); } else if (context == "local" || !context) { var localBox = getRect(cm.display.sizer); left += localBox.left; From 14fc698b95f4229bf4609e84caf2194c9c2c214b Mon Sep 17 00:00:00 2001 From: Marijn Haverbeke Date: Thu, 30 May 2013 15:25:58 +0200 Subject: [PATCH 1143/5780] Remove explicit compensation for paddingTop in scrolling code Local coords now include paddingTop. Issue #1557 --- lib/codemirror.js | 13 ++++++------- test/test.js | 18 ++++++++++++++++++ 2 files changed, 24 insertions(+), 7 deletions(-) diff --git a/lib/codemirror.js b/lib/codemirror.js index a5fc2f2ed3..f56c6880e4 100644 --- a/lib/codemirror.js +++ b/lib/codemirror.js @@ -2537,9 +2537,9 @@ window.CodeMirror = (function() { function scrollCursorIntoView(cm) { var coords = scrollPosIntoView(cm, cm.doc.sel.head, cm.options.cursorScrollMargin); if (!cm.state.focused) return; - var display = cm.display, box = getRect(display.sizer), doScroll = null, pTop = paddingTop(cm.display); - if (coords.top + pTop + box.top < 0) doScroll = true; - else if (coords.bottom + pTop + box.top > (window.innerHeight || document.documentElement.clientHeight)) doScroll = false; + var display = cm.display, box = getRect(display.sizer), doScroll = null; + if (coords.top + box.top < 0) doScroll = true; + else if (coords.bottom + box.top > (window.innerHeight || document.documentElement.clientHeight)) doScroll = false; if (doScroll != null && !phantom) { var hidden = display.cursor.style.display == "none"; if (hidden) { @@ -2577,12 +2577,11 @@ window.CodeMirror = (function() { } function calculateScrollPos(cm, x1, y1, x2, y2) { - var display = cm.display, pt = paddingTop(display); - y1 += pt; y2 += pt; + var display = cm.display, snapMargin = textHeight(cm.display); if (y1 < 0) y1 = 0; var screen = display.scroller.clientHeight - scrollerCutOff, screentop = display.scroller.scrollTop, result = {}; var docBottom = cm.doc.height + paddingVert(display); - var atTop = y1 < pt + 10, atBottom = y2 + pt > docBottom - 10; + var atTop = y1 < snapMargin, atBottom = y2 > docBottom - snapMargin; if (y1 < screentop) { result.scrollTop = atTop ? 0 : y1; } else if (y2 > screentop + screen) { @@ -2950,7 +2949,7 @@ window.CodeMirror = (function() { if (left + node.offsetWidth > hspace) left = hspace - node.offsetWidth; } - node.style.top = (top + paddingTop(display)) + "px"; + node.style.top = top + "px"; node.style.left = node.style.right = ""; if (horiz == "right") { left = display.sizer.clientWidth - node.offsetWidth; diff --git a/test/test.js b/test/test.js index ec309107bf..07e2d67130 100644 --- a/test/test.js +++ b/test/test.js @@ -516,6 +516,24 @@ testCM("scrollSnap", function(cm) { is(info.left == 0 && info.top + 2 > info.height - cm.getScrollerElement().clientHeight, "scrolled clean to bottom"); }); +testCM("scrollIntoView", function(cm) { + var outer = cm.getWrapperElement().getBoundingClientRect(); + function test(line, ch) { + var pos = Pos(line, ch); + cm.scrollIntoView(pos); + var box = cm.charCoords(pos, "window"); + is(box.left >= outer.left && box.right <= outer.right && + box.top >= outer.top && box.bottom <= outer.bottom); + } + addDoc(cm, 200, 200); + test(199, 199); + test(0, 0); + test(100, 100); + test(199, 0); + test(0, 199); + test(100, 100); +}); + testCM("selectionPos", function(cm) { if (phantom) return; cm.setSize(100, 100); From 97b3110549e3e68a5519cff294929d149bdbff2c Mon Sep 17 00:00:00 2001 From: Marijn Haverbeke Date: Thu, 30 May 2013 15:36:30 +0200 Subject: [PATCH 1144/5780] Another test that fails mysteriously on Travis' Phantom (but not on my local one, or in a real browser) --- test/test.js | 1 + 1 file changed, 1 insertion(+) diff --git a/test/test.js b/test/test.js index 07e2d67130..cefe14cc90 100644 --- a/test/test.js +++ b/test/test.js @@ -517,6 +517,7 @@ testCM("scrollSnap", function(cm) { }); testCM("scrollIntoView", function(cm) { + if (phantom) return; var outer = cm.getWrapperElement().getBoundingClientRect(); function test(line, ch) { var pos = Pos(line, ch); From c2f527ecea98b99eb5d21fbf666e672412a0a115 Mon Sep 17 00:00:00 2001 From: Mike Ivanov Date: Thu, 30 May 2013 20:43:09 -0300 Subject: [PATCH 1145/5780] Fix a non-word --- doc/manual.html | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/doc/manual.html b/doc/manual.html index ffa7c6f09c..b36053a4fd 100644 --- a/doc/manual.html +++ b/doc/manual.html @@ -757,14 +757,14 @@

    Constructor

    Content manipulation methods

    -
    doc.getValue(?seperator: string) → string
    +
    doc.getValue(?separator: string) → string
    Get the current editor content. You can pass it an optional argument to specify the string to be used to separate lines (defaults to "\n").
    doc.setValue(content: string)
    Set the editor content.
    -
    doc.getRange(from: {line, ch}, to: {line, ch}, ?seperator: string) → string
    +
    doc.getRange(from: {line, ch}, to: {line, ch}, ?separator: string) → string
    Get the text between the given points in the editor, which should be {line, ch} objects. An optional third argument can be given to indicate the line separator string to From 83709e32d44941bfef4e3f7bce02b68bde9ef663 Mon Sep 17 00:00:00 2001 From: Marijn Haverbeke Date: Fri, 31 May 2013 10:07:38 +0200 Subject: [PATCH 1146/5780] Fix bug in measuring of lines with only collapsed elements Closes #1559 --- lib/codemirror.js | 11 ++++------- 1 file changed, 4 insertions(+), 7 deletions(-) diff --git a/lib/codemirror.js b/lib/codemirror.js index f56c6880e4..ab09ad7f2c 100644 --- a/lib/codemirror.js +++ b/lib/codemirror.js @@ -4153,9 +4153,8 @@ window.CodeMirror = (function() { } function lineContent(cm, realLine, measure) { - var merged, line = realLine, lineBefore, sawBefore, simple = true; + var merged, line = realLine, lineBefore, sawBefore, empty = true; while (merged = collapsedSpanAtStart(line)) { - simple = false; line = getLine(cm.doc, merged.find().from.line); if (!lineBefore) lineBefore = line; } @@ -4165,6 +4164,7 @@ window.CodeMirror = (function() { if (line.textClass) builder.pre.className = line.textClass; do { + if (line.text) empty = false; builder.measure = line == realLine && measure; builder.pos = 0; builder.addToken = builder.measure ? buildTokenMeasure : buildToken; @@ -4176,14 +4176,11 @@ window.CodeMirror = (function() { } var next = insertLineContent(line, builder, getLineStyles(cm, line)); sawBefore = line == lineBefore; - if (next) { - line = getLine(cm.doc, next.to.line); - simple = false; - } + if (next) line = getLine(cm.doc, next.to.line); } while (next); if (measure && !builder.addedOne) - measure[0] = builder.pre.appendChild(simple ? elt("span", "\u00a0") : zeroWidthElement(cm.display.measure)); + measure[0] = builder.pre.appendChild(empty ? elt("span", "\u00a0") : zeroWidthElement(cm.display.measure)); if (!builder.pre.firstChild && !lineIsHidden(cm.doc, realLine)) builder.pre.appendChild(document.createTextNode("\u00a0")); From f338135f2f35dd18d3f6e948edcaeae393150035 Mon Sep 17 00:00:00 2001 From: Marijn Haverbeke Date: Fri, 31 May 2013 10:50:39 +0200 Subject: [PATCH 1147/5780] Support a disableInput property on keymaps Stop suppressing key events for nofallthrough keymaps. Issue #1558 --- keymap/emacs.js | 2 +- keymap/vim.js | 1 + lib/codemirror.js | 21 +++++++++++++-------- 3 files changed, 15 insertions(+), 9 deletions(-) diff --git a/keymap/emacs.js b/keymap/emacs.js index fab3ab9fe6..39bea17dcf 100644 --- a/keymap/emacs.js +++ b/keymap/emacs.js @@ -25,6 +25,6 @@ CodeMirror.keyMap["emacs-Ctrl-X"] = { "Ctrl-S": "save", "Ctrl-W": "save", "S": "saveAll", "F": "open", "U": "undo", "K": "close", - auto: "emacs", nofallthrough: true + auto: "emacs", nofallthrough: true, disableInput: true }; })(); diff --git a/keymap/vim.js b/keymap/vim.js index d29f39cac4..bd1c16c292 100644 --- a/keymap/vim.js +++ b/keymap/vim.js @@ -3208,6 +3208,7 @@ var modifiers = ['Shift', 'Ctrl']; var cmToVimKeymap = { 'nofallthrough': true, + 'disableInput': true, 'style': 'fat-cursor' }; function bindKeys(keys, modifier) { diff --git a/lib/codemirror.js b/lib/codemirror.js index ab09ad7f2c..9a8bd37379 100644 --- a/lib/codemirror.js +++ b/lib/codemirror.js @@ -236,9 +236,11 @@ window.CodeMirror = (function() { } function keyMapChanged(cm) { - var style = keyMap[cm.options.keyMap].style; + var map = keyMap[cm.options.keyMap], style = map.style; cm.display.wrapper.className = cm.display.wrapper.className.replace(/\s*cm-keymap-\S+/g, "") + (style ? " cm-keymap-" + style : ""); + if (map.disableInput) cm.doc.cantEdit |= cantEdit_keymap; + else cm.doc.cantEdit &= ~cantEdit_keymap; } function themeChanged(cm) { @@ -2003,7 +2005,6 @@ window.CodeMirror = (function() { } else { handled = lookupKey(name, keymaps, function(b) { return doHandleBinding(cm, b); }); } - if (handled == "stop") handled = false; if (handled) { e_preventDefault(e); @@ -2485,7 +2486,7 @@ window.CodeMirror = (function() { function skipAtomic(doc, pos, bias, mayClear) { var flipped = false, curPos = pos; var dir = bias || 1; - doc.cantEdit = false; + doc.cantEdit &= ~cantEdit_inatomic; search: for (;;) { var line = getLine(doc, curPos.line); if (line.markedSpans) { @@ -2517,7 +2518,7 @@ window.CodeMirror = (function() { // -- try again *with* clearing, if we didn't already if (!mayClear) return skipAtomic(doc, pos, bias, true); // Otherwise, turn off editing until further notice, and return the start of the doc - doc.cantEdit = true; + doc.cantEdit |= cantEdit_inatomic; return Pos(doc.first, 0); } flipped = true; newPos = pos; dir = -dir; @@ -3428,7 +3429,7 @@ window.CodeMirror = (function() { for (var i = 0; i < maps.length; ++i) { var done = lookup(maps[i]); - if (done) return done; + if (done) return done != "stop"; } } function isModifierKey(event) { @@ -3610,8 +3611,8 @@ window.CodeMirror = (function() { if (min != null && cm) regChange(cm, min, max + 1); this.lines.length = 0; this.explicitlyCleared = true; - if (this.collapsed && this.doc.cantEdit) { - this.doc.cantEdit = false; + if (this.atomic && (this.doc.cantEdit & cantEdit_inatomic)) { + this.doc.cantEdit &= ~cantEdit_inatomic; if (cm) reCheckSelection(cm); } if (withOp) endOperation(cm); @@ -4527,6 +4528,8 @@ window.CodeMirror = (function() { } }; + var cantEdit_inatomic = 1, cantEdit_keymap = 2; + var nextDocId = 0; var Doc = CodeMirror.Doc = function(text, mode, firstLine) { if (!(this instanceof Doc)) return new Doc(text, mode, firstLine); @@ -4535,7 +4538,7 @@ window.CodeMirror = (function() { BranchChunk.call(this, [new LeafChunk([makeLine("", null)])]); this.first = firstLine; this.scrollTop = this.scrollLeft = 0; - this.cantEdit = false; + this.cantEdit = 0; this.history = makeHistory(); this.frontier = firstLine; var start = Pos(firstLine, 0); @@ -4783,6 +4786,8 @@ window.CodeMirror = (function() { loadMode(cm); if (!cm.options.lineWrapping) computeMaxLength(cm); cm.options.mode = doc.modeOption; + if (keyMap[cm.options.keyMap].disableInput) doc.cantEdit |= cantEdit_keymap; + else doc.cantEdit &= ~cantEdit_keymap; regChange(cm); } From 63a49817a4522ada322ea81fd8b341d4dd7ee598 Mon Sep 17 00:00:00 2001 From: Yunchi Luo Date: Sat, 1 Jun 2013 00:54:26 -0400 Subject: [PATCH 1148/5780] Change disableInput behavior to allow programmatic edits. --- lib/codemirror.js | 20 ++++++++------------ 1 file changed, 8 insertions(+), 12 deletions(-) diff --git a/lib/codemirror.js b/lib/codemirror.js index 9a8bd37379..5f361e4b3e 100644 --- a/lib/codemirror.js +++ b/lib/codemirror.js @@ -239,8 +239,7 @@ window.CodeMirror = (function() { var map = keyMap[cm.options.keyMap], style = map.style; cm.display.wrapper.className = cm.display.wrapper.className.replace(/\s*cm-keymap-\S+/g, "") + (style ? " cm-keymap-" + style : ""); - if (map.disableInput) cm.doc.cantEdit |= cantEdit_keymap; - else cm.doc.cantEdit &= ~cantEdit_keymap; + cm.doc.disableInput = !!map.disableInput; } function themeChanged(cm) { @@ -1423,7 +1422,7 @@ window.CodeMirror = (function() { if (!cm.state.focused || hasSelection(input) || isReadOnly(cm)) return false; var text = input.value; if (text == prevInput && posEq(sel.from, sel.to)) return false; - if (ie && !ie_lt9 && cm.display.inputHasSelection === text) { + if (cm.doc.disableInput || ie && !ie_lt9 && cm.display.inputHasSelection === text) { resetInput(cm, true); return false; } @@ -2486,7 +2485,7 @@ window.CodeMirror = (function() { function skipAtomic(doc, pos, bias, mayClear) { var flipped = false, curPos = pos; var dir = bias || 1; - doc.cantEdit &= ~cantEdit_inatomic; + doc.cantEdit = false; search: for (;;) { var line = getLine(doc, curPos.line); if (line.markedSpans) { @@ -2518,7 +2517,7 @@ window.CodeMirror = (function() { // -- try again *with* clearing, if we didn't already if (!mayClear) return skipAtomic(doc, pos, bias, true); // Otherwise, turn off editing until further notice, and return the start of the doc - doc.cantEdit |= cantEdit_inatomic; + doc.cantEdit = true; return Pos(doc.first, 0); } flipped = true; newPos = pos; dir = -dir; @@ -3611,8 +3610,8 @@ window.CodeMirror = (function() { if (min != null && cm) regChange(cm, min, max + 1); this.lines.length = 0; this.explicitlyCleared = true; - if (this.atomic && (this.doc.cantEdit & cantEdit_inatomic)) { - this.doc.cantEdit &= ~cantEdit_inatomic; + if (this.atomic && this.doc.cantEdit) { + this.doc.cantEdit = false; if (cm) reCheckSelection(cm); } if (withOp) endOperation(cm); @@ -4528,8 +4527,6 @@ window.CodeMirror = (function() { } }; - var cantEdit_inatomic = 1, cantEdit_keymap = 2; - var nextDocId = 0; var Doc = CodeMirror.Doc = function(text, mode, firstLine) { if (!(this instanceof Doc)) return new Doc(text, mode, firstLine); @@ -4538,7 +4535,7 @@ window.CodeMirror = (function() { BranchChunk.call(this, [new LeafChunk([makeLine("", null)])]); this.first = firstLine; this.scrollTop = this.scrollLeft = 0; - this.cantEdit = 0; + this.cantEdit = false; this.history = makeHistory(); this.frontier = firstLine; var start = Pos(firstLine, 0); @@ -4786,8 +4783,7 @@ window.CodeMirror = (function() { loadMode(cm); if (!cm.options.lineWrapping) computeMaxLength(cm); cm.options.mode = doc.modeOption; - if (keyMap[cm.options.keyMap].disableInput) doc.cantEdit |= cantEdit_keymap; - else doc.cantEdit &= ~cantEdit_keymap; + doc.disableInput = !!keyMap[cm.options.keyMap].disableInput; regChange(cm); } From c086444347fae9c4bca0d2b98c19b7499539fd57 Mon Sep 17 00:00:00 2001 From: Marijn Haverbeke Date: Sun, 2 Jun 2013 17:42:59 +0200 Subject: [PATCH 1149/5780] Fixup 63a49817a4522ada322ea81fd8b341d4dd7ee598 Issue #1565 --- lib/codemirror.js | 7 +++---- 1 file changed, 3 insertions(+), 4 deletions(-) diff --git a/lib/codemirror.js b/lib/codemirror.js index 5f361e4b3e..d378040336 100644 --- a/lib/codemirror.js +++ b/lib/codemirror.js @@ -239,7 +239,7 @@ window.CodeMirror = (function() { var map = keyMap[cm.options.keyMap], style = map.style; cm.display.wrapper.className = cm.display.wrapper.className.replace(/\s*cm-keymap-\S+/g, "") + (style ? " cm-keymap-" + style : ""); - cm.doc.disableInput = !!map.disableInput; + cm.state.disableInput = map.disableInput; } function themeChanged(cm) { @@ -1419,10 +1419,10 @@ window.CodeMirror = (function() { // supported or compatible enough yet to rely on.) function readInput(cm) { var input = cm.display.input, prevInput = cm.display.prevInput, doc = cm.doc, sel = doc.sel; - if (!cm.state.focused || hasSelection(input) || isReadOnly(cm)) return false; + if (!cm.state.focused || hasSelection(input) || isReadOnly(cm) || cm.state.disableInput) return false; var text = input.value; if (text == prevInput && posEq(sel.from, sel.to)) return false; - if (cm.doc.disableInput || ie && !ie_lt9 && cm.display.inputHasSelection === text) { + if (ie && !ie_lt9 && cm.display.inputHasSelection === text) { resetInput(cm, true); return false; } @@ -4783,7 +4783,6 @@ window.CodeMirror = (function() { loadMode(cm); if (!cm.options.lineWrapping) computeMaxLength(cm); cm.options.mode = doc.modeOption; - doc.disableInput = !!keyMap[cm.options.keyMap].disableInput; regChange(cm); } From 88efc61e32fefde35b845510c6445b9bb1be7bb8 Mon Sep 17 00:00:00 2001 From: Marijn Haverbeke Date: Sun, 2 Jun 2013 18:02:37 +0200 Subject: [PATCH 1150/5780] [closebrackets addon] Fix reference to removed variable Issue #1552 --- addon/edit/closebrackets.js | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/addon/edit/closebrackets.js b/addon/edit/closebrackets.js index 32819516fb..6fbf38ae67 100644 --- a/addon/edit/closebrackets.js +++ b/addon/edit/closebrackets.js @@ -30,7 +30,7 @@ Backspace: function(cm) { if (cm.somethingSelected()) return CodeMirror.Pass; var cur = cm.getCursor(), around = charsAround(cm, cur); - if (around && pairs.indexOf(line.slice(cur.ch - 1, cur.ch + 1)) % 2 == 0) + if (around && pairs.indexOf(around) % 2 == 0) cm.replaceRange("", CodeMirror.Pos(cur.line, cur.ch - 1), CodeMirror.Pos(cur.line, cur.ch + 1)); else return CodeMirror.Pass; From 5e9bcf71dfe0d449cb045aa6c51404620e5a49e5 Mon Sep 17 00:00:00 2001 From: Yunchi Luo Date: Sat, 1 Jun 2013 10:15:02 -0400 Subject: [PATCH 1151/5780] [vim keymap] Add support for :sort --- keymap/vim.js | 92 ++++++++++++++++++++++++++++++++++++++++++++---- test/vim_test.js | 55 ++++++++++++++++++++++++++++- 2 files changed, 139 insertions(+), 8 deletions(-) diff --git a/keymap/vim.js b/keymap/vim.js index bd1c16c292..df40bfc02e 100644 --- a/keymap/vim.js +++ b/keymap/vim.js @@ -967,7 +967,9 @@ }, processEx: function(cm, vim, command) { function onPromptClose(input) { - exCommandDispatcher.processCommand(cm, input); + // Give the prompt some time to close so that if processCommand shows + // an error, the elements don't overlap. + window.setTimeout(10, exCommandDispatcher.processCommand(cm, input)); } function onPromptKeyDown(e, input, close) { var keyName = CodeMirror.keyName(e); @@ -1189,7 +1191,7 @@ return null; }, jumpToMark: function(cm, motionArgs, vim) { - var best = cm.getCursor(); + var best = cm.getCursor(); for (var i = 0; i < motionArgs.repeat; i++) { var cursor = best; for (var key in vim.marks) { @@ -2150,7 +2152,7 @@ } }, // TODO: The original Vim implementation only operates on level 1 and 2. - // The current implementation doesn't check for code block level and + // The current implementation doesn't check for code block level and // therefore it operates on any levels. method: { init: function(state) { @@ -2202,7 +2204,7 @@ reverseSymb: (forward ? { ')': '(', '}': '{' } : { '(': ')', '{': '}' })[symb], forward: forward, depth: 0, - curMoveThrough: false + curMoveThrough: false }; var mode = symbolToMode[symb]; if(!mode)return cur; @@ -2828,6 +2830,7 @@ { name: 'write', shortName: 'w', type: 'builtIn' }, { name: 'undo', shortName: 'u', type: 'builtIn' }, { name: 'redo', shortName: 'red', type: 'builtIn' }, + { name: 'sort', shortName: 'sor', type: 'builtIn'}, { name: 'substitute', shortName: 's', type: 'builtIn'}, { name: 'nohlsearch', shortName: 'noh', type: 'builtIn'}, { name: 'delmarks', shortName: 'delm', type: 'builtin'} @@ -2874,7 +2877,11 @@ showConfirm(cm, 'Not an editor command ":' + input + '"'); return; } - exCommands[commandName](cm, params); + try { + exCommands[commandName](cm, params); + } catch(e) { + showConfirm(cm, e); + } }, parseInput_: function(cm, inputStream, result) { inputStream.eatWhile(':'); @@ -2919,7 +2926,7 @@ break; default: inputStream.backUp(1); - return cm.getCursor().line; + return undefined; } }, parseCommandArgs_: function(inputStream, params, command) { @@ -3029,6 +3036,77 @@ linewise: true }, repeatOverride: params.line+1}); }, + sort: function(cm, params) { + var reverse, ignoreCase, unique, number; + function parseArgs() { + if (params.argString) { + var args = new CodeMirror.StringStream(params.argString); + if (args.eat('!')) { reverse = true; } + if (args.eol()) { return; } + if (!args.eatSpace()) { throw 'invalid arguments ' + args.match(/.*/)[0]; } + var opts = args.match(/[a-z]+/); + if (opts) { + opts = opts[0]; + ignoreCase = opts.indexOf('i') != -1; + unique = opts.indexOf('u') != -1; + var decimal = opts.indexOf('d') != -1 && 1; + var hex = opts.indexOf('x') != -1 && 1; + var octal = opts.indexOf('o') != -1 && 1; + if (decimal + hex + octal > 1) { throw 'invalid arguments'; } + number = decimal && 'decimal' || hex && 'hex' || octal && 'octal'; + } + if (args.eatSpace() && args.match(/\/.*\//)) { throw 'patterns not supported'; } + } + } + parseArgs(); + var lineStart = params.line || cm.firstLine(); + var lineEnd = params.lineEnd || params.line || cm.lastLine(); + if (lineStart == lineEnd) { return; } + var curStart = { line: lineStart, ch: 0 }; + var curEnd = { line: lineEnd, ch: lineLength(cm, lineEnd) }; + var text = cm.getRange(curStart, curEnd).split('\n'); + var numberRegex = (number == 'decimal') ? /(-?)([\d]+)/ : + (number == 'hex') ? /(-?)(?:0x)?([0-9a-f]+)/i : + (number == 'octal') ? /([0-7]+)/ : null; + var radix = (number == 'decimal') ? 10 : (number == 'hex') ? 16 : (number == 'octal') ? 8 : null; + var numPart = [], textPart = []; + if (number) { + for (var i = 0; i < text.length; i++) { + if (numberRegex.exec(text[i])) { + numPart.push(text[i]); + } else { + textPart.push(text[i]); + } + } + } else { + textPart = text; + } + function compareFn(a, b) { + if (reverse) { var tmp; tmp = a; a = b; b = tmp; } + if (ignoreCase) { a = a.toLowerCase(); b = b.toLowerCase(); } + var anum = number && numberRegex.exec(a); + var bnum = number && numberRegex.exec(b); + if (!anum) { return a < b ? -1 : 1; } + anum = parseInt((anum[1] + anum[2]).toLowerCase(), radix); + bnum = parseInt((bnum[1] + bnum[2]).toLowerCase(), radix); + return anum - bnum; + } + numPart.sort(compareFn); + textPart.sort(compareFn); + text = (!reverse) ? textPart.concat(numPart) : numPart.concat(textPart); + if (unique) { // Remove duplicate lines + var textOld = text; + var lastLine; + text = [] + for (var i = 0; i < textOld.length; i++) { + if (textOld[i] != lastLine) { + text.push(textOld[i]); + } + lastLine = textOld[i]; + } + } + cm.replaceRange(text.join('\n'), curStart, curEnd); + }, substitute: function(cm, params) { var argString = params.argString; var slashes = findUnescapedSlashes(argString); @@ -3067,7 +3145,7 @@ } var state = getSearchState(cm); var query = state.getQuery(); - var lineStart = params.line || cm.firstLine(); + var lineStart = (params.line !== undefined) ? params.line : cm.getCursor().line; var lineEnd = params.lineEnd || lineStart; if (count) { lineStart = lineEnd; diff --git a/test/vim_test.js b/test/vim_test.js index f3b53508f6..28c967b8f5 100644 --- a/test/vim_test.js +++ b/test/vim_test.js @@ -11,7 +11,7 @@ var code = '' + ' bufp = buf;\n' + ' }\n' + '\n' + -' return (--n >= 0) ? (unsigned char) *bufp++ : EOF;\n' + +' return (--n >= 0) ? (unsigned char) *bufp++ : EOF;\n' + ' \n' + '}\n'; @@ -1940,6 +1940,59 @@ testVim('ex_write', function(cm, vim, helpers) { } CodeMirror.commands.save = tmp; }); +testVim('ex_sort', function(cm, vim, helpers) { + helpers.doEx('sort'); + eq('Z\na\nb\nc\nd', cm.getValue()); +}, { value: 'b\nZ\nd\nc\na'}); +testVim('ex_sort_reverse', function(cm, vim, helpers) { + helpers.doEx('sort!'); + eq('d\nc\nb\na', cm.getValue()); +}, { value: 'b\nd\nc\na'}); +testVim('ex_sort_range', function(cm, vim, helpers) { + helpers.doEx('2,3sort'); + eq('b\nc\nd\na', cm.getValue()); +}, { value: 'b\nd\nc\na'}); +testVim('ex_sort_oneline', function(cm, vim, helpers) { + helpers.doEx('2sort'); + // Expect no change. + eq('b\nd\nc\na', cm.getValue()); +}, { value: 'b\nd\nc\na'}); +testVim('ex_sort_ignoreCase', function(cm, vim, helpers) { + helpers.doEx('sort i'); + eq('a\nb\nc\nd\nZ', cm.getValue()); +}, { value: 'b\nZ\nd\nc\na'}); +testVim('ex_sort_unique', function(cm, vim, helpers) { + helpers.doEx('sort u'); + eq('Z\na\nb\nc\nd', cm.getValue()); +}, { value: 'b\nZ\na\na\nd\na\nc\na'}); +testVim('ex_sort_decimal', function(cm, vim, helpers) { + helpers.doEx('sort d'); + eq('d3\n s5\n6\n.9', cm.getValue()); +}, { value: '6\nd3\n s5\n.9'}); +testVim('ex_sort_decimal_negative', function(cm, vim, helpers) { + helpers.doEx('sort d'); + eq('z-9\nd3\n s5\n6\n.9', cm.getValue()); +}, { value: '6\nd3\n s5\n.9\nz-9'}); +testVim('ex_sort_decimal_reverse', function(cm, vim, helpers) { + helpers.doEx('sort! d'); + eq('.9\n6\n s5\nd3', cm.getValue()); +}, { value: '6\nd3\n s5\n.9'}); +testVim('ex_sort_hex', function(cm, vim, helpers) { + helpers.doEx('sort x'); + eq(' s5\n6\n.9\n&0xB\nd3', cm.getValue()); +}, { value: '6\nd3\n s5\n&0xB\n.9'}); +testVim('ex_sort_octal', function(cm, vim, helpers) { + helpers.doEx('sort o'); + eq('.8\n.9\nd3\n s5\n6', cm.getValue()); +}, { value: '6\nd3\n s5\n.9\n.8'}); +testVim('ex_sort_decimal_mixed', function(cm, vim, helpers) { + helpers.doEx('sort d'); + eq('y\nz\nc1\nb2\na3', cm.getValue()); +}, { value: 'a3\nz\nc1\ny\nb2'}); +testVim('ex_sort_decimal_mixed_reverse', function(cm, vim, helpers) { + helpers.doEx('sort! d'); + eq('a3\nb2\nc1\nz\ny', cm.getValue()); +}, { value: 'a3\nz\nc1\ny\nb2'}); testVim('ex_substitute_same_line', function(cm, vim, helpers) { cm.setCursor(1, 0); helpers.doEx('s/one/two'); From 366c18b89eb02b9e6b1e3b7c45e64ae9cb6a71e6 Mon Sep 17 00:00:00 2001 From: Marijn Haverbeke Date: Sun, 2 Jun 2013 18:26:44 +0200 Subject: [PATCH 1152/5780] [show-hint addon] (experiment) Add a container option --- addon/hint/show-hint.js | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/addon/hint/show-hint.js b/addon/hint/show-hint.js index a0661e4622..ab23e97383 100644 --- a/addon/hint/show-hint.js +++ b/addon/hint/show-hint.js @@ -54,7 +54,7 @@ CodeMirror.showHint = function(cm, getHints, options) { var left = pos.left, top = pos.bottom, below = true; hints.style.left = left + "px"; hints.style.top = top + "px"; - document.body.appendChild(hints); + (options.container || document.body).appendChild(hints); CodeMirror.signal(data, "shown"); // If we're at the edge of the screen, then we want the menu to appear on the left of the cursor. From 7c72746248d722fdc8c51f064e18e118d0ae7ad6 Mon Sep 17 00:00:00 2001 From: Marijn Haverbeke Date: Mon, 3 Jun 2013 11:36:17 +0200 Subject: [PATCH 1153/5780] Fix bug that caused zero-length spans to occasionally survive edits Issue #1561 --- lib/codemirror.js | 7 +++++++ test/test.js | 7 +++++++ 2 files changed, 14 insertions(+) diff --git a/lib/codemirror.js b/lib/codemirror.js index d378040336..eab89da931 100644 --- a/lib/codemirror.js +++ b/lib/codemirror.js @@ -3847,6 +3847,13 @@ window.CodeMirror = (function() { } } } + if (sameLine && first) { + // Make sure we didn't create any zero-length spans + for (var i = 0; i < first.length; ++i) + if (first[i].from != null && first[i].from == first[i].to && first[i].marker.type != "bookmark") + first.splice(i--, 1); + if (!first.length) first = null; + } var newMarkers = [first]; if (!sameLine) { diff --git a/test/test.js b/test/test.js index cefe14cc90..fe478bb58e 100644 --- a/test/test.js +++ b/test/test.js @@ -450,6 +450,13 @@ testCM("markClearBetween", function(cm) { eq(cm.findMarksAt(Pos(1, 1)).length, 0); }); +testCM("deleteSpanCollapsedInclusiveLeft", function(cm) { + var from = Pos(1, 0), to = Pos(1, 1); + var m = cm.markText(from, to, {collapsed: true, inclusiveLeft: true}); + // Delete collapsed span. + cm.replaceRange("", from, to); +}, {value: "abc\nX\ndef"}); + testCM("bookmark", function(cm) { function p(v) { return v && Pos(v[0], v[1]); } forEach([{a: [1, 0], b: [1, 1], c: "", d: [1, 4]}, From de92ca1bab49a2f18bd6f7e9d8200a9d75270304 Mon Sep 17 00:00:00 2001 From: Marijn Haverbeke Date: Mon, 3 Jun 2013 14:06:42 +0200 Subject: [PATCH 1154/5780] Allow inline widgets to wrap again, measurement of their start and end --- lib/codemirror.css | 1 - lib/codemirror.js | 55 ++++++++++++++++++++++++++++++++-------------- test/test.js | 16 ++++++++++++++ 3 files changed, 54 insertions(+), 18 deletions(-) diff --git a/lib/codemirror.css b/lib/codemirror.css index f5379d967c..52881f7dfd 100644 --- a/lib/codemirror.css +++ b/lib/codemirror.css @@ -205,7 +205,6 @@ div.CodeMirror span.CodeMirror-nonmatchingbracket {color: #f22;} } .CodeMirror-widget { - display: inline-block; } .CodeMirror-wrap .CodeMirror-scroll { diff --git a/lib/codemirror.js b/lib/codemirror.js index eab89da931..b32de75c37 100644 --- a/lib/codemirror.js +++ b/lib/codemirror.js @@ -799,17 +799,17 @@ window.CodeMirror = (function() { function drawForLine(line, fromArg, toArg, retTop) { var lineObj = getLine(doc, line); var lineLen = lineObj.text.length, rVal = retTop ? Infinity : -Infinity; - function coords(ch) { - return charCoords(cm, Pos(line, ch), "div", lineObj); + function coords(ch, bias) { + return charCoords(cm, Pos(line, ch), "div", lineObj, bias); } iterateBidiSections(getOrder(lineObj), fromArg || 0, toArg == null ? lineLen : toArg, function(from, to, dir) { - var leftPos = coords(from), rightPos, left, right; + var leftPos = coords(from, "left"), rightPos, left, right; if (from == to) { rightPos = leftPos; left = right = leftPos.left; } else { - rightPos = coords(to - 1); + rightPos = coords(to - 1, "right"); if (dir == "rtl") { var tmp = leftPos; leftPos = rightPos; rightPos = tmp; } left = leftPos.left; right = rightPos.right; @@ -968,7 +968,7 @@ window.CodeMirror = (function() { return e.offsetLeft; } - function measureChar(cm, line, ch, data) { + function measureChar(cm, line, ch, data, bias) { var dir = -1; data = data || measureLine(cm, line); @@ -979,7 +979,8 @@ window.CodeMirror = (function() { } return {left: pos < ch ? r.right : r.left, right: pos > ch ? r.left : r.right, - top: r.top, bottom: r.bottom}; + top: bias == "right" && r.topRight != null ? r.topRight : r.top, + bottom: bias == "right" && r.bottomRight != null ? r.bottomRight : r.bottom}; } function findCachedMeasurement(cm, line) { @@ -1052,9 +1053,9 @@ window.CodeMirror = (function() { if (ie_lt9 && display.measure.first != pre) removeChildrenAndAdd(display.measure, pre); - for (var i = 0, cur; i < measure.length; ++i) if (cur = measure[i]) { - var size = getRect(cur); - var top = Math.max(0, size.top - outer.top), bot = Math.min(size.bottom - outer.top, maxBot); + function categorizeVSpan(top, bot) { + if (bot > maxBot) bot = maxBot; + if (top < 0) top = 0; for (var j = 0; j < vranges.length; j += 2) { var rtop = vranges[j], rbot = vranges[j+1]; if (rtop > bot || rbot < top) continue; @@ -1063,17 +1064,37 @@ window.CodeMirror = (function() { Math.min(bot, rbot) - Math.max(top, rtop) >= (bot - top) >> 1) { vranges[j] = Math.min(top, rtop); vranges[j+1] = Math.max(bot, rbot); - break; + return j; } } - if (j == vranges.length) vranges.push(top, bot); + vranges.push(top, bot); + return j; + } + + for (var i = 0, cur; i < measure.length; ++i) if (cur = measure[i]) { + var size; + // A widget might wrap, needs special care + if (/\bCodeMirror-widget\b/.test(cur.className) && cur.getClientRects) { + var rects = cur.getClientRects(), rLeft = rects[0], rRight = rects[rects.length - 1]; + if (rects.length == 1) { + size = rLeft; + } else { + var vCatLeft = categorizeVSpan(rLeft.top - outer.top, rLeft.bottom - outer.top); + var vCatRight = categorizeVSpan(rRight.top - outer.top, rRight.bottom - outer.top); + data[i] = {left: rLeft.left - outer.left, right: rRight.right - outer.left, + top: vCatLeft, topRight: vCatRight}; + continue; + } + } else size = getRect(cur); + var vCat = categorizeVSpan(size.top - outer.top, size.bottom - outer.top); var right = size.right; if (cur.measureRight) right = getRect(cur.measureRight).left; - data[i] = {left: size.left - outer.left, right: right - outer.left, top: j}; + data[i] = {left: size.left - outer.left, right: right - outer.left, top: vCat}; } for (var i = 0, cur; i < data.length; ++i) if (cur = data[i]) { - var vr = cur.top; + var vr = cur.top, vrRight = cur.topRight; cur.top = vranges[vr]; cur.bottom = vranges[vr+1]; + if (vrRight != null) { cur.topRight = vranges[vrRight]; cur.bottomRight = vranges[vrRight]; } } return data; @@ -1086,7 +1107,7 @@ window.CodeMirror = (function() { if (sp.collapsed && (sp.to == null || sp.to == line.text.length)) hasBadSpan = true; } var cached = !hasBadSpan && findCachedMeasurement(cm, line); - if (cached) return measureChar(cm, line, line.text.length, cached.measure).right; + if (cached) return measureChar(cm, line, line.text.length, cached.measure, "right").right; var pre = lineContent(cm, line); var end = pre.appendChild(zeroWidthElement(cm.display.measure)); @@ -1144,16 +1165,16 @@ window.CodeMirror = (function() { return {left: left - lineSpaceBox.left, top: top - lineSpaceBox.top}; } - function charCoords(cm, pos, context, lineObj) { + function charCoords(cm, pos, context, lineObj, bias) { if (!lineObj) lineObj = getLine(cm.doc, pos.line); - return intoCoordSystem(cm, lineObj, measureChar(cm, lineObj, pos.ch), context); + return intoCoordSystem(cm, lineObj, measureChar(cm, lineObj, pos.ch, null, bias), context); } function cursorCoords(cm, pos, context, lineObj, measurement) { lineObj = lineObj || getLine(cm.doc, pos.line); if (!measurement) measurement = measureLine(cm, lineObj); function get(ch, right) { - var m = measureChar(cm, lineObj, ch, measurement); + var m = measureChar(cm, lineObj, ch, measurement, right ? "right" : "left"); if (right) m.left = m.right; else m.right = m.left; return intoCoordSystem(cm, lineObj, m, context); } diff --git a/test/test.js b/test/test.js index fe478bb58e..8936be8daf 100644 --- a/test/test.js +++ b/test/test.js @@ -786,6 +786,22 @@ testCM("badNestedFold", function(cm) { is(/overlap/i.test(caught.message), "wrong error"); }); +testCM("wrappingInlineWidget", function(cm) { + cm.setSize("10em"); + var w = document.createElement("span"); + w.style.background = "yellow"; + w.innerHTML = "one two three four"; + cm.markText(Pos(0, 6), Pos(0, 9), {replacedWith: w}); + var cur0 = cm.cursorCoords(Pos(0, 0)), cur1 = cm.cursorCoords(Pos(0, 10)); + is(cur0.top < cur1.top); + is(cur0.bottom < cur1.bottom); + var curL = cm.cursorCoords(Pos(0, 6)), curR = cm.cursorCoords(Pos(0, 9)); + eq(curL.top, cur0.top); + eq(curL.bottom, cur0.bottom); + eq(curR.top, cur1.top); + eq(curR.bottom, cur1.bottom); +}, {value: "1 2 3 xxx 4 5 6", lineWrapping: true}); + testCM("inlineWidget", function(cm) { var w = cm.setBookmark(Pos(0, 2), {widget: document.createTextNode("uu")}); cm.setCursor(0, 2); From 0bcc95abe15059403e0bd4d31e839c1267e8b36d Mon Sep 17 00:00:00 2001 From: tfjgeorge Date: Tue, 4 Jun 2013 11:45:39 +0300 Subject: [PATCH 1155/5780] Update index.html. Fixed typo jsfiddle.net instead of jsfiddle.com --- index.html | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/index.html b/index.html index 0f855ecbb3..59599c46cb 100644 --- a/index.html +++ b/index.html @@ -123,7 +123,7 @@

    Real-world uses:

  • Light Table
  • Adobe Brackets
  • jsbin.com
  • -
  • jsfiddle.com
  • +
  • jsfiddle.net
  • Bitbucket
  • Google Apps Script
  • Eloquent JavaScript
  • From 9fcee65e590a96b3dff045bf10f7db17d2d38c0d Mon Sep 17 00:00:00 2001 From: Marijn Haverbeke Date: Tue, 4 Jun 2013 11:14:19 +0200 Subject: [PATCH 1156/5780] Fix bug where measuring the right of a wrapped inline widget broke at end of line --- lib/codemirror.js | 7 ++++--- test/test.js | 10 +++++++--- 2 files changed, 11 insertions(+), 6 deletions(-) diff --git a/lib/codemirror.js b/lib/codemirror.js index b32de75c37..bd5d5d25d3 100644 --- a/lib/codemirror.js +++ b/lib/codemirror.js @@ -977,10 +977,11 @@ window.CodeMirror = (function() { if (r) break; if (dir < 0 && pos == 0) dir = 1; } + var rightV = (pos < ch || bias == "right") && r.topRight != null; return {left: pos < ch ? r.right : r.left, right: pos > ch ? r.left : r.right, - top: bias == "right" && r.topRight != null ? r.topRight : r.top, - bottom: bias == "right" && r.bottomRight != null ? r.bottomRight : r.bottom}; + top: rightV ? r.topRight : r.top, + bottom: rightV ? r.bottomRight : r.bottom}; } function findCachedMeasurement(cm, line) { @@ -1094,7 +1095,7 @@ window.CodeMirror = (function() { for (var i = 0, cur; i < data.length; ++i) if (cur = data[i]) { var vr = cur.top, vrRight = cur.topRight; cur.top = vranges[vr]; cur.bottom = vranges[vr+1]; - if (vrRight != null) { cur.topRight = vranges[vrRight]; cur.bottomRight = vranges[vrRight]; } + if (vrRight != null) { cur.topRight = vranges[vrRight]; cur.bottomRight = vranges[vrRight+1]; } } return data; diff --git a/test/test.js b/test/test.js index 8936be8daf..fa8cccdc6f 100644 --- a/test/test.js +++ b/test/test.js @@ -787,9 +787,9 @@ testCM("badNestedFold", function(cm) { }); testCM("wrappingInlineWidget", function(cm) { - cm.setSize("10em"); + cm.setSize("11em"); var w = document.createElement("span"); - w.style.background = "yellow"; + w.style.color = "red"; w.innerHTML = "one two three four"; cm.markText(Pos(0, 6), Pos(0, 9), {replacedWith: w}); var cur0 = cm.cursorCoords(Pos(0, 0)), cur1 = cm.cursorCoords(Pos(0, 10)); @@ -800,7 +800,11 @@ testCM("wrappingInlineWidget", function(cm) { eq(curL.bottom, cur0.bottom); eq(curR.top, cur1.top); eq(curR.bottom, cur1.bottom); -}, {value: "1 2 3 xxx 4 5 6", lineWrapping: true}); + cm.replaceRange("", Pos(0, 9), Pos(0)); + curR = cm.cursorCoords(Pos(0, 9)); + eq(curR.top, cur1.top); + eq(curR.bottom, cur1.bottom); +}, {value: "1 2 3 xxx 4", lineWrapping: true}); testCM("inlineWidget", function(cm) { var w = cm.setBookmark(Pos(0, 2), {widget: document.createTextNode("uu")}); From eafb2cadf67c9e6da560ab948dfdb2cb84baa2e3 Mon Sep 17 00:00:00 2001 From: santec Date: Wed, 5 Jun 2013 08:57:20 +0200 Subject: [PATCH 1157/5780] [sql mode] Minor fixes * index.html included codemirror.js twice * optimize .tableName syntax check --- mode/sql/index.html | 3 +-- mode/sql/sql.js | 2 +- 2 files changed, 2 insertions(+), 3 deletions(-) diff --git a/mode/sql/index.html b/mode/sql/index.html index 8a2495ca24..bc3b3a819c 100644 --- a/mode/sql/index.html +++ b/mode/sql/index.html @@ -5,7 +5,6 @@ SQL Mode for CodeMirror - + + +

    CodeMirror: merge view demo

    + +
    + +

    The merge +addon provides an interface for displaying and merging diffs, +either two-way +or three-way. The left +(or center) pane is editable, and the differences with the other +pane(s) are shown live as you edit it.

    + +

    This addon depends on +the google-diff-match-patch +library to compute the diffs.

    + + diff --git a/doc/manual.html b/doc/manual.html index 4bf94b1c81..2ecd71fbe3 100644 --- a/doc/manual.html +++ b/doc/manual.html @@ -1957,6 +1957,20 @@

    Add-ons

    Also gives the editor a CodeMirror-empty CSS class whenever it doesn't contain any text. See the demo.
    + +
    merge/merge.js
    +
    Implements an interface for merging changes, using either a + 2-way or a 3-way view. The CodeMirror.MergeView + constructor takes arguments similar to + the CodeMirror + constructor, first a node to append the interface to, and then + an options object. Two extra optional options are + recognized, origLeft and origRight, + which may be strings that provide original versions of the + document, which will be shown to the left and right of the + editor in non-editable CodeMirror instances. The merge interface + will highlight changes between the editable document and the + original(s) (demo).

    Writing CodeMirror Modes

    diff --git a/test/lint/lint.js b/test/lint/lint.js index 5f808922f2..4fc577fa01 100644 --- a/test/lint/lint.js +++ b/test/lint/lint.js @@ -103,7 +103,7 @@ function checkDir(dir) { fs.readdirSync(dir).forEach(function(file) { var fname = dir + "/" + file; if (/\.js$/.test(file)) checkFile(fname); - else if (fs.lstatSync(fname).isDirectory()) checkDir(fname); + else if (file != "dep" && fs.lstatSync(fname).isDirectory()) checkDir(fname); }); } From 4dee296563f3d6e6c1f1d93919e3a4875f60c70a Mon Sep 17 00:00:00 2001 From: Marijn Haverbeke Date: Wed, 19 Jun 2013 19:31:08 +0200 Subject: [PATCH 1243/5780] [show-hint addon] Fix problem with close event firing too late --- addon/hint/show-hint.js | 4 +++- 1 file changed, 3 insertions(+), 1 deletion(-) diff --git a/addon/hint/show-hint.js b/addon/hint/show-hint.js index 02c94f4408..35e5cbb721 100644 --- a/addon/hint/show-hint.js +++ b/addon/hint/show-hint.js @@ -19,7 +19,7 @@ this.cm = cm; this.getHints = getHints; this.options = options; - this.widget = null; + this.widget = this.onClose = null; } Completion.prototype = { @@ -27,6 +27,7 @@ if (!this.active()) return; if (this.widget) this.widget.close(); + if (this.onClose) this.onClose(); this.cm.state.completionActive = null; CodeMirror.signal(this.cm, "endCompletion", this.cm); }, @@ -96,6 +97,7 @@ debounce = setTimeout(update, 170); } this.cm.on("cursorActivity", activity); + this.onClose = done; } }; From 03449c69ffebdac0648a3f4d347cddd365522572 Mon Sep 17 00:00:00 2001 From: Brandon Frohs Date: Wed, 19 Jun 2013 15:53:02 -0400 Subject: [PATCH 1244/5780] [markdown mode] Fix bug with two links or email addresses on same line. --- mode/markdown/markdown.js | 4 ++-- mode/markdown/test.js | 6 ++++++ 2 files changed, 8 insertions(+), 2 deletions(-) diff --git a/mode/markdown/markdown.js b/mode/markdown/markdown.js index d93d55590e..72b0d6ced2 100644 --- a/mode/markdown/markdown.js +++ b/mode/markdown/markdown.js @@ -308,11 +308,11 @@ CodeMirror.defineMode("markdown", function(cmCfg, modeCfg) { return type; } - if (ch === '<' && stream.match(/^(https?|ftps?):\/\/(?:[^\\>]|\\.)+>/, true)) { + if (ch === '<' && stream.match(/^(https?|ftps?):\/\/(?:[^\\>]|\\.)+>/, false)) { return switchInline(stream, state, inlineElement(linkinline, '>')); } - if (ch === '<' && stream.match(/^[^> \\]+@(?:[^\\>]|\\.)+>/, true)) { + if (ch === '<' && stream.match(/^[^> \\]+@(?:[^\\>]|\\.)+>/, false)) { return switchInline(stream, state, inlineElement(linkemail, '>')); } diff --git a/mode/markdown/test.js b/mode/markdown/test.js index c451412fee..99ae056132 100644 --- a/mode/markdown/test.js +++ b/mode/markdown/test.js @@ -533,9 +533,15 @@ MT("linkWeb", "[link ] foo"); + MT("linkWebDouble", + "[link ] foo [link ]"); + MT("linkEmail", "[link ] foo"); + MT("linkEmailDouble", + "[link ] foo [link ]"); + MT("emAsterisk", "[em *foo*] bar"); From 112fdb507e33d0e3140124cdc4a689e790b2fec0 Mon Sep 17 00:00:00 2001 From: Brandon Frohs Date: Wed, 19 Jun 2013 16:00:29 -0400 Subject: [PATCH 1245/5780] [test harness] Escape < and & in test input/results. --- test/mode_test.js | 6 +++--- 1 file changed, 3 insertions(+), 3 deletions(-) diff --git a/test/mode_test.js b/test/mode_test.js index 79a7752492..c759b257bf 100644 --- a/test/mode_test.js +++ b/test/mode_test.js @@ -84,7 +84,7 @@ var s = ''; if (pass) { s += '
    '; - s += '
    ' + text + '
    '; + s += '
    ' + text.replace('&', '&').replace('<', '<') + '
    '; s += '
    '; s += prettyPrintOutputTable(observedOutput); s += '
    '; @@ -92,7 +92,7 @@ return s; } else { s += '
    '; - s += '
    ' + text + '
    '; + s += '
    ' + text.replace('&', '&').replace('<', '<') + '
    '; s += '
    '; s += 'expected:'; s += prettyPrintOutputTable(expectedOutput); @@ -178,7 +178,7 @@ s += '' + '' + - val.replace(/ /g,'\xb7') + + val.replace(/ /g,'\xb7').replace('&', '&').replace('<', '<') + '' + ''; } From b609e8f0e9041a619ad647d714b6ecdcf908e909 Mon Sep 17 00:00:00 2001 From: Marijn Haverbeke Date: Thu, 20 Jun 2013 10:41:33 +0200 Subject: [PATCH 1246/5780] Document inputRead and keyHandled events --- doc/manual.html | 11 +++++++++++ 1 file changed, 11 insertions(+) diff --git a/doc/manual.html b/doc/manual.html index 2ecd71fbe3..ceaad6b44b 100644 --- a/doc/manual.html +++ b/doc/manual.html @@ -431,6 +431,17 @@

    Events

    Will be fired when the cursor or selection moves, or any change is made to the editor content.
    +
    "keyHandled" (instance: CodeMirror, name: string, event: Event)
    +
    Fired after a key is handled through a + keymap. name is the name of the handled key (for + example "Ctrl-X" or "'q'"), + and event is the DOM keydown + or keypress event.
    + +
    "inputRead" (instance: CodeMirror, changeObj: object)
    +
    Fired whenever new input is read from the hidden textarea + (typed or pasted by the user).
    +
    "beforeSelectionChange" (instance: CodeMirror, selection: {head, anchor})
    This event is fired before the selection is moved. Its handler may modify the resulting selection head and anchor. From 7e4a1382542273717942961b862dd13da966123b Mon Sep 17 00:00:00 2001 From: Marijn Haverbeke Date: Thu, 20 Jun 2013 10:44:37 +0200 Subject: [PATCH 1247/5780] Mark release 3.14.0 --- doc/compress.html | 2 ++ doc/manual.html | 10 ++++------ doc/oldrelease.html | 14 ++++++++++++++ index.html | 41 +++++++++++++++++++++++++++-------------- lib/codemirror.js | 2 +- package.json | 2 +- 6 files changed, 49 insertions(+), 22 deletions(-) diff --git a/doc/compress.html b/doc/compress.html index 9c0fafec18..fede6f43fa 100644 --- a/doc/compress.html +++ b/doc/compress.html @@ -30,6 +30,7 @@

    { } CodeMi

    Version: ", f); + else + f(prompt(text, "")); + } + + // Tooltips + + function tempTooltip(cm, content) { + var where = cm.cursorCoords(); + var tip = makeTooltip(where.right + 1, where.bottom, content); + function clear() { + if (!tip.parentNode) return; + cm.off("cursorActivity", clear); + fadeOut(tip); + } + setTimeout(clear, 1700); + cm.on("cursorActivity", clear); + } + + function makeTooltip(x, y, content) { + var node = elt("div", cls + "tooltip", content); + node.style.left = x + "px"; + node.style.top = y + "px"; + document.body.appendChild(node); + return node; + } + + function remove(node) { + var p = node && node.parentNode; + if (p) p.removeChild(node); + } + + function fadeOut(tooltip) { + tooltip.style.opacity = "0"; + setTimeout(function() { remove(tooltip); }, 1100); + } + + function showError(ts, cm, msg) { + if (ts.options.showError) + ts.options.showError(cm, msg); + else + tempTooltip(cm, String(msg)); + } +})(); diff --git a/demo/tern.html b/demo/tern.html new file mode 100644 index 0000000000..22464f8a60 --- /dev/null +++ b/demo/tern.html @@ -0,0 +1,95 @@ + + + + + CodeMirror: Tern Demo + + + + + + + + + + + + + + + + + + + + + + + + + +

    CodeMirror: Tern Demo

    + +

    + + + + + From 2677aba253ef5c0a1ed6332e961cef5712f334d3 Mon Sep 17 00:00:00 2001 From: Marijn Haverbeke Date: Thu, 20 Jun 2013 17:37:44 +0200 Subject: [PATCH 1251/5780] [xml-fold addon] Prevent infinite recursion from weird lastIndexOf behavior Issue #1625 --- addon/fold/xml-fold.js | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/addon/fold/xml-fold.js b/addon/fold/xml-fold.js index b764bc0190..29d730eb71 100644 --- a/addon/fold/xml-fold.js +++ b/addon/fold/xml-fold.js @@ -43,7 +43,7 @@ } function toTagStart(iter) { for (;;) { - var lt = iter.text.lastIndexOf("<", iter.ch - 1); + var lt = iter.ch ? iter.text.lastIndexOf("<", iter.ch - 1) : -1; if (lt == -1) { if (prevLine(iter)) continue; else return; } if (!tagAt(iter, lt + 1)) { iter.ch = lt; continue; } xmlTagStart.lastIndex = lt; @@ -65,7 +65,7 @@ } function toPrevTag(iter) { for (;;) { - var gt = iter.text.lastIndexOf(">", iter.ch - 1); + var gt = iter.ch ? iter.text.lastIndexOf(">", iter.ch - 1) : -1; if (gt == -1) { if (prevLine(iter)) continue; else return; } if (!tagAt(iter, gt + 1)) { iter.ch = gt; continue; } var lastSlash = iter.text.lastIndexOf("/", gt); From f674dd148c27c882d3a85885caa9c27bb5638ee5 Mon Sep 17 00:00:00 2001 From: Marijn Haverbeke Date: Fri, 21 Jun 2013 09:53:32 +0200 Subject: [PATCH 1252/5780] Support insertAt option to addLineWidget Issue #1627 --- doc/manual.html | 10 ++++++++-- lib/codemirror.js | 4 +++- 2 files changed, 11 insertions(+), 3 deletions(-) diff --git a/doc/manual.html b/doc/manual.html index 655e64db4c..68692e2956 100644 --- a/doc/manual.html +++ b/doc/manual.html @@ -1253,7 +1253,7 @@

    Widget, gutter, and decoration methods

    widget again, simply use DOM methods (move it somewhere else, or call removeChild on its parent).

    -
    cm.addLineWidget(line: integer|LineHandle, node: Element, ?options: object) → LineWidget
    +
    cm.addLineWidget(line: integer|LineHandle, node: Element, ?options: object, ?pos: integer) → LineWidget
    Adds a line widget, an element shown below a line, spanning the whole of the editor's width, and moving the lines below it downwards. line should be either an integer or a @@ -1261,7 +1261,7 @@

    Widget, gutter, and decoration methods

    will be displayed below the given line. options, when given, should be an object that configures the behavior of the widget. The following options are supported (all default to - false) → + false):
    coverGutter: boolean
    Whether the widget should cover the gutter.
    @@ -1279,6 +1279,12 @@

    Widget, gutter, and decoration methods

    drag events occurring in this widget. Default is false—the events will be left alone for the default browser handler, or specific handlers on the widget, to capture.
    +
    insertAt: integer
    +
    By default, the widget is added below other widgets for + the line. This option can be used to place it at a different + position (zero for the top, N to put it after the Nth other + widget). Note that this only has effect once, when the + widget is created. Note that the widget node will become a descendant of nodes with CodeMirror-specific CSS classes, and those classes might in some diff --git a/lib/codemirror.js b/lib/codemirror.js index 5da1f17fea..ea3f8d9328 100644 --- a/lib/codemirror.js +++ b/lib/codemirror.js @@ -4078,7 +4078,9 @@ window.CodeMirror = (function() { var widget = new LineWidget(cm, node, options); if (widget.noHScroll) cm.display.alignWidgets = true; changeLine(cm, handle, function(line) { - (line.widgets || (line.widgets = [])).push(widget); + var widgets = line.widgets || (line.widgets = []); + if (options.insertAt == null) widgets.push(widget); + else widgets.splice(Math.max(widgets.length - 1, options.insertAt), 0, widget); widget.line = line; if (!lineIsHidden(cm.doc, line) || widget.showIfHidden) { var aboveVisible = heightAtLine(cm, line) < cm.display.scroller.scrollTop; From 93a850a52b98ddf5a633b793df33edb0b3794ab1 Mon Sep 17 00:00:00 2001 From: James Campos Date: Tue, 18 Jun 2013 17:56:48 -0700 Subject: [PATCH 1253/5780] [vim keymap] Fix bugs in line handling Closes #1612 --- keymap/vim.js | 49 ++++++++++++++++++++++++------------------------ test/vim_test.js | 25 +++++++++++++++++------- 2 files changed, 42 insertions(+), 32 deletions(-) diff --git a/keymap/vim.js b/keymap/vim.js index e05be03176..8c942a3ff5 100644 --- a/keymap/vim.js +++ b/keymap/vim.js @@ -554,19 +554,16 @@ // Clear input state and get back to normal mode. vim.inputState = new InputState(); if (vim.visualMode) { - exitVisualMode(cm, vim); + exitVisualMode(cm); } return; } - if (vim.visualMode && - cursorEqual(cm.getCursor('head'), cm.getCursor('anchor'))) { - // The selection was cleared. Exit visual mode. - exitVisualMode(cm, vim); - } + // Enter visual mode when the mouse selects text. if (!vim.visualMode && !cursorEqual(cm.getCursor('head'), cm.getCursor('anchor'))) { vim.visualMode = true; vim.visualLine = false; + cm.on('mousedown', exitVisualMode); } if (key != '0' || (key == '0' && vim.inputState.getRepeat() === 0)) { // Have to special case 0 since it's both a motion and a number. @@ -1146,7 +1143,7 @@ operators[operator](cm, operatorArgs, vim, curStart, curEnd, curOriginal); if (vim.visualMode) { - exitVisualMode(cm, vim); + exitVisualMode(cm); } if (operatorArgs.enterInsertMode) { actions.enterInsertMode(cm, {}, vim); @@ -1272,8 +1269,13 @@ } var repeat = motionArgs.repeat+(motionArgs.repeatOffset||0); var line = motionArgs.forward ? cur.line + repeat : cur.line - repeat; - if (line < cm.firstLine() || line > cm.lastLine() ) { - return null; + var first = cm.firstLine(); + var last = cm.lastLine(); + // Vim cancels linewise motions that start on an edge and move beyond + // that edge. It does not cancel motions that do not start on an edge. + if ((line < first && cur.line == first) || + (line > last && cur.line == last)) { + return; } if(motionArgs.toFirstChar){ endCh=findFirstNonWhiteSpaceCharacter(cm.getLine(line)); @@ -1478,18 +1480,12 @@ operatorArgs.registerName, 'change', cm.getRange(curStart, curEnd), operatorArgs.linewise); if (operatorArgs.linewise) { - // Delete starting at the first nonwhitespace character of the first - // line, instead of from the start of the first line. This way we get - // an indent when we get into insert mode. This behavior isn't quite - // correct because we should treat this as a completely new line, and - // indent should be whatever codemirror thinks is the right indent. - // But cm.indentLine doesn't seem work on empty lines. - // TODO: Fix the above. - curStart.ch = - findFirstNonWhiteSpaceCharacter(cm.getLine(curStart.line)); - // Insert an additional newline so that insert mode can start there. - // curEnd should be on the first character of the new line. - cm.replaceRange('\n', curStart, curEnd); + // Push the next line back down, if there is a next line. + var replacement = curEnd.line > cm.lastLine() ? '' : '\n'; + cm.replaceRange(replacement, curStart, curEnd); + cm.indentLine(curStart.line, 'smart'); + // null ch so setCursor moves to end of line. + curStart.ch = null; } else { // Exclude trailing whitespace if the range is not all whitespace. var text = cm.getRange(curStart, curEnd); @@ -1651,6 +1647,7 @@ // equal to the repeat times the size of the previous visual // operation. if (!vim.visualMode) { + cm.on('mousedown', exitVisualMode); vim.visualMode = true; vim.visualLine = !!actionArgs.linewise; if (vim.visualLine) { @@ -1692,7 +1689,7 @@ // mode instead of exiting visual mode. vim.visualLine = false; } else { - exitVisualMode(cm, vim); + exitVisualMode(cm); } } updateMark(cm, vim, '<', cursorIsBefore(curStart, curEnd) ? curStart @@ -1832,7 +1829,7 @@ cm.replaceRange(replaceWithStr, curStart, curEnd); if(vim.visualMode){ cm.setCursor(curStart); - exitVisualMode(cm,vim); + exitVisualMode(cm); }else{ cm.setCursor(offsetCursor(curEnd, 0, -1)); } @@ -1989,7 +1986,9 @@ return s.replace(/([.?*+$\[\]\/\\(){}|\-])/g, '\\$1'); } - function exitVisualMode(cm, vim) { + function exitVisualMode(cm) { + cm.off('mousedown', exitVisualMode); + var vim = cm.vimState; vim.visualMode = false; vim.visualLine = false; var selectionStart = cm.getCursor('anchor'); @@ -2842,7 +2841,7 @@ processCommand: function(cm, input) { var vim = getVimState(cm); if (vim.visualMode) { - exitVisualMode(cm, vim); + exitVisualMode(cm); } var inputStream = new CodeMirror.StringStream(input); var params = {}; diff --git a/test/vim_test.js b/test/vim_test.js index 47fc6cd14f..44639c72c8 100644 --- a/test/vim_test.js +++ b/test/vim_test.js @@ -296,8 +296,10 @@ testMotion('l', 'l', makeCursor(0, 1)); testMotion('l_repeat', ['2', 'l'], makeCursor(0, 2)); testMotion('j', 'j', offsetCursor(word1.end, 1, 0), word1.end); testMotion('j_repeat', ['2', 'j'], offsetCursor(word1.end, 2, 0), word1.end); +testMotion('j_repeat_clip', ['1000', 'j'], endOfDocument); testMotion('k', 'k', offsetCursor(word3.end, -1, 0), word3.end); testMotion('k_repeat', ['2', 'k'], makeCursor(0, 4), makeCursor(2, 4)); +testMotion('k_repeat_clip', ['1000', 'k'], makeCursor(0, 4), makeCursor(2, 4)); testMotion('w', 'w', word1.start); testMotion('w_multiple_newlines_no_space', 'w', makeCursor(12, 2), makeCursor(11, 2)); testMotion('w_multiple_newlines_with_space', 'w', makeCursor(14, 0), makeCursor(12, 51)); @@ -818,12 +820,6 @@ testVim('dd_lastline', function(cm, vim, helpers) { eq(expectedLineCount, cm.lineCount()); helpers.assertCursorAt(cm.lineCount() - 1, 0); }); -testVim('cw', function(cm, vim, helpers) { - cm.setCursor(0, 0); - helpers.doKeys('c', '2', 'w'); - eq(' word3', cm.getValue()); - helpers.assertCursorAt(0, 0); -}, { value: 'word1 word2 word3'}); // Yank commands should behave the exact same as d commands, expect that nothing // gets deleted. testVim('yw_repeat', function(cm, vim, helpers) { @@ -854,6 +850,12 @@ testVim('yy_multiply_repeat', function(cm, vim, helpers) { // Change commands behave like d commands except that it also enters insert // mode. In addition, when the change is linewise, an additional newline is // inserted so that insert mode starts on that line. +testVim('cw', function(cm, vim, helpers) { + cm.setCursor(0, 0); + helpers.doKeys('c', '2', 'w'); + eq(' word3', cm.getValue()); + helpers.assertCursorAt(0, 0); +}, { value: 'word1 word2 word3'}); testVim('cw_repeat', function(cm, vim, helpers) { // Assert that cw does delete newline if it should go to the next line, and // that repeat works properly. @@ -877,9 +879,14 @@ testVim('cc_multiply_repeat', function(cm, vim, helpers) { var register = helpers.getRegisterController().getRegister(); eq(expectedBuffer, register.text); is(register.linewise); - helpers.assertCursorAt(0, lines[0].textStart); eq('vim-insert', cm.getOption('keyMap')); }); +testVim('cc_append', function(cm, vim, helpers) { + var expectedLineCount = cm.lineCount(); + cm.setCursor(cm.lastLine(), 0); + helpers.doKeys('c', 'c'); + eq(expectedLineCount, cm.lineCount()); +}); // Swapcase commands edit in place and do not modify registers. testVim('g~w_repeat', function(cm, vim, helpers) { // Assert that dw does delete newline if it should go to the next line, and @@ -1471,6 +1478,10 @@ testVim('visual_join', function(cm, vim, helpers) { helpers.doKeys('l', 'V', 'l', 'j', 'j', 'J'); eq(' 1 2 3\n 4\n 5', cm.getValue()); }, { value: ' 1\n 2\n 3\n 4\n 5' }); +testVim('visual_blank', function(cm, vim, helpers) { + helpers.doKeys('v', 'k'); + eq(vim.visualMode, true); +}, { value: '\n' }); testVim('/ and n/N', function(cm, vim, helpers) { cm.openDialog = helpers.fakeOpenDialog('match'); helpers.doKeys('/'); From 44cc08d6610783f9be8c98310b221ed684f721a9 Mon Sep 17 00:00:00 2001 From: Marijn Haverbeke Date: Fri, 21 Jun 2013 10:05:41 +0200 Subject: [PATCH 1254/5780] Fix bug in addLineWidget / insertAt implementation (f674dd148c27c882d3a85885caa9c27bb5638ee5) --- lib/codemirror.js | 6 +++--- 1 file changed, 3 insertions(+), 3 deletions(-) diff --git a/lib/codemirror.js b/lib/codemirror.js index ea3f8d9328..986f1640c8 100644 --- a/lib/codemirror.js +++ b/lib/codemirror.js @@ -4035,7 +4035,7 @@ window.CodeMirror = (function() { // LINE WIDGETS var LineWidget = CodeMirror.LineWidget = function(cm, node, options) { - for (var opt in options) if (options.hasOwnProperty(opt)) + if (options) for (var opt in options) if (options.hasOwnProperty(opt)) this[opt] = options[opt]; this.cm = cm; this.node = node; @@ -4079,8 +4079,8 @@ window.CodeMirror = (function() { if (widget.noHScroll) cm.display.alignWidgets = true; changeLine(cm, handle, function(line) { var widgets = line.widgets || (line.widgets = []); - if (options.insertAt == null) widgets.push(widget); - else widgets.splice(Math.max(widgets.length - 1, options.insertAt), 0, widget); + if (widget.insertAt == null) widgets.push(widget); + else widgets.splice(Math.max(widgets.length - 1, widget.insertAt), 0, widget); widget.line = line; if (!lineIsHidden(cm.doc, line) || widget.showIfHidden) { var aboveVisible = heightAtLine(cm, line) < cm.display.scroller.scrollTop; From 41deb2c055ba2efee8b5ee62b740df4582390827 Mon Sep 17 00:00:00 2001 From: Marijn Haverbeke Date: Mon, 24 Jun 2013 10:24:02 +0200 Subject: [PATCH 1255/5780] [matchtags addon] Add Issue #1625 --- addon/edit/matchtags.js | 31 +++++++++++++++++++++++++++++++ addon/fold/xml-fold.js | 15 ++++++++++----- demo/matchtags.html | 37 +++++++++++++++++++++++++++++++++++++ doc/manual.html | 11 +++++++++++ 4 files changed, 89 insertions(+), 5 deletions(-) create mode 100644 addon/edit/matchtags.js create mode 100644 demo/matchtags.html diff --git a/addon/edit/matchtags.js b/addon/edit/matchtags.js new file mode 100644 index 0000000000..8cdc8b20c8 --- /dev/null +++ b/addon/edit/matchtags.js @@ -0,0 +1,31 @@ +(function() { + "use strict"; + + CodeMirror.defineOption("matchTags", false, function(cm, val, old) { + if (old && old != CodeMirror.Init) + cm.off("cursorActivity", doMatchTags); + if (val) + cm.on("cursorActivity", doMatchTags); + }); + + function doMatchTags(cm) { + cm.operation(function() { + if (cm.state.matchedTags) { cm.state.matchedTags(); cm.state.matchedTags = null; } + + var cur = cm.getCursor(); + var match = CodeMirror.findMatchingTag(cm, cur) || CodeMirror.findEnclosingTag(cm, cur); + if (!match) return; + var one = cm.markText(match.open.from, match.open.to, {className: "CodeMirror-matchingbracket"}); + var two = cm.markText(match.close.from, match.close.to, {className: "CodeMirror-matchingbracket"}); + cm.state.matchedTags = function() { one.clear(); two.clear(); }; + }); + } + + CodeMirror.commands.toMatchingTag = function(cm) { + var found = CodeMirror.findMatchingTag(cm, cm.getCursor()); + if (found) { + var other = found.at == "close" ? found.open : found.close; + cm.setSelection(other.to, other.from); + } + }; +})(); diff --git a/addon/fold/xml-fold.js b/addon/fold/xml-fold.js index 29d730eb71..572feaa244 100644 --- a/addon/fold/xml-fold.js +++ b/addon/fold/xml-fold.js @@ -2,6 +2,7 @@ "use strict"; var Pos = CodeMirror.Pos; + function cmp(a, b) { return a.line - b.line || a.ch - b.ch; } var nameStartChar = "A-Z_a-z\\u00C0-\\u00D6\\u00D8-\\u00F6\\u00F8-\\u02FF\\u0370-\\u037D\\u037F-\\u1FFF\\u200C-\\u200D\\u2070-\\u218F\\u2C00-\\u2FEF\\u3001-\\uD7FF\\uF900-\\uFDCF\\uFDF0-\\uFFFD"; var nameChar = nameStartChar + "\-\:\.0-9\\u00B7\\u0300-\\u036F\\u203F-\\u2040"; @@ -136,14 +137,18 @@ CodeMirror.findMatchingTag = function(cm, pos) { var iter = new Iter(cm, pos.line, pos.ch); - var end = toTagEnd(iter), start = toTagStart(iter); - if (!end || end == "selfClose" || !start) return; + var end = toTagEnd(iter), to = end && Pos(iter.line, iter.ch); + var start = end && toTagStart(iter); + if (!end || end == "selfClose" || !start || cmp(iter, pos) > 0) return; + var here = {from: Pos(iter.line, iter.ch), to: to, tag: start[2]}; if (start[1]) { // closing tag - return findMatchingOpen(iter, start[2]); + var open = findMatchingOpen(iter, start[2]); + return open && {open: open, close: here, at: "close"}; } else { // opening tag - toTagEnd(iter); - return findMatchingClose(iter, start[2]); + iter = new Iter(cm, to.line, to.ch); + var close = findMatchingClose(iter, start[2]); + return close && {open: here, close: close, at: "open"}; } }; diff --git a/demo/matchtags.html b/demo/matchtags.html new file mode 100644 index 0000000000..053c0ed667 --- /dev/null +++ b/demo/matchtags.html @@ -0,0 +1,37 @@ + + + + + CodeMirror: Tag Matcher Demo + + + + + + + + + + +

    CodeMirror: Tag Matcher Demo

    + +
    + + + +

    Put the cursor on or inside a pair of tags to highlight them. + Press Ctrl-J to jump to the tag that matches the one under the + cursor.

    + + diff --git a/doc/manual.html b/doc/manual.html index 68692e2956..4284535f3f 100644 --- a/doc/manual.html +++ b/doc/manual.html @@ -1659,6 +1659,17 @@

    Add-ons

    them, should have the second character also moved to its own line.
    Demo here.
    +
    edit/matchtags.js
    +
    Defines an option matchTags that, when enabled, + will cause the tags around the cursor to be highlighted (using + the CodeMirror-matchingbrackets class). Also + defines + a command toMatchingTag, + which you can bind a key to in order to jump to the tag mathing + the one under the cursor. Depends on + the addon/fold/xml-fold.js + addon. Demo here.
    +
    edit/trailingspace.js
    Adds an option showTrailingSpace which, when enabled, adds the CSS class cm-trailingspace to From 493bd95ca309958ac3e1335a704893decc77a92d Mon Sep 17 00:00:00 2001 From: Marijn Haverbeke Date: Mon, 24 Jun 2013 10:37:24 +0200 Subject: [PATCH 1256/5780] [real-world uses] Add Echoplexus and Shadertoy --- doc/realworld.html | 2 ++ 1 file changed, 2 insertions(+) diff --git a/doc/realworld.html b/doc/realworld.html index 98aaff989b..6e76980b4a 100644 --- a/doc/realworld.html +++ b/doc/realworld.html @@ -47,6 +47,7 @@

    { } CodeMi
  • CSSDeck (CSS showcase)
  • Deck.js integration (slides with editors)
  • DbNinja (MySQL access interface)
  • +
  • Echoplexus (chat and collaborative coding)
  • Elm language examples
  • Eloquent JavaScript (book)
  • Emmet (fast XML editing)
  • @@ -89,6 +90,7 @@

    { } CodeMi
  • Quivive File Manager
  • Rascal (tiny computer)
  • RealTime.io (Internet-of-Things infrastructure)
  • +
  • Shadertoy (shader sharing)
  • sketchPatch Livecodelab
  • Skulpt (in-browser Python environment)
  • Snippets.pro (code snippet sharing)
  • From a48327868c691c94841f635442330603a9ca4bf4 Mon Sep 17 00:00:00 2001 From: Drew Bratcher Date: Sun, 23 Jun 2013 22:49:37 -0700 Subject: [PATCH 1257/5780] [jade mode] Add --- mode/jade/index.html | 54 +++++++++++++++++++++++++++ mode/jade/jade.js | 88 ++++++++++++++++++++++++++++++++++++++++++++ 2 files changed, 142 insertions(+) create mode 100644 mode/jade/index.html create mode 100644 mode/jade/jade.js diff --git a/mode/jade/index.html b/mode/jade/index.html new file mode 100644 index 0000000000..0e68ba8ac4 --- /dev/null +++ b/mode/jade/index.html @@ -0,0 +1,54 @@ + + + + + CodeMirror: Jade Templating Mode + + + + + + + +

    CodeMirror: Jade Templating Mode

    + + +

    The Jade Templating Mode

    +

    Created by Drew Bratcher. Managed as part of an Adobe Brackets extension at https://github.com/dbratcher/brackets-jade.

    +

    MIME type defined: text/x-jade.

    + + diff --git a/mode/jade/jade.js b/mode/jade/jade.js new file mode 100644 index 0000000000..2c62632f1e --- /dev/null +++ b/mode/jade/jade.js @@ -0,0 +1,88 @@ +CodeMirror.defineMode("jade", function () { + var symbol_regex1 = /^(?:~|!|%|\^|\*|\+|=|\\|:|;|,|\/|\?|&|<|>|\|)/; + var open_paren_regex = /^(\(|\[)/; + var close_paren_regex = /^(\)|\])/; + var keyword_regex1 = /^(if|else|return|var|function|include|doctype|each)/; + var keyword_regex2 = /^(#|{|}|\.)/; + var keyword_regex3 = /^(in)/; + var html_regex1 = /^(html|head|title|meta|link|script|body|br|div|input|span|a|img)/; + var html_regex2 = /^(h1|h2|h3|h4|h5|p|strong|em)/; + return { + startState: function () { + return { + inString: false, + stringType: "", + beforeTag: true, + justMatchedKeyword: false, + afterParen: false + }; + }, + token: function (stream, state) { + //check for state changes + if (!state.inString && ((stream.peek() == '"') || (stream.peek() == "'"))) { + state.stringType = stream.peek(); + stream.next(); // Skip quote + state.inString = true; // Update state + } + + //return state + if (state.inString) { + if (stream.skipTo(state.stringType)) { // Quote found on this line + stream.next(); // Skip quote + state.inString = false; // Clear flag + } else { + stream.skipToEnd(); // Rest of line is string + } + state.justMatchedKeyword = false; + return "string"; // Token style + } else if(stream.sol()) { + stream.eatSpace(); + if(stream.match(keyword_regex1)) { + state.justMatchedKeyword = true; + stream.eatSpace(); + return "keyword"; + } + if(stream.match(html_regex1) || stream.match(html_regex2)) { + state.justMatchedKeyword = true; + return "variable"; + } + return null; + } else if(stream.eatSpace()) { + state.justMatchedKeyword = false; + if(stream.match(keyword_regex3) && stream.eatSpace()) { + state.justMatchedKeyword = true; + return "keyword"; + } + return null; + } else if(stream.match(symbol_regex1)) { + state.justMatchedKeyword = false; + return "atom"; + } else if(stream.match(open_paren_regex)) { + state.afterParen = true; + state.justMatchedKeyword = true; + return "def"; + } else if(stream.match(close_paren_regex)) { + state.afterParen = false; + state.justMatchedKeyword = true; + return "def"; + } else if(stream.match(keyword_regex2)) { + state.justMatchedKeyword = true; + return "keyword"; + } else if(stream.eatSpace()) { + state.justMatchedKeyword = false; + return null; + } else { + stream.next(); + if(state.justMatchedKeyword){ + return "property"; + } else if(state.afterParen) { + return "property"; + } + return null; + } + } + }; +}); + +CodeMirror.defineMIME('text/x-jade', 'jade'); + From 8942221a52622b750f1d1c52c857aef05c85effc Mon Sep 17 00:00:00 2001 From: Marijn Haverbeke Date: Mon, 24 Jun 2013 10:41:22 +0200 Subject: [PATCH 1258/5780] [jade mode] Integrate --- doc/compress.html | 1 + doc/modes.html | 1 + mode/meta.js | 1 + 3 files changed, 3 insertions(+) diff --git a/doc/compress.html b/doc/compress.html index fede6f43fa..8b7a11d670 100644 --- a/doc/compress.html +++ b/doc/compress.html @@ -93,6 +93,7 @@

    { } CodeMi + diff --git a/doc/modes.html b/doc/modes.html index af798aa774..69c7d5e9ad 100644 --- a/doc/modes.html +++ b/doc/modes.html @@ -45,6 +45,7 @@

    { } CodeMi
  • HTML mixed-mode
  • HTTP
  • Java
  • +
  • Jade
  • JavaScript
  • Jinja2
  • LESS
  • diff --git a/mode/meta.js b/mode/meta.js index e8cfc8fb63..cb1051e68f 100644 --- a/mode/meta.js +++ b/mode/meta.js @@ -26,6 +26,7 @@ CodeMirror.modeInfo = [ {name: 'JavaServer Pages', mime: 'application/x-jsp', mode: 'htmlembedded'}, {name: 'HTML', mime: 'text/html', mode: 'htmlmixed'}, {name: 'HTTP', mime: 'message/http', mode: 'http'}, + {name: 'Jade', mime: 'text/x-jade', mode: 'jade'}, {name: 'JavaScript', mime: 'text/javascript', mode: 'javascript'}, {name: 'JSON', mime: 'application/x-json', mode: 'javascript'}, {name: 'JSON', mime: 'application/json', mode: 'javascript'}, From a4cbc645611017015594fe5e8f9738b27d1c571c Mon Sep 17 00:00:00 2001 From: Marijn Haverbeke Date: Mon, 24 Jun 2013 12:45:42 +0200 Subject: [PATCH 1259/5780] [tern addon] Support custom type/completion tips + a few other fixes --- addon/tern/tern.js | 83 ++++++++++++++++++++++++---------------------- demo/tern.html | 15 ++++++++- 2 files changed, 58 insertions(+), 40 deletions(-) diff --git a/addon/tern/tern.js b/addon/tern/tern.js index 662736637c..5cff75782b 100644 --- a/addon/tern/tern.js +++ b/addon/tern/tern.js @@ -16,23 +16,16 @@ // multi-file view, switch the view or focus to the named file. // * showError: A function(editor, message) that can be used to // override the way errors are displayed. +// * completionTip: Customize the content in tooltips for completions. +// Is passed a single argument—the completion's data as returned by +// Tern—and may return a string, DOM node, or null to indicate that +// no tip should be shown. By default the docstring is shown. +// * typeTip: Like completionTip, but for the tooltips shown for type +// queries. (function() { "use strict"; - CodeMirror.getURL = function(url, c) { - var xhr = new XMLHttpRequest(); - xhr.open("get", url, true); - xhr.send(); - xhr.onreadystatechange = function() { - if (xhr.readyState != 4) return; - if (xhr.status < 400) return c(null, xhr.responseText); - var e = new Error(xhr.responseText || "No response"); - e.status = xhr.status; - c(e); - }; - }; - CodeMirror.TernServer = function(options) { var self = this; this.options = options || {}; @@ -62,7 +55,7 @@ delDoc: function(name) { var found = this.docs[name]; if (!found) return; - CodeMirror.on(found.doc, "change", this.trackChange); + CodeMirror.off(found.doc, "change", this.trackChange); delete this.docs[name]; this.server.delFile(name); }, @@ -72,6 +65,8 @@ CodeMirror.showHint(cm, function(cm, c) { return hint(self, cm, c); }, {async: true}); }, + getHint: function(cm, c) { return hint(this, cm, c); }, + showType: function(cm) { showType(this, cm); }, updateArgHints: function(cm) { updateArgHints(this, cm); }, @@ -80,7 +75,11 @@ jumpBack: function(cm) { jumpBack(this, cm); }, - rename: function(cm) { rename(this, cm); } + rename: function(cm) { rename(this, cm); }, + + request: function(cm, query, c) { + this.server.request(buildRequest(this, findDoc(this, cm.getDoc()), query), c); + } }; var Pos = CodeMirror.Pos; @@ -139,10 +138,7 @@ // Completion function hint(ts, cm, c) { - var doc = findDoc(ts, cm.getDoc()); - var req = buildRequest(ts, doc, {type: "completions", types: true, docs: true}); - - ts.server.request(req, function(error, data) { + ts.request(cm, {type: "completions", types: true, docs: true}, function(error, data) { if (error) return showError(ts, cm, error); var completions = [], after = ""; var from = data.start, to = data.end; @@ -156,7 +152,7 @@ completions.push({text: completion.name + after, displayText: completion.name, className: className, - doc: completion.doc}); + data: completion}); } var obj = {from: from, to: to, list: completions}; @@ -164,9 +160,10 @@ CodeMirror.on(obj, "close", function() { remove(tooltip); }); CodeMirror.on(obj, "select", function(cur, node) { remove(tooltip); - if (cur.doc) { + var content = ts.options.completionTip ? ts.options.completionTip(cur.data) : cur.data.doc; + if (content) { tooltip = makeTooltip(node.parentNode.getBoundingClientRect().right + window.pageXOffset, - node.getBoundingClientRect().top + window.pageYOffset, cur.doc); + node.getBoundingClientRect().top + window.pageYOffset, content); tooltip.className += " " + cls + "hint-doc"; } }); @@ -187,15 +184,18 @@ // Type queries function showType(ts, cm) { - var doc = findDoc(ts, cm.getDoc()); - ts.server.request(buildRequest(ts, doc, "type"), function(error, data) { + ts.request(cm, "type", function(error, data) { if (error) return showError(ts, cm, error); - var tip = elt("span", null, elt("strong", null, data.type || "not found")); - if (data.doc) - tip.appendChild(document.createTextNode(" — " + data.doc)); - if (data.url) { - tip.appendChild(document.createTextNode(" ")); - tip.appendChild(elt("a", null, "[docs]")).href = data.url; + if (ts.options.typeTip) { + var tip = ts.options.typeTip(data); + } else { + var tip = elt("span", null, elt("strong", null, data.type || "not found")); + if (data.doc) + tip.appendChild(document.createTextNode(" — " + data.doc)); + if (data.url) { + tip.appendChild(document.createTextNode(" ")); + tip.appendChild(elt("a", null, "[docs]")).href = data.url; + } } tempTooltip(cm, tip); }); @@ -204,7 +204,7 @@ // Maintaining argument hints function updateArgHints(ts, cm) { - if (ts.activeArgHints) { remove(ts.activeArgHints); ts.activeArgHints = null; } + closeArgHints(ts); if (cm.somethingSelected()) return; var lex = cm.getTokenAt(cm.getCursor()).state.lexical; @@ -220,8 +220,7 @@ if (cache && cache.doc == cm.getDoc() && cmpPos(start, cache.start) == 0) return showArgHints(ts, cm, pos); - var query = {type: "type", preferFunction: true, end: start}; - ts.server.request(buildRequest(ts, findDoc(ts, cm.getDoc()), query), function(error, data) { + ts.request(cm, {type: "type", preferFunction: true, end: start}, function(error, data) { if (error || !data.type || !(/^fn\(/).test(data.type)) return; ts.cachedArgHints = { start: pos, @@ -235,7 +234,7 @@ } function showArgHints(ts, cm, pos) { - if (ts.activeArgHints) { remove(ts.activeArgHints); ts.activeArgHints = null; } + closeArgHints(ts); var cache = ts.cachedArgHints, tp = cache.type; var tip = elt("span", cache.guess ? cls + "fhint-guess" : null, @@ -324,8 +323,10 @@ function moveTo(ts, curDoc, doc, start, end) { doc.doc.setSelection(end, start); - if (curDoc != doc && ts.options.switchToDoc) + if (curDoc != doc && ts.options.switchToDoc) { + closeArgHints(ts); ts.options.switchToDoc(doc.name); + } } // The {line,ch} representation of positions makes this rather awkward. @@ -371,8 +372,7 @@ var token = cm.getTokenAt(cm.getCursor()); if (!/\w/.test(token.string)) showError(ts, cm, "Not at a variable"); dialog(cm, "New name for " + token.string, function(newName) { - var req = {type: "rename", newName: newName}, doc = findDoc(ts, cm.getDoc()); - ts.server.request(buildRequest(ts, doc, req, false), function(error, data) { + ts.request(cm, {type: "rename", newName: newName, fullDocs: true}, function(error, data) { if (error) return showError(ts, cm, error); applyChanges(ts, data.changes); }); @@ -400,8 +400,9 @@ // Generic request-building helper - function buildRequest(ts, doc, query, allowFragments) { - var files = [], offsetLines = 0; + function buildRequest(ts, doc, query) { + var files = [], offsetLines = 0, allowFragments = !query.fullDocs; + if (!allowFragments) delete query.fullDocs; if (typeof query == "string") query = {type: query}; query.lineCharPositions = true; if (query.end == null) { @@ -528,4 +529,8 @@ else tempTooltip(cm, String(msg)); } + + function closeArgHints(ts) { + if (ts.activeArgHints) { remove(ts.activeArgHints); ts.activeArgHints = null; } + } })(); diff --git a/demo/tern.html b/demo/tern.html index 22464f8a60..42c2b1a7bb 100644 --- a/demo/tern.html +++ b/demo/tern.html @@ -71,8 +71,21 @@

    CodeMirror: Tern Demo

    diff --git a/keymap/vim.js b/keymap/vim.js index 8c942a3ff5..977642ea38 100644 --- a/keymap/vim.js +++ b/keymap/vim.js @@ -206,8 +206,7 @@ // Operators { keys: ['d'], type: 'operator', operator: 'delete' }, { keys: ['y'], type: 'operator', operator: 'yank' }, - { keys: ['c'], type: 'operator', operator: 'change', - operatorArgs: { enterInsertMode: true } }, + { keys: ['c'], type: 'operator', operator: 'change' }, { keys: ['>'], type: 'operator', operator: 'indent', operatorArgs: { indentRight: true }}, { keys: ['<'], type: 'operator', operator: 'indent', @@ -231,7 +230,7 @@ motion: 'moveToEol', motionArgs: { inclusive: true }, operatorMotionArgs: { visualLine: true }}, { keys: ['C'], type: 'operatorMotion', - operator: 'change', operatorArgs: { enterInsertMode: true }, + operator: 'change', motion: 'moveToEol', motionArgs: { inclusive: true }, operatorMotionArgs: { visualLine: true }}, { keys: ['~'], type: 'operatorMotion', operator: 'swapcase', @@ -315,6 +314,27 @@ ]; var Vim = function() { + CodeMirror.defineOption('vimMode', false, function(cm, val) { + if (val) { + cm.setOption('keyMap', 'vim'); + cm.on('beforeSelectionChange', beforeSelectionChange); + maybeInitVimState(cm); + } else if (cm.state.vim) { + cm.setOption('keyMap', 'default'); + cm.off('beforeSelectionChange', beforeSelectionChange); + cm.state.vim = null; + } + }); + function beforeSelectionChange(cm, cur) { + var vim = cm.state.vim; + if (vim.insertMode || vim.exMode) return; + + var head = cur.head; + if (head.ch && head.ch == cm.doc.getLine(head.line).length) { + head.ch--; + } + } + var numberRegex = /[\d]/; var wordRegexp = [(/\w/), (/[^\w\s]/)], bigWordRegexp = [(/\S/)]; function makeKeyRange(start, size) { @@ -450,28 +470,11 @@ }; }; - // Global Vim state. Call getVimGlobalState to get and initialize. - var vimGlobalState; - function getVimGlobalState() { - if (!vimGlobalState) { - vimGlobalState = { - // The current search query. - searchQuery: null, - // Whether we are searching backwards. - searchIsReversed: false, - jumpList: createCircularJumpList(), - macroModeState: createMacroState(), - // Recording latest f, t, F or T motion command. - lastChararacterSearch: {increment:0, forward:true, selectedCharacter:''}, - registerController: new RegisterController({}) - }; - } - return vimGlobalState; - } - function getVimState(cm) { - if (!cm.vimState) { + + function maybeInitVimState(cm) { + if (!cm.state.vim) { // Store instance state in the CodeMirror object. - cm.vimState = { + cm.state.vim = { inputState: new InputState(), // Vim's input state that triggered the last edit, used to repeat // motions and operators with '.'. @@ -500,7 +503,21 @@ visualLine: false }; } - return cm.vimState; + return cm.state.vim; + } + var vimGlobalState; + function resetVimGlobalState() { + vimGlobalState = { + // The current search query. + searchQuery: null, + // Whether we are searching backwards. + searchIsReversed: false, + jumpList: createCircularJumpList(), + macroModeState: createMacroState(), + // Recording latest f, t, F or T motion command. + lastChararacterSearch: {increment:0, forward:true, selectedCharacter:''}, + registerController: new RegisterController({}) + }; } var vimApi= { @@ -510,16 +527,19 @@ // Testing hook, though it might be useful to expose the register // controller anyways. getRegisterController: function() { - return getVimGlobalState().registerController; + return vimGlobalState.registerController; }, // Testing hook. - clearVimGlobalState_: function() { - vimGlobalState = null; - }, + resetVimGlobalState_: resetVimGlobalState, + // Testing hook. getVimGlobalState_: function() { return vimGlobalState; }, + + // Testing hook. + maybeInitVimState_: maybeInitVimState, + InsertModeKey: InsertModeKey, map: function(lhs, rhs) { // Add user defined key bindings. @@ -532,17 +552,12 @@ exCommands[name]=func; exCommandDispatcher.commandMap_[prefix]={name:name, shortName:prefix, type:'api'}; }, - // Initializes vim state variable on the CodeMirror object. Should only be - // called lazily by handleKey or for testing. - maybeInitState: function(cm) { - getVimState(cm); - }, // This is the outermost function called by CodeMirror, after keys have // been mapped to their Vim equivalents. handleKey: function(cm, key) { var command; - var vim = getVimState(cm); - var macroModeState = getVimGlobalState().macroModeState; + var vim = maybeInitVimState(cm); + var macroModeState = vimGlobalState.macroModeState; if (macroModeState.enteredMacroMode) { if (key == 'q') { actions.exitMacroRecordMode(); @@ -967,7 +982,7 @@ // cachedCursor is used to save the old position of the cursor // when * or # causes vim to seek for the nearest word and shift // the cursor before entering the motion. - getVimGlobalState().jumpList.cachedCursor = cm.getCursor(); + vimGlobalState.jumpList.cachedCursor = cm.getCursor(); cm.setCursor(word.start); handleQuery(query, true /** ignoreCase */, false /** smartCase */); @@ -1049,7 +1064,7 @@ return; } if (motionArgs.toJumplist) { - var jumpList = getVimGlobalState().jumpList; + var jumpList = vimGlobalState.jumpList; // if the current motion is # or *, use cachedCursor var cachedCursor = jumpList.cachedCursor; if (cachedCursor) { @@ -1145,13 +1160,10 @@ if (vim.visualMode) { exitVisualMode(cm); } - if (operatorArgs.enterInsertMode) { - actions.enterInsertMode(cm, {}, vim); - } } }, recordLastEdit: function(vim, inputState, actionCommand) { - var macroModeState = getVimGlobalState().macroModeState; + var macroModeState = vimGlobalState.macroModeState; if (macroModeState.inReplay) { return; } vim.lastEditInputState = inputState; vim.lastEditActionCommand = actionCommand; @@ -1458,7 +1470,7 @@ return [start, end]; }, repeatLastCharacterSearch: function(cm, motionArgs) { - var lastSearch = getVimGlobalState().lastChararacterSearch; + var lastSearch = vimGlobalState.lastChararacterSearch; var repeat = motionArgs.repeat; var forward = motionArgs.forward === lastSearch.forward; var increment = (lastSearch.increment ? 1 : 0) * (forward ? -1 : 1); @@ -1476,7 +1488,7 @@ var operators = { change: function(cm, operatorArgs, _vim, curStart, curEnd) { - getVimGlobalState().registerController.pushText( + vimGlobalState.registerController.pushText( operatorArgs.registerName, 'change', cm.getRange(curStart, curEnd), operatorArgs.linewise); if (operatorArgs.linewise) { @@ -1497,6 +1509,7 @@ } cm.replaceRange('', curStart, curEnd); } + actions.enterInsertMode(cm, {}, cm.state.vim); cm.setCursor(curStart); }, // delete is a javascript keyword. @@ -1508,7 +1521,7 @@ curStart.line--; curStart.ch = lineLength(cm, curStart.line); } - getVimGlobalState().registerController.pushText( + vimGlobalState.registerController.pushText( operatorArgs.registerName, 'delete', cm.getRange(curStart, curEnd), operatorArgs.linewise); cm.replaceRange('', curStart, curEnd); @@ -1550,7 +1563,7 @@ cm.setCursor(curOriginal); }, yank: function(cm, operatorArgs, _vim, curStart, curEnd, curOriginal) { - getVimGlobalState().registerController.pushText( + vimGlobalState.registerController.pushText( operatorArgs.registerName, 'yank', cm.getRange(curStart, curEnd), operatorArgs.linewise); cm.setCursor(curOriginal); @@ -1564,7 +1577,7 @@ } var repeat = actionArgs.repeat; var forward = actionArgs.forward; - var jumpList = getVimGlobalState().jumpList; + var jumpList = vimGlobalState.jumpList; var mark = jumpList.move(cm, forward ? repeat : -repeat); var markPos = mark ? mark.find() : undefined; @@ -1590,7 +1603,7 @@ replayMacro: function(cm, actionArgs) { var registerName = actionArgs.selectedCharacter; var repeat = actionArgs.repeat; - var macroModeState = getVimGlobalState().macroModeState; + var macroModeState = vimGlobalState.macroModeState; if (registerName == '@') { registerName = macroModeState.latestRegister; } @@ -1600,13 +1613,13 @@ } }, exitMacroRecordMode: function() { - var macroModeState = getVimGlobalState().macroModeState; + var macroModeState = vimGlobalState.macroModeState; macroModeState.toggle(); parseKeyBufferToRegister(macroModeState.latestRegister, macroModeState.macroKeyBuffer); }, enterMacroRecordMode: function(cm, actionArgs) { - var macroModeState = getVimGlobalState().macroModeState; + var macroModeState = vimGlobalState.macroModeState; var registerName = actionArgs.selectedCharacter; macroModeState.toggle(cm, registerName); emptyMacroKeyBuffer(macroModeState); @@ -1632,7 +1645,7 @@ } else { cm.setOption('keyMap', 'vim-insert'); } - if (!getVimGlobalState().macroModeState.inReplay) { + if (!vimGlobalState.macroModeState.inReplay) { // Only record if not replaying. cm.on('change', onChange); cm.on('cursorActivity', onCursorActivity); @@ -1725,6 +1738,7 @@ }); }, newLineAndEnterInsertMode: function(cm, actionArgs, vim) { + vim.insertMode = true; var insertAt = cm.getCursor(); if (insertAt.line === cm.firstLine() && !actionArgs.after) { // Special case for inserting newline before start of document. @@ -1743,7 +1757,7 @@ }, paste: function(cm, actionArgs) { var cur = cm.getCursor(); - var register = getVimGlobalState().registerController.getRegister( + var register = vimGlobalState.registerController.getRegister( actionArgs.registerName); if (!register.text) { return; @@ -1988,7 +2002,7 @@ function exitVisualMode(cm) { cm.off('mousedown', exitVisualMode); - var vim = cm.vimState; + var vim = cm.state.vim; vim.visualMode = false; vim.visualLine = false; var selectionStart = cm.getCursor('anchor'); @@ -2112,12 +2126,11 @@ function recordJumpPosition(cm, oldCur, newCur) { if(!cursorEqual(oldCur, newCur)) { - getVimGlobalState().jumpList.add(cm, oldCur, newCur); + vimGlobalState.jumpList.add(cm, oldCur, newCur); } } function recordLastCharacterSearch(increment, args) { - var vimGlobalState = getVimGlobalState(); vimGlobalState.lastChararacterSearch.increment = increment; vimGlobalState.lastChararacterSearch.forward = args.forward; vimGlobalState.lastChararacterSearch.selectedCharacter = args.selectedCharacter; @@ -2571,10 +2584,10 @@ function SearchState() {} SearchState.prototype = { getQuery: function() { - return getVimGlobalState().query; + return vimGlobalState.query; }, setQuery: function(query) { - getVimGlobalState().query = query; + vimGlobalState.query = query; }, getOverlay: function() { return this.searchOverlay; @@ -2583,14 +2596,14 @@ this.searchOverlay = overlay; }, isReversed: function() { - return getVimGlobalState().isReversed; + return vimGlobalState.isReversed; }, setReversed: function(reversed) { - getVimGlobalState().isReversed = reversed; + vimGlobalState.isReversed = reversed; } }; function getSearchState(cm) { - var vim = getVimState(cm); + var vim = cm.state.vim; return vim.searchState_ || (vim.searchState_ = new SearchState()); } function dialog(cm, template, shortText, onClose, options) { @@ -2839,7 +2852,7 @@ }; Vim.ExCommandDispatcher.prototype = { processCommand: function(cm, input) { - var vim = getVimState(cm); + var vim = cm.state.vim; if (vim.visualMode) { exitVisualMode(cm); } @@ -2920,7 +2933,7 @@ case '$': return cm.lastLine(); case '\'': - var mark = getVimState(cm).marks[inputStream.next()]; + var mark = cm.state.vim.marks[inputStream.next()]; if (mark && mark.find()) { return mark.find().line; } @@ -3030,7 +3043,7 @@ exCommandDispatcher.map(mapArgs[0], mapArgs[1], cm); }, move: function(cm, params) { - commandDispatcher.processCommand(cm, getVimState(cm), { + commandDispatcher.processCommand(cm, cm.state.vim, { type: 'motion', motion: 'moveToLineOrEdgeOfDocument', motionArgs: { forward: false, explicitRepeat: true, @@ -3185,7 +3198,7 @@ return; } - var state = getVimState(cm); + var state = cm.state.vim; var stream = new CodeMirror.StringStream(params.argString.trim()); while (!stream.eol()) { stream.eatSpace(); @@ -3256,6 +3269,7 @@ function doReplace(cm, confirm, lineStart, lineEnd, searchCursor, query, replaceWith) { // Set up all the functions. + cm.state.vim.exMode = true; var done = false; var lastPos = searchCursor.from(); function replaceAll() { @@ -3290,7 +3304,8 @@ cm.focus(); if (lastPos) { cm.setCursor(lastPos); - var vim = getVimState(cm); + var vim = cm.state.vim; + vim.exMode = false; vim.lastHPos = vim.lastHSPos = lastPos.ch; } } @@ -3402,9 +3417,8 @@ CodeMirror.keyMap.vim = buildVimKeyMap(); function exitInsertMode(cm) { - var vim = getVimState(cm); - vim.insertMode = false; - var inReplay = getVimGlobalState().macroModeState.inReplay; + var vim = cm.state.vim; + var inReplay = vimGlobalState.macroModeState.inReplay; if (!inReplay) { cm.off('change', onChange); cm.off('cursorActivity', onCursorActivity); @@ -3418,6 +3432,7 @@ } delete vim.insertModeRepeat; cm.setCursor(cm.getCursor().line, cm.getCursor().ch-1, true); + vim.insertMode = false; cm.setOption('keyMap', 'vim'); cm.toggleOverwrite(false); // exit replace mode if we were in it. } @@ -3445,7 +3460,7 @@ function parseRegisterToKeyBuffer(macroModeState, registerName) { var match, key; - var register = getVimGlobalState().registerController.getRegister(registerName); + var register = vimGlobalState.registerController.getRegister(registerName); var text = register.toString(); var macroKeyBuffer = macroModeState.macroKeyBuffer; emptyMacroKeyBuffer(macroModeState); @@ -3461,7 +3476,7 @@ function parseKeyBufferToRegister(registerName, keyBuffer) { var text = keyBuffer.join(''); - getVimGlobalState().registerController.setRegisterText(registerName, text); + vimGlobalState.registerController.setRegisterText(registerName, text); } function emptyMacroKeyBuffer(macroModeState) { @@ -3489,7 +3504,7 @@ * Should only be active in insert mode. */ function onChange(_cm, changeObj) { - var macroModeState = getVimGlobalState().macroModeState; + var macroModeState = vimGlobalState.macroModeState; var lastChange = macroModeState.lastInsertModeChanges; while (changeObj) { lastChange.expectCursorActivityForChange = true; @@ -3509,7 +3524,7 @@ * - Should only be active in insert mode. */ function onCursorActivity() { - var macroModeState = getVimGlobalState().macroModeState; + var macroModeState = vimGlobalState.macroModeState; var lastChange = macroModeState.lastInsertModeChanges; if (lastChange.expectCursorActivityForChange) { lastChange.expectCursorActivityForChange = false; @@ -3530,7 +3545,7 @@ * - For recording deletes in insert mode. */ function onKeyEventTargetKeyDown(e) { - var macroModeState = getVimGlobalState().macroModeState; + var macroModeState = vimGlobalState.macroModeState; var lastChange = macroModeState.lastInsertModeChanges; var keyName = CodeMirror.keyName(e); function onKeyFound() { @@ -3552,7 +3567,7 @@ * corresponding enterInsertMode call was made with a count. */ function repeatLastEdit(cm, vim, repeat, repeatForInsert) { - var macroModeState = getVimGlobalState().macroModeState; + var macroModeState = vimGlobalState.macroModeState; macroModeState.inReplay = true; var isAction = !!vim.lastEditActionCommand; var cachedInputState = vim.inputState; @@ -3620,6 +3635,7 @@ } } + resetVimGlobalState(); return vimApi; }; // Initialize Vim and make it available as an API. diff --git a/lib/codemirror.js b/lib/codemirror.js index f3f0efb640..e4f5e19349 100644 --- a/lib/codemirror.js +++ b/lib/codemirror.js @@ -4976,7 +4976,8 @@ window.CodeMirror = (function() { } function historyChangeFromChange(doc, change) { - var histChange = {from: change.from, to: changeEnd(change), text: getBetween(doc, change.from, change.to)}; + var from = { line: change.from.line, ch: change.from.ch }; + var histChange = {from: from, to: changeEnd(change), text: getBetween(doc, change.from, change.to)}; attachLocalSpans(doc, histChange, change.from.line, change.to.line + 1); linkedDocs(doc, function(doc) {attachLocalSpans(doc, histChange, change.from.line, change.to.line + 1);}, true); return histChange; diff --git a/test/vim_test.js b/test/vim_test.js index 44639c72c8..0e88243215 100644 --- a/test/vim_test.js +++ b/test/vim_test.js @@ -99,7 +99,7 @@ function copyCursor(cur) { function testVim(name, run, opts, expectedFail) { var vimOpts = { lineNumbers: true, - keyMap: 'vim', + vimMode: true, showCursorWhenSelecting: true, value: code }; @@ -111,8 +111,7 @@ function testVim(name, run, opts, expectedFail) { return test('vim_' + name, function() { var place = document.getElementById("testground"); var cm = CodeMirror(place, vimOpts); - CodeMirror.Vim.maybeInitState(cm); - var vim = cm.vimState; + var vim = CodeMirror.Vim.maybeInitVimState_(cm); function doKeysFn(cm) { return function(args) { @@ -140,7 +139,7 @@ function testVim(name, run, opts, expectedFail) { // Find key in keymap and handle. var handled = CodeMirror.lookupKey(key, ['vim-insert'], executeHandler); // Record for insert mode. - if (handled === true && cm.vimState.insertMode && arguments[i] != 'Esc') { + if (handled === true && cm.state.vim.insertMode && arguments[i] != 'Esc') { var lastChange = CodeMirror.Vim.getVimGlobalState_().macroModeState.lastInsertModeChanges; if (lastChange) { lastChange.changes.push(new CodeMirror.Vim.InsertModeKey(key)); @@ -184,7 +183,7 @@ function testVim(name, run, opts, expectedFail) { return CodeMirror.Vim.getRegisterController(); } } - CodeMirror.Vim.clearVimGlobalState_(); + CodeMirror.Vim.resetVimGlobalState_(); var successful = false; try { run(cm, vim, helpers); @@ -228,7 +227,7 @@ function testJumplist(name, keys, endPos, startPos, dialog) { endPos = makeCursor(endPos[0], endPos[1]); startPos = makeCursor(startPos[0], startPos[1]); testVim(name, function(cm, vim, helpers) { - CodeMirror.Vim.clearVimGlobalState_(); + CodeMirror.Vim.resetVimGlobalState_(); if(dialog)cm.openDialog = helpers.fakeOpenDialog('word'); cm.setCursor(startPos); helpers.doKeys.apply(null, keys); @@ -501,15 +500,13 @@ testVim('dl', function(cm, vim, helpers) { eqPos(curStart, cm.getCursor()); }, { value: ' word1 ' }); testVim('dl_eol', function(cm, vim, helpers) { - // TODO: This test is incorrect. The cursor should end up at (0, 5). - var curStart = makeCursor(0, 6); - cm.setCursor(curStart); + cm.setCursor(0, 6); helpers.doKeys('d', 'l'); eq(' word1', cm.getValue()); var register = helpers.getRegisterController().getRegister(); eq(' ', register.text); is(!register.linewise); - helpers.assertCursorAt(0, 6); + helpers.assertCursorAt(0, 5); }, { value: ' word1 ' }); testVim('dl_repeat', function(cm, vim, helpers) { var curStart = makeCursor(0, 0); @@ -594,38 +591,35 @@ testVim('dw_word', function(cm, vim, helpers) { testVim('dw_only_word', function(cm, vim, helpers) { // Test that if there is only 1 word left, dw deletes till the end of the // line. - var curStart = makeCursor(0, 1); - cm.setCursor(curStart); + cm.setCursor(0, 1); helpers.doKeys('d', 'w'); eq(' ', cm.getValue()); var register = helpers.getRegisterController().getRegister(); eq('word1 ', register.text); is(!register.linewise); - eqPos(curStart, cm.getCursor()); + helpers.assertCursorAt(0, 0); }, { value: ' word1 ' }); testVim('dw_eol', function(cm, vim, helpers) { // Assert that dw does not delete the newline if last word to delete is at end // of line. - var curStart = makeCursor(0, 1); - cm.setCursor(curStart); + cm.setCursor(0, 1); helpers.doKeys('d', 'w'); eq(' \nword2', cm.getValue()); var register = helpers.getRegisterController().getRegister(); eq('word1', register.text); is(!register.linewise); - eqPos(curStart, cm.getCursor()); + helpers.assertCursorAt(0, 0); }, { value: ' word1\nword2' }); testVim('dw_eol_with_multiple_newlines', function(cm, vim, helpers) { // Assert that dw does not delete the newline if last word to delete is at end // of line and it is followed by multiple newlines. - var curStart = makeCursor(0, 1); - cm.setCursor(curStart); + cm.setCursor(0, 1); helpers.doKeys('d', 'w'); eq(' \n\nword2', cm.getValue()); var register = helpers.getRegisterController().getRegister(); eq('word1', register.text); is(!register.linewise); - eqPos(curStart, cm.getCursor()); + helpers.assertCursorAt(0, 0); }, { value: ' word1\n\nword2' }); testVim('dw_empty_line_followed_by_whitespace', function(cm, vim, helpers) { cm.setCursor(0, 0); @@ -665,14 +659,13 @@ testVim('dw_end_of_document', function(cm, vim, helpers) { testVim('dw_repeat', function(cm, vim, helpers) { // Assert that dw does delete newline if it should go to the next line, and // that repeat works properly. - var curStart = makeCursor(0, 1); - cm.setCursor(curStart); + cm.setCursor(0, 1); helpers.doKeys('d', '2', 'w'); eq(' ', cm.getValue()); var register = helpers.getRegisterController().getRegister(); eq('word1\nword2', register.text); is(!register.linewise); - eqPos(curStart, cm.getCursor()); + helpers.assertCursorAt(0, 0); }, { value: ' word1\nword2' }); testVim('de_word_start_and_empty_lines', function(cm, vim, helpers) { cm.setCursor(0, 0); @@ -1001,14 +994,13 @@ testEdit('daW_end_punct', 'foo \tbAr.', /A/, 'daW', 'foo'); // Operator-motion tests testVim('D', function(cm, vim, helpers) { - var curStart = makeCursor(0, 3); - cm.setCursor(curStart); + cm.setCursor(0, 3); helpers.doKeys('D'); eq(' wo\nword2\n word3', cm.getValue()); var register = helpers.getRegisterController().getRegister(); eq('rd1', register.text); is(!register.linewise); - helpers.assertCursorAt(0, 3); + helpers.assertCursorAt(0, 2); }, { value: ' word1\nword2\n word3' }); testVim('C', function(cm, vim, helpers) { var curStart = makeCursor(0, 3); @@ -1018,7 +1010,7 @@ testVim('C', function(cm, vim, helpers) { var register = helpers.getRegisterController().getRegister(); eq('rd1', register.text); is(!register.linewise); - helpers.assertCursorAt(0, 3); + eqPos(curStart, cm.getCursor()); eq('vim-insert', cm.getOption('keyMap')); }, { value: ' word1\nword2\n word3' }); testVim('Y', function(cm, vim, helpers) { From ca6db50f5874b003f5d4daae23391c10dfa7d957 Mon Sep 17 00:00:00 2001 From: Marijn Haverbeke Date: Tue, 2 Jul 2013 13:58:49 +0200 Subject: [PATCH 1279/5780] Fix bug in findWordAt Which could result in a selection at a negative character offset when double-clicking. Issue #1638 --- lib/codemirror.js | 3 +-- 1 file changed, 1 insertion(+), 2 deletions(-) diff --git a/lib/codemirror.js b/lib/codemirror.js index e4f5e19349..1aa350a2df 100644 --- a/lib/codemirror.js +++ b/lib/codemirror.js @@ -1064,7 +1064,6 @@ window.CodeMirror = (function() { } function finishRect(rect) { rect.bottom = vranges[rect.top+1]; - if (isNaN(rect.bottom)) debugger; rect.top = vranges[rect.top]; } @@ -2757,7 +2756,7 @@ window.CodeMirror = (function() { function findWordAt(line, pos) { var start = pos.ch, end = pos.ch; if (line) { - if (pos.xRel < 0 || end == line.length) --start; else ++end; + if ((pos.xRel < 0 || end == line.length) && start) --start; else ++end; var startChar = line.charAt(start); var check = isWordChar(startChar) ? isWordChar : /\s/.test(startChar) ? function(ch) {return /\s/.test(ch);} From e0e2181cf0033ebb0f24c7909b0ce546d8c62edd Mon Sep 17 00:00:00 2001 From: Marijn Haverbeke Date: Tue, 2 Jul 2013 14:04:20 +0200 Subject: [PATCH 1280/5780] [real-world uses] Add Fiddle Salad --- doc/realworld.html | 1 + 1 file changed, 1 insertion(+) diff --git a/doc/realworld.html b/doc/realworld.html index 6e76980b4a..2150abe401 100644 --- a/doc/realworld.html +++ b/doc/realworld.html @@ -54,6 +54,7 @@

    { } CodeMi
  • Fastfig (online computation/math tool)
  • Farabi (modern Perl IDE)
  • FathomJS integration (slides with editors, again)
  • +
  • Fiddle Salad (web development environment)
  • Firepad (collaborative text editor)
  • Go language tour
  • GitHub's Android app
  • From 836932ff202d860d86fa40c1788e238096394d15 Mon Sep 17 00:00:00 2001 From: Marijn Haverbeke Date: Tue, 2 Jul 2013 14:25:35 +0200 Subject: [PATCH 1281/5780] [compression helper] Sort and complete addon scripts --- doc/compress.html | 45 +++++++++++++++++++++++++-------------------- 1 file changed, 25 insertions(+), 20 deletions(-) diff --git a/doc/compress.html b/doc/compress.html index 8b7a11d670..c7eec47d5e 100644 --- a/doc/compress.html +++ b/doc/compress.html @@ -140,38 +140,43 @@

    { } CodeMi - + + + + + - - + - - + - - - - - + + + - + + + + + - + + + + - + - - - - - - - + + + + + From 4d85080d2a031fecf7fe21f5fa3fe8e6376db4ea Mon Sep 17 00:00:00 2001 From: Marijn Haverbeke Date: Tue, 2 Jul 2013 15:32:12 +0200 Subject: [PATCH 1282/5780] [merge addon] Make sure synchronized scrolling scrolls other pane to top/bot --- addon/merge/merge.js | 20 +++++++++++++++++--- 1 file changed, 17 insertions(+), 3 deletions(-) diff --git a/addon/merge/merge.js b/addon/merge/merge.js index 0e813f3e25..66721e3584 100644 --- a/addon/merge/merge.js +++ b/addon/merge/merge.js @@ -93,7 +93,21 @@ var off = getOffsets(editor, type == DIFF_INSERT ? around.edit : around.orig); var offOther = getOffsets(other, type == DIFF_INSERT ? around.orig : around.edit); var ratio = (midY - off.top) / (off.bot - off.top); - other.scrollTo(null, (offOther.top - halfScreen) + ratio * (offOther.bot - offOther.top)); + var targetPos = (offOther.top - halfScreen) + ratio * (offOther.bot - offOther.top); + + var botDist, mix; + // Some careful tweaking to make sure no space is left out of view + // when scrolling to top or bottom. + if (targetPos > sInfo.top && (mix = sInfo.top / halfScreen) < 1) { + targetPos = targetPos * mix + sInfo.top * (1 - mix); + } else if ((botDist = sInfo.height - sInfo.clientHeight - sInfo.top) < halfScreen) { + var otherInfo = other.getScrollInfo(); + var botDistOther = otherInfo.height - otherInfo.clientHeight - targetPos; + if (botDistOther > botDist && (mix = botDist / halfScreen) < 1) + targetPos = targetPos * mix + (otherInfo.height - otherInfo.clientHeight - botDist) * (1 - mix); + } + + other.scrollTo(null, targetPos); other.state.scrollSetAt = now; other.state.scrollSetBy = dv; return true; @@ -208,8 +222,8 @@ var vpEdit = dv.edit.getViewport(), vpOrig = dv.orig.getViewport(); var sTopEdit = dv.edit.getScrollInfo().top, sTopOrig = dv.orig.getScrollInfo().top; iterateChunks(dv.diff, function(topOrig, botOrig, topEdit, botEdit) { - if (topEdit >= vpEdit.to || botEdit < vpEdit.from || - topOrig >= vpOrig.to || botOrig < vpOrig.from) + if (topEdit > vpEdit.to || botEdit < vpEdit.from || + topOrig > vpOrig.to || botOrig < vpOrig.from) return; var topLpx = dv.orig.heightAtLine(topOrig, "local") - sTopOrig, top = topLpx; if (dv.svg) { From f3bc32b662b5b04d83e63dab71c523ee5edbe405 Mon Sep 17 00:00:00 2001 From: Marijn Haverbeke Date: Tue, 2 Jul 2013 15:33:59 +0200 Subject: [PATCH 1283/5780] [merge addon] Synchronize vertical scrolling --- addon/merge/merge.js | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/addon/merge/merge.js b/addon/merge/merge.js index 66721e3584..cae9ea7167 100644 --- a/addon/merge/merge.js +++ b/addon/merge/merge.js @@ -107,7 +107,7 @@ targetPos = targetPos * mix + (otherInfo.height - otherInfo.clientHeight - botDist) * (1 - mix); } - other.scrollTo(null, targetPos); + other.scrollTo(sInfo.left, targetPos); other.state.scrollSetAt = now; other.state.scrollSetBy = dv; return true; From b4cca0242a4570dae04cfa38024f29f196c896e8 Mon Sep 17 00:00:00 2001 From: Marijn Haverbeke Date: Tue, 2 Jul 2013 15:35:51 +0200 Subject: [PATCH 1284/5780] [merge addon] Use CodeMirror-merge-* CSS class names Since the addon is called merge, not diff. --- addon/merge/merge.css | 56 +++++++++++++++++++++---------------------- addon/merge/merge.js | 44 +++++++++++++++++----------------- 2 files changed, 50 insertions(+), 50 deletions(-) diff --git a/addon/merge/merge.css b/addon/merge/merge.css index 3bd6bbb9a5..63237fc8e9 100644 --- a/addon/merge/merge.css +++ b/addon/merge/merge.css @@ -1,30 +1,30 @@ -.CodeMirror-diff { +.CodeMirror-merge { position: relative; border: 1px solid #ddd; white-space: pre; } -.CodeMirror-diff, .CodeMirror-diff .CodeMirror { +.CodeMirror-merge, .CodeMirror-merge .CodeMirror { height: 350px; } -.CodeMirror-diff-2pane .CodeMirror-diff-pane { width: 47%; } -.CodeMirror-diff-2pane .CodeMirror-diff-gap { width: 6%; } -.CodeMirror-diff-3pane .CodeMirror-diff-pane { width: 31%; } -.CodeMirror-diff-3pane .CodeMirror-diff-gap { width: 3.5%; } +.CodeMirror-merge-2pane .CodeMirror-merge-pane { width: 47%; } +.CodeMirror-merge-2pane .CodeMirror-merge-gap { width: 6%; } +.CodeMirror-merge-3pane .CodeMirror-merge-pane { width: 31%; } +.CodeMirror-merge-3pane .CodeMirror-merge-gap { width: 3.5%; } -.CodeMirror-diff-pane { +.CodeMirror-merge-pane { display: inline-block; white-space: normal; vertical-align: top; } -.CodeMirror-diff-pane-rightmost { +.CodeMirror-merge-pane-rightmost { position: absolute; right: 0px; z-index: 1; } -.CodeMirror-diff-gap { +.CodeMirror-merge-gap { z-index: 2; display: inline-block; height: 100%; @@ -37,11 +37,11 @@ background: #f8f8f8; } -.CodeMirror-diff-scrolllock-wrap { +.CodeMirror-merge-scrolllock-wrap { position: absolute; bottom: 0; left: 50%; } -.CodeMirror-diff-scrolllock { +.CodeMirror-merge-scrolllock { position: relative; left: -50%; cursor: pointer; @@ -49,44 +49,44 @@ line-height: 1; } -.CodeMirror-diff-copybuttons-left, .CodeMirror-diff-copybuttons-right { +.CodeMirror-merge-copybuttons-left, .CodeMirror-merge-copybuttons-right { position: absolute; left: 0; top: 0; right: 0; bottom: 0; line-height: 1; } -.CodeMirror-diff-copy { +.CodeMirror-merge-copy { position: absolute; cursor: pointer; color: #44c; } -.CodeMirror-diff-copybuttons-left .CodeMirror-diff-copy { left: 2px; } -.CodeMirror-diff-copybuttons-right .CodeMirror-diff-copy { right: 2px; } +.CodeMirror-merge-copybuttons-left .CodeMirror-merge-copy { left: 2px; } +.CodeMirror-merge-copybuttons-right .CodeMirror-merge-copy { right: 2px; } -.CodeMirror-diff-r-inserted, .CodeMirror-diff-l-inserted { +.CodeMirror-merge-r-inserted, .CodeMirror-merge-l-inserted { background-image: url(data:image/png;base64,iVBORw0KGgoAAAANSUhEUgAAAAMAAAACCAYAAACddGYaAAAAGUlEQVQI12MwuCXy3+CWyH8GBgYGJgYkAABZbAQ9ELXurwAAAABJRU5ErkJggg==); background-position: bottom left; background-repeat: repeat-x; } -.CodeMirror-diff-r-deleted, .CodeMirror-diff-l-deleted { +.CodeMirror-merge-r-deleted, .CodeMirror-merge-l-deleted { background-image: url(data:image/png;base64,iVBORw0KGgoAAAANSUhEUgAAAAMAAAACCAYAAACddGYaAAAAGUlEQVQI12M4Kyb2/6yY2H8GBgYGJgYkAABURgPz6Ks7wQAAAABJRU5ErkJggg==); background-position: bottom left; background-repeat: repeat-x; } -.CodeMirror-diff-r-chunk { background: #ffffe0; } -.CodeMirror-diff-r-chunk-start { border-top: 1px solid #ee8; } -.CodeMirror-diff-r-chunk-end { border-bottom: 1px solid #ee8; } -.CodeMirror-diff-r-connect { fill: #ffffe0; stroke: #ee8; stroke-width: 1px; } +.CodeMirror-merge-r-chunk { background: #ffffe0; } +.CodeMirror-merge-r-chunk-start { border-top: 1px solid #ee8; } +.CodeMirror-merge-r-chunk-end { border-bottom: 1px solid #ee8; } +.CodeMirror-merge-r-connect { fill: #ffffe0; stroke: #ee8; stroke-width: 1px; } -.CodeMirror-diff-l-chunk { background: #eef; } -.CodeMirror-diff-l-chunk-start { border-top: 1px solid #88e; } -.CodeMirror-diff-l-chunk-end { border-bottom: 1px solid #88e; } -.CodeMirror-diff-l-connect { fill: #eef; stroke: #88e; stroke-width: 1px; } +.CodeMirror-merge-l-chunk { background: #eef; } +.CodeMirror-merge-l-chunk-start { border-top: 1px solid #88e; } +.CodeMirror-merge-l-chunk-end { border-bottom: 1px solid #88e; } +.CodeMirror-merge-l-connect { fill: #eef; stroke: #88e; stroke-width: 1px; } -.CodeMirror-diff-l-chunk.CodeMirror-diff-r-chunk { background: #dfd; } -.CodeMirror-diff-l-chunk-start.CodeMirror-diff-r-chunk-start { border-top: 1px solid #4e4; } -.CodeMirror-diff-l-chunk-end.CodeMirror-diff-r-chunk-end { border-bottom: 1px solid #4e4; } +.CodeMirror-merge-l-chunk.CodeMirror-merge-r-chunk { background: #dfd; } +.CodeMirror-merge-l-chunk-start.CodeMirror-merge-r-chunk-start { border-top: 1px solid #4e4; } +.CodeMirror-merge-l-chunk-end.CodeMirror-merge-r-chunk-end { border-bottom: 1px solid #4e4; } diff --git a/addon/merge/merge.js b/addon/merge/merge.js index cae9ea7167..16c3356c20 100644 --- a/addon/merge/merge.js +++ b/addon/merge/merge.js @@ -8,18 +8,18 @@ this.mv = mv; this.type = type; this.classes = type == "left" - ? {chunk: "CodeMirror-diff-l-chunk", - start: "CodeMirror-diff-l-chunk-start", - end: "CodeMirror-diff-l-chunk-end", - insert: "CodeMirror-diff-l-inserted", - del: "CodeMirror-diff-l-deleted", - connect: "CodeMirror-diff-l-connect"} - : {chunk: "CodeMirror-diff-r-chunk", - start: "CodeMirror-diff-r-chunk-start", - end: "CodeMirror-diff-r-chunk-end", - insert: "CodeMirror-diff-r-inserted", - del: "CodeMirror-diff-r-deleted", - connect: "CodeMirror-diff-r-connect"}; + ? {chunk: "CodeMirror-merge-l-chunk", + start: "CodeMirror-merge-l-chunk-start", + end: "CodeMirror-merge-l-chunk-end", + insert: "CodeMirror-merge-l-inserted", + del: "CodeMirror-merge-l-deleted", + connect: "CodeMirror-merge-l-connect"} + : {chunk: "CodeMirror-merge-r-chunk", + start: "CodeMirror-merge-r-chunk-start", + end: "CodeMirror-merge-r-chunk-end", + insert: "CodeMirror-merge-r-inserted", + del: "CodeMirror-merge-r-deleted", + connect: "CodeMirror-merge-r-connect"}; } DiffView.prototype = { @@ -239,7 +239,7 @@ "class", dv.classes.connect); } var copy = dv.copyButtons.appendChild(elt("div", dv.type == "left" ? "\u21dd" : "\u21dc", - "CodeMirror-diff-copy")); + "CodeMirror-merge-copy")); copy.title = "Revert chunk"; copy.chunk = {topEdit: topEdit, botEdit: botEdit, topOrig: topOrig, botOrig: botOrig}; copy.style.top = top + "px"; @@ -264,25 +264,25 @@ if (hasLeft) { left = this.left = new DiffView(this, "left"); - var leftPane = elt("div", null, "CodeMirror-diff-pane"); + var leftPane = elt("div", null, "CodeMirror-merge-pane"); wrap.push(leftPane); wrap.push(buildGap(left)); } - var editPane = elt("div", null, "CodeMirror-diff-pane"); + var editPane = elt("div", null, "CodeMirror-merge-pane"); wrap.push(editPane); if (hasRight) { right = this.right = new DiffView(this, "right"); wrap.push(buildGap(right)); - var rightPane = elt("div", null, "CodeMirror-diff-pane"); + var rightPane = elt("div", null, "CodeMirror-merge-pane"); wrap.push(rightPane); } - (hasRight ? rightPane : editPane).className += " CodeMirror-diff-pane-rightmost"; + (hasRight ? rightPane : editPane).className += " CodeMirror-merge-pane-rightmost"; wrap.push(elt("div", null, null, "height: 0; clear: both;")); - var wrapElt = this.wrap = node.appendChild(elt("div", wrap, "CodeMirror-diff CodeMirror-diff-" + panes + "pane")); + var wrapElt = this.wrap = node.appendChild(elt("div", wrap, "CodeMirror-merge CodeMirror-merge-" + panes + "pane")); this.edit = CodeMirror(editPane, copyObj(options)); if (left) left.init(leftPane, origLeft, options); @@ -300,11 +300,11 @@ }; function buildGap(dv) { - var lock = dv.lockButton = elt("div", null, "CodeMirror-diff-scrolllock"); + var lock = dv.lockButton = elt("div", null, "CodeMirror-merge-scrolllock"); lock.title = "Toggle locked scrolling"; - var lockWrap = elt("div", [lock], "CodeMirror-diff-scrolllock-wrap"); + var lockWrap = elt("div", [lock], "CodeMirror-merge-scrolllock-wrap"); CodeMirror.on(lock, "click", function() { setScrollLock(dv, !dv.lockScroll); }); - dv.copyButtons = elt("div", null, "CodeMirror-diff-copybuttons-" + dv.type); + dv.copyButtons = elt("div", null, "CodeMirror-merge-copybuttons-" + dv.type); CodeMirror.on(dv.copyButtons, "click", function(e) { var node = e.target || e.srcElement; if (node.chunk) copyChunk(dv, node.chunk); @@ -315,7 +315,7 @@ dv.svg = svg; if (svg) gapElts.push(svg); - return dv.gap = elt("div", gapElts, "CodeMirror-diff-gap"); + return dv.gap = elt("div", gapElts, "CodeMirror-merge-gap"); } MergeView.prototype = { From bc85bdb354799ab2e1c8dd4a1dd2600db826e42c Mon Sep 17 00:00:00 2001 From: Marijn Haverbeke Date: Wed, 3 Jul 2013 08:48:19 +0200 Subject: [PATCH 1285/5780] [matchtags addon] Only highlight matching tag, try to be more efficient --- addon/edit/matchtags.js | 36 ++++++++++++++++++++++++++---------- addon/fold/xml-fold.js | 20 +++++++++++--------- demo/matchtags.html | 1 + 3 files changed, 38 insertions(+), 19 deletions(-) diff --git a/addon/edit/matchtags.js b/addon/edit/matchtags.js index 8cdc8b20c8..66e57c8e9e 100644 --- a/addon/edit/matchtags.js +++ b/addon/edit/matchtags.js @@ -2,25 +2,41 @@ "use strict"; CodeMirror.defineOption("matchTags", false, function(cm, val, old) { - if (old && old != CodeMirror.Init) + if (old && old != CodeMirror.Init) { cm.off("cursorActivity", doMatchTags); - if (val) + cm.off("viewportChange", maybeUpdateMatch); + clear(cm); + } + if (val) { cm.on("cursorActivity", doMatchTags); + cm.on("viewportChange", maybeUpdateMatch); + doMatchTags(cm); + } }); + function clear(cm) { + if (cm.state.matchedTag) { + cm.state.matchedTag.clear(); + cm.state.matchedTag = null; + } + } + function doMatchTags(cm) { cm.operation(function() { - if (cm.state.matchedTags) { cm.state.matchedTags(); cm.state.matchedTags = null; } - - var cur = cm.getCursor(); - var match = CodeMirror.findMatchingTag(cm, cur) || CodeMirror.findEnclosingTag(cm, cur); - if (!match) return; - var one = cm.markText(match.open.from, match.open.to, {className: "CodeMirror-matchingbracket"}); - var two = cm.markText(match.close.from, match.close.to, {className: "CodeMirror-matchingbracket"}); - cm.state.matchedTags = function() { one.clear(); two.clear(); }; + clear(cm); + var cur = cm.getCursor(), range = cm.getViewport(); + range.from = Math.min(range.from, cur.line); range.to = Math.max(cur.line + 1, range.to); + var match = CodeMirror.findMatchingTag(cm, cur, range); + if (cm.state.failedTagMatch = !match) return; + var other = match.at == "close" ? match.open : match.close; + cm.state.matchedTag = cm.markText(other.from, other.to, {className: "CodeMirror-matchingtag"}); }); } + function maybeUpdateMatch(cm) { + if (cm.state.failedTagMatch) doMatchTags(cm); + } + CodeMirror.commands.toMatchingTag = function(cm) { var found = CodeMirror.findMatchingTag(cm, cm.getCursor()); if (found) { diff --git a/addon/fold/xml-fold.js b/addon/fold/xml-fold.js index 572feaa244..bf8ca9c2ac 100644 --- a/addon/fold/xml-fold.js +++ b/addon/fold/xml-fold.js @@ -8,9 +8,11 @@ var nameChar = nameStartChar + "\-\:\.0-9\\u00B7\\u0300-\\u036F\\u203F-\\u2040"; var xmlTagStart = new RegExp("<(/?)([" + nameStartChar + "][" + nameChar + "]*)", "g"); - function Iter(cm, line, ch) { + function Iter(cm, line, ch, range) { this.line = line; this.ch = ch; this.cm = cm; this.text = cm.getLine(line); + this.min = range ? range.from : cm.firstLine(); + this.max = range ? range.to - 1 : cm.lastLine(); } function tagAt(iter, ch) { @@ -19,13 +21,13 @@ } function nextLine(iter) { - if (iter.line >= iter.cm.lastLine()) return; + if (iter.line >= iter.max) return; iter.ch = 0; iter.text = iter.cm.getLine(++iter.line); return true; } function prevLine(iter) { - if (iter.line <= iter.cm.firstLine()) return; + if (iter.line <= iter.min) return; iter.text = iter.cm.getLine(--iter.line); iter.ch = iter.text.length; return true; @@ -135,8 +137,8 @@ } }; - CodeMirror.findMatchingTag = function(cm, pos) { - var iter = new Iter(cm, pos.line, pos.ch); + CodeMirror.findMatchingTag = function(cm, pos, range) { + var iter = new Iter(cm, pos.line, pos.ch, range); var end = toTagEnd(iter), to = end && Pos(iter.line, iter.ch); var start = end && toTagStart(iter); if (!end || end == "selfClose" || !start || cmp(iter, pos) > 0) return; @@ -146,18 +148,18 @@ var open = findMatchingOpen(iter, start[2]); return open && {open: open, close: here, at: "close"}; } else { // opening tag - iter = new Iter(cm, to.line, to.ch); + iter = new Iter(cm, to.line, to.ch, range); var close = findMatchingClose(iter, start[2]); return close && {open: here, close: close, at: "open"}; } }; - CodeMirror.findEnclosingTag = function(cm, pos) { - var iter = new Iter(cm, pos.line, pos.ch); + CodeMirror.findEnclosingTag = function(cm, pos, range) { + var iter = new Iter(cm, pos.line, pos.ch, range); for (;;) { var open = findMatchingOpen(iter); if (!open) break; - var forward = new Iter(cm, pos.line, pos.ch); + var forward = new Iter(cm, pos.line, pos.ch, range); var close = findMatchingClose(forward, open.tag); if (close) return {open: open, close: close}; } diff --git a/demo/matchtags.html b/demo/matchtags.html index 053c0ed667..8f4217debe 100644 --- a/demo/matchtags.html +++ b/demo/matchtags.html @@ -12,6 +12,7 @@ From c969e83279331d1535096fb8925816630667760a Mon Sep 17 00:00:00 2001 From: Marijn Haverbeke Date: Wed, 3 Jul 2013 09:43:27 +0200 Subject: [PATCH 1286/5780] Stabilize scroll position when removing widgets (When they are above visible range, scroll up to correspond for height reduction.) Closes #1645 --- lib/codemirror.js | 4 +++- 1 file changed, 3 insertions(+), 1 deletion(-) diff --git a/lib/codemirror.js b/lib/codemirror.js index 1aa350a2df..c702260103 100644 --- a/lib/codemirror.js +++ b/lib/codemirror.js @@ -4057,7 +4057,9 @@ window.CodeMirror = (function() { if (no == null || !ws) return; for (var i = 0; i < ws.length; ++i) if (ws[i] == this) ws.splice(i--, 1); if (!ws.length) this.line.widgets = null; + var aboveVisible = heightAtLine(this.cm, this.line) < this.cm.doc.scrollTop; updateLineHeight(this.line, Math.max(0, this.line.height - widgetHeight(this))); + if (aboveVisible) addToScrollPos(this.cm, 0, -this.height); regChange(this.cm, no, no + 1); }); LineWidget.prototype.changed = widgetOperation(function() { @@ -4086,7 +4088,7 @@ window.CodeMirror = (function() { else widgets.splice(Math.max(widgets.length - 1, widget.insertAt), 0, widget); widget.line = line; if (!lineIsHidden(cm.doc, line) || widget.showIfHidden) { - var aboveVisible = heightAtLine(cm, line) < cm.display.scroller.scrollTop; + var aboveVisible = heightAtLine(cm, line) < cm.doc.scrollTop; updateLineHeight(line, line.height + widgetHeight(widget)); if (aboveVisible) addToScrollPos(cm, 0, widget.height); } From a7dba9653f39b100879b82b6e6e5d6d542474488 Mon Sep 17 00:00:00 2001 From: Marijn Haverbeke Date: Wed, 3 Jul 2013 10:21:33 +0200 Subject: [PATCH 1287/5780] Fix problem in widget-preserving line redraws Issue #1645 --- lib/codemirror.js | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/lib/codemirror.js b/lib/codemirror.js index c702260103..4721f67e2f 100644 --- a/lib/codemirror.js +++ b/lib/codemirror.js @@ -668,10 +668,10 @@ window.CodeMirror = (function() { if (!/\bCodeMirror-linewidget\b/.test(n.className)) { reuse.removeChild(n); } else { - for (var i = 0, first = true; i < line.widgets.length; ++i) { + for (var i = 0; i < line.widgets.length; ++i) { var widget = line.widgets[i]; - if (!widget.above) { insertBefore = n; first = false; } if (widget.node == n.firstChild) { + if (!widget.above && !insertBefore) insertBefore = n; positionLineWidget(widget, n, reuse, dims); ++widgetsSeen; break; From ac14de0d671c0219c4c10c28ed2f63aa9b4e3993 Mon Sep 17 00:00:00 2001 From: Marijn Haverbeke Date: Wed, 3 Jul 2013 10:41:28 +0200 Subject: [PATCH 1288/5780] Fix bug in character measurement Issue #1617 --- lib/codemirror.js | 5 +++-- test/test.js | 7 +++++-- 2 files changed, 8 insertions(+), 4 deletions(-) diff --git a/lib/codemirror.js b/lib/codemirror.js index 4721f67e2f..180594f198 100644 --- a/lib/codemirror.js +++ b/lib/codemirror.js @@ -964,8 +964,9 @@ window.CodeMirror = (function() { if (r) break; if (dir < 0 && pos == 0) dir = 1; } - if ((pos > ch || bias == "left") && r.leftSide) r = r.leftSide; - else if ((pos < ch || bias == "right") && r.rightSide) r = r.rightSide; + bias = pos > ch ? "left" : pos < ch ? "right" : bias; + if (bias == "left" && r.leftSide) r = r.leftSide; + else if (bias == "right" && r.rightSide) r = r.rightSide; return {left: pos < ch ? r.right : r.left, right: pos > ch ? r.left : r.right, top: r.top, diff --git a/test/test.js b/test/test.js index cc4b57a4b6..641818484d 100644 --- a/test/test.js +++ b/test/test.js @@ -501,7 +501,8 @@ testCM("bookmarkInsertLeft", function(cm) { testCM("bookmarkCursor", function(cm) { var pos01 = cm.cursorCoords(Pos(0, 1)), pos11 = cm.cursorCoords(Pos(1, 1)), - pos20 = cm.cursorCoords(Pos(2, 0)), pos30 = cm.cursorCoords(Pos(3, 0)); + pos20 = cm.cursorCoords(Pos(2, 0)), pos30 = cm.cursorCoords(Pos(3, 0)), + pos41 = cm.cursorCoords(Pos(4, 1)); cm.setBookmark(Pos(0, 1), {widget: document.createTextNode("←"), insertLeft: true}); cm.setBookmark(Pos(2, 0), {widget: document.createTextNode("←"), insertLeft: true}); cm.setBookmark(Pos(1, 1), {widget: document.createTextNode("→")}); @@ -512,7 +513,9 @@ testCM("bookmarkCursor", function(cm) { is(new11.left > pos11.left && new11.top == pos11.top, "at right, middle of line"); is(new20.left == pos20.left && new20.top == pos20.top, "at left, empty line"); is(new30.left > pos30.left && new30.top == pos30.top, "at right, empty line"); -}, {value: "foo\nbar\n\n\nx"}); + cm.setBookmark(Pos(4, 0), {widget: document.createTextNode("→")}); + is(cm.cursorCoords(Pos(4, 1)).left > pos41.left, "single-char bug"); +}, {value: "foo\nbar\n\n\nx\ny"}); testCM("getAllMarks", function(cm) { addDoc(cm, 10, 10); From ba3a62bde5305d4e02ec8555564aa25cb5743862 Mon Sep 17 00:00:00 2001 From: Marijn Haverbeke Date: Wed, 3 Jul 2013 11:37:13 +0200 Subject: [PATCH 1289/5780] [anyword-hint addon] Add --- addon/hint/anyword-hint.js | 34 +++++++++++++++++++ demo/anywordhint.html | 69 ++++++++++++++++++++++++++++++++++++++ doc/manual.html | 9 +++++ 3 files changed, 112 insertions(+) create mode 100644 addon/hint/anyword-hint.js create mode 100644 demo/anywordhint.html diff --git a/addon/hint/anyword-hint.js b/addon/hint/anyword-hint.js new file mode 100644 index 0000000000..b3b2c1e1c0 --- /dev/null +++ b/addon/hint/anyword-hint.js @@ -0,0 +1,34 @@ +(function() { + "use strict"; + + var WORD = /[\w$]+/, RANGE = 500; + + CodeMirror.anyWordHint = function(editor, options) { + var word = options && options.word || WORD; + var range = options && options.range || RANGE; + var cur = editor.getCursor(), curLine = editor.getLine(cur.line); + var start = cur.ch, end = start; + while (end < curLine.length && word.test(curLine.charAt(end))) ++end; + while (start && word.test(curLine.charAt(start - 1))) --start; + var curWord = start != end && curLine.slice(start, end); + + var list = [], seen = {}; + function scan(dir) { + var line = cur.line, end = Math.min(Math.max(line + dir * range, editor.firstLine()), editor.lastLine()) + dir; + for (; line != end; line += dir) { + var text = editor.getLine(line), m; + var re = new RegExp(word.source, "g"); + while (m = re.exec(text)) { + if (line == cur.line && m[0] === curWord) continue; + if ((!curWord || m[0].indexOf(curWord) == 0) && !seen.hasOwnProperty(m[0])) { + seen[m[0]] = true; + list.push(m[0]); + } + } + } + } + scan(-1); + scan(1); + return {list: list, from: CodeMirror.Pos(cur.line, start), to: CodeMirror.Pos(cur.line, end)}; + }; +})(); diff --git a/demo/anywordhint.html b/demo/anywordhint.html new file mode 100644 index 0000000000..820df39e90 --- /dev/null +++ b/demo/anywordhint.html @@ -0,0 +1,69 @@ + + + + + CodeMirror: Any Word Completion Demo + + + + + + + + + +

    CodeMirror: Any Word Completion Demo

    + +
    + +

    Press ctrl-space to activate autocompletion. The +completion uses +the anyword-hint.js +module, which simply looks at nearby words in the buffer and completes +to those.

    + + + + diff --git a/doc/manual.html b/doc/manual.html index 4284535f3f..74b2aab783 100644 --- a/doc/manual.html +++ b/doc/manual.html @@ -1915,6 +1915,15 @@

    Add-ons

    A very simple hinting function for Python code. Defines CodeMirror.pythonHint.
    +
    hint/anyword-hint.js
    +
    A very simple hinting function + (CodeMirror.anyWordHint) that simply looks for + words in the nearby code and completes to those. Takes two + optional options, word, a regular expression that + matches words (sequences of one or more character), + and range, which defines how many lines the addon + should scan when completing (defaults to 500).
    +
    match-highlighter.js
    Adds a highlightSelectionMatches option that can be enabled to highlight all instances of a currently From 022bc2862faa29970193fa10f8bbb3f469600e26 Mon Sep 17 00:00:00 2001 From: Marijn Haverbeke Date: Wed, 3 Jul 2013 13:45:22 +0200 Subject: [PATCH 1290/5780] Add registerHelper/getHelper mechanism Move hinting functions into it. --- addon/hint/anyword-hint.js | 4 +-- addon/hint/html-hint.js | 8 +++-- addon/hint/javascript-hint.js | 10 +++++-- addon/hint/pig-hint.js | 6 ++-- addon/hint/python-hint.js | 6 ++-- addon/hint/show-hint.js | 2 ++ addon/hint/xml-hint.js | 7 +++-- demo/anywordhint.html | 6 ++-- demo/complete.html | 2 +- demo/html5complete.html | 2 +- demo/xmlcomplete.html | 4 +-- doc/manual.html | 55 ++++++++++++++++++++++++++--------- lib/codemirror.js | 18 +++++++++++- mode/xml/xml.js | 3 +- 14 files changed, 96 insertions(+), 37 deletions(-) diff --git a/addon/hint/anyword-hint.js b/addon/hint/anyword-hint.js index b3b2c1e1c0..36ff618e09 100644 --- a/addon/hint/anyword-hint.js +++ b/addon/hint/anyword-hint.js @@ -3,7 +3,7 @@ var WORD = /[\w$]+/, RANGE = 500; - CodeMirror.anyWordHint = function(editor, options) { + CodeMirror.registerHelper("hint", "anyword", function(editor, options) { var word = options && options.word || WORD; var range = options && options.range || RANGE; var cur = editor.getCursor(), curLine = editor.getLine(cur.line); @@ -30,5 +30,5 @@ scan(-1); scan(1); return {list: list, from: CodeMirror.Pos(cur.line, start), to: CodeMirror.Pos(cur.line, end)}; - }; + }); })(); diff --git a/addon/hint/html-hint.js b/addon/hint/html-hint.js index 23238df054..cf256851ef 100755 --- a/addon/hint/html-hint.js +++ b/addon/hint/html-hint.js @@ -327,9 +327,11 @@ populate(data[tag]); CodeMirror.htmlSchema = data; - CodeMirror.htmlHint = function(cm, options) { + function htmlHint(cm, options) { var local = {schemaInfo: data}; if (options) for (var opt in options) local[opt] = options[opt]; - return CodeMirror.xmlHint(cm, local); - }; + return CodeMirror.hint.xml(cm, local); + } + CodeMirror.htmlHint = htmlHint; // deprecated + CodeMirror.registerHelper("hint", "html", htmlHint); })(); diff --git a/addon/hint/javascript-hint.js b/addon/hint/javascript-hint.js index b961c5abe9..042fe1325d 100644 --- a/addon/hint/javascript-hint.js +++ b/addon/hint/javascript-hint.js @@ -56,11 +56,13 @@ to: Pos(cur.line, token.end)}; } - CodeMirror.javascriptHint = function(editor, options) { + function javascriptHint(editor, options) { return scriptHint(editor, javascriptKeywords, function (e, cur) {return e.getTokenAt(cur);}, options); }; + CodeMirror.javascriptHint = javascriptHint; // deprecated + CodeMirror.registerHelper("hint", "javascript", javascriptHint); function getCoffeeScriptToken(editor, cur) { // This getToken, it is for coffeescript, imitates the behavior of @@ -80,9 +82,11 @@ return token; } - CodeMirror.coffeescriptHint = function(editor, options) { + function coffeescriptHint(editor, options) { return scriptHint(editor, coffeescriptKeywords, getCoffeeScriptToken, options); - }; + } + CodeMirror.coffeescriptHint = coffeescriptHint; // deprecated + CodeMirror.registerHelper("hint", "coffeescript", coffeescriptHint); var stringProps = ("charAt charCodeAt indexOf lastIndexOf substring substr slice trim trimLeft trimRight " + "toUpperCase toLowerCase split concat match replace search").split(" "); diff --git a/addon/hint/pig-hint.js b/addon/hint/pig-hint.js index d831ccfe93..155973f22d 100644 --- a/addon/hint/pig-hint.js +++ b/addon/hint/pig-hint.js @@ -41,9 +41,11 @@ to: CodeMirror.Pos(cur.line, token.end)}; } - CodeMirror.pigHint = function(editor) { + function pigHint(editor) { return scriptHint(editor, pigKeywordsU, function (e, cur) {return e.getTokenAt(cur);}); - }; + } + CodeMirror.pigHint = pigHint; // deprecated + CodeMirror.registerHelper("hint", "pig", hinter); var pigKeywords = "VOID IMPORT RETURNS DEFINE LOAD FILTER FOREACH ORDER CUBE DISTINCT COGROUP " + "JOIN CROSS UNION SPLIT INTO IF OTHERWISE ALL AS BY USING INNER OUTER ONSCHEMA PARALLEL " diff --git a/addon/hint/python-hint.js b/addon/hint/python-hint.js index 60221b89ec..98d2a5897f 100644 --- a/addon/hint/python-hint.js +++ b/addon/hint/python-hint.js @@ -41,9 +41,11 @@ to: CodeMirror.Pos(cur.line, token.end)}; } - CodeMirror.pythonHint = function(editor) { + function pythonHint(editor) { return scriptHint(editor, pythonKeywordsU, function (e, cur) {return e.getTokenAt(cur);}); - }; + } + CodeMirror.pythonHint = pythonHint; // deprecated + CodeMirror.registerHelper("hint", "python", pythonHint); var pythonKeywords = "and del from not while as elif global or with assert else if pass yield" + "break except import print class exec in raise continue finally is return def for lambda try"; diff --git a/addon/hint/show-hint.js b/addon/hint/show-hint.js index 35e5cbb721..5a34f552f7 100644 --- a/addon/hint/show-hint.js +++ b/addon/hint/show-hint.js @@ -4,6 +4,8 @@ CodeMirror.showHint = function(cm, getHints, options) { // We want a single cursor position. if (cm.somethingSelected()) return; + if (getHints == null) getHints = cm.getHelper(cm.getCursor(), "hint"); + if (getHints == null) return; if (cm.state.completionActive) cm.state.completionActive.close(); diff --git a/addon/hint/xml-hint.js b/addon/hint/xml-hint.js index ea5b8d546a..b6c1da2ce2 100644 --- a/addon/hint/xml-hint.js +++ b/addon/hint/xml-hint.js @@ -3,7 +3,7 @@ var Pos = CodeMirror.Pos; - CodeMirror.xmlHint = function(cm, options) { + function getHints(cm, options) { var tags = options && options.schemaInfo; var quote = (options && options.quoteChar) || '"'; if (!tags) return; @@ -61,5 +61,8 @@ from: replaceToken ? Pos(cur.line, token.start) : cur, to: replaceToken ? Pos(cur.line, token.end) : cur }; - }; + } + + CodeMirror.xmlHint = getHints; // deprecated + CodeMirror.registerHelper("hint", "xml", getHints); })(); diff --git a/demo/anywordhint.html b/demo/anywordhint.html index 820df39e90..5bb0a62a10 100644 --- a/demo/anywordhint.html +++ b/demo/anywordhint.html @@ -20,7 +20,7 @@

    CodeMirror: Any Word Completion Demo

    var WORD = /[\w$]+/g, RANGE = 500; - CodeMirror.anyWordHint = function(editor, options) { + CodeMirror.registerHelper("hint", "anyword", function(editor, options) { var word = options && options.word || WORD; var range = options && options.range || RANGE; var cur = editor.getCursor(), curLine = editor.getLine(cur.line); @@ -46,7 +46,7 @@

    CodeMirror: Any Word Completion Demo

    scan(-1); scan(1); return {list: list, from: CodeMirror.Pos(cur.line, start), to: CodeMirror.Pos(cur.line, end)}; - }; + }); })(); @@ -58,7 +58,7 @@

    CodeMirror: Any Word Completion Demo

    diff --git a/doc/manual.html b/doc/manual.html index c4e2ece461..c7466ce243 100644 --- a/doc/manual.html +++ b/doc/manual.html @@ -1745,24 +1745,28 @@

    Addons

    fails, line-comments it.
    fold/foldcode.js
    -
    Helps with code folding. Add a foldCode method +
    Helps with code folding. Adds a foldCode method to editor instances, which will try to do a code fold starting at the given line, or unfold the fold that is already present. The method takes as first argument the position that should be folded (may be a line number or - a Pos), and as second argument - either a range-finder function, or an options object, supporting - the following properties: + a Pos), and as second optional + argument either a range-finder function, or an options object, + supporting the following properties:
    rangeFinder: fn(CodeMirror, Pos)
    -
    The function that is used to find foldable ranges. There - are files in the addon/fold/ - directory providing CodeMirror.braceRangeFinder, - which finds blocks in brace languages (JavaScript, C, Java, - etc), CodeMirror.indentRangeFinder, for languages - where indentation determines block structure (Python, - Haskell), and CodeMirror.tagRangeFinder, for - XML-style languages.
    +
    The function that is used to find foldable ranges. If this + is not directly passed, it will + call getHelper with + a "fold" type to find one that's appropriate for + the mode. There are files in + the addon/fold/ + directory providing CodeMirror.fold.brace, which + finds blocks in brace languages (JavaScript, C, Java, + etc), CodeMirror.fold.indent, for languages where + indentation determines block structure (Python, Haskell), + and CodeMirror.fold.xml, for XML-style + languages.
    widget: string|Element
    The widget to show for folded ranges. Can be either a string, in which case it'll become a span with diff --git a/lib/codemirror.js b/lib/codemirror.js index a43abb9724..659f8c96bc 100644 --- a/lib/codemirror.js +++ b/lib/codemirror.js @@ -2874,7 +2874,7 @@ window.CodeMirror = (function() { if (!helpers.hasOwnProperty(type)) return; var state = this.getTokenAt(pos).state, help = helpers[type]; var mode = CodeMirror.innerMode(this.getMode(), state).mode; - return mode.type && help[mode[type]] || + return mode[type] && help[mode[type]] || mode.helperType && help[mode.helperType] || help[mode.name]; }, diff --git a/mode/clike/clike.js b/mode/clike/clike.js index 3fcc1a757b..f6626cd0ea 100644 --- a/mode/clike/clike.js +++ b/mode/clike/clike.js @@ -158,7 +158,8 @@ CodeMirror.defineMode("clike", function(config, parserConfig) { electricChars: "{}", blockCommentStart: "/*", blockCommentEnd: "*/", - lineComment: "//" + lineComment: "//", + fold: "brace" }; }); diff --git a/mode/coffeescript/coffeescript.js b/mode/coffeescript/coffeescript.js index 509d9207bb..4f54b0c690 100644 --- a/mode/coffeescript/coffeescript.js +++ b/mode/coffeescript/coffeescript.js @@ -339,7 +339,8 @@ CodeMirror.defineMode('coffeescript', function(conf) { return state.scopes[0].offset; }, - lineComment: "#" + lineComment: "#", + fold: "indent" }; return external; }); diff --git a/mode/css/css.js b/mode/css/css.js index b38a968e5e..b52e787a70 100644 --- a/mode/css/css.js +++ b/mode/css/css.js @@ -289,7 +289,8 @@ CodeMirror.defineMode("css-base", function(config, parserConfig) { electricChars: "}", blockCommentStart: "/*", - blockCommentEnd: "*/" + blockCommentEnd: "*/", + fold: "brace" }; }); diff --git a/mode/groovy/groovy.js b/mode/groovy/groovy.js index 92b948192e..6800e0aaf5 100644 --- a/mode/groovy/groovy.js +++ b/mode/groovy/groovy.js @@ -203,7 +203,8 @@ CodeMirror.defineMode("groovy", function(config) { else return ctx.indented + (closing ? 0 : config.indentUnit); }, - electricChars: "{}" + electricChars: "{}", + fold: "brace" }; }); diff --git a/mode/javascript/javascript.js b/mode/javascript/javascript.js index 6435e138d6..f8c710f912 100644 --- a/mode/javascript/javascript.js +++ b/mode/javascript/javascript.js @@ -461,6 +461,7 @@ CodeMirror.defineMode("javascript", function(config, parserConfig) { blockCommentStart: jsonMode ? null : "/*", blockCommentEnd: jsonMode ? null : "*/", lineComment: jsonMode ? null : "//", + fold: "brace", jsonMode: jsonMode }; diff --git a/mode/python/python.js b/mode/python/python.js index b623972b88..565d963fdf 100644 --- a/mode/python/python.js +++ b/mode/python/python.js @@ -333,7 +333,8 @@ CodeMirror.defineMode("python", function(conf, parserConf) { return state.scopes[0].offset; }, - lineComment: "#" + lineComment: "#", + fold: "indent" }; return external; }); diff --git a/mode/rust/rust.js b/mode/rust/rust.js index 7bee489b47..c7530b6cc6 100644 --- a/mode/rust/rust.js +++ b/mode/rust/rust.js @@ -428,7 +428,8 @@ CodeMirror.defineMode("rust", function() { electricChars: "{}", blockCommentStart: "/*", blockCommentEnd: "*/", - lineComment: "//" + lineComment: "//", + fold: "brace" }; }); From e0e28c5dc02b54922f8853c1560d1d1fe74b9ef1 Mon Sep 17 00:00:00 2001 From: Marijn Haverbeke Date: Wed, 3 Jul 2013 14:26:22 +0200 Subject: [PATCH 1292/5780] [lint addon] Move over to registerHelper --- addon/lint/coffeescript-lint.js | 5 +++-- addon/lint/javascript-lint.js | 9 +++------ addon/lint/json-lint.js | 5 +++-- addon/lint/lint.js | 18 ++++++++++++------ demo/lint.html | 6 +++--- doc/manual.html | 13 ++++++++----- mode/javascript/javascript.js | 1 + 7 files changed, 33 insertions(+), 24 deletions(-) diff --git a/addon/lint/coffeescript-lint.js b/addon/lint/coffeescript-lint.js index 9c30de51c2..75f8db6565 100644 --- a/addon/lint/coffeescript-lint.js +++ b/addon/lint/coffeescript-lint.js @@ -1,6 +1,6 @@ // Depends on coffeelint.js from http://www.coffeelint.org/js/coffeelint.js -CodeMirror.coffeeValidator = function(text) { +CodeMirror.registerHelper("lint", "coffeescript", function(text) { var found = []; var parseError = function(err) { var loc = err.lineNumber; @@ -21,4 +21,5 @@ CodeMirror.coffeeValidator = function(text) { message: e.message}); } return found; -}; +}); +CodeMirror.coffeeValidator = CodeMirror.lint.coffeescript; // deprecated diff --git a/addon/lint/javascript-lint.js b/addon/lint/javascript-lint.js index 9a815c824e..d7a6f6af28 100644 --- a/addon/lint/javascript-lint.js +++ b/addon/lint/javascript-lint.js @@ -9,18 +9,15 @@ "Unmatched ", " and instead saw", " is not defined", "Unclosed string", "Stopping, unable to continue" ]; - function validator(options, text) { + function validator(text, options) { JSHINT(text, options); var errors = JSHINT.data().errors, result = []; if (errors) parseErrors(errors, result); return result; } - CodeMirror.javascriptValidatorWithOptions = function(options) { - return function(text) { return validator(options, text); }; - }; - - CodeMirror.javascriptValidator = CodeMirror.javascriptValidatorWithOptions(null); + CodeMirror.registerHelper("lint", "javascript", validator); + CodeMirror.javascriptValidator = CodeMirror.lint.javascript; // deprecated function cleanup(error) { // All problems are warnings by default diff --git a/addon/lint/json-lint.js b/addon/lint/json-lint.js index 42b36abb39..1dfc6b8fdc 100644 --- a/addon/lint/json-lint.js +++ b/addon/lint/json-lint.js @@ -1,6 +1,6 @@ // Depends on jsonlint.js from https://github.com/zaach/jsonlint -CodeMirror.jsonValidator = function(text) { +CodeMirror.registerHelper("lint", "json", function(text) { var found = []; jsonlint.parseError = function(str, hash) { var loc = hash.loc; @@ -11,4 +11,5 @@ CodeMirror.jsonValidator = function(text) { try { jsonlint.parse(text); } catch(e) {} return found; -}; +}); +CodeMirror.jsonValidator = CodeMirror.lint.json; // deprecated diff --git a/addon/lint/lint.js b/addon/lint/lint.js index 2e7cea1925..67a92970a3 100644 --- a/addon/lint/lint.js +++ b/addon/lint/lint.js @@ -1,4 +1,5 @@ -CodeMirror.validate = (function() { +(function() { + "use strict"; var GUTTER_ID = "CodeMirror-lint-markers"; var SEVERITIES = /^(?:error|warning)$/; @@ -52,9 +53,11 @@ CodeMirror.validate = (function() { this.onMouseOver = function(e) { onMouseOver(cm, e); }; } - function parseOptions(options) { + function parseOptions(cm, options) { if (options instanceof Function) return {getAnnotations: options}; - else if (!options || !options.getAnnotations) throw new Error("Required option 'getAnnotations' missing (lint addon)"); + if (!options || options === true) options = {}; + if (!options.getAnnotations) options.getAnnotations = cm.getHelper(CodeMirror.Pos(0, 0), "lint"); + if (!options.getAnnotations) throw new Error("Required option 'getAnnotations' missing (lint addon)"); return options; } @@ -175,7 +178,7 @@ CodeMirror.validate = (function() { } } - CodeMirror.defineOption("lintWith", false, function(cm, val, old) { + function optionHandler(cm, val, old) { if (old && old != CodeMirror.Init) { clearMarks(cm); cm.off("change", onChange); @@ -186,12 +189,15 @@ CodeMirror.validate = (function() { if (val) { var gutters = cm.getOption("gutters"), hasLintGutter = false; for (var i = 0; i < gutters.length; ++i) if (gutters[i] == GUTTER_ID) hasLintGutter = true; - var state = cm.state.lint = new LintState(cm, parseOptions(val), hasLintGutter); + var state = cm.state.lint = new LintState(cm, parseOptions(cm, val), hasLintGutter); cm.on("change", onChange); if (state.options.tooltips != false) CodeMirror.on(cm.getWrapperElement(), "mouseover", state.onMouseOver); startLinting(cm); } - }); + } + + CodeMirror.defineOption("lintWith", false, optionHandler); // deprecated + CodeMirror.defineOption("lint", false, optionHandler); // deprecated })(); diff --git a/demo/lint.html b/demo/lint.html index ece8b1cef7..d379c31934 100644 --- a/demo/lint.html +++ b/demo/lint.html @@ -7,7 +7,7 @@ - + @@ -75,14 +75,14 @@

    CodeMirror: Linter Demo

    lineNumbers: true, mode: "javascript", gutters: ["CodeMirror-lint-markers"], - lintWith: CodeMirror.javascriptValidator + lint: true }); var editor_json = CodeMirror.fromTextArea(document.getElementById("code-json"), { lineNumbers: true, mode: "application/json", gutters: ["CodeMirror-lint-markers"], - lintWith: CodeMirror.jsonValidator + lint: true }); diff --git a/doc/manual.html b/doc/manual.html index c7466ce243..d028b9615c 100644 --- a/doc/manual.html +++ b/doc/manual.html @@ -1974,11 +1974,14 @@

    Addons

    with pluggable warning sources (see json-lint.js and javascript-lint.js - in the same directory). Defines a lintWith option - that can be set to a warning source (for - example CodeMirror.javascriptValidator). Depends - on addon/lint/lint.css. A demo can be - found here.
    + in the same directory). Defines a lint option that + can be set to a warning source (for + example CodeMirror.lint.javascript), or + to true, in which + case getHelper with + type "lint" is used to determined a validator + function. Depends on addon/lint/lint.css. A demo + can be found here.
    selection/mark-selection.js
    Causes the selected text to be marked with the CSS class diff --git a/mode/javascript/javascript.js b/mode/javascript/javascript.js index f8c710f912..f5507ce699 100644 --- a/mode/javascript/javascript.js +++ b/mode/javascript/javascript.js @@ -463,6 +463,7 @@ CodeMirror.defineMode("javascript", function(config, parserConfig) { lineComment: jsonMode ? null : "//", fold: "brace", + helperType: jsonMode ? "json" : "javascript", jsonMode: jsonMode }; }); From 348db87b65b38d0b27f1a796797ee36f2a11195c Mon Sep 17 00:00:00 2001 From: Marijn Haverbeke Date: Wed, 3 Jul 2013 14:40:45 +0200 Subject: [PATCH 1293/5780] [manual] Fix missing tag --- doc/manual.html | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/doc/manual.html b/doc/manual.html index d028b9615c..68be9718f6 100644 --- a/doc/manual.html +++ b/doc/manual.html @@ -1584,7 +1584,7 @@

    Static properties

    (with the instance as argument) whenever a new CodeMirror instance is initialized.
    -
    CodeMirror.registerHelper(type: string, name: string, value: helper)
    +
    CodeMirror.registerHelper(type: string, name: string, value: helper)
    Registers a helper value with the given name in the given namespace (type). This is used to define functionality that may be looked up by mode. Will create (if it From 9d6f762ebec15dd5e00157c4baf5e3f5e73513dd Mon Sep 17 00:00:00 2001 From: Marijn Haverbeke Date: Thu, 4 Jul 2013 08:32:26 +0200 Subject: [PATCH 1294/5780] [matchtags addon] Fix inefficiency Issue #1653 --- addon/edit/matchtags.js | 10 +++++++--- addon/fold/xml-fold.js | 6 ++---- 2 files changed, 9 insertions(+), 7 deletions(-) diff --git a/addon/edit/matchtags.js b/addon/edit/matchtags.js index 66e57c8e9e..3eabfeb8d3 100644 --- a/addon/edit/matchtags.js +++ b/addon/edit/matchtags.js @@ -22,14 +22,18 @@ } function doMatchTags(cm) { + cm.state.failedTagMatch = false; cm.operation(function() { clear(cm); var cur = cm.getCursor(), range = cm.getViewport(); range.from = Math.min(range.from, cur.line); range.to = Math.max(cur.line + 1, range.to); var match = CodeMirror.findMatchingTag(cm, cur, range); - if (cm.state.failedTagMatch = !match) return; + if (!match) return; var other = match.at == "close" ? match.open : match.close; - cm.state.matchedTag = cm.markText(other.from, other.to, {className: "CodeMirror-matchingtag"}); + if (other) + cm.state.matchedTag = cm.markText(other.from, other.to, {className: "CodeMirror-matchingtag"}); + else + cm.state.failedTagMatch = true; }); } @@ -41,7 +45,7 @@ var found = CodeMirror.findMatchingTag(cm, cm.getCursor()); if (found) { var other = found.at == "close" ? found.open : found.close; - cm.setSelection(other.to, other.from); + if (other) cm.setSelection(other.to, other.from); } }; })(); diff --git a/addon/fold/xml-fold.js b/addon/fold/xml-fold.js index 08a87bd0a0..5707701aa5 100644 --- a/addon/fold/xml-fold.js +++ b/addon/fold/xml-fold.js @@ -146,12 +146,10 @@ var here = {from: Pos(iter.line, iter.ch), to: to, tag: start[2]}; if (start[1]) { // closing tag - var open = findMatchingOpen(iter, start[2]); - return open && {open: open, close: here, at: "close"}; + return {open: findMatchingOpen(iter, start[2]), close: here, at: "close"}; } else { // opening tag iter = new Iter(cm, to.line, to.ch, range); - var close = findMatchingClose(iter, start[2]); - return close && {open: here, close: close, at: "open"}; + return {open: here, close: findMatchingClose(iter, start[2]), at: "open"}; } }; From 5e706977b5f67d250c12f17ba71cbd1784783b29 Mon Sep 17 00:00:00 2001 From: Marijn Haverbeke Date: Thu, 4 Jul 2013 10:58:31 +0200 Subject: [PATCH 1295/5780] [foldgutter addon] Add --- addon/fold/foldcode.js | 3 + addon/fold/foldgutter.js | 122 +++++++++++++++++++++++++++++++++++++++ demo/folding.html | 30 ++++++++-- doc/manual.html | 33 ++++++++++- 4 files changed, 181 insertions(+), 7 deletions(-) create mode 100644 addon/fold/foldgutter.js diff --git a/addon/fold/foldcode.js b/addon/fold/foldcode.js index d83a0f49dc..99b4c79cf5 100644 --- a/addon/fold/foldcode.js +++ b/addon/fold/foldcode.js @@ -16,7 +16,9 @@ if (marks[i].__isFold) { if (!allowFolded) return null; range.cleared = true; + var found = marks[i].find(); marks[i].clear(); + CodeMirror.signal(cm, "unfold", cm, found.from, found.to); } } return range; @@ -36,6 +38,7 @@ clearOnEnter: true, __isFold: true }); + CodeMirror.signal(cm, "fold", cm, range.from, range.to); } function makeWidget(options) { diff --git a/addon/fold/foldgutter.js b/addon/fold/foldgutter.js new file mode 100644 index 0000000000..b809c93f56 --- /dev/null +++ b/addon/fold/foldgutter.js @@ -0,0 +1,122 @@ +(function() { + "use strict"; + + CodeMirror.defineOption("foldGutter", false, function(cm, val, old) { + if (old && old != CodeMirror.Init) { + cm.clearGutter(cm.state.foldGutter.options.gutter); + cm.state.foldGutter = null; + cm.off("gutterClick", onGutterClick); + cm.off("change", onChange); + cm.off("viewportChange", onViewportChange); + cm.off("fold", onFold); + cm.off("unfold", onFold); + } + if (val) { + cm.state.foldGutter = new State(parseOptions(val)); + updateInViewport(cm); + cm.on("gutterClick", onGutterClick); + cm.on("change", onChange); + cm.on("viewportChange", onViewportChange); + cm.on("fold", onFold); + cm.on("unfold", onFold); + } + }); + + var Pos = CodeMirror.Pos; + + function State(options) { + this.options = options; + this.from = this.to = 0; + } + + function parseOptions(opts) { + if (opts === true) opts = {}; + if (opts.gutter == null) opts.gutter = "CodeMirror-foldgutter"; + if (opts.indicatorOpen == null) opts.indicatorOpen = "CodeMirror-foldgutter-open"; + if (opts.indicatorFolded == null) opts.indicatorFolded = "CodeMirror-foldgutter-folded"; + return opts; + } + + function isFolded(cm, line) { + var marks = cm.findMarksAt(Pos(line)); + for (var i = 0; i < marks.length; ++i) + if (marks[i].__isFold && marks[i].find().from.line == line) return true; + } + + function marker(spec) { + if (typeof spec == "string") { + var elt = document.createElement("div"); + elt.className = spec; + return elt; + } else { + return spec.cloneNode(true); + } + } + + function updateFoldInfo(cm, from, to) { + var opts = cm.state.foldGutter.options, cur = from; + cm.eachLine(from, to, function(line) { + var mark = null; + if (isFolded(cm, cur)) { + mark = marker(opts.indicatorFolded); + } else { + var pos = Pos(cur, 0), func = opts.rangeFinder || cm.getHelper(pos, "fold"); + var range = func && func(cm, pos); + if (range && range.from.line + 1 < range.to.line) + mark = marker(opts.indicatorOpen); + } + cm.setGutterMarker(line, opts.gutter, mark); + ++cur; + }); + } + + function updateInViewport(cm) { + var vp = cm.getViewport(), state = cm.state.foldGutter; + if (!state) return; + cm.operation(function() { + updateFoldInfo(cm, vp.from, vp.to); + }); + state.from = vp.from; state.to = vp.to; + } + + function onGutterClick(cm, line, gutter) { + var opts = cm.state.foldGutter.options; + if (gutter != opts.gutter) return; + cm.foldCode(Pos(line, 0), opts.rangeFinder); + } + + function onChange(cm) { + var state = cm.state.foldGutter; + state.from = state.to = 0; + clearTimeout(state.changeUpdate); + state.changeUpdate = setTimeout(function() { updateInViewport(cm); }, 600); + } + + function onViewportChange(cm) { + var state = cm.state.foldGutter; + clearTimeout(state.changeUpdate); + state.changeUpdate = setTimeout(function() { + var vp = cm.getViewport(); + if (state.from == state.to || vp.from - state.to > 20 || state.from - vp.to > 20) { + updateInViewport(cm); + } else { + cm.operation(function() { + if (vp.from < state.from) { + updateFoldInfo(cm, vp.from, state.from); + state.from = vp.from; + } + if (vp.to > state.to) { + updateFoldInfo(cm, state.to, vp.to); + state.to = vp.to; + } + }); + } + }, 400); + } + + function onFold(cm, from) { + var state = cm.state.foldGutter, line = from.line; + if (line >= state.from && line < state.to) + updateFoldInfo(cm, line, line + 1); + } +})(); diff --git a/demo/folding.html b/demo/folding.html index d9823cf377..e6cecf26d0 100644 --- a/demo/folding.html +++ b/demo/folding.html @@ -6,6 +6,7 @@ + @@ -21,14 +22,29 @@ line-height: .3; cursor: pointer; } + .CodeMirror-foldgutter { + width: .7em; + } + .CodeMirror-foldgutter-open, + .CodeMirror-foldgutter-folded { + color: #555; + cursor: pointer; + } + .CodeMirror-foldgutter-open:after { + content: "\25BE"; + } + .CodeMirror-foldgutter-folded:after { + content: "\25B8"; + }

    CodeMirror: Code Folding Demo

    Demonstration of code folding using the code - in foldcode.js. - Press ctrl-q or click on the gutter to fold a block, again + in foldcode.js + and foldgutter.js. + Press ctrl-q or click on the gutter markers to fold a block, again to unfold.

    JavaScript:
    @@ -47,18 +63,20 @@

    CodeMirror: Code Folding Demo

    mode: "javascript", lineNumbers: true, lineWrapping: true, - extraKeys: {"Ctrl-Q": function(cm){ cm.foldCode(cm.getCursor()); }} + extraKeys: {"Ctrl-Q": function(cm){ cm.foldCode(cm.getCursor()); }}, + foldGutter: true, + gutters: ["CodeMirror-linenumbers", "CodeMirror-foldgutter"] }); - editor.on("gutterClick", function(cm, n) { cm.foldCode(CodeMirror.Pos(n, 0)); }); editor.foldCode(CodeMirror.Pos(8, 0)); window.editor_html = CodeMirror.fromTextArea(te_html, { mode: "text/html", lineNumbers: true, lineWrapping: true, - extraKeys: {"Ctrl-Q": function(cm){ cm.foldCode(cm.getCursor()); }} + extraKeys: {"Ctrl-Q": function(cm){ cm.foldCode(cm.getCursor()); }}, + foldGutter: true, + gutters: ["CodeMirror-linenumbers", "CodeMirror-foldgutter"] }); - editor_html.on("gutterClick", function(cm, n) { cm.foldCode(CodeMirror.Pos(n, 0)); }); editor_html.foldCode(CodeMirror.Pos(13, 0)); editor_html.foldCode(CodeMirror.Pos(1, 0)); }; diff --git a/doc/manual.html b/doc/manual.html index 68be9718f6..12e2af7359 100644 --- a/doc/manual.html +++ b/doc/manual.html @@ -1432,7 +1432,7 @@

    Mode, state, and token-related methods

    unstyled tokens, and a string, potentially containing multiple space-separated style names, otherwise.
    -
    cm.getHelperAt(pos: {line, ch}, type: string) → helper
    +
    cm.getHelper(pos: {line, ch}, type: string) → helper
    Fetch appropriate helper for the given position. Helpers provide a way to look up functionality appropriate for a mode. The type argument provides the helper namespace @@ -1783,6 +1783,37 @@

    Addons

    See the demo for an example.
    +
    fold/foldgutter.js
    +
    Provides an option foldGutter, which can be + used to create a gutter with markers indicating the blocks that + can be folded. Create a gutter using + the gutters option, + giving it the class CodeMirror-foldgutter or + something else if you configure the addon to use a different + class, and this addon will show markers next to folded and + foldable blocks, and handle clicks in this gutter. The option + can be either set to true, or an object containing + the following optional option fields: +
    +
    gutter: string
    +
    The CSS class of the gutter. Defaults + to "CodeMirror-foldgutter". You will have to + style this yourself to give it a width (and possibly a + background).
    +
    indicatorOpen: string | Element
    +
    A CSS class or DOM element to be used as the marker for + open, foldable blocks. Defaults + to "CodeMirror-foldgutter-open".
    +
    indicatorFolded: string | Element
    +
    A CSS class or DOM element to be used as the marker for + folded blocks. Defaults to "CodeMirror-foldgutter-folded".
    +
    rangeFinder: fn(CodeMirror, Pos)
    +
    The range-finder function to use when determining whether + something can be folded. When not + given, getHelper will be + used to determine a default.
    +
    +
    runmode/runmode.js
    Can be used to run a CodeMirror mode over text without actually opening an editor instance. From 7499eace15c819055b9693daf375d9dcf4512aee Mon Sep 17 00:00:00 2001 From: Marijn Haverbeke Date: Thu, 4 Jul 2013 11:00:56 +0200 Subject: [PATCH 1296/5780] Rename combineRangeFinders to CodeMirror.fold.combine --- addon/fold/foldcode.js | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/addon/fold/foldcode.js b/addon/fold/foldcode.js index 99b4c79cf5..51a7f5fd0e 100644 --- a/addon/fold/foldcode.js +++ b/addon/fold/foldcode.js @@ -60,7 +60,7 @@ // New-style interface CodeMirror.defineExtension("foldCode", function(pos, options) { doFold(this, pos, options); }); - CodeMirror.combineRangeFinders = function() { + CodeMirror.fold.combine = function() { var funcs = Array.prototype.slice.call(arguments, 0); return function(cm, start) { for (var i = 0; i < funcs.length; ++i) { From d9a839fbe29a4ad186f1175458480f3557295587 Mon Sep 17 00:00:00 2001 From: Marijn Haverbeke Date: Thu, 4 Jul 2013 11:21:51 +0200 Subject: [PATCH 1297/5780] [match-highlighter addon] Improve showToken option --- addon/search/match-highlighter.js | 20 +++++++++++--------- demo/matchhighlighter.html | 2 +- doc/manual.html | 13 ++++++++----- 3 files changed, 20 insertions(+), 15 deletions(-) diff --git a/addon/search/match-highlighter.js b/addon/search/match-highlighter.js index 212167580a..3df6985984 100644 --- a/addon/search/match-highlighter.js +++ b/addon/search/match-highlighter.js @@ -55,11 +55,13 @@ cm.removeOverlay(state.overlay); state.overlay = null; } - if (!cm.somethingSelected() && state.showToken) { - var tok = cm.getTokenAt(cm.getCursor()).string; - if (/\w/.test(tok)) - cm.addOverlay(state.overlay = makeOverlay(tok, true, state.style)); + var re = state.showToken === true ? /[\w$]/ : state.showToken; + var cur = cm.getCursor(), line = cm.getLine(cur.line), start = cur.ch, end = start; + while (start && re.test(line.charAt(start - 1))) --start; + while (end < line.length && re.test(line.charAt(end))) ++end; + if (start < end) + cm.addOverlay(state.overlay = makeOverlay(line.slice(start, end), re, state.style)); return; } if (cm.getCursor("head").line != cm.getCursor("anchor").line) return; @@ -69,15 +71,15 @@ }); } - function boundariesAround(stream) { - return (stream.start || /.\b./.test(stream.string.slice(stream.start - 1, stream.start + 1))) && - (stream.pos == stream.string.length || /.\b./.test(stream.string.slice(stream.pos - 1, stream.pos + 1))); + function boundariesAround(stream, re) { + return (!stream.start || !re.test(stream.string.charAt(stream.start - 1))) && + (stream.pos == stream.string.length || !re.test(stream.string.charAt(stream.pos))); } - function makeOverlay(query, wordBoundaries, style) { + function makeOverlay(query, hasBoundary, style) { return {token: function(stream) { if (stream.match(query) && - (!wordBoundaries || boundariesAround(stream))) + (!hasBoundary || boundariesAround(stream, hasBoundary))) return style; stream.next(); stream.skipTo(query.charAt(0)) || stream.skipToEnd(); diff --git a/demo/matchhighlighter.html b/demo/matchhighlighter.html index c574fbae81..9d2fdd098a 100644 --- a/demo/matchhighlighter.html +++ b/demo/matchhighlighter.html @@ -28,7 +28,7 @@

    CodeMirror: Match Highlighter Demo

    diff --git a/doc/manual.html b/doc/manual.html index 12e2af7359..5b08097a90 100644 --- a/doc/manual.html +++ b/doc/manual.html @@ -1812,7 +1812,8 @@

    Addons

    something can be folded. When not given, getHelper will be used to determine a default.
    -

    + + Demo here.
    runmode/runmode.js
    Can be used to run a CodeMirror mode over text without @@ -1994,10 +1995,12 @@

    Addons

    minimum amount of selected characters that triggers a highlight (default 2), style, for the style to be used to highlight the matches (default "matchhighlight", - which will correspond to CSS class cm-matchhighlight), - and showToken which, when enabled, causes the - current token to be highlighted when nothing is selected - (defaults to off). + which will correspond to CSS + class cm-matchhighlight), + and showToken which can be set to true + or to a regexp matching the characters that make up a word. When + enabled, it causes the current word to be highlighted when + nothing is selected (defaults to off). Demo here.
    lint/lint.js
    From b2b6b54b6e4617acc46a36cb1d924712c03f5237 Mon Sep 17 00:00:00 2001 From: Aurelian Oancea Date: Thu, 4 Jul 2013 16:09:28 +0200 Subject: [PATCH 1298/5780] [midnight theme] fixed syntax error --- theme/midnight.css | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/theme/midnight.css b/theme/midnight.css index f10edf932a..bc2d49be1c 100644 --- a/theme/midnight.css +++ b/theme/midnight.css @@ -2,7 +2,7 @@ /**/ .cm-s-midnight span.CodeMirror-matchhighlight { background: #494949 } -.cm-s-midnight.CodeMirror-focused span.CodeMirror-matchhighlight { background: #314D67; !important } +.cm-s-midnight.CodeMirror-focused span.CodeMirror-matchhighlight { background: #314D67 !important; } /**/ .cm-s-midnight .activeline {background: #253540 !important;} From 8b8b7b061e441f8409b080ade327cacbc732272c Mon Sep 17 00:00:00 2001 From: Marijn Haverbeke Date: Fri, 5 Jul 2013 08:43:04 +0200 Subject: [PATCH 1299/5780] [xml-fold addon] Add fast-case return to findMatchingTag --- addon/fold/xml-fold.js | 1 + lib/codemirror.js | 1 + 2 files changed, 2 insertions(+) diff --git a/addon/fold/xml-fold.js b/addon/fold/xml-fold.js index 5707701aa5..b87da9088c 100644 --- a/addon/fold/xml-fold.js +++ b/addon/fold/xml-fold.js @@ -140,6 +140,7 @@ CodeMirror.findMatchingTag = function(cm, pos, range) { var iter = new Iter(cm, pos.line, pos.ch, range); + if (!tagAt(iter, iter.ch)) return; var end = toTagEnd(iter), to = end && Pos(iter.line, iter.ch); var start = end && toTagStart(iter); if (!end || end == "selfClose" || !start || cmp(iter, pos) > 0) return; diff --git a/lib/codemirror.js b/lib/codemirror.js index 659f8c96bc..96e579e753 100644 --- a/lib/codemirror.js +++ b/lib/codemirror.js @@ -2862,6 +2862,7 @@ window.CodeMirror = (function() { pos = clipPos(this.doc, pos); var styles = getLineStyles(this, getLine(this.doc, pos.line)); var before = 0, after = (styles.length - 1) / 2, ch = pos.ch; + if (ch == 0) return styles[2]; for (;;) { var mid = (before + after) >> 1; if ((mid ? styles[mid * 2 - 1] : 0) >= ch) after = mid; From 4f43c8ce8780c531b6a1772e1e3eb34dcbfe037f Mon Sep 17 00:00:00 2001 From: Marijn Haverbeke Date: Fri, 5 Jul 2013 16:11:26 +0200 Subject: [PATCH 1300/5780] Add getModeAt method Use it to prevent some unneeded state computation. --- addon/comment/comment.js | 6 +++--- addon/fold/foldcode.js | 4 ++-- doc/manual.html | 10 ++++++++-- lib/codemirror.js | 9 +++++++-- 4 files changed, 20 insertions(+), 9 deletions(-) diff --git a/addon/comment/comment.js b/addon/comment/comment.js index 3c76744654..cd2123e175 100644 --- a/addon/comment/comment.js +++ b/addon/comment/comment.js @@ -17,7 +17,7 @@ CodeMirror.defineExtension("lineComment", function(from, to, options) { if (!options) options = noOptions; - var self = this, mode = CodeMirror.innerMode(self.getMode(), self.getTokenAt(from).state).mode; + var self = this, mode = self.getModeAt(from); var commentString = options.lineComment || mode.lineComment; if (!commentString) { if (options.blockCommentStart || mode.blockCommentStart) { @@ -52,7 +52,7 @@ CodeMirror.defineExtension("blockComment", function(from, to, options) { if (!options) options = noOptions; - var self = this, mode = CodeMirror.innerMode(self.getMode(), self.getTokenAt(from).state).mode; + var self = this, mode = self.getModeAt(from); var startString = options.blockCommentStart || mode.blockCommentStart; var endString = options.blockCommentEnd || mode.blockCommentEnd; if (!startString || !endString) { @@ -85,7 +85,7 @@ CodeMirror.defineExtension("uncomment", function(from, to, options) { if (!options) options = noOptions; - var self = this, mode = CodeMirror.innerMode(self.getMode(), self.getTokenAt(from).state).mode; + var self = this, mode = self.getModeAt(from); var end = Math.min(to.line, self.lastLine()), start = Math.min(from.line, end); // Try finding line comments diff --git a/addon/fold/foldcode.js b/addon/fold/foldcode.js index 51a7f5fd0e..69513cb8bb 100644 --- a/addon/fold/foldcode.js +++ b/addon/fold/foldcode.js @@ -60,7 +60,7 @@ // New-style interface CodeMirror.defineExtension("foldCode", function(pos, options) { doFold(this, pos, options); }); - CodeMirror.fold.combine = function() { + CodeMirror.registerHelper("fold", "combine", function() { var funcs = Array.prototype.slice.call(arguments, 0); return function(cm, start) { for (var i = 0; i < funcs.length; ++i) { @@ -68,5 +68,5 @@ if (found) return found; } }; - }; + }); })(); diff --git a/doc/manual.html b/doc/manual.html index 5b08097a90..64c684b10b 100644 --- a/doc/manual.html +++ b/doc/manual.html @@ -1401,11 +1401,17 @@

    Mode, state, and token-related methods

    doc.getMode() → object
    -
    Gets the mode object for the editor. Note that this is - distinct from getOption("mode"), which gives you +
    Gets the (outer) mode object for the editor. Note that this + is distinct from getOption("mode"), which gives you the mode specification, rather than the resolved, instantiated mode object.
    +
    doc.getModeAt(pos: {line, ch}) → object
    +
    Gets the inner mode at a given position. This will return + the same as getMode for + simple modes, but will return an inner mode for nesting modes + (such as htmlmixed).
    +
    cm.getTokenAt(pos: {line, ch}, ?precise: boolean) → object
    Retrieves information about the token the current mode found before the given position (a {line, ch} object). The diff --git a/lib/codemirror.js b/lib/codemirror.js index 96e579e753..1caa59d2d2 100644 --- a/lib/codemirror.js +++ b/lib/codemirror.js @@ -2871,10 +2871,15 @@ window.CodeMirror = (function() { } }, + getModeAt: function(pos) { + var mode = this.doc.mode; + if (!mode.innerMode) return mode; + return CodeMirror.innerMode(mode, this.getTokenAt(pos).state).mode; + }, + getHelper: function(pos, type) { if (!helpers.hasOwnProperty(type)) return; - var state = this.getTokenAt(pos).state, help = helpers[type]; - var mode = CodeMirror.innerMode(this.getMode(), state).mode; + var help = helpers[type], mode = this.getModeAt(pos); return mode[type] && help[mode[type]] || mode.helperType && help[mode.helperType] || help[mode.name]; From 76894881e46571dc5bb8df953b32d71dc0113a0a Mon Sep 17 00:00:00 2001 From: Brandon Frohs Date: Sun, 7 Jul 2013 21:11:40 -0400 Subject: [PATCH 1301/5780] [markdown] Highlight trailing spaces that affect line breaks --- mode/markdown/index.html | 7 ++++++- mode/markdown/markdown.js | 27 ++++++++++++++++++++++++++- mode/markdown/test.js | 14 ++++++++++++++ 3 files changed, 46 insertions(+), 2 deletions(-) diff --git a/mode/markdown/index.html b/mode/markdown/index.html index 6f97b10e73..bb785b13fc 100644 --- a/mode/markdown/index.html +++ b/mode/markdown/index.html @@ -8,7 +8,12 @@ - + diff --git a/mode/markdown/markdown.js b/mode/markdown/markdown.js index 72b0d6ced2..bf1750d5b6 100644 --- a/mode/markdown/markdown.js +++ b/mode/markdown/markdown.js @@ -103,6 +103,9 @@ CodeMirror.defineMode("markdown", function(cmCfg, modeCfg) { state.f = inlineNormal; state.block = blockNormal; } + // Reset state.trailingSpace + state.trailingSpace = 0; + state.trailingSpaceNewLine = false; // Mark this line as blank state.thisLineHasContent = false; return null; @@ -217,6 +220,12 @@ CodeMirror.defineMode("markdown", function(cmCfg, modeCfg) { } } + if (state.trailingSpaceNewLine) { + styles.push("trailing-space-new-line"); + } else if (state.trailingSpace) { + styles.push("trailing-space-" + (state.trailingSpace % 2 ? "a" : "b")); + } + return styles.length ? styles.join(' ') : null; } @@ -369,6 +378,14 @@ CodeMirror.defineMode("markdown", function(cmCfg, modeCfg) { } } + if (ch === ' ') { + if (stream.match(/ +$/, false)) { + state.trailingSpace++; + } else if (state.trailingSpace) { + state.trailingSpaceNewLine = true; + } + } + return getType(state); } @@ -453,7 +470,9 @@ CodeMirror.defineMode("markdown", function(cmCfg, modeCfg) { taskList: false, list: false, listDepth: 0, - quote: 0 + quote: 0, + trailingSpace: 0, + trailingSpaceNewLine: false }; }, @@ -481,6 +500,8 @@ CodeMirror.defineMode("markdown", function(cmCfg, modeCfg) { list: s.list, listDepth: s.listDepth, quote: s.quote, + trailingSpace: s.trailingSpace, + trailingSpaceNewLine: s.trailingSpaceNewLine, md_inside: s.md_inside }; }, @@ -504,6 +525,10 @@ CodeMirror.defineMode("markdown", function(cmCfg, modeCfg) { // Reset state.code state.code = false; + // Reset state.trailingSpace + state.trailingSpace = 0; + state.trailingSpaceNewLine = false; + state.f = state.block; var indentation = stream.match(/^\s*/, true)[0].replace(/\t/g, ' ').length; var difference = Math.floor((indentation - state.indentation) / 4) * 4; diff --git a/mode/markdown/test.js b/mode/markdown/test.js index 99ae056132..f167917289 100644 --- a/mode/markdown/test.js +++ b/mode/markdown/test.js @@ -5,6 +5,20 @@ MT("plainText", "foo"); + // Don't style single trailing space + MT("trailingSpace1", + "foo "); + + // Two or more trailing spaces should be styled with line break character + MT("trailingSpace2", + "foo[trailing-space-a ][trailing-space-new-line ]"); + + MT("trailingSpace3", + "foo[trailing-space-a ][trailing-space-b ][trailing-space-new-line ]"); + + MT("trailingSpace4", + "foo[trailing-space-a ][trailing-space-b ][trailing-space-a ][trailing-space-new-line ]"); + // Code blocks using 4 spaces (regardless of CodeMirror.tabSize value) MT("codeBlocksUsing4Spaces", " [comment foo]"); From 25e4be044206c7b26b003df7cb8e2fcb0cbe71cf Mon Sep 17 00:00:00 2001 From: Marijn Haverbeke Date: Mon, 8 Jul 2013 09:22:56 +0200 Subject: [PATCH 1302/5780] [javascript mode] Don't get confused by empty brackets --- mode/javascript/javascript.js | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/mode/javascript/javascript.js b/mode/javascript/javascript.js index f5507ce699..d51745d837 100644 --- a/mode/javascript/javascript.js +++ b/mode/javascript/javascript.js @@ -311,7 +311,7 @@ CodeMirror.defineMode("javascript", function(config, parserConfig) { if (type == ";") return; if (type == "(") return cont(pushlex(")", "call"), commasep(expressionNoComma, ")"), poplex, me); if (type == ".") return cont(property, me); - if (type == "[") return cont(pushlex("]"), expression, expect("]"), poplex, me); + if (type == "[") return cont(pushlex("]"), maybeexpression, expect("]"), poplex, me); } function maybelabel(type) { if (type == ":") return cont(poplex, statement); From e99efe2543edea09ce6780e826288add7ddec48d Mon Sep 17 00:00:00 2001 From: Marijn Haverbeke Date: Tue, 9 Jul 2013 10:36:05 +0200 Subject: [PATCH 1303/5780] [javascript mode] Fix bad indentation below if Closes #1662 --- mode/javascript/javascript.js | 24 ++++++++++++------------ 1 file changed, 12 insertions(+), 12 deletions(-) diff --git a/mode/javascript/javascript.js b/mode/javascript/javascript.js index d51745d837..3d04603c28 100644 --- a/mode/javascript/javascript.js +++ b/mode/javascript/javascript.js @@ -258,17 +258,17 @@ CodeMirror.defineMode("javascript", function(config, parserConfig) { if (type == "keyword b") return cont(pushlex("form"), statement, poplex); if (type == "{") return cont(pushlex("}"), block, poplex); if (type == ";") return cont(); - if (type == "if") return cont(pushlex("form"), expression, statement, poplex, maybeelse(cx.state.indented)); + if (type == "if") return cont(pushlex("form"), expression, statement, poplex, maybeelse); if (type == "function") return cont(functiondef); if (type == "for") return cont(pushlex("form"), expect("("), pushlex(")"), forspec1, expect(")"), - poplex, statement, poplex); + poplex, statement, poplex); if (type == "variable") return cont(pushlex("stat"), maybelabel); if (type == "switch") return cont(pushlex("form"), expression, pushlex("}", "switch"), expect("{"), - block, poplex, poplex); + block, poplex, poplex); if (type == "case") return cont(expression, expect(":")); if (type == "default") return cont(expect(":")); if (type == "catch") return cont(pushlex("form"), pushcontext, expect("("), funarg, expect(")"), - statement, poplex, popcontext); + statement, poplex, popcontext); return pass(pushlex("stat"), expression, expect(";"), poplex); } function expression(type) { @@ -373,14 +373,8 @@ CodeMirror.defineMode("javascript", function(config, parserConfig) { if (value == "=") return cont(expressionNoComma, vardef2); if (type == ",") return cont(vardef1); } - function maybeelse(indent) { - return function(type, value) { - if (type == "keyword b" && value == "else") { - cx.state.lexical = new JSLexical(indent, 0, "form", null, cx.state.lexical); - return cont(statement, poplex); - } - return pass(); - }; + function maybeelse(type, value) { + if (type == "keyword b" && value == "else") return cont(pushlex("form"), statement, poplex); } function forspec1(type) { if (type == "var") return cont(vardef1, expect(";"), forspec2); @@ -441,6 +435,12 @@ CodeMirror.defineMode("javascript", function(config, parserConfig) { if (state.tokenize == jsTokenComment) return CodeMirror.Pass; if (state.tokenize != jsTokenBase) return 0; var firstChar = textAfter && textAfter.charAt(0), lexical = state.lexical; + // Kludge to prevent 'maybelse' from blocking lexical scope pops + for (var i = state.cc.length - 1; i >= 0; --i) { + var c = state.cc[i]; + if (c == poplex) lexical = lexical.prev; + else if (c != maybeelse || /^else\b/.test(textAfter)) break; + } if (lexical.type == "stat" && firstChar == "}") lexical = lexical.prev; if (statementIndent && lexical.type == ")" && lexical.prev.type == "stat") lexical = lexical.prev; From c921c10de61a733a55015b5607f1c360baf2e285 Mon Sep 17 00:00:00 2001 From: Marijn Haverbeke Date: Tue, 9 Jul 2013 11:09:30 +0200 Subject: [PATCH 1304/5780] Add another exception to the spanAffectsWrapping hack And turn the hack off in recent Chrome builds. Issue #1663 --- lib/codemirror.js | 8 +++++--- 1 file changed, 5 insertions(+), 3 deletions(-) diff --git a/lib/codemirror.js b/lib/codemirror.js index 1caa59d2d2..ab1cce5fb6 100644 --- a/lib/codemirror.js +++ b/lib/codemirror.js @@ -5391,10 +5391,12 @@ window.CodeMirror = (function() { spanAffectsWrapping = function(str, i) { return /\-[^ \-?]|\?[^ !\'\"\),.\-\/:;\?\]\}]/.test(str.slice(i - 1, i + 1)); }; - else if (webkit) + else if (webkit && !/Chrome\/(?:29|[3-9]\d|\d\d\d)\./.test(navigator.userAgent)) spanAffectsWrapping = function(str, i) { - if (i > 1 && str.charCodeAt(i - 1) == 45 && /\w/.test(str.charAt(i - 2)) && /[^\-?\.]/.test(str.charAt(i))) - return true; + if (i > 1 && str.charCodeAt(i - 1) == 45) { + if (/\w/.test(str.charAt(i - 2)) && /[^\-?\.]/.test(str.charAt(i))) return true; + if (i > 2 && /[\d\.,]/.test(str.charAt(i - 2)) && /[\d\.,]/.test(str.charAt(i))) return false; + } return /[~!#%&*)=+}\]|\"\.>,:;][({[<]|-[^\-?\.\u2010-\u201f\u2026]|\?[\w~`@#$%\^&*(_=+{[|><]|…[\w~`@#$%\^&*(_=+{[><]/.test(str.slice(i - 1, i + 1)); }; From 8068b3d55d397ad14350e6b1593fa53095927f4b Mon Sep 17 00:00:00 2001 From: Marijn Haverbeke Date: Thu, 11 Jul 2013 08:09:06 +0200 Subject: [PATCH 1305/5780] Fix removeKeyMap for string keymap values --- lib/codemirror.js | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/lib/codemirror.js b/lib/codemirror.js index ab1cce5fb6..330a09e385 100644 --- a/lib/codemirror.js +++ b/lib/codemirror.js @@ -2798,7 +2798,7 @@ window.CodeMirror = (function() { removeKeyMap: function(map) { var maps = this.state.keyMaps; for (var i = 0; i < maps.length; ++i) - if ((typeof map == "string" ? maps[i].name : maps[i]) == map) { + if (maps[i] == map || (typeof maps[i] != "string" && maps[i].name == map)) { maps.splice(i, 1); return true; } From 4651de869bcd7a1503bbe14540aebc3a8879705f Mon Sep 17 00:00:00 2001 From: Matthias BUSSONNIER Date: Tue, 25 Jun 2013 17:12:09 +0200 Subject: [PATCH 1306/5780] [python mode] Add MIME for Cython dialect --- doc/modes.html | 1 + index.html | 1 + mode/meta.js | 3 ++- mode/python/index.html | 46 +++++++++++++++++++++++++++++++++++++++--- mode/python/python.js | 16 +++++++++++++++ 5 files changed, 63 insertions(+), 4 deletions(-) diff --git a/doc/modes.html b/doc/modes.html index 69c7d5e9ad..1c76f2fb1b 100644 --- a/doc/modes.html +++ b/doc/modes.html @@ -31,6 +31,7 @@

    { } CodeMi
  • CoffeeScript
  • Common Lisp
  • CSS
  • +
  • Cython
  • D
  • diff
  • ECL
  • diff --git a/index.html b/index.html index 6a9dbcb603..544aac1200 100644 --- a/index.html +++ b/index.html @@ -40,6 +40,7 @@

    Supported modes:

  • CoffeeScript
  • Common Lisp
  • CSS
  • +
  • Cython
  • D
  • diff
  • ECL
  • diff --git a/mode/meta.js b/mode/meta.js index cb1051e68f..1ea0f672b0 100644 --- a/mode/meta.js +++ b/mode/meta.js @@ -16,7 +16,7 @@ CodeMirror.modeInfo = [ {name: 'ECL', mime: 'text/x-ecl', mode: 'ecl'}, {name: 'Erlang', mime: 'text/x-erlang', mode: 'erlang'}, {name: 'Gas', mime: 'text/x-gas', mode: 'gas'}, - {name: 'GitHub Flavored Markdown', mode: 'gfm'}, + {name: 'GitHub Flavored Markdown', mime: 'text/x-gfm', mode: 'gfm'}, {name: 'GO', mime: 'text/x-go', mode: 'go'}, {name: 'Groovy', mime: 'text/x-groovy', mode: 'groovy'}, {name: 'Haskell', mime: 'text/x-haskell', mode: 'haskell'}, @@ -47,6 +47,7 @@ CodeMirror.modeInfo = [ {name: 'Plain Text', mime: 'text/plain', mode: 'null'}, {name: 'Properties files', mime: 'text/x-properties', mode: 'clike'}, {name: 'Python', mime: 'text/x-python', mode: 'python'}, + {name: 'Cython', mime: 'text/x-cython', mode: 'python'}, {name: 'R', mime: 'text/x-rsrc', mode: 'r'}, {name: 'reStructuredText', mime: 'text/x-rst', mode: 'rst'}, {name: 'Ruby', mime: 'text/x-ruby', mode: 'ruby'}, diff --git a/mode/python/index.html b/mode/python/index.html index 4244c6fc5a..1229a8bf83 100644 --- a/mode/python/index.html +++ b/mode/python/index.html @@ -12,7 +12,7 @@

    CodeMirror: Python mode

    - +

    Python mode

    + + +

    Cython mode

    + +
    + -

    Configuration Options:

    +

    Configuration Options for Python mode:

    • version - 2/3 - The version of Python to recognize. Default is 2.
    • 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.
    • @@ -127,9 +165,11 @@

      Advanced Configuration Options:

    • doubleDelimiters - RegEx - Regular Expressoin for double delimiters matching, default :
      ^((\\+=)|(\\-=)|(\\*=)|(%=)|(/=)|(&=)|(\\|=)|(\\^=))
    • tripleDelimiters - RegEx - Regular Expression for triple delimiters matching, default :
      ^((//=)|(>>=)|(<<=)|(\\*\\*=))
    • identifiers - RegEx - Regular Expression for identifier, default :
      ^[_A-Za-z][_A-Za-z0-9]*
    • +
    • extra_keywords - list of string - List of extra words ton consider as keywords
    • +
    • extra_builtins - list of string - List of extra words ton consider as builtins
    -

    MIME types defined: text/x-python.

    +

    MIME types defined: text/x-python and text/x-cython.

    diff --git a/mode/python/python.js b/mode/python/python.js index 565d963fdf..2c6d1d83c7 100644 --- a/mode/python/python.js +++ b/mode/python/python.js @@ -36,6 +36,12 @@ CodeMirror.defineMode("python", function(conf, parserConf) { var py3 = {'builtins': ['ascii', 'bytes', 'exec', 'print'], 'keywords': ['nonlocal', 'False', 'True', 'None']}; + if(parserConf.extra_keywords != undefined){ + commonkeywords = commonkeywords.concat(parserConf.extra_keywords); + } + if(parserConf.extra_builtins != undefined){ + commonBuiltins = commonBuiltins.concat(parserConf.extra_builtins); + } if (!!parserConf.version && parseInt(parserConf.version, 10) === 3) { commonkeywords = commonkeywords.concat(py3.keywords); commonBuiltins = commonBuiltins.concat(py3.builtins); @@ -340,3 +346,13 @@ CodeMirror.defineMode("python", function(conf, parserConf) { }); CodeMirror.defineMIME("text/x-python", "python"); + +var words = function(str){return str.split(' ');}; + + +CodeMirror.defineMIME("text/x-cython", { + name: "python", + extra_keywords: words("by cdef cimport cpdef ctypedef enum except"+ + "extern gil include nogil property public"+ + "readonly struct union DEF IF ELIF ELSE") +}); From 9e8199b3d604009f1a3b8df4691f9d3469c7aefe Mon Sep 17 00:00:00 2001 From: Michael Zhou Date: Tue, 9 Jul 2013 12:42:25 -0700 Subject: [PATCH 1307/5780] Fix bug in addLineWidget. Fixed clipping of the insertAt index. --- lib/codemirror.js | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/lib/codemirror.js b/lib/codemirror.js index 330a09e385..03933ff9eb 100644 --- a/lib/codemirror.js +++ b/lib/codemirror.js @@ -4108,7 +4108,7 @@ window.CodeMirror = (function() { changeLine(cm, handle, function(line) { var widgets = line.widgets || (line.widgets = []); if (widget.insertAt == null) widgets.push(widget); - else widgets.splice(Math.max(widgets.length - 1, widget.insertAt), 0, widget); + else widgets.splice(Math.min(widgets.length - 1, Math.max(0, widget.insertAt)), 0, widget); widget.line = line; if (!lineIsHidden(cm.doc, line) || widget.showIfHidden) { var aboveVisible = heightAtLine(cm, line) < cm.doc.scrollTop; From 486976e16d42ca9cf2b6b01bdd21912124fbc562 Mon Sep 17 00:00:00 2001 From: Michael Zhou Date: Tue, 9 Jul 2013 15:02:43 -0700 Subject: [PATCH 1308/5780] Fix addLineWidget documentation. Removed "pos" parameter. --- doc/manual.html | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/doc/manual.html b/doc/manual.html index 64c684b10b..a4f8bc060e 100644 --- a/doc/manual.html +++ b/doc/manual.html @@ -1253,7 +1253,7 @@

    Widget, gutter, and decoration methods

    widget again, simply use DOM methods (move it somewhere else, or call removeChild on its parent).
    -
    cm.addLineWidget(line: integer|LineHandle, node: Element, ?options: object, ?pos: integer) → LineWidget
    +
    cm.addLineWidget(line: integer|LineHandle, node: Element, ?options: object) → LineWidget
    Adds a line widget, an element shown below a line, spanning the whole of the editor's width, and moving the lines below it downwards. line should be either an integer or a From ab2b604ed8bc64788c3590683e446f38043bb671 Mon Sep 17 00:00:00 2001 From: Daniel Huigens Date: Wed, 10 Jul 2013 21:16:48 +0200 Subject: [PATCH 1309/5780] Fix selecting until the start of a bidi line --- lib/codemirror.js | 6 +++++- 1 file changed, 5 insertions(+), 1 deletion(-) diff --git a/lib/codemirror.js b/lib/codemirror.js index 03933ff9eb..6bfd652fe5 100644 --- a/lib/codemirror.js +++ b/lib/codemirror.js @@ -5483,11 +5483,15 @@ window.CodeMirror = (function() { function iterateBidiSections(order, from, to, f) { if (!order) return f(from, to, "ltr"); + var found = false; for (var i = 0; i < order.length; ++i) { var part = order[i]; - if (part.from < to && part.to > from || from == to && part.to == from) + if (part.from < to && part.to > from || from == to && part.to == from) { f(Math.max(part.from, from), Math.min(part.to, to), part.level == 1 ? "rtl" : "ltr"); + found = true; + } } + if (!found) f(from, to, "ltr"); } function bidiLeft(part) { return part.level % 2 ? part.to : part.from; } From 1a028d007a50c117775aabca1e5d932115333e9f Mon Sep 17 00:00:00 2001 From: Marijn Haverbeke Date: Thu, 11 Jul 2013 10:18:01 +0200 Subject: [PATCH 1310/5780] Support 'title' option to markText Issue #1654 --- doc/manual.html | 4 ++++ lib/codemirror.js | 19 +++++++++++-------- 2 files changed, 15 insertions(+), 8 deletions(-) diff --git a/doc/manual.html b/doc/manual.html index a4f8bc060e..cb3bc50509 100644 --- a/doc/manual.html +++ b/doc/manual.html @@ -1151,6 +1151,10 @@

    Text-marking methods

    is part of the marker.
    endStyle: string
    Equivalent to startStyle, but for the rightmost span.
    +
    title: + string
    When given, will give the nodes created + for this span a HTML title attribute with the + given value.
    shared: boolean
    When the target document is linked to other documents, you can set shared to true to make the diff --git a/lib/codemirror.js b/lib/codemirror.js index 6bfd652fe5..fe956fd81a 100644 --- a/lib/codemirror.js +++ b/lib/codemirror.js @@ -3791,7 +3791,7 @@ window.CodeMirror = (function() { } if (cm) { if (updateMaxLine) cm.curOp.updateMaxLine = true; - if (marker.className || marker.startStyle || marker.endStyle || marker.collapsed) + if (marker.className || marker.title || marker.startStyle || marker.endStyle || marker.collapsed) regChange(cm, from.line, to.line + 1); if (marker.atomic) reCheckSelection(cm); } @@ -4284,7 +4284,7 @@ window.CodeMirror = (function() { } var tokenSpecialChars = /[\t\u0000-\u0019\u00ad\u200b\u2028\u2029\uFEFF]/g; - function buildToken(builder, text, style, startStyle, endStyle) { + function buildToken(builder, text, style, startStyle, endStyle, title) { if (!text) return; if (!tokenSpecialChars.test(text)) { builder.col += text.length; @@ -4317,7 +4317,9 @@ window.CodeMirror = (function() { var fullStyle = style || ""; if (startStyle) fullStyle += startStyle; if (endStyle) fullStyle += endStyle; - return builder.pre.appendChild(elt("span", [content], fullStyle)); + var token = elt("span", [content], fullStyle); + if (title) token.title = title; + return builder.pre.appendChild(token); } builder.pre.appendChild(content); } @@ -4355,8 +4357,8 @@ window.CodeMirror = (function() { out += " "; return out; } - return function(builder, text, style, startStyle, endStyle) { - return inner(builder, text.replace(/ {3,}/, split), style, startStyle, endStyle); + return function(builder, text, style, startStyle, endStyle, title) { + return inner(builder, text.replace(/ {3,}/, split), style, startStyle, endStyle, title); }; } @@ -4392,10 +4394,10 @@ window.CodeMirror = (function() { } var len = allText.length, pos = 0, i = 1, text = "", style; - var nextChange = 0, spanStyle, spanEndStyle, spanStartStyle, collapsed; + var nextChange = 0, spanStyle, spanEndStyle, spanStartStyle, title, collapsed; for (;;) { if (nextChange == pos) { // Update current marker set - spanStyle = spanEndStyle = spanStartStyle = ""; + spanStyle = spanEndStyle = spanStartStyle = title = ""; collapsed = null; nextChange = Infinity; var foundBookmark = null; for (var j = 0; j < spans.length; ++j) { @@ -4405,6 +4407,7 @@ window.CodeMirror = (function() { if (m.className) spanStyle += " " + m.className; if (m.startStyle && sp.from == pos) spanStartStyle += " " + m.startStyle; if (m.endStyle && sp.to == nextChange) spanEndStyle += " " + m.endStyle; + if (m.title && !title) title = m.title; if (m.collapsed && (!collapsed || collapsed.marker.size < m.size)) collapsed = sp; } else if (sp.from > pos && nextChange > sp.from) { @@ -4428,7 +4431,7 @@ window.CodeMirror = (function() { if (!collapsed) { var tokenText = end > upto ? text.slice(0, upto - pos) : text; builder.addToken(builder, tokenText, style ? style + spanStyle : spanStyle, - spanStartStyle, pos + tokenText.length == nextChange ? spanEndStyle : ""); + spanStartStyle, pos + tokenText.length == nextChange ? spanEndStyle : "", title); } if (end >= upto) {text = text.slice(upto - pos); pos = upto; break;} pos = end; From 4f85a7ef97942a31b07609b11f762c785a71a34e Mon Sep 17 00:00:00 2001 From: John Snelson Date: Thu, 11 Jul 2013 09:50:43 +0100 Subject: [PATCH 1311/5780] Added SPARQL 1.1 keywords to the SPARQL mode. --- mode/sparql/sparql.js | 4 +++- 1 file changed, 3 insertions(+), 1 deletion(-) diff --git a/mode/sparql/sparql.js b/mode/sparql/sparql.js index f7237698d3..1168f23a52 100644 --- a/mode/sparql/sparql.js +++ b/mode/sparql/sparql.js @@ -9,7 +9,9 @@ CodeMirror.defineMode("sparql", function(config) { "isblank", "isliteral", "union", "a"]); var keywords = wordRegexp(["base", "prefix", "select", "distinct", "reduced", "construct", "describe", "ask", "from", "named", "where", "order", "limit", "offset", "filter", "optional", - "graph", "by", "asc", "desc"]); + "graph", "by", "asc", "desc", "as", "having", "undef", "values", "group", + "minus", "in", "not", "service", "silent", "using", "insert", "delete", + "data", "copy", "to", "move", "add", "create", "drop", "clear", "load"]); var operatorChars = /[*+\-<>=&|]/; function tokenBase(stream, state) { From ef328ed46d2a7cc5e3d47735a9725230fadc4d59 Mon Sep 17 00:00:00 2001 From: Marijn Haverbeke Date: Fri, 12 Jul 2013 10:38:17 +0200 Subject: [PATCH 1312/5780] Add convenient event methods to all event-emitting objects i.e. linehandle.on, markedrange.off, etc. --- doc/manual.html | 22 ++++++++++------------ lib/codemirror.js | 34 +++++++++++++++++++++------------- 2 files changed, 31 insertions(+), 25 deletions(-) diff --git a/doc/manual.html b/doc/manual.html index cb3bc50509..d17eb79853 100644 --- a/doc/manual.html +++ b/doc/manual.html @@ -375,14 +375,15 @@

    Configuration

    Events

    -

    A CodeMirror instance emits a number of events, which allow - client code to react to various situations. These are registered - with the on method (and - removed with the off - method). These are the events that fire on the instance object. - The name of the event is followed by the arguments that will be - passed to the handler. The instance argument always - refers to the editor instance.

    +

    Various CodeMirror-related objects emit events, which allow + client code to react to various situations. Handlers for such + events can be registed with the on + and off methods on the objects + that the event fires on.

    + +

    An editor instance fires the following events. + The instance argument always refers to the editor + itself.

    "change" (instance: CodeMirror, changeObj: object)
    @@ -497,10 +498,7 @@

    Events

    CodeMirror should do no further handling.
    -

    It is also possible to register events on - other objects. Use CodeMirror.on(handle, "eventName", - func) to register handlers on objects that don't have their - own on method. Document objects (instances +

    Document objects (instances of CodeMirror.Doc) emit the following events:

    diff --git a/lib/codemirror.js b/lib/codemirror.js index fe956fd81a..35c2e30f18 100644 --- a/lib/codemirror.js +++ b/lib/codemirror.js @@ -3123,9 +3123,6 @@ window.CodeMirror = (function() { this.refresh(); }, - on: function(type, f) {on(this, type, f);}, - off: function(type, f) {off(this, type, f);}, - operation: function(f){return runInOp(this, f);}, refresh: operation(null, function() { @@ -3149,6 +3146,7 @@ window.CodeMirror = (function() { getScrollerElement: function(){return this.display.scroller;}, getGutterElement: function(){return this.display.gutters;} }; + eventMixin(CodeMirror); // OPTION DEFAULTS @@ -3657,6 +3655,7 @@ window.CodeMirror = (function() { this.doc = doc; } CodeMirror.TextMarker = TextMarker; + eventMixin(TextMarker); TextMarker.prototype.clear = function() { if (this.explicitlyCleared) return; @@ -3809,6 +3808,7 @@ window.CodeMirror = (function() { } } CodeMirror.SharedTextMarker = SharedTextMarker; + eventMixin(SharedTextMarker); SharedTextMarker.prototype.clear = function() { if (this.explicitlyCleared) return; @@ -4066,6 +4066,7 @@ window.CodeMirror = (function() { this.cm = cm; this.node = node; }; + eventMixin(LineWidget); function widgetOperation(f) { return function() { var withOp = !this.cm.curOp; @@ -4124,12 +4125,12 @@ window.CodeMirror = (function() { // Line objects. These hold state related to a line, including // highlighting info (the styles array). - function makeLine(text, markedSpans, estimateHeight) { - var line = {text: text}; - attachMarkedSpans(line, markedSpans); - line.height = estimateHeight ? estimateHeight(line) : 1; - return line; + function Line(text, markedSpans, estimateHeight) { + this.text = text; + attachMarkedSpans(this, markedSpans); + this.height = estimateHeight ? estimateHeight(this) : 1; } + eventMixin(Line); function updateLine(line, text, markedSpans, estimateHeight) { line.text = text; @@ -4461,7 +4462,7 @@ window.CodeMirror = (function() { // This is a whole-line replace. Treated specially to make // sure line objects move the way they are supposed to. for (var i = 0, e = text.length - 1, added = []; i < e; ++i) - added.push(makeLine(text[i], spansFor(i), estimateHeight)); + added.push(new Line(text[i], spansFor(i), estimateHeight)); update(lastLine, lastLine.text, lastSpans); if (nlines) doc.remove(from.line, nlines); if (added.length) doc.insert(from.line, added); @@ -4470,8 +4471,8 @@ window.CodeMirror = (function() { update(firstLine, firstLine.text.slice(0, from.ch) + lastText + firstLine.text.slice(to.ch), lastSpans); } else { for (var added = [], i = 1, e = text.length - 1; i < e; ++i) - added.push(makeLine(text[i], spansFor(i), estimateHeight)); - added.push(makeLine(lastText + firstLine.text.slice(to.ch), lastSpans, estimateHeight)); + added.push(new Line(text[i], spansFor(i), estimateHeight)); + added.push(new Line(lastText + firstLine.text.slice(to.ch), lastSpans, estimateHeight)); update(firstLine, firstLine.text.slice(0, from.ch) + text[0], spansFor(0)); doc.insert(from.line + 1, added); } @@ -4482,7 +4483,7 @@ window.CodeMirror = (function() { update(firstLine, firstLine.text.slice(0, from.ch) + text[0], spansFor(0)); update(lastLine, lastText + lastLine.text.slice(to.ch), lastSpans); for (var i = 1, e = text.length - 1, added = []; i < e; ++i) - added.push(makeLine(text[i], spansFor(i), estimateHeight)); + added.push(new Line(text[i], spansFor(i), estimateHeight)); if (nlines > 1) doc.remove(from.line + 1, nlines - 1); doc.insert(from.line + 1, added); } @@ -4625,7 +4626,7 @@ window.CodeMirror = (function() { if (!(this instanceof Doc)) return new Doc(text, mode, firstLine); if (firstLine == null) firstLine = 0; - BranchChunk.call(this, [new LeafChunk([makeLine("", null)])]); + BranchChunk.call(this, [new LeafChunk([new Line("", null)])]); this.first = firstLine; this.scrollTop = this.scrollLeft = 0; this.cantEdit = false; @@ -4860,6 +4861,8 @@ window.CodeMirror = (function() { return function() {return method.apply(this.doc, arguments);}; })(Doc.prototype[prop]); + eventMixin(Doc); + function linkedDocs(doc, f, sharedHistOnly) { function propagate(doc, skip, sharedHist) { if (doc.linked) for (var i = 0; i < doc.linked.length; ++i) { @@ -5243,6 +5246,11 @@ window.CodeMirror = (function() { CodeMirror.on = on; CodeMirror.off = off; CodeMirror.signal = signal; + function eventMixin(ctor) { + ctor.prototype.on = function(type, f) {on(this, type, f);}; + ctor.prototype.off = function(type, f) {off(this, type, f);}; + } + // MISC UTILITIES // Number of pixels added to scroller and sizer to hide scrollbar From 649b848b91d33471ad43708dc2af3abfeebc17ad Mon Sep 17 00:00:00 2001 From: Marijn Haverbeke Date: Fri, 12 Jul 2013 10:45:55 +0200 Subject: [PATCH 1313/5780] [activeline addon] Don't get confused by collapsed spans Issue #1668 --- addon/selection/active-line.js | 2 +- lib/codemirror.js | 9 +++++++-- 2 files changed, 8 insertions(+), 3 deletions(-) diff --git a/addon/selection/active-line.js b/addon/selection/active-line.js index 65fab6f162..e50508653a 100644 --- a/addon/selection/active-line.js +++ b/addon/selection/active-line.js @@ -29,7 +29,7 @@ } function updateActiveLine(cm) { - var line = cm.getLineHandle(cm.getCursor().line); + var line = cm.getLineHandleVisualStart(cm.getCursor().line); if (cm.state.activeLine == line) return; clearActiveLine(cm); cm.addLineClass(line, "wrap", WRAP_CLASS); diff --git a/lib/codemirror.js b/lib/codemirror.js index 35c2e30f18..9a6a932010 100644 --- a/lib/codemirror.js +++ b/lib/codemirror.js @@ -4125,11 +4125,11 @@ window.CodeMirror = (function() { // Line objects. These hold state related to a line, including // highlighting info (the styles array). - function Line(text, markedSpans, estimateHeight) { + var Line = CodeMirror.Line = function(text, markedSpans, estimateHeight) { this.text = text; attachMarkedSpans(this, markedSpans); this.height = estimateHeight ? estimateHeight(this) : 1; - } + }; eventMixin(Line); function updateLine(line, text, markedSpans, estimateHeight) { @@ -4691,6 +4691,11 @@ window.CodeMirror = (function() { getLineHandle: function(line) {if (isLine(this, line)) return getLine(this, line);}, getLineNumber: function(line) {return lineNo(line);}, + getLineHandleVisualStart: function(line) { + if (typeof line == "number") line = getLine(this, line); + return visualLine(this, line); + }, + lineCount: function() {return this.size;}, firstLine: function() {return this.first;}, lastLine: function() {return this.first + this.size - 1;}, From 47655e693d5d29fc549797aa0cd79800d7828427 Mon Sep 17 00:00:00 2001 From: Ruslan Osmanov Date: Sat, 29 Jun 2013 19:28:16 +0500 Subject: [PATCH 1314/5780] [smartymixed mode] Add --- .gitignore | 2 + mode/smartymixed/index.html | 107 ++++++++++++++++++++ mode/smartymixed/smartymixed.js | 170 ++++++++++++++++++++++++++++++++ 3 files changed, 279 insertions(+) create mode 100644 mode/smartymixed/index.html create mode 100644 mode/smartymixed/smartymixed.js diff --git a/.gitignore b/.gitignore index bc20ab58d5..b471fe6e63 100644 --- a/.gitignore +++ b/.gitignore @@ -2,3 +2,5 @@ /npm-debug.log test.html .tern-* +*~ +*.swp diff --git a/mode/smartymixed/index.html b/mode/smartymixed/index.html new file mode 100644 index 0000000000..6567b27d6b --- /dev/null +++ b/mode/smartymixed/index.html @@ -0,0 +1,107 @@ + + + + + CodeMirror: Smarty mixed mode + + + + + + + + + + + + + + + +

    CodeMirror: Smarty mixed mode

    + + + + +

    The Smarty mixed mode depends on the Smarty and HTML mixed modes. HTML + mixed mode itself depends on XML, JavaScript, and CSS modes.

    + +

    It takes the same options, as Smarty and HTML mixed modes.

    + +

    MIME types defined: text/x-smarty.

    + + + diff --git a/mode/smartymixed/smartymixed.js b/mode/smartymixed/smartymixed.js new file mode 100644 index 0000000000..c5d008885a --- /dev/null +++ b/mode/smartymixed/smartymixed.js @@ -0,0 +1,170 @@ +/** +* @file smartymixed.js +* @brief Smarty Mixed Codemirror mode (Smarty + Mixed HTML) +* @author Ruslan Osmanov +* @version 3.0 +* @date 05.07.2013 +*/ +CodeMirror.defineMode("smartymixed", function(config) { + var settings, regs, helpers, parsers, + htmlMixedMode = CodeMirror.getMode(config, "htmlmixed"), + smartyMode = CodeMirror.getMode(config, "smarty"), + + settings = { + rightDelimiter: '}', + leftDelimiter: '{' + }; + + if (config.hasOwnProperty("leftDelimiter")) { + settings.leftDelimiter = config.leftDelimiter; + } + if (config.hasOwnProperty("rightDelimiter")) { + settings.rightDelimiter = config.rightDelimiter; + } + + regs = { + smartyComment: new RegExp("^" + settings.leftDelimiter + "\\*"), + literalOpen: new RegExp(settings.leftDelimiter + "literal" + settings.rightDelimiter), + literalClose: new RegExp(settings.leftDelimiter + "\/literal" + settings.rightDelimiter), + hasLeftDelimeter: new RegExp(".*" + settings.leftDelimiter), + htmlHasLeftDelimeter: new RegExp("[^<>]*" + settings.leftDelimiter) + }; + + helpers = { + chain: function(stream, state, parser) { + state.tokenize = parser; + return parser(stream, state); + }, + + cleanChain: function(stream, state, parser) { + state.tokenize = null; + state.localState = null; + state.localMode = null; + return (typeof parser == "string") ? (parser ? parser : null) : parser(stream, state); + }, + + maybeBackup: function(stream, pat, style) { + var cur = stream.current(); + var close = cur.search(pat), + m; + if (close > - 1) stream.backUp(cur.length - close); + else if (m = cur.match(/<\/?$/)) { + stream.backUp(cur.length); + if (!stream.match(pat, false)) stream.match(cur[0]); + } + return style; + } + }; + + parsers = { + html: function(stream, state) { + if (!state.inLiteral && stream.match(regs.htmlHasLeftDelimeter, false)) { + state.tokenize = parsers.smarty; + state.localMode = smartyMode; + state.localState = smartyMode.startState(htmlMixedMode.indent(state.htmlMixedState, "")); + return helpers.maybeBackup(stream, settings.leftDelimiter, smartyMode.token(stream, state.localState)); + } + return htmlMixedMode.token(stream, state.htmlMixedState); + }, + + smarty: function(stream, state) { + if (stream.match(settings.leftDelimiter, false)) { + if (stream.match(regs.smartyComment, false)) { + return helpers.chain(stream, state, parsers.inBlock("comment", "*" + settings.rightDelimiter)); + } + } else if (stream.match(settings.rightDelimiter, false)) { + stream.eat(settings.rightDelimiter); + state.tokenize = parsers.html; + state.localMode = htmlMixedMode; + state.localState = state.htmlMixedState; + return "tag"; + } + + return helpers.maybeBackup(stream, settings.rightDelimiter, smartyMode.token(stream, state.localState)); + }, + + inBlock: function(style, terminator) { + return function(stream, state) { + while (!stream.eol()) { + if (stream.match(terminator)) { + helpers.cleanChain(stream, state, ""); + break; + } + stream.next(); + } + return style; + }; + } + }; + + return { + startState: function() { + var state = htmlMixedMode.startState(); + return { + token: parsers.html, + localMode: null, + localState: null, + htmlMixedState: state, + tokenize: null, + inLiteral: false + }; + }, + + copyState: function(state) { + var local = null, tok = (state.tokenize || state.token); + if (state.localState) { + local = CodeMirror.copyState((tok != parsers.html ? smartyMode : htmlMixedMode), state.localState); + } + return { + token: state.token, + tokenize: state.tokenize, + localMode: state.localMode, + localState: local, + htmlMixedState: CodeMirror.copyState(htmlMixedMode, state.htmlMixedState), + inLiteral: state.inLiteral + }; + }, + + token: function(stream, state) { + if (stream.match(settings.leftDelimiter, false)) { + if (!state.inLiteral && stream.match(regs.literalOpen, true)) { + state.inLiteral = true; + return "keyword"; + } else if (state.inLiteral && stream.match(regs.literalClose, true)) { + state.inLiteral = false; + return "keyword"; + } + } + if (state.inLiteral && state.localState != state.htmlMixedState) { + state.tokenize = parsers.html; + state.localMode = htmlMixedMode; + state.localState = state.htmlMixedState; + } + + var style = (state.tokenize || state.token)(stream, state); + return style; + }, + + indent: function(state, textAfter) { + if (state.localMode == smartyMode + || (state.inLiteral && !state.localMode) + || regs.hasLeftDelimeter.test(textAfter)) { + return CodeMirror.Pass; + } + return htmlMixedMode.indent(state.htmlMixedState, textAfter); + }, + + electricChars: "/{}:", + + innerMode: function(state) { + return { + state: state.localState || state.htmlMixedState, + mode: state.localMode || htmlMixedMode + }; + } + }; +}, +"htmlmixed"); + +CodeMirror.defineMIME("text/x-smarty", "smartymixed"); +// vim: et ts=2 sts=2 sw=2 From 7f560afa1ce8c6ce7a43c561e35bd43f4b9652c3 Mon Sep 17 00:00:00 2001 From: Marijn Haverbeke Date: Fri, 12 Jul 2013 10:50:43 +0200 Subject: [PATCH 1315/5780] [smartymixed mode] Integrate --- doc/compress.html | 1 + doc/modes.html | 1 + mode/meta.js | 1 + 3 files changed, 3 insertions(+) diff --git a/doc/compress.html b/doc/compress.html index c7eec47d5e..9ccafba4ae 100644 --- a/doc/compress.html +++ b/doc/compress.html @@ -123,6 +123,7 @@

    { } CodeMi + diff --git a/doc/modes.html b/doc/modes.html index 1c76f2fb1b..cb72754eb7 100644 --- a/doc/modes.html +++ b/doc/modes.html @@ -76,6 +76,7 @@

    { } CodeMi
  • Sieve
  • Smalltalk
  • Smarty
  • +
  • Smarty/HTML mixed
  • SQL (several dialects)
  • SPARQL
  • sTeX, LaTeX
  • diff --git a/mode/meta.js b/mode/meta.js index 1ea0f672b0..63123a0254 100644 --- a/mode/meta.js +++ b/mode/meta.js @@ -59,6 +59,7 @@ CodeMirror.modeInfo = [ {name: 'Sieve', mime: 'application/sieve', mode: 'sieve'}, {name: 'Smalltalk', mime: 'text/x-stsrc', mode: 'smalltalk'}, {name: 'Smarty', mime: 'text/x-smarty', mode: 'smarty'}, + {name: 'SmartyMixed', mime: 'text/x-smarty', mode: 'smartymixed'}, {name: 'SPARQL', mime: 'application/x-sparql-query', mode: 'sparql'}, {name: 'SQL', mime: 'text/x-sql', mode: 'sql'}, {name: 'MariaDB', mime: 'text/x-mariadb', mode: 'sql'}, From 47f1a861d4493dc5b582b0b04d48155311301c1e Mon Sep 17 00:00:00 2001 From: Yunchi Luo Date: Fri, 12 Jul 2013 22:34:09 -0400 Subject: [PATCH 1316/5780] [vim] Disable insert mode if document is readonly --- keymap/vim.js | 1 + 1 file changed, 1 insertion(+) diff --git a/keymap/vim.js b/keymap/vim.js index 977642ea38..ab7f757b1e 100644 --- a/keymap/vim.js +++ b/keymap/vim.js @@ -1625,6 +1625,7 @@ emptyMacroKeyBuffer(macroModeState); }, enterInsertMode: function(cm, actionArgs, vim) { + if (cm.getOption('readOnly')) { return; } vim.insertMode = true; vim.insertModeRepeat = actionArgs && actionArgs.repeat || 1; var insertAt = (actionArgs) ? actionArgs.insertAt : null; From 13dc1753c35768c4802dfc8749b26011bf18bd42 Mon Sep 17 00:00:00 2001 From: Marijn Haverbeke Date: Sat, 13 Jul 2013 11:40:48 +0200 Subject: [PATCH 1317/5780] Fire DOM event from contextmenu handler Issue #1674 --- doc/manual.html | 2 +- lib/codemirror.js | 5 +++-- 2 files changed, 4 insertions(+), 3 deletions(-) diff --git a/doc/manual.html b/doc/manual.html index d17eb79853..55f7196aaf 100644 --- a/doc/manual.html +++ b/doc/manual.html @@ -489,7 +489,7 @@

    Events

    should not try to change the state of the editor.
    "mousedown", - "dblclick", "keydown", "keypress", + "dblclick", "contextmenu", "keydown", "keypress", "keyup", "dragstart", "dragenter", "dragover", "drop" (instance: CodeMirror, event: Event)
    diff --git a/lib/codemirror.js b/lib/codemirror.js index 9a6a932010..4e3a07eccb 100644 --- a/lib/codemirror.js +++ b/lib/codemirror.js @@ -2107,6 +2107,7 @@ window.CodeMirror = (function() { var detectingSelectAll; function onContextMenu(cm, e) { + if (signalDOMEvent(cm, e, "contextmenu")) return; var display = cm.display, sel = cm.doc.sel; if (eventInWidget(display, e)) return; @@ -5232,8 +5233,8 @@ window.CodeMirror = (function() { delayedCallbacks.push(bnd(arr[i])); } - function signalDOMEvent(cm, e) { - signal(cm, e.type, cm, e); + function signalDOMEvent(cm, e, override) { + signal(cm, override || e.type, cm, e); return e_defaultPrevented(e); } From 35ae796c194fa638dde8a97da00286012f41c5c4 Mon Sep 17 00:00:00 2001 From: Marijn Haverbeke Date: Sat, 13 Jul 2013 11:43:39 +0200 Subject: [PATCH 1318/5780] [real-world used] Add CodeZample --- doc/realworld.html | 1 + 1 file changed, 1 insertion(+) diff --git a/doc/realworld.html b/doc/realworld.html index 2150abe401..9aca75a75d 100644 --- a/doc/realworld.html +++ b/doc/realworld.html @@ -41,6 +41,7 @@

    { } CodeMi
  • Code Snippets (WordPress snippet management plugin)
  • Code together (collaborative editing)
  • Codev (collaborative IDE)
  • +
  • CodeZample (code snippet sharing)
  • Collaborative CodeMirror demo (CodeMirror + operational transforms)
  • Community Code Camp (code snippet sharing)
  • CKWNC (UML editor)
  • From b05b96e0306d3b33b8a7810884a0d1cfb7b42969 Mon Sep 17 00:00:00 2001 From: Andrey Lushnikov Date: Fri, 12 Jul 2013 12:47:00 +0400 Subject: [PATCH 1319/5780] [closebrackets addon] Do not insert pair of quotes right after the word --- addon/edit/closebrackets.js | 4 +++- lib/codemirror.js | 4 ++++ 2 files changed, 7 insertions(+), 1 deletion(-) diff --git a/addon/edit/closebrackets.js b/addon/edit/closebrackets.js index 6fbf38ae67..88718b7729 100644 --- a/addon/edit/closebrackets.js +++ b/addon/edit/closebrackets.js @@ -54,7 +54,9 @@ if (cm.somethingSelected()) return surround(cm); if (left == right && maybeOverwrite(cm) != CodeMirror.Pass) return; var cur = cm.getCursor(), ahead = CodeMirror.Pos(cur.line, cur.ch + 1); - var line = cm.getLine(cur.line), nextChar = line.charAt(cur.ch); + var line = cm.getLine(cur.line), nextChar = line.charAt(cur.ch), curChar = cur.ch > 0 ? line.charAt(cur.ch - 1) : ""; + if (left == right && CodeMirror.isWordChar(curChar)) + return CodeMirror.Pass; if (line.length == cur.ch || closingBrackets.indexOf(nextChar) >= 0 || SPACE_CHAR_REGEX.test(nextChar)) cm.replaceSelection(left + right, {head: ahead, anchor: ahead}); else diff --git a/lib/codemirror.js b/lib/codemirror.js index 4e3a07eccb..aed7e83b13 100644 --- a/lib/codemirror.js +++ b/lib/codemirror.js @@ -3319,6 +3319,10 @@ window.CodeMirror = (function() { helpers[type][name] = value; }; + // UTILITIES + + CodeMirror.isWordChar = isWordChar; + // MODE STATE HANDLING // Utility functions for working with state. Exported because modes From 2e3cce0cd6aea4093ad72daa8e6b537f3cefe635 Mon Sep 17 00:00:00 2001 From: Leonya Khachaturov Date: Fri, 12 Jul 2013 12:18:19 +0200 Subject: [PATCH 1320/5780] [css mode] Add several non-standard but very common CSS properties --- mode/css/css.js | 6 +++--- 1 file changed, 3 insertions(+), 3 deletions(-) diff --git a/mode/css/css.js b/mode/css/css.js index b52e787a70..70a55d30d4 100644 --- a/mode/css/css.js +++ b/mode/css/css.js @@ -386,15 +386,15 @@ CodeMirror.defineMode("css-base", function(config, parserConfig) { "text-decoration-color", "text-decoration-line", "text-decoration-skip", "text-decoration-style", "text-emphasis", "text-emphasis-color", "text-emphasis-position", "text-emphasis-style", "text-height", - "text-indent", "text-justify", "text-outline", "text-shadow", - "text-space-collapse", "text-transform", "text-underline-position", + "text-indent", "text-justify", "text-outline", "text-overflow", "text-shadow", + "text-size-adjust", "text-space-collapse", "text-transform", "text-underline-position", "text-wrap", "top", "transform", "transform-origin", "transform-style", "transition", "transition-delay", "transition-duration", "transition-property", "transition-timing-function", "unicode-bidi", "vertical-align", "visibility", "voice-balance", "voice-duration", "voice-family", "voice-pitch", "voice-range", "voice-rate", "voice-stress", "voice-volume", "volume", "white-space", "widows", "width", "word-break", - "word-spacing", "word-wrap", "z-index", + "word-spacing", "word-wrap", "z-index", "zoom", // SVG-specific "clip-path", "clip-rule", "mask", "enable-background", "filter", "flood-color", "flood-opacity", "lighting-color", "stop-color", "stop-opacity", "pointer-events", From 36c356cc6166775cbc89756165465172ee423228 Mon Sep 17 00:00:00 2001 From: Marijn Haverbeke Date: Thu, 25 Jul 2013 08:02:41 +0200 Subject: [PATCH 1321/5780] Remove trailing whitespace --- mode/css/css.js | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/mode/css/css.js b/mode/css/css.js index 70a55d30d4..a81fa40408 100644 --- a/mode/css/css.js +++ b/mode/css/css.js @@ -386,7 +386,7 @@ CodeMirror.defineMode("css-base", function(config, parserConfig) { "text-decoration-color", "text-decoration-line", "text-decoration-skip", "text-decoration-style", "text-emphasis", "text-emphasis-color", "text-emphasis-position", "text-emphasis-style", "text-height", - "text-indent", "text-justify", "text-outline", "text-overflow", "text-shadow", + "text-indent", "text-justify", "text-outline", "text-overflow", "text-shadow", "text-size-adjust", "text-space-collapse", "text-transform", "text-underline-position", "text-wrap", "top", "transform", "transform-origin", "transform-style", "transition", "transition-delay", "transition-duration", From 0ca2c2546475945dca9dacfd93de8cee3a6e5492 Mon Sep 17 00:00:00 2001 From: "Jan T. Sott" Date: Mon, 15 Jul 2013 10:36:47 +0200 Subject: [PATCH 1322/5780] [base16 themes] Add --- theme/3024-day.css | 33 +++++++++++++++++++++++++++++++ theme/3024-night.css | 33 +++++++++++++++++++++++++++++++ theme/base16-dark.css | 33 +++++++++++++++++++++++++++++++ theme/base16-light.css | 33 +++++++++++++++++++++++++++++++ theme/tomorrow-night-eighties.css | 33 +++++++++++++++++++++++++++++++ 5 files changed, 165 insertions(+) create mode 100644 theme/3024-day.css create mode 100644 theme/3024-night.css create mode 100644 theme/base16-dark.css create mode 100644 theme/base16-light.css create mode 100644 theme/tomorrow-night-eighties.css diff --git a/theme/3024-day.css b/theme/3024-day.css new file mode 100644 index 0000000000..e931a49de4 --- /dev/null +++ b/theme/3024-day.css @@ -0,0 +1,33 @@ +/* + + Name: 3024 day + Author: Jan T. Sott (http://github.com/idleberg) + + CodeMirror template by Jan T. Sott (https://github.com/idleberg/base16-codemirror) + Original Base16 color scheme by Chris Kempson (https://github.com/chriskempson/base16) + +*/ + +.cm-s-3024-day.CodeMirror {background: #f7f7f7; color: #3a3432;} +.cm-s-3024-day div.CodeMirror-selected {background: #d6d5d4 !important;} +.cm-s-3024-day .CodeMirror-gutters {background: #f7f7f7; border-right: 0px;} +.cm-s-3024-day .CodeMirror-linenumber {color: #807d7c;} +.cm-s-3024-day .CodeMirror-cursor {border-left: 1px solid #5c5855 !important;} + +.cm-s-3024-day span.cm-comment {color: #cdab53;} +.cm-s-3024-day span.cm-atom {color: #a16a94;} +.cm-s-3024-day span.cm-number {color: #a16a94;} + +.cm-s-3024-day span.cm-property, .cm-s-3024-day span.cm-attribute {color: #01a252;} +.cm-s-3024-day span.cm-keyword {color: #db2d20;} +.cm-s-3024-day span.cm-string {color: #fded02;} + +.cm-s-3024-day span.cm-variable {color: #01a252;} +.cm-s-3024-day span.cm-variable-2 {color: #01a0e4;} +.cm-s-3024-day span.cm-def {color: #e8bbd0;} +.cm-s-3024-day span.cm-error {background: #db2d20; color: #5c5855;} +.cm-s-3024-day span.cm-bracket {color: #3a3432;} +.cm-s-3024-day span.cm-tag {color: #db2d20;} +.cm-s-3024-day span.cm-link {color: #a16a94;} + +.cm-s-3024-day .CodeMirror-matchingbracket { text-decoration: underline; color: white !important;} diff --git a/theme/3024-night.css b/theme/3024-night.css new file mode 100644 index 0000000000..87c7f8abe0 --- /dev/null +++ b/theme/3024-night.css @@ -0,0 +1,33 @@ +/* + + Name: 3024 night + Author: Jan T. Sott (http://github.com/idleberg) + + CodeMirror template by Jan T. Sott (https://github.com/idleberg/base16-codemirror) + Original Base16 color scheme by Chris Kempson (https://github.com/chriskempson/base16) + +*/ + +.cm-s-3024-night.CodeMirror {background: #090300; color: #d6d5d4;} +.cm-s-3024-night div.CodeMirror-selected {background: #3a3432 !important;} +.cm-s-3024-night .CodeMirror-gutters {background: #090300; border-right: 0px;} +.cm-s-3024-night .CodeMirror-linenumber {color: #5c5855;} +.cm-s-3024-night .CodeMirror-cursor {border-left: 1px solid #807d7c !important;} + +.cm-s-3024-night span.cm-comment {color: #cdab53;} +.cm-s-3024-night span.cm-atom {color: #a16a94;} +.cm-s-3024-night span.cm-number {color: #a16a94;} + +.cm-s-3024-night span.cm-property, .cm-s-3024-night span.cm-attribute {color: #01a252;} +.cm-s-3024-night span.cm-keyword {color: #db2d20;} +.cm-s-3024-night span.cm-string {color: #fded02;} + +.cm-s-3024-night span.cm-variable {color: #01a252;} +.cm-s-3024-night span.cm-variable-2 {color: #01a0e4;} +.cm-s-3024-night span.cm-def {color: #e8bbd0;} +.cm-s-3024-night span.cm-error {background: #db2d20; color: #807d7c;} +.cm-s-3024-night span.cm-bracket {color: #d6d5d4;} +.cm-s-3024-night span.cm-tag {color: #db2d20;} +.cm-s-3024-night span.cm-link {color: #a16a94;} + +.cm-s-3024-night .CodeMirror-matchingbracket { text-decoration: underline; color: white !important;} diff --git a/theme/base16-dark.css b/theme/base16-dark.css new file mode 100644 index 0000000000..7a818b75f5 --- /dev/null +++ b/theme/base16-dark.css @@ -0,0 +1,33 @@ +/* + + Name: Base16 Default Dark + Author: Chris Kempson (http://chriskempson.com) + + CodeMirror template by Jan T. Sott (https://github.com/idleberg/base16-chrome-devtools) + Original Base16 color scheme by Chris Kempson (https://github.com/chriskempson/base16) + +*/ + +.cm-s-base16-dark.CodeMirror {background: #151515; color: #e0e0e0;} +.cm-s-base16-dark div.CodeMirror-selected {background: #202020 !important;} +.cm-s-base16-dark .CodeMirror-gutters {background: #151515; border-right: 0px;} +.cm-s-base16-dark .CodeMirror-linenumber {color: #505050;} +.cm-s-base16-dark .CodeMirror-cursor {border-left: 1px solid #b0b0b0 !important;} + +.cm-s-base16-dark span.cm-comment {color: #8f5536;} +.cm-s-base16-dark span.cm-atom {color: #aa759f;} +.cm-s-base16-dark span.cm-number {color: #aa759f;} + +.cm-s-base16-dark span.cm-property, .cm-s-base16-dark span.cm-attribute {color: #90a959;} +.cm-s-base16-dark span.cm-keyword {color: #ac4142;} +.cm-s-base16-dark span.cm-string {color: #f4bf75;} + +.cm-s-base16-dark span.cm-variable {color: #90a959;} +.cm-s-base16-dark span.cm-variable-2 {color: #6a9fb5;} +.cm-s-base16-dark span.cm-def {color: #d28445;} +.cm-s-base16-dark span.cm-error {background: #ac4142; color: #b0b0b0;} +.cm-s-base16-dark span.cm-bracket {color: #e0e0e0;} +.cm-s-base16-dark span.cm-tag {color: #ac4142;} +.cm-s-base16-dark span.cm-link {color: #aa759f;} + +.cm-s-base16-dark .CodeMirror-matchingbracket { text-decoration: underline; color: white !important;} diff --git a/theme/base16-light.css b/theme/base16-light.css new file mode 100644 index 0000000000..a3b2d4dcba --- /dev/null +++ b/theme/base16-light.css @@ -0,0 +1,33 @@ +/* + + Name: Base16 Default Light + Author: Chris Kempson (http://chriskempson.com) + + CodeMirror template by Jan T. Sott (https://github.com/idleberg/base16-chrome-devtools) + Original Base16 color scheme by Chris Kempson (https://github.com/chriskempson/base16) + +*/ + +.cm-s-base16-light.CodeMirror {background: #f5f5f5; color: #202020;} +.cm-s-base16-light div.CodeMirror-selected {background: #e0e0e0 !important;} +.cm-s-base16-light .CodeMirror-gutters {background: #f5f5f5; border-right: 0px;} +.cm-s-base16-light .CodeMirror-linenumber {color: #b0b0b0;} +.cm-s-base16-light .CodeMirror-cursor {border-left: 1px solid #505050 !important;} + +.cm-s-base16-light span.cm-comment {color: #8f5536;} +.cm-s-base16-light span.cm-atom {color: #aa759f;} +.cm-s-base16-light span.cm-number {color: #aa759f;} + +.cm-s-base16-light span.cm-property, .cm-s-base16-light span.cm-attribute {color: #90a959;} +.cm-s-base16-light span.cm-keyword {color: #ac4142;} +.cm-s-base16-light span.cm-string {color: #f4bf75;} + +.cm-s-base16-light span.cm-variable {color: #90a959;} +.cm-s-base16-light span.cm-variable-2 {color: #6a9fb5;} +.cm-s-base16-light span.cm-def {color: #d28445;} +.cm-s-base16-light span.cm-error {background: #ac4142; color: #505050;} +.cm-s-base16-light span.cm-bracket {color: #202020;} +.cm-s-base16-light span.cm-tag {color: #ac4142;} +.cm-s-base16-light span.cm-link {color: #aa759f;} + +.cm-s-base16-light .CodeMirror-matchingbracket { text-decoration: underline; color: white !important;} diff --git a/theme/tomorrow-night-eighties.css b/theme/tomorrow-night-eighties.css new file mode 100644 index 0000000000..3aa84cc829 --- /dev/null +++ b/theme/tomorrow-night-eighties.css @@ -0,0 +1,33 @@ +/* + + Name: Tomorrow Night - Eighties + Author: Chris Kempson + + CodeMirror template by Jan T. Sott (https://github.com/idleberg/base16-codemirror) + Original Base16 color scheme by Chris Kempson (https://github.com/chriskempson/base16) + +*/ + +.cm-s-tomorrownight-eighties-night.CodeMirror {background: #000000; color: #CCCCCC;} +.cm-s-tomorrownight-eighties-night div.CodeMirror-selected {background: #2D2D2D !important;} +.cm-s-tomorrownight-eighties-night .CodeMirror-gutters {background: #000000; border-right: 0px;} +.cm-s-tomorrownight-eighties-night .CodeMirror-linenumber {color: #515151;} +.cm-s-tomorrownight-eighties-night .CodeMirror-cursor {border-left: 1px solid #6A6A6A !important;} + +.cm-s-tomorrownight-eighties-night span.cm-comment {color: #d27b53;} +.cm-s-tomorrownight-eighties-night span.cm-atom {color: #a16a94;} +.cm-s-tomorrownight-eighties-night span.cm-number {color: #a16a94;} + +.cm-s-tomorrownight-eighties-night span.cm-property, .cm-s-tomorrownight-eighties-night span.cm-attribute {color: #99cc99;} +.cm-s-tomorrownight-eighties-night span.cm-keyword {color: #f2777a;} +.cm-s-tomorrownight-eighties-night span.cm-string {color: #ffcc66;} + +.cm-s-tomorrownight-eighties-night span.cm-variable {color: #99cc99;} +.cm-s-tomorrownight-eighties-night span.cm-variable-2 {color: #6699cc;} +.cm-s-tomorrownight-eighties-night span.cm-def {color: #f99157;} +.cm-s-tomorrownight-eighties-night span.cm-error {background: #f2777a; color: #6A6A6A;} +.cm-s-tomorrownight-eighties-night span.cm-bracket {color: #CCCCCC;} +.cm-s-tomorrownight-eighties-night span.cm-tag {color: #f2777a;} +.cm-s-tomorrownight-eighties-night span.cm-link {color: #a16a94;} + +.cm-s-tomorrownight-eighties-night .CodeMirror-matchingbracket { text-decoration: underline; color: white !important;} From 23c845ce7cd3f13cf660caa934acd12b7e70b86c Mon Sep 17 00:00:00 2001 From: Marijn Haverbeke Date: Thu, 25 Jul 2013 08:13:13 +0200 Subject: [PATCH 1323/5780] [base16 themes] Integrate --- demo/theme.html | 10 +++++++ theme/tomorrow-night-eighties.css | 46 +++++++++++++++---------------- 2 files changed, 33 insertions(+), 23 deletions(-) diff --git a/demo/theme.html b/demo/theme.html index 147a89ff14..62544be1a7 100644 --- a/demo/theme.html +++ b/demo/theme.html @@ -22,6 +22,11 @@ + + + + + @@ -49,7 +54,11 @@

    CodeMirror: Theme demo

    Select a theme: + + +

    MIME types defined: text/nginx.

    + + + diff --git a/mode/nginx/nginx.js b/mode/nginx/nginx.js new file mode 100644 index 0000000000..4ea49886c3 --- /dev/null +++ b/mode/nginx/nginx.js @@ -0,0 +1,162 @@ +CodeMirror.defineMode("nginx", function(config) { + + function words(str) { + var obj = {}, words = str.split(" "); + for (var i = 0; i < words.length; ++i) obj[words[i]] = true; + return obj; + } + + var keywords = words( + /* ngxDirectiveControl */ "break return rewrite set" + + /* ngxDirective */ " accept_mutex accept_mutex_delay access_log add_after_body add_before_body add_header addition_types aio alias allow ancient_browser ancient_browser_value auth_basic auth_basic_user_file auth_http auth_http_header auth_http_timeout autoindex autoindex_exact_size autoindex_localtime charset charset_types client_body_buffer_size client_body_in_file_only client_body_in_single_buffer client_body_temp_path client_body_timeout client_header_buffer_size client_header_timeout client_max_body_size connection_pool_size create_full_put_path daemon dav_access dav_methods debug_connection debug_points default_type degradation degrade deny devpoll_changes devpoll_events directio directio_alignment empty_gif env epoll_events error_log eventport_events expires fastcgi_bind fastcgi_buffer_size fastcgi_buffers fastcgi_busy_buffers_size fastcgi_cache fastcgi_cache_key fastcgi_cache_methods fastcgi_cache_min_uses fastcgi_cache_path fastcgi_cache_use_stale fastcgi_cache_valid fastcgi_catch_stderr fastcgi_connect_timeout fastcgi_hide_header fastcgi_ignore_client_abort fastcgi_ignore_headers fastcgi_index fastcgi_intercept_errors fastcgi_max_temp_file_size fastcgi_next_upstream fastcgi_param fastcgi_pass_header fastcgi_pass_request_body fastcgi_pass_request_headers fastcgi_read_timeout fastcgi_send_lowat fastcgi_send_timeout fastcgi_split_path_info fastcgi_store fastcgi_store_access fastcgi_temp_file_write_size fastcgi_temp_path fastcgi_upstream_fail_timeout fastcgi_upstream_max_fails flv geoip_city geoip_country google_perftools_profiles gzip gzip_buffers gzip_comp_level gzip_disable gzip_hash gzip_http_version gzip_min_length gzip_no_buffer gzip_proxied gzip_static gzip_types gzip_vary gzip_window if_modified_since ignore_invalid_headers image_filter image_filter_buffer image_filter_jpeg_quality image_filter_transparency imap_auth imap_capabilities imap_client_buffer index ip_hash keepalive_requests keepalive_timeout kqueue_changes kqueue_events large_client_header_buffers limit_conn limit_conn_log_level limit_rate limit_rate_after limit_req limit_req_log_level limit_req_zone limit_zone lingering_time lingering_timeout lock_file log_format log_not_found log_subrequest map_hash_bucket_size map_hash_max_size master_process memcached_bind memcached_buffer_size memcached_connect_timeout memcached_next_upstream memcached_read_timeout memcached_send_timeout memcached_upstream_fail_timeout memcached_upstream_max_fails merge_slashes min_delete_depth modern_browser modern_browser_value msie_padding msie_refresh multi_accept open_file_cache open_file_cache_errors open_file_cache_events open_file_cache_min_uses open_file_cache_valid open_log_file_cache output_buffers override_charset perl perl_modules perl_require perl_set pid pop3_auth pop3_capabilities port_in_redirect postpone_gzipping postpone_output protocol proxy proxy_bind proxy_buffer proxy_buffer_size proxy_buffering proxy_buffers proxy_busy_buffers_size proxy_cache proxy_cache_key proxy_cache_methods proxy_cache_min_uses proxy_cache_path proxy_cache_use_stale proxy_cache_valid proxy_connect_timeout proxy_headers_hash_bucket_size proxy_headers_hash_max_size proxy_hide_header proxy_ignore_client_abort proxy_ignore_headers proxy_intercept_errors proxy_max_temp_file_size proxy_method proxy_next_upstream proxy_pass_error_message proxy_pass_header proxy_pass_request_body proxy_pass_request_headers proxy_read_timeout proxy_redirect proxy_send_lowat proxy_send_timeout proxy_set_body proxy_set_header proxy_ssl_session_reuse proxy_store proxy_store_access proxy_temp_file_write_size proxy_temp_path proxy_timeout proxy_upstream_fail_timeout proxy_upstream_max_fails random_index read_ahead real_ip_header recursive_error_pages request_pool_size reset_timedout_connection resolver resolver_timeout rewrite_log rtsig_overflow_events rtsig_overflow_test rtsig_overflow_threshold rtsig_signo satisfy secure_link_secret send_lowat send_timeout sendfile sendfile_max_chunk server_name_in_redirect server_names_hash_bucket_size server_names_hash_max_size server_tokens set_real_ip_from smtp_auth smtp_capabilities smtp_client_buffer smtp_greeting_delay so_keepalive source_charset ssi ssi_ignore_recycled_buffers ssi_min_file_chunk ssi_silent_errors ssi_types ssi_value_length ssl ssl_certificate ssl_certificate_key ssl_ciphers ssl_client_certificate ssl_crl ssl_dhparam ssl_engine ssl_prefer_server_ciphers ssl_protocols ssl_session_cache ssl_session_timeout ssl_verify_client ssl_verify_depth starttls stub_status sub_filter sub_filter_once sub_filter_types tcp_nodelay tcp_nopush thread_stack_size timeout timer_resolution types_hash_bucket_size types_hash_max_size underscores_in_headers uninitialized_variable_warn use user userid userid_domain userid_expires userid_mark userid_name userid_p3p userid_path userid_service valid_referers variables_hash_bucket_size variables_hash_max_size worker_connections worker_cpu_affinity worker_priority worker_processes worker_rlimit_core worker_rlimit_nofile worker_rlimit_sigpending worker_threads working_directory xclient xml_entities xslt_stylesheet xslt_typesdrew@li229-23" + ); + + var keywords_block = words( + /* ngxDirectiveBlock */ "http mail events server types location upstream charset_map limit_except if geo map" + ); + + var keywords_important = words( + /* ngxDirectiveImportant */ "include root server server_name listen internal proxy_pass memcached_pass fastcgi_pass try_files" + ); + + var indentUnit = config.indentUnit, type; + function ret(style, tp) {type = tp; return style;} + + function tokenBase(stream, state) { + + + stream.eatWhile(/[\w\$_]/); + + var cur = stream.current(); + + + if (keywords.propertyIsEnumerable(cur)) { + return "keyword"; + } + else if (keywords_block.propertyIsEnumerable(cur)) { + return "variable-2"; + } + else if (keywords_important.propertyIsEnumerable(cur)) { + return "string-2"; + } + /**/ + + var ch = stream.next(); + if (ch == "@") {stream.eatWhile(/[\w\\\-]/); return ret("meta", stream.current());} + else if (ch == "/" && stream.eat("*")) { + state.tokenize = tokenCComment; + return tokenCComment(stream, state); + } + else if (ch == "<" && stream.eat("!")) { + state.tokenize = tokenSGMLComment; + return tokenSGMLComment(stream, state); + } + else if (ch == "=") ret(null, "compare"); + else if ((ch == "~" || ch == "|") && stream.eat("=")) return ret(null, "compare"); + else if (ch == "\"" || ch == "'") { + state.tokenize = tokenString(ch); + return state.tokenize(stream, state); + } + else if (ch == "#") { + stream.skipToEnd(); + return ret("comment", "comment"); + } + else if (ch == "!") { + stream.match(/^\s*\w*/); + return ret("keyword", "important"); + } + else if (/\d/.test(ch)) { + stream.eatWhile(/[\w.%]/); + return ret("number", "unit"); + } + else if (/[,.+>*\/]/.test(ch)) { + return ret(null, "select-op"); + } + else if (/[;{}:\[\]]/.test(ch)) { + return ret(null, ch); + } + else { + stream.eatWhile(/[\w\\\-]/); + return ret("variable", "variable"); + } + } + + function tokenCComment(stream, state) { + var maybeEnd = false, ch; + while ((ch = stream.next()) != null) { + if (maybeEnd && ch == "/") { + state.tokenize = tokenBase; + break; + } + maybeEnd = (ch == "*"); + } + return ret("comment", "comment"); + } + + function tokenSGMLComment(stream, state) { + var dashes = 0, ch; + while ((ch = stream.next()) != null) { + if (dashes >= 2 && ch == ">") { + state.tokenize = tokenBase; + break; + } + dashes = (ch == "-") ? dashes + 1 : 0; + } + return ret("comment", "comment"); + } + + function tokenString(quote) { + return function(stream, state) { + var escaped = false, ch; + while ((ch = stream.next()) != null) { + if (ch == quote && !escaped) + break; + escaped = !escaped && ch == "\\"; + } + if (!escaped) state.tokenize = tokenBase; + return ret("string", "string"); + }; + } + + return { + startState: function(base) { + return {tokenize: tokenBase, + baseIndent: base || 0, + stack: []}; + }, + + token: function(stream, state) { + if (stream.eatSpace()) return null; + var style = state.tokenize(stream, state); + + var context = state.stack[state.stack.length-1]; + if (type == "hash" && context == "rule") style = "atom"; + else if (style == "variable") { + if (context == "rule") style = "number"; + else if (!context || context == "@media{") style = "tag"; + } + + if (context == "rule" && /^[\{\};]$/.test(type)) + state.stack.pop(); + if (type == "{") { + if (context == "@media") state.stack[state.stack.length-1] = "@media{"; + else state.stack.push("{"); + } + else if (type == "}") state.stack.pop(); + else if (type == "@media") state.stack.push("@media"); + else if (context == "{" && type != "comment") state.stack.push("rule"); + return style; + }, + + indent: function(state, textAfter) { + var n = state.stack.length; + if (/^\}/.test(textAfter)) + n -= state.stack[state.stack.length-1] == "rule" ? 2 : 1; + return state.baseIndent + n * indentUnit; + }, + + electricChars: "}" + }; +}); + +CodeMirror.defineMIME("text/nginx", "nginx"); From 562af66579ad69425134bdbac03a82db10bf0fe3 Mon Sep 17 00:00:00 2001 From: Marijn Haverbeke Date: Mon, 29 Jul 2013 08:38:29 +0200 Subject: [PATCH 1350/5780] [nginx mode] Integrate --- doc/compress.html | 1 + doc/modes.html | 1 + mode/meta.js | 1 + mode/nginx/index.html | 259 +++++++++++++++++++++--------------------- mode/nginx/nginx.js | 3 +- 5 files changed, 134 insertions(+), 131 deletions(-) diff --git a/doc/compress.html b/doc/compress.html index 9ccafba4ae..f76629754a 100644 --- a/doc/compress.html +++ b/doc/compress.html @@ -101,6 +101,7 @@

    { } CodeMi + diff --git a/doc/modes.html b/doc/modes.html index cb72754eb7..82738f19e6 100644 --- a/doc/modes.html +++ b/doc/modes.html @@ -54,6 +54,7 @@

    { } CodeMi
  • Lua
  • Markdown (GitHub-flavour)
  • mIRC
  • +
  • Nginx
  • NTriples
  • OCaml
  • Pascal
  • diff --git a/mode/meta.js b/mode/meta.js index 63123a0254..8e8c8f5f5c 100644 --- a/mode/meta.js +++ b/mode/meta.js @@ -37,6 +37,7 @@ CodeMirror.modeInfo = [ {name: 'Lua', mime: 'text/x-lua', mode: 'lua'}, {name: 'Markdown (GitHub-flavour)', mime: 'text/x-markdown', mode: 'markdown'}, {name: 'mIRC', mime: 'text/mirc', mode: 'mirc'}, + {name: 'Nginx', mime: 'text/x-nginx-conf', mode: 'nginx'}, {name: 'NTriples', mime: 'text/n-triples', mode: 'ntriples'}, {name: 'OCaml', mime: 'text/x-ocaml', mode: 'ocaml'}, {name: 'Pascal', mime: 'text/x-pascal', mode: 'pascal'}, diff --git a/mode/nginx/index.html b/mode/nginx/index.html index c33a224ff4..aa712e1e32 100644 --- a/mode/nginx/index.html +++ b/mode/nginx/index.html @@ -8,155 +8,154 @@ - + - +

    CodeMirror: NGINX mode

    + @@ -119,6 +120,7 @@

    CodeMirror: Tcl mode

    theme: "night", lineNumbers: true, indentUnit: 2, + scrollPastEnd: true, mode: "text/x-tcl" }); From 730d5dd19e3dcfaa972e33060d1d58cb51bc71d9 Mon Sep 17 00:00:00 2001 From: Marijn Haverbeke Date: Wed, 31 Jul 2013 15:34:04 +0200 Subject: [PATCH 1371/5780] [show-hint addon] Fix bug with updating completions when typing --- addon/hint/show-hint.js | 8 ++------ 1 file changed, 2 insertions(+), 6 deletions(-) diff --git a/addon/hint/show-hint.js b/addon/hint/show-hint.js index a4960ddeb0..6247459dd0 100644 --- a/addon/hint/show-hint.js +++ b/addon/hint/show-hint.js @@ -69,13 +69,9 @@ completion.cm.off("cursorActivity", activity); CodeMirror.signal(data, "close"); } - function isDone() { - if (finished) return true; - if (!completion.widget) { done(); return true; } - } function update() { - if (isDone()) return; + if (finished) return; CodeMirror.signal(data, "update"); if (completion.options.async) completion.getHints(completion.cm, finishUpdate, completion.options); @@ -84,7 +80,7 @@ } function finishUpdate(data_) { data = data_; - if (isDone()) return; + if (finished) return; if (!data || !data.list.length) return done(); completion.widget = new Widget(completion, data); } From 98394a96cd1998f4a59a1688dba662f18ae52deb Mon Sep 17 00:00:00 2001 From: Marijn Haverbeke Date: Wed, 31 Jul 2013 15:40:33 +0200 Subject: [PATCH 1372/5780] [show-hint addon] Don't try to signal event on null data Closes #1714 --- addon/hint/show-hint.js | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/addon/hint/show-hint.js b/addon/hint/show-hint.js index 6247459dd0..a33c4c3559 100644 --- a/addon/hint/show-hint.js +++ b/addon/hint/show-hint.js @@ -67,7 +67,7 @@ finished = true; completion.close(); completion.cm.off("cursorActivity", activity); - CodeMirror.signal(data, "close"); + if (data) CodeMirror.signal(data, "close"); } function update() { From 3e4540fcf90495280269c4a9c0ba8aae2f0e989f Mon Sep 17 00:00:00 2001 From: Marijn Haverbeke Date: Thu, 1 Aug 2013 09:00:13 +0200 Subject: [PATCH 1373/5780] Remove remaining local LICENSE files --- mode/livescript/LICENSE | 23 ----------------------- mode/sieve/LICENSE | 19 ------------------- mode/xquery/LICENSE | 20 -------------------- 3 files changed, 62 deletions(-) delete mode 100644 mode/livescript/LICENSE delete mode 100644 mode/sieve/LICENSE delete mode 100644 mode/xquery/LICENSE diff --git a/mode/livescript/LICENSE b/mode/livescript/LICENSE deleted file mode 100644 index a675c40233..0000000000 --- a/mode/livescript/LICENSE +++ /dev/null @@ -1,23 +0,0 @@ -The MIT License - -Copyright (c) 2013 Kenneth Bentley -Modified from the CoffeeScript CodeMirror mode, Copyright (c) 2011 Jeff Pickhardt -Modified from the Python CodeMirror mode, Copyright (c) 2010 Timothy Farrell - -Permission is hereby granted, free of charge, to any person obtaining a copy -of this software and associated documentation files (the "Software"), to deal -in the Software without restriction, including without limitation the rights -to use, copy, modify, merge, publish, distribute, sublicense, and/or sell -copies of the Software, and to permit persons to whom the Software is -furnished to do so, subject to the following conditions: - -The above copyright notice and this permission notice shall be included in -all copies or substantial portions of the Software. - -THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR -IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, -FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE -AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER -LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, -OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN -THE SOFTWARE. diff --git a/mode/sieve/LICENSE b/mode/sieve/LICENSE deleted file mode 100644 index 8a74612cba..0000000000 --- a/mode/sieve/LICENSE +++ /dev/null @@ -1,19 +0,0 @@ -Copyright (C) 2012 Thomas Schmid - -Permission is hereby granted, free of charge, to any person obtaining a copy -of this software and associated documentation files (the "Software"), to deal -in the Software without restriction, including without limitation the rights -to use, copy, modify, merge, publish, distribute, sublicense, and/or sell -copies of the Software, and to permit persons to whom the Software is -furnished to do so, subject to the following conditions: - -The above copyright notice and this permission notice shall be included in -all copies or substantial portions of the Software. - -THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR -IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, -FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE -AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER -LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, -OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN -THE SOFTWARE. diff --git a/mode/xquery/LICENSE b/mode/xquery/LICENSE deleted file mode 100644 index 2a2d47be53..0000000000 --- a/mode/xquery/LICENSE +++ /dev/null @@ -1,20 +0,0 @@ -Copyright (C) 2011 by MarkLogic Corporation -Author: Mike Brevoort - -Permission is hereby granted, free of charge, to any person obtaining a copy -of this software and associated documentation files (the "Software"), to deal -in the Software without restriction, including without limitation the rights -to use, copy, modify, merge, publish, distribute, sublicense, and/or sell -copies of the Software, and to permit persons to whom the Software is -furnished to do so, subject to the following conditions: - -The above copyright notice and this permission notice shall be included in -all copies or substantial portions of the Software. - -THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR -IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, -FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE -AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER -LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, -OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN -THE SOFTWARE. \ No newline at end of file From 1984f2957ff49fb8dac3491c2329ef76f86b0a33 Mon Sep 17 00:00:00 2001 From: "Jan T. Sott" Date: Wed, 31 Jul 2013 16:01:52 +0200 Subject: [PATCH 1374/5780] [paraiso theme] Add --- demo/theme.html | 36 ++++++++++++++++++++---------------- theme/paraiso-dark.css | 33 +++++++++++++++++++++++++++++++++ theme/paraiso-light.css | 33 +++++++++++++++++++++++++++++++++ 3 files changed, 86 insertions(+), 16 deletions(-) create mode 100644 theme/paraiso-dark.css create mode 100644 theme/paraiso-light.css diff --git a/demo/theme.html b/demo/theme.html index 62544be1a7..53bd90a5a4 100644 --- a/demo/theme.html +++ b/demo/theme.html @@ -5,28 +5,30 @@ CodeMirror: Theme Demo - + + + + + + + + - + + - - + + + + - - - - - - - - - - - - + + + + @@ -69,6 +71,8 @@

    CodeMirror: Theme demo

    + + diff --git a/theme/paraiso-dark.css b/theme/paraiso-dark.css new file mode 100644 index 0000000000..1e457918de --- /dev/null +++ b/theme/paraiso-dark.css @@ -0,0 +1,33 @@ +/* + + Name: Paraíso (Dark) + Author: Jan T. Sott + + Color scheme by Jan T. Sott (https://github.com/idleberg/Paraiso.tmTheme) + Inspired by the art of Rubens LP (http://www.rubenslp.com.br) + +*/ + +.cm-s-paraiso-dark.CodeMirror {background: #2f1e2e; color: #b9b6b0;} +.cm-s-paraiso-dark div.CodeMirror-selected {background: #41323f !important;} +.cm-s-paraiso-dark .CodeMirror-gutters {background: #2f1e2e; border-right: 0px;} +.cm-s-paraiso-dark .CodeMirror-linenumber {color: #776e71;} +.cm-s-paraiso-dark .CodeMirror-cursor {border-left: 1px solid #8d8687 !important;} + +.cm-s-paraiso-dark span.cm-comment {color: #e96ba8;} +.cm-s-paraiso-dark span.cm-atom {color: #815ba4;} +.cm-s-paraiso-dark span.cm-number {color: #815ba4;} + +.cm-s-paraiso-dark span.cm-property, .cm-s-paraiso-dark span.cm-attribute {color: #48b685;} +.cm-s-paraiso-dark span.cm-keyword {color: #ef6155;} +.cm-s-paraiso-dark span.cm-string {color: #fec418;} + +.cm-s-paraiso-dark span.cm-variable {color: #48b685;} +.cm-s-paraiso-dark span.cm-variable-2 {color: #06b6ef;} +.cm-s-paraiso-dark span.cm-def {color: #f99b15;} +.cm-s-paraiso-dark span.cm-error {background: #ef6155; color: #8d8687;} +.cm-s-paraiso-dark span.cm-bracket {color: #b9b6b0;} +.cm-s-paraiso-dark span.cm-tag {color: #ef6155;} +.cm-s-paraiso-dark span.cm-link {color: #815ba4;} + +.cm-s-paraiso-dark .CodeMirror-matchingbracket { text-decoration: underline; color: white !important;} diff --git a/theme/paraiso-light.css b/theme/paraiso-light.css new file mode 100644 index 0000000000..540d58b7d3 --- /dev/null +++ b/theme/paraiso-light.css @@ -0,0 +1,33 @@ +/* + + Name: Paraíso (Light) + Author: Jan T. Sott + + Color scheme by Jan T. Sott (https://github.com/idleberg/Paraiso.tmTheme) + Inspired by the art of Rubens LP (http://www.rubenslp.com.br) + +*/ + +.cm-s-paraiso-light.CodeMirror {background: #e7e9db; color: #41323f;} +.cm-s-paraiso-light div.CodeMirror-selected {background: #b9b6b0 !important;} +.cm-s-paraiso-light .CodeMirror-gutters {background: #e7e9db; border-right: 0px;} +.cm-s-paraiso-light .CodeMirror-linenumber {color: #8d8687;} +.cm-s-paraiso-light .CodeMirror-cursor {border-left: 1px solid #776e71 !important;} + +.cm-s-paraiso-light span.cm-comment {color: #e96ba8;} +.cm-s-paraiso-light span.cm-atom {color: #815ba4;} +.cm-s-paraiso-light span.cm-number {color: #815ba4;} + +.cm-s-paraiso-light span.cm-property, .cm-s-paraiso-light span.cm-attribute {color: #48b685;} +.cm-s-paraiso-light span.cm-keyword {color: #ef6155;} +.cm-s-paraiso-light span.cm-string {color: #fec418;} + +.cm-s-paraiso-light span.cm-variable {color: #48b685;} +.cm-s-paraiso-light span.cm-variable-2 {color: #06b6ef;} +.cm-s-paraiso-light span.cm-def {color: #f99b15;} +.cm-s-paraiso-light span.cm-error {background: #ef6155; color: #776e71;} +.cm-s-paraiso-light span.cm-bracket {color: #41323f;} +.cm-s-paraiso-light span.cm-tag {color: #ef6155;} +.cm-s-paraiso-light span.cm-link {color: #815ba4;} + +.cm-s-paraiso-light .CodeMirror-matchingbracket { text-decoration: underline; color: white !important;} From 92c906f82687f22be78df5fd8495a0aeccabf071 Mon Sep 17 00:00:00 2001 From: "Jan T. Sott" Date: Thu, 1 Aug 2013 11:53:01 +0200 Subject: [PATCH 1375/5780] [paraiso theme] Fix url --- theme/paraiso-dark.css | 2 +- theme/paraiso-light.css | 2 +- 2 files changed, 2 insertions(+), 2 deletions(-) diff --git a/theme/paraiso-dark.css b/theme/paraiso-dark.css index 1e457918de..abee9ab860 100644 --- a/theme/paraiso-dark.css +++ b/theme/paraiso-dark.css @@ -3,7 +3,7 @@ Name: Paraíso (Dark) Author: Jan T. Sott - Color scheme by Jan T. Sott (https://github.com/idleberg/Paraiso.tmTheme) + Color scheme by Jan T. Sott (https://github.com/idleberg/Paraiso-CodeMirror) Inspired by the art of Rubens LP (http://www.rubenslp.com.br) */ diff --git a/theme/paraiso-light.css b/theme/paraiso-light.css index 540d58b7d3..e9da474c82 100644 --- a/theme/paraiso-light.css +++ b/theme/paraiso-light.css @@ -3,7 +3,7 @@ Name: Paraíso (Light) Author: Jan T. Sott - Color scheme by Jan T. Sott (https://github.com/idleberg/Paraiso.tmTheme) + Color scheme by Jan T. Sott (https://github.com/idleberg/Paraiso-CodeMirror) Inspired by the art of Rubens LP (http://www.rubenslp.com.br) */ From 849035a1541b66a9e3ed50d371d51e8fdbd4ee09 Mon Sep 17 00:00:00 2001 From: Jochen Berger Date: Fri, 2 Aug 2013 15:49:29 +0200 Subject: [PATCH 1376/5780] the mode for "Properties files" should be "properties", not "clike" --- mode/meta.js | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/mode/meta.js b/mode/meta.js index 8e8c8f5f5c..9c5d986f66 100644 --- a/mode/meta.js +++ b/mode/meta.js @@ -46,7 +46,7 @@ CodeMirror.modeInfo = [ {name: 'PHP(HTML)', mime: 'application/x-httpd-php', mode: 'php'}, {name: 'Pig', mime: 'text/x-pig', mode: 'pig'}, {name: 'Plain Text', mime: 'text/plain', mode: 'null'}, - {name: 'Properties files', mime: 'text/x-properties', mode: 'clike'}, + {name: 'Properties files', mime: 'text/x-properties', mode: 'properties'}, {name: 'Python', mime: 'text/x-python', mode: 'python'}, {name: 'Cython', mime: 'text/x-cython', mode: 'python'}, {name: 'R', mime: 'text/x-rsrc', mode: 'r'}, From b86ffff14a7a73b08c905641037ccdef1d25fa79 Mon Sep 17 00:00:00 2001 From: Benjamin DeCoste Date: Fri, 2 Aug 2013 00:57:27 -0300 Subject: [PATCH 1377/5780] [vim mode] Fix visual mode downwards movement bug Previously, if you tried to move further down the document than exist the cursor would not move down. This sets selectionEnd.line to be no greater than the total length of the document. --- keymap/vim.js | 5 +++++ 1 file changed, 5 insertions(+) diff --git a/keymap/vim.js b/keymap/vim.js index 8db2767a9e..9c9454d6c8 100644 --- a/keymap/vim.js +++ b/keymap/vim.js @@ -1104,6 +1104,11 @@ if (vim.visualLine) { if (cursorIsBefore(selectionStart, selectionEnd)) { selectionStart.ch = 0; + + var lastLine = cm.lastLine(); + if (selectionEnd.line > lastLine) { + selectionEnd.line = lastLine; + } selectionEnd.ch = lineLength(cm, selectionEnd.line); } else { selectionEnd.ch = 0; From 455a880611641aeb01e47f0f17999c15318a7af6 Mon Sep 17 00:00:00 2001 From: Hasan Karahan Date: Sat, 3 Aug 2013 21:57:29 +0500 Subject: [PATCH 1378/5780] [rst mode] LaTex/Python inner mode glitches The were some minor issues w.r.t. where exactly an `stex` or `python` inner mode starts and where the outer `rst` mode resumes; fixed. --- mode/rst/rst.js | 35 ++++++++++++++++++++--------------- 1 file changed, 20 insertions(+), 15 deletions(-) diff --git a/mode/rst/rst.js b/mode/rst/rst.js index 508896d25b..54990bb257 100644 --- a/mode/rst/rst.js +++ b/mode/rst/rst.js @@ -124,9 +124,7 @@ CodeMirror.defineMode('rst-base', function (config) { token = 'keyword'; if (stream.current().match(/^(?:math|latex)/)) { - state.tmp = { - mode: mode_stex, local: mode_stex.startState() - }; + state.tmp_stex = true; } break; case 2: @@ -135,6 +133,12 @@ CodeMirror.defineMode('rst-base', function (config) { token = 'meta'; break; case 3: + if (state.tmp_stex) { + state.tmp_stex = undefined; state.tmp = { + mode: mode_stex, local: mode_stex.startState() + }; + } + if (state.tmp) { if (stream.peek() == '`') { change(state, to_normal, context(rx_role_pre, 4)); @@ -345,24 +349,24 @@ CodeMirror.defineMode('rst-base', function (config) { change(state, to_explicit, context(rx_directive, 2)); assert(stream.match(rx_directive_tail)); token = 'meta'; - break; - default: + if (stream.match(/^latex\s*$/) || state.tmp_stex) { - state.tmp_stex = undefined; - change(state, to_mode, { + state.tmp_stex = undefined; change(state, to_mode, { mode: mode_stex, local: mode_stex.startState() }); - } else if (stream.match(/^python\s*$/) || state.tmp_py) { - state.tmp_py = undefined; - change(state, to_mode, { + } + break; + case 2: + change(state, to_explicit, context(rx_directive, 3)); + if (stream.match(/^python\s*$/) || state.tmp_py) { + state.tmp_py = undefined; change(state, to_mode, { mode: mode_python, local: mode_python.startState() }); } - - else { - change(state, to_normal); - assert(stream.current() == ''); - } + break; + default: + change(state, to_normal); + assert(stream.current() == ''); } } else if (phase(state) == rx_link || stream.match(rx_link, false)) { @@ -441,6 +445,7 @@ CodeMirror.defineMode('rst-base', function (config) { return state.ctx.mode.token(stream, state.ctx.local); } catch (ex) { change(state, to_normal); + console.error (ex); return null; } } From f813f18719f3e6e55794a79fe6411fac4711333b Mon Sep 17 00:00:00 2001 From: Hasan Karahan Date: Sun, 4 Aug 2013 13:22:45 +0500 Subject: [PATCH 1379/5780] [rst mode] Removed unknown bugs catcher --- mode/rst/rst.js | 8 +------- 1 file changed, 1 insertion(+), 7 deletions(-) diff --git a/mode/rst/rst.js b/mode/rst/rst.js index 54990bb257..75563ba982 100644 --- a/mode/rst/rst.js +++ b/mode/rst/rst.js @@ -441,13 +441,7 @@ CodeMirror.defineMode('rst-base', function (config) { return null; } - try { - return state.ctx.mode.token(stream, state.ctx.local); - } catch (ex) { - change(state, to_normal); - console.error (ex); - return null; - } + return state.ctx.mode.token(stream, state.ctx.local); } change(state, to_normal); From 29392e85975be5ed5b747966e7bff7949473685c Mon Sep 17 00:00:00 2001 From: Marijn Haverbeke Date: Mon, 5 Aug 2013 13:15:38 +0200 Subject: [PATCH 1380/5780] Prevent onFocus/resetInput from interfering with nested editor --- lib/codemirror.js | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/lib/codemirror.js b/lib/codemirror.js index ba02ba63c2..fccf66314e 100644 --- a/lib/codemirror.js +++ b/lib/codemirror.js @@ -2118,7 +2118,7 @@ window.CodeMirror = (function() { cm.state.focused = true; if (cm.display.wrapper.className.search(/\bCodeMirror-focused\b/) == -1) cm.display.wrapper.className += " CodeMirror-focused"; - resetInput(cm, true); + if (!cm.curOp) resetInput(cm, true); } slowPoll(cm); restartBlink(cm); From 36409a986e38b7f5665db94c6d7d4f3ecfe1da7f Mon Sep 17 00:00:00 2001 From: Marijn Haverbeke Date: Mon, 5 Aug 2013 13:42:48 +0200 Subject: [PATCH 1381/5780] [brace-fold addon] Use getTokenStyleAt instead of getTokenAt when scanning --- addon/fold/brace-fold.js | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/addon/fold/brace-fold.js b/addon/fold/brace-fold.js index 0b4d7dc34d..2c76229eb1 100644 --- a/addon/fold/brace-fold.js +++ b/addon/fold/brace-fold.js @@ -12,7 +12,7 @@ CodeMirror.registerHelper("fold", "brace", function(cm, start) { continue; } if (pass == 1 && found < start.ch) break; - tokenType = cm.getTokenAt(CodeMirror.Pos(line, found + 1)).type; + tokenType = cm.getTokenTypeAt(CodeMirror.Pos(line, found + 1)); if (!/^(comment|string)/.test(tokenType)) return found + 1; at = found - 1; } @@ -34,7 +34,7 @@ CodeMirror.registerHelper("fold", "brace", function(cm, start) { if (nextClose < 0) nextClose = text.length; pos = Math.min(nextOpen, nextClose); if (pos == text.length) break; - if (cm.getTokenAt(CodeMirror.Pos(i, pos + 1)).type == tokenType) { + if (cm.getTokenTypeAt(CodeMirror.Pos(i, pos + 1)) == tokenType) { if (pos == nextOpen) ++count; else if (!--count) { end = i; endCh = pos; break outer; } } From ba21985bfa7c2b8ad6b1ecd564a41b06083d2475 Mon Sep 17 00:00:00 2001 From: Marijn Haverbeke Date: Mon, 5 Aug 2013 13:45:54 +0200 Subject: [PATCH 1382/5780] Use a bigger search range when finding a start-of-parse point in mixed modes --- lib/codemirror.js | 6 +++--- 1 file changed, 3 insertions(+), 3 deletions(-) diff --git a/lib/codemirror.js b/lib/codemirror.js index fccf66314e..483819e8c2 100644 --- a/lib/codemirror.js +++ b/lib/codemirror.js @@ -930,8 +930,8 @@ window.CodeMirror = (function() { // smallest indentation, which tends to need the least context to // parse correctly. function findStartLine(cm, n, precise) { - var minindent, minline, doc = cm.doc; - for (var search = n, lim = n - 100; search > lim; --search) { + var minindent, minline, doc = cm.doc, maxScan = cm.doc.mode.innerMode ? 1000 : 100; + for (var search = n, lim = n - maxScan; search > lim; --search) { if (search <= doc.first) return doc.first; var line = getLine(doc, search - 1); if (line.stateAfter && (!precise || search <= doc.frontier)) return search; @@ -946,7 +946,7 @@ window.CodeMirror = (function() { function getStateBefore(cm, n, precise) { var doc = cm.doc, display = cm.display; - if (!doc.mode.startState) return true; + if (!doc.mode.startState) return true; var pos = findStartLine(cm, n, precise), state = pos > doc.first && getLine(doc, pos-1).stateAfter; if (!state) state = startState(doc.mode); else state = copyState(doc.mode, state); From fd11debd80ddf65fb783fe83a6668e9fc3fa99cc Mon Sep 17 00:00:00 2001 From: Marijn Haverbeke Date: Mon, 5 Aug 2013 14:01:35 +0200 Subject: [PATCH 1383/5780] [brace-fold addon] Fix bad use of lastIndexOf --- addon/fold/brace-fold.js | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/addon/fold/brace-fold.js b/addon/fold/brace-fold.js index 2c76229eb1..2560b2b94c 100644 --- a/addon/fold/brace-fold.js +++ b/addon/fold/brace-fold.js @@ -4,7 +4,7 @@ CodeMirror.registerHelper("fold", "brace", function(cm, start) { function findOpening(openCh) { for (var at = start.ch, pass = 0;;) { - var found = lineText.lastIndexOf(openCh, at - 1); + var found = at <= 0 ? -1 : lineText.lastIndexOf(openCh, at - 1); if (found == -1) { if (pass == 1) break; pass = 1; From 0d45af397e9e02670a1b4e925ddd4fcc5d088820 Mon Sep 17 00:00:00 2001 From: Steve O'Hara Date: Sat, 3 Aug 2013 13:50:37 +0100 Subject: [PATCH 1384/5780] [velocity mode] Update * Updated for CodeMirror 3 - added comment clock declarations * Corrected handling of macro calls with a body * Added extra features for detecting variables within strings and methods & properties of objects * Corrected the detection of methods and properties of Java objects --- mode/velocity/index.html | 4 +++ mode/velocity/velocity.js | 66 ++++++++++++++++++++++++++++++++------- 2 files changed, 58 insertions(+), 12 deletions(-) diff --git a/mode/velocity/index.html b/mode/velocity/index.html index fb59cb590d..96df4c88b8 100644 --- a/mode/velocity/index.html +++ b/mode/velocity/index.html @@ -62,13 +62,17 @@

    CodeMirror: Velocity mode

    $someObject.getValues("this is a string split across lines") +$someObject("This plus $something in the middle").method(7567).property + #macro( tablerows $color $somelist ) #foreach( $something in $somelist ) $something + $bodyContent #end #end #tablerows("red" ["dadsdf","dsa"]) +#@tablerows("red" ["dadsdf","dsa"]) some body content #end Variable reference: #set( $monkey = $bill ) String literal: #set( $monkey.Friend = 'monica' ) diff --git a/mode/velocity/velocity.js b/mode/velocity/velocity.js index 43a97ba676..968d8799e9 100644 --- a/mode/velocity/velocity.js +++ b/mode/velocity/velocity.js @@ -9,7 +9,7 @@ CodeMirror.defineMode("velocity", function() { "#{end} #{else} #{break} #{stop}"); var functions = parseWords("#if #elseif #foreach #set #include #parse #macro #define #evaluate " + "#{if} #{elseif} #{foreach} #{set} #{include} #{parse} #{macro} #{define} #{evaluate}"); - var specials = parseWords("$foreach.count $foreach.hasNext $foreach.first $foreach.last $foreach.topmost $foreach.parent $velocityCount"); + var specials = parseWords("$foreach.count $foreach.hasNext $foreach.first $foreach.last $foreach.topmost $foreach.parent.count $foreach.parent.hasNext $foreach.parent.first $foreach.parent.last $foreach.parent $velocityCount $!bodyContent $bodyContent"); var isOperatorChar = /[+\-*&%=<>!?:\/|]/; function chain(stream, state, f) { @@ -20,30 +20,50 @@ CodeMirror.defineMode("velocity", function() { var beforeParams = state.beforeParams; state.beforeParams = false; var ch = stream.next(); - // start of string? - if ((ch == '"' || ch == "'") && state.inParams) + // start of unparsed string? + if ((ch == "'") && state.inParams) { + state.lastTokenWasBuiltin = false; return chain(stream, state, tokenString(ch)); + } + // start of parsed string? + else if ((ch == '"')) { + state.lastTokenWasBuiltin = false; + if (state.inString) { + state.inString = false; + return "string"; + } + else if (state.inParams) + return chain(stream, state, tokenString(ch)); + } // is it one of the special signs []{}().,;? Seperator? else if (/[\[\]{}\(\),;\.]/.test(ch)) { - if (ch == "(" && beforeParams) state.inParams = true; - else if (ch == ")") state.inParams = false; + if (ch == "(" && beforeParams) + state.inParams = true; + else if (ch == ")") { + state.inParams = false; + state.lastTokenWasBuiltin = true; + } return null; } // start of a number value? else if (/\d/.test(ch)) { + state.lastTokenWasBuiltin = false; stream.eatWhile(/[\w\.]/); return "number"; } // multi line comment? else if (ch == "#" && stream.eat("*")) { + state.lastTokenWasBuiltin = false; return chain(stream, state, tokenComment); } // unparsed content? else if (ch == "#" && stream.match(/ *\[ *\[/)) { + state.lastTokenWasBuiltin = false; return chain(stream, state, tokenUnparsed); } // single line comment? else if (ch == "#" && stream.eat("#")) { + state.lastTokenWasBuiltin = false; stream.skipToEnd(); return "comment"; } @@ -51,33 +71,44 @@ CodeMirror.defineMode("velocity", function() { else if (ch == "$") { stream.eatWhile(/[\w\d\$_\.{}]/); // is it one of the specials? - if (specials && specials.propertyIsEnumerable(stream.current().toLowerCase())) { + if (specials && specials.propertyIsEnumerable(stream.current())) { return "keyword"; } else { + state.lastTokenWasBuiltin = true; state.beforeParams = true; return "builtin"; } } // is it a operator? else if (isOperatorChar.test(ch)) { + state.lastTokenWasBuiltin = false; stream.eatWhile(isOperatorChar); return "operator"; } else { // get the whole word - stream.eatWhile(/[\w\$_{}]/); - var word = stream.current().toLowerCase(); + stream.eatWhile(/[\w\$_{}@]/); + var word = stream.current(); // is it one of the listed keywords? if (keywords && keywords.propertyIsEnumerable(word)) return "keyword"; // is it one of the listed functions? if (functions && functions.propertyIsEnumerable(word) || - stream.current().match(/^#[a-z0-9_]+ *$/i) && stream.peek()=="(") { + (stream.current().match(/^#@?[a-z0-9_]+ *$/i) && stream.peek()=="(") && + !(functions && functions.propertyIsEnumerable(word.toLowerCase()))) { state.beforeParams = true; + state.lastTokenWasBuiltin = false; return "keyword"; } + if (state.inString) { + state.lastTokenWasBuiltin = false; + return "string"; + } + if (stream.pos > word.length && stream.string.charAt(stream.pos-word.length-1)=="." && state.lastTokenWasBuiltin) + return "builtin"; // default: just a "word" + state.lastTokenWasBuiltin = false; return null; } } @@ -86,7 +117,12 @@ CodeMirror.defineMode("velocity", function() { return function(stream, state) { var escaped = false, next, end = false; while ((next = stream.next()) != null) { - if (next == quote && !escaped) { + if ((next == quote) && !escaped) { + end = true; + break; + } + if (quote=='"' && stream.peek() == '$' && !escaped) { + state.inString = true; end = true; break; } @@ -130,14 +166,20 @@ CodeMirror.defineMode("velocity", function() { return { tokenize: tokenBase, beforeParams: false, - inParams: false + inParams: false, + inString: false, + lastTokenWasBuiltin: false }; }, token: function(stream, state) { if (stream.eatSpace()) return null; return state.tokenize(stream, state); - } + }, + blockCommentStart: "#*", + blockCommentEnd: "*#", + lineComment: "##", + fold: "velocity" }; }); From 55583715087cf148be173f1eacd1840ee1a944ec Mon Sep 17 00:00:00 2001 From: Marijn Haverbeke Date: Mon, 5 Aug 2013 15:28:06 +0200 Subject: [PATCH 1385/5780] (Mostly) handle cursor positioning around multiple bookmarks in same position Closes #1726 --- lib/codemirror.js | 10 ++++++---- test/test.js | 17 +++++++++++++++++ 2 files changed, 23 insertions(+), 4 deletions(-) diff --git a/lib/codemirror.js b/lib/codemirror.js index 483819e8c2..cead3e409b 100644 --- a/lib/codemirror.js +++ b/lib/codemirror.js @@ -4415,11 +4415,13 @@ window.CodeMirror = (function() { if (size) { builder.measure[builder.pos] = widget; } else { - var elt = builder.measure[builder.pos] = zeroWidthElement(builder.cm.display.measure); - if (marker.type != "bookmark" || marker.insertLeft) - builder.pre.insertBefore(elt, widget); + var elt = zeroWidthElement(builder.cm.display.measure); + if (marker.type == "bookmark" && !marker.insertLeft) + builder.measure[builder.pos] = builder.pre.appendChild(elt); + else if (builder.measure[builder.pos]) + return; else - builder.pre.appendChild(elt); + builder.measure[builder.pos] = builder.pre.insertBefore(elt, widget); } builder.measuredSomething = true; } diff --git a/test/test.js b/test/test.js index 69897648dc..57777f86f9 100644 --- a/test/test.js +++ b/test/test.js @@ -517,6 +517,23 @@ testCM("bookmarkCursor", function(cm) { is(cm.cursorCoords(Pos(4, 1)).left > pos41.left, "single-char bug"); }, {value: "foo\nbar\n\n\nx\ny"}); +testCM("multiBookmarkCursor", function(cm) { + var ms = [], m; + function add(insertLeft) { + for (var i = 0; i < 3; ++i) { + var node = document.createElement("span"); + node.innerHTML = "X"; + ms.push(cm.setBookmark(Pos(0, 1), {widget: node, insertLeft: insertLeft})); + } + } + var base1 = cm.cursorCoords(Pos(0, 1)).left, base4 = cm.cursorCoords(Pos(0, 4)).left; + add(true); + eq(base1, cm.cursorCoords(Pos(0, 1)).left); + while (m = ms.pop()) m.clear(); + add(false); + eq(base4, cm.cursorCoords(Pos(0, 1)).left); +}, {value: "abcdefg"}); + testCM("getAllMarks", function(cm) { addDoc(cm, 10, 10); var m1 = cm.setBookmark(Pos(0, 2)); From 84d12b21be4033fb09145759ce0c7e4be12e7f9f Mon Sep 17 00:00:00 2001 From: Marijn Haverbeke Date: Mon, 5 Aug 2013 15:31:42 +0200 Subject: [PATCH 1386/5780] Disable another test on Phantom (Works in real browsers.) --- test/test.js | 1 + 1 file changed, 1 insertion(+) diff --git a/test/test.js b/test/test.js index 57777f86f9..1c37b4dfe1 100644 --- a/test/test.js +++ b/test/test.js @@ -518,6 +518,7 @@ testCM("bookmarkCursor", function(cm) { }, {value: "foo\nbar\n\n\nx\ny"}); testCM("multiBookmarkCursor", function(cm) { + if (phantom) return; var ms = [], m; function add(insertLeft) { for (var i = 0; i < 3; ++i) { From 5804a9322cdb68be041ffc7a515e982cf7a63bc8 Mon Sep 17 00:00:00 2001 From: Marijn Haverbeke Date: Thu, 8 Aug 2013 10:15:05 +0200 Subject: [PATCH 1387/5780] Re-reset the input right after a focus event It seems recent versions of Chrome/Blink fire the handler first, and then clear the selection. Issue #1730 --- lib/codemirror.js | 5 ++++- 1 file changed, 4 insertions(+), 1 deletion(-) diff --git a/lib/codemirror.js b/lib/codemirror.js index cead3e409b..75882a4416 100644 --- a/lib/codemirror.js +++ b/lib/codemirror.js @@ -2118,7 +2118,10 @@ window.CodeMirror = (function() { cm.state.focused = true; if (cm.display.wrapper.className.search(/\bCodeMirror-focused\b/) == -1) cm.display.wrapper.className += " CodeMirror-focused"; - if (!cm.curOp) resetInput(cm, true); + if (!cm.curOp) { + resetInput(cm, true); + if (webkit) setTimeout(bind(resetInput, cm, true), 0); // Issue #1730 + } } slowPoll(cm); restartBlink(cm); From 7f82421f694e680fda16c05bfbd9c061382949ba Mon Sep 17 00:00:00 2001 From: Andrey Lushnikov Date: Wed, 7 Aug 2013 20:47:05 +0400 Subject: [PATCH 1388/5780] [xml mode] Fix value-less attributes Issue #902 --- mode/xml/xml.js | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/mode/xml/xml.js b/mode/xml/xml.js index 84f34a2934..53285c848c 100644 --- a/mode/xml/xml.js +++ b/mode/xml/xml.js @@ -261,7 +261,7 @@ CodeMirror.defineMode("xml", function(config, parserConfig) { function attribute(type) { if (type == "equals") return cont(attvalue, attributes); if (!Kludges.allowMissing) setStyle = "error"; - else if (type == "word") setStyle = "attribute"; + else if (type == "word") {setStyle = "attribute"; return cont(attribute, attributes);} return (type == "endTag" || type == "selfcloseTag") ? pass() : cont(); } function attvalue(type) { From 4e061c0b09871d4c3df9b14cae54a00e5286171a Mon Sep 17 00:00:00 2001 From: Marijn Haverbeke Date: Fri, 9 Aug 2013 12:40:36 +0200 Subject: [PATCH 1389/5780] Clear measure div after line measurement To prevent a lot of DOM nodes from sticking around after measuring a long line, easing the pressure on the GC. --- lib/codemirror.js | 1 + 1 file changed, 1 insertion(+) diff --git a/lib/codemirror.js b/lib/codemirror.js index 75882a4416..7946092ea1 100644 --- a/lib/codemirror.js +++ b/lib/codemirror.js @@ -1096,6 +1096,7 @@ window.CodeMirror = (function() { if (cur.measureRight) rect.right = getRect(cur.measureRight).left; if (cur.leftSide) rect.leftSide = measureRect(getRect(cur.leftSide)); } + removeChildren(cm.display.measure); for (var i = 0, cur; i < data.length; ++i) if (cur = data[i]) { finishRect(cur); if (cur.leftSide) finishRect(cur.leftSide); From d64948850d2abcf9ca1e51d712ed658ae5b12218 Mon Sep 17 00:00:00 2001 From: Marijn Haverbeke Date: Fri, 9 Aug 2013 12:49:38 +0200 Subject: [PATCH 1390/5780] [continuecomment addon] Move to addon/comment (from addon/edit) --- addon/{edit => comment}/continuecomment.js | 0 doc/compress.html | 4 ++-- doc/manual.html | 2 +- mode/javascript/index.html | 2 +- 4 files changed, 4 insertions(+), 4 deletions(-) rename addon/{edit => comment}/continuecomment.js (100%) diff --git a/addon/edit/continuecomment.js b/addon/comment/continuecomment.js similarity index 100% rename from addon/edit/continuecomment.js rename to addon/comment/continuecomment.js diff --git a/doc/compress.html b/doc/compress.html index 864f4f28cd..e2d79f581d 100644 --- a/doc/compress.html +++ b/doc/compress.html @@ -148,8 +148,8 @@

    { } CodeMi - - + + diff --git a/doc/manual.html b/doc/manual.html index dc4100f70b..532285163c 100644 --- a/doc/manual.html +++ b/doc/manual.html @@ -2060,7 +2060,7 @@

    Addons

    editor instance to refresh its mode when the loading succeeded. See the
    demo. -
    edit/continuecomment.js
    +
    comment/continuecomment.js
    Adds an continueComments option, which can be set to true to have the editor prefix new lines inside C-like block comments with an asterisk when Enter is pressed. It can diff --git a/mode/javascript/index.html b/mode/javascript/index.html index db063b772d..0db35219b9 100644 --- a/mode/javascript/index.html +++ b/mode/javascript/index.html @@ -6,7 +6,7 @@ - + From 57036ab839b159632cc13404e547114f17aba415 Mon Sep 17 00:00:00 2001 From: Chris Coyier Date: Fri, 9 Aug 2013 18:10:46 -0500 Subject: [PATCH 1391/5780] "grey" is an accepted named color value (same as "gray") --- mode/css/css.js | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/mode/css/css.js b/mode/css/css.js index bdcd500868..c393cedff5 100644 --- a/mode/css/css.js +++ b/mode/css/css.js @@ -432,7 +432,7 @@ CodeMirror.defineMode("css-base", function(config, parserConfig) { "darkslateblue", "darkslategray", "darkturquoise", "darkviolet", "deeppink", "deepskyblue", "dimgray", "dodgerblue", "firebrick", "floralwhite", "forestgreen", "fuchsia", "gainsboro", "ghostwhite", - "gold", "goldenrod", "gray", "green", "greenyellow", "honeydew", + "gold", "goldenrod", "gray", "grey", "green", "greenyellow", "honeydew", "hotpink", "indianred", "indigo", "ivory", "khaki", "lavender", "lavenderblush", "lawngreen", "lemonchiffon", "lightblue", "lightcoral", "lightcyan", "lightgoldenrodyellow", "lightgray", "lightgreen", "lightpink", From 3f5c8432b6080e22da5ded416c4e4d7a0f4ff341 Mon Sep 17 00:00:00 2001 From: Maksym Taran Date: Sun, 11 Aug 2013 15:20:05 -0700 Subject: [PATCH 1392/5780] Python function/class definition highlighting. --- mode/python/python.js | 16 ++++++++++++---- 1 file changed, 12 insertions(+), 4 deletions(-) diff --git a/mode/python/python.js b/mode/python/python.js index 4fe4d28912..28108e4c1a 100644 --- a/mode/python/python.js +++ b/mode/python/python.js @@ -151,6 +151,9 @@ CodeMirror.defineMode("python", function(conf, parserConf) { } if (stream.match(identifiers)) { + if (state.lastToken == 'def' || state.lastToken == 'class') { + return 'def'; + } return 'variable'; } @@ -258,7 +261,7 @@ CodeMirror.defineMode("python", function(conf, parserConf) { // Handle '.' connected identifiers if (current === '.') { style = stream.match(identifiers, false) ? null : ERRORCLASS; - if (style === null && state.lastToken === 'meta') { + if (style === null && state.lastStyle === 'meta') { // Apply 'meta' style to '.' connected identifiers when // appropriate. style = 'meta'; @@ -272,7 +275,7 @@ CodeMirror.defineMode("python", function(conf, parserConf) { } if ((style === 'variable' || style === 'builtin') - && state.lastToken === 'meta') { + && state.lastStyle === 'meta') { style = 'meta'; } @@ -313,6 +316,7 @@ CodeMirror.defineMode("python", function(conf, parserConf) { return { tokenize: tokenBase, scopes: [{offset:basecolumn || 0, type:'py'}], + lastStyle: null, lastToken: null, lambda: false, dedent: 0 @@ -322,12 +326,16 @@ CodeMirror.defineMode("python", function(conf, parserConf) { token: function(stream, state) { var style = tokenLexer(stream, state); - state.lastToken = style; + state.lastStyle = style; + + var current = stream.current(); + if (current && style) { + state.lastToken = current; + } if (stream.eol() && state.lambda) { state.lambda = false; } - return style; }, From b8a38503a24d143b975603c1509b9340ae9abbf2 Mon Sep 17 00:00:00 2001 From: mats cronqvist Date: Sun, 11 Aug 2013 19:41:13 +0200 Subject: [PATCH 1393/5780] erlang mode bug fixes --- mode/erlang/erlang.js | 56 ++++++++++++++++++++++-------------------- mode/erlang/index.html | 2 +- 2 files changed, 31 insertions(+), 27 deletions(-) diff --git a/mode/erlang/erlang.js b/mode/erlang/erlang.js index 79e0434d1f..f6e304c93a 100644 --- a/mode/erlang/erlang.js +++ b/mode/erlang/erlang.js @@ -11,18 +11,10 @@ CodeMirror.defineMIME("text/x-erlang", "erlang"); CodeMirror.defineMode("erlang", function(cmCfg) { - function rval(state,stream,type) { + function rval(state,_stream,type) { // distinguish between "." as terminator and record field operator - if (type == "record") { - state.context = "record"; - }else{ - state.context = false; - } + state.in_record = (type == "record"); - // remember last significant bit on last line for indenting - if (type != "whitespace" && type != "comment") { - state.lastToken = stream.current(); - } // erlang -> CodeMirror tag switch (type) { case "atom": return "atom"; @@ -133,6 +125,19 @@ CodeMirror.defineMode("erlang", function(cmCfg) { } function tokenize(stream, state) { + // in multi-line string + if (state.in_string) { + state.in_string = (!doubleQuote(stream)); + return rval(state,stream,"string"); + } + + // in multi-line atom + if (state.in_atom) { + state.in_atom = (!singleQuote(stream)); + return rval(state,stream,"atom"); + } + + // whitespace if (stream.eatSpace()) { return rval(state,stream,"whitespace"); } @@ -183,20 +188,14 @@ CodeMirror.defineMode("erlang", function(cmCfg) { // quoted atom if (ch == '\'') { - if (singleQuote(stream)) { - return rval(state,stream,"atom"); - }else{ - return rval(state,stream,"error"); - } + state.in_atom = (!singleQuote(stream)); + return rval(state,stream,"atom"); } // string if (ch == '"') { - if (doubleQuote(stream)) { - return rval(state,stream,"string"); - }else{ - return rval(state,stream,"error"); - } + state.in_string = (!doubleQuote(stream)); + return rval(state,stream,"string"); } // variable @@ -282,7 +281,7 @@ CodeMirror.defineMode("erlang", function(cmCfg) { // separators if (greedy(stream,sepRE,separatorWords)) { // distinguish between "." as terminator and record field operator - if (state.context == false) { + if (!state.in_record) { pushToken(state,stream); } return rval(state,stream,"separator"); @@ -359,15 +358,19 @@ CodeMirror.defineMode("erlang", function(cmCfg) { var token = (peekToken(state)).token; var wordAfter = takewhile(textAfter,/[^a-z]/); - if (isMember(token,openParenWords)) { - return (peekToken(state)).column+token.length; - }else if (token == "." || token == ""){ + if (state.in_string || state.in_atom) { return 0; + }else if (token == "." || token == "") { + return 0; + }else if (isMember(token,openParenWords)) { + return (peekToken(state)).column+token.length; }else if (token == "->") { if (wordAfter == "end") { return peekToken(state,2).column; }else if (peekToken(state,2).token == "fun") { return peekToken(state,2).column+indent; + }else if (peekToken(state,2).token == ".") { + return indent; }else{ return (peekToken(state)).indent+indent; } @@ -445,8 +448,9 @@ CodeMirror.defineMode("erlang", function(cmCfg) { startState: function() { return {tokenStack: [], - context: false, - lastToken: null}; + in_record: false, + in_string: false, + in_atom: false}; }, token: diff --git a/mode/erlang/index.html b/mode/erlang/index.html index fd21521c88..5bb81f669a 100644 --- a/mode/erlang/index.html +++ b/mode/erlang/index.html @@ -32,7 +32,7 @@

    CodeMirror: Erlang mode

    rec_info(demo) -> record_info(fields,demo). demo() -> expand_recs(?MODULE,#demo{a="A",b="BB"}). - + expand_recs(M,List) when is_list(List) -> [expand_recs(M,L)||L<-List]; expand_recs(M,Tup) when is_tuple(Tup) -> From 8a55c04f984e9a7ee0091eb1bbe277657357485a Mon Sep 17 00:00:00 2001 From: Chandra Sekhar Pydi Date: Fri, 9 Aug 2013 11:32:58 -0700 Subject: [PATCH 1394/5780] Configurable delay between selection of a word to highlighting of word. current delay of 100ms between selection of word to highlighting is almost instantaneous. --- addon/search/match-highlighter.js | 5 ++++- 1 file changed, 4 insertions(+), 1 deletion(-) diff --git a/addon/search/match-highlighter.js b/addon/search/match-highlighter.js index 3df6985984..e5cbeacab2 100644 --- a/addon/search/match-highlighter.js +++ b/addon/search/match-highlighter.js @@ -15,15 +15,18 @@ (function() { var DEFAULT_MIN_CHARS = 2; var DEFAULT_TOKEN_STYLE = "matchhighlight"; + var DEFAULT_DELAY = 100; function State(options) { if (typeof options == "object") { this.minChars = options.minChars; this.style = options.style; this.showToken = options.showToken; + this.delay = options.delay; } if (this.style == null) this.style = DEFAULT_TOKEN_STYLE; if (this.minChars == null) this.minChars = DEFAULT_MIN_CHARS; + if (this.delay == null) this.delay = DEFAULT_DELAY; this.overlay = this.timeout = null; } @@ -45,7 +48,7 @@ function cursorActivity(cm) { var state = cm.state.matchHighlighter; clearTimeout(state.timeout); - state.timeout = setTimeout(function() {highlightMatches(cm);}, 100); + state.timeout = setTimeout(function() {highlightMatches(cm);}, state.delay); } function highlightMatches(cm) { From 1f27f945e646e08b2ca6fb53c07d2e6f9e45e764 Mon Sep 17 00:00:00 2001 From: Daniel Huigens Date: Fri, 12 Jul 2013 13:51:19 +0200 Subject: [PATCH 1395/5780] Fix linenumbers when undoing multiple changes --- lib/codemirror.js | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/lib/codemirror.js b/lib/codemirror.js index 7946092ea1..e5137c6219 100644 --- a/lib/codemirror.js +++ b/lib/codemirror.js @@ -461,7 +461,7 @@ window.CodeMirror = (function() { var positionsChangedFrom = Infinity; if (cm.options.lineNumbers) for (var i = 0; i < changes.length; ++i) - if (changes[i].diff) { positionsChangedFrom = changes[i].from; break; } + if (changes[i].diff && changes[i].from < positionsChangedFrom) { positionsChangedFrom = changes[i].from; } var end = doc.first + doc.size; var from = Math.max(visible.from - cm.options.viewportMargin, doc.first); From 0252410f301ed01603e6ace8e5bc0b728f0fefcc Mon Sep 17 00:00:00 2001 From: Marijn Haverbeke Date: Mon, 12 Aug 2013 11:49:15 +0200 Subject: [PATCH 1396/5780] Fix bug in handling of Esc key in IE --- lib/codemirror.js | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/lib/codemirror.js b/lib/codemirror.js index e5137c6219..4e610ee64b 100644 --- a/lib/codemirror.js +++ b/lib/codemirror.js @@ -2081,8 +2081,8 @@ window.CodeMirror = (function() { function onKeyDown(e) { var cm = this; if (!cm.state.focused) onFocus(cm); - if (ie && e.keyCode == 27) { e.returnValue = false; } if (signalDOMEvent(cm, e) || cm.options.onKeyEvent && cm.options.onKeyEvent(cm, addStop(e))) return; + if (ie && e.keyCode == 27) e.returnValue = false; var code = e.keyCode; // IE does strange things with escape. cm.doc.sel.shift = code == 16 || e.shiftKey; From 015b2e6399c1da1f0481f372313014e5bba74c38 Mon Sep 17 00:00:00 2001 From: Ingo Richter Date: Mon, 12 Aug 2013 18:16:52 -0700 Subject: [PATCH 1397/5780] [css mode] Add properties for CSS regions --- mode/css/css.js | 14 +++++++------- 1 file changed, 7 insertions(+), 7 deletions(-) diff --git a/mode/css/css.js b/mode/css/css.js index c393cedff5..c6d34a3a97 100644 --- a/mode/css/css.js +++ b/mode/css/css.js @@ -366,9 +366,9 @@ CodeMirror.defineMode("css-base", function(config, parserConfig) { "drop-initial-before-align", "drop-initial-size", "drop-initial-value", "elevation", "empty-cells", "fit", "fit-position", "flex", "flex-basis", "flex-direction", "flex-flow", "flex-grow", "flex-shrink", "flex-wrap", - "float", "float-offset", "font", "font-feature-settings", "font-family", - "font-kerning", "font-language-override", "font-size", "font-size-adjust", - "font-stretch", "font-style", "font-synthesis", "font-variant", + "float", "float-offset", "flow-into", "flow-from", "font", "font-feature-settings", + "font-family", "font-kerning", "font-language-override", "font-size", + "font-size-adjust", "font-stretch", "font-style", "font-synthesis", "font-variant", "font-variant-alternates", "font-variant-caps", "font-variant-east-asian", "font-variant-ligatures", "font-variant-numeric", "font-variant-position", "font-weight", "grid-cell", "grid-column", "grid-column-align", @@ -393,8 +393,8 @@ CodeMirror.defineMode("css-base", function(config, parserConfig) { "perspective-origin", "pitch", "pitch-range", "play-during", "position", "presentation-level", "punctuation-trim", "quotes", "rendering-intent", "resize", "rest", "rest-after", "rest-before", "richness", "right", - "rotation", "rotation-point", "ruby-align", "ruby-overhang", - "ruby-position", "ruby-span", "size", "speak", "speak-as", "speak-header", + "rotation", "rotation-point", "ruby-align", "ruby-overhang", "ruby-position", + "ruby-span", "shape-inside", "shape-outside", "size", "speak", "speak-as", "speak-header", "speak-numeral", "speak-punctuation", "speech-rate", "stress", "string-set", "tab-size", "table-layout", "target", "target-name", "target-new", "target-position", "text-align", "text-align-last", "text-decoration", @@ -504,8 +504,8 @@ CodeMirror.defineMode("css-base", function(config, parserConfig) { "narrower", "ne-resize", "nesw-resize", "no-close-quote", "no-drop", "no-open-quote", "no-repeat", "none", "normal", "not-allowed", "nowrap", "ns-resize", "nw-resize", "nwse-resize", "oblique", "octal", "open-quote", - "optimizeLegibility", "optimizeSpeed", "oriya", "oromo", "outset", - "outside", "overlay", "overline", "padding", "padding-box", "painted", + "optimizeLegibility", "optimizeSpeed", "oriya", "oromo", "outset", "outside", + "outside-shape", "overlay", "overline", "padding", "padding-box", "painted", "paused", "persian", "plus-darker", "plus-lighter", "pointer", "portrait", "pre", "pre-line", "pre-wrap", "preserve-3d", "progress", "push-button", "radio", "read-only", "read-write", "read-write-plaintext-only", "relative", From 12a25eaf34f9dc7cf0fd6f6663ad1160408c7e4f Mon Sep 17 00:00:00 2001 From: Marijn Haverbeke Date: Tue, 13 Aug 2013 18:34:34 +0200 Subject: [PATCH 1398/5780] Move to a new website layout --- demo/activeline.html | 41 +- demo/anywordhint.html | 44 +- demo/bidi.html | 41 +- demo/btree.html | 45 +- demo/buffers.html | 43 +- demo/changemode.html | 41 +- demo/closebrackets.html | 54 +-- demo/closetag.html | 55 ++- demo/complete.html | 52 +- demo/emacs.html | 53 ++- demo/folding.html | 54 ++- demo/fullscreen.html | 41 +- demo/html5complete.html | 56 ++- demo/indentwrap.html | 41 +- demo/lint.html | 51 +- demo/loadmode.html | 39 +- demo/marker.html | 41 +- demo/markselection.html | 41 +- demo/matchhighlighter.html | 41 +- demo/matchtags.html | 45 +- demo/merge.html | 41 +- demo/multiplex.html | 41 +- demo/mustache.html | 43 +- demo/placeholder.html | 39 +- demo/preview.html | 46 +- demo/resize.html | 39 +- demo/runmode.html | 40 +- demo/search.html | 47 +- demo/spanaffectswrapping_shim.html | 32 +- demo/tern.html | 76 +-- demo/theme.html | 89 ++-- demo/trailingspace.html | 39 +- demo/variableheight.html | 45 +- demo/vim.html | 47 +- demo/visibletabs.html | 39 +- demo/widget.html | 41 +- demo/xmlcomplete.html | 46 +- doc/activebookmark.js | 42 ++ doc/baboon.png | Bin 23299 -> 0 bytes doc/baboon_vector.svg | 153 ------ doc/compress.html | 38 +- doc/docs.css | 274 ++++++----- doc/internals.html | 108 +++-- doc/logo.png | Bin 0 -> 12003 bytes doc/logo.svg | 147 ++++++ doc/manual.html | 158 +++--- doc/modes.html | 100 ---- doc/realworld.html | 37 +- doc/{oldrelease.html => releases.html} | 350 +++++++++++--- doc/reporting.html | 41 +- doc/upgrade_v2.2.html | 48 +- doc/upgrade_v3.html | 135 +++--- index.html | 634 +++++++------------------ mode/apl/index.html | 41 +- mode/asterisk/index.html | 42 +- mode/clike/index.html | 41 +- mode/clike/scala.html | 54 +-- mode/clojure/index.html | 42 +- mode/cobol/index.html | 85 ++-- mode/coffeescript/index.html | 42 +- mode/commonlisp/index.html | 42 +- mode/css/index.html | 42 +- mode/css/scss.html | 42 +- mode/d/index.html | 41 +- mode/diff/index.html | 42 +- mode/ecl/index.html | 43 +- mode/erlang/index.html | 43 +- mode/gas/index.html | 43 +- mode/gfm/index.html | 56 ++- mode/go/index.html | 43 +- mode/groovy/index.html | 41 +- mode/haml/index.html | 50 +- mode/haskell/index.html | 43 +- mode/haxe/index.html | 41 +- mode/htmlembedded/index.html | 47 +- mode/htmlmixed/index.html | 50 +- mode/http/index.html | 41 +- mode/index.html | 106 +++++ mode/jade/index.html | 42 +- mode/javascript/index.html | 47 +- mode/javascript/typescript.html | 41 +- mode/jinja2/index.html | 42 +- mode/less/index.html | 46 +- mode/livescript/index.html | 43 +- mode/lua/index.html | 46 +- mode/markdown/index.html | 46 +- mode/mirc/index.html | 44 +- mode/nginx/index.html | 40 +- mode/ntriples/index.html | 42 +- mode/ocaml/index.html | 29 +- mode/pascal/index.html | 41 +- mode/perl/index.html | 41 +- mode/php/index.html | 51 +- mode/pig/index.html | 37 +- mode/properties/index.html | 42 +- mode/python/index.html | 44 +- mode/q/index.html | 43 +- mode/r/index.html | 42 +- mode/rpm/changes/index.html | 34 +- mode/rpm/spec/index.html | 33 +- mode/rst/index.html | 40 +- mode/ruby/index.html | 44 +- mode/rust/index.html | 41 +- mode/sass/index.html | 44 +- mode/scheme/index.html | 42 +- mode/shell/index.html | 29 +- mode/sieve/index.html | 42 +- mode/smalltalk/index.html | 41 +- mode/smarty/index.html | 44 +- mode/smartymixed/index.html | 51 +- mode/sparql/index.html | 44 +- mode/sql/index.html | 57 +-- mode/stex/index.html | 42 +- mode/tcl/index.html | 44 +- mode/tiddlywiki/index.html | 45 +- mode/tiki/index.html | 44 +- mode/turtle/index.html | 42 +- mode/vb/index.html | 49 +- mode/vbscript/index.html | 42 +- mode/velocity/index.html | 44 +- mode/verilog/index.html | 39 +- mode/xml/index.html | 42 +- mode/xquery/index.html | 71 ++- mode/yaml/index.html | 42 +- mode/z80/index.html | 41 +- test/index.html | 105 ++-- 126 files changed, 4225 insertions(+), 3079 deletions(-) create mode 100644 doc/activebookmark.js delete mode 100644 doc/baboon.png delete mode 100644 doc/baboon_vector.svg create mode 100644 doc/logo.png create mode 100644 doc/logo.svg delete mode 100644 doc/modes.html rename doc/{oldrelease.html => releases.html} (70%) create mode 100644 mode/index.html diff --git a/demo/activeline.html b/demo/activeline.html index b0ea9b9070..a9b000e65b 100644 --- a/demo/activeline.html +++ b/demo/activeline.html @@ -1,23 +1,33 @@ - - - - CodeMirror: Active Line Demo - - - - - - - - -

    CodeMirror: Active Line Demo

    + -
    +
    +

    B-Tree visualization

    +

    @@ -83,5 +83,4 @@

    CodeMirror: B-Tree visualization

    - - + diff --git a/demo/buffers.html b/demo/buffers.html index bfd8248e43..951209cad5 100644 --- a/demo/buffers.html +++ b/demo/buffers.html @@ -1,20 +1,32 @@ - - - - CodeMirror: Multiple Buffer & Split View Demo - - - - - - - - - -

    CodeMirror: Multiple Buffer & Split View Demo

    + + +
    +

    Multiple Buffer & Split View Demo

    +
    @@ -94,5 +106,4 @@

    CodeMirror: Multiple Buffer & Split View Demo

    using swapDoc to use a single editor to display multiple documents.

    - - +
    diff --git a/demo/changemode.html b/demo/changemode.html index 364c5cdb07..61c1786074 100644 --- a/demo/changemode.html +++ b/demo/changemode.html @@ -1,22 +1,32 @@ - - - - CodeMirror: Mode-Changing Demo - - - - - - - - -

    CodeMirror: Mode-Changing demo

    + -
    +
    +

    Close-Tag Demo

    +
    - - +
    diff --git a/demo/complete.html b/demo/complete.html index 1a65f6e0d9..56999b9cc9 100644 --- a/demo/complete.html +++ b/demo/complete.html @@ -1,20 +1,31 @@ - - - - CodeMirror: Autocomplete Demo - - - - - - - - - -

    CodeMirror: Autocomplete demo

    -
    -

    Press ctrl-space to activate autocompletion. See -the code (here -and here) to figure out -how it works.

    +

    Press ctrl-space to activate autocompletion. Built +on top of the show-hint +and javascript-hint +addons.

    - - + diff --git a/demo/emacs.html b/demo/emacs.html index 0a8cfc5d99..5b622a9992 100644 --- a/demo/emacs.html +++ b/demo/emacs.html @@ -1,28 +1,38 @@ - - - - CodeMirror: Emacs bindings demo - - - - - - - - - - - - - - -

    CodeMirror: Emacs bindings demo

    + -
    HTML:
    @@ -81,5 +86,4 @@

    CodeMirror: Code Folding Demo

    editor_html.foldCode(CodeMirror.Pos(1, 0)); }; - - + diff --git a/demo/fullscreen.html b/demo/fullscreen.html index 2709ebb4b5..8cf8005bdb 100644 --- a/demo/fullscreen.html +++ b/demo/fullscreen.html @@ -1,15 +1,14 @@ - - - - CodeMirror: Full Screen Editing - - - - - - - - -

    CodeMirror: Full Screen Editing

    + -
    @@ -32,5 +42,4 @@

    CodeMirror: Mark Selection Demo

    Simple addon to easily mark (and style) selected text.

    - - + diff --git a/demo/matchhighlighter.html b/demo/matchhighlighter.html index 9d2fdd098a..170213bfc5 100644 --- a/demo/matchhighlighter.html +++ b/demo/matchhighlighter.html @@ -1,15 +1,14 @@ - - - - CodeMirror: Match Highlighter Demo - - - - - - - - -

    CodeMirror: Match Highlighter Demo

    + -
    @@ -34,5 +44,4 @@

    CodeMirror: Match Highlighter Demo

    Search and highlight occurences of the selected text.

    - - + diff --git a/demo/matchtags.html b/demo/matchtags.html index 8f4217debe..40ee8c0b7e 100644 --- a/demo/matchtags.html +++ b/demo/matchtags.html @@ -1,22 +1,34 @@ - - - - CodeMirror: Tag Matcher Demo - - - - - - - - - - -

    CodeMirror: Tag Matcher Demo

    + + +
    +

    Tag Matcher Demo

    +
    @@ -34,5 +46,4 @@

    CodeMirror: Tag Matcher Demo

    Put the cursor on or inside a pair of tags to highlight them. Press Ctrl-J to jump to the tag that matches the one under the cursor.

    - - +
    diff --git a/demo/merge.html b/demo/merge.html index 907f769d57..d3c8eb03cc 100644 --- a/demo/merge.html +++ b/demo/merge.html @@ -1,15 +1,16 @@ - - - - - - - - - - CodeMirror: merge view demo - - + + +
    +

    merge view demo

    -

    CodeMirror: merge view demo

    @@ -61,3 +75,4 @@

    CodeMirror: merge view demo

    initUI(2); }; +
    diff --git a/demo/multiplex.html b/demo/multiplex.html index ec0519cb98..2ad9608a82 100644 --- a/demo/multiplex.html +++ b/demo/multiplex.html @@ -1,23 +1,33 @@ - - - - CodeMirror: Multiplexing Parser Demo - - - - - - - - -

    CodeMirror: Multiplexing Parser Demo

    + -
    +
    +

    Placeholder demo

    +

    The placeholder plug-in adds an option placeholder that can be set to @@ -32,5 +42,4 @@

    CodeMirror: Placeholder demo

    }); - - +
    diff --git a/demo/preview.html b/demo/preview.html index f70cdb009a..ccf9122e9b 100644 --- a/demo/preview.html +++ b/demo/preview.html @@ -1,16 +1,16 @@ - - - - CodeMirror: HTML5 preview - - - - - - - - - - -

    CodeMirror: HTML5 preview

    + + +
    +

    HTML5 preview

    + @@ -35,5 +45,4 @@

    CodeMirror: Trailing Whitespace Demo

    the trailingspace addon to highlight trailing whitespace.

    - - +
    diff --git a/demo/variableheight.html b/demo/variableheight.html index b00f7e4542..1ef8fc445d 100644 --- a/demo/variableheight.html +++ b/demo/variableheight.html @@ -1,24 +1,34 @@ - - - - CodeMirror: Variable Height Demo - - - - - - - - - -

    CodeMirror: Variable Height Demo

    - -

    Press ctrl-space, or type a '<' character to @@ -102,5 +113,4 @@

    CodeMirror: XML Autocomplete demo

    } }); - - + diff --git a/doc/activebookmark.js b/doc/activebookmark.js new file mode 100644 index 0000000000..69a126e8ce --- /dev/null +++ b/doc/activebookmark.js @@ -0,0 +1,42 @@ +(function() { + var pending = false, prevVal = null; + + function updateSoon() { + if (!pending) { + pending = true; + setTimeout(update, 250); + } + } + + function update() { + pending = false; + var marks = document.getElementById("nav").getElementsByTagName("a"), found; + for (var i = 0; i < marks.length; ++i) { + var mark = marks[i], m; + if (mark.getAttribute("data-default")) { + if (found == null) found = i; + } else if (m = mark.href.match(/#(.*)/)) { + var ref = document.getElementById(m[1]); + if (ref && ref.getBoundingClientRect().top < 50) + found = i; + } + } + if (found != null && found != prevVal) { + prevVal = found; + var lis = document.getElementById("nav").getElementsByTagName("li"); + for (var i = 0; i < lis.length; ++i) lis[i].className = ""; + for (var i = 0; i < marks.length; ++i) { + if (found == i) { + marks[i].className = "active"; + for (var n = marks[i]; n; n = n.parentNode) + if (n.nodeName == "LI") n.className = "active"; + } else { + marks[i].className = ""; + } + } + } + } + + window.addEventListener("scroll", updateSoon); + window.addEventListener("load", updateSoon); +})(); diff --git a/doc/baboon.png b/doc/baboon.png deleted file mode 100644 index 55d97f70b817ff2b78ebb409bf34a5147fe40d07..0000000000000000000000000000000000000000 GIT binary patch literal 0 HcmV?d00001 literal 23299 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

    #6335mO-CwW+nmUD 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 diff --git a/doc/baboon_vector.svg b/doc/baboon_vector.svg deleted file mode 100644 index dc1667af91..0000000000 --- a/doc/baboon_vector.svg +++ /dev/null @@ -1,153 +0,0 @@ - - - -image/svg+xml \ No newline at end of file diff --git a/doc/compress.html b/doc/compress.html index e2d79f581d..91a76b254e 100644 --- a/doc/compress.html +++ b/doc/compress.html @@ -1,23 +1,26 @@ - - - - CodeMirror: Compression Helper - - - - -

    { } CodeMirror

    +CodeMirror: Compression Helper + + -
    - -
    -/* Script compression
    -   helper */
    -
    + + diff --git a/doc/docs.css b/doc/docs.css index 170cd41244..9cd7319f38 100644 --- a/doc/docs.css +++ b/doc/docs.css @@ -1,167 +1,213 @@ +@font-face { + font-family: 'Source Sans Pro'; + font-style: normal; + font-weight: 400; + src: local('Source Sans Pro'), local('SourceSansPro-Regular'), url(http://themes.googleusercontent.com/static/fonts/sourcesanspro/v5/ODelI1aHBYDBqgeIAH2zlBM0YzuT7MdOe03otPbuUS0.woff) format('woff'); +} + +body, html { margin: 0; padding: 0; height: 100%; } +section, article { display: block; padding: 0; } + body { - font-family: Droid Sans, Arial, sans-serif; - line-height: 1.5; - max-width: 64.3em; - margin: 3em auto; - padding: 0 1em; + background: #f8f8f8; } -h1 { - letter-spacing: -3px; - font-size: 3.23em; - font-weight: bold; - margin: 0; +article { + max-width: 700px; + margin: 0 auto; + border-left: 2px solid #E30808; + border-right: 1px solid #ddd; + padding: 30px 50px 100px 50px; + background: white; + z-index: 2; + position: relative; + min-height: 100%; + box-sizing: border-box; } -h2 { - font-size: 1.23em; - font-weight: bold; - margin: .5em 0; - letter-spacing: -1px; +body { + font-family: 'Source Sans Pro', Helvetica, Arial, sans-serif; + line-height: 1.5; } -h3 { - font-size: 1.1em; - font-weight: bold; - margin: .4em 0; +p { margin-top: 0; } + +h2, h3 { + font-weight: normal; + text-decoration: underline; + margin-bottom: .7em; } +h2 { font-size: 120%; } +h3 { font-size: 110%; } +article > h2:first-child, section:first-child > h2 { margin-top: 0; } -pre { - background-color: #eee; - -moz-border-radius: 6px; - -webkit-border-radius: 6px; - border-radius: 6px; - padding: 1em; +a, a:visited, a:link, .quasilink { + color: #A21313; + text-decoration: none; } -pre.code { - margin: 0 1em; +.quasilink { + cursor: pointer; } -.grey { - background-color: #eee; - border-radius: 6px; - margin-bottom: 1.65em; - margin-top: 0.825em; - padding: 0.825em 1.65em; - position: relative; +#nav { + position: fixed; + top: 30px; + right: 50%; + padding-right: 350px; + text-align: right; + z-index: 1; } -img.logo { - position: absolute; - right: -1em; - bottom: 4px; - max-width: 23.6875em; /* Scale image down with text to prevent clipping */ +#nav ul { + display: block; + margin: 0; padding: 0; + margin-bottom: 32px; } -.grey > pre { - background:none; - border-radius:0; - padding:0; - margin:0; - font-size:2.2em; - line-height:1.2em; +#nav li { + display: block; + margin-bottom: 4px; } -a:link, a:visited, .quasilink { - color: #df0019; - cursor: pointer; - text-decoration: none; +#nav li ul { + font-size: 80%; + margin-bottom: 0; + display: none; +} + +#nav li.active ul { + display: block; } -a:hover, .quasilink:hover { - color: #800004; +#nav li li a { + padding-right: 20px; } -h1 a:link, h1 a:visited, h1 a:hover { +#nav ul a { color: black; + padding: 0 7px 1px 11px; } -ul { - margin: 0; - padding-left: 1.2em; +#nav ul a.active, #nav ul a:hover { + border-bottom: 1px solid #E30808; + color: #E30808; } -a.download { - color: white; - background-color: #df0019; - width: 100%; - display: block; - text-align: center; - font-size: 1.23em; - font-weight: bold; - text-decoration: none; - -moz-border-radius: 6px; - -webkit-border-radius: 6px; - border-radius: 6px; - padding: .5em 0; - margin-bottom: 1em; +#logo { + margin-right: 7px; + margin-bottom: 25px; } -a.download:hover { - background-color: #bb0010; +section { + border-top: 1px solid #E30808; + margin: 1.5em 0; } -.rel { - margin-bottom: 0; +section.first { + border: none; + margin-top: 0; } -.rel-note { - color: #777; - font-size: .9em; - margin-top: .1em; +#demo { + position: relative; +} + +#demolist { + position: absolute; + right: 5px; + top: 5px; + z-index: 25; +} + +#bankinfo { + text-align: left; + display: none; + padding: 0 .5em; + position: absolute; + border: 2px solid #aaa; + border-radius: 5px; + background: #eee; + top: 10px; + left: 30px; +} + +#bankinfo_close { + position: absolute; + top: 0; right: 6px; + font-weight: bold; + cursor: pointer; } -.logo-braces { - color: #df0019; +.bigbutton { + cursor: pointer; + text-align: center; + padding: 0 1em; + display: inline-block; + color: white; position: relative; - top: -4px; + line-height: 1.9; + color: white !important; + background: #A21313; } -.blk { - float: left; +.bigbutton.right { + border-bottom-left-radius: 100px; + border-top-left-radius: 100px; } -.left { - margin-right: 20.68em; - max-width: 37em; - padding-right: 6.53em; - padding-bottom: 1em; +.bigbutton.left { + border-bottom-right-radius: 100px; + border-top-right-radius: 100px; } -.left1 { - width: 15.24em; - padding-right: 6.45em; +.bigbutton:hover { + background: #E30808; } -.left2 { - max-width: 15.24em; +th { + text-decoration: underline; + font-weight: normal; + text-align: left; } -.right { - width: 20.68em; - margin-left: -20.68em; +#features ul { + list-style: none; + margin: 0 0 1em; + padding: 0 0 0 1.2em; } -.leftbig { - width: 42.44em; - padding-right: 6.53em; +#features li:before { + content: "-"; + width: 1em; + display: inline-block; + padding: 0; + margin: 0; + margin-left: -1em; } -.rightsmall { - width: 15.24em; +.rel { + margin-bottom: 0; +} +.rel-note { + margin-top: 0; + color: #555; } -.clear:after { - visibility: hidden; - display: block; - font-size: 0; - content: " "; - clear: both; - height: 0; -} -.clear { display: inline-block; } -/* start commented backslash hack \*/ -* html .clear { height: 1%; } -.clear { display: block; } -/* close commented backslash hack */ +pre { + padding-left: 15px; + border-left: 2px solid #ddd; +} + +code { + padding: 0 2px; +} + +strong { + text-decoration: underline; + font-weight: normal; +} + +.field { + border: 1px solid #A21313; +} diff --git a/doc/internals.html b/doc/internals.html index 4336ba4d1c..3f1b7de869 100644 --- a/doc/internals.html +++ b/doc/internals.html @@ -1,25 +1,35 @@ - - - - CodeMirror: Internals - - - - - - -

    { } CodeMirror

    - -
    - -
    -/* (Re-) Implementing A Syntax-
    -   Highlighting Editor in JavaScript */
    -
    + +CodeMirror: Internals + + + + + + -
    +
    + +

    (Re-) Implementing A Syntax-Highlighting Editor in JavaScript

    Topic: JavaScript, code editor implementation
    @@ -103,7 +113,8 @@

    { } CodeMi with new scary hacks in order to keep up. This was starting to lose its appeal.

    -

    General Approach

    +
    +

    General Approach

    What CodeMirror 2 does is try to sidestep most of the hairy hacks that came up in version 1. I owe a lot to the @@ -136,8 +147,9 @@

    General Approach

    do the rest only when needed. (Fortunately, the onscroll event works almost the same on all browsers, and lends itself well to displaying things only as they are scrolled into view.)

    - -

    Input

    +
    +
    +

    Input

    ACE uses its hidden textarea only as a text input shim, and does all cursor movement and things like text deletion itself by directly @@ -165,8 +177,9 @@

    Input

    Of course, since only a small part of the document sits in the textarea, keys like page up and ctrl-end won't do the right thing. CodeMirror is catching those events and handling them itself.

    - -

    Selection

    +
    +
    +

    Selection

    Getting and setting the selection range of a textarea in modern browsers is trivial—you just use the selectionStart @@ -213,8 +226,9 @@

    Selection

    This, of course, doesn't work if the first time the key is used was for extending an inverted selection, but it works most of the time.

    - -

    Intelligent Updating

    +
    +
    +

    Intelligent Updating

    One thing that always comes up when you have a complicated internal state that's reflected in some user-visible external representation @@ -274,8 +288,9 @@

    Intelligent Updating

    uses this to reset individual lines, the refresh updater builds an HTML chunk for the whole visible document at once, and then uses a single innerHTML update to do the refresh.

    - -

    Parsers can be Simple

    +
    +
    +

    Parsers can be Simple

    When I wrote CodeMirror 1, I thought interruptable @@ -315,8 +330,9 @@

    Parsers can be Simple

    manages some 1500 lines during that time on Chrome. All it has to do is munge strings, so there is no real reason for it to be slow anymore.

    - -

    What Gives?

    +
    +
    +

    What Gives?

    Given all this, what can you expect from CodeMirror 2?

    @@ -368,8 +384,9 @@

    What Gives?

    longer be current. I've left the text intact, but added markers at the passages that are now inaccurate. The new situation is described below.

    - -

    Content Representation

    +
    +
    +

    Content Representation

    The original implementation of CodeMirror 2 represented the document as a flat array of line objects. This worked well—splicing @@ -419,8 +436,9 @@

    Content Representation

    patterns that may result in a seriously unbalanced tree, but even such an unbalanced tree will perform well, unless you spend a day making strangely repeating edits to a really big document.

    - -

    Keymaps

    +
    +
    +

    Keymaps

    Above, I claimed that directly catching key events for things like cursor movement is impractical because it @@ -482,24 +500,4 @@

    Keymaps

    is updated during composition. So we poll, whenever the editor is focused, to provide immediate updates of the display.

    -

    - -
     
    - - + diff --git a/doc/logo.png b/doc/logo.png new file mode 100644 index 0000000000000000000000000000000000000000..2334f5e0999358985862f477ce812805dd66d08d GIT binary patch literal 12003 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 + + + + + + + + + + + + + image/svg+xml + + + + + + + if (unit == "char") moveOnce(); else if (unit == "column") moveOnce(true); else if (unit == "word" || unit == "group") { var sawType = null, group = unit == "group"; for (var first = true;; first = false) { if (dir < 0 && !moveOnce(!first)) break; var cur = lineObj.text.charAt(ch) || "\n"; var type = isWordChar(cur) ? "w" : !group ? null : /\s/.test(cur) ? null : "p"; // punctuation if (sawType && sawType + + Code Mirror + + diff --git a/doc/manual.html b/doc/manual.html index 532285163c..37230f5426 100644 --- a/doc/manual.html +++ b/doc/manual.html @@ -1,38 +1,63 @@ - - - - CodeMirror: User Manual - - - - - - - - - - - - - - - -

    { } CodeMirror

    - -
    - -
    -/* User manual and
    -   reference guide */
    -
    + +CodeMirror: User Manual + + + + + + + + + + + + + + + -
    +
    -

    Overview

    +
    +

    User manual and reference guide

    CodeMirror is a code-editor component that can be embedded in Web pages. The core library provides only the editor @@ -50,8 +75,10 @@

    Overview

    of modes (see the mode/ directory), and it isn't hard to write new ones for other languages.

    +
    -

    Basic Usage

    +
    +

    Basic Usage

    The easiest way to use CodeMirror is to simply load the script and style sheet found under lib/ in the distribution, @@ -107,7 +134,9 @@

    Basic Usage

    of a form) is submitted. See the API reference for a full description of this method.

    -

    Configuration

    +
    +
    +

    Configuration

    Both the CodeMirror function and its fromTextArea method take as second @@ -372,8 +401,10 @@

    Configuration

    This will have bad effects on performance of big documents. +
    -

    Events

    +
    +

    Events

    Various CodeMirror-related objects emit events, which allow client code to react to various situations. Handlers for such @@ -589,8 +620,10 @@

    Events

    or the line the widget is on require the widget to be redrawn. +
    -

    Keymaps

    +
    +

    Keymaps

    Keymaps are ways to associate keys with functionality. A keymap is an object mapping strings that identify the keys to functions @@ -661,8 +694,10 @@

    Keymaps

    to true, the default effect of inserting a character will be suppressed when the keymap is active as the top-level map.

    +
    -

    Customized Styling

    +
    +

    Customized Styling

    Up to a certain extent, CodeMirror's look can be changed by modifying style sheet files. The style sheets supplied by modes @@ -740,8 +775,10 @@

    Customized Styling

    Themes are also simply CSS files, which define colors for various syntactic elements. See the files in the theme directory.

    +
    -

    Programming API

    +
    +

    Programming API

    A lot of CodeMirror features are only available through its API. Thus, you need to write code (or @@ -1620,8 +1657,10 @@

    Static properties

    returned position will be the end of the changed range, after the change is applied. +
    -

    Addons

    +
    +

    Addons

    The addon directory in the distribution contains a number of reusable components that implement extra editor @@ -2088,8 +2127,10 @@

    Addons

    will highlight changes between the editable document and the original(s) (demo). +
    -

    Writing CodeMirror Modes

    +
    +

    Writing CodeMirror Modes

    Modes typically consist of a single JavaScript file. This file defines, in the simplest case, a lexer (tokenizer) for your @@ -2318,43 +2359,8 @@

    Writing CodeMirror Modes

    specifies the properties that should be added. This is mostly useful to add utilities that can later be looked up through getMode.

    +
    -
    - -
     
    + - - - diff --git a/doc/modes.html b/doc/modes.html deleted file mode 100644 index 82738f19e6..0000000000 --- a/doc/modes.html +++ /dev/null @@ -1,100 +0,0 @@ - - - - - CodeMirror: Mode list - - - - - -

    { } CodeMirror

    - -
    - -
    -/* Full list of
    -   modes */
    -
    -
    - -

    Every mode in the distribution. The list on the front-page leaves -out some of the more obscure ones.

    - - - - - diff --git a/doc/realworld.html b/doc/realworld.html index 36c95b3ea0..9b6d7386fb 100644 --- a/doc/realworld.html +++ b/doc/realworld.html @@ -1,23 +1,26 @@ - - - - CodeMirror: Real-world uses - - - - -

    { } CodeMirror

    +CodeMirror: Real-world Uses + + -
    - -
    -/* Real world uses,
    -   full list */
    -
    + +
    + +

    CodeMirror real-world uses

    +

    Contact me if you'd like your project to be added to this list.

    @@ -117,5 +120,5 @@

    { } CodeMi
  • xsd2codemirror (convert XSD to CM XML completion info)
  • - - +

    + diff --git a/doc/oldrelease.html b/doc/releases.html similarity index 70% rename from doc/oldrelease.html rename to doc/releases.html index c72dc8f61a..93650b8fe0 100644 --- a/doc/oldrelease.html +++ b/doc/releases.html @@ -1,24 +1,196 @@ - - - - CodeMirror - - - - - - -

    { } CodeMirror

    - -
    - -
    -/* Old release
    -   history */
    -
    + +CodeMirror: Release History + + + + + +
    + +

    Release notes and version history

    + +
    + +

    Version 3.x

    + +

    29-07-2013: Version 3.15:

    + + + +

    20-06-2013: Version 3.14:

    + + + +

    20-05-2013: Version 3.13:

    + + + +

    19-04-2013: Version 3.12:

    + + + +

    20-03-2013: Version 3.11:

    + + + +

    21-02-2013: Version 3.1:

    + + + + +

    25-01-2013: Version 3.02:

    + +

    Single-bugfix release. Fixes a problem that + prevents CodeMirror instances from being garbage-collected after + they become unused.

    + +

    21-01-2013: Version 3.01:

    + + + +

    10-12-2012: Version 3.0:

    + +

    New major version. Only + partially backwards-compatible. See + the upgrading guide for more + information. Changes since release candidate 2:

    + +
      +
    • Rewritten VIM mode.
    • +
    • Fix a few minor scrolling and sizing issues.
    • +
    • Work around Safari segfault when dragging.
    • +
    • Full list of patches.
    • +
    +

    20-11-2012: Version 3.0, release candidate 2:

    -

    20-11-2012: Version 2.36:

    - - -

    20-11-2012: Version 3.0, release candidate 1:

    -

    22-10-2012: Version 2.35:

    - -
      -
    • New (sub) mode: TypeScript.
    • -
    • Don't overwrite (insert key) when pasting.
    • -
    • Fix several bugs in markText/undo interaction.
    • -
    • Better indentation of JavaScript code without semicolons.
    • -
    • Add defineInitHook function.
    • -
    • Full list of patches.
    • -
    -

    22-10-2012: Version 3.0, beta 2:

    +

    19-09-2012: Version 3.0, beta 1:

    + +
      +
    • Bi-directional text support.
    • +
    • More powerful gutter model.
    • +
    • Support for arbitrary text/widget height.
    • +
    • In-line widgets.
    • +
    • Generalized event handling.
    • +
    + +
    + +
    + +

    Version 2.x

    + +

    21-01-2013: Version 2.38:

    + +

    Integrate some bugfixes, enhancements to the vim keymap, and new + modes + (D, Sass, APL) + from the v3 branch.

    + +

    20-12-2012: Version 2.37:

    + +
      +
    • New mode: SQL (will replace plsql and mysql modes).
    • +
    • Further work on the new VIM mode.
    • +
    • Fix Cmd/Ctrl keys on recent Operas on OS X.
    • +
    • Full list of patches.
    • +
    + +

    20-11-2012: Version 2.36:

    + + + +

    22-10-2012: Version 2.35:

    + +
      +
    • New (sub) mode: TypeScript.
    • +
    • Don't overwrite (insert key) when pasting.
    • +
    • Fix several bugs in markText/undo interaction.
    • +
    • Better indentation of JavaScript code without semicolons.
    • +
    • Add defineInitHook function.
    • +
    • Full list of patches.
    • +
    +

    19-09-2012: Version 2.34:

    -

    19-09-2012: Version 3.0, beta 1:

    - -
      -
    • Bi-directional text support.
    • -
    • More powerful gutter model.
    • -
    • Support for arbitrary text/widget height.
    • -
    • In-line widgets.
    • -
    • Generalized event handling.
    • -
    -

    23-08-2012: Version 2.33:

    +
    + +
    + +

    Version 0.x

    + +

    28-03-2011: Version 1.0:

    +
      +
    • Fix error when debug history overflows.
    • +
    • Refine handling of C# verbatim strings.
    • +
    • Fix some issues with JavaScript indentation.
    • +

    17-12-2010: Version 0.92:

      @@ -527,20 +726,17 @@

      { } CodeMi parser. And, as usual, add workarounds for various newly discovered browser incompatibilities.

      -

      31-08-2009: Version -0.63:

      -

      Overhaul of paste-handling (less fragile), fixes for several -serious IE8 issues (cursor jumping, end-of-document bugs) and a number -of small problems.

      - -

      30-05-2009: Version -0.62:

      -

      Introduces Python -and Lua parsers. Add -setParser (on-the-fly mode changing) and -clearHistory methods. Make parsing passes time-based -instead of lines-based (see the passTime option).

      - - +

      31-08-2009: Version 0.63:

      +

      Overhaul of paste-handling (less fragile), fixes for several + serious IE8 issues (cursor jumping, end-of-document bugs) and a number + of small problems.

      + +

      30-05-2009: Version 0.62:

      +

      Introduces Python + and Lua parsers. Add + setParser (on-the-fly mode changing) and + clearHistory methods. Make parsing passes time-based + instead of lines-based (see the passTime option).

      + +

    +
    diff --git a/doc/reporting.html b/doc/reporting.html index a616512530..47e37a5541 100644 --- a/doc/reporting.html +++ b/doc/reporting.html @@ -1,24 +1,26 @@ - - - - CodeMirror: Reporting Bugs - - - - - - -

    { } CodeMirror

    - -
    - -
    -/* Reporting bugs
    -   effectively */
    -
    + +CodeMirror: Reporting Bugs + + + + +
    + +

    Reporting bugs effectively

    +

    So you found a problem in CodeMirror. By all means, report it! Bug @@ -56,5 +58,4 @@

    { } CodeMi

    - - +
    diff --git a/doc/upgrade_v2.2.html b/doc/upgrade_v2.2.html index 7e4d840043..a2dddef760 100644 --- a/doc/upgrade_v2.2.html +++ b/doc/upgrade_v2.2.html @@ -1,36 +1,37 @@ - - - - CodeMirror: Upgrading to v2.2 - - - - - -

    { } CodeMirror

    - -
    - -
    -/* Upgrading to
    -   v2.2 */
    -
    + +CodeMirror: Version 2.2 upgrade guide + + + + -
    +
    + +

    Upgrading to v2.2

    There are a few things in the 2.2 release that require some care when upgrading.

    -

    No more default.css

    +

    No more default.css

    The default theme is now included in codemirror.css, so you do not have to included it separately anymore. (It was tiny, so even if you're not using it, the extra data overhead is negligible.) -

    Different key customization

    +

    Different key customization

    CodeMirror has moved to a system where keymaps are used to @@ -81,7 +82,7 @@

    Different key customization

    behaviors. Or you can write your own handler function to do something different altogether.

    -

    Tabs

    +

    Tabs

    Handling of tabs changed completely. The display width of tabs can now be set with the tabSize option, and tabs can @@ -92,7 +93,4 @@

    Tabs

    hard-wired into browsers. If you are relying on 8-space tabs, make sure you explicitly set tabSize: 8 in your options.

    -
    - - - + diff --git a/doc/upgrade_v3.html b/doc/upgrade_v3.html index 7e8a6b61ae..19757924c1 100644 --- a/doc/upgrade_v3.html +++ b/doc/upgrade_v3.html @@ -1,32 +1,44 @@ - - - - CodeMirror: Upgrading to v3 - - - - - - - - - - - - - -

    { } CodeMirror

    - -
    - -
    -/* Upgrading to
    -   version 3 */
    -
    + +CodeMirror: Version 3 upgrade guide + + + + + + + + + + + + + -
    +
    + +

    Upgrading to version 3

    Version 3 does not depart too much from 2.x API, and sites that use CodeMirror in a very simple way might be able to upgrade without @@ -37,7 +49,8 @@

    { } CodeMi Explorer 7. The editor will mostly work on that browser, but it'll be significantly glitchy.

    -

    DOM structure

    +
    +

    DOM structure

    This one is the most likely to cause problems. The internal structure of the editor has changed quite a lot, mostly to implement a @@ -53,8 +66,9 @@

    DOM structure

    See the styling section of the manual for more information.

    - -

    Gutter model

    +
    +
    +

    Gutter model

    In CodeMirror 2.x, there was a single gutter, and line markers created with setMarker would have to somehow coexist with @@ -87,8 +101,9 @@

    Gutter model

    cm.setGutterMarker(0, "note-gutter", document.createTextNode("hi")); </script> - -

    Event handling

    +
    +
    +

    Event handling

    Most of the onXYZ options have been removed. The same effect is now obtained by calling @@ -107,8 +122,9 @@

    Event handling

    console.log("something changed! (" + change.origin + ")"); }); - -

    markText method arguments

    +
    +
    +

    markText method arguments

    The markText method (which has gained some interesting new features, such as creating @@ -124,8 +140,9 @@

    markText method arguments

    atomic: true }); - -

    Line folding

    +
    +
    +

    Line folding

    The interface for hiding lines has been removed. markText can @@ -146,8 +163,9 @@

    Line folding

    console.log("boom"); }); - -

    Line CSS classes

    +
    +
    +

    Line CSS classes

    The setLineClass method has been replaced by addLineClass @@ -160,8 +178,9 @@

    Line CSS classes

    cm.removeLineClass(marked, "background", "highlighted-line"); }); - -

    Position properties

    +
    +
    +

    Position properties

    All methods that take or return objects that represent screen positions now use {left, top, bottom, right} properties @@ -171,14 +190,16 @@

    Position properties

    Affected methods are cursorCoords, charCoords, coordsChar, and getScrollInfo.

    - -

    Bracket matching no longer in core

    +
    +
    +

    Bracket matching no longer in core

    The matchBrackets option is no longer defined in the core editor. Load addon/edit/matchbrackets.js to enable it.

    - -

    Mode management

    +
    +
    +

    Mode management

    The CodeMirror.listModes and CodeMirror.listMIMEs functions, used for listing @@ -186,8 +207,9 @@

    Mode management

    inspect CodeMirror.modes (mapping mode names to mode constructors) and CodeMirror.mimeModes (mapping MIME strings to mode specs).

    - -

    New features

    +
    +
    +

    New features

    Some more reasons to upgrade to version 3.

    @@ -202,26 +224,7 @@

    New features

  • Defining custom options with CodeMirror.defineOption.
  • - -

    + + - - diff --git a/index.html b/index.html index 2f2ba05096..3bd665e871 100644 --- a/index.html +++ b/index.html @@ -1,480 +1,186 @@ - - - - CodeMirror - - - - - -

    { } CodeMirror

    +CodeMirror + -
    - -
    -/* In-browser code editing
    -   made bearable */
    -
    -
    - -
    - -

    CodeMirror is a JavaScript component that - provides a code editor in the browser. When a mode is available for - the language you are coding in, it will color your code, and - optionally help with indentation.

    - -

    A rich programming API and a CSS - theming system are available for customizing CodeMirror to fit your - application, and extending it with new functionality.

    - - - -

    Getting the code

    - -

    All of CodeMirror is released under a MIT-style license. To get it, you can download - the latest - release or the current development - snapshot as zip files. To create a custom minified script file, - you can use the compression API.

    - -

    We use git for version control. - The main repository can be fetched in this way:

    - -
    git clone http://marijnhaverbeke.nl/git/codemirror
    - -

    CodeMirror can also be found on GitHub at marijnh/CodeMirror. - If you plan to hack on the code and contribute patches, the best way - to do it is to create a GitHub fork, and send pull requests.

    - -

    Documentation

    - -

    The manual is your first stop for - learning how to use this library. It starts with a quick explanation - of how to use the editor, and then describes the API in detail.

    + + + + + + + + -

    For those who want to learn more about the code, there is - a series of - posts on CodeMirror on my blog, and the - old overview of the editor - internals. - The source code - itself is, for the most part, also very readable.

    + -

    Support and bug reports

    + -

    Community discussion, questions, and informal bug reporting is - done on - the CodeMirror - Google group. There is a separate - group, CodeMirror-announce, - which is lower-volume, and is only used for major announcements—new - versions and such. These will be cross-posted to both groups, so you - don't need to subscribe to both.

    - -

    Though bug reports through e-mail are responded to, the preferred - way to report bugs is to use - the GitHub - issue tracker. Before reporting a - bug, read these pointers. Also, - the issue tracker is for bugs, not requests for help.

    - -

    When none of these seem fitting, you can - simply e-mail the maintainer - directly.

    - -

    Supported browsers

    - -

    The following desktop browsers are able to run CodeMirror:

    + -
    - - Download the latest release - -

    Support CodeMirror

    - +
    + +
    +

    CodeMirror is a versatile text editor + implemented in JavaScript for the browser. It is specialized for + editing code, and comes with a number of language modes and addons + that implement more advanced editing functionaly.

    + +

    A rich programming API and a + CSS theming system are + available for customizing CodeMirror to fit your application, and + extending it with new functionality.

    +
    + +
    +

    This is CodeMirror

    + + + +
    + DOWNLOAD LATEST RELEASE +
    version 3.15 (Release notes)
    + +
    + DONATE WITH PAYPAL +
    + or Bank, + Gittip, + Flattr
    +
    + × + Bank: Rabobank
    + Country: Netherlands
    + SWIFT: RABONL2U
    + Account: 147850770
    + Name: Marijn Haverbeke
    + IBAN: NL26 RABO 0147 8507 70 +
    +
    + + +
    +
    +
    + Purchase commercial support +
    +
    +
    +
    + +
    +

    Features

    +
    + +
    +

    Community

    + +

    CodeMirror is an open-source project shared under + an MIT license. It is the editor used in + Light + Table, Adobe + Brackets, Google Apps + Script, Bitbucket, + and many other projects.

    + +

    Development and bug tracking happens + on github + (alternate git + repository). + Please read these + pointers before submitting a bug. Use pull requests to submit + patches. All contributions must be released under the same MIT + license that CodeMirror uses.

    + +

    Discussion around the project is done on + a mailing list. + There is also + the codemirror-announce + list, which is only used for major announcements (such as new + versions). If needed, you can + contact the maintainer + directly.

    - - -

    Reading material

    - - - -

    Releases

    - -

    29-07-2013: Version 3.15:

    - - - -

    20-06-2013: Version 3.14:

    - - - -

    20-05-2013: Version 3.13:

    - - - -

    19-04-2013: Version 3.12:

    - - - -

    20-03-2013: Version 3.11:

    - - - -

    21-02-2013: Version 3.1:

    - - - - -

    25-01-2013: Version 3.02:

    - -

    Single-bugfix release. Fixes a problem that - prevents CodeMirror instances from being garbage-collected after - they become unused.

    - -

    21-01-2013: Version 3.01:

    - - - -

    21-01-2013: Version 2.38:

    - -

    Integrate some bugfixes, enhancements to the vim keymap, and new - modes - (D, Sass, APL) - from the v3 branch.

    - -

    20-12-2012: Version 2.37:

    - -
      -
    • New mode: SQL (will replace plsql and mysql modes).
    • -
    • Further work on the new VIM mode.
    • -
    • Fix Cmd/Ctrl keys on recent Operas on OS X.
    • -
    • Full list of patches.
    • -
    - -

    10-12-2012: Version 3.0:

    - -

    New major version. Only - partially backwards-compatible. See - the upgrading guide for more - information. Changes since release candidate 2:

    - -
      -
    • Rewritten VIM mode.
    • -
    • Fix a few minor scrolling and sizing issues.
    • -
    • Work around Safari segfault when dragging.
    • -
    • Full list of patches.
    • -
    - -

    Older releases...

    - -
    - -
     
    - -
    - - -
    - - - +

    A list of CodeMirror-related software that is not part of the + main distribution is maintained + on our + wiki. Feel free to add your project.

    + + +
    +

    Browser support

    +

    The desktop versions of the following browsers, + in standards mode (HTML5 <!doctype html> + recommended) are supported:

    + + + + + + +
    Firefoxversion 3 and up
    Chromeany version
    Safariversion 5.2 and up
    Internet Explorerversion 8 and up
    Operaversion 9 and up
    +

    Modern mobile browsers tend to partly work. Bug reports and + patches for mobile support, but the maintainer does not have the + time budget to actually work on it himself.

    +
    + + diff --git a/mode/apl/index.html b/mode/apl/index.html index 119ff17f19..f8282ac42f 100644 --- a/mode/apl/index.html +++ b/mode/apl/index.html @@ -1,20 +1,32 @@ - - - - CodeMirror: APL mode - - - - - - - - -

    CodeMirror: APL mode

    + +
    +

    APL mode

    
       

    MIME type defined: text/x-vb.

    - + diff --git a/mode/vbscript/index.html b/mode/vbscript/index.html index 9ae46676b2..9b506b7985 100644 --- a/mode/vbscript/index.html +++ b/mode/vbscript/index.html @@ -1,16 +1,30 @@ - - - - CodeMirror: VBScript mode - - - - - - - -

    CodeMirror: VBScript mode

    + +CodeMirror: VBScript mode + + + + + + + + + +
    +

    VBScript mode

    +
    HTML:
    - @@ -1817,8 +1817,9 @@

    Addons

    finds blocks in brace languages (JavaScript, C, Java, etc), CodeMirror.fold.indent, for languages where indentation determines block structure (Python, Haskell), - and CodeMirror.fold.xml, for XML-style - languages. + and CodeMirror.fold.xml, for XML-style languages, + and CodeMirror.fold.comment, for folding comment + blocks.
    widget: string|Element
    The widget to show for folded ranges. Can be either a string, in which case it'll become a span with diff --git a/mode/xquery/xquery.js b/mode/xquery/xquery.js index f4e3362905..04ddb8f5ae 100644 --- a/mode/xquery/xquery.js +++ b/mode/xquery/xquery.js @@ -443,10 +443,10 @@ CodeMirror.defineMode("xquery", function() { var style = state.tokenize(stream, state); return style; }, - + blockCommentStart: "(:", blockCommentEnd: ":)" - + }; }); From cc56aa020df77db22c20a68132b0ec6e7508b6f6 Mon Sep 17 00:00:00 2001 From: Marijn Haverbeke Date: Mon, 19 Aug 2013 15:00:02 +0200 Subject: [PATCH 1411/5780] [manual] Fix accidental change --- doc/manual.html | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/doc/manual.html b/doc/manual.html index bb4680d30c..7a85ef7f70 100644 --- a/doc/manual.html +++ b/doc/manual.html @@ -14,7 +14,7 @@ From c3f4f321bf8564cb60fa8dad442985fb98a318a2 Mon Sep 17 00:00:00 2001 From: "SCLINIC\\jdecker" Date: Tue, 13 Aug 2013 09:58:55 -0500 Subject: [PATCH 1412/5780] Added ability to disable cursor blinking. When the cursor blink rate is zero (or negative) the cursor should not blink. Previously, it was blinking as fast as possible when the rate was zero. --- lib/codemirror.js | 7 ++++--- 1 file changed, 4 insertions(+), 3 deletions(-) diff --git a/lib/codemirror.js b/lib/codemirror.js index af20122ae9..15a3daaaef 100644 --- a/lib/codemirror.js +++ b/lib/codemirror.js @@ -877,9 +877,10 @@ window.CodeMirror = (function() { clearInterval(display.blinker); var on = true; display.cursor.style.visibility = display.otherCursor.style.visibility = ""; - display.blinker = setInterval(function() { - display.cursor.style.visibility = display.otherCursor.style.visibility = (on = !on) ? "" : "hidden"; - }, cm.options.cursorBlinkRate); + if (cm.options.cursorBlinkRate > 0) + display.blinker = setInterval(function() { + display.cursor.style.visibility = display.otherCursor.style.visibility = (on = !on) ? "" : "hidden"; + }, cm.options.cursorBlinkRate); } // HIGHLIGHT WORKER From b71833f9b264fc1c57eeb0e5b340030ed56f5f7d Mon Sep 17 00:00:00 2001 From: Marijn Haverbeke Date: Mon, 19 Aug 2013 15:11:14 +0200 Subject: [PATCH 1413/5780] [manual] Mention cursorBlinkRate=0 functionality Issue #1741 --- doc/manual.html | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/doc/manual.html b/doc/manual.html index 7a85ef7f70..3d0b70b6dd 100644 --- a/doc/manual.html +++ b/doc/manual.html @@ -344,7 +344,7 @@

    Configuration

    cursorBlinkRate: number
    Half-period in milliseconds used for cursor blinking. The default blink - rate is 530ms.
    + rate is 530ms. By setting this to zero, blinking can be disabled.
    cursorScrollMargin: number
    How much extra space to always keep above and below the From 7b883926072d71fd2d41a7ed20c63d54ee128b6b Mon Sep 17 00:00:00 2001 From: Marijn Haverbeke Date: Mon, 19 Aug 2013 15:21:55 +0200 Subject: [PATCH 1414/5780] [midnight theme] Make tag style contrast with background Closes #1744 --- theme/midnight.css | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/theme/midnight.css b/theme/midnight.css index d51c915e1c..f8016a81d8 100644 --- a/theme/midnight.css +++ b/theme/midnight.css @@ -34,7 +34,7 @@ .cm-s-midnight span.cm-def {color: #4DD;} .cm-s-midnight span.cm-error {background: #F92672; color: #F8F8F0;} .cm-s-midnight span.cm-bracket {color: #D1EDFF;} -.cm-s-midnight span.cm-tag {color: #008;} +.cm-s-midnight span.cm-tag {color: #449;} .cm-s-midnight span.cm-link {color: #AE81FF;} .cm-s-midnight .CodeMirror-activeline-background {background: #192741 !important;} From 7218f45a32d9c2641ea422e41c2e3c64eed8bb7d Mon Sep 17 00:00:00 2001 From: Marijn Haverbeke Date: Mon, 19 Aug 2013 15:27:05 +0200 Subject: [PATCH 1415/5780] [themes] Update existing activeline rules to use correct class Rather than adding a new rule. Issue #1747 --- theme/ambiance.css | 4 +--- theme/midnight.css | 3 +-- theme/solarized.css | 12 ++---------- 3 files changed, 4 insertions(+), 15 deletions(-) diff --git a/theme/ambiance.css b/theme/ambiance.css index 9c4a047da9..53e9ce78f7 100644 --- a/theme/ambiance.css +++ b/theme/ambiance.css @@ -65,12 +65,10 @@ border-left: 1px solid #7991E8; } -.cm-s-ambiance .activeline { +.cm-s-ambiance .CodeMirror-activeline-background { background: none repeat scroll 0% 0% rgba(255, 255, 255, 0.031); } -.cm-s-ambiance .CodeMirror-activeline-background {background: #3C3636 !important;} - .cm-s-ambiance.CodeMirror, .cm-s-ambiance .CodeMirror-gutters { background-image: url("data:image/png;base64,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"); diff --git a/theme/midnight.css b/theme/midnight.css index f8016a81d8..2824dfa5f7 100644 --- a/theme/midnight.css +++ b/theme/midnight.css @@ -5,7 +5,7 @@ .cm-s-midnight.CodeMirror-focused span.CodeMirror-matchhighlight { background: #314D67 !important; } /**/ -.cm-s-midnight .activeline {background: #253540 !important;} +.cm-s-midnight .CodeMirror-activeline-background {background: #253540 !important;} .cm-s-midnight.CodeMirror { background: #0F192A; @@ -37,7 +37,6 @@ .cm-s-midnight span.cm-tag {color: #449;} .cm-s-midnight span.cm-link {color: #AE81FF;} -.cm-s-midnight .CodeMirror-activeline-background {background: #192741 !important;} .cm-s-midnight .CodeMirror-matchingbracket { text-decoration: underline; color: white !important; diff --git a/theme/solarized.css b/theme/solarized.css index 85b1f0a888..f6b2a3f277 100644 --- a/theme/solarized.css +++ b/theme/solarized.css @@ -186,15 +186,10 @@ http://ethanschoonover.com/solarized/img/solarized-palette.png Active line. Negative margin compensates left padding of the text in the view-port */ -.cm-s-solarized .activeline { - margin-left: -20px; -} - -.cm-s-solarized.cm-s-dark .activeline { +.cm-s-solarized.cm-s-dark .CodeMirror-activeline-background { background: rgba(255, 255, 255, 0.05); - } -.cm-s-solarized.cm-s-light .activeline { +.cm-s-solarized.cm-s-light .CodeMirror-activeline-background { background: rgba(0, 0, 0, 0.05); } @@ -205,6 +200,3 @@ View-port and gutter both get little noise background to give it a real feel. .cm-s-solarized .CodeMirror-gutters { background-image: url("data:image/png;base64,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"); } - -.cm-s-solarized.cm-s-dark .CodeMirror-activeline-background {background: #013542 !important;} -.cm-s-solarized.cm-s-light .CodeMirror-activeline-background {background: #EBE0C3 !important;} \ No newline at end of file From ba9033cf06bdf8ba1c05673426e22c3a07396ec8 Mon Sep 17 00:00:00 2001 From: Maksym Taran Date: Tue, 13 Aug 2013 23:52:16 -0700 Subject: [PATCH 1416/5780] [clike mode] Add syntax examples for C++ & Java. --- mode/clike/index.html | 89 +++++++++++++++++++++++++++++++++++++++++-- 1 file changed, 85 insertions(+), 4 deletions(-) diff --git a/mode/clike/index.html b/mode/clike/index.html index 58ba24c380..45add4910b 100644 --- a/mode/clike/index.html +++ b/mode/clike/index.html @@ -25,7 +25,8 @@

    C-like mode

    -
    +
    + +

    C++ example

    + +
    + +

    Java example

    + +

    Simple mode that tries to handle C-like languages as well as it @@ -111,4 +192,4 @@

    C-like mode

    (C code), text/x-c++src (C++ code), text/x-java (Java code), text/x-csharp (C#).

    - + From 993dceea2f84c6f43ca0da7bcd7123ea873ed5ab Mon Sep 17 00:00:00 2001 From: Daniel Huigens Date: Tue, 13 Aug 2013 19:09:16 +0200 Subject: [PATCH 1417/5780] [tests] Support the other format of error.stack This makes error.stack parsing work in Firefox and Opera 12-, and maybe in Safari too (didn't test). --- test/driver.js | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/test/driver.js b/test/driver.js index 8c32ec7faa..5befa77278 100644 --- a/test/driver.js +++ b/test/driver.js @@ -84,7 +84,7 @@ function runTests(callback) { if (expFail) callback("expected", test.name); else if (e instanceof Failure) callback("fail", test.name, e.message); else { - var pos = /\bat .*?([^\/:]+):(\d+):/.exec(e.stack); + var pos = /(?:\bat |@).*?([^\/:]+):(\d+)/.exec(e.stack); callback("error", test.name, e.toString() + (pos ? " (" + pos[1] + ":" + pos[2] + ")" : "")); } } From f790cbeeafd50c5c5956b08d98c116f10b38b3b8 Mon Sep 17 00:00:00 2001 From: Chandra Sekhar Pydi Date: Wed, 14 Aug 2013 15:05:00 -0700 Subject: [PATCH 1418/5780] [continuecomment addon] Continue blocks only if mode has blockCommentContinue Changes in https://github.com/marijnh/CodeMirror/pull/1731 are missing. --- addon/comment/continuecomment.js | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/addon/comment/continuecomment.js b/addon/comment/continuecomment.js index 308026229f..9dba156189 100644 --- a/addon/comment/continuecomment.js +++ b/addon/comment/continuecomment.js @@ -10,7 +10,7 @@ var mode = CodeMirror.innerMode(cm.getMode(), token.state).mode; var space; - if (token.type == "comment" && mode.blockCommentStart) { + if (token.type == "comment" && mode.blockCommentStart && mode.blockCommentContinue) { var end = token.string.indexOf(mode.blockCommentEnd); var full = cm.getRange(CodeMirror.Pos(pos.line, 0), CodeMirror.Pos(pos.line, token.end)), found; if (end != -1 && end == token.string.length - mode.blockCommentEnd.length) { From f2c3bf51e6be8c0388b300ab1d3915775c4fd33f Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Tom=20Erik=20St=C3=B8wer?= Date: Wed, 14 Aug 2013 21:26:55 +0200 Subject: [PATCH 1419/5780] [css mode] Added keep-all to valueKeywords. Valid value of e.g. word-break --- mode/css/css.js | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/mode/css/css.js b/mode/css/css.js index 0889c12e52..8abfb315c2 100644 --- a/mode/css/css.js +++ b/mode/css/css.js @@ -488,7 +488,7 @@ CodeMirror.defineMode("css-base", function(config, parserConfig) { "inactiveborder", "inactivecaption", "inactivecaptiontext", "infinite", "infobackground", "infotext", "inherit", "initial", "inline", "inline-axis", "inline-block", "inline-table", "inset", "inside", "intrinsic", "invert", - "italic", "justify", "kannada", "katakana", "katakana-iroha", "khmer", + "italic", "justify", "kannada", "katakana", "katakana-iroha", "keep-all", "khmer", "landscape", "lao", "large", "larger", "left", "level", "lighter", "line-through", "linear", "lines", "list-item", "listbox", "listitem", "local", "logical", "loud", "lower", "lower-alpha", "lower-armenian", From aef141a143b477a5d1bbfa61054734771676db23 Mon Sep 17 00:00:00 2001 From: Marijn Haverbeke Date: Mon, 19 Aug 2013 15:44:33 +0200 Subject: [PATCH 1420/5780] [project page] Clarify sentence --- index.html | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/index.html b/index.html index 127672dc8b..96e2eecd24 100644 --- a/index.html +++ b/index.html @@ -182,7 +182,7 @@

    Browser support

    Modern mobile browsers tend to partly work. Bug reports and patches for mobile support are welcome, but the maintainer does not - have the time budget to actually work on it himself.

    + have the time or budget to actually work on it himself.

    From 8214c1fa2c9dc34dbae56958f3a21555cb092b48 Mon Sep 17 00:00:00 2001 From: Marijn Haverbeke Date: Mon, 19 Aug 2013 15:57:38 +0200 Subject: [PATCH 1421/5780] [javascript mode] Improve number tokenizing A leading minus isn't part of the number token, but numbers can start with a dot, which wasn't handled before. Closes #1753 --- mode/javascript/javascript.js | 5 +++-- 1 file changed, 3 insertions(+), 2 deletions(-) diff --git a/mode/javascript/javascript.js b/mode/javascript/javascript.js index 0be9b79f7c..e8ace32d89 100644 --- a/mode/javascript/javascript.js +++ b/mode/javascript/javascript.js @@ -78,18 +78,19 @@ CodeMirror.defineMode("javascript", function(config, parserConfig) { type = tp; content = cont; return style; } - function jsTokenBase(stream, state) { var ch = stream.next(); if (ch == '"' || ch == "'") return chain(stream, state, jsTokenString(ch)); + else if (ch == "." && stream.match(/^\d+(?:[eE][+\-]?\d+)?/)) + return ret("number", "number"); else if (/[\[\]{}\(\),;\:\.]/.test(ch)) return ret(ch); else if (ch == "0" && stream.eat(/x/i)) { stream.eatWhile(/[\da-f]/i); return ret("number", "number"); } - else if (/\d/.test(ch) || ch == "-" && stream.eat(/\d/)) { + else if (/\d/.test(ch)) { stream.match(/^\d*(?:\.\d*)?(?:[eE][+\-]?\d+)?/); return ret("number", "number"); } From 3b501519ffe8a0e1380ac2d746d754626f6f3528 Mon Sep 17 00:00:00 2001 From: mats cronqvist Date: Fri, 16 Aug 2013 13:43:06 +0200 Subject: [PATCH 1422/5780] [erlang-mode] Improvements --- mode/erlang/erlang.js | 184 +++++++++++++++++++++++------------------- theme/erlang-dark.css | 10 ++- 2 files changed, 108 insertions(+), 86 deletions(-) diff --git a/mode/erlang/erlang.js b/mode/erlang/erlang.js index f6e304c93a..af8953c33b 100644 --- a/mode/erlang/erlang.js +++ b/mode/erlang/erlang.js @@ -19,6 +19,7 @@ CodeMirror.defineMode("erlang", function(cmCfg) { switch (type) { case "atom": return "atom"; case "attribute": return "attribute"; + case "boolean": return "special"; case "builtin": return "builtin"; case "comment": return "comment"; case "fun": return "meta"; @@ -47,6 +48,7 @@ CodeMirror.defineMode("erlang", function(cmCfg) { "after","begin","catch","case","cond","end","fun","if", "let","of","query","receive","try","when"]; + var separatorRE = /[\->\.,:;]/; var separatorWords = [ "->",";",":",".",","]; @@ -54,12 +56,15 @@ CodeMirror.defineMode("erlang", function(cmCfg) { "and","andalso","band","bnot","bor","bsl","bsr","bxor", "div","not","or","orelse","rem","xor"]; + var symbolRE = /[\+\-\*\/<>=\|:!]/; var symbolWords = [ - "+","-","*","/",">",">=","<","=<","=:=","==","=/=","/=","||","<-"]; + "+","-","*","/",">",">=","<","=<","=:=","==","=/=","/=","||","<-","!"]; + var openParenRE = /[<\(\[\{]/; var openParenWords = [ "<<","(","[","{"]; + var closeParenRE = /[>\)\]\}]/; var closeParenWords = [ "}","]",")",">>"]; @@ -94,37 +99,14 @@ CodeMirror.defineMode("erlang", function(cmCfg) { "term_to_binary","time","throw","tl","trunc","tuple_size", "tuple_to_list","unlink","unregister","whereis"]; - // ignored for indenting purposes - var ignoreWords = [ - ",", ":", "catch", "after", "of", "cond", "let", "query"]; - - - var smallRE = /[a-z_]/; - var largeRE = /[A-Z_]/; - var digitRE = /[0-9]/; - var octitRE = /[0-7]/; - var anumRE = /[a-z_A-Z0-9]/; - var symbolRE = /[\+\-\*\/<>=\|:]/; - var openParenRE = /[<\(\[\{]/; - var closeParenRE = /[>\)\]\}]/; - var sepRE = /[\->\.,:;]/; - - function isMember(element,list) { - return (-1 < list.indexOf(element)); - } - - function isPrev(stream,string) { - var start = stream.start; - var len = string.length; - if (len <= start) { - var word = stream.string.slice(start-len,start); - return word == string; - }else{ - return false; - } - } +// [Ø-Þ] [À-Ö] +// [ß-ö] [ø-ÿ] + var anumRE = /[\w@Ø-ÞÀ-Öß-öø-ÿ]/; + var escapesRE = + /[0-7]{1,3}|[bdefnrstv\\"']|\^[a-zA-Z]|x[0-9a-zA-Z]{2}|x{[0-9a-zA-Z]+}/; function tokenize(stream, state) { + // in multi-line string if (state.in_string) { state.in_string = (!doubleQuote(stream)); @@ -143,17 +125,13 @@ CodeMirror.defineMode("erlang", function(cmCfg) { } // attributes and type specs - if ((peekToken(state).token == "" || peekToken(state).token == ".") && - stream.peek() == '-') { - stream.next(); - if (stream.eat(smallRE) && stream.eatWhile(anumRE)) { - if (isMember(stream.current(),typeWords)) { - return rval(state,stream,"type"); - }else{ - return rval(state,stream,"attribute"); - } + if ((peekToken(state).token == "") && + stream.match(/-\s*[a-zß-öø-ÿ][\wØ-ÞÀ-Öß-öø-ÿ]*/)) { + if (isMember(stream.current(),typeWords)) { + return rval(state,stream,"type"); + }else{ + return rval(state,stream,"attribute"); } - stream.backUp(1); } var ch = stream.next(); @@ -171,24 +149,31 @@ CodeMirror.defineMode("erlang", function(cmCfg) { } // record - if ( ch == "#") { + if (ch == "#") { stream.eatWhile(anumRE); return rval(state,stream,"record"); } - // char - if ( ch == "$") { - if (stream.next() == "\\") { - if (!stream.eatWhile(octitRE)) { - stream.next(); - } + // dollar escape + if ( ch == "$" ) { + if (stream.next() == "\\" && !stream.match(escapesRE)) { + return rval(state,stream,"error"); } - return rval(state,stream,"string"); + return rval(state,stream,"number"); } // quoted atom if (ch == '\'') { - state.in_atom = (!singleQuote(stream)); + if (!(state.in_atom = (!singleQuote(stream)))) { + if (stream.match(/\s*\/\s*[0-9]/,false)) { + stream.match(/\s*\/\s*[0-9]/,true); + popToken(state); + return rval(state,stream,"fun"); // 'f'/0 style fun + } + if (stream.match(/\s*\(/,false) || stream.match(/\s*:/,false)) { + return rval(state,stream,"function"); + } + } return rval(state,stream,"atom"); } @@ -199,23 +184,19 @@ CodeMirror.defineMode("erlang", function(cmCfg) { } // variable - if (largeRE.test(ch)) { + if (/[A-Z_Ø-ÞÀ-Ö]/.test(ch)) { stream.eatWhile(anumRE); return rval(state,stream,"variable"); } // atom/keyword/BIF/function - if (smallRE.test(ch)) { + if (/[a-z_ß-öø-ÿ]/.test(ch)) { stream.eatWhile(anumRE); - if (stream.peek() == "/") { - stream.next(); - if (stream.eatWhile(digitRE)) { - return rval(state,stream,"fun"); // f/0 style fun - }else{ - stream.backUp(1); - return rval(state,stream,"atom"); - } + if (stream.match(/\s*\/\s*[0-9]/,false)) { + stream.match(/\s*\/\s*[0-9]/,true); + popToken(state); + return rval(state,stream,"fun"); // f/0 style fun } var w = stream.current(); @@ -223,37 +204,38 @@ CodeMirror.defineMode("erlang", function(cmCfg) { if (isMember(w,keywordWords)) { pushToken(state,stream); return rval(state,stream,"keyword"); - } - if (stream.peek() == "(") { + }else if (stream.match(/\s*\(/,false)) { // 'put' and 'erlang:put' are bifs, 'foo:put' is not if (isMember(w,bifWords) && (!isPrev(stream,":") || isPrev(stream,"erlang:"))) { return rval(state,stream,"builtin"); + }else if (isMember(w,guardWords)) { + return rval(state,stream,"guard"); }else{ return rval(state,stream,"function"); } - } - if (isMember(w,guardWords)) { - return rval(state,stream,"guard"); - } - if (isMember(w,operatorWords)) { + }else if (isMember(w,operatorWords)) { return rval(state,stream,"operator"); - } - if (stream.peek() == ":") { + }else if (stream.match(/\s*:/,false)) { if (w == "erlang") { return rval(state,stream,"builtin"); } else { return rval(state,stream,"function"); } + }else if (isMember(w,["true","false"])) { + return rval(state,stream,"boolean"); + }else{ + return rval(state,stream,"atom"); } - return rval(state,stream,"atom"); } // number + var digitRE = /[0-9]/; + var radixRE = /[0-9a-zA-Z]/; // 36#zZ style int if (digitRE.test(ch)) { stream.eatWhile(digitRE); if (stream.eat('#')) { - stream.eatWhile(digitRE); // 16#10 style integer + stream.eatWhile(radixRE); // 36#aZ style integer } else { if (stream.eat('.')) { // float stream.eatWhile(digitRE); @@ -279,7 +261,7 @@ CodeMirror.defineMode("erlang", function(cmCfg) { } // separators - if (greedy(stream,sepRE,separatorWords)) { + if (greedy(stream,separatorRE,separatorWords)) { // distinguish between "." as terminator and record field operator if (!state.in_record) { pushToken(state,stream); @@ -295,6 +277,17 @@ CodeMirror.defineMode("erlang", function(cmCfg) { return rval(state,stream,null); } + function isPrev(stream,string) { + var start = stream.start; + var len = string.length; + if (len <= start) { + var word = stream.string.slice(start-len,start); + return word == string; + }else{ + return false; + } + } + function nongreedy(stream,re,words) { if (stream.current().length == 1 && re.test(stream.current())) { stream.backUp(1); @@ -346,35 +339,37 @@ CodeMirror.defineMode("erlang", function(cmCfg) { return false; } - function Token(stream) { - this.token = stream ? stream.current() : ""; - this.column = stream ? stream.column() : 0; - this.indent = stream ? stream.indentation() : 0; + function isMember(element,list) { + return (-1 < list.indexOf(element)); } +///////////////////////////////////////////////////////////////////////////// function myIndent(state,textAfter) { var indent = cmCfg.indentUnit; - var outdentWords = ["after","catch"]; var token = (peekToken(state)).token; var wordAfter = takewhile(textAfter,/[^a-z]/); if (state.in_string || state.in_atom) { - return 0; - }else if (token == "." || token == "") { + return CodeMirror.Pass; + }else if (token == "") { return 0; }else if (isMember(token,openParenWords)) { return (peekToken(state)).column+token.length; + }else if (token == "when") { + return (peekToken(state)).column+token.length+1; + }else if (token == "fun" && wordAfter == "") { + return (peekToken(state)).column+token.length; }else if (token == "->") { - if (wordAfter == "end") { + if (isMember(wordAfter,["end","after","catch"])) { return peekToken(state,2).column; }else if (peekToken(state,2).token == "fun") { return peekToken(state,2).column+indent; - }else if (peekToken(state,2).token == ".") { + }else if (peekToken(state,2).token == "") { return indent; }else{ return (peekToken(state)).indent+indent; } - }else if (isMember(wordAfter,outdentWords)) { + }else if (isMember(wordAfter,["after","catch","of"])) { return (peekToken(state)).indent; }else{ return (peekToken(state)).column+indent; @@ -386,6 +381,12 @@ CodeMirror.defineMode("erlang", function(cmCfg) { return m ? str.slice(0,m.index) : str; } + function Token(stream) { + this.token = stream ? stream.current() : ""; + this.column = stream ? stream.column() : 0; + this.indent = stream ? stream.indentation() : 0; + } + function popToken(state) { return state.tokenStack.pop(); } @@ -403,7 +404,13 @@ CodeMirror.defineMode("erlang", function(cmCfg) { function pushToken(state,stream) { var token = stream.current(); var prev_token = peekToken(state).token; - if (isMember(token,ignoreWords)) { + + if (token == ".") { + state.tokenStack = []; + return false; + }else if(isMember(token,[",", ":", "of", "cond", "let", "query"])) { + return false; + }else if (drop_last(prev_token,token)) { return false; }else if (drop_both(prev_token,token)) { popToken(state); @@ -411,18 +418,25 @@ CodeMirror.defineMode("erlang", function(cmCfg) { }else if (drop_first(prev_token,token)) { popToken(state); return pushToken(state,stream); + }else if (isMember(token,["after","catch"])) { + return false; }else{ state.tokenStack.push(new Token(stream)); return true; } } + function drop_last(open, close) { + switch(open+" "+close) { + case "when ;": return true; + default: return false; + } + } + function drop_first(open, close) { switch (open+" "+close) { case "when ->": return true; case "-> end": return true; - case "-> .": return true; - case ". .": return true; default: return false; } } @@ -439,6 +453,8 @@ CodeMirror.defineMode("erlang", function(cmCfg) { case "if end": return true; case "receive end": return true; case "try end": return true; + case "-> catch": return true; + case "-> after": return true; case "-> ;": return true; default: return false; } diff --git a/theme/erlang-dark.css b/theme/erlang-dark.css index fce9f68664..a4a3682647 100644 --- a/theme/erlang-dark.css +++ b/theme/erlang-dark.css @@ -4,7 +4,7 @@ .cm-s-erlang-dark .CodeMirror-linenumber { color: #d0d0d0; } .cm-s-erlang-dark .CodeMirror-cursor { border-left: 1px solid white !important; } -.cm-s-erlang-dark span.cm-atom { color: #845dc4; } +.cm-s-erlang-dark span.cm-atom { color: #f133f1; } .cm-s-erlang-dark span.cm-attribute { color: #ff80e1; } .cm-s-erlang-dark span.cm-bracket { color: #ff9d00; } .cm-s-erlang-dark span.cm-builtin { color: #eaa; } @@ -14,11 +14,17 @@ .cm-s-erlang-dark span.cm-keyword { color: #ffee80; } .cm-s-erlang-dark span.cm-meta { color: #50fefe; } .cm-s-erlang-dark span.cm-number { color: #ffd0d0; } -.cm-s-erlang-dark span.cm-operator { color: #d11; } +.cm-s-erlang-dark span.cm-operator { color: #d55; } +.cm-s-erlang-dark span.cm-property { color: #ccc; } +.cm-s-erlang-dark span.cm-qualifier { color: #ccc; } +.cm-s-erlang-dark span.cm-quote { color: #ccc; } +.cm-s-erlang-dark span.cm-special { color: #ffbbbb; } .cm-s-erlang-dark span.cm-string { color: #3ad900; } +.cm-s-erlang-dark span.cm-string-2 { color: #ccc; } .cm-s-erlang-dark span.cm-tag { color: #9effff; } .cm-s-erlang-dark span.cm-variable { color: #50fe50; } .cm-s-erlang-dark span.cm-variable-2 { color: #e0e; } +.cm-s-erlang-dark span.cm-variable-3 { color: #ccc; } .cm-s-erlang-dark .CodeMirror-activeline-background {background: #013461 !important;} .cm-s-erlang-dark .CodeMirror-matchingbracket {outline:1px solid grey; color:white !important;} From e800079f2930bd670aa936740b26e462a5e4cfb4 Mon Sep 17 00:00:00 2001 From: Marijn Haverbeke Date: Mon, 19 Aug 2013 16:26:45 +0200 Subject: [PATCH 1423/5780] Fix another problem caused by the hack for #1474 --- lib/codemirror.js | 5 +++-- 1 file changed, 3 insertions(+), 2 deletions(-) diff --git a/lib/codemirror.js b/lib/codemirror.js index 15a3daaaef..123014b4a3 100644 --- a/lib/codemirror.js +++ b/lib/codemirror.js @@ -1600,7 +1600,7 @@ window.CodeMirror = (function() { on(d.scroller, "dragover", drag_); on(d.scroller, "drop", operation(cm, onDrop)); } - on(d.scroller, "paste", function(e){ + on(d.scroller, "paste", function(e) { if (eventInWidget(d, e)) return; focusInput(cm); fastPoll(cm); @@ -1609,7 +1609,7 @@ window.CodeMirror = (function() { // Workaround for webkit bug https://bugs.webkit.org/show_bug.cgi?id=90206 // Add a char to the end of textarea before paste occur so that // selection doesn't span to the end of textarea. - if (webkit && !cm.state.fakedLastChar) { + if (webkit && !cm.state.fakedLastChar && !(new Date - cm.state.lastMiddleDown < 200)) { var start = d.input.selectionStart, end = d.input.selectionEnd; d.input.value += "$"; d.input.selectionStart = start; @@ -1679,6 +1679,7 @@ window.CodeMirror = (function() { if (captureMiddleClick) onContextMenu.call(cm, cm, e); return; case 2: + if (webkit) cm.state.lastMiddleDown = +new Date; if (start) extendSelection(cm.doc, start); setTimeout(bind(focusInput, cm), 20); e_preventDefault(e); From fdc2de9de3e260c05a5e7b4d9cfbab0014cf8a48 Mon Sep 17 00:00:00 2001 From: Gabriel Nahmias Date: Sun, 18 Aug 2013 12:40:04 -0500 Subject: [PATCH 1424/5780] [the-matrix theme] Add --- theme/the-matrix.css | 24 ++++++++++++++++++++++++ 1 file changed, 24 insertions(+) create mode 100644 theme/the-matrix.css diff --git a/theme/the-matrix.css b/theme/the-matrix.css new file mode 100644 index 0000000000..e0e77ffb83 --- /dev/null +++ b/theme/the-matrix.css @@ -0,0 +1,24 @@ +.cm-s-the-matrix.CodeMirror { background: #000000; color: #00FF00; } +.cm-s-the-matrix span.CodeMirror-selected { background: #a8f !important; } +.cm-s-the-matrix .CodeMirror-gutters { background: #060; border-right: 2px solid #00FF00; } +.cm-s-the-matrix .CodeMirror-linenumber { color: #FFFFFF; } +.cm-s-the-matrix .CodeMirror-cursor { border-left: 1px solid #00FF00 !important; } + +.cm-s-the-matrix span.cm-keyword {color: #008803; font-weight: bold;} +.cm-s-the-matrix span.cm-atom {color: #3FF;} +.cm-s-the-matrix span.cm-number {color: #FFB94F;} +.cm-s-the-matrix span.cm-def {color: #99C;} +.cm-s-the-matrix span.cm-variable {color: #F6C;} +.cm-s-the-matrix span.cm-variable-2 {color: #C6F;} +.cm-s-the-matrix span.cm-variable-3 {color: #96F;} +.cm-s-the-matrix span.cm-property {color: #62FFA0;} +.cm-s-the-matrix span.cm-operator {color: #999} +.cm-s-the-matrix span.cm-comment {color: #CCCCCC;} +.cm-s-the-matrix span.cm-string {color: #39C;} +.cm-s-the-matrix span.cm-meta {color: #C9F;} +.cm-s-the-matrix span.cm-error {color: #FF0000;} +.cm-s-the-matrix span.cm-qualifier {color: #FFF700;} +.cm-s-the-matrix span.cm-builtin {color: #30a;} +.cm-s-the-matrix span.cm-bracket {color: #cc7;} +.cm-s-the-matrix span.cm-tag {color: #FFBD40;} +.cm-s-the-matrix span.cm-attribute {color: #FFF700;} From 3d3686d51600c4785a15d95018421ea108c7b9c5 Mon Sep 17 00:00:00 2001 From: Marijn Haverbeke Date: Mon, 19 Aug 2013 16:37:29 +0200 Subject: [PATCH 1425/5780] [the-matrix theme] Integrate --- demo/theme.html | 2 ++ theme/the-matrix.css | 2 ++ 2 files changed, 4 insertions(+) diff --git a/demo/theme.html b/demo/theme.html index d6aba19870..504673d865 100644 --- a/demo/theme.html +++ b/demo/theme.html @@ -24,6 +24,7 @@ + @@ -88,6 +89,7 @@

    Theme Demo

    + diff --git a/theme/the-matrix.css b/theme/the-matrix.css index e0e77ffb83..1ea26a1016 100644 --- a/theme/the-matrix.css +++ b/theme/the-matrix.css @@ -22,3 +22,5 @@ .cm-s-the-matrix span.cm-bracket {color: #cc7;} .cm-s-the-matrix span.cm-tag {color: #FFBD40;} .cm-s-the-matrix span.cm-attribute {color: #FFF700;} + +.cm-s-the-matrix .CodeMirror-activeline-background {background: #040;} From 612c6c9b2de394c14eb95459a5e11549912b12cd Mon Sep 17 00:00:00 2001 From: Daniel Huigens Date: Mon, 19 Aug 2013 18:23:33 +0200 Subject: [PATCH 1426/5780] Fix windows detection --- lib/codemirror.js | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/lib/codemirror.js b/lib/codemirror.js index 123014b4a3..fed6830d29 100644 --- a/lib/codemirror.js +++ b/lib/codemirror.js @@ -24,7 +24,7 @@ window.CodeMirror = (function() { // This is woefully incomplete. Suggestions for alternative methods welcome. var mobile = ios || /Android|webOS|BlackBerry|Opera Mini|Opera Mobi|IEMobile/i.test(navigator.userAgent); var mac = ios || /Mac/.test(navigator.platform); - var windows = /windows/i.test(navigator.platform); + var windows = /win/i.test(navigator.platform); var opera_version = opera && navigator.userAgent.match(/Version\/(\d*\.\d*)/); if (opera_version) opera_version = Number(opera_version[1]); From 569a636553a83c0bce7497314729a0a34f4add7d Mon Sep 17 00:00:00 2001 From: Ingo Richter Date: Mon, 19 Aug 2013 15:27:13 -0700 Subject: [PATCH 1427/5780] [css mode] Add flow-from and flow-into properties --- mode/css/css.js | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/mode/css/css.js b/mode/css/css.js index 8abfb315c2..2eaa96a4a8 100644 --- a/mode/css/css.js +++ b/mode/css/css.js @@ -366,8 +366,8 @@ CodeMirror.defineMode("css-base", function(config, parserConfig) { "drop-initial-before-align", "drop-initial-size", "drop-initial-value", "elevation", "empty-cells", "fit", "fit-position", "flex", "flex-basis", "flex-direction", "flex-flow", "flex-grow", "flex-shrink", "flex-wrap", - "float", "float-offset", "font", "font-feature-settings", "font-family", - "font-kerning", "font-language-override", "font-size", "font-size-adjust", + "float", "float-offset", "flow-from", "flow-into", "font", "font-feature-settings", + "font-family", "font-kerning", "font-language-override", "font-size", "font-size-adjust", "font-stretch", "font-style", "font-synthesis", "font-variant", "font-variant-alternates", "font-variant-caps", "font-variant-east-asian", "font-variant-ligatures", "font-variant-numeric", "font-variant-position", From 35688322fac76223f0a2b840262b2ecff9b3502e Mon Sep 17 00:00:00 2001 From: Daniel Huigens Date: Tue, 20 Aug 2013 13:08:15 +0200 Subject: [PATCH 1428/5780] Some small editing corrections in the manual --- doc/manual.html | 6 +++--- 1 file changed, 3 insertions(+), 3 deletions(-) diff --git a/doc/manual.html b/doc/manual.html index 3d0b70b6dd..c1cf9f9c64 100644 --- a/doc/manual.html +++ b/doc/manual.html @@ -930,7 +930,7 @@

    Cursor and selection methods

    Set the cursor position. You can either pass a single {line, ch} object, or the line and the character as two separate parameters.
    -
    doc.setSelection(anchor: {line, ch}, head: {line, ch})
    +
    doc.setSelection(anchor: {line, ch}, ?head: {line, ch})
    Set the selection range. anchor and head should be {line, ch} objects. head defaults to anchor when @@ -941,7 +941,7 @@

    Cursor and selection methods

    will, if shift is held or the extending flag is set, move the head of the selection while leaving the anchor at its current - place. pos2 is optional, and can be passed to + place. to is optional, and can be passed to ensure a region (for example a word or paragraph) will end up selected (in addition to whatever lies between that region and the current anchor).
    @@ -2326,7 +2326,7 @@

    Writing CodeMirror Modes

    state.

    In a nested mode, it is recommended to add an - extra methods, innerMode which, given a state object, + extra method, innerMode which, given a state object, returns a {state, mode} object with the inner mode and its state for the current position. These are used by utility scripts such as the tag closer to From 69c8610dce41eabad58ba5c20f094554c78785fe Mon Sep 17 00:00:00 2001 From: Marijn Haverbeke Date: Tue, 20 Aug 2013 16:40:08 +0200 Subject: [PATCH 1429/5780] [fullscreen addon] Add --- addon/display/fullscreen.css | 6 +++++ addon/display/fullscreen.js | 26 ++++++++++++++++++++ demo/fullscreen.html | 46 ++++++++---------------------------- doc/compress.html | 1 + doc/manual.html | 7 ++++++ 5 files changed, 50 insertions(+), 36 deletions(-) create mode 100644 addon/display/fullscreen.css create mode 100644 addon/display/fullscreen.js diff --git a/addon/display/fullscreen.css b/addon/display/fullscreen.css new file mode 100644 index 0000000000..00ad677ff5 --- /dev/null +++ b/addon/display/fullscreen.css @@ -0,0 +1,6 @@ +.CodeMirror-fullscreen { + position: fixed; + top: 0; left: 0; right: 0; bottom: 0; + height: auto; + z-index: 9999; +} diff --git a/addon/display/fullscreen.js b/addon/display/fullscreen.js new file mode 100644 index 0000000000..1ceee327d5 --- /dev/null +++ b/addon/display/fullscreen.js @@ -0,0 +1,26 @@ +(function() { + "use strict"; + + CodeMirror.defineOption("fullScreen", false, function(cm, val, old) { + if (old == CodeMirror.Init) old = false; + if (!old == !val) return; + if (val) setFullscreen(cm); + else setNormal(cm); + }); + + function setFullscreen(cm) { + cm.state.restoreScreenScrollPos = {top: window.pageYOffset, left: window.pageXOffset}; + cm.getWrapperElement().className += " CodeMirror-fullscreen"; + document.documentElement.style.overflow = "hidden"; + cm.refresh(); + } + + function setNormal(cm) { + var wrap = cm.getWrapperElement(); + wrap.className = wrap.className.replace(/\s*CodeMirror-fullscreen\b/, ""); + document.documentElement.style.overflow = ""; + var scroll = cm.state.restoreScreenScrollPos; + window.scrollTo(scroll.left, scroll.top); + cm.refresh(); + } +})(); diff --git a/demo/fullscreen.html b/demo/fullscreen.html index 8cf8005bdb..827d55d0cd 100644 --- a/demo/fullscreen.html +++ b/demo/fullscreen.html @@ -5,18 +5,12 @@ + - + +

    -

    Match Highlighter Demo

    +

    Match Selection Demo

    + +

    The TOML Mode

    +

    Created by Forbes Lindesay.

    +

    MIME type defined: text/x-toml.

    +
    diff --git a/mode/toml/toml.js b/mode/toml/toml.js new file mode 100644 index 0000000000..1d163f13bf --- /dev/null +++ b/mode/toml/toml.js @@ -0,0 +1,71 @@ +CodeMirror.defineMode("toml", function () { + return { + startState: function () { + return { + inString: false, + stringType: "", + lhs: true, + inArray: 0 + }; + }, + token: function (stream, state) { + //check for state changes + if (!state.inString && ((stream.peek() == '"') || (stream.peek() == "'"))) { + state.stringType = stream.peek(); + stream.next(); // Skip quote + state.inString = true; // Update state + } + if (stream.sol() && state.inArray === 0) { + state.lhs = true; + } + //return state + if (state.inString) { + while (state.inString && !stream.eol()) { + if (stream.peek() === state.stringType) { + stream.next(); // Skip quote + state.inString = false; // Clear flag + } else if (stream.peek() === '\\') { + stream.next(); + stream.next(); + } else { + stream.match(/^.[^\\\"\']*/); + } + } + return state.lhs ? "property string" : "string"; // Token style + } else if (state.inArray && stream.peek() === ']') { + stream.next(); + state.inArray--; + return 'bracket'; + } else if (state.lhs && stream.peek() === '[' && stream.skipTo(']')) { + stream.next();//skip closing ] + return "atom"; + } else if (stream.peek() === "#") { + stream.skipToEnd(); + return "comment"; + } else if (stream.eatSpace()) { + return null; + } else if (state.lhs && stream.eatWhile(function (c) { return c != '=' && c != ' '; })) { + return "property"; + } else if (state.lhs && stream.peek() === "=") { + stream.next(); + state.lhs = false; + return null; + } else if (!state.lhs && stream.match(/^\d\d\d\d[\d\-\:\.T]*Z/)) { + return 'atom'; //date + } else if (!state.lhs && (stream.match('true') || stream.match('false'))) { + return 'atom'; + } else if (!state.lhs && stream.peek() === '[') { + state.inArray++; + stream.next(); + return 'bracket'; + } else if (!state.lhs && stream.match(/^\-?\d+(?:\.\d+)?/)) { + return 'number'; + } else if (!stream.eatSpace()) { + stream.next(); + } + return null; + } + }; +}); + +CodeMirror.defineMIME('text/x-toml', 'toml'); From df7ce151b4baf26ffbd0acb0d67ab9ac28b0e2eb Mon Sep 17 00:00:00 2001 From: Marijn Haverbeke Date: Tue, 3 Sep 2013 18:01:44 +0200 Subject: [PATCH 1471/5780] [tern addon] Fix fetching of parser state Issue #1799 --- addon/tern/tern.js | 5 ++++- 1 file changed, 4 insertions(+), 1 deletion(-) diff --git a/addon/tern/tern.js b/addon/tern/tern.js index 1be5ba7f5d..4348c8d8e8 100644 --- a/addon/tern/tern.js +++ b/addon/tern/tern.js @@ -233,7 +233,10 @@ closeArgHints(ts); if (cm.somethingSelected()) return; - var lex = cm.getTokenAt(cm.getCursor()).state.lexical; + var state = cm.getTokenAt(cm.getCursor()).state; + var inner = CodeMirror.innerMode(cm.getMode(), state); + if (inner.mode.name != "javascript") return; + var lex = inner.state.lexical; if (lex.info != "call") return; var ch = lex.column, pos = lex.pos || 0; From 4f081574b95c73bec7214f3707b03a76647d6060 Mon Sep 17 00:00:00 2001 From: Marijn Haverbeke Date: Thu, 5 Sep 2013 10:37:02 +0200 Subject: [PATCH 1472/5780] [sql mode] Add comment style properties --- mode/sql/sql.js | 6 +++++- 1 file changed, 5 insertions(+), 1 deletion(-) diff --git a/mode/sql/sql.js b/mode/sql/sql.js index 9016cc7aae..e9dcb7fd75 100644 --- a/mode/sql/sql.js +++ b/mode/sql/sql.js @@ -180,7 +180,11 @@ CodeMirror.defineMode("sql", function(config, parserConfig) { if (!cx) return CodeMirror.Pass; if (cx.align) return cx.col + (textAfter.charAt(0) == cx.type ? 0 : 1); else return cx.indent + config.indentUnit; - } + }, + + blockCommentStart: "/*", + blockCommentEnd: "*/", + lineComment: support.commentSlashSlash ? "//" : support.commentHash ? "#" : null }; }); From 36edc05b1d7386fa5d8cb6f67214896ed95123a1 Mon Sep 17 00:00:00 2001 From: Marijn Haverbeke Date: Thu, 5 Sep 2013 11:22:45 +0200 Subject: [PATCH 1473/5780] In refresh, recompute line heights when no known text height Issue #1793 --- lib/codemirror.js | 2 ++ 1 file changed, 2 insertions(+) diff --git a/lib/codemirror.js b/lib/codemirror.js index fcb6bc03dd..c9a8b9d580 100644 --- a/lib/codemirror.js +++ b/lib/codemirror.js @@ -3188,9 +3188,11 @@ window.CodeMirror = (function() { operation: function(f){return runInOp(this, f);}, refresh: operation(null, function() { + var badHeight = this.display.cachedTextHeight == null; clearCaches(this); updateScrollPos(this, this.doc.scrollLeft, this.doc.scrollTop); regChange(this); + if (badHeight) estimateLineHeights(this); }), swapDoc: operation(null, function(doc) { From 22ea8d9dbc41c108d67675d7b3a8cbdde7e7133d Mon Sep 17 00:00:00 2001 From: Marijn Haverbeke Date: Thu, 5 Sep 2013 11:32:33 +0200 Subject: [PATCH 1474/5780] [matchtags addon] Don't match tags when there is a selection Closes #1802 --- addon/edit/matchtags.js | 1 + 1 file changed, 1 insertion(+) diff --git a/addon/edit/matchtags.js b/addon/edit/matchtags.js index 52e8460245..f189c1f8ef 100644 --- a/addon/edit/matchtags.js +++ b/addon/edit/matchtags.js @@ -25,6 +25,7 @@ cm.state.failedTagMatch = false; cm.operation(function() { clear(cm); + if (cm.somethingSelected()) return; var cur = cm.getCursor(), range = cm.getViewport(); range.from = Math.min(range.from, cur.line); range.to = Math.max(cur.line + 1, range.to); var match = CodeMirror.findMatchingTag(cm, cur, range); From b8e09e976015c065d7911e472bc1902d7f299940 Mon Sep 17 00:00:00 2001 From: AndersMad Date: Sat, 31 Aug 2013 09:57:19 +0200 Subject: [PATCH 1475/5780] [css-hint addon] Add I have created this simple css autocomplete/hint that uses data from the css mode. Autocomplete works for both properties and values - but it does not filter the values by property. Better than nothing for now. --- addon/hint/css-hint.js | 50 ++++++++++++++++++++++++++++++++++++++++++ 1 file changed, 50 insertions(+) create mode 100644 addon/hint/css-hint.js diff --git a/addon/hint/css-hint.js b/addon/hint/css-hint.js new file mode 100644 index 0000000000..d8fe3ebaf0 --- /dev/null +++ b/addon/hint/css-hint.js @@ -0,0 +1,50 @@ +(function () { + "use strict"; + + function getHints(cm) { + var cur = cm.getCursor(), token = cm.getTokenAt(cur); + var inner = CodeMirror.innerMode(cm.getMode(), token.state); + if (inner.mode.name != "css-base") return; + + // If it's not a 'word-style' token, ignore the token. + if (!/^[\w$_-]*$/.test(token.string)) { + token = { + start: cur.ch, end: cur.ch, string: "", state: token.state, + type: null + }; + var stack = token.state.stack; + var lastToken = stack && stack.length > 0 ? stack[stack.length - 1] : ""; + if (token.string == ":" || lastToken.indexOf("property") == 0) + token.type = "variable"; + else if (token.string == "{" || lastToken.indexOf("rule") == 0) + token.type = "property"; + } + + if (!token.type) + return; + + var spec = CodeMirror.resolveMode("text/css"); + var keywords = null; + if (token.type.indexOf("property") == 0) + keywords = spec.propertyKeywords; + else if (token.type.indexOf("variable") == 0) + keywords = spec.valueKeywords; + + if (!keywords) + return; + + var result = []; + for (var name in keywords) { + if (name.indexOf(token.string) == 0 /* > -1 */) + result.push(name); + } + + return { + list: result, + from: CodeMirror.Pos(cur.line, token.start), + to: CodeMirror.Pos(cur.line, token.end) + }; + } + + CodeMirror.registerHelper("hint", "css", getHints); +})(); From 90d036d610e4ba58649ec325967b3b7ef5efd15f Mon Sep 17 00:00:00 2001 From: Marijn Haverbeke Date: Thu, 5 Sep 2013 12:34:43 +0200 Subject: [PATCH 1476/5780] [css-hint addon] Integrate --- addon/hint/css-hint.js | 2 +- doc/compress.html | 1 + doc/manual.html | 4 ++++ mode/css/css.js | 12 +++++------- 4 files changed, 11 insertions(+), 8 deletions(-) diff --git a/addon/hint/css-hint.js b/addon/hint/css-hint.js index d8fe3ebaf0..2b15300d0c 100644 --- a/addon/hint/css-hint.js +++ b/addon/hint/css-hint.js @@ -4,7 +4,7 @@ function getHints(cm) { var cur = cm.getCursor(), token = cm.getTokenAt(cur); var inner = CodeMirror.innerMode(cm.getMode(), token.state); - if (inner.mode.name != "css-base") return; + if (inner.mode.name != "css") return; // If it's not a 'word-style' token, ignore the token. if (!/^[\w$_-]*$/.test(token.string)) { diff --git a/doc/compress.html b/doc/compress.html index 92c0987ba9..5ee78bb4c5 100644 --- a/doc/compress.html +++ b/doc/compress.html @@ -157,6 +157,7 @@

    Script compression helper

    + diff --git a/doc/manual.html b/doc/manual.html index 3f591492b6..9f516b7e05 100644 --- a/doc/manual.html +++ b/doc/manual.html @@ -2049,6 +2049,10 @@

    Addons

    schema data. See the demo. +
    hint/css-hint.js
    +
    A minimal hinting function for CSS code. + Defines CodeMirror.hint.css.
    +
    hint/python-hint.js
    A very simple hinting function for Python code. Defines CodeMirror.hint.python.
    diff --git a/mode/css/css.js b/mode/css/css.js index 0caebe9fac..085b119de4 100644 --- a/mode/css/css.js +++ b/mode/css/css.js @@ -1,10 +1,8 @@ -CodeMirror.defineMode("css", function(config) { - return CodeMirror.getMode(config, "text/css"); -}); - -CodeMirror.defineMode("css-base", function(config, parserConfig) { +CodeMirror.defineMode("css", function(config, parserConfig) { "use strict"; + if (!parserConfig.propertyKeywords) parserConfig = CodeMirror.resolveMode("text/css"); + var indentUnit = config.indentUnit, hooks = parserConfig.hooks || {}, atMediaTypes = parserConfig.atMediaTypes || {}, @@ -580,7 +578,7 @@ CodeMirror.defineMode("css-base", function(config, parserConfig) { return false; } }, - name: "css-base" + name: "css" }); CodeMirror.defineMIME("text/x-scss", { @@ -624,6 +622,6 @@ CodeMirror.defineMode("css-base", function(config, parserConfig) { } } }, - name: "css-base" + name: "css" }); })(); From 47965d3bd07e347b18e1ee4ab40617c7f1e41b12 Mon Sep 17 00:00:00 2001 From: Marijn Haverbeke Date: Thu, 5 Sep 2013 12:47:49 +0200 Subject: [PATCH 1477/5780] [closetag addon] Restructure --- addon/edit/closetag.js | 55 +++++++++++++++++++++--------------------- 1 file changed, 28 insertions(+), 27 deletions(-) diff --git a/addon/edit/closetag.js b/addon/edit/closetag.js index 454dfea5e6..0bf8881d08 100644 --- a/addon/edit/closetag.js +++ b/addon/edit/closetag.js @@ -27,9 +27,9 @@ if (val && (old == CodeMirror.Init || !old)) { var map = {name: "autoCloseTags"}; if (typeof val != "object" || val.whenClosing) - map["'/'"] = function(cm) { return autoCloseTag(cm, '/'); }; + map["'/'"] = function(cm) { return autoCloseSlash(cm); }; if (typeof val != "object" || val.whenOpening) - map["'>'"] = function(cm) { return autoCloseTag(cm, '>'); }; + map["'>'"] = function(cm) { return autoCloseGT(cm); }; cm.addKeyMap(map); } else if (!val && (old != CodeMirror.Init && old)) { cm.removeKeyMap("autoCloseTags"); @@ -41,40 +41,41 @@ var htmlIndent = ["applet", "blockquote", "body", "button", "div", "dl", "fieldset", "form", "frameset", "h1", "h2", "h3", "h4", "h5", "h6", "head", "html", "iframe", "layer", "legend", "object", "ol", "p", "select", "table", "ul"]; - function autoCloseTag(cm, ch) { + function autoCloseGT(cm) { var pos = cm.getCursor(), tok = cm.getTokenAt(pos); var inner = CodeMirror.innerMode(cm.getMode(), tok.state), state = inner.state; - if (inner.mode.name != "xml") return CodeMirror.Pass; + if (inner.mode.name != "xml" || !state.tagName) return CodeMirror.Pass; var opt = cm.getOption("autoCloseTags"), html = inner.mode.configuration == "html"; var dontCloseTags = (typeof opt == "object" && opt.dontCloseTags) || (html && htmlDontClose); var indentTags = (typeof opt == "object" && opt.indentTags) || (html && htmlIndent); - if (ch == ">" && state.tagName) { - var tagName = state.tagName; - if (tok.end > pos.ch) tagName = tagName.slice(0, tagName.length - tok.end + pos.ch); - var lowerTagName = tagName.toLowerCase(); - // Don't process the '>' at the end of an end-tag or self-closing tag - if (tok.type == "tag" && state.type == "closeTag" || - tok.string.indexOf("/") == (tok.string.length - 1) || // match something like - dontCloseTags && indexOf(dontCloseTags, lowerTagName) > -1) - return CodeMirror.Pass; + var tagName = state.tagName; + if (tok.end > pos.ch) tagName = tagName.slice(0, tagName.length - tok.end + pos.ch); + var lowerTagName = tagName.toLowerCase(); + // Don't process the '>' at the end of an end-tag or self-closing tag + if (tok.type == "tag" && state.type == "closeTag" || + tok.string.indexOf("/") == (tok.string.length - 1) || // match something like + dontCloseTags && indexOf(dontCloseTags, lowerTagName) > -1) + return CodeMirror.Pass; - var doIndent = indentTags && indexOf(indentTags, lowerTagName) > -1; - var curPos = doIndent ? CodeMirror.Pos(pos.line + 1, 0) : CodeMirror.Pos(pos.line, pos.ch + 1); - cm.replaceSelection(">" + (doIndent ? "\n\n" : "") + "", - {head: curPos, anchor: curPos}); - if (doIndent) { - cm.indentLine(pos.line + 1); - cm.indentLine(pos.line + 2); - } - return; - } else if (ch == "/" && tok.string == "<") { - var tagName = state.context && state.context.tagName; - if (tagName) cm.replaceSelection("/" + tagName + ">", "end"); - return; + var doIndent = indentTags && indexOf(indentTags, lowerTagName) > -1; + var curPos = doIndent ? CodeMirror.Pos(pos.line + 1, 0) : CodeMirror.Pos(pos.line, pos.ch + 1); + cm.replaceSelection(">" + (doIndent ? "\n\n" : "") + "", + {head: curPos, anchor: curPos}); + if (doIndent) { + cm.indentLine(pos.line + 1); + cm.indentLine(pos.line + 2); } - return CodeMirror.Pass; + } + + function autoCloseSlash(cm) { + var pos = cm.getCursor(), tok = cm.getTokenAt(pos); + var inner = CodeMirror.innerMode(cm.getMode(), tok.state), state = inner.state; + if (tok.string != "<" || inner.mode.name != "xml") return CodeMirror.Pass; + + var tagName = state.context && state.context.tagName; + if (tagName) cm.replaceSelection("/" + tagName + ">", "end"); } function indexOf(collection, elt) { From a55b506103247d6664ee2fe72e21a85cb86e164b Mon Sep 17 00:00:00 2001 From: Marijn Haverbeke Date: Thu, 5 Sep 2013 12:51:31 +0200 Subject: [PATCH 1478/5780] [closetag addon] Allow closing by typing / even when before a word Closes #1795 --- addon/edit/closetag.js | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/addon/edit/closetag.js b/addon/edit/closetag.js index 0bf8881d08..0bc3e8be17 100644 --- a/addon/edit/closetag.js +++ b/addon/edit/closetag.js @@ -72,7 +72,7 @@ function autoCloseSlash(cm) { var pos = cm.getCursor(), tok = cm.getTokenAt(pos); var inner = CodeMirror.innerMode(cm.getMode(), tok.state), state = inner.state; - if (tok.string != "<" || inner.mode.name != "xml") return CodeMirror.Pass; + if (tok.string.charAt(0) != "<" || inner.mode.name != "xml") return CodeMirror.Pass; var tagName = state.context && state.context.tagName; if (tagName) cm.replaceSelection("/" + tagName + ">", "end"); From f5b9d3ca9030117806a8822f48853b7ff4b8bdaa Mon Sep 17 00:00:00 2001 From: Marijn Haverbeke Date: Thu, 5 Sep 2013 14:52:48 +0200 Subject: [PATCH 1479/5780] [haskell mode] Allow keyword sets to be overridden --- mode/haskell/haskell.js | 6 +++++- 1 file changed, 5 insertions(+), 1 deletion(-) diff --git a/mode/haskell/haskell.js b/mode/haskell/haskell.js index b18d5ced1a..59ca7f0baa 100644 --- a/mode/haskell/haskell.js +++ b/mode/haskell/haskell.js @@ -1,4 +1,4 @@ -CodeMirror.defineMode("haskell", function() { +CodeMirror.defineMode("haskell", function(_config, modeConfig) { function switchState(source, setState, f) { setState(f); @@ -221,6 +221,10 @@ CodeMirror.defineMode("haskell", function() { "unwords", "unzip", "unzip3", "userError", "words", "writeFile", "zip", "zip3", "zipWith", "zipWith3"); + var override = modeConfig.overrideKeywords; + if (override) for (var word in override) if (override.hasOwnProperty(word)) + wkw[word] = override[word]; + return wkw; })(); From fab90b772626b054ba82a81f26b9caa35a356f93 Mon Sep 17 00:00:00 2001 From: Marijn Haverbeke Date: Thu, 5 Sep 2013 15:10:51 +0200 Subject: [PATCH 1480/5780] [continuecomment addon] Clean up, support line comment blocks --- addon/comment/continuecomment.js | 40 ++++++++++++++++++++------------ 1 file changed, 25 insertions(+), 15 deletions(-) diff --git a/addon/comment/continuecomment.js b/addon/comment/continuecomment.js index 9dba156189..94e5a3760f 100644 --- a/addon/comment/continuecomment.js +++ b/addon/comment/continuecomment.js @@ -1,35 +1,43 @@ (function() { var modes = ["clike", "css", "javascript"]; for (var i = 0; i < modes.length; ++i) - CodeMirror.extendMode(modes[i], {blockCommentStart: "/*", - blockCommentEnd: "*/", - blockCommentContinue: " * "}); + CodeMirror.extendMode(modes[i], {blockCommentContinue: " * "}); function continueComment(cm) { var pos = cm.getCursor(), token = cm.getTokenAt(pos); + if (token.type != "comment") return CodeMirror.Pass; var mode = CodeMirror.innerMode(cm.getMode(), token.state).mode; - var space; - if (token.type == "comment" && mode.blockCommentStart && mode.blockCommentContinue) { + var insert; + if (mode.blockCommentStart && mode.blockCommentContinue) { var end = token.string.indexOf(mode.blockCommentEnd); var full = cm.getRange(CodeMirror.Pos(pos.line, 0), CodeMirror.Pos(pos.line, token.end)), found; if (end != -1 && end == token.string.length - mode.blockCommentEnd.length) { // Comment ended, don't continue it } else if (token.string.indexOf(mode.blockCommentStart) == 0) { - space = full.slice(0, token.start); - if (!/^\s*$/.test(space)) { - space = ""; - for (var i = 0; i < token.start; ++i) space += " "; + insert = full.slice(0, token.start); + if (!/^\s*$/.test(insert)) { + insert = ""; + for (var i = 0; i < token.start; ++i) insert += " "; } } else if ((found = full.indexOf(mode.blockCommentContinue)) != -1 && found + mode.blockCommentContinue.length > token.start && /^\s*$/.test(full.slice(0, found))) { - space = full.slice(0, found); + insert = full.slice(0, found); + } + if (insert != null) insert += mode.blockCommentContinue; + } + if (insert == null && mode.lineComment) { + var line = cm.getLine(pos.line), found = line.indexOf(mode.lineComment); + if (found > -1) { + insert = line.slice(0, found); + if (/\S/.test(insert)) insert = null; + else insert += mode.lineComment + line.slice(found + mode.lineComment.length).match(/^\s*/)[0]; } } - if (space != null) - cm.replaceSelection("\n" + space + mode.blockCommentContinue, "end"); + if (insert != null) + cm.replaceSelection("\n" + insert, "end"); else return CodeMirror.Pass; } @@ -37,8 +45,10 @@ CodeMirror.defineOption("continueComments", null, function(cm, val, prev) { if (prev && prev != CodeMirror.Init) cm.removeKeyMap("continueComment"); - var map = {name: "continueComment"}; - map[typeof val == "string" ? val : "Enter"] = continueComment; - cm.addKeyMap(map); + if (val) { + var map = {name: "continueComment"}; + map[typeof val == "string" ? val : "Enter"] = continueComment; + cm.addKeyMap(map); + } }); })(); From f8282ccd7a17c2f9b81aa90452fe04c89cc256e7 Mon Sep 17 00:00:00 2001 From: Peter Kroon Date: Thu, 5 Sep 2013 14:20:04 +0200 Subject: [PATCH 1481/5780] [less mode] Fix indentation of nested rules Issue #1801 --- mode/less/less.js | 98 +++++++++++++++++++++++------------------------ 1 file changed, 49 insertions(+), 49 deletions(-) diff --git a/mode/less/less.js b/mode/less/less.js index 09f510e032..d637f6db98 100644 --- a/mode/less/less.js +++ b/mode/less/less.js @@ -1,7 +1,8 @@ /* LESS mode - http://www.lesscss.org/ Ported to CodeMirror by Peter Kroon - Report bugs/issues here: https://github.com/marijnh/CodeMirror/issues GitHub: @peterkroon + Report bugs/issues here: https://github.com/marijnh/CodeMirror/issues + GitHub: @peterkroon */ CodeMirror.defineMode("less", function(config) { @@ -17,68 +18,60 @@ CodeMirror.defineMode("less", function(config) { else if (ch == "/" && stream.eat("*")) { state.tokenize = tokenCComment; return tokenCComment(stream, state); - } - else if (ch == "<" && stream.eat("!")) { + } else if (ch == "<" && stream.eat("!")) { state.tokenize = tokenSGMLComment; return tokenSGMLComment(stream, state); - } - else if (ch == "=") ret(null, "compare"); + } else if (ch == "=") ret(null, "compare"); else if (ch == "|" && stream.eat("=")) return ret(null, "compare"); else if (ch == "\"" || ch == "'") { state.tokenize = tokenString(ch); return state.tokenize(stream, state); - } - else if (ch == "/") { // e.g.: .png will not be parsed as a class + } else if (ch == "/") { // e.g.: .png will not be parsed as a class if(stream.eat("/")){ state.tokenize = tokenSComment; return tokenSComment(stream, state); - }else{ - if(type == "string" || type == "(")return ret("string", "string"); - if(state.stack[state.stack.length-1] != undefined)return ret(null, ch); + } else { + if(type == "string" || type == "(") return ret("string", "string"); + if(state.stack[state.stack.length-1] != undefined) return ret(null, ch); stream.eatWhile(/[\a-zA-Z0-9\-_.\s]/); if( /\/|\)|#/.test(stream.peek() || (stream.eatSpace() && stream.peek() == ")")) || stream.eol() )return ret("string", "string"); // let url(/images/logo.png) without quotes return as string } - } - else if (ch == "!") { + } else if (ch == "!") { stream.match(/^\s*\w*/); return ret("keyword", "important"); - } - else if (/\d/.test(ch)) { + } else if (/\d/.test(ch)) { stream.eatWhile(/[\w.%]/); return ret("number", "unit"); - } - else if (/[,+<>*\/]/.test(ch)) { + } else if (/[,+<>*\/]/.test(ch)) { if(stream.peek() == "=" || type == "a")return ret("string", "string"); + if(ch === ",")return ret(null, ch); return ret(null, "select-op"); - } - else if (/[;{}:\[\]()~\|]/.test(ch)) { + } else if (/[;{}:\[\]()~\|]/.test(ch)) { if(ch == ":"){ stream.eatWhile(/[a-z\\\-]/); if( selectors.test(stream.current()) ){ return ret("tag", "tag"); - }else if(stream.peek() == ":"){//::-webkit-search-decoration + } else if(stream.peek() == ":"){//::-webkit-search-decoration stream.next(); stream.eatWhile(/[a-z\\\-]/); if(stream.current().match(/\:\:\-(o|ms|moz|webkit)\-/))return ret("string", "string"); if( selectors.test(stream.current().substring(1)) )return ret("tag", "tag"); return ret(null, ch); - }else{ + } else { return ret(null, ch); } - }else if(ch == "~"){ + } else if(ch == "~"){ if(type == "r")return ret("string", "string"); - }else{ + } else { return ret(null, ch); } - } - else if (ch == ".") { + } else if (ch == ".") { if(type == "(" || type == "string")return ret("string", "string"); // allow url(../image.png) stream.eatWhile(/[\a-zA-Z0-9\-_]/); if(stream.peek() == " ")stream.eatSpace(); if(stream.peek() == ")")return ret("number", "unit");//rgba(0,0,0,.25); return ret("tag", "tag"); - } - else if (ch == "#") { + } else if (ch == "#") { //we don't eat white-space, we want the hex color and or id only stream.eatWhile(/[A-Za-z0-9]/); //check if there is a proper hex color length e.g. #eee || #eeeEEE @@ -93,43 +86,42 @@ CodeMirror.defineMode("less", function(config) { //#time { color: #aaa } else if(stream.peek() == "}" )return ret("number", "unit"); //we have a valid hex color value, parse as id whenever an element/class is defined after the hex(id) value e.g. #eee aaa || #eee .aaa - else if( /[a-zA-Z\\]/.test(stream.peek()) )return ret("atom", "tag"); + else if( /[a-zA-Z\\]/.test(stream.peek()) )return ret("atom", "tag"); //when a hex value is on the end of a line, parse as id - else if(stream.eol())return ret("atom", "tag"); + else if(stream.eol())return ret("atom", "tag"); //default - else return ret("number", "unit"); - }else{//when not a valid hexvalue in the current stream e.g. #footer + else return ret("number", "unit"); + } else {//when not a valid hexvalue in the current stream e.g. #footer stream.eatWhile(/[\w\\\-]/); return ret("atom", "tag"); } - }else{//when not a valid hexvalue length + } else {//when not a valid hexvalue length stream.eatWhile(/[\w\\\-]/); return ret("atom", "tag"); } - } - else if (ch == "&") { + } else if (ch == "&") { stream.eatWhile(/[\w\-]/); return ret(null, ch); - } - else { + } else { stream.eatWhile(/[\w\\\-_%.{]/); + //console.log(type); if(type == "string"){ return ret("string", "string"); - }else if(stream.current().match(/(^http$|^https$)/) != null){ + } else if(stream.current().match(/(^http$|^https$)/) != null){ stream.eatWhile(/[\w\\\-_%.{:\/]/); return ret("string", "string"); - }else if(stream.peek() == "<" || stream.peek() == ">"){ + } else if(stream.peek() == "<" || stream.peek() == ">" || stream.peek() == "+"){ return ret("tag", "tag"); - }else if( /\(/.test(stream.peek()) ){ + } else if( /\(/.test(stream.peek()) ){ return ret(null, ch); - }else if (stream.peek() == "/" && state.stack[state.stack.length-1] != undefined){ // url(dir/center/image.png) + } else if (stream.peek() == "/" && state.stack[state.stack.length-1] != undefined){ // url(dir/center/image.png) return ret("string", "string"); - }else if( stream.current().match(/\-\d|\-.\d/) ){ // match e.g.: -5px -0.4 etc... only colorize the minus sign + } else if( stream.current().match(/\-\d|\-.\d/) ){ // match e.g.: -5px -0.4 etc... only colorize the minus sign //commment out these 2 comment if you want the minus sign to be parsed as null -500px //stream.backUp(stream.current().length-1); - //return ret(null, ch); //console.log( stream.current() ); + //return ret(null, ch); return ret("number", "unit"); - }else if( /\/|[\s\)]/.test(stream.peek() || stream.eol() || (stream.eatSpace() && stream.peek() == "/")) && stream.current().indexOf(".") !== -1){ + } else if( /\/|[\s\)]/.test(stream.peek() || stream.eol() || (stream.eatSpace() && stream.peek() == "/")) && stream.current().indexOf(".") !== -1){ if(stream.current().substring(stream.current().length-1,stream.current().length) == "{"){ stream.backUp(1); return ret("tag", "tag"); @@ -137,14 +129,15 @@ CodeMirror.defineMode("less", function(config) { stream.eatSpace(); if( /[{<>.a-zA-Z\/]/.test(stream.peek()) || stream.eol() )return ret("tag", "tag"); // e.g. button.icon-plus return ret("string", "string"); // let url(/images/logo.png) without quotes return as string - }else if( stream.eol() || stream.peek() == "[" || stream.peek() == "#" || type == "tag" ){ + } else if( stream.eol() || stream.peek() == "[" || stream.peek() == "#" || type == "tag" ){ if(stream.current().substring(stream.current().length-1,stream.current().length) == "{")stream.backUp(1); return ret("tag", "tag"); - }else if(type == "compare" || type == "a" || type == "("){ + } else if(type == "compare" || type == "a" || type == "("){ return ret("string", "string"); - }else if(type == "|" || stream.current() == "-" || type == "["){ + } else if(type == "|" || stream.current() == "-" || type == "["){ + if(type == "|" )return ret("tag", "tag"); return ret(null, ch); - }else if(stream.peek() == ":") { + } else if(stream.peek() == ":") { stream.next(); var t_v = stream.peek() == ":" ? true : false; if(!t_v){ @@ -156,11 +149,14 @@ CodeMirror.defineMode("less", function(config) { stream.backUp(new_pos-(old_pos-1)); return ret("tag", "tag"); } else stream.backUp(new_pos-(old_pos-1)); - }else{ + } else { stream.backUp(1); } if(t_v)return ret("tag", "tag"); else return ret("variable", "variable"); - }else{ + } else if(state.stack[state.stack.length-1] === "font-family"){ + return ret(null, null); + } else { + if(state.stack[state.stack.length-1] === "{" || type === "select-op" || (state.stack[state.stack.length-1] === "rule" && type === ",") )return ret("tag", "tag"); return ret("variable", "variable"); } } @@ -238,12 +234,16 @@ CodeMirror.defineMode("less", function(config) { } else if (type == "}") state.stack.pop(); else if (type == "@media") state.stack.push("@media"); - else if (context == "{" && type != "comment") state.stack.push("rule"); + else if (stream.current() === "font-family") state.stack[state.stack.length-1] = "font-family"; + else if (context == "{" && type != "comment" && type !== "tag") state.stack.push("rule"); + else if (stream.peek() === ":" && stream.current().match(/@|#/) === null) style = type; return style; }, indent: function(state, textAfter) { var n = state.stack.length; + console.log(n); + if (/^\}/.test(textAfter)) n -= state.stack[state.stack.length-1] == "rule" ? 2 : 1; return state.baseIndent + n * indentUnit; From b3353e715e9ae03aa7810d5533bfcb613f81a5b3 Mon Sep 17 00:00:00 2001 From: Marijn Haverbeke Date: Fri, 6 Sep 2013 09:41:26 +0200 Subject: [PATCH 1482/5780] [less mode] Remove console.log --- mode/less/less.js | 2 -- 1 file changed, 2 deletions(-) diff --git a/mode/less/less.js b/mode/less/less.js index d637f6db98..8384b3cd76 100644 --- a/mode/less/less.js +++ b/mode/less/less.js @@ -104,7 +104,6 @@ CodeMirror.defineMode("less", function(config) { return ret(null, ch); } else { stream.eatWhile(/[\w\\\-_%.{]/); - //console.log(type); if(type == "string"){ return ret("string", "string"); } else if(stream.current().match(/(^http$|^https$)/) != null){ @@ -242,7 +241,6 @@ CodeMirror.defineMode("less", function(config) { indent: function(state, textAfter) { var n = state.stack.length; - console.log(n); if (/^\}/.test(textAfter)) n -= state.stack[state.stack.length-1] == "rule" ? 2 : 1; From c55df0d8666b55e1d2c195c1d98770005aeb058d Mon Sep 17 00:00:00 2001 From: Matt Pass Date: Thu, 5 Sep 2013 18:23:48 +0100 Subject: [PATCH 1483/5780] Better lint icons and in fewer bytes Original error & warning icons had jagged edges due to being GIFs This made them look quite poor/low quality on dark BG'd themes So, new, better looking icons added which have been run thru TinyPNG for compression Result is fewer bytes than original and they look good on dark BG's too --- addon/lint/lint.css | 6 +++--- 1 file changed, 3 insertions(+), 3 deletions(-) diff --git a/addon/lint/lint.css b/addon/lint/lint.css index a57694e00e..e592b3672a 100644 --- a/addon/lint/lint.css +++ b/addon/lint/lint.css @@ -57,15 +57,15 @@ } .CodeMirror-lint-marker-error, .CodeMirror-lint-message-error { - background-image: url("data:image/gif;base64,R0lGODlhEAAQANUAAPVvcvWHiPVucvRuc+ttcfV6f91KVN5LU99PV/FZY/JhaM4oN84pONE4Rd1ATfJLWutVYPRgbdxpcsgWKMgZKs4lNfE/UvE/U+artcpdSc5uXveimslHPuBhW/eJhfV5efaCgO2CgP+/v+PExP///////wAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAACH5BAEAACUALAAAAAAQABAAAAZ+wJJwSCwaScgkySgkjTQZTkYzWhadnE5oE+pwqkSshwQqkzxfa4kkQXxEpA9J9EFI1KQGQQBAigYCBA14ExEWF0gXihETeA0QD3AkD5QQg0NsDnAJmwkOd5gYFSQKpXAFDBhqaxgLBwQBBAapq00YEg0UDRKqTGtKSL7Cw8JBADs="); + background-image: url("data:image/png;base64,iVBORw0KGgoAAAANSUhEUgAAABAAAAAQCAMAAAAoLQ9TAAAAHlBMVEW7AAC7AACxAAC7AAC7AAAAAAC4AAC5AAD///+7AAAUdclpAAAABnRSTlMXnORSiwCK0ZKSAAAATUlEQVR42mWPOQ7AQAgDuQLx/z8csYRmPRIFIwRGnosRrpamvkKi0FTIiMASR3hhKW+hAN6/tIWhu9PDWiTGNEkTtIOucA5Oyr9ckPgAWm0GPBog6v4AAAAASUVORK5CYII="); } .CodeMirror-lint-marker-warning, .CodeMirror-lint-message-warning { - background-image: url("data:image/gif;base64,R0lGODlhEAAQANUAAP7bc//egf/ij/7ijv/jl/7kl//mnv7lnv/uwf7CTP7DTf7DT/7IW//Na/7Na//NbP7QdP/dmbltAIJNAF03AMSAJMSCLKqASa2DS6uBSquCSrGHTq6ETbCHT7WKUrKIUcCVXL+UXMOYX8GWXsSZYMiib6+ETbOIUcOXX86uhd3Muf///wAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAACH5BAEAACsALAAAAAAQABAAAAZowJVwSCwaj0ihikRSJYcoBEL0XKlGkcjImQQhJBREKFnyICoThKeE/AAW6AXgdPyUAgrLJBEo0YsbAQyDhAEdRRwDDw8OaA4NDQImRBgFEJdglxAEGEQZKQcHBqOkKRpFF6mqq1WtrUEAOw=="); + background-image: url("data:image/png;base64,iVBORw0KGgoAAAANSUhEUgAAABAAAAAQCAMAAAAoLQ9TAAAANlBMVEX/uwDvrwD/uwD/uwD/uwD/uwD/uwD/uwD/uwD6twD/uwAAAADurwD2tQD7uAD+ugAAAAD/uwDhmeTRAAAADHRSTlMJ8mN1EYcbmiixgACm7WbuAAAAVklEQVR42n3PUQqAIBBFUU1LLc3u/jdbOJoW1P08DA9Gba8+YWJ6gNJoNYIBzAA2chBth5kLmG9YUoG0NHAUwFXwO9LuBQL1giCQb8gC9Oro2vp5rncCIY8L8uEx5ZkAAAAASUVORK5CYII="); } .CodeMirror-lint-marker-multiple { - background-image: url("data:image/png;base64,iVBORw0KGgoAAAANSUhEUgAAAAcAAAAHCAYAAADEUlfTAAAAAXNSR0IArs4c6QAAAAZiS0dEAAAAAAAA+UO7fwAAAAlwSFlzAAALEwAACxMBAJqcGAAAAAd0SU1FB9sJEAQvB2JVdrAAAAAdaVRYdENvbW1lbnQAAAAAAENyZWF0ZWQgd2l0aCBHSU1QZC5lBwAAAD1JREFUCNdtjkESADAEAzemf69f66HMqGlOIhYiFRFRtSQBWAY7mzx+EDTL6sSgb1jTk7Q87rxyqe37fXsAa78gLyZnRgEAAAAASUVORK5CYII="); + background-image: url("data:image/png;base64,iVBORw0KGgoAAAANSUhEUgAAAAcAAAAHCAMAAADzjKfhAAAACVBMVEUAAAAAAAC/v7914kyHAAAAAXRSTlMAQObYZgAAACNJREFUeNo1ioEJAAAIwmz/H90iFFSGJgFMe3gaLZ0od+9/AQZ0ADosbYraAAAAAElFTkSuQmCC"); background-repeat: no-repeat; background-position: right bottom; width: 100%; height: 100%; From 30a9ee56fe988d54e3bdd435f7c37af68c5c4c21 Mon Sep 17 00:00:00 2001 From: Marijn Haverbeke Date: Fri, 6 Sep 2013 10:23:44 +0200 Subject: [PATCH 1484/5780] Don't mutate options.gutters in setGuttersForLineNumbers Closes #1807 Closes #1808 --- lib/codemirror.js | 14 ++++++-------- 1 file changed, 6 insertions(+), 8 deletions(-) diff --git a/lib/codemirror.js b/lib/codemirror.js index c9a8b9d580..740389fc03 100644 --- a/lib/codemirror.js +++ b/lib/codemirror.js @@ -304,15 +304,13 @@ window.CodeMirror = (function() { // Make sure the gutters options contains the element // "CodeMirror-linenumbers" when the lineNumbers option is true. function setGuttersForLineNumbers(options) { - var found = false; - for (var i = 0; i < options.gutters.length; ++i) { - if (options.gutters[i] == "CodeMirror-linenumbers") { - if (options.lineNumbers) found = true; - else options.gutters.splice(i--, 1); - } + var found = indexOf(options.gutters, "CodeMirror-linenumbers"); + if (found == -1 && options.lineNumbers) { + options.gutters = options.gutters.concat(["CodeMirror-linenumbers"]); + } else if (found > -1 && !options.lineNumbers) { + options.gutters = options.gutters.slice(0); + options.gutters.splice(i, 1); } - if (!found && options.lineNumbers) - options.gutters.push("CodeMirror-linenumbers"); } // SCROLLBARS From c8c7fc18af5f025733a930fea202d98e54e12a5e Mon Sep 17 00:00:00 2001 From: Marijn Haverbeke Date: Fri, 6 Sep 2013 11:56:51 +0200 Subject: [PATCH 1485/5780] Prevent corrupted measurements due to vanishing/appearing scrollbar Issue #1787 --- lib/codemirror.js | 8 +++++--- 1 file changed, 5 insertions(+), 3 deletions(-) diff --git a/lib/codemirror.js b/lib/codemirror.js index 740389fc03..b4b5549ee0 100644 --- a/lib/codemirror.js +++ b/lib/codemirror.js @@ -355,6 +355,7 @@ window.CodeMirror = (function() { if (mac_geLion && scrollbarWidth(d.measure) === 0) d.scrollbarV.style.minWidth = d.scrollbarH.style.minHeight = mac_geMountainLion ? "18px" : "12px"; + return needsV; } function visibleLines(display, doc, viewPort) { @@ -410,11 +411,13 @@ window.CodeMirror = (function() { var oldFrom = cm.display.showingFrom, oldTo = cm.display.showingTo, updated; var visible = visibleLines(cm.display, cm.doc, viewPort); for (;;) { + var hadVScroll = cm.display.scroller.scrollHeight > cm.display.scroller.clientHeight + 1; if (!updateDisplayInner(cm, changes, visible, forced)) break; - forced = false; updated = true; + changes = []; updateSelection(cm); - updateScrollbars(cm); + if (updateScrollbars(cm) != hadVScroll) continue; + forced = false; // Clip forced viewport to actual scrollable area if (viewPort) @@ -423,7 +426,6 @@ window.CodeMirror = (function() { visible = visibleLines(cm.display, cm.doc, viewPort); if (visible.from >= cm.display.showingFrom && visible.to <= cm.display.showingTo) break; - changes = []; } if (updated) { From 7ea6f43856685f6d7ae71d3344cb2a1bdb1ab037 Mon Sep 17 00:00:00 2001 From: Marijn Haverbeke Date: Mon, 9 Sep 2013 10:32:32 +0200 Subject: [PATCH 1486/5780] Amend c8c7fc18af5f to handle window resize-tiggered updated Issue #1787 --- lib/codemirror.js | 9 ++++++--- 1 file changed, 6 insertions(+), 3 deletions(-) diff --git a/lib/codemirror.js b/lib/codemirror.js index b4b5549ee0..8b6377dc50 100644 --- a/lib/codemirror.js +++ b/lib/codemirror.js @@ -355,7 +355,6 @@ window.CodeMirror = (function() { if (mac_geLion && scrollbarWidth(d.measure) === 0) d.scrollbarV.style.minWidth = d.scrollbarH.style.minHeight = mac_geMountainLion ? "18px" : "12px"; - return needsV; } function visibleLines(display, doc, viewPort) { @@ -411,12 +410,16 @@ window.CodeMirror = (function() { var oldFrom = cm.display.showingFrom, oldTo = cm.display.showingTo, updated; var visible = visibleLines(cm.display, cm.doc, viewPort); for (;;) { - var hadVScroll = cm.display.scroller.scrollHeight > cm.display.scroller.clientHeight + 1; + var oldWidth = cm.display.scroller.clientWidth; if (!updateDisplayInner(cm, changes, visible, forced)) break; updated = true; changes = []; updateSelection(cm); - if (updateScrollbars(cm) != hadVScroll) continue; + updateScrollbars(cm); + if (cm.options.lineWrapping && oldWidth != cm.display.scroller.clientWidth) { + forced = true; + continue; + } forced = false; // Clip forced viewport to actual scrollable area From 007434607dbd4faf5c7d4c99a836e989fe8b20f5 Mon Sep 17 00:00:00 2001 From: Peter Kroon Date: Mon, 9 Sep 2013 10:38:16 +0200 Subject: [PATCH 1487/5780] [dtd mode] Add --- doc/compress.html | 1 + mode/dtd/dtd.js | 127 ++++++++++++++++++++++++++++++++++++++++++++ mode/dtd/index.html | 89 +++++++++++++++++++++++++++++++ mode/index.html | 1 + 4 files changed, 218 insertions(+) create mode 100644 mode/dtd/dtd.js create mode 100644 mode/dtd/index.html diff --git a/doc/compress.html b/doc/compress.html index 5ee78bb4c5..d49c193263 100644 --- a/doc/compress.html +++ b/doc/compress.html @@ -86,6 +86,7 @@

    Script compression helper

    + diff --git a/mode/dtd/dtd.js b/mode/dtd/dtd.js new file mode 100644 index 0000000000..7033bf0f8b --- /dev/null +++ b/mode/dtd/dtd.js @@ -0,0 +1,127 @@ +/* + DTD mode + Ported to CodeMirror by Peter Kroon + Report bugs/issues here: https://github.com/marijnh/CodeMirror/issues + GitHub: @peterkroon +*/ + +CodeMirror.defineMode("dtd", function(config) { + var indentUnit = config.indentUnit, type; + function ret(style, tp) {type = tp; return style;} + + function tokenBase(stream, state) { + var ch = stream.next(); + + if (ch == "<" && stream.eat("!") ) { + if (stream.eatWhile(/[\-]/)) { + state.tokenize = tokenSGMLComment; + return tokenSGMLComment(stream, state); + } else if (stream.eatWhile(/[\w]/)) return ret("keyword", "doindent"); + } else if (ch == "<" && stream.eat("?")) { //xml declaration + state.tokenize = inBlock("meta", "?>"); + return ret("meta", ch); + } else if (ch == "#" && stream.eatWhile(/[\w]/)) return ret("atom", "tag"); + else if (ch == "|") return ret("keyword", "seperator"); + else if (ch.match(/[\(\)\[\]\-\.,\+\?>]/)) return ret(null, ch);//if(ch === ">") return ret(null, "endtag"); else + else if (ch.match(/[\[\]]/)) return ret("rule", ch); + else if (ch == "\"" || ch == "'") { + state.tokenize = tokenString(ch); + return state.tokenize(stream, state); + } else if (stream.eatWhile(/[a-zA-Z\?\+\d]/)) { + var sc = stream.current(); + if( sc.substr(sc.length-1,sc.length).match(/\?|\+/) !== null )stream.backUp(1); + return ret("tag", "tag"); + } else if (ch == "%" || ch == "*" ) return ret("number", "number"); + else { + stream.eatWhile(/[\w\\\-_%.{,]/); + return ret(null, null); + } + } + + function tokenSGMLComment(stream, state) { + var dashes = 0, ch; + while ((ch = stream.next()) != null) { + if (dashes >= 2 && ch == ">") { + state.tokenize = tokenBase; + break; + } + dashes = (ch == "-") ? dashes + 1 : 0; + } + return ret("comment", "comment"); + } + + function tokenString(quote) { + return function(stream, state) { + var escaped = false, ch; + while ((ch = stream.next()) != null) { + if (ch == quote && !escaped) { + state.tokenize = tokenBase; + break; + } + escaped = !escaped && ch == "\\"; + } + return ret("string", "tag"); + }; + } + + function inBlock(style, terminator) { + return function(stream, state) { + while (!stream.eol()) { + if (stream.match(terminator)) { + state.tokenize = tokenBase; + break; + } + stream.next(); + } + return style; + }; + } + + return { + startState: function(base) { + return {tokenize: tokenBase, + baseIndent: base || 0, + stack: []}; + }, + + token: function(stream, state) { + if (stream.eatSpace()) return null; + var style = state.tokenize(stream, state); + + var context = state.stack[state.stack.length-1]; + if (stream.current() == "[" || type === "doindent" || type == "[") state.stack.push("rule"); + else if (type === "endtag") state.stack[state.stack.length-1] = "endtag"; + else if (stream.current() == "]" || type == "]" || (type == ">" && context == "rule")) state.stack.pop(); + else if (type == "[") state.stack.push("["); + return style; + }, + + indent: function(state, textAfter) { + var n = state.stack.length; + + if( textAfter.match(/\]\s+|\]/) )n=n-1; + else if(textAfter.substr(textAfter.length-1, textAfter.length) === ">"){ + if(textAfter.substr(0,1) === "<")n; + else if( type == "doindent" && textAfter.length > 1 )n; + else if( type == "doindent")n--; + else if( type == ">" && textAfter.length > 1)n; + else if( type == "tag" && textAfter !== ">")n; + else if( type == "tag" && state.stack[state.stack.length-1] == "rule")n--; + else if( type == "tag")n++; + else if( textAfter === ">" && state.stack[state.stack.length-1] == "rule" && type === ">")n--; + else if( textAfter === ">" && state.stack[state.stack.length-1] == "rule")n; + else if( textAfter.substr(0,1) !== "<" && textAfter.substr(0,1) === ">" )n=n-1; + else if( textAfter === ">")n; + else n=n-1; + //over rule them all + if(type == null || type == "]")n--; + } + + return state.baseIndent + n * indentUnit; + }, + + electricChars: "]>" + }; +}); + +CodeMirror.defineMIME("application/xml-dtd", "dtd"); diff --git a/mode/dtd/index.html b/mode/dtd/index.html new file mode 100644 index 0000000000..076d827f7f --- /dev/null +++ b/mode/dtd/index.html @@ -0,0 +1,89 @@ + + +CodeMirror: DTD mode + + + + + + + + + +
    +

    DTD mode

    +
    + + +

    MIME types defined: application/xml-dtd.

    +
    diff --git a/mode/index.html b/mode/index.html index 5601a2c458..2b54d60689 100644 --- a/mode/index.html +++ b/mode/index.html @@ -41,6 +41,7 @@

    Language modes

  • Cython
  • D
  • diff
  • +
  • DTD
  • ECL
  • Erlang
  • Gas (AT&T-style assembly)
  • From 1564808cf8d9c3fc0a66c4e95a53962312f6e12c Mon Sep 17 00:00:00 2001 From: Marijn Haverbeke Date: Mon, 9 Sep 2013 10:46:14 +0200 Subject: [PATCH 1488/5780] Fix drag/drop on Chrome by setting an image src Work around a Chrome regression where it aborts the drag if the setDragImage image is src-less. Closes #1810 --- lib/codemirror.js | 1 + 1 file changed, 1 insertion(+) diff --git a/lib/codemirror.js b/lib/codemirror.js index 8b6377dc50..ce83709954 100644 --- a/lib/codemirror.js +++ b/lib/codemirror.js @@ -1912,6 +1912,7 @@ window.CodeMirror = (function() { // Recent Safari (~6.0.2) have a tendency to segfault when this happens, so we don't do it there. if (e.dataTransfer.setDragImage && !safari) { var img = elt("img", null, null, "position: fixed; left: 0; top: 0;"); + img.src = "data:image/gif;base64,R0lGODlhAQABAAAAACH5BAEKAAEALAAAAAABAAEAAAICTAEAOw=="; if (opera) { img.width = img.height = 1; cm.display.wrapper.appendChild(img); From 687026e00a0809dd8a836095c3d760ddc4cc1a08 Mon Sep 17 00:00:00 2001 From: Marijn Haverbeke Date: Mon, 9 Sep 2013 11:16:55 +0200 Subject: [PATCH 1489/5780] [tern addon] Compensate for fact that call start pos is a column, not char offset Closes #1811 --- addon/tern/tern.js | 15 ++++++++++++--- 1 file changed, 12 insertions(+), 3 deletions(-) diff --git a/addon/tern/tern.js b/addon/tern/tern.js index 4348c8d8e8..8d35f0630b 100644 --- a/addon/tern/tern.js +++ b/addon/tern/tern.js @@ -239,9 +239,18 @@ var lex = inner.state.lexical; if (lex.info != "call") return; - var ch = lex.column, pos = lex.pos || 0; - for (var line = cm.getCursor().line, e = Math.max(0, line - 9), found = false; line >= e; --line) - if (cm.getLine(line).charAt(ch) == "(") {found = true; break;} + var ch, pos = lex.pos || 0, tabSize = cm.getOption("tabSize"); + for (var line = cm.getCursor().line, e = Math.max(0, line - 9), found = false; line >= e; --line) { + var str = cm.getLine(line), extra = 0; + for (var pos = 0;;) { + var tab = str.indexOf("\t", pos); + if (tab == -1) break; + extra += tabSize - (tab + extra) % tabSize - 1; + pos = tab + 1; + } + ch = lex.column - extra; + if (str.charAt(ch) == "(") {found = true; break;} + } if (!found) return; var start = Pos(line, ch); From e0b0e32ed97bdd9b4dcb559f0d383bbb197961e4 Mon Sep 17 00:00:00 2001 From: Marijn Haverbeke Date: Mon, 9 Sep 2013 12:32:03 +0200 Subject: [PATCH 1490/5780] Allow styling of lines from mode tokenizers (experimental) --- lib/codemirror.js | 37 +++++++++++++++++++++++++------------ test/test.js | 19 +++++++++++++++++++ 2 files changed, 44 insertions(+), 12 deletions(-) diff --git a/lib/codemirror.js b/lib/codemirror.js index ce83709954..a14ce5358f 100644 --- a/lib/codemirror.js +++ b/lib/codemirror.js @@ -666,10 +666,11 @@ window.CodeMirror = (function() { } function buildLineElement(cm, line, lineNo, dims, reuse) { - var lineElement = lineContent(cm, line); + var built = buildLineContent(cm, line), lineElement = built.pre; var markers = line.gutterMarkers, display = cm.display, wrap; - if (!cm.options.lineNumbers && !markers && !line.bgClass && !line.wrapClass && !line.widgets) + var bgClass = built.bgClass ? built.bgClass + " " + (line.bgClass || "") : line.bgClass; + if (!cm.options.lineNumbers && !markers && !bgClass && !line.wrapClass && !line.widgets) return lineElement; // Lines with gutter elements, widgets or a background class need @@ -707,8 +708,8 @@ window.CodeMirror = (function() { wrap.appendChild(lineElement); } // Kludge to make sure the styled element lies behind the selection (by z-index) - if (line.bgClass) - wrap.insertBefore(elt("div", null, line.bgClass + " CodeMirror-linebackground"), wrap.firstChild); + if (bgClass) + wrap.insertBefore(elt("div", null, bgClass + " CodeMirror-linebackground"), wrap.firstChild); if (cm.options.lineNumbers || markers) { var gutterWrap = wrap.insertBefore(elt("div", null, null, "position: absolute; left: " + (cm.options.fixedGutter ? dims.fixedPos : -dims.gutterTotalWidth) + "px"), @@ -1031,7 +1032,7 @@ window.CodeMirror = (function() { return crudelyMeasureLine(cm, line); var display = cm.display, measure = emptyArray(line.text.length); - var pre = lineContent(cm, line, measure, true); + var pre = buildLineContent(cm, line, measure, true).pre; // IE does not cache element positions of inline elements between // calls to getBoundingClientRect. This makes the loop below, @@ -1135,7 +1136,7 @@ window.CodeMirror = (function() { if (cached || line.text.length >= cm.options.crudeMeasuringFrom) return measureChar(cm, line, line.text.length, cached && cached.measure, "right").right; - var pre = lineContent(cm, line, null, true); + var pre = buildLineContent(cm, line, null, true).pre; var end = pre.appendChild(zeroWidthElement(cm.display.measure)); removeChildrenAndAdd(cm.display.measure, pre); return getRect(end).right - getRect(cm.display.lineDiv).left; @@ -4314,13 +4315,23 @@ window.CodeMirror = (function() { } var styleToClassCache = {}; - function styleToClass(style) { + function interpretTokenStyle(style, builder) { if (!style) return null; + for (;;) { + var lineClass = style.match(/(?:^|\s)line-(background-)?(\S+)/); + if (!lineClass) break; + style = style.slice(0, lineClass.index) + style.slice(lineClass.index + lineClass[0].length); + var prop = lineClass[1] ? "bgClass" : "textClass"; + if (builder[prop] == null) + builder[prop] = lineClass[2]; + else if (!(new RegExp("(?:^|\s)" + lineClass[2] + "(?:$|\s)")).test(builder[prop])) + builder[prop] += " " + lineClass[2]; + } return styleToClassCache[style] || (styleToClassCache[style] = "cm-" + style.replace(/ +/g, " cm-")); } - function lineContent(cm, realLine, measure, copyWidgets) { + function buildLineContent(cm, realLine, measure, copyWidgets) { var merged, line = realLine, empty = true; while (merged = collapsedSpanAtStart(line)) line = getLine(cm.doc, merged.find().from.line); @@ -4328,7 +4339,6 @@ window.CodeMirror = (function() { var builder = {pre: elt("pre"), col: 0, pos: 0, measure: null, measuredSomething: false, cm: cm, copyWidgets: copyWidgets}; - if (line.textClass) builder.pre.className = line.textClass; do { if (line.text) empty = false; @@ -4365,8 +4375,11 @@ window.CodeMirror = (function() { } } + var textClass = builder.textClass ? builder.textClass + " " + (realLine.textClass || "") : realLine.textClass; + if (textClass) builder.pre.className = textClass; + signal(cm, "renderLine", cm, realLine, builder.pre); - return builder.pre; + return builder; } var tokenSpecialChars = /[\t\u0000-\u0019\u00ad\u200b\u2028\u2029\uFEFF]/g; @@ -4477,7 +4490,7 @@ window.CodeMirror = (function() { var spans = line.markedSpans, allText = line.text, at = 0; if (!spans) { for (var i = 1; i < styles.length; i+=2) - builder.addToken(builder, allText.slice(at, at = styles[i]), styleToClass(styles[i+1])); + builder.addToken(builder, allText.slice(at, at = styles[i]), interpretTokenStyle(styles[i+1], builder)); return; } @@ -4527,7 +4540,7 @@ window.CodeMirror = (function() { spanStartStyle = ""; } text = allText.slice(at, at = styles[i++]); - style = styleToClass(styles[i++]); + style = interpretTokenStyle(styles[i++], builder); } } } diff --git a/test/test.js b/test/test.js index 21e09cb12e..a05b8afb42 100644 --- a/test/test.js +++ b/test/test.js @@ -1539,3 +1539,22 @@ testCM("change_removedText", function(cm) { eq(removedText[0].join("\n"), "abc\nd"); eq(removedText[1].join("\n"), ""); }); + +testCM("lineStyleFromMode", function(cm) { + CodeMirror.defineMode("test_mode", function() { + return {token: function(stream) { + if (stream.match(/^\[[^\]]*\]/)) return "line-brackets"; + if (stream.match(/^\([^\]]*\)/)) return "line-background-parens"; + stream.match(/^\s+|^\S+/); + }}; + }); + cm.setOption("mode", "test_mode"); + var bracketElts = byClassName(cm.getWrapperElement(), "brackets"); + eq(bracketElts.length, 1); + eq(bracketElts[0].nodeName, "PRE"); + is(!/brackets.*brackets/.test(bracketElts[0].className)); + var parenElts = byClassName(cm.getWrapperElement(), "parens"); + eq(parenElts.length, 1); + eq(parenElts[0].nodeName, "DIV"); + is(!/parens.*parens/.test(parenElts[0].className)); +}, {value: "line1: [br] [br]\nline2: (par) (par)\nline3: nothing"}); From 45d3cda933484698bfc0a8074cca717ffd2bc8eb Mon Sep 17 00:00:00 2001 From: stoskov Date: Mon, 9 Sep 2013 17:52:00 +0300 Subject: [PATCH 1491/5780] Add a hook to server response --- addon/tern/tern.js | 15 +++++++++++++-- 1 file changed, 13 insertions(+), 2 deletions(-) diff --git a/addon/tern/tern.js b/addon/tern/tern.js index 8d35f0630b..5084539993 100644 --- a/addon/tern/tern.js +++ b/addon/tern/tern.js @@ -24,6 +24,9 @@ // no tip should be shown. By default the docstring is shown. // * typeTip: Like completionTip, but for the tooltips shown for type // queries. +// * responseFilter: A function(doc, query, request, error, data) that +// will be applied to the Tern responses before treating them +// // // It is possible to run the Tern server in a web worker by specifying // these additional options: @@ -102,8 +105,16 @@ rename: function(cm) { rename(this, cm); }, - request: function(cm, query, c) { - this.server.request(buildRequest(this, findDoc(this, cm.getDoc()), query), c); + request: function (cm, query, c) { + var self = this; + var doc = findDoc(this, cm.getDoc()); + var request = buildRequest(this, doc, query); + + this.server.request(request, function (error, data) { + if (!error && self.options.responseFilter) + data = self.options.responseFilter(doc, query, request, error, data); + c(error, data); + }); } }; From 1b3f9d090503ddb6727356224c42db3a2664e088 Mon Sep 17 00:00:00 2001 From: Peter Kroon Date: Tue, 10 Sep 2013 18:58:06 +0200 Subject: [PATCH 1492/5780] [lint demo] Remove duplicate script tag --- demo/lint.html | 1 - 1 file changed, 1 deletion(-) diff --git a/demo/lint.html b/demo/lint.html index 05236150ef..8372c52c47 100644 --- a/demo/lint.html +++ b/demo/lint.html @@ -13,7 +13,6 @@ - From 6dc51c9ab3d0ba68ad794ed4971a7efc46aebc3d Mon Sep 17 00:00:00 2001 From: Brett Zamir Date: Wed, 11 Sep 2013 13:50:04 +0800 Subject: [PATCH 1493/5780] [lint demo] missing semicolon --- demo/complete.html | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/demo/complete.html b/demo/complete.html index 56999b9cc9..e256a908b4 100644 --- a/demo/complete.html +++ b/demo/complete.html @@ -71,7 +71,7 @@

    Autocomplete Demo

    + From 7579444498ba0db844f4921ee59a5dbaed9f93d0 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Mart=C3=ADn=20Gait=C3=A1n?= Date: Tue, 10 Sep 2013 16:20:42 -0300 Subject: [PATCH 1498/5780] [fortran mode] Add --- doc/compress.html | 1 + mode/fortran/fortran.js | 173 ++++++++++++++++++++++++++++++++++++++++ mode/fortran/index.html | 81 +++++++++++++++++++ mode/index.html | 1 + 4 files changed, 256 insertions(+) create mode 100644 mode/fortran/fortran.js create mode 100644 mode/fortran/index.html diff --git a/doc/compress.html b/doc/compress.html index d49c193263..8548991f90 100644 --- a/doc/compress.html +++ b/doc/compress.html @@ -89,6 +89,7 @@

    Script compression helper

    + diff --git a/mode/fortran/fortran.js b/mode/fortran/fortran.js new file mode 100644 index 0000000000..83fd8fde85 --- /dev/null +++ b/mode/fortran/fortran.js @@ -0,0 +1,173 @@ +CodeMirror.defineMode("fortran", function() { + function words(array) { + var keys = {}; + for (var i = 0; i < array.length; ++i) { + keys[array[i]] = true; + } + return keys; + } + + var keywords = words([ + "abstract", "accept", "allocatable", "allocate", + "array", "assign", "asynchronous", "backspace", + "bind", "block", "byte", "call", "case", + "class", "close", "common", "contains", + "continue", "cycle", "data", "deallocate", + "decode", "deferred", "dimension", "do", + "elemental", "else", "encode", "end", + "endif", "entry", "enumerator", "equivalence", + "exit", "external", "extrinsic", "final", + "forall", "format", "function", "generic", + "go", "goto", "if", "implicit", "import", "include", + "inquire", "intent", "interface", "intrinsic", + "module", "namelist", "non_intrinsic", + "non_overridable", "none", "nopass", + "nullify", "open", "optional", "options", + "parameter", "pass", "pause", "pointer", + "print", "private", "program", "protected", + "public", "pure", "read", "recursive", "result", + "return", "rewind", "save", "select", "sequence", + "stop", "subroutine", "target", "then", "to", "type", + "use", "value", "volatile", "where", "while", + "write"]); + var builtins = words(["abort", "abs", "access", "achar", "acos", + "adjustl", "adjustr", "aimag", "aint", "alarm", + "all", "allocated", "alog", "amax", "amin", + "amod", "and", "anint", "any", "asin", + "associated", "atan", "besj", "besjn", "besy", + "besyn", "bit_size", "btest", "cabs", "ccos", + "ceiling", "cexp", "char", "chdir", "chmod", + "clog", "cmplx", "command_argument_count", + "complex", "conjg", "cos", "cosh", "count", + "cpu_time", "cshift", "csin", "csqrt", "ctime", + "c_funloc", "c_loc", "c_associated", "c_null_ptr", + "c_null_funptr", "c_f_pointer", "c_null_char", + "c_alert", "c_backspace", "c_form_feed", + "c_new_line", "c_carriage_return", + "c_horizontal_tab", "c_vertical_tab", "dabs", + "dacos", "dasin", "datan", "date_and_time", + "dbesj", "dbesj", "dbesjn", "dbesy", "dbesy", + "dbesyn", "dble", "dcos", "dcosh", "ddim", "derf", + "derfc", "dexp", "digits", "dim", "dint", "dlog", + "dlog", "dmax", "dmin", "dmod", "dnint", + "dot_product", "dprod", "dsign", "dsinh", + "dsin", "dsqrt", "dtanh", "dtan", "dtime", + "eoshift", "epsilon", "erf", "erfc", "etime", + "exit", "exp", "exponent", "extends_type_of", + "fdate", "fget", "fgetc", "float", "floor", + "flush", "fnum", "fputc", "fput", "fraction", + "fseek", "fstat", "ftell", "gerror", "getarg", + "get_command", "get_command_argument", + "get_environment_variable", "getcwd", + "getenv", "getgid", "getlog", "getpid", + "getuid", "gmtime", "hostnm", "huge", "iabs", + "iachar", "iand", "iargc", "ibclr", "ibits", + "ibset", "ichar", "idate", "idim", "idint", + "idnint", "ieor", "ierrno", "ifix", "imag", + "imagpart", "index", "int", "ior", "irand", + "isatty", "ishft", "ishftc", "isign", + "iso_c_binding", "is_iostat_end", "is_iostat_eor", + "itime", "kill", "kind", "lbound", "len", "len_trim", + "lge", "lgt", "link", "lle", "llt", "lnblnk", "loc", + "log", "logical", "long", "lshift", "lstat", "ltime", + "matmul", "max", "maxexponent", "maxloc", "maxval", + "mclock", "merge", "move_alloc", "min", "minexponent", + "minloc", "minval", "mod", "modulo", "mvbits", + "nearest", "new_line", "nint", "not", "or", "pack", + "perror", "precision", "present", "product", "radix", + "rand", "random_number", "random_seed", "range", + "real", "realpart", "rename", "repeat", "reshape", + "rrspacing", "rshift", "same_type_as", "scale", + "scan", "second", "selected_int_kind", + "selected_real_kind", "set_exponent", "shape", + "short", "sign", "signal", "sinh", "sin", "sleep", + "sngl", "spacing", "spread", "sqrt", "srand", "stat", + "sum", "symlnk", "system", "system_clock", "tan", + "tanh", "time", "tiny", "transfer", "transpose", + "trim", "ttynam", "ubound", "umask", "unlink", + "unpack", "verify", "xor", "zabs", "zcos", "zexp", + "zlog", "zsin", "zsqrt"]); + + var dataTypes = words(["c_bool", "c_char", "c_double", "c_double_complex", + "c_float", "c_float_complex", "c_funptr", "c_int", + "c_int16_t", "c_int32_t", "c_int64_t", "c_int8_t", + "c_int_fast16_t", "c_int_fast32_t", "c_int_fast64_t", + "c_int_fast8_t", "c_int_least16_t", "c_int_least32_t", + "c_int_least64_t", "c_int_least8_t", "c_intmax_t", + "c_intptr_t", "c_long", "c_long_double", + "c_long_double_complex", "c_long_long", "c_ptr", + "c_short", "c_signed_char", "c_size_t", "character", + "complex", "double", "integer", "logical", "real"]); + var isOperatorChar = /[+\-*&=<>\/\:]/; + var litOperator = new RegExp("(\.and\.|\.or\.|\.eq\.|\.lt\.|\.le\.|\.gt\.|\.ge\.|\.ne\.|\.not\.|\.eqv\.|\.neqv\.)", "i"); + + function tokenBase(stream, state) { + + if (stream.match(litOperator)){ + return 'operator'; + } + + var ch = stream.next(); + if (ch == "!") { + stream.skipToEnd(); + return "comment"; + } + if (ch == '"' || ch == "'") { + state.tokenize = tokenString(ch); + return state.tokenize(stream, state); + } + if (/[\[\]\(\),]/.test(ch)) { + return null; + } + if (/\d/.test(ch)) { + stream.eatWhile(/[\w\.]/); + return "number"; + } + if (isOperatorChar.test(ch)) { + stream.eatWhile(isOperatorChar); + return "operator"; + } + stream.eatWhile(/[\w\$_]/); + var word = stream.current().toLowerCase(); + + if (keywords.hasOwnProperty(word)){ + return 'keyword'; + } + if (builtins.hasOwnProperty(word) || dataTypes.hasOwnProperty(word)) { + return 'builtin'; + } + return "variable"; + } + + function tokenString(quote) { + return function(stream, state) { + var escaped = false, next, end = false; + while ((next = stream.next()) != null) { + if (next == quote && !escaped) { + end = true; + break; + } + escaped = !escaped && next == "\\"; + } + if (end || !escaped) state.tokenize = null; + return "string"; + }; + } + + // Interface + + return { + startState: function() { + return {tokenize: null}; + }, + + token: function(stream, state) { + if (stream.eatSpace()) return null; + var style = (state.tokenize || tokenBase)(stream, state); + if (style == "comment" || style == "meta") return style; + return style; + } + }; +}); + +CodeMirror.defineMIME("text/x-fortran", "fortran"); diff --git a/mode/fortran/index.html b/mode/fortran/index.html new file mode 100644 index 0000000000..efa55ec89a --- /dev/null +++ b/mode/fortran/index.html @@ -0,0 +1,81 @@ + + +CodeMirror: Fortran mode + + + + + + + + + +
    +

    Fortran mode

    + + +
    + + + +

    MIME types defined: text/x-Fortran.

    +
    diff --git a/mode/index.html b/mode/index.html index 2b54d60689..f2bba985c2 100644 --- a/mode/index.html +++ b/mode/index.html @@ -44,6 +44,7 @@

    Language modes

  • DTD
  • ECL
  • Erlang
  • +
  • Fortran
  • Gas (AT&T-style assembly)
  • Go
  • Groovy
  • From afd9b56612b6326706631e363874c731da9debd7 Mon Sep 17 00:00:00 2001 From: Marijn Haverbeke Date: Mon, 16 Sep 2013 14:56:23 +0200 Subject: [PATCH 1499/5780] Fix false positive in IE9/10 selection reset workaround hack Closes #1820 --- lib/codemirror.js | 5 ++++- 1 file changed, 4 insertions(+), 1 deletion(-) diff --git a/lib/codemirror.js b/lib/codemirror.js index a14ce5358f..10545b9573 100644 --- a/lib/codemirror.js +++ b/lib/codemirror.js @@ -1605,7 +1605,10 @@ window.CodeMirror = (function() { if (signalDOMEvent(cm, e) || cm.options.onKeyEvent && cm.options.onKeyEvent(cm, addStop(e))) return; if (e.keyCode == 16) cm.doc.sel.shift = false; })); - on(d.input, "input", bind(fastPoll, cm)); + on(d.input, "input", function() { + if (ie && !ie_lt9 && cm.display.inputHasSelection) cm.display.inputHasSelection = null; + fastPoll(cm); + }); on(d.input, "keydown", operation(cm, onKeyDown)); on(d.input, "keypress", operation(cm, onKeyPress)); on(d.input, "focus", bind(onFocus, cm)); From e470d7f4ad2e3bc60ece03e43319d3d70e15337e Mon Sep 17 00:00:00 2001 From: Tomas-A Date: Thu, 12 Sep 2013 16:53:15 +0100 Subject: [PATCH 1500/5780] [searchcursor addon] Fix multiple-line search There must be an error in line 73 because if the phrase starts at the same character position then conditional if will always become true (same number is equal to same number) therefore it must be changed to strict less than. --- addon/search/searchcursor.js | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/addon/search/searchcursor.js b/addon/search/searchcursor.js index 3da3f04e8f..c034d5865b 100644 --- a/addon/search/searchcursor.js +++ b/addon/search/searchcursor.js @@ -69,8 +69,8 @@ this.matches = function(reverse, pos) { var ln = pos.line, idx = (reverse ? target.length - 1 : 0), match = target[idx], line = fold(doc.getLine(ln)); var offsetA = (reverse ? line.indexOf(match) + match.length : line.lastIndexOf(match)); - if (reverse ? offsetA >= pos.ch || offsetA != match.length - : offsetA <= pos.ch || offsetA != line.length - match.length) + if (reverse ? offsetA > pos.ch || offsetA != match.length + : offsetA < pos.ch || offsetA != line.length - match.length) return; for (;;) { if (reverse ? !ln : ln == doc.lineCount() - 1) return; From 5cd7bdcaa1498c30e6dde4bd72674345ce8e3e12 Mon Sep 17 00:00:00 2001 From: Marijn Haverbeke Date: Mon, 16 Sep 2013 15:16:11 +0200 Subject: [PATCH 1501/5780] [sql mode] Merge functions and builtin list for plsql dialect Closes #1803 --- mode/sql/sql.js | 7 +++---- 1 file changed, 3 insertions(+), 4 deletions(-) diff --git a/mode/sql/sql.js b/mode/sql/sql.js index e9dcb7fd75..63ce3fa843 100644 --- a/mode/sql/sql.js +++ b/mode/sql/sql.js @@ -266,7 +266,7 @@ CodeMirror.defineMode("sql", function(config, parserConfig) { name: "sql", client: set("charset clear connect edit ego exit go help nopager notee nowarning pager print prompt quit rehash source status system tee"), keywords: set(sqlKeywords + "accessible action add after algorithm all analyze asensitive at authors auto_increment autocommit avg avg_row_length before binary binlog both btree cache call cascade cascaded case catalog_name chain change changed character check checkpoint checksum class_origin client_statistics close coalesce code collate collation collations column columns comment commit committed completion concurrent condition connection consistent constraint contains continue contributors convert cross current_date current_time current_timestamp current_user cursor data database databases day_hour day_microsecond day_minute day_second deallocate dec declare default delay_key_write delayed delimiter des_key_file describe deterministic dev_pop dev_samp deviance directory disable discard distinctrow div dual dumpfile each elseif enable enclosed end ends engine engines enum errors escape escaped even event events every execute exists exit explain extended fast fetch field fields first flush for force foreign found_rows full fulltext function general global grant grants group groupby_concat handler hash help high_priority hosts hour_microsecond hour_minute hour_second if ignore ignore_server_ids import index index_statistics infile inner innodb inout insensitive insert_method install interval invoker isolation iterate key keys kill language last leading leave left level limit linear lines list load local localtime localtimestamp lock logs low_priority master master_heartbeat_period master_ssl_verify_server_cert masters match max max_rows maxvalue message_text middleint migrate min min_rows minute_microsecond minute_second mod mode modifies modify mutex mysql_errno natural next no no_write_to_binlog offline offset one online open optimize option optionally out outer outfile pack_keys parser partition partitions password phase plugin plugins prepare preserve prev primary privileges procedure processlist profile profiles purge query quick range read read_write reads real rebuild recover references regexp relaylog release remove rename reorganize repair repeatable replace require resignal restrict resume return returns revoke right rlike rollback rollup row row_format rtree savepoint schedule schema schema_name schemas second_microsecond security sensitive separator serializable server session share show signal slave slow smallint snapshot soname spatial specific sql sql_big_result sql_buffer_result sql_cache sql_calc_found_rows sql_no_cache sql_small_result sqlexception sqlstate sqlwarning ssl start starting starts status std stddev stddev_pop stddev_samp storage straight_join subclass_origin sum suspend table_name table_statistics tables tablespace temporary terminated to trailing transaction trigger triggers truncate uncommitted undo uninstall unique unlock upgrade usage use use_frm user user_resources user_statistics using utc_date utc_time utc_timestamp value variables varying view views warnings when while with work write xa xor year_month zerofill begin do then else loop repeat"), - builtin: set("bool boolean bit blob decimal double enum float long longblob longtext medium mediumblob mediumint mediumtext time timestamp tinyblob tinyint tinytext text bigint int int1 int2 int3 int4 int8 integer float float4 float8 double char varbinary varchar varcharacter precision date datetime year unsigned signed numeric"), + builtin: set("bool boolean bit blob decimal double float long longblob longtext medium mediumblob mediumint mediumtext time timestamp tinyblob tinyint tinytext text bigint int int1 int2 int3 int4 int8 integer float float4 float8 double char varbinary varchar varcharacter precision date datetime year unsigned signed numeric"), atoms: set("false true null unknown"), operatorChars: /^[*+\-%<>!=&|^]/, dateSQL: set("date time timestamp"), @@ -282,7 +282,7 @@ CodeMirror.defineMode("sql", function(config, parserConfig) { name: "sql", client: set("charset clear connect edit ego exit go help nopager notee nowarning pager print prompt quit rehash source status system tee"), keywords: set(sqlKeywords + "accessible action add after algorithm all always analyze asensitive at authors auto_increment autocommit avg avg_row_length before binary binlog both btree cache call cascade cascaded case catalog_name chain change changed character check checkpoint checksum class_origin client_statistics close coalesce code collate collation collations column columns comment commit committed completion concurrent condition connection consistent constraint contains continue contributors convert cross current_date current_time current_timestamp current_user cursor data database databases day_hour day_microsecond day_minute day_second deallocate dec declare default delay_key_write delayed delimiter des_key_file describe deterministic dev_pop dev_samp deviance directory disable discard distinctrow div dual dumpfile each elseif enable enclosed end ends engine engines enum errors escape escaped even event events every execute exists exit explain extended fast fetch field fields first flush for force foreign found_rows full fulltext function general generated global grant grants group groupby_concat handler hard hash help high_priority hosts hour_microsecond hour_minute hour_second if ignore ignore_server_ids import index index_statistics infile inner innodb inout insensitive insert_method install interval invoker isolation iterate key keys kill language last leading leave left level limit linear lines list load local localtime localtimestamp lock logs low_priority master master_heartbeat_period master_ssl_verify_server_cert masters match max max_rows maxvalue message_text middleint migrate min min_rows minute_microsecond minute_second mod mode modifies modify mutex mysql_errno natural next no no_write_to_binlog offline offset one online open optimize option optionally out outer outfile pack_keys parser partition partitions password persistent phase plugin plugins prepare preserve prev primary privileges procedure processlist profile profiles purge query quick range read read_write reads real rebuild recover references regexp relaylog release remove rename reorganize repair repeatable replace require resignal restrict resume return returns revoke right rlike rollback rollup row row_format rtree savepoint schedule schema schema_name schemas second_microsecond security sensitive separator serializable server session share show signal slave slow smallint snapshot soft soname spatial specific sql sql_big_result sql_buffer_result sql_cache sql_calc_found_rows sql_no_cache sql_small_result sqlexception sqlstate sqlwarning ssl start starting starts status std stddev stddev_pop stddev_samp storage straight_join subclass_origin sum suspend table_name table_statistics tables tablespace temporary terminated to trailing transaction trigger triggers truncate uncommitted undo uninstall unique unlock upgrade usage use use_frm user user_resources user_statistics using utc_date utc_time utc_timestamp value variables varying view views virtual warnings when while with work write xa xor year_month zerofill begin do then else loop repeat"), - builtin: set("bool boolean bit blob decimal double enum float long longblob longtext medium mediumblob mediumint mediumtext time timestamp tinyblob tinyint tinytext text bigint int int1 int2 int3 int4 int8 integer float float4 float8 double char varbinary varchar varcharacter precision date datetime year unsigned signed numeric"), + builtin: set("bool boolean bit blob decimal double float long longblob longtext medium mediumblob mediumint mediumtext time timestamp tinyblob tinyint tinytext text bigint int int1 int2 int3 int4 int8 integer float float4 float8 double char varbinary varchar varcharacter precision date datetime year unsigned signed numeric"), atoms: set("false true null unknown"), operatorChars: /^[*+\-%<>!=&|^]/, dateSQL: set("date time timestamp"), @@ -313,8 +313,7 @@ CodeMirror.defineMode("sql", function(config, parserConfig) { name: "sql", client: set("appinfo arraysize autocommit autoprint autorecovery autotrace blockterminator break btitle cmdsep colsep compatibility compute concat copycommit copytypecheck define describe echo editfile embedded escape exec execute feedback flagger flush heading headsep instance linesize lno loboffset logsource long longchunksize markup native newpage numformat numwidth pagesize pause pno recsep recsepchar release repfooter repheader serveroutput shiftinout show showmode size spool sqlblanklines sqlcase sqlcode sqlcontinue sqlnumber sqlpluscompatibility sqlprefix sqlprompt sqlterminator suffix tab term termout time timing trimout trimspool ttitle underline verify version wrap"), keywords: set("abort accept access add all alter and any array arraylen as asc assert assign at attributes audit authorization avg base_table begin between binary_integer body boolean by case cast char char_base check close cluster clusters colauth column comment commit compress connect connected constant constraint crash create current currval cursor data_base database date dba deallocate debugoff debugon decimal declare default definition delay delete desc digits dispose distinct do drop else elsif enable end entry escape exception exception_init exchange exclusive exists exit external fast fetch file for force form from function generic goto grant group having identified if immediate in increment index indexes indicator initial initrans insert interface intersect into is key level library like limited local lock log logging long loop master maxextents maxtrans member minextents minus mislabel mode modify multiset new next no noaudit nocompress nologging noparallel not nowait number_base object of off offline on online only open option or order out package parallel partition pctfree pctincrease pctused pls_integer positive positiven pragma primary prior private privileges procedure public raise range raw read rebuild record ref references refresh release rename replace resource restrict return returning reverse revoke rollback row rowid rowlabel rownum rows run savepoint schema segment select separate session set share snapshot some space split sql start statement storage subtype successful synonym tabauth table tables tablespace task terminate then to trigger truncate type union unique unlimited unrecoverable unusable update use using validate value values variable view views when whenever where while with work"), - functions: set("abs acos add_months ascii asin atan atan2 average bfilename ceil chartorowid chr concat convert cos cosh count decode deref dual dump dup_val_on_index empty error exp false floor found glb greatest hextoraw initcap instr instrb isopen last_day least lenght lenghtb ln lower lpad ltrim lub make_ref max min mod months_between new_time next_day nextval nls_charset_decl_len nls_charset_id nls_charset_name nls_initcap nls_lower nls_sort nls_upper nlssort no_data_found notfound null nvl others power rawtohex reftohex round rowcount rowidtochar rpad rtrim sign sin sinh soundex sqlcode sqlerrm sqrt stddev substr substrb sum sysdate tan tanh to_char to_date to_label to_multi_byte to_number to_single_byte translate true trunc uid upper user userenv variance vsize"), - builtin: set("bfile blob character clob dec float int integer mlslabel natural naturaln nchar nclob number numeric nvarchar2 real rowtype signtype smallint string varchar varchar2"), + builtin: set("bfile blob character clob dec float int integer mlslabel natural naturaln nchar nclob number numeric nvarchar2 real rowtype signtype smallint string varchar varchar2 abs acos add_months ascii asin atan atan2 average bfilename ceil chartorowid chr concat convert cos cosh count decode deref dual dump dup_val_on_index empty error exp false floor found glb greatest hextoraw initcap instr instrb isopen last_day least lenght lenghtb ln lower lpad ltrim lub make_ref max min mod months_between new_time next_day nextval nls_charset_decl_len nls_charset_id nls_charset_name nls_initcap nls_lower nls_sort nls_upper nlssort no_data_found notfound null nvl others power rawtohex reftohex round rowcount rowidtochar rpad rtrim sign sin sinh soundex sqlcode sqlerrm sqrt stddev substr substrb sum sysdate tan tanh to_char to_date to_label to_multi_byte to_number to_single_byte translate true trunc uid upper user userenv variance vsize"), operatorChars: /^[*+\-%<>!=~]/, dateSQL: set("date time timestamp"), support: set("doubleQuote nCharCast zerolessFloat binaryNumber hexNumber") From 1bdd4cb199082187c7f9251d10337eb413001ea8 Mon Sep 17 00:00:00 2001 From: Marijn Haverbeke Date: Mon, 16 Sep 2013 15:43:40 +0200 Subject: [PATCH 1502/5780] Don't make changes to bgClass invalidate line measurement caches --- lib/codemirror.js | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/lib/codemirror.js b/lib/codemirror.js index 10545b9573..f4ee4b1c17 100644 --- a/lib/codemirror.js +++ b/lib/codemirror.js @@ -1001,7 +1001,7 @@ window.CodeMirror = (function() { var memo = cache[i]; if (memo.text == line.text && memo.markedSpans == line.markedSpans && cm.display.scroller.clientWidth == memo.width && - memo.classes == line.textClass + "|" + line.bgClass + "|" + line.wrapClass) + memo.classes == line.textClass + "|" + line.wrapClass) return memo; } } @@ -1021,7 +1021,7 @@ window.CodeMirror = (function() { var cache = cm.display.measureLineCache; var memo = {text: line.text, width: cm.display.scroller.clientWidth, markedSpans: line.markedSpans, measure: measure, - classes: line.textClass + "|" + line.bgClass + "|" + line.wrapClass}; + classes: line.textClass + "|" + line.wrapClass}; if (cache.length == 16) cache[++cm.display.measureLineCachePos % 16] = memo; else cache.push(memo); return measure; From a59d5767655792c8888f3e6a093f6f887908f8ae Mon Sep 17 00:00:00 2001 From: Marijn Haverbeke Date: Mon, 16 Sep 2013 15:55:52 +0200 Subject: [PATCH 1503/5780] [merge addon] Signal updateDiff events --- addon/merge/merge.js | 1 + 1 file changed, 1 insertion(+) diff --git a/addon/merge/merge.js b/addon/merge/merge.js index 391eb885fb..ceb48032b1 100644 --- a/addon/merge/merge.js +++ b/addon/merge/merge.js @@ -60,6 +60,7 @@ if (dv.diffOutOfDate) { dv.diff = getDiff(dv.orig.getValue(), dv.edit.getValue()); dv.diffOutOfDate = false; + CodeMirror.signal(dv.edit, "updateDiff", dv.diff); } if (dv.showDifferences) { updateMarks(dv.edit, dv.diff, edit, DIFF_INSERT, dv.classes); From ecafd26e8c3cb6de5f36fde708368505f0e1ca08 Mon Sep 17 00:00:00 2001 From: Marijn Haverbeke Date: Mon, 16 Sep 2013 16:06:25 +0200 Subject: [PATCH 1504/5780] Fix spanAffectsWrapping regexp for Safari Issue #1825 --- demo/spanaffectswrapping_shim.html | 2 +- lib/codemirror.js | 2 +- 2 files changed, 2 insertions(+), 2 deletions(-) diff --git a/demo/spanaffectswrapping_shim.html b/demo/spanaffectswrapping_shim.html index 00de3bdc2b..e598221c0c 100644 --- a/demo/spanaffectswrapping_shim.html +++ b/demo/spanaffectswrapping_shim.html @@ -32,7 +32,7 @@

    Automatically derive odd wrapping behavior for your browser

    + + + + +
    +

    Octave mode

    + +
    + + +

    MIME types defined: text/x-octave.

    +
    diff --git a/mode/octave/octave.js b/mode/octave/octave.js new file mode 100644 index 0000000000..23cd2fe222 --- /dev/null +++ b/mode/octave/octave.js @@ -0,0 +1,118 @@ +CodeMirror.defineMode("octave", function() { + function wordRegexp(words) { + return new RegExp("^((" + words.join(")|(") + "))\\b"); + } + + var singleOperators = new RegExp("^[\\+\\-\\*/&|\\^~<>!@'\\\\]"); + var singleDelimiters = new RegExp('^[\\(\\[\\{\\},:=;]'); + var doubleOperators = new RegExp("^((==)|(~=)|(<=)|(>=)|(<<)|(>>)|(\\.[\\+\\-\\*/\\^\\\\]))"); + var doubleDelimiters = new RegExp("^((!=)|(\\+=)|(\\-=)|(\\*=)|(/=)|(&=)|(\\|=)|(\\^=))"); + var tripleDelimiters = new RegExp("^((>>=)|(<<=))"); + var expressionEnd = new RegExp("^[\\]\\)]"); + var identifiers = new RegExp("^[_A-Za-z][_A-Za-z0-9]*"); + + var builtins = wordRegexp([ + 'error', 'eval', 'function', 'abs', 'acos', 'atan', 'asin', 'cos', + 'cosh', 'exp', 'log', 'prod', 'log10', 'max', 'min', 'sign', 'sin', 'sinh', + 'sqrt', 'tan', 'reshape', 'break', 'zeros', 'default', 'margin', 'round', 'ones', + 'rand', 'syn', 'ceil', 'floor', 'size', 'clear', 'zeros', 'eye', 'mean', 'std', 'cov', + 'det', 'eig', 'inv', 'norm', 'rank', 'trace', 'expm', 'logm', 'sqrtm', 'linspace', 'plot', + 'title', 'xlabel', 'ylabel', 'legend', 'text', 'meshgrid', 'mesh', 'num2str' + ]); + + var keywords = wordRegexp([ + 'return', 'case', 'switch', 'else', 'elseif', 'end', 'endif', 'endfunction', + 'if', 'otherwise', 'do', 'for', 'while', 'try', 'catch', 'classdef', 'properties', 'events', + 'methods', 'global', 'persistent', 'endfor', 'endwhile', 'printf', 'disp', 'until', 'continue' + ]); + + + // tokenizers + function tokenTranspose(stream, state) { + if (!stream.sol() && stream.peek() === '\'') { + stream.next(); + state.tokenize = tokenBase; + return 'operator'; + } + state.tokenize = tokenBase; + return tokenBase(stream, state); + } + + + function tokenComment(stream, state) { + if (stream.match(/^.*%}/)) { + state.tokenize = tokenBase; + return 'comment'; + }; + stream.skipToEnd(); + return 'comment'; + } + + function tokenBase(stream, state) { + // whitespaces + if (stream.eatSpace()) return null; + + // Handle one line Comments + if (stream.match('%{')){ + state.tokenize = tokenComment; + stream.skipToEnd(); + return 'comment'; + } + + if (stream.match(/^(%)|(\.\.\.)/)){ + stream.skipToEnd(); + return 'comment'; + } + + // Handle Number Literals + if (stream.match(/^[0-9\.+-]/, false)) { + if (stream.match(/^[+-]?0x[0-9a-fA-F]+[ij]?/)) { + stream.tokenize = tokenBase; + return 'number'; }; + if (stream.match(/^[+-]?\d*\.\d+([EeDd][+-]?\d+)?[ij]?/)) { return 'number'; }; + if (stream.match(/^[+-]?\d+([EeDd][+-]?\d+)?[ij]?/)) { return 'number'; }; + } + if (stream.match(wordRegexp(['nan','NaN','inf','Inf']))) { return 'number'; }; + + // Handle Strings + if (stream.match(/^"([^"]|(""))*"/)) { return 'string'; } ; + if (stream.match(/^'([^']|(''))*'/)) { return 'string'; } ; + + // Handle words + if (stream.match(keywords)) { return 'keyword'; } ; + if (stream.match(builtins)) { return 'builtin'; } ; + if (stream.match(identifiers)) { return 'variable'; } ; + + if (stream.match(singleOperators) || stream.match(doubleOperators)) { return 'operator'; }; + if (stream.match(singleDelimiters) || stream.match(doubleDelimiters) || stream.match(tripleDelimiters)) { return null; }; + + if (stream.match(expressionEnd)) { + state.tokenize = tokenTranspose; + return null; + }; + + + // Handle non-detected items + stream.next(); + return 'error'; + }; + + + return { + startState: function() { + return { + tokenize: tokenBase + }; + }, + + token: function(stream, state) { + var style = state.tokenize(stream, state); + if (style === 'number' || style === 'variable'){ + state.tokenize = tokenTranspose; + } + return style; + } + }; +}); + +CodeMirror.defineMIME("text/x-octave", "octave"); From 51c8aee26fd4bb175f5640307dbc41295407697f Mon Sep 17 00:00:00 2001 From: Marijn Haverbeke Date: Tue, 17 Sep 2013 17:03:29 +0200 Subject: [PATCH 1508/5780] [mode/meta.js] Add missing modes --- mode/meta.js | 6 ++++++ 1 file changed, 6 insertions(+) diff --git a/mode/meta.js b/mode/meta.js index 9c5d986f66..ce51c8ae19 100644 --- a/mode/meta.js +++ b/mode/meta.js @@ -13,12 +13,15 @@ CodeMirror.modeInfo = [ {name: 'CSS', mime: 'text/css', mode: 'css'}, {name: 'D', mime: 'text/x-d', mode: 'd'}, {name: 'diff', mime: 'text/x-diff', mode: 'diff'}, + {name: 'DTD', mime: 'application/xml-dtd', mode: 'dtd'}, {name: 'ECL', mime: 'text/x-ecl', mode: 'ecl'}, {name: 'Erlang', mime: 'text/x-erlang', mode: 'erlang'}, + {name: 'Fortran', mime: 'text/x-fortran', mode: 'fortran'}, {name: 'Gas', mime: 'text/x-gas', mode: 'gas'}, {name: 'GitHub Flavored Markdown', mime: 'text/x-gfm', mode: 'gfm'}, {name: 'GO', mime: 'text/x-go', mode: 'go'}, {name: 'Groovy', mime: 'text/x-groovy', mode: 'groovy'}, + {name: 'HAML', mime: 'text/x-haml', mode: 'haml'}, {name: 'Haskell', mime: 'text/x-haskell', mode: 'haskell'}, {name: 'Haxe', mime: 'text/x-haxe', mode: 'haxe'}, {name: 'ASP.NET', mime: 'application/x-aspx', mode: 'htmlembedded'}, @@ -40,6 +43,7 @@ CodeMirror.modeInfo = [ {name: 'Nginx', mime: 'text/x-nginx-conf', mode: 'nginx'}, {name: 'NTriples', mime: 'text/n-triples', mode: 'ntriples'}, {name: 'OCaml', mime: 'text/x-ocaml', mode: 'ocaml'}, + {name: 'Octave', mime: 'text/x-octave', mode: 'octave'}, {name: 'Pascal', mime: 'text/x-pascal', mode: 'pascal'}, {name: 'Perl', mime: 'text/x-perl', mode: 'perl'}, {name: 'PHP', mime: 'text/x-php', mode: 'php'}, @@ -69,6 +73,8 @@ CodeMirror.modeInfo = [ {name: 'Tcl', mime: 'text/x-tcl', mode: 'tcl'}, {name: 'TiddlyWiki ', mime: 'text/x-tiddlywiki', mode: 'tiddlywiki'}, {name: 'Tiki wiki', mime: 'text/tiki', mode: 'tiki'}, + {name: 'TOML', mime: 'text/x-toml', mode: 'toml'}, + {name: 'Turtle', mime: 'text/turtle', mode: 'turtle'}, {name: 'VB.NET', mime: 'text/x-vb', mode: 'vb'}, {name: 'VBScript', mime: 'text/vbscript', mode: 'vbscript'}, {name: 'Velocity', mime: 'text/velocity', mode: 'velocity'}, From e987409ef0c42435d8658c605f3ed2e517f475e8 Mon Sep 17 00:00:00 2001 From: Marijn Haverbeke Date: Thu, 19 Sep 2013 22:18:54 +0200 Subject: [PATCH 1509/5780] [d mode] Remove executable flag from mode files Closes #1830 --- mode/d/d.js | 0 mode/d/index.html | 0 2 files changed, 0 insertions(+), 0 deletions(-) mode change 100755 => 100644 mode/d/d.js mode change 100755 => 100644 mode/d/index.html diff --git a/mode/d/d.js b/mode/d/d.js old mode 100755 new mode 100644 diff --git a/mode/d/index.html b/mode/d/index.html old mode 100755 new mode 100644 From 344446ee7b1eb7747e52f6490bac1b407a0fb9a5 Mon Sep 17 00:00:00 2001 From: Marijn Haverbeke Date: Mon, 23 Sep 2013 08:41:48 +0200 Subject: [PATCH 1510/5780] [real-world uses] Add nodeMirror --- doc/realworld.html | 1 + 1 file changed, 1 insertion(+) diff --git a/doc/realworld.html b/doc/realworld.html index 040da5910a..421250162c 100644 --- a/doc/realworld.html +++ b/doc/realworld.html @@ -85,6 +85,7 @@

    CodeMirror real-world uses

  • Mongo MapReduce WebBrowser
  • My2ndGeneration (social coding)
  • Navigate CMS
  • +
  • nodeMirror (IDE project)
  • NoTex (rST authoring)
  • Oak (online outliner)
  • ORG (z80 assembly IDE)
  • From d662cafbce9893e1232ccafaa9714a24388456ac Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Guillaume=20Mass=C3=A9?= Date: Thu, 19 Sep 2013 17:15:38 -0400 Subject: [PATCH 1511/5780] solarized selection should match gutter --- theme/solarized.css | 26 ++------------------------ 1 file changed, 2 insertions(+), 24 deletions(-) diff --git a/theme/solarized.css b/theme/solarized.css index f6b2a3f277..1a87d2d801 100644 --- a/theme/solarized.css +++ b/theme/solarized.css @@ -99,36 +99,14 @@ http://ethanschoonover.com/solarized/img/solarized-palette.png color: #586e75; } -.cm-s-solarized.cm-s-dark .CodeMirror-focused .CodeMirror-selected { - background: #386774; - color: inherit; -} - -.cm-s-solarized.cm-s-dark ::selection { - background: #386774; - color: inherit; -} - .cm-s-solarized.cm-s-dark .CodeMirror-selected { - background: #586e75; -} - -.cm-s-solarized.cm-s-light .CodeMirror-focused .CodeMirror-selected { - background: #eee8d5; - color: inherit; -} - -.cm-s-solarized.cm-s-light ::selection { - background: #eee8d5; - color: inherit; + background: #073642; } .cm-s-solarized.cm-s-light .CodeMirror-selected { - background: #93a1a1; + background: #eee8d5; } - - /* Editor styling */ From 9d67c932a266a9c5333f74bb61af5f587ae73cde Mon Sep 17 00:00:00 2001 From: Marijn Haverbeke Date: Mon, 23 Sep 2013 12:07:31 +0200 Subject: [PATCH 1512/5780] [css mode] Recognize numbers with leading dot Closes #1833 Closes #1834 --- mode/css/css.js | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/mode/css/css.js b/mode/css/css.js index 085b119de4..4264bc606c 100644 --- a/mode/css/css.js +++ b/mode/css/css.js @@ -37,7 +37,7 @@ CodeMirror.defineMode("css", function(config, parserConfig) { stream.match(/^\s*\w*/); return ret("keyword", "important"); } - else if (/\d/.test(ch)) { + else if (/\d/.test(ch) || ch == "." && stream.eat(/\d/)) { stream.eatWhile(/[\w.%]/); return ret("number", "unit"); } From d2b2ed74c55dcfe03eeaf467180041265f879009 Mon Sep 17 00:00:00 2001 From: Marijn Haverbeke Date: Mon, 23 Sep 2013 12:14:56 +0200 Subject: [PATCH 1513/5780] Ensure scrollbar/width instability doesn't cause infinite loop Issue #1787 --- lib/codemirror.js | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/lib/codemirror.js b/lib/codemirror.js index 88f992d8af..cdf7652e72 100644 --- a/lib/codemirror.js +++ b/lib/codemirror.js @@ -409,14 +409,14 @@ window.CodeMirror = (function() { function updateDisplay(cm, changes, viewPort, forced) { var oldFrom = cm.display.showingFrom, oldTo = cm.display.showingTo, updated; var visible = visibleLines(cm.display, cm.doc, viewPort); - for (;;) { + for (var first = true;; first = false) { var oldWidth = cm.display.scroller.clientWidth; if (!updateDisplayInner(cm, changes, visible, forced)) break; updated = true; changes = []; updateSelection(cm); updateScrollbars(cm); - if (cm.options.lineWrapping && oldWidth != cm.display.scroller.clientWidth) { + if (first && cm.options.lineWrapping && oldWidth != cm.display.scroller.clientWidth) { forced = true; continue; } From a3538de0decec580bc79fa0f9237973c83c8f5a9 Mon Sep 17 00:00:00 2001 From: Marijn Haverbeke Date: Mon, 23 Sep 2013 12:30:12 +0200 Subject: [PATCH 1514/5780] Don't add null changes to history Issue #1836 --- lib/codemirror.js | 1 + 1 file changed, 1 insertion(+) diff --git a/lib/codemirror.js b/lib/codemirror.js index cdf7652e72..41b4d759ed 100644 --- a/lib/codemirror.js +++ b/lib/codemirror.js @@ -2329,6 +2329,7 @@ window.CodeMirror = (function() { } function makeChangeNoReadonly(doc, change, selUpdate) { + if (change.text.length == 1 && change.text[0] == "" && posEq(change.from, change.to)) return; var selAfter = computeSelAfterChange(doc, change, selUpdate); addToHistory(doc, change, selAfter, doc.cm ? doc.cm.curOp.id : NaN); From c2f2f34b6cedebf0a1ade910e17c438e2ae49623 Mon Sep 17 00:00:00 2001 From: Marijn Haverbeke Date: Mon, 23 Sep 2013 13:02:14 +0200 Subject: [PATCH 1515/5780] [test suite] Fix windows/IE incompatibilities Closes #1826 --- test/test.js | 8 +++++--- 1 file changed, 5 insertions(+), 3 deletions(-) diff --git a/test/test.js b/test/test.js index a05b8afb42..e5d9afdedd 100644 --- a/test/test.js +++ b/test/test.js @@ -1,5 +1,7 @@ var Pos = CodeMirror.Pos; +CodeMirror.defaults.rtlMoveVisually = true; + function forEach(arr, f) { for (var i = 0, e = arr.length; i < e; ++i) f(arr[i]); } @@ -529,10 +531,10 @@ testCM("multiBookmarkCursor", function(cm) { } var base1 = cm.cursorCoords(Pos(0, 1)).left, base4 = cm.cursorCoords(Pos(0, 4)).left; add(true); - eq(base1, cm.cursorCoords(Pos(0, 1)).left); + is(Math.abs(base1 - cm.cursorCoords(Pos(0, 1)).left) < .1); while (m = ms.pop()) m.clear(); add(false); - eq(base4, cm.cursorCoords(Pos(0, 1)).left); + is(Math.abs(base4 - cm.cursorCoords(Pos(0, 1)).left) < .1); }, {value: "abcdefg"}); testCM("getAllMarks", function(cm) { @@ -1159,7 +1161,7 @@ testCM("rtlMovement", function(cm) { prevX = cursor.offsetLeft; } }); -}, {rtlMoveVisually: true}); +}); // Verify that updating a line clears its bidi ordering testCM("bidiUpdate", function(cm) { From 46bab8eab13594efcb8f6752e44013ddb1502661 Mon Sep 17 00:00:00 2001 From: Marijn Haverbeke Date: Mon, 23 Sep 2013 13:50:16 +0200 Subject: [PATCH 1516/5780] Mark release 3.17 --- AUTHORS | 11 +++++++++++ bower.json | 2 +- doc/compress.html | 1 + doc/releases.html | 9 +++++++++ index.html | 2 +- lib/codemirror.js | 2 +- package.json | 2 +- 7 files changed, 25 insertions(+), 4 deletions(-) diff --git a/AUTHORS b/AUTHORS index bb6f8c8c64..ad2ec99576 100644 --- a/AUTHORS +++ b/AUTHORS @@ -17,6 +17,7 @@ alexey-k Alex Piggott Amy Ananya Sen +AndersMad Andre von Houck Andrey Lushnikov Andy Kimball @@ -36,6 +37,7 @@ Ben Keen boomyjee borawjm Brandon Frohs +Brett Zamir Brian Sletten Bruce Mitchener Chandra Sekhar Pydi @@ -44,6 +46,7 @@ Chris Coyier Chris Granger Chris Morgan Christopher Brown +ciaranj CodeAnimal ComFreek dagsta @@ -76,6 +79,7 @@ Felipe Lalanne Felix Raab Filip Noetzel flack +ForbesLindesay Ford_Lawnmower Gabriel Nahmias galambalazs @@ -85,6 +89,7 @@ Golevka Gordon Smith greengiant Guillaume Massé +Guillaume Massé Hans Engel Hardest Hasan Karahan @@ -137,6 +142,7 @@ komakino Konstantin Lopuhin koops ks-ifware +kubelsmieci Lanny leaf corcoran Leonya Khachaturov @@ -151,6 +157,8 @@ Marco Aurélio Marijn Haverbeke Mario Pietsch Mark Lentczner +Martin Balek +Martín Gaitán Mason Malone Mateusz Paprocki mats cronqvist @@ -219,6 +227,7 @@ Stas Kobzar Stefan Borsje Steffen Beyer Steve O'Hara +stoskov Tarmil tfjgeorge Thaddee Tyl @@ -228,6 +237,8 @@ Thomas Schmid Tim Baumann Timothy Farrell Timothy Hatcher +TobiasBg +Tomas-A Tomas Varaneckas Tom Erik Støwer Tom MacWright diff --git a/bower.json b/bower.json index 451d654c0c..2aeffb8990 100644 --- a/bower.json +++ b/bower.json @@ -1,6 +1,6 @@ { "name": "CodeMirror", - "version": "3.16.0", + "version": "3.17.0", "main": ["lib/codemirror.js", "lib/codemirror.css"], "ignore": [ "**/.*", diff --git a/doc/compress.html b/doc/compress.html index d7f720fae1..30b148dd63 100644 --- a/doc/compress.html +++ b/doc/compress.html @@ -33,6 +33,7 @@

    Script compression helper

    Version:

    Version: + +

    Demonstration of +the hardwrap addon. +The above editor has its change event hooked up to +the wrapParagraphsInRange method, so that the paragraphs +are reflown as you are typing.

    + + + + diff --git a/doc/compress.html b/doc/compress.html index a3e3792a18..5ae53c4838 100644 --- a/doc/compress.html +++ b/doc/compress.html @@ -166,6 +166,7 @@

    Script compression helper

    + diff --git a/doc/manual.html b/doc/manual.html index 9f516b7e05..51ad9799bc 100644 --- a/doc/manual.html +++ b/doc/manual.html @@ -14,9 +14,10 @@ diff --git a/mode/julia/julia.js b/mode/julia/julia.js index edf9dc41b5..9ec2428cd4 100644 --- a/mode/julia/julia.js +++ b/mode/julia/julia.js @@ -11,7 +11,7 @@ CodeMirror.defineMode("julia", function(_conf, parserConf) { var blockOpeners = ["begin", "function", "type", "immutable", "let", "macro", "for", "while", "quote", "if", "else", "elseif", "try", "finally", "catch"]; var blockClosers = ["end", "else", "elseif", "catch", "finally"]; var keywordList = ['if', 'else', 'elseif', 'while', 'for', 'begin', 'let', 'end', 'do', 'try', 'catch', 'finally', 'return', 'break', 'continue', 'global', 'local', 'const', 'export', 'import', 'importall', 'using', 'function', 'macro', 'module', 'baremodule', 'type', 'immutable', 'quote', 'typealias', 'abstract', 'bitstype', 'ccall']; - var builtinList = ['all', 'true', 'false', 'any', 'enumerate', 'open', 'close', 'linspace', 'nothing', 'NaN', 'Inf', 'print', 'println', 'Int8', 'Uint8', 'Int16', 'Uint16', 'Int32', 'Uint32', 'Int64', 'Uint64', 'Int128', 'Uint128', 'Bool', 'Char', 'Float16', 'Float32', 'Float64', 'Array', 'Vector', 'Matrix', 'String', 'error', 'warn', 'info']; + var builtinList = ['true', 'false', 'enumerate', 'open', 'close', 'nothing', 'NaN', 'Inf', 'print', 'println', 'Int8', 'Uint8', 'Int16', 'Uint16', 'Int32', 'Uint32', 'Int64', 'Uint64', 'Int128', 'Uint128', 'Bool', 'Char', 'Float16', 'Float32', 'Float64', 'Array', 'Vector', 'Matrix', 'String', 'UTF8String', 'ASCIIString', 'error', 'warn', 'info', '@printf']; //var stringPrefixes = new RegExp("^[br]?('|\")") var stringPrefixes = /^[br]?('|"{3}|")/; @@ -19,6 +19,7 @@ CodeMirror.defineMode("julia", function(_conf, parserConf) { var builtins = wordRegexp(builtinList); var openers = wordRegexp(blockOpeners); var closers = wordRegexp(blockClosers); + var macro = /@[_A-Za-z][_A-Za-z0-9]*!*/; var indentInfo = null; function in_array(state) { @@ -147,6 +148,7 @@ CodeMirror.defineMode("julia", function(_conf, parserConf) { if (stream.match(operators)) { return 'operator'; } + if (stream.match(delimiters)) { return null; } @@ -159,6 +161,9 @@ CodeMirror.defineMode("julia", function(_conf, parserConf) { return 'builtin'; } + if (stream.match(macro)) { + return 'meta'; + } if (stream.match(identifiers)) { state.leaving_expr=true; @@ -220,11 +225,6 @@ CodeMirror.defineMode("julia", function(_conf, parserConf) { return style; } - // Handle macro calls - if (current === '@') { - return stream.match(identifiers, false) ? 'meta' : ERRORCLASS; - } - return style; } From d64d8ba868dac600ba8572d3e4b51d612a9116fa Mon Sep 17 00:00:00 2001 From: Jason Johnston Date: Tue, 22 Oct 2013 13:41:45 -0600 Subject: [PATCH 1601/5780] [matchbrackets addon] add a maxScanLines configuration, defaulting to 100. --- addon/edit/matchbrackets.js | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-) diff --git a/addon/edit/matchbrackets.js b/addon/edit/matchbrackets.js index 131fe831fd..9d9b3882f7 100644 --- a/addon/edit/matchbrackets.js +++ b/addon/edit/matchbrackets.js @@ -8,6 +8,7 @@ function findMatchingBracket(cm, where, strict) { var state = cm.state.matchBrackets; var maxScanLen = (state && state.maxScanLineLength) || 10000; + var maxScanLines = (state && state.maxScanLines) || 100; var cur = where || cm.getCursor(), line = cm.getLineHandle(cur.line), pos = cur.ch - 1; var match = (pos >= 0 && matching[line.text.charAt(pos)]) || matching[line.text.charAt(++pos)]; @@ -32,7 +33,7 @@ } } } - for (var i = cur.line, found, e = forward ? Math.min(i + 100, cm.lineCount()) : Math.max(-1, i - 100); i != e; i+=d) { + for (var i = cur.line, found, e = forward ? Math.min(i + maxScanLines, cm.lineCount()) : Math.max(-1, i - maxScanLines); i != e; i+=d) { if (i == cur.line) found = scan(line, i, pos); else found = scan(cm.getLineHandle(i), i); if (found) break; From 02f49478ebbe133a1567832726589f05af0d4a3f Mon Sep 17 00:00:00 2001 From: angelozerr Date: Tue, 22 Oct 2013 17:03:01 +0200 Subject: [PATCH 1602/5780] [tern addon] Make it possible to pass in explicit positions Create Tern request with optionnal pos to manage for instance tern hover. See demo at http://codemirror-java.opensagres.eu.cloudbees.net/codemirror-javascript/demo/javascript-all.html --- addon/tern/tern.js | 14 +++++++------- 1 file changed, 7 insertions(+), 7 deletions(-) diff --git a/addon/tern/tern.js b/addon/tern/tern.js index fe93fa7335..1f18a657fb 100644 --- a/addon/tern/tern.js +++ b/addon/tern/tern.js @@ -96,7 +96,7 @@ getHint: function(cm, c) { return hint(this, cm, c); }, - showType: function(cm) { showType(this, cm); }, + showType: function(cm, pos) { showType(this, cm, pos); }, updateArgHints: function(cm) { updateArgHints(this, cm); }, @@ -106,10 +106,10 @@ rename: function(cm) { rename(this, cm); }, - request: function (cm, query, c) { + request: function (cm, query, c, pos) { var self = this; var doc = findDoc(this, cm.getDoc()); - var request = buildRequest(this, doc, query); + var request = buildRequest(this, doc, query, pos); this.server.request(request, function (error, data) { if (!error && self.options.responseFilter) @@ -221,7 +221,7 @@ // Type queries - function showType(ts, cm) { + function showType(ts, cm, pos) { ts.request(cm, "type", function(error, data) { if (error) return showError(ts, cm, error); if (ts.options.typeTip) { @@ -236,7 +236,7 @@ } } tempTooltip(cm, tip); - }); + }, pos); } // Maintaining argument hints @@ -450,13 +450,13 @@ // Generic request-building helper - function buildRequest(ts, doc, query) { + function buildRequest(ts, doc, query, pos) { var files = [], offsetLines = 0, allowFragments = !query.fullDocs; if (!allowFragments) delete query.fullDocs; if (typeof query == "string") query = {type: query}; query.lineCharPositions = true; if (query.end == null) { - query.end = doc.doc.getCursor("end"); + query.end = pos || doc.doc.getCursor("end"); if (doc.doc.somethingSelected()) query.start = doc.doc.getCursor("start"); } From ae0640c4befccf946cdbe12988d59bd3572ec00e Mon Sep 17 00:00:00 2001 From: Marijn Haverbeke Date: Thu, 24 Oct 2013 14:49:43 +0200 Subject: [PATCH 1603/5780] [javascript mode] Properly handle regexp at start of file --- mode/javascript/javascript.js | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/mode/javascript/javascript.js b/mode/javascript/javascript.js index e8ace32d89..9fca783c44 100644 --- a/mode/javascript/javascript.js +++ b/mode/javascript/javascript.js @@ -103,7 +103,7 @@ CodeMirror.defineMode("javascript", function(config, parserConfig) { return ret("comment", "comment"); } else if (state.lastType == "operator" || state.lastType == "keyword c" || - /^[\[{}\(,;:]$/.test(state.lastType)) { + state.lastType == "sof" || /^[\[{}\(,;:]$/.test(state.lastType)) { nextUntilUnescaped(stream, "/"); stream.eatWhile(/[gimy]/); // 'y' is "sticky" option in Mozilla return ret("regexp", "string-2"); @@ -410,7 +410,7 @@ CodeMirror.defineMode("javascript", function(config, parserConfig) { startState: function(basecolumn) { return { tokenize: jsTokenBase, - lastType: null, + lastType: "sof", cc: [], lexical: new JSLexical((basecolumn || 0) - indentUnit, 0, "block", false), localVars: parserConfig.localVars, From 759b3a0e9ab1ce6486341651252d968e417ffe5e Mon Sep 17 00:00:00 2001 From: Marijn Haverbeke Date: Sat, 26 Oct 2013 22:06:59 +0200 Subject: [PATCH 1604/5780] Fix undesirable selection-mangling in indentLine Issue #1910 --- lib/codemirror.js | 4 +--- 1 file changed, 1 insertion(+), 3 deletions(-) diff --git a/lib/codemirror.js b/lib/codemirror.js index e52c883201..231b782971 100644 --- a/lib/codemirror.js +++ b/lib/codemirror.js @@ -2759,9 +2759,7 @@ window.CodeMirror = (function() { for (var i = Math.floor(indentation / tabSize); i; --i) {pos += tabSize; indentString += "\t";} if (pos < indentation) indentString += spaceStr(indentation - pos); - if (indentString == curSpaceString) - setSelection(cm.doc, Pos(n, curSpaceString.length), Pos(n, curSpaceString.length)); - else + if (indentString != curSpaceString || doc.sel.head.line == n && doc.sel.head.ch < curSpaceString.length) replaceRange(cm.doc, indentString, Pos(n, 0), Pos(n, curSpaceString.length), "+input"); line.stateAfter = null; } From 8d000ddae0935f73e32ef517841c4df21db175e2 Mon Sep 17 00:00:00 2001 From: Marijn Haverbeke Date: Mon, 28 Oct 2013 19:40:54 +0100 Subject: [PATCH 1605/5780] [javascript mode] Fix bug in recognizing local variable in nested context --- mode/javascript/javascript.js | 4 ++++ 1 file changed, 4 insertions(+) diff --git a/mode/javascript/javascript.js b/mode/javascript/javascript.js index 9fca783c44..05ae70bd2f 100644 --- a/mode/javascript/javascript.js +++ b/mode/javascript/javascript.js @@ -165,6 +165,10 @@ CodeMirror.defineMode("javascript", function(config, parserConfig) { function inScope(state, varname) { for (var v = state.localVars; v; v = v.next) if (v.name == varname) return true; + for (var cx = state.context; cx; cx = cx.prev) { + for (var v = cx.vars; v; v = v.next) + if (v.name == varname) return true; + } } function parseJS(state, style, type, content, stream) { From bf55dac09202b4dbca190a8323ed698e0d2eff74 Mon Sep 17 00:00:00 2001 From: Marijn Haverbeke Date: Tue, 29 Oct 2013 08:46:21 +0100 Subject: [PATCH 1606/5780] Disable textarea when readOnly=nocursor Issue #1909 --- lib/codemirror.js | 10 ++++++++-- 1 file changed, 8 insertions(+), 2 deletions(-) diff --git a/lib/codemirror.js b/lib/codemirror.js index 231b782971..9e7b720781 100644 --- a/lib/codemirror.js +++ b/lib/codemirror.js @@ -3308,8 +3308,14 @@ window.CodeMirror = (function() { option("resetSelectionOnContextMenu", true); option("readOnly", false, function(cm, val) { - if (val == "nocursor") {onBlur(cm); cm.display.input.blur();} - else if (!val) resetInput(cm, true); + if (val == "nocursor") { + onBlur(cm); + cm.display.input.blur(); + cm.display.disabled = true; + } else { + cm.display.disabled = false; + if (!val) resetInput(cm, true); + } }); option("dragDrop", true); From 65c750e8f7b04c7b99a915d912c16f235430ca3e Mon Sep 17 00:00:00 2001 From: ilvalle Date: Mon, 28 Oct 2013 10:21:27 +0100 Subject: [PATCH 1607/5780] [indent-fold addon] Fix folding for empty files --- addon/fold/indent-fold.js | 5 +++-- 1 file changed, 3 insertions(+), 2 deletions(-) diff --git a/addon/fold/indent-fold.js b/addon/fold/indent-fold.js index b54da34777..7ae005d904 100644 --- a/addon/fold/indent-fold.js +++ b/addon/fold/indent-fold.js @@ -1,8 +1,9 @@ CodeMirror.registerHelper("fold", "indent", function(cm, start) { var lastLine = cm.lastLine(), tabSize = cm.getOption("tabSize"), - firstLine = cm.getLine(start.line), - myIndent = CodeMirror.countColumn(firstLine, null, tabSize); + firstLine = cm.getLine(start.line); + if (!tabSize || !firstLine) return; + var myIndent = CodeMirror.countColumn(firstLine, null, tabSize); function foldEnded(curColumn, prevColumn) { return curColumn < myIndent || From 701b226107b3eded05cb10575c258647854c0352 Mon Sep 17 00:00:00 2001 From: Forbes Lindesay Date: Tue, 22 Oct 2013 17:17:16 +0100 Subject: [PATCH 1608/5780] Add the PEG.js language --- mode/peg/index.html | 66 ++++++++++++++++++++++++++++ mode/peg/peg.js | 103 ++++++++++++++++++++++++++++++++++++++++++++ 2 files changed, 169 insertions(+) create mode 100644 mode/peg/index.html create mode 100644 mode/peg/peg.js diff --git a/mode/peg/index.html b/mode/peg/index.html new file mode 100644 index 0000000000..3e55a5e4b5 --- /dev/null +++ b/mode/peg/index.html @@ -0,0 +1,66 @@ + + + + CodeMirror: PEG Mode + + + + + + + + + + + + +
    +

    PEG.js Mode

    +
    + +

    The PEG.js Mode

    +

    Created by Forbes Lindesay.

    +
    + + \ No newline at end of file diff --git a/mode/peg/peg.js b/mode/peg/peg.js new file mode 100644 index 0000000000..6f8b480946 --- /dev/null +++ b/mode/peg/peg.js @@ -0,0 +1,103 @@ +CodeMirror.defineMode("peg", function (config) { + var jsMode = CodeMirror.getMode(config, "javascript"); + + function identifier(stream) { + return stream.match(/^[a-zA-Z_][a-zA-Z0-9_]*/); + } + + return { + startState: function () { + return { + inString: false, + stringType: null, + inComment: false, + inChracterClass: false, + braced: 0, + lhs: true, + localState: null + }; + }, + token: function (stream, state) { + if (stream) + + //check for state changes + if (!state.inString && !state.inComment && ((stream.peek() == '"') || (stream.peek() == "'"))) { + state.stringType = stream.peek(); + stream.next(); // Skip quote + state.inString = true; // Update state + } + if (!state.inString && !state.inComment && stream.match(/^\/\*/)) { + state.inComment = true; + } + + //return state + if (state.inString) { + while (state.inString && !stream.eol()) { + if (stream.peek() === state.stringType) { + stream.next(); // Skip quote + state.inString = false; // Clear flag + } else if (stream.peek() === '\\') { + stream.next(); + stream.next(); + } else { + stream.match(/^.[^\\\"\']*/); + } + } + return state.lhs ? "property string" : "string"; // Token style + } else if (state.inComment) { + while (state.inComment && !stream.eol()) { + if (stream.match(/\*\//)) { + state.inComment = false; // Clear flag + } else { + stream.match(/^.[^\*]*/); + } + } + return "comment"; + } else if (state.inChracterClass) { + if (stream.match(/^[^\]\\]+/)) { + return; + } else if (stream.match(/^\\./)) { + return; + } else { + stream.next(); + state.inChracterClass = false; + return 'bracket'; + } + } else if (stream.peek() === '[') { + stream.next(); + state.inChracterClass = true; + return 'bracket'; + } else if (stream.match(/^\/\//)) { + stream.skipToEnd(); + return "comment"; + } else if (state.braced || stream.peek() === '{') { + if (state.localState === null) { + state.localState = jsMode.startState(); + } + var token = jsMode.token(stream, state.localState); + var text = stream.current(); + if (!token) { + for (var i = 0; i < text.length; i++) { + if (text[i] === '{') { + state.braced++; + } else if (text[i] === '}') { + state.braced--; + } + }; + } + return token; + } else if (identifier(stream)) { + if (stream.peek() === ':') { + return 'variable'; + } + return 'variable-2'; + } else if (['[', ']', '(', ')'].indexOf(stream.peek()) != -1) { + stream.next(); + return 'bracket'; + } else if (!stream.eatSpace()) { + stream.next(); + } + return null; + } + }; +}); From acb16d4be0aba6edf51b97c413cd66a4e248c9e1 Mon Sep 17 00:00:00 2001 From: Marijn Haverbeke Date: Tue, 29 Oct 2013 08:58:07 +0100 Subject: [PATCH 1609/5780] [pegjs mode] Integrate --- doc/compress.html | 1 + mode/index.html | 1 + mode/meta.js | 1 + mode/{peg => pegjs}/index.html | 10 +++++----- mode/{peg/peg.js => pegjs/pegjs.js} | 4 ++-- 5 files changed, 10 insertions(+), 7 deletions(-) rename mode/{peg => pegjs}/index.html (90%) rename mode/{peg/peg.js => pegjs/pegjs.js} (97%) diff --git a/doc/compress.html b/doc/compress.html index d1384a76ef..f49c978b2e 100644 --- a/doc/compress.html +++ b/doc/compress.html @@ -118,6 +118,7 @@

    Script compression helper

    + diff --git a/mode/index.html b/mode/index.html index 337f3336e4..81abc42d8c 100644 --- a/mode/index.html +++ b/mode/index.html @@ -71,6 +71,7 @@

    Language modes

  • OCaml
  • Octave (MATLAB)
  • Pascal
  • +
  • PEG.js
  • Perl
  • PHP
  • Pig Latin
  • diff --git a/mode/meta.js b/mode/meta.js index 47f042568d..226cff12e5 100644 --- a/mode/meta.js +++ b/mode/meta.js @@ -48,6 +48,7 @@ CodeMirror.modeInfo = [ {name: 'OCaml', mime: 'text/x-ocaml', mode: 'ocaml'}, {name: 'Octave', mime: 'text/x-octave', mode: 'octave'}, {name: 'Pascal', mime: 'text/x-pascal', mode: 'pascal'}, + {name: 'PEG.js', mime: null, mode: 'pegjs'}, {name: 'Perl', mime: 'text/x-perl', mode: 'perl'}, {name: 'PHP', mime: 'text/x-php', mode: 'php'}, {name: 'PHP(HTML)', mime: 'application/x-httpd-php', mode: 'php'}, diff --git a/mode/peg/index.html b/mode/pegjs/index.html similarity index 90% rename from mode/peg/index.html rename to mode/pegjs/index.html index 3e55a5e4b5..678b714d8e 100644 --- a/mode/peg/index.html +++ b/mode/pegjs/index.html @@ -1,14 +1,14 @@ - CodeMirror: PEG Mode + CodeMirror: PEG.js Mode - + @@ -22,7 +22,7 @@
    @@ -55,7 +55,7 @@

    PEG.js Mode

    letter = [a-z]+ @@ -63,4 +63,4 @@

    The PEG.js Mode

    Created by Forbes Lindesay.

    - \ No newline at end of file + diff --git a/mode/peg/peg.js b/mode/pegjs/pegjs.js similarity index 97% rename from mode/peg/peg.js rename to mode/pegjs/pegjs.js index 6f8b480946..6cdcc61f30 100644 --- a/mode/peg/peg.js +++ b/mode/pegjs/pegjs.js @@ -1,4 +1,4 @@ -CodeMirror.defineMode("peg", function (config) { +CodeMirror.defineMode("pegjs", function (config) { var jsMode = CodeMirror.getMode(config, "javascript"); function identifier(stream) { @@ -100,4 +100,4 @@ CodeMirror.defineMode("peg", function (config) { return null; } }; -}); +}, "javascript"); From 0d32c1e73bd30408e4d70503aebeefdaef8fecda Mon Sep 17 00:00:00 2001 From: Marijn Haverbeke Date: Wed, 30 Oct 2013 20:03:31 +0100 Subject: [PATCH 1610/5780] [haskell mode] Fix unintended prototype property access bug Closes #1917 --- mode/haskell/haskell.js | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/mode/haskell/haskell.js b/mode/haskell/haskell.js index 59ca7f0baa..68a6317e64 100644 --- a/mode/haskell/haskell.js +++ b/mode/haskell/haskell.js @@ -237,7 +237,7 @@ CodeMirror.defineMode("haskell", function(_config, modeConfig) { token: function(stream, state) { var t = state.f(stream, function(s) { state.f = s; }); var w = stream.current(); - return (w in wellKnownWords) ? wellKnownWords[w] : t; + return wellKnownWords.hasOwnProperty(w) ? wellKnownWords[w] : t; }, blockCommentStart: "{-", From de91f48272d060cfe83406280242ba469f5aba27 Mon Sep 17 00:00:00 2001 From: Marijn Haverbeke Date: Thu, 31 Oct 2013 08:23:15 +0100 Subject: [PATCH 1611/5780] [real-world uses] Add Crudzilla --- doc/realworld.html | 1 + 1 file changed, 1 insertion(+) diff --git a/doc/realworld.html b/doc/realworld.html index 8afe73aa0a..4e0896c377 100644 --- a/doc/realworld.html +++ b/doc/realworld.html @@ -51,6 +51,7 @@

    CodeMirror real-world uses

  • Community Code Camp (code snippet sharing)
  • compilejava.net (online Java sandbox)
  • CKWNC (UML editor)
  • +
  • Crudzilla (self-hosted web IDE)
  • CSSDeck (CSS showcase)
  • Deck.js integration (slides with editors)
  • DbNinja (MySQL access interface)
  • From b41fc2b370c0cb530060f5f2fb7b391ef7afa5d2 Mon Sep 17 00:00:00 2001 From: Marijn Haverbeke Date: Thu, 31 Oct 2013 08:53:52 +0100 Subject: [PATCH 1612/5780] [closetag addon] Don't react to > and / keys when in an attribute Closes #1916 --- addon/edit/closetag.js | 7 +++++-- 1 file changed, 5 insertions(+), 2 deletions(-) diff --git a/addon/edit/closetag.js b/addon/edit/closetag.js index d6a8fafd3c..7b0975043a 100644 --- a/addon/edit/closetag.js +++ b/addon/edit/closetag.js @@ -54,7 +54,8 @@ if (tok.end > pos.ch) tagName = tagName.slice(0, tagName.length - tok.end + pos.ch); var lowerTagName = tagName.toLowerCase(); // Don't process the '>' at the end of an end-tag or self-closing tag - if (tok.type == "tag" && state.type == "closeTag" || + if (tok.type == "string" && (tok.string.charAt(tok.string.length - 1) != '"' || tok.string.length == 1) || + tok.type == "tag" && state.type == "closeTag" || tok.string.indexOf("/") == (tok.string.length - 1) || // match something like dontCloseTags && indexOf(dontCloseTags, lowerTagName) > -1) return CodeMirror.Pass; @@ -72,7 +73,9 @@ function autoCloseSlash(cm) { var pos = cm.getCursor(), tok = cm.getTokenAt(pos); var inner = CodeMirror.innerMode(cm.getMode(), tok.state), state = inner.state; - if (tok.string.charAt(0) != "<" || tok.start != pos.ch - 1 || inner.mode.name != "xml") return CodeMirror.Pass; + if (tok.type == "string" || tok.string.charAt(0) != "<" || + tok.start != pos.ch - 1 || inner.mode.name != "xml") + return CodeMirror.Pass; var tagName = state.context && state.context.tagName; if (tagName) cm.replaceSelection("/" + tagName + ">", "end"); From 01e9fbb6496cda9e3e8fb3b29b4830770cebe642 Mon Sep 17 00:00:00 2001 From: Marijn Haverbeke Date: Thu, 31 Oct 2013 10:17:57 +0100 Subject: [PATCH 1613/5780] [closetag addon] Another corner case when typing in attribute Issue #1916 --- addon/edit/closetag.js | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/addon/edit/closetag.js b/addon/edit/closetag.js index 7b0975043a..a0dc32618e 100644 --- a/addon/edit/closetag.js +++ b/addon/edit/closetag.js @@ -54,7 +54,7 @@ if (tok.end > pos.ch) tagName = tagName.slice(0, tagName.length - tok.end + pos.ch); var lowerTagName = tagName.toLowerCase(); // Don't process the '>' at the end of an end-tag or self-closing tag - if (tok.type == "string" && (tok.string.charAt(tok.string.length - 1) != '"' || tok.string.length == 1) || + if (tok.type == "string" && (tok.end != pos.ch || !/[\"\']/.test(tok.string.charAt(tok.string.length - 1)) || tok.string.length == 1) || tok.type == "tag" && state.type == "closeTag" || tok.string.indexOf("/") == (tok.string.length - 1) || // match something like dontCloseTags && indexOf(dontCloseTags, lowerTagName) > -1) From dcf0b65a89e2d8fd9cc254cb39ee58bae44650c0 Mon Sep 17 00:00:00 2001 From: Marijn Haverbeke Date: Sun, 3 Nov 2013 20:18:53 +0100 Subject: [PATCH 1614/5780] Another iteration on pushing the selection forward on no-change reindents --- lib/codemirror.js | 4 +++- 1 file changed, 3 insertions(+), 1 deletion(-) diff --git a/lib/codemirror.js b/lib/codemirror.js index 9e7b720781..23fc2c5bee 100644 --- a/lib/codemirror.js +++ b/lib/codemirror.js @@ -2759,8 +2759,10 @@ window.CodeMirror = (function() { for (var i = Math.floor(indentation / tabSize); i; --i) {pos += tabSize; indentString += "\t";} if (pos < indentation) indentString += spaceStr(indentation - pos); - if (indentString != curSpaceString || doc.sel.head.line == n && doc.sel.head.ch < curSpaceString.length) + if (indentString != curSpaceString) replaceRange(cm.doc, indentString, Pos(n, 0), Pos(n, curSpaceString.length), "+input"); + else if (doc.sel.head.line == n && doc.sel.head.ch < curSpaceString.length) + setSelection(doc, Pos(n, curSpaceString.length), Pos(n, curSpaceString.length), 1); line.stateAfter = null; } From a8c5cfb5cc0f48bed8c9f7e3e264503a17d20b07 Mon Sep 17 00:00:00 2001 From: soliton4 Date: Sat, 2 Nov 2013 17:48:11 +0100 Subject: [PATCH 1615/5780] [less mode] Bugfix --- mode/less/less.js | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/mode/less/less.js b/mode/less/less.js index ec62319080..fb42241c8c 100644 --- a/mode/less/less.js +++ b/mode/less/less.js @@ -204,7 +204,7 @@ CodeMirror.defineMode("less", function(config) { else if(type === "unit" && state.stack[state.stack.length-1] === "rule")return ret(null, "unit"); else if(type === "unit" && state.stack[state.stack.length-1] === ";")return ret(null, "unit"); else if(type === ")" && state.stack[state.stack.length-1] === "rule")return ret(null, "unit"); - else if(type.match("@") !== null && state.stack[state.stack.length-1] === "rule")return ret(null, "unit"); + else if(type && type.match("@") !== null && state.stack[state.stack.length-1] === "rule")return ret(null, "unit"); //else if(type === "unit" && state.stack[state.stack.length-1] === "rule")return ret(null, stream.current()); else if((type === ";" || type === "}" || type === ",") && state.stack[state.stack.length-1] === ";")return ret("tag", stream.current()); From 36b887d52e12767212ddfb4af353a9e2ecf64366 Mon Sep 17 00:00:00 2001 From: Marko Bonaci Date: Sun, 3 Nov 2013 19:24:29 +0100 Subject: [PATCH 1616/5780] [mbo theme] Fix matching tag background highlighting --- theme/mbo.css | 4 +++- 1 file changed, 3 insertions(+), 1 deletion(-) diff --git a/theme/mbo.css b/theme/mbo.css index f3250a7313..93fe3ee24c 100644 --- a/theme/mbo.css +++ b/theme/mbo.css @@ -24,9 +24,11 @@ .cm-s-mbo .CodeMirror-activeline-background {background: #494b41 !important;} .cm-s-mbo .CodeMirror-matchingbracket { - text-decoration: underline; + text-decoration: underline; color: #f5e107 !important; } + +.cm-s-mbo .CodeMirror-matchingtag {background: #4e4e4e;} div.CodeMirror span.CodeMirror-searching { background-color: none; From 464dd7b555c6fc3eb27afc5c78b34883ba972ba4 Mon Sep 17 00:00:00 2001 From: Peter Kroon Date: Mon, 4 Nov 2013 11:03:23 +0100 Subject: [PATCH 1617/5780] [less mode] Fix null dereference bug Issue #1926 --- mode/less/less.js | 4 +++- 1 file changed, 3 insertions(+), 1 deletion(-) diff --git a/mode/less/less.js b/mode/less/less.js index fb42241c8c..6333a98c48 100644 --- a/mode/less/less.js +++ b/mode/less/less.js @@ -70,7 +70,9 @@ CodeMirror.defineMode("less", function(config) { stream.eatWhile(/[\a-zA-Z0-9\-_]/); if(stream.peek() === " ")stream.eatSpace(); if(stream.peek() === ")" || type === ":")return ret("number", "unit");//rgba(0,0,0,.25); - else if(state.stack[state.stack.length-1] === "rule" && stream.peek().match(/{|,|\+|\(/) === null)return ret("number", "unit"); + else if(stream.peek() !== undefined ){ + if(state.stack[state.stack.length-1] === "rule" && stream.peek().match(/{|,|\+|\(/) === null)return ret("number", "unit"); + } return ret("tag", "tag"); } else if (ch == "#") { //we don't eat white-space, we want the hex color and or id only From 8c42f651f15b6b43022a75b1162f7953bae0559e Mon Sep 17 00:00:00 2001 From: Marijn Haverbeke Date: Mon, 4 Nov 2013 15:07:12 +0100 Subject: [PATCH 1618/5780] [less mode] Fix null dereference bug (part 2) Issue #1926 --- mode/less/less.js | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/mode/less/less.js b/mode/less/less.js index 6333a98c48..da39074851 100644 --- a/mode/less/less.js +++ b/mode/less/less.js @@ -70,7 +70,7 @@ CodeMirror.defineMode("less", function(config) { stream.eatWhile(/[\a-zA-Z0-9\-_]/); if(stream.peek() === " ")stream.eatSpace(); if(stream.peek() === ")" || type === ":")return ret("number", "unit");//rgba(0,0,0,.25); - else if(stream.peek() !== undefined ){ + else if(stream.current().length >1){ if(state.stack[state.stack.length-1] === "rule" && stream.peek().match(/{|,|\+|\(/) === null)return ret("number", "unit"); } return ret("tag", "tag"); From bf7acd64a1a825dc077d9fcb2d90c1e961a1bebc Mon Sep 17 00:00:00 2001 From: Marijn Haverbeke Date: Mon, 4 Nov 2013 15:07:37 +0100 Subject: [PATCH 1619/5780] [javascript-hint addon] Don't fire hints in comments or strings --- addon/hint/javascript-hint.js | 1 + 1 file changed, 1 insertion(+) diff --git a/addon/hint/javascript-hint.js b/addon/hint/javascript-hint.js index 513fb782b0..c66b0a7a5b 100644 --- a/addon/hint/javascript-hint.js +++ b/addon/hint/javascript-hint.js @@ -21,6 +21,7 @@ function scriptHint(editor, keywords, getToken, options) { // Find the token at the cursor var cur = editor.getCursor(), token = getToken(editor, cur), tprop = token; + if (/\b(?:string|comment)\b/.test(token.type)) return; token.state = CodeMirror.innerMode(editor.getMode(), token.state).state; // If it's not a 'word-style' token, ignore the token. From 977c64fbaa0c610edff107a4c13099f18858f3d6 Mon Sep 17 00:00:00 2001 From: Forbes Lindesay Date: Mon, 4 Nov 2013 15:23:13 +0000 Subject: [PATCH 1620/5780] [pig mode] Add DUMP as keyword --- mode/pig/pig.js | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/mode/pig/pig.js b/mode/pig/pig.js index c2f611a1a0..4b44e7ccc3 100644 --- a/mode/pig/pig.js +++ b/mode/pig/pig.js @@ -157,7 +157,7 @@ CodeMirror.defineMode("pig", function(_config, parserConfig) { + "JOIN CROSS UNION SPLIT INTO IF OTHERWISE ALL AS BY USING INNER OUTER ONSCHEMA PARALLEL " + "PARTITION GROUP AND OR NOT GENERATE FLATTEN ASC DESC IS STREAM THROUGH STORE MAPREDUCE " + "SHIP CACHE INPUT OUTPUT STDERROR STDIN STDOUT LIMIT SAMPLE LEFT RIGHT FULL EQ GT LT GTE LTE " - + "NEQ MATCHES TRUE FALSE "; + + "NEQ MATCHES TRUE FALSE DUMP"; // data types var pTypes = "BOOLEAN INT LONG FLOAT DOUBLE CHARARRAY BYTEARRAY BAG TUPLE MAP "; From 575a29a40942229bee4f02f73f3b73c838b5b386 Mon Sep 17 00:00:00 2001 From: Marijn Haverbeke Date: Tue, 5 Nov 2013 10:12:18 +0100 Subject: [PATCH 1621/5780] Add IE11+ detection, use it to fix failing test Closes #1826 --- lib/codemirror.js | 6 +++++- 1 file changed, 5 insertions(+), 1 deletion(-) diff --git a/lib/codemirror.js b/lib/codemirror.js index 23fc2c5bee..254c0e03fc 100644 --- a/lib/codemirror.js +++ b/lib/codemirror.js @@ -7,9 +7,13 @@ window.CodeMirror = (function() { // Crude, but necessary to handle a number of hard-to-feature-detect // bugs and behavior differences. var gecko = /gecko\/\d/i.test(navigator.userAgent); + // IE11 currently doesn't count as 'ie', since it has almost none of + // the same bugs as earlier versions. Use ie_gt10 to handle + // incompatibilities in that version. var ie = /MSIE \d/.test(navigator.userAgent); var ie_lt8 = ie && (document.documentMode == null || document.documentMode < 8); var ie_lt9 = ie && (document.documentMode == null || document.documentMode < 9); + var ie_gt10 = /Trident\/([7-9]|\d{2,})\./; var webkit = /WebKit\//.test(navigator.userAgent); var qtwebkit = webkit && /Qt\/\d+\.\d+/.test(navigator.userAgent); var chrome = /Chrome\//.test(navigator.userAgent); @@ -4403,7 +4407,7 @@ window.CodeMirror = (function() { // Work around problem with the reported dimensions of single-char // direction spans on IE (issue #1129). See also the comment in // cursorCoords. - if (measure && ie && (order = getOrder(line))) { + if (measure && (ie || ie_gt10) && (order = getOrder(line))) { var l = order.length - 1; if (order[l].from == order[l].to) --l; var last = order[l], prev = order[l - 1]; From 567a94b89cbe8bf12f6a210e2e395c5c147272a9 Mon Sep 17 00:00:00 2001 From: Marijn Haverbeke Date: Tue, 5 Nov 2013 22:27:04 +0100 Subject: [PATCH 1622/5780] [coffeescript mode] Some refinement in indentation Issue #1932 --- mode/coffeescript/coffeescript.js | 21 +++++++++++++-------- 1 file changed, 13 insertions(+), 8 deletions(-) diff --git a/mode/coffeescript/coffeescript.js b/mode/coffeescript/coffeescript.js index d29ad2b73a..93b77bcb67 100644 --- a/mode/coffeescript/coffeescript.js +++ b/mode/coffeescript/coffeescript.js @@ -213,6 +213,8 @@ CodeMirror.defineMode("coffeescript", function(conf) { if (type !== "coffee") { align = null; alignOffset = stream.column() + stream.current().length; + } else if (state.scope.align) { + state.scope.align = false; } state.scope = { offset: offset, @@ -268,7 +270,6 @@ CodeMirror.defineMode("coffeescript", function(conf) { } if (((current === "->" || current === "=>") && !state.lambda && - state.scope.type == "coffee" && !stream.peek()) || style === "indent") { indent(stream, state); @@ -292,9 +293,10 @@ CodeMirror.defineMode("coffeescript", function(conf) { } delimiter_index = "])}".indexOf(current); if (delimiter_index !== -1) { - if (dedent(stream, state)) { - return ERRORCLASS; - } + while (state.scope.type == "coffee" && state.scope.prev) + state.scope = state.scope.prev; + if (state.scope.type == current) + state.scope = state.scope.prev; } if (state.dedent > 0 && stream.eol() && state.scope.type == "coffee") { if (state.scope.prev) state.scope = state.scope.prev; @@ -333,11 +335,14 @@ CodeMirror.defineMode("coffeescript", function(conf) { indent: function(state, text) { if (state.tokenize != tokenBase) return 0; - var closes = state.scope.type === (text && text.charAt(0)); - if (state.scope.align) - return state.scope.alignOffset - (closes ? 1 : 0); + var scope = state.scope; + var closer = "])}".indexOf(text.charAt(0)) > -1; + if (closer) while (scope.type == "coffee" && scope.prev) scope = scope.prev; + var closes = scope.type === text.charAt(0); + if (scope.align) + return scope.alignOffset - (closes ? 1 : 0); else - return (closes ? state.scope.prev : state.scope).offset; + return (closes ? scope.prev : scope).offset; }, lineComment: "#", From 4da56b598000a451423cf38598e413b10b8a4f62 Mon Sep 17 00:00:00 2001 From: Marijn Haverbeke Date: Wed, 6 Nov 2013 08:29:25 +0100 Subject: [PATCH 1623/5780] [coffeescript mode] Fix bug introduced by 567a94b89cbe8b Issue #1932 --- mode/coffeescript/coffeescript.js | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/mode/coffeescript/coffeescript.js b/mode/coffeescript/coffeescript.js index 93b77bcb67..e8bfe48a24 100644 --- a/mode/coffeescript/coffeescript.js +++ b/mode/coffeescript/coffeescript.js @@ -336,9 +336,9 @@ CodeMirror.defineMode("coffeescript", function(conf) { indent: function(state, text) { if (state.tokenize != tokenBase) return 0; var scope = state.scope; - var closer = "])}".indexOf(text.charAt(0)) > -1; + var closer = text && "])}".indexOf(text.charAt(0)) > -1; if (closer) while (scope.type == "coffee" && scope.prev) scope = scope.prev; - var closes = scope.type === text.charAt(0); + var closes = closer && scope.type === text.charAt(0); if (scope.align) return scope.alignOffset - (closes ? 1 : 0); else From c8639251bb6d06c1b6c4b729a6656642ae4aa8d2 Mon Sep 17 00:00:00 2001 From: Maksym Taran Date: Tue, 5 Nov 2013 15:54:31 -0800 Subject: [PATCH 1624/5780] [indent-fold addon] Include empty lines --- addon/fold/indent-fold.js | 46 ++++++++++++++++++++------------------- 1 file changed, 24 insertions(+), 22 deletions(-) diff --git a/addon/fold/indent-fold.js b/addon/fold/indent-fold.js index 7ae005d904..fabfa4f04e 100644 --- a/addon/fold/indent-fold.js +++ b/addon/fold/indent-fold.js @@ -1,27 +1,29 @@ CodeMirror.registerHelper("fold", "indent", function(cm, start) { - var lastLine = cm.lastLine(), - tabSize = cm.getOption("tabSize"), - firstLine = cm.getLine(start.line); - if (!tabSize || !firstLine) return; - var myIndent = CodeMirror.countColumn(firstLine, null, tabSize); - - function foldEnded(curColumn, prevColumn) { - return curColumn < myIndent || - (curColumn == myIndent && prevColumn >= myIndent) || - (curColumn > myIndent && i == lastLine); - } - - for (var i = start.line + 1; i <= lastLine; i++) { - var curColumn = CodeMirror.countColumn(cm.getLine(i), null, tabSize); - var prevColumn = CodeMirror.countColumn(cm.getLine(i-1), null, tabSize); - - if (foldEnded(curColumn, prevColumn)) { - var lastFoldLineNumber = curColumn > myIndent && i == lastLine ? i : i-1; - var lastFoldLine = cm.getLine(lastFoldLineNumber); - return {from: CodeMirror.Pos(start.line, firstLine.length), - to: CodeMirror.Pos(lastFoldLineNumber, lastFoldLine.length)}; + var tabSize = cm.getOption("tabSize"), firstLine = cm.getLine(start.line); + var getIndent = function(lineNum) { + return CodeMirror.countColumn(lineNum, null, tabSize); + }; + var myIndent = getIndent(firstLine); + var lastLineInFold = null; + // Go through lines until we find a line that definitely doesn't belong in + // the block we're folding, or to the end. + for (var i = start.line + 1, end = cm.lineCount(); i < end; ++i) { + var curLine = cm.getLine(i); + var curIndent = getIndent(curLine); + if (curIndent > myIndent) { + // Lines with a greater indent are considered part of the block. + lastLineInFold = i; + } else if (curIndent == 0 && curLine.length == 0) { + // Empty lines might be breaks within the block we're trying to fold. + } else { + // A non-empty line at an indent equal to or less than ours marks the + // start of another block. + break; } } + return { + from: CodeMirror.Pos(start.line, firstLine.length), + to: CodeMirror.Pos(lastLineInFold, cm.getLine(lastLineInFold).length) + }; }); - CodeMirror.indentRangeFinder = CodeMirror.fold.indent; // deprecated From 5b875f598e94ee4a4eb74faf69065c84eb7b7a0e Mon Sep 17 00:00:00 2001 From: Marijn Haverbeke Date: Wed, 6 Nov 2013 08:51:57 +0100 Subject: [PATCH 1625/5780] [indent-fold addon] Consider all-whitespace lines empty lines --- addon/fold/indent-fold.js | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/addon/fold/indent-fold.js b/addon/fold/indent-fold.js index fabfa4f04e..3787b3f378 100644 --- a/addon/fold/indent-fold.js +++ b/addon/fold/indent-fold.js @@ -13,7 +13,7 @@ CodeMirror.registerHelper("fold", "indent", function(cm, start) { if (curIndent > myIndent) { // Lines with a greater indent are considered part of the block. lastLineInFold = i; - } else if (curIndent == 0 && curLine.length == 0) { + } else if (!/\S/.test(curLine)) { // Empty lines might be breaks within the block we're trying to fold. } else { // A non-empty line at an indent equal to or less than ours marks the From 47a3734c3286d7525ba9451ff0ce25984d22f1e5 Mon Sep 17 00:00:00 2001 From: Marijn Haverbeke Date: Wed, 6 Nov 2013 08:54:57 +0100 Subject: [PATCH 1626/5780] [indent-fold addon] Return nothing on edge cases --- addon/fold/indent-fold.js | 5 +++-- 1 file changed, 3 insertions(+), 2 deletions(-) diff --git a/addon/fold/indent-fold.js b/addon/fold/indent-fold.js index 3787b3f378..1bd600be42 100644 --- a/addon/fold/indent-fold.js +++ b/addon/fold/indent-fold.js @@ -1,5 +1,6 @@ CodeMirror.registerHelper("fold", "indent", function(cm, start) { var tabSize = cm.getOption("tabSize"), firstLine = cm.getLine(start.line); + if (!/\S/.test(firstLine)) return; var getIndent = function(lineNum) { return CodeMirror.countColumn(lineNum, null, tabSize); }; @@ -7,7 +8,7 @@ CodeMirror.registerHelper("fold", "indent", function(cm, start) { var lastLineInFold = null; // Go through lines until we find a line that definitely doesn't belong in // the block we're folding, or to the end. - for (var i = start.line + 1, end = cm.lineCount(); i < end; ++i) { + for (var i = start.line + 1, end = cm.lastLine(); i <= end; ++i) { var curLine = cm.getLine(i); var curIndent = getIndent(curLine); if (curIndent > myIndent) { @@ -21,7 +22,7 @@ CodeMirror.registerHelper("fold", "indent", function(cm, start) { break; } } - return { + if (lastLineInFold) return { from: CodeMirror.Pos(start.line, firstLine.length), to: CodeMirror.Pos(lastLineInFold, cm.getLine(lastLineInFold).length) }; From c63d7e342079143eed9a18fd115930366be5f6c3 Mon Sep 17 00:00:00 2001 From: Marijn Haverbeke Date: Mon, 11 Nov 2013 08:21:55 +0100 Subject: [PATCH 1627/5780] Clip scroll positions given to scrollTo to scrollable space Closes #1934 --- lib/codemirror.js | 6 ++++-- 1 file changed, 4 insertions(+), 2 deletions(-) diff --git a/lib/codemirror.js b/lib/codemirror.js index 254c0e03fc..0b65bb13cf 100644 --- a/lib/codemirror.js +++ b/lib/codemirror.js @@ -1387,8 +1387,10 @@ window.CodeMirror = (function() { } if (!updated && op.selectionChanged) updateSelection(cm); if (op.updateScrollPos) { - display.scroller.scrollTop = display.scrollbarV.scrollTop = doc.scrollTop = newScrollPos.scrollTop; - display.scroller.scrollLeft = display.scrollbarH.scrollLeft = doc.scrollLeft = newScrollPos.scrollLeft; + var top = Math.max(0, Math.min(display.scroller.scrollHeight - display.scroller.clientHeight, newScrollPos.scrollTop)); + var left = Math.max(0, Math.min(display.scroller.scrollWidth - display.scroller.clientWidth, newScrollPos.scrollLeft)); + display.scroller.scrollTop = display.scrollbarV.scrollTop = doc.scrollTop = top; + display.scroller.scrollLeft = display.scrollbarH.scrollLeft = doc.scrollLeft = left; alignHorizontally(cm); if (op.scrollToPos) scrollPosIntoView(cm, clipPos(cm.doc, op.scrollToPos.from), From 66a5cd630a8a2423cd79eccb170df4d7ee1bb83c Mon Sep 17 00:00:00 2001 From: Marijn Haverbeke Date: Mon, 11 Nov 2013 08:24:19 +0100 Subject: [PATCH 1628/5780] Add more extending unicode ranges --- lib/codemirror.js | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/lib/codemirror.js b/lib/codemirror.js index 0b65bb13cf..6d0e9d6459 100644 --- a/lib/codemirror.js +++ b/lib/codemirror.js @@ -5491,7 +5491,7 @@ window.CodeMirror = (function() { return true; } - var isExtendingChar = /[\u0300-\u036F\u0483-\u0487\u0488-\u0489\u0591-\u05BD\u05BF\u05C1-\u05C2\u05C4-\u05C5\u05C7\u0610-\u061A\u064B-\u065F\u0670\u06D6-\u06DC\u06DF-\u06E4\u06E7-\u06E8\u06EA-\u06ED\uA66F\uA670-\uA672\uA674-\uA67D\uA69F\udc00-\udfff]/; + var isExtendingChar = /[\u0300-\u036F\u0483-\u0487\u0488-\u0489\u0591-\u05BD\u05BF\u05C1-\u05C2\u05C4-\u05C5\u05C7\u0610-\u061A\u064B-\u065F\u0670\u06D6-\u06DC\u06DF-\u06E4\u06E7-\u06E8\u06EA-\u06ED\uA66F\u1DC0–\u1DFF\u20D0–\u20FF\uA670-\uA672\uA674-\uA67D\uA69F\udc00-\udfff\uFE20–\uFE2F]/; // DOM UTILITIES From b8d83f2e461e87f5b87773301f27792490963a35 Mon Sep 17 00:00:00 2001 From: Marijn Haverbeke Date: Mon, 11 Nov 2013 08:31:25 +0100 Subject: [PATCH 1629/5780] Document execCommand Issue #1935 --- doc/manual.html | 3 +++ 1 file changed, 3 insertions(+) diff --git a/doc/manual.html b/doc/manual.html index ba8789a836..d79265fd6b 100644 --- a/doc/manual.html +++ b/doc/manual.html @@ -1576,6 +1576,9 @@

    Miscellaneous methods

    given an argument), or sets the overwrite mode to a specific state (when given an argument). +
    cm.execCommand(name: string)
    +
    Runs the command with the given name on the editor.
    +
    doc.posFromIndex(index: integer) → {line, ch}
    Calculates and returns a {line, ch} object for a zero-based index who's value is relative to the start of the From 398915a47ed46d86decadadf24aa7059f26e96c3 Mon Sep 17 00:00:00 2001 From: Andy Joslin Date: Thu, 7 Nov 2013 13:48:24 -0500 Subject: [PATCH 1630/5780] [dialog addon] Add cm.openNotification: timed & unobtrusive dialog --- addon/dialog/dialog.js | 35 +++++++++++++++++++++++++++++++++++ 1 file changed, 35 insertions(+) diff --git a/addon/dialog/dialog.js b/addon/dialog/dialog.js index 71e2287447..14ab724849 100644 --- a/addon/dialog/dialog.js +++ b/addon/dialog/dialog.js @@ -77,4 +77,39 @@ CodeMirror.on(b, "focus", function() { ++blurring; }); } }); + + /* + * openNotification + * Opens a notification, that can be closed with an optional timer + * (default 5000ms timer) and always closes on click. + * + * If a notification is opened while another is opened, it will close the + * currently opened one and open the new one immediately. + */ + var currentNotificationClose; + CodeMirror.defineExtension("openNotification", function(template, callback, options) { + var dialog = dialogDiv(this, template, options && options.bottom); + var duration = options && (options.duration === undefined ? 5000 : options.duration); + var closed = false, me = this, doneTimer; + + function close() { + if (closed) return; + closed = true; + clearTimeout(doneTimer); + doneTimer = null; + if (callback) callback(me); + dialog.parentNode.removeChild(dialog); + } + + if (currentNotificationClose) currentNotificationClose(); + currentNotificationClose = close; + + CodeMirror.on(dialog, 'click', function(e) { + CodeMirror.e_preventDefault(e); + close(); + }); + if (duration) { + doneTimer = setTimeout(close, options.duration); + } + }); })(); From 598648f13032852b285cec6509e3c7930e15d437 Mon Sep 17 00:00:00 2001 From: Marijn Haverbeke Date: Mon, 11 Nov 2013 08:46:21 +0100 Subject: [PATCH 1631/5780] [dialog addon] Slight modifications to openNotification Remove callback argument, save currently open dialog in per-editor state. --- addon/dialog/dialog.js | 22 ++++++++++++---------- doc/manual.html | 13 ++++++++----- 2 files changed, 20 insertions(+), 15 deletions(-) diff --git a/addon/dialog/dialog.js b/addon/dialog/dialog.js index 14ab724849..ced394d3fc 100644 --- a/addon/dialog/dialog.js +++ b/addon/dialog/dialog.js @@ -14,7 +14,14 @@ return dialog; } + function closeNotification(cm, newVal) { + if (cm.state.currentNotificationClose) + cm.state.currentNotificationClose(); + cm.state.currentNotificationClose = newVal; + } + CodeMirror.defineExtension("openDialog", function(template, callback, options) { + closeNotification(this, null); var dialog = dialogDiv(this, template, options && options.bottom); var closed = false, me = this; function close() { @@ -51,6 +58,7 @@ }); CodeMirror.defineExtension("openConfirm", function(template, callbacks, options) { + closeNotification(this, null); var dialog = dialogDiv(this, template, options && options.bottom); var buttons = dialog.getElementsByTagName("button"); var closed = false, me = this, blurring = 1; @@ -86,30 +94,24 @@ * If a notification is opened while another is opened, it will close the * currently opened one and open the new one immediately. */ - var currentNotificationClose; - CodeMirror.defineExtension("openNotification", function(template, callback, options) { + CodeMirror.defineExtension("openNotification", function(template, options) { + closeNotification(this, close); var dialog = dialogDiv(this, template, options && options.bottom); var duration = options && (options.duration === undefined ? 5000 : options.duration); - var closed = false, me = this, doneTimer; + var closed = false, doneTimer; function close() { if (closed) return; closed = true; clearTimeout(doneTimer); - doneTimer = null; - if (callback) callback(me); dialog.parentNode.removeChild(dialog); } - if (currentNotificationClose) currentNotificationClose(); - currentNotificationClose = close; - CodeMirror.on(dialog, 'click', function(e) { CodeMirror.e_preventDefault(e); close(); }); - if (duration) { + if (duration) doneTimer = setTimeout(close, options.duration); - } }); })(); diff --git a/doc/manual.html b/doc/manual.html index d79265fd6b..80de6d950f 100644 --- a/doc/manual.html +++ b/doc/manual.html @@ -1708,11 +1708,14 @@

    Addons

    dialog/dialog.js
    Provides a very simple way to query users for text input. - Adds an openDialog method to CodeMirror instances, - which can be called with an HTML fragment that provides the - prompt (should include an input tag), and a - callback function that is called when text has been entered. - Depends on addon/dialog/dialog.css.
    + Adds an openDialog method to + CodeMirror instances, which can be called with an HTML fragment + that provides the prompt (should include an input + tag), and a callback function that is called when text has been + entered. Also adds + an openNotification function that + simply shows an HTML fragment as a notification. Depends + on addon/dialog/dialog.css.
    search/searchcursor.js
    Adds the getSearchCursor(query, start, caseFold) → From 11c50cb69b406794eac7421b2997407a8d17528f Mon Sep 17 00:00:00 2001 From: Andy Joslin Date: Thu, 7 Nov 2013 13:49:19 -0500 Subject: [PATCH 1632/5780] [vim mode] use openNotification instead of openConfirm for errors --- keymap/vim.js | 7 +++---- 1 file changed, 3 insertions(+), 4 deletions(-) diff --git a/keymap/vim.js b/keymap/vim.js index e67a46ed7f..dab10e21a4 100644 --- a/keymap/vim.js +++ b/keymap/vim.js @@ -2726,10 +2726,9 @@ return regexp; } function showConfirm(cm, text) { - if (cm.openConfirm) { - cm.openConfirm('' + text + - ' ', function() {}, - {bottom: true}); + if (cm.openNotification) { + cm.openNotification('' + text + '', + {bottom: true, duration: 5000}); } else { alert(text); } From 74a3f2261d8f8646a282e089cf8d8c777e498274 Mon Sep 17 00:00:00 2001 From: Brandon Frohs Date: Thu, 7 Nov 2013 17:13:09 -0500 Subject: [PATCH 1633/5780] [markdown mode] `\n* ` should not toggle em state. Closes #1920. --- mode/markdown/markdown.js | 7 ++++++- mode/markdown/test.js | 4 ++++ 2 files changed, 10 insertions(+), 1 deletion(-) diff --git a/mode/markdown/markdown.js b/mode/markdown/markdown.js index bf1750d5b6..0d60b3db8f 100644 --- a/mode/markdown/markdown.js +++ b/mode/markdown/markdown.js @@ -257,6 +257,9 @@ CodeMirror.defineMode("markdown", function(cmCfg, modeCfg) { state.taskOpen = false; state.taskClosed = false; + // Get sol() value now, before character is consumed + var sol = stream.sol(); + var ch = stream.next(); if (ch === '\\') { @@ -355,7 +358,9 @@ CodeMirror.defineMode("markdown", function(cmCfg, modeCfg) { } var t = getType(state); if (ch === '*' || (ch === '_' && !ignoreUnderscore)) { - if (state.strong === ch && stream.eat(ch)) { // Remove STRONG + if (sol && stream.peek() === ' ') { + // Do nothing, surrounded by newline and space + } else if (state.strong === ch && stream.eat(ch)) { // Remove STRONG state.strong = false; return t; } else if (!state.strong && stream.eat(ch)) { // Add STRONG diff --git a/mode/markdown/test.js b/mode/markdown/test.js index f167917289..081be96be1 100644 --- a/mode/markdown/test.js +++ b/mode/markdown/test.js @@ -577,6 +577,10 @@ MT("emEscapedBySpaceOut", "foo _ bar[em _hello_]world"); + MT("emEscapedByNewline", + "foo", + "_ bar[em _hello_]world"); + // Unclosed emphasis characters // Instead of simply marking as EM / STRONG, it would be nice to have an // incomplete flag for EM and STRONG, that is styled slightly different. From a7120a79dc85e7caccc639e9192a63a6c209308f Mon Sep 17 00:00:00 2001 From: Marijn Haverbeke Date: Mon, 11 Nov 2013 08:55:07 +0100 Subject: [PATCH 1634/5780] [real-world uses] Add MVC Playground --- doc/realworld.html | 1 + 1 file changed, 1 insertion(+) diff --git a/doc/realworld.html b/doc/realworld.html index 4e0896c377..6e23106793 100644 --- a/doc/realworld.html +++ b/doc/realworld.html @@ -88,6 +88,7 @@

    CodeMirror real-world uses

  • Mergely (interactive diffing)
  • MIHTool (iOS web-app debugging tool)
  • Mongo MapReduce WebBrowser
  • +
  • MVC Playground
  • My2ndGeneration (social coding)
  • Navigate CMS
  • nodeMirror (IDE project)
  • From 8f491df2a175a942c4d3b51781998bb1ad868054 Mon Sep 17 00:00:00 2001 From: Michael Zhou Date: Sat, 9 Nov 2013 05:03:52 -0500 Subject: [PATCH 1635/5780] [clike demo] Fix duplicate variable names for C and C++ editors The C editor and the C++ editor have the same variable name, so the latter shadows the former. Fixed by renaming both. --- mode/clike/index.html | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/mode/clike/index.html b/mode/clike/index.html index 45add4910b..93bd718a0a 100644 --- a/mode/clike/index.html +++ b/mode/clike/index.html @@ -165,12 +165,12 @@

    Java example

    ]", + "[link ]", + "[tag&bracket <][tag div][tag&bracket >]", + "[tag&bracket ]"); + })(); From 60ab165bfa2bfc81b97d3b2b2d16c9665002adf5 Mon Sep 17 00:00:00 2001 From: Marijn Haverbeke Date: Tue, 6 May 2014 11:04:27 +0200 Subject: [PATCH 2340/5780] [dylan mode] Add module loading shim Closes #2533 --- mode/dylan/dylan.js | 12 ++++++++++++ 1 file changed, 12 insertions(+) diff --git a/mode/dylan/dylan.js b/mode/dylan/dylan.js index d81ef0cbcb..ccf9679181 100644 --- a/mode/dylan/dylan.js +++ b/mode/dylan/dylan.js @@ -1,3 +1,13 @@ +(function(mod) { + if (typeof exports == "object" && typeof module == "object") // CommonJS + mod(require("../../lib/codemirror")); + else if (typeof define == "function" && define.amd) // AMD + define(["../../lib/codemirror"], mod); + else // Plain browser env + mod(CodeMirror); +})(function(CodeMirror) { +"use strict"; + CodeMirror.defineMode("dylan", function(_config) { // Words var words = { @@ -282,3 +292,5 @@ CodeMirror.defineMode("dylan", function(_config) { }); CodeMirror.defineMIME("text/x-dylan", "dylan"); + +}); From 6abb339705fd234de93b14c28f37a5a212b65629 Mon Sep 17 00:00:00 2001 From: binny Date: Mon, 5 May 2014 21:55:30 +0530 Subject: [PATCH 2341/5780] [vim] Visual paste operation with unit tests --- keymap/vim.js | 61 +++++++++++++++++++++++++++++------------------- test/vim_test.js | 25 ++++++++++++++++++++ 2 files changed, 62 insertions(+), 24 deletions(-) diff --git a/keymap/vim.js b/keymap/vim.js index c78d76af94..1ea39c1ef0 100644 --- a/keymap/vim.js +++ b/keymap/vim.js @@ -2051,7 +2051,7 @@ } this.enterInsertMode(cm, { repeat: actionArgs.repeat }, vim); }, - paste: function(cm, actionArgs) { + paste: function(cm, actionArgs, vim) { var cur = copyCursor(cm.getCursor()); var register = vimGlobalState.registerController.getRegister( actionArgs.registerName); @@ -2092,7 +2092,9 @@ } var linewise = register.linewise; if (linewise) { - if (actionArgs.after) { + if(vim.visualMode) { + text = vim.visualLine ? text.slice(0, -1) : '\n' + text.slice(0, text.length - 1) + '\n'; + } else if (actionArgs.after) { // Move the newline at the end to the start instead, and paste just // before the newline character of the line we are on right now. text = '\n' + text.slice(0, text.length - 1); @@ -2103,24 +2105,35 @@ } else { cur.ch += actionArgs.after ? 1 : 0; } - cm.replaceRange(text, cur); - // Now fine tune the cursor to where we want it. var curPosFinal; var idx; - if (linewise && actionArgs.after) { - curPosFinal = Pos( - cur.line + 1, - findFirstNonWhiteSpaceCharacter(cm.getLine(cur.line + 1))); - } else if (linewise && !actionArgs.after) { - curPosFinal = Pos( - cur.line, - findFirstNonWhiteSpaceCharacter(cm.getLine(cur.line))); - } else if (!linewise && actionArgs.after) { - idx = cm.indexFromPos(cur); - curPosFinal = cm.posFromIndex(idx + text.length - 1); + if (vim.visualMode) { + var selectedArea = getSelectedAreaRange(cm, vim); + var selectionStart = selectedArea[0]; + var selectionEnd = selectedArea[1]; + // push the previously selected text to unnamed register + vimGlobalState.registerController.unnamedRegister.setText(cm.getRange(selectionStart, selectionEnd)); + cm.replaceRange(text, selectionStart, selectionEnd); + curPosFinal = cm.posFromIndex(cm.indexFromPos(selectionStart) + text.length - 1); + if(linewise)curPosFinal.ch=0; } else { - idx = cm.indexFromPos(cur); - curPosFinal = cm.posFromIndex(idx + text.length); + cm.replaceRange(text, cur); + // Now fine tune the cursor to where we want it. + if (linewise && actionArgs.after) { + curPosFinal = Pos( + cur.line + 1, + findFirstNonWhiteSpaceCharacter(cm.getLine(cur.line + 1))); + } else if (linewise && !actionArgs.after) { + curPosFinal = Pos( + cur.line, + findFirstNonWhiteSpaceCharacter(cm.getLine(cur.line))); + } else if (!linewise && actionArgs.after) { + idx = cm.indexFromPos(cur); + curPosFinal = cm.posFromIndex(idx + text.length - 1); + } else { + idx = cm.indexFromPos(cur); + curPosFinal = cm.posFromIndex(idx + text.length); + } } cm.setCursor(curPosFinal); }, @@ -2220,13 +2233,6 @@ var selectionStart = selectedAreaRange[0]; var selectionEnd = selectedAreaRange[1]; var toLower = actionArgs.toLower; - if (cursorIsBefore(selectionEnd, selectionStart)) { - var tmp = selectionStart; - selectionStart = selectionEnd; - selectionEnd = tmp; - } else { - selectionEnd = cm.clipPos(Pos(selectionEnd.line, selectionEnd.ch+1)); - } var text = cm.getRange(selectionStart, selectionEnd); cm.replaceRange(toLower ? text.toLowerCase() : text.toUpperCase(), selectionStart, selectionEnd); cm.setCursor(selectionStart); @@ -2324,6 +2330,13 @@ return [{line: selectionStart.line, ch: 0}, {line: selectionEnd.line, ch: lineLength(cm, selectionEnd.line)}]; } } else { + if (cursorIsBefore(selectionEnd, selectionStart)) { + var tmp = selectionStart; + selectionStart = selectionEnd; + selectionEnd = tmp; + } else { + selectionEnd = cm.clipPos(Pos(selectionEnd.line, selectionEnd.ch+1)); + } exitVisualMode(cm); } return [selectionStart, selectionEnd]; diff --git a/test/vim_test.js b/test/vim_test.js index 71cd5ed941..a3f3fed3ad 100644 --- a/test/vim_test.js +++ b/test/vim_test.js @@ -1639,6 +1639,31 @@ testVim('uppercase/lowercase_visual', function(cm, vim, helpers) { helpers.doKeys('V', 'U', 'j', '.'); eq('ABCDEF\nGHIJKL\nMnopq\nSHORT LINE\nLONG LINE OF TEXT', cm.getValue()); }, { value: 'abcdef\nghijkl\nmnopq\nshort line\nlong line of text'}); +testVim('visual_paste', function(cm, vim, helpers) { + cm.setCursor(0, 0); + helpers.doKeys('v', 'l', 'l', 'y', 'j', 'v', 'l', 'p'); + helpers.assertCursorAt(1, 4); + eq('this is a\nunthi test for visual paste', cm.getValue()); + cm.setCursor(0, 0); + // in case of pasting whole line + helpers.doKeys('y', 'y'); + cm.setCursor(1, 6); + helpers.doKeys('v', 'l', 'l', 'l', 'p'); + helpers.assertCursorAt(2, 0); + eq('this is a\nunthi \nthis is a\n for visual paste', cm.getValue()); +}, { value: 'this is a\nunit test for visual paste'}); + +// This checks the contents of the register used to paste the text +testVim('v_paste_from_register', function(cm, vim, helpers) { + cm.setCursor(0, 0); + helpers.doKeys('"', 'a', 'y', 'w'); + cm.setCursor(1, 0); + helpers.doKeys('v', 'p'); + cm.openDialog = helpers.fakeOpenDialog('registers'); + cm.openNotification = helpers.fakeOpenNotification(function(text) { + is(/a\s+register/.test(text)); + }); +}, { value: 'register contents\nare not erased'}); testVim('S_normal', function(cm, vim, helpers) { cm.setCursor(0, 1); helpers.doKeys('j', 'S'); From 6db5ec2870fc78977c817e35440185e26bff9175 Mon Sep 17 00:00:00 2001 From: binny Date: Wed, 30 Apr 2014 06:01:39 +0530 Subject: [PATCH 2342/5780] [vim] support for append action command in visual mode added --- keymap/vim.js | 8 ++++++++ test/vim_test.js | 7 +++++++ 2 files changed, 15 insertions(+) diff --git a/keymap/vim.js b/keymap/vim.js index 1ea39c1ef0..c48506adaa 100644 --- a/keymap/vim.js +++ b/keymap/vim.js @@ -271,6 +271,7 @@ actionArgs: { insertAt: 'charAfter' }}, { keys: ['A'], type: 'action', action: 'enterInsertMode', isEdit: true, actionArgs: { insertAt: 'eol' }}, + { keys: ['A'], type: 'action', action: 'enterInsertMode', isEdit: true, actionArgs: { insertAt: 'endOfSelectedArea' }, context: 'visual' }, { keys: ['i'], type: 'action', action: 'enterInsertMode', isEdit: true, actionArgs: { insertAt: 'inplace' }}, { keys: ['I'], type: 'action', action: 'enterInsertMode', isEdit: true, @@ -1914,6 +1915,13 @@ cm.setCursor(offsetCursor(cm.getCursor(), 0, 1)); } else if (insertAt == 'firstNonBlank') { cm.setCursor(motions.moveToFirstNonWhiteSpaceCharacter(cm)); + } else if (insertAt == 'endOfSelectedArea') { + var selectionEnd = cm.getCursor('head'); + var selectionStart = cm.getCursor('anchor'); + var cursor = cm.getCursor(); + cursor = cursorIsBefore(selectionStart, selectionEnd) ? Pos(cursor.line, selectionEnd.ch+1) : Pos(cursor.line, selectionEnd.ch); + cm.setCursor(cursor); + exitVisualMode(cm); } cm.setOption('keyMap', 'vim-insert'); cm.setOption('disableInput', false); diff --git a/test/vim_test.js b/test/vim_test.js index a3f3fed3ad..fecc52ba35 100644 --- a/test/vim_test.js +++ b/test/vim_test.js @@ -1141,6 +1141,13 @@ testVim('a_eol', function(cm, vim, helpers) { helpers.assertCursorAt(0, lines[0].length); eq('vim-insert', cm.getOption('keyMap')); }); +testVim('a_endOfSelectedArea', function(cm, vim, helpers) { + cm.setCursor(0, 0); + helpers.doKeys('v', 'j', 'l'); + helpers.doKeys('A'); + helpers.assertCursorAt(1, 2); + eq('vim-insert', cm.getOption('keyMap')); +}, {value: 'foo\nbar'}); testVim('i', function(cm, vim, helpers) { cm.setCursor(0, 1); helpers.doKeys('i'); From 546eb6ce365d860d1649f0b6e22cd6b1f7879a21 Mon Sep 17 00:00:00 2001 From: binny Date: Fri, 2 May 2014 03:32:33 +0530 Subject: [PATCH 2343/5780] [vim] Resolve upward selection bug --- keymap/vim.js | 5 ++--- 1 file changed, 2 insertions(+), 3 deletions(-) diff --git a/keymap/vim.js b/keymap/vim.js index c48506adaa..e33d269089 100644 --- a/keymap/vim.js +++ b/keymap/vim.js @@ -1918,9 +1918,8 @@ } else if (insertAt == 'endOfSelectedArea') { var selectionEnd = cm.getCursor('head'); var selectionStart = cm.getCursor('anchor'); - var cursor = cm.getCursor(); - cursor = cursorIsBefore(selectionStart, selectionEnd) ? Pos(cursor.line, selectionEnd.ch+1) : Pos(cursor.line, selectionEnd.ch); - cm.setCursor(cursor); + selectionEnd = cursorIsBefore(selectionStart, selectionEnd) ? Pos(selectionEnd.line, selectionEnd.ch+1) : (selectionEnd.line < selectionStart.line ? Pos(selectionStart.line, 0) : selectionEnd); + cm.setCursor(selectionEnd); exitVisualMode(cm); } cm.setOption('keyMap', 'vim-insert'); From 41a29b0132445f8f10a5fc4b83f7c1e456171d03 Mon Sep 17 00:00:00 2001 From: Marijn Haverbeke Date: Thu, 8 May 2014 12:22:20 +0200 Subject: [PATCH 2344/5780] Abort wheel delta measurement when scrolling explicitly Issue #2537 --- lib/codemirror.js | 4 ++++ 1 file changed, 4 insertions(+) diff --git a/lib/codemirror.js b/lib/codemirror.js index 4a843d3f63..0bf6f2d8e8 100644 --- a/lib/codemirror.js +++ b/lib/codemirror.js @@ -1927,6 +1927,10 @@ if (!updated && op.selectionChanged) updateSelection(cm); if (!updated && op.startHeight != cm.doc.height) updateScrollbars(cm); + // Abort mouse wheel delta measurement, when scrolling explicitly + if (display.wheelStartX != null && (op.scrollTop != null || op.scrollLeft != null || op.scrollToPos)) + display.wheelStartX = display.wheelStartY = null; + // Propagate the scroll position to the actual DOM scroller if (op.scrollTop != null && display.scroller.scrollTop != op.scrollTop) { var top = Math.max(0, Math.min(display.scroller.scrollHeight - display.scroller.clientHeight, op.scrollTop)); From 0efe7a57115a252abf033e16ea1d1a375cd5a229 Mon Sep 17 00:00:00 2001 From: Marijn Haverbeke Date: Fri, 9 May 2014 14:48:38 +0200 Subject: [PATCH 2345/5780] [sublime keybindings] Don't include zero-char selection bottoms in swapLine* Issue #2542 --- keymap/sublime.js | 16 ++++++++-------- test/sublime_test.js | 6 ++++++ 2 files changed, 14 insertions(+), 8 deletions(-) diff --git a/keymap/sublime.js b/keymap/sublime.js index eea46347fd..9158e27944 100644 --- a/keymap/sublime.js +++ b/keymap/sublime.js @@ -184,9 +184,12 @@ }; cmds[map["Shift-" + ctrl + "Up"] = "swapLineUp"] = function(cm) { - var ranges = cm.listSelections(), linesToMove = [], at = cm.firstLine() - 1; + var ranges = cm.listSelections(), linesToMove = [], at = cm.firstLine() - 1, newSels = []; for (var i = 0; i < ranges.length; i++) { var range = ranges[i], from = range.from().line - 1, to = range.to().line; + newSels.push({anchor: Pos(range.anchor.line - 1, range.anchor.ch), + head: Pos(range.head.line - 1, range.head.ch)}); + if (range.to().ch == 0 && !range.empty()) --to; if (from > at) linesToMove.push(from, to); else if (linesToMove.length) linesToMove[linesToMove.length - 1] = to; at = to; @@ -196,16 +199,12 @@ var from = linesToMove[i], to = linesToMove[i + 1]; var line = cm.getLine(from); cm.replaceRange("", Pos(from, 0), Pos(from + 1, 0), "+swapLine"); - if (to > cm.lastLine()) { + if (to > cm.lastLine()) cm.replaceRange("\n" + line, Pos(cm.lastLine()), null, "+swapLine"); - var sels = cm.listSelections(), last = sels[sels.length - 1]; - var head = last.head.line == to ? Pos(to - 1) : last.head; - var anchor = last.anchor.line == to ? Pos(to - 1) : last.anchor; - cm.setSelections(sels.slice(0, sels.length - 1).concat([{head: head, anchor: anchor}])); - } else { + else cm.replaceRange(line + "\n", Pos(to, 0), null, "+swapLine"); - } } + cm.setSelections(newSels); cm.scrollIntoView(); }); }; @@ -214,6 +213,7 @@ var ranges = cm.listSelections(), linesToMove = [], at = cm.lastLine() + 1; for (var i = ranges.length - 1; i >= 0; i--) { var range = ranges[i], from = range.to().line + 1, to = range.from().line; + if (range.to().ch == 0 && !range.empty()) from--; if (from < at) linesToMove.push(from, to); else if (linesToMove.length) linesToMove[linesToMove.length - 1] = to; at = to; diff --git a/test/sublime_test.js b/test/sublime_test.js index f09504c51a..c93e041b87 100644 --- a/test/sublime_test.js +++ b/test/sublime_test.js @@ -178,6 +178,12 @@ 1, 0, 2, 0, 2, 2, 2, 2)); + stTest("swapLineEmptyBottomSel", "1\n2\n3", + setSel(0, 1, 1, 0), + "swapLineDown", val("2\n1\n3"), hasSel(1, 1, 2, 0), + "swapLineUp", val("1\n2\n3"), hasSel(0, 1, 1, 0), + "swapLineUp", val("1\n2\n3"), hasSel(0, 0, 0, 0)); + stTest("swapLineUpFromEnd", "a\nb\nc", Pos(2, 1), "swapLineUp", hasSel(1, 1, 1, 1), val("a\nc\nb")); From fea31914767042d0d42800ab82dc29b3e76a6a2b Mon Sep 17 00:00:00 2001 From: Marijn Haverbeke Date: Fri, 9 May 2014 15:18:36 +0200 Subject: [PATCH 2346/5780] [javascript-hint addon] Support an option that disables use of the current global scope --- addon/hint/javascript-hint.js | 6 ++++-- 1 file changed, 4 insertions(+), 2 deletions(-) diff --git a/addon/hint/javascript-hint.js b/addon/hint/javascript-hint.js index 305bb85a29..baf20b45dc 100644 --- a/addon/hint/javascript-hint.js +++ b/addon/hint/javascript-hint.js @@ -108,7 +108,8 @@ if (obj.type && obj.type.indexOf("variable") === 0) { if (options && options.additionalContext) base = options.additionalContext[obj.string]; - base = base || window[obj.string]; + if (!options || options.useGlobalScope !== false) + base = base || window[obj.string]; } else if (obj.type == "string") { base = ""; } else if (obj.type == "atom") { @@ -128,7 +129,8 @@ // (reading into JS mode internals to get at the local and global variables) for (var v = token.state.localVars; v; v = v.next) maybeAdd(v.name); for (var v = token.state.globalVars; v; v = v.next) maybeAdd(v.name); - gatherCompletions(window); + if (!options || options.useGlobalScope !== false) + gatherCompletions(window); forEach(keywords, maybeAdd); } return found; From a6faf989152a86a22f6155a775f3c16d724d1a88 Mon Sep 17 00:00:00 2001 From: Bem Jones-Bey Date: Tue, 6 May 2014 15:00:32 -0700 Subject: [PATCH 2347/5780] [css mode] Add more CSS Shapes properties Add shape-image-threshold and shape-margin. --- mode/css/css.js | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/mode/css/css.js b/mode/css/css.js index 3fae9687fd..8828329fc4 100644 --- a/mode/css/css.js +++ b/mode/css/css.js @@ -433,8 +433,8 @@ CodeMirror.defineMode("css", function(config, parserConfig) { "region-break-before", "region-break-inside", "region-fragment", "rendering-intent", "resize", "rest", "rest-after", "rest-before", "richness", "right", "rotation", "rotation-point", "ruby-align", "ruby-overhang", - "ruby-position", "ruby-span", "shape-inside", "shape-outside", "size", - "speak", "speak-as", "speak-header", + "ruby-position", "ruby-span", "shape-image-threshold", "shape-inside", "shape-margin", + "shape-outside", "size", "speak", "speak-as", "speak-header", "speak-numeral", "speak-punctuation", "speech-rate", "stress", "string-set", "tab-size", "table-layout", "target", "target-name", "target-new", "target-position", "text-align", "text-align-last", "text-decoration", From 81aeea3757b2c9826af68fb21fd5a170684f0aac Mon Sep 17 00:00:00 2001 From: Marijn Haverbeke Date: Fri, 9 May 2014 15:24:47 +0200 Subject: [PATCH 2348/5780] [real-world uses] Add Better Text Viewer and CrossUI --- doc/realworld.html | 2 ++ 1 file changed, 2 insertions(+) diff --git a/doc/realworld.html b/doc/realworld.html index ccfbf4be41..e6649b1f02 100644 --- a/doc/realworld.html +++ b/doc/realworld.html @@ -28,6 +28,7 @@

    CodeMirror real-world uses

  • Adobe Brackets (code editor)
  • Amber (JavaScript-based Smalltalk system)
  • APEye (tool for testing & documenting APIs)
  • +
  • Better Text Viewer (plain text reader app for Chrome)
  • Bitbucket (code hosting)
  • Blogger's template editor
  • BlueGriffon (HTML editor)
  • @@ -35,6 +36,7 @@

    CodeMirror real-world uses

  • Chrome DevTools
  • ClickHelp (technical writing tool)
  • Complete.ly playground
  • +
  • CrossUI (cross-platform UI builder)
  • Cruncher (notepad with calculation features)
  • Code per Node (Drupal module)
  • Codebug (PHP Xdebug front-end)
  • From 01e844f09eb74ad55a987779ead1c1fb17d5aea9 Mon Sep 17 00:00:00 2001 From: Marijn Haverbeke Date: Fri, 9 May 2014 15:52:39 +0200 Subject: [PATCH 2349/5780] Add license and version comment to all scripts --- addon/comment/comment.js | 3 +++ addon/comment/continuecomment.js | 3 +++ addon/dialog/dialog.js | 3 +++ addon/display/fullscreen.js | 3 +++ addon/display/placeholder.js | 3 +++ addon/display/rulers.js | 3 +++ addon/edit/closebrackets.js | 3 +++ addon/edit/closetag.js | 3 +++ addon/edit/continuelist.js | 3 +++ addon/edit/matchbrackets.js | 3 +++ addon/edit/matchtags.js | 3 +++ addon/edit/trailingspace.js | 3 +++ addon/fold/brace-fold.js | 3 +++ addon/fold/comment-fold.js | 3 +++ addon/fold/foldcode.js | 3 +++ addon/fold/foldgutter.js | 3 +++ addon/fold/indent-fold.js | 3 +++ addon/fold/markdown-fold.js | 3 +++ addon/fold/xml-fold.js | 3 +++ addon/hint/anyword-hint.js | 3 +++ addon/hint/css-hint.js | 3 +++ addon/hint/html-hint.js | 3 +++ addon/hint/javascript-hint.js | 3 +++ addon/hint/python-hint.js | 3 +++ addon/hint/show-hint.js | 3 +++ addon/hint/sql-hint.js | 3 +++ addon/hint/xml-hint.js | 3 +++ addon/lint/coffeescript-lint.js | 3 +++ addon/lint/css-lint.js | 3 +++ addon/lint/javascript-lint.js | 3 +++ addon/lint/json-lint.js | 3 +++ addon/lint/lint.js | 3 +++ addon/lint/yaml-lint.js | 3 +++ addon/merge/merge.js | 3 +++ addon/mode/loadmode.js | 3 +++ addon/mode/multiplex.js | 3 +++ addon/mode/multiplex_test.js | 3 +++ addon/mode/overlay.js | 3 +++ addon/runmode/colorize.js | 3 +++ addon/runmode/runmode-standalone.js | 3 +++ addon/runmode/runmode.js | 3 +++ addon/runmode/runmode.node.js | 3 +++ addon/scroll/scrollpastend.js | 3 +++ addon/search/match-highlighter.js | 3 +++ addon/search/search.js | 3 +++ addon/search/searchcursor.js | 3 +++ addon/selection/active-line.js | 3 +++ addon/selection/mark-selection.js | 3 +++ addon/tern/tern.js | 3 +++ addon/tern/worker.js | 3 +++ addon/wrap/hardwrap.js | 3 +++ bin/release | 15 +++++++++++++++ keymap/emacs.js | 3 +++ keymap/sublime.js | 3 +++ keymap/vim.js | 3 +++ lib/codemirror.js | 3 +++ mode/apl/apl.js | 3 +++ mode/asterisk/asterisk.js | 3 +++ mode/clike/clike.js | 3 +++ mode/clojure/clojure.js | 3 +++ mode/cobol/cobol.js | 3 +++ mode/coffeescript/coffeescript.js | 3 +++ mode/commonlisp/commonlisp.js | 3 +++ mode/css/css.js | 3 +++ mode/css/less_test.js | 3 +++ mode/css/scss_test.js | 3 +++ mode/css/test.js | 3 +++ mode/cypher/cypher.js | 3 +++ mode/d/d.js | 3 +++ mode/diff/diff.js | 3 +++ mode/django/django.js | 3 +++ mode/dtd/dtd.js | 3 +++ mode/dylan/dylan.js | 3 +++ mode/ecl/ecl.js | 3 +++ mode/eiffel/eiffel.js | 3 +++ mode/erlang/erlang.js | 3 +++ mode/fortran/fortran.js | 3 +++ mode/gas/gas.js | 3 +++ mode/gfm/gfm.js | 3 +++ mode/gfm/test.js | 3 +++ mode/gherkin/gherkin.js | 3 +++ mode/go/go.js | 3 +++ mode/groovy/groovy.js | 3 +++ mode/haml/haml.js | 3 +++ mode/haml/test.js | 3 +++ mode/haskell/haskell.js | 3 +++ mode/haxe/haxe.js | 3 +++ mode/htmlembedded/htmlembedded.js | 3 +++ mode/htmlmixed/htmlmixed.js | 3 +++ mode/http/http.js | 3 +++ mode/jade/jade.js | 3 +++ mode/javascript/javascript.js | 3 +++ mode/javascript/test.js | 3 +++ mode/jinja2/jinja2.js | 3 +++ mode/julia/julia.js | 3 +++ mode/livescript/livescript.js | 3 +++ mode/lua/lua.js | 3 +++ mode/markdown/markdown.js | 3 +++ mode/markdown/test.js | 3 +++ mode/meta.js | 3 +++ mode/mirc/mirc.js | 3 +++ mode/mllike/mllike.js | 3 +++ mode/nginx/nginx.js | 3 +++ mode/ntriples/ntriples.js | 3 +++ mode/octave/octave.js | 3 +++ mode/pascal/pascal.js | 3 +++ mode/pegjs/pegjs.js | 3 +++ mode/perl/perl.js | 3 +++ mode/php/php.js | 3 +++ mode/php/test.js | 3 +++ mode/pig/pig.js | 3 +++ mode/properties/properties.js | 3 +++ mode/puppet/puppet.js | 3 +++ mode/python/python.js | 3 +++ mode/q/q.js | 3 +++ mode/r/r.js | 3 +++ mode/rpm/rpm.js | 3 +++ mode/rst/rst.js | 3 +++ mode/ruby/ruby.js | 3 +++ mode/ruby/test.js | 3 +++ mode/rust/rust.js | 3 +++ mode/sass/sass.js | 3 +++ mode/scheme/scheme.js | 3 +++ mode/shell/shell.js | 3 +++ mode/shell/test.js | 3 +++ mode/sieve/sieve.js | 3 +++ mode/smalltalk/smalltalk.js | 3 +++ mode/smarty/smarty.js | 3 +++ mode/smartymixed/smartymixed.js | 3 +++ mode/solr/solr.js | 3 +++ mode/sparql/sparql.js | 3 +++ mode/sql/sql.js | 3 +++ mode/stex/stex.js | 3 +++ mode/stex/test.js | 3 +++ mode/tcl/tcl.js | 3 +++ mode/tiddlywiki/tiddlywiki.js | 3 +++ mode/tiki/tiki.js | 3 +++ mode/toml/toml.js | 3 +++ mode/turtle/turtle.js | 3 +++ mode/vb/vb.js | 3 +++ mode/vbscript/vbscript.js | 3 +++ mode/velocity/velocity.js | 3 +++ mode/verilog/test.js | 3 +++ mode/verilog/verilog.js | 3 +++ mode/xml/test.js | 3 +++ mode/xml/xml.js | 3 +++ mode/xquery/test.js | 3 +++ mode/xquery/xquery.js | 3 +++ mode/yaml/yaml.js | 3 +++ mode/z80/z80.js | 3 +++ test/lint/lint.js | 7 ++++++- 151 files changed, 468 insertions(+), 1 deletion(-) diff --git a/addon/comment/comment.js b/addon/comment/comment.js index 1eb9a05c5d..f56721206e 100644 --- a/addon/comment/comment.js +++ b/addon/comment/comment.js @@ -1,3 +1,6 @@ +// CodeMirror 4.1.1, copyright (c) by Marijn Haverbeke and others +// Distributed under an MIT license: http://codemirror.net/LICENSE + (function(mod) { if (typeof exports == "object" && typeof module == "object") // CommonJS mod(require("../../lib/codemirror")); diff --git a/addon/comment/continuecomment.js b/addon/comment/continuecomment.js index 42277267f5..5247a845c4 100644 --- a/addon/comment/continuecomment.js +++ b/addon/comment/continuecomment.js @@ -1,3 +1,6 @@ +// CodeMirror 4.1.1, copyright (c) by Marijn Haverbeke and others +// Distributed under an MIT license: http://codemirror.net/LICENSE + (function(mod) { if (typeof exports == "object" && typeof module == "object") // CommonJS mod(require("../../lib/codemirror")); diff --git a/addon/dialog/dialog.js b/addon/dialog/dialog.js index 586b7370dc..8fbdea168d 100644 --- a/addon/dialog/dialog.js +++ b/addon/dialog/dialog.js @@ -1,3 +1,6 @@ +// CodeMirror 4.1.1, copyright (c) by Marijn Haverbeke and others +// Distributed under an MIT license: http://codemirror.net/LICENSE + // Open simple dialogs on top of an editor. Relies on dialog.css. (function(mod) { diff --git a/addon/display/fullscreen.js b/addon/display/fullscreen.js index e39c6e162f..e7f22a7936 100644 --- a/addon/display/fullscreen.js +++ b/addon/display/fullscreen.js @@ -1,3 +1,6 @@ +// CodeMirror 4.1.1, copyright (c) by Marijn Haverbeke and others +// Distributed under an MIT license: http://codemirror.net/LICENSE + (function(mod) { if (typeof exports == "object" && typeof module == "object") // CommonJS mod(require("../../lib/codemirror")); diff --git a/addon/display/placeholder.js b/addon/display/placeholder.js index 0fdc9b0d5b..fbc0a8bdf0 100644 --- a/addon/display/placeholder.js +++ b/addon/display/placeholder.js @@ -1,3 +1,6 @@ +// CodeMirror 4.1.1, copyright (c) by Marijn Haverbeke and others +// Distributed under an MIT license: http://codemirror.net/LICENSE + (function(mod) { if (typeof exports == "object" && typeof module == "object") // CommonJS mod(require("../../lib/codemirror")); diff --git a/addon/display/rulers.js b/addon/display/rulers.js index 42cc2b9a90..e025415b51 100644 --- a/addon/display/rulers.js +++ b/addon/display/rulers.js @@ -1,3 +1,6 @@ +// CodeMirror 4.1.1, copyright (c) by Marijn Haverbeke and others +// Distributed under an MIT license: http://codemirror.net/LICENSE + (function(mod) { if (typeof exports == "object" && typeof module == "object") // CommonJS mod(require("../../lib/codemirror")); diff --git a/addon/edit/closebrackets.js b/addon/edit/closebrackets.js index 3cea887d1d..4caac60b98 100644 --- a/addon/edit/closebrackets.js +++ b/addon/edit/closebrackets.js @@ -1,3 +1,6 @@ +// CodeMirror 4.1.1, copyright (c) by Marijn Haverbeke and others +// Distributed under an MIT license: http://codemirror.net/LICENSE + (function(mod) { if (typeof exports == "object" && typeof module == "object") // CommonJS mod(require("../../lib/codemirror")); diff --git a/addon/edit/closetag.js b/addon/edit/closetag.js index c7c0701ba5..d64045ab1b 100644 --- a/addon/edit/closetag.js +++ b/addon/edit/closetag.js @@ -1,3 +1,6 @@ +// CodeMirror 4.1.1, copyright (c) by Marijn Haverbeke and others +// Distributed under an MIT license: http://codemirror.net/LICENSE + /** * Tag-closer extension for CodeMirror. * diff --git a/addon/edit/continuelist.js b/addon/edit/continuelist.js index 2946aa6a24..3dcf757397 100644 --- a/addon/edit/continuelist.js +++ b/addon/edit/continuelist.js @@ -1,3 +1,6 @@ +// CodeMirror 4.1.1, copyright (c) by Marijn Haverbeke and others +// Distributed under an MIT license: http://codemirror.net/LICENSE + (function(mod) { if (typeof exports == "object" && typeof module == "object") // CommonJS mod(require("../../lib/codemirror")); diff --git a/addon/edit/matchbrackets.js b/addon/edit/matchbrackets.js index dcdde81dfe..c1221ca4c0 100644 --- a/addon/edit/matchbrackets.js +++ b/addon/edit/matchbrackets.js @@ -1,3 +1,6 @@ +// CodeMirror 4.1.1, copyright (c) by Marijn Haverbeke and others +// Distributed under an MIT license: http://codemirror.net/LICENSE + (function(mod) { if (typeof exports == "object" && typeof module == "object") // CommonJS mod(require("../../lib/codemirror")); diff --git a/addon/edit/matchtags.js b/addon/edit/matchtags.js index 76a7b87c9a..a91d578365 100644 --- a/addon/edit/matchtags.js +++ b/addon/edit/matchtags.js @@ -1,3 +1,6 @@ +// CodeMirror 4.1.1, copyright (c) by Marijn Haverbeke and others +// Distributed under an MIT license: http://codemirror.net/LICENSE + (function(mod) { if (typeof exports == "object" && typeof module == "object") // CommonJS mod(require("../../lib/codemirror"), require("../fold/xml-fold")); diff --git a/addon/edit/trailingspace.js b/addon/edit/trailingspace.js index ec07221e30..e12162d33c 100644 --- a/addon/edit/trailingspace.js +++ b/addon/edit/trailingspace.js @@ -1,3 +1,6 @@ +// CodeMirror 4.1.1, copyright (c) by Marijn Haverbeke and others +// Distributed under an MIT license: http://codemirror.net/LICENSE + (function(mod) { if (typeof exports == "object" && typeof module == "object") // CommonJS mod(require("../../lib/codemirror")); diff --git a/addon/fold/brace-fold.js b/addon/fold/brace-fold.js index f0ee62029a..a0161e2c19 100644 --- a/addon/fold/brace-fold.js +++ b/addon/fold/brace-fold.js @@ -1,3 +1,6 @@ +// CodeMirror 4.1.1, copyright (c) by Marijn Haverbeke and others +// Distributed under an MIT license: http://codemirror.net/LICENSE + (function(mod) { if (typeof exports == "object" && typeof module == "object") // CommonJS mod(require("../../lib/codemirror")); diff --git a/addon/fold/comment-fold.js b/addon/fold/comment-fold.js index d72c5479a7..593539affd 100644 --- a/addon/fold/comment-fold.js +++ b/addon/fold/comment-fold.js @@ -1,3 +1,6 @@ +// CodeMirror 4.1.1, copyright (c) by Marijn Haverbeke and others +// Distributed under an MIT license: http://codemirror.net/LICENSE + (function(mod) { if (typeof exports == "object" && typeof module == "object") // CommonJS mod(require("../../lib/codemirror")); diff --git a/addon/fold/foldcode.js b/addon/fold/foldcode.js index 81094e2543..51cc3939b3 100644 --- a/addon/fold/foldcode.js +++ b/addon/fold/foldcode.js @@ -1,3 +1,6 @@ +// CodeMirror 4.1.1, copyright (c) by Marijn Haverbeke and others +// Distributed under an MIT license: http://codemirror.net/LICENSE + (function(mod) { if (typeof exports == "object" && typeof module == "object") // CommonJS mod(require("../../lib/codemirror")); diff --git a/addon/fold/foldgutter.js b/addon/fold/foldgutter.js index 9caba59aad..04ce8493e8 100644 --- a/addon/fold/foldgutter.js +++ b/addon/fold/foldgutter.js @@ -1,3 +1,6 @@ +// CodeMirror 4.1.1, copyright (c) by Marijn Haverbeke and others +// Distributed under an MIT license: http://codemirror.net/LICENSE + (function(mod) { if (typeof exports == "object" && typeof module == "object") // CommonJS mod(require("../../lib/codemirror"), require("./foldcode")); diff --git a/addon/fold/indent-fold.js b/addon/fold/indent-fold.js index d0130836a2..126ed4953c 100644 --- a/addon/fold/indent-fold.js +++ b/addon/fold/indent-fold.js @@ -1,3 +1,6 @@ +// CodeMirror 4.1.1, copyright (c) by Marijn Haverbeke and others +// Distributed under an MIT license: http://codemirror.net/LICENSE + (function(mod) { if (typeof exports == "object" && typeof module == "object") // CommonJS mod(require("../../lib/codemirror")); diff --git a/addon/fold/markdown-fold.js b/addon/fold/markdown-fold.js index 3bbf5b6077..ecee9e7e27 100644 --- a/addon/fold/markdown-fold.js +++ b/addon/fold/markdown-fold.js @@ -1,3 +1,6 @@ +// CodeMirror 4.1.1, copyright (c) by Marijn Haverbeke and others +// Distributed under an MIT license: http://codemirror.net/LICENSE + (function(mod) { if (typeof exports == "object" && typeof module == "object") // CommonJS mod(require("../../lib/codemirror")); diff --git a/addon/fold/xml-fold.js b/addon/fold/xml-fold.js index d554e2fc42..e1fd5756d8 100644 --- a/addon/fold/xml-fold.js +++ b/addon/fold/xml-fold.js @@ -1,3 +1,6 @@ +// CodeMirror 4.1.1, copyright (c) by Marijn Haverbeke and others +// Distributed under an MIT license: http://codemirror.net/LICENSE + (function(mod) { if (typeof exports == "object" && typeof module == "object") // CommonJS mod(require("../../lib/codemirror")); diff --git a/addon/hint/anyword-hint.js b/addon/hint/anyword-hint.js index 3ef979b524..0b6d872f3d 100644 --- a/addon/hint/anyword-hint.js +++ b/addon/hint/anyword-hint.js @@ -1,3 +1,6 @@ +// CodeMirror 4.1.1, copyright (c) by Marijn Haverbeke and others +// Distributed under an MIT license: http://codemirror.net/LICENSE + (function(mod) { if (typeof exports == "object" && typeof module == "object") // CommonJS mod(require("../../lib/codemirror")); diff --git a/addon/hint/css-hint.js b/addon/hint/css-hint.js index 96d9d52e1b..a93458819c 100644 --- a/addon/hint/css-hint.js +++ b/addon/hint/css-hint.js @@ -1,3 +1,6 @@ +// CodeMirror 4.1.1, copyright (c) by Marijn Haverbeke and others +// Distributed under an MIT license: http://codemirror.net/LICENSE + (function(mod) { if (typeof exports == "object" && typeof module == "object") // CommonJS mod(require("../../lib/codemirror"), require("../../mode/css/css")); diff --git a/addon/hint/html-hint.js b/addon/hint/html-hint.js index cbe7c61ad4..cfdcff9de2 100755 --- a/addon/hint/html-hint.js +++ b/addon/hint/html-hint.js @@ -1,3 +1,6 @@ +// CodeMirror 4.1.1, copyright (c) by Marijn Haverbeke and others +// Distributed under an MIT license: http://codemirror.net/LICENSE + (function(mod) { if (typeof exports == "object" && typeof module == "object") // CommonJS mod(require("../../lib/codemirror")); diff --git a/addon/hint/javascript-hint.js b/addon/hint/javascript-hint.js index baf20b45dc..29f3960902 100644 --- a/addon/hint/javascript-hint.js +++ b/addon/hint/javascript-hint.js @@ -1,3 +1,6 @@ +// CodeMirror 4.1.1, copyright (c) by Marijn Haverbeke and others +// Distributed under an MIT license: http://codemirror.net/LICENSE + (function(mod) { if (typeof exports == "object" && typeof module == "object") // CommonJS mod(require("../../lib/codemirror")); diff --git a/addon/hint/python-hint.js b/addon/hint/python-hint.js index eebfcc76dc..49dfb08f3a 100644 --- a/addon/hint/python-hint.js +++ b/addon/hint/python-hint.js @@ -1,3 +1,6 @@ +// CodeMirror 4.1.1, copyright (c) by Marijn Haverbeke and others +// Distributed under an MIT license: http://codemirror.net/LICENSE + (function(mod) { if (typeof exports == "object" && typeof module == "object") // CommonJS mod(require("../../lib/codemirror")); diff --git a/addon/hint/show-hint.js b/addon/hint/show-hint.js index 46b55648cb..4bede1fb03 100644 --- a/addon/hint/show-hint.js +++ b/addon/hint/show-hint.js @@ -1,3 +1,6 @@ +// CodeMirror 4.1.1, copyright (c) by Marijn Haverbeke and others +// Distributed under an MIT license: http://codemirror.net/LICENSE + (function(mod) { if (typeof exports == "object" && typeof module == "object") // CommonJS mod(require("../../lib/codemirror")); diff --git a/addon/hint/sql-hint.js b/addon/hint/sql-hint.js index a48d2b3cea..e527a878b9 100644 --- a/addon/hint/sql-hint.js +++ b/addon/hint/sql-hint.js @@ -1,3 +1,6 @@ +// CodeMirror 4.1.1, copyright (c) by Marijn Haverbeke and others +// Distributed under an MIT license: http://codemirror.net/LICENSE + (function(mod) { if (typeof exports == "object" && typeof module == "object") // CommonJS mod(require("../../lib/codemirror"), require("../../mode/sql/sql")); diff --git a/addon/hint/xml-hint.js b/addon/hint/xml-hint.js index 9cfd1e884f..de9f79eb3b 100644 --- a/addon/hint/xml-hint.js +++ b/addon/hint/xml-hint.js @@ -1,3 +1,6 @@ +// CodeMirror 4.1.1, copyright (c) by Marijn Haverbeke and others +// Distributed under an MIT license: http://codemirror.net/LICENSE + (function(mod) { if (typeof exports == "object" && typeof module == "object") // CommonJS mod(require("../../lib/codemirror")); diff --git a/addon/lint/coffeescript-lint.js b/addon/lint/coffeescript-lint.js index 6df17f8f84..2cc6314f06 100644 --- a/addon/lint/coffeescript-lint.js +++ b/addon/lint/coffeescript-lint.js @@ -1,3 +1,6 @@ +// CodeMirror 4.1.1, copyright (c) by Marijn Haverbeke and others +// Distributed under an MIT license: http://codemirror.net/LICENSE + // Depends on coffeelint.js from http://www.coffeelint.org/js/coffeelint.js // declare global: coffeelint diff --git a/addon/lint/css-lint.js b/addon/lint/css-lint.js index de9cd20d7a..93f2e5f5d8 100644 --- a/addon/lint/css-lint.js +++ b/addon/lint/css-lint.js @@ -1,3 +1,6 @@ +// CodeMirror 4.1.1, copyright (c) by Marijn Haverbeke and others +// Distributed under an MIT license: http://codemirror.net/LICENSE + // Depends on csslint.js from https://github.com/stubbornella/csslint // declare global: CSSLint diff --git a/addon/lint/javascript-lint.js b/addon/lint/javascript-lint.js index 86c863a4a2..ed62dd989c 100644 --- a/addon/lint/javascript-lint.js +++ b/addon/lint/javascript-lint.js @@ -1,3 +1,6 @@ +// CodeMirror 4.1.1, copyright (c) by Marijn Haverbeke and others +// Distributed under an MIT license: http://codemirror.net/LICENSE + (function(mod) { if (typeof exports == "object" && typeof module == "object") // CommonJS mod(require("../../lib/codemirror")); diff --git a/addon/lint/json-lint.js b/addon/lint/json-lint.js index 1f5f82d0ca..b548a8e439 100644 --- a/addon/lint/json-lint.js +++ b/addon/lint/json-lint.js @@ -1,3 +1,6 @@ +// CodeMirror 4.1.1, copyright (c) by Marijn Haverbeke and others +// Distributed under an MIT license: http://codemirror.net/LICENSE + // Depends on jsonlint.js from https://github.com/zaach/jsonlint // declare global: jsonlint diff --git a/addon/lint/lint.js b/addon/lint/lint.js index 393a689036..1682acf6ea 100644 --- a/addon/lint/lint.js +++ b/addon/lint/lint.js @@ -1,3 +1,6 @@ +// CodeMirror 4.1.1, copyright (c) by Marijn Haverbeke and others +// Distributed under an MIT license: http://codemirror.net/LICENSE + (function(mod) { if (typeof exports == "object" && typeof module == "object") // CommonJS mod(require("../../lib/codemirror")); diff --git a/addon/lint/yaml-lint.js b/addon/lint/yaml-lint.js index b53673af4b..3ada0eaf9f 100644 --- a/addon/lint/yaml-lint.js +++ b/addon/lint/yaml-lint.js @@ -1,3 +1,6 @@ +// CodeMirror 4.1.1, copyright (c) by Marijn Haverbeke and others +// Distributed under an MIT license: http://codemirror.net/LICENSE + (function(mod) { if (typeof exports == "object" && typeof module == "object") // CommonJS mod(require("../../lib/codemirror")); diff --git a/addon/merge/merge.js b/addon/merge/merge.js index fe3fcf4282..61e25e5d36 100644 --- a/addon/merge/merge.js +++ b/addon/merge/merge.js @@ -1,3 +1,6 @@ +// CodeMirror 4.1.1, copyright (c) by Marijn Haverbeke and others +// Distributed under an MIT license: http://codemirror.net/LICENSE + (function(mod) { if (typeof exports == "object" && typeof module == "object") // CommonJS mod(require("../../lib/codemirror")); diff --git a/addon/mode/loadmode.js b/addon/mode/loadmode.js index e08c281321..e49421b8fc 100644 --- a/addon/mode/loadmode.js +++ b/addon/mode/loadmode.js @@ -1,3 +1,6 @@ +// CodeMirror 4.1.1, copyright (c) by Marijn Haverbeke and others +// Distributed under an MIT license: http://codemirror.net/LICENSE + (function(mod) { if (typeof exports == "object" && typeof module == "object") // CommonJS mod(require("../../lib/codemirror")); diff --git a/addon/mode/multiplex.js b/addon/mode/multiplex.js index 07385c35f2..40601f4dfa 100644 --- a/addon/mode/multiplex.js +++ b/addon/mode/multiplex.js @@ -1,3 +1,6 @@ +// CodeMirror 4.1.1, copyright (c) by Marijn Haverbeke and others +// Distributed under an MIT license: http://codemirror.net/LICENSE + (function(mod) { if (typeof exports == "object" && typeof module == "object") // CommonJS mod(require("../../lib/codemirror")); diff --git a/addon/mode/multiplex_test.js b/addon/mode/multiplex_test.js index c0656357c7..a10012ce8c 100644 --- a/addon/mode/multiplex_test.js +++ b/addon/mode/multiplex_test.js @@ -1,3 +1,6 @@ +// CodeMirror 4.1.1, copyright (c) by Marijn Haverbeke and others +// Distributed under an MIT license: http://codemirror.net/LICENSE + (function() { CodeMirror.defineMode("markdown_with_stex", function(){ var inner = CodeMirror.getMode({}, "stex"); diff --git a/addon/mode/overlay.js b/addon/mode/overlay.js index e32fa830b3..82f754c461 100644 --- a/addon/mode/overlay.js +++ b/addon/mode/overlay.js @@ -1,3 +1,6 @@ +// CodeMirror 4.1.1, copyright (c) by Marijn Haverbeke and others +// Distributed under an MIT license: http://codemirror.net/LICENSE + // Utility function that allows modes to be combined. The mode given // as the base argument takes care of most of the normal mode // functionality, but a second (typically simple) mode is used, which diff --git a/addon/runmode/colorize.js b/addon/runmode/colorize.js index 0f9530b17b..eb99f29945 100644 --- a/addon/runmode/colorize.js +++ b/addon/runmode/colorize.js @@ -1,3 +1,6 @@ +// CodeMirror 4.1.1, copyright (c) by Marijn Haverbeke and others +// Distributed under an MIT license: http://codemirror.net/LICENSE + (function(mod) { if (typeof exports == "object" && typeof module == "object") // CommonJS mod(require("../../lib/codemirror"), require("./runmode")); diff --git a/addon/runmode/runmode-standalone.js b/addon/runmode/runmode-standalone.js index e36e00fbc6..3e1e746e95 100644 --- a/addon/runmode/runmode-standalone.js +++ b/addon/runmode/runmode-standalone.js @@ -1,3 +1,6 @@ +// CodeMirror 4.1.1, copyright (c) by Marijn Haverbeke and others +// Distributed under an MIT license: http://codemirror.net/LICENSE + window.CodeMirror = {}; (function() { diff --git a/addon/runmode/runmode.js b/addon/runmode/runmode.js index 5592a99a60..5406ff72fd 100644 --- a/addon/runmode/runmode.js +++ b/addon/runmode/runmode.js @@ -1,3 +1,6 @@ +// CodeMirror 4.1.1, copyright (c) by Marijn Haverbeke and others +// Distributed under an MIT license: http://codemirror.net/LICENSE + (function(mod) { if (typeof exports == "object" && typeof module == "object") // CommonJS mod(require("../../lib/codemirror")); diff --git a/addon/runmode/runmode.node.js b/addon/runmode/runmode.node.js index 15bf392313..f8c467f381 100644 --- a/addon/runmode/runmode.node.js +++ b/addon/runmode/runmode.node.js @@ -1,3 +1,6 @@ +// CodeMirror 4.1.1, copyright (c) by Marijn Haverbeke and others +// Distributed under an MIT license: http://codemirror.net/LICENSE + /* Just enough of CodeMirror to run runMode under node.js */ // declare global: StringStream diff --git a/addon/scroll/scrollpastend.js b/addon/scroll/scrollpastend.js index 467b7aa1cf..e9f4ecb68a 100644 --- a/addon/scroll/scrollpastend.js +++ b/addon/scroll/scrollpastend.js @@ -1,3 +1,6 @@ +// CodeMirror 4.1.1, copyright (c) by Marijn Haverbeke and others +// Distributed under an MIT license: http://codemirror.net/LICENSE + (function(mod) { if (typeof exports == "object" && typeof module == "object") // CommonJS mod(require("../../lib/codemirror")); diff --git a/addon/search/match-highlighter.js b/addon/search/match-highlighter.js index 83ab8c33c7..0dc28c10ba 100644 --- a/addon/search/match-highlighter.js +++ b/addon/search/match-highlighter.js @@ -1,3 +1,6 @@ +// CodeMirror 4.1.1, copyright (c) by Marijn Haverbeke and others +// Distributed under an MIT license: http://codemirror.net/LICENSE + // Highlighting text that matches the selection // // Defines an option highlightSelectionMatches, which, when enabled, diff --git a/addon/search/search.js b/addon/search/search.js index 7a1db6ee09..7bc3414c7b 100644 --- a/addon/search/search.js +++ b/addon/search/search.js @@ -1,3 +1,6 @@ +// CodeMirror 4.1.1, copyright (c) by Marijn Haverbeke and others +// Distributed under an MIT license: http://codemirror.net/LICENSE + // Define search commands. Depends on dialog.js or another // implementation of the openDialog method. diff --git a/addon/search/searchcursor.js b/addon/search/searchcursor.js index 899f44c4ab..f6df00a94e 100644 --- a/addon/search/searchcursor.js +++ b/addon/search/searchcursor.js @@ -1,3 +1,6 @@ +// CodeMirror 4.1.1, copyright (c) by Marijn Haverbeke and others +// Distributed under an MIT license: http://codemirror.net/LICENSE + (function(mod) { if (typeof exports == "object" && typeof module == "object") // CommonJS mod(require("../../lib/codemirror")); diff --git a/addon/selection/active-line.js b/addon/selection/active-line.js index a818f109b6..bafcb6b8c3 100644 --- a/addon/selection/active-line.js +++ b/addon/selection/active-line.js @@ -1,3 +1,6 @@ +// CodeMirror 4.1.1, copyright (c) by Marijn Haverbeke and others +// Distributed under an MIT license: http://codemirror.net/LICENSE + // Because sometimes you need to style the cursor's line. // // Adds an option 'styleActiveLine' which, when enabled, gives the diff --git a/addon/selection/mark-selection.js b/addon/selection/mark-selection.js index ae0d393143..f621c0704a 100644 --- a/addon/selection/mark-selection.js +++ b/addon/selection/mark-selection.js @@ -1,3 +1,6 @@ +// CodeMirror 4.1.1, copyright (c) by Marijn Haverbeke and others +// Distributed under an MIT license: http://codemirror.net/LICENSE + // Because sometimes you need to mark the selected *text*. // // Adds an option 'styleSelectedText' which, when enabled, gives diff --git a/addon/tern/tern.js b/addon/tern/tern.js index 2f450ed024..318f1484d9 100644 --- a/addon/tern/tern.js +++ b/addon/tern/tern.js @@ -1,3 +1,6 @@ +// CodeMirror 4.1.1, copyright (c) by Marijn Haverbeke and others +// Distributed under an MIT license: http://codemirror.net/LICENSE + // Glue code between CodeMirror and Tern. // // Create a CodeMirror.TernServer to wrap an actual Tern server, diff --git a/addon/tern/worker.js b/addon/tern/worker.js index 1ff63de411..615ce61e6a 100644 --- a/addon/tern/worker.js +++ b/addon/tern/worker.js @@ -1,3 +1,6 @@ +// CodeMirror 4.1.1, copyright (c) by Marijn Haverbeke and others +// Distributed under an MIT license: http://codemirror.net/LICENSE + // declare global: tern, server var server; diff --git a/addon/wrap/hardwrap.js b/addon/wrap/hardwrap.js index 87aab1b8f6..fb0b5f2fcf 100644 --- a/addon/wrap/hardwrap.js +++ b/addon/wrap/hardwrap.js @@ -1,3 +1,6 @@ +// CodeMirror 4.1.1, copyright (c) by Marijn Haverbeke and others +// Distributed under an MIT license: http://codemirror.net/LICENSE + (function(mod) { if (typeof exports == "object" && typeof module == "object") // CommonJS mod(require("../../lib/codemirror")); diff --git a/bin/release b/bin/release index 3c33290f3f..91451faae5 100755 --- a/bin/release +++ b/bin/release @@ -27,6 +27,21 @@ rewrite("doc/manual.html", function(manual) { return manual.replace(/>version \d+\.\d+\.\d+<\/span>/, ">version " + number + ""); }); +function walkDir(dir) { + fs.readdirSync(dir).forEach(function(file) { + var fname = dir + "/" + file; + if (/\.js$/.test(file)) rewrite(file, function(script) { + return script.replace(/^\/\/ CodeMirror \d+\.\d+\.\d+, copyright/, + "// CodeMirror " + number + ", copyright"); + }); + else if (fs.lstatSync(fname).isDirectory()) walkDir(fname); + }); +} +walkDir("mode"); +walkDir("lib"); +walkDir("addon"); +walkDir("keymap"); + if (bumpOnly) process.exit(0); child.exec("bash bin/authors.sh", function(){}); diff --git a/keymap/emacs.js b/keymap/emacs.js index 7cecf1f627..b1b70254d8 100644 --- a/keymap/emacs.js +++ b/keymap/emacs.js @@ -1,3 +1,6 @@ +// CodeMirror 4.1.1, copyright (c) by Marijn Haverbeke and others +// Distributed under an MIT license: http://codemirror.net/LICENSE + (function(mod) { if (typeof exports == "object" && typeof module == "object") // CommonJS mod(require("../lib/codemirror")); diff --git a/keymap/sublime.js b/keymap/sublime.js index 9158e27944..0681fa3e2b 100644 --- a/keymap/sublime.js +++ b/keymap/sublime.js @@ -1,3 +1,6 @@ +// CodeMirror 4.1.1, copyright (c) by Marijn Haverbeke and others +// Distributed under an MIT license: http://codemirror.net/LICENSE + // A rough approximation of Sublime Text's keybindings // Depends on addon/search/searchcursor.js and optionally addon/dialog/dialogs.js diff --git a/keymap/vim.js b/keymap/vim.js index e33d269089..9dce29e253 100644 --- a/keymap/vim.js +++ b/keymap/vim.js @@ -1,3 +1,6 @@ +// CodeMirror 4.1.1, copyright (c) by Marijn Haverbeke and others +// Distributed under an MIT license: http://codemirror.net/LICENSE + /** * Supported keybindings: * diff --git a/lib/codemirror.js b/lib/codemirror.js index 0bf6f2d8e8..50bccb38a9 100644 --- a/lib/codemirror.js +++ b/lib/codemirror.js @@ -1,3 +1,6 @@ +// CodeMirror 4.1.1, copyright (c) by Marijn Haverbeke and others +// Distributed under an MIT license: http://codemirror.net/LICENSE + // This is CodeMirror (http://codemirror.net), a code editor // implemented in JavaScript on top of the browser's DOM. // diff --git a/mode/apl/apl.js b/mode/apl/apl.js index 2ba74c18c2..0a318dad61 100644 --- a/mode/apl/apl.js +++ b/mode/apl/apl.js @@ -1,3 +1,6 @@ +// CodeMirror 4.1.1, copyright (c) by Marijn Haverbeke and others +// Distributed under an MIT license: http://codemirror.net/LICENSE + (function(mod) { if (typeof exports == "object" && typeof module == "object") // CommonJS mod(require("../../lib/codemirror")); diff --git a/mode/asterisk/asterisk.js b/mode/asterisk/asterisk.js index c56fc0b38a..f34471d4eb 100644 --- a/mode/asterisk/asterisk.js +++ b/mode/asterisk/asterisk.js @@ -1,3 +1,6 @@ +// CodeMirror 4.1.1, copyright (c) by Marijn Haverbeke and others +// Distributed under an MIT license: http://codemirror.net/LICENSE + /* * ===================================================================================== * diff --git a/mode/clike/clike.js b/mode/clike/clike.js index 60b88bf1aa..a114344f87 100644 --- a/mode/clike/clike.js +++ b/mode/clike/clike.js @@ -1,3 +1,6 @@ +// CodeMirror 4.1.1, copyright (c) by Marijn Haverbeke and others +// Distributed under an MIT license: http://codemirror.net/LICENSE + (function(mod) { if (typeof exports == "object" && typeof module == "object") // CommonJS mod(require("../../lib/codemirror")); diff --git a/mode/clojure/clojure.js b/mode/clojure/clojure.js index 3b596ad607..f8e8879d42 100644 --- a/mode/clojure/clojure.js +++ b/mode/clojure/clojure.js @@ -1,3 +1,6 @@ +// CodeMirror 4.1.1, copyright (c) by Marijn Haverbeke and others +// Distributed under an MIT license: http://codemirror.net/LICENSE + /** * Author: Hans Engel * Branched from CodeMirror's Scheme mode (by Koh Zi Han, based on implementation by Koh Zi Chun) diff --git a/mode/cobol/cobol.js b/mode/cobol/cobol.js index e66e42a191..231218509a 100644 --- a/mode/cobol/cobol.js +++ b/mode/cobol/cobol.js @@ -1,3 +1,6 @@ +// CodeMirror 4.1.1, copyright (c) by Marijn Haverbeke and others +// Distributed under an MIT license: http://codemirror.net/LICENSE + /** * Author: Gautam Mehta * Branched from CodeMirror's Scheme mode diff --git a/mode/coffeescript/coffeescript.js b/mode/coffeescript/coffeescript.js index c6da8f2a29..5350f6099c 100644 --- a/mode/coffeescript/coffeescript.js +++ b/mode/coffeescript/coffeescript.js @@ -1,3 +1,6 @@ +// CodeMirror 4.1.1, copyright (c) by Marijn Haverbeke and others +// Distributed under an MIT license: http://codemirror.net/LICENSE + /** * Link to the project's GitHub page: * https://github.com/pickhardt/coffeescript-codemirror-mode diff --git a/mode/commonlisp/commonlisp.js b/mode/commonlisp/commonlisp.js index a0f0732bf6..ffb3c96493 100644 --- a/mode/commonlisp/commonlisp.js +++ b/mode/commonlisp/commonlisp.js @@ -1,3 +1,6 @@ +// CodeMirror 4.1.1, copyright (c) by Marijn Haverbeke and others +// Distributed under an MIT license: http://codemirror.net/LICENSE + (function(mod) { if (typeof exports == "object" && typeof module == "object") // CommonJS mod(require("../../lib/codemirror")); diff --git a/mode/css/css.js b/mode/css/css.js index 8828329fc4..6b49e473f0 100644 --- a/mode/css/css.js +++ b/mode/css/css.js @@ -1,3 +1,6 @@ +// CodeMirror 4.1.1, copyright (c) by Marijn Haverbeke and others +// Distributed under an MIT license: http://codemirror.net/LICENSE + (function(mod) { if (typeof exports == "object" && typeof module == "object") // CommonJS mod(require("../../lib/codemirror")); diff --git a/mode/css/less_test.js b/mode/css/less_test.js index ea64f91d12..a569e107dd 100644 --- a/mode/css/less_test.js +++ b/mode/css/less_test.js @@ -1,3 +1,6 @@ +// CodeMirror 4.1.1, copyright (c) by Marijn Haverbeke and others +// Distributed under an MIT license: http://codemirror.net/LICENSE + (function() { "use strict"; diff --git a/mode/css/scss_test.js b/mode/css/scss_test.js index c51cb42bba..85077532dc 100644 --- a/mode/css/scss_test.js +++ b/mode/css/scss_test.js @@ -1,3 +1,6 @@ +// CodeMirror 4.1.1, copyright (c) by Marijn Haverbeke and others +// Distributed under an MIT license: http://codemirror.net/LICENSE + (function() { var mode = CodeMirror.getMode({indentUnit: 2}, "text/x-scss"); function MT(name) { test.mode(name, mode, Array.prototype.slice.call(arguments, 1), "scss"); } diff --git a/mode/css/test.js b/mode/css/test.js index f9f667295c..85f597b570 100644 --- a/mode/css/test.js +++ b/mode/css/test.js @@ -1,3 +1,6 @@ +// CodeMirror 4.1.1, copyright (c) by Marijn Haverbeke and others +// Distributed under an MIT license: http://codemirror.net/LICENSE + (function() { var mode = CodeMirror.getMode({indentUnit: 2}, "css"); function MT(name) { test.mode(name, mode, Array.prototype.slice.call(arguments, 1)); } diff --git a/mode/cypher/cypher.js b/mode/cypher/cypher.js index f102c9ec1a..902430c2b0 100644 --- a/mode/cypher/cypher.js +++ b/mode/cypher/cypher.js @@ -1,3 +1,6 @@ +// CodeMirror 4.1.1, copyright (c) by Marijn Haverbeke and others +// Distributed under an MIT license: http://codemirror.net/LICENSE + // By the Neo4j Team and contributors. // https://github.com/neo4j-contrib/CodeMirror diff --git a/mode/d/d.js b/mode/d/d.js index 3ab8b42838..cb9a674cec 100644 --- a/mode/d/d.js +++ b/mode/d/d.js @@ -1,3 +1,6 @@ +// CodeMirror 4.1.1, copyright (c) by Marijn Haverbeke and others +// Distributed under an MIT license: http://codemirror.net/LICENSE + (function(mod) { if (typeof exports == "object" && typeof module == "object") // CommonJS mod(require("../../lib/codemirror")); diff --git a/mode/diff/diff.js b/mode/diff/diff.js index d43f15d51a..ad62f2fcef 100644 --- a/mode/diff/diff.js +++ b/mode/diff/diff.js @@ -1,3 +1,6 @@ +// CodeMirror 4.1.1, copyright (c) by Marijn Haverbeke and others +// Distributed under an MIT license: http://codemirror.net/LICENSE + (function(mod) { if (typeof exports == "object" && typeof module == "object") // CommonJS mod(require("../../lib/codemirror")); diff --git a/mode/django/django.js b/mode/django/django.js index 38de7d5ee3..3fe57cdea3 100644 --- a/mode/django/django.js +++ b/mode/django/django.js @@ -1,3 +1,6 @@ +// CodeMirror 4.1.1, copyright (c) by Marijn Haverbeke and others +// Distributed under an MIT license: http://codemirror.net/LICENSE + (function(mod) { if (typeof exports == "object" && typeof module == "object") // CommonJS mod(require("../../lib/codemirror"), require("../htmlmixed/htmlmixed"), diff --git a/mode/dtd/dtd.js b/mode/dtd/dtd.js index b4a6cb3155..7c3b6a1cab 100644 --- a/mode/dtd/dtd.js +++ b/mode/dtd/dtd.js @@ -1,3 +1,6 @@ +// CodeMirror 4.1.1, copyright (c) by Marijn Haverbeke and others +// Distributed under an MIT license: http://codemirror.net/LICENSE + /* DTD mode Ported to CodeMirror by Peter Kroon diff --git a/mode/dylan/dylan.js b/mode/dylan/dylan.js index ccf9679181..b4526f6fad 100644 --- a/mode/dylan/dylan.js +++ b/mode/dylan/dylan.js @@ -1,3 +1,6 @@ +// CodeMirror 4.1.1, copyright (c) by Marijn Haverbeke and others +// Distributed under an MIT license: http://codemirror.net/LICENSE + (function(mod) { if (typeof exports == "object" && typeof module == "object") // CommonJS mod(require("../../lib/codemirror")); diff --git a/mode/ecl/ecl.js b/mode/ecl/ecl.js index 2b841ff5f1..18d60899e0 100644 --- a/mode/ecl/ecl.js +++ b/mode/ecl/ecl.js @@ -1,3 +1,6 @@ +// CodeMirror 4.1.1, copyright (c) by Marijn Haverbeke and others +// Distributed under an MIT license: http://codemirror.net/LICENSE + (function(mod) { if (typeof exports == "object" && typeof module == "object") // CommonJS mod(require("../../lib/codemirror")); diff --git a/mode/eiffel/eiffel.js b/mode/eiffel/eiffel.js index c6c3c84600..52c54e7863 100644 --- a/mode/eiffel/eiffel.js +++ b/mode/eiffel/eiffel.js @@ -1,3 +1,6 @@ +// CodeMirror 4.1.1, copyright (c) by Marijn Haverbeke and others +// Distributed under an MIT license: http://codemirror.net/LICENSE + (function(mod) { if (typeof exports == "object" && typeof module == "object") // CommonJS mod(require("../../lib/codemirror")); diff --git a/mode/erlang/erlang.js b/mode/erlang/erlang.js index 3d4b1ba92e..7e2a1d4ad3 100644 --- a/mode/erlang/erlang.js +++ b/mode/erlang/erlang.js @@ -1,3 +1,6 @@ +// CodeMirror 4.1.1, copyright (c) by Marijn Haverbeke and others +// Distributed under an MIT license: http://codemirror.net/LICENSE + /*jshint unused:true, eqnull:true, curly:true, bitwise:true */ /*jshint undef:true, latedef:true, trailing:true */ /*global CodeMirror:true */ diff --git a/mode/fortran/fortran.js b/mode/fortran/fortran.js index 58dac127c1..66b9c9ad70 100644 --- a/mode/fortran/fortran.js +++ b/mode/fortran/fortran.js @@ -1,3 +1,6 @@ +// CodeMirror 4.1.1, copyright (c) by Marijn Haverbeke and others +// Distributed under an MIT license: http://codemirror.net/LICENSE + (function(mod) { if (typeof exports == "object" && typeof module == "object") // CommonJS mod(require("../../lib/codemirror")); diff --git a/mode/gas/gas.js b/mode/gas/gas.js index ba5f195a11..6bf66b6f49 100644 --- a/mode/gas/gas.js +++ b/mode/gas/gas.js @@ -1,3 +1,6 @@ +// CodeMirror 4.1.1, copyright (c) by Marijn Haverbeke and others +// Distributed under an MIT license: http://codemirror.net/LICENSE + (function(mod) { if (typeof exports == "object" && typeof module == "object") // CommonJS mod(require("../../lib/codemirror")); diff --git a/mode/gfm/gfm.js b/mode/gfm/gfm.js index 5a028c48b4..5de044807d 100644 --- a/mode/gfm/gfm.js +++ b/mode/gfm/gfm.js @@ -1,3 +1,6 @@ +// CodeMirror 4.1.1, copyright (c) by Marijn Haverbeke and others +// Distributed under an MIT license: http://codemirror.net/LICENSE + (function(mod) { if (typeof exports == "object" && typeof module == "object") // CommonJS mod(require("../../lib/codemirror"), require("../markdown/markdown"), require("../../addon/mode/overlay")); diff --git a/mode/gfm/test.js b/mode/gfm/test.js index d06a219240..f3d72bfadf 100644 --- a/mode/gfm/test.js +++ b/mode/gfm/test.js @@ -1,3 +1,6 @@ +// CodeMirror 4.1.1, copyright (c) by Marijn Haverbeke and others +// Distributed under an MIT license: http://codemirror.net/LICENSE + (function() { var mode = CodeMirror.getMode({tabSize: 4}, "gfm"); function MT(name) { test.mode(name, mode, Array.prototype.slice.call(arguments, 1)); } diff --git a/mode/gherkin/gherkin.js b/mode/gherkin/gherkin.js index 41003641ec..c94efcb6aa 100644 --- a/mode/gherkin/gherkin.js +++ b/mode/gherkin/gherkin.js @@ -1,3 +1,6 @@ +// CodeMirror 4.1.1, copyright (c) by Marijn Haverbeke and others +// Distributed under an MIT license: http://codemirror.net/LICENSE + /* Gherkin mode - http://www.cukes.info/ Report bugs/issues here: https://github.com/marijnh/CodeMirror/issues diff --git a/mode/go/go.js b/mode/go/go.js index 82463052f6..46e13b6c54 100644 --- a/mode/go/go.js +++ b/mode/go/go.js @@ -1,3 +1,6 @@ +// CodeMirror 4.1.1, copyright (c) by Marijn Haverbeke and others +// Distributed under an MIT license: http://codemirror.net/LICENSE + (function(mod) { if (typeof exports == "object" && typeof module == "object") // CommonJS mod(require("../../lib/codemirror")); diff --git a/mode/groovy/groovy.js b/mode/groovy/groovy.js index 399452d536..4a867d8337 100644 --- a/mode/groovy/groovy.js +++ b/mode/groovy/groovy.js @@ -1,3 +1,6 @@ +// CodeMirror 4.1.1, copyright (c) by Marijn Haverbeke and others +// Distributed under an MIT license: http://codemirror.net/LICENSE + (function(mod) { if (typeof exports == "object" && typeof module == "object") // CommonJS mod(require("../../lib/codemirror")); diff --git a/mode/haml/haml.js b/mode/haml/haml.js index 59a86e7cec..1ab5001fc2 100644 --- a/mode/haml/haml.js +++ b/mode/haml/haml.js @@ -1,3 +1,6 @@ +// CodeMirror 4.1.1, copyright (c) by Marijn Haverbeke and others +// Distributed under an MIT license: http://codemirror.net/LICENSE + (function(mod) { if (typeof exports == "object" && typeof module == "object") // CommonJS mod(require("../../lib/codemirror"), require("../htmlmixed/htmlmixed"), require("../ruby/ruby")); diff --git a/mode/haml/test.js b/mode/haml/test.js index 75d0e7710f..0a8fc55696 100644 --- a/mode/haml/test.js +++ b/mode/haml/test.js @@ -1,3 +1,6 @@ +// CodeMirror 4.1.1, copyright (c) by Marijn Haverbeke and others +// Distributed under an MIT license: http://codemirror.net/LICENSE + (function() { var mode = CodeMirror.getMode({tabSize: 4, indentUnit: 2}, "haml"); function MT(name) { test.mode(name, mode, Array.prototype.slice.call(arguments, 1)); } diff --git a/mode/haskell/haskell.js b/mode/haskell/haskell.js index 2876172a95..4d923568d0 100644 --- a/mode/haskell/haskell.js +++ b/mode/haskell/haskell.js @@ -1,3 +1,6 @@ +// CodeMirror 4.1.1, copyright (c) by Marijn Haverbeke and others +// Distributed under an MIT license: http://codemirror.net/LICENSE + (function(mod) { if (typeof exports == "object" && typeof module == "object") // CommonJS mod(require("../../lib/codemirror")); diff --git a/mode/haxe/haxe.js b/mode/haxe/haxe.js index ffde5991ad..487a88ddf3 100644 --- a/mode/haxe/haxe.js +++ b/mode/haxe/haxe.js @@ -1,3 +1,6 @@ +// CodeMirror 4.1.1, copyright (c) by Marijn Haverbeke and others +// Distributed under an MIT license: http://codemirror.net/LICENSE + (function(mod) { if (typeof exports == "object" && typeof module == "object") // CommonJS mod(require("../../lib/codemirror")); diff --git a/mode/htmlembedded/htmlembedded.js b/mode/htmlembedded/htmlembedded.js index 3a07c3432e..7b3b9be725 100644 --- a/mode/htmlembedded/htmlembedded.js +++ b/mode/htmlembedded/htmlembedded.js @@ -1,3 +1,6 @@ +// CodeMirror 4.1.1, copyright (c) by Marijn Haverbeke and others +// Distributed under an MIT license: http://codemirror.net/LICENSE + (function(mod) { if (typeof exports == "object" && typeof module == "object") // CommonJS mod(require("../../lib/codemirror"), require("../htmlmixed/htmlmixed")); diff --git a/mode/htmlmixed/htmlmixed.js b/mode/htmlmixed/htmlmixed.js index d80ef9c64c..1dacbe8098 100644 --- a/mode/htmlmixed/htmlmixed.js +++ b/mode/htmlmixed/htmlmixed.js @@ -1,3 +1,6 @@ +// CodeMirror 4.1.1, copyright (c) by Marijn Haverbeke and others +// Distributed under an MIT license: http://codemirror.net/LICENSE + (function(mod) { if (typeof exports == "object" && typeof module == "object") // CommonJS mod(require("../../lib/codemirror"), require("../xml/xml"), require("../javascript/javascript"), require("../css/css")); diff --git a/mode/http/http.js b/mode/http/http.js index d2ad5994b6..bd0cb574d3 100644 --- a/mode/http/http.js +++ b/mode/http/http.js @@ -1,3 +1,6 @@ +// CodeMirror 4.1.1, copyright (c) by Marijn Haverbeke and others +// Distributed under an MIT license: http://codemirror.net/LICENSE + (function(mod) { if (typeof exports == "object" && typeof module == "object") // CommonJS mod(require("../../lib/codemirror")); diff --git a/mode/jade/jade.js b/mode/jade/jade.js index 64d4d2f4be..b7c99b083d 100644 --- a/mode/jade/jade.js +++ b/mode/jade/jade.js @@ -1,3 +1,6 @@ +// CodeMirror 4.1.1, copyright (c) by Marijn Haverbeke and others +// Distributed under an MIT license: http://codemirror.net/LICENSE + (function(mod) { if (typeof exports == "object" && typeof module == "object") // CommonJS mod(require("../../lib/codemirror"), require("../javascript/javascript"), require("../css/css"), require("../htmlmixed/htmlmixed")); diff --git a/mode/javascript/javascript.js b/mode/javascript/javascript.js index e719089349..71e8a09c6f 100644 --- a/mode/javascript/javascript.js +++ b/mode/javascript/javascript.js @@ -1,3 +1,6 @@ +// CodeMirror 4.1.1, copyright (c) by Marijn Haverbeke and others +// Distributed under an MIT license: http://codemirror.net/LICENSE + // TODO actually recognize syntax of TypeScript constructs (function(mod) { diff --git a/mode/javascript/test.js b/mode/javascript/test.js index 782f0457f9..4411a98452 100644 --- a/mode/javascript/test.js +++ b/mode/javascript/test.js @@ -1,3 +1,6 @@ +// CodeMirror 4.1.1, copyright (c) by Marijn Haverbeke and others +// Distributed under an MIT license: http://codemirror.net/LICENSE + (function() { var mode = CodeMirror.getMode({indentUnit: 2}, "javascript"); function MT(name) { test.mode(name, mode, Array.prototype.slice.call(arguments, 1)); } diff --git a/mode/jinja2/jinja2.js b/mode/jinja2/jinja2.js index 4b535e5322..e80c772929 100644 --- a/mode/jinja2/jinja2.js +++ b/mode/jinja2/jinja2.js @@ -1,3 +1,6 @@ +// CodeMirror 4.1.1, copyright (c) by Marijn Haverbeke and others +// Distributed under an MIT license: http://codemirror.net/LICENSE + (function(mod) { if (typeof exports == "object" && typeof module == "object") // CommonJS mod(require("../../lib/codemirror")); diff --git a/mode/julia/julia.js b/mode/julia/julia.js index d37ab503ed..79b47fb3ef 100644 --- a/mode/julia/julia.js +++ b/mode/julia/julia.js @@ -1,3 +1,6 @@ +// CodeMirror 4.1.1, copyright (c) by Marijn Haverbeke and others +// Distributed under an MIT license: http://codemirror.net/LICENSE + (function(mod) { if (typeof exports == "object" && typeof module == "object") // CommonJS mod(require("../../lib/codemirror")); diff --git a/mode/livescript/livescript.js b/mode/livescript/livescript.js index 1efc3ecf66..8e822bd6d3 100644 --- a/mode/livescript/livescript.js +++ b/mode/livescript/livescript.js @@ -1,3 +1,6 @@ +// CodeMirror 4.1.1, copyright (c) by Marijn Haverbeke and others +// Distributed under an MIT license: http://codemirror.net/LICENSE + /** * Link to the project's GitHub page: * https://github.com/duralog/CodeMirror diff --git a/mode/lua/lua.js b/mode/lua/lua.js index 3673557c27..6121b86b38 100644 --- a/mode/lua/lua.js +++ b/mode/lua/lua.js @@ -1,3 +1,6 @@ +// CodeMirror 4.1.1, copyright (c) by Marijn Haverbeke and others +// Distributed under an MIT license: http://codemirror.net/LICENSE + // LUA mode. Ported to CodeMirror 2 from Franciszek Wawrzak's // CodeMirror 1 mode. // highlights keywords, strings, comments (no leveling supported! ("[==[")), tokens, basic indenting diff --git a/mode/markdown/markdown.js b/mode/markdown/markdown.js index 84ea4d46b9..81472f02f7 100644 --- a/mode/markdown/markdown.js +++ b/mode/markdown/markdown.js @@ -1,3 +1,6 @@ +// CodeMirror 4.1.1, copyright (c) by Marijn Haverbeke and others +// Distributed under an MIT license: http://codemirror.net/LICENSE + (function(mod) { if (typeof exports == "object" && typeof module == "object") // CommonJS mod(require("../../lib/codemirror", require("../xml/xml"))); diff --git a/mode/markdown/test.js b/mode/markdown/test.js index 4d1d70ad14..ba1b15fc82 100644 --- a/mode/markdown/test.js +++ b/mode/markdown/test.js @@ -1,3 +1,6 @@ +// CodeMirror 4.1.1, copyright (c) by Marijn Haverbeke and others +// Distributed under an MIT license: http://codemirror.net/LICENSE + (function() { var mode = CodeMirror.getMode({tabSize: 4}, "markdown"); function MT(name) { test.mode(name, mode, Array.prototype.slice.call(arguments, 1)); } diff --git a/mode/meta.js b/mode/meta.js index 5fc51ebeec..5cccf7e9ab 100644 --- a/mode/meta.js +++ b/mode/meta.js @@ -1,3 +1,6 @@ +// CodeMirror 4.1.1, copyright (c) by Marijn Haverbeke and others +// Distributed under an MIT license: http://codemirror.net/LICENSE + (function(mod) { if (typeof exports == "object" && typeof module == "object") // CommonJS mod(require("../lib/codemirror")); diff --git a/mode/mirc/mirc.js b/mode/mirc/mirc.js index 6b2a23a16c..321830baf7 100644 --- a/mode/mirc/mirc.js +++ b/mode/mirc/mirc.js @@ -1,3 +1,6 @@ +// CodeMirror 4.1.1, copyright (c) by Marijn Haverbeke and others +// Distributed under an MIT license: http://codemirror.net/LICENSE + //mIRC mode by Ford_Lawnmower :: Based on Velocity mode by Steve O'Hara (function(mod) { diff --git a/mode/mllike/mllike.js b/mode/mllike/mllike.js index d4d59fceb7..18771467a8 100644 --- a/mode/mllike/mllike.js +++ b/mode/mllike/mllike.js @@ -1,3 +1,6 @@ +// CodeMirror 4.1.1, copyright (c) by Marijn Haverbeke and others +// Distributed under an MIT license: http://codemirror.net/LICENSE + (function(mod) { if (typeof exports == "object" && typeof module == "object") // CommonJS mod(require("../../lib/codemirror")); diff --git a/mode/nginx/nginx.js b/mode/nginx/nginx.js index 4e17cdb3c4..22c266384d 100644 --- a/mode/nginx/nginx.js +++ b/mode/nginx/nginx.js @@ -1,3 +1,6 @@ +// CodeMirror 4.1.1, copyright (c) by Marijn Haverbeke and others +// Distributed under an MIT license: http://codemirror.net/LICENSE + (function(mod) { if (typeof exports == "object" && typeof module == "object") // CommonJS mod(require("../../lib/codemirror")); diff --git a/mode/ntriples/ntriples.js b/mode/ntriples/ntriples.js index cd5eb24f07..1f9ba451cb 100644 --- a/mode/ntriples/ntriples.js +++ b/mode/ntriples/ntriples.js @@ -1,3 +1,6 @@ +// CodeMirror 4.1.1, copyright (c) by Marijn Haverbeke and others +// Distributed under an MIT license: http://codemirror.net/LICENSE + /********************************************************** * This script provides syntax highlighting support for * the Ntriples format. diff --git a/mode/octave/octave.js b/mode/octave/octave.js index 16fe4dfd78..e5a50bce3b 100644 --- a/mode/octave/octave.js +++ b/mode/octave/octave.js @@ -1,3 +1,6 @@ +// CodeMirror 4.1.1, copyright (c) by Marijn Haverbeke and others +// Distributed under an MIT license: http://codemirror.net/LICENSE + (function(mod) { if (typeof exports == "object" && typeof module == "object") // CommonJS mod(require("../../lib/codemirror")); diff --git a/mode/pascal/pascal.js b/mode/pascal/pascal.js index 642dd9bf79..652addd2c2 100644 --- a/mode/pascal/pascal.js +++ b/mode/pascal/pascal.js @@ -1,3 +1,6 @@ +// CodeMirror 4.1.1, copyright (c) by Marijn Haverbeke and others +// Distributed under an MIT license: http://codemirror.net/LICENSE + (function(mod) { if (typeof exports == "object" && typeof module == "object") // CommonJS mod(require("../../lib/codemirror")); diff --git a/mode/pegjs/pegjs.js b/mode/pegjs/pegjs.js index f74f2a4f22..725579d5a0 100644 --- a/mode/pegjs/pegjs.js +++ b/mode/pegjs/pegjs.js @@ -1,3 +1,6 @@ +// CodeMirror 4.1.1, copyright (c) by Marijn Haverbeke and others +// Distributed under an MIT license: http://codemirror.net/LICENSE + (function(mod) { if (typeof exports == "object" && typeof module == "object") // CommonJS mod(require("../../lib/codemirror"), require("../javascript/javascript")); diff --git a/mode/perl/perl.js b/mode/perl/perl.js index 0126d3ea36..9b02783a1d 100644 --- a/mode/perl/perl.js +++ b/mode/perl/perl.js @@ -1,3 +1,6 @@ +// CodeMirror 4.1.1, copyright (c) by Marijn Haverbeke and others +// Distributed under an MIT license: http://codemirror.net/LICENSE + // CodeMirror2 mode/perl/perl.js (text/x-perl) beta 0.10 (2011-11-08) // This is a part of CodeMirror from https://github.com/sabaca/CodeMirror_mode_perl (mail@sabaca.com) diff --git a/mode/php/php.js b/mode/php/php.js index 81591e4d65..4185804791 100644 --- a/mode/php/php.js +++ b/mode/php/php.js @@ -1,3 +1,6 @@ +// CodeMirror 4.1.1, copyright (c) by Marijn Haverbeke and others +// Distributed under an MIT license: http://codemirror.net/LICENSE + (function(mod) { if (typeof exports == "object" && typeof module == "object") // CommonJS mod(require("../../lib/codemirror"), require("../htmlmixed/htmlmixed"), require("../clike/clike")); diff --git a/mode/php/test.js b/mode/php/test.js index db68d75de3..2d901cf08d 100644 --- a/mode/php/test.js +++ b/mode/php/test.js @@ -1,3 +1,6 @@ +// CodeMirror 4.1.1, copyright (c) by Marijn Haverbeke and others +// Distributed under an MIT license: http://codemirror.net/LICENSE + (function() { var mode = CodeMirror.getMode({indentUnit: 2}, "php"); function MT(name) { test.mode(name, mode, Array.prototype.slice.call(arguments, 1)); } diff --git a/mode/pig/pig.js b/mode/pig/pig.js index 64ac506a79..52ad349c41 100644 --- a/mode/pig/pig.js +++ b/mode/pig/pig.js @@ -1,3 +1,6 @@ +// CodeMirror 4.1.1, copyright (c) by Marijn Haverbeke and others +// Distributed under an MIT license: http://codemirror.net/LICENSE + /* * Pig Latin Mode for CodeMirror 2 * @author Prasanth Jayachandran diff --git a/mode/properties/properties.js b/mode/properties/properties.js index 6dfe06f128..b91191a7b5 100644 --- a/mode/properties/properties.js +++ b/mode/properties/properties.js @@ -1,3 +1,6 @@ +// CodeMirror 4.1.1, copyright (c) by Marijn Haverbeke and others +// Distributed under an MIT license: http://codemirror.net/LICENSE + (function(mod) { if (typeof exports == "object" && typeof module == "object") // CommonJS mod(require("../../lib/codemirror")); diff --git a/mode/puppet/puppet.js b/mode/puppet/puppet.js index da8823e5ed..bba431eb3d 100644 --- a/mode/puppet/puppet.js +++ b/mode/puppet/puppet.js @@ -1,3 +1,6 @@ +// CodeMirror 4.1.1, copyright (c) by Marijn Haverbeke and others +// Distributed under an MIT license: http://codemirror.net/LICENSE + (function(mod) { if (typeof exports == "object" && typeof module == "object") // CommonJS mod(require("../../lib/codemirror")); diff --git a/mode/python/python.js b/mode/python/python.js index 4fc4354ff7..1fb32a7c16 100644 --- a/mode/python/python.js +++ b/mode/python/python.js @@ -1,3 +1,6 @@ +// CodeMirror 4.1.1, copyright (c) by Marijn Haverbeke and others +// Distributed under an MIT license: http://codemirror.net/LICENSE + (function(mod) { if (typeof exports == "object" && typeof module == "object") // CommonJS mod(require("../../lib/codemirror")); diff --git a/mode/q/q.js b/mode/q/q.js index d6e3b66610..ff788d9d52 100644 --- a/mode/q/q.js +++ b/mode/q/q.js @@ -1,3 +1,6 @@ +// CodeMirror 4.1.1, copyright (c) by Marijn Haverbeke and others +// Distributed under an MIT license: http://codemirror.net/LICENSE + (function(mod) { if (typeof exports == "object" && typeof module == "object") // CommonJS mod(require("../../lib/codemirror")); diff --git a/mode/r/r.js b/mode/r/r.js index 281d7fa40f..d04eca3899 100644 --- a/mode/r/r.js +++ b/mode/r/r.js @@ -1,3 +1,6 @@ +// CodeMirror 4.1.1, copyright (c) by Marijn Haverbeke and others +// Distributed under an MIT license: http://codemirror.net/LICENSE + (function(mod) { if (typeof exports == "object" && typeof module == "object") // CommonJS mod(require("../../lib/codemirror")); diff --git a/mode/rpm/rpm.js b/mode/rpm/rpm.js index 497997c4ff..bad1b3e024 100644 --- a/mode/rpm/rpm.js +++ b/mode/rpm/rpm.js @@ -1,3 +1,6 @@ +// CodeMirror 4.1.1, copyright (c) by Marijn Haverbeke and others +// Distributed under an MIT license: http://codemirror.net/LICENSE + (function(mod) { if (typeof exports == "object" && typeof module == "object") // CommonJS mod(require("../../lib/codemirror")); diff --git a/mode/rst/rst.js b/mode/rst/rst.js index 0763d4b999..11c3678428 100644 --- a/mode/rst/rst.js +++ b/mode/rst/rst.js @@ -1,3 +1,6 @@ +// CodeMirror 4.1.1, copyright (c) by Marijn Haverbeke and others +// Distributed under an MIT license: http://codemirror.net/LICENSE + (function(mod) { if (typeof exports == "object" && typeof module == "object") // CommonJS mod(require("../../lib/codemirror"), require("../python/python"), require("../stex/stex"), require("../../addon/mode/overlay")); diff --git a/mode/ruby/ruby.js b/mode/ruby/ruby.js index 4ef08b1552..8ffd2d7e8a 100644 --- a/mode/ruby/ruby.js +++ b/mode/ruby/ruby.js @@ -1,3 +1,6 @@ +// CodeMirror 4.1.1, copyright (c) by Marijn Haverbeke and others +// Distributed under an MIT license: http://codemirror.net/LICENSE + (function(mod) { if (typeof exports == "object" && typeof module == "object") // CommonJS mod(require("../../lib/codemirror")); diff --git a/mode/ruby/test.js b/mode/ruby/test.js index c97d106686..1a6067d027 100644 --- a/mode/ruby/test.js +++ b/mode/ruby/test.js @@ -1,3 +1,6 @@ +// CodeMirror 4.1.1, copyright (c) by Marijn Haverbeke and others +// Distributed under an MIT license: http://codemirror.net/LICENSE + (function() { var mode = CodeMirror.getMode({indentUnit: 2}, "ruby"); function MT(name) { test.mode(name, mode, Array.prototype.slice.call(arguments, 1)); } diff --git a/mode/rust/rust.js b/mode/rust/rust.js index 2e6e20b231..e7abed3520 100644 --- a/mode/rust/rust.js +++ b/mode/rust/rust.js @@ -1,3 +1,6 @@ +// CodeMirror 4.1.1, copyright (c) by Marijn Haverbeke and others +// Distributed under an MIT license: http://codemirror.net/LICENSE + (function(mod) { if (typeof exports == "object" && typeof module == "object") // CommonJS mod(require("../../lib/codemirror")); diff --git a/mode/sass/sass.js b/mode/sass/sass.js index 74ae91db12..8cdb1365f9 100644 --- a/mode/sass/sass.js +++ b/mode/sass/sass.js @@ -1,3 +1,6 @@ +// CodeMirror 4.1.1, copyright (c) by Marijn Haverbeke and others +// Distributed under an MIT license: http://codemirror.net/LICENSE + (function(mod) { if (typeof exports == "object" && typeof module == "object") // CommonJS mod(require("../../lib/codemirror")); diff --git a/mode/scheme/scheme.js b/mode/scheme/scheme.js index 7124f7283b..97979057ef 100644 --- a/mode/scheme/scheme.js +++ b/mode/scheme/scheme.js @@ -1,3 +1,6 @@ +// CodeMirror 4.1.1, copyright (c) by Marijn Haverbeke and others +// Distributed under an MIT license: http://codemirror.net/LICENSE + /** * Author: Koh Zi Han, based on implementation by Koh Zi Chun */ diff --git a/mode/shell/shell.js b/mode/shell/shell.js index 5abb44711f..c1893a4dd1 100644 --- a/mode/shell/shell.js +++ b/mode/shell/shell.js @@ -1,3 +1,6 @@ +// CodeMirror 4.1.1, copyright (c) by Marijn Haverbeke and others +// Distributed under an MIT license: http://codemirror.net/LICENSE + (function(mod) { if (typeof exports == "object" && typeof module == "object") // CommonJS mod(require("../../lib/codemirror")); diff --git a/mode/shell/test.js b/mode/shell/test.js index 3ab045c087..8bdb82103d 100644 --- a/mode/shell/test.js +++ b/mode/shell/test.js @@ -1,3 +1,6 @@ +// CodeMirror 4.1.1, copyright (c) by Marijn Haverbeke and others +// Distributed under an MIT license: http://codemirror.net/LICENSE + (function() { var mode = CodeMirror.getMode({}, "shell"); function MT(name) { test.mode(name, mode, Array.prototype.slice.call(arguments, 1)); } diff --git a/mode/sieve/sieve.js b/mode/sieve/sieve.js index 8256dda0a4..d74c839e01 100644 --- a/mode/sieve/sieve.js +++ b/mode/sieve/sieve.js @@ -1,3 +1,6 @@ +// CodeMirror 4.1.1, copyright (c) by Marijn Haverbeke and others +// Distributed under an MIT license: http://codemirror.net/LICENSE + (function(mod) { if (typeof exports == "object" && typeof module == "object") // CommonJS mod(require("../../lib/codemirror")); diff --git a/mode/smalltalk/smalltalk.js b/mode/smalltalk/smalltalk.js index deb78a4f7a..42fdc67830 100644 --- a/mode/smalltalk/smalltalk.js +++ b/mode/smalltalk/smalltalk.js @@ -1,3 +1,6 @@ +// CodeMirror 4.1.1, copyright (c) by Marijn Haverbeke and others +// Distributed under an MIT license: http://codemirror.net/LICENSE + (function(mod) { if (typeof exports == "object" && typeof module == "object") // CommonJS mod(require("../../lib/codemirror")); diff --git a/mode/smarty/smarty.js b/mode/smarty/smarty.js index 2a78c6d394..8c4bd13b61 100644 --- a/mode/smarty/smarty.js +++ b/mode/smarty/smarty.js @@ -1,3 +1,6 @@ +// CodeMirror 4.1.1, copyright (c) by Marijn Haverbeke and others +// Distributed under an MIT license: http://codemirror.net/LICENSE + /** * Smarty 2 and 3 mode. */ diff --git a/mode/smartymixed/smartymixed.js b/mode/smartymixed/smartymixed.js index 7e5e12c0ec..3be014b872 100644 --- a/mode/smartymixed/smartymixed.js +++ b/mode/smartymixed/smartymixed.js @@ -1,3 +1,6 @@ +// CodeMirror 4.1.1, copyright (c) by Marijn Haverbeke and others +// Distributed under an MIT license: http://codemirror.net/LICENSE + /** * @file smartymixed.js * @brief Smarty Mixed Codemirror mode (Smarty + Mixed HTML) diff --git a/mode/solr/solr.js b/mode/solr/solr.js index 25d928ec1b..ed7eff6eb6 100644 --- a/mode/solr/solr.js +++ b/mode/solr/solr.js @@ -1,3 +1,6 @@ +// CodeMirror 4.1.1, copyright (c) by Marijn Haverbeke and others +// Distributed under an MIT license: http://codemirror.net/LICENSE + (function(mod) { if (typeof exports == "object" && typeof module == "object") // CommonJS mod(require("../../lib/codemirror")); diff --git a/mode/sparql/sparql.js b/mode/sparql/sparql.js index f228b1dffe..f4453e6864 100644 --- a/mode/sparql/sparql.js +++ b/mode/sparql/sparql.js @@ -1,3 +1,6 @@ +// CodeMirror 4.1.1, copyright (c) by Marijn Haverbeke and others +// Distributed under an MIT license: http://codemirror.net/LICENSE + (function(mod) { if (typeof exports == "object" && typeof module == "object") // CommonJS mod(require("../../lib/codemirror")); diff --git a/mode/sql/sql.js b/mode/sql/sql.js index 417db06282..59510ffa08 100644 --- a/mode/sql/sql.js +++ b/mode/sql/sql.js @@ -1,3 +1,6 @@ +// CodeMirror 4.1.1, copyright (c) by Marijn Haverbeke and others +// Distributed under an MIT license: http://codemirror.net/LICENSE + (function(mod) { if (typeof exports == "object" && typeof module == "object") // CommonJS mod(require("../../lib/codemirror")); diff --git a/mode/stex/stex.js b/mode/stex/stex.js index 59a395a0fe..b5ef7898f3 100644 --- a/mode/stex/stex.js +++ b/mode/stex/stex.js @@ -1,3 +1,6 @@ +// CodeMirror 4.1.1, copyright (c) by Marijn Haverbeke and others +// Distributed under an MIT license: http://codemirror.net/LICENSE + /* * Author: Constantin Jucovschi (c.jucovschi@jacobs-university.de) * Licence: MIT diff --git a/mode/stex/test.js b/mode/stex/test.js index ab629e81ea..6eaf0f6881 100644 --- a/mode/stex/test.js +++ b/mode/stex/test.js @@ -1,3 +1,6 @@ +// CodeMirror 4.1.1, copyright (c) by Marijn Haverbeke and others +// Distributed under an MIT license: http://codemirror.net/LICENSE + (function() { var mode = CodeMirror.getMode({tabSize: 4}, "stex"); function MT(name) { test.mode(name, mode, Array.prototype.slice.call(arguments, 1)); } diff --git a/mode/tcl/tcl.js b/mode/tcl/tcl.js index 4c29ee7d98..49c555b108 100644 --- a/mode/tcl/tcl.js +++ b/mode/tcl/tcl.js @@ -1,3 +1,6 @@ +// CodeMirror 4.1.1, copyright (c) by Marijn Haverbeke and others +// Distributed under an MIT license: http://codemirror.net/LICENSE + //tcl mode by Ford_Lawnmower :: Based on Velocity mode by Steve O'Hara (function(mod) { diff --git a/mode/tiddlywiki/tiddlywiki.js b/mode/tiddlywiki/tiddlywiki.js index ecd1d173c0..31dff8cb93 100644 --- a/mode/tiddlywiki/tiddlywiki.js +++ b/mode/tiddlywiki/tiddlywiki.js @@ -1,3 +1,6 @@ +// CodeMirror 4.1.1, copyright (c) by Marijn Haverbeke and others +// Distributed under an MIT license: http://codemirror.net/LICENSE + /*** |''Name''|tiddlywiki.js| |''Description''|Enables TiddlyWikiy syntax highlighting using CodeMirror| diff --git a/mode/tiki/tiki.js b/mode/tiki/tiki.js index eb9a893fde..02c2f1b74b 100644 --- a/mode/tiki/tiki.js +++ b/mode/tiki/tiki.js @@ -1,3 +1,6 @@ +// CodeMirror 4.1.1, copyright (c) by Marijn Haverbeke and others +// Distributed under an MIT license: http://codemirror.net/LICENSE + (function(mod) { if (typeof exports == "object" && typeof module == "object") // CommonJS mod(require("../../lib/codemirror")); diff --git a/mode/toml/toml.js b/mode/toml/toml.js index 2c722b3752..593807a900 100644 --- a/mode/toml/toml.js +++ b/mode/toml/toml.js @@ -1,3 +1,6 @@ +// CodeMirror 4.1.1, copyright (c) by Marijn Haverbeke and others +// Distributed under an MIT license: http://codemirror.net/LICENSE + (function(mod) { if (typeof exports == "object" && typeof module == "object") // CommonJS mod(require("../../lib/codemirror")); diff --git a/mode/turtle/turtle.js b/mode/turtle/turtle.js index de9fc2b6c7..b2c7023c16 100644 --- a/mode/turtle/turtle.js +++ b/mode/turtle/turtle.js @@ -1,3 +1,6 @@ +// CodeMirror 4.1.1, copyright (c) by Marijn Haverbeke and others +// Distributed under an MIT license: http://codemirror.net/LICENSE + (function(mod) { if (typeof exports == "object" && typeof module == "object") // CommonJS mod(require("../../lib/codemirror")); diff --git a/mode/vb/vb.js b/mode/vb/vb.js index 4fd80210fb..6fa42d97eb 100644 --- a/mode/vb/vb.js +++ b/mode/vb/vb.js @@ -1,3 +1,6 @@ +// CodeMirror 4.1.1, copyright (c) by Marijn Haverbeke and others +// Distributed under an MIT license: http://codemirror.net/LICENSE + (function(mod) { if (typeof exports == "object" && typeof module == "object") // CommonJS mod(require("../../lib/codemirror")); diff --git a/mode/vbscript/vbscript.js b/mode/vbscript/vbscript.js index 4be7c7f2bb..9293c0af34 100644 --- a/mode/vbscript/vbscript.js +++ b/mode/vbscript/vbscript.js @@ -1,3 +1,6 @@ +// CodeMirror 4.1.1, copyright (c) by Marijn Haverbeke and others +// Distributed under an MIT license: http://codemirror.net/LICENSE + /* For extra ASP classic objects, initialize CodeMirror instance with this option: isASP: true diff --git a/mode/velocity/velocity.js b/mode/velocity/velocity.js index b64636bb43..fd0292920b 100644 --- a/mode/velocity/velocity.js +++ b/mode/velocity/velocity.js @@ -1,3 +1,6 @@ +// CodeMirror 4.1.1, copyright (c) by Marijn Haverbeke and others +// Distributed under an MIT license: http://codemirror.net/LICENSE + (function(mod) { if (typeof exports == "object" && typeof module == "object") // CommonJS mod(require("../../lib/codemirror")); diff --git a/mode/verilog/test.js b/mode/verilog/test.js index 6f5770b848..42d4f618cf 100644 --- a/mode/verilog/test.js +++ b/mode/verilog/test.js @@ -1,3 +1,6 @@ +// CodeMirror 4.1.1, copyright (c) by Marijn Haverbeke and others +// Distributed under an MIT license: http://codemirror.net/LICENSE + (function() { var mode = CodeMirror.getMode({indentUnit: 4}, "verilog"); function MT(name) { test.mode(name, mode, Array.prototype.slice.call(arguments, 1)); } diff --git a/mode/verilog/verilog.js b/mode/verilog/verilog.js index d52ecea2a3..c575ac5f51 100644 --- a/mode/verilog/verilog.js +++ b/mode/verilog/verilog.js @@ -1,3 +1,6 @@ +// CodeMirror 4.1.1, copyright (c) by Marijn Haverbeke and others +// Distributed under an MIT license: http://codemirror.net/LICENSE + (function(mod) { if (typeof exports == "object" && typeof module == "object") // CommonJS mod(require("../../lib/codemirror")); diff --git a/mode/xml/test.js b/mode/xml/test.js index 1b9d9d1760..122e1675ce 100644 --- a/mode/xml/test.js +++ b/mode/xml/test.js @@ -1,3 +1,6 @@ +// CodeMirror 4.1.1, copyright (c) by Marijn Haverbeke and others +// Distributed under an MIT license: http://codemirror.net/LICENSE + (function() { var mode = CodeMirror.getMode({indentUnit: 2}, "xml"), mname = "xml"; function MT(name) { test.mode(name, mode, Array.prototype.slice.call(arguments, 1), mname); } diff --git a/mode/xml/xml.js b/mode/xml/xml.js index 3248c454d1..946757fc57 100644 --- a/mode/xml/xml.js +++ b/mode/xml/xml.js @@ -1,3 +1,6 @@ +// CodeMirror 4.1.1, copyright (c) by Marijn Haverbeke and others +// Distributed under an MIT license: http://codemirror.net/LICENSE + (function(mod) { if (typeof exports == "object" && typeof module == "object") // CommonJS mod(require("../../lib/codemirror")); diff --git a/mode/xquery/test.js b/mode/xquery/test.js index 41719dd169..511a879d90 100644 --- a/mode/xquery/test.js +++ b/mode/xquery/test.js @@ -1,3 +1,6 @@ +// CodeMirror 4.1.1, copyright (c) by Marijn Haverbeke and others +// Distributed under an MIT license: http://codemirror.net/LICENSE + // Don't take these too seriously -- the expected results appear to be // based on the results of actual runs without any serious manual // verification. If a change you made causes them to fail, the test is diff --git a/mode/xquery/xquery.js b/mode/xquery/xquery.js index 2c7faf425a..0426bf66a5 100644 --- a/mode/xquery/xquery.js +++ b/mode/xquery/xquery.js @@ -1,3 +1,6 @@ +// CodeMirror 4.1.1, copyright (c) by Marijn Haverbeke and others +// Distributed under an MIT license: http://codemirror.net/LICENSE + (function(mod) { if (typeof exports == "object" && typeof module == "object") // CommonJS mod(require("../../lib/codemirror")); diff --git a/mode/yaml/yaml.js b/mode/yaml/yaml.js index f7b3a90c03..41679f1eea 100644 --- a/mode/yaml/yaml.js +++ b/mode/yaml/yaml.js @@ -1,3 +1,6 @@ +// CodeMirror 4.1.1, copyright (c) by Marijn Haverbeke and others +// Distributed under an MIT license: http://codemirror.net/LICENSE + (function(mod) { if (typeof exports == "object" && typeof module == "object") // CommonJS mod(require("../../lib/codemirror")); diff --git a/mode/z80/z80.js b/mode/z80/z80.js index c778803072..d441e383ac 100644 --- a/mode/z80/z80.js +++ b/mode/z80/z80.js @@ -1,3 +1,6 @@ +// CodeMirror 4.1.1, copyright (c) by Marijn Haverbeke and others +// Distributed under an MIT license: http://codemirror.net/LICENSE + (function(mod) { if (typeof exports == "object" && typeof module == "object") // CommonJS mod(require("../../lib/codemirror")); diff --git a/test/lint/lint.js b/test/lint/lint.js index a65deb60be..ba8900f7d7 100644 --- a/test/lint/lint.js +++ b/test/lint/lint.js @@ -31,6 +31,8 @@ var scopePasser = walk.make({ ScopeBody: function(node, prev, c) { c(node, node.scope); } }); +var cBlob = /^\/\/ CodeMirror \d+\.\d+\.\d+, copyright \(c\) by Marijn Haverbeke and others\n\/\/ Distributed under an MIT license: http:\/\/codemirror.net\/LICENSE\n\n/; + function checkFile(fileName) { var file = fs.readFileSync(fileName, "utf8"), notAllowed; if (notAllowed = file.match(/[\x00-\x08\x0b\x0c\x0e-\x19\uFEFF\t]|[ \t]\n/)) { @@ -41,6 +43,9 @@ function checkFile(fileName) { var info = acorn.getLineInfo(file, notAllowed.index); fail(msg + " at line " + info.line + ", column " + info.column, {source: fileName}); } + + if (!cBlob.test(file)) + fail("Missing license blob", {source: fileName}); var globalsSeen = Object.create(null); @@ -152,7 +157,7 @@ function checkDir(dir) { fs.readdirSync(dir).forEach(function(file) { var fname = dir + "/" + file; if (/\.js$/.test(file)) checkFile(fname); - else if (file != "dep" && fs.lstatSync(fname).isDirectory()) checkDir(fname); + else if (fs.lstatSync(fname).isDirectory()) checkDir(fname); }); } From 01fb28518dd76a7848606669020bc1b85a81c899 Mon Sep 17 00:00:00 2001 From: Marijn Haverbeke Date: Fri, 9 May 2014 15:56:59 +0200 Subject: [PATCH 2350/5780] Fix bug in release script --- bin/release | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/bin/release b/bin/release index 91451faae5..c26d290f94 100755 --- a/bin/release +++ b/bin/release @@ -30,7 +30,7 @@ rewrite("doc/manual.html", function(manual) { function walkDir(dir) { fs.readdirSync(dir).forEach(function(file) { var fname = dir + "/" + file; - if (/\.js$/.test(file)) rewrite(file, function(script) { + if (/\.js$/.test(file)) rewrite(fname, function(script) { return script.replace(/^\/\/ CodeMirror \d+\.\d+\.\d+, copyright/, "// CodeMirror " + number + ", copyright"); }); From a5b79b5c30fd8694fcb1fdaafeb70ab0bd335ca9 Mon Sep 17 00:00:00 2001 From: as3boyan Date: Sat, 10 May 2014 22:00:23 +0300 Subject: [PATCH 2351/5780] [show-hint addon] Fix a typo --- addon/hint/show-hint.js | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/addon/hint/show-hint.js b/addon/hint/show-hint.js index 4bede1fb03..59322749ce 100644 --- a/addon/hint/show-hint.js +++ b/addon/hint/show-hint.js @@ -379,7 +379,7 @@ alignWithWord: true, closeCharacters: /[\s()\[\]{};:>,]/, closeOnUnfocus: true, - completeOnSignleClick: false, + completeOnSingleClick: false, container: null, customKeys: null, extraKeys: null From a0edb5ba6f5d6ccb289ab6ab2ef2b6b4ad98762b Mon Sep 17 00:00:00 2001 From: Marijn Haverbeke Date: Mon, 12 May 2014 09:20:28 +0200 Subject: [PATCH 2352/5780] Don't mess up the display when updateDisplay is given a negative top offset Issue #2554 --- lib/codemirror.js | 3 +-- 1 file changed, 1 insertion(+), 2 deletions(-) diff --git a/lib/codemirror.js b/lib/codemirror.js index 50bccb38a9..ddb2514618 100644 --- a/lib/codemirror.js +++ b/lib/codemirror.js @@ -463,7 +463,7 @@ // the the current scroll position). viewPort may contain top, // height, and ensure (see op.scrollToPos) properties. function visibleLines(display, doc, viewPort) { - var top = viewPort && viewPort.top != null ? viewPort.top : display.scroller.scrollTop; + var top = viewPort && viewPort.top != null ? Math.max(0, viewPort.top) : display.scroller.scrollTop; top = Math.floor(top - paddingTop(display)); var bottom = viewPort && viewPort.bottom != null ? viewPort.bottom : top + display.wrapper.clientHeight; @@ -667,7 +667,6 @@ cm.display.gutters.style.height = Math.max(measure.docHeight, measure.clientHeight - scrollerCutOff) + "px"; } - function checkForWebkitWidthBug(cm, measure) { // Work around Webkit bug where it sometimes reserves space for a // non-existing phantom scrollbar in the scroller (Issue #2420) From bb0725b52fd9a1bcf19c623e0fb19f43ace40522 Mon Sep 17 00:00:00 2001 From: Bem Jones-Bey Date: Fri, 9 May 2014 10:28:00 -0700 Subject: [PATCH 2353/5780] [css mode] Add object-fit and object-position properties --- mode/css/css.js | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-) diff --git a/mode/css/css.js b/mode/css/css.js index 6b49e473f0..e5ed6ad5f5 100644 --- a/mode/css/css.js +++ b/mode/css/css.js @@ -425,7 +425,8 @@ CodeMirror.defineMode("css", function(config, parserConfig) { "marker-offset", "marks", "marquee-direction", "marquee-loop", "marquee-play-count", "marquee-speed", "marquee-style", "max-height", "max-width", "min-height", "min-width", "move-to", "nav-down", "nav-index", - "nav-left", "nav-right", "nav-up", "opacity", "order", "orphans", "outline", + "nav-left", "nav-right", "nav-up", "object-fit", "object-position", + "opacity", "order", "orphans", "outline", "outline-color", "outline-offset", "outline-style", "outline-width", "overflow", "overflow-style", "overflow-wrap", "overflow-x", "overflow-y", "padding", "padding-bottom", "padding-left", "padding-right", "padding-top", From 33eb40146574312f7de39c697af3b18c86d0d902 Mon Sep 17 00:00:00 2001 From: Marijn Haverbeke Date: Mon, 12 May 2014 10:07:27 +0200 Subject: [PATCH 2354/5780] [css mode] Better handling of parentheses in property values Issue #2551 --- mode/css/css.js | 15 +++++---------- mode/css/test.js | 10 ++++++++++ 2 files changed, 15 insertions(+), 10 deletions(-) diff --git a/mode/css/css.js b/mode/css/css.js index e5ed6ad5f5..00d3785bcf 100644 --- a/mode/css/css.js +++ b/mode/css/css.js @@ -167,7 +167,7 @@ CodeMirror.defineMode("css", function(config, parserConfig) { } else if (type == ":") { return "pseudo"; } else if (allowNested && type == "(") { - return pushContext(state, stream, "params"); + return pushContext(state, stream, "parens"); } return state.context.type; }; @@ -228,6 +228,8 @@ CodeMirror.defineMode("css", function(config, parserConfig) { states.parens = function(type, stream, state) { if (type == "{" || type == "}") return popAndPass(type, stream, state); if (type == ")") return popContext(state); + if (type == "(") return pushContext(state, stream, "parens"); + if (type == "word") wordAsValue(stream); return "parens"; }; @@ -303,13 +305,6 @@ CodeMirror.defineMode("css", function(config, parserConfig) { return "interpolation"; }; - states.params = function(type, stream, state) { - if (type == ")") return popContext(state); - if (type == "{" || type == "}") return popAndPass(type, stream, state); - if (type == "word") wordAsValue(stream); - return "params"; - }; - return { startState: function(base) { return {tokenize: null, @@ -332,10 +327,10 @@ CodeMirror.defineMode("css", function(config, parserConfig) { indent: function(state, textAfter) { var cx = state.context, ch = textAfter && textAfter.charAt(0); var indent = cx.indent; - if (cx.type == "prop" && ch == "}") cx = cx.prev; + if (cx.type == "prop" && (ch == "}" || ch == ")")) cx = cx.prev; if (cx.prev && (ch == "}" && (cx.type == "block" || cx.type == "top" || cx.type == "interpolation" || cx.type == "font_face") || - ch == ")" && (cx.type == "parens" || cx.type == "params" || cx.type == "media_parens") || + ch == ")" && (cx.type == "parens" || cx.type == "media_parens") || ch == "{" && (cx.type == "at" || cx.type == "media"))) { indent = cx.indent - indentUnit; cx = cx.prev; diff --git a/mode/css/test.js b/mode/css/test.js index 85f597b570..1a75e08ea4 100644 --- a/mode/css/test.js +++ b/mode/css/test.js @@ -122,4 +122,14 @@ MT("empty_url", "[def @import] [tag url]() [tag screen];"); + + MT("parens", + "[qualifier .foo] {", + " [property background-image]: [variable fade]([atom #000], [number 20%]);", + " [property border-image]: [variable linear-gradient](", + " [atom to] [atom bottom],", + " [variable fade]([atom #000], [number 20%]) [number 0%],", + " [variable fade]([atom #000], [number 20%]) [number 100%]", + " );", + "}"); })(); From 687fb51f4a6547942b08efee81e8b84393d99a5b Mon Sep 17 00:00:00 2001 From: daines Date: Mon, 12 May 2014 09:24:07 -0400 Subject: [PATCH 2355/5780] Fix typo in manual --- doc/manual.html | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/doc/manual.html b/doc/manual.html index 59c81f3456..d78797dadd 100644 --- a/doc/manual.html +++ b/doc/manual.html @@ -2328,7 +2328,7 @@

    Addons

    of the token that is being completed as {line, ch} objects.
    If no hinting function is given, the addon will - use CodeMirror.hint.auto, with + use CodeMirror.hint.auto, which calls getHelpers with the "hint" type to find applicable hinting functions, and tries them one by one. If that fails, it looks From 87df1c6e7f2a07c42689c6afba4b7192445196c8 Mon Sep 17 00:00:00 2001 From: Marijn Haverbeke Date: Tue, 13 May 2014 11:20:30 +0200 Subject: [PATCH 2356/5780] [closetag addon] Improve heuristic for seeing if closing tag already exists Issue #2557 --- addon/edit/closetag.js | 28 +++++++++++++++++++++++++--- addon/fold/xml-fold.js | 2 +- mode/xml/xml.js | 2 +- 3 files changed, 27 insertions(+), 5 deletions(-) diff --git a/addon/edit/closetag.js b/addon/edit/closetag.js index d64045ab1b..db31b0eb02 100644 --- a/addon/edit/closetag.js +++ b/addon/edit/closetag.js @@ -58,6 +58,7 @@ var pos = ranges[i].head, tok = cm.getTokenAt(pos); var inner = CodeMirror.innerMode(cm.getMode(), tok.state), state = inner.state; if (inner.mode.name != "xml" || !state.tagName) return CodeMirror.Pass; + var opt = cm.getOption("autoCloseTags"), html = inner.mode.configuration == "html"; var dontCloseTags = (typeof opt == "object" && opt.dontCloseTags) || (html && htmlDontClose); var indentTags = (typeof opt == "object" && opt.indentTags) || (html && htmlIndent); @@ -71,8 +72,7 @@ tok.type == "tag" && state.type == "closeTag" || tok.string.indexOf("/") == (tok.string.length - 1) || // match something like dontCloseTags && indexOf(dontCloseTags, lowerTagName) > -1 || - CodeMirror.scanForClosingTag && CodeMirror.scanForClosingTag(cm, pos, tagName, - Math.min(cm.lastLine() + 1, pos.line + 50))) + closingTagExists(cm, tagName, pos, state, true)) return CodeMirror.Pass; var indent = indentTags && indexOf(indentTags, lowerTagName) > -1; @@ -103,7 +103,8 @@ var inner = CodeMirror.innerMode(cm.getMode(), tok.state), state = inner.state; if (tok.type == "string" || tok.string.charAt(0) != "<" || tok.start != pos.ch - 1 || inner.mode.name != "xml" || - !state.context || !state.context.tagName) + !state.context || !state.context.tagName || + closingTagExists(cm, state.context.tagName, pos, state)) return CodeMirror.Pass; replacements[i] = "/" + state.context.tagName + ">"; } @@ -116,4 +117,25 @@ if (collection[i] == elt) return i; return -1; } + + // If xml-fold is loaded, we use its functionality to try and verify + // whether a given tag is actually unclosed. + function closingTagExists(cm, tagName, pos, state, newTag) { + if (!CodeMirror.scanForClosingTag) return false; + var end = Math.min(cm.lastLine() + 1, pos.line + 500); + var nextClose = CodeMirror.scanForClosingTag(cm, pos, null, end); + if (!nextClose || nextClose.tag != tagName) return false; + var cx = state.context; + // If the immediate wrapping context contains onCx instances of + // the same tag, a closing tag only exists if there are at least + // that many closing tags of that type following. + for (var onCx = newTag ? 1 : 0; cx && cx.tagName == tagName; cx = cx.prev) ++onCx; + pos = nextClose.to; + for (var i = 1; i < onCx; i++) { + var next = CodeMirror.scanForClosingTag(cm, pos, null, end); + if (!next || next.tag != tagName) return false; + pos = next.to; + } + return true; + } }); diff --git a/addon/fold/xml-fold.js b/addon/fold/xml-fold.js index e1fd5756d8..33d6e07b53 100644 --- a/addon/fold/xml-fold.js +++ b/addon/fold/xml-fold.js @@ -176,6 +176,6 @@ // Used by addon/edit/closetag.js CodeMirror.scanForClosingTag = function(cm, pos, name, end) { var iter = new Iter(cm, pos.line, pos.ch, end ? {from: 0, to: end} : null); - return !!findMatchingClose(iter, name); + return findMatchingClose(iter, name); }; }); diff --git a/mode/xml/xml.js b/mode/xml/xml.js index 946757fc57..9a84b632aa 100644 --- a/mode/xml/xml.js +++ b/mode/xml/xml.js @@ -124,7 +124,7 @@ CodeMirror.defineMode("xml", function(config, parserConfig) { state.state = baseState; state.tagName = state.tagStart = null; var next = state.tokenize(stream, state); - return next ? next + " error" : "error"; + return next ? next + " tag error" : "tag error"; } else if (/[\'\"]/.test(ch)) { state.tokenize = inAttribute(ch); state.stringStartCol = stream.column(); From 5b5c813680f3f69affc472942e3000c1b714e0db Mon Sep 17 00:00:00 2001 From: Marijn Haverbeke Date: Tue, 13 May 2014 11:32:21 +0200 Subject: [PATCH 2357/5780] Make ctrl-drag copy text Issue #2556 --- lib/codemirror.js | 16 +++++++++------- 1 file changed, 9 insertions(+), 7 deletions(-) diff --git a/lib/codemirror.js b/lib/codemirror.js index ddb2514618..ba39b4e9a3 100644 --- a/lib/codemirror.js +++ b/lib/codemirror.js @@ -2574,17 +2574,17 @@ lastClick = {time: now, pos: start}; } - var sel = cm.doc.sel, addNew = mac ? e.metaKey : e.ctrlKey; - if (cm.options.dragDrop && dragAndDrop && !addNew && !isReadOnly(cm) && + var sel = cm.doc.sel, modifier = mac ? e.metaKey : e.ctrlKey; + if (cm.options.dragDrop && dragAndDrop && !isReadOnly(cm) && type == "single" && sel.contains(start) > -1 && sel.somethingSelected()) - leftButtonStartDrag(cm, e, start); + leftButtonStartDrag(cm, e, start, modifier); else - leftButtonSelect(cm, e, start, type, addNew); + leftButtonSelect(cm, e, start, type, modifier); } // Start a text drag. When it ends, see if any dragging actually // happen, and treat as a click if it didn't. - function leftButtonStartDrag(cm, e, start) { + function leftButtonStartDrag(cm, e, start, modifier) { var display = cm.display; var dragEnd = operation(cm, function(e2) { if (webkit) display.scroller.draggable = false; @@ -2593,7 +2593,8 @@ off(display.scroller, "drop", dragEnd); if (Math.abs(e.clientX - e2.clientX) + Math.abs(e.clientY - e2.clientY) < 10) { e_preventDefault(e2); - extendSelection(cm.doc, start); + if (!modifier) + extendSelection(cm.doc, start); focusInput(cm); // Work around unexplainable focus problem in IE9 (#2127) if (ie_upto10 && !ie_upto8) @@ -2817,7 +2818,8 @@ try { var text = e.dataTransfer.getData("Text"); if (text) { - var selected = cm.state.draggingText && cm.listSelections(); + if (cm.state.draggingText && !(mac ? e.metaKey : e.ctrlKey)) + var selected = cm.listSelections(); setSelectionNoUndo(cm.doc, simpleSelection(pos, pos)); if (selected) for (var i = 0; i < selected.length; ++i) replaceRange(cm.doc, "", selected[i].anchor, selected[i].head, "drag"); From e6eed2510342b43bdc869f860385a49f8e4061a0 Mon Sep 17 00:00:00 2001 From: Marijn Haverbeke Date: Tue, 13 May 2014 11:53:02 +0200 Subject: [PATCH 2358/5780] Ensure a viewport always contains a single line --- lib/codemirror.js | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/lib/codemirror.js b/lib/codemirror.js index ba39b4e9a3..371c37651f 100644 --- a/lib/codemirror.js +++ b/lib/codemirror.js @@ -479,7 +479,7 @@ return {from: lineAtHeight(doc, heightAtLine(getLine(doc, ensureTo)) - display.wrapper.clientHeight), to: ensureTo}; } - return {from: from, to: to}; + return {from: from, to: Math.max(to, from + 1)}; } // LINE NUMBERS From 02725ae5464565178884059639e791ca42aee266 Mon Sep 17 00:00:00 2001 From: Marijn Haverbeke Date: Tue, 13 May 2014 13:18:48 +0200 Subject: [PATCH 2359/5780] [tern addon] Improve alignment of completion docs --- addon/tern/tern.css | 1 + 1 file changed, 1 insertion(+) diff --git a/addon/tern/tern.css b/addon/tern/tern.css index eacc2f053a..76fba33d4a 100644 --- a/addon/tern/tern.css +++ b/addon/tern/tern.css @@ -76,6 +76,7 @@ .CodeMirror-Tern-hint-doc { max-width: 25em; + margin-top: -3px; } .CodeMirror-Tern-fname { color: black; } From 9556bdd640d17ffccaa40d80939cfbce0e36b160 Mon Sep 17 00:00:00 2001 From: Marijn Haverbeke Date: Tue, 13 May 2014 13:34:43 +0200 Subject: [PATCH 2360/5780] [tern addon] Remove unnecessary error check --- addon/tern/tern.js | 3 +-- 1 file changed, 1 insertion(+), 2 deletions(-) diff --git a/addon/tern/tern.js b/addon/tern/tern.js index 318f1484d9..3508f18130 100644 --- a/addon/tern/tern.js +++ b/addon/tern/tern.js @@ -432,7 +432,7 @@ function rename(ts, cm) { var token = cm.getTokenAt(cm.getCursor()); - if (!/\w/.test(token.string)) showError(ts, cm, "Not at a variable"); + if (!/\w/.test(token.string)) return showError(ts, cm, "Not at a variable"); dialog(cm, "New name for " + token.string, function(newName) { ts.request(cm, {type: "rename", newName: newName, fullDocs: true}, function(error, data) { if (error) return showError(ts, cm, error); @@ -443,7 +443,6 @@ function selectName(ts, cm) { var cur = cm.getCursor(), token = cm.getTokenAt(cur); - if (!/\w/.test(token.string)) showError(ts, cm, "Not at a variable"); var name = findDoc(ts, cm.doc).name; ts.request(cm, {type: "refs"}, function(error, data) { if (error) return showError(ts, cm, error); From 09c8a0b09afe6eab99ea297a2d2bc1c737546b80 Mon Sep 17 00:00:00 2001 From: Marijn Haverbeke Date: Tue, 13 May 2014 13:41:49 +0200 Subject: [PATCH 2361/5780] [tern addon] Lint fix --- addon/tern/tern.js | 1 - 1 file changed, 1 deletion(-) diff --git a/addon/tern/tern.js b/addon/tern/tern.js index 3508f18130..9bae94b27e 100644 --- a/addon/tern/tern.js +++ b/addon/tern/tern.js @@ -442,7 +442,6 @@ } function selectName(ts, cm) { - var cur = cm.getCursor(), token = cm.getTokenAt(cur); var name = findDoc(ts, cm.doc).name; ts.request(cm, {type: "refs"}, function(error, data) { if (error) return showError(ts, cm, error); From 714954d744e74626b38a87834c7e46a589133f01 Mon Sep 17 00:00:00 2001 From: Marijn Haverbeke Date: Thu, 15 May 2014 12:34:58 +0200 Subject: [PATCH 2362/5780] [tern addon] Allow hiding and deleting docs by identity, not just name --- addon/tern/tern.js | 22 ++++++++++++++-------- 1 file changed, 14 insertions(+), 8 deletions(-) diff --git a/addon/tern/tern.js b/addon/tern/tern.js index 9bae94b27e..27d6421512 100644 --- a/addon/tern/tern.js +++ b/addon/tern/tern.js @@ -17,7 +17,7 @@ // indicate that a file is not available. // * fileFilter: A function(value, docName, doc) that will be applied // to documents before passing them on to Tern. -// * switchToDoc: A function(name) that should, when providing a +// * switchToDoc: A function(name, doc) that should, when providing a // multi-file view, switch the view or focus to the named file. // * showError: A function(editor, message) that can be used to // override the way errors are displayed. @@ -88,17 +88,17 @@ return this.docs[name] = data; }, - delDoc: function(name) { - var found = this.docs[name]; + delDoc: function(id) { + var found = resolveDoc(this, id); if (!found) return; CodeMirror.off(found.doc, "change", this.trackChange); - delete this.docs[name]; - this.server.delFile(name); + delete this.docs[found.name]; + this.server.delFile(found.name); }, - hideDoc: function(name) { + hideDoc: function(id) { closeArgHints(this); - var found = this.docs[name]; + var found = resolveDoc(this, id); if (found && found.changed) sendDoc(this, found); }, @@ -157,6 +157,12 @@ return ts.addDoc(name, doc); } + function resolveDoc(ts, id) { + if (typeof id == "string") return ts.docs[id]; + if (id instanceof CodeMirror) id = id.getDoc(); + if (id instanceof CodeMirror.Doc) return findDoc(ts, id); + } + function trackChange(ts, doc, change) { var data = findDoc(ts, doc); @@ -387,7 +393,7 @@ doc.doc.setSelection(end, start); if (curDoc != doc && ts.options.switchToDoc) { closeArgHints(ts); - ts.options.switchToDoc(doc.name); + ts.options.switchToDoc(doc.name, doc.doc); } } From c6e7cd1bd76cb42fe67199cd643d2b17cc3d0372 Mon Sep 17 00:00:00 2001 From: Yunchi Luo Date: Fri, 9 May 2014 20:42:08 +0200 Subject: [PATCH 2363/5780] [vim] Add vim keypress and command done events --- demo/vim.html | 11 +++++++++++ keymap/vim.js | 16 +++++++++++----- 2 files changed, 22 insertions(+), 5 deletions(-) diff --git a/demo/vim.html b/demo/vim.html index ad34660892..f92ff9e6d8 100644 --- a/demo/vim.html +++ b/demo/vim.html @@ -45,6 +45,7 @@

    Vim bindings demo

    return (--n >= 0) ? (unsigned char) *bufp++ : EOF; } +
    + + +

    Mode for Kotlin (http://kotlin.jetbrains.org/)

    +

    Developed by Hadi Hariri (https://github.com/hhariri).

    +

    MIME type defined: text/x-kotlin.

    + diff --git a/mode/kotlin/kotlin.js b/mode/kotlin/kotlin.js new file mode 100644 index 0000000000..73c84f6c4f --- /dev/null +++ b/mode/kotlin/kotlin.js @@ -0,0 +1,280 @@ +// CodeMirror, copyright (c) by Marijn Haverbeke and others +// Distributed under an MIT license: http://codemirror.net/LICENSE + +(function(mod) { + if (typeof exports == "object" && typeof module == "object") // CommonJS + mod(require("../../lib/codemirror")); + else if (typeof define == "function" && define.amd) // AMD + define(["../../lib/codemirror"], mod); + else // Plain browser env + mod(CodeMirror); +})(function(CodeMirror) { +"use strict"; + +CodeMirror.defineMode("kotlin", function (config, parserConfig) { + function words(str) { + var obj = {}, words = str.split(" "); + for (var i = 0; i < words.length; ++i) obj[words[i]] = true; + return obj; + } + + var multiLineStrings = parserConfig.multiLineStrings; + + var keywords = words( + "package continue return object while break class data trait throw super" + + " when type this else This try val var fun for is in if do as true false null get set"); + var softKeywords = words("import" + + " where by get set abstract enum open annotation override private public internal" + + " protected catch out vararg inline finally final ref"); + var blockKeywords = words("catch class do else finally for if where try while enum"); + var atoms = words("null true false this"); + + var curPunc; + + function tokenBase(stream, state) { + var ch = stream.next(); + if (ch == '"' || ch == "'") { + return startString(ch, stream, state); + } + // Wildcard import w/o trailing semicolon (import smth.*) + if (ch == "." && stream.eat("*")) { + return "word"; + } + if (/[\[\]{}\(\),;\:\.]/.test(ch)) { + curPunc = ch; + return null; + } + if (/\d/.test(ch)) { + if (stream.eat(/eE/)) { + stream.eat(/\+\-/); + stream.eatWhile(/\d/); + } + return "number"; + } + if (ch == "/") { + if (stream.eat("*")) { + state.tokenize.push(tokenComment); + return tokenComment(stream, state); + } + if (stream.eat("/")) { + stream.skipToEnd(); + return "comment"; + } + if (expectExpression(state.lastToken)) { + return startString(ch, stream, state); + } + } + // Commented + if (ch == "-" && stream.eat(">")) { + curPunc = "->"; + return null; + } + if (/[\-+*&%=<>!?|\/~]/.test(ch)) { + stream.eatWhile(/[\-+*&%=<>|~]/); + return "operator"; + } + stream.eatWhile(/[\w\$_]/); + + var cur = stream.current(); + if (atoms.propertyIsEnumerable(cur)) { + return "atom"; + } + if (softKeywords.propertyIsEnumerable(cur)) { + if (blockKeywords.propertyIsEnumerable(cur)) curPunc = "newstatement"; + return "softKeyword"; + } + + if (keywords.propertyIsEnumerable(cur)) { + if (blockKeywords.propertyIsEnumerable(cur)) curPunc = "newstatement"; + return "keyword"; + } + return "word"; + } + + tokenBase.isBase = true; + + function startString(quote, stream, state) { + var tripleQuoted = false; + if (quote != "/" && stream.eat(quote)) { + if (stream.eat(quote)) tripleQuoted = true; + else return "string"; + } + function t(stream, state) { + var escaped = false, next, end = !tripleQuoted; + + while ((next = stream.next()) != null) { + if (next == quote && !escaped) { + if (!tripleQuoted) { + break; + } + if (stream.match(quote + quote)) { + end = true; + break; + } + } + + if (quote == '"' && next == "$" && !escaped && stream.eat("{")) { + state.tokenize.push(tokenBaseUntilBrace()); + return "string"; + } + + if (next == "$" && !escaped && !stream.eat(" ")) { + state.tokenize.push(tokenBaseUntilSpace()); + return "string"; + } + escaped = !escaped && next == "\\"; + } + if (multiLineStrings) + state.tokenize.push(t); + if (end) state.tokenize.pop(); + return "string"; + } + + state.tokenize.push(t); + return t(stream, state); + } + + function tokenBaseUntilBrace() { + var depth = 1; + + function t(stream, state) { + if (stream.peek() == "}") { + depth--; + if (depth == 0) { + state.tokenize.pop(); + return state.tokenize[state.tokenize.length - 1](stream, state); + } + } else if (stream.peek() == "{") { + depth++; + } + return tokenBase(stream, state); + } + + t.isBase = true; + return t; + } + + function tokenBaseUntilSpace() { + function t(stream, state) { + if (stream.eat(/[\w]/)) { + var isWord = stream.eatWhile(/[\w]/); + if (isWord) { + state.tokenize.pop(); + return "word"; + } + } + state.tokenize.pop(); + return "string"; + } + + t.isBase = true; + return t; + } + + function tokenComment(stream, state) { + var maybeEnd = false, ch; + while (ch = stream.next()) { + if (ch == "/" && maybeEnd) { + state.tokenize.pop(); + break; + } + maybeEnd = (ch == "*"); + } + return "comment"; + } + + function expectExpression(last) { + return !last || last == "operator" || last == "->" || /[\.\[\{\(,;:]/.test(last) || + last == "newstatement" || last == "keyword" || last == "proplabel"; + } + + function Context(indented, column, type, align, prev) { + this.indented = indented; + this.column = column; + this.type = type; + this.align = align; + this.prev = prev; + } + + function pushContext(state, col, type) { + return state.context = new Context(state.indented, col, type, null, state.context); + } + + function popContext(state) { + var t = state.context.type; + if (t == ")" || t == "]" || t == "}") + state.indented = state.context.indented; + return state.context = state.context.prev; + } + + // Interface + + return { + startState: function (basecolumn) { + return { + tokenize: [tokenBase], + context: new Context((basecolumn || 0) - config.indentUnit, 0, "top", false), + indented: 0, + startOfLine: true, + lastToken: null + }; + }, + + token: function (stream, state) { + var ctx = state.context; + if (stream.sol()) { + if (ctx.align == null) ctx.align = false; + state.indented = stream.indentation(); + state.startOfLine = true; + // Automatic semicolon insertion + if (ctx.type == "statement" && !expectExpression(state.lastToken)) { + popContext(state); + ctx = state.context; + } + } + if (stream.eatSpace()) return null; + curPunc = null; + var style = state.tokenize[state.tokenize.length - 1](stream, state); + if (style == "comment") return style; + if (ctx.align == null) ctx.align = true; + if ((curPunc == ";" || curPunc == ":") && ctx.type == "statement") popContext(state); + // Handle indentation for {x -> \n ... } + else if (curPunc == "->" && ctx.type == "statement" && ctx.prev.type == "}") { + popContext(state); + state.context.align = false; + } + else if (curPunc == "{") pushContext(state, stream.column(), "}"); + else if (curPunc == "[") pushContext(state, stream.column(), "]"); + else if (curPunc == "(") pushContext(state, stream.column(), ")"); + else if (curPunc == "}") { + while (ctx.type == "statement") ctx = popContext(state); + if (ctx.type == "}") ctx = popContext(state); + while (ctx.type == "statement") ctx = popContext(state); + } + else if (curPunc == ctx.type) popContext(state); + else if (ctx.type == "}" || ctx.type == "top" || (ctx.type == "statement" && curPunc == "newstatement")) + pushContext(state, stream.column(), "statement"); + state.startOfLine = false; + state.lastToken = curPunc || style; + return style; + }, + + indent: function (state, textAfter) { + if (!state.tokenize[state.tokenize.length - 1].isBase) return 0; + var firstChar = textAfter && textAfter.charAt(0), ctx = state.context; + if (ctx.type == "statement" && !expectExpression(state.lastToken)) ctx = ctx.prev; + var closing = firstChar == ctx.type; + if (ctx.type == "statement") { + return ctx.indented + (firstChar == "{" ? 0 : config.indentUnit); + } + else if (ctx.align) return ctx.column + (closing ? 0 : 1); + else return ctx.indented + (closing ? 0 : config.indentUnit); + }, + + electricChars: "{}" + }; +}); + +CodeMirror.defineMIME("text/x-kotlin", "kotlin"); + +}); From 4928d37fb9650ce76e1dcba8ad593f1a579b1f70 Mon Sep 17 00:00:00 2001 From: Marijn Haverbeke Date: Fri, 4 Jul 2014 11:54:03 +0200 Subject: [PATCH 2465/5780] [kotlin mode] Integrate Issue #2678 --- doc/compress.html | 1 + mode/index.html | 1 + mode/kotlin/index.html | 3 +- mode/meta.js | 193 +++++++++++++++++++++-------------------- 4 files changed, 101 insertions(+), 97 deletions(-) diff --git a/doc/compress.html b/doc/compress.html index 5762aa442b..ede45e1c6e 100644 --- a/doc/compress.html +++ b/doc/compress.html @@ -123,6 +123,7 @@

    Script compression helper

    + diff --git a/mode/index.html b/mode/index.html index 91fb92bff8..1aa0b8d821 100644 --- a/mode/index.html +++ b/mode/index.html @@ -65,6 +65,7 @@

    Language modes

  • JavaScript
  • Jinja2
  • Julia
  • +
  • Kotlin
  • LESS
  • LiveScript
  • Lua
  • diff --git a/mode/kotlin/index.html b/mode/kotlin/index.html index 24fb46706f..38700f3226 100644 --- a/mode/kotlin/index.html +++ b/mode/kotlin/index.html @@ -79,7 +79,8 @@

    Kotlin mode

    Mode for Kotlin (http://kotlin.jetbrains.org/)

    diff --git a/mode/meta.js b/mode/meta.js index 4e42cb32df..3627cd7470 100644 --- a/mode/meta.js +++ b/mode/meta.js @@ -12,102 +12,103 @@ "use strict"; CodeMirror.modeInfo = [ - {name: 'APL', mime: 'text/apl', mode: 'apl'}, - {name: 'Asterisk', mime: 'text/x-asterisk', mode: 'asterisk'}, - {name: 'C', mime: 'text/x-csrc', mode: 'clike'}, - {name: 'C++', mime: 'text/x-c++src', mode: 'clike'}, - {name: 'Cobol', mime: 'text/x-cobol', mode: 'cobol'}, - {name: 'Java', mime: 'text/x-java', mode: 'clike'}, - {name: 'C#', mime: 'text/x-csharp', mode: 'clike'}, - {name: 'Scala', mime: 'text/x-scala', mode: 'clike'}, - {name: 'Clojure', mime: 'text/x-clojure', mode: 'clojure'}, - {name: 'CoffeeScript', mime: 'text/x-coffeescript', mode: 'coffeescript'}, - {name: 'Common Lisp', mime: 'text/x-common-lisp', mode: 'commonlisp'}, - {name: 'Cypher', mime: 'application/x-cypher-query', mode: 'cypher'}, - {name: 'CSS', mime: 'text/css', mode: 'css'}, - {name: 'D', mime: 'text/x-d', mode: 'd'}, - {name: 'diff', mime: 'text/x-diff', mode: 'diff'}, - {name: 'DTD', mime: 'application/xml-dtd', mode: 'dtd'}, - {name: 'Dylan', mime: 'text/x-dylan', mode: 'dylan'}, - {name: 'ECL', mime: 'text/x-ecl', mode: 'ecl'}, - {name: 'Eiffel', mime: 'text/x-eiffel', mode: 'eiffel'}, - {name: 'Erlang', mime: 'text/x-erlang', mode: 'erlang'}, - {name: 'Fortran', mime: 'text/x-fortran', mode: 'fortran'}, - {name: 'F#', mime: 'text/x-fsharp', mode: 'mllike'}, - {name: 'Gas', mime: 'text/x-gas', mode: 'gas'}, - {name: 'Gherkin', mime: 'text/x-feature', mode: 'gherkin'}, - {name: 'GitHub Flavored Markdown', mime: 'text/x-gfm', mode: 'gfm'}, - {name: 'Go', mime: 'text/x-go', mode: 'go'}, - {name: 'Groovy', mime: 'text/x-groovy', mode: 'groovy'}, - {name: 'HAML', mime: 'text/x-haml', mode: 'haml'}, - {name: 'Haskell', mime: 'text/x-haskell', mode: 'haskell'}, - {name: 'Haxe', mime: 'text/x-haxe', mode: 'haxe'}, - {name: 'ASP.NET', mime: 'application/x-aspx', mode: 'htmlembedded'}, - {name: 'Embedded Javascript', mime: 'application/x-ejs', mode: 'htmlembedded'}, - {name: 'JavaServer Pages', mime: 'application/x-jsp', mode: 'htmlembedded'}, - {name: 'HTML', mime: 'text/html', mode: 'htmlmixed'}, - {name: 'HTTP', mime: 'message/http', mode: 'http'}, - {name: 'Jade', mime: 'text/x-jade', mode: 'jade'}, - {name: 'JavaScript', mime: 'text/javascript', mode: 'javascript'}, - {name: 'JavaScript', mime: 'application/javascript', mode: 'javascript'}, - {name: 'JSON', mime: 'application/x-json', mode: 'javascript'}, - {name: 'JSON', mime: 'application/json', mode: 'javascript'}, - {name: 'JSON-LD', mime: 'application/ld+json', mode: 'javascript'}, - {name: 'TypeScript', mime: 'application/typescript', mode: 'javascript'}, - {name: 'Jinja2', mime: null, mode: 'jinja2'}, - {name: 'Julia', mime: 'text/x-julia', mode: 'julia'}, - {name: 'LESS', mime: 'text/x-less', mode: 'css'}, - {name: 'LiveScript', mime: 'text/x-livescript', mode: 'livescript'}, - {name: 'Lua', mime: 'text/x-lua', mode: 'lua'}, - {name: 'Markdown (GitHub-flavour)', mime: 'text/x-markdown', mode: 'markdown'}, - {name: 'mIRC', mime: 'text/mirc', mode: 'mirc'}, - {name: 'Nginx', mime: 'text/x-nginx-conf', mode: 'nginx'}, - {name: 'NTriples', mime: 'text/n-triples', mode: 'ntriples'}, - {name: 'OCaml', mime: 'text/x-ocaml', mode: 'mllike'}, - {name: 'Octave', mime: 'text/x-octave', mode: 'octave'}, - {name: 'Pascal', mime: 'text/x-pascal', mode: 'pascal'}, - {name: 'PEG.js', mime: null, mode: 'pegjs'}, - {name: 'Perl', mime: 'text/x-perl', mode: 'perl'}, - {name: 'PHP', mime: 'text/x-php', mode: 'php'}, - {name: 'PHP(HTML)', mime: 'application/x-httpd-php', mode: 'php'}, - {name: 'Pig', mime: 'text/x-pig', mode: 'pig'}, - {name: 'Plain Text', mime: 'text/plain', mode: 'null'}, - {name: 'Properties files', mime: 'text/x-properties', mode: 'properties'}, - {name: 'Python', mime: 'text/x-python', mode: 'python'}, - {name: 'Puppet', mime: 'text/x-puppet', mode: 'puppet'}, - {name: 'Cython', mime: 'text/x-cython', mode: 'python'}, - {name: 'R', mime: 'text/x-rsrc', mode: 'r'}, - {name: 'reStructuredText', mime: 'text/x-rst', mode: 'rst'}, - {name: 'Ruby', mime: 'text/x-ruby', mode: 'ruby'}, - {name: 'Rust', mime: 'text/x-rustsrc', mode: 'rust'}, - {name: 'Sass', mime: 'text/x-sass', mode: 'sass'}, - {name: 'Scheme', mime: 'text/x-scheme', mode: 'scheme'}, - {name: 'SCSS', mime: 'text/x-scss', mode: 'css'}, - {name: 'Shell', mime: 'text/x-sh', mode: 'shell'}, - {name: 'Sieve', mime: 'application/sieve', mode: 'sieve'}, - {name: 'Smalltalk', mime: 'text/x-stsrc', mode: 'smalltalk'}, - {name: 'Smarty', mime: 'text/x-smarty', mode: 'smarty'}, - {name: 'SmartyMixed', mime: 'text/x-smarty', mode: 'smartymixed'}, - {name: 'Solr', mime: 'text/x-solr', mode: 'solr'}, - {name: 'SPARQL', mime: 'application/x-sparql-query', mode: 'sparql'}, - {name: 'SQL', mime: 'text/x-sql', mode: 'sql'}, - {name: 'MariaDB', mime: 'text/x-mariadb', mode: 'sql'}, - {name: 'sTeX', mime: 'text/x-stex', mode: 'stex'}, - {name: 'LaTeX', mime: 'text/x-latex', mode: 'stex'}, - {name: 'SystemVerilog', mime: 'text/x-systemverilog', mode: 'verilog'}, - {name: 'Tcl', mime: 'text/x-tcl', mode: 'tcl'}, - {name: 'TiddlyWiki ', mime: 'text/x-tiddlywiki', mode: 'tiddlywiki'}, - {name: 'Tiki wiki', mime: 'text/tiki', mode: 'tiki'}, - {name: 'TOML', mime: 'text/x-toml', mode: 'toml'}, - {name: 'Turtle', mime: 'text/turtle', mode: 'turtle'}, - {name: 'VB.NET', mime: 'text/x-vb', mode: 'vb'}, - {name: 'VBScript', mime: 'text/vbscript', mode: 'vbscript'}, - {name: 'Velocity', mime: 'text/velocity', mode: 'velocity'}, - {name: 'Verilog', mime: 'text/x-verilog', mode: 'verilog'}, - {name: 'XML', mime: 'application/xml', mode: 'xml'}, - {name: 'XQuery', mime: 'application/xquery', mode: 'xquery'}, - {name: 'YAML', mime: 'text/x-yaml', mode: 'yaml'}, - {name: 'Z80', mime: 'text/x-z80', mode: 'z80'} + {name: "APL", mime: "text/apl", mode: "apl"}, + {name: "Asterisk", mime: "text/x-asterisk", mode: "asterisk"}, + {name: "C", mime: "text/x-csrc", mode: "clike"}, + {name: "C++", mime: "text/x-c++src", mode: "clike"}, + {name: "Cobol", mime: "text/x-cobol", mode: "cobol"}, + {name: "Java", mime: "text/x-java", mode: "clike"}, + {name: "C#", mime: "text/x-csharp", mode: "clike"}, + {name: "Scala", mime: "text/x-scala", mode: "clike"}, + {name: "Clojure", mime: "text/x-clojure", mode: "clojure"}, + {name: "CoffeeScript", mime: "text/x-coffeescript", mode: "coffeescript"}, + {name: "Common Lisp", mime: "text/x-common-lisp", mode: "commonlisp"}, + {name: "Cypher", mime: "application/x-cypher-query", mode: "cypher"}, + {name: "CSS", mime: "text/css", mode: "css"}, + {name: "D", mime: "text/x-d", mode: "d"}, + {name: "diff", mime: "text/x-diff", mode: "diff"}, + {name: "DTD", mime: "application/xml-dtd", mode: "dtd"}, + {name: "Dylan", mime: "text/x-dylan", mode: "dylan"}, + {name: "ECL", mime: "text/x-ecl", mode: "ecl"}, + {name: "Eiffel", mime: "text/x-eiffel", mode: "eiffel"}, + {name: "Erlang", mime: "text/x-erlang", mode: "erlang"}, + {name: "Fortran", mime: "text/x-fortran", mode: "fortran"}, + {name: "F#", mime: "text/x-fsharp", mode: "mllike"}, + {name: "Gas", mime: "text/x-gas", mode: "gas"}, + {name: "Gherkin", mime: "text/x-feature", mode: "gherkin"}, + {name: "GitHub Flavored Markdown", mime: "text/x-gfm", mode: "gfm"}, + {name: "Go", mime: "text/x-go", mode: "go"}, + {name: "Groovy", mime: "text/x-groovy", mode: "groovy"}, + {name: "HAML", mime: "text/x-haml", mode: "haml"}, + {name: "Haskell", mime: "text/x-haskell", mode: "haskell"}, + {name: "Haxe", mime: "text/x-haxe", mode: "haxe"}, + {name: "ASP.NET", mime: "application/x-aspx", mode: "htmlembedded"}, + {name: "Embedded Javascript", mime: "application/x-ejs", mode: "htmlembedded"}, + {name: "JavaServer Pages", mime: "application/x-jsp", mode: "htmlembedded"}, + {name: "HTML", mime: "text/html", mode: "htmlmixed"}, + {name: "HTTP", mime: "message/http", mode: "http"}, + {name: "Jade", mime: "text/x-jade", mode: "jade"}, + {name: "JavaScript", mime: "text/javascript", mode: "javascript"}, + {name: "JavaScript", mime: "application/javascript", mode: "javascript"}, + {name: "JSON", mime: "application/x-json", mode: "javascript"}, + {name: "JSON", mime: "application/json", mode: "javascript"}, + {name: "JSON-LD", mime: "application/ld+json", mode: "javascript"}, + {name: "TypeScript", mime: "application/typescript", mode: "javascript"}, + {name: "Jinja2", mime: null, mode: "jinja2"}, + {name: "Julia", mime: "text/x-julia", mode: "julia"}, + {name: "Kotlin", mime: "text/x-kotlin", mode: "kotlin"}, + {name: "LESS", mime: "text/x-less", mode: "css"}, + {name: "LiveScript", mime: "text/x-livescript", mode: "livescript"}, + {name: "Lua", mime: "text/x-lua", mode: "lua"}, + {name: "Markdown (GitHub-flavour)", mime: "text/x-markdown", mode: "markdown"}, + {name: "mIRC", mime: "text/mirc", mode: "mirc"}, + {name: "Nginx", mime: "text/x-nginx-conf", mode: "nginx"}, + {name: "NTriples", mime: "text/n-triples", mode: "ntriples"}, + {name: "OCaml", mime: "text/x-ocaml", mode: "mllike"}, + {name: "Octave", mime: "text/x-octave", mode: "octave"}, + {name: "Pascal", mime: "text/x-pascal", mode: "pascal"}, + {name: "PEG.js", mime: null, mode: "pegjs"}, + {name: "Perl", mime: "text/x-perl", mode: "perl"}, + {name: "PHP", mime: "text/x-php", mode: "php"}, + {name: "PHP(HTML)", mime: "application/x-httpd-php", mode: "php"}, + {name: "Pig", mime: "text/x-pig", mode: "pig"}, + {name: "Plain Text", mime: "text/plain", mode: "null"}, + {name: "Properties files", mime: "text/x-properties", mode: "properties"}, + {name: "Python", mime: "text/x-python", mode: "python"}, + {name: "Puppet", mime: "text/x-puppet", mode: "puppet"}, + {name: "Cython", mime: "text/x-cython", mode: "python"}, + {name: "R", mime: "text/x-rsrc", mode: "r"}, + {name: "reStructuredText", mime: "text/x-rst", mode: "rst"}, + {name: "Ruby", mime: "text/x-ruby", mode: "ruby"}, + {name: "Rust", mime: "text/x-rustsrc", mode: "rust"}, + {name: "Sass", mime: "text/x-sass", mode: "sass"}, + {name: "Scheme", mime: "text/x-scheme", mode: "scheme"}, + {name: "SCSS", mime: "text/x-scss", mode: "css"}, + {name: "Shell", mime: "text/x-sh", mode: "shell"}, + {name: "Sieve", mime: "application/sieve", mode: "sieve"}, + {name: "Smalltalk", mime: "text/x-stsrc", mode: "smalltalk"}, + {name: "Smarty", mime: "text/x-smarty", mode: "smarty"}, + {name: "SmartyMixed", mime: "text/x-smarty", mode: "smartymixed"}, + {name: "Solr", mime: "text/x-solr", mode: "solr"}, + {name: "SPARQL", mime: "application/x-sparql-query", mode: "sparql"}, + {name: "SQL", mime: "text/x-sql", mode: "sql"}, + {name: "MariaDB", mime: "text/x-mariadb", mode: "sql"}, + {name: "sTeX", mime: "text/x-stex", mode: "stex"}, + {name: "LaTeX", mime: "text/x-latex", mode: "stex"}, + {name: "SystemVerilog", mime: "text/x-systemverilog", mode: "verilog"}, + {name: "Tcl", mime: "text/x-tcl", mode: "tcl"}, + {name: "TiddlyWiki ", mime: "text/x-tiddlywiki", mode: "tiddlywiki"}, + {name: "Tiki wiki", mime: "text/tiki", mode: "tiki"}, + {name: "TOML", mime: "text/x-toml", mode: "toml"}, + {name: "Turtle", mime: "text/turtle", mode: "turtle"}, + {name: "VB.NET", mime: "text/x-vb", mode: "vb"}, + {name: "VBScript", mime: "text/vbscript", mode: "vbscript"}, + {name: "Velocity", mime: "text/velocity", mode: "velocity"}, + {name: "Verilog", mime: "text/x-verilog", mode: "verilog"}, + {name: "XML", mime: "application/xml", mode: "xml"}, + {name: "XQuery", mime: "application/xquery", mode: "xquery"}, + {name: "YAML", mime: "text/x-yaml", mode: "yaml"}, + {name: "Z80", mime: "text/x-z80", mode: "z80"} ]; }); From b55cc7f5cd0b1128508dda51f525538dd1897f29 Mon Sep 17 00:00:00 2001 From: Marijn Haverbeke Date: Thu, 3 Jul 2014 15:05:42 +0200 Subject: [PATCH 2466/5780] [multi-editor operations] Set up operation grouping Now nested operations from different editors end at the same time --- lib/codemirror.js | 66 ++++++++++++++++++++++++++++++----------------- 1 file changed, 42 insertions(+), 24 deletions(-) diff --git a/lib/codemirror.js b/lib/codemirror.js index 1aee422b42..85cfea9d7b 100644 --- a/lib/codemirror.js +++ b/lib/codemirror.js @@ -1911,10 +1911,13 @@ // error-prone). Instead, display updates are batched and then all // combined and executed at once. + var operationGroup = null; + var nextOpId = 0; // Start a new operation. function startOperation(cm) { cm.curOp = { + cm: cm, viewChanged: false, // Flag that indicates that lines might need to be redrawn startHeight: cm.doc.height, // Used to detect need to update scrollbar forceUpdate: false, // Used to force a redraw @@ -1928,19 +1931,32 @@ scrollToPos: null, // Used to scroll to a specific position id: ++nextOpId // Unique ID }; - if (!delayedCallbackDepth++) delayedCallbacks = []; + if (operationGroup) { + operationGroup.ops.push(cm.curOp); + } else { + cm.curOp.ownsGroup = operationGroup = { + ops: [cm.curOp], + delayedCallbacks: [] + }; + } } // Finish an operation, updating the display and signalling delayed events function endOperation(cm) { - var op = cm.curOp, doc = cm.doc, display = cm.display; - cm.curOp = null; - var delayed; - if (!--delayedCallbackDepth) { - delayed = delayedCallbacks; - delayedCallbacks = null; - } + var op = cm.curOp, group = op.ownsGroup; + if (!group) return; + + cm.curOp = operationGroup = null; + for (var i = 0; i < group.ops.length; i++) + endOperationInner(group.ops[i]); + + for (var i = 0; i < group.delayedCallbacks.length; ++i) + group.delayedCallbacks[i](); + } + + function endOperationInner(op) { + var cm = op.cm, display = cm.display, doc = cm.doc; if (op.updateMaxLine) findMaxLine(cm); // If it looks like an update might be needed, call updateDisplay @@ -1949,11 +1965,11 @@ op.scrollToPos.to.line >= display.viewTo) || display.maxLineChanged && cm.options.lineWrapping) { var updated = updateDisplay(cm, {top: op.scrollTop, ensure: op.scrollToPos}, op.forceUpdate); - if (cm.display.scroller.offsetHeight) cm.doc.scrollTop = cm.display.scroller.scrollTop; + if (cm.display.scroller.offsetHeight) doc.scrollTop = cm.display.scroller.scrollTop; } // If no update was run, but the selection changed, redraw that. if (!updated && op.selectionChanged) updateSelection(cm); - if (!updated && op.startHeight != cm.doc.height) updateScrollbars(cm); + if (!updated && op.startHeight != doc.height) updateScrollbars(cm); // Abort mouse wheel delta measurement, when scrolling explicitly if (display.wheelStartX != null && (op.scrollTop != null || op.scrollLeft != null || op.scrollToPos)) @@ -1971,8 +1987,8 @@ } // If we need to scroll a specific position into view, do so. if (op.scrollToPos) { - var coords = scrollPosIntoView(cm, clipPos(cm.doc, op.scrollToPos.from), - clipPos(cm.doc, op.scrollToPos.to), op.scrollToPos.margin); + var coords = scrollPosIntoView(cm, clipPos(doc, op.scrollToPos.from), + clipPos(doc, op.scrollToPos.to), op.scrollToPos.margin); if (op.scrollToPos.isCursor && cm.state.focused) maybeScrollWindow(cm, coords); } @@ -1992,7 +2008,6 @@ // Fire change events, and delayed event handlers if (op.changeObjs) signal(cm, "changes", cm, op.changeObjs); - if (delayed) for (var i = 0; i < delayed.length; ++i) delayed[i](); if (op.cursorActivityHandlers) for (var i = 0; i < op.cursorActivityHandlers.length; i++) op.cursorActivityHandlers[i](cm); @@ -6960,6 +6975,8 @@ for (var i = 0; i < arr.length; ++i) arr[i].apply(null, args); }; + var orphanDelayedCallbacks = null; + // Often, we want to signal events at a point where we are in the // middle of some work, but don't want the handler to start calling // other methods on the editor, which might be in an inconsistent @@ -6967,25 +6984,26 @@ // signalLater looks whether there are any handlers, and schedules // them to be executed when the last operation ends, or, if no // operation is active, when a timeout fires. - var delayedCallbacks, delayedCallbackDepth = 0; function signalLater(emitter, type /*, values...*/) { var arr = emitter._handlers && emitter._handlers[type]; if (!arr) return; - var args = Array.prototype.slice.call(arguments, 2); - if (!delayedCallbacks) { - ++delayedCallbackDepth; - delayedCallbacks = []; - setTimeout(fireDelayed, 0); + var args = Array.prototype.slice.call(arguments, 2), list; + if (operationGroup) { + list = operationGroup.delayedCallbacks; + } else if (orphanDelayedCallbacks) { + list = orphanDelayedCallbacks; + } else { + list = orphanDelayedCallbacks = []; + setTimeout(fireOrphanDelayed, 0); } function bnd(f) {return function(){f.apply(null, args);};}; for (var i = 0; i < arr.length; ++i) - delayedCallbacks.push(bnd(arr[i])); + list.push(bnd(arr[i])); } - function fireDelayed() { - --delayedCallbackDepth; - var delayed = delayedCallbacks; - delayedCallbacks = null; + function fireOrphanDelayed() { + var delayed = orphanDelayedCallbacks; + orphanDelayedCallbacks = null; for (var i = 0; i < delayed.length; ++i) delayed[i](); } From 0bdd5ea0b31aade11c0f2a385cdd720aab90eea4 Mon Sep 17 00:00:00 2001 From: Marijn Haverbeke Date: Thu, 3 Jul 2014 15:32:10 +0200 Subject: [PATCH 2467/5780] [multi-editor operations] Fire delayed events before the operation ends --- lib/codemirror.js | 33 +++++++++++++++++++++++---------- 1 file changed, 23 insertions(+), 10 deletions(-) diff --git a/lib/codemirror.js b/lib/codemirror.js index 85cfea9d7b..b9c63a226d 100644 --- a/lib/codemirror.js +++ b/lib/codemirror.js @@ -1925,6 +1925,7 @@ typing: false, // Whether this reset should be careful to leave existing text (for compositing) changeObjs: null, // Accumulated changes, for firing change events cursorActivityHandlers: null, // Set of handlers to fire cursorActivity on + cursorActivityCalled: 0, // Tracks which cursorActivity handlers have been called already selectionChanged: false, // Whether the selection needs to be redrawn updateMaxLine: false, // Set when the widest line needs to be determined anew scrollLeft: null, scrollTop: null, // Intermediate scroll position, not pushed to DOM yet @@ -1941,18 +1942,33 @@ } } + function fireCallbacksForOps(group) { + // Calls delayed callbacks and cursorActivity handlers until no + // new ones appear + var callbacks = group.delayedCallbacks, i = 0; + do { + for (; i < callbacks.length; i++) + callbacks[i](); + for (var j = 0; j < group.ops.length; j++) { + var op = group.ops[j]; + if (op.cursorActivityHandlers) + while (op.cursorActivityCalled < op.cursorActivityHandlers.length) + op.cursorActivityHandlers[op.cursorActivityCalled++](op.cm); + } + } while (i < callbacks.length); + } + // Finish an operation, updating the display and signalling delayed events function endOperation(cm) { var op = cm.curOp, group = op.ownsGroup; if (!group) return; - cm.curOp = operationGroup = null; - - for (var i = 0; i < group.ops.length; i++) - endOperationInner(group.ops[i]); - - for (var i = 0; i < group.delayedCallbacks.length; ++i) - group.delayedCallbacks[i](); + try { fireCallbacksForOps(group); } + finally { + cm.curOp = operationGroup = null; + for (var i = 0; i < group.ops.length; i++) + endOperationInner(group.ops[i]); + } } function endOperationInner(op) { @@ -2008,9 +2024,6 @@ // Fire change events, and delayed event handlers if (op.changeObjs) signal(cm, "changes", cm, op.changeObjs); - if (op.cursorActivityHandlers) - for (var i = 0; i < op.cursorActivityHandlers.length; i++) - op.cursorActivityHandlers[i](cm); } // Run the given function in an operation From 55e1ed94dc83539dcecc4abb56935c3b411da557 Mon Sep 17 00:00:00 2001 From: Marijn Haverbeke Date: Thu, 3 Jul 2014 15:54:08 +0200 Subject: [PATCH 2468/5780] Run operations cautiously again in highlightWorker --- lib/codemirror.js | 7 +++++-- 1 file changed, 5 insertions(+), 2 deletions(-) diff --git a/lib/codemirror.js b/lib/codemirror.js index b9c63a226d..089a3e9f3b 100644 --- a/lib/codemirror.js +++ b/lib/codemirror.js @@ -1408,8 +1408,8 @@ if (doc.frontier >= cm.display.viewTo) return; var end = +new Date + cm.options.workTime; var state = copyState(doc.mode, getStateBefore(cm, doc.frontier)); + var changedLines = []; - runInOp(cm, function() { doc.iter(doc.frontier, Math.min(doc.first + doc.size, cm.display.viewTo + 500), function(line) { if (doc.frontier >= cm.display.viewFrom) { // Visible var oldStyles = line.styles; @@ -1421,7 +1421,7 @@ var ischange = !oldStyles || oldStyles.length != line.styles.length || oldCls != newCls && (!oldCls || !newCls || oldCls.bgClass != newCls.bgClass || oldCls.textClass != newCls.textClass); for (var i = 0; !ischange && i < oldStyles.length; ++i) ischange = oldStyles[i] != line.styles[i]; - if (ischange) regLineChange(cm, doc.frontier, "text"); + if (ischange) changedLines.push(doc.frontier); line.stateAfter = copyState(doc.mode, state); } else { processLine(cm, line.text, state); @@ -1433,6 +1433,9 @@ return true; } }); + if (changedLines.length) runInOp(cm, function() { + for (var i = 0; i < changedLines.length; i++) + regLineChange(cm, changedLines[i], "text"); }); } From 24920fb8a247a6079bc41d92af08f64cfe82b4b9 Mon Sep 17 00:00:00 2001 From: Marijn Haverbeke Date: Thu, 3 Jul 2014 15:55:22 +0200 Subject: [PATCH 2469/5780] Don't wrap onKeyUp in an operation It doesn't need it --- lib/codemirror.js | 6 +++--- 1 file changed, 3 insertions(+), 3 deletions(-) diff --git a/lib/codemirror.js b/lib/codemirror.js index 089a3e9f3b..b2e194273f 100644 --- a/lib/codemirror.js +++ b/lib/codemirror.js @@ -2452,7 +2452,7 @@ // Prevent wrapper from ever scrolling on(d.wrapper, "scroll", function() { d.wrapper.scrollTop = d.wrapper.scrollLeft = 0; }); - on(d.input, "keyup", operation(cm, onKeyUp)); + on(d.input, "keyup", function(e) { onKeyUp.call(cm, e); }); on(d.input, "input", function() { if (ie && ie_version >= 9 && cm.display.inputHasSelection) cm.display.inputHasSelection = null; fastPoll(cm); @@ -3145,8 +3145,8 @@ } function onKeyUp(e) { - if (signalDOMEvent(this, e)) return; if (e.keyCode == 16) this.doc.sel.shift = false; + signalDOMEvent(this, e); } function onKeyPress(e) { @@ -4164,7 +4164,7 @@ triggerOnKeyDown: methodOp(onKeyDown), triggerOnKeyPress: methodOp(onKeyPress), - triggerOnKeyUp: methodOp(onKeyUp), + triggerOnKeyUp: onKeyUp, execCommand: function(cmd) { if (commands.hasOwnProperty(cmd)) From e72560be12964597656fdf05d775893c46b81b14 Mon Sep 17 00:00:00 2001 From: Marijn Haverbeke Date: Thu, 3 Jul 2014 18:03:27 +0200 Subject: [PATCH 2470/5780] [multi-editor operations] Split actions at op end into steps, call in order --- lib/codemirror.js | 295 ++++++++++++++++++++++++++++------------------ 1 file changed, 181 insertions(+), 114 deletions(-) diff --git a/lib/codemirror.js b/lib/codemirror.js index b2e194273f..1bf02d2089 100644 --- a/lib/codemirror.js +++ b/lib/codemirror.js @@ -108,6 +108,7 @@ for (var opt in optionHandlers) if (optionHandlers.hasOwnProperty(opt)) optionHandlers[opt](cm, options[opt], Init); + maybeUpdateLineNumberWidth(cm); for (var i = 0; i < initHooks.length; ++i) initHooks[i](cm); }); } @@ -467,18 +468,18 @@ } // Compute the lines that are visible in a given viewport (defaults - // the the current scroll position). viewPort may contain top, + // the the current scroll position). viewport may contain top, // height, and ensure (see op.scrollToPos) properties. - function visibleLines(display, doc, viewPort) { - var top = viewPort && viewPort.top != null ? Math.max(0, viewPort.top) : display.scroller.scrollTop; + function visibleLines(display, doc, viewport) { + var top = viewport && viewport.top != null ? Math.max(0, viewport.top) : display.scroller.scrollTop; top = Math.floor(top - paddingTop(display)); - var bottom = viewPort && viewPort.bottom != null ? viewPort.bottom : top + display.wrapper.clientHeight; + var bottom = viewport && viewport.bottom != null ? viewport.bottom : top + display.wrapper.clientHeight; var from = lineAtHeight(doc, top), to = lineAtHeight(doc, bottom); // Ensure is a {from: {line, ch}, to: {line, ch}} object, and // forces those lines into the viewport (if possible). - if (viewPort && viewPort.ensure) { - var ensureFrom = viewPort.ensure.from.line, ensureTo = viewPort.ensure.to.line; + if (viewport && viewport.ensure) { + var ensureFrom = viewport.ensure.from.line, ensureTo = viewport.ensure.to.line; if (ensureFrom < from) return {from: ensureFrom, to: lineAtHeight(doc, heightAtLine(getLine(doc, ensureFrom)) + display.wrapper.clientHeight)}; @@ -543,83 +544,46 @@ // DISPLAY DRAWING - // Updates the display, selection, and scrollbars, using the - // information in display.view to find out which nodes are no longer - // up-to-date. Tries to bail out early when no changes are needed, - // unless forced is true. - // Returns true if an actual update happened, false otherwise. - function updateDisplay(cm, viewPort, forced) { - var oldFrom = cm.display.viewFrom, oldTo = cm.display.viewTo, updated; - var visible = visibleLines(cm.display, cm.doc, viewPort); - for (var first = true;; first = false) { - var oldWidth = cm.display.scroller.clientWidth; - if (!updateDisplayInner(cm, visible, forced)) break; - updated = true; - - // If the max line changed since it was last measured, measure it, - // and ensure the document's width matches it. - if (cm.display.maxLineChanged && !cm.options.lineWrapping) - adjustContentWidth(cm); - - var barMeasure = measureForScrollbars(cm); - updateSelection(cm); - setDocumentHeight(cm, barMeasure); - updateScrollbars(cm, barMeasure); - if (webkit && cm.options.lineWrapping) - checkForWebkitWidthBug(cm, barMeasure); // (Issue #2420) - if (webkit && barMeasure.scrollWidth > barMeasure.clientWidth && - barMeasure.scrollWidth < barMeasure.clientWidth + 1 && - !hScrollbarTakesSpace(cm)) - updateScrollbars(cm); // (Issue #2562) - if (first && cm.options.lineWrapping && oldWidth != cm.display.scroller.clientWidth) { - forced = true; - continue; - } - forced = false; - - // Clip forced viewport to actual scrollable area. - if (viewPort && viewPort.top != null) - viewPort = {top: Math.min(barMeasure.docHeight - scrollerCutOff - barMeasure.clientHeight, viewPort.top)}; - // Updated line heights might result in the drawn area not - // actually covering the viewport. Keep looping until it does. - visible = visibleLines(cm.display, cm.doc, viewPort); - if (visible.from >= cm.display.viewFrom && visible.to <= cm.display.viewTo) - break; - } + function DisplayUpdate(cm, viewport, force) { + var display = cm.display; - cm.display.updateLineNumbers = null; - if (updated) { - signalLater(cm, "update", cm); - if (cm.display.viewFrom != oldFrom || cm.display.viewTo != oldTo) - signalLater(cm, "viewportChange", cm, cm.display.viewFrom, cm.display.viewTo); - } - return updated; + this.viewport = viewport; + // Store some values that we'll need later (but don't want to force a relayout for) + this.visible = visibleLines(display, cm.doc, viewport); + this.editorIsHidden = !display.wrapper.offsetWidth; + this.wrapperHeight = display.wrapper.clientHeight; + this.oldViewFrom = display.viewFrom; this.oldViewTo = display.viewTo; + this.oldScrollerWidth = display.scroller.clientWidth; + this.force = force; + this.dims = getDimensions(cm); } // Does the actual updating of the line display. Bails out // (returning false) when there is nothing to be done and forced is // false. - function updateDisplayInner(cm, visible, forced) { + function updateDisplayIfNeeded(cm, update) { var display = cm.display, doc = cm.doc; - if (!display.wrapper.offsetWidth) { + if (update.editorIsHidden) { resetView(cm); - return; + return false; } // Bail out if the visible area is already rendered and nothing changed. - if (!forced && visible.from >= display.viewFrom && visible.to <= display.viewTo && + if (!update.force && + update.visible.from >= display.viewFrom && update.visible.to <= display.viewTo && (display.updateLineNumbers == null || display.updateLineNumbers >= display.viewTo) && countDirtyView(cm) == 0) - return; + return false; - if (maybeUpdateLineNumberWidth(cm)) + if (maybeUpdateLineNumberWidth(cm)) { resetView(cm); - var dims = getDimensions(cm); + update.dims = getDimensions(cm); + } // Compute a suitable new viewport (from & to) var end = doc.first + doc.size; - var from = Math.max(visible.from - cm.options.viewportMargin, doc.first); - var to = Math.min(end, visible.to + cm.options.viewportMargin); + var from = Math.max(update.visible.from - cm.options.viewportMargin, doc.first); + var to = Math.min(end, update.visible.to + cm.options.viewportMargin); if (display.viewFrom < from && from - display.viewFrom < 20) from = Math.max(doc.first, display.viewFrom); if (display.viewTo > to && display.viewTo - to < 20) to = Math.min(end, display.viewTo); if (sawCollapsedSpans) { @@ -628,7 +592,7 @@ } var different = from != display.viewFrom || to != display.viewTo || - display.lastSizeC != display.wrapper.clientHeight; + display.lastSizeC != update.wrapperHeight; adjustView(cm, from, to); display.viewOffset = heightAtLine(getLine(cm.doc, display.viewFrom)); @@ -636,13 +600,15 @@ cm.display.mover.style.top = display.viewOffset + "px"; var toUpdate = countDirtyView(cm); - if (!different && toUpdate == 0 && !forced) return; + if (!different && toUpdate == 0 && !update.force && + (display.updateLineNumbers == null || display.updateLineNumbers >= display.viewTo)) + return false; // For big changes, we hide the enclosing element during the // update, since that speeds up the operations on most browsers. var focused = activeElt(); if (toUpdate > 4) display.lineDiv.style.display = "none"; - patchDisplay(cm, display.updateLineNumbers, dims); + patchDisplay(cm, display.updateLineNumbers, update.dims); if (toUpdate > 4) display.lineDiv.style.display = ""; // There might have been a widget with a focused element that got // hidden or updated, if so re-focus it. @@ -654,24 +620,50 @@ removeChildren(display.selectionDiv); if (different) { - display.lastSizeC = display.wrapper.clientHeight; + display.lastSizeC = update.wrapperHeight; startWorker(cm, 400); } - updateHeightsInViewport(cm); + display.updateLineNumbers = null; return true; } - function adjustContentWidth(cm) { - var display = cm.display; - var width = measureChar(cm, display.maxLine, display.maxLine.text.length).left; - display.maxLineChanged = false; - var minWidth = Math.max(0, width + 3); - var maxScrollLeft = Math.max(0, display.sizer.offsetLeft + minWidth + scrollerCutOff - display.scroller.clientWidth); - display.sizer.style.minWidth = minWidth + "px"; - if (maxScrollLeft < cm.doc.scrollLeft) - setScrollLeft(cm, Math.min(display.scroller.scrollLeft, maxScrollLeft), true); + function postUpdateDisplay(cm, update) { + var force = update.force, viewport = update.viewport; + for (var first = true;; first = false) { + updateHeightsInViewport(cm); + if (first && cm.options.lineWrapping && update.oldScrollerWidth != cm.display.scroller.clientWidth) { + force = true; + } else { + force = false; + // Clip forced viewport to actual scrollable area. + if (viewport && viewport.top != null) + viewport = {top: Math.min(cm.doc.height + paddingVert(cm.display) - scrollerCutOff - + cm.display.scroller.clientHeight, viewport.top)}; + // Updated line heights might result in the drawn area not + // actually covering the viewport. Keep looping until it does. + var visible = visibleLines(cm.display, cm.doc, viewport); + if (visible.from >= cm.display.viewFrom && visible.to <= cm.display.viewTo) + break; + } + if (!updateDisplayIfNeeded(cm, update)) break; + } + + signalLater(cm, "update", cm); + if (cm.display.viewFrom != update.oldViewFrom || cm.display.viewTo != update.oldViewTo) + signalLater(cm, "viewportChange", cm, cm.display.viewFrom, cm.display.viewTo); + } + + function updateDisplaySimple(cm, viewport) { + var update = new DisplayUpdate(cm, viewport); + if (updateDisplayIfNeeded(cm, update)) { + postUpdateDisplay(cm, update); + var barMeasure = measureForScrollbars(cm); + updateSelection(cm); + setDocumentHeight(cm, barMeasure); + updateScrollbars(cm, barMeasure); + } } function setDocumentHeight(cm, measure) { @@ -1257,10 +1249,10 @@ // SELECTION DRAWING // Redraw the selection and/or cursor - function updateSelection(cm) { - var display = cm.display, doc = cm.doc; - var curFragment = document.createDocumentFragment(); - var selFragment = document.createDocumentFragment(); + function drawSelection(cm) { + var display = cm.display, doc = cm.doc, result = {}; + var curFragment = result.cursors = document.createDocumentFragment(); + var selFragment = result.selection = document.createDocumentFragment(); for (var i = 0; i < doc.sel.ranges.length; i++) { var range = doc.sel.ranges[i]; @@ -1275,16 +1267,23 @@ if (cm.options.moveInputWithCursor) { var headPos = cursorCoords(cm, doc.sel.primary().head, "div"); var wrapOff = display.wrapper.getBoundingClientRect(), lineOff = display.lineDiv.getBoundingClientRect(); - var top = Math.max(0, Math.min(display.wrapper.clientHeight - 10, - headPos.top + lineOff.top - wrapOff.top)); - var left = Math.max(0, Math.min(display.wrapper.clientWidth - 10, - headPos.left + lineOff.left - wrapOff.left)); - display.inputDiv.style.top = top + "px"; - display.inputDiv.style.left = left + "px"; + result.teTop = Math.max(0, Math.min(display.wrapper.clientHeight - 10, + headPos.top + lineOff.top - wrapOff.top)); + result.teLeft = Math.max(0, Math.min(display.wrapper.clientWidth - 10, + headPos.left + lineOff.left - wrapOff.left)); } - removeChildrenAndAdd(display.cursorDiv, curFragment); - removeChildrenAndAdd(display.selectionDiv, selFragment); + return result; + } + + function updateSelection(cm, drawn) { + if (!drawn) drawn = drawSelection(cm); + removeChildrenAndAdd(cm.display.cursorDiv, drawn.cursors); + removeChildrenAndAdd(cm.display.selectionDiv, drawn.selection); + if (drawn.teTop != null) { + cm.display.inputDiv.style.top = drawn.teTop + "px"; + cm.display.inputDiv.style.left = drawn.teLeft + "px"; + } } // Draws a cursor for the given range @@ -1967,28 +1966,88 @@ if (!group) return; try { fireCallbacksForOps(group); } - finally { - cm.curOp = operationGroup = null; + finally { + operationGroup = null; for (var i = 0; i < group.ops.length; i++) - endOperationInner(group.ops[i]); + group.ops[i].cm.curOp = null; + endOperations(group); } } - function endOperationInner(op) { - var cm = op.cm, display = cm.display, doc = cm.doc; + // The DOM updates done when an operation finishes are batched so + // that the minimum number of relayouts are required. + function endOperations(group) { + var ops = group.ops; + for (var i = 0; i < ops.length; i++) // Read DOM + endOperation_R1(ops[i]); + for (var i = 0; i < ops.length; i++) // Write DOM (maybe) + endOperation_W1(ops[i]); + for (var i = 0; i < ops.length; i++) // Read DOM + endOperation_R2(ops[i]); + for (var i = 0; i < ops.length; i++) // Write DOM (maybe) + endOperation_W2(ops[i]); + for (var i = 0; i < ops.length; i++) // Read DOM + endOperation_finish(ops[i]); + } + + function endOperation_R1(op) { + var cm = op.cm, display = cm.display; if (op.updateMaxLine) findMaxLine(cm); - // If it looks like an update might be needed, call updateDisplay - if (op.viewChanged || op.forceUpdate || op.scrollTop != null || - op.scrollToPos && (op.scrollToPos.from.line < display.viewFrom || - op.scrollToPos.to.line >= display.viewTo) || - display.maxLineChanged && cm.options.lineWrapping) { - var updated = updateDisplay(cm, {top: op.scrollTop, ensure: op.scrollToPos}, op.forceUpdate); - if (cm.display.scroller.offsetHeight) doc.scrollTop = cm.display.scroller.scrollTop; + op.mustUpdate = op.viewChanged || op.forceUpdate || op.scrollTop != null || + op.scrollToPos && (op.scrollToPos.from.line < display.viewFrom || + op.scrollToPos.to.line >= display.viewTo) || + display.maxLineChanged && cm.options.lineWrapping; + op.update = op.mustUpdate && + new DisplayUpdate(cm, op.mustUpdate && {top: op.scrollTop, ensure: op.scrollToPos}, op.forceUpdate); + } + + function endOperation_W1(op) { + op.updatedDisplay = op.mustUpdate && updateDisplayIfNeeded(op.cm, op.update); + } + + function endOperation_R2(op) { + var cm = op.cm, display = cm.display; + if (op.updatedDisplay) postUpdateDisplay(cm, op.update); + + // If the max line changed since it was last measured, measure it, + // and ensure the document's width matches it. + // updateDisplayIfNeeded will use these properties to do the actual resizing + if (display.maxLineChanged && !cm.options.lineWrapping) { + op.adjustWidthTo = measureChar(cm, display.maxLine, display.maxLine.text.length).left; + op.maxScrollLeft = Math.max(0, display.sizer.offsetLeft + op.adjustWidthTo + + scrollerCutOff - display.scroller.clientWidth); } - // If no update was run, but the selection changed, redraw that. - if (!updated && op.selectionChanged) updateSelection(cm); - if (!updated && op.startHeight != doc.height) updateScrollbars(cm); + + op.barMeasure = measureForScrollbars(cm); + if (op.updatedDisplay || op.selectionChanged) + op.newSelectionNodes = drawSelection(cm); + } + + function endOperation_W2(op) { + var cm = op.cm; + + if (op.adjustWidthTo != null) { + cm.display.sizer.style.minWidth = op.adjustWidthTo + "px"; + if (op.maxScrollLeft < cm.doc.scrollLeft) + setScrollLeft(cm, Math.min(cm.display.scroller.scrollLeft, op.maxScrollLeft), true); + } + + if (op.newSelectionNodes) + updateSelection(cm, op.newSelectionNodes); + if (op.updatedDisplay) + setDocumentHeight(cm, op.barMeasure); + if (op.updatedDisplay || op.startHeight != cm.doc.height) + updateScrollbars(cm, op.barMeasure); + + if (op.selectionChanged) restartBlink(cm); + + if (cm.state.focused && op.updateInput) + resetInput(cm, op.typing); + } + + function endOperation_finish(op) { + var cm = op.cm, display = cm.display, doc = cm.doc; // Abort mouse wheel delta measurement, when scrolling explicitly if (display.wheelStartX != null && (op.scrollTop != null || op.scrollLeft != null || op.scrollToPos)) @@ -2011,11 +2070,6 @@ if (op.scrollToPos.isCursor && cm.state.focused) maybeScrollWindow(cm, coords); } - if (op.selectionChanged) restartBlink(cm); - - if (cm.state.focused && op.updateInput) - resetInput(cm, op.typing); - // Fire events for markers that are hidden/unidden by editing or // undoing var hidden = op.maybeHiddenMarkers, unhidden = op.maybeUnhiddenMarkers; @@ -2024,6 +2078,19 @@ if (unhidden) for (var i = 0; i < unhidden.length; ++i) if (unhidden[i].lines.length) signal(unhidden[i], "unhide"); + if (display.wrapper.offsetHeight) + doc.scrollTop = cm.display.scroller.scrollTop; + + // Apply workaround for two webkit bugs + if (op.updatedDisplay && webkit) { + if (cm.options.lineWrapping) + checkForWebkitWidthBug(cm, op.barMeasure); // (Issue #2420) + if (op.barMeasure.scrollWidth > op.barMeasure.clientWidth && + op.barMeasure.scrollWidth < op.barMeasure.clientWidth + 1 && + !hScrollbarTakesSpace(cm)) + updateScrollbars(cm); // (Issue #2562) + } + // Fire change events, and delayed event handlers if (op.changeObjs) signal(cm, "changes", cm, op.changeObjs); @@ -2916,10 +2983,10 @@ function setScrollTop(cm, val) { if (Math.abs(cm.doc.scrollTop - val) < 2) return; cm.doc.scrollTop = val; - if (!gecko) updateDisplay(cm, {top: val}); + if (!gecko) updateDisplaySimple(cm, {top: val}); if (cm.display.scroller.scrollTop != val) cm.display.scroller.scrollTop = val; if (cm.display.scrollbarV.scrollTop != val) cm.display.scrollbarV.scrollTop = val; - if (gecko) updateDisplay(cm); + if (gecko) updateDisplaySimple(cm); startWorker(cm, 100); } // Sync scroller and scrollbar, ensure the gutter elements are @@ -3002,7 +3069,7 @@ var top = cm.doc.scrollTop, bot = top + display.wrapper.clientHeight; if (pixels < 0) top = Math.max(0, top + pixels - 50); else bot = Math.min(cm.doc.height, bot + pixels + 50); - updateDisplay(cm, {top: top, bottom: bot}); + updateDisplaySimple(cm, {top: top, bottom: bot}); } if (wheelSamples < 20) { From c698ab758b4682c2aca55135c9948735dd75a05f Mon Sep 17 00:00:00 2001 From: Marijn Haverbeke Date: Fri, 4 Jul 2014 12:26:41 +0200 Subject: [PATCH 2471/5780] [multi-editor operations] Add a few tests --- test/test.js | 35 +++++++++++++++++++++++++++++++++++ 1 file changed, 35 insertions(+) diff --git a/test/test.js b/test/test.js index 4ef4ef547d..dcfffb7c07 100644 --- a/test/test.js +++ b/test/test.js @@ -1988,3 +1988,38 @@ testCM("resizeLineWidget", function(cm) { cm.setSize(40); is(widget.parentNode.offsetWidth < 42); }); + +testCM("combinedOperations", function(cm) { + var place = document.getElementById("testground"); + var other = CodeMirror(place, {value: "123"}); + try { + cm.operation(function() { + cm.addLineClass(0, "wrap", "foo"); + other.addLineClass(0, "wrap", "foo"); + }); + eq(byClassName(cm.getWrapperElement(), "foo").length, 1); + eq(byClassName(other.getWrapperElement(), "foo").length, 1); + cm.operation(function() { + cm.removeLineClass(0, "wrap", "foo"); + other.removeLineClass(0, "wrap", "foo"); + }); + eq(byClassName(cm.getWrapperElement(), "foo").length, 0); + eq(byClassName(other.getWrapperElement(), "foo").length, 0); + } finally { + place.removeChild(other.getWrapperElement()); + } +}, {value: "abc"}); + +testCM("eventOrder", function(cm) { + var seen = []; + cm.on("change", function() { + if (!seen.length) cm.replaceSelection("."); + seen.push("change"); + }); + cm.on("cursorActivity", function() { + cm.replaceSelection("!"); + seen.push("activity"); + }); + cm.replaceSelection("/"); + eq(seen.join(","), "change,change,activity,change"); +}); From 63591907b0dcd51c2f64dc967143e044ecac6923 Mon Sep 17 00:00:00 2001 From: Marijn Haverbeke Date: Mon, 7 Jul 2014 14:30:27 +0200 Subject: [PATCH 2472/5780] Work around IE10- client rect + zoom issue Issue #2665 --- lib/codemirror.js | 24 ++++++++++++++++++++++++ test/lint/lint.js | 2 +- 2 files changed, 25 insertions(+), 1 deletion(-) diff --git a/lib/codemirror.js b/lib/codemirror.js index 1bf02d2089..cc7caace19 100644 --- a/lib/codemirror.js +++ b/lib/codemirror.js @@ -1667,6 +1667,8 @@ rect = nullRect; } + if (ie && ie_version < 11) rect = maybeUpdateRectForZooming(cm.display.measure, rect); + var rtop = rect.top - prepared.rect.top, rbot = rect.bottom - prepared.rect.top; var mid = (rtop + rbot) / 2; var heights = prepared.view.measure.heights; @@ -1678,9 +1680,22 @@ top: top, bottom: bot}; if (!rect.left && !rect.right) result.bogus = true; if (!cm.options.singleCursorHeightPerLine) { result.rtop = rtop; result.rbottom = rbot; } + return result; } + // Work around problem with bounding client rects on ranges being + // returned incorrectly when zoomed on IE10 and below. + function maybeUpdateRectForZooming(measure, rect) { + if (!window.screen || screen.logicalXDPI == null || + screen.logicalXDPI == screen.deviceXDPI || !hasBadZoomedRects(measure)) + return rect; + var scaleX = screen.logicalXDPI / screen.deviceXDPI; + var scaleY = screen.logicalYDPI / screen.deviceYDPI; + return {left: rect.left * scaleX, right: rect.right * scaleX, + top: rect.top * scaleY, bottom: rect.bottom * scaleY}; + } + function clearLineMeasurementCacheFor(lineView) { if (lineView.measure) { lineView.measure.cache = {}; @@ -7432,6 +7447,15 @@ return typeof e.oncopy == "function"; })(); + var badZoomedRects = null; + function hasBadZoomedRects(measure) { + if (badZoomedRects != null) return badZoomedRects; + var node = removeChildrenAndAdd(measure, elt("span", "x")); + var normal = node.getBoundingClientRect(); + var fromRange = range(node, 0, 1).getBoundingClientRect(); + return badZoomedRects = Math.abs(normal.left - fromRange.left) > 1; + } + // KEY NAMES var keyNames = {3: "Enter", 8: "Backspace", 9: "Tab", 13: "Enter", 16: "Shift", 17: "Ctrl", 18: "Alt", diff --git a/test/lint/lint.js b/test/lint/lint.js index ef4c13bd9a..c2c45262a6 100644 --- a/test/lint/lint.js +++ b/test/lint/lint.js @@ -19,7 +19,7 @@ var topAllowedGlobals = Object.create(null); ("Error RegExp Number String Array Function Object Math Date undefined " + "parseInt parseFloat Infinity NaN isNaN " + "window document navigator prompt alert confirm console " + - "FileReader Worker postMessage importScripts " + + "screen FileReader Worker postMessage importScripts " + "setInterval clearInterval setTimeout clearTimeout " + "CodeMirror " + "test exports require module define") From 57761d076413ad7e49f1e45d94b122dd8dfb2c55 Mon Sep 17 00:00:00 2001 From: Jaydeep Solanki Date: Sat, 5 Jul 2014 06:06:24 +0530 Subject: [PATCH 2473/5780] [ruby mode] Better heuristic for distinguishing division from regexps --- mode/ruby/ruby.js | 20 +++++++++++++++++--- 1 file changed, 17 insertions(+), 3 deletions(-) diff --git a/mode/ruby/ruby.js b/mode/ruby/ruby.js index b68ee2912a..d6c9d8796e 100644 --- a/mode/ruby/ruby.js +++ b/mode/ruby/ruby.js @@ -46,9 +46,23 @@ CodeMirror.defineMode("ruby", function(config) { var ch = stream.next(), m; if (ch == "`" || ch == "'" || ch == '"') { return chain(readQuoted(ch, "string", ch == '"' || ch == "`"), stream, state); - } else if (ch == "/" && !stream.eol() && stream.peek() != " ") { - if (stream.eat("=")) return "operator"; - return chain(readQuoted(ch, "string-2", true), stream, state); + } else if (ch == "/") { + var currentIndex = stream.current().length; + if (stream.skipTo("/")) { + var search_till = stream.current().length; + stream.backUp(stream.current().length - currentIndex); + var balance = 0; // balance brackets + while (stream.current().length < search_till) { + var chchr = stream.next(); + if (chchr == "(") balance += 1; + else if (chchr == ")") balance -= 1; + if (balance < 0) break; + } + stream.backUp(stream.current().length - currentIndex); + if (balance == 0) + return chain(readQuoted(ch, "string-2", true), stream, state); + } + return "operator"; } else if (ch == "%") { var style = "string", embed = true; if (stream.eat("s")) style = "atom"; From 347affcf2784dc8ed6e13abba517f7bd4968e715 Mon Sep 17 00:00:00 2001 From: Marijn Haverbeke Date: Mon, 7 Jul 2014 23:22:38 +0200 Subject: [PATCH 2474/5780] Handle altGr in early return from onKeyPress Issue #2671 --- lib/codemirror.js | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/lib/codemirror.js b/lib/codemirror.js index cc7caace19..dcbacc258b 100644 --- a/lib/codemirror.js +++ b/lib/codemirror.js @@ -3233,7 +3233,7 @@ function onKeyPress(e) { var cm = this; - if (signalDOMEvent(cm, e) || e.ctrlKey || mac && e.metaKey) return; + if (signalDOMEvent(cm, e) || e.ctrlKey && !e.altKey || mac && e.metaKey) return; var keyCode = e.keyCode, charCode = e.charCode; if (presto && keyCode == lastStoppedKey) {lastStoppedKey = null; e_preventDefault(e); return;} if (((presto && (!e.which || e.which < 10)) || khtml) && handleKeyBinding(cm, e)) return; From 93c846bd2b3190459aed37399a3b5583d578911a Mon Sep 17 00:00:00 2001 From: binny Date: Wed, 2 Jul 2014 00:09:42 +0530 Subject: [PATCH 2475/5780] [vim] reselectSelection for visualBlock --- keymap/vim.js | 78 ++++++++++++++++++++++++++++++++---------------- test/vim_test.js | 24 ++++++++++++--- 2 files changed, 73 insertions(+), 29 deletions(-) diff --git a/keymap/vim.js b/keymap/vim.js index 6ff5edce4c..ab61e17afb 100644 --- a/keymap/vim.js +++ b/keymap/vim.js @@ -1858,6 +1858,8 @@ }, // delete is a javascript keyword. 'delete': function(cm, operatorArgs, vim, curStart, curEnd) { + // Save the '>' mark before cm.replaceRange clears it. + var selectionEnd = vim.visualMode ? vim.marks['>'].find() : null; // If the ending line is past the last line, inclusive, instead of // including the trailing \n, include the \n before the starting line if (operatorArgs.linewise && @@ -1876,6 +1878,10 @@ } else { cm.replaceRange('', curStart, curEnd); } + // restore the saved bookmark + if (selectionEnd) { + vim.marks['>'] = cm.setBookmark(selectionEnd); + } if (operatorArgs.linewise) { cm.setCursor(motions.moveToFirstNonWhiteSpaceCharacter(cm)); } else { @@ -2085,7 +2091,6 @@ curStart = cm.getCursor('anchor'); curEnd = cm.getCursor('head'); if (vim.visualLine) { - vim.visualLine = false; if (actionArgs.blockwise) { // This means Ctrl-V pressed in linewise visual vim.visualBlock = true; @@ -2097,8 +2102,8 @@ } else { exitVisualMode(cm); } + vim.visualLine = false; } else if (vim.visualBlock) { - vim.visualBlock = false; if (actionArgs.linewise) { // Shift-V pressed in blockwise visual mode vim.visualLine = true; @@ -2118,6 +2123,7 @@ } else { exitVisualMode(cm); } + vim.visualBlock = false; } else if (actionArgs.linewise) { // Shift-V pressed in characterwise visual mode. Switch to linewise // visual mode instead of exiting visual mode. @@ -2142,27 +2148,40 @@ : curStart); }, reselectLastSelection: function(cm, _actionArgs, vim) { + var curStart = vim.marks['<'].find(); + var curEnd = vim.marks['>'].find(); var lastSelection = vim.lastSelection; if (lastSelection) { - var curStart = lastSelection.curStartMark.find(); - var curEnd = lastSelection.curEndMark.find(); - cm.setSelection(curStart, curEnd); + // Set the selections as per last selection + var selectionStart = lastSelection.curStartMark.find(); + var selectionEnd = lastSelection.curEndMark.find(); + var blockwise = lastSelection.visualBlock; + // update last selection + updateLastSelection(cm, vim, curStart, curEnd); + if (blockwise) { + cm.setCursor(selectionStart); + selectionStart = selectBlock(cm, selectionEnd); + } else { + cm.setSelection(selectionStart, selectionEnd); + selectionStart = cm.getCursor('anchor'); + selectionEnd = cm.getCursor('head'); + } if (vim.visualMode) { - updateLastSelection(cm, vim); - var selectionStart = cm.getCursor('anchor'); - var selectionEnd = cm.getCursor('head'); updateMark(cm, vim, '<', cursorIsBefore(selectionStart, selectionEnd) ? selectionStart - : selectionEnd); + : selectionEnd); updateMark(cm, vim, '>', cursorIsBefore(selectionStart, selectionEnd) ? selectionEnd - : selectionStart); + : selectionStart); } + // Last selection is updated now + vim.visualMode = true; if (lastSelection.visualLine) { - vim.visualMode = true; vim.visualLine = true; - } - else { - vim.visualMode = true; + vim.visualBlock = false; + } else if (lastSelection.visualBlock) { vim.visualLine = false; + vim.visualBlock = true; + } else { + vim.visualBlock = vim.visualLine = false; } CodeMirror.signal(cm, "vim-mode-change", {mode: "visual", subMode: vim.visualLine ? "linewise" : ""}); } @@ -2552,6 +2571,10 @@ } start++; } + // Update selectionEnd and selectionStart + // after selection crossing + selectionEnd.ch = selections[0].head.ch; + selectionStart.ch = selections[0].anchor.ch; cm.setSelections(selections, primIndex); return selectionStart; } @@ -2586,22 +2609,27 @@ } return [selectionStart, selectionEnd]; } - function updateLastSelection(cm, vim) { - // We need the vim mark '<' to get the selection in case of yank and put - var selectionStart = vim.marks['<'].find() || cm.getCursor('anchor'); - var selectionEnd = vim.marks['>'].find() ||cm.getCursor('head'); - // To accommodate the effect lastPastedText in the last selection + function updateLastSelection(cm, vim, selectionStart, selectionEnd) { + if (!selectionStart || !selectionEnd) { + selectionStart = vim.marks['<'].find() || cm.getCursor('anchor'); + selectionEnd = vim.marks['>'].find() || cm.getCursor('head'); + } + // To accommodate the effect of lastPastedText in the last selection if (vim.lastPastedText) { - selectionEnd = cm.posFromIndex(cm.indexFromPos(selectionStart) + vim.lastPastedText.length-1); + selectionEnd = cm.posFromIndex(cm.indexFromPos(selectionStart) + vim.lastPastedText.length); vim.lastPastedText = null; } + var ranges = cm.listSelections(); + // This check ensures to set the cursor + // position where we left off in previous selection + var swap = getIndex(ranges, selectionStart) > -1; // can't use selection state here because yank has already reset its cursor // Also, Bookmarks make the visual selections robust to edit operations - vim.lastSelection = {'curStartMark': cm.setBookmark(selectionStart), 'curEndMark': cm.setBookmark(selectionEnd), 'visualMode': vim.visualMode, 'visualLine': vim.visualLine}; - if (cursorIsBefore(selectionEnd, selectionStart)) { - vim.lastSelection.curStartMark = cm.setBookmark(selectionEnd); - vim.lastSelection.curEndMark = cm.setBookmark(selectionStart); - } + vim.lastSelection = {'curStartMark': cm.setBookmark(swap ? selectionEnd : selectionStart), + 'curEndMark': cm.setBookmark(swap ? selectionStart : selectionEnd), + 'visualMode': vim.visualMode, + 'visualLine': vim.visualLine, + 'visualBlock': vim.visualBlock}; } function exitVisualMode(cm) { diff --git a/test/vim_test.js b/test/vim_test.js index b09d730d50..1c9e8c3c9a 100644 --- a/test/vim_test.js +++ b/test/vim_test.js @@ -1655,7 +1655,8 @@ testVim('reselect_visual', function(cm, vim, helpers) { eq('123456\n2345\nbar', cm.getValue()); cm.setCursor(0, 0); helpers.doKeys('g', 'v'); - helpers.assertCursorAt(1, 3); + // here the fake cursor is at (1, 3) + helpers.assertCursorAt(2, 0); eqPos(makeCursor(1, 0), cm.getCursor('anchor')); helpers.doKeys('v'); cm.setCursor(2, 0); @@ -1669,17 +1670,32 @@ testVim('reselect_visual', function(cm, vim, helpers) { }, { value: '123456\nfoo\nbar' }); testVim('reselect_visual_line', function(cm, vim, helpers) { helpers.doKeys('l', 'V', 'j', 'j', 'V', 'g', 'v', 'd'); - eq('\nfoo\nand\nbar', cm.getValue()); + eq('foo\nand\nbar', cm.getValue()); cm.setCursor(1, 0); helpers.doKeys('V', 'y', 'j'); helpers.doKeys('V', 'p' , 'g', 'v', 'd'); - eq('\nfoo\nbar', cm.getValue()); + eq('foo\nand', cm.getValue()); }, { value: 'hello\nthis\nis\nfoo\nand\nbar' }); +testVim('reselect_visual_block', function(cm, vim, helpers) { + cm.setCursor(1, 2); + helpers.doKeys('', 'k', 'h', ''); + cm.setCursor(2, 1); + helpers.doKeys('v', 'l', 'g', 'v'); + helpers.assertCursorAt(0, 1); + // Ensure selection is done with visual block mode rather than one + // continuous range. + eq(cm.getSelections().join(''), '23oo') + helpers.doKeys('g', 'v'); + helpers.assertCursorAt(2, 3); + // Ensure selection of deleted range + cm.setCursor(1, 1); + helpers.doKeys('v', '', 'j', 'd', 'g', 'v'); + eq(cm.getSelections().join(''), 'or'); +}, { value: '123456\nfoo\nbar' }); testVim('s_normal', function(cm, vim, helpers) { cm.setCursor(0, 1); helpers.doKeys('s'); helpers.doInsertModeKeys('Esc'); - helpers.assertCursorAt(0, 0); eq('ac', cm.getValue()); }, { value: 'abc'}); testVim('s_visual', function(cm, vim, helpers) { From 238b451301227eb8987a4204fc726e1f1207777f Mon Sep 17 00:00:00 2001 From: Marijn Haverbeke Date: Tue, 8 Jul 2014 23:44:59 +0200 Subject: [PATCH 2476/5780] Fix broken double-checking of display coverage Both a potential infinite loop, due to updateDisplayIfNeeded not using the updated set of visibile lines, and the fact that the current check wasn't really covering changing document size, due to it happening before the call to setDocumentHeight Issue #2683 --- lib/codemirror.js | 14 ++++++++++---- 1 file changed, 10 insertions(+), 4 deletions(-) diff --git a/lib/codemirror.js b/lib/codemirror.js index dcbacc258b..19abaf50b7 100644 --- a/lib/codemirror.js +++ b/lib/codemirror.js @@ -632,7 +632,6 @@ function postUpdateDisplay(cm, update) { var force = update.force, viewport = update.viewport; for (var first = true;; first = false) { - updateHeightsInViewport(cm); if (first && cm.options.lineWrapping && update.oldScrollerWidth != cm.display.scroller.clientWidth) { force = true; } else { @@ -643,11 +642,16 @@ cm.display.scroller.clientHeight, viewport.top)}; // Updated line heights might result in the drawn area not // actually covering the viewport. Keep looping until it does. - var visible = visibleLines(cm.display, cm.doc, viewport); - if (visible.from >= cm.display.viewFrom && visible.to <= cm.display.viewTo) + update.visible = visibleLines(cm.display, cm.doc, viewport); + if (update.visible.from >= cm.display.viewFrom && update.visible.to <= cm.display.viewTo) break; } if (!updateDisplayIfNeeded(cm, update)) break; + updateHeightsInViewport(cm); + var barMeasure = measureForScrollbars(cm); + updateSelection(cm); + setDocumentHeight(cm, barMeasure); + updateScrollbars(cm, barMeasure); } signalLater(cm, "update", cm); @@ -2023,7 +2027,7 @@ function endOperation_R2(op) { var cm = op.cm, display = cm.display; - if (op.updatedDisplay) postUpdateDisplay(cm, op.update); + if (op.updatedDisplay) updateHeightsInViewport(cm); // If the max line changed since it was last measured, measure it, // and ensure the document's width matches it. @@ -2064,6 +2068,8 @@ function endOperation_finish(op) { var cm = op.cm, display = cm.display, doc = cm.doc; + if (op.updatedDisplay) postUpdateDisplay(cm, op.update); + // Abort mouse wheel delta measurement, when scrolling explicitly if (display.wheelStartX != null && (op.scrollTop != null || op.scrollLeft != null || op.scrollToPos)) display.wheelStartX = display.wheelStartY = null; From 6c0cd2b56b50837a010bd27f322a57edfbe9fee9 Mon Sep 17 00:00:00 2001 From: Richard van der Meer Date: Tue, 8 Jul 2014 11:51:35 +0200 Subject: [PATCH 2477/5780] [vbscript mode] Fixed "Cannot read property 'substr' of null" Error occurs when entering multiple dots --- mode/vbscript/vbscript.js | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/mode/vbscript/vbscript.js b/mode/vbscript/vbscript.js index bff2594477..b66df2239a 100644 --- a/mode/vbscript/vbscript.js +++ b/mode/vbscript/vbscript.js @@ -291,7 +291,7 @@ CodeMirror.defineMode("vbscript", function(conf, parserConf) { style = state.tokenize(stream, state); current = stream.current(); - if (style.substr(0, 8) === 'variable' || style==='builtin' || style==='keyword'){//|| knownWords.indexOf(current.substring(1)) > -1) { + if (style && (style.substr(0, 8) === 'variable' || style==='builtin' || style==='keyword')){//|| knownWords.indexOf(current.substring(1)) > -1) { if (style === 'builtin' || style === 'keyword') style='variable'; if (knownWords.indexOf(current.substr(1)) > -1) style='variable-2'; From e92998af5dd97c771821f03c223b88442974bc6b Mon Sep 17 00:00:00 2001 From: Yunchi Luo Date: Tue, 8 Jul 2014 20:35:07 -0700 Subject: [PATCH 2478/5780] [vim] Add features list to demo page --- demo/vim.html | 32 ++++++++++++++++++++------------ 1 file changed, 20 insertions(+), 12 deletions(-) diff --git a/demo/vim.html b/demo/vim.html index f92ff9e6d8..cc616759f0 100644 --- a/demo/vim.html +++ b/demo/vim.html @@ -45,17 +45,32 @@

    Vim bindings demo

    return (--n >= 0) ? (unsigned char) *bufp++ : EOF; } -
    - -
    +
    Key buffer:

    The vim keybindings are enabled by including keymap/vim.js and setting the vimMode option to true. This will also automatically change the keyMap option to "vim".

    +

    Features

    + +
      +
    • All common motions and operators, including text objects
    • +
    • Operator motion orthogonality
    • +
    • Visual mode - characterwise, linewise, partial support for blockwise
    • +
    • Full macro support (q, @)
    • +
    • Incremental highlighted search (/, ?, #, *, g#, g*)
    • +
    • Search/replace with confirm (:substitute, :%s)
    • +
    • Search history
    • +
    • Jump lists (Ctrl-o, Ctrl-i)
    • +
    • Key/command mapping with API (:map, :nmap, :vmap)
    • +
    • Sort (:sort)
    • +
    • Marks (`, ')
    • +
    • :global
    • +
    • Insert mode behaves identical to base CodeMirror
    • +
    • Cross-buffer yank/paste
    • +
    +

    Note that while the vim mode tries to emulate the most useful features of vim as faithfully as possible, it does not strive to become a complete vim implementation

    @@ -69,13 +84,6 @@

    Vim bindings demo

    matchBrackets: true, showCursorWhenSelecting: true }); - var editor2 = CodeMirror.fromTextArea(document.getElementById("code2"), { - lineNumbers: true, - mode: "text/x-csrc", - vimMode: true, - matchBrackets: true, - showCursorWhenSelecting: true - }); var commandDisplay = document.getElementById('command-display'); var keys = ''; CodeMirror.on(editor, 'vim-keypress', function(key) { From 770c0970cf2e3541ad21ee6fa0c7d78b21b368e0 Mon Sep 17 00:00:00 2001 From: binny Date: Wed, 9 Jul 2014 19:15:58 +0530 Subject: [PATCH 2479/5780] [vim] change for blockwise visual --- keymap/vim.js | 51 ++++++++++++++++++++++++++++++++++++------------ test/vim_test.js | 14 +++++++++++++ 2 files changed, 52 insertions(+), 13 deletions(-) diff --git a/keymap/vim.js b/keymap/vim.js index ab61e17afb..c1b0df4683 100644 --- a/keymap/vim.js +++ b/keymap/vim.js @@ -1831,17 +1831,39 @@ }; var operators = { - change: function(cm, operatorArgs, _vim, curStart, curEnd) { + change: function(cm, operatorArgs, vim) { + var selections = cm.listSelections(); + var start = selections[0], end = selections[selections.length-1]; + var curStart = cursorIsBefore(start.anchor, start.head) ? start.anchor : start.head; + var curEnd = cursorIsBefore(end.anchor, end.head) ? end.head : end.anchor; + var text = cm.getSelection(); + var replacement = new Array(selections.length).join('1').split('1'); vimGlobalState.registerController.pushText( - operatorArgs.registerName, 'change', cm.getRange(curStart, curEnd), + operatorArgs.registerName, 'change', text, operatorArgs.linewise); if (operatorArgs.linewise) { - // Push the next line back down, if there is a next line. - var replacement = curEnd.line > cm.lastLine() ? '' : '\n'; - cm.replaceRange(replacement, curStart, curEnd); - cm.indentLine(curStart.line, 'smart'); - // null ch so setCursor moves to end of line. - curStart.ch = null; + // 'C' in visual block extends the block till eol for all lines + if (vim.visualBlock){ + var startLine = curStart.line; + while (startLine <= curEnd.line) { + var endCh = lineLength(cm, startLine); + var head = Pos(startLine, endCh); + var anchor = Pos(startLine, curStart.ch); + startLine++; + cm.replaceRange('', anchor, head); + } + } else { + // Push the next line back down, if there is a next line. + replacement = '\n'; + if (curEnd.line == curStart.line && curEnd.line == cm.lastLine()) { + replacement = ''; + } + cm.replaceRange(replacement, curStart, curEnd); + cm.indentLine(curStart.line, 'smart'); + // null ch so setCursor moves to end of line. + curStart.ch = null; + cm.setCursor(curStart); + } } else { // Exclude trailing whitespace if the range is not all whitespace. var text = cm.getRange(curStart, curEnd); @@ -1851,15 +1873,20 @@ curEnd = offsetCursor(curEnd, 0, - match[0].length); } } - cm.replaceRange('', curStart, curEnd); + if (vim.visualBlock) { + cm.replaceSelections(replacement); + } else { + cm.setCursor(curStart); + cm.replaceRange('', curStart, curEnd); + } } actions.enterInsertMode(cm, {}, cm.state.vim); - cm.setCursor(curStart); }, // delete is a javascript keyword. 'delete': function(cm, operatorArgs, vim, curStart, curEnd) { // Save the '>' mark before cm.replaceRange clears it. var selectionEnd = vim.visualMode ? vim.marks['>'].find() : null; + var text = cm.getSelection(); // If the ending line is past the last line, inclusive, instead of // including the trailing \n, include the \n before the starting line if (operatorArgs.linewise && @@ -1868,7 +1895,7 @@ curStart.ch = lineLength(cm, curStart.line); } vimGlobalState.registerController.pushText( - operatorArgs.registerName, 'delete', cm.getRange(curStart, curEnd), + operatorArgs.registerName, 'delete', text, operatorArgs.linewise); if (vim.visualBlock) { var selections = cm.listSelections(); @@ -1926,8 +1953,6 @@ var curStart = ranges[0].anchor; var curEnd = ranges[0].head; if (!operatorArgs.shouldMoveCursor) { - // extendSelection swaps curStart and curEnd, so make sure - // curStart < curEnd cm.setCursor(cursorIsBefore(curStart, curEnd) ? curStart : curEnd); } }, diff --git a/test/vim_test.js b/test/vim_test.js index 1c9e8c3c9a..76a2699780 100644 --- a/test/vim_test.js +++ b/test/vim_test.js @@ -895,6 +895,20 @@ testVim('cc_append', function(cm, vim, helpers) { helpers.doKeys('c', 'c'); eq(expectedLineCount, cm.lineCount()); }); +testVim('c_visual_block', function(cm, vim, helpers) { + cm.setCursor(0, 1); + helpers.doKeys('', '2', 'j', 'l', 'l', 'l', 'c'); + var replacement = new Array(cm.listSelections().length+1).join('hello ').split(' '); + replacement.pop(); + cm.replaceSelections(replacement); + eq('1hello\n5hello\nahellofg', cm.getValue()); + cm.setCursor(2, 3); + helpers.doKeys('', '2', 'k', 'h', 'C'); + replacement = new Array(cm.listSelections().length+1).join('world ').split(' '); + replacement.pop(); + cm.replaceSelections(replacement); + eq('1hworld\n5hworld\nahworld', cm.getValue()); +}, {value: '1234\n5678\nabcdefg'}); // Swapcase commands edit in place and do not modify registers. testVim('g~w_repeat', function(cm, vim, helpers) { // Assert that dw does delete newline if it should go to the next line, and From 5285494ab15ba444bc95f956c389502796b0bce1 Mon Sep 17 00:00:00 2001 From: Marijn Haverbeke Date: Fri, 11 Jul 2014 08:51:22 +0200 Subject: [PATCH 2480/5780] Ignore auto indentation beyond column 80 Issue #2688 --- lib/codemirror.js | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/lib/codemirror.js b/lib/codemirror.js index 19abaf50b7..49fd45a084 100644 --- a/lib/codemirror.js +++ b/lib/codemirror.js @@ -3812,7 +3812,7 @@ how = "not"; } else if (how == "smart") { indentation = cm.doc.mode.indent(state, line.text.slice(curSpaceString.length), line.text); - if (indentation == Pass) { + if (indentation == Pass || indentation > 80) { if (!aggressive) return; how = "prev"; } From ce7536377698f682eaf3959408c70df665f690cf Mon Sep 17 00:00:00 2001 From: Marijn Haverbeke Date: Fri, 11 Jul 2014 08:56:12 +0200 Subject: [PATCH 2481/5780] Bump indentation-ignoring threshold to 150 You might actually want to align things on long lines Issue #2688 --- lib/codemirror.js | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/lib/codemirror.js b/lib/codemirror.js index 49fd45a084..ad77be2cb9 100644 --- a/lib/codemirror.js +++ b/lib/codemirror.js @@ -3812,7 +3812,7 @@ how = "not"; } else if (how == "smart") { indentation = cm.doc.mode.indent(state, line.text.slice(curSpaceString.length), line.text); - if (indentation == Pass || indentation > 80) { + if (indentation == Pass || indentation > 150) { if (!aggressive) return; how = "prev"; } From ae978a7e9372f7bae489510c68740741dfe67e05 Mon Sep 17 00:00:00 2001 From: Marijn Haverbeke Date: Fri, 11 Jul 2014 11:20:31 +0200 Subject: [PATCH 2482/5780] Bind Cmd-Home to goDocStart on Mac Closes #2687 --- doc/manual.html | 2 +- lib/codemirror.js | 2 +- 2 files changed, 2 insertions(+), 2 deletions(-) diff --git a/doc/manual.html b/doc/manual.html index 2deb8422a5..490143e31b 100644 --- a/doc/manual.html +++ b/doc/manual.html @@ -815,7 +815,7 @@

    Commands

    Redo the last change to the selection, or the last text change if no selection changes remain.
    -
    goDocStartCtrl-Up (PC), Cmd-Up (Mac)
    +
    goDocStartCtrl-Up (PC), Cmd-Up (Mac), Cmd-Home (Mac)
    Move the cursor to the start of the document.
    goDocEndCtrl-Down (PC), Cmd-End (Mac), Cmd-Down (Mac)
    diff --git a/lib/codemirror.js b/lib/codemirror.js index ad77be2cb9..bf5d6baa2e 100644 --- a/lib/codemirror.js +++ b/lib/codemirror.js @@ -4824,7 +4824,7 @@ }; keyMap.macDefault = { "Cmd-A": "selectAll", "Cmd-D": "deleteLine", "Cmd-Z": "undo", "Shift-Cmd-Z": "redo", "Cmd-Y": "redo", - "Cmd-Up": "goDocStart", "Cmd-End": "goDocEnd", "Cmd-Down": "goDocEnd", "Alt-Left": "goGroupLeft", + "Cmd-Home": "goDocStart", "Cmd-Up": "goDocStart", "Cmd-End": "goDocEnd", "Cmd-Down": "goDocEnd", "Alt-Left": "goGroupLeft", "Alt-Right": "goGroupRight", "Cmd-Left": "goLineStart", "Cmd-Right": "goLineEnd", "Alt-Backspace": "delGroupBefore", "Ctrl-Alt-Backspace": "delGroupAfter", "Alt-Delete": "delGroupAfter", "Cmd-S": "save", "Cmd-F": "find", "Cmd-G": "findNext", "Shift-Cmd-G": "findPrev", "Cmd-Alt-F": "replace", "Shift-Cmd-Alt-F": "replaceAll", From 0b3d49f4cfc458d481a21b38d7be44fb48c1b46f Mon Sep 17 00:00:00 2001 From: Tim Alby Date: Thu, 10 Jul 2014 17:31:43 +0100 Subject: [PATCH 2483/5780] Fix key-binding behaviour on Mac in wrap mode Bind Cmd-Left to goLineLeft and Cmd-right to goLineRight instead of goLineStart and goLineEnd Create and bind delVisualLeft and delVisualRight to Cmd-Backspace and Cmd-Delete --- doc/manual.html | 16 +++++++++++----- lib/codemirror.js | 18 ++++++++++++++++-- 2 files changed, 27 insertions(+), 7 deletions(-) diff --git a/doc/manual.html b/doc/manual.html index 490143e31b..ed0d076a9f 100644 --- a/doc/manual.html +++ b/doc/manual.html @@ -797,9 +797,15 @@

    Commands

    deleteLineCtrl-D (PC), Cmd-D (Mac)
    Deletes the whole line under the cursor, including newline at the end.
    -
    delLineLeftCmd-Backspace (Mac)
    +
    delLineLeft
    Delete the part of the line before the cursor.
    +
    delWrappedLineLeftCmd-Backspace (Mac)
    +
    Delete the part of the line from the left side of the visual line the cursor is on to the cursor.
    + +
    delWrappedLineRightCmd-Delete (Mac)
    +
    Delete the part of the line from the cursor to the right side of the visual line the cursor is on.
    +
    undoCtrl-Z (PC), Cmd-Z (Mac)
    Undo the last change.
    @@ -821,7 +827,7 @@

    Commands

    goDocEndCtrl-Down (PC), Cmd-End (Mac), Cmd-Down (Mac)
    Move the cursor to the end of the document.
    -
    goLineStartAlt-Left (PC), Cmd-Left (Mac), Ctrl-A (Mac)
    +
    goLineStartAlt-Left (PC), Ctrl-A (Mac)
    Move the cursor to the start of the line.
    goLineStartSmartHome
    @@ -829,14 +835,14 @@

    Commands

    already there, to the actual start of the line (including whitespace). -
    goLineEndAlt-Right (PC), Cmd-Right (Mac), Ctrl-E (Mac)
    +
    goLineEndAlt-Right (PC), Ctrl-E (Mac)
    Move the cursor to the end of the line.
    -
    goLineLeft
    +
    goLineLeftCmd-Left (Mac)
    Move the cursor to the left side of the visual line it is on. If this line is wrapped, that may not be the start of the line.
    -
    goLineRight
    +
    goLineRightCmd-Right (Mac)
    Move the cursor to the right side of the visual line it is on.
    goLineUpUp, Ctrl-P (Mac)
    diff --git a/lib/codemirror.js b/lib/codemirror.js index bf5d6baa2e..3fef17d95e 100644 --- a/lib/codemirror.js +++ b/lib/codemirror.js @@ -4687,6 +4687,20 @@ return {from: Pos(range.from().line, 0), to: range.from()}; }); }, + delWrappedLineLeft: function(cm) { + deleteNearSelection(cm, function(range) { + var top = cm.charCoords(range.head, "div").top + 5; + var leftPos = cm.coordsChar({left: 0, top: top}, "div"); + return {from: leftPos, to: range.from()}; + }); + }, + delWrappedLineRight: function(cm) { + deleteNearSelection(cm, function(range) { + var top = cm.charCoords(range.head, "div").top + 5; + var rightPos = cm.coordsChar({left: cm.display.lineDiv.offsetWidth + 100, top: top}, "div"); + return {from: range.from(), to: rightPos }; + }); + }, undo: function(cm) {cm.undo();}, redo: function(cm) {cm.redo();}, undoSelection: function(cm) {cm.undoSelection();}, @@ -4825,10 +4839,10 @@ keyMap.macDefault = { "Cmd-A": "selectAll", "Cmd-D": "deleteLine", "Cmd-Z": "undo", "Shift-Cmd-Z": "redo", "Cmd-Y": "redo", "Cmd-Home": "goDocStart", "Cmd-Up": "goDocStart", "Cmd-End": "goDocEnd", "Cmd-Down": "goDocEnd", "Alt-Left": "goGroupLeft", - "Alt-Right": "goGroupRight", "Cmd-Left": "goLineStart", "Cmd-Right": "goLineEnd", "Alt-Backspace": "delGroupBefore", + "Alt-Right": "goGroupRight", "Cmd-Left": "goLineLeft", "Cmd-Right": "goLineRight", "Alt-Backspace": "delGroupBefore", "Ctrl-Alt-Backspace": "delGroupAfter", "Alt-Delete": "delGroupAfter", "Cmd-S": "save", "Cmd-F": "find", "Cmd-G": "findNext", "Shift-Cmd-G": "findPrev", "Cmd-Alt-F": "replace", "Shift-Cmd-Alt-F": "replaceAll", - "Cmd-[": "indentLess", "Cmd-]": "indentMore", "Cmd-Backspace": "delLineLeft", + "Cmd-[": "indentLess", "Cmd-]": "indentMore", "Cmd-Backspace": "delWrappedLineLeft", "Cmd-Delete": "delWrappedLineRight", "Cmd-U": "undoSelection", "Shift-Cmd-U": "redoSelection", fallthrough: ["basic", "emacsy"] }; From 0392fd3bb51fe8b2ac0f385ff12e18299900630d Mon Sep 17 00:00:00 2001 From: Marijn Haverbeke Date: Fri, 11 Jul 2014 11:55:55 +0200 Subject: [PATCH 2484/5780] Catch and suppress input of certain code point on Mac Ctrl-arrow key presses were, for some reason, inserting such characters into our textarea. Issue #2689 --- lib/codemirror.js | 7 +++++-- 1 file changed, 5 insertions(+), 2 deletions(-) diff --git a/lib/codemirror.js b/lib/codemirror.js index 3fef17d95e..f52f7865c6 100644 --- a/lib/codemirror.js +++ b/lib/codemirror.js @@ -2388,8 +2388,11 @@ var text = input.value; // If nothing changed, bail. if (text == prevInput && !cm.somethingSelected()) return false; - // Work around nonsensical selection resetting in IE9/10 - if (ie && ie_version >= 9 && cm.display.inputHasSelection === text) { + // Work around nonsensical selection resetting in IE9/10, and + // inexplicable appearance of private area unicode characters on + // some key combos in Mac (#2689). + if (ie && ie_version >= 9 && cm.display.inputHasSelection === text || + mac && /[\uf700-\uf7ff]/.test(text)) { resetInput(cm); return false; } From f02df0b41300a1069ab5e0e3ebae3431b6b507b9 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Roberto=20Abdelkader=20Mart=C3=ADnez=20P=C3=A9rez?= Date: Sun, 6 Jul 2014 04:30:44 +0200 Subject: [PATCH 2485/5780] [lint addon] Do not constrain severity names Issue #2681 --- addon/lint/lint.js | 5 ++--- 1 file changed, 2 insertions(+), 3 deletions(-) diff --git a/addon/lint/lint.js b/addon/lint/lint.js index 604e2e6058..a87e70c09e 100644 --- a/addon/lint/lint.js +++ b/addon/lint/lint.js @@ -11,7 +11,6 @@ })(function(CodeMirror) { "use strict"; var GUTTER_ID = "CodeMirror-lint-markers"; - var SEVERITIES = /^(?:error|warning)$/; function showTooltip(e, content) { var tt = document.createElement("div"); @@ -110,7 +109,7 @@ function annotationTooltip(ann) { var severity = ann.severity; - if (!SEVERITIES.test(severity)) severity = "error"; + if (!severity) severity = "error"; var tip = document.createElement("div"); tip.className = "CodeMirror-lint-message-" + severity; tip.appendChild(document.createTextNode(ann.message)); @@ -141,7 +140,7 @@ for (var i = 0; i < anns.length; ++i) { var ann = anns[i]; var severity = ann.severity; - if (!SEVERITIES.test(severity)) severity = "error"; + if (!severity) severity = "error"; maxSeverity = getMaxSeverity(maxSeverity, severity); if (options.formatAnnotation) ann = options.formatAnnotation(ann); From 13acf661b6976badca64e69861f054111757084b Mon Sep 17 00:00:00 2001 From: Marijn Haverbeke Date: Mon, 14 Jul 2014 11:07:30 +0200 Subject: [PATCH 2486/5780] Don't treat %= as the start of a string Closes #2692 --- mode/ruby/ruby.js | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/mode/ruby/ruby.js b/mode/ruby/ruby.js index d6c9d8796e..e7de7b57f1 100644 --- a/mode/ruby/ruby.js +++ b/mode/ruby/ruby.js @@ -69,7 +69,7 @@ CodeMirror.defineMode("ruby", function(config) { else if (stream.eat(/[WQ]/)) style = "string"; else if (stream.eat(/[r]/)) style = "string-2"; else if (stream.eat(/[wxq]/)) { style = "string"; embed = false; } - var delim = stream.eat(/[^\w\s]/); + var delim = stream.eat(/[^\w\s=]/); if (!delim) return "operator"; if (matching.propertyIsEnumerable(delim)) delim = matching[delim]; return chain(readQuoted(delim, style, embed, true), stream, state); From f4ae5b42c929edc78c2df0888ae1b0179098652a Mon Sep 17 00:00:00 2001 From: Marijn Haverbeke Date: Tue, 15 Jul 2014 11:13:36 +0200 Subject: [PATCH 2487/5780] Track last copied text, in order to find selection boundaries on paste Issue #2697 --- lib/codemirror.js | 25 +++++++++++++++++++------ 1 file changed, 19 insertions(+), 6 deletions(-) diff --git a/lib/codemirror.js b/lib/codemirror.js index f52f7865c6..fcaaab28bd 100644 --- a/lib/codemirror.js +++ b/lib/codemirror.js @@ -2366,6 +2366,11 @@ cm.display.poll.set(20, p); } + // This will be set to an array of strings when copying, so that, + // when pasting, we know what kind of selections the copied text + // was made out of. + var lastCopied = null; + // Read input from the textarea, and update the document to match. // When something is selected, it is present in the textarea, and // selected (unless it is huge, in which case a placeholder is @@ -2409,7 +2414,13 @@ var inserted = text.slice(same), textLines = splitLines(inserted); // When pasing N lines into N selections, insert one line per selection - var multiPaste = cm.state.pasteIncoming && textLines.length > 1 && doc.sel.ranges.length == textLines.length; + var multiPaste = null; + if (cm.state.pasteIncoming && doc.sel.ranges.length > 1) { + if (lastCopied && lastCopied.join("\n") == inserted) + multiPaste = lastCopied.length == doc.sel.ranges.length && map(lastCopied, splitLines); + else if (textLines.length == doc.sel.ranges.length) + multiPaste = map(textLines, function(l) { return [l]; }); + } // Normal behavior is to insert the new text into every selection for (var i = doc.sel.ranges.length - 1; i >= 0; i--) { @@ -2422,7 +2433,7 @@ else if (cm.state.overwrite && range.empty() && !cm.state.pasteIncoming) to = Pos(to.line, Math.min(getLine(doc, to.line).text.length, to.ch + lst(textLines).length)); var updateInput = cm.curOp.updateInput; - var changeEvent = {from: from, to: to, text: multiPaste ? [textLines[i]] : textLines, + var changeEvent = {from: from, to: to, text: multiPaste ? multiPaste[i] : textLines, origin: cm.state.pasteIncoming ? "paste" : cm.state.cutIncoming ? "cut" : "+input"}; makeChange(cm.doc, changeEvent); signalLater(cm, "inputRead", cm, changeEvent); @@ -2589,27 +2600,29 @@ function prepareCopyCut(e) { if (cm.somethingSelected()) { + lastCopied = cm.getSelections(); if (d.inaccurateSelection) { d.prevInput = ""; d.inaccurateSelection = false; - d.input.value = cm.getSelection(); + d.input.value = lastCopied.join("\n"); selectInput(d.input); } } else { - var text = "", ranges = []; + var text = [], ranges = []; for (var i = 0; i < cm.doc.sel.ranges.length; i++) { var line = cm.doc.sel.ranges[i].head.line; var lineRange = {anchor: Pos(line, 0), head: Pos(line + 1, 0)}; ranges.push(lineRange); - text += cm.getRange(lineRange.anchor, lineRange.head); + text.push(cm.getRange(lineRange.anchor, lineRange.head)); } if (e.type == "cut") { cm.setSelections(ranges, null, sel_dontScroll); } else { d.prevInput = ""; - d.input.value = text; + d.input.value = text.join("\n"); selectInput(d.input); } + lastCopied = text; } if (e.type == "cut") cm.state.cutIncoming = true; } From bc87689c02950857049470355f20e8a29eb19639 Mon Sep 17 00:00:00 2001 From: Marijn Haverbeke Date: Wed, 16 Jul 2014 10:04:41 +0200 Subject: [PATCH 2488/5780] Remove unused extra argument to computeSelAfterChange --- lib/codemirror.js | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/lib/codemirror.js b/lib/codemirror.js index fcaaab28bd..0aad3fda68 100644 --- a/lib/codemirror.js +++ b/lib/codemirror.js @@ -3549,7 +3549,7 @@ antiChanges.push(historyChangeFromChange(doc, change)); - var after = i ? computeSelAfterChange(doc, change, null) : lst(source); + var after = i ? computeSelAfterChange(doc, change) : lst(source); makeChangeSingleDoc(doc, change, after, mergeOldSpans(doc, change)); if (!i && doc.cm) doc.cm.scrollIntoView(change); var rebased = []; @@ -3608,7 +3608,7 @@ change.removed = getBetween(doc, change.from, change.to); - if (!selAfter) selAfter = computeSelAfterChange(doc, change, null); + if (!selAfter) selAfter = computeSelAfterChange(doc, change); if (doc.cm) makeChangeSingleDocInEditor(doc.cm, change, spans); else updateDoc(doc, change, spans); setSelectionNoUndo(doc, selAfter, sel_dontScroll); From 59138ec896618d730046fda7002d57eef02bd6a8 Mon Sep 17 00:00:00 2001 From: Marijn Haverbeke Date: Wed, 16 Jul 2014 12:55:59 +0200 Subject: [PATCH 2489/5780] [javascript mode] Indent properly in case of function arg in wrapped arg list --- mode/javascript/javascript.js | 2 ++ mode/javascript/test.js | 6 ++++++ 2 files changed, 8 insertions(+) diff --git a/mode/javascript/javascript.js b/mode/javascript/javascript.js index 315674be74..fdb066eb1f 100644 --- a/mode/javascript/javascript.js +++ b/mode/javascript/javascript.js @@ -298,6 +298,8 @@ CodeMirror.defineMode("javascript", function(config, parserConfig) { var result = function() { var state = cx.state, indent = state.indented; if (state.lexical.type == "stat") indent = state.lexical.indented; + else for (var outer = state.lexical; outer && outer.type == ")" && outer.align; outer = outer.prev) + indent = outer.indented; state.lexical = new JSLexical(indent, cx.stream.column(), type, null, state.lexical, info); }; result.lex = true; diff --git a/mode/javascript/test.js b/mode/javascript/test.js index a9cc993d3d..77cc695fef 100644 --- a/mode/javascript/test.js +++ b/mode/javascript/test.js @@ -128,6 +128,12 @@ " [keyword else]", " [number 3];"); + MT("indent_funarg", + "[variable foo]([number 10000],", + " [keyword function]([def a]) {", + " [keyword debugger];", + "};"); + MT("indent_below_if", "[keyword for] (;;)", " [keyword if] ([variable foo])", From 2fa988d3b46d47a732bdbf6d5ac1f52f49b6a6e0 Mon Sep 17 00:00:00 2001 From: Sander AKA Redsandro Date: Thu, 17 Jul 2014 16:44:50 +0200 Subject: [PATCH 2490/5780] Disable replace for readOnly content Disable the `replace()` when the selected `CodeMirror` is `readOnly`. --- addon/search/search.js | 1 + 1 file changed, 1 insertion(+) diff --git a/addon/search/search.js b/addon/search/search.js index 3ce7cc95d8..b177dce6ed 100644 --- a/addon/search/search.js +++ b/addon/search/search.js @@ -110,6 +110,7 @@ var replacementQueryDialog = 'With: '; var doReplaceConfirm = "Replace? "; function replace(cm, all) { + if (cm.getOption("readOnly")) return; dialog(cm, replaceQueryDialog, "Replace:", cm.getSelection(), function(query) { if (!query) return; query = parseQuery(query); From ddd36cb0b085358e797cd64ff08ed819005b9f60 Mon Sep 17 00:00:00 2001 From: binny Date: Sat, 5 Jul 2014 07:02:00 +0530 Subject: [PATCH 2491/5780] [vim] visual block replace --- keymap/vim.js | 33 ++++++++++++++++++++++----------- test/vim_test.js | 13 +++++++++++++ 2 files changed, 35 insertions(+), 11 deletions(-) diff --git a/keymap/vim.js b/keymap/vim.js index c1b0df4683..b239929f07 100644 --- a/keymap/vim.js +++ b/keymap/vim.js @@ -2370,10 +2370,11 @@ var curStart = cm.getCursor(); var replaceTo; var curEnd; - if (vim.visualMode){ - curStart=cm.getCursor('start'); - curEnd=cm.getCursor('end'); - }else{ + var selections = cm.listSelections(); + if (vim.visualMode) { + curStart = cm.getCursor('start'); + curEnd = cm.getCursor('end'); + } else { var line = cm.getLine(curStart.line); replaceTo = curStart.ch + actionArgs.repeat; if (replaceTo > line.length) { @@ -2381,19 +2382,29 @@ } curEnd = Pos(curStart.line, replaceTo); } - if (replaceWith=='\n'){ + if (replaceWith=='\n') { if (!vim.visualMode) cm.replaceRange('', curStart, curEnd); // special case, where vim help says to replace by just one line-break (CodeMirror.commands.newlineAndIndentContinueComment || CodeMirror.commands.newlineAndIndent)(cm); - }else { - var replaceWithStr=cm.getRange(curStart, curEnd); + } else { + var replaceWithStr = cm.getRange(curStart, curEnd); //replace all characters in range by selected, but keep linebreaks - replaceWithStr=replaceWithStr.replace(/[^\n]/g,replaceWith); - cm.replaceRange(replaceWithStr, curStart, curEnd); - if (vim.visualMode){ + replaceWithStr = replaceWithStr.replace(/[^\n]/g, replaceWith); + if (vim.visualBlock) { + // Tabs are split in visua block before replacing + var spaces = new Array(cm.options.tabSize+1).join(' '); + replaceWithStr = cm.getSelection(); + replaceWithStr = replaceWithStr.replace(/\t/g, spaces).replace(/[^\n]/g, replaceWith).split('\n'); + cm.replaceSelections(replaceWithStr); + } else { + cm.replaceRange(replaceWithStr, curStart, curEnd); + } + if (vim.visualMode) { + curStart = cursorIsBefore(selections[0].anchor, selections[0].head) ? + selections[0].anchor : selections[0].head; cm.setCursor(curStart); exitVisualMode(cm); - }else{ + } else { cm.setCursor(offsetCursor(curEnd, 0, -1)); } } diff --git a/test/vim_test.js b/test/vim_test.js index 76a2699780..342737de52 100644 --- a/test/vim_test.js +++ b/test/vim_test.js @@ -1332,6 +1332,19 @@ testVim('r', function(cm, vim, helpers) { helpers.doKeys('v', 'j', 'h', 'r', ''); eq('wuuu \n her', cm.getValue(),'Replacing selection by space-characters failed'); }, { value: 'wordet\nanother' }); +testVim('r_visual_block', function(cm, vim, helpers) { + cm.setCursor(2, 3); + helpers.doKeys('', 'k', 'k', 'h', 'h', 'r', 'l'); + eq('1lll\n5lll\nalllefg', cm.getValue()); + helpers.doKeys('', 'l', 'j', 'r', ''); + eq('1 l\n5 l\nalllefg', cm.getValue()); + cm.setCursor(2, 0); + helpers.doKeys('o'); + helpers.doInsertModeKeys('Esc'); + cm.replaceRange('\t\t', cm.getCursor()); + helpers.doKeys('', 'h', 'h', 'r', 'r'); + eq('1 l\n5 l\nalllefg\nrrrrrrrr', cm.getValue()); +}, {value: '1234\n5678\nabcdefg'}); testVim('R', function(cm, vim, helpers) { cm.setCursor(0, 1); helpers.doKeys('R'); From bce69920dfdce2b5eeb910bf7a7cff147a3bd5ca Mon Sep 17 00:00:00 2001 From: binny Date: Tue, 15 Jul 2014 05:32:25 +0530 Subject: [PATCH 2492/5780] [vim] changeCase for blockwise visual --- keymap/vim.js | 89 +++++++++++++++++++++++++++++++++++------------- test/vim_test.js | 16 +++++++-- 2 files changed, 79 insertions(+), 26 deletions(-) diff --git a/keymap/vim.js b/keymap/vim.js index b239929f07..e7bb885bf2 100644 --- a/keymap/vim.js +++ b/keymap/vim.js @@ -2348,6 +2348,9 @@ } } cm.setCursor(curPosFinal); + if (vim.visualMode) { + exitVisualMode(cm); + } }, undo: function(cm, actionArgs) { cm.operation(function() { @@ -2449,17 +2452,26 @@ repeatLastEdit(cm, vim, repeat, false /** repeatForInsert */); }, changeCase: function(cm, actionArgs, vim) { - var selectedAreaRange = getSelectedAreaRange(cm, vim); - var selectionStart = selectedAreaRange[0]; - var selectionEnd = selectedAreaRange[1]; + var selectionStart = getSelectedAreaRange(cm, vim)[0]; + var text = cm.getSelection(); + var lastSelectionCurEnd; + var blockSelection; + if (vim.lastSelection) { // save the curEnd marker to avoid its removal due to cm.replaceRange - var lastSelectionCurEnd = vim.lastSelection.curEndMark.find(); + lastSelectionCurEnd = vim.lastSelection.curEndMark.find(); + blockSelection = vim.lastSelection.visualBlock; + } var toLower = actionArgs.toLower; - var text = cm.getRange(selectionStart, selectionEnd); - cm.replaceRange(toLower ? text.toLowerCase() : text.toUpperCase(), selectionStart, selectionEnd); + text = toLower ? text.toLowerCase() : text.toUpperCase(); + cm.replaceSelections(vim.visualBlock || blockSelection ? text.split('\n') : [text]); // restore the last selection curEnd marker - vim.lastSelection.curEndMark = cm.setBookmark(lastSelectionCurEnd); + if (lastSelectionCurEnd) { + vim.lastSelection.curEndMark = cm.setBookmark(lastSelectionCurEnd); + } cm.setCursor(selectionStart); + if (vim.visualMode) { + exitVisualMode(cm); + } } }; @@ -2623,27 +2635,56 @@ return -1; } function getSelectedAreaRange(cm, vim) { - var selectionStart = cm.getCursor('anchor'); - var selectionEnd = cm.getCursor('head'); var lastSelection = vim.lastSelection; - if (!vim.visualMode) { - var lastSelectionCurStart = vim.lastSelection.curStartMark.find(); - var lastSelectionCurEnd = vim.lastSelection.curEndMark.find(); - var line = lastSelectionCurEnd.line - lastSelectionCurStart.line; - var ch = line ? lastSelectionCurEnd.ch : lastSelectionCurEnd.ch - lastSelectionCurStart.ch; - selectionEnd = {line: selectionEnd.line + line, ch: line ? selectionEnd.ch : ch + selectionEnd.ch}; - if (lastSelection.visualLine) { - return [{line: selectionStart.line, ch: 0}, {line: selectionEnd.line, ch: lineLength(cm, selectionEnd.line)}]; + var getCurrentSelectedAreaRange = function() { + var selections = cm.listSelections(); + var start = selections[0]; + var end = selections[selections.length-1]; + var selectionStart = cursorIsBefore(start.anchor, start.head) ? start.anchor : start.head; + var selectionEnd = cursorIsBefore(end.anchor, end.head) ? end.head : end.anchor; + return [selectionStart, selectionEnd]; + }; + var getLastSelectedAreaRange = function() { + var start = lastSelection.curStartMark.find(); + var end = lastSelection.curEndMark.find(); + var selectionStart = cm.getCursor(); + var selectionEnd = cm.getCursor(); + if (lastSelection.visualBlock) { + var anchor = Pos(Math.min(start.line, end.line), Math.min(start.ch, end.ch)); + var head = Pos(Math.max(start.line, end.line), Math.max(start.ch, end.ch)); + var width = head.ch - anchor.ch; + var height = head.line - anchor.line; + selectionEnd = Pos(selectionStart.line + height, selectionStart.ch + width); + var endCh = cm.clipPos(selectionEnd).ch; + // We do not want selection crossing while selecting here. + // So, we cut down the selection. + while (endCh != selectionEnd.ch) { + if (endCh-1 == selectionStart.ch) { + break; + } + selectionEnd.line--; + endCh = cm.clipPos(selectionEnd).ch; + } + cm.setCursor(selectionStart); + selectBlock(cm, selectionEnd); + } else { + var line = end.line - start.line; + var ch = end.ch - start.ch; + selectionEnd = {line: selectionEnd.line + line, ch: line ? selectionEnd.ch : ch + selectionEnd.ch}; + if (lastSelection.visualLine) { + selectionStart = Pos(selectionStart.line, 0); + selectionEnd = Pos(selectionEnd.line, lineLength(cm, selectionEnd.line)); + } + cm.setSelection(selectionStart, selectionEnd); } + return [selectionStart, selectionEnd]; + }; + if (!vim.visualMode) { + // In case of replaying the action. + return getLastSelectedAreaRange(); } else { - if (cursorIsBefore(selectionEnd, selectionStart)) { - var tmp = selectionStart; - selectionStart = selectionEnd; - selectionEnd = tmp; - } - exitVisualMode(cm); + return getCurrentSelectedAreaRange(); } - return [selectionStart, selectionEnd]; } function updateLastSelection(cm, vim, selectionStart, selectionEnd) { if (!selectionStart || !selectionEnd) { diff --git a/test/vim_test.js b/test/vim_test.js index 342737de52..2fb1843169 100644 --- a/test/vim_test.js +++ b/test/vim_test.js @@ -1683,7 +1683,7 @@ testVim('reselect_visual', function(cm, vim, helpers) { cm.setCursor(0, 0); helpers.doKeys('g', 'v'); // here the fake cursor is at (1, 3) - helpers.assertCursorAt(2, 0); + helpers.assertCursorAt(1, 4); eqPos(makeCursor(1, 0), cm.getCursor('anchor')); helpers.doKeys('v'); cm.setCursor(2, 0); @@ -1753,7 +1753,7 @@ testVim('o_visual_block', function(cm, vim, helpers) { helpers.doKeys('o'); helpers.assertCursorAt(3, 1); }, { value: 'abcd\nefgh\nijkl\nmnop'}); -testVim('uppercase/lowercase_visual', function(cm, vim, helpers) { +testVim('changeCase_visual', function(cm, vim, helpers) { cm.setCursor(0, 0); helpers.doKeys('v', 'l', 'l'); helpers.doKeys('U'); @@ -1772,6 +1772,18 @@ testVim('uppercase/lowercase_visual', function(cm, vim, helpers) { helpers.doKeys('V', 'U', 'j', '.'); eq('ABCDEF\nGHIJKL\nMnopq\nSHORT LINE\nLONG LINE OF TEXT', cm.getValue()); }, { value: 'abcdef\nghijkl\nmnopq\nshort line\nlong line of text'}); +testVim('changeCase_visual_block', function(cm, vim, helpers) { + cm.setCursor(2, 1); + helpers.doKeys('', 'k', 'k', 'h', 'U'); + eq('ABcdef\nGHijkl\nMNopq\nfoo', cm.getValue()); + cm.setCursor(0, 2); + helpers.doKeys('.'); + eq('ABCDef\nGHIJkl\nMNOPq\nfoo', cm.getValue()); + // check when last line is shorter. + cm.setCursor(2, 2); + helpers.doKeys('.'); + eq('ABCDef\nGHIJkl\nMNOPq\nfoO', cm.getValue()); +}, { value: 'abcdef\nghijkl\nmnopq\nfoo'}); testVim('visual_paste', function(cm, vim, helpers) { cm.setCursor(0, 0); helpers.doKeys('v', 'l', 'l', 'y', 'j', 'v', 'l', 'p'); From 9312c712a80d665e569e29ea8448e6bb52c03b69 Mon Sep 17 00:00:00 2001 From: binny Date: Fri, 18 Jul 2014 05:30:15 +0530 Subject: [PATCH 2493/5780] [vim] using dot to replay actions and operators --- keymap/vim.js | 111 ++++++++++++++++++++++++++++++++--------------- test/vim_test.js | 17 +++++++- 2 files changed, 92 insertions(+), 36 deletions(-) diff --git a/keymap/vim.js b/keymap/vim.js index e7bb885bf2..16f51b3bf1 100644 --- a/keymap/vim.js +++ b/keymap/vim.js @@ -1411,6 +1411,7 @@ if (operator) { var inverted = false; vim.lastMotion = null; + var lastSelection = vim.lastSelection; operatorArgs.repeat = repeat; // Indent in visual mode needs this. if (vim.visualMode) { curStart = selectionStart; @@ -1437,6 +1438,24 @@ curEnd.line = curStart.line + operatorArgs.selOffset.line; if (operatorArgs.selOffset.line) {curEnd.ch = operatorArgs.selOffset.ch; } else { curEnd.ch = curStart.ch + operatorArgs.selOffset.ch; } + // In case of blockwise visual + if (lastSelection && lastSelection.visualBlock) { + var block = lastSelection.visualBlock; + var width = block.width; + var height = block.height; + curEnd = Pos(curStart.line + height, curStart.ch + width); + // selectBlock creates a 'proper' rectangular block. + // We do not want that in all cases, so we manually set selections. + var selections = []; + for (var i = curStart.line; i < curEnd.line; i++) { + var anchor = Pos(i, curStart.ch); + var head = Pos(i, curEnd.ch); + var range = {anchor: anchor, head: head}; + selections.push(range); + } + cm.setSelections(selections); + var blockSelected = true; + } } else if (vim.visualMode) { var selOffset = Pos(); selOffset.line = curEnd.line - curStart.line; @@ -1457,8 +1476,8 @@ operatorArgs.registerName = registerName; // Keep track of linewise as it affects how paste and change behave. operatorArgs.linewise = linewise; - if (!vim.visualBlock) { - cm.extendSelection(curStart, curEnd); + if (!vim.visualBlock && !blockSelected) { + cm.setSelection(curStart, curEnd); } operators[operator](cm, operatorArgs, vim, curStart, curEnd, curOriginal); @@ -1838,6 +1857,8 @@ var curEnd = cursorIsBefore(end.anchor, end.head) ? end.head : end.anchor; var text = cm.getSelection(); var replacement = new Array(selections.length).join('1').split('1'); + // save the selectionEnd mark + var selectionEnd = vim.marks['>'] ? vim.marks['>'].find() : cm.getCursor('head'); vimGlobalState.registerController.pushText( operatorArgs.registerName, 'change', text, operatorArgs.linewise); @@ -1880,34 +1901,52 @@ cm.replaceRange('', curStart, curEnd); } } + vim.marks['>'] = cm.setBookmark(selectionEnd); actions.enterInsertMode(cm, {}, cm.state.vim); }, // delete is a javascript keyword. - 'delete': function(cm, operatorArgs, vim, curStart, curEnd) { + 'delete': function(cm, operatorArgs, vim) { + var selections = cm.listSelections(); + var start = selections[0], end = selections[selections.length-1]; + var curStart = cursorIsBefore(start.anchor, start.head) ? start.anchor : start.head; + var curEnd = cursorIsBefore(end.anchor, end.head) ? end.head : end.anchor; // Save the '>' mark before cm.replaceRange clears it. - var selectionEnd = vim.visualMode ? vim.marks['>'].find() : null; + var selectionEnd, selectionStart; + if (vim.visualMode) { + selectionEnd = vim.marks['>'].find(); + selectionStart = vim.marks['<'].find(); + } else if (vim.lastSelection) { + selectionEnd = vim.lastSelection.curStartMark.find(); + selectionStart = vim.lastSelection.curEndMark.find(); + } var text = cm.getSelection(); + vimGlobalState.registerController.pushText( + operatorArgs.registerName, 'delete', text, + operatorArgs.linewise); + var replacement = new Array(selections.length).join('1').split('1'); // If the ending line is past the last line, inclusive, instead of // including the trailing \n, include the \n before the starting line if (operatorArgs.linewise && - curEnd.line > cm.lastLine() && curStart.line > cm.firstLine()) { + curEnd.line == cm.lastLine() && curStart.line == curEnd.line) { + var tmp = copyCursor(curEnd); curStart.line--; curStart.ch = lineLength(cm, curStart.line); - } - vimGlobalState.registerController.pushText( - operatorArgs.registerName, 'delete', text, - operatorArgs.linewise); - if (vim.visualBlock) { - var selections = cm.listSelections(); - curStart = selections[0].anchor; - var replacement = new Array(selections.length).join('1').split('1'); - cm.replaceSelections(replacement); - } else { + curEnd = tmp; cm.replaceRange('', curStart, curEnd); + } else { + cm.replaceSelections(replacement); } // restore the saved bookmark if (selectionEnd) { - vim.marks['>'] = cm.setBookmark(selectionEnd); + var curStartMark = cm.setBookmark(selectionStart); + var curEndMark = cm.setBookmark(selectionEnd); + if (vim.visualMode) { + vim.marks['<'] = curStartMark; + vim.marks['>'] = curEndMark; + } else { + vim.lastSelection.curStartMark = curStartMark; + vim.lastSelection.curEndMark = curEndMark; + } } if (operatorArgs.linewise) { cm.setCursor(motions.moveToFirstNonWhiteSpaceCharacter(cm)); @@ -2645,29 +2684,26 @@ return [selectionStart, selectionEnd]; }; var getLastSelectedAreaRange = function() { - var start = lastSelection.curStartMark.find(); - var end = lastSelection.curEndMark.find(); var selectionStart = cm.getCursor(); var selectionEnd = cm.getCursor(); - if (lastSelection.visualBlock) { - var anchor = Pos(Math.min(start.line, end.line), Math.min(start.ch, end.ch)); - var head = Pos(Math.max(start.line, end.line), Math.max(start.ch, end.ch)); - var width = head.ch - anchor.ch; - var height = head.line - anchor.line; + var block = lastSelection.visualBlock; + if (block) { + var width = block.width; + var height = block.height; selectionEnd = Pos(selectionStart.line + height, selectionStart.ch + width); - var endCh = cm.clipPos(selectionEnd).ch; - // We do not want selection crossing while selecting here. - // So, we cut down the selection. - while (endCh != selectionEnd.ch) { - if (endCh-1 == selectionStart.ch) { - break; - } - selectionEnd.line--; - endCh = cm.clipPos(selectionEnd).ch; + var selections = []; + // selectBlock creates a 'proper' rectangular block. + // We do not want that in all cases, so we manually set selections. + for (var i = selectionStart.line; i < selectionEnd.line; i++) { + var anchor = Pos(i, selectionStart.ch); + var head = Pos(i, selectionEnd.ch); + var range = {anchor: anchor, head: head}; + selections.push(range); } - cm.setCursor(selectionStart); - selectBlock(cm, selectionEnd); + cm.setSelections(selections); } else { + var start = lastSelection.curStartMark.find(); + var end = lastSelection.curEndMark.find(); var line = end.line - start.line; var ch = end.ch - start.ch; selectionEnd = {line: selectionEnd.line + line, ch: line ? selectionEnd.ch : ch + selectionEnd.ch}; @@ -2700,13 +2736,18 @@ // This check ensures to set the cursor // position where we left off in previous selection var swap = getIndex(ranges, selectionStart) > -1; + if (vim.visualBlock) { + var height = Math.abs(selectionStart.line - selectionEnd.line)+1; + var width = Math.abs(selectionStart.ch - selectionEnd.ch); + var block = {height: height, width: width}; + } // can't use selection state here because yank has already reset its cursor // Also, Bookmarks make the visual selections robust to edit operations vim.lastSelection = {'curStartMark': cm.setBookmark(swap ? selectionEnd : selectionStart), 'curEndMark': cm.setBookmark(swap ? selectionStart : selectionEnd), 'visualMode': vim.visualMode, 'visualLine': vim.visualLine, - 'visualBlock': vim.visualBlock}; + 'visualBlock': block}; } function exitVisualMode(cm) { diff --git a/test/vim_test.js b/test/vim_test.js index 2fb1843169..7c24634a89 100644 --- a/test/vim_test.js +++ b/test/vim_test.js @@ -944,7 +944,22 @@ testVim('visual_block_~', function(cm, vim, helpers) { helpers.assertCursorAt(2, 0); eq('hello\nwoRLd\nAbcDe', cm.getValue()); },{value: 'hello\nwOrld\nabcde' }); - +testVim('._swapCase_visualBlock', function(cm, vim, helpers) { + helpers.doKeys('', 'j', 'j', 'l', '~'); + cm.setCursor(0, 3); + helpers.doKeys('.'); + eq('HelLO\nWorLd\nAbcdE', cm.getValue()); +},{value: 'hEllo\nwOrlD\naBcDe' }); +testVim('._delete_visualBlock', function(cm, vim, helpers) { + helpers.doKeys('', 'j', 'x'); + eq('ive\ne\nsome\nsugar', cm.getValue()); + helpers.doKeys('.'); + eq('ve\n\nsome\nsugar', cm.getValue()); + helpers.doKeys('j', 'j', '.'); + eq('ve\n\nome\nugar', cm.getValue()); + helpers.doKeys('u', '', '.'); + eq('ve\n\nme\ngar', cm.getValue()); +},{value: 'give\nme\nsome\nsugar' }); testVim('>{motion}', function(cm, vim, helpers) { cm.setCursor(1, 3); var expectedLineCount = cm.lineCount(); From 252a9d6cb8afd782762bf10c75760cbbd75a5c43 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Guillaume=20Mass=C3=A9?= Date: Sat, 12 Jul 2014 20:20:08 -0400 Subject: [PATCH 2494/5780] [clike mode] Enable multilineString for scala It is a good approximation --- mode/clike/clike.js | 1 + 1 file changed, 1 insertion(+) diff --git a/mode/clike/clike.js b/mode/clike/clike.js index 3e253624b4..2873e3629b 100644 --- a/mode/clike/clike.js +++ b/mode/clike/clike.js @@ -369,6 +369,7 @@ CodeMirror.defineMode("clike", function(config, parserConfig) { ), + multiLineStrings: true, blockKeywords: words("catch class do else finally for forSome if match switch try while"), atoms: words("true false null"), hooks: { From 58d2a8a8b28e6f45ca98729952870de92184cf05 Mon Sep 17 00:00:00 2001 From: Marijn Haverbeke Date: Sun, 20 Jul 2014 09:34:51 +0200 Subject: [PATCH 2495/5780] [yaml mode] Be less restrictive about keys in front of colons Issue #2695 --- mode/yaml/yaml.js | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/mode/yaml/yaml.js b/mode/yaml/yaml.js index 4ebe5c81cf..15a5916df1 100644 --- a/mode/yaml/yaml.js +++ b/mode/yaml/yaml.js @@ -80,7 +80,7 @@ CodeMirror.defineMode("yaml", function() { } /* pairs (associative arrays) -> key */ - if (!state.pair && stream.match(/^\s*\S+(?=\s*:($|\s))/i)) { + if (!state.pair && stream.match(/^\s*[^\-:{}"\[\]][^:{}"\[\]]*(?=\s*:($|\s))/i)) { state.pair = true; state.keyCol = stream.indentation(); return "atom"; From dff9738301bedfc57e0a3f3a2168edb05fd5dcf9 Mon Sep 17 00:00:00 2001 From: Marijn Haverbeke Date: Sun, 20 Jul 2014 09:41:56 +0200 Subject: [PATCH 2496/5780] [puppet mode] Make regexp for regexps non-greedy Issue #2696 --- mode/puppet/puppet.js | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/mode/puppet/puppet.js b/mode/puppet/puppet.js index 66698bb6ad..b407ded883 100644 --- a/mode/puppet/puppet.js +++ b/mode/puppet/puppet.js @@ -176,7 +176,7 @@ CodeMirror.defineMode("puppet", function () { // Match characters that we are going to assume // are trying to be regex if (ch == '/') { - stream.match(/.*\//); + stream.match(/.*?\//); return 'variable-3'; } // Match all the numbers From 4e7e863c0e34f9fc450351d408a3f585d3843549 Mon Sep 17 00:00:00 2001 From: Marijn Haverbeke Date: Sun, 20 Jul 2014 10:26:22 +0200 Subject: [PATCH 2497/5780] Remove a few unneccesary property accesses --- lib/codemirror.js | 6 +++--- 1 file changed, 3 insertions(+), 3 deletions(-) diff --git a/lib/codemirror.js b/lib/codemirror.js index 0aad3fda68..8b3feac507 100644 --- a/lib/codemirror.js +++ b/lib/codemirror.js @@ -3815,7 +3815,7 @@ if (how == "smart") { // Fall back to "prev" when the mode doesn't have an indentation // method. - if (!cm.doc.mode.indent) how = "prev"; + if (!doc.mode.indent) how = "prev"; else state = getStateBefore(cm, n); } @@ -3827,7 +3827,7 @@ indentation = 0; how = "not"; } else if (how == "smart") { - indentation = cm.doc.mode.indent(state, line.text.slice(curSpaceString.length), line.text); + indentation = doc.mode.indent(state, line.text.slice(curSpaceString.length), line.text); if (indentation == Pass || indentation > 150) { if (!aggressive) return; how = "prev"; @@ -3851,7 +3851,7 @@ if (pos < indentation) indentString += spaceStr(indentation - pos); if (indentString != curSpaceString) { - replaceRange(cm.doc, indentString, Pos(n, 0), Pos(n, curSpaceString.length), "+input"); + replaceRange(doc, indentString, Pos(n, 0), Pos(n, curSpaceString.length), "+input"); } else { // Ensure that, if the cursor was in the whitespace at the start // of the line, it is moved to the end of that space. From 1ddba34bb6e467282eeda6a79bd16c9650668169 Mon Sep 17 00:00:00 2001 From: Marijn Haverbeke Date: Sun, 20 Jul 2014 10:56:24 +0200 Subject: [PATCH 2498/5780] Force stable y scroll when focusing textarea in onContextMenu Closes #2712 --- lib/codemirror.js | 2 ++ 1 file changed, 2 insertions(+) diff --git a/lib/codemirror.js b/lib/codemirror.js index 8b3feac507..09ea0a3134 100644 --- a/lib/codemirror.js +++ b/lib/codemirror.js @@ -3319,7 +3319,9 @@ "px; left: " + (e.clientX - 5) + "px; z-index: 1000; background: " + (ie ? "rgba(255, 255, 255, .05)" : "transparent") + "; outline: none; border-width: 0; outline: none; overflow: hidden; opacity: .05; filter: alpha(opacity=5);"; + if (webkit) var oldScrollY = window.scrollY; // Work around Chrome issue (#2712) focusInput(cm); + if (webkit) window.scrollTo(null, oldScrollY); resetInput(cm); // Adds "Select all" to context menu in FF if (!cm.somethingSelected()) display.input.value = display.prevInput = " "; From 1d35536ee0f23be031beba12309a38db9f49b4d5 Mon Sep 17 00:00:00 2001 From: Yunchi Luo Date: Sun, 20 Jul 2014 14:01:11 -0700 Subject: [PATCH 2499/5780] [vim] Do not open prompt if no cm.openDialog --- keymap/vim.js | 10 +++++++--- 1 file changed, 7 insertions(+), 3 deletions(-) diff --git a/keymap/vim.js b/keymap/vim.js index 16f51b3bf1..a47b005d02 100644 --- a/keymap/vim.js +++ b/keymap/vim.js @@ -561,7 +561,9 @@ MacroModeState.prototype = { exitMacroRecordMode: function() { var macroModeState = vimGlobalState.macroModeState; - macroModeState.onRecordingDone(); // close dialog + if (macroModeState.onRecordingDone) { + macroModeState.onRecordingDone(); // close dialog + } macroModeState.onRecordingDone = undefined; macroModeState.isRecording = false; }, @@ -571,8 +573,10 @@ if (register) { register.clear(); this.latestRegister = registerName; - this.onRecordingDone = cm.openDialog( - '(recording)['+registerName+']', null, {bottom:true}); + if (cm.openDialog) { + this.onRecordingDone = cm.openDialog( + '(recording)['+registerName+']', null, {bottom:true}); + } this.isRecording = true; } } From 1d4b525f8127e79aaee811ff2b6972a3fa76a7bd Mon Sep 17 00:00:00 2001 From: Marijn Haverbeke Date: Mon, 21 Jul 2014 08:28:07 +0200 Subject: [PATCH 2500/5780] [yaml mode] Tweak key regexp Issue #2695 --- mode/yaml/yaml.js | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/mode/yaml/yaml.js b/mode/yaml/yaml.js index 15a5916df1..332aef6a23 100644 --- a/mode/yaml/yaml.js +++ b/mode/yaml/yaml.js @@ -80,7 +80,7 @@ CodeMirror.defineMode("yaml", function() { } /* pairs (associative arrays) -> key */ - if (!state.pair && stream.match(/^\s*[^\-:{}"\[\]][^:{}"\[\]]*(?=\s*:($|\s))/i)) { + if (!state.pair && stream.match(/^\s*(?:[,\[\]{}&*!|>'"%@`][^\s'":]|[^,\[\]{}#&*!|>'"%@`])[^#]*?(?=\s*:($|\s))/)) { state.pair = true; state.keyCol = stream.indentation(); return "atom"; From e02b946bfdf68ac4c3933b99e119a8fa9898efb6 Mon Sep 17 00:00:00 2001 From: Marijn Haverbeke Date: Mon, 21 Jul 2014 08:33:42 +0200 Subject: [PATCH 2501/5780] Also split pasted content by selection when selection length is a multiple of clipboard length Issue #2697 --- lib/codemirror.js | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/lib/codemirror.js b/lib/codemirror.js index 09ea0a3134..31c6d71be5 100644 --- a/lib/codemirror.js +++ b/lib/codemirror.js @@ -2417,7 +2417,7 @@ var multiPaste = null; if (cm.state.pasteIncoming && doc.sel.ranges.length > 1) { if (lastCopied && lastCopied.join("\n") == inserted) - multiPaste = lastCopied.length == doc.sel.ranges.length && map(lastCopied, splitLines); + multiPaste = doc.sel.ranges.length % lastCopied.length == 0 && map(lastCopied, splitLines); else if (textLines.length == doc.sel.ranges.length) multiPaste = map(textLines, function(l) { return [l]; }); } @@ -2433,7 +2433,7 @@ else if (cm.state.overwrite && range.empty() && !cm.state.pasteIncoming) to = Pos(to.line, Math.min(getLine(doc, to.line).text.length, to.ch + lst(textLines).length)); var updateInput = cm.curOp.updateInput; - var changeEvent = {from: from, to: to, text: multiPaste ? multiPaste[i] : textLines, + var changeEvent = {from: from, to: to, text: multiPaste ? multiPaste[i % multiPaste.length] : textLines, origin: cm.state.pasteIncoming ? "paste" : cm.state.cutIncoming ? "cut" : "+input"}; makeChange(cm.doc, changeEvent); signalLater(cm, "inputRead", cm, changeEvent); From ee088bc36b3fca2771835cc42febcabeb19e791c Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Martin=20Haso=C5=88?= Date: Mon, 14 Jul 2014 08:32:17 +0200 Subject: [PATCH 2502/5780] [bower.json] Normalized a package name The package name on http://bower.io/search/ is lowercase. --- bower.json | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/bower.json b/bower.json index b103179156..407b86d649 100644 --- a/bower.json +++ b/bower.json @@ -1,5 +1,5 @@ { - "name": "CodeMirror", + "name": "codemirror", "version":"4.3.1", "main": ["lib/codemirror.js", "lib/codemirror.css"], "ignore": [ From 485a7da897e378025268e686f4eb79377973219e Mon Sep 17 00:00:00 2001 From: Marijn Haverbeke Date: Mon, 21 Jul 2014 08:51:28 +0200 Subject: [PATCH 2503/5780] Mark release 4.4 --- AUTHORS | 9 +++++++++ bower.json | 2 +- doc/compress.html | 1 + doc/manual.html | 2 +- doc/releases.html | 14 ++++++++++++++ index.html | 2 +- lib/codemirror.js | 2 +- package.json | 2 +- 8 files changed, 29 insertions(+), 5 deletions(-) diff --git a/AUTHORS b/AUTHORS index f5f569721e..0c2f67c31a 100644 --- a/AUTHORS +++ b/AUTHORS @@ -18,6 +18,7 @@ Alberto Pose Albert Xing Alexander Pavlov Alexander Schepanovski +Alexander Shvets Alexander Solovyov Alexandre Bique alexey-k @@ -168,6 +169,7 @@ Jason Grout Jason Johnston Jason San Jose Jason Siefken +Jaydeep Solanki Jean Boussier jeffkenton Jeff Pickhardt @@ -205,6 +207,7 @@ kubelsmieci Lanny Laszlo Vidacs leaf corcoran +Leonid Khachaturov Leonya Khachaturov Liam Newman LM @@ -269,6 +272,7 @@ Niels van Groningen Nikita Beloglazov Nikita Vasilyev Nikolay Kostov +nilp0inter nlwillia pablo Page @@ -291,7 +295,9 @@ Radek Piórkowski Rahul Randy Edmunds Rasmus Erik Voel Jensen +Richard van der Meer Richard Z.H. Wang +Roberto Abdelkader Martínez Pérez robertop23 Robert Plummer Ruslan Osmanov @@ -299,6 +305,7 @@ Ryan Prior sabaca Samuel Ainsworth sandeepshetty +Sander AKA Redsandro santec Sascha Peilicke satchmorun @@ -330,6 +337,7 @@ Thaddee Tyl think Thomas Dvornik Thomas Schmid +Tim Alby Tim Baumann Timothy Farrell Timothy Hatcher @@ -349,6 +357,7 @@ Volker Mische wenli Wesley Wiser William Jamieson +William Stein Wojtek Ptak Xavier Mendez YNH Webdev diff --git a/bower.json b/bower.json index 407b86d649..8c57fcdd4b 100644 --- a/bower.json +++ b/bower.json @@ -1,6 +1,6 @@ { "name": "codemirror", - "version":"4.3.1", + "version":"4.4.0", "main": ["lib/codemirror.js", "lib/codemirror.css"], "ignore": [ "**/.*", diff --git a/doc/compress.html b/doc/compress.html index ede45e1c6e..859210c45c 100644 --- a/doc/compress.html +++ b/doc/compress.html @@ -36,6 +36,7 @@

    Script compression helper

    Version: + + +

    MIME types defined: application/x-slim.

    + +

    + Parsing/Highlighting Tests: + normal, + verbose. +

    + diff --git a/mode/slim/slim.js b/mode/slim/slim.js new file mode 100644 index 0000000000..5e737131aa --- /dev/null +++ b/mode/slim/slim.js @@ -0,0 +1,575 @@ +// CodeMirror, copyright (c) by Marijn Haverbeke and others +// Distributed under an MIT license: http://codemirror.net/LICENSE + +// Slim Highlighting for CodeMirror copyright (c) HicknHack Software Gmbh + +(function(mod) { + if (typeof exports == "object" && typeof module == "object") // CommonJS + mod(require("../../lib/codemirror"), require("../htmlmixed/htmlmixed"), require("../ruby/ruby")); + else if (typeof define == "function" && define.amd) // AMD + define(["../../lib/codemirror", "../htmlmixed/htmlmixed", "../ruby/ruby"], mod); + else // Plain browser env + mod(CodeMirror); +})(function(CodeMirror) { +"use strict"; + + CodeMirror.defineMode("slim", function(config) { + var htmlMode = CodeMirror.getMode(config, {name: "htmlmixed"}); + var rubyMode = CodeMirror.getMode(config, "ruby"); + var modes = { html: htmlMode, ruby: rubyMode }; + var embedded = { + ruby: "ruby", + javascript: "javascript", + css: "text/css", + sass: "text/x-sass", + scss: "text/x-scss", + less: "text/x-less", + styl: "text/x-styl", // no highlighting so far + coffee: "coffeescript", + asciidoc: "text/x-asciidoc", + markdown: "text/x-markdown", + textile: "text/x-textile", // no highlighting so far + creole: "text/x-creole", // no highlighting so far + wiki: "text/x-wiki", // no highlighting so far + mediawiki: "text/x-mediawiki", // no highlighting so far + rdoc: "text/x-rdoc", // no highlighting so far + builder: "text/x-builder", // no highlighting so far + nokogiri: "text/x-nokogiri", // no highlighting so far + erb: "application/x-erb" + }; + var embeddedRegexp = function(map){ + var arr = []; + for(var key in map) arr.push(key); + return new RegExp("^("+arr.join('|')+"):"); + }(embedded); + + var styleMap = { + "commentLine": "comment", + "slimSwitch": "operator special", + "slimTag": "tag", + "slimId": "attribute def", + "slimClass": "attribute qualifier", + "slimAttribute": "attribute", + "slimSubmode": "keyword special", + "closeAttributeTag": null, + "slimDoctype": null, + "lineContinuation": null + }; + var closing = { + "{": "}", + "[": "]", + "(": ")" + }; + + var nameStartChar = "_a-zA-Z\xC0-\xD6\xD8-\xF6\xF8-\u02FF\u0370-\u037D\u037F-\u1FFF\u200C-\u200D\u2070-\u218F\u2C00-\u2FEF\u3001-\uD7FF\uF900-\uFDCF\uFDF0-\uFFFD"; + var nameChar = nameStartChar + "\\-0-9\xB7\u0300-\u036F\u203F-\u2040"; + var nameRegexp = new RegExp("^[:"+nameStartChar+"](?::["+nameChar+"]|["+nameChar+"]*)"); + var attributeNameRegexp = new RegExp("^[:"+nameStartChar+"][:\\."+nameChar+"]*(?=\\s*=)"); + var wrappedAttributeNameRegexp = new RegExp("^[:"+nameStartChar+"][:\\."+nameChar+"]*"); + var classNameRegexp = /^\.-?[_a-zA-Z]+[\w\-]*/; + var classIdRegexp = /^#[_a-zA-Z]+[\w\-]*/; + + function backup(pos, tokenize, style) { + var restore = function(stream, state) { + state.tokenize = tokenize; + if (stream.pos < pos) { + stream.pos = pos; + return style; + } + return state.tokenize(stream, state); + }; + return function(stream, state) { + state.tokenize = restore; + return tokenize(stream, state); + }; + } + + function maybeBackup(stream, state, pat, offset, style) { + var cur = stream.current(); + var idx = cur.search(pat); + if (idx > -1) { + state.tokenize = backup(stream.pos, state.tokenize, style); + stream.backUp(cur.length - idx - offset); + } + return style; + } + + function continueLine(state, column) { + state.stack = { + parent: state.stack, + style: "continuation", + indented: column, + tokenize: state.line + }; + state.line = state.tokenize; + } + function finishContinue(state) { + if (state.line == state.tokenize) { + state.line = state.stack.tokenize; + state.stack = state.stack.parent; + } + } + + function lineContinuable(column, tokenize) { + return function(stream, state) { + finishContinue(state); + if (stream.match(/^\\$/)) { + continueLine(state, column); + return "lineContinuation"; + } + var style = tokenize(stream, state); + if (stream.eol() && stream.current().match(/(?:^|[^\\])(?:\\\\)*\\$/)) { + stream.backUp(1); + } + return style; + }; + } + function commaContinuable(column, tokenize) { + return function(stream, state) { + finishContinue(state); + var style = tokenize(stream, state); + if (stream.eol() && stream.current().match(/,$/)) { + continueLine(state, column); + } + return style; + }; + } + + function rubyInQuote(endQuote, tokenize) { + // TODO: add multi line support + return function(stream, state) { + var ch = stream.peek(); + if (ch == endQuote && state.rubyState.tokenize.length == 1) { + // step out of ruby context as it seems to complete processing all the braces + stream.next(); + state.tokenize = tokenize; + return "closeAttributeTag"; + } else { + return ruby(stream, state); + } + }; + } + function startRubySplat(tokenize) { + var rubyState; + var runSplat = function(stream, state) { + if (state.rubyState.tokenize.length == 1 && !state.rubyState.context.prev) { + stream.backUp(1); + if (stream.eatSpace()) { + state.rubyState = rubyState; + state.tokenize = tokenize; + return tokenize(stream, state); + } + stream.next(); + } + return ruby(stream, state); + }; + return function(stream, state) { + rubyState = state.rubyState; + state.rubyState = rubyMode.startState(); + state.tokenize = runSplat; + return ruby(stream, state); + }; + } + + function ruby(stream, state) { + return rubyMode.token(stream, state.rubyState); + } + + function htmlLine(stream, state) { + if (stream.match(/^\\$/)) { + return "lineContinuation"; + } + return html(stream, state); + } + function html(stream, state) { + if (stream.match(/^#\{/)) { + state.tokenize = rubyInQuote("}", state.tokenize); + return null; + } + return maybeBackup(stream, state, /[^\\]#\{/, 1, htmlMode.token(stream, state.htmlState)); + } + + function startHtmlLine(lastTokenize) { + return function(stream, state) { + var style = htmlLine(stream, state); + if (stream.eol()) state.tokenize = lastTokenize; + return style; + }; + } + + function startHtmlMode(stream, state, offset) { + state.stack = { + parent: state.stack, + style: "html", + indented: stream.column() + offset, // pipe + space + tokenize: state.line + }; + state.line = state.tokenize = html; + return null; + } + + function comment(stream, state) { + stream.skipToEnd(); + return state.stack.style; + } + + function commentMode(stream, state) { + state.stack = { + parent: state.stack, + style: "comment", + indented: state.indented + 1, + tokenize: state.line + }; + state.line = comment; + return comment(stream, state); + } + + function attributeWrapper(stream, state) { + if (stream.eat(state.stack.endQuote)) { + state.line = state.stack.line; + state.tokenize = state.stack.tokenize; + state.stack = state.stack.parent; + return null; + } + if (stream.match(wrappedAttributeNameRegexp)) { + state.tokenize = attributeWrapperAssign; + return "slimAttribute"; + } + stream.next(); + return null; + } + function attributeWrapperAssign(stream, state) { + if (stream.match(/^==?/)) { + state.tokenize = attributeWrapperValue; + return null; + } + return attributeWrapper(stream, state); + } + function attributeWrapperValue(stream, state) { + var ch = stream.peek(); + if (ch == '"' || ch == "\'") { + state.tokenize = readQuoted(ch, "string", true, false, attributeWrapper); + stream.next(); + return state.tokenize(stream, state); + } + if (ch == '[') { + return startRubySplat(attributeWrapper)(stream, state); + } + if (stream.match(/^(true|false|nil)\b/)) { + state.tokenize = attributeWrapper; + return "keyword"; + } + return startRubySplat(attributeWrapper)(stream, state); + } + + function startAttributeWrapperMode(state, endQuote, tokenize) { + state.stack = { + parent: state.stack, + style: "wrapper", + indented: state.indented + 1, + tokenize: tokenize, + line: state.line, + endQuote: endQuote + }; + state.line = state.tokenize = attributeWrapper; + return null; + } + + function sub(stream, state) { + if (stream.match(/^#\{/)) { + state.tokenize = rubyInQuote("}", state.tokenize); + return null; + } + var subStream = new CodeMirror.StringStream(stream.string.slice(state.stack.indented), stream.tabSize); + subStream.pos = stream.pos - state.stack.indented; + subStream.start = stream.start - state.stack.indented; + subStream.lastColumnPos = stream.lastColumnPos - state.stack.indented; + subStream.lastColumnValue = stream.lastColumnValue - state.stack.indented; + var style = state.subMode.token(subStream, state.subState); + stream.pos = subStream.pos + state.stack.indented; + return style; + } + function firstSub(stream, state) { + state.stack.indented = stream.column(); + state.line = state.tokenize = sub; + return state.tokenize(stream, state); + } + + function createMode(mode) { + var query = embedded[mode]; + var spec = CodeMirror.mimeModes[query]; + if (spec) { + return CodeMirror.getMode(config, spec); + } + var factory = CodeMirror.modes[query]; + if (factory) { + return factory(config, {name: query}); + } + return CodeMirror.getMode(config, "null"); + } + + function getMode(mode) { + if (!modes.hasOwnProperty(mode)) { + return modes[mode] = createMode(mode); + } + return modes[mode]; + } + + function startSubMode(mode, state) { + var subMode = getMode(mode); + var subState = subMode.startState && subMode.startState(); + + state.subMode = subMode; + state.subState = subState; + + state.stack = { + parent: state.stack, + style: "sub", + indented: state.indented + 1, + tokenize: state.line + }; + state.line = state.tokenize = firstSub; + return "slimSubmode"; + } + + function doctypeLine(stream, _state) { + stream.skipToEnd(); + return "slimDoctype"; + } + + function startLine(stream, state) { + var ch = stream.peek(); + if (ch == '<') { + return (state.tokenize = startHtmlLine(state.tokenize))(stream, state); + } + if (stream.match(/^[|']/)) { + return startHtmlMode(stream, state, 1); + } + if (stream.match(/^\/(!|\[\w+])?/)) { + return commentMode(stream, state); + } + if (stream.match(/^(-|==?[<>]?)/)) { + state.tokenize = lineContinuable(stream.column(), commaContinuable(stream.column(), ruby)); + return "slimSwitch"; + } + if (stream.match(/^doctype\b/)) { + state.tokenize = doctypeLine; + return "keyword"; + } + + var m = stream.match(embeddedRegexp); + if (m) { + return startSubMode(m[1], state); + } + + return slimTag(stream, state); + } + + function slim(stream, state) { + if (state.startOfLine) { + return startLine(stream, state); + } + return slimTag(stream, state); + } + + function slimTag(stream, state) { + if (stream.eat('*')) { + state.tokenize = startRubySplat(slimTagExtras); + return null; + } + if (stream.match(nameRegexp)) { + state.tokenize = slimTagExtras; + return "slimTag"; + } + return slimClass(stream, state); + } + function slimTagExtras(stream, state) { + if (stream.match(/^(<>?|> state.indented && state.last != "slimSubmode") { + state.line = state.tokenize = state.stack.tokenize; + state.stack = state.stack.parent; + state.subMode = null; + state.subState = null; + } + } + if (stream.eatSpace()) return null; + var style = state.tokenize(stream, state); + state.startOfLine = false; + if (style) state.last = style; + return styleMap.hasOwnProperty(style) ? styleMap[style] : style; + }, + + blankLine: function(state) { + if (state.subMode && state.subMode.blankLine) { + return state.subMode.blankLine(state.subState); + } + }, + + innerMode: function(state) { + if (state.subMode) return {state: state.subState, mode: state.subMode}; + return {state: state, mode: mode}; + } + + //indent: function(state) { + // return state.indented; + //} + }; + return mode; + }, "htmlmixed", "ruby"); + + CodeMirror.defineMIME("text/x-slim", "slim"); + CodeMirror.defineMIME("application/x-slim", "slim"); +}); diff --git a/mode/slim/test.js b/mode/slim/test.js new file mode 100644 index 0000000000..be4ddacb62 --- /dev/null +++ b/mode/slim/test.js @@ -0,0 +1,96 @@ +// CodeMirror, copyright (c) by Marijn Haverbeke and others +// Distributed under an MIT license: http://codemirror.net/LICENSE + +// Slim Highlighting for CodeMirror copyright (c) HicknHack Software Gmbh + +(function() { + var mode = CodeMirror.getMode({tabSize: 4, indentUnit: 2}, "slim"); + function MT(name) { test.mode(name, mode, Array.prototype.slice.call(arguments, 1)); } + + // Requires at least one media query + MT("elementName", + "[tag h1] Hey There"); + + MT("oneElementPerLine", + "[tag h1] Hey There .h2"); + + MT("idShortcut", + "[attribute&def #test] Hey There"); + + MT("tagWithIdShortcuts", + "[tag h1][attribute&def #test] Hey There"); + + MT("classShortcut", + "[attribute&qualifier .hello] Hey There"); + + MT("tagWithIdAndClassShortcuts", + "[tag h1][attribute&def #test][attribute&qualifier .hello] Hey There"); + + MT("docType", + "[keyword doctype] xml"); + + MT("comment", + "[comment / Hello WORLD]"); + + MT("notComment", + "[tag h1] This is not a / comment "); + + MT("attributes", + "[tag a]([attribute title]=[string \"test\"]) [attribute href]=[string \"link\"]}"); + + MT("multiLineAttributes", + "[tag a]([attribute title]=[string \"test\"]", + " ) [attribute href]=[string \"link\"]}"); + + MT("htmlCode", + "[tag&bracket <][tag h1][tag&bracket >]Title[tag&bracket ]"); + + MT("rubyBlock", + "[operator&special =][variable-2 @item]"); + + MT("selectorRubyBlock", + "[tag a][attribute&qualifier .test][operator&special =] [variable-2 @item]"); + + MT("nestedRubyBlock", + "[tag a]", + " [operator&special =][variable puts] [string \"test\"]"); + + MT("multilinePlaintext", + "[tag p]", + " | Hello,", + " World"); + + MT("multilineRuby", + "[tag p]", + " [comment /# this is a comment]", + " [comment and this is a comment too]", + " | Date/Time", + " [operator&special -] [variable now] [operator =] [tag DateTime][operator .][property now]", + " [tag strong][operator&special =] [variable now]", + " [operator&special -] [keyword if] [variable now] [operator >] [tag DateTime][operator .][property parse]([string \"December 31, 2006\"])", + " [operator&special =][string \"Happy\"]", + " [operator&special =][string \"Belated\"]", + " [operator&special =][string \"Birthday\"]"); + + MT("multilineComment", + "[comment /]", + " [comment Multiline]", + " [comment Comment]"); + + MT("hamlAfterRubyTag", + "[attribute&qualifier .block]", + " [tag strong][operator&special =] [variable now]", + " [attribute&qualifier .test]", + " [operator&special =][variable now]", + " [attribute&qualifier .right]"); + + MT("stretchedRuby", + "[operator&special =] [variable puts] [string \"Hello\"],", + " [string \"World\"]"); + + MT("interpolationInHashAttribute", + "[tag div]{[attribute id] = [string \"]#{[variable test]}[string _]#{[variable ting]}[string \"]} test"); + + MT("interpolationInHTMLAttribute", + "[tag div]([attribute title]=[string \"]#{[variable test]}[string _]#{[variable ting]()}[string \"]) Test"); +})(); diff --git a/test/index.html b/test/index.html index 9930301eb5..a10bd182d4 100644 --- a/test/index.html +++ b/test/index.html @@ -94,6 +94,8 @@

    Test Suite

    + + From b6e9eea8bb4daec6a0ff23f97104d251a0940414 Mon Sep 17 00:00:00 2001 From: Marijn Haverbeke Date: Thu, 14 Aug 2014 14:17:48 +0200 Subject: [PATCH 2545/5780] [slim mode] Integrate Issue #2755 --- doc/compress.html | 1 + mode/index.html | 1 + mode/meta.js | 1 + mode/slim/slim.js | 8 ++++---- 4 files changed, 7 insertions(+), 4 deletions(-) diff --git a/doc/compress.html b/doc/compress.html index 859210c45c..8d25e4b4e9 100644 --- a/doc/compress.html +++ b/doc/compress.html @@ -152,6 +152,7 @@

    Script compression helper

    + diff --git a/mode/index.html b/mode/index.html index 1aa0b8d821..1c106ae8e0 100644 --- a/mode/index.html +++ b/mode/index.html @@ -95,6 +95,7 @@

    Language modes

  • SCSS
  • Shell
  • Sieve
  • +
  • Slim
  • Smalltalk
  • Smarty
  • Smarty/HTML mixed
  • diff --git a/mode/meta.js b/mode/meta.js index 3627cd7470..e3c32b6f6c 100644 --- a/mode/meta.js +++ b/mode/meta.js @@ -86,6 +86,7 @@ CodeMirror.modeInfo = [ {name: "SCSS", mime: "text/x-scss", mode: "css"}, {name: "Shell", mime: "text/x-sh", mode: "shell"}, {name: "Sieve", mime: "application/sieve", mode: "sieve"}, + {name: "Slim", mime: "text/x-slim", mode: "slim"}, {name: "Smalltalk", mime: "text/x-stsrc", mode: "smalltalk"}, {name: "Smarty", mime: "text/x-smarty", mode: "smarty"}, {name: "SmartyMixed", mime: "text/x-smarty", mode: "smartymixed"}, diff --git a/mode/slim/slim.js b/mode/slim/slim.js index 5e737131aa..164464d066 100644 --- a/mode/slim/slim.js +++ b/mode/slim/slim.js @@ -104,10 +104,10 @@ state.line = state.tokenize; } function finishContinue(state) { - if (state.line == state.tokenize) { - state.line = state.stack.tokenize; - state.stack = state.stack.parent; - } + if (state.line == state.tokenize) { + state.line = state.stack.tokenize; + state.stack = state.stack.parent; + } } function lineContinuable(column, tokenize) { From bbc53ebda20b9a0a5b889d10a3ca4a44a43b3c9a Mon Sep 17 00:00:00 2001 From: Hakan Tunc Date: Mon, 28 Jul 2014 14:36:45 -0500 Subject: [PATCH 2546/5780] Add mimetype text/x-nesc --- mode/clike/clike.js | 11 +++++++++++ 1 file changed, 11 insertions(+) diff --git a/mode/clike/clike.js b/mode/clike/clike.js index 2873e3629b..ee2c77a025 100644 --- a/mode/clike/clike.js +++ b/mode/clike/clike.js @@ -437,4 +437,15 @@ CodeMirror.defineMode("clike", function(config, parserConfig) { modeProps: {fold: ["brace", "include"]} }); + def("text/x-nesc", { + name: "clike", + keywords: words(cKeywords + "as atomic async call command component components configuration event generic " + + "implementation includes interface module new norace nx_struct nx_union post provides " + + "signal task uses abstract extends"), + blockKeywords: words("case do else for if switch while struct"), + atoms: words("null"), + hooks: {"#": cppHook}, + modeProps: {fold: ["brace", "include"]} + }); + }); From a46ad916a6f40a0208c3f04b9fb35dcb7153f691 Mon Sep 17 00:00:00 2001 From: amuntean Date: Mon, 28 Jul 2014 13:31:06 +0200 Subject: [PATCH 2547/5780] [merge] new allowEditingOriginals option to edit all compared files --- addon/merge/merge.css | 6 ++++++ addon/merge/merge.js | 27 ++++++++++++++++++++++----- 2 files changed, 28 insertions(+), 5 deletions(-) diff --git a/addon/merge/merge.css b/addon/merge/merge.css index 63237fc8e9..5d24b9bb7f 100644 --- a/addon/merge/merge.css +++ b/addon/merge/merge.css @@ -62,6 +62,12 @@ color: #44c; } +.CodeMirror-merge-copy-reverse { + position: absolute; + cursor: pointer; + color: #44c; +} + .CodeMirror-merge-copybuttons-left .CodeMirror-merge-copy { left: 2px; } .CodeMirror-merge-copybuttons-right .CodeMirror-merge-copy { right: 2px; } diff --git a/addon/merge/merge.js b/addon/merge/merge.js index bde461fcea..d9b277664b 100644 --- a/addon/merge/merge.js +++ b/addon/merge/merge.js @@ -37,7 +37,7 @@ constructor: DiffView, init: function(pane, orig, options) { this.edit = this.mv.edit; - this.orig = CodeMirror(pane, copyObj({value: orig, readOnly: true}, copyObj(options))); + this.orig = CodeMirror(pane, copyObj({value: orig, readOnly: !this.mv.options.allowEditingOriginals}, copyObj(options))); this.diff = getDiff(asString(orig), asString(options.value)); this.diffOutOfDate = false; @@ -282,16 +282,27 @@ if (dv.copyButtons) { var copy = dv.copyButtons.appendChild(elt("div", dv.type == "left" ? "\u21dd" : "\u21dc", "CodeMirror-merge-copy")); - copy.title = "Revert chunk"; + var editOriginals = dv.mv.options.allowEditingOriginals; + copy.title = editOriginals ? "Push to left" : "Revert chunk"; copy.chunk = {topEdit: topEdit, botEdit: botEdit, topOrig: topOrig, botOrig: botOrig}; copy.style.top = top + "px"; + + if (editOriginals) { + var topReverse = dv.orig.heightAtLine(topEdit, "local") - sTopEdit; + var copyReverse = dv.copyButtons.appendChild(elt("div", dv.type == "right" ? "\u21dd" : "\u21dc", + "CodeMirror-merge-copy-reverse")); + copyReverse.title = "Push to right"; + copyReverse.chunk = {topEdit: topOrig, botEdit: botOrig, topOrig: topEdit, botOrig: botEdit}; + copyReverse.style.top = topReverse + "px"; + dv.type == "right" ? copyReverse.style.left = "2px" : copyReverse.style.right = "2px"; + } } }); } - function copyChunk(dv, chunk) { + function copyChunk(dv, to, from, chunk) { if (dv.diffOutOfDate) return; - dv.edit.replaceRange(dv.orig.getRange(Pos(chunk.topOrig, 0), Pos(chunk.botOrig, 0)), + to.replaceRange(from.getRange(Pos(chunk.topOrig, 0), Pos(chunk.botOrig, 0)), Pos(chunk.topEdit, 0), Pos(chunk.botEdit, 0)); } @@ -326,6 +337,7 @@ (hasRight ? rightPane : editPane).className += " CodeMirror-merge-pane-rightmost"; wrap.push(elt("div", null, null, "height: 0; clear: both;")); + var wrapElt = this.wrap = node.appendChild(elt("div", wrap, "CodeMirror-merge CodeMirror-merge-" + panes + "pane")); this.edit = CodeMirror(editPane, copyObj(options)); @@ -353,7 +365,12 @@ dv.copyButtons = elt("div", null, "CodeMirror-merge-copybuttons-" + dv.type); CodeMirror.on(dv.copyButtons, "click", function(e) { var node = e.target || e.srcElement; - if (node.chunk) copyChunk(dv, node.chunk); + if (!node.chunk) return; + if (node.className == "CodeMirror-merge-copy-reverse") { + copyChunk(dv, dv.orig, dv.edit, node.chunk); + return; + } + copyChunk(dv, dv.edit, dv.orig, node.chunk); }); gapElts.unshift(dv.copyButtons); } From 162c6073e60fcc6635ba31e8ed7a0d35da68a5e0 Mon Sep 17 00:00:00 2001 From: Doug Wikle Date: Mon, 21 Jul 2014 08:29:29 -0400 Subject: [PATCH 2548/5780] [verilog mode] Addressed indentation issue Blocking indentation for import/export keywords --- mode/verilog/test.js | 144 +++++++++++++++++++++++++++++++++++++--- mode/verilog/verilog.js | 5 ++ 2 files changed, 138 insertions(+), 11 deletions(-) diff --git a/mode/verilog/test.js b/mode/verilog/test.js index e78860deb9..376d198685 100644 --- a/mode/verilog/test.js +++ b/mode/verilog/test.js @@ -5,7 +5,7 @@ var mode = CodeMirror.getMode({indentUnit: 4}, "verilog"); function MT(name) { test.mode(name, mode, Array.prototype.slice.call(arguments, 1)); } - MT("Binary literals", + MT("binary_literals", "[number 1'b0]", "[number 1'b1]", "[number 1'bx]", @@ -30,14 +30,14 @@ "[number 'b0101]" ); - MT("Octal literals", + MT("octal_literals", "[number 3'o7]", "[number 3'O7]", "[number 3'so7]", "[number 3'SO7]" ); - MT("Decimal literals", + MT("decimal_literals", "[number 0]", "[number 1]", "[number 7]", @@ -52,7 +52,7 @@ "[number 32 'd 123]" ); - MT("Hex literals", + MT("hex_literals", "[number 4'h0]", "[number 4'ha]", "[number 4'hF]", @@ -69,7 +69,7 @@ "[number 32'hFFF?]" ); - MT("Real number literals", + MT("real_number_literals", "[number 1.2]", "[number 0.1]", "[number 2394.26331]", @@ -82,36 +82,158 @@ "[number 236.123_763_e-12]" ); - MT("Operators", + MT("operators", "[meta ^]" ); - MT("Keywords", + MT("keywords", "[keyword logic]", "[keyword logic] [variable foo]", "[keyword reg] [variable abc]" ); - MT("Variables", + MT("variables", "[variable _leading_underscore]", "[variable _if]", "[number 12] [variable foo]", "[variable foo] [number 14]" ); - MT("Tick defines", + MT("tick_defines", "[def `FOO]", "[def `foo]", "[def `FOO_bar]" ); - MT("System calls", + MT("system_calls", "[meta $display]", "[meta $vpi_printf]" ); - MT("Line comment", "[comment // Hello world]"); + MT("line_comment", "[comment // Hello world]"); + // Alignment tests + MT("align_port_map_style1", + /** + * mod mod(.a(a), + * .b(b) + * ); + */ + "[variable mod] [variable mod][bracket (].[variable a][bracket (][variable a][bracket )],", + " .[variable b][bracket (][variable b][bracket )]", + " [bracket )];", + "" + ); + + MT("align_port_map_style2", + /** + * mod mod( + * .a(a), + * .b(b) + * ); + */ + "[variable mod] [variable mod][bracket (]", + " .[variable a][bracket (][variable a][bracket )],", + " .[variable b][bracket (][variable b][bracket )]", + "[bracket )];", + "" + ); + + // Indentation tests + MT("indent_single_statement_if", + "[keyword if] [bracket (][variable foo][bracket )]", + " [keyword break];", + "" + ); + + MT("no_indent_after_single_line_if", + "[keyword if] [bracket (][variable foo][bracket )] [keyword break];", + "" + ); + + MT("indent_after_if_begin_same_line", + "[keyword if] [bracket (][variable foo][bracket )] [keyword begin]", + " [keyword break];", + " [keyword break];", + "[keyword end]", + "" + ); + + MT("indent_after_if_begin_next_line", + "[keyword if] [bracket (][variable foo][bracket )]", + " [keyword begin]", + " [keyword break];", + " [keyword break];", + " [keyword end]", + "" + ); + + MT("indent_single_statement_if_else", + "[keyword if] [bracket (][variable foo][bracket )]", + " [keyword break];", + "[keyword else]", + " [keyword break];", + "" + ); + + MT("indent_if_else_begin_same_line", + "[keyword if] [bracket (][variable foo][bracket )] [keyword begin]", + " [keyword break];", + " [keyword break];", + "[keyword end] [keyword else] [keyword begin]", + " [keyword break];", + " [keyword break];", + "[keyword end]", + "" + ); + + MT("indent_if_else_begin_next_line", + "[keyword if] [bracket (][variable foo][bracket )]", + " [keyword begin]", + " [keyword break];", + " [keyword break];", + " [keyword end]", + "[keyword else]", + " [keyword begin]", + " [keyword break];", + " [keyword break];", + " [keyword end]", + "" + ); + + MT("indent_if_nested_without_begin", + "[keyword if] [bracket (][variable foo][bracket )]", + " [keyword if] [bracket (][variable foo][bracket )]", + " [keyword if] [bracket (][variable foo][bracket )]", + " [keyword break];", + "" + ); + + MT("indent_case", + "[keyword case] [bracket (][variable state][bracket )]", + " [variable FOO]:", + " [keyword break];", + " [variable BAR]:", + " [keyword break];", + "[keyword endcase]", + "" + ); + + MT("unindent_after_end_with_preceding_text", + "[keyword begin]", + " [keyword break]; [keyword end]", + "" + ); + + MT("export_function_does_not_indent", + "[keyword export] [string \"DPI-C\"] [keyword function] [variable helloFromSV];", + "" + ); + + MT("export_task_does_not_indent", + "[keyword export] [string \"DPI-C\"] [keyword task] [variable helloFromSV];", + "" + ); })(); diff --git a/mode/verilog/verilog.js b/mode/verilog/verilog.js index 3414ec022e..46209b2492 100644 --- a/mode/verilog/verilog.js +++ b/mode/verilog/verilog.js @@ -95,6 +95,11 @@ CodeMirror.defineMode("verilog", function(config, parserConfig) { openClose["do" ] = "while"; openClose["fork" ] = "join;join_any;join_none"; + // This is a bit of a hack but will work to not indent after import/epxort statements + // as long as the function/task name is on the same line + openClose["import"] = "function;task"; + openClose["export"] = "function;task"; + for (var i in noIndentKeywords) { var keyword = noIndentKeywords[i]; if (openClose[keyword]) { From d0916042da46ae396e3cba45de623f2a24c137f1 Mon Sep 17 00:00:00 2001 From: TheHowl Date: Sun, 25 May 2014 14:49:59 +0200 Subject: [PATCH 2549/5780] [php mode] Add json, curl, mysqli extensions --- mode/php/php.js | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/mode/php/php.js b/mode/php/php.js index 75b003ff75..f8821ed651 100644 --- a/mode/php/php.js +++ b/mode/php/php.js @@ -95,7 +95,7 @@ "die echo empty exit eval include include_once isset list require require_once return " + "print unset __halt_compiler self static parent yield insteadof finally"; var phpAtoms = "true false null TRUE FALSE NULL __CLASS__ __DIR__ __FILE__ __LINE__ __METHOD__ __FUNCTION__ __NAMESPACE__ __TRAIT__"; - var phpBuiltin = "func_num_args func_get_arg func_get_args strlen strcmp strncmp strcasecmp strncasecmp each error_reporting define defined trigger_error user_error set_error_handler restore_error_handler get_declared_classes get_loaded_extensions extension_loaded get_extension_funcs debug_backtrace constant bin2hex hex2bin sleep usleep time mktime gmmktime strftime gmstrftime strtotime date gmdate getdate localtime checkdate flush wordwrap htmlspecialchars htmlentities html_entity_decode md5 md5_file crc32 getimagesize image_type_to_mime_type phpinfo phpversion phpcredits strnatcmp strnatcasecmp substr_count strspn strcspn strtok strtoupper strtolower strpos strrpos strrev hebrev hebrevc nl2br basename dirname pathinfo stripslashes stripcslashes strstr stristr strrchr str_shuffle str_word_count strcoll substr substr_replace quotemeta ucfirst ucwords strtr addslashes addcslashes rtrim str_replace str_repeat count_chars chunk_split trim ltrim strip_tags similar_text explode implode setlocale localeconv parse_str str_pad chop strchr sprintf printf vprintf vsprintf sscanf fscanf parse_url urlencode urldecode rawurlencode rawurldecode readlink linkinfo link unlink exec system escapeshellcmd escapeshellarg passthru shell_exec proc_open proc_close rand srand getrandmax mt_rand mt_srand mt_getrandmax base64_decode base64_encode abs ceil floor round is_finite is_nan is_infinite bindec hexdec octdec decbin decoct dechex base_convert number_format fmod ip2long long2ip getenv putenv getopt microtime gettimeofday getrusage uniqid quoted_printable_decode set_time_limit get_cfg_var magic_quotes_runtime set_magic_quotes_runtime get_magic_quotes_gpc get_magic_quotes_runtime import_request_variables error_log serialize unserialize memory_get_usage var_dump var_export debug_zval_dump print_r highlight_file show_source highlight_string ini_get ini_get_all ini_set ini_alter ini_restore get_include_path set_include_path restore_include_path setcookie header headers_sent connection_aborted connection_status ignore_user_abort parse_ini_file is_uploaded_file move_uploaded_file intval floatval doubleval strval gettype settype is_null is_resource is_bool is_long is_float is_int is_integer is_double is_real is_numeric is_string is_array is_object is_scalar ereg ereg_replace eregi eregi_replace split spliti join sql_regcase dl pclose popen readfile rewind rmdir umask fclose feof fgetc fgets fgetss fread fopen fpassthru ftruncate fstat fseek ftell fflush fwrite fputs mkdir rename copy tempnam tmpfile file file_get_contents stream_select stream_context_create stream_context_set_params stream_context_set_option stream_context_get_options stream_filter_prepend stream_filter_append fgetcsv flock get_meta_tags stream_set_write_buffer set_file_buffer set_socket_blocking stream_set_blocking socket_set_blocking stream_get_meta_data stream_register_wrapper stream_wrapper_register stream_set_timeout socket_set_timeout socket_get_status realpath fnmatch fsockopen pfsockopen pack unpack get_browser crypt opendir closedir chdir getcwd rewinddir readdir dir glob fileatime filectime filegroup fileinode filemtime fileowner fileperms filesize filetype file_exists is_writable is_writeable is_readable is_executable is_file is_dir is_link stat lstat chown touch clearstatcache mail ob_start ob_flush ob_clean ob_end_flush ob_end_clean ob_get_flush ob_get_clean ob_get_length ob_get_level ob_get_status ob_get_contents ob_implicit_flush ob_list_handlers ksort krsort natsort natcasesort asort arsort sort rsort usort uasort uksort shuffle array_walk count end prev next reset current key min max in_array array_search extract compact array_fill range array_multisort array_push array_pop array_shift array_unshift array_splice array_slice array_merge array_merge_recursive array_keys array_values array_count_values array_reverse array_reduce array_pad array_flip array_change_key_case array_rand array_unique array_intersect array_intersect_assoc array_diff array_diff_assoc array_sum array_filter array_map array_chunk array_key_exists pos sizeof key_exists assert assert_options version_compare ftok str_rot13 aggregate session_name session_module_name session_save_path session_id session_regenerate_id session_decode session_register session_unregister session_is_registered session_encode session_start session_destroy session_unset session_set_save_handler session_cache_limiter session_cache_expire session_set_cookie_params session_get_cookie_params session_write_close preg_match preg_match_all preg_replace preg_replace_callback preg_split preg_quote preg_grep overload ctype_alnum ctype_alpha ctype_cntrl ctype_digit ctype_lower ctype_graph ctype_print ctype_punct ctype_space ctype_upper ctype_xdigit virtual apache_request_headers apache_note apache_lookup_uri apache_child_terminate apache_setenv apache_response_headers apache_get_version getallheaders mysql_connect mysql_pconnect mysql_close mysql_select_db mysql_create_db mysql_drop_db mysql_query mysql_unbuffered_query mysql_db_query mysql_list_dbs mysql_list_tables mysql_list_fields mysql_list_processes mysql_error mysql_errno mysql_affected_rows mysql_insert_id mysql_result mysql_num_rows mysql_num_fields mysql_fetch_row mysql_fetch_array mysql_fetch_assoc mysql_fetch_object mysql_data_seek mysql_fetch_lengths mysql_fetch_field mysql_field_seek mysql_free_result mysql_field_name mysql_field_table mysql_field_len mysql_field_type mysql_field_flags mysql_escape_string mysql_real_escape_string mysql_stat mysql_thread_id mysql_client_encoding mysql_get_client_info mysql_get_host_info mysql_get_proto_info mysql_get_server_info mysql_info mysql mysql_fieldname mysql_fieldtable mysql_fieldlen mysql_fieldtype mysql_fieldflags mysql_selectdb mysql_createdb mysql_dropdb mysql_freeresult mysql_numfields mysql_numrows mysql_listdbs mysql_listtables mysql_listfields mysql_db_name mysql_dbname mysql_tablename mysql_table_name pg_connect pg_pconnect pg_close pg_connection_status pg_connection_busy pg_connection_reset pg_host pg_dbname pg_port pg_tty pg_options pg_ping pg_query pg_send_query pg_cancel_query pg_fetch_result pg_fetch_row pg_fetch_assoc pg_fetch_array pg_fetch_object pg_fetch_all pg_affected_rows pg_get_result pg_result_seek pg_result_status pg_free_result pg_last_oid pg_num_rows pg_num_fields pg_field_name pg_field_num pg_field_size pg_field_type pg_field_prtlen pg_field_is_null pg_get_notify pg_get_pid pg_result_error pg_last_error pg_last_notice pg_put_line pg_end_copy pg_copy_to pg_copy_from pg_trace pg_untrace pg_lo_create pg_lo_unlink pg_lo_open pg_lo_close pg_lo_read pg_lo_write pg_lo_read_all pg_lo_import pg_lo_export pg_lo_seek pg_lo_tell pg_escape_string pg_escape_bytea pg_unescape_bytea pg_client_encoding pg_set_client_encoding pg_meta_data pg_convert pg_insert pg_update pg_delete pg_select pg_exec pg_getlastoid pg_cmdtuples pg_errormessage pg_numrows pg_numfields pg_fieldname pg_fieldsize pg_fieldtype pg_fieldnum pg_fieldprtlen pg_fieldisnull pg_freeresult pg_result pg_loreadall pg_locreate pg_lounlink pg_loopen pg_loclose pg_loread pg_lowrite pg_loimport pg_loexport http_response_code get_declared_traits getimagesizefromstring socket_import_stream stream_set_chunk_size trait_exists header_register_callback class_uses session_status session_register_shutdown echo print global static exit array empty eval isset unset die include require include_once require_once"; + var phpBuiltin = "func_num_args func_get_arg func_get_args strlen strcmp strncmp strcasecmp strncasecmp each error_reporting define defined trigger_error user_error set_error_handler restore_error_handler get_declared_classes get_loaded_extensions extension_loaded get_extension_funcs debug_backtrace constant bin2hex hex2bin sleep usleep time mktime gmmktime strftime gmstrftime strtotime date gmdate getdate localtime checkdate flush wordwrap htmlspecialchars htmlentities html_entity_decode md5 md5_file crc32 getimagesize image_type_to_mime_type phpinfo phpversion phpcredits strnatcmp strnatcasecmp substr_count strspn strcspn strtok strtoupper strtolower strpos strrpos strrev hebrev hebrevc nl2br basename dirname pathinfo stripslashes stripcslashes strstr stristr strrchr str_shuffle str_word_count strcoll substr substr_replace quotemeta ucfirst ucwords strtr addslashes addcslashes rtrim str_replace str_repeat count_chars chunk_split trim ltrim strip_tags similar_text explode implode setlocale localeconv parse_str str_pad chop strchr sprintf printf vprintf vsprintf sscanf fscanf parse_url urlencode urldecode rawurlencode rawurldecode readlink linkinfo link unlink exec system escapeshellcmd escapeshellarg passthru shell_exec proc_open proc_close rand srand getrandmax mt_rand mt_srand mt_getrandmax base64_decode base64_encode abs ceil floor round is_finite is_nan is_infinite bindec hexdec octdec decbin decoct dechex base_convert number_format fmod ip2long long2ip getenv putenv getopt microtime gettimeofday getrusage uniqid quoted_printable_decode set_time_limit get_cfg_var magic_quotes_runtime set_magic_quotes_runtime get_magic_quotes_gpc get_magic_quotes_runtime import_request_variables error_log serialize unserialize memory_get_usage var_dump var_export debug_zval_dump print_r highlight_file show_source highlight_string ini_get ini_get_all ini_set ini_alter ini_restore get_include_path set_include_path restore_include_path setcookie header headers_sent connection_aborted connection_status ignore_user_abort parse_ini_file is_uploaded_file move_uploaded_file intval floatval doubleval strval gettype settype is_null is_resource is_bool is_long is_float is_int is_integer is_double is_real is_numeric is_string is_array is_object is_scalar ereg ereg_replace eregi eregi_replace split spliti join sql_regcase dl pclose popen readfile rewind rmdir umask fclose feof fgetc fgets fgetss fread fopen fpassthru ftruncate fstat fseek ftell fflush fwrite fputs mkdir rename copy tempnam tmpfile file file_get_contents stream_select stream_context_create stream_context_set_params stream_context_set_option stream_context_get_options stream_filter_prepend stream_filter_append fgetcsv flock get_meta_tags stream_set_write_buffer set_file_buffer set_socket_blocking stream_set_blocking socket_set_blocking stream_get_meta_data stream_register_wrapper stream_wrapper_register stream_set_timeout socket_set_timeout socket_get_status realpath fnmatch fsockopen pfsockopen pack unpack get_browser crypt opendir closedir chdir getcwd rewinddir readdir dir glob fileatime filectime filegroup fileinode filemtime fileowner fileperms filesize filetype file_exists is_writable is_writeable is_readable is_executable is_file is_dir is_link stat lstat chown touch clearstatcache mail ob_start ob_flush ob_clean ob_end_flush ob_end_clean ob_get_flush ob_get_clean ob_get_length ob_get_level ob_get_status ob_get_contents ob_implicit_flush ob_list_handlers ksort krsort natsort natcasesort asort arsort sort rsort usort uasort uksort shuffle array_walk count end prev next reset current key min max in_array array_search extract compact array_fill range array_multisort array_push array_pop array_shift array_unshift array_splice array_slice array_merge array_merge_recursive array_keys array_values array_count_values array_reverse array_reduce array_pad array_flip array_change_key_case array_rand array_unique array_intersect array_intersect_assoc array_diff array_diff_assoc array_sum array_filter array_map array_chunk array_key_exists pos sizeof key_exists assert assert_options version_compare ftok str_rot13 aggregate session_name session_module_name session_save_path session_id session_regenerate_id session_decode session_register session_unregister session_is_registered session_encode session_start session_destroy session_unset session_set_save_handler session_cache_limiter session_cache_expire session_set_cookie_params session_get_cookie_params session_write_close preg_match preg_match_all preg_replace preg_replace_callback preg_split preg_quote preg_grep overload ctype_alnum ctype_alpha ctype_cntrl ctype_digit ctype_lower ctype_graph ctype_print ctype_punct ctype_space ctype_upper ctype_xdigit virtual apache_request_headers apache_note apache_lookup_uri apache_child_terminate apache_setenv apache_response_headers apache_get_version getallheaders mysql_connect mysql_pconnect mysql_close mysql_select_db mysql_create_db mysql_drop_db mysql_query mysql_unbuffered_query mysql_db_query mysql_list_dbs mysql_list_tables mysql_list_fields mysql_list_processes mysql_error mysql_errno mysql_affected_rows mysql_insert_id mysql_result mysql_num_rows mysql_num_fields mysql_fetch_row mysql_fetch_array mysql_fetch_assoc mysql_fetch_object mysql_data_seek mysql_fetch_lengths mysql_fetch_field mysql_field_seek mysql_free_result mysql_field_name mysql_field_table mysql_field_len mysql_field_type mysql_field_flags mysql_escape_string mysql_real_escape_string mysql_stat mysql_thread_id mysql_client_encoding mysql_get_client_info mysql_get_host_info mysql_get_proto_info mysql_get_server_info mysql_info mysql mysql_fieldname mysql_fieldtable mysql_fieldlen mysql_fieldtype mysql_fieldflags mysql_selectdb mysql_createdb mysql_dropdb mysql_freeresult mysql_numfields mysql_numrows mysql_listdbs mysql_listtables mysql_listfields mysql_db_name mysql_dbname mysql_tablename mysql_table_name pg_connect pg_pconnect pg_close pg_connection_status pg_connection_busy pg_connection_reset pg_host pg_dbname pg_port pg_tty pg_options pg_ping pg_query pg_send_query pg_cancel_query pg_fetch_result pg_fetch_row pg_fetch_assoc pg_fetch_array pg_fetch_object pg_fetch_all pg_affected_rows pg_get_result pg_result_seek pg_result_status pg_free_result pg_last_oid pg_num_rows pg_num_fields pg_field_name pg_field_num pg_field_size pg_field_type pg_field_prtlen pg_field_is_null pg_get_notify pg_get_pid pg_result_error pg_last_error pg_last_notice pg_put_line pg_end_copy pg_copy_to pg_copy_from pg_trace pg_untrace pg_lo_create pg_lo_unlink pg_lo_open pg_lo_close pg_lo_read pg_lo_write pg_lo_read_all pg_lo_import pg_lo_export pg_lo_seek pg_lo_tell pg_escape_string pg_escape_bytea pg_unescape_bytea pg_client_encoding pg_set_client_encoding pg_meta_data pg_convert pg_insert pg_update pg_delete pg_select pg_exec pg_getlastoid pg_cmdtuples pg_errormessage pg_numrows pg_numfields pg_fieldname pg_fieldsize pg_fieldtype pg_fieldnum pg_fieldprtlen pg_fieldisnull pg_freeresult pg_result pg_loreadall pg_locreate pg_lounlink pg_loopen pg_loclose pg_loread pg_lowrite pg_loimport pg_loexport http_response_code get_declared_traits getimagesizefromstring socket_import_stream stream_set_chunk_size trait_exists header_register_callback class_uses session_status session_register_shutdown echo print global static exit array empty eval isset unset die include require include_once require_once json_decode json_encode json_last_error json_last_error_msg curl_close curl_copy_handle curl_errno curl_error curl_escape curl_exec curl_file_create curl_getinfo curl_init curl_multi_add_handle curl_multi_close curl_multi_exec curl_multi_getcontent curl_multi_info_read curl_multi_init curl_multi_remove_handle curl_multi_select curl_multi_setopt curl_multi_strerror curl_pause curl_reset curl_setopt_array curl_setopt curl_share_close curl_share_init curl_share_setopt curl_strerror curl_unescape curl_version mysqli_affected_rows mysqli_autocommit mysqli_change_user mysqli_character_set_name mysqli_close mysqli_commit mysqli_connect_errno mysqli_connect_error mysqli_connect mysqli_data_seek mysqli_debug mysqli_dump_debug_info mysqli_errno mysqli_error_list mysqli_error mysqli_fetch_all mysqli_fetch_array mysqli_fetch_assoc mysqli_fetch_field_direct mysqli_fetch_field mysqli_fetch_fields mysqli_fetch_lengths mysqli_fetch_object mysqli_fetch_row mysqli_field_count mysqli_field_seek mysqli_field_tell mysqli_free_result mysqli_get_charset mysqli_get_client_info mysqli_get_client_stats mysqli_get_client_version mysqli_get_connection_stats mysqli_get_host_info mysqli_get_proto_info mysqli_get_server_info mysqli_get_server_version mysqli_info mysqli_init mysqli_insert_id mysqli_kill mysqli_more_results mysqli_multi_query mysqli_next_result mysqli_num_fields mysqli_num_rows mysqli_options mysqli_ping mysqli_prepare mysqli_query mysqli_real_connect mysqli_real_escape_string mysqli_real_query mysqli_reap_async_query mysqli_refresh mysqli_rollback mysqli_select_db mysqli_set_charset mysqli_set_local_infile_default mysqli_set_local_infile_handler mysqli_sqlstate mysqli_ssl_set mysqli_stat mysqli_stmt_init mysqli_store_result mysqli_thread_id mysqli_thread_safe mysqli_use_result mysqli_warning_count"; CodeMirror.registerHelper("hintWords", "php", [phpKeywords, phpAtoms, phpBuiltin].join(" ").split(" ")); CodeMirror.registerHelper("wordChars", "php", /[\w$]/); From d46fd84445330c370d61c9cf38fc5e2c90236e08 Mon Sep 17 00:00:00 2001 From: Marijn Haverbeke Date: Thu, 14 Aug 2014 18:02:28 +0200 Subject: [PATCH 2550/5780] [sql-hint addon] Clean up, fix handling of whitespace Closes #2761 --- addon/hint/sql-hint.js | 110 ++++++++++++++++++++--------------------- mode/sql/index.html | 10 +++- 2 files changed, 62 insertions(+), 58 deletions(-) diff --git a/addon/hint/sql-hint.js b/addon/hint/sql-hint.js index fd58b8834e..cc756a2485 100644 --- a/addon/hint/sql-hint.js +++ b/addon/hint/sql-hint.js @@ -21,7 +21,7 @@ function getKeywords(editor) { var mode = editor.doc.modeOption; - if(mode === "sql") mode = "text/x-sql"; + if (mode === "sql") mode = "text/x-sql"; return CodeMirror.resolveMode(mode).keywords; } @@ -32,12 +32,12 @@ } function addMatches(result, search, wordlist, formatter) { - for(var word in wordlist) { - if(!wordlist.hasOwnProperty(word)) continue; - if(Array.isArray(wordlist)) { + for (var word in wordlist) { + if (!wordlist.hasOwnProperty(word)) continue; + if (Array.isArray(wordlist)) { word = wordlist[word]; } - if(match(search, word)) { + if (match(search, word)) { result.push(formatter(word)); } } @@ -49,33 +49,30 @@ var string = token.string.substr(1); var prevCur = Pos(cur.line, token.start); var table = editor.getTokenAt(prevCur).string; - if( !tables.hasOwnProperty( table ) ){ + if (!tables.hasOwnProperty(table)) table = findTableByAlias(table, editor); - } var columns = tables[table]; - if(!columns) { - return; - } - addMatches(result, string, columns, - function(w) {return "." + w;}); + if (!columns) return; + + addMatches(result, string, columns, function(w) {return "." + w;}); } function eachWord(lineText, f) { - if( !lineText ){return;} + if (!lineText) return; var excepted = /[,;]/g; - var words = lineText.split( " " ); - for( var i = 0; i < words.length; i++ ){ - f( words[i]?words[i].replace( excepted, '' ) : '' ); + var words = lineText.split(" "); + for (var i = 0; i < words.length; i++) { + f(words[i]?words[i].replace(excepted, '') : ''); } } - function convertCurToNumber( cur ){ + function convertCurToNumber(cur) { // max characters of a line is 999,999. - return cur.line + cur.ch / Math.pow( 10, 6 ); + return cur.line + cur.ch / Math.pow(10, 6); } - function convertNumberToCur( num ){ - return Pos(Math.floor( num ), +num.toString().split( '.' ).pop()); + function convertNumberToCur(num) { + return Pos(Math.floor(num), +num.toString().split('.').pop()); } function findTableByAlias(alias, editor) { @@ -86,26 +83,26 @@ var table = ""; var separator = []; var validRange = { - start: Pos( 0, 0 ), - end: Pos( editor.lastLine(), editor.getLineHandle( editor.lastLine() ).length ) + start: Pos(0, 0), + end: Pos(editor.lastLine(), editor.getLineHandle(editor.lastLine()).length) }; //add separator - var indexOfSeparator = fullQuery.indexOf( CONS.QUERY_DIV ); - while( indexOfSeparator != -1 ){ - separator.push( doc.posFromIndex(indexOfSeparator)); - indexOfSeparator = fullQuery.indexOf( CONS.QUERY_DIV, indexOfSeparator+1); + var indexOfSeparator = fullQuery.indexOf(CONS.QUERY_DIV); + while(indexOfSeparator != -1) { + separator.push(doc.posFromIndex(indexOfSeparator)); + indexOfSeparator = fullQuery.indexOf(CONS.QUERY_DIV, indexOfSeparator+1); } - separator.unshift( Pos( 0, 0 ) ); - separator.push( Pos( editor.lastLine(), editor.getLineHandle( editor.lastLine() ).text.length ) ); + separator.unshift(Pos(0, 0)); + separator.push(Pos(editor.lastLine(), editor.getLineHandle(editor.lastLine()).text.length)); - //find valieRange + //find valid range var prevItem = 0; - var current = convertCurToNumber( editor.getCursor() ); - for( var i=0; i< separator.length; i++){ - var _v = convertCurToNumber( separator[i] ); - if( current > prevItem && current <= _v ){ - validRange = { start: convertNumberToCur( prevItem ), end: convertNumberToCur( _v ) }; + var current = convertCurToNumber(editor.getCursor()); + for (var i=0; i< separator.length; i++) { + var _v = convertCurToNumber(separator[i]); + if (current > prevItem && current <= _v) { + validRange = { start: convertNumberToCur(prevItem), end: convertNumberToCur(_v) }; break; } prevItem = _v; @@ -113,52 +110,51 @@ var query = doc.getRange(validRange.start, validRange.end, false); - for(var i=0; i < query.length; i++){ + for (var i = 0; i < query.length; i++) { var lineText = query[i]; - eachWord( lineText, function( word ){ + eachWord(lineText, function(word) { var wordUpperCase = word.toUpperCase(); - if( wordUpperCase === aliasUpperCase && tables.hasOwnProperty( previousWord ) ){ + if (wordUpperCase === aliasUpperCase && tables.hasOwnProperty(previousWord)) { table = previousWord; } - if( wordUpperCase !== CONS.ALIAS_KEYWORD ){ + if (wordUpperCase !== CONS.ALIAS_KEYWORD) { previousWord = word; } }); - if( table ){ break; } + if (table) break; } return table; } - function sqlHint(editor, options) { + CodeMirror.registerHelper("hint", "sql", function(editor, options) { tables = (options && options.tables) || {}; keywords = keywords || getKeywords(editor); var cur = editor.getCursor(); - var token = editor.getTokenAt(cur), end = token.end; var result = []; - var search = token.string.trim(); - + var token = editor.getTokenAt(cur), start, end, search; + if (token.string.match(/^\.?[\w@]+$/)) { + search = token.string; + start = token.start; + end = token.end; + } else { + start = end = cur.ch; + search = ""; + } if (search.charAt(0) == ".") { columnCompletion(result, editor); if (!result.length) { - while (token.start && search.charAt(0) == ".") { + while (start && search.charAt(0) == ".") { token = editor.getTokenAt(Pos(cur.line, token.start - 1)); + start = token.start; search = token.string + search; } - addMatches(result, search, tables, - function(w) {return w;}); + addMatches(result, search, tables, function(w) {return w;}); } } else { - addMatches(result, search, keywords, - function(w) {return w.toUpperCase();}); - addMatches(result, search, tables, - function(w) {return w;}); + addMatches(result, search, tables, function(w) {return w;}); + addMatches(result, search, keywords, function(w) {return w.toUpperCase();}); } - return { - list: result, - from: Pos(cur.line, token.start), - to: Pos(cur.line, end) - }; - } - CodeMirror.registerHelper("hint", "sql", sqlHint); + return {list: result, from: Pos(cur.line, start), to: Pos(cur.line, end)}; + }); }); diff --git a/mode/sql/index.html b/mode/sql/index.html index 79a2e74e0f..7dd5f3075e 100644 --- a/mode/sql/index.html +++ b/mode/sql/index.html @@ -7,6 +7,9 @@ + + + + + +
    +

    Modelica mode

    + +
    + + + +

    Simple mode that tries to handle Modelica as well as it can.

    + +

    MIME types defined: text/x-modelica + (Modlica code).

    +
    diff --git a/mode/modelica/modelica.js b/mode/modelica/modelica.js new file mode 100644 index 0000000000..77ec7a3c18 --- /dev/null +++ b/mode/modelica/modelica.js @@ -0,0 +1,245 @@ +// CodeMirror, copyright (c) by Marijn Haverbeke and others +// Distributed under an MIT license: http://codemirror.net/LICENSE + +// Modelica support for CodeMirror, copyright (c) by Lennart Ochel + +(function(mod) { + if (typeof exports == "object" && typeof module == "object") // CommonJS + mod(require("../../lib/codemirror")); + else if (typeof define == "function" && define.amd) // AMD + define(["../../lib/codemirror"], mod); + else // Plain browser env + mod(CodeMirror); +}) + +(function(CodeMirror) { + "use strict"; + + CodeMirror.defineMode("modelica", function(config, parserConfig) { + + var indentUnit = config.indentUnit; + var keywords = parserConfig.keywords || {}; + var builtin = parserConfig.builtin || {}; + var atoms = parserConfig.atoms || {}; + + var isSingleOperatorChar = /[;=\(:\),{}.*<>+\-\/^\[\]]/; + var isDoubleOperatorChar = /(:=|<=|>=|==|<>|\.\+|\.\-|\.\*|\.\/|\.\^)/; + var isDigit = /[0-9]/; + var isNonDigit = /[_a-zA-Z]/; + + function tokenLineComment(stream, state) { + stream.skipToEnd(); + state.tokenize = null; + return "comment"; + } + + function tokenBlockComment(stream, state) { + var maybeEnd = false, ch; + while (ch = stream.next()) { + if (maybeEnd && ch == "/") { + state.tokenize = null; + break; + } + maybeEnd = (ch == "*"); + } + return "comment"; + } + + function tokenString(stream, state) { + var escaped = false, ch; + while ((ch = stream.next()) != null) { + if (ch == '"' && !escaped) { + state.tokenize = null; + state.sol = false; + break; + } + escaped = !escaped && ch == "\\"; + } + + return "string"; + } + + function tokenIdent(stream, state) { + stream.eatWhile(isDigit); + while (stream.eat(isDigit) || stream.eat(isNonDigit)) { } + + + var cur = stream.current(); + + if(state.sol && (cur == "package" || cur == "model" || cur == "when" || cur == "connector")) state.level++; + else if(state.sol && cur == "end" && state.level > 0) state.level--; + + state.tokenize = null; + state.sol = false; + + if (keywords.propertyIsEnumerable(cur)) return "keyword"; + else if (builtin.propertyIsEnumerable(cur)) return "builtin"; + else if (atoms.propertyIsEnumerable(cur)) return "atom"; + else return "variable"; + } + + function tokenQIdent(stream, state) { + while (stream.eat(/[^']/)) { } + + state.tokenize = null; + state.sol = false; + + if(stream.eat("'")) + return "variable"; + else + return "error"; + } + + function tokenUnsignedNuber(stream, state) { + stream.eatWhile(isDigit); + if (stream.eat('.')) { + stream.eatWhile(isDigit); + } + if (stream.eat('e') || stream.eat('E')) { + if (!stream.eat('-')) + stream.eat('+'); + stream.eatWhile(isDigit); + } + + state.tokenize = null; + state.sol = false; + return "number"; + } + + // Interface + return { + startState: function() { + return { + tokenize: null, + level: 0, + sol: true + }; + }, + + token: function(stream, state) { + if(state.tokenize != null) { + return state.tokenize(stream, state); + } + + if(stream.sol()) { + state.sol = true; + } + + // WHITESPACE + if(stream.eatSpace()) { + state.tokenize = null; + return null; + } + + var ch = stream.next(); + + // LINECOMMENT + if(ch == '/' && stream.eat('/')) { + state.tokenize = tokenLineComment; + } + // BLOCKCOMMENT + else if(ch == '/' && stream.eat('*')) { + state.tokenize = tokenBlockComment; + } + // TWO SYMBOL TOKENS + else if(isDoubleOperatorChar.test(ch+stream.peek())) { + stream.next(); + state.tokenize = null; + return "operator"; + } + // SINGLE SYMBOL TOKENS + else if(isSingleOperatorChar.test(ch)) { + state.tokenize = null; + return "operator"; + } + // IDENT + else if(isNonDigit.test(ch)) { + state.tokenize = tokenIdent; + } + // Q-IDENT + else if(ch == "'" && stream.peek() && stream.peek() != "'") { + state.tokenize = tokenQIdent; + } + // STRING + else if(ch == '"') { + state.tokenize = tokenString; + } + // UNSIGNED_NUBER + else if(isDigit.test(ch)) { + state.tokenize = tokenUnsignedNuber; + } + // ERROR + else { + state.tokenize = null; + return "error"; + } + + return state.tokenize(stream, state); + }, + + indent: function(state, textAfter) { + if (state.tokenize != null) return CodeMirror.Pass; + + var level = state.level; + if(/(algorithm)/.test(textAfter)) level--; + if(/(equation)/.test(textAfter)) level--; + if(/(initial algorithm)/.test(textAfter)) level--; + if(/(initial equation)/.test(textAfter)) level--; + if(/(end)/.test(textAfter)) level--; + + if(level > 0) + return indentUnit*level; + else + return 0; + }, + + blockCommentStart: "/*", + blockCommentEnd: "*/", + lineComment: "//" + }; + }); + + function words(str) { + var obj = {}, words = str.split(" "); + for (var i=0; i Date: Wed, 27 Aug 2014 00:39:53 -0700 Subject: [PATCH 2569/5780] [python mode] Make 'in' a keyword --- mode/python/python.js | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/mode/python/python.js b/mode/python/python.js index 45dd63ba48..4e1f296ae1 100644 --- a/mode/python/python.js +++ b/mode/python/python.js @@ -15,12 +15,12 @@ return new RegExp("^((" + words.join(")|(") + "))\\b"); } - var wordOperators = wordRegexp(["and", "or", "not", "is", "in"]); + var wordOperators = wordRegexp(["and", "or", "not", "is"]); var commonKeywords = ["as", "assert", "break", "class", "continue", "def", "del", "elif", "else", "except", "finally", "for", "from", "global", "if", "import", "lambda", "pass", "raise", "return", - "try", "while", "with", "yield"]; + "try", "while", "with", "yield", "in"]; var commonBuiltins = ["abs", "all", "any", "bin", "bool", "bytearray", "callable", "chr", "classmethod", "compile", "complex", "delattr", "dict", "dir", "divmod", "enumerate", "eval", "filter", "float", "format", "frozenset", From 43379968f6a62b89e2b350acd9174971e3e50706 Mon Sep 17 00:00:00 2001 From: Hiroyuki Makino Date: Thu, 28 Aug 2014 23:03:04 +0900 Subject: [PATCH 2570/5780] [release notes] Fix section structure --- doc/releases.html | 10 +++++++--- 1 file changed, 7 insertions(+), 3 deletions(-) diff --git a/doc/releases.html b/doc/releases.html index 4b6d384b9e..f88a0373a6 100644 --- a/doc/releases.html +++ b/doc/releases.html @@ -25,9 +25,9 @@

    Release notes and version history

    -
    +
    -

    Version 4.x

    +

    Version 4.x

    21-08-2014: Version 4.5:

    @@ -114,7 +114,11 @@

    Version 4.x

  • Full list of patches.
  • -

    Version 3.x

    +
    + +
    + +

    Version 3.x

    22-04-2014: Version 3.24:

    From 3d8c1e506fd80feeb28a873a6dc6d1087d82327a Mon Sep 17 00:00:00 2001 From: Marijn Haverbeke Date: Mon, 1 Sep 2014 13:32:06 +0200 Subject: [PATCH 2571/5780] Export findWordAt Closes #2790 --- doc/manual.html | 4 ++++ lib/codemirror.js | 42 +++++++++++++++++++++--------------------- 2 files changed, 25 insertions(+), 21 deletions(-) diff --git a/doc/manual.html b/doc/manual.html index 6069fa6aea..02f3a59aaf 100644 --- a/doc/manual.html +++ b/doc/manual.html @@ -1312,6 +1312,10 @@

    Cursor and selection methods

    be "line" or "page". The other arguments and the returned value have the same interpretation as they have in findPosH. + +
    cm.findWordAt(pos: {line, ch}) → {anchor: {line, ch}, head: {line, ch}}
    +
    Returns the start and end of the 'word' (the stretch of + letters, whitespace, or punctuation) at the given position.

    Configuration methods

    diff --git a/lib/codemirror.js b/lib/codemirror.js index 2c3b17df67..4270b98a45 100644 --- a/lib/codemirror.js +++ b/lib/codemirror.js @@ -2528,7 +2528,7 @@ var pos = posFromMouse(cm, e); if (!pos || clickInGutter(cm, e) || eventInWidget(cm.display, e)) return; e_preventDefault(e); - var word = findWordAt(cm, pos); + var word = cm.findWordAt(pos); extendSelection(cm.doc, word.anchor, word.head); })); else @@ -2807,7 +2807,7 @@ start = posFromMouse(cm, e, true, true); ourIndex = -1; } else if (type == "double") { - var word = findWordAt(cm, start); + var word = cm.findWordAt(start); if (cm.display.shift || doc.extend) ourRange = extendRange(doc, ourRange, word.anchor, word.head); else @@ -2861,7 +2861,7 @@ var anchor = oldRange.anchor, head = pos; if (type != "single") { if (type == "double") - var range = findWordAt(cm, pos); + var range = cm.findWordAt(pos); else var range = new Range(Pos(pos.line, 0), clipPos(doc, Pos(pos.line + 1, 0))); if (cmp(range.anchor, anchor) > 0) { @@ -3999,24 +3999,6 @@ return target; } - // Find the word at the given position (as returned by coordsChar). - function findWordAt(cm, pos) { - var doc = cm.doc, line = getLine(doc, pos.line).text; - var start = pos.ch, end = pos.ch; - if (line) { - var helper = cm.getHelper(pos, "wordChars"); - if ((pos.xRel < 0 || end == line.length) && start) --start; else ++end; - var startChar = line.charAt(start); - var check = isWordChar(startChar, helper) - ? function(ch) { return isWordChar(ch, helper); } - : /\s/.test(startChar) ? function(ch) {return /\s/.test(ch);} - : function(ch) {return !/\s/.test(ch) && !isWordChar(ch);}; - while (start > 0 && check(line.charAt(start - 1))) --start; - while (end < line.length && check(line.charAt(end))) ++end; - } - return new Range(Pos(pos.line, start), Pos(pos.line, end)); - } - // EDITOR METHODS // The publicly visible API. Note that methodOp(f) means @@ -4358,6 +4340,24 @@ doc.sel.ranges[i].goalColumn = goals[i]; }), + // Find the word at the given position (as returned by coordsChar). + findWordAt: function(pos) { + var doc = this.doc, line = getLine(doc, pos.line).text; + var start = pos.ch, end = pos.ch; + if (line) { + var helper = this.getHelper(pos, "wordChars"); + if ((pos.xRel < 0 || end == line.length) && start) --start; else ++end; + var startChar = line.charAt(start); + var check = isWordChar(startChar, helper) + ? function(ch) { return isWordChar(ch, helper); } + : /\s/.test(startChar) ? function(ch) {return /\s/.test(ch);} + : function(ch) {return !/\s/.test(ch) && !isWordChar(ch);}; + while (start > 0 && check(line.charAt(start - 1))) --start; + while (end < line.length && check(line.charAt(end))) ++end; + } + return new Range(Pos(pos.line, start), Pos(pos.line, end)); + }, + toggleOverwrite: function(value) { if (value != null && value == this.state.overwrite) return; if (this.state.overwrite = !this.state.overwrite) From 9b87ca9ca4f8c81f84ff755e921ddaf3aa7373fc Mon Sep 17 00:00:00 2001 From: Marijn Haverbeke Date: Mon, 1 Sep 2014 13:34:35 +0200 Subject: [PATCH 2572/5780] [merge addon] Add diff_match_patch as explicit dependency Closes #2783 --- addon/merge/merge.js | 12 ++++++------ 1 file changed, 6 insertions(+), 6 deletions(-) diff --git a/addon/merge/merge.js b/addon/merge/merge.js index d9b277664b..da3ea47ccf 100644 --- a/addon/merge/merge.js +++ b/addon/merge/merge.js @@ -1,17 +1,17 @@ // CodeMirror, copyright (c) by Marijn Haverbeke and others // Distributed under an MIT license: http://codemirror.net/LICENSE +// declare global: diff_match_patch, DIFF_INSERT, DIFF_DELETE, DIFF_EQUAL + (function(mod) { if (typeof exports == "object" && typeof module == "object") // CommonJS - mod(require("../../lib/codemirror")); + mod(require("../../lib/codemirror"), require("diff_match_patch")); else if (typeof define == "function" && define.amd) // AMD - define(["../../lib/codemirror"], mod); + define(["../../lib/codemirror", "diff_match_patch"], mod); else // Plain browser env - mod(CodeMirror); -})(function(CodeMirror) { + mod(CodeMirror, diff_match_patch); +})(function(CodeMirror, diff_match_patch) { "use strict"; - // declare global: diff_match_patch, DIFF_INSERT, DIFF_DELETE, DIFF_EQUAL - var Pos = CodeMirror.Pos; var svgNS = "http://www.w3.org/2000/svg"; From 59b7b9082be50d51e9b5589e6957668c5d3cb35a Mon Sep 17 00:00:00 2001 From: Marijn Haverbeke Date: Mon, 1 Sep 2014 13:44:22 +0200 Subject: [PATCH 2573/5780] Add Hebrew code range to nonASCIISingleCaseWordChar regexp Fixes detection of Hebrew letters as word characters Closes #2789 --- lib/codemirror.js | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/lib/codemirror.js b/lib/codemirror.js index 4270b98a45..f06b99bd72 100644 --- a/lib/codemirror.js +++ b/lib/codemirror.js @@ -7295,7 +7295,7 @@ return function(){return f.apply(null, args);}; } - var nonASCIISingleCaseWordChar = /[\u00df\u3040-\u309f\u30a0-\u30ff\u3400-\u4db5\u4e00-\u9fcc\uac00-\ud7af]/; + var nonASCIISingleCaseWordChar = /[\u00df\u0590-\u05f4\u3040-\u309f\u30a0-\u30ff\u3400-\u4db5\u4e00-\u9fcc\uac00-\ud7af]/; var isWordCharBasic = CodeMirror.isWordChar = function(ch) { return /\w/.test(ch) || ch > "\x80" && (ch.toUpperCase() != ch.toLowerCase() || nonASCIISingleCaseWordChar.test(ch)); From 623eb7badb58f5d7bc9bbd836f86f046180e2a1c Mon Sep 17 00:00:00 2001 From: Marijn Haverbeke Date: Mon, 1 Sep 2014 17:59:23 +0200 Subject: [PATCH 2574/5780] Force scroll update from swapDoc Issue #2714 --- lib/codemirror.js | 5 +++-- 1 file changed, 3 insertions(+), 2 deletions(-) diff --git a/lib/codemirror.js b/lib/codemirror.js index f06b99bd72..c865bc560b 100644 --- a/lib/codemirror.js +++ b/lib/codemirror.js @@ -2090,11 +2090,11 @@ display.wheelStartX = display.wheelStartY = null; // Propagate the scroll position to the actual DOM scroller - if (op.scrollTop != null && display.scroller.scrollTop != op.scrollTop) { + if (op.scrollTop != null && (display.scroller.scrollTop != op.scrollTop || op.forceScroll)) { var top = Math.max(0, Math.min(display.scroller.scrollHeight - display.scroller.clientHeight, op.scrollTop)); display.scroller.scrollTop = display.scrollbarV.scrollTop = doc.scrollTop = top; } - if (op.scrollLeft != null && display.scroller.scrollLeft != op.scrollLeft) { + if (op.scrollLeft != null && (display.scroller.scrollLeft != op.scrollLeft || op.forceScroll)) { var left = Math.max(0, Math.min(display.scroller.scrollWidth - display.scroller.clientWidth, op.scrollLeft)); display.scroller.scrollLeft = display.scrollbarH.scrollLeft = doc.scrollLeft = left; alignHorizontally(cm); @@ -4444,6 +4444,7 @@ clearCaches(this); resetInput(this); this.scrollTo(doc.scrollLeft, doc.scrollTop); + this.curOp.forceScroll = true; signalLater(this, "swapDoc", this, old); return old; }), From 376f3ec90d05272459bf0b79d4ff9005a284705e Mon Sep 17 00:00:00 2001 From: Marijn Haverbeke Date: Mon, 1 Sep 2014 18:04:52 +0200 Subject: [PATCH 2575/5780] [sass mode] Make sure indentCount is initialized propertly Issue #2786 --- mode/sass/sass.js | 1 + 1 file changed, 1 insertion(+) diff --git a/mode/sass/sass.js b/mode/sass/sass.js index 68df323e18..b792a02aa3 100644 --- a/mode/sass/sass.js +++ b/mode/sass/sass.js @@ -303,6 +303,7 @@ CodeMirror.defineMode("sass", function(config) { return { tokenizer: tokenBase, scopes: [{offset: 0, type: "sass"}], + indentCount: 0, definedVars: [], definedMixins: [] }; From cb5416f1229e2c8555784cb089916a5953c07b1b Mon Sep 17 00:00:00 2001 From: Marijn Haverbeke Date: Wed, 3 Sep 2014 08:36:11 +0200 Subject: [PATCH 2576/5780] [markdown mode] Fix handling of escaped characters Issue #2792 --- mode/markdown/markdown.js | 15 ++------------- 1 file changed, 2 insertions(+), 13 deletions(-) diff --git a/mode/markdown/markdown.js b/mode/markdown/markdown.js index 81ed24e8d3..90a9282f85 100644 --- a/mode/markdown/markdown.js +++ b/mode/markdown/markdown.js @@ -360,15 +360,9 @@ CodeMirror.defineMode("markdown", function(cmCfg, modeCfg) { var ch = stream.next(); - if (state.escape) { - state.escape = false; - return getType(state); - } - if (ch === '\\') { - if (modeCfg.highlightFormatting) state.formatting = "escape"; - state.escape = true; - return getType(state); + stream.next(); + if (modeCfg.highlightFormatting) return getType(state) + " escape"; } // Matches link titles present on next line @@ -650,7 +644,6 @@ CodeMirror.defineMode("markdown", function(cmCfg, modeCfg) { inline: inlineNormal, text: handleText, - escape: false, formatting: false, linkText: false, linkHref: false, @@ -683,7 +676,6 @@ CodeMirror.defineMode("markdown", function(cmCfg, modeCfg) { inline: s.inline, text: s.text, - escape: false, formatting: false, linkTitle: s.linkTitle, em: s.em, @@ -718,9 +710,6 @@ CodeMirror.defineMode("markdown", function(cmCfg, modeCfg) { state.thisLineHasContent = true; } - // Reset state.escape - state.escape = false; - // Reset state.taskList state.taskList = false; From 31319ee225a60dcc750ba01a40e70bdedaaaafc7 Mon Sep 17 00:00:00 2001 From: Marijn Haverbeke Date: Wed, 3 Sep 2014 08:42:11 +0200 Subject: [PATCH 2577/5780] [markdown mode] Make test pass --- mode/markdown/markdown.js | 5 ++++- mode/markdown/test.js | 2 +- 2 files changed, 5 insertions(+), 2 deletions(-) diff --git a/mode/markdown/markdown.js b/mode/markdown/markdown.js index 90a9282f85..1bad78d65e 100644 --- a/mode/markdown/markdown.js +++ b/mode/markdown/markdown.js @@ -362,7 +362,10 @@ CodeMirror.defineMode("markdown", function(cmCfg, modeCfg) { if (ch === '\\') { stream.next(); - if (modeCfg.highlightFormatting) return getType(state) + " escape"; + if (modeCfg.highlightFormatting) { + var type = getType(state); + return type ? type + " formatting-escape" : "formatting-escape"; + } } // Matches link titles present on next line diff --git a/mode/markdown/test.js b/mode/markdown/test.js index 77a3b659c0..96ca1aefc7 100644 --- a/mode/markdown/test.js +++ b/mode/markdown/test.js @@ -54,7 +54,7 @@ "[link&formatting&formatting-link <][link user@example.com][link&formatting&formatting-link >]"); FT("formatting_escape", - "[formatting&formatting-escape \\]*"); + "[formatting-escape \\*]"); MT("plainText", "foo"); From f593e03a09a156f1a7824757beaa001369f11422 Mon Sep 17 00:00:00 2001 From: Marijn Haverbeke Date: Wed, 3 Sep 2014 22:23:25 +0200 Subject: [PATCH 2578/5780] Set a min height on the lines element to prevent collapsing of wrapper Issue #2765 --- lib/codemirror.css | 1 + 1 file changed, 1 insertion(+) diff --git a/lib/codemirror.css b/lib/codemirror.css index c0897771aa..1ab8acb296 100644 --- a/lib/codemirror.css +++ b/lib/codemirror.css @@ -213,6 +213,7 @@ div.CodeMirror span.CodeMirror-nonmatchingbracket {color: #f22;} .CodeMirror-lines { cursor: text; + min-height: 1px; /* prevents collapsing before first draw */ } .CodeMirror pre { /* Reset some styles that the rest of the page might have set */ From 17c992e979daf3a3aa75926d38144e3c9d4dc064 Mon Sep 17 00:00:00 2001 From: Marijn Haverbeke Date: Thu, 4 Sep 2014 08:44:24 +0200 Subject: [PATCH 2579/5780] Only apply IE<11 zooming compensation for client rects when getting rect from a range It does not happen when measuring a node Issue #2665 --- lib/codemirror.js | 3 +-- 1 file changed, 1 insertion(+), 2 deletions(-) diff --git a/lib/codemirror.js b/lib/codemirror.js index c865bc560b..151ee91a66 100644 --- a/lib/codemirror.js +++ b/lib/codemirror.js @@ -1665,6 +1665,7 @@ start = start - 1; collapse = "right"; } + if (ie && ie_version < 11) rect = maybeUpdateRectForZooming(cm.display.measure, rect); } else { // If it is a widget, simply get the box for the whole widget. if (start > 0) collapse = bias = "right"; var rects; @@ -1681,8 +1682,6 @@ rect = nullRect; } - if (ie && ie_version < 11) rect = maybeUpdateRectForZooming(cm.display.measure, rect); - var rtop = rect.top - prepared.rect.top, rbot = rect.bottom - prepared.rect.top; var mid = (rtop + rbot) / 2; var heights = prepared.view.measure.heights; From 616339b47245b3a044057afa460a3768ffa0369c Mon Sep 17 00:00:00 2001 From: Marijn Haverbeke Date: Mon, 8 Sep 2014 12:52:49 +0200 Subject: [PATCH 2580/5780] [closetag addon] Work better with htmlmixed mode It now closes script and style tags when the slash is typed Closes #2797 --- addon/edit/closetag.js | 22 ++++++++++++++++++---- 1 file changed, 18 insertions(+), 4 deletions(-) diff --git a/addon/edit/closetag.js b/addon/edit/closetag.js index 69ea4446be..a0bec7dd43 100644 --- a/addon/edit/closetag.js +++ b/addon/edit/closetag.js @@ -102,11 +102,25 @@ var pos = ranges[i].head, tok = cm.getTokenAt(pos); var inner = CodeMirror.innerMode(cm.getMode(), tok.state), state = inner.state; if (tok.type == "string" || tok.string.charAt(0) != "<" || - tok.start != pos.ch - 1 || inner.mode.name != "xml" || - !state.context || !state.context.tagName || - closingTagExists(cm, state.context.tagName, pos, state)) + tok.start != pos.ch - 1) return CodeMirror.Pass; - replacements[i] = "/" + state.context.tagName + ">"; + // Kludge to get around the fact that we are not in XML mode + // when completing in JS/CSS snippet in htmlmixed mode. Does not + // work for other XML embedded languages (there is no general + // way to go from a mixed mode to its current XML state). + if (inner.mode.name != "xml") { + if (cm.getMode().name == "htmlmixed" && inner.mode.name == "javascript") + replacements[i] = "/script>"; + else if (cm.getMode().name == "htmlmixed" && inner.mode.name == "css") + replacements[i] = "/style>"; + else + return CodeMirror.Pass; + } else { + if (!state.context || !state.context.tagName || + closingTagExists(cm, state.context.tagName, pos, state)) + return CodeMirror.Pass; + replacements[i] = "/" + state.context.tagName + ">"; + } } cm.replaceSelections(replacements); ranges = cm.listSelections(); From 7817708c3165f1ca376bdc7181df6d036cb90ad8 Mon Sep 17 00:00:00 2001 From: Marijn Haverbeke Date: Mon, 8 Sep 2014 13:03:54 +0200 Subject: [PATCH 2581/5780] [javascript mode] Fix parsing of argument-less fat arrow functions Closes #2794 --- mode/javascript/javascript.js | 8 +++----- 1 file changed, 3 insertions(+), 5 deletions(-) diff --git a/mode/javascript/javascript.js b/mode/javascript/javascript.js index 46616bc021..2e24d3a55b 100644 --- a/mode/javascript/javascript.js +++ b/mode/javascript/javascript.js @@ -391,7 +391,7 @@ CodeMirror.defineMode("javascript", function(config, parserConfig) { function maybeoperatorNoComma(type, value, noComma) { var me = noComma == false ? maybeoperatorComma : maybeoperatorNoComma; var expr = noComma == false ? expression : expressionNoComma; - if (value == "=>") return cont(pushcontext, noComma ? arrowBodyNoComma : arrowBody, popcontext); + if (type == "=>") return cont(pushcontext, noComma ? arrowBodyNoComma : arrowBody, popcontext); if (type == "operator") { if (/\+\+|--/.test(value)) return cont(me); if (value == "?") return cont(expression, expect(":"), expr); @@ -417,13 +417,11 @@ CodeMirror.defineMode("javascript", function(config, parserConfig) { } function arrowBody(type) { findFatArrow(cx.stream, cx.state); - if (type == "{") return pass(statement); - return pass(expression); + return pass(type == "{" ? statement : expression); } function arrowBodyNoComma(type) { findFatArrow(cx.stream, cx.state); - if (type == "{") return pass(statement); - return pass(expressionNoComma); + return pass(type == "{" ? statement : expressionNoComma); } function maybelabel(type) { if (type == ":") return cont(poplex, statement); From 0a91c1427a2771a94731384f3537c78468be44f8 Mon Sep 17 00:00:00 2001 From: Marijn Haverbeke Date: Mon, 8 Sep 2014 13:22:02 +0200 Subject: [PATCH 2582/5780] [smalltalk mode] Use \s instead of space in regexps that denote whitespace Closes #2796 --- mode/smalltalk/smalltalk.js | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/mode/smalltalk/smalltalk.js b/mode/smalltalk/smalltalk.js index 50f41cc615..bb510ba2e1 100644 --- a/mode/smalltalk/smalltalk.js +++ b/mode/smalltalk/smalltalk.js @@ -53,7 +53,7 @@ CodeMirror.defineMode('smalltalk', function(config) { stream.next(); token = nextSymbol(stream, new Context(nextSymbol, context)); } else { - if (stream.eatWhile(/[^ .{}\[\]()]/)) + if (stream.eatWhile(/[^\s.{}\[\]()]/)) token.name = 'string-2'; else token.name = 'meta'; @@ -61,7 +61,7 @@ CodeMirror.defineMode('smalltalk', function(config) { } else if (aChar === '$') { if (stream.next() === '<') { - stream.eatWhile(/[^ >]/); + stream.eatWhile(/[^\s>]/); stream.next(); } token.name = 'string-2'; From 1adaa01dbf7fb030b61731314dd245d9dbf8014e Mon Sep 17 00:00:00 2001 From: Marijn Haverbeke Date: Tue, 9 Sep 2014 16:22:11 +0200 Subject: [PATCH 2583/5780] Add a requireJS demo --- addon/hint/html-hint.js | 4 ++-- demo/requirejs.html | 52 +++++++++++++++++++++++++++++++++++++++++ 2 files changed, 54 insertions(+), 2 deletions(-) create mode 100644 demo/requirejs.html diff --git a/addon/hint/html-hint.js b/addon/hint/html-hint.js index addd9b7bc0..992218f288 100755 --- a/addon/hint/html-hint.js +++ b/addon/hint/html-hint.js @@ -3,9 +3,9 @@ (function(mod) { if (typeof exports == "object" && typeof module == "object") // CommonJS - mod(require("../../lib/codemirror")); + mod(require("../../lib/codemirror", "./xml-hint")); else if (typeof define == "function" && define.amd) // AMD - define(["../../lib/codemirror"], mod); + define(["../../lib/codemirror", "./xml-hint"], mod); else // Plain browser env mod(CodeMirror); })(function(CodeMirror) { diff --git a/demo/requirejs.html b/demo/requirejs.html new file mode 100644 index 0000000000..6399f8d769 --- /dev/null +++ b/demo/requirejs.html @@ -0,0 +1,52 @@ + + + + CodeMirror: HTML completion demo + + + + + + + + + + + + +
    +

    RequireJS module loading demo

    + +

    This demo does the same thing as + the HTML5 completion demo, but + loads its dependencies + with Require.js, rather than + explicitly. Press ctrl-space to activate + completion.

    + +
    + + +
    + From c012f411ee01c9802d046c2d76dc3b4b0001d67b Mon Sep 17 00:00:00 2001 From: Marcel Gerber Date: Tue, 9 Sep 2014 12:23:51 +0200 Subject: [PATCH 2584/5780] Improve isWordChar() for Arabic language --- lib/codemirror.js | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/lib/codemirror.js b/lib/codemirror.js index 151ee91a66..ea2caaff62 100644 --- a/lib/codemirror.js +++ b/lib/codemirror.js @@ -7295,7 +7295,7 @@ return function(){return f.apply(null, args);}; } - var nonASCIISingleCaseWordChar = /[\u00df\u0590-\u05f4\u3040-\u309f\u30a0-\u30ff\u3400-\u4db5\u4e00-\u9fcc\uac00-\ud7af]/; + var nonASCIISingleCaseWordChar = /[\u00df\u0590-\u05f4\u0600-\u06ff\u3040-\u309f\u30a0-\u30ff\u3400-\u4db5\u4e00-\u9fcc\uac00-\ud7af]/; var isWordCharBasic = CodeMirror.isWordChar = function(ch) { return /\w/.test(ch) || ch > "\x80" && (ch.toUpperCase() != ch.toLowerCase() || nonASCIISingleCaseWordChar.test(ch)); From 8fac50ac265de832e08d428faffb36d464cb5bda Mon Sep 17 00:00:00 2001 From: Marijn Haverbeke Date: Mon, 15 Sep 2014 11:32:38 +0200 Subject: [PATCH 2585/5780] Fix handling of electricChars in multi-line edits Closes #2804 --- lib/codemirror.js | 6 +++--- 1 file changed, 3 insertions(+), 3 deletions(-) diff --git a/lib/codemirror.js b/lib/codemirror.js index ea2caaff62..246492a9fb 100644 --- a/lib/codemirror.js +++ b/lib/codemirror.js @@ -2456,16 +2456,16 @@ cm.options.smartIndent && range.head.ch < 100 && (!i || doc.sel.ranges[i - 1].head.line != range.head.line)) { var mode = cm.getModeAt(range.head); + var end = changeEnd(changeEvent); if (mode.electricChars) { for (var j = 0; j < mode.electricChars.length; j++) if (inserted.indexOf(mode.electricChars.charAt(j)) > -1) { - indentLine(cm, range.head.line, "smart"); + indentLine(cm, end.line, "smart"); break; } } else if (mode.electricInput) { - var end = changeEnd(changeEvent); if (mode.electricInput.test(getLine(doc, end.line).text.slice(0, end.ch))) - indentLine(cm, range.head.line, "smart"); + indentLine(cm, end.line, "smart"); } } } From c2ee5a6ec55f4542adc9838b467861019a3cad9b Mon Sep 17 00:00:00 2001 From: Marko Bonaci Date: Fri, 12 Sep 2014 19:14:38 +0200 Subject: [PATCH 2586/5780] Make parentheses visible while under `matchingbracket` rule Code: Brackets now become black when they are highlighted as "matching". JSON: key names are now in "white" (json was a bit saturated with orange - keys and string values). Cleanup: removed some unnecessary rules. Default face color normalized to #ffffec (was #ffffe9). --- theme/mbo.css | 24 ++++++++++-------------- 1 file changed, 10 insertions(+), 14 deletions(-) diff --git a/theme/mbo.css b/theme/mbo.css index 6cb2b18d9b..0ad6360b50 100644 --- a/theme/mbo.css +++ b/theme/mbo.css @@ -1,6 +1,10 @@ -/* Based on mbonaci's Brackets mbo theme */ +/****************************************************************/ +/* Based on mbonaci's Brackets mbo theme */ +/* https://github.com/mbonaci/global/blob/master/Mbo.tmTheme */ +/* Create your own: http://tmtheme-editor.herokuapp.com */ +/****************************************************************/ -.cm-s-mbo.CodeMirror {background: #2c2c2c; color: #ffffe9;} +.cm-s-mbo.CodeMirror {background: #2c2c2c; color: #ffffec;} .cm-s-mbo div.CodeMirror-selected {background: #716C62 !important;} .cm-s-mbo .CodeMirror-gutters {background: #4e4e4e; border-right: 0px;} .cm-s-mbo .CodeMirror-guttermarker { color: white; } @@ -15,6 +19,7 @@ .cm-s-mbo span.cm-property, .cm-s-mbo span.cm-attribute {color: #9ddfe9;} .cm-s-mbo span.cm-keyword {color: #ffb928;} .cm-s-mbo span.cm-string {color: #ffcf6c;} +.cm-s-mbo span.cm-string.cm-property {color: #ffffec;} .cm-s-mbo span.cm-variable {color: #ffffec;} .cm-s-mbo span.cm-variable-2 {color: #00a8c6;} @@ -23,17 +28,8 @@ .cm-s-mbo span.cm-tag {color: #9ddfe9;} .cm-s-mbo span.cm-link {color: #f54b07;} .cm-s-mbo span.cm-error {border-bottom: #636363; color: #ffffec;} +.cm-s-mbo span.cm-qualifier {color: #ffffec;} .cm-s-mbo .CodeMirror-activeline-background {background: #494b41 !important;} -.cm-s-mbo .CodeMirror-matchingbracket { - text-decoration: underline; - color: #f5e107 !important; - } - -.cm-s-mbo .CodeMirror-matchingtag { background: rgba(255, 255, 255, .37); } - -.cm-s-mbo span.cm-searching { - background-color: none; - background: none; - box-shadow: 0 0 0 1px #ffffec; -} +.cm-s-mbo .CodeMirror-matchingbracket {color: #222 !important;} +.cm-s-mbo .CodeMirror-matchingtag {background: rgba(255, 255, 255, .37);} From fcacef102486908b4331c8db9dc084daff7562bd Mon Sep 17 00:00:00 2001 From: Marijn Haverbeke Date: Mon, 15 Sep 2014 15:29:33 +0200 Subject: [PATCH 2587/5780] [show-hint addon] Give active hint color more precedence To make it easy for client code to color completions without clashing with the selected styling. --- addon/hint/show-hint.css | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/addon/hint/show-hint.css b/addon/hint/show-hint.css index 8a4ff052e3..924e638f7f 100644 --- a/addon/hint/show-hint.css +++ b/addon/hint/show-hint.css @@ -32,7 +32,7 @@ cursor: pointer; } -.CodeMirror-hint-active { +li.CodeMirror-hint-active { background: #08f; color: white; } From 640fbb28abf7dc4014c8c5ee6efe9309c091a98a Mon Sep 17 00:00:00 2001 From: snasa Date: Mon, 15 Sep 2014 22:30:25 -0700 Subject: [PATCH 2588/5780] Update shell.js --- mode/shell/shell.js | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-) diff --git a/mode/shell/shell.js b/mode/shell/shell.js index 77be75b97d..8e31f6f30b 100644 --- a/mode/shell/shell.js +++ b/mode/shell/shell.js @@ -128,7 +128,8 @@ CodeMirror.defineMode('shell', function() { startState: function() {return {tokens:[]};}, token: function(stream, state) { return tokenize(stream, state); - } + }, + lineComment: '#' }; }); From f09b6c5ec7bf2d7a7b2ee1ac8cc5c53b27f76f62 Mon Sep 17 00:00:00 2001 From: Daniele Di Sarli Date: Mon, 1 Sep 2014 21:53:17 +0200 Subject: [PATCH 2589/5780] Added wordsOnly option. Updated documentation. --- addon/search/match-highlighter.js | 32 +++++++++++++++++++++++++++---- 1 file changed, 28 insertions(+), 4 deletions(-) diff --git a/addon/search/match-highlighter.js b/addon/search/match-highlighter.js index 14dd4d4184..e9a22721f7 100644 --- a/addon/search/match-highlighter.js +++ b/addon/search/match-highlighter.js @@ -8,12 +8,15 @@ // document. // // The option can be set to true to simply enable it, or to a -// {minChars, style, showToken} object to explicitly configure it. -// minChars is the minimum amount of characters that should be +// {minChars, style, wordsOnly, showToken, delay} object to explicitly +// configure it. minChars is the minimum amount of characters that should be // selected for the behavior to occur, and style is the token style to // apply to the matches. This will be prefixed by "cm-" to create an -// actual CSS class name. showToken, when enabled, will cause the -// current token to be highlighted when nothing is selected. +// actual CSS class name. If wordsOnly is enabled, the matches will be +// highlighted only if the selected text is a word. showToken, when enabled, +// will cause the current token to be highlighted when nothing is selected. +// delay is used to specify how much time to wait, in milliseconds, before +// highlighting the matches. (function(mod) { if (typeof exports == "object" && typeof module == "object") // CommonJS @@ -28,6 +31,7 @@ var DEFAULT_MIN_CHARS = 2; var DEFAULT_TOKEN_STYLE = "matchhighlight"; var DEFAULT_DELAY = 100; + var DEFAULT_WORDS_ONLY = false; function State(options) { if (typeof options == "object") { @@ -35,10 +39,12 @@ this.style = options.style; this.showToken = options.showToken; this.delay = options.delay; + this.wordsOnly = options.wordsOnly; } if (this.style == null) this.style = DEFAULT_TOKEN_STYLE; if (this.minChars == null) this.minChars = DEFAULT_MIN_CHARS; if (this.delay == null) this.delay = DEFAULT_DELAY; + if (this.wordsOnly == null) this.wordsOnly = DEFAULT_WORDS_ONLY; this.overlay = this.timeout = null; } @@ -81,12 +87,30 @@ } var from = cm.getCursor("from"), to = cm.getCursor("to"); if (from.line != to.line) return; + if (state.wordsOnly && !isWord(cm, from, to)) return; var selection = cm.getRange(from, to).replace(/^\s+|\s+$/g, ""); if (selection.length >= state.minChars) cm.addOverlay(state.overlay = makeOverlay(selection, false, state.style)); }); } + function isWord(cm, from, to) { + var str = cm.getRange(from, to); + if (str.match(/^\w+$/) !== null) { + if (from.ch > 0) { + var pos = {line: from.line, ch: from.ch - 1}; + var chr = cm.getRange(pos, from); + if (chr.match(/\W/) === null) return false; + } + if (to.ch < cm.getLine(from.line).length) { + var pos = {line: to.line, ch: to.ch + 1}; + var chr = cm.getRange(to, pos); + if (chr.match(/\W/) === null) return false; + } + return true; + } else return false; + } + function boundariesAround(stream, re) { return (!stream.start || !re.test(stream.string.charAt(stream.start - 1))) && (stream.pos == stream.string.length || !re.test(stream.string.charAt(stream.pos))); From cf15c70108b65fda224510177c9ac18e6727f9c0 Mon Sep 17 00:00:00 2001 From: Marijn Haverbeke Date: Thu, 18 Sep 2014 14:59:08 +0200 Subject: [PATCH 2590/5780] Add example of using markselection to style background to demo --- demo/markselection.html | 15 +++++++++++---- lib/codemirror.css | 9 ++++++++- 2 files changed, 19 insertions(+), 5 deletions(-) diff --git a/demo/markselection.html b/demo/markselection.html index 93e38ec833..a182217fd5 100644 --- a/demo/markselection.html +++ b/demo/markselection.html @@ -12,6 +12,7 @@ .CodeMirror {border-top: 1px solid black; border-bottom: 1px solid black;} .CodeMirror-selected { background-color: blue !important; } .CodeMirror-selectedtext { color: white; } + .styled-background { background-color: #ff7; }
    -

    Addons

    +

    Addons

    The addon directory in the distribution contains a number of reusable components that implement extra editor diff --git a/index.html b/index.html index 7a2bbdcfce..cf9f943ac5 100644 --- a/index.html +++ b/index.html @@ -40,7 +40,7 @@

    CodeMirror is a versatile text editor implemented in JavaScript for the browser. It is specialized for - editing code, and comes with a number of language modes and addons + editing code, and comes with a number of language modes and addons that implement more advanced editing functionality.

    A rich programming API and a From 137737779912ef2cb67416e5e91852753acfe6ae Mon Sep 17 00:00:00 2001 From: Yunchi Luo Date: Tue, 14 Oct 2014 22:53:08 -0400 Subject: [PATCH 2653/5780] [vim] Fix uppercase key lookup --- keymap/vim.js | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/keymap/vim.js b/keymap/vim.js index 9abb0f52bc..a0a01e06c6 100644 --- a/keymap/vim.js +++ b/keymap/vim.js @@ -230,7 +230,7 @@ function lookupKey(e) { var keyCode = e.keyCode; if (modifierCodes.indexOf(keyCode) != -1) { return; } - var hasModifier = e.ctrlKey || e.shiftKey || e.metaKey; + var hasModifier = e.ctrlKey || e.metaKey; var key = CodeMirror.keyNames[keyCode]; key = specialKey[key] || key; var name = ''; From 0caeb00baf3abf8e89d0358fbc3c5ee7eb2d75dd Mon Sep 17 00:00:00 2001 From: Marijn Haverbeke Date: Thu, 16 Oct 2014 09:45:57 +0200 Subject: [PATCH 2654/5780] Move ctrl-up/down bindings to mac keymap, bind them to goLineUp/Down on PC --- lib/codemirror.js | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/lib/codemirror.js b/lib/codemirror.js index dbfe56a503..df514190d2 100644 --- a/lib/codemirror.js +++ b/lib/codemirror.js @@ -4864,7 +4864,7 @@ // are simply ignored. keyMap.pcDefault = { "Ctrl-A": "selectAll", "Ctrl-D": "deleteLine", "Ctrl-Z": "undo", "Shift-Ctrl-Z": "redo", "Ctrl-Y": "redo", - "Ctrl-Home": "goDocStart", "Ctrl-Up": "goDocStart", "Ctrl-End": "goDocEnd", "Ctrl-Down": "goDocEnd", + "Ctrl-Home": "goDocStart", "Ctrl-End": "goDocEnd", "Ctrl-Up": "goLineUp", "Ctrl-Down": "goLineDown", "Ctrl-Left": "goGroupLeft", "Ctrl-Right": "goGroupRight", "Alt-Left": "goLineStart", "Alt-Right": "goLineEnd", "Ctrl-Backspace": "delGroupBefore", "Ctrl-Delete": "delGroupAfter", "Ctrl-S": "save", "Ctrl-F": "find", "Ctrl-G": "findNext", "Shift-Ctrl-G": "findPrev", "Shift-Ctrl-F": "replace", "Shift-Ctrl-R": "replaceAll", @@ -4879,7 +4879,7 @@ "Ctrl-Alt-Backspace": "delGroupAfter", "Alt-Delete": "delGroupAfter", "Cmd-S": "save", "Cmd-F": "find", "Cmd-G": "findNext", "Shift-Cmd-G": "findPrev", "Cmd-Alt-F": "replace", "Shift-Cmd-Alt-F": "replaceAll", "Cmd-[": "indentLess", "Cmd-]": "indentMore", "Cmd-Backspace": "delWrappedLineLeft", "Cmd-Delete": "delWrappedLineRight", - "Cmd-U": "undoSelection", "Shift-Cmd-U": "redoSelection", + "Cmd-U": "undoSelection", "Shift-Cmd-U": "redoSelection", "Ctrl-Up": "goDocStart", "Ctrl-Down": "goDocEnd", fallthrough: ["basic", "emacsy"] }; // Very basic readline/emacs-style bindings, which are standard on Mac. From 10d9bf1100c1636aa8d9eb381b86bef23bc3ca3d Mon Sep 17 00:00:00 2001 From: Norman Rzepka Date: Tue, 14 Oct 2014 13:24:42 +0200 Subject: [PATCH 2655/5780] continuelist: hitting enter twice cancels the list --- addon/edit/continuelist.js | 21 ++++++++++++++++----- 1 file changed, 16 insertions(+), 5 deletions(-) diff --git a/addon/edit/continuelist.js b/addon/edit/continuelist.js index 8cee761aee..56f54c9227 100644 --- a/addon/edit/continuelist.js +++ b/addon/edit/continuelist.js @@ -12,6 +12,7 @@ "use strict"; var listRE = /^(\s*)([*+-]|(\d+)\.)(\s+)/, + emptyListRE = /^(\s*)([*+-]|(\d+)\.)(\s*)$/, unorderedBullets = "*+-"; CodeMirror.commands.newlineAndIndentContinueMarkdownList = function(cm) { @@ -25,12 +26,22 @@ cm.execCommand("newlineAndIndent"); return; } - var indent = match[1], after = match[4]; - var bullet = unorderedBullets.indexOf(match[2]) >= 0 - ? match[2] - : (parseInt(match[3], 10) + 1) + "."; + if (cm.getLine(pos.line).match(emptyListRE)) { + cm.replaceRange("", { + line: pos.line, ch: 0 + }, { + line: pos.line, ch: pos.ch + 1 + }); + replacements[i] = "\n"; - replacements[i] = "\n" + indent + bullet + after; + } else { + var indent = match[1], after = match[4]; + var bullet = unorderedBullets.indexOf(match[2]) >= 0 + ? match[2] + : (parseInt(match[3], 10) + 1) + "."; + + replacements[i] = "\n" + indent + bullet + after; + } } cm.replaceSelections(replacements); From d913da999746ef1bd1510ed9f4e275d023c5a785 Mon Sep 17 00:00:00 2001 From: Marijn Haverbeke Date: Thu, 16 Oct 2014 09:54:20 +0200 Subject: [PATCH 2656/5780] [perl mode] Remove electric chars Issue #2870 --- mode/perl/perl.js | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/mode/perl/perl.js b/mode/perl/perl.js index c12677d249..2a64c2ed41 100644 --- a/mode/perl/perl.js +++ b/mode/perl/perl.js @@ -789,7 +789,7 @@ CodeMirror.defineMode("perl",function(){ tail:null};}, token:function(stream,state){ return (state.tokenize||tokenPerl)(stream,state);}, - electricChars:"{}"};}); + };}); CodeMirror.registerHelper("wordChars", "perl", /[\w$]/); From 0462939fa2e8e2699aabcfbb9c86802cbdb6c828 Mon Sep 17 00:00:00 2001 From: Marijn Haverbeke Date: Thu, 16 Oct 2014 10:02:50 +0200 Subject: [PATCH 2657/5780] Fix superfluous comma --- mode/perl/perl.js | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/mode/perl/perl.js b/mode/perl/perl.js index 2a64c2ed41..311574e74a 100644 --- a/mode/perl/perl.js +++ b/mode/perl/perl.js @@ -788,7 +788,7 @@ CodeMirror.defineMode("perl",function(){ style:null, tail:null};}, token:function(stream,state){ - return (state.tokenize||tokenPerl)(stream,state);}, + return (state.tokenize||tokenPerl)(stream,state);} };}); CodeMirror.registerHelper("wordChars", "perl", /[\w$]/); From c576c4cb4a9923b4acd172e7daa48fe67ef3c74f Mon Sep 17 00:00:00 2001 From: Marijn Haverbeke Date: Thu, 16 Oct 2014 17:45:14 +0200 Subject: [PATCH 2658/5780] Don't use finally in the operation that initializes the editor Trying to call endOperation on a half-constructed editor will never end well, and the resulting errors will mask the actual error that happened. --- lib/codemirror.js | 25 ++++++++++++------------- 1 file changed, 12 insertions(+), 13 deletions(-) diff --git a/lib/codemirror.js b/lib/codemirror.js index df514190d2..7277b8bd0d 100644 --- a/lib/codemirror.js +++ b/lib/codemirror.js @@ -96,21 +96,20 @@ registerEventHandlers(this); ensureGlobalHandlers(); - var cm = this; - runInOp(this, function() { - cm.curOp.forceUpdate = true; - attachDoc(cm, doc); + startOperation(this); + this.curOp.forceUpdate = true; + attachDoc(this, doc); - if ((options.autofocus && !mobile) || activeElt() == display.input) - setTimeout(bind(onFocus, cm), 20); - else - onBlur(cm); + if ((options.autofocus && !mobile) || activeElt() == display.input) + setTimeout(bind(onFocus, this), 20); + else + onBlur(this); - for (var opt in optionHandlers) if (optionHandlers.hasOwnProperty(opt)) - optionHandlers[opt](cm, options[opt], Init); - maybeUpdateLineNumberWidth(cm); - for (var i = 0; i < initHooks.length; ++i) initHooks[i](cm); - }); + for (var opt in optionHandlers) if (optionHandlers.hasOwnProperty(opt)) + optionHandlers[opt](this, options[opt], Init); + maybeUpdateLineNumberWidth(this); + for (var i = 0; i < initHooks.length; ++i) initHooks[i](this); + endOperation(this); } // DISPLAY CONSTRUCTOR From 833ccba1d5d0b8d226ccc100b9b9eb6e854f3836 Mon Sep 17 00:00:00 2001 From: Marijn Haverbeke Date: Thu, 16 Oct 2014 17:51:41 +0200 Subject: [PATCH 2659/5780] Add simple mode addon --- addon/mode/simple.js | 200 +++++++++++++++++++++++++++++++++++++++++++ demo/simplemode.html | 172 +++++++++++++++++++++++++++++++++++++ doc/compress.html | 1 + doc/manual.html | 5 ++ 4 files changed, 378 insertions(+) create mode 100644 addon/mode/simple.js create mode 100644 demo/simplemode.html diff --git a/addon/mode/simple.js b/addon/mode/simple.js new file mode 100644 index 0000000000..7299eaeb34 --- /dev/null +++ b/addon/mode/simple.js @@ -0,0 +1,200 @@ +// CodeMirror, copyright (c) by Marijn Haverbeke and others +// Distributed under an MIT license: http://codemirror.net/LICENSE + +(function(mod) { + if (typeof exports == "object" && typeof module == "object") // CommonJS + mod(require("../../lib/codemirror")); + else if (typeof define == "function" && define.amd) // AMD + define(["../../lib/codemirror"], mod); + else // Plain browser env + mod(CodeMirror); +})(function(CodeMirror) { + "use strict"; + + CodeMirror.defineSimpleMode = function(name, states, props) { + CodeMirror.defineMode(name, function(config) { + return CodeMirror.simpleMode(config, states, props); + }); + }; + + CodeMirror.simpleMode = function(config, states) { + ensureState(states, "start"); + var states_ = {}, meta = states.meta || {}, hasIndentation = false; + for (var state in states) if (state != meta && states.hasOwnProperty(state)) { + var list = states_[state] = [], orig = states[state]; + for (var i = 0; i < orig.length; i++) { + var data = orig[i]; + list.push(new Rule(data, states)); + if (data.indent || data.dedent) hasIndentation = true; + } + } + var mode = { + startState: function() { + return {state: "start", pending: null, + local: null, localState: null, + indent: hasIndentation ? [] : null}; + }, + copyState: function(state) { + var s = {state: state.state, pending: state.pending, + local: state.local, localState: null, + indent: state.indent && state.indent.slice(0)}; + if (state.localState) + s.localState = CodeMirror.copyState(state.local.mode, state.localState); + for (var pers = state.persistentStates; pers; pers = pers.next) + s.persistentStates = {mode: pers.mode, + spec: pers.spec, + state: pers.state == state.localState ? s.localState : CodeMirror.copyState(pers.mode, pers.state), + next: s.persistentStates}; + return s; + }, + token: tokenFunction(states_, config), + innerMode: function(state) { return state.local && {mode: state.local.mode, state: state.localState}; }, + indent: indentFunction(states_, meta) + }; + if (meta) for (var prop in meta) if (meta.hasOwnProperty(prop)) + mode[prop] = meta[prop]; + return mode; + }; + + function ensureState(states, name) { + if (!states.hasOwnProperty(name)) + throw new Error("Undefined state " + name + "in simple mode"); + } + + function toRegex(val, caret) { + if (!val) return /(?:)/; + var flags = ""; + if (val instanceof RegExp) { + if (val.ignoreCase) flags = "i"; + val = val.source; + } else { + val = String(val); + } + return new RegExp((caret === false ? "" : "^") + "(?:" + val + ")", flags); + } + + function asToken(val) { + if (!val) return null; + if (typeof val == "string") return val.replace(/\./g, " "); + var result = []; + for (var i = 0; i < val.length; i++) + result.push(val[i] && val[i].replace(/\./g, " ")); + return result; + } + + function Rule(data, states) { + if (data.next) ensureState(states, data.next); + this.regex = toRegex(data.regex); + this.token = asToken(data.token); + this.data = data; + } + + function tokenFunction(states, config) { + return function(stream, state) { + if (state.pending) { + var pend = state.pending.shift(); + if (state.pending.length == 0) state.pending = null; + stream.pos += pend.text.length; + return pend.token; + } + + if (state.local) { + if (state.local.end && stream.match(state.local.end)) { + var tok = state.local.endToken || null; + state.local = state.localState = null; + return tok; + } else { + var tok = state.local.mode.token(stream, state.localState), m; + if (state.local.endScan && (m = state.local.endScan.exec(stream.current()))) + stream.pos = stream.start + m.index; + return tok; + } + } + + var curState = states[state.state]; + for (var i = 0; i < curState.length; i++) { + var rule = curState[i]; + var matches = stream.match(rule.regex); + if (matches) { + if (rule.data.next) + state.state = rule.data.next; + if (rule.data.mode) + enterLocalMode(config, state, rule.data.mode, rule.token); + if (rule.data.indent) + state.indent.push(stream.indentation() + config.indentUnit); + if (rule.data.dedent) + state.indent.pop(); + if (matches.length > 2) { + state.pending = []; + for (var j = 2; j < matches.length; j++) + state.pending.push({text: matches[j], token: rule.token[j - 1]}); + stream.backUp(matches[0].length - matches[1].length); + return rule.token[0]; + } else if (rule.token && rule.token.join) { + return rule.token[0]; + } else { + return rule.token; + } + } + } + stream.next(); + return null; + }; + } + + function cmp(a, b) { + if (a === b) return true; + if (!a || typeof a != "object" || !b || typeof b != "object") return false; + var props = 0; + for (var prop in a) if (a.hasOwnProperty(prop)) { + if (!b.hasOwnProperty(prop) || !cmp(a[prop], b[prop])) return false; + props++; + } + for (var prop in b) if (b.hasOwnProperty(prop)) props--; + return props == 0; + } + + function enterLocalMode(config, state, spec, token) { + var pers; + if (spec.persistent) for (var p = state.persistentStates; p && !pers; p = p.next) + if (spec.spec ? cmp(spec.spec, p.spec) : spec.mode == p.mode) pers = p; + var mode = pers ? pers.mode : spec.mode || CodeMirror.getMode(config, spec.spec); + var lState = pers ? pers.state : CodeMirror.startState(mode); + if (spec.persistent && !pers) + state.persistentStates = {mode: mode, spec: spec.spec, state: lState, next: state.persistentStates}; + + state.localState = lState; + state.local = {mode: mode, + end: spec.end && toRegex(spec.end), + endScan: spec.end && spec.forceEnd !== false && toRegex(spec.end, false), + endToken: token && token.join ? token[token.length - 1] : token}; + } + + function indexOf(val, arr) { + for (var i = 0; i < arr.length; i++) if (arr[i] === val) return true; + } + + function indentFunction(states, meta) { + return function(state, textAfter, line) { + if (state.local && state.local.mode.indent) + return state.local.mode.indent(state.localState, textAfter, line); + if (state.indent == null || state.local || meta.dontIndentStates && indexOf(state.state, meta.dontIndentStates) > -1) + return CodeMirror.Pass; + + var pos = state.indent.length - 1, rules = states[state.state]; + scan: for (;;) { + for (var i = 0; i < rules.length; i++) { + var rule = rules[i], m = rule.regex.exec(textAfter); + if (m) { + if (rule.data.dedent && rule.data.dedentIfLineStart !== false) pos--; + if (rule.next) rules = states[rule.next]; + textAfter = textAfter.slice(m[0].length); + continue scan; + } + } + break; + } + return pos < 0 ? 0 : state.indent[pos]; + }; + } +}); diff --git a/demo/simplemode.html b/demo/simplemode.html new file mode 100644 index 0000000000..4b8de8cded --- /dev/null +++ b/demo/simplemode.html @@ -0,0 +1,172 @@ + + +CodeMirror: Simple Mode Demo + + + + + + + + + +

    + +
    +

    Simple Mode Demo

    + +

    The mode/simple +addon allows CodeMirror modes to be specified with a relatively simple +declarative format. This format is not as powerful as writing code +directly against the mode +interface, but is a lot easier to get started with, and +sufficiently expressive for many simple language modes.

    + +

    This interface is still in flux. It is unlikely to be scrapped or +overhauled completely, so do start writing code against it, but +details might change as it stabilizes, and you might have to tweak +your code when upgrading.

    + +

    Simple modes (loosely based on +the Common +JavaScript Syntax Highlighting Specification, which never took +off), are state machines, where each state has a number of rules that +match tokens. A rule describes a type of token that may occur in the +current state, and possibly a transition to another state caused by +that token.

    + +

    The CodeMirror.defineSimpleMode(name, states) method +takes a mode name and an object that describes the mode's states. The +editor below shows an example of such a mode (and is itself +highlighted by the mode shown in it).

    + +
    + +

    Each state is an array of rules. A rule may have the following properties:

    + +
    +
    regex
    +
    The regular expression that matches the token. May be a string + or a regex object. When a regex, the ignoreCase flag + will be taken into account when matching the token. This regex + should only capture groups when the token property is + an array.
    +
    token
    +
    An optional token style. Multiple styles can be specified by + separating them with dots or spaces. When the regex for + this rule captures groups, it must capture all of the + string (since JS provides no way to find out where a group matched), + and this property must hold an array of token styles that has one + style for each matched group.
    +
    next
    +
    When a next property is present, the mode will + transfer to another state when the token is encountered.
    +
    mode
    +
    Can be used to embed another mode inside a mode. When present, + must hold an object with a spec property that describes + the embedded mode, and an optional end end property + that specifies the regexp that will end the extent of the mode. When + a persistent property is set (and true), the nested + mode's state will be preserved between occurrences of the mode.
    +
    indent
    +
    When true, this token changes the indentation to be one unit + more than the current line's indentation.
    +
    dedent
    +
    When true, this token will pop one scope off the indentation + stack.
    +
    dedentIfLineStart
    +
    If a token has its dedent property set, it will, by + default, cause lines where it appears at the start to be dedented. + Set this property to false to prevent that behavior.
    +
    + +

    The meta property of the states object is special, and +will not be interpreted as a state. Instead, properties set on it will +be set on the mode, which is useful for properties +like lineComment, +which sets the comment style for a mode. The simple mode addon also +recognizes a few such properties:

    + +
    +
    dontIndentStates
    +
    An array of states in which the mode's auto-indentation should + not take effect. Usually used for multi-line comment and string + states.
    +
    + + + + + +
    diff --git a/doc/compress.html b/doc/compress.html index 19639903a3..940465259d 100644 --- a/doc/compress.html +++ b/doc/compress.html @@ -219,6 +219,7 @@

    Script compression helper

    + diff --git a/doc/manual.html b/doc/manual.html index fccf7d6416..f89cc4b3fd 100644 --- a/doc/manual.html +++ b/doc/manual.html @@ -2715,6 +2715,11 @@

    Writing CodeMirror Modes

    advances it past a token, and returns a style for that token. More advanced modes can also handle indentation for the language.

    +

    This section describes the low-level mode interface. Many modes + are written directly against this, since it offers a lot of + control, but for a quick mode definition, you might want to use + the simple mode addon.

    +

    The mode script should call CodeMirror.defineMode to register itself with CodeMirror. This function takes two From eee65eef5a258fef405d6880566213f5acf9924c Mon Sep 17 00:00:00 2001 From: Marijn Haverbeke Date: Thu, 16 Oct 2014 17:53:19 +0200 Subject: [PATCH 2660/5780] [simple mode demo] Slight rewording --- demo/simplemode.html | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/demo/simplemode.html b/demo/simplemode.html index 4b8de8cded..9218b04247 100644 --- a/demo/simplemode.html +++ b/demo/simplemode.html @@ -32,7 +32,7 @@

    Simple Mode Demo

    The mode/simple -addon allows CodeMirror modes to be specified with a relatively simple +addon allows CodeMirror modes to be specified using a relatively simple declarative format. This format is not as powerful as writing code directly against the mode interface, but is a lot easier to get started with, and From a9acabd88b1930b2e5d4470cb3a6ce2a20ffa5db Mon Sep 17 00:00:00 2001 From: Marijn Haverbeke Date: Fri, 17 Oct 2014 00:27:53 +0200 Subject: [PATCH 2661/5780] Switch to new logo --- demo/activeline.html | 2 +- demo/anywordhint.html | 2 +- demo/bidi.html | 2 +- demo/btree.html | 2 +- demo/buffers.html | 2 +- demo/changemode.html | 2 +- demo/closebrackets.html | 2 +- demo/closetag.html | 2 +- demo/complete.html | 2 +- demo/emacs.html | 2 +- demo/folding.html | 2 +- demo/fullscreen.html | 2 +- demo/hardwrap.html | 2 +- demo/html5complete.html | 2 +- demo/indentwrap.html | 2 +- demo/lint.html | 2 +- demo/loadmode.html | 2 +- demo/marker.html | 2 +- demo/markselection.html | 2 +- demo/matchhighlighter.html | 2 +- demo/matchtags.html | 2 +- demo/merge.html | 2 +- demo/multiplex.html | 2 +- demo/mustache.html | 2 +- demo/placeholder.html | 2 +- demo/preview.html | 2 +- demo/requirejs.html | 2 +- demo/resize.html | 2 +- demo/rulers.html | 2 +- demo/runmode.html | 2 +- demo/search.html | 2 +- demo/simplemode.html | 2 +- demo/spanaffectswrapping_shim.html | 2 +- demo/sublime.html | 2 +- demo/tern.html | 2 +- demo/theme.html | 2 +- demo/trailingspace.html | 2 +- demo/variableheight.html | 2 +- demo/vim.html | 2 +- demo/visibletabs.html | 2 +- demo/widget.html | 2 +- demo/xmlcomplete.html | 2 +- doc/compress.html | 2 +- doc/docs.css | 13 +- doc/internals.html | 2 +- doc/logo.png | Bin 12003 -> 9730 bytes doc/logo.svg | 279 ++++++++++++++++------------- doc/manual.html | 2 +- doc/realworld.html | 2 +- doc/releases.html | 2 +- doc/reporting.html | 2 +- doc/upgrade_v2.2.html | 2 +- doc/upgrade_v3.html | 2 +- doc/upgrade_v4.html | 2 +- index.html | 2 +- mode/apl/index.html | 2 +- mode/asterisk/index.html | 2 +- mode/clike/index.html | 2 +- mode/clike/scala.html | 2 +- mode/clojure/index.html | 2 +- mode/cobol/index.html | 2 +- mode/coffeescript/index.html | 2 +- mode/commonlisp/index.html | 2 +- mode/css/index.html | 2 +- mode/css/less.html | 2 +- mode/css/scss.html | 2 +- mode/cypher/index.html | 2 +- mode/d/index.html | 2 +- mode/diff/index.html | 2 +- mode/django/index.html | 2 +- mode/dtd/index.html | 2 +- mode/dylan/index.html | 2 +- mode/ecl/index.html | 2 +- mode/eiffel/index.html | 2 +- mode/erlang/index.html | 2 +- mode/fortran/index.html | 2 +- mode/gas/index.html | 2 +- mode/gfm/index.html | 2 +- mode/gherkin/index.html | 2 +- mode/go/index.html | 2 +- mode/groovy/index.html | 2 +- mode/haml/index.html | 2 +- mode/haskell/index.html | 2 +- mode/haxe/index.html | 2 +- mode/htmlembedded/index.html | 2 +- mode/htmlmixed/index.html | 2 +- mode/http/index.html | 2 +- mode/index.html | 2 +- mode/jade/index.html | 2 +- mode/javascript/index.html | 2 +- mode/javascript/json-ld.html | 2 +- mode/javascript/typescript.html | 2 +- mode/jinja2/index.html | 2 +- mode/julia/index.html | 2 +- mode/kotlin/index.html | 2 +- mode/livescript/index.html | 2 +- mode/lua/index.html | 2 +- mode/markdown/index.html | 2 +- mode/mirc/index.html | 2 +- mode/mllike/index.html | 2 +- mode/modelica/index.html | 2 +- mode/nginx/index.html | 2 +- mode/ntriples/index.html | 2 +- mode/octave/index.html | 2 +- mode/pascal/index.html | 2 +- mode/pegjs/index.html | 2 +- mode/perl/index.html | 2 +- mode/php/index.html | 2 +- mode/pig/index.html | 2 +- mode/properties/index.html | 2 +- mode/puppet/index.html | 2 +- mode/python/index.html | 2 +- mode/q/index.html | 2 +- mode/r/index.html | 2 +- mode/rpm/changes/index.html | 2 +- mode/rpm/index.html | 2 +- mode/rst/index.html | 2 +- mode/ruby/index.html | 2 +- mode/rust/index.html | 2 +- mode/sass/index.html | 2 +- mode/scheme/index.html | 2 +- mode/shell/index.html | 2 +- mode/sieve/index.html | 2 +- mode/slim/index.html | 2 +- mode/smalltalk/index.html | 2 +- mode/smarty/index.html | 2 +- mode/smartymixed/index.html | 2 +- mode/solr/index.html | 2 +- mode/sparql/index.html | 2 +- mode/sql/index.html | 2 +- mode/stex/index.html | 2 +- mode/tcl/index.html | 2 +- mode/textile/index.html | 2 +- mode/tiddlywiki/index.html | 2 +- mode/tiki/index.html | 2 +- mode/toml/index.html | 2 +- mode/tornado/index.html | 2 +- mode/turtle/index.html | 2 +- mode/vb/index.html | 2 +- mode/vbscript/index.html | 2 +- mode/velocity/index.html | 2 +- mode/verilog/index.html | 2 +- mode/xml/index.html | 2 +- mode/xquery/index.html | 2 +- mode/yaml/index.html | 2 +- mode/z80/index.html | 2 +- test/index.html | 2 +- 147 files changed, 311 insertions(+), 269 deletions(-) diff --git a/demo/activeline.html b/demo/activeline.html index c1391ef27e..741f6c45a4 100644 --- a/demo/activeline.html +++ b/demo/activeline.html @@ -12,7 +12,7 @@ .CodeMirror {border-top: 1px solid black; border-bottom: 1px solid black;}