From 5e75c0a8679e5195a45d506de3461fd8109e7764 Mon Sep 17 00:00:00 2001 From: Gabriel Luong Date: Tue, 26 Apr 2016 11:44:18 -0400 Subject: [PATCH] Bug 1266267 - Upgrade to CodeMirror 5.14.2 r=bgrins --- .../client/sourceeditor/codemirror/README | 2 +- .../codemirror/addon/comment/comment.js | 11 +++- .../codemirror/addon/hint/show-hint.js | 63 ++++++++----------- .../addon/search/match-highlighter.js | 51 +++++++-------- .../codemirror/addon/search/search.js | 7 ++- .../codemirror/addon/tern/tern.js | 4 +- .../sourceeditor/codemirror/keymap/sublime.js | 2 + .../sourceeditor/codemirror/keymap/vim.js | 34 +++++----- .../sourceeditor/codemirror/lib/codemirror.js | 17 ++--- .../sourceeditor/codemirror/mode/clike.js | 2 +- .../sourceeditor/test/codemirror/vim_test.js | 16 +++-- 11 files changed, 111 insertions(+), 98 deletions(-) mode change 100755 => 100644 devtools/client/sourceeditor/codemirror/addon/search/search.js diff --git a/devtools/client/sourceeditor/codemirror/README b/devtools/client/sourceeditor/codemirror/README index e204f193df69..52ab5037c58a 100644 --- a/devtools/client/sourceeditor/codemirror/README +++ b/devtools/client/sourceeditor/codemirror/README @@ -5,7 +5,7 @@ code, and optionally help with indentation. # Upgrade -Currently used version is 5.13.2. To upgrade, download a new version of +Currently used version is 5.14.2. To upgrade, download a new version of CodeMirror from the project's page [1] and replace all JavaScript and CSS files inside the codemirror directory [2]. diff --git a/devtools/client/sourceeditor/codemirror/addon/comment/comment.js b/devtools/client/sourceeditor/codemirror/addon/comment/comment.js index 3aa468089e8a..2c4f975d08f5 100644 --- a/devtools/client/sourceeditor/codemirror/addon/comment/comment.js +++ b/devtools/client/sourceeditor/codemirror/addon/comment/comment.js @@ -44,9 +44,17 @@ } }); + // Rough heuristic to try and detect lines that are part of multi-line string + function probablyInsideString(cm, pos, line) { + return /\bstring\b/.test(cm.getTokenTypeAt(Pos(pos.line, 0))) && !/^[\'\"`]/.test(line) + } + CodeMirror.defineExtension("lineComment", function(from, to, options) { if (!options) options = noOptions; var self = this, mode = self.getModeAt(from); + var firstLine = self.getLine(from.line); + if (firstLine == null || probablyInsideString(self, from, firstLine)) return; + var commentString = options.lineComment || mode.lineComment; if (!commentString) { if (options.blockCommentStart || mode.blockCommentStart) { @@ -55,8 +63,7 @@ } return; } - 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; var blankLines = options.commentBlankLines || from.line == to.line; diff --git a/devtools/client/sourceeditor/codemirror/addon/hint/show-hint.js b/devtools/client/sourceeditor/codemirror/addon/hint/show-hint.js index 7661f6c2cd3a..f426b5c8ca03 100644 --- a/devtools/client/sourceeditor/codemirror/addon/hint/show-hint.js +++ b/devtools/client/sourceeditor/codemirror/addon/hint/show-hint.js @@ -108,15 +108,11 @@ }, update: function(first) { - if (this.tick == null) return; - if (!this.options.hint.async) { - this.finishUpdate(this.options.hint(this.cm, this.options), first); - } else { - var myTick = ++this.tick, self = this; - this.options.hint(this.cm, function(data) { - if (self.tick == myTick) self.finishUpdate(data, first); - }, this.options); - } + if (this.tick == null) return + var self = this, myTick = ++this.tick + fetchHints(this.options.hint, this.cm, this.options, function(data) { + if (self.tick == myTick) self.finishUpdate(data, first) + }) }, finishUpdate: function(data, first) { @@ -362,40 +358,31 @@ return result } + function fetchHints(hint, cm, options, callback) { + if (hint.async) { + hint(cm, callback, options) + } else { + var result = hint(cm, options) + if (result && result.then) result.then(callback) + else callback(result) + } + } + function resolveAutoHints(cm, pos) { var helpers = cm.getHelpers(pos, "hint"), words if (helpers.length) { - var async = false, resolved - for (var i = 0; i < helpers.length; i++) if (helpers[i].async) async = true - if (async) { - resolved = function(cm, callback, options) { - var app = applicableHelpers(cm, helpers) - function run(i, result) { - if (i == app.length) return callback(null) - var helper = app[i] - if (helper.async) { - helper(cm, function(result) { - if (result) callback(result) - else run(i + 1) - }, options) - } else { - var result = helper(cm, options) - if (result) callback(result) - else run(i + 1) - } - } - run(0) - } - resolved.async = true - } else { - resolved = function(cm, options) { - var app = applicableHelpers(cm, helpers) - for (var i = 0; i < app.length; i++) { - var cur = app[i](cm, options) - if (cur && cur.list.length) return cur - } + var resolved = function(cm, callback, options) { + var app = applicableHelpers(cm, helpers); + function run(i) { + if (i == app.length) return callback(null) + fetchHints(app[i], cm, options, function(result) { + if (result && result.list.length > 0) callback(result) + else run(i + 1) + }) } + run(0) } + resolved.async = true resolved.supportsSelection = true return resolved } else if (words = cm.getHelper(cm.getCursor(), "hintWords")) { diff --git a/devtools/client/sourceeditor/codemirror/addon/search/match-highlighter.js b/devtools/client/sourceeditor/codemirror/addon/search/match-highlighter.js index 8f02f01c873d..2c2914a9d0b6 100644 --- a/devtools/client/sourceeditor/codemirror/addon/search/match-highlighter.js +++ b/devtools/client/sourceeditor/codemirror/addon/search/match-highlighter.js @@ -16,7 +16,7 @@ // 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. If annotateScrollbar is enabled, the occurances +// highlighting the matches. If annotateScrollbar is enabled, the occurences // will be highlighted on the scrollbar via the matchesonscrollbar addon. (function(mod) { @@ -29,24 +29,20 @@ })(function(CodeMirror) { "use strict"; - var DEFAULT_MIN_CHARS = 2; - var DEFAULT_TOKEN_STYLE = "matchhighlight"; - var DEFAULT_DELAY = 100; - var DEFAULT_WORDS_ONLY = false; + var defaults = { + style: "matchhighlight", + minChars: 2, + delay: 100, + wordsOnly: false, + annotateScrollbar: false, + showToken: false, + trim: true + } function State(options) { - if (typeof options == "object") { - this.minChars = options.minChars; - this.style = options.style; - this.showToken = options.showToken; - this.delay = options.delay; - this.wordsOnly = options.wordsOnly; - this.annotateScrollbar = options.annotateScrollbar; - } - 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.options = {} + for (var name in defaults) + this.options[name] = (options && options.hasOwnProperty(name) ? options : defaults)[name] this.overlay = this.timeout = null; this.matchesonscroll = null; } @@ -68,13 +64,13 @@ function cursorActivity(cm) { var state = cm.state.matchHighlighter; clearTimeout(state.timeout); - state.timeout = setTimeout(function() {highlightMatches(cm);}, state.delay); + state.timeout = setTimeout(function() {highlightMatches(cm);}, state.options.delay); } function addOverlay(cm, query, hasBoundary, style) { var state = cm.state.matchHighlighter; cm.addOverlay(state.overlay = makeOverlay(query, hasBoundary, style)); - if (state.annotateScrollbar) { + if (state.options.annotateScrollbar && cm.showMatchesOnScrollbar) { var searchFor = hasBoundary ? new RegExp("\\b" + query + "\\b") : query; state.matchesonscroll = cm.showMatchesOnScrollbar(searchFor, true, {className: "CodeMirror-selection-highlight-scrollbar"}); @@ -86,7 +82,7 @@ if (state.overlay) { cm.removeOverlay(state.overlay); state.overlay = null; - if (state.annotateScrollbar) { + if (state.matchesonscroll) { state.matchesonscroll.clear(); state.matchesonscroll = null; } @@ -97,21 +93,22 @@ cm.operation(function() { var state = cm.state.matchHighlighter; removeOverlay(cm); - if (!cm.somethingSelected() && state.showToken) { - var re = state.showToken === true ? /[\w$]/ : state.showToken; + if (!cm.somethingSelected() && state.options.showToken) { + var re = state.options.showToken === true ? /[\w$]/ : state.options.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) - addOverlay(cm, line.slice(start, end), re, state.style); + addOverlay(cm, line.slice(start, end), re, state.options.style); return; } 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) - addOverlay(cm, selection, false, state.style); + if (state.options.wordsOnly && !isWord(cm, from, to)) return; + var selection = cm.getRange(from, to) + if (state.options.trim) selection = selection.replace(/^\s+|\s+$/g, "") + if (selection.length >= state.options.minChars) + addOverlay(cm, selection, false, state.options.style); }); } diff --git a/devtools/client/sourceeditor/codemirror/addon/search/search.js b/devtools/client/sourceeditor/codemirror/addon/search/search.js old mode 100755 new mode 100644 index 935a14a1dbf7..b303a5abb111 --- a/devtools/client/sourceeditor/codemirror/addon/search/search.js +++ b/devtools/client/sourceeditor/codemirror/addon/search/search.js @@ -136,7 +136,10 @@ persistentDialog(cm, queryDialog, q, function(query, event) { CodeMirror.e_stop(event); if (!query) return; - if (query != state.queryText) startSearch(cm, state, query); + if (query != state.queryText) { + startSearch(cm, state, query); + state.posFrom = state.posTo = cm.getCursor(); + } if (hiding) hiding.style.opacity = 1 findNext(cm, event.shiftKey, function(_, to) { var dialog @@ -208,7 +211,7 @@ replaceAll(cm, query, text) } else { clearSearch(cm); - var cursor = getSearchCursor(cm, query, cm.getCursor()); + var cursor = getSearchCursor(cm, query, cm.getCursor("from")); var advance = function() { var start = cursor.from(), match; if (!(match = cursor.findNext())) { diff --git a/devtools/client/sourceeditor/codemirror/addon/tern/tern.js b/devtools/client/sourceeditor/codemirror/addon/tern/tern.js index c345c49781eb..efdf2ed628a4 100644 --- a/devtools/client/sourceeditor/codemirror/addon/tern/tern.js +++ b/devtools/client/sourceeditor/codemirror/addon/tern/tern.js @@ -179,7 +179,7 @@ var data = findDoc(ts, doc); var argHints = ts.cachedArgHints; - if (argHints && argHints.doc == doc && cmpPos(argHints.start, change.to) <= 0) + if (argHints && argHints.doc == doc && cmpPos(argHints.start, change.to) >= 0) ts.cachedArgHints = null; var changed = data.changed; @@ -306,7 +306,7 @@ 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, + start: start, type: parseFnType(data.type), name: data.exprName || data.name || "fn", guess: data.guess, diff --git a/devtools/client/sourceeditor/codemirror/keymap/sublime.js b/devtools/client/sourceeditor/codemirror/keymap/sublime.js index 49db3b871c90..3cf67413abd9 100644 --- a/devtools/client/sourceeditor/codemirror/keymap/sublime.js +++ b/devtools/client/sourceeditor/codemirror/keymap/sublime.js @@ -55,6 +55,8 @@ cmds[map["Alt-Left"] = "goSubwordLeft"] = function(cm) { moveSubword(cm, -1); }; cmds[map["Alt-Right"] = "goSubwordRight"] = function(cm) { moveSubword(cm, 1); }; + if (mac) map["Cmd-Left"] = "goLineStartSmart"; + var scrollLineCombo = mac ? "Ctrl-Alt-" : "Ctrl-"; cmds[map[scrollLineCombo + "Up"] = "scrollLineUp"] = function(cm) { diff --git a/devtools/client/sourceeditor/codemirror/keymap/vim.js b/devtools/client/sourceeditor/codemirror/keymap/vim.js index 5ed2d9bf7385..b2bee5a1c7ec 100644 --- a/devtools/client/sourceeditor/codemirror/keymap/vim.js +++ b/devtools/client/sourceeditor/codemirror/keymap/vim.js @@ -26,7 +26,7 @@ * 2. Variable declarations and short basic helpers * 3. Instance (External API) implementation * 4. Internal state tracking objects (input state, counter) implementation - * and instanstiation + * and instantiation * 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 @@ -226,6 +226,7 @@ { name: 'sort', shortName: 'sor' }, { name: 'substitute', shortName: 's', possiblyAsync: true }, { name: 'nohlsearch', shortName: 'noh' }, + { name: 'yank', shortName: 'y' }, { name: 'delmarks', shortName: 'delm' }, { name: 'registers', shortName: 'reg', excludeFromCommandHistory: true }, { name: 'global', shortName: 'g' } @@ -641,7 +642,7 @@ jumpList: createCircularJumpList(), macroModeState: new MacroModeState, // Recording latest f, t, F or T motion command. - lastChararacterSearch: {increment:0, forward:true, selectedCharacter:''}, + lastCharacterSearch: {increment:0, forward:true, selectedCharacter:''}, registerController: new RegisterController({}), // search history buffer searchHistoryController: new HistoryController({}), @@ -1047,7 +1048,7 @@ }; function HistoryController() { this.historyBuffer = []; - this.iterator; + this.iterator = 0; this.initialPrefix = null; } HistoryController.prototype = { @@ -1372,7 +1373,7 @@ } }, evalInput: function(cm, vim) { - // If the motion comand is set, execute both the operator and motion. + // If the motion command is set, execute both the operator and motion. // Otherwise return. var inputState = vim.inputState; var motion = inputState.motion; @@ -1909,7 +1910,7 @@ }, repeatLastCharacterSearch: function(cm, head, motionArgs) { - var lastSearch = vimGlobalState.lastChararacterSearch; + var lastSearch = vimGlobalState.lastCharacterSearch; var repeat = motionArgs.repeat; var forward = motionArgs.forward === lastSearch.forward; var increment = (lastSearch.increment ? 1 : 0) * (forward ? -1 : 1); @@ -3002,7 +3003,7 @@ // Only clip if the selection ends with trailing newline + whitespace if (/\n\s*$/.test(selection)) { var lines = selection.split('\n'); - // We know this is all whitepsace. + // We know this is all whitespace. lines.pop(); // Cases: @@ -3088,9 +3089,9 @@ } function recordLastCharacterSearch(increment, args) { - vimGlobalState.lastChararacterSearch.increment = increment; - vimGlobalState.lastChararacterSearch.forward = args.forward; - vimGlobalState.lastChararacterSearch.selectedCharacter = args.selectedCharacter; + vimGlobalState.lastCharacterSearch.increment = increment; + vimGlobalState.lastCharacterSearch.forward = args.forward; + vimGlobalState.lastCharacterSearch.selectedCharacter = args.selectedCharacter; } var symbolToMode = { @@ -3289,8 +3290,6 @@ line = cm.getLine(lineNum); pos = (dir > 0) ? 0 : line.length; } - // Should never get here. - throw new Error('The impossible happened.'); } /** @@ -3452,7 +3451,7 @@ } // TODO: perhaps this finagling of start and end positions belonds - // in codmirror/replaceRange? + // in codemirror/replaceRange? function selectCompanionObject(cm, head, symb, inclusive) { var cur = head, start, end; @@ -4514,14 +4513,21 @@ 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. + } else if (cm.save) { + // Saves to text area if no save command is defined and cm.save() is available. cm.save(); } }, nohlsearch: function(cm) { clearSearchHighlight(cm); }, + yank: function (cm) { + var cur = copyCursor(cm.getCursor()); + var line = cur.line; + var lineText = cm.getLine(line); + vimGlobalState.registerController.pushText( + '0', 'yank', lineText, true, true); + }, delmarks: function(cm, params) { if (!params.argString || !trim(params.argString)) { showConfirm(cm, 'Argument required'); diff --git a/devtools/client/sourceeditor/codemirror/lib/codemirror.js b/devtools/client/sourceeditor/codemirror/lib/codemirror.js index ec49e3a33806..aa0d3b4ffbce 100644 --- a/devtools/client/sourceeditor/codemirror/lib/codemirror.js +++ b/devtools/client/sourceeditor/codemirror/lib/codemirror.js @@ -41,6 +41,7 @@ // This is woefully incomplete. Suggestions for alternative methods welcome. var mobile = ios || /Android|webOS|BlackBerry|Opera Mini|Opera Mobi|IEMobile/i.test(userAgent); var mac = ios || /Mac/.test(platform); + var chromeOS = /\bCrOS\b/.test(userAgent); var windows = /win/i.test(platform); var presto_version = presto && userAgent.match(/Version\/(\d*\.\d*)/); @@ -3680,7 +3681,7 @@ ourIndex = doc.sel.primIndex; } - if (e.altKey) { + if (chromeOS ? e.shiftKey && e.metaKey : e.altKey) { type = "rect"; if (!addNew) ourRange = new Range(start, start); start = posFromMouse(cm, e, true, true); @@ -3905,6 +3906,7 @@ if (signalDOMEvent(cm, e) || eventInWidget(cm.display, e)) return; e.dataTransfer.setData("Text", cm.getSelection()); + e.dataTransfer.effectAllowed = "copyMove" // 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. @@ -7625,9 +7627,9 @@ var spans = line.markedSpans; if (spans) for (var i = 0; i < spans.length; i++) { var span = spans[i]; - if (!(span.to != null && lineNo == from.line && from.ch > span.to || + if (!(span.to != null && lineNo == from.line && from.ch >= span.to || span.from == null && lineNo != from.line || - span.from != null && lineNo == to.line && span.from > to.ch) && + span.from != null && lineNo == to.line && span.from >= to.ch) && (!filter || filter(span.marker))) found.push(span.marker.parent || span.marker); } @@ -7646,9 +7648,9 @@ }, posFromIndex: function(off) { - var ch, lineNo = this.first; + var ch, lineNo = this.first, sepSize = this.lineSeparator().length; this.iter(function(line) { - var sz = line.text.length + 1; + var sz = line.text.length + sepSize; if (sz > off) { ch = off; return true; } off -= sz; ++lineNo; @@ -7659,8 +7661,9 @@ coords = clipPos(this, coords); var index = coords.ch; if (coords.line < this.first || coords.ch < 0) return 0; + var sepSize = this.lineSeparator().length; this.iter(this.first, coords.line, function (line) { - index += line.text.length + 1; + index += line.text.length + sepSize; }); return index; }, @@ -8889,7 +8892,7 @@ // THE END - CodeMirror.version = "5.13.2"; + CodeMirror.version = "5.14.2"; return CodeMirror; }); diff --git a/devtools/client/sourceeditor/codemirror/mode/clike.js b/devtools/client/sourceeditor/codemirror/mode/clike.js index 34d3a5afd3a9..695d5ceff3a0 100644 --- a/devtools/client/sourceeditor/codemirror/mode/clike.js +++ b/devtools/client/sourceeditor/codemirror/mode/clike.js @@ -667,7 +667,7 @@ CodeMirror.defineMode("clike", function(config, parserConfig) { def("text/x-objectivec", { name: "clike", - keywords: words(cKeywords + "inline restrict _Bool _Complex _Imaginery BOOL Class bycopy byref id IMP in " + + keywords: words(cKeywords + "inline restrict _Bool _Complex _Imaginary BOOL Class bycopy byref id IMP in " + "inout nil oneway out Protocol SEL self super atomic nonatomic retain copy readwrite readonly"), types: words(cTypes), atoms: words("YES NO NULL NILL ON OFF true false"), diff --git a/devtools/client/sourceeditor/test/codemirror/vim_test.js b/devtools/client/sourceeditor/test/codemirror/vim_test.js index 7080c3062f37..fb612b1407f9 100644 --- a/devtools/client/sourceeditor/test/codemirror/vim_test.js +++ b/devtools/client/sourceeditor/test/codemirror/vim_test.js @@ -273,10 +273,10 @@ testJumplist('jumplist_repeat_', ['*', '*', '*', '3', '', '2', '' 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', +testJumplist('jumplist_skip_deleted_mark', ['*', 'n', 'n', 'k', 'd', 'k', '', '', ''], [0,2], [0,2]); -testJumplist('jumplist_skip_delted_mark', +testJumplist('jumplist_skip_deleted_mark', ['*', 'n', 'n', 'k', 'd', 'k', '', '', ''], [1,0], [0,2]); @@ -482,7 +482,7 @@ testVim('gj_gk', function(cm, vim, helpers) { 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.' }); +},{ lineNumbers: false, lineWrapping:true, value: 'Thislineisintentionallylongtotestmovementofgjandgkoverwrappedlines.' }); testVim('}', function(cm, vim, helpers) { cm.setCursor(0, 0); helpers.doKeys('}'); @@ -3682,7 +3682,7 @@ function testSubstituteConfirm(name, command, initialValue, expectedValue, keys, } catch(e) { throw e } finally { - // Restore overriden functions. + // Restore overridden functions. CodeMirror.keyName = savedKeyName; cm.openDialog = savedOpenDialog; } @@ -3725,6 +3725,14 @@ testVim('ex_noh_clearSearchHighlight', function(cm, vim, helpers) { helpers.doKeys('n'); helpers.assertCursorAt(0, 11,'can\'t resume search after clearing highlighting'); }, { value: 'match nope match \n nope Match' }); +testVim('ex_yank', function (cm, vim, helpers) { + var curStart = makeCursor(3, 0); + cm.setCursor(curStart); + helpers.doEx('y'); + var register = helpers.getRegisterController().getRegister(); + var line = cm.getLine(3); + eq(line + '\n', register.toString()); +}); testVim('set_boolean', function(cm, vim, helpers) { CodeMirror.Vim.defineOption('testoption', true, 'boolean'); // Test default value is set.