From 5a6f158eedf354acd88f66f1d09c9b221fc636d8 Mon Sep 17 00:00:00 2001 From: Stu Kennedy Date: Wed, 11 Nov 2015 23:42:25 +0000 Subject: [PATCH 0001/1790] [show-hint addon] Change selected item in hint options on mousmove Moving the mouse will change the selected hint option --- addon/hint/show-hint.js | 6 ++++++ 1 file changed, 6 insertions(+) diff --git a/addon/hint/show-hint.js b/addon/hint/show-hint.js index a1e56c38..b359208d 100644 --- a/addon/hint/show-hint.js +++ b/addon/hint/show-hint.js @@ -295,6 +295,12 @@ setTimeout(function(){cm.focus();}, 20); }); + CodeMirror.on(hints, "mousemove", function(e) { + var elt = getHintElement(hints, e.target || e.srcElement); + if (elt && elt.hintId != null) + widget.changeActive(elt.hintId); + }); + CodeMirror.signal(data, "select", completions[0], hints.firstChild); return true; } From 1de8132e2cefbe34043508dea747fffdfb90a2bc Mon Sep 17 00:00:00 2001 From: Marijn Haverbeke Date: Thu, 12 Nov 2015 11:35:18 +0100 Subject: [PATCH 0002/1790] [show-hint addon] Only follow mouse with focus when completeOnSingleClick is on Make this the default Issue #3636 --- addon/hint/show-hint.js | 13 +++++++------ 1 file changed, 7 insertions(+), 6 deletions(-) diff --git a/addon/hint/show-hint.js b/addon/hint/show-hint.js index b359208d..7eefad8b 100644 --- a/addon/hint/show-hint.js +++ b/addon/hint/show-hint.js @@ -295,11 +295,12 @@ setTimeout(function(){cm.focus();}, 20); }); - CodeMirror.on(hints, "mousemove", function(e) { - var elt = getHintElement(hints, e.target || e.srcElement); - if (elt && elt.hintId != null) - widget.changeActive(elt.hintId); - }); + if (completion.options.completeOnSingleClick) + CodeMirror.on(hints, "mousemove", function(e) { + var elt = getHintElement(hints, e.target || e.srcElement); + if (elt && elt.hintId != null) + widget.changeActive(elt.hintId); + }); CodeMirror.signal(data, "select", completions[0], hints.firstChild); return true; @@ -436,7 +437,7 @@ alignWithWord: true, closeCharacters: /[\s()\[\]{};:>,]/, closeOnUnfocus: true, - completeOnSingleClick: false, + completeOnSingleClick: true, container: null, customKeys: null, extraKeys: null From 7530191700a67477d5866e1d606a4d4b8d4072b6 Mon Sep 17 00:00:00 2001 From: mihailik Date: Thu, 12 Nov 2015 15:59:28 +0000 Subject: [PATCH 0003/1790] [markdown mode] Fix typo --- mode/markdown/markdown.js | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/mode/markdown/markdown.js b/mode/markdown/markdown.js index 2349ddf2..97dfb746 100644 --- a/mode/markdown/markdown.js +++ b/mode/markdown/markdown.js @@ -712,7 +712,7 @@ CodeMirror.defineMode("markdown", function(cmCfg, modeCfg) { f: s.f, prevLine: s.prevLine, - thisLine: s.this, + thisLine: s.thisLine, block: s.block, htmlState: s.htmlState && CodeMirror.copyState(htmlMode, s.htmlState), From 8e0660682d4171fc203867f7acdcb94ea77c80c5 Mon Sep 17 00:00:00 2001 From: Marijn Haverbeke Date: Tue, 17 Nov 2015 05:16:48 +0100 Subject: [PATCH 0004/1790] [javascript mode] Recognize regexp after case and new keywords Closes ##3648 --- mode/javascript/javascript.js | 3 +-- 1 file changed, 1 insertion(+), 2 deletions(-) diff --git a/mode/javascript/javascript.js b/mode/javascript/javascript.js index b961b890..0cbc36d7 100644 --- a/mode/javascript/javascript.js +++ b/mode/javascript/javascript.js @@ -121,8 +121,7 @@ CodeMirror.defineMode("javascript", function(config, parserConfig) { } else if (stream.eat("/")) { stream.skipToEnd(); return ret("comment", "comment"); - } else if (state.lastType == "operator" || state.lastType == "keyword c" || - state.lastType == "sof" || /^[\[{}\(,;:]$/.test(state.lastType)) { + } else if (/^(?:operator|sof|keyword c|case|new|[\[{}\(,;:])$/.test(state.lastType)) { readRegexp(stream); stream.match(/^\b(([gimyu])(?![gimyu]*\2))+\b/); return ret("regexp", "string-2"); From cedae180f76cec84dbed6ab530f80ec136054b99 Mon Sep 17 00:00:00 2001 From: satamas Date: Mon, 16 Nov 2015 20:25:41 +0300 Subject: [PATCH 0005/1790] [kotlin mode] Improve string tokenizing, add keywords --- mode/clike/clike.js | 29 +++++++++++++++++++++++++++-- 1 file changed, 27 insertions(+), 2 deletions(-) diff --git a/mode/clike/clike.js b/mode/clike/clike.js index 566aa048..0cbe2fae 100644 --- a/mode/clike/clike.js +++ b/mode/clike/clike.js @@ -535,19 +535,36 @@ CodeMirror.defineMode("clike", function(config, parserConfig) { modeProps: {closeBrackets: {triples: '"'}} }); + function tokenKotlinString(tripleString){ + return function (stream, state) { + var escaped = false, next, end = false; + while (!stream.eol()) { + if (!tripleString && !escaped && stream.match('"') ) {end = true; break;} + if (tripleString && stream.match('"""')) {end = true; break;} + next = stream.next(); + if(!escaped && next == "$" && stream.match('{')) + stream.skipTo("}"); + escaped = !escaped && next == "\\" && !tripleString; + } + if (end || !tripleString) + state.tokenize = null; + return "string"; + } + } + def("text/x-kotlin", { name: "clike", keywords: words( /*keywords*/ "package as typealias class interface this super val " + "var fun for is in This throw return " + - "break continue object if else while do try when !in !is as?" + + "break continue object if else while do try when !in !is as? " + /*soft keywords*/ "file import where by get set abstract enum open inner override private public internal " + "protected catch finally out final vararg reified dynamic companion constructor init " + "sealed field property receiver param sparam lateinit data inline noinline tailrec " + - "external annotation crossinline" + "external annotation crossinline const operator infix" ), types: words( /* package java.lang */ @@ -556,10 +573,18 @@ CodeMirror.defineMode("clike", function(config, parserConfig) { "Runtime Runnable SecurityManager Short StackTraceElement StrictMath String " + "StringBuffer System Thread ThreadGroup ThreadLocal Throwable Triple Void" ), + intendSwitch: false, + indentStatements: false, multiLineStrings: true, blockKeywords: words("catch class do else finally for if where try while enum"), defKeywords: words("class val var object package interface fun"), atoms: words("true false null this"), + hooks: { + '"': function(stream, state) { + state.tokenize = tokenKotlinString(stream.match('""')); + return state.tokenize(stream, state); + } + }, modeProps: {closeBrackets: {triples: '"'}} }); From e477745492b75e1851689cd4832f7e16f7a94335 Mon Sep 17 00:00:00 2001 From: Michael Goderbauer Date: Sat, 14 Nov 2015 02:58:07 -0500 Subject: [PATCH 0006/1790] [dart mode] add "as" to keywords `import "package:foo/foo.dart" as bar;` should highlight "as" as a keyword. --- mode/dart/dart.js | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/mode/dart/dart.js b/mode/dart/dart.js index b1ee3776..88bb2779 100644 --- a/mode/dart/dart.js +++ b/mode/dart/dart.js @@ -15,7 +15,7 @@ "implements get native operator set typedef with enum throw rethrow " + "assert break case continue default in return new deferred async await " + "try catch finally do else for if switch while import library export " + - "part of show hide is").split(" "); + "part of show hide is as").split(" "); var blockKeywords = "try catch finally do else for if switch while".split(" "); var atoms = "true false null".split(" "); var builtins = "void bool num int double dynamic var String".split(" "); From a1e780ba7a6dce48781c93f998e6544fae657280 Mon Sep 17 00:00:00 2001 From: Michael Goderbauer Date: Sat, 14 Nov 2015 03:12:06 -0500 Subject: [PATCH 0007/1790] [dart mode] in annotations "." is legal `@ng1.Injectable` is a legal annotation --- mode/dart/dart.js | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/mode/dart/dart.js b/mode/dart/dart.js index 88bb2779..d92eb519 100644 --- a/mode/dart/dart.js +++ b/mode/dart/dart.js @@ -46,7 +46,7 @@ atoms: set(atoms), hooks: { "@": function(stream) { - stream.eatWhile(/[\w\$_]/); + stream.eatWhile(/[\w\$_\.]/); return "meta"; }, From 0216c3a753ec7dc8b594bb9bf155ea28bb422575 Mon Sep 17 00:00:00 2001 From: Marijn Haverbeke Date: Wed, 18 Nov 2015 16:13:07 +0100 Subject: [PATCH 0008/1790] [javascript mode] Allow spread in object literals Closes #3650 --- mode/javascript/javascript.js | 2 ++ 1 file changed, 2 insertions(+) diff --git a/mode/javascript/javascript.js b/mode/javascript/javascript.js index 0cbc36d7..26e1693c 100644 --- a/mode/javascript/javascript.js +++ b/mode/javascript/javascript.js @@ -464,6 +464,8 @@ CodeMirror.defineMode("javascript", function(config, parserConfig) { return cont(afterprop); } else if (type == "[") { return cont(expression, expect("]"), afterprop); + } else if (type == "spread") { + return cont(expression); } } function getterSetter(type) { From 5bf7669e2fe96ffd46ebf46584c3a25d1e4d5ef8 Mon Sep 17 00:00:00 2001 From: Kayur Patel Date: Wed, 18 Nov 2015 00:46:30 -0800 Subject: [PATCH 0009/1790] [vim keymap] Fix JSDoc comment According to http://usejsdoc.org/about-getting-started.html, jsdoc comments must start with /** --- keymap/vim.js | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/keymap/vim.js b/keymap/vim.js index 14897a84..02ed53af 100644 --- a/keymap/vim.js +++ b/keymap/vim.js @@ -3208,7 +3208,7 @@ 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 * the cursor. If the cursor is at the start/end of a word, and we are going From 160d3f64d4998b5677c7c79e7f333c8ad4caf479 Mon Sep 17 00:00:00 2001 From: Marijn Haverbeke Date: Thu, 19 Nov 2015 00:21:36 +0100 Subject: [PATCH 0010/1790] Initialize Doc.extend property in constructor Closes #3652 --- lib/codemirror.js | 1 + 1 file changed, 1 insertion(+) diff --git a/lib/codemirror.js b/lib/codemirror.js index ff2f1a54..f97998ed 100644 --- a/lib/codemirror.js +++ b/lib/codemirror.js @@ -7358,6 +7358,7 @@ this.id = ++nextDocId; this.modeOption = mode; this.lineSep = lineSep; + this.extend = false; if (typeof text == "string") text = this.splitLines(text); updateDoc(this, {from: start, to: start, text: text}); From be4c5f3cb32757f47c402d3cfa85faf14e184dae Mon Sep 17 00:00:00 2001 From: Marijn Haverbeke Date: Fri, 20 Nov 2015 21:32:44 +0100 Subject: [PATCH 0011/1790] [python mode] Fix parsing of properties Clean up weirdly specified regexps Closes #3641 --- mode/python/python.js | 45 ++++++++++++++++--------------------------- 1 file changed, 17 insertions(+), 28 deletions(-) diff --git a/mode/python/python.js b/mode/python/python.js index e5a09719..553f2d6f 100644 --- a/mode/python/python.js +++ b/mode/python/python.js @@ -48,18 +48,18 @@ CodeMirror.defineMode("python", function(conf, parserConf) { var ERRORCLASS = "error"; - 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 singleDelimiters = parserConf.singleDelimiters || /^[\(\)\[\]\{\}@,:`=;\.]/; + var doubleOperators = parserConf.doubleOperators || /^([!<>]==|<>|<<|>>|\/\/|\*\*)/; + var doubleDelimiters = parserConf.doubleDelimiters || /^(\+=|\-=|\*=|%=|\/=|&=|\|=|\^=)/; + var tripleDelimiters = parserConf.tripleDelimiters || /^(\/\/=|>>=|<<=|\*\*=)/; if (parserConf.version && parseInt(parserConf.version, 10) == 3){ // since http://legacy.python.org/dev/peps/pep-0465/ @ is also an operator - var singleOperators = parserConf.singleOperators || new RegExp("^[\\+\\-\\*/%&|\\^~<>!@]"); - var identifiers = parserConf.identifiers|| new RegExp("^[_A-Za-z\u00A1-\uFFFF][_A-Za-z0-9\u00A1-\uFFFF]*"); + var singleOperators = parserConf.singleOperators || /^[\+\-\*\/%&|\^~<>!@]/; + var identifiers = parserConf.identifiers|| /^[_A-Za-z\u00A1-\uFFFF][_A-Za-z0-9\u00A1-\uFFFF]*/; } else { - var singleOperators = parserConf.singleOperators || new RegExp("^[\\+\\-\\*/%&|\\^~<>!]"); - var identifiers = parserConf.identifiers|| new RegExp("^[_A-Za-z][_A-Za-z0-9]*"); + var singleOperators = parserConf.singleOperators || /^[\+\-\*\/%&|\^~<>!]/; + var identifiers = parserConf.identifiers|| /^[_A-Za-z][_A-Za-z0-9]*/; } var hangingIndent = parserConf.hangingIndent || conf.indentUnit; @@ -160,13 +160,16 @@ // Handle operators and Delimiters if (stream.match(tripleDelimiters) || stream.match(doubleDelimiters)) - return null; + return "punctuation"; if (stream.match(doubleOperators) || stream.match(singleOperators)) return "operator"; if (stream.match(singleDelimiters)) - return null; + return "punctuation"; + + if (state.lastToken == "." && stream.match(identifiers)) + return "property"; if (stream.match(keywords) || stream.match(wordOperators)) return "keyword"; @@ -246,17 +249,6 @@ var style = state.tokenize(stream, state); var current = stream.current(); - // Handle '.' connected identifiers - if (current == ".") { - style = stream.match(identifiers, false) ? null : ERRORCLASS; - if (style == null && state.lastStyle == "meta") { - // Apply 'meta' style to '.' connected identifiers when - // appropriate. - style = "meta"; - } - return style; - } - // Handle decorators if (current == "@"){ if(parserConf.version && parseInt(parserConf.version, 10) == 3){ @@ -267,7 +259,7 @@ } if ((style == "variable" || style == "builtin") - && state.lastStyle == "meta") + && state.lastToken == "meta") style = "meta"; // Handle scope changes. @@ -300,7 +292,6 @@ return { tokenize: tokenBase, scopes: [{offset: basecolumn || 0, type: "py", align: null}], - lastStyle: null, lastToken: null, lambda: false, dedent: 0 @@ -312,11 +303,9 @@ if (addErr) state.errorToken = false; var style = tokenLexer(stream, state); - state.lastStyle = style; - - var current = stream.current(); - if (current && style) - state.lastToken = current; + if (style && style != "comment") + state.lastToken = (style == "keyword" || style == "punctuation") ? stream.current() : style; + if (style == "punctuation") style = null; if (stream.eol() && state.lambda) state.lambda = false; From 2a84b591b06470ad2063e2f2f03516eca2e4b42c Mon Sep 17 00:00:00 2001 From: Josh Cohen Date: Sun, 15 Nov 2015 23:57:24 -0500 Subject: [PATCH 0012/1790] [haml mode] Ignore {% liquid tags The old behavior sees the closing %} as a haml tag begin. The result is that the rest of the file after the %} doesnt highlight correctly. Dont go into quote mode when stream is '{%'. --- mode/haml/haml.js | 6 ++++-- 1 file changed, 4 insertions(+), 2 deletions(-) diff --git a/mode/haml/haml.js b/mode/haml/haml.js index 8fe63b02..03ce8335 100644 --- a/mode/haml/haml.js +++ b/mode/haml/haml.js @@ -85,8 +85,10 @@ state.tokenize = rubyInQuote(")"); return state.tokenize(stream, state); } else if (ch == "{") { - state.tokenize = rubyInQuote("}"); - return state.tokenize(stream, state); + if (!stream.match(/^\{%.*/)) { + state.tokenize = rubyInQuote("}"); + return state.tokenize(stream, state); + } } } From 88071b32ffd90b7ea50df965e7f511850ba4c8b1 Mon Sep 17 00:00:00 2001 From: Marijn Haverbeke Date: Sat, 21 Nov 2015 12:43:29 +0100 Subject: [PATCH 0013/1790] [comment-fold addon] Ignore block comment start strings inside comments Closes #3651 --- addon/fold/comment-fold.js | 4 +++- 1 file changed, 3 insertions(+), 1 deletion(-) diff --git a/addon/fold/comment-fold.js b/addon/fold/comment-fold.js index b75db7ea..60fa3e43 100644 --- a/addon/fold/comment-fold.js +++ b/addon/fold/comment-fold.js @@ -28,7 +28,9 @@ CodeMirror.registerGlobalHelper("fold", "comment", function(mode) { continue; } if (pass == 1 && found < start.ch) return; - if (/comment/.test(cm.getTokenTypeAt(CodeMirror.Pos(line, found + 1)))) { + if (/comment/.test(cm.getTokenTypeAt(CodeMirror.Pos(line, found + 1))) && + (lineText.slice(found - endToken.length, found) == endToken || + !/comment/.test(cm.getTokenTypeAt(CodeMirror.Pos(line, found))))) { startCh = found + startToken.length; break; } From 9024f7cc61fcfd6b1490b9ee52045a2c7d0e7b99 Mon Sep 17 00:00:00 2001 From: Marijn Haverbeke Date: Sat, 21 Nov 2015 12:59:44 +0100 Subject: [PATCH 0014/1790] [javascript mode] Revert "attempt to fix parsing when async/await are present" This reverts commit 94fc3b71e8e9f52b9239276a34658023933aac68. --- mode/javascript/javascript.js | 9 +++------ 1 file changed, 3 insertions(+), 6 deletions(-) diff --git a/mode/javascript/javascript.js b/mode/javascript/javascript.js index 26e1693c..993d3eaf 100644 --- a/mode/javascript/javascript.js +++ b/mode/javascript/javascript.js @@ -32,12 +32,12 @@ CodeMirror.defineMode("javascript", function(config, parserConfig) { "if": kw("if"), "while": A, "with": A, "else": B, "do": B, "try": B, "finally": B, "return": C, "break": C, "continue": C, "new": kw("new"), "delete": C, "throw": C, "debugger": C, "var": kw("var"), "const": kw("var"), "let": kw("var"), - "async": kw("async"), "function": kw("function"), "catch": kw("catch"), + "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, "this": kw("this"), "class": kw("class"), "super": kw("atom"), - "await": C, "yield": C, "export": kw("export"), "import": kw("import"), "extends": C + "yield": C, "export": kw("export"), "import": kw("import"), "extends": C }; // Extend the 'normal' keywords with the TypeScript language extensions @@ -372,7 +372,6 @@ CodeMirror.defineMode("javascript", function(config, parserConfig) { var maybeop = noComma ? maybeoperatorNoComma : maybeoperatorComma; if (atomicTypes.hasOwnProperty(type)) return cont(maybeop); - if (type == "async") return cont(expression); if (type == "function") return cont(functiondef, maybeop); if (type == "keyword c") return cont(noComma ? maybeexpressionNoComma : maybeexpression); if (type == "(") return cont(pushlex(")"), maybeexpression, comprehension, expect(")"), poplex, maybeop); @@ -451,9 +450,7 @@ CodeMirror.defineMode("javascript", function(config, parserConfig) { if (type == "variable") {cx.marked = "property"; return cont();} } function objprop(type, value) { - if (type == "async") { - return cont(objprop); - } else if (type == "variable" || cx.style == "keyword") { + if (type == "variable" || cx.style == "keyword") { cx.marked = "property"; if (value == "get" || value == "set") return cont(getterSetter); return cont(afterprop); From 446d89def9aabe93f4030ab446546598b7ea2df6 Mon Sep 17 00:00:00 2001 From: Sergey Goder Date: Fri, 20 Nov 2015 19:02:33 -0800 Subject: [PATCH 0015/1790] [sql mode] Add `limit` to default SQL keyords --- 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 a9082771..86c68f72 100644 --- a/mode/sql/sql.js +++ b/mode/sql/sql.js @@ -257,7 +257,7 @@ CodeMirror.defineMode("sql", function(config, parserConfig) { } // these keywords are used by all SQL dialects (however, a mode can still overwrite it) - var sqlKeywords = "alter and as asc between by count create delete desc distinct drop from group having in insert into is join like not on or order select set table union update values where "; + var sqlKeywords = "alter and as asc between by count create delete desc distinct drop from group having in insert into is join like not on or order select set table union update values where limit"; // turn a space-separated list into an array function set(str) { From daad4a601ad1ffb35f4607bd49cdc3249cb5dda1 Mon Sep 17 00:00:00 2001 From: Marijn Haverbeke Date: Mon, 23 Nov 2015 10:53:41 +0100 Subject: [PATCH 0016/1790] [rust mode] Support multiline strings Issue #3660 --- mode/rust/rust.js | 19 ++++++++++++++++--- 1 file changed, 16 insertions(+), 3 deletions(-) diff --git a/mode/rust/rust.js b/mode/rust/rust.js index 1ce0c01e..8558b53f 100644 --- a/mode/rust/rust.js +++ b/mode/rust/rust.js @@ -12,11 +12,12 @@ "use strict"; CodeMirror.defineSimpleMode("rust",{ - start:[ + start: [ // string and byte string - {regex: /b?"(?:[^\\]|\\.)*?"/, token: "string"}, + {regex: /b?"/, token: "string", next: "string"}, // raw string and raw byte string - {regex: /(b?r)(#*)(".*?)("\2)/, token: ["string", "string", "string", "string"]}, + {regex: /b?r"/, token: "string", next: "string_raw"}, + {regex: /b?r#+"/, token: "string", next: "string_raw_hash"}, // character {regex: /'(?:[^'\\]|\\(?:[nrt0'"]|x[\da-fA-F]{2}|u\{[\da-fA-F]{6}\}))'/, token: "string-2"}, // byte @@ -39,6 +40,18 @@ CodeMirror.defineSimpleMode("rust",{ {regex: /[\{\[\(]/, indent: true}, {regex: /[\}\]\)]/, dedent: true} ], + string: [ + {regex: /"/, token: "string", next: "start"}, + {regex: /(?:[^\\"]|\\(?:.|$))*/, token: "string"} + ], + string_raw: [ + {regex: /"/, token: "string", next: "start"}, + {regex: /[^"]*/, token: "string"} + ], + string_raw_hash: [ + {regex: /"#+/, token: "string", next: "start"}, + {regex: /(?:[^"]|"(?!#))*/, token: "string"} + ], comment: [ {regex: /.*?\*\//, token: "comment", next: "start"}, {regex: /.*/, token: "comment"} From a29e51697083c3cd6678f5bdda459a447321f3aa Mon Sep 17 00:00:00 2001 From: Marijn Haverbeke Date: Mon, 23 Nov 2015 10:57:41 +0100 Subject: [PATCH 0017/1790] [handlebars and nsis modes] Do tokenize mismatched quotes as strings So that autoclosing works. Issue #3660 --- mode/handlebars/handlebars.js | 4 ++-- mode/nsis/nsis.js | 6 +++--- 2 files changed, 5 insertions(+), 5 deletions(-) diff --git a/mode/handlebars/handlebars.js b/mode/handlebars/handlebars.js index 40dfea42..d6d9f5b0 100644 --- a/mode/handlebars/handlebars.js +++ b/mode/handlebars/handlebars.js @@ -21,8 +21,8 @@ { regex: /\}\}/, pop: true, token: "tag" }, // Double and single quotes - { regex: /"(?:[^\\]|\\.)*?"/, token: "string" }, - { regex: /'(?:[^\\]|\\.)*?'/, token: "string" }, + { regex: /"(?:[^\\"]|\\.)*"?/, token: "string" }, + { regex: /'(?:[^\\']|\\.)*'?/, token: "string" }, // Handlebars keywords { regex: />|[#\/]([A-Za-z_]\w*)/, token: "keyword" }, diff --git a/mode/nsis/nsis.js b/mode/nsis/nsis.js index 4c73fb73..172207c5 100644 --- a/mode/nsis/nsis.js +++ b/mode/nsis/nsis.js @@ -19,9 +19,9 @@ CodeMirror.defineSimpleMode("nsis",{ {regex: /(?:[+-]?)(?:0x[\d,a-f]+)|(?:0o[0-7]+)|(?:0b[0,1]+)|(?:\d+.?\d*)/, token: "number"}, // Strings - { regex: /"(?:[^\\]|\\.)*?"/, token: "string" }, - { regex: /'(?:[^\\]|\\.)*?'/, token: "string" }, - { regex: /`(?:[^\\]|\\.)*?`/, token: "string" }, + { regex: /"(?:[^\\"]|\\.)*"?/, token: "string" }, + { regex: /'(?:[^\\']|\\.)*'?/, token: "string" }, + { regex: /`(?:[^\\`]|\\.)*`?/, token: "string" }, // Compile Time Commands {regex: /(?:\!(include|addincludedir|addplugindir|appendfile|cd|delfile|echo|error|execute|packhdr|finalize|getdllversion|system|tempfile|warning|verbose|define|undef|insertmacro|makensis|searchparse|searchreplace))\b/, token: "keyword"}, From 2e3a2a1134cd4c5f92f21fb762c80c9a3e8f332d Mon Sep 17 00:00:00 2001 From: Marijn Haverbeke Date: Mon, 23 Nov 2015 11:12:10 +0100 Subject: [PATCH 0018/1790] [rust mode] Adjust tests to new handling of strings --- mode/rust/test.js | 1 - 1 file changed, 1 deletion(-) diff --git a/mode/rust/test.js b/mode/rust/test.js index 1a3c6e72..eb256c47 100644 --- a/mode/rust/test.js +++ b/mode/rust/test.js @@ -26,7 +26,6 @@ '[string "\\"foo\\""]', '[string r#""foo""#]', '[string "foo #\\"# bar"]', - '[string r##"foo #"# bar"##]', '[string b"foo"]', '[string br"foo"]', From 10b0c73e45a2f23f2b34871e5b8b00bcc2fe54c1 Mon Sep 17 00:00:00 2001 From: Marijn Haverbeke Date: Mon, 23 Nov 2015 11:47:09 +0100 Subject: [PATCH 0019/1790] Support adding multiple css strings to a token using markText --- lib/codemirror.js | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/lib/codemirror.js b/lib/codemirror.js index f97998ed..249f40ee 100644 --- a/lib/codemirror.js +++ b/lib/codemirror.js @@ -7089,7 +7089,7 @@ spanEndStyle = ""; } if (m.className) spanStyle += " " + m.className; - if (m.css) css = m.css; + if (m.css) css = (css ? css + ";" : "") + m.css; 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; From ec73f71ee6d04cc5c257f098bfcf693460be1866 Mon Sep 17 00:00:00 2001 From: Marijn Haverbeke Date: Mon, 23 Nov 2015 12:22:30 +0100 Subject: [PATCH 0020/1790] Mark release 5.9.0 --- AUTHORS | 7 +++++++ doc/compress.html | 1 + doc/manual.html | 2 +- doc/releases.html | 11 +++++++++++ index.html | 2 +- lib/codemirror.js | 2 +- package.json | 2 +- 7 files changed, 23 insertions(+), 4 deletions(-) diff --git a/AUTHORS b/AUTHORS index 1e3ece23..4f060641 100644 --- a/AUTHORS +++ b/AUTHORS @@ -91,6 +91,7 @@ Brian Sletten Bruce Mitchener Caitlin Potter Calin Barbat +Chad Jolly Chandra Sekhar Pydi Charles Skelton Cheah Chu Yeow @@ -233,6 +234,7 @@ jeffkenton Jeff Pickhardt jem (graphite) Jeremy Parmenter +JobJob Jochen Berger Johan Ask John Connor @@ -247,6 +249,7 @@ Jon Malmaud Jon Sangster Joost-Wim Boekesteijn Joseph Pecoraro +Josh Cohen Joshua Newman Josh Watzman jots @@ -259,6 +262,7 @@ Justin Hileman jwallers@gmail.com kaniga karevn +Kayur Patel Ken Newman Ken Rockot Kevin Earls @@ -437,6 +441,7 @@ SCLINIC\jdecker Scott Aikin Scott Goodhew Sebastian Zaha +Sergey Goder Se-Won Kim shaund shaun gilchrist @@ -459,12 +464,14 @@ Steffen Beyer Stephen Lavelle Steve O'Hara stoskov +Stu Kennedy Sungho Kim sverweij Taha Jahangir Tako Schotanus Takuji Shimokawa Tarmil +TDaglis tel tfjgeorge Thaddee Tyl diff --git a/doc/compress.html b/doc/compress.html index 6a183ca5..7712274c 100644 --- a/doc/compress.html +++ b/doc/compress.html @@ -36,6 +36,7 @@

Script compression helper

Version: (Use line:column or scroll% syntax)'; + + function jumpToLine(cm) { + var cur = cm.getCursor(); + dialog(cm, jumpDialog, 'Jump to line:', (cur.line+1)+':'+(cur.ch+1), function(posStr) { + if (!posStr) return; + + var clnMatch = /^\s*([\+\-]?\d+)\s*\:\s*(\d+)\s*$/.exec(posStr); + var prcMatch = /^\s*([\+\-]?\d+(\.\d+)?)\%\s*/.exec(posStr); + var lnMatch = /^\s*\:?\s*([\+\-]?\d+)\s*/.exec(posStr); + if (clnMatch) { + try { + var line = parseInt(clnMatch[1]); + var ch = parseInt(clnMatch[2]); + if ('+-'.indexOf(clnMatch[1].charAt(0))>=0) + line = cur.line+line+1; + } + catch (error) { return; } + cm.setCursor(line-1, ch-1); + } + else if (prcMatch) { + try { + var prc = parseFloat(prcMatch[1]); + var line = Math.round(cm.lineCount()*prc/100); + if ('+-'.indexOf(prcMatch[1].charAt(0))>=0) + line = cur.line+line+1; + } + catch (error) { return; } + cm.setCursor(line-1, cur.ch); + } + else if (lnMatch) { + try { + var line = parseInt(lnMatch[1]); + if ('+-'.indexOf(lnMatch[1].charAt(0))>=0) + line = cur.line+line+1; + } + catch (error) { return; } + cm.setCursor(line-1, cur.ch); + } + }) + } + + CodeMirror.commands.jumpToLine = jumpToLine; + CodeMirror.keyMap.default["Alt-G"] = "jumpToLine"; +}); diff --git a/demo/search.html b/demo/search.html index 21c34251..fd445db2 100644 --- a/demo/search.html +++ b/demo/search.html @@ -14,6 +14,7 @@ + - - -

CodeMirror: msgenny mode

- -
- - - -

MIME types defined: text/x-msgenny

- - diff --git a/mode/mscgen/index_xu.html b/mode/mscgen/index_xu.html deleted file mode 100644 index 2f7bf9ec..00000000 --- a/mode/mscgen/index_xu.html +++ /dev/null @@ -1,70 +0,0 @@ - - - - - CodeMirror: xu mode - - - - - - -

CodeMirror: xù mode

- -
- - - -

MIME types defined: text/x-xu

- - From 382f51d257112027d147f583d14d6880618fd3da Mon Sep 17 00:00:00 2001 From: sverweij Date: Sat, 5 Dec 2015 19:55:08 +0100 Subject: [PATCH 0042/1790] [mscgen mode] simplifies the regexps to recognize keywords for our purposes (=>|<=|...) works just as well as ((<=)|(=>)|...) --- mode/mscgen/mscgen.js | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/mode/mscgen/mscgen.js b/mode/mscgen/mscgen.js index 3cf4eb08..d61b4706 100644 --- a/mode/mscgen/mscgen.js +++ b/mode/mscgen/mscgen.js @@ -69,11 +69,11 @@ CodeMirror.defineMIME("text/x-msgenny", {name: "mscgen", language: "msgenny"}); function wordRegexpBoundary(pWords) { - return new RegExp("\\b((" + pWords.join(")|(") + "))\\b", "i"); + return new RegExp("\\b(" + pWords.join("|") + ")\\b", "i"); } function wordRegexp(pWords) { - return new RegExp("((" + pWords.join(")|(") + "))", "i"); + return new RegExp("(" + pWords.join("|") + ")", "i"); } function startStateFn() { From 764022a60ee7c092e18c9a45f3678d6b52852a00 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Nicol=C3=B2=20Ribaudo?= Date: Sat, 5 Dec 2015 22:05:27 +0100 Subject: [PATCH 0043/1790] [javascript mode] Recognize regex inside template literal Closes gh-3687 --- 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 e97593a2..d4ae6687 100644 --- a/mode/javascript/javascript.js +++ b/mode/javascript/javascript.js @@ -126,7 +126,8 @@ CodeMirror.defineMode("javascript", function(config, parserConfig) { } else if (stream.eat("/")) { stream.skipToEnd(); return ret("comment", "comment"); - } else if (/^(?:operator|sof|keyword c|case|new|[\[{}\(,;:])$/.test(state.lastType)) { + } else if (/^(?:operator|sof|keyword c|case|new|[\[{}\(,;:])$/.test(state.lastType) || + (state.lastType == "quasi" && /\{\s*$/.test(stream.string.slice(0, stream.pos - 1)))) { readRegexp(stream); stream.match(/^\b(([gimyu])(?![gimyu]*\2))+\b/); return ret("regexp", "string-2"); From 9399b1cdee43d648fd8649c15220afdc33fc3ec0 Mon Sep 17 00:00:00 2001 From: Marijn Haverbeke Date: Mon, 7 Dec 2015 15:43:13 +0100 Subject: [PATCH 0044/1790] [vim bindings] Use a simpler way to handle splitting on dashes --- keymap/vim.js | 7 +------ 1 file changed, 1 insertion(+), 6 deletions(-) diff --git a/keymap/vim.js b/keymap/vim.js index 02ed53af..c1532a17 100644 --- a/keymap/vim.js +++ b/keymap/vim.js @@ -292,12 +292,7 @@ // Keypress character binding of format "'a'" return key.charAt(1); } - var pieces = key.split('-'); - if (/-$/.test(key)) { - // If the - key was typed, split will result in 2 extra empty strings - // in the array. Replace them with 1 '-'. - pieces.splice(-2, 2, '-'); - } + var pieces = key.split(/-(?!$)/); var lastPiece = pieces[pieces.length - 1]; if (pieces.length == 1 && pieces[0].length == 1) { // No-modifier bindings use literal character bindings above. Skip. From 4bdf6440429bc455f4ce39486d424fba0be7fa14 Mon Sep 17 00:00:00 2001 From: mihailik Date: Sat, 28 Nov 2015 15:55:34 +0000 Subject: [PATCH 0045/1790] Fixing hidding keyboard on arrow keys on mobile Makes sure the selection is not entirely cleared, which causs the on-screen keyboard to get hidden. Disabled for Firefox, where this causes other problems. Issue #3653 --- lib/codemirror.js | 9 +++++++-- 1 file changed, 7 insertions(+), 2 deletions(-) diff --git a/lib/codemirror.js b/lib/codemirror.js index 88c47bba..5bfa91d6 100644 --- a/lib/codemirror.js +++ b/lib/codemirror.js @@ -1688,8 +1688,13 @@ try { var rng = range(start.node, start.offset, end.offset, end.node); } catch(e) {} // Our model of the DOM might be outdated, in which case the range we try to set can be impossible if (rng) { - sel.removeAllRanges(); - sel.addRange(rng); + if (!gecko && this.cm.state.focused) { + sel.collapse(start.node, start.offset); + if (!rng.collapsed) sel.addRange(rng); + } else { + sel.removeAllRanges(); + sel.addRange(rng); + } if (old && sel.anchorNode == null) sel.addRange(old); else if (gecko) this.startGracePeriod(); } From b3010eddf829821ef279ce80a73047b6e6db6011 Mon Sep 17 00:00:00 2001 From: Marijn Haverbeke Date: Sun, 13 Dec 2015 00:07:29 +0100 Subject: [PATCH 0046/1790] [css mode] Clean up handling of inline option --- mode/css/css.js | 7 +++---- 1 file changed, 3 insertions(+), 4 deletions(-) diff --git a/mode/css/css.js b/mode/css/css.js index b20b4907..2673074a 100644 --- a/mode/css/css.js +++ b/mode/css/css.js @@ -12,9 +12,8 @@ "use strict"; CodeMirror.defineMode("css", function(config, parserConfig) { - var provided = parserConfig; + var inline = parserConfig.inline if (!parserConfig.propertyKeywords) parserConfig = CodeMirror.resolveMode("text/css"); - parserConfig.inline = provided.inline; var indentUnit = config.indentUnit, tokenHooks = parserConfig.tokenHooks, @@ -368,9 +367,9 @@ CodeMirror.defineMode("css", function(config, parserConfig) { return { startState: function(base) { return {tokenize: null, - state: parserConfig.inline ? "block" : "top", + state: inline ? "block" : "top", stateArg: null, - context: new Context(parserConfig.inline ? "block" : "top", base || 0, null)}; + context: new Context(inline ? "block" : "top", base || 0, null)}; }, token: function(stream, state) { From 0dbe0ef55d50734818325bbbc211f47c21c3c0ed Mon Sep 17 00:00:00 2001 From: TDaglis Date: Tue, 15 Dec 2015 11:39:14 +0000 Subject: [PATCH 0047/1790] [django mode] better highlighting of in/and/or/not Currently operators in most themes look like normal text because cm-operator doesn't usually get special styling. This patch highlights word operators (in/and/or/not) to make them stand out better. --- mode/django/django.js | 9 ++++++++- 1 file changed, 8 insertions(+), 1 deletion(-) diff --git a/mode/django/django.js b/mode/django/django.js index 7fae876c..eb8d6591 100644 --- a/mode/django/django.js +++ b/mode/django/django.js @@ -35,11 +35,13 @@ "truncatechars_html", "truncatewords", "truncatewords_html", "unordered_list", "upper", "urlencode", "urlize", "urlizetrunc", "wordcount", "wordwrap", "yesno"], - operators = ["==", "!=", "<", ">", "<=", ">=", "in", "not", "or", "and"]; + operators = ["==", "!=", "<", ">", "<=", ">="], + wordOperators = ["in", "not", "or", "and"]; keywords = new RegExp("^\\b(" + keywords.join("|") + ")\\b"); filters = new RegExp("^\\b(" + filters.join("|") + ")\\b"); operators = new RegExp("^\\b(" + operators.join("|") + ")\\b"); + wordOperators = new RegExp("^\\b(" + wordOperators.join("|") + ")\\b"); // We have to return "null" instead of null, in order to avoid string // styling as the default, when using Django templates inside HTML @@ -270,6 +272,11 @@ return "operator"; } + // Attempt to match a word operator + if (stream.match(wordOperators)) { + return "keyword"; + } + // Attempt to match a keyword var keywordMatch = stream.match(keywords); if (keywordMatch) { From 07207dd57c351b7ce9861960c1fba03caf301a99 Mon Sep 17 00:00:00 2001 From: Marijn Haverbeke Date: Tue, 15 Dec 2015 22:13:51 +0100 Subject: [PATCH 0048/1790] [show-hint addon] Use mouseover rather than mousemove to change selection So that tiny mouse motions don't keep resetting the selected item Closes #3698 --- addon/hint/show-hint.js | 8 ++++---- 1 file changed, 4 insertions(+), 4 deletions(-) diff --git a/addon/hint/show-hint.js b/addon/hint/show-hint.js index 7eefad8b..204e136f 100644 --- a/addon/hint/show-hint.js +++ b/addon/hint/show-hint.js @@ -296,10 +296,10 @@ }); if (completion.options.completeOnSingleClick) - CodeMirror.on(hints, "mousemove", function(e) { - var elt = getHintElement(hints, e.target || e.srcElement); - if (elt && elt.hintId != null) - widget.changeActive(elt.hintId); + CodeMirror.on(hints, "mouseover", function(e) { + var target = e.target || e.srcElement + if (target.hintId != null && !target.contains(e.relatedTarget || e.fromElement)) + widget.changeActive(target.hintId); }); CodeMirror.signal(data, "select", completions[0], hints.firstChild); From 9951761dc3e690cb958b4f11f7a84092466c9c56 Mon Sep 17 00:00:00 2001 From: Marijn Haverbeke Date: Tue, 15 Dec 2015 22:36:46 +0100 Subject: [PATCH 0049/1790] Fall back to putting CodeMirror in window if this is undefined Issue #3708 --- lib/codemirror.js | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/lib/codemirror.js b/lib/codemirror.js index 5bfa91d6..07a23f1b 100644 --- a/lib/codemirror.js +++ b/lib/codemirror.js @@ -13,7 +13,7 @@ else if (typeof define == "function" && define.amd) // AMD return define([], mod); else // Plain browser env - this.CodeMirror = mod(); + (this || window).CodeMirror = mod(); })(function() { "use strict"; From b7f07850a9339b85a97fe0c261b5d3929c31d835 Mon Sep 17 00:00:00 2001 From: Cole R Lawrence Date: Mon, 14 Dec 2015 11:59:30 -0600 Subject: [PATCH 0050/1790] Link text runmode script in the correct location --- demo/runmode.html | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/demo/runmode.html b/demo/runmode.html index 257f03d6..ab8938d8 100644 --- a/demo/runmode.html +++ b/demo/runmode.html @@ -43,7 +43,7 @@

Mode Runner Demo

Running a CodeMirror mode outside of the editor. The CodeMirror.runMode function, defined - in lib/runmode.js takes the following arguments:

+ in addon/runmode/runmode.js takes the following arguments:

text (string)
From 4c66400caefe1dbaf959b30ed835feec6942aa86 Mon Sep 17 00:00:00 2001 From: Jim Date: Mon, 14 Dec 2015 11:47:37 -0800 Subject: [PATCH 0051/1790] Correct typo in codemirror.css (`actuall` vs `actual`) There was a typo in one of the comments (`actuall` vs `actual`). --- lib/codemirror.css | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/lib/codemirror.css b/lib/codemirror.css index 3543523e..1067b3ee 100644 --- a/lib/codemirror.css +++ b/lib/codemirror.css @@ -165,7 +165,7 @@ div.CodeMirror span.CodeMirror-nonmatchingbracket {color: #f22;} } /* The fake, visible scrollbars. Used to force redraw during scrolling - before actuall scrolling happens, thus preventing shaking and + before actual scrolling happens, thus preventing shaking and flickering artifacts. */ .CodeMirror-vscrollbar, .CodeMirror-hscrollbar, .CodeMirror-scrollbar-filler, .CodeMirror-gutter-filler { position: absolute; From 188ae75e0154ba68d918ab21939163d8bf2d9f87 Mon Sep 17 00:00:00 2001 From: McBrainy Date: Mon, 14 Dec 2015 17:49:23 -0600 Subject: [PATCH 0052/1790] Fix typo in manual It took me forever to figure out why vim mode wasn't working, and all because the manual said to set the "keymap" option and not the "keyMap" option. I also added quotes to make it clear that "vim" is a string. --- doc/manual.html | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/doc/manual.html b/doc/manual.html index 22d32df0..8812a171 100644 --- a/doc/manual.html +++ b/doc/manual.html @@ -3251,8 +3251,8 @@

VIM Mode API

CodeMirror has a robust VIM mode that attempts to faithfully emulate VIM's most useful features. It can be enabled by including keymap/vim.js - and setting the keymap option to - vim.

+ and setting the keyMap option to + "vim".

Configuration

From 7e35f03ad1639d92735c8dee1e1b5c86a727c873 Mon Sep 17 00:00:00 2001 From: Marijn Haverbeke Date: Tue, 15 Dec 2015 22:48:56 +0100 Subject: [PATCH 0053/1790] [runmode addon] Treat everything with an appendChild property as a DOM output node Closes #3703 --- addon/runmode/runmode.js | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/addon/runmode/runmode.js b/addon/runmode/runmode.js index 07d2279f..a51c6d0d 100644 --- a/addon/runmode/runmode.js +++ b/addon/runmode/runmode.js @@ -16,7 +16,7 @@ CodeMirror.runMode = function(string, modespec, callback, options) { var ie = /MSIE \d/.test(navigator.userAgent); var ie_lt9 = ie && (document.documentMode == null || document.documentMode < 9); - if (callback.nodeType == 1) { + if (callback.appendChild) { var tabSize = (options && options.tabSize) || CodeMirror.defaults.tabSize; var node = callback, col = 0; node.innerHTML = ""; From bae907609a698e0da4579f69d13f84eccc252833 Mon Sep 17 00:00:00 2001 From: Marijn Haverbeke Date: Wed, 16 Dec 2015 09:09:22 +0100 Subject: [PATCH 0054/1790] [sublime bindings] Give ctrl-/ command a name Closes #3689 --- keymap/sublime.js | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/keymap/sublime.js b/keymap/sublime.js index e0640b76..e0d0e92b 100644 --- a/keymap/sublime.js +++ b/keymap/sublime.js @@ -243,7 +243,7 @@ }); }; - map[ctrl + "/"] = function(cm) { + cmds[map[ctrl + "/"] = "toggleCommentIndented"] = function(cm) { cm.toggleComment({ indent: true }); } From 06e83fd66ebe639038eb64013891a1535ed07f25 Mon Sep 17 00:00:00 2001 From: Marijn Haverbeke Date: Wed, 16 Dec 2015 09:15:52 +0100 Subject: [PATCH 0055/1790] [show-hint addon] Revert select-on-mouse behavior Issue #3636 Issue #3698 --- addon/hint/show-hint.js | 7 ------- 1 file changed, 7 deletions(-) diff --git a/addon/hint/show-hint.js b/addon/hint/show-hint.js index 204e136f..cbe3b39a 100644 --- a/addon/hint/show-hint.js +++ b/addon/hint/show-hint.js @@ -295,13 +295,6 @@ setTimeout(function(){cm.focus();}, 20); }); - if (completion.options.completeOnSingleClick) - CodeMirror.on(hints, "mouseover", function(e) { - var target = e.target || e.srcElement - if (target.hintId != null && !target.contains(e.relatedTarget || e.fromElement)) - widget.changeActive(target.hintId); - }); - CodeMirror.signal(data, "select", completions[0], hints.firstChild); return true; } From 463797fb5ed003bacd64b69fc0910fbdb2cd0ca5 Mon Sep 17 00:00:00 2001 From: mihailik Date: Wed, 16 Dec 2015 09:55:27 +0000 Subject: [PATCH 0056/1790] [jump-to-line addon] Quote `default` property To avoid IE8 parser issue. --- addon/search/jump-to-line.js | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/addon/search/jump-to-line.js b/addon/search/jump-to-line.js index 49f3df4e..8b599cbc 100644 --- a/addon/search/jump-to-line.js +++ b/addon/search/jump-to-line.js @@ -45,5 +45,5 @@ }); }; - CodeMirror.keyMap.default["Alt-G"] = "jumpToLine"; + CodeMirror.keyMap["default"]["Alt-G"] = "jumpToLine"; }); From ab78fe07c459a56d92c6c4c9b50b3be86c314534 Mon Sep 17 00:00:00 2001 From: nightwing Date: Fri, 11 Dec 2015 00:49:09 +0400 Subject: [PATCH 0057/1790] [vim] fix autoindent of S command --- keymap/vim.js | 20 ++++++++++++++------ test/vim_test.js | 15 ++++++++++++--- 2 files changed, 26 insertions(+), 9 deletions(-) diff --git a/keymap/vim.js b/keymap/vim.js index c1532a17..59815957 100644 --- a/keymap/vim.js +++ b/keymap/vim.js @@ -1954,13 +1954,21 @@ text = text.slice(0, - match[0].length); } } - var wasLastLine = head.line - 1 == cm.lastLine(); - cm.replaceRange('', anchor, head); - if (args.linewise && !wasLastLine) { + var prevLineEnd = new Pos(anchor.line - 1, Number.MAX_VALUE); + var wasLastLine = cm.firstLine() == cm.lastLine(); + if (head.line > cm.lastLine() && args.linewise && !wasLastLine) { + cm.replaceRange('', prevLineEnd, head); + } else { + cm.replaceRange('', anchor, head); + } + if (args.linewise) { // Push the next line back down, if there is a next line. - CodeMirror.commands.newlineAndIndent(cm); - // null ch so setCursor moves to end of line. - anchor.ch = null; + if (!wasLastLine) { + cm.setCursor(prevLineEnd); + CodeMirror.commands.newlineAndIndent(cm); + } + // make sure cursor ends up at the end of the line. + anchor.ch = Number.MAX_VALUE; } finalHead = anchor; } else { diff --git a/test/vim_test.js b/test/vim_test.js index 855cb882..74c6a950 100644 --- a/test/vim_test.js +++ b/test/vim_test.js @@ -2148,9 +2148,18 @@ testVim('S_normal', function(cm, vim, helpers) { cm.setCursor(0, 1); helpers.doKeys('j', 'S'); helpers.doKeys(''); - helpers.assertCursorAt(1, 0); - eq('aa\n\ncc', cm.getValue()); -}, { value: 'aa\nbb\ncc'}); + helpers.assertCursorAt(1, 1); + eq('aa{\n \ncc', cm.getValue()); + helpers.doKeys('j', 'S'); + eq('aa{\n \n ', cm.getValue()); + helpers.assertCursorAt(2, 2); + helpers.doKeys(''); + helpers.doKeys('d', 'd', 'd', 'd'); + helpers.assertCursorAt(0, 0); + helpers.doKeys('S'); + is(vim.insertMode); + eq('', cm.getValue()); +}, { value: 'aa{\nbb\ncc'}); testVim('blockwise_paste', function(cm, vim, helpers) { cm.setCursor(0, 0); helpers.doKeys('', '3', 'j', 'l', 'y'); From 78e3ac3520aec44b19a47d7c43232813a0e28f39 Mon Sep 17 00:00:00 2001 From: Justin Andresen Date: Thu, 17 Dec 2015 19:58:16 +0100 Subject: [PATCH 0058/1790] Update manual entry of extendSelectionsBy. --- doc/manual.html | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/doc/manual.html b/doc/manual.html index 8812a171..2d8d618a 100644 --- a/doc/manual.html +++ b/doc/manual.html @@ -1350,7 +1350,7 @@

Cursor and selection methods

An equivalent of extendSelection that acts on all selections at once.
-
doc.extendSelectionsBy(f: function(range: {anchor, head}) → {anchor, head}), ?options: object)
+
doc.extendSelectionsBy(f: function(range: {anchor, head}) → {line, ch}), ?options: object)
Applies the given function to all existing selections, and calls extendSelections on the result.
From 3e88446bb59cacf133e7db1ae2221a85728b5bcc Mon Sep 17 00:00:00 2001 From: Drini Cami Date: Sat, 19 Dec 2015 02:50:21 -0500 Subject: [PATCH 0059/1790] [sparql mode] Add lineComment property --- 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 bbf8a76a..0cf40f58 100644 --- a/mode/sparql/sparql.js +++ b/mode/sparql/sparql.js @@ -165,7 +165,9 @@ CodeMirror.defineMode("sparql", function(config) { return context.col + (closing ? 0 : 1); else return context.indent + (closing ? 0 : indentUnit); - } + }, + + lineComment: "#" }; }); From d9042e78013f3377580966bf5a3092ccfa7848c0 Mon Sep 17 00:00:00 2001 From: Justin Andresen Date: Thu, 17 Dec 2015 18:58:45 +0100 Subject: [PATCH 0060/1790] Fix options of extendSelections. --- lib/codemirror.js | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/lib/codemirror.js b/lib/codemirror.js index 07a23f1b..c7c507d9 100644 --- a/lib/codemirror.js +++ b/lib/codemirror.js @@ -7454,7 +7454,7 @@ extendSelection(this, clipPos(this, head), other && clipPos(this, other), options); }), extendSelections: docMethodOp(function(heads, options) { - extendSelections(this, clipPosArray(this, heads, options)); + extendSelections(this, clipPosArray(this, heads), options); }), extendSelectionsBy: docMethodOp(function(f, options) { extendSelections(this, map(this.sel.ranges, f), options); From c39008a728e3172b196546eb7c22bb3d8a7d50f3 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Jan=20Sch=C3=A4r?= Date: Wed, 16 Dec 2015 21:10:11 +0100 Subject: [PATCH 0061/1790] [markdown mode] fix escaped brackets in link def Example: `[foo\[bar\]foo]: https://example.com` --- mode/markdown/markdown.js | 4 ++-- mode/markdown/test.js | 9 +++++++++ 2 files changed, 11 insertions(+), 2 deletions(-) diff --git a/mode/markdown/markdown.js b/mode/markdown/markdown.js index 97dfb746..70889205 100644 --- a/mode/markdown/markdown.js +++ b/mode/markdown/markdown.js @@ -620,7 +620,7 @@ CodeMirror.defineMode("markdown", function(cmCfg, modeCfg) { } function footnoteLink(stream, state) { - if (stream.match(/^[^\]]*\]:/, false)) { + if (stream.match(/^([^\]\\]|\\.)*\]:/, false)) { state.f = footnoteLinkInside; stream.next(); // Consume [ if (modeCfg.highlightFormatting) state.formatting = "link"; @@ -639,7 +639,7 @@ CodeMirror.defineMode("markdown", function(cmCfg, modeCfg) { return returnType; } - stream.match(/^[^\]]+/, true); + stream.match(/^([^\]\\]|\\.)+/, true); return tokenTypes.linkText; } diff --git a/mode/markdown/test.js b/mode/markdown/test.js index f9cc27c3..6d7829fa 100644 --- a/mode/markdown/test.js +++ b/mode/markdown/test.js @@ -696,6 +696,15 @@ "[link [[foo]]:] [string&url http://example.com/]", "(bar\" hello"); + MT("labelEscape", + "[link [[foo \\]] ]]:] [string&url http://example.com/]"); + + MT("labelEscapeColon", + "[link [[foo \\]]: bar]]:] [string&url http://example.com/]"); + + MT("labelEscapeEnd", + "[[foo\\]]: http://example.com/"); + MT("linkWeb", "[link ] foo"); From 420cefd50bb4f62db4217c709064b39f55976650 Mon Sep 17 00:00:00 2001 From: Justin Andresen Date: Thu, 17 Dec 2015 19:02:41 +0100 Subject: [PATCH 0062/1790] Clip extended ranges. --- lib/codemirror.js | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-) diff --git a/lib/codemirror.js b/lib/codemirror.js index c7c507d9..dde88b4f 100644 --- a/lib/codemirror.js +++ b/lib/codemirror.js @@ -7457,7 +7457,8 @@ extendSelections(this, clipPosArray(this, heads), options); }), extendSelectionsBy: docMethodOp(function(f, options) { - extendSelections(this, map(this.sel.ranges, f), options); + var heads = map(this.sel.ranges, f); + extendSelections(this, clipPosArray(this, heads), options); }), setSelections: docMethodOp(function(ranges, primary, options) { if (!ranges.length) return; From fec88d280e1c0bf0f09b6b1db50318f39a74f172 Mon Sep 17 00:00:00 2001 From: Justin Andresen Date: Thu, 17 Dec 2015 19:02:41 +0100 Subject: [PATCH 0063/1790] Add an origin field to beforeSelectionChange event objects Issue #3723 --- doc/manual.html | 8 +++++--- lib/codemirror.js | 7 ++++--- 2 files changed, 9 insertions(+), 6 deletions(-) diff --git a/doc/manual.html b/doc/manual.html index 2d8d618a..bda852a7 100644 --- a/doc/manual.html +++ b/doc/manual.html @@ -582,15 +582,17 @@

Events

mode's electric patterns, and this caused the line's indentation to change. -
"beforeSelectionChange" (instance: CodeMirror, obj: {ranges, update})
+
"beforeSelectionChange" (instance: CodeMirror, obj: {ranges, origin, update})
This event is fired before the selection is moved. Its handler may inspect the set of selection ranges, present as an array of {anchor, head} objects in the ranges property of the obj argument, and optionally change them by calling the update method on this object, passing an array - of ranges in the same format. Handlers for this event have the - same restriction + of ranges in the same format. The object also contains + an origin property holding the origin string passed + to the selection-changing method, if any. Handlers for this + event have the same restriction as "beforeChange" handlers — they should not do anything to directly update the state of the editor.
diff --git a/lib/codemirror.js b/lib/codemirror.js index dde88b4f..2141cda1 100644 --- a/lib/codemirror.js +++ b/lib/codemirror.js @@ -2155,7 +2155,7 @@ // Give beforeSelectionChange handlers a change to influence a // selection update. - function filterSelectionChange(doc, sel) { + function filterSelectionChange(doc, sel, options) { var obj = { ranges: sel.ranges, update: function(ranges) { @@ -2163,7 +2163,8 @@ for (var i = 0; i < ranges.length; i++) this.ranges[i] = new Range(clipPos(doc, ranges[i].anchor), clipPos(doc, ranges[i].head)); - } + }, + origin: options && options.origin }; signal(doc, "beforeSelectionChange", doc, obj); if (doc.cm) signal(doc.cm, "beforeSelectionChange", doc.cm, obj); @@ -2189,7 +2190,7 @@ function setSelectionNoUndo(doc, sel, options) { if (hasHandler(doc, "beforeSelectionChange") || doc.cm && hasHandler(doc.cm, "beforeSelectionChange")) - sel = filterSelectionChange(doc, sel); + sel = filterSelectionChange(doc, sel, options); var bias = options && options.bias || (cmp(sel.primary().head, doc.sel.primary().head) < 0 ? -1 : 1); From ca8fb83d48ebb9fe2d24b8f08abfe204f706dcad Mon Sep 17 00:00:00 2001 From: Marijn Haverbeke Date: Sun, 20 Dec 2015 21:50:47 +0100 Subject: [PATCH 0064/1790] Fire DOM events for paste --- doc/manual.html | 2 +- lib/codemirror.js | 8 +++++--- 2 files changed, 6 insertions(+), 4 deletions(-) diff --git a/doc/manual.html b/doc/manual.html index bda852a7..695a7ed2 100644 --- a/doc/manual.html +++ b/doc/manual.html @@ -654,7 +654,7 @@

Events

"mousedown", "dblclick", "contextmenu", "keydown", "keypress", - "keyup", "dragstart", "dragenter", + "keyup", "paste", "dragstart", "dragenter", "dragover", "drop" (instance: CodeMirror, event: Event)
Fired when CodeMirror is handling a DOM event of this type. diff --git a/lib/codemirror.js b/lib/codemirror.js index 2141cda1..6305cafd 100644 --- a/lib/codemirror.js +++ b/lib/codemirror.js @@ -1251,7 +1251,7 @@ }); on(te, "paste", function(e) { - if (handlePaste(e, cm)) return true; + if (signalDOMEvent(cm, e) || handlePaste(e, cm)) return cm.state.pasteIncoming = true; input.fastPoll(); @@ -1285,7 +1285,7 @@ on(te, "copy", prepareCopyCut); on(display.scroller, "paste", function(e) { - if (eventInWidget(display, e)) return; + if (eventInWidget(display, e) || signalDOMEvent(cm, e)) return; cm.state.pasteIncoming = true; input.focus(); }); @@ -1570,7 +1570,9 @@ var div = input.div = display.lineDiv; disableBrowserMagic(div); - on(div, "paste", function(e) { handlePaste(e, cm); }) + on(div, "paste", function(e) { + if (!signalDOMEvent(cm, e)) handlePaste(e, cm); + }) on(div, "compositionstart", function(e) { var data = e.data; From a4e987079517e1ff23d73c5a572f69d15ec4ff55 Mon Sep 17 00:00:00 2001 From: Chunliang Lyu Date: Sat, 19 Dec 2015 22:39:10 +0800 Subject: [PATCH 0065/1790] Add mode for GitHub Flavored Markdown with YAML front matter --- mode/yaml-markdown/index.html | 114 ++++++++++++++++++++++++++++ mode/yaml-markdown/yaml-markdown.js | 64 ++++++++++++++++ 2 files changed, 178 insertions(+) create mode 100644 mode/yaml-markdown/index.html create mode 100644 mode/yaml-markdown/yaml-markdown.js diff --git a/mode/yaml-markdown/index.html b/mode/yaml-markdown/index.html new file mode 100644 index 00000000..ad610da8 --- /dev/null +++ b/mode/yaml-markdown/index.html @@ -0,0 +1,114 @@ + + +CodeMirror: GitHub Flavored Markdown with YAML front matter mode + + + + + + + + + + + + + +
+

GitHub Flavored Markdown with YAML front matter mode

+
+ + +
diff --git a/mode/yaml-markdown/yaml-markdown.js b/mode/yaml-markdown/yaml-markdown.js new file mode 100644 index 00000000..a1af80f3 --- /dev/null +++ b/mode/yaml-markdown/yaml-markdown.js @@ -0,0 +1,64 @@ +// 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"), require("../gfm/gfm"), require("../yaml/yaml")); + else if (typeof define == "function" && define.amd) // AMD + define(["../../lib/codemirror", "../gfm/gfm", "../yaml/yaml"], mod); + else // Plain browser env + mod(CodeMirror); +})(function (CodeMirror) { + + // a mixed mode for Markdown text with an optional YAML front matter + CodeMirror.defineMode("yaml-markdown", function (config) { + var gfmMode = CodeMirror.getMode(config, {name: "gfm"}); + var yamlMode = CodeMirror.getMode(config, {name: "yaml"}); + + return { + startState: function () { + var gfmState = gfmMode.startState(); + var yamlState = yamlMode.startState(); + return { + firstLine: true, + mode: gfmMode, + gfmState: gfmState, + yamlState: yamlState + }; + }, + copyState: function (state) { + return { + mode: state.mode, + gfmState: gfmMode.copyState(state.gfmState), + yamlState: state.yamlState + }; + }, + token: function (stream, state) { + if (state.firstLine && stream.match(/---/, false)) { + state.firstLine = false; + state.mode = yamlMode; + return yamlMode.token(stream, state.yamlState); + } else if (state.mode == yamlMode && stream.match(/---/, false)) { + state.mode = gfmMode; + return yamlMode.token(stream, state.yamlState); + } else if (state.mode == yamlMode) { + return state.mode.token(stream, state.yamlState); + } else { + return state.mode.token(stream, state.gfmState); + } + }, + innerMode: function (state) { + if (state.mode == gfmMode) { + return gfmMode.innerMode(state.gfmState); + } else { + return {mode: yamlMode, state: state}; + } + }, + blankLine: function (state) { + if (state.mode == gfmMode) { + return gfmMode.blankLine(state.gfmState) + } + } + }; + }); +}); From d6212b9216e4a3add9df636726d0c59dfd62ca74 Mon Sep 17 00:00:00 2001 From: Marijn Haverbeke Date: Sun, 20 Dec 2015 22:15:16 +0100 Subject: [PATCH 0066/1790] [yaml-frontmatter mode] Repurpose yaml-markdown mode to a general yaml-frontmatter mode Issue #3722 --- doc/compress.html | 1 + mode/index.html | 1 + .../index.html | 17 +++-- mode/yaml-frontmatter/yaml-frontmatter.js | 68 +++++++++++++++++++ mode/yaml-markdown/yaml-markdown.js | 64 ----------------- 5 files changed, 82 insertions(+), 69 deletions(-) rename mode/{yaml-markdown => yaml-frontmatter}/index.html (83%) create mode 100644 mode/yaml-frontmatter/yaml-frontmatter.js delete mode 100644 mode/yaml-markdown/yaml-markdown.js diff --git a/doc/compress.html b/doc/compress.html index f7c511c7..fe8260ad 100644 --- a/doc/compress.html +++ b/doc/compress.html @@ -219,6 +219,7 @@

Script compression helper

+ diff --git a/mode/index.html b/mode/index.html index 477ca2c1..72419241 100644 --- a/mode/index.html +++ b/mode/index.html @@ -148,6 +148,7 @@

Language modes

  • XML/HTML
  • XQuery
  • YAML
  • +
  • YAML frontmatter
  • Z80
  • diff --git a/mode/yaml-markdown/index.html b/mode/yaml-frontmatter/index.html similarity index 83% rename from mode/yaml-markdown/index.html rename to mode/yaml-frontmatter/index.html index ad610da8..30bed2f8 100644 --- a/mode/yaml-markdown/index.html +++ b/mode/yaml-frontmatter/index.html @@ -1,6 +1,6 @@ -CodeMirror: GitHub Flavored Markdown with YAML front matter mode +CodeMirror: YAML front matter mode @@ -10,7 +10,7 @@ - +
    -

    GitHub Flavored Markdown with YAML front matter mode

    +

    YAML front matter mode

    + +

    Defines a mode that parses +a YAML frontmatter +at the start of a file, switching to a base mode at the end of that. +Takes a mode configuration option base to configure the +base mode, which defaults to "gfm".

    +
    diff --git a/mode/yaml-frontmatter/yaml-frontmatter.js b/mode/yaml-frontmatter/yaml-frontmatter.js new file mode 100644 index 00000000..5b65dffb --- /dev/null +++ b/mode/yaml-frontmatter/yaml-frontmatter.js @@ -0,0 +1,68 @@ +// 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"), require("../yaml/yaml")) + else if (typeof define == "function" && define.amd) // AMD + define(["../../lib/codemirror", "../yaml/yaml"], mod) + else // Plain browser env + mod(CodeMirror) +})(function (CodeMirror) { + + var START = 0, FRONTMATTER = 1, BODY = 2 + + // a mixed mode for Markdown text with an optional YAML front matter + CodeMirror.defineMode("yaml-frontmatter", function (config, parserConfig) { + var yamlMode = CodeMirror.getMode(config, "yaml") + var innerMode = CodeMirror.getMode(config, parserConfig && parserConfig.base || "gfm") + + function curMode(state) { + return state.state == BODY ? innerMode : yamlMode + } + + return { + startState: function () { + return { + state: START, + inner: CodeMirror.startState(yamlMode) + } + }, + copyState: function (state) { + return { + state: state.state, + inner: CodeMirror.copyState(curMode(state), state.inner) + } + }, + token: function (stream, state) { + if (state.state == START) { + if (stream.match(/---/, false)) { + state.state = FRONTMATTER + return yamlMode.token(stream, state.inner) + } else { + stream.state = BODY + state.inner = CodeMirror.startState(innerMode) + return innerMode.token(stream, state.inner) + } + } else if (state.state == FRONTMATTER) { + var end = stream.sol() && stream.match(/---/, false) + var style = yamlMode.token(stream, state.inner) + if (end) { + state.state = BODY + state.inner = CodeMirror.startState(innerMode) + } + return style + } else { + return innerMode.token(stream, state.inner) + } + }, + innerMode: function (state) { + return {mode: curMode(state), state: state.inner} + }, + blankLine: function (state) { + var mode = curMode(state) + if (mode.blankLine) return mode.blankLine(state.inner) + } + } + }) +}) diff --git a/mode/yaml-markdown/yaml-markdown.js b/mode/yaml-markdown/yaml-markdown.js deleted file mode 100644 index a1af80f3..00000000 --- a/mode/yaml-markdown/yaml-markdown.js +++ /dev/null @@ -1,64 +0,0 @@ -// 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"), require("../gfm/gfm"), require("../yaml/yaml")); - else if (typeof define == "function" && define.amd) // AMD - define(["../../lib/codemirror", "../gfm/gfm", "../yaml/yaml"], mod); - else // Plain browser env - mod(CodeMirror); -})(function (CodeMirror) { - - // a mixed mode for Markdown text with an optional YAML front matter - CodeMirror.defineMode("yaml-markdown", function (config) { - var gfmMode = CodeMirror.getMode(config, {name: "gfm"}); - var yamlMode = CodeMirror.getMode(config, {name: "yaml"}); - - return { - startState: function () { - var gfmState = gfmMode.startState(); - var yamlState = yamlMode.startState(); - return { - firstLine: true, - mode: gfmMode, - gfmState: gfmState, - yamlState: yamlState - }; - }, - copyState: function (state) { - return { - mode: state.mode, - gfmState: gfmMode.copyState(state.gfmState), - yamlState: state.yamlState - }; - }, - token: function (stream, state) { - if (state.firstLine && stream.match(/---/, false)) { - state.firstLine = false; - state.mode = yamlMode; - return yamlMode.token(stream, state.yamlState); - } else if (state.mode == yamlMode && stream.match(/---/, false)) { - state.mode = gfmMode; - return yamlMode.token(stream, state.yamlState); - } else if (state.mode == yamlMode) { - return state.mode.token(stream, state.yamlState); - } else { - return state.mode.token(stream, state.gfmState); - } - }, - innerMode: function (state) { - if (state.mode == gfmMode) { - return gfmMode.innerMode(state.gfmState); - } else { - return {mode: yamlMode, state: state}; - } - }, - blankLine: function (state) { - if (state.mode == gfmMode) { - return gfmMode.blankLine(state.gfmState) - } - } - }; - }); -}); From 237a6b69baab3c5d0c4dedc00ad70ceb436d6d13 Mon Sep 17 00:00:00 2001 From: Erik Welander Date: Sat, 12 Dec 2015 01:56:34 -0800 Subject: [PATCH 0067/1790] [vim] Correct the scroll position for zt & zb. zt was placing 40% of the line above the visible area, and zb was placing it too high. --- keymap/vim.js | 4 +--- test/vim_test.js | 19 +++++++++++++++++++ 2 files changed, 20 insertions(+), 3 deletions(-) diff --git a/keymap/vim.js b/keymap/vim.js index 59815957..0548b75b 100644 --- a/keymap/vim.js +++ b/keymap/vim.js @@ -2147,9 +2147,7 @@ switch (actionArgs.position) { case 'center': y = y - (height / 2) + lineHeight; break; - case 'bottom': y = y - height + lineHeight*1.4; - break; - case 'top': y = y + lineHeight*0.4; + case 'bottom': y = y - height + lineHeight; break; } cm.scrollTo(null, y); diff --git a/test/vim_test.js b/test/vim_test.js index 74c6a950..25f7e75e 100644 --- a/test/vim_test.js +++ b/test/vim_test.js @@ -3119,6 +3119,25 @@ forEach(['zb','zz','zt','z-','z.','z'], function(e, idx){ return new Array(500).join('\n'); })()}); }); +testVim('zb_to_bottom', function(cm, vim, helpers){ + var lineNum = 250; + cm.setSize(600, 35*cm.defaultTextHeight()); + cm.setCursor(lineNum, 0); + helpers.doKeys('z', 'b'); + var scrollInfo = cm.getScrollInfo(); + eq(scrollInfo.top + scrollInfo.clientHeight, cm.charCoords(Pos(lineNum, 0), 'local').bottom); +}, { value: (function(){ + return new Array(500).join('\n'); +})()}); +testVim('zt_to_top', function(cm, vim, helpers){ + var lineNum = 250; + cm.setSize(600, 35*cm.defaultTextHeight()); + cm.setCursor(lineNum, 0); + helpers.doKeys('z', 't'); + eq(cm.getScrollInfo().top, cm.charCoords(Pos(lineNum, 0), 'local').top); +}, { value: (function(){ + return new Array(500).join('\n'); +})()}); testVim('zb Date: Mon, 21 Dec 2015 10:05:31 +0100 Subject: [PATCH 0068/1790] [clike mode] Highlight comments in preprocessor lines Closes #3709 --- mode/clike/clike.js | 25 ++++++++++++------------- mode/clike/test.js | 9 +++++++++ 2 files changed, 21 insertions(+), 13 deletions(-) diff --git a/mode/clike/clike.js b/mode/clike/clike.js index 0cbe2fae..3766209c 100644 --- a/mode/clike/clike.js +++ b/mode/clike/clike.js @@ -262,21 +262,20 @@ CodeMirror.defineMode("clike", function(config, parserConfig) { var cTypes = "int long char short double float unsigned signed void size_t ptrdiff_t"; function cppHook(stream, state) { - if (!state.startOfLine) return false; - for (;;) { - if (stream.skipTo("\\")) { - stream.next(); - if (stream.eol()) { - state.tokenize = cppHook; - break; - } - } else { - stream.skipToEnd(); - state.tokenize = null; - break; + if (!state.startOfLine) return false + for (var ch, next = null; ch = stream.peek();) { + if (!ch) { + break + } else if (ch == "\\" && stream.match(/^.$/)) { + next = cppHook + break + } else if (ch == "/" && stream.match(/^\/[\/\*]/, false)) { + break } + stream.next() } - return "meta"; + state.tokenize = next + return "meta" } function pointerHook(_stream, state) { diff --git a/mode/clike/test.js b/mode/clike/test.js index c84d22e1..c2600326 100644 --- a/mode/clike/test.js +++ b/mode/clike/test.js @@ -31,6 +31,15 @@ " [variable x][operator ++];", "[keyword return];"); + MT("preprocessor", + "[meta #define FOO 3]", + "[variable-3 int] [variable foo];", + "[meta #define BAR\\]", + "[meta 4]", + "[variable-3 unsigned] [variable-3 int] [variable bar] [operator =] [number 8];", + "[meta #include ][comment // comment]") + + var mode_cpp = CodeMirror.getMode({indentUnit: 2}, "text/x-c++src"); function MTCPP(name) { test.mode(name, mode_cpp, Array.prototype.slice.call(arguments, 1)); } From c7b64ca080df3657f5094a51a976a167176a4178 Mon Sep 17 00:00:00 2001 From: TSUYUSATO Kitsune Date: Thu, 17 Dec 2015 00:12:45 +0900 Subject: [PATCH 0069/1790] [crystal mode] Add --- mode/crystal/crystal.js | 391 ++++++++++++++++++++++++++++++++++++++++ mode/crystal/index.html | 119 ++++++++++++ mode/index.html | 1 + mode/meta.js | 1 + 4 files changed, 512 insertions(+) create mode 100644 mode/crystal/crystal.js create mode 100644 mode/crystal/index.html diff --git a/mode/crystal/crystal.js b/mode/crystal/crystal.js new file mode 100644 index 00000000..8fd65a5f --- /dev/null +++ b/mode/crystal/crystal.js @@ -0,0 +1,391 @@ +// 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("crystal", function(config) { + function wordRegExp(words, end) { + return new RegExp((end ? "" : "^") + "(?:" + words.join("|") + ")" + (end ? "$" : "\\b")); + } + + function chain(tokenize, stream, state) { + state.tokenize.push(tokenize); + return tokenize(stream, state); + } + + var operators = /^(?:[-+/%|&^]|\*\*?|[<>]{2})/; + var conditionalOperators = /^(?:[=!]~|===|<=>|[<>=!]=?|[|&]{2}|~)/; + var indexingOperators = /^(?:\[\][?=]?)/; + var anotherOperators = /^(?:\.(?:\.{2})?|->|[?:])/; + var idents = /^[a-z_\u009F-\uFFFF][a-zA-Z0-9_\u009F-\uFFFF]*/; + var types = /^[A-Z_\u009F-\uFFFF][a-zA-Z0-9_\u009F-\uFFFF]*/; + var keywords = wordRegExp([ + "abstract", "alias", "as", "asm", "begin", "break", "case", "class", "def", "do", + "else", "elsif", "end", "ensure", "enum", "extend", "for", "fun", "if", "ifdef", + "include", "instance_sizeof", "lib", "macro", "module", "next", "of", "out", "pointerof", + "private", "protected", "rescue", "return", "require", "sizeof", "struct", + "super", "then", "type", "typeof", "union", "unless", "until", "when", "while", "with", + "yield", "__DIR__", "__FILE__", "__LINE__" + ]); + var atomWords = wordRegExp(["true", "false", "nil", "self"]); + var indentKeywordsArray = [ + "def", "fun", "macro", + "class", "module", "struct", "lib", "enum", "union", + "if", "unless", "case", "while", "until", "begin", "then", + "do", + "for", "ifdef" + ]; + var indentKeywords = wordRegExp(indentKeywordsArray); + var dedentKeywordsArray = [ + "end", + "else", "elsif", + "rescue", "ensure" + ]; + var dedentKeywords = wordRegExp(dedentKeywordsArray); + var dedentPunctualsArray = ["\\)", "\\}", "\\]"]; + var dedentPunctuals = new RegExp("^(?:" + dedentPunctualsArray.join("|") + ")$"); + var nextTokenizer = { + "def": tokenFollowIdent, "fun": tokenFollowIdent, "macro": tokenMacroDef, + "class": tokenFollowType, "module": tokenFollowType, "struct": tokenFollowType, + "lib": tokenFollowType, "enum": tokenFollowType, "union": tokenFollowType + }; + var matching = {"[": "]", "{": "}", "(": ")", "<": ">"}; + + function tokenBase(stream, state) { + if (stream.eatSpace()) { + return null; + } + + // Macros + if (state.lastToken != "\\" && stream.match("{%", false)) { + return chain(tokenMacro("%", "%"), stream, state); + } + + if (state.lastToken != "\\" && stream.match("{{", false)) { + return chain(tokenMacro("{", "}"), stream, state); + } + + // Comments + if (stream.peek() == "#") { + stream.skipToEnd(); + return "comment"; + } + + // Variables and keywords + var matched; + if (matched = stream.match(idents)) { + stream.eat(/[?!]/); + + matched = stream.current(); + if (stream.eat(":")) { + return "atom"; + } else if (state.lastToken == ".") { + return "property"; + } else if (keywords.test(matched)) { + if (state.lastToken != "abstract" && indentKeywords.test(matched)) { + if (!(matched == "fun" && state.blocks.indexOf("lib") >= 0)) { + state.blocks.push(matched); + state.currentIndent += 1; + } + } else if (dedentKeywords.test(matched)) { + state.blocks.pop(); + state.currentIndent -= 1; + } + + if (nextTokenizer.hasOwnProperty(matched)) { + state.tokenize.push(nextTokenizer[matched]); + } + + return "keyword"; + } else if (atomWords.test(matched)) { + return "atom"; + } + + return "variable"; + } + + // Class variables and instance variables + // or attributes + if (stream.eat("@")) { + if (stream.peek() == "[") { + return chain(tokenNest("[", "]", "meta"), stream, state); + } + + stream.eat("@"); + stream.match(idents) || stream.match(types); + return "variable-2"; + } + + // Global variables + if (stream.eat("$")) { + stream.eat(/[0-9]+|\?/) || stream.match(idents) || stream.match(types); + return "variable-3"; + } + + // Constants and types + if (stream.match(types)) { + return "tag"; + } + + // Symbols or ':' operator + if (stream.eat(":")) { + if (stream.eat("\"")) { + return chain(tokenQuote("\"", "atom", false), stream, state); + } else if (stream.match(idents) || stream.match(types) || + stream.match(operators) || stream.match(conditionalOperators) || stream.match(indexingOperators)) { + return "atom"; + } + stream.eat(":"); + return "operator"; + } + + // Strings + if (stream.eat("\"")) { + return chain(tokenQuote("\"", "string", true), stream, state); + } + + // Strings or regexps or macro variables or '%' operator + if (stream.peek() == "%") { + var style = "string"; + var embed = true; + var delim; + + if (stream.match("%r")) { + // Regexps + style = "string-2"; + delim = stream.next(); + } else if (stream.match("%w")) { + embed = false; + delim = stream.next(); + } else { + if(delim = stream.match(/^%([^\w\s=])/)) { + delim = delim[1]; + } else if (stream.match(/^%[a-zA-Z0-9_\u009F-\uFFFF]*/)) { + // Macro variables + return "meta"; + } else { + // '%' operator + return "operator"; + } + } + + if (matching.hasOwnProperty(delim)) { + delim = matching[delim]; + } + return chain(tokenQuote(delim, style, embed), stream, state); + } + + // Characters + if (stream.eat("'")) { + stream.match(/^(?:[^']|\\(?:[befnrtv0'"]|[0-7]{3}|u(?:[0-9a-fA-F]{4}|\{[0-9a-fA-F]{1,6}\})))/); + stream.eat("'"); + return "atom"; + } + + // Numbers + if (stream.eat("0")) { + if (stream.eat("x")) { + stream.match(/^[0-9a-fA-F]+/); + } else if (stream.eat("o")) { + stream.match(/^[0-7]+/); + } else if (stream.eat("b")) { + stream.match(/^[01]+/); + } + return "number"; + } + + if (stream.eat(/\d/)) { + stream.match(/^\d*(?:\.\d+)?(?:[eE][+-]?\d+)?/); + return "number"; + } + + // Operators + if (stream.match(operators)) { + stream.eat("="); // Operators can follow assigin symbol. + return "operator"; + } + + if (stream.match(conditionalOperators) || stream.match(anotherOperators)) { + return "operator"; + } + + // Parens and braces + if (matched = stream.match(/[({[]/, false)) { + matched = matched[0]; + return chain(tokenNest(matched, matching[matched], null), stream, state); + } + + // Escapes + if (stream.eat("\\")) { + stream.next(); + return "meta"; + } + + stream.next(); + return null; + } + + function tokenNest(begin, end, style, started) { + return function (stream, state) { + if (!started && stream.match(begin)) { + state.tokenize[state.tokenize.length - 1] = tokenNest(begin, end, style, true); + state.currentIndent += 1; + return style; + } + + var nextStyle = tokenBase(stream, state); + if (stream.current() === end) { + state.tokenize.pop(); + state.currentIndent -= 1; + nextStyle = style; + } + + return nextStyle; + }; + } + + function tokenMacro(begin, end, started) { + return function (stream, state) { + if (!started && stream.match("{" + begin)) { + state.currentIndent += 1; + state.tokenize[state.tokenize.length - 1] = tokenMacro(begin, end, true); + return "meta"; + } + + if (stream.match(end + "}")) { + state.currentIndent -= 1; + state.tokenize.pop(); + return "meta"; + } + + return tokenBase(stream, state); + }; + } + + function tokenMacroDef(stream, state) { + if (stream.eatSpace()) { + return null; + } + + var matched; + if (matched = stream.match(idents)) { + if (matched == "def") { + return "keyword"; + } + stream.eat(/[?!]/); + } + + state.tokenize.pop(); + return "def"; + } + + function tokenFollowIdent(stream, state) { + if (stream.eatSpace()) { + return null; + } + + if (stream.match(idents)) { + stream.eat(/[!?]/); + } else { + stream.match(operators) || stream.match(conditionalOperators) || stream.match(indexingOperators); + } + state.tokenize.pop(); + return "def"; + } + + function tokenFollowType(stream, state) { + if (stream.eatSpace()) { + return null; + } + + stream.match(types); + state.tokenize.pop(); + return "def"; + } + + function tokenQuote(end, style, embed) { + return function (stream, state) { + var escaped = false; + + while (stream.peek()) { + if (!escaped) { + if (stream.match("{%", false)) { + state.tokenize.push(tokenMacro("%", "%")); + return style; + } + + if (stream.match("{{", false)) { + state.tokenize.push(tokenMacro("{", "}")); + return style; + } + + if (embed && stream.match("#{", false)) { + state.tokenize.push(tokenNest("#{", "}", "meta")); + return style; + } + + var ch = stream.next(); + + if (ch == end) { + state.tokenize.pop(); + return style; + } + + escaped = ch == "\\"; + } else { + stream.next(); + escaped = false; + } + } + + return style; + }; + } + + return { + startState: function () { + return { + tokenize: [tokenBase], + currentIndent: 0, + lastToken: null, + blocks: [] + }; + }, + + token: function (stream, state) { + var style = state.tokenize[state.tokenize.length - 1](stream, state); + var token = stream.current(); + + if (style && style != "comment") { + state.lastToken = token; + } + + return style; + }, + + indent: function (state, textAfter) { + textAfter = textAfter.replace(/^\s*(?:\{%)?\s*|\s*(?:%\})?\s*$/g, ""); + + if (dedentKeywords.test(textAfter) || dedentPunctuals.test(textAfter)) { + return config.indentUnit * (state.currentIndent - 1); + } + + return config.indentUnit * state.currentIndent; + }, + + fold: "indent", + electricInput: wordRegExp(dedentPunctualsArray.concat(dedentKeywordsArray), true), + lineComment: '#' + }; + }); + + CodeMirror.defineMIME("text/x-crystal", "crystal"); +}); diff --git a/mode/crystal/index.html b/mode/crystal/index.html new file mode 100644 index 00000000..4bd0399f --- /dev/null +++ b/mode/crystal/index.html @@ -0,0 +1,119 @@ + + +CodeMirror: Ruby mode + + + + + + + + + + + +
    +

    Crystal mode

    +
    + + +

    MIME types defined: text/x-crystal.

    +
    diff --git a/mode/index.html b/mode/index.html index 72419241..072b89bd 100644 --- a/mode/index.html +++ b/mode/index.html @@ -42,6 +42,7 @@

    Language modes

  • COBOL
  • CoffeeScript
  • Common Lisp
  • +
  • Crystal
  • CSS
  • Cypher
  • Cython
  • diff --git a/mode/meta.js b/mode/meta.js index 7af51c1e..69e2a3ef 100644 --- a/mode/meta.js +++ b/mode/meta.js @@ -28,6 +28,7 @@ {name: "Common Lisp", mime: "text/x-common-lisp", mode: "commonlisp", ext: ["cl", "lisp", "el"], alias: ["lisp"]}, {name: "Cypher", mime: "application/x-cypher-query", mode: "cypher", ext: ["cyp", "cypher"]}, {name: "Cython", mime: "text/x-cython", mode: "python", ext: ["pyx", "pxd", "pxi"]}, + {name: "Crystal", mime: "text/x-crystal", mode: "crystal", ext: ["cr"]}, {name: "CSS", mime: "text/css", mode: "css", ext: ["css"]}, {name: "CQL", mime: "text/x-cassandra", mode: "sql", ext: ["cql"]}, {name: "D", mime: "text/x-d", mode: "d", ext: ["d"]}, From 09e20bce214c2bd849667783365a7283b803d8eb Mon Sep 17 00:00:00 2001 From: Marijn Haverbeke Date: Mon, 21 Dec 2015 10:42:29 +0100 Subject: [PATCH 0070/1790] [crystal mode] Integrate --- doc/compress.html | 1 + mode/crystal/crystal.js | 2 +- 2 files changed, 2 insertions(+), 1 deletion(-) diff --git a/doc/compress.html b/doc/compress.html index fe8260ad..74c68347 100644 --- a/doc/compress.html +++ b/doc/compress.html @@ -122,6 +122,7 @@

    Script compression helper

    + diff --git a/mode/crystal/crystal.js b/mode/crystal/crystal.js index 8fd65a5f..2e74bee4 100644 --- a/mode/crystal/crystal.js +++ b/mode/crystal/crystal.js @@ -81,7 +81,7 @@ // Variables and keywords var matched; - if (matched = stream.match(idents)) { + if (stream.match(idents)) { stream.eat(/[?!]/); matched = stream.current(); From 6bc4c5a082be035ecf1c2bc66c0237df7c94850f Mon Sep 17 00:00:00 2001 From: TSUYUSATO Kitsune Date: Mon, 21 Dec 2015 18:55:12 +0900 Subject: [PATCH 0071/1790] [crystal mode] Crystal looks like Ruby but not Ruby --- mode/crystal/index.html | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/mode/crystal/index.html b/mode/crystal/index.html index 4bd0399f..ec03e250 100644 --- a/mode/crystal/index.html +++ b/mode/crystal/index.html @@ -1,6 +1,6 @@ -CodeMirror: Ruby mode +CodeMirror: Crystal mode @@ -23,7 +23,7 @@ From fcfe83818aaee069c932be21cffeb855d7ac6cb0 Mon Sep 17 00:00:00 2001 From: Marijn Haverbeke Date: Mon, 21 Dec 2015 11:16:41 +0100 Subject: [PATCH 0072/1790] Fix assignment of end styles when there are multiple active marks We can't check for nextChange until all marks have been looked at See https://discuss.codemirror.net/t/marktext-endstyle-appearing-multiple-times/568/5 --- lib/codemirror.js | 7 +++++-- 1 file changed, 5 insertions(+), 2 deletions(-) diff --git a/lib/codemirror.js b/lib/codemirror.js index 6305cafd..a90b9b59 100644 --- a/lib/codemirror.js +++ b/lib/codemirror.js @@ -7089,7 +7089,7 @@ if (nextChange == pos) { // Update current marker set spanStyle = spanEndStyle = spanStartStyle = title = css = ""; collapsed = null; nextChange = Infinity; - var foundBookmarks = []; + var foundBookmarks = [], endStyles for (var j = 0; j < spans.length; ++j) { var sp = spans[j], m = sp.marker; if (m.type == "bookmark" && sp.from == pos && m.widgetNode) { @@ -7102,7 +7102,7 @@ if (m.className) spanStyle += " " + m.className; if (m.css) css = (css ? css + ";" : "") + m.css; if (m.startStyle && sp.from == pos) spanStartStyle += " " + m.startStyle; - if (m.endStyle && sp.to == nextChange) spanEndStyle += " " + m.endStyle; + if (m.endStyle && sp.to == nextChange) (endStyles || (endStyles = [])).push(m.endStyle, sp.to) if (m.title && !title) title = m.title; if (m.collapsed && (!collapsed || compareCollapsedMarkers(collapsed.marker, m) < 0)) collapsed = sp; @@ -7110,6 +7110,9 @@ nextChange = sp.from; } } + if (endStyles) for (var j = 0; j < endStyles.length; j += 2) + if (endStyles[j + 1] == nextChange) spanEndStyle += " " + endStyles[j] + if (collapsed && (collapsed.from || 0) == pos) { buildCollapsedSpan(builder, (collapsed.to == null ? len + 1 : collapsed.to) - pos, collapsed.marker, collapsed.from == null); From 8a3e59cb49b05da385639c350858c2a2a07e60c1 Mon Sep 17 00:00:00 2001 From: Marijn Haverbeke Date: Mon, 21 Dec 2015 11:30:25 +0100 Subject: [PATCH 0073/1790] Mark release 5.10.0 --- AUTHORS | 11 +++++++++++ doc/compress.html | 1 + doc/manual.html | 2 +- doc/releases.html | 19 ++++++++++++++++--- index.html | 2 +- lib/codemirror.js | 2 +- package.json | 2 +- 7 files changed, 32 insertions(+), 7 deletions(-) diff --git a/AUTHORS b/AUTHORS index 4f060641..830a968c 100644 --- a/AUTHORS +++ b/AUTHORS @@ -105,9 +105,11 @@ Christian Petrov Christopher Brown Christopher Mitchell Christopher Pfohl +Chunliang Lyu ciaranj CodeAnimal coderaiser +Cole R Lawrence ComFreek Curtis Gagliardi dagsta @@ -144,6 +146,7 @@ Doug Wikle Drew Bratcher Drew Hintz Drew Khoury +Drini Cami Dror BG duralog eborden @@ -152,6 +155,7 @@ ekhaled Elisée Enam Mijbah Noor Eric Allam +Erik Welander eustas Fabien O'Carroll Fabio Zendhi Nagao @@ -219,6 +223,7 @@ Jan Jongboom jankeromnes Jan Keromnes Jan Odvarko +Jan Schär Jan T. Sott Jared Forsyth Jason @@ -234,7 +239,9 @@ jeffkenton Jeff Pickhardt jem (graphite) Jeremy Parmenter +Jim JobJob +jochenberger Jochen Berger Johan Ask John Connor @@ -258,6 +265,7 @@ ju1ius Juan Benavides Romero Jucovschi Constantin Juho Vuori +Justin Andresen Justin Hileman jwallers@gmail.com kaniga @@ -337,6 +345,7 @@ Max Kirsch Max Schaefer Max Xiantu mbarkhau +McBrainy melpon Metatheos Micah Dubinko @@ -376,6 +385,7 @@ Nicholas Bollweg Nicholas Bollweg (Nick) Nick Kreeger Nick Small +Nicolò Ribaudo Niels van Groningen nightwing Nikita Beloglazov @@ -492,6 +502,7 @@ Tom MacWright Tony Jian Travis Heppe Triangle717 +TSUYUSATO Kitsune twifkak Vestimir Markov vf diff --git a/doc/compress.html b/doc/compress.html index 74c68347..1f1d0f07 100644 --- a/doc/compress.html +++ b/doc/compress.html @@ -36,6 +36,7 @@

    Script compression helper

    Version: + + + + + From 07bcf88d8606b6aa75951862abe504fcf83f64b0 Mon Sep 17 00:00:00 2001 From: Marijn Haverbeke Date: Tue, 29 Dec 2015 09:32:54 +0100 Subject: [PATCH 0086/1790] [haskell-literate mode] Integrate Issue #3730 --- doc/compress.html | 1 + mode/haskell-literate/haskell-literate.js | 27 ++++++++++++++--------- mode/haskell-literate/index.html | 6 +++++ mode/index.html | 2 +- mode/meta.js | 1 + 5 files changed, 25 insertions(+), 12 deletions(-) diff --git a/doc/compress.html b/doc/compress.html index 1f1d0f07..25379782 100644 --- a/doc/compress.html +++ b/doc/compress.html @@ -149,6 +149,7 @@

    Script compression helper

    + diff --git a/mode/haskell-literate/haskell-literate.js b/mode/haskell-literate/haskell-literate.js index f50d2fb1..f51e96b6 100644 --- a/mode/haskell-literate/haskell-literate.js +++ b/mode/haskell-literate/haskell-literate.js @@ -9,30 +9,35 @@ else // Plain browser env mod(CodeMirror) })(function (CodeMirror) { - CodeMirror.defineMode("haskell-literate", function (config) { - var haskellMode = CodeMirror.getMode(config, "haskell") + "use strict" + + CodeMirror.defineMode("haskell-literate", function (config, parserConfig) { + var baseMode = CodeMirror.getMode(config, (parserConfig && parserConfig.base) || "haskell") + return { startState: function () { return { - haskellCode: false, - haskellState: CodeMirror.startState(haskellMode) + inCode: false, + baseState: CodeMirror.startState(baseMode) } }, token: function (stream, state) { - if ((stream.sol() && stream.next() == '>') || state.haskellCode) { - state.haskellCode = true - return haskellMode.token(stream, state.haskellState) + if (stream.sol()) { + if (state.inCode = stream.eat(">")) + return "meta" + } + if (state.inCode) { + return baseMode.token(stream, state.baseState) } else { stream.skipToEnd() return "comment" } }, - blankLine: function (state) { - state.haskellCode = false - }, innerMode: function (state) { - return {state: state.haskellState, mode: haskellMode}; + return state.inCode ? {state: state.baseState, mode: baseMode} : null } } }) + + CodeMirror.defineMIME("text/x-literate-haskell", "haskell-literate") }) diff --git a/mode/haskell-literate/index.html b/mode/haskell-literate/index.html index 05172477..8c9bc60d 100644 --- a/mode/haskell-literate/index.html +++ b/mode/haskell-literate/index.html @@ -269,6 +269,12 @@

    Haskell literate mode

    +

    MIME types + defined: text/x-literate-haskell.

    + +

    Parser configuration parameters recognized: base to + set the base mode (defaults to "haskell").

    + diff --git a/mode/index.html b/mode/index.html index 072b89bd..2a159ec5 100644 --- a/mode/index.html +++ b/mode/index.html @@ -68,7 +68,7 @@

    Language modes

  • Groovy
  • HAML
  • Handlebars
  • -
  • Haskell
  • +
  • Haskell (Literate)
  • Haxe
  • HTML embedded (JSP, ASP.NET)
  • HTML mixed-mode
  • diff --git a/mode/meta.js b/mode/meta.js index 69e2a3ef..e4b97360 100644 --- a/mode/meta.js +++ b/mode/meta.js @@ -56,6 +56,7 @@ {name: "Groovy", mime: "text/x-groovy", mode: "groovy", ext: ["groovy"]}, {name: "HAML", mime: "text/x-haml", mode: "haml", ext: ["haml"]}, {name: "Haskell", mime: "text/x-haskell", mode: "haskell", ext: ["hs"]}, + {name: "Haskell (Literate)", mime: "text/x-literate-haskell", mode: "haskell-literate", ext: ["lhs"]}, {name: "Haxe", mime: "text/x-haxe", mode: "haxe", ext: ["hx"]}, {name: "HXML", mime: "text/x-hxml", mode: "haxe", ext: ["hxml"]}, {name: "ASP.NET", mime: "application/x-aspx", mode: "htmlembedded", ext: ["aspx"], alias: ["asp", "aspx"]}, From b3f9487046e37facd64196380ebdd8639efc57b5 Mon Sep 17 00:00:00 2001 From: Marijn Haverbeke Date: Tue, 29 Dec 2015 13:33:29 +0100 Subject: [PATCH 0087/1790] [jsx mode] Add Closes #3742 Closes #3744 --- doc/compress.html | 2 +- mode/index.html | 2 +- mode/javascript/javascript.js | 16 +++++-- mode/jsx/index.html | 89 +++++++++++++++++++++++++++++++++++ mode/jsx/jsx.js | 85 +++++++++++++++++++++++++++++++++ mode/jsx/test.js | 35 ++++++++++++++ mode/meta.js | 1 + mode/xml/xml.js | 25 ++++++---- test/index.html | 2 + 9 files changed, 243 insertions(+), 14 deletions(-) create mode 100644 mode/jsx/index.html create mode 100644 mode/jsx/jsx.js create mode 100644 mode/jsx/test.js diff --git a/doc/compress.html b/doc/compress.html index 25379782..a79b3097 100644 --- a/doc/compress.html +++ b/doc/compress.html @@ -159,7 +159,7 @@

    Script compression helper

    - + diff --git a/mode/index.html b/mode/index.html index 2a159ec5..a6c293ec 100644 --- a/mode/index.html +++ b/mode/index.html @@ -76,7 +76,7 @@

    Language modes

  • IDL
  • Java
  • Jade
  • -
  • JavaScript
  • +
  • JavaScript (JSX)
  • Jinja2
  • Julia
  • Kotlin
  • diff --git a/mode/javascript/javascript.js b/mode/javascript/javascript.js index d4ae6687..c8515477 100644 --- a/mode/javascript/javascript.js +++ b/mode/javascript/javascript.js @@ -13,6 +13,11 @@ })(function(CodeMirror) { "use strict"; +function expressionAllowed(stream, state, backUp) { + return /^(?:operator|sof|keyword c|case|new|[\[{}\(,;:])$/.test(state.lastType) || + (state.lastType == "quasi" && /\{\s*$/.test(stream.string.slice(0, stream.pos - (backUp || 0)))) +} + CodeMirror.defineMode("javascript", function(config, parserConfig) { var indentUnit = config.indentUnit; var statementIndent = parserConfig.statementIndent; @@ -126,8 +131,7 @@ CodeMirror.defineMode("javascript", function(config, parserConfig) { } else if (stream.eat("/")) { stream.skipToEnd(); return ret("comment", "comment"); - } else if (/^(?:operator|sof|keyword c|case|new|[\[{}\(,;:])$/.test(state.lastType) || - (state.lastType == "quasi" && /\{\s*$/.test(stream.string.slice(0, stream.pos - 1)))) { + } else if (expressionAllowed(stream, state, 1)) { readRegexp(stream); stream.match(/^\b(([gimyu])(?![gimyu]*\2))+\b/); return ret("regexp", "string-2"); @@ -711,7 +715,13 @@ CodeMirror.defineMode("javascript", function(config, parserConfig) { helperType: jsonMode ? "json" : "javascript", jsonldMode: jsonldMode, - jsonMode: jsonMode + jsonMode: jsonMode, + + expressionAllowed: expressionAllowed, + skipExpression: function(state) { + var top = state.cc[state.cc.length - 1] + if (top == expression || top == expressionNoComma) state.cc.pop() + } }; }); diff --git a/mode/jsx/index.html b/mode/jsx/index.html new file mode 100644 index 00000000..cb51edb3 --- /dev/null +++ b/mode/jsx/index.html @@ -0,0 +1,89 @@ + + +CodeMirror: JSX mode + + + + + + + + + + + +
    +

    JSX mode

    + +
    + + + +

    JSX Mode for React's +JavaScript syntax extension.

    + +

    MIME types defined: text/jsx.

    + +
    diff --git a/mode/jsx/jsx.js b/mode/jsx/jsx.js new file mode 100644 index 00000000..c3d227a8 --- /dev/null +++ b/mode/jsx/jsx.js @@ -0,0 +1,85 @@ +// 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"), require("../xml/xml"), require("../javascript/javascript")) + else if (typeof define == "function" && define.amd) // AMD + define(["../../lib/codemirror", "../xml/xml", "../javascript/javascript"], mod) + else // Plain browser env + mod(CodeMirror) +})(function(CodeMirror) { + "use strict" + + function copyContext(context) { + return {state: CodeMirror.copyState(context.mode, context.state), + mode: context.mode, + depth: context.depth, + prev: context.prev && copyContext(context.prev)} + } + + CodeMirror.defineMode("jsx", function(config) { + var xmlMode = CodeMirror.getMode(config, "xml") + var jsMode = CodeMirror.getMode(config, "javascript") + + return { + startState: function() { + return {context: {state: CodeMirror.startState(jsMode), mode: jsMode}} + }, + + copyState: function(state) { + return {context: copyContext(state.context)} + }, + + token: function(stream, state) { + var cx = state.context + if (cx.mode == xmlMode) { + if (stream.peek() == "{") { + xmlMode.skipAttribute(cx.state) + state.context = {state: CodeMirror.startState(jsMode, xmlMode.indent(cx.state, "")), + mode: jsMode, + depth: 1, + prev: state.context} + return jsMode.token(stream, state.context.state) + } else { // FIXME skip attribute + var style = xmlMode.token(stream, cx.state), cur, brace + if (/\btag\b/.test(style) && !cx.state.context && /^\/?>$/.test(stream.current())) + state.context = state.context.prev + else if (!style && (brace = (cur = stream.current()).indexOf("{")) > -1) + stream.backUp(cur.length - brace) + return style + } + } else { // jsMode + if (stream.peek() == "<" && jsMode.expressionAllowed(stream, cx.state)) { + jsMode.skipExpression(cx.state) + state.context = {state: CodeMirror.startState(xmlMode, jsMode.indent(cx.state, "")), + mode: xmlMode, + prev: state.context} + return xmlMode.token(stream, state.context.state) + } else { + var style = jsMode.token(stream, cx.state) + if (!style && cx.depth != null) { + var cur = stream.current() + if (cur == "{") { + cx.depth++ + } else if (cur == "}") { + if (--cx.depth == 0) state.context = state.context.prev + } + } + return style + } + } + }, + + indent: function(state, textAfter, fullLine) { + return state.context.mode.indent(state.context.state, textAfter, fullLine) + }, + + innerMode: function(state) { + return state.context[state.context.length - 1] + } + } + }, "xml", "javascript") + + CodeMirror.defineMIME("text/jsx", "jsx") +}) diff --git a/mode/jsx/test.js b/mode/jsx/test.js new file mode 100644 index 00000000..63fafb27 --- /dev/null +++ b/mode/jsx/test.js @@ -0,0 +1,35 @@ +// CodeMirror, copyright (c) by Marijn Haverbeke and others +// Distributed under an MIT license: http://codemirror.net/LICENSE + +(function() { + var mode = CodeMirror.getMode({indentUnit: 2}, "jsx") + function MT(name) { test.mode(name, mode, Array.prototype.slice.call(arguments, 1)) } + + MT("selfclose", + "[keyword var] [def x] [operator =] [bracket&tag <] [tag foo] [bracket&tag />] [operator +] [number 1];") + + MT("openclose", + "([bracket&tag <][tag foo][bracket&tag >]hello [atom &][bracket&tag ][operator ++])") + + MT("attr", + "([bracket&tag <][tag foo] [attribute abc]=[string 'value'][bracket&tag >]hello [atom &][bracket&tag ][operator ++])") + + MT("braced_attr", + "([bracket&tag <][tag foo] [attribute abc]={[number 10]}[bracket&tag >]hello [atom &][bracket&tag ][operator ++])") + + MT("braced_text", + "([bracket&tag <][tag foo][bracket&tag >]hello {[number 10]} [atom &][bracket&tag ][operator ++])") + + MT("nested_tag", + "([bracket&tag <][tag foo][bracket&tag ><][tag bar][bracket&tag >][operator ++])") + + MT("nested_jsx", + "[keyword return] (", + " [bracket&tag <][tag foo][bracket&tag >]", + " say {[number 1] [operator +] [bracket&tag <][tag bar] [attribute attr]={[number 10]}[bracket&tag />]}!", + " [bracket&tag ][operator ++]", + ")") + + MT("preserve_js_context", + "[variable x] [operator =] [string-2 `quasi${][bracket&tag <][tag foo][bracket&tag />][string-2 }quoted`]") +})() diff --git a/mode/meta.js b/mode/meta.js index e4b97360..49520717 100644 --- a/mode/meta.js +++ b/mode/meta.js @@ -70,6 +70,7 @@ mode: "javascript", ext: ["js"], alias: ["ecmascript", "js", "node"]}, {name: "JSON", mimes: ["application/json", "application/x-json"], mode: "javascript", ext: ["json", "map"], alias: ["json5"]}, {name: "JSON-LD", mime: "application/ld+json", mode: "javascript", ext: ["jsonld"], alias: ["jsonld"]}, + {name: "JSX", mime: "text/jsx", mode: "jsx", ext: ["jsx"]}, {name: "Jinja2", mime: "null", mode: "jinja2"}, {name: "Julia", mime: "text/x-julia", mode: "julia", ext: ["jl"]}, {name: "Kotlin", mime: "text/x-kotlin", mode: "clike", ext: ["kt"]}, diff --git a/mode/xml/xml.js b/mode/xml/xml.js index 5ad21720..92808e14 100644 --- a/mode/xml/xml.js +++ b/mode/xml/xml.js @@ -297,12 +297,14 @@ CodeMirror.defineMode("xml", function(config, parserConfig) { } return { - startState: function() { - return {tokenize: inText, - state: baseState, - indented: 0, - tagName: null, tagStart: null, - context: null}; + startState: function(baseIndent) { + var state = {tokenize: inText, + state: baseState, + indented: baseIndent || 0, + tagName: null, tagStart: null, + context: null} + if (baseIndent != null) state.baseIndent = baseIndent + return state }, token: function(stream, state) { @@ -362,10 +364,10 @@ CodeMirror.defineMode("xml", function(config, parserConfig) { break; } } - while (context && !context.startOfLine) + while (context && context.prev && !context.startOfLine) context = context.prev; if (context) return context.indent + indentUnit; - else return 0; + else return state.baseIndent || 0; }, electricInput: /<\/[\s\w:]+>$/, @@ -373,7 +375,12 @@ CodeMirror.defineMode("xml", function(config, parserConfig) { blockCommentEnd: "-->", configuration: parserConfig.htmlMode ? "html" : "xml", - helperType: parserConfig.htmlMode ? "html" : "xml" + helperType: parserConfig.htmlMode ? "html" : "xml", + + skipAttribute: function(state) { + if (state.state == attrValueState) + state.state = attrState + } }; }); diff --git a/test/index.html b/test/index.html index b0b1fa97..3e227a06 100644 --- a/test/index.html +++ b/test/index.html @@ -23,6 +23,7 @@ + @@ -107,6 +108,7 @@

    Test Suite

    + From b42563cadcfb6a490732090757dae38569ba6068 Mon Sep 17 00:00:00 2001 From: Marijn Haverbeke Date: Tue, 29 Dec 2015 22:16:37 +0100 Subject: [PATCH 0088/1790] [jsx mode] Support JS comments Issue #3745 --- mode/jsx/jsx.js | 43 +++++++++++++++++++++++++++---------------- mode/jsx/test.js | 9 +++++++++ 2 files changed, 36 insertions(+), 16 deletions(-) diff --git a/mode/jsx/jsx.js b/mode/jsx/jsx.js index c3d227a8..ebae7844 100644 --- a/mode/jsx/jsx.js +++ b/mode/jsx/jsx.js @@ -11,11 +11,15 @@ })(function(CodeMirror) { "use strict" + function Context(state, mode, depth, prev) { + this.state = state; this.mode = mode; this.depth = depth; this.prev = prev + } + function copyContext(context) { - return {state: CodeMirror.copyState(context.mode, context.state), - mode: context.mode, - depth: context.depth, - prev: context.prev && copyContext(context.prev)} + return new Context(CodeMirror.copyState(context.mode, context.state), + context.mode, + context.depth, + context.prev && copyContext(context.prev)) } CodeMirror.defineMode("jsx", function(config) { @@ -24,7 +28,7 @@ return { startState: function() { - return {context: {state: CodeMirror.startState(jsMode), mode: jsMode}} + return {context: new Context(CodeMirror.startState(jsMode), jsMode)} }, copyState: function(state) { @@ -34,27 +38,34 @@ token: function(stream, state) { var cx = state.context if (cx.mode == xmlMode) { - if (stream.peek() == "{") { + if (cx.depth) { // Inside a JS /* */ comment + if (stream.match(/^.*?\*\//)) cx.depth = 0 + else stream.skipToEnd() + return "comment" + } else if (stream.peek() == "{") { xmlMode.skipAttribute(cx.state) - state.context = {state: CodeMirror.startState(jsMode, xmlMode.indent(cx.state, "")), - mode: jsMode, - depth: 1, - prev: state.context} + state.context = new Context(CodeMirror.startState(jsMode, xmlMode.indent(cx.state, "")), + jsMode, 1, state.context) return jsMode.token(stream, state.context.state) + } else if (stream.match("//")) { + stream.skipToEnd() + return "comment" + } else if (stream.match("/*")) { + cx.depth = 1 + return this.token(stream, state) } else { // FIXME skip attribute - var style = xmlMode.token(stream, cx.state), cur, brace + var style = xmlMode.token(stream, cx.state), cur, stop if (/\btag\b/.test(style) && !cx.state.context && /^\/?>$/.test(stream.current())) state.context = state.context.prev - else if (!style && (brace = (cur = stream.current()).indexOf("{")) > -1) - stream.backUp(cur.length - brace) + else if (!style && (stop = (cur = stream.current()).search(/\{|\/[*\/]/)) > -1) + stream.backUp(cur.length - stop) return style } } else { // jsMode if (stream.peek() == "<" && jsMode.expressionAllowed(stream, cx.state)) { jsMode.skipExpression(cx.state) - state.context = {state: CodeMirror.startState(xmlMode, jsMode.indent(cx.state, "")), - mode: xmlMode, - prev: state.context} + state.context = new Context(CodeMirror.startState(xmlMode, jsMode.indent(cx.state, "")), + xmlMode, 0, state.context) return xmlMode.token(stream, state.context.state) } else { var style = jsMode.token(stream, cx.state) diff --git a/mode/jsx/test.js b/mode/jsx/test.js index 63fafb27..e45f67cd 100644 --- a/mode/jsx/test.js +++ b/mode/jsx/test.js @@ -32,4 +32,13 @@ MT("preserve_js_context", "[variable x] [operator =] [string-2 `quasi${][bracket&tag <][tag foo][bracket&tag />][string-2 }quoted`]") + + MT("line_comment", + "([bracket&tag <][tag foo][bracket&tag >] [comment // hello]", + " [bracket&tag ][operator ++])") + + MT("block_comment", + "([bracket&tag <][tag foo][bracket&tag >] [comment /* hello]", + "[comment line 2]", + "[comment line 3 */] [bracket&tag ][operator ++])") })() From e8ad6773ba672017a50af029b260eafbc93c5f46 Mon Sep 17 00:00:00 2001 From: Marijn Haverbeke Date: Tue, 29 Dec 2015 22:30:28 +0100 Subject: [PATCH 0089/1790] [xml mode] Allow more direct access to configuration Use it to enable value-less attributes in the JSX mode Issue #3745 --- mode/jsx/jsx.js | 2 +- mode/jsx/test.js | 4 ++ mode/xml/xml.js | 124 ++++++++++++++++++++++++----------------------- 3 files changed, 68 insertions(+), 62 deletions(-) diff --git a/mode/jsx/jsx.js b/mode/jsx/jsx.js index ebae7844..d38d16c8 100644 --- a/mode/jsx/jsx.js +++ b/mode/jsx/jsx.js @@ -23,7 +23,7 @@ } CodeMirror.defineMode("jsx", function(config) { - var xmlMode = CodeMirror.getMode(config, "xml") + var xmlMode = CodeMirror.getMode(config, {name: "xml", allowMissing: true}) var jsMode = CodeMirror.getMode(config, "javascript") return { diff --git a/mode/jsx/test.js b/mode/jsx/test.js index e45f67cd..c0032a4c 100644 --- a/mode/jsx/test.js +++ b/mode/jsx/test.js @@ -41,4 +41,8 @@ "([bracket&tag <][tag foo][bracket&tag >] [comment /* hello]", "[comment line 2]", "[comment line 3 */] [bracket&tag ][operator ++])") + + MT("missing_attr", + "([bracket&tag <][tag foo] [attribute selected][bracket&tag />][operator ++])") + })() diff --git a/mode/xml/xml.js b/mode/xml/xml.js index 92808e14..014f7d84 100644 --- a/mode/xml/xml.js +++ b/mode/xml/xml.js @@ -11,54 +11,56 @@ })(function(CodeMirror) { "use strict"; -CodeMirror.defineMode("xml", function(config, parserConfig) { - var indentUnit = config.indentUnit; - var multilineTagIndentFactor = parserConfig.multilineTagIndentFactor || 1; - var multilineTagIndentPastTag = parserConfig.multilineTagIndentPastTag; - if (multilineTagIndentPastTag == null) multilineTagIndentPastTag = true; +var htmlConfig = { + autoSelfClosers: {'area': true, 'base': true, 'br': true, 'col': true, 'command': true, + 'embed': true, 'frame': true, 'hr': true, 'img': true, 'input': true, + 'keygen': true, 'link': true, 'meta': true, 'param': true, 'source': true, + 'track': true, 'wbr': true, 'menuitem': true}, + implicitlyClosed: {'dd': true, 'li': true, 'optgroup': true, 'option': true, 'p': true, + 'rp': true, 'rt': true, 'tbody': true, 'td': true, 'tfoot': true, + 'th': true, 'tr': true}, + contextGrabbers: { + 'dd': {'dd': true, 'dt': true}, + 'dt': {'dd': true, 'dt': true}, + 'li': {'li': true}, + 'option': {'option': true, 'optgroup': true}, + 'optgroup': {'optgroup': true}, + 'p': {'address': true, 'article': true, 'aside': true, 'blockquote': true, 'dir': true, + 'div': true, 'dl': true, 'fieldset': true, 'footer': true, 'form': true, + 'h1': true, 'h2': true, 'h3': true, 'h4': true, 'h5': true, 'h6': true, + 'header': true, 'hgroup': true, 'hr': true, 'menu': true, 'nav': true, 'ol': true, + 'p': true, 'pre': true, 'section': true, 'table': true, 'ul': true}, + 'rp': {'rp': true, 'rt': true}, + 'rt': {'rp': true, 'rt': true}, + 'tbody': {'tbody': true, 'tfoot': true}, + 'td': {'td': true, 'th': true}, + 'tfoot': {'tbody': true}, + 'th': {'td': true, 'th': true}, + 'thead': {'tbody': true, 'tfoot': true}, + 'tr': {'tr': true} + }, + doNotIndent: {"pre": true}, + allowUnquoted: true, + allowMissing: true, + caseFold: true +} - 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, - 'keygen': true, 'link': true, 'meta': true, 'param': true, 'source': true, - 'track': true, 'wbr': true, 'menuitem': true}, - implicitlyClosed: {'dd': true, 'li': true, 'optgroup': true, 'option': true, 'p': true, - 'rp': true, 'rt': true, 'tbody': true, 'td': true, 'tfoot': true, - 'th': true, 'tr': true}, - contextGrabbers: { - 'dd': {'dd': true, 'dt': true}, - 'dt': {'dd': true, 'dt': true}, - 'li': {'li': true}, - 'option': {'option': true, 'optgroup': true}, - 'optgroup': {'optgroup': true}, - 'p': {'address': true, 'article': true, 'aside': true, 'blockquote': true, 'dir': true, - 'div': true, 'dl': true, 'fieldset': true, 'footer': true, 'form': true, - 'h1': true, 'h2': true, 'h3': true, 'h4': true, 'h5': true, 'h6': true, - 'header': true, 'hgroup': true, 'hr': true, 'menu': true, 'nav': true, 'ol': true, - 'p': true, 'pre': true, 'section': true, 'table': true, 'ul': true}, - 'rp': {'rp': true, 'rt': true}, - 'rt': {'rp': true, 'rt': true}, - 'tbody': {'tbody': true, 'tfoot': true}, - 'td': {'td': true, 'th': true}, - 'tfoot': {'tbody': true}, - 'th': {'td': true, 'th': true}, - 'thead': {'tbody': true, 'tfoot': true}, - 'tr': {'tr': true} - }, - doNotIndent: {"pre": true}, - allowUnquoted: true, - allowMissing: true, - caseFold: true - } : { - autoSelfClosers: {}, - implicitlyClosed: {}, - contextGrabbers: {}, - doNotIndent: {}, - allowUnquoted: false, - allowMissing: false, - caseFold: false - }; - var alignCDATA = parserConfig.alignCDATA; +var xmlConfig = { + autoSelfClosers: {}, + implicitlyClosed: {}, + contextGrabbers: {}, + doNotIndent: {}, + allowUnquoted: false, + allowMissing: false, + caseFold: false +} + +CodeMirror.defineMode("xml", function(editorConf, config_) { + var indentUnit = editorConf.indentUnit + var config = {} + var defaults = config_.htmlMode ? htmlConfig : xmlConfig + for (var prop in defaults) config[prop] = defaults[prop] + for (var prop in config_) config[prop] = config_[prop] // Return variables for tokenizers var type, setStyle; @@ -188,7 +190,7 @@ CodeMirror.defineMode("xml", function(config, parserConfig) { this.tagName = tagName; this.indent = state.indented; this.startOfLine = startOfLine; - if (Kludges.doNotIndent.hasOwnProperty(tagName) || (state.context && state.context.noIndent)) + if (config.doNotIndent.hasOwnProperty(tagName) || (state.context && state.context.noIndent)) this.noIndent = true; } function popContext(state) { @@ -201,8 +203,8 @@ CodeMirror.defineMode("xml", function(config, parserConfig) { return; } parentTagName = state.context.tagName; - if (!Kludges.contextGrabbers.hasOwnProperty(parentTagName) || - !Kludges.contextGrabbers[parentTagName].hasOwnProperty(nextTagName)) { + if (!config.contextGrabbers.hasOwnProperty(parentTagName) || + !config.contextGrabbers[parentTagName].hasOwnProperty(nextTagName)) { return; } popContext(state); @@ -233,7 +235,7 @@ CodeMirror.defineMode("xml", function(config, parserConfig) { if (type == "word") { var tagName = stream.current(); if (state.context && state.context.tagName != tagName && - Kludges.implicitlyClosed.hasOwnProperty(state.context.tagName)) + config.implicitlyClosed.hasOwnProperty(state.context.tagName)) popContext(state); if (state.context && state.context.tagName == tagName) { setStyle = "tag"; @@ -269,7 +271,7 @@ CodeMirror.defineMode("xml", function(config, parserConfig) { var tagName = state.tagName, tagStart = state.tagStart; state.tagName = state.tagStart = null; if (type == "selfcloseTag" || - Kludges.autoSelfClosers.hasOwnProperty(tagName)) { + config.autoSelfClosers.hasOwnProperty(tagName)) { maybePopContext(state, tagName); } else { maybePopContext(state, tagName); @@ -282,12 +284,12 @@ CodeMirror.defineMode("xml", function(config, parserConfig) { } function attrEqState(type, stream, state) { if (type == "equals") return attrValueState; - if (!Kludges.allowMissing) setStyle = "error"; + if (!config.allowMissing) setStyle = "error"; return attrState(type, stream, state); } function attrValueState(type, stream, state) { if (type == "string") return attrContinuedState; - if (type == "word" && Kludges.allowUnquoted) {setStyle = "string"; return attrState;} + if (type == "word" && config.allowUnquoted) {setStyle = "string"; return attrState;} setStyle = "error"; return attrState(type, stream, state); } @@ -337,19 +339,19 @@ CodeMirror.defineMode("xml", function(config, parserConfig) { return fullLine ? fullLine.match(/^(\s*)/)[0].length : 0; // Indent the starts of attribute names. if (state.tagName) { - if (multilineTagIndentPastTag) + if (config.multilineTagIndentPastTag !== false) return state.tagStart + state.tagName.length + 2; else - return state.tagStart + indentUnit * multilineTagIndentFactor; + return state.tagStart + indentUnit * (config.multilineTagIndentFactor || 1); } - if (alignCDATA && /", - configuration: parserConfig.htmlMode ? "html" : "xml", - helperType: parserConfig.htmlMode ? "html" : "xml", + configuration: config.htmlMode ? "html" : "xml", + helperType: config.htmlMode ? "html" : "xml", skipAttribute: function(state) { if (state.state == attrValueState) From e3dc9731678b2f4b33b5faf9ffb90332d3c97c96 Mon Sep 17 00:00:00 2001 From: Marijn Haverbeke Date: Tue, 29 Dec 2015 22:49:02 +0100 Subject: [PATCH 0090/1790] [jsx mode] Improve indentation of nested JavaScript Kludge the xml indentation to ignore inside-tag positions, and properly set start indentation state in javascript mode. Issue #3745 --- mode/javascript/javascript.js | 2 +- mode/jsx/jsx.js | 5 ++++- 2 files changed, 5 insertions(+), 2 deletions(-) diff --git a/mode/javascript/javascript.js b/mode/javascript/javascript.js index c8515477..ee6e4016 100644 --- a/mode/javascript/javascript.js +++ b/mode/javascript/javascript.js @@ -659,7 +659,7 @@ CodeMirror.defineMode("javascript", function(config, parserConfig) { lexical: new JSLexical((basecolumn || 0) - indentUnit, 0, "block", false), localVars: parserConfig.localVars, context: parserConfig.localVars && {vars: parserConfig.localVars}, - indented: 0 + indented: basecolumn || 0 }; if (parserConfig.globalVars && typeof parserConfig.globalVars == "object") state.globalVars = parserConfig.globalVars; diff --git a/mode/jsx/jsx.js b/mode/jsx/jsx.js index d38d16c8..ccf2ee74 100644 --- a/mode/jsx/jsx.js +++ b/mode/jsx/jsx.js @@ -44,8 +44,11 @@ return "comment" } else if (stream.peek() == "{") { xmlMode.skipAttribute(cx.state) + var tagName = cx.state.tagName + cx.state.tagName = null state.context = new Context(CodeMirror.startState(jsMode, xmlMode.indent(cx.state, "")), jsMode, 1, state.context) + cx.state.tagName = tagName return jsMode.token(stream, state.context.state) } else if (stream.match("//")) { stream.skipToEnd() @@ -87,7 +90,7 @@ }, innerMode: function(state) { - return state.context[state.context.length - 1] + return state.context } } }, "xml", "javascript") From d103ebfc453193406d3d01c8fbf638cadd00793d Mon Sep 17 00:00:00 2001 From: Marijn Haverbeke Date: Tue, 29 Dec 2015 22:54:28 +0100 Subject: [PATCH 0091/1790] [jsx mode] Add test case for spread syntax --- mode/jsx/test.js | 11 ++++++++++- 1 file changed, 10 insertions(+), 1 deletion(-) diff --git a/mode/jsx/test.js b/mode/jsx/test.js index c0032a4c..1d2fe498 100644 --- a/mode/jsx/test.js +++ b/mode/jsx/test.js @@ -44,5 +44,14 @@ MT("missing_attr", "([bracket&tag <][tag foo] [attribute selected][bracket&tag />][operator ++])") - + + MT("indent_js", + "([bracket&tag <][tag foo][bracket&tag >]", + " [bracket&tag <][tag bar] [attribute baz]={[keyword function]() {", + " [keyword return] [number 10]", + " }}[bracket&tag />]", + " [bracket&tag ])") + + MT("spread", + "([bracket&tag <][tag foo] [attribute bar]={[meta ...][variable baz] [operator /][number 2]}[bracket&tag />])") })() From bec6669991a226de6c8b8f6046f2296ac346b68c Mon Sep 17 00:00:00 2001 From: Marijn Haverbeke Date: Wed, 30 Dec 2015 11:52:33 +0100 Subject: [PATCH 0092/1790] [jsx mode] Only recognize comments inside of tags Issue #3745 --- mode/jsx/jsx.js | 35 ++++++++++++++++++++++------------- mode/jsx/test.js | 17 +++++++++++++---- 2 files changed, 35 insertions(+), 17 deletions(-) diff --git a/mode/jsx/jsx.js b/mode/jsx/jsx.js index ccf2ee74..6b7b6c24 100644 --- a/mode/jsx/jsx.js +++ b/mode/jsx/jsx.js @@ -11,6 +11,9 @@ })(function(CodeMirror) { "use strict" + // Depth means the amount of open braces in JS context, in XML + // context 0 means not in tag, 1 means in tag, and 2 means in tag + // and js block comment. function Context(state, mode, depth, prev) { this.state = state; this.mode = mode; this.depth = depth; this.prev = prev } @@ -23,7 +26,7 @@ } CodeMirror.defineMode("jsx", function(config) { - var xmlMode = CodeMirror.getMode(config, {name: "xml", allowMissing: true}) + var xmlMode = CodeMirror.getMode(config, {name: "xml", allowMissing: true, multilineTagIndentPastTag: false}) var jsMode = CodeMirror.getMode(config, "javascript") return { @@ -38,8 +41,8 @@ token: function(stream, state) { var cx = state.context if (cx.mode == xmlMode) { - if (cx.depth) { // Inside a JS /* */ comment - if (stream.match(/^.*?\*\//)) cx.depth = 0 + if (cx.depth == 2) { // Inside a JS /* */ comment + if (stream.match(/^.*?\*\//)) cx.depth = 1 else stream.skipToEnd() return "comment" } else if (stream.peek() == "{") { @@ -47,21 +50,27 @@ var tagName = cx.state.tagName cx.state.tagName = null state.context = new Context(CodeMirror.startState(jsMode, xmlMode.indent(cx.state, "")), - jsMode, 1, state.context) + jsMode, 0, state.context) cx.state.tagName = tagName - return jsMode.token(stream, state.context.state) - } else if (stream.match("//")) { + return this.token(stream, state) + } else if (cx.depth == 1 && stream.match("//")) { stream.skipToEnd() return "comment" - } else if (stream.match("/*")) { - cx.depth = 1 + } else if (cx.depth == 1 && stream.match("/*")) { + cx.depth = 2 return this.token(stream, state) } else { // FIXME skip attribute - var style = xmlMode.token(stream, cx.state), cur, stop - if (/\btag\b/.test(style) && !cx.state.context && /^\/?>$/.test(stream.current())) - state.context = state.context.prev - else if (!style && (stop = (cur = stream.current()).search(/\{|\/[*\/]/)) > -1) + var style = xmlMode.token(stream, cx.state), cur = stream.current(), stop + if (/\btag\b/.test(style)) { + if (/>$/.test(cur)) { + if (cx.state.context) cx.depth = 0 + else state.context = state.context.prev + } else if (/^ -1) { stream.backUp(cur.length - stop) + } return style } } else { // jsMode @@ -69,7 +78,7 @@ jsMode.skipExpression(cx.state) state.context = new Context(CodeMirror.startState(xmlMode, jsMode.indent(cx.state, "")), xmlMode, 0, state.context) - return xmlMode.token(stream, state.context.state) + return this.token(stream, state) } else { var style = jsMode.token(stream, cx.state) if (!style && cx.depth != null) { diff --git a/mode/jsx/test.js b/mode/jsx/test.js index 1d2fe498..ee601651 100644 --- a/mode/jsx/test.js +++ b/mode/jsx/test.js @@ -34,13 +34,22 @@ "[variable x] [operator =] [string-2 `quasi${][bracket&tag <][tag foo][bracket&tag />][string-2 }quoted`]") MT("line_comment", - "([bracket&tag <][tag foo][bracket&tag >] [comment // hello]", + "([bracket&tag <][tag foo] [comment // hello]", + " [bracket&tag >][operator ++])") + + MT("line_comment_not_in_tag", + "([bracket&tag <][tag foo][bracket&tag >] // hello", " [bracket&tag ][operator ++])") MT("block_comment", - "([bracket&tag <][tag foo][bracket&tag >] [comment /* hello]", - "[comment line 2]", - "[comment line 3 */] [bracket&tag ][operator ++])") + "([bracket&tag <][tag foo] [comment /* hello]", + "[comment line 2]", + "[comment line 3 */] [bracket&tag >][operator ++])") + + MT("block_comment_not_in_tag", + "([bracket&tag <][tag foo][bracket&tag >]/* hello", + " line 2", + " line 3 */ [bracket&tag ][operator ++])") MT("missing_attr", "([bracket&tag <][tag foo] [attribute selected][bracket&tag />][operator ++])") From 8870302a3f2efec00e68d32f6a9d5af959210241 Mon Sep 17 00:00:00 2001 From: Marijn Haverbeke Date: Wed, 30 Dec 2015 12:02:21 +0100 Subject: [PATCH 0093/1790] [jsx mode] Support tag attributes Issue #3745 --- mode/jsx/jsx.js | 134 ++++++++++++++++++++++++++++------------------- mode/jsx/test.js | 3 ++ 2 files changed, 82 insertions(+), 55 deletions(-) diff --git a/mode/jsx/jsx.js b/mode/jsx/jsx.js index 6b7b6c24..5f6afc18 100644 --- a/mode/jsx/jsx.js +++ b/mode/jsx/jsx.js @@ -29,6 +29,84 @@ var xmlMode = CodeMirror.getMode(config, {name: "xml", allowMissing: true, multilineTagIndentPastTag: false}) var jsMode = CodeMirror.getMode(config, "javascript") + function flatXMLIndent(state) { + var tagName = state.tagName + state.tagName = null + var result = xmlMode.indent(state, "") + state.tagName = tagName + return result + } + + function token(stream, state) { + if (state.context.mode == xmlMode) + return xmlToken(stream, state, state.context) + else + return jsToken(stream, state, state.context) + } + + function xmlToken(stream, state, cx) { + if (cx.depth == 2) { // Inside a JS /* */ comment + if (stream.match(/^.*?\*\//)) cx.depth = 1 + else stream.skipToEnd() + return "comment" + } + + if (stream.peek() == "{") { + xmlMode.skipAttribute(cx.state) + state.context = new Context(CodeMirror.startState(jsMode, flatXMLIndent(cx.state)), + jsMode, 0, state.context) + return token(stream, state) + } + + if (cx.depth == 1) { // Inside of tag + if (stream.peek() == "<") { // Tag inside of tag + xmlMode.skipAttribute(cx.state) + state.context = new Context(CodeMirror.startState(xmlMode, flatXMLIndent(cx.state)), + xmlMode, 0, state.context) + return token(stream, state) + } else if (stream.match("//")) { + stream.skipToEnd() + return "comment" + } else if (stream.match("/*")) { + cx.depth = 2 + return token(stream, state) + } + } + + var style = xmlMode.token(stream, cx.state), cur = stream.current(), stop + if (/\btag\b/.test(style)) { + if (/>$/.test(cur)) { + if (cx.state.context) cx.depth = 0 + else state.context = state.context.prev + } else if (/^ -1) { + stream.backUp(cur.length - stop) + } + return style + } + + function jsToken(stream, state, cx) { + if (stream.peek() == "<" && jsMode.expressionAllowed(stream, cx.state)) { + jsMode.skipExpression(cx.state) + state.context = new Context(CodeMirror.startState(xmlMode, jsMode.indent(cx.state, "")), + xmlMode, 0, state.context) + return token(stream, state) + } + + var style = jsMode.token(stream, cx.state) + if (!style && cx.depth != null) { + var cur = stream.current() + if (cur == "{") { + cx.depth++ + } else if (cur == "}") { + if (--cx.depth == 0) state.context = state.context.prev + } + } + return style + } + return { startState: function() { return {context: new Context(CodeMirror.startState(jsMode), jsMode)} @@ -38,61 +116,7 @@ return {context: copyContext(state.context)} }, - token: function(stream, state) { - var cx = state.context - if (cx.mode == xmlMode) { - if (cx.depth == 2) { // Inside a JS /* */ comment - if (stream.match(/^.*?\*\//)) cx.depth = 1 - else stream.skipToEnd() - return "comment" - } else if (stream.peek() == "{") { - xmlMode.skipAttribute(cx.state) - var tagName = cx.state.tagName - cx.state.tagName = null - state.context = new Context(CodeMirror.startState(jsMode, xmlMode.indent(cx.state, "")), - jsMode, 0, state.context) - cx.state.tagName = tagName - return this.token(stream, state) - } else if (cx.depth == 1 && stream.match("//")) { - stream.skipToEnd() - return "comment" - } else if (cx.depth == 1 && stream.match("/*")) { - cx.depth = 2 - return this.token(stream, state) - } else { // FIXME skip attribute - var style = xmlMode.token(stream, cx.state), cur = stream.current(), stop - if (/\btag\b/.test(style)) { - if (/>$/.test(cur)) { - if (cx.state.context) cx.depth = 0 - else state.context = state.context.prev - } else if (/^ -1) { - stream.backUp(cur.length - stop) - } - return style - } - } else { // jsMode - if (stream.peek() == "<" && jsMode.expressionAllowed(stream, cx.state)) { - jsMode.skipExpression(cx.state) - state.context = new Context(CodeMirror.startState(xmlMode, jsMode.indent(cx.state, "")), - xmlMode, 0, state.context) - return this.token(stream, state) - } else { - var style = jsMode.token(stream, cx.state) - if (!style && cx.depth != null) { - var cur = stream.current() - if (cur == "{") { - cx.depth++ - } else if (cur == "}") { - if (--cx.depth == 0) state.context = state.context.prev - } - } - return style - } - } - }, + token: token, indent: function(state, textAfter, fullLine) { return state.context.mode.indent(state.context.state, textAfter, fullLine) diff --git a/mode/jsx/test.js b/mode/jsx/test.js index ee601651..0ea99017 100644 --- a/mode/jsx/test.js +++ b/mode/jsx/test.js @@ -63,4 +63,7 @@ MT("spread", "([bracket&tag <][tag foo] [attribute bar]={[meta ...][variable baz] [operator /][number 2]}[bracket&tag />])") + + MT("tag_attribute", + "([bracket&tag <][tag foo] [attribute bar]=[bracket&tag <][tag foo][bracket&tag />/>][operator ++])") })() From 78aba6492cb31e1b3ab680af47896e1a0b8a2156 Mon Sep 17 00:00:00 2001 From: Marijn Haverbeke Date: Wed, 30 Dec 2015 12:09:05 +0100 Subject: [PATCH 0094/1790] [javascript mode] Allow expressions after fat arrow Issue #3745 --- 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 ee6e4016..f4e7ed6d 100644 --- a/mode/javascript/javascript.js +++ b/mode/javascript/javascript.js @@ -14,7 +14,7 @@ "use strict"; function expressionAllowed(stream, state, backUp) { - return /^(?:operator|sof|keyword c|case|new|[\[{}\(,;:])$/.test(state.lastType) || + return /^(?:operator|sof|keyword c|case|new|[\[{}\(,;:]|=>)$/.test(state.lastType) || (state.lastType == "quasi" && /\{\s*$/.test(stream.string.slice(0, stream.pos - (backUp || 0)))) } From 5c53fe7c7f97147650bac3f20c1518a7b5cdd16e Mon Sep 17 00:00:00 2001 From: Marijn Haverbeke Date: Wed, 30 Dec 2015 14:09:47 +0100 Subject: [PATCH 0095/1790] [nginx mode] Fix MIME declaration Closes #3746 --- mode/nginx/nginx.js | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/mode/nginx/nginx.js b/mode/nginx/nginx.js index 135b9cc7..00a32249 100644 --- a/mode/nginx/nginx.js +++ b/mode/nginx/nginx.js @@ -173,6 +173,6 @@ CodeMirror.defineMode("nginx", function(config) { }; }); -CodeMirror.defineMIME("text/nginx", "text/x-nginx-conf"); +CodeMirror.defineMIME("text/x-nginx-conf", "nginx"); }); From 5f228475fd7059c3d712da96c441496fccd25081 Mon Sep 17 00:00:00 2001 From: Marijn Haverbeke Date: Mon, 4 Jan 2016 10:34:49 +0100 Subject: [PATCH 0096/1790] Signal touchstart event, signal mousedown even during touch Issue #3736 --- doc/manual.html | 3 ++- lib/codemirror.js | 4 ++-- 2 files changed, 4 insertions(+), 3 deletions(-) diff --git a/doc/manual.html b/doc/manual.html index a33b77ae..5087df90 100644 --- a/doc/manual.html +++ b/doc/manual.html @@ -653,7 +653,8 @@

    Events

    should not try to change the state of the editor.
    "mousedown", - "dblclick", "contextmenu", "keydown", "keypress", + "dblclick", "touchstart", "contextmenu", + "keydown", "keypress", "keyup", "cut", "copy", "paste", "dragstart", "dragenter", "dragover", "drop" diff --git a/lib/codemirror.js b/lib/codemirror.js index c4f5a000..55412ae7 100644 --- a/lib/codemirror.js +++ b/lib/codemirror.js @@ -3435,7 +3435,7 @@ return dx * dx + dy * dy > 20 * 20; } on(d.scroller, "touchstart", function(e) { - if (!isMouseLikeTouchEvent(e)) { + if (!signalDOMEvent(cm, e) && !isMouseLikeTouchEvent(e)) { clearTimeout(touchFinished); var now = +new Date; d.activeTouch = {start: now, moved: false, @@ -3564,7 +3564,7 @@ // not interfere with, such as a scrollbar or widget. function onMouseDown(e) { var cm = this, display = cm.display; - if (display.activeTouch && display.input.supportsTouch() || signalDOMEvent(cm, e)) return; + if (signalDOMEvent(cm, e) || display.activeTouch && display.input.supportsTouch()) return; display.shift = e.shiftKey; if (eventInWidget(display, e)) { From 0d73c4bf947efccfb43b534ca2f78c513f43cdb5 Mon Sep 17 00:00:00 2001 From: Devin Abbott Date: Wed, 30 Dec 2015 12:35:43 -0800 Subject: [PATCH 0097/1790] [javascript mode] Allow trailing comma in object destructure Issue #3745 --- mode/javascript/javascript.js | 1 + mode/javascript/test.js | 4 ++++ 2 files changed, 5 insertions(+) diff --git a/mode/javascript/javascript.js b/mode/javascript/javascript.js index f4e7ed6d..fa5721d5 100644 --- a/mode/javascript/javascript.js +++ b/mode/javascript/javascript.js @@ -537,6 +537,7 @@ CodeMirror.defineMode("javascript", function(config, parserConfig) { } if (type == "variable") cx.marked = "property"; if (type == "spread") return cont(pattern); + if (type == "}") return pass(); return cont(expect(":"), pattern, maybeAssign); } function maybeAssign(_type, value) { diff --git a/mode/javascript/test.js b/mode/javascript/test.js index 252e064d..cb43d089 100644 --- a/mode/javascript/test.js +++ b/mode/javascript/test.js @@ -17,6 +17,10 @@ " [[[variable-2 c], [variable y] ]] [operator =] [variable-2 c];", "})();"); + MT("destructure_trailing_comma", + "[keyword let] {[def a], [def b],} [operator =] [variable foo];", + "[keyword let] [def c];"); // Parser still in good state? + MT("class_body", "[keyword class] [def Foo] {", " [property constructor]() {}", From 65950ad0acb3d3014077d9bfe0defc8bd468b11c Mon Sep 17 00:00:00 2001 From: Marijn Haverbeke Date: Mon, 4 Jan 2016 10:45:34 +0100 Subject: [PATCH 0098/1790] [jsx mode] Return empty token when switching inner modes So that addModeClass sees the right mode for the token Issue #3745 --- mode/jsx/jsx.js | 6 +++--- 1 file changed, 3 insertions(+), 3 deletions(-) diff --git a/mode/jsx/jsx.js b/mode/jsx/jsx.js index 5f6afc18..af0e2633 100644 --- a/mode/jsx/jsx.js +++ b/mode/jsx/jsx.js @@ -55,7 +55,7 @@ xmlMode.skipAttribute(cx.state) state.context = new Context(CodeMirror.startState(jsMode, flatXMLIndent(cx.state)), jsMode, 0, state.context) - return token(stream, state) + return null } if (cx.depth == 1) { // Inside of tag @@ -63,7 +63,7 @@ xmlMode.skipAttribute(cx.state) state.context = new Context(CodeMirror.startState(xmlMode, flatXMLIndent(cx.state)), xmlMode, 0, state.context) - return token(stream, state) + return null } else if (stream.match("//")) { stream.skipToEnd() return "comment" @@ -92,7 +92,7 @@ jsMode.skipExpression(cx.state) state.context = new Context(CodeMirror.startState(xmlMode, jsMode.indent(cx.state, "")), xmlMode, 0, state.context) - return token(stream, state) + return null } var style = jsMode.token(stream, cx.state) From 8f2149c2d5861842883f078003d055546951aec4 Mon Sep 17 00:00:00 2001 From: Marijn Haverbeke Date: Mon, 4 Jan 2016 11:03:46 +0100 Subject: [PATCH 0099/1790] Draw bookmarks next to a collapsed span See https://discuss.codemirror.net/t/inserting-a-boomark-immediately-before-a-textmarker/590/1 --- lib/codemirror.js | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/lib/codemirror.js b/lib/codemirror.js index 55412ae7..aa5664a1 100644 --- a/lib/codemirror.js +++ b/lib/codemirror.js @@ -7115,14 +7115,14 @@ if (endStyles) for (var j = 0; j < endStyles.length; j += 2) if (endStyles[j + 1] == nextChange) spanEndStyle += " " + endStyles[j] + if (!collapsed || collapsed.from == pos) for (var j = 0; j < foundBookmarks.length; ++j) + buildCollapsedSpan(builder, 0, foundBookmarks[j]); if (collapsed && (collapsed.from || 0) == pos) { buildCollapsedSpan(builder, (collapsed.to == null ? len + 1 : collapsed.to) - pos, collapsed.marker, collapsed.from == null); if (collapsed.to == null) return; if (collapsed.to == pos) collapsed = false; } - if (!collapsed && foundBookmarks.length) for (var j = 0; j < foundBookmarks.length; ++j) - buildCollapsedSpan(builder, 0, foundBookmarks[j]); } if (pos >= len) break; From f923adb268043880178ab17dcaeaeacaf5d5fdd5 Mon Sep 17 00:00:00 2001 From: Will Dean Date: Fri, 1 Jan 2016 11:50:16 +0000 Subject: [PATCH 0100/1790] Various HTML cleanups --- demo/btree.html | 4 +--- demo/tern.html | 4 ++-- demo/xmlcomplete.html | 4 ++-- doc/internals.html | 3 ++- doc/manual.html | 4 ++-- doc/releases.html | 4 ++-- mode/asn.1/index.html | 3 +-- mode/handlebars/index.html | 5 ++--- mode/mumps/index.html | 4 ++-- mode/nginx/index.html | 4 ++-- mode/pig/index.html | 4 +--- mode/tiki/tiki.css | 2 +- 12 files changed, 20 insertions(+), 25 deletions(-) diff --git a/demo/btree.html b/demo/btree.html index fc4997f4..ba07bc74 100644 --- a/demo/btree.html +++ b/demo/btree.html @@ -1,4 +1,4 @@ - + CodeMirror: B-Tree visualization @@ -26,9 +26,7 @@

    B-Tree visualization

    -
    - - - +

    Handlebars syntax highlighting for CodeMirror.

    MIME types defined: text/x-handlebars-template

    diff --git a/mode/mumps/index.html b/mode/mumps/index.html index bd1f69ae..b1f92c21 100644 --- a/mode/mumps/index.html +++ b/mode/mumps/index.html @@ -1,4 +1,4 @@ - + CodeMirror: MUMPS mode @@ -73,7 +73,7 @@

    MUMPS mode

    IF '$LENGTH($PIECE(XUSER(1),U,2)) QUIT 21 ;p419, p434 Q 0 ; - + + +
    Fired when CodeMirror is handling a DOM event of this type. You can preventDefault the event, or give it a diff --git a/lib/codemirror.js b/lib/codemirror.js index d1599c99..372020d8 100644 --- a/lib/codemirror.js +++ b/lib/codemirror.js @@ -3495,7 +3495,7 @@ over: function(e) {if (!signalDOMEvent(cm, e)) { onDragOver(cm, e); e_stop(e); }}, start: function(e){onDragStart(cm, e);}, drop: operation(cm, onDrop), - leave: function() {clearDragCursor(cm);} + leave: function(e) {if (!signalDOMEvent(cm, e)) { clearDragCursor(cm); }} }; var inp = d.input.getField(); From ecaa8914751ad5472f315bce5f0c49c232aa5c26 Mon Sep 17 00:00:00 2001 From: Marijn Haverbeke Date: Mon, 21 Mar 2016 11:18:29 +0100 Subject: [PATCH 0197/1790] Make sure gutters are never left higher than the view height Closes #3884 --- lib/codemirror.js | 4 ++++ 1 file changed, 4 insertions(+) diff --git a/lib/codemirror.js b/lib/codemirror.js index 372020d8..c736708e 100644 --- a/lib/codemirror.js +++ b/lib/codemirror.js @@ -747,6 +747,7 @@ function postUpdateDisplay(cm, update) { var viewport = update.viewport; + for (var first = true;; first = false) { if (!first || !cm.options.lineWrapping || update.oldDisplayWidth == displayWidth(cm)) { // Clip forced viewport to actual scrollable area. @@ -766,6 +767,9 @@ updateScrollbars(cm, barMeasure); } + if (parseInt(cm.display.gutters.style.height) > cm.display.scroller.clientHeight) + cm.display.gutters.style.height = cm.display.scroller.clientHeight + "px" + update.signal(cm, "update", cm); if (cm.display.viewFrom != cm.display.reportedViewFrom || cm.display.viewTo != cm.display.reportedViewTo) { update.signal(cm, "viewportChange", cm, cm.display.viewFrom, cm.display.viewTo); From 8e6158c94c79797d9aa818d0572f6f2ac8c6113d Mon Sep 17 00:00:00 2001 From: Marijn Haverbeke Date: Mon, 21 Mar 2016 11:37:00 +0100 Subject: [PATCH 0198/1790] Mark release 5.13.0 --- AUTHORS | 10 ++++++++++ CHANGELOG.md | 28 ++++++++++++++++++++++++++++ doc/compress.html | 1 + doc/manual.html | 2 +- doc/releases.html | 21 +++++++++++++++++++-- index.html | 2 +- lib/codemirror.js | 2 +- package.json | 2 +- 8 files changed, 62 insertions(+), 6 deletions(-) diff --git a/AUTHORS b/AUTHORS index 741ba1e3..c3680eb4 100644 --- a/AUTHORS +++ b/AUTHORS @@ -75,6 +75,7 @@ benbro Beni Cherniavsky-Paskin Benjamin DeCoste Ben Keen +Ben Mosher Bernhard Sirlinger Bert Chang Billy Moon @@ -137,6 +138,7 @@ David Barnett David Mignot David Pathakjee David Vázquez +David Whittington deebugger Deep Thought Devin Abbott @@ -184,6 +186,7 @@ galambalazs Gautam Mehta Gavin Douglas gekkoe +geowarin Gerard Braad Gergely Hegykozi Giovanni Calò @@ -198,6 +201,7 @@ greengiant Gregory Koberger Guillaume Massé Guillaume Massé +guraga Gustavo Rodrigues Hakan Tunc Hans Engel @@ -274,6 +278,7 @@ ju1ius Juan Benavides Romero Jucovschi Constantin Juho Vuori +Julien Rebetez Justin Andresen Justin Hileman jwallers@gmail.com @@ -281,6 +286,7 @@ kaniga karevn Kayur Patel Ken Newman +ken restivo Ken Rockot Kevin Earls Kevin Sawicki @@ -426,7 +432,9 @@ peter Peter Flynn peterkroon Peter Kroon +Philipp A Philip Stadermann +Pierre Gerold Piët Delport prasanthj Prasanth J @@ -476,6 +484,7 @@ Shiv Deepak Shmuel Englard Shubham Jain silverwind +sinkuu snasa soliton4 sonson @@ -518,6 +527,7 @@ Tom MacWright Tony Jian Travis Heppe Triangle717 +Tristan Tarrant TSUYUSATO Kitsune twifkak Vestimir Markov diff --git a/CHANGELOG.md b/CHANGELOG.md index fe78c71d..60ec9df5 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -1,3 +1,31 @@ +## 5.13.0 (2016-03-21) + +### New features + +New DOM event forwarded: [`"dragleave"`](http://codemirror.net/doc/manual.html#event_dom). + +[protobuf mode](http://codemirror.net/mode/protobuf/index.html): Newly added. + +### Bugfixes + +Fix problem where [`findMarks`](http://codemirror.net/doc/manual.html#findMarks) sometimes failed to find multi-line marks. + +Fix crash that showed up when atomic ranges and bidi text were combined. + +[show-hint addon](http://codemirror.net/demo/complete.html): Completion widgets no longer close when the line indented or dedented. + +[merge addon](http://codemirror.net/demo/merge.html): Fix bug when merging chunks at the end of the file. + +[placeholder addon](http://codemirror.net/doc/manual.html#addon_placeholder): No longer gets confused by [`swapDoc`](http://codemirror.net/doc/manual.html#swapDoc). + +[simplescrollbars addon](http://codemirror.net/doc/manual.html#addon_simplescrollbars): Fix invalid state when deleting at end of document. + +[clike mode](http://codemirror.net/mode/clike/index.html): No longer gets confused when a comment starts after an operator. + +[markdown mode](http://codemirror.net/mode/markdown/index.html): Now supports CommonMark-style flexible list indentation. + +[dylan mode](http://codemirror.net/mode/dylan/index.html): Several improvements and fixes. + ## 5.12.0 (2016-02-19) ### New features diff --git a/doc/compress.html b/doc/compress.html index 302890c4..a0d4364a 100644 --- a/doc/compress.html +++ b/doc/compress.html @@ -36,6 +36,7 @@

    Script compression helper

    Version:

    Version: + + + +

    MIME types defined: text/x-powershell.

    + + diff --git a/mode/powershell/powershell.js b/mode/powershell/powershell.js new file mode 100644 index 00000000..944088fb --- /dev/null +++ b/mode/powershell/powershell.js @@ -0,0 +1,191 @@ +CodeMirror.defineMode("powershell", function() { + var ERRORCLASS = 'error'; + + function wordRegexp(words) { + return new RegExp("^((" + words.join(")|(") + "))\\b"); + } + + var wordOperators = wordRegexp(['-eq', '-ne', '-gt', '-lt', '-le', '-ge']); + var commonkeywords = ['begin', 'break', 'continue', 'do', 'default', 'else', 'elseif', + 'end', 'filter', 'for', 'foreach', 'function', 'if', 'in', 'param', + 'process', 'return', 'switch', 'until', 'where', 'while']; + + var isOperatorChar = /[+\-*&^%:=<>!|\/]/; + var isString = /("|')(\`?.)*?\1/; + + var keywords = wordRegexp(commonkeywords); + //var builtins = wordRegexp(commonBuiltins); + + var indentInfo = null; + + // tokenizers + function tokenBase(stream, state) { + + + + // Handle Comments + //var ch = stream.peek(); + + if (stream.match(keywords)) { + return('variable-2'); + } + + if (stream.match(isString)) { + return('string'); + } + + if (stream.match(wordOperators)) { + return('variable-2'); + } + if (stream.match(isOperatorChar)) { + return('variable-1'); + } + + + // Handle Variables + + + // Handle Number Literals + if (stream.match(/^[0-9\.]/, false)) { + var floatLiteral = false; + // Floats + if (stream.match(/^\d*\.\d+(e[\+\-]?\d+)?/i)) { floatLiteral = true; } + if (stream.match(/^\d+\.\d*/)) { floatLiteral = true; } + if (stream.match(/^\.\d+/)) { floatLiteral = true; } + if (floatLiteral) { + // Float literals may be "imaginary" + stream.eat(/J/i); + return 'number'; + } + // Integers + var intLiteral = false; + // Hex + if (stream.match(/^0x[0-9a-f]+/i)) { intLiteral = true; } + // Binary + if (stream.match(/^0b[01]+/i)) { intLiteral = true; } + // Octal + if (stream.match(/^0o[0-7]+/i)) { intLiteral = true; } + // Decimal + if (stream.match(/^[1-9]\d*(e[\+\-]?\d+)?/)) { + // Decimal literals may be "imaginary" + stream.eat(/J/i); + // TODO - Can you have imaginary longs? + intLiteral = true; + } + // Zero by itself with no other piece of number. + if (stream.match(/^0(?![\dx])/i)) { intLiteral = true; } + if (intLiteral) { + // Integer literals may be "long" + stream.eat(/L/i); + return 'number'; + } + } + + var ch = stream.next(); + + if (ch === '$') { + if (stream.eat('{')) { + state.tokenize = tokenVariable; + return tokenVariable(stream, state); + } else { + stream.eatWhile(/[\w\\\-]/); + return 'variable-2'; + } + } + + if (ch === '<' && stream.eat('#')) { + state.tokenize = tokenComment; + return tokenComment(stream, state); + } + + if (ch === '#') { + stream.skipToEnd(); + return 'comment'; + } + + if (ch === '@' && stream.eat('\"')) { + state.tokenize = tokenMultiString; + return tokenMultiString(stream, state); + } + + //if (isOperatorChar.test(ch)) { + // stream.eat; + //stream.next; + // return("variable-1"); + // } + + stream.next(); + return ERRORCLASS; + } + + + function tokenComment(stream, state) { + var maybeEnd = false, ch; + while ((ch = stream.next()) != null) { + if (maybeEnd && ch == ">") { + state.tokenize = tokenBase; + break; + } + maybeEnd = (ch === '#'); + } + return("comment"); + } + + function tokenVariable(stream, state) { + while ((ch = stream.next()) != null) { + if (ch == "}") { + state.tokenize = tokenBase; + break; + } + } + return("variable-2"); + } + + function tokenMultiString(stream, state) { + var maybeEnd = false, ch; + while ((ch = stream.next()) != null) { + if (maybeEnd && ch == "@") { + state.tokenize = tokenBase; + break; + } + maybeEnd = (ch === '"'); + } + return("string"); + } + + function tokenLexer(stream, state) { + //indentInfo = null; + var style = state.tokenize(stream, state); + //var current = stream.current(); + return style; + } + + var external = { + startState: function(basecolumn) { + return { + tokenize: tokenBase, + scopes: [{offset:basecolumn || 0, type:'py'}], + lastToken: null, + lambda: false, + dedent: 0 + }; + }, + + token: function(stream, state) { + var style = tokenLexer(stream, state); + state.lastToken = {style:style, content: stream.current()}; + if (stream.eol() && stream.lambda) { + state.lambda = false; + } + + return style; + }, + + blockCommentStart: "<#", + blockCommentEnd: "#>", + lineComment: "#" + }; + return external; +}); + +CodeMirror.defineMIME("text/x-powershell", "powershell"); From 20641be6a4973f7914d4a7ef4261f78ebe969411 Mon Sep 17 00:00:00 2001 From: Andrey Shchekin Date: Mon, 30 Jun 2014 00:20:00 +1200 Subject: [PATCH 0212/1790] [powershell mode] Implement actual PowerShell syntax. --- mode/powershell/index.html | 231 +++++++++++------ mode/powershell/powershell.js | 462 ++++++++++++++++++++++------------ 2 files changed, 464 insertions(+), 229 deletions(-) diff --git a/mode/powershell/index.html b/mode/powershell/index.html index 940840c3..bc0f9c48 100644 --- a/mode/powershell/index.html +++ b/mode/powershell/index.html @@ -3,97 +3,179 @@ CodeMirror: Powershell mode - - - - + + + - -

    CodeMirror: Powershell mode

    - -
    +# Built-in functions +A: +Add-Computer Add-Content Add-History Add-Member Add-PSSnapin Add-Type +B: +C: +Checkpoint-Computer Clear-Content Clear-EventLog Clear-History Clear-Host Clear-Item +Clear-ItemProperty Clear-Variable Compare-Object Complete-Transaction Connect-PSSession +ConvertFrom-Csv ConvertFrom-Json ConvertFrom-SecureString ConvertFrom-StringData +Convert-Path ConvertTo-Csv ConvertTo-Html ConvertTo-Json ConvertTo-SecureString +ConvertTo-Xml Copy-Item Copy-ItemProperty +D: +Debug-Process Disable-ComputerRestore Disable-PSBreakpoint Disable-PSRemoting +Disable-PSSessionConfiguration Disconnect-PSSession +E: +Enable-ComputerRestore Enable-PSBreakpoint Enable-PSRemoting Enable-PSSessionConfiguration +Enter-PSSession Exit-PSSession Export-Alias Export-Clixml Export-Console Export-Counter +Export-Csv Export-FormatData Export-ModuleMember Export-PSSession +F: +ForEach-Object Format-Custom Format-List Format-Table Format-Wide +G: +Get-Acl Get-Alias Get-AuthenticodeSignature Get-ChildItem Get-Command Get-ComputerRestorePoint +Get-Content Get-ControlPanelItem Get-Counter Get-Credential Get-Culture Get-Date +Get-Event Get-EventLog Get-EventSubscriber Get-ExecutionPolicy Get-FormatData Get-Help +Get-History Get-Host Get-HotFix Get-Item Get-ItemProperty Get-Job Get-Location Get-Member +Get-Module Get-PfxCertificate Get-Process Get-PSBreakpoint Get-PSCallStack Get-PSDrive +Get-PSProvider Get-PSSession Get-PSSessionConfiguration Get-PSSnapin Get-Random Get-Service +Get-TraceSource Get-Transaction Get-TypeData Get-UICulture Get-Unique Get-Variable Get-Verb +Get-WinEvent Get-WmiObject Group-Object +H: +help +I: +Import-Alias Import-Clixml Import-Counter Import-Csv Import-LocalizedData Import-Module +Import-PSSession ImportSystemModules Invoke-Command Invoke-Expression Invoke-History +Invoke-Item Invoke-RestMethod Invoke-WebRequest Invoke-WmiMethod +J: +Join-Path +K: +L: +Limit-EventLog +M: +Measure-Command Measure-Object mkdir more Move-Item Move-ItemProperty +N: +New-Alias New-Event New-EventLog New-Item New-ItemProperty New-Module New-ModuleManifest +New-Object New-PSDrive New-PSSession New-PSSessionConfigurationFile New-PSSessionOption +New-PSTransportOption New-Service New-TimeSpan New-Variable New-WebServiceProxy +New-WinEvent +O: +oss Out-Default Out-File Out-GridView Out-Host Out-Null Out-Printer Out-String +P: +Pause Pop-Location prompt Push-Location +Q: +R: +Read-Host Receive-Job Receive-PSSession Register-EngineEvent Register-ObjectEvent +Register-PSSessionConfiguration Register-WmiEvent Remove-Computer Remove-Event +Remove-EventLog Remove-Item Remove-ItemProperty Remove-Job Remove-Module +Remove-PSBreakpoint Remove-PSDrive Remove-PSSession Remove-PSSnapin Remove-TypeData +Remove-Variable Remove-WmiObject Rename-Computer Rename-Item Rename-ItemProperty +Reset-ComputerMachinePassword Resolve-Path Restart-Computer Restart-Service +Restore-Computer Resume-Job Resume-Service +S: +Save-Help Select-Object Select-String Select-Xml Send-MailMessage Set-Acl Set-Alias +Set-AuthenticodeSignature Set-Content Set-Date Set-ExecutionPolicy Set-Item +Set-ItemProperty Set-Location Set-PSBreakpoint Set-PSDebug +Set-PSSessionConfiguration Set-Service Set-StrictMode Set-TraceSource Set-Variable +Set-WmiInstance Show-Command Show-ControlPanelItem Show-EventLog Sort-Object +Split-Path Start-Job Start-Process Start-Service Start-Sleep Start-Transaction +Start-Transcript Stop-Computer Stop-Job Stop-Process Stop-Service Stop-Transcript +Suspend-Job Suspend-Service +T: +TabExpansion2 Tee-Object Test-ComputerSecureChannel Test-Connection +Test-ModuleManifest Test-Path Test-PSSessionConfigurationFile Trace-Command +U: +Unblock-File Undo-Transaction Unregister-Event Unregister-PSSessionConfiguration +Update-FormatData Update-Help Update-List Update-TypeData Use-Transaction +V: +W: +Wait-Event Wait-Job Wait-Process Where-Object Write-Debug Write-Error Write-EventLog +Write-Host Write-Output Write-Progress Write-Verbose Write-Warning +X: +Y: +Z: -

    MIME types defined: text/x-powershell.

    diff --git a/mode/powershell/powershell.js b/mode/powershell/powershell.js index 944088fb..f63111b2 100644 --- a/mode/powershell/powershell.js +++ b/mode/powershell/powershell.js @@ -1,191 +1,345 @@ -CodeMirror.defineMode("powershell", function() { - var ERRORCLASS = 'error'; +// Initially based on CodeMirror Python mode, copyright (c) by Marijn Haverbeke and others +// PowerShell mode, copyright (c) Andrey Shchekin, VapidWorx and others +// Distributed under an MIT license: http://codemirror.net/LICENSE - function wordRegexp(words) { - return new RegExp("^((" + words.join(")|(") + "))\\b"); +(function(mod) { + 'use strict'; + if (typeof exports == 'object' && typeof module == 'object') // CommonJS + mod(require('codemirror')); + else if (typeof define == 'function' && define.amd) // AMD + define(['codemirror'], mod); + else // Plain browser env + mod(window.CodeMirror); +})(function(CodeMirror) { +'use strict'; + +CodeMirror.defineMode('powershell', function() { + function buildRegexp(patterns, options) { + options = options || {}; + var prefix = options.prefix !== undefined ? options.prefix : '^'; + var suffix = options.suffix !== undefined ? options.suffix : '\\b'; + + for (var i = 0; i < patterns.length; i++) { + if (patterns[i] instanceof RegExp) { + patterns[i] = patterns[i].source; + } + else { + patterns[i] = patterns[i].replace(/[-\/\\^$*+?.()|[\]{}]/g, '\\$&'); + } + } + + return new RegExp(prefix + '(' + patterns.join('|') + ')' + suffix, 'i'); } - var wordOperators = wordRegexp(['-eq', '-ne', '-gt', '-lt', '-le', '-ge']); - var commonkeywords = ['begin', 'break', 'continue', 'do', 'default', 'else', 'elseif', - 'end', 'filter', 'for', 'foreach', 'function', 'if', 'in', 'param', - 'process', 'return', 'switch', 'until', 'where', 'while']; + var notCharacterOrDash = '(?=[^A-Z\\d\\-_]|$)'; + var keywords = buildRegexp([ + /begin|break|catch|continue|data|default|do|dynamicparam/, + /else|elseif|end|exit|filter|finally|for|foreach|from|function|if|in/, + /param|process|return|switch|throw|trap|try|until|where|while/ + ], { suffix: notCharacterOrDash }); + + var punctuation = /[\[\]{},;`\.]|@[({]/; + var wordOperators = buildRegexp([ + 'f', + /b?not/, + /[ic]?split/, 'join', + /is(not)?/, 'as', + /[ic]?(eq|ne|[gl][te])/, + /[ic]?(not)?(like|match|contains)/, + /[ic]?replace/, + /b?(and|or|xor)/ + ], { prefix: '-' }); + var symbolOperators = /[+\-*\/%]=|\+\+|--|\.\.|[+\-*&^%:=<>!|\/]/; + var operators = buildRegexp([wordOperators, symbolOperators], { suffix: '' }); + + var numbers = /^[+-]?(0x[\da-f]+|(\d+(\.\d+)?|\.\d*)(e[\+\-]?\d+)?)[ld]?([kmgtp]b)?/i; - var isOperatorChar = /[+\-*&^%:=<>!|\/]/; - var isString = /("|')(\`?.)*?\1/; - - var keywords = wordRegexp(commonkeywords); - //var builtins = wordRegexp(commonBuiltins); + var identifiers = /^[A-Za-z\_][A-Za-z\-\_\d]*\b/; - var indentInfo = null; + var symbolBuiltins = /[A-Z]:|%|\?/i; + var namedBuiltins = buildRegexp([ + /Add-(Computer|Content|History|Member|PSSnapin|Type)/, + /Checkpoint-Computer/, + /Clear-(Content|EventLog|History|Host|Item(Property)?|Variable)/, + /Compare-Object/, + /Complete-Transaction/, + /Connect-PSSession/, + /ConvertFrom-(Csv|Json|SecureString|StringData)/, + /Convert-Path/, + /ConvertTo-(Csv|Html|Json|SecureString|Xml)/, + /Copy-Item(Property)?/, + /Debug-Process/, + /Disable-(ComputerRestore|PSBreakpoint|PSRemoting|PSSessionConfiguration)/, + /Disconnect-PSSession/, + /Enable-(ComputerRestore|PSBreakpoint|PSRemoting|PSSessionConfiguration)/, + /(Enter|Exit)-PSSession/, + /Export-(Alias|Clixml|Console|Counter|Csv|FormatData|ModuleMember|PSSession)/, + /ForEach-Object/, + /Format-(Custom|List|Table|Wide)/, + new RegExp('Get-(Acl|Alias|AuthenticodeSignature|ChildItem|Command|ComputerRestorePoint|Content|ControlPanelItem|Counter|Credential' + + '|Culture|Date|Event|EventLog|EventSubscriber|ExecutionPolicy|FormatData|Help|History|Host|HotFix|Item|ItemProperty|Job' + + '|Location|Member|Module|PfxCertificate|Process|PSBreakpoint|PSCallStack|PSDrive|PSProvider|PSSession|PSSessionConfiguration' + + '|PSSnapin|Random|Service|TraceSource|Transaction|TypeData|UICulture|Unique|Variable|Verb|WinEvent|WmiObject)'), + /Group-Object/, + /Import-(Alias|Clixml|Counter|Csv|LocalizedData|Module|PSSession)/, + /ImportSystemModules/, + /Invoke-(Command|Expression|History|Item|RestMethod|WebRequest|WmiMethod)/, + /Join-Path/, + /Limit-EventLog/, + /Measure-(Command|Object)/, + /Move-Item(Property)?/, + new RegExp('New-(Alias|Event|EventLog|Item(Property)?|Module|ModuleManifest|Object|PSDrive|PSSession|PSSessionConfigurationFile' + + '|PSSessionOption|PSTransportOption|Service|TimeSpan|Variable|WebServiceProxy|WinEvent)'), + /Out-(Default|File|GridView|Host|Null|Printer|String)/, + /Pause/, + /(Pop|Push)-Location/, + /Read-Host/, + /Receive-(Job|PSSession)/, + /Register-(EngineEvent|ObjectEvent|PSSessionConfiguration|WmiEvent)/, + /Remove-(Computer|Event|EventLog|Item(Property)?|Job|Module|PSBreakpoint|PSDrive|PSSession|PSSnapin|TypeData|Variable|WmiObject)/, + /Rename-(Computer|Item(Property)?)/, + /Reset-ComputerMachinePassword/, + /Resolve-Path/, + /Restart-(Computer|Service)/, + /Restore-Computer/, + /Resume-(Job|Service)/, + /Save-Help/, + /Select-(Object|String|Xml)/, + /Send-MailMessage/, + new RegExp('Set-(Acl|Alias|AuthenticodeSignature|Content|Date|ExecutionPolicy|Item(Property)?|Location|PSBreakpoint|PSDebug' + + '|PSSessionConfiguration|Service|StrictMode|TraceSource|Variable|WmiInstance)'), + /Show-(Command|ControlPanelItem|EventLog)/, + /Sort-Object/, + /Split-Path/, + /Start-(Job|Process|Service|Sleep|Transaction|Transcript)/, + /Stop-(Computer|Job|Process|Service|Transcript)/, + /Suspend-(Job|Service)/, + /TabExpansion2/, + /Tee-Object/, + /Test-(ComputerSecureChannel|Connection|ModuleManifest|Path|PSSessionConfigurationFile)/, + /Trace-Command/, + /Unblock-File/, + /Undo-Transaction/, + /Unregister-(Event|PSSessionConfiguration)/, + /Update-(FormatData|Help|List|TypeData)/, + /Use-Transaction/, + /Wait-(Event|Job|Process)/, + /Where-Object/, + /Write-(Debug|Error|EventLog|Host|Output|Progress|Verbose|Warning)/, + /cd|help|mkdir|more|oss|prompt/, + /ac|asnp|cat|cd|chdir|clc|clear|clhy|cli|clp|cls|clv|cnsn|compare|copy|cp|cpi|cpp|cvpa|dbp|del|diff|dir|dnsn|ebp/, + /echo|epal|epcsv|epsn|erase|etsn|exsn|fc|fl|foreach|ft|fw|gal|gbp|gc|gci|gcm|gcs|gdr|ghy|gi|gjb|gl|gm|gmo|gp|gps/, + /group|gsn|gsnp|gsv|gu|gv|gwmi|h|history|icm|iex|ihy|ii|ipal|ipcsv|ipmo|ipsn|irm|ise|iwmi|iwr|kill|lp|ls|man|md/, + /measure|mi|mount|move|mp|mv|nal|ndr|ni|nmo|npssc|nsn|nv|ogv|oh|popd|ps|pushd|pwd|r|rbp|rcjb|rcsn|rd|rdr|ren|ri/, + /rjb|rm|rmdir|rmo|rni|rnp|rp|rsn|rsnp|rujb|rv|rvpa|rwmi|sajb|sal|saps|sasv|sbp|sc|select|set|shcm|si|sl|sleep|sls/, + /sort|sp|spjb|spps|spsv|start|sujb|sv|swmi|tee|trcm|type|where|wjb|write/, + ], { prefix: '', suffix: '' }); + var variableBuiltins = buildRegexp([ + /[$?^_]|Args|ConfirmPreference|ConsoleFileName|DebugPreference|Error|ErrorActionPreference|ErrorView|ExecutionContext/, + /FormatEnumerationLimit|Home|Host|Input|MaximumAliasCount|MaximumDriveCount|MaximumErrorCount|MaximumFunctionCount/, + /MaximumHistoryCount|MaximumVariableCount|MyInvocation|NestedPromptLevel|OutputEncoding|Pid|Profile|ProgressPreference/, + /PSBoundParameters|PSCommandPath|PSCulture|PSDefaultParameterValues|PSEmailServer|PSHome|PSScriptRoot|PSSessionApplicationName/, + /PSSessionConfigurationName|PSSessionOption|PSUICulture|PSVersionTable|Pwd|ShellId|StackTrace|VerbosePreference/, + /WarningPreference|WhatIfPreference/, + + /Event|EventArgs|EventSubscriber|Sender/, + /Matches|Ofs|ForEach|LastExitCode|PSCmdlet|PSItem|PSSenderInfo|This/, + /true|false|null/ + ], { prefix: '\\$', suffix: '' }); + + var builtins = buildRegexp([ symbolBuiltins, namedBuiltins, variableBuiltins ], { suffix: notCharacterOrDash }); + + var grammar = { + keyword: keywords, + number: numbers, + operator: operators, + builtin: builtins, + punctuation: punctuation, + indetifier: identifiers + }; // tokenizers function tokenBase(stream, state) { - - - - // Handle Comments - //var ch = stream.peek(); - - if (stream.match(keywords)) { - return('variable-2'); - } - - if (stream.match(isString)) { - return('string'); - } - - if (stream.match(wordOperators)) { - return('variable-2'); - } - if (stream.match(isOperatorChar)) { - return('variable-1'); - } - - - // Handle Variables - - - // Handle Number Literals - if (stream.match(/^[0-9\.]/, false)) { - var floatLiteral = false; - // Floats - if (stream.match(/^\d*\.\d+(e[\+\-]?\d+)?/i)) { floatLiteral = true; } - if (stream.match(/^\d+\.\d*/)) { floatLiteral = true; } - if (stream.match(/^\.\d+/)) { floatLiteral = true; } - if (floatLiteral) { - // Float literals may be "imaginary" - stream.eat(/J/i); - return 'number'; - } - // Integers - var intLiteral = false; - // Hex - if (stream.match(/^0x[0-9a-f]+/i)) { intLiteral = true; } - // Binary - if (stream.match(/^0b[01]+/i)) { intLiteral = true; } - // Octal - if (stream.match(/^0o[0-7]+/i)) { intLiteral = true; } - // Decimal - if (stream.match(/^[1-9]\d*(e[\+\-]?\d+)?/)) { - // Decimal literals may be "imaginary" - stream.eat(/J/i); - // TODO - Can you have imaginary longs? - intLiteral = true; - } - // Zero by itself with no other piece of number. - if (stream.match(/^0(?![\dx])/i)) { intLiteral = true; } - if (intLiteral) { - // Integer literals may be "long" - stream.eat(/L/i); - return 'number'; + // Handle Comments + //var ch = stream.peek(); + + if (stream.eatSpace()) { + return null; + } + + var parent = state.returnStack[state.returnStack.length - 1]; + if (parent && parent.shouldReturnFrom(state)) { + state.tokenize = parent.tokenize; + state.returnStack.pop(); + return state.tokenize(stream, state); + } + + if (stream.eat('(')) { + state.bracketNesting += 1; + return 'punctuation'; + } + + if (stream.eat(')')) { + state.bracketNesting -= 1; + return 'punctuation'; + } + + for (var key in grammar) { + if (stream.match(grammar[key])) { + return key; } } - var ch = stream.next(); + // single-quote string + if (stream.match(/'([^']|'')+'/)) { + return 'string'; + } + + var ch = stream.next(); + if (ch === '$') { + return tokenVariable(stream, state); + } + + // double-quote string + if (ch === '"') { + return tokenDoubleQuoteString(stream, state); + } - if (ch === '$') { - if (stream.eat('{')) { - state.tokenize = tokenVariable; - return tokenVariable(stream, state); - } else { - stream.eatWhile(/[\w\\\-]/); - return 'variable-2'; - } - } - if (ch === '<' && stream.eat('#')) { - state.tokenize = tokenComment; - return tokenComment(stream, state); - } - - if (ch === '#') { + state.tokenize = tokenComment; + return tokenComment(stream, state); + } + + if (ch === '#') { stream.skipToEnd(); return 'comment'; } - if (ch === '@' && stream.eat('\"')) { - state.tokenize = tokenMultiString; - return tokenMultiString(stream, state); - } - - //if (isOperatorChar.test(ch)) { - // stream.eat; - //stream.next; - // return("variable-1"); - // } + if (ch === '@') { + var quoteMatch = stream.eat(/["']/); + if (quoteMatch && stream.eol()) { + state.tokenize = tokenMultiString; + state.startQuote = quoteMatch[0]; + return tokenMultiString(stream, state); + } + } stream.next(); - return ERRORCLASS; + return 'error'; + } + + function tokenDoubleQuoteString(stream, state) { + var ch; + while((ch = stream.peek()) != null) { + if (ch === '$') { + state.tokenize = tokenInterpolation; + return 'string'; + } + + stream.next(); + if (ch === '`') { + stream.next(); + continue; + } + + if (ch === '"' && !stream.eat('"')) { + state.tokenize = tokenBase; + return 'string'; + } + } + + return 'error'; + } + + function tokenInterpolation(stream, state) { + if (stream.match('$(')) { + var savedBracketNesting = state.bracketNesting; + state.returnStack.push({ + /*jshint loopfunc:true */ + shouldReturnFrom: function(state) { + return state.bracketNesting === savedBracketNesting; + }, + tokenize: tokenDoubleQuoteString + }); + state.tokenize = tokenBase; + state.bracketNesting += 1; + return 'punctuation'; + } else { + stream.next(); + state.returnStack.push({ + shouldReturnFrom: function() { return true; }, + tokenize: tokenDoubleQuoteString + }); + state.tokenize = tokenVariable; + return state.tokenize(stream, state); + } } - - function tokenComment(stream, state) { - var maybeEnd = false, ch; - while ((ch = stream.next()) != null) { - if (maybeEnd && ch == ">") { - state.tokenize = tokenBase; - break; - } - maybeEnd = (ch === '#'); + function tokenComment(stream, state) { + var maybeEnd = false, ch; + while ((ch = stream.next()) != null) { + if (maybeEnd && ch == '>') { + state.tokenize = tokenBase; + break; + } + maybeEnd = (ch === '#'); + } + return 'comment'; } - return("comment"); - } - - function tokenVariable(stream, state) { - while ((ch = stream.next()) != null) { - if (ch == "}") { - state.tokenize = tokenBase; - break; - } + + function tokenVariable(stream, state) { + if (stream.eat('{')) { + state.tokenize = tokenVariableWithBraces; + return tokenVariableWithBraces(stream, state); + } else { + stream.eatWhile(/[\w\\\-:]/); + state.tokenize = tokenBase; + return 'variable-2'; + } } - return("variable-2"); - } - - function tokenMultiString(stream, state) { - var maybeEnd = false, ch; - while ((ch = stream.next()) != null) { - if (maybeEnd && ch == "@") { - state.tokenize = tokenBase; - break; - } - maybeEnd = (ch === '"'); + + function tokenVariableWithBraces(stream, state) { + var ch; + while ((ch = stream.next()) != null) { + if (ch === '}') { + state.tokenize = tokenBase; + break; + } + } + return 'variable-2'; } - return("string"); - } - - function tokenLexer(stream, state) { - //indentInfo = null; - var style = state.tokenize(stream, state); - //var current = stream.current(); - return style; + + function tokenMultiString(stream, state) { + var quote = state.startQuote; + if (stream.sol() && stream.match(new RegExp(quote + '@'))) { + state.tokenize = tokenBase; + } + else { + stream.skipToEnd(); + } + + return 'string'; } var external = { - startState: function(basecolumn) { + startState: function() { return { - tokenize: tokenBase, - scopes: [{offset:basecolumn || 0, type:'py'}], - lastToken: null, - lambda: false, - dedent: 0 - }; + returnStack: [], + bracketNesting: 0, + tokenize: tokenBase + }; }, token: function(stream, state) { - var style = tokenLexer(stream, state); - state.lastToken = {style:style, content: stream.current()}; - if (stream.eol() && stream.lambda) { - state.lambda = false; - } - - return style; + return state.tokenize(stream, state); }, - blockCommentStart: "<#", - blockCommentEnd: "#>", - lineComment: "#" + blockCommentStart: '<#', + blockCommentEnd: '#>', + lineComment: '#' }; return external; }); -CodeMirror.defineMIME("text/x-powershell", "powershell"); +CodeMirror.defineMIME('text/x-powershell', 'powershell'); +}); \ No newline at end of file From d0e82a76f88a1e359447da39df2cb68c31067f6a Mon Sep 17 00:00:00 2001 From: Ben Miller Date: Thu, 24 Sep 2015 15:48:23 +1200 Subject: [PATCH 0213/1790] [powershell mode] Improve corrected angle bracket matching in operators (fixes block comments) corrected digit matching regex corrected spelling of 'identifier' in grammar dictionary notCharacterOrDash explicitly includes lowercase characters improved variable matching splatted variables now match bare '$' and '@' are errors, not variables moved single-quoted string processing into tokenSingleQuoteString incomplete strings are errors now empty strings are no longer errors added support for here-string interpolation with nesting support added highlighting for splatted vars removed arbitrary stream advancement prior to default error in tokenBase enabled folding braces --- mode/powershell/powershell.js | 104 ++++++++++++++++++++++++++-------- 1 file changed, 80 insertions(+), 24 deletions(-) diff --git a/mode/powershell/powershell.js b/mode/powershell/powershell.js index f63111b2..8295635a 100644 --- a/mode/powershell/powershell.js +++ b/mode/powershell/powershell.js @@ -1,6 +1,9 @@ -// Initially based on CodeMirror Python mode, copyright (c) by Marijn Haverbeke and others -// PowerShell mode, copyright (c) Andrey Shchekin, VapidWorx and others -// Distributed under an MIT license: http://codemirror.net/LICENSE +/** + * @license + * Initially based on CodeMirror Python mode, copyright (c) by Marijn Haverbeke and others + * PowerShell mode, copyright (c) Andrey Shchekin, VapidWorx and others + * Distributed under an MIT license: http://codemirror.net/LICENSE + */ (function(mod) { 'use strict'; @@ -31,7 +34,8 @@ CodeMirror.defineMode('powershell', function() { return new RegExp(prefix + '(' + patterns.join('|') + ')' + suffix, 'i'); } - var notCharacterOrDash = '(?=[^A-Z\\d\\-_]|$)'; + var notCharacterOrDash = '(?=[^A-Za-z\\d\\-_]|$)'; + var varNames = /[\w\-:]/ var keywords = buildRegexp([ /begin|break|catch|continue|data|default|do|dynamicparam/, /else|elseif|end|exit|filter|finally|for|foreach|from|function|if|in/, @@ -49,10 +53,10 @@ CodeMirror.defineMode('powershell', function() { /[ic]?replace/, /b?(and|or|xor)/ ], { prefix: '-' }); - var symbolOperators = /[+\-*\/%]=|\+\+|--|\.\.|[+\-*&^%:=<>!|\/]/; + var symbolOperators = /[+\-*\/%]=|\+\+|--|\.\.|[+\-*&^%:=!|\/]|<(?!#)|(?!#)>/; var operators = buildRegexp([wordOperators, symbolOperators], { suffix: '' }); - var numbers = /^[+-]?(0x[\da-f]+|(\d+(\.\d+)?|\.\d*)(e[\+\-]?\d+)?)[ld]?([kmgtp]b)?/i; + var numbers = /^[+-]?((0x[\da-f]+)|((\d+\.\d+|\d\.|\.\d+|\d+)(e[\+\-]?\d+)?))[ld]?([kmgtp]b)?/i; var identifiers = /^[A-Za-z\_][A-Za-z\-\_\d]*\b/; @@ -106,8 +110,8 @@ CodeMirror.defineMode('powershell', function() { /Save-Help/, /Select-(Object|String|Xml)/, /Send-MailMessage/, - new RegExp('Set-(Acl|Alias|AuthenticodeSignature|Content|Date|ExecutionPolicy|Item(Property)?|Location|PSBreakpoint|PSDebug' - + '|PSSessionConfiguration|Service|StrictMode|TraceSource|Variable|WmiInstance)'), + new RegExp('Set-(Acl|Alias|AuthenticodeSignature|Content|Date|ExecutionPolicy|Item(Property)?|Location|PSBreakpoint|PSDebug' + + '|PSSessionConfiguration|Service|StrictMode|TraceSource|Variable|WmiInstance)'), /Show-(Command|ControlPanelItem|EventLog)/, /Sort-Object/, /Split-Path/, @@ -132,7 +136,7 @@ CodeMirror.defineMode('powershell', function() { /group|gsn|gsnp|gsv|gu|gv|gwmi|h|history|icm|iex|ihy|ii|ipal|ipcsv|ipmo|ipsn|irm|ise|iwmi|iwr|kill|lp|ls|man|md/, /measure|mi|mount|move|mp|mv|nal|ndr|ni|nmo|npssc|nsn|nv|ogv|oh|popd|ps|pushd|pwd|r|rbp|rcjb|rcsn|rd|rdr|ren|ri/, /rjb|rm|rmdir|rmo|rni|rnp|rp|rsn|rsnp|rujb|rv|rvpa|rwmi|sajb|sal|saps|sasv|sbp|sc|select|set|shcm|si|sl|sleep|sls/, - /sort|sp|spjb|spps|spsv|start|sujb|sv|swmi|tee|trcm|type|where|wjb|write/, + /sort|sp|spjb|spps|spsv|start|sujb|sv|swmi|tee|trcm|type|where|wjb|write/ ], { prefix: '', suffix: '' }); var variableBuiltins = buildRegexp([ /[$?^_]|Args|ConfirmPreference|ConsoleFileName|DebugPreference|Error|ErrorActionPreference|ErrorView|ExecutionContext/, @@ -147,7 +151,7 @@ CodeMirror.defineMode('powershell', function() { /true|false|null/ ], { prefix: '\\$', suffix: '' }); - var builtins = buildRegexp([ symbolBuiltins, namedBuiltins, variableBuiltins ], { suffix: notCharacterOrDash }); + var builtins = buildRegexp([symbolBuiltins, namedBuiltins, variableBuiltins], { suffix: notCharacterOrDash }); var grammar = { keyword: keywords, @@ -155,7 +159,7 @@ CodeMirror.defineMode('powershell', function() { operator: operators, builtin: builtins, punctuation: punctuation, - indetifier: identifiers + identifier: identifiers }; // tokenizers @@ -190,12 +194,13 @@ CodeMirror.defineMode('powershell', function() { } } + var ch = stream.next(); + // single-quote string - if (stream.match(/'([^']|'')+'/)) { - return 'string'; + if (ch === "'") { + return tokenSingleQuoteString(stream, state); } - var ch = stream.next(); if (ch === '$') { return tokenVariable(stream, state); } @@ -221,18 +226,35 @@ CodeMirror.defineMode('powershell', function() { state.tokenize = tokenMultiString; state.startQuote = quoteMatch[0]; return tokenMultiString(stream, state); + } else if (stream.peek().match(/[({]/)) { + return 'punctuation'; + } else if (stream.match(varNames)) { + // splatted variable + return tokenVariable(stream, state); + } + } + return 'error'; + } + + function tokenSingleQuoteString(stream, state) { + var ch; + while ((ch = stream.peek()) != null) { + stream.next(); + + if (ch === "'" && !stream.eat("'")) { + state.tokenize = tokenBase; + return 'string'; } } - stream.next(); return 'error'; } function tokenDoubleQuoteString(stream, state) { var ch; - while((ch = stream.peek()) != null) { + while ((ch = stream.peek()) != null) { if (ch === '$') { - state.tokenize = tokenInterpolation; + state.tokenize = tokenStringInterpolation; return 'string'; } @@ -251,7 +273,22 @@ CodeMirror.defineMode('powershell', function() { return 'error'; } - function tokenInterpolation(stream, state) { + function tokenStringInterpolation(stream, state) { + return tokenInterpolation(stream, state, tokenDoubleQuoteString); + } + + function tokenMultiStringReturn(stream, state) { + state.tokenize = tokenMultiString; + state.startQuote = '"' + return tokenMultiString(stream, state); + } + + function tokenHereStringInterpolation(stream, state) { + var saved; + return tokenInterpolation(stream, state, tokenMultiStringReturn); + } + + function tokenInterpolation(stream, state, parentTokenize) { if (stream.match('$(')) { var savedBracketNesting = state.bracketNesting; state.returnStack.push({ @@ -259,7 +296,7 @@ CodeMirror.defineMode('powershell', function() { shouldReturnFrom: function(state) { return state.bracketNesting === savedBracketNesting; }, - tokenize: tokenDoubleQuoteString + tokenize: parentTokenize }); state.tokenize = tokenBase; state.bracketNesting += 1; @@ -268,7 +305,7 @@ CodeMirror.defineMode('powershell', function() { stream.next(); state.returnStack.push({ shouldReturnFrom: function() { return true; }, - tokenize: tokenDoubleQuoteString + tokenize: parentTokenize }); state.tokenize = tokenVariable; return state.tokenize(stream, state); @@ -288,13 +325,17 @@ CodeMirror.defineMode('powershell', function() { } function tokenVariable(stream, state) { + var ch = stream.peek(); if (stream.eat('{')) { state.tokenize = tokenVariableWithBraces; return tokenVariableWithBraces(stream, state); - } else { - stream.eatWhile(/[\w\\\-:]/); + } else if (ch != undefined && ch.match(varNames)) { + stream.eatWhile(varNames); state.tokenize = tokenBase; return 'variable-2'; + } else { + state.tokenize = tokenBase; + return 'error'; } } @@ -314,6 +355,20 @@ CodeMirror.defineMode('powershell', function() { if (stream.sol() && stream.match(new RegExp(quote + '@'))) { state.tokenize = tokenBase; } + else if (quote === '"') { + while (!stream.eol()) { + var ch = stream.peek(); + if (ch === '$') { + state.tokenize = tokenHereStringInterpolation; + return 'string'; + } + + stream.next(); + if (ch === '`') { + stream.next(); + } + } + } else { stream.skipToEnd(); } @@ -336,10 +391,11 @@ CodeMirror.defineMode('powershell', function() { blockCommentStart: '<#', blockCommentEnd: '#>', - lineComment: '#' + lineComment: '#', + fold: 'brace' }; return external; }); CodeMirror.defineMIME('text/x-powershell', 'powershell'); -}); \ No newline at end of file +}); From 95f6840611f8b7025de3783f8408e9f353a830cc Mon Sep 17 00:00:00 2001 From: Andrey Shchekin Date: Sun, 27 Mar 2016 22:11:07 +1300 Subject: [PATCH 0214/1790] [powershell mode] Prepare for merge into CodeMirror repository Added tests and fixed some issues uncovered by testing. --- mode/powershell/index.html | 46 ++- mode/powershell/powershell.js | 697 +++++++++++++++++----------------- mode/powershell/test.js | 72 ++++ test/index.html | 2 + 4 files changed, 451 insertions(+), 366 deletions(-) create mode 100644 mode/powershell/test.js diff --git a/mode/powershell/index.html b/mode/powershell/index.html index bc0f9c48..6b235df8 100644 --- a/mode/powershell/index.html +++ b/mode/powershell/index.html @@ -3,15 +3,30 @@ CodeMirror: Powershell mode - - - + + + + -

    CodeMirror: Powershell mode

    +
    CodeMirror.defineDocExtension(name: string, value: any)
    -
    Like defineExtension, +
    Like defineExtension, but the method will be added to the interface for Doc objects instead.
    diff --git a/index.html b/index.html index 1f775abc..da5a06c4 100644 --- a/index.html +++ b/index.html @@ -106,7 +106,7 @@

    This is CodeMirror

    maintainers need to subsist.
    Current funding status =
    You can help per month or - once. + once.
    diff --git a/keymap/vim.js b/keymap/vim.js index b7e8d858..7f2fb627 100644 --- a/keymap/vim.js +++ b/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 @@ -642,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({}), @@ -1373,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; @@ -1910,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); @@ -3089,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 = { @@ -3451,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; diff --git a/mode/clike/clike.js b/mode/clike/clike.js index 34d3a5af..695d5cef 100644 --- a/mode/clike/clike.js +++ b/mode/clike/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/mode/crystal/crystal.js b/mode/crystal/crystal.js index 2e74bee4..e63627ce 100644 --- a/mode/crystal/crystal.js +++ b/mode/crystal/crystal.js @@ -209,7 +209,7 @@ // Operators if (stream.match(operators)) { - stream.eat("="); // Operators can follow assigin symbol. + stream.eat("="); // Operators can follow assign symbol. return "operator"; } diff --git a/mode/django/django.js b/mode/django/django.js index a8a7d831..7b4ef3b5 100644 --- a/mode/django/django.js +++ b/mode/django/django.js @@ -66,11 +66,11 @@ } // A string can be included in either single or double quotes (this is - // the delimeter). Mark everything as a string until the start delimeter + // the delimiter). Mark everything as a string until the start delimiter // occurs again. - function inString (delimeter, previousTokenizer) { + function inString (delimiter, previousTokenizer) { return function (stream, state) { - if (!state.escapeNext && stream.eat(delimeter)) { + if (!state.escapeNext && stream.eat(delimiter)) { state.tokenize = previousTokenizer; } else { if (state.escapeNext) { @@ -80,7 +80,7 @@ var ch = stream.next(); // Take into account the backslash for escaping characters, such as - // the string delimeter. + // the string delimiter. if (ch == "\\") { state.escapeNext = true; } @@ -100,7 +100,7 @@ return "null"; } - // Dot folowed by a non-word character should be considered an error. + // Dot followed by a non-word character should be considered an error. if (stream.match(/\.\W+/)) { return "error"; } else if (stream.eat(".")) { @@ -119,7 +119,7 @@ return "null"; } - // Pipe folowed by a non-word character should be considered an error. + // Pipe followed by a non-word character should be considered an error. if (stream.match(/\.\W+/)) { return "error"; } else if (stream.eat("|")) { @@ -199,7 +199,7 @@ return "null"; } - // Dot folowed by a non-word character should be considered an error. + // Dot followed by a non-word character should be considered an error. if (stream.match(/\.\W+/)) { return "error"; } else if (stream.eat(".")) { @@ -218,7 +218,7 @@ return "null"; } - // Pipe folowed by a non-word character should be considered an error. + // Pipe followed by a non-word character should be considered an error. if (stream.match(/\.\W+/)) { return "error"; } else if (stream.eat("|")) { diff --git a/mode/gfm/index.html b/mode/gfm/index.html index 7e38c52d..24c90c06 100644 --- a/mode/gfm/index.html +++ b/mode/gfm/index.html @@ -47,7 +47,7 @@

    GFM mode

    GFM adds syntax to strikethrough text, which is missing from standard Markdown. ~~Mistaken text.~~ -~~**works with other fomatting**~~ +~~**works with other formatting**~~ ~~spans across lines~~ diff --git a/mode/haml/haml.js b/mode/haml/haml.js index 03ce8335..86def73e 100644 --- a/mode/haml/haml.js +++ b/mode/haml/haml.js @@ -11,7 +11,7 @@ })(function(CodeMirror) { "use strict"; - // full haml mode. This handled embeded ruby and html fragments too + // full haml mode. This handled embedded ruby and html fragments too CodeMirror.defineMode("haml", function(config) { var htmlMode = CodeMirror.getMode(config, {name: "htmlmixed"}); var rubyMode = CodeMirror.getMode(config, "ruby"); diff --git a/mode/htmlembedded/index.html b/mode/htmlembedded/index.html index 365ef8f3..f27582ef 100644 --- a/mode/htmlembedded/index.html +++ b/mode/htmlembedded/index.html @@ -52,7 +52,7 @@

    Html Embedded Scripts mode

    Mode for html embedded scripts like JSP and ASP.NET. Depends on HtmlMixed which in turn depends on - JavaScript, CSS and XML.
    Other dependancies include those of the scriping language chosen.

    + JavaScript, CSS and XML.
    Other dependencies include those of the scripting language chosen.

    MIME types defined: application/x-aspx (ASP.NET), application/x-ejs (Embedded Javascript), application/x-jsp (JavaServer Pages)

    diff --git a/mode/markdown/test.js b/mode/markdown/test.js index a48d1531..e2b3a815 100644 --- a/mode/markdown/test.js +++ b/mode/markdown/test.js @@ -647,7 +647,7 @@ MT("linkReferenceEmStrong", "[link [[][link&strong **][link&em&strong *foo**][link&em *][link ]]][string&url [[bar]]] hello"); - // Reference-style links with optional space separator (per docuentation) + // Reference-style links with optional space separator (per documentation) // "You can optionally use a space to separate the sets of brackets" MT("linkReferenceSpace", "[link [[foo]]] [string&url [[bar]]] hello"); @@ -683,7 +683,7 @@ MT("labelTitleSingleQuotes", "[link [[foo]]:] [string&url http://example.com/ 'bar']"); - MT("labelTitleParenthese", + MT("labelTitleParentheses", "[link [[foo]]:] [string&url http://example.com/ (bar)]"); MT("labelTitleInvalid", @@ -700,7 +700,7 @@ "[link [[foo]]:] [string&url http://example.com/]", "[string 'bar'] hello"); - MT("labelTitleNextParenthese", + MT("labelTitleNextParentheses", "[link [[foo]]:] [string&url http://example.com/]", "[string (bar)] hello"); diff --git a/mode/octave/index.html b/mode/octave/index.html index 79df5811..3490ee63 100644 --- a/mode/octave/index.html +++ b/mode/octave/index.html @@ -65,7 +65,7 @@

    Octave mode

    %one line comment %{ multi -line commment %} +line comment %} + + + + + +
    +

    yacas mode

    + + + + + + +

    MIME types defined: text/x-yacas (yacas).

    +
    diff --git a/mode/yacas/yacas.js b/mode/yacas/yacas.js new file mode 100644 index 00000000..2967382b --- /dev/null +++ b/mode/yacas/yacas.js @@ -0,0 +1,138 @@ +// CodeMirror, copyright (c) by Marijn Haverbeke and others +// Distributed under an MIT license: http://codemirror.net/LICENSE + +// Yacas mode copyright (c) 2015 by Grzegorz Mazur +// Loosely based on mathematica mode by Calin Barbat + +(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('yacas', function(_config, _parserConfig) { + + // patterns + var pFloatForm = "(?:(?:\\.\\d+|\\d+\\.\\d*|\\d+)(?:[eE][+-]?\\d+)?)"; + var pIdentifier = "(?:[a-zA-Z\\$'][a-zA-Z0-9\\$']*)"; + + // regular expressions + var reFloatForm = new RegExp(pFloatForm); + var reIdentifier = new RegExp(pIdentifier); + var rePattern = new RegExp(pIdentifier + "?_" + pIdentifier); + var reFunctionLike = new RegExp(pIdentifier + "\\s*\\("); + + function tokenBase(stream, state) { + var ch; + + // get next character + ch = stream.next(); + + // string + if (ch === '"') { + state.tokenize = tokenString; + return state.tokenize(stream, state); + } + + // comment + if (ch === '/') { + if (stream.eat('*')) { + state.tokenize = tokenComment; + return state.tokenize(stream, state); + } + if (stream.eat("/")) { + stream.skipToEnd(); + return "comment"; + } + } + + // go back one character + stream.backUp(1); + + // look for ordered rules + if (stream.match(/\d+ *#/, true, false)) { + return 'qualifier'; + } + + // look for numbers + if (stream.match(reFloatForm, true, false)) { + return 'number'; + } + + // look for placeholders + if (stream.match(rePattern, true, false)) { + return 'variable-3'; + } + + // match all braces separately + if (stream.match(/(?:\[|\]|{|}|\(|\))/, true, false)) { + return 'bracket'; + } + + // literals looking like function calls + if (stream.match(reFunctionLike, true, false)) { + stream.backUp(1); + return 'variable'; + } + + // all other identifiers + if (stream.match(reIdentifier, true, false)) { + return 'variable-2'; + } + + // operators; note that operators like @@ or /; are matched separately for each symbol. + if (stream.match(/(?:\\|\+|\-|\*|\/|,|;|\.|:|@|~|=|>|<|&|\||_|`|'|\^|\?|!|%)/, true, false)) { + return 'operator'; + } + + // everything else is an error + return 'error'; + } + + function tokenString(stream, state) { + var next, end = false, escaped = false; + while ((next = stream.next()) != null) { + if (next === '"' && !escaped) { + end = true; + break; + } + escaped = !escaped && next === '\\'; + } + if (end && !escaped) { + state.tokenize = tokenBase; + } + return 'string'; + }; + + function tokenComment(stream, state) { + var prev, next; + while((next = stream.next()) != null) { + if (prev === '*' && next === '/') + break; + prev = next; + } + state.tokenize = tokenBase; + return 'comment'; + } + + return { + startState: function() {return {tokenize: tokenBase, commentLevel: 0};}, + token: function(stream, state) { + if (stream.eatSpace()) return null; + return state.tokenize(stream, state); + }, + blockCommentStart: "/*", + blockCommentEnd: "*/", + lineComment: "//" + }; +}); + +CodeMirror.defineMIME('text/x-yacas', { + name: 'yacas' +}); + +}); From 66fd40ff072151a1a451e2f1b80e34340d293d38 Mon Sep 17 00:00:00 2001 From: Marijn Haverbeke Date: Wed, 6 Apr 2016 10:25:46 +0200 Subject: [PATCH 0231/1790] [manual] Explain the way fromTextArea can leak memory Issue #3938 --- doc/manual.html | 45 ++++++++++++++++++++++++--------------------- 1 file changed, 24 insertions(+), 21 deletions(-) diff --git a/doc/manual.html b/doc/manual.html index 59419027..ce03bd30 100644 --- a/doc/manual.html +++ b/doc/manual.html @@ -1,4 +1,4 @@ - + CodeMirror: User Manual @@ -2064,26 +2064,29 @@

    Static properties

    else (usually one) for dev snapshots.
    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. The text in the - textarea will provide the content for the editor. A CodeMirror - instance created this way has three additional methods: -
    -
    cm.save()
    -
    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.getTextArea() → TextAreaElement
    -
    Returns the textarea that the instance was based on.
    -
    +
    This 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. The text in the + textarea will provide the content for the editor. A CodeMirror + instance created this way has three additional methods: +
    +
    cm.save()
    +
    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). If you dynamically create and + destroy editors made with `fromTextArea`, without destroying + the form they are part of, you should make sure to call + `toTextArea` to remove the editor, or its `"submit"` handler + on the form will cause a memory leak.
    + +
    cm.getTextArea() → TextAreaElement
    +
    Returns the textarea that the instance was based on.
    +
    CodeMirror.defaults: object
    From 225a35fc4a107fc0027c544743fe6c10d86f9f52 Mon Sep 17 00:00:00 2001 From: Marijn Haverbeke Date: Wed, 6 Apr 2016 21:55:28 +0200 Subject: [PATCH 0232/1790] Bump version number to 5.13.5 5.13.4 was a trivial release restoring a LICENSE file. --- doc/manual.html | 2 +- lib/codemirror.js | 2 +- package.json | 2 +- 3 files changed, 3 insertions(+), 3 deletions(-) diff --git a/doc/manual.html b/doc/manual.html index ce03bd30..b513bf94 100644 --- a/doc/manual.html +++ b/doc/manual.html @@ -69,7 +69,7 @@

    User manual and reference guide - version 5.13.3 + version 5.13.5

    CodeMirror is a code-editor component that can be embedded in diff --git a/lib/codemirror.js b/lib/codemirror.js index 22186b45..19baf009 100644 --- a/lib/codemirror.js +++ b/lib/codemirror.js @@ -8890,7 +8890,7 @@ // THE END - CodeMirror.version = "5.13.3"; + CodeMirror.version = "5.13.5"; return CodeMirror; }); diff --git a/package.json b/package.json index 11551de2..ca0d787e 100644 --- a/package.json +++ b/package.json @@ -1,6 +1,6 @@ { "name": "codemirror", - "version":"5.13.3", + "version":"5.13.5", "main": "lib/codemirror.js", "description": "In-browser code editing made bearable", "license": "MIT", From d6e8f34ae926f782379e96a1d911e74363a6a416 Mon Sep 17 00:00:00 2001 From: Matt Pass Date: Wed, 6 Apr 2016 23:20:39 +0100 Subject: [PATCH 0233/1790] [icecoder theme] Warmer background and fixed matchingbracket --- theme/icecoder.css | 8 ++++---- 1 file changed, 4 insertions(+), 4 deletions(-) diff --git a/theme/icecoder.css b/theme/icecoder.css index d70d26e8..ffebaf2f 100644 --- a/theme/icecoder.css +++ b/theme/icecoder.css @@ -2,7 +2,7 @@ ICEcoder default theme by Matt Pass, used in code editor available at https://icecoder.net */ -.cm-s-icecoder { color: #666; background: #141612; } +.cm-s-icecoder { color: #666; background: #1d1d1b; } .cm-s-icecoder span.cm-keyword { color: #eee; font-weight:bold; } /* off-white 1 */ .cm-s-icecoder span.cm-atom { color: #e1c76e; } /* yellow */ @@ -37,7 +37,7 @@ ICEcoder default theme by Matt Pass, used in code editor available at https://ic .cm-s-icecoder .CodeMirror-cursor { border-left: 1px solid white; } .cm-s-icecoder div.CodeMirror-selected { color: #fff; background: #037; } -.cm-s-icecoder .CodeMirror-gutters { background: #141612; min-width: 41px; border-right: 0; } +.cm-s-icecoder .CodeMirror-gutters { background: #1d1d1b; min-width: 41px; border-right: 0; } .cm-s-icecoder .CodeMirror-linenumber { color: #555; cursor: default; } -.cm-s-icecoder .CodeMirror-matchingbracket { border: 1px solid grey; color: black !important; } -.cm-s-icecoder .CodeMirror-activeline-background { background: #000; } \ No newline at end of file +.cm-s-icecoder .CodeMirror-matchingbracket { color: #fff !important; background: #555 !important; } +.cm-s-icecoder .CodeMirror-activeline-background { background: #000; } From adb73ffa1810807bfedf4a218e8cd1462425df14 Mon Sep 17 00:00:00 2001 From: Timothy Gu Date: Sat, 2 Apr 2016 20:25:41 -0700 Subject: [PATCH 0234/1790] [webidl mode] Add --- doc/compress.html | 1 + mode/index.html | 1 + mode/meta.js | 1 + mode/webidl/index.html | 71 +++++++++++++++ mode/webidl/webidl.js | 197 +++++++++++++++++++++++++++++++++++++++++ 5 files changed, 271 insertions(+) create mode 100644 mode/webidl/index.html create mode 100644 mode/webidl/webidl.js diff --git a/doc/compress.html b/doc/compress.html index 538ecbe5..44e6fea8 100644 --- a/doc/compress.html +++ b/doc/compress.html @@ -226,6 +226,7 @@

    Script compression helper

    + diff --git a/mode/index.html b/mode/index.html index efb64143..822f04fc 100644 --- a/mode/index.html +++ b/mode/index.html @@ -149,6 +149,7 @@

    Language modes

  • Verilog/SystemVerilog
  • VHDL
  • Vue.js app
  • +
  • Web IDL
  • XML/HTML
  • XQuery
  • Yacas
  • diff --git a/mode/meta.js b/mode/meta.js index 3539beaa..dcdcdab9 100644 --- a/mode/meta.js +++ b/mode/meta.js @@ -145,6 +145,7 @@ {name: "Turtle", mime: "text/turtle", mode: "turtle", ext: ["ttl"]}, {name: "TypeScript", mime: "application/typescript", mode: "javascript", ext: ["ts"], alias: ["ts"]}, {name: "Twig", mime: "text/x-twig", mode: "twig"}, + {name: "Web IDL", mime: "text/x-webidl", mode: "webidl", ext: ["webidl"]}, {name: "VB.NET", mime: "text/x-vb", mode: "vb", ext: ["vb"]}, {name: "VBScript", mime: "text/vbscript", mode: "vbscript", ext: ["vbs"]}, {name: "Velocity", mime: "text/velocity", mode: "velocity", ext: ["vtl"]}, diff --git a/mode/webidl/index.html b/mode/webidl/index.html new file mode 100644 index 00000000..1d4112e1 --- /dev/null +++ b/mode/webidl/index.html @@ -0,0 +1,71 @@ + + +CodeMirror: Web IDL mode + + + + + + + + + + +
    +

    Web IDL mode

    + +
    + +
    + + + +

    MIME type defined: text/x-webidl.

    +
    diff --git a/mode/webidl/webidl.js b/mode/webidl/webidl.js new file mode 100644 index 00000000..6a60fa23 --- /dev/null +++ b/mode/webidl/webidl.js @@ -0,0 +1,197 @@ +// 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"; + +function wordRegexp(words) { + return new RegExp("^((" + words.join(")|(") + "))\\b"); +}; + +var builtinArray = [ + "Clamp", + "Constructor", + "EnforceRange", + "Exposed", + "ImplicitThis", + "Global", "PrimaryGlobal", + "LegacyArrayClass", + "LegacyUnenumerableNamedProperties", + "LenientThis", + "NamedConstructor", + "NewObject", + "NoInterfaceObject", + "OverrideBuiltins", + "PutForwards", + "Replaceable", + "SameObject", + "TreatNonObjectAsNull", + "TreatNullAs", + "EmptyString", + "Unforgeable", + "Unscopeable" +]; +var builtins = wordRegexp(builtinArray); + +var typeArray = [ + "unsigned", "short", "long", // UnsignedIntegerType + "unrestricted", "float", "double", // UnrestrictedFloatType + "boolean", "byte", "octet", // Rest of PrimitiveType + "Promise", // PromiseType + "ArrayBuffer", "DataView", "Int8Array", "Int16Array", "Int32Array", + "Uint8Array", "Uint16Array", "Uint32Array", "Uint8ClampedArray", + "Float32Array", "Float64Array", // BufferRelatedType + "ByteString", "DOMString", "USVString", "sequence", "object", "RegExp", + "Error", "DOMException", "FrozenArray", // Rest of NonAnyType + "any", // Rest of SingleType + "void" // Rest of ReturnType +]; +var types = wordRegexp(typeArray); + +var keywordArray = [ + "attribute", "callback", "const", "deleter", "dictionary", "enum", "getter", + "implements", "inherit", "interface", "iterable", "legacycaller", "maplike", + "partial", "required", "serializer", "setlike", "setter", "static", + "stringifier", "typedef", // ArgumentNameKeyword except + // "unrestricted" + "optional", "readonly", "or" +]; +var keywords = wordRegexp(keywordArray); + +var atomArray = [ + "true", "false", // BooleanLiteral + "Infinity", "NaN", // FloatLiteral + "null" // Rest of ConstValue +]; +var atoms = wordRegexp(atomArray); + +CodeMirror.registerHelper("hintWords", "webidl", + builtinArray.concat(typeArray).concat(keywordArray).concat(atomArray)); + +var startDefArray = ["callback", "dictionary", "enum", "interface"]; +var startDefs = wordRegexp(startDefArray); + +var endDefArray = ["typedef"]; +var endDefs = wordRegexp(endDefArray); + +var singleOperators = /^[:<=>?]/; +var integers = /^-?([1-9][0-9]*|0[Xx][0-9A-Fa-f]+|0[0-7]*)/; +var floats = /^-?(([0-9]+\.[0-9]*|[0-9]*\.[0-9]+)([Ee][+-]?[0-9]+)?|[0-9]+[Ee][+-]?[0-9]+)/; +var identifiers = /^_?[A-Za-z][0-9A-Z_a-z-]*/; +var strings = /^"[^"]*"/; +var multilineComments = /^\/\*.*?\*\//; +var multilineCommentsStart = /^\/\*.*/; +var multilineCommentsEnd = /^.*?\*\//; + +function readToken(stream, state) { + // whitespace + if (stream.eatSpace()) return null; + + // comment + if (state.inComment) { + if (stream.match(multilineCommentsEnd)) { + state.inComment = false; + return "comment"; + } + stream.skipToEnd(); + return "comment"; + } + if (stream.match("//")) { + stream.skipToEnd(); + return "comment"; + } + if (stream.match(multilineComments)) return "comment"; + if (stream.match(multilineCommentsStart)) { + state.inComment = true; + return "comment"; + } + + // integer and float + if (stream.match(/^-?[0-9\.]/, false)) { + if (stream.match(integers) || stream.match(floats)) return "number"; + } + + // string + if (stream.match(strings)) return "string"; + + // identifier + var pos = stream.pos; + if (stream.match(identifiers)) { + if (state.startDef) return "def"; + if (state.endDef && stream.match(/^\s*;/, false)) { + state.endDef = false; + return "def"; + } + stream.pos = pos; + } + + if (stream.match(keywords)) return "keyword"; + + if (stream.match(types)) { + var lastToken = state.lastToken; + var nextToken = (stream.match(/^\s*(.+?)\b/, false) || [])[1]; + + if (lastToken === ":" || lastToken === "implements" || + nextToken === "implements" || nextToken === "=") { + // Used as identifier + return "builtin"; + } else { + // Used as type + return "variable-3"; + } + } + + if (stream.match(builtins)) return "builtin"; + if (stream.match(atoms)) return "atom"; + if (stream.match(identifiers)) return "variable"; + + // other + if (stream.match(singleOperators)) return "operator"; + + // unrecognized + stream.next(); + return null; +}; + +CodeMirror.defineMode("webidl", function() { + return { + startState: function() { + return { + // Is in multiline comment + inComment: false, + // Last non-whitespace, matched token + lastToken: "", + // Next token is a definition + startDef: false, + // Last token of the statement is a definition + endDef: false + }; + }, + token: function(stream, state) { + var style = readToken(stream, state); + + if (style) { + var cur = stream.current(); + state.lastToken = cur; + if (style === "keyword") { + state.startDef = startDefs.test(cur); + state.endDef = state.endDef || endDefs.test(cur); + } else { + state.startDef = false; + } + } + + return style; + } + }; +}); + +CodeMirror.defineMIME("text/x-webidl", "webidl"); +}); From def70631ca7a50cfb5bf414c27df4db53f899a15 Mon Sep 17 00:00:00 2001 From: Timothy Gu Date: Thu, 7 Apr 2016 06:36:38 -0700 Subject: [PATCH 0235/1790] [webidl mode] Remove unnecessary pos saving --- mode/webidl/webidl.js | 2 -- 1 file changed, 2 deletions(-) diff --git a/mode/webidl/webidl.js b/mode/webidl/webidl.js index 6a60fa23..6f024c63 100644 --- a/mode/webidl/webidl.js +++ b/mode/webidl/webidl.js @@ -122,14 +122,12 @@ function readToken(stream, state) { if (stream.match(strings)) return "string"; // identifier - var pos = stream.pos; if (stream.match(identifiers)) { if (state.startDef) return "def"; if (state.endDef && stream.match(/^\s*;/, false)) { state.endDef = false; return "def"; } - stream.pos = pos; } if (stream.match(keywords)) return "keyword"; From 300f7f8eb885fa5ff5cdbf0a43126849a7881095 Mon Sep 17 00:00:00 2001 From: Kris Ciccarello Date: Thu, 7 Apr 2016 13:35:42 -0400 Subject: [PATCH 0236/1790] [docs] `doc.getSelections` returns an array of strings. --- doc/manual.html | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/doc/manual.html b/doc/manual.html index b513bf94..8b600eac 100644 --- a/doc/manual.html +++ b/doc/manual.html @@ -1254,7 +1254,7 @@

    Cursor and selection methods

    separator to put between the lines in the output. When multiple selections are present, they are concatenated with instances of lineSep in between. -
    doc.getSelections(?lineSep: string) → string
    +
    doc.getSelections(?lineSep: string) → array<string>
    Returns an array containing a string for each selection, representing the content of the selections.
    From bef7e37a093a55ca7f14e1cd823431db07326200 Mon Sep 17 00:00:00 2001 From: Michael Zhou Date: Thu, 7 Apr 2016 17:10:23 -0400 Subject: [PATCH 0237/1790] [mode/meta] Recognize Bazel, Buck and Pants build files as Python in mode/meta.js BUCK, BUILD, *.BUILD and *.bzl files are build files for the three build systems mentioned, and they are all valid Python files. --- mode/meta.js | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/mode/meta.js b/mode/meta.js index dcdcdab9..20ebe0ae 100644 --- a/mode/meta.js +++ b/mode/meta.js @@ -105,7 +105,7 @@ {name: "PowerShell", mime: "application/x-powershell", mode: "powershell", ext: ["ps1", "psd1", "psm1"]}, {name: "Properties files", mime: "text/x-properties", mode: "properties", ext: ["properties", "ini", "in"], alias: ["ini", "properties"]}, {name: "ProtoBuf", mime: "text/x-protobuf", mode: "protobuf", ext: ["proto"]}, - {name: "Python", mime: "text/x-python", mode: "python", ext: ["py", "pyw"]}, + {name: "Python", mime: "text/x-python", mode: "python", ext: ["BUILD", "bzl", "py", "pyw"], file: /^(BUCK|BUILD)$/}, {name: "Puppet", mime: "text/x-puppet", mode: "puppet", ext: ["pp"]}, {name: "Q", mime: "text/x-q", mode: "q", ext: ["q"]}, {name: "R", mime: "text/x-rsrc", mode: "r", ext: ["r"], alias: ["rscript"]}, From 44e4bd42c7ecbd67a48d1c005bdde6140f8475cd Mon Sep 17 00:00:00 2001 From: Gary Sheng Date: Thu, 7 Apr 2016 17:07:17 -0400 Subject: [PATCH 0238/1790] [sql mode] Add __key__ keyword to GQL MIME definition --- 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 c90918b7..daec60ce 100644 --- a/mode/sql/sql.js +++ b/mode/sql/sql.js @@ -376,7 +376,7 @@ CodeMirror.defineMode("sql", function(config, parserConfig) { name: "sql", keywords: set("ancestor and asc by contains desc descendant distinct from group has in is limit offset on order select superset where"), atoms: set("false true"), - builtin: set("blob datetime first key string integer double boolean null"), + builtin: set("blob datetime first key __key__ string integer double boolean null"), operatorChars: /^[*+\-%<>!=]/ }); }()); From 7fa0cdc493e48571e163317d27b58483f5c8d464 Mon Sep 17 00:00:00 2001 From: Jared Dean Date: Fri, 1 Apr 2016 11:37:36 -0400 Subject: [PATCH 0239/1790] [sas mode] Add --- AUTHORS | 1 + mode/meta.js | 1 + mode/sas/index.html | 87 ++++++++++ mode/sas/sas.js | 381 ++++++++++++++++++++++++++++++++++++++++++++ 4 files changed, 470 insertions(+) create mode 100644 mode/sas/index.html create mode 100755 mode/sas/sas.js diff --git a/AUTHORS b/AUTHORS index 03b1ac5b..2c8a9bf3 100644 --- a/AUTHORS +++ b/AUTHORS @@ -238,6 +238,7 @@ Jan Keromnes Jan Odvarko Jan Schär Jan T. Sott +Jared Dean Jared Forsyth Jason Jason Barnabe diff --git a/mode/meta.js b/mode/meta.js index 20ebe0ae..f93078c0 100644 --- a/mode/meta.js +++ b/mode/meta.js @@ -114,6 +114,7 @@ {name: "RPM Spec", mime: "text/x-rpm-spec", mode: "rpm", ext: ["spec"]}, {name: "Ruby", mime: "text/x-ruby", mode: "ruby", ext: ["rb"], alias: ["jruby", "macruby", "rake", "rb", "rbx"]}, {name: "Rust", mime: "text/x-rustsrc", mode: "rust", ext: ["rs"]}, + {name: "SAS", mime: "text/x-sas", mode: "sas", ext: ["sas"]}, {name: "Sass", mime: "text/x-sass", mode: "sass", ext: ["sass"]}, {name: "Scala", mime: "text/x-scala", mode: "clike", ext: ["scala"]}, {name: "Scheme", mime: "text/x-scheme", mode: "scheme", ext: ["scm", "ss"]}, diff --git a/mode/sas/index.html b/mode/sas/index.html new file mode 100644 index 00000000..80957403 --- /dev/null +++ b/mode/sas/index.html @@ -0,0 +1,87 @@ + + +CodeMirror: SAS mode + + + + + + + + + + + +
    +

    SAS mode

    + + + + + +

    MIME types defined: text/x-sas.

    + + +
    diff --git a/mode/sas/sas.js b/mode/sas/sas.js new file mode 100755 index 00000000..4043bc70 --- /dev/null +++ b/mode/sas/sas.js @@ -0,0 +1,381 @@ +// CodeMirror, copyright (c) by Marijn Haverbeke and others +// Distributed under an MIT license: http://codemirror.net/LICENSE + + +// SAS mode copyright (c) 2016 Jared Dean, SAS Institute +// Created by Jared Dean + +// TODO +// indent and de-indent +// identify macro variables + + +//Definitions +// comment -- text withing * ; or /* */ +// keyword -- SAS language variable +// variable -- macro variables starts with '&' or variable formats +// variable-2 -- DATA Step, proc, or macro names +// string -- text within ' ' or " " +// operator -- numeric operator + / - * ** le eq ge ... and so on +// builtin -- proc %macro data run mend +// atom +// def + + +(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("sas", function () { + var words = {}; + var isDoubleOperatorSym = { + eq: 'operator', + lt: 'operator', + le: 'operator', + gt: 'operator', + ge: 'operator', + in: 'operator', + ne: 'operator', + or: 'operator' + }; + var isDoubleOperatorChar = /(<=|>=|!=|<>)/; + var isSingleOperatorChar = /[=\(:\),{}.*<>+\-\/^\[\]]/; + + // Takes a string of words separated by spaces and adds them as + // keys with the value of the first argument 'style' + var define = function (style, string, context) { + if (context) { + var split = string.split(' '); + for (var i = 0; i < split.length; i++) { + words[split[i]] = {style: style, state: context}; + } + } + }; + //datastep + define('def', 'stack pgm view source debug nesting nolist', ['inDataStep']); + define('def', 'if while until for do do; end end; then else cancel', ['inDataStep']); + define('def', 'label format _n_ _error_', ['inDataStep']); + define('def', 'ALTER BUFNO BUFSIZE CNTLLEV COMPRESS DLDMGACTION ENCRYPT ENCRYPTKEY EXTENDOBSCOUNTER GENMAX GENNUM INDEX LABEL OBSBUF OUTREP PW PWREQ READ REPEMPTY REPLACE REUSE ROLE SORTEDBY SPILL TOBSNO TYPE WRITE FILECLOSE FIRSTOBS IN OBS POINTOBS WHERE WHEREUP IDXNAME IDXWHERE DROP KEEP RENAME', ['inDataStep']); + define('def', 'filevar finfo finv fipname fipnamel fipstate first firstobs floor', ['inDataStep']); + define('def', 'varfmt varinfmt varlabel varlen varname varnum varray varrayx vartype verify vformat vformatd vformatdx vformatn vformatnx vformatw vformatwx vformatx vinarray vinarrayx vinformat vinformatd vinformatdx vinformatn vinformatnx vinformatw vinformatwx vinformatx vlabel vlabelx vlength vlengthx vname vnamex vnferr vtype vtypex weekday', ['inDataStep']); + define('def', 'zipfips zipname zipnamel zipstate', ['inDataStep']); + define('def', 'put putc putn', ['inDataStep']); + define('builtin', 'data run', ['inDataStep']); + + + //proc + define('def', 'data', ['inProc']); + + // flow control for macros + define('def', '%if %end %end; %else %else; %do %do; %then', ['inMacro']); + + //everywhere + define('builtin', 'proc run; quit; libname filename %macro %mend option options', ['ALL']); + + define('def', 'footnote title libname ods', ['ALL']); + define('def', '%let %put %global %sysfunc %eval ', ['ALL']); + // automatic macro variables http://support.sas.com/documentation/cdl/en/mcrolref/61885/HTML/default/viewer.htm#a003167023.htm + define('variable', '&sysbuffr &syscc &syscharwidth &syscmd &sysdate &sysdate9 &sysday &sysdevic &sysdmg &sysdsn &sysencoding &sysenv &syserr &syserrortext &sysfilrc &syshostname &sysindex &sysinfo &sysjobid &syslast &syslckrc &syslibrc &syslogapplname &sysmacroname &sysmenv &sysmsg &sysncpu &sysodspath &sysparm &syspbuff &sysprocessid &sysprocessname &sysprocname &sysrc &sysscp &sysscpl &sysscpl &syssite &sysstartid &sysstartname &systcpiphostname &systime &sysuserid &sysver &sysvlong &sysvlong4 &syswarningtext', ['ALL']); + + //footnote[1-9]? title[1-9]? + + //options statement + define('def', 'source2 nosource2 page pageno pagesize', ['ALL']); + + //proc and datastep + define('def', '_all_ _character_ _cmd_ _freq_ _i_ _infile_ _last_ _msg_ _null_ _numeric_ _temporary_ _type_ abort abs addr adjrsq airy alpha alter altlog altprint and arcos array arsin as atan attrc attrib attrn authserver autoexec awscontrol awsdef awsmenu awsmenumerge awstitle backward band base betainv between blocksize blshift bnot bor brshift bufno bufsize bxor by byerr byline byte calculated call cards cards4 catcache cbufno cdf ceil center cexist change chisq cinv class cleanup close cnonct cntllev coalesce codegen col collate collin column comamid comaux1 comaux2 comdef compbl compound compress config continue convert cos cosh cpuid create cross crosstab css curobs cv daccdb daccdbsl daccsl daccsyd dacctab dairy datalines datalines4 datejul datepart datetime day dbcslang dbcstype dclose ddm delete delimiter depdb depdbsl depsl depsyd deptab dequote descending descript design= device dflang dhms dif digamma dim dinfo display distinct dkricond dkrocond dlm dnum do dopen doptname doptnum dread drop dropnote dsname dsnferr echo else emaildlg emailid emailpw emailserver emailsys encrypt end endsas engine eof eov erf erfc error errorcheck errors exist exp fappend fclose fcol fdelete feedback fetch fetchobs fexist fget file fileclose fileexist filefmt filename fileref fmterr fmtsearch fnonct fnote font fontalias fopen foptname foptnum force formatted formchar formdelim formdlim forward fpoint fpos fput fread frewind frlen from fsep fuzz fwrite gaminv gamma getoption getvarc getvarn go goto group gwindow hbar hbound helpenv helploc hms honorappearance hosthelp hostprint hour hpct html hvar ibessel ibr id if index indexc indexw initcmd initstmt inner input inputc inputn inr insert int intck intnx into intrr invaliddata irr is jbessel join juldate keep kentb kurtosis label lag last lbound leave left length levels lgamma lib library libref line linesize link list log log10 log2 logpdf logpmf logsdf lostcard lowcase lrecl ls macro macrogen maps mautosource max maxdec maxr mdy mean measures median memtype merge merror min minute missing missover mlogic mod mode model modify month mopen mort mprint mrecall msglevel msymtabmax mvarsize myy n nest netpv new news nmiss no nobatch nobs nocaps nocardimage nocenter nocharcode nocmdmac nocol nocum nodate nodbcs nodetails nodmr nodms nodmsbatch nodup nodupkey noduplicates noechoauto noequals noerrorabend noexitwindows nofullstimer noicon noimplmac noint nolist noloadlist nomiss nomlogic nomprint nomrecall nomsgcase nomstored nomultenvappl nonotes nonumber noobs noovp nopad nopercent noprint noprintinit normal norow norsasuser nosetinit nosplash nosymbolgen note notes notitle notitles notsorted noverbose noxsync noxwait npv null number numkeys nummousekeys nway obs on open order ordinal otherwise out outer outp= output over ovp p(1 5 10 25 50 75 90 95 99) pad pad2 paired parm parmcards path pathdll pathname pdf peek peekc pfkey pmf point poisson poke position printer probbeta probbnml probchi probf probgam probhypr probit probnegb probnorm probsig probt procleave prt ps pw pwreq qtr quote r ranbin rancau ranexp rangam range ranks rannor ranpoi rantbl rantri ranuni read recfm register regr remote remove rename repeat replace resolve retain return reuse reverse rewind right round rsquare rtf rtrace rtraceloc s s2 samploc sasautos sascontrol sasfrscr sasmsg sasmstore sasscript sasuser saving scan sdf second select selection separated seq serror set setcomm setot sign simple sin sinh siteinfo skewness skip sle sls sortedby sortpgm sortseq sortsize soundex spedis splashlocation split spool sqrt start std stderr stdin stfips stimer stname stnamel stop stopover subgroup subpopn substr sum sumwgt symbol symbolgen symget symput sysget sysin sysleave sysmsg sysparm sysprint sysprintfont sysprod sysrc system t table tables tan tanh tapeclose tbufsize terminal test then timepart tinv tnonct to today tol tooldef totper transformout translate trantab tranwrd trigamma trim trimn trunc truncover type unformatted uniform union until upcase update user usericon uss validate value var weight when where while wincharset window work workinit workterm write wsum xsync xwait yearcutoff yes yyq min max', ['inDataStep', 'inProc']); + define('operator', 'and not ', ['inDataStep', 'inProc']); + + // Main function + function tokenize(stream, state) { + // Finally advance the stream + var ch = stream.next(); + + // BLOCKCOMMENT + if (ch === '/' && stream.eat('*')) { + state.continueComment = true; + return "comment"; + } + // in comment block + else if (state.continueComment === true) { + //comment ends at the beginning of the line + if (ch === '*' && stream.peek() === '/') { + stream.next(); + state.continueComment = false; + } + //comment is potentially later in line + else if (stream.skipTo('*')) { + stream.skipTo('*'); + stream.next(); + if (stream.eat('/')) { + state.continueComment = false; + } + } + else { + stream.skipToEnd(); + } + return "comment"; + } + + // DoubleOperator match + var doubleOperator = ch + stream.peek(); + + // Match all line comments. + var myString = stream.string; + var myRegexp = /(?:^\s*|[;]\s*)(\*.*?);/ig; + var match = myRegexp.exec(myString); + if (match !== null) + { + if (match.index === 0 && (stream.column() !== (match.index + match[0].length - 1))) { + stream.backUp(stream.column()); + stream.skipTo(';'); + stream.next(); + return 'comment'; + } + // the ';' triggers the match so move one past it to start + // the comment block that is why match.index+1 + else if (match.index + 1 < stream.column() && stream.column() < match.index + match[0].length - 1) { + stream.backUp(stream.column() - match.index - 1); + stream.skipTo(';'); + stream.next(); + return 'comment'; + } + } + // Have we found a string? + else + if (!state.continueString && (ch === '"' || ch === "'")) { + state.continueString = ch; //save the matching quote in the state + return "string"; + } + else if (state.continueString !== null) { + if (stream.skipTo(state.continueString)) { + // quote found on this line + stream.next(); + state.continueString = null; + } + else { + stream.skipToEnd(); + } + return "string"; + } + else if (state.continueString !== null && stream.eol()) { + stream.skipTo(state.continueString) || stream.skipToEnd(); + return "string"; + } + //find numbers + else if (/[\d\.]/.test(ch)) { + if (ch === ".") { + stream.match(/^[0-9]+([eE][\-+]?[0-9]+)?/); + } else if (ch === "0") { + stream.match(/^[xX][0-9a-fA-F]+/) || stream.match(/^0[0-7]+/); + } else { + stream.match(/^[0-9]*\.?[0-9]*([eE][\-+]?[0-9]+)?/); + } + return "number"; + } + // TWO SYMBOL TOKENS + else if (isDoubleOperatorChar.test(ch + stream.peek())) { + stream.next(); + state.tokenize = null; + return "operator"; + } + else if (isDoubleOperatorSym.hasOwnProperty(doubleOperator)) { + stream.next(); + if (stream.peek() === ' ') { + return isDoubleOperatorSym[doubleOperator.toLowerCase()]; + } + + } + // SINGLE SYMBOL TOKENS + else if (isSingleOperatorChar.test(ch)) { + state.tokenize = null; + return "operator"; + } + + // Matches one whole word -- even if the word is a character + var word; + if (stream.match(/[%&;\w]+/, false) != null) { + word = ch + stream.match(/[%&;\w]+/, true); + if (/&/.test(word)) { + return 'variable' + } + } + else { + + word = ch; + } + // the word after DATA PROC or MACRO + if (state.nextword) { + stream.match(/[\w]+/); + // match memname.libname + if (stream.peek() === '.') { + stream.skipTo(' '); + } + state.nextword = false; + return 'variable-2'; + + } + + // Are we in a DATA Step? + if (state.inDataStep) { + if (word.toLowerCase() === 'run;' || stream.match(/run\s;/)) { + state.inDataStep = false; + return 'builtin'; + } + // variable formats + if ((word) && stream.next() === '.') { + //either a format or libname.memname + if (/\w/.test(stream.peek())) { + //libname.memname + return 'variable-2'; + } + else { + //format + return 'variable'; + } + } + // do we have a DATA Step keyword + if (word && words.hasOwnProperty(word.toLowerCase()) && (words[word.toLowerCase()].state.indexOf("inDataStep") !== -1 || words[word.toLowerCase()].state.indexOf("ALL") !== -1)) { + //backup to the start of the word + if (stream.start < stream.pos) { + stream.backUp(stream.pos - stream.start); + } + //advance the length of the word and return + for (var i = 0; i < word.length; ++i) { + stream.next(); + } + return words[word.toLowerCase()].style; + } + + + } + // Are we in an Proc statement? + if (state.inProc) { + if (word.toLowerCase() === 'run;' || word.toLowerCase() === 'quit;') { + state.inProc = false; + return 'builtin'; + } + // do we have a proc keyword + if (word && words.hasOwnProperty(word.toLowerCase()) && (words[word.toLowerCase()].state.indexOf("inProc") !== -1 || words[word.toLowerCase()].state.indexOf("ALL") !== -1)) { + stream.match(/[\w]+/); + return words[word].style; + } + + + } + // Are we in a Macro statement? + if (state.inMacro) { + if (word.toLowerCase() === '%mend') { + if (stream.peek() === ';') { + stream.next(); + } + state.inMacro = false; + return 'builtin'; + } + if (word && words.hasOwnProperty(word.toLowerCase()) && (words[word.toLowerCase()].state.indexOf("inMacro") !== -1 || words[word.toLowerCase()].state.indexOf("ALL") !== -1)) { + stream.match(/[\w]+/); + return words[word.toLowerCase()].style; + } + + return 'atom'; + } + // Do we have Keywords specific words? + if (word && words.hasOwnProperty(word.toLowerCase())) { + // Negates the initial next() + stream.backUp(1); + // Actually move the stream + stream.match(/[\w]+/); + if (word.toLowerCase() === 'data' && /=/.test(stream.peek()) === false) { + state.inDataStep = true; + state.nextword = true; + return 'builtin'; + } + if (word.toLowerCase() === 'proc') { + state.inProc = true; + state.nextword = true; + return 'builtin'; + } + if (word.toLowerCase() === '%macro') { + state.inMacro = true; + state.nextword = true; + return 'builtin'; + } + if (/title[1-9]/i.test(word/*+stream.peek()*/)) { + //if (/title[1-9]/.test(word.toLowerCase())) { + //stream.next(); + return 'def'; + } + if (word.toLowerCase() === 'footnote' && /[1-9]/.test(stream.peek())) { + stream.eat(); + return 'def'; + } + else if (word.toLowerCase() === 'footnote') { + return 'def'; + } + + // Returns their value as state in the prior define methods + if (state.inDataStep === true && words[word.toLowerCase()].state.indexOf("inDataStep") !== -1) { + return words[word.toLowerCase()].style; + } + else if (state.inProc === true && words[word.toLowerCase()].state.indexOf("inProc") !== -1) { + return words[word.toLowerCase()].style; + } + else if (state.inMacro === true && words[word.toLowerCase()].state.indexOf("inMacro") !== -1) { + return words[word.toLowerCase()].style; + } + else if (words[word.toLowerCase()].state.indexOf("ALL") !== -1) { + return words[word.toLowerCase()].style; + } + else { + return null; + } + } + // Return a blank line for everything else + return null; + } + + // Start here + return { + startState: function () { + var state = {}; + state.inDataStep = false; + state.inProc = false; + state.inMacro = false; + state.pending = false; + state.lastToken = null; + state.nextword = false; + state.continueString = null; + state.continueComment = false; + state.tokenize = null; + return state; + }, + token: function (stream, state) { + if (state.tokenize != null) { + return state.tokenize(stream, state); + } + // Strip the spaces, but regex will account for them either way + if (stream.eatSpace()) return null; + var style = state.tokenize; + if (style === "comment") return style; + // Go through the main process + return tokenize(stream, state); + }, + + blockCommentStart: "/*", + blockCommentEnd: "*/" + }; + + }); + + CodeMirror.defineMIME("text/x-sas", "sas"); + +}); From 9389a050790e2be94530f34a3513e34602d39483 Mon Sep 17 00:00:00 2001 From: Marijn Haverbeke Date: Fri, 8 Apr 2016 16:44:18 +0200 Subject: [PATCH 0240/1790] [sas mode] Code style, integrate Issue #3932 --- mode/index.html | 1 + mode/sas/index.html | 34 +-- mode/sas/sas.js | 624 ++++++++++++++++++++------------------------ 3 files changed, 294 insertions(+), 365 deletions(-) diff --git a/mode/index.html b/mode/index.html index 822f04fc..be583159 100644 --- a/mode/index.html +++ b/mode/index.html @@ -114,6 +114,7 @@

    Language modes

  • reStructuredText
  • Ruby
  • Rust
  • +
  • SAS
  • Sass
  • Spreadsheet
  • Scala
  • diff --git a/mode/sas/index.html b/mode/sas/index.html index 80957403..636e0659 100644 --- a/mode/sas/index.html +++ b/mode/sas/index.html @@ -6,15 +6,14 @@ - + .CodeMirror {border-top: 1px solid black; border-bottom: 1px solid black;} + .cm-s-default .cm-trailing-space-a:before, + .cm-s-default .cm-trailing-space-b:before {position: absolute; content: "\00B7"; color: #777;} + .cm-s-default .cm-trailing-space-new-line:before {position: absolute; content: "\21B5"; color: #777;} +
    - The plugin understands the following options (the options object + +
    The plugin understands the following options (the options object will also be passed along to the hinting function, which may understand additional options):
    @@ -2580,7 +2581,10 @@

    Addons

    arguments (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. By default, hinting only works when there is no + completions. + The hinting function can also return a promise, and the completion + interface will only be popped when the promise resolves. + By default, hinting only works when there is no selection. You can give a hinting function a supportsSelection property with a truthy value to indicate that it supports selections.
    From 87d10fa06bfe63b94ace873df0014128f9769107 Mon Sep 17 00:00:00 2001 From: Marijn Haverbeke Date: Wed, 20 Apr 2016 10:23:44 +0200 Subject: [PATCH 0253/1790] [show-hint addon] Simplify Promise support Issue #3974 --- addon/hint/show-hint.js | 35 +++++------- demo/complete.html | 123 +++++++++++++++------------------------- doc/manual.html | 4 +- 3 files changed, 63 insertions(+), 99 deletions(-) diff --git a/addon/hint/show-hint.js b/addon/hint/show-hint.js index 46ca61b9..f426b5c8 100644 --- a/addon/hint/show-hint.js +++ b/addon/hint/show-hint.js @@ -108,13 +108,11 @@ }, update: function(first) { - if (this.tick == null) return; - var self = this; - var hint = asynchronifyHinter(this.options.hint) - var myTick = ++this.tick; - 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) { @@ -360,17 +358,14 @@ return result } - function asynchronifyHinter(helper){ - // given a hinter function, return an async one. - if(helper.async) return helper - var _help = function(cm, callback, option){ - var res = helper(cm, option) - if(res && res.then) return res.then(callback) - return callback(res) + 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) } - _help.async = true; - _help.supportsSelection = helper.supportsSelection; - return _help; } function resolveAutoHints(cm, pos) { @@ -380,12 +375,10 @@ var app = applicableHelpers(cm, helpers); function run(i) { if (i == app.length) return callback(null) - var helper = app[i] - helper = asynchronifyHinter(helper); - helper(cm, function(result) { + fetchHints(app[i], cm, options, function(result) { if (result && result.list.length > 0) callback(result) else run(i + 1) - }, options) + }) } run(0) } diff --git a/demo/complete.html b/demo/complete.html index fac48cbf..18e17c81 100644 --- a/demo/complete.html +++ b/demo/complete.html @@ -70,86 +70,57 @@

    Autocomplete Demo

    and javascript-hint addons.

    - -
    + - - - - - - + diff --git a/doc/manual.html b/doc/manual.html index 6588e71c..5f3b2330 100644 --- a/doc/manual.html +++ b/doc/manual.html @@ -2568,9 +2568,9 @@

    Addons

    to: {line, ch}
    Optional to position that will be used by pick() instead of the global one passed with the full list of completions.
    - + -
    The plugin understands the following options (the options object +
    The plugin understands the following options (the options object will also be passed along to the hinting function, which may understand additional options):
    From db62891c78fb9240ad9e4020020e7ed81a03ef6a Mon Sep 17 00:00:00 2001 From: Matthias Bussonnier Date: Tue, 19 Apr 2016 10:24:13 +0200 Subject: [PATCH 0254/1790] [Python] distinguish decorator at beginning of line from __matmul__ @ as __matmul__ operator only on Python 3, Add tests for Python mode for this use case. Closes #3957 --- mode/python/python.js | 19 ++++++++++++++----- mode/python/test.js | 28 ++++++++++++++++++++++++++++ test/index.html | 2 ++ 3 files changed, 44 insertions(+), 5 deletions(-) create mode 100644 mode/python/test.js diff --git a/mode/python/python.js b/mode/python/python.js index 9160b8e7..95e4d1fe 100644 --- a/mode/python/python.js +++ b/mode/python/python.js @@ -249,15 +249,24 @@ } function tokenLexer(stream, state) { + var sol = stream.sol(); + if (sol){ + state.whiteSpaceAtBeginningOfLine = true; + } var style = state.tokenize(stream, state); var current = stream.current(); // Handle decorators - if (current == "@") { - if (parserConf.version && parseInt(parserConf.version, 10) == 3) - return stream.match(identifiers, false) ? "meta" : "operator"; - else - return stream.match(identifiers, false) ? "meta" : ERRORCLASS; + if (state.whiteSpaceAtBeginningOfLine){ + if (current == "@") { + if (parserConf.version && parseInt(parserConf.version, 10) == 3) + return stream.match(identifiers, false) ? "meta" : "operator"; + else + return stream.match(identifiers, false) ? "meta" : ERRORCLASS; + } + } + if(!current.match(/^ +$/, false)){ + state.whiteSpaceAtBeginningOfLine = false; } if ((style == "variable" || style == "builtin") diff --git a/mode/python/test.js b/mode/python/test.js new file mode 100644 index 00000000..342c6cf3 --- /dev/null +++ b/mode/python/test.js @@ -0,0 +1,28 @@ +// CodeMirror, copyright (c) by Marijn Haverbeke and others +// Distributed under an MIT license: http://codemirror.net/LICENSE + +(function() { + var mode = CodeMirror.getMode({indentUnit: 4}, + {name: "python", + version: 3, + singleLineStringErrors: false}); + function MT(name) { test.mode(name, mode, Array.prototype.slice.call(arguments, 1)); } + + // Error, because "foobarhello" is neither a known type or property, but + // property was expected (after "and"), and it should be in parentheses. + MT("decoratorStartOfLine", + "[meta @dec]", + "[keyword def] [def function]():", + " [keyword pass]"); + + MT("decoratorIndented", + "[keyword class] [def Foo]:", + " [meta @dec]", + " [keyword def] [def function]():", + " [keyword pass]"); + + MT("matmulWithSpace:", "[variable a] [operator @] [variable b]"); + MT("matmulWithoutSpace:", "[variable a][operator @][variable b]"); + MT("matmulSpaceBefore:", "[variable a] [operator @][variable b]"); + +})(); diff --git a/test/index.html b/test/index.html index 68dc4dfc..90c03351 100644 --- a/test/index.html +++ b/test/index.html @@ -27,6 +27,7 @@ + @@ -123,6 +124,7 @@

    Test Suite

    + From 3b36e587eec6df1d907d48129cd61db45e83a71f Mon Sep 17 00:00:00 2001 From: Marijn Haverbeke Date: Wed, 20 Apr 2016 10:35:21 +0200 Subject: [PATCH 0255/1790] [python mode] Clean up previous patch --- mode/python/python.js | 55 +++++++++++++++---------------------------- 1 file changed, 19 insertions(+), 36 deletions(-) diff --git a/mode/python/python.js b/mode/python/python.js index 95e4d1fe..ec662b1a 100644 --- a/mode/python/python.js +++ b/mode/python/python.js @@ -32,13 +32,6 @@ "sorted", "staticmethod", "str", "sum", "super", "tuple", "type", "vars", "zip", "__import__", "NotImplemented", "Ellipsis", "__debug__"]; - var py2 = {builtins: ["apply", "basestring", "buffer", "cmp", "coerce", "execfile", - "file", "intern", "long", "raw_input", "reduce", "reload", - "unichr", "unicode", "xrange", "False", "True", "None"], - keywords: ["exec", "print"]}; - var py3 = {builtins: ["ascii", "bytes", "exec", "print"], - keywords: ["nonlocal", "False", "True", "None", "async", "await"]}; - CodeMirror.registerHelper("hintWords", "python", commonKeywords.concat(commonBuiltins)); function top(state) { @@ -53,15 +46,6 @@ var doubleDelimiters = parserConf.doubleDelimiters || /^(\+=|\-=|\*=|%=|\/=|&=|\|=|\^=)/; var tripleDelimiters = parserConf.tripleDelimiters || /^(\/\/=|>>=|<<=|\*\*=)/; - if (parserConf.version && parseInt(parserConf.version, 10) == 3) { - // since http://legacy.python.org/dev/peps/pep-0465/ @ is also an operator - var singleOperators = parserConf.singleOperators || /^[\+\-\*\/%&|\^~<>!@]/; - var identifiers = parserConf.identifiers|| /^[_A-Za-z\u00A1-\uFFFF][_A-Za-z0-9\u00A1-\uFFFF]*/; - } else { - var singleOperators = parserConf.singleOperators || /^[\+\-\*\/%&|\^~<>!]/; - var identifiers = parserConf.identifiers|| /^[_A-Za-z][_A-Za-z0-9]*/; - } - var hangingIndent = parserConf.hangingIndent || conf.indentUnit; var myKeywords = commonKeywords, myBuiltins = commonBuiltins; @@ -71,13 +55,21 @@ if (parserConf.extra_builtins != undefined) myBuiltins = myBuiltins.concat(parserConf.extra_builtins); - if (parserConf.version && parseInt(parserConf.version, 10) == 3) { - myKeywords = myKeywords.concat(py3.keywords); - myBuiltins = myBuiltins.concat(py3.builtins); + var py3 = parserConf.version && parseInt(parserConf.version, 10) == 3 + if (py3) { + // since http://legacy.python.org/dev/peps/pep-0465/ @ is also an operator + var singleOperators = parserConf.singleOperators || /^[\+\-\*\/%&|\^~<>!@]/; + var identifiers = parserConf.identifiers|| /^[_A-Za-z\u00A1-\uFFFF][_A-Za-z0-9\u00A1-\uFFFF]*/; + myKeywords = myKeywords.concat(["nonlocal", "False", "True", "None", "async", "await"]); + myBuiltins = myBuiltins.concat(["ascii", "bytes", "exec", "print"]); var stringPrefixes = new RegExp("^(([rbuf]|(br))?('{3}|\"{3}|['\"]))", "i"); } else { - myKeywords = myKeywords.concat(py2.keywords); - myBuiltins = myBuiltins.concat(py2.builtins); + var singleOperators = parserConf.singleOperators || /^[\+\-\*\/%&|\^~<>!]/; + var identifiers = parserConf.identifiers|| /^[_A-Za-z][_A-Za-z0-9]*/; + myKeywords = myKeywords.concat(["exec", "print"]); + myBuiltins = myBuiltins.concat(["apply", "basestring", "buffer", "cmp", "coerce", "execfile", + "file", "intern", "long", "raw_input", "reduce", "reload", + "unichr", "unicode", "xrange", "False", "True", "None"]); var stringPrefixes = new RegExp("^(([rub]|(ur)|(br))?('{3}|\"{3}|['\"]))", "i"); } var keywords = wordRegexp(myKeywords); @@ -249,25 +241,16 @@ } function tokenLexer(stream, state) { - var sol = stream.sol(); - if (sol){ - state.whiteSpaceAtBeginningOfLine = true; - } + if (stream.sol()) state.beginningOfLine = true; + var style = state.tokenize(stream, state); var current = stream.current(); // Handle decorators - if (state.whiteSpaceAtBeginningOfLine){ - if (current == "@") { - if (parserConf.version && parseInt(parserConf.version, 10) == 3) - return stream.match(identifiers, false) ? "meta" : "operator"; - else - return stream.match(identifiers, false) ? "meta" : ERRORCLASS; - } - } - if(!current.match(/^ +$/, false)){ - state.whiteSpaceAtBeginningOfLine = false; - } + if (state.beginningOfLine && current == "@") + return stream.match(identifiers, false) ? "meta" : py3 ? "operator" : ERRORCLASS; + + if (/\S/.test(current)) state.beginningOfLine = false; if ((style == "variable" || style == "builtin") && state.lastToken == "meta") From a579a10d7b6934f3efa7307dccd382b981a728a9 Mon Sep 17 00:00:00 2001 From: Matthias Bussonnier Date: Tue, 19 Apr 2016 10:24:13 +0200 Subject: [PATCH 0256/1790] [python mode] Add two more tests Issue #3973 --- mode/python/test.js | 2 ++ 1 file changed, 2 insertions(+) diff --git a/mode/python/test.js b/mode/python/test.js index 342c6cf3..c1a9c6a9 100644 --- a/mode/python/test.js +++ b/mode/python/test.js @@ -25,4 +25,6 @@ MT("matmulWithoutSpace:", "[variable a][operator @][variable b]"); MT("matmulSpaceBefore:", "[variable a] [operator @][variable b]"); + MT("fValidStringPrefix", "[string f'this is a {formatted} string']"); + MT("uValidStringPrefix", "[string u'this is an unicode string']"); })(); From b609227f0f8e2a5d431960a708ef0a0a0db79adc Mon Sep 17 00:00:00 2001 From: Marijn Haverbeke Date: Wed, 20 Apr 2016 11:09:20 +0200 Subject: [PATCH 0257/1790] Make findMarks only returns marks that overlap the range Closes #3958 --- lib/codemirror.js | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/lib/codemirror.js b/lib/codemirror.js index 70e3fec5..6b9b71c7 100644 --- a/lib/codemirror.js +++ b/lib/codemirror.js @@ -7626,9 +7626,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); } From a7868e1519c67bc6e4f74dbd5ef94098003438df Mon Sep 17 00:00:00 2001 From: Manideep Date: Fri, 15 Apr 2016 12:27:33 +0530 Subject: [PATCH 0258/1790] [real-world uses] Add OpenCampus --- doc/realworld.html | 1 + 1 file changed, 1 insertion(+) diff --git a/doc/realworld.html b/doc/realworld.html index 7129e216..31139f2e 100644 --- a/doc/realworld.html +++ b/doc/realworld.html @@ -122,6 +122,7 @@

    CodeMirror real-world uses

  • nodeMirror (IDE project)
  • NoTex (rST authoring)
  • Oak (online outliner)
  • +
  • OpenCampus
  • ORG (z80 assembly IDE)
  • Orion-CodeMirror integration (running CodeMirror modes in Orion)
  • Paper.js (graphics scripting)
  • From bf8e72f4a51ef588880aa8530388b9b76488f169 Mon Sep 17 00:00:00 2001 From: Sergey Tselovalnikov Date: Fri, 15 Apr 2016 13:14:45 +0300 Subject: [PATCH 0259/1790] [lint addon] Return null checks for annotation Fix "Uncaught TypeError: Cannot read property 'severity' of undefined" --- addon/lint/lint.js | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-) diff --git a/addon/lint/lint.js b/addon/lint/lint.js index 01f322b5..e3a45276 100644 --- a/addon/lint/lint.js +++ b/addon/lint/lint.js @@ -204,7 +204,8 @@ var annotations = []; for (var i = 0; i < spans.length; ++i) { - annotations.push(spans[i].__annotation); + var ann = spans[i].__annotation; + if (ann) annotations.push(ann); } if (annotations.length) popupTooltips(annotations, e); } From 0055101e4527d0b168f3b147680a678eabca8c50 Mon Sep 17 00:00:00 2001 From: Timothy Gu Date: Sat, 16 Apr 2016 18:23:11 -0700 Subject: [PATCH 0260/1790] [mbox mode] Add --- doc/compress.html | 1 + mode/index.html | 1 + mode/mbox/index.html | 44 +++++++++++++++ mode/mbox/mbox.js | 129 +++++++++++++++++++++++++++++++++++++++++++ 4 files changed, 175 insertions(+) create mode 100644 mode/mbox/index.html create mode 100644 mode/mbox/mbox.js diff --git a/doc/compress.html b/doc/compress.html index 44e6fea8..4459f46e 100644 --- a/doc/compress.html +++ b/doc/compress.html @@ -168,6 +168,7 @@

    Script compression helper

    + diff --git a/mode/index.html b/mode/index.html index be583159..732e0e52 100644 --- a/mode/index.html +++ b/mode/index.html @@ -86,6 +86,7 @@

    Language modes

  • Lua
  • Markdown (GitHub-flavour)
  • Mathematica
  • +
  • mbox
  • mIRC
  • Modelica
  • MscGen
  • diff --git a/mode/mbox/index.html b/mode/mbox/index.html new file mode 100644 index 00000000..248ea98e --- /dev/null +++ b/mode/mbox/index.html @@ -0,0 +1,44 @@ + + +CodeMirror: mbox mode + + + + + + + + + +
    +

    mbox mode

    +
    + + +

    MIME types defined: application/mbox.

    + +
    diff --git a/mode/mbox/mbox.js b/mode/mbox/mbox.js new file mode 100644 index 00000000..ba2416ac --- /dev/null +++ b/mode/mbox/mbox.js @@ -0,0 +1,129 @@ +// 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"; + +var rfc2822 = [ + "From", "Sender", "Reply-To", "To", "Cc", "Bcc", "Message-ID", + "In-Reply-To", "References", "Resent-From", "Resent-Sender", "Resent-To", + "Resent-Cc", "Resent-Bcc", "Resent-Message-ID", "Return-Path", "Received" +]; +var rfc2822NoEmail = [ + "Date", "Subject", "Comments", "Keywords", "Resent-Date" +]; + +CodeMirror.registerHelper("hintWords", "mbox", rfc2822.concat(rfc2822NoEmail)); + +var whitespace = /^[ \t]/; +var separator = /^From /; // See RFC 4155 +var rfc2822Header = new RegExp("^(" + rfc2822.join("|") + "): "); +var rfc2822HeaderNoEmail = new RegExp("^(" + rfc2822NoEmail.join("|") + "): "); +var header = /^[^:]+:/; // Optional fields defined in RFC 2822 +var email = /^[^ ]+@[^ ]+/; +var untilEmail = /^.*?(?=[^ ]+?@[^ ]+)/; +var bracketedEmail = /^<.*?>/; +var untilBracketedEmail = /^.*?(?=<.*>)/; + +function styleForHeader(header) { + if (header === "Subject") return "header"; + return "string"; +} + +function readToken(stream, state) { + if (stream.sol()) { + // From last line + state.inSeparator = false; + if (state.inHeader && stream.match(whitespace)) { + // Header folding + return null; + } else { + state.inHeader = false; + state.header = null; + } + + if (stream.match(separator)) { + state.inHeaders = true; + state.inSeparator = true; + return "atom"; + } + + var match; + var emailPermitted = false; + if ((match = stream.match(rfc2822HeaderNoEmail)) || + (emailPermitted = true) && (match = stream.match(rfc2822Header))) { + state.inHeaders = true; + state.inHeader = true; + state.emailPermitted = emailPermitted; + state.header = match[1]; + return "atom"; + } + + // Use vim's heuristics: recognize custom headers only if the line is in a + // block of legitimate headers. + if (state.inHeaders && (match = stream.match(header))) { + state.inHeader = true; + state.emailPermitted = true; + state.header = match[1]; + return "atom"; + } + + state.inHeaders = false; + stream.skipToEnd(); + return null; + } + + if (state.inSeparator) { + if (stream.match(email)) return "link"; + if (stream.match(untilEmail)) return "atom"; + stream.skipToEnd(); + return "atom"; + } + + if (state.inHeader) { + var style = styleForHeader(state.header); + + if (state.emailPermitted) { + if (stream.match(bracketedEmail)) return style + " link"; + if (stream.match(untilBracketedEmail)) return style; + } + stream.skipToEnd(); + return style; + } + + stream.skipToEnd(); + return null; +}; + +CodeMirror.defineMode("mbox", function() { + return { + startState: function() { + return { + // Is in a mbox separator + inSeparator: false, + // Is in a mail header + inHeader: false, + // If bracketed email is permitted. Only applicable when inHeader + emailPermitted: false, + // Name of current header + header: null, + // Is in a region of mail headers + inHeaders: false + }; + }, + token: readToken, + blankLine: function(state) { + state.inHeaders = state.inSeparator = state.inHeader = false; + } + }; +}); + +CodeMirror.defineMIME("application/mbox", "mbox"); +}); From 83483ef884d43264e682357e3cb6e6d652d96c65 Mon Sep 17 00:00:00 2001 From: Marijn Haverbeke Date: Wed, 20 Apr 2016 11:41:40 +0200 Subject: [PATCH 0261/1790] Mark release 5.14.0 --- AUTHORS | 14 ++++++++++++++ CHANGELOG.md | 30 ++++++++++++++++++++++++++++++ doc/compress.html | 1 + doc/manual.html | 2 +- doc/releases.html | 18 ++++++++++++++++++ lib/codemirror.js | 2 +- package.json | 2 +- 7 files changed, 66 insertions(+), 3 deletions(-) diff --git a/AUTHORS b/AUTHORS index 2c8a9bf3..f64cb6d1 100644 --- a/AUTHORS +++ b/AUTHORS @@ -45,6 +45,7 @@ Andre von Houck Andrey Fedorov Andrey Klyuchnikov Andrey Lushnikov +Andrey Shchekin Andy Joslin Andy Kimball Andy Li @@ -75,9 +76,11 @@ benbro Beni Cherniavsky-Paskin Benjamin DeCoste Ben Keen +Ben Miller Ben Mosher Bernhard Sirlinger Bert Chang +Bharad Billy Moon binny B Krishna Chaitanya @@ -183,6 +186,7 @@ Gabriel Gheorghian Gabriel Horner Gabriel Nahmias galambalazs +Gary Sheng Gautam Mehta Gavin Douglas gekkoe @@ -199,6 +203,7 @@ Gordon Smith Grant Skinner greengiant Gregory Koberger +Grzegorz Mazur Guillaume Massé Guillaume Massé guraga @@ -230,6 +235,7 @@ Jakob Miland Jakub Vrana Jakub Vrána James Campos +James Howard James Thorne Jamie Hill Jan Jongboom @@ -257,6 +263,7 @@ Jim JobJob jochenberger Jochen Berger +joelpinheiro Johan Ask John Connor John Engler @@ -272,6 +279,7 @@ Jon Sangster Joost-Wim Boekesteijn Joseph Pecoraro Josh Cohen +Josh Soref Joshua Newman Josh Watzman jots @@ -298,6 +306,7 @@ Koh Zi Han, Cliff komakino Konstantin Lopuhin koops +Kris Ciccarello ks-ifware kubelsmieci KwanEsq @@ -315,6 +324,7 @@ LM lochel Lorenzo Stoakes Luciano Longo +Lu Fangjian Luke Stagner lynschinzer M1cha @@ -322,6 +332,7 @@ Madhura Jayaratne Maksim Lin Maksym Taran Malay Majithia +Manideep Manuel Rego Casasnovas Marat Dreizin Marcel Gerber @@ -476,6 +487,7 @@ Scott Aikin Scott Goodhew Sebastian Zaha Sergey Goder +Sergey Tselovalnikov Se-Won Kim shaund shaun gilchrist @@ -520,6 +532,7 @@ Thomas Schmid Tim Alby Tim Baumann Timothy Farrell +Timothy Gu Timothy Hatcher TobiasBg Tomas-A @@ -532,6 +545,7 @@ Triangle717 Tristan Tarrant TSUYUSATO Kitsune twifkak +VapidWorx Vestimir Markov vf Victor Bocharsky diff --git a/CHANGELOG.md b/CHANGELOG.md index b0a6ab57..d6e39cce 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -1,3 +1,33 @@ +## 5.14.0 (2016-04-20) + +### Bugfixes + +[`posFromIndex`](http://codemirror.net/doc/manual.html#posFromIndex) and [`indexFromPos`](http://codemirror.net/doc/manual.html#indexFromPos) now take [`lineSeparator`](http://codemirror.net/doc/manual.html#option_lineSeparator) into account. + +[vim bindings](http://codemirror.net/demo/vim.html): Only call `.save()` when it is actually available. + +[commend addon](http://codemirror.net/doc/manual.html#addon_comment): Be careful not to mangle multi-line strings. + +[Python mode](http://codemirror.net/mode/python/index.html): Improve distinguishing of decorators from `@` operators. + +[`findMarks`](http://codemirror.net/doc/manual.html#findMarks): No longer return marks that touch but don't overlap given range. + +### New features + +[vim bindings](http://codemirror.net/demo/vim.html): Add yank command. + +[match-highlighter addon](http://codemirror.net/doc/manual.html#addon_match-highlighter): Add `trim` option to disable ignoring of whitespace. + +[PowerShell mode](http://codemirror.net/mode/powershell/index.html): Added. + +[Yacas mode](http://codemirror.net/mode/yacas/index.html): Added. + +[Web IDL mode](http://codemirror.net/mode/webidl/index.html): Added. + +[SAS mode](http://codemirror.net/mode/sas/index.html): Added. + +[mbox mode](http://codemirror.net/mode/mbox/index.html): Added. + ## 5.13.2 (2016-03-23) ### Bugfixes diff --git a/doc/compress.html b/doc/compress.html index 4459f46e..1e507c6c 100644 --- a/doc/compress.html +++ b/doc/compress.html @@ -36,6 +36,7 @@

    Script compression helper

    Version:

    Version:

    Version: ' + - ''; - if (desc) { - raw += ''; - raw += desc; - raw += ''; - } + var raw = '' + + (prefix || "") + ''; + if (desc) + raw += ' ' + desc + ''; return raw; } var searchPromptDesc = '(Javascript regexp)'; From 85ef20aa66a408f65103ab86e12b91aad3200795 Mon Sep 17 00:00:00 2001 From: Camilo Roca Date: Fri, 29 Apr 2016 22:14:42 +0200 Subject: [PATCH 0275/1790] [clojure more] Split long string into several lines --- mode/clojure/clojure.js | 62 +++++++++++++++++++++++++++++++++++++++-- 1 file changed, 59 insertions(+), 3 deletions(-) diff --git a/mode/clojure/clojure.js b/mode/clojure/clojure.js index cd8129f7..b4844baf 100644 --- a/mode/clojure/clojure.js +++ b/mode/clojure/clojure.js @@ -31,14 +31,70 @@ CodeMirror.defineMode("clojure", function (options) { 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"); + "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 *default-data-reader-fn* as-> cond-> cond->> reduced reduced? send-via set-agent-send-executor! set-agent-send-off-executor! some-> some->>"); + "* *' *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 - "ns fn def defn defmethod bound-fn if if-not case condp when while when-not when-first do future comment doto locking proxy with-open with-precision reify deftype defrecord defprotocol extend extend-protocol extend-type try catch " + + "ns fn def defn defmethod bound-fn if if-not case condp when while when-not when-first do future comment doto " + + "locking proxy with-open with-precision reify deftype defrecord defprotocol extend extend-protocol extend-type " + + "try catch " + // Binding forms "let letfn binding loop for doseq dotimes when-let if-let " + From ea22650592d378d39a644997ef66b422478791b7 Mon Sep 17 00:00:00 2001 From: Camilo Roca Date: Fri, 29 Apr 2016 22:36:45 +0200 Subject: [PATCH 0276/1790] [clojure mode] Add functions names from Clojure v1.7 according to Clojure's changelog: https://github.com/clojure/clojure/blob/master/changes.md --- mode/clojure/clojure.js | 16 ++++++++-------- 1 file changed, 8 insertions(+), 8 deletions(-) diff --git a/mode/clojure/clojure.js b/mode/clojure/clojure.js index b4844baf..ed6af2c8 100644 --- a/mode/clojure/clojure.js +++ b/mode/clojure/clojure.js @@ -48,14 +48,14 @@ CodeMirror.defineMode("clojure", function (options) { "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 " + + "byte byte-array bytes case cat 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 " + + "clojure-version coll? comment commute comp comparator compare compare-and-set! compile complement completing 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 " + + "declare dedupe 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 " + + "drop-while eduction 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? " + @@ -71,7 +71,7 @@ CodeMirror.defineMode("clojure", function (options) { "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 " + + "quot rand rand-int rand-nth random-sample 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 " + @@ -79,13 +79,13 @@ CodeMirror.defineMode("clojure", function (options) { "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 " + + "symbol? sync take take-last take-nth take-while test the-ns thread-bound? time to-array to-array-2d trampoline transduce " + "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 " + + "unquote-splicing update update-in update-proxy use val vals var-get var-set var? vary-meta vec vector vector-of " + + "vector? volatile! volatile? vreset! vswap! 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->>"); From f9b042fc97873aa6a43d6a1ed76ea9961109766e Mon Sep 17 00:00:00 2001 From: Chris Smith Date: Sat, 30 Apr 2016 17:14:00 -0700 Subject: [PATCH 0277/1790] Update URL for CodeWorld in real world uses --- doc/realworld.html | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/doc/realworld.html b/doc/realworld.html index 4d8871d4..7b7eeeaf 100644 --- a/doc/realworld.html +++ b/doc/realworld.html @@ -39,7 +39,7 @@

    CodeMirror real-world uses

  • Cargo Collective (creative publishing platform)
  • Chrome DevTools
  • ClickHelp (technical writing tool)
  • -
  • CodeWorld (Haskell playground)
  • +
  • CodeWorld (Haskell playground)
  • Complete.ly playground
  • Codeanywhere (multi-platform cloud editor)
  • Code per Node (Drupal module)
  • From 329b9e6ec334f92cf615644d0602090cb3f79d38 Mon Sep 17 00:00:00 2001 From: Marijn Haverbeke Date: Mon, 2 May 2016 10:57:39 +0200 Subject: [PATCH 0278/1790] [clike mode] Provide a dontIndentStatement option Use it to make the C++ mode not indent template statements Closes #4001 --- mode/clike/clike.js | 39 ++++++++++++++++++--------------------- 1 file changed, 18 insertions(+), 21 deletions(-) diff --git a/mode/clike/clike.js b/mode/clike/clike.js index 695d5cef..cac358a5 100644 --- a/mode/clike/clike.js +++ b/mode/clike/clike.js @@ -11,21 +11,19 @@ })(function(CodeMirror) { "use strict"; -function Context(indented, column, type, align, prev) { +function Context(indented, column, type, info, align, prev) { this.indented = indented; this.column = column; this.type = type; + this.info = info; this.align = align; this.prev = prev; } -function isStatement(type) { - return type == "statement" || type == "switchstatement" || type == "namespace"; -} -function pushContext(state, col, type) { +function pushContext(state, col, type, info) { var indent = state.indented; - if (state.context && isStatement(state.context.type) && !isStatement(type)) + if (state.context && state.context.type != "statement" && type != "statement") indent = state.context.indented; - return state.context = new Context(indent, col, type, null, state.context); + return state.context = new Context(indent, col, type, info, null, state.context); } function popContext(state) { var t = state.context.type; @@ -42,7 +40,7 @@ function typeBefore(stream, state) { function isTopScope(context) { for (;;) { if (!context || context.type == "top") return true; - if (context.type == "}" && context.prev.type != "namespace") return false; + if (context.type == "}" && context.prev.info != "namespace") return false; context = context.prev; } } @@ -173,25 +171,20 @@ CodeMirror.defineMode("clike", function(config, parserConfig) { if (style == "comment" || style == "meta") return style; if (ctx.align == null) ctx.align = true; - if (endStatement.test(curPunc)) while (isStatement(state.context.type)) popContext(state); + if (endStatement.test(curPunc)) while (state.context.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 (isStatement(ctx.type)) ctx = popContext(state); + while (ctx.type == "statement") ctx = popContext(state); if (ctx.type == "}") ctx = popContext(state); - while (isStatement(ctx.type)) ctx = popContext(state); + while (ctx.type == "statement") ctx = popContext(state); } else if (curPunc == ctx.type) popContext(state); else if (indentStatements && (((ctx.type == "}" || ctx.type == "top") && curPunc != ";") || - (isStatement(ctx.type) && curPunc == "newstatement"))) { - var type = "statement"; - if (curPunc == "newstatement" && indentSwitch && stream.current() == "switch") - type = "switchstatement"; - else if (style == "keyword" && stream.current() == "namespace") - type = "namespace"; - pushContext(state, stream.column(), type); + (ctx.type == "statement" && curPunc == "newstatement"))) { + pushContext(state, stream.column(), "statement", stream.current()); } if (style == "variable" && @@ -215,18 +208,21 @@ CodeMirror.defineMode("clike", function(config, parserConfig) { indent: function(state, textAfter) { if (state.tokenize != tokenBase && state.tokenize != null) return CodeMirror.Pass; var ctx = state.context, firstChar = textAfter && textAfter.charAt(0); - if (isStatement(ctx.type) && firstChar == "}") ctx = ctx.prev; + if (ctx.type == "statement" && firstChar == "}") ctx = ctx.prev; + if (parserConfig.dontIndentStatements) + while (ctx.type == "statement" && parserConfig.dontIndentStatements.test(ctx.info)) + ctx = ctx.prev if (hooks.indent) { var hook = hooks.indent(state, ctx, textAfter); if (typeof hook == "number") return hook } var closing = firstChar == ctx.type; - var switchBlock = ctx.prev && ctx.prev.type == "switchstatement"; + var switchBlock = ctx.prev && ctx.prev.info == "switch"; if (parserConfig.allmanIndentation && /[{(]/.test(firstChar)) { while (ctx.type != "top" && ctx.type != "}") ctx = ctx.prev return ctx.indented } - if (isStatement(ctx.type)) + if (ctx.type == "statement") return ctx.indented + (firstChar == "{" ? 0 : statementIndentUnit); if (ctx.align && (!dontAlignCalls || ctx.type != ")")) return ctx.column + (closing ? 0 : 1); @@ -386,6 +382,7 @@ CodeMirror.defineMode("clike", function(config, parserConfig) { defKeywords: words("class namespace struct enum union"), typeFirstDefinitions: true, atoms: words("true false null"), + dontIndentStatements: /^template$/, hooks: { "#": cppHook, "*": pointerHook, From 6ddab774c022754022106330ba966a74faae7ced Mon Sep 17 00:00:00 2001 From: Marijn Haverbeke Date: Mon, 2 May 2016 11:05:25 +0200 Subject: [PATCH 0279/1790] [clike mode] Fix broken indentation caused by refactor in 329b9e6 Issue #4001 --- 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 cac358a5..17c7f2ac 100644 --- a/mode/clike/clike.js +++ b/mode/clike/clike.js @@ -171,7 +171,7 @@ CodeMirror.defineMode("clike", function(config, parserConfig) { if (style == "comment" || style == "meta") return style; if (ctx.align == null) ctx.align = true; - if (endStatement.test(curPunc)) while (state.context.type != "statement") popContext(state); + if (endStatement.test(curPunc)) while (state.context.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(), ")"); From b9e0c4cd0bab86a440cb10e3505bd6691bb0f001 Mon Sep 17 00:00:00 2001 From: Marijn Haverbeke Date: Mon, 2 May 2016 11:42:50 +0200 Subject: [PATCH 0280/1790] Fix dropping of selection in contenteditable input style Issue #3979 --- lib/codemirror.js | 12 ++++++------ 1 file changed, 6 insertions(+), 6 deletions(-) diff --git a/lib/codemirror.js b/lib/codemirror.js index 238aa438..3d78108f 100644 --- a/lib/codemirror.js +++ b/lib/codemirror.js @@ -1663,9 +1663,9 @@ return result; }, - showSelection: function(info) { + showSelection: function(info, takeFocus) { if (!info || !this.cm.display.view.length) return; - if (info.focus) this.showPrimarySelection(); + if (info.focus || takeFocus) this.showPrimarySelection(); this.showMultipleSelections(info); }, @@ -3101,7 +3101,7 @@ } if (op.updatedDisplay || op.selectionChanged) - op.preparedSelection = display.input.prepareSelection(); + op.preparedSelection = display.input.prepareSelection(op.focus); } function endOperation_W2(op) { @@ -3114,8 +3114,9 @@ cm.display.maxLineChanged = false; } + var takeFocus = op.focus && op.focus == activeElt() && (!document.hasFocus || document.hasFocus()) if (op.preparedSelection) - cm.display.input.showSelection(op.preparedSelection); + cm.display.input.showSelection(op.preparedSelection, takeFocus); if (op.updatedDisplay || op.startHeight != cm.doc.height) updateScrollbars(cm, op.barMeasure); if (op.updatedDisplay) @@ -3125,8 +3126,7 @@ if (cm.state.focused && op.updateInput) cm.display.input.reset(op.typing); - if (op.focus && op.focus == activeElt() && (!document.hasFocus || document.hasFocus())) - ensureFocus(op.cm); + if (takeFocus) ensureFocus(op.cm); } function endOperation_finish(op) { From 9ac5d204e359950c40bd655baeb18dfd97417c6f Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Pavel=20Petr=C5=BEela?= Date: Wed, 20 Apr 2016 14:29:16 +0200 Subject: [PATCH 0281/1790] [sublime keymap] Auto-indent after insertLine --- keymap/sublime.js | 1 + 1 file changed, 1 insertion(+) diff --git a/keymap/sublime.js b/keymap/sublime.js index 3cf67413..d9b64ba9 100644 --- a/keymap/sublime.js +++ b/keymap/sublime.js @@ -124,6 +124,7 @@ } cm.setSelections(newSelection); }); + cm.execCommand("indentAuto"); } cmds[map[ctrl + "Enter"] = "insertLineAfter"] = function(cm) { return insertLine(cm, false); }; From bef43ea825f5fdc865ce1942a046cd0a1fc63aeb Mon Sep 17 00:00:00 2001 From: ngn Date: Mon, 25 Apr 2016 14:14:47 +0100 Subject: [PATCH 0282/1790] Include all ASCII control codes in specialChars * \x1a-\x1f were missing though it looks like the intention was to cover all below \x20 (space) * added \x7f which is usually considered non-printable * removed \t which was already covered in \x00-\x19 as \x09 --- lib/codemirror.js | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/lib/codemirror.js b/lib/codemirror.js index 3d78108f..3c154b95 100644 --- a/lib/codemirror.js +++ b/lib/codemirror.js @@ -5392,7 +5392,7 @@ for (var i = newBreaks.length - 1; i >= 0; i--) replaceRange(cm.doc, val, newBreaks[i], Pos(newBreaks[i].line, newBreaks[i].ch + val.length)) }); - option("specialChars", /[\t\u0000-\u0019\u00ad\u200b-\u200f\u2028\u2029\ufeff]/g, function(cm, val, old) { + option("specialChars", /[\u0000-\u001f\u007f\u00ad\u200b-\u200f\u2028\u2029\ufeff]/g, function(cm, val, old) { cm.state.specialChars = new RegExp(val.source + (val.test("\t") ? "" : "|\t"), "g"); if (old != CodeMirror.Init) cm.refresh(); }); From 5b84f3d0a9d37bc837d5f2f914c2c75b4b6776e5 Mon Sep 17 00:00:00 2001 From: Marijn Haverbeke Date: Mon, 2 May 2016 11:55:51 +0200 Subject: [PATCH 0283/1790] Update manual to reflect current default of specialChars --- doc/manual.html | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/doc/manual.html b/doc/manual.html index 4e5f5ced..8c99a99e 100644 --- a/doc/manual.html +++ b/doc/manual.html @@ -285,7 +285,7 @@

    Configuration

    should be replaced by a special placeholder. Mostly useful for non-printing special characters. The default - is /[\u0000-\u0019\u00ad\u200b-\u200f\u2028\u2029\ufeff]/.
    + is /[\u0000-\u001f\u007f\u00ad\u200b-\u200f\u2028\u2029\ufeff]/.
    specialCharPlaceholder: function(char) → Element
    A function that, given a special character identified by the specialChars From b38eb3f49c0582f7dcc8b5bf4a35ab2404616318 Mon Sep 17 00:00:00 2001 From: Marijn Haverbeke Date: Mon, 2 May 2016 12:00:46 +0200 Subject: [PATCH 0284/1790] Use CodeMirror.startState helper when getting state for inner modes Closes #3994 --- mode/ebnf/ebnf.js | 2 +- mode/haml/haml.js | 4 ++-- mode/htmlmixed/htmlmixed.js | 2 +- mode/jade/jade.js | 6 +++--- mode/markdown/markdown.js | 2 +- mode/pegjs/pegjs.js | 2 +- mode/slim/slim.js | 8 ++++---- 7 files changed, 13 insertions(+), 13 deletions(-) diff --git a/mode/ebnf/ebnf.js b/mode/ebnf/ebnf.js index 6b51aba0..9618f8e4 100644 --- a/mode/ebnf/ebnf.js +++ b/mode/ebnf/ebnf.js @@ -94,7 +94,7 @@ if (bracesMode !== null && (state.braced || peek === "{")) { if (state.localState === null) - state.localState = bracesMode.startState(); + state.localState = CodeMirror.startState(bracesMode); var token = bracesMode.token(stream, state.localState), text = stream.current(); diff --git a/mode/haml/haml.js b/mode/haml/haml.js index 86def73e..20ae1e19 100644 --- a/mode/haml/haml.js +++ b/mode/haml/haml.js @@ -98,8 +98,8 @@ return { // default to html mode startState: function() { - var htmlState = htmlMode.startState(); - var rubyState = rubyMode.startState(); + var htmlState = CodeMirror.startState(htmlMode); + var rubyState = CodeMirror.startState(rubyMode); return { htmlState: htmlState, rubyState: rubyState, diff --git a/mode/htmlmixed/htmlmixed.js b/mode/htmlmixed/htmlmixed.js index 6574fbd5..d74083ee 100644 --- a/mode/htmlmixed/htmlmixed.js +++ b/mode/htmlmixed/htmlmixed.js @@ -115,7 +115,7 @@ return { startState: function () { - var state = htmlMode.startState(); + var state = CodeMirror.startState(htmlMode); return {token: html, inTag: null, localMode: null, localState: null, htmlState: state}; }, diff --git a/mode/jade/jade.js b/mode/jade/jade.js index 1db069a9..51ed105a 100644 --- a/mode/jade/jade.js +++ b/mode/jade/jade.js @@ -36,7 +36,7 @@ CodeMirror.defineMode('jade', function (config) { this.isInterpolating = false; this.interpolationNesting = 0; - this.jsState = jsMode.startState(); + this.jsState = CodeMirror.startState(jsMode); this.restOfLine = ''; @@ -386,7 +386,7 @@ CodeMirror.defineMode('jade', function (config) { if (state.inAttributeName && stream.match(/^[^=,\)!]+/)) { if (stream.peek() === '=' || stream.peek() === '!') { state.inAttributeName = false; - state.jsState = jsMode.startState(); + state.jsState = CodeMirror.startState(jsMode); if (state.lastTag === 'script' && stream.current().trim().toLowerCase() === 'type') { state.attributeIsType = true; } else { @@ -492,7 +492,7 @@ CodeMirror.defineMode('jade', function (config) { if (stream.indentation() > state.indentOf || (state.innerModeForLine && !stream.sol()) || force) { if (state.innerMode) { if (!state.innerState) { - state.innerState = state.innerMode.startState ? state.innerMode.startState(stream.indentation()) : {}; + state.innerState = state.innerMode.startState ? CodeMirror.startState(state.innerMode, stream.indentation()) : {}; } return stream.hideFirstChars(state.indentOf + 2, function () { return state.innerMode.token(stream, state.innerState) || true; diff --git a/mode/markdown/markdown.js b/mode/markdown/markdown.js index 8995fd5f..fd8f647f 100644 --- a/mode/markdown/markdown.js +++ b/mode/markdown/markdown.js @@ -218,7 +218,7 @@ CodeMirror.defineMode("markdown", function(cmCfg, modeCfg) { state.fencedChars = match[1] // try switching mode state.localMode = getMode(match[2]); - if (state.localMode) state.localState = state.localMode.startState(); + if (state.localMode) state.localState = CodeMirror.startState(state.localMode); state.f = state.block = local; if (modeCfg.highlightFormatting) state.formatting = "code-block"; state.code = -1 diff --git a/mode/pegjs/pegjs.js b/mode/pegjs/pegjs.js index 8e87b59e..6c720746 100644 --- a/mode/pegjs/pegjs.js +++ b/mode/pegjs/pegjs.js @@ -81,7 +81,7 @@ CodeMirror.defineMode("pegjs", function (config) { return "comment"; } else if (state.braced || stream.peek() === '{') { if (state.localState === null) { - state.localState = jsMode.startState(); + state.localState = CodeMirror.startState(jsMode); } var token = jsMode.token(stream, state.localState); var text = stream.current(); diff --git a/mode/slim/slim.js b/mode/slim/slim.js index 164464d0..991a97ef 100644 --- a/mode/slim/slim.js +++ b/mode/slim/slim.js @@ -165,7 +165,7 @@ }; return function(stream, state) { rubyState = state.rubyState; - state.rubyState = rubyMode.startState(); + state.rubyState = CodeMirror.startState(rubyMode); state.tokenize = runSplat; return ruby(stream, state); }; @@ -317,7 +317,7 @@ function startSubMode(mode, state) { var subMode = getMode(mode); - var subState = subMode.startState && subMode.startState(); + var subState = CodeMirror.startState(subMode); state.subMode = subMode; state.subState = subState; @@ -507,8 +507,8 @@ var mode = { // default to html mode startState: function() { - var htmlState = htmlMode.startState(); - var rubyState = rubyMode.startState(); + var htmlState = CodeMirror.startState(htmlMode); + var rubyState = CodeMirror.startState(rubyMode); return { htmlState: htmlState, rubyState: rubyState, From 0587510a41598fbcb14ce22782e8d4ec0fbc8f9f Mon Sep 17 00:00:00 2001 From: Marijn Haverbeke Date: Mon, 2 May 2016 12:16:50 +0200 Subject: [PATCH 0285/1790] Fix bug in check for overlapping collapsed ranges Closes #3998 --- lib/codemirror.js | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/lib/codemirror.js b/lib/codemirror.js index 3c154b95..f00f2da9 100644 --- a/lib/codemirror.js +++ b/lib/codemirror.js @@ -6560,8 +6560,8 @@ var fromCmp = cmp(found.from, from) || extraLeft(sp.marker) - extraLeft(marker); var toCmp = cmp(found.to, to) || extraRight(sp.marker) - extraRight(marker); if (fromCmp >= 0 && toCmp <= 0 || fromCmp <= 0 && toCmp >= 0) continue; - if (fromCmp <= 0 && (cmp(found.to, from) > 0 || (sp.marker.inclusiveRight && marker.inclusiveLeft)) || - fromCmp >= 0 && (cmp(found.from, to) < 0 || (sp.marker.inclusiveLeft && marker.inclusiveRight))) + if (fromCmp <= 0 && (sp.marker.inclusiveRight && marker.inclusiveLeft ? cmp(found.to, from) >= 0 : cmp(found.to, from) > 0) || + fromCmp >= 0 && (sp.marker.inclusiveRight && marker.inclusiveLeft ? cmp(found.from, to) <= 0 : cmp(found.from, to) < 0)) return true; } } From 848c524d450abb44895277e3865c4832f3a117c6 Mon Sep 17 00:00:00 2001 From: Marijn Haverbeke Date: Thu, 5 May 2016 21:05:51 +0200 Subject: [PATCH 0286/1790] [markdown mode] Better heuristic for finding the end of a link URL Closes #4013 --- mode/markdown/markdown.js | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/mode/markdown/markdown.js b/mode/markdown/markdown.js index fd8f647f..2e2b5fe4 100644 --- a/mode/markdown/markdown.js +++ b/mode/markdown/markdown.js @@ -608,7 +608,7 @@ CodeMirror.defineMode("markdown", function(cmCfg, modeCfg) { return function(stream, state) { var ch = stream.next(); - if (ch === endChar) { + if (ch === endChar && stream.match(/^([.,\s;:]|[^\]\)]*$)/, false)) { state.f = state.inline = inlineNormal; if (modeCfg.highlightFormatting) state.formatting = "link-string"; var returnState = getType(state); From 575d1aa98a081ebfb65efd2039f0c48afbefacc8 Mon Sep 17 00:00:00 2001 From: Marijn Haverbeke Date: Thu, 5 May 2016 21:32:01 +0200 Subject: [PATCH 0287/1790] [markdown mode] Make link parsing conform to CommonMark spec Issue #4013 --- mode/markdown/markdown.js | 26 ++++++++------------------ mode/markdown/test.js | 3 +++ 2 files changed, 11 insertions(+), 18 deletions(-) diff --git a/mode/markdown/markdown.js b/mode/markdown/markdown.js index 2e2b5fe4..37329c23 100644 --- a/mode/markdown/markdown.js +++ b/mode/markdown/markdown.js @@ -596,7 +596,7 @@ CodeMirror.defineMode("markdown", function(cmCfg, modeCfg) { } var ch = stream.next(); if (ch === '(' || ch === '[') { - state.f = state.inline = getLinkHrefInside(ch === "(" ? ")" : "]"); + state.f = state.inline = getLinkHrefInside(ch === "(" ? ")" : "]", 0); if (modeCfg.highlightFormatting) state.formatting = "link-string"; state.linkHref = true; return getType(state); @@ -604,11 +604,16 @@ CodeMirror.defineMode("markdown", function(cmCfg, modeCfg) { return 'error'; } + var linkRE = { + ")": /^(?:[^\\\(\)]|\\.|\((?:[^\\\(\)]|\\.)*\))*?(?=\))/, + "]": /^(?:[^\\\[\]]|\\.|\[(?:[^\\\[\\]]|\\.)*\])*?(?=\])/ + } + function getLinkHrefInside(endChar) { return function(stream, state) { var ch = stream.next(); - if (ch === endChar && stream.match(/^([.,\s;:]|[^\]\)]*$)/, false)) { + if (ch === endChar) { state.f = state.inline = inlineNormal; if (modeCfg.highlightFormatting) state.formatting = "link-string"; var returnState = getType(state); @@ -616,10 +621,7 @@ CodeMirror.defineMode("markdown", function(cmCfg, modeCfg) { return returnState; } - if (stream.match(inlineRE(endChar), true)) { - stream.backUp(1); - } - + stream.match(linkRE[endChar]) state.linkHref = true; return getType(state); }; @@ -667,18 +669,6 @@ CodeMirror.defineMode("markdown", function(cmCfg, modeCfg) { return tokenTypes.linkHref + " url"; } - var savedInlineRE = []; - function inlineRE(endChar) { - 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 - // endChar. - savedInlineRE[endChar] = new RegExp('^(?:[^\\\\]|\\\\.)*?(' + endChar + ')'); - } - return savedInlineRE[endChar]; - } - var mode = { startState: function() { return { diff --git a/mode/markdown/test.js b/mode/markdown/test.js index e2b3a815..e76eae90 100644 --- a/mode/markdown/test.js +++ b/mode/markdown/test.js @@ -782,6 +782,9 @@ MT("emStrongMixed", "[em *foo][em&strong __bar_hello** world]"); + MT("linkWithNestedParens", + "[link [[foo]]][string&url (bar(baz))]") + // These characters should be escaped: // \ backslash // ` backtick From f3027c4f810fd5870e3626de008bf85dc0dc8edf Mon Sep 17 00:00:00 2001 From: Marijn Haverbeke Date: Thu, 5 May 2016 21:33:55 +0200 Subject: [PATCH 0288/1790] [php mode] Add some builtins Closes #4009 --- 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 22494467..57ba812d 100644 --- a/mode/php/php.js +++ b/mode/php/php.js @@ -86,7 +86,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 file_put_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"; + 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 file_put_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 array_intersect_key array_combine array_column 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 14dae96542c63c155adf6e7dfce79df1b0a43218 Mon Sep 17 00:00:00 2001 From: Marijn Haverbeke Date: Thu, 5 May 2016 21:58:59 +0200 Subject: [PATCH 0289/1790] [clike mode] Look across lines to recognize defined names after types Closes #4012 --- mode/clike/clike.js | 17 ++++++++++++----- mode/clike/test.js | 4 ++++ 2 files changed, 16 insertions(+), 5 deletions(-) diff --git a/mode/clike/clike.js b/mode/clike/clike.js index 17c7f2ac..8f6757c5 100644 --- a/mode/clike/clike.js +++ b/mode/clike/clike.js @@ -32,9 +32,10 @@ function popContext(state) { return state.context = state.context.prev; } -function typeBefore(stream, state) { +function typeBefore(stream, state, pos) { if (state.prevToken == "variable" || state.prevToken == "variable-3") return true; - if (/\S(?:[^- ]>|[*\]])\s*$|\*$/.test(stream.string.slice(0, stream.start))) return true; + if (/\S(?:[^- ]>|[*\]])\s*$|\*$/.test(stream.string.slice(0, pos))) return true; + if (state.typeAtEndOfLine && stream.column() == stream.indentation()) return true; } function isTopScope(context) { @@ -145,6 +146,11 @@ CodeMirror.defineMode("clike", function(config, parserConfig) { return "comment"; } + function maybeEOL(stream, state) { + if (parserConfig.typeFirstDefinitions && stream.eol() && isTopScope(state.context)) + state.typeAtEndOfLine = typeBefore(stream, state, stream.pos) + } + // Interface return { @@ -165,7 +171,7 @@ CodeMirror.defineMode("clike", function(config, parserConfig) { state.indented = stream.indentation(); state.startOfLine = true; } - if (stream.eatSpace()) return null; + if (stream.eatSpace()) { maybeEOL(stream, state); return null; } curPunc = isDefKeyword = null; var style = (state.tokenize || tokenBase)(stream, state); if (style == "comment" || style == "meta") return style; @@ -189,7 +195,7 @@ CodeMirror.defineMode("clike", function(config, parserConfig) { if (style == "variable" && ((state.prevToken == "def" || - (parserConfig.typeFirstDefinitions && typeBefore(stream, state) && + (parserConfig.typeFirstDefinitions && typeBefore(stream, state, stream.start) && isTopScope(state.context) && stream.match(/^\s*\(/, false))))) style = "def"; @@ -202,11 +208,12 @@ CodeMirror.defineMode("clike", function(config, parserConfig) { state.startOfLine = false; state.prevToken = isDefKeyword ? "def" : style || curPunc; + maybeEOL(stream, state); return style; }, indent: function(state, textAfter) { - if (state.tokenize != tokenBase && state.tokenize != null) return CodeMirror.Pass; + if (state.tokenize != tokenBase && state.tokenize != null || state.typeAtEndOfLine) return CodeMirror.Pass; var ctx = state.context, firstChar = textAfter && textAfter.charAt(0); if (ctx.type == "statement" && firstChar == "}") ctx = ctx.prev; if (parserConfig.dontIndentStatements) diff --git a/mode/clike/test.js b/mode/clike/test.js index c2600326..bea85b86 100644 --- a/mode/clike/test.js +++ b/mode/clike/test.js @@ -25,6 +25,10 @@ "[keyword struct] [def bar]{}", "[variable-3 int] [variable-3 *][def baz]() {}"); + MT("def_new_line", + "::[variable std]::[variable SomeTerribleType][operator <][variable T][operator >]", + "[def SomeLongMethodNameThatDoesntFitIntoOneLine]([keyword const] [variable MyType][operator &] [variable param]) {}") + MT("double_block", "[keyword for] (;;)", " [keyword for] (;;)", From 90d8250048153cc33b2e72d50a000670dbbbc33b Mon Sep 17 00:00:00 2001 From: TDaglis Date: Mon, 9 May 2016 18:03:59 +0300 Subject: [PATCH 0290/1790] [fortran mode] Fix typo in demo page --- mode/fortran/index.html | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/mode/fortran/index.html b/mode/fortran/index.html index 102e8f82..9aed0efc 100644 --- a/mode/fortran/index.html +++ b/mode/fortran/index.html @@ -77,5 +77,5 @@

    Fortran mode

    }); -

    MIME types defined: text/x-Fortran.

    +

    MIME types defined: text/x-fortran.

    From 948235002deeb6a7a50ed98f4c19e7b18e1136ae Mon Sep 17 00:00:00 2001 From: Marijn Haverbeke Date: Wed, 11 May 2016 12:09:14 +0200 Subject: [PATCH 0291/1790] Clean up Array.join kludge --- lib/codemirror.js | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/lib/codemirror.js b/lib/codemirror.js index f00f2da9..33928bc8 100644 --- a/lib/codemirror.js +++ b/lib/codemirror.js @@ -5721,7 +5721,7 @@ for (var i = 0; i < ranges.length; i++) { var pos = ranges[i].from(); var col = countColumn(cm.getLine(pos.line), pos.ch, tabSize); - spaces.push(new Array(tabSize - col % tabSize + 1).join(" ")); + spaces.push(spaceStr(tabSize - col % tabSize)); } cm.replaceSelections(spaces); }, From 7dd2cfd7ceb131f711cccf965bef086b238264e2 Mon Sep 17 00:00:00 2001 From: Marijn Haverbeke Date: Thu, 12 May 2016 09:40:02 +0200 Subject: [PATCH 0292/1790] [clike mode] Fix bug in context copying Closes #4020 --- 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 8f6757c5..aadab9e1 100644 --- a/mode/clike/clike.js +++ b/mode/clike/clike.js @@ -157,7 +157,7 @@ CodeMirror.defineMode("clike", function(config, parserConfig) { startState: function(basecolumn) { return { tokenize: null, - context: new Context((basecolumn || 0) - indentUnit, 0, "top", false), + context: new Context((basecolumn || 0) - indentUnit, 0, "top", null, false), indented: 0, startOfLine: true, prevToken: null @@ -535,7 +535,7 @@ CodeMirror.defineMode("clike", function(config, parserConfig) { "=": function(stream, state) { var cx = state.context if (cx.type == "}" && cx.align && stream.eat(">")) { - state.context = new Context(cx.indented, cx.column, cx.type, null, cx.prev) + state.context = new Context(cx.indented, cx.column, cx.type, cx.info, null, cx.prev) return "operator" } else { return false From 5cf02c9187a8b951664d54cc531ce9b67f7226c6 Mon Sep 17 00:00:00 2001 From: Jared Jacobs Date: Wed, 11 May 2016 17:44:28 -0700 Subject: [PATCH 0293/1790] Optimizing BranchChunk.insertInner for large files --- lib/codemirror.js | 63 ++++++++++++++++++++++++++++------------------- 1 file changed, 38 insertions(+), 25 deletions(-) diff --git a/lib/codemirror.js b/lib/codemirror.js index 33928bc8..2fe344e0 100644 --- a/lib/codemirror.js +++ b/lib/codemirror.js @@ -7315,13 +7315,20 @@ var child = this.children[i], sz = child.chunkSize(); if (at <= sz) { 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); - var newleaf = new LeafChunk(spilled); - child.height -= newleaf.height; - this.children.splice(i + 1, 0, newleaf); - newleaf.parent = this; + var childLines = child.lines; + var childLinesLen = childLines && childLines.length; + if (childLinesLen > 50) { + // To avoid memory thrashing when childLines is huge (e.g. first view of a large file), it's never spliced. + // Instead, small slices are taken. They're taken in order because sequential memory accesses are fastest. + var numNewLeafs = Math.ceil((childLinesLen - 50) / 25); + var newChildLinesLen = childLinesLen - numNewLeafs * 25; + child.lines = childLines.slice(0, newChildLinesLen); + for (var j = 0; j < numNewLeafs; j++) { + var leafStart = newChildLinesLen + j * 25; + var leaf = new LeafChunk(childLines.slice(leafStart, leafStart + 25)); + child.height -= leaf.height; + this.children.push(leaf); + leaf.parent = this; } this.maybeSpill(); } @@ -7332,25 +7339,31 @@ }, // When a node has grown, check whether it should be split. maybeSpill: function() { - if (this.children.length <= 10) return; var me = this; - do { - var spilled = me.children.splice(me.children.length - 5, 5); - var sibling = new BranchChunk(spilled); - if (!me.parent) { // Become the parent node - var copy = new BranchChunk(me.children); - copy.parent = me; - me.children = [copy, sibling]; - me = copy; - } else { - me.size -= sibling.size; - me.height -= sibling.height; - var myIndex = indexOf(me.parent.children, me); - me.parent.children.splice(myIndex + 1, 0, sibling); - } - sibling.parent = me.parent; - } while (me.children.length > 10); - me.parent.maybeSpill(); + var children = me.children; + var numChildren = children.length; + if (numChildren <= 10) return; + // To avoid memory thrashing when the children array is huge (e.g. first view of a large file), it's never spliced. + // Instead, small slices are taken. They're taken in order because sequential memory accesses are fastest. + var parent = me.parent || me; + var numMoreBranches = Math.ceil((numChildren - 10) / 5); + var firstBranchNumChildren = numChildren - numMoreBranches * 5; + var firstBranch = new BranchChunk(children.slice(0, firstBranchNumChildren)); + var branches = [firstBranch]; + firstBranch.parent = parent; + for (var i = 0; i < numMoreBranches; i++) { + var branchStart = firstBranchNumChildren + i * 5; + var branch = new BranchChunk(children.slice(branchStart, branchStart + 5)); + branches.push(branch); + branch.parent = parent; + } + if (parent === me) { + parent.children = branches; + } else { + var myIndex = indexOf(parent.children, me); + parent.children.splice(myIndex, 1, branches); + } + parent.maybeSpill(); }, iterN: function(at, n, op) { for (var i = 0; i < this.children.length; ++i) { From 9ad383797c28dab90ef9182083c96ccd8a96bb58 Mon Sep 17 00:00:00 2001 From: Marijn Haverbeke Date: Thu, 12 May 2016 10:11:30 +0200 Subject: [PATCH 0294/1790] Update code in 5cf02c9187 Issue #4022 --- lib/codemirror.js | 18 +++++++----------- 1 file changed, 7 insertions(+), 11 deletions(-) diff --git a/lib/codemirror.js b/lib/codemirror.js index 2fe344e0..37a0fd92 100644 --- a/lib/codemirror.js +++ b/lib/codemirror.js @@ -7315,21 +7315,17 @@ var child = this.children[i], sz = child.chunkSize(); if (at <= sz) { child.insertInner(at, lines, height); - var childLines = child.lines; - var childLinesLen = childLines && childLines.length; - if (childLinesLen > 50) { - // To avoid memory thrashing when childLines is huge (e.g. first view of a large file), it's never spliced. + if (child.lines && child.lines.length > 50) { + // To avoid memory thrashing when child.lines is huge (e.g. first view of a large file), it's never spliced. // Instead, small slices are taken. They're taken in order because sequential memory accesses are fastest. - var numNewLeafs = Math.ceil((childLinesLen - 50) / 25); - var newChildLinesLen = childLinesLen - numNewLeafs * 25; - child.lines = childLines.slice(0, newChildLinesLen); - for (var j = 0; j < numNewLeafs; j++) { - var leafStart = newChildLinesLen + j * 25; - var leaf = new LeafChunk(childLines.slice(leafStart, leafStart + 25)); + var remaining = child.lines.length % 25 + 25 + for (var pos = remaining; pos < child.lines.length;) { + var leaf = new LeafChunk(child.lines.slice(pos, pos += 25)); child.height -= leaf.height; - this.children.push(leaf); + this.children.splice(++i, 0, leaf); leaf.parent = this; } + child.lines = child.lines.slice(0, remaining); this.maybeSpill(); } break; From 1943fc428a26ad298d4e18eb049b268add4d0347 Mon Sep 17 00:00:00 2001 From: Marijn Haverbeke Date: Thu, 12 May 2016 10:53:29 +0200 Subject: [PATCH 0295/1790] Make workaround for Webkit whitespace: pre bug more robust Issue #2901 --- lib/codemirror.js | 7 +++++-- 1 file changed, 5 insertions(+), 2 deletions(-) diff --git a/lib/codemirror.js b/lib/codemirror.js index 37a0fd92..fe51bd3b 100644 --- a/lib/codemirror.js +++ b/lib/codemirror.js @@ -6963,8 +6963,11 @@ } // See issue #2901 - if (webkit && /\bcm-tab\b/.test(builder.content.lastChild.className)) - builder.content.className = "cm-tab-wrap-hack"; + if (webkit) { + var last = builder.content.lastChild + if (/\bcm-tab\b/.test(last.className) || (last.querySelector && last.querySelector(".cm-tab"))) + builder.content.className = "cm-tab-wrap-hack"; + } signal(cm, "renderLine", cm, lineView.line, builder.pre); if (builder.pre.className) From 0ca8205af4a691683ce336949780e8d42e05cd52 Mon Sep 17 00:00:00 2001 From: Marijn Haverbeke Date: Sun, 15 May 2016 21:56:30 +0200 Subject: [PATCH 0296/1790] [lint addon] Set precise colors for tooltips Closes #4025 --- addon/lint/lint.css | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/addon/lint/lint.css b/addon/lint/lint.css index 414a9a0e..f097cfe3 100644 --- a/addon/lint/lint.css +++ b/addon/lint/lint.css @@ -4,10 +4,10 @@ } .CodeMirror-lint-tooltip { - background-color: infobackground; + background-color: #ffd; border: 1px solid black; border-radius: 4px 4px 4px 4px; - color: infotext; + color: black; font-family: monospace; font-size: 10pt; overflow: hidden; From cdabb671e357cf7d52f16abc0df2f73e01cec816 Mon Sep 17 00:00:00 2001 From: Weiyan Shao Date: Tue, 17 May 2016 11:19:30 +0200 Subject: [PATCH 0297/1790] [htmlembedded mode] Update documentation 1. Mention multiplex as a dependency 2. Add ERB as a MIME-type --- mode/htmlembedded/index.html | 7 ++++--- 1 file changed, 4 insertions(+), 3 deletions(-) diff --git a/mode/htmlembedded/index.html b/mode/htmlembedded/index.html index f27582ef..9ed33cff 100644 --- a/mode/htmlembedded/index.html +++ b/mode/htmlembedded/index.html @@ -51,9 +51,10 @@

    Html Embedded Scripts mode

    }); -

    Mode for html embedded scripts like JSP and ASP.NET. Depends on HtmlMixed which in turn depends on +

    Mode for html embedded scripts like JSP and ASP.NET. Depends on multiplex and HtmlMixed which in turn depends on JavaScript, CSS and XML.
    Other dependencies include those of the scripting language chosen.

    -

    MIME types defined: application/x-aspx (ASP.NET), - application/x-ejs (Embedded Javascript), application/x-jsp (JavaServer Pages)

    +

    MIME types defined: application/x-aspx (ASP.NET), + application/x-ejs (Embedded Javascript), application/x-jsp (JavaServer Pages) + and application/x-erb

    From 601cb37f77aea0e512cc2546d00814eedfb2d011 Mon Sep 17 00:00:00 2001 From: Manuel Rego Casasnovas Date: Mon, 2 May 2016 15:11:32 +0200 Subject: [PATCH 0298/1790] [css mode] Update Grid Layout properties and values Add gutters properties (grid-gap, grid-column-gap and grid-row-gap): https://www.w3.org/TR/css-grid-1/#gutters Take advantage to add some missing keywors: * grid and inline-grid: https://www.w3.org/TR/css-grid-1/#grid-containers * dense: https://www.w3.org/TR/css-grid-1/#grid-auto-flow-property It's been a while since the list of properties has been updated: https://drafts.csswg.org/css-grid/#property-index --- mode/css/css.js | 12 ++++++------ 1 file changed, 6 insertions(+), 6 deletions(-) diff --git a/mode/css/css.js b/mode/css/css.js index e9656e3d..ea7bd01d 100644 --- a/mode/css/css.js +++ b/mode/css/css.js @@ -484,9 +484,9 @@ CodeMirror.defineMode("css", function(config, parserConfig) { "font-variant-alternates", "font-variant-caps", "font-variant-east-asian", "font-variant-ligatures", "font-variant-numeric", "font-variant-position", "font-weight", "grid", "grid-area", "grid-auto-columns", "grid-auto-flow", - "grid-auto-position", "grid-auto-rows", "grid-column", "grid-column-end", - "grid-column-start", "grid-row", "grid-row-end", "grid-row-start", - "grid-template", "grid-template-areas", "grid-template-columns", + "grid-auto-rows", "grid-column", "grid-column-end", "grid-column-gap", + "grid-column-start", "grid-gap", "grid-row", "grid-row-end", "grid-row-gap", + "grid-row-start", "grid-template", "grid-template-areas", "grid-template-columns", "grid-template-rows", "hanging-punctuation", "height", "hyphens", "icon", "image-orientation", "image-rendering", "image-resolution", "inline-box-align", "justify-content", "left", "letter-spacing", @@ -601,7 +601,7 @@ CodeMirror.defineMode("css", function(config, parserConfig) { "compact", "condensed", "contain", "content", "content-box", "context-menu", "continuous", "copy", "counter", "counters", "cover", "crop", "cross", "crosshair", "currentcolor", "cursive", "cyclic", "darken", "dashed", "decimal", - "decimal-leading-zero", "default", "default-button", "destination-atop", + "decimal-leading-zero", "default", "default-button", "dense", "destination-atop", "destination-in", "destination-out", "destination-over", "devanagari", "difference", "disc", "discard", "disclosure-closed", "disclosure-open", "document", "dot-dash", "dot-dot-dash", @@ -615,13 +615,13 @@ CodeMirror.defineMode("css", function(config, parserConfig) { "ethiopic-halehame-ti-er", "ethiopic-halehame-ti-et", "ethiopic-halehame-tig", "ethiopic-numeric", "ew-resize", "exclusion", "expanded", "extends", "extra-condensed", "extra-expanded", "fantasy", "fast", "fill", "fixed", "flat", "flex", "flex-end", "flex-start", "footnotes", - "forwards", "from", "geometricPrecision", "georgian", "graytext", "groove", + "forwards", "from", "geometricPrecision", "georgian", "graytext", "grid", "groove", "gujarati", "gurmukhi", "hand", "hangul", "hangul-consonant", "hard-light", "hebrew", "help", "hidden", "hide", "higher", "highlight", "highlighttext", "hiragana", "hiragana-iroha", "horizontal", "hsl", "hsla", "hue", "icon", "ignore", "inactiveborder", "inactivecaption", "inactivecaptiontext", "infinite", "infobackground", "infotext", "inherit", "initial", "inline", "inline-axis", - "inline-block", "inline-flex", "inline-table", "inset", "inside", "intrinsic", "invert", + "inline-block", "inline-flex", "inline-grid", "inline-table", "inset", "inside", "intrinsic", "invert", "italic", "japanese-formal", "japanese-informal", "justify", "kannada", "katakana", "katakana-iroha", "keep-all", "khmer", "korean-hangul-formal", "korean-hanja-formal", "korean-hanja-informal", From 5067baec7bbbe33633c14049d82a0198d9e168b7 Mon Sep 17 00:00:00 2001 From: TDaglis Date: Wed, 11 May 2016 12:44:54 +0300 Subject: [PATCH 0299/1790] [vim mode] Fix fat-cursor border --- lib/codemirror.css | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/lib/codemirror.css b/lib/codemirror.css index 1cf66a9f..4bd815eb 100644 --- a/lib/codemirror.css +++ b/lib/codemirror.css @@ -52,7 +52,7 @@ } .cm-fat-cursor .CodeMirror-cursor { width: auto; - border: 0; + border: 0 !important; background: #7e7; } .cm-fat-cursor div.CodeMirror-cursors { From 55a47a5ecb40e3303d2cdbf602f6dbbe87ee5a3a Mon Sep 17 00:00:00 2001 From: Marijn Haverbeke Date: Tue, 17 May 2016 15:27:24 +0200 Subject: [PATCH 0300/1790] Revert "[sublime keymap] Add sublime-style smart backspace" This reverts commit 8dbb84f44d3f4cdecde25e910277ca1816f80a24. Issue #3127 --- keymap/sublime.js | 21 --------------------- 1 file changed, 21 deletions(-) diff --git a/keymap/sublime.js b/keymap/sublime.js index d9b64ba9..c1749e71 100644 --- a/keymap/sublime.js +++ b/keymap/sublime.js @@ -420,27 +420,6 @@ map[cK + ctrl + "Backspace"] = "delLineLeft"; - cmds[map["Backspace"] = "smartBackspace"] = function(cm) { - if (cm.somethingSelected()) return CodeMirror.Pass; - - var cursor = cm.getCursor(); - var toStartOfLine = cm.getRange({line: cursor.line, ch: 0}, cursor); - var column = CodeMirror.countColumn(toStartOfLine, null, cm.getOption("tabSize")); - var indentUnit = cm.getOption("indentUnit"); - - if (toStartOfLine && !/\S/.test(toStartOfLine) && column % indentUnit == 0) { - var prevIndent = new Pos(cursor.line, - CodeMirror.findColumn(toStartOfLine, column - indentUnit, indentUnit)); - - // If no smart delete is happening (due to tab sizing) just do a regular delete - if (prevIndent.ch == cursor.ch) return CodeMirror.Pass; - - return cm.replaceRange("", prevIndent, cursor, "+delete"); - } else { - return CodeMirror.Pass; - } - }; - cmds[map[cK + ctrl + "K"] = "delLineRight"] = function(cm) { cm.operation(function() { var ranges = cm.listSelections(); From ea280945f56262d47e2e33ffa212e20e913bc0e6 Mon Sep 17 00:00:00 2001 From: Markus Bordihn Date: Fri, 13 May 2016 12:06:25 +0200 Subject: [PATCH 0301/1790] Fixed redeclared variables to avoid compiler warnings. --- addon/edit/closebrackets.js | 2 +- addon/fold/brace-fold.js | 16 ++++++++-------- addon/fold/xml-fold.js | 6 +++--- 3 files changed, 12 insertions(+), 12 deletions(-) diff --git a/addon/edit/closebrackets.js b/addon/edit/closebrackets.js index 3eb9d8ea..af7fce2a 100644 --- a/addon/edit/closebrackets.js +++ b/addon/edit/closebrackets.js @@ -109,7 +109,7 @@ var ranges = cm.listSelections(); var opening = pos % 2 == 0; - var type, next; + var type; for (var i = 0; i < ranges.length; i++) { var range = ranges[i], cur = range.head, curType; var next = cm.getRange(cur, Pos(cur.line, cur.ch + 1)); diff --git a/addon/fold/brace-fold.js b/addon/fold/brace-fold.js index 1605f6c2..13c0f0cd 100644 --- a/addon/fold/brace-fold.js +++ b/addon/fold/brace-fold.js @@ -13,7 +13,7 @@ CodeMirror.registerHelper("fold", "brace", function(cm, start) { var line = start.line, lineText = cm.getLine(line); - var startCh, tokenType; + var tokenType; function findOpening(openCh) { for (var at = start.ch, pass = 0;;) { @@ -72,15 +72,15 @@ CodeMirror.registerHelper("fold", "import", function(cm, start) { } } - var start = start.line, has = hasImport(start), prev; - if (!has || hasImport(start - 1) || ((prev = hasImport(start - 2)) && prev.end.line == start - 1)) + var startLine = start.line, has = hasImport(startLine), prev; + if (!has || hasImport(startLine - 1) || ((prev = hasImport(startLine - 2)) && prev.end.line == startLine - 1)) return null; for (var end = has.end;;) { var next = hasImport(end.line + 1); if (next == null) break; end = next.end; } - return {from: cm.clipPos(CodeMirror.Pos(start, has.startCh + 1)), to: end}; + return {from: cm.clipPos(CodeMirror.Pos(startLine, has.startCh + 1)), to: end}; }); CodeMirror.registerHelper("fold", "include", function(cm, start) { @@ -91,14 +91,14 @@ CodeMirror.registerHelper("fold", "include", function(cm, start) { if (start.type == "meta" && start.string.slice(0, 8) == "#include") return start.start + 8; } - var start = start.line, has = hasInclude(start); - if (has == null || hasInclude(start - 1) != null) return null; - for (var end = start;;) { + var startLine = start.line, has = hasInclude(startLine); + if (has == null || hasInclude(startLine - 1) != null) return null; + for (var end = startLine;;) { var next = hasInclude(end + 1); if (next == null) break; ++end; } - return {from: CodeMirror.Pos(start, has + 1), + return {from: CodeMirror.Pos(startLine, has + 1), to: cm.clipPos(CodeMirror.Pos(end))}; }); diff --git a/addon/fold/xml-fold.js b/addon/fold/xml-fold.js index 504727f3..f8c67b89 100644 --- a/addon/fold/xml-fold.js +++ b/addon/fold/xml-fold.js @@ -140,9 +140,9 @@ var openTag = toNextTag(iter), end; if (!openTag || iter.line != start.line || !(end = toTagEnd(iter))) return; if (!openTag[1] && end != "selfClose") { - var start = Pos(iter.line, iter.ch); - var close = findMatchingClose(iter, openTag[2]); - return close && {from: start, to: close.from}; + var startPos = Pos(iter.line, iter.ch); + var endPos = findMatchingClose(iter, openTag[2]); + return endPos && {from: startPos, to: endPos.from}; } } }); From 65eee1b8c2a893311d68c7db070393590ba68d85 Mon Sep 17 00:00:00 2001 From: Marijn Haverbeke Date: Tue, 17 May 2016 15:49:53 +0200 Subject: [PATCH 0302/1790] Bind Ctrl-O to openLine on OS X Closes #745 --- lib/codemirror.js | 4 +++- 1 file changed, 3 insertions(+), 1 deletion(-) diff --git a/lib/codemirror.js b/lib/codemirror.js index fe51bd3b..0a343efa 100644 --- a/lib/codemirror.js +++ b/lib/codemirror.js @@ -5764,6 +5764,7 @@ ensureCursorVisible(cm); }); }, + openLine: function(cm) {cm.replaceSelection("\n", "start")}, toggleOverwrite: function(cm) {cm.toggleOverwrite();} }; @@ -5798,7 +5799,8 @@ "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": "goPageDown", "Shift-Ctrl-V": "goPageUp", "Ctrl-D": "delCharAfter", "Ctrl-H": "delCharBefore", - "Alt-D": "delWordAfter", "Alt-Backspace": "delWordBefore", "Ctrl-K": "killLine", "Ctrl-T": "transposeChars" + "Alt-D": "delWordAfter", "Alt-Backspace": "delWordBefore", "Ctrl-K": "killLine", "Ctrl-T": "transposeChars", + "Ctrl-O": "openLine" }; keyMap.macDefault = { "Cmd-A": "selectAll", "Cmd-D": "deleteLine", "Cmd-Z": "undo", "Shift-Cmd-Z": "redo", "Cmd-Y": "redo", From 981217306a2a1250a95d5ca8a829b5380f7a618b Mon Sep 17 00:00:00 2001 From: matthewhayes Date: Mon, 16 May 2016 19:25:16 -0700 Subject: [PATCH 0303/1790] Paste linewise-copied content at beginning of line Closes #3983 --- lib/codemirror.js | 10 ++++++++++ 1 file changed, 10 insertions(+) diff --git a/lib/codemirror.js b/lib/codemirror.js index 0a343efa..a51a525e 100644 --- a/lib/codemirror.js +++ b/lib/codemirror.js @@ -1100,6 +1100,10 @@ // when pasting, we know what kind of selections the copied text // was made out of. var lastCopied = null; + // This will be true when the last copy/cut occurred with no selection + // and lineWiseCopyCut was enabled. This allows us to alter the paste + // behavior to be more convenient when lineWiseCopyCut is enabled. + var lastCopiedLinewise = false; function applyTextInput(cm, inserted, deleted, sel, origin) { var doc = cm.doc; @@ -1130,6 +1134,8 @@ from = Pos(from.line, from.ch - deleted); else if (cm.state.overwrite && !paste) // Handle overwrite to = Pos(to.line, Math.min(getLine(doc, to.line).text.length, to.ch + lst(textLines).length)); + else if (paste && lastCopiedLinewise && cm.options.lineWiseCopyCut) // Handle paste after lineWiseCopyCut + from = to = Pos(from.line, 0) } var updateInput = cm.curOp.updateInput; var changeEvent = {from: from, to: to, text: multiPaste ? multiPaste[i % multiPaste.length] : textLines, @@ -1261,6 +1267,7 @@ function prepareCopyCut(e) { if (signalDOMEvent(cm, e)) return + lastCopiedLinewise = false; if (cm.somethingSelected()) { lastCopied = cm.getSelections(); if (input.inaccurateSelection) { @@ -1274,6 +1281,7 @@ } else { var ranges = copyableRanges(cm); lastCopied = ranges.text; + lastCopiedLinewise = true; if (e.type == "cut") { cm.setSelections(ranges.ranges, null, sel_dontScroll); } else { @@ -1620,6 +1628,7 @@ function onCopyCut(e) { if (signalDOMEvent(cm, e)) return + lastCopiedLinewise = false; if (cm.somethingSelected()) { lastCopied = cm.getSelections(); if (e.type == "cut") cm.replaceSelection("", null, "cut"); @@ -1628,6 +1637,7 @@ } else { var ranges = copyableRanges(cm); lastCopied = ranges.text; + lastCopiedLinewise = true; if (e.type == "cut") { cm.operation(function() { cm.setSelections(ranges.ranges, 0, sel_dontScroll); From 889098f1232a1223c52d5f0bbf1631b24b66e79f Mon Sep 17 00:00:00 2001 From: Marijn Haverbeke Date: Tue, 17 May 2016 15:59:42 +0200 Subject: [PATCH 0304/1790] Tie lastCopied and lastCopiedLineWise into single value To make it clearer that they are updated together Issue #4028 --- lib/codemirror.js | 38 +++++++++++++++----------------------- 1 file changed, 15 insertions(+), 23 deletions(-) diff --git a/lib/codemirror.js b/lib/codemirror.js index a51a525e..c289defd 100644 --- a/lib/codemirror.js +++ b/lib/codemirror.js @@ -1096,14 +1096,10 @@ if (!cm.state.focused) { cm.display.input.focus(); onFocus(cm); } } - // 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. + // This will be set to a {lineWise: bool, text: [string]} object, so + // that, when pasting, we know what kind of selections the copied + // text was made out of. var lastCopied = null; - // This will be true when the last copy/cut occurred with no selection - // and lineWiseCopyCut was enabled. This allows us to alter the paste - // behavior to be more convenient when lineWiseCopyCut is enabled. - var lastCopiedLinewise = false; function applyTextInput(cm, inserted, deleted, sel, origin) { var doc = cm.doc; @@ -1114,11 +1110,11 @@ var textLines = doc.splitLines(inserted), multiPaste = null; // When pasing N lines into N selections, insert one line per selection if (paste && sel.ranges.length > 1) { - if (lastCopied && lastCopied.join("\n") == inserted) { - if (sel.ranges.length % lastCopied.length == 0) { + if (lastCopied && lastCopied.text.join("\n") == inserted) { + if (sel.ranges.length % lastCopied.text.length == 0) { multiPaste = []; - for (var i = 0; i < lastCopied.length; i++) - multiPaste.push(doc.splitLines(lastCopied[i])); + for (var i = 0; i < lastCopied.text.length; i++) + multiPaste.push(doc.splitLines(lastCopied.text[i])); } } else if (textLines.length == sel.ranges.length) { multiPaste = map(textLines, function(l) { return [l]; }); @@ -1134,7 +1130,7 @@ from = Pos(from.line, from.ch - deleted); else if (cm.state.overwrite && !paste) // Handle overwrite to = Pos(to.line, Math.min(getLine(doc, to.line).text.length, to.ch + lst(textLines).length)); - else if (paste && lastCopiedLinewise && cm.options.lineWiseCopyCut) // Handle paste after lineWiseCopyCut + else if (paste && lastCopied.lineWise && cm.options.lineWiseCopyCut) // Handle paste after lineWiseCopyCut from = to = Pos(from.line, 0) } var updateInput = cm.curOp.updateInput; @@ -1267,21 +1263,19 @@ function prepareCopyCut(e) { if (signalDOMEvent(cm, e)) return - lastCopiedLinewise = false; if (cm.somethingSelected()) { - lastCopied = cm.getSelections(); + lastCopied = {lineWise: false, text: cm.getSelections()}; if (input.inaccurateSelection) { input.prevInput = ""; input.inaccurateSelection = false; - te.value = lastCopied.join("\n"); + te.value = lastCopied.text.join("\n"); selectInput(te); } } else if (!cm.options.lineWiseCopyCut) { return; } else { var ranges = copyableRanges(cm); - lastCopied = ranges.text; - lastCopiedLinewise = true; + lastCopied = {lineWise: true, text: ranges.text}; if (e.type == "cut") { cm.setSelections(ranges.ranges, null, sel_dontScroll); } else { @@ -1628,16 +1622,14 @@ function onCopyCut(e) { if (signalDOMEvent(cm, e)) return - lastCopiedLinewise = false; if (cm.somethingSelected()) { - lastCopied = cm.getSelections(); + lastCopied = {lineWise: false, text: cm.getSelections()}; if (e.type == "cut") cm.replaceSelection("", null, "cut"); } else if (!cm.options.lineWiseCopyCut) { return; } else { var ranges = copyableRanges(cm); - lastCopied = ranges.text; - lastCopiedLinewise = true; + lastCopied = {lineWise: true, text: ranges.text}; if (e.type == "cut") { cm.operation(function() { cm.setSelections(ranges.ranges, 0, sel_dontScroll); @@ -1649,12 +1641,12 @@ if (e.clipboardData && !ios) { e.preventDefault(); e.clipboardData.clearData(); - e.clipboardData.setData("text/plain", lastCopied.join("\n")); + e.clipboardData.setData("text/plain", lastCopied.text.join("\n")); } else { // Old-fashioned briefly-focus-a-textarea hack var kludge = hiddenTextarea(), te = kludge.firstChild; cm.display.lineSpace.insertBefore(kludge, cm.display.lineSpace.firstChild); - te.value = lastCopied.join("\n"); + te.value = lastCopied.text.join("\n"); var hadFocus = document.activeElement; selectInput(te); setTimeout(function() { From b2c11167b61d20cbba14eaebde00a28f0a8e4bc5 Mon Sep 17 00:00:00 2001 From: Marijn Haverbeke Date: Tue, 17 May 2016 16:08:32 +0200 Subject: [PATCH 0305/1790] [javascript mode] Support async and await Closes #3926 Closes #4026 --- mode/javascript/javascript.js | 4 +++- 1 file changed, 3 insertions(+), 1 deletion(-) diff --git a/mode/javascript/javascript.js b/mode/javascript/javascript.js index fa5721d5..a929321d 100644 --- a/mode/javascript/javascript.js +++ b/mode/javascript/javascript.js @@ -42,7 +42,8 @@ CodeMirror.defineMode("javascript", function(config, parserConfig) { "in": operator, "typeof": operator, "instanceof": operator, "true": atom, "false": atom, "null": atom, "undefined": atom, "NaN": atom, "Infinity": atom, "this": kw("this"), "class": kw("class"), "super": kw("atom"), - "yield": C, "export": kw("export"), "import": kw("import"), "extends": C + "yield": C, "export": kw("export"), "import": kw("import"), "extends": C, + "await": C, "async": kw("async") }; // Extend the 'normal' keywords with the TypeScript language extensions @@ -366,6 +367,7 @@ CodeMirror.defineMode("javascript", function(config, parserConfig) { if (type == "export") return cont(pushlex("stat"), afterExport, poplex); if (type == "import") return cont(pushlex("stat"), afterImport, poplex); if (type == "module") return cont(pushlex("form"), pattern, pushlex("}"), expect("{"), block, poplex, poplex) + if (type == "async") return cont(statement) return pass(pushlex("stat"), expression, expect(";"), poplex); } function expression(type) { From 28abd453ff7f6cf9b3ca35f74a2de6ff4e00d339 Mon Sep 17 00:00:00 2001 From: Marijn Haverbeke Date: Thu, 19 May 2016 09:36:21 +0200 Subject: [PATCH 0306/1790] Only read lastCopied.lineWise when lastCopied matches clipboard Closes #4030 Closes #4031 --- lib/codemirror.js | 5 +++-- 1 file changed, 3 insertions(+), 2 deletions(-) diff --git a/lib/codemirror.js b/lib/codemirror.js index c289defd..6cca4465 100644 --- a/lib/codemirror.js +++ b/lib/codemirror.js @@ -1107,10 +1107,11 @@ if (!sel) sel = doc.sel; var paste = cm.state.pasteIncoming || origin == "paste"; - var textLines = doc.splitLines(inserted), multiPaste = null; + var textLines = doc.splitLines(inserted), multiPaste = null, lineWisePaste = false; // When pasing N lines into N selections, insert one line per selection if (paste && sel.ranges.length > 1) { if (lastCopied && lastCopied.text.join("\n") == inserted) { + lineWisePaste = lastCopied.lineWise if (sel.ranges.length % lastCopied.text.length == 0) { multiPaste = []; for (var i = 0; i < lastCopied.text.length; i++) @@ -1130,7 +1131,7 @@ from = Pos(from.line, from.ch - deleted); else if (cm.state.overwrite && !paste) // Handle overwrite to = Pos(to.line, Math.min(getLine(doc, to.line).text.length, to.ch + lst(textLines).length)); - else if (paste && lastCopied.lineWise && cm.options.lineWiseCopyCut) // Handle paste after lineWiseCopyCut + else if (lineWisePaste) from = to = Pos(from.line, 0) } var updateInput = cm.curOp.updateInput; From 20d981f539eb8934ca73c44135b5e545a3d33902 Mon Sep 17 00:00:00 2001 From: Marijn Haverbeke Date: Thu, 19 May 2016 23:17:06 +0200 Subject: [PATCH 0307/1790] Fix patch 28abd453ff7 Issue #3983 --- lib/codemirror.js | 5 ++--- 1 file changed, 2 insertions(+), 3 deletions(-) diff --git a/lib/codemirror.js b/lib/codemirror.js index 6cca4465..99df533b 100644 --- a/lib/codemirror.js +++ b/lib/codemirror.js @@ -1107,11 +1107,10 @@ if (!sel) sel = doc.sel; var paste = cm.state.pasteIncoming || origin == "paste"; - var textLines = doc.splitLines(inserted), multiPaste = null, lineWisePaste = false; + var textLines = doc.splitLines(inserted), multiPaste = null // When pasing N lines into N selections, insert one line per selection if (paste && sel.ranges.length > 1) { if (lastCopied && lastCopied.text.join("\n") == inserted) { - lineWisePaste = lastCopied.lineWise if (sel.ranges.length % lastCopied.text.length == 0) { multiPaste = []; for (var i = 0; i < lastCopied.text.length; i++) @@ -1131,7 +1130,7 @@ from = Pos(from.line, from.ch - deleted); else if (cm.state.overwrite && !paste) // Handle overwrite to = Pos(to.line, Math.min(getLine(doc, to.line).text.length, to.ch + lst(textLines).length)); - else if (lineWisePaste) + else if (lastCopied && lastCopied.lineWise && lastCopied.text.join("\n") == inserted) from = to = Pos(from.line, 0) } var updateInput = cm.curOp.updateInput; From 59c4ebd9e6e3bec3adeabd2724e2048143872856 Mon Sep 17 00:00:00 2001 From: Marijn Haverbeke Date: Fri, 20 May 2016 10:40:02 +0200 Subject: [PATCH 0308/1790] [clike mode] Allow underscores in Java numbers Closes #3900 --- mode/clike/clike.js | 1 + 1 file changed, 1 insertion(+) diff --git a/mode/clike/clike.js b/mode/clike/clike.js index aadab9e1..a37921fd 100644 --- a/mode/clike/clike.js +++ b/mode/clike/clike.js @@ -433,6 +433,7 @@ CodeMirror.defineMode("clike", function(config, parserConfig) { typeFirstDefinitions: true, atoms: words("true false null"), endStatement: /^[;:]$/, + number: /^(?:0x[a-f\d_]+|0b[01_]+|(?:[\d_]+\.?\d*|\.\d+)(?:e[-+]?[\d_]+)?)(u|ll?|l|f)?/i, hooks: { "@": function(stream) { stream.eatWhile(/[\w\$_]/); From eb3eab7088989841d8dba74400c584bdeb0084ba Mon Sep 17 00:00:00 2001 From: Marijn Haverbeke Date: Fri, 20 May 2016 10:56:39 +0200 Subject: [PATCH 0309/1790] [simplescrollbars addon] Make sure thumb position is updated when size changes Closes #3896 --- addon/scroll/simplescrollbars.js | 15 +++++++++------ 1 file changed, 9 insertions(+), 6 deletions(-) diff --git a/addon/scroll/simplescrollbars.js b/addon/scroll/simplescrollbars.js index 32ba2f35..23f3e03f 100644 --- a/addon/scroll/simplescrollbars.js +++ b/addon/scroll/simplescrollbars.js @@ -59,10 +59,10 @@ CodeMirror.on(this.node, "DOMMouseScroll", onWheel); } - Bar.prototype.setPos = function(pos) { + Bar.prototype.setPos = function(pos, force) { if (pos < 0) pos = 0; if (pos > this.total - this.screen) pos = this.total - this.screen; - if (pos == this.pos) return false; + if (!force && pos == this.pos) return false; this.pos = pos; this.inner.style[this.orientation == "horizontal" ? "left" : "top"] = (pos * (this.size / this.total)) + "px"; @@ -76,9 +76,12 @@ var minButtonSize = 10; Bar.prototype.update = function(scrollSize, clientSize, barSize) { - this.screen = clientSize; - this.total = scrollSize; - this.size = barSize; + var sizeChanged = this.screen != clientSize || this.total != scrollSize || this.size != barSize + if (sizeChanged) { + this.screen = clientSize; + this.total = scrollSize; + this.size = barSize; + } var buttonSize = this.screen * (this.size / this.total); if (buttonSize < minButtonSize) { @@ -87,7 +90,7 @@ } this.inner.style[this.orientation == "horizontal" ? "width" : "height"] = buttonSize + "px"; - this.setPos(this.pos); + this.setPos(this.pos, sizeChanged); }; function SimpleScrollbars(cls, place, scroll) { From 8c00b06b12fcaaec59b36649fb3a672ef6045a9c Mon Sep 17 00:00:00 2001 From: Marijn Haverbeke Date: Fri, 20 May 2016 11:11:39 +0200 Subject: [PATCH 0310/1790] [javascript mode] Improve parsing of TypeScript types Closes #3796 --- mode/javascript/javascript.js | 21 +++++++++++++-------- 1 file changed, 13 insertions(+), 8 deletions(-) diff --git a/mode/javascript/javascript.js b/mode/javascript/javascript.js index a929321d..5f3b3d64 100644 --- a/mode/javascript/javascript.js +++ b/mode/javascript/javascript.js @@ -264,6 +264,7 @@ CodeMirror.defineMode("javascript", function(config, parserConfig) { while(true) { var combinator = cc.length ? cc.pop() : jsonMode ? expression : statement; + if (!combinator.call) console.log(combinator) if (combinator(type, content)) { while(cc.length && cc[cc.length - 1].lex) cc.pop()(); @@ -490,17 +491,17 @@ CodeMirror.defineMode("javascript", function(config, parserConfig) { if (type == "(") return pass(functiondef); } function commasep(what, end) { - function proceed(type) { + function proceed(type, value) { 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(); + if (type == end || value == end) return cont(); return cont(expect(end)); } - return function(type) { - if (type == end) return cont(); + return function(type, value) { + if (type == end || value == end) return cont(); return pass(what, proceed); }; } @@ -514,13 +515,17 @@ CodeMirror.defineMode("javascript", function(config, parserConfig) { return pass(statement, block); } function maybetype(type) { - if (isTS && type == ":") return cont(typedef); + if (isTS && type == ":") return cont(typeexpr); } function maybedefault(_, value) { if (value == "=") return cont(expressionNoComma); } - function typedef(type) { - if (type == "variable") {cx.marked = "variable-3"; return cont();} + function typeexpr(type) { + if (type == "variable") {cx.marked = "variable-3"; return cont(afterType);} + } + function afterType(type, value) { + if (value == "<") return cont(commasep(typeexpr, ">"), afterType) + if (type == "[") return cont(expect("]"), afterType) } function vardef() { return pass(pattern, maybetype, maybeAssign, vardefCont); @@ -575,7 +580,7 @@ CodeMirror.defineMode("javascript", function(config, parserConfig) { function functiondef(type, value) { if (value == "*") {cx.marked = "keyword"; return cont(functiondef);} if (type == "variable") {register(value); return cont(functiondef);} - if (type == "(") return cont(pushcontext, pushlex(")"), commasep(funarg, ")"), poplex, statement, popcontext); + if (type == "(") return cont(pushcontext, pushlex(")"), commasep(funarg, ")"), poplex, maybetype, statement, popcontext); } function funarg(type) { if (type == "spread") return cont(funarg); From b27701cb1e0cfa4b8c9ee3ca2bc1ce4e4caf24e6 Mon Sep 17 00:00:00 2001 From: Marijn Haverbeke Date: Fri, 20 May 2016 11:23:36 +0200 Subject: [PATCH 0311/1790] [sql-hint addon] Don't reuse global keywords variable See https://discuss.codemirror.net/t/dynamically-switch-mode-with-sql-hint-addon-bugs/751 --- addon/hint/sql-hint.js | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/addon/hint/sql-hint.js b/addon/hint/sql-hint.js index 62c4f68d..ed8f6d85 100644 --- a/addon/hint/sql-hint.js +++ b/addon/hint/sql-hint.js @@ -241,7 +241,7 @@ var defaultTableName = options && options.defaultTable; var disableKeywords = options && options.disableKeywords; defaultTable = defaultTableName && getTable(defaultTableName); - keywords = keywords || getKeywords(editor); + keywords = getKeywords(editor); if (defaultTableName && !defaultTable) defaultTable = findTableByAlias(defaultTableName, editor); From 5473c7ae4fc9585f406202bbca2a8d1f23dbb541 Mon Sep 17 00:00:00 2001 From: Marijn Haverbeke Date: Fri, 20 May 2016 11:24:36 +0200 Subject: [PATCH 0312/1790] Remove debug statement --- mode/javascript/javascript.js | 1 - 1 file changed, 1 deletion(-) diff --git a/mode/javascript/javascript.js b/mode/javascript/javascript.js index 5f3b3d64..ca875411 100644 --- a/mode/javascript/javascript.js +++ b/mode/javascript/javascript.js @@ -264,7 +264,6 @@ CodeMirror.defineMode("javascript", function(config, parserConfig) { while(true) { var combinator = cc.length ? cc.pop() : jsonMode ? expression : statement; - if (!combinator.call) console.log(combinator) if (combinator(type, content)) { while(cc.length && cc[cc.length - 1].lex) cc.pop()(); From 28844205a6ab2f62bdd577351cdea22560f52a8e Mon Sep 17 00:00:00 2001 From: Marijn Haverbeke Date: Fri, 20 May 2016 12:01:05 +0200 Subject: [PATCH 0313/1790] Mark release 5.15.0 --- AUTHORS | 6 ++++++ CHANGELOG.md | 26 ++++++++++++++++++++++++++ doc/compress.html | 1 + doc/manual.html | 2 +- doc/releases.html | 15 +++++++++++++++ index.html | 2 +- lib/codemirror.js | 2 +- package.json | 2 +- 8 files changed, 52 insertions(+), 4 deletions(-) diff --git a/AUTHORS b/AUTHORS index f64cb6d1..5e373a02 100644 --- a/AUTHORS +++ b/AUTHORS @@ -97,6 +97,7 @@ Brian Sletten Bruce Mitchener Caitlin Potter Calin Barbat +Camilo Roca Chad Jolly Chandra Sekhar Pydi Charles Skelton @@ -107,6 +108,7 @@ Chris Granger Chris Houseknecht Chris Lohfink Chris Morgan +Chris Smith Christian Oyarzun Christian Petrov Christopher Brown @@ -246,6 +248,7 @@ Jan Schär Jan T. Sott Jared Dean Jared Forsyth +Jared Jacobs Jason Jason Barnabe Jason Grout @@ -360,6 +363,7 @@ mats cronqvist Matt Gaide Matthew Bauer Matthew Beale +matthewhayes Matthew Rathbone Matthias Bussonnier Matthias BUSSONNIER @@ -438,6 +442,7 @@ Paul Garvin Paul Ivanov Pavel Pavel Feldman +Pavel Petržela Pavel Strashkin Paweł Bartkiewicz peteguhl @@ -551,6 +556,7 @@ vf Victor Bocharsky Vincent Woo Volker Mische +Weiyan Shao wenli Wes Cossick Wesley Wiser diff --git a/CHANGELOG.md b/CHANGELOG.md index f6f31237..562034a1 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -1,3 +1,29 @@ +## 5.15.0 (2016-05-20) + +### Bugfixes + +Fix bug that caused the selection to reset when focusing the editor in contentEditable input mode. + +Fix issue where not all ASCII control characters were being replaced by placeholders. + +Remove the assumption that all modes have a `startState` method from several wrapping modes. + +Fix issue where the editor would complain about overlapping collapsed ranges when there weren't any. + +Optimize document tree building when loading or pasting huge chunks of content. + +[markdown mode](http://codemirror.net/mode/markdown/): Fix several issues in matching link targets. + +[clike mode](http://codemirror.net/mode/clike/): Improve indentation of C++ template declarations. + +### New features + +Explicitly bind Ctrl-O on OS X to make that binding (“open line”) act as expected. + +Pasting [linewise-copied](http://codemirror.net/doc/manual.html#option_lineWiseCopyCut) content when there is no selection now inserts the lines above the current line. + +[javascript mode](http://codemirror.net/mode/javascript/): Support `async`/`await` and improve support for TypeScript type syntax. + ## 5.14.2 (2016-04-20) ### Bugfixes diff --git a/doc/compress.html b/doc/compress.html index 47912692..061c3c69 100644 --- a/doc/compress.html +++ b/doc/compress.html @@ -36,6 +36,7 @@

    Script compression helper

    Version:

    Version:

    Version:

    Version:

    From 23172fded8ddc9c9b1bee9c2617f25c884ed9d0d Mon Sep 17 00:00:00 2001 From: Marijn Haverbeke Date: Tue, 19 Jul 2016 23:17:38 +0200 Subject: [PATCH 0369/1790] Add missing scripts to compression helper Closes #4122 --- doc/compress.html | 2 ++ 1 file changed, 2 insertions(+) diff --git a/doc/compress.html b/doc/compress.html index 7f08f71c..80de52da 100644 --- a/doc/compress.html +++ b/doc/compress.html @@ -252,12 +252,14 @@

    Script compression helper

    + + From f8b765b2eb5ab7d99b038f41de3d35621d71baee Mon Sep 17 00:00:00 2001 From: Marijn Haverbeke Date: Fri, 22 Jul 2016 13:54:18 +0200 Subject: [PATCH 0370/1790] [matchbrackets addon] Clear matched brackets when the addon is turned off Closes #4128 --- addon/edit/matchbrackets.js | 4 +++- 1 file changed, 3 insertions(+), 1 deletion(-) diff --git a/addon/edit/matchbrackets.js b/addon/edit/matchbrackets.js index 70e1ae18..76754ed5 100644 --- a/addon/edit/matchbrackets.js +++ b/addon/edit/matchbrackets.js @@ -102,8 +102,10 @@ } CodeMirror.defineOption("matchBrackets", false, function(cm, val, old) { - if (old && old != CodeMirror.Init) + if (old && old != CodeMirror.Init) { cm.off("cursorActivity", doMatchBrackets); + if (currentlyHighlighted) {currentlyHighlighted(); currentlyHighlighted = null;} + } if (val) { cm.state.matchBrackets = typeof val == "object" ? val : {}; cm.on("cursorActivity", doMatchBrackets); From 5e29ee6e8e157efb6c9bf5291324348bc02a8ef8 Mon Sep 17 00:00:00 2001 From: Marijn Haverbeke Date: Sat, 23 Jul 2016 22:40:00 +0200 Subject: [PATCH 0371/1790] [javascript mode] Fix bug in array literal parsing Closes #4130 --- 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 3909c85e..923f42e2 100644 --- a/mode/javascript/javascript.js +++ b/mode/javascript/javascript.js @@ -641,7 +641,7 @@ CodeMirror.defineMode("javascript", function(config, parserConfig) { } function arrayLiteral(type) { if (type == "]") return cont(); - return pass(expressionNoComma, commasep(expressionNoComma, "]")); + return pass(commasep(expressionNoComma, "]")); } function isContinuedStatement(state, textAfter) { From b3482bb9306be0388d3ab09740863932875eb998 Mon Sep 17 00:00:00 2001 From: Marijn Haverbeke Date: Mon, 25 Jul 2016 09:25:35 +0200 Subject: [PATCH 0372/1790] [javascript mode] Fix a bug in parsing of arrow function with no args Closes #4131 --- 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 923f42e2..da6b760f 100644 --- a/mode/javascript/javascript.js +++ b/mode/javascript/javascript.js @@ -216,7 +216,7 @@ CodeMirror.defineMode("javascript", function(config, parserConfig) { var bracket = brackets.indexOf(ch); if (bracket >= 0 && bracket < 3) { if (!depth) { ++pos; break; } - if (--depth == 0) break; + if (--depth == 0) { if (ch == "(") sawSomething = true; break; } } else if (bracket >= 3 && bracket < 6) { ++depth; } else if (wordRE.test(ch)) { From 6d1379e29ad5a7bcef3ecfec7c6cd739a53567d9 Mon Sep 17 00:00:00 2001 From: Joel Einbinder Date: Fri, 29 Jul 2016 12:31:37 -0700 Subject: [PATCH 0373/1790] Fix gutterBackgrounds to a fixedGutter --- lib/codemirror.js | 8 ++++++-- 1 file changed, 6 insertions(+), 2 deletions(-) diff --git a/lib/codemirror.js b/lib/codemirror.js index ff7126fb..4568673e 100644 --- a/lib/codemirror.js +++ b/lib/codemirror.js @@ -592,8 +592,12 @@ var comp = compensateForHScroll(display) - display.scroller.scrollLeft + cm.doc.scrollLeft; var gutterW = display.gutters.offsetWidth, left = comp + "px"; for (var i = 0; i < view.length; i++) if (!view[i].hidden) { - if (cm.options.fixedGutter && view[i].gutter) - view[i].gutter.style.left = left; + if (cm.options.fixedGutter) { + if (view[i].gutter) + view[i].gutter.style.left = left; + if (view[i].gutterBackground) + view[i].gutterBackground.style.left = left; + } var align = view[i].alignable; if (align) for (var j = 0; j < align.length; j++) align[j].style.left = left; From 597a8c81b5109b86dca7274ea47dd64dbf2870ee Mon Sep 17 00:00:00 2001 From: Marijn Haverbeke Date: Thu, 4 Aug 2016 17:15:32 +0200 Subject: [PATCH 0374/1790] [htmlmixed mode] Strip whitespace from attribute values Closes #4147 --- 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 d74083ee..eb21fcc1 100644 --- a/mode/htmlmixed/htmlmixed.js +++ b/mode/htmlmixed/htmlmixed.js @@ -46,7 +46,7 @@ function getAttrValue(text, attr) { var match = text.match(getAttrRegexp(attr)) - return match ? match[2] : "" + return match ? /^\s*(.*?)\s*$/.exec(match[2])[1] : "" } function getTagRegexp(tagName, anchored) { From 46b93a27f88b865c24ce87b7f4af80ad36d6fe7b Mon Sep 17 00:00:00 2001 From: Marijn Haverbeke Date: Fri, 5 Aug 2016 14:00:03 +0200 Subject: [PATCH 0375/1790] [javascript mode] Support for TypeScript object types Closes #4149 --- mode/javascript/javascript.js | 9 +++++++++ 1 file changed, 9 insertions(+) diff --git a/mode/javascript/javascript.js b/mode/javascript/javascript.js index da6b760f..79239ef4 100644 --- a/mode/javascript/javascript.js +++ b/mode/javascript/javascript.js @@ -525,6 +525,15 @@ CodeMirror.defineMode("javascript", function(config, parserConfig) { } function typeexpr(type) { if (type == "variable") {cx.marked = "variable-3"; return cont(afterType);} + if (type == "{") return cont(commasep(typeprop, "}")) + } + function typeprop(type, value) { + if (type == "variable" || cx.style == "keyword") { + cx.marked = "property" + return cont(typeprop) + } else if (type == ":") { + return cont(typeexpr) + } } function afterType(type, value) { if (value == "<") return cont(commasep(typeexpr, ">"), afterType) From da81eebc0e3d90e1c1d3a690a1a945a91d4cf647 Mon Sep 17 00:00:00 2001 From: Marijn Haverbeke Date: Fri, 5 Aug 2016 14:04:52 +0200 Subject: [PATCH 0376/1790] Fix linter issue --- 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 79239ef4..092bc6f3 100644 --- a/mode/javascript/javascript.js +++ b/mode/javascript/javascript.js @@ -527,7 +527,7 @@ CodeMirror.defineMode("javascript", function(config, parserConfig) { if (type == "variable") {cx.marked = "variable-3"; return cont(afterType);} if (type == "{") return cont(commasep(typeprop, "}")) } - function typeprop(type, value) { + function typeprop(type) { if (type == "variable" || cx.style == "keyword") { cx.marked = "property" return cont(typeprop) From 524d006e33b09e32a00d993845ea34356067f6d6 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Filip=20Stoll=C3=A1r?= Date: Mon, 8 Aug 2016 15:05:24 +0100 Subject: [PATCH 0377/1790] [solarized theme] Fix fat cursor color in light mode --- theme/solarized.css | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/theme/solarized.css b/theme/solarized.css index 4b1e1630..1f39c7ed 100644 --- a/theme/solarized.css +++ b/theme/solarized.css @@ -155,8 +155,8 @@ http://ethanschoonover.com/solarized/img/solarized-palette.png .cm-s-solarized .CodeMirror-cursor { border-left: 1px solid #819090; } /* Fat cursor */ -.cm-s-solarized.cm-s-light.cm-fat-cursor .CodeMirror-cursor { background: #fdf6e3; } -.cm-s-solarized.cm-s-light .cm-animate-fat-cursor { background-color: #fdf6e3; } +.cm-s-solarized.cm-s-light.cm-fat-cursor .CodeMirror-cursor { background: #77ee77; } +.cm-s-solarized.cm-s-light .cm-animate-fat-cursor { background-color: #77ee77; } .cm-s-solarized.cm-s-dark.cm-fat-cursor .CodeMirror-cursor { background: #586e75; } .cm-s-solarized.cm-s-dark .cm-animate-fat-cursor { background-color: #586e75; } From 92b64d23ad6bc792dd23ffb4889ab070760c5e05 Mon Sep 17 00:00:00 2001 From: callodacity Date: Tue, 9 Aug 2016 12:48:16 +1000 Subject: [PATCH 0378/1790] Fix typo in comment --- lib/codemirror.js | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/lib/codemirror.js b/lib/codemirror.js index 4568673e..d08aa67f 100644 --- a/lib/codemirror.js +++ b/lib/codemirror.js @@ -7962,7 +7962,7 @@ } // Register a change in the history. Merges changes that are within - // a single operation, ore are close together with an origin that + // a single operation, or are close together with an origin that // allows merging (starting with "+") into a single event. function addChangeToHistory(doc, change, selAfter, opId) { var hist = doc.history; From e47ab5f92d291ed3890459833c8897d7fb1e3fcd Mon Sep 17 00:00:00 2001 From: Alasdair Smith Date: Tue, 9 Aug 2016 16:51:45 +0100 Subject: [PATCH 0379/1790] [search addon] Fix error on immediate search with no previous query Searching with findPersistentNext or findPersistentPrev will immediately run a search with the previous query, however it will throw and error if no previous query exists. This commit fixes by preventing the immediate execution if no previous query is set --- addon/search/search.js | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/addon/search/search.js b/addon/search/search.js index c005866f..cd612565 100644 --- a/addon/search/search.js +++ b/addon/search/search.js @@ -146,7 +146,7 @@ searchNext(query, event); } }); - if (immediate) { + if (immediate && q) { startSearch(cm, state, q); findNext(cm, rev); } From e452ba29689cffa2b81dd8759d3dadf679cd3ab0 Mon Sep 17 00:00:00 2001 From: Alasdair Smith Date: Wed, 10 Aug 2016 14:04:49 +0100 Subject: [PATCH 0380/1790] [search addon] Find in extraKeys when handling next/prev command in persistent search When persistent search dialog is open, looking up commands for findNext/ Prev keys would only search the current keyMap. This commit changes this to also look up commands in extraKeys --- addon/search/search.js | 4 +++- 1 file changed, 3 insertions(+), 1 deletion(-) diff --git a/addon/search/search.js b/addon/search/search.js index cd612565..be14c93e 100644 --- a/addon/search/search.js +++ b/addon/search/search.js @@ -136,7 +136,9 @@ }) }; persistentDialog(cm, queryDialog, q, searchNext, function(event, query) { - var cmd = CodeMirror.keyMap[cm.getOption("keyMap")][CodeMirror.keyName(event)]; + var keyName = CodeMirror.keyName(event) + var cmd = CodeMirror.keyMap[cm.getOption("keyMap")][keyName] + if (!cmd) cmd = cm.getOption('extraKeys')[keyName] if (cmd == "findNext" || cmd == "findPrev") { CodeMirror.e_stop(event); startSearch(cm, getSearchState(cm), query); From 26d8c7109aa175adf8d234a92649c46bdf3b2bb2 Mon Sep 17 00:00:00 2001 From: Alasdair Smith Date: Wed, 10 Aug 2016 14:07:31 +0100 Subject: [PATCH 0381/1790] [search addon] Add findPersistentNext/Prev commands to execute in search dialog --- addon/search/search.js | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-) diff --git a/addon/search/search.js b/addon/search/search.js index be14c93e..753b1afe 100644 --- a/addon/search/search.js +++ b/addon/search/search.js @@ -139,7 +139,8 @@ var keyName = CodeMirror.keyName(event) var cmd = CodeMirror.keyMap[cm.getOption("keyMap")][keyName] if (!cmd) cmd = cm.getOption('extraKeys')[keyName] - if (cmd == "findNext" || cmd == "findPrev") { + if (cmd == "findNext" || cmd == "findPrev" || + cmd == "findPersistentNext" || cmd == "findPersistentPrev") { CodeMirror.e_stop(event); startSearch(cm, getSearchState(cm), query); cm.execCommand(cmd); From 5738f9b2cff5241ea13e32db3579eb347e56e7a0 Mon Sep 17 00:00:00 2001 From: Timothy Hatcher Date: Fri, 12 Aug 2016 17:30:55 -0700 Subject: [PATCH 0382/1790] Special case JSON based MIME-types. Fixes issue #4163. --- lib/codemirror.js | 2 ++ 1 file changed, 2 insertions(+) diff --git a/lib/codemirror.js b/lib/codemirror.js index d08aa67f..65bfc799 100644 --- a/lib/codemirror.js +++ b/lib/codemirror.js @@ -5533,6 +5533,8 @@ spec.name = found.name; } else if (typeof spec == "string" && /^[\w\-]+\/[\w\-]+\+xml$/.test(spec)) { return CodeMirror.resolveMode("application/xml"); + } else if (typeof spec == "string" && /^[\w\-]+\/[\w\-]+\+json$/.test(spec)) { + return CodeMirror.resolveMode("application/json"); } if (typeof spec == "string") return {name: spec}; else return spec || {name: "null"}; From 4fda0e229a099b62be077a0d7f3c2f431308e9e9 Mon Sep 17 00:00:00 2001 From: Marijn Haverbeke Date: Mon, 15 Aug 2016 12:19:09 +0200 Subject: [PATCH 0383/1790] [manual] Clarify findMarks behavior --- doc/manual.html | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/doc/manual.html b/doc/manual.html index 408ac8bc..b912a78f 100644 --- a/doc/manual.html +++ b/doc/manual.html @@ -1674,7 +1674,7 @@

    Text-marking methods

    doc.findMarks(from: {line, ch}, to: {line, ch}) → array<TextMarker>
    Returns an array of all the bookmarks and marked ranges - found between the given positions.
    + found between the given positions (non-inclusive).
    doc.findMarksAt(pos: {line, ch}) → array<TextMarker>
    Returns an array of all the bookmarks and marked ranges present at the given position.
    From 6b307573e476e2f5215681303b0a39d3068a9d2f Mon Sep 17 00:00:00 2001 From: Marijn Haverbeke Date: Mon, 15 Aug 2016 12:20:41 +0200 Subject: [PATCH 0384/1790] [livescript mode] Fix bug with use of last token Closes #4140 --- mode/livescript/livescript.js | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/mode/livescript/livescript.js b/mode/livescript/livescript.js index 4b26e046..1e363f87 100644 --- a/mode/livescript/livescript.js +++ b/mode/livescript/livescript.js @@ -50,7 +50,7 @@ startState: function(){ return { next: 'start', - lastToken: null + lastToken: {style: null, indent: 0, content: ""} }; }, token: function(stream, state){ From 3ac216b65f8281f0c24b3b2dfac7c892bff08399 Mon Sep 17 00:00:00 2001 From: Marijn Haverbeke Date: Mon, 15 Aug 2016 12:34:39 +0200 Subject: [PATCH 0385/1790] [comment addon] Remove misguided test Closes #4148 --- addon/comment/comment.js | 2 +- test/comment_test.js | 5 +++++ 2 files changed, 6 insertions(+), 1 deletion(-) diff --git a/addon/comment/comment.js b/addon/comment/comment.js index 2c4f975d..2f2f071d 100644 --- a/addon/comment/comment.js +++ b/addon/comment/comment.js @@ -140,7 +140,7 @@ var line = self.getLine(i); var found = line.indexOf(lineString); if (found > -1 && !/comment/.test(self.getTokenTypeAt(Pos(i, found + 1)))) found = -1; - if (found == -1 && (i != end || i == start) && nonWS.test(line)) break lineComment; + if (found == -1 && nonWS.test(line)) break lineComment; if (found > -1 && nonWS.test(line.slice(0, found))) break lineComment; lines.push(line); } diff --git a/test/comment_test.js b/test/comment_test.js index 26e47449..3e6a86bb 100644 --- a/test/comment_test.js +++ b/test/comment_test.js @@ -97,4 +97,9 @@ namespace = "comment_"; test("dontMessWithStrings3", "javascript", function(cm) { cm.execCommand("toggleComment"); }, "// console.log(\"// string\");", "console.log(\"// string\");"); + + test("includeLastLine", "javascript", function(cm) { + cm.execCommand("selectAll") + cm.execCommand("toggleComment") + }, "// foo\n// bar\nbaz", "// // foo\n// // bar\n// baz") })(); From a7bfe347ca6d9e402526bd1331437b025c868c46 Mon Sep 17 00:00:00 2001 From: Marijn Haverbeke Date: Mon, 15 Aug 2016 12:39:58 +0200 Subject: [PATCH 0386/1790] [jsx mode] Add a MIME type for TypeScript+JSX Issue #3893 --- mode/jsx/index.html | 2 +- mode/jsx/jsx.js | 1 + 2 files changed, 2 insertions(+), 1 deletion(-) diff --git a/mode/jsx/index.html b/mode/jsx/index.html index cb51edb3..1054bbcc 100644 --- a/mode/jsx/index.html +++ b/mode/jsx/index.html @@ -84,6 +84,6 @@

    JSX mode

    JSX Mode for React's JavaScript syntax extension.

    -

    MIME types defined: text/jsx.

    +

    MIME types defined: text/jsx, text/typescript-jsx.

    diff --git a/mode/jsx/jsx.js b/mode/jsx/jsx.js index aff01b8d..45c3024a 100644 --- a/mode/jsx/jsx.js +++ b/mode/jsx/jsx.js @@ -144,4 +144,5 @@ }, "xml", "javascript") CodeMirror.defineMIME("text/jsx", "jsx") + CodeMirror.defineMIME("text/typescript-jsx", {name: "jsx", base: {name: "javascript", typescript: true}}) }); From cbbd9415a09835d10978cfe29750cfe3f4cc392d Mon Sep 17 00:00:00 2001 From: Marijn Haverbeke Date: Mon, 15 Aug 2016 12:53:42 +0200 Subject: [PATCH 0387/1790] [javascript mode] Improve handling of async in object literal Closes #4152 --- 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 092bc6f3..18c65e90 100644 --- a/mode/javascript/javascript.js +++ b/mode/javascript/javascript.js @@ -463,8 +463,10 @@ CodeMirror.defineMode("javascript", function(config, parserConfig) { if (type == "variable") {cx.marked = "property"; return cont();} } function objprop(type, value) { - if (type == "async") return cont(objprop); - if (type == "variable" || cx.style == "keyword") { + if (type == "async") { + cx.marked = "property"; + return cont(objprop); + } else if (type == "variable" || cx.style == "keyword") { cx.marked = "property"; if (value == "get" || value == "set") return cont(getterSetter); return cont(afterprop); @@ -479,6 +481,8 @@ CodeMirror.defineMode("javascript", function(config, parserConfig) { return cont(expression, expect("]"), afterprop); } else if (type == "spread") { return cont(expression); + } else if (type == ":") { + return pass(afterprop) } } function getterSetter(type) { From 7061a5a4016b2fb24fe79ad39e8f320e5136ff92 Mon Sep 17 00:00:00 2001 From: Marijn Haverbeke Date: Mon, 15 Aug 2016 13:07:10 +0200 Subject: [PATCH 0388/1790] [javascript mode] Recognize TypeScript function types Closes #4150 --- mode/javascript/javascript.js | 4 ++++ 1 file changed, 4 insertions(+) diff --git a/mode/javascript/javascript.js b/mode/javascript/javascript.js index 18c65e90..17f05045 100644 --- a/mode/javascript/javascript.js +++ b/mode/javascript/javascript.js @@ -530,6 +530,10 @@ CodeMirror.defineMode("javascript", function(config, parserConfig) { function typeexpr(type) { if (type == "variable") {cx.marked = "variable-3"; return cont(afterType);} if (type == "{") return cont(commasep(typeprop, "}")) + if (type == "(") return cont(commasep(typeprop, ")"), maybeReturnType) + } + function maybeReturnType(type) { + if (type == "=>") return cont(typeexpr) } function typeprop(type) { if (type == "variable" || cx.style == "keyword") { From c5c1e9272504445a11cec3afc7253fe35db4f46c Mon Sep 17 00:00:00 2001 From: Marijn Haverbeke Date: Mon, 15 Aug 2016 13:13:51 +0200 Subject: [PATCH 0389/1790] Add experimental spellcheck option Closes #4113 --- lib/codemirror.js | 9 ++++++--- 1 file changed, 6 insertions(+), 3 deletions(-) diff --git a/lib/codemirror.js b/lib/codemirror.js index 65bfc799..0d34b852 100644 --- a/lib/codemirror.js +++ b/lib/codemirror.js @@ -1197,10 +1197,10 @@ return {text: text, ranges: ranges}; } - function disableBrowserMagic(field) { + function disableBrowserMagic(field, spellcheck) { field.setAttribute("autocorrect", "off"); field.setAttribute("autocapitalize", "off"); - field.setAttribute("spellcheck", "false"); + field.setAttribute("spellcheck", !!spellcheck); } // TEXTAREA INPUT STYLE @@ -1578,7 +1578,7 @@ init: function(display) { var input = this, cm = input.cm; var div = input.div = display.lineDiv; - disableBrowserMagic(div); + disableBrowserMagic(div, cm.options.spellcheck); on(div, "paste", function(e) { if (!signalDOMEvent(cm, e)) handlePaste(e, cm); @@ -5424,6 +5424,9 @@ option("inputStyle", mobile ? "contenteditable" : "textarea", function() { throw new Error("inputStyle can not (yet) be changed in a running editor"); // FIXME }, true); + option("spellcheck", false, function(cm, val) { + cm.getInputField().spellcheck = val + }, true); option("rtlMoveVisually", !windows); option("wholeLineUpdateBefore", true); From 85888eb1db2b9042a69885f1ef9641f45ab1d527 Mon Sep 17 00:00:00 2001 From: Marijn Haverbeke Date: Mon, 15 Aug 2016 13:41:15 +0200 Subject: [PATCH 0390/1790] Add a priority option to addOverlay See https://discuss.codemirror.net/t/addoverlay-removeoverlay-and-opaque/835 --- doc/manual.html | 20 +++++++++++++++----- lib/codemirror.js | 12 +++++++++++- 2 files changed, 26 insertions(+), 6 deletions(-) diff --git a/doc/manual.html b/doc/manual.html index b912a78f..8da9cbb2 100644 --- a/doc/manual.html +++ b/doc/manual.html @@ -1435,11 +1435,21 @@

    Configuration methods

    spec or a mode object (an object with a token method). The options parameter is optional. If given, it - should be an object. Currently, only the opaque - option is recognized. This defaults to off, but can be given to - allow the overlay styling, when not null, to - override the styling of the base mode entirely, instead of the - two being applied together. + should be an object, optionally containing the following options: +
    +
    opaque: bool
    +
    Defaults to off, but can be given to allow the overlay + styling, when not null, to override the styling of + the base mode entirely, instead of the two being applied + together.
    +
    priority: number
    +
    Determines the ordering in which the overlays are + applied. Those with high priority are applied after those + with lower priority, and able to override the opaqueness of + the ones that come before. Defaults to 0.
    +
    + +
    cm.removeOverlay(mode: string|object)
    Pass this the exact value passed for the mode parameter to addOverlay, diff --git a/lib/codemirror.js b/lib/codemirror.js index 0d34b852..b36b3697 100644 --- a/lib/codemirror.js +++ b/lib/codemirror.js @@ -4952,7 +4952,10 @@ addOverlay: methodOp(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.state.overlays.push({mode: mode, modeSpec: spec, opaque: options && options.opaque}); + insertSorted(this.state.overlays, + {mode: mode, modeSpec: spec, opaque: options && options.opaque, + priority: (options && options.priority) || 0}, + function(overlay) { return overlay.priority }) this.state.modeGen++; regChange(this); }), @@ -8370,6 +8373,13 @@ return out; } + function insertSorted(array, value, score) { + console.log(array) + var pos = 0, priority = score(value) + while (pos < array.length && score(array[pos]) <= priority) pos++ + array.splice(pos, 0, value) + } + function nothing() {} function createObj(base, props) { From 41d73785ae1b0976b20dc27a6d5ae92cd3ca785e Mon Sep 17 00:00:00 2001 From: Marijn Haverbeke Date: Mon, 15 Aug 2016 13:41:45 +0200 Subject: [PATCH 0391/1790] Remove debug statement --- lib/codemirror.js | 1 - 1 file changed, 1 deletion(-) diff --git a/lib/codemirror.js b/lib/codemirror.js index b36b3697..1702dbe9 100644 --- a/lib/codemirror.js +++ b/lib/codemirror.js @@ -8374,7 +8374,6 @@ } function insertSorted(array, value, score) { - console.log(array) var pos = 0, priority = score(value) while (pos < array.length && score(array[pos]) <= priority) pos++ array.splice(pos, 0, value) From 08c34ebdde097c5aca49df530a2ff203727cdfa9 Mon Sep 17 00:00:00 2001 From: Marijn Haverbeke Date: Mon, 15 Aug 2016 13:46:20 +0200 Subject: [PATCH 0392/1790] [pug mode] Rename from Jade Closes #4166 --- doc/compress.html | 2 +- mode/index.html | 2 +- mode/meta.js | 2 +- mode/{jade => pug}/index.html | 16 ++++++++-------- mode/{jade/jade.js => pug/pug.js} | 5 +++-- 5 files changed, 14 insertions(+), 13 deletions(-) rename mode/{jade => pug}/index.html (83%) rename mode/{jade/jade.js => pug/pug.js} (99%) diff --git a/doc/compress.html b/doc/compress.html index 80de52da..3487d75e 100644 --- a/doc/compress.html +++ b/doc/compress.html @@ -166,7 +166,6 @@

    Script compression helper

    - @@ -193,6 +192,7 @@

    Script compression helper

    + diff --git a/mode/index.html b/mode/index.html index 732e0e52..3a2fe551 100644 --- a/mode/index.html +++ b/mode/index.html @@ -76,7 +76,6 @@

    Language modes

  • HTTP
  • IDL
  • Java
  • -
  • Jade
  • JavaScript (JSX)
  • Jinja2
  • Julia
  • @@ -107,6 +106,7 @@

    Language modes

  • PowerShell
  • Properties files
  • ProtoBuf
  • +
  • Pug
  • Puppet
  • Python
  • Q
  • diff --git a/mode/meta.js b/mode/meta.js index eb25e242..2e9ac7ce 100644 --- a/mode/meta.js +++ b/mode/meta.js @@ -66,7 +66,7 @@ {name: "HTML", mime: "text/html", mode: "htmlmixed", ext: ["html", "htm"], alias: ["xhtml"]}, {name: "HTTP", mime: "message/http", mode: "http"}, {name: "IDL", mime: "text/x-idl", mode: "idl", ext: ["pro"]}, - {name: "Jade", mime: "text/x-jade", mode: "jade", ext: ["jade"]}, + {name: "Pug", mime: "text/x-pug", mode: "pug", ext: ["jade", "pug"], alias: ["jade"]}, {name: "Java", mime: "text/x-java", mode: "clike", ext: ["java"]}, {name: "Java Server Pages", mime: "application/x-jsp", mode: "htmlembedded", ext: ["jsp"], alias: ["jsp"]}, {name: "JavaScript", mimes: ["text/javascript", "text/ecmascript", "application/javascript", "application/x-javascript", "application/ecmascript"], diff --git a/mode/jade/index.html b/mode/pug/index.html similarity index 83% rename from mode/jade/index.html rename to mode/pug/index.html index e534981b..1765853a 100644 --- a/mode/jade/index.html +++ b/mode/pug/index.html @@ -1,6 +1,6 @@ -CodeMirror: Jade Templating Mode +CodeMirror: Pug Templating Mode @@ -10,7 +10,7 @@ - +
    -

    Jade Templating Mode

    +

    Pug Templating Mode

    -

    The Jade Templating Mode

    +

    The Pug Templating Mode

    Created by Forbes Lindesay. Managed as part of a Brackets extension at https://github.com/ForbesLindesay/jade-brackets.

    -

    MIME type defined: text/x-jade.

    +

    MIME type defined: text/x-pug, text/x-jade.

    diff --git a/mode/jade/jade.js b/mode/pug/pug.js similarity index 99% rename from mode/jade/jade.js rename to mode/pug/pug.js index 51ed105a..40182336 100644 --- a/mode/jade/jade.js +++ b/mode/pug/pug.js @@ -11,7 +11,7 @@ })(function(CodeMirror) { "use strict"; -CodeMirror.defineMode('jade', function (config) { +CodeMirror.defineMode("pug", function (config) { // token types var KEYWORD = 'keyword'; var DOCTYPE = 'meta'; @@ -585,6 +585,7 @@ CodeMirror.defineMode('jade', function (config) { }; }, 'javascript', 'css', 'htmlmixed'); -CodeMirror.defineMIME('text/x-jade', 'jade'); +CodeMirror.defineMIME('text/x-pug', 'pug'); +CodeMirror.defineMIME('text/x-jade', 'pug'); }); From fafb7dc17d45d24ef5e84d3f61696ae9a4ea28fd Mon Sep 17 00:00:00 2001 From: Marijn Haverbeke Date: Tue, 16 Aug 2016 14:37:06 +0200 Subject: [PATCH 0393/1790] Improve paste handling on IE in contentEditable mode Issue #4091 --- lib/codemirror.js | 39 ++++++++++++++++++++++----------------- 1 file changed, 22 insertions(+), 17 deletions(-) diff --git a/lib/codemirror.js b/lib/codemirror.js index 1702dbe9..0d3b7ba5 100644 --- a/lib/codemirror.js +++ b/lib/codemirror.js @@ -1153,7 +1153,7 @@ } function handlePaste(e, cm) { - var pasted = e.clipboardData && e.clipboardData.getData("text/plain"); + var pasted = e.clipboardData && e.clipboardData.getData("Text"); if (pasted) { e.preventDefault(); if (!cm.isReadOnly() && !cm.options.disableInput) @@ -1581,7 +1581,9 @@ disableBrowserMagic(div, cm.options.spellcheck); on(div, "paste", function(e) { - if (!signalDOMEvent(cm, e)) handlePaste(e, cm); + if (signalDOMEvent(cm, e) || handlePaste(e, cm)) return + // IE doesn't fire input events, so we schedule a read for the pasted content in this way + if (ie_version <= 11) setTimeout(operation(cm, function() {regChange(cm);}), 20) }) on(div, "compositionstart", function(e) { @@ -1641,23 +1643,26 @@ }); } } - // iOS exposes the clipboard API, but seems to discard content inserted into it - if (e.clipboardData && !ios) { - e.preventDefault(); + if (e.clipboardData) { e.clipboardData.clearData(); - e.clipboardData.setData("text/plain", lastCopied.text.join("\n")); - } else { - // Old-fashioned briefly-focus-a-textarea hack - var kludge = hiddenTextarea(), te = kludge.firstChild; - cm.display.lineSpace.insertBefore(kludge, cm.display.lineSpace.firstChild); - te.value = lastCopied.text.join("\n"); - var hadFocus = document.activeElement; - selectInput(te); - setTimeout(function() { - cm.display.lineSpace.removeChild(kludge); - hadFocus.focus(); - }, 50); + var content = lastCopied.text.join("\n") + // iOS exposes the clipboard API, but seems to discard content inserted into it + e.clipboardData.setData("Text", content); + if (e.clipboardData.getData("Text") == content) { + e.preventDefault(); + return + } } + // Old-fashioned briefly-focus-a-textarea hack + var kludge = hiddenTextarea(), te = kludge.firstChild; + cm.display.lineSpace.insertBefore(kludge, cm.display.lineSpace.firstChild); + te.value = lastCopied.text.join("\n"); + var hadFocus = document.activeElement; + selectInput(te); + setTimeout(function() { + cm.display.lineSpace.removeChild(kludge); + hadFocus.focus(); + }, 50); } on(div, "copy", onCopyCut); on(div, "cut", onCopyCut); From a2b3204e31aef62fd9fb46b061d763e07616440d Mon Sep 17 00:00:00 2001 From: Marijn Haverbeke Date: Wed, 17 Aug 2016 14:15:09 +0200 Subject: [PATCH 0394/1790] [match-highlighter addon] Don't start highlighting until the editor is focused Issue #4162 --- addon/search/match-highlighter.js | 23 +++++++++++++++++++++-- 1 file changed, 21 insertions(+), 2 deletions(-) diff --git a/addon/search/match-highlighter.js b/addon/search/match-highlighter.js index 2121de41..73ba0e05 100644 --- a/addon/search/match-highlighter.js +++ b/addon/search/match-highlighter.js @@ -45,6 +45,7 @@ this.options[name] = (options && options.hasOwnProperty(name) ? options : defaults)[name] this.overlay = this.timeout = null; this.matchesonscroll = null; + this.active = false; } CodeMirror.defineOption("highlightSelectionMatches", false, function(cm, val, old) { @@ -53,16 +54,34 @@ clearTimeout(cm.state.matchHighlighter.timeout); cm.state.matchHighlighter = null; cm.off("cursorActivity", cursorActivity); + cm.off("focus", onFocus) } if (val) { - cm.state.matchHighlighter = new State(val); - highlightMatches(cm); + var state = cm.state.matchHighlighter = new State(val); + if (cm.hasFocus()) { + state.active = true + highlightMatches(cm) + } else { + cm.on("focus", onFocus) + } cm.on("cursorActivity", cursorActivity); } }); function cursorActivity(cm) { var state = cm.state.matchHighlighter; + if (state.active || cm.hasFocus()) scheduleHighlight(cm, state) + } + + function onFocus(cm) { + var state = cm.state.matchHighlighter + if (!state.active) { + state.active = true + scheduleHighlight(cm, state) + } + } + + function scheduleHighlight(cm, state) { clearTimeout(state.timeout); state.timeout = setTimeout(function() {highlightMatches(cm);}, state.options.delay); } From 476b06e70aa524981c4c57a8c2b7728bbecb7ba3 Mon Sep 17 00:00:00 2001 From: Marijn Haverbeke Date: Wed, 17 Aug 2016 16:36:28 +0200 Subject: [PATCH 0395/1790] [tern demo] Adjust to change in Tern Closes #4172 --- demo/tern.html | 6 +++--- 1 file changed, 3 insertions(+), 3 deletions(-) diff --git a/demo/tern.html b/demo/tern.html index b7b65608..d0dee8a7 100644 --- a/demo/tern.html +++ b/demo/tern.html @@ -1,4 +1,4 @@ - + CodeMirror: Tern Demo @@ -109,8 +109,8 @@

    Tern Demo

    } var server; - getURL("//ternjs.net/defs/ecma5.json", function(err, code) { - if (err) throw new Error("Request for ecma5.json: " + err); + getURL("//ternjs.net/defs/ecmascript.json", function(err, code) { + if (err) throw new Error("Request for ecmascript.json: " + err); server = new CodeMirror.TernServer({defs: [JSON.parse(code)]}); editor.setOption("extraKeys", { "Ctrl-Space": function(cm) { server.complete(cm); }, From f8c3009222c71c05e4614de7a770ac0b90b3d7a1 Mon Sep 17 00:00:00 2001 From: Marijn Haverbeke Date: Fri, 19 Aug 2016 10:38:35 +0200 Subject: [PATCH 0396/1790] Prevent clearing of selection after focus-textarea copy kludge --- lib/codemirror.js | 1 + 1 file changed, 1 insertion(+) diff --git a/lib/codemirror.js b/lib/codemirror.js index 0d3b7ba5..aa125b66 100644 --- a/lib/codemirror.js +++ b/lib/codemirror.js @@ -1662,6 +1662,7 @@ setTimeout(function() { cm.display.lineSpace.removeChild(kludge); hadFocus.focus(); + if (hadFocus == div) input.showPrimarySelection() }, 50); } on(div, "copy", onCopyCut); From 1fe94513f14a49cfcce17fc6a96fdf40bf1fb467 Mon Sep 17 00:00:00 2001 From: Marijn Haverbeke Date: Fri, 19 Aug 2016 10:39:19 +0200 Subject: [PATCH 0397/1790] Fix copy-pasted bug in locateNodeInLineView --- lib/codemirror.js | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/lib/codemirror.js b/lib/codemirror.js index aa125b66..292eda96 100644 --- a/lib/codemirror.js +++ b/lib/codemirror.js @@ -1971,7 +1971,7 @@ if (found) return badPos(Pos(found.line, found.ch + dist), bad); else - dist += after.textContent.length; + dist += before.textContent.length; } } From a4984139632e15e0d8d95acdafb268a73c1878a5 Mon Sep 17 00:00:00 2001 From: Marijn Haverbeke Date: Fri, 19 Aug 2016 10:39:49 +0200 Subject: [PATCH 0398/1790] Fix read-dom-on-paste workaround for IE Issue #4091 --- lib/codemirror.js | 4 +++- 1 file changed, 3 insertions(+), 1 deletion(-) diff --git a/lib/codemirror.js b/lib/codemirror.js index 292eda96..58c45f45 100644 --- a/lib/codemirror.js +++ b/lib/codemirror.js @@ -1583,7 +1583,9 @@ on(div, "paste", function(e) { if (signalDOMEvent(cm, e) || handlePaste(e, cm)) return // IE doesn't fire input events, so we schedule a read for the pasted content in this way - if (ie_version <= 11) setTimeout(operation(cm, function() {regChange(cm);}), 20) + if (ie_version <= 11) setTimeout(operation(cm, function() { + if (!input.pollContent()) regChange(cm); + }), 20) }) on(div, "compositionstart", function(e) { From bd1a7b621680de4b8ec2538168421d7104816ddc Mon Sep 17 00:00:00 2001 From: Marijn Haverbeke Date: Fri, 19 Aug 2016 10:47:40 +0200 Subject: [PATCH 0399/1790] [dialog addon] Improve docs --- doc/manual.html | 18 +++++++++--------- 1 file changed, 9 insertions(+), 9 deletions(-) diff --git a/doc/manual.html b/doc/manual.html index 8da9cbb2..40892d92 100644 --- a/doc/manual.html +++ b/doc/manual.html @@ -2188,23 +2188,23 @@

    Addons

    which, if called, will close the dialog immediately. openDialog takes the following options:
    -
    closeOnEnter:
    +
    closeOnEnter: bool
    If true, the dialog will be closed when the user presses enter in the input. Defaults to true.
    -
    onKeyDown:
    -
    An event handler of the signature (event, value, closeFunction) - that will be called whenever keydown fires in the +
    closeOnBlur: bool
    +
    Determines whether the dialog is closed when it loses focus. Defaults to true.
    +
    onKeyDown: fn(event: KeyboardEvent, value: string, close: fn()) → bool
    +
    An event handler that will be called whenever keydown fires in the dialog's input. If your callback returns true, the dialog will not do any further processing of the event.
    -
    onKeyUp:
    +
    onKeyUp: fn(event: KeyboardEvent, value: string, close: fn()) → bool
    Same as onKeyDown but for the keyup event.
    -
    onInput:
    +
    onInput: fn(event: InputEvent, value: string, close: fn()) → bool
    Same as onKeyDown but for the input event.
    -
    onClose:
    -
    A callback of the signature (dialogInstance) - that will be called after the dialog has been closed and +
    onClose: fn(instance):
    +
    A callback that will be called after the dialog has been closed and removed from the DOM. No return value.
    From 1c884ebabf690423e5988e6dde61c94ceb1433fc Mon Sep 17 00:00:00 2001 From: Sam Lee Date: Wed, 17 Aug 2016 17:34:53 -0700 Subject: [PATCH 0400/1790] [javascript hint] autocomplete non-enumerable properties, e.g., window.Promise --- addon/hint/javascript-hint.js | 42 ++++++++++++++++++++++++++++++++++- 1 file changed, 41 insertions(+), 1 deletion(-) diff --git a/addon/hint/javascript-hint.js b/addon/hint/javascript-hint.js index 7bcbf4a0..36d7dadf 100644 --- a/addon/hint/javascript-hint.js +++ b/addon/hint/javascript-hint.js @@ -103,10 +103,50 @@ if (str.lastIndexOf(start, 0) == 0 && !arrayContains(found, str)) found.push(str); } function gatherCompletions(obj) { + function gatherCompletionsOfTypeObj(obj) { + + /** + * Helper to get all properties of an object, including non-enumerable ones + * as completon candidates. + * + * In Chrome 51, most top-level classes, e.g., Event, Promise, etc., are not enumerable, + * therefore only available for javascript hint via this new code path. + * @see https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Global_Objects/Object/getOwnPropertyNames + */ + function getAllPropertyNames(obj) { + // Consider a generator-=like variation getAllPropertyNamesForEach(obj, fn), + // which takens additional callback fn (analous to Array.forEach) + // It has the advantage of avoiding names array build-up. + + var names = []; + // Traverse up prototype chain to get all properties (not just direct one) + // of obj. + for(var o = obj; o; o = Object.getPrototypeOf(o)) { + // concat() in place, plus more environment supports it + // Conceptually it is names.push(e[0], e[1], e[2], ...) + names.push.apply(names, Object.getOwnPropertyNames(o)); + } + return names; + } // function getAllPropertyNames(..) + + // a helper method to determine if the function is supported in the + // environment (really old browsers will not support it) + function getAllPropertyNamesIsSupported() { + return ((Object.getOwnPropertyNames && Object.getPrototypeOf) ? true : false); + }; // function getAllPropertyNamesIsSupported() + + if (getAllPropertyNamesIsSupported()) { + getAllPropertyNames(obj).forEach(maybeAdd); + } else { + // Old code path where non-enumerable properties will not be listed + for (var name in obj) maybeAdd(name); + } + } // function gatherCompletionsOfTypeObj(..) + if (typeof obj == "string") forEach(stringProps, maybeAdd); else if (obj instanceof Array) forEach(arrayProps, maybeAdd); else if (obj instanceof Function) forEach(funcProps, maybeAdd); - for (var name in obj) maybeAdd(name); + gatherCompletionsOfTypeObj(obj); } if (context && context.length) { From 5fea9c5737eaa19be5b7335603660e0ea1bb9945 Mon Sep 17 00:00:00 2001 From: Marijn Haverbeke Date: Fri, 19 Aug 2016 10:58:01 +0200 Subject: [PATCH 0401/1790] [javascript-hint] More concise coding style for property enumeration Issue #4175 --- addon/hint/javascript-hint.js | 51 +++++++---------------------------- 1 file changed, 10 insertions(+), 41 deletions(-) diff --git a/addon/hint/javascript-hint.js b/addon/hint/javascript-hint.js index 36d7dadf..d7088c19 100644 --- a/addon/hint/javascript-hint.js +++ b/addon/hint/javascript-hint.js @@ -97,56 +97,25 @@ 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 forAllProps(obj, callback) { + if (!Object.getOwnPropertyNames || !Object.getPrototypeOf) { + for (var name in obj) callback(name) + } else { + for (var o = obj; o; o = Object.getPrototypeOf(o)) + Object.getOwnPropertyNames(o).forEach(callback) + } + } + function getCompletions(token, context, keywords, options) { var found = [], start = token.string, global = options && options.globalScope || window; function maybeAdd(str) { if (str.lastIndexOf(start, 0) == 0 && !arrayContains(found, str)) found.push(str); } function gatherCompletions(obj) { - function gatherCompletionsOfTypeObj(obj) { - - /** - * Helper to get all properties of an object, including non-enumerable ones - * as completon candidates. - * - * In Chrome 51, most top-level classes, e.g., Event, Promise, etc., are not enumerable, - * therefore only available for javascript hint via this new code path. - * @see https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Global_Objects/Object/getOwnPropertyNames - */ - function getAllPropertyNames(obj) { - // Consider a generator-=like variation getAllPropertyNamesForEach(obj, fn), - // which takens additional callback fn (analous to Array.forEach) - // It has the advantage of avoiding names array build-up. - - var names = []; - // Traverse up prototype chain to get all properties (not just direct one) - // of obj. - for(var o = obj; o; o = Object.getPrototypeOf(o)) { - // concat() in place, plus more environment supports it - // Conceptually it is names.push(e[0], e[1], e[2], ...) - names.push.apply(names, Object.getOwnPropertyNames(o)); - } - return names; - } // function getAllPropertyNames(..) - - // a helper method to determine if the function is supported in the - // environment (really old browsers will not support it) - function getAllPropertyNamesIsSupported() { - return ((Object.getOwnPropertyNames && Object.getPrototypeOf) ? true : false); - }; // function getAllPropertyNamesIsSupported() - - if (getAllPropertyNamesIsSupported()) { - getAllPropertyNames(obj).forEach(maybeAdd); - } else { - // Old code path where non-enumerable properties will not be listed - for (var name in obj) maybeAdd(name); - } - } // function gatherCompletionsOfTypeObj(..) - if (typeof obj == "string") forEach(stringProps, maybeAdd); else if (obj instanceof Array) forEach(arrayProps, maybeAdd); else if (obj instanceof Function) forEach(funcProps, maybeAdd); - gatherCompletionsOfTypeObj(obj); + forAllProps(obj, maybeAdd) } if (context && context.length) { From 4f70169d9e9aa40c9e6d64a6721b47728ba361d8 Mon Sep 17 00:00:00 2001 From: Marijn Haverbeke Date: Fri, 19 Aug 2016 11:01:55 +0200 Subject: [PATCH 0402/1790] [python mode] Default to Python 3 --- mode/python/python.js | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/mode/python/python.js b/mode/python/python.js index be65ad76..4873a3e3 100644 --- a/mode/python/python.js +++ b/mode/python/python.js @@ -55,7 +55,7 @@ if (parserConf.extra_builtins != undefined) myBuiltins = myBuiltins.concat(parserConf.extra_builtins); - var py3 = parserConf.version && parseInt(parserConf.version, 10) == 3 + var py3 = parserConf.version && Number(parserConf.version) < 3 if (py3) { // since http://legacy.python.org/dev/peps/pep-0465/ @ is also an operator var singleOperators = parserConf.singleOperators || /^[\+\-\*\/%&|\^~<>!@]/; From acb20c0929b67062df989f7b3e24d777b487031f Mon Sep 17 00:00:00 2001 From: Marijn Haverbeke Date: Fri, 19 Aug 2016 11:08:16 +0200 Subject: [PATCH 0403/1790] [sas mode] Fix tokenizing of empty strings Closes #4178 --- mode/sas/sas.js | 7 +++---- 1 file changed, 3 insertions(+), 4 deletions(-) diff --git a/mode/sas/sas.js b/mode/sas/sas.js index fe114827..e88744be 100755 --- a/mode/sas/sas.js +++ b/mode/sas/sas.js @@ -137,10 +137,9 @@ stream.next(); return 'comment'; } - } else if (!state.continueString && (ch === '"' || ch === "'")) { - // Have we found a string? - state.continueString = ch; //save the matching quote in the state - return "string"; + } else if (ch === '"' || ch === "'") { + state.continueString = state.continueString == ch ? null : ch + return "string" } else if (state.continueString !== null) { if (stream.skipTo(state.continueString)) { // quote found on this line From 83e56a6783e1ec19609814d8c4cf4f73156d0575 Mon Sep 17 00:00:00 2001 From: Marijn Haverbeke Date: Fri, 19 Aug 2016 11:10:01 +0200 Subject: [PATCH 0404/1790] [python mode] Fix version detection Issue #4176 --- mode/python/python.js | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/mode/python/python.js b/mode/python/python.js index 4873a3e3..5142f970 100644 --- a/mode/python/python.js +++ b/mode/python/python.js @@ -55,7 +55,7 @@ if (parserConf.extra_builtins != undefined) myBuiltins = myBuiltins.concat(parserConf.extra_builtins); - var py3 = parserConf.version && Number(parserConf.version) < 3 + var py3 = !(parserConf.version && Number(parserConf.version) < 3) if (py3) { // since http://legacy.python.org/dev/peps/pep-0465/ @ is also an operator var singleOperators = parserConf.singleOperators || /^[\+\-\*\/%&|\^~<>!@]/; From f87597dd55df81585191771f74ebf9f514423d52 Mon Sep 17 00:00:00 2001 From: Marijn Haverbeke Date: Fri, 19 Aug 2016 11:13:53 +0200 Subject: [PATCH 0405/1790] [javascript mode] Parse class extends clause as a type in TypeScript mode Closes #4179 --- mode/javascript/javascript.js | 4 +--- 1 file changed, 1 insertion(+), 3 deletions(-) diff --git a/mode/javascript/javascript.js b/mode/javascript/javascript.js index 17f05045..df5f714f 100644 --- a/mode/javascript/javascript.js +++ b/mode/javascript/javascript.js @@ -1,8 +1,6 @@ // CodeMirror, 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) { if (typeof exports == "object" && typeof module == "object") // CommonJS mod(require("../../lib/codemirror")); @@ -610,7 +608,7 @@ CodeMirror.defineMode("javascript", function(config, parserConfig) { if (type == "variable") {register(value); return cont(classNameAfter);} } function classNameAfter(type, value) { - if (value == "extends") return cont(expression, classNameAfter); + if (value == "extends") return cont(isTS ? typeexpr : expression, classNameAfter); if (type == "{") return cont(pushlex("}"), classBody, poplex); } function classBody(type, value) { From c45674b11e990fe37abc662b0c507d3bb1f635e7 Mon Sep 17 00:00:00 2001 From: Thomas Kluyver Date: Fri, 19 Aug 2016 11:28:17 +0100 Subject: [PATCH 0406/1790] [python mode] Add f to delimiter stripping for Python mode So f-strings get terminated correctly --- mode/python/python.js | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/mode/python/python.js b/mode/python/python.js index 5142f970..efeed7f1 100644 --- a/mode/python/python.js +++ b/mode/python/python.js @@ -185,7 +185,7 @@ } function tokenStringFactory(delimiter) { - while ("rub".indexOf(delimiter.charAt(0).toLowerCase()) >= 0) + while ("rubf".indexOf(delimiter.charAt(0).toLowerCase()) >= 0) delimiter = delimiter.substr(1); var singleline = delimiter.length == 1; From 51e7bacb8bd14729c0e9da343e5403b4a57efd74 Mon Sep 17 00:00:00 2001 From: Marijn Haverbeke Date: Fri, 19 Aug 2016 23:11:03 +0200 Subject: [PATCH 0407/1790] [sas mode] Properly fix string tokenizing Issue #4178 --- mode/sas/sas.js | 10 ++++++---- 1 file changed, 6 insertions(+), 4 deletions(-) diff --git a/mode/sas/sas.js b/mode/sas/sas.js index e88744be..9ec22d5f 100755 --- a/mode/sas/sas.js +++ b/mode/sas/sas.js @@ -137,11 +137,13 @@ stream.next(); return 'comment'; } - } else if (ch === '"' || ch === "'") { - state.continueString = state.continueString == ch ? null : ch + } else if ((ch === '"' || ch === "'") && !state.continueString) { + state.continueString = ch return "string" - } else if (state.continueString !== null) { - if (stream.skipTo(state.continueString)) { + } else if (state.continueString) { + if (state.continueString == ch) { + state.continueString = null; + } else if (stream.skipTo(state.continueString)) { // quote found on this line stream.next(); state.continueString = null; From ee6368d2b8c0731c744f2eea4569964b463f2c2d Mon Sep 17 00:00:00 2001 From: Marijn Haverbeke Date: Fri, 19 Aug 2016 23:21:29 +0200 Subject: [PATCH 0408/1790] [sas mode] Fix and simplify case-insensitive keyword lookup Closes #4185 --- mode/sas/sas.js | 58 ++++++++++++++++++++++++------------------------- 1 file changed, 29 insertions(+), 29 deletions(-) diff --git a/mode/sas/sas.js b/mode/sas/sas.js index 9ec22d5f..a6109eb1 100755 --- a/mode/sas/sas.js +++ b/mode/sas/sas.js @@ -188,12 +188,12 @@ if (stream.peek() === '.') stream.skipTo(' '); state.nextword = false; return 'variable-2'; - } + word = word.toLowerCase() // Are we in a DATA Step? if (state.inDataStep) { - if (word.toLowerCase() === 'run;' || stream.match(/run\s;/)) { + if (word === 'run;' || stream.match(/run\s;/)) { state.inDataStep = false; return 'builtin'; } @@ -204,84 +204,84 @@ else return 'variable'; } // do we have a DATA Step keyword - if (word && words.hasOwnProperty(word.toLowerCase()) && - (words[word.toLowerCase()].state.indexOf("inDataStep") !== -1 || - words[word.toLowerCase()].state.indexOf("ALL") !== -1)) { + if (word && words.hasOwnProperty(word) && + (words[word].state.indexOf("inDataStep") !== -1 || + words[word].state.indexOf("ALL") !== -1)) { //backup to the start of the word if (stream.start < stream.pos) stream.backUp(stream.pos - stream.start); //advance the length of the word and return for (var i = 0; i < word.length; ++i) stream.next(); - return words[word.toLowerCase()].style; + return words[word].style; } } // Are we in an Proc statement? if (state.inProc) { - if (word.toLowerCase() === 'run;' || word.toLowerCase() === 'quit;') { + if (word === 'run;' || word === 'quit;') { state.inProc = false; return 'builtin'; } // do we have a proc keyword - if (word && words.hasOwnProperty(word.toLowerCase()) && - (words[word.toLowerCase()].state.indexOf("inProc") !== -1 || - words[word.toLowerCase()].state.indexOf("ALL") !== -1)) { + if (word && words.hasOwnProperty(word) && + (words[word].state.indexOf("inProc") !== -1 || + words[word].state.indexOf("ALL") !== -1)) { stream.match(/[\w]+/); return words[word].style; } } // Are we in a Macro statement? if (state.inMacro) { - if (word.toLowerCase() === '%mend') { + if (word === '%mend') { if (stream.peek() === ';') stream.next(); state.inMacro = false; return 'builtin'; } - if (word && words.hasOwnProperty(word.toLowerCase()) && - (words[word.toLowerCase()].state.indexOf("inMacro") !== -1 || - words[word.toLowerCase()].state.indexOf("ALL") !== -1)) { + if (word && words.hasOwnProperty(word) && + (words[word].state.indexOf("inMacro") !== -1 || + words[word].state.indexOf("ALL") !== -1)) { stream.match(/[\w]+/); - return words[word.toLowerCase()].style; + return words[word].style; } return 'atom'; } // Do we have Keywords specific words? - if (word && words.hasOwnProperty(word.toLowerCase())) { + if (word && words.hasOwnProperty(word)) { // Negates the initial next() stream.backUp(1); // Actually move the stream stream.match(/[\w]+/); - if (word.toLowerCase() === 'data' && /=/.test(stream.peek()) === false) { + if (word === 'data' && /=/.test(stream.peek()) === false) { state.inDataStep = true; state.nextword = true; return 'builtin'; } - if (word.toLowerCase() === 'proc') { + if (word === 'proc') { state.inProc = true; state.nextword = true; return 'builtin'; } - if (word.toLowerCase() === '%macro') { + if (word === '%macro') { state.inMacro = true; state.nextword = true; return 'builtin'; } - if (/title[1-9]/i.test(word)) return 'def'; + if (/title[1-9]/.test(word)) return 'def'; - if (word.toLowerCase() === 'footnote') { + if (word === 'footnote') { stream.eat(/[1-9]/); return 'def'; } // Returns their value as state in the prior define methods - if (state.inDataStep === true && words[word.toLowerCase()].state.indexOf("inDataStep") !== -1) - return words[word.toLowerCase()].style; - if (state.inProc === true && words[word.toLowerCase()].state.indexOf("inProc") !== -1) - return words[word.toLowerCase()].style; - if (state.inMacro === true && words[word.toLowerCase()].state.indexOf("inMacro") !== -1) - return words[word.toLowerCase()].style; - if (words[word.toLowerCase()].state.indexOf("ALL") !== -1) - return words[word.toLowerCase()].style; + if (state.inDataStep === true && words[word].state.indexOf("inDataStep") !== -1) + return words[word].style; + if (state.inProc === true && words[word].state.indexOf("inProc") !== -1) + return words[word].style; + if (state.inMacro === true && words[word].state.indexOf("inMacro") !== -1) + return words[word].style; + if (words[word].state.indexOf("ALL") !== -1) + return words[word].style; return null; } // Unrecognized syntax From e387d21d867759b73f817f7d19c4adc23a0931e2 Mon Sep 17 00:00:00 2001 From: Marijn Haverbeke Date: Mon, 22 Aug 2016 11:11:14 +0200 Subject: [PATCH 0409/1790] [javascript mode] Add two TypeScript tests Issue #4188 --- mode/javascript/javascript.js | 6 +++++- mode/javascript/test.js | 11 +++++++++++ test/mode_test.js | 1 + 3 files changed, 17 insertions(+), 1 deletion(-) diff --git a/mode/javascript/javascript.js b/mode/javascript/javascript.js index df5f714f..d7c5716b 100644 --- a/mode/javascript/javascript.js +++ b/mode/javascript/javascript.js @@ -528,7 +528,7 @@ CodeMirror.defineMode("javascript", function(config, parserConfig) { function typeexpr(type) { if (type == "variable") {cx.marked = "variable-3"; return cont(afterType);} if (type == "{") return cont(commasep(typeprop, "}")) - if (type == "(") return cont(commasep(typeprop, ")"), maybeReturnType) + if (type == "(") return cont(commasep(typearg, ")"), maybeReturnType) } function maybeReturnType(type) { if (type == "=>") return cont(typeexpr) @@ -541,6 +541,10 @@ CodeMirror.defineMode("javascript", function(config, parserConfig) { return cont(typeexpr) } } + function typearg(type) { + if (type == "variable") return cont(typearg) + else if (type == ":") return cont(typeexpr) + } function afterType(type, value) { if (value == "<") return cont(commasep(typeexpr, ">"), afterType) if (type == "[") return cont(expect("]"), afterType) diff --git a/mode/javascript/test.js b/mode/javascript/test.js index 8916b755..11a13568 100644 --- a/mode/javascript/test.js +++ b/mode/javascript/test.js @@ -167,6 +167,17 @@ " }", "}"); + var ts_mode = CodeMirror.getMode({indentUnit: 2}, "application/typescript") + function TS(name) { + test.mode(name, ts_mode, Array.prototype.slice.call(arguments, 1)) + } + + TS("extend_type", + "[keyword class] [def Foo] [keyword extends] [variable-3 Some][operator <][variable-3 Type][operator >] {}") + + TS("arrow_type", + "[keyword let] [def x]: ([variable arg]: [variable-3 Type]) [operator =>] [variable-3 ReturnType]") + var jsonld_mode = CodeMirror.getMode( {indentUnit: 2}, {name: "javascript", jsonld: true} diff --git a/test/mode_test.js b/test/mode_test.js index 0aed50f7..f706c4f5 100644 --- a/test/mode_test.js +++ b/test/mode_test.js @@ -61,6 +61,7 @@ test.mode = function(name, mode, tokens, modeName) { var data = parseTokens(tokens); + if (name == "extend_type") console.log("set", (modeName || mode.name) + "_" + name) return test((modeName || mode.name) + "_" + name, function() { return compare(data.plain, data.tokens, mode); }); From 751fd52c3e16f10739a0df54769b8b44a836b35b Mon Sep 17 00:00:00 2001 From: Marijn Haverbeke Date: Mon, 22 Aug 2016 15:45:37 +0200 Subject: [PATCH 0410/1790] Mark release 5.18.0 --- AUTHORS | 5 +++++ CHANGELOG.md | 28 ++++++++++++++++++++++++++++ doc/compress.html | 1 + doc/manual.html | 2 +- doc/releases.html | 15 +++++++++++++++ index.html | 2 +- lib/codemirror.js | 2 +- package.json | 2 +- 8 files changed, 53 insertions(+), 4 deletions(-) diff --git a/AUTHORS b/AUTHORS index 6a96b87c..0f2bc1bb 100644 --- a/AUTHORS +++ b/AUTHORS @@ -98,6 +98,7 @@ Brian Sletten Bruce Mitchener Caitlin Potter Calin Barbat +callodacity Camilo Roca Chad Jolly Chandra Sekhar Pydi @@ -180,6 +181,7 @@ feizhang365 Felipe Lalanne Felix Raab Filip Noetzel +Filip Stollár flack ForbesLindesay Forbes Lindesay @@ -271,6 +273,7 @@ Jim JobJob jochenberger Jochen Berger +Joel Einbinder joelpinheiro Johan Ask John Connor @@ -485,6 +488,7 @@ Rrandom Ruslan Osmanov Ryan Prior sabaca +Sam Lee Samuel Ainsworth Sam Wilson sandeepshetty @@ -542,6 +546,7 @@ thanasis TheHowl think Thomas Dvornik +Thomas Kluyver Thomas Schmid Tim Alby Tim Baumann diff --git a/CHANGELOG.md b/CHANGELOG.md index 55ace8ff..5e1973fb 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -1,3 +1,31 @@ +## 5.18.0 (2016-08-22) + +### Bugfixes + +Make sure [gutter backgrounds](http://codemirror.net/doc/manual.html#addLineClass) stick to the rest of the gutter during horizontal scrolling. + +The contenteditable [`inputStyle`](http://codemirror.net/doc/manual.html#option_inputStyle) now properly supports pasting on pre-Edge IE versions. + +[javascript mode](http://codemirror.net/mode/javascript): Fix some small parsing bugs and improve TypeScript support. + +[matchbrackets addon](http://codemirror.net/doc/manual.html#addon_matchbrackets): Fix bug where active highlighting was left in editor when the addon was disabled. + +[match-highlighter addon](http://codemirror.net/doc/manual.html#addon_match-highlighter): Only start highlighting things when the editor gains focus. + +[javascript-hint addon](http://codemirror.net/doc/manual.html#addon_javascript-hint): Also complete non-enumerable properties. + +### New features + +The [`addOverlay`](http://codemirror.net/doc/manual.html#addOverlay) method now supports a `priority` option to control the order in which overlays are applied. + +MIME types that end in `+json` now default to the JSON mode when the MIME itself is not defined. + +### Breaking changes + +The mode formerly known as Jade was renamed to [Pug](http://codemirror.net/mode/pug). + +The [Python mode](http://codemirror.net/mode/python) now defaults to Python 3 (rather than 2) syntax. + ## 5.17.0 (2016-07-19) ### Bugfixes diff --git a/doc/compress.html b/doc/compress.html index 3487d75e..1cded19a 100644 --- a/doc/compress.html +++ b/doc/compress.html @@ -36,6 +36,7 @@

    Script compression helper

    Version:

    Version:

    Version:

    From 562e8eff5b0916d3b63fc59eda9540f8f455c6ed Mon Sep 17 00:00:00 2001 From: Luke Browning Date: Thu, 22 Sep 2016 14:55:40 +0100 Subject: [PATCH 0441/1790] [sql mode] Add exec keyword to mssql --- 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 01ebd80a..e3cbae54 100644 --- a/mode/sql/sql.js +++ b/mode/sql/sql.js @@ -280,7 +280,7 @@ CodeMirror.defineMode("sql", function(config, parserConfig) { CodeMirror.defineMIME("text/x-mssql", { 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 + "begin trigger proc view index for add constraint key primary foreign collate clustered nonclustered declare"), + keywords: set(sqlKeywords + "begin trigger proc view index for add constraint key primary foreign collate clustered nonclustered declare exec"), builtin: set("bigint numeric bit smallint decimal smallmoney int tinyint money float real char varchar text nchar nvarchar ntext binary varbinary image cursor timestamp hierarchyid uniqueidentifier sql_variant xml table "), atoms: set("false true null unknown"), operatorChars: /^[*+\-%<>!=]/, From e15e2d5e50cce51e940f3a66cb03044dfd935bad Mon Sep 17 00:00:00 2001 From: Marijn Haverbeke Date: Sat, 24 Sep 2016 15:52:45 +0200 Subject: [PATCH 0442/1790] Ignore keypress events for backspace Closes #4253 --- lib/codemirror.js | 2 ++ 1 file changed, 2 insertions(+) diff --git a/lib/codemirror.js b/lib/codemirror.js index 3798e5ec..ececc94c 100644 --- a/lib/codemirror.js +++ b/lib/codemirror.js @@ -4246,6 +4246,8 @@ if (presto && keyCode == lastStoppedKey) {lastStoppedKey = null; e_preventDefault(e); return;} if ((presto && (!e.which || e.which < 10)) && handleKeyBinding(cm, e)) return; var ch = String.fromCharCode(charCode == null ? keyCode : charCode); + // Some browsers fire keypress events for backspace + if (ch == "\x08") return; if (handleCharBinding(cm, e, ch)) return; cm.display.input.onKeyPress(e); } From 383c40e468319d0e98e86e7c9a6dcfbe256c8476 Mon Sep 17 00:00:00 2001 From: Marijn Haverbeke Date: Mon, 26 Sep 2016 12:59:43 +0200 Subject: [PATCH 0443/1790] [javascript mode] Fix indentation in TypeScript demo --- mode/javascript/typescript.html | 16 ++++++++-------- 1 file changed, 8 insertions(+), 8 deletions(-) diff --git a/mode/javascript/typescript.html b/mode/javascript/typescript.html index 2cfc5381..1f26d7fe 100644 --- a/mode/javascript/typescript.html +++ b/mode/javascript/typescript.html @@ -28,13 +28,13 @@

    TypeScript mode

    - - - - - diff --git a/doc/manual.html b/doc/manual.html index 8a0e2dcb..e97ce07e 100644 --- a/doc/manual.html +++ b/doc/manual.html @@ -96,8 +96,7 @@

    Basic Usage

    The easiest way to use CodeMirror is to simply load the script and style sheet found under lib/ in the distribution, plus a mode script from one of the mode/ directories. - (See the compression helper for an - easy way to combine scripts.) For example:

    + For example:

    <script src="lib/codemirror.js"></script>
     <link rel="stylesheet" href="lib/codemirror.css">
    diff --git a/index.html b/index.html
    index 636639ec..3423eab2 100644
    --- a/index.html
    +++ b/index.html
    @@ -97,9 +97,9 @@ 

    This is CodeMirror

    Get the current version: 5.19.0.
    - You can see the code or
    - read the release notes.
    - There is a minification helper. + You can see the code,
    + read the release notes,
    + or study the user manual.
    Software needs maintenance,
    From 7955eadf723a9b6420fc4c822436bed1d8dd0203 Mon Sep 17 00:00:00 2001 From: Marijn Haverbeke Date: Mon, 26 Sep 2016 16:52:17 +0200 Subject: [PATCH 0450/1790] Add missing import Closes #4258 --- src/edit/mouse_events.js | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/edit/mouse_events.js b/src/edit/mouse_events.js index 08a564bd..ebda0c28 100644 --- a/src/edit/mouse_events.js +++ b/src/edit/mouse_events.js @@ -5,7 +5,7 @@ import { clipPos, cmp, maxPos, minPos, Pos } from "../line/pos"; import { getLine, lineAtHeight } from "../line/utils_line"; import { posFromMouse } from "../measurement/position_measurement"; import { eventInWidget } from "../measurement/widgets"; -import { normalizeSelection, Range } from "../model/selection"; +import { normalizeSelection, Range, Selection } from "../model/selection"; import { extendRange, extendSelection, replaceOneSelection, setSelection } from "../model/selection_updates"; import { captureRightClick, chromeOS, ie, ie_version, mac, webkit } from "../util/browser"; import { activeElt } from "../util/dom"; From ac205e6783c631cf42a6e4771216d790f1a70e10 Mon Sep 17 00:00:00 2001 From: Adrian Heine Date: Mon, 26 Sep 2016 09:55:06 +0200 Subject: [PATCH 0451/1790] Remove semicolons --- package.json | 2 +- src/codemirror.js | 4 +- src/display/Display.js | 98 ++-- src/display/focus.js | 48 +- src/display/gutters.js | 30 +- src/display/highlight_worker.js | 60 +-- src/display/line_numbers.js | 50 +- src/display/mode_state.js | 24 +- src/display/operations.js | 180 ++++---- src/display/scroll_events.js | 124 ++--- src/display/scrollbars.js | 178 ++++---- src/display/scrolling.js | 118 ++--- src/display/selection.js | 152 +++---- src/display/update_display.js | 228 +++++----- src/display/update_lines.js | 58 +-- src/display/view_tracking.js | 138 +++--- src/edit/CodeMirror.js | 242 +++++----- src/edit/commands.js | 230 +++++----- src/edit/deleteNearSelection.js | 28 +- src/edit/drop_events.js | 126 ++--- src/edit/fromTextArea.js | 64 +-- src/edit/global_events.js | 44 +- src/edit/key_events.js | 150 +++--- src/edit/legacy.js | 116 ++--- src/edit/main.js | 66 +-- src/edit/methods.js | 582 ++++++++++++------------ src/edit/mouse_events.js | 320 ++++++------- src/edit/options.js | 276 +++++------ src/edit/utils.js | 6 +- src/input/ContentEditableInput.js | 468 +++++++++---------- src/input/TextareaInput.js | 322 ++++++------- src/input/indent.js | 72 +-- src/input/input.js | 124 ++--- src/input/keymap.js | 114 ++--- src/input/keynames.js | 8 +- src/line/highlight.js | 202 ++++---- src/line/line_data.js | 304 ++++++------- src/line/pos.js | 36 +- src/line/saw_special_spans.js | 6 +- src/line/spans.js | 310 ++++++------- src/line/utils_line.js | 76 ++-- src/measurement/position_measurement.js | 532 +++++++++++----------- src/measurement/update_line.js | 178 ++++---- src/measurement/widgets.js | 22 +- src/model/Doc.js | 436 +++++++++--------- src/model/change_measurement.js | 54 +-- src/model/changes.js | 300 ++++++------ src/model/chunk.js | 154 +++---- src/model/document_data.js | 102 ++--- src/model/history.js | 174 +++---- src/model/line_widget.js | 92 ++-- src/model/mark_text.js | 284 ++++++------ src/model/selection.js | 68 +-- src/model/selection_updates.js | 156 +++---- src/modes.js | 84 ++-- src/util/StringStream.js | 84 ++-- src/util/bidi.js | 216 ++++----- src/util/browser.js | 48 +- src/util/dom.js | 98 ++-- src/util/event.js | 72 +-- src/util/feature_detection.js | 98 ++-- src/util/misc.js | 102 ++--- src/util/operation_group.js | 52 +-- 63 files changed, 4595 insertions(+), 4595 deletions(-) diff --git a/package.json b/package.json index 1f4b036e..a6e61d06 100644 --- a/package.json +++ b/package.json @@ -15,7 +15,7 @@ "lint": "bin/lint" }, "devDependencies": { - "blint": ">=0.1.1", + "blint": "^0.5.1", "node-static": "0.6.0", "phantomjs-prebuilt": "^2.1.12", "rollup": "^0.34.10", diff --git a/src/codemirror.js b/src/codemirror.js index 4b75d8cb..3c16cc87 100644 --- a/src/codemirror.js +++ b/src/codemirror.js @@ -1,3 +1,3 @@ -import { CodeMirror } from "./edit/main"; +import { CodeMirror } from "./edit/main" -export default CodeMirror; +export default CodeMirror diff --git a/src/display/Display.js b/src/display/Display.js index e1cb6a09..fa4c52fe 100644 --- a/src/display/Display.js +++ b/src/display/Display.js @@ -1,105 +1,105 @@ -import { gecko, ie, ie_version, mobile, webkit } from "../util/browser"; -import { elt } from "../util/dom"; -import { scrollerGap } from "../util/misc"; +import { gecko, ie, ie_version, mobile, webkit } from "../util/browser" +import { elt } from "../util/dom" +import { scrollerGap } from "../util/misc" // The display handles the DOM integration, both for input reading // and content drawing. It holds references to DOM nodes and // display-related state. export function Display(place, doc, input) { - var d = this; - this.input = input; + var d = this + this.input = input // Covers bottom-right square when both scrollbars are present. - d.scrollbarFiller = elt("div", null, "CodeMirror-scrollbar-filler"); - d.scrollbarFiller.setAttribute("cm-not-content", "true"); + d.scrollbarFiller = elt("div", null, "CodeMirror-scrollbar-filler") + d.scrollbarFiller.setAttribute("cm-not-content", "true") // Covers bottom of gutter when coverGutterNextToScrollbar is on // and h scrollbar is present. - d.gutterFiller = elt("div", null, "CodeMirror-gutter-filler"); - d.gutterFiller.setAttribute("cm-not-content", "true"); + d.gutterFiller = elt("div", null, "CodeMirror-gutter-filler") + d.gutterFiller.setAttribute("cm-not-content", "true") // Will contain the actual code, positioned to cover the viewport. - d.lineDiv = elt("div", null, "CodeMirror-code"); + d.lineDiv = elt("div", null, "CodeMirror-code") // Elements are added to these to represent selection and cursors. - d.selectionDiv = elt("div", null, null, "position: relative; z-index: 1"); - d.cursorDiv = elt("div", null, "CodeMirror-cursors"); + d.selectionDiv = elt("div", null, null, "position: relative; z-index: 1") + d.cursorDiv = elt("div", null, "CodeMirror-cursors") // A visibility: hidden element used to find the size of things. - d.measure = elt("div", null, "CodeMirror-measure"); + d.measure = elt("div", null, "CodeMirror-measure") // When lines outside of the viewport are measured, they are drawn in this. - d.lineMeasure = elt("div", null, "CodeMirror-measure"); + d.lineMeasure = elt("div", null, "CodeMirror-measure") // Wraps everything that needs to exist inside the vertically-padded coordinate system d.lineSpace = elt("div", [d.measure, d.lineMeasure, d.selectionDiv, d.cursorDiv, d.lineDiv], - null, "position: relative; outline: none"); + 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.mover = elt("div", [elt("div", [d.lineSpace], "CodeMirror-lines")], null, "position: relative") // Set to the height of the document, allowing scrolling. - d.sizer = elt("div", [d.mover], "CodeMirror-sizer"); - d.sizerWidth = null; + d.sizer = elt("div", [d.mover], "CodeMirror-sizer") + d.sizerWidth = null // Behavior of elts with overflow: auto and padding is // inconsistent across browsers. This is used to ensure the // scrollable area is big enough. - d.heightForcer = elt("div", null, null, "position: absolute; height: " + scrollerGap + "px; width: 1px;"); + d.heightForcer = elt("div", null, null, "position: absolute; height: " + scrollerGap + "px; width: 1px;") // Will contain the gutters, if any. - d.gutters = elt("div", null, "CodeMirror-gutters"); - d.lineGutter = null; + d.gutters = elt("div", null, "CodeMirror-gutters") + d.lineGutter = null // Actual scrollable element. - d.scroller = elt("div", [d.sizer, d.heightForcer, d.gutters], "CodeMirror-scroll"); - d.scroller.setAttribute("tabIndex", "-1"); + 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.scrollbarFiller, d.gutterFiller, d.scroller], "CodeMirror"); + d.wrapper = elt("div", [d.scrollbarFiller, d.gutterFiller, d.scroller], "CodeMirror") // Work around IE7 z-index bug (not perfect, hence IE7 not really being supported) - if (ie && ie_version < 8) { d.gutters.style.zIndex = -1; d.scroller.style.paddingRight = 0; } - if (!webkit && !(gecko && mobile)) d.scroller.draggable = true; + if (ie && ie_version < 8) { d.gutters.style.zIndex = -1; d.scroller.style.paddingRight = 0 } + if (!webkit && !(gecko && mobile)) d.scroller.draggable = true if (place) { - if (place.appendChild) place.appendChild(d.wrapper); - else place(d.wrapper); + if (place.appendChild) place.appendChild(d.wrapper) + else place(d.wrapper) } // Current rendered range (may be bigger than the view window). - d.viewFrom = d.viewTo = doc.first; - d.reportedViewFrom = d.reportedViewTo = doc.first; + d.viewFrom = d.viewTo = doc.first + d.reportedViewFrom = d.reportedViewTo = doc.first // Information about the rendered lines. - d.view = []; - d.renderedView = null; + d.view = [] + d.renderedView = null // Holds info about a single rendered line when it was rendered // for measurement, while not in view. - d.externalMeasured = null; + d.externalMeasured = null // Empty space (in pixels) above the view - d.viewOffset = 0; - d.lastWrapHeight = d.lastWrapWidth = 0; - d.updateLineNumbers = null; + d.viewOffset = 0 + d.lastWrapHeight = d.lastWrapWidth = 0 + d.updateLineNumbers = null - d.nativeBarWidth = d.barHeight = d.barWidth = 0; - d.scrollbarsClipped = false; + d.nativeBarWidth = d.barHeight = d.barWidth = 0 + d.scrollbarsClipped = false // 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.lineNumInnerWidth = d.lineNumChars = null; + d.lineNumWidth = d.lineNumInnerWidth = d.lineNumChars = null // Set to true when a non-horizontal-scrolling line widget is // added. As an optimization, line widget aligning is skipped when // this is false. - d.alignWidgets = false; + d.alignWidgets = false - d.cachedCharWidth = d.cachedTextHeight = d.cachedPaddingH = null; + d.cachedCharWidth = d.cachedTextHeight = d.cachedPaddingH = null // 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; + d.maxLine = null + d.maxLineLength = 0 + d.maxLineChanged = false // Used for measuring wheel scrolling granularity - d.wheelDX = d.wheelDY = d.wheelStartX = d.wheelStartY = null; + d.wheelDX = d.wheelDY = d.wheelStartX = d.wheelStartY = null // True when shift is held down. - d.shift = false; + d.shift = false // Used to track whether anything happened since the context menu // was opened. - d.selForContextMenu = null; + d.selForContextMenu = null - d.activeTouch = null; + d.activeTouch = null - input.init(d); + input.init(d) } diff --git a/src/display/focus.js b/src/display/focus.js index abe70f09..e6a9f521 100644 --- a/src/display/focus.js +++ b/src/display/focus.js @@ -1,49 +1,49 @@ -import { restartBlink } from "./selection"; -import { webkit } from "../util/browser"; -import { addClass, rmClass } from "../util/dom"; -import { signal } from "../util/event"; +import { restartBlink } from "./selection" +import { webkit } from "../util/browser" +import { addClass, rmClass } from "../util/dom" +import { signal } from "../util/event" export function ensureFocus(cm) { - if (!cm.state.focused) { cm.display.input.focus(); onFocus(cm); } + if (!cm.state.focused) { cm.display.input.focus(); onFocus(cm) } } export function delayBlurEvent(cm) { - cm.state.delayingBlurEvent = true; + cm.state.delayingBlurEvent = true setTimeout(function() { if (cm.state.delayingBlurEvent) { - cm.state.delayingBlurEvent = false; - onBlur(cm); + cm.state.delayingBlurEvent = false + onBlur(cm) } - }, 100); + }, 100) } export function onFocus(cm, e) { - if (cm.state.delayingBlurEvent) cm.state.delayingBlurEvent = false; + if (cm.state.delayingBlurEvent) cm.state.delayingBlurEvent = false - if (cm.options.readOnly == "nocursor") return; + if (cm.options.readOnly == "nocursor") return if (!cm.state.focused) { - signal(cm, "focus", cm, e); - cm.state.focused = true; - addClass(cm.display.wrapper, "CodeMirror-focused"); + signal(cm, "focus", cm, e) + cm.state.focused = true + addClass(cm.display.wrapper, "CodeMirror-focused") // This test prevents this from firing when a context // menu is closed (since the input reset would kill the // select-all detection hack) if (!cm.curOp && cm.display.selForContextMenu != cm.doc.sel) { - cm.display.input.reset(); - if (webkit) setTimeout(function() { cm.display.input.reset(true); }, 20); // Issue #1730 + cm.display.input.reset() + if (webkit) setTimeout(function() { cm.display.input.reset(true) }, 20) // Issue #1730 } - cm.display.input.receivedFocus(); + cm.display.input.receivedFocus() } - restartBlink(cm); + restartBlink(cm) } export function onBlur(cm, e) { - if (cm.state.delayingBlurEvent) return; + if (cm.state.delayingBlurEvent) return if (cm.state.focused) { - signal(cm, "blur", cm, e); - cm.state.focused = false; - rmClass(cm.display.wrapper, "CodeMirror-focused"); + signal(cm, "blur", cm, e) + cm.state.focused = false + rmClass(cm.display.wrapper, "CodeMirror-focused") } - clearInterval(cm.display.blinker); - setTimeout(function() {if (!cm.state.focused) cm.display.shift = false;}, 150); + clearInterval(cm.display.blinker) + setTimeout(function() {if (!cm.state.focused) cm.display.shift = false}, 150) } diff --git a/src/display/gutters.js b/src/display/gutters.js index 0a0f591a..3fe9a134 100644 --- a/src/display/gutters.js +++ b/src/display/gutters.js @@ -1,33 +1,33 @@ -import { elt, removeChildren } from "../util/dom"; -import { indexOf } from "../util/misc"; +import { elt, removeChildren } from "../util/dom" +import { indexOf } from "../util/misc" -import { updateGutterSpace } from "./update_display"; +import { updateGutterSpace } from "./update_display" // Rebuild the gutter elements, ensure the margin to the left of the // code matches their width. export function updateGutters(cm) { - var gutters = cm.display.gutters, specs = cm.options.gutters; - removeChildren(gutters); + 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)); + 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"; + cm.display.lineGutter = gElt + gElt.style.width = (cm.display.lineNumWidth || 1) + "px" } } - gutters.style.display = i ? "" : "none"; - updateGutterSpace(cm); + gutters.style.display = i ? "" : "none" + updateGutterSpace(cm) } // Make sure the gutters options contains the element // "CodeMirror-linenumbers" when the lineNumbers option is true. export function setGuttersForLineNumbers(options) { - var found = indexOf(options.gutters, "CodeMirror-linenumbers"); + var found = indexOf(options.gutters, "CodeMirror-linenumbers") if (found == -1 && options.lineNumbers) { - options.gutters = options.gutters.concat(["CodeMirror-linenumbers"]); + options.gutters = options.gutters.concat(["CodeMirror-linenumbers"]) } else if (found > -1 && !options.lineNumbers) { - options.gutters = options.gutters.slice(0); - options.gutters.splice(found, 1); + options.gutters = options.gutters.slice(0) + options.gutters.splice(found, 1) } } diff --git a/src/display/highlight_worker.js b/src/display/highlight_worker.js index 101b9acb..b8a48c0e 100644 --- a/src/display/highlight_worker.js +++ b/src/display/highlight_worker.js @@ -1,51 +1,51 @@ -import { getStateBefore, highlightLine, processLine } from "../line/highlight"; -import { copyState } from "../modes"; -import { bind } from "../util/misc"; +import { getStateBefore, highlightLine, processLine } from "../line/highlight" +import { copyState } from "../modes" +import { bind } from "../util/misc" -import { runInOp } from "./operations"; -import { regLineChange } from "./view_tracking"; +import { runInOp } from "./operations" +import { regLineChange } from "./view_tracking" // HIGHLIGHT WORKER export function startWorker(cm, time) { if (cm.doc.mode.startState && cm.doc.frontier < cm.display.viewTo) - cm.state.highlight.set(time, bind(highlightWorker, cm)); + cm.state.highlight.set(time, bind(highlightWorker, cm)) } function highlightWorker(cm) { - var doc = cm.doc; - if (doc.frontier < doc.first) doc.frontier = doc.first; - 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 = []; + var doc = cm.doc + if (doc.frontier < doc.first) doc.frontier = doc.first + 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 = [] 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, tooLong = line.text.length > cm.options.maxHighlightLength; - var highlighted = highlightLine(cm, line, tooLong ? copyState(doc.mode, state) : state, true); - line.styles = highlighted.styles; - var oldCls = line.styleClasses, newCls = highlighted.classes; - if (newCls) line.styleClasses = newCls; - else if (oldCls) line.styleClasses = null; + var oldStyles = line.styles, tooLong = line.text.length > cm.options.maxHighlightLength + var highlighted = highlightLine(cm, line, tooLong ? copyState(doc.mode, state) : state, true) + line.styles = highlighted.styles + var oldCls = line.styleClasses, newCls = highlighted.classes + if (newCls) line.styleClasses = newCls + else if (oldCls) line.styleClasses = null 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) changedLines.push(doc.frontier); - line.stateAfter = tooLong ? state : copyState(doc.mode, state); + 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) changedLines.push(doc.frontier) + line.stateAfter = tooLong ? state : copyState(doc.mode, state) } else { if (line.text.length <= cm.options.maxHighlightLength) - processLine(cm, line.text, state); - line.stateAfter = doc.frontier % 5 == 0 ? copyState(doc.mode, state) : null; + processLine(cm, line.text, state) + line.stateAfter = doc.frontier % 5 == 0 ? copyState(doc.mode, state) : null } - ++doc.frontier; + ++doc.frontier if (+new Date > end) { - startWorker(cm, cm.options.workDelay); - return true; + startWorker(cm, cm.options.workDelay) + return true } - }); + }) if (changedLines.length) runInOp(cm, function() { for (var i = 0; i < changedLines.length; i++) - regLineChange(cm, changedLines[i], "text"); - }); + regLineChange(cm, changedLines[i], "text") + }) } diff --git a/src/display/line_numbers.js b/src/display/line_numbers.js index 85964db7..22bd32ff 100644 --- a/src/display/line_numbers.js +++ b/src/display/line_numbers.js @@ -1,48 +1,48 @@ -import { lineNumberFor } from "../line/utils_line"; -import { compensateForHScroll } from "../measurement/position_measurement"; -import { elt } from "../util/dom"; +import { lineNumberFor } from "../line/utils_line" +import { compensateForHScroll } from "../measurement/position_measurement" +import { elt } from "../util/dom" -import { updateGutterSpace } from "./update_display"; +import { updateGutterSpace } from "./update_display" // Re-align line numbers and gutter marks to compensate for // horizontal scrolling. export function alignHorizontally(cm) { - var display = cm.display, view = display.view; - if (!display.alignWidgets && (!display.gutters.firstChild || !cm.options.fixedGutter)) return; - var comp = compensateForHScroll(display) - display.scroller.scrollLeft + cm.doc.scrollLeft; - var gutterW = display.gutters.offsetWidth, left = comp + "px"; + var display = cm.display, view = display.view + if (!display.alignWidgets && (!display.gutters.firstChild || !cm.options.fixedGutter)) return + var comp = compensateForHScroll(display) - display.scroller.scrollLeft + cm.doc.scrollLeft + var gutterW = display.gutters.offsetWidth, left = comp + "px" for (var i = 0; i < view.length; i++) if (!view[i].hidden) { if (cm.options.fixedGutter) { if (view[i].gutter) - view[i].gutter.style.left = left; + view[i].gutter.style.left = left if (view[i].gutterBackground) - view[i].gutterBackground.style.left = left; + view[i].gutterBackground.style.left = left } - var align = view[i].alignable; + var align = view[i].alignable if (align) for (var j = 0; j < align.length; j++) - align[j].style.left = left; + align[j].style.left = left } if (cm.options.fixedGutter) - display.gutters.style.left = (comp + gutterW) + "px"; + display.gutters.style.left = (comp + gutterW) + "px" } // Used to ensure that the line number gutter is still the right // size for the current document size. Returns true when an update // is needed. export function maybeUpdateLineNumberWidth(cm) { - if (!cm.options.lineNumbers) return false; - var doc = cm.doc, last = lineNumberFor(cm.options, doc.first + doc.size - 1), display = cm.display; + if (!cm.options.lineNumbers) return false + 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")); - var innerW = test.firstChild.offsetWidth, padding = test.offsetWidth - innerW; - display.lineGutter.style.width = ""; - display.lineNumInnerWidth = Math.max(innerW, display.lineGutter.offsetWidth - padding) + 1; - display.lineNumWidth = display.lineNumInnerWidth + padding; - display.lineNumChars = display.lineNumInnerWidth ? last.length : -1; - display.lineGutter.style.width = display.lineNumWidth + "px"; - updateGutterSpace(cm); - return true; + "CodeMirror-linenumber CodeMirror-gutter-elt")) + var innerW = test.firstChild.offsetWidth, padding = test.offsetWidth - innerW + display.lineGutter.style.width = "" + display.lineNumInnerWidth = Math.max(innerW, display.lineGutter.offsetWidth - padding) + 1 + display.lineNumWidth = display.lineNumInnerWidth + padding + display.lineNumChars = display.lineNumInnerWidth ? last.length : -1 + display.lineGutter.style.width = display.lineNumWidth + "px" + updateGutterSpace(cm) + return true } - return false; + return false } diff --git a/src/display/mode_state.js b/src/display/mode_state.js index 005e60e3..7742c722 100644 --- a/src/display/mode_state.js +++ b/src/display/mode_state.js @@ -1,22 +1,22 @@ -import { getMode } from "../modes"; +import { getMode } from "../modes" -import { startWorker } from "./highlight_worker"; -import { regChange } from "./view_tracking"; +import { startWorker } from "./highlight_worker" +import { regChange } from "./view_tracking" // Used to get the editor into a consistent state again when options change. export function loadMode(cm) { - cm.doc.mode = getMode(cm.options, cm.doc.modeOption); - resetModeState(cm); + cm.doc.mode = getMode(cm.options, cm.doc.modeOption) + resetModeState(cm) } export function resetModeState(cm) { cm.doc.iter(function(line) { - if (line.stateAfter) line.stateAfter = null; - if (line.styles) line.styles = null; - }); - cm.doc.frontier = cm.doc.first; - startWorker(cm, 100); - cm.state.modeGen++; - if (cm.curOp) regChange(cm); + if (line.stateAfter) line.stateAfter = null + if (line.styles) line.styles = null + }) + cm.doc.frontier = cm.doc.first + startWorker(cm, 100) + cm.state.modeGen++ + if (cm.curOp) regChange(cm) } diff --git a/src/display/operations.js b/src/display/operations.js index a3439d00..2873e0e9 100644 --- a/src/display/operations.js +++ b/src/display/operations.js @@ -1,18 +1,18 @@ -import { clipPos } from "../line/pos"; -import { findMaxLine } from "../line/spans"; -import { displayWidth, measureChar, scrollGap } from "../measurement/position_measurement"; -import { signal } from "../util/event"; -import { activeElt } from "../util/dom"; -import { finishOperation, pushOperation } from "../util/operation_group"; - -import { ensureFocus } from "./focus"; -import { alignHorizontally } from "./line_numbers"; -import { measureForScrollbars, updateScrollbars } from "./scrollbars"; -import { setScrollLeft } from "./scroll_events"; -import { restartBlink } from "./selection"; -import { maybeScrollWindow, scrollPosIntoView } from "./scrolling"; -import { DisplayUpdate, maybeClipScrollbars, postUpdateDisplay, setDocumentHeight, updateDisplayIfNeeded } from "./update_display"; -import { updateHeightsInViewport } from "./update_lines"; +import { clipPos } from "../line/pos" +import { findMaxLine } from "../line/spans" +import { displayWidth, measureChar, scrollGap } from "../measurement/position_measurement" +import { signal } from "../util/event" +import { activeElt } from "../util/dom" +import { finishOperation, pushOperation } from "../util/operation_group" + +import { ensureFocus } from "./focus" +import { alignHorizontally } from "./line_numbers" +import { measureForScrollbars, updateScrollbars } from "./scrollbars" +import { setScrollLeft } from "./scroll_events" +import { restartBlink } from "./selection" +import { maybeScrollWindow, scrollPosIntoView } from "./scrolling" +import { DisplayUpdate, maybeClipScrollbars, postUpdateDisplay, setDocumentHeight, updateDisplayIfNeeded } from "./update_display" +import { updateHeightsInViewport } from "./update_lines" // Operations are used to wrap a series of changes to the editor // state in such a way that each change won't have to update the @@ -20,7 +20,7 @@ import { updateHeightsInViewport } from "./update_lines"; // error-prone). Instead, display updates are batched and then all // combined and executed at once. -var nextOpId = 0; +var nextOpId = 0 // Start a new operation. export function startOperation(cm) { cm.curOp = { @@ -39,177 +39,177 @@ export function startOperation(cm) { scrollToPos: null, // Used to scroll to a specific position focus: false, id: ++nextOpId // Unique ID - }; - pushOperation(cm.curOp); + } + pushOperation(cm.curOp) } // Finish an operation, updating the display and signalling delayed events export function endOperation(cm) { - var op = cm.curOp; + var op = cm.curOp finishOperation(op, function(group) { for (var i = 0; i < group.ops.length; i++) - group.ops[i].cm.curOp = null; - endOperations(group); - }); + group.ops[i].cm.curOp = null + endOperations(group) + }) } // 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; + var ops = group.ops for (var i = 0; i < ops.length; i++) // Read DOM - endOperation_R1(ops[i]); + endOperation_R1(ops[i]) for (var i = 0; i < ops.length; i++) // Write DOM (maybe) - endOperation_W1(ops[i]); + endOperation_W1(ops[i]) for (var i = 0; i < ops.length; i++) // Read DOM - endOperation_R2(ops[i]); + endOperation_R2(ops[i]) for (var i = 0; i < ops.length; i++) // Write DOM (maybe) - endOperation_W2(ops[i]); + endOperation_W2(ops[i]) for (var i = 0; i < ops.length; i++) // Read DOM - endOperation_finish(ops[i]); + endOperation_finish(ops[i]) } function endOperation_R1(op) { - var cm = op.cm, display = cm.display; - maybeClipScrollbars(cm); - if (op.updateMaxLine) findMaxLine(cm); + var cm = op.cm, display = cm.display + maybeClipScrollbars(cm) + if (op.updateMaxLine) findMaxLine(cm) 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; + display.maxLineChanged && cm.options.lineWrapping op.update = op.mustUpdate && - new DisplayUpdate(cm, op.mustUpdate && {top: op.scrollTop, ensure: op.scrollToPos}, op.forceUpdate); + 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); + op.updatedDisplay = op.mustUpdate && updateDisplayIfNeeded(op.cm, op.update) } function endOperation_R2(op) { - var cm = op.cm, display = cm.display; - if (op.updatedDisplay) updateHeightsInViewport(cm); + var cm = op.cm, display = cm.display + if (op.updatedDisplay) updateHeightsInViewport(cm) - op.barMeasure = measureForScrollbars(cm); + op.barMeasure = measureForScrollbars(cm) // If the max line changed since it was last measured, measure it, // and ensure the document's width matches it. // updateDisplay_W2 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 + 3; - cm.display.sizerWidth = op.adjustWidthTo; + op.adjustWidthTo = measureChar(cm, display.maxLine, display.maxLine.text.length).left + 3 + cm.display.sizerWidth = op.adjustWidthTo op.barMeasure.scrollWidth = - Math.max(display.scroller.clientWidth, display.sizer.offsetLeft + op.adjustWidthTo + scrollGap(cm) + cm.display.barWidth); - op.maxScrollLeft = Math.max(0, display.sizer.offsetLeft + op.adjustWidthTo - displayWidth(cm)); + Math.max(display.scroller.clientWidth, display.sizer.offsetLeft + op.adjustWidthTo + scrollGap(cm) + cm.display.barWidth) + op.maxScrollLeft = Math.max(0, display.sizer.offsetLeft + op.adjustWidthTo - displayWidth(cm)) } if (op.updatedDisplay || op.selectionChanged) - op.preparedSelection = display.input.prepareSelection(op.focus); + op.preparedSelection = display.input.prepareSelection(op.focus) } function endOperation_W2(op) { - var cm = op.cm; + var cm = op.cm if (op.adjustWidthTo != null) { - cm.display.sizer.style.minWidth = op.adjustWidthTo + "px"; + 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); - cm.display.maxLineChanged = false; + setScrollLeft(cm, Math.min(cm.display.scroller.scrollLeft, op.maxScrollLeft), true) + cm.display.maxLineChanged = false } var takeFocus = op.focus && op.focus == activeElt() && (!document.hasFocus || document.hasFocus()) if (op.preparedSelection) - cm.display.input.showSelection(op.preparedSelection, takeFocus); + cm.display.input.showSelection(op.preparedSelection, takeFocus) if (op.updatedDisplay || op.startHeight != cm.doc.height) - updateScrollbars(cm, op.barMeasure); + updateScrollbars(cm, op.barMeasure) if (op.updatedDisplay) - setDocumentHeight(cm, op.barMeasure); + setDocumentHeight(cm, op.barMeasure) - if (op.selectionChanged) restartBlink(cm); + if (op.selectionChanged) restartBlink(cm) if (cm.state.focused && op.updateInput) - cm.display.input.reset(op.typing); - if (takeFocus) ensureFocus(op.cm); + cm.display.input.reset(op.typing) + if (takeFocus) ensureFocus(op.cm) } function endOperation_finish(op) { - var cm = op.cm, display = cm.display, doc = cm.doc; + var cm = op.cm, display = cm.display, doc = cm.doc - if (op.updatedDisplay) postUpdateDisplay(cm, op.update); + 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; + display.wheelStartX = display.wheelStartY = null // Propagate the scroll position to the actual DOM scroller if (op.scrollTop != null && (display.scroller.scrollTop != op.scrollTop || op.forceScroll)) { - doc.scrollTop = Math.max(0, Math.min(display.scroller.scrollHeight - display.scroller.clientHeight, op.scrollTop)); - display.scrollbars.setScrollTop(doc.scrollTop); - display.scroller.scrollTop = doc.scrollTop; + doc.scrollTop = Math.max(0, Math.min(display.scroller.scrollHeight - display.scroller.clientHeight, op.scrollTop)) + display.scrollbars.setScrollTop(doc.scrollTop) + display.scroller.scrollTop = doc.scrollTop } if (op.scrollLeft != null && (display.scroller.scrollLeft != op.scrollLeft || op.forceScroll)) { - doc.scrollLeft = Math.max(0, Math.min(display.scroller.scrollWidth - display.scroller.clientWidth, op.scrollLeft)); - display.scrollbars.setScrollLeft(doc.scrollLeft); - display.scroller.scrollLeft = doc.scrollLeft; - alignHorizontally(cm); + doc.scrollLeft = Math.max(0, Math.min(display.scroller.scrollWidth - display.scroller.clientWidth, op.scrollLeft)) + display.scrollbars.setScrollLeft(doc.scrollLeft) + display.scroller.scrollLeft = doc.scrollLeft + alignHorizontally(cm) } // If we need to scroll a specific position into view, do so. if (op.scrollToPos) { 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); + clipPos(doc, op.scrollToPos.to), op.scrollToPos.margin) + if (op.scrollToPos.isCursor && cm.state.focused) maybeScrollWindow(cm, coords) } // Fire events for markers that are hidden/unidden by editing or // undoing - var hidden = op.maybeHiddenMarkers, unhidden = op.maybeUnhiddenMarkers; + 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 (!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 (unhidden[i].lines.length) signal(unhidden[i], "unhide") if (display.wrapper.offsetHeight) - doc.scrollTop = cm.display.scroller.scrollTop; + doc.scrollTop = cm.display.scroller.scrollTop // Fire change events, and delayed event handlers if (op.changeObjs) - signal(cm, "changes", cm, op.changeObjs); + signal(cm, "changes", cm, op.changeObjs) if (op.update) - op.update.finish(); + op.update.finish() } // Run the given function in an operation export function runInOp(cm, f) { - if (cm.curOp) return f(); - startOperation(cm); - try { return f(); } - finally { endOperation(cm); } + if (cm.curOp) return f() + startOperation(cm) + try { return f() } + finally { endOperation(cm) } } // Wraps a function in an operation. Returns the wrapped function. export function operation(cm, f) { return function() { - if (cm.curOp) return f.apply(cm, arguments); - startOperation(cm); - try { return f.apply(cm, arguments); } - finally { endOperation(cm); } - }; + if (cm.curOp) return f.apply(cm, arguments) + startOperation(cm) + try { return f.apply(cm, arguments) } + finally { endOperation(cm) } + } } // Used to add methods to editor and doc instances, wrapping them in // operations. export function methodOp(f) { return function() { - if (this.curOp) return f.apply(this, arguments); - startOperation(this); - try { return f.apply(this, arguments); } - finally { endOperation(this); } - }; + if (this.curOp) return f.apply(this, arguments) + startOperation(this) + try { return f.apply(this, arguments) } + finally { endOperation(this) } + } } export function docMethodOp(f) { return function() { - var cm = this.cm; - if (!cm || cm.curOp) return f.apply(this, arguments); - startOperation(cm); - try { return f.apply(this, arguments); } - finally { endOperation(cm); } - }; + var cm = this.cm + if (!cm || cm.curOp) return f.apply(this, arguments) + startOperation(cm) + try { return f.apply(this, arguments) } + finally { endOperation(cm) } + } } diff --git a/src/display/scroll_events.js b/src/display/scroll_events.js index 3c5a666e..99afe7ee 100644 --- a/src/display/scroll_events.js +++ b/src/display/scroll_events.js @@ -1,30 +1,30 @@ -import { chrome, gecko, ie, mac, presto, safari, webkit } from "../util/browser"; -import { e_preventDefault } from "../util/event"; +import { chrome, gecko, ie, mac, presto, safari, webkit } from "../util/browser" +import { e_preventDefault } from "../util/event" -import { startWorker } from "./highlight_worker"; -import { alignHorizontally } from "./line_numbers"; -import { updateDisplaySimple} from "./update_display"; +import { startWorker } from "./highlight_worker" +import { alignHorizontally } from "./line_numbers" +import { updateDisplaySimple} from "./update_display" // Sync the scrollable area and scrollbars, ensure the viewport // covers the visible area. export function setScrollTop(cm, val) { - if (Math.abs(cm.doc.scrollTop - val) < 2) return; - cm.doc.scrollTop = val; - if (!gecko) updateDisplaySimple(cm, {top: val}); - if (cm.display.scroller.scrollTop != val) cm.display.scroller.scrollTop = val; - cm.display.scrollbars.setScrollTop(val); - if (gecko) updateDisplaySimple(cm); - startWorker(cm, 100); + if (Math.abs(cm.doc.scrollTop - val) < 2) return + cm.doc.scrollTop = val + if (!gecko) updateDisplaySimple(cm, {top: val}) + if (cm.display.scroller.scrollTop != val) cm.display.scroller.scrollTop = val + cm.display.scrollbars.setScrollTop(val) + if (gecko) updateDisplaySimple(cm) + startWorker(cm, 100) } // Sync scroller and scrollbar, ensure the gutter elements are // aligned. export function setScrollLeft(cm, val, isScroller) { - 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.doc.scrollLeft = val; - alignHorizontally(cm); - if (cm.display.scroller.scrollLeft != val) cm.display.scroller.scrollLeft = val; - cm.display.scrollbars.setScrollLeft(val); + 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.doc.scrollLeft = val + alignHorizontally(cm) + if (cm.display.scroller.scrollLeft != val) cm.display.scroller.scrollLeft = val + cm.display.scrollbars.setScrollLeft(val) } // Since the delta values reported on mouse wheel events are @@ -38,38 +38,38 @@ export function setScrollLeft(cm, val, isScroller) { // is that it gives us a chance to update the display before the // actual scrolling happens, reducing flickering. -var wheelSamples = 0, 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 // scroll (if it is large enough). -if (ie) wheelPixelsPerUnit = -.53; -else if (gecko) wheelPixelsPerUnit = 15; -else if (chrome) wheelPixelsPerUnit = -.7; -else if (safari) wheelPixelsPerUnit = -1/3; +if (ie) wheelPixelsPerUnit = -.53 +else if (gecko) wheelPixelsPerUnit = 15 +else if (chrome) wheelPixelsPerUnit = -.7 +else if (safari) wheelPixelsPerUnit = -1/3 var wheelEventDelta = function(e) { - var dx = e.wheelDeltaX, dy = e.wheelDeltaY; - if (dx == null && e.detail && e.axis == e.HORIZONTAL_AXIS) dx = e.detail; - if (dy == null && e.detail && e.axis == e.VERTICAL_AXIS) dy = e.detail; - else if (dy == null) dy = e.wheelDelta; - return {x: dx, y: dy}; -}; + var dx = e.wheelDeltaX, dy = e.wheelDeltaY + if (dx == null && e.detail && e.axis == e.HORIZONTAL_AXIS) dx = e.detail + if (dy == null && e.detail && e.axis == e.VERTICAL_AXIS) dy = e.detail + else if (dy == null) dy = e.wheelDelta + return {x: dx, y: dy} +} export function wheelEventPixels(e) { - var delta = wheelEventDelta(e); - delta.x *= wheelPixelsPerUnit; - delta.y *= wheelPixelsPerUnit; - return delta; + var delta = wheelEventDelta(e) + delta.x *= wheelPixelsPerUnit + delta.y *= wheelPixelsPerUnit + return delta } export function onScrollWheel(cm, e) { - var delta = wheelEventDelta(e), dx = delta.x, dy = delta.y; + var delta = wheelEventDelta(e), dx = delta.x, dy = delta.y - var display = cm.display, scroll = display.scroller; + var display = cm.display, scroll = display.scroller // Quit if there's nothing to scroll here - var canScrollX = scroll.scrollWidth > scroll.clientWidth; - var canScrollY = scroll.scrollHeight > scroll.clientHeight; - if (!(dx && canScrollX || dy && canScrollY)) return; + var canScrollX = scroll.scrollWidth > scroll.clientWidth + var canScrollY = scroll.scrollHeight > scroll.clientHeight + if (!(dx && canScrollX || dy && canScrollY)) return // Webkit browsers on OS X abort momentum scrolls when the target // of the scroll event is removed from the scrollable element. @@ -79,8 +79,8 @@ export function onScrollWheel(cm, e) { outer: for (var cur = e.target, view = display.view; cur != scroll; cur = cur.parentNode) { for (var i = 0; i < view.length; i++) { if (view[i].node == cur) { - cm.display.currentWheelTarget = cur; - break outer; + cm.display.currentWheelTarget = cur + break outer } } } @@ -94,45 +94,45 @@ export function onScrollWheel(cm, e) { // better than glitching out. if (dx && !gecko && !presto && wheelPixelsPerUnit != null) { if (dy && canScrollY) - 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))); + 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))) // Only prevent default scrolling if vertical scrolling is // actually possible. Otherwise, it causes vertical scroll // jitter on OSX trackpads when deltaX is small and deltaY // is large (issue #3579) if (!dy || (dy && canScrollY)) - e_preventDefault(e); - display.wheelStartX = null; // Abort measurement, if in progress - return; + e_preventDefault(e) + display.wheelStartX = null // Abort measurement, if in progress + return } // 'Project' the visible viewport to cover the area that is being // scrolled into view (if we know enough to estimate it). if (dy && wheelPixelsPerUnit != null) { - var pixels = dy * wheelPixelsPerUnit; - 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); - updateDisplaySimple(cm, {top: top, bottom: bot}); + var pixels = dy * wheelPixelsPerUnit + 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) + updateDisplaySimple(cm, {top: top, bottom: bot}) } if (wheelSamples < 20) { if (display.wheelStartX == null) { - display.wheelStartX = scroll.scrollLeft; display.wheelStartY = scroll.scrollTop; - display.wheelDX = dx; display.wheelDY = dy; + display.wheelStartX = scroll.scrollLeft; display.wheelStartY = scroll.scrollTop + display.wheelDX = dx; display.wheelDY = dy setTimeout(function() { - if (display.wheelStartX == null) return; - var movedX = scroll.scrollLeft - display.wheelStartX; - var movedY = scroll.scrollTop - display.wheelStartY; + 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); + (movedX && display.wheelDX && movedX / display.wheelDX) + display.wheelStartX = display.wheelStartY = null + if (!sample) return + wheelPixelsPerUnit = (wheelPixelsPerUnit * wheelSamples + sample) / (wheelSamples + 1) + ++wheelSamples + }, 200) } else { - display.wheelDX += dx; display.wheelDY += dy; + display.wheelDX += dx; display.wheelDY += dy } } } diff --git a/src/display/scrollbars.js b/src/display/scrollbars.js index a29444b5..075ae41a 100644 --- a/src/display/scrollbars.js +++ b/src/display/scrollbars.js @@ -1,19 +1,19 @@ -import { addClass, elt, rmClass } from "../util/dom"; -import { on } from "../util/event"; -import { scrollGap, paddingVert } from "../measurement/position_measurement"; -import { ie, ie_version, mac, mac_geMountainLion } from "../util/browser"; -import { updateHeightsInViewport } from "./update_lines"; -import { copyObj, Delayed } from "../util/misc"; +import { addClass, elt, rmClass } from "../util/dom" +import { on } from "../util/event" +import { scrollGap, paddingVert } from "../measurement/position_measurement" +import { ie, ie_version, mac, mac_geMountainLion } from "../util/browser" +import { updateHeightsInViewport } from "./update_lines" +import { copyObj, Delayed } from "../util/misc" -import { setScrollLeft, setScrollTop } from "./scroll_events"; +import { setScrollLeft, setScrollTop } from "./scroll_events" // SCROLLBARS // Prepare DOM reads needed to update the scrollbars. Done in one // shot to minimize update/measure roundtrips. export function measureForScrollbars(cm) { - var d = cm.display, gutterW = d.gutters.offsetWidth; - var docH = Math.round(cm.doc.height + paddingVert(cm.display)); + var d = cm.display, gutterW = d.gutters.offsetWidth + var docH = Math.round(cm.doc.height + paddingVert(cm.display)) return { clientHeight: d.scroller.clientHeight, viewHeight: d.wrapper.clientHeight, @@ -24,81 +24,81 @@ export function measureForScrollbars(cm) { scrollHeight: docH + scrollGap(cm) + d.barHeight, nativeBarWidth: d.nativeBarWidth, gutterWidth: gutterW - }; + } } function NativeScrollbars(place, scroll, cm) { - this.cm = cm; - var vert = this.vert = elt("div", [elt("div", null, null, "min-width: 1px")], "CodeMirror-vscrollbar"); - var horiz = this.horiz = elt("div", [elt("div", null, null, "height: 100%; min-height: 1px")], "CodeMirror-hscrollbar"); - place(vert); place(horiz); + this.cm = cm + var vert = this.vert = elt("div", [elt("div", null, null, "min-width: 1px")], "CodeMirror-vscrollbar") + var horiz = this.horiz = elt("div", [elt("div", null, null, "height: 100%; min-height: 1px")], "CodeMirror-hscrollbar") + place(vert); place(horiz) on(vert, "scroll", function() { - if (vert.clientHeight) scroll(vert.scrollTop, "vertical"); - }); + if (vert.clientHeight) scroll(vert.scrollTop, "vertical") + }) on(horiz, "scroll", function() { - if (horiz.clientWidth) scroll(horiz.scrollLeft, "horizontal"); - }); + if (horiz.clientWidth) scroll(horiz.scrollLeft, "horizontal") + }) - this.checkedZeroWidth = false; + this.checkedZeroWidth = false // Need to set a minimum width to see the scrollbar on IE7 (but must not set it on IE8). - if (ie && ie_version < 8) this.horiz.style.minHeight = this.vert.style.minWidth = "18px"; + if (ie && ie_version < 8) this.horiz.style.minHeight = this.vert.style.minWidth = "18px" } NativeScrollbars.prototype = copyObj({ update: function(measure) { - var needsH = measure.scrollWidth > measure.clientWidth + 1; - var needsV = measure.scrollHeight > measure.clientHeight + 1; - var sWidth = measure.nativeBarWidth; + var needsH = measure.scrollWidth > measure.clientWidth + 1 + var needsV = measure.scrollHeight > measure.clientHeight + 1 + var sWidth = measure.nativeBarWidth if (needsV) { - this.vert.style.display = "block"; - this.vert.style.bottom = needsH ? sWidth + "px" : "0"; - var totalHeight = measure.viewHeight - (needsH ? sWidth : 0); + this.vert.style.display = "block" + this.vert.style.bottom = needsH ? sWidth + "px" : "0" + var totalHeight = measure.viewHeight - (needsH ? sWidth : 0) // A bug in IE8 can cause this value to be negative, so guard it. this.vert.firstChild.style.height = - Math.max(0, measure.scrollHeight - measure.clientHeight + totalHeight) + "px"; + Math.max(0, measure.scrollHeight - measure.clientHeight + totalHeight) + "px" } else { - this.vert.style.display = ""; - this.vert.firstChild.style.height = "0"; + this.vert.style.display = "" + this.vert.firstChild.style.height = "0" } if (needsH) { - this.horiz.style.display = "block"; - this.horiz.style.right = needsV ? sWidth + "px" : "0"; - this.horiz.style.left = measure.barLeft + "px"; - var totalWidth = measure.viewWidth - measure.barLeft - (needsV ? sWidth : 0); + this.horiz.style.display = "block" + this.horiz.style.right = needsV ? sWidth + "px" : "0" + this.horiz.style.left = measure.barLeft + "px" + var totalWidth = measure.viewWidth - measure.barLeft - (needsV ? sWidth : 0) this.horiz.firstChild.style.width = - (measure.scrollWidth - measure.clientWidth + totalWidth) + "px"; + (measure.scrollWidth - measure.clientWidth + totalWidth) + "px" } else { - this.horiz.style.display = ""; - this.horiz.firstChild.style.width = "0"; + this.horiz.style.display = "" + this.horiz.firstChild.style.width = "0" } if (!this.checkedZeroWidth && measure.clientHeight > 0) { - if (sWidth == 0) this.zeroWidthHack(); - this.checkedZeroWidth = true; + if (sWidth == 0) this.zeroWidthHack() + this.checkedZeroWidth = true } - return {right: needsV ? sWidth : 0, bottom: needsH ? sWidth : 0}; + return {right: needsV ? sWidth : 0, bottom: needsH ? sWidth : 0} }, setScrollLeft: function(pos) { - if (this.horiz.scrollLeft != pos) this.horiz.scrollLeft = pos; - if (this.disableHoriz) this.enableZeroWidthBar(this.horiz, this.disableHoriz); + if (this.horiz.scrollLeft != pos) this.horiz.scrollLeft = pos + if (this.disableHoriz) this.enableZeroWidthBar(this.horiz, this.disableHoriz) }, setScrollTop: function(pos) { - if (this.vert.scrollTop != pos) this.vert.scrollTop = pos; - if (this.disableVert) this.enableZeroWidthBar(this.vert, this.disableVert); + if (this.vert.scrollTop != pos) this.vert.scrollTop = pos + if (this.disableVert) this.enableZeroWidthBar(this.vert, this.disableVert) }, zeroWidthHack: function() { - var w = mac && !mac_geMountainLion ? "12px" : "18px"; - this.horiz.style.height = this.vert.style.width = w; - this.horiz.style.pointerEvents = this.vert.style.pointerEvents = "none"; - this.disableHoriz = new Delayed; - this.disableVert = new Delayed; + var w = mac && !mac_geMountainLion ? "12px" : "18px" + this.horiz.style.height = this.vert.style.width = w + this.horiz.style.pointerEvents = this.vert.style.pointerEvents = "none" + this.disableHoriz = new Delayed + this.disableVert = new Delayed }, enableZeroWidthBar: function(bar, delay) { - bar.style.pointerEvents = "auto"; + bar.style.pointerEvents = "auto" function maybeDisable() { // To find out whether the scrollbar is still visible, we // check whether the element under the pixel in the bottom @@ -106,83 +106,83 @@ NativeScrollbars.prototype = copyObj({ // itself (when the bar is still visible) or its filler child // (when the bar is hidden). If it is still visible, we keep // it enabled, if it's hidden, we disable pointer events. - var box = bar.getBoundingClientRect(); - var elt = document.elementFromPoint(box.left + 1, box.bottom - 1); - if (elt != bar) bar.style.pointerEvents = "none"; - else delay.set(1000, maybeDisable); + var box = bar.getBoundingClientRect() + var elt = document.elementFromPoint(box.left + 1, box.bottom - 1) + if (elt != bar) bar.style.pointerEvents = "none" + else delay.set(1000, maybeDisable) } - delay.set(1000, maybeDisable); + delay.set(1000, maybeDisable) }, clear: function() { - var parent = this.horiz.parentNode; - parent.removeChild(this.horiz); - parent.removeChild(this.vert); + var parent = this.horiz.parentNode + parent.removeChild(this.horiz) + parent.removeChild(this.vert) } -}, NativeScrollbars.prototype); +}, NativeScrollbars.prototype) function NullScrollbars() {} NullScrollbars.prototype = copyObj({ - update: function() { return {bottom: 0, right: 0}; }, + update: function() { return {bottom: 0, right: 0} }, setScrollLeft: function() {}, setScrollTop: function() {}, clear: function() {} -}, NullScrollbars.prototype); +}, NullScrollbars.prototype) export function updateScrollbars(cm, measure) { - if (!measure) measure = measureForScrollbars(cm); - var startWidth = cm.display.barWidth, startHeight = cm.display.barHeight; - updateScrollbarsInner(cm, measure); + if (!measure) measure = measureForScrollbars(cm) + var startWidth = cm.display.barWidth, startHeight = cm.display.barHeight + updateScrollbarsInner(cm, measure) for (var i = 0; i < 4 && startWidth != cm.display.barWidth || startHeight != cm.display.barHeight; i++) { if (startWidth != cm.display.barWidth && cm.options.lineWrapping) - updateHeightsInViewport(cm); - updateScrollbarsInner(cm, measureForScrollbars(cm)); - startWidth = cm.display.barWidth; startHeight = cm.display.barHeight; + updateHeightsInViewport(cm) + updateScrollbarsInner(cm, measureForScrollbars(cm)) + startWidth = cm.display.barWidth; startHeight = cm.display.barHeight } } // Re-synchronize the fake scrollbars with the actual size of the // content. function updateScrollbarsInner(cm, measure) { - var d = cm.display; - var sizes = d.scrollbars.update(measure); + var d = cm.display + var sizes = d.scrollbars.update(measure) - d.sizer.style.paddingRight = (d.barWidth = sizes.right) + "px"; - d.sizer.style.paddingBottom = (d.barHeight = sizes.bottom) + "px"; + d.sizer.style.paddingRight = (d.barWidth = sizes.right) + "px" + d.sizer.style.paddingBottom = (d.barHeight = sizes.bottom) + "px" d.heightForcer.style.borderBottom = sizes.bottom + "px solid transparent" if (sizes.right && sizes.bottom) { - d.scrollbarFiller.style.display = "block"; - d.scrollbarFiller.style.height = sizes.bottom + "px"; - d.scrollbarFiller.style.width = sizes.right + "px"; - } else d.scrollbarFiller.style.display = ""; + d.scrollbarFiller.style.display = "block" + d.scrollbarFiller.style.height = sizes.bottom + "px" + d.scrollbarFiller.style.width = sizes.right + "px" + } else d.scrollbarFiller.style.display = "" if (sizes.bottom && cm.options.coverGutterNextToScrollbar && cm.options.fixedGutter) { - d.gutterFiller.style.display = "block"; - d.gutterFiller.style.height = sizes.bottom + "px"; - d.gutterFiller.style.width = measure.gutterWidth + "px"; - } else d.gutterFiller.style.display = ""; + d.gutterFiller.style.display = "block" + d.gutterFiller.style.height = sizes.bottom + "px" + d.gutterFiller.style.width = measure.gutterWidth + "px" + } else d.gutterFiller.style.display = "" } -export var scrollbarModel = {"native": NativeScrollbars, "null": NullScrollbars}; +export var scrollbarModel = {"native": NativeScrollbars, "null": NullScrollbars} export function initScrollbars(cm) { if (cm.display.scrollbars) { - cm.display.scrollbars.clear(); + cm.display.scrollbars.clear() if (cm.display.scrollbars.addClass) - rmClass(cm.display.wrapper, cm.display.scrollbars.addClass); + rmClass(cm.display.wrapper, cm.display.scrollbars.addClass) } cm.display.scrollbars = new scrollbarModel[cm.options.scrollbarStyle](function(node) { - cm.display.wrapper.insertBefore(node, cm.display.scrollbarFiller); + cm.display.wrapper.insertBefore(node, cm.display.scrollbarFiller) // Prevent clicks in the scrollbars from killing focus on(node, "mousedown", function() { - if (cm.state.focused) setTimeout(function() { cm.display.input.focus(); }, 0); - }); - node.setAttribute("cm-not-content", "true"); + if (cm.state.focused) setTimeout(function() { cm.display.input.focus() }, 0) + }) + node.setAttribute("cm-not-content", "true") }, function(pos, axis) { - if (axis == "horizontal") setScrollLeft(cm, pos); - else setScrollTop(cm, pos); - }, cm); + if (axis == "horizontal") setScrollLeft(cm, pos) + else setScrollTop(cm, pos) + }, cm) if (cm.display.scrollbars.addClass) - addClass(cm.display.wrapper, cm.display.scrollbars.addClass); + addClass(cm.display.wrapper, cm.display.scrollbars.addClass) } diff --git a/src/display/scrolling.js b/src/display/scrolling.js index 50ea4012..ec8cec67 100644 --- a/src/display/scrolling.js +++ b/src/display/scrolling.js @@ -1,29 +1,29 @@ -import { Pos } from "../line/pos"; -import { cursorCoords, displayHeight, displayWidth, estimateCoords, paddingTop, paddingVert, scrollGap, textHeight } from "../measurement/position_measurement"; -import { phantom } from "../util/browser"; -import { elt } from "../util/dom"; -import { signalDOMEvent } from "../util/event"; +import { Pos } from "../line/pos" +import { cursorCoords, displayHeight, displayWidth, estimateCoords, paddingTop, paddingVert, scrollGap, textHeight } from "../measurement/position_measurement" +import { phantom } from "../util/browser" +import { elt } from "../util/dom" +import { signalDOMEvent } from "../util/event" -import { setScrollLeft, setScrollTop } from "./scroll_events"; +import { setScrollLeft, setScrollTop } from "./scroll_events" // SCROLLING THINGS INTO VIEW // If an editor sits on the top or bottom of the window, partially // scrolled out of view, this ensures that the cursor is visible. export function maybeScrollWindow(cm, coords) { - if (signalDOMEvent(cm, "scrollCursorIntoView")) return; + if (signalDOMEvent(cm, "scrollCursorIntoView")) 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; + 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 && !phantom) { var scrollNode = elt("div", "\u200b", null, "position: absolute; top: " + (coords.top - display.viewOffset - paddingTop(cm.display)) + "px; height: " + (coords.bottom - coords.top + scrollGap(cm) + display.barHeight) + "px; left: " + - coords.left + "px; width: 2px;"); - cm.display.lineSpace.appendChild(scrollNode); - scrollNode.scrollIntoView(doScroll); - cm.display.lineSpace.removeChild(scrollNode); + coords.left + "px; width: 2px;") + cm.display.lineSpace.appendChild(scrollNode) + scrollNode.scrollIntoView(doScroll) + cm.display.lineSpace.removeChild(scrollNode) } } @@ -31,33 +31,33 @@ export function maybeScrollWindow(cm, coords) { // it actually became visible (as line heights are accurately // measured, the position of something may 'drift' during drawing). export function scrollPosIntoView(cm, pos, end, margin) { - if (margin == null) margin = 0; + if (margin == null) margin = 0 for (var limit = 0; limit < 5; limit++) { - var changed = false, coords = cursorCoords(cm, pos); - var endCoords = !end || end == pos ? coords : cursorCoords(cm, end); + var changed = false, coords = cursorCoords(cm, pos) + var endCoords = !end || end == pos ? coords : cursorCoords(cm, end) var scrollPos = calculateScrollPos(cm, Math.min(coords.left, endCoords.left), Math.min(coords.top, endCoords.top) - margin, Math.max(coords.left, endCoords.left), - Math.max(coords.bottom, endCoords.bottom) + margin); - var startTop = cm.doc.scrollTop, startLeft = cm.doc.scrollLeft; + Math.max(coords.bottom, endCoords.bottom) + margin) + var startTop = cm.doc.scrollTop, startLeft = cm.doc.scrollLeft if (scrollPos.scrollTop != null) { - setScrollTop(cm, scrollPos.scrollTop); - if (Math.abs(cm.doc.scrollTop - startTop) > 1) changed = true; + setScrollTop(cm, scrollPos.scrollTop) + if (Math.abs(cm.doc.scrollTop - startTop) > 1) changed = true } if (scrollPos.scrollLeft != null) { - setScrollLeft(cm, scrollPos.scrollLeft); - if (Math.abs(cm.doc.scrollLeft - startLeft) > 1) changed = true; + setScrollLeft(cm, scrollPos.scrollLeft) + if (Math.abs(cm.doc.scrollLeft - startLeft) > 1) changed = true } - if (!changed) break; + if (!changed) break } - return coords; + return coords } // Scroll a given set of coordinates into view (immediately). export function scrollIntoView(cm, x1, y1, x2, y2) { - var scrollPos = calculateScrollPos(cm, x1, y1, x2, y2); - if (scrollPos.scrollTop != null) setScrollTop(cm, scrollPos.scrollTop); - if (scrollPos.scrollLeft != null) setScrollLeft(cm, scrollPos.scrollLeft); + var scrollPos = calculateScrollPos(cm, x1, y1, x2, y2) + if (scrollPos.scrollTop != null) setScrollTop(cm, scrollPos.scrollTop) + if (scrollPos.scrollLeft != null) setScrollLeft(cm, scrollPos.scrollLeft) } // Calculate a new scroll position needed to scroll the given @@ -65,53 +65,53 @@ export function scrollIntoView(cm, x1, y1, x2, y2) { // scrollLeft properties. When these are undefined, the // vertical/horizontal position does not need to be adjusted. export function calculateScrollPos(cm, x1, y1, x2, y2) { - var display = cm.display, snapMargin = textHeight(cm.display); - if (y1 < 0) y1 = 0; - var screentop = cm.curOp && cm.curOp.scrollTop != null ? cm.curOp.scrollTop : display.scroller.scrollTop; - var screen = displayHeight(cm), result = {}; - if (y2 - y1 > screen) y2 = y1 + screen; - var docBottom = cm.doc.height + paddingVert(display); - var atTop = y1 < snapMargin, atBottom = y2 > docBottom - snapMargin; + var display = cm.display, snapMargin = textHeight(cm.display) + if (y1 < 0) y1 = 0 + var screentop = cm.curOp && cm.curOp.scrollTop != null ? cm.curOp.scrollTop : display.scroller.scrollTop + var screen = displayHeight(cm), result = {} + if (y2 - y1 > screen) y2 = y1 + screen + var docBottom = cm.doc.height + paddingVert(display) + var atTop = y1 < snapMargin, atBottom = y2 > docBottom - snapMargin if (y1 < screentop) { - result.scrollTop = atTop ? 0 : y1; + 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 newTop = Math.min(y1, (atBottom ? docBottom : y2) - screen) + if (newTop != screentop) result.scrollTop = newTop } - var screenleft = cm.curOp && cm.curOp.scrollLeft != null ? cm.curOp.scrollLeft : display.scroller.scrollLeft; - var screenw = displayWidth(cm) - (cm.options.fixedGutter ? display.gutters.offsetWidth : 0); - var tooWide = x2 - x1 > screenw; - if (tooWide) x2 = x1 + screenw; + var screenleft = cm.curOp && cm.curOp.scrollLeft != null ? cm.curOp.scrollLeft : display.scroller.scrollLeft + var screenw = displayWidth(cm) - (cm.options.fixedGutter ? display.gutters.offsetWidth : 0) + var tooWide = x2 - x1 > screenw + if (tooWide) x2 = x1 + screenw if (x1 < 10) - result.scrollLeft = 0; + result.scrollLeft = 0 else if (x1 < screenleft) - result.scrollLeft = Math.max(0, x1 - (tooWide ? 0 : 10)); + result.scrollLeft = Math.max(0, x1 - (tooWide ? 0 : 10)) else if (x2 > screenw + screenleft - 3) - result.scrollLeft = x2 + (tooWide ? 0 : 10) - screenw; - return result; + result.scrollLeft = x2 + (tooWide ? 0 : 10) - screenw + return result } // Store a relative adjustment to the scroll position in the current // operation (to be applied when the operation finishes). export function addToScrollPos(cm, left, top) { - if (left != null || top != null) resolveScrollToPos(cm); + if (left != null || top != null) resolveScrollToPos(cm) if (left != null) - cm.curOp.scrollLeft = (cm.curOp.scrollLeft == null ? cm.doc.scrollLeft : cm.curOp.scrollLeft) + left; + cm.curOp.scrollLeft = (cm.curOp.scrollLeft == null ? cm.doc.scrollLeft : cm.curOp.scrollLeft) + left if (top != null) - cm.curOp.scrollTop = (cm.curOp.scrollTop == null ? cm.doc.scrollTop : cm.curOp.scrollTop) + top; + cm.curOp.scrollTop = (cm.curOp.scrollTop == null ? cm.doc.scrollTop : cm.curOp.scrollTop) + top } // Make sure that at the end of the operation the current cursor is // shown. export function ensureCursorVisible(cm) { - resolveScrollToPos(cm); - var cur = cm.getCursor(), from = cur, to = cur; + resolveScrollToPos(cm) + var cur = cm.getCursor(), from = cur, to = cur if (!cm.options.lineWrapping) { - from = cur.ch ? Pos(cur.line, cur.ch - 1) : cur; - to = Pos(cur.line, cur.ch + 1); + from = cur.ch ? Pos(cur.line, cur.ch - 1) : cur + to = Pos(cur.line, cur.ch + 1) } - cm.curOp.scrollToPos = {from: from, to: to, margin: cm.options.cursorScrollMargin, isCursor: true}; + cm.curOp.scrollToPos = {from: from, to: to, margin: cm.options.cursorScrollMargin, isCursor: true} } // When an operation has its scrollToPos property set, and another @@ -119,14 +119,14 @@ export function ensureCursorVisible(cm) { // 'simulates' scrolling that position into view in a cheap way, so // that the effect of intermediate scroll commands is not ignored. export function resolveScrollToPos(cm) { - var range = cm.curOp.scrollToPos; + var range = cm.curOp.scrollToPos if (range) { - cm.curOp.scrollToPos = null; - var from = estimateCoords(cm, range.from), to = estimateCoords(cm, range.to); + cm.curOp.scrollToPos = null + var from = estimateCoords(cm, range.from), to = estimateCoords(cm, range.to) var sPos = calculateScrollPos(cm, Math.min(from.left, to.left), Math.min(from.top, to.top) - range.margin, Math.max(from.right, to.right), - Math.max(from.bottom, to.bottom) + range.margin); - cm.scrollTo(sPos.scrollLeft, sPos.scrollTop); + Math.max(from.bottom, to.bottom) + range.margin) + cm.scrollTo(sPos.scrollLeft, sPos.scrollTop) } } diff --git a/src/display/selection.js b/src/display/selection.js index b6ca8b6f..3ee79fa3 100644 --- a/src/display/selection.js +++ b/src/display/selection.js @@ -1,137 +1,137 @@ -import { Pos } from "../line/pos"; -import { visualLine } from "../line/spans"; -import { getLine } from "../line/utils_line"; -import { charCoords, cursorCoords, displayWidth, paddingH } from "../measurement/position_measurement"; -import { getOrder, iterateBidiSections } from "../util/bidi"; -import { elt } from "../util/dom"; +import { Pos } from "../line/pos" +import { visualLine } from "../line/spans" +import { getLine } from "../line/utils_line" +import { charCoords, cursorCoords, displayWidth, paddingH } from "../measurement/position_measurement" +import { getOrder, iterateBidiSections } from "../util/bidi" +import { elt } from "../util/dom" export function updateSelection(cm) { - cm.display.input.showSelection(cm.display.input.prepareSelection()); + cm.display.input.showSelection(cm.display.input.prepareSelection()) } export function prepareSelection(cm, primary) { - var doc = cm.doc, result = {}; - var curFragment = result.cursors = document.createDocumentFragment(); - var selFragment = result.selection = document.createDocumentFragment(); + var 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++) { - if (primary === false && i == doc.sel.primIndex) continue; - var range = doc.sel.ranges[i]; - if (range.from().line >= cm.display.viewTo || range.to().line < cm.display.viewFrom) continue; - var collapsed = range.empty(); + if (primary === false && i == doc.sel.primIndex) continue + var range = doc.sel.ranges[i] + if (range.from().line >= cm.display.viewTo || range.to().line < cm.display.viewFrom) continue + var collapsed = range.empty() if (collapsed || cm.options.showCursorWhenSelecting) - drawSelectionCursor(cm, range.head, curFragment); + drawSelectionCursor(cm, range.head, curFragment) if (!collapsed) - drawSelectionRange(cm, range, selFragment); + drawSelectionRange(cm, range, selFragment) } - return result; + return result } // Draws a cursor for the given range export function drawSelectionCursor(cm, head, output) { - var pos = cursorCoords(cm, head, "div", null, null, !cm.options.singleCursorHeightPerLine); + var pos = cursorCoords(cm, head, "div", null, null, !cm.options.singleCursorHeightPerLine) - var cursor = output.appendChild(elt("div", "\u00a0", "CodeMirror-cursor")); - cursor.style.left = pos.left + "px"; - cursor.style.top = pos.top + "px"; - cursor.style.height = Math.max(0, pos.bottom - pos.top) * cm.options.cursorHeight + "px"; + var cursor = output.appendChild(elt("div", "\u00a0", "CodeMirror-cursor")) + cursor.style.left = pos.left + "px" + cursor.style.top = pos.top + "px" + cursor.style.height = Math.max(0, pos.bottom - pos.top) * cm.options.cursorHeight + "px" if (pos.other) { // Secondary cursor, shown when on a 'jump' in bi-directional text - var otherCursor = output.appendChild(elt("div", "\u00a0", "CodeMirror-cursor CodeMirror-secondarycursor")); - 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"; + var otherCursor = output.appendChild(elt("div", "\u00a0", "CodeMirror-cursor CodeMirror-secondarycursor")) + 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" } } // Draws the given range as a highlighted selection function drawSelectionRange(cm, range, output) { - var display = cm.display, doc = cm.doc; - var fragment = document.createDocumentFragment(); - var padding = paddingH(cm.display), leftSide = padding.left; - var rightSide = Math.max(display.sizerWidth, displayWidth(cm) - display.sizer.offsetLeft) - padding.right; + var display = cm.display, doc = cm.doc + var fragment = document.createDocumentFragment() + var padding = paddingH(cm.display), leftSide = padding.left + var rightSide = Math.max(display.sizerWidth, displayWidth(cm) - display.sizer.offsetLeft) - padding.right function add(left, top, width, bottom) { - if (top < 0) top = 0; - top = Math.round(top); - bottom = Math.round(bottom); + if (top < 0) top = 0 + top = Math.round(top) + bottom = Math.round(bottom) fragment.appendChild(elt("div", null, "CodeMirror-selected", "position: absolute; left: " + left + "px; top: " + top + "px; width: " + (width == null ? rightSide - left : width) + - "px; height: " + (bottom - top) + "px")); + "px; height: " + (bottom - top) + "px")) } function drawForLine(line, fromArg, toArg) { - var lineObj = getLine(doc, line); - var lineLen = lineObj.text.length; - var start, end; + var lineObj = getLine(doc, line) + var lineLen = lineObj.text.length + var start, end function coords(ch, bias) { - return charCoords(cm, Pos(line, ch), "div", lineObj, 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, "left"), rightPos, left, right; + var leftPos = coords(from, "left"), rightPos, left, right if (from == to) { - rightPos = leftPos; - left = right = leftPos.left; + rightPos = leftPos + left = right = leftPos.left } else { - rightPos = coords(to - 1, "right"); - if (dir == "rtl") { var tmp = leftPos; leftPos = rightPos; rightPos = tmp; } - left = leftPos.left; - right = rightPos.right; + rightPos = coords(to - 1, "right") + if (dir == "rtl") { var tmp = leftPos; leftPos = rightPos; rightPos = tmp } + left = leftPos.left + right = rightPos.right } - if (fromArg == null && from == 0) left = leftSide; + if (fromArg == null && from == 0) left = leftSide if (rightPos.top - leftPos.top > 3) { // Different lines, draw top part - add(left, leftPos.top, null, leftPos.bottom); - left = leftSide; - if (leftPos.bottom < rightPos.top) add(left, leftPos.bottom, null, rightPos.top); + add(left, leftPos.top, null, leftPos.bottom) + left = leftSide + if (leftPos.bottom < rightPos.top) add(left, leftPos.bottom, null, rightPos.top) } - if (toArg == null && to == lineLen) right = rightSide; + if (toArg == null && to == lineLen) right = rightSide if (!start || leftPos.top < start.top || leftPos.top == start.top && leftPos.left < start.left) - start = leftPos; + start = leftPos if (!end || rightPos.bottom > end.bottom || rightPos.bottom == end.bottom && rightPos.right > end.right) - end = rightPos; - if (left < leftSide + 1) left = leftSide; - add(left, rightPos.top, right - left, rightPos.bottom); - }); - return {start: start, end: end}; + end = rightPos + if (left < leftSide + 1) left = leftSide + add(left, rightPos.top, right - left, rightPos.bottom) + }) + return {start: start, end: end} } - var sFrom = range.from(), sTo = range.to(); + var sFrom = range.from(), sTo = range.to() if (sFrom.line == sTo.line) { - drawForLine(sFrom.line, sFrom.ch, sTo.ch); + drawForLine(sFrom.line, sFrom.ch, sTo.ch) } else { - var fromLine = getLine(doc, sFrom.line), toLine = getLine(doc, sTo.line); - var singleVLine = visualLine(fromLine) == visualLine(toLine); - var leftEnd = drawForLine(sFrom.line, sFrom.ch, singleVLine ? fromLine.text.length + 1 : null).end; - var rightStart = drawForLine(sTo.line, singleVLine ? 0 : null, sTo.ch).start; + var fromLine = getLine(doc, sFrom.line), toLine = getLine(doc, sTo.line) + var singleVLine = visualLine(fromLine) == visualLine(toLine) + var leftEnd = drawForLine(sFrom.line, sFrom.ch, singleVLine ? fromLine.text.length + 1 : null).end + var rightStart = drawForLine(sTo.line, singleVLine ? 0 : null, sTo.ch).start if (singleVLine) { if (leftEnd.top < rightStart.top - 2) { - add(leftEnd.right, leftEnd.top, null, leftEnd.bottom); - add(leftSide, rightStart.top, rightStart.left, rightStart.bottom); + add(leftEnd.right, leftEnd.top, null, leftEnd.bottom) + add(leftSide, rightStart.top, rightStart.left, rightStart.bottom) } else { - add(leftEnd.right, leftEnd.top, rightStart.left - leftEnd.right, leftEnd.bottom); + add(leftEnd.right, leftEnd.top, rightStart.left - leftEnd.right, leftEnd.bottom) } } if (leftEnd.bottom < rightStart.top) - add(leftSide, leftEnd.bottom, null, rightStart.top); + add(leftSide, leftEnd.bottom, null, rightStart.top) } - output.appendChild(fragment); + output.appendChild(fragment) } // Cursor-blinking export function restartBlink(cm) { - if (!cm.state.focused) return; - var display = cm.display; - clearInterval(display.blinker); - var on = true; - display.cursorDiv.style.visibility = ""; + if (!cm.state.focused) return + var display = cm.display + clearInterval(display.blinker) + var on = true + display.cursorDiv.style.visibility = "" if (cm.options.cursorBlinkRate > 0) display.blinker = setInterval(function() { - display.cursorDiv.style.visibility = (on = !on) ? "" : "hidden"; - }, cm.options.cursorBlinkRate); + display.cursorDiv.style.visibility = (on = !on) ? "" : "hidden" + }, cm.options.cursorBlinkRate) else if (cm.options.cursorBlinkRate < 0) - display.cursorDiv.style.visibility = "hidden"; + display.cursorDiv.style.visibility = "hidden" } diff --git a/src/display/update_display.js b/src/display/update_display.js index c69f75f9..06551eec 100644 --- a/src/display/update_display.js +++ b/src/display/update_display.js @@ -1,54 +1,54 @@ -import { sawCollapsedSpans } from "../line/saw_special_spans"; -import { heightAtLine, visualLineEndNo, visualLineNo } from "../line/spans"; -import { getLine, lineNumberFor } from "../line/utils_line"; -import { displayHeight, displayWidth, getDimensions, paddingVert, scrollGap } from "../measurement/position_measurement"; -import { buildLineElement, updateLineForChanges } from "../measurement/update_line"; -import { mac, webkit } from "../util/browser"; -import { activeElt, removeChildren } from "../util/dom"; -import { hasHandler, signal } from "../util/event"; -import { indexOf } from "../util/misc"; - -import { startWorker } from "./highlight_worker"; -import { maybeUpdateLineNumberWidth } from "./line_numbers"; -import { measureForScrollbars, updateScrollbars } from "./scrollbars"; -import { updateSelection } from "./selection"; -import { updateHeightsInViewport, visibleLines } from "./update_lines"; -import { adjustView, countDirtyView, resetView } from "./view_tracking"; +import { sawCollapsedSpans } from "../line/saw_special_spans" +import { heightAtLine, visualLineEndNo, visualLineNo } from "../line/spans" +import { getLine, lineNumberFor } from "../line/utils_line" +import { displayHeight, displayWidth, getDimensions, paddingVert, scrollGap } from "../measurement/position_measurement" +import { buildLineElement, updateLineForChanges } from "../measurement/update_line" +import { mac, webkit } from "../util/browser" +import { activeElt, removeChildren } from "../util/dom" +import { hasHandler, signal } from "../util/event" +import { indexOf } from "../util/misc" + +import { startWorker } from "./highlight_worker" +import { maybeUpdateLineNumberWidth } from "./line_numbers" +import { measureForScrollbars, updateScrollbars } from "./scrollbars" +import { updateSelection } from "./selection" +import { updateHeightsInViewport, visibleLines } from "./update_lines" +import { adjustView, countDirtyView, resetView } from "./view_tracking" // DISPLAY DRAWING export function DisplayUpdate(cm, viewport, force) { - var display = cm.display; + var display = cm.display - this.viewport = viewport; + 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.wrapperWidth = display.wrapper.clientWidth; - this.oldDisplayWidth = displayWidth(cm); - this.force = force; - this.dims = getDimensions(cm); - this.events = []; + this.visible = visibleLines(display, cm.doc, viewport) + this.editorIsHidden = !display.wrapper.offsetWidth + this.wrapperHeight = display.wrapper.clientHeight + this.wrapperWidth = display.wrapper.clientWidth + this.oldDisplayWidth = displayWidth(cm) + this.force = force + this.dims = getDimensions(cm) + this.events = [] } DisplayUpdate.prototype.signal = function(emitter, type) { if (hasHandler(emitter, type)) - this.events.push(arguments); -}; + this.events.push(arguments) +} DisplayUpdate.prototype.finish = function() { for (var i = 0; i < this.events.length; i++) - signal.apply(null, this.events[i]); -}; + signal.apply(null, this.events[i]) +} export function maybeClipScrollbars(cm) { - var display = cm.display; + var display = cm.display if (!display.scrollbarsClipped && display.scroller.offsetWidth) { - display.nativeBarWidth = display.scroller.offsetWidth - display.scroller.clientWidth; - display.heightForcer.style.height = scrollGap(cm) + "px"; - display.sizer.style.marginBottom = -display.nativeBarWidth + "px"; - display.sizer.style.borderRightWidth = scrollGap(cm) + "px"; - display.scrollbarsClipped = true; + display.nativeBarWidth = display.scroller.offsetWidth - display.scroller.clientWidth + display.heightForcer.style.height = scrollGap(cm) + "px" + display.sizer.style.marginBottom = -display.nativeBarWidth + "px" + display.sizer.style.borderRightWidth = scrollGap(cm) + "px" + display.scrollbarsClipped = true } } @@ -56,11 +56,11 @@ export function maybeClipScrollbars(cm) { // (returning false) when there is nothing to be done and forced is // false. export function updateDisplayIfNeeded(cm, update) { - var display = cm.display, doc = cm.doc; + var display = cm.display, doc = cm.doc if (update.editorIsHidden) { - resetView(cm); - return false; + resetView(cm) + return false } // Bail out if the visible area is already rendered and nothing changed. @@ -68,104 +68,104 @@ export function updateDisplayIfNeeded(cm, update) { update.visible.from >= display.viewFrom && update.visible.to <= display.viewTo && (display.updateLineNumbers == null || display.updateLineNumbers >= display.viewTo) && display.renderedView == display.view && countDirtyView(cm) == 0) - return false; + return false if (maybeUpdateLineNumberWidth(cm)) { - resetView(cm); - update.dims = getDimensions(cm); + resetView(cm) + update.dims = getDimensions(cm) } // Compute a suitable new viewport (from & to) - var end = doc.first + doc.size; - 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); + var end = doc.first + doc.size + 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) { - from = visualLineNo(cm.doc, from); - to = visualLineEndNo(cm.doc, to); + from = visualLineNo(cm.doc, from) + to = visualLineEndNo(cm.doc, to) } var different = from != display.viewFrom || to != display.viewTo || - display.lastWrapHeight != update.wrapperHeight || display.lastWrapWidth != update.wrapperWidth; - adjustView(cm, from, to); + display.lastWrapHeight != update.wrapperHeight || display.lastWrapWidth != update.wrapperWidth + adjustView(cm, from, to) - display.viewOffset = heightAtLine(getLine(cm.doc, display.viewFrom)); + display.viewOffset = heightAtLine(getLine(cm.doc, display.viewFrom)) // Position the mover div to align with the current scroll position - cm.display.mover.style.top = display.viewOffset + "px"; + cm.display.mover.style.top = display.viewOffset + "px" - var toUpdate = countDirtyView(cm); + var toUpdate = countDirtyView(cm) if (!different && toUpdate == 0 && !update.force && display.renderedView == display.view && (display.updateLineNumbers == null || display.updateLineNumbers >= display.viewTo)) - return false; + 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, update.dims); - if (toUpdate > 4) display.lineDiv.style.display = ""; - display.renderedView = display.view; + var focused = activeElt() + if (toUpdate > 4) display.lineDiv.style.display = "none" + patchDisplay(cm, display.updateLineNumbers, update.dims) + if (toUpdate > 4) display.lineDiv.style.display = "" + display.renderedView = display.view // There might have been a widget with a focused element that got // hidden or updated, if so re-focus it. - if (focused && activeElt() != focused && focused.offsetHeight) focused.focus(); + if (focused && activeElt() != focused && focused.offsetHeight) focused.focus() // Prevent selection and cursors from interfering with the scroll // width and height. - removeChildren(display.cursorDiv); - removeChildren(display.selectionDiv); - display.gutters.style.height = display.sizer.style.minHeight = 0; + removeChildren(display.cursorDiv) + removeChildren(display.selectionDiv) + display.gutters.style.height = display.sizer.style.minHeight = 0 if (different) { - display.lastWrapHeight = update.wrapperHeight; - display.lastWrapWidth = update.wrapperWidth; - startWorker(cm, 400); + display.lastWrapHeight = update.wrapperHeight + display.lastWrapWidth = update.wrapperWidth + startWorker(cm, 400) } - display.updateLineNumbers = null; + display.updateLineNumbers = null - return true; + return true } export function postUpdateDisplay(cm, update) { - var viewport = update.viewport; + var viewport = update.viewport for (var first = true;; first = false) { if (!first || !cm.options.lineWrapping || update.oldDisplayWidth == displayWidth(cm)) { // Clip forced viewport to actual scrollable area. if (viewport && viewport.top != null) - viewport = {top: Math.min(cm.doc.height + paddingVert(cm.display) - displayHeight(cm), viewport.top)}; + viewport = {top: Math.min(cm.doc.height + paddingVert(cm.display) - displayHeight(cm), viewport.top)} // Updated line heights might result in the drawn area not // actually covering the viewport. Keep looping until it does. - update.visible = visibleLines(cm.display, cm.doc, viewport); + update.visible = visibleLines(cm.display, cm.doc, viewport) if (update.visible.from >= cm.display.viewFrom && update.visible.to <= cm.display.viewTo) - break; + break } - if (!updateDisplayIfNeeded(cm, update)) break; - updateHeightsInViewport(cm); - var barMeasure = measureForScrollbars(cm); - updateSelection(cm); - updateScrollbars(cm, barMeasure); - setDocumentHeight(cm, barMeasure); + if (!updateDisplayIfNeeded(cm, update)) break + updateHeightsInViewport(cm) + var barMeasure = measureForScrollbars(cm) + updateSelection(cm) + updateScrollbars(cm, barMeasure) + setDocumentHeight(cm, barMeasure) } - update.signal(cm, "update", cm); + update.signal(cm, "update", cm) if (cm.display.viewFrom != cm.display.reportedViewFrom || cm.display.viewTo != cm.display.reportedViewTo) { - update.signal(cm, "viewportChange", cm, cm.display.viewFrom, cm.display.viewTo); - cm.display.reportedViewFrom = cm.display.viewFrom; cm.display.reportedViewTo = cm.display.viewTo; + update.signal(cm, "viewportChange", cm, cm.display.viewFrom, cm.display.viewTo) + cm.display.reportedViewFrom = cm.display.viewFrom; cm.display.reportedViewTo = cm.display.viewTo } } export function updateDisplaySimple(cm, viewport) { - var update = new DisplayUpdate(cm, viewport); + var update = new DisplayUpdate(cm, viewport) if (updateDisplayIfNeeded(cm, update)) { - updateHeightsInViewport(cm); - postUpdateDisplay(cm, update); - var barMeasure = measureForScrollbars(cm); - updateSelection(cm); - updateScrollbars(cm, barMeasure); - setDocumentHeight(cm, barMeasure); - update.finish(); + updateHeightsInViewport(cm) + postUpdateDisplay(cm, update) + var barMeasure = measureForScrollbars(cm) + updateSelection(cm) + updateScrollbars(cm, barMeasure) + setDocumentHeight(cm, barMeasure) + update.finish() } } @@ -174,54 +174,54 @@ export function updateDisplaySimple(cm, viewport) { // that are not there yet, and updating the ones that are out of // date. function patchDisplay(cm, updateNumbersFrom, dims) { - var display = cm.display, lineNumbers = cm.options.lineNumbers; - var container = display.lineDiv, cur = container.firstChild; + var display = cm.display, lineNumbers = cm.options.lineNumbers + var container = display.lineDiv, cur = container.firstChild function rm(node) { - var next = node.nextSibling; + var next = node.nextSibling // Works around a throw-scroll bug in OS X Webkit if (webkit && mac && cm.display.currentWheelTarget == node) - node.style.display = "none"; + node.style.display = "none" else - node.parentNode.removeChild(node); - return next; + node.parentNode.removeChild(node) + return next } - var view = display.view, lineN = display.viewFrom; + var view = display.view, lineN = display.viewFrom // Loop over the elements in the view, syncing cur (the DOM nodes // in display.lineDiv) with the view as we go. for (var i = 0; i < view.length; i++) { - var lineView = view[i]; + var lineView = view[i] if (lineView.hidden) { } else if (!lineView.node || lineView.node.parentNode != container) { // Not drawn yet - var node = buildLineElement(cm, lineView, lineN, dims); - container.insertBefore(node, cur); + var node = buildLineElement(cm, lineView, lineN, dims) + container.insertBefore(node, cur) } else { // Already drawn - while (cur != lineView.node) cur = rm(cur); + while (cur != lineView.node) cur = rm(cur) var updateNumber = lineNumbers && updateNumbersFrom != null && - updateNumbersFrom <= lineN && lineView.lineNumber; + updateNumbersFrom <= lineN && lineView.lineNumber if (lineView.changes) { - if (indexOf(lineView.changes, "gutter") > -1) updateNumber = false; - updateLineForChanges(cm, lineView, lineN, dims); + if (indexOf(lineView.changes, "gutter") > -1) updateNumber = false + updateLineForChanges(cm, lineView, lineN, dims) } if (updateNumber) { - removeChildren(lineView.lineNumber); - lineView.lineNumber.appendChild(document.createTextNode(lineNumberFor(cm.options, lineN))); + removeChildren(lineView.lineNumber) + lineView.lineNumber.appendChild(document.createTextNode(lineNumberFor(cm.options, lineN))) } - cur = lineView.node.nextSibling; + cur = lineView.node.nextSibling } - lineN += lineView.size; + lineN += lineView.size } - while (cur) cur = rm(cur); + while (cur) cur = rm(cur) } export function updateGutterSpace(cm) { - var width = cm.display.gutters.offsetWidth; - cm.display.sizer.style.marginLeft = width + "px"; + var width = cm.display.gutters.offsetWidth + cm.display.sizer.style.marginLeft = width + "px" } export function setDocumentHeight(cm, measure) { - cm.display.sizer.style.minHeight = measure.docHeight + "px"; - cm.display.heightForcer.style.top = measure.docHeight + "px"; - cm.display.gutters.style.height = (measure.docHeight + cm.display.barHeight + scrollGap(cm)) + "px"; + cm.display.sizer.style.minHeight = measure.docHeight + "px" + cm.display.heightForcer.style.top = measure.docHeight + "px" + cm.display.gutters.style.height = (measure.docHeight + cm.display.barHeight + scrollGap(cm)) + "px" } diff --git a/src/display/update_lines.js b/src/display/update_lines.js index e10d8b9c..8de0698c 100644 --- a/src/display/update_lines.js +++ b/src/display/update_lines.js @@ -1,31 +1,31 @@ -import { heightAtLine } from "../line/spans"; -import { getLine, lineAtHeight, updateLineHeight } from "../line/utils_line"; -import { paddingTop, textHeight } from "../measurement/position_measurement"; -import { ie, ie_version } from "../util/browser"; +import { heightAtLine } from "../line/spans" +import { getLine, lineAtHeight, updateLineHeight } from "../line/utils_line" +import { paddingTop, textHeight } from "../measurement/position_measurement" +import { ie, ie_version } from "../util/browser" // Read the actual heights of the rendered lines, and update their // stored heights to match. export function updateHeightsInViewport(cm) { - var display = cm.display; - var prevBottom = display.lineDiv.offsetTop; + var display = cm.display + var prevBottom = display.lineDiv.offsetTop for (var i = 0; i < display.view.length; i++) { - var cur = display.view[i], height; - if (cur.hidden) continue; + var cur = display.view[i], height + if (cur.hidden) continue if (ie && ie_version < 8) { - var bot = cur.node.offsetTop + cur.node.offsetHeight; - height = bot - prevBottom; - prevBottom = bot; + var bot = cur.node.offsetTop + cur.node.offsetHeight + height = bot - prevBottom + prevBottom = bot } else { - var box = cur.node.getBoundingClientRect(); - height = box.bottom - box.top; + var box = cur.node.getBoundingClientRect() + height = box.bottom - box.top } - var diff = cur.line.height - height; - if (height < 2) height = textHeight(display); + var diff = cur.line.height - height + if (height < 2) height = textHeight(display) if (diff > .001 || diff < -.001) { - updateLineHeight(cur.line, height); - updateWidgetHeight(cur.line); + updateLineHeight(cur.line, height) + updateWidgetHeight(cur.line) if (cur.rest) for (var j = 0; j < cur.rest.length; j++) - updateWidgetHeight(cur.rest[j]); + updateWidgetHeight(cur.rest[j]) } } } @@ -34,29 +34,29 @@ export function updateHeightsInViewport(cm) { // given line. function updateWidgetHeight(line) { if (line.widgets) for (var i = 0; i < line.widgets.length; ++i) - line.widgets[i].height = line.widgets[i].node.parentNode.offsetHeight; + line.widgets[i].height = line.widgets[i].node.parentNode.offsetHeight } // Compute the lines that are visible in a given viewport (defaults // the the current scroll position). viewport may contain top, // height, and ensure (see op.scrollToPos) properties. export 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 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 from = lineAtHeight(doc, top), to = lineAtHeight(doc, bottom); + 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; + var ensureFrom = viewport.ensure.from.line, ensureTo = viewport.ensure.to.line if (ensureFrom < from) { - from = ensureFrom; - to = lineAtHeight(doc, heightAtLine(getLine(doc, ensureFrom)) + display.wrapper.clientHeight); + from = ensureFrom + to = lineAtHeight(doc, heightAtLine(getLine(doc, ensureFrom)) + display.wrapper.clientHeight) } else if (Math.min(ensureTo, doc.lastLine()) >= to) { - from = lineAtHeight(doc, heightAtLine(getLine(doc, ensureTo)) - display.wrapper.clientHeight); - to = ensureTo; + from = lineAtHeight(doc, heightAtLine(getLine(doc, ensureTo)) - display.wrapper.clientHeight) + to = ensureTo } } - return {from: from, to: Math.max(to, from + 1)}; + return {from: from, to: Math.max(to, from + 1)} } diff --git a/src/display/view_tracking.js b/src/display/view_tracking.js index fd535178..d7ca682b 100644 --- a/src/display/view_tracking.js +++ b/src/display/view_tracking.js @@ -1,8 +1,8 @@ -import { buildViewArray } from "../line/line_data"; -import { sawCollapsedSpans } from "../line/saw_special_spans"; -import { visualLineEndNo, visualLineNo } from "../line/spans"; -import { findViewIndex } from "../measurement/position_measurement"; -import { indexOf } from "../util/misc"; +import { buildViewArray } from "../line/line_data" +import { sawCollapsedSpans } from "../line/saw_special_spans" +import { visualLineEndNo, visualLineNo } from "../line/spans" +import { findViewIndex } from "../measurement/position_measurement" +import { indexOf } from "../util/misc" // Updates the display.view data structure for a given change to the // document. From and to are in pre-change coordinates. Lendiff is @@ -11,142 +11,142 @@ import { indexOf } from "../util/misc"; // lines are divided into visual lines. regLineChange (below) // registers single-line changes. export function regChange(cm, from, to, lendiff) { - if (from == null) from = cm.doc.first; - if (to == null) to = cm.doc.first + cm.doc.size; - if (!lendiff) lendiff = 0; + if (from == null) from = cm.doc.first + if (to == null) to = cm.doc.first + cm.doc.size + if (!lendiff) lendiff = 0 - var display = cm.display; + var display = cm.display if (lendiff && to < display.viewTo && (display.updateLineNumbers == null || display.updateLineNumbers > from)) - display.updateLineNumbers = from; + display.updateLineNumbers = from - cm.curOp.viewChanged = true; + cm.curOp.viewChanged = true if (from >= display.viewTo) { // Change after if (sawCollapsedSpans && visualLineNo(cm.doc, from) < display.viewTo) - resetView(cm); + resetView(cm) } else if (to <= display.viewFrom) { // Change before if (sawCollapsedSpans && visualLineEndNo(cm.doc, to + lendiff) > display.viewFrom) { - resetView(cm); + resetView(cm) } else { - display.viewFrom += lendiff; - display.viewTo += lendiff; + display.viewFrom += lendiff + display.viewTo += lendiff } } else if (from <= display.viewFrom && to >= display.viewTo) { // Full overlap - resetView(cm); + resetView(cm) } else if (from <= display.viewFrom) { // Top overlap - var cut = viewCuttingPoint(cm, to, to + lendiff, 1); + var cut = viewCuttingPoint(cm, to, to + lendiff, 1) if (cut) { - display.view = display.view.slice(cut.index); - display.viewFrom = cut.lineN; - display.viewTo += lendiff; + display.view = display.view.slice(cut.index) + display.viewFrom = cut.lineN + display.viewTo += lendiff } else { - resetView(cm); + resetView(cm) } } else if (to >= display.viewTo) { // Bottom overlap - var cut = viewCuttingPoint(cm, from, from, -1); + var cut = viewCuttingPoint(cm, from, from, -1) if (cut) { - display.view = display.view.slice(0, cut.index); - display.viewTo = cut.lineN; + display.view = display.view.slice(0, cut.index) + display.viewTo = cut.lineN } else { - resetView(cm); + resetView(cm) } } else { // Gap in the middle - var cutTop = viewCuttingPoint(cm, from, from, -1); - var cutBot = viewCuttingPoint(cm, to, to + lendiff, 1); + var cutTop = viewCuttingPoint(cm, from, from, -1) + var cutBot = viewCuttingPoint(cm, to, to + lendiff, 1) if (cutTop && cutBot) { display.view = display.view.slice(0, cutTop.index) .concat(buildViewArray(cm, cutTop.lineN, cutBot.lineN)) - .concat(display.view.slice(cutBot.index)); - display.viewTo += lendiff; + .concat(display.view.slice(cutBot.index)) + display.viewTo += lendiff } else { - resetView(cm); + resetView(cm) } } - var ext = display.externalMeasured; + var ext = display.externalMeasured if (ext) { if (to < ext.lineN) - ext.lineN += lendiff; + ext.lineN += lendiff else if (from < ext.lineN + ext.size) - display.externalMeasured = null; + display.externalMeasured = null } } // Register a change to a single line. Type must be one of "text", // "gutter", "class", "widget" export function regLineChange(cm, line, type) { - cm.curOp.viewChanged = true; - var display = cm.display, ext = cm.display.externalMeasured; + cm.curOp.viewChanged = true + var display = cm.display, ext = cm.display.externalMeasured if (ext && line >= ext.lineN && line < ext.lineN + ext.size) - display.externalMeasured = null; + display.externalMeasured = null - if (line < display.viewFrom || line >= display.viewTo) return; - var lineView = display.view[findViewIndex(cm, line)]; - if (lineView.node == null) return; - var arr = lineView.changes || (lineView.changes = []); - if (indexOf(arr, type) == -1) arr.push(type); + if (line < display.viewFrom || line >= display.viewTo) return + var lineView = display.view[findViewIndex(cm, line)] + if (lineView.node == null) return + var arr = lineView.changes || (lineView.changes = []) + if (indexOf(arr, type) == -1) arr.push(type) } // Clear the view. export function resetView(cm) { - cm.display.viewFrom = cm.display.viewTo = cm.doc.first; - cm.display.view = []; - cm.display.viewOffset = 0; + cm.display.viewFrom = cm.display.viewTo = cm.doc.first + cm.display.view = [] + cm.display.viewOffset = 0 } function viewCuttingPoint(cm, oldN, newN, dir) { - var index = findViewIndex(cm, oldN), diff, view = cm.display.view; + var index = findViewIndex(cm, oldN), diff, view = cm.display.view if (!sawCollapsedSpans || newN == cm.doc.first + cm.doc.size) - return {index: index, lineN: newN}; + return {index: index, lineN: newN} for (var i = 0, n = cm.display.viewFrom; i < index; i++) - n += view[i].size; + n += view[i].size if (n != oldN) { if (dir > 0) { - if (index == view.length - 1) return null; - diff = (n + view[index].size) - oldN; - index++; + if (index == view.length - 1) return null + diff = (n + view[index].size) - oldN + index++ } else { - diff = n - oldN; + diff = n - oldN } - oldN += diff; newN += diff; + oldN += diff; newN += diff } while (visualLineNo(cm.doc, newN) != newN) { - if (index == (dir < 0 ? 0 : view.length - 1)) return null; - newN += dir * view[index - (dir < 0 ? 1 : 0)].size; - index += dir; + if (index == (dir < 0 ? 0 : view.length - 1)) return null + newN += dir * view[index - (dir < 0 ? 1 : 0)].size + index += dir } - return {index: index, lineN: newN}; + return {index: index, lineN: newN} } // Force the view to cover a given range, adding empty view element // or clipping off existing ones as needed. export function adjustView(cm, from, to) { - var display = cm.display, view = display.view; + var display = cm.display, view = display.view if (view.length == 0 || from >= display.viewTo || to <= display.viewFrom) { - display.view = buildViewArray(cm, from, to); - display.viewFrom = from; + display.view = buildViewArray(cm, from, to) + display.viewFrom = from } else { if (display.viewFrom > from) - display.view = buildViewArray(cm, from, display.viewFrom).concat(display.view); + display.view = buildViewArray(cm, from, display.viewFrom).concat(display.view) else if (display.viewFrom < from) - display.view = display.view.slice(findViewIndex(cm, from)); - display.viewFrom = from; + display.view = display.view.slice(findViewIndex(cm, from)) + display.viewFrom = from if (display.viewTo < to) - display.view = display.view.concat(buildViewArray(cm, display.viewTo, to)); + display.view = display.view.concat(buildViewArray(cm, display.viewTo, to)) else if (display.viewTo > to) - display.view = display.view.slice(0, findViewIndex(cm, to)); + display.view = display.view.slice(0, findViewIndex(cm, to)) } - display.viewTo = to; + display.viewTo = to } // Count the number of lines in the view whose DOM representation is // out of date (or nonexistent). export function countDirtyView(cm) { - var view = cm.display.view, dirty = 0; + var view = cm.display.view, dirty = 0 for (var i = 0; i < view.length; i++) { - var lineView = view[i]; - if (!lineView.hidden && (!lineView.node || lineView.changes)) ++dirty; + var lineView = view[i] + if (!lineView.hidden && (!lineView.node || lineView.changes)) ++dirty } - return dirty; + return dirty } diff --git a/src/edit/CodeMirror.js b/src/edit/CodeMirror.js index 7bd6a60a..ee549d54 100644 --- a/src/edit/CodeMirror.js +++ b/src/edit/CodeMirror.js @@ -1,52 +1,52 @@ -import { Display } from "../display/Display"; -import { onFocus, onBlur } from "../display/focus"; -import { setGuttersForLineNumbers, updateGutters } from "../display/gutters"; -import { maybeUpdateLineNumberWidth } from "../display/line_numbers"; -import { endOperation, operation, startOperation } from "../display/operations"; -import { initScrollbars } from "../display/scrollbars"; -import { onScrollWheel, setScrollLeft, setScrollTop } from "../display/scroll_events"; -import { clipPos, Pos } from "../line/pos"; -import { posFromMouse } from "../measurement/position_measurement"; -import { eventInWidget } from "../measurement/widgets"; -import Doc from "../model/Doc"; -import { attachDoc } from "../model/document_data"; -import { Range } from "../model/selection"; -import { extendSelection } from "../model/selection_updates"; -import { captureRightClick, ie, ie_version, mobile, webkit } from "../util/browser"; -import { e_preventDefault, e_stop, on, signal, signalDOMEvent } from "../util/event"; -import { bind, copyObj, Delayed } from "../util/misc"; - -import { clearDragCursor, onDragOver, onDragStart, onDrop } from "./drop_events"; -import { ensureGlobalHandlers } from "./global_events"; -import { onKeyDown, onKeyPress, onKeyUp } from "./key_events"; -import { clickInGutter, onContextMenu, onMouseDown } from "./mouse_events"; -import { themeChanged } from "./utils"; -import { defaults, optionHandlers, Init } from "./options"; +import { Display } from "../display/Display" +import { onFocus, onBlur } from "../display/focus" +import { setGuttersForLineNumbers, updateGutters } from "../display/gutters" +import { maybeUpdateLineNumberWidth } from "../display/line_numbers" +import { endOperation, operation, startOperation } from "../display/operations" +import { initScrollbars } from "../display/scrollbars" +import { onScrollWheel, setScrollLeft, setScrollTop } from "../display/scroll_events" +import { clipPos, Pos } from "../line/pos" +import { posFromMouse } from "../measurement/position_measurement" +import { eventInWidget } from "../measurement/widgets" +import Doc from "../model/Doc" +import { attachDoc } from "../model/document_data" +import { Range } from "../model/selection" +import { extendSelection } from "../model/selection_updates" +import { captureRightClick, ie, ie_version, mobile, webkit } from "../util/browser" +import { e_preventDefault, e_stop, on, signal, signalDOMEvent } from "../util/event" +import { bind, copyObj, Delayed } from "../util/misc" + +import { clearDragCursor, onDragOver, onDragStart, onDrop } from "./drop_events" +import { ensureGlobalHandlers } from "./global_events" +import { onKeyDown, onKeyPress, onKeyUp } from "./key_events" +import { clickInGutter, onContextMenu, onMouseDown } from "./mouse_events" +import { themeChanged } from "./utils" +import { defaults, optionHandlers, Init } from "./options" // A CodeMirror instance represents an editor. This is the object // that user code is usually dealing with. export function CodeMirror(place, options) { - if (!(this instanceof CodeMirror)) return new CodeMirror(place, options); + if (!(this instanceof CodeMirror)) return new CodeMirror(place, options) - this.options = options = options ? copyObj(options) : {}; + this.options = options = options ? copyObj(options) : {} // Determine effective options based on given values and defaults. - copyObj(defaults, options, false); - setGuttersForLineNumbers(options); - - var doc = options.value; - if (typeof doc == "string") doc = new Doc(doc, options.mode, null, options.lineSeparator); - this.doc = doc; - - var input = new CodeMirror.inputStyles[options.inputStyle](this); - var display = this.display = new Display(place, doc, input); - display.wrapper.CodeMirror = this; - updateGutters(this); - themeChanged(this); + copyObj(defaults, options, false) + setGuttersForLineNumbers(options) + + var doc = options.value + if (typeof doc == "string") doc = new Doc(doc, options.mode, null, options.lineSeparator) + this.doc = doc + + var input = new CodeMirror.inputStyles[options.inputStyle](this) + var display = this.display = new Display(place, doc, input) + display.wrapper.CodeMirror = this + updateGutters(this) + themeChanged(this) if (options.lineWrapping) - this.display.wrapper.className += " CodeMirror-wrap"; - if (options.autofocus && !mobile) display.input.focus(); - initScrollbars(this); + this.display.wrapper.className += " CodeMirror-wrap" + if (options.autofocus && !mobile) display.input.focus() + initScrollbars(this) this.state = { keyMaps: [], // stores maps added by addKeyMap @@ -62,152 +62,152 @@ export function CodeMirror(place, options) { highlight: new Delayed(), // stores highlight worker timeout keySeq: null, // Unfinished key sequence specialChars: null - }; + } - var cm = this; + var cm = this // Override magic textarea content restore that IE sometimes does // on our hidden textarea on reload - if (ie && ie_version < 11) setTimeout(function() { cm.display.input.reset(true); }, 20); + if (ie && ie_version < 11) setTimeout(function() { cm.display.input.reset(true) }, 20) - registerEventHandlers(this); - ensureGlobalHandlers(); + registerEventHandlers(this) + ensureGlobalHandlers() - startOperation(this); - this.curOp.forceUpdate = true; - attachDoc(this, doc); + startOperation(this) + this.curOp.forceUpdate = true + attachDoc(this, doc) if ((options.autofocus && !mobile) || cm.hasFocus()) - setTimeout(bind(onFocus, this), 20); + setTimeout(bind(onFocus, this), 20) else - onBlur(this); + onBlur(this) for (var opt in optionHandlers) if (optionHandlers.hasOwnProperty(opt)) - optionHandlers[opt](this, options[opt], Init); - maybeUpdateLineNumberWidth(this); - if (options.finishInit) options.finishInit(this); - for (var i = 0; i < initHooks.length; ++i) initHooks[i](this); - endOperation(this); + optionHandlers[opt](this, options[opt], Init) + maybeUpdateLineNumberWidth(this) + if (options.finishInit) options.finishInit(this) + for (var i = 0; i < initHooks.length; ++i) initHooks[i](this) + endOperation(this) // Suppress optimizelegibility in Webkit, since it breaks text // measuring on line wrapping boundaries. if (webkit && options.lineWrapping && getComputedStyle(display.lineDiv).textRendering == "optimizelegibility") - display.lineDiv.style.textRendering = "auto"; + display.lineDiv.style.textRendering = "auto" } // The default configuration options. -CodeMirror.defaults = defaults; +CodeMirror.defaults = defaults // Functions to run when options are changed. -CodeMirror.optionHandlers = optionHandlers; +CodeMirror.optionHandlers = optionHandlers -export default CodeMirror; +export default CodeMirror // Attach the necessary event handlers when initializing the editor function registerEventHandlers(cm) { - var d = cm.display; - on(d.scroller, "mousedown", operation(cm, onMouseDown)); + var d = cm.display + on(d.scroller, "mousedown", operation(cm, onMouseDown)) // Older IE's will not fire a second mousedown for a double click if (ie && ie_version < 11) on(d.scroller, "dblclick", operation(cm, function(e) { - if (signalDOMEvent(cm, e)) return; - var pos = posFromMouse(cm, e); - if (!pos || clickInGutter(cm, e) || eventInWidget(cm.display, e)) return; - e_preventDefault(e); - var word = cm.findWordAt(pos); - extendSelection(cm.doc, word.anchor, word.head); - })); + if (signalDOMEvent(cm, e)) return + var pos = posFromMouse(cm, e) + if (!pos || clickInGutter(cm, e) || eventInWidget(cm.display, e)) return + e_preventDefault(e) + var word = cm.findWordAt(pos) + extendSelection(cm.doc, word.anchor, word.head) + })) else - on(d.scroller, "dblclick", function(e) { signalDOMEvent(cm, e) || e_preventDefault(e); }); + on(d.scroller, "dblclick", function(e) { signalDOMEvent(cm, e) || e_preventDefault(e) }) // Some browsers fire contextmenu *after* opening the menu, at // which point we can't mess with it anymore. Context menu is // handled in onMouseDown for these browsers. - if (!captureRightClick) on(d.scroller, "contextmenu", function(e) {onContextMenu(cm, e);}); + if (!captureRightClick) on(d.scroller, "contextmenu", function(e) {onContextMenu(cm, e)}) // Used to suppress mouse event handling when a touch happens - var touchFinished, prevTouch = {end: 0}; + var touchFinished, prevTouch = {end: 0} function finishTouch() { if (d.activeTouch) { - touchFinished = setTimeout(function() {d.activeTouch = null;}, 1000); - prevTouch = d.activeTouch; - prevTouch.end = +new Date; + touchFinished = setTimeout(function() {d.activeTouch = null}, 1000) + prevTouch = d.activeTouch + prevTouch.end = +new Date } } function isMouseLikeTouchEvent(e) { - if (e.touches.length != 1) return false; - var touch = e.touches[0]; - return touch.radiusX <= 1 && touch.radiusY <= 1; + if (e.touches.length != 1) return false + var touch = e.touches[0] + return touch.radiusX <= 1 && touch.radiusY <= 1 } function farAway(touch, other) { - if (other.left == null) return true; - var dx = other.left - touch.left, dy = other.top - touch.top; - return dx * dx + dy * dy > 20 * 20; + if (other.left == null) return true + var dx = other.left - touch.left, dy = other.top - touch.top + return dx * dx + dy * dy > 20 * 20 } on(d.scroller, "touchstart", function(e) { if (!signalDOMEvent(cm, e) && !isMouseLikeTouchEvent(e)) { - clearTimeout(touchFinished); - var now = +new Date; + clearTimeout(touchFinished) + var now = +new Date d.activeTouch = {start: now, moved: false, - prev: now - prevTouch.end <= 300 ? prevTouch : null}; + prev: now - prevTouch.end <= 300 ? prevTouch : null} if (e.touches.length == 1) { - d.activeTouch.left = e.touches[0].pageX; - d.activeTouch.top = e.touches[0].pageY; + d.activeTouch.left = e.touches[0].pageX + d.activeTouch.top = e.touches[0].pageY } } - }); + }) on(d.scroller, "touchmove", function() { - if (d.activeTouch) d.activeTouch.moved = true; - }); + if (d.activeTouch) d.activeTouch.moved = true + }) on(d.scroller, "touchend", function(e) { - var touch = d.activeTouch; + var touch = d.activeTouch if (touch && !eventInWidget(d, e) && touch.left != null && !touch.moved && new Date - touch.start < 300) { - var pos = cm.coordsChar(d.activeTouch, "page"), range; + var pos = cm.coordsChar(d.activeTouch, "page"), range if (!touch.prev || farAway(touch, touch.prev)) // Single tap - range = new Range(pos, pos); + range = new Range(pos, pos) else if (!touch.prev.prev || farAway(touch, touch.prev.prev)) // Double tap - range = cm.findWordAt(pos); + range = cm.findWordAt(pos) else // Triple tap - range = new Range(Pos(pos.line, 0), clipPos(cm.doc, Pos(pos.line + 1, 0))); - cm.setSelection(range.anchor, range.head); - cm.focus(); - e_preventDefault(e); + range = new Range(Pos(pos.line, 0), clipPos(cm.doc, Pos(pos.line + 1, 0))) + cm.setSelection(range.anchor, range.head) + cm.focus() + e_preventDefault(e) } - finishTouch(); - }); - on(d.scroller, "touchcancel", finishTouch); + finishTouch() + }) + on(d.scroller, "touchcancel", finishTouch) // Sync scrolling between fake scrollbars and real scrollable // area, ensure viewport is updated when scrolling. on(d.scroller, "scroll", function() { if (d.scroller.clientHeight) { - setScrollTop(cm, d.scroller.scrollTop); - setScrollLeft(cm, d.scroller.scrollLeft, true); - signal(cm, "scroll", cm); + setScrollTop(cm, d.scroller.scrollTop) + setScrollLeft(cm, d.scroller.scrollLeft, true) + signal(cm, "scroll", cm) } - }); + }) // Listen to wheel events in order to try and update the viewport on time. - on(d.scroller, "mousewheel", function(e){onScrollWheel(cm, e);}); - on(d.scroller, "DOMMouseScroll", function(e){onScrollWheel(cm, e);}); + on(d.scroller, "mousewheel", function(e){onScrollWheel(cm, e)}) + on(d.scroller, "DOMMouseScroll", function(e){onScrollWheel(cm, e)}) // Prevent wrapper from ever scrolling - on(d.wrapper, "scroll", function() { d.wrapper.scrollTop = d.wrapper.scrollLeft = 0; }); + on(d.wrapper, "scroll", function() { d.wrapper.scrollTop = d.wrapper.scrollLeft = 0 }) d.dragFunctions = { - enter: function(e) {if (!signalDOMEvent(cm, e)) e_stop(e);}, - over: function(e) {if (!signalDOMEvent(cm, e)) { onDragOver(cm, e); e_stop(e); }}, - start: function(e){onDragStart(cm, e);}, + enter: function(e) {if (!signalDOMEvent(cm, e)) e_stop(e)}, + over: function(e) {if (!signalDOMEvent(cm, e)) { onDragOver(cm, e); e_stop(e) }}, + start: function(e){onDragStart(cm, e)}, drop: operation(cm, onDrop), - leave: function(e) {if (!signalDOMEvent(cm, e)) { clearDragCursor(cm); }} - }; - - var inp = d.input.getField(); - on(inp, "keyup", function(e) { onKeyUp.call(cm, e); }); - on(inp, "keydown", operation(cm, onKeyDown)); - on(inp, "keypress", operation(cm, onKeyPress)); - on(inp, "focus", function(e) { onFocus(cm, e); }); - on(inp, "blur", function (e) { onBlur(cm, e); }); + leave: function(e) {if (!signalDOMEvent(cm, e)) { clearDragCursor(cm) }} + } + + var inp = d.input.getField() + on(inp, "keyup", function(e) { onKeyUp.call(cm, e) }) + on(inp, "keydown", operation(cm, onKeyDown)) + on(inp, "keypress", operation(cm, onKeyPress)) + on(inp, "focus", function(e) { onFocus(cm, e) }) + on(inp, "blur", function (e) { onBlur(cm, e) }) } -var initHooks = []; -CodeMirror.defineInitHook = function(f) {initHooks.push(f);}; +var initHooks = [] +CodeMirror.defineInitHook = function(f) {initHooks.push(f)} diff --git a/src/edit/commands.js b/src/edit/commands.js index 3e8735ec..2d670ab5 100644 --- a/src/edit/commands.js +++ b/src/edit/commands.js @@ -1,156 +1,156 @@ -import { deleteNearSelection } from "./deleteNearSelection"; -import { runInOp } from "../display/operations"; -import { ensureCursorVisible } from "../display/scrolling"; -import { clipPos, Pos } from "../line/pos"; -import { collapsedSpanAtEnd, visualLine } from "../line/spans"; -import { getLine, lineNo } from "../line/utils_line"; -import { Range } from "../model/selection"; -import { selectAll } from "../model/selection_updates"; -import { countColumn, sel_dontScroll, sel_move, spaceStr } from "../util/misc"; -import { getOrder, lineLeft, lineRight } from "../util/bidi"; +import { deleteNearSelection } from "./deleteNearSelection" +import { runInOp } from "../display/operations" +import { ensureCursorVisible } from "../display/scrolling" +import { clipPos, Pos } from "../line/pos" +import { collapsedSpanAtEnd, visualLine } from "../line/spans" +import { getLine, lineNo } from "../line/utils_line" +import { Range } from "../model/selection" +import { selectAll } from "../model/selection_updates" +import { countColumn, sel_dontScroll, sel_move, spaceStr } from "../util/misc" +import { getOrder, lineLeft, lineRight } from "../util/bidi" // Commands are parameter-less actions that can be performed on an // editor, mostly used for keybindings. export var commands = { selectAll: selectAll, singleSelection: function(cm) { - cm.setSelection(cm.getCursor("anchor"), cm.getCursor("head"), sel_dontScroll); + cm.setSelection(cm.getCursor("anchor"), cm.getCursor("head"), sel_dontScroll) }, killLine: function(cm) { deleteNearSelection(cm, function(range) { if (range.empty()) { - var len = getLine(cm.doc, range.head.line).text.length; + var len = getLine(cm.doc, range.head.line).text.length if (range.head.ch == len && range.head.line < cm.lastLine()) - return {from: range.head, to: Pos(range.head.line + 1, 0)}; + return {from: range.head, to: Pos(range.head.line + 1, 0)} else - return {from: range.head, to: Pos(range.head.line, len)}; + return {from: range.head, to: Pos(range.head.line, len)} } else { - return {from: range.from(), to: range.to()}; + return {from: range.from(), to: range.to()} } - }); + }) }, deleteLine: function(cm) { deleteNearSelection(cm, function(range) { return {from: Pos(range.from().line, 0), - to: clipPos(cm.doc, Pos(range.to().line + 1, 0))}; - }); + to: clipPos(cm.doc, Pos(range.to().line + 1, 0))} + }) }, delLineLeft: function(cm) { deleteNearSelection(cm, function(range) { - return {from: Pos(range.from().line, 0), to: range.from()}; - }); + 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()}; - }); + 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();}, - redoSelection: function(cm) {cm.redoSelection();}, - goDocStart: function(cm) {cm.extendSelection(Pos(cm.firstLine(), 0));}, - goDocEnd: function(cm) {cm.extendSelection(Pos(cm.lastLine()));}, + 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()}, + redoSelection: function(cm) {cm.redoSelection()}, + goDocStart: function(cm) {cm.extendSelection(Pos(cm.firstLine(), 0))}, + goDocEnd: function(cm) {cm.extendSelection(Pos(cm.lastLine()))}, goLineStart: function(cm) { - cm.extendSelectionsBy(function(range) { return lineStart(cm, range.head.line); }, - {origin: "+move", bias: 1}); + cm.extendSelectionsBy(function(range) { return lineStart(cm, range.head.line) }, + {origin: "+move", bias: 1}) }, goLineStartSmart: function(cm) { cm.extendSelectionsBy(function(range) { - return lineStartSmart(cm, range.head); - }, {origin: "+move", bias: 1}); + return lineStartSmart(cm, range.head) + }, {origin: "+move", bias: 1}) }, goLineEnd: function(cm) { - cm.extendSelectionsBy(function(range) { return lineEnd(cm, range.head.line); }, - {origin: "+move", bias: -1}); + cm.extendSelectionsBy(function(range) { return lineEnd(cm, range.head.line) }, + {origin: "+move", bias: -1}) }, goLineRight: function(cm) { cm.extendSelectionsBy(function(range) { - var top = cm.charCoords(range.head, "div").top + 5; - return cm.coordsChar({left: cm.display.lineDiv.offsetWidth + 100, top: top}, "div"); - }, sel_move); + var top = cm.charCoords(range.head, "div").top + 5 + return cm.coordsChar({left: cm.display.lineDiv.offsetWidth + 100, top: top}, "div") + }, sel_move) }, goLineLeft: function(cm) { cm.extendSelectionsBy(function(range) { - var top = cm.charCoords(range.head, "div").top + 5; - return cm.coordsChar({left: 0, top: top}, "div"); - }, sel_move); + var top = cm.charCoords(range.head, "div").top + 5 + return cm.coordsChar({left: 0, top: top}, "div") + }, sel_move) }, goLineLeftSmart: function(cm) { cm.extendSelectionsBy(function(range) { - var top = cm.charCoords(range.head, "div").top + 5; - var pos = cm.coordsChar({left: 0, top: top}, "div"); - if (pos.ch < cm.getLine(pos.line).search(/\S/)) return lineStartSmart(cm, range.head); - return pos; - }, sel_move); - }, - goLineUp: function(cm) {cm.moveV(-1, "line");}, - goLineDown: function(cm) {cm.moveV(1, "line");}, - goPageUp: function(cm) {cm.moveV(-1, "page");}, - goPageDown: function(cm) {cm.moveV(1, "page");}, - goCharLeft: function(cm) {cm.moveH(-1, "char");}, - goCharRight: function(cm) {cm.moveH(1, "char");}, - 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");}, - insertTab: function(cm) {cm.replaceSelection("\t");}, + var top = cm.charCoords(range.head, "div").top + 5 + var pos = cm.coordsChar({left: 0, top: top}, "div") + if (pos.ch < cm.getLine(pos.line).search(/\S/)) return lineStartSmart(cm, range.head) + return pos + }, sel_move) + }, + goLineUp: function(cm) {cm.moveV(-1, "line")}, + goLineDown: function(cm) {cm.moveV(1, "line")}, + goPageUp: function(cm) {cm.moveV(-1, "page")}, + goPageDown: function(cm) {cm.moveV(1, "page")}, + goCharLeft: function(cm) {cm.moveH(-1, "char")}, + goCharRight: function(cm) {cm.moveH(1, "char")}, + 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")}, + insertTab: function(cm) {cm.replaceSelection("\t")}, insertSoftTab: function(cm) { - var spaces = [], ranges = cm.listSelections(), tabSize = cm.options.tabSize; + var spaces = [], ranges = cm.listSelections(), tabSize = cm.options.tabSize for (var i = 0; i < ranges.length; i++) { - var pos = ranges[i].from(); - var col = countColumn(cm.getLine(pos.line), pos.ch, tabSize); - spaces.push(spaceStr(tabSize - col % tabSize)); + var pos = ranges[i].from() + var col = countColumn(cm.getLine(pos.line), pos.ch, tabSize) + spaces.push(spaceStr(tabSize - col % tabSize)) } - cm.replaceSelections(spaces); + cm.replaceSelections(spaces) }, defaultTab: function(cm) { - if (cm.somethingSelected()) cm.indentSelection("add"); - else cm.execCommand("insertTab"); + if (cm.somethingSelected()) cm.indentSelection("add") + else cm.execCommand("insertTab") }, transposeChars: function(cm) { runInOp(cm, function() { - var ranges = cm.listSelections(), newSel = []; + var ranges = cm.listSelections(), newSel = [] for (var i = 0; i < ranges.length; i++) { - var cur = ranges[i].head, line = getLine(cm.doc, cur.line).text; + var cur = ranges[i].head, line = getLine(cm.doc, cur.line).text if (line) { - if (cur.ch == line.length) cur = new Pos(cur.line, cur.ch - 1); + if (cur.ch == line.length) cur = new Pos(cur.line, cur.ch - 1) if (cur.ch > 0) { - cur = new Pos(cur.line, cur.ch + 1); + cur = new Pos(cur.line, cur.ch + 1) cm.replaceRange(line.charAt(cur.ch - 1) + line.charAt(cur.ch - 2), - Pos(cur.line, cur.ch - 2), cur, "+transpose"); + Pos(cur.line, cur.ch - 2), cur, "+transpose") } else if (cur.line > cm.doc.first) { - var prev = getLine(cm.doc, cur.line - 1).text; + var prev = getLine(cm.doc, cur.line - 1).text if (prev) cm.replaceRange(line.charAt(0) + cm.doc.lineSeparator() + prev.charAt(prev.length - 1), - Pos(cur.line - 1, prev.length - 1), Pos(cur.line, 1), "+transpose"); + Pos(cur.line - 1, prev.length - 1), Pos(cur.line, 1), "+transpose") } } - newSel.push(new Range(cur, cur)); + newSel.push(new Range(cur, cur)) } - cm.setSelections(newSel); - }); + cm.setSelections(newSel) + }) }, newlineAndIndent: function(cm) { runInOp(cm, function() { @@ -160,40 +160,40 @@ export var commands = { sels = cm.listSelections() for (var i = 0; i < sels.length; i++) cm.indentLine(sels[i].from().line, null, true) - ensureCursorVisible(cm); - }); + ensureCursorVisible(cm) + }) }, openLine: function(cm) {cm.replaceSelection("\n", "start")}, - toggleOverwrite: function(cm) {cm.toggleOverwrite();} -}; + toggleOverwrite: function(cm) {cm.toggleOverwrite()} +} function lineStart(cm, lineN) { - var line = getLine(cm.doc, lineN); - var visual = visualLine(line); - if (visual != line) lineN = lineNo(visual); - var order = getOrder(visual); - var ch = !order ? 0 : order[0].level % 2 ? lineRight(visual) : lineLeft(visual); - return Pos(lineN, ch); + var line = getLine(cm.doc, lineN) + var visual = visualLine(line) + if (visual != line) lineN = lineNo(visual) + var order = getOrder(visual) + var ch = !order ? 0 : order[0].level % 2 ? lineRight(visual) : lineLeft(visual) + return Pos(lineN, ch) } function lineEnd(cm, lineN) { - var merged, line = getLine(cm.doc, lineN); + var merged, line = getLine(cm.doc, lineN) while (merged = collapsedSpanAtEnd(line)) { - line = merged.find(1, true).line; - lineN = null; + line = merged.find(1, true).line + lineN = null } - var order = getOrder(line); - var ch = !order ? line.text.length : order[0].level % 2 ? lineLeft(line) : lineRight(line); - return Pos(lineN == null ? lineNo(line) : lineN, ch); + var order = getOrder(line) + var ch = !order ? line.text.length : order[0].level % 2 ? lineLeft(line) : lineRight(line) + return Pos(lineN == null ? lineNo(line) : lineN, ch) } function lineStartSmart(cm, pos) { - var start = lineStart(cm, pos.line); - var line = getLine(cm.doc, start.line); - var order = getOrder(line); + var start = lineStart(cm, pos.line) + var line = getLine(cm.doc, start.line) + var order = getOrder(line) if (!order || order[0].level == 0) { - var firstNonWS = Math.max(0, line.text.search(/\S/)); - var inWS = pos.line == start.line && pos.ch <= firstNonWS && pos.ch; - return Pos(start.line, inWS ? 0 : firstNonWS); + var firstNonWS = Math.max(0, line.text.search(/\S/)) + var inWS = pos.line == start.line && pos.ch <= firstNonWS && pos.ch + return Pos(start.line, inWS ? 0 : firstNonWS) } - return start; + return start } diff --git a/src/edit/deleteNearSelection.js b/src/edit/deleteNearSelection.js index dd136902..00b4aa08 100644 --- a/src/edit/deleteNearSelection.js +++ b/src/edit/deleteNearSelection.js @@ -1,30 +1,30 @@ -import { runInOp } from "../display/operations"; -import { ensureCursorVisible } from "../display/scrolling"; -import { cmp } from "../line/pos"; -import { replaceRange } from "../model/changes"; -import { lst } from "../util/misc"; +import { runInOp } from "../display/operations" +import { ensureCursorVisible } from "../display/scrolling" +import { cmp } from "../line/pos" +import { replaceRange } from "../model/changes" +import { lst } from "../util/misc" // Helper for deleting text near the selection(s), used to implement // backspace, delete, and similar functionality. export function deleteNearSelection(cm, compute) { - var ranges = cm.doc.sel.ranges, kill = []; + var ranges = cm.doc.sel.ranges, kill = [] // Build up a set of ranges to kill first, merging overlapping // ranges. for (var i = 0; i < ranges.length; i++) { - var toKill = compute(ranges[i]); + var toKill = compute(ranges[i]) while (kill.length && cmp(toKill.from, lst(kill).to) <= 0) { - var replaced = kill.pop(); + var replaced = kill.pop() if (cmp(replaced.from, toKill.from) < 0) { - toKill.from = replaced.from; - break; + toKill.from = replaced.from + break } } - kill.push(toKill); + kill.push(toKill) } // Next, remove those actual ranges. runInOp(cm, function() { for (var i = kill.length - 1; i >= 0; i--) - replaceRange(cm.doc, "", kill[i].from, kill[i].to, "+delete"); - ensureCursorVisible(cm); - }); + replaceRange(cm.doc, "", kill[i].from, kill[i].to, "+delete") + ensureCursorVisible(cm) + }) } diff --git a/src/edit/drop_events.js b/src/edit/drop_events.js index fac58a26..d2aeaa7c 100644 --- a/src/edit/drop_events.js +++ b/src/edit/drop_events.js @@ -1,74 +1,74 @@ -import { drawSelectionCursor } from "../display/selection"; -import { operation } from "../display/operations"; -import { clipPos } from "../line/pos"; -import { posFromMouse } from "../measurement/position_measurement"; -import { eventInWidget } from "../measurement/widgets"; -import { makeChange, replaceRange } from "../model/changes"; -import { changeEnd } from "../model/change_measurement"; -import { simpleSelection } from "../model/selection"; -import { setSelectionNoUndo, setSelectionReplaceHistory } from "../model/selection_updates"; -import { ie, presto, safari } from "../util/browser"; -import { elt, removeChildrenAndAdd } from "../util/dom"; -import { e_preventDefault, e_stop, signalDOMEvent } from "../util/event"; -import { indexOf } from "../util/misc"; +import { drawSelectionCursor } from "../display/selection" +import { operation } from "../display/operations" +import { clipPos } from "../line/pos" +import { posFromMouse } from "../measurement/position_measurement" +import { eventInWidget } from "../measurement/widgets" +import { makeChange, replaceRange } from "../model/changes" +import { changeEnd } from "../model/change_measurement" +import { simpleSelection } from "../model/selection" +import { setSelectionNoUndo, setSelectionReplaceHistory } from "../model/selection_updates" +import { ie, presto, safari } from "../util/browser" +import { elt, removeChildrenAndAdd } from "../util/dom" +import { e_preventDefault, e_stop, signalDOMEvent } from "../util/event" +import { indexOf } from "../util/misc" // 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; +var lastDrop = 0 export function onDrop(e) { - var cm = this; - clearDragCursor(cm); + var cm = this + clearDragCursor(cm) if (signalDOMEvent(cm, e) || eventInWidget(cm.display, e)) - return; - e_preventDefault(e); - if (ie) lastDrop = +new Date; - var pos = posFromMouse(cm, e, true), files = e.dataTransfer.files; - if (!pos || cm.isReadOnly()) return; + return + e_preventDefault(e) + if (ie) lastDrop = +new Date + var pos = posFromMouse(cm, e, true), files = e.dataTransfer.files + if (!pos || cm.isReadOnly()) return // Might be a file drop, in which case we simply extract the text // and insert it. if (files && files.length && window.FileReader && window.File) { - var n = files.length, text = Array(n), read = 0; + var n = files.length, text = Array(n), read = 0 var loadFile = function(file, i) { if (cm.options.allowDropFileTypes && indexOf(cm.options.allowDropFileTypes, file.type) == -1) - return; + return - var reader = new FileReader; + var reader = new FileReader reader.onload = operation(cm, function() { - var content = reader.result; - if (/[\x00-\x08\x0e-\x1f]{2}/.test(content)) content = ""; - text[i] = content; + var content = reader.result + if (/[\x00-\x08\x0e-\x1f]{2}/.test(content)) content = "" + text[i] = content if (++read == n) { - pos = clipPos(cm.doc, pos); + pos = clipPos(cm.doc, pos) var change = {from: pos, to: pos, text: cm.doc.splitLines(text.join(cm.doc.lineSeparator())), - origin: "paste"}; - makeChange(cm.doc, change); - setSelectionReplaceHistory(cm.doc, simpleSelection(pos, changeEnd(change))); + origin: "paste"} + makeChange(cm.doc, change) + setSelectionReplaceHistory(cm.doc, simpleSelection(pos, changeEnd(change))) } - }); - reader.readAsText(file); - }; - for (var i = 0; i < n; ++i) loadFile(files[i], i); + }) + reader.readAsText(file) + } + for (var i = 0; i < n; ++i) loadFile(files[i], i) } else { // Normal drop // Don't do a replace if the drop happened inside of the selected text. if (cm.state.draggingText && cm.doc.sel.contains(pos) > -1) { - cm.state.draggingText(e); + cm.state.draggingText(e) // Ensure the editor is re-focused - setTimeout(function() {cm.display.input.focus();}, 20); - return; + setTimeout(function() {cm.display.input.focus()}, 20) + return } try { - var text = e.dataTransfer.getData("Text"); + var text = e.dataTransfer.getData("Text") if (text) { if (cm.state.draggingText && !cm.state.draggingText.copy) - var selected = cm.listSelections(); - setSelectionNoUndo(cm.doc, simpleSelection(pos, pos)); + 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"); - cm.replaceSelection(text, "around", "paste"); - cm.display.input.focus(); + replaceRange(cm.doc, "", selected[i].anchor, selected[i].head, "drag") + cm.replaceSelection(text, "around", "paste") + cm.display.input.focus() } } catch(e){} @@ -76,43 +76,43 @@ export function onDrop(e) { } export function onDragStart(cm, e) { - if (ie && (!cm.state.draggingText || +new Date - lastDrop < 100)) { e_stop(e); return; } - if (signalDOMEvent(cm, e) || eventInWidget(cm.display, e)) return; + if (ie && (!cm.state.draggingText || +new Date - lastDrop < 100)) { e_stop(e); return } + if (signalDOMEvent(cm, e) || eventInWidget(cm.display, e)) return - e.dataTransfer.setData("Text", cm.getSelection()); + 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. if (e.dataTransfer.setDragImage && !safari) { - var img = elt("img", null, null, "position: fixed; left: 0; top: 0;"); - img.src = "data:image/gif;base64,R0lGODlhAQABAAAAACH5BAEKAAEALAAAAAABAAEAAAICTAEAOw=="; + var img = elt("img", null, null, "position: fixed; left: 0; top: 0;") + img.src = "data:image/gif;base64,R0lGODlhAQABAAAAACH5BAEKAAEALAAAAAABAAEAAAICTAEAOw==" if (presto) { - img.width = img.height = 1; - cm.display.wrapper.appendChild(img); + 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; + img._top = img.offsetTop } - e.dataTransfer.setDragImage(img, 0, 0); - if (presto) img.parentNode.removeChild(img); + e.dataTransfer.setDragImage(img, 0, 0) + if (presto) img.parentNode.removeChild(img) } } export function onDragOver(cm, e) { - var pos = posFromMouse(cm, e); - if (!pos) return; - var frag = document.createDocumentFragment(); - drawSelectionCursor(cm, pos, frag); + var pos = posFromMouse(cm, e) + if (!pos) return + var frag = document.createDocumentFragment() + drawSelectionCursor(cm, pos, frag) if (!cm.display.dragCursor) { - cm.display.dragCursor = elt("div", null, "CodeMirror-cursors CodeMirror-dragcursors"); - cm.display.lineSpace.insertBefore(cm.display.dragCursor, cm.display.cursorDiv); + cm.display.dragCursor = elt("div", null, "CodeMirror-cursors CodeMirror-dragcursors") + cm.display.lineSpace.insertBefore(cm.display.dragCursor, cm.display.cursorDiv) } - removeChildrenAndAdd(cm.display.dragCursor, frag); + removeChildrenAndAdd(cm.display.dragCursor, frag) } export function clearDragCursor(cm) { if (cm.display.dragCursor) { - cm.display.lineSpace.removeChild(cm.display.dragCursor); - cm.display.dragCursor = null; + cm.display.lineSpace.removeChild(cm.display.dragCursor) + cm.display.dragCursor = null } } diff --git a/src/edit/fromTextArea.js b/src/edit/fromTextArea.js index aa1a040f..dda26d4b 100644 --- a/src/edit/fromTextArea.js +++ b/src/edit/fromTextArea.js @@ -1,59 +1,59 @@ -import { CodeMirror } from "./CodeMirror"; -import { activeElt } from "../util/dom"; -import { off, on } from "../util/event"; -import { copyObj } from "../util/misc"; +import { CodeMirror } from "./CodeMirror" +import { activeElt } from "../util/dom" +import { off, on } from "../util/event" +import { copyObj } from "../util/misc" export function fromTextArea(textarea, options) { - options = options ? copyObj(options) : {}; - options.value = textarea.value; + options = options ? copyObj(options) : {} + options.value = textarea.value if (!options.tabindex && textarea.tabIndex) - options.tabindex = textarea.tabIndex; + options.tabindex = textarea.tabIndex if (!options.placeholder && textarea.placeholder) - 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) { - var hasFocus = activeElt(); + var hasFocus = activeElt() options.autofocus = hasFocus == textarea || - textarea.getAttribute("autofocus") != null && hasFocus == document.body; + textarea.getAttribute("autofocus") != null && hasFocus == document.body } - function save() {textarea.value = cm.getValue();} + function save() {textarea.value = cm.getValue()} if (textarea.form) { - on(textarea.form, "submit", save); + on(textarea.form, "submit", save) // Deplorable hack to make the submit method do the right thing. if (!options.leaveSubmitMethodAlone) { - var form = textarea.form, realSubmit = form.submit; + var form = textarea.form, realSubmit = form.submit try { var wrappedSubmit = form.submit = function() { - save(); - form.submit = realSubmit; - form.submit(); - form.submit = wrappedSubmit; - }; + save() + form.submit = realSubmit + form.submit() + form.submit = wrappedSubmit + } } catch(e) {} } } options.finishInit = function(cm) { - cm.save = save; - cm.getTextArea = function() { return textarea; }; + cm.save = save + cm.getTextArea = function() { return textarea } cm.toTextArea = function() { - cm.toTextArea = isNaN; // Prevent this from being ran twice - save(); - textarea.parentNode.removeChild(cm.getWrapperElement()); - textarea.style.display = ""; + cm.toTextArea = isNaN // Prevent this from being ran twice + save() + textarea.parentNode.removeChild(cm.getWrapperElement()) + textarea.style.display = "" if (textarea.form) { - off(textarea.form, "submit", save); + off(textarea.form, "submit", save) if (typeof textarea.form.submit == "function") - textarea.form.submit = realSubmit; + textarea.form.submit = realSubmit } - }; - }; + } + } - textarea.style.display = "none"; + textarea.style.display = "none" var cm = CodeMirror(function(node) { - textarea.parentNode.insertBefore(node, textarea.nextSibling); - }, options); - return cm; + textarea.parentNode.insertBefore(node, textarea.nextSibling) + }, options) + return cm } diff --git a/src/edit/global_events.js b/src/edit/global_events.js index b219285d..57542f2d 100644 --- a/src/edit/global_events.js +++ b/src/edit/global_events.js @@ -1,46 +1,46 @@ -import { onBlur } from "../display/focus"; -import { on } from "../util/event"; +import { onBlur } from "../display/focus" +import { on } from "../util/event" // These must be handled carefully, because naively registering a // handler for each editor will cause the editors to never be // garbage collected. function forEachCodeMirror(f) { - if (!document.body.getElementsByClassName) return; - var byClass = document.body.getElementsByClassName("CodeMirror"); + if (!document.body.getElementsByClassName) return + var byClass = document.body.getElementsByClassName("CodeMirror") for (var i = 0; i < byClass.length; i++) { - var cm = byClass[i].CodeMirror; - if (cm) f(cm); + var cm = byClass[i].CodeMirror + if (cm) f(cm) } } -var globalsRegistered = false; +var globalsRegistered = false export function ensureGlobalHandlers() { - if (globalsRegistered) return; - registerGlobalHandlers(); - globalsRegistered = true; + if (globalsRegistered) return + registerGlobalHandlers() + globalsRegistered = true } function registerGlobalHandlers() { // When the window resizes, we need to refresh active editors. - var resizeTimer; + var resizeTimer on(window, "resize", function() { if (resizeTimer == null) resizeTimer = setTimeout(function() { - resizeTimer = null; - forEachCodeMirror(onResize); - }, 100); - }); + resizeTimer = null + forEachCodeMirror(onResize) + }, 100) + }) // When the window loses focus, we want to show the editor as blurred on(window, "blur", function() { - forEachCodeMirror(onBlur); - }); + forEachCodeMirror(onBlur) + }) } // Called when the window resizes function onResize(cm) { - var d = cm.display; + var d = cm.display if (d.lastWrapHeight == d.wrapper.clientHeight && d.lastWrapWidth == d.wrapper.clientWidth) - return; + return // Might be a text scaling operation, clear size caches. - d.cachedCharWidth = d.cachedTextHeight = d.cachedPaddingH = null; - d.scrollbarsClipped = false; - cm.setSize(); + d.cachedCharWidth = d.cachedTextHeight = d.cachedPaddingH = null + d.scrollbarsClipped = false + cm.setSize() } diff --git a/src/edit/key_events.js b/src/edit/key_events.js index 92763037..9a494441 100644 --- a/src/edit/key_events.js +++ b/src/edit/key_events.js @@ -1,153 +1,153 @@ -import { signalLater } from "../util/operation_group"; -import { restartBlink } from "../display/selection"; -import { isModifierKey, keyName, lookupKey } from "../input/keymap"; -import { eventInWidget } from "../measurement/widgets"; -import { ie, ie_version, mac, presto } from "../util/browser"; -import { activeElt, addClass, rmClass } from "../util/dom"; -import { e_preventDefault, off, on, signalDOMEvent } from "../util/event"; -import { hasCopyEvent } from "../util/feature_detection"; -import { Delayed, Pass } from "../util/misc"; - -import { commands } from "./commands"; +import { signalLater } from "../util/operation_group" +import { restartBlink } from "../display/selection" +import { isModifierKey, keyName, lookupKey } from "../input/keymap" +import { eventInWidget } from "../measurement/widgets" +import { ie, ie_version, mac, presto } from "../util/browser" +import { activeElt, addClass, rmClass } from "../util/dom" +import { e_preventDefault, off, on, signalDOMEvent } from "../util/event" +import { hasCopyEvent } from "../util/feature_detection" +import { Delayed, Pass } from "../util/misc" + +import { commands } from "./commands" // Run a handler that was bound to a key. function doHandleBinding(cm, bound, dropShift) { if (typeof bound == "string") { - bound = commands[bound]; - if (!bound) return false; + bound = commands[bound] + if (!bound) return false } // Ensure previous input has been read, so that the handler sees a // consistent view of the document - cm.display.input.ensurePolled(); - var prevShift = cm.display.shift, done = false; + cm.display.input.ensurePolled() + var prevShift = cm.display.shift, done = false try { - if (cm.isReadOnly()) cm.state.suppressEdits = true; - if (dropShift) cm.display.shift = false; - done = bound(cm) != Pass; + if (cm.isReadOnly()) cm.state.suppressEdits = true + if (dropShift) cm.display.shift = false + done = bound(cm) != Pass } finally { - cm.display.shift = prevShift; - cm.state.suppressEdits = false; + cm.display.shift = prevShift + cm.state.suppressEdits = false } - return done; + return done } function lookupKeyForEditor(cm, name, handle) { for (var i = 0; i < cm.state.keyMaps.length; i++) { - var result = lookupKey(name, cm.state.keyMaps[i], handle, cm); - if (result) return result; + var result = lookupKey(name, cm.state.keyMaps[i], handle, cm) + if (result) return result } return (cm.options.extraKeys && lookupKey(name, cm.options.extraKeys, handle, cm)) - || lookupKey(name, cm.options.keyMap, handle, cm); + || lookupKey(name, cm.options.keyMap, handle, cm) } -var stopSeq = new Delayed; +var stopSeq = new Delayed function dispatchKey(cm, name, e, handle) { - var seq = cm.state.keySeq; + var seq = cm.state.keySeq if (seq) { - if (isModifierKey(name)) return "handled"; + if (isModifierKey(name)) return "handled" stopSeq.set(50, function() { if (cm.state.keySeq == seq) { - cm.state.keySeq = null; - cm.display.input.reset(); + cm.state.keySeq = null + cm.display.input.reset() } - }); - name = seq + " " + name; + }) + name = seq + " " + name } - var result = lookupKeyForEditor(cm, name, handle); + var result = lookupKeyForEditor(cm, name, handle) if (result == "multi") - cm.state.keySeq = name; + cm.state.keySeq = name if (result == "handled") - signalLater(cm, "keyHandled", cm, name, e); + signalLater(cm, "keyHandled", cm, name, e) if (result == "handled" || result == "multi") { - e_preventDefault(e); - restartBlink(cm); + e_preventDefault(e) + restartBlink(cm) } if (seq && !result && /\'$/.test(name)) { - e_preventDefault(e); - return true; + e_preventDefault(e) + return true } - return !!result; + return !!result } // Handle a key from the keydown event. function handleKeyBinding(cm, e) { - var name = keyName(e, true); - if (!name) return false; + var name = keyName(e, true) + if (!name) return false if (e.shiftKey && !cm.state.keySeq) { // 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-'. - return dispatchKey(cm, "Shift-" + name, e, function(b) {return doHandleBinding(cm, b, true);}) + return dispatchKey(cm, "Shift-" + name, e, function(b) {return doHandleBinding(cm, b, true)}) || dispatchKey(cm, name, e, function(b) { if (typeof b == "string" ? /^go[A-Z]/.test(b) : b.motion) - return doHandleBinding(cm, b); - }); + return doHandleBinding(cm, b) + }) } else { - return dispatchKey(cm, name, e, function(b) { return doHandleBinding(cm, b); }); + return dispatchKey(cm, name, e, function(b) { return doHandleBinding(cm, b) }) } } // Handle a key from the keypress event function handleCharBinding(cm, e, ch) { return dispatchKey(cm, "'" + ch + "'", e, - function(b) { return doHandleBinding(cm, b, true); }); + function(b) { return doHandleBinding(cm, b, true) }) } -var lastStoppedKey = null; +var lastStoppedKey = null export function onKeyDown(e) { - var cm = this; - cm.curOp.focus = activeElt(); - if (signalDOMEvent(cm, e)) return; + var cm = this + cm.curOp.focus = activeElt() + if (signalDOMEvent(cm, e)) return // IE does strange things with escape. - if (ie && ie_version < 11 && e.keyCode == 27) e.returnValue = false; - var code = e.keyCode; - cm.display.shift = code == 16 || e.shiftKey; - var handled = handleKeyBinding(cm, e); + if (ie && ie_version < 11 && e.keyCode == 27) e.returnValue = false + var code = e.keyCode + cm.display.shift = code == 16 || e.shiftKey + var handled = handleKeyBinding(cm, e) if (presto) { - lastStoppedKey = handled ? code : null; + lastStoppedKey = handled ? code : null // Opera has no cut event... we try to at least catch the key combo if (!handled && code == 88 && !hasCopyEvent && (mac ? e.metaKey : e.ctrlKey)) - cm.replaceSelection("", null, "cut"); + cm.replaceSelection("", null, "cut") } // Turn mouse into crosshair when Alt is held on Mac. if (code == 18 && !/\bCodeMirror-crosshair\b/.test(cm.display.lineDiv.className)) - showCrossHair(cm); + showCrossHair(cm) } function showCrossHair(cm) { - var lineDiv = cm.display.lineDiv; - addClass(lineDiv, "CodeMirror-crosshair"); + var lineDiv = cm.display.lineDiv + addClass(lineDiv, "CodeMirror-crosshair") function up(e) { if (e.keyCode == 18 || !e.altKey) { - rmClass(lineDiv, "CodeMirror-crosshair"); - off(document, "keyup", up); - off(document, "mouseover", up); + rmClass(lineDiv, "CodeMirror-crosshair") + off(document, "keyup", up) + off(document, "mouseover", up) } } - on(document, "keyup", up); - on(document, "mouseover", up); + on(document, "keyup", up) + on(document, "mouseover", up) } export function onKeyUp(e) { - if (e.keyCode == 16) this.doc.sel.shift = false; - signalDOMEvent(this, e); + if (e.keyCode == 16) this.doc.sel.shift = false + signalDOMEvent(this, e) } export function onKeyPress(e) { - var cm = this; - if (eventInWidget(cm.display, e) || 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)) && handleKeyBinding(cm, e)) return; - var ch = String.fromCharCode(charCode == null ? keyCode : charCode); + var cm = this + if (eventInWidget(cm.display, e) || 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)) && handleKeyBinding(cm, e)) return + var ch = String.fromCharCode(charCode == null ? keyCode : charCode) // Some browsers fire keypress events for backspace - if (ch == "\x08") return; - if (handleCharBinding(cm, e, ch)) return; - cm.display.input.onKeyPress(e); + if (ch == "\x08") return + if (handleCharBinding(cm, e, ch)) return + cm.display.input.onKeyPress(e) } diff --git a/src/edit/legacy.js b/src/edit/legacy.js index 150f8a72..bc3df6c8 100644 --- a/src/edit/legacy.js +++ b/src/edit/legacy.js @@ -1,62 +1,62 @@ -import { scrollbarModel } from "../display/scrollbars"; -import { wheelEventPixels } from "../display/scroll_events"; -import { keyMap, keyName, isModifierKey, lookupKey, normalizeKeyMap } from "../input/keymap"; -import { keyNames } from "../input/keynames"; -import { Line } from "../line/line_data"; -import { cmp, Pos } from "../line/pos"; -import { changeEnd } from "../model/change_measurement"; -import Doc from "../model/Doc"; -import { LineWidget } from "../model/line_widget"; -import { SharedTextMarker, TextMarker } from "../model/mark_text"; -import { copyState, extendMode, getMode, innerMode, mimeModes, modeExtensions, modes, resolveMode, startState } from "../modes"; -import { addClass, contains, rmClass } from "../util/dom"; -import { e_preventDefault, e_stop, e_stopPropagation, off, on, signal } from "../util/event"; -import { splitLinesAuto } from "../util/feature_detection"; -import { countColumn, findColumn, isWordCharBasic, Pass } from "../util/misc"; -import StringStream from "../util/StringStream"; +import { scrollbarModel } from "../display/scrollbars" +import { wheelEventPixels } from "../display/scroll_events" +import { keyMap, keyName, isModifierKey, lookupKey, normalizeKeyMap } from "../input/keymap" +import { keyNames } from "../input/keynames" +import { Line } from "../line/line_data" +import { cmp, Pos } from "../line/pos" +import { changeEnd } from "../model/change_measurement" +import Doc from "../model/Doc" +import { LineWidget } from "../model/line_widget" +import { SharedTextMarker, TextMarker } from "../model/mark_text" +import { copyState, extendMode, getMode, innerMode, mimeModes, modeExtensions, modes, resolveMode, startState } from "../modes" +import { addClass, contains, rmClass } from "../util/dom" +import { e_preventDefault, e_stop, e_stopPropagation, off, on, signal } from "../util/event" +import { splitLinesAuto } from "../util/feature_detection" +import { countColumn, findColumn, isWordCharBasic, Pass } from "../util/misc" +import StringStream from "../util/StringStream" -import { commands } from "./commands"; +import { commands } from "./commands" export function addLegacyProps(CodeMirror) { - CodeMirror.off = off; - CodeMirror.on = on; - CodeMirror.wheelEventPixels = wheelEventPixels; - CodeMirror.Doc = Doc; - CodeMirror.splitLines = splitLinesAuto; - CodeMirror.countColumn = countColumn; - CodeMirror.findColumn = findColumn; - CodeMirror.isWordChar = isWordCharBasic; - CodeMirror.Pass = Pass; - CodeMirror.signal = signal; - CodeMirror.Line = Line; - CodeMirror.changeEnd = changeEnd; - CodeMirror.scrollbarModel = scrollbarModel; - CodeMirror.Pos = Pos; - CodeMirror.cmpPos = cmp; - CodeMirror.modes = modes; - CodeMirror.mimeModes = mimeModes; - CodeMirror.resolveMode = resolveMode; - CodeMirror.getMode = getMode; - CodeMirror.modeExtensions = modeExtensions; - CodeMirror.extendMode = extendMode; - CodeMirror.copyState = copyState; - CodeMirror.startState = startState; - CodeMirror.innerMode = innerMode; - CodeMirror.commands = commands; - CodeMirror.keyMap = keyMap; - CodeMirror.keyName = keyName; - CodeMirror.isModifierKey = isModifierKey; - CodeMirror.lookupKey = lookupKey; - CodeMirror.normalizeKeyMap = normalizeKeyMap; - CodeMirror.StringStream = StringStream; - CodeMirror.SharedTextMarker = SharedTextMarker; - CodeMirror.TextMarker = TextMarker; - CodeMirror.LineWidget = LineWidget; - CodeMirror.e_preventDefault = e_preventDefault; - CodeMirror.e_stopPropagation = e_stopPropagation; - CodeMirror.e_stop = e_stop; - CodeMirror.addClass = addClass; - CodeMirror.contains = contains; - CodeMirror.rmClass = rmClass; - CodeMirror.keyNames = keyNames; + CodeMirror.off = off + CodeMirror.on = on + CodeMirror.wheelEventPixels = wheelEventPixels + CodeMirror.Doc = Doc + CodeMirror.splitLines = splitLinesAuto + CodeMirror.countColumn = countColumn + CodeMirror.findColumn = findColumn + CodeMirror.isWordChar = isWordCharBasic + CodeMirror.Pass = Pass + CodeMirror.signal = signal + CodeMirror.Line = Line + CodeMirror.changeEnd = changeEnd + CodeMirror.scrollbarModel = scrollbarModel + CodeMirror.Pos = Pos + CodeMirror.cmpPos = cmp + CodeMirror.modes = modes + CodeMirror.mimeModes = mimeModes + CodeMirror.resolveMode = resolveMode + CodeMirror.getMode = getMode + CodeMirror.modeExtensions = modeExtensions + CodeMirror.extendMode = extendMode + CodeMirror.copyState = copyState + CodeMirror.startState = startState + CodeMirror.innerMode = innerMode + CodeMirror.commands = commands + CodeMirror.keyMap = keyMap + CodeMirror.keyName = keyName + CodeMirror.isModifierKey = isModifierKey + CodeMirror.lookupKey = lookupKey + CodeMirror.normalizeKeyMap = normalizeKeyMap + CodeMirror.StringStream = StringStream + CodeMirror.SharedTextMarker = SharedTextMarker + CodeMirror.TextMarker = TextMarker + CodeMirror.LineWidget = LineWidget + CodeMirror.e_preventDefault = e_preventDefault + CodeMirror.e_stopPropagation = e_stopPropagation + CodeMirror.e_stop = e_stop + CodeMirror.addClass = addClass + CodeMirror.contains = contains + CodeMirror.rmClass = rmClass + CodeMirror.keyNames = keyNames } diff --git a/src/edit/main.js b/src/edit/main.js index a955aaa1..3f76d702 100644 --- a/src/edit/main.js +++ b/src/edit/main.js @@ -1,71 +1,71 @@ // EDITOR CONSTRUCTOR -import { CodeMirror } from "./CodeMirror"; -export { CodeMirror } from "./CodeMirror"; +import { CodeMirror } from "./CodeMirror" +export { CodeMirror } from "./CodeMirror" -import { eventMixin } from "../util/event"; -import { indexOf } from "../util/misc"; +import { eventMixin } from "../util/event" +import { indexOf } from "../util/misc" -import { defineOptions } from "./options"; +import { defineOptions } from "./options" -defineOptions(CodeMirror); +defineOptions(CodeMirror) -import addEditorMethods from "./methods"; +import addEditorMethods from "./methods" -addEditorMethods(CodeMirror); +addEditorMethods(CodeMirror) -import Doc from "../model/Doc"; +import Doc from "../model/Doc" // Set up methods on CodeMirror's prototype to redirect to the editor's document. -var dontDelegate = "iter insert remove copy getEditor constructor".split(" "); +var dontDelegate = "iter insert remove copy getEditor constructor".split(" ") for (var prop in Doc.prototype) if (Doc.prototype.hasOwnProperty(prop) && indexOf(dontDelegate, prop) < 0) CodeMirror.prototype[prop] = (function(method) { - return function() {return method.apply(this.doc, arguments);}; - })(Doc.prototype[prop]); + return function() {return method.apply(this.doc, arguments)} + })(Doc.prototype[prop]) -eventMixin(Doc); +eventMixin(Doc) // INPUT HANDLING -import ContentEditableInput from "../input/ContentEditableInput"; -import TextareaInput from "../input/TextareaInput"; -CodeMirror.inputStyles = {"textarea": TextareaInput, "contenteditable": ContentEditableInput}; +import ContentEditableInput from "../input/ContentEditableInput" +import TextareaInput from "../input/TextareaInput" +CodeMirror.inputStyles = {"textarea": TextareaInput, "contenteditable": ContentEditableInput} // MODE DEFINITION AND QUERYING -import { defineMIME, defineMode } from "../modes"; +import { defineMIME, defineMode } from "../modes" // Extra arguments are stored as the mode's dependencies, which is // used by (legacy) mechanisms like loadmode.js to automatically // load a mode. (Preferred mechanism is the require/define calls.) CodeMirror.defineMode = function(name/*, mode, …*/) { - if (!CodeMirror.defaults.mode && name != "null") CodeMirror.defaults.mode = name; - defineMode.apply(this, arguments); -}; + if (!CodeMirror.defaults.mode && name != "null") CodeMirror.defaults.mode = name + defineMode.apply(this, arguments) +} -CodeMirror.defineMIME = defineMIME; +CodeMirror.defineMIME = defineMIME // Minimal default mode. CodeMirror.defineMode("null", function() { - return {token: function(stream) {stream.skipToEnd();}}; -}); -CodeMirror.defineMIME("text/plain", "null"); + return {token: function(stream) {stream.skipToEnd()}} +}) +CodeMirror.defineMIME("text/plain", "null") // EXTENSIONS CodeMirror.defineExtension = function(name, func) { - CodeMirror.prototype[name] = func; -}; + CodeMirror.prototype[name] = func +} CodeMirror.defineDocExtension = function(name, func) { - Doc.prototype[name] = func; -}; + Doc.prototype[name] = func +} -import { fromTextArea } from "./fromTextArea"; +import { fromTextArea } from "./fromTextArea" -CodeMirror.fromTextArea = fromTextArea; +CodeMirror.fromTextArea = fromTextArea -import { addLegacyProps } from "./legacy"; +import { addLegacyProps } from "./legacy" -addLegacyProps(CodeMirror); +addLegacyProps(CodeMirror) -CodeMirror.version = "5.19.1"; +CodeMirror.version = "5.19.1" diff --git a/src/edit/methods.js b/src/edit/methods.js index 84aaf397..828304d3 100644 --- a/src/edit/methods.js +++ b/src/edit/methods.js @@ -1,27 +1,27 @@ -import { deleteNearSelection } from "./deleteNearSelection"; -import { changeLine } from "../model/changes"; -import { commands } from "./commands"; -import { attachDoc } from "../model/document_data"; -import { activeElt, addClass, rmClass } from "../util/dom"; -import { eventMixin, signal } from "../util/event"; -import { getLineStyles, getStateBefore, takeToken } from "../line/highlight"; -import { indentLine } from "../input/indent"; -import { triggerElectric } from "../input/input"; -import { onKeyDown, onKeyPress, onKeyUp } from "./key_events"; -import { getKeyMap } from "../input/keymap"; -import { methodOp, operation, runInOp } from "../display/operations"; -import { clipLine, clipPos, cmp, Pos } from "../line/pos"; -import { charCoords, charWidth, clearCaches, clearLineMeasurementCache, coordsChar, cursorCoords, displayHeight, displayWidth, estimateLineHeights, fromCoordSystem, intoCoordSystem, scrollGap, textHeight } from "../measurement/position_measurement"; -import { Range } from "../model/selection"; -import { replaceOneSelection, skipAtomic } from "../model/selection_updates"; -import { addToScrollPos, calculateScrollPos, ensureCursorVisible, resolveScrollToPos, scrollIntoView } from "../display/scrolling"; -import { heightAtLine } from "../line/spans"; -import { updateGutterSpace } from "../display/update_display"; -import { lineLeft, lineRight, moveLogically, moveVisually } from "../util/bidi"; -import { indexOf, insertSorted, isEmpty, isWordChar, sel_dontScroll, sel_move } from "../util/misc"; -import { signalLater } from "../util/operation_group"; -import { getLine, isLine, lineAtHeight, lineNo } from "../line/utils_line"; -import { regChange, regLineChange } from "../display/view_tracking"; +import { deleteNearSelection } from "./deleteNearSelection" +import { changeLine } from "../model/changes" +import { commands } from "./commands" +import { attachDoc } from "../model/document_data" +import { activeElt, addClass, rmClass } from "../util/dom" +import { eventMixin, signal } from "../util/event" +import { getLineStyles, getStateBefore, takeToken } from "../line/highlight" +import { indentLine } from "../input/indent" +import { triggerElectric } from "../input/input" +import { onKeyDown, onKeyPress, onKeyUp } from "./key_events" +import { getKeyMap } from "../input/keymap" +import { methodOp, operation, runInOp } from "../display/operations" +import { clipLine, clipPos, cmp, Pos } from "../line/pos" +import { charCoords, charWidth, clearCaches, clearLineMeasurementCache, coordsChar, cursorCoords, displayHeight, displayWidth, estimateLineHeights, fromCoordSystem, intoCoordSystem, scrollGap, textHeight } from "../measurement/position_measurement" +import { Range } from "../model/selection" +import { replaceOneSelection, skipAtomic } from "../model/selection_updates" +import { addToScrollPos, calculateScrollPos, ensureCursorVisible, resolveScrollToPos, scrollIntoView } from "../display/scrolling" +import { heightAtLine } from "../line/spans" +import { updateGutterSpace } from "../display/update_display" +import { lineLeft, lineRight, moveLogically, moveVisually } from "../util/bidi" +import { indexOf, insertSorted, isEmpty, isWordChar, sel_dontScroll, sel_move } from "../util/misc" +import { signalLater } from "../util/operation_group" +import { getLine, isLine, lineAtHeight, lineNo } from "../line/utils_line" +import { regChange, regLineChange } from "../display/view_tracking" // The publicly visible API. Note that methodOp(f) means // 'wrap f in an operation, performed on its `this` parameter'. @@ -32,84 +32,84 @@ import { regChange, regLineChange } from "../display/view_tracking"; // convenience. export default function(CodeMirror) { - var optionHandlers = CodeMirror.optionHandlers; + var optionHandlers = CodeMirror.optionHandlers - var helpers = CodeMirror.helpers = {}; + var helpers = CodeMirror.helpers = {} CodeMirror.prototype = { constructor: CodeMirror, - focus: function(){window.focus(); this.display.input.focus();}, + focus: function(){window.focus(); this.display.input.focus()}, setOption: function(option, value) { - var options = this.options, old = options[option]; - if (options[option] == value && option != "mode") return; - options[option] = value; + var options = this.options, old = options[option] + if (options[option] == value && option != "mode") return + options[option] = value if (optionHandlers.hasOwnProperty(option)) - operation(this, optionHandlers[option])(this, value, old); + operation(this, optionHandlers[option])(this, value, old) }, - getOption: function(option) {return this.options[option];}, - getDoc: function() {return this.doc;}, + getOption: function(option) {return this.options[option]}, + getDoc: function() {return this.doc}, addKeyMap: function(map, bottom) { - this.state.keyMaps[bottom ? "push" : "unshift"](getKeyMap(map)); + this.state.keyMaps[bottom ? "push" : "unshift"](getKeyMap(map)) }, removeKeyMap: function(map) { - var maps = this.state.keyMaps; + var maps = this.state.keyMaps for (var i = 0; i < maps.length; ++i) if (maps[i] == map || maps[i].name == map) { - maps.splice(i, 1); - return true; + maps.splice(i, 1) + return true } }, addOverlay: methodOp(function(spec, options) { - var mode = spec.token ? spec : CodeMirror.getMode(this.options, spec); - if (mode.startState) throw new Error("Overlays may not be stateful."); + var mode = spec.token ? spec : CodeMirror.getMode(this.options, spec) + if (mode.startState) throw new Error("Overlays may not be stateful.") insertSorted(this.state.overlays, {mode: mode, modeSpec: spec, opaque: options && options.opaque, priority: (options && options.priority) || 0}, function(overlay) { return overlay.priority }) - this.state.modeGen++; - regChange(this); + this.state.modeGen++ + regChange(this) }), removeOverlay: methodOp(function(spec) { - var overlays = this.state.overlays; + var overlays = this.state.overlays for (var i = 0; i < overlays.length; ++i) { - var cur = overlays[i].modeSpec; + var cur = overlays[i].modeSpec if (cur == spec || typeof spec == "string" && cur.name == spec) { - overlays.splice(i, 1); - this.state.modeGen++; - regChange(this); - return; + overlays.splice(i, 1) + this.state.modeGen++ + regChange(this) + return } } }), indentLine: methodOp(function(n, dir, aggressive) { if (typeof dir != "string" && typeof dir != "number") { - if (dir == null) dir = this.options.smartIndent ? "smart" : "prev"; - else dir = dir ? "add" : "subtract"; + 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)) indentLine(this, n, dir, aggressive) }), indentSelection: methodOp(function(how) { - var ranges = this.doc.sel.ranges, end = -1; + var ranges = this.doc.sel.ranges, end = -1 for (var i = 0; i < ranges.length; i++) { - var range = ranges[i]; + var range = ranges[i] if (!range.empty()) { - var from = range.from(), to = range.to(); - var start = Math.max(end, from.line); - end = Math.min(this.lastLine(), to.line - (to.ch ? 0 : 1)) + 1; + var from = range.from(), to = range.to() + var start = Math.max(end, from.line) + end = Math.min(this.lastLine(), to.line - (to.ch ? 0 : 1)) + 1 for (var j = start; j < end; ++j) - indentLine(this, j, how); - var newRanges = this.doc.sel.ranges; + indentLine(this, j, how) + var newRanges = this.doc.sel.ranges if (from.ch == 0 && ranges.length == newRanges.length && newRanges[i].from().ch > 0) - replaceOneSelection(this.doc, i, new Range(from, newRanges[i].to()), sel_dontScroll); + replaceOneSelection(this.doc, i, new Range(from, newRanges[i].to()), sel_dontScroll) } else if (range.head.line > end) { - indentLine(this, range.head.line, how, true); - end = range.head.line; - if (i == this.doc.sel.primIndex) ensureCursorVisible(this); + indentLine(this, range.head.line, how, true) + end = range.head.line + if (i == this.doc.sel.primIndex) ensureCursorVisible(this) } } }), @@ -117,178 +117,178 @@ export default function(CodeMirror) { // Fetch the parser token for a given character. Useful for hacks // that want to inspect the mode state (say, for completion). getTokenAt: function(pos, precise) { - return takeToken(this, pos, precise); + return takeToken(this, pos, precise) }, getLineTokens: function(line, precise) { - return takeToken(this, Pos(line), precise, true); + return takeToken(this, Pos(line), precise, true) }, getTokenTypeAt: function(pos) { - 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; - var type; - if (ch == 0) type = styles[2]; + 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 + var type + if (ch == 0) type = styles[2] else for (;;) { - var mid = (before + after) >> 1; - if ((mid ? styles[mid * 2 - 1] : 0) >= ch) after = mid; - else if (styles[mid * 2 + 1] < ch) before = mid + 1; - else { type = styles[mid * 2 + 2]; break; } + var mid = (before + after) >> 1 + if ((mid ? styles[mid * 2 - 1] : 0) >= ch) after = mid + else if (styles[mid * 2 + 1] < ch) before = mid + 1 + else { type = styles[mid * 2 + 2]; break } } - var cut = type ? type.indexOf("cm-overlay ") : -1; - return cut < 0 ? type : cut == 0 ? null : type.slice(0, cut - 1); + var cut = type ? type.indexOf("cm-overlay ") : -1 + return cut < 0 ? type : cut == 0 ? null : type.slice(0, cut - 1) }, getModeAt: function(pos) { - var mode = this.doc.mode; - if (!mode.innerMode) return mode; - return CodeMirror.innerMode(mode, this.getTokenAt(pos).state).mode; + var mode = this.doc.mode + if (!mode.innerMode) return mode + return CodeMirror.innerMode(mode, this.getTokenAt(pos).state).mode }, getHelper: function(pos, type) { - return this.getHelpers(pos, type)[0]; + return this.getHelpers(pos, type)[0] }, getHelpers: function(pos, type) { - var found = []; - if (!helpers.hasOwnProperty(type)) return found; - var help = helpers[type], mode = this.getModeAt(pos); + var found = [] + if (!helpers.hasOwnProperty(type)) return found + var help = helpers[type], mode = this.getModeAt(pos) if (typeof mode[type] == "string") { - if (help[mode[type]]) found.push(help[mode[type]]); + if (help[mode[type]]) found.push(help[mode[type]]) } else if (mode[type]) { for (var i = 0; i < mode[type].length; i++) { - var val = help[mode[type][i]]; - if (val) found.push(val); + var val = help[mode[type][i]] + if (val) found.push(val) } } else if (mode.helperType && help[mode.helperType]) { - found.push(help[mode.helperType]); + found.push(help[mode.helperType]) } else if (help[mode.name]) { - found.push(help[mode.name]); + found.push(help[mode.name]) } for (var i = 0; i < help._global.length; i++) { - var cur = help._global[i]; + var cur = help._global[i] if (cur.pred(mode, this) && indexOf(found, cur.val) == -1) - found.push(cur.val); + found.push(cur.val) } - return found; + return found }, getStateAfter: function(line, precise) { - var doc = this.doc; - line = clipLine(doc, line == null ? doc.first + doc.size - 1: line); - return getStateBefore(this, line + 1, precise); + var doc = this.doc + line = clipLine(doc, line == null ? doc.first + doc.size - 1: line) + return getStateBefore(this, line + 1, precise) }, cursorCoords: function(start, mode) { - var pos, range = this.doc.sel.primary(); - if (start == null) pos = range.head; - else if (typeof start == "object") pos = clipPos(this.doc, start); - else pos = start ? range.from() : range.to(); - return cursorCoords(this, pos, mode || "page"); + var pos, range = this.doc.sel.primary() + if (start == null) pos = range.head + else if (typeof start == "object") pos = clipPos(this.doc, start) + else pos = start ? range.from() : range.to() + return cursorCoords(this, pos, mode || "page") }, charCoords: function(pos, mode) { - return charCoords(this, clipPos(this.doc, pos), mode || "page"); + return charCoords(this, clipPos(this.doc, pos), mode || "page") }, coordsChar: function(coords, mode) { - coords = fromCoordSystem(this, coords, mode || "page"); - return coordsChar(this, coords.left, coords.top); + coords = fromCoordSystem(this, coords, mode || "page") + 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); + height = fromCoordSystem(this, {top: height, left: 0}, mode || "page").top + return lineAtHeight(this.doc, height + this.display.viewOffset) }, heightAtLine: function(line, mode) { - var end = false, lineObj; + var end = false, lineObj if (typeof line == "number") { - var last = this.doc.first + this.doc.size - 1; - if (line < this.doc.first) line = this.doc.first; - else if (line > last) { line = last; end = true; } - lineObj = getLine(this.doc, line); + var last = this.doc.first + this.doc.size - 1 + if (line < this.doc.first) line = this.doc.first + else if (line > last) { line = last; end = true } + lineObj = getLine(this.doc, line) } else { - lineObj = line; + lineObj = line } return intoCoordSystem(this, lineObj, {top: 0, left: 0}, mode || "page").top + - (end ? this.doc.height - heightAtLine(lineObj) : 0); + (end ? this.doc.height - heightAtLine(lineObj) : 0) }, - defaultTextHeight: function() { return textHeight(this.display); }, - defaultCharWidth: function() { return charWidth(this.display); }, + defaultTextHeight: function() { return textHeight(this.display) }, + defaultCharWidth: function() { return charWidth(this.display) }, setGutterMarker: methodOp(function(line, gutterID, value) { return changeLine(this.doc, line, "gutter", function(line) { - var markers = line.gutterMarkers || (line.gutterMarkers = {}); - markers[gutterID] = value; - if (!value && isEmpty(markers)) line.gutterMarkers = null; - return true; - }); + var markers = line.gutterMarkers || (line.gutterMarkers = {}) + markers[gutterID] = value + if (!value && isEmpty(markers)) line.gutterMarkers = null + return true + }) }), clearGutter: methodOp(function(gutterID) { - var cm = this, doc = cm.doc, i = doc.first; + var cm = this, doc = cm.doc, i = doc.first doc.iter(function(line) { if (line.gutterMarkers && line.gutterMarkers[gutterID]) { - line.gutterMarkers[gutterID] = null; - regLineChange(cm, i, "gutter"); - if (isEmpty(line.gutterMarkers)) line.gutterMarkers = null; + line.gutterMarkers[gutterID] = null + regLineChange(cm, i, "gutter") + if (isEmpty(line.gutterMarkers)) line.gutterMarkers = null } - ++i; - }); + ++i + }) }), lineInfo: function(line) { if (typeof line == "number") { - if (!isLine(this.doc, line)) return null; - var n = line; - line = getLine(this.doc, line); - if (!line) return null; + if (!isLine(this.doc, line)) return null + var n = line + line = getLine(this.doc, line) + if (!line) return null } else { - var n = lineNo(line); - if (n == null) return null; + var n = lineNo(line) + if (n == null) return null } return {line: n, handle: line, text: line.text, gutterMarkers: line.gutterMarkers, textClass: line.textClass, bgClass: line.bgClass, wrapClass: line.wrapClass, - widgets: line.widgets}; + widgets: line.widgets} }, - getViewport: function() { return {from: this.display.viewFrom, to: this.display.viewTo};}, + getViewport: function() { return {from: this.display.viewFrom, to: this.display.viewTo}}, addWidget: function(pos, node, scroll, vert, horiz) { - var display = this.display; - pos = cursorCoords(this, clipPos(this.doc, pos)); - var top = pos.bottom, left = pos.left; - node.style.position = "absolute"; - node.setAttribute("cm-ignore-events", "true"); - this.display.input.setUneditable(node); - display.sizer.appendChild(node); + var display = this.display + pos = cursorCoords(this, clipPos(this.doc, pos)) + var top = pos.bottom, left = pos.left + node.style.position = "absolute" + node.setAttribute("cm-ignore-events", "true") + this.display.input.setUneditable(node) + display.sizer.appendChild(node) if (vert == "over") { - top = pos.top; + top = pos.top } else if (vert == "above" || vert == "near") { var vspace = Math.max(display.wrapper.clientHeight, this.doc.height), - hspace = Math.max(display.sizer.clientWidth, display.lineSpace.clientWidth); + hspace = Math.max(display.sizer.clientWidth, display.lineSpace.clientWidth) // Default to positioning above (if specified and possible); otherwise default to positioning below if ((vert == 'above' || pos.bottom + node.offsetHeight > vspace) && pos.top > node.offsetHeight) - top = pos.top - node.offsetHeight; + top = pos.top - node.offsetHeight else if (pos.bottom + node.offsetHeight <= vspace) - top = pos.bottom; + top = pos.bottom if (left + node.offsetWidth > hspace) - left = hspace - node.offsetWidth; + left = hspace - node.offsetWidth } - node.style.top = top + "px"; - node.style.left = node.style.right = ""; + node.style.top = top + "px" + node.style.left = node.style.right = "" if (horiz == "right") { - left = display.sizer.clientWidth - node.offsetWidth; - node.style.right = "0px"; + 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 (horiz == "left") left = 0 + else if (horiz == "middle") left = (display.sizer.clientWidth - node.offsetWidth) / 2 + node.style.left = left + "px" } if (scroll) - scrollIntoView(this, left, top, left + node.offsetWidth, top + node.offsetHeight); + scrollIntoView(this, left, top, left + node.offsetWidth, top + node.offsetHeight) }, triggerOnKeyDown: methodOp(onKeyDown), @@ -297,199 +297,199 @@ export default function(CodeMirror) { execCommand: function(cmd) { if (commands.hasOwnProperty(cmd)) - return commands[cmd].call(null, this); + return commands[cmd].call(null, this) }, - triggerElectric: methodOp(function(text) { triggerElectric(this, text); }), + triggerElectric: methodOp(function(text) { triggerElectric(this, text) }), findPosH: function(from, amount, unit, visually) { - var dir = 1; - if (amount < 0) { dir = -1; amount = -amount; } + 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; + cur = findPosH(this.doc, cur, dir, unit, visually) + if (cur.hitSide) break } - return cur; + return cur }, moveH: methodOp(function(dir, unit) { - var cm = this; + var cm = this cm.extendSelectionsBy(function(range) { if (cm.display.shift || cm.doc.extend || range.empty()) - return findPosH(cm.doc, range.head, dir, unit, cm.options.rtlMoveVisually); + return findPosH(cm.doc, range.head, dir, unit, cm.options.rtlMoveVisually) else - return dir < 0 ? range.from() : range.to(); - }, sel_move); + return dir < 0 ? range.from() : range.to() + }, sel_move) }), deleteH: methodOp(function(dir, unit) { - var sel = this.doc.sel, doc = this.doc; + var sel = this.doc.sel, doc = this.doc if (sel.somethingSelected()) - doc.replaceSelection("", null, "+delete"); + doc.replaceSelection("", null, "+delete") else deleteNearSelection(this, function(range) { - var other = findPosH(doc, range.head, dir, unit, false); - return dir < 0 ? {from: other, to: range.head} : {from: range.head, to: other}; - }); + var other = findPosH(doc, range.head, dir, unit, false) + return dir < 0 ? {from: other, to: range.head} : {from: range.head, to: other} + }) }), findPosV: function(from, amount, unit, goalColumn) { - var dir = 1, x = goalColumn; - if (amount < 0) { dir = -1; amount = -amount; } + 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"); - if (x == null) x = coords.left; - else coords.left = x; - cur = findPosV(this, coords, dir, unit); - if (cur.hitSide) break; + 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 } - return cur; + return cur }, moveV: methodOp(function(dir, unit) { - var cm = this, doc = this.doc, goals = []; - var collapse = !cm.display.shift && !doc.extend && doc.sel.somethingSelected(); + var cm = this, doc = this.doc, goals = [] + var collapse = !cm.display.shift && !doc.extend && doc.sel.somethingSelected() doc.extendSelectionsBy(function(range) { if (collapse) - return dir < 0 ? range.from() : range.to(); - var headPos = cursorCoords(cm, range.head, "div"); - if (range.goalColumn != null) headPos.left = range.goalColumn; - goals.push(headPos.left); - var pos = findPosV(cm, headPos, dir, unit); + return dir < 0 ? range.from() : range.to() + var headPos = cursorCoords(cm, range.head, "div") + if (range.goalColumn != null) headPos.left = range.goalColumn + goals.push(headPos.left) + var pos = findPosV(cm, headPos, dir, unit) if (unit == "page" && range == doc.sel.primary()) - addToScrollPos(cm, null, charCoords(cm, pos, "div").top - headPos.top); - return pos; - }, sel_move); + addToScrollPos(cm, null, charCoords(cm, pos, "div").top - headPos.top) + return pos + }, sel_move) if (goals.length) for (var i = 0; i < doc.sel.ranges.length; i++) - doc.sel.ranges[i].goalColumn = goals[i]; + 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; + 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 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; + ? 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)); + return new Range(Pos(pos.line, start), Pos(pos.line, end)) }, toggleOverwrite: function(value) { - if (value != null && value == this.state.overwrite) return; + if (value != null && value == this.state.overwrite) return if (this.state.overwrite = !this.state.overwrite) - addClass(this.display.cursorDiv, "CodeMirror-overwrite"); + addClass(this.display.cursorDiv, "CodeMirror-overwrite") else - rmClass(this.display.cursorDiv, "CodeMirror-overwrite"); + rmClass(this.display.cursorDiv, "CodeMirror-overwrite") - signal(this, "overwriteToggle", this, this.state.overwrite); + signal(this, "overwriteToggle", this, this.state.overwrite) }, - hasFocus: function() { return this.display.input.getField() == activeElt(); }, - isReadOnly: function() { return !!(this.options.readOnly || this.doc.cantEdit); }, + hasFocus: function() { return this.display.input.getField() == activeElt() }, + isReadOnly: function() { return !!(this.options.readOnly || this.doc.cantEdit) }, scrollTo: methodOp(function(x, y) { - if (x != null || y != null) resolveScrollToPos(this); - if (x != null) this.curOp.scrollLeft = x; - if (y != null) this.curOp.scrollTop = y; + if (x != null || y != null) resolveScrollToPos(this) + if (x != null) this.curOp.scrollLeft = x + if (y != null) this.curOp.scrollTop = y }), getScrollInfo: function() { - var scroller = this.display.scroller; + var scroller = this.display.scroller return {left: scroller.scrollLeft, top: scroller.scrollTop, height: scroller.scrollHeight - scrollGap(this) - this.display.barHeight, width: scroller.scrollWidth - scrollGap(this) - this.display.barWidth, - clientHeight: displayHeight(this), clientWidth: displayWidth(this)}; + clientHeight: displayHeight(this), clientWidth: displayWidth(this)} }, scrollIntoView: methodOp(function(range, margin) { if (range == null) { - range = {from: this.doc.sel.primary().head, to: null}; - if (margin == null) margin = this.options.cursorScrollMargin; + range = {from: this.doc.sel.primary().head, to: null} + if (margin == null) margin = this.options.cursorScrollMargin } else if (typeof range == "number") { - range = {from: Pos(range, 0), to: null}; + range = {from: Pos(range, 0), to: null} } else if (range.from == null) { - range = {from: range, to: null}; + range = {from: range, to: null} } - if (!range.to) range.to = range.from; - range.margin = margin || 0; + if (!range.to) range.to = range.from + range.margin = margin || 0 if (range.from.line != null) { - resolveScrollToPos(this); - this.curOp.scrollToPos = range; + resolveScrollToPos(this) + this.curOp.scrollToPos = range } else { var sPos = calculateScrollPos(this, Math.min(range.from.left, range.to.left), Math.min(range.from.top, range.to.top) - range.margin, Math.max(range.from.right, range.to.right), - Math.max(range.from.bottom, range.to.bottom) + range.margin); - this.scrollTo(sPos.scrollLeft, sPos.scrollTop); + Math.max(range.from.bottom, range.to.bottom) + range.margin) + this.scrollTo(sPos.scrollLeft, sPos.scrollTop) } }), setSize: methodOp(function(width, height) { - var cm = this; + var cm = this function interpret(val) { - return typeof val == "number" || /^\d+$/.test(String(val)) ? val + "px" : val; + return typeof val == "number" || /^\d+$/.test(String(val)) ? val + "px" : val } - if (width != null) cm.display.wrapper.style.width = interpret(width); - if (height != null) cm.display.wrapper.style.height = interpret(height); - if (cm.options.lineWrapping) clearLineMeasurementCache(this); - var lineNo = cm.display.viewFrom; + if (width != null) cm.display.wrapper.style.width = interpret(width) + if (height != null) cm.display.wrapper.style.height = interpret(height) + if (cm.options.lineWrapping) clearLineMeasurementCache(this) + var lineNo = cm.display.viewFrom cm.doc.iter(lineNo, cm.display.viewTo, function(line) { if (line.widgets) for (var i = 0; i < line.widgets.length; i++) - if (line.widgets[i].noHScroll) { regLineChange(cm, lineNo, "widget"); break; } - ++lineNo; - }); - cm.curOp.forceUpdate = true; - signal(cm, "refresh", this); + if (line.widgets[i].noHScroll) { regLineChange(cm, lineNo, "widget"); break } + ++lineNo + }) + cm.curOp.forceUpdate = true + signal(cm, "refresh", this) }), - operation: function(f){return runInOp(this, f);}, + operation: function(f){return runInOp(this, f)}, refresh: methodOp(function() { - var oldHeight = this.display.cachedTextHeight; - regChange(this); - this.curOp.forceUpdate = true; - clearCaches(this); - this.scrollTo(this.doc.scrollLeft, this.doc.scrollTop); - updateGutterSpace(this); + var oldHeight = this.display.cachedTextHeight + regChange(this) + this.curOp.forceUpdate = true + clearCaches(this) + this.scrollTo(this.doc.scrollLeft, this.doc.scrollTop) + updateGutterSpace(this) if (oldHeight == null || Math.abs(oldHeight - textHeight(this.display)) > .5) - estimateLineHeights(this); - signal(this, "refresh", this); + estimateLineHeights(this) + signal(this, "refresh", this) }), swapDoc: methodOp(function(doc) { - var old = this.doc; - old.cm = null; - attachDoc(this, doc); - clearCaches(this); - this.display.input.reset(); - this.scrollTo(doc.scrollLeft, doc.scrollTop); - this.curOp.forceScroll = true; - signalLater(this, "swapDoc", this, old); - return old; + var old = this.doc + old.cm = null + attachDoc(this, doc) + clearCaches(this) + this.display.input.reset() + this.scrollTo(doc.scrollLeft, doc.scrollTop) + this.curOp.forceScroll = true + signalLater(this, "swapDoc", this, old) + return old }), - getInputField: function(){return this.display.input.getField();}, - getWrapperElement: function(){return this.display.wrapper;}, - getScrollerElement: function(){return this.display.scroller;}, - getGutterElement: function(){return this.display.gutters;} - }; - eventMixin(CodeMirror); + getInputField: function(){return this.display.input.getField()}, + getWrapperElement: function(){return this.display.wrapper}, + getScrollerElement: function(){return this.display.scroller}, + getGutterElement: function(){return this.display.gutters} + } + eventMixin(CodeMirror) CodeMirror.registerHelper = function(type, name, value) { - if (!helpers.hasOwnProperty(type)) helpers[type] = CodeMirror[type] = {_global: []}; - helpers[type][name] = value; - }; + if (!helpers.hasOwnProperty(type)) helpers[type] = CodeMirror[type] = {_global: []} + helpers[type][name] = value + } CodeMirror.registerGlobalHelper = function(type, name, predicate, value) { - CodeMirror.registerHelper(type, name, value); - helpers[type]._global.push({pred: predicate, val: value}); - }; + CodeMirror.registerHelper(type, name, value) + helpers[type]._global.push({pred: predicate, val: value}) + } } // Used for horizontal relative motion. Dir is -1 or 1 (left or @@ -502,23 +502,23 @@ export default function(CodeMirror) { // position. The resulting position will have a hitSide=true // property if it reached the end of the document. function findPosH(doc, pos, dir, unit, visually) { - var line = pos.line, ch = pos.ch, origDir = dir; - var lineObj = getLine(doc, line); + var line = pos.line, ch = pos.ch, origDir = dir + var lineObj = getLine(doc, line) function findNextLine() { - var l = line + dir; + var l = line + dir if (l < doc.first || l >= doc.first + doc.size) return false - line = l; - return lineObj = getLine(doc, l); + line = l + return lineObj = getLine(doc, l) } function moveOnce(boundToLine) { - var next = (visually ? moveVisually : moveLogically)(lineObj, ch, dir, true); + 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; + if (visually) ch = (dir < 0 ? lineRight : lineLeft)(lineObj) + else ch = dir < 0 ? lineObj.text.length : 0 } else return false - } else ch = next; - return true; + } else ch = next + return true } if (unit == "char") { @@ -526,48 +526,48 @@ function findPosH(doc, pos, dir, unit, visually) { } else if (unit == "column") { moveOnce(true) } else if (unit == "word" || unit == "group") { - var sawType = null, group = unit == "group"; - var helper = doc.cm && doc.cm.getHelper(pos, "wordChars"); + var sawType = null, group = unit == "group" + var helper = doc.cm && doc.cm.getHelper(pos, "wordChars") for (var first = true;; first = false) { - if (dir < 0 && !moveOnce(!first)) break; - var cur = lineObj.text.charAt(ch) || "\n"; + if (dir < 0 && !moveOnce(!first)) break + var cur = lineObj.text.charAt(ch) || "\n" var type = isWordChar(cur, helper) ? "w" : group && cur == "\n" ? "n" : !group || /\s/.test(cur) ? null - : "p"; - if (group && !first && !type) type = "s"; + : "p" + if (group && !first && !type) type = "s" if (sawType && sawType != type) { - if (dir < 0) {dir = 1; moveOnce();} - break; + if (dir < 0) {dir = 1; moveOnce()} + break } - if (type) sawType = type; - if (dir > 0 && !moveOnce(!first)) break; + if (type) sawType = type + if (dir > 0 && !moveOnce(!first)) break } } - var result = skipAtomic(doc, Pos(line, ch), pos, origDir, true); - if (!cmp(pos, result)) result.hitSide = true; - return result; + var result = skipAtomic(doc, Pos(line, ch), pos, origDir, true) + if (!cmp(pos, result)) result.hitSide = true + return result } // For relative vertical movement. Dir may be -1 or 1. Unit can be // "page" or "line". The resulting position will have a hitSide=true // property if it reached the end of the document. function findPosV(cm, pos, dir, unit) { - var doc = cm.doc, 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); - var moveAmount = Math.max(pageSize - .5 * textHeight(cm.display), 3); - y = (dir > 0 ? pos.bottom : pos.top) + dir * moveAmount; + var pageSize = Math.min(cm.display.wrapper.clientHeight, window.innerHeight || document.documentElement.clientHeight) + var moveAmount = Math.max(pageSize - .5 * textHeight(cm.display), 3) + y = (dir > 0 ? pos.bottom : pos.top) + dir * moveAmount } else if (unit == "line") { - y = dir > 0 ? pos.bottom + 3 : pos.top - 3; + 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; + 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; + return target } diff --git a/src/edit/mouse_events.js b/src/edit/mouse_events.js index ebda0c28..408cc497 100644 --- a/src/edit/mouse_events.js +++ b/src/edit/mouse_events.js @@ -1,17 +1,17 @@ -import { delayBlurEvent, ensureFocus } from "../display/focus"; -import { operation } from "../display/operations"; -import { visibleLines } from "../display/update_lines"; -import { clipPos, cmp, maxPos, minPos, Pos } from "../line/pos"; -import { getLine, lineAtHeight } from "../line/utils_line"; -import { posFromMouse } from "../measurement/position_measurement"; -import { eventInWidget } from "../measurement/widgets"; -import { normalizeSelection, Range, Selection } from "../model/selection"; -import { extendRange, extendSelection, replaceOneSelection, setSelection } from "../model/selection_updates"; -import { captureRightClick, chromeOS, ie, ie_version, mac, webkit } from "../util/browser"; -import { activeElt } from "../util/dom"; -import { e_button, e_defaultPrevented, e_preventDefault, e_target, hasHandler, off, on, signal, signalDOMEvent } from "../util/event"; -import { dragAndDrop } from "../util/feature_detection"; -import { bind, countColumn, findColumn, sel_mouse } from "../util/misc"; +import { delayBlurEvent, ensureFocus } from "../display/focus" +import { operation } from "../display/operations" +import { visibleLines } from "../display/update_lines" +import { clipPos, cmp, maxPos, minPos, Pos } from "../line/pos" +import { getLine, lineAtHeight } from "../line/utils_line" +import { posFromMouse } from "../measurement/position_measurement" +import { eventInWidget } from "../measurement/widgets" +import { normalizeSelection, Range, Selection } from "../model/selection" +import { extendRange, extendSelection, replaceOneSelection, setSelection } from "../model/selection_updates" +import { captureRightClick, chromeOS, ie, ie_version, mac, webkit } from "../util/browser" +import { activeElt } from "../util/dom" +import { e_button, e_defaultPrevented, e_preventDefault, e_target, hasHandler, off, on, signal, signalDOMEvent } from "../util/event" +import { dragAndDrop } from "../util/feature_detection" +import { bind, countColumn, findColumn, sel_mouse } from "../util/misc" // A mouse down can be a single click, double click, triple click, // start of selection drag, start of text drag, new cursor @@ -19,275 +19,275 @@ import { bind, countColumn, findColumn, sel_mouse } from "../util/misc"; // middle-click-paste. Or it might be a click on something we should // not interfere with, such as a scrollbar or widget. export function onMouseDown(e) { - var cm = this, display = cm.display; - if (signalDOMEvent(cm, e) || display.activeTouch && display.input.supportsTouch()) return; - display.shift = e.shiftKey; + var cm = this, display = cm.display + if (signalDOMEvent(cm, e) || display.activeTouch && display.input.supportsTouch()) return + display.shift = e.shiftKey if (eventInWidget(display, e)) { if (!webkit) { // Briefly turn off draggability, to allow widgets to do // normal dragging things. - display.scroller.draggable = false; - setTimeout(function(){display.scroller.draggable = true;}, 100); + display.scroller.draggable = false + setTimeout(function(){display.scroller.draggable = true}, 100) } - return; + return } - if (clickInGutter(cm, e)) return; - var start = posFromMouse(cm, e); - window.focus(); + if (clickInGutter(cm, e)) return + var start = posFromMouse(cm, e) + window.focus() switch (e_button(e)) { case 1: // #3261: make sure, that we're not starting a second selection if (cm.state.selectingText) - cm.state.selectingText(e); + cm.state.selectingText(e) else if (start) - leftButtonDown(cm, e, start); + leftButtonDown(cm, e, start) else if (e_target(e) == display.scroller) - e_preventDefault(e); - break; + e_preventDefault(e) + break case 2: - if (webkit) cm.state.lastMiddleDown = +new Date; - if (start) extendSelection(cm.doc, start); - setTimeout(function() {display.input.focus();}, 20); - e_preventDefault(e); - break; + if (webkit) cm.state.lastMiddleDown = +new Date + if (start) extendSelection(cm.doc, start) + setTimeout(function() {display.input.focus()}, 20) + e_preventDefault(e) + break case 3: - if (captureRightClick) onContextMenu(cm, e); - else delayBlurEvent(cm); - break; + if (captureRightClick) onContextMenu(cm, e) + else delayBlurEvent(cm) + break } } -var lastClick, lastDoubleClick; +var lastClick, lastDoubleClick function leftButtonDown(cm, e, start) { - if (ie) setTimeout(bind(ensureFocus, cm), 0); - else cm.curOp.focus = activeElt(); + if (ie) setTimeout(bind(ensureFocus, cm), 0) + else cm.curOp.focus = activeElt() - var now = +new Date, type; + var now = +new Date, type if (lastDoubleClick && lastDoubleClick.time > now - 400 && cmp(lastDoubleClick.pos, start) == 0) { - type = "triple"; + type = "triple" } else if (lastClick && lastClick.time > now - 400 && cmp(lastClick.pos, start) == 0) { - type = "double"; - lastDoubleClick = {time: now, pos: start}; + type = "double" + lastDoubleClick = {time: now, pos: start} } else { - type = "single"; - lastClick = {time: now, pos: start}; + type = "single" + lastClick = {time: now, pos: start} } - var sel = cm.doc.sel, modifier = mac ? e.metaKey : e.ctrlKey, contained; + var sel = cm.doc.sel, modifier = mac ? e.metaKey : e.ctrlKey, contained if (cm.options.dragDrop && dragAndDrop && !cm.isReadOnly() && type == "single" && (contained = sel.contains(start)) > -1 && (cmp((contained = sel.ranges[contained]).from(), start) < 0 || start.xRel > 0) && (cmp(contained.to(), start) > 0 || start.xRel < 0)) - leftButtonStartDrag(cm, e, start, modifier); + leftButtonStartDrag(cm, e, start, modifier) else - leftButtonSelect(cm, e, start, type, modifier); + 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, modifier) { - var display = cm.display, startTime = +new Date; + var display = cm.display, startTime = +new Date var dragEnd = operation(cm, function(e2) { - if (webkit) display.scroller.draggable = false; - cm.state.draggingText = false; - off(document, "mouseup", dragEnd); - off(display.scroller, "drop", dragEnd); + if (webkit) display.scroller.draggable = 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); + e_preventDefault(e2) if (!modifier && +new Date - 200 < startTime) - extendSelection(cm.doc, start); + extendSelection(cm.doc, start) // Work around unexplainable focus problem in IE9 (#2127) and Chrome (#3081) if (webkit || ie && ie_version == 9) - setTimeout(function() {document.body.focus(); display.input.focus();}, 20); + setTimeout(function() {document.body.focus(); display.input.focus()}, 20) else - display.input.focus(); + display.input.focus() } - }); + }) // Let the drag handler handle this. - if (webkit) display.scroller.draggable = true; - cm.state.draggingText = dragEnd; + if (webkit) display.scroller.draggable = true + cm.state.draggingText = dragEnd dragEnd.copy = mac ? e.altKey : e.ctrlKey // IE's approach to draggable - if (display.scroller.dragDrop) display.scroller.dragDrop(); - on(document, "mouseup", dragEnd); - on(display.scroller, "drop", dragEnd); + if (display.scroller.dragDrop) display.scroller.dragDrop() + on(document, "mouseup", dragEnd) + on(display.scroller, "drop", dragEnd) } // Normal selection, as opposed to text dragging. function leftButtonSelect(cm, e, start, type, addNew) { - var display = cm.display, doc = cm.doc; - e_preventDefault(e); + var display = cm.display, doc = cm.doc + e_preventDefault(e) - var ourRange, ourIndex, startSel = doc.sel, ranges = startSel.ranges; + var ourRange, ourIndex, startSel = doc.sel, ranges = startSel.ranges if (addNew && !e.shiftKey) { - ourIndex = doc.sel.contains(start); + ourIndex = doc.sel.contains(start) if (ourIndex > -1) - ourRange = ranges[ourIndex]; + ourRange = ranges[ourIndex] else - ourRange = new Range(start, start); + ourRange = new Range(start, start) } else { - ourRange = doc.sel.primary(); - ourIndex = doc.sel.primIndex; + ourRange = doc.sel.primary() + ourIndex = doc.sel.primIndex } if (chromeOS ? e.shiftKey && e.metaKey : e.altKey) { - type = "rect"; - if (!addNew) ourRange = new Range(start, start); - start = posFromMouse(cm, e, true, true); - ourIndex = -1; + type = "rect" + if (!addNew) ourRange = new Range(start, start) + start = posFromMouse(cm, e, true, true) + ourIndex = -1 } else if (type == "double") { - var word = cm.findWordAt(start); + var word = cm.findWordAt(start) if (cm.display.shift || doc.extend) - ourRange = extendRange(doc, ourRange, word.anchor, word.head); + ourRange = extendRange(doc, ourRange, word.anchor, word.head) else - ourRange = word; + ourRange = word } else if (type == "triple") { - var line = new Range(Pos(start.line, 0), clipPos(doc, Pos(start.line + 1, 0))); + var line = new Range(Pos(start.line, 0), clipPos(doc, Pos(start.line + 1, 0))) if (cm.display.shift || doc.extend) - ourRange = extendRange(doc, ourRange, line.anchor, line.head); + ourRange = extendRange(doc, ourRange, line.anchor, line.head) else - ourRange = line; + ourRange = line } else { - ourRange = extendRange(doc, ourRange, start); + ourRange = extendRange(doc, ourRange, start) } if (!addNew) { - ourIndex = 0; - setSelection(doc, new Selection([ourRange], 0), sel_mouse); - startSel = doc.sel; + ourIndex = 0 + setSelection(doc, new Selection([ourRange], 0), sel_mouse) + startSel = doc.sel } else if (ourIndex == -1) { - ourIndex = ranges.length; + ourIndex = ranges.length setSelection(doc, normalizeSelection(ranges.concat([ourRange]), ourIndex), - {scroll: false, origin: "*mouse"}); + {scroll: false, origin: "*mouse"}) } else if (ranges.length > 1 && ranges[ourIndex].empty() && type == "single" && !e.shiftKey) { setSelection(doc, normalizeSelection(ranges.slice(0, ourIndex).concat(ranges.slice(ourIndex + 1)), 0), - {scroll: false, origin: "*mouse"}); - startSel = doc.sel; + {scroll: false, origin: "*mouse"}) + startSel = doc.sel } else { - replaceOneSelection(doc, ourIndex, ourRange, sel_mouse); + replaceOneSelection(doc, ourIndex, ourRange, sel_mouse) } - var lastPos = start; + var lastPos = start function extendTo(pos) { - if (cmp(lastPos, pos) == 0) return; - lastPos = pos; + if (cmp(lastPos, pos) == 0) return + lastPos = pos if (type == "rect") { - var ranges = [], tabSize = cm.options.tabSize; - var startCol = countColumn(getLine(doc, start.line).text, start.ch, tabSize); - var posCol = countColumn(getLine(doc, pos.line).text, pos.ch, tabSize); - var left = Math.min(startCol, posCol), right = Math.max(startCol, posCol); + var ranges = [], tabSize = cm.options.tabSize + var startCol = countColumn(getLine(doc, start.line).text, start.ch, tabSize) + var posCol = countColumn(getLine(doc, pos.line).text, pos.ch, tabSize) + var left = Math.min(startCol, posCol), right = Math.max(startCol, posCol) for (var line = Math.min(start.line, pos.line), end = Math.min(cm.lastLine(), Math.max(start.line, pos.line)); line <= end; line++) { - var text = getLine(doc, line).text, leftPos = findColumn(text, left, tabSize); + var text = getLine(doc, line).text, leftPos = findColumn(text, left, tabSize) if (left == right) - ranges.push(new Range(Pos(line, leftPos), Pos(line, leftPos))); + ranges.push(new Range(Pos(line, leftPos), Pos(line, leftPos))) else if (text.length > leftPos) - ranges.push(new Range(Pos(line, leftPos), Pos(line, findColumn(text, right, tabSize)))); + ranges.push(new Range(Pos(line, leftPos), Pos(line, findColumn(text, right, tabSize)))) } - if (!ranges.length) ranges.push(new Range(start, start)); + if (!ranges.length) ranges.push(new Range(start, start)) setSelection(doc, normalizeSelection(startSel.ranges.slice(0, ourIndex).concat(ranges), ourIndex), - {origin: "*mouse", scroll: false}); - cm.scrollIntoView(pos); + {origin: "*mouse", scroll: false}) + cm.scrollIntoView(pos) } else { - var oldRange = ourRange; - var anchor = oldRange.anchor, head = pos; + var oldRange = ourRange + var anchor = oldRange.anchor, head = pos if (type != "single") { if (type == "double") - var range = cm.findWordAt(pos); + var range = cm.findWordAt(pos) else - var range = new Range(Pos(pos.line, 0), clipPos(doc, Pos(pos.line + 1, 0))); + var range = new Range(Pos(pos.line, 0), clipPos(doc, Pos(pos.line + 1, 0))) if (cmp(range.anchor, anchor) > 0) { - head = range.head; - anchor = minPos(oldRange.from(), range.anchor); + head = range.head + anchor = minPos(oldRange.from(), range.anchor) } else { - head = range.anchor; - anchor = maxPos(oldRange.to(), range.head); + head = range.anchor + anchor = maxPos(oldRange.to(), range.head) } } - var ranges = startSel.ranges.slice(0); - ranges[ourIndex] = new Range(clipPos(doc, anchor), head); - setSelection(doc, normalizeSelection(ranges, ourIndex), sel_mouse); + var ranges = startSel.ranges.slice(0) + ranges[ourIndex] = new Range(clipPos(doc, anchor), head) + setSelection(doc, normalizeSelection(ranges, ourIndex), sel_mouse) } } - var editorSize = display.wrapper.getBoundingClientRect(); + 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; + var counter = 0 function extend(e) { - var curCount = ++counter; - var cur = posFromMouse(cm, e, true, type == "rect"); - if (!cur) return; + var curCount = ++counter + var cur = posFromMouse(cm, e, true, type == "rect") + if (!cur) return if (cmp(cur, lastPos) != 0) { - cm.curOp.focus = activeElt(); - extendTo(cur); - var visible = visibleLines(display, doc); + cm.curOp.focus = activeElt() + extendTo(cur) + var visible = visibleLines(display, doc) if (cur.line >= visible.to || cur.line < visible.from) - setTimeout(operation(cm, function(){if (counter == curCount) 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; + 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); + if (counter != curCount) return + display.scroller.scrollTop += outside + extend(e) + }), 50) } } function done(e) { - cm.state.selectingText = false; - counter = Infinity; - e_preventDefault(e); - display.input.focus(); - off(document, "mousemove", move); - off(document, "mouseup", up); - doc.history.lastSelOrigin = null; + cm.state.selectingText = false + counter = Infinity + e_preventDefault(e) + display.input.focus() + off(document, "mousemove", move) + off(document, "mouseup", up) + doc.history.lastSelOrigin = null } var move = operation(cm, function(e) { - if (!e_button(e)) done(e); - else extend(e); - }); - var up = operation(cm, done); - cm.state.selectingText = up; - on(document, "mousemove", move); - on(document, "mouseup", up); + if (!e_button(e)) done(e) + else extend(e) + }) + var up = operation(cm, done) + cm.state.selectingText = up + on(document, "mousemove", move) + on(document, "mouseup", up) } // Determines whether an event happened in the gutter, and fires the // handlers for the corresponding event. function gutterEvent(cm, e, type, prevent) { - try { var mX = e.clientX, mY = e.clientY; } - catch(e) { return false; } - if (mX >= Math.floor(cm.display.gutters.getBoundingClientRect().right)) return false; - if (prevent) e_preventDefault(e); + try { var mX = e.clientX, mY = e.clientY } + catch(e) { return false } + if (mX >= Math.floor(cm.display.gutters.getBoundingClientRect().right)) return false + if (prevent) e_preventDefault(e) - var display = cm.display; - var lineBox = display.lineDiv.getBoundingClientRect(); + var display = cm.display + var lineBox = display.lineDiv.getBoundingClientRect() - if (mY > lineBox.bottom || !hasHandler(cm, type)) return e_defaultPrevented(e); - mY -= lineBox.top - display.viewOffset; + if (mY > lineBox.bottom || !hasHandler(cm, type)) return e_defaultPrevented(e) + mY -= lineBox.top - display.viewOffset for (var i = 0; i < cm.options.gutters.length; ++i) { - var g = display.gutters.childNodes[i]; + var g = display.gutters.childNodes[i] if (g && g.getBoundingClientRect().right >= mX) { - var line = lineAtHeight(cm.doc, mY); - var gutter = cm.options.gutters[i]; - signal(cm, type, cm, line, gutter, e); - return e_defaultPrevented(e); + var line = lineAtHeight(cm.doc, mY) + var gutter = cm.options.gutters[i] + signal(cm, type, cm, line, gutter, e) + return e_defaultPrevented(e) } } } export function clickInGutter(cm, e) { - return gutterEvent(cm, e, "gutterClick", true); + return gutterEvent(cm, e, "gutterClick", true) } // CONTEXT MENU HANDLING @@ -296,12 +296,12 @@ export function clickInGutter(cm, e) { // textarea (making it as unobtrusive as possible) to let the // right-click take effect on it. export function onContextMenu(cm, e) { - if (eventInWidget(cm.display, e) || contextMenuInGutter(cm, e)) return; - if (signalDOMEvent(cm, e, "contextmenu")) return; - cm.display.input.onContextMenu(e); + if (eventInWidget(cm.display, e) || contextMenuInGutter(cm, e)) return + if (signalDOMEvent(cm, e, "contextmenu")) return + cm.display.input.onContextMenu(e) } function contextMenuInGutter(cm, e) { - if (!hasHandler(cm, "gutterContextMenu")) return false; - return gutterEvent(cm, e, "gutterContextMenu", false); + if (!hasHandler(cm, "gutterContextMenu")) return false + return gutterEvent(cm, e, "gutterContextMenu", false) } diff --git a/src/edit/options.js b/src/edit/options.js index 308eb574..cf0d4d81 100644 --- a/src/edit/options.js +++ b/src/edit/options.js @@ -1,196 +1,196 @@ -import { onBlur } from "../display/focus"; -import { setGuttersForLineNumbers, updateGutters } from "../display/gutters"; -import { alignHorizontally } from "../display/line_numbers"; -import { loadMode, resetModeState } from "../display/mode_state"; -import { initScrollbars, updateScrollbars } from "../display/scrollbars"; -import { updateSelection } from "../display/selection"; -import { regChange } from "../display/view_tracking"; -import { getKeyMap } from "../input/keymap"; -import { defaultSpecialCharPlaceholder } from "../line/line_data"; -import { Pos } from "../line/pos"; -import { findMaxLine } from "../line/spans"; -import { clearCaches, compensateForHScroll, estimateLineHeights } from "../measurement/position_measurement"; -import { replaceRange } from "../model/changes"; -import { mobile, windows } from "../util/browser"; -import { addClass, rmClass } from "../util/dom"; -import { off, on } from "../util/event"; - -import { themeChanged } from "./utils"; - -export var Init = {toString: function(){return "CodeMirror.Init";}}; - -export var defaults = {}; -export var optionHandlers = {}; +import { onBlur } from "../display/focus" +import { setGuttersForLineNumbers, updateGutters } from "../display/gutters" +import { alignHorizontally } from "../display/line_numbers" +import { loadMode, resetModeState } from "../display/mode_state" +import { initScrollbars, updateScrollbars } from "../display/scrollbars" +import { updateSelection } from "../display/selection" +import { regChange } from "../display/view_tracking" +import { getKeyMap } from "../input/keymap" +import { defaultSpecialCharPlaceholder } from "../line/line_data" +import { Pos } from "../line/pos" +import { findMaxLine } from "../line/spans" +import { clearCaches, compensateForHScroll, estimateLineHeights } from "../measurement/position_measurement" +import { replaceRange } from "../model/changes" +import { mobile, windows } from "../util/browser" +import { addClass, rmClass } from "../util/dom" +import { off, on } from "../util/event" + +import { themeChanged } from "./utils" + +export var Init = {toString: function(){return "CodeMirror.Init"}} + +export var defaults = {} +export var optionHandlers = {} export function defineOptions(CodeMirror) { - var optionHandlers = CodeMirror.optionHandlers; + var optionHandlers = CodeMirror.optionHandlers function option(name, deflt, handle, notOnInit) { - CodeMirror.defaults[name] = deflt; + CodeMirror.defaults[name] = deflt if (handle) optionHandlers[name] = - notOnInit ? function(cm, val, old) {if (old != Init) handle(cm, val, old);} : handle; + notOnInit ? function(cm, val, old) {if (old != Init) handle(cm, val, old)} : handle } - CodeMirror.defineOption = option; + CodeMirror.defineOption = option // Passed to option handlers when there is no old value. - CodeMirror.Init = Init; + CodeMirror.Init = Init // 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); + cm.setValue(val) + }, true) option("mode", null, function(cm, val) { - cm.doc.modeOption = val; - loadMode(cm); - }, true); + cm.doc.modeOption = val + loadMode(cm) + }, true) - option("indentUnit", 2, loadMode, true); - option("indentWithTabs", false); - option("smartIndent", true); + option("indentUnit", 2, loadMode, true) + option("indentWithTabs", false) + option("smartIndent", true) option("tabSize", 4, function(cm) { - resetModeState(cm); - clearCaches(cm); - regChange(cm); - }, true); + resetModeState(cm) + clearCaches(cm) + regChange(cm) + }, true) option("lineSeparator", null, function(cm, val) { - cm.doc.lineSep = val; - if (!val) return; - var newBreaks = [], lineNo = cm.doc.first; + cm.doc.lineSep = val + if (!val) return + var newBreaks = [], lineNo = cm.doc.first cm.doc.iter(function(line) { for (var pos = 0;;) { - var found = line.text.indexOf(val, pos); - if (found == -1) break; - pos = found + val.length; - newBreaks.push(Pos(lineNo, found)); + var found = line.text.indexOf(val, pos) + if (found == -1) break + pos = found + val.length + newBreaks.push(Pos(lineNo, found)) } - lineNo++; - }); + lineNo++ + }) for (var i = newBreaks.length - 1; i >= 0; i--) replaceRange(cm.doc, val, newBreaks[i], Pos(newBreaks[i].line, newBreaks[i].ch + val.length)) - }); + }) option("specialChars", /[\u0000-\u001f\u007f\u00ad\u200b-\u200f\u2028\u2029\ufeff]/g, function(cm, val, old) { - cm.state.specialChars = new RegExp(val.source + (val.test("\t") ? "" : "|\t"), "g"); - if (old != Init) cm.refresh(); - }); - option("specialCharPlaceholder", defaultSpecialCharPlaceholder, function(cm) {cm.refresh();}, true); - option("electricChars", true); + cm.state.specialChars = new RegExp(val.source + (val.test("\t") ? "" : "|\t"), "g") + if (old != Init) cm.refresh() + }) + option("specialCharPlaceholder", defaultSpecialCharPlaceholder, function(cm) {cm.refresh()}, true) + option("electricChars", true) option("inputStyle", mobile ? "contenteditable" : "textarea", function() { - throw new Error("inputStyle can not (yet) be changed in a running editor"); // FIXME - }, true); + throw new Error("inputStyle can not (yet) be changed in a running editor") // FIXME + }, true) option("spellcheck", false, function(cm, val) { cm.getInputField().spellcheck = val - }, true); - option("rtlMoveVisually", !windows); - option("wholeLineUpdateBefore", true); + }, true) + option("rtlMoveVisually", !windows) + option("wholeLineUpdateBefore", true) option("theme", "default", function(cm) { - themeChanged(cm); - guttersChanged(cm); - }, true); + themeChanged(cm) + guttersChanged(cm) + }, true) option("keyMap", "default", function(cm, val, old) { - var next = getKeyMap(val); - var prev = old != Init && getKeyMap(old); - if (prev && prev.detach) prev.detach(cm, next); - if (next.attach) next.attach(cm, prev || null); - }); - option("extraKeys", null); - - option("lineWrapping", false, wrappingChanged, true); + var next = getKeyMap(val) + var prev = old != Init && getKeyMap(old) + if (prev && prev.detach) prev.detach(cm, next) + if (next.attach) next.attach(cm, prev || null) + }) + option("extraKeys", null) + + option("lineWrapping", false, wrappingChanged, true) option("gutters", [], function(cm) { - setGuttersForLineNumbers(cm.options); - guttersChanged(cm); - }, true); + 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("coverGutterNextToScrollbar", false, function(cm) {updateScrollbars(cm);}, true); + cm.display.gutters.style.left = val ? compensateForHScroll(cm.display) + "px" : "0" + cm.refresh() + }, true) + option("coverGutterNextToScrollbar", false, function(cm) {updateScrollbars(cm)}, true) option("scrollbarStyle", "native", function(cm) { - initScrollbars(cm); - updateScrollbars(cm); - cm.display.scrollbars.setScrollTop(cm.doc.scrollTop); - cm.display.scrollbars.setScrollLeft(cm.doc.scrollLeft); - }, true); + initScrollbars(cm) + updateScrollbars(cm) + cm.display.scrollbars.setScrollTop(cm.doc.scrollTop) + cm.display.scrollbars.setScrollLeft(cm.doc.scrollLeft) + }, true) option("lineNumbers", false, function(cm) { - setGuttersForLineNumbers(cm.options); - guttersChanged(cm); - }, true); - option("firstLineNumber", 1, guttersChanged, true); - option("lineNumberFormatter", function(integer) {return integer;}, guttersChanged, true); - option("showCursorWhenSelecting", false, updateSelection, true); + setGuttersForLineNumbers(cm.options) + guttersChanged(cm) + }, true) + option("firstLineNumber", 1, guttersChanged, true) + option("lineNumberFormatter", function(integer) {return integer}, guttersChanged, true) + option("showCursorWhenSelecting", false, updateSelection, true) - option("resetSelectionOnContextMenu", true); - option("lineWiseCopyCut", true); + option("resetSelectionOnContextMenu", true) + option("lineWiseCopyCut", true) option("readOnly", false, function(cm, val) { if (val == "nocursor") { - onBlur(cm); - cm.display.input.blur(); - cm.display.disabled = true; + onBlur(cm) + cm.display.input.blur() + cm.display.disabled = true } else { - cm.display.disabled = false; + cm.display.disabled = false } cm.display.input.readOnlyChanged(val) - }); - option("disableInput", false, function(cm, val) {if (!val) cm.display.input.reset();}, true); - option("dragDrop", true, dragDropChanged); - option("allowDropFileTypes", null); - - option("cursorBlinkRate", 530); - option("cursorScrollMargin", 0); - option("cursorHeight", 1, updateSelection, true); - option("singleCursorHeightPerLine", true, updateSelection, true); - option("workTime", 100); - option("workDelay", 100); - option("flattenSpans", true, resetModeState, true); - option("addModeClass", false, resetModeState, true); - option("pollInterval", 100); - option("undoDepth", 200, function(cm, val){cm.doc.history.undoDepth = val;}); - option("historyEventDelay", 1250); - option("viewportMargin", 10, function(cm){cm.refresh();}, true); - option("maxHighlightLength", 10000, resetModeState, true); + }) + option("disableInput", false, function(cm, val) {if (!val) cm.display.input.reset()}, true) + option("dragDrop", true, dragDropChanged) + option("allowDropFileTypes", null) + + option("cursorBlinkRate", 530) + option("cursorScrollMargin", 0) + option("cursorHeight", 1, updateSelection, true) + option("singleCursorHeightPerLine", true, updateSelection, true) + option("workTime", 100) + option("workDelay", 100) + option("flattenSpans", true, resetModeState, true) + option("addModeClass", false, resetModeState, true) + option("pollInterval", 100) + option("undoDepth", 200, function(cm, val){cm.doc.history.undoDepth = val}) + option("historyEventDelay", 1250) + option("viewportMargin", 10, function(cm){cm.refresh()}, true) + option("maxHighlightLength", 10000, resetModeState, true) option("moveInputWithCursor", true, function(cm, val) { - if (!val) cm.display.input.resetPosition(); - }); + if (!val) cm.display.input.resetPosition() + }) option("tabindex", null, function(cm, val) { - cm.display.input.getField().tabIndex = val || ""; - }); - option("autofocus", null); + cm.display.input.getField().tabIndex = val || "" + }) + option("autofocus", null) } function guttersChanged(cm) { - updateGutters(cm); - regChange(cm); - setTimeout(function(){alignHorizontally(cm);}, 20); + updateGutters(cm) + regChange(cm) + setTimeout(function(){alignHorizontally(cm)}, 20) } function dragDropChanged(cm, value, old) { - var wasOn = old && old != Init; + var wasOn = old && old != Init if (!value != !wasOn) { - var funcs = cm.display.dragFunctions; - var toggle = value ? on : off; - toggle(cm.display.scroller, "dragstart", funcs.start); - toggle(cm.display.scroller, "dragenter", funcs.enter); - toggle(cm.display.scroller, "dragover", funcs.over); - toggle(cm.display.scroller, "dragleave", funcs.leave); - toggle(cm.display.scroller, "drop", funcs.drop); + var funcs = cm.display.dragFunctions + var toggle = value ? on : off + toggle(cm.display.scroller, "dragstart", funcs.start) + toggle(cm.display.scroller, "dragenter", funcs.enter) + toggle(cm.display.scroller, "dragover", funcs.over) + toggle(cm.display.scroller, "dragleave", funcs.leave) + toggle(cm.display.scroller, "drop", funcs.drop) } } function wrappingChanged(cm) { if (cm.options.lineWrapping) { - addClass(cm.display.wrapper, "CodeMirror-wrap"); - cm.display.sizer.style.minWidth = ""; - cm.display.sizerWidth = null; + addClass(cm.display.wrapper, "CodeMirror-wrap") + cm.display.sizer.style.minWidth = "" + cm.display.sizerWidth = null } else { - rmClass(cm.display.wrapper, "CodeMirror-wrap"); - findMaxLine(cm); + rmClass(cm.display.wrapper, "CodeMirror-wrap") + findMaxLine(cm) } - estimateLineHeights(cm); - regChange(cm); - clearCaches(cm); - setTimeout(function(){updateScrollbars(cm);}, 100); + estimateLineHeights(cm) + regChange(cm) + clearCaches(cm) + setTimeout(function(){updateScrollbars(cm)}, 100) } diff --git a/src/edit/utils.js b/src/edit/utils.js index cdef3156..61f79557 100644 --- a/src/edit/utils.js +++ b/src/edit/utils.js @@ -1,7 +1,7 @@ -import { clearCaches } from "../measurement/position_measurement"; +import { clearCaches } from "../measurement/position_measurement" export 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); + cm.options.theme.replace(/(^|\s)\s*/g, " cm-s-") + clearCaches(cm) } diff --git a/src/input/ContentEditableInput.js b/src/input/ContentEditableInput.js index 69d53c0c..51597d19 100644 --- a/src/input/ContentEditableInput.js +++ b/src/input/ContentEditableInput.js @@ -1,317 +1,317 @@ -import { operation, runInOp } from "../display/operations"; -import { prepareSelection } from "../display/selection"; -import { regChange } from "../display/view_tracking"; -import { applyTextInput, copyableRanges, disableBrowserMagic, handlePaste, hiddenTextarea, lastCopied, setLastCopied } from "./input"; -import { cmp, maxPos, minPos, Pos } from "../line/pos"; -import { getBetween, getLine, lineNo } from "../line/utils_line"; -import { findViewForLine, findViewIndex, mapFromLineView, nodeAndOffsetInLineMap } from "../measurement/position_measurement"; -import { replaceRange } from "../model/changes"; -import { simpleSelection } from "../model/selection"; -import { setSelection } from "../model/selection_updates"; -import { getBidiPartAt, getOrder } from "../util/bidi"; -import { gecko, ie_version } from "../util/browser"; -import { contains, range, removeChildrenAndAdd, selectInput } from "../util/dom"; -import { on, signalDOMEvent } from "../util/event"; -import { copyObj, Delayed, lst, nothing, sel_dontScroll } from "../util/misc"; +import { operation, runInOp } from "../display/operations" +import { prepareSelection } from "../display/selection" +import { regChange } from "../display/view_tracking" +import { applyTextInput, copyableRanges, disableBrowserMagic, handlePaste, hiddenTextarea, lastCopied, setLastCopied } from "./input" +import { cmp, maxPos, minPos, Pos } from "../line/pos" +import { getBetween, getLine, lineNo } from "../line/utils_line" +import { findViewForLine, findViewIndex, mapFromLineView, nodeAndOffsetInLineMap } from "../measurement/position_measurement" +import { replaceRange } from "../model/changes" +import { simpleSelection } from "../model/selection" +import { setSelection } from "../model/selection_updates" +import { getBidiPartAt, getOrder } from "../util/bidi" +import { gecko, ie_version } from "../util/browser" +import { contains, range, removeChildrenAndAdd, selectInput } from "../util/dom" +import { on, signalDOMEvent } from "../util/event" +import { copyObj, Delayed, lst, nothing, sel_dontScroll } from "../util/misc" // CONTENTEDITABLE INPUT STYLE export default function ContentEditableInput(cm) { - this.cm = cm; - this.lastAnchorNode = this.lastAnchorOffset = this.lastFocusNode = this.lastFocusOffset = null; - this.polling = new Delayed(); - this.gracePeriod = false; + this.cm = cm + this.lastAnchorNode = this.lastAnchorOffset = this.lastFocusNode = this.lastFocusOffset = null + this.polling = new Delayed() + this.gracePeriod = false } ContentEditableInput.prototype = copyObj({ init: function(display) { - var input = this, cm = input.cm; - var div = input.div = display.lineDiv; - disableBrowserMagic(div, cm.options.spellcheck); + var input = this, cm = input.cm + var div = input.div = display.lineDiv + disableBrowserMagic(div, cm.options.spellcheck) on(div, "paste", function(e) { if (signalDOMEvent(cm, e) || handlePaste(e, cm)) return // IE doesn't fire input events, so we schedule a read for the pasted content in this way if (ie_version <= 11) setTimeout(operation(cm, function() { - if (!input.pollContent()) regChange(cm); + if (!input.pollContent()) regChange(cm) }), 20) }) on(div, "compositionstart", function(e) { - var data = e.data; - input.composing = {sel: cm.doc.sel, data: data, startData: data}; - if (!data) return; - var prim = cm.doc.sel.primary(); - var line = cm.getLine(prim.head.line); - var found = line.indexOf(data, Math.max(0, prim.head.ch - data.length)); + var data = e.data + input.composing = {sel: cm.doc.sel, data: data, startData: data} + if (!data) return + var prim = cm.doc.sel.primary() + var line = cm.getLine(prim.head.line) + var found = line.indexOf(data, Math.max(0, prim.head.ch - data.length)) if (found > -1 && found <= prim.head.ch) input.composing.sel = simpleSelection(Pos(prim.head.line, found), - Pos(prim.head.line, found + data.length)); - }); + Pos(prim.head.line, found + data.length)) + }) on(div, "compositionupdate", function(e) { - input.composing.data = e.data; - }); + input.composing.data = e.data + }) on(div, "compositionend", function(e) { - var ours = input.composing; - if (!ours) return; + var ours = input.composing + if (!ours) return if (e.data != ours.startData && !/\u200b/.test(e.data)) - ours.data = e.data; + ours.data = e.data // Need a small delay to prevent other code (input event, // selection polling) from doing damage when fired right after // compositionend. setTimeout(function() { if (!ours.handled) - input.applyComposition(ours); + input.applyComposition(ours) if (input.composing == ours) - input.composing = null; - }, 50); - }); + input.composing = null + }, 50) + }) on(div, "touchstart", function() { - input.forceCompositionEnd(); - }); + input.forceCompositionEnd() + }) on(div, "input", function() { - if (input.composing) return; + if (input.composing) return if (cm.isReadOnly() || !input.pollContent()) - runInOp(input.cm, function() {regChange(cm);}); - }); + runInOp(input.cm, function() {regChange(cm)}) + }) function onCopyCut(e) { if (signalDOMEvent(cm, e)) return if (cm.somethingSelected()) { - setLastCopied({lineWise: false, text: cm.getSelections()}); - if (e.type == "cut") cm.replaceSelection("", null, "cut"); + setLastCopied({lineWise: false, text: cm.getSelections()}) + if (e.type == "cut") cm.replaceSelection("", null, "cut") } else if (!cm.options.lineWiseCopyCut) { - return; + return } else { - var ranges = copyableRanges(cm); - setLastCopied({lineWise: true, text: ranges.text}); + var ranges = copyableRanges(cm) + setLastCopied({lineWise: true, text: ranges.text}) if (e.type == "cut") { cm.operation(function() { - cm.setSelections(ranges.ranges, 0, sel_dontScroll); - cm.replaceSelection("", null, "cut"); - }); + cm.setSelections(ranges.ranges, 0, sel_dontScroll) + cm.replaceSelection("", null, "cut") + }) } } if (e.clipboardData) { - e.clipboardData.clearData(); + e.clipboardData.clearData() var content = lastCopied.text.join("\n") // iOS exposes the clipboard API, but seems to discard content inserted into it - e.clipboardData.setData("Text", content); + e.clipboardData.setData("Text", content) if (e.clipboardData.getData("Text") == content) { - e.preventDefault(); + e.preventDefault() return } } // Old-fashioned briefly-focus-a-textarea hack - var kludge = hiddenTextarea(), te = kludge.firstChild; - cm.display.lineSpace.insertBefore(kludge, cm.display.lineSpace.firstChild); - te.value = lastCopied.text.join("\n"); - var hadFocus = document.activeElement; - selectInput(te); + var kludge = hiddenTextarea(), te = kludge.firstChild + cm.display.lineSpace.insertBefore(kludge, cm.display.lineSpace.firstChild) + te.value = lastCopied.text.join("\n") + var hadFocus = document.activeElement + selectInput(te) setTimeout(function() { - cm.display.lineSpace.removeChild(kludge); - hadFocus.focus(); + cm.display.lineSpace.removeChild(kludge) + hadFocus.focus() if (hadFocus == div) input.showPrimarySelection() - }, 50); + }, 50) } - on(div, "copy", onCopyCut); - on(div, "cut", onCopyCut); + on(div, "copy", onCopyCut) + on(div, "cut", onCopyCut) }, prepareSelection: function() { - var result = prepareSelection(this.cm, false); - result.focus = this.cm.state.focused; - return result; + var result = prepareSelection(this.cm, false) + result.focus = this.cm.state.focused + return result }, showSelection: function(info, takeFocus) { - if (!info || !this.cm.display.view.length) return; - if (info.focus || takeFocus) this.showPrimarySelection(); - this.showMultipleSelections(info); + if (!info || !this.cm.display.view.length) return + if (info.focus || takeFocus) this.showPrimarySelection() + this.showMultipleSelections(info) }, showPrimarySelection: function() { - var sel = window.getSelection(), prim = this.cm.doc.sel.primary(); - var curAnchor = domToPos(this.cm, sel.anchorNode, sel.anchorOffset); - var curFocus = domToPos(this.cm, sel.focusNode, sel.focusOffset); + var sel = window.getSelection(), prim = this.cm.doc.sel.primary() + var curAnchor = domToPos(this.cm, sel.anchorNode, sel.anchorOffset) + var curFocus = domToPos(this.cm, sel.focusNode, sel.focusOffset) if (curAnchor && !curAnchor.bad && curFocus && !curFocus.bad && cmp(minPos(curAnchor, curFocus), prim.from()) == 0 && cmp(maxPos(curAnchor, curFocus), prim.to()) == 0) - return; + return - var start = posToDOM(this.cm, prim.from()); - var end = posToDOM(this.cm, prim.to()); - if (!start && !end) return; + var start = posToDOM(this.cm, prim.from()) + var end = posToDOM(this.cm, prim.to()) + if (!start && !end) return - var view = this.cm.display.view; - var old = sel.rangeCount && sel.getRangeAt(0); + var view = this.cm.display.view + var old = sel.rangeCount && sel.getRangeAt(0) if (!start) { - start = {node: view[0].measure.map[2], offset: 0}; + start = {node: view[0].measure.map[2], offset: 0} } else if (!end) { // FIXME dangerously hacky - var measure = view[view.length - 1].measure; - var map = measure.maps ? measure.maps[measure.maps.length - 1] : measure.map; - end = {node: map[map.length - 1], offset: map[map.length - 2] - map[map.length - 3]}; + var measure = view[view.length - 1].measure + var map = measure.maps ? measure.maps[measure.maps.length - 1] : measure.map + end = {node: map[map.length - 1], offset: map[map.length - 2] - map[map.length - 3]} } - try { var rng = range(start.node, start.offset, end.offset, end.node); } + try { var rng = range(start.node, start.offset, end.offset, end.node) } catch(e) {} // Our model of the DOM might be outdated, in which case the range we try to set can be impossible if (rng) { if (!gecko && this.cm.state.focused) { - sel.collapse(start.node, start.offset); - if (!rng.collapsed) sel.addRange(rng); + sel.collapse(start.node, start.offset) + if (!rng.collapsed) sel.addRange(rng) } else { - sel.removeAllRanges(); - sel.addRange(rng); + sel.removeAllRanges() + sel.addRange(rng) } - if (old && sel.anchorNode == null) sel.addRange(old); - else if (gecko) this.startGracePeriod(); + if (old && sel.anchorNode == null) sel.addRange(old) + else if (gecko) this.startGracePeriod() } - this.rememberSelection(); + this.rememberSelection() }, startGracePeriod: function() { - var input = this; - clearTimeout(this.gracePeriod); + var input = this + clearTimeout(this.gracePeriod) this.gracePeriod = setTimeout(function() { - input.gracePeriod = false; + input.gracePeriod = false if (input.selectionChanged()) - input.cm.operation(function() { input.cm.curOp.selectionChanged = true; }); - }, 20); + input.cm.operation(function() { input.cm.curOp.selectionChanged = true }) + }, 20) }, showMultipleSelections: function(info) { - removeChildrenAndAdd(this.cm.display.cursorDiv, info.cursors); - removeChildrenAndAdd(this.cm.display.selectionDiv, info.selection); + removeChildrenAndAdd(this.cm.display.cursorDiv, info.cursors) + removeChildrenAndAdd(this.cm.display.selectionDiv, info.selection) }, rememberSelection: function() { - var sel = window.getSelection(); - this.lastAnchorNode = sel.anchorNode; this.lastAnchorOffset = sel.anchorOffset; - this.lastFocusNode = sel.focusNode; this.lastFocusOffset = sel.focusOffset; + var sel = window.getSelection() + this.lastAnchorNode = sel.anchorNode; this.lastAnchorOffset = sel.anchorOffset + this.lastFocusNode = sel.focusNode; this.lastFocusOffset = sel.focusOffset }, selectionInEditor: function() { - var sel = window.getSelection(); - if (!sel.rangeCount) return false; - var node = sel.getRangeAt(0).commonAncestorContainer; - return contains(this.div, node); + var sel = window.getSelection() + if (!sel.rangeCount) return false + var node = sel.getRangeAt(0).commonAncestorContainer + return contains(this.div, node) }, focus: function() { - if (this.cm.options.readOnly != "nocursor") this.div.focus(); + if (this.cm.options.readOnly != "nocursor") this.div.focus() }, - blur: function() { this.div.blur(); }, - getField: function() { return this.div; }, + blur: function() { this.div.blur() }, + getField: function() { return this.div }, - supportsTouch: function() { return true; }, + supportsTouch: function() { return true }, receivedFocus: function() { - var input = this; + var input = this if (this.selectionInEditor()) - this.pollSelection(); + this.pollSelection() else - runInOp(this.cm, function() { input.cm.curOp.selectionChanged = true; }); + runInOp(this.cm, function() { input.cm.curOp.selectionChanged = true }) function poll() { if (input.cm.state.focused) { - input.pollSelection(); - input.polling.set(input.cm.options.pollInterval, poll); + input.pollSelection() + input.polling.set(input.cm.options.pollInterval, poll) } } - this.polling.set(this.cm.options.pollInterval, poll); + this.polling.set(this.cm.options.pollInterval, poll) }, selectionChanged: function() { - var sel = window.getSelection(); + var sel = window.getSelection() return sel.anchorNode != this.lastAnchorNode || sel.anchorOffset != this.lastAnchorOffset || - sel.focusNode != this.lastFocusNode || sel.focusOffset != this.lastFocusOffset; + sel.focusNode != this.lastFocusNode || sel.focusOffset != this.lastFocusOffset }, pollSelection: function() { if (!this.composing && !this.gracePeriod && this.selectionChanged()) { - var sel = window.getSelection(), cm = this.cm; - this.rememberSelection(); - var anchor = domToPos(cm, sel.anchorNode, sel.anchorOffset); - var head = domToPos(cm, sel.focusNode, sel.focusOffset); + var sel = window.getSelection(), cm = this.cm + this.rememberSelection() + var anchor = domToPos(cm, sel.anchorNode, sel.anchorOffset) + var head = domToPos(cm, sel.focusNode, sel.focusOffset) if (anchor && head) runInOp(cm, function() { - setSelection(cm.doc, simpleSelection(anchor, head), sel_dontScroll); - if (anchor.bad || head.bad) cm.curOp.selectionChanged = true; - }); + setSelection(cm.doc, simpleSelection(anchor, head), sel_dontScroll) + if (anchor.bad || head.bad) cm.curOp.selectionChanged = true + }) } }, pollContent: function() { - var cm = this.cm, display = cm.display, sel = cm.doc.sel.primary(); - var from = sel.from(), to = sel.to(); - if (from.line < display.viewFrom || to.line > display.viewTo - 1) return false; + var cm = this.cm, display = cm.display, sel = cm.doc.sel.primary() + var from = sel.from(), to = sel.to() + if (from.line < display.viewFrom || to.line > display.viewTo - 1) return false - var fromIndex; + var fromIndex if (from.line == display.viewFrom || (fromIndex = findViewIndex(cm, from.line)) == 0) { - var fromLine = lineNo(display.view[0].line); - var fromNode = display.view[0].node; + var fromLine = lineNo(display.view[0].line) + var fromNode = display.view[0].node } else { - var fromLine = lineNo(display.view[fromIndex].line); - var fromNode = display.view[fromIndex - 1].node.nextSibling; + var fromLine = lineNo(display.view[fromIndex].line) + var fromNode = display.view[fromIndex - 1].node.nextSibling } - var toIndex = findViewIndex(cm, to.line); + var toIndex = findViewIndex(cm, to.line) if (toIndex == display.view.length - 1) { - var toLine = display.viewTo - 1; - var toNode = display.lineDiv.lastChild; + var toLine = display.viewTo - 1 + var toNode = display.lineDiv.lastChild } else { - var toLine = lineNo(display.view[toIndex + 1].line) - 1; - var toNode = display.view[toIndex + 1].node.previousSibling; + var toLine = lineNo(display.view[toIndex + 1].line) - 1 + var toNode = display.view[toIndex + 1].node.previousSibling } - var newText = cm.doc.splitLines(domTextBetween(cm, fromNode, toNode, fromLine, toLine)); - var oldText = getBetween(cm.doc, Pos(fromLine, 0), Pos(toLine, getLine(cm.doc, toLine).text.length)); + var newText = cm.doc.splitLines(domTextBetween(cm, fromNode, toNode, fromLine, toLine)) + var oldText = getBetween(cm.doc, Pos(fromLine, 0), Pos(toLine, getLine(cm.doc, toLine).text.length)) while (newText.length > 1 && oldText.length > 1) { - if (lst(newText) == lst(oldText)) { newText.pop(); oldText.pop(); toLine--; } - else if (newText[0] == oldText[0]) { newText.shift(); oldText.shift(); fromLine++; } - else break; + if (lst(newText) == lst(oldText)) { newText.pop(); oldText.pop(); toLine-- } + else if (newText[0] == oldText[0]) { newText.shift(); oldText.shift(); fromLine++ } + else break } - var cutFront = 0, cutEnd = 0; - var newTop = newText[0], oldTop = oldText[0], maxCutFront = Math.min(newTop.length, oldTop.length); + var cutFront = 0, cutEnd = 0 + var newTop = newText[0], oldTop = oldText[0], maxCutFront = Math.min(newTop.length, oldTop.length) while (cutFront < maxCutFront && newTop.charCodeAt(cutFront) == oldTop.charCodeAt(cutFront)) - ++cutFront; - var newBot = lst(newText), oldBot = lst(oldText); + ++cutFront + var newBot = lst(newText), oldBot = lst(oldText) var maxCutEnd = Math.min(newBot.length - (newText.length == 1 ? cutFront : 0), - oldBot.length - (oldText.length == 1 ? cutFront : 0)); + oldBot.length - (oldText.length == 1 ? cutFront : 0)) while (cutEnd < maxCutEnd && newBot.charCodeAt(newBot.length - cutEnd - 1) == oldBot.charCodeAt(oldBot.length - cutEnd - 1)) - ++cutEnd; + ++cutEnd - newText[newText.length - 1] = newBot.slice(0, newBot.length - cutEnd); - newText[0] = newText[0].slice(cutFront); + newText[newText.length - 1] = newBot.slice(0, newBot.length - cutEnd) + newText[0] = newText[0].slice(cutFront) - var chFrom = Pos(fromLine, cutFront); - var chTo = Pos(toLine, oldText.length ? lst(oldText).length - cutEnd : 0); + var chFrom = Pos(fromLine, cutFront) + var chTo = Pos(toLine, oldText.length ? lst(oldText).length - cutEnd : 0) if (newText.length > 1 || newText[0] || cmp(chFrom, chTo)) { - replaceRange(cm.doc, newText, chFrom, chTo, "+input"); - return true; + replaceRange(cm.doc, newText, chFrom, chTo, "+input") + return true } }, ensurePolled: function() { - this.forceCompositionEnd(); + this.forceCompositionEnd() }, reset: function() { - this.forceCompositionEnd(); + this.forceCompositionEnd() }, forceCompositionEnd: function() { - if (!this.composing || this.composing.handled) return; - this.applyComposition(this.composing); - this.composing.handled = true; - this.div.blur(); - this.div.focus(); + if (!this.composing || this.composing.handled) return + this.applyComposition(this.composing) + this.composing.handled = true + this.div.blur() + this.div.focus() }, applyComposition: function(composing) { if (this.cm.isReadOnly()) operation(this.cm, regChange)(this.cm) else if (composing.data && composing.data != composing.startData) - operation(this.cm, applyTextInput)(this.cm, composing.data, 0, composing.sel); + operation(this.cm, applyTextInput)(this.cm, composing.data, 0, composing.sel) }, setUneditable: function(node) { @@ -319,9 +319,9 @@ ContentEditableInput.prototype = copyObj({ }, onKeyPress: function(e) { - e.preventDefault(); + e.preventDefault() if (!this.cm.isReadOnly()) - operation(this.cm, applyTextInput)(this.cm, String.fromCharCode(e.charCode == null ? e.keyCode : e.charCode), 0); + operation(this.cm, applyTextInput)(this.cm, String.fromCharCode(e.charCode == null ? e.keyCode : e.charCode), 0) }, readOnlyChanged: function(val) { @@ -332,137 +332,137 @@ ContentEditableInput.prototype = copyObj({ resetPosition: nothing, needsContentAttribute: true - }, ContentEditableInput.prototype); + }, ContentEditableInput.prototype) function posToDOM(cm, pos) { - var view = findViewForLine(cm, pos.line); - if (!view || view.hidden) return null; - var line = getLine(cm.doc, pos.line); - var info = mapFromLineView(view, line, pos.line); + var view = findViewForLine(cm, pos.line) + if (!view || view.hidden) return null + var line = getLine(cm.doc, pos.line) + var info = mapFromLineView(view, line, pos.line) - var order = getOrder(line), side = "left"; + var order = getOrder(line), side = "left" if (order) { - var partPos = getBidiPartAt(order, pos.ch); - side = partPos % 2 ? "right" : "left"; + var partPos = getBidiPartAt(order, pos.ch) + side = partPos % 2 ? "right" : "left" } - var result = nodeAndOffsetInLineMap(info.map, pos.ch, side); - result.offset = result.collapse == "right" ? result.end : result.start; - return result; + var result = nodeAndOffsetInLineMap(info.map, pos.ch, side) + result.offset = result.collapse == "right" ? result.end : result.start + return result } -function badPos(pos, bad) { if (bad) pos.bad = true; return pos; } +function badPos(pos, bad) { if (bad) pos.bad = true; return pos } function domTextBetween(cm, from, to, fromLine, toLine) { - var text = "", closing = false, lineSep = cm.doc.lineSeparator(); - function recognizeMarker(id) { return function(marker) { return marker.id == id; }; } + var text = "", closing = false, lineSep = cm.doc.lineSeparator() + function recognizeMarker(id) { return function(marker) { return marker.id == id } } function walk(node) { if (node.nodeType == 1) { - var cmText = node.getAttribute("cm-text"); + var cmText = node.getAttribute("cm-text") if (cmText != null) { - if (cmText == "") cmText = node.textContent.replace(/\u200b/g, ""); - text += cmText; - return; + if (cmText == "") cmText = node.textContent.replace(/\u200b/g, "") + text += cmText + return } - var markerID = node.getAttribute("cm-marker"), range; + var markerID = node.getAttribute("cm-marker"), range if (markerID) { - var found = cm.findMarks(Pos(fromLine, 0), Pos(toLine + 1, 0), recognizeMarker(+markerID)); + var found = cm.findMarks(Pos(fromLine, 0), Pos(toLine + 1, 0), recognizeMarker(+markerID)) if (found.length && (range = found[0].find())) - text += getBetween(cm.doc, range.from, range.to).join(lineSep); - return; + text += getBetween(cm.doc, range.from, range.to).join(lineSep) + return } - if (node.getAttribute("contenteditable") == "false") return; + if (node.getAttribute("contenteditable") == "false") return for (var i = 0; i < node.childNodes.length; i++) - walk(node.childNodes[i]); + walk(node.childNodes[i]) if (/^(pre|div|p)$/i.test(node.nodeName)) - closing = true; + closing = true } else if (node.nodeType == 3) { - var val = node.nodeValue; - if (!val) return; + var val = node.nodeValue + if (!val) return if (closing) { - text += lineSep; - closing = false; + text += lineSep + closing = false } - text += val; + text += val } } for (;;) { - walk(from); - if (from == to) break; - from = from.nextSibling; + walk(from) + if (from == to) break + from = from.nextSibling } - return text; + return text } function domToPos(cm, node, offset) { - var lineNode; + var lineNode if (node == cm.display.lineDiv) { - lineNode = cm.display.lineDiv.childNodes[offset]; - if (!lineNode) return badPos(cm.clipPos(Pos(cm.display.viewTo - 1)), true); - node = null; offset = 0; + lineNode = cm.display.lineDiv.childNodes[offset] + if (!lineNode) return badPos(cm.clipPos(Pos(cm.display.viewTo - 1)), true) + node = null; offset = 0 } else { for (lineNode = node;; lineNode = lineNode.parentNode) { - if (!lineNode || lineNode == cm.display.lineDiv) return null; - if (lineNode.parentNode && lineNode.parentNode == cm.display.lineDiv) break; + if (!lineNode || lineNode == cm.display.lineDiv) return null + if (lineNode.parentNode && lineNode.parentNode == cm.display.lineDiv) break } } for (var i = 0; i < cm.display.view.length; i++) { - var lineView = cm.display.view[i]; + var lineView = cm.display.view[i] if (lineView.node == lineNode) - return locateNodeInLineView(lineView, node, offset); + return locateNodeInLineView(lineView, node, offset) } } function locateNodeInLineView(lineView, node, offset) { - var wrapper = lineView.text.firstChild, bad = false; - if (!node || !contains(wrapper, node)) return badPos(Pos(lineNo(lineView.line), 0), true); + var wrapper = lineView.text.firstChild, bad = false + if (!node || !contains(wrapper, node)) return badPos(Pos(lineNo(lineView.line), 0), true) if (node == wrapper) { - bad = true; - node = wrapper.childNodes[offset]; - offset = 0; + bad = true + node = wrapper.childNodes[offset] + offset = 0 if (!node) { - var line = lineView.rest ? lst(lineView.rest) : lineView.line; - return badPos(Pos(lineNo(line), line.text.length), bad); + var line = lineView.rest ? lst(lineView.rest) : lineView.line + return badPos(Pos(lineNo(line), line.text.length), bad) } } - var textNode = node.nodeType == 3 ? node : null, topNode = node; + var textNode = node.nodeType == 3 ? node : null, topNode = node if (!textNode && node.childNodes.length == 1 && node.firstChild.nodeType == 3) { - textNode = node.firstChild; - if (offset) offset = textNode.nodeValue.length; + textNode = node.firstChild + if (offset) offset = textNode.nodeValue.length } - while (topNode.parentNode != wrapper) topNode = topNode.parentNode; - var measure = lineView.measure, maps = measure.maps; + while (topNode.parentNode != wrapper) topNode = topNode.parentNode + var measure = lineView.measure, maps = measure.maps function find(textNode, topNode, offset) { for (var i = -1; i < (maps ? maps.length : 0); i++) { - var map = i < 0 ? measure.map : maps[i]; + var map = i < 0 ? measure.map : maps[i] for (var j = 0; j < map.length; j += 3) { - var curNode = map[j + 2]; + var curNode = map[j + 2] if (curNode == textNode || curNode == topNode) { - var line = lineNo(i < 0 ? lineView.line : lineView.rest[i]); - var ch = map[j] + offset; - if (offset < 0 || curNode != textNode) ch = map[j + (offset ? 1 : 0)]; - return Pos(line, ch); + var line = lineNo(i < 0 ? lineView.line : lineView.rest[i]) + var ch = map[j] + offset + if (offset < 0 || curNode != textNode) ch = map[j + (offset ? 1 : 0)] + return Pos(line, ch) } } } } - var found = find(textNode, topNode, offset); - if (found) return badPos(found, bad); + var found = find(textNode, topNode, offset) + if (found) return badPos(found, bad) // FIXME this is all really shaky. might handle the few cases it needs to handle, but likely to cause problems for (var after = topNode.nextSibling, dist = textNode ? textNode.nodeValue.length - offset : 0; after; after = after.nextSibling) { - found = find(after, after.firstChild, 0); + found = find(after, after.firstChild, 0) if (found) - return badPos(Pos(found.line, found.ch - dist), bad); + return badPos(Pos(found.line, found.ch - dist), bad) else - dist += after.textContent.length; + dist += after.textContent.length } for (var before = topNode.previousSibling, dist = offset; before; before = before.previousSibling) { - found = find(before, before.firstChild, -1); + found = find(before, before.firstChild, -1) if (found) - return badPos(Pos(found.line, found.ch + dist), bad); + return badPos(Pos(found.line, found.ch + dist), bad) else - dist += before.textContent.length; + dist += before.textContent.length } } diff --git a/src/input/TextareaInput.js b/src/input/TextareaInput.js index 421ce17a..4c08f377 100644 --- a/src/input/TextareaInput.js +++ b/src/input/TextareaInput.js @@ -1,210 +1,210 @@ -import { operation, runInOp } from "../display/operations"; -import { prepareSelection } from "../display/selection"; -import { applyTextInput, copyableRanges, handlePaste, hiddenTextarea, lastCopied, setLastCopied } from "./input"; -import { cursorCoords, posFromMouse } from "../measurement/position_measurement"; -import { eventInWidget } from "../measurement/widgets"; -import { simpleSelection } from "../model/selection"; -import { selectAll, setSelection } from "../model/selection_updates"; -import { captureRightClick, ie, ie_version, ios, mac, mobile, presto, webkit } from "../util/browser"; -import { activeElt, removeChildrenAndAdd, selectInput } from "../util/dom"; -import { e_preventDefault, e_stop, off, on, signalDOMEvent } from "../util/event"; -import { hasCopyEvent, hasSelection } from "../util/feature_detection"; -import { copyObj, Delayed, nothing, sel_dontScroll } from "../util/misc"; +import { operation, runInOp } from "../display/operations" +import { prepareSelection } from "../display/selection" +import { applyTextInput, copyableRanges, handlePaste, hiddenTextarea, lastCopied, setLastCopied } from "./input" +import { cursorCoords, posFromMouse } from "../measurement/position_measurement" +import { eventInWidget } from "../measurement/widgets" +import { simpleSelection } from "../model/selection" +import { selectAll, setSelection } from "../model/selection_updates" +import { captureRightClick, ie, ie_version, ios, mac, mobile, presto, webkit } from "../util/browser" +import { activeElt, removeChildrenAndAdd, selectInput } from "../util/dom" +import { e_preventDefault, e_stop, off, on, signalDOMEvent } from "../util/event" +import { hasCopyEvent, hasSelection } from "../util/feature_detection" +import { copyObj, Delayed, nothing, sel_dontScroll } from "../util/misc" // TEXTAREA INPUT STYLE export default function TextareaInput(cm) { - this.cm = cm; + this.cm = cm // See input.poll and input.reset - this.prevInput = ""; + this.prevInput = "" // Flag that indicates whether we expect input to appear real soon // now (after some event like 'keypress' or 'input') and are // polling intensively. - this.pollingFast = false; + this.pollingFast = false // Self-resetting timeout for the poller - this.polling = new Delayed(); + this.polling = new Delayed() // Tracks when input.reset has punted to just putting a short // string into the textarea instead of the full selection. - this.inaccurateSelection = false; + this.inaccurateSelection = false // Used to work around IE issue with selection being forgotten when focus moves away from textarea - this.hasSelection = false; - this.composing = null; + this.hasSelection = false + this.composing = null } TextareaInput.prototype = copyObj({ init: function(display) { - var input = this, cm = this.cm; + var input = this, cm = this.cm // Wraps and hides input textarea - var div = this.wrapper = hiddenTextarea(); + var div = this.wrapper = hiddenTextarea() // The semihidden textarea that is focused when the editor is // focused, and receives input. - var te = this.textarea = div.firstChild; - display.wrapper.insertBefore(div, display.wrapper.firstChild); + var te = this.textarea = div.firstChild + display.wrapper.insertBefore(div, display.wrapper.firstChild) // Needed to hide big blue blinking cursor on Mobile Safari (doesn't seem to work in iOS 8 anymore) - if (ios) te.style.width = "0px"; + if (ios) te.style.width = "0px" on(te, "input", function() { - if (ie && ie_version >= 9 && input.hasSelection) input.hasSelection = null; - input.poll(); - }); + if (ie && ie_version >= 9 && input.hasSelection) input.hasSelection = null + input.poll() + }) on(te, "paste", function(e) { if (signalDOMEvent(cm, e) || handlePaste(e, cm)) return - cm.state.pasteIncoming = true; - input.fastPoll(); - }); + cm.state.pasteIncoming = true + input.fastPoll() + }) function prepareCopyCut(e) { if (signalDOMEvent(cm, e)) return if (cm.somethingSelected()) { - setLastCopied({lineWise: false, text: cm.getSelections()}); + setLastCopied({lineWise: false, text: cm.getSelections()}) if (input.inaccurateSelection) { - input.prevInput = ""; - input.inaccurateSelection = false; - te.value = lastCopied.text.join("\n"); - selectInput(te); + input.prevInput = "" + input.inaccurateSelection = false + te.value = lastCopied.text.join("\n") + selectInput(te) } } else if (!cm.options.lineWiseCopyCut) { - return; + return } else { - var ranges = copyableRanges(cm); - setLastCopied({lineWise: true, text: ranges.text}); + var ranges = copyableRanges(cm) + setLastCopied({lineWise: true, text: ranges.text}) if (e.type == "cut") { - cm.setSelections(ranges.ranges, null, sel_dontScroll); + cm.setSelections(ranges.ranges, null, sel_dontScroll) } else { - input.prevInput = ""; - te.value = ranges.text.join("\n"); - selectInput(te); + input.prevInput = "" + te.value = ranges.text.join("\n") + selectInput(te) } } - if (e.type == "cut") cm.state.cutIncoming = true; + if (e.type == "cut") cm.state.cutIncoming = true } - on(te, "cut", prepareCopyCut); - on(te, "copy", prepareCopyCut); + on(te, "cut", prepareCopyCut) + on(te, "copy", prepareCopyCut) on(display.scroller, "paste", function(e) { - if (eventInWidget(display, e) || signalDOMEvent(cm, e)) return; - cm.state.pasteIncoming = true; - input.focus(); - }); + if (eventInWidget(display, e) || signalDOMEvent(cm, e)) return + cm.state.pasteIncoming = true + input.focus() + }) // Prevent normal selection in the editor (we handle our own) on(display.lineSpace, "selectstart", function(e) { - if (!eventInWidget(display, e)) e_preventDefault(e); - }); + if (!eventInWidget(display, e)) e_preventDefault(e) + }) on(te, "compositionstart", function() { - var start = cm.getCursor("from"); + var start = cm.getCursor("from") if (input.composing) input.composing.range.clear() input.composing = { start: start, range: cm.markText(start, cm.getCursor("to"), {className: "CodeMirror-composing"}) - }; - }); + } + }) on(te, "compositionend", function() { if (input.composing) { - input.poll(); - input.composing.range.clear(); - input.composing = null; + input.poll() + input.composing.range.clear() + input.composing = null } - }); + }) }, prepareSelection: function() { // Redraw the selection and/or cursor - var cm = this.cm, display = cm.display, doc = cm.doc; - var result = prepareSelection(cm); + var cm = this.cm, display = cm.display, doc = cm.doc + var result = prepareSelection(cm) // Move the hidden textarea near the cursor to prevent scrolling artifacts if (cm.options.moveInputWithCursor) { - var headPos = cursorCoords(cm, doc.sel.primary().head, "div"); - var wrapOff = display.wrapper.getBoundingClientRect(), lineOff = display.lineDiv.getBoundingClientRect(); + var headPos = cursorCoords(cm, doc.sel.primary().head, "div") + var wrapOff = display.wrapper.getBoundingClientRect(), lineOff = display.lineDiv.getBoundingClientRect() result.teTop = Math.max(0, Math.min(display.wrapper.clientHeight - 10, - headPos.top + lineOff.top - wrapOff.top)); + headPos.top + lineOff.top - wrapOff.top)) result.teLeft = Math.max(0, Math.min(display.wrapper.clientWidth - 10, - headPos.left + lineOff.left - wrapOff.left)); + headPos.left + lineOff.left - wrapOff.left)) } - return result; + return result }, showSelection: function(drawn) { - var cm = this.cm, display = cm.display; - removeChildrenAndAdd(display.cursorDiv, drawn.cursors); - removeChildrenAndAdd(display.selectionDiv, drawn.selection); + var cm = this.cm, display = cm.display + removeChildrenAndAdd(display.cursorDiv, drawn.cursors) + removeChildrenAndAdd(display.selectionDiv, drawn.selection) if (drawn.teTop != null) { - this.wrapper.style.top = drawn.teTop + "px"; - this.wrapper.style.left = drawn.teLeft + "px"; + this.wrapper.style.top = drawn.teTop + "px" + this.wrapper.style.left = drawn.teLeft + "px" } }, // Reset the input to correspond to the selection (or to be empty, // when not typing and nothing is selected) reset: function(typing) { - if (this.contextMenuPending) return; - var minimal, selected, cm = this.cm, doc = cm.doc; + if (this.contextMenuPending) return + var minimal, selected, cm = this.cm, doc = cm.doc if (cm.somethingSelected()) { - this.prevInput = ""; - var range = doc.sel.primary(); + this.prevInput = "" + var range = doc.sel.primary() minimal = hasCopyEvent && - (range.to().line - range.from().line > 100 || (selected = cm.getSelection()).length > 1000); - var content = minimal ? "-" : selected || cm.getSelection(); - this.textarea.value = content; - if (cm.state.focused) selectInput(this.textarea); - if (ie && ie_version >= 9) this.hasSelection = content; + (range.to().line - range.from().line > 100 || (selected = cm.getSelection()).length > 1000) + var content = minimal ? "-" : selected || cm.getSelection() + this.textarea.value = content + if (cm.state.focused) selectInput(this.textarea) + if (ie && ie_version >= 9) this.hasSelection = content } else if (!typing) { - this.prevInput = this.textarea.value = ""; - if (ie && ie_version >= 9) this.hasSelection = null; + this.prevInput = this.textarea.value = "" + if (ie && ie_version >= 9) this.hasSelection = null } - this.inaccurateSelection = minimal; + this.inaccurateSelection = minimal }, - getField: function() { return this.textarea; }, + getField: function() { return this.textarea }, - supportsTouch: function() { return false; }, + supportsTouch: function() { return false }, focus: function() { if (this.cm.options.readOnly != "nocursor" && (!mobile || activeElt() != this.textarea)) { - try { this.textarea.focus(); } + try { this.textarea.focus() } catch (e) {} // IE8 will throw if the textarea is display: none or not in DOM } }, - blur: function() { this.textarea.blur(); }, + blur: function() { this.textarea.blur() }, resetPosition: function() { - this.wrapper.style.top = this.wrapper.style.left = 0; + this.wrapper.style.top = this.wrapper.style.left = 0 }, - receivedFocus: function() { this.slowPoll(); }, + receivedFocus: function() { this.slowPoll() }, // Poll for input changes, using the normal rate of polling. This // runs as long as the editor is focused. slowPoll: function() { - var input = this; - if (input.pollingFast) return; + var input = this + if (input.pollingFast) return input.polling.set(this.cm.options.pollInterval, function() { - input.poll(); - if (input.cm.state.focused) input.slowPoll(); - }); + input.poll() + if (input.cm.state.focused) input.slowPoll() + }) }, // When an event has just come in that is likely to add or change // something in the input textarea, we poll faster, to ensure that // the change appears on the screen quickly. fastPoll: function() { - var missed = false, input = this; - input.pollingFast = true; + var missed = false, input = this + input.pollingFast = true function p() { - var changed = input.poll(); - if (!changed && !missed) {missed = true; input.polling.set(60, p);} - else {input.pollingFast = false; input.slowPoll();} + var changed = input.poll() + if (!changed && !missed) {missed = true; input.polling.set(60, p)} + else {input.pollingFast = false; input.slowPoll()} } - input.polling.set(20, p); + input.polling.set(20, p) }, // Read input from the textarea, and update the document to match. @@ -214,7 +214,7 @@ TextareaInput.prototype = copyObj({ // seen text (can be empty), which is stored in prevInput (we must // not reset the textarea when typing, because that breaks IME). poll: function() { - var cm = this.cm, input = this.textarea, prevInput = this.prevInput; + var cm = this.cm, input = this.textarea, prevInput = this.prevInput // Since this is called a *lot*, try to bail out as cheaply as // possible when it is clear that nothing happened. hasSelection // will be the case when there is a lot of text in the textarea, @@ -222,138 +222,138 @@ TextareaInput.prototype = copyObj({ if (this.contextMenuPending || !cm.state.focused || (hasSelection(input) && !prevInput && !this.composing) || cm.isReadOnly() || cm.options.disableInput || cm.state.keySeq) - return false; + return false - var text = input.value; + var text = input.value // If nothing changed, bail. - if (text == prevInput && !cm.somethingSelected()) return false; + if (text == prevInput && !cm.somethingSelected()) return false // 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 && this.hasSelection === text || mac && /[\uf700-\uf7ff]/.test(text)) { - cm.display.input.reset(); - return false; + cm.display.input.reset() + return false } if (cm.doc.sel == cm.display.selForContextMenu) { - var first = text.charCodeAt(0); - if (first == 0x200b && !prevInput) prevInput = "\u200b"; - if (first == 0x21da) { this.reset(); return this.cm.execCommand("undo"); } + var first = text.charCodeAt(0) + if (first == 0x200b && !prevInput) prevInput = "\u200b" + if (first == 0x21da) { this.reset(); return this.cm.execCommand("undo") } } // Find the part of the input that is actually new - var same = 0, l = Math.min(prevInput.length, text.length); - while (same < l && prevInput.charCodeAt(same) == text.charCodeAt(same)) ++same; + var same = 0, l = Math.min(prevInput.length, text.length) + while (same < l && prevInput.charCodeAt(same) == text.charCodeAt(same)) ++same - var self = this; + var self = this runInOp(cm, function() { applyTextInput(cm, text.slice(same), prevInput.length - same, - null, self.composing ? "*compose" : null); + null, self.composing ? "*compose" : null) // Don't leave long text in the textarea, since it makes further polling slow - if (text.length > 1000 || text.indexOf("\n") > -1) input.value = self.prevInput = ""; - else self.prevInput = text; + if (text.length > 1000 || text.indexOf("\n") > -1) input.value = self.prevInput = "" + else self.prevInput = text if (self.composing) { - self.composing.range.clear(); + self.composing.range.clear() self.composing.range = cm.markText(self.composing.start, cm.getCursor("to"), - {className: "CodeMirror-composing"}); + {className: "CodeMirror-composing"}) } - }); - return true; + }) + return true }, ensurePolled: function() { - if (this.pollingFast && this.poll()) this.pollingFast = false; + if (this.pollingFast && this.poll()) this.pollingFast = false }, onKeyPress: function() { - if (ie && ie_version >= 9) this.hasSelection = null; - this.fastPoll(); + if (ie && ie_version >= 9) this.hasSelection = null + this.fastPoll() }, onContextMenu: function(e) { - var input = this, cm = input.cm, display = cm.display, te = input.textarea; - var pos = posFromMouse(cm, e), scrollPos = display.scroller.scrollTop; - if (!pos || presto) return; // Opera is difficult. + var input = this, cm = input.cm, display = cm.display, te = input.textarea + var pos = posFromMouse(cm, e), scrollPos = display.scroller.scrollTop + if (!pos || presto) return // Opera is difficult. // Reset the current text selection only if the click is done outside of the selection // and 'resetSelectionOnContextMenu' option is true. - var reset = cm.options.resetSelectionOnContextMenu; + var reset = cm.options.resetSelectionOnContextMenu if (reset && cm.doc.sel.contains(pos) == -1) - operation(cm, setSelection)(cm.doc, simpleSelection(pos), sel_dontScroll); + operation(cm, setSelection)(cm.doc, simpleSelection(pos), sel_dontScroll) - var oldCSS = te.style.cssText, oldWrapperCSS = input.wrapper.style.cssText; + var oldCSS = te.style.cssText, oldWrapperCSS = input.wrapper.style.cssText input.wrapper.style.cssText = "position: absolute" var wrapperBox = input.wrapper.getBoundingClientRect() te.style.cssText = "position: absolute; width: 30px; height: 30px; top: " + (e.clientY - wrapperBox.top - 5) + "px; left: " + (e.clientX - wrapperBox.left - 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) - display.input.focus(); - if (webkit) window.scrollTo(null, oldScrollY); - display.input.reset(); + "; 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) + display.input.focus() + if (webkit) window.scrollTo(null, oldScrollY) + display.input.reset() // Adds "Select all" to context menu in FF - if (!cm.somethingSelected()) te.value = input.prevInput = " "; - input.contextMenuPending = true; - display.selForContextMenu = cm.doc.sel; - clearTimeout(display.detectingSelectAll); + if (!cm.somethingSelected()) te.value = input.prevInput = " " + input.contextMenuPending = true + display.selForContextMenu = cm.doc.sel + clearTimeout(display.detectingSelectAll) // Select-all will be greyed out if there's nothing to select, so // this adds a zero-width space so that we can later check whether // it got selected. function prepareSelectAllHack() { if (te.selectionStart != null) { - var selected = cm.somethingSelected(); - var extval = "\u200b" + (selected ? te.value : ""); - te.value = "\u21da"; // Used to catch context-menu undo - te.value = extval; - input.prevInput = selected ? "" : "\u200b"; - te.selectionStart = 1; te.selectionEnd = extval.length; + var selected = cm.somethingSelected() + var extval = "\u200b" + (selected ? te.value : "") + te.value = "\u21da" // Used to catch context-menu undo + te.value = extval + input.prevInput = selected ? "" : "\u200b" + te.selectionStart = 1; te.selectionEnd = extval.length // Re-set this, in case some other handler touched the // selection in the meantime. - display.selForContextMenu = cm.doc.sel; + display.selForContextMenu = cm.doc.sel } } function rehide() { - input.contextMenuPending = false; + input.contextMenuPending = false input.wrapper.style.cssText = oldWrapperCSS - te.style.cssText = oldCSS; - if (ie && ie_version < 9) display.scrollbars.setScrollTop(display.scroller.scrollTop = scrollPos); + te.style.cssText = oldCSS + if (ie && ie_version < 9) display.scrollbars.setScrollTop(display.scroller.scrollTop = scrollPos) // Try to detect the user choosing select-all if (te.selectionStart != null) { - if (!ie || (ie && ie_version < 9)) prepareSelectAllHack(); + if (!ie || (ie && ie_version < 9)) prepareSelectAllHack() var i = 0, poll = function() { if (display.selForContextMenu == cm.doc.sel && te.selectionStart == 0 && te.selectionEnd > 0 && input.prevInput == "\u200b") - operation(cm, selectAll)(cm); - else if (i++ < 10) display.detectingSelectAll = setTimeout(poll, 500); - else display.input.reset(); - }; - display.detectingSelectAll = setTimeout(poll, 200); + operation(cm, selectAll)(cm) + else if (i++ < 10) display.detectingSelectAll = setTimeout(poll, 500) + else display.input.reset() + } + display.detectingSelectAll = setTimeout(poll, 200) } } - if (ie && ie_version >= 9) prepareSelectAllHack(); + if (ie && ie_version >= 9) prepareSelectAllHack() if (captureRightClick) { - e_stop(e); + e_stop(e) var mouseup = function() { - off(window, "mouseup", mouseup); - setTimeout(rehide, 20); - }; - on(window, "mouseup", mouseup); + off(window, "mouseup", mouseup) + setTimeout(rehide, 20) + } + on(window, "mouseup", mouseup) } else { - setTimeout(rehide, 50); + setTimeout(rehide, 50) } }, readOnlyChanged: function(val) { - if (!val) this.reset(); + if (!val) this.reset() }, setUneditable: nothing, needsContentAttribute: false -}, TextareaInput.prototype); +}, TextareaInput.prototype) diff --git a/src/input/indent.js b/src/input/indent.js index d814452e..f0fc78fd 100644 --- a/src/input/indent.js +++ b/src/input/indent.js @@ -1,10 +1,10 @@ -import { getStateBefore } from "../line/highlight"; -import { Pos } from "../line/pos"; -import { getLine } from "../line/utils_line"; -import { replaceRange } from "../model/changes"; -import { Range } from "../model/selection"; -import { replaceOneSelection } from "../model/selection_updates"; -import { countColumn, Pass, spaceStr } from "../util/misc"; +import { getStateBefore } from "../line/highlight" +import { Pos } from "../line/pos" +import { getLine } from "../line/utils_line" +import { replaceRange } from "../model/changes" +import { Range } from "../model/selection" +import { replaceOneSelection } from "../model/selection_updates" +import { countColumn, Pass, spaceStr } from "../util/misc" // Indent the given line. The how parameter can be "smart", // "add"/null, "subtract", or "prev". When aggressive is false @@ -12,59 +12,59 @@ import { countColumn, Pass, spaceStr } from "../util/misc"; // lines are not indented, and places where the mode returns Pass // are left alone. export function indentLine(cm, n, how, aggressive) { - var doc = cm.doc, state; - if (how == null) how = "add"; + var doc = cm.doc, state + if (how == null) how = "add" if (how == "smart") { // Fall back to "prev" when the mode doesn't have an indentation // method. - if (!doc.mode.indent) how = "prev"; - else state = getStateBefore(cm, n); + if (!doc.mode.indent) how = "prev" + else state = getStateBefore(cm, n) } - var tabSize = cm.options.tabSize; - var line = getLine(doc, n), curSpace = countColumn(line.text, null, tabSize); - if (line.stateAfter) line.stateAfter = null; - var curSpaceString = line.text.match(/^\s*/)[0], indentation; + var tabSize = cm.options.tabSize + var line = getLine(doc, n), curSpace = countColumn(line.text, null, tabSize) + if (line.stateAfter) line.stateAfter = null + var curSpaceString = line.text.match(/^\s*/)[0], indentation if (!aggressive && !/\S/.test(line.text)) { - indentation = 0; - how = "not"; + indentation = 0 + how = "not" } else if (how == "smart") { - indentation = 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"; + if (!aggressive) return + how = "prev" } } if (how == "prev") { - if (n > doc.first) indentation = countColumn(getLine(doc, n-1).text, null, tabSize); - else indentation = 0; + 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; + indentation = curSpace + cm.options.indentUnit } else if (how == "subtract") { - indentation = curSpace - cm.options.indentUnit; + indentation = curSpace - cm.options.indentUnit } else if (typeof how == "number") { - indentation = curSpace + how; + indentation = curSpace + how } - indentation = Math.max(0, indentation); + indentation = Math.max(0, indentation) - var indentString = "", pos = 0; + var indentString = "", pos = 0 if (cm.options.indentWithTabs) - for (var i = Math.floor(indentation / tabSize); i; --i) {pos += tabSize; indentString += "\t";} - if (pos < indentation) indentString += spaceStr(indentation - pos); + for (var i = Math.floor(indentation / tabSize); i; --i) {pos += tabSize; indentString += "\t"} + if (pos < indentation) indentString += spaceStr(indentation - pos) if (indentString != curSpaceString) { - replaceRange(doc, indentString, Pos(n, 0), Pos(n, curSpaceString.length), "+input"); - line.stateAfter = null; - return true; + replaceRange(doc, indentString, Pos(n, 0), Pos(n, curSpaceString.length), "+input") + line.stateAfter = null + return true } 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. for (var i = 0; i < doc.sel.ranges.length; i++) { - var range = doc.sel.ranges[i]; + var range = doc.sel.ranges[i] if (range.head.line == n && range.head.ch < curSpaceString.length) { - var pos = Pos(n, curSpaceString.length); - replaceOneSelection(doc, i, new Range(pos, pos)); - break; + var pos = Pos(n, curSpaceString.length) + replaceOneSelection(doc, i, new Range(pos, pos)) + break } } } diff --git a/src/input/input.js b/src/input/input.js index b017934a..a084e08f 100644 --- a/src/input/input.js +++ b/src/input/input.js @@ -1,133 +1,133 @@ -import { runInOp } from "../display/operations"; -import { ensureCursorVisible } from "../display/scrolling"; -import { Pos } from "../line/pos"; -import { getLine } from "../line/utils_line"; -import { makeChange } from "../model/changes"; -import { ios, webkit } from "../util/browser"; -import { elt } from "../util/dom"; -import { lst, map } from "../util/misc"; -import { signalLater } from "../util/operation_group"; +import { runInOp } from "../display/operations" +import { ensureCursorVisible } from "../display/scrolling" +import { Pos } from "../line/pos" +import { getLine } from "../line/utils_line" +import { makeChange } from "../model/changes" +import { ios, webkit } from "../util/browser" +import { elt } from "../util/dom" +import { lst, map } from "../util/misc" +import { signalLater } from "../util/operation_group" -import { indentLine } from "./indent"; +import { indentLine } from "./indent" // This will be set to a {lineWise: bool, text: [string]} object, so // that, when pasting, we know what kind of selections the copied // text was made out of. -export var lastCopied = null; +export var lastCopied = null export function setLastCopied(newLastCopied) { - lastCopied = newLastCopied; + lastCopied = newLastCopied } export function applyTextInput(cm, inserted, deleted, sel, origin) { - var doc = cm.doc; - cm.display.shift = false; - if (!sel) sel = doc.sel; + var doc = cm.doc + cm.display.shift = false + if (!sel) sel = doc.sel - var paste = cm.state.pasteIncoming || origin == "paste"; + var paste = cm.state.pasteIncoming || origin == "paste" var textLines = doc.splitLines(inserted), multiPaste = null // When pasing N lines into N selections, insert one line per selection if (paste && sel.ranges.length > 1) { if (lastCopied && lastCopied.text.join("\n") == inserted) { if (sel.ranges.length % lastCopied.text.length == 0) { - multiPaste = []; + multiPaste = [] for (var i = 0; i < lastCopied.text.length; i++) - multiPaste.push(doc.splitLines(lastCopied.text[i])); + multiPaste.push(doc.splitLines(lastCopied.text[i])) } } else if (textLines.length == sel.ranges.length) { - multiPaste = map(textLines, function(l) { return [l]; }); + multiPaste = map(textLines, function(l) { return [l] }) } } // Normal behavior is to insert the new text into every selection for (var i = sel.ranges.length - 1; i >= 0; i--) { - var range = sel.ranges[i]; - var from = range.from(), to = range.to(); + var range = sel.ranges[i] + var from = range.from(), to = range.to() if (range.empty()) { if (deleted && deleted > 0) // Handle deletion - from = Pos(from.line, from.ch - deleted); + from = Pos(from.line, from.ch - deleted) else if (cm.state.overwrite && !paste) // Handle overwrite - to = Pos(to.line, Math.min(getLine(doc, to.line).text.length, to.ch + lst(textLines).length)); + to = Pos(to.line, Math.min(getLine(doc, to.line).text.length, to.ch + lst(textLines).length)) else if (lastCopied && lastCopied.lineWise && lastCopied.text.join("\n") == inserted) from = to = Pos(from.line, 0) } - var updateInput = cm.curOp.updateInput; + var updateInput = cm.curOp.updateInput var changeEvent = {from: from, to: to, text: multiPaste ? multiPaste[i % multiPaste.length] : textLines, - origin: origin || (paste ? "paste" : cm.state.cutIncoming ? "cut" : "+input")}; - makeChange(cm.doc, changeEvent); - signalLater(cm, "inputRead", cm, changeEvent); + origin: origin || (paste ? "paste" : cm.state.cutIncoming ? "cut" : "+input")} + makeChange(cm.doc, changeEvent) + signalLater(cm, "inputRead", cm, changeEvent) } if (inserted && !paste) - triggerElectric(cm, inserted); + triggerElectric(cm, inserted) - ensureCursorVisible(cm); - cm.curOp.updateInput = updateInput; - cm.curOp.typing = true; - cm.state.pasteIncoming = cm.state.cutIncoming = false; + ensureCursorVisible(cm) + cm.curOp.updateInput = updateInput + cm.curOp.typing = true + cm.state.pasteIncoming = cm.state.cutIncoming = false } export function handlePaste(e, cm) { - var pasted = e.clipboardData && e.clipboardData.getData("Text"); + var pasted = e.clipboardData && e.clipboardData.getData("Text") if (pasted) { - e.preventDefault(); + e.preventDefault() if (!cm.isReadOnly() && !cm.options.disableInput) - runInOp(cm, function() { applyTextInput(cm, pasted, 0, null, "paste"); }); - return true; + runInOp(cm, function() { applyTextInput(cm, pasted, 0, null, "paste") }) + return true } } export function triggerElectric(cm, inserted) { // When an 'electric' character is inserted, immediately trigger a reindent - if (!cm.options.electricChars || !cm.options.smartIndent) return; - var sel = cm.doc.sel; + if (!cm.options.electricChars || !cm.options.smartIndent) return + var sel = cm.doc.sel for (var i = sel.ranges.length - 1; i >= 0; i--) { - var range = sel.ranges[i]; - if (range.head.ch > 100 || (i && sel.ranges[i - 1].head.line == range.head.line)) continue; - var mode = cm.getModeAt(range.head); - var indented = false; + var range = sel.ranges[i] + if (range.head.ch > 100 || (i && sel.ranges[i - 1].head.line == range.head.line)) continue + var mode = cm.getModeAt(range.head) + var indented = false if (mode.electricChars) { for (var j = 0; j < mode.electricChars.length; j++) if (inserted.indexOf(mode.electricChars.charAt(j)) > -1) { - indented = indentLine(cm, range.head.line, "smart"); - break; + indented = indentLine(cm, range.head.line, "smart") + break } } else if (mode.electricInput) { if (mode.electricInput.test(getLine(cm.doc, range.head.line).text.slice(0, range.head.ch))) - indented = indentLine(cm, range.head.line, "smart"); + indented = indentLine(cm, range.head.line, "smart") } - if (indented) signalLater(cm, "electricInput", cm, range.head.line); + if (indented) signalLater(cm, "electricInput", cm, range.head.line) } } export function copyableRanges(cm) { - 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.push(cm.getRange(lineRange.anchor, lineRange.head)); + var line = cm.doc.sel.ranges[i].head.line + var lineRange = {anchor: Pos(line, 0), head: Pos(line + 1, 0)} + ranges.push(lineRange) + text.push(cm.getRange(lineRange.anchor, lineRange.head)) } - return {text: text, ranges: ranges}; + return {text: text, ranges: ranges} } export function disableBrowserMagic(field, spellcheck) { - field.setAttribute("autocorrect", "off"); - field.setAttribute("autocapitalize", "off"); - field.setAttribute("spellcheck", !!spellcheck); + field.setAttribute("autocorrect", "off") + field.setAttribute("autocapitalize", "off") + field.setAttribute("spellcheck", !!spellcheck) } export function hiddenTextarea() { - var te = elt("textarea", null, null, "position: absolute; bottom: -1em; padding: 0; width: 1px; height: 1em; outline: none"); - var div = elt("div", [te], null, "overflow: hidden; position: relative; width: 3px; height: 0px;"); + var te = elt("textarea", null, null, "position: absolute; bottom: -1em; padding: 0; width: 1px; height: 1em; outline: none") + var div = elt("div", [te], null, "overflow: hidden; position: relative; width: 3px; height: 0px;") // The textarea is kept positioned near the cursor to prevent the // fact that it'll be scrolled into view on input from scrolling // our fake cursor out of view. On webkit, when wrap=off, paste is // very slow. So make the area wide instead. - if (webkit) te.style.width = "1000px"; - else te.setAttribute("wrap", "off"); + if (webkit) te.style.width = "1000px" + else te.setAttribute("wrap", "off") // If border: 0; -- iOS fails to open keyboard (issue #1287) - if (ios) te.style.border = "1px solid black"; - disableBrowserMagic(te); - return div; + if (ios) te.style.border = "1px solid black" + disableBrowserMagic(te) + return div } diff --git a/src/input/keymap.js b/src/input/keymap.js index 7b287924..0f5941e8 100644 --- a/src/input/keymap.js +++ b/src/input/keymap.js @@ -1,9 +1,9 @@ -import { flipCtrlCmd, mac, presto } from "../util/browser"; -import { map } from "../util/misc"; +import { flipCtrlCmd, mac, presto } from "../util/browser" +import { map } from "../util/misc" -import { keyNames } from "./keynames"; +import { keyNames } from "./keynames" -export var keyMap = {}; +export var keyMap = {} keyMap.basic = { "Left": "goCharLeft", "Right": "goCharRight", "Up": "goLineUp", "Down": "goLineDown", @@ -12,7 +12,7 @@ keyMap.basic = { "Tab": "defaultTab", "Shift-Tab": "indentAuto", "Enter": "newlineAndIndent", "Insert": "toggleOverwrite", "Esc": "singleSelection" -}; +} // Note that the save and find-related commands aren't defined by // default. User code or addons can define them. Unknown commands // are simply ignored. @@ -25,7 +25,7 @@ keyMap.pcDefault = { "Ctrl-[": "indentLess", "Ctrl-]": "indentMore", "Ctrl-U": "undoSelection", "Shift-Ctrl-U": "redoSelection", "Alt-U": "redoSelection", fallthrough: "basic" -}; +} // Very basic readline/emacs-style bindings, which are standard on Mac. keyMap.emacsy = { "Ctrl-F": "goCharRight", "Ctrl-B": "goCharLeft", "Ctrl-P": "goLineUp", "Ctrl-N": "goLineDown", @@ -33,7 +33,7 @@ keyMap.emacsy = { "Ctrl-V": "goPageDown", "Shift-Ctrl-V": "goPageUp", "Ctrl-D": "delCharAfter", "Ctrl-H": "delCharBefore", "Alt-D": "delWordAfter", "Alt-Backspace": "delWordBefore", "Ctrl-K": "killLine", "Ctrl-T": "transposeChars", "Ctrl-O": "openLine" -}; +} 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", @@ -43,27 +43,27 @@ keyMap.macDefault = { "Cmd-[": "indentLess", "Cmd-]": "indentMore", "Cmd-Backspace": "delWrappedLineLeft", "Cmd-Delete": "delWrappedLineRight", "Cmd-U": "undoSelection", "Shift-Cmd-U": "redoSelection", "Ctrl-Up": "goDocStart", "Ctrl-Down": "goDocEnd", fallthrough: ["basic", "emacsy"] -}; -keyMap["default"] = mac ? keyMap.macDefault : keyMap.pcDefault; +} +keyMap["default"] = mac ? keyMap.macDefault : keyMap.pcDefault // KEYMAP DISPATCH function normalizeKeyName(name) { - var parts = name.split(/-(?!$)/), name = parts[parts.length - 1]; - var alt, ctrl, shift, cmd; + var parts = name.split(/-(?!$)/), name = parts[parts.length - 1] + var alt, ctrl, shift, cmd for (var i = 0; i < parts.length - 1; i++) { - var mod = parts[i]; - if (/^(cmd|meta|m)$/i.test(mod)) cmd = true; - else if (/^a(lt)?$/i.test(mod)) alt = true; - else if (/^(c|ctrl|control)$/i.test(mod)) ctrl = true; - else if (/^s(hift)$/i.test(mod)) shift = true; - else throw new Error("Unrecognized modifier name: " + mod); + var mod = parts[i] + if (/^(cmd|meta|m)$/i.test(mod)) cmd = true + else if (/^a(lt)?$/i.test(mod)) alt = true + else if (/^(c|ctrl|control)$/i.test(mod)) ctrl = true + else if (/^s(hift)$/i.test(mod)) shift = true + else throw new Error("Unrecognized modifier name: " + mod) } - if (alt) name = "Alt-" + name; - if (ctrl) name = "Ctrl-" + name; - if (cmd) name = "Cmd-" + name; - if (shift) name = "Shift-" + name; - return name; + if (alt) name = "Alt-" + name + if (ctrl) name = "Ctrl-" + name + if (cmd) name = "Cmd-" + name + if (shift) name = "Shift-" + name + return name } // This is a kludge to keep keymaps mostly working as raw objects @@ -72,45 +72,45 @@ function normalizeKeyName(name) { // new normalized keymap, and then updates the old object to reflect // this. export function normalizeKeyMap(keymap) { - var copy = {}; + var copy = {} for (var keyname in keymap) if (keymap.hasOwnProperty(keyname)) { - var value = keymap[keyname]; - if (/^(name|fallthrough|(de|at)tach)$/.test(keyname)) continue; - if (value == "...") { delete keymap[keyname]; continue; } + var value = keymap[keyname] + if (/^(name|fallthrough|(de|at)tach)$/.test(keyname)) continue + if (value == "...") { delete keymap[keyname]; continue } - var keys = map(keyname.split(" "), normalizeKeyName); + var keys = map(keyname.split(" "), normalizeKeyName) for (var i = 0; i < keys.length; i++) { - var val, name; + var val, name if (i == keys.length - 1) { - name = keys.join(" "); - val = value; + name = keys.join(" ") + val = value } else { - name = keys.slice(0, i + 1).join(" "); - val = "..."; + name = keys.slice(0, i + 1).join(" ") + val = "..." } - var prev = copy[name]; - if (!prev) copy[name] = val; - else if (prev != val) throw new Error("Inconsistent bindings for " + name); + var prev = copy[name] + if (!prev) copy[name] = val + else if (prev != val) throw new Error("Inconsistent bindings for " + name) } - delete keymap[keyname]; + delete keymap[keyname] } - for (var prop in copy) keymap[prop] = copy[prop]; - return keymap; + for (var prop in copy) keymap[prop] = copy[prop] + return keymap } export function lookupKey(key, map, handle, context) { - map = getKeyMap(map); - var found = map.call ? map.call(key, context) : map[key]; - if (found === false) return "nothing"; - if (found === "...") return "multi"; - if (found != null && handle(found)) return "handled"; + map = getKeyMap(map) + var found = map.call ? map.call(key, context) : map[key] + if (found === false) return "nothing" + if (found === "...") return "multi" + if (found != null && handle(found)) return "handled" if (map.fallthrough) { if (Object.prototype.toString.call(map.fallthrough) != "[object Array]") - return lookupKey(key, map.fallthrough, handle, context); + return lookupKey(key, map.fallthrough, handle, context) for (var i = 0; i < map.fallthrough.length; i++) { - var result = lookupKey(key, map.fallthrough[i], handle, context); - if (result) return result; + var result = lookupKey(key, map.fallthrough[i], handle, context) + if (result) return result } } } @@ -118,22 +118,22 @@ export function lookupKey(key, map, handle, context) { // Modifier key presses don't count as 'real' key presses for the // purpose of keymap fallthrough. export function isModifierKey(value) { - var name = typeof value == "string" ? value : keyNames[value.keyCode]; - return name == "Ctrl" || name == "Alt" || name == "Shift" || name == "Mod"; + var name = typeof value == "string" ? value : keyNames[value.keyCode] + return name == "Ctrl" || name == "Alt" || name == "Shift" || name == "Mod" } // Look up the name of a key as indicated by an event object. export function keyName(event, noShift) { - if (presto && event.keyCode == 34 && event["char"]) return false; - var base = keyNames[event.keyCode], name = base; - if (name == null || event.altGraphKey) return false; - if (event.altKey && base != "Alt") name = "Alt-" + name; - if ((flipCtrlCmd ? event.metaKey : event.ctrlKey) && base != "Ctrl") name = "Ctrl-" + name; - if ((flipCtrlCmd ? event.ctrlKey : event.metaKey) && base != "Cmd") name = "Cmd-" + name; - if (!noShift && event.shiftKey && base != "Shift") name = "Shift-" + name; - return name; + if (presto && event.keyCode == 34 && event["char"]) return false + var base = keyNames[event.keyCode], name = base + if (name == null || event.altGraphKey) return false + if (event.altKey && base != "Alt") name = "Alt-" + name + if ((flipCtrlCmd ? event.metaKey : event.ctrlKey) && base != "Ctrl") name = "Ctrl-" + name + if ((flipCtrlCmd ? event.ctrlKey : event.metaKey) && base != "Cmd") name = "Cmd-" + name + if (!noShift && event.shiftKey && base != "Shift") name = "Shift-" + name + return name } export function getKeyMap(val) { - return typeof val == "string" ? keyMap[val] : val; + return typeof val == "string" ? keyMap[val] : val } diff --git a/src/input/keynames.js b/src/input/keynames.js index 5956093e..8cf11f38 100644 --- a/src/input/keynames.js +++ b/src/input/keynames.js @@ -7,11 +7,11 @@ export var keyNames = { 173: "-", 186: ";", 187: "=", 188: ",", 189: "-", 190: ".", 191: "/", 192: "`", 219: "[", 220: "\\", 221: "]", 222: "'", 63232: "Up", 63233: "Down", 63234: "Left", 63235: "Right", 63272: "Delete", 63273: "Home", 63275: "End", 63276: "PageUp", 63277: "PageDown", 63302: "Insert" -}; +} // Number keys -for (var i = 0; i < 10; i++) keyNames[i + 48] = keyNames[i + 96] = String(i); +for (var i = 0; i < 10; i++) keyNames[i + 48] = keyNames[i + 96] = String(i) // Alphabetic keys -for (var i = 65; i <= 90; i++) keyNames[i] = String.fromCharCode(i); +for (var i = 65; i <= 90; i++) keyNames[i] = String.fromCharCode(i) // Function keys -for (var i = 1; i <= 12; i++) keyNames[i + 111] = keyNames[i + 63235] = "F" + i; +for (var i = 1; i <= 12; i++) keyNames[i + 111] = keyNames[i + 63235] = "F" + i diff --git a/src/line/highlight.js b/src/line/highlight.js index eada1a06..0632be6e 100644 --- a/src/line/highlight.js +++ b/src/line/highlight.js @@ -1,9 +1,9 @@ -import { countColumn } from "../util/misc"; -import { copyState, innerMode, startState } from "../modes"; -import StringStream from "../util/StringStream"; +import { countColumn } from "../util/misc" +import { copyState, innerMode, startState } from "../modes" +import StringStream from "../util/StringStream" -import { getLine, lineNo } from "./utils_line"; -import { clipPos } from "./pos"; +import { getLine, lineNo } from "./utils_line" +import { clipPos } from "./pos" // Compute a style array (an array starting with a mode generation // -- for invalidation -- followed by pairs of end positions and @@ -12,98 +12,98 @@ import { clipPos } from "./pos"; export function highlightLine(cm, line, state, forceToEnd) { // A styles array always starts with a number identifying the // mode/overlays that it is based on (for easy invalidation). - var st = [cm.state.modeGen], lineClasses = {}; + var st = [cm.state.modeGen], lineClasses = {} // Compute the base array of styles runMode(cm, line.text, cm.doc.mode, state, function(end, style) { - st.push(end, style); - }, lineClasses, forceToEnd); + st.push(end, style) + }, lineClasses, forceToEnd) // Run overlays, adjust style array. for (var o = 0; o < cm.state.overlays.length; ++o) { - var overlay = cm.state.overlays[o], i = 1, at = 0; + var overlay = cm.state.overlays[o], i = 1, at = 0 runMode(cm, line.text, overlay.mode, true, function(end, style) { - var start = i; + var start = i // Ensure there's a token end at the current position, and that i points at it while (at < end) { - var i_end = st[i]; + var i_end = st[i] if (i_end > end) - st.splice(i, 1, end, st[i+1], i_end); - i += 2; - at = Math.min(end, i_end); + st.splice(i, 1, end, st[i+1], i_end) + i += 2 + at = Math.min(end, i_end) } - if (!style) return; + if (!style) return if (overlay.opaque) { - st.splice(start, i - start, end, "cm-overlay " + style); - i = start + 2; + st.splice(start, i - start, end, "cm-overlay " + style) + i = start + 2 } else { for (; start < i; start += 2) { - var cur = st[start+1]; - st[start+1] = (cur ? cur + " " : "") + "cm-overlay " + style; + var cur = st[start+1] + st[start+1] = (cur ? cur + " " : "") + "cm-overlay " + style } } - }, lineClasses); + }, lineClasses) } - return {styles: st, classes: lineClasses.bgClass || lineClasses.textClass ? lineClasses : null}; + return {styles: st, classes: lineClasses.bgClass || lineClasses.textClass ? lineClasses : null} } export function getLineStyles(cm, line, updateFrontier) { if (!line.styles || line.styles[0] != cm.state.modeGen) { - var state = getStateBefore(cm, lineNo(line)); - var result = highlightLine(cm, line, line.text.length > cm.options.maxHighlightLength ? copyState(cm.doc.mode, state) : state); - line.stateAfter = state; - line.styles = result.styles; - if (result.classes) line.styleClasses = result.classes; - else if (line.styleClasses) line.styleClasses = null; - if (updateFrontier === cm.doc.frontier) cm.doc.frontier++; + var state = getStateBefore(cm, lineNo(line)) + var result = highlightLine(cm, line, line.text.length > cm.options.maxHighlightLength ? copyState(cm.doc.mode, state) : state) + line.stateAfter = state + line.styles = result.styles + if (result.classes) line.styleClasses = result.classes + else if (line.styleClasses) line.styleClasses = null + if (updateFrontier === cm.doc.frontier) cm.doc.frontier++ } - return line.styles; + return line.styles } export function getStateBefore(cm, n, precise) { - var doc = cm.doc, display = cm.display; - 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); + var doc = cm.doc, display = cm.display + 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) doc.iter(pos, n, function(line) { - processLine(cm, line.text, state); - var save = pos == n - 1 || pos % 5 == 0 || pos >= display.viewFrom && pos < display.viewTo; - line.stateAfter = save ? copyState(doc.mode, state) : null; - ++pos; - }); - if (precise) doc.frontier = pos; - return state; + processLine(cm, line.text, state) + var save = pos == n - 1 || pos % 5 == 0 || pos >= display.viewFrom && pos < display.viewTo + line.stateAfter = save ? copyState(doc.mode, state) : null + ++pos + }) + if (precise) doc.frontier = pos + return state } // Lightweight form of highlight -- proceed over this line and // update state, but don't save a style array. Used for lines that // aren't currently visible. export function processLine(cm, text, state, startAt) { - var mode = cm.doc.mode; - var stream = new StringStream(text, cm.options.tabSize); - stream.start = stream.pos = startAt || 0; - if (text == "") callBlankLine(mode, state); + var mode = cm.doc.mode + var stream = new StringStream(text, cm.options.tabSize) + stream.start = stream.pos = startAt || 0 + if (text == "") callBlankLine(mode, state) while (!stream.eol()) { - readToken(mode, stream, state); - stream.start = stream.pos; + readToken(mode, stream, state) + stream.start = stream.pos } } function callBlankLine(mode, state) { - if (mode.blankLine) return mode.blankLine(state); - if (!mode.innerMode) return; - var inner = innerMode(mode, state); - if (inner.mode.blankLine) return inner.mode.blankLine(inner.state); + if (mode.blankLine) return mode.blankLine(state) + if (!mode.innerMode) return + var inner = innerMode(mode, state) + if (inner.mode.blankLine) return inner.mode.blankLine(inner.state) } export function readToken(mode, stream, state, inner) { for (var i = 0; i < 10; i++) { - if (inner) inner[0] = innerMode(mode, state).mode; - var style = mode.token(stream, state); - if (stream.pos > stream.start) return style; + if (inner) inner[0] = innerMode(mode, state).mode + var style = mode.token(stream, state) + if (stream.pos > stream.start) return style } - throw new Error("Mode " + mode.name + " failed to advance stream."); + throw new Error("Mode " + mode.name + " failed to advance stream.") } // Utility for getTokenAt and getLineTokens @@ -112,73 +112,73 @@ export function takeToken(cm, pos, precise, asArray) { return {start: stream.start, end: stream.pos, string: stream.current(), type: style || null, - state: copy ? copyState(doc.mode, state) : state}; + state: copy ? copyState(doc.mode, state) : state} } - var doc = cm.doc, mode = doc.mode, style; - pos = clipPos(doc, pos); - var line = getLine(doc, pos.line), state = getStateBefore(cm, pos.line, precise); - var stream = new StringStream(line.text, cm.options.tabSize), tokens; - if (asArray) tokens = []; + var doc = cm.doc, mode = doc.mode, style + pos = clipPos(doc, pos) + var line = getLine(doc, pos.line), state = getStateBefore(cm, pos.line, precise) + var stream = new StringStream(line.text, cm.options.tabSize), tokens + if (asArray) tokens = [] while ((asArray || stream.pos < pos.ch) && !stream.eol()) { - stream.start = stream.pos; - style = readToken(mode, stream, state); - if (asArray) tokens.push(getObj(true)); + stream.start = stream.pos + style = readToken(mode, stream, state) + if (asArray) tokens.push(getObj(true)) } - return asArray ? tokens : getObj(); + return asArray ? tokens : getObj() } function extractLineClasses(type, output) { if (type) for (;;) { - var lineClass = type.match(/(?:^|\s+)line-(background-)?(\S+)/); - if (!lineClass) break; - type = type.slice(0, lineClass.index) + type.slice(lineClass.index + lineClass[0].length); - var prop = lineClass[1] ? "bgClass" : "textClass"; + var lineClass = type.match(/(?:^|\s+)line-(background-)?(\S+)/) + if (!lineClass) break + type = type.slice(0, lineClass.index) + type.slice(lineClass.index + lineClass[0].length) + var prop = lineClass[1] ? "bgClass" : "textClass" if (output[prop] == null) - output[prop] = lineClass[2]; + output[prop] = lineClass[2] else if (!(new RegExp("(?:^|\s)" + lineClass[2] + "(?:$|\s)")).test(output[prop])) - output[prop] += " " + lineClass[2]; + output[prop] += " " + lineClass[2] } - return type; + return type } // Run the given mode's parser over a line, calling f for each token. function runMode(cm, text, mode, state, f, lineClasses, forceToEnd) { - var flattenSpans = mode.flattenSpans; - if (flattenSpans == null) flattenSpans = cm.options.flattenSpans; - var curStart = 0, curStyle = null; - var stream = new StringStream(text, cm.options.tabSize), style; - var inner = cm.options.addModeClass && [null]; - if (text == "") extractLineClasses(callBlankLine(mode, state), lineClasses); + var flattenSpans = mode.flattenSpans + if (flattenSpans == null) flattenSpans = cm.options.flattenSpans + var curStart = 0, curStyle = null + var stream = new StringStream(text, cm.options.tabSize), style + var inner = cm.options.addModeClass && [null] + if (text == "") extractLineClasses(callBlankLine(mode, state), lineClasses) while (!stream.eol()) { if (stream.pos > cm.options.maxHighlightLength) { - flattenSpans = false; - if (forceToEnd) processLine(cm, text, state, stream.pos); - stream.pos = text.length; - style = null; + flattenSpans = false + if (forceToEnd) processLine(cm, text, state, stream.pos) + stream.pos = text.length + style = null } else { - style = extractLineClasses(readToken(mode, stream, state, inner), lineClasses); + style = extractLineClasses(readToken(mode, stream, state, inner), lineClasses) } if (inner) { - var mName = inner[0].name; - if (mName) style = "m-" + (style ? mName + " " + style : mName); + var mName = inner[0].name + if (mName) style = "m-" + (style ? mName + " " + style : mName) } if (!flattenSpans || curStyle != style) { while (curStart < stream.start) { - curStart = Math.min(stream.start, curStart + 5000); - f(curStart, curStyle); + curStart = Math.min(stream.start, curStart + 5000) + f(curStart, curStyle) } - curStyle = style; + curStyle = style } - stream.start = stream.pos; + stream.start = stream.pos } while (curStart < stream.pos) { // Webkit seems to refuse to render text nodes longer than 57444 // characters, and returns inaccurate measurements in nodes // starting around 5000 chars. - var pos = Math.min(stream.pos, curStart + 5000); - f(pos, curStyle); - curStart = pos; + var pos = Math.min(stream.pos, curStart + 5000) + f(pos, curStyle) + curStart = pos } } @@ -188,17 +188,17 @@ function runMode(cm, text, mode, state, f, lineClasses, forceToEnd) { // smallest indentation, which tends to need the least context to // parse correctly. function findStartLine(cm, n, precise) { - var minindent, minline, doc = cm.doc; - var lim = precise ? -1 : n - (cm.doc.mode.innerMode ? 1000 : 100); + var minindent, minline, doc = cm.doc + var lim = precise ? -1 : n - (cm.doc.mode.innerMode ? 1000 : 100) for (var search = n; 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; - var indented = countColumn(line.text, null, cm.options.tabSize); + if (search <= doc.first) return doc.first + var line = getLine(doc, search - 1) + if (line.stateAfter && (!precise || search <= doc.frontier)) return search + var indented = countColumn(line.text, null, cm.options.tabSize) if (minline == null || minindent > indented) { - minline = search - 1; - minindent = indented; + minline = search - 1 + minindent = indented } } - return minline; + return minline } diff --git a/src/line/line_data.js b/src/line/line_data.js index 018bd0e2..688aa1bd 100644 --- a/src/line/line_data.js +++ b/src/line/line_data.js @@ -1,55 +1,55 @@ -import { getOrder } from "../util/bidi"; -import { ie, ie_version, webkit } from "../util/browser"; -import { elt, joinClasses } from "../util/dom"; -import { eventMixin, signal } from "../util/event"; -import { hasBadBidiRects, zeroWidthElement } from "../util/feature_detection"; -import { lst, spaceStr } from "../util/misc"; +import { getOrder } from "../util/bidi" +import { ie, ie_version, webkit } from "../util/browser" +import { elt, joinClasses } from "../util/dom" +import { eventMixin, signal } from "../util/event" +import { hasBadBidiRects, zeroWidthElement } from "../util/feature_detection" +import { lst, spaceStr } from "../util/misc" -import { getLineStyles } from "./highlight"; -import { attachMarkedSpans, compareCollapsedMarkers, detachMarkedSpans, lineIsHidden, visualLineContinued } from "./spans"; -import { getLine, lineNo, updateLineHeight } from "./utils_line"; +import { getLineStyles } from "./highlight" +import { attachMarkedSpans, compareCollapsedMarkers, detachMarkedSpans, lineIsHidden, visualLineContinued } from "./spans" +import { getLine, lineNo, updateLineHeight } from "./utils_line" // LINE DATA STRUCTURE // Line objects. These hold state related to a line, including // highlighting info (the styles array). export function Line(text, markedSpans, estimateHeight) { - this.text = text; - attachMarkedSpans(this, markedSpans); - this.height = estimateHeight ? estimateHeight(this) : 1; + this.text = text + attachMarkedSpans(this, markedSpans) + this.height = estimateHeight ? estimateHeight(this) : 1 } -eventMixin(Line); -Line.prototype.lineNo = function() { return lineNo(this); }; +eventMixin(Line) +Line.prototype.lineNo = function() { return lineNo(this) } // Change the content (text, markers) of a line. Automatically // invalidates cached information and tries to re-estimate the // line's height. export 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); - var estHeight = estimateHeight ? estimateHeight(line) : 1; - if (estHeight != line.height) updateLineHeight(line, estHeight); + 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) + var estHeight = estimateHeight ? estimateHeight(line) : 1 + if (estHeight != line.height) updateLineHeight(line, estHeight) } // Detach a line from the document tree and its markers. export function cleanUpLine(line) { - line.parent = null; - detachMarkedSpans(line); + line.parent = null + detachMarkedSpans(line) } // Convert a style as returned by a mode (either null, or a string // containing one or more styles) to a CSS style. This is cached, // and also looks for line-wide styles. -var styleToClassCache = {}, styleToClassCacheWithMode = {}; +var styleToClassCache = {}, styleToClassCacheWithMode = {} function interpretTokenStyle(style, options) { - if (!style || /^\s*$/.test(style)) return null; - var cache = options.addModeClass ? styleToClassCacheWithMode : styleToClassCache; + if (!style || /^\s*$/.test(style)) return null + var cache = options.addModeClass ? styleToClassCacheWithMode : styleToClassCache return cache[style] || - (cache[style] = style.replace(/\S+/g, "cm-$&")); + (cache[style] = style.replace(/\S+/g, "cm-$&")) } // Render the DOM representation of the text of a line. Also builds @@ -61,43 +61,43 @@ export function buildLineContent(cm, lineView) { // The padding-right forces the element to have a 'border', which // is needed on Webkit to be able to get line-level bounding // rectangles for it (in measureChar). - var content = elt("span", null, null, webkit ? "padding-right: .1px" : null); + var content = elt("span", null, null, webkit ? "padding-right: .1px" : null) var builder = {pre: elt("pre", [content], "CodeMirror-line"), content: content, col: 0, pos: 0, cm: cm, trailingSpace: false, - splitSpaces: (ie || webkit) && cm.getOption("lineWrapping")}; - lineView.measure = {}; + splitSpaces: (ie || webkit) && cm.getOption("lineWrapping")} + lineView.measure = {} // Iterate over the logical lines that make up this visual line. for (var i = 0; i <= (lineView.rest ? lineView.rest.length : 0); i++) { - var line = i ? lineView.rest[i - 1] : lineView.line, order; - builder.pos = 0; - builder.addToken = buildToken; + var line = i ? lineView.rest[i - 1] : lineView.line, order + builder.pos = 0 + builder.addToken = buildToken // Optionally wire in some hacks into the token-rendering // algorithm, to deal with browser quirks. if (hasBadBidiRects(cm.display.measure) && (order = getOrder(line))) - builder.addToken = buildTokenBadBidi(builder.addToken, order); - builder.map = []; - var allowFrontierUpdate = lineView != cm.display.externalMeasured && lineNo(line); - insertLineContent(line, builder, getLineStyles(cm, line, allowFrontierUpdate)); + builder.addToken = buildTokenBadBidi(builder.addToken, order) + builder.map = [] + var allowFrontierUpdate = lineView != cm.display.externalMeasured && lineNo(line) + insertLineContent(line, builder, getLineStyles(cm, line, allowFrontierUpdate)) if (line.styleClasses) { if (line.styleClasses.bgClass) - builder.bgClass = joinClasses(line.styleClasses.bgClass, builder.bgClass || ""); + builder.bgClass = joinClasses(line.styleClasses.bgClass, builder.bgClass || "") if (line.styleClasses.textClass) - builder.textClass = joinClasses(line.styleClasses.textClass, builder.textClass || ""); + builder.textClass = joinClasses(line.styleClasses.textClass, builder.textClass || "") } // Ensure at least a single node is present, for measuring. if (builder.map.length == 0) - builder.map.push(0, 0, builder.content.appendChild(zeroWidthElement(cm.display.measure))); + builder.map.push(0, 0, builder.content.appendChild(zeroWidthElement(cm.display.measure))) // Store the map and a cache object for the current logical line if (i == 0) { - lineView.measure.map = builder.map; - lineView.measure.cache = {}; + lineView.measure.map = builder.map + lineView.measure.cache = {} } else { - (lineView.measure.maps || (lineView.measure.maps = [])).push(builder.map); - (lineView.measure.caches || (lineView.measure.caches = [])).push({}); + (lineView.measure.maps || (lineView.measure.maps = [])).push(builder.map) + ;(lineView.measure.caches || (lineView.measure.caches = [])).push({}) } } @@ -105,82 +105,82 @@ export function buildLineContent(cm, lineView) { if (webkit) { var last = builder.content.lastChild if (/\bcm-tab\b/.test(last.className) || (last.querySelector && last.querySelector(".cm-tab"))) - builder.content.className = "cm-tab-wrap-hack"; + builder.content.className = "cm-tab-wrap-hack" } - signal(cm, "renderLine", cm, lineView.line, builder.pre); + signal(cm, "renderLine", cm, lineView.line, builder.pre) if (builder.pre.className) - builder.textClass = joinClasses(builder.pre.className, builder.textClass || ""); + builder.textClass = joinClasses(builder.pre.className, builder.textClass || "") - return builder; + return builder } export function defaultSpecialCharPlaceholder(ch) { - var token = elt("span", "\u2022", "cm-invalidchar"); - token.title = "\\u" + ch.charCodeAt(0).toString(16); - token.setAttribute("aria-label", token.title); - return token; + var token = elt("span", "\u2022", "cm-invalidchar") + token.title = "\\u" + ch.charCodeAt(0).toString(16) + token.setAttribute("aria-label", token.title) + return token } // Build up the DOM representation for a single token, and add it to // the line map. Takes care to render special characters separately. function buildToken(builder, text, style, startStyle, endStyle, title, css) { - if (!text) return; + if (!text) return var displayText = builder.splitSpaces ? splitSpaces(text, builder.trailingSpace) : text - var special = builder.cm.state.specialChars, mustWrap = false; + var special = builder.cm.state.specialChars, mustWrap = false if (!special.test(text)) { - builder.col += text.length; - var content = document.createTextNode(displayText); - builder.map.push(builder.pos, builder.pos + text.length, content); - if (ie && ie_version < 9) mustWrap = true; - builder.pos += text.length; + builder.col += text.length + var content = document.createTextNode(displayText) + builder.map.push(builder.pos, builder.pos + text.length, content) + if (ie && ie_version < 9) mustWrap = true + builder.pos += text.length } else { - var content = document.createDocumentFragment(), pos = 0; + var content = document.createDocumentFragment(), pos = 0 while (true) { - special.lastIndex = pos; - var m = special.exec(text); - var skipped = m ? m.index - pos : text.length - pos; + special.lastIndex = pos + var m = special.exec(text) + var skipped = m ? m.index - pos : text.length - pos if (skipped) { - var txt = document.createTextNode(displayText.slice(pos, pos + skipped)); - if (ie && ie_version < 9) content.appendChild(elt("span", [txt])); - else content.appendChild(txt); - builder.map.push(builder.pos, builder.pos + skipped, txt); - builder.col += skipped; - builder.pos += skipped; + var txt = document.createTextNode(displayText.slice(pos, pos + skipped)) + if (ie && ie_version < 9) content.appendChild(elt("span", [txt])) + else content.appendChild(txt) + builder.map.push(builder.pos, builder.pos + skipped, txt) + builder.col += skipped + builder.pos += skipped } - if (!m) break; - pos += skipped + 1; + if (!m) break + pos += skipped + 1 if (m[0] == "\t") { - var tabSize = builder.cm.options.tabSize, tabWidth = tabSize - builder.col % tabSize; - var txt = content.appendChild(elt("span", spaceStr(tabWidth), "cm-tab")); - txt.setAttribute("role", "presentation"); - txt.setAttribute("cm-text", "\t"); - builder.col += tabWidth; + var tabSize = builder.cm.options.tabSize, tabWidth = tabSize - builder.col % tabSize + var txt = content.appendChild(elt("span", spaceStr(tabWidth), "cm-tab")) + txt.setAttribute("role", "presentation") + txt.setAttribute("cm-text", "\t") + builder.col += tabWidth } else if (m[0] == "\r" || m[0] == "\n") { - var txt = content.appendChild(elt("span", m[0] == "\r" ? "\u240d" : "\u2424", "cm-invalidchar")); - txt.setAttribute("cm-text", m[0]); - builder.col += 1; + var txt = content.appendChild(elt("span", m[0] == "\r" ? "\u240d" : "\u2424", "cm-invalidchar")) + txt.setAttribute("cm-text", m[0]) + builder.col += 1 } else { - var txt = builder.cm.options.specialCharPlaceholder(m[0]); - txt.setAttribute("cm-text", m[0]); - if (ie && ie_version < 9) content.appendChild(elt("span", [txt])); - else content.appendChild(txt); - builder.col += 1; + var txt = builder.cm.options.specialCharPlaceholder(m[0]) + txt.setAttribute("cm-text", m[0]) + if (ie && ie_version < 9) content.appendChild(elt("span", [txt])) + else content.appendChild(txt) + builder.col += 1 } - builder.map.push(builder.pos, builder.pos + 1, txt); - builder.pos++; + builder.map.push(builder.pos, builder.pos + 1, txt) + builder.pos++ } } builder.trailingSpace = displayText.charCodeAt(text.length - 1) == 32 if (style || startStyle || endStyle || mustWrap || css) { - var fullStyle = style || ""; - if (startStyle) fullStyle += startStyle; - if (endStyle) fullStyle += endStyle; - var token = elt("span", [content], fullStyle, css); - if (title) token.title = title; - return builder.content.appendChild(token); + var fullStyle = style || "" + if (startStyle) fullStyle += startStyle + if (endStyle) fullStyle += endStyle + var token = elt("span", [content], fullStyle, css) + if (title) token.title = title + return builder.content.appendChild(token) } - builder.content.appendChild(content); + builder.content.appendChild(content) } function splitSpaces(text, trailingBefore) { @@ -200,105 +200,105 @@ function splitSpaces(text, trailingBefore) { // right-to-left text. function buildTokenBadBidi(inner, order) { return function(builder, text, style, startStyle, endStyle, title, css) { - style = style ? style + " cm-force-border" : "cm-force-border"; - var start = builder.pos, end = start + text.length; + style = style ? style + " cm-force-border" : "cm-force-border" + var start = builder.pos, end = start + text.length for (;;) { // Find the part that overlaps with the start of this text for (var i = 0; i < order.length; i++) { - var part = order[i]; - if (part.to > start && part.from <= start) break; + var part = order[i] + if (part.to > start && part.from <= start) break } - if (part.to >= end) return inner(builder, text, style, startStyle, endStyle, title, css); - inner(builder, text.slice(0, part.to - start), style, startStyle, null, title, css); - startStyle = null; - text = text.slice(part.to - start); - start = part.to; + if (part.to >= end) return inner(builder, text, style, startStyle, endStyle, title, css) + inner(builder, text.slice(0, part.to - start), style, startStyle, null, title, css) + startStyle = null + text = text.slice(part.to - start) + start = part.to } - }; + } } function buildCollapsedSpan(builder, size, marker, ignoreWidget) { - var widget = !ignoreWidget && marker.widgetNode; - if (widget) builder.map.push(builder.pos, builder.pos + size, widget); + var widget = !ignoreWidget && marker.widgetNode + if (widget) builder.map.push(builder.pos, builder.pos + size, widget) if (!ignoreWidget && builder.cm.display.input.needsContentAttribute) { if (!widget) - widget = builder.content.appendChild(document.createElement("span")); - widget.setAttribute("cm-marker", marker.id); + widget = builder.content.appendChild(document.createElement("span")) + widget.setAttribute("cm-marker", marker.id) } if (widget) { - builder.cm.display.input.setUneditable(widget); - builder.content.appendChild(widget); + builder.cm.display.input.setUneditable(widget) + builder.content.appendChild(widget) } - builder.pos += size; + builder.pos += size builder.trailingSpace = false } // Outputs a number of spans to make up a line, taking highlighting // and marked text into account. function insertLineContent(line, builder, styles) { - var spans = line.markedSpans, allText = line.text, at = 0; + 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]), interpretTokenStyle(styles[i+1], builder.cm.options)); - return; + builder.addToken(builder, allText.slice(at, at = styles[i]), interpretTokenStyle(styles[i+1], builder.cm.options)) + return } - var len = allText.length, pos = 0, i = 1, text = "", style, css; - var nextChange = 0, spanStyle, spanEndStyle, spanStartStyle, title, collapsed; + var len = allText.length, pos = 0, i = 1, text = "", style, css + var nextChange = 0, spanStyle, spanEndStyle, spanStartStyle, title, collapsed for (;;) { if (nextChange == pos) { // Update current marker set - spanStyle = spanEndStyle = spanStartStyle = title = css = ""; - collapsed = null; nextChange = Infinity; + spanStyle = spanEndStyle = spanStartStyle = title = css = "" + collapsed = null; nextChange = Infinity var foundBookmarks = [], endStyles for (var j = 0; j < spans.length; ++j) { - var sp = spans[j], m = sp.marker; + var sp = spans[j], m = sp.marker if (m.type == "bookmark" && sp.from == pos && m.widgetNode) { - foundBookmarks.push(m); + foundBookmarks.push(m) } else if (sp.from <= pos && (sp.to == null || sp.to > pos || m.collapsed && sp.to == pos && sp.from == pos)) { if (sp.to != null && sp.to != pos && nextChange > sp.to) { - nextChange = sp.to; - spanEndStyle = ""; + nextChange = sp.to + spanEndStyle = "" } - if (m.className) spanStyle += " " + m.className; - if (m.css) css = (css ? css + ";" : "") + m.css; - if (m.startStyle && sp.from == pos) spanStartStyle += " " + m.startStyle; + if (m.className) spanStyle += " " + m.className + if (m.css) css = (css ? css + ";" : "") + m.css + if (m.startStyle && sp.from == pos) spanStartStyle += " " + m.startStyle if (m.endStyle && sp.to == nextChange) (endStyles || (endStyles = [])).push(m.endStyle, sp.to) - if (m.title && !title) title = m.title; + if (m.title && !title) title = m.title if (m.collapsed && (!collapsed || compareCollapsedMarkers(collapsed.marker, m) < 0)) - collapsed = sp; + collapsed = sp } else if (sp.from > pos && nextChange > sp.from) { - nextChange = sp.from; + nextChange = sp.from } } if (endStyles) for (var j = 0; j < endStyles.length; j += 2) if (endStyles[j + 1] == nextChange) spanEndStyle += " " + endStyles[j] if (!collapsed || collapsed.from == pos) for (var j = 0; j < foundBookmarks.length; ++j) - buildCollapsedSpan(builder, 0, foundBookmarks[j]); + buildCollapsedSpan(builder, 0, foundBookmarks[j]) if (collapsed && (collapsed.from || 0) == pos) { buildCollapsedSpan(builder, (collapsed.to == null ? len + 1 : collapsed.to) - pos, - collapsed.marker, collapsed.from == null); - if (collapsed.to == null) return; - if (collapsed.to == pos) collapsed = false; + collapsed.marker, collapsed.from == null) + if (collapsed.to == null) return + if (collapsed.to == pos) collapsed = false } } - if (pos >= len) break; + if (pos >= len) break - var upto = Math.min(len, nextChange); + var upto = Math.min(len, nextChange) while (true) { if (text) { - var end = pos + text.length; + var end = pos + text.length if (!collapsed) { - var tokenText = end > upto ? text.slice(0, upto - pos) : text; + var tokenText = end > upto ? text.slice(0, upto - pos) : text builder.addToken(builder, tokenText, style ? style + spanStyle : spanStyle, - spanStartStyle, pos + tokenText.length == nextChange ? spanEndStyle : "", title, css); + spanStartStyle, pos + tokenText.length == nextChange ? spanEndStyle : "", title, css) } - if (end >= upto) {text = text.slice(upto - pos); pos = upto; break;} - pos = end; - spanStartStyle = ""; + if (end >= upto) {text = text.slice(upto - pos); pos = upto; break} + pos = end + spanStartStyle = "" } - text = allText.slice(at, at = styles[i++]); - style = interpretTokenStyle(styles[i++], builder.cm.options); + text = allText.slice(at, at = styles[i++]) + style = interpretTokenStyle(styles[i++], builder.cm.options) } } } @@ -309,22 +309,22 @@ function insertLineContent(line, builder, styles) { // logical lines, if those are connected by collapsed ranges. export function LineView(doc, line, lineN) { // The starting line - this.line = line; + this.line = line // Continuing lines, if any - this.rest = visualLineContinued(line); + this.rest = visualLineContinued(line) // Number of logical lines in this visual line - this.size = this.rest ? lineNo(lst(this.rest)) - lineN + 1 : 1; - this.node = this.text = null; - this.hidden = lineIsHidden(doc, line); + this.size = this.rest ? lineNo(lst(this.rest)) - lineN + 1 : 1 + this.node = this.text = null + this.hidden = lineIsHidden(doc, line) } // Create a range of LineView objects for the given lines. export function buildViewArray(cm, from, to) { - var array = [], nextPos; + var array = [], nextPos for (var pos = from; pos < to; pos = nextPos) { - var view = new LineView(cm.doc, getLine(cm.doc, pos), pos); - nextPos = pos + view.size; - array.push(view); + var view = new LineView(cm.doc, getLine(cm.doc, pos), pos) + nextPos = pos + view.size + array.push(view) } - return array; + return array } diff --git a/src/line/pos.js b/src/line/pos.js index e81eb584..b5d2513a 100644 --- a/src/line/pos.js +++ b/src/line/pos.js @@ -1,35 +1,35 @@ -import { getLine } from "./utils_line"; +import { getLine } from "./utils_line" // A Pos instance represents a position within the text. export function Pos (line, ch) { - if (!(this instanceof Pos)) return new Pos(line, ch); - this.line = line; this.ch = ch; + if (!(this instanceof Pos)) return new Pos(line, ch) + this.line = line; this.ch = ch } // Compare two positions, return 0 if they are the same, a negative // number when a is less, and a positive number otherwise. -export function cmp(a, b) { return a.line - b.line || a.ch - b.ch; } +export function cmp(a, b) { return a.line - b.line || a.ch - b.ch } -export function copyPos(x) {return Pos(x.line, x.ch);} -export function maxPos(a, b) { return cmp(a, b) < 0 ? b : a; } -export function minPos(a, b) { return cmp(a, b) < 0 ? a : b; } +export function copyPos(x) {return Pos(x.line, x.ch)} +export function maxPos(a, b) { return cmp(a, b) < 0 ? b : a } +export function minPos(a, b) { return cmp(a, b) < 0 ? a : b } // Most of the external API clips given positions to make sure they // actually exist within the document. -export function clipLine(doc, n) {return Math.max(doc.first, Math.min(n, doc.first + doc.size - 1));} +export function clipLine(doc, n) {return Math.max(doc.first, Math.min(n, doc.first + doc.size - 1))} export function clipPos(doc, pos) { - 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); - return clipToLen(pos, getLine(doc, pos.line).text.length); + 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) + 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; + 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 } export function clipPosArray(doc, array) { - for (var out = [], i = 0; i < array.length; i++) out[i] = clipPos(doc, array[i]); - return out; + for (var out = [], i = 0; i < array.length; i++) out[i] = clipPos(doc, array[i]) + return out } diff --git a/src/line/saw_special_spans.js b/src/line/saw_special_spans.js index 4c1f8e0b..0cf5ff32 100644 --- a/src/line/saw_special_spans.js +++ b/src/line/saw_special_spans.js @@ -1,10 +1,10 @@ // Optimize some code when these features are not used. -export var sawReadOnlySpans = false, sawCollapsedSpans = false; +export var sawReadOnlySpans = false, sawCollapsedSpans = false export function seeReadOnlySpans() { - sawReadOnlySpans = true; + sawReadOnlySpans = true } export function seeCollapsedSpans() { - sawCollapsedSpans = true; + sawCollapsedSpans = true } diff --git a/src/line/spans.js b/src/line/spans.js index d114d894..fd9bd554 100644 --- a/src/line/spans.js +++ b/src/line/spans.js @@ -1,34 +1,34 @@ -import { indexOf, lst } from "../util/misc"; +import { indexOf, lst } from "../util/misc" -import { cmp } from "./pos"; -import { sawCollapsedSpans } from "./saw_special_spans"; -import { getLine, isLine, lineNo } from "./utils_line"; +import { cmp } from "./pos" +import { sawCollapsedSpans } from "./saw_special_spans" +import { getLine, isLine, lineNo } from "./utils_line" // TEXTMARKER SPANS export function MarkedSpan(marker, from, to) { - this.marker = marker; - this.from = from; this.to = to; + this.marker = marker + this.from = from; this.to = to } // Search an array of spans for a span matching the given marker. export function getMarkedSpanFor(spans, marker) { if (spans) for (var i = 0; i < spans.length; ++i) { - var span = spans[i]; - if (span.marker == marker) return span; + var span = spans[i] + if (span.marker == marker) return span } } // Remove a span from an array, returning undefined if no spans are // left (we don't store arrays for lines without spans). export function removeMarkedSpan(spans, span) { for (var r, i = 0; i < spans.length; ++i) - if (spans[i] != span) (r || (r = [])).push(spans[i]); - return r; + if (spans[i] != span) (r || (r = [])).push(spans[i]) + return r } // Add a span to a line. export function addMarkedSpan(line, span) { - line.markedSpans = line.markedSpans ? line.markedSpans.concat([span]) : [span]; - span.marker.attachLine(line); + line.markedSpans = line.markedSpans ? line.markedSpans.concat([span]) : [span] + span.marker.attachLine(line) } // Used for the algorithm that adjusts markers for a change in the @@ -37,26 +37,26 @@ export function addMarkedSpan(line, span) { // undefined if nothing remains). function markedSpansBefore(old, startCh, isInsert) { 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); + var span = old[i], marker = span.marker + var startsBefore = span.from == null || (marker.inclusiveLeft ? span.from <= startCh : span.from < startCh) if (startsBefore || span.from == startCh && marker.type == "bookmark" && (!isInsert || !span.marker.insertLeft)) { - var endsAfter = span.to == null || (marker.inclusiveRight ? span.to >= startCh : span.to > startCh); - (nw || (nw = [])).push(new MarkedSpan(marker, span.from, endsAfter ? null : span.to)); + var endsAfter = span.to == null || (marker.inclusiveRight ? span.to >= startCh : span.to > startCh) + ;(nw || (nw = [])).push(new MarkedSpan(marker, span.from, endsAfter ? null : span.to)) } } - return nw; + return nw } function markedSpansAfter(old, endCh, isInsert) { 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); + var span = old[i], marker = span.marker + var endsAfter = span.to == null || (marker.inclusiveRight ? span.to >= endCh : span.to > endCh) if (endsAfter || span.from == endCh && marker.type == "bookmark" && (!isInsert || span.marker.insertLeft)) { - var startsBefore = span.from == null || (marker.inclusiveLeft ? span.from <= endCh : span.from < endCh); - (nw || (nw = [])).push(new MarkedSpan(marker, startsBefore ? null : span.from - endCh, - span.to == null ? null : span.to - endCh)); + var startsBefore = span.from == null || (marker.inclusiveLeft ? span.from <= endCh : span.from < endCh) + ;(nw || (nw = [])).push(new MarkedSpan(marker, startsBefore ? null : span.from - endCh, + span.to == null ? null : span.to - endCh)) } } - return nw; + return nw } // Given a change object, compute the new set of marker spans that @@ -66,171 +66,171 @@ function markedSpansAfter(old, endCh, isInsert) { // spans partially within the change. Returns an array of span // arrays with one element for each line in (after) the change. export function stretchSpansOverChange(doc, change) { - if (change.full) return null; - 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; + if (change.full) return null + 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, isInsert = cmp(change.from, change.to) == 0; + var startCh = change.from.ch, endCh = change.to.ch, isInsert = cmp(change.from, change.to) == 0 // Get the spans that 'stick out' on both sides - var first = markedSpansBefore(oldFirst, startCh, isInsert); - var last = markedSpansAfter(oldLast, endCh, isInsert); + var first = markedSpansBefore(oldFirst, startCh, isInsert) + var last = markedSpansAfter(oldLast, endCh, isInsert) // Next, merge those two ends - var sameLine = change.text.length == 1, offset = lst(change.text).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) { - var span = first[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; + 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; + var span = last[i] + if (span.to != null) span.to += offset if (span.from == null) { - var found = getMarkedSpanFor(first, span.marker); + var found = getMarkedSpanFor(first, span.marker) if (!found) { - span.from = offset; - if (sameLine) (first || (first = [])).push(span); + span.from = offset + if (sameLine) (first || (first = [])).push(span) } } else { - span.from += offset; - if (sameLine) (first || (first = [])).push(span); + span.from += offset + if (sameLine) (first || (first = [])).push(span) } } } // Make sure we didn't create any zero-length spans - if (first) first = clearEmptySpans(first); - if (last && last != first) last = clearEmptySpans(last); + if (first) first = clearEmptySpans(first) + if (last && last != first) last = clearEmptySpans(last) - var newMarkers = [first]; + var newMarkers = [first] if (!sameLine) { // Fill gap with whole-line-spans - var gap = change.text.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(new MarkedSpan(first[i].marker, null, null)); + (gapMarkers || (gapMarkers = [])).push(new MarkedSpan(first[i].marker, null, null)) for (var i = 0; i < gap; ++i) - newMarkers.push(gapMarkers); - newMarkers.push(last); + newMarkers.push(gapMarkers) + newMarkers.push(last) } - return newMarkers; + return newMarkers } // Remove spans that are empty and don't have a clearWhenEmpty // option of false. function clearEmptySpans(spans) { for (var i = 0; i < spans.length; ++i) { - var span = spans[i]; + var span = spans[i] if (span.from != null && span.from == span.to && span.marker.clearWhenEmpty !== false) - spans.splice(i--, 1); + spans.splice(i--, 1) } - if (!spans.length) return null; - return spans; + if (!spans.length) return null + return spans } // Used to 'clip' out readOnly ranges when making a change. export function removeReadOnlyRanges(doc, from, to) { - var markers = null; + var markers = null doc.iter(from.line, to.line + 1, function(line) { if (line.markedSpans) for (var i = 0; i < line.markedSpans.length; ++i) { - var mark = line.markedSpans[i].marker; + var mark = line.markedSpans[i].marker if (mark.readOnly && (!markers || indexOf(markers, mark) == -1)) - (markers || (markers = [])).push(mark); + (markers || (markers = [])).push(mark) } - }); - if (!markers) return null; - var parts = [{from: from, to: to}]; + }) + if (!markers) return null + var parts = [{from: from, to: to}] for (var i = 0; i < markers.length; ++i) { - var mk = markers[i], m = mk.find(0); + var mk = markers[i], m = mk.find(0) for (var j = 0; j < parts.length; ++j) { - var p = parts[j]; - if (cmp(p.to, m.from) < 0 || cmp(p.from, m.to) > 0) continue; - var newParts = [j, 1], dfrom = cmp(p.from, m.from), dto = cmp(p.to, m.to); + var p = parts[j] + if (cmp(p.to, m.from) < 0 || cmp(p.from, m.to) > 0) continue + var newParts = [j, 1], dfrom = cmp(p.from, m.from), dto = cmp(p.to, m.to) if (dfrom < 0 || !mk.inclusiveLeft && !dfrom) - newParts.push({from: p.from, to: m.from}); + newParts.push({from: p.from, to: m.from}) if (dto > 0 || !mk.inclusiveRight && !dto) - newParts.push({from: m.to, to: p.to}); - parts.splice.apply(parts, newParts); - j += newParts.length - 1; + newParts.push({from: m.to, to: p.to}) + parts.splice.apply(parts, newParts) + j += newParts.length - 1 } } - return parts; + return parts } // Connect or disconnect spans from a line. export function detachMarkedSpans(line) { - var spans = line.markedSpans; - if (!spans) return; + var spans = line.markedSpans + if (!spans) return for (var i = 0; i < spans.length; ++i) - spans[i].marker.detachLine(line); - line.markedSpans = null; + spans[i].marker.detachLine(line) + line.markedSpans = null } export function attachMarkedSpans(line, spans) { - if (!spans) return; + if (!spans) return for (var i = 0; i < spans.length; ++i) - spans[i].marker.attachLine(line); - line.markedSpans = spans; + spans[i].marker.attachLine(line) + line.markedSpans = spans } // Helpers used when computing which overlapping collapsed span // counts as the larger one. -function extraLeft(marker) { return marker.inclusiveLeft ? -1 : 0; } -function extraRight(marker) { return marker.inclusiveRight ? 1 : 0; } +function extraLeft(marker) { return marker.inclusiveLeft ? -1 : 0 } +function extraRight(marker) { return marker.inclusiveRight ? 1 : 0 } // Returns a number indicating which of two overlapping collapsed // spans is larger (and thus includes the other). Falls back to // comparing ids when the spans cover exactly the same range. export function compareCollapsedMarkers(a, b) { - var lenDiff = a.lines.length - b.lines.length; - if (lenDiff != 0) return lenDiff; - var aPos = a.find(), bPos = b.find(); - var fromCmp = cmp(aPos.from, bPos.from) || extraLeft(a) - extraLeft(b); - if (fromCmp) return -fromCmp; - var toCmp = cmp(aPos.to, bPos.to) || extraRight(a) - extraRight(b); - if (toCmp) return toCmp; - return b.id - a.id; + var lenDiff = a.lines.length - b.lines.length + if (lenDiff != 0) return lenDiff + var aPos = a.find(), bPos = b.find() + var fromCmp = cmp(aPos.from, bPos.from) || extraLeft(a) - extraLeft(b) + if (fromCmp) return -fromCmp + var toCmp = cmp(aPos.to, bPos.to) || extraRight(a) - extraRight(b) + if (toCmp) return toCmp + return b.id - a.id } // Find out whether a line ends or starts in a collapsed span. If // so, return the marker for that span. function collapsedSpanAtSide(line, start) { - var sps = sawCollapsedSpans && line.markedSpans, found; + var sps = sawCollapsedSpans && line.markedSpans, found if (sps) for (var sp, i = 0; i < sps.length; ++i) { - sp = sps[i]; + sp = sps[i] if (sp.marker.collapsed && (start ? sp.from : sp.to) == null && (!found || compareCollapsedMarkers(found, sp.marker) < 0)) - found = sp.marker; + found = sp.marker } - return found; + return found } -export function collapsedSpanAtStart(line) { return collapsedSpanAtSide(line, true); } -export function collapsedSpanAtEnd(line) { return collapsedSpanAtSide(line, false); } +export function collapsedSpanAtStart(line) { return collapsedSpanAtSide(line, true) } +export function collapsedSpanAtEnd(line) { return collapsedSpanAtSide(line, false) } // Test whether there exists a collapsed span that partially // overlaps (covers the start or end, but not both) of a new span. // Such overlap is not allowed. export function conflictingCollapsedRange(doc, lineNo, from, to, marker) { - var line = getLine(doc, lineNo); - var sps = sawCollapsedSpans && line.markedSpans; + var line = getLine(doc, lineNo) + var sps = sawCollapsedSpans && line.markedSpans if (sps) for (var i = 0; i < sps.length; ++i) { - var sp = sps[i]; - if (!sp.marker.collapsed) continue; - var found = sp.marker.find(0); - var fromCmp = cmp(found.from, from) || extraLeft(sp.marker) - extraLeft(marker); - var toCmp = cmp(found.to, to) || extraRight(sp.marker) - extraRight(marker); - if (fromCmp >= 0 && toCmp <= 0 || fromCmp <= 0 && toCmp >= 0) continue; + var sp = sps[i] + if (!sp.marker.collapsed) continue + var found = sp.marker.find(0) + var fromCmp = cmp(found.from, from) || extraLeft(sp.marker) - extraLeft(marker) + var toCmp = cmp(found.to, to) || extraRight(sp.marker) - extraRight(marker) + if (fromCmp >= 0 && toCmp <= 0 || fromCmp <= 0 && toCmp >= 0) continue if (fromCmp <= 0 && (sp.marker.inclusiveRight && marker.inclusiveLeft ? cmp(found.to, from) >= 0 : cmp(found.to, from) > 0) || fromCmp >= 0 && (sp.marker.inclusiveRight && marker.inclusiveLeft ? cmp(found.from, to) <= 0 : cmp(found.from, to) < 0)) - return true; + return true } } @@ -239,124 +239,124 @@ export function conflictingCollapsedRange(doc, lineNo, from, to, marker) { // visual line. This finds the start of the visual line that the // given line is part of (usually that is the line itself). export function visualLine(line) { - var merged; + var merged while (merged = collapsedSpanAtStart(line)) - line = merged.find(-1, true).line; - return line; + line = merged.find(-1, true).line + return line } // Returns an array of logical lines that continue the visual line // started by the argument, or undefined if there are no such lines. export function visualLineContinued(line) { - var merged, lines; + var merged, lines while (merged = collapsedSpanAtEnd(line)) { - line = merged.find(1, true).line; - (lines || (lines = [])).push(line); + line = merged.find(1, true).line + ;(lines || (lines = [])).push(line) } - return lines; + return lines } // Get the line number of the start of the visual line that the // given line number is part of. export function visualLineNo(doc, lineN) { - var line = getLine(doc, lineN), vis = visualLine(line); - if (line == vis) return lineN; - return lineNo(vis); + var line = getLine(doc, lineN), vis = visualLine(line) + if (line == vis) return lineN + return lineNo(vis) } // Get the line number of the start of the next visual line after // the given line. export function visualLineEndNo(doc, lineN) { - if (lineN > doc.lastLine()) return lineN; - var line = getLine(doc, lineN), merged; - if (!lineIsHidden(doc, line)) return lineN; + if (lineN > doc.lastLine()) return lineN + var line = getLine(doc, lineN), merged + if (!lineIsHidden(doc, line)) return lineN while (merged = collapsedSpanAtEnd(line)) - line = merged.find(1, true).line; - return lineNo(line) + 1; + line = merged.find(1, true).line + return lineNo(line) + 1 } // Compute whether a line is hidden. Lines count as hidden when they // are part of a visual line that starts with another line, or when // they are entirely covered by collapsed, non-widget span. export function lineIsHidden(doc, line) { - var sps = sawCollapsedSpans && line.markedSpans; + var sps = sawCollapsedSpans && line.markedSpans if (sps) for (var sp, i = 0; i < sps.length; ++i) { - sp = sps[i]; - if (!sp.marker.collapsed) continue; - if (sp.from == null) return true; - if (sp.marker.widgetNode) continue; + sp = sps[i] + if (!sp.marker.collapsed) continue + if (sp.from == null) return true + if (sp.marker.widgetNode) continue if (sp.from == 0 && sp.marker.inclusiveLeft && lineIsHiddenInner(doc, line, sp)) - return true; + return true } } function lineIsHiddenInner(doc, line, span) { if (span.to == null) { - var end = span.marker.find(1, true); - return lineIsHiddenInner(doc, end.line, getMarkedSpanFor(end.line.markedSpans, span.marker)); + var end = span.marker.find(1, true) + return lineIsHiddenInner(doc, end.line, getMarkedSpanFor(end.line.markedSpans, span.marker)) } if (span.marker.inclusiveRight && span.to == line.text.length) - return true; + return true for (var sp, i = 0; i < line.markedSpans.length; ++i) { - sp = line.markedSpans[i]; + sp = line.markedSpans[i] if (sp.marker.collapsed && !sp.marker.widgetNode && sp.from == span.to && (sp.to == null || sp.to != span.from) && (sp.marker.inclusiveLeft || span.marker.inclusiveRight) && - lineIsHiddenInner(doc, line, sp)) return true; + lineIsHiddenInner(doc, line, sp)) return true } } // Find the height above the given line. export function heightAtLine(lineObj) { - lineObj = visualLine(lineObj); + lineObj = visualLine(lineObj) - var h = 0, chunk = lineObj.parent; + var h = 0, chunk = lineObj.parent for (var i = 0; i < chunk.lines.length; ++i) { - var line = chunk.lines[i]; - if (line == lineObj) break; - else h += line.height; + var line = chunk.lines[i] + if (line == lineObj) break + else h += line.height } for (var p = chunk.parent; p; chunk = p, p = chunk.parent) { for (var i = 0; i < p.children.length; ++i) { - var cur = p.children[i]; - if (cur == chunk) break; - else h += cur.height; + var cur = p.children[i] + if (cur == chunk) break + else h += cur.height } } - return h; + return h } // Compute the character length of a line, taking into account // collapsed ranges (see markText) that might hide parts, and join // other lines onto it. export function lineLength(line) { - if (line.height == 0) return 0; - var len = line.text.length, merged, cur = line; + if (line.height == 0) return 0 + var len = line.text.length, merged, cur = line while (merged = collapsedSpanAtStart(cur)) { - var found = merged.find(0, true); - cur = found.from.line; - len += found.from.ch - found.to.ch; + var found = merged.find(0, true) + cur = found.from.line + len += found.from.ch - found.to.ch } - cur = line; + cur = line while (merged = collapsedSpanAtEnd(cur)) { - var found = merged.find(0, true); - len -= cur.text.length - found.from.ch; - cur = found.to.line; - len += cur.text.length - found.to.ch; + var found = merged.find(0, true) + len -= cur.text.length - found.from.ch + cur = found.to.line + len += cur.text.length - found.to.ch } - return len; + return len } // Find the longest line in the document. export function findMaxLine(cm) { - var d = cm.display, doc = cm.doc; - d.maxLine = getLine(doc, doc.first); - d.maxLineLength = lineLength(d.maxLine); - d.maxLineChanged = true; + var d = cm.display, doc = cm.doc + d.maxLine = getLine(doc, doc.first) + d.maxLineLength = lineLength(d.maxLine) + d.maxLineChanged = true doc.iter(function(line) { - var len = lineLength(line); + var len = lineLength(line) if (len > d.maxLineLength) { - d.maxLineLength = len; - d.maxLine = line; + d.maxLineLength = len + d.maxLine = line } - }); + }) } diff --git a/src/line/utils_line.js b/src/line/utils_line.js index b194ff38..4e10a1d8 100644 --- a/src/line/utils_line.js +++ b/src/line/utils_line.js @@ -1,83 +1,83 @@ -import { indexOf } from "../util/misc"; +import { indexOf } from "../util/misc" // Find the line object corresponding to the given line number. export function getLine(doc, n) { - n -= doc.first; - if (n < 0 || n >= doc.size) throw new Error("There is no line " + (n + doc.first) + " in the document."); + n -= doc.first + if (n < 0 || n >= doc.size) throw new Error("There is no line " + (n + doc.first) + " in the document.") for (var chunk = doc; !chunk.lines;) { for (var i = 0;; ++i) { - var child = chunk.children[i], sz = child.chunkSize(); - if (n < sz) { chunk = child; break; } - n -= sz; + var child = chunk.children[i], sz = child.chunkSize() + if (n < sz) { chunk = child; break } + n -= sz } } - return chunk.lines[n]; + return chunk.lines[n] } // Get the part of a document between two positions, as an array of // strings. export function getBetween(doc, start, end) { - var out = [], n = start.line; + 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; + 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 } // Get the lines between from and to, as array of strings. export function getLines(doc, from, to) { - var out = []; - doc.iter(from, to, function(line) { out.push(line.text); }); - return out; + var out = [] + doc.iter(from, to, function(line) { out.push(line.text) }) + return out } // Update the height of a line, propagating the height change // upwards to parent nodes. export function updateLineHeight(line, height) { - var diff = height - line.height; - if (diff) for (var n = line; n; n = n.parent) n.height += diff; + var diff = height - line.height + if (diff) for (var n = line; n; n = n.parent) n.height += diff } // Given a line object, find its line number by walking up through // its parent links. export function lineNo(line) { - if (line.parent == null) return null; - var cur = line.parent, no = indexOf(cur.lines, line); + if (line.parent == null) return null + var cur = line.parent, no = indexOf(cur.lines, line) for (var chunk = cur.parent; chunk; cur = chunk, chunk = chunk.parent) { for (var i = 0;; ++i) { - if (chunk.children[i] == cur) break; - no += chunk.children[i].chunkSize(); + if (chunk.children[i] == cur) break + no += chunk.children[i].chunkSize() } } - return no + cur.first; + return no + cur.first } // Find the line at the given vertical position, using the height // information in the document tree. export function lineAtHeight(chunk, h) { - var n = chunk.first; + var n = chunk.first outer: do { for (var i = 0; i < chunk.children.length; ++i) { - var child = chunk.children[i], ch = child.height; - if (h < ch) { chunk = child; continue outer; } - h -= ch; - n += child.chunkSize(); + var child = chunk.children[i], ch = child.height + if (h < ch) { chunk = child; continue outer } + h -= ch + n += child.chunkSize() } - return n; - } while (!chunk.lines); + return n + } while (!chunk.lines) for (var i = 0; i < chunk.lines.length; ++i) { - var line = chunk.lines[i], lh = line.height; - if (h < lh) break; - h -= lh; + var line = chunk.lines[i], lh = line.height + if (h < lh) break + h -= lh } - return n + i; + return n + i } -export function isLine(doc, l) {return l >= doc.first && l < doc.first + doc.size;} +export function isLine(doc, l) {return l >= doc.first && l < doc.first + doc.size} export function lineNumberFor(options, i) { - return String(options.lineNumberFormatter(i + options.firstLineNumber)); + return String(options.lineNumberFormatter(i + options.firstLineNumber)) } diff --git a/src/measurement/position_measurement.js b/src/measurement/position_measurement.js index b479e250..75011a34 100644 --- a/src/measurement/position_measurement.js +++ b/src/measurement/position_measurement.js @@ -1,36 +1,36 @@ -import { buildLineContent, LineView } from "../line/line_data"; -import { clipPos, Pos } from "../line/pos"; -import { collapsedSpanAtEnd, heightAtLine, lineIsHidden, visualLine } from "../line/spans"; -import { getLine, lineAtHeight, lineNo, updateLineHeight } from "../line/utils_line"; -import { bidiLeft, bidiRight, bidiOther, getBidiPartAt, getOrder, lineLeft, lineRight, moveVisually } from "../util/bidi"; -import { ie, ie_version } from "../util/browser"; -import { elt, removeChildren, range, removeChildrenAndAdd } from "../util/dom"; -import { e_target } from "../util/event"; -import { hasBadZoomedRects } from "../util/feature_detection"; -import { countColumn, isExtendingChar, scrollerGap } from "../util/misc"; - -import { updateLineForChanges } from "./update_line"; -import { widgetHeight } from "./widgets"; +import { buildLineContent, LineView } from "../line/line_data" +import { clipPos, Pos } from "../line/pos" +import { collapsedSpanAtEnd, heightAtLine, lineIsHidden, visualLine } from "../line/spans" +import { getLine, lineAtHeight, lineNo, updateLineHeight } from "../line/utils_line" +import { bidiLeft, bidiRight, bidiOther, getBidiPartAt, getOrder, lineLeft, lineRight, moveVisually } from "../util/bidi" +import { ie, ie_version } from "../util/browser" +import { elt, removeChildren, range, removeChildrenAndAdd } from "../util/dom" +import { e_target } from "../util/event" +import { hasBadZoomedRects } from "../util/feature_detection" +import { countColumn, isExtendingChar, scrollerGap } from "../util/misc" + +import { updateLineForChanges } from "./update_line" +import { widgetHeight } from "./widgets" // POSITION MEASUREMENT -export function paddingTop(display) {return display.lineSpace.offsetTop;} -export function paddingVert(display) {return display.mover.offsetHeight - display.lineSpace.offsetHeight;} +export function paddingTop(display) {return display.lineSpace.offsetTop} +export function paddingVert(display) {return display.mover.offsetHeight - display.lineSpace.offsetHeight} export function paddingH(display) { - if (display.cachedPaddingH) return display.cachedPaddingH; - var e = removeChildrenAndAdd(display.measure, elt("pre", "x")); - var style = window.getComputedStyle ? window.getComputedStyle(e) : e.currentStyle; - var data = {left: parseInt(style.paddingLeft), right: parseInt(style.paddingRight)}; - if (!isNaN(data.left) && !isNaN(data.right)) display.cachedPaddingH = data; - return data; + if (display.cachedPaddingH) return display.cachedPaddingH + var e = removeChildrenAndAdd(display.measure, elt("pre", "x")) + var style = window.getComputedStyle ? window.getComputedStyle(e) : e.currentStyle + var data = {left: parseInt(style.paddingLeft), right: parseInt(style.paddingRight)} + if (!isNaN(data.left) && !isNaN(data.right)) display.cachedPaddingH = data + return data } -export function scrollGap(cm) { return scrollerGap - cm.display.nativeBarWidth; } +export function scrollGap(cm) { return scrollerGap - cm.display.nativeBarWidth } export function displayWidth(cm) { - return cm.display.scroller.clientWidth - scrollGap(cm) - cm.display.barWidth; + return cm.display.scroller.clientWidth - scrollGap(cm) - cm.display.barWidth } export function displayHeight(cm) { - return cm.display.scroller.clientHeight - scrollGap(cm) - cm.display.barHeight; + return cm.display.scroller.clientHeight - scrollGap(cm) - cm.display.barHeight } // Ensure the lineView.wrapping.heights array is populated. This is @@ -38,20 +38,20 @@ export function displayHeight(cm) { // line. When lineWrapping is on, there might be more than one // height. function ensureLineHeights(cm, lineView, rect) { - var wrapping = cm.options.lineWrapping; - var curWidth = wrapping && displayWidth(cm); + var wrapping = cm.options.lineWrapping + var curWidth = wrapping && displayWidth(cm) if (!lineView.measure.heights || wrapping && lineView.measure.width != curWidth) { - var heights = lineView.measure.heights = []; + var heights = lineView.measure.heights = [] if (wrapping) { - lineView.measure.width = curWidth; - var rects = lineView.text.firstChild.getClientRects(); + lineView.measure.width = curWidth + var rects = lineView.text.firstChild.getClientRects() for (var i = 0; i < rects.length - 1; i++) { - var cur = rects[i], next = rects[i + 1]; + var cur = rects[i], next = rects[i + 1] if (Math.abs(cur.bottom - next.bottom) > 2) - heights.push((cur.bottom + next.top) / 2 - rect.top); + heights.push((cur.bottom + next.top) / 2 - rect.top) } } - heights.push(rect.bottom - rect.top); + heights.push(rect.bottom - rect.top) } } @@ -60,41 +60,41 @@ function ensureLineHeights(cm, lineView, rect) { // contain multiple lines when collapsed ranges are present.) export function mapFromLineView(lineView, line, lineN) { if (lineView.line == line) - return {map: lineView.measure.map, cache: lineView.measure.cache}; + return {map: lineView.measure.map, cache: lineView.measure.cache} for (var i = 0; i < lineView.rest.length; i++) if (lineView.rest[i] == line) - return {map: lineView.measure.maps[i], cache: lineView.measure.caches[i]}; + return {map: lineView.measure.maps[i], cache: lineView.measure.caches[i]} for (var i = 0; i < lineView.rest.length; i++) if (lineNo(lineView.rest[i]) > lineN) - return {map: lineView.measure.maps[i], cache: lineView.measure.caches[i], before: true}; + return {map: lineView.measure.maps[i], cache: lineView.measure.caches[i], before: true} } // Render a line into the hidden node display.externalMeasured. Used // when measurement is needed for a line that's not in the viewport. function updateExternalMeasurement(cm, line) { - line = visualLine(line); - var lineN = lineNo(line); - var view = cm.display.externalMeasured = new LineView(cm.doc, line, lineN); - view.lineN = lineN; - var built = view.built = buildLineContent(cm, view); - view.text = built.pre; - removeChildrenAndAdd(cm.display.lineMeasure, built.pre); - return view; + line = visualLine(line) + var lineN = lineNo(line) + var view = cm.display.externalMeasured = new LineView(cm.doc, line, lineN) + view.lineN = lineN + var built = view.built = buildLineContent(cm, view) + view.text = built.pre + removeChildrenAndAdd(cm.display.lineMeasure, built.pre) + return view } // Get a {top, bottom, left, right} box (in line-local coordinates) // for a given character. export function measureChar(cm, line, ch, bias) { - return measureCharPrepared(cm, prepareMeasureForLine(cm, line), ch, bias); + return measureCharPrepared(cm, prepareMeasureForLine(cm, line), ch, bias) } // Find a line view that corresponds to the given line number. export function findViewForLine(cm, lineN) { if (lineN >= cm.display.viewFrom && lineN < cm.display.viewTo) - return cm.display.view[findViewIndex(cm, lineN)]; - var ext = cm.display.externalMeasured; + return cm.display.view[findViewIndex(cm, lineN)] + var ext = cm.display.externalMeasured if (ext && lineN >= ext.lineN && lineN < ext.lineN + ext.size) - return ext; + return ext } // Measurement can be split in two steps, the set-up work that @@ -103,84 +103,84 @@ export function findViewForLine(cm, lineN) { // measurements in a row, can thus ensure that the set-up work is // only done once. function prepareMeasureForLine(cm, line) { - var lineN = lineNo(line); - var view = findViewForLine(cm, lineN); + var lineN = lineNo(line) + var view = findViewForLine(cm, lineN) if (view && !view.text) { - view = null; + view = null } else if (view && view.changes) { - updateLineForChanges(cm, view, lineN, getDimensions(cm)); - cm.curOp.forceUpdate = true; + updateLineForChanges(cm, view, lineN, getDimensions(cm)) + cm.curOp.forceUpdate = true } if (!view) - view = updateExternalMeasurement(cm, line); + view = updateExternalMeasurement(cm, line) - var info = mapFromLineView(view, line, lineN); + var info = mapFromLineView(view, line, lineN) return { line: line, view: view, rect: null, map: info.map, cache: info.cache, before: info.before, hasHeights: false - }; + } } // Given a prepared measurement object, measures the position of an // actual character (or fetches it from the cache). function measureCharPrepared(cm, prepared, ch, bias, varHeight) { - if (prepared.before) ch = -1; - var key = ch + (bias || ""), found; + if (prepared.before) ch = -1 + var key = ch + (bias || ""), found if (prepared.cache.hasOwnProperty(key)) { - found = prepared.cache[key]; + found = prepared.cache[key] } else { if (!prepared.rect) - prepared.rect = prepared.view.text.getBoundingClientRect(); + prepared.rect = prepared.view.text.getBoundingClientRect() if (!prepared.hasHeights) { - ensureLineHeights(cm, prepared.view, prepared.rect); - prepared.hasHeights = true; + ensureLineHeights(cm, prepared.view, prepared.rect) + prepared.hasHeights = true } - found = measureCharInner(cm, prepared, ch, bias); - if (!found.bogus) prepared.cache[key] = found; + found = measureCharInner(cm, prepared, ch, bias) + if (!found.bogus) prepared.cache[key] = found } return {left: found.left, right: found.right, top: varHeight ? found.rtop : found.top, - bottom: varHeight ? found.rbottom : found.bottom}; + bottom: varHeight ? found.rbottom : found.bottom} } -var nullRect = {left: 0, right: 0, top: 0, bottom: 0}; +var nullRect = {left: 0, right: 0, top: 0, bottom: 0} export function nodeAndOffsetInLineMap(map, ch, bias) { - var node, start, end, collapse; + var node, start, end, collapse // First, search the line map for the text node corresponding to, // or closest to, the target character. for (var i = 0; i < map.length; i += 3) { - var mStart = map[i], mEnd = map[i + 1]; + var mStart = map[i], mEnd = map[i + 1] if (ch < mStart) { - start = 0; end = 1; - collapse = "left"; + start = 0; end = 1 + collapse = "left" } else if (ch < mEnd) { - start = ch - mStart; - end = start + 1; + start = ch - mStart + end = start + 1 } else if (i == map.length - 3 || ch == mEnd && map[i + 3] > ch) { - end = mEnd - mStart; - start = end - 1; - if (ch >= mEnd) collapse = "right"; + end = mEnd - mStart + start = end - 1 + if (ch >= mEnd) collapse = "right" } if (start != null) { - node = map[i + 2]; + node = map[i + 2] if (mStart == mEnd && bias == (node.insertLeft ? "left" : "right")) - collapse = bias; + collapse = bias if (bias == "left" && start == 0) while (i && map[i - 2] == map[i - 3] && map[i - 1].insertLeft) { - node = map[(i -= 3) + 2]; - collapse = "left"; + node = map[(i -= 3) + 2] + collapse = "left" } if (bias == "right" && start == mEnd - mStart) while (i < map.length - 3 && map[i + 3] == map[i + 4] && !map[i + 5].insertLeft) { - node = map[(i += 3) + 2]; - collapse = "right"; + node = map[(i += 3) + 2] + collapse = "right" } - break; + break } } - return {node: node, start: start, end: end, collapse: collapse, coverStart: mStart, coverEnd: mEnd}; + return {node: node, start: start, end: end, collapse: collapse, coverStart: mStart, coverEnd: mEnd} } function getUsefulRect(rects, bias) { @@ -194,53 +194,53 @@ function getUsefulRect(rects, bias) { } function measureCharInner(cm, prepared, ch, bias) { - var place = nodeAndOffsetInLineMap(prepared.map, ch, bias); - var node = place.node, start = place.start, end = place.end, collapse = place.collapse; + var place = nodeAndOffsetInLineMap(prepared.map, ch, bias) + var node = place.node, start = place.start, end = place.end, collapse = place.collapse - var rect; + var rect if (node.nodeType == 3) { // If it is a text node, use a range to retrieve the coordinates. for (var i = 0; i < 4; i++) { // Retry a maximum of 4 times when nonsense rectangles are returned - while (start && isExtendingChar(prepared.line.text.charAt(place.coverStart + start))) --start; - while (place.coverStart + end < place.coverEnd && isExtendingChar(prepared.line.text.charAt(place.coverStart + end))) ++end; + while (start && isExtendingChar(prepared.line.text.charAt(place.coverStart + start))) --start + while (place.coverStart + end < place.coverEnd && isExtendingChar(prepared.line.text.charAt(place.coverStart + end))) ++end if (ie && ie_version < 9 && start == 0 && end == place.coverEnd - place.coverStart) - rect = node.parentNode.getBoundingClientRect(); + rect = node.parentNode.getBoundingClientRect() else rect = getUsefulRect(range(node, start, end).getClientRects(), bias) - if (rect.left || rect.right || start == 0) break; - end = start; - start = start - 1; - collapse = "right"; + if (rect.left || rect.right || start == 0) break + end = start + start = start - 1 + collapse = "right" } - if (ie && ie_version < 11) rect = maybeUpdateRectForZooming(cm.display.measure, rect); + 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; + if (start > 0) collapse = bias = "right" + var rects if (cm.options.lineWrapping && (rects = node.getClientRects()).length > 1) - rect = rects[bias == "right" ? rects.length - 1 : 0]; + rect = rects[bias == "right" ? rects.length - 1 : 0] else - rect = node.getBoundingClientRect(); + rect = node.getBoundingClientRect() } if (ie && ie_version < 9 && !start && (!rect || !rect.left && !rect.right)) { - var rSpan = node.parentNode.getClientRects()[0]; + var rSpan = node.parentNode.getClientRects()[0] if (rSpan) - rect = {left: rSpan.left, right: rSpan.left + charWidth(cm.display), top: rSpan.top, bottom: rSpan.bottom}; + rect = {left: rSpan.left, right: rSpan.left + charWidth(cm.display), top: rSpan.top, bottom: rSpan.bottom} else - rect = nullRect; + rect = nullRect } - var rtop = rect.top - prepared.rect.top, rbot = rect.bottom - prepared.rect.top; - var mid = (rtop + rbot) / 2; - var heights = prepared.view.measure.heights; + var rtop = rect.top - prepared.rect.top, rbot = rect.bottom - prepared.rect.top + var mid = (rtop + rbot) / 2 + var heights = prepared.view.measure.heights for (var i = 0; i < heights.length - 1; i++) - if (mid < heights[i]) break; - var top = i ? heights[i - 1] : 0, bot = heights[i]; + if (mid < heights[i]) break + var top = i ? heights[i - 1] : 0, bot = heights[i] var result = {left: (collapse == "right" ? rect.right : rect.left) - prepared.rect.left, right: (collapse == "left" ? rect.left : rect.right) - prepared.rect.left, - top: top, bottom: bot}; - if (!rect.left && !rect.right) result.bogus = true; - if (!cm.options.singleCursorHeightPerLine) { result.rtop = rtop; result.rbottom = rbot; } + top: top, bottom: bot} + if (!rect.left && !rect.right) result.bogus = true + if (!cm.options.singleCursorHeightPerLine) { result.rtop = rtop; result.rbottom = rbot } - return result; + return result } // Work around problem with bounding client rects on ranges being @@ -248,38 +248,38 @@ function measureCharInner(cm, prepared, ch, bias) { 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 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}; + top: rect.top * scaleY, bottom: rect.bottom * scaleY} } export function clearLineMeasurementCacheFor(lineView) { if (lineView.measure) { - lineView.measure.cache = {}; - lineView.measure.heights = null; + lineView.measure.cache = {} + lineView.measure.heights = null if (lineView.rest) for (var i = 0; i < lineView.rest.length; i++) - lineView.measure.caches[i] = {}; + lineView.measure.caches[i] = {} } } export function clearLineMeasurementCache(cm) { - cm.display.externalMeasure = null; - removeChildren(cm.display.lineMeasure); + cm.display.externalMeasure = null + removeChildren(cm.display.lineMeasure) for (var i = 0; i < cm.display.view.length; i++) - clearLineMeasurementCacheFor(cm.display.view[i]); + clearLineMeasurementCacheFor(cm.display.view[i]) } export function clearCaches(cm) { - clearLineMeasurementCache(cm); - cm.display.cachedCharWidth = cm.display.cachedTextHeight = cm.display.cachedPaddingH = null; - if (!cm.options.lineWrapping) cm.display.maxLineChanged = true; - cm.display.lineNumChars = null; + clearLineMeasurementCache(cm) + cm.display.cachedCharWidth = cm.display.cachedTextHeight = cm.display.cachedPaddingH = null + if (!cm.options.lineWrapping) cm.display.maxLineChanged = true + cm.display.lineNumChars = null } -function pageScrollX() { return window.pageXOffset || (document.documentElement || document.body).scrollLeft; } -function pageScrollY() { return window.pageYOffset || (document.documentElement || document.body).scrollTop; } +function pageScrollX() { return window.pageXOffset || (document.documentElement || document.body).scrollLeft } +function pageScrollY() { return window.pageYOffset || (document.documentElement || document.body).scrollTop } // Converts a {top, bottom, left, right} box from line-local // coordinates into another coordinate system. Context may be one of @@ -287,89 +287,89 @@ function pageScrollY() { return window.pageYOffset || (document.documentElement // or "page". export 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 = widgetHeight(lineObj.widgets[i]); - rect.top += size; rect.bottom += size; + var size = widgetHeight(lineObj.widgets[i]) + rect.top += size; rect.bottom += size } - if (context == "line") return rect; - if (!context) context = "local"; - var yOff = heightAtLine(lineObj); - if (context == "local") yOff += paddingTop(cm.display); - else yOff -= cm.display.viewOffset; + if (context == "line") return rect + if (!context) context = "local" + var yOff = heightAtLine(lineObj) + if (context == "local") yOff += paddingTop(cm.display) + else yOff -= cm.display.viewOffset if (context == "page" || context == "window") { - var lOff = cm.display.lineSpace.getBoundingClientRect(); - yOff += lOff.top + (context == "window" ? 0 : pageScrollY()); - var xOff = lOff.left + (context == "window" ? 0 : pageScrollX()); - rect.left += xOff; rect.right += xOff; + var lOff = cm.display.lineSpace.getBoundingClientRect() + 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; - return rect; + rect.top += yOff; rect.bottom += yOff + return rect } // Coverts a box from "div" coords to another coordinate system. // Context may be "window", "page", "div", or "local"./null. export function fromCoordSystem(cm, coords, context) { - if (context == "div") return coords; - var left = coords.left, top = coords.top; + if (context == "div") return coords + var left = coords.left, top = coords.top // First move into "page" coordinate system if (context == "page") { - left -= pageScrollX(); - top -= pageScrollY(); + left -= pageScrollX() + top -= pageScrollY() } else if (context == "local" || !context) { - var localBox = cm.display.sizer.getBoundingClientRect(); - left += localBox.left; - top += localBox.top; + var localBox = cm.display.sizer.getBoundingClientRect() + left += localBox.left + top += localBox.top } - var lineSpaceBox = cm.display.lineSpace.getBoundingClientRect(); - return {left: left - lineSpaceBox.left, top: top - lineSpaceBox.top}; + var lineSpaceBox = cm.display.lineSpace.getBoundingClientRect() + return {left: left - lineSpaceBox.left, top: top - lineSpaceBox.top} } export function charCoords(cm, pos, context, lineObj, bias) { - if (!lineObj) lineObj = getLine(cm.doc, pos.line); - return intoCoordSystem(cm, lineObj, measureChar(cm, lineObj, pos.ch, bias), context); + if (!lineObj) lineObj = getLine(cm.doc, pos.line) + return intoCoordSystem(cm, lineObj, measureChar(cm, lineObj, pos.ch, bias), context) } // Returns a box for a given cursor position, which may have an // 'other' property containing the position of the secondary cursor // on a bidi boundary. export function cursorCoords(cm, pos, context, lineObj, preparedMeasure, varHeight) { - lineObj = lineObj || getLine(cm.doc, pos.line); - if (!preparedMeasure) preparedMeasure = prepareMeasureForLine(cm, lineObj); + lineObj = lineObj || getLine(cm.doc, pos.line) + if (!preparedMeasure) preparedMeasure = prepareMeasureForLine(cm, lineObj) function get(ch, right) { - var m = measureCharPrepared(cm, preparedMeasure, ch, right ? "right" : "left", varHeight); - if (right) m.left = m.right; else m.right = m.left; - return intoCoordSystem(cm, lineObj, m, context); + var m = measureCharPrepared(cm, preparedMeasure, ch, right ? "right" : "left", varHeight) + if (right) m.left = m.right; else m.right = m.left + return intoCoordSystem(cm, lineObj, m, context) } function getBidi(ch, partPos) { - var part = order[partPos], right = part.level % 2; + var part = order[partPos], right = part.level % 2 if (ch == bidiLeft(part) && partPos && part.level < order[partPos - 1].level) { - part = order[--partPos]; - ch = bidiRight(part) - (part.level % 2 ? 0 : 1); - right = true; + part = order[--partPos] + ch = bidiRight(part) - (part.level % 2 ? 0 : 1) + right = true } else if (ch == bidiRight(part) && partPos < order.length - 1 && part.level < order[partPos + 1].level) { - part = order[++partPos]; - ch = bidiLeft(part) - part.level % 2; - right = false; + part = order[++partPos] + ch = bidiLeft(part) - part.level % 2 + right = false } - if (right && ch == part.to && ch > part.from) return get(ch - 1); - return get(ch, right); + if (right && ch == part.to && ch > part.from) return get(ch - 1) + return get(ch, right) } - var order = getOrder(lineObj), ch = pos.ch; - if (!order) return get(ch); - var partPos = getBidiPartAt(order, ch); - var val = getBidi(ch, partPos); - if (bidiOther != null) val.other = getBidi(ch, bidiOther); - return val; + var order = getOrder(lineObj), ch = pos.ch + if (!order) return get(ch) + var partPos = getBidiPartAt(order, ch) + var val = getBidi(ch, partPos) + if (bidiOther != null) val.other = getBidi(ch, bidiOther) + return val } // Used to cheaply estimate the coordinates for a position. Used for // intermediate scroll updates. export function estimateCoords(cm, pos) { - var left = 0, pos = clipPos(cm.doc, pos); - if (!cm.options.lineWrapping) left = charWidth(cm.display) * pos.ch; - var lineObj = getLine(cm.doc, pos.line); - var top = heightAtLine(lineObj) + paddingTop(cm.display); - return {left: left, right: left, top: top, bottom: top + lineObj.height}; + var left = 0, pos = clipPos(cm.doc, pos) + if (!cm.options.lineWrapping) left = charWidth(cm.display) * pos.ch + var lineObj = getLine(cm.doc, pos.line) + var top = heightAtLine(lineObj) + paddingTop(cm.display) + return {left: left, right: left, top: top, bottom: top + lineObj.height} } // Positions returned by coordsChar contain some extra information. @@ -379,170 +379,170 @@ export function estimateCoords(cm, pos) { // is true, that means the coordinates lie outside the line's // vertical range. function PosWithInfo(line, ch, outside, xRel) { - var pos = Pos(line, ch); - pos.xRel = xRel; - if (outside) pos.outside = true; - return pos; + var pos = Pos(line, ch) + pos.xRel = xRel + if (outside) pos.outside = true + return pos } // Compute the character position closest to the given coordinates. // Input must be lineSpace-local ("div" coordinate system). export function coordsChar(cm, x, y) { - var doc = cm.doc; - y += cm.display.viewOffset; - if (y < 0) return PosWithInfo(doc.first, 0, true, -1); - var lineN = lineAtHeight(doc, y), last = doc.first + doc.size - 1; + var doc = cm.doc + y += cm.display.viewOffset + if (y < 0) return PosWithInfo(doc.first, 0, true, -1) + var lineN = lineAtHeight(doc, y), last = doc.first + doc.size - 1 if (lineN > last) - return PosWithInfo(doc.first + doc.size - 1, getLine(doc, last).text.length, true, 1); - if (x < 0) x = 0; + return PosWithInfo(doc.first + doc.size - 1, getLine(doc, last).text.length, true, 1) + if (x < 0) x = 0 - var lineObj = getLine(doc, lineN); + var lineObj = getLine(doc, lineN) for (;;) { - var found = coordsCharInner(cm, lineObj, lineN, x, y); - var merged = collapsedSpanAtEnd(lineObj); - var mergedPos = merged && merged.find(0, true); + var found = coordsCharInner(cm, lineObj, lineN, x, y) + var merged = collapsedSpanAtEnd(lineObj) + var mergedPos = merged && merged.find(0, true) if (merged && (found.ch > mergedPos.from.ch || found.ch == mergedPos.from.ch && found.xRel > 0)) - lineN = lineNo(lineObj = mergedPos.to.line); + lineN = lineNo(lineObj = mergedPos.to.line) else - return found; + return found } } function coordsCharInner(cm, lineObj, lineNo, x, y) { - var innerOff = y - heightAtLine(lineObj); - var wrongLine = false, adjust = 2 * cm.display.wrapper.clientWidth; - var preparedMeasure = prepareMeasureForLine(cm, lineObj); + var innerOff = y - heightAtLine(lineObj) + var wrongLine = false, adjust = 2 * cm.display.wrapper.clientWidth + var preparedMeasure = prepareMeasureForLine(cm, lineObj) function getX(ch) { - var sp = cursorCoords(cm, Pos(lineNo, ch), "line", lineObj, preparedMeasure); - wrongLine = true; - if (innerOff > sp.bottom) return sp.left - adjust; - else if (innerOff < sp.top) return sp.left + adjust; - else wrongLine = false; - return sp.left; + var sp = cursorCoords(cm, Pos(lineNo, ch), "line", lineObj, preparedMeasure) + wrongLine = true + if (innerOff > sp.bottom) return sp.left - adjust + else if (innerOff < sp.top) return sp.left + adjust + else wrongLine = false + return sp.left } - var bidi = getOrder(lineObj), dist = lineObj.text.length; - var from = lineLeft(lineObj), to = lineRight(lineObj); - var fromX = getX(from), fromOutside = wrongLine, toX = getX(to), toOutside = wrongLine; + var bidi = getOrder(lineObj), dist = lineObj.text.length + var from = lineLeft(lineObj), to = lineRight(lineObj) + var fromX = getX(from), fromOutside = wrongLine, toX = getX(to), toOutside = wrongLine - if (x > toX) return PosWithInfo(lineNo, to, toOutside, 1); + if (x > toX) return PosWithInfo(lineNo, to, toOutside, 1) // Do a binary search between these bounds. for (;;) { if (bidi ? to == from || to == moveVisually(lineObj, from, 1) : to - from <= 1) { - var ch = x < fromX || x - fromX <= toX - x ? from : to; + var ch = x < fromX || x - fromX <= toX - x ? from : to var outside = ch == from ? fromOutside : toOutside - var xDiff = x - (ch == from ? fromX : toX); + var xDiff = x - (ch == from ? fromX : toX) // This is a kludge to handle the case where the coordinates // are after a line-wrapped line. We should replace it with a // more general handling of cursor positions around line // breaks. (Issue #4078) if (toOutside && !bidi && !/\s/.test(lineObj.text.charAt(ch)) && xDiff > 0 && ch < lineObj.text.length && preparedMeasure.view.measure.heights.length > 1) { - var charSize = measureCharPrepared(cm, preparedMeasure, ch, "right"); + var charSize = measureCharPrepared(cm, preparedMeasure, ch, "right") if (innerOff <= charSize.bottom && innerOff >= charSize.top && Math.abs(x - charSize.right) < xDiff) { outside = false ch++ xDiff = x - charSize.right } } - while (isExtendingChar(lineObj.text.charAt(ch))) ++ch; - var pos = PosWithInfo(lineNo, ch, outside, xDiff < -1 ? -1 : xDiff > 1 ? 1 : 0); - return pos; + while (isExtendingChar(lineObj.text.charAt(ch))) ++ch + var pos = PosWithInfo(lineNo, ch, outside, xDiff < -1 ? -1 : xDiff > 1 ? 1 : 0) + return pos } - var step = Math.ceil(dist / 2), middle = from + step; + 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); + 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 (toOutside = wrongLine) toX += 1000; dist = step;} - else {from = middle; fromX = middleX; fromOutside = wrongLine; dist -= step;} + 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} } } -var measureText; +var measureText // Compute the default text height. export function textHeight(display) { - if (display.cachedTextHeight != null) return display.cachedTextHeight; + if (display.cachedTextHeight != null) return display.cachedTextHeight if (measureText == null) { - measureText = elt("pre"); + 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")) + measureText.appendChild(elt("br")) } - measureText.appendChild(document.createTextNode("x")); + 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; + removeChildrenAndAdd(display.measure, measureText) + var height = measureText.offsetHeight / 50 + if (height > 3) display.cachedTextHeight = height + removeChildren(display.measure) + return height || 1 } // Compute the default character width. export function charWidth(display) { - if (display.cachedCharWidth != null) return display.cachedCharWidth; - var anchor = elt("span", "xxxxxxxxxx"); - var pre = elt("pre", [anchor]); - removeChildrenAndAdd(display.measure, pre); - var rect = anchor.getBoundingClientRect(), width = (rect.right - rect.left) / 10; - if (width > 2) display.cachedCharWidth = width; - return width || 10; + if (display.cachedCharWidth != null) return display.cachedCharWidth + var anchor = elt("span", "xxxxxxxxxx") + var pre = elt("pre", [anchor]) + removeChildrenAndAdd(display.measure, pre) + var rect = anchor.getBoundingClientRect(), width = (rect.right - rect.left) / 10 + if (width > 2) display.cachedCharWidth = width + return width || 10 } // Do a bulk-read of the DOM positions and sizes needed to draw the // view, so that we don't interleave reading and writing to the DOM. export function getDimensions(cm) { - var d = cm.display, left = {}, width = {}; - var gutterLeft = d.gutters.clientLeft; + var d = cm.display, left = {}, width = {} + var gutterLeft = d.gutters.clientLeft for (var n = d.gutters.firstChild, i = 0; n; n = n.nextSibling, ++i) { - left[cm.options.gutters[i]] = n.offsetLeft + n.clientLeft + gutterLeft; - width[cm.options.gutters[i]] = n.clientWidth; + left[cm.options.gutters[i]] = n.offsetLeft + n.clientLeft + gutterLeft + width[cm.options.gutters[i]] = n.clientWidth } return {fixedPos: compensateForHScroll(d), gutterTotalWidth: d.gutters.offsetWidth, gutterLeft: left, gutterWidth: width, - wrapperWidth: d.wrapper.clientWidth}; + wrapperWidth: d.wrapper.clientWidth} } // Computes display.scroller.scrollLeft + display.gutters.offsetWidth, // but using getBoundingClientRect to get a sub-pixel-accurate // result. export function compensateForHScroll(display) { - return display.scroller.getBoundingClientRect().left - display.sizer.getBoundingClientRect().left; + return display.scroller.getBoundingClientRect().left - display.sizer.getBoundingClientRect().left } // Returns a function that estimates the height of a line, to use as // first approximation until the line becomes visible (and is thus // properly measurable). export 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); + 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(cm.doc, line)) return 0; + if (lineIsHidden(cm.doc, line)) return 0 - var widgetsHeight = 0; + var widgetsHeight = 0 if (line.widgets) for (var i = 0; i < line.widgets.length; i++) { - if (line.widgets[i].height) widgetsHeight += line.widgets[i].height; + if (line.widgets[i].height) widgetsHeight += line.widgets[i].height } if (wrapping) - return widgetsHeight + (Math.ceil(line.text.length / perLine) || 1) * th; + return widgetsHeight + (Math.ceil(line.text.length / perLine) || 1) * th else - return widgetsHeight + th; - }; + return widgetsHeight + th + } } export function estimateLineHeights(cm) { - var doc = cm.doc, est = estimateHeight(cm); + var doc = cm.doc, est = estimateHeight(cm) doc.iter(function(line) { - var estHeight = est(line); - if (estHeight != line.height) updateLineHeight(line, estHeight); - }); + var estHeight = est(line) + if (estHeight != line.height) updateLineHeight(line, estHeight) + }) } // Given a mouse event, find the corresponding position. If liberal @@ -551,30 +551,30 @@ export function estimateLineHeights(cm) { // selections, and tries to estimate a character position even for // coordinates beyond the right of the text. export function posFromMouse(cm, e, liberal, forRect) { - var display = cm.display; - if (!liberal && e_target(e).getAttribute("cm-not-content") == "true") return null; + var display = cm.display + if (!liberal && e_target(e).getAttribute("cm-not-content") == "true") return null - var x, y, space = display.lineSpace.getBoundingClientRect(); + var x, y, space = display.lineSpace.getBoundingClientRect() // Fails unpredictably on IE[67] when mouse is dragged around quickly. - try { x = e.clientX - space.left; y = e.clientY - space.top; } - catch (e) { return null; } - var coords = coordsChar(cm, x, y), line; + try { x = e.clientX - space.left; y = e.clientY - space.top } + catch (e) { return null } + var coords = coordsChar(cm, x, y), line if (forRect && coords.xRel == 1 && (line = getLine(cm.doc, coords.line).text).length == coords.ch) { - var colDiff = countColumn(line, line.length, cm.options.tabSize) - line.length; - coords = Pos(coords.line, Math.max(0, Math.round((x - paddingH(cm.display).left) / charWidth(cm.display)) - colDiff)); + var colDiff = countColumn(line, line.length, cm.options.tabSize) - line.length + coords = Pos(coords.line, Math.max(0, Math.round((x - paddingH(cm.display).left) / charWidth(cm.display)) - colDiff)) } - return coords; + return coords } // Find the view element corresponding to a given line. Return null // when the line isn't visible. export function findViewIndex(cm, n) { - if (n >= cm.display.viewTo) return null; - n -= cm.display.viewFrom; - if (n < 0) return null; - var view = cm.display.view; + if (n >= cm.display.viewTo) return null + n -= cm.display.viewFrom + if (n < 0) return null + var view = cm.display.view for (var i = 0; i < view.length; i++) { - n -= view[i].size; - if (n < 0) return i; + n -= view[i].size + if (n < 0) return i } } diff --git a/src/measurement/update_line.js b/src/measurement/update_line.js index 5cac95db..a80d8c50 100644 --- a/src/measurement/update_line.js +++ b/src/measurement/update_line.js @@ -1,189 +1,189 @@ -import { buildLineContent } from "../line/line_data"; -import { lineNumberFor } from "../line/utils_line"; -import { ie, ie_version } from "../util/browser"; -import { elt } from "../util/dom"; -import { signalLater } from "../util/operation_group"; +import { buildLineContent } from "../line/line_data" +import { lineNumberFor } from "../line/utils_line" +import { ie, ie_version } from "../util/browser" +import { elt } from "../util/dom" +import { signalLater } from "../util/operation_group" // When an aspect of a line changes, a string is added to // lineView.changes. This updates the relevant part of the line's // DOM structure. export function updateLineForChanges(cm, lineView, lineN, dims) { for (var j = 0; j < lineView.changes.length; j++) { - var type = lineView.changes[j]; - if (type == "text") updateLineText(cm, lineView); - else if (type == "gutter") updateLineGutter(cm, lineView, lineN, dims); - else if (type == "class") updateLineClasses(lineView); - else if (type == "widget") updateLineWidgets(cm, lineView, dims); + var type = lineView.changes[j] + if (type == "text") updateLineText(cm, lineView) + else if (type == "gutter") updateLineGutter(cm, lineView, lineN, dims) + else if (type == "class") updateLineClasses(lineView) + else if (type == "widget") updateLineWidgets(cm, lineView, dims) } - lineView.changes = null; + lineView.changes = null } // Lines with gutter elements, widgets or a background class need to // be wrapped, and have the extra elements added to the wrapper div function ensureLineWrapped(lineView) { if (lineView.node == lineView.text) { - lineView.node = elt("div", null, null, "position: relative"); + lineView.node = elt("div", null, null, "position: relative") if (lineView.text.parentNode) - lineView.text.parentNode.replaceChild(lineView.node, lineView.text); - lineView.node.appendChild(lineView.text); - if (ie && ie_version < 8) lineView.node.style.zIndex = 2; + lineView.text.parentNode.replaceChild(lineView.node, lineView.text) + lineView.node.appendChild(lineView.text) + if (ie && ie_version < 8) lineView.node.style.zIndex = 2 } - return lineView.node; + return lineView.node } function updateLineBackground(lineView) { - var cls = lineView.bgClass ? lineView.bgClass + " " + (lineView.line.bgClass || "") : lineView.line.bgClass; - if (cls) cls += " CodeMirror-linebackground"; + var cls = lineView.bgClass ? lineView.bgClass + " " + (lineView.line.bgClass || "") : lineView.line.bgClass + if (cls) cls += " CodeMirror-linebackground" if (lineView.background) { - if (cls) lineView.background.className = cls; - else { lineView.background.parentNode.removeChild(lineView.background); lineView.background = null; } + if (cls) lineView.background.className = cls + else { lineView.background.parentNode.removeChild(lineView.background); lineView.background = null } } else if (cls) { - var wrap = ensureLineWrapped(lineView); - lineView.background = wrap.insertBefore(elt("div", null, cls), wrap.firstChild); + var wrap = ensureLineWrapped(lineView) + lineView.background = wrap.insertBefore(elt("div", null, cls), wrap.firstChild) } } // Wrapper around buildLineContent which will reuse the structure // in display.externalMeasured when possible. function getLineContent(cm, lineView) { - var ext = cm.display.externalMeasured; + var ext = cm.display.externalMeasured if (ext && ext.line == lineView.line) { - cm.display.externalMeasured = null; - lineView.measure = ext.measure; - return ext.built; + cm.display.externalMeasured = null + lineView.measure = ext.measure + return ext.built } - return buildLineContent(cm, lineView); + return buildLineContent(cm, lineView) } // Redraw the line's text. Interacts with the background and text // classes because the mode may output tokens that influence these // classes. function updateLineText(cm, lineView) { - var cls = lineView.text.className; - var built = getLineContent(cm, lineView); - if (lineView.text == lineView.node) lineView.node = built.pre; - lineView.text.parentNode.replaceChild(built.pre, lineView.text); - lineView.text = built.pre; + var cls = lineView.text.className + var built = getLineContent(cm, lineView) + if (lineView.text == lineView.node) lineView.node = built.pre + lineView.text.parentNode.replaceChild(built.pre, lineView.text) + lineView.text = built.pre if (built.bgClass != lineView.bgClass || built.textClass != lineView.textClass) { - lineView.bgClass = built.bgClass; - lineView.textClass = built.textClass; - updateLineClasses(lineView); + lineView.bgClass = built.bgClass + lineView.textClass = built.textClass + updateLineClasses(lineView) } else if (cls) { - lineView.text.className = cls; + lineView.text.className = cls } } function updateLineClasses(lineView) { - updateLineBackground(lineView); + updateLineBackground(lineView) if (lineView.line.wrapClass) - ensureLineWrapped(lineView).className = lineView.line.wrapClass; + ensureLineWrapped(lineView).className = lineView.line.wrapClass else if (lineView.node != lineView.text) - lineView.node.className = ""; - var textClass = lineView.textClass ? lineView.textClass + " " + (lineView.line.textClass || "") : lineView.line.textClass; - lineView.text.className = textClass || ""; + lineView.node.className = "" + var textClass = lineView.textClass ? lineView.textClass + " " + (lineView.line.textClass || "") : lineView.line.textClass + lineView.text.className = textClass || "" } function updateLineGutter(cm, lineView, lineN, dims) { if (lineView.gutter) { - lineView.node.removeChild(lineView.gutter); - lineView.gutter = null; + lineView.node.removeChild(lineView.gutter) + lineView.gutter = null } if (lineView.gutterBackground) { - lineView.node.removeChild(lineView.gutterBackground); - lineView.gutterBackground = null; + lineView.node.removeChild(lineView.gutterBackground) + lineView.gutterBackground = null } if (lineView.line.gutterClass) { - var wrap = ensureLineWrapped(lineView); + var wrap = ensureLineWrapped(lineView) lineView.gutterBackground = elt("div", null, "CodeMirror-gutter-background " + lineView.line.gutterClass, "left: " + (cm.options.fixedGutter ? dims.fixedPos : -dims.gutterTotalWidth) + - "px; width: " + dims.gutterTotalWidth + "px"); - wrap.insertBefore(lineView.gutterBackground, lineView.text); + "px; width: " + dims.gutterTotalWidth + "px") + wrap.insertBefore(lineView.gutterBackground, lineView.text) } - var markers = lineView.line.gutterMarkers; + var markers = lineView.line.gutterMarkers if (cm.options.lineNumbers || markers) { - var wrap = ensureLineWrapped(lineView); + var wrap = ensureLineWrapped(lineView) var gutterWrap = lineView.gutter = elt("div", null, "CodeMirror-gutter-wrapper", "left: " + - (cm.options.fixedGutter ? dims.fixedPos : -dims.gutterTotalWidth) + "px"); - cm.display.input.setUneditable(gutterWrap); - wrap.insertBefore(gutterWrap, lineView.text); + (cm.options.fixedGutter ? dims.fixedPos : -dims.gutterTotalWidth) + "px") + cm.display.input.setUneditable(gutterWrap) + wrap.insertBefore(gutterWrap, lineView.text) if (lineView.line.gutterClass) - gutterWrap.className += " " + lineView.line.gutterClass; + gutterWrap.className += " " + lineView.line.gutterClass if (cm.options.lineNumbers && (!markers || !markers["CodeMirror-linenumbers"])) lineView.lineNumber = gutterWrap.appendChild( elt("div", lineNumberFor(cm.options, lineN), "CodeMirror-linenumber CodeMirror-gutter-elt", "left: " + dims.gutterLeft["CodeMirror-linenumbers"] + "px; width: " - + cm.display.lineNumInnerWidth + "px")); + + cm.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]; + var id = cm.options.gutters[k], found = markers.hasOwnProperty(id) && markers[id] if (found) gutterWrap.appendChild(elt("div", [found], "CodeMirror-gutter-elt", "left: " + - dims.gutterLeft[id] + "px; width: " + dims.gutterWidth[id] + "px")); + dims.gutterLeft[id] + "px; width: " + dims.gutterWidth[id] + "px")) } } } function updateLineWidgets(cm, lineView, dims) { - if (lineView.alignable) lineView.alignable = null; + if (lineView.alignable) lineView.alignable = null for (var node = lineView.node.firstChild, next; node; node = next) { - var next = node.nextSibling; + var next = node.nextSibling if (node.className == "CodeMirror-linewidget") - lineView.node.removeChild(node); + lineView.node.removeChild(node) } - insertLineWidgets(cm, lineView, dims); + insertLineWidgets(cm, lineView, dims) } // Build a line's DOM representation from scratch export function buildLineElement(cm, lineView, lineN, dims) { - var built = getLineContent(cm, lineView); - lineView.text = lineView.node = built.pre; - if (built.bgClass) lineView.bgClass = built.bgClass; - if (built.textClass) lineView.textClass = built.textClass; + var built = getLineContent(cm, lineView) + lineView.text = lineView.node = built.pre + if (built.bgClass) lineView.bgClass = built.bgClass + if (built.textClass) lineView.textClass = built.textClass - updateLineClasses(lineView); - updateLineGutter(cm, lineView, lineN, dims); - insertLineWidgets(cm, lineView, dims); - return lineView.node; + updateLineClasses(lineView) + updateLineGutter(cm, lineView, lineN, dims) + insertLineWidgets(cm, lineView, dims) + return lineView.node } // A lineView may contain multiple logical lines (when merged by // collapsed spans). The widgets for all of them need to be drawn. function insertLineWidgets(cm, lineView, dims) { - insertLineWidgetsFor(cm, lineView.line, lineView, dims, true); + insertLineWidgetsFor(cm, lineView.line, lineView, dims, true) if (lineView.rest) for (var i = 0; i < lineView.rest.length; i++) - insertLineWidgetsFor(cm, lineView.rest[i], lineView, dims, false); + insertLineWidgetsFor(cm, lineView.rest[i], lineView, dims, false) } function insertLineWidgetsFor(cm, line, lineView, dims, allowAbove) { - if (!line.widgets) return; - var wrap = ensureLineWrapped(lineView); + if (!line.widgets) return + var wrap = ensureLineWrapped(lineView) 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.setAttribute("cm-ignore-events", "true"); - positionLineWidget(widget, node, lineView, dims); - cm.display.input.setUneditable(node); + var widget = ws[i], node = elt("div", [widget.node], "CodeMirror-linewidget") + if (!widget.handleMouseEvents) node.setAttribute("cm-ignore-events", "true") + positionLineWidget(widget, node, lineView, dims) + cm.display.input.setUneditable(node) if (allowAbove && widget.above) - wrap.insertBefore(node, lineView.gutter || lineView.text); + wrap.insertBefore(node, lineView.gutter || lineView.text) else - wrap.appendChild(node); - signalLater(widget, "redraw"); + wrap.appendChild(node) + signalLater(widget, "redraw") } } function positionLineWidget(widget, node, lineView, dims) { if (widget.noHScroll) { - (lineView.alignable || (lineView.alignable = [])).push(node); - var width = dims.wrapperWidth; - node.style.left = dims.fixedPos + "px"; + (lineView.alignable || (lineView.alignable = [])).push(node) + var width = dims.wrapperWidth + node.style.left = dims.fixedPos + "px" if (!widget.coverGutter) { - width -= dims.gutterTotalWidth; - node.style.paddingLeft = dims.gutterTotalWidth + "px"; + width -= dims.gutterTotalWidth + node.style.paddingLeft = dims.gutterTotalWidth + "px" } - node.style.width = width + "px"; + node.style.width = width + "px" } if (widget.coverGutter) { - node.style.zIndex = 5; - node.style.position = "relative"; - if (!widget.noHScroll) node.style.marginLeft = -dims.gutterTotalWidth + "px"; + node.style.zIndex = 5 + node.style.position = "relative" + if (!widget.noHScroll) node.style.marginLeft = -dims.gutterTotalWidth + "px" } } diff --git a/src/measurement/widgets.js b/src/measurement/widgets.js index 283740e2..f20d313e 100644 --- a/src/measurement/widgets.js +++ b/src/measurement/widgets.js @@ -1,19 +1,19 @@ -import { contains, elt, removeChildrenAndAdd } from "../util/dom"; -import { e_target } from "../util/event"; +import { contains, elt, removeChildrenAndAdd } from "../util/dom" +import { e_target } from "../util/event" export function widgetHeight(widget) { - if (widget.height != null) return widget.height; - var cm = widget.doc.cm; - if (!cm) return 0; + if (widget.height != null) return widget.height + var cm = widget.doc.cm + if (!cm) return 0 if (!contains(document.body, widget.node)) { - var parentStyle = "position: relative;"; + var parentStyle = "position: relative;" if (widget.coverGutter) - parentStyle += "margin-left: -" + cm.display.gutters.offsetWidth + "px;"; + parentStyle += "margin-left: -" + cm.display.gutters.offsetWidth + "px;" if (widget.noHScroll) - parentStyle += "width: " + cm.display.wrapper.clientWidth + "px;"; - removeChildrenAndAdd(cm.display.measure, elt("div", [widget.node], null, parentStyle)); + parentStyle += "width: " + cm.display.wrapper.clientWidth + "px;" + removeChildrenAndAdd(cm.display.measure, elt("div", [widget.node], null, parentStyle)) } - return widget.height = widget.node.parentNode.offsetHeight; + return widget.height = widget.node.parentNode.offsetHeight } // Return true when the given mouse event happened in a widget @@ -21,6 +21,6 @@ export function eventInWidget(display, e) { for (var n = e_target(e); n != display.wrapper; n = n.parentNode) { if (!n || (n.nodeType == 1 && n.getAttribute("cm-ignore-events") == "true") || (n.parentNode == display.sizer && n != display.mover)) - return true; + return true } } diff --git a/src/model/Doc.js b/src/model/Doc.js index 68d38c90..ba004631 100644 --- a/src/model/Doc.js +++ b/src/model/Doc.js @@ -1,47 +1,47 @@ -import CodeMirror from "../edit/CodeMirror"; -import { docMethodOp } from "../display/operations"; -import { Line } from "../line/line_data"; -import { clipPos, clipPosArray, Pos } from "../line/pos"; -import { visualLine } from "../line/spans"; -import { getBetween, getLine, getLines, isLine, lineNo } from "../line/utils_line"; -import { classTest } from "../util/dom"; -import { splitLinesAuto } from "../util/feature_detection"; -import { createObj, map, sel_dontScroll } from "../util/misc"; -import { ensureCursorVisible } from "../display/scrolling"; +import CodeMirror from "../edit/CodeMirror" +import { docMethodOp } from "../display/operations" +import { Line } from "../line/line_data" +import { clipPos, clipPosArray, Pos } from "../line/pos" +import { visualLine } from "../line/spans" +import { getBetween, getLine, getLines, isLine, lineNo } from "../line/utils_line" +import { classTest } from "../util/dom" +import { splitLinesAuto } from "../util/feature_detection" +import { createObj, map, sel_dontScroll } from "../util/misc" +import { ensureCursorVisible } from "../display/scrolling" -import { changeLine, makeChange, makeChangeFromHistory, replaceRange } from "./changes"; -import { computeReplacedSel } from "./change_measurement"; -import { BranchChunk, LeafChunk } from "./chunk"; -import { linkedDocs, updateDoc } from "./document_data"; -import { copyHistoryArray, History } from "./history"; -import { addLineWidget } from "./line_widget"; -import { copySharedMarkers, detachSharedMarkers, findSharedMarkers, markText } from "./mark_text"; -import { normalizeSelection, Range, simpleSelection } from "./selection"; -import { extendSelection, extendSelections, setSelection, setSelectionReplaceHistory, setSimpleSelection } from "./selection_updates"; +import { changeLine, makeChange, makeChangeFromHistory, replaceRange } from "./changes" +import { computeReplacedSel } from "./change_measurement" +import { BranchChunk, LeafChunk } from "./chunk" +import { linkedDocs, updateDoc } from "./document_data" +import { copyHistoryArray, History } from "./history" +import { addLineWidget } from "./line_widget" +import { copySharedMarkers, detachSharedMarkers, findSharedMarkers, markText } from "./mark_text" +import { normalizeSelection, Range, simpleSelection } from "./selection" +import { extendSelection, extendSelections, setSelection, setSelectionReplaceHistory, setSimpleSelection } from "./selection_updates" -var nextDocId = 0; +var nextDocId = 0 var Doc = function(text, mode, firstLine, lineSep) { - if (!(this instanceof Doc)) return new Doc(text, mode, firstLine, lineSep); - if (firstLine == null) firstLine = 0; + if (!(this instanceof Doc)) return new Doc(text, mode, firstLine, lineSep) + if (firstLine == null) firstLine = 0 - BranchChunk.call(this, [new LeafChunk([new Line("", null)])]); - this.first = firstLine; - this.scrollTop = this.scrollLeft = 0; - this.cantEdit = false; - this.cleanGeneration = 1; - this.frontier = firstLine; - var start = Pos(firstLine, 0); - this.sel = simpleSelection(start); - this.history = new History(null); - this.id = ++nextDocId; - this.modeOption = mode; - this.lineSep = lineSep; - this.extend = false; + BranchChunk.call(this, [new LeafChunk([new Line("", null)])]) + this.first = firstLine + this.scrollTop = this.scrollLeft = 0 + this.cantEdit = false + this.cleanGeneration = 1 + this.frontier = firstLine + var start = Pos(firstLine, 0) + this.sel = simpleSelection(start) + this.history = new History(null) + this.id = ++nextDocId + this.modeOption = mode + this.lineSep = lineSep + this.extend = false - if (typeof text == "string") text = this.splitLines(text); - updateDoc(this, {from: start, to: start, text: text}); - setSelection(this, simpleSelection(start), sel_dontScroll); -}; + if (typeof text == "string") text = this.splitLines(text) + updateDoc(this, {from: start, to: start, text: text}) + setSelection(this, simpleSelection(start), sel_dontScroll) +} Doc.prototype = createObj(BranchChunk.prototype, { constructor: Doc, @@ -50,335 +50,335 @@ Doc.prototype = createObj(BranchChunk.prototype, { // three, it iterates over the range given by the first two (with // the second being non-inclusive). 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); + if (op) this.iterN(from - this.first, to - from, op) + else this.iterN(this.first, this.first + this.size, from) }, // Non-public interface for adding and removing lines. insert: function(at, lines) { - var height = 0; - for (var i = 0; i < lines.length; ++i) height += lines[i].height; - this.insertInner(at - this.first, lines, height); + var height = 0 + for (var i = 0; i < lines.length; ++i) height += lines[i].height + this.insertInner(at - this.first, lines, height) }, - remove: function(at, n) { this.removeInner(at - this.first, n); }, + remove: function(at, n) { this.removeInner(at - this.first, n) }, // From here, the methods are part of the public interface. Most // are also available from CodeMirror (editor) instances. getValue: function(lineSep) { - var lines = getLines(this, this.first, this.first + this.size); - if (lineSep === false) return lines; - return lines.join(lineSep || this.lineSeparator()); + var lines = getLines(this, this.first, this.first + this.size) + if (lineSep === false) return lines + return lines.join(lineSep || this.lineSeparator()) }, setValue: docMethodOp(function(code) { - var top = Pos(this.first, 0), last = this.first + this.size - 1; + 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: this.splitLines(code), origin: "setValue", full: true}, true); - setSelection(this, simpleSelection(top)); + text: this.splitLines(code), origin: "setValue", full: true}, true) + setSelection(this, simpleSelection(top)) }), replaceRange: function(code, from, to, origin) { - from = clipPos(this, from); - to = to ? clipPos(this, to) : from; - replaceRange(this, code, from, to, origin); + from = clipPos(this, from) + to = to ? clipPos(this, to) : from + replaceRange(this, code, from, to, origin) }, getRange: function(from, to, lineSep) { - var lines = getBetween(this, clipPos(this, from), clipPos(this, to)); - if (lineSep === false) return lines; - return lines.join(lineSep || this.lineSeparator()); + var lines = getBetween(this, clipPos(this, from), clipPos(this, to)) + if (lineSep === false) return lines + return lines.join(lineSep || this.lineSeparator()) }, - getLine: function(line) {var l = this.getLineHandle(line); return l && l.text;}, + getLine: function(line) {var l = this.getLineHandle(line); return l && l.text}, - getLineHandle: function(line) {if (isLine(this, line)) return getLine(this, line);}, - getLineNumber: function(line) {return lineNo(line);}, + 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(line); + if (typeof line == "number") line = getLine(this, line) + return visualLine(line) }, - lineCount: function() {return this.size;}, - firstLine: function() {return this.first;}, - lastLine: function() {return this.first + this.size - 1;}, + 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);}, + clipPos: function(pos) {return clipPos(this, pos)}, getCursor: function(start) { - var range = this.sel.primary(), pos; - if (start == null || start == "head") pos = range.head; - else if (start == "anchor") pos = range.anchor; - else if (start == "end" || start == "to" || start === false) pos = range.to(); - else pos = range.from(); - return pos; + var range = this.sel.primary(), pos + if (start == null || start == "head") pos = range.head + else if (start == "anchor") pos = range.anchor + else if (start == "end" || start == "to" || start === false) pos = range.to() + else pos = range.from() + return pos }, - listSelections: function() { return this.sel.ranges; }, - somethingSelected: function() {return this.sel.somethingSelected();}, + listSelections: function() { return this.sel.ranges }, + somethingSelected: function() {return this.sel.somethingSelected()}, setCursor: docMethodOp(function(line, ch, options) { - setSimpleSelection(this, clipPos(this, typeof line == "number" ? Pos(line, ch || 0) : line), null, options); + setSimpleSelection(this, clipPos(this, typeof line == "number" ? Pos(line, ch || 0) : line), null, options) }), setSelection: docMethodOp(function(anchor, head, options) { - setSimpleSelection(this, clipPos(this, anchor), clipPos(this, head || anchor), options); + setSimpleSelection(this, clipPos(this, anchor), clipPos(this, head || anchor), options) }), extendSelection: docMethodOp(function(head, other, options) { - extendSelection(this, clipPos(this, head), other && clipPos(this, other), options); + extendSelection(this, clipPos(this, head), other && clipPos(this, other), options) }), extendSelections: docMethodOp(function(heads, options) { - extendSelections(this, clipPosArray(this, heads), options); + extendSelections(this, clipPosArray(this, heads), options) }), extendSelectionsBy: docMethodOp(function(f, options) { - var heads = map(this.sel.ranges, f); - extendSelections(this, clipPosArray(this, heads), options); + var heads = map(this.sel.ranges, f) + extendSelections(this, clipPosArray(this, heads), options) }), setSelections: docMethodOp(function(ranges, primary, options) { - if (!ranges.length) return; + if (!ranges.length) return for (var i = 0, out = []; i < ranges.length; i++) out[i] = new Range(clipPos(this, ranges[i].anchor), - clipPos(this, ranges[i].head)); - if (primary == null) primary = Math.min(ranges.length - 1, this.sel.primIndex); - setSelection(this, normalizeSelection(out, primary), options); + clipPos(this, ranges[i].head)) + if (primary == null) primary = Math.min(ranges.length - 1, this.sel.primIndex) + setSelection(this, normalizeSelection(out, primary), options) }), addSelection: docMethodOp(function(anchor, head, options) { - var ranges = this.sel.ranges.slice(0); - ranges.push(new Range(clipPos(this, anchor), clipPos(this, head || anchor))); - setSelection(this, normalizeSelection(ranges, ranges.length - 1), options); + var ranges = this.sel.ranges.slice(0) + ranges.push(new Range(clipPos(this, anchor), clipPos(this, head || anchor))) + setSelection(this, normalizeSelection(ranges, ranges.length - 1), options) }), getSelection: function(lineSep) { - var ranges = this.sel.ranges, lines; + var ranges = this.sel.ranges, lines for (var i = 0; i < ranges.length; i++) { - var sel = getBetween(this, ranges[i].from(), ranges[i].to()); - lines = lines ? lines.concat(sel) : sel; + var sel = getBetween(this, ranges[i].from(), ranges[i].to()) + lines = lines ? lines.concat(sel) : sel } - if (lineSep === false) return lines; - else return lines.join(lineSep || this.lineSeparator()); + if (lineSep === false) return lines + else return lines.join(lineSep || this.lineSeparator()) }, getSelections: function(lineSep) { - var parts = [], ranges = this.sel.ranges; + var parts = [], ranges = this.sel.ranges for (var i = 0; i < ranges.length; i++) { - var sel = getBetween(this, ranges[i].from(), ranges[i].to()); - if (lineSep !== false) sel = sel.join(lineSep || this.lineSeparator()); - parts[i] = sel; + var sel = getBetween(this, ranges[i].from(), ranges[i].to()) + if (lineSep !== false) sel = sel.join(lineSep || this.lineSeparator()) + parts[i] = sel } - return parts; + return parts }, replaceSelection: function(code, collapse, origin) { - var dup = []; + var dup = [] for (var i = 0; i < this.sel.ranges.length; i++) - dup[i] = code; - this.replaceSelections(dup, collapse, origin || "+input"); + dup[i] = code + this.replaceSelections(dup, collapse, origin || "+input") }, replaceSelections: docMethodOp(function(code, collapse, origin) { - var changes = [], sel = this.sel; + var changes = [], sel = this.sel for (var i = 0; i < sel.ranges.length; i++) { - var range = sel.ranges[i]; - changes[i] = {from: range.from(), to: range.to(), text: this.splitLines(code[i]), origin: origin}; + var range = sel.ranges[i] + changes[i] = {from: range.from(), to: range.to(), text: this.splitLines(code[i]), origin: origin} } - var newSel = collapse && collapse != "end" && computeReplacedSel(this, changes, collapse); + var newSel = collapse && collapse != "end" && computeReplacedSel(this, changes, collapse) for (var i = changes.length - 1; i >= 0; i--) - makeChange(this, changes[i]); - if (newSel) setSelectionReplaceHistory(this, newSel); - else if (this.cm) ensureCursorVisible(this.cm); + makeChange(this, changes[i]) + if (newSel) setSelectionReplaceHistory(this, newSel) + else if (this.cm) ensureCursorVisible(this.cm) }), - undo: docMethodOp(function() {makeChangeFromHistory(this, "undo");}), - redo: docMethodOp(function() {makeChangeFromHistory(this, "redo");}), - undoSelection: docMethodOp(function() {makeChangeFromHistory(this, "undo", true);}), - redoSelection: docMethodOp(function() {makeChangeFromHistory(this, "redo", true);}), + undo: docMethodOp(function() {makeChangeFromHistory(this, "undo")}), + redo: docMethodOp(function() {makeChangeFromHistory(this, "redo")}), + undoSelection: docMethodOp(function() {makeChangeFromHistory(this, "undo", true)}), + redoSelection: docMethodOp(function() {makeChangeFromHistory(this, "redo", true)}), - setExtending: function(val) {this.extend = val;}, - getExtending: function() {return this.extend;}, + setExtending: function(val) {this.extend = val}, + getExtending: function() {return this.extend}, historySize: function() { - var hist = this.history, done = 0, undone = 0; - for (var i = 0; i < hist.done.length; i++) if (!hist.done[i].ranges) ++done; - for (var i = 0; i < hist.undone.length; i++) if (!hist.undone[i].ranges) ++undone; - return {undo: done, redo: undone}; + var hist = this.history, done = 0, undone = 0 + for (var i = 0; i < hist.done.length; i++) if (!hist.done[i].ranges) ++done + for (var i = 0; i < hist.undone.length; i++) if (!hist.undone[i].ranges) ++undone + return {undo: done, redo: undone} }, - clearHistory: function() {this.history = new History(this.history.maxGeneration);}, + clearHistory: function() {this.history = new History(this.history.maxGeneration)}, markClean: function() { - this.cleanGeneration = this.changeGeneration(true); + this.cleanGeneration = this.changeGeneration(true) }, changeGeneration: function(forceSplit) { if (forceSplit) - this.history.lastOp = this.history.lastSelOp = this.history.lastOrigin = null; - return this.history.generation; + this.history.lastOp = this.history.lastSelOp = this.history.lastOrigin = null + return this.history.generation }, isClean: function (gen) { - return this.history.generation == (gen || this.cleanGeneration); + return this.history.generation == (gen || this.cleanGeneration) }, getHistory: function() { return {done: copyHistoryArray(this.history.done), - undone: copyHistoryArray(this.history.undone)}; + undone: copyHistoryArray(this.history.undone)} }, setHistory: function(histData) { - var hist = this.history = new History(this.history.maxGeneration); - hist.done = copyHistoryArray(histData.done.slice(0), null, true); - hist.undone = copyHistoryArray(histData.undone.slice(0), null, true); + var hist = this.history = new History(this.history.maxGeneration) + hist.done = copyHistoryArray(histData.done.slice(0), null, true) + hist.undone = copyHistoryArray(histData.undone.slice(0), null, true) }, addLineClass: docMethodOp(function(handle, where, cls) { return changeLine(this, handle, where == "gutter" ? "gutter" : "class", function(line) { var prop = where == "text" ? "textClass" : where == "background" ? "bgClass" - : where == "gutter" ? "gutterClass" : "wrapClass"; - if (!line[prop]) line[prop] = cls; - else if (classTest(cls).test(line[prop])) return false; - else line[prop] += " " + cls; - return true; - }); + : where == "gutter" ? "gutterClass" : "wrapClass" + if (!line[prop]) line[prop] = cls + else if (classTest(cls).test(line[prop])) return false + else line[prop] += " " + cls + return true + }) }), removeLineClass: docMethodOp(function(handle, where, cls) { return changeLine(this, handle, where == "gutter" ? "gutter" : "class", function(line) { var prop = where == "text" ? "textClass" : where == "background" ? "bgClass" - : where == "gutter" ? "gutterClass" : "wrapClass"; - var cur = line[prop]; - if (!cur) return false; - else if (cls == null) line[prop] = null; + : where == "gutter" ? "gutterClass" : "wrapClass" + var cur = line[prop] + if (!cur) return false + else if (cls == null) line[prop] = null else { - var found = cur.match(classTest(cls)); - if (!found) return false; - var end = found.index + found[0].length; - line[prop] = cur.slice(0, found.index) + (!found.index || end == cur.length ? "" : " ") + cur.slice(end) || null; + var found = cur.match(classTest(cls)) + if (!found) return false + var end = found.index + found[0].length + line[prop] = cur.slice(0, found.index) + (!found.index || end == cur.length ? "" : " ") + cur.slice(end) || null } - return true; - }); + return true + }) }), addLineWidget: docMethodOp(function(handle, node, options) { - return addLineWidget(this, handle, node, options); + return addLineWidget(this, handle, node, options) }), - removeLineWidget: function(widget) { widget.clear(); }, + removeLineWidget: function(widget) { widget.clear() }, markText: function(from, to, options) { - return markText(this, clipPos(this, from), clipPos(this, to), options, options && options.type || "range"); + return markText(this, clipPos(this, from), clipPos(this, to), options, options && options.type || "range") }, setBookmark: function(pos, options) { var realOpts = {replacedWith: options && (options.nodeType == null ? options.widget : options), insertLeft: options && options.insertLeft, clearWhenEmpty: false, shared: options && options.shared, - handleMouseEvents: options && options.handleMouseEvents}; - pos = clipPos(this, pos); - return markText(this, pos, pos, realOpts, "bookmark"); + handleMouseEvents: options && options.handleMouseEvents} + pos = clipPos(this, pos) + return markText(this, pos, pos, realOpts, "bookmark") }, findMarksAt: function(pos) { - pos = clipPos(this, pos); - var markers = [], spans = getLine(this, pos.line).markedSpans; + 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]; + var span = spans[i] if ((span.from == null || span.from <= pos.ch) && (span.to == null || span.to >= pos.ch)) - markers.push(span.marker.parent || span.marker); + markers.push(span.marker.parent || span.marker) } - return markers; + return markers }, findMarks: function(from, to, filter) { - from = clipPos(this, from); to = clipPos(this, to); - var found = [], lineNo = from.line; + from = clipPos(this, from); to = clipPos(this, to) + var found = [], lineNo = from.line this.iter(from.line, to.line + 1, function(line) { - var spans = line.markedSpans; + var spans = line.markedSpans if (spans) for (var i = 0; i < spans.length; i++) { - var span = spans[i]; + var span = spans[i] 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) && (!filter || filter(span.marker))) - found.push(span.marker.parent || span.marker); + found.push(span.marker.parent || span.marker) } - ++lineNo; - }); - return found; + ++lineNo + }) + return found }, getAllMarks: function() { - var markers = []; + var markers = [] this.iter(function(line) { - var sps = line.markedSpans; + 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; + if (sps[i].from != null) markers.push(sps[i].marker) + }) + return markers }, posFromIndex: function(off) { - var ch, lineNo = this.first, sepSize = this.lineSeparator().length; + var ch, lineNo = this.first, sepSize = this.lineSeparator().length this.iter(function(line) { - var sz = line.text.length + sepSize; - if (sz > off) { ch = off; return true; } - off -= sz; - ++lineNo; - }); - return clipPos(this, Pos(lineNo, ch)); + var sz = line.text.length + sepSize + if (sz > off) { ch = off; return true } + off -= sz + ++lineNo + }) + return clipPos(this, Pos(lineNo, ch)) }, indexFromPos: function (coords) { - coords = clipPos(this, coords); - var index = coords.ch; - if (coords.line < this.first || coords.ch < 0) return 0; - var sepSize = this.lineSeparator().length; + 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 + sepSize; - }); - return index; + index += line.text.length + sepSize + }) + return index }, copy: function(copyHistory) { var doc = new Doc(getLines(this, this.first, this.first + this.size), - this.modeOption, this.first, this.lineSep); - doc.scrollTop = this.scrollTop; doc.scrollLeft = this.scrollLeft; - doc.sel = this.sel; - doc.extend = false; + this.modeOption, this.first, this.lineSep) + doc.scrollTop = this.scrollTop; doc.scrollLeft = this.scrollLeft + doc.sel = this.sel + doc.extend = false if (copyHistory) { - doc.history.undoDepth = this.history.undoDepth; - doc.setHistory(this.getHistory()); + doc.history.undoDepth = this.history.undoDepth + doc.setHistory(this.getHistory()) } - return doc; + 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, this.lineSep); - 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}]; - copySharedMarkers(copy, findSharedMarkers(this)); - return copy; + 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, this.lineSep) + 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}] + copySharedMarkers(copy, findSharedMarkers(this)) + return copy }, unlinkDoc: function(other) { - if (other instanceof CodeMirror) other = other.doc; + 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); - detachSharedMarkers(findSharedMarkers(this)); - break; + var link = this.linked[i] + if (link.doc != other) continue + this.linked.splice(i, 1) + other.unlinkDoc(this) + detachSharedMarkers(findSharedMarkers(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 = new History(null); - other.history.done = copyHistoryArray(this.history.done, splitIds); - other.history.undone = copyHistoryArray(this.history.undone, splitIds); + var splitIds = [other.id] + linkedDocs(other, function(doc) {splitIds.push(doc.id)}, true) + other.history = new History(null) + other.history.done = copyHistoryArray(this.history.done, splitIds) + other.history.undone = copyHistoryArray(this.history.undone, splitIds) } }, - iterLinkedDocs: function(f) {linkedDocs(this, f);}, + iterLinkedDocs: function(f) {linkedDocs(this, f)}, - getMode: function() {return this.mode;}, - getEditor: function() {return this.cm;}, + getMode: function() {return this.mode}, + getEditor: function() {return this.cm}, splitLines: function(str) { - if (this.lineSep) return str.split(this.lineSep); - return splitLinesAuto(str); + if (this.lineSep) return str.split(this.lineSep) + return splitLinesAuto(str) }, - lineSeparator: function() { return this.lineSep || "\n"; } -}); + lineSeparator: function() { return this.lineSep || "\n" } +}) // Public alias. -Doc.prototype.eachLine = Doc.prototype.iter; +Doc.prototype.eachLine = Doc.prototype.iter -export default Doc; +export default Doc diff --git a/src/model/change_measurement.js b/src/model/change_measurement.js index cb2bb2d1..ac14fa54 100644 --- a/src/model/change_measurement.js +++ b/src/model/change_measurement.js @@ -1,61 +1,61 @@ -import { cmp, Pos } from "../line/pos"; -import { lst } from "../util/misc"; +import { cmp, Pos } from "../line/pos" +import { lst } from "../util/misc" -import { normalizeSelection, Range, Selection } from "./selection"; +import { normalizeSelection, Range, Selection } from "./selection" // Compute the position of the end of a change (its 'to' property // refers to the pre-change end). export function changeEnd(change) { - if (!change.text) return change.to; + if (!change.text) return change.to return Pos(change.from.line + change.text.length - 1, - lst(change.text).length + (change.text.length == 1 ? change.from.ch : 0)); + lst(change.text).length + (change.text.length == 1 ? change.from.ch : 0)) } // Adjust a position to refer to the post-change position of the // same text, or the end of the change if the change covers it. function adjustForChange(pos, change) { - if (cmp(pos, change.from) < 0) return pos; - if (cmp(pos, change.to) <= 0) return changeEnd(change); + if (cmp(pos, change.from) < 0) return pos + if (cmp(pos, change.to) <= 0) return changeEnd(change) - var line = pos.line + change.text.length - (change.to.line - change.from.line) - 1, ch = pos.ch; - if (pos.line == change.to.line) ch += changeEnd(change).ch - change.to.ch; - return Pos(line, ch); + var line = pos.line + change.text.length - (change.to.line - change.from.line) - 1, ch = pos.ch + if (pos.line == change.to.line) ch += changeEnd(change).ch - change.to.ch + return Pos(line, ch) } export function computeSelAfterChange(doc, change) { - var out = []; + var out = [] for (var i = 0; i < doc.sel.ranges.length; i++) { - var range = doc.sel.ranges[i]; + var range = doc.sel.ranges[i] out.push(new Range(adjustForChange(range.anchor, change), - adjustForChange(range.head, change))); + adjustForChange(range.head, change))) } - return normalizeSelection(out, doc.sel.primIndex); + return normalizeSelection(out, doc.sel.primIndex) } function offsetPos(pos, old, nw) { if (pos.line == old.line) - return Pos(nw.line, pos.ch - old.ch + nw.ch); + return Pos(nw.line, pos.ch - old.ch + nw.ch) else - return Pos(nw.line + (pos.line - old.line), pos.ch); + return Pos(nw.line + (pos.line - old.line), pos.ch) } // Used by replaceSelections to allow moving the selection to the // start or around the replaced test. Hint may be "start" or "around". export function computeReplacedSel(doc, changes, hint) { - var out = []; - var oldPrev = Pos(doc.first, 0), newPrev = oldPrev; + var out = [] + var oldPrev = Pos(doc.first, 0), newPrev = oldPrev for (var i = 0; i < changes.length; i++) { - var change = changes[i]; - var from = offsetPos(change.from, oldPrev, newPrev); - var to = offsetPos(changeEnd(change), oldPrev, newPrev); - oldPrev = change.to; - newPrev = to; + var change = changes[i] + var from = offsetPos(change.from, oldPrev, newPrev) + var to = offsetPos(changeEnd(change), oldPrev, newPrev) + oldPrev = change.to + newPrev = to if (hint == "around") { - var range = doc.sel.ranges[i], inv = cmp(range.head, range.anchor) < 0; - out[i] = new Range(inv ? to : from, inv ? from : to); + var range = doc.sel.ranges[i], inv = cmp(range.head, range.anchor) < 0 + out[i] = new Range(inv ? to : from, inv ? from : to) } else { - out[i] = new Range(from, from); + out[i] = new Range(from, from) } } - return new Selection(out, doc.sel.primIndex); + return new Selection(out, doc.sel.primIndex) } diff --git a/src/model/changes.js b/src/model/changes.js index 01526d29..6a8606af 100644 --- a/src/model/changes.js +++ b/src/model/changes.js @@ -1,20 +1,20 @@ -import { startWorker } from "../display/highlight_worker"; -import { operation } from "../display/operations"; -import { regChange, regLineChange } from "../display/view_tracking"; -import { clipLine, clipPos, cmp, Pos } from "../line/pos"; -import { sawReadOnlySpans } from "../line/saw_special_spans"; -import { lineLength, removeReadOnlyRanges, stretchSpansOverChange, visualLine } from "../line/spans"; -import { getBetween, getLine, lineNo } from "../line/utils_line"; -import { estimateHeight } from "../measurement/position_measurement"; -import { hasHandler, signal, signalCursorActivity } from "../util/event"; -import { indexOf, lst, map, sel_dontScroll } from "../util/misc"; -import { signalLater } from "../util/operation_group"; - -import { changeEnd, computeSelAfterChange } from "./change_measurement"; -import { isWholeLineUpdate, linkedDocs, updateDoc } from "./document_data"; -import { addChangeToHistory, historyChangeFromChange, mergeOldSpans, pushSelectionToHistory } from "./history"; -import { Range, Selection } from "./selection"; -import { setSelection, setSelectionNoUndo } from "./selection_updates"; +import { startWorker } from "../display/highlight_worker" +import { operation } from "../display/operations" +import { regChange, regLineChange } from "../display/view_tracking" +import { clipLine, clipPos, cmp, Pos } from "../line/pos" +import { sawReadOnlySpans } from "../line/saw_special_spans" +import { lineLength, removeReadOnlyRanges, stretchSpansOverChange, visualLine } from "../line/spans" +import { getBetween, getLine, lineNo } from "../line/utils_line" +import { estimateHeight } from "../measurement/position_measurement" +import { hasHandler, signal, signalCursorActivity } from "../util/event" +import { indexOf, lst, map, sel_dontScroll } from "../util/misc" +import { signalLater } from "../util/operation_group" + +import { changeEnd, computeSelAfterChange } from "./change_measurement" +import { isWholeLineUpdate, linkedDocs, updateDoc } from "./document_data" +import { addChangeToHistory, historyChangeFromChange, mergeOldSpans, pushSelectionToHistory } from "./history" +import { Range, Selection } from "./selection" +import { setSelection, setSelectionNoUndo } from "./selection_updates" // UPDATING @@ -26,140 +26,140 @@ function filterChange(doc, change, update) { to: change.to, text: change.text, origin: change.origin, - cancel: function() { this.canceled = true; } - }; + 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); - - if (obj.canceled) return null; - return {from: obj.from, to: obj.to, text: obj.text, origin: obj.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) + + if (obj.canceled) return null + return {from: obj.from, to: obj.to, text: obj.text, origin: obj.origin} } // Apply a change to a document, and add it to the document's // history, and propagating it to all linked documents. export function makeChange(doc, change, ignoreReadOnly) { if (doc.cm) { - if (!doc.cm.curOp) return operation(doc.cm, makeChange)(doc, change, ignoreReadOnly); - if (doc.cm.state.suppressEdits) return; + if (!doc.cm.curOp) return operation(doc.cm, makeChange)(doc, change, ignoreReadOnly) + if (doc.cm.state.suppressEdits) return } if (hasHandler(doc, "beforeChange") || doc.cm && hasHandler(doc.cm, "beforeChange")) { - change = filterChange(doc, change, true); - if (!change) return; + change = filterChange(doc, change, true) + 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); + var split = sawReadOnlySpans && !ignoreReadOnly && removeReadOnlyRanges(doc, change.from, change.to) if (split) { for (var i = split.length - 1; i >= 0; --i) - makeChangeInner(doc, {from: split[i].from, to: split[i].to, text: i ? [""] : change.text}); + makeChangeInner(doc, {from: split[i].from, to: split[i].to, text: i ? [""] : change.text}) } else { - makeChangeInner(doc, change); + makeChangeInner(doc, change) } } function makeChangeInner(doc, change) { - if (change.text.length == 1 && change.text[0] == "" && cmp(change.from, change.to) == 0) return; - var selAfter = computeSelAfterChange(doc, change); - addChangeToHistory(doc, change, selAfter, doc.cm ? doc.cm.curOp.id : NaN); + if (change.text.length == 1 && change.text[0] == "" && cmp(change.from, change.to) == 0) return + var selAfter = computeSelAfterChange(doc, change) + addChangeToHistory(doc, change, selAfter, doc.cm ? doc.cm.curOp.id : NaN) - makeChangeSingleDoc(doc, change, selAfter, stretchSpansOverChange(doc, change)); - var rebased = []; + makeChangeSingleDoc(doc, change, selAfter, stretchSpansOverChange(doc, change)) + var rebased = [] linkedDocs(doc, function(doc, sharedHist) { if (!sharedHist && indexOf(rebased, doc.history) == -1) { - rebaseHist(doc.history, change); - rebased.push(doc.history); + rebaseHist(doc.history, change) + rebased.push(doc.history) } - makeChangeSingleDoc(doc, change, null, stretchSpansOverChange(doc, change)); - }); + makeChangeSingleDoc(doc, change, null, stretchSpansOverChange(doc, change)) + }) } // Revert a change stored in a document's history. export function makeChangeFromHistory(doc, type, allowSelectionOnly) { - if (doc.cm && doc.cm.state.suppressEdits && !allowSelectionOnly) return; + if (doc.cm && doc.cm.state.suppressEdits && !allowSelectionOnly) return - var hist = doc.history, event, selAfter = doc.sel; - var source = type == "undo" ? hist.done : hist.undone, dest = type == "undo" ? hist.undone : hist.done; + var hist = doc.history, event, selAfter = doc.sel + var source = type == "undo" ? hist.done : hist.undone, dest = type == "undo" ? hist.undone : hist.done // Verify that there is a useable event (so that ctrl-z won't // needlessly clear selection events) for (var i = 0; i < source.length; i++) { - event = source[i]; + event = source[i] if (allowSelectionOnly ? event.ranges && !event.equals(doc.sel) : !event.ranges) - break; + break } - if (i == source.length) return; - hist.lastOrigin = hist.lastSelOrigin = null; + if (i == source.length) return + hist.lastOrigin = hist.lastSelOrigin = null for (;;) { - event = source.pop(); + event = source.pop() if (event.ranges) { - pushSelectionToHistory(event, dest); + pushSelectionToHistory(event, dest) if (allowSelectionOnly && !event.equals(doc.sel)) { - setSelection(doc, event, {clearRedo: false}); - return; + setSelection(doc, event, {clearRedo: false}) + return } - selAfter = event; + selAfter = event } - else break; + else break } // Build up a reverse change object to add to the opposite history // stack (redo when undoing, and vice versa). - var antiChanges = []; - pushSelectionToHistory(selAfter, dest); - dest.push({changes: antiChanges, generation: hist.generation}); - hist.generation = event.generation || ++hist.maxGeneration; + var antiChanges = [] + pushSelectionToHistory(selAfter, dest) + dest.push({changes: antiChanges, generation: hist.generation}) + hist.generation = event.generation || ++hist.maxGeneration - var filter = hasHandler(doc, "beforeChange") || doc.cm && hasHandler(doc.cm, "beforeChange"); + 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; + var change = event.changes[i] + change.origin = type if (filter && !filterChange(doc, change, false)) { - source.length = 0; - return; + source.length = 0 + return } - antiChanges.push(historyChangeFromChange(doc, change)); + antiChanges.push(historyChangeFromChange(doc, change)) - var after = i ? computeSelAfterChange(doc, change) : lst(source); - makeChangeSingleDoc(doc, change, after, mergeOldSpans(doc, change)); - if (!i && doc.cm) doc.cm.scrollIntoView({from: change.from, to: changeEnd(change)}); - var rebased = []; + var after = i ? computeSelAfterChange(doc, change) : lst(source) + makeChangeSingleDoc(doc, change, after, mergeOldSpans(doc, change)) + if (!i && doc.cm) doc.cm.scrollIntoView({from: change.from, to: changeEnd(change)}) + var rebased = [] // Propagate to the linked documents linkedDocs(doc, function(doc, sharedHist) { if (!sharedHist && indexOf(rebased, doc.history) == -1) { - rebaseHist(doc.history, change); - rebased.push(doc.history); + rebaseHist(doc.history, change) + rebased.push(doc.history) } - makeChangeSingleDoc(doc, change, null, mergeOldSpans(doc, change)); - }); + makeChangeSingleDoc(doc, change, null, mergeOldSpans(doc, change)) + }) } } // Sub-views need their line numbers shifted when text is added // above or below them in the parent document. function shiftDoc(doc, distance) { - if (distance == 0) return; - doc.first += distance; + if (distance == 0) return + doc.first += distance doc.sel = new Selection(map(doc.sel.ranges, function(range) { return new Range(Pos(range.anchor.line + distance, range.anchor.ch), - Pos(range.head.line + distance, range.head.ch)); - }), doc.sel.primIndex); + Pos(range.head.line + distance, range.head.ch)) + }), doc.sel.primIndex) if (doc.cm) { - regChange(doc.cm, doc.first, doc.first - distance, distance); + regChange(doc.cm, doc.first, doc.first - distance, distance) for (var d = doc.cm.display, l = d.viewFrom; l < d.viewTo; l++) - regLineChange(doc.cm, l, "gutter"); + regLineChange(doc.cm, l, "gutter") } } @@ -167,111 +167,111 @@ function shiftDoc(doc, distance) { // (not linked ones). function makeChangeSingleDoc(doc, change, selAfter, spans) { if (doc.cm && !doc.cm.curOp) - return operation(doc.cm, makeChangeSingleDoc)(doc, change, selAfter, spans); + 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; + shiftDoc(doc, change.text.length - 1 - (change.to.line - change.from.line)) + return } - if (change.from.line > doc.lastLine()) 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); + var shift = change.text.length - 1 - (doc.first - change.from.line) + shiftDoc(doc, shift) change = {from: Pos(doc.first, 0), to: Pos(change.to.line + shift, change.to.ch), - text: [lst(change.text)], origin: change.origin}; + text: [lst(change.text)], origin: change.origin} } - var last = doc.lastLine(); + var last = doc.lastLine() if (change.to.line > last) { change = {from: change.from, to: Pos(last, getLine(doc, last).text.length), - text: [change.text[0]], origin: change.origin}; + text: [change.text[0]], origin: change.origin} } - change.removed = getBetween(doc, change.from, change.to); + change.removed = getBetween(doc, change.from, change.to) - if (!selAfter) selAfter = computeSelAfterChange(doc, change); - if (doc.cm) makeChangeSingleDocInEditor(doc.cm, change, spans); - else updateDoc(doc, change, spans); - setSelectionNoUndo(doc, selAfter, sel_dontScroll); + if (!selAfter) selAfter = computeSelAfterChange(doc, change) + if (doc.cm) makeChangeSingleDocInEditor(doc.cm, change, spans) + else updateDoc(doc, change, spans) + setSelectionNoUndo(doc, selAfter, sel_dontScroll) } // Handle the interaction of a change to a document with the editor // that this document is part of. function makeChangeSingleDocInEditor(cm, change, spans) { - var doc = cm.doc, display = cm.display, from = change.from, to = change.to; + var doc = cm.doc, display = cm.display, from = change.from, to = change.to - var recomputeMaxLength = false, checkWidthStart = from.line; + var recomputeMaxLength = false, checkWidthStart = from.line if (!cm.options.lineWrapping) { - checkWidthStart = lineNo(visualLine(getLine(doc, from.line))); + checkWidthStart = lineNo(visualLine(getLine(doc, from.line))) doc.iter(checkWidthStart, to.line + 1, function(line) { if (line == display.maxLine) { - recomputeMaxLength = true; - return true; + recomputeMaxLength = true + return true } - }); + }) } if (doc.sel.contains(change.from, change.to) > -1) - signalCursorActivity(cm); + signalCursorActivity(cm) - updateDoc(doc, change, spans, estimateHeight(cm)); + updateDoc(doc, change, spans, estimateHeight(cm)) if (!cm.options.lineWrapping) { doc.iter(checkWidthStart, from.line + change.text.length, function(line) { - var len = lineLength(line); + var len = lineLength(line) if (len > display.maxLineLength) { - display.maxLine = line; - display.maxLineLength = len; - display.maxLineChanged = true; - recomputeMaxLength = false; + display.maxLine = line + display.maxLineLength = len + display.maxLineChanged = true + recomputeMaxLength = false } - }); - if (recomputeMaxLength) cm.curOp.updateMaxLine = true; + }) + if (recomputeMaxLength) cm.curOp.updateMaxLine = true } // Adjust frontier, schedule worker - doc.frontier = Math.min(doc.frontier, from.line); - startWorker(cm, 400); + doc.frontier = Math.min(doc.frontier, from.line) + startWorker(cm, 400) - var lendiff = change.text.length - (to.line - from.line) - 1; + var lendiff = change.text.length - (to.line - from.line) - 1 // Remember that these lines changed, for updating the display if (change.full) - regChange(cm); + regChange(cm) else if (from.line == to.line && change.text.length == 1 && !isWholeLineUpdate(cm.doc, change)) - regLineChange(cm, from.line, "text"); + regLineChange(cm, from.line, "text") else - regChange(cm, from.line, to.line + 1, lendiff); + regChange(cm, from.line, to.line + 1, lendiff) - var changesHandler = hasHandler(cm, "changes"), changeHandler = hasHandler(cm, "change"); + var changesHandler = hasHandler(cm, "changes"), changeHandler = hasHandler(cm, "change") if (changeHandler || changesHandler) { var obj = { from: from, to: to, text: change.text, removed: change.removed, origin: change.origin - }; - if (changeHandler) signalLater(cm, "change", cm, obj); - if (changesHandler) (cm.curOp.changeObjs || (cm.curOp.changeObjs = [])).push(obj); + } + if (changeHandler) signalLater(cm, "change", cm, obj) + if (changesHandler) (cm.curOp.changeObjs || (cm.curOp.changeObjs = [])).push(obj) } - cm.display.selForContextMenu = null; + cm.display.selForContextMenu = null } export function replaceRange(doc, code, from, to, origin) { - if (!to) to = from; - if (cmp(to, from) < 0) { var tmp = to; to = from; from = tmp; } - if (typeof code == "string") code = doc.splitLines(code); - makeChange(doc, {from: from, to: to, text: code, origin: origin}); + if (!to) to = from + if (cmp(to, from) < 0) { var tmp = to; to = from; from = tmp } + if (typeof code == "string") code = doc.splitLines(code) + makeChange(doc, {from: from, to: to, text: code, origin: origin}) } // Rebasing/resetting history to deal with externally-sourced changes function rebaseHistSelSingle(pos, from, to, diff) { if (to < pos.line) { - pos.line += diff; + pos.line += diff } else if (from < pos.line) { - pos.line = from; - pos.ch = 0; + pos.line = from + pos.ch = 0 } } @@ -284,46 +284,46 @@ function rebaseHistSelSingle(pos, from, to, diff) { // 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; + var sub = array[i], ok = true if (sub.ranges) { - if (!sub.copied) { sub = array[i] = sub.deepCopy(); sub.copied = true; } + if (!sub.copied) { sub = array[i] = sub.deepCopy(); sub.copied = true } for (var j = 0; j < sub.ranges.length; j++) { - rebaseHistSelSingle(sub.ranges[j].anchor, from, to, diff); - rebaseHistSelSingle(sub.ranges[j].head, from, to, diff); + rebaseHistSelSingle(sub.ranges[j].anchor, from, to, diff) + rebaseHistSelSingle(sub.ranges[j].head, from, to, diff) } - continue; + continue } for (var j = 0; j < sub.changes.length; ++j) { - var cur = sub.changes[j]; + var cur = sub.changes[j] if (to < cur.from.line) { - cur.from = Pos(cur.from.line + diff, cur.from.ch); - cur.to = Pos(cur.to.line + diff, cur.to.ch); + cur.from = Pos(cur.from.line + diff, cur.from.ch) + cur.to = Pos(cur.to.line + diff, cur.to.ch) } else if (from <= cur.to.line) { - ok = false; - break; + ok = false + break } } if (!ok) { - array.splice(0, i + 1); - i = 0; + array.splice(0, i + 1) + i = 0 } } } 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); + 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) } // Utility for applying a change to a line by handle or number, // returning the number and optionally registering the line as // changed. export function changeLine(doc, handle, changeType, op) { - var no = handle, line = handle; - if (typeof handle == "number") line = getLine(doc, clipLine(doc, handle)); - else no = lineNo(handle); - if (no == null) return null; - if (op(line, no) && doc.cm) regLineChange(doc.cm, no, changeType); - return line; + var no = handle, line = handle + if (typeof handle == "number") line = getLine(doc, clipLine(doc, handle)) + else no = lineNo(handle) + if (no == null) return null + if (op(line, no) && doc.cm) regLineChange(doc.cm, no, changeType) + return line } diff --git a/src/model/chunk.js b/src/model/chunk.js index 28fd4153..0aee95fe 100644 --- a/src/model/chunk.js +++ b/src/model/chunk.js @@ -1,6 +1,6 @@ -import { cleanUpLine } from "../line/line_data"; -import { indexOf } from "../util/misc"; -import { signalLater } from "../util/operation_group"; +import { cleanUpLine } from "../line/line_data" +import { indexOf } from "../util/misc" +import { signalLater } from "../util/operation_group" // The document is represented as a BTree consisting of leaves, with // chunk of lines in them, and branches, with up to ten leaves or @@ -16,142 +16,142 @@ import { signalLater } from "../util/operation_group"; // See also http://marijnhaverbeke.nl/blog/codemirror-line-tree.html export function LeafChunk(lines) { - this.lines = lines; - this.parent = null; + this.lines = lines + this.parent = null for (var i = 0, height = 0; i < lines.length; ++i) { - lines[i].parent = this; - height += lines[i].height; + lines[i].parent = this + height += lines[i].height } - this.height = height; + this.height = height } LeafChunk.prototype = { - chunkSize: function() { return this.lines.length; }, + chunkSize: function() { return this.lines.length }, // Remove the n lines at offset 'at'. 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(line, "delete"); + var line = this.lines[i] + this.height -= line.height + cleanUpLine(line) + signalLater(line, "delete") } - this.lines.splice(at, n); + this.lines.splice(at, n) }, // Helper used to collapse a small branch into a single leaf. collapse: function(lines) { - lines.push.apply(lines, this.lines); + lines.push.apply(lines, this.lines) }, // Insert the given array of lines at offset 'at', count them as // having the given 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; i < lines.length; ++i) lines[i].parent = this; + this.height += height + this.lines = this.lines.slice(0, at).concat(lines).concat(this.lines.slice(at)) + for (var i = 0; i < lines.length; ++i) lines[i].parent = this }, // Used to iterate over a part of the tree. iterN: function(at, n, op) { for (var e = at + n; at < e; ++at) - if (op(this.lines[at])) return true; + if (op(this.lines[at])) return true } -}; +} export function BranchChunk(children) { - this.children = children; - var size = 0, height = 0; + this.children = children + var size = 0, height = 0 for (var i = 0; i < children.length; ++i) { - var ch = children[i]; - size += ch.chunkSize(); height += ch.height; - ch.parent = this; + var ch = children[i] + size += ch.chunkSize(); height += ch.height + ch.parent = this } - this.size = size; - this.height = height; - this.parent = null; + this.size = size + this.height = height + this.parent = null } BranchChunk.prototype = { - chunkSize: function() { return this.size; }, + chunkSize: function() { return this.size }, removeInner: function(at, n) { - this.size -= n; + this.size -= n for (var i = 0; i < this.children.length; ++i) { - var child = this.children[i], sz = child.chunkSize(); + var child = this.children[i], sz = child.chunkSize() if (at < sz) { - var rm = Math.min(n, sz - at), oldHeight = child.height; - 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; - at = 0; - } else at -= sz; + var rm = Math.min(n, sz - at), oldHeight = child.height + 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 + at = 0 + } else at -= sz } // If the result is smaller than 25 lines, ensure that it is a // single leaf node. if (this.size - n < 25 && (this.children.length > 1 || !(this.children[0] instanceof LeafChunk))) { - var lines = []; - this.collapse(lines); - this.children = [new LeafChunk(lines)]; - this.children[0].parent = this; + var lines = [] + this.collapse(lines) + this.children = [new LeafChunk(lines)] + this.children[0].parent = this } }, collapse: function(lines) { - for (var i = 0; i < this.children.length; ++i) this.children[i].collapse(lines); + for (var i = 0; i < this.children.length; ++i) this.children[i].collapse(lines) }, insertInner: function(at, lines, height) { - this.size += lines.length; - this.height += height; + this.size += lines.length + this.height += height for (var i = 0; i < this.children.length; ++i) { - var child = this.children[i], sz = child.chunkSize(); + var child = this.children[i], sz = child.chunkSize() if (at <= sz) { - child.insertInner(at, lines, height); + child.insertInner(at, lines, height) if (child.lines && child.lines.length > 50) { // To avoid memory thrashing when child.lines is huge (e.g. first view of a large file), it's never spliced. // Instead, small slices are taken. They're taken in order because sequential memory accesses are fastest. var remaining = child.lines.length % 25 + 25 for (var pos = remaining; pos < child.lines.length;) { - var leaf = new LeafChunk(child.lines.slice(pos, pos += 25)); - child.height -= leaf.height; - this.children.splice(++i, 0, leaf); - leaf.parent = this; + var leaf = new LeafChunk(child.lines.slice(pos, pos += 25)) + child.height -= leaf.height + this.children.splice(++i, 0, leaf) + leaf.parent = this } - child.lines = child.lines.slice(0, remaining); - this.maybeSpill(); + child.lines = child.lines.slice(0, remaining) + this.maybeSpill() } - break; + break } - at -= sz; + at -= sz } }, // When a node has grown, check whether it should be split. maybeSpill: function() { - if (this.children.length <= 10) return; - var me = this; + if (this.children.length <= 10) return + var me = this do { - var spilled = me.children.splice(me.children.length - 5, 5); - var sibling = new BranchChunk(spilled); + var spilled = me.children.splice(me.children.length - 5, 5) + var sibling = new BranchChunk(spilled) if (!me.parent) { // Become the parent node - var copy = new BranchChunk(me.children); - copy.parent = me; - me.children = [copy, sibling]; - me = copy; + var copy = new BranchChunk(me.children) + copy.parent = me + me.children = [copy, sibling] + me = copy } else { - me.size -= sibling.size; - me.height -= sibling.height; - var myIndex = indexOf(me.parent.children, me); - me.parent.children.splice(myIndex + 1, 0, sibling); + me.size -= sibling.size + me.height -= sibling.height + var myIndex = indexOf(me.parent.children, me) + me.parent.children.splice(myIndex + 1, 0, sibling) } - sibling.parent = me.parent; - } while (me.children.length > 10); - me.parent.maybeSpill(); + sibling.parent = me.parent + } while (me.children.length > 10) + me.parent.maybeSpill() }, iterN: function(at, n, op) { for (var i = 0; i < this.children.length; ++i) { - var child = this.children[i], sz = child.chunkSize(); + var child = this.children[i], sz = child.chunkSize() if (at < sz) { - var used = Math.min(n, sz - at); - if (child.iterN(at, used, op)) return true; - if ((n -= used) == 0) break; - at = 0; - } else at -= sz; + var used = Math.min(n, sz - at) + if (child.iterN(at, used, op)) return true + if ((n -= used) == 0) break + at = 0 + } else at -= sz } } -}; +} diff --git a/src/model/document_data.js b/src/model/document_data.js index 62ee034a..8e423baf 100644 --- a/src/model/document_data.js +++ b/src/model/document_data.js @@ -1,11 +1,11 @@ -import { loadMode } from "../display/mode_state"; -import { regChange } from "../display/view_tracking"; -import { Line, updateLine } from "../line/line_data"; -import { findMaxLine } from "../line/spans"; -import { getLine } from "../line/utils_line"; -import { estimateLineHeights } from "../measurement/position_measurement"; -import { lst } from "../util/misc"; -import { signalLater } from "../util/operation_group"; +import { loadMode } from "../display/mode_state" +import { regChange } from "../display/view_tracking" +import { Line, updateLine } from "../line/line_data" +import { findMaxLine } from "../line/spans" +import { getLine } from "../line/utils_line" +import { estimateLineHeights } from "../measurement/position_measurement" +import { lst } from "../util/misc" +import { signalLater } from "../util/operation_group" // DOCUMENT DATA STRUCTURE @@ -14,84 +14,84 @@ import { signalLater } from "../util/operation_group"; // widgets and marker elements with the text behave more intuitive. export function isWholeLineUpdate(doc, change) { return change.from.ch == 0 && change.to.ch == 0 && lst(change.text) == "" && - (!doc.cm || doc.cm.options.wholeLineUpdateBefore); + (!doc.cm || doc.cm.options.wholeLineUpdateBefore) } // Perform a change on the document data structure. export function updateDoc(doc, change, markedSpans, estimateHeight) { - function spansFor(n) {return markedSpans ? markedSpans[n] : null;} + function spansFor(n) {return markedSpans ? markedSpans[n] : null} function update(line, text, spans) { - updateLine(line, text, spans, estimateHeight); - signalLater(line, "change", line, change); + updateLine(line, text, spans, estimateHeight) + signalLater(line, "change", line, change) } function linesFor(start, end) { for (var i = start, result = []; i < end; ++i) - result.push(new Line(text[i], spansFor(i), estimateHeight)); - return result; + result.push(new Line(text[i], spansFor(i), estimateHeight)) + return result } - 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; + 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 // Adjust the line structure if (change.full) { - doc.insert(0, linesFor(0, text.length)); - doc.remove(text.length, doc.size - text.length); + doc.insert(0, linesFor(0, text.length)) + doc.remove(text.length, doc.size - text.length) } else if (isWholeLineUpdate(doc, change)) { // This is a whole-line replace. Treated specially to make // sure line objects move the way they are supposed to. - var added = linesFor(0, text.length - 1); - update(lastLine, lastLine.text, lastSpans); - if (nlines) doc.remove(from.line, nlines); - if (added.length) doc.insert(from.line, added); + var added = linesFor(0, text.length - 1) + 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) { - update(firstLine, firstLine.text.slice(0, from.ch) + lastText + firstLine.text.slice(to.ch), lastSpans); + update(firstLine, firstLine.text.slice(0, from.ch) + lastText + firstLine.text.slice(to.ch), lastSpans) } else { - var added = linesFor(1, text.length - 1); - 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); + var added = linesFor(1, text.length - 1) + 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) } } else if (text.length == 1) { - update(firstLine, firstLine.text.slice(0, from.ch) + text[0] + lastLine.text.slice(to.ch), spansFor(0)); - doc.remove(from.line + 1, nlines); + update(firstLine, firstLine.text.slice(0, from.ch) + text[0] + lastLine.text.slice(to.ch), spansFor(0)) + doc.remove(from.line + 1, nlines) } else { - update(firstLine, firstLine.text.slice(0, from.ch) + text[0], spansFor(0)); - update(lastLine, lastText + lastLine.text.slice(to.ch), lastSpans); - var added = linesFor(1, text.length - 1); - if (nlines > 1) doc.remove(from.line + 1, nlines - 1); - doc.insert(from.line + 1, added); + update(firstLine, firstLine.text.slice(0, from.ch) + text[0], spansFor(0)) + update(lastLine, lastText + lastLine.text.slice(to.ch), lastSpans) + var added = linesFor(1, text.length - 1) + if (nlines > 1) doc.remove(from.line + 1, nlines - 1) + doc.insert(from.line + 1, added) } - signalLater(doc, "change", doc, change); + signalLater(doc, "change", doc, change) } // Call f for all linked documents. export 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); + 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); + propagate(doc, null, true) } // Attach a document to an editor. export 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) findMaxLine(cm); - cm.options.mode = doc.modeOption; - regChange(cm); + 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) findMaxLine(cm) + cm.options.mode = doc.modeOption + regChange(cm) } diff --git a/src/model/history.js b/src/model/history.js index 8e344273..8cc77b1a 100644 --- a/src/model/history.js +++ b/src/model/history.js @@ -1,44 +1,44 @@ -import { cmp, copyPos } from "../line/pos"; -import { stretchSpansOverChange } from "../line/spans"; -import { getBetween } from "../line/utils_line"; -import { signal } from "../util/event"; -import { indexOf, lst } from "../util/misc"; +import { cmp, copyPos } from "../line/pos" +import { stretchSpansOverChange } from "../line/spans" +import { getBetween } from "../line/utils_line" +import { signal } from "../util/event" +import { indexOf, lst } from "../util/misc" -import { changeEnd } from "./change_measurement"; -import { linkedDocs } from "./document_data"; -import { Selection } from "./selection"; +import { changeEnd } from "./change_measurement" +import { linkedDocs } from "./document_data" +import { Selection } from "./selection" export function History(startGen) { // Arrays of change events and selections. Doing something adds an // event to done and clears undo. Undoing moves events from done // to undone, redoing moves them in the other direction. - this.done = []; this.undone = []; - this.undoDepth = Infinity; + this.done = []; this.undone = [] + this.undoDepth = Infinity // Used to track when changes can be merged into a single undo // event - this.lastModTime = this.lastSelTime = 0; - this.lastOp = this.lastSelOp = null; - this.lastOrigin = this.lastSelOrigin = null; + this.lastModTime = this.lastSelTime = 0 + this.lastOp = this.lastSelOp = null + this.lastOrigin = this.lastSelOrigin = null // Used by the isClean() method - this.generation = this.maxGeneration = startGen || 1; + this.generation = this.maxGeneration = startGen || 1 } // Create a history change event from an updateDoc-style change // object. export function historyChangeFromChange(doc, change) { - var histChange = {from: copyPos(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; + var histChange = {from: copyPos(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 } // Pop all selection events off the end of a history array. Stop at // a change event. function clearSelectionEvents(array) { while (array.length) { - var last = lst(array); - if (last.ranges) array.pop(); - else break; + var last = lst(array) + if (last.ranges) array.pop() + else break } } @@ -46,13 +46,13 @@ function clearSelectionEvents(array) { // events that are in the way. function lastChangeEvent(hist, force) { if (force) { - clearSelectionEvents(hist.done); - return lst(hist.done); + clearSelectionEvents(hist.done) + return lst(hist.done) } else if (hist.done.length && !lst(hist.done).ranges) { - return lst(hist.done); + return lst(hist.done) } else if (hist.done.length > 1 && !hist.done[hist.done.length - 2].ranges) { - hist.done.pop(); - return lst(hist.done); + hist.done.pop() + return lst(hist.done) } } @@ -60,9 +60,9 @@ function lastChangeEvent(hist, force) { // a single operation, or are close together with an origin that // allows merging (starting with "+") into a single event. export function addChangeToHistory(doc, change, selAfter, opId) { - var hist = doc.history; - hist.undone.length = 0; - var time = +new Date, cur; + var hist = doc.history + hist.undone.length = 0 + var time = +new Date, cur if ((hist.lastOp == opId || hist.lastOrigin == change.origin && change.origin && @@ -70,44 +70,44 @@ export function addChangeToHistory(doc, change, selAfter, opId) { change.origin.charAt(0) == "*")) && (cur = lastChangeEvent(hist, hist.lastOp == opId))) { // Merge this change into the last event - var last = lst(cur.changes); + var last = lst(cur.changes) if (cmp(change.from, change.to) == 0 && cmp(change.from, last.to) == 0) { // Optimized case for simple insertion -- don't want to add // new changesets for every character typed - last.to = changeEnd(change); + last.to = changeEnd(change) } else { // Add new sub-event - cur.changes.push(historyChangeFromChange(doc, change)); + cur.changes.push(historyChangeFromChange(doc, change)) } } else { // Can not be merged, start a new event. - var before = lst(hist.done); + var before = lst(hist.done) if (!before || !before.ranges) - pushSelectionToHistory(doc.sel, hist.done); + pushSelectionToHistory(doc.sel, hist.done) cur = {changes: [historyChangeFromChange(doc, change)], - generation: hist.generation}; - hist.done.push(cur); + generation: hist.generation} + hist.done.push(cur) while (hist.done.length > hist.undoDepth) { - hist.done.shift(); - if (!hist.done[0].ranges) hist.done.shift(); + hist.done.shift() + if (!hist.done[0].ranges) hist.done.shift() } } - hist.done.push(selAfter); - hist.generation = ++hist.maxGeneration; - hist.lastModTime = hist.lastSelTime = time; - hist.lastOp = hist.lastSelOp = opId; - hist.lastOrigin = hist.lastSelOrigin = change.origin; + hist.done.push(selAfter) + hist.generation = ++hist.maxGeneration + hist.lastModTime = hist.lastSelTime = time + hist.lastOp = hist.lastSelOp = opId + hist.lastOrigin = hist.lastSelOrigin = change.origin - if (!last) signal(doc, "historyAdded"); + if (!last) signal(doc, "historyAdded") } function selectionEventCanBeMerged(doc, origin, prev, sel) { - var ch = origin.charAt(0); + var ch = origin.charAt(0) return ch == "*" || ch == "+" && prev.ranges.length == sel.ranges.length && prev.somethingSelected() == sel.somethingSelected() && - new Date - doc.history.lastSelTime <= (doc.cm ? doc.cm.options.historyEventDelay : 500); + new Date - doc.history.lastSelTime <= (doc.cm ? doc.cm.options.historyEventDelay : 500) } // Called whenever the selection changes, sets the new selection as @@ -115,7 +115,7 @@ function selectionEventCanBeMerged(doc, origin, prev, sel) { // selection into the 'done' array when it was significantly // different (in number of selected ranges, emptiness, or time). export function addSelectionToHistory(doc, sel, opId, options) { - var hist = doc.history, origin = options && options.origin; + var hist = doc.history, origin = options && options.origin // A new event is started when the previous origin does not match // the current, or the origins don't allow matching. Origins @@ -125,51 +125,51 @@ export function addSelectionToHistory(doc, sel, opId, options) { (origin && hist.lastSelOrigin == origin && (hist.lastModTime == hist.lastSelTime && hist.lastOrigin == origin || selectionEventCanBeMerged(doc, origin, lst(hist.done), sel)))) - hist.done[hist.done.length - 1] = sel; + hist.done[hist.done.length - 1] = sel else - pushSelectionToHistory(sel, hist.done); + pushSelectionToHistory(sel, hist.done) - hist.lastSelTime = +new Date; - hist.lastSelOrigin = origin; - hist.lastSelOp = opId; + hist.lastSelTime = +new Date + hist.lastSelOrigin = origin + hist.lastSelOp = opId if (options && options.clearRedo !== false) - clearSelectionEvents(hist.undone); + clearSelectionEvents(hist.undone) } export function pushSelectionToHistory(sel, dest) { - var top = lst(dest); + var top = lst(dest) if (!(top && top.ranges && top.equals(sel))) - dest.push(sel); + dest.push(sel) } // Used to store marked span information in the history. function attachLocalSpans(doc, change, from, to) { - var existing = change["spans_" + doc.id], n = 0; + 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; - }); + (existing || (existing = change["spans_" + doc.id] = {}))[n] = line.markedSpans + ++n + }) } // When un/re-doing restores text containing marked spans, those // that have been explicitly cleared should not be restored. function removeClearedSpans(spans) { - if (!spans) return null; + 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]); + 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; + return !out ? spans : out.length ? out : null } // Retrieve and filter the old marked spans stored in a change event. function getOldSpans(doc, change) { - var found = change["spans_" + doc.id]; - if (!found) return null; + 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; + nw.push(removeClearedSpans(found[i])) + return nw } // Used for un/re-doing changes from the history. Combines the @@ -177,48 +177,48 @@ function getOldSpans(doc, change) { // existed in the history (so that deleting around a span and then // undoing brings back the span). export function mergeOldSpans(doc, change) { - var old = getOldSpans(doc, change); - var stretched = stretchSpansOverChange(doc, change); - if (!old) return stretched; - if (!stretched) return old; + var old = getOldSpans(doc, change) + var stretched = stretchSpansOverChange(doc, change) + if (!old) return stretched + if (!stretched) return old for (var i = 0; i < old.length; ++i) { - var oldCur = old[i], stretchCur = stretched[i]; + var oldCur = old[i], stretchCur = stretched[i] if (oldCur && stretchCur) { spans: for (var j = 0; j < stretchCur.length; ++j) { - var span = stretchCur[j]; + var span = stretchCur[j] for (var k = 0; k < oldCur.length; ++k) - if (oldCur[k].marker == span.marker) continue spans; - oldCur.push(span); + if (oldCur[k].marker == span.marker) continue spans + oldCur.push(span) } } else if (stretchCur) { - old[i] = stretchCur; + old[i] = stretchCur } } - return old; + return old } // Used both to provide a JSON-safe object in .getHistory, and, when // detaching a document, to split the history in two export function copyHistoryArray(events, newGroup, instantiateSel) { for (var i = 0, copy = []; i < events.length; ++i) { - var event = events[i]; + var event = events[i] if (event.ranges) { - copy.push(instantiateSel ? Selection.prototype.deepCopy.call(event) : event); - continue; + copy.push(instantiateSel ? Selection.prototype.deepCopy.call(event) : event) + continue } - var changes = event.changes, newChanges = []; - copy.push({changes: newChanges}); + var changes = event.changes, newChanges = [] + copy.push({changes: newChanges}) for (var j = 0; j < changes.length; ++j) { - var change = changes[j], m; - newChanges.push({from: change.from, to: change.to, text: change.text}); + 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]; + lst(newChanges)[prop] = change[prop] + delete change[prop] } } } } - return copy; + return copy } diff --git a/src/model/line_widget.js b/src/model/line_widget.js index 241327ce..b3b422aa 100644 --- a/src/model/line_widget.js +++ b/src/model/line_widget.js @@ -1,67 +1,67 @@ -import { runInOp } from "../display/operations"; -import { addToScrollPos } from "../display/scrolling"; -import { regLineChange } from "../display/view_tracking"; -import { heightAtLine, lineIsHidden } from "../line/spans"; -import { lineNo, updateLineHeight } from "../line/utils_line"; -import { widgetHeight } from "../measurement/widgets"; -import { changeLine } from "./changes"; -import { eventMixin } from "../util/event"; +import { runInOp } from "../display/operations" +import { addToScrollPos } from "../display/scrolling" +import { regLineChange } from "../display/view_tracking" +import { heightAtLine, lineIsHidden } from "../line/spans" +import { lineNo, updateLineHeight } from "../line/utils_line" +import { widgetHeight } from "../measurement/widgets" +import { changeLine } from "./changes" +import { eventMixin } from "../util/event" // Line widgets are block elements displayed above or below a line. export function LineWidget(doc, node, options) { if (options) for (var opt in options) if (options.hasOwnProperty(opt)) - this[opt] = options[opt]; - this.doc = doc; - this.node = node; + this[opt] = options[opt] + this.doc = doc + this.node = node } -eventMixin(LineWidget); +eventMixin(LineWidget) function adjustScrollWhenAboveVisible(cm, line, diff) { if (heightAtLine(line) < ((cm.curOp && cm.curOp.scrollTop) || cm.doc.scrollTop)) - addToScrollPos(cm, null, diff); + addToScrollPos(cm, null, diff) } LineWidget.prototype.clear = function() { - var cm = this.doc.cm, ws = this.line.widgets, line = this.line, no = lineNo(line); - if (no == null || !ws) return; - for (var i = 0; i < ws.length; ++i) if (ws[i] == this) ws.splice(i--, 1); - if (!ws.length) line.widgets = null; - var height = widgetHeight(this); - updateLineHeight(line, Math.max(0, line.height - height)); + var cm = this.doc.cm, ws = this.line.widgets, line = this.line, no = lineNo(line) + if (no == null || !ws) return + for (var i = 0; i < ws.length; ++i) if (ws[i] == this) ws.splice(i--, 1) + if (!ws.length) line.widgets = null + var height = widgetHeight(this) + updateLineHeight(line, Math.max(0, line.height - height)) if (cm) runInOp(cm, function() { - adjustScrollWhenAboveVisible(cm, line, -height); - regLineChange(cm, no, "widget"); - }); -}; + adjustScrollWhenAboveVisible(cm, line, -height) + regLineChange(cm, no, "widget") + }) +} LineWidget.prototype.changed = function() { - var oldH = this.height, cm = this.doc.cm, line = this.line; - this.height = null; - var diff = widgetHeight(this) - oldH; - if (!diff) return; - updateLineHeight(line, line.height + diff); + var oldH = this.height, cm = this.doc.cm, line = this.line + this.height = null + var diff = widgetHeight(this) - oldH + if (!diff) return + updateLineHeight(line, line.height + diff) if (cm) runInOp(cm, function() { - cm.curOp.forceUpdate = true; - adjustScrollWhenAboveVisible(cm, line, diff); - }); -}; + cm.curOp.forceUpdate = true + adjustScrollWhenAboveVisible(cm, line, diff) + }) +} export function addLineWidget(doc, handle, node, options) { - var widget = new LineWidget(doc, node, options); - var cm = doc.cm; - if (cm && widget.noHScroll) cm.display.alignWidgets = true; + var widget = new LineWidget(doc, node, options) + var cm = doc.cm + if (cm && widget.noHScroll) cm.display.alignWidgets = true changeLine(doc, handle, "widget", function(line) { - var widgets = line.widgets || (line.widgets = []); - if (widget.insertAt == null) widgets.push(widget); - else widgets.splice(Math.min(widgets.length - 1, Math.max(0, widget.insertAt)), 0, widget); - widget.line = line; + var widgets = line.widgets || (line.widgets = []) + if (widget.insertAt == null) widgets.push(widget) + else widgets.splice(Math.min(widgets.length - 1, Math.max(0, widget.insertAt)), 0, widget) + widget.line = line if (cm && !lineIsHidden(doc, line)) { - var aboveVisible = heightAtLine(line) < doc.scrollTop; - updateLineHeight(line, line.height + widgetHeight(widget)); - if (aboveVisible) addToScrollPos(cm, null, widget.height); - cm.curOp.forceUpdate = true; + var aboveVisible = heightAtLine(line) < doc.scrollTop + updateLineHeight(line, line.height + widgetHeight(widget)) + if (aboveVisible) addToScrollPos(cm, null, widget.height) + cm.curOp.forceUpdate = true } - return true; - }); - return widget; + return true + }) + return widget } diff --git a/src/model/mark_text.js b/src/model/mark_text.js index 7a334e53..c103a574 100644 --- a/src/model/mark_text.js +++ b/src/model/mark_text.js @@ -1,19 +1,19 @@ -import { elt } from "../util/dom"; -import { eventMixin, hasHandler, on } from "../util/event"; -import { endOperation, operation, runInOp, startOperation } from "../display/operations"; -import { clipPos, cmp, Pos } from "../line/pos"; -import { lineNo, updateLineHeight } from "../line/utils_line"; -import { clearLineMeasurementCacheFor, findViewForLine, textHeight } from "../measurement/position_measurement"; -import { seeReadOnlySpans, seeCollapsedSpans } from "../line/saw_special_spans"; -import { addMarkedSpan, conflictingCollapsedRange, getMarkedSpanFor, lineIsHidden, lineLength, MarkedSpan, removeMarkedSpan, visualLine } from "../line/spans"; -import { copyObj, indexOf, lst } from "../util/misc"; -import { signalLater } from "../util/operation_group"; -import { widgetHeight } from "../measurement/widgets"; -import { regChange, regLineChange } from "../display/view_tracking"; +import { elt } from "../util/dom" +import { eventMixin, hasHandler, on } from "../util/event" +import { endOperation, operation, runInOp, startOperation } from "../display/operations" +import { clipPos, cmp, Pos } from "../line/pos" +import { lineNo, updateLineHeight } from "../line/utils_line" +import { clearLineMeasurementCacheFor, findViewForLine, textHeight } from "../measurement/position_measurement" +import { seeReadOnlySpans, seeCollapsedSpans } from "../line/saw_special_spans" +import { addMarkedSpan, conflictingCollapsedRange, getMarkedSpanFor, lineIsHidden, lineLength, MarkedSpan, removeMarkedSpan, visualLine } from "../line/spans" +import { copyObj, indexOf, lst } from "../util/misc" +import { signalLater } from "../util/operation_group" +import { widgetHeight } from "../measurement/widgets" +import { regChange, regLineChange } from "../display/view_tracking" -import { linkedDocs } from "./document_data"; -import { addChangeToHistory } from "./history"; -import { reCheckSelection } from "./selection_updates"; +import { linkedDocs } from "./document_data" +import { addChangeToHistory } from "./history" +import { reCheckSelection } from "./selection_updates" // TEXTMARKERS @@ -27,58 +27,58 @@ import { reCheckSelection } from "./selection_updates"; // marker continues beyond the start/end of the line. Markers have // links back to the lines they currently touch. -var nextMarkerId = 0; +var nextMarkerId = 0 export function TextMarker(doc, type) { - this.lines = []; - this.type = type; - this.doc = doc; - this.id = ++nextMarkerId; + this.lines = [] + this.type = type + this.doc = doc + this.id = ++nextMarkerId } -eventMixin(TextMarker); +eventMixin(TextMarker) // Clear the marker. TextMarker.prototype.clear = function() { - if (this.explicitlyCleared) return; - var cm = this.doc.cm, withOp = cm && !cm.curOp; - if (withOp) startOperation(cm); + if (this.explicitlyCleared) return + var cm = this.doc.cm, withOp = cm && !cm.curOp + if (withOp) startOperation(cm) if (hasHandler(this, "clear")) { - var found = this.find(); - if (found) signalLater(this, "clear", found.from, found.to); + var found = this.find() + if (found) signalLater(this, "clear", found.from, found.to) } - var min = null, max = null; + 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); - if (cm && !this.collapsed) regLineChange(cm, lineNo(line), "text"); + var line = this.lines[i] + var span = getMarkedSpanFor(line.markedSpans, this) + if (cm && !this.collapsed) regLineChange(cm, lineNo(line), "text") else if (cm) { - if (span.to != null) max = lineNo(line); - if (span.from != null) min = lineNo(line); + if (span.to != null) max = lineNo(line) + if (span.from != null) min = lineNo(line) } - line.markedSpans = removeMarkedSpan(line.markedSpans, span); + line.markedSpans = removeMarkedSpan(line.markedSpans, span) if (span.from == null && this.collapsed && !lineIsHidden(this.doc, line) && cm) - updateLineHeight(line, textHeight(cm.display)); + updateLineHeight(line, textHeight(cm.display)) } if (cm && this.collapsed && !cm.options.lineWrapping) for (var i = 0; i < this.lines.length; ++i) { - var visual = visualLine(this.lines[i]), len = lineLength(visual); + var visual = visualLine(this.lines[i]), len = lineLength(visual) if (len > cm.display.maxLineLength) { - cm.display.maxLine = visual; - cm.display.maxLineLength = len; - cm.display.maxLineChanged = true; + cm.display.maxLine = visual + cm.display.maxLineLength = len + cm.display.maxLineChanged = true } } - if (min != null && cm && this.collapsed) regChange(cm, min, max + 1); - this.lines.length = 0; - this.explicitlyCleared = true; + if (min != null && cm && this.collapsed) regChange(cm, min, max + 1) + this.lines.length = 0 + this.explicitlyCleared = true if (this.atomic && this.doc.cantEdit) { - this.doc.cantEdit = false; - if (cm) reCheckSelection(cm.doc); + this.doc.cantEdit = false + if (cm) reCheckSelection(cm.doc) } - if (cm) signalLater(cm, "markerCleared", cm, this); - if (withOp) endOperation(cm); - if (this.parent) this.parent.clear(); -}; + if (cm) signalLater(cm, "markerCleared", cm, this) + if (withOp) endOperation(cm) + if (this.parent) this.parent.clear() +} // Find the position of the marker in the document. Returns a {from, // to} object by default. Side can be passed to get a specific side @@ -86,135 +86,135 @@ TextMarker.prototype.clear = function() { // Pos objects returned contain a line object, rather than a line // number (used to prevent looking up the same line twice). TextMarker.prototype.find = function(side, lineObj) { - if (side == null && this.type == "bookmark") side = 1; - var from, to; + if (side == null && this.type == "bookmark") side = 1 + var from, to for (var i = 0; i < this.lines.length; ++i) { - var line = this.lines[i]; - var span = getMarkedSpanFor(line.markedSpans, this); + var line = this.lines[i] + var span = getMarkedSpanFor(line.markedSpans, this) if (span.from != null) { - from = Pos(lineObj ? line : lineNo(line), span.from); - if (side == -1) return from; + from = Pos(lineObj ? line : lineNo(line), span.from) + if (side == -1) return from } if (span.to != null) { - to = Pos(lineObj ? line : lineNo(line), span.to); - if (side == 1) return to; + to = Pos(lineObj ? line : lineNo(line), span.to) + if (side == 1) return to } } - return from && {from: from, to: to}; -}; + return from && {from: from, to: to} +} // Signals that the marker's widget changed, and surrounding layout // should be recomputed. TextMarker.prototype.changed = function() { - var pos = this.find(-1, true), widget = this, cm = this.doc.cm; - if (!pos || !cm) return; + var pos = this.find(-1, true), widget = this, cm = this.doc.cm + if (!pos || !cm) return runInOp(cm, function() { - var line = pos.line, lineN = lineNo(pos.line); - var view = findViewForLine(cm, lineN); + var line = pos.line, lineN = lineNo(pos.line) + var view = findViewForLine(cm, lineN) if (view) { - clearLineMeasurementCacheFor(view); - cm.curOp.selectionChanged = cm.curOp.forceUpdate = true; + clearLineMeasurementCacheFor(view) + cm.curOp.selectionChanged = cm.curOp.forceUpdate = true } - cm.curOp.updateMaxLine = true; + cm.curOp.updateMaxLine = true if (!lineIsHidden(widget.doc, line) && widget.height != null) { - var oldHeight = widget.height; - widget.height = null; - var dHeight = widgetHeight(widget) - oldHeight; + var oldHeight = widget.height + widget.height = null + var dHeight = widgetHeight(widget) - oldHeight if (dHeight) - updateLineHeight(line, line.height + dHeight); + updateLineHeight(line, line.height + dHeight) } - }); -}; + }) +} TextMarker.prototype.attachLine = function(line) { if (!this.lines.length && this.doc.cm) { - var op = this.doc.cm.curOp; + var op = this.doc.cm.curOp if (!op.maybeHiddenMarkers || indexOf(op.maybeHiddenMarkers, this) == -1) - (op.maybeUnhiddenMarkers || (op.maybeUnhiddenMarkers = [])).push(this); + (op.maybeUnhiddenMarkers || (op.maybeUnhiddenMarkers = [])).push(this) } - this.lines.push(line); -}; + this.lines.push(line) +} TextMarker.prototype.detachLine = function(line) { - this.lines.splice(indexOf(this.lines, line), 1); + 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); + var op = this.doc.cm.curOp + ;(op.maybeHiddenMarkers || (op.maybeHiddenMarkers = [])).push(this) } -}; +} // Collapsed markers have unique ids, in order to be able to order // them, which is needed for uniquely determining an outer marker // when they overlap (they may nest, but not partially overlap). -var nextMarkerId = 0; +var nextMarkerId = 0 // Create a marker, wire it up to the right lines, and export function markText(doc, from, to, options, type) { // Shared markers (across linked documents) are handled separately // (markTextShared will call out to this again, once per // document). - if (options && options.shared) return markTextShared(doc, from, to, options, type); + if (options && options.shared) return markTextShared(doc, from, to, options, type) // Ensure we are in an operation. - if (doc.cm && !doc.cm.curOp) return operation(doc.cm, markText)(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), diff = cmp(from, to); - if (options) copyObj(options, marker, false); + var marker = new TextMarker(doc, type), diff = cmp(from, to) + if (options) copyObj(options, marker, false) // Don't connect empty markers unless clearWhenEmpty is false if (diff > 0 || diff == 0 && marker.clearWhenEmpty !== false) - return marker; + return marker if (marker.replacedWith) { // Showing up as a widget implies collapsed (widget replaces text) - marker.collapsed = true; - marker.widgetNode = elt("span", [marker.replacedWith], "CodeMirror-widget"); - if (!options.handleMouseEvents) marker.widgetNode.setAttribute("cm-ignore-events", "true"); - if (options.insertLeft) marker.widgetNode.insertLeft = true; + marker.collapsed = true + marker.widgetNode = elt("span", [marker.replacedWith], "CodeMirror-widget") + if (!options.handleMouseEvents) marker.widgetNode.setAttribute("cm-ignore-events", "true") + if (options.insertLeft) marker.widgetNode.insertLeft = true } if (marker.collapsed) { if (conflictingCollapsedRange(doc, from.line, from, to, marker) || from.line != to.line && conflictingCollapsedRange(doc, to.line, from, to, marker)) - throw new Error("Inserting collapsed marker partially overlapping an existing one"); - seeCollapsedSpans(); + throw new Error("Inserting collapsed marker partially overlapping an existing one") + seeCollapsedSpans() } if (marker.addToHistory) - addChangeToHistory(doc, {from: from, to: to, origin: "markText"}, doc.sel, NaN); + addChangeToHistory(doc, {from: from, to: to, origin: "markText"}, doc.sel, NaN) - var curLine = from.line, cm = doc.cm, updateMaxLine; + var curLine = from.line, cm = doc.cm, updateMaxLine doc.iter(curLine, to.line + 1, function(line) { if (cm && marker.collapsed && !cm.options.lineWrapping && visualLine(line) == cm.display.maxLine) - updateMaxLine = true; - if (marker.collapsed && curLine != from.line) updateLineHeight(line, 0); + updateMaxLine = true + if (marker.collapsed && curLine != from.line) updateLineHeight(line, 0) addMarkedSpan(line, new MarkedSpan(marker, curLine == from.line ? from.ch : null, - curLine == to.line ? to.ch : null)); - ++curLine; - }); + curLine == to.line ? to.ch : null)) + ++curLine + }) // lineIsHidden depends on the presence of the spans, so needs a second pass if (marker.collapsed) doc.iter(from.line, to.line + 1, function(line) { - if (lineIsHidden(doc, line)) updateLineHeight(line, 0); - }); + if (lineIsHidden(doc, line)) updateLineHeight(line, 0) + }) - if (marker.clearOnEnter) on(marker, "beforeCursorEnter", function() { marker.clear(); }); + if (marker.clearOnEnter) on(marker, "beforeCursorEnter", function() { marker.clear() }) if (marker.readOnly) { - seeReadOnlySpans(); + seeReadOnlySpans() if (doc.history.done.length || doc.history.undone.length) - doc.clearHistory(); + doc.clearHistory() } if (marker.collapsed) { - marker.id = ++nextMarkerId; - marker.atomic = true; + marker.id = ++nextMarkerId + marker.atomic = true } if (cm) { // Sync editor state - if (updateMaxLine) cm.curOp.updateMaxLine = true; + if (updateMaxLine) cm.curOp.updateMaxLine = true if (marker.collapsed) - regChange(cm, from.line, to.line + 1); + regChange(cm, from.line, to.line + 1) else if (marker.className || marker.title || marker.startStyle || marker.endStyle || marker.css) - for (var i = from.line; i <= to.line; i++) regLineChange(cm, i, "text"); - if (marker.atomic) reCheckSelection(cm.doc); - signalLater(cm, "markerAdded", cm, marker); + for (var i = from.line; i <= to.line; i++) regLineChange(cm, i, "text") + if (marker.atomic) reCheckSelection(cm.doc) + signalLater(cm, "markerAdded", cm, marker) } - return marker; + return marker } // SHARED TEXTMARKERS @@ -223,65 +223,65 @@ export function markText(doc, from, to, options, type) { // implemented as a meta-marker-object controlling multiple normal // markers. export function SharedTextMarker(markers, primary) { - this.markers = markers; - this.primary = primary; + this.markers = markers + this.primary = primary for (var i = 0; i < markers.length; ++i) - markers[i].parent = this; + markers[i].parent = this } -eventMixin(SharedTextMarker); +eventMixin(SharedTextMarker) SharedTextMarker.prototype.clear = function() { - if (this.explicitlyCleared) return; - this.explicitlyCleared = true; + if (this.explicitlyCleared) return + this.explicitlyCleared = true for (var i = 0; i < this.markers.length; ++i) - this.markers[i].clear(); - signalLater(this, "clear"); -}; + this.markers[i].clear() + signalLater(this, "clear") +} SharedTextMarker.prototype.find = function(side, lineObj) { - return this.primary.find(side, lineObj); -}; + return this.primary.find(side, lineObj) +} function markTextShared(doc, from, to, options, type) { - options = copyObj(options); - options.shared = false; - var markers = [markText(doc, from, to, options, type)], primary = markers[0]; - var widget = options.widgetNode; + options = copyObj(options) + options.shared = false + var markers = [markText(doc, from, to, options, type)], primary = markers[0] + var widget = options.widgetNode linkedDocs(doc, function(doc) { - if (widget) options.widgetNode = widget.cloneNode(true); - markers.push(markText(doc, clipPos(doc, from), clipPos(doc, to), options, type)); + if (widget) options.widgetNode = 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; - primary = lst(markers); - }); - return new SharedTextMarker(markers, primary); + if (doc.linked[i].isParent) return + primary = lst(markers) + }) + return new SharedTextMarker(markers, primary) } export function findSharedMarkers(doc) { return doc.findMarks(Pos(doc.first, 0), doc.clipPos(Pos(doc.lastLine())), - function(m) { return m.parent; }); + function(m) { return m.parent }) } export function copySharedMarkers(doc, markers) { for (var i = 0; i < markers.length; i++) { - var marker = markers[i], pos = marker.find(); - var mFrom = doc.clipPos(pos.from), mTo = doc.clipPos(pos.to); + var marker = markers[i], pos = marker.find() + var mFrom = doc.clipPos(pos.from), mTo = doc.clipPos(pos.to) if (cmp(mFrom, mTo)) { - var subMark = markText(doc, mFrom, mTo, marker.primary, marker.primary.type); - marker.markers.push(subMark); - subMark.parent = marker; + var subMark = markText(doc, mFrom, mTo, marker.primary, marker.primary.type) + marker.markers.push(subMark) + subMark.parent = marker } } } export function detachSharedMarkers(markers) { for (var i = 0; i < markers.length; i++) { - var marker = markers[i], linked = [marker.primary.doc]; - linkedDocs(marker.primary.doc, function(d) { linked.push(d); }); + var marker = markers[i], linked = [marker.primary.doc] + linkedDocs(marker.primary.doc, function(d) { linked.push(d) }) for (var j = 0; j < marker.markers.length; j++) { - var subMarker = marker.markers[j]; + var subMarker = marker.markers[j] if (indexOf(linked, subMarker.doc) == -1) { - subMarker.parent = null; - marker.markers.splice(j--, 1); + subMarker.parent = null + marker.markers.splice(j--, 1) } } } diff --git a/src/model/selection.js b/src/model/selection.js index ce3c228c..c75fcd61 100644 --- a/src/model/selection.js +++ b/src/model/selection.js @@ -1,5 +1,5 @@ -import { cmp, copyPos, maxPos, minPos } from "../line/pos"; -import { indexOf } from "../util/misc"; +import { cmp, copyPos, maxPos, minPos } from "../line/pos" +import { indexOf } from "../util/misc" // Selection objects are immutable. A new one is created every time // the selection changes. A selection is one or more non-overlapping @@ -7,73 +7,73 @@ import { indexOf } from "../util/misc"; // which one is the primary selection (the one that's scrolled into // view, that getCursor returns, etc). export function Selection(ranges, primIndex) { - this.ranges = ranges; - this.primIndex = primIndex; + this.ranges = ranges + this.primIndex = primIndex } Selection.prototype = { - primary: function() { return this.ranges[this.primIndex]; }, + primary: function() { return this.ranges[this.primIndex] }, equals: function(other) { - if (other == this) return true; - if (other.primIndex != this.primIndex || other.ranges.length != this.ranges.length) return false; + if (other == this) return true + if (other.primIndex != this.primIndex || other.ranges.length != this.ranges.length) return false for (var i = 0; i < this.ranges.length; i++) { - var here = this.ranges[i], there = other.ranges[i]; - if (cmp(here.anchor, there.anchor) != 0 || cmp(here.head, there.head) != 0) return false; + var here = this.ranges[i], there = other.ranges[i] + if (cmp(here.anchor, there.anchor) != 0 || cmp(here.head, there.head) != 0) return false } - return true; + return true }, deepCopy: function() { for (var out = [], i = 0; i < this.ranges.length; i++) - out[i] = new Range(copyPos(this.ranges[i].anchor), copyPos(this.ranges[i].head)); - return new Selection(out, this.primIndex); + out[i] = new Range(copyPos(this.ranges[i].anchor), copyPos(this.ranges[i].head)) + return new Selection(out, this.primIndex) }, somethingSelected: function() { for (var i = 0; i < this.ranges.length; i++) - if (!this.ranges[i].empty()) return true; - return false; + if (!this.ranges[i].empty()) return true + return false }, contains: function(pos, end) { - if (!end) end = pos; + if (!end) end = pos for (var i = 0; i < this.ranges.length; i++) { - var range = this.ranges[i]; + var range = this.ranges[i] if (cmp(end, range.from()) >= 0 && cmp(pos, range.to()) <= 0) - return i; + return i } - return -1; + return -1 } -}; +} export function Range(anchor, head) { - this.anchor = anchor; this.head = head; + this.anchor = anchor; this.head = head } Range.prototype = { - from: function() { return minPos(this.anchor, this.head); }, - to: function() { return maxPos(this.anchor, this.head); }, + from: function() { return minPos(this.anchor, this.head) }, + to: function() { return maxPos(this.anchor, this.head) }, empty: function() { - return this.head.line == this.anchor.line && this.head.ch == this.anchor.ch; + return this.head.line == this.anchor.line && this.head.ch == this.anchor.ch } -}; +} // Take an unsorted, potentially overlapping set of ranges, and // build a selection out of it. 'Consumes' ranges array (modifying // it). export function normalizeSelection(ranges, primIndex) { - var prim = ranges[primIndex]; - ranges.sort(function(a, b) { return cmp(a.from(), b.from()); }); - primIndex = indexOf(ranges, prim); + var prim = ranges[primIndex] + ranges.sort(function(a, b) { return cmp(a.from(), b.from()) }) + primIndex = indexOf(ranges, prim) for (var i = 1; i < ranges.length; i++) { - var cur = ranges[i], prev = ranges[i - 1]; + var cur = ranges[i], prev = ranges[i - 1] if (cmp(prev.to(), cur.from()) >= 0) { - var from = minPos(prev.from(), cur.from()), to = maxPos(prev.to(), cur.to()); - var inv = prev.empty() ? cur.from() == cur.head : prev.from() == prev.head; - if (i <= primIndex) --primIndex; - ranges.splice(--i, 2, new Range(inv ? to : from, inv ? from : to)); + var from = minPos(prev.from(), cur.from()), to = maxPos(prev.to(), cur.to()) + var inv = prev.empty() ? cur.from() == cur.head : prev.from() == prev.head + if (i <= primIndex) --primIndex + ranges.splice(--i, 2, new Range(inv ? to : from, inv ? from : to)) } } - return new Selection(ranges, primIndex); + return new Selection(ranges, primIndex) } export function simpleSelection(anchor, head) { - return new Selection([new Range(anchor, head || anchor)], 0); + return new Selection([new Range(anchor, head || anchor)], 0) } diff --git a/src/model/selection_updates.js b/src/model/selection_updates.js index e105afa9..640af1f2 100644 --- a/src/model/selection_updates.js +++ b/src/model/selection_updates.js @@ -1,12 +1,12 @@ -import { signalLater } from "../util/operation_group"; -import { ensureCursorVisible } from "../display/scrolling"; -import { clipPos, cmp, Pos } from "../line/pos"; -import { getLine } from "../line/utils_line"; -import { hasHandler, signal, signalCursorActivity } from "../util/event"; -import { lst, sel_dontScroll } from "../util/misc"; +import { signalLater } from "../util/operation_group" +import { ensureCursorVisible } from "../display/scrolling" +import { clipPos, cmp, Pos } from "../line/pos" +import { getLine } from "../line/utils_line" +import { hasHandler, signal, signalCursorActivity } from "../util/event" +import { lst, sel_dontScroll } from "../util/misc" -import { addSelectionToHistory } from "./history"; -import { normalizeSelection, Range, Selection, simpleSelection } from "./selection"; +import { addSelectionToHistory } from "./history" +import { normalizeSelection, Range, Selection, simpleSelection } from "./selection" // The 'scroll' parameter given to many of these indicated whether // the new cursor position should be scrolled into view after @@ -18,46 +18,46 @@ import { normalizeSelection, Range, Selection, simpleSelection } from "./selecti // Used for cursor motion and such. export function extendRange(doc, range, head, other) { if (doc.cm && doc.cm.display.shift || doc.extend) { - var anchor = range.anchor; + var anchor = range.anchor if (other) { - var posBefore = cmp(head, anchor) < 0; + var posBefore = cmp(head, anchor) < 0 if (posBefore != (cmp(other, anchor) < 0)) { - anchor = head; - head = other; + anchor = head + head = other } else if (posBefore != (cmp(head, other) < 0)) { - head = other; + head = other } } - return new Range(anchor, head); + return new Range(anchor, head) } else { - return new Range(other || head, head); + return new Range(other || head, head) } } // Extend the primary selection range, discard the rest. export function extendSelection(doc, head, other, options) { - setSelection(doc, new Selection([extendRange(doc, doc.sel.primary(), head, other)], 0), options); + setSelection(doc, new Selection([extendRange(doc, doc.sel.primary(), head, other)], 0), options) } // Extend all selections (pos is an array of selections with length // equal the number of selections) export function extendSelections(doc, heads, options) { for (var out = [], i = 0; i < doc.sel.ranges.length; i++) - out[i] = extendRange(doc, doc.sel.ranges[i], heads[i], null); - var newSel = normalizeSelection(out, doc.sel.primIndex); - setSelection(doc, newSel, options); + out[i] = extendRange(doc, doc.sel.ranges[i], heads[i], null) + var newSel = normalizeSelection(out, doc.sel.primIndex) + setSelection(doc, newSel, options) } // Updates a single range in the selection. export function replaceOneSelection(doc, i, range, options) { - var ranges = doc.sel.ranges.slice(0); - ranges[i] = range; - setSelection(doc, normalizeSelection(ranges, doc.sel.primIndex), options); + var ranges = doc.sel.ranges.slice(0) + ranges[i] = range + setSelection(doc, normalizeSelection(ranges, doc.sel.primIndex), options) } // Reset the selection to a single range. export function setSimpleSelection(doc, anchor, head, options) { - setSelection(doc, simpleSelection(anchor, head), options); + setSelection(doc, simpleSelection(anchor, head), options) } // Give beforeSelectionChange handlers a change to influence a @@ -66,140 +66,140 @@ function filterSelectionChange(doc, sel, options) { var obj = { ranges: sel.ranges, update: function(ranges) { - this.ranges = []; + this.ranges = [] for (var i = 0; i < ranges.length; i++) this.ranges[i] = new Range(clipPos(doc, ranges[i].anchor), - clipPos(doc, ranges[i].head)); + clipPos(doc, ranges[i].head)) }, origin: options && options.origin - }; - signal(doc, "beforeSelectionChange", doc, obj); - if (doc.cm) signal(doc.cm, "beforeSelectionChange", doc.cm, obj); - if (obj.ranges != sel.ranges) return normalizeSelection(obj.ranges, obj.ranges.length - 1); - else return sel; + } + signal(doc, "beforeSelectionChange", doc, obj) + if (doc.cm) signal(doc.cm, "beforeSelectionChange", doc.cm, obj) + if (obj.ranges != sel.ranges) return normalizeSelection(obj.ranges, obj.ranges.length - 1) + else return sel } export function setSelectionReplaceHistory(doc, sel, options) { - var done = doc.history.done, last = lst(done); + var done = doc.history.done, last = lst(done) if (last && last.ranges) { - done[done.length - 1] = sel; - setSelectionNoUndo(doc, sel, options); + done[done.length - 1] = sel + setSelectionNoUndo(doc, sel, options) } else { - setSelection(doc, sel, options); + setSelection(doc, sel, options) } } // Set a new selection. export function setSelection(doc, sel, options) { - setSelectionNoUndo(doc, sel, options); - addSelectionToHistory(doc, doc.sel, doc.cm ? doc.cm.curOp.id : NaN, options); + setSelectionNoUndo(doc, sel, options) + addSelectionToHistory(doc, doc.sel, doc.cm ? doc.cm.curOp.id : NaN, options) } export function setSelectionNoUndo(doc, sel, options) { if (hasHandler(doc, "beforeSelectionChange") || doc.cm && hasHandler(doc.cm, "beforeSelectionChange")) - sel = filterSelectionChange(doc, sel, options); + sel = filterSelectionChange(doc, sel, options) var bias = options && options.bias || - (cmp(sel.primary().head, doc.sel.primary().head) < 0 ? -1 : 1); - setSelectionInner(doc, skipAtomicInSelection(doc, sel, bias, true)); + (cmp(sel.primary().head, doc.sel.primary().head) < 0 ? -1 : 1) + setSelectionInner(doc, skipAtomicInSelection(doc, sel, bias, true)) if (!(options && options.scroll === false) && doc.cm) - ensureCursorVisible(doc.cm); + ensureCursorVisible(doc.cm) } function setSelectionInner(doc, sel) { - if (sel.equals(doc.sel)) return; + if (sel.equals(doc.sel)) return - doc.sel = sel; + doc.sel = sel if (doc.cm) { - doc.cm.curOp.updateInput = doc.cm.curOp.selectionChanged = true; - signalCursorActivity(doc.cm); + doc.cm.curOp.updateInput = doc.cm.curOp.selectionChanged = true + signalCursorActivity(doc.cm) } - signalLater(doc, "cursorActivity", doc); + signalLater(doc, "cursorActivity", doc) } // Verify that the selection does not partially select any atomic // marked ranges. export function reCheckSelection(doc) { - setSelectionInner(doc, skipAtomicInSelection(doc, doc.sel, null, false), sel_dontScroll); + setSelectionInner(doc, skipAtomicInSelection(doc, doc.sel, null, false), sel_dontScroll) } // Return a selection that does not partially select any atomic // ranges. function skipAtomicInSelection(doc, sel, bias, mayClear) { - var out; + var out for (var i = 0; i < sel.ranges.length; i++) { - var range = sel.ranges[i]; - var old = sel.ranges.length == doc.sel.ranges.length && doc.sel.ranges[i]; - var newAnchor = skipAtomic(doc, range.anchor, old && old.anchor, bias, mayClear); - var newHead = skipAtomic(doc, range.head, old && old.head, bias, mayClear); + var range = sel.ranges[i] + var old = sel.ranges.length == doc.sel.ranges.length && doc.sel.ranges[i] + var newAnchor = skipAtomic(doc, range.anchor, old && old.anchor, bias, mayClear) + var newHead = skipAtomic(doc, range.head, old && old.head, bias, mayClear) if (out || newAnchor != range.anchor || newHead != range.head) { - if (!out) out = sel.ranges.slice(0, i); - out[i] = new Range(newAnchor, newHead); + if (!out) out = sel.ranges.slice(0, i) + out[i] = new Range(newAnchor, newHead) } } - return out ? normalizeSelection(out, sel.primIndex) : sel; + return out ? normalizeSelection(out, sel.primIndex) : sel } function skipAtomicInner(doc, pos, oldPos, dir, mayClear) { - var line = getLine(doc, pos.line); + var line = getLine(doc, pos.line) if (line.markedSpans) for (var i = 0; i < line.markedSpans.length; ++i) { - var sp = line.markedSpans[i], m = sp.marker; + var sp = line.markedSpans[i], m = sp.marker if ((sp.from == null || (m.inclusiveLeft ? sp.from <= pos.ch : sp.from < pos.ch)) && (sp.to == null || (m.inclusiveRight ? sp.to >= pos.ch : sp.to > pos.ch))) { if (mayClear) { - signal(m, "beforeCursorEnter"); + signal(m, "beforeCursorEnter") if (m.explicitlyCleared) { - if (!line.markedSpans) break; - else {--i; continue;} + if (!line.markedSpans) break + else {--i; continue} } } - if (!m.atomic) continue; + if (!m.atomic) continue if (oldPos) { - var near = m.find(dir < 0 ? 1 : -1), diff; + var near = m.find(dir < 0 ? 1 : -1), diff if (dir < 0 ? m.inclusiveRight : m.inclusiveLeft) - near = movePos(doc, near, -dir, near && near.line == pos.line ? line : null); + near = movePos(doc, near, -dir, near && near.line == pos.line ? line : null) if (near && near.line == pos.line && (diff = cmp(near, oldPos)) && (dir < 0 ? diff < 0 : diff > 0)) - return skipAtomicInner(doc, near, pos, dir, mayClear); + return skipAtomicInner(doc, near, pos, dir, mayClear) } - var far = m.find(dir < 0 ? -1 : 1); + var far = m.find(dir < 0 ? -1 : 1) if (dir < 0 ? m.inclusiveLeft : m.inclusiveRight) - far = movePos(doc, far, dir, far.line == pos.line ? line : null); - return far ? skipAtomicInner(doc, far, pos, dir, mayClear) : null; + far = movePos(doc, far, dir, far.line == pos.line ? line : null) + return far ? skipAtomicInner(doc, far, pos, dir, mayClear) : null } } - return pos; + return pos } // Ensure a given position is not inside an atomic range. export function skipAtomic(doc, pos, oldPos, bias, mayClear) { - var dir = bias || 1; + var dir = bias || 1 var found = skipAtomicInner(doc, pos, oldPos, dir, mayClear) || (!mayClear && skipAtomicInner(doc, pos, oldPos, dir, true)) || skipAtomicInner(doc, pos, oldPos, -dir, mayClear) || - (!mayClear && skipAtomicInner(doc, pos, oldPos, -dir, true)); + (!mayClear && skipAtomicInner(doc, pos, oldPos, -dir, true)) if (!found) { - doc.cantEdit = true; - return Pos(doc.first, 0); + doc.cantEdit = true + return Pos(doc.first, 0) } - return found; + return found } function movePos(doc, pos, dir, line) { if (dir < 0 && pos.ch == 0) { - if (pos.line > doc.first) return clipPos(doc, Pos(pos.line - 1)); - else return null; + if (pos.line > doc.first) return clipPos(doc, Pos(pos.line - 1)) + else return null } else if (dir > 0 && pos.ch == (line || getLine(doc, pos.line)).text.length) { - if (pos.line < doc.first + doc.size - 1) return Pos(pos.line + 1, 0); - else return null; + if (pos.line < doc.first + doc.size - 1) return Pos(pos.line + 1, 0) + else return null } else { - return new Pos(pos.line, pos.ch + dir); + return new Pos(pos.line, pos.ch + dir) } } export function selectAll(cm) { - cm.setSelection(Pos(cm.firstLine(), 0), Pos(cm.lastLine()), sel_dontScroll); + cm.setSelection(Pos(cm.firstLine(), 0), Pos(cm.lastLine()), sel_dontScroll) } diff --git a/src/modes.js b/src/modes.js index ed5e9180..15d16cef 100644 --- a/src/modes.js +++ b/src/modes.js @@ -1,95 +1,95 @@ -import { copyObj, createObj } from "./util/misc"; +import { copyObj, createObj } from "./util/misc" // Known modes, by name and by MIME -export var modes = {}, mimeModes = {}; +export var modes = {}, mimeModes = {} // Extra arguments are stored as the mode's dependencies, which is // used by (legacy) mechanisms like loadmode.js to automatically // load a mode. (Preferred mechanism is the require/define calls.) export function defineMode(name, mode) { if (arguments.length > 2) - mode.dependencies = Array.prototype.slice.call(arguments, 2); - modes[name] = mode; + mode.dependencies = Array.prototype.slice.call(arguments, 2) + modes[name] = mode } export function defineMIME(mime, spec) { - mimeModes[mime] = spec; + mimeModes[mime] = spec } // Given a MIME type, a {name, ...options} config object, or a name // string, return a mode config object. export function resolveMode(spec) { if (typeof spec == "string" && mimeModes.hasOwnProperty(spec)) { - spec = mimeModes[spec]; + spec = mimeModes[spec] } else if (spec && typeof spec.name == "string" && mimeModes.hasOwnProperty(spec.name)) { - var found = mimeModes[spec.name]; - if (typeof found == "string") found = {name: found}; - spec = createObj(found, spec); - spec.name = found.name; + var found = mimeModes[spec.name] + if (typeof found == "string") found = {name: found} + spec = createObj(found, spec) + spec.name = found.name } else if (typeof spec == "string" && /^[\w\-]+\/[\w\-]+\+xml$/.test(spec)) { - return resolveMode("application/xml"); + return resolveMode("application/xml") } else if (typeof spec == "string" && /^[\w\-]+\/[\w\-]+\+json$/.test(spec)) { - return resolveMode("application/json"); + return resolveMode("application/json") } - if (typeof spec == "string") return {name: spec}; - else return spec || {name: "null"}; + if (typeof spec == "string") return {name: spec} + else return spec || {name: "null"} } // Given a mode spec (anything that resolveMode accepts), find and // initialize an actual mode object. export function getMode(options, spec) { - var spec = resolveMode(spec); - var mfactory = modes[spec.name]; - if (!mfactory) return getMode(options, "text/plain"); - var modeObj = mfactory(options, spec); + var spec = resolveMode(spec) + var mfactory = modes[spec.name] + if (!mfactory) return getMode(options, "text/plain") + var modeObj = mfactory(options, spec) if (modeExtensions.hasOwnProperty(spec.name)) { - var exts = modeExtensions[spec.name]; + var exts = modeExtensions[spec.name] for (var prop in exts) { - if (!exts.hasOwnProperty(prop)) continue; - if (modeObj.hasOwnProperty(prop)) modeObj["_" + prop] = modeObj[prop]; - modeObj[prop] = exts[prop]; + if (!exts.hasOwnProperty(prop)) continue + if (modeObj.hasOwnProperty(prop)) modeObj["_" + prop] = modeObj[prop] + modeObj[prop] = exts[prop] } } - modeObj.name = spec.name; - if (spec.helperType) modeObj.helperType = spec.helperType; + modeObj.name = spec.name + if (spec.helperType) modeObj.helperType = spec.helperType if (spec.modeProps) for (var prop in spec.modeProps) - modeObj[prop] = spec.modeProps[prop]; + modeObj[prop] = spec.modeProps[prop] - return modeObj; + return modeObj } // This can be used to attach properties to mode objects from // outside the actual mode definition. -export var modeExtensions = {}; +export var modeExtensions = {} export function extendMode(mode, properties) { - var exts = modeExtensions.hasOwnProperty(mode) ? modeExtensions[mode] : (modeExtensions[mode] = {}); - copyObj(properties, exts); + var exts = modeExtensions.hasOwnProperty(mode) ? modeExtensions[mode] : (modeExtensions[mode] = {}) + copyObj(properties, exts) } export function copyState(mode, state) { - if (state === true) return state; - if (mode.copyState) return mode.copyState(state); - var nstate = {}; + 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; + var val = state[n] + if (val instanceof Array) val = val.concat([]) + nstate[n] = val } - return nstate; + return nstate } // Given a mode and a state (for that mode), find the inner mode and // state at the position that the state refers to. export function innerMode(mode, state) { while (mode.innerMode) { - var info = mode.innerMode(state); - if (!info || info.mode == mode) break; - state = info.state; - mode = info.mode; + var info = mode.innerMode(state) + if (!info || info.mode == mode) break + state = info.state + mode = info.mode } - return info || {mode: mode, state: state}; + return info || {mode: mode, state: state} } export function startState(mode, a1, a2) { - return mode.startState ? mode.startState(a1, a2) : true; + return mode.startState ? mode.startState(a1, a2) : true } diff --git a/src/util/StringStream.js b/src/util/StringStream.js index f07357be..d0480742 100644 --- a/src/util/StringStream.js +++ b/src/util/StringStream.js @@ -1,4 +1,4 @@ -import { countColumn } from "./misc"; +import { countColumn } from "./misc" // STRING STREAM @@ -6,75 +6,75 @@ import { countColumn } from "./misc"; // parsers more succinct. var StringStream = function(string, tabSize) { - this.pos = this.start = 0; - this.string = string; - this.tabSize = tabSize || 8; - this.lastColumnPos = this.lastColumnValue = 0; - this.lineStart = 0; -}; + this.pos = this.start = 0 + this.string = string + this.tabSize = tabSize || 8 + this.lastColumnPos = this.lastColumnValue = 0 + this.lineStart = 0 +} StringStream.prototype = { - eol: function() {return this.pos >= this.string.length;}, - sol: function() {return this.pos == this.lineStart;}, - peek: function() {return this.string.charAt(this.pos) || undefined;}, + eol: function() {return this.pos >= this.string.length}, + sol: function() {return this.pos == this.lineStart}, + peek: function() {return this.string.charAt(this.pos) || undefined}, next: function() { if (this.pos < this.string.length) - return this.string.charAt(this.pos++); + 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;} + 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; + var start = this.pos while (this.eat(match)){} - return this.pos > start; + 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; + 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;}, + 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;} + var found = this.string.indexOf(ch, this.pos) + if (found > -1) {this.pos = found; return true} }, - backUp: function(n) {this.pos -= n;}, + backUp: function(n) {this.pos -= n}, column: function() { if (this.lastColumnPos < this.start) { - this.lastColumnValue = countColumn(this.string, this.start, this.tabSize, this.lastColumnPos, this.lastColumnValue); - this.lastColumnPos = this.start; + this.lastColumnValue = countColumn(this.string, this.start, this.tabSize, this.lastColumnPos, this.lastColumnValue) + this.lastColumnPos = this.start } - return this.lastColumnValue - (this.lineStart ? countColumn(this.string, this.lineStart, this.tabSize) : 0); + return this.lastColumnValue - (this.lineStart ? countColumn(this.string, this.lineStart, this.tabSize) : 0) }, indentation: function() { return countColumn(this.string, null, this.tabSize) - - (this.lineStart ? countColumn(this.string, this.lineStart, this.tabSize) : 0); + (this.lineStart ? countColumn(this.string, this.lineStart, this.tabSize) : 0) }, match: function(pattern, consume, caseInsensitive) { if (typeof pattern == "string") { - var cased = function(str) {return caseInsensitive ? str.toLowerCase() : str;}; - var substr = this.string.substr(this.pos, pattern.length); + var cased = function(str) {return caseInsensitive ? str.toLowerCase() : str} + var substr = this.string.substr(this.pos, pattern.length) if (cased(substr) == cased(pattern)) { - if (consume !== false) this.pos += pattern.length; - return true; + if (consume !== false) this.pos += pattern.length + return true } } 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; + 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 } }, - current: function(){return this.string.slice(this.start, this.pos);}, + current: function(){return this.string.slice(this.start, this.pos)}, hideFirstChars: function(n, inner) { - this.lineStart += n; - try { return inner(); } - finally { this.lineStart -= n; } + this.lineStart += n + try { return inner() } + finally { this.lineStart -= n } } -}; +} -export default StringStream; +export default StringStream diff --git a/src/util/bidi.js b/src/util/bidi.js index 776319d0..f12dccf5 100644 --- a/src/util/bidi.js +++ b/src/util/bidi.js @@ -1,63 +1,63 @@ -import { isExtendingChar, lst } from "./misc"; +import { isExtendingChar, lst } from "./misc" // BIDI HELPERS export function iterateBidiSections(order, from, to, f) { - if (!order) return f(from, to, "ltr"); - var found = false; + if (!order) return f(from, to, "ltr") + var found = false for (var i = 0; i < order.length; ++i) { - var part = order[i]; + var part = order[i] 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; + f(Math.max(part.from, from), Math.min(part.to, to), part.level == 1 ? "rtl" : "ltr") + found = true } } - if (!found) f(from, to, "ltr"); + if (!found) f(from, to, "ltr") } -export function bidiLeft(part) { return part.level % 2 ? part.to : part.from; } -export function bidiRight(part) { return part.level % 2 ? part.from : part.to; } +export function bidiLeft(part) { return part.level % 2 ? part.to : part.from } +export function bidiRight(part) { return part.level % 2 ? part.from : part.to } -export function lineLeft(line) { var order = getOrder(line); return order ? bidiLeft(order[0]) : 0; } +export function lineLeft(line) { var order = getOrder(line); return order ? bidiLeft(order[0]) : 0 } export function lineRight(line) { - var order = getOrder(line); - if (!order) return line.text.length; - return bidiRight(lst(order)); + var order = getOrder(line) + if (!order) return line.text.length + return bidiRight(lst(order)) } function compareBidiLevel(order, a, b) { - var linedir = order[0].level; - if (a == linedir) return true; - if (b == linedir) return false; - return a < b; + var linedir = order[0].level + if (a == linedir) return true + if (b == linedir) return false + return a < b } -export var bidiOther = null; +export var bidiOther = null export function getBidiPartAt(order, pos) { - bidiOther = null; + bidiOther = null for (var i = 0, found; i < order.length; ++i) { - var cur = order[i]; - if (cur.from < pos && cur.to > pos) return i; + var cur = order[i] + if (cur.from < pos && cur.to > pos) return i if ((cur.from == pos || cur.to == pos)) { if (found == null) { - found = i; + found = i } else if (compareBidiLevel(order, cur.level, order[found].level)) { - if (cur.from != cur.to) bidiOther = found; - return i; + if (cur.from != cur.to) bidiOther = found + return i } else { - if (cur.from != cur.to) bidiOther = i; - return found; + if (cur.from != cur.to) bidiOther = i + return found } } } - return found; + return found } function moveInLine(line, pos, dir, byUnit) { - if (!byUnit) return pos + dir; - do pos += dir; - while (pos > 0 && isExtendingChar(line.text.charAt(pos))); - return pos; + if (!byUnit) return pos + dir + do pos += dir + while (pos > 0 && isExtendingChar(line.text.charAt(pos))) + return pos } // This is needed in order to move 'visually' through bi-directional @@ -66,32 +66,32 @@ function moveInLine(line, pos, dir, byUnit) { // LTR text touch each other. This often requires the cursor offset // to move more than one unit, in order to visually move one unit. export function moveVisually(line, start, dir, byUnit) { - var bidi = getOrder(line); - if (!bidi) return moveLogically(line, start, dir, byUnit); - var pos = getBidiPartAt(bidi, start), part = bidi[pos]; - var target = moveInLine(line, start, part.level % 2 ? -dir : dir, byUnit); + var bidi = getOrder(line) + if (!bidi) return moveLogically(line, start, dir, byUnit) + var pos = getBidiPartAt(bidi, start), part = bidi[pos] + var target = moveInLine(line, start, part.level % 2 ? -dir : dir, byUnit) for (;;) { - if (target > part.from && target < part.to) return target; + if (target > part.from && target < part.to) return target if (target == part.from || target == part.to) { - if (getBidiPartAt(bidi, target) == pos) return target; - part = bidi[pos += dir]; - return (dir > 0) == part.level % 2 ? part.to : part.from; + if (getBidiPartAt(bidi, target) == pos) return target + part = bidi[pos += dir] + return (dir > 0) == part.level % 2 ? part.to : part.from } else { - part = bidi[pos += dir]; - if (!part) return null; + part = bidi[pos += dir] + if (!part) return null if ((dir > 0) == part.level % 2) - target = moveInLine(line, part.to, -1, byUnit); + target = moveInLine(line, part.to, -1, byUnit) else - target = moveInLine(line, part.from, 1, byUnit); + target = moveInLine(line, part.from, 1, byUnit) } } } export function moveLogically(line, start, dir, byUnit) { - var target = start + dir; - if (byUnit) while (target > 0 && isExtendingChar(line.text.charAt(target))) target += dir; - return target < 0 || target > line.text.length ? null : target; + var target = start + dir + if (byUnit) while (target > 0 && isExtendingChar(line.text.charAt(target))) target += dir + return target < 0 || target > line.text.length ? null : target } // Bidirectional ordering algorithm @@ -119,43 +119,43 @@ export function moveLogically(line, start, dir, byUnit) { // objects) in the order in which they occur visually. export var bidiOrdering = (function() { // Character types for codepoints 0 to 0xff - var lowTypes = "bbbbbbbbbtstwsbbbbbbbbbbbbbbssstwNN%%%NNNNNN,N,N1111111111NNNNNNNLLLLLLLLLLLLLLLLLLLLLLLLLLNNNNNNLLLLLLLLLLLLLLLLLLLLLLLLLLNNNNbbbbbbsbbbbbbbbbbbbbbbbbbbbbbbbbb,N%%%%NNNNLNNNNN%%11NLNNN1LNNNNNLLLLLLLLLLLLLLLLLLLLLLLNLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLN"; + var lowTypes = "bbbbbbbbbtstwsbbbbbbbbbbbbbbssstwNN%%%NNNNNN,N,N1111111111NNNNNNNLLLLLLLLLLLLLLLLLLLLLLLLLLNNNNNNLLLLLLLLLLLLLLLLLLLLLLLLLLNNNNbbbbbbsbbbbbbbbbbbbbbbbbbbbbbbbbb,N%%%%NNNNLNNNNN%%11NLNNN1LNNNNNLLLLLLLLLLLLLLLLLLLLLLLNLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLN" // Character types for codepoints 0x600 to 0x6ff - var arabicTypes = "rrrrrrrrrrrr,rNNmmmmmmrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrmmmmmmmmmmmmmmrrrrrrrnnnnnnnnnn%nnrrrmrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrmmmmmmmmmmmmmmmmmmmNmmmm"; + var arabicTypes = "rrrrrrrrrrrr,rNNmmmmmmrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrmmmmmmmmmmmmmmrrrrrrrnnnnnnnnnn%nnrrrmrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrmmmmmmmmmmmmmmmmmmmNmmmm" function charType(code) { - if (code <= 0xf7) return lowTypes.charAt(code); - else if (0x590 <= code && code <= 0x5f4) return "R"; - else if (0x600 <= code && code <= 0x6ed) return arabicTypes.charAt(code - 0x600); - else if (0x6ee <= code && code <= 0x8ac) return "r"; - else if (0x2000 <= code && code <= 0x200b) return "w"; - else if (code == 0x200c) return "b"; - else return "L"; + if (code <= 0xf7) return lowTypes.charAt(code) + else if (0x590 <= code && code <= 0x5f4) return "R" + else if (0x600 <= code && code <= 0x6ed) return arabicTypes.charAt(code - 0x600) + else if (0x6ee <= code && code <= 0x8ac) return "r" + else if (0x2000 <= code && code <= 0x200b) return "w" + else if (code == 0x200c) return "b" + else return "L" } - var bidiRE = /[\u0590-\u05f4\u0600-\u06ff\u0700-\u08ac]/; - var isNeutral = /[stwN]/, isStrong = /[LRr]/, countsAsLeft = /[Lb1n]/, countsAsNum = /[1n]/; + var bidiRE = /[\u0590-\u05f4\u0600-\u06ff\u0700-\u08ac]/ + var isNeutral = /[stwN]/, isStrong = /[LRr]/, countsAsLeft = /[Lb1n]/, countsAsNum = /[1n]/ // Browsers seem to always treat the boundaries of block elements as being L. - var outerType = "L"; + var outerType = "L" function BidiSpan(level, from, to) { - this.level = level; - this.from = from; this.to = to; + this.level = level + this.from = from; this.to = to } return function(str) { - if (!bidiRE.test(str)) return false; - var len = str.length, types = []; + if (!bidiRE.test(str)) return false + var len = str.length, types = [] for (var i = 0, type; i < len; ++i) - types.push(type = charType(str.charCodeAt(i))); + types.push(type = charType(str.charCodeAt(i))) // 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 = outerType; i < len; ++i) { - var type = types[i]; - if (type == "m") types[i] = prev; - else prev = type; + var type = types[i] + if (type == "m") types[i] = prev + else prev = type } // W2. Search backwards from each instance of a European number @@ -164,20 +164,20 @@ export var bidiOrdering = (function() { // number. // W3. Change all ALs to R. for (var i = 0, cur = outerType; 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"; } + 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"; + 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; + (prev == "1" || prev == "n")) types[i] = prev + prev = type } // W5. A sequence of European terminators adjacent to European @@ -185,13 +185,13 @@ export var bidiOrdering = (function() { // 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"; + 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 && types[end] == "1") ? "1" : "N"; - for (var j = i; j < end; ++j) types[j] = replace; - i = end - 1; + var replace = (i && types[i-1] == "!") || (end < len && types[end] == "1") ? "1" : "N" + for (var j = i; j < end; ++j) types[j] = replace + i = end - 1 } } @@ -199,9 +199,9 @@ export var bidiOrdering = (function() { // 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 = outerType; i < len; ++i) { - var type = types[i]; - if (cur == "L" && type == "1") types[i] = "L"; - else if (isStrong.test(type)) cur = type; + 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 @@ -213,11 +213,11 @@ export var bidiOrdering = (function() { 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] : outerType) == "L"; - var after = (end < len ? types[end] : outerType) == "L"; - var replace = before || after ? "L" : "R"; - for (var j = i; j < end; ++j) types[j] = replace; - i = end - 1; + var before = (i ? types[i-1] : outerType) == "L" + var after = (end < len ? types[end] : outerType) == "L" + var replace = before || after ? "L" : "R" + for (var j = i; j < end; ++j) types[j] = replace + i = end - 1 } } @@ -226,49 +226,49 @@ export var bidiOrdering = (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 = [], m; + var order = [], m for (var i = 0; i < len;) { if (countsAsLeft.test(types[i])) { - var start = i; + var start = i for (++i; i < len && countsAsLeft.test(types[i]); ++i) {} - order.push(new BidiSpan(0, start, i)); + order.push(new BidiSpan(0, start, i)) } else { - var pos = i, at = order.length; + 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, new BidiSpan(1, pos, j)); - var nstart = j; + if (pos < j) order.splice(at, 0, new BidiSpan(1, pos, j)) + var nstart = j for (++j; j < i && countsAsNum.test(types[j]); ++j) {} - order.splice(at, 0, new BidiSpan(2, nstart, j)); - pos = j; - } else ++j; + order.splice(at, 0, new BidiSpan(2, nstart, j)) + pos = j + } else ++j } - if (pos < i) order.splice(at, 0, new BidiSpan(1, pos, i)); + if (pos < i) order.splice(at, 0, new BidiSpan(1, pos, i)) } } if (order[0].level == 1 && (m = str.match(/^\s+/))) { - order[0].from = m[0].length; - order.unshift(new BidiSpan(0, 0, m[0].length)); + order[0].from = m[0].length + order.unshift(new BidiSpan(0, 0, m[0].length)) } if (lst(order).level == 1 && (m = str.match(/\s+$/))) { - lst(order).to -= m[0].length; - order.push(new BidiSpan(0, len - m[0].length, len)); + lst(order).to -= m[0].length + order.push(new BidiSpan(0, len - m[0].length, len)) } if (order[0].level == 2) - order.unshift(new BidiSpan(1, order[0].to, order[0].to)); + order.unshift(new BidiSpan(1, order[0].to, order[0].to)) if (order[0].level != lst(order).level) - order.push(new BidiSpan(order[0].level, len, len)); + order.push(new BidiSpan(order[0].level, len, len)) - return order; - }; -})(); + return order + } +})() // Get the bidi ordering for the given line (and cache it). Returns // false for lines that are fully left-to-right, and an array of // BidiSpan objects otherwise. export function getOrder(line) { - var order = line.order; - if (order == null) order = line.order = bidiOrdering(line.text); - return order; + var order = line.order + if (order == null) order = line.order = bidiOrdering(line.text) + return order } diff --git a/src/util/browser.js b/src/util/browser.js index 7941f930..ae48fb5c 100644 --- a/src/util/browser.js +++ b/src/util/browser.js @@ -1,31 +1,31 @@ // Kludges for bugs and behavior differences that can't be feature // detected are enabled based on userAgent etc sniffing. -var userAgent = navigator.userAgent; -var platform = navigator.platform; +var userAgent = navigator.userAgent +var platform = navigator.platform -export var gecko = /gecko\/\d/i.test(userAgent); -var ie_upto10 = /MSIE \d/.test(userAgent); -var ie_11up = /Trident\/(?:[7-9]|\d{2,})\..*rv:(\d+)/.exec(userAgent); -export var ie = ie_upto10 || ie_11up; -export var ie_version = ie && (ie_upto10 ? document.documentMode || 6 : ie_11up[1]); -export var webkit = /WebKit\//.test(userAgent); -var qtwebkit = webkit && /Qt\/\d+\.\d+/.test(userAgent); -export var chrome = /Chrome\//.test(userAgent); -export var presto = /Opera\//.test(userAgent); -export var safari = /Apple Computer/.test(navigator.vendor); -export var mac_geMountainLion = /Mac OS X 1\d\D([8-9]|\d\d)\D/.test(userAgent); -export var phantom = /PhantomJS/.test(userAgent); +export var gecko = /gecko\/\d/i.test(userAgent) +var ie_upto10 = /MSIE \d/.test(userAgent) +var ie_11up = /Trident\/(?:[7-9]|\d{2,})\..*rv:(\d+)/.exec(userAgent) +export var ie = ie_upto10 || ie_11up +export var ie_version = ie && (ie_upto10 ? document.documentMode || 6 : ie_11up[1]) +export var webkit = /WebKit\//.test(userAgent) +var qtwebkit = webkit && /Qt\/\d+\.\d+/.test(userAgent) +export var chrome = /Chrome\//.test(userAgent) +export var presto = /Opera\//.test(userAgent) +export var safari = /Apple Computer/.test(navigator.vendor) +export var mac_geMountainLion = /Mac OS X 1\d\D([8-9]|\d\d)\D/.test(userAgent) +export var phantom = /PhantomJS/.test(userAgent) -export var ios = /AppleWebKit/.test(userAgent) && /Mobile\/\w+/.test(userAgent); +export var ios = /AppleWebKit/.test(userAgent) && /Mobile\/\w+/.test(userAgent) // This is woefully incomplete. Suggestions for alternative methods welcome. -export var mobile = ios || /Android|webOS|BlackBerry|Opera Mini|Opera Mobi|IEMobile/i.test(userAgent); -export var mac = ios || /Mac/.test(platform); -export var chromeOS = /\bCrOS\b/.test(userAgent); -export var windows = /win/i.test(platform); +export var mobile = ios || /Android|webOS|BlackBerry|Opera Mini|Opera Mobi|IEMobile/i.test(userAgent) +export var mac = ios || /Mac/.test(platform) +export var chromeOS = /\bCrOS\b/.test(userAgent) +export var windows = /win/i.test(platform) -var presto_version = presto && userAgent.match(/Version\/(\d*\.\d*)/); -if (presto_version) presto_version = Number(presto_version[1]); -if (presto_version && presto_version >= 15) { presto = false; webkit = true; } +var presto_version = presto && userAgent.match(/Version\/(\d*\.\d*)/) +if (presto_version) presto_version = Number(presto_version[1]) +if (presto_version && presto_version >= 15) { presto = false; webkit = true } // Some browsers use the wrong event properties to signal cmd/ctrl on OS X -export var flipCtrlCmd = mac && (qtwebkit || presto && (presto_version == null || presto_version < 12.11)); -export var captureRightClick = gecko || (ie && ie_version >= 9); +export var flipCtrlCmd = mac && (qtwebkit || presto && (presto_version == null || presto_version < 12.11)) +export var captureRightClick = gecko || (ie && ie_version >= 9) diff --git a/src/util/dom.js b/src/util/dom.js index 478402e6..df2cfd0a 100644 --- a/src/util/dom.js +++ b/src/util/dom.js @@ -1,89 +1,89 @@ -import { ie, ie_version, ios } from "./browser"; +import { ie, ie_version, ios } from "./browser" -export function classTest(cls) { return new RegExp("(^|\\s)" + cls + "(?:$|\\s)\\s*"); } +export function classTest(cls) { return new RegExp("(^|\\s)" + cls + "(?:$|\\s)\\s*") } export var rmClass = function(node, cls) { - var current = node.className; - var match = classTest(cls).exec(current); + var current = node.className + var match = classTest(cls).exec(current) if (match) { - var after = current.slice(match.index + match[0].length); - node.className = current.slice(0, match.index) + (after ? match[1] + after : ""); + var after = current.slice(match.index + match[0].length) + node.className = current.slice(0, match.index) + (after ? match[1] + after : "") } -}; +} export function removeChildren(e) { for (var count = e.childNodes.length; count > 0; --count) - e.removeChild(e.firstChild); - return e; + e.removeChild(e.firstChild) + return e } export function removeChildrenAndAdd(parent, e) { - return removeChildren(parent).appendChild(e); + return removeChildren(parent).appendChild(e) } export function elt(tag, content, className, style) { - var e = document.createElement(tag); - if (className) e.className = className; - if (style) e.style.cssText = style; - if (typeof content == "string") e.appendChild(document.createTextNode(content)); - else if (content) for (var i = 0; i < content.length; ++i) e.appendChild(content[i]); - return e; + var e = document.createElement(tag) + if (className) e.className = className + if (style) e.style.cssText = style + if (typeof content == "string") e.appendChild(document.createTextNode(content)) + else if (content) for (var i = 0; i < content.length; ++i) e.appendChild(content[i]) + return e } -export var range; +export var range if (document.createRange) range = function(node, start, end, endNode) { - var r = document.createRange(); - r.setEnd(endNode || node, end); - r.setStart(node, start); - return r; -}; + var r = document.createRange() + r.setEnd(endNode || node, end) + r.setStart(node, start) + return r +} else range = function(node, start, end) { - var r = document.body.createTextRange(); - try { r.moveToElementText(node.parentNode); } - catch(e) { return r; } - r.collapse(true); - r.moveEnd("character", end); - r.moveStart("character", start); - return r; -}; + var r = document.body.createTextRange() + try { r.moveToElementText(node.parentNode) } + catch(e) { return r } + r.collapse(true) + r.moveEnd("character", end) + r.moveStart("character", start) + return r +} export function contains(parent, child) { if (child.nodeType == 3) // Android browser always returns false when child is a textnode - child = child.parentNode; + child = child.parentNode if (parent.contains) - return parent.contains(child); + return parent.contains(child) do { - if (child.nodeType == 11) child = child.host; - if (child == parent) return true; - } while (child = child.parentNode); + if (child.nodeType == 11) child = child.host + if (child == parent) return true + } while (child = child.parentNode) } export var activeElt = function() { - var activeElement = document.activeElement; + var activeElement = document.activeElement while (activeElement && activeElement.root && activeElement.root.activeElement) - activeElement = activeElement.root.activeElement; - return activeElement; + activeElement = activeElement.root.activeElement + return activeElement } // Older versions of IE throws unspecified error when touching // document.activeElement in some cases (during loading, in iframe) if (ie && ie_version < 11) activeElt = function() { - try { return document.activeElement; } - catch(e) { return document.body; } -}; + try { return document.activeElement } + catch(e) { return document.body } +} export function addClass(node, cls) { - var current = node.className; - if (!classTest(cls).test(current)) node.className += (current ? " " : "") + cls; + var current = node.className + if (!classTest(cls).test(current)) node.className += (current ? " " : "") + cls } export function joinClasses(a, b) { - var as = a.split(" "); + var as = a.split(" ") for (var i = 0; i < as.length; i++) - if (as[i] && !classTest(as[i]).test(b)) b += " " + as[i]; - return b; + if (as[i] && !classTest(as[i]).test(b)) b += " " + as[i] + return b } -export var selectInput = function(node) { node.select(); }; +export var selectInput = function(node) { node.select() } if (ios) // Mobile Safari apparently has a bug where select() is broken. - selectInput = function(node) { node.selectionStart = 0; node.selectionEnd = node.value.length; }; + selectInput = function(node) { node.selectionStart = 0; node.selectionEnd = node.value.length } else if (ie) // Suppress mysterious IE10 errors - selectInput = function(node) { try { node.select(); } catch(_e) {} }; + selectInput = function(node) { try { node.select() } catch(_e) {} } diff --git a/src/util/event.js b/src/util/event.js index 4c479cbd..b2137583 100644 --- a/src/util/event.js +++ b/src/util/event.js @@ -1,5 +1,5 @@ -import { mac } from "./browser"; -import { indexOf } from "./misc"; +import { mac } from "./browser" +import { indexOf } from "./misc" // EVENT HANDLING @@ -8,15 +8,15 @@ import { indexOf } from "./misc"; export var on = function(emitter, type, f) { if (emitter.addEventListener) - emitter.addEventListener(type, f, false); + emitter.addEventListener(type, f, false) else if (emitter.attachEvent) - emitter.attachEvent("on" + type, f); + emitter.attachEvent("on" + type, f) else { - var map = emitter._handlers || (emitter._handlers = {}); - var arr = map[type] || (map[type] = []); - arr.push(f); + var map = emitter._handlers || (emitter._handlers = {}) + var arr = map[type] || (map[type] = []) + arr.push(f) } -}; +} var noHandlers = [] export function getHandlers(emitter, type, copy) { @@ -27,21 +27,21 @@ export function getHandlers(emitter, type, copy) { export function off(emitter, type, f) { if (emitter.removeEventListener) - emitter.removeEventListener(type, f, false); + emitter.removeEventListener(type, f, false) else if (emitter.detachEvent) - emitter.detachEvent("on" + type, f); + emitter.detachEvent("on" + type, f) else { var handlers = getHandlers(emitter, type, false) for (var i = 0; i < handlers.length; ++i) - if (handlers[i] == f) { handlers.splice(i, 1); break; } + if (handlers[i] == f) { handlers.splice(i, 1); break } } } export function signal(emitter, type /*, values...*/) { var handlers = getHandlers(emitter, type, true) - if (!handlers.length) return; - var args = Array.prototype.slice.call(arguments, 2); - for (var i = 0; i < handlers.length; ++i) handlers[i].apply(null, args); + if (!handlers.length) return + var args = Array.prototype.slice.call(arguments, 2) + for (var i = 0; i < handlers.length; ++i) handlers[i].apply(null, args) } // The DOM events that CodeMirror handles can be overridden by @@ -49,17 +49,17 @@ export function signal(emitter, type /*, values...*/) { // and preventDefault-ing the event in that handler. export function signalDOMEvent(cm, e, override) { if (typeof e == "string") - e = {type: e, preventDefault: function() { this.defaultPrevented = true; }}; - signal(cm, override || e.type, cm, e); - return e_defaultPrevented(e) || e.codemirrorIgnore; + e = {type: e, preventDefault: function() { this.defaultPrevented = true }} + signal(cm, override || e.type, cm, e) + return e_defaultPrevented(e) || e.codemirrorIgnore } export function signalCursorActivity(cm) { - var arr = cm._handlers && cm._handlers.cursorActivity; - if (!arr) return; - var set = cm.curOp.cursorActivityHandlers || (cm.curOp.cursorActivityHandlers = []); + var arr = cm._handlers && cm._handlers.cursorActivity + if (!arr) return + var set = cm.curOp.cursorActivityHandlers || (cm.curOp.cursorActivityHandlers = []) for (var i = 0; i < arr.length; ++i) if (indexOf(set, arr[i]) == -1) - set.push(arr[i]); + set.push(arr[i]) } export function hasHandler(emitter, type) { @@ -69,34 +69,34 @@ export function hasHandler(emitter, type) { // Add on and off methods to a constructor's prototype, to make // registering events on such objects more convenient. export function eventMixin(ctor) { - ctor.prototype.on = function(type, f) {on(this, type, f);}; - ctor.prototype.off = function(type, f) {off(this, type, f);}; + ctor.prototype.on = function(type, f) {on(this, type, f)} + ctor.prototype.off = function(type, f) {off(this, type, f)} } // Due to the fact that we still support jurassic IE versions, some // compatibility wrappers are needed. export function e_preventDefault(e) { - if (e.preventDefault) e.preventDefault(); - else e.returnValue = false; + if (e.preventDefault) e.preventDefault() + else e.returnValue = false } export function e_stopPropagation(e) { - if (e.stopPropagation) e.stopPropagation(); - else e.cancelBubble = true; + if (e.stopPropagation) e.stopPropagation() + else e.cancelBubble = true } export function e_defaultPrevented(e) { - return e.defaultPrevented != null ? e.defaultPrevented : e.returnValue == false; + return e.defaultPrevented != null ? e.defaultPrevented : e.returnValue == false } -export function e_stop(e) {e_preventDefault(e); e_stopPropagation(e);} +export function e_stop(e) {e_preventDefault(e); e_stopPropagation(e)} -export function e_target(e) {return e.target || e.srcElement;} +export function e_target(e) {return e.target || e.srcElement} export function e_button(e) { - var b = e.which; + var b = e.which if (b == null) { - if (e.button & 1) b = 1; - else if (e.button & 2) b = 3; - else if (e.button & 4) b = 2; + if (e.button & 1) b = 1 + else if (e.button & 2) b = 3 + else if (e.button & 4) b = 2 } - if (mac && e.ctrlKey && b == 1) b = 3; - return b; + if (mac && e.ctrlKey && b == 1) b = 3 + return b } diff --git a/src/util/feature_detection.js b/src/util/feature_detection.js index bce86ad2..1e9baf41 100644 --- a/src/util/feature_detection.js +++ b/src/util/feature_detection.js @@ -1,83 +1,83 @@ -import { elt, range, removeChildren, removeChildrenAndAdd } from "./dom"; -import { ie, ie_version } from "./browser"; +import { elt, range, removeChildren, removeChildrenAndAdd } from "./dom" +import { ie, ie_version } from "./browser" // Detect drag-and-drop export 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 && ie_version < 9) return false; - var div = elt('div'); - return "draggable" in div || "dragDrop" in div; -}(); + if (ie && ie_version < 9) return false + var div = elt('div') + return "draggable" in div || "dragDrop" in div +}() -var zwspSupported; +var zwspSupported export function zeroWidthElement(measure) { if (zwspSupported == null) { - var test = elt("span", "\u200b"); - removeChildrenAndAdd(measure, elt("span", [test, document.createTextNode("x")])); + 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 && !(ie && ie_version < 8); + zwspSupported = test.offsetWidth <= 1 && test.offsetHeight > 2 && !(ie && ie_version < 8) } var node = zwspSupported ? elt("span", "\u200b") : - elt("span", "\u00a0", null, "display: inline-block; width: 1px; margin-right: -1px"); - node.setAttribute("cm-text", ""); - return node; + elt("span", "\u00a0", null, "display: inline-block; width: 1px; margin-right: -1px") + node.setAttribute("cm-text", "") + return node } // Feature-detect IE's crummy client rect reporting for bidi text -var badBidiRects; +var badBidiRects export function hasBadBidiRects(measure) { - if (badBidiRects != null) return badBidiRects; - var txt = removeChildrenAndAdd(measure, document.createTextNode("A\u062eA")); - var r0 = range(txt, 0, 1).getBoundingClientRect(); - var r1 = range(txt, 1, 2).getBoundingClientRect(); - removeChildren(measure); - if (!r0 || r0.left == r0.right) return false; // Safari returns null in some cases (#2780) - return badBidiRects = (r1.right - r0.right < 3); + if (badBidiRects != null) return badBidiRects + var txt = removeChildrenAndAdd(measure, document.createTextNode("A\u062eA")) + var r0 = range(txt, 0, 1).getBoundingClientRect() + var r1 = range(txt, 1, 2).getBoundingClientRect() + removeChildren(measure) + if (!r0 || r0.left == r0.right) return false // Safari returns null in some cases (#2780) + return badBidiRects = (r1.right - r0.right < 3) } // See if "".split is the broken IE version, if so, provide an // alternative way to split lines. export var splitLinesAuto = "\n\nb".split(/\n/).length != 3 ? function(string) { - var pos = 0, result = [], l = string.length; + var pos = 0, result = [], l = string.length while (pos <= l) { - var nl = string.indexOf("\n", pos); - if (nl == -1) nl = string.length; - var line = string.slice(pos, string.charAt(nl - 1) == "\r" ? nl - 1 : nl); - var rt = line.indexOf("\r"); + var nl = string.indexOf("\n", pos) + if (nl == -1) nl = string.length + var line = string.slice(pos, string.charAt(nl - 1) == "\r" ? nl - 1 : nl) + var rt = line.indexOf("\r") if (rt != -1) { - result.push(line.slice(0, rt)); - pos += rt + 1; + result.push(line.slice(0, rt)) + pos += rt + 1 } else { - result.push(line); - pos = nl + 1; + result.push(line) + pos = nl + 1 } } - return result; -} : function(string){return string.split(/\r\n?|\n/);}; + return result +} : function(string){return string.split(/\r\n?|\n/)} export var hasSelection = window.getSelection ? function(te) { - try { return te.selectionStart != te.selectionEnd; } - catch(e) { return false; } + try { return te.selectionStart != te.selectionEnd } + catch(e) { return false } } : function(te) { - try {var range = te.ownerDocument.selection.createRange();} + try {var range = te.ownerDocument.selection.createRange()} catch(e) {} - if (!range || range.parentElement() != te) return false; - return range.compareEndPoints("StartToEnd", range) != 0; -}; + if (!range || range.parentElement() != te) return false + return range.compareEndPoints("StartToEnd", range) != 0 +} export var hasCopyEvent = (function() { - var e = elt("div"); - if ("oncopy" in e) return true; - e.setAttribute("oncopy", "return;"); - return typeof e.oncopy == "function"; -})(); + var e = elt("div") + if ("oncopy" in e) return true + e.setAttribute("oncopy", "return;") + return typeof e.oncopy == "function" +})() -var badZoomedRects = null; +var badZoomedRects = null export 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; + 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 } diff --git a/src/util/misc.js b/src/util/misc.js index a98a1883..a6e3340d 100644 --- a/src/util/misc.js +++ b/src/util/misc.js @@ -1,84 +1,84 @@ export function bind(f) { - var args = Array.prototype.slice.call(arguments, 1); - return function(){return f.apply(null, args);}; + var args = Array.prototype.slice.call(arguments, 1) + return function(){return f.apply(null, args)} } export function copyObj(obj, target, overwrite) { - if (!target) target = {}; + if (!target) target = {} for (var prop in obj) if (obj.hasOwnProperty(prop) && (overwrite !== false || !target.hasOwnProperty(prop))) - target[prop] = obj[prop]; - return target; + target[prop] = obj[prop] + return target } // Counts the column offset in a string, taking tabs into account. // Used mostly to find indentation. export function countColumn(string, end, tabSize, startIndex, startValue) { if (end == null) { - end = string.search(/[^\s\u00a0]/); - if (end == -1) end = string.length; + end = string.search(/[^\s\u00a0]/) + if (end == -1) end = string.length } for (var i = startIndex || 0, n = startValue || 0;;) { - var nextTab = string.indexOf("\t", i); + var nextTab = string.indexOf("\t", i) if (nextTab < 0 || nextTab >= end) - return n + (end - i); - n += nextTab - i; - n += tabSize - (n % tabSize); - i = nextTab + 1; + return n + (end - i) + n += nextTab - i + n += tabSize - (n % tabSize) + i = nextTab + 1 } } -export function Delayed() {this.id = null;} +export function Delayed() {this.id = null} Delayed.prototype.set = function(ms, f) { - clearTimeout(this.id); - this.id = setTimeout(f, ms); -}; + clearTimeout(this.id) + this.id = setTimeout(f, ms) +} export function indexOf(array, elt) { for (var i = 0; i < array.length; ++i) - if (array[i] == elt) return i; - return -1; + if (array[i] == elt) return i + return -1 } // Number of pixels added to scroller and sizer to hide scrollbar -export var scrollerGap = 30; +export var scrollerGap = 30 // Returned or thrown by various protocols to signal 'I'm not // handling this'. -export var Pass = {toString: function(){return "CodeMirror.Pass";}}; +export var Pass = {toString: function(){return "CodeMirror.Pass"}} // Reused option objects for setSelection & friends -export var sel_dontScroll = {scroll: false}, sel_mouse = {origin: "*mouse"}, sel_move = {origin: "+move"}; +export var sel_dontScroll = {scroll: false}, sel_mouse = {origin: "*mouse"}, sel_move = {origin: "+move"} // The inverse of countColumn -- find the offset that corresponds to // a particular column. export function findColumn(string, goal, tabSize) { for (var pos = 0, col = 0;;) { - var nextTab = string.indexOf("\t", pos); - if (nextTab == -1) nextTab = string.length; - var skipped = nextTab - pos; + var nextTab = string.indexOf("\t", pos) + if (nextTab == -1) nextTab = string.length + var skipped = nextTab - pos if (nextTab == string.length || col + skipped >= goal) - return pos + Math.min(skipped, goal - col); - col += nextTab - pos; - col += tabSize - (col % tabSize); - pos = nextTab + 1; - if (col >= goal) return pos; + return pos + Math.min(skipped, goal - col) + col += nextTab - pos + col += tabSize - (col % tabSize) + pos = nextTab + 1 + if (col >= goal) return pos } } -var spaceStrs = [""]; +var spaceStrs = [""] export function spaceStr(n) { while (spaceStrs.length <= n) - spaceStrs.push(lst(spaceStrs) + " "); - return spaceStrs[n]; + spaceStrs.push(lst(spaceStrs) + " ") + return spaceStrs[n] } -export function lst(arr) { return arr[arr.length-1]; } +export function lst(arr) { return arr[arr.length-1] } export function map(array, f) { - var out = []; - for (var i = 0; i < array.length; i++) out[i] = f(array[i], i); - return out; + var out = [] + for (var i = 0; i < array.length; i++) out[i] = f(array[i], i) + return out } export function insertSorted(array, value, score) { @@ -90,31 +90,31 @@ export function insertSorted(array, value, score) { export function nothing() {} export function createObj(base, props) { - var inst; + var inst if (Object.create) { - inst = Object.create(base); + inst = Object.create(base) } else { - nothing.prototype = base; - inst = new nothing(); + nothing.prototype = base + inst = new nothing() } - if (props) copyObj(props, inst); - return inst; + if (props) copyObj(props, inst) + return inst } -var nonASCIISingleCaseWordChar = /[\u00df\u0587\u0590-\u05f4\u0600-\u06ff\u3040-\u309f\u30a0-\u30ff\u3400-\u4db5\u4e00-\u9fcc\uac00-\ud7af]/; +var nonASCIISingleCaseWordChar = /[\u00df\u0587\u0590-\u05f4\u0600-\u06ff\u3040-\u309f\u30a0-\u30ff\u3400-\u4db5\u4e00-\u9fcc\uac00-\ud7af]/ export function isWordCharBasic(ch) { return /\w/.test(ch) || ch > "\x80" && - (ch.toUpperCase() != ch.toLowerCase() || nonASCIISingleCaseWordChar.test(ch)); + (ch.toUpperCase() != ch.toLowerCase() || nonASCIISingleCaseWordChar.test(ch)) } export function isWordChar(ch, helper) { - if (!helper) return isWordCharBasic(ch); - if (helper.source.indexOf("\\w") > -1 && isWordCharBasic(ch)) return true; - return helper.test(ch); + if (!helper) return isWordCharBasic(ch) + if (helper.source.indexOf("\\w") > -1 && isWordCharBasic(ch)) return true + return helper.test(ch) } export function isEmpty(obj) { - for (var n in obj) if (obj.hasOwnProperty(n) && obj[n]) return false; - return true; + for (var n in obj) if (obj.hasOwnProperty(n) && obj[n]) return false + return true } // Extending unicode characters. A series of a non-extending char + @@ -122,5 +122,5 @@ export function isEmpty(obj) { // as editing and measuring is concerned. This is not fully correct, // since some scripts/fonts/browsers also treat other configurations // of code points as a group. -var extendingChars = /[\u0300-\u036f\u0483-\u0489\u0591-\u05bd\u05bf\u05c1\u05c2\u05c4\u05c5\u05c7\u0610-\u061a\u064b-\u065e\u0670\u06d6-\u06dc\u06de-\u06e4\u06e7\u06e8\u06ea-\u06ed\u0711\u0730-\u074a\u07a6-\u07b0\u07eb-\u07f3\u0816-\u0819\u081b-\u0823\u0825-\u0827\u0829-\u082d\u0900-\u0902\u093c\u0941-\u0948\u094d\u0951-\u0955\u0962\u0963\u0981\u09bc\u09be\u09c1-\u09c4\u09cd\u09d7\u09e2\u09e3\u0a01\u0a02\u0a3c\u0a41\u0a42\u0a47\u0a48\u0a4b-\u0a4d\u0a51\u0a70\u0a71\u0a75\u0a81\u0a82\u0abc\u0ac1-\u0ac5\u0ac7\u0ac8\u0acd\u0ae2\u0ae3\u0b01\u0b3c\u0b3e\u0b3f\u0b41-\u0b44\u0b4d\u0b56\u0b57\u0b62\u0b63\u0b82\u0bbe\u0bc0\u0bcd\u0bd7\u0c3e-\u0c40\u0c46-\u0c48\u0c4a-\u0c4d\u0c55\u0c56\u0c62\u0c63\u0cbc\u0cbf\u0cc2\u0cc6\u0ccc\u0ccd\u0cd5\u0cd6\u0ce2\u0ce3\u0d3e\u0d41-\u0d44\u0d4d\u0d57\u0d62\u0d63\u0dca\u0dcf\u0dd2-\u0dd4\u0dd6\u0ddf\u0e31\u0e34-\u0e3a\u0e47-\u0e4e\u0eb1\u0eb4-\u0eb9\u0ebb\u0ebc\u0ec8-\u0ecd\u0f18\u0f19\u0f35\u0f37\u0f39\u0f71-\u0f7e\u0f80-\u0f84\u0f86\u0f87\u0f90-\u0f97\u0f99-\u0fbc\u0fc6\u102d-\u1030\u1032-\u1037\u1039\u103a\u103d\u103e\u1058\u1059\u105e-\u1060\u1071-\u1074\u1082\u1085\u1086\u108d\u109d\u135f\u1712-\u1714\u1732-\u1734\u1752\u1753\u1772\u1773\u17b7-\u17bd\u17c6\u17c9-\u17d3\u17dd\u180b-\u180d\u18a9\u1920-\u1922\u1927\u1928\u1932\u1939-\u193b\u1a17\u1a18\u1a56\u1a58-\u1a5e\u1a60\u1a62\u1a65-\u1a6c\u1a73-\u1a7c\u1a7f\u1b00-\u1b03\u1b34\u1b36-\u1b3a\u1b3c\u1b42\u1b6b-\u1b73\u1b80\u1b81\u1ba2-\u1ba5\u1ba8\u1ba9\u1c2c-\u1c33\u1c36\u1c37\u1cd0-\u1cd2\u1cd4-\u1ce0\u1ce2-\u1ce8\u1ced\u1dc0-\u1de6\u1dfd-\u1dff\u200c\u200d\u20d0-\u20f0\u2cef-\u2cf1\u2de0-\u2dff\u302a-\u302f\u3099\u309a\ua66f-\ua672\ua67c\ua67d\ua6f0\ua6f1\ua802\ua806\ua80b\ua825\ua826\ua8c4\ua8e0-\ua8f1\ua926-\ua92d\ua947-\ua951\ua980-\ua982\ua9b3\ua9b6-\ua9b9\ua9bc\uaa29-\uaa2e\uaa31\uaa32\uaa35\uaa36\uaa43\uaa4c\uaab0\uaab2-\uaab4\uaab7\uaab8\uaabe\uaabf\uaac1\uabe5\uabe8\uabed\udc00-\udfff\ufb1e\ufe00-\ufe0f\ufe20-\ufe26\uff9e\uff9f]/; -export function isExtendingChar(ch) { return ch.charCodeAt(0) >= 768 && extendingChars.test(ch); } +var extendingChars = /[\u0300-\u036f\u0483-\u0489\u0591-\u05bd\u05bf\u05c1\u05c2\u05c4\u05c5\u05c7\u0610-\u061a\u064b-\u065e\u0670\u06d6-\u06dc\u06de-\u06e4\u06e7\u06e8\u06ea-\u06ed\u0711\u0730-\u074a\u07a6-\u07b0\u07eb-\u07f3\u0816-\u0819\u081b-\u0823\u0825-\u0827\u0829-\u082d\u0900-\u0902\u093c\u0941-\u0948\u094d\u0951-\u0955\u0962\u0963\u0981\u09bc\u09be\u09c1-\u09c4\u09cd\u09d7\u09e2\u09e3\u0a01\u0a02\u0a3c\u0a41\u0a42\u0a47\u0a48\u0a4b-\u0a4d\u0a51\u0a70\u0a71\u0a75\u0a81\u0a82\u0abc\u0ac1-\u0ac5\u0ac7\u0ac8\u0acd\u0ae2\u0ae3\u0b01\u0b3c\u0b3e\u0b3f\u0b41-\u0b44\u0b4d\u0b56\u0b57\u0b62\u0b63\u0b82\u0bbe\u0bc0\u0bcd\u0bd7\u0c3e-\u0c40\u0c46-\u0c48\u0c4a-\u0c4d\u0c55\u0c56\u0c62\u0c63\u0cbc\u0cbf\u0cc2\u0cc6\u0ccc\u0ccd\u0cd5\u0cd6\u0ce2\u0ce3\u0d3e\u0d41-\u0d44\u0d4d\u0d57\u0d62\u0d63\u0dca\u0dcf\u0dd2-\u0dd4\u0dd6\u0ddf\u0e31\u0e34-\u0e3a\u0e47-\u0e4e\u0eb1\u0eb4-\u0eb9\u0ebb\u0ebc\u0ec8-\u0ecd\u0f18\u0f19\u0f35\u0f37\u0f39\u0f71-\u0f7e\u0f80-\u0f84\u0f86\u0f87\u0f90-\u0f97\u0f99-\u0fbc\u0fc6\u102d-\u1030\u1032-\u1037\u1039\u103a\u103d\u103e\u1058\u1059\u105e-\u1060\u1071-\u1074\u1082\u1085\u1086\u108d\u109d\u135f\u1712-\u1714\u1732-\u1734\u1752\u1753\u1772\u1773\u17b7-\u17bd\u17c6\u17c9-\u17d3\u17dd\u180b-\u180d\u18a9\u1920-\u1922\u1927\u1928\u1932\u1939-\u193b\u1a17\u1a18\u1a56\u1a58-\u1a5e\u1a60\u1a62\u1a65-\u1a6c\u1a73-\u1a7c\u1a7f\u1b00-\u1b03\u1b34\u1b36-\u1b3a\u1b3c\u1b42\u1b6b-\u1b73\u1b80\u1b81\u1ba2-\u1ba5\u1ba8\u1ba9\u1c2c-\u1c33\u1c36\u1c37\u1cd0-\u1cd2\u1cd4-\u1ce0\u1ce2-\u1ce8\u1ced\u1dc0-\u1de6\u1dfd-\u1dff\u200c\u200d\u20d0-\u20f0\u2cef-\u2cf1\u2de0-\u2dff\u302a-\u302f\u3099\u309a\ua66f-\ua672\ua67c\ua67d\ua6f0\ua6f1\ua802\ua806\ua80b\ua825\ua826\ua8c4\ua8e0-\ua8f1\ua926-\ua92d\ua947-\ua951\ua980-\ua982\ua9b3\ua9b6-\ua9b9\ua9bc\uaa29-\uaa2e\uaa31\uaa32\uaa35\uaa36\uaa43\uaa4c\uaab0\uaab2-\uaab4\uaab7\uaab8\uaabe\uaabf\uaac1\uabe5\uabe8\uabed\udc00-\udfff\ufb1e\ufe00-\ufe0f\ufe20-\ufe26\uff9e\uff9f]/ +export function isExtendingChar(ch) { return ch.charCodeAt(0) >= 768 && extendingChars.test(ch) } diff --git a/src/util/operation_group.js b/src/util/operation_group.js index 478f0728..d48f6eb3 100644 --- a/src/util/operation_group.js +++ b/src/util/operation_group.js @@ -1,46 +1,46 @@ -import { getHandlers } from "./event"; +import { getHandlers } from "./event" -var operationGroup = null; +var operationGroup = null export function pushOperation(op) { if (operationGroup) { - operationGroup.ops.push(op); + operationGroup.ops.push(op) } else { op.ownsGroup = operationGroup = { ops: [op], delayedCallbacks: [] - }; + } } } function fireCallbacksForOps(group) { // Calls delayed callbacks and cursorActivity handlers until no // new ones appear - var callbacks = group.delayedCallbacks, i = 0; + var callbacks = group.delayedCallbacks, i = 0 do { for (; i < callbacks.length; i++) - callbacks[i].call(null); + callbacks[i].call(null) for (var j = 0; j < group.ops.length; j++) { - var op = group.ops[j]; + var op = group.ops[j] if (op.cursorActivityHandlers) while (op.cursorActivityCalled < op.cursorActivityHandlers.length) - op.cursorActivityHandlers[op.cursorActivityCalled++].call(null, op.cm); + op.cursorActivityHandlers[op.cursorActivityCalled++].call(null, op.cm) } - } while (i < callbacks.length); + } while (i < callbacks.length) } export function finishOperation(op, endCb) { - var group = op.ownsGroup; - if (!group) return; + var group = op.ownsGroup + if (!group) return - try { fireCallbacksForOps(group); } + try { fireCallbacksForOps(group) } finally { - operationGroup = null; - endCb(group); + operationGroup = null + endCb(group) } } -var orphanDelayedCallbacks = null; +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 @@ -51,23 +51,23 @@ var orphanDelayedCallbacks = null; // operation is active, when a timeout fires. export function signalLater(emitter, type /*, values...*/) { var arr = getHandlers(emitter, type, false) - if (!arr.length) return; - var args = Array.prototype.slice.call(arguments, 2), list; + if (!arr.length) return + var args = Array.prototype.slice.call(arguments, 2), list if (operationGroup) { - list = operationGroup.delayedCallbacks; + list = operationGroup.delayedCallbacks } else if (orphanDelayedCallbacks) { - list = orphanDelayedCallbacks; + list = orphanDelayedCallbacks } else { - list = orphanDelayedCallbacks = []; - setTimeout(fireOrphanDelayed, 0); + list = orphanDelayedCallbacks = [] + setTimeout(fireOrphanDelayed, 0) } - function bnd(f) {return function(){f.apply(null, args);};} + function bnd(f) {return function(){f.apply(null, args)}} for (var i = 0; i < arr.length; ++i) - list.push(bnd(arr[i])); + list.push(bnd(arr[i])) } function fireOrphanDelayed() { - var delayed = orphanDelayedCallbacks; - orphanDelayedCallbacks = null; - for (var i = 0; i < delayed.length; ++i) delayed[i](); + var delayed = orphanDelayedCallbacks + orphanDelayedCallbacks = null + for (var i = 0; i < delayed.length; ++i) delayed[i]() } From 2182dfd79e92d9370802d39933cfbb446dd60081 Mon Sep 17 00:00:00 2001 From: Adrian Heine Date: Mon, 26 Sep 2016 20:25:10 +0200 Subject: [PATCH 0452/1790] Disallow semicolons in src/ --- test/lint.js | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-) diff --git a/test/lint.js b/test/lint.js index 355b3e1c..cc03ce64 100644 --- a/test/lint.js +++ b/test/lint.js @@ -11,7 +11,8 @@ var blint = require("blint"); ["src"].forEach(function(dir) { blint.checkDir(dir, { browser: true, - ecmaVersion: 6 + ecmaVersion: 6, + semicolons: false }); }); From 5bb6a94ea4ed91e13ad7918fbde3b5c77df9df81 Mon Sep 17 00:00:00 2001 From: Marijn Haverbeke Date: Mon, 26 Sep 2016 22:30:44 +0200 Subject: [PATCH 0453/1790] Add semicolons to make the linter pass --- src/line/line_data.js | 2 +- src/measurement/update_line.js | 2 +- 2 files changed, 2 insertions(+), 2 deletions(-) diff --git a/src/line/line_data.js b/src/line/line_data.js index 688aa1bd..8b247d53 100644 --- a/src/line/line_data.js +++ b/src/line/line_data.js @@ -96,7 +96,7 @@ export function buildLineContent(cm, lineView) { lineView.measure.map = builder.map lineView.measure.cache = {} } else { - (lineView.measure.maps || (lineView.measure.maps = [])).push(builder.map) + ;(lineView.measure.maps || (lineView.measure.maps = [])).push(builder.map) ;(lineView.measure.caches || (lineView.measure.caches = [])).push({}) } } diff --git a/src/measurement/update_line.js b/src/measurement/update_line.js index a80d8c50..be94b009 100644 --- a/src/measurement/update_line.js +++ b/src/measurement/update_line.js @@ -172,7 +172,7 @@ function insertLineWidgetsFor(cm, line, lineView, dims, allowAbove) { function positionLineWidget(widget, node, lineView, dims) { if (widget.noHScroll) { - (lineView.alignable || (lineView.alignable = [])).push(node) + ;(lineView.alignable || (lineView.alignable = [])).push(node) var width = dims.wrapperWidth node.style.left = dims.fixedPos + "px" if (!widget.coverGutter) { From cae456c7f18711a94e528bded3203efd716597db Mon Sep 17 00:00:00 2001 From: Marijn Haverbeke Date: Tue, 27 Sep 2016 09:40:33 +0200 Subject: [PATCH 0454/1790] Document the refresh event --- doc/manual.html | 5 +++++ 1 file changed, 5 insertions(+) diff --git a/doc/manual.html b/doc/manual.html index e97ce07e..b1377928 100644 --- a/doc/manual.html +++ b/doc/manual.html @@ -634,6 +634,11 @@

    Events

    "scroll" (instance: CodeMirror)
    Fires when the editor is scrolled.
    +
    "resize" (instance: CodeMirror)
    +
    Fires when the editor is refreshed + or resized. Mostly useful to invalidate + cached values that depend on the editor or character size.
    +
    "scrollCursorIntoView" (instance: CodeMirror, event: Event)
    Fires when the editor tries to scroll its cursor into view. Can be hooked into to take care of additional scrollable From 39ffcd8701c35363ab754b82c3c171913ecf478e Mon Sep 17 00:00:00 2001 From: Marijn Haverbeke Date: Tue, 27 Sep 2016 09:41:45 +0200 Subject: [PATCH 0455/1790] Fix event name in manual --- doc/manual.html | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/doc/manual.html b/doc/manual.html index b1377928..65e030c0 100644 --- a/doc/manual.html +++ b/doc/manual.html @@ -634,7 +634,7 @@

    Events

    "scroll" (instance: CodeMirror)
    Fires when the editor is scrolled.
    -
    "resize" (instance: CodeMirror)
    +
    "refresh" (instance: CodeMirror)
    Fires when the editor is refreshed or resized. Mostly useful to invalidate cached values that depend on the editor or character size.
    From 581854eaeb3b3d5ecf4614b7ade0babc5aaa5fe6 Mon Sep 17 00:00:00 2001 From: Apollo Zhu Date: Thu, 29 Sep 2016 01:09:40 -0400 Subject: [PATCH 0456/1790] [swift] Update to Swift3 - remove non-swifty keywords - remove repeated keywords - rearrange in more logical way --- mode/swift/swift.js | 27 ++++++++++++--------------- 1 file changed, 12 insertions(+), 15 deletions(-) diff --git a/mode/swift/swift.js b/mode/swift/swift.js index 3c28ced3..9dcd822e 100644 --- a/mode/swift/swift.js +++ b/mode/swift/swift.js @@ -19,24 +19,21 @@ return set } - var keywords = wordSet(["var","let","class","deinit","enum","extension","func","import","init","protocol", - "static","struct","subscript","typealias","as","dynamicType","is","new","super", - "self","Self","Type","__COLUMN__","__FILE__","__FUNCTION__","__LINE__","break","case", - "continue","default","do","else","fallthrough","if","in","for","return","switch", - "where","while","associativity","didSet","get","infix","inout","left","mutating", - "none","nonmutating","operator","override","postfix","precedence","prefix","right", - "set","unowned","weak","willSet"]) - var definingKeywords = wordSet(["var","let","class","enum","extension","func","import","protocol","struct", - "typealias","dynamicType","for"]) - var atoms = wordSet(["Infinity","NaN","undefined","null","true","false","on","off","yes","no","nil","null", - "this","super"]) - var types = wordSet(["String","bool","int","string","double","Double","Int","Float","float","public", - "private","extension"]) - var operators = "+-/*%=|&<>#" + var keywords = wordSet(["_","var","let","class","enum","extension","import","protocol","struct","func","typealias","associatedtype", + "open","public","internal","fileprivate","private","deinit","init","new","override","self","subscript","super", + "convenience","dynamic","final","indirect","lazy","required","static","unowned","unowned(safe)","unowned(unsafe)","weak","as","is", + "break","case","continue","default","else","fallthrough","for","guard","if","in","repeat","switch","where","while", + "defer","return","inout","mutating","nonmutating","catch","do","rethrows","throw","throws","try","didSet","get","set","willSet", + "assignment","associativity","infix","left","none","operator","postfix","precedence","precedencegroup","prefix","right", + "Any","AnyObject","Type","dynamicType","Self","Protocol","__COLUMN__","__FILE__","__FUNCTION__","__LINE__"]) + var definingKeywords = wordSet(["var","let","class","enum","extension","import","protocol","struct","func","typealias","associatedtype"]) + var atoms = wordSet(["true","false","nil","self","super","_"]) + var types = wordSet(["Array","Bool","Dictionary","Double","Float","Int","Never","Optional","String","Void"]) + var operators = "+-/*%=|&<>" var punc = ";,.(){}[]" var number = /^-?(?:(?:[\d_]+\.[_\d]*|\.[_\d]+|0o[0-7_\.]+|0b[01_\.]+)(?:e-?[\d_]+)?|0x[\d_a-f\.]+(?:p-?[\d_]+)?)/i var identifier = /^[_A-Za-z$][_A-Za-z$0-9]*/ - var property = /^[@\.][_A-Za-z$][_A-Za-z$0-9]*/ + var property = /^[@\#\.][_A-Za-z$][_A-Za-z$0-9]*/ var regexp = /^\/(?!\s)(?:\/\/)?(?:\\.|[^\/])+\// function tokenBase(stream, state, prev) { From 27acd6358252095da9cd51e499a4c086f82eabfe Mon Sep 17 00:00:00 2001 From: Adrian Heine Date: Tue, 27 Sep 2016 09:23:50 +0200 Subject: [PATCH 0457/1790] Move to rollup.config.js --- package.json | 4 ++-- src/banner.js => rollup.config.js | 9 ++++++++- 2 files changed, 10 insertions(+), 3 deletions(-) rename src/banner.js => rollup.config.js (61%) diff --git a/package.json b/package.json index a6e61d06..4cbaa309 100644 --- a/package.json +++ b/package.json @@ -8,8 +8,8 @@ "lib": "./lib" }, "scripts": { - "build": "rollup --banner \"`cat src/banner.js`\" --format umd -n CodeMirror src/codemirror.js -o lib/codemirror.js", - "watch": "rollup -w --banner \"`cat src/banner.js`\" --format umd -n CodeMirror src/codemirror.js -o lib/codemirror.js", + "build": "rollup -c", + "watch": "rollup -w -c", "prepublish": "npm run-script build", "test": "node ./test/run.js", "lint": "bin/lint" diff --git a/src/banner.js b/rollup.config.js similarity index 61% rename from src/banner.js rename to rollup.config.js index 6bd6553f..c1c8c11a 100644 --- a/src/banner.js +++ b/rollup.config.js @@ -1,4 +1,5 @@ -// CodeMirror, copyright (c) by Marijn Haverbeke and others +export default { + banner: `// CodeMirror, 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 @@ -6,3 +7,9 @@ // // You can find some technical background for some of the code below // at http://marijnhaverbeke.nl/blog/#cm-internals . +`, + entry: "src/codemirror.js", + format: "umd", + dest: "lib/codemirror.js", + moduleName: "CodeMirror" +}; From 519b3ad211033ae1f855ceb0bc01f0248953be8e Mon Sep 17 00:00:00 2001 From: Adrian Heine Date: Tue, 27 Sep 2016 09:24:26 +0200 Subject: [PATCH 0458/1790] Add buble to rollup --- package.json | 1 + rollup.config.js | 5 ++++- 2 files changed, 5 insertions(+), 1 deletion(-) diff --git a/package.json b/package.json index 4cbaa309..057280c5 100644 --- a/package.json +++ b/package.json @@ -19,6 +19,7 @@ "node-static": "0.6.0", "phantomjs-prebuilt": "^2.1.12", "rollup": "^0.34.10", + "rollup-plugin-buble": "^0.14.0", "rollup-watch": "^2.5.0" }, "bugs": "http://github.com/codemirror/CodeMirror/issues", diff --git a/rollup.config.js b/rollup.config.js index c1c8c11a..584dfe1e 100644 --- a/rollup.config.js +++ b/rollup.config.js @@ -1,3 +1,5 @@ +import buble from 'rollup-plugin-buble'; + export default { banner: `// CodeMirror, copyright (c) by Marijn Haverbeke and others // Distributed under an MIT license: http://codemirror.net/LICENSE @@ -11,5 +13,6 @@ export default { entry: "src/codemirror.js", format: "umd", dest: "lib/codemirror.js", - moduleName: "CodeMirror" + moduleName: "CodeMirror", + plugins: [ buble() ] }; From c17d0230e0312c2189d1d0a4d805aaca70a4e2ed Mon Sep 17 00:00:00 2001 From: Adrian Heine Date: Tue, 27 Sep 2016 10:04:56 +0200 Subject: [PATCH 0459/1790] Replace var with let --- src/display/Display.js | 2 +- src/display/gutters.js | 11 +- src/display/highlight_worker.js | 20 +-- src/display/line_numbers.js | 18 +-- src/display/operations.js | 38 ++--- src/display/scroll_events.js | 30 ++-- src/display/scrollbars.js | 36 ++--- src/display/scrolling.js | 44 ++--- src/display/selection.js | 50 +++--- src/display/update_display.js | 48 +++--- src/display/update_lines.js | 26 +-- src/display/view_tracking.js | 31 ++-- src/edit/CodeMirror.js | 34 ++-- src/edit/commands.js | 66 ++++---- src/edit/deleteNearSelection.js | 10 +- src/edit/drop_events.js | 31 ++-- src/edit/fromTextArea.js | 11 +- src/edit/global_events.js | 12 +- src/edit/key_events.js | 30 ++-- src/edit/main.js | 4 +- src/edit/methods.js | 160 +++++++++--------- src/edit/mouse_events.js | 78 ++++----- src/edit/options.js | 26 +-- src/input/ContentEditableInput.js | 160 +++++++++--------- src/input/TextareaInput.js | 63 ++++---- src/input/indent.js | 18 +-- src/input/input.js | 47 +++--- src/input/keymap.js | 37 ++--- src/input/keynames.js | 8 +- src/line/highlight.js | 64 ++++---- src/line/line_data.js | 94 +++++------ src/line/pos.js | 7 +- src/line/saw_special_spans.js | 2 +- src/line/spans.js | 149 ++++++++--------- src/line/utils_line.js | 34 ++-- src/measurement/position_measurement.js | 205 ++++++++++++------------ src/measurement/update_line.js | 44 ++--- src/measurement/widgets.js | 6 +- src/model/Doc.js | 113 ++++++------- src/model/change_measurement.js | 22 +-- src/model/changes.js | 63 ++++---- src/model/chunk.js | 53 +++--- src/model/document_data.js | 21 +-- src/model/history.js | 56 ++++--- src/model/line_widget.js | 20 +-- src/model/mark_text.js | 80 +++++---- src/model/selection.js | 23 +-- src/model/selection_updates.js | 45 +++--- src/modes.js | 29 ++-- src/util/StringStream.js | 21 +-- src/util/bidi.js | 99 ++++++------ src/util/browser.js | 44 ++--- src/util/dom.js | 32 ++-- src/util/event.js | 28 ++-- src/util/feature_detection.js | 45 +++--- src/util/misc.js | 38 ++--- src/util/operation_group.js | 22 +-- 57 files changed, 1327 insertions(+), 1281 deletions(-) diff --git a/src/display/Display.js b/src/display/Display.js index fa4c52fe..c1879e7b 100644 --- a/src/display/Display.js +++ b/src/display/Display.js @@ -7,7 +7,7 @@ import { scrollerGap } from "../util/misc" // display-related state. export function Display(place, doc, input) { - var d = this + let d = this this.input = input // Covers bottom-right square when both scrollbars are present. diff --git a/src/display/gutters.js b/src/display/gutters.js index 3fe9a134..7ccf119e 100644 --- a/src/display/gutters.js +++ b/src/display/gutters.js @@ -6,11 +6,12 @@ import { updateGutterSpace } from "./update_display" // Rebuild the gutter elements, ensure the margin to the left of the // code matches their width. export function updateGutters(cm) { - var gutters = cm.display.gutters, specs = cm.options.gutters + let 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)) + let i = 0 + for (; i < specs.length; ++i) { + let gutterClass = specs[i] + let 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" @@ -23,7 +24,7 @@ export function updateGutters(cm) { // Make sure the gutters options contains the element // "CodeMirror-linenumbers" when the lineNumbers option is true. export function setGuttersForLineNumbers(options) { - var found = indexOf(options.gutters, "CodeMirror-linenumbers") + let found = indexOf(options.gutters, "CodeMirror-linenumbers") if (found == -1 && options.lineNumbers) { options.gutters = options.gutters.concat(["CodeMirror-linenumbers"]) } else if (found > -1 && !options.lineNumbers) { diff --git a/src/display/highlight_worker.js b/src/display/highlight_worker.js index b8a48c0e..5d535abe 100644 --- a/src/display/highlight_worker.js +++ b/src/display/highlight_worker.js @@ -13,24 +13,24 @@ export function startWorker(cm, time) { } function highlightWorker(cm) { - var doc = cm.doc + let doc = cm.doc if (doc.frontier < doc.first) doc.frontier = doc.first 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 = [] + let end = +new Date + cm.options.workTime + let state = copyState(doc.mode, getStateBefore(cm, doc.frontier)) + let changedLines = [] 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, tooLong = line.text.length > cm.options.maxHighlightLength - var highlighted = highlightLine(cm, line, tooLong ? copyState(doc.mode, state) : state, true) + let oldStyles = line.styles, tooLong = line.text.length > cm.options.maxHighlightLength + let highlighted = highlightLine(cm, line, tooLong ? copyState(doc.mode, state) : state, true) line.styles = highlighted.styles - var oldCls = line.styleClasses, newCls = highlighted.classes + let oldCls = line.styleClasses, newCls = highlighted.classes if (newCls) line.styleClasses = newCls else if (oldCls) line.styleClasses = null - var ischange = !oldStyles || oldStyles.length != line.styles.length || + let 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] + for (let i = 0; !ischange && i < oldStyles.length; ++i) ischange = oldStyles[i] != line.styles[i] if (ischange) changedLines.push(doc.frontier) line.stateAfter = tooLong ? state : copyState(doc.mode, state) } else { @@ -45,7 +45,7 @@ function highlightWorker(cm) { } }) if (changedLines.length) runInOp(cm, function() { - for (var i = 0; i < changedLines.length; i++) + for (let i = 0; i < changedLines.length; i++) regLineChange(cm, changedLines[i], "text") }) } diff --git a/src/display/line_numbers.js b/src/display/line_numbers.js index 22bd32ff..c48f2204 100644 --- a/src/display/line_numbers.js +++ b/src/display/line_numbers.js @@ -7,19 +7,19 @@ import { updateGutterSpace } from "./update_display" // Re-align line numbers and gutter marks to compensate for // horizontal scrolling. export function alignHorizontally(cm) { - var display = cm.display, view = display.view + let display = cm.display, view = display.view if (!display.alignWidgets && (!display.gutters.firstChild || !cm.options.fixedGutter)) return - var comp = compensateForHScroll(display) - display.scroller.scrollLeft + cm.doc.scrollLeft - var gutterW = display.gutters.offsetWidth, left = comp + "px" - for (var i = 0; i < view.length; i++) if (!view[i].hidden) { + let comp = compensateForHScroll(display) - display.scroller.scrollLeft + cm.doc.scrollLeft + let gutterW = display.gutters.offsetWidth, left = comp + "px" + for (let i = 0; i < view.length; i++) if (!view[i].hidden) { if (cm.options.fixedGutter) { if (view[i].gutter) view[i].gutter.style.left = left if (view[i].gutterBackground) view[i].gutterBackground.style.left = left } - var align = view[i].alignable - if (align) for (var j = 0; j < align.length; j++) + let align = view[i].alignable + if (align) for (let j = 0; j < align.length; j++) align[j].style.left = left } if (cm.options.fixedGutter) @@ -31,11 +31,11 @@ export function alignHorizontally(cm) { // is needed. export function maybeUpdateLineNumberWidth(cm) { if (!cm.options.lineNumbers) return false - var doc = cm.doc, last = lineNumberFor(cm.options, doc.first + doc.size - 1), display = cm.display + let 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)], + let test = display.measure.appendChild(elt("div", [elt("div", last)], "CodeMirror-linenumber CodeMirror-gutter-elt")) - var innerW = test.firstChild.offsetWidth, padding = test.offsetWidth - innerW + let innerW = test.firstChild.offsetWidth, padding = test.offsetWidth - innerW display.lineGutter.style.width = "" display.lineNumInnerWidth = Math.max(innerW, display.lineGutter.offsetWidth - padding) + 1 display.lineNumWidth = display.lineNumInnerWidth + padding diff --git a/src/display/operations.js b/src/display/operations.js index 2873e0e9..cc6eef7b 100644 --- a/src/display/operations.js +++ b/src/display/operations.js @@ -20,7 +20,7 @@ import { updateHeightsInViewport } from "./update_lines" // error-prone). Instead, display updates are batched and then all // combined and executed at once. -var nextOpId = 0 +let nextOpId = 0 // Start a new operation. export function startOperation(cm) { cm.curOp = { @@ -45,9 +45,9 @@ export function startOperation(cm) { // Finish an operation, updating the display and signalling delayed events export function endOperation(cm) { - var op = cm.curOp + let op = cm.curOp finishOperation(op, function(group) { - for (var i = 0; i < group.ops.length; i++) + for (let i = 0; i < group.ops.length; i++) group.ops[i].cm.curOp = null endOperations(group) }) @@ -56,21 +56,21 @@ export function endOperation(cm) { // 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 + let ops = group.ops + for (let i = 0; i < ops.length; i++) // Read DOM endOperation_R1(ops[i]) - for (var i = 0; i < ops.length; i++) // Write DOM (maybe) + for (let i = 0; i < ops.length; i++) // Write DOM (maybe) endOperation_W1(ops[i]) - for (var i = 0; i < ops.length; i++) // Read DOM + for (let i = 0; i < ops.length; i++) // Read DOM endOperation_R2(ops[i]) - for (var i = 0; i < ops.length; i++) // Write DOM (maybe) + for (let i = 0; i < ops.length; i++) // Write DOM (maybe) endOperation_W2(ops[i]) - for (var i = 0; i < ops.length; i++) // Read DOM + for (let i = 0; i < ops.length; i++) // Read DOM endOperation_finish(ops[i]) } function endOperation_R1(op) { - var cm = op.cm, display = cm.display + let cm = op.cm, display = cm.display maybeClipScrollbars(cm) if (op.updateMaxLine) findMaxLine(cm) @@ -87,7 +87,7 @@ function endOperation_W1(op) { } function endOperation_R2(op) { - var cm = op.cm, display = cm.display + let cm = op.cm, display = cm.display if (op.updatedDisplay) updateHeightsInViewport(cm) op.barMeasure = measureForScrollbars(cm) @@ -108,7 +108,7 @@ function endOperation_R2(op) { } function endOperation_W2(op) { - var cm = op.cm + let cm = op.cm if (op.adjustWidthTo != null) { cm.display.sizer.style.minWidth = op.adjustWidthTo + "px" @@ -117,7 +117,7 @@ function endOperation_W2(op) { cm.display.maxLineChanged = false } - var takeFocus = op.focus && op.focus == activeElt() && (!document.hasFocus || document.hasFocus()) + let takeFocus = op.focus && op.focus == activeElt() && (!document.hasFocus || document.hasFocus()) if (op.preparedSelection) cm.display.input.showSelection(op.preparedSelection, takeFocus) if (op.updatedDisplay || op.startHeight != cm.doc.height) @@ -133,7 +133,7 @@ function endOperation_W2(op) { } function endOperation_finish(op) { - var cm = op.cm, display = cm.display, doc = cm.doc + let cm = op.cm, display = cm.display, doc = cm.doc if (op.updatedDisplay) postUpdateDisplay(cm, op.update) @@ -155,17 +155,17 @@ function endOperation_finish(op) { } // If we need to scroll a specific position into view, do so. if (op.scrollToPos) { - var coords = scrollPosIntoView(cm, clipPos(doc, op.scrollToPos.from), + let 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) } // Fire events for markers that are hidden/unidden by editing or // undoing - var hidden = op.maybeHiddenMarkers, unhidden = op.maybeUnhiddenMarkers - if (hidden) for (var i = 0; i < hidden.length; ++i) + let hidden = op.maybeHiddenMarkers, unhidden = op.maybeUnhiddenMarkers + if (hidden) for (let 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) for (let i = 0; i < unhidden.length; ++i) if (unhidden[i].lines.length) signal(unhidden[i], "unhide") if (display.wrapper.offsetHeight) @@ -206,7 +206,7 @@ export function methodOp(f) { } export function docMethodOp(f) { return function() { - var cm = this.cm + let cm = this.cm if (!cm || cm.curOp) return f.apply(this, arguments) startOperation(cm) try { return f.apply(this, arguments) } diff --git a/src/display/scroll_events.js b/src/display/scroll_events.js index 99afe7ee..3c166b48 100644 --- a/src/display/scroll_events.js +++ b/src/display/scroll_events.js @@ -38,7 +38,7 @@ export function setScrollLeft(cm, val, isScroller) { // is that it gives us a chance to update the display before the // actual scrolling happens, reducing flickering. -var wheelSamples = 0, wheelPixelsPerUnit = null +let 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 @@ -48,27 +48,27 @@ else if (gecko) wheelPixelsPerUnit = 15 else if (chrome) wheelPixelsPerUnit = -.7 else if (safari) wheelPixelsPerUnit = -1/3 -var wheelEventDelta = function(e) { - var dx = e.wheelDeltaX, dy = e.wheelDeltaY +let wheelEventDelta = function(e) { + let dx = e.wheelDeltaX, dy = e.wheelDeltaY if (dx == null && e.detail && e.axis == e.HORIZONTAL_AXIS) dx = e.detail if (dy == null && e.detail && e.axis == e.VERTICAL_AXIS) dy = e.detail else if (dy == null) dy = e.wheelDelta return {x: dx, y: dy} } export function wheelEventPixels(e) { - var delta = wheelEventDelta(e) + let delta = wheelEventDelta(e) delta.x *= wheelPixelsPerUnit delta.y *= wheelPixelsPerUnit return delta } export function onScrollWheel(cm, e) { - var delta = wheelEventDelta(e), dx = delta.x, dy = delta.y + let delta = wheelEventDelta(e), dx = delta.x, dy = delta.y - var display = cm.display, scroll = display.scroller + let display = cm.display, scroll = display.scroller // Quit if there's nothing to scroll here - var canScrollX = scroll.scrollWidth > scroll.clientWidth - var canScrollY = scroll.scrollHeight > scroll.clientHeight + let canScrollX = scroll.scrollWidth > scroll.clientWidth + let canScrollY = scroll.scrollHeight > scroll.clientHeight if (!(dx && canScrollX || dy && canScrollY)) return // Webkit browsers on OS X abort momentum scrolls when the target @@ -76,8 +76,8 @@ export function onScrollWheel(cm, e) { // This hack (see related code in patchDisplay) makes sure the // element is kept around. if (dy && mac && webkit) { - outer: for (var cur = e.target, view = display.view; cur != scroll; cur = cur.parentNode) { - for (var i = 0; i < view.length; i++) { + outer: for (let cur = e.target, view = display.view; cur != scroll; cur = cur.parentNode) { + for (let i = 0; i < view.length; i++) { if (view[i].node == cur) { cm.display.currentWheelTarget = cur break outer @@ -109,8 +109,8 @@ export function onScrollWheel(cm, e) { // 'Project' the visible viewport to cover the area that is being // scrolled into view (if we know enough to estimate it). if (dy && wheelPixelsPerUnit != null) { - var pixels = dy * wheelPixelsPerUnit - var top = cm.doc.scrollTop, bot = top + display.wrapper.clientHeight + let pixels = dy * wheelPixelsPerUnit + let 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) updateDisplaySimple(cm, {top: top, bottom: bot}) @@ -122,9 +122,9 @@ export function onScrollWheel(cm, e) { display.wheelDX = dx; display.wheelDY = dy setTimeout(function() { 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) || + let movedX = scroll.scrollLeft - display.wheelStartX + let movedY = scroll.scrollTop - display.wheelStartY + let sample = (movedY && display.wheelDY && movedY / display.wheelDY) || (movedX && display.wheelDX && movedX / display.wheelDX) display.wheelStartX = display.wheelStartY = null if (!sample) return diff --git a/src/display/scrollbars.js b/src/display/scrollbars.js index 075ae41a..a07f2a16 100644 --- a/src/display/scrollbars.js +++ b/src/display/scrollbars.js @@ -12,8 +12,8 @@ import { setScrollLeft, setScrollTop } from "./scroll_events" // Prepare DOM reads needed to update the scrollbars. Done in one // shot to minimize update/measure roundtrips. export function measureForScrollbars(cm) { - var d = cm.display, gutterW = d.gutters.offsetWidth - var docH = Math.round(cm.doc.height + paddingVert(cm.display)) + let d = cm.display, gutterW = d.gutters.offsetWidth + let docH = Math.round(cm.doc.height + paddingVert(cm.display)) return { clientHeight: d.scroller.clientHeight, viewHeight: d.wrapper.clientHeight, @@ -29,8 +29,8 @@ export function measureForScrollbars(cm) { function NativeScrollbars(place, scroll, cm) { this.cm = cm - var vert = this.vert = elt("div", [elt("div", null, null, "min-width: 1px")], "CodeMirror-vscrollbar") - var horiz = this.horiz = elt("div", [elt("div", null, null, "height: 100%; min-height: 1px")], "CodeMirror-hscrollbar") + let vert = this.vert = elt("div", [elt("div", null, null, "min-width: 1px")], "CodeMirror-vscrollbar") + let horiz = this.horiz = elt("div", [elt("div", null, null, "height: 100%; min-height: 1px")], "CodeMirror-hscrollbar") place(vert); place(horiz) on(vert, "scroll", function() { @@ -47,14 +47,14 @@ function NativeScrollbars(place, scroll, cm) { NativeScrollbars.prototype = copyObj({ update: function(measure) { - var needsH = measure.scrollWidth > measure.clientWidth + 1 - var needsV = measure.scrollHeight > measure.clientHeight + 1 - var sWidth = measure.nativeBarWidth + let needsH = measure.scrollWidth > measure.clientWidth + 1 + let needsV = measure.scrollHeight > measure.clientHeight + 1 + let sWidth = measure.nativeBarWidth if (needsV) { this.vert.style.display = "block" this.vert.style.bottom = needsH ? sWidth + "px" : "0" - var totalHeight = measure.viewHeight - (needsH ? sWidth : 0) + let totalHeight = measure.viewHeight - (needsH ? sWidth : 0) // A bug in IE8 can cause this value to be negative, so guard it. this.vert.firstChild.style.height = Math.max(0, measure.scrollHeight - measure.clientHeight + totalHeight) + "px" @@ -67,7 +67,7 @@ NativeScrollbars.prototype = copyObj({ this.horiz.style.display = "block" this.horiz.style.right = needsV ? sWidth + "px" : "0" this.horiz.style.left = measure.barLeft + "px" - var totalWidth = measure.viewWidth - measure.barLeft - (needsV ? sWidth : 0) + let totalWidth = measure.viewWidth - measure.barLeft - (needsV ? sWidth : 0) this.horiz.firstChild.style.width = (measure.scrollWidth - measure.clientWidth + totalWidth) + "px" } else { @@ -91,7 +91,7 @@ NativeScrollbars.prototype = copyObj({ if (this.disableVert) this.enableZeroWidthBar(this.vert, this.disableVert) }, zeroWidthHack: function() { - var w = mac && !mac_geMountainLion ? "12px" : "18px" + let w = mac && !mac_geMountainLion ? "12px" : "18px" this.horiz.style.height = this.vert.style.width = w this.horiz.style.pointerEvents = this.vert.style.pointerEvents = "none" this.disableHoriz = new Delayed @@ -106,15 +106,15 @@ NativeScrollbars.prototype = copyObj({ // itself (when the bar is still visible) or its filler child // (when the bar is hidden). If it is still visible, we keep // it enabled, if it's hidden, we disable pointer events. - var box = bar.getBoundingClientRect() - var elt = document.elementFromPoint(box.left + 1, box.bottom - 1) + let box = bar.getBoundingClientRect() + let elt = document.elementFromPoint(box.left + 1, box.bottom - 1) if (elt != bar) bar.style.pointerEvents = "none" else delay.set(1000, maybeDisable) } delay.set(1000, maybeDisable) }, clear: function() { - var parent = this.horiz.parentNode + let parent = this.horiz.parentNode parent.removeChild(this.horiz) parent.removeChild(this.vert) } @@ -131,9 +131,9 @@ NullScrollbars.prototype = copyObj({ export function updateScrollbars(cm, measure) { if (!measure) measure = measureForScrollbars(cm) - var startWidth = cm.display.barWidth, startHeight = cm.display.barHeight + let startWidth = cm.display.barWidth, startHeight = cm.display.barHeight updateScrollbarsInner(cm, measure) - for (var i = 0; i < 4 && startWidth != cm.display.barWidth || startHeight != cm.display.barHeight; i++) { + for (let i = 0; i < 4 && startWidth != cm.display.barWidth || startHeight != cm.display.barHeight; i++) { if (startWidth != cm.display.barWidth && cm.options.lineWrapping) updateHeightsInViewport(cm) updateScrollbarsInner(cm, measureForScrollbars(cm)) @@ -144,8 +144,8 @@ export function updateScrollbars(cm, measure) { // Re-synchronize the fake scrollbars with the actual size of the // content. function updateScrollbarsInner(cm, measure) { - var d = cm.display - var sizes = d.scrollbars.update(measure) + let d = cm.display + let sizes = d.scrollbars.update(measure) d.sizer.style.paddingRight = (d.barWidth = sizes.right) + "px" d.sizer.style.paddingBottom = (d.barHeight = sizes.bottom) + "px" @@ -163,7 +163,7 @@ function updateScrollbarsInner(cm, measure) { } else d.gutterFiller.style.display = "" } -export var scrollbarModel = {"native": NativeScrollbars, "null": NullScrollbars} +export let scrollbarModel = {"native": NativeScrollbars, "null": NullScrollbars} export function initScrollbars(cm) { if (cm.display.scrollbars) { diff --git a/src/display/scrolling.js b/src/display/scrolling.js index ec8cec67..d58efe0b 100644 --- a/src/display/scrolling.js +++ b/src/display/scrolling.js @@ -13,11 +13,11 @@ import { setScrollLeft, setScrollTop } from "./scroll_events" export function maybeScrollWindow(cm, coords) { if (signalDOMEvent(cm, "scrollCursorIntoView")) return - var display = cm.display, box = display.sizer.getBoundingClientRect(), doScroll = null + let 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 && !phantom) { - var scrollNode = elt("div", "\u200b", null, "position: absolute; top: " + + let scrollNode = elt("div", "\u200b", null, "position: absolute; top: " + (coords.top - display.viewOffset - paddingTop(cm.display)) + "px; height: " + (coords.bottom - coords.top + scrollGap(cm) + display.barHeight) + "px; left: " + coords.left + "px; width: 2px;") @@ -32,14 +32,16 @@ export function maybeScrollWindow(cm, coords) { // measured, the position of something may 'drift' during drawing). export function scrollPosIntoView(cm, pos, end, margin) { if (margin == null) margin = 0 - for (var limit = 0; limit < 5; limit++) { - var changed = false, coords = cursorCoords(cm, pos) - var endCoords = !end || end == pos ? coords : cursorCoords(cm, end) - var scrollPos = calculateScrollPos(cm, Math.min(coords.left, endCoords.left), + let coords + for (let limit = 0; limit < 5; limit++) { + let changed = false + coords = cursorCoords(cm, pos) + let endCoords = !end || end == pos ? coords : cursorCoords(cm, end) + let scrollPos = calculateScrollPos(cm, Math.min(coords.left, endCoords.left), Math.min(coords.top, endCoords.top) - margin, Math.max(coords.left, endCoords.left), Math.max(coords.bottom, endCoords.bottom) + margin) - var startTop = cm.doc.scrollTop, startLeft = cm.doc.scrollLeft + let startTop = cm.doc.scrollTop, startLeft = cm.doc.scrollLeft if (scrollPos.scrollTop != null) { setScrollTop(cm, scrollPos.scrollTop) if (Math.abs(cm.doc.scrollTop - startTop) > 1) changed = true @@ -55,7 +57,7 @@ export function scrollPosIntoView(cm, pos, end, margin) { // Scroll a given set of coordinates into view (immediately). export function scrollIntoView(cm, x1, y1, x2, y2) { - var scrollPos = calculateScrollPos(cm, x1, y1, x2, y2) + let scrollPos = calculateScrollPos(cm, x1, y1, x2, y2) if (scrollPos.scrollTop != null) setScrollTop(cm, scrollPos.scrollTop) if (scrollPos.scrollLeft != null) setScrollLeft(cm, scrollPos.scrollLeft) } @@ -65,23 +67,23 @@ export function scrollIntoView(cm, x1, y1, x2, y2) { // scrollLeft properties. When these are undefined, the // vertical/horizontal position does not need to be adjusted. export function calculateScrollPos(cm, x1, y1, x2, y2) { - var display = cm.display, snapMargin = textHeight(cm.display) + let display = cm.display, snapMargin = textHeight(cm.display) if (y1 < 0) y1 = 0 - var screentop = cm.curOp && cm.curOp.scrollTop != null ? cm.curOp.scrollTop : display.scroller.scrollTop - var screen = displayHeight(cm), result = {} + let screentop = cm.curOp && cm.curOp.scrollTop != null ? cm.curOp.scrollTop : display.scroller.scrollTop + let screen = displayHeight(cm), result = {} if (y2 - y1 > screen) y2 = y1 + screen - var docBottom = cm.doc.height + paddingVert(display) - var atTop = y1 < snapMargin, atBottom = y2 > docBottom - snapMargin + let docBottom = cm.doc.height + paddingVert(display) + let atTop = y1 < snapMargin, atBottom = y2 > docBottom - snapMargin if (y1 < screentop) { result.scrollTop = atTop ? 0 : y1 } else if (y2 > screentop + screen) { - var newTop = Math.min(y1, (atBottom ? docBottom : y2) - screen) + let newTop = Math.min(y1, (atBottom ? docBottom : y2) - screen) if (newTop != screentop) result.scrollTop = newTop } - var screenleft = cm.curOp && cm.curOp.scrollLeft != null ? cm.curOp.scrollLeft : display.scroller.scrollLeft - var screenw = displayWidth(cm) - (cm.options.fixedGutter ? display.gutters.offsetWidth : 0) - var tooWide = x2 - x1 > screenw + let screenleft = cm.curOp && cm.curOp.scrollLeft != null ? cm.curOp.scrollLeft : display.scroller.scrollLeft + let screenw = displayWidth(cm) - (cm.options.fixedGutter ? display.gutters.offsetWidth : 0) + let tooWide = x2 - x1 > screenw if (tooWide) x2 = x1 + screenw if (x1 < 10) result.scrollLeft = 0 @@ -106,7 +108,7 @@ export function addToScrollPos(cm, left, top) { // shown. export function ensureCursorVisible(cm) { resolveScrollToPos(cm) - var cur = cm.getCursor(), from = cur, to = cur + let cur = cm.getCursor(), from = cur, to = cur if (!cm.options.lineWrapping) { from = cur.ch ? Pos(cur.line, cur.ch - 1) : cur to = Pos(cur.line, cur.ch + 1) @@ -119,11 +121,11 @@ export function ensureCursorVisible(cm) { // 'simulates' scrolling that position into view in a cheap way, so // that the effect of intermediate scroll commands is not ignored. export function resolveScrollToPos(cm) { - var range = cm.curOp.scrollToPos + let range = cm.curOp.scrollToPos if (range) { cm.curOp.scrollToPos = null - var from = estimateCoords(cm, range.from), to = estimateCoords(cm, range.to) - var sPos = calculateScrollPos(cm, Math.min(from.left, to.left), + let from = estimateCoords(cm, range.from), to = estimateCoords(cm, range.to) + let sPos = calculateScrollPos(cm, Math.min(from.left, to.left), Math.min(from.top, to.top) - range.margin, Math.max(from.right, to.right), Math.max(from.bottom, to.bottom) + range.margin) diff --git a/src/display/selection.js b/src/display/selection.js index 3ee79fa3..9fa8f6ee 100644 --- a/src/display/selection.js +++ b/src/display/selection.js @@ -10,15 +10,15 @@ export function updateSelection(cm) { } export function prepareSelection(cm, primary) { - var doc = cm.doc, result = {} - var curFragment = result.cursors = document.createDocumentFragment() - var selFragment = result.selection = document.createDocumentFragment() + let doc = cm.doc, result = {} + let curFragment = result.cursors = document.createDocumentFragment() + let selFragment = result.selection = document.createDocumentFragment() - for (var i = 0; i < doc.sel.ranges.length; i++) { + for (let i = 0; i < doc.sel.ranges.length; i++) { if (primary === false && i == doc.sel.primIndex) continue - var range = doc.sel.ranges[i] + let range = doc.sel.ranges[i] if (range.from().line >= cm.display.viewTo || range.to().line < cm.display.viewFrom) continue - var collapsed = range.empty() + let collapsed = range.empty() if (collapsed || cm.options.showCursorWhenSelecting) drawSelectionCursor(cm, range.head, curFragment) if (!collapsed) @@ -29,16 +29,16 @@ export function prepareSelection(cm, primary) { // Draws a cursor for the given range export function drawSelectionCursor(cm, head, output) { - var pos = cursorCoords(cm, head, "div", null, null, !cm.options.singleCursorHeightPerLine) + let pos = cursorCoords(cm, head, "div", null, null, !cm.options.singleCursorHeightPerLine) - var cursor = output.appendChild(elt("div", "\u00a0", "CodeMirror-cursor")) + let cursor = output.appendChild(elt("div", "\u00a0", "CodeMirror-cursor")) cursor.style.left = pos.left + "px" cursor.style.top = pos.top + "px" cursor.style.height = Math.max(0, pos.bottom - pos.top) * cm.options.cursorHeight + "px" if (pos.other) { // Secondary cursor, shown when on a 'jump' in bi-directional text - var otherCursor = output.appendChild(elt("div", "\u00a0", "CodeMirror-cursor CodeMirror-secondarycursor")) + let otherCursor = output.appendChild(elt("div", "\u00a0", "CodeMirror-cursor CodeMirror-secondarycursor")) otherCursor.style.display = "" otherCursor.style.left = pos.other.left + "px" otherCursor.style.top = pos.other.top + "px" @@ -48,10 +48,10 @@ export function drawSelectionCursor(cm, head, output) { // Draws the given range as a highlighted selection function drawSelectionRange(cm, range, output) { - var display = cm.display, doc = cm.doc - var fragment = document.createDocumentFragment() - var padding = paddingH(cm.display), leftSide = padding.left - var rightSide = Math.max(display.sizerWidth, displayWidth(cm) - display.sizer.offsetLeft) - padding.right + let display = cm.display, doc = cm.doc + let fragment = document.createDocumentFragment() + let padding = paddingH(cm.display), leftSide = padding.left + let rightSide = Math.max(display.sizerWidth, displayWidth(cm) - display.sizer.offsetLeft) - padding.right function add(left, top, width, bottom) { if (top < 0) top = 0 @@ -63,21 +63,21 @@ function drawSelectionRange(cm, range, output) { } function drawForLine(line, fromArg, toArg) { - var lineObj = getLine(doc, line) - var lineLen = lineObj.text.length - var start, end + let lineObj = getLine(doc, line) + let lineLen = lineObj.text.length + let start, end 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, "left"), rightPos, left, right + let leftPos = coords(from, "left"), rightPos, left, right if (from == to) { rightPos = leftPos left = right = leftPos.left } else { rightPos = coords(to - 1, "right") - if (dir == "rtl") { var tmp = leftPos; leftPos = rightPos; rightPos = tmp } + if (dir == "rtl") { let tmp = leftPos; leftPos = rightPos; rightPos = tmp } left = leftPos.left right = rightPos.right } @@ -98,14 +98,14 @@ function drawSelectionRange(cm, range, output) { return {start: start, end: end} } - var sFrom = range.from(), sTo = range.to() + let sFrom = range.from(), sTo = range.to() if (sFrom.line == sTo.line) { drawForLine(sFrom.line, sFrom.ch, sTo.ch) } else { - var fromLine = getLine(doc, sFrom.line), toLine = getLine(doc, sTo.line) - var singleVLine = visualLine(fromLine) == visualLine(toLine) - var leftEnd = drawForLine(sFrom.line, sFrom.ch, singleVLine ? fromLine.text.length + 1 : null).end - var rightStart = drawForLine(sTo.line, singleVLine ? 0 : null, sTo.ch).start + let fromLine = getLine(doc, sFrom.line), toLine = getLine(doc, sTo.line) + let singleVLine = visualLine(fromLine) == visualLine(toLine) + let leftEnd = drawForLine(sFrom.line, sFrom.ch, singleVLine ? fromLine.text.length + 1 : null).end + let rightStart = drawForLine(sTo.line, singleVLine ? 0 : null, sTo.ch).start if (singleVLine) { if (leftEnd.top < rightStart.top - 2) { add(leftEnd.right, leftEnd.top, null, leftEnd.bottom) @@ -124,9 +124,9 @@ function drawSelectionRange(cm, range, output) { // Cursor-blinking export function restartBlink(cm) { if (!cm.state.focused) return - var display = cm.display + let display = cm.display clearInterval(display.blinker) - var on = true + let on = true display.cursorDiv.style.visibility = "" if (cm.options.cursorBlinkRate > 0) display.blinker = setInterval(function() { diff --git a/src/display/update_display.js b/src/display/update_display.js index 06551eec..a8d6a4de 100644 --- a/src/display/update_display.js +++ b/src/display/update_display.js @@ -18,7 +18,7 @@ import { adjustView, countDirtyView, resetView } from "./view_tracking" // DISPLAY DRAWING export function DisplayUpdate(cm, viewport, force) { - var display = cm.display + let display = cm.display this.viewport = viewport // Store some values that we'll need later (but don't want to force a relayout for) @@ -37,12 +37,12 @@ DisplayUpdate.prototype.signal = function(emitter, type) { this.events.push(arguments) } DisplayUpdate.prototype.finish = function() { - for (var i = 0; i < this.events.length; i++) + for (let i = 0; i < this.events.length; i++) signal.apply(null, this.events[i]) } export function maybeClipScrollbars(cm) { - var display = cm.display + let display = cm.display if (!display.scrollbarsClipped && display.scroller.offsetWidth) { display.nativeBarWidth = display.scroller.offsetWidth - display.scroller.clientWidth display.heightForcer.style.height = scrollGap(cm) + "px" @@ -56,7 +56,7 @@ export function maybeClipScrollbars(cm) { // (returning false) when there is nothing to be done and forced is // false. export function updateDisplayIfNeeded(cm, update) { - var display = cm.display, doc = cm.doc + let display = cm.display, doc = cm.doc if (update.editorIsHidden) { resetView(cm) @@ -76,9 +76,9 @@ export function updateDisplayIfNeeded(cm, update) { } // Compute a suitable new viewport (from & to) - var end = doc.first + doc.size - var from = Math.max(update.visible.from - cm.options.viewportMargin, doc.first) - var to = Math.min(end, update.visible.to + cm.options.viewportMargin) + let end = doc.first + doc.size + let from = Math.max(update.visible.from - cm.options.viewportMargin, doc.first) + let 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) { @@ -86,7 +86,7 @@ export function updateDisplayIfNeeded(cm, update) { to = visualLineEndNo(cm.doc, to) } - var different = from != display.viewFrom || to != display.viewTo || + let different = from != display.viewFrom || to != display.viewTo || display.lastWrapHeight != update.wrapperHeight || display.lastWrapWidth != update.wrapperWidth adjustView(cm, from, to) @@ -94,14 +94,14 @@ export function updateDisplayIfNeeded(cm, update) { // Position the mover div to align with the current scroll position cm.display.mover.style.top = display.viewOffset + "px" - var toUpdate = countDirtyView(cm) + let toUpdate = countDirtyView(cm) if (!different && toUpdate == 0 && !update.force && display.renderedView == display.view && (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() + let focused = activeElt() if (toUpdate > 4) display.lineDiv.style.display = "none" patchDisplay(cm, display.updateLineNumbers, update.dims) if (toUpdate > 4) display.lineDiv.style.display = "" @@ -128,9 +128,9 @@ export function updateDisplayIfNeeded(cm, update) { } export function postUpdateDisplay(cm, update) { - var viewport = update.viewport + let viewport = update.viewport - for (var first = true;; first = false) { + for (let first = true;; first = false) { if (!first || !cm.options.lineWrapping || update.oldDisplayWidth == displayWidth(cm)) { // Clip forced viewport to actual scrollable area. if (viewport && viewport.top != null) @@ -143,7 +143,7 @@ export function postUpdateDisplay(cm, update) { } if (!updateDisplayIfNeeded(cm, update)) break updateHeightsInViewport(cm) - var barMeasure = measureForScrollbars(cm) + let barMeasure = measureForScrollbars(cm) updateSelection(cm) updateScrollbars(cm, barMeasure) setDocumentHeight(cm, barMeasure) @@ -157,11 +157,11 @@ export function postUpdateDisplay(cm, update) { } export function updateDisplaySimple(cm, viewport) { - var update = new DisplayUpdate(cm, viewport) + let update = new DisplayUpdate(cm, viewport) if (updateDisplayIfNeeded(cm, update)) { updateHeightsInViewport(cm) postUpdateDisplay(cm, update) - var barMeasure = measureForScrollbars(cm) + let barMeasure = measureForScrollbars(cm) updateSelection(cm) updateScrollbars(cm, barMeasure) setDocumentHeight(cm, barMeasure) @@ -174,11 +174,11 @@ export function updateDisplaySimple(cm, viewport) { // that are not there yet, and updating the ones that are out of // date. function patchDisplay(cm, updateNumbersFrom, dims) { - var display = cm.display, lineNumbers = cm.options.lineNumbers - var container = display.lineDiv, cur = container.firstChild + let display = cm.display, lineNumbers = cm.options.lineNumbers + let container = display.lineDiv, cur = container.firstChild function rm(node) { - var next = node.nextSibling + let next = node.nextSibling // Works around a throw-scroll bug in OS X Webkit if (webkit && mac && cm.display.currentWheelTarget == node) node.style.display = "none" @@ -187,18 +187,18 @@ function patchDisplay(cm, updateNumbersFrom, dims) { return next } - var view = display.view, lineN = display.viewFrom + let view = display.view, lineN = display.viewFrom // Loop over the elements in the view, syncing cur (the DOM nodes // in display.lineDiv) with the view as we go. - for (var i = 0; i < view.length; i++) { - var lineView = view[i] + for (let i = 0; i < view.length; i++) { + let lineView = view[i] if (lineView.hidden) { } else if (!lineView.node || lineView.node.parentNode != container) { // Not drawn yet - var node = buildLineElement(cm, lineView, lineN, dims) + let node = buildLineElement(cm, lineView, lineN, dims) container.insertBefore(node, cur) } else { // Already drawn while (cur != lineView.node) cur = rm(cur) - var updateNumber = lineNumbers && updateNumbersFrom != null && + let updateNumber = lineNumbers && updateNumbersFrom != null && updateNumbersFrom <= lineN && lineView.lineNumber if (lineView.changes) { if (indexOf(lineView.changes, "gutter") > -1) updateNumber = false @@ -216,7 +216,7 @@ function patchDisplay(cm, updateNumbersFrom, dims) { } export function updateGutterSpace(cm) { - var width = cm.display.gutters.offsetWidth + let width = cm.display.gutters.offsetWidth cm.display.sizer.style.marginLeft = width + "px" } diff --git a/src/display/update_lines.js b/src/display/update_lines.js index 8de0698c..095cddef 100644 --- a/src/display/update_lines.js +++ b/src/display/update_lines.js @@ -6,25 +6,25 @@ import { ie, ie_version } from "../util/browser" // Read the actual heights of the rendered lines, and update their // stored heights to match. export function updateHeightsInViewport(cm) { - var display = cm.display - var prevBottom = display.lineDiv.offsetTop - for (var i = 0; i < display.view.length; i++) { - var cur = display.view[i], height + let display = cm.display + let prevBottom = display.lineDiv.offsetTop + for (let i = 0; i < display.view.length; i++) { + let cur = display.view[i], height if (cur.hidden) continue if (ie && ie_version < 8) { - var bot = cur.node.offsetTop + cur.node.offsetHeight + let bot = cur.node.offsetTop + cur.node.offsetHeight height = bot - prevBottom prevBottom = bot } else { - var box = cur.node.getBoundingClientRect() + let box = cur.node.getBoundingClientRect() height = box.bottom - box.top } - var diff = cur.line.height - height + let diff = cur.line.height - height if (height < 2) height = textHeight(display) if (diff > .001 || diff < -.001) { updateLineHeight(cur.line, height) updateWidgetHeight(cur.line) - if (cur.rest) for (var j = 0; j < cur.rest.length; j++) + if (cur.rest) for (let j = 0; j < cur.rest.length; j++) updateWidgetHeight(cur.rest[j]) } } @@ -33,7 +33,7 @@ export function updateHeightsInViewport(cm) { // Read and store the height of line widgets associated with the // given line. function updateWidgetHeight(line) { - if (line.widgets) for (var i = 0; i < line.widgets.length; ++i) + if (line.widgets) for (let i = 0; i < line.widgets.length; ++i) line.widgets[i].height = line.widgets[i].node.parentNode.offsetHeight } @@ -41,15 +41,15 @@ function updateWidgetHeight(line) { // the the current scroll position). viewport may contain top, // height, and ensure (see op.scrollToPos) properties. export function visibleLines(display, doc, viewport) { - var top = viewport && viewport.top != null ? Math.max(0, viewport.top) : display.scroller.scrollTop + let 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 + let bottom = viewport && viewport.bottom != null ? viewport.bottom : top + display.wrapper.clientHeight - var from = lineAtHeight(doc, top), to = lineAtHeight(doc, bottom) + let 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 + let ensureFrom = viewport.ensure.from.line, ensureTo = viewport.ensure.to.line if (ensureFrom < from) { from = ensureFrom to = lineAtHeight(doc, heightAtLine(getLine(doc, ensureFrom)) + display.wrapper.clientHeight) diff --git a/src/display/view_tracking.js b/src/display/view_tracking.js index d7ca682b..b9abd2fc 100644 --- a/src/display/view_tracking.js +++ b/src/display/view_tracking.js @@ -15,7 +15,7 @@ export function regChange(cm, from, to, lendiff) { if (to == null) to = cm.doc.first + cm.doc.size if (!lendiff) lendiff = 0 - var display = cm.display + let display = cm.display if (lendiff && to < display.viewTo && (display.updateLineNumbers == null || display.updateLineNumbers > from)) display.updateLineNumbers = from @@ -35,7 +35,7 @@ export function regChange(cm, from, to, lendiff) { } else if (from <= display.viewFrom && to >= display.viewTo) { // Full overlap resetView(cm) } else if (from <= display.viewFrom) { // Top overlap - var cut = viewCuttingPoint(cm, to, to + lendiff, 1) + let cut = viewCuttingPoint(cm, to, to + lendiff, 1) if (cut) { display.view = display.view.slice(cut.index) display.viewFrom = cut.lineN @@ -44,7 +44,7 @@ export function regChange(cm, from, to, lendiff) { resetView(cm) } } else if (to >= display.viewTo) { // Bottom overlap - var cut = viewCuttingPoint(cm, from, from, -1) + let cut = viewCuttingPoint(cm, from, from, -1) if (cut) { display.view = display.view.slice(0, cut.index) display.viewTo = cut.lineN @@ -52,8 +52,8 @@ export function regChange(cm, from, to, lendiff) { resetView(cm) } } else { // Gap in the middle - var cutTop = viewCuttingPoint(cm, from, from, -1) - var cutBot = viewCuttingPoint(cm, to, to + lendiff, 1) + let cutTop = viewCuttingPoint(cm, from, from, -1) + let cutBot = viewCuttingPoint(cm, to, to + lendiff, 1) if (cutTop && cutBot) { display.view = display.view.slice(0, cutTop.index) .concat(buildViewArray(cm, cutTop.lineN, cutBot.lineN)) @@ -64,7 +64,7 @@ export function regChange(cm, from, to, lendiff) { } } - var ext = display.externalMeasured + let ext = display.externalMeasured if (ext) { if (to < ext.lineN) ext.lineN += lendiff @@ -77,14 +77,14 @@ export function regChange(cm, from, to, lendiff) { // "gutter", "class", "widget" export function regLineChange(cm, line, type) { cm.curOp.viewChanged = true - var display = cm.display, ext = cm.display.externalMeasured + let display = cm.display, ext = cm.display.externalMeasured if (ext && line >= ext.lineN && line < ext.lineN + ext.size) display.externalMeasured = null if (line < display.viewFrom || line >= display.viewTo) return - var lineView = display.view[findViewIndex(cm, line)] + let lineView = display.view[findViewIndex(cm, line)] if (lineView.node == null) return - var arr = lineView.changes || (lineView.changes = []) + let arr = lineView.changes || (lineView.changes = []) if (indexOf(arr, type) == -1) arr.push(type) } @@ -96,10 +96,11 @@ export function resetView(cm) { } function viewCuttingPoint(cm, oldN, newN, dir) { - var index = findViewIndex(cm, oldN), diff, view = cm.display.view + let index = findViewIndex(cm, oldN), diff, view = cm.display.view if (!sawCollapsedSpans || newN == cm.doc.first + cm.doc.size) return {index: index, lineN: newN} - for (var i = 0, n = cm.display.viewFrom; i < index; i++) + let n = cm.display.viewFrom + for (let i = 0; i < index; i++) n += view[i].size if (n != oldN) { if (dir > 0) { @@ -122,7 +123,7 @@ function viewCuttingPoint(cm, oldN, newN, dir) { // Force the view to cover a given range, adding empty view element // or clipping off existing ones as needed. export function adjustView(cm, from, to) { - var display = cm.display, view = display.view + let display = cm.display, view = display.view if (view.length == 0 || from >= display.viewTo || to <= display.viewFrom) { display.view = buildViewArray(cm, from, to) display.viewFrom = from @@ -143,9 +144,9 @@ export function adjustView(cm, from, to) { // Count the number of lines in the view whose DOM representation is // out of date (or nonexistent). export function countDirtyView(cm) { - var view = cm.display.view, dirty = 0 - for (var i = 0; i < view.length; i++) { - var lineView = view[i] + let view = cm.display.view, dirty = 0 + for (let i = 0; i < view.length; i++) { + let lineView = view[i] if (!lineView.hidden && (!lineView.node || lineView.changes)) ++dirty } return dirty diff --git a/src/edit/CodeMirror.js b/src/edit/CodeMirror.js index ee549d54..90650ff0 100644 --- a/src/edit/CodeMirror.js +++ b/src/edit/CodeMirror.js @@ -34,12 +34,12 @@ export function CodeMirror(place, options) { copyObj(defaults, options, false) setGuttersForLineNumbers(options) - var doc = options.value + let doc = options.value if (typeof doc == "string") doc = new Doc(doc, options.mode, null, options.lineSeparator) this.doc = doc - var input = new CodeMirror.inputStyles[options.inputStyle](this) - var display = this.display = new Display(place, doc, input) + let input = new CodeMirror.inputStyles[options.inputStyle](this) + let display = this.display = new Display(place, doc, input) display.wrapper.CodeMirror = this updateGutters(this) themeChanged(this) @@ -64,7 +64,7 @@ export function CodeMirror(place, options) { specialChars: null } - var cm = this + let cm = this // Override magic textarea content restore that IE sometimes does // on our hidden textarea on reload @@ -82,11 +82,11 @@ export function CodeMirror(place, options) { else onBlur(this) - for (var opt in optionHandlers) if (optionHandlers.hasOwnProperty(opt)) + for (let opt in optionHandlers) if (optionHandlers.hasOwnProperty(opt)) optionHandlers[opt](this, options[opt], Init) maybeUpdateLineNumberWidth(this) if (options.finishInit) options.finishInit(this) - for (var i = 0; i < initHooks.length; ++i) initHooks[i](this) + for (let i = 0; i < initHooks.length; ++i) initHooks[i](this) endOperation(this) // Suppress optimizelegibility in Webkit, since it breaks text // measuring on line wrapping boundaries. @@ -104,16 +104,16 @@ export default CodeMirror // Attach the necessary event handlers when initializing the editor function registerEventHandlers(cm) { - var d = cm.display + let d = cm.display on(d.scroller, "mousedown", operation(cm, onMouseDown)) // Older IE's will not fire a second mousedown for a double click if (ie && ie_version < 11) on(d.scroller, "dblclick", operation(cm, function(e) { if (signalDOMEvent(cm, e)) return - var pos = posFromMouse(cm, e) + let pos = posFromMouse(cm, e) if (!pos || clickInGutter(cm, e) || eventInWidget(cm.display, e)) return e_preventDefault(e) - var word = cm.findWordAt(pos) + let word = cm.findWordAt(pos) extendSelection(cm.doc, word.anchor, word.head) })) else @@ -124,7 +124,7 @@ function registerEventHandlers(cm) { if (!captureRightClick) on(d.scroller, "contextmenu", function(e) {onContextMenu(cm, e)}) // Used to suppress mouse event handling when a touch happens - var touchFinished, prevTouch = {end: 0} + let touchFinished, prevTouch = {end: 0} function finishTouch() { if (d.activeTouch) { touchFinished = setTimeout(function() {d.activeTouch = null}, 1000) @@ -134,18 +134,18 @@ function registerEventHandlers(cm) { } function isMouseLikeTouchEvent(e) { if (e.touches.length != 1) return false - var touch = e.touches[0] + let touch = e.touches[0] return touch.radiusX <= 1 && touch.radiusY <= 1 } function farAway(touch, other) { if (other.left == null) return true - var dx = other.left - touch.left, dy = other.top - touch.top + let dx = other.left - touch.left, dy = other.top - touch.top return dx * dx + dy * dy > 20 * 20 } on(d.scroller, "touchstart", function(e) { if (!signalDOMEvent(cm, e) && !isMouseLikeTouchEvent(e)) { clearTimeout(touchFinished) - var now = +new Date + let now = +new Date d.activeTouch = {start: now, moved: false, prev: now - prevTouch.end <= 300 ? prevTouch : null} if (e.touches.length == 1) { @@ -158,10 +158,10 @@ function registerEventHandlers(cm) { if (d.activeTouch) d.activeTouch.moved = true }) on(d.scroller, "touchend", function(e) { - var touch = d.activeTouch + let touch = d.activeTouch if (touch && !eventInWidget(d, e) && touch.left != null && !touch.moved && new Date - touch.start < 300) { - var pos = cm.coordsChar(d.activeTouch, "page"), range + let pos = cm.coordsChar(d.activeTouch, "page"), range if (!touch.prev || farAway(touch, touch.prev)) // Single tap range = new Range(pos, pos) else if (!touch.prev.prev || farAway(touch, touch.prev.prev)) // Double tap @@ -201,7 +201,7 @@ function registerEventHandlers(cm) { leave: function(e) {if (!signalDOMEvent(cm, e)) { clearDragCursor(cm) }} } - var inp = d.input.getField() + let inp = d.input.getField() on(inp, "keyup", function(e) { onKeyUp.call(cm, e) }) on(inp, "keydown", operation(cm, onKeyDown)) on(inp, "keypress", operation(cm, onKeyPress)) @@ -209,5 +209,5 @@ function registerEventHandlers(cm) { on(inp, "blur", function (e) { onBlur(cm, e) }) } -var initHooks = [] +let initHooks = [] CodeMirror.defineInitHook = function(f) {initHooks.push(f)} diff --git a/src/edit/commands.js b/src/edit/commands.js index 2d670ab5..97a30d6d 100644 --- a/src/edit/commands.js +++ b/src/edit/commands.js @@ -11,7 +11,7 @@ import { getOrder, lineLeft, lineRight } from "../util/bidi" // Commands are parameter-less actions that can be performed on an // editor, mostly used for keybindings. -export var commands = { +export let commands = { selectAll: selectAll, singleSelection: function(cm) { cm.setSelection(cm.getCursor("anchor"), cm.getCursor("head"), sel_dontScroll) @@ -19,7 +19,7 @@ export var commands = { killLine: function(cm) { deleteNearSelection(cm, function(range) { if (range.empty()) { - var len = getLine(cm.doc, range.head.line).text.length + let len = getLine(cm.doc, range.head.line).text.length if (range.head.ch == len && range.head.line < cm.lastLine()) return {from: range.head, to: Pos(range.head.line + 1, 0)} else @@ -42,15 +42,15 @@ export var commands = { }, 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") + let top = cm.charCoords(range.head, "div").top + 5 + let 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") + let top = cm.charCoords(range.head, "div").top + 5 + let rightPos = cm.coordsChar({left: cm.display.lineDiv.offsetWidth + 100, top: top}, "div") return {from: range.from(), to: rightPos } }) }, @@ -75,20 +75,20 @@ export var commands = { }, goLineRight: function(cm) { cm.extendSelectionsBy(function(range) { - var top = cm.charCoords(range.head, "div").top + 5 + let top = cm.charCoords(range.head, "div").top + 5 return cm.coordsChar({left: cm.display.lineDiv.offsetWidth + 100, top: top}, "div") }, sel_move) }, goLineLeft: function(cm) { cm.extendSelectionsBy(function(range) { - var top = cm.charCoords(range.head, "div").top + 5 + let top = cm.charCoords(range.head, "div").top + 5 return cm.coordsChar({left: 0, top: top}, "div") }, sel_move) }, goLineLeftSmart: function(cm) { cm.extendSelectionsBy(function(range) { - var top = cm.charCoords(range.head, "div").top + 5 - var pos = cm.coordsChar({left: 0, top: top}, "div") + let top = cm.charCoords(range.head, "div").top + 5 + let pos = cm.coordsChar({left: 0, top: top}, "div") if (pos.ch < cm.getLine(pos.line).search(/\S/)) return lineStartSmart(cm, range.head) return pos }, sel_move) @@ -116,10 +116,10 @@ export var commands = { indentLess: function(cm) {cm.indentSelection("subtract")}, insertTab: function(cm) {cm.replaceSelection("\t")}, insertSoftTab: function(cm) { - var spaces = [], ranges = cm.listSelections(), tabSize = cm.options.tabSize - for (var i = 0; i < ranges.length; i++) { - var pos = ranges[i].from() - var col = countColumn(cm.getLine(pos.line), pos.ch, tabSize) + let spaces = [], ranges = cm.listSelections(), tabSize = cm.options.tabSize + for (let i = 0; i < ranges.length; i++) { + let pos = ranges[i].from() + let col = countColumn(cm.getLine(pos.line), pos.ch, tabSize) spaces.push(spaceStr(tabSize - col % tabSize)) } cm.replaceSelections(spaces) @@ -130,9 +130,9 @@ export var commands = { }, transposeChars: function(cm) { runInOp(cm, function() { - var ranges = cm.listSelections(), newSel = [] - for (var i = 0; i < ranges.length; i++) { - var cur = ranges[i].head, line = getLine(cm.doc, cur.line).text + let ranges = cm.listSelections(), newSel = [] + for (let i = 0; i < ranges.length; i++) { + let cur = ranges[i].head, line = getLine(cm.doc, cur.line).text if (line) { if (cur.ch == line.length) cur = new Pos(cur.line, cur.ch - 1) if (cur.ch > 0) { @@ -140,7 +140,7 @@ export var commands = { cm.replaceRange(line.charAt(cur.ch - 1) + line.charAt(cur.ch - 2), Pos(cur.line, cur.ch - 2), cur, "+transpose") } else if (cur.line > cm.doc.first) { - var prev = getLine(cm.doc, cur.line - 1).text + let prev = getLine(cm.doc, cur.line - 1).text if (prev) cm.replaceRange(line.charAt(0) + cm.doc.lineSeparator() + prev.charAt(prev.length - 1), @@ -154,11 +154,11 @@ export var commands = { }, newlineAndIndent: function(cm) { runInOp(cm, function() { - var sels = cm.listSelections() - for (var i = sels.length - 1; i >= 0; i--) + let sels = cm.listSelections() + for (let i = sels.length - 1; i >= 0; i--) cm.replaceRange(cm.doc.lineSeparator(), sels[i].anchor, sels[i].head, "+input") sels = cm.listSelections() - for (var i = 0; i < sels.length; i++) + for (let i = 0; i < sels.length; i++) cm.indentLine(sels[i].from().line, null, true) ensureCursorVisible(cm) }) @@ -169,30 +169,30 @@ export var commands = { function lineStart(cm, lineN) { - var line = getLine(cm.doc, lineN) - var visual = visualLine(line) + let line = getLine(cm.doc, lineN) + let visual = visualLine(line) if (visual != line) lineN = lineNo(visual) - var order = getOrder(visual) - var ch = !order ? 0 : order[0].level % 2 ? lineRight(visual) : lineLeft(visual) + let order = getOrder(visual) + let ch = !order ? 0 : order[0].level % 2 ? lineRight(visual) : lineLeft(visual) return Pos(lineN, ch) } function lineEnd(cm, lineN) { - var merged, line = getLine(cm.doc, lineN) + let merged, line = getLine(cm.doc, lineN) while (merged = collapsedSpanAtEnd(line)) { line = merged.find(1, true).line lineN = null } - var order = getOrder(line) - var ch = !order ? line.text.length : order[0].level % 2 ? lineLeft(line) : lineRight(line) + let order = getOrder(line) + let ch = !order ? line.text.length : order[0].level % 2 ? lineLeft(line) : lineRight(line) return Pos(lineN == null ? lineNo(line) : lineN, ch) } function lineStartSmart(cm, pos) { - var start = lineStart(cm, pos.line) - var line = getLine(cm.doc, start.line) - var order = getOrder(line) + let start = lineStart(cm, pos.line) + let line = getLine(cm.doc, start.line) + let order = getOrder(line) if (!order || order[0].level == 0) { - var firstNonWS = Math.max(0, line.text.search(/\S/)) - var inWS = pos.line == start.line && pos.ch <= firstNonWS && pos.ch + let firstNonWS = Math.max(0, line.text.search(/\S/)) + let inWS = pos.line == start.line && pos.ch <= firstNonWS && pos.ch return Pos(start.line, inWS ? 0 : firstNonWS) } return start diff --git a/src/edit/deleteNearSelection.js b/src/edit/deleteNearSelection.js index 00b4aa08..38c5342f 100644 --- a/src/edit/deleteNearSelection.js +++ b/src/edit/deleteNearSelection.js @@ -7,13 +7,13 @@ import { lst } from "../util/misc" // Helper for deleting text near the selection(s), used to implement // backspace, delete, and similar functionality. export function deleteNearSelection(cm, compute) { - var ranges = cm.doc.sel.ranges, kill = [] + let ranges = cm.doc.sel.ranges, kill = [] // Build up a set of ranges to kill first, merging overlapping // ranges. - for (var i = 0; i < ranges.length; i++) { - var toKill = compute(ranges[i]) + for (let i = 0; i < ranges.length; i++) { + let toKill = compute(ranges[i]) while (kill.length && cmp(toKill.from, lst(kill).to) <= 0) { - var replaced = kill.pop() + let replaced = kill.pop() if (cmp(replaced.from, toKill.from) < 0) { toKill.from = replaced.from break @@ -23,7 +23,7 @@ export function deleteNearSelection(cm, compute) { } // Next, remove those actual ranges. runInOp(cm, function() { - for (var i = kill.length - 1; i >= 0; i--) + for (let i = kill.length - 1; i >= 0; i--) replaceRange(cm.doc, "", kill[i].from, kill[i].to, "+delete") ensureCursorVisible(cm) }) diff --git a/src/edit/drop_events.js b/src/edit/drop_events.js index d2aeaa7c..b8024d92 100644 --- a/src/edit/drop_events.js +++ b/src/edit/drop_events.js @@ -14,34 +14,34 @@ import { indexOf } from "../util/misc" // 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 +let lastDrop = 0 export function onDrop(e) { - var cm = this + let cm = this clearDragCursor(cm) if (signalDOMEvent(cm, e) || eventInWidget(cm.display, e)) return e_preventDefault(e) if (ie) lastDrop = +new Date - var pos = posFromMouse(cm, e, true), files = e.dataTransfer.files + let pos = posFromMouse(cm, e, true), files = e.dataTransfer.files if (!pos || cm.isReadOnly()) return // Might be a file drop, in which case we simply extract the text // and insert it. if (files && files.length && window.FileReader && window.File) { - var n = files.length, text = Array(n), read = 0 - var loadFile = function(file, i) { + let n = files.length, text = Array(n), read = 0 + let loadFile = function(file, i) { if (cm.options.allowDropFileTypes && indexOf(cm.options.allowDropFileTypes, file.type) == -1) return - var reader = new FileReader + let reader = new FileReader reader.onload = operation(cm, function() { - var content = reader.result + let content = reader.result if (/[\x00-\x08\x0e-\x1f]{2}/.test(content)) content = "" text[i] = content if (++read == n) { pos = clipPos(cm.doc, pos) - var change = {from: pos, to: pos, + let change = {from: pos, to: pos, text: cm.doc.splitLines(text.join(cm.doc.lineSeparator())), origin: "paste"} makeChange(cm.doc, change) @@ -50,7 +50,7 @@ export function onDrop(e) { }) reader.readAsText(file) } - for (var i = 0; i < n; ++i) loadFile(files[i], i) + for (let i = 0; i < n; ++i) loadFile(files[i], i) } else { // Normal drop // Don't do a replace if the drop happened inside of the selected text. if (cm.state.draggingText && cm.doc.sel.contains(pos) > -1) { @@ -60,12 +60,13 @@ export function onDrop(e) { return } try { - var text = e.dataTransfer.getData("Text") + let text = e.dataTransfer.getData("Text") if (text) { + let selected if (cm.state.draggingText && !cm.state.draggingText.copy) - var selected = cm.listSelections() + selected = cm.listSelections() setSelectionNoUndo(cm.doc, simpleSelection(pos, pos)) - if (selected) for (var i = 0; i < selected.length; ++i) + if (selected) for (let i = 0; i < selected.length; ++i) replaceRange(cm.doc, "", selected[i].anchor, selected[i].head, "drag") cm.replaceSelection(text, "around", "paste") cm.display.input.focus() @@ -85,7 +86,7 @@ export function onDragStart(cm, e) { // 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. if (e.dataTransfer.setDragImage && !safari) { - var img = elt("img", null, null, "position: fixed; left: 0; top: 0;") + let img = elt("img", null, null, "position: fixed; left: 0; top: 0;") img.src = "data:image/gif;base64,R0lGODlhAQABAAAAACH5BAEKAAEALAAAAAABAAEAAAICTAEAOw==" if (presto) { img.width = img.height = 1 @@ -99,9 +100,9 @@ export function onDragStart(cm, e) { } export function onDragOver(cm, e) { - var pos = posFromMouse(cm, e) + let pos = posFromMouse(cm, e) if (!pos) return - var frag = document.createDocumentFragment() + let frag = document.createDocumentFragment() drawSelectionCursor(cm, pos, frag) if (!cm.display.dragCursor) { cm.display.dragCursor = elt("div", null, "CodeMirror-cursors CodeMirror-dragcursors") diff --git a/src/edit/fromTextArea.js b/src/edit/fromTextArea.js index dda26d4b..7d221014 100644 --- a/src/edit/fromTextArea.js +++ b/src/edit/fromTextArea.js @@ -13,19 +13,22 @@ export function fromTextArea(textarea, options) { // Set autofocus to true if this textarea is focused, or if it has // autofocus and no other element is focused. if (options.autofocus == null) { - var hasFocus = activeElt() + let hasFocus = activeElt() options.autofocus = hasFocus == textarea || textarea.getAttribute("autofocus") != null && hasFocus == document.body } function save() {textarea.value = cm.getValue()} + + let realSubmit if (textarea.form) { on(textarea.form, "submit", save) // Deplorable hack to make the submit method do the right thing. if (!options.leaveSubmitMethodAlone) { - var form = textarea.form, realSubmit = form.submit + let form = textarea.form + realSubmit = form.submit try { - var wrappedSubmit = form.submit = function() { + let wrappedSubmit = form.submit = function() { save() form.submit = realSubmit form.submit() @@ -52,7 +55,7 @@ export function fromTextArea(textarea, options) { } textarea.style.display = "none" - var cm = CodeMirror(function(node) { + let cm = CodeMirror(function(node) { textarea.parentNode.insertBefore(node, textarea.nextSibling) }, options) return cm diff --git a/src/edit/global_events.js b/src/edit/global_events.js index 57542f2d..a7ec22fe 100644 --- a/src/edit/global_events.js +++ b/src/edit/global_events.js @@ -7,14 +7,14 @@ import { on } from "../util/event" function forEachCodeMirror(f) { if (!document.body.getElementsByClassName) return - var byClass = document.body.getElementsByClassName("CodeMirror") - for (var i = 0; i < byClass.length; i++) { - var cm = byClass[i].CodeMirror + let byClass = document.body.getElementsByClassName("CodeMirror") + for (let i = 0; i < byClass.length; i++) { + let cm = byClass[i].CodeMirror if (cm) f(cm) } } -var globalsRegistered = false +let globalsRegistered = false export function ensureGlobalHandlers() { if (globalsRegistered) return registerGlobalHandlers() @@ -22,7 +22,7 @@ export function ensureGlobalHandlers() { } function registerGlobalHandlers() { // When the window resizes, we need to refresh active editors. - var resizeTimer + let resizeTimer on(window, "resize", function() { if (resizeTimer == null) resizeTimer = setTimeout(function() { resizeTimer = null @@ -36,7 +36,7 @@ function registerGlobalHandlers() { } // Called when the window resizes function onResize(cm) { - var d = cm.display + let d = cm.display if (d.lastWrapHeight == d.wrapper.clientHeight && d.lastWrapWidth == d.wrapper.clientWidth) return // Might be a text scaling operation, clear size caches. diff --git a/src/edit/key_events.js b/src/edit/key_events.js index 9a494441..8530aef3 100644 --- a/src/edit/key_events.js +++ b/src/edit/key_events.js @@ -19,7 +19,7 @@ function doHandleBinding(cm, bound, dropShift) { // Ensure previous input has been read, so that the handler sees a // consistent view of the document cm.display.input.ensurePolled() - var prevShift = cm.display.shift, done = false + let prevShift = cm.display.shift, done = false try { if (cm.isReadOnly()) cm.state.suppressEdits = true if (dropShift) cm.display.shift = false @@ -32,17 +32,17 @@ function doHandleBinding(cm, bound, dropShift) { } function lookupKeyForEditor(cm, name, handle) { - for (var i = 0; i < cm.state.keyMaps.length; i++) { - var result = lookupKey(name, cm.state.keyMaps[i], handle, cm) + for (let i = 0; i < cm.state.keyMaps.length; i++) { + let result = lookupKey(name, cm.state.keyMaps[i], handle, cm) if (result) return result } return (cm.options.extraKeys && lookupKey(name, cm.options.extraKeys, handle, cm)) || lookupKey(name, cm.options.keyMap, handle, cm) } -var stopSeq = new Delayed +let stopSeq = new Delayed function dispatchKey(cm, name, e, handle) { - var seq = cm.state.keySeq + let seq = cm.state.keySeq if (seq) { if (isModifierKey(name)) return "handled" stopSeq.set(50, function() { @@ -53,7 +53,7 @@ function dispatchKey(cm, name, e, handle) { }) name = seq + " " + name } - var result = lookupKeyForEditor(cm, name, handle) + let result = lookupKeyForEditor(cm, name, handle) if (result == "multi") cm.state.keySeq = name @@ -74,7 +74,7 @@ function dispatchKey(cm, name, e, handle) { // Handle a key from the keydown event. function handleKeyBinding(cm, e) { - var name = keyName(e, true) + let name = keyName(e, true) if (!name) return false if (e.shiftKey && !cm.state.keySeq) { @@ -97,16 +97,16 @@ function handleCharBinding(cm, e, ch) { function(b) { return doHandleBinding(cm, b, true) }) } -var lastStoppedKey = null +let lastStoppedKey = null export function onKeyDown(e) { - var cm = this + let cm = this cm.curOp.focus = activeElt() if (signalDOMEvent(cm, e)) return // IE does strange things with escape. if (ie && ie_version < 11 && e.keyCode == 27) e.returnValue = false - var code = e.keyCode + let code = e.keyCode cm.display.shift = code == 16 || e.shiftKey - var handled = handleKeyBinding(cm, e) + let handled = handleKeyBinding(cm, e) if (presto) { lastStoppedKey = handled ? code : null // Opera has no cut event... we try to at least catch the key combo @@ -120,7 +120,7 @@ export function onKeyDown(e) { } function showCrossHair(cm) { - var lineDiv = cm.display.lineDiv + let lineDiv = cm.display.lineDiv addClass(lineDiv, "CodeMirror-crosshair") function up(e) { @@ -140,12 +140,12 @@ export function onKeyUp(e) { } export function onKeyPress(e) { - var cm = this + let cm = this if (eventInWidget(cm.display, e) || signalDOMEvent(cm, e) || e.ctrlKey && !e.altKey || mac && e.metaKey) return - var keyCode = e.keyCode, charCode = e.charCode + let keyCode = e.keyCode, charCode = e.charCode if (presto && keyCode == lastStoppedKey) {lastStoppedKey = null; e_preventDefault(e); return} if ((presto && (!e.which || e.which < 10)) && handleKeyBinding(cm, e)) return - var ch = String.fromCharCode(charCode == null ? keyCode : charCode) + let ch = String.fromCharCode(charCode == null ? keyCode : charCode) // Some browsers fire keypress events for backspace if (ch == "\x08") return if (handleCharBinding(cm, e, ch)) return diff --git a/src/edit/main.js b/src/edit/main.js index 3f76d702..55432c08 100644 --- a/src/edit/main.js +++ b/src/edit/main.js @@ -17,8 +17,8 @@ addEditorMethods(CodeMirror) import Doc from "../model/Doc" // Set up methods on CodeMirror's prototype to redirect to the editor's document. -var dontDelegate = "iter insert remove copy getEditor constructor".split(" ") -for (var prop in Doc.prototype) if (Doc.prototype.hasOwnProperty(prop) && indexOf(dontDelegate, prop) < 0) +let dontDelegate = "iter insert remove copy getEditor constructor".split(" ") +for (let prop in Doc.prototype) if (Doc.prototype.hasOwnProperty(prop) && indexOf(dontDelegate, prop) < 0) CodeMirror.prototype[prop] = (function(method) { return function() {return method.apply(this.doc, arguments)} })(Doc.prototype[prop]) diff --git a/src/edit/methods.js b/src/edit/methods.js index 828304d3..04be8512 100644 --- a/src/edit/methods.js +++ b/src/edit/methods.js @@ -32,16 +32,16 @@ import { regChange, regLineChange } from "../display/view_tracking" // convenience. export default function(CodeMirror) { - var optionHandlers = CodeMirror.optionHandlers + let optionHandlers = CodeMirror.optionHandlers - var helpers = CodeMirror.helpers = {} + let helpers = CodeMirror.helpers = {} CodeMirror.prototype = { constructor: CodeMirror, focus: function(){window.focus(); this.display.input.focus()}, setOption: function(option, value) { - var options = this.options, old = options[option] + let options = this.options, old = options[option] if (options[option] == value && option != "mode") return options[option] = value if (optionHandlers.hasOwnProperty(option)) @@ -55,8 +55,8 @@ export default function(CodeMirror) { this.state.keyMaps[bottom ? "push" : "unshift"](getKeyMap(map)) }, removeKeyMap: function(map) { - var maps = this.state.keyMaps - for (var i = 0; i < maps.length; ++i) + let maps = this.state.keyMaps + for (let i = 0; i < maps.length; ++i) if (maps[i] == map || maps[i].name == map) { maps.splice(i, 1) return true @@ -64,7 +64,7 @@ export default function(CodeMirror) { }, addOverlay: methodOp(function(spec, options) { - var mode = spec.token ? spec : CodeMirror.getMode(this.options, spec) + let mode = spec.token ? spec : CodeMirror.getMode(this.options, spec) if (mode.startState) throw new Error("Overlays may not be stateful.") insertSorted(this.state.overlays, {mode: mode, modeSpec: spec, opaque: options && options.opaque, @@ -74,9 +74,9 @@ export default function(CodeMirror) { regChange(this) }), removeOverlay: methodOp(function(spec) { - var overlays = this.state.overlays - for (var i = 0; i < overlays.length; ++i) { - var cur = overlays[i].modeSpec + let overlays = this.state.overlays + for (let i = 0; i < overlays.length; ++i) { + let cur = overlays[i].modeSpec if (cur == spec || typeof spec == "string" && cur.name == spec) { overlays.splice(i, 1) this.state.modeGen++ @@ -94,16 +94,16 @@ export default function(CodeMirror) { if (isLine(this.doc, n)) indentLine(this, n, dir, aggressive) }), indentSelection: methodOp(function(how) { - var ranges = this.doc.sel.ranges, end = -1 - for (var i = 0; i < ranges.length; i++) { - var range = ranges[i] + let ranges = this.doc.sel.ranges, end = -1 + for (let i = 0; i < ranges.length; i++) { + let range = ranges[i] if (!range.empty()) { - var from = range.from(), to = range.to() - var start = Math.max(end, from.line) + let from = range.from(), to = range.to() + let start = Math.max(end, from.line) end = Math.min(this.lastLine(), to.line - (to.ch ? 0 : 1)) + 1 - for (var j = start; j < end; ++j) + for (let j = start; j < end; ++j) indentLine(this, j, how) - var newRanges = this.doc.sel.ranges + let newRanges = this.doc.sel.ranges if (from.ch == 0 && ranges.length == newRanges.length && newRanges[i].from().ch > 0) replaceOneSelection(this.doc, i, new Range(from, newRanges[i].to()), sel_dontScroll) } else if (range.head.line > end) { @@ -126,22 +126,22 @@ export default function(CodeMirror) { getTokenTypeAt: function(pos) { 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 - var type + let styles = getLineStyles(this, getLine(this.doc, pos.line)) + let before = 0, after = (styles.length - 1) / 2, ch = pos.ch + let type if (ch == 0) type = styles[2] else for (;;) { - var mid = (before + after) >> 1 + let mid = (before + after) >> 1 if ((mid ? styles[mid * 2 - 1] : 0) >= ch) after = mid else if (styles[mid * 2 + 1] < ch) before = mid + 1 else { type = styles[mid * 2 + 2]; break } } - var cut = type ? type.indexOf("cm-overlay ") : -1 + let cut = type ? type.indexOf("cm-overlay ") : -1 return cut < 0 ? type : cut == 0 ? null : type.slice(0, cut - 1) }, getModeAt: function(pos) { - var mode = this.doc.mode + let mode = this.doc.mode if (!mode.innerMode) return mode return CodeMirror.innerMode(mode, this.getTokenAt(pos).state).mode }, @@ -151,14 +151,14 @@ export default function(CodeMirror) { }, getHelpers: function(pos, type) { - var found = [] + let found = [] if (!helpers.hasOwnProperty(type)) return found - var help = helpers[type], mode = this.getModeAt(pos) + let help = helpers[type], mode = this.getModeAt(pos) if (typeof mode[type] == "string") { if (help[mode[type]]) found.push(help[mode[type]]) } else if (mode[type]) { - for (var i = 0; i < mode[type].length; i++) { - var val = help[mode[type][i]] + for (let i = 0; i < mode[type].length; i++) { + let val = help[mode[type][i]] if (val) found.push(val) } } else if (mode.helperType && help[mode.helperType]) { @@ -166,8 +166,8 @@ export default function(CodeMirror) { } else if (help[mode.name]) { found.push(help[mode.name]) } - for (var i = 0; i < help._global.length; i++) { - var cur = help._global[i] + for (let i = 0; i < help._global.length; i++) { + let cur = help._global[i] if (cur.pred(mode, this) && indexOf(found, cur.val) == -1) found.push(cur.val) } @@ -175,13 +175,13 @@ export default function(CodeMirror) { }, getStateAfter: function(line, precise) { - var doc = this.doc + let doc = this.doc line = clipLine(doc, line == null ? doc.first + doc.size - 1: line) return getStateBefore(this, line + 1, precise) }, cursorCoords: function(start, mode) { - var pos, range = this.doc.sel.primary() + let pos, range = this.doc.sel.primary() if (start == null) pos = range.head else if (typeof start == "object") pos = clipPos(this.doc, start) else pos = start ? range.from() : range.to() @@ -202,9 +202,9 @@ export default function(CodeMirror) { return lineAtHeight(this.doc, height + this.display.viewOffset) }, heightAtLine: function(line, mode) { - var end = false, lineObj + let end = false, lineObj if (typeof line == "number") { - var last = this.doc.first + this.doc.size - 1 + let last = this.doc.first + this.doc.size - 1 if (line < this.doc.first) line = this.doc.first else if (line > last) { line = last; end = true } lineObj = getLine(this.doc, line) @@ -220,7 +220,7 @@ export default function(CodeMirror) { setGutterMarker: methodOp(function(line, gutterID, value) { return changeLine(this.doc, line, "gutter", function(line) { - var markers = line.gutterMarkers || (line.gutterMarkers = {}) + let markers = line.gutterMarkers || (line.gutterMarkers = {}) markers[gutterID] = value if (!value && isEmpty(markers)) line.gutterMarkers = null return true @@ -228,7 +228,7 @@ export default function(CodeMirror) { }), clearGutter: methodOp(function(gutterID) { - var cm = this, doc = cm.doc, i = doc.first + let cm = this, doc = cm.doc, i = doc.first doc.iter(function(line) { if (line.gutterMarkers && line.gutterMarkers[gutterID]) { line.gutterMarkers[gutterID] = null @@ -240,13 +240,14 @@ export default function(CodeMirror) { }), lineInfo: function(line) { + let n if (typeof line == "number") { if (!isLine(this.doc, line)) return null - var n = line + n = line line = getLine(this.doc, line) if (!line) return null } else { - var n = lineNo(line) + n = lineNo(line) if (n == null) return null } return {line: n, handle: line, text: line.text, gutterMarkers: line.gutterMarkers, @@ -257,9 +258,9 @@ export default function(CodeMirror) { getViewport: function() { return {from: this.display.viewFrom, to: this.display.viewTo}}, addWidget: function(pos, node, scroll, vert, horiz) { - var display = this.display + let display = this.display pos = cursorCoords(this, clipPos(this.doc, pos)) - var top = pos.bottom, left = pos.left + let top = pos.bottom, left = pos.left node.style.position = "absolute" node.setAttribute("cm-ignore-events", "true") this.display.input.setUneditable(node) @@ -267,7 +268,7 @@ export default function(CodeMirror) { if (vert == "over") { top = pos.top } else if (vert == "above" || vert == "near") { - var vspace = Math.max(display.wrapper.clientHeight, this.doc.height), + let vspace = Math.max(display.wrapper.clientHeight, this.doc.height), hspace = Math.max(display.sizer.clientWidth, display.lineSpace.clientWidth) // Default to positioning above (if specified and possible); otherwise default to positioning below if ((vert == 'above' || pos.bottom + node.offsetHeight > vspace) && pos.top > node.offsetHeight) @@ -303,9 +304,10 @@ export default function(CodeMirror) { triggerElectric: methodOp(function(text) { triggerElectric(this, text) }), findPosH: function(from, amount, unit, visually) { - var dir = 1 + let dir = 1 if (amount < 0) { dir = -1; amount = -amount } - for (var i = 0, cur = clipPos(this.doc, from); i < amount; ++i) { + let cur = clipPos(this.doc, from) + for (let i = 0; i < amount; ++i) { cur = findPosH(this.doc, cur, dir, unit, visually) if (cur.hitSide) break } @@ -313,7 +315,7 @@ export default function(CodeMirror) { }, moveH: methodOp(function(dir, unit) { - var cm = this + let cm = this cm.extendSelectionsBy(function(range) { if (cm.display.shift || cm.doc.extend || range.empty()) return findPosH(cm.doc, range.head, dir, unit, cm.options.rtlMoveVisually) @@ -323,21 +325,22 @@ export default function(CodeMirror) { }), deleteH: methodOp(function(dir, unit) { - var sel = this.doc.sel, doc = this.doc + let sel = this.doc.sel, doc = this.doc if (sel.somethingSelected()) doc.replaceSelection("", null, "+delete") else deleteNearSelection(this, function(range) { - var other = findPosH(doc, range.head, dir, unit, false) + let other = findPosH(doc, range.head, dir, unit, false) return dir < 0 ? {from: other, to: range.head} : {from: range.head, to: other} }) }), findPosV: function(from, amount, unit, goalColumn) { - var dir = 1, x = goalColumn + let 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") + let cur = clipPos(this.doc, from) + for (let i = 0; i < amount; ++i) { + let coords = cursorCoords(this, cur, "div") if (x == null) x = coords.left else coords.left = x cur = findPosV(this, coords, dir, unit) @@ -347,32 +350,32 @@ export default function(CodeMirror) { }, moveV: methodOp(function(dir, unit) { - var cm = this, doc = this.doc, goals = [] - var collapse = !cm.display.shift && !doc.extend && doc.sel.somethingSelected() + let cm = this, doc = this.doc, goals = [] + let collapse = !cm.display.shift && !doc.extend && doc.sel.somethingSelected() doc.extendSelectionsBy(function(range) { if (collapse) return dir < 0 ? range.from() : range.to() - var headPos = cursorCoords(cm, range.head, "div") + let headPos = cursorCoords(cm, range.head, "div") if (range.goalColumn != null) headPos.left = range.goalColumn goals.push(headPos.left) - var pos = findPosV(cm, headPos, dir, unit) + let pos = findPosV(cm, headPos, dir, unit) if (unit == "page" && range == doc.sel.primary()) addToScrollPos(cm, null, charCoords(cm, pos, "div").top - headPos.top) return pos }, sel_move) - if (goals.length) for (var i = 0; i < doc.sel.ranges.length; i++) + if (goals.length) for (let i = 0; i < doc.sel.ranges.length; i++) 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 + let doc = this.doc, line = getLine(doc, pos.line).text + let start = pos.ch, end = pos.ch if (line) { - var helper = this.getHelper(pos, "wordChars") + let 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) + let startChar = line.charAt(start) + let 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)} @@ -400,7 +403,7 @@ export default function(CodeMirror) { if (y != null) this.curOp.scrollTop = y }), getScrollInfo: function() { - var scroller = this.display.scroller + let scroller = this.display.scroller return {left: scroller.scrollLeft, top: scroller.scrollTop, height: scroller.scrollHeight - scrollGap(this) - this.display.barHeight, width: scroller.scrollWidth - scrollGap(this) - this.display.barWidth, @@ -423,7 +426,7 @@ export default function(CodeMirror) { resolveScrollToPos(this) this.curOp.scrollToPos = range } else { - var sPos = calculateScrollPos(this, Math.min(range.from.left, range.to.left), + let sPos = calculateScrollPos(this, Math.min(range.from.left, range.to.left), Math.min(range.from.top, range.to.top) - range.margin, Math.max(range.from.right, range.to.right), Math.max(range.from.bottom, range.to.bottom) + range.margin) @@ -432,16 +435,16 @@ export default function(CodeMirror) { }), setSize: methodOp(function(width, height) { - var cm = this + let cm = this function interpret(val) { return typeof val == "number" || /^\d+$/.test(String(val)) ? val + "px" : val } if (width != null) cm.display.wrapper.style.width = interpret(width) if (height != null) cm.display.wrapper.style.height = interpret(height) if (cm.options.lineWrapping) clearLineMeasurementCache(this) - var lineNo = cm.display.viewFrom + let lineNo = cm.display.viewFrom cm.doc.iter(lineNo, cm.display.viewTo, function(line) { - if (line.widgets) for (var i = 0; i < line.widgets.length; i++) + if (line.widgets) for (let i = 0; i < line.widgets.length; i++) if (line.widgets[i].noHScroll) { regLineChange(cm, lineNo, "widget"); break } ++lineNo }) @@ -452,7 +455,7 @@ export default function(CodeMirror) { operation: function(f){return runInOp(this, f)}, refresh: methodOp(function() { - var oldHeight = this.display.cachedTextHeight + let oldHeight = this.display.cachedTextHeight regChange(this) this.curOp.forceUpdate = true clearCaches(this) @@ -464,7 +467,7 @@ export default function(CodeMirror) { }), swapDoc: methodOp(function(doc) { - var old = this.doc + let old = this.doc old.cm = null attachDoc(this, doc) clearCaches(this) @@ -502,16 +505,16 @@ export default function(CodeMirror) { // position. The resulting position will have a hitSide=true // property if it reached the end of the document. function findPosH(doc, pos, dir, unit, visually) { - var line = pos.line, ch = pos.ch, origDir = dir - var lineObj = getLine(doc, line) + let line = pos.line, ch = pos.ch, origDir = dir + let lineObj = getLine(doc, line) function findNextLine() { - var l = line + dir + let l = line + dir if (l < doc.first || l >= doc.first + doc.size) return false line = l return lineObj = getLine(doc, l) } function moveOnce(boundToLine) { - var next = (visually ? moveVisually : moveLogically)(lineObj, ch, dir, true) + let next = (visually ? moveVisually : moveLogically)(lineObj, ch, dir, true) if (next == null) { if (!boundToLine && findNextLine()) { if (visually) ch = (dir < 0 ? lineRight : lineLeft)(lineObj) @@ -526,12 +529,12 @@ function findPosH(doc, pos, dir, unit, visually) { } else if (unit == "column") { moveOnce(true) } else if (unit == "word" || unit == "group") { - var sawType = null, group = unit == "group" - var helper = doc.cm && doc.cm.getHelper(pos, "wordChars") - for (var first = true;; first = false) { + let sawType = null, group = unit == "group" + let helper = doc.cm && doc.cm.getHelper(pos, "wordChars") + for (let first = true;; first = false) { if (dir < 0 && !moveOnce(!first)) break - var cur = lineObj.text.charAt(ch) || "\n" - var type = isWordChar(cur, helper) ? "w" + let cur = lineObj.text.charAt(ch) || "\n" + let type = isWordChar(cur, helper) ? "w" : group && cur == "\n" ? "n" : !group || /\s/.test(cur) ? null : "p" @@ -545,7 +548,7 @@ function findPosH(doc, pos, dir, unit, visually) { if (dir > 0 && !moveOnce(!first)) break } } - var result = skipAtomic(doc, Pos(line, ch), pos, origDir, true) + let result = skipAtomic(doc, Pos(line, ch), pos, origDir, true) if (!cmp(pos, result)) result.hitSide = true return result } @@ -554,17 +557,18 @@ function findPosH(doc, pos, dir, unit, visually) { // "page" or "line". The resulting position will have a hitSide=true // property if it reached the end of the document. function findPosV(cm, pos, dir, unit) { - var doc = cm.doc, x = pos.left, y + let doc = cm.doc, x = pos.left, y if (unit == "page") { - var pageSize = Math.min(cm.display.wrapper.clientHeight, window.innerHeight || document.documentElement.clientHeight) - var moveAmount = Math.max(pageSize - .5 * textHeight(cm.display), 3) + let pageSize = Math.min(cm.display.wrapper.clientHeight, window.innerHeight || document.documentElement.clientHeight) + let moveAmount = Math.max(pageSize - .5 * textHeight(cm.display), 3) y = (dir > 0 ? pos.bottom : pos.top) + dir * moveAmount } else if (unit == "line") { y = dir > 0 ? pos.bottom + 3 : pos.top - 3 } + let target for (;;) { - var target = coordsChar(cm, x, y) + target = coordsChar(cm, x, y) if (!target.outside) break if (dir < 0 ? y <= 0 : y >= doc.height) { target.hitSide = true; break } y += dir * 5 diff --git a/src/edit/mouse_events.js b/src/edit/mouse_events.js index 408cc497..5fe10ea3 100644 --- a/src/edit/mouse_events.js +++ b/src/edit/mouse_events.js @@ -19,7 +19,7 @@ import { bind, countColumn, findColumn, sel_mouse } from "../util/misc" // middle-click-paste. Or it might be a click on something we should // not interfere with, such as a scrollbar or widget. export function onMouseDown(e) { - var cm = this, display = cm.display + let cm = this, display = cm.display if (signalDOMEvent(cm, e) || display.activeTouch && display.input.supportsTouch()) return display.shift = e.shiftKey @@ -33,7 +33,7 @@ export function onMouseDown(e) { return } if (clickInGutter(cm, e)) return - var start = posFromMouse(cm, e) + let start = posFromMouse(cm, e) window.focus() switch (e_button(e)) { @@ -59,12 +59,12 @@ export function onMouseDown(e) { } } -var lastClick, lastDoubleClick +let lastClick, lastDoubleClick function leftButtonDown(cm, e, start) { if (ie) setTimeout(bind(ensureFocus, cm), 0) else cm.curOp.focus = activeElt() - var now = +new Date, type + let now = +new Date, type if (lastDoubleClick && lastDoubleClick.time > now - 400 && cmp(lastDoubleClick.pos, start) == 0) { type = "triple" } else if (lastClick && lastClick.time > now - 400 && cmp(lastClick.pos, start) == 0) { @@ -75,7 +75,7 @@ function leftButtonDown(cm, e, start) { lastClick = {time: now, pos: start} } - var sel = cm.doc.sel, modifier = mac ? e.metaKey : e.ctrlKey, contained + let sel = cm.doc.sel, modifier = mac ? e.metaKey : e.ctrlKey, contained if (cm.options.dragDrop && dragAndDrop && !cm.isReadOnly() && type == "single" && (contained = sel.contains(start)) > -1 && (cmp((contained = sel.ranges[contained]).from(), start) < 0 || start.xRel > 0) && @@ -88,8 +88,8 @@ function leftButtonDown(cm, e, start) { // 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, modifier) { - var display = cm.display, startTime = +new Date - var dragEnd = operation(cm, function(e2) { + let display = cm.display, startTime = +new Date + let dragEnd = operation(cm, function(e2) { if (webkit) display.scroller.draggable = false cm.state.draggingText = false off(document, "mouseup", dragEnd) @@ -117,10 +117,10 @@ function leftButtonStartDrag(cm, e, start, modifier) { // Normal selection, as opposed to text dragging. function leftButtonSelect(cm, e, start, type, addNew) { - var display = cm.display, doc = cm.doc + let display = cm.display, doc = cm.doc e_preventDefault(e) - var ourRange, ourIndex, startSel = doc.sel, ranges = startSel.ranges + let ourRange, ourIndex, startSel = doc.sel, ranges = startSel.ranges if (addNew && !e.shiftKey) { ourIndex = doc.sel.contains(start) if (ourIndex > -1) @@ -138,13 +138,13 @@ function leftButtonSelect(cm, e, start, type, addNew) { start = posFromMouse(cm, e, true, true) ourIndex = -1 } else if (type == "double") { - var word = cm.findWordAt(start) + let word = cm.findWordAt(start) if (cm.display.shift || doc.extend) ourRange = extendRange(doc, ourRange, word.anchor, word.head) else ourRange = word } else if (type == "triple") { - var line = new Range(Pos(start.line, 0), clipPos(doc, Pos(start.line + 1, 0))) + let line = new Range(Pos(start.line, 0), clipPos(doc, Pos(start.line + 1, 0))) if (cm.display.shift || doc.extend) ourRange = extendRange(doc, ourRange, line.anchor, line.head) else @@ -169,19 +169,19 @@ function leftButtonSelect(cm, e, start, type, addNew) { replaceOneSelection(doc, ourIndex, ourRange, sel_mouse) } - var lastPos = start + let lastPos = start function extendTo(pos) { if (cmp(lastPos, pos) == 0) return lastPos = pos if (type == "rect") { - var ranges = [], tabSize = cm.options.tabSize - var startCol = countColumn(getLine(doc, start.line).text, start.ch, tabSize) - var posCol = countColumn(getLine(doc, pos.line).text, pos.ch, tabSize) - var left = Math.min(startCol, posCol), right = Math.max(startCol, posCol) - for (var line = Math.min(start.line, pos.line), end = Math.min(cm.lastLine(), Math.max(start.line, pos.line)); + let ranges = [], tabSize = cm.options.tabSize + let startCol = countColumn(getLine(doc, start.line).text, start.ch, tabSize) + let posCol = countColumn(getLine(doc, pos.line).text, pos.ch, tabSize) + let left = Math.min(startCol, posCol), right = Math.max(startCol, posCol) + for (let line = Math.min(start.line, pos.line), end = Math.min(cm.lastLine(), Math.max(start.line, pos.line)); line <= end; line++) { - var text = getLine(doc, line).text, leftPos = findColumn(text, left, tabSize) + let text = getLine(doc, line).text, leftPos = findColumn(text, left, tabSize) if (left == right) ranges.push(new Range(Pos(line, leftPos), Pos(line, leftPos))) else if (text.length > leftPos) @@ -192,13 +192,14 @@ function leftButtonSelect(cm, e, start, type, addNew) { {origin: "*mouse", scroll: false}) cm.scrollIntoView(pos) } else { - var oldRange = ourRange - var anchor = oldRange.anchor, head = pos + let oldRange = ourRange + let anchor = oldRange.anchor, head = pos if (type != "single") { + let range if (type == "double") - var range = cm.findWordAt(pos) + range = cm.findWordAt(pos) else - var range = new Range(Pos(pos.line, 0), clipPos(doc, Pos(pos.line + 1, 0))) + range = new Range(Pos(pos.line, 0), clipPos(doc, Pos(pos.line + 1, 0))) if (cmp(range.anchor, anchor) > 0) { head = range.head anchor = minPos(oldRange.from(), range.anchor) @@ -207,31 +208,31 @@ function leftButtonSelect(cm, e, start, type, addNew) { anchor = maxPos(oldRange.to(), range.head) } } - var ranges = startSel.ranges.slice(0) + let ranges = startSel.ranges.slice(0) ranges[ourIndex] = new Range(clipPos(doc, anchor), head) setSelection(doc, normalizeSelection(ranges, ourIndex), sel_mouse) } } - var editorSize = display.wrapper.getBoundingClientRect() + let 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 + let counter = 0 function extend(e) { - var curCount = ++counter - var cur = posFromMouse(cm, e, true, type == "rect") + let curCount = ++counter + let cur = posFromMouse(cm, e, true, type == "rect") if (!cur) return if (cmp(cur, lastPos) != 0) { cm.curOp.focus = activeElt() extendTo(cur) - var visible = visibleLines(display, doc) + let visible = visibleLines(display, doc) if (cur.line >= visible.to || cur.line < visible.from) setTimeout(operation(cm, function(){if (counter == curCount) extend(e)}), 150) } else { - var outside = e.clientY < editorSize.top ? -20 : e.clientY > editorSize.bottom ? 20 : 0 + let 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 @@ -250,11 +251,11 @@ function leftButtonSelect(cm, e, start, type, addNew) { doc.history.lastSelOrigin = null } - var move = operation(cm, function(e) { + let move = operation(cm, function(e) { if (!e_button(e)) done(e) else extend(e) }) - var up = operation(cm, done) + let up = operation(cm, done) cm.state.selectingText = up on(document, "mousemove", move) on(document, "mouseup", up) @@ -264,22 +265,23 @@ function leftButtonSelect(cm, e, start, type, addNew) { // Determines whether an event happened in the gutter, and fires the // handlers for the corresponding event. function gutterEvent(cm, e, type, prevent) { - try { var mX = e.clientX, mY = e.clientY } + let mX, mY + try { mX = e.clientX; mY = e.clientY } catch(e) { return false } if (mX >= Math.floor(cm.display.gutters.getBoundingClientRect().right)) return false if (prevent) e_preventDefault(e) - var display = cm.display - var lineBox = display.lineDiv.getBoundingClientRect() + let display = cm.display + let lineBox = display.lineDiv.getBoundingClientRect() if (mY > lineBox.bottom || !hasHandler(cm, type)) return e_defaultPrevented(e) mY -= lineBox.top - display.viewOffset - for (var i = 0; i < cm.options.gutters.length; ++i) { - var g = display.gutters.childNodes[i] + for (let i = 0; i < cm.options.gutters.length; ++i) { + let g = display.gutters.childNodes[i] if (g && g.getBoundingClientRect().right >= mX) { - var line = lineAtHeight(cm.doc, mY) - var gutter = cm.options.gutters[i] + let line = lineAtHeight(cm.doc, mY) + let gutter = cm.options.gutters[i] signal(cm, type, cm, line, gutter, e) return e_defaultPrevented(e) } diff --git a/src/edit/options.js b/src/edit/options.js index cf0d4d81..a2ca1c68 100644 --- a/src/edit/options.js +++ b/src/edit/options.js @@ -17,13 +17,13 @@ import { off, on } from "../util/event" import { themeChanged } from "./utils" -export var Init = {toString: function(){return "CodeMirror.Init"}} +export let Init = {toString: function(){return "CodeMirror.Init"}} -export var defaults = {} -export var optionHandlers = {} +export let defaults = {} +export let optionHandlers = {} export function defineOptions(CodeMirror) { - var optionHandlers = CodeMirror.optionHandlers + let optionHandlers = CodeMirror.optionHandlers function option(name, deflt, handle, notOnInit) { CodeMirror.defaults[name] = deflt @@ -57,17 +57,17 @@ export function defineOptions(CodeMirror) { option("lineSeparator", null, function(cm, val) { cm.doc.lineSep = val if (!val) return - var newBreaks = [], lineNo = cm.doc.first + let newBreaks = [], lineNo = cm.doc.first cm.doc.iter(function(line) { - for (var pos = 0;;) { - var found = line.text.indexOf(val, pos) + for (let pos = 0;;) { + let found = line.text.indexOf(val, pos) if (found == -1) break pos = found + val.length newBreaks.push(Pos(lineNo, found)) } lineNo++ }) - for (var i = newBreaks.length - 1; i >= 0; i--) + for (let i = newBreaks.length - 1; i >= 0; i--) replaceRange(cm.doc, val, newBreaks[i], Pos(newBreaks[i].line, newBreaks[i].ch + val.length)) }) option("specialChars", /[\u0000-\u001f\u007f\u00ad\u200b-\u200f\u2028\u2029\ufeff]/g, function(cm, val, old) { @@ -90,8 +90,8 @@ export function defineOptions(CodeMirror) { guttersChanged(cm) }, true) option("keyMap", "default", function(cm, val, old) { - var next = getKeyMap(val) - var prev = old != Init && getKeyMap(old) + let next = getKeyMap(val) + let prev = old != Init && getKeyMap(old) if (prev && prev.detach) prev.detach(cm, next) if (next.attach) next.attach(cm, prev || null) }) @@ -168,10 +168,10 @@ function guttersChanged(cm) { } function dragDropChanged(cm, value, old) { - var wasOn = old && old != Init + let wasOn = old && old != Init if (!value != !wasOn) { - var funcs = cm.display.dragFunctions - var toggle = value ? on : off + let funcs = cm.display.dragFunctions + let toggle = value ? on : off toggle(cm.display.scroller, "dragstart", funcs.start) toggle(cm.display.scroller, "dragenter", funcs.enter) toggle(cm.display.scroller, "dragover", funcs.over) diff --git a/src/input/ContentEditableInput.js b/src/input/ContentEditableInput.js index 51597d19..80e91f84 100644 --- a/src/input/ContentEditableInput.js +++ b/src/input/ContentEditableInput.js @@ -25,8 +25,8 @@ export default function ContentEditableInput(cm) { ContentEditableInput.prototype = copyObj({ init: function(display) { - var input = this, cm = input.cm - var div = input.div = display.lineDiv + let input = this, cm = input.cm + let div = input.div = display.lineDiv disableBrowserMagic(div, cm.options.spellcheck) on(div, "paste", function(e) { @@ -38,12 +38,12 @@ ContentEditableInput.prototype = copyObj({ }) on(div, "compositionstart", function(e) { - var data = e.data + let data = e.data input.composing = {sel: cm.doc.sel, data: data, startData: data} if (!data) return - var prim = cm.doc.sel.primary() - var line = cm.getLine(prim.head.line) - var found = line.indexOf(data, Math.max(0, prim.head.ch - data.length)) + let prim = cm.doc.sel.primary() + let line = cm.getLine(prim.head.line) + let found = line.indexOf(data, Math.max(0, prim.head.ch - data.length)) if (found > -1 && found <= prim.head.ch) input.composing.sel = simpleSelection(Pos(prim.head.line, found), Pos(prim.head.line, found + data.length)) @@ -52,7 +52,7 @@ ContentEditableInput.prototype = copyObj({ input.composing.data = e.data }) on(div, "compositionend", function(e) { - var ours = input.composing + let ours = input.composing if (!ours) return if (e.data != ours.startData && !/\u200b/.test(e.data)) ours.data = e.data @@ -85,7 +85,7 @@ ContentEditableInput.prototype = copyObj({ } else if (!cm.options.lineWiseCopyCut) { return } else { - var ranges = copyableRanges(cm) + let ranges = copyableRanges(cm) setLastCopied({lineWise: true, text: ranges.text}) if (e.type == "cut") { cm.operation(function() { @@ -96,7 +96,7 @@ ContentEditableInput.prototype = copyObj({ } if (e.clipboardData) { e.clipboardData.clearData() - var content = lastCopied.text.join("\n") + let content = lastCopied.text.join("\n") // iOS exposes the clipboard API, but seems to discard content inserted into it e.clipboardData.setData("Text", content) if (e.clipboardData.getData("Text") == content) { @@ -105,10 +105,10 @@ ContentEditableInput.prototype = copyObj({ } } // Old-fashioned briefly-focus-a-textarea hack - var kludge = hiddenTextarea(), te = kludge.firstChild + let kludge = hiddenTextarea(), te = kludge.firstChild cm.display.lineSpace.insertBefore(kludge, cm.display.lineSpace.firstChild) te.value = lastCopied.text.join("\n") - var hadFocus = document.activeElement + let hadFocus = document.activeElement selectInput(te) setTimeout(function() { cm.display.lineSpace.removeChild(kludge) @@ -121,7 +121,7 @@ ContentEditableInput.prototype = copyObj({ }, prepareSelection: function() { - var result = prepareSelection(this.cm, false) + let result = prepareSelection(this.cm, false) result.focus = this.cm.state.focused return result }, @@ -133,29 +133,30 @@ ContentEditableInput.prototype = copyObj({ }, showPrimarySelection: function() { - var sel = window.getSelection(), prim = this.cm.doc.sel.primary() - var curAnchor = domToPos(this.cm, sel.anchorNode, sel.anchorOffset) - var curFocus = domToPos(this.cm, sel.focusNode, sel.focusOffset) + let sel = window.getSelection(), prim = this.cm.doc.sel.primary() + let curAnchor = domToPos(this.cm, sel.anchorNode, sel.anchorOffset) + let curFocus = domToPos(this.cm, sel.focusNode, sel.focusOffset) if (curAnchor && !curAnchor.bad && curFocus && !curFocus.bad && cmp(minPos(curAnchor, curFocus), prim.from()) == 0 && cmp(maxPos(curAnchor, curFocus), prim.to()) == 0) return - var start = posToDOM(this.cm, prim.from()) - var end = posToDOM(this.cm, prim.to()) + let start = posToDOM(this.cm, prim.from()) + let end = posToDOM(this.cm, prim.to()) if (!start && !end) return - var view = this.cm.display.view - var old = sel.rangeCount && sel.getRangeAt(0) + let view = this.cm.display.view + let old = sel.rangeCount && sel.getRangeAt(0) if (!start) { start = {node: view[0].measure.map[2], offset: 0} } else if (!end) { // FIXME dangerously hacky - var measure = view[view.length - 1].measure - var map = measure.maps ? measure.maps[measure.maps.length - 1] : measure.map + let measure = view[view.length - 1].measure + let map = measure.maps ? measure.maps[measure.maps.length - 1] : measure.map end = {node: map[map.length - 1], offset: map[map.length - 2] - map[map.length - 3]} } - try { var rng = range(start.node, start.offset, end.offset, end.node) } + let rng + try { rng = range(start.node, start.offset, end.offset, end.node) } catch(e) {} // Our model of the DOM might be outdated, in which case the range we try to set can be impossible if (rng) { if (!gecko && this.cm.state.focused) { @@ -172,7 +173,7 @@ ContentEditableInput.prototype = copyObj({ }, startGracePeriod: function() { - var input = this + let input = this clearTimeout(this.gracePeriod) this.gracePeriod = setTimeout(function() { input.gracePeriod = false @@ -187,15 +188,15 @@ ContentEditableInput.prototype = copyObj({ }, rememberSelection: function() { - var sel = window.getSelection() + let sel = window.getSelection() this.lastAnchorNode = sel.anchorNode; this.lastAnchorOffset = sel.anchorOffset this.lastFocusNode = sel.focusNode; this.lastFocusOffset = sel.focusOffset }, selectionInEditor: function() { - var sel = window.getSelection() + let sel = window.getSelection() if (!sel.rangeCount) return false - var node = sel.getRangeAt(0).commonAncestorContainer + let node = sel.getRangeAt(0).commonAncestorContainer return contains(this.div, node) }, @@ -208,7 +209,7 @@ ContentEditableInput.prototype = copyObj({ supportsTouch: function() { return true }, receivedFocus: function() { - var input = this + let input = this if (this.selectionInEditor()) this.pollSelection() else @@ -224,17 +225,17 @@ ContentEditableInput.prototype = copyObj({ }, selectionChanged: function() { - var sel = window.getSelection() + let sel = window.getSelection() return sel.anchorNode != this.lastAnchorNode || sel.anchorOffset != this.lastAnchorOffset || sel.focusNode != this.lastFocusNode || sel.focusOffset != this.lastFocusOffset }, pollSelection: function() { if (!this.composing && !this.gracePeriod && this.selectionChanged()) { - var sel = window.getSelection(), cm = this.cm + let sel = window.getSelection(), cm = this.cm this.rememberSelection() - var anchor = domToPos(cm, sel.anchorNode, sel.anchorOffset) - var head = domToPos(cm, sel.focusNode, sel.focusOffset) + let anchor = domToPos(cm, sel.anchorNode, sel.anchorOffset) + let head = domToPos(cm, sel.focusNode, sel.focusOffset) if (anchor && head) runInOp(cm, function() { setSelection(cm.doc, simpleSelection(anchor, head), sel_dontScroll) if (anchor.bad || head.bad) cm.curOp.selectionChanged = true @@ -243,41 +244,42 @@ ContentEditableInput.prototype = copyObj({ }, pollContent: function() { - var cm = this.cm, display = cm.display, sel = cm.doc.sel.primary() - var from = sel.from(), to = sel.to() + let cm = this.cm, display = cm.display, sel = cm.doc.sel.primary() + let from = sel.from(), to = sel.to() if (from.line < display.viewFrom || to.line > display.viewTo - 1) return false - var fromIndex + let fromIndex, fromLine, fromNode if (from.line == display.viewFrom || (fromIndex = findViewIndex(cm, from.line)) == 0) { - var fromLine = lineNo(display.view[0].line) - var fromNode = display.view[0].node + fromLine = lineNo(display.view[0].line) + fromNode = display.view[0].node } else { - var fromLine = lineNo(display.view[fromIndex].line) - var fromNode = display.view[fromIndex - 1].node.nextSibling + fromLine = lineNo(display.view[fromIndex].line) + fromNode = display.view[fromIndex - 1].node.nextSibling } - var toIndex = findViewIndex(cm, to.line) + let toIndex = findViewIndex(cm, to.line) + let toLine, toNode if (toIndex == display.view.length - 1) { - var toLine = display.viewTo - 1 - var toNode = display.lineDiv.lastChild + toLine = display.viewTo - 1 + toNode = display.lineDiv.lastChild } else { - var toLine = lineNo(display.view[toIndex + 1].line) - 1 - var toNode = display.view[toIndex + 1].node.previousSibling + toLine = lineNo(display.view[toIndex + 1].line) - 1 + toNode = display.view[toIndex + 1].node.previousSibling } - var newText = cm.doc.splitLines(domTextBetween(cm, fromNode, toNode, fromLine, toLine)) - var oldText = getBetween(cm.doc, Pos(fromLine, 0), Pos(toLine, getLine(cm.doc, toLine).text.length)) + let newText = cm.doc.splitLines(domTextBetween(cm, fromNode, toNode, fromLine, toLine)) + let oldText = getBetween(cm.doc, Pos(fromLine, 0), Pos(toLine, getLine(cm.doc, toLine).text.length)) while (newText.length > 1 && oldText.length > 1) { if (lst(newText) == lst(oldText)) { newText.pop(); oldText.pop(); toLine-- } else if (newText[0] == oldText[0]) { newText.shift(); oldText.shift(); fromLine++ } else break } - var cutFront = 0, cutEnd = 0 - var newTop = newText[0], oldTop = oldText[0], maxCutFront = Math.min(newTop.length, oldTop.length) + let cutFront = 0, cutEnd = 0 + let newTop = newText[0], oldTop = oldText[0], maxCutFront = Math.min(newTop.length, oldTop.length) while (cutFront < maxCutFront && newTop.charCodeAt(cutFront) == oldTop.charCodeAt(cutFront)) ++cutFront - var newBot = lst(newText), oldBot = lst(oldText) - var maxCutEnd = Math.min(newBot.length - (newText.length == 1 ? cutFront : 0), + let newBot = lst(newText), oldBot = lst(oldText) + let maxCutEnd = Math.min(newBot.length - (newText.length == 1 ? cutFront : 0), oldBot.length - (oldText.length == 1 ? cutFront : 0)) while (cutEnd < maxCutEnd && newBot.charCodeAt(newBot.length - cutEnd - 1) == oldBot.charCodeAt(oldBot.length - cutEnd - 1)) @@ -286,8 +288,8 @@ ContentEditableInput.prototype = copyObj({ newText[newText.length - 1] = newBot.slice(0, newBot.length - cutEnd) newText[0] = newText[0].slice(cutFront) - var chFrom = Pos(fromLine, cutFront) - var chTo = Pos(toLine, oldText.length ? lst(oldText).length - cutEnd : 0) + let chFrom = Pos(fromLine, cutFront) + let chTo = Pos(toLine, oldText.length ? lst(oldText).length - cutEnd : 0) if (newText.length > 1 || newText[0] || cmp(chFrom, chTo)) { replaceRange(cm.doc, newText, chFrom, chTo, "+input") return true @@ -335,17 +337,17 @@ ContentEditableInput.prototype = copyObj({ }, ContentEditableInput.prototype) function posToDOM(cm, pos) { - var view = findViewForLine(cm, pos.line) + let view = findViewForLine(cm, pos.line) if (!view || view.hidden) return null - var line = getLine(cm.doc, pos.line) - var info = mapFromLineView(view, line, pos.line) + let line = getLine(cm.doc, pos.line) + let info = mapFromLineView(view, line, pos.line) - var order = getOrder(line), side = "left" + let order = getOrder(line), side = "left" if (order) { - var partPos = getBidiPartAt(order, pos.ch) + let partPos = getBidiPartAt(order, pos.ch) side = partPos % 2 ? "right" : "left" } - var result = nodeAndOffsetInLineMap(info.map, pos.ch, side) + let result = nodeAndOffsetInLineMap(info.map, pos.ch, side) result.offset = result.collapse == "right" ? result.end : result.start return result } @@ -353,30 +355,30 @@ function posToDOM(cm, pos) { function badPos(pos, bad) { if (bad) pos.bad = true; return pos } function domTextBetween(cm, from, to, fromLine, toLine) { - var text = "", closing = false, lineSep = cm.doc.lineSeparator() + let text = "", closing = false, lineSep = cm.doc.lineSeparator() function recognizeMarker(id) { return function(marker) { return marker.id == id } } function walk(node) { if (node.nodeType == 1) { - var cmText = node.getAttribute("cm-text") + let cmText = node.getAttribute("cm-text") if (cmText != null) { if (cmText == "") cmText = node.textContent.replace(/\u200b/g, "") text += cmText return } - var markerID = node.getAttribute("cm-marker"), range + let markerID = node.getAttribute("cm-marker"), range if (markerID) { - var found = cm.findMarks(Pos(fromLine, 0), Pos(toLine + 1, 0), recognizeMarker(+markerID)) + let found = cm.findMarks(Pos(fromLine, 0), Pos(toLine + 1, 0), recognizeMarker(+markerID)) if (found.length && (range = found[0].find())) text += getBetween(cm.doc, range.from, range.to).join(lineSep) return } if (node.getAttribute("contenteditable") == "false") return - for (var i = 0; i < node.childNodes.length; i++) + for (let i = 0; i < node.childNodes.length; i++) walk(node.childNodes[i]) if (/^(pre|div|p)$/i.test(node.nodeName)) closing = true } else if (node.nodeType == 3) { - var val = node.nodeValue + let val = node.nodeValue if (!val) return if (closing) { text += lineSep @@ -394,7 +396,7 @@ function domTextBetween(cm, from, to, fromLine, toLine) { } function domToPos(cm, node, offset) { - var lineNode + let lineNode if (node == cm.display.lineDiv) { lineNode = cm.display.lineDiv.childNodes[offset] if (!lineNode) return badPos(cm.clipPos(Pos(cm.display.viewTo - 1)), true) @@ -405,60 +407,60 @@ function domToPos(cm, node, offset) { if (lineNode.parentNode && lineNode.parentNode == cm.display.lineDiv) break } } - for (var i = 0; i < cm.display.view.length; i++) { - var lineView = cm.display.view[i] + for (let i = 0; i < cm.display.view.length; i++) { + let lineView = cm.display.view[i] if (lineView.node == lineNode) return locateNodeInLineView(lineView, node, offset) } } function locateNodeInLineView(lineView, node, offset) { - var wrapper = lineView.text.firstChild, bad = false + let wrapper = lineView.text.firstChild, bad = false if (!node || !contains(wrapper, node)) return badPos(Pos(lineNo(lineView.line), 0), true) if (node == wrapper) { bad = true node = wrapper.childNodes[offset] offset = 0 if (!node) { - var line = lineView.rest ? lst(lineView.rest) : lineView.line + let line = lineView.rest ? lst(lineView.rest) : lineView.line return badPos(Pos(lineNo(line), line.text.length), bad) } } - var textNode = node.nodeType == 3 ? node : null, topNode = node + let textNode = node.nodeType == 3 ? node : null, topNode = node if (!textNode && node.childNodes.length == 1 && node.firstChild.nodeType == 3) { textNode = node.firstChild if (offset) offset = textNode.nodeValue.length } while (topNode.parentNode != wrapper) topNode = topNode.parentNode - var measure = lineView.measure, maps = measure.maps + let measure = lineView.measure, maps = measure.maps function find(textNode, topNode, offset) { - for (var i = -1; i < (maps ? maps.length : 0); i++) { - var map = i < 0 ? measure.map : maps[i] - for (var j = 0; j < map.length; j += 3) { - var curNode = map[j + 2] + for (let i = -1; i < (maps ? maps.length : 0); i++) { + let map = i < 0 ? measure.map : maps[i] + for (let j = 0; j < map.length; j += 3) { + let curNode = map[j + 2] if (curNode == textNode || curNode == topNode) { - var line = lineNo(i < 0 ? lineView.line : lineView.rest[i]) - var ch = map[j] + offset + let line = lineNo(i < 0 ? lineView.line : lineView.rest[i]) + let ch = map[j] + offset if (offset < 0 || curNode != textNode) ch = map[j + (offset ? 1 : 0)] return Pos(line, ch) } } } } - var found = find(textNode, topNode, offset) + let found = find(textNode, topNode, offset) if (found) return badPos(found, bad) // FIXME this is all really shaky. might handle the few cases it needs to handle, but likely to cause problems - for (var after = topNode.nextSibling, dist = textNode ? textNode.nodeValue.length - offset : 0; after; after = after.nextSibling) { + for (let after = topNode.nextSibling, dist = textNode ? textNode.nodeValue.length - offset : 0; after; after = after.nextSibling) { found = find(after, after.firstChild, 0) if (found) return badPos(Pos(found.line, found.ch - dist), bad) else dist += after.textContent.length } - for (var before = topNode.previousSibling, dist = offset; before; before = before.previousSibling) { + for (let before = topNode.previousSibling, dist = offset; before; before = before.previousSibling) { found = find(before, before.firstChild, -1) if (found) return badPos(Pos(found.line, found.ch + dist), bad) diff --git a/src/input/TextareaInput.js b/src/input/TextareaInput.js index 4c08f377..e244a2d1 100644 --- a/src/input/TextareaInput.js +++ b/src/input/TextareaInput.js @@ -34,13 +34,13 @@ export default function TextareaInput(cm) { TextareaInput.prototype = copyObj({ init: function(display) { - var input = this, cm = this.cm + let input = this, cm = this.cm // Wraps and hides input textarea - var div = this.wrapper = hiddenTextarea() + let div = this.wrapper = hiddenTextarea() // The semihidden textarea that is focused when the editor is // focused, and receives input. - var te = this.textarea = div.firstChild + let te = this.textarea = div.firstChild display.wrapper.insertBefore(div, display.wrapper.firstChild) // Needed to hide big blue blinking cursor on Mobile Safari (doesn't seem to work in iOS 8 anymore) @@ -71,7 +71,7 @@ TextareaInput.prototype = copyObj({ } else if (!cm.options.lineWiseCopyCut) { return } else { - var ranges = copyableRanges(cm) + let ranges = copyableRanges(cm) setLastCopied({lineWise: true, text: ranges.text}) if (e.type == "cut") { cm.setSelections(ranges.ranges, null, sel_dontScroll) @@ -98,7 +98,7 @@ TextareaInput.prototype = copyObj({ }) on(te, "compositionstart", function() { - var start = cm.getCursor("from") + let start = cm.getCursor("from") if (input.composing) input.composing.range.clear() input.composing = { start: start, @@ -116,13 +116,13 @@ TextareaInput.prototype = copyObj({ prepareSelection: function() { // Redraw the selection and/or cursor - var cm = this.cm, display = cm.display, doc = cm.doc - var result = prepareSelection(cm) + let cm = this.cm, display = cm.display, doc = cm.doc + let result = prepareSelection(cm) // Move the hidden textarea near the cursor to prevent scrolling artifacts if (cm.options.moveInputWithCursor) { - var headPos = cursorCoords(cm, doc.sel.primary().head, "div") - var wrapOff = display.wrapper.getBoundingClientRect(), lineOff = display.lineDiv.getBoundingClientRect() + let headPos = cursorCoords(cm, doc.sel.primary().head, "div") + let wrapOff = display.wrapper.getBoundingClientRect(), lineOff = display.lineDiv.getBoundingClientRect() 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, @@ -133,7 +133,7 @@ TextareaInput.prototype = copyObj({ }, showSelection: function(drawn) { - var cm = this.cm, display = cm.display + let cm = this.cm, display = cm.display removeChildrenAndAdd(display.cursorDiv, drawn.cursors) removeChildrenAndAdd(display.selectionDiv, drawn.selection) if (drawn.teTop != null) { @@ -146,13 +146,13 @@ TextareaInput.prototype = copyObj({ // when not typing and nothing is selected) reset: function(typing) { if (this.contextMenuPending) return - var minimal, selected, cm = this.cm, doc = cm.doc + let minimal, selected, cm = this.cm, doc = cm.doc if (cm.somethingSelected()) { this.prevInput = "" - var range = doc.sel.primary() + let range = doc.sel.primary() minimal = hasCopyEvent && (range.to().line - range.from().line > 100 || (selected = cm.getSelection()).length > 1000) - var content = minimal ? "-" : selected || cm.getSelection() + let content = minimal ? "-" : selected || cm.getSelection() this.textarea.value = content if (cm.state.focused) selectInput(this.textarea) if (ie && ie_version >= 9) this.hasSelection = content @@ -185,7 +185,7 @@ TextareaInput.prototype = copyObj({ // Poll for input changes, using the normal rate of polling. This // runs as long as the editor is focused. slowPoll: function() { - var input = this + let input = this if (input.pollingFast) return input.polling.set(this.cm.options.pollInterval, function() { input.poll() @@ -197,10 +197,10 @@ TextareaInput.prototype = copyObj({ // something in the input textarea, we poll faster, to ensure that // the change appears on the screen quickly. fastPoll: function() { - var missed = false, input = this + let missed = false, input = this input.pollingFast = true function p() { - var changed = input.poll() + let changed = input.poll() if (!changed && !missed) {missed = true; input.polling.set(60, p)} else {input.pollingFast = false; input.slowPoll()} } @@ -214,7 +214,7 @@ TextareaInput.prototype = copyObj({ // seen text (can be empty), which is stored in prevInput (we must // not reset the textarea when typing, because that breaks IME). poll: function() { - var cm = this.cm, input = this.textarea, prevInput = this.prevInput + let cm = this.cm, input = this.textarea, prevInput = this.prevInput // Since this is called a *lot*, try to bail out as cheaply as // possible when it is clear that nothing happened. hasSelection // will be the case when there is a lot of text in the textarea, @@ -224,7 +224,7 @@ TextareaInput.prototype = copyObj({ cm.isReadOnly() || cm.options.disableInput || cm.state.keySeq) return false - var text = input.value + let text = input.value // If nothing changed, bail. if (text == prevInput && !cm.somethingSelected()) return false // Work around nonsensical selection resetting in IE9/10, and @@ -237,15 +237,15 @@ TextareaInput.prototype = copyObj({ } if (cm.doc.sel == cm.display.selForContextMenu) { - var first = text.charCodeAt(0) + let first = text.charCodeAt(0) if (first == 0x200b && !prevInput) prevInput = "\u200b" if (first == 0x21da) { this.reset(); return this.cm.execCommand("undo") } } // Find the part of the input that is actually new - var same = 0, l = Math.min(prevInput.length, text.length) + let same = 0, l = Math.min(prevInput.length, text.length) while (same < l && prevInput.charCodeAt(same) == text.charCodeAt(same)) ++same - var self = this + let self = this runInOp(cm, function() { applyTextInput(cm, text.slice(same), prevInput.length - same, null, self.composing ? "*compose" : null) @@ -273,24 +273,25 @@ TextareaInput.prototype = copyObj({ }, onContextMenu: function(e) { - var input = this, cm = input.cm, display = cm.display, te = input.textarea - var pos = posFromMouse(cm, e), scrollPos = display.scroller.scrollTop + let input = this, cm = input.cm, display = cm.display, te = input.textarea + let pos = posFromMouse(cm, e), scrollPos = display.scroller.scrollTop if (!pos || presto) return // Opera is difficult. // Reset the current text selection only if the click is done outside of the selection // and 'resetSelectionOnContextMenu' option is true. - var reset = cm.options.resetSelectionOnContextMenu + let reset = cm.options.resetSelectionOnContextMenu if (reset && cm.doc.sel.contains(pos) == -1) operation(cm, setSelection)(cm.doc, simpleSelection(pos), sel_dontScroll) - var oldCSS = te.style.cssText, oldWrapperCSS = input.wrapper.style.cssText + let oldCSS = te.style.cssText, oldWrapperCSS = input.wrapper.style.cssText input.wrapper.style.cssText = "position: absolute" - var wrapperBox = input.wrapper.getBoundingClientRect() + let wrapperBox = input.wrapper.getBoundingClientRect() te.style.cssText = "position: absolute; width: 30px; height: 30px; top: " + (e.clientY - wrapperBox.top - 5) + "px; left: " + (e.clientX - wrapperBox.left - 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) + let oldScrollY + if (webkit) oldScrollY = window.scrollY // Work around Chrome issue (#2712) display.input.focus() if (webkit) window.scrollTo(null, oldScrollY) display.input.reset() @@ -305,8 +306,8 @@ TextareaInput.prototype = copyObj({ // it got selected. function prepareSelectAllHack() { if (te.selectionStart != null) { - var selected = cm.somethingSelected() - var extval = "\u200b" + (selected ? te.value : "") + let selected = cm.somethingSelected() + let extval = "\u200b" + (selected ? te.value : "") te.value = "\u21da" // Used to catch context-menu undo te.value = extval input.prevInput = selected ? "" : "\u200b" @@ -325,7 +326,7 @@ TextareaInput.prototype = copyObj({ // Try to detect the user choosing select-all if (te.selectionStart != null) { if (!ie || (ie && ie_version < 9)) prepareSelectAllHack() - var i = 0, poll = function() { + let i = 0, poll = function() { if (display.selForContextMenu == cm.doc.sel && te.selectionStart == 0 && te.selectionEnd > 0 && input.prevInput == "\u200b") operation(cm, selectAll)(cm) @@ -339,7 +340,7 @@ TextareaInput.prototype = copyObj({ if (ie && ie_version >= 9) prepareSelectAllHack() if (captureRightClick) { e_stop(e) - var mouseup = function() { + let mouseup = function() { off(window, "mouseup", mouseup) setTimeout(rehide, 20) } diff --git a/src/input/indent.js b/src/input/indent.js index f0fc78fd..2c1755eb 100644 --- a/src/input/indent.js +++ b/src/input/indent.js @@ -12,7 +12,7 @@ import { countColumn, Pass, spaceStr } from "../util/misc" // lines are not indented, and places where the mode returns Pass // are left alone. export function indentLine(cm, n, how, aggressive) { - var doc = cm.doc, state + let doc = cm.doc, state if (how == null) how = "add" if (how == "smart") { // Fall back to "prev" when the mode doesn't have an indentation @@ -21,10 +21,10 @@ export function indentLine(cm, n, how, aggressive) { else state = getStateBefore(cm, n) } - var tabSize = cm.options.tabSize - var line = getLine(doc, n), curSpace = countColumn(line.text, null, tabSize) + let tabSize = cm.options.tabSize + let line = getLine(doc, n), curSpace = countColumn(line.text, null, tabSize) if (line.stateAfter) line.stateAfter = null - var curSpaceString = line.text.match(/^\s*/)[0], indentation + let curSpaceString = line.text.match(/^\s*/)[0], indentation if (!aggressive && !/\S/.test(line.text)) { indentation = 0 how = "not" @@ -47,9 +47,9 @@ export function indentLine(cm, n, how, aggressive) { } indentation = Math.max(0, indentation) - var indentString = "", pos = 0 + let indentString = "", pos = 0 if (cm.options.indentWithTabs) - for (var i = Math.floor(indentation / tabSize); i; --i) {pos += tabSize; indentString += "\t"} + for (let i = Math.floor(indentation / tabSize); i; --i) {pos += tabSize; indentString += "\t"} if (pos < indentation) indentString += spaceStr(indentation - pos) if (indentString != curSpaceString) { @@ -59,10 +59,10 @@ export function indentLine(cm, n, how, aggressive) { } 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. - for (var i = 0; i < doc.sel.ranges.length; i++) { - var range = doc.sel.ranges[i] + for (let i = 0; i < doc.sel.ranges.length; i++) { + let range = doc.sel.ranges[i] if (range.head.line == n && range.head.ch < curSpaceString.length) { - var pos = Pos(n, curSpaceString.length) + let pos = Pos(n, curSpaceString.length) replaceOneSelection(doc, i, new Range(pos, pos)) break } diff --git a/src/input/input.js b/src/input/input.js index a084e08f..6cc3e911 100644 --- a/src/input/input.js +++ b/src/input/input.js @@ -13,25 +13,25 @@ import { indentLine } from "./indent" // This will be set to a {lineWise: bool, text: [string]} object, so // that, when pasting, we know what kind of selections the copied // text was made out of. -export var lastCopied = null +export let lastCopied = null export function setLastCopied(newLastCopied) { lastCopied = newLastCopied } export function applyTextInput(cm, inserted, deleted, sel, origin) { - var doc = cm.doc + let doc = cm.doc cm.display.shift = false if (!sel) sel = doc.sel - var paste = cm.state.pasteIncoming || origin == "paste" - var textLines = doc.splitLines(inserted), multiPaste = null + let paste = cm.state.pasteIncoming || origin == "paste" + let textLines = doc.splitLines(inserted), multiPaste = null // When pasing N lines into N selections, insert one line per selection if (paste && sel.ranges.length > 1) { if (lastCopied && lastCopied.text.join("\n") == inserted) { if (sel.ranges.length % lastCopied.text.length == 0) { multiPaste = [] - for (var i = 0; i < lastCopied.text.length; i++) + for (let i = 0; i < lastCopied.text.length; i++) multiPaste.push(doc.splitLines(lastCopied.text[i])) } } else if (textLines.length == sel.ranges.length) { @@ -39,10 +39,11 @@ export function applyTextInput(cm, inserted, deleted, sel, origin) { } } + let updateInput // Normal behavior is to insert the new text into every selection - for (var i = sel.ranges.length - 1; i >= 0; i--) { - var range = sel.ranges[i] - var from = range.from(), to = range.to() + for (let i = sel.ranges.length - 1; i >= 0; i--) { + let range = sel.ranges[i] + let from = range.from(), to = range.to() if (range.empty()) { if (deleted && deleted > 0) // Handle deletion from = Pos(from.line, from.ch - deleted) @@ -51,8 +52,8 @@ export function applyTextInput(cm, inserted, deleted, sel, origin) { else if (lastCopied && lastCopied.lineWise && lastCopied.text.join("\n") == inserted) from = to = Pos(from.line, 0) } - var updateInput = cm.curOp.updateInput - var changeEvent = {from: from, to: to, text: multiPaste ? multiPaste[i % multiPaste.length] : textLines, + updateInput = cm.curOp.updateInput + let changeEvent = {from: from, to: to, text: multiPaste ? multiPaste[i % multiPaste.length] : textLines, origin: origin || (paste ? "paste" : cm.state.cutIncoming ? "cut" : "+input")} makeChange(cm.doc, changeEvent) signalLater(cm, "inputRead", cm, changeEvent) @@ -67,7 +68,7 @@ export function applyTextInput(cm, inserted, deleted, sel, origin) { } export function handlePaste(e, cm) { - var pasted = e.clipboardData && e.clipboardData.getData("Text") + let pasted = e.clipboardData && e.clipboardData.getData("Text") if (pasted) { e.preventDefault() if (!cm.isReadOnly() && !cm.options.disableInput) @@ -79,15 +80,15 @@ export function handlePaste(e, cm) { export function triggerElectric(cm, inserted) { // When an 'electric' character is inserted, immediately trigger a reindent if (!cm.options.electricChars || !cm.options.smartIndent) return - var sel = cm.doc.sel + let sel = cm.doc.sel - for (var i = sel.ranges.length - 1; i >= 0; i--) { - var range = sel.ranges[i] + for (let i = sel.ranges.length - 1; i >= 0; i--) { + let range = sel.ranges[i] if (range.head.ch > 100 || (i && sel.ranges[i - 1].head.line == range.head.line)) continue - var mode = cm.getModeAt(range.head) - var indented = false + let mode = cm.getModeAt(range.head) + let indented = false if (mode.electricChars) { - for (var j = 0; j < mode.electricChars.length; j++) + for (let j = 0; j < mode.electricChars.length; j++) if (inserted.indexOf(mode.electricChars.charAt(j)) > -1) { indented = indentLine(cm, range.head.line, "smart") break @@ -101,10 +102,10 @@ export function triggerElectric(cm, inserted) { } export function copyableRanges(cm) { - 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)} + let text = [], ranges = [] + for (let i = 0; i < cm.doc.sel.ranges.length; i++) { + let line = cm.doc.sel.ranges[i].head.line + let lineRange = {anchor: Pos(line, 0), head: Pos(line + 1, 0)} ranges.push(lineRange) text.push(cm.getRange(lineRange.anchor, lineRange.head)) } @@ -118,8 +119,8 @@ export function disableBrowserMagic(field, spellcheck) { } export function hiddenTextarea() { - var te = elt("textarea", null, null, "position: absolute; bottom: -1em; padding: 0; width: 1px; height: 1em; outline: none") - var div = elt("div", [te], null, "overflow: hidden; position: relative; width: 3px; height: 0px;") + let te = elt("textarea", null, null, "position: absolute; bottom: -1em; padding: 0; width: 1px; height: 1em; outline: none") + let div = elt("div", [te], null, "overflow: hidden; position: relative; width: 3px; height: 0px;") // The textarea is kept positioned near the cursor to prevent the // fact that it'll be scrolled into view on input from scrolling // our fake cursor out of view. On webkit, when wrap=off, paste is diff --git a/src/input/keymap.js b/src/input/keymap.js index 0f5941e8..1b9564ef 100644 --- a/src/input/keymap.js +++ b/src/input/keymap.js @@ -3,7 +3,7 @@ import { map } from "../util/misc" import { keyNames } from "./keynames" -export var keyMap = {} +export let keyMap = {} keyMap.basic = { "Left": "goCharLeft", "Right": "goCharRight", "Up": "goLineUp", "Down": "goLineDown", @@ -49,10 +49,11 @@ keyMap["default"] = mac ? keyMap.macDefault : keyMap.pcDefault // KEYMAP DISPATCH function normalizeKeyName(name) { - var parts = name.split(/-(?!$)/), name = parts[parts.length - 1] - var alt, ctrl, shift, cmd - for (var i = 0; i < parts.length - 1; i++) { - var mod = parts[i] + let parts = name.split(/-(?!$)/) + name = parts[parts.length - 1] + let alt, ctrl, shift, cmd + for (let i = 0; i < parts.length - 1; i++) { + let mod = parts[i] if (/^(cmd|meta|m)$/i.test(mod)) cmd = true else if (/^a(lt)?$/i.test(mod)) alt = true else if (/^(c|ctrl|control)$/i.test(mod)) ctrl = true @@ -72,15 +73,15 @@ function normalizeKeyName(name) { // new normalized keymap, and then updates the old object to reflect // this. export function normalizeKeyMap(keymap) { - var copy = {} - for (var keyname in keymap) if (keymap.hasOwnProperty(keyname)) { - var value = keymap[keyname] + let copy = {} + for (let keyname in keymap) if (keymap.hasOwnProperty(keyname)) { + let value = keymap[keyname] if (/^(name|fallthrough|(de|at)tach)$/.test(keyname)) continue if (value == "...") { delete keymap[keyname]; continue } - var keys = map(keyname.split(" "), normalizeKeyName) - for (var i = 0; i < keys.length; i++) { - var val, name + let keys = map(keyname.split(" "), normalizeKeyName) + for (let i = 0; i < keys.length; i++) { + let val, name if (i == keys.length - 1) { name = keys.join(" ") val = value @@ -88,19 +89,19 @@ export function normalizeKeyMap(keymap) { name = keys.slice(0, i + 1).join(" ") val = "..." } - var prev = copy[name] + let prev = copy[name] if (!prev) copy[name] = val else if (prev != val) throw new Error("Inconsistent bindings for " + name) } delete keymap[keyname] } - for (var prop in copy) keymap[prop] = copy[prop] + for (let prop in copy) keymap[prop] = copy[prop] return keymap } export function lookupKey(key, map, handle, context) { map = getKeyMap(map) - var found = map.call ? map.call(key, context) : map[key] + let found = map.call ? map.call(key, context) : map[key] if (found === false) return "nothing" if (found === "...") return "multi" if (found != null && handle(found)) return "handled" @@ -108,8 +109,8 @@ export function lookupKey(key, map, handle, context) { if (map.fallthrough) { if (Object.prototype.toString.call(map.fallthrough) != "[object Array]") return lookupKey(key, map.fallthrough, handle, context) - for (var i = 0; i < map.fallthrough.length; i++) { - var result = lookupKey(key, map.fallthrough[i], handle, context) + for (let i = 0; i < map.fallthrough.length; i++) { + let result = lookupKey(key, map.fallthrough[i], handle, context) if (result) return result } } @@ -118,14 +119,14 @@ export function lookupKey(key, map, handle, context) { // Modifier key presses don't count as 'real' key presses for the // purpose of keymap fallthrough. export function isModifierKey(value) { - var name = typeof value == "string" ? value : keyNames[value.keyCode] + let name = typeof value == "string" ? value : keyNames[value.keyCode] return name == "Ctrl" || name == "Alt" || name == "Shift" || name == "Mod" } // Look up the name of a key as indicated by an event object. export function keyName(event, noShift) { if (presto && event.keyCode == 34 && event["char"]) return false - var base = keyNames[event.keyCode], name = base + let base = keyNames[event.keyCode], name = base if (name == null || event.altGraphKey) return false if (event.altKey && base != "Alt") name = "Alt-" + name if ((flipCtrlCmd ? event.metaKey : event.ctrlKey) && base != "Ctrl") name = "Ctrl-" + name diff --git a/src/input/keynames.js b/src/input/keynames.js index 8cf11f38..66bc8001 100644 --- a/src/input/keynames.js +++ b/src/input/keynames.js @@ -1,4 +1,4 @@ -export var keyNames = { +export let 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", 36: "Home", 37: "Left", 38: "Up", 39: "Right", 40: "Down", 44: "PrintScrn", 45: "Insert", @@ -10,8 +10,8 @@ export var keyNames = { } // Number keys -for (var i = 0; i < 10; i++) keyNames[i + 48] = keyNames[i + 96] = String(i) +for (let i = 0; i < 10; i++) keyNames[i + 48] = keyNames[i + 96] = String(i) // Alphabetic keys -for (var i = 65; i <= 90; i++) keyNames[i] = String.fromCharCode(i) +for (let i = 65; i <= 90; i++) keyNames[i] = String.fromCharCode(i) // Function keys -for (var i = 1; i <= 12; i++) keyNames[i + 111] = keyNames[i + 63235] = "F" + i +for (let i = 1; i <= 12; i++) keyNames[i + 111] = keyNames[i + 63235] = "F" + i diff --git a/src/line/highlight.js b/src/line/highlight.js index 0632be6e..8fd6d47b 100644 --- a/src/line/highlight.js +++ b/src/line/highlight.js @@ -12,20 +12,20 @@ import { clipPos } from "./pos" export function highlightLine(cm, line, state, forceToEnd) { // A styles array always starts with a number identifying the // mode/overlays that it is based on (for easy invalidation). - var st = [cm.state.modeGen], lineClasses = {} + let st = [cm.state.modeGen], lineClasses = {} // Compute the base array of styles runMode(cm, line.text, cm.doc.mode, state, function(end, style) { st.push(end, style) }, lineClasses, forceToEnd) // Run overlays, adjust style array. - for (var o = 0; o < cm.state.overlays.length; ++o) { - var overlay = cm.state.overlays[o], i = 1, at = 0 + for (let o = 0; o < cm.state.overlays.length; ++o) { + let overlay = cm.state.overlays[o], i = 1, at = 0 runMode(cm, line.text, overlay.mode, true, function(end, style) { - var start = i + let start = i // Ensure there's a token end at the current position, and that i points at it while (at < end) { - var i_end = st[i] + let i_end = st[i] if (i_end > end) st.splice(i, 1, end, st[i+1], i_end) i += 2 @@ -37,7 +37,7 @@ export function highlightLine(cm, line, state, forceToEnd) { i = start + 2 } else { for (; start < i; start += 2) { - var cur = st[start+1] + let cur = st[start+1] st[start+1] = (cur ? cur + " " : "") + "cm-overlay " + style } } @@ -49,8 +49,8 @@ export function highlightLine(cm, line, state, forceToEnd) { export function getLineStyles(cm, line, updateFrontier) { if (!line.styles || line.styles[0] != cm.state.modeGen) { - var state = getStateBefore(cm, lineNo(line)) - var result = highlightLine(cm, line, line.text.length > cm.options.maxHighlightLength ? copyState(cm.doc.mode, state) : state) + let state = getStateBefore(cm, lineNo(line)) + let result = highlightLine(cm, line, line.text.length > cm.options.maxHighlightLength ? copyState(cm.doc.mode, state) : state) line.stateAfter = state line.styles = result.styles if (result.classes) line.styleClasses = result.classes @@ -61,14 +61,14 @@ export function getLineStyles(cm, line, updateFrontier) { } export function getStateBefore(cm, n, precise) { - var doc = cm.doc, display = cm.display + let doc = cm.doc, display = cm.display if (!doc.mode.startState) return true - var pos = findStartLine(cm, n, precise), state = pos > doc.first && getLine(doc, pos-1).stateAfter + let 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) doc.iter(pos, n, function(line) { processLine(cm, line.text, state) - var save = pos == n - 1 || pos % 5 == 0 || pos >= display.viewFrom && pos < display.viewTo + let save = pos == n - 1 || pos % 5 == 0 || pos >= display.viewFrom && pos < display.viewTo line.stateAfter = save ? copyState(doc.mode, state) : null ++pos }) @@ -80,8 +80,8 @@ export function getStateBefore(cm, n, precise) { // update state, but don't save a style array. Used for lines that // aren't currently visible. export function processLine(cm, text, state, startAt) { - var mode = cm.doc.mode - var stream = new StringStream(text, cm.options.tabSize) + let mode = cm.doc.mode + let stream = new StringStream(text, cm.options.tabSize) stream.start = stream.pos = startAt || 0 if (text == "") callBlankLine(mode, state) while (!stream.eol()) { @@ -93,14 +93,14 @@ export function processLine(cm, text, state, startAt) { function callBlankLine(mode, state) { if (mode.blankLine) return mode.blankLine(state) if (!mode.innerMode) return - var inner = innerMode(mode, state) + let inner = innerMode(mode, state) if (inner.mode.blankLine) return inner.mode.blankLine(inner.state) } export function readToken(mode, stream, state, inner) { - for (var i = 0; i < 10; i++) { + for (let i = 0; i < 10; i++) { if (inner) inner[0] = innerMode(mode, state).mode - var style = mode.token(stream, state) + let style = mode.token(stream, state) if (stream.pos > stream.start) return style } throw new Error("Mode " + mode.name + " failed to advance stream.") @@ -115,10 +115,10 @@ export function takeToken(cm, pos, precise, asArray) { state: copy ? copyState(doc.mode, state) : state} } - var doc = cm.doc, mode = doc.mode, style + let doc = cm.doc, mode = doc.mode, style pos = clipPos(doc, pos) - var line = getLine(doc, pos.line), state = getStateBefore(cm, pos.line, precise) - var stream = new StringStream(line.text, cm.options.tabSize), tokens + let line = getLine(doc, pos.line), state = getStateBefore(cm, pos.line, precise) + let stream = new StringStream(line.text, cm.options.tabSize), tokens if (asArray) tokens = [] while ((asArray || stream.pos < pos.ch) && !stream.eol()) { stream.start = stream.pos @@ -130,10 +130,10 @@ export function takeToken(cm, pos, precise, asArray) { function extractLineClasses(type, output) { if (type) for (;;) { - var lineClass = type.match(/(?:^|\s+)line-(background-)?(\S+)/) + let lineClass = type.match(/(?:^|\s+)line-(background-)?(\S+)/) if (!lineClass) break type = type.slice(0, lineClass.index) + type.slice(lineClass.index + lineClass[0].length) - var prop = lineClass[1] ? "bgClass" : "textClass" + let prop = lineClass[1] ? "bgClass" : "textClass" if (output[prop] == null) output[prop] = lineClass[2] else if (!(new RegExp("(?:^|\s)" + lineClass[2] + "(?:$|\s)")).test(output[prop])) @@ -144,11 +144,11 @@ function extractLineClasses(type, output) { // Run the given mode's parser over a line, calling f for each token. function runMode(cm, text, mode, state, f, lineClasses, forceToEnd) { - var flattenSpans = mode.flattenSpans + let flattenSpans = mode.flattenSpans if (flattenSpans == null) flattenSpans = cm.options.flattenSpans - var curStart = 0, curStyle = null - var stream = new StringStream(text, cm.options.tabSize), style - var inner = cm.options.addModeClass && [null] + let curStart = 0, curStyle = null + let stream = new StringStream(text, cm.options.tabSize), style + let inner = cm.options.addModeClass && [null] if (text == "") extractLineClasses(callBlankLine(mode, state), lineClasses) while (!stream.eol()) { if (stream.pos > cm.options.maxHighlightLength) { @@ -160,7 +160,7 @@ function runMode(cm, text, mode, state, f, lineClasses, forceToEnd) { style = extractLineClasses(readToken(mode, stream, state, inner), lineClasses) } if (inner) { - var mName = inner[0].name + let mName = inner[0].name if (mName) style = "m-" + (style ? mName + " " + style : mName) } if (!flattenSpans || curStyle != style) { @@ -176,7 +176,7 @@ function runMode(cm, text, mode, state, f, lineClasses, forceToEnd) { // Webkit seems to refuse to render text nodes longer than 57444 // characters, and returns inaccurate measurements in nodes // starting around 5000 chars. - var pos = Math.min(stream.pos, curStart + 5000) + let pos = Math.min(stream.pos, curStart + 5000) f(pos, curStyle) curStart = pos } @@ -188,13 +188,13 @@ function runMode(cm, text, mode, state, f, lineClasses, forceToEnd) { // smallest indentation, which tends to need the least context to // parse correctly. function findStartLine(cm, n, precise) { - var minindent, minline, doc = cm.doc - var lim = precise ? -1 : n - (cm.doc.mode.innerMode ? 1000 : 100) - for (var search = n; search > lim; --search) { + let minindent, minline, doc = cm.doc + let lim = precise ? -1 : n - (cm.doc.mode.innerMode ? 1000 : 100) + for (let search = n; search > lim; --search) { if (search <= doc.first) return doc.first - var line = getLine(doc, search - 1) + let line = getLine(doc, search - 1) if (line.stateAfter && (!precise || search <= doc.frontier)) return search - var indented = countColumn(line.text, null, cm.options.tabSize) + let indented = countColumn(line.text, null, cm.options.tabSize) if (minline == null || minindent > indented) { minline = search - 1 minindent = indented diff --git a/src/line/line_data.js b/src/line/line_data.js index 8b247d53..7a3a4122 100644 --- a/src/line/line_data.js +++ b/src/line/line_data.js @@ -31,7 +31,7 @@ export function updateLine(line, text, markedSpans, estimateHeight) { if (line.order != null) line.order = null detachMarkedSpans(line) attachMarkedSpans(line, markedSpans) - var estHeight = estimateHeight ? estimateHeight(line) : 1 + let estHeight = estimateHeight ? estimateHeight(line) : 1 if (estHeight != line.height) updateLineHeight(line, estHeight) } @@ -44,10 +44,10 @@ export function cleanUpLine(line) { // Convert a style as returned by a mode (either null, or a string // containing one or more styles) to a CSS style. This is cached, // and also looks for line-wide styles. -var styleToClassCache = {}, styleToClassCacheWithMode = {} +let styleToClassCache = {}, styleToClassCacheWithMode = {} function interpretTokenStyle(style, options) { if (!style || /^\s*$/.test(style)) return null - var cache = options.addModeClass ? styleToClassCacheWithMode : styleToClassCache + let cache = options.addModeClass ? styleToClassCacheWithMode : styleToClassCache return cache[style] || (cache[style] = style.replace(/\S+/g, "cm-$&")) } @@ -61,16 +61,16 @@ export function buildLineContent(cm, lineView) { // The padding-right forces the element to have a 'border', which // is needed on Webkit to be able to get line-level bounding // rectangles for it (in measureChar). - var content = elt("span", null, null, webkit ? "padding-right: .1px" : null) - var builder = {pre: elt("pre", [content], "CodeMirror-line"), content: content, + let content = elt("span", null, null, webkit ? "padding-right: .1px" : null) + let builder = {pre: elt("pre", [content], "CodeMirror-line"), content: content, col: 0, pos: 0, cm: cm, trailingSpace: false, splitSpaces: (ie || webkit) && cm.getOption("lineWrapping")} lineView.measure = {} // Iterate over the logical lines that make up this visual line. - for (var i = 0; i <= (lineView.rest ? lineView.rest.length : 0); i++) { - var line = i ? lineView.rest[i - 1] : lineView.line, order + for (let i = 0; i <= (lineView.rest ? lineView.rest.length : 0); i++) { + let line = i ? lineView.rest[i - 1] : lineView.line, order builder.pos = 0 builder.addToken = buildToken // Optionally wire in some hacks into the token-rendering @@ -78,7 +78,7 @@ export function buildLineContent(cm, lineView) { if (hasBadBidiRects(cm.display.measure) && (order = getOrder(line))) builder.addToken = buildTokenBadBidi(builder.addToken, order) builder.map = [] - var allowFrontierUpdate = lineView != cm.display.externalMeasured && lineNo(line) + let allowFrontierUpdate = lineView != cm.display.externalMeasured && lineNo(line) insertLineContent(line, builder, getLineStyles(cm, line, allowFrontierUpdate)) if (line.styleClasses) { if (line.styleClasses.bgClass) @@ -103,7 +103,7 @@ export function buildLineContent(cm, lineView) { // See issue #2901 if (webkit) { - var last = builder.content.lastChild + let last = builder.content.lastChild if (/\bcm-tab\b/.test(last.className) || (last.querySelector && last.querySelector(".cm-tab"))) builder.content.className = "cm-tab-wrap-hack" } @@ -116,7 +116,7 @@ export function buildLineContent(cm, lineView) { } export function defaultSpecialCharPlaceholder(ch) { - var token = elt("span", "\u2022", "cm-invalidchar") + let token = elt("span", "\u2022", "cm-invalidchar") token.title = "\\u" + ch.charCodeAt(0).toString(16) token.setAttribute("aria-label", token.title) return token @@ -126,22 +126,24 @@ export function defaultSpecialCharPlaceholder(ch) { // the line map. Takes care to render special characters separately. function buildToken(builder, text, style, startStyle, endStyle, title, css) { if (!text) return - var displayText = builder.splitSpaces ? splitSpaces(text, builder.trailingSpace) : text - var special = builder.cm.state.specialChars, mustWrap = false + let displayText = builder.splitSpaces ? splitSpaces(text, builder.trailingSpace) : text + let special = builder.cm.state.specialChars, mustWrap = false + let content if (!special.test(text)) { builder.col += text.length - var content = document.createTextNode(displayText) + content = document.createTextNode(displayText) builder.map.push(builder.pos, builder.pos + text.length, content) if (ie && ie_version < 9) mustWrap = true builder.pos += text.length } else { - var content = document.createDocumentFragment(), pos = 0 + content = document.createDocumentFragment() + let pos = 0 while (true) { special.lastIndex = pos - var m = special.exec(text) - var skipped = m ? m.index - pos : text.length - pos + let m = special.exec(text) + let skipped = m ? m.index - pos : text.length - pos if (skipped) { - var txt = document.createTextNode(displayText.slice(pos, pos + skipped)) + let txt = document.createTextNode(displayText.slice(pos, pos + skipped)) if (ie && ie_version < 9) content.appendChild(elt("span", [txt])) else content.appendChild(txt) builder.map.push(builder.pos, builder.pos + skipped, txt) @@ -150,18 +152,19 @@ function buildToken(builder, text, style, startStyle, endStyle, title, css) { } if (!m) break pos += skipped + 1 + let txt if (m[0] == "\t") { - var tabSize = builder.cm.options.tabSize, tabWidth = tabSize - builder.col % tabSize - var txt = content.appendChild(elt("span", spaceStr(tabWidth), "cm-tab")) + let tabSize = builder.cm.options.tabSize, tabWidth = tabSize - builder.col % tabSize + txt = content.appendChild(elt("span", spaceStr(tabWidth), "cm-tab")) txt.setAttribute("role", "presentation") txt.setAttribute("cm-text", "\t") builder.col += tabWidth } else if (m[0] == "\r" || m[0] == "\n") { - var txt = content.appendChild(elt("span", m[0] == "\r" ? "\u240d" : "\u2424", "cm-invalidchar")) + txt = content.appendChild(elt("span", m[0] == "\r" ? "\u240d" : "\u2424", "cm-invalidchar")) txt.setAttribute("cm-text", m[0]) builder.col += 1 } else { - var txt = builder.cm.options.specialCharPlaceholder(m[0]) + txt = builder.cm.options.specialCharPlaceholder(m[0]) txt.setAttribute("cm-text", m[0]) if (ie && ie_version < 9) content.appendChild(elt("span", [txt])) else content.appendChild(txt) @@ -173,10 +176,10 @@ function buildToken(builder, text, style, startStyle, endStyle, title, css) { } builder.trailingSpace = displayText.charCodeAt(text.length - 1) == 32 if (style || startStyle || endStyle || mustWrap || css) { - var fullStyle = style || "" + let fullStyle = style || "" if (startStyle) fullStyle += startStyle if (endStyle) fullStyle += endStyle - var token = elt("span", [content], fullStyle, css) + let token = elt("span", [content], fullStyle, css) if (title) token.title = title return builder.content.appendChild(token) } @@ -185,9 +188,9 @@ function buildToken(builder, text, style, startStyle, endStyle, title, css) { function splitSpaces(text, trailingBefore) { if (text.length > 1 && !/ /.test(text)) return text - var spaceBefore = trailingBefore, result = "" - for (var i = 0; i < text.length; i++) { - var ch = text.charAt(i) + let spaceBefore = trailingBefore, result = "" + for (let i = 0; i < text.length; i++) { + let ch = text.charAt(i) if (ch == " " && spaceBefore && (i == text.length - 1 || text.charCodeAt(i + 1) == 32)) ch = "\u00a0" result += ch @@ -201,11 +204,12 @@ function splitSpaces(text, trailingBefore) { function buildTokenBadBidi(inner, order) { return function(builder, text, style, startStyle, endStyle, title, css) { style = style ? style + " cm-force-border" : "cm-force-border" - var start = builder.pos, end = start + text.length + let start = builder.pos, end = start + text.length for (;;) { // Find the part that overlaps with the start of this text - for (var i = 0; i < order.length; i++) { - var part = order[i] + let part + for (let i = 0; i < order.length; i++) { + part = order[i] if (part.to > start && part.from <= start) break } if (part.to >= end) return inner(builder, text, style, startStyle, endStyle, title, css) @@ -218,7 +222,7 @@ function buildTokenBadBidi(inner, order) { } function buildCollapsedSpan(builder, size, marker, ignoreWidget) { - var widget = !ignoreWidget && marker.widgetNode + let widget = !ignoreWidget && marker.widgetNode if (widget) builder.map.push(builder.pos, builder.pos + size, widget) if (!ignoreWidget && builder.cm.display.input.needsContentAttribute) { if (!widget) @@ -236,22 +240,22 @@ function buildCollapsedSpan(builder, size, marker, ignoreWidget) { // Outputs a number of spans to make up a line, taking highlighting // and marked text into account. function insertLineContent(line, builder, styles) { - var spans = line.markedSpans, allText = line.text, at = 0 + let spans = line.markedSpans, allText = line.text, at = 0 if (!spans) { - for (var i = 1; i < styles.length; i+=2) + for (let i = 1; i < styles.length; i+=2) builder.addToken(builder, allText.slice(at, at = styles[i]), interpretTokenStyle(styles[i+1], builder.cm.options)) return } - var len = allText.length, pos = 0, i = 1, text = "", style, css - var nextChange = 0, spanStyle, spanEndStyle, spanStartStyle, title, collapsed + let len = allText.length, pos = 0, i = 1, text = "", style, css + let nextChange = 0, spanStyle, spanEndStyle, spanStartStyle, title, collapsed for (;;) { if (nextChange == pos) { // Update current marker set spanStyle = spanEndStyle = spanStartStyle = title = css = "" collapsed = null; nextChange = Infinity - var foundBookmarks = [], endStyles - for (var j = 0; j < spans.length; ++j) { - var sp = spans[j], m = sp.marker + let foundBookmarks = [], endStyles + for (let j = 0; j < spans.length; ++j) { + let sp = spans[j], m = sp.marker if (m.type == "bookmark" && sp.from == pos && m.widgetNode) { foundBookmarks.push(m) } else if (sp.from <= pos && (sp.to == null || sp.to > pos || m.collapsed && sp.to == pos && sp.from == pos)) { @@ -270,10 +274,10 @@ function insertLineContent(line, builder, styles) { nextChange = sp.from } } - if (endStyles) for (var j = 0; j < endStyles.length; j += 2) + if (endStyles) for (let j = 0; j < endStyles.length; j += 2) if (endStyles[j + 1] == nextChange) spanEndStyle += " " + endStyles[j] - if (!collapsed || collapsed.from == pos) for (var j = 0; j < foundBookmarks.length; ++j) + if (!collapsed || collapsed.from == pos) for (let j = 0; j < foundBookmarks.length; ++j) buildCollapsedSpan(builder, 0, foundBookmarks[j]) if (collapsed && (collapsed.from || 0) == pos) { buildCollapsedSpan(builder, (collapsed.to == null ? len + 1 : collapsed.to) - pos, @@ -284,12 +288,12 @@ function insertLineContent(line, builder, styles) { } if (pos >= len) break - var upto = Math.min(len, nextChange) + let upto = Math.min(len, nextChange) while (true) { if (text) { - var end = pos + text.length + let end = pos + text.length if (!collapsed) { - var tokenText = end > upto ? text.slice(0, upto - pos) : text + let tokenText = end > upto ? text.slice(0, upto - pos) : text builder.addToken(builder, tokenText, style ? style + spanStyle : spanStyle, spanStartStyle, pos + tokenText.length == nextChange ? spanEndStyle : "", title, css) } @@ -320,9 +324,9 @@ export function LineView(doc, line, lineN) { // Create a range of LineView objects for the given lines. export function buildViewArray(cm, from, to) { - var array = [], nextPos - for (var pos = from; pos < to; pos = nextPos) { - var view = new LineView(cm.doc, getLine(cm.doc, pos), pos) + let array = [], nextPos + for (let pos = from; pos < to; pos = nextPos) { + let view = new LineView(cm.doc, getLine(cm.doc, pos), pos) nextPos = pos + view.size array.push(view) } diff --git a/src/line/pos.js b/src/line/pos.js index b5d2513a..73b2e0b8 100644 --- a/src/line/pos.js +++ b/src/line/pos.js @@ -19,17 +19,18 @@ export function minPos(a, b) { return cmp(a, b) < 0 ? a : b } export function clipLine(doc, n) {return Math.max(doc.first, Math.min(n, doc.first + doc.size - 1))} export function clipPos(doc, pos) { if (pos.line < doc.first) return Pos(doc.first, 0) - var last = doc.first + doc.size - 1 + let last = doc.first + doc.size - 1 if (pos.line > last) return Pos(last, getLine(doc, last).text.length) return clipToLen(pos, getLine(doc, pos.line).text.length) } function clipToLen(pos, linelen) { - var ch = pos.ch + let 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 } export function clipPosArray(doc, array) { - for (var out = [], i = 0; i < array.length; i++) out[i] = clipPos(doc, array[i]) + let out = [] + for (let i = 0; i < array.length; i++) out[i] = clipPos(doc, array[i]) return out } diff --git a/src/line/saw_special_spans.js b/src/line/saw_special_spans.js index 0cf5ff32..d315e7ba 100644 --- a/src/line/saw_special_spans.js +++ b/src/line/saw_special_spans.js @@ -1,5 +1,5 @@ // Optimize some code when these features are not used. -export var sawReadOnlySpans = false, sawCollapsedSpans = false +export let sawReadOnlySpans = false, sawCollapsedSpans = false export function seeReadOnlySpans() { sawReadOnlySpans = true diff --git a/src/line/spans.js b/src/line/spans.js index fd9bd554..63acacfc 100644 --- a/src/line/spans.js +++ b/src/line/spans.js @@ -13,15 +13,16 @@ export function MarkedSpan(marker, from, to) { // Search an array of spans for a span matching the given marker. export function getMarkedSpanFor(spans, marker) { - if (spans) for (var i = 0; i < spans.length; ++i) { - var span = spans[i] + if (spans) for (let i = 0; i < spans.length; ++i) { + let span = spans[i] if (span.marker == marker) return span } } // Remove a span from an array, returning undefined if no spans are // left (we don't store arrays for lines without spans). export function removeMarkedSpan(spans, span) { - for (var r, i = 0; i < spans.length; ++i) + let r + for (let i = 0; i < spans.length; ++i) if (spans[i] != span) (r || (r = [])).push(spans[i]) return r } @@ -36,22 +37,24 @@ export function addMarkedSpan(line, span) { // character position, returning an array of remaining chunks (or // undefined if nothing remains). function markedSpansBefore(old, startCh, isInsert) { - 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) + let nw + if (old) for (let i = 0; i < old.length; ++i) { + let span = old[i], marker = span.marker + let startsBefore = span.from == null || (marker.inclusiveLeft ? span.from <= startCh : span.from < startCh) if (startsBefore || span.from == startCh && marker.type == "bookmark" && (!isInsert || !span.marker.insertLeft)) { - var endsAfter = span.to == null || (marker.inclusiveRight ? span.to >= startCh : span.to > startCh) + let endsAfter = span.to == null || (marker.inclusiveRight ? span.to >= startCh : span.to > startCh) ;(nw || (nw = [])).push(new MarkedSpan(marker, span.from, endsAfter ? null : span.to)) } } return nw } function markedSpansAfter(old, endCh, isInsert) { - 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) + let nw + if (old) for (let i = 0; i < old.length; ++i) { + let span = old[i], marker = span.marker + let endsAfter = span.to == null || (marker.inclusiveRight ? span.to >= endCh : span.to > endCh) if (endsAfter || span.from == endCh && marker.type == "bookmark" && (!isInsert || span.marker.insertLeft)) { - var startsBefore = span.from == null || (marker.inclusiveLeft ? span.from <= endCh : span.from < endCh) + let startsBefore = span.from == null || (marker.inclusiveLeft ? span.from <= endCh : span.from < endCh) ;(nw || (nw = [])).push(new MarkedSpan(marker, startsBefore ? null : span.from - endCh, span.to == null ? null : span.to - endCh)) } @@ -67,23 +70,23 @@ function markedSpansAfter(old, endCh, isInsert) { // arrays with one element for each line in (after) the change. export function stretchSpansOverChange(doc, change) { if (change.full) return null - 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 + let oldFirst = isLine(doc, change.from.line) && getLine(doc, change.from.line).markedSpans + let 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, isInsert = cmp(change.from, change.to) == 0 + let startCh = change.from.ch, endCh = change.to.ch, isInsert = cmp(change.from, change.to) == 0 // Get the spans that 'stick out' on both sides - var first = markedSpansBefore(oldFirst, startCh, isInsert) - var last = markedSpansAfter(oldLast, endCh, isInsert) + let first = markedSpansBefore(oldFirst, startCh, isInsert) + let last = markedSpansAfter(oldLast, endCh, isInsert) // Next, merge those two ends - var sameLine = change.text.length == 1, offset = lst(change.text).length + (sameLine ? startCh : 0) + let 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) { - var span = first[i] + for (let i = 0; i < first.length; ++i) { + let span = first[i] if (span.to == null) { - var found = getMarkedSpanFor(last, span.marker) + let found = getMarkedSpanFor(last, span.marker) if (!found) span.to = startCh else if (sameLine) span.to = found.to == null ? null : found.to + offset } @@ -91,11 +94,11 @@ export function stretchSpansOverChange(doc, change) { } 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] + for (let i = 0; i < last.length; ++i) { + let span = last[i] if (span.to != null) span.to += offset if (span.from == null) { - var found = getMarkedSpanFor(first, span.marker) + let found = getMarkedSpanFor(first, span.marker) if (!found) { span.from = offset if (sameLine) (first || (first = [])).push(span) @@ -110,15 +113,15 @@ export function stretchSpansOverChange(doc, change) { if (first) first = clearEmptySpans(first) if (last && last != first) last = clearEmptySpans(last) - var newMarkers = [first] + let newMarkers = [first] if (!sameLine) { // Fill gap with whole-line-spans - var gap = change.text.length - 2, gapMarkers + let gap = change.text.length - 2, gapMarkers if (gap > 0 && first) - for (var i = 0; i < first.length; ++i) + for (let i = 0; i < first.length; ++i) if (first[i].to == null) (gapMarkers || (gapMarkers = [])).push(new MarkedSpan(first[i].marker, null, null)) - for (var i = 0; i < gap; ++i) + for (let i = 0; i < gap; ++i) newMarkers.push(gapMarkers) newMarkers.push(last) } @@ -128,8 +131,8 @@ export function stretchSpansOverChange(doc, change) { // Remove spans that are empty and don't have a clearWhenEmpty // option of false. function clearEmptySpans(spans) { - for (var i = 0; i < spans.length; ++i) { - var span = spans[i] + for (let i = 0; i < spans.length; ++i) { + let span = spans[i] if (span.from != null && span.from == span.to && span.marker.clearWhenEmpty !== false) spans.splice(i--, 1) } @@ -139,22 +142,22 @@ function clearEmptySpans(spans) { // Used to 'clip' out readOnly ranges when making a change. export function removeReadOnlyRanges(doc, from, to) { - var markers = null + let markers = null doc.iter(from.line, to.line + 1, function(line) { - if (line.markedSpans) for (var i = 0; i < line.markedSpans.length; ++i) { - var mark = line.markedSpans[i].marker + if (line.markedSpans) for (let i = 0; i < line.markedSpans.length; ++i) { + let mark = line.markedSpans[i].marker if (mark.readOnly && (!markers || indexOf(markers, mark) == -1)) (markers || (markers = [])).push(mark) } }) if (!markers) return null - var parts = [{from: from, to: to}] - for (var i = 0; i < markers.length; ++i) { - var mk = markers[i], m = mk.find(0) - for (var j = 0; j < parts.length; ++j) { - var p = parts[j] + let parts = [{from: from, to: to}] + for (let i = 0; i < markers.length; ++i) { + let mk = markers[i], m = mk.find(0) + for (let j = 0; j < parts.length; ++j) { + let p = parts[j] if (cmp(p.to, m.from) < 0 || cmp(p.from, m.to) > 0) continue - var newParts = [j, 1], dfrom = cmp(p.from, m.from), dto = cmp(p.to, m.to) + let newParts = [j, 1], dfrom = cmp(p.from, m.from), dto = cmp(p.to, m.to) if (dfrom < 0 || !mk.inclusiveLeft && !dfrom) newParts.push({from: p.from, to: m.from}) if (dto > 0 || !mk.inclusiveRight && !dto) @@ -168,15 +171,15 @@ export function removeReadOnlyRanges(doc, from, to) { // Connect or disconnect spans from a line. export function detachMarkedSpans(line) { - var spans = line.markedSpans + let spans = line.markedSpans if (!spans) return - for (var i = 0; i < spans.length; ++i) + for (let i = 0; i < spans.length; ++i) spans[i].marker.detachLine(line) line.markedSpans = null } export function attachMarkedSpans(line, spans) { if (!spans) return - for (var i = 0; i < spans.length; ++i) + for (let i = 0; i < spans.length; ++i) spans[i].marker.attachLine(line) line.markedSpans = spans } @@ -190,12 +193,12 @@ function extraRight(marker) { return marker.inclusiveRight ? 1 : 0 } // spans is larger (and thus includes the other). Falls back to // comparing ids when the spans cover exactly the same range. export function compareCollapsedMarkers(a, b) { - var lenDiff = a.lines.length - b.lines.length + let lenDiff = a.lines.length - b.lines.length if (lenDiff != 0) return lenDiff - var aPos = a.find(), bPos = b.find() - var fromCmp = cmp(aPos.from, bPos.from) || extraLeft(a) - extraLeft(b) + let aPos = a.find(), bPos = b.find() + let fromCmp = cmp(aPos.from, bPos.from) || extraLeft(a) - extraLeft(b) if (fromCmp) return -fromCmp - var toCmp = cmp(aPos.to, bPos.to) || extraRight(a) - extraRight(b) + let toCmp = cmp(aPos.to, bPos.to) || extraRight(a) - extraRight(b) if (toCmp) return toCmp return b.id - a.id } @@ -203,8 +206,8 @@ export function compareCollapsedMarkers(a, b) { // Find out whether a line ends or starts in a collapsed span. If // so, return the marker for that span. function collapsedSpanAtSide(line, start) { - var sps = sawCollapsedSpans && line.markedSpans, found - if (sps) for (var sp, i = 0; i < sps.length; ++i) { + let sps = sawCollapsedSpans && line.markedSpans, found + if (sps) for (let sp, i = 0; i < sps.length; ++i) { sp = sps[i] if (sp.marker.collapsed && (start ? sp.from : sp.to) == null && (!found || compareCollapsedMarkers(found, sp.marker) < 0)) @@ -219,14 +222,14 @@ export function collapsedSpanAtEnd(line) { return collapsedSpanAtSide(line, fals // overlaps (covers the start or end, but not both) of a new span. // Such overlap is not allowed. export function conflictingCollapsedRange(doc, lineNo, from, to, marker) { - var line = getLine(doc, lineNo) - var sps = sawCollapsedSpans && line.markedSpans - if (sps) for (var i = 0; i < sps.length; ++i) { - var sp = sps[i] + let line = getLine(doc, lineNo) + let sps = sawCollapsedSpans && line.markedSpans + if (sps) for (let i = 0; i < sps.length; ++i) { + let sp = sps[i] if (!sp.marker.collapsed) continue - var found = sp.marker.find(0) - var fromCmp = cmp(found.from, from) || extraLeft(sp.marker) - extraLeft(marker) - var toCmp = cmp(found.to, to) || extraRight(sp.marker) - extraRight(marker) + let found = sp.marker.find(0) + let fromCmp = cmp(found.from, from) || extraLeft(sp.marker) - extraLeft(marker) + let toCmp = cmp(found.to, to) || extraRight(sp.marker) - extraRight(marker) if (fromCmp >= 0 && toCmp <= 0 || fromCmp <= 0 && toCmp >= 0) continue if (fromCmp <= 0 && (sp.marker.inclusiveRight && marker.inclusiveLeft ? cmp(found.to, from) >= 0 : cmp(found.to, from) > 0) || fromCmp >= 0 && (sp.marker.inclusiveRight && marker.inclusiveLeft ? cmp(found.from, to) <= 0 : cmp(found.from, to) < 0)) @@ -239,7 +242,7 @@ export function conflictingCollapsedRange(doc, lineNo, from, to, marker) { // visual line. This finds the start of the visual line that the // given line is part of (usually that is the line itself). export function visualLine(line) { - var merged + let merged while (merged = collapsedSpanAtStart(line)) line = merged.find(-1, true).line return line @@ -248,7 +251,7 @@ export function visualLine(line) { // Returns an array of logical lines that continue the visual line // started by the argument, or undefined if there are no such lines. export function visualLineContinued(line) { - var merged, lines + let merged, lines while (merged = collapsedSpanAtEnd(line)) { line = merged.find(1, true).line ;(lines || (lines = [])).push(line) @@ -259,7 +262,7 @@ export function visualLineContinued(line) { // Get the line number of the start of the visual line that the // given line number is part of. export function visualLineNo(doc, lineN) { - var line = getLine(doc, lineN), vis = visualLine(line) + let line = getLine(doc, lineN), vis = visualLine(line) if (line == vis) return lineN return lineNo(vis) } @@ -268,7 +271,7 @@ export function visualLineNo(doc, lineN) { // the given line. export function visualLineEndNo(doc, lineN) { if (lineN > doc.lastLine()) return lineN - var line = getLine(doc, lineN), merged + let line = getLine(doc, lineN), merged if (!lineIsHidden(doc, line)) return lineN while (merged = collapsedSpanAtEnd(line)) line = merged.find(1, true).line @@ -279,8 +282,8 @@ export function visualLineEndNo(doc, lineN) { // are part of a visual line that starts with another line, or when // they are entirely covered by collapsed, non-widget span. export function lineIsHidden(doc, line) { - var sps = sawCollapsedSpans && line.markedSpans - if (sps) for (var sp, i = 0; i < sps.length; ++i) { + let sps = sawCollapsedSpans && line.markedSpans + if (sps) for (let sp, i = 0; i < sps.length; ++i) { sp = sps[i] if (!sp.marker.collapsed) continue if (sp.from == null) return true @@ -291,12 +294,12 @@ export function lineIsHidden(doc, line) { } function lineIsHiddenInner(doc, line, span) { if (span.to == null) { - var end = span.marker.find(1, true) + let end = span.marker.find(1, true) return lineIsHiddenInner(doc, end.line, getMarkedSpanFor(end.line.markedSpans, span.marker)) } if (span.marker.inclusiveRight && span.to == line.text.length) return true - for (var sp, i = 0; i < line.markedSpans.length; ++i) { + for (let sp, i = 0; i < line.markedSpans.length; ++i) { sp = line.markedSpans[i] if (sp.marker.collapsed && !sp.marker.widgetNode && sp.from == span.to && (sp.to == null || sp.to != span.from) && @@ -309,15 +312,15 @@ function lineIsHiddenInner(doc, line, span) { export function heightAtLine(lineObj) { lineObj = visualLine(lineObj) - var h = 0, chunk = lineObj.parent - for (var i = 0; i < chunk.lines.length; ++i) { - var line = chunk.lines[i] + let h = 0, chunk = lineObj.parent + for (let i = 0; i < chunk.lines.length; ++i) { + let line = chunk.lines[i] if (line == lineObj) break else h += line.height } - for (var p = chunk.parent; p; chunk = p, p = chunk.parent) { - for (var i = 0; i < p.children.length; ++i) { - var cur = p.children[i] + for (let p = chunk.parent; p; chunk = p, p = chunk.parent) { + for (let i = 0; i < p.children.length; ++i) { + let cur = p.children[i] if (cur == chunk) break else h += cur.height } @@ -330,15 +333,15 @@ export function heightAtLine(lineObj) { // other lines onto it. export function lineLength(line) { if (line.height == 0) return 0 - var len = line.text.length, merged, cur = line + let len = line.text.length, merged, cur = line while (merged = collapsedSpanAtStart(cur)) { - var found = merged.find(0, true) + let found = merged.find(0, true) cur = found.from.line len += found.from.ch - found.to.ch } cur = line while (merged = collapsedSpanAtEnd(cur)) { - var found = merged.find(0, true) + let found = merged.find(0, true) len -= cur.text.length - found.from.ch cur = found.to.line len += cur.text.length - found.to.ch @@ -348,12 +351,12 @@ export function lineLength(line) { // Find the longest line in the document. export function findMaxLine(cm) { - var d = cm.display, doc = cm.doc + let d = cm.display, doc = cm.doc d.maxLine = getLine(doc, doc.first) d.maxLineLength = lineLength(d.maxLine) d.maxLineChanged = true doc.iter(function(line) { - var len = lineLength(line) + let len = lineLength(line) if (len > d.maxLineLength) { d.maxLineLength = len d.maxLine = line diff --git a/src/line/utils_line.js b/src/line/utils_line.js index 4e10a1d8..647c0779 100644 --- a/src/line/utils_line.js +++ b/src/line/utils_line.js @@ -4,9 +4,10 @@ import { indexOf } from "../util/misc" export function getLine(doc, n) { n -= doc.first if (n < 0 || n >= doc.size) throw new Error("There is no line " + (n + doc.first) + " in the document.") - for (var chunk = doc; !chunk.lines;) { - for (var i = 0;; ++i) { - var child = chunk.children[i], sz = child.chunkSize() + let chunk = doc + while (!chunk.lines) { + for (let i = 0;; ++i) { + let child = chunk.children[i], sz = child.chunkSize() if (n < sz) { chunk = child; break } n -= sz } @@ -17,9 +18,9 @@ export function getLine(doc, n) { // Get the part of a document between two positions, as an array of // strings. export function getBetween(doc, start, end) { - var out = [], n = start.line + let out = [], n = start.line doc.iter(start.line, end.line + 1, function(line) { - var text = line.text + let 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) @@ -29,7 +30,7 @@ export function getBetween(doc, start, end) { } // Get the lines between from and to, as array of strings. export function getLines(doc, from, to) { - var out = [] + let out = [] doc.iter(from, to, function(line) { out.push(line.text) }) return out } @@ -37,17 +38,17 @@ export function getLines(doc, from, to) { // Update the height of a line, propagating the height change // upwards to parent nodes. export function updateLineHeight(line, height) { - var diff = height - line.height - if (diff) for (var n = line; n; n = n.parent) n.height += diff + let diff = height - line.height + if (diff) for (let n = line; n; n = n.parent) n.height += diff } // Given a line object, find its line number by walking up through // its parent links. export function lineNo(line) { if (line.parent == null) return null - var cur = line.parent, no = indexOf(cur.lines, line) - for (var chunk = cur.parent; chunk; cur = chunk, chunk = chunk.parent) { - for (var i = 0;; ++i) { + let cur = line.parent, no = indexOf(cur.lines, line) + for (let chunk = cur.parent; chunk; cur = chunk, chunk = chunk.parent) { + for (let i = 0;; ++i) { if (chunk.children[i] == cur) break no += chunk.children[i].chunkSize() } @@ -58,18 +59,19 @@ export function lineNo(line) { // Find the line at the given vertical position, using the height // information in the document tree. export function lineAtHeight(chunk, h) { - var n = chunk.first + let n = chunk.first outer: do { - for (var i = 0; i < chunk.children.length; ++i) { - var child = chunk.children[i], ch = child.height + for (let i = 0; i < chunk.children.length; ++i) { + let child = chunk.children[i], ch = child.height if (h < ch) { chunk = child; continue outer } h -= ch n += child.chunkSize() } return n } while (!chunk.lines) - for (var i = 0; i < chunk.lines.length; ++i) { - var line = chunk.lines[i], lh = line.height + let i = 0 + for (; i < chunk.lines.length; ++i) { + let line = chunk.lines[i], lh = line.height if (h < lh) break h -= lh } diff --git a/src/measurement/position_measurement.js b/src/measurement/position_measurement.js index 75011a34..63e3771b 100644 --- a/src/measurement/position_measurement.js +++ b/src/measurement/position_measurement.js @@ -18,9 +18,9 @@ export function paddingTop(display) {return display.lineSpace.offsetTop} export function paddingVert(display) {return display.mover.offsetHeight - display.lineSpace.offsetHeight} export function paddingH(display) { if (display.cachedPaddingH) return display.cachedPaddingH - var e = removeChildrenAndAdd(display.measure, elt("pre", "x")) - var style = window.getComputedStyle ? window.getComputedStyle(e) : e.currentStyle - var data = {left: parseInt(style.paddingLeft), right: parseInt(style.paddingRight)} + let e = removeChildrenAndAdd(display.measure, elt("pre", "x")) + let style = window.getComputedStyle ? window.getComputedStyle(e) : e.currentStyle + let data = {left: parseInt(style.paddingLeft), right: parseInt(style.paddingRight)} if (!isNaN(data.left) && !isNaN(data.right)) display.cachedPaddingH = data return data } @@ -38,15 +38,15 @@ export function displayHeight(cm) { // line. When lineWrapping is on, there might be more than one // height. function ensureLineHeights(cm, lineView, rect) { - var wrapping = cm.options.lineWrapping - var curWidth = wrapping && displayWidth(cm) + let wrapping = cm.options.lineWrapping + let curWidth = wrapping && displayWidth(cm) if (!lineView.measure.heights || wrapping && lineView.measure.width != curWidth) { - var heights = lineView.measure.heights = [] + let heights = lineView.measure.heights = [] if (wrapping) { lineView.measure.width = curWidth - var rects = lineView.text.firstChild.getClientRects() - for (var i = 0; i < rects.length - 1; i++) { - var cur = rects[i], next = rects[i + 1] + let rects = lineView.text.firstChild.getClientRects() + for (let i = 0; i < rects.length - 1; i++) { + let cur = rects[i], next = rects[i + 1] if (Math.abs(cur.bottom - next.bottom) > 2) heights.push((cur.bottom + next.top) / 2 - rect.top) } @@ -61,10 +61,10 @@ function ensureLineHeights(cm, lineView, rect) { export function mapFromLineView(lineView, line, lineN) { if (lineView.line == line) return {map: lineView.measure.map, cache: lineView.measure.cache} - for (var i = 0; i < lineView.rest.length; i++) + for (let i = 0; i < lineView.rest.length; i++) if (lineView.rest[i] == line) return {map: lineView.measure.maps[i], cache: lineView.measure.caches[i]} - for (var i = 0; i < lineView.rest.length; i++) + for (let i = 0; i < lineView.rest.length; i++) if (lineNo(lineView.rest[i]) > lineN) return {map: lineView.measure.maps[i], cache: lineView.measure.caches[i], before: true} } @@ -73,10 +73,10 @@ export function mapFromLineView(lineView, line, lineN) { // when measurement is needed for a line that's not in the viewport. function updateExternalMeasurement(cm, line) { line = visualLine(line) - var lineN = lineNo(line) - var view = cm.display.externalMeasured = new LineView(cm.doc, line, lineN) + let lineN = lineNo(line) + let view = cm.display.externalMeasured = new LineView(cm.doc, line, lineN) view.lineN = lineN - var built = view.built = buildLineContent(cm, view) + let built = view.built = buildLineContent(cm, view) view.text = built.pre removeChildrenAndAdd(cm.display.lineMeasure, built.pre) return view @@ -92,7 +92,7 @@ export function measureChar(cm, line, ch, bias) { export function findViewForLine(cm, lineN) { if (lineN >= cm.display.viewFrom && lineN < cm.display.viewTo) return cm.display.view[findViewIndex(cm, lineN)] - var ext = cm.display.externalMeasured + let ext = cm.display.externalMeasured if (ext && lineN >= ext.lineN && lineN < ext.lineN + ext.size) return ext } @@ -103,8 +103,8 @@ export function findViewForLine(cm, lineN) { // measurements in a row, can thus ensure that the set-up work is // only done once. function prepareMeasureForLine(cm, line) { - var lineN = lineNo(line) - var view = findViewForLine(cm, lineN) + let lineN = lineNo(line) + let view = findViewForLine(cm, lineN) if (view && !view.text) { view = null } else if (view && view.changes) { @@ -114,7 +114,7 @@ function prepareMeasureForLine(cm, line) { if (!view) view = updateExternalMeasurement(cm, line) - var info = mapFromLineView(view, line, lineN) + let info = mapFromLineView(view, line, lineN) return { line: line, view: view, rect: null, map: info.map, cache: info.cache, before: info.before, @@ -126,7 +126,7 @@ function prepareMeasureForLine(cm, line) { // actual character (or fetches it from the cache). function measureCharPrepared(cm, prepared, ch, bias, varHeight) { if (prepared.before) ch = -1 - var key = ch + (bias || ""), found + let key = ch + (bias || ""), found if (prepared.cache.hasOwnProperty(key)) { found = prepared.cache[key] } else { @@ -144,14 +144,15 @@ function measureCharPrepared(cm, prepared, ch, bias, varHeight) { bottom: varHeight ? found.rbottom : found.bottom} } -var nullRect = {left: 0, right: 0, top: 0, bottom: 0} +let nullRect = {left: 0, right: 0, top: 0, bottom: 0} export function nodeAndOffsetInLineMap(map, ch, bias) { - var node, start, end, collapse + let node, start, end, collapse, mStart, mEnd // First, search the line map for the text node corresponding to, // or closest to, the target character. - for (var i = 0; i < map.length; i += 3) { - var mStart = map[i], mEnd = map[i + 1] + for (let i = 0; i < map.length; i += 3) { + mStart = map[i] + mEnd = map[i + 1] if (ch < mStart) { start = 0; end = 1 collapse = "left" @@ -184,22 +185,22 @@ export function nodeAndOffsetInLineMap(map, ch, bias) { } function getUsefulRect(rects, bias) { - var rect = nullRect - if (bias == "left") for (var i = 0; i < rects.length; i++) { + let rect = nullRect + if (bias == "left") for (let i = 0; i < rects.length; i++) { if ((rect = rects[i]).left != rect.right) break - } else for (var i = rects.length - 1; i >= 0; i--) { + } else for (let i = rects.length - 1; i >= 0; i--) { if ((rect = rects[i]).left != rect.right) break } return rect } function measureCharInner(cm, prepared, ch, bias) { - var place = nodeAndOffsetInLineMap(prepared.map, ch, bias) - var node = place.node, start = place.start, end = place.end, collapse = place.collapse + let place = nodeAndOffsetInLineMap(prepared.map, ch, bias) + let node = place.node, start = place.start, end = place.end, collapse = place.collapse - var rect + let rect if (node.nodeType == 3) { // If it is a text node, use a range to retrieve the coordinates. - for (var i = 0; i < 4; i++) { // Retry a maximum of 4 times when nonsense rectangles are returned + for (let i = 0; i < 4; i++) { // Retry a maximum of 4 times when nonsense rectangles are returned while (start && isExtendingChar(prepared.line.text.charAt(place.coverStart + start))) --start while (place.coverStart + end < place.coverEnd && isExtendingChar(prepared.line.text.charAt(place.coverStart + end))) ++end if (ie && ie_version < 9 && start == 0 && end == place.coverEnd - place.coverStart) @@ -214,27 +215,28 @@ function measureCharInner(cm, prepared, ch, bias) { 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 + let rects if (cm.options.lineWrapping && (rects = node.getClientRects()).length > 1) rect = rects[bias == "right" ? rects.length - 1 : 0] else rect = node.getBoundingClientRect() } if (ie && ie_version < 9 && !start && (!rect || !rect.left && !rect.right)) { - var rSpan = node.parentNode.getClientRects()[0] + let rSpan = node.parentNode.getClientRects()[0] if (rSpan) rect = {left: rSpan.left, right: rSpan.left + charWidth(cm.display), top: rSpan.top, bottom: rSpan.bottom} else rect = nullRect } - var rtop = rect.top - prepared.rect.top, rbot = rect.bottom - prepared.rect.top - var mid = (rtop + rbot) / 2 - var heights = prepared.view.measure.heights - for (var i = 0; i < heights.length - 1; i++) + let rtop = rect.top - prepared.rect.top, rbot = rect.bottom - prepared.rect.top + let mid = (rtop + rbot) / 2 + let heights = prepared.view.measure.heights + let i = 0 + for (; i < heights.length - 1; i++) if (mid < heights[i]) break - var top = i ? heights[i - 1] : 0, bot = heights[i] - var result = {left: (collapse == "right" ? rect.right : rect.left) - prepared.rect.left, + let top = i ? heights[i - 1] : 0, bot = heights[i] + let result = {left: (collapse == "right" ? rect.right : rect.left) - prepared.rect.left, right: (collapse == "left" ? rect.left : rect.right) - prepared.rect.left, top: top, bottom: bot} if (!rect.left && !rect.right) result.bogus = true @@ -249,8 +251,8 @@ 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 + let scaleX = screen.logicalXDPI / screen.deviceXDPI + let scaleY = screen.logicalYDPI / screen.deviceYDPI return {left: rect.left * scaleX, right: rect.right * scaleX, top: rect.top * scaleY, bottom: rect.bottom * scaleY} } @@ -259,7 +261,7 @@ export function clearLineMeasurementCacheFor(lineView) { if (lineView.measure) { lineView.measure.cache = {} lineView.measure.heights = null - if (lineView.rest) for (var i = 0; i < lineView.rest.length; i++) + if (lineView.rest) for (let i = 0; i < lineView.rest.length; i++) lineView.measure.caches[i] = {} } } @@ -267,7 +269,7 @@ export function clearLineMeasurementCacheFor(lineView) { export function clearLineMeasurementCache(cm) { cm.display.externalMeasure = null removeChildren(cm.display.lineMeasure) - for (var i = 0; i < cm.display.view.length; i++) + for (let i = 0; i < cm.display.view.length; i++) clearLineMeasurementCacheFor(cm.display.view[i]) } @@ -286,19 +288,19 @@ function pageScrollY() { return window.pageYOffset || (document.documentElement // "line", "div" (display.lineDiv), "local"./null (editor), "window", // or "page". export 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 = widgetHeight(lineObj.widgets[i]) + if (lineObj.widgets) for (let i = 0; i < lineObj.widgets.length; ++i) if (lineObj.widgets[i].above) { + let size = widgetHeight(lineObj.widgets[i]) rect.top += size; rect.bottom += size } if (context == "line") return rect if (!context) context = "local" - var yOff = heightAtLine(lineObj) + let yOff = heightAtLine(lineObj) if (context == "local") yOff += paddingTop(cm.display) else yOff -= cm.display.viewOffset if (context == "page" || context == "window") { - var lOff = cm.display.lineSpace.getBoundingClientRect() + let lOff = cm.display.lineSpace.getBoundingClientRect() yOff += lOff.top + (context == "window" ? 0 : pageScrollY()) - var xOff = lOff.left + (context == "window" ? 0 : pageScrollX()) + let xOff = lOff.left + (context == "window" ? 0 : pageScrollX()) rect.left += xOff; rect.right += xOff } rect.top += yOff; rect.bottom += yOff @@ -309,18 +311,18 @@ export function intoCoordSystem(cm, lineObj, rect, context) { // Context may be "window", "page", "div", or "local"./null. export function fromCoordSystem(cm, coords, context) { if (context == "div") return coords - var left = coords.left, top = coords.top + let left = coords.left, top = coords.top // First move into "page" coordinate system if (context == "page") { left -= pageScrollX() top -= pageScrollY() } else if (context == "local" || !context) { - var localBox = cm.display.sizer.getBoundingClientRect() + let localBox = cm.display.sizer.getBoundingClientRect() left += localBox.left top += localBox.top } - var lineSpaceBox = cm.display.lineSpace.getBoundingClientRect() + let lineSpaceBox = cm.display.lineSpace.getBoundingClientRect() return {left: left - lineSpaceBox.left, top: top - lineSpaceBox.top} } @@ -336,12 +338,12 @@ export function cursorCoords(cm, pos, context, lineObj, preparedMeasure, varHeig lineObj = lineObj || getLine(cm.doc, pos.line) if (!preparedMeasure) preparedMeasure = prepareMeasureForLine(cm, lineObj) function get(ch, right) { - var m = measureCharPrepared(cm, preparedMeasure, ch, right ? "right" : "left", varHeight) + let m = measureCharPrepared(cm, preparedMeasure, ch, right ? "right" : "left", varHeight) if (right) m.left = m.right; else m.right = m.left return intoCoordSystem(cm, lineObj, m, context) } function getBidi(ch, partPos) { - var part = order[partPos], right = part.level % 2 + let part = order[partPos], right = part.level % 2 if (ch == bidiLeft(part) && partPos && part.level < order[partPos - 1].level) { part = order[--partPos] ch = bidiRight(part) - (part.level % 2 ? 0 : 1) @@ -354,10 +356,10 @@ export function cursorCoords(cm, pos, context, lineObj, preparedMeasure, varHeig if (right && ch == part.to && ch > part.from) return get(ch - 1) return get(ch, right) } - var order = getOrder(lineObj), ch = pos.ch + let order = getOrder(lineObj), ch = pos.ch if (!order) return get(ch) - var partPos = getBidiPartAt(order, ch) - var val = getBidi(ch, partPos) + let partPos = getBidiPartAt(order, ch) + let val = getBidi(ch, partPos) if (bidiOther != null) val.other = getBidi(ch, bidiOther) return val } @@ -365,10 +367,11 @@ export function cursorCoords(cm, pos, context, lineObj, preparedMeasure, varHeig // Used to cheaply estimate the coordinates for a position. Used for // intermediate scroll updates. export function estimateCoords(cm, pos) { - var left = 0, pos = clipPos(cm.doc, pos) + let left = 0 + pos = clipPos(cm.doc, pos) if (!cm.options.lineWrapping) left = charWidth(cm.display) * pos.ch - var lineObj = getLine(cm.doc, pos.line) - var top = heightAtLine(lineObj) + paddingTop(cm.display) + let lineObj = getLine(cm.doc, pos.line) + let top = heightAtLine(lineObj) + paddingTop(cm.display) return {left: left, right: left, top: top, bottom: top + lineObj.height} } @@ -379,7 +382,7 @@ export function estimateCoords(cm, pos) { // is true, that means the coordinates lie outside the line's // vertical range. function PosWithInfo(line, ch, outside, xRel) { - var pos = Pos(line, ch) + let pos = Pos(line, ch) pos.xRel = xRel if (outside) pos.outside = true return pos @@ -388,19 +391,19 @@ function PosWithInfo(line, ch, outside, xRel) { // Compute the character position closest to the given coordinates. // Input must be lineSpace-local ("div" coordinate system). export function coordsChar(cm, x, y) { - var doc = cm.doc + let doc = cm.doc y += cm.display.viewOffset if (y < 0) return PosWithInfo(doc.first, 0, true, -1) - var lineN = lineAtHeight(doc, y), last = doc.first + doc.size - 1 + let lineN = lineAtHeight(doc, y), last = doc.first + doc.size - 1 if (lineN > last) return PosWithInfo(doc.first + doc.size - 1, getLine(doc, last).text.length, true, 1) if (x < 0) x = 0 - var lineObj = getLine(doc, lineN) + let lineObj = getLine(doc, lineN) for (;;) { - var found = coordsCharInner(cm, lineObj, lineN, x, y) - var merged = collapsedSpanAtEnd(lineObj) - var mergedPos = merged && merged.find(0, true) + let found = coordsCharInner(cm, lineObj, lineN, x, y) + let merged = collapsedSpanAtEnd(lineObj) + let mergedPos = merged && merged.find(0, true) if (merged && (found.ch > mergedPos.from.ch || found.ch == mergedPos.from.ch && found.xRel > 0)) lineN = lineNo(lineObj = mergedPos.to.line) else @@ -409,12 +412,12 @@ export function coordsChar(cm, x, y) { } function coordsCharInner(cm, lineObj, lineNo, x, y) { - var innerOff = y - heightAtLine(lineObj) - var wrongLine = false, adjust = 2 * cm.display.wrapper.clientWidth - var preparedMeasure = prepareMeasureForLine(cm, lineObj) + let innerOff = y - heightAtLine(lineObj) + let wrongLine = false, adjust = 2 * cm.display.wrapper.clientWidth + let preparedMeasure = prepareMeasureForLine(cm, lineObj) function getX(ch) { - var sp = cursorCoords(cm, Pos(lineNo, ch), "line", lineObj, preparedMeasure) + let sp = cursorCoords(cm, Pos(lineNo, ch), "line", lineObj, preparedMeasure) wrongLine = true if (innerOff > sp.bottom) return sp.left - adjust else if (innerOff < sp.top) return sp.left + adjust @@ -422,24 +425,24 @@ function coordsCharInner(cm, lineObj, lineNo, x, y) { return sp.left } - var bidi = getOrder(lineObj), dist = lineObj.text.length - var from = lineLeft(lineObj), to = lineRight(lineObj) - var fromX = getX(from), fromOutside = wrongLine, toX = getX(to), toOutside = wrongLine + let bidi = getOrder(lineObj), dist = lineObj.text.length + let from = lineLeft(lineObj), to = lineRight(lineObj) + let fromX = getX(from), fromOutside = wrongLine, toX = getX(to), toOutside = wrongLine if (x > toX) return PosWithInfo(lineNo, to, toOutside, 1) // Do a binary search between these bounds. for (;;) { if (bidi ? to == from || to == moveVisually(lineObj, from, 1) : to - from <= 1) { - var ch = x < fromX || x - fromX <= toX - x ? from : to - var outside = ch == from ? fromOutside : toOutside - var xDiff = x - (ch == from ? fromX : toX) + let ch = x < fromX || x - fromX <= toX - x ? from : to + let outside = ch == from ? fromOutside : toOutside + let xDiff = x - (ch == from ? fromX : toX) // This is a kludge to handle the case where the coordinates // are after a line-wrapped line. We should replace it with a // more general handling of cursor positions around line // breaks. (Issue #4078) if (toOutside && !bidi && !/\s/.test(lineObj.text.charAt(ch)) && xDiff > 0 && ch < lineObj.text.length && preparedMeasure.view.measure.heights.length > 1) { - var charSize = measureCharPrepared(cm, preparedMeasure, ch, "right") + let charSize = measureCharPrepared(cm, preparedMeasure, ch, "right") if (innerOff <= charSize.bottom && innerOff >= charSize.top && Math.abs(x - charSize.right) < xDiff) { outside = false ch++ @@ -447,21 +450,21 @@ function coordsCharInner(cm, lineObj, lineNo, x, y) { } } while (isExtendingChar(lineObj.text.charAt(ch))) ++ch - var pos = PosWithInfo(lineNo, ch, outside, xDiff < -1 ? -1 : xDiff > 1 ? 1 : 0) + let pos = PosWithInfo(lineNo, ch, outside, xDiff < -1 ? -1 : xDiff > 1 ? 1 : 0) return pos } - var step = Math.ceil(dist / 2), middle = from + step + let step = Math.ceil(dist / 2), middle = from + step if (bidi) { middle = from - for (var i = 0; i < step; ++i) middle = moveVisually(lineObj, middle, 1) + for (let i = 0; i < step; ++i) middle = moveVisually(lineObj, middle, 1) } - var middleX = getX(middle) + let 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} } } -var measureText +let measureText // Compute the default text height. export function textHeight(display) { if (display.cachedTextHeight != null) return display.cachedTextHeight @@ -469,14 +472,14 @@ export function textHeight(display) { measureText = elt("pre") // Measure a bunch of lines, for browsers that compute // fractional heights. - for (var i = 0; i < 49; ++i) { + for (let 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 + let height = measureText.offsetHeight / 50 if (height > 3) display.cachedTextHeight = height removeChildren(display.measure) return height || 1 @@ -485,10 +488,10 @@ export function textHeight(display) { // Compute the default character width. export function charWidth(display) { if (display.cachedCharWidth != null) return display.cachedCharWidth - var anchor = elt("span", "xxxxxxxxxx") - var pre = elt("pre", [anchor]) + let anchor = elt("span", "xxxxxxxxxx") + let pre = elt("pre", [anchor]) removeChildrenAndAdd(display.measure, pre) - var rect = anchor.getBoundingClientRect(), width = (rect.right - rect.left) / 10 + let rect = anchor.getBoundingClientRect(), width = (rect.right - rect.left) / 10 if (width > 2) display.cachedCharWidth = width return width || 10 } @@ -496,9 +499,9 @@ export function charWidth(display) { // Do a bulk-read of the DOM positions and sizes needed to draw the // view, so that we don't interleave reading and writing to the DOM. export function getDimensions(cm) { - var d = cm.display, left = {}, width = {} - var gutterLeft = d.gutters.clientLeft - for (var n = d.gutters.firstChild, i = 0; n; n = n.nextSibling, ++i) { + let d = cm.display, left = {}, width = {} + let gutterLeft = d.gutters.clientLeft + for (let n = d.gutters.firstChild, i = 0; n; n = n.nextSibling, ++i) { left[cm.options.gutters[i]] = n.offsetLeft + n.clientLeft + gutterLeft width[cm.options.gutters[i]] = n.clientWidth } @@ -520,13 +523,13 @@ export function compensateForHScroll(display) { // first approximation until the line becomes visible (and is thus // properly measurable). export 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) + let th = textHeight(cm.display), wrapping = cm.options.lineWrapping + let perLine = wrapping && Math.max(5, cm.display.scroller.clientWidth / charWidth(cm.display) - 3) return function(line) { if (lineIsHidden(cm.doc, line)) return 0 - var widgetsHeight = 0 - if (line.widgets) for (var i = 0; i < line.widgets.length; i++) { + let widgetsHeight = 0 + if (line.widgets) for (let i = 0; i < line.widgets.length; i++) { if (line.widgets[i].height) widgetsHeight += line.widgets[i].height } @@ -538,9 +541,9 @@ export function estimateHeight(cm) { } export function estimateLineHeights(cm) { - var doc = cm.doc, est = estimateHeight(cm) + let doc = cm.doc, est = estimateHeight(cm) doc.iter(function(line) { - var estHeight = est(line) + let estHeight = est(line) if (estHeight != line.height) updateLineHeight(line, estHeight) }) } @@ -551,16 +554,16 @@ export function estimateLineHeights(cm) { // selections, and tries to estimate a character position even for // coordinates beyond the right of the text. export function posFromMouse(cm, e, liberal, forRect) { - var display = cm.display + let display = cm.display if (!liberal && e_target(e).getAttribute("cm-not-content") == "true") return null - var x, y, space = display.lineSpace.getBoundingClientRect() + let x, y, space = display.lineSpace.getBoundingClientRect() // Fails unpredictably on IE[67] when mouse is dragged around quickly. try { x = e.clientX - space.left; y = e.clientY - space.top } catch (e) { return null } - var coords = coordsChar(cm, x, y), line + let coords = coordsChar(cm, x, y), line if (forRect && coords.xRel == 1 && (line = getLine(cm.doc, coords.line).text).length == coords.ch) { - var colDiff = countColumn(line, line.length, cm.options.tabSize) - line.length + let colDiff = countColumn(line, line.length, cm.options.tabSize) - line.length coords = Pos(coords.line, Math.max(0, Math.round((x - paddingH(cm.display).left) / charWidth(cm.display)) - colDiff)) } return coords @@ -572,8 +575,8 @@ export function findViewIndex(cm, n) { if (n >= cm.display.viewTo) return null n -= cm.display.viewFrom if (n < 0) return null - var view = cm.display.view - for (var i = 0; i < view.length; i++) { + let view = cm.display.view + for (let i = 0; i < view.length; i++) { n -= view[i].size if (n < 0) return i } diff --git a/src/measurement/update_line.js b/src/measurement/update_line.js index be94b009..4a80a766 100644 --- a/src/measurement/update_line.js +++ b/src/measurement/update_line.js @@ -8,8 +8,8 @@ import { signalLater } from "../util/operation_group" // lineView.changes. This updates the relevant part of the line's // DOM structure. export function updateLineForChanges(cm, lineView, lineN, dims) { - for (var j = 0; j < lineView.changes.length; j++) { - var type = lineView.changes[j] + for (let j = 0; j < lineView.changes.length; j++) { + let type = lineView.changes[j] if (type == "text") updateLineText(cm, lineView) else if (type == "gutter") updateLineGutter(cm, lineView, lineN, dims) else if (type == "class") updateLineClasses(lineView) @@ -32,13 +32,13 @@ function ensureLineWrapped(lineView) { } function updateLineBackground(lineView) { - var cls = lineView.bgClass ? lineView.bgClass + " " + (lineView.line.bgClass || "") : lineView.line.bgClass + let cls = lineView.bgClass ? lineView.bgClass + " " + (lineView.line.bgClass || "") : lineView.line.bgClass if (cls) cls += " CodeMirror-linebackground" if (lineView.background) { if (cls) lineView.background.className = cls else { lineView.background.parentNode.removeChild(lineView.background); lineView.background = null } } else if (cls) { - var wrap = ensureLineWrapped(lineView) + let wrap = ensureLineWrapped(lineView) lineView.background = wrap.insertBefore(elt("div", null, cls), wrap.firstChild) } } @@ -46,7 +46,7 @@ function updateLineBackground(lineView) { // Wrapper around buildLineContent which will reuse the structure // in display.externalMeasured when possible. function getLineContent(cm, lineView) { - var ext = cm.display.externalMeasured + let ext = cm.display.externalMeasured if (ext && ext.line == lineView.line) { cm.display.externalMeasured = null lineView.measure = ext.measure @@ -59,8 +59,8 @@ function getLineContent(cm, lineView) { // classes because the mode may output tokens that influence these // classes. function updateLineText(cm, lineView) { - var cls = lineView.text.className - var built = getLineContent(cm, lineView) + let cls = lineView.text.className + let built = getLineContent(cm, lineView) if (lineView.text == lineView.node) lineView.node = built.pre lineView.text.parentNode.replaceChild(built.pre, lineView.text) lineView.text = built.pre @@ -79,7 +79,7 @@ function updateLineClasses(lineView) { ensureLineWrapped(lineView).className = lineView.line.wrapClass else if (lineView.node != lineView.text) lineView.node.className = "" - var textClass = lineView.textClass ? lineView.textClass + " " + (lineView.line.textClass || "") : lineView.line.textClass + let textClass = lineView.textClass ? lineView.textClass + " " + (lineView.line.textClass || "") : lineView.line.textClass lineView.text.className = textClass || "" } @@ -93,16 +93,16 @@ function updateLineGutter(cm, lineView, lineN, dims) { lineView.gutterBackground = null } if (lineView.line.gutterClass) { - var wrap = ensureLineWrapped(lineView) + let wrap = ensureLineWrapped(lineView) lineView.gutterBackground = elt("div", null, "CodeMirror-gutter-background " + lineView.line.gutterClass, "left: " + (cm.options.fixedGutter ? dims.fixedPos : -dims.gutterTotalWidth) + "px; width: " + dims.gutterTotalWidth + "px") wrap.insertBefore(lineView.gutterBackground, lineView.text) } - var markers = lineView.line.gutterMarkers + let markers = lineView.line.gutterMarkers if (cm.options.lineNumbers || markers) { - var wrap = ensureLineWrapped(lineView) - var gutterWrap = lineView.gutter = elt("div", null, "CodeMirror-gutter-wrapper", "left: " + + let wrap = ensureLineWrapped(lineView) + let gutterWrap = lineView.gutter = elt("div", null, "CodeMirror-gutter-wrapper", "left: " + (cm.options.fixedGutter ? dims.fixedPos : -dims.gutterTotalWidth) + "px") cm.display.input.setUneditable(gutterWrap) wrap.insertBefore(gutterWrap, lineView.text) @@ -114,8 +114,8 @@ function updateLineGutter(cm, lineView, lineN, dims) { "CodeMirror-linenumber CodeMirror-gutter-elt", "left: " + dims.gutterLeft["CodeMirror-linenumbers"] + "px; width: " + cm.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 (markers) for (let k = 0; k < cm.options.gutters.length; ++k) { + let id = cm.options.gutters[k], found = markers.hasOwnProperty(id) && markers[id] if (found) gutterWrap.appendChild(elt("div", [found], "CodeMirror-gutter-elt", "left: " + dims.gutterLeft[id] + "px; width: " + dims.gutterWidth[id] + "px")) @@ -125,8 +125,8 @@ function updateLineGutter(cm, lineView, lineN, dims) { function updateLineWidgets(cm, lineView, dims) { if (lineView.alignable) lineView.alignable = null - for (var node = lineView.node.firstChild, next; node; node = next) { - var next = node.nextSibling + for (let node = lineView.node.firstChild, next; node; node = next) { + next = node.nextSibling if (node.className == "CodeMirror-linewidget") lineView.node.removeChild(node) } @@ -135,7 +135,7 @@ function updateLineWidgets(cm, lineView, dims) { // Build a line's DOM representation from scratch export function buildLineElement(cm, lineView, lineN, dims) { - var built = getLineContent(cm, lineView) + let built = getLineContent(cm, lineView) lineView.text = lineView.node = built.pre if (built.bgClass) lineView.bgClass = built.bgClass if (built.textClass) lineView.textClass = built.textClass @@ -150,15 +150,15 @@ export function buildLineElement(cm, lineView, lineN, dims) { // collapsed spans). The widgets for all of them need to be drawn. function insertLineWidgets(cm, lineView, dims) { insertLineWidgetsFor(cm, lineView.line, lineView, dims, true) - if (lineView.rest) for (var i = 0; i < lineView.rest.length; i++) + if (lineView.rest) for (let i = 0; i < lineView.rest.length; i++) insertLineWidgetsFor(cm, lineView.rest[i], lineView, dims, false) } function insertLineWidgetsFor(cm, line, lineView, dims, allowAbove) { if (!line.widgets) return - var wrap = ensureLineWrapped(lineView) - for (var i = 0, ws = line.widgets; i < ws.length; ++i) { - var widget = ws[i], node = elt("div", [widget.node], "CodeMirror-linewidget") + let wrap = ensureLineWrapped(lineView) + for (let i = 0, ws = line.widgets; i < ws.length; ++i) { + let widget = ws[i], node = elt("div", [widget.node], "CodeMirror-linewidget") if (!widget.handleMouseEvents) node.setAttribute("cm-ignore-events", "true") positionLineWidget(widget, node, lineView, dims) cm.display.input.setUneditable(node) @@ -173,7 +173,7 @@ function insertLineWidgetsFor(cm, line, lineView, dims, allowAbove) { function positionLineWidget(widget, node, lineView, dims) { if (widget.noHScroll) { ;(lineView.alignable || (lineView.alignable = [])).push(node) - var width = dims.wrapperWidth + let width = dims.wrapperWidth node.style.left = dims.fixedPos + "px" if (!widget.coverGutter) { width -= dims.gutterTotalWidth diff --git a/src/measurement/widgets.js b/src/measurement/widgets.js index f20d313e..554cf809 100644 --- a/src/measurement/widgets.js +++ b/src/measurement/widgets.js @@ -3,10 +3,10 @@ import { e_target } from "../util/event" export function widgetHeight(widget) { if (widget.height != null) return widget.height - var cm = widget.doc.cm + let cm = widget.doc.cm if (!cm) return 0 if (!contains(document.body, widget.node)) { - var parentStyle = "position: relative;" + let parentStyle = "position: relative;" if (widget.coverGutter) parentStyle += "margin-left: -" + cm.display.gutters.offsetWidth + "px;" if (widget.noHScroll) @@ -18,7 +18,7 @@ export function widgetHeight(widget) { // Return true when the given mouse event happened in a widget export function eventInWidget(display, e) { - for (var n = e_target(e); n != display.wrapper; n = n.parentNode) { + for (let n = e_target(e); n != display.wrapper; n = n.parentNode) { if (!n || (n.nodeType == 1 && n.getAttribute("cm-ignore-events") == "true") || (n.parentNode == display.sizer && n != display.mover)) return true diff --git a/src/model/Doc.js b/src/model/Doc.js index ba004631..c02fe062 100644 --- a/src/model/Doc.js +++ b/src/model/Doc.js @@ -19,8 +19,8 @@ import { copySharedMarkers, detachSharedMarkers, findSharedMarkers, markText } f import { normalizeSelection, Range, simpleSelection } from "./selection" import { extendSelection, extendSelections, setSelection, setSelectionReplaceHistory, setSimpleSelection } from "./selection_updates" -var nextDocId = 0 -var Doc = function(text, mode, firstLine, lineSep) { +let nextDocId = 0 +let Doc = function(text, mode, firstLine, lineSep) { if (!(this instanceof Doc)) return new Doc(text, mode, firstLine, lineSep) if (firstLine == null) firstLine = 0 @@ -30,7 +30,7 @@ var Doc = function(text, mode, firstLine, lineSep) { this.cantEdit = false this.cleanGeneration = 1 this.frontier = firstLine - var start = Pos(firstLine, 0) + let start = Pos(firstLine, 0) this.sel = simpleSelection(start) this.history = new History(null) this.id = ++nextDocId @@ -56,8 +56,8 @@ Doc.prototype = createObj(BranchChunk.prototype, { // Non-public interface for adding and removing lines. insert: function(at, lines) { - var height = 0 - for (var i = 0; i < lines.length; ++i) height += lines[i].height + let height = 0 + for (let i = 0; i < lines.length; ++i) height += lines[i].height this.insertInner(at - this.first, lines, height) }, remove: function(at, n) { this.removeInner(at - this.first, n) }, @@ -66,12 +66,12 @@ Doc.prototype = createObj(BranchChunk.prototype, { // are also available from CodeMirror (editor) instances. getValue: function(lineSep) { - var lines = getLines(this, this.first, this.first + this.size) + let lines = getLines(this, this.first, this.first + this.size) if (lineSep === false) return lines return lines.join(lineSep || this.lineSeparator()) }, setValue: docMethodOp(function(code) { - var top = Pos(this.first, 0), last = this.first + this.size - 1 + let top = Pos(this.first, 0), last = this.first + this.size - 1 makeChange(this, {from: top, to: Pos(last, getLine(this, last).text.length), text: this.splitLines(code), origin: "setValue", full: true}, true) setSelection(this, simpleSelection(top)) @@ -82,12 +82,12 @@ Doc.prototype = createObj(BranchChunk.prototype, { replaceRange(this, code, from, to, origin) }, getRange: function(from, to, lineSep) { - var lines = getBetween(this, clipPos(this, from), clipPos(this, to)) + let lines = getBetween(this, clipPos(this, from), clipPos(this, to)) if (lineSep === false) return lines return lines.join(lineSep || this.lineSeparator()) }, - getLine: function(line) {var l = this.getLineHandle(line); return l && l.text}, + getLine: function(line) {let l = this.getLineHandle(line); return l && l.text}, getLineHandle: function(line) {if (isLine(this, line)) return getLine(this, line)}, getLineNumber: function(line) {return lineNo(line)}, @@ -104,7 +104,7 @@ Doc.prototype = createObj(BranchChunk.prototype, { clipPos: function(pos) {return clipPos(this, pos)}, getCursor: function(start) { - var range = this.sel.primary(), pos + let range = this.sel.primary(), pos if (start == null || start == "head") pos = range.head else if (start == "anchor") pos = range.anchor else if (start == "end" || start == "to" || start === false) pos = range.to() @@ -127,55 +127,56 @@ Doc.prototype = createObj(BranchChunk.prototype, { extendSelections(this, clipPosArray(this, heads), options) }), extendSelectionsBy: docMethodOp(function(f, options) { - var heads = map(this.sel.ranges, f) + let heads = map(this.sel.ranges, f) extendSelections(this, clipPosArray(this, heads), options) }), setSelections: docMethodOp(function(ranges, primary, options) { if (!ranges.length) return - for (var i = 0, out = []; i < ranges.length; i++) + let out = [] + for (let i = 0; i < ranges.length; i++) out[i] = new Range(clipPos(this, ranges[i].anchor), clipPos(this, ranges[i].head)) if (primary == null) primary = Math.min(ranges.length - 1, this.sel.primIndex) setSelection(this, normalizeSelection(out, primary), options) }), addSelection: docMethodOp(function(anchor, head, options) { - var ranges = this.sel.ranges.slice(0) + let ranges = this.sel.ranges.slice(0) ranges.push(new Range(clipPos(this, anchor), clipPos(this, head || anchor))) setSelection(this, normalizeSelection(ranges, ranges.length - 1), options) }), getSelection: function(lineSep) { - var ranges = this.sel.ranges, lines - for (var i = 0; i < ranges.length; i++) { - var sel = getBetween(this, ranges[i].from(), ranges[i].to()) + let ranges = this.sel.ranges, lines + for (let i = 0; i < ranges.length; i++) { + let sel = getBetween(this, ranges[i].from(), ranges[i].to()) lines = lines ? lines.concat(sel) : sel } if (lineSep === false) return lines else return lines.join(lineSep || this.lineSeparator()) }, getSelections: function(lineSep) { - var parts = [], ranges = this.sel.ranges - for (var i = 0; i < ranges.length; i++) { - var sel = getBetween(this, ranges[i].from(), ranges[i].to()) + let parts = [], ranges = this.sel.ranges + for (let i = 0; i < ranges.length; i++) { + let sel = getBetween(this, ranges[i].from(), ranges[i].to()) if (lineSep !== false) sel = sel.join(lineSep || this.lineSeparator()) parts[i] = sel } return parts }, replaceSelection: function(code, collapse, origin) { - var dup = [] - for (var i = 0; i < this.sel.ranges.length; i++) + let dup = [] + for (let i = 0; i < this.sel.ranges.length; i++) dup[i] = code this.replaceSelections(dup, collapse, origin || "+input") }, replaceSelections: docMethodOp(function(code, collapse, origin) { - var changes = [], sel = this.sel - for (var i = 0; i < sel.ranges.length; i++) { - var range = sel.ranges[i] + let changes = [], sel = this.sel + for (let i = 0; i < sel.ranges.length; i++) { + let range = sel.ranges[i] changes[i] = {from: range.from(), to: range.to(), text: this.splitLines(code[i]), origin: origin} } - var newSel = collapse && collapse != "end" && computeReplacedSel(this, changes, collapse) - for (var i = changes.length - 1; i >= 0; i--) + let newSel = collapse && collapse != "end" && computeReplacedSel(this, changes, collapse) + for (let i = changes.length - 1; i >= 0; i--) makeChange(this, changes[i]) if (newSel) setSelectionReplaceHistory(this, newSel) else if (this.cm) ensureCursorVisible(this.cm) @@ -189,9 +190,9 @@ Doc.prototype = createObj(BranchChunk.prototype, { getExtending: function() {return this.extend}, historySize: function() { - var hist = this.history, done = 0, undone = 0 - for (var i = 0; i < hist.done.length; i++) if (!hist.done[i].ranges) ++done - for (var i = 0; i < hist.undone.length; i++) if (!hist.undone[i].ranges) ++undone + let hist = this.history, done = 0, undone = 0 + for (let i = 0; i < hist.done.length; i++) if (!hist.done[i].ranges) ++done + for (let i = 0; i < hist.undone.length; i++) if (!hist.undone[i].ranges) ++undone return {undo: done, redo: undone} }, clearHistory: function() {this.history = new History(this.history.maxGeneration)}, @@ -213,14 +214,14 @@ Doc.prototype = createObj(BranchChunk.prototype, { undone: copyHistoryArray(this.history.undone)} }, setHistory: function(histData) { - var hist = this.history = new History(this.history.maxGeneration) + let hist = this.history = new History(this.history.maxGeneration) hist.done = copyHistoryArray(histData.done.slice(0), null, true) hist.undone = copyHistoryArray(histData.undone.slice(0), null, true) }, addLineClass: docMethodOp(function(handle, where, cls) { return changeLine(this, handle, where == "gutter" ? "gutter" : "class", function(line) { - var prop = where == "text" ? "textClass" + let prop = where == "text" ? "textClass" : where == "background" ? "bgClass" : where == "gutter" ? "gutterClass" : "wrapClass" if (!line[prop]) line[prop] = cls @@ -231,16 +232,16 @@ Doc.prototype = createObj(BranchChunk.prototype, { }), removeLineClass: docMethodOp(function(handle, where, cls) { return changeLine(this, handle, where == "gutter" ? "gutter" : "class", function(line) { - var prop = where == "text" ? "textClass" + let prop = where == "text" ? "textClass" : where == "background" ? "bgClass" : where == "gutter" ? "gutterClass" : "wrapClass" - var cur = line[prop] + let cur = line[prop] if (!cur) return false else if (cls == null) line[prop] = null else { - var found = cur.match(classTest(cls)) + let found = cur.match(classTest(cls)) if (!found) return false - var end = found.index + found[0].length + let end = found.index + found[0].length line[prop] = cur.slice(0, found.index) + (!found.index || end == cur.length ? "" : " ") + cur.slice(end) || null } return true @@ -256,7 +257,7 @@ Doc.prototype = createObj(BranchChunk.prototype, { return markText(this, clipPos(this, from), clipPos(this, to), options, options && options.type || "range") }, setBookmark: function(pos, options) { - var realOpts = {replacedWith: options && (options.nodeType == null ? options.widget : options), + let realOpts = {replacedWith: options && (options.nodeType == null ? options.widget : options), insertLeft: options && options.insertLeft, clearWhenEmpty: false, shared: options && options.shared, handleMouseEvents: options && options.handleMouseEvents} @@ -265,9 +266,9 @@ Doc.prototype = createObj(BranchChunk.prototype, { }, 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] + let markers = [], spans = getLine(this, pos.line).markedSpans + if (spans) for (let i = 0; i < spans.length; ++i) { + let span = spans[i] if ((span.from == null || span.from <= pos.ch) && (span.to == null || span.to >= pos.ch)) markers.push(span.marker.parent || span.marker) @@ -276,11 +277,11 @@ Doc.prototype = createObj(BranchChunk.prototype, { }, findMarks: function(from, to, filter) { from = clipPos(this, from); to = clipPos(this, to) - var found = [], lineNo = from.line + let found = [], lineNo = from.line this.iter(from.line, to.line + 1, function(line) { - var spans = line.markedSpans - if (spans) for (var i = 0; i < spans.length; i++) { - var span = spans[i] + let spans = line.markedSpans + if (spans) for (let i = 0; i < spans.length; i++) { + let span = spans[i] 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) && @@ -292,19 +293,19 @@ Doc.prototype = createObj(BranchChunk.prototype, { return found }, getAllMarks: function() { - var markers = [] + let markers = [] this.iter(function(line) { - var sps = line.markedSpans - if (sps) for (var i = 0; i < sps.length; ++i) + let sps = line.markedSpans + if (sps) for (let 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, sepSize = this.lineSeparator().length + let ch, lineNo = this.first, sepSize = this.lineSeparator().length this.iter(function(line) { - var sz = line.text.length + sepSize + let sz = line.text.length + sepSize if (sz > off) { ch = off; return true } off -= sz ++lineNo @@ -313,9 +314,9 @@ Doc.prototype = createObj(BranchChunk.prototype, { }, indexFromPos: function (coords) { coords = clipPos(this, coords) - var index = coords.ch + let index = coords.ch if (coords.line < this.first || coords.ch < 0) return 0 - var sepSize = this.lineSeparator().length + let sepSize = this.lineSeparator().length this.iter(this.first, coords.line, function (line) { index += line.text.length + sepSize }) @@ -323,7 +324,7 @@ Doc.prototype = createObj(BranchChunk.prototype, { }, copy: function(copyHistory) { - var doc = new Doc(getLines(this, this.first, this.first + this.size), + let doc = new Doc(getLines(this, this.first, this.first + this.size), this.modeOption, this.first, this.lineSep) doc.scrollTop = this.scrollTop; doc.scrollLeft = this.scrollLeft doc.sel = this.sel @@ -337,10 +338,10 @@ Doc.prototype = createObj(BranchChunk.prototype, { linkedDoc: function(options) { if (!options) options = {} - var from = this.first, to = this.first + this.size + let 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, this.lineSep) + let copy = new Doc(getLines(this, from, to), options.mode || this.modeOption, from, this.lineSep) 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}] @@ -349,8 +350,8 @@ Doc.prototype = createObj(BranchChunk.prototype, { }, 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 (this.linked) for (let i = 0; i < this.linked.length; ++i) { + let link = this.linked[i] if (link.doc != other) continue this.linked.splice(i, 1) other.unlinkDoc(this) @@ -359,7 +360,7 @@ Doc.prototype = createObj(BranchChunk.prototype, { } // If the histories were shared, split them again if (other.history == this.history) { - var splitIds = [other.id] + let splitIds = [other.id] linkedDocs(other, function(doc) {splitIds.push(doc.id)}, true) other.history = new History(null) other.history.done = copyHistoryArray(this.history.done, splitIds) diff --git a/src/model/change_measurement.js b/src/model/change_measurement.js index ac14fa54..881f39eb 100644 --- a/src/model/change_measurement.js +++ b/src/model/change_measurement.js @@ -17,15 +17,15 @@ function adjustForChange(pos, change) { if (cmp(pos, change.from) < 0) return pos if (cmp(pos, change.to) <= 0) return changeEnd(change) - var line = pos.line + change.text.length - (change.to.line - change.from.line) - 1, ch = pos.ch + let line = pos.line + change.text.length - (change.to.line - change.from.line) - 1, ch = pos.ch if (pos.line == change.to.line) ch += changeEnd(change).ch - change.to.ch return Pos(line, ch) } export function computeSelAfterChange(doc, change) { - var out = [] - for (var i = 0; i < doc.sel.ranges.length; i++) { - var range = doc.sel.ranges[i] + let out = [] + for (let i = 0; i < doc.sel.ranges.length; i++) { + let range = doc.sel.ranges[i] out.push(new Range(adjustForChange(range.anchor, change), adjustForChange(range.head, change))) } @@ -42,16 +42,16 @@ function offsetPos(pos, old, nw) { // Used by replaceSelections to allow moving the selection to the // start or around the replaced test. Hint may be "start" or "around". export function computeReplacedSel(doc, changes, hint) { - var out = [] - var oldPrev = Pos(doc.first, 0), newPrev = oldPrev - for (var i = 0; i < changes.length; i++) { - var change = changes[i] - var from = offsetPos(change.from, oldPrev, newPrev) - var to = offsetPos(changeEnd(change), oldPrev, newPrev) + let out = [] + let oldPrev = Pos(doc.first, 0), newPrev = oldPrev + for (let i = 0; i < changes.length; i++) { + let change = changes[i] + let from = offsetPos(change.from, oldPrev, newPrev) + let to = offsetPos(changeEnd(change), oldPrev, newPrev) oldPrev = change.to newPrev = to if (hint == "around") { - var range = doc.sel.ranges[i], inv = cmp(range.head, range.anchor) < 0 + let range = doc.sel.ranges[i], inv = cmp(range.head, range.anchor) < 0 out[i] = new Range(inv ? to : from, inv ? from : to) } else { out[i] = new Range(from, from) diff --git a/src/model/changes.js b/src/model/changes.js index 6a8606af..e39bc303 100644 --- a/src/model/changes.js +++ b/src/model/changes.js @@ -20,7 +20,7 @@ import { setSelection, setSelectionNoUndo } from "./selection_updates" // Allow "beforeChange" event handlers to influence a change function filterChange(doc, change, update) { - var obj = { + let obj = { canceled: false, from: change.from, to: change.to, @@ -56,9 +56,9 @@ export function makeChange(doc, change, ignoreReadOnly) { // 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) + let split = sawReadOnlySpans && !ignoreReadOnly && removeReadOnlyRanges(doc, change.from, change.to) if (split) { - for (var i = split.length - 1; i >= 0; --i) + for (let i = split.length - 1; i >= 0; --i) makeChangeInner(doc, {from: split[i].from, to: split[i].to, text: i ? [""] : change.text}) } else { makeChangeInner(doc, change) @@ -67,11 +67,11 @@ export function makeChange(doc, change, ignoreReadOnly) { function makeChangeInner(doc, change) { if (change.text.length == 1 && change.text[0] == "" && cmp(change.from, change.to) == 0) return - var selAfter = computeSelAfterChange(doc, change) + let selAfter = computeSelAfterChange(doc, change) addChangeToHistory(doc, change, selAfter, doc.cm ? doc.cm.curOp.id : NaN) makeChangeSingleDoc(doc, change, selAfter, stretchSpansOverChange(doc, change)) - var rebased = [] + let rebased = [] linkedDocs(doc, function(doc, sharedHist) { if (!sharedHist && indexOf(rebased, doc.history) == -1) { @@ -86,12 +86,13 @@ function makeChangeInner(doc, change) { export function makeChangeFromHistory(doc, type, allowSelectionOnly) { if (doc.cm && doc.cm.state.suppressEdits && !allowSelectionOnly) return - var hist = doc.history, event, selAfter = doc.sel - var source = type == "undo" ? hist.done : hist.undone, dest = type == "undo" ? hist.undone : hist.done + let hist = doc.history, event, selAfter = doc.sel + let source = type == "undo" ? hist.done : hist.undone, dest = type == "undo" ? hist.undone : hist.done // Verify that there is a useable event (so that ctrl-z won't // needlessly clear selection events) - for (var i = 0; i < source.length; i++) { + let i = 0 + for (; i < source.length; i++) { event = source[i] if (allowSelectionOnly ? event.ranges && !event.equals(doc.sel) : !event.ranges) break @@ -114,15 +115,15 @@ export function makeChangeFromHistory(doc, type, allowSelectionOnly) { // Build up a reverse change object to add to the opposite history // stack (redo when undoing, and vice versa). - var antiChanges = [] + let antiChanges = [] pushSelectionToHistory(selAfter, dest) dest.push({changes: antiChanges, generation: hist.generation}) hist.generation = event.generation || ++hist.maxGeneration - var filter = hasHandler(doc, "beforeChange") || doc.cm && hasHandler(doc.cm, "beforeChange") + let 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] + for (let i = event.changes.length - 1; i >= 0; --i) { + let change = event.changes[i] change.origin = type if (filter && !filterChange(doc, change, false)) { source.length = 0 @@ -131,10 +132,10 @@ export function makeChangeFromHistory(doc, type, allowSelectionOnly) { antiChanges.push(historyChangeFromChange(doc, change)) - var after = i ? computeSelAfterChange(doc, change) : lst(source) + let after = i ? computeSelAfterChange(doc, change) : lst(source) makeChangeSingleDoc(doc, change, after, mergeOldSpans(doc, change)) if (!i && doc.cm) doc.cm.scrollIntoView({from: change.from, to: changeEnd(change)}) - var rebased = [] + let rebased = [] // Propagate to the linked documents linkedDocs(doc, function(doc, sharedHist) { @@ -158,7 +159,7 @@ function shiftDoc(doc, distance) { }), doc.sel.primIndex) if (doc.cm) { regChange(doc.cm, doc.first, doc.first - distance, distance) - for (var d = doc.cm.display, l = d.viewFrom; l < d.viewTo; l++) + for (let d = doc.cm.display, l = d.viewFrom; l < d.viewTo; l++) regLineChange(doc.cm, l, "gutter") } } @@ -177,12 +178,12 @@ function makeChangeSingleDoc(doc, change, selAfter, spans) { // 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) + let shift = change.text.length - 1 - (doc.first - change.from.line) shiftDoc(doc, shift) 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() + let last = doc.lastLine() if (change.to.line > last) { change = {from: change.from, to: Pos(last, getLine(doc, last).text.length), text: [change.text[0]], origin: change.origin} @@ -199,9 +200,9 @@ function makeChangeSingleDoc(doc, change, selAfter, spans) { // Handle the interaction of a change to a document with the editor // that this document is part of. function makeChangeSingleDocInEditor(cm, change, spans) { - var doc = cm.doc, display = cm.display, from = change.from, to = change.to + let doc = cm.doc, display = cm.display, from = change.from, to = change.to - var recomputeMaxLength = false, checkWidthStart = from.line + let recomputeMaxLength = false, checkWidthStart = from.line if (!cm.options.lineWrapping) { checkWidthStart = lineNo(visualLine(getLine(doc, from.line))) doc.iter(checkWidthStart, to.line + 1, function(line) { @@ -219,7 +220,7 @@ function makeChangeSingleDocInEditor(cm, change, spans) { if (!cm.options.lineWrapping) { doc.iter(checkWidthStart, from.line + change.text.length, function(line) { - var len = lineLength(line) + let len = lineLength(line) if (len > display.maxLineLength) { display.maxLine = line display.maxLineLength = len @@ -234,7 +235,7 @@ function makeChangeSingleDocInEditor(cm, change, spans) { doc.frontier = Math.min(doc.frontier, from.line) startWorker(cm, 400) - var lendiff = change.text.length - (to.line - from.line) - 1 + let lendiff = change.text.length - (to.line - from.line) - 1 // Remember that these lines changed, for updating the display if (change.full) regChange(cm) @@ -243,9 +244,9 @@ function makeChangeSingleDocInEditor(cm, change, spans) { else regChange(cm, from.line, to.line + 1, lendiff) - var changesHandler = hasHandler(cm, "changes"), changeHandler = hasHandler(cm, "change") + let changesHandler = hasHandler(cm, "changes"), changeHandler = hasHandler(cm, "change") if (changeHandler || changesHandler) { - var obj = { + let obj = { from: from, to: to, text: change.text, removed: change.removed, @@ -259,7 +260,7 @@ function makeChangeSingleDocInEditor(cm, change, spans) { export function replaceRange(doc, code, from, to, origin) { if (!to) to = from - if (cmp(to, from) < 0) { var tmp = to; to = from; from = tmp } + if (cmp(to, from) < 0) { let tmp = to; to = from; from = tmp } if (typeof code == "string") code = doc.splitLines(code) makeChange(doc, {from: from, to: to, text: code, origin: origin}) } @@ -283,18 +284,18 @@ function rebaseHistSelSingle(pos, from, to, diff) { // 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 (let i = 0; i < array.length; ++i) { + let sub = array[i], ok = true if (sub.ranges) { if (!sub.copied) { sub = array[i] = sub.deepCopy(); sub.copied = true } - for (var j = 0; j < sub.ranges.length; j++) { + for (let j = 0; j < sub.ranges.length; j++) { rebaseHistSelSingle(sub.ranges[j].anchor, from, to, diff) rebaseHistSelSingle(sub.ranges[j].head, from, to, diff) } continue } - for (var j = 0; j < sub.changes.length; ++j) { - var cur = sub.changes[j] + for (let j = 0; j < sub.changes.length; ++j) { + let cur = sub.changes[j] if (to < cur.from.line) { cur.from = Pos(cur.from.line + diff, cur.from.ch) cur.to = Pos(cur.to.line + diff, cur.to.ch) @@ -311,7 +312,7 @@ function rebaseHistArray(array, from, to, diff) { } function rebaseHist(hist, change) { - var from = change.from.line, to = change.to.line, diff = change.text.length - (to - from) - 1 + let 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) } @@ -320,7 +321,7 @@ function rebaseHist(hist, change) { // returning the number and optionally registering the line as // changed. export function changeLine(doc, handle, changeType, op) { - var no = handle, line = handle + let no = handle, line = handle if (typeof handle == "number") line = getLine(doc, clipLine(doc, handle)) else no = lineNo(handle) if (no == null) return null diff --git a/src/model/chunk.js b/src/model/chunk.js index 0aee95fe..2a05d7c5 100644 --- a/src/model/chunk.js +++ b/src/model/chunk.js @@ -18,7 +18,8 @@ import { signalLater } from "../util/operation_group" export function LeafChunk(lines) { this.lines = lines this.parent = null - for (var i = 0, height = 0; i < lines.length; ++i) { + let height = 0 + for (let i = 0; i < lines.length; ++i) { lines[i].parent = this height += lines[i].height } @@ -29,8 +30,8 @@ LeafChunk.prototype = { chunkSize: function() { return this.lines.length }, // Remove the n lines at offset 'at'. removeInner: function(at, n) { - for (var i = at, e = at + n; i < e; ++i) { - var line = this.lines[i] + for (let i = at, e = at + n; i < e; ++i) { + let line = this.lines[i] this.height -= line.height cleanUpLine(line) signalLater(line, "delete") @@ -46,20 +47,20 @@ LeafChunk.prototype = { 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; i < lines.length; ++i) lines[i].parent = this + for (let i = 0; i < lines.length; ++i) lines[i].parent = this }, // Used to iterate over a part of the tree. iterN: function(at, n, op) { - for (var e = at + n; at < e; ++at) + for (let e = at + n; at < e; ++at) if (op(this.lines[at])) return true } } export function BranchChunk(children) { this.children = children - var size = 0, height = 0 - for (var i = 0; i < children.length; ++i) { - var ch = children[i] + let size = 0, height = 0 + for (let i = 0; i < children.length; ++i) { + let ch = children[i] size += ch.chunkSize(); height += ch.height ch.parent = this } @@ -72,10 +73,10 @@ BranchChunk.prototype = { chunkSize: function() { return this.size }, removeInner: function(at, n) { this.size -= n - for (var i = 0; i < this.children.length; ++i) { - var child = this.children[i], sz = child.chunkSize() + for (let i = 0; i < this.children.length; ++i) { + let child = this.children[i], sz = child.chunkSize() if (at < sz) { - var rm = Math.min(n, sz - at), oldHeight = child.height + let rm = Math.min(n, sz - at), oldHeight = child.height child.removeInner(at, rm) this.height -= oldHeight - child.height if (sz == rm) { this.children.splice(i--, 1); child.parent = null } @@ -87,28 +88,28 @@ BranchChunk.prototype = { // single leaf node. if (this.size - n < 25 && (this.children.length > 1 || !(this.children[0] instanceof LeafChunk))) { - var lines = [] + let lines = [] this.collapse(lines) this.children = [new LeafChunk(lines)] this.children[0].parent = this } }, collapse: function(lines) { - for (var i = 0; i < this.children.length; ++i) this.children[i].collapse(lines) + for (let i = 0; i < this.children.length; ++i) this.children[i].collapse(lines) }, insertInner: function(at, lines, height) { this.size += lines.length this.height += height - for (var i = 0; i < this.children.length; ++i) { - var child = this.children[i], sz = child.chunkSize() + for (let i = 0; i < this.children.length; ++i) { + let child = this.children[i], sz = child.chunkSize() if (at <= sz) { child.insertInner(at, lines, height) if (child.lines && child.lines.length > 50) { // To avoid memory thrashing when child.lines is huge (e.g. first view of a large file), it's never spliced. // Instead, small slices are taken. They're taken in order because sequential memory accesses are fastest. - var remaining = child.lines.length % 25 + 25 - for (var pos = remaining; pos < child.lines.length;) { - var leaf = new LeafChunk(child.lines.slice(pos, pos += 25)) + let remaining = child.lines.length % 25 + 25 + for (let pos = remaining; pos < child.lines.length;) { + let leaf = new LeafChunk(child.lines.slice(pos, pos += 25)) child.height -= leaf.height this.children.splice(++i, 0, leaf) leaf.parent = this @@ -124,19 +125,19 @@ BranchChunk.prototype = { // When a node has grown, check whether it should be split. maybeSpill: function() { if (this.children.length <= 10) return - var me = this + let me = this do { - var spilled = me.children.splice(me.children.length - 5, 5) - var sibling = new BranchChunk(spilled) + let spilled = me.children.splice(me.children.length - 5, 5) + let sibling = new BranchChunk(spilled) if (!me.parent) { // Become the parent node - var copy = new BranchChunk(me.children) + let copy = new BranchChunk(me.children) copy.parent = me me.children = [copy, sibling] me = copy } else { me.size -= sibling.size me.height -= sibling.height - var myIndex = indexOf(me.parent.children, me) + let myIndex = indexOf(me.parent.children, me) me.parent.children.splice(myIndex + 1, 0, sibling) } sibling.parent = me.parent @@ -144,10 +145,10 @@ BranchChunk.prototype = { me.parent.maybeSpill() }, iterN: function(at, n, op) { - for (var i = 0; i < this.children.length; ++i) { - var child = this.children[i], sz = child.chunkSize() + for (let i = 0; i < this.children.length; ++i) { + let child = this.children[i], sz = child.chunkSize() if (at < sz) { - var used = Math.min(n, sz - at) + let used = Math.min(n, sz - at) if (child.iterN(at, used, op)) return true if ((n -= used) == 0) break at = 0 diff --git a/src/model/document_data.js b/src/model/document_data.js index 8e423baf..6ce2ae58 100644 --- a/src/model/document_data.js +++ b/src/model/document_data.js @@ -25,14 +25,15 @@ export function updateDoc(doc, change, markedSpans, estimateHeight) { signalLater(line, "change", line, change) } function linesFor(start, end) { - for (var i = start, result = []; i < end; ++i) + let result = [] + for (let i = start; i < end; ++i) result.push(new Line(text[i], spansFor(i), estimateHeight)) return result } - 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 + let from = change.from, to = change.to, text = change.text + let firstLine = getLine(doc, from.line), lastLine = getLine(doc, to.line) + let lastText = lst(text), lastSpans = spansFor(text.length - 1), nlines = to.line - from.line // Adjust the line structure if (change.full) { @@ -41,7 +42,7 @@ export function updateDoc(doc, change, markedSpans, estimateHeight) { } else if (isWholeLineUpdate(doc, change)) { // This is a whole-line replace. Treated specially to make // sure line objects move the way they are supposed to. - var added = linesFor(0, text.length - 1) + let added = linesFor(0, text.length - 1) update(lastLine, lastLine.text, lastSpans) if (nlines) doc.remove(from.line, nlines) if (added.length) doc.insert(from.line, added) @@ -49,7 +50,7 @@ export function updateDoc(doc, change, markedSpans, estimateHeight) { if (text.length == 1) { update(firstLine, firstLine.text.slice(0, from.ch) + lastText + firstLine.text.slice(to.ch), lastSpans) } else { - var added = linesFor(1, text.length - 1) + let added = linesFor(1, text.length - 1) 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) @@ -60,7 +61,7 @@ export function updateDoc(doc, change, markedSpans, estimateHeight) { } else { update(firstLine, firstLine.text.slice(0, from.ch) + text[0], spansFor(0)) update(lastLine, lastText + lastLine.text.slice(to.ch), lastSpans) - var added = linesFor(1, text.length - 1) + let added = linesFor(1, text.length - 1) if (nlines > 1) doc.remove(from.line + 1, nlines - 1) doc.insert(from.line + 1, added) } @@ -71,10 +72,10 @@ export function updateDoc(doc, change, markedSpans, estimateHeight) { // Call f for all linked documents. export 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 (doc.linked) for (let i = 0; i < doc.linked.length; ++i) { + let rel = doc.linked[i] if (rel.doc == skip) continue - var shared = sharedHist && rel.sharedHist + let shared = sharedHist && rel.sharedHist if (sharedHistOnly && !shared) continue f(rel.doc, shared) propagate(rel.doc, doc, shared) diff --git a/src/model/history.js b/src/model/history.js index 8cc77b1a..0c7cf5bb 100644 --- a/src/model/history.js +++ b/src/model/history.js @@ -26,7 +26,7 @@ export function History(startGen) { // Create a history change event from an updateDoc-style change // object. export function historyChangeFromChange(doc, change) { - var histChange = {from: copyPos(change.from), to: changeEnd(change), text: getBetween(doc, change.from, change.to)} + let histChange = {from: copyPos(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 @@ -36,7 +36,7 @@ export function historyChangeFromChange(doc, change) { // a change event. function clearSelectionEvents(array) { while (array.length) { - var last = lst(array) + let last = lst(array) if (last.ranges) array.pop() else break } @@ -60,9 +60,10 @@ function lastChangeEvent(hist, force) { // a single operation, or are close together with an origin that // allows merging (starting with "+") into a single event. export function addChangeToHistory(doc, change, selAfter, opId) { - var hist = doc.history + let hist = doc.history hist.undone.length = 0 - var time = +new Date, cur + let time = +new Date, cur + let last if ((hist.lastOp == opId || hist.lastOrigin == change.origin && change.origin && @@ -70,7 +71,7 @@ export function addChangeToHistory(doc, change, selAfter, opId) { change.origin.charAt(0) == "*")) && (cur = lastChangeEvent(hist, hist.lastOp == opId))) { // Merge this change into the last event - var last = lst(cur.changes) + last = lst(cur.changes) if (cmp(change.from, change.to) == 0 && cmp(change.from, last.to) == 0) { // Optimized case for simple insertion -- don't want to add // new changesets for every character typed @@ -81,7 +82,7 @@ export function addChangeToHistory(doc, change, selAfter, opId) { } } else { // Can not be merged, start a new event. - var before = lst(hist.done) + let before = lst(hist.done) if (!before || !before.ranges) pushSelectionToHistory(doc.sel, hist.done) cur = {changes: [historyChangeFromChange(doc, change)], @@ -102,7 +103,7 @@ export function addChangeToHistory(doc, change, selAfter, opId) { } function selectionEventCanBeMerged(doc, origin, prev, sel) { - var ch = origin.charAt(0) + let ch = origin.charAt(0) return ch == "*" || ch == "+" && prev.ranges.length == sel.ranges.length && @@ -115,7 +116,7 @@ function selectionEventCanBeMerged(doc, origin, prev, sel) { // selection into the 'done' array when it was significantly // different (in number of selected ranges, emptiness, or time). export function addSelectionToHistory(doc, sel, opId, options) { - var hist = doc.history, origin = options && options.origin + let hist = doc.history, origin = options && options.origin // A new event is started when the previous origin does not match // the current, or the origins don't allow matching. Origins @@ -137,14 +138,14 @@ export function addSelectionToHistory(doc, sel, opId, options) { } export function pushSelectionToHistory(sel, dest) { - var top = lst(dest) + let top = lst(dest) if (!(top && top.ranges && top.equals(sel))) dest.push(sel) } // Used to store marked span information in the history. function attachLocalSpans(doc, change, from, to) { - var existing = change["spans_" + doc.id], n = 0 + let 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 @@ -156,7 +157,8 @@ function attachLocalSpans(doc, change, from, to) { // that have been explicitly cleared should not be restored. function removeClearedSpans(spans) { if (!spans) return null - for (var i = 0, out; i < spans.length; ++i) { + let out + for (let 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]) } @@ -165,9 +167,10 @@ function removeClearedSpans(spans) { // Retrieve and filter the old marked spans stored in a change event. function getOldSpans(doc, change) { - var found = change["spans_" + doc.id] + let found = change["spans_" + doc.id] if (!found) return null - for (var i = 0, nw = []; i < change.text.length; ++i) + let nw = [] + for (let i = 0; i < change.text.length; ++i) nw.push(removeClearedSpans(found[i])) return nw } @@ -177,17 +180,17 @@ function getOldSpans(doc, change) { // existed in the history (so that deleting around a span and then // undoing brings back the span). export function mergeOldSpans(doc, change) { - var old = getOldSpans(doc, change) - var stretched = stretchSpansOverChange(doc, change) + let old = getOldSpans(doc, change) + let stretched = stretchSpansOverChange(doc, change) if (!old) return stretched if (!stretched) return old - for (var i = 0; i < old.length; ++i) { - var oldCur = old[i], stretchCur = stretched[i] + for (let i = 0; i < old.length; ++i) { + let oldCur = old[i], stretchCur = stretched[i] if (oldCur && stretchCur) { - spans: for (var j = 0; j < stretchCur.length; ++j) { - var span = stretchCur[j] - for (var k = 0; k < oldCur.length; ++k) + spans: for (let j = 0; j < stretchCur.length; ++j) { + let span = stretchCur[j] + for (let k = 0; k < oldCur.length; ++k) if (oldCur[k].marker == span.marker) continue spans oldCur.push(span) } @@ -201,18 +204,19 @@ export function mergeOldSpans(doc, change) { // Used both to provide a JSON-safe object in .getHistory, and, when // detaching a document, to split the history in two export function copyHistoryArray(events, newGroup, instantiateSel) { - for (var i = 0, copy = []; i < events.length; ++i) { - var event = events[i] + let copy = [] + for (let i = 0; i < events.length; ++i) { + let event = events[i] if (event.ranges) { copy.push(instantiateSel ? Selection.prototype.deepCopy.call(event) : event) continue } - var changes = event.changes, newChanges = [] + let changes = event.changes, newChanges = [] copy.push({changes: newChanges}) - for (var j = 0; j < changes.length; ++j) { - var change = changes[j], m + for (let j = 0; j < changes.length; ++j) { + let 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 (newGroup) for (let prop in change) if (m = prop.match(/^spans_(\d+)$/)) { if (indexOf(newGroup, Number(m[1])) > -1) { lst(newChanges)[prop] = change[prop] delete change[prop] diff --git a/src/model/line_widget.js b/src/model/line_widget.js index b3b422aa..23b2381b 100644 --- a/src/model/line_widget.js +++ b/src/model/line_widget.js @@ -10,7 +10,7 @@ import { eventMixin } from "../util/event" // Line widgets are block elements displayed above or below a line. export function LineWidget(doc, node, options) { - if (options) for (var opt in options) if (options.hasOwnProperty(opt)) + if (options) for (let opt in options) if (options.hasOwnProperty(opt)) this[opt] = options[opt] this.doc = doc this.node = node @@ -23,11 +23,11 @@ function adjustScrollWhenAboveVisible(cm, line, diff) { } LineWidget.prototype.clear = function() { - var cm = this.doc.cm, ws = this.line.widgets, line = this.line, no = lineNo(line) + let cm = this.doc.cm, ws = this.line.widgets, line = this.line, no = lineNo(line) if (no == null || !ws) return - for (var i = 0; i < ws.length; ++i) if (ws[i] == this) ws.splice(i--, 1) + for (let i = 0; i < ws.length; ++i) if (ws[i] == this) ws.splice(i--, 1) if (!ws.length) line.widgets = null - var height = widgetHeight(this) + let height = widgetHeight(this) updateLineHeight(line, Math.max(0, line.height - height)) if (cm) runInOp(cm, function() { adjustScrollWhenAboveVisible(cm, line, -height) @@ -35,9 +35,9 @@ LineWidget.prototype.clear = function() { }) } LineWidget.prototype.changed = function() { - var oldH = this.height, cm = this.doc.cm, line = this.line + let oldH = this.height, cm = this.doc.cm, line = this.line this.height = null - var diff = widgetHeight(this) - oldH + let diff = widgetHeight(this) - oldH if (!diff) return updateLineHeight(line, line.height + diff) if (cm) runInOp(cm, function() { @@ -47,16 +47,16 @@ LineWidget.prototype.changed = function() { } export function addLineWidget(doc, handle, node, options) { - var widget = new LineWidget(doc, node, options) - var cm = doc.cm + let widget = new LineWidget(doc, node, options) + let cm = doc.cm if (cm && widget.noHScroll) cm.display.alignWidgets = true changeLine(doc, handle, "widget", function(line) { - var widgets = line.widgets || (line.widgets = []) + let widgets = line.widgets || (line.widgets = []) if (widget.insertAt == null) widgets.push(widget) else widgets.splice(Math.min(widgets.length - 1, Math.max(0, widget.insertAt)), 0, widget) widget.line = line if (cm && !lineIsHidden(doc, line)) { - var aboveVisible = heightAtLine(line) < doc.scrollTop + let aboveVisible = heightAtLine(line) < doc.scrollTop updateLineHeight(line, line.height + widgetHeight(widget)) if (aboveVisible) addToScrollPos(cm, null, widget.height) cm.curOp.forceUpdate = true diff --git a/src/model/mark_text.js b/src/model/mark_text.js index c103a574..f15cefb2 100644 --- a/src/model/mark_text.js +++ b/src/model/mark_text.js @@ -27,7 +27,10 @@ import { reCheckSelection } from "./selection_updates" // marker continues beyond the start/end of the line. Markers have // links back to the lines they currently touch. -var nextMarkerId = 0 +// Collapsed markers have unique ids, in order to be able to order +// them, which is needed for uniquely determining an outer marker +// when they overlap (they may nest, but not partially overlap). +let nextMarkerId = 0 export function TextMarker(doc, type) { this.lines = [] @@ -40,16 +43,16 @@ eventMixin(TextMarker) // Clear the marker. TextMarker.prototype.clear = function() { if (this.explicitlyCleared) return - var cm = this.doc.cm, withOp = cm && !cm.curOp + let cm = this.doc.cm, withOp = cm && !cm.curOp if (withOp) startOperation(cm) if (hasHandler(this, "clear")) { - var found = this.find() + let found = this.find() if (found) signalLater(this, "clear", found.from, found.to) } - 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) + let min = null, max = null + for (let i = 0; i < this.lines.length; ++i) { + let line = this.lines[i] + let span = getMarkedSpanFor(line.markedSpans, this) if (cm && !this.collapsed) regLineChange(cm, lineNo(line), "text") else if (cm) { if (span.to != null) max = lineNo(line) @@ -59,8 +62,8 @@ TextMarker.prototype.clear = function() { if (span.from == null && this.collapsed && !lineIsHidden(this.doc, 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(this.lines[i]), len = lineLength(visual) + if (cm && this.collapsed && !cm.options.lineWrapping) for (let i = 0; i < this.lines.length; ++i) { + let visual = visualLine(this.lines[i]), len = lineLength(visual) if (len > cm.display.maxLineLength) { cm.display.maxLine = visual cm.display.maxLineLength = len @@ -87,10 +90,10 @@ TextMarker.prototype.clear = function() { // number (used to prevent looking up the same line twice). TextMarker.prototype.find = function(side, lineObj) { if (side == null && this.type == "bookmark") side = 1 - var from, to - for (var i = 0; i < this.lines.length; ++i) { - var line = this.lines[i] - var span = getMarkedSpanFor(line.markedSpans, this) + let from, to + for (let i = 0; i < this.lines.length; ++i) { + let line = this.lines[i] + let span = getMarkedSpanFor(line.markedSpans, this) if (span.from != null) { from = Pos(lineObj ? line : lineNo(line), span.from) if (side == -1) return from @@ -106,20 +109,20 @@ TextMarker.prototype.find = function(side, lineObj) { // Signals that the marker's widget changed, and surrounding layout // should be recomputed. TextMarker.prototype.changed = function() { - var pos = this.find(-1, true), widget = this, cm = this.doc.cm + let pos = this.find(-1, true), widget = this, cm = this.doc.cm if (!pos || !cm) return runInOp(cm, function() { - var line = pos.line, lineN = lineNo(pos.line) - var view = findViewForLine(cm, lineN) + let line = pos.line, lineN = lineNo(pos.line) + let view = findViewForLine(cm, lineN) if (view) { clearLineMeasurementCacheFor(view) cm.curOp.selectionChanged = cm.curOp.forceUpdate = true } cm.curOp.updateMaxLine = true if (!lineIsHidden(widget.doc, line) && widget.height != null) { - var oldHeight = widget.height + let oldHeight = widget.height widget.height = null - var dHeight = widgetHeight(widget) - oldHeight + let dHeight = widgetHeight(widget) - oldHeight if (dHeight) updateLineHeight(line, line.height + dHeight) } @@ -128,7 +131,7 @@ TextMarker.prototype.changed = function() { TextMarker.prototype.attachLine = function(line) { if (!this.lines.length && this.doc.cm) { - var op = this.doc.cm.curOp + let op = this.doc.cm.curOp if (!op.maybeHiddenMarkers || indexOf(op.maybeHiddenMarkers, this) == -1) (op.maybeUnhiddenMarkers || (op.maybeUnhiddenMarkers = [])).push(this) } @@ -137,16 +140,11 @@ TextMarker.prototype.attachLine = function(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 + let op = this.doc.cm.curOp ;(op.maybeHiddenMarkers || (op.maybeHiddenMarkers = [])).push(this) } } -// Collapsed markers have unique ids, in order to be able to order -// them, which is needed for uniquely determining an outer marker -// when they overlap (they may nest, but not partially overlap). -var nextMarkerId = 0 - // Create a marker, wire it up to the right lines, and export function markText(doc, from, to, options, type) { // Shared markers (across linked documents) are handled separately @@ -156,7 +154,7 @@ export function markText(doc, from, to, options, type) { // Ensure we are in an operation. if (doc.cm && !doc.cm.curOp) return operation(doc.cm, markText)(doc, from, to, options, type) - var marker = new TextMarker(doc, type), diff = cmp(from, to) + let marker = new TextMarker(doc, type), diff = cmp(from, to) if (options) copyObj(options, marker, false) // Don't connect empty markers unless clearWhenEmpty is false if (diff > 0 || diff == 0 && marker.clearWhenEmpty !== false) @@ -178,7 +176,7 @@ export function markText(doc, from, to, options, type) { if (marker.addToHistory) addChangeToHistory(doc, {from: from, to: to, origin: "markText"}, doc.sel, NaN) - var curLine = from.line, cm = doc.cm, updateMaxLine + let curLine = from.line, cm = doc.cm, updateMaxLine doc.iter(curLine, to.line + 1, function(line) { if (cm && marker.collapsed && !cm.options.lineWrapping && visualLine(line) == cm.display.maxLine) updateMaxLine = true @@ -210,7 +208,7 @@ export function markText(doc, from, to, options, type) { if (marker.collapsed) regChange(cm, from.line, to.line + 1) else if (marker.className || marker.title || marker.startStyle || marker.endStyle || marker.css) - for (var i = from.line; i <= to.line; i++) regLineChange(cm, i, "text") + for (let i = from.line; i <= to.line; i++) regLineChange(cm, i, "text") if (marker.atomic) reCheckSelection(cm.doc) signalLater(cm, "markerAdded", cm, marker) } @@ -225,7 +223,7 @@ export function markText(doc, from, to, options, type) { export function SharedTextMarker(markers, primary) { this.markers = markers this.primary = primary - for (var i = 0; i < markers.length; ++i) + for (let i = 0; i < markers.length; ++i) markers[i].parent = this } eventMixin(SharedTextMarker) @@ -233,7 +231,7 @@ eventMixin(SharedTextMarker) SharedTextMarker.prototype.clear = function() { if (this.explicitlyCleared) return this.explicitlyCleared = true - for (var i = 0; i < this.markers.length; ++i) + for (let i = 0; i < this.markers.length; ++i) this.markers[i].clear() signalLater(this, "clear") } @@ -244,12 +242,12 @@ SharedTextMarker.prototype.find = function(side, lineObj) { function markTextShared(doc, from, to, options, type) { options = copyObj(options) options.shared = false - var markers = [markText(doc, from, to, options, type)], primary = markers[0] - var widget = options.widgetNode + let markers = [markText(doc, from, to, options, type)], primary = markers[0] + let widget = options.widgetNode linkedDocs(doc, function(doc) { if (widget) options.widgetNode = widget.cloneNode(true) markers.push(markText(doc, clipPos(doc, from), clipPos(doc, to), options, type)) - for (var i = 0; i < doc.linked.length; ++i) + for (let i = 0; i < doc.linked.length; ++i) if (doc.linked[i].isParent) return primary = lst(markers) }) @@ -262,11 +260,11 @@ export function findSharedMarkers(doc) { } export function copySharedMarkers(doc, markers) { - for (var i = 0; i < markers.length; i++) { - var marker = markers[i], pos = marker.find() - var mFrom = doc.clipPos(pos.from), mTo = doc.clipPos(pos.to) + for (let i = 0; i < markers.length; i++) { + let marker = markers[i], pos = marker.find() + let mFrom = doc.clipPos(pos.from), mTo = doc.clipPos(pos.to) if (cmp(mFrom, mTo)) { - var subMark = markText(doc, mFrom, mTo, marker.primary, marker.primary.type) + let subMark = markText(doc, mFrom, mTo, marker.primary, marker.primary.type) marker.markers.push(subMark) subMark.parent = marker } @@ -274,11 +272,11 @@ export function copySharedMarkers(doc, markers) { } export function detachSharedMarkers(markers) { - for (var i = 0; i < markers.length; i++) { - var marker = markers[i], linked = [marker.primary.doc] + for (let i = 0; i < markers.length; i++) { + let marker = markers[i], linked = [marker.primary.doc] linkedDocs(marker.primary.doc, function(d) { linked.push(d) }) - for (var j = 0; j < marker.markers.length; j++) { - var subMarker = marker.markers[j] + for (let j = 0; j < marker.markers.length; j++) { + let subMarker = marker.markers[j] if (indexOf(linked, subMarker.doc) == -1) { subMarker.parent = null marker.markers.splice(j--, 1) diff --git a/src/model/selection.js b/src/model/selection.js index c75fcd61..63371954 100644 --- a/src/model/selection.js +++ b/src/model/selection.js @@ -16,26 +16,27 @@ Selection.prototype = { equals: function(other) { if (other == this) return true if (other.primIndex != this.primIndex || other.ranges.length != this.ranges.length) return false - for (var i = 0; i < this.ranges.length; i++) { - var here = this.ranges[i], there = other.ranges[i] + for (let i = 0; i < this.ranges.length; i++) { + let here = this.ranges[i], there = other.ranges[i] if (cmp(here.anchor, there.anchor) != 0 || cmp(here.head, there.head) != 0) return false } return true }, deepCopy: function() { - for (var out = [], i = 0; i < this.ranges.length; i++) + let out = [] + for (let i = 0; i < this.ranges.length; i++) out[i] = new Range(copyPos(this.ranges[i].anchor), copyPos(this.ranges[i].head)) return new Selection(out, this.primIndex) }, somethingSelected: function() { - for (var i = 0; i < this.ranges.length; i++) + for (let i = 0; i < this.ranges.length; i++) if (!this.ranges[i].empty()) return true return false }, contains: function(pos, end) { if (!end) end = pos - for (var i = 0; i < this.ranges.length; i++) { - var range = this.ranges[i] + for (let i = 0; i < this.ranges.length; i++) { + let range = this.ranges[i] if (cmp(end, range.from()) >= 0 && cmp(pos, range.to()) <= 0) return i } @@ -59,14 +60,14 @@ Range.prototype = { // build a selection out of it. 'Consumes' ranges array (modifying // it). export function normalizeSelection(ranges, primIndex) { - var prim = ranges[primIndex] + let prim = ranges[primIndex] ranges.sort(function(a, b) { return cmp(a.from(), b.from()) }) primIndex = indexOf(ranges, prim) - for (var i = 1; i < ranges.length; i++) { - var cur = ranges[i], prev = ranges[i - 1] + for (let i = 1; i < ranges.length; i++) { + let cur = ranges[i], prev = ranges[i - 1] if (cmp(prev.to(), cur.from()) >= 0) { - var from = minPos(prev.from(), cur.from()), to = maxPos(prev.to(), cur.to()) - var inv = prev.empty() ? cur.from() == cur.head : prev.from() == prev.head + let from = minPos(prev.from(), cur.from()), to = maxPos(prev.to(), cur.to()) + let inv = prev.empty() ? cur.from() == cur.head : prev.from() == prev.head if (i <= primIndex) --primIndex ranges.splice(--i, 2, new Range(inv ? to : from, inv ? from : to)) } diff --git a/src/model/selection_updates.js b/src/model/selection_updates.js index 640af1f2..0e310a5d 100644 --- a/src/model/selection_updates.js +++ b/src/model/selection_updates.js @@ -18,9 +18,9 @@ import { normalizeSelection, Range, Selection, simpleSelection } from "./selecti // Used for cursor motion and such. export function extendRange(doc, range, head, other) { if (doc.cm && doc.cm.display.shift || doc.extend) { - var anchor = range.anchor + let anchor = range.anchor if (other) { - var posBefore = cmp(head, anchor) < 0 + let posBefore = cmp(head, anchor) < 0 if (posBefore != (cmp(other, anchor) < 0)) { anchor = head head = other @@ -42,15 +42,16 @@ export function extendSelection(doc, head, other, options) { // Extend all selections (pos is an array of selections with length // equal the number of selections) export function extendSelections(doc, heads, options) { - for (var out = [], i = 0; i < doc.sel.ranges.length; i++) + let out = [] + for (let i = 0; i < doc.sel.ranges.length; i++) out[i] = extendRange(doc, doc.sel.ranges[i], heads[i], null) - var newSel = normalizeSelection(out, doc.sel.primIndex) + let newSel = normalizeSelection(out, doc.sel.primIndex) setSelection(doc, newSel, options) } // Updates a single range in the selection. export function replaceOneSelection(doc, i, range, options) { - var ranges = doc.sel.ranges.slice(0) + let ranges = doc.sel.ranges.slice(0) ranges[i] = range setSelection(doc, normalizeSelection(ranges, doc.sel.primIndex), options) } @@ -63,11 +64,11 @@ export function setSimpleSelection(doc, anchor, head, options) { // Give beforeSelectionChange handlers a change to influence a // selection update. function filterSelectionChange(doc, sel, options) { - var obj = { + let obj = { ranges: sel.ranges, update: function(ranges) { this.ranges = [] - for (var i = 0; i < ranges.length; i++) + for (let i = 0; i < ranges.length; i++) this.ranges[i] = new Range(clipPos(doc, ranges[i].anchor), clipPos(doc, ranges[i].head)) }, @@ -80,7 +81,7 @@ function filterSelectionChange(doc, sel, options) { } export function setSelectionReplaceHistory(doc, sel, options) { - var done = doc.history.done, last = lst(done) + let done = doc.history.done, last = lst(done) if (last && last.ranges) { done[done.length - 1] = sel setSelectionNoUndo(doc, sel, options) @@ -99,7 +100,7 @@ export function setSelectionNoUndo(doc, sel, options) { if (hasHandler(doc, "beforeSelectionChange") || doc.cm && hasHandler(doc.cm, "beforeSelectionChange")) sel = filterSelectionChange(doc, sel, options) - var bias = options && options.bias || + let bias = options && options.bias || (cmp(sel.primary().head, doc.sel.primary().head) < 0 ? -1 : 1) setSelectionInner(doc, skipAtomicInSelection(doc, sel, bias, true)) @@ -128,12 +129,12 @@ export function reCheckSelection(doc) { // Return a selection that does not partially select any atomic // ranges. function skipAtomicInSelection(doc, sel, bias, mayClear) { - var out - for (var i = 0; i < sel.ranges.length; i++) { - var range = sel.ranges[i] - var old = sel.ranges.length == doc.sel.ranges.length && doc.sel.ranges[i] - var newAnchor = skipAtomic(doc, range.anchor, old && old.anchor, bias, mayClear) - var newHead = skipAtomic(doc, range.head, old && old.head, bias, mayClear) + let out + for (let i = 0; i < sel.ranges.length; i++) { + let range = sel.ranges[i] + let old = sel.ranges.length == doc.sel.ranges.length && doc.sel.ranges[i] + let newAnchor = skipAtomic(doc, range.anchor, old && old.anchor, bias, mayClear) + let newHead = skipAtomic(doc, range.head, old && old.head, bias, mayClear) if (out || newAnchor != range.anchor || newHead != range.head) { if (!out) out = sel.ranges.slice(0, i) out[i] = new Range(newAnchor, newHead) @@ -143,9 +144,9 @@ function skipAtomicInSelection(doc, sel, bias, mayClear) { } function skipAtomicInner(doc, pos, oldPos, dir, mayClear) { - var line = getLine(doc, pos.line) - if (line.markedSpans) for (var i = 0; i < line.markedSpans.length; ++i) { - var sp = line.markedSpans[i], m = sp.marker + let line = getLine(doc, pos.line) + if (line.markedSpans) for (let i = 0; i < line.markedSpans.length; ++i) { + let sp = line.markedSpans[i], m = sp.marker if ((sp.from == null || (m.inclusiveLeft ? sp.from <= pos.ch : sp.from < pos.ch)) && (sp.to == null || (m.inclusiveRight ? sp.to >= pos.ch : sp.to > pos.ch))) { if (mayClear) { @@ -158,14 +159,14 @@ function skipAtomicInner(doc, pos, oldPos, dir, mayClear) { if (!m.atomic) continue if (oldPos) { - var near = m.find(dir < 0 ? 1 : -1), diff + let near = m.find(dir < 0 ? 1 : -1), diff if (dir < 0 ? m.inclusiveRight : m.inclusiveLeft) near = movePos(doc, near, -dir, near && near.line == pos.line ? line : null) if (near && near.line == pos.line && (diff = cmp(near, oldPos)) && (dir < 0 ? diff < 0 : diff > 0)) return skipAtomicInner(doc, near, pos, dir, mayClear) } - var far = m.find(dir < 0 ? -1 : 1) + let far = m.find(dir < 0 ? -1 : 1) if (dir < 0 ? m.inclusiveLeft : m.inclusiveRight) far = movePos(doc, far, dir, far.line == pos.line ? line : null) return far ? skipAtomicInner(doc, far, pos, dir, mayClear) : null @@ -176,8 +177,8 @@ function skipAtomicInner(doc, pos, oldPos, dir, mayClear) { // Ensure a given position is not inside an atomic range. export function skipAtomic(doc, pos, oldPos, bias, mayClear) { - var dir = bias || 1 - var found = skipAtomicInner(doc, pos, oldPos, dir, mayClear) || + let dir = bias || 1 + let found = skipAtomicInner(doc, pos, oldPos, dir, mayClear) || (!mayClear && skipAtomicInner(doc, pos, oldPos, dir, true)) || skipAtomicInner(doc, pos, oldPos, -dir, mayClear) || (!mayClear && skipAtomicInner(doc, pos, oldPos, -dir, true)) diff --git a/src/modes.js b/src/modes.js index 15d16cef..065a463b 100644 --- a/src/modes.js +++ b/src/modes.js @@ -1,7 +1,7 @@ import { copyObj, createObj } from "./util/misc" // Known modes, by name and by MIME -export var modes = {}, mimeModes = {} +export let modes = {}, mimeModes = {} // Extra arguments are stored as the mode's dependencies, which is // used by (legacy) mechanisms like loadmode.js to automatically @@ -22,7 +22,7 @@ export function resolveMode(spec) { if (typeof spec == "string" && mimeModes.hasOwnProperty(spec)) { spec = mimeModes[spec] } else if (spec && typeof spec.name == "string" && mimeModes.hasOwnProperty(spec.name)) { - var found = mimeModes[spec.name] + let found = mimeModes[spec.name] if (typeof found == "string") found = {name: found} spec = createObj(found, spec) spec.name = found.name @@ -38,13 +38,13 @@ export function resolveMode(spec) { // Given a mode spec (anything that resolveMode accepts), find and // initialize an actual mode object. export function getMode(options, spec) { - var spec = resolveMode(spec) - var mfactory = modes[spec.name] + spec = resolveMode(spec) + let mfactory = modes[spec.name] if (!mfactory) return getMode(options, "text/plain") - var modeObj = mfactory(options, spec) + let modeObj = mfactory(options, spec) if (modeExtensions.hasOwnProperty(spec.name)) { - var exts = modeExtensions[spec.name] - for (var prop in exts) { + let exts = modeExtensions[spec.name] + for (let prop in exts) { if (!exts.hasOwnProperty(prop)) continue if (modeObj.hasOwnProperty(prop)) modeObj["_" + prop] = modeObj[prop] modeObj[prop] = exts[prop] @@ -52,7 +52,7 @@ export function getMode(options, spec) { } modeObj.name = spec.name if (spec.helperType) modeObj.helperType = spec.helperType - if (spec.modeProps) for (var prop in spec.modeProps) + if (spec.modeProps) for (let prop in spec.modeProps) modeObj[prop] = spec.modeProps[prop] return modeObj @@ -60,18 +60,18 @@ export function getMode(options, spec) { // This can be used to attach properties to mode objects from // outside the actual mode definition. -export var modeExtensions = {} +export let modeExtensions = {} export function extendMode(mode, properties) { - var exts = modeExtensions.hasOwnProperty(mode) ? modeExtensions[mode] : (modeExtensions[mode] = {}) + let exts = modeExtensions.hasOwnProperty(mode) ? modeExtensions[mode] : (modeExtensions[mode] = {}) copyObj(properties, exts) } export 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] + let nstate = {} + for (let n in state) { + let val = state[n] if (val instanceof Array) val = val.concat([]) nstate[n] = val } @@ -81,8 +81,9 @@ export function copyState(mode, state) { // Given a mode and a state (for that mode), find the inner mode and // state at the position that the state refers to. export function innerMode(mode, state) { + let info while (mode.innerMode) { - var info = mode.innerMode(state) + info = mode.innerMode(state) if (!info || info.mode == mode) break state = info.state mode = info.mode diff --git a/src/util/StringStream.js b/src/util/StringStream.js index d0480742..cf7b8244 100644 --- a/src/util/StringStream.js +++ b/src/util/StringStream.js @@ -5,7 +5,7 @@ import { countColumn } from "./misc" // Fed to the mode parsers, provides helper functions to make // parsers more succinct. -var StringStream = function(string, tabSize) { +let StringStream = function(string, tabSize) { this.pos = this.start = 0 this.string = string this.tabSize = tabSize || 8 @@ -22,24 +22,25 @@ StringStream.prototype = { 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)) + let ch = this.string.charAt(this.pos) + let ok + if (typeof match == "string") ok = ch == match + else ok = ch && (match.test ? match.test(ch) : match(ch)) if (ok) {++this.pos; return ch} }, eatWhile: function(match) { - var start = this.pos + let start = this.pos while (this.eat(match)){} return this.pos > start }, eatSpace: function() { - var start = this.pos + let 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) + let found = this.string.indexOf(ch, this.pos) if (found > -1) {this.pos = found; return true} }, backUp: function(n) {this.pos -= n}, @@ -56,14 +57,14 @@ StringStream.prototype = { }, match: function(pattern, consume, caseInsensitive) { if (typeof pattern == "string") { - var cased = function(str) {return caseInsensitive ? str.toLowerCase() : str} - var substr = this.string.substr(this.pos, pattern.length) + let cased = function(str) {return caseInsensitive ? str.toLowerCase() : str} + let substr = this.string.substr(this.pos, pattern.length) if (cased(substr) == cased(pattern)) { if (consume !== false) this.pos += pattern.length return true } } else { - var match = this.string.slice(this.pos).match(pattern) + let 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 diff --git a/src/util/bidi.js b/src/util/bidi.js index f12dccf5..4c365f4c 100644 --- a/src/util/bidi.js +++ b/src/util/bidi.js @@ -4,9 +4,9 @@ import { isExtendingChar, lst } from "./misc" export 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] + let found = false + for (let i = 0; i < order.length; ++i) { + let part = order[i] 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 @@ -18,25 +18,26 @@ export function iterateBidiSections(order, from, to, f) { export function bidiLeft(part) { return part.level % 2 ? part.to : part.from } export function bidiRight(part) { return part.level % 2 ? part.from : part.to } -export function lineLeft(line) { var order = getOrder(line); return order ? bidiLeft(order[0]) : 0 } +export function lineLeft(line) { let order = getOrder(line); return order ? bidiLeft(order[0]) : 0 } export function lineRight(line) { - var order = getOrder(line) + let order = getOrder(line) if (!order) return line.text.length return bidiRight(lst(order)) } function compareBidiLevel(order, a, b) { - var linedir = order[0].level + let linedir = order[0].level if (a == linedir) return true if (b == linedir) return false return a < b } -export var bidiOther = null +export let bidiOther = null export function getBidiPartAt(order, pos) { + let found bidiOther = null - for (var i = 0, found; i < order.length; ++i) { - var cur = order[i] + for (let i = 0; i < order.length; ++i) { + let cur = order[i] if (cur.from < pos && cur.to > pos) return i if ((cur.from == pos || cur.to == pos)) { if (found == null) { @@ -66,10 +67,10 @@ function moveInLine(line, pos, dir, byUnit) { // LTR text touch each other. This often requires the cursor offset // to move more than one unit, in order to visually move one unit. export function moveVisually(line, start, dir, byUnit) { - var bidi = getOrder(line) + let bidi = getOrder(line) if (!bidi) return moveLogically(line, start, dir, byUnit) - var pos = getBidiPartAt(bidi, start), part = bidi[pos] - var target = moveInLine(line, start, part.level % 2 ? -dir : dir, byUnit) + let pos = getBidiPartAt(bidi, start), part = bidi[pos] + let target = moveInLine(line, start, part.level % 2 ? -dir : dir, byUnit) for (;;) { if (target > part.from && target < part.to) return target @@ -89,7 +90,7 @@ export function moveVisually(line, start, dir, byUnit) { } export function moveLogically(line, start, dir, byUnit) { - var target = start + dir + let target = start + dir if (byUnit) while (target > 0 && isExtendingChar(line.text.charAt(target))) target += dir return target < 0 || target > line.text.length ? null : target } @@ -117,11 +118,11 @@ export function moveLogically(line, start, dir, byUnit) { // 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. -export var bidiOrdering = (function() { +export let bidiOrdering = (function() { // Character types for codepoints 0 to 0xff - var lowTypes = "bbbbbbbbbtstwsbbbbbbbbbbbbbbssstwNN%%%NNNNNN,N,N1111111111NNNNNNNLLLLLLLLLLLLLLLLLLLLLLLLLLNNNNNNLLLLLLLLLLLLLLLLLLLLLLLLLLNNNNbbbbbbsbbbbbbbbbbbbbbbbbbbbbbbbbb,N%%%%NNNNLNNNNN%%11NLNNN1LNNNNNLLLLLLLLLLLLLLLLLLLLLLLNLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLN" + let lowTypes = "bbbbbbbbbtstwsbbbbbbbbbbbbbbssstwNN%%%NNNNNN,N,N1111111111NNNNNNNLLLLLLLLLLLLLLLLLLLLLLLLLLNNNNNNLLLLLLLLLLLLLLLLLLLLLLLLLLNNNNbbbbbbsbbbbbbbbbbbbbbbbbbbbbbbbbb,N%%%%NNNNLNNNNN%%11NLNNN1LNNNNNLLLLLLLLLLLLLLLLLLLLLLLNLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLN" // Character types for codepoints 0x600 to 0x6ff - var arabicTypes = "rrrrrrrrrrrr,rNNmmmmmmrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrmmmmmmmmmmmmmmrrrrrrrnnnnnnnnnn%nnrrrmrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrmmmmmmmmmmmmmmmmmmmNmmmm" + let arabicTypes = "rrrrrrrrrrrr,rNNmmmmmmrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrmmmmmmmmmmmmmmrrrrrrrnnnnnnnnnn%nnrrrmrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrmmmmmmmmmmmmmmmmmmmNmmmm" function charType(code) { if (code <= 0xf7) return lowTypes.charAt(code) else if (0x590 <= code && code <= 0x5f4) return "R" @@ -132,10 +133,10 @@ export var bidiOrdering = (function() { else return "L" } - var bidiRE = /[\u0590-\u05f4\u0600-\u06ff\u0700-\u08ac]/ - var isNeutral = /[stwN]/, isStrong = /[LRr]/, countsAsLeft = /[Lb1n]/, countsAsNum = /[1n]/ + let bidiRE = /[\u0590-\u05f4\u0600-\u06ff\u0700-\u08ac]/ + let isNeutral = /[stwN]/, isStrong = /[LRr]/, countsAsLeft = /[Lb1n]/, countsAsNum = /[1n]/ // Browsers seem to always treat the boundaries of block elements as being L. - var outerType = "L" + let outerType = "L" function BidiSpan(level, from, to) { this.level = level @@ -144,16 +145,16 @@ export var bidiOrdering = (function() { return function(str) { if (!bidiRE.test(str)) return false - var len = str.length, types = [] - for (var i = 0, type; i < len; ++i) - types.push(type = charType(str.charCodeAt(i))) + let len = str.length, types = [] + for (let i = 0; i < len; ++i) + types.push(charType(str.charCodeAt(i))) // 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 = outerType; i < len; ++i) { - var type = types[i] + for (let i = 0, prev = outerType; i < len; ++i) { + let type = types[i] if (type == "m") types[i] = prev else prev = type } @@ -163,8 +164,8 @@ export var bidiOrdering = (function() { // AL is found, change the type of the European number to Arabic // number. // W3. Change all ALs to R. - for (var i = 0, cur = outerType; i < len; ++i) { - var type = types[i] + for (let i = 0, cur = outerType; i < len; ++i) { + let type = types[i] if (type == "1" && cur == "r") types[i] = "n" else if (isStrong.test(type)) { cur = type; if (type == "r") types[i] = "R" } } @@ -172,8 +173,8 @@ export var bidiOrdering = (function() { // 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] + for (let i = 1, prev = types[0]; i < len - 1; ++i) { + let 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 @@ -184,13 +185,14 @@ export var bidiOrdering = (function() { // 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] + for (let i = 0; i < len; ++i) { + let 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 && types[end] == "1") ? "1" : "N" - for (var j = i; j < end; ++j) types[j] = replace + let end + for (end = i + 1; end < len && types[end] == "%"; ++end) {} + let replace = (i && types[i-1] == "!") || (end < len && types[end] == "1") ? "1" : "N" + for (let j = i; j < end; ++j) types[j] = replace i = end - 1 } } @@ -198,8 +200,8 @@ export var bidiOrdering = (function() { // 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 = outerType; i < len; ++i) { - var type = types[i] + for (let i = 0, cur = outerType; i < len; ++i) { + let type = types[i] if (cur == "L" && type == "1") types[i] = "L" else if (isStrong.test(type)) cur = type } @@ -210,13 +212,14 @@ export var bidiOrdering = (function() { // 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) { + for (let 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] : outerType) == "L" - var after = (end < len ? types[end] : outerType) == "L" - var replace = before || after ? "L" : "R" - for (var j = i; j < end; ++j) types[j] = replace + let end + for (end = i + 1; end < len && isNeutral.test(types[end]); ++end) {} + let before = (i ? types[i-1] : outerType) == "L" + let after = (end < len ? types[end] : outerType) == "L" + let replace = before || after ? "L" : "R" + for (let j = i; j < end; ++j) types[j] = replace i = end - 1 } } @@ -226,19 +229,19 @@ export var bidiOrdering = (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 = [], m - for (var i = 0; i < len;) { + let order = [], m + for (let i = 0; i < len;) { if (countsAsLeft.test(types[i])) { - var start = i + let start = i for (++i; i < len && countsAsLeft.test(types[i]); ++i) {} order.push(new BidiSpan(0, start, i)) } else { - var pos = i, at = order.length + let pos = i, at = order.length for (++i; i < len && types[i] != "L"; ++i) {} - for (var j = pos; j < i;) { + for (let j = pos; j < i;) { if (countsAsNum.test(types[j])) { if (pos < j) order.splice(at, 0, new BidiSpan(1, pos, j)) - var nstart = j + let nstart = j for (++j; j < i && countsAsNum.test(types[j]); ++j) {} order.splice(at, 0, new BidiSpan(2, nstart, j)) pos = j @@ -268,7 +271,7 @@ export var bidiOrdering = (function() { // false for lines that are fully left-to-right, and an array of // BidiSpan objects otherwise. export function getOrder(line) { - var order = line.order + let order = line.order if (order == null) order = line.order = bidiOrdering(line.text) return order } diff --git a/src/util/browser.js b/src/util/browser.js index ae48fb5c..ce678a82 100644 --- a/src/util/browser.js +++ b/src/util/browser.js @@ -1,31 +1,31 @@ // Kludges for bugs and behavior differences that can't be feature // detected are enabled based on userAgent etc sniffing. -var userAgent = navigator.userAgent -var platform = navigator.platform +let userAgent = navigator.userAgent +let platform = navigator.platform -export var gecko = /gecko\/\d/i.test(userAgent) -var ie_upto10 = /MSIE \d/.test(userAgent) -var ie_11up = /Trident\/(?:[7-9]|\d{2,})\..*rv:(\d+)/.exec(userAgent) -export var ie = ie_upto10 || ie_11up -export var ie_version = ie && (ie_upto10 ? document.documentMode || 6 : ie_11up[1]) -export var webkit = /WebKit\//.test(userAgent) -var qtwebkit = webkit && /Qt\/\d+\.\d+/.test(userAgent) -export var chrome = /Chrome\//.test(userAgent) -export var presto = /Opera\//.test(userAgent) -export var safari = /Apple Computer/.test(navigator.vendor) -export var mac_geMountainLion = /Mac OS X 1\d\D([8-9]|\d\d)\D/.test(userAgent) -export var phantom = /PhantomJS/.test(userAgent) +export let gecko = /gecko\/\d/i.test(userAgent) +let ie_upto10 = /MSIE \d/.test(userAgent) +let ie_11up = /Trident\/(?:[7-9]|\d{2,})\..*rv:(\d+)/.exec(userAgent) +export let ie = ie_upto10 || ie_11up +export let ie_version = ie && (ie_upto10 ? document.documentMode || 6 : ie_11up[1]) +export let webkit = /WebKit\//.test(userAgent) +let qtwebkit = webkit && /Qt\/\d+\.\d+/.test(userAgent) +export let chrome = /Chrome\//.test(userAgent) +export let presto = /Opera\//.test(userAgent) +export let safari = /Apple Computer/.test(navigator.vendor) +export let mac_geMountainLion = /Mac OS X 1\d\D([8-9]|\d\d)\D/.test(userAgent) +export let phantom = /PhantomJS/.test(userAgent) -export var ios = /AppleWebKit/.test(userAgent) && /Mobile\/\w+/.test(userAgent) +export let ios = /AppleWebKit/.test(userAgent) && /Mobile\/\w+/.test(userAgent) // This is woefully incomplete. Suggestions for alternative methods welcome. -export var mobile = ios || /Android|webOS|BlackBerry|Opera Mini|Opera Mobi|IEMobile/i.test(userAgent) -export var mac = ios || /Mac/.test(platform) -export var chromeOS = /\bCrOS\b/.test(userAgent) -export var windows = /win/i.test(platform) +export let mobile = ios || /Android|webOS|BlackBerry|Opera Mini|Opera Mobi|IEMobile/i.test(userAgent) +export let mac = ios || /Mac/.test(platform) +export let chromeOS = /\bCrOS\b/.test(userAgent) +export let windows = /win/i.test(platform) -var presto_version = presto && userAgent.match(/Version\/(\d*\.\d*)/) +let presto_version = presto && userAgent.match(/Version\/(\d*\.\d*)/) if (presto_version) presto_version = Number(presto_version[1]) if (presto_version && presto_version >= 15) { presto = false; webkit = true } // Some browsers use the wrong event properties to signal cmd/ctrl on OS X -export var flipCtrlCmd = mac && (qtwebkit || presto && (presto_version == null || presto_version < 12.11)) -export var captureRightClick = gecko || (ie && ie_version >= 9) +export let flipCtrlCmd = mac && (qtwebkit || presto && (presto_version == null || presto_version < 12.11)) +export let captureRightClick = gecko || (ie && ie_version >= 9) diff --git a/src/util/dom.js b/src/util/dom.js index df2cfd0a..465dbb5a 100644 --- a/src/util/dom.js +++ b/src/util/dom.js @@ -2,17 +2,17 @@ import { ie, ie_version, ios } from "./browser" export function classTest(cls) { return new RegExp("(^|\\s)" + cls + "(?:$|\\s)\\s*") } -export var rmClass = function(node, cls) { - var current = node.className - var match = classTest(cls).exec(current) +export let rmClass = function(node, cls) { + let current = node.className + let match = classTest(cls).exec(current) if (match) { - var after = current.slice(match.index + match[0].length) + let after = current.slice(match.index + match[0].length) node.className = current.slice(0, match.index) + (after ? match[1] + after : "") } } export function removeChildren(e) { - for (var count = e.childNodes.length; count > 0; --count) + for (let count = e.childNodes.length; count > 0; --count) e.removeChild(e.firstChild) return e } @@ -22,23 +22,23 @@ export function removeChildrenAndAdd(parent, e) { } export function elt(tag, content, className, style) { - var e = document.createElement(tag) + let e = document.createElement(tag) if (className) e.className = className if (style) e.style.cssText = style if (typeof content == "string") e.appendChild(document.createTextNode(content)) - else if (content) for (var i = 0; i < content.length; ++i) e.appendChild(content[i]) + else if (content) for (let i = 0; i < content.length; ++i) e.appendChild(content[i]) return e } -export var range +export let range if (document.createRange) range = function(node, start, end, endNode) { - var r = document.createRange() + let r = document.createRange() r.setEnd(endNode || node, end) r.setStart(node, start) return r } else range = function(node, start, end) { - var r = document.body.createTextRange() + let r = document.body.createTextRange() try { r.moveToElementText(node.parentNode) } catch(e) { return r } r.collapse(true) @@ -58,8 +58,8 @@ export function contains(parent, child) { } while (child = child.parentNode) } -export var activeElt = function() { - var activeElement = document.activeElement +export let activeElt = function() { + let activeElement = document.activeElement while (activeElement && activeElement.root && activeElement.root.activeElement) activeElement = activeElement.root.activeElement return activeElement @@ -72,17 +72,17 @@ if (ie && ie_version < 11) activeElt = function() { } export function addClass(node, cls) { - var current = node.className + let current = node.className if (!classTest(cls).test(current)) node.className += (current ? " " : "") + cls } export function joinClasses(a, b) { - var as = a.split(" ") - for (var i = 0; i < as.length; i++) + let as = a.split(" ") + for (let i = 0; i < as.length; i++) if (as[i] && !classTest(as[i]).test(b)) b += " " + as[i] return b } -export var selectInput = function(node) { node.select() } +export let selectInput = function(node) { node.select() } if (ios) // Mobile Safari apparently has a bug where select() is broken. selectInput = function(node) { node.selectionStart = 0; node.selectionEnd = node.value.length } else if (ie) // Suppress mysterious IE10 errors diff --git a/src/util/event.js b/src/util/event.js index b2137583..e667a9f3 100644 --- a/src/util/event.js +++ b/src/util/event.js @@ -6,21 +6,21 @@ import { indexOf } from "./misc" // Lightweight event framework. on/off also work on DOM nodes, // registering native DOM handlers. -export var on = function(emitter, type, f) { +export let on = function(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] = []) + let map = emitter._handlers || (emitter._handlers = {}) + let arr = map[type] || (map[type] = []) arr.push(f) } } -var noHandlers = [] +let noHandlers = [] export function getHandlers(emitter, type, copy) { - var arr = emitter._handlers && emitter._handlers[type] + let arr = emitter._handlers && emitter._handlers[type] if (copy) return arr && arr.length > 0 ? arr.slice() : noHandlers else return arr || noHandlers } @@ -31,17 +31,17 @@ export function off(emitter, type, f) { else if (emitter.detachEvent) emitter.detachEvent("on" + type, f) else { - var handlers = getHandlers(emitter, type, false) - for (var i = 0; i < handlers.length; ++i) + let handlers = getHandlers(emitter, type, false) + for (let i = 0; i < handlers.length; ++i) if (handlers[i] == f) { handlers.splice(i, 1); break } } } export function signal(emitter, type /*, values...*/) { - var handlers = getHandlers(emitter, type, true) + let handlers = getHandlers(emitter, type, true) if (!handlers.length) return - var args = Array.prototype.slice.call(arguments, 2) - for (var i = 0; i < handlers.length; ++i) handlers[i].apply(null, args) + let args = Array.prototype.slice.call(arguments, 2) + for (let i = 0; i < handlers.length; ++i) handlers[i].apply(null, args) } // The DOM events that CodeMirror handles can be overridden by @@ -55,10 +55,10 @@ export function signalDOMEvent(cm, e, override) { } export function signalCursorActivity(cm) { - var arr = cm._handlers && cm._handlers.cursorActivity + let arr = cm._handlers && cm._handlers.cursorActivity if (!arr) return - var set = cm.curOp.cursorActivityHandlers || (cm.curOp.cursorActivityHandlers = []) - for (var i = 0; i < arr.length; ++i) if (indexOf(set, arr[i]) == -1) + let set = cm.curOp.cursorActivityHandlers || (cm.curOp.cursorActivityHandlers = []) + for (let i = 0; i < arr.length; ++i) if (indexOf(set, arr[i]) == -1) set.push(arr[i]) } @@ -91,7 +91,7 @@ export function e_stop(e) {e_preventDefault(e); e_stopPropagation(e)} export function e_target(e) {return e.target || e.srcElement} export function e_button(e) { - var b = e.which + let b = e.which if (b == null) { if (e.button & 1) b = 1 else if (e.button & 2) b = 3 diff --git a/src/util/feature_detection.js b/src/util/feature_detection.js index 1e9baf41..d98393af 100644 --- a/src/util/feature_detection.js +++ b/src/util/feature_detection.js @@ -2,35 +2,35 @@ import { elt, range, removeChildren, removeChildrenAndAdd } from "./dom" import { ie, ie_version } from "./browser" // Detect drag-and-drop -export var dragAndDrop = function() { +export let 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 && ie_version < 9) return false - var div = elt('div') + let div = elt('div') return "draggable" in div || "dragDrop" in div }() -var zwspSupported +let zwspSupported export function zeroWidthElement(measure) { if (zwspSupported == null) { - var test = elt("span", "\u200b") + let test = elt("span", "\u200b") removeChildrenAndAdd(measure, elt("span", [test, document.createTextNode("x")])) if (measure.firstChild.offsetHeight != 0) zwspSupported = test.offsetWidth <= 1 && test.offsetHeight > 2 && !(ie && ie_version < 8) } - var node = zwspSupported ? elt("span", "\u200b") : + let node = zwspSupported ? elt("span", "\u200b") : elt("span", "\u00a0", null, "display: inline-block; width: 1px; margin-right: -1px") node.setAttribute("cm-text", "") return node } // Feature-detect IE's crummy client rect reporting for bidi text -var badBidiRects +let badBidiRects export function hasBadBidiRects(measure) { if (badBidiRects != null) return badBidiRects - var txt = removeChildrenAndAdd(measure, document.createTextNode("A\u062eA")) - var r0 = range(txt, 0, 1).getBoundingClientRect() - var r1 = range(txt, 1, 2).getBoundingClientRect() + let txt = removeChildrenAndAdd(measure, document.createTextNode("A\u062eA")) + let r0 = range(txt, 0, 1).getBoundingClientRect() + let r1 = range(txt, 1, 2).getBoundingClientRect() removeChildren(measure) if (!r0 || r0.left == r0.right) return false // Safari returns null in some cases (#2780) return badBidiRects = (r1.right - r0.right < 3) @@ -38,13 +38,13 @@ export function hasBadBidiRects(measure) { // See if "".split is the broken IE version, if so, provide an // alternative way to split lines. -export var splitLinesAuto = "\n\nb".split(/\n/).length != 3 ? function(string) { - var pos = 0, result = [], l = string.length +export let splitLinesAuto = "\n\nb".split(/\n/).length != 3 ? function(string) { + let pos = 0, result = [], l = string.length while (pos <= l) { - var nl = string.indexOf("\n", pos) + let nl = string.indexOf("\n", pos) if (nl == -1) nl = string.length - var line = string.slice(pos, string.charAt(nl - 1) == "\r" ? nl - 1 : nl) - var rt = line.indexOf("\r") + let line = string.slice(pos, string.charAt(nl - 1) == "\r" ? nl - 1 : nl) + let rt = line.indexOf("\r") if (rt != -1) { result.push(line.slice(0, rt)) pos += rt + 1 @@ -56,28 +56,29 @@ export var splitLinesAuto = "\n\nb".split(/\n/).length != 3 ? function(string) { return result } : function(string){return string.split(/\r\n?|\n/)} -export var hasSelection = window.getSelection ? function(te) { +export let hasSelection = window.getSelection ? function(te) { try { return te.selectionStart != te.selectionEnd } catch(e) { return false } } : function(te) { - try {var range = te.ownerDocument.selection.createRange()} + let range + try {range = te.ownerDocument.selection.createRange()} catch(e) {} if (!range || range.parentElement() != te) return false return range.compareEndPoints("StartToEnd", range) != 0 } -export var hasCopyEvent = (function() { - var e = elt("div") +export let hasCopyEvent = (function() { + let e = elt("div") if ("oncopy" in e) return true e.setAttribute("oncopy", "return;") return typeof e.oncopy == "function" })() -var badZoomedRects = null +let badZoomedRects = null export 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() + let node = removeChildrenAndAdd(measure, elt("span", "x")) + let normal = node.getBoundingClientRect() + let fromRange = range(node, 0, 1).getBoundingClientRect() return badZoomedRects = Math.abs(normal.left - fromRange.left) > 1 } diff --git a/src/util/misc.js b/src/util/misc.js index a6e3340d..c94de8bd 100644 --- a/src/util/misc.js +++ b/src/util/misc.js @@ -1,11 +1,11 @@ export function bind(f) { - var args = Array.prototype.slice.call(arguments, 1) + let args = Array.prototype.slice.call(arguments, 1) return function(){return f.apply(null, args)} } export function copyObj(obj, target, overwrite) { if (!target) target = {} - for (var prop in obj) + for (let prop in obj) if (obj.hasOwnProperty(prop) && (overwrite !== false || !target.hasOwnProperty(prop))) target[prop] = obj[prop] return target @@ -18,8 +18,8 @@ export function countColumn(string, end, tabSize, startIndex, startValue) { end = string.search(/[^\s\u00a0]/) if (end == -1) end = string.length } - for (var i = startIndex || 0, n = startValue || 0;;) { - var nextTab = string.indexOf("\t", i) + for (let i = startIndex || 0, n = startValue || 0;;) { + let nextTab = string.indexOf("\t", i) if (nextTab < 0 || nextTab >= end) return n + (end - i) n += nextTab - i @@ -35,28 +35,28 @@ Delayed.prototype.set = function(ms, f) { } export function indexOf(array, elt) { - for (var i = 0; i < array.length; ++i) + for (let i = 0; i < array.length; ++i) if (array[i] == elt) return i return -1 } // Number of pixels added to scroller and sizer to hide scrollbar -export var scrollerGap = 30 +export let scrollerGap = 30 // Returned or thrown by various protocols to signal 'I'm not // handling this'. -export var Pass = {toString: function(){return "CodeMirror.Pass"}} +export let Pass = {toString: function(){return "CodeMirror.Pass"}} // Reused option objects for setSelection & friends -export var sel_dontScroll = {scroll: false}, sel_mouse = {origin: "*mouse"}, sel_move = {origin: "+move"} +export let sel_dontScroll = {scroll: false}, sel_mouse = {origin: "*mouse"}, sel_move = {origin: "+move"} // The inverse of countColumn -- find the offset that corresponds to // a particular column. export function findColumn(string, goal, tabSize) { - for (var pos = 0, col = 0;;) { - var nextTab = string.indexOf("\t", pos) + for (let pos = 0, col = 0;;) { + let nextTab = string.indexOf("\t", pos) if (nextTab == -1) nextTab = string.length - var skipped = nextTab - pos + let skipped = nextTab - pos if (nextTab == string.length || col + skipped >= goal) return pos + Math.min(skipped, goal - col) col += nextTab - pos @@ -66,7 +66,7 @@ export function findColumn(string, goal, tabSize) { } } -var spaceStrs = [""] +let spaceStrs = [""] export function spaceStr(n) { while (spaceStrs.length <= n) spaceStrs.push(lst(spaceStrs) + " ") @@ -76,13 +76,13 @@ export function spaceStr(n) { export function lst(arr) { return arr[arr.length-1] } export function map(array, f) { - var out = [] - for (var i = 0; i < array.length; i++) out[i] = f(array[i], i) + let out = [] + for (let i = 0; i < array.length; i++) out[i] = f(array[i], i) return out } export function insertSorted(array, value, score) { - var pos = 0, priority = score(value) + let pos = 0, priority = score(value) while (pos < array.length && score(array[pos]) <= priority) pos++ array.splice(pos, 0, value) } @@ -90,7 +90,7 @@ export function insertSorted(array, value, score) { export function nothing() {} export function createObj(base, props) { - var inst + let inst if (Object.create) { inst = Object.create(base) } else { @@ -101,7 +101,7 @@ export function createObj(base, props) { return inst } -var nonASCIISingleCaseWordChar = /[\u00df\u0587\u0590-\u05f4\u0600-\u06ff\u3040-\u309f\u30a0-\u30ff\u3400-\u4db5\u4e00-\u9fcc\uac00-\ud7af]/ +let nonASCIISingleCaseWordChar = /[\u00df\u0587\u0590-\u05f4\u0600-\u06ff\u3040-\u309f\u30a0-\u30ff\u3400-\u4db5\u4e00-\u9fcc\uac00-\ud7af]/ export function isWordCharBasic(ch) { return /\w/.test(ch) || ch > "\x80" && (ch.toUpperCase() != ch.toLowerCase() || nonASCIISingleCaseWordChar.test(ch)) @@ -113,7 +113,7 @@ export function isWordChar(ch, helper) { } export function isEmpty(obj) { - for (var n in obj) if (obj.hasOwnProperty(n) && obj[n]) return false + for (let n in obj) if (obj.hasOwnProperty(n) && obj[n]) return false return true } @@ -122,5 +122,5 @@ export function isEmpty(obj) { // as editing and measuring is concerned. This is not fully correct, // since some scripts/fonts/browsers also treat other configurations // of code points as a group. -var extendingChars = /[\u0300-\u036f\u0483-\u0489\u0591-\u05bd\u05bf\u05c1\u05c2\u05c4\u05c5\u05c7\u0610-\u061a\u064b-\u065e\u0670\u06d6-\u06dc\u06de-\u06e4\u06e7\u06e8\u06ea-\u06ed\u0711\u0730-\u074a\u07a6-\u07b0\u07eb-\u07f3\u0816-\u0819\u081b-\u0823\u0825-\u0827\u0829-\u082d\u0900-\u0902\u093c\u0941-\u0948\u094d\u0951-\u0955\u0962\u0963\u0981\u09bc\u09be\u09c1-\u09c4\u09cd\u09d7\u09e2\u09e3\u0a01\u0a02\u0a3c\u0a41\u0a42\u0a47\u0a48\u0a4b-\u0a4d\u0a51\u0a70\u0a71\u0a75\u0a81\u0a82\u0abc\u0ac1-\u0ac5\u0ac7\u0ac8\u0acd\u0ae2\u0ae3\u0b01\u0b3c\u0b3e\u0b3f\u0b41-\u0b44\u0b4d\u0b56\u0b57\u0b62\u0b63\u0b82\u0bbe\u0bc0\u0bcd\u0bd7\u0c3e-\u0c40\u0c46-\u0c48\u0c4a-\u0c4d\u0c55\u0c56\u0c62\u0c63\u0cbc\u0cbf\u0cc2\u0cc6\u0ccc\u0ccd\u0cd5\u0cd6\u0ce2\u0ce3\u0d3e\u0d41-\u0d44\u0d4d\u0d57\u0d62\u0d63\u0dca\u0dcf\u0dd2-\u0dd4\u0dd6\u0ddf\u0e31\u0e34-\u0e3a\u0e47-\u0e4e\u0eb1\u0eb4-\u0eb9\u0ebb\u0ebc\u0ec8-\u0ecd\u0f18\u0f19\u0f35\u0f37\u0f39\u0f71-\u0f7e\u0f80-\u0f84\u0f86\u0f87\u0f90-\u0f97\u0f99-\u0fbc\u0fc6\u102d-\u1030\u1032-\u1037\u1039\u103a\u103d\u103e\u1058\u1059\u105e-\u1060\u1071-\u1074\u1082\u1085\u1086\u108d\u109d\u135f\u1712-\u1714\u1732-\u1734\u1752\u1753\u1772\u1773\u17b7-\u17bd\u17c6\u17c9-\u17d3\u17dd\u180b-\u180d\u18a9\u1920-\u1922\u1927\u1928\u1932\u1939-\u193b\u1a17\u1a18\u1a56\u1a58-\u1a5e\u1a60\u1a62\u1a65-\u1a6c\u1a73-\u1a7c\u1a7f\u1b00-\u1b03\u1b34\u1b36-\u1b3a\u1b3c\u1b42\u1b6b-\u1b73\u1b80\u1b81\u1ba2-\u1ba5\u1ba8\u1ba9\u1c2c-\u1c33\u1c36\u1c37\u1cd0-\u1cd2\u1cd4-\u1ce0\u1ce2-\u1ce8\u1ced\u1dc0-\u1de6\u1dfd-\u1dff\u200c\u200d\u20d0-\u20f0\u2cef-\u2cf1\u2de0-\u2dff\u302a-\u302f\u3099\u309a\ua66f-\ua672\ua67c\ua67d\ua6f0\ua6f1\ua802\ua806\ua80b\ua825\ua826\ua8c4\ua8e0-\ua8f1\ua926-\ua92d\ua947-\ua951\ua980-\ua982\ua9b3\ua9b6-\ua9b9\ua9bc\uaa29-\uaa2e\uaa31\uaa32\uaa35\uaa36\uaa43\uaa4c\uaab0\uaab2-\uaab4\uaab7\uaab8\uaabe\uaabf\uaac1\uabe5\uabe8\uabed\udc00-\udfff\ufb1e\ufe00-\ufe0f\ufe20-\ufe26\uff9e\uff9f]/ +let extendingChars = /[\u0300-\u036f\u0483-\u0489\u0591-\u05bd\u05bf\u05c1\u05c2\u05c4\u05c5\u05c7\u0610-\u061a\u064b-\u065e\u0670\u06d6-\u06dc\u06de-\u06e4\u06e7\u06e8\u06ea-\u06ed\u0711\u0730-\u074a\u07a6-\u07b0\u07eb-\u07f3\u0816-\u0819\u081b-\u0823\u0825-\u0827\u0829-\u082d\u0900-\u0902\u093c\u0941-\u0948\u094d\u0951-\u0955\u0962\u0963\u0981\u09bc\u09be\u09c1-\u09c4\u09cd\u09d7\u09e2\u09e3\u0a01\u0a02\u0a3c\u0a41\u0a42\u0a47\u0a48\u0a4b-\u0a4d\u0a51\u0a70\u0a71\u0a75\u0a81\u0a82\u0abc\u0ac1-\u0ac5\u0ac7\u0ac8\u0acd\u0ae2\u0ae3\u0b01\u0b3c\u0b3e\u0b3f\u0b41-\u0b44\u0b4d\u0b56\u0b57\u0b62\u0b63\u0b82\u0bbe\u0bc0\u0bcd\u0bd7\u0c3e-\u0c40\u0c46-\u0c48\u0c4a-\u0c4d\u0c55\u0c56\u0c62\u0c63\u0cbc\u0cbf\u0cc2\u0cc6\u0ccc\u0ccd\u0cd5\u0cd6\u0ce2\u0ce3\u0d3e\u0d41-\u0d44\u0d4d\u0d57\u0d62\u0d63\u0dca\u0dcf\u0dd2-\u0dd4\u0dd6\u0ddf\u0e31\u0e34-\u0e3a\u0e47-\u0e4e\u0eb1\u0eb4-\u0eb9\u0ebb\u0ebc\u0ec8-\u0ecd\u0f18\u0f19\u0f35\u0f37\u0f39\u0f71-\u0f7e\u0f80-\u0f84\u0f86\u0f87\u0f90-\u0f97\u0f99-\u0fbc\u0fc6\u102d-\u1030\u1032-\u1037\u1039\u103a\u103d\u103e\u1058\u1059\u105e-\u1060\u1071-\u1074\u1082\u1085\u1086\u108d\u109d\u135f\u1712-\u1714\u1732-\u1734\u1752\u1753\u1772\u1773\u17b7-\u17bd\u17c6\u17c9-\u17d3\u17dd\u180b-\u180d\u18a9\u1920-\u1922\u1927\u1928\u1932\u1939-\u193b\u1a17\u1a18\u1a56\u1a58-\u1a5e\u1a60\u1a62\u1a65-\u1a6c\u1a73-\u1a7c\u1a7f\u1b00-\u1b03\u1b34\u1b36-\u1b3a\u1b3c\u1b42\u1b6b-\u1b73\u1b80\u1b81\u1ba2-\u1ba5\u1ba8\u1ba9\u1c2c-\u1c33\u1c36\u1c37\u1cd0-\u1cd2\u1cd4-\u1ce0\u1ce2-\u1ce8\u1ced\u1dc0-\u1de6\u1dfd-\u1dff\u200c\u200d\u20d0-\u20f0\u2cef-\u2cf1\u2de0-\u2dff\u302a-\u302f\u3099\u309a\ua66f-\ua672\ua67c\ua67d\ua6f0\ua6f1\ua802\ua806\ua80b\ua825\ua826\ua8c4\ua8e0-\ua8f1\ua926-\ua92d\ua947-\ua951\ua980-\ua982\ua9b3\ua9b6-\ua9b9\ua9bc\uaa29-\uaa2e\uaa31\uaa32\uaa35\uaa36\uaa43\uaa4c\uaab0\uaab2-\uaab4\uaab7\uaab8\uaabe\uaabf\uaac1\uabe5\uabe8\uabed\udc00-\udfff\ufb1e\ufe00-\ufe0f\ufe20-\ufe26\uff9e\uff9f]/ export function isExtendingChar(ch) { return ch.charCodeAt(0) >= 768 && extendingChars.test(ch) } diff --git a/src/util/operation_group.js b/src/util/operation_group.js index d48f6eb3..d743f994 100644 --- a/src/util/operation_group.js +++ b/src/util/operation_group.js @@ -1,6 +1,6 @@ import { getHandlers } from "./event" -var operationGroup = null +let operationGroup = null export function pushOperation(op) { if (operationGroup) { @@ -16,12 +16,12 @@ export function pushOperation(op) { function fireCallbacksForOps(group) { // Calls delayed callbacks and cursorActivity handlers until no // new ones appear - var callbacks = group.delayedCallbacks, i = 0 + let callbacks = group.delayedCallbacks, i = 0 do { for (; i < callbacks.length; i++) callbacks[i].call(null) - for (var j = 0; j < group.ops.length; j++) { - var op = group.ops[j] + for (let j = 0; j < group.ops.length; j++) { + let op = group.ops[j] if (op.cursorActivityHandlers) while (op.cursorActivityCalled < op.cursorActivityHandlers.length) op.cursorActivityHandlers[op.cursorActivityCalled++].call(null, op.cm) @@ -30,7 +30,7 @@ function fireCallbacksForOps(group) { } export function finishOperation(op, endCb) { - var group = op.ownsGroup + let group = op.ownsGroup if (!group) return try { fireCallbacksForOps(group) } @@ -40,7 +40,7 @@ export function finishOperation(op, endCb) { } } -var orphanDelayedCallbacks = null +let 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 @@ -50,9 +50,9 @@ var orphanDelayedCallbacks = null // them to be executed when the last operation ends, or, if no // operation is active, when a timeout fires. export function signalLater(emitter, type /*, values...*/) { - var arr = getHandlers(emitter, type, false) + let arr = getHandlers(emitter, type, false) if (!arr.length) return - var args = Array.prototype.slice.call(arguments, 2), list + let args = Array.prototype.slice.call(arguments, 2), list if (operationGroup) { list = operationGroup.delayedCallbacks } else if (orphanDelayedCallbacks) { @@ -62,12 +62,12 @@ export function signalLater(emitter, type /*, values...*/) { setTimeout(fireOrphanDelayed, 0) } function bnd(f) {return function(){f.apply(null, args)}} - for (var i = 0; i < arr.length; ++i) + for (let i = 0; i < arr.length; ++i) list.push(bnd(arr[i])) } function fireOrphanDelayed() { - var delayed = orphanDelayedCallbacks + let delayed = orphanDelayedCallbacks orphanDelayedCallbacks = null - for (var i = 0; i < delayed.length; ++i) delayed[i]() + for (let i = 0; i < delayed.length; ++i) delayed[i]() } From 6992c534ad33ce60e7500fe04a99a481ef8f548e Mon Sep 17 00:00:00 2001 From: Marijn Haverbeke Date: Thu, 29 Sep 2016 11:16:44 +0200 Subject: [PATCH 0460/1790] =?UTF-8?q?Change=20one=20let=20back=20to=20var?= =?UTF-8?q?=20to=20work=20around=20a=20Bubl=C3=A9=20issue?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Issue #4261 --- src/model/history.js | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/model/history.js b/src/model/history.js index 0c7cf5bb..2a7502cb 100644 --- a/src/model/history.js +++ b/src/model/history.js @@ -216,7 +216,7 @@ export function copyHistoryArray(events, newGroup, instantiateSel) { for (let j = 0; j < changes.length; ++j) { let change = changes[j], m newChanges.push({from: change.from, to: change.to, text: change.text}) - if (newGroup) for (let prop in change) if (m = prop.match(/^spans_(\d+)$/)) { + 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] From 9ad40ee76ffba6ab90f990b5b5bf1490b32410ef Mon Sep 17 00:00:00 2001 From: Adrian Heine Date: Thu, 29 Sep 2016 11:00:09 +0200 Subject: [PATCH 0461/1790] Convert anonymous to arrow functions --- src/display/focus.js | 14 +- src/display/highlight_worker.js | 4 +- src/display/mode_state.js | 2 +- src/display/operations.js | 2 +- src/display/scroll_events.js | 4 +- src/display/scrollbars.js | 12 +- src/display/selection.js | 7 +- src/edit/CodeMirror.js | 40 ++-- src/edit/commands.js | 252 +++++++++++------------- src/edit/deleteNearSelection.js | 2 +- src/edit/drop_events.js | 6 +- src/edit/fromTextArea.js | 13 +- src/edit/global_events.js | 8 +- src/edit/key_events.js | 11 +- src/edit/main.js | 8 +- src/edit/methods.js | 24 ++- src/edit/mouse_events.js | 14 +- src/edit/options.js | 58 +++--- src/input/ContentEditableInput.js | 36 ++-- src/input/TextareaInput.js | 20 +- src/input/input.js | 4 +- src/line/highlight.js | 21 +- src/line/line_data.js | 2 +- src/line/spans.js | 4 +- src/line/utils_line.js | 4 +- src/measurement/position_measurement.js | 4 +- src/model/Doc.js | 14 +- src/model/changes.js | 28 +-- src/model/history.js | 4 +- src/model/line_widget.js | 6 +- src/model/mark_text.js | 15 +- src/model/selection.js | 2 +- src/util/StringStream.js | 2 +- src/util/feature_detection.js | 10 +- src/util/operation_group.js | 3 +- 35 files changed, 305 insertions(+), 355 deletions(-) diff --git a/src/display/focus.js b/src/display/focus.js index e6a9f521..ee52daff 100644 --- a/src/display/focus.js +++ b/src/display/focus.js @@ -9,12 +9,10 @@ export function ensureFocus(cm) { export function delayBlurEvent(cm) { cm.state.delayingBlurEvent = true - setTimeout(function() { - if (cm.state.delayingBlurEvent) { - cm.state.delayingBlurEvent = false - onBlur(cm) - } - }, 100) + setTimeout(() => { if (cm.state.delayingBlurEvent) { + cm.state.delayingBlurEvent = false + onBlur(cm) + } }, 100) } export function onFocus(cm, e) { @@ -30,7 +28,7 @@ export function onFocus(cm, e) { // select-all detection hack) if (!cm.curOp && cm.display.selForContextMenu != cm.doc.sel) { cm.display.input.reset() - if (webkit) setTimeout(function() { cm.display.input.reset(true) }, 20) // Issue #1730 + if (webkit) setTimeout(() => cm.display.input.reset(true), 20) // Issue #1730 } cm.display.input.receivedFocus() } @@ -45,5 +43,5 @@ export function onBlur(cm, e) { rmClass(cm.display.wrapper, "CodeMirror-focused") } clearInterval(cm.display.blinker) - setTimeout(function() {if (!cm.state.focused) cm.display.shift = false}, 150) + setTimeout(() => { if (!cm.state.focused) cm.display.shift = false }, 150) } diff --git a/src/display/highlight_worker.js b/src/display/highlight_worker.js index 5d535abe..d34db2b4 100644 --- a/src/display/highlight_worker.js +++ b/src/display/highlight_worker.js @@ -20,7 +20,7 @@ function highlightWorker(cm) { let state = copyState(doc.mode, getStateBefore(cm, doc.frontier)) let changedLines = [] - doc.iter(doc.frontier, Math.min(doc.first + doc.size, cm.display.viewTo + 500), function(line) { + doc.iter(doc.frontier, Math.min(doc.first + doc.size, cm.display.viewTo + 500), line => { if (doc.frontier >= cm.display.viewFrom) { // Visible let oldStyles = line.styles, tooLong = line.text.length > cm.options.maxHighlightLength let highlighted = highlightLine(cm, line, tooLong ? copyState(doc.mode, state) : state, true) @@ -44,7 +44,7 @@ function highlightWorker(cm) { return true } }) - if (changedLines.length) runInOp(cm, function() { + if (changedLines.length) runInOp(cm, () => { for (let i = 0; i < changedLines.length; i++) regLineChange(cm, changedLines[i], "text") }) diff --git a/src/display/mode_state.js b/src/display/mode_state.js index 7742c722..8c4c60b0 100644 --- a/src/display/mode_state.js +++ b/src/display/mode_state.js @@ -11,7 +11,7 @@ export function loadMode(cm) { } export function resetModeState(cm) { - cm.doc.iter(function(line) { + cm.doc.iter(line => { if (line.stateAfter) line.stateAfter = null if (line.styles) line.styles = null }) diff --git a/src/display/operations.js b/src/display/operations.js index cc6eef7b..30a63a61 100644 --- a/src/display/operations.js +++ b/src/display/operations.js @@ -46,7 +46,7 @@ export function startOperation(cm) { // Finish an operation, updating the display and signalling delayed events export function endOperation(cm) { let op = cm.curOp - finishOperation(op, function(group) { + finishOperation(op, group => { for (let i = 0; i < group.ops.length; i++) group.ops[i].cm.curOp = null endOperations(group) diff --git a/src/display/scroll_events.js b/src/display/scroll_events.js index 3c166b48..e85d2a02 100644 --- a/src/display/scroll_events.js +++ b/src/display/scroll_events.js @@ -48,7 +48,7 @@ else if (gecko) wheelPixelsPerUnit = 15 else if (chrome) wheelPixelsPerUnit = -.7 else if (safari) wheelPixelsPerUnit = -1/3 -let wheelEventDelta = function(e) { +function wheelEventDelta(e) { let dx = e.wheelDeltaX, dy = e.wheelDeltaY if (dx == null && e.detail && e.axis == e.HORIZONTAL_AXIS) dx = e.detail if (dy == null && e.detail && e.axis == e.VERTICAL_AXIS) dy = e.detail @@ -120,7 +120,7 @@ export function onScrollWheel(cm, e) { if (display.wheelStartX == null) { display.wheelStartX = scroll.scrollLeft; display.wheelStartY = scroll.scrollTop display.wheelDX = dx; display.wheelDY = dy - setTimeout(function() { + setTimeout(() => { if (display.wheelStartX == null) return let movedX = scroll.scrollLeft - display.wheelStartX let movedY = scroll.scrollTop - display.wheelStartY diff --git a/src/display/scrollbars.js b/src/display/scrollbars.js index a07f2a16..a85fffe9 100644 --- a/src/display/scrollbars.js +++ b/src/display/scrollbars.js @@ -33,10 +33,10 @@ function NativeScrollbars(place, scroll, cm) { let horiz = this.horiz = elt("div", [elt("div", null, null, "height: 100%; min-height: 1px")], "CodeMirror-hscrollbar") place(vert); place(horiz) - on(vert, "scroll", function() { + on(vert, "scroll", () => { if (vert.clientHeight) scroll(vert.scrollTop, "vertical") }) - on(horiz, "scroll", function() { + on(horiz, "scroll", () => { if (horiz.clientWidth) scroll(horiz.scrollLeft, "horizontal") }) @@ -172,14 +172,14 @@ export function initScrollbars(cm) { rmClass(cm.display.wrapper, cm.display.scrollbars.addClass) } - cm.display.scrollbars = new scrollbarModel[cm.options.scrollbarStyle](function(node) { + cm.display.scrollbars = new scrollbarModel[cm.options.scrollbarStyle](node => { cm.display.wrapper.insertBefore(node, cm.display.scrollbarFiller) // Prevent clicks in the scrollbars from killing focus - on(node, "mousedown", function() { - if (cm.state.focused) setTimeout(function() { cm.display.input.focus() }, 0) + on(node, "mousedown", () => { + if (cm.state.focused) setTimeout(() => cm.display.input.focus(), 0) }) node.setAttribute("cm-not-content", "true") - }, function(pos, axis) { + }, (pos, axis) => { if (axis == "horizontal") setScrollLeft(cm, pos) else setScrollTop(cm, pos) }, cm) diff --git a/src/display/selection.js b/src/display/selection.js index 9fa8f6ee..8380de82 100644 --- a/src/display/selection.js +++ b/src/display/selection.js @@ -70,7 +70,7 @@ function drawSelectionRange(cm, range, output) { return charCoords(cm, Pos(line, ch), "div", lineObj, bias) } - iterateBidiSections(getOrder(lineObj), fromArg || 0, toArg == null ? lineLen : toArg, function(from, to, dir) { + iterateBidiSections(getOrder(lineObj), fromArg || 0, toArg == null ? lineLen : toArg, (from, to, dir) => { let leftPos = coords(from, "left"), rightPos, left, right if (from == to) { rightPos = leftPos @@ -129,9 +129,8 @@ export function restartBlink(cm) { let on = true display.cursorDiv.style.visibility = "" if (cm.options.cursorBlinkRate > 0) - display.blinker = setInterval(function() { - display.cursorDiv.style.visibility = (on = !on) ? "" : "hidden" - }, cm.options.cursorBlinkRate) + display.blinker = setInterval(() => display.cursorDiv.style.visibility = (on = !on) ? "" : "hidden", + cm.options.cursorBlinkRate) else if (cm.options.cursorBlinkRate < 0) display.cursorDiv.style.visibility = "hidden" } diff --git a/src/edit/CodeMirror.js b/src/edit/CodeMirror.js index 90650ff0..909ffcb1 100644 --- a/src/edit/CodeMirror.js +++ b/src/edit/CodeMirror.js @@ -68,7 +68,7 @@ export function CodeMirror(place, options) { // Override magic textarea content restore that IE sometimes does // on our hidden textarea on reload - if (ie && ie_version < 11) setTimeout(function() { cm.display.input.reset(true) }, 20) + if (ie && ie_version < 11) setTimeout(() => cm.display.input.reset(true), 20) registerEventHandlers(this) ensureGlobalHandlers() @@ -108,7 +108,7 @@ function registerEventHandlers(cm) { on(d.scroller, "mousedown", operation(cm, onMouseDown)) // Older IE's will not fire a second mousedown for a double click if (ie && ie_version < 11) - on(d.scroller, "dblclick", operation(cm, function(e) { + on(d.scroller, "dblclick", operation(cm, e => { if (signalDOMEvent(cm, e)) return let pos = posFromMouse(cm, e) if (!pos || clickInGutter(cm, e) || eventInWidget(cm.display, e)) return @@ -117,17 +117,17 @@ function registerEventHandlers(cm) { extendSelection(cm.doc, word.anchor, word.head) })) else - on(d.scroller, "dblclick", function(e) { signalDOMEvent(cm, e) || e_preventDefault(e) }) + on(d.scroller, "dblclick", e => signalDOMEvent(cm, e) || e_preventDefault(e)) // Some browsers fire contextmenu *after* opening the menu, at // which point we can't mess with it anymore. Context menu is // handled in onMouseDown for these browsers. - if (!captureRightClick) on(d.scroller, "contextmenu", function(e) {onContextMenu(cm, e)}) + if (!captureRightClick) on(d.scroller, "contextmenu", e => onContextMenu(cm, e)) // Used to suppress mouse event handling when a touch happens let touchFinished, prevTouch = {end: 0} function finishTouch() { if (d.activeTouch) { - touchFinished = setTimeout(function() {d.activeTouch = null}, 1000) + touchFinished = setTimeout(() => d.activeTouch = null, 1000) prevTouch = d.activeTouch prevTouch.end = +new Date } @@ -142,7 +142,7 @@ function registerEventHandlers(cm) { let dx = other.left - touch.left, dy = other.top - touch.top return dx * dx + dy * dy > 20 * 20 } - on(d.scroller, "touchstart", function(e) { + on(d.scroller, "touchstart", e => { if (!signalDOMEvent(cm, e) && !isMouseLikeTouchEvent(e)) { clearTimeout(touchFinished) let now = +new Date @@ -154,10 +154,10 @@ function registerEventHandlers(cm) { } } }) - on(d.scroller, "touchmove", function() { + on(d.scroller, "touchmove", () => { if (d.activeTouch) d.activeTouch.moved = true }) - on(d.scroller, "touchend", function(e) { + on(d.scroller, "touchend", e => { let touch = d.activeTouch if (touch && !eventInWidget(d, e) && touch.left != null && !touch.moved && new Date - touch.start < 300) { @@ -178,7 +178,7 @@ function registerEventHandlers(cm) { // Sync scrolling between fake scrollbars and real scrollable // area, ensure viewport is updated when scrolling. - on(d.scroller, "scroll", function() { + on(d.scroller, "scroll", () => { if (d.scroller.clientHeight) { setScrollTop(cm, d.scroller.scrollTop) setScrollLeft(cm, d.scroller.scrollLeft, true) @@ -187,27 +187,27 @@ function registerEventHandlers(cm) { }) // Listen to wheel events in order to try and update the viewport on time. - on(d.scroller, "mousewheel", function(e){onScrollWheel(cm, e)}) - on(d.scroller, "DOMMouseScroll", function(e){onScrollWheel(cm, e)}) + on(d.scroller, "mousewheel", e => onScrollWheel(cm, e)) + on(d.scroller, "DOMMouseScroll", e => onScrollWheel(cm, e)) // Prevent wrapper from ever scrolling - on(d.wrapper, "scroll", function() { d.wrapper.scrollTop = d.wrapper.scrollLeft = 0 }) + on(d.wrapper, "scroll", () => d.wrapper.scrollTop = d.wrapper.scrollLeft = 0) d.dragFunctions = { - enter: function(e) {if (!signalDOMEvent(cm, e)) e_stop(e)}, - over: function(e) {if (!signalDOMEvent(cm, e)) { onDragOver(cm, e); e_stop(e) }}, - start: function(e){onDragStart(cm, e)}, + enter: e => {if (!signalDOMEvent(cm, e)) e_stop(e)}, + over: e => {if (!signalDOMEvent(cm, e)) { onDragOver(cm, e); e_stop(e) }}, + start: e => onDragStart(cm, e), drop: operation(cm, onDrop), - leave: function(e) {if (!signalDOMEvent(cm, e)) { clearDragCursor(cm) }} + leave: e => {if (!signalDOMEvent(cm, e)) { clearDragCursor(cm) }} } let inp = d.input.getField() - on(inp, "keyup", function(e) { onKeyUp.call(cm, e) }) + on(inp, "keyup", e => onKeyUp.call(cm, e)) on(inp, "keydown", operation(cm, onKeyDown)) on(inp, "keypress", operation(cm, onKeyPress)) - on(inp, "focus", function(e) { onFocus(cm, e) }) - on(inp, "blur", function (e) { onBlur(cm, e) }) + on(inp, "focus", e => onFocus(cm, e)) + on(inp, "blur", e => onBlur(cm, e)) } let initHooks = [] -CodeMirror.defineInitHook = function(f) {initHooks.push(f)} +CodeMirror.defineInitHook = f => initHooks.push(f) diff --git a/src/edit/commands.js b/src/edit/commands.js index 97a30d6d..fe39ed72 100644 --- a/src/edit/commands.js +++ b/src/edit/commands.js @@ -13,109 +13,87 @@ import { getOrder, lineLeft, lineRight } from "../util/bidi" // editor, mostly used for keybindings. export let commands = { selectAll: selectAll, - singleSelection: function(cm) { - cm.setSelection(cm.getCursor("anchor"), cm.getCursor("head"), sel_dontScroll) - }, - killLine: function(cm) { - deleteNearSelection(cm, function(range) { - if (range.empty()) { - let len = getLine(cm.doc, range.head.line).text.length - if (range.head.ch == len && range.head.line < cm.lastLine()) - return {from: range.head, to: Pos(range.head.line + 1, 0)} - else - return {from: range.head, to: Pos(range.head.line, len)} - } else { - return {from: range.from(), to: range.to()} - } - }) - }, - deleteLine: function(cm) { - deleteNearSelection(cm, function(range) { - return {from: Pos(range.from().line, 0), - to: clipPos(cm.doc, Pos(range.to().line + 1, 0))} - }) - }, - delLineLeft: function(cm) { - deleteNearSelection(cm, function(range) { - return {from: Pos(range.from().line, 0), to: range.from()} - }) - }, - delWrappedLineLeft: function(cm) { - deleteNearSelection(cm, function(range) { - let top = cm.charCoords(range.head, "div").top + 5 - let leftPos = cm.coordsChar({left: 0, top: top}, "div") - return {from: leftPos, to: range.from()} - }) - }, - delWrappedLineRight: function(cm) { - deleteNearSelection(cm, function(range) { - let top = cm.charCoords(range.head, "div").top + 5 - let 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()}, - redoSelection: function(cm) {cm.redoSelection()}, - goDocStart: function(cm) {cm.extendSelection(Pos(cm.firstLine(), 0))}, - goDocEnd: function(cm) {cm.extendSelection(Pos(cm.lastLine()))}, - goLineStart: function(cm) { - cm.extendSelectionsBy(function(range) { return lineStart(cm, range.head.line) }, - {origin: "+move", bias: 1}) - }, - goLineStartSmart: function(cm) { - cm.extendSelectionsBy(function(range) { - return lineStartSmart(cm, range.head) - }, {origin: "+move", bias: 1}) - }, - goLineEnd: function(cm) { - cm.extendSelectionsBy(function(range) { return lineEnd(cm, range.head.line) }, - {origin: "+move", bias: -1}) - }, - goLineRight: function(cm) { - cm.extendSelectionsBy(function(range) { - let top = cm.charCoords(range.head, "div").top + 5 - return cm.coordsChar({left: cm.display.lineDiv.offsetWidth + 100, top: top}, "div") - }, sel_move) - }, - goLineLeft: function(cm) { - cm.extendSelectionsBy(function(range) { - let top = cm.charCoords(range.head, "div").top + 5 - return cm.coordsChar({left: 0, top: top}, "div") - }, sel_move) - }, - goLineLeftSmart: function(cm) { - cm.extendSelectionsBy(function(range) { - let top = cm.charCoords(range.head, "div").top + 5 - let pos = cm.coordsChar({left: 0, top: top}, "div") - if (pos.ch < cm.getLine(pos.line).search(/\S/)) return lineStartSmart(cm, range.head) - return pos - }, sel_move) - }, - goLineUp: function(cm) {cm.moveV(-1, "line")}, - goLineDown: function(cm) {cm.moveV(1, "line")}, - goPageUp: function(cm) {cm.moveV(-1, "page")}, - goPageDown: function(cm) {cm.moveV(1, "page")}, - goCharLeft: function(cm) {cm.moveH(-1, "char")}, - goCharRight: function(cm) {cm.moveH(1, "char")}, - 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")}, - insertTab: function(cm) {cm.replaceSelection("\t")}, - insertSoftTab: function(cm) { + singleSelection: cm => cm.setSelection(cm.getCursor("anchor"), cm.getCursor("head"), sel_dontScroll), + killLine: cm => deleteNearSelection(cm, range => { + if (range.empty()) { + let len = getLine(cm.doc, range.head.line).text.length + if (range.head.ch == len && range.head.line < cm.lastLine()) + return {from: range.head, to: Pos(range.head.line + 1, 0)} + else + return {from: range.head, to: Pos(range.head.line, len)} + } else { + return {from: range.from(), to: range.to()} + } + }), + deleteLine: cm => deleteNearSelection(cm, range => ({ + from: Pos(range.from().line, 0), + to: clipPos(cm.doc, Pos(range.to().line + 1, 0)) + })), + delLineLeft: cm => deleteNearSelection(cm, range => ({ + from: Pos(range.from().line, 0), to: range.from() + })), + delWrappedLineLeft: cm => deleteNearSelection(cm, range => { + let top = cm.charCoords(range.head, "div").top + 5 + let leftPos = cm.coordsChar({left: 0, top: top}, "div") + return {from: leftPos, to: range.from()} + }), + delWrappedLineRight: cm => deleteNearSelection(cm, range => { + let top = cm.charCoords(range.head, "div").top + 5 + let rightPos = cm.coordsChar({left: cm.display.lineDiv.offsetWidth + 100, top: top}, "div") + return {from: range.from(), to: rightPos } + }), + undo: cm => cm.undo(), + redo: cm => cm.redo(), + undoSelection: cm => cm.undoSelection(), + redoSelection: cm => cm.redoSelection(), + goDocStart: cm => cm.extendSelection(Pos(cm.firstLine(), 0)), + goDocEnd: cm => cm.extendSelection(Pos(cm.lastLine())), + goLineStart: cm => cm.extendSelectionsBy(range => lineStart(cm, range.head.line), + {origin: "+move", bias: 1} + ), + goLineStartSmart: cm => cm.extendSelectionsBy(range => lineStartSmart(cm, range.head), + {origin: "+move", bias: 1} + ), + goLineEnd: cm => cm.extendSelectionsBy(range => lineEnd(cm, range.head.line), + {origin: "+move", bias: -1} + ), + goLineRight: cm => cm.extendSelectionsBy(range => { + let top = cm.charCoords(range.head, "div").top + 5 + return cm.coordsChar({left: cm.display.lineDiv.offsetWidth + 100, top: top}, "div") + }, sel_move), + goLineLeft: cm => cm.extendSelectionsBy(range => { + let top = cm.charCoords(range.head, "div").top + 5 + return cm.coordsChar({left: 0, top: top}, "div") + }, sel_move), + goLineLeftSmart: cm => cm.extendSelectionsBy(range => { + let top = cm.charCoords(range.head, "div").top + 5 + let pos = cm.coordsChar({left: 0, top: top}, "div") + if (pos.ch < cm.getLine(pos.line).search(/\S/)) return lineStartSmart(cm, range.head) + return pos + }, sel_move), + goLineUp: cm => cm.moveV(-1, "line"), + goLineDown: cm => cm.moveV(1, "line"), + goPageUp: cm => cm.moveV(-1, "page"), + goPageDown: cm => cm.moveV(1, "page"), + goCharLeft: cm => cm.moveH(-1, "char"), + goCharRight: cm => cm.moveH(1, "char"), + goColumnLeft: cm => cm.moveH(-1, "column"), + goColumnRight: cm => cm.moveH(1, "column"), + goWordLeft: cm => cm.moveH(-1, "word"), + goGroupRight: cm => cm.moveH(1, "group"), + goGroupLeft: cm => cm.moveH(-1, "group"), + goWordRight: cm => cm.moveH(1, "word"), + delCharBefore: cm => cm.deleteH(-1, "char"), + delCharAfter: cm => cm.deleteH(1, "char"), + delWordBefore: cm => cm.deleteH(-1, "word"), + delWordAfter: cm => cm.deleteH(1, "word"), + delGroupBefore: cm => cm.deleteH(-1, "group"), + delGroupAfter: cm => cm.deleteH(1, "group"), + indentAuto: cm => cm.indentSelection("smart"), + indentMore: cm => cm.indentSelection("add"), + indentLess: cm => cm.indentSelection("subtract"), + insertTab: cm => cm.replaceSelection("\t"), + insertSoftTab: cm => { let spaces = [], ranges = cm.listSelections(), tabSize = cm.options.tabSize for (let i = 0; i < ranges.length; i++) { let pos = ranges[i].from() @@ -124,47 +102,43 @@ export let commands = { } cm.replaceSelections(spaces) }, - defaultTab: function(cm) { + defaultTab: cm => { if (cm.somethingSelected()) cm.indentSelection("add") else cm.execCommand("insertTab") }, - transposeChars: function(cm) { - runInOp(cm, function() { - let ranges = cm.listSelections(), newSel = [] - for (let i = 0; i < ranges.length; i++) { - let cur = ranges[i].head, line = getLine(cm.doc, cur.line).text - if (line) { - if (cur.ch == line.length) cur = new Pos(cur.line, cur.ch - 1) - if (cur.ch > 0) { - cur = new Pos(cur.line, cur.ch + 1) - cm.replaceRange(line.charAt(cur.ch - 1) + line.charAt(cur.ch - 2), - Pos(cur.line, cur.ch - 2), cur, "+transpose") - } else if (cur.line > cm.doc.first) { - let prev = getLine(cm.doc, cur.line - 1).text - if (prev) - cm.replaceRange(line.charAt(0) + cm.doc.lineSeparator() + - prev.charAt(prev.length - 1), - Pos(cur.line - 1, prev.length - 1), Pos(cur.line, 1), "+transpose") - } + transposeChars: cm => runInOp(cm, () => { + let ranges = cm.listSelections(), newSel = [] + for (let i = 0; i < ranges.length; i++) { + let cur = ranges[i].head, line = getLine(cm.doc, cur.line).text + if (line) { + if (cur.ch == line.length) cur = new Pos(cur.line, cur.ch - 1) + if (cur.ch > 0) { + cur = new Pos(cur.line, cur.ch + 1) + cm.replaceRange(line.charAt(cur.ch - 1) + line.charAt(cur.ch - 2), + Pos(cur.line, cur.ch - 2), cur, "+transpose") + } else if (cur.line > cm.doc.first) { + let prev = getLine(cm.doc, cur.line - 1).text + if (prev) + cm.replaceRange(line.charAt(0) + cm.doc.lineSeparator() + + prev.charAt(prev.length - 1), + Pos(cur.line - 1, prev.length - 1), Pos(cur.line, 1), "+transpose") } - newSel.push(new Range(cur, cur)) } - cm.setSelections(newSel) - }) - }, - newlineAndIndent: function(cm) { - runInOp(cm, function() { - let sels = cm.listSelections() - for (let i = sels.length - 1; i >= 0; i--) - cm.replaceRange(cm.doc.lineSeparator(), sels[i].anchor, sels[i].head, "+input") - sels = cm.listSelections() - for (let i = 0; i < sels.length; i++) - cm.indentLine(sels[i].from().line, null, true) - ensureCursorVisible(cm) - }) - }, - openLine: function(cm) {cm.replaceSelection("\n", "start")}, - toggleOverwrite: function(cm) {cm.toggleOverwrite()} + newSel.push(new Range(cur, cur)) + } + cm.setSelections(newSel) + }), + newlineAndIndent: cm => runInOp(cm, () => { + let sels = cm.listSelections() + for (let i = sels.length - 1; i >= 0; i--) + cm.replaceRange(cm.doc.lineSeparator(), sels[i].anchor, sels[i].head, "+input") + sels = cm.listSelections() + for (let i = 0; i < sels.length; i++) + cm.indentLine(sels[i].from().line, null, true) + ensureCursorVisible(cm) + }), + openLine: cm => cm.replaceSelection("\n", "start"), + toggleOverwrite: cm => cm.toggleOverwrite() } diff --git a/src/edit/deleteNearSelection.js b/src/edit/deleteNearSelection.js index 38c5342f..5a9bd2cf 100644 --- a/src/edit/deleteNearSelection.js +++ b/src/edit/deleteNearSelection.js @@ -22,7 +22,7 @@ export function deleteNearSelection(cm, compute) { kill.push(toKill) } // Next, remove those actual ranges. - runInOp(cm, function() { + runInOp(cm, () => { for (let i = kill.length - 1; i >= 0; i--) replaceRange(cm.doc, "", kill[i].from, kill[i].to, "+delete") ensureCursorVisible(cm) diff --git a/src/edit/drop_events.js b/src/edit/drop_events.js index b8024d92..43e996fb 100644 --- a/src/edit/drop_events.js +++ b/src/edit/drop_events.js @@ -29,13 +29,13 @@ export function onDrop(e) { // and insert it. if (files && files.length && window.FileReader && window.File) { let n = files.length, text = Array(n), read = 0 - let loadFile = function(file, i) { + let loadFile = (file, i) => { if (cm.options.allowDropFileTypes && indexOf(cm.options.allowDropFileTypes, file.type) == -1) return let reader = new FileReader - reader.onload = operation(cm, function() { + reader.onload = operation(cm, () => { let content = reader.result if (/[\x00-\x08\x0e-\x1f]{2}/.test(content)) content = "" text[i] = content @@ -56,7 +56,7 @@ export function onDrop(e) { if (cm.state.draggingText && cm.doc.sel.contains(pos) > -1) { cm.state.draggingText(e) // Ensure the editor is re-focused - setTimeout(function() {cm.display.input.focus()}, 20) + setTimeout(() => cm.display.input.focus(), 20) return } try { diff --git a/src/edit/fromTextArea.js b/src/edit/fromTextArea.js index 7d221014..5d920830 100644 --- a/src/edit/fromTextArea.js +++ b/src/edit/fromTextArea.js @@ -28,7 +28,7 @@ export function fromTextArea(textarea, options) { let form = textarea.form realSubmit = form.submit try { - let wrappedSubmit = form.submit = function() { + let wrappedSubmit = form.submit = () => { save() form.submit = realSubmit form.submit() @@ -38,10 +38,10 @@ export function fromTextArea(textarea, options) { } } - options.finishInit = function(cm) { + options.finishInit = cm => { cm.save = save - cm.getTextArea = function() { return textarea } - cm.toTextArea = function() { + cm.getTextArea = () => textarea + cm.toTextArea = () => { cm.toTextArea = isNaN // Prevent this from being ran twice save() textarea.parentNode.removeChild(cm.getWrapperElement()) @@ -55,8 +55,7 @@ export function fromTextArea(textarea, options) { } textarea.style.display = "none" - let cm = CodeMirror(function(node) { - textarea.parentNode.insertBefore(node, textarea.nextSibling) - }, options) + let cm = CodeMirror(node => textarea.parentNode.insertBefore(node, textarea.nextSibling), + options) return cm } diff --git a/src/edit/global_events.js b/src/edit/global_events.js index a7ec22fe..dd7d1853 100644 --- a/src/edit/global_events.js +++ b/src/edit/global_events.js @@ -23,16 +23,14 @@ export function ensureGlobalHandlers() { function registerGlobalHandlers() { // When the window resizes, we need to refresh active editors. let resizeTimer - on(window, "resize", function() { - if (resizeTimer == null) resizeTimer = setTimeout(function() { + on(window, "resize", () => { + if (resizeTimer == null) resizeTimer = setTimeout(() => { resizeTimer = null forEachCodeMirror(onResize) }, 100) }) // When the window loses focus, we want to show the editor as blurred - on(window, "blur", function() { - forEachCodeMirror(onBlur) - }) + on(window, "blur", () => forEachCodeMirror(onBlur)) } // Called when the window resizes function onResize(cm) { diff --git a/src/edit/key_events.js b/src/edit/key_events.js index 8530aef3..8e695473 100644 --- a/src/edit/key_events.js +++ b/src/edit/key_events.js @@ -45,7 +45,7 @@ function dispatchKey(cm, name, e, handle) { let seq = cm.state.keySeq if (seq) { if (isModifierKey(name)) return "handled" - stopSeq.set(50, function() { + stopSeq.set(50, () => { if (cm.state.keySeq == seq) { cm.state.keySeq = null cm.display.input.reset() @@ -81,20 +81,19 @@ function handleKeyBinding(cm, e) { // 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-'. - return dispatchKey(cm, "Shift-" + name, e, function(b) {return doHandleBinding(cm, b, true)}) - || dispatchKey(cm, name, e, function(b) { + return dispatchKey(cm, "Shift-" + name, e, b => doHandleBinding(cm, b, true)) + || dispatchKey(cm, name, e, b => { if (typeof b == "string" ? /^go[A-Z]/.test(b) : b.motion) return doHandleBinding(cm, b) }) } else { - return dispatchKey(cm, name, e, function(b) { return doHandleBinding(cm, b) }) + return dispatchKey(cm, name, e, b => doHandleBinding(cm, b)) } } // Handle a key from the keypress event function handleCharBinding(cm, e, ch) { - return dispatchKey(cm, "'" + ch + "'", e, - function(b) { return doHandleBinding(cm, b, true) }) + return dispatchKey(cm, "'" + ch + "'", e, b => doHandleBinding(cm, b, true)) } let lastStoppedKey = null diff --git a/src/edit/main.js b/src/edit/main.js index 55432c08..2831d2a0 100644 --- a/src/edit/main.js +++ b/src/edit/main.js @@ -46,17 +46,15 @@ CodeMirror.defineMode = function(name/*, mode, …*/) { CodeMirror.defineMIME = defineMIME // Minimal default mode. -CodeMirror.defineMode("null", function() { - return {token: function(stream) {stream.skipToEnd()}} -}) +CodeMirror.defineMode("null", () => ({token: stream => stream.skipToEnd()})) CodeMirror.defineMIME("text/plain", "null") // EXTENSIONS -CodeMirror.defineExtension = function(name, func) { +CodeMirror.defineExtension = (name, func) => { CodeMirror.prototype[name] = func } -CodeMirror.defineDocExtension = function(name, func) { +CodeMirror.defineDocExtension = (name, func) => { Doc.prototype[name] = func } diff --git a/src/edit/methods.js b/src/edit/methods.js index 04be8512..260ffa5d 100644 --- a/src/edit/methods.js +++ b/src/edit/methods.js @@ -69,7 +69,7 @@ export default function(CodeMirror) { insertSorted(this.state.overlays, {mode: mode, modeSpec: spec, opaque: options && options.opaque, priority: (options && options.priority) || 0}, - function(overlay) { return overlay.priority }) + overlay => overlay.priority) this.state.modeGen++ regChange(this) }), @@ -219,7 +219,7 @@ export default function(CodeMirror) { defaultCharWidth: function() { return charWidth(this.display) }, setGutterMarker: methodOp(function(line, gutterID, value) { - return changeLine(this.doc, line, "gutter", function(line) { + return changeLine(this.doc, line, "gutter", line => { let markers = line.gutterMarkers || (line.gutterMarkers = {}) markers[gutterID] = value if (!value && isEmpty(markers)) line.gutterMarkers = null @@ -229,7 +229,7 @@ export default function(CodeMirror) { clearGutter: methodOp(function(gutterID) { let cm = this, doc = cm.doc, i = doc.first - doc.iter(function(line) { + doc.iter(line => { if (line.gutterMarkers && line.gutterMarkers[gutterID]) { line.gutterMarkers[gutterID] = null regLineChange(cm, i, "gutter") @@ -316,7 +316,7 @@ export default function(CodeMirror) { moveH: methodOp(function(dir, unit) { let cm = this - cm.extendSelectionsBy(function(range) { + cm.extendSelectionsBy(range => { if (cm.display.shift || cm.doc.extend || range.empty()) return findPosH(cm.doc, range.head, dir, unit, cm.options.rtlMoveVisually) else @@ -329,7 +329,7 @@ export default function(CodeMirror) { if (sel.somethingSelected()) doc.replaceSelection("", null, "+delete") else - deleteNearSelection(this, function(range) { + deleteNearSelection(this, range => { let other = findPosH(doc, range.head, dir, unit, false) return dir < 0 ? {from: other, to: range.head} : {from: range.head, to: other} }) @@ -352,7 +352,7 @@ export default function(CodeMirror) { moveV: methodOp(function(dir, unit) { let cm = this, doc = this.doc, goals = [] let collapse = !cm.display.shift && !doc.extend && doc.sel.somethingSelected() - doc.extendSelectionsBy(function(range) { + doc.extendSelectionsBy(range => { if (collapse) return dir < 0 ? range.from() : range.to() let headPos = cursorCoords(cm, range.head, "div") @@ -376,9 +376,9 @@ export default function(CodeMirror) { if ((pos.xRel < 0 || end == line.length) && start) --start; else ++end let startChar = line.charAt(start) let 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)} + ? ch => isWordChar(ch, helper) + : /\s/.test(startChar) ? ch => /\s/.test(ch) + : ch => (!/\s/.test(ch) && !isWordChar(ch)) while (start > 0 && check(line.charAt(start - 1))) --start while (end < line.length && check(line.charAt(end))) ++end } @@ -436,14 +436,12 @@ export default function(CodeMirror) { setSize: methodOp(function(width, height) { let cm = this - function interpret(val) { - return typeof val == "number" || /^\d+$/.test(String(val)) ? val + "px" : val - } + let interpret = val => typeof val == "number" || /^\d+$/.test(String(val)) ? val + "px" : val if (width != null) cm.display.wrapper.style.width = interpret(width) if (height != null) cm.display.wrapper.style.height = interpret(height) if (cm.options.lineWrapping) clearLineMeasurementCache(this) let lineNo = cm.display.viewFrom - cm.doc.iter(lineNo, cm.display.viewTo, function(line) { + cm.doc.iter(lineNo, cm.display.viewTo, line => { if (line.widgets) for (let i = 0; i < line.widgets.length; i++) if (line.widgets[i].noHScroll) { regLineChange(cm, lineNo, "widget"); break } ++lineNo diff --git a/src/edit/mouse_events.js b/src/edit/mouse_events.js index 5fe10ea3..784f195b 100644 --- a/src/edit/mouse_events.js +++ b/src/edit/mouse_events.js @@ -28,7 +28,7 @@ export function onMouseDown(e) { // Briefly turn off draggability, to allow widgets to do // normal dragging things. display.scroller.draggable = false - setTimeout(function(){display.scroller.draggable = true}, 100) + setTimeout(() => display.scroller.draggable = true, 100) } return } @@ -49,7 +49,7 @@ export function onMouseDown(e) { case 2: if (webkit) cm.state.lastMiddleDown = +new Date if (start) extendSelection(cm.doc, start) - setTimeout(function() {display.input.focus()}, 20) + setTimeout(() => display.input.focus(), 20) e_preventDefault(e) break case 3: @@ -89,7 +89,7 @@ function leftButtonDown(cm, e, start) { // happen, and treat as a click if it didn't. function leftButtonStartDrag(cm, e, start, modifier) { let display = cm.display, startTime = +new Date - let dragEnd = operation(cm, function(e2) { + let dragEnd = operation(cm, e2 => { if (webkit) display.scroller.draggable = false cm.state.draggingText = false off(document, "mouseup", dragEnd) @@ -100,7 +100,7 @@ function leftButtonStartDrag(cm, e, start, modifier) { extendSelection(cm.doc, start) // Work around unexplainable focus problem in IE9 (#2127) and Chrome (#3081) if (webkit || ie && ie_version == 9) - setTimeout(function() {document.body.focus(); display.input.focus()}, 20) + setTimeout(() => {document.body.focus(); display.input.focus()}, 20) else display.input.focus() } @@ -230,10 +230,10 @@ function leftButtonSelect(cm, e, start, type, addNew) { extendTo(cur) let visible = visibleLines(display, doc) if (cur.line >= visible.to || cur.line < visible.from) - setTimeout(operation(cm, function(){if (counter == curCount) extend(e)}), 150) + setTimeout(operation(cm, () => {if (counter == curCount) extend(e)}), 150) } else { let outside = e.clientY < editorSize.top ? -20 : e.clientY > editorSize.bottom ? 20 : 0 - if (outside) setTimeout(operation(cm, function() { + if (outside) setTimeout(operation(cm, () => { if (counter != curCount) return display.scroller.scrollTop += outside extend(e) @@ -251,7 +251,7 @@ function leftButtonSelect(cm, e, start, type, addNew) { doc.history.lastSelOrigin = null } - let move = operation(cm, function(e) { + let move = operation(cm, e => { if (!e_button(e)) done(e) else extend(e) }) diff --git a/src/edit/options.js b/src/edit/options.js index a2ca1c68..ea19d5ba 100644 --- a/src/edit/options.js +++ b/src/edit/options.js @@ -28,7 +28,7 @@ export function defineOptions(CodeMirror) { function option(name, deflt, handle, notOnInit) { CodeMirror.defaults[name] = deflt if (handle) optionHandlers[name] = - notOnInit ? function(cm, val, old) {if (old != Init) handle(cm, val, old)} : handle + notOnInit ? (cm, val, old) => {if (old != Init) handle(cm, val, old)} : handle } CodeMirror.defineOption = option @@ -38,10 +38,8 @@ export function defineOptions(CodeMirror) { // 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, function(cm, val) { + option("value", "", (cm, val) => cm.setValue(val), true) + option("mode", null, (cm, val) => { cm.doc.modeOption = val loadMode(cm) }, true) @@ -49,16 +47,16 @@ export function defineOptions(CodeMirror) { option("indentUnit", 2, loadMode, true) option("indentWithTabs", false) option("smartIndent", true) - option("tabSize", 4, function(cm) { + option("tabSize", 4, cm => { resetModeState(cm) clearCaches(cm) regChange(cm) }, true) - option("lineSeparator", null, function(cm, val) { + option("lineSeparator", null, (cm, val) => { cm.doc.lineSep = val if (!val) return let newBreaks = [], lineNo = cm.doc.first - cm.doc.iter(function(line) { + cm.doc.iter(line => { for (let pos = 0;;) { let found = line.text.indexOf(val, pos) if (found == -1) break @@ -70,26 +68,24 @@ export function defineOptions(CodeMirror) { for (let i = newBreaks.length - 1; i >= 0; i--) replaceRange(cm.doc, val, newBreaks[i], Pos(newBreaks[i].line, newBreaks[i].ch + val.length)) }) - option("specialChars", /[\u0000-\u001f\u007f\u00ad\u200b-\u200f\u2028\u2029\ufeff]/g, function(cm, val, old) { + option("specialChars", /[\u0000-\u001f\u007f\u00ad\u200b-\u200f\u2028\u2029\ufeff]/g, (cm, val, old) => { cm.state.specialChars = new RegExp(val.source + (val.test("\t") ? "" : "|\t"), "g") if (old != Init) cm.refresh() }) - option("specialCharPlaceholder", defaultSpecialCharPlaceholder, function(cm) {cm.refresh()}, true) + option("specialCharPlaceholder", defaultSpecialCharPlaceholder, cm => cm.refresh(), true) option("electricChars", true) - option("inputStyle", mobile ? "contenteditable" : "textarea", function() { + option("inputStyle", mobile ? "contenteditable" : "textarea", () => { throw new Error("inputStyle can not (yet) be changed in a running editor") // FIXME }, true) - option("spellcheck", false, function(cm, val) { - cm.getInputField().spellcheck = val - }, true) + option("spellcheck", false, (cm, val) => cm.getInputField().spellcheck = val, true) option("rtlMoveVisually", !windows) option("wholeLineUpdateBefore", true) - option("theme", "default", function(cm) { + option("theme", "default", cm => { themeChanged(cm) guttersChanged(cm) }, true) - option("keyMap", "default", function(cm, val, old) { + option("keyMap", "default", (cm, val, old) => { let next = getKeyMap(val) let prev = old != Init && getKeyMap(old) if (prev && prev.detach) prev.detach(cm, next) @@ -98,33 +94,33 @@ export function defineOptions(CodeMirror) { option("extraKeys", null) option("lineWrapping", false, wrappingChanged, true) - option("gutters", [], function(cm) { + option("gutters", [], cm => { setGuttersForLineNumbers(cm.options) guttersChanged(cm) }, true) - option("fixedGutter", true, function(cm, val) { + option("fixedGutter", true, (cm, val) => { cm.display.gutters.style.left = val ? compensateForHScroll(cm.display) + "px" : "0" cm.refresh() }, true) - option("coverGutterNextToScrollbar", false, function(cm) {updateScrollbars(cm)}, true) - option("scrollbarStyle", "native", function(cm) { + option("coverGutterNextToScrollbar", false, cm => updateScrollbars(cm), true) + option("scrollbarStyle", "native", cm => { initScrollbars(cm) updateScrollbars(cm) cm.display.scrollbars.setScrollTop(cm.doc.scrollTop) cm.display.scrollbars.setScrollLeft(cm.doc.scrollLeft) }, true) - option("lineNumbers", false, function(cm) { + option("lineNumbers", false, cm => { setGuttersForLineNumbers(cm.options) guttersChanged(cm) }, true) option("firstLineNumber", 1, guttersChanged, true) - option("lineNumberFormatter", function(integer) {return integer}, guttersChanged, true) + option("lineNumberFormatter", integer => integer, guttersChanged, true) option("showCursorWhenSelecting", false, updateSelection, true) option("resetSelectionOnContextMenu", true) option("lineWiseCopyCut", true) - option("readOnly", false, function(cm, val) { + option("readOnly", false, (cm, val) => { if (val == "nocursor") { onBlur(cm) cm.display.input.blur() @@ -134,7 +130,7 @@ export function defineOptions(CodeMirror) { } cm.display.input.readOnlyChanged(val) }) - option("disableInput", false, function(cm, val) {if (!val) cm.display.input.reset()}, true) + option("disableInput", false, (cm, val) => {if (!val) cm.display.input.reset()}, true) option("dragDrop", true, dragDropChanged) option("allowDropFileTypes", null) @@ -147,24 +143,22 @@ export function defineOptions(CodeMirror) { option("flattenSpans", true, resetModeState, true) option("addModeClass", false, resetModeState, true) option("pollInterval", 100) - option("undoDepth", 200, function(cm, val){cm.doc.history.undoDepth = val}) + option("undoDepth", 200, (cm, val) => cm.doc.history.undoDepth = val) option("historyEventDelay", 1250) - option("viewportMargin", 10, function(cm){cm.refresh()}, true) + option("viewportMargin", 10, cm => cm.refresh(), true) option("maxHighlightLength", 10000, resetModeState, true) - option("moveInputWithCursor", true, function(cm, val) { + option("moveInputWithCursor", true, (cm, val) => { if (!val) cm.display.input.resetPosition() }) - option("tabindex", null, function(cm, val) { - cm.display.input.getField().tabIndex = val || "" - }) + option("tabindex", null, (cm, val) => cm.display.input.getField().tabIndex = val || "") option("autofocus", null) } function guttersChanged(cm) { updateGutters(cm) regChange(cm) - setTimeout(function(){alignHorizontally(cm)}, 20) + setTimeout(() => alignHorizontally(cm), 20) } function dragDropChanged(cm, value, old) { @@ -192,5 +186,5 @@ function wrappingChanged(cm) { estimateLineHeights(cm) regChange(cm) clearCaches(cm) - setTimeout(function(){updateScrollbars(cm)}, 100) + setTimeout(() => updateScrollbars(cm), 100) } diff --git a/src/input/ContentEditableInput.js b/src/input/ContentEditableInput.js index 80e91f84..59ef55fa 100644 --- a/src/input/ContentEditableInput.js +++ b/src/input/ContentEditableInput.js @@ -29,15 +29,15 @@ ContentEditableInput.prototype = copyObj({ let div = input.div = display.lineDiv disableBrowserMagic(div, cm.options.spellcheck) - on(div, "paste", function(e) { + on(div, "paste", e => { if (signalDOMEvent(cm, e) || handlePaste(e, cm)) return // IE doesn't fire input events, so we schedule a read for the pasted content in this way - if (ie_version <= 11) setTimeout(operation(cm, function() { + if (ie_version <= 11) setTimeout(operation(cm, () => { if (!input.pollContent()) regChange(cm) }), 20) }) - on(div, "compositionstart", function(e) { + on(div, "compositionstart", e => { let data = e.data input.composing = {sel: cm.doc.sel, data: data, startData: data} if (!data) return @@ -48,10 +48,8 @@ ContentEditableInput.prototype = copyObj({ input.composing.sel = simpleSelection(Pos(prim.head.line, found), Pos(prim.head.line, found + data.length)) }) - on(div, "compositionupdate", function(e) { - input.composing.data = e.data - }) - on(div, "compositionend", function(e) { + on(div, "compositionupdate", e => input.composing.data = e.data) + on(div, "compositionend", e => { let ours = input.composing if (!ours) return if (e.data != ours.startData && !/\u200b/.test(e.data)) @@ -59,7 +57,7 @@ ContentEditableInput.prototype = copyObj({ // Need a small delay to prevent other code (input event, // selection polling) from doing damage when fired right after // compositionend. - setTimeout(function() { + setTimeout(() => { if (!ours.handled) input.applyComposition(ours) if (input.composing == ours) @@ -67,14 +65,12 @@ ContentEditableInput.prototype = copyObj({ }, 50) }) - on(div, "touchstart", function() { - input.forceCompositionEnd() - }) + on(div, "touchstart", () => input.forceCompositionEnd()) - on(div, "input", function() { + on(div, "input", () => { if (input.composing) return if (cm.isReadOnly() || !input.pollContent()) - runInOp(input.cm, function() {regChange(cm)}) + runInOp(input.cm, () => regChange(cm)) }) function onCopyCut(e) { @@ -88,7 +84,7 @@ ContentEditableInput.prototype = copyObj({ let ranges = copyableRanges(cm) setLastCopied({lineWise: true, text: ranges.text}) if (e.type == "cut") { - cm.operation(function() { + cm.operation(() => { cm.setSelections(ranges.ranges, 0, sel_dontScroll) cm.replaceSelection("", null, "cut") }) @@ -110,7 +106,7 @@ ContentEditableInput.prototype = copyObj({ te.value = lastCopied.text.join("\n") let hadFocus = document.activeElement selectInput(te) - setTimeout(function() { + setTimeout(() => { cm.display.lineSpace.removeChild(kludge) hadFocus.focus() if (hadFocus == div) input.showPrimarySelection() @@ -175,10 +171,10 @@ ContentEditableInput.prototype = copyObj({ startGracePeriod: function() { let input = this clearTimeout(this.gracePeriod) - this.gracePeriod = setTimeout(function() { + this.gracePeriod = setTimeout(() => { input.gracePeriod = false if (input.selectionChanged()) - input.cm.operation(function() { input.cm.curOp.selectionChanged = true }) + input.cm.operation(() => input.cm.curOp.selectionChanged = true) }, 20) }, @@ -213,7 +209,7 @@ ContentEditableInput.prototype = copyObj({ if (this.selectionInEditor()) this.pollSelection() else - runInOp(this.cm, function() { input.cm.curOp.selectionChanged = true }) + runInOp(this.cm, () => input.cm.curOp.selectionChanged = true) function poll() { if (input.cm.state.focused) { @@ -236,7 +232,7 @@ ContentEditableInput.prototype = copyObj({ this.rememberSelection() let anchor = domToPos(cm, sel.anchorNode, sel.anchorOffset) let head = domToPos(cm, sel.focusNode, sel.focusOffset) - if (anchor && head) runInOp(cm, function() { + if (anchor && head) runInOp(cm, () => { setSelection(cm.doc, simpleSelection(anchor, head), sel_dontScroll) if (anchor.bad || head.bad) cm.curOp.selectionChanged = true }) @@ -356,7 +352,7 @@ function badPos(pos, bad) { if (bad) pos.bad = true; return pos } function domTextBetween(cm, from, to, fromLine, toLine) { let text = "", closing = false, lineSep = cm.doc.lineSeparator() - function recognizeMarker(id) { return function(marker) { return marker.id == id } } + function recognizeMarker(id) { return marker => marker.id == id } function walk(node) { if (node.nodeType == 1) { let cmText = node.getAttribute("cm-text") diff --git a/src/input/TextareaInput.js b/src/input/TextareaInput.js index e244a2d1..17f6f599 100644 --- a/src/input/TextareaInput.js +++ b/src/input/TextareaInput.js @@ -46,12 +46,12 @@ TextareaInput.prototype = copyObj({ // Needed to hide big blue blinking cursor on Mobile Safari (doesn't seem to work in iOS 8 anymore) if (ios) te.style.width = "0px" - on(te, "input", function() { + on(te, "input", () => { if (ie && ie_version >= 9 && input.hasSelection) input.hasSelection = null input.poll() }) - on(te, "paste", function(e) { + on(te, "paste", e => { if (signalDOMEvent(cm, e) || handlePaste(e, cm)) return cm.state.pasteIncoming = true @@ -86,18 +86,18 @@ TextareaInput.prototype = copyObj({ on(te, "cut", prepareCopyCut) on(te, "copy", prepareCopyCut) - on(display.scroller, "paste", function(e) { + on(display.scroller, "paste", e => { if (eventInWidget(display, e) || signalDOMEvent(cm, e)) return cm.state.pasteIncoming = true input.focus() }) // Prevent normal selection in the editor (we handle our own) - on(display.lineSpace, "selectstart", function(e) { + on(display.lineSpace, "selectstart", e => { if (!eventInWidget(display, e)) e_preventDefault(e) }) - on(te, "compositionstart", function() { + on(te, "compositionstart", () => { let start = cm.getCursor("from") if (input.composing) input.composing.range.clear() input.composing = { @@ -105,7 +105,7 @@ TextareaInput.prototype = copyObj({ range: cm.markText(start, cm.getCursor("to"), {className: "CodeMirror-composing"}) } }) - on(te, "compositionend", function() { + on(te, "compositionend", () => { if (input.composing) { input.poll() input.composing.range.clear() @@ -187,7 +187,7 @@ TextareaInput.prototype = copyObj({ slowPoll: function() { let input = this if (input.pollingFast) return - input.polling.set(this.cm.options.pollInterval, function() { + input.polling.set(this.cm.options.pollInterval, () => { input.poll() if (input.cm.state.focused) input.slowPoll() }) @@ -246,7 +246,7 @@ TextareaInput.prototype = copyObj({ while (same < l && prevInput.charCodeAt(same) == text.charCodeAt(same)) ++same let self = this - runInOp(cm, function() { + runInOp(cm, () => { applyTextInput(cm, text.slice(same), prevInput.length - same, null, self.composing ? "*compose" : null) @@ -326,7 +326,7 @@ TextareaInput.prototype = copyObj({ // Try to detect the user choosing select-all if (te.selectionStart != null) { if (!ie || (ie && ie_version < 9)) prepareSelectAllHack() - let i = 0, poll = function() { + let i = 0, poll = () => { if (display.selForContextMenu == cm.doc.sel && te.selectionStart == 0 && te.selectionEnd > 0 && input.prevInput == "\u200b") operation(cm, selectAll)(cm) @@ -340,7 +340,7 @@ TextareaInput.prototype = copyObj({ if (ie && ie_version >= 9) prepareSelectAllHack() if (captureRightClick) { e_stop(e) - let mouseup = function() { + let mouseup = () => { off(window, "mouseup", mouseup) setTimeout(rehide, 20) } diff --git a/src/input/input.js b/src/input/input.js index 6cc3e911..e51a90a1 100644 --- a/src/input/input.js +++ b/src/input/input.js @@ -35,7 +35,7 @@ export function applyTextInput(cm, inserted, deleted, sel, origin) { multiPaste.push(doc.splitLines(lastCopied.text[i])) } } else if (textLines.length == sel.ranges.length) { - multiPaste = map(textLines, function(l) { return [l] }) + multiPaste = map(textLines, l => [l]) } } @@ -72,7 +72,7 @@ export function handlePaste(e, cm) { if (pasted) { e.preventDefault() if (!cm.isReadOnly() && !cm.options.disableInput) - runInOp(cm, function() { applyTextInput(cm, pasted, 0, null, "paste") }) + runInOp(cm, () => applyTextInput(cm, pasted, 0, null, "paste")) return true } } diff --git a/src/line/highlight.js b/src/line/highlight.js index 8fd6d47b..f54e7eaa 100644 --- a/src/line/highlight.js +++ b/src/line/highlight.js @@ -14,14 +14,13 @@ export function highlightLine(cm, line, state, forceToEnd) { // mode/overlays that it is based on (for easy invalidation). let st = [cm.state.modeGen], lineClasses = {} // Compute the base array of styles - runMode(cm, line.text, cm.doc.mode, state, function(end, style) { - st.push(end, style) - }, lineClasses, forceToEnd) + runMode(cm, line.text, cm.doc.mode, state, (end, style) => st.push(end, style), + lineClasses, forceToEnd) // Run overlays, adjust style array. for (let o = 0; o < cm.state.overlays.length; ++o) { let overlay = cm.state.overlays[o], i = 1, at = 0 - runMode(cm, line.text, overlay.mode, true, function(end, style) { + runMode(cm, line.text, overlay.mode, true, (end, style) => { let start = i // Ensure there's a token end at the current position, and that i points at it while (at < end) { @@ -66,7 +65,7 @@ export function getStateBefore(cm, n, precise) { let 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) - doc.iter(pos, n, function(line) { + doc.iter(pos, n, line => { processLine(cm, line.text, state) let save = pos == n - 1 || pos % 5 == 0 || pos >= display.viewFrom && pos < display.viewTo line.stateAfter = save ? copyState(doc.mode, state) : null @@ -108,12 +107,12 @@ export function readToken(mode, stream, state, inner) { // Utility for getTokenAt and getLineTokens export function takeToken(cm, pos, precise, asArray) { - function getObj(copy) { - return {start: stream.start, end: stream.pos, - string: stream.current(), - type: style || null, - state: copy ? copyState(doc.mode, state) : state} - } + let getObj = copy => ({ + start: stream.start, end: stream.pos, + string: stream.current(), + type: style || null, + state: copy ? copyState(doc.mode, state) : state + }) let doc = cm.doc, mode = doc.mode, style pos = clipPos(doc, pos) diff --git a/src/line/line_data.js b/src/line/line_data.js index 7a3a4122..93b57577 100644 --- a/src/line/line_data.js +++ b/src/line/line_data.js @@ -202,7 +202,7 @@ function splitSpaces(text, trailingBefore) { // Work around nonsense dimensions being reported for stretches of // right-to-left text. function buildTokenBadBidi(inner, order) { - return function(builder, text, style, startStyle, endStyle, title, css) { + return (builder, text, style, startStyle, endStyle, title, css) => { style = style ? style + " cm-force-border" : "cm-force-border" let start = builder.pos, end = start + text.length for (;;) { diff --git a/src/line/spans.js b/src/line/spans.js index 63acacfc..267ee718 100644 --- a/src/line/spans.js +++ b/src/line/spans.js @@ -143,7 +143,7 @@ function clearEmptySpans(spans) { // Used to 'clip' out readOnly ranges when making a change. export function removeReadOnlyRanges(doc, from, to) { let markers = null - doc.iter(from.line, to.line + 1, function(line) { + doc.iter(from.line, to.line + 1, line => { if (line.markedSpans) for (let i = 0; i < line.markedSpans.length; ++i) { let mark = line.markedSpans[i].marker if (mark.readOnly && (!markers || indexOf(markers, mark) == -1)) @@ -355,7 +355,7 @@ export function findMaxLine(cm) { d.maxLine = getLine(doc, doc.first) d.maxLineLength = lineLength(d.maxLine) d.maxLineChanged = true - doc.iter(function(line) { + doc.iter(line => { let len = lineLength(line) if (len > d.maxLineLength) { d.maxLineLength = len diff --git a/src/line/utils_line.js b/src/line/utils_line.js index 647c0779..e4e6943f 100644 --- a/src/line/utils_line.js +++ b/src/line/utils_line.js @@ -19,7 +19,7 @@ export function getLine(doc, n) { // strings. export function getBetween(doc, start, end) { let out = [], n = start.line - doc.iter(start.line, end.line + 1, function(line) { + doc.iter(start.line, end.line + 1, line => { let text = line.text if (n == end.line) text = text.slice(0, end.ch) if (n == start.line) text = text.slice(start.ch) @@ -31,7 +31,7 @@ export function getBetween(doc, start, end) { // Get the lines between from and to, as array of strings. export function getLines(doc, from, to) { let out = [] - doc.iter(from, to, function(line) { out.push(line.text) }) + doc.iter(from, to, line => { out.push(line.text) }) // iter aborts when callback returns truthy value return out } diff --git a/src/measurement/position_measurement.js b/src/measurement/position_measurement.js index 63e3771b..5688836e 100644 --- a/src/measurement/position_measurement.js +++ b/src/measurement/position_measurement.js @@ -525,7 +525,7 @@ export function compensateForHScroll(display) { export function estimateHeight(cm) { let th = textHeight(cm.display), wrapping = cm.options.lineWrapping let perLine = wrapping && Math.max(5, cm.display.scroller.clientWidth / charWidth(cm.display) - 3) - return function(line) { + return line => { if (lineIsHidden(cm.doc, line)) return 0 let widgetsHeight = 0 @@ -542,7 +542,7 @@ export function estimateHeight(cm) { export function estimateLineHeights(cm) { let doc = cm.doc, est = estimateHeight(cm) - doc.iter(function(line) { + doc.iter(line => { let estHeight = est(line) if (estHeight != line.height) updateLineHeight(line, estHeight) }) diff --git a/src/model/Doc.js b/src/model/Doc.js index c02fe062..fcb2c1e1 100644 --- a/src/model/Doc.js +++ b/src/model/Doc.js @@ -220,7 +220,7 @@ Doc.prototype = createObj(BranchChunk.prototype, { }, addLineClass: docMethodOp(function(handle, where, cls) { - return changeLine(this, handle, where == "gutter" ? "gutter" : "class", function(line) { + return changeLine(this, handle, where == "gutter" ? "gutter" : "class", line => { let prop = where == "text" ? "textClass" : where == "background" ? "bgClass" : where == "gutter" ? "gutterClass" : "wrapClass" @@ -231,7 +231,7 @@ Doc.prototype = createObj(BranchChunk.prototype, { }) }), removeLineClass: docMethodOp(function(handle, where, cls) { - return changeLine(this, handle, where == "gutter" ? "gutter" : "class", function(line) { + return changeLine(this, handle, where == "gutter" ? "gutter" : "class", line => { let prop = where == "text" ? "textClass" : where == "background" ? "bgClass" : where == "gutter" ? "gutterClass" : "wrapClass" @@ -278,7 +278,7 @@ Doc.prototype = createObj(BranchChunk.prototype, { findMarks: function(from, to, filter) { from = clipPos(this, from); to = clipPos(this, to) let found = [], lineNo = from.line - this.iter(from.line, to.line + 1, function(line) { + this.iter(from.line, to.line + 1, line => { let spans = line.markedSpans if (spans) for (let i = 0; i < spans.length; i++) { let span = spans[i] @@ -294,7 +294,7 @@ Doc.prototype = createObj(BranchChunk.prototype, { }, getAllMarks: function() { let markers = [] - this.iter(function(line) { + this.iter(line => { let sps = line.markedSpans if (sps) for (let i = 0; i < sps.length; ++i) if (sps[i].from != null) markers.push(sps[i].marker) @@ -304,7 +304,7 @@ Doc.prototype = createObj(BranchChunk.prototype, { posFromIndex: function(off) { let ch, lineNo = this.first, sepSize = this.lineSeparator().length - this.iter(function(line) { + this.iter(line => { let sz = line.text.length + sepSize if (sz > off) { ch = off; return true } off -= sz @@ -317,7 +317,7 @@ Doc.prototype = createObj(BranchChunk.prototype, { let index = coords.ch if (coords.line < this.first || coords.ch < 0) return 0 let sepSize = this.lineSeparator().length - this.iter(this.first, coords.line, function (line) { + this.iter(this.first, coords.line, line => { // iter aborts when callback returns a truthy value index += line.text.length + sepSize }) return index @@ -361,7 +361,7 @@ Doc.prototype = createObj(BranchChunk.prototype, { // If the histories were shared, split them again if (other.history == this.history) { let splitIds = [other.id] - linkedDocs(other, function(doc) {splitIds.push(doc.id)}, true) + linkedDocs(other, doc => splitIds.push(doc.id), true) other.history = new History(null) other.history.done = copyHistoryArray(this.history.done, splitIds) other.history.undone = copyHistoryArray(this.history.undone, splitIds) diff --git a/src/model/changes.js b/src/model/changes.js index e39bc303..c6692d4c 100644 --- a/src/model/changes.js +++ b/src/model/changes.js @@ -26,13 +26,13 @@ function filterChange(doc, change, update) { to: change.to, text: change.text, origin: change.origin, - cancel: function() { this.canceled = true } + cancel: () => obj.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 + if (update) obj.update = (from, to, text, origin) => { + if (from) obj.from = clipPos(doc, from) + if (to) obj.to = clipPos(doc, to) + if (text) obj.text = text + if (origin !== undefined) obj.origin = origin } signal(doc, "beforeChange", doc, obj) if (doc.cm) signal(doc.cm, "beforeChange", doc.cm, obj) @@ -73,7 +73,7 @@ function makeChangeInner(doc, change) { makeChangeSingleDoc(doc, change, selAfter, stretchSpansOverChange(doc, change)) let rebased = [] - linkedDocs(doc, function(doc, sharedHist) { + linkedDocs(doc, (doc, sharedHist) => { if (!sharedHist && indexOf(rebased, doc.history) == -1) { rebaseHist(doc.history, change) rebased.push(doc.history) @@ -138,7 +138,7 @@ export function makeChangeFromHistory(doc, type, allowSelectionOnly) { let rebased = [] // Propagate to the linked documents - linkedDocs(doc, function(doc, sharedHist) { + linkedDocs(doc, (doc, sharedHist) => { if (!sharedHist && indexOf(rebased, doc.history) == -1) { rebaseHist(doc.history, change) rebased.push(doc.history) @@ -153,10 +153,10 @@ export function makeChangeFromHistory(doc, type, allowSelectionOnly) { function shiftDoc(doc, distance) { if (distance == 0) return doc.first += distance - doc.sel = new Selection(map(doc.sel.ranges, function(range) { - return new Range(Pos(range.anchor.line + distance, range.anchor.ch), - Pos(range.head.line + distance, range.head.ch)) - }), doc.sel.primIndex) + doc.sel = new Selection(map(doc.sel.ranges, range => new Range( + Pos(range.anchor.line + distance, range.anchor.ch), + Pos(range.head.line + distance, range.head.ch) + )), doc.sel.primIndex) if (doc.cm) { regChange(doc.cm, doc.first, doc.first - distance, distance) for (let d = doc.cm.display, l = d.viewFrom; l < d.viewTo; l++) @@ -205,7 +205,7 @@ function makeChangeSingleDocInEditor(cm, change, spans) { let recomputeMaxLength = false, checkWidthStart = from.line if (!cm.options.lineWrapping) { checkWidthStart = lineNo(visualLine(getLine(doc, from.line))) - doc.iter(checkWidthStart, to.line + 1, function(line) { + doc.iter(checkWidthStart, to.line + 1, line => { if (line == display.maxLine) { recomputeMaxLength = true return true @@ -219,7 +219,7 @@ function makeChangeSingleDocInEditor(cm, change, spans) { updateDoc(doc, change, spans, estimateHeight(cm)) if (!cm.options.lineWrapping) { - doc.iter(checkWidthStart, from.line + change.text.length, function(line) { + doc.iter(checkWidthStart, from.line + change.text.length, line => { let len = lineLength(line) if (len > display.maxLineLength) { display.maxLine = line diff --git a/src/model/history.js b/src/model/history.js index 2a7502cb..83938cf4 100644 --- a/src/model/history.js +++ b/src/model/history.js @@ -28,7 +28,7 @@ export function History(startGen) { export function historyChangeFromChange(doc, change) { let histChange = {from: copyPos(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) + linkedDocs(doc, doc => attachLocalSpans(doc, histChange, change.from.line, change.to.line + 1), true) return histChange } @@ -146,7 +146,7 @@ export function pushSelectionToHistory(sel, dest) { // Used to store marked span information in the history. function attachLocalSpans(doc, change, from, to) { let existing = change["spans_" + doc.id], n = 0 - doc.iter(Math.max(doc.first, from), Math.min(doc.first + doc.size, to), function(line) { + doc.iter(Math.max(doc.first, from), Math.min(doc.first + doc.size, to), line => { if (line.markedSpans) (existing || (existing = change["spans_" + doc.id] = {}))[n] = line.markedSpans ++n diff --git a/src/model/line_widget.js b/src/model/line_widget.js index 23b2381b..e832e8c4 100644 --- a/src/model/line_widget.js +++ b/src/model/line_widget.js @@ -29,7 +29,7 @@ LineWidget.prototype.clear = function() { if (!ws.length) line.widgets = null let height = widgetHeight(this) updateLineHeight(line, Math.max(0, line.height - height)) - if (cm) runInOp(cm, function() { + if (cm) runInOp(cm, () => { adjustScrollWhenAboveVisible(cm, line, -height) regLineChange(cm, no, "widget") }) @@ -40,7 +40,7 @@ LineWidget.prototype.changed = function() { let diff = widgetHeight(this) - oldH if (!diff) return updateLineHeight(line, line.height + diff) - if (cm) runInOp(cm, function() { + if (cm) runInOp(cm, () => { cm.curOp.forceUpdate = true adjustScrollWhenAboveVisible(cm, line, diff) }) @@ -50,7 +50,7 @@ export function addLineWidget(doc, handle, node, options) { let widget = new LineWidget(doc, node, options) let cm = doc.cm if (cm && widget.noHScroll) cm.display.alignWidgets = true - changeLine(doc, handle, "widget", function(line) { + changeLine(doc, handle, "widget", line => { let widgets = line.widgets || (line.widgets = []) if (widget.insertAt == null) widgets.push(widget) else widgets.splice(Math.min(widgets.length - 1, Math.max(0, widget.insertAt)), 0, widget) diff --git a/src/model/mark_text.js b/src/model/mark_text.js index f15cefb2..4250288e 100644 --- a/src/model/mark_text.js +++ b/src/model/mark_text.js @@ -111,7 +111,7 @@ TextMarker.prototype.find = function(side, lineObj) { TextMarker.prototype.changed = function() { let pos = this.find(-1, true), widget = this, cm = this.doc.cm if (!pos || !cm) return - runInOp(cm, function() { + runInOp(cm, () => { let line = pos.line, lineN = lineNo(pos.line) let view = findViewForLine(cm, lineN) if (view) { @@ -177,7 +177,7 @@ export function markText(doc, from, to, options, type) { addChangeToHistory(doc, {from: from, to: to, origin: "markText"}, doc.sel, NaN) let curLine = from.line, cm = doc.cm, updateMaxLine - doc.iter(curLine, to.line + 1, function(line) { + doc.iter(curLine, to.line + 1, line => { if (cm && marker.collapsed && !cm.options.lineWrapping && visualLine(line) == cm.display.maxLine) updateMaxLine = true if (marker.collapsed && curLine != from.line) updateLineHeight(line, 0) @@ -187,11 +187,11 @@ export function markText(doc, from, to, options, type) { ++curLine }) // lineIsHidden depends on the presence of the spans, so needs a second pass - if (marker.collapsed) doc.iter(from.line, to.line + 1, function(line) { + if (marker.collapsed) doc.iter(from.line, to.line + 1, line => { if (lineIsHidden(doc, line)) updateLineHeight(line, 0) }) - if (marker.clearOnEnter) on(marker, "beforeCursorEnter", function() { marker.clear() }) + if (marker.clearOnEnter) on(marker, "beforeCursorEnter", () => marker.clear()) if (marker.readOnly) { seeReadOnlySpans() @@ -244,7 +244,7 @@ function markTextShared(doc, from, to, options, type) { options.shared = false let markers = [markText(doc, from, to, options, type)], primary = markers[0] let widget = options.widgetNode - linkedDocs(doc, function(doc) { + linkedDocs(doc, doc => { if (widget) options.widgetNode = widget.cloneNode(true) markers.push(markText(doc, clipPos(doc, from), clipPos(doc, to), options, type)) for (let i = 0; i < doc.linked.length; ++i) @@ -255,8 +255,7 @@ function markTextShared(doc, from, to, options, type) { } export function findSharedMarkers(doc) { - return doc.findMarks(Pos(doc.first, 0), doc.clipPos(Pos(doc.lastLine())), - function(m) { return m.parent }) + return doc.findMarks(Pos(doc.first, 0), doc.clipPos(Pos(doc.lastLine())), m => m.parent) } export function copySharedMarkers(doc, markers) { @@ -274,7 +273,7 @@ export function copySharedMarkers(doc, markers) { export function detachSharedMarkers(markers) { for (let i = 0; i < markers.length; i++) { let marker = markers[i], linked = [marker.primary.doc] - linkedDocs(marker.primary.doc, function(d) { linked.push(d) }) + linkedDocs(marker.primary.doc, d => linked.push(d)) for (let j = 0; j < marker.markers.length; j++) { let subMarker = marker.markers[j] if (indexOf(linked, subMarker.doc) == -1) { diff --git a/src/model/selection.js b/src/model/selection.js index 63371954..120af2a4 100644 --- a/src/model/selection.js +++ b/src/model/selection.js @@ -61,7 +61,7 @@ Range.prototype = { // it). export function normalizeSelection(ranges, primIndex) { let prim = ranges[primIndex] - ranges.sort(function(a, b) { return cmp(a.from(), b.from()) }) + ranges.sort((a, b) => cmp(a.from(), b.from())) primIndex = indexOf(ranges, prim) for (let i = 1; i < ranges.length; i++) { let cur = ranges[i], prev = ranges[i - 1] diff --git a/src/util/StringStream.js b/src/util/StringStream.js index cf7b8244..92dbc690 100644 --- a/src/util/StringStream.js +++ b/src/util/StringStream.js @@ -57,7 +57,7 @@ StringStream.prototype = { }, match: function(pattern, consume, caseInsensitive) { if (typeof pattern == "string") { - let cased = function(str) {return caseInsensitive ? str.toLowerCase() : str} + let cased = str => caseInsensitive ? str.toLowerCase() : str let substr = this.string.substr(this.pos, pattern.length) if (cased(substr) == cased(pattern)) { if (consume !== false) this.pos += pattern.length diff --git a/src/util/feature_detection.js b/src/util/feature_detection.js index d98393af..e65881d4 100644 --- a/src/util/feature_detection.js +++ b/src/util/feature_detection.js @@ -38,7 +38,7 @@ export function hasBadBidiRects(measure) { // See if "".split is the broken IE version, if so, provide an // alternative way to split lines. -export let splitLinesAuto = "\n\nb".split(/\n/).length != 3 ? function(string) { +export let splitLinesAuto = "\n\nb".split(/\n/).length != 3 ? string => { let pos = 0, result = [], l = string.length while (pos <= l) { let nl = string.indexOf("\n", pos) @@ -54,12 +54,12 @@ export let splitLinesAuto = "\n\nb".split(/\n/).length != 3 ? function(string) { } } return result -} : function(string){return string.split(/\r\n?|\n/)} +} : string => string.split(/\r\n?|\n/) -export let hasSelection = window.getSelection ? function(te) { +export let hasSelection = window.getSelection ? te => { try { return te.selectionStart != te.selectionEnd } catch(e) { return false } -} : function(te) { +} : te => { let range try {range = te.ownerDocument.selection.createRange()} catch(e) {} @@ -67,7 +67,7 @@ export let hasSelection = window.getSelection ? function(te) { return range.compareEndPoints("StartToEnd", range) != 0 } -export let hasCopyEvent = (function() { +export let hasCopyEvent = (() => { let e = elt("div") if ("oncopy" in e) return true e.setAttribute("oncopy", "return;") diff --git a/src/util/operation_group.js b/src/util/operation_group.js index d743f994..f50da343 100644 --- a/src/util/operation_group.js +++ b/src/util/operation_group.js @@ -61,9 +61,8 @@ export function signalLater(emitter, type /*, values...*/) { list = orphanDelayedCallbacks = [] setTimeout(fireOrphanDelayed, 0) } - function bnd(f) {return function(){f.apply(null, args)}} for (let i = 0; i < arr.length; ++i) - list.push(bnd(arr[i])) + list.push(() => arr[i].apply(null, args)) } function fireOrphanDelayed() { From cef4089021830fc17e8374619a008ead8fb57d90 Mon Sep 17 00:00:00 2001 From: Marijn Haverbeke Date: Fri, 30 Sep 2016 10:27:29 +0200 Subject: [PATCH 0462/1790] Remove a few vars that worked around this-binding issue --- src/edit/CodeMirror.js | 6 ++--- src/edit/methods.js | 38 +++++++++++++++---------------- src/input/ContentEditableInput.js | 7 +++--- src/input/TextareaInput.js | 24 +++++++++---------- 4 files changed, 34 insertions(+), 41 deletions(-) diff --git a/src/edit/CodeMirror.js b/src/edit/CodeMirror.js index 909ffcb1..7e17002d 100644 --- a/src/edit/CodeMirror.js +++ b/src/edit/CodeMirror.js @@ -64,11 +64,9 @@ export function CodeMirror(place, options) { specialChars: null } - let cm = this - // Override magic textarea content restore that IE sometimes does // on our hidden textarea on reload - if (ie && ie_version < 11) setTimeout(() => cm.display.input.reset(true), 20) + if (ie && ie_version < 11) setTimeout(() => this.display.input.reset(true), 20) registerEventHandlers(this) ensureGlobalHandlers() @@ -77,7 +75,7 @@ export function CodeMirror(place, options) { this.curOp.forceUpdate = true attachDoc(this, doc) - if ((options.autofocus && !mobile) || cm.hasFocus()) + if ((options.autofocus && !mobile) || this.hasFocus()) setTimeout(bind(onFocus, this), 20) else onBlur(this) diff --git a/src/edit/methods.js b/src/edit/methods.js index 260ffa5d..f609d912 100644 --- a/src/edit/methods.js +++ b/src/edit/methods.js @@ -228,11 +228,11 @@ export default function(CodeMirror) { }), clearGutter: methodOp(function(gutterID) { - let cm = this, doc = cm.doc, i = doc.first + let doc = this.doc, i = doc.first doc.iter(line => { if (line.gutterMarkers && line.gutterMarkers[gutterID]) { line.gutterMarkers[gutterID] = null - regLineChange(cm, i, "gutter") + regLineChange(this, i, "gutter") if (isEmpty(line.gutterMarkers)) line.gutterMarkers = null } ++i @@ -315,10 +315,9 @@ export default function(CodeMirror) { }, moveH: methodOp(function(dir, unit) { - let cm = this - cm.extendSelectionsBy(range => { - if (cm.display.shift || cm.doc.extend || range.empty()) - return findPosH(cm.doc, range.head, dir, unit, cm.options.rtlMoveVisually) + this.extendSelectionsBy(range => { + if (this.display.shift || this.doc.extend || range.empty()) + return findPosH(this.doc, range.head, dir, unit, this.options.rtlMoveVisually) else return dir < 0 ? range.from() : range.to() }, sel_move) @@ -350,17 +349,17 @@ export default function(CodeMirror) { }, moveV: methodOp(function(dir, unit) { - let cm = this, doc = this.doc, goals = [] - let collapse = !cm.display.shift && !doc.extend && doc.sel.somethingSelected() + let doc = this.doc, goals = [] + let collapse = !this.display.shift && !doc.extend && doc.sel.somethingSelected() doc.extendSelectionsBy(range => { if (collapse) return dir < 0 ? range.from() : range.to() - let headPos = cursorCoords(cm, range.head, "div") + let headPos = cursorCoords(this, range.head, "div") if (range.goalColumn != null) headPos.left = range.goalColumn goals.push(headPos.left) - let pos = findPosV(cm, headPos, dir, unit) + let pos = findPosV(this, headPos, dir, unit) if (unit == "page" && range == doc.sel.primary()) - addToScrollPos(cm, null, charCoords(cm, pos, "div").top - headPos.top) + addToScrollPos(this, null, charCoords(this, pos, "div").top - headPos.top) return pos }, sel_move) if (goals.length) for (let i = 0; i < doc.sel.ranges.length; i++) @@ -435,19 +434,18 @@ export default function(CodeMirror) { }), setSize: methodOp(function(width, height) { - let cm = this let interpret = val => typeof val == "number" || /^\d+$/.test(String(val)) ? val + "px" : val - if (width != null) cm.display.wrapper.style.width = interpret(width) - if (height != null) cm.display.wrapper.style.height = interpret(height) - if (cm.options.lineWrapping) clearLineMeasurementCache(this) - let lineNo = cm.display.viewFrom - cm.doc.iter(lineNo, cm.display.viewTo, line => { + if (width != null) this.display.wrapper.style.width = interpret(width) + if (height != null) this.display.wrapper.style.height = interpret(height) + if (this.options.lineWrapping) clearLineMeasurementCache(this) + let lineNo = this.display.viewFrom + this.doc.iter(lineNo, this.display.viewTo, line => { if (line.widgets) for (let i = 0; i < line.widgets.length; i++) - if (line.widgets[i].noHScroll) { regLineChange(cm, lineNo, "widget"); break } + if (line.widgets[i].noHScroll) { regLineChange(this, lineNo, "widget"); break } ++lineNo }) - cm.curOp.forceUpdate = true - signal(cm, "refresh", this) + this.curOp.forceUpdate = true + signal(this, "refresh", this) }), operation: function(f){return runInOp(this, f)}, diff --git a/src/input/ContentEditableInput.js b/src/input/ContentEditableInput.js index 59ef55fa..fbb327f0 100644 --- a/src/input/ContentEditableInput.js +++ b/src/input/ContentEditableInput.js @@ -169,12 +169,11 @@ ContentEditableInput.prototype = copyObj({ }, startGracePeriod: function() { - let input = this clearTimeout(this.gracePeriod) this.gracePeriod = setTimeout(() => { - input.gracePeriod = false - if (input.selectionChanged()) - input.cm.operation(() => input.cm.curOp.selectionChanged = true) + this.gracePeriod = false + if (this.selectionChanged()) + this.cm.operation(() => this.cm.curOp.selectionChanged = true) }, 20) }, diff --git a/src/input/TextareaInput.js b/src/input/TextareaInput.js index 17f6f599..3193aa68 100644 --- a/src/input/TextareaInput.js +++ b/src/input/TextareaInput.js @@ -47,7 +47,7 @@ TextareaInput.prototype = copyObj({ if (ios) te.style.width = "0px" on(te, "input", () => { - if (ie && ie_version >= 9 && input.hasSelection) input.hasSelection = null + if (ie && ie_version >= 9 && this.hasSelection) this.hasSelection = null input.poll() }) @@ -185,11 +185,10 @@ TextareaInput.prototype = copyObj({ // Poll for input changes, using the normal rate of polling. This // runs as long as the editor is focused. slowPoll: function() { - let input = this - if (input.pollingFast) return - input.polling.set(this.cm.options.pollInterval, () => { - input.poll() - if (input.cm.state.focused) input.slowPoll() + if (this.pollingFast) return + this.polling.set(this.cm.options.pollInterval, () => { + this.poll() + if (this.cm.state.focused) this.slowPoll() }) }, @@ -245,18 +244,17 @@ TextareaInput.prototype = copyObj({ let same = 0, l = Math.min(prevInput.length, text.length) while (same < l && prevInput.charCodeAt(same) == text.charCodeAt(same)) ++same - let self = this runInOp(cm, () => { applyTextInput(cm, text.slice(same), prevInput.length - same, - null, self.composing ? "*compose" : null) + null, this.composing ? "*compose" : null) // Don't leave long text in the textarea, since it makes further polling slow - if (text.length > 1000 || text.indexOf("\n") > -1) input.value = self.prevInput = "" - else self.prevInput = text + if (text.length > 1000 || text.indexOf("\n") > -1) input.value = this.prevInput = "" + else this.prevInput = text - if (self.composing) { - self.composing.range.clear() - self.composing.range = cm.markText(self.composing.start, cm.getCursor("to"), + if (this.composing) { + this.composing.range.clear() + this.composing.range = cm.markText(this.composing.start, cm.getCursor("to"), {className: "CodeMirror-composing"}) } }) From 96cd8885ad759350273c71235493f6b225590442 Mon Sep 17 00:00:00 2001 From: Adrian Heine Date: Thu, 29 Sep 2016 22:45:48 +0200 Subject: [PATCH 0463/1790] Convert a few long CSS strings to template strings --- src/display/scrolling.js | 8 ++++---- src/display/selection.js | 6 +++--- src/input/TextareaInput.js | 8 ++++---- src/measurement/update_line.js | 16 ++++++++-------- 4 files changed, 19 insertions(+), 19 deletions(-) diff --git a/src/display/scrolling.js b/src/display/scrolling.js index d58efe0b..db9463cd 100644 --- a/src/display/scrolling.js +++ b/src/display/scrolling.js @@ -17,10 +17,10 @@ export function maybeScrollWindow(cm, coords) { 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) { - let scrollNode = elt("div", "\u200b", null, "position: absolute; top: " + - (coords.top - display.viewOffset - paddingTop(cm.display)) + "px; height: " + - (coords.bottom - coords.top + scrollGap(cm) + display.barHeight) + "px; left: " + - coords.left + "px; width: 2px;") + let scrollNode = elt("div", "\u200b", null, `position: absolute; + top: ${coords.top - display.viewOffset - paddingTop(cm.display)}px; + height: ${coords.bottom - coords.top + scrollGap(cm) + display.barHeight}px; + left: ${coords.left}px; width: 2px;`) cm.display.lineSpace.appendChild(scrollNode) scrollNode.scrollIntoView(doScroll) cm.display.lineSpace.removeChild(scrollNode) diff --git a/src/display/selection.js b/src/display/selection.js index 8380de82..e3f91240 100644 --- a/src/display/selection.js +++ b/src/display/selection.js @@ -57,9 +57,9 @@ function drawSelectionRange(cm, range, output) { if (top < 0) top = 0 top = Math.round(top) bottom = Math.round(bottom) - fragment.appendChild(elt("div", null, "CodeMirror-selected", "position: absolute; left: " + left + - "px; top: " + top + "px; width: " + (width == null ? rightSide - left : width) + - "px; height: " + (bottom - top) + "px")) + fragment.appendChild(elt("div", null, "CodeMirror-selected", `position: absolute; left: ${left}px; + top: ${top}px; width: ${width == null ? rightSide - left : width}px; + height: ${bottom - top}px`)) } function drawForLine(line, fromArg, toArg) { diff --git a/src/input/TextareaInput.js b/src/input/TextareaInput.js index 3193aa68..a150f05d 100644 --- a/src/input/TextareaInput.js +++ b/src/input/TextareaInput.js @@ -284,10 +284,10 @@ TextareaInput.prototype = copyObj({ let oldCSS = te.style.cssText, oldWrapperCSS = input.wrapper.style.cssText input.wrapper.style.cssText = "position: absolute" let wrapperBox = input.wrapper.getBoundingClientRect() - te.style.cssText = "position: absolute; width: 30px; height: 30px; top: " + (e.clientY - wrapperBox.top - 5) + - "px; left: " + (e.clientX - wrapperBox.left - 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);" + te.style.cssText = `position: absolute; width: 30px; height: 30px; + top: ${e.clientY - wrapperBox.top - 5}px; left: ${e.clientX - wrapperBox.left - 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);` let oldScrollY if (webkit) oldScrollY = window.scrollY // Work around Chrome issue (#2712) display.input.focus() diff --git a/src/measurement/update_line.js b/src/measurement/update_line.js index 4a80a766..cbfa657b 100644 --- a/src/measurement/update_line.js +++ b/src/measurement/update_line.js @@ -95,15 +95,15 @@ function updateLineGutter(cm, lineView, lineN, dims) { if (lineView.line.gutterClass) { let wrap = ensureLineWrapped(lineView) lineView.gutterBackground = elt("div", null, "CodeMirror-gutter-background " + lineView.line.gutterClass, - "left: " + (cm.options.fixedGutter ? dims.fixedPos : -dims.gutterTotalWidth) + - "px; width: " + dims.gutterTotalWidth + "px") + `left: ${cm.options.fixedGutter ? dims.fixedPos : -dims.gutterTotalWidth}px + width: ${dims.gutterTotalWidth}px`) wrap.insertBefore(lineView.gutterBackground, lineView.text) } let markers = lineView.line.gutterMarkers if (cm.options.lineNumbers || markers) { let wrap = ensureLineWrapped(lineView) - let gutterWrap = lineView.gutter = elt("div", null, "CodeMirror-gutter-wrapper", "left: " + - (cm.options.fixedGutter ? dims.fixedPos : -dims.gutterTotalWidth) + "px") + let gutterWrap = lineView.gutter = elt("div", null, "CodeMirror-gutter-wrapper", `left: + ${cm.options.fixedGutter ? dims.fixedPos : -dims.gutterTotalWidth}px`) cm.display.input.setUneditable(gutterWrap) wrap.insertBefore(gutterWrap, lineView.text) if (lineView.line.gutterClass) @@ -112,13 +112,13 @@ function updateLineGutter(cm, lineView, lineN, dims) { lineView.lineNumber = gutterWrap.appendChild( elt("div", lineNumberFor(cm.options, lineN), "CodeMirror-linenumber CodeMirror-gutter-elt", - "left: " + dims.gutterLeft["CodeMirror-linenumbers"] + "px; width: " - + cm.display.lineNumInnerWidth + "px")) + `left: ${dims.gutterLeft["CodeMirror-linenumbers"]}px; + width: ${cm.display.lineNumInnerWidth}px`)) if (markers) for (let k = 0; k < cm.options.gutters.length; ++k) { let id = cm.options.gutters[k], found = markers.hasOwnProperty(id) && markers[id] if (found) - gutterWrap.appendChild(elt("div", [found], "CodeMirror-gutter-elt", "left: " + - dims.gutterLeft[id] + "px; width: " + dims.gutterWidth[id] + "px")) + gutterWrap.appendChild(elt("div", [found], "CodeMirror-gutter-elt", + `left: ${dims.gutterLeft[id]}px; width: ${dims.gutterWidth[id]}px`)) } } } From 850671fa39bc5a6ab3c35340f22b87e1d98160eb Mon Sep 17 00:00:00 2001 From: Hasan Karahan Date: Fri, 30 Sep 2016 14:41:17 +0400 Subject: [PATCH 0464/1790] Fix duplicate cm- prefix in overlay styling Ensure that when an overlay is added that the class `cm-overlay` is used to denote a `span` instead of `cm-cm-overlay` (with the `cm-` prefix repeated twice). * E.g. before fix: ``` zpelling ``` * And after fix: ``` zpelling ``` --- src/line/highlight.js | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/src/line/highlight.js b/src/line/highlight.js index f54e7eaa..dcf020ad 100644 --- a/src/line/highlight.js +++ b/src/line/highlight.js @@ -32,12 +32,12 @@ export function highlightLine(cm, line, state, forceToEnd) { } if (!style) return if (overlay.opaque) { - st.splice(start, i - start, end, "cm-overlay " + style) + st.splice(start, i - start, end, "overlay " + style) i = start + 2 } else { for (; start < i; start += 2) { let cur = st[start+1] - st[start+1] = (cur ? cur + " " : "") + "cm-overlay " + style + st[start+1] = (cur ? cur + " " : "") + "overlay " + style } } }, lineClasses) From 920ecc1c6b82aa565a43702f3e7f1e766ab26849 Mon Sep 17 00:00:00 2001 From: Marijn Haverbeke Date: Fri, 30 Sep 2016 14:11:45 +0200 Subject: [PATCH 0465/1790] Fix ignoring of overlay styles in getTokenTypeAt --- src/edit/methods.js | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/edit/methods.js b/src/edit/methods.js index f609d912..b63f9bd2 100644 --- a/src/edit/methods.js +++ b/src/edit/methods.js @@ -136,7 +136,7 @@ export default function(CodeMirror) { else if (styles[mid * 2 + 1] < ch) before = mid + 1 else { type = styles[mid * 2 + 2]; break } } - let cut = type ? type.indexOf("cm-overlay ") : -1 + let cut = type ? type.indexOf("overlay ") : -1 return cut < 0 ? type : cut == 0 ? null : type.slice(0, cut - 1) }, From 27018275ec6912f4cbdeadb11d89e96d54f48ce9 Mon Sep 17 00:00:00 2001 From: Kyle Kelley Date: Sat, 1 Oct 2016 09:20:37 -0700 Subject: [PATCH 0466/1790] [real-world uses] Add nteract --- doc/realworld.html | 1 + 1 file changed, 1 insertion(+) diff --git a/doc/realworld.html b/doc/realworld.html index 1b272862..8db34cd9 100644 --- a/doc/realworld.html +++ b/doc/realworld.html @@ -123,6 +123,7 @@

    CodeMirror real-world uses

  • Navigate CMS
  • nodeMirror (IDE project)
  • NoTex (rST authoring)
  • +
  • nteract (interactive literate coding notebook)
  • Oak (online outliner)
  • OpenCampus
  • ORG (z80 assembly IDE)
  • From 1233299f9f4e697102ad2d30f9cff5015d2623a8 Mon Sep 17 00:00:00 2001 From: Marijn Haverbeke Date: Sun, 2 Oct 2016 11:42:32 +0200 Subject: [PATCH 0467/1790] [python mode] Don't highlight comments with odd indentation as error Closes #4276 --- mode/python/python.js | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/mode/python/python.js b/mode/python/python.js index efeed7f1..30f1428e 100644 --- a/mode/python/python.js +++ b/mode/python/python.js @@ -85,7 +85,7 @@ var lineOffset = stream.indentation(); if (lineOffset > scopeOffset) pushPyScope(state); - else if (lineOffset < scopeOffset && dedent(stream, state)) + else if (lineOffset < scopeOffset && dedent(stream, state) && stream.peek() != "#") state.errorToken = true; return null; } else { From 5f93b65403f06505f729b8336a0da8e7b46fe67c Mon Sep 17 00:00:00 2001 From: Marijn Haverbeke Date: Sun, 2 Oct 2016 12:11:40 +0200 Subject: [PATCH 0468/1790] [haskell mode] Make sure unfinished strings have token type string To make closebrackets work with this mode Closes #4277 --- mode/haskell/haskell.js | 6 +++--- 1 file changed, 3 insertions(+), 3 deletions(-) diff --git a/mode/haskell/haskell.js b/mode/haskell/haskell.js index fe0bab67..4197666a 100644 --- a/mode/haskell/haskell.js +++ b/mode/haskell/haskell.js @@ -56,7 +56,7 @@ CodeMirror.defineMode("haskell", function(_config, modeConfig) { if (source.eat('\'')) { return "string"; } - return "error"; + return "string error"; } if (ch == '"') { @@ -166,7 +166,7 @@ CodeMirror.defineMode("haskell", function(_config, modeConfig) { } } setState(normal); - return "error"; + return "string error"; } function stringGap(source, setState) { @@ -194,7 +194,7 @@ CodeMirror.defineMode("haskell", function(_config, modeConfig) { "module", "newtype", "of", "then", "type", "where", "_"); setType("keyword")( - "\.\.", ":", "::", "=", "\\", "\"", "<-", "->", "@", "~", "=>"); + "\.\.", ":", "::", "=", "\\", "<-", "->", "@", "~", "=>"); setType("builtin")( "!!", "$!", "$", "&&", "+", "++", "-", ".", "/", "/=", "<", "<=", "=<<", From 4cedb7e56ad865ac5825a18de2ddae65def32cce Mon Sep 17 00:00:00 2001 From: Marijn Haverbeke Date: Sun, 2 Oct 2016 12:12:05 +0200 Subject: [PATCH 0469/1790] Add lib/codemirror.js to .gitignore --- .gitignore | 1 + 1 file changed, 1 insertion(+) diff --git a/.gitignore b/.gitignore index f91c241f..01019cf1 100644 --- a/.gitignore +++ b/.gitignore @@ -6,3 +6,4 @@ *.swp .idea *.iml +/lib/codemirror.js From 7dbaf144c085ccdccc9ee3b8e8fced2a349d7eda Mon Sep 17 00:00:00 2001 From: Leon Sorokin Date: Sun, 2 Oct 2016 17:45:27 -0500 Subject: [PATCH 0470/1790] [dracula theme] consistent selection styling regardless of focus This prevents a selection from inheriting the default gray background when editor is defocused. --- theme/dracula.css | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/theme/dracula.css b/theme/dracula.css index 57f979ae..b2ef6291 100644 --- a/theme/dracula.css +++ b/theme/dracula.css @@ -16,7 +16,7 @@ .cm-s-dracula .CodeMirror-gutters { color: #282a36; } .cm-s-dracula .CodeMirror-cursor { border-left: solid thin #f8f8f0; } .cm-s-dracula .CodeMirror-linenumber { color: #6D8A88; } -.cm-s-dracula.CodeMirror-focused div.CodeMirror-selected { background: rgba(255, 255, 255, 0.10); } +.cm-s-dracula .CodeMirror-selected { background: rgba(255, 255, 255, 0.10); } .cm-s-dracula .CodeMirror-line::selection, .cm-s-dracula .CodeMirror-line > span::selection, .cm-s-dracula .CodeMirror-line > span > span::selection { background: rgba(255, 255, 255, 0.10); } .cm-s-dracula .CodeMirror-line::-moz-selection, .cm-s-dracula .CodeMirror-line > span::-moz-selection, .cm-s-dracula .CodeMirror-line > span > span::-moz-selection { background: rgba(255, 255, 255, 0.10); } .cm-s-dracula span.cm-comment { color: #6272a4; } From ba82593e49c3622f1d3053d4e239e1a22b3e10f3 Mon Sep 17 00:00:00 2001 From: Marijn Haverbeke Date: Tue, 4 Oct 2016 09:09:39 +0200 Subject: [PATCH 0471/1790] Remove IE7-related CSS kludges --- lib/codemirror.css | 6 ------ 1 file changed, 6 deletions(-) diff --git a/lib/codemirror.css b/lib/codemirror.css index 18b0bf70..d7821d17 100644 --- a/lib/codemirror.css +++ b/lib/codemirror.css @@ -206,9 +206,6 @@ div.CodeMirror span.CodeMirror-nonmatchingbracket {color: #f22;} display: inline-block; vertical-align: top; margin-bottom: -30px; - /* Hack to make IE7 behave */ - *zoom:1; - *display:inline; } .CodeMirror-gutter-wrapper { position: absolute; @@ -327,9 +324,6 @@ div.CodeMirror-dragcursors { background: rgba(255, 255, 0, .4); } -/* IE7 hack to prevent it from returning funny offsetTops on the spans */ -.CodeMirror span { *vertical-align: text-bottom; } - /* Used to force a border model for a node */ .cm-force-border { padding-right: .1px; } From fb78601b475393d0e534662cf19910e5582b4101 Mon Sep 17 00:00:00 2001 From: Marijn Haverbeke Date: Tue, 4 Oct 2016 09:12:15 +0200 Subject: [PATCH 0472/1790] Fix normalizing of s- prefix to key names --- src/input/keymap.js | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/input/keymap.js b/src/input/keymap.js index 1b9564ef..3a107bb4 100644 --- a/src/input/keymap.js +++ b/src/input/keymap.js @@ -57,7 +57,7 @@ function normalizeKeyName(name) { if (/^(cmd|meta|m)$/i.test(mod)) cmd = true else if (/^a(lt)?$/i.test(mod)) alt = true else if (/^(c|ctrl|control)$/i.test(mod)) ctrl = true - else if (/^s(hift)$/i.test(mod)) shift = true + else if (/^s(hift)?$/i.test(mod)) shift = true else throw new Error("Unrecognized modifier name: " + mod) } if (alt) name = "Alt-" + name From a194325bb17111d5fc6bfd5a384a9df6a30e9b4d Mon Sep 17 00:00:00 2001 From: Adrian Heine Date: Sun, 2 Oct 2016 16:37:31 +0200 Subject: [PATCH 0473/1790] Add regression test for #4078 --- test/test.js | 17 +++++++++++++++++ 1 file changed, 17 insertions(+) diff --git a/test/test.js b/test/test.js index a7658311..de2c66e8 100644 --- a/test/test.js +++ b/test/test.js @@ -1107,6 +1107,23 @@ testCM("measureEndOfLine", function(cm) { eqPos(cm.coordsChar({left: endPos.left, top: endPos.top + 5}), Pos(0, 18)); }, {mode: "text/html", value: "", lineWrapping: true}, ie_lt8 || opera_lt10); +testCM("measureWrappedEndOfLine", function(cm) { + if (phantom) return; + cm.setSize(null, "auto"); + var inner = byClassName(cm.getWrapperElement(), "CodeMirror-lines")[0].firstChild; + var lh = inner.offsetHeight; + 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; } + else break; + } + } + var endPos = cm.charCoords(Pos(0, 12)); // Next-to-last since last would wrap (#1862) + endPos.left += w; // Add width of editor just to be sure that we are behind last character + eqPos(cm.coordsChar(endPos), Pos(0, 13)); +}, {mode: "text/html", value: "0123456789abcde0123456789", lineWrapping: true}, ie_lt8 || opera_lt10); + testCM("scrollVerticallyAndHorizontally", function(cm) { if (cm.getOption("inputStyle") != "textarea") return; cm.setSize(100, 100); From 6b3b6a600101eb29a53a45f6117ba3db14ba17c2 Mon Sep 17 00:00:00 2001 From: Adrian Heine Date: Sun, 2 Oct 2016 13:44:45 +0200 Subject: [PATCH 0474/1790] Document transposeChars, fix some inconsistencies With non-empty selections, transposeChar would only work on the chars next to head and ignore the selected chars. Also, a cursor at line start was not moved on successful swapping. --- src/edit/commands.js | 14 ++++++++++++-- test/emacs_test.js | 2 +- 2 files changed, 13 insertions(+), 3 deletions(-) diff --git a/src/edit/commands.js b/src/edit/commands.js index fe39ed72..0b0125d1 100644 --- a/src/edit/commands.js +++ b/src/edit/commands.js @@ -106,9 +106,17 @@ export let commands = { if (cm.somethingSelected()) cm.indentSelection("add") else cm.execCommand("insertTab") }, + // Swap the two chars left and right of each selection's head. + // Move cursor behind the two swapped characters afterwards. + // + // Doesn't consider line feeds a character. + // Doesn't scan more than one line above to find a character. + // Doesn't do anything on an empty line. + // Doesn't do anything with non-empty selections. transposeChars: cm => runInOp(cm, () => { let ranges = cm.listSelections(), newSel = [] for (let i = 0; i < ranges.length; i++) { + if (!ranges[i].empty()) continue let cur = ranges[i].head, line = getLine(cm.doc, cur.line).text if (line) { if (cur.ch == line.length) cur = new Pos(cur.line, cur.ch - 1) @@ -118,10 +126,12 @@ export let commands = { Pos(cur.line, cur.ch - 2), cur, "+transpose") } else if (cur.line > cm.doc.first) { let prev = getLine(cm.doc, cur.line - 1).text - if (prev) + if (prev) { + cur = new Pos(cur.line, 1) cm.replaceRange(line.charAt(0) + cm.doc.lineSeparator() + prev.charAt(prev.length - 1), - Pos(cur.line - 1, prev.length - 1), Pos(cur.line, 1), "+transpose") + Pos(cur.line - 1, prev.length - 1), cur, "+transpose") + } } } newSel.push(new Range(cur, cur)) diff --git a/test/emacs_test.js b/test/emacs_test.js index 124575c7..628651c7 100644 --- a/test/emacs_test.js +++ b/test/emacs_test.js @@ -111,7 +111,7 @@ sim("transposeChar", "abcd\ne", "Ctrl-F", "Ctrl-T", "Ctrl-T", txt("bcad\ne"), at(0, 3), "Ctrl-F", "Ctrl-T", "Ctrl-T", "Ctrl-T", txt("bcda\ne"), at(0, 4), - "Ctrl-F", "Ctrl-T", txt("bcde\na"), at(1, 0)); + "Ctrl-F", "Ctrl-T", txt("bcde\na"), at(1, 1)); sim("manipWordCase", "foo BAR bAZ", "Alt-C", "Alt-L", "Alt-U", txt("Foo bar BAZ"), From f5751d0feb78bcb6c0307d4be527fba25341a691 Mon Sep 17 00:00:00 2001 From: Marijn Haverbeke Date: Thu, 6 Oct 2016 21:41:07 +0200 Subject: [PATCH 0475/1790] [javascript mode] Support TypeScript return type annotations on arrow functions Closes #4256 --- mode/javascript/javascript.js | 5 +++++ 1 file changed, 5 insertions(+) diff --git a/mode/javascript/javascript.js b/mode/javascript/javascript.js index 65f85f3a..8ff38b81 100644 --- a/mode/javascript/javascript.js +++ b/mode/javascript/javascript.js @@ -209,6 +209,11 @@ CodeMirror.defineMode("javascript", function(config, parserConfig) { var arrow = stream.string.indexOf("=>", stream.start); if (arrow < 0) return; + if (isTS) { // Try to skip TypeScript return type declarations after the arguments + var m = /:\s*(?:\w+(?:<[^>]*>|\[\])?|\{[^}]*\})\s*$/.exec(stream.string.slice(stream.start, arrow)) + if (m) arrow = m.index + } + var depth = 0, sawSomething = false; for (var pos = arrow - 1; pos >= 0; --pos) { var ch = stream.string.charAt(pos); From 437f3842a2b339297f9934e5d6cb9ce861c05e0a Mon Sep 17 00:00:00 2001 From: mtaran-google Date: Tue, 11 Oct 2016 13:39:30 -0700 Subject: [PATCH 0476/1790] [sublime] Fix unfoldAll "ctrl+K ctrl+J" shortcut --- keymap/sublime.js | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/keymap/sublime.js b/keymap/sublime.js index db9f54de..fada04c0 100644 --- a/keymap/sublime.js +++ b/keymap/sublime.js @@ -570,7 +570,7 @@ map["Shift-" + ctrl + "["] = "fold"; map["Shift-" + ctrl + "]"] = "unfold"; - map[cK + ctrl + "0"] = map[cK + ctrl + "j"] = "unfoldAll"; + map[cK + ctrl + "0"] = map[cK + ctrl + "J"] = "unfoldAll"; map[ctrl + "I"] = "findIncremental"; map["Shift-" + ctrl + "I"] = "findIncrementalReverse"; From 9903967353f5ca1a1f9a14e3d3f98809ef0aee5e Mon Sep 17 00:00:00 2001 From: mtaran-google Date: Mon, 10 Oct 2016 18:28:59 -0700 Subject: [PATCH 0477/1790] [markdown] auto-close backticks --- mode/markdown/markdown.js | 1 + 1 file changed, 1 insertion(+) diff --git a/mode/markdown/markdown.js b/mode/markdown/markdown.js index 9dd44574..3dcce8d3 100644 --- a/mode/markdown/markdown.js +++ b/mode/markdown/markdown.js @@ -809,6 +809,7 @@ CodeMirror.defineMode("markdown", function(cmCfg, modeCfg) { getType: getType, + closeBrackets: "()[]{}''\"\"``", fold: "markdown" }; return mode; From 358086a5694d67424ccaa46c1bc0c9a16d4b3e09 Mon Sep 17 00:00:00 2001 From: mtaran-google Date: Mon, 10 Oct 2016 18:11:48 -0700 Subject: [PATCH 0478/1790] [go] auto-close backticks --- mode/go/go.js | 1 + 1 file changed, 1 insertion(+) diff --git a/mode/go/go.js b/mode/go/go.js index 3c9ef6b9..803a5ba2 100644 --- a/mode/go/go.js +++ b/mode/go/go.js @@ -173,6 +173,7 @@ CodeMirror.defineMode("go", function(config) { }, electricChars: "{}):", + closeBrackets: "()[]{}''\"\"``", fold: "brace", blockCommentStart: "/*", blockCommentEnd: "*/", From 147389d43293bc63fcab10e1c7d34f0c0a921016 Mon Sep 17 00:00:00 2001 From: mtaran-google Date: Mon, 10 Oct 2016 18:10:36 -0700 Subject: [PATCH 0479/1790] [shell] auto-close backticks --- mode/shell/shell.js | 1 + 1 file changed, 1 insertion(+) diff --git a/mode/shell/shell.js b/mode/shell/shell.js index a684e8c2..570b4e24 100644 --- a/mode/shell/shell.js +++ b/mode/shell/shell.js @@ -129,6 +129,7 @@ CodeMirror.defineMode('shell', function() { token: function(stream, state) { return tokenize(stream, state); }, + closeBrackets: "()[]{}''\"\"``", lineComment: '#', fold: "brace" }; From 0e4c156a9ab63dcbc5fa9f60ada54e3c5d5ad03b Mon Sep 17 00:00:00 2001 From: Philipp A Date: Tue, 11 Oct 2016 14:03:48 +0200 Subject: [PATCH 0480/1790] [r mode] Various improvements Closes #4297 --- mode/r/index.html | 65 +++++++++++++++++++++++++---------------------- mode/r/r.js | 15 ++++++++--- 2 files changed, 45 insertions(+), 35 deletions(-) diff --git a/mode/r/index.html b/mode/r/index.html index 6dd96346..01b6e664 100644 --- a/mode/r/index.html +++ b/mode/r/index.html @@ -31,47 +31,50 @@

    R mode

    + @@ -109,6 +110,7 @@

    Test Suite

    + From 55fb95f0e859c23b5eba667ac91e2fff7b07f375 Mon Sep 17 00:00:00 2001 From: Marijn Haverbeke Date: Mon, 17 Oct 2016 21:21:39 +0200 Subject: [PATCH 0493/1790] Move measurement/update_line to display/ Because it has nothing to do with measurement. --- src/display/update_display.js | 2 +- src/{measurement => display}/update_line.js | 0 src/measurement/position_measurement.js | 2 +- 3 files changed, 2 insertions(+), 2 deletions(-) rename src/{measurement => display}/update_line.js (100%) diff --git a/src/display/update_display.js b/src/display/update_display.js index a8d6a4de..4e016a25 100644 --- a/src/display/update_display.js +++ b/src/display/update_display.js @@ -2,12 +2,12 @@ import { sawCollapsedSpans } from "../line/saw_special_spans" import { heightAtLine, visualLineEndNo, visualLineNo } from "../line/spans" import { getLine, lineNumberFor } from "../line/utils_line" import { displayHeight, displayWidth, getDimensions, paddingVert, scrollGap } from "../measurement/position_measurement" -import { buildLineElement, updateLineForChanges } from "../measurement/update_line" import { mac, webkit } from "../util/browser" import { activeElt, removeChildren } from "../util/dom" import { hasHandler, signal } from "../util/event" import { indexOf } from "../util/misc" +import { buildLineElement, updateLineForChanges } from "./update_line" import { startWorker } from "./highlight_worker" import { maybeUpdateLineNumberWidth } from "./line_numbers" import { measureForScrollbars, updateScrollbars } from "./scrollbars" diff --git a/src/measurement/update_line.js b/src/display/update_line.js similarity index 100% rename from src/measurement/update_line.js rename to src/display/update_line.js diff --git a/src/measurement/position_measurement.js b/src/measurement/position_measurement.js index 5688836e..4ece080e 100644 --- a/src/measurement/position_measurement.js +++ b/src/measurement/position_measurement.js @@ -8,8 +8,8 @@ import { elt, removeChildren, range, removeChildrenAndAdd } from "../util/dom" import { e_target } from "../util/event" import { hasBadZoomedRects } from "../util/feature_detection" import { countColumn, isExtendingChar, scrollerGap } from "../util/misc" +import { updateLineForChanges } from "../display/update_line" -import { updateLineForChanges } from "./update_line" import { widgetHeight } from "./widgets" // POSITION MEASUREMENT From 05115c54a6d168535d78d303a21ce027fa4647d0 Mon Sep 17 00:00:00 2001 From: Marijn Haverbeke Date: Tue, 18 Oct 2016 22:06:08 +0200 Subject: [PATCH 0494/1790] [css mode] Make translateX/Y/Z highlight properly Closes #4325 --- 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 ea7bd01d..b6be4cd5 100644 --- a/mode/css/css.js +++ b/mode/css/css.js @@ -672,7 +672,7 @@ CodeMirror.defineMode("css", function(config, parserConfig) { "threedlightshadow", "threedshadow", "tibetan", "tigre", "tigrinya-er", "tigrinya-er-abegede", "tigrinya-et", "tigrinya-et-abegede", "to", "top", "trad-chinese-formal", "trad-chinese-informal", - "translate", "translate3d", "translateX", "translateY", "translateZ", + "translate", "translate3d", "translatex", "translatey", "translatez", "transparent", "ultra-condensed", "ultra-expanded", "underline", "up", "upper-alpha", "upper-armenian", "upper-greek", "upper-hexadecimal", "upper-latin", "upper-norwegian", "upper-roman", "uppercase", "urdu", "url", From 344c8d560f7f4ea5c2a68209600e3689fbd61193 Mon Sep 17 00:00:00 2001 From: Marijn Haverbeke Date: Tue, 18 Oct 2016 22:28:49 +0200 Subject: [PATCH 0495/1790] [css mode] Better fix for mixed-case keywords Closes #4325 --- 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 b6be4cd5..e56e3dd8 100644 --- a/mode/css/css.js +++ b/mode/css/css.js @@ -414,7 +414,7 @@ CodeMirror.defineMode("css", function(config, parserConfig) { function keySet(array) { var keys = {}; for (var i = 0; i < array.length; ++i) { - keys[array[i]] = true; + keys[array[i].toLowerCase()] = true; } return keys; } @@ -672,7 +672,7 @@ CodeMirror.defineMode("css", function(config, parserConfig) { "threedlightshadow", "threedshadow", "tibetan", "tigre", "tigrinya-er", "tigrinya-er-abegede", "tigrinya-et", "tigrinya-et-abegede", "to", "top", "trad-chinese-formal", "trad-chinese-informal", - "translate", "translate3d", "translatex", "translatey", "translatez", + "translate", "translate3d", "translateX", "translateY", "translateZ", "transparent", "ultra-condensed", "ultra-expanded", "underline", "up", "upper-alpha", "upper-armenian", "upper-greek", "upper-hexadecimal", "upper-latin", "upper-norwegian", "upper-roman", "uppercase", "urdu", "url", From edd8e6180a5209d20c175ce6aae43b3621dee4cb Mon Sep 17 00:00:00 2001 From: Marijn Haverbeke Date: Thu, 20 Oct 2016 09:50:25 +0200 Subject: [PATCH 0496/1790] [closebrackets addon] Don't skip over quotes at the start of a string Closes #4287 --- addon/edit/closebrackets.js | 9 ++++++++- 1 file changed, 8 insertions(+), 1 deletion(-) diff --git a/addon/edit/closebrackets.js b/addon/edit/closebrackets.js index af7fce2a..7c47bcd0 100644 --- a/addon/edit/closebrackets.js +++ b/addon/edit/closebrackets.js @@ -116,7 +116,9 @@ if (opening && !range.empty()) { curType = "surround"; } else if ((identical || !opening) && next == ch) { - if (triples.indexOf(ch) >= 0 && cm.getRange(cur, Pos(cur.line, cur.ch + 3)) == ch + ch + ch) + if (identical && stringStartsAfter(cm, cur)) + curType = "both"; + else if (triples.indexOf(ch) >= 0 && cm.getRange(cur, Pos(cur.line, cur.ch + 3)) == ch + ch + ch) curType = "skipThree"; else curType = "skip"; @@ -192,4 +194,9 @@ stream.start = stream.pos; } } + + function stringStartsAfter(cm, pos) { + var token = cm.getTokenAt(Pos(pos.line, pos.ch + 1)) + return /\bstring/.test(token.type) && token.start == pos.ch + } }); From b25f85746f7b036642ebd74cd9496a36b636cbbd Mon Sep 17 00:00:00 2001 From: Marijn Haverbeke Date: Thu, 20 Oct 2016 09:58:35 +0200 Subject: [PATCH 0497/1790] Normalize line endings in inserted/pasted content Issue #4289 --- src/input/input.js | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-) diff --git a/src/input/input.js b/src/input/input.js index e51a90a1..57e4ea53 100644 --- a/src/input/input.js +++ b/src/input/input.js @@ -7,6 +7,7 @@ import { ios, webkit } from "../util/browser" import { elt } from "../util/dom" import { lst, map } from "../util/misc" import { signalLater } from "../util/operation_group" +import { splitLinesAuto } from "../util/feature_detection" import { indentLine } from "./indent" @@ -25,7 +26,7 @@ export function applyTextInput(cm, inserted, deleted, sel, origin) { if (!sel) sel = doc.sel let paste = cm.state.pasteIncoming || origin == "paste" - let textLines = doc.splitLines(inserted), multiPaste = null + let textLines = splitLinesAuto(inserted), multiPaste = null // When pasing N lines into N selections, insert one line per selection if (paste && sel.ranges.length > 1) { if (lastCopied && lastCopied.text.join("\n") == inserted) { From 1502f805a868a2e3ee87d0574ff18e592fd9785f Mon Sep 17 00:00:00 2001 From: Todd Berman Date: Thu, 6 Oct 2016 14:37:54 -0700 Subject: [PATCH 0498/1790] Add `mergeMarkerLocation` to options to allow customization --- addon/merge/merge.js | 33 +++++++++++++++++++++++++-------- doc/manual.html | 6 +++++- 2 files changed, 30 insertions(+), 9 deletions(-) diff --git a/addon/merge/merge.js b/addon/merge/merge.js index cc94cafb..2cfeabe4 100644 --- a/addon/merge/merge.js +++ b/addon/merge/merge.js @@ -40,6 +40,7 @@ (this.edit.state.diffViews || (this.edit.state.diffViews = [])).push(this); this.orig = CodeMirror(pane, copyObj({value: orig, readOnly: !this.mv.options.allowEditingOriginals}, copyObj(options))); this.orig.state.diffViews = [this]; + this.classes.location = options.mergeMarkerLocation || "background"; this.diff = getDiff(asString(orig), asString(options.value)); this.chunks = getChunks(this.diff); @@ -192,14 +193,20 @@ // Updating the marks for editor content function clearMarks(editor, arr, classes) { + function removeLineClass(l, klass) { + var classList = Array.isArray(classes.location) ? classes.location : [classes.location] + for (var c = 0; c < classList.length; ++c) { + editor.removeLineClass(l, classList[c], klass) + } + } for (var i = 0; i < arr.length; ++i) { var mark = arr[i]; if (mark instanceof CodeMirror.TextMarker) { mark.clear(); } else if (mark.parent) { - editor.removeLineClass(mark, "background", classes.chunk); - editor.removeLineClass(mark, "background", classes.start); - editor.removeLineClass(mark, "background", classes.end); + removeLineClass(mark, classes.chunk); + removeLineClass(mark, classes.start); + removeLineClass(mark, classes.end); } } arr.length = 0; @@ -231,19 +238,29 @@ var top = Pos(from, 0), bot = editor.clipPos(Pos(to - 1)); var cls = type == DIFF_DELETE ? classes.del : classes.insert; function markChunk(start, end) { + function addLineClass(l, klass) { + var classList = Array.isArray(classes.location) ? classes.location : [classes.location] + var ret = null + for (var c = 0; c < classList.length; ++c) { + ret = editor.addLineClass(l, classList[c], klass) + } + + return ret + } + var bfrom = Math.max(from, start), bto = Math.min(to, end); for (var i = bfrom; i < bto; ++i) { - var line = editor.addLineClass(i, "background", classes.chunk); - if (i == start) editor.addLineClass(line, "background", classes.start); - if (i == end - 1) editor.addLineClass(line, "background", classes.end); + var line = addLineClass(i, classes.chunk); + if (i == start) addLineClass(line, classes.start); + if (i == end - 1) addLineClass(line, classes.end); marks.push(line); } // When the chunk is empty, make sure a horizontal line shows up if (start == end && bfrom == end && bto == end) { if (bfrom) - marks.push(editor.addLineClass(bfrom - 1, "background", classes.end)); + marks.push(addLineClass(bfrom - 1, classes.end)); else - marks.push(editor.addLineClass(bfrom, "background", classes.start)); + marks.push(addLineClass(bfrom, classes.start)); } } diff --git a/doc/manual.html b/doc/manual.html index 65e030c0..baa8356f 100644 --- a/doc/manual.html +++ b/doc/manual.html @@ -2583,7 +2583,7 @@

    Addons

    Optional to position that will be used by pick() instead of the global one passed with the full list of completions.
    - +
    The plugin understands the following options (the options object will also be passed along to the hinting function, which may understand additional options): @@ -2986,6 +2986,10 @@

    Addons

    showDifferences: boolean
    When true (the default), changed pieces of text are highlighted.
    +
    mergeMarkerLocations: string|Array
    +
    By default the merge highlights are added using addLineClass with "background". + Override this to customize it to be any valid `where` parameter or an Array of valid `where` + parameters.
    The addon also defines commands "goNextDiff" and "goPrevDiff" to quickly jump to the next From 9a316b33d72439cf52ab189ab1e60a16e8b15fe5 Mon Sep 17 00:00:00 2001 From: Marijn Haverbeke Date: Thu, 20 Oct 2016 10:27:34 +0200 Subject: [PATCH 0499/1790] Update previous patch to change option name and clean up code Issue #4288 --- addon/merge/merge.js | 60 +++++++++++++++++++++----------------------- doc/manual.html | 8 +++--- 2 files changed, 34 insertions(+), 34 deletions(-) diff --git a/addon/merge/merge.js b/addon/merge/merge.js index 2cfeabe4..2f53406e 100644 --- a/addon/merge/merge.js +++ b/addon/merge/merge.js @@ -40,7 +40,9 @@ (this.edit.state.diffViews || (this.edit.state.diffViews = [])).push(this); this.orig = CodeMirror(pane, copyObj({value: orig, readOnly: !this.mv.options.allowEditingOriginals}, copyObj(options))); this.orig.state.diffViews = [this]; - this.classes.location = options.mergeMarkerLocation || "background"; + var classLocation = options.chunkClassLocation || "background"; + if (Object.prototype.toString.call(classLocation) != "[object Array]") classLocation = [classLocation] + this.classes.classLocation = classLocation this.diff = getDiff(asString(orig), asString(options.value)); this.chunks = getChunks(this.diff); @@ -192,22 +194,22 @@ // Updating the marks for editor content - function clearMarks(editor, arr, classes) { - function removeLineClass(l, klass) { - var classList = Array.isArray(classes.location) ? classes.location : [classes.location] - for (var c = 0; c < classList.length; ++c) { - editor.removeLineClass(l, classList[c], klass) - } + function removeClass(editor, line, classes) { + var locs = classes.classLocation + for (var i = 0; i < locs.length; i++) { + editor.removeLineClass(line, locs[i], classes.chunk); + editor.removeLineClass(line, locs[i], classes.start); + editor.removeLineClass(line, locs[i], classes.chunk); } + } + + function clearMarks(editor, arr, classes) { for (var i = 0; i < arr.length; ++i) { var mark = arr[i]; - if (mark instanceof CodeMirror.TextMarker) { + if (mark instanceof CodeMirror.TextMarker) mark.clear(); - } else if (mark.parent) { - removeLineClass(mark, classes.chunk); - removeLineClass(mark, classes.start); - removeLineClass(mark, classes.end); - } + else if (mark.parent) + removeClass(editor, mark, classes); } arr.length = 0; } @@ -233,34 +235,30 @@ }); } + function addClass(editor, lineNr, classes, main, start, end) { + var locs = classes.classLocation, line = editor.getLineHandle(lineNr); + for (var i = 0; i < locs.length; i++) { + if (main) editor.addLineClass(line, locs[i], classes.chunk); + if (start) editor.addLineClass(line, locs[i], classes.start); + if (end) editor.addLineClass(line, locs[i], classes.end); + } + return line; + } + function markChanges(editor, diff, type, marks, from, to, classes) { var pos = Pos(0, 0); var top = Pos(from, 0), bot = editor.clipPos(Pos(to - 1)); var cls = type == DIFF_DELETE ? classes.del : classes.insert; function markChunk(start, end) { - function addLineClass(l, klass) { - var classList = Array.isArray(classes.location) ? classes.location : [classes.location] - var ret = null - for (var c = 0; c < classList.length; ++c) { - ret = editor.addLineClass(l, classList[c], klass) - } - - return ret - } - var bfrom = Math.max(from, start), bto = Math.min(to, end); - for (var i = bfrom; i < bto; ++i) { - var line = addLineClass(i, classes.chunk); - if (i == start) addLineClass(line, classes.start); - if (i == end - 1) addLineClass(line, classes.end); - marks.push(line); - } + for (var i = bfrom; i < bto; ++i) + marks.push(addClass(editor, i, classes, true, i == start, i == end - 1)); // When the chunk is empty, make sure a horizontal line shows up if (start == end && bfrom == end && bto == end) { if (bfrom) - marks.push(addLineClass(bfrom - 1, classes.end)); + marks.push(addClass(editor, bfrom - 1, classes, false, false, true)); else - marks.push(addLineClass(bfrom, classes.start)); + marks.push(addClass(editor, bfrom, classes, false, true, false)); } } diff --git a/doc/manual.html b/doc/manual.html index baa8356f..f9e99465 100644 --- a/doc/manual.html +++ b/doc/manual.html @@ -2986,9 +2986,11 @@

    Addons

    showDifferences: boolean
    When true (the default), changed pieces of text are highlighted.
    -
    mergeMarkerLocations: string|Array
    -
    By default the merge highlights are added using addLineClass with "background". - Override this to customize it to be any valid `where` parameter or an Array of valid `where` +
    chunkClassLocation: string|Array
    +
    By default the chunk highlights are added + using addLineClass + with "background". Override this to customize it to be any + valid `where` parameter or an Array of valid `where` parameters.
    The addon also defines commands "goNextDiff" From 7484049deddcff8ccc9a7ff1989adb4bb00a9e80 Mon Sep 17 00:00:00 2001 From: themrmax Date: Mon, 10 Oct 2016 10:34:54 +1100 Subject: [PATCH 0500/1790] add quickstart to project readme --- README.md | 6 ++++++ 1 file changed, 6 insertions(+) diff --git a/README.md b/README.md index ff2622a2..84466293 100644 --- a/README.md +++ b/README.md @@ -26,3 +26,9 @@ The CodeMirror community aims to be welcoming to everybody. We use the [Contributor Covenant (1.1)](http://contributor-covenant.org/version/1/1/0/) as our code of conduct. + +### Quickstart + +To build the project, make sure you have Node.js installed (at least version 6) +and then `npm install && npm build`. To run, just open `index.html` in your +browser (you don't need to run a webserver). Run the tests with `npm test`. From e1c3b9e9e7ab876abf4fb3458006b205709367c4 Mon Sep 17 00:00:00 2001 From: Marijn Haverbeke Date: Thu, 20 Oct 2016 11:00:48 +0200 Subject: [PATCH 0501/1790] [lint addon] Provide a way to show tooltips only on the gutter Closes #4308 --- 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 e3a45276..c1f1702f 100644 --- a/addon/lint/lint.js +++ b/addon/lint/lint.js @@ -226,7 +226,7 @@ var state = cm.state.lint = new LintState(cm, parseOptions(cm, val), hasLintGutter); if (state.options.lintOnChange !== false) cm.on("change", onChange); - if (state.options.tooltips != false) + if (state.options.tooltips != false && state.options.tooltips != "gutter") CodeMirror.on(cm.getWrapperElement(), "mouseover", state.onMouseOver); startLinting(cm); From 1523a8b79afab2618e7bedaaf34adf5f232fd15b Mon Sep 17 00:00:00 2001 From: Mu-An Chiou Date: Wed, 12 Oct 2016 17:02:30 +0800 Subject: [PATCH 0502/1790] Fix addRange behavior in contenteditable mode When selecting at the beginning of a line ranges need to be removed for new range to be added properly. (in Safari) --- src/input/ContentEditableInput.js | 5 ++++- 1 file changed, 4 insertions(+), 1 deletion(-) diff --git a/src/input/ContentEditableInput.js b/src/input/ContentEditableInput.js index fbb327f0..cdaa8254 100644 --- a/src/input/ContentEditableInput.js +++ b/src/input/ContentEditableInput.js @@ -157,7 +157,10 @@ ContentEditableInput.prototype = copyObj({ if (rng) { if (!gecko && this.cm.state.focused) { sel.collapse(start.node, start.offset) - if (!rng.collapsed) sel.addRange(rng) + if (!rng.collapsed) { + sel.removeAllRanges() + sel.addRange(rng) + } } else { sel.removeAllRanges() sel.addRange(rng) From 763575c2bd91747c7b44a70ebc24f79b85d71e6e Mon Sep 17 00:00:00 2001 From: Marijn Haverbeke Date: Thu, 20 Oct 2016 11:29:52 +0200 Subject: [PATCH 0503/1790] Mark release 5.20.0 --- AUTHORS | 12 ++++++++++++ CHANGELOG.md | 24 ++++++++++++++++++++++++ doc/manual.html | 2 +- doc/releases.html | 14 ++++++++++++++ index.html | 2 +- package.json | 2 +- 6 files changed, 53 insertions(+), 3 deletions(-) diff --git a/AUTHORS b/AUTHORS index ada8c3f9..e2cb74a5 100644 --- a/AUTHORS +++ b/AUTHORS @@ -9,6 +9,7 @@ Adam King adanlobato Adán Lobato Adrian Aichner +Adrian Heine aeroson Ahmad Amireh Ahmad M. Zawawi @@ -61,10 +62,12 @@ anthonygego Anthony Gégo Anthony Grimes Anton Kovalyov +Apollo Zhu AQNOUCH Mohammed areos Arnab Bose as3boyan +atelierbram AtomicPages LLC Atul Bhouraskar Aurelian Oancea @@ -151,6 +154,7 @@ deebugger Deep Thought Devin Abbott Devon Carew +Dick Choi dignifiedquire Dimage Sapelkin Dmitry Kiselyov @@ -284,6 +288,7 @@ John Van Der Loo Jon Ander Peñalba Jonas Döbertin Jonathan Malmaud +Jon Gacnik jongalloway Jon Malmaud Jon Sangster @@ -321,6 +326,7 @@ Kris Ciccarello ks-ifware kubelsmieci KwanEsq +Kyle Kelley Lanfei Lanny Laszlo Vidacs @@ -336,6 +342,7 @@ lochel Lorenzo Stoakes Luciano Longo Lu Fangjian +Luke Browning Luke Granger-Brown Luke Stagner lynschinzer @@ -358,6 +365,7 @@ Mario Pietsch Mark Anderson Mark Lentczner Marko Bonaci +Mark Peace Markus Bordihn Martin Balek Martín Gaitán @@ -415,6 +423,7 @@ Moritz Schwörer mps ms mtaran-google +Mu-An Chiou Narciso Jaramillo Nathan Williams ndr @@ -439,6 +448,7 @@ nlwillia noragrossman Norman Rzepka Oreoluwa Onatemowo +Oskar Segersvärd pablo pabloferz Page @@ -547,6 +557,7 @@ tfjgeorge Thaddee Tyl thanasis TheHowl +themrmax think Thomas Dvornik Thomas Kluyver @@ -557,6 +568,7 @@ Timothy Farrell Timothy Gu Timothy Hatcher TobiasBg +Todd Berman Tomas-A Tomas Varaneckas Tom Erik Støwer diff --git a/CHANGELOG.md b/CHANGELOG.md index f368485c..b2435465 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -1,3 +1,27 @@ +## 5.20.0 (2016-10-20) + +### Bug fixes + +Make `newlineAndIndent` command work with multiple cursors on the same line. + +Make sure keypress events for backspace are ignored. + +Tokens styled with overlays no longer get a nonsense `cm-cm-overlay` class. + +Line endings for pasted content are now normalized to the editor's [preferred ending](http://codemirror.net/doc/manual.html#option_lineSeparator). + +[javascript mode](http://codemirror.net/mode/javascript): Improve support for class expressions. Support TypeScript optional class properties, the `abstract` keyword, and return type declarations for arrow functions. + +[css mode](http://codemirror.net/mode/css): Fix highlighting of mixed-case keywords. + +[closebrackets addon](http://codemirror.net/doc/manual.html#addon_closebrackets): Improve behavior when typing a quote before a string. + +### New features + +The core is now maintained as a number of small files, using ES6 syntax and modules, under the `src/` directory. A git checkout no longer contains a working `codemirror.js` until you `npm build` (but when installing from NPM, it is included). + +The [`refresh`](http://codemirror.net/doc/manual.html#event_refresh) event is now documented and stable. + ## 5.19.0 (2016-09-20) ### Bugfixes diff --git a/doc/manual.html b/doc/manual.html index f9e99465..36354b71 100644 --- a/doc/manual.html +++ b/doc/manual.html @@ -69,7 +69,7 @@

    User manual and reference guide - version 5.19.1 + version 5.20.0

    CodeMirror is a code-editor component that can be embedded in diff --git a/doc/releases.html b/doc/releases.html index 5a0c6b71..cfc366f3 100644 --- a/doc/releases.html +++ b/doc/releases.html @@ -30,6 +30,20 @@

    Release notes and version history

    Version 5.x

    +

    20-10-2016: Version 5.20.0:

    + +
      +
    • Make newlineAndIndent command work with multiple cursors on the same line.
    • +
    • Make sure keypress events for backspace are ignored.
    • +
    • Tokens styled with overlays no longer get a nonsense cm-cm-overlay class.
    • +
    • Line endings for pasted content are now normalized to the editor's preferred ending.
    • +
    • javascript mode: Improve support for class expressions. Support TypeScript optional class properties, the abstract keyword, and return type declarations for arrow functions.
    • +
    • css mode: Fix highlighting of mixed-case keywords.
    • +
    • closebrackets addon: Improve behavior when typing a quote before a string.
    • +
    • The core is now maintained as a number of small files, using ES6 syntax and modules, under the src/ directory. A git checkout no longer contains a working codemirror.js until you npm build (but when installing from NPM, it is included).
    • +
    • The refresh event is now documented and stable.
    • +
    +

    20-09-2016: Version 5.19.0:

      diff --git a/index.html b/index.html index 3423eab2..f4e56a70 100644 --- a/index.html +++ b/index.html @@ -96,7 +96,7 @@

      This is CodeMirror

      - Get the current version: 5.19.0.
      + Get the current version: 5.20.0.
      You can see the code,
      read the release notes,
      or study the user manual. diff --git a/package.json b/package.json index 057280c5..74040d56 100644 --- a/package.json +++ b/package.json @@ -1,6 +1,6 @@ { "name": "codemirror", - "version": "5.19.1", + "version": "5.20.0", "main": "lib/codemirror.js", "description": "Full-featured in-browser code editor", "license": "MIT", From 81d7f09b6de4044e2945bed39edb48e61d1f5495 Mon Sep 17 00:00:00 2001 From: Marijn Haverbeke Date: Thu, 20 Oct 2016 11:33:05 +0200 Subject: [PATCH 0504/1790] Bump version number post-5.20 --- doc/manual.html | 2 +- package.json | 2 +- 2 files changed, 2 insertions(+), 2 deletions(-) diff --git a/doc/manual.html b/doc/manual.html index 36354b71..0cff1be8 100644 --- a/doc/manual.html +++ b/doc/manual.html @@ -69,7 +69,7 @@

      User manual and reference guide - version 5.20.0 + version 5.20.1

      CodeMirror is a code-editor component that can be embedded in diff --git a/package.json b/package.json index 74040d56..7dbf4751 100644 --- a/package.json +++ b/package.json @@ -1,6 +1,6 @@ { "name": "codemirror", - "version": "5.20.0", + "version": "5.20.1", "main": "lib/codemirror.js", "description": "Full-featured in-browser code editor", "license": "MIT", From cea0e041e3fcefdb0869891cb02880bdc07ab966 Mon Sep 17 00:00:00 2001 From: Marijn Haverbeke Date: Thu, 20 Oct 2016 15:39:49 +0200 Subject: [PATCH 0505/1790] Drop bower.json To further underline our lack of commitment to having the github repository serve as a package distribution mechanism. --- bower.json | 17 ----------------- 1 file changed, 17 deletions(-) delete mode 100644 bower.json diff --git a/bower.json b/bower.json deleted file mode 100644 index 903d9f55..00000000 --- a/bower.json +++ /dev/null @@ -1,17 +0,0 @@ -{ - "name": "codemirror", - "main": ["lib/codemirror.js", "lib/codemirror.css"], - "ignore": [ - "**/.*", - "node_modules", - "components", - "bin", - "demo", - "doc", - "test", - "index.html", - "package.json", - "mode/*/*test.js", - "mode/*/*.html" - ] -} From 33f2044c8105b37b5f4b4e60893d457f696ba634 Mon Sep 17 00:00:00 2001 From: Marijn Haverbeke Date: Thu, 20 Oct 2016 21:56:21 +0200 Subject: [PATCH 0506/1790] Fix installation instruction Closes #4333 --- README.md | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/README.md b/README.md index 84466293..1b330988 100644 --- a/README.md +++ b/README.md @@ -30,5 +30,5 @@ conduct. ### Quickstart To build the project, make sure you have Node.js installed (at least version 6) -and then `npm install && npm build`. To run, just open `index.html` in your +and then `npm install && npm run build`. To run, just open `index.html` in your browser (you don't need to run a webserver). Run the tests with `npm test`. From f73ff23d5aa3247e0fc46d3ffde73cf9c900f48d Mon Sep 17 00:00:00 2001 From: Marijn Haverbeke Date: Fri, 21 Oct 2016 08:13:42 +0200 Subject: [PATCH 0507/1790] Fix release script to bump version in correct file --- bin/release | 2 +- src/edit/main.js | 2 +- 2 files changed, 2 insertions(+), 2 deletions(-) diff --git a/bin/release b/bin/release index df1fb269..b31d5118 100755 --- a/bin/release +++ b/bin/release @@ -16,7 +16,7 @@ function rewrite(file, f) { fs.writeFileSync(file, f(fs.readFileSync(file, "utf8")), "utf8"); } -rewrite("lib/codemirror.js", function(lib) { +rewrite("src/edit/main.js", function(lib) { return lib.replace(/CodeMirror\.version = "\d+\.\d+\.\d+"/, "CodeMirror.version = \"" + number + "\""); }); diff --git a/src/edit/main.js b/src/edit/main.js index 2831d2a0..16dc0321 100644 --- a/src/edit/main.js +++ b/src/edit/main.js @@ -66,4 +66,4 @@ import { addLegacyProps } from "./legacy" addLegacyProps(CodeMirror) -CodeMirror.version = "5.19.1" +CodeMirror.version = "5.20.1" From d221bf5d15680e06528e1558c014d178c4bc740d Mon Sep 17 00:00:00 2001 From: Marijn Haverbeke Date: Fri, 21 Oct 2016 08:17:46 +0200 Subject: [PATCH 0508/1790] Mark release 5.20.2 --- CHANGELOG.md | 6 ++++++ doc/manual.html | 2 +- index.html | 2 +- package.json | 2 +- src/edit/main.js | 2 +- 5 files changed, 10 insertions(+), 4 deletions(-) diff --git a/CHANGELOG.md b/CHANGELOG.md index b2435465..a7e995ab 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -1,3 +1,9 @@ +## 5.20.2 (2016-10-21) + +### Bug fixes + +Fix `CodeMirror.version` returning the wrong version number. + ## 5.20.0 (2016-10-20) ### Bug fixes diff --git a/doc/manual.html b/doc/manual.html index 0cff1be8..2944f0d5 100644 --- a/doc/manual.html +++ b/doc/manual.html @@ -69,7 +69,7 @@

      User manual and reference guide - version 5.20.1 + version 5.20.2

      CodeMirror is a code-editor component that can be embedded in diff --git a/index.html b/index.html index f4e56a70..1d1bb3c8 100644 --- a/index.html +++ b/index.html @@ -96,7 +96,7 @@

      This is CodeMirror

      - Get the current version: 5.20.0.
      + Get the current version: 5.20.2.
      You can see the code,
      read the release notes,
      or study the user manual. diff --git a/package.json b/package.json index 7dbf4751..8a58fbde 100644 --- a/package.json +++ b/package.json @@ -1,6 +1,6 @@ { "name": "codemirror", - "version": "5.20.1", + "version": "5.20.2", "main": "lib/codemirror.js", "description": "Full-featured in-browser code editor", "license": "MIT", diff --git a/src/edit/main.js b/src/edit/main.js index 16dc0321..43359656 100644 --- a/src/edit/main.js +++ b/src/edit/main.js @@ -66,4 +66,4 @@ import { addLegacyProps } from "./legacy" addLegacyProps(CodeMirror) -CodeMirror.version = "5.20.1" +CodeMirror.version = "5.20.2" From 06431f4d7eb24b0945bc7bb0b775a4f644766c64 Mon Sep 17 00:00:00 2001 From: Marijn Haverbeke Date: Fri, 21 Oct 2016 08:19:57 +0200 Subject: [PATCH 0509/1790] Bump version number post-5.20.2 --- doc/manual.html | 2 +- package.json | 2 +- src/edit/main.js | 2 +- 3 files changed, 3 insertions(+), 3 deletions(-) diff --git a/doc/manual.html b/doc/manual.html index 2944f0d5..6ef27689 100644 --- a/doc/manual.html +++ b/doc/manual.html @@ -69,7 +69,7 @@

      User manual and reference guide - version 5.20.2 + version 5.20.3

      CodeMirror is a code-editor component that can be embedded in diff --git a/package.json b/package.json index 8a58fbde..1d07d681 100644 --- a/package.json +++ b/package.json @@ -1,6 +1,6 @@ { "name": "codemirror", - "version": "5.20.2", + "version": "5.20.3", "main": "lib/codemirror.js", "description": "Full-featured in-browser code editor", "license": "MIT", diff --git a/src/edit/main.js b/src/edit/main.js index 43359656..57fcffa0 100644 --- a/src/edit/main.js +++ b/src/edit/main.js @@ -66,4 +66,4 @@ import { addLegacyProps } from "./legacy" addLegacyProps(CodeMirror) -CodeMirror.version = "5.20.2" +CodeMirror.version = "5.20.3" From 63bf6594d80b09359025ebc9282e49ed37574cd2 Mon Sep 17 00:00:00 2001 From: Todd Berman Date: Thu, 20 Oct 2016 20:37:42 -0700 Subject: [PATCH 0510/1790] Add classes to each pane for isolated style changes, handle document swapping --- addon/merge/merge.js | 12 +++++++++--- 1 file changed, 9 insertions(+), 3 deletions(-) diff --git a/addon/merge/merge.js b/addon/merge/merge.js index 2f53406e..b0f78c87 100644 --- a/addon/merge/merge.js +++ b/addon/merge/merge.js @@ -116,8 +116,14 @@ // Update faster when a line was added/removed setDealign(change.text.length - 1 != change.to.line - change.from.line); } + function swapDoc() { + dv.diffOutOfDate = true; + update("full"); + } dv.edit.on("change", change); dv.orig.on("change", change); + dv.edit.on("swapDoc", swapDoc); + dv.orig.on("swapDoc", swapDoc); dv.edit.on("markerAdded", setDealign); dv.edit.on("markerCleared", setDealign); dv.orig.on("markerAdded", setDealign); @@ -464,18 +470,18 @@ if (hasLeft) { left = this.left = new DiffView(this, "left"); - var leftPane = elt("div", null, "CodeMirror-merge-pane"); + var leftPane = elt("div", null, "CodeMirror-merge-pane CodeMirror-merge-left"); wrap.push(leftPane); wrap.push(buildGap(left)); } - var editPane = elt("div", null, "CodeMirror-merge-pane"); + var editPane = elt("div", null, "CodeMirror-merge-pane CodeMirror-merge-editor"); wrap.push(editPane); if (hasRight) { right = this.right = new DiffView(this, "right"); wrap.push(buildGap(right)); - var rightPane = elt("div", null, "CodeMirror-merge-pane"); + var rightPane = elt("div", null, "CodeMirror-merge-pane CodeMirror-merge-right"); wrap.push(rightPane); } From e83ee37e5c54d0b088087bfdb897c7e3d5aacbad Mon Sep 17 00:00:00 2001 From: Marijn Haverbeke Date: Sat, 22 Oct 2016 09:03:43 +0200 Subject: [PATCH 0511/1790] [css mode] Drop marker-offset property Closes #4340 --- 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 e56e3dd8..b7573203 100644 --- a/mode/css/css.js +++ b/mode/css/css.js @@ -494,7 +494,7 @@ CodeMirror.defineMode("css", function(config, parserConfig) { "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", + "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", "object-fit", "object-position", From db12d64243ee9d2994e12ffb2935ebac0cbf3c1c Mon Sep 17 00:00:00 2001 From: Todd Berman Date: Sat, 22 Oct 2016 17:59:55 -0700 Subject: [PATCH 0512/1790] [merge addon] Remove the end class when removing chunk styling --- 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 b0f78c87..e9700821 100644 --- a/addon/merge/merge.js +++ b/addon/merge/merge.js @@ -205,7 +205,7 @@ for (var i = 0; i < locs.length; i++) { editor.removeLineClass(line, locs[i], classes.chunk); editor.removeLineClass(line, locs[i], classes.start); - editor.removeLineClass(line, locs[i], classes.chunk); + editor.removeLineClass(line, locs[i], classes.end); } } From 0164f02bf1ce823e300520ef198d620f30d47220 Mon Sep 17 00:00:00 2001 From: BigBlueHat Date: Wed, 26 Oct 2016 17:01:08 -0400 Subject: [PATCH 0513/1790] [yaml mode] Add text/yaml MIME type --- mode/meta.js | 2 +- mode/yaml/yaml.js | 1 + 2 files changed, 2 insertions(+), 1 deletion(-) diff --git a/mode/meta.js b/mode/meta.js index 47e9a31c..cb0684b9 100644 --- a/mode/meta.js +++ b/mode/meta.js @@ -157,7 +157,7 @@ {name: "XML", mimes: ["application/xml", "text/xml"], mode: "xml", ext: ["xml", "xsl", "xsd"], alias: ["rss", "wsdl", "xsd"]}, {name: "XQuery", mime: "application/xquery", mode: "xquery", ext: ["xy", "xquery"]}, {name: "Yacas", mime: "text/x-yacas", mode: "yacas", ext: ["ys"]}, - {name: "YAML", mime: "text/x-yaml", mode: "yaml", ext: ["yaml", "yml"], alias: ["yml"]}, + {name: "YAML", mimes: ["text/x-yaml", "text/yaml"], mode: "yaml", ext: ["yaml", "yml"], alias: ["yml"]}, {name: "Z80", mime: "text/x-z80", mode: "z80", ext: ["z80"]}, {name: "mscgen", mime: "text/x-mscgen", mode: "mscgen", ext: ["mscgen", "mscin", "msc"]}, {name: "xu", mime: "text/x-xu", mode: "mscgen", ext: ["xu"]}, diff --git a/mode/yaml/yaml.js b/mode/yaml/yaml.js index b7015e59..59c0ecdb 100644 --- a/mode/yaml/yaml.js +++ b/mode/yaml/yaml.js @@ -113,5 +113,6 @@ CodeMirror.defineMode("yaml", function() { }); CodeMirror.defineMIME("text/x-yaml", "yaml"); +CodeMirror.defineMIME("text/yaml", "yaml"); }); From 2eb94e54b6e9e1df5004fe8089311be8e3b73146 Mon Sep 17 00:00:00 2001 From: BigBlueHat Date: Thu, 27 Oct 2016 10:41:00 -0400 Subject: [PATCH 0514/1790] [vue mode] Fix media type Demo had the correct `text/x-vue` defineMIME was using `script/x-vue` There is, however, no `script/*` top-level media type: http://www.iana.org/assignments/media-types/media-types.xhtml Left, old `script/x-vue` for backwards compatibility. --- mode/meta.js | 1 + mode/vue/vue.js | 1 + 2 files changed, 2 insertions(+) diff --git a/mode/meta.js b/mode/meta.js index cb0684b9..8faf7677 100644 --- a/mode/meta.js +++ b/mode/meta.js @@ -154,6 +154,7 @@ {name: "Velocity", mime: "text/velocity", mode: "velocity", ext: ["vtl"]}, {name: "Verilog", mime: "text/x-verilog", mode: "verilog", ext: ["v"]}, {name: "VHDL", mime: "text/x-vhdl", mode: "vhdl", ext: ["vhd", "vhdl"]}, + {name: "Vue.js Component", mimes: ["script/x-vue", "text/x-vue"], mode: "vue", ext: ["vue"]}, {name: "XML", mimes: ["application/xml", "text/xml"], mode: "xml", ext: ["xml", "xsl", "xsd"], alias: ["rss", "wsdl", "xsd"]}, {name: "XQuery", mime: "application/xquery", mode: "xquery", ext: ["xy", "xquery"]}, {name: "Yacas", mime: "text/x-yacas", mode: "yacas", ext: ["ys"]}, diff --git a/mode/vue/vue.js b/mode/vue/vue.js index f8089af5..c0eab6b8 100644 --- a/mode/vue/vue.js +++ b/mode/vue/vue.js @@ -66,4 +66,5 @@ }, "htmlmixed", "xml", "javascript", "coffeescript", "css", "sass", "stylus", "pug", "handlebars"); CodeMirror.defineMIME("script/x-vue", "vue"); + CodeMirror.defineMIME("text/x-vue", "vue"); }); From e2c146e6463f31839b49bfef303e962116970a88 Mon Sep 17 00:00:00 2001 From: Adrien Bertrand Date: Sat, 29 Oct 2016 19:14:54 +0200 Subject: [PATCH 0515/1790] Fix constructor typo for MergeView. --- 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 e9700821..72526d01 100644 --- a/addon/merge/merge.js +++ b/addon/merge/merge.js @@ -545,7 +545,7 @@ } MergeView.prototype = { - constuctor: MergeView, + constructor: MergeView, editor: function() { return this.edit; }, rightOriginal: function() { return this.right && this.right.orig; }, leftOriginal: function() { return this.left && this.left.orig; }, From 7b00c30cdb959cf1980ca5f5cbac3cf331b83b08 Mon Sep 17 00:00:00 2001 From: Marijn Haverbeke Date: Mon, 31 Oct 2016 09:47:35 +0100 Subject: [PATCH 0516/1790] [ruby mode] Make else and elsif electric Closes #4345 --- 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 10cad8d9..085f909f 100644 --- a/mode/ruby/ruby.js +++ b/mode/ruby/ruby.js @@ -275,7 +275,7 @@ CodeMirror.defineMode("ruby", function(config) { (state.continuedLine ? config.indentUnit : 0); }, - electricInput: /^\s*(?:end|rescue|\})$/, + electricInput: /^\s*(?:end|rescue|elsif|else|\})$/, lineComment: "#" }; }); From c4d9363e18925842b7741e11611d870baf4bb14e Mon Sep 17 00:00:00 2001 From: sverweij Date: Fri, 28 Oct 2016 23:03:34 +0200 Subject: [PATCH 0517/1790] [mscgen mode] adds support for language constants MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit and adds the xù specific keyword 'xu' --- mode/mscgen/index.html | 2 +- mode/mscgen/mscgen.js | 8 +++++++- mode/mscgen/mscgen_test.js | 10 +++++++++- mode/mscgen/msgenny_test.js | 7 ++++++- mode/mscgen/xu_test.js | 19 +++++++++++++++---- 5 files changed, 38 insertions(+), 8 deletions(-) diff --git a/mode/mscgen/index.html b/mode/mscgen/index.html index 8c28ee62..b1d7e7c2 100644 --- a/mode/mscgen/index.html +++ b/mode/mscgen/index.html @@ -59,7 +59,7 @@

      Xù mode

      # Xù - expansions to MscGen to support inline expressions # https://github.com/sverweij/mscgen_js/blob/master/wikum/xu.md # More samples: https://sverweij.github.io/mscgen_js -msc { +xu { hscale="0.8", width="700"; diff --git a/mode/mscgen/mscgen.js b/mode/mscgen/mscgen.js index d61b4706..2cd6f427 100644 --- a/mode/mscgen/mscgen.js +++ b/mode/mscgen/mscgen.js @@ -23,6 +23,7 @@ mscgen: { "keywords" : ["msc"], "options" : ["hscale", "width", "arcgradient", "wordwraparcs"], + "constants" : ["true", "false", "on", "off"], "attributes" : ["label", "idurl", "id", "url", "linecolor", "linecolour", "textcolor", "textcolour", "textbgcolor", "textbgcolour", "arclinecolor", "arclinecolour", "arctextcolor", "arctextcolour", "arctextbgcolor", "arctextbgcolour", "arcskip"], "brackets" : ["\\{", "\\}"], // [ and ] are brackets too, but these get handled in with lists "arcsWords" : ["note", "abox", "rbox", "box"], @@ -31,8 +32,9 @@ "operators" : ["="] }, xu: { - "keywords" : ["msc"], + "keywords" : ["msc", "xu"], "options" : ["hscale", "width", "arcgradient", "wordwraparcs", "watermark"], + "constants" : ["true", "false", "on", "off", "auto"], "attributes" : ["label", "idurl", "id", "url", "linecolor", "linecolour", "textcolor", "textcolour", "textbgcolor", "textbgcolour", "arclinecolor", "arclinecolour", "arctextcolor", "arctextcolour", "arctextbgcolor", "arctextbgcolour", "arcskip"], "brackets" : ["\\{", "\\}"], // [ and ] are brackets too, but these get handled in with lists "arcsWords" : ["note", "abox", "rbox", "box", "alt", "else", "opt", "break", "par", "seq", "strict", "neg", "critical", "ignore", "consider", "assert", "loop", "ref", "exc"], @@ -43,6 +45,7 @@ msgenny: { "keywords" : null, "options" : ["hscale", "width", "arcgradient", "wordwraparcs", "watermark"], + "constants" : ["true", "false", "on", "off", "auto"], "attributes" : null, "brackets" : ["\\{", "\\}"], "arcsWords" : ["note", "abox", "rbox", "box", "alt", "else", "opt", "break", "par", "seq", "strict", "neg", "critical", "ignore", "consider", "assert", "loop", "ref", "exc"], @@ -146,6 +149,9 @@ if (!!pConfig.operators && pStream.match(wordRegexp(pConfig.operators), true, true)) return "operator"; + if (!!pConfig.constants && pStream.match(wordRegexp(pConfig.constants), true, true)) + return "variable"; + /* attribute lists */ if (!pConfig.inAttributeList && !!pConfig.attributes && pStream.match(/\[/, true, true)) { pConfig.inAttributeList = true; diff --git a/mode/mscgen/mscgen_test.js b/mode/mscgen/mscgen_test.js index e319a399..956c5758 100644 --- a/mode/mscgen/mscgen_test.js +++ b/mode/mscgen/mscgen_test.js @@ -29,6 +29,14 @@ "[base alt loop opt ref else break par seq assert]" ); + MT("xù/ msgenny constants classify as 'base'", + "[base auto]" + ); + + MT("mscgen constants classify as 'variable'", + "[variable true]", "[variable false]", "[variable on]", "[variable off]" + ); + MT("mscgen options classify as keyword", "[keyword hscale]", "[keyword width]", "[keyword arcgradient]", "[keyword wordwraparcs]" ); @@ -63,7 +71,7 @@ MT("a typical program", "[comment # typical mscgen program]", "[keyword msc][base ][bracket {]", - "[keyword wordwraparcs][operator =][string \"true\"][base , ][keyword hscale][operator =][string \"0.8\"][keyword arcgradient][operator =][base 30;]", + "[keyword wordwraparcs][operator =][variable true][base , ][keyword hscale][operator =][string \"0.8\"][base , ][keyword arcgradient][operator =][base 30;]", "[base a][bracket [[][attribute label][operator =][string \"Entity A\"][bracket ]]][base ,]", "[base b][bracket [[][attribute label][operator =][string \"Entity B\"][bracket ]]][base ,]", "[base c][bracket [[][attribute label][operator =][string \"Entity C\"][bracket ]]][base ;]", diff --git a/mode/mscgen/msgenny_test.js b/mode/mscgen/msgenny_test.js index 80173de0..edf9da09 100644 --- a/mode/mscgen/msgenny_test.js +++ b/mode/mscgen/msgenny_test.js @@ -23,6 +23,11 @@ "[keyword alt]","[keyword loop]","[keyword opt]","[keyword ref]","[keyword else]","[keyword break]","[keyword par]","[keyword seq]","[keyword assert]" ); + MT("xù/ msgenny constants classify as 'variable'", + "[variable auto]", + "[variable true]", "[variable false]", "[variable on]", "[variable off]" + ); + MT("mscgen options classify as keyword", "[keyword hscale]", "[keyword width]", "[keyword arcgradient]", "[keyword wordwraparcs]" ); @@ -56,7 +61,7 @@ MT("a typical program", "[comment # typical msgenny program]", - "[keyword wordwraparcs][operator =][string \"true\"][base , ][keyword hscale][operator =][string \"0.8\"][base , ][keyword arcgradient][operator =][base 30;]", + "[keyword wordwraparcs][operator =][variable true][base , ][keyword hscale][operator =][string \"0.8\"][base , ][keyword arcgradient][operator =][base 30;]", "[base a : ][string \"Entity A\"][base ,]", "[base b : Entity B,]", "[base c : Entity C;]", diff --git a/mode/mscgen/xu_test.js b/mode/mscgen/xu_test.js index f9a50f0a..950aeca1 100644 --- a/mode/mscgen/xu_test.js +++ b/mode/mscgen/xu_test.js @@ -9,7 +9,13 @@ "[keyword msc][bracket {]", "[base ]", "[bracket }]" - ); + ); + + MT("empty chart", + "[keyword xu][bracket {]", + "[base ]", + "[bracket }]" + ); MT("comments", "[comment // a single line comment]", @@ -29,6 +35,11 @@ "[keyword alt]","[keyword loop]","[keyword opt]","[keyword ref]","[keyword else]","[keyword break]","[keyword par]","[keyword seq]","[keyword assert]" ); + MT("xù/ msgenny constants classify as 'variable'", + "[variable auto]", + "[variable true]", "[variable false]", "[variable on]", "[variable off]" + ); + MT("mscgen options classify as keyword", "[keyword hscale]", "[keyword width]", "[keyword arcgradient]", "[keyword wordwraparcs]" ); @@ -61,9 +72,9 @@ ); MT("a typical program", - "[comment # typical mscgen program]", - "[keyword msc][base ][bracket {]", - "[keyword wordwraparcs][operator =][string \"true\"][keyword hscale][operator =][string \"0.8\"][keyword arcgradient][operator =][base 30;]", + "[comment # typical xu program]", + "[keyword xu][base ][bracket {]", + "[keyword wordwraparcs][operator =][string \"true\"][base , ][keyword hscale][operator =][string \"0.8\"][base , ][keyword arcgradient][operator =][base 30, ][keyword width][operator =][variable auto][base ;]", "[base a][bracket [[][attribute label][operator =][string \"Entity A\"][bracket ]]][base ,]", "[base b][bracket [[][attribute label][operator =][string \"Entity B\"][bracket ]]][base ,]", "[base c][bracket [[][attribute label][operator =][string \"Entity C\"][bracket ]]][base ;]", From d34e94781fac24518a31a33b8d0980639ad8547e Mon Sep 17 00:00:00 2001 From: Steve Hoover Date: Mon, 24 Oct 2016 14:57:06 -0400 Subject: [PATCH 0518/1790] [verilog mode] Cleanup/rewrite. --- mode/verilog/verilog.js | 400 ++++++++++++++++++++++++++-------------- 1 file changed, 264 insertions(+), 136 deletions(-) diff --git a/mode/verilog/verilog.js b/mode/verilog/verilog.js index 7513dced..1f6ecb9f 100644 --- a/mode/verilog/verilog.js +++ b/mode/verilog/verilog.js @@ -302,7 +302,13 @@ CodeMirror.defineMode("verilog", function(config, parserConfig) { state.indented = stream.indentation(); state.startOfLine = true; } - if (hooks.token) hooks.token(stream, state); + if (hooks.token) { + // Call hook, with an optional return value of a style to override verilog styling. + var style = hooks.token(stream, state); + if (style !== undefined) { + return style; + } + } if (stream.eatSpace()) return null; curPunc = null; curKeyword = null; @@ -375,163 +381,285 @@ CodeMirror.defineMode("verilog", function(config, parserConfig) { name: "verilog" }); - // TLVVerilog mode - var tlvchScopePrefixes = { - ">": "property", "->": "property", "-": "hr", "|": "link", "?$": "qualifier", "?*": "qualifier", - "@-": "variable-3", "@": "variable-3", "?": "qualifier" + + // TL-Verilog mode. + // See tl-x.org for language spec. + // See the mode in action at makerchip.com. + // Contact: steve.hoover@redwoodeda.com + + // TLV Identifier prefixes. + // Note that sign is not treated separately, so "+/-" versions of numeric identifiers + // are included. + var tlvIdentifierStyle = { + "|": "link", + ">": "property", // Should condition this off for > TLV 1c. + "$": "variable", + "$$": "variable", + "?$": "qualifier", + "?*": "qualifier", + "-": "hr", + "/": "property", + "/-": "property", + "@": "variable-3", + "@-": "variable-3", + "@++": "variable-3", + "@+=": "variable-3", + "@+=-": "variable-3", + "@--": "variable-3", + "@-=": "variable-3", + "%+": "tag", + "%-": "tag", + "%": "tag", + ">>": "tag", + "<<": "tag", + "<>": "tag", + "#": "tag", // Need to choose a style for this. + "^": "attribute", + "^^": "attribute", + "^!": "attribute", + "*": "variable-2", + "**": "variable-2", + "\\": "keyword", + "\"": "comment" }; - function tlvGenIndent(stream, state) { - var tlvindentUnit = 2; - var rtnIndent = -1, indentUnitRq = 0, curIndent = stream.indentation(); - switch (state.tlvCurCtlFlowChar) { - case "\\": - curIndent = 0; - break; - case "|": - if (state.tlvPrevPrevCtlFlowChar == "@") { - indentUnitRq = -2; //-2 new pipe rq after cur pipe - break; - } - if (tlvchScopePrefixes[state.tlvPrevCtlFlowChar]) - indentUnitRq = 1; // +1 new scope - break; - case "M": // m4 - if (state.tlvPrevPrevCtlFlowChar == "@") { - indentUnitRq = -2; //-2 new inst rq after pipe - break; - } - if (tlvchScopePrefixes[state.tlvPrevCtlFlowChar]) - indentUnitRq = 1; // +1 new scope - break; - case "@": - if (state.tlvPrevCtlFlowChar == "S") - indentUnitRq = -1; // new pipe stage after stmts - if (state.tlvPrevCtlFlowChar == "|") - indentUnitRq = 1; // 1st pipe stage - break; - case "S": - if (state.tlvPrevCtlFlowChar == "@") - indentUnitRq = 1; // flow in pipe stage - if (tlvchScopePrefixes[state.tlvPrevCtlFlowChar]) - indentUnitRq = 1; // +1 new scope - break; - } - var statementIndentUnit = tlvindentUnit; - rtnIndent = curIndent + (indentUnitRq*statementIndentUnit); - return rtnIndent >= 0 ? rtnIndent : curIndent; + // Lines starting with these characters define scope (result in indentation). + var tlvScopePrefixChars = { + "/": "beh-hier", + ">": "beh-hier", + "-": "phys-hier", + "|": "pipe", + "?": "when", + "@": "stage", + "\\": "keyword" + }; + var tlvIndentUnit = 3; + var tlvTrackStatements = false; + var tlvIdentMatch = /^([~!@#\$%\^&\*-\+=\?\/\\\|'"<>]+)([\d\w_]*)/; // Matches an identifiere. + // Note that ':' is excluded, because of it's use in [:]. + var tlvFirstLevelIndentMatch = /^[! ] /; + var tlvLineIndentationMatch = /^[! ] */; + var tlvCommentMatch = /^\/[\/\*]/; + + + // Returns a style specific to the scope at the given indentation column. + // Type is one of: "indent", "scope-ident", "before-scope-ident". + function tlvScopeStyle(state, indentation, type) { + // Begin scope. + var depth = indentation / tlvIndentUnit; // TODO: Pass this in instead. + return "tlv-" + state.tlvIndentationStyle[depth] + "-" + type; + } + + // Return true if the next thing in the stream is an identifier with a mnemonic. + function tlvIdentNext(stream) { + var match; + return (match = stream.match(tlvIdentMatch, false)) && match[2].length > 0; } CodeMirror.defineMIME("text/x-tlv", { name: "verilog", + hooks: { - "\\": function(stream, state) { - var vxIndent = 0, style = false; - var curPunc = stream.string; - if ((stream.sol()) && ((/\\SV/.test(stream.string)) || (/\\TLV/.test(stream.string)))) { - curPunc = (/\\TLV_version/.test(stream.string)) - ? "\\TLV_version" : stream.string; - stream.skipToEnd(); - if (curPunc == "\\SV" && state.vxCodeActive) {state.vxCodeActive = false;}; - if ((/\\TLV/.test(curPunc) && !state.vxCodeActive) - || (curPunc=="\\TLV_version" && state.vxCodeActive)) {state.vxCodeActive = true;}; - style = "keyword"; - state.tlvCurCtlFlowChar = state.tlvPrevPrevCtlFlowChar - = state.tlvPrevCtlFlowChar = ""; - if (state.vxCodeActive == true) { - state.tlvCurCtlFlowChar = "\\"; - vxIndent = tlvGenIndent(stream, state); + + electricInput: false, + + + // Return undefined for verilog tokenizing, or style for TLV token (null not used). + // Standard CM styles are used for most formatting, but some TL-Verilog-specific highlighting + // can be enabled with the definition of cm-tlv-* styles, including highlighting for: + // - M4 tokens + // - TLV scope indentation + // - Statement delimitation (enabled by tlvTrackStatements) + token: function(stream, state) { + var style = undefined; + var match; // Return value of pattern matches. + + // Set highlighting mode based on code region (TLV or SV). + if (stream.sol() && ! state.tlvInBlockComment) { + // Process region. + if (stream.peek() == '\\') { + style = "def"; + stream.skipToEnd(); + if (stream.string.match(/\\SV/)) { + state.tlvCodeActive = false; + } else if (stream.string.match(/\\TLV/)){ + state.tlvCodeActive = true; + } + } + // Correct indentation in the face of a line prefix char. + if (state.tlvCodeActive && stream.pos == 0 && + (state.indented == 0) && (match = stream.match(tlvLineIndentationMatch, false))) { + state.indented = match[0].length; + } + + // Compute indentation state: + // o Required indentation on next line + // o Indentation scope styles + var indented = state.indented; + var depth = indented / tlvIndentUnit; + if (depth <= state.tlvIndentationStyle.length) { + // not deeper than current scope + + var blankline = stream.string.length == indented; + var chPos = depth * tlvIndentUnit; + if (chPos < stream.string.length) { + var bodyString = stream.string.slice(chPos); + var ch = bodyString[0]; + if (tlvScopePrefixChars[ch] && ((match = bodyString.match(tlvIdentMatch)) && + tlvIdentifierStyle[match[1]])) { + // this line begins scope (except non-region keyword identifiers, which are statements themselves) + if (!(ch == "\\" && chPos > 0)) { + indented += tlvIndentUnit; + state.tlvIndentationStyle[depth] = tlvScopePrefixChars[ch]; + if (tlvTrackStatements) {state.statementComment = false;} + depth++; + } + } + } + // Clear out deeper indentation levels unless line is blank. + if (!blankline) { + while (state.tlvIndentationStyle.length > depth) { + state.tlvIndentationStyle.pop(); + } + } } - state.vxIndentRq = vxIndent; } - return style; - }, - tokenBase: function(stream, state) { - var vxIndent = 0, style = false; - var tlvisOperatorChar = /[\[\]=:]/; - var tlvkpScopePrefixs = { - "**":"variable-2", "*":"variable-2", "$$":"variable", "$":"variable", - "^^":"attribute", "^":"attribute"}; - var ch = stream.peek(); - var vxCurCtlFlowCharValueAtStart = state.tlvCurCtlFlowChar; - if (state.vxCodeActive == true) { - if (/[\[\]{}\(\);\:]/.test(ch)) { - // bypass nesting and 1 char punc - style = "meta"; - stream.next(); - } else if (ch == "/") { - stream.next(); - if (stream.eat("/")) { + + if (state.tlvCodeActive) { + // Highlight as TLV. + + var beginStatement = false; + if (tlvTrackStatements) { + // This starts a statement if the position is at the scope level + // and we're not within a statement leading comment. + beginStatement = + (stream.peek() != " ") && // not a space + (style === undefined) && // not a region identifier + !state.tlvInBlockComment && // not in block comment + //!stream.match(tlvCommentMatch, false) && // not comment start + (stream.column() == state.tlvIndentationStyle.length * tlvIndentUnit); // at scope level + if (beginStatement) { + if (state.statementComment) { + // statement already started by comment + beginStatement = false; + } + state.statementComment = + stream.match(tlvCommentMatch, false); // comment start + } + } + + var match; + if (style !== undefined) { + // Region line. + style += " " + tlvScopeStyle(state, 0, "scope-ident") + } else if (((stream.pos / tlvIndentUnit) < state.tlvIndentationStyle.length) && + (match = stream.match(stream.sol() ? tlvFirstLevelIndentMatch : /^ /))) { + // Indentation + style = // make this style distinct from the previous one to prevent + // codemirror from combining spans + "tlv-indent-" + (((stream.pos % 2) == 0) ? "even" : "odd") + + // and style it + " " + tlvScopeStyle(state, stream.pos - tlvIndentUnit, "indent"); + // Style the line prefix character. + if (match[0].charAt(0) == "!") { + style += " tlv-alert-line-prefix"; + } + // Place a class before a scope identifier. + if (tlvIdentNext(stream)) { + style += " " + tlvScopeStyle(state, stream.pos, "before-scope-ident"); + } + } else if (state.tlvInBlockComment) { + // In a block comment. + if (stream.match(/^.*?\*\//)) { + // Exit block comment. + state.tlvInBlockComment = false; + if (tlvTrackStatements && !stream.eol()) { + // Anything after comment is assumed to be real statement content. + state.statementComment = false; + } + } else { + stream.skipToEnd(); + } + style = "comment"; + } else if ((match = stream.match(tlvCommentMatch)) && !state.tlvInBlockComment) { + // Start comment. + if (match[0] == "//") { + // Line comment. stream.skipToEnd(); - style = "comment"; - state.tlvCurCtlFlowChar = "S"; } else { - stream.backUp(1); + // Block comment. + state.tlvInBlockComment = true; } - } else if (ch == "@") { - // pipeline stage - style = tlvchScopePrefixes[ch]; - state.tlvCurCtlFlowChar = "@"; - stream.next(); - stream.eatWhile(/[\w\$_]/); - } else if (stream.match(/\b[mM]4+/, true)) { // match: function(pattern, consume, caseInsensitive) - // m4 pre proc - stream.skipTo("("); - style = "def"; - state.tlvCurCtlFlowChar = "M"; - } else if (ch == "!" && stream.sol()) { - // v stmt in tlv region - // state.tlvCurCtlFlowChar = "S"; style = "comment"; + } else if (match = stream.match(tlvIdentMatch)) { + // looks like an identifier (or identifier prefix) + var prefix = match[1]; + var mnemonic = match[2]; + if (// is identifier prefix + (prefix in tlvIdentifierStyle) && + // has mnemonic or we're at the end of the line (maybe it hasn't been typed yet) + (mnemonic.length > 0 || stream.eol())) { + style = tlvIdentifierStyle[prefix]; + if (stream.column() == state.indented) { + // Begin scope. + style += " " + tlvScopeStyle(state, stream.column(), "scope-ident") + } + } else { + // Just swallow one character and try again. + // This enables subsequent identifier match with preceding symbol character, which + // is legal within a statement. (Eg, !$reset). It also enables detection of + // comment start with preceding symbols. + stream.backUp(stream.current().length - 1); + style = "tlv-default"; + } + } else if (stream.match(/^\t+/)) { + // Highlight tabs, which are illegal. + style = "tlv-tab"; + } else if (stream.match(/^[\[\]{}\(\);\:]+/)) { + // [:], (), {}, ;. + style = "meta"; + } else if (match = stream.match(/^[mM]4([\+_])?[\w\d_]*/)) { + // m4 pre proc + style = (match[1] == "+") ? "tlv-m4-plus" : "tlv-m4"; + } else if (stream.match(/^ +/)){ + // Skip over spaces. + if (stream.eol()) { + // Trailing spaces. + style = "error"; + } else { + // Non-trailing spaces. + style = "tlv-default"; + } + } else if (stream.match(/^[\w\d_]+/)) { + // alpha-numeric token. + style = "number"; + } else { + // Eat the next char w/ no formatting. stream.next(); - } else if (tlvisOperatorChar.test(ch)) { - // operators - stream.eatWhile(tlvisOperatorChar); - style = "operator"; - } else if (ch == "#") { - // phy hier - state.tlvCurCtlFlowChar = (state.tlvCurCtlFlowChar == "") - ? ch : state.tlvCurCtlFlowChar; - stream.next(); - stream.eatWhile(/[+-]\d/); - style = "tag"; - } else if (tlvkpScopePrefixs.propertyIsEnumerable(ch)) { - // special TLV operators - style = tlvkpScopePrefixs[ch]; - state.tlvCurCtlFlowChar = state.tlvCurCtlFlowChar == "" ? "S" : state.tlvCurCtlFlowChar; // stmt - stream.next(); - stream.match(/[a-zA-Z_0-9]+/); - } else if (style = tlvchScopePrefixes[ch] || false) { - // special TLV operators - state.tlvCurCtlFlowChar = state.tlvCurCtlFlowChar == "" ? ch : state.tlvCurCtlFlowChar; - stream.next(); - stream.match(/[a-zA-Z_0-9]+/); + style = "tlv-default"; + } + if (beginStatement) { + style += " tlv-statement"; } - if (state.tlvCurCtlFlowChar != vxCurCtlFlowCharValueAtStart) { // flow change - vxIndent = tlvGenIndent(stream, state); - state.vxIndentRq = vxIndent; + } else { + if (stream.match(/^[mM]4([\w\d_]*)/)) { + // m4 pre proc + style = "tlv-m4"; } } return style; }, - token: function(stream, state) { - if (state.vxCodeActive == true && stream.sol() && state.tlvCurCtlFlowChar != "") { - state.tlvPrevPrevCtlFlowChar = state.tlvPrevCtlFlowChar; - state.tlvPrevCtlFlowChar = state.tlvCurCtlFlowChar; - state.tlvCurCtlFlowChar = ""; - } - }, - indent: function(state) { - return (state.vxCodeActive == true) ? state.vxIndentRq : -1; - }, + startState: function(state) { - state.tlvCurCtlFlowChar = ""; - state.tlvPrevCtlFlowChar = ""; - state.tlvPrevPrevCtlFlowChar = ""; - state.vxCodeActive = true; - state.vxIndentRq = 0; + state.tlvIndentationStyle = []; // Styles to use for each level of indentation. + state.tlvCodeActive = true; // True when we're in a TLV region (and at beginning of file). + state.tlvInBlockComment = false; // True inside /**/ comment. + if (tlvTrackStatements) { + state.statementComment = false; // True inside a statement's header comment. + } } + } }); }); From 5105da7fcd5a98dbaf276b769ce8581daac84121 Mon Sep 17 00:00:00 2001 From: Marijn Haverbeke Date: Tue, 1 Nov 2016 11:04:58 +0100 Subject: [PATCH 0519/1790] [merge addon] Fix bug in chunk-aligning algorithm Closes #4353 --- addon/merge/merge.js | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-) diff --git a/addon/merge/merge.js b/addon/merge/merge.js index 72526d01..e7772ace 100644 --- a/addon/merge/merge.js +++ b/addon/merge/merge.js @@ -343,11 +343,12 @@ j = -1; break; } else if (align[1] > chunk.editTo) { + j-- break; } } if (j > -1) - linesToAlign.splice(j - 1, 0, [getMatchingOrigLine(chunk.editTo, dv.chunks), chunk.editTo, chunk.origTo]); + linesToAlign.splice(j, 0, [getMatchingOrigLine(chunk.editTo, dv.chunks), chunk.editTo, chunk.origTo]); } } return linesToAlign; From 6d3a7457d1d9b6c4165a5b066be6e1da99776e2b Mon Sep 17 00:00:00 2001 From: Marijn Haverbeke Date: Tue, 1 Nov 2016 11:07:10 +0100 Subject: [PATCH 0520/1790] [merge addon] Fix corner case in alignable-chunk sorting Issue #4353 --- addon/merge/merge.js | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/addon/merge/merge.js b/addon/merge/merge.js index e7772ace..1d7a5ccc 100644 --- a/addon/merge/merge.js +++ b/addon/merge/merge.js @@ -340,14 +340,14 @@ for (var j = 0; j < linesToAlign.length; j++) { var align = linesToAlign[j]; if (align[1] == chunk.editTo) { - j = -1; + j = -2; break; } else if (align[1] > chunk.editTo) { j-- break; } } - if (j > -1) + if (j > -2) linesToAlign.splice(j, 0, [getMatchingOrigLine(chunk.editTo, dv.chunks), chunk.editTo, chunk.origTo]); } } From ea796dad693cc1f7c7d7f4628a1ee4953ae6b3db Mon Sep 17 00:00:00 2001 From: Marijn Haverbeke Date: Wed, 2 Nov 2016 10:07:05 +0100 Subject: [PATCH 0521/1790] [merge addon] Fix sorted insertion in aligned line set (again) Issue #4353 --- addon/merge/merge.js | 16 +++++----------- 1 file changed, 5 insertions(+), 11 deletions(-) diff --git a/addon/merge/merge.js b/addon/merge/merge.js index 1d7a5ccc..9bd08658 100644 --- a/addon/merge/merge.js +++ b/addon/merge/merge.js @@ -335,20 +335,14 @@ linesToAlign.push([chunk.origTo, chunk.editTo, other ? getMatchingOrigLine(chunk.editTo, other.chunks) : null]); } if (other) { - for (var i = 0; i < other.chunks.length; i++) { + chunkLoop: for (var i = 0; i < other.chunks.length; i++) { var chunk = other.chunks[i]; for (var j = 0; j < linesToAlign.length; j++) { - var align = linesToAlign[j]; - if (align[1] == chunk.editTo) { - j = -2; - break; - } else if (align[1] > chunk.editTo) { - j-- - break; - } + var diff = linesToAlign[j][1] - chunk.editTo; + if (diff == 0) continue chunkLoop + if (diff > 0) break; } - if (j > -2) - linesToAlign.splice(j, 0, [getMatchingOrigLine(chunk.editTo, dv.chunks), chunk.editTo, chunk.origTo]); + linesToAlign.splice(j, 0, [getMatchingOrigLine(chunk.editTo, dv.chunks), chunk.editTo, chunk.origTo]); } } return linesToAlign; From 2466392a929761bd61e85cbea86cd533ac84eef4 Mon Sep 17 00:00:00 2001 From: Erik Welander Date: Tue, 1 Nov 2016 22:30:50 -0700 Subject: [PATCH 0522/1790] [rulers addon] Draw rulers all the way when scrollPastEnd is on. --- addon/display/rulers.js | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/addon/display/rulers.js b/addon/display/rulers.js index 73005447..151cc820 100644 --- a/addon/display/rulers.js +++ b/addon/display/rulers.js @@ -13,12 +13,12 @@ CodeMirror.defineOption("rulers", false, function(cm, val) { if (cm.state.rulerDiv) { - cm.display.lineSpace.removeChild(cm.state.rulerDiv) + cm.state.rulerDiv.parentElement.removeChild(cm.state.rulerDiv) cm.state.rulerDiv = null cm.off("refresh", drawRulers) } if (val && val.length) { - cm.state.rulerDiv = cm.display.lineSpace.insertBefore(document.createElement("div"), cm.display.cursorDiv) + cm.state.rulerDiv = cm.display.lineSpace.parentElement.insertBefore(document.createElement("div"), cm.display.lineSpace) cm.state.rulerDiv.className = "CodeMirror-rulers" drawRulers(cm) cm.on("refresh", drawRulers) From f5e211fa49315e0c0da212dffc275d769609d1ab Mon Sep 17 00:00:00 2001 From: Marijn Haverbeke Date: Wed, 2 Nov 2016 11:08:17 +0100 Subject: [PATCH 0523/1790] Add an includeWidgets argument to heightAtLine And use it in the merge addon to get the proper offsets for merge buttons Issue #4364 --- addon/merge/merge.js | 8 ++++---- doc/manual.html | 7 +++++-- src/edit/methods.js | 4 ++-- src/measurement/position_measurement.js | 4 ++-- 4 files changed, 13 insertions(+), 10 deletions(-) diff --git a/addon/merge/merge.js b/addon/merge/merge.js index 9bd08658..0c54a664 100644 --- a/addon/merge/merge.js +++ b/addon/merge/merge.js @@ -408,13 +408,13 @@ function drawConnectorsForChunk(dv, chunk, sTopOrig, sTopEdit, w) { var flip = dv.type == "left"; - var top = dv.orig.heightAtLine(chunk.origFrom, "local") - sTopOrig; + var top = dv.orig.heightAtLine(chunk.origFrom, "local", true) - sTopOrig; if (dv.svg) { var topLpx = top; - var topRpx = dv.edit.heightAtLine(chunk.editFrom, "local") - sTopEdit; + var topRpx = dv.edit.heightAtLine(chunk.editFrom, "local", true) - sTopEdit; if (flip) { var tmp = topLpx; topLpx = topRpx; topRpx = tmp; } - var botLpx = dv.orig.heightAtLine(chunk.origTo, "local") - sTopOrig; - var botRpx = dv.edit.heightAtLine(chunk.editTo, "local") - sTopEdit; + var botLpx = dv.orig.heightAtLine(chunk.origTo, "local", true) - sTopOrig; + var botRpx = dv.edit.heightAtLine(chunk.editTo, "local", true) - sTopEdit; if (flip) { var tmp = botLpx; botLpx = botRpx; botRpx = tmp; } var curveTop = " C " + w/2 + " " + topRpx + " " + w/2 + " " + topLpx + " " + (w + 2) + " " + topLpx; var curveBot = " C " + w/2 + " " + botLpx + " " + w/2 + " " + botRpx + " -1 " + botRpx; diff --git a/doc/manual.html b/doc/manual.html index 6ef27689..e74ec362 100644 --- a/doc/manual.html +++ b/doc/manual.html @@ -1863,13 +1863,16 @@

      Sizing, scrolling and positioning methods

      height. mode can be one of the same strings that coordsChar accepts. -
      cm.heightAtLine(line: integer|LineHandle, ?mode: string) → number
      +
      cm.heightAtLine(line: integer|LineHandle, ?mode: string, ?includeWidgets: bool) → number
      Computes the height of the top of a line, in the coordinate system specified by mode (see coordsChar), which defaults to "page". When a line below the bottom of the document is specified, the returned value is the bottom of - the last line in the document.
      + the last line in the document. By default, the position of the + actual text is returned. If `includeWidgets` is true and the + line has line widgets, the position above the first line widget + is returned.
      cm.defaultTextHeight() → number
      Returns the line height of the default font for the editor.
      cm.defaultCharWidth() → number
      diff --git a/src/edit/methods.js b/src/edit/methods.js index b63f9bd2..8aa2a437 100644 --- a/src/edit/methods.js +++ b/src/edit/methods.js @@ -201,7 +201,7 @@ export default function(CodeMirror) { height = fromCoordSystem(this, {top: height, left: 0}, mode || "page").top return lineAtHeight(this.doc, height + this.display.viewOffset) }, - heightAtLine: function(line, mode) { + heightAtLine: function(line, mode, includeWidgets) { let end = false, lineObj if (typeof line == "number") { let last = this.doc.first + this.doc.size - 1 @@ -211,7 +211,7 @@ export default function(CodeMirror) { } else { lineObj = line } - return intoCoordSystem(this, lineObj, {top: 0, left: 0}, mode || "page").top + + return intoCoordSystem(this, lineObj, {top: 0, left: 0}, mode || "page", includeWidgets).top + (end ? this.doc.height - heightAtLine(lineObj) : 0) }, diff --git a/src/measurement/position_measurement.js b/src/measurement/position_measurement.js index 4ece080e..f62672c4 100644 --- a/src/measurement/position_measurement.js +++ b/src/measurement/position_measurement.js @@ -287,8 +287,8 @@ function pageScrollY() { return window.pageYOffset || (document.documentElement // coordinates into another coordinate system. Context may be one of // "line", "div" (display.lineDiv), "local"./null (editor), "window", // or "page". -export function intoCoordSystem(cm, lineObj, rect, context) { - if (lineObj.widgets) for (let i = 0; i < lineObj.widgets.length; ++i) if (lineObj.widgets[i].above) { +export function intoCoordSystem(cm, lineObj, rect, context, includeWidgets) { + if (!includeWidgets && lineObj.widgets) for (let i = 0; i < lineObj.widgets.length; ++i) if (lineObj.widgets[i].above) { let size = widgetHeight(lineObj.widgets[i]) rect.top += size; rect.bottom += size } From e6ec325be0535893137b26545ec14b1c411fb16d Mon Sep 17 00:00:00 2001 From: Marijn Haverbeke Date: Wed, 2 Nov 2016 11:15:32 +0100 Subject: [PATCH 0524/1790] Make sure initial connectors are drawn after aligning/collapsing So that their vertical offsets actually match the position of the corresponding lines. Issue #4364 --- addon/merge/merge.js | 9 +++++++-- 1 file changed, 7 insertions(+), 2 deletions(-) diff --git a/addon/merge/merge.js b/addon/merge/merge.js index 0c54a664..f0d74644 100644 --- a/addon/merge/merge.js +++ b/addon/merge/merge.js @@ -49,6 +49,8 @@ this.diffOutOfDate = this.dealigned = false; this.showDifferences = options.showDifferences !== false; + }, + registerEvents: function() { this.forceUpdate = registerUpdate(this); setScrollLock(this, true, false); registerScroll(this); @@ -91,10 +93,11 @@ updateMarks(dv.edit, dv.diff, edit, DIFF_INSERT, dv.classes); updateMarks(dv.orig, dv.diff, orig, DIFF_DELETE, dv.classes); } - makeConnections(dv); if (dv.mv.options.connect == "align") alignChunks(dv); + makeConnections(dv); + updating = false; } function setDealign(fast) { @@ -489,7 +492,6 @@ if (left) left.init(leftPane, origLeft, options); if (right) right.init(rightPane, origRight, options); - if (options.collapseIdentical) this.editor().operation(function() { collapseIdenticalStretches(self, options.collapseIdentical); @@ -498,6 +500,9 @@ this.aligners = []; alignChunks(this.left || this.right, true); } + if (left) left.registerEvents() + if (right) right.registerEvents() + var onResize = function() { if (left) makeConnections(left); From 73c5bf098ffff029a892a91d91bb8249d40c635f Mon Sep 17 00:00:00 2001 From: Steve Hoover Date: Thu, 3 Nov 2016 10:37:00 -0400 Subject: [PATCH 0525/1790] [verilog mode] Fixed inadvertent removal of TL-Verilog indent(..) function. --- mode/verilog/verilog.js | 16 +++++++++++++--- 1 file changed, 13 insertions(+), 3 deletions(-) diff --git a/mode/verilog/verilog.js b/mode/verilog/verilog.js index 1f6ecb9f..460cdb3b 100644 --- a/mode/verilog/verilog.js +++ b/mode/verilog/verilog.js @@ -494,7 +494,7 @@ CodeMirror.defineMode("verilog", function(config, parserConfig) { } // Compute indentation state: - // o Required indentation on next line + // o Auto indentation on next line // o Indentation scope styles var indented = state.indented; var depth = indented / tlvIndentUnit; @@ -508,9 +508,12 @@ CodeMirror.defineMode("verilog", function(config, parserConfig) { var ch = bodyString[0]; if (tlvScopePrefixChars[ch] && ((match = bodyString.match(tlvIdentMatch)) && tlvIdentifierStyle[match[1]])) { - // this line begins scope (except non-region keyword identifiers, which are statements themselves) + // This line begins scope. + // Next line gets indented one level. + indented += tlvIndentUnit; + // Style the next level of indentation (except non-region keyword identifiers, + // which are statements themselves) if (!(ch == "\\" && chPos > 0)) { - indented += tlvIndentUnit; state.tlvIndentationStyle[depth] = tlvScopePrefixChars[ch]; if (tlvTrackStatements) {state.statementComment = false;} depth++; @@ -524,6 +527,8 @@ CodeMirror.defineMode("verilog", function(config, parserConfig) { } } } + // Set next level of indentation. + state.tlvNextIndent = indented; } if (state.tlvCodeActive) { @@ -651,9 +656,14 @@ CodeMirror.defineMode("verilog", function(config, parserConfig) { return style; }, + indent: function(state) { + return (state.tlvCodeActive == true) ? state.tlvNextIndent : -1; + }, + startState: function(state) { state.tlvIndentationStyle = []; // Styles to use for each level of indentation. state.tlvCodeActive = true; // True when we're in a TLV region (and at beginning of file). + state.tlvNextIndent = -1; // The number of spaces to autoindent the next line if tlvCodeActive. state.tlvInBlockComment = false; // True inside /**/ comment. if (tlvTrackStatements) { state.statementComment = false; // True inside a statement's header comment. From dd10e2eef8a73349b3a8535508b3c878967a3215 Mon Sep 17 00:00:00 2001 From: pabloferz Date: Fri, 5 Feb 2016 12:09:26 +0100 Subject: [PATCH 0526/1790] [julia mode] Fixes for julia 0.5 --- mode/julia/julia.js | 217 ++++++++++++++++++++++++-------------------- 1 file changed, 119 insertions(+), 98 deletions(-) diff --git a/mode/julia/julia.js b/mode/julia/julia.js index 004de443..6c40bf20 100644 --- a/mode/julia/julia.js +++ b/mode/julia/julia.js @@ -11,51 +11,62 @@ })(function(CodeMirror) { "use strict"; -CodeMirror.defineMode("julia", function(_conf, parserConf) { - var ERRORCLASS = 'error'; - +CodeMirror.defineMode("julia", function(config, parserConf) { function wordRegexp(words, end) { - if (typeof end === 'undefined') { end = "\\b"; } + if (typeof end === "undefined") { end = "\\b"; } return new RegExp("^((" + words.join(")|(") + "))" + end); } var octChar = "\\\\[0-7]{1,3}"; var hexChar = "\\\\x[A-Fa-f0-9]{1,2}"; - var specialChar = "\\\\[abfnrtv0%?'\"\\\\]"; - var singleChar = "([^\\u0027\\u005C\\uD800-\\uDFFF]|[\\uD800-\\uDFFF][\\uDC00-\\uDFFF])"; - var operators = parserConf.operators || /^\.?[|&^\\%*+\-<>!=\/]=?|\?|~|:|\$|\.[<>]|<<=?|>>>?=?|\.[<>=]=|->?|\/\/|\bin\b(?!\()|[\u2208\u2209](?!\()/; + var sChar = "\\\\[abefnrtv0%?'\"\\\\]"; + var uChar = "([^\\u0027\\u005C\\uD800-\\uDFFF]|[\\uD800-\\uDFFF][\\uDC00-\\uDFFF])"; + + var operators = parserConf.operators || wordRegexp([ + "\\.?[\\\\%*+\\-<>!=\\/^]=?", "\\.?[|&\\u00F7\\u2260\\u2264\\u2265]", + "\\u00D7", "\\u2208", "\\u2209", "\\u220B", "\\u220C", "\\u2229", + "\\u222A", "\\u2286", "\\u2288", "\\u228A", "\\u22c5", "\\?", "~", ":", + "\\$", "\\.[<>]", "<<=?", ">>>?=?", "\\.[<>=]=", "->?", "\\/\\/", "=>", + "<:", "\\bin\\b(?!\\()"], ""); var delimiters = parserConf.delimiters || /^[;,()[\]{}]/; var identifiers = parserConf.identifiers || /^[_A-Za-z\u00A1-\uFFFF][\w\u00A1-\uFFFF]*!*/; - var charsList = [octChar, hexChar, specialChar, singleChar]; - var blockOpeners = ["begin", "function", "type", "immutable", "let", "macro", "for", "while", "quote", "if", "else", "elseif", "try", "finally", "catch", "do"]; - 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']; - var builtinList = ['true', 'false', 'nothing', 'NaN', 'Inf']; - - //var stringPrefixes = new RegExp("^[br]?('|\")") - var stringPrefixes = /^(`|"{3}|([brv]?"))/; - var chars = wordRegexp(charsList, "'"); - var keywords = wordRegexp(keywordList); - var builtins = wordRegexp(builtinList); - var openers = wordRegexp(blockOpeners); - var closers = wordRegexp(blockClosers); + + var chars = wordRegexp([octChar, hexChar, sChar, uChar], "'"); + var openers = wordRegexp(["begin", "function", "type", "immutable", "let", + "macro", "for", "while", "quote", "if", "else", "elseif", "try", + "finally", "catch", "do"]); + var closers = wordRegexp(["end", "else", "elseif", "catch", "finally"]); + var keywords = wordRegexp(["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"]); + var builtins = wordRegexp(["true", "false", "nothing", "NaN", "Inf"]); + var macro = /^@[_A-Za-z][\w]*/; var symbol = /^:[_A-Za-z\u00A1-\uFFFF][\w\u00A1-\uFFFF]*!*/; - var typeAnnotation = /^::[^,;"{()=$\s]+({[^}]*}+)*/; + var stringPrefixes = /^(`|"{3}|([_A-Za-z\u00A1-\uFFFF]*"))/; function inArray(state) { - var ch = currentScope(state); - if (ch == '[') { + return inGenerator(state, '[') + } + + function inGenerator(state, bracket) { + var curr = currentScope(state), + prev = currentScope(state, 1); + if (typeof(bracket) === "undefined") { bracket = '('; } + if (curr === bracket || (prev === bracket && curr === "for")) { return true; } return false; } - function currentScope(state) { - if (state.scopes.length == 0) { + function currentScope(state, n) { + if (typeof(n) === "undefined") { n = 0; } + if (state.scopes.length <= n) { return null; } - return state.scopes[state.scopes.length - 1]; + return state.scopes[state.scopes.length - (n + 1)]; } // tokenizers @@ -72,14 +83,15 @@ CodeMirror.defineMode("julia", function(_conf, parserConf) { leavingExpr = false; } state.leavingExpr = false; + if (leavingExpr) { if (stream.match(/^'+/)) { - return 'operator'; + return "operator"; } } if (stream.match(/^\.{2,3}/)) { - return 'operator'; + return "operator"; } if (stream.eatSpace()) { @@ -91,7 +103,7 @@ CodeMirror.defineMode("julia", function(_conf, parserConf) { // Handle single line comments if (ch === '#') { stream.skipToEnd(); - return 'comment'; + return "comment"; } if (ch === '[') { @@ -104,36 +116,55 @@ CodeMirror.defineMode("julia", function(_conf, parserConf) { var scope = currentScope(state); - if (scope == '[' && ch === ']') { + if (inArray(state) && ch === ']') { + if (scope === "for") { state.scopes.pop(); } state.scopes.pop(); state.leavingExpr = true; } - if (scope == '(' && ch === ')') { + if (inGenerator(state) && ch === ')') { + if (scope === "for") { state.scopes.pop(); } state.scopes.pop(); state.leavingExpr = true; } var match; - if (!inArray(state) && (match=stream.match(openers, false))) { - state.scopes.push(match); + if (match = stream.match(openers, false)) { + state.scopes.push(match[0]); } - if (!inArray(state) && stream.match(closers, false)) { + if (stream.match(closers, false)) { state.scopes.pop(); } if (inArray(state)) { - if (state.lastToken == 'end' && stream.match(/^:/)) { - return 'operator'; + if (state.lastToken == "end" && stream.match(/^:/)) { + return "operator"; } if (stream.match(/^end/)) { - return 'number'; + return "number"; } } - if (stream.match(/^=>/)) { - return 'operator'; + // Handle type annotations + if (stream.match(/^::(?![:\$])/)) { + state.tokenize = tokenAnnotation; + return state.tokenize(stream, state); + } + + // Handle symbols + if (!leavingExpr && stream.match(symbol) || stream.match(/:\./)) { + return "builtin"; + } + + // Handle parametric types + if (stream.match(/^{[^}]*}(?=\()/)) { + return "builtin"; + } + + // Handle operators and Delimiters + if (stream.match(operators)) { + return "operator"; } // Handle Number Literals @@ -156,33 +187,10 @@ CodeMirror.defineMode("julia", function(_conf, parserConf) { // Integer literals may be "long" stream.match(imMatcher); state.leavingExpr = true; - return 'number'; + return "number"; } } - if (stream.match(/^<:/)) { - return 'operator'; - } - - if (stream.match(typeAnnotation)) { - return 'builtin'; - } - - // Handle symbols - if (!leavingExpr && stream.match(symbol) || stream.match(/:\./)) { - return 'builtin'; - } - - // Handle parametric types - if (stream.match(/^{[^}]*}(?=\()/)) { - return 'builtin'; - } - - // Handle operators and Delimiters - if (stream.match(operators)) { - return 'operator'; - } - // Handle Chars if (stream.match(/^'/)) { state.tokenize = tokenChar; @@ -196,7 +204,7 @@ CodeMirror.defineMode("julia", function(_conf, parserConf) { } if (stream.match(macro)) { - return 'meta'; + return "meta"; } if (stream.match(delimiters)) { @@ -204,38 +212,36 @@ CodeMirror.defineMode("julia", function(_conf, parserConf) { } if (stream.match(keywords)) { - return 'keyword'; + return "keyword"; } if (stream.match(builtins)) { - return 'builtin'; + return "builtin"; } - var isDefinition = state.isDefinition || - state.lastToken == 'function' || - state.lastToken == 'macro' || - state.lastToken == 'type' || - state.lastToken == 'immutable'; + var isDefinition = state.isDefinition || state.lastToken == "function" || + state.lastToken == "macro" || state.lastToken == "type" || + state.lastToken == "immutable"; if (stream.match(identifiers)) { if (isDefinition) { if (stream.peek() === '.') { state.isDefinition = true; - return 'variable'; + return "variable"; } state.isDefinition = false; - return 'def'; + return "def"; } if (stream.match(/^({[^}]*})*\(/, false)) { return callOrDef(stream, state); } state.leavingExpr = true; - return 'variable'; + return "variable"; } // Handle non-detected items stream.next(); - return ERRORCLASS; + return "error"; } function callOrDef(stream, state) { @@ -255,8 +261,8 @@ CodeMirror.defineMode("julia", function(_conf, parserConf) { state.firstParenPos = -1; state.charsAdvanced = 0; if (isDefinition) - return 'def'; - return 'builtin'; + return "def"; + return "builtin"; } } // Unfortunately javascript does not support multiline strings, so we have @@ -268,25 +274,40 @@ CodeMirror.defineMode("julia", function(_conf, parserConf) { state.scopes.pop(); state.firstParenPos = -1; state.charsAdvanced = 0; - return 'builtin'; + return "builtin"; } state.charsAdvanced += stream.match(/^([^()]*)/)[1].length; return callOrDef(stream, state); } + function tokenAnnotation(stream, state) { + stream.match(/.*?(?=,|;|{|}|\(|\)|=|$|\s)/); + if (stream.match(/^{/)) { + state.nestedLevels++; + } else if (stream.match(/^}/)) { + state.nestedLevels--; + } + if (state.nestedLevels > 0) { + stream.match(/.*?(?={|})/); + } else if (state.nestedLevels == 0) { + state.tokenize = tokenBase; + } + return "builtin"; + } + function tokenComment(stream, state) { if (stream.match(/^#=/)) { - state.weakScopes++; + state.nestedLevels++; } if (!stream.match(/.*?(?=(#=|=#))/)) { stream.skipToEnd(); } if (stream.match(/^=#/)) { - state.weakScopes--; - if (state.weakScopes == 0) + state.nestedLevels--; + if (state.nestedLevels == 0) state.tokenize = tokenBase; } - return 'comment'; + return "comment"; } function tokenChar(stream, state) { @@ -309,33 +330,29 @@ CodeMirror.defineMode("julia", function(_conf, parserConf) { if (isChar) { state.leavingExpr = true; state.tokenize = tokenBase; - return 'string'; + return "string"; } if (!stream.match(/^[^']+(?=')/)) { stream.skipToEnd(); } if (stream.match(/^'/)) { state.tokenize = tokenBase; } - return ERRORCLASS; + return "error"; } function tokenStringFactory(delimiter) { - while ('bruv'.indexOf(delimiter.charAt(0).toLowerCase()) >= 0) { - delimiter = delimiter.substr(1); - } - var OUTCLASS = 'string'; - + delimiter = (delimiter === '`' || delimiter === '"""') ? delimiter : '"' function tokenString(stream, state) { while (!stream.eol()) { - stream.eatWhile(/[^"\\]/); + stream.eatWhile(/[^\\"]/); if (stream.eat('\\')) { stream.next(); } else if (stream.match(delimiter)) { state.tokenize = tokenBase; state.leavingExpr = true; - return OUTCLASS; + return "string"; } else { - stream.eat(/["]/); + stream.eat('"'); } } - return OUTCLASS; + return "string"; } tokenString.isString = true; return tokenString; @@ -346,10 +363,10 @@ CodeMirror.defineMode("julia", function(_conf, parserConf) { return { tokenize: tokenBase, scopes: [], - weakScopes: 0, lastToken: null, leavingExpr: false, isDefinition: false, + nestedLevels: 0, charsAdvanced: 0, firstParenPos: -1 }; @@ -366,20 +383,24 @@ CodeMirror.defineMode("julia", function(_conf, parserConf) { // Handle '.' connected identifiers if (current === '.') { style = stream.match(identifiers, false) || stream.match(macro, false) || - stream.match(/\(/, false) ? 'operator' : ERRORCLASS; + stream.match(/\(/, false) ? "operator" : "error"; } return style; }, indent: function(state, textAfter) { var delta = 0; - if (textAfter == "]" || textAfter == ")" || textAfter == "end" || textAfter == "else" || textAfter == "elseif" || textAfter == "catch" || textAfter == "finally") { + if ( textAfter === ']' || textAfter === ')' || textAfter === "end" || + textAfter === "else" || textAfter === "catch" || + textAfter === "finally" ) { delta = -1; } - return (state.scopes.length + delta) * _conf.indentUnit; + return (state.scopes.length + delta) * config.indentUnit; }, - electricInput: /(end|else(if)?|catch|finally)$/, + electricInput: /\b(end|else|catch|finally)\b/, + blockCommentStart: "#=", + blockCommentEnd: "=#", lineComment: "#", fold: "indent" }; From 4a1ed91341378942e49a4ca1d978d99eff4dd33e Mon Sep 17 00:00:00 2001 From: Jim Avery Date: Thu, 3 Nov 2016 16:38:14 -0500 Subject: [PATCH 0527/1790] [swift mode] Various improvements - Added for as a defining keyword - Added new types and operators - Fixed numbers so basic integers are represented - Identifiers can now be surrounded with backticks - Properties and #/@ instructions are now distinct, with the latter represented as a builtin type - Properties are now matched before punctuation. Code can now fold. - Remove the regexp checking as that syntax does not currently exist in Swift - Added tests --- mode/swift/swift.js | 39 +++++++----- mode/swift/test.js | 149 ++++++++++++++++++++++++++++++++++++++++++++ test/index.html | 2 + 3 files changed, 176 insertions(+), 14 deletions(-) create mode 100644 mode/swift/test.js diff --git a/mode/swift/swift.js b/mode/swift/swift.js index 9dcd822e..32947066 100644 --- a/mode/swift/swift.js +++ b/mode/swift/swift.js @@ -26,14 +26,20 @@ "defer","return","inout","mutating","nonmutating","catch","do","rethrows","throw","throws","try","didSet","get","set","willSet", "assignment","associativity","infix","left","none","operator","postfix","precedence","precedencegroup","prefix","right", "Any","AnyObject","Type","dynamicType","Self","Protocol","__COLUMN__","__FILE__","__FUNCTION__","__LINE__"]) - var definingKeywords = wordSet(["var","let","class","enum","extension","import","protocol","struct","func","typealias","associatedtype"]) + var definingKeywords = wordSet(["var","let","class","enum","extension","import","protocol","struct","func","typealias","associatedtype","for"]) var atoms = wordSet(["true","false","nil","self","super","_"]) - var types = wordSet(["Array","Bool","Dictionary","Double","Float","Int","Never","Optional","String","Void"]) - var operators = "+-/*%=|&<>" - var punc = ";,.(){}[]" - var number = /^-?(?:(?:[\d_]+\.[_\d]*|\.[_\d]+|0o[0-7_\.]+|0b[01_\.]+)(?:e-?[\d_]+)?|0x[\d_a-f\.]+(?:p-?[\d_]+)?)/i - var identifier = /^[_A-Za-z$][_A-Za-z$0-9]*/ - var property = /^[@\#\.][_A-Za-z$][_A-Za-z$0-9]*/ + var types = wordSet(["Array","Bool","Character","Dictionary","Double","Float","Int","Int8","Int16","Int32","Int64","Never","Optional","Set","String", + "UInt8","UInt16","UInt32","UInt64","Void"]) + var operators = "+-/*%=|&<>~^?!" + var punc = ":;,.(){}[]" + var binary = /^\-?0b[01][01_]*/ + var octal = /^\-?0o[0-7][0-7_]*/ + var hexadecimal = /^\-?0x[\dA-Fa-f][\dA-Fa-f_]*(?:(?:\.[\dA-Fa-f][\dA-Fa-f_]*)?[Pp]\-?\d[\d_]*)?/ + var decimal = /^\-?\d[\d_]*(?:\.\d[\d_]*)?(?:[Ee]\-?\d[\d_]*)?/ + var identifier = /^\$\d+|(`?)[_A-Za-z][_A-Za-z$0-9]*\1/ + var property = /^\.(?:\$\d+|(`?)[_A-Za-z][_A-Za-z$0-9]*\1)/ + var instruction = /^\#[A-Za-z]+/ + var attribute = /^@(?:\$\d+|(`?)[_A-Za-z][_A-Za-z$0-9]*\1)/ var regexp = /^\/(?!\s)(?:\/\/)?(?:\\.|[^\/])+\// function tokenBase(stream, state, prev) { @@ -50,8 +56,14 @@ state.tokenize.push(tokenComment) return tokenComment(stream, state) } - if (stream.match(regexp)) return "string-2" } + if (stream.match(instruction)) return "builtin" + if (stream.match(attribute)) return "attribute" + if (stream.match(binary)) return "number" + if (stream.match(octal)) return "number" + if (stream.match(hexadecimal)) return "number" + if (stream.match(decimal)) return "number" + if (stream.match(property)) return "property" if (operators.indexOf(ch) > -1) { stream.next() return "operator" @@ -68,18 +80,15 @@ return tokenize(stream, state) } - if (stream.match(number)) return "number" - if (stream.match(property)) return "property" - if (stream.match(identifier)) { var ident = stream.current() + if (types.hasOwnProperty(ident)) return "variable-2" + if (atoms.hasOwnProperty(ident)) return "atom" if (keywords.hasOwnProperty(ident)) { if (definingKeywords.hasOwnProperty(ident)) state.prev = "define" return "keyword" } - if (types.hasOwnProperty(ident)) return "variable-2" - if (atoms.hasOwnProperty(ident)) return "atom" if (prev == "define") return "def" return "variable" } @@ -191,7 +200,9 @@ lineComment: "//", blockCommentStart: "/*", - blockCommentEnd: "*/" + blockCommentEnd: "*/", + fold: "brace", + closeBrackets: "()[]{}''\"\"``" } }) diff --git a/mode/swift/test.js b/mode/swift/test.js new file mode 100644 index 00000000..8c8ee2f6 --- /dev/null +++ b/mode/swift/test.js @@ -0,0 +1,149 @@ +// CodeMirror, copyright (c) by Marijn Haverbeke and others +// Distributed under an MIT license: http://codemirror.net/LICENSE + +(function() { + var mode = CodeMirror.getMode({indentUnit: 2}, "swift"); + function MT(name) { test.mode(name, mode, Array.prototype.slice.call(arguments, 1)); } + + // Ensure all number types are properly represented. + MT("numbers", + "[keyword var] [def a] [operator =] [number 17]", + "[keyword var] [def b] [operator =] [number -0.5]", + "[keyword var] [def c] [operator =] [number 0.3456e-4]", + "[keyword var] [def d] [operator =] [number 345e2]", + "[keyword var] [def e] [operator =] [number 0o7324]", + "[keyword var] [def f] [operator =] [number 0b10010]", + "[keyword var] [def g] [operator =] [number -0x35ade]", + "[keyword var] [def h] [operator =] [number 0xaea.ep-13]". + "[keyword var] [def i] [operator =] [number 0x13ep6"); + + // Variable/class/etc definition. + MT("definition", + "[keyword var] [def a] [operator =] [number 5]", + "[keyword let] [def b][punctuation :] [variable-2 Int] [operator =] [number 10]", + "[keyword class] [def C] [punctuation {] [punctuation }]", + "[keyword struct] [def D] [punctuation {] [punctuation }]", + "[keyword enum] [def E] [punctuation {] [punctuation }]", + "[keyword extension] [def F] [punctuation {] [punctuation }]", + "[keyword protocol] [def G] [punctuation {] [punctuation }]", + "[keyword func] [def h][punctuation ()] [punctuation {] [punctuation }]", + "[keyword import] [def Foundation]", + "[keyword typealias] [def NewString] [operator =] [variable-2 String]", + "[keyword associatedtype] [def I]", + "[keyword for] [def j] [keyword in] [number 0][punctuation ..][operator <][number 3] [punctuation {] [punctuation }]"); + + // Strings and string interpolation. + MT("strings", + "[keyword var] [def a][punctuation :] [variable-2 String] [operator =] [string \"test\"]", + "[keyword var] [def b][punctuation :] [variable-2 String] [operator =] [string \"\\(][variable a][string )\"]"); + + // Comments. + MT("comments", + "[comment // This is a comment]", + "[comment /* This is another comment */]", + "[keyword var] [def a] [operator =] [number 5] [comment // Third comment]"); + + // Atoms. + MT("atoms", + "[keyword class] [def FooClass] [punctuation {]", + " [keyword let] [def fooBool][punctuation :] [variable-2 Bool][operator ?]", + " [keyword let] [def fooInt][punctuation :] [variable-2 Int][operator ?]", + " [keyword func] [keyword init][punctuation (][variable fooBool][punctuation :] [variable-2 Bool][punctuation ,] [variable barBool][punctuation :] [variable-2 Bool][punctuation )] [punctuation {]", + " [atom super][property .init][punctuation ()]", + " [atom self][property .fooBool] [operator =] [variable fooBool]", + " [variable fooInt] [operator =] [atom nil]", + " [keyword if] [variable barBool] [operator ==] [atom true] [punctuation {]", + " [variable print][punctuation (][string \"True!\"][punctuation )]", + " [punctuation }] [keyword else] [keyword if] [variable barBool] [operator ==] [atom false] [punctuation {]", + " [keyword for] [atom _] [keyword in] [number 0][punctuation ...][number 5] [punctuation {]", + " [variable print][punctuation (][string \"False!\"][punctuation )]", + " [punctuation }]", + " [punctuation }]", + " [punctuation }]", + "[punctuation }]"); + + // Types. + MT("types", + "[keyword var] [def a] [operator =] [variable-2 Array][operator <][variable-2 Int][operator >]", + "[keyword var] [def b] [operator =] [variable-2 Set][operator <][variable-2 Bool][operator >]", + "[keyword var] [def c] [operator =] [variable-2 Dictionary][operator <][variable-2 String][punctuation ,][variable-2 Character][operator >]", + "[keyword var] [def d][punctuation :] [variable-2 Int64][operator ?] [operator =] [variable-2 Optional][punctuation (][number 8][punctuation )]", + "[keyword func] [def e][punctuation ()] [operator ->] [variable-2 Void] [punctuation {]", + " [keyword var] [def e1][punctuation :] [variable-2 Float] [operator =] [number 1.2]", + "[punctuation }]", + "[keyword func] [def f][punctuation ()] [operator ->] [variable-2 Never] [punctuation {]", + " [keyword var] [def f1][punctuation :] [variable-2 Double] [operator =] [number 2.4]", + "[punctuation }]"); + + // Operators. + MT("operators", + "[keyword var] [def a] [operator =] [number 1] [operator +] [number 2]", + "[keyword var] [def b] [operator =] [number 1] [operator -] [number 2]", + "[keyword var] [def c] [operator =] [number 1] [operator *] [number 2]", + "[keyword var] [def d] [operator =] [number 1] [operator /] [number 2]", + "[keyword var] [def e] [operator =] [number 1] [operator %] [number 2]", + "[keyword var] [def f] [operator =] [number 1] [operator |] [number 2]", + "[keyword var] [def g] [operator =] [number 1] [operator &] [number 2]", + "[keyword var] [def h] [operator =] [number 1] [operator <<] [number 2]", + "[keyword var] [def i] [operator =] [number 1] [operator >>] [number 2]", + "[keyword var] [def j] [operator =] [number 1] [operator ^] [number 2]", + "[keyword var] [def k] [operator =] [operator ~][number 1]", + "[keyword var] [def l] [operator =] [variable foo] [operator ?] [number 1] [punctuation :] [number 2]", + "[keyword var] [def m][punctuation :] [variable-2 Int] [operator =] [variable-2 Optional][punctuation (][number 8][punctuation )][operator !]"); + + // Punctuation. + MT("punctuation", + "[keyword let] [def a] [operator =] [number 1][punctuation ;] [keyword let] [def b] [operator =] [number 2]", + "[keyword let] [def testArr][punctuation :] [punctuation [[][variable-2 Int][punctuation ]]] [operator =] [punctuation [[][variable a][punctuation ,] [variable b][punctuation ]]]", + "[keyword for] [def i] [keyword in] [number 0][punctuation ..][operator <][variable testArr][property .count] [punctuation {]", + " [variable print][punctuation (][variable testArr][punctuation [[][variable i][punctuation ]])]", + "[punctuation }]"); + + // Identifiers. + MT("identifiers", + "[keyword let] [def abc] [operator =] [number 1]", + "[keyword let] [def ABC] [operator =] [number 2]", + "[keyword let] [def _123] [operator =] [number 3]", + "[keyword let] [def _$1$2$3] [operator =] [number 4]", + "[keyword let] [def A1$_c32_$_] [operator =] [number 5]", + "[keyword let] [def `var`] [operator =] [punctuation [[][number 1][punctuation ,] [number 2][punctuation ,] [number 3][punctuation ]]]", + "[keyword let] [def square$] [operator =] [variable `var`][property .map] [punctuation {][variable $0] [operator *] [variable $0][punctuation }]", + "$$ [number 1][variable a] $[atom _] [variable _$] [variable __] `[variable a] [variable b]`"); + + // Properties. + MT("properties", + "[variable print][punctuation (][variable foo][property .abc][punctuation )]", + "[variable print][punctuation (][variable foo][property .ABC][punctuation )]", + "[variable print][punctuation (][variable foo][property ._123][punctuation )]", + "[variable print][punctuation (][variable foo][property ._$1$2$3][punctuation )]", + "[variable print][punctuation (][variable foo][property .A1$_c32_$_][punctuation )]", + "[variable print][punctuation (][variable foo][property .`var`][punctuation )]", + "[variable print][punctuation (][variable foo][property .__][punctuation )]"); + + // Instructions or other things that start with #. + MT("instructions", + "[keyword if] [instruction #available][punctuation (][variable iOS] [number 9][punctuation ,] [operator *][punctuation )] [punctuation {}", + "[variable print][punctuation (][instruction #file][punctuation ,] [instruction #function][punctuation )]", + "[variable print][punctuation (][instruction #line][punctuation ,] [instruction #column][punctuation )]", + "[instruction #if] [atom true]", + " [keyword import] [variable A]", + "[instruction #elseif] [atom false]", + " [keyword import] [variable B]", + "[instruction #endif]", + "[instruction #sourceLocation][punctuation (][variable file][punctuation :] [string \"file.swift\"][punctuation ,] [variable line][punctuation :] [number 2][punctuation )]"); + + // Attributes; things that start with @. + MT("attributes", + "[instruction @objc][punctuation (][variable objcFoo][punctuation :)]", + "[instruction @available][punctuation (][variable iOS][punctuation )]"); + + // Property/number edge case. + MT("property_number", + "[variable print][punctuation (][variable foo][property ._123][punctuation )]", + "[variable print][punctuation (]") + + // TODO: correctly identify when multiple variables are being declared + // by use of a comma-separated list. + // TODO: correctly identify when variables are being declared in a tuple. + // TODO: identify protocols as types when used before an extension? +})(); diff --git a/test/index.html b/test/index.html index 8ac33c09..cfa3bb71 100644 --- a/test/index.html +++ b/test/index.html @@ -35,6 +35,7 @@ + @@ -122,6 +123,7 @@

      Test Suite

      + From 18c1bcb556bd1e5bab56d2564fd79cefee4ddc79 Mon Sep 17 00:00:00 2001 From: Marijn Haverbeke Date: Wed, 9 Nov 2016 11:03:13 +0100 Subject: [PATCH 0528/1790] [swift mode] Make tests syntactically valid and in agreement with the mode Issue #4374 --- mode/swift/swift.js | 2 +- mode/swift/test.js | 26 +++++++++++++------------- 2 files changed, 14 insertions(+), 14 deletions(-) diff --git a/mode/swift/swift.js b/mode/swift/swift.js index 32947066..43ab7c8f 100644 --- a/mode/swift/swift.js +++ b/mode/swift/swift.js @@ -40,7 +40,7 @@ var property = /^\.(?:\$\d+|(`?)[_A-Za-z][_A-Za-z$0-9]*\1)/ var instruction = /^\#[A-Za-z]+/ var attribute = /^@(?:\$\d+|(`?)[_A-Za-z][_A-Za-z$0-9]*\1)/ - var regexp = /^\/(?!\s)(?:\/\/)?(?:\\.|[^\/])+\// + //var regexp = /^\/(?!\s)(?:\/\/)?(?:\\.|[^\/])+\// function tokenBase(stream, state, prev) { if (stream.sol()) state.indented = stream.indentation() diff --git a/mode/swift/test.js b/mode/swift/test.js index 8c8ee2f6..786b89e2 100644 --- a/mode/swift/test.js +++ b/mode/swift/test.js @@ -14,8 +14,8 @@ "[keyword var] [def e] [operator =] [number 0o7324]", "[keyword var] [def f] [operator =] [number 0b10010]", "[keyword var] [def g] [operator =] [number -0x35ade]", - "[keyword var] [def h] [operator =] [number 0xaea.ep-13]". - "[keyword var] [def i] [operator =] [number 0x13ep6"); + "[keyword var] [def h] [operator =] [number 0xaea.ep-13]", + "[keyword var] [def i] [operator =] [number 0x13ep6]"); // Variable/class/etc definition. MT("definition", @@ -122,20 +122,20 @@ // Instructions or other things that start with #. MT("instructions", - "[keyword if] [instruction #available][punctuation (][variable iOS] [number 9][punctuation ,] [operator *][punctuation )] [punctuation {}", - "[variable print][punctuation (][instruction #file][punctuation ,] [instruction #function][punctuation )]", - "[variable print][punctuation (][instruction #line][punctuation ,] [instruction #column][punctuation )]", - "[instruction #if] [atom true]", - " [keyword import] [variable A]", - "[instruction #elseif] [atom false]", - " [keyword import] [variable B]", - "[instruction #endif]", - "[instruction #sourceLocation][punctuation (][variable file][punctuation :] [string \"file.swift\"][punctuation ,] [variable line][punctuation :] [number 2][punctuation )]"); + "[keyword if] [builtin #available][punctuation (][variable iOS] [number 9][punctuation ,] [operator *][punctuation )] [punctuation {}]", + "[variable print][punctuation (][builtin #file][punctuation ,] [builtin #function][punctuation )]", + "[variable print][punctuation (][builtin #line][punctuation ,] [builtin #column][punctuation )]", + "[builtin #if] [atom true]", + "[keyword import] [def A]", + "[builtin #elseif] [atom false]", + "[keyword import] [def B]", + "[builtin #endif]", + "[builtin #sourceLocation][punctuation (][variable file][punctuation :] [string \"file.swift\"][punctuation ,] [variable line][punctuation :] [number 2][punctuation )]"); // Attributes; things that start with @. MT("attributes", - "[instruction @objc][punctuation (][variable objcFoo][punctuation :)]", - "[instruction @available][punctuation (][variable iOS][punctuation )]"); + "[attribute @objc][punctuation (][variable objcFoo][punctuation :)]", + "[attribute @available][punctuation (][variable iOS][punctuation )]"); // Property/number edge case. MT("property_number", From a34c02883bcf5ad0d9328de6fff0fafde9a7aa3f Mon Sep 17 00:00:00 2001 From: Paris Kasidiaris Date: Thu, 10 Nov 2016 10:16:01 +0200 Subject: [PATCH 0529/1790] [real-world uses] Add SourceLair --- doc/realworld.html | 1 + 1 file changed, 1 insertion(+) diff --git a/doc/realworld.html b/doc/realworld.html index 8db34cd9..e52c9f00 100644 --- a/doc/realworld.html +++ b/doc/realworld.html @@ -146,6 +146,7 @@

      CodeMirror real-world uses

    • Shadertoy (shader sharing)
    • sketchPatch Livecodelab
    • Skulpt (in-browser Python environment)
    • +
    • SourceLair (in-browser IDE for Django, Node.js, PHP and HTML5)
    • Snap Tomato (HTML editing/testing page)
    • Snippets.pro (code snippet sharing)
    • SolidShops (hosted e-commerce platform)
    • From 5d235c1b6ecb299892179ab8fe0f3f28693aabf1 Mon Sep 17 00:00:00 2001 From: Sander Verweij Date: Sun, 13 Nov 2016 15:26:24 +0100 Subject: [PATCH 0530/1790] [real world uses] adds mscgen.js.org --- doc/realworld.html | 1 + 1 file changed, 1 insertion(+) diff --git a/doc/realworld.html b/doc/realworld.html index e52c9f00..dc2a7f6a 100644 --- a/doc/realworld.html +++ b/doc/realworld.html @@ -118,6 +118,7 @@

      CodeMirror real-world uses

    • MIHTool (iOS web-app debugging tool)
    • Mongo MapReduce WebBrowser
    • Montage Studio (web app creator suite)
    • +
    • mscgen_js (online sequence chart editor)
    • MVC Playground
    • My2ndGeneration (social coding)
    • Navigate CMS
    • From e6eebeb19291889aa05f2bd34ce113702ec44090 Mon Sep 17 00:00:00 2001 From: pabloferz Date: Sat, 12 Nov 2016 23:13:25 -0600 Subject: [PATCH 0531/1790] [julia mode] Fix string tokenizer --- mode/julia/julia.js | 23 ++++++++++------------- 1 file changed, 10 insertions(+), 13 deletions(-) diff --git a/mode/julia/julia.js b/mode/julia/julia.js index 6c40bf20..0174210b 100644 --- a/mode/julia/julia.js +++ b/mode/julia/julia.js @@ -338,23 +338,20 @@ CodeMirror.defineMode("julia", function(config, parserConf) { } function tokenStringFactory(delimiter) { - delimiter = (delimiter === '`' || delimiter === '"""') ? delimiter : '"' + delimiter = (delimiter === '`' || delimiter === '"""') ? delimiter : '"'; function tokenString(stream, state) { - while (!stream.eol()) { - stream.eatWhile(/[^\\"]/); - if (stream.eat('\\')) { - stream.next(); - } else if (stream.match(delimiter)) { - state.tokenize = tokenBase; - state.leavingExpr = true; - return "string"; - } else { - stream.eat('"'); - } + if (stream.eat('\\')) { + stream.next(); + } else if (stream.match(delimiter)) { + state.tokenize = tokenBase; + state.leavingExpr = true; + return "string"; + } else { + stream.eat(/[`"]/); } + stream.eatWhile(/[^\\`"]/); return "string"; } - tokenString.isString = true; return tokenString; } From 0a90aa456c6c850956b2bdc45334dfe5ce7f0d5e Mon Sep 17 00:00:00 2001 From: Marijn Haverbeke Date: Mon, 14 Nov 2016 10:36:23 +0100 Subject: [PATCH 0532/1790] Simplify build instructions in README --- README.md | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/README.md b/README.md index 1b330988..3328e3bd 100644 --- a/README.md +++ b/README.md @@ -30,5 +30,5 @@ conduct. ### Quickstart To build the project, make sure you have Node.js installed (at least version 6) -and then `npm install && npm run build`. To run, just open `index.html` in your +and then `npm install`. To run, just open `index.html` in your browser (you don't need to run a webserver). Run the tests with `npm test`. From 90819c54aae09c0002286cd43e57522432580d7c Mon Sep 17 00:00:00 2001 From: Mark Peace Date: Fri, 11 Nov 2016 16:48:36 +0000 Subject: [PATCH 0533/1790] [cypher mode] Highlight empty string literals correctly --- mode/cypher/cypher.js | 6 +++--- mode/cypher/test.js | 18 ++++++++++++++++++ 2 files changed, 21 insertions(+), 3 deletions(-) diff --git a/mode/cypher/cypher.js b/mode/cypher/cypher.js index 1d9ca433..9b249001 100644 --- a/mode/cypher/cypher.js +++ b/mode/cypher/cypher.js @@ -20,12 +20,12 @@ CodeMirror.defineMode("cypher", function(config) { var tokenBase = function(stream/*, state*/) { var ch = stream.next(); - if (ch === "\"") { - stream.match(/.+?["]/); + if (ch ==='"') { + stream.match(/.*?"/); return "string"; } if (ch === "'") { - stream.match(/.+?[']/); + stream.match(/.*?'/); return "string"; } if (/[{}\(\),\.;\[\]]/.test(ch)) { diff --git a/mode/cypher/test.js b/mode/cypher/test.js index 34cf96ca..76d0d082 100644 --- a/mode/cypher/test.js +++ b/mode/cypher/test.js @@ -16,4 +16,22 @@ MT("singleQuotedString", "[string 'a'][variable b]"); + + MT("single attribute (with content)", + "[node {][atom a:][string 'a'][node }]"); + + MT("multiple attribute, singleQuotedString (with content)", + "[node {][atom a:][string 'a'][node ,][atom b:][string 'b'][node }]"); + + MT("multiple attribute, doubleQuotedString (with content)", + "[node {][atom a:][string \"a\"][node ,][atom b:][string \"b\"][node }]"); + + MT("single attribute (without content)", + "[node {][atom a:][string 'a'][node }]"); + + MT("multiple attribute, singleQuotedString (without content)", + "[node {][atom a:][string ''][node ,][atom b:][string ''][node }]"); + + MT("multiple attribute, doubleQuotedString (without content)", + "[node {][atom a:][string \"\"][node ,][atom b:][string \"\"][node }]"); })(); From 532ae310c9248e696caa21ca34519b79367cd4eb Mon Sep 17 00:00:00 2001 From: Marcelo Camargo Date: Mon, 14 Nov 2016 17:37:54 -0200 Subject: [PATCH 0534/1790] [sql mode] Remove non-strict useless comparison for booleans --- mode/sql/sql.js | 14 +++++++------- 1 file changed, 7 insertions(+), 7 deletions(-) diff --git a/mode/sql/sql.js b/mode/sql/sql.js index e3cbae54..32ced3e9 100644 --- a/mode/sql/sql.js +++ b/mode/sql/sql.js @@ -32,13 +32,13 @@ CodeMirror.defineMode("sql", function(config, parserConfig) { if (result !== false) return result; } - if (support.hexNumber == true && + if (support.hexNumber && ((ch == "0" && stream.match(/^[xX][0-9a-fA-F]+/)) || (ch == "x" || ch == "X") && stream.match(/^'[0-9a-fA-F]+'/))) { // hex // ref: http://dev.mysql.com/doc/refman/5.5/en/hexadecimal-literals.html return "number"; - } else if (support.binaryNumber == true && + } else if (support.binaryNumber && (((ch == "b" || ch == "B") && stream.match(/^'[01]+'/)) || (ch == "0" && stream.match(/^b[01]+/)))) { // bitstring @@ -48,7 +48,7 @@ CodeMirror.defineMode("sql", function(config, parserConfig) { // numbers // ref: http://dev.mysql.com/doc/refman/5.5/en/number-literals.html stream.match(/^[0-9]*\.?[0-9]+([eE][-+]?[0-9]+)?/); - support.decimallessFloat == true && stream.eat('.'); + support.decimallessFloat && stream.eat('.'); return "number"; } else if (ch == "?" && (stream.eatSpace() || stream.eol() || stream.eat(";"))) { // placeholders @@ -58,8 +58,8 @@ CodeMirror.defineMode("sql", function(config, parserConfig) { // ref: http://dev.mysql.com/doc/refman/5.5/en/string-literals.html state.tokenize = tokenLiteral(ch); return state.tokenize(stream, state); - } else if ((((support.nCharCast == true && (ch == "n" || ch == "N")) - || (support.charsetCast == true && ch == "_" && stream.match(/[a-z][a-z0-9]*/i))) + } else if ((((support.nCharCast && (ch == "n" || ch == "N")) + || (support.charsetCast && ch == "_" && stream.match(/[a-z][a-z0-9]*/i))) && (stream.peek() == "'" || stream.peek() == '"'))) { // charset casting: _utf8'str', N'str', n'str' // ref: http://dev.mysql.com/doc/refman/5.5/en/string-literals.html @@ -84,12 +84,12 @@ CodeMirror.defineMode("sql", function(config, parserConfig) { return state.tokenize(stream, state); } else if (ch == ".") { // .1 for 0.1 - if (support.zerolessFloat == true && stream.match(/^(?:\d+(?:e[+-]?\d+)?)/i)) { + if (support.zerolessFloat && stream.match(/^(?:\d+(?:e[+-]?\d+)?)/i)) { return "number"; } // .table_name (ODBC) // // ref: http://dev.mysql.com/doc/refman/5.6/en/identifier-qualifiers.html - if (support.ODBCdotTable == true && stream.match(/^[a-zA-Z_]+/)) { + if (support.ODBCdotTable && stream.match(/^[a-zA-Z_]+/)) { return "variable-2"; } } else if (operatorChars.test(ch)) { From beb838248ad29721f11c5b33ce08c701d93875da Mon Sep 17 00:00:00 2001 From: takamori Date: Mon, 14 Nov 2016 12:44:30 -0800 Subject: [PATCH 0535/1790] [css mode] Support user-select. As described in http://caniuse.com/#feat=user-select-none and https://developer.mozilla.org/en-US/docs/Web/CSS/user-select --- 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 b7573203..985287f4 100644 --- a/mode/css/css.js +++ b/mode/css/css.js @@ -522,7 +522,7 @@ CodeMirror.defineMode("css", function(config, parserConfig) { "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", + "user-select", "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", From 5012e8772371632bc4e4162e1a0f674d42cd1d79 Mon Sep 17 00:00:00 2001 From: Marijn Haverbeke Date: Tue, 15 Nov 2016 13:18:46 +0100 Subject: [PATCH 0536/1790] [contenteditable input] Force editor selection in focus method So that the selection isn't reset to the start of the element by div.focus(). --- src/input/ContentEditableInput.js | 6 +++++- 1 file changed, 5 insertions(+), 1 deletion(-) diff --git a/src/input/ContentEditableInput.js b/src/input/ContentEditableInput.js index cdaa8254..594f17f6 100644 --- a/src/input/ContentEditableInput.js +++ b/src/input/ContentEditableInput.js @@ -199,7 +199,11 @@ ContentEditableInput.prototype = copyObj({ }, focus: function() { - if (this.cm.options.readOnly != "nocursor") this.div.focus() + if (this.cm.options.readOnly != "nocursor") { + if (!this.selectionInEditor()) + this.showSelection(this.prepareSelection(), true) + this.div.focus() + } }, blur: function() { this.div.blur() }, getField: function() { return this.div }, From da8a35d05e720db980c4ae2307c7615aceaa53ac Mon Sep 17 00:00:00 2001 From: Marijn Haverbeke Date: Tue, 15 Nov 2016 13:46:59 +0100 Subject: [PATCH 0537/1790] Handle compositionupdate events without corresponding compositionstart Because Android, especially Google Keyboard, just doesn't care --- src/input/ContentEditableInput.js | 10 +++++++--- 1 file changed, 7 insertions(+), 3 deletions(-) diff --git a/src/input/ContentEditableInput.js b/src/input/ContentEditableInput.js index 594f17f6..524d571e 100644 --- a/src/input/ContentEditableInput.js +++ b/src/input/ContentEditableInput.js @@ -37,8 +37,7 @@ ContentEditableInput.prototype = copyObj({ }), 20) }) - on(div, "compositionstart", e => { - let data = e.data + function startComposing(data) { input.composing = {sel: cm.doc.sel, data: data, startData: data} if (!data) return let prim = cm.doc.sel.primary() @@ -47,8 +46,13 @@ ContentEditableInput.prototype = copyObj({ if (found > -1 && found <= prim.head.ch) input.composing.sel = simpleSelection(Pos(prim.head.line, found), Pos(prim.head.line, found + data.length)) + } + + on(div, "compositionstart", e => startComposing(e.data)) + on(div, "compositionupdate", e => { + if (input.composing) input.composing.data = e.data + else startComposing(e.data) }) - on(div, "compositionupdate", e => input.composing.data = e.data) on(div, "compositionend", e => { let ours = input.composing if (!ours) return From 0e545326ddb3a82df1b76eb18b2221990536e588 Mon Sep 17 00:00:00 2001 From: Todd Berman Date: Tue, 8 Nov 2016 09:17:56 -0800 Subject: [PATCH 0538/1790] Move setGutterMarker, clearGutter and lineInfo to Doc --- doc/manual.html | 6 +++--- src/edit/methods.js | 42 ++---------------------------------------- src/model/Doc.js | 41 ++++++++++++++++++++++++++++++++++++++++- 3 files changed, 45 insertions(+), 44 deletions(-) diff --git a/doc/manual.html b/doc/manual.html index e74ec362..ecfe3071 100644 --- a/doc/manual.html +++ b/doc/manual.html @@ -1699,7 +1699,7 @@

      Text-marking methods

      Widget, gutter, and decoration methods

      -
      cm.setGutterMarker(line: integer|LineHandle, gutterID: string, value: Element) → LineHandle
      +
      doc.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) @@ -1708,7 +1708,7 @@

      Widget, gutter, and decoration methods

      will be shown in the specified gutter next to the specified line.
      -
      cm.clearGutter(gutterID: string)
      +
      doc.clearGutter(gutterID: string)
      Remove all gutter markers in the gutter with the given ID.
      @@ -1733,7 +1733,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) → object
      +
      doc.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, diff --git a/src/edit/methods.js b/src/edit/methods.js index 8aa2a437..7efaf20e 100644 --- a/src/edit/methods.js +++ b/src/edit/methods.js @@ -1,5 +1,4 @@ import { deleteNearSelection } from "./deleteNearSelection" -import { changeLine } from "../model/changes" import { commands } from "./commands" import { attachDoc } from "../model/document_data" import { activeElt, addClass, rmClass } from "../util/dom" @@ -18,9 +17,9 @@ import { addToScrollPos, calculateScrollPos, ensureCursorVisible, resolveScrollT import { heightAtLine } from "../line/spans" import { updateGutterSpace } from "../display/update_display" import { lineLeft, lineRight, moveLogically, moveVisually } from "../util/bidi" -import { indexOf, insertSorted, isEmpty, isWordChar, sel_dontScroll, sel_move } from "../util/misc" +import { indexOf, insertSorted, isWordChar, sel_dontScroll, sel_move } from "../util/misc" import { signalLater } from "../util/operation_group" -import { getLine, isLine, lineAtHeight, lineNo } from "../line/utils_line" +import { getLine, isLine, lineAtHeight } from "../line/utils_line" import { regChange, regLineChange } from "../display/view_tracking" // The publicly visible API. Note that methodOp(f) means @@ -218,43 +217,6 @@ export default function(CodeMirror) { defaultTextHeight: function() { return textHeight(this.display) }, defaultCharWidth: function() { return charWidth(this.display) }, - setGutterMarker: methodOp(function(line, gutterID, value) { - return changeLine(this.doc, line, "gutter", line => { - let markers = line.gutterMarkers || (line.gutterMarkers = {}) - markers[gutterID] = value - if (!value && isEmpty(markers)) line.gutterMarkers = null - return true - }) - }), - - clearGutter: methodOp(function(gutterID) { - let doc = this.doc, i = doc.first - doc.iter(line => { - if (line.gutterMarkers && line.gutterMarkers[gutterID]) { - line.gutterMarkers[gutterID] = null - regLineChange(this, i, "gutter") - if (isEmpty(line.gutterMarkers)) line.gutterMarkers = null - } - ++i - }) - }), - - lineInfo: function(line) { - let n - if (typeof line == "number") { - if (!isLine(this.doc, line)) return null - n = line - line = getLine(this.doc, line) - if (!line) return null - } else { - n = lineNo(line) - if (n == null) return null - } - return {line: n, handle: line, text: line.text, gutterMarkers: line.gutterMarkers, - textClass: line.textClass, bgClass: line.bgClass, wrapClass: line.wrapClass, - widgets: line.widgets} - }, - getViewport: function() { return {from: this.display.viewFrom, to: this.display.viewTo}}, addWidget: function(pos, node, scroll, vert, horiz) { diff --git a/src/model/Doc.js b/src/model/Doc.js index fcb2c1e1..27b62d19 100644 --- a/src/model/Doc.js +++ b/src/model/Doc.js @@ -6,7 +6,7 @@ import { visualLine } from "../line/spans" import { getBetween, getLine, getLines, isLine, lineNo } from "../line/utils_line" import { classTest } from "../util/dom" import { splitLinesAuto } from "../util/feature_detection" -import { createObj, map, sel_dontScroll } from "../util/misc" +import { createObj, map, isEmpty, sel_dontScroll } from "../util/misc" import { ensureCursorVisible } from "../display/scrolling" import { changeLine, makeChange, makeChangeFromHistory, replaceRange } from "./changes" @@ -219,6 +219,45 @@ Doc.prototype = createObj(BranchChunk.prototype, { hist.undone = copyHistoryArray(histData.undone.slice(0), null, true) }, + setGutterMarker: docMethodOp(function(line, gutterID, value) { + return changeLine(this, line, "gutter", line => { + let markers = line.gutterMarkers || (line.gutterMarkers = {}) + markers[gutterID] = value + if (!value && isEmpty(markers)) line.gutterMarkers = null + return true + }) + }), + + clearGutter: docMethodOp(function(gutterID) { + let i = this.first + this.iter(line => { + if (line.gutterMarkers && line.gutterMarkers[gutterID]) { + changeLine(this, line, "gutter", () => { + line.gutterMarkers[gutterID] = null + if (isEmpty(line.gutterMarkers)) line.gutterMarkers = null + return true + }) + } + ++i + }) + }), + + lineInfo: function(line) { + let n + if (typeof line == "number") { + if (!isLine(this, line)) return null + n = line + line = getLine(this, line) + if (!line) return null + } else { + n = lineNo(line) + if (n == null) return null + } + return {line: n, handle: line, text: line.text, gutterMarkers: line.gutterMarkers, + textClass: line.textClass, bgClass: line.bgClass, wrapClass: line.wrapClass, + widgets: line.widgets} + }, + addLineClass: docMethodOp(function(handle, where, cls) { return changeLine(this, handle, where == "gutter" ? "gutter" : "class", line => { let prop = where == "text" ? "textClass" From 441641e6cb75ebbd2f5551befe2b2cde9ddf9ab2 Mon Sep 17 00:00:00 2001 From: Marijn Haverbeke Date: Tue, 15 Nov 2016 14:54:05 +0100 Subject: [PATCH 0539/1790] [contenteditable input] Read from the DOM to get composition input And do so only after a delay, so that subsequent input events get a chance to fire. --- src/edit/CodeMirror.js | 1 + src/edit/mouse_events.js | 1 + src/input/ContentEditableInput.js | 67 +++++++++++++------------------ 3 files changed, 30 insertions(+), 39 deletions(-) diff --git a/src/edit/CodeMirror.js b/src/edit/CodeMirror.js index 7e17002d..a3dc622c 100644 --- a/src/edit/CodeMirror.js +++ b/src/edit/CodeMirror.js @@ -142,6 +142,7 @@ function registerEventHandlers(cm) { } on(d.scroller, "touchstart", e => { if (!signalDOMEvent(cm, e) && !isMouseLikeTouchEvent(e)) { + d.input.ensurePolled() clearTimeout(touchFinished) let now = +new Date d.activeTouch = {start: now, moved: false, diff --git a/src/edit/mouse_events.js b/src/edit/mouse_events.js index 784f195b..0b96f4cf 100644 --- a/src/edit/mouse_events.js +++ b/src/edit/mouse_events.js @@ -21,6 +21,7 @@ import { bind, countColumn, findColumn, sel_mouse } from "../util/misc" export function onMouseDown(e) { let cm = this, display = cm.display if (signalDOMEvent(cm, e) || display.activeTouch && display.input.supportsTouch()) return + display.input.ensurePolled() display.shift = e.shiftKey if (eventInWidget(display, e)) { diff --git a/src/input/ContentEditableInput.js b/src/input/ContentEditableInput.js index 524d571e..a7254b12 100644 --- a/src/input/ContentEditableInput.js +++ b/src/input/ContentEditableInput.js @@ -20,7 +20,9 @@ export default function ContentEditableInput(cm) { this.cm = cm this.lastAnchorNode = this.lastAnchorOffset = this.lastFocusNode = this.lastFocusOffset = null this.polling = new Delayed() + this.composing = null this.gracePeriod = false + this.readDOMTimeout = null } ContentEditableInput.prototype = copyObj({ @@ -37,44 +39,23 @@ ContentEditableInput.prototype = copyObj({ }), 20) }) - function startComposing(data) { - input.composing = {sel: cm.doc.sel, data: data, startData: data} - if (!data) return - let prim = cm.doc.sel.primary() - let line = cm.getLine(prim.head.line) - let found = line.indexOf(data, Math.max(0, prim.head.ch - data.length)) - if (found > -1 && found <= prim.head.ch) - input.composing.sel = simpleSelection(Pos(prim.head.line, found), - Pos(prim.head.line, found + data.length)) - } - - on(div, "compositionstart", e => startComposing(e.data)) + on(div, "compositionstart", e => { + this.composing = {data: e.data} + }) on(div, "compositionupdate", e => { - if (input.composing) input.composing.data = e.data - else startComposing(e.data) + if (!this.composing) this.composing = {data: e.data} }) on(div, "compositionend", e => { - let ours = input.composing - if (!ours) return - if (e.data != ours.startData && !/\u200b/.test(e.data)) - ours.data = e.data - // Need a small delay to prevent other code (input event, - // selection polling) from doing damage when fired right after - // compositionend. - setTimeout(() => { - if (!ours.handled) - input.applyComposition(ours) - if (input.composing == ours) - input.composing = null - }, 50) + if (this.composing) { + if (e.data != this.composing.data) this.readFromDOMSoon() + this.composing = null + } }) on(div, "touchstart", () => input.forceCompositionEnd()) on(div, "input", () => { - if (input.composing) return - if (cm.isReadOnly() || !input.pollContent()) - runInOp(input.cm, () => regChange(cm)) + if (!this.composing) this.readFromDOMSoon() }) function onCopyCut(e) { @@ -237,7 +218,7 @@ ContentEditableInput.prototype = copyObj({ }, pollSelection: function() { - if (!this.composing && !this.gracePeriod && this.selectionChanged()) { + if (!this.composing && this.readDOMTimeout == null && !this.gracePeriod && this.selectionChanged()) { let sel = window.getSelection(), cm = this.cm this.rememberSelection() let anchor = domToPos(cm, sel.anchorNode, sel.anchorOffset) @@ -250,6 +231,11 @@ ContentEditableInput.prototype = copyObj({ }, pollContent: function() { + if (this.readDOMTimeout != null) { + clearTimeout(this.readDOMTimeout) + this.readDOMTimeout = null + } + let cm = this.cm, display = cm.display, sel = cm.doc.sel.primary() let from = sel.from(), to = sel.to() if (from.line < display.viewFrom || to.line > display.viewTo - 1) return false @@ -309,17 +295,20 @@ ContentEditableInput.prototype = copyObj({ this.forceCompositionEnd() }, forceCompositionEnd: function() { - if (!this.composing || this.composing.handled) return - this.applyComposition(this.composing) - this.composing.handled = true + if (!this.composing) return + this.composing = null + if (!this.pollContent()) regChange(this.cm) this.div.blur() this.div.focus() }, - applyComposition: function(composing) { - if (this.cm.isReadOnly()) - operation(this.cm, regChange)(this.cm) - else if (composing.data && composing.data != composing.startData) - operation(this.cm, applyTextInput)(this.cm, composing.data, 0, composing.sel) + readFromDOMSoon: function() { + if (this.readDOMTimeout != null) return + this.readDOMTimeout = setTimeout(() => { + this.readDOMTimeout = null + if (this.composing) return + if (this.cm.isReadOnly() || !this.pollContent()) + runInOp(this.cm, () => regChange(this.cm)) + }, 80) }, setUneditable: function(node) { From d7b1370ca45d742c0961ce98d25ea2c2d3f0f484 Mon Sep 17 00:00:00 2001 From: Marijn Haverbeke Date: Tue, 15 Nov 2016 17:04:49 +0100 Subject: [PATCH 0540/1790] Copy event handler arrays on write Rather than on read --- src/util/event.js | 35 ++++++++++++++++++----------------- src/util/operation_group.js | 2 +- 2 files changed, 19 insertions(+), 18 deletions(-) diff --git a/src/util/event.js b/src/util/event.js index e667a9f3..29fd4c59 100644 --- a/src/util/event.js +++ b/src/util/event.js @@ -6,39 +6,40 @@ import { indexOf } from "./misc" // Lightweight event framework. on/off also work on DOM nodes, // registering native DOM handlers. +const noHandlers = [] + export let on = function(emitter, type, f) { - if (emitter.addEventListener) + if (emitter.addEventListener) { emitter.addEventListener(type, f, false) - else if (emitter.attachEvent) + } else if (emitter.attachEvent) { emitter.attachEvent("on" + type, f) - else { + } else { let map = emitter._handlers || (emitter._handlers = {}) - let arr = map[type] || (map[type] = []) - arr.push(f) + map[type] = (map[type] || noHandlers).concat(f) } } -let noHandlers = [] -export function getHandlers(emitter, type, copy) { - let arr = emitter._handlers && emitter._handlers[type] - if (copy) return arr && arr.length > 0 ? arr.slice() : noHandlers - else return arr || noHandlers +export function getHandlers(emitter, type) { + return emitter._handlers && emitter._handlers[type] || noHandlers } export function off(emitter, type, f) { - if (emitter.removeEventListener) + if (emitter.removeEventListener) { emitter.removeEventListener(type, f, false) - else if (emitter.detachEvent) + } else if (emitter.detachEvent) { emitter.detachEvent("on" + type, f) - else { - let handlers = getHandlers(emitter, type, false) - for (let i = 0; i < handlers.length; ++i) - if (handlers[i] == f) { handlers.splice(i, 1); break } + } else { + let map = emitter._handlers, arr = map && map[type] + if (arr) { + let index = indexOf(arr, f) + if (index > -1) + map[type] = arr.slice(0, index).concat(arr.slice(index + 1)) + } } } export function signal(emitter, type /*, values...*/) { - let handlers = getHandlers(emitter, type, true) + let handlers = getHandlers(emitter, type) if (!handlers.length) return let args = Array.prototype.slice.call(arguments, 2) for (let i = 0; i < handlers.length; ++i) handlers[i].apply(null, args) diff --git a/src/util/operation_group.js b/src/util/operation_group.js index f50da343..b8fa78ac 100644 --- a/src/util/operation_group.js +++ b/src/util/operation_group.js @@ -50,7 +50,7 @@ let orphanDelayedCallbacks = null // them to be executed when the last operation ends, or, if no // operation is active, when a timeout fires. export function signalLater(emitter, type /*, values...*/) { - let arr = getHandlers(emitter, type, false) + let arr = getHandlers(emitter, type) if (!arr.length) return let args = Array.prototype.slice.call(arguments, 2), list if (operationGroup) { From 6019b1308d4c513cb327f1e7c3f7ff86f258a217 Mon Sep 17 00:00:00 2001 From: Marijn Haverbeke Date: Tue, 15 Nov 2016 17:33:10 +0100 Subject: [PATCH 0541/1790] [contenteditable input] Expand scanned range when selection at start/end of line So that the code doesn't get confused when backspacing or deleting across a line. This is still flaky. Ideally we'd capture backspace as a key event, but Android Chrome makes that impossible. Issue #4307 --- src/input/ContentEditableInput.js | 5 +++++ 1 file changed, 5 insertions(+) diff --git a/src/input/ContentEditableInput.js b/src/input/ContentEditableInput.js index a7254b12..c385bea1 100644 --- a/src/input/ContentEditableInput.js +++ b/src/input/ContentEditableInput.js @@ -238,6 +238,10 @@ ContentEditableInput.prototype = copyObj({ let cm = this.cm, display = cm.display, sel = cm.doc.sel.primary() let from = sel.from(), to = sel.to() + if (from.ch == 0 && from.line > cm.firstLine()) + from = Pos(from.line - 1, getLine(cm.doc, from.line - 1).length) + if (to.ch == getLine(cm.doc, to.line).text.length && to.line < cm.lastLine()) + to = Pos(to.line + 1, 0) if (from.line < display.viewFrom || to.line > display.viewTo - 1) return false let fromIndex, fromLine, fromNode @@ -258,6 +262,7 @@ ContentEditableInput.prototype = copyObj({ toNode = display.view[toIndex + 1].node.previousSibling } + if (!fromNode) return false let newText = cm.doc.splitLines(domTextBetween(cm, fromNode, toNode, fromLine, toLine)) let oldText = getBetween(cm.doc, Pos(fromLine, 0), Pos(toLine, getLine(cm.doc, toLine).text.length)) while (newText.length > 1 && oldText.length > 1) { From 69669e4b74c30a6fa2c25751970b17daf53cf88c Mon Sep 17 00:00:00 2001 From: John-David Dalton Date: Tue, 15 Nov 2016 17:22:20 -0800 Subject: [PATCH 0542/1790] =?UTF-8?q?Avoid=20=E2=80=9CUnspecified=20Error?= =?UTF-8?q?=E2=80=9D=20in=20IE?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit when accessing `document.activeElement` from inside an iframe. --- src/util/dom.js | 20 +++++++++++--------- 1 file changed, 11 insertions(+), 9 deletions(-) diff --git a/src/util/dom.js b/src/util/dom.js index 465dbb5a..349fae07 100644 --- a/src/util/dom.js +++ b/src/util/dom.js @@ -1,4 +1,4 @@ -import { ie, ie_version, ios } from "./browser" +import { ie, ios } from "./browser" export function classTest(cls) { return new RegExp("(^|\\s)" + cls + "(?:$|\\s)\\s*") } @@ -58,18 +58,20 @@ export function contains(parent, child) { } while (child = child.parentNode) } -export let activeElt = function() { - let activeElement = document.activeElement +export function activeElt() { + // IE and Edge may throw an "Unspecified Error" when accessing document.activeElement. + // IE < 10 will throw when accessed while the page is loading or in an iframe. + // IE > 9 and Edge will throw when accessed in an iframe if document.body is unavailable. + let activeElement + try { + activeElement = document.activeElement + } catch(e) { + activeElement = document.body || null + } while (activeElement && activeElement.root && activeElement.root.activeElement) activeElement = activeElement.root.activeElement return activeElement } -// Older versions of IE throws unspecified error when touching -// document.activeElement in some cases (during loading, in iframe) -if (ie && ie_version < 11) activeElt = function() { - try { return document.activeElement } - catch(e) { return document.body } -} export function addClass(node, cls) { let current = node.className From 8ecbdc5c6aedbac6b4038d94c30b97bddc950b1b Mon Sep 17 00:00:00 2001 From: Marijn Haverbeke Date: Mon, 21 Nov 2016 10:12:06 +0100 Subject: [PATCH 0543/1790] [markdown mode] Allow lists without a blank line above As per CommonMark (conflicting with markdown.pl, but never mind markdown.pl) Closes #4395 --- mode/markdown/markdown.js | 15 ++++----------- mode/markdown/test.js | 7 +++---- 2 files changed, 7 insertions(+), 15 deletions(-) diff --git a/mode/markdown/markdown.js b/mode/markdown/markdown.js index 3dcce8d3..6aedc360 100644 --- a/mode/markdown/markdown.js +++ b/mode/markdown/markdown.js @@ -83,9 +83,8 @@ 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 + , listRE = /^(?:[*\-+]|^[0-9]+([.)]))\s+/ + , taskListRE = /^\[(x| )\](?=\s)/ // Must follow listRE , atxHeaderRE = modeCfg.allowAtxHeaderWithoutSpace ? /^(#+)/ : /^(#+)(?: |$)/ , setextHeaderRE = /^ *(?:\={1,}|-{1,})\s*$/ , textRE = /^[^#!\[\]*_\\<>` "'(~]+/ @@ -189,14 +188,8 @@ CodeMirror.defineMode("markdown", function(cmCfg, modeCfg) { } else if (stream.match(hrRE, true)) { state.hr = true; return tokenTypes.hr; - } else if ((lineIsEmpty(state.prevLine) || prevLineIsList) && (stream.match(ulRE, false) || stream.match(olRE, false))) { - var listType = null; - if (stream.match(ulRE, true)) { - listType = 'ul'; - } else { - stream.match(olRE, true); - listType = 'ol'; - } + } else if (match = stream.match(listRE)) { + var listType = match[1] ? "ol" : "ul"; state.indentation = stream.column() + stream.current().length; state.list = true; diff --git a/mode/markdown/test.js b/mode/markdown/test.js index 2f43a170..37ecb4bb 100644 --- a/mode/markdown/test.js +++ b/mode/markdown/test.js @@ -357,11 +357,10 @@ "[variable-2 1. foo]", "[variable-2 2. bar]"); - // Lists require a preceding blank line (per Dingus) - MT("listBogus", + MT("listFromParagraph", "foo", - "1. bar", - "2. hello"); + "[variable-2 1. bar]", + "[variable-2 2. hello]"); // List after hr MT("listAfterHr", From 333a1f2bfb09151f8119b4c4de5ed26c47dba2f1 Mon Sep 17 00:00:00 2001 From: Kazuhito Hokamura Date: Wed, 9 Nov 2016 23:21:13 +0900 Subject: [PATCH 0544/1790] [vim mode] Add keymap to indent --- keymap/vim.js | 5 +++++ test/vim_test.js | 15 +++++++++++++++ 2 files changed, 20 insertions(+) diff --git a/keymap/vim.js b/keymap/vim.js index a166f72b..34570bb8 100644 --- a/keymap/vim.js +++ b/keymap/vim.js @@ -190,6 +190,8 @@ { keys: '.', type: 'action', action: 'repeatLastEdit' }, { keys: '', type: 'action', action: 'incrementNumberToken', isEdit: true, actionArgs: {increase: true, backtrack: false}}, { keys: '', type: 'action', action: 'incrementNumberToken', isEdit: true, actionArgs: {increase: false, backtrack: false}}, + { keys: '', type: 'action', action: 'indent', actionArgs: { indentRight: true }, context: 'insert' }, + { keys: '', type: 'action', action: 'indent', actionArgs: { indentRight: false }, context: 'insert' }, // Text object motions { keys: 'a', type: 'motion', motion: 'textObjectManipulation' }, { keys: 'i', type: 'motion', motion: 'textObjectManipulation', motionArgs: { textObjectInner: true }}, @@ -2616,6 +2618,9 @@ } repeatLastEdit(cm, vim, repeat, false /** repeatForInsert */); }, + indent: function(cm, actionArgs) { + cm.indentLine(cm.getCursor().line, actionArgs.indentRight); + }, exitInsertMode: exitInsertMode }; diff --git a/test/vim_test.js b/test/vim_test.js index 6eea5553..703a07a7 100644 --- a/test/vim_test.js +++ b/test/vim_test.js @@ -3393,6 +3393,21 @@ testVim('[m, ]m, [M, ]M', function(cm, vim, helpers) { helpers.assertCursorAt(7,3); }, { value: squareBracketMotionSandbox}); +testVim('i_indent_right', function(cm, vim, helpers) { + cm.setCursor(0, 3); + var expectedValue = ' word1\nword2\nword3 '; + helpers.doKeys('i', ''); + eq(expectedValue, cm.getValue()); + helpers.assertCursorAt(0, 5); +}, { value: ' word1\nword2\nword3 ', indentUnit: 2 }); +testVim('i_indent_left', function(cm, vim, helpers) { + cm.setCursor(0, 3); + var expectedValue = ' word1\nword2\nword3 '; + helpers.doKeys('i', ''); + eq(expectedValue, cm.getValue()); + helpers.assertCursorAt(0, 1); +}, { value: ' word1\nword2\nword3 ', indentUnit: 2 }); + // Ex mode tests testVim('ex_go_to_line', function(cm, vim, helpers) { cm.setCursor(0, 0); From 692393d609e4cc96a1726830ff161c3f4e56670e Mon Sep 17 00:00:00 2001 From: Marijn Haverbeke Date: Mon, 21 Nov 2016 10:49:06 +0100 Subject: [PATCH 0545/1790] Drop zero-width spaces in text read from DOM Issue #4307 --- src/input/ContentEditableInput.js | 8 ++++---- 1 file changed, 4 insertions(+), 4 deletions(-) diff --git a/src/input/ContentEditableInput.js b/src/input/ContentEditableInput.js index c385bea1..d76058ff 100644 --- a/src/input/ContentEditableInput.js +++ b/src/input/ContentEditableInput.js @@ -282,8 +282,8 @@ ContentEditableInput.prototype = copyObj({ newBot.charCodeAt(newBot.length - cutEnd - 1) == oldBot.charCodeAt(oldBot.length - cutEnd - 1)) ++cutEnd - newText[newText.length - 1] = newBot.slice(0, newBot.length - cutEnd) - newText[0] = newText[0].slice(cutFront) + newText[newText.length - 1] = newBot.slice(0, newBot.length - cutEnd).replace(/^\u200b+/, "") + newText[0] = newText[0].slice(cutFront).replace(/\u200b+$/, "") let chFrom = Pos(fromLine, cutFront) let chTo = Pos(toLine, oldText.length ? lst(oldText).length - cutEnd : 0) @@ -361,8 +361,8 @@ function domTextBetween(cm, from, to, fromLine, toLine) { if (node.nodeType == 1) { let cmText = node.getAttribute("cm-text") if (cmText != null) { - if (cmText == "") cmText = node.textContent.replace(/\u200b/g, "") - text += cmText + if (cmText == "") text += node.textContent.replace(/\u200b/g, "") + else text += cmText return } let markerID = node.getAttribute("cm-marker"), range From b63d14df7846691db1b45e47ca11409ee3540482 Mon Sep 17 00:00:00 2001 From: Marijn Haverbeke Date: Mon, 21 Nov 2016 11:12:00 +0100 Subject: [PATCH 0546/1790] Mark release 5.21.0 --- AUTHORS | 10 ++++++++++ CHANGELOG.md | 30 ++++++++++++++++++++++++++++++ doc/manual.html | 2 +- doc/releases.html | 17 +++++++++++++++++ index.html | 2 +- package.json | 2 +- src/edit/main.js | 2 +- 7 files changed, 61 insertions(+), 4 deletions(-) diff --git a/AUTHORS b/AUTHORS index e2cb74a5..09fb4cc8 100644 --- a/AUTHORS +++ b/AUTHORS @@ -10,6 +10,7 @@ adanlobato Adán Lobato Adrian Aichner Adrian Heine +Adrien Bertrand aeroson Ahmad Amireh Ahmad M. Zawawi @@ -85,6 +86,7 @@ Ben Mosher Bernhard Sirlinger Bert Chang Bharad +BigBlueHat Billy Moon binny B Krishna Chaitanya @@ -274,6 +276,7 @@ Jeff Pickhardt jem (graphite) Jeremy Parmenter Jim +Jim Avery JobJob jochenberger Jochen Berger @@ -281,6 +284,7 @@ Joel Einbinder joelpinheiro Johan Ask John Connor +John-David Dalton John Engler John Lees-Miller John Snelson @@ -311,6 +315,7 @@ jwallers@gmail.com kaniga karevn Kayur Patel +Kazuhito Hokamura Ken Newman ken restivo Ken Rockot @@ -355,6 +360,7 @@ Manideep Manuel Rego Casasnovas Marat Dreizin Marcel Gerber +Marcelo Camargo Marco Aurélio Marco Munizaga Marcus Bointon @@ -455,6 +461,7 @@ Page Panupong Pasupat paris Paris +Paris Kasidiaris Patil Arpith Patrick Stoica Patrick Strawderman @@ -506,6 +513,7 @@ Samuel Ainsworth Sam Wilson sandeepshetty Sander AKA Redsandro +Sander Verweij santec Sascha Peilicke satamas @@ -542,12 +550,14 @@ Steffen Beyer Steffen Bruchmann Stephen Lavelle Steve Champagne +Steve Hoover Steve O'Hara stoskov Stu Kennedy Sungho Kim sverweij Taha Jahangir +takamori Tako Schotanus Takuji Shimokawa Tarmil diff --git a/CHANGELOG.md b/CHANGELOG.md index a7e995ab..2404815f 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -1,3 +1,33 @@ +## 5.21.0 (2016-11-21) + +### Bug fixes + +Tapping/clicking the editor in [contentEditable mode](http://codemirror.net/doc/manual.html#option_inputStyle) on Chrome now puts the cursor at the tapped position. + +Fix various crashes and misbehaviors when reading composition events in [contentEditable mode](http://codemirror.net/doc/manual.html#option_inputStyle). + +Catches and ignores an IE 'Unspecified Error' when creating an editor in an iframe before there is a ``. + +[merge addon](http://codemirror.net/doc/manual.html#addon_merge): Fix several issues in the chunk-aligning feature. + +[verilog mode](http://codemirror.net/mode/verilog): Rewritten to address various issues. + +[julia mode](http://codemirror.net/mode/julia): Recognize Julia 0.5 syntax. + +[swift mode](http://codemirror.net/mode/swift): Various fixes and adjustments to current syntax. + +[markdown mode](http://codemirror.net/mode/markdown): Allow lists without a blank line above them. + +### New features + +The [`setGutterMarker`](http://codemirror.net/doc/manual.html#setGutterMarker), [`clearGutter`](http://codemirror.net/doc/manual.html#clearGutter), and [`lineInfo`](http://codemirror.net/doc/manual.html#lineInfo) methods are now available on `Doc` objects. + +The [`heightAtLine`](http://codemirror.net/doc/manual.html#heightAtLine) method now takes an extra argument to allow finding the height at the top of the line's line widgets. + +[ruby mode](http://codemirror.net/mode/ruby): `else` and `elsif` are now immediately indented. + +[vim bindings](http://codemirror.net/demo/vim.html): Bind Ctrl-T and Ctrl-D to in- and dedent in insert mode. + ## 5.20.2 (2016-10-21) ### Bug fixes diff --git a/doc/manual.html b/doc/manual.html index ecfe3071..be834f0f 100644 --- a/doc/manual.html +++ b/doc/manual.html @@ -69,7 +69,7 @@

      User manual and reference guide - version 5.20.3 + version 5.21.0

      CodeMirror is a code-editor component that can be embedded in diff --git a/doc/releases.html b/doc/releases.html index cfc366f3..58804691 100644 --- a/doc/releases.html +++ b/doc/releases.html @@ -30,6 +30,23 @@

      Release notes and version history

      Version 5.x

      +

      21-11-2016: Version 5.21.0:

      + +
        +
      • Tapping/clicking the editor in contentEditable mode on Chrome now puts the cursor at the tapped position.
      • +
      • Fix various crashes and misbehaviors when reading composition events in contentEditable mode.
      • +
      • Catches and ignores an IE 'Unspecified Error' when creating an editor in an iframe before there is a <body>.
      • +
      • merge addon: Fix several issues in the chunk-aligning feature.
      • +
      • verilog mode: Rewritten to address various issues.
      • +
      • julia mode: Recognize Julia 0.5 syntax.
      • +
      • swift mode: Various fixes and adjustments to current syntax.
      • +
      • markdown mode: Allow lists without a blank line above them.
      • +
      • The setGutterMarker, clearGutter, and lineInfo methods are now available on Doc objects.
      • +
      • The heightAtLine method now takes an extra argument to allow finding the height at the top of the line's line widgets.
      • +
      • ruby mode: else and elsif are now immediately indented.
      • +
      • vim bindings: Bind Ctrl-T and Ctrl-D to in- and dedent in insert mode.
      • +
      +

      20-10-2016: Version 5.20.0:

        diff --git a/index.html b/index.html index 1d1bb3c8..71642960 100644 --- a/index.html +++ b/index.html @@ -96,7 +96,7 @@

        This is CodeMirror

      - Get the current version: 5.20.2.
      + Get the current version: 5.21.0.
      You can see the code,
      read the release notes,
      or study the user manual. diff --git a/package.json b/package.json index 1d07d681..3235a47d 100644 --- a/package.json +++ b/package.json @@ -1,6 +1,6 @@ { "name": "codemirror", - "version": "5.20.3", + "version": "5.21.0", "main": "lib/codemirror.js", "description": "Full-featured in-browser code editor", "license": "MIT", diff --git a/src/edit/main.js b/src/edit/main.js index 57fcffa0..78c6da49 100644 --- a/src/edit/main.js +++ b/src/edit/main.js @@ -66,4 +66,4 @@ import { addLegacyProps } from "./legacy" addLegacyProps(CodeMirror) -CodeMirror.version = "5.20.3" +CodeMirror.version = "5.21.0" From 5fc55e8227b3c0d1d8e3178a45fbb37f6f581e48 Mon Sep 17 00:00:00 2001 From: Marijn Haverbeke Date: Mon, 21 Nov 2016 11:24:43 +0100 Subject: [PATCH 0547/1790] Bump version number post-5.21.0 --- doc/manual.html | 2 +- package.json | 2 +- src/edit/main.js | 2 +- 3 files changed, 3 insertions(+), 3 deletions(-) diff --git a/doc/manual.html b/doc/manual.html index be834f0f..05c49718 100644 --- a/doc/manual.html +++ b/doc/manual.html @@ -69,7 +69,7 @@

      User manual and reference guide - version 5.21.0 + version 5.21.1

      CodeMirror is a code-editor component that can be embedded in diff --git a/package.json b/package.json index 3235a47d..d2e45f2f 100644 --- a/package.json +++ b/package.json @@ -1,6 +1,6 @@ { "name": "codemirror", - "version": "5.21.0", + "version": "5.21.1", "main": "lib/codemirror.js", "description": "Full-featured in-browser code editor", "license": "MIT", diff --git a/src/edit/main.js b/src/edit/main.js index 78c6da49..64b647b5 100644 --- a/src/edit/main.js +++ b/src/edit/main.js @@ -66,4 +66,4 @@ import { addLegacyProps } from "./legacy" addLegacyProps(CodeMirror) -CodeMirror.version = "5.21.0" +CodeMirror.version = "5.21.1" From d0cde7f8470d6638aee972ec29108abd564598e0 Mon Sep 17 00:00:00 2001 From: Todd Berman Date: Tue, 22 Nov 2016 11:23:04 -0800 Subject: [PATCH 0548/1790] [overlay addon] Fix the `combine` option for overlay modes inside blankLines --- addon/mode/overlay.js | 9 +++++++-- 1 file changed, 7 insertions(+), 2 deletions(-) diff --git a/addon/mode/overlay.js b/addon/mode/overlay.js index e1b9ed37..4e96010a 100644 --- a/addon/mode/overlay.js +++ b/addon/mode/overlay.js @@ -76,8 +76,13 @@ CodeMirror.overlayMode = function(base, overlay, combine) { 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); + var baseToken, overlayToken; + if (base.blankLine) baseToken = base.blankLine(state.base); + if (overlay.blankLine) overlayToken = overlay.blankLine(state.overlay); + + return overlayToken == null ? + baseToken : + (combine ? baseToken + " " + overlayToken : overlayToken); } }; }; From 214b6bf63ccf3d542930a303fe96f5c8f4134365 Mon Sep 17 00:00:00 2001 From: Marijn Haverbeke Date: Wed, 23 Nov 2016 09:39:07 +0100 Subject: [PATCH 0549/1790] [overlay addon] Fix another append-null-as-string issue --- addon/mode/overlay.js | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/addon/mode/overlay.js b/addon/mode/overlay.js index 4e96010a..4a9f99a0 100644 --- a/addon/mode/overlay.js +++ b/addon/mode/overlay.js @@ -82,7 +82,7 @@ CodeMirror.overlayMode = function(base, overlay, combine) { return overlayToken == null ? baseToken : - (combine ? baseToken + " " + overlayToken : overlayToken); + (combine && baseToken != null ? baseToken + " " + overlayToken : overlayToken); } }; }; From 8bfabc472acf00eaee0d4a099f3b90d8b5dd47a8 Mon Sep 17 00:00:00 2001 From: Marijn Haverbeke Date: Wed, 23 Nov 2016 09:42:52 +0100 Subject: [PATCH 0550/1790] [commonlisp mode] Recognize character literal syntax Closes #4401 --- mode/commonlisp/commonlisp.js | 1 + 1 file changed, 1 insertion(+) diff --git a/mode/commonlisp/commonlisp.js b/mode/commonlisp/commonlisp.js index fb1f99c6..5b407a92 100644 --- a/mode/commonlisp/commonlisp.js +++ b/mode/commonlisp/commonlisp.js @@ -48,6 +48,7 @@ CodeMirror.defineMode("commonlisp", function (config) { 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 if (ch == "\\") { stream.next(); readSym(stream); return "string-2" } else return "error"; } else { var name = readSym(stream); From 959f8690d2f643ba7730cc847da0154767c29777 Mon Sep 17 00:00:00 2001 From: Adrian Heine Date: Wed, 23 Nov 2016 13:48:29 +0100 Subject: [PATCH 0551/1790] Correct bidi types for some chars --- src/util/bidi.js | 6 +++--- 1 file changed, 3 insertions(+), 3 deletions(-) diff --git a/src/util/bidi.js b/src/util/bidi.js index 4c365f4c..6812d4fd 100644 --- a/src/util/bidi.js +++ b/src/util/bidi.js @@ -121,12 +121,12 @@ export function moveLogically(line, start, dir, byUnit) { export let bidiOrdering = (function() { // Character types for codepoints 0 to 0xff let lowTypes = "bbbbbbbbbtstwsbbbbbbbbbbbbbbssstwNN%%%NNNNNN,N,N1111111111NNNNNNNLLLLLLLLLLLLLLLLLLLLLLLLLLNNNNNNLLLLLLLLLLLLLLLLLLLLLLLLLLNNNNbbbbbbsbbbbbbbbbbbbbbbbbbbbbbbbbb,N%%%%NNNNLNNNNN%%11NLNNN1LNNNNNLLLLLLLLLLLLLLLLLLLLLLLNLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLN" - // Character types for codepoints 0x600 to 0x6ff - let arabicTypes = "rrrrrrrrrrrr,rNNmmmmmmrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrmmmmmmmmmmmmmmrrrrrrrnnnnnnnnnn%nnrrrmrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrmmmmmmmmmmmmmmmmmmmNmmmm" + // Character types for codepoints 0x600 to 0x6f9 + let arabicTypes = "rrrrrrrrrrrr,rNNmmmmmmrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrmmmmmmmmmmmmmmrrrrrrrnnnnnnnnnn%nnrrrmrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrmmmmmmmmmmmmmmmrrmmNmmmmrr1111111111" function charType(code) { if (code <= 0xf7) return lowTypes.charAt(code) else if (0x590 <= code && code <= 0x5f4) return "R" - else if (0x600 <= code && code <= 0x6ed) return arabicTypes.charAt(code - 0x600) + else if (0x600 <= code && code <= 0x6f9) return arabicTypes.charAt(code - 0x600) else if (0x6ee <= code && code <= 0x8ac) return "r" else if (0x2000 <= code && code <= 0x200b) return "w" else if (code == 0x200c) return "b" From dac0b89f8cd14b4dd64d657db6e35e4c320659ac Mon Sep 17 00:00:00 2001 From: Adrian Heine Date: Thu, 24 Nov 2016 11:38:45 +0100 Subject: [PATCH 0552/1790] Correct bidi types for remaining Arabic chars --- src/util/bidi.js | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/util/bidi.js b/src/util/bidi.js index 6812d4fd..6a849140 100644 --- a/src/util/bidi.js +++ b/src/util/bidi.js @@ -122,7 +122,7 @@ export let bidiOrdering = (function() { // Character types for codepoints 0 to 0xff let lowTypes = "bbbbbbbbbtstwsbbbbbbbbbbbbbbssstwNN%%%NNNNNN,N,N1111111111NNNNNNNLLLLLLLLLLLLLLLLLLLLLLLLLLNNNNNNLLLLLLLLLLLLLLLLLLLLLLLLLLNNNNbbbbbbsbbbbbbbbbbbbbbbbbbbbbbbbbb,N%%%%NNNNLNNNNN%%11NLNNN1LNNNNNLLLLLLLLLLLLLLLLLLLLLLLNLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLN" // Character types for codepoints 0x600 to 0x6f9 - let arabicTypes = "rrrrrrrrrrrr,rNNmmmmmmrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrmmmmmmmmmmmmmmrrrrrrrnnnnnnnnnn%nnrrrmrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrmmmmmmmmmmmmmmmrrmmNmmmmrr1111111111" + let arabicTypes = "nnnnnnNNr%%r,rNNmmmmmmmmmmmrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrmmmmmmmmmmmmmmmmmmmmmnnnnnnnnnn%nnrrrmrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrmmmmmmmnNmmmmmmrrmmNmmmmrr1111111111" function charType(code) { if (code <= 0xf7) return lowTypes.charAt(code) else if (0x590 <= code && code <= 0x5f4) return "R" From 2bed274eb4287624cdc5c07762a32c4042e3b3dc Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Oskar=20Segersv=C3=A4rd?= Date: Fri, 25 Nov 2016 14:42:07 +0100 Subject: [PATCH 0553/1790] [soy mode] Extend and add tests Add template/variable definitions, checking, types, additional keywords and indentation fixes. --- mode/soy/soy.js | 123 ++++++++++++++++++++++++++++++++++++++++++----- mode/soy/test.js | 75 +++++++++++++++++++++++++++++ test/index.html | 2 + 3 files changed, 189 insertions(+), 11 deletions(-) create mode 100644 mode/soy/test.js diff --git a/mode/soy/soy.js b/mode/soy/soy.js index 580c306f..9fd75c6d 100644 --- a/mode/soy/soy.js +++ b/mode/soy/soy.js @@ -45,12 +45,40 @@ return result; } + function contains(list, element) { + while (list) { + if (list.element === element) return true; + list = list.next; + } + return false; + } + + function prepend(list, element) { + return { + element: element, + next: list + }; + } + + function pop(list) { + return list && list.next; + } + + // Reference a variable `name` in `list`. + // Let `loose` be truthy to ignore missing identifiers. + function ref(list, name, loose) { + return contains(list, name) ? "variable-2" : (loose ? "variable" : "variable-2 error"); + } + return { startState: function() { return { kind: [], kindTag: [], soyState: [], + templates: null, + variables: null, + scopes: null, indent: 0, localMode: modes.html, localState: CodeMirror.startState(modes.html) @@ -63,6 +91,9 @@ kind: state.kind.concat([]), // Values of kind="" attributes. kindTag: state.kindTag.concat([]), // Opened tags with kind="" attributes. soyState: state.soyState.concat([]), + templates: state.templates, + variables: state.variables, + scopes: state.scopes, indent: state.indent, // Indentation of the following line. localMode: state.localMode, localState: CodeMirror.copyState(state.localMode, state.localState) @@ -81,19 +112,71 @@ } return "comment"; - case "variable": - if (stream.match(/^}/)) { - state.indent -= 2 * config.indentUnit; + case "templ-def": + if (match = stream.match(/^\.?([\w]+(?!\.[\w]+)*)/)) { + state.templates = prepend(state.templates, match[1]); + state.scopes = prepend(state.scopes, state.variables); + state.soyState.pop(); + return "def"; + } + stream.next(); + return null; + + case "templ-ref": + if (match = stream.match(/^\.?([\w]+)/)) { + state.soyState.pop(); + // If the first character is '.', try to match against a local template name. + if (match[0][0] == '.') { + return ref(state.templates, match[1], true); + } + // Otherwise + return "variable"; + } + stream.next(); + return null; + + case "param-def": + if (match = stream.match(/^([\w]+)(?=:)/)) { + state.variables = prepend(state.variables, match[1]); state.soyState.pop(); - return "variable-2"; + state.soyState.push("param-type"); + return "def"; + } + stream.next(); + return null; + + case "param-type": + if (stream.peek() == "}") { + state.soyState.pop(); + return null; + } + if (stream.eatWhile(/^[\w]+/)) { + return "variable-3"; + } + stream.next(); + return null; + + case "var-def": + if (match = stream.match(/^\$([\w]+)/)) { + state.variables = prepend(state.variables, match[1]); + state.soyState.pop(); + return "def"; } stream.next(); return null; case "tag": if (stream.match(/^\/?}/)) { - if (state.tag == "/template" || state.tag == "/deltemplate") state.indent = 0; - else state.indent -= (stream.current() == "/}" || indentingTags.indexOf(state.tag) == -1 ? 2 : 1) * config.indentUnit; + if (state.tag == "/template" || state.tag == "/deltemplate") { + state.variables = state.scopes = pop(state.scopes); + state.indent = 0; + } else { + if (state.tag == "/for" || state.tag == "/foreach") { + state.variables = state.scopes = pop(state.scopes); + } + state.indent -= config.indentUnit * + (stream.current() == "/}" || indentingTags.indexOf(state.tag) == -1 ? 2 : 1); + } state.soyState.pop(); return "keyword"; } else if (stream.match(/^([\w?]+)(?==)/)) { @@ -109,6 +192,12 @@ state.soyState.push("string"); return "string"; } + if (match = stream.match(/^\$([\w]+)/)) { + return ref(state.variables, match[1]); + } + if (stream.match(/(?:as|and|or|not|in)/)) { + return "keyword"; + } stream.next(); return null; @@ -135,17 +224,13 @@ return "comment"; } else if (stream.match(stream.sol() ? /^\s*\/\/.*/ : /^\s+\/\/.*/)) { return "comment"; - } else if (stream.match(/^\{\$[\w?]*/)) { - state.indent += 2 * config.indentUnit; - state.soyState.push("variable"); - return "variable-2"; } else if (stream.match(/^\{literal}/)) { state.indent += config.indentUnit; state.soyState.push("literal"); return "keyword"; } else if (match = stream.match(/^\{([\/@\\]?[\w?]*)/)) { if (match[1] != "/switch") - state.indent += (/^(\/|(else|elseif|case|default)$)/.test(match[1]) && state.tag != "switch" ? 1 : 2) * config.indentUnit; + state.indent += (/^(\/|(else|elseif|ifempty|case|default)$)/.test(match[1]) && state.tag != "switch" ? 1 : 2) * config.indentUnit; state.tag = match[1]; if (state.tag == "/" + last(state.kindTag)) { // We found the tag that opened the current kind="". @@ -155,6 +240,22 @@ state.localState = CodeMirror.startState(state.localMode); } state.soyState.push("tag"); + if (state.tag == "template" || state.tag == "deltemplate") { + state.soyState.push("templ-def"); + } + if (state.tag == "call" || state.tag == "delcall") { + state.soyState.push("templ-ref"); + } + if (state.tag == "let") { + state.soyState.push("var-def"); + } + if (state.tag == "for" || state.tag == "foreach") { + state.scopes = prepend(state.scopes, state.variables); + state.soyState.push("var-def"); + } + if (state.tag.match(/^@param\??/)) { + state.soyState.push("param-def"); + } return "keyword"; } diff --git a/mode/soy/test.js b/mode/soy/test.js new file mode 100644 index 00000000..1a962de3 --- /dev/null +++ b/mode/soy/test.js @@ -0,0 +1,75 @@ +// CodeMirror, copyright (c) by Marijn Haverbeke and others +// Distributed under an MIT license: http://codemirror.net/LICENSE + +(function() { + var mode = CodeMirror.getMode({indentUnit: 2}, "soy"); + function MT(name) {test.mode(name, mode, Array.prototype.slice.call(arguments, 1));} + + MT('let-test', + '[keyword {template] [def .name][keyword }]', + ' [keyword {let] [def $name]: [string "world"][keyword /}]', + ' [tag&bracket <][tag h1][tag&bracket >]', + ' Hello, [keyword {][variable-2 $name][keyword }]', + ' [tag&bracket ]', + '[keyword {/template}]', + ''); + + MT('param-type-test', + '[keyword {@param] [def a]: ' + + '[variable-3 list]<[[[variable-3 a]: [variable-3 int], ' + + '[variable-3 b]: [variable-3 map]<[variable-3 string], ' + + '[variable-3 bool]>]]>][keyword }]'); + + MT('undefined-var', + '[keyword {][variable-2&error $var]'); + + MT('param-scope-test', + '[keyword {template] [def .a][keyword }]', + ' [keyword {@param] [def x]: [variable-3 string][keyword }]', + ' [keyword {][variable-2 $x][keyword }]', + '[keyword {/template}]', + '', + '[keyword {template] [def .b][keyword }]', + ' [keyword {][variable-2&error $x][keyword }]', + '[keyword {/template}]', + ''); + + MT('if-variable-test', + '[keyword {if] [variable-2&error $showThing][keyword }]', + ' Yo!', + '[keyword {/if}]', + ''); + + MT('defined-if-variable-test', + '[keyword {template] [def .foo][keyword }]', + ' [keyword {@param?] [def showThing]: [variable-3 bool][keyword }]', + ' [keyword {if] [variable-2 $showThing][keyword }]', + ' Yo!', + ' [keyword {/if}]', + '[keyword {/template}]', + ''); + + MT('template-calls-test', + '[keyword {template] [def .foo][keyword }]', + ' Yo!', + '[keyword {/template}]', + '[keyword {call] [variable-2 .foo][keyword /}]', + '[keyword {call] [variable foo][keyword /}]', + '[keyword {call] [variable .bar][keyword /}]', + '[keyword {call] [variable bar][keyword /}]', + ''); + + MT('foreach-scope-test', + '[keyword {foreach] [def $foo] [keyword in] [variable-2&error $foos][keyword }]', + ' [keyword {][variable-2 $foo][keyword }]', + '[keyword {/foreach}]', + '[keyword {][variable-2&error $foo][keyword }]'); + + MT('foreach-ifempty-indent-test', + '[keyword {foreach] [def $foo] [keyword in] [variable-2&error $foos][keyword }]', + ' something', + '[keyword {ifempty}]', + ' nothing', + '[keyword {/foreach}]', + ''); +})(); diff --git a/test/index.html b/test/index.html index cfa3bb71..6ddf5b10 100644 --- a/test/index.html +++ b/test/index.html @@ -33,6 +33,7 @@ + @@ -122,6 +123,7 @@

      Test Suite

      + From 0d296633aa4f297741a09ad8efa031589f6b2d9c Mon Sep 17 00:00:00 2001 From: coderaiser Date: Thu, 24 Nov 2016 17:38:01 +0200 Subject: [PATCH 0554/1790] [npmignore] add files that do nothing when installed with npm --- .npmignore | 2 ++ 1 file changed, 2 insertions(+) diff --git a/.npmignore b/.npmignore index 5ed053f8..de3a2408 100644 --- a/.npmignore +++ b/.npmignore @@ -8,3 +8,5 @@ /mode/*/*.html /mode/index.html .* +bin +rollup.config.js From 69159ccd6780c51526f112a9e028b46fbf6ecb42 Mon Sep 17 00:00:00 2001 From: Marijn Haverbeke Date: Sat, 3 Dec 2016 10:18:14 +0100 Subject: [PATCH 0555/1790] [sublime bindings] Make selectBetweenBrackets multi-cursor-aware Closes #4419 --- keymap/sublime.js | 24 +++++++++++++++--------- 1 file changed, 15 insertions(+), 9 deletions(-) diff --git a/keymap/sublime.js b/keymap/sublime.js index 98fce4d3..171e692e 100644 --- a/keymap/sublime.js +++ b/keymap/sublime.js @@ -166,17 +166,23 @@ var mirror = "(){}[]"; function selectBetweenBrackets(cm) { - var pos = cm.getCursor(), opening = cm.scanForBracket(pos, -1); - if (!opening) return; - for (;;) { - var closing = cm.scanForBracket(pos, 1); - if (!closing) return; - if (closing.ch == mirror.charAt(mirror.indexOf(opening.ch) + 1)) { - cm.setSelection(Pos(opening.pos.line, opening.pos.ch + 1), closing.pos, false); - return true; + var ranges = cm.listSelections(), newRanges = [] + for (var i = 0; i < ranges.length; i++) { + let range = ranges[i], pos = range.head, opening = cm.scanForBracket(pos, -1); + if (!opening) return false; + for (;;) { + var closing = cm.scanForBracket(pos, 1); + if (!closing) return false; + if (closing.ch == mirror.charAt(mirror.indexOf(opening.ch) + 1)) { + newRanges.push({anchor: Pos(opening.pos.line, opening.pos.ch + 1), + head: closing.pos}); + break; + } + pos = Pos(closing.pos.line, closing.pos.ch + 1); } - pos = Pos(closing.pos.line, closing.pos.ch + 1); } + cm.setSelections(newRanges); + return true; } cmds[map["Shift-" + ctrl + "Space"] = "selectScope"] = function(cm) { From af766c48523eb70cbf672fae6165c7612ad04e1a Mon Sep 17 00:00:00 2001 From: Marijn Haverbeke Date: Sat, 3 Dec 2016 10:22:14 +0100 Subject: [PATCH 0556/1790] Fix accidental use of 'let' --- keymap/sublime.js | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/keymap/sublime.js b/keymap/sublime.js index 171e692e..c5d2906b 100644 --- a/keymap/sublime.js +++ b/keymap/sublime.js @@ -168,7 +168,7 @@ function selectBetweenBrackets(cm) { var ranges = cm.listSelections(), newRanges = [] for (var i = 0; i < ranges.length; i++) { - let range = ranges[i], pos = range.head, opening = cm.scanForBracket(pos, -1); + var range = ranges[i], pos = range.head, opening = cm.scanForBracket(pos, -1); if (!opening) return false; for (;;) { var closing = cm.scanForBracket(pos, 1); From 5e342f21ed72f87f77f70e5ac69f111e32470704 Mon Sep 17 00:00:00 2001 From: Andrew Cheng Date: Wed, 7 Dec 2016 02:33:06 -0500 Subject: [PATCH 0557/1790] [emacs keymap] export kill, killRegion, repeated so other potential emacs-type modules can use --- keymap/emacs.js | 2 ++ 1 file changed, 2 insertions(+) diff --git a/keymap/emacs.js b/keymap/emacs.js index 3eec1e57..57cf6e85 100644 --- a/keymap/emacs.js +++ b/keymap/emacs.js @@ -271,6 +271,8 @@ clearMark(cm); } + CodeMirror.emacs = {kill: kill, killRegion: killRegion, repeated: repeated}; + // Actual keymap var keyMap = CodeMirror.keyMap.emacs = CodeMirror.normalizeKeyMap({ From 7760d1bb83f1f881834e9ee8ea780baeb01936e5 Mon Sep 17 00:00:00 2001 From: Adrian Heine Date: Fri, 9 Dec 2016 10:54:48 +0100 Subject: [PATCH 0558/1790] Add U+061C Arabic Letter Mark to special chars --- src/edit/options.js | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/edit/options.js b/src/edit/options.js index ea19d5ba..97587f73 100644 --- a/src/edit/options.js +++ b/src/edit/options.js @@ -68,7 +68,7 @@ export function defineOptions(CodeMirror) { for (let i = newBreaks.length - 1; i >= 0; i--) replaceRange(cm.doc, val, newBreaks[i], Pos(newBreaks[i].line, newBreaks[i].ch + val.length)) }) - option("specialChars", /[\u0000-\u001f\u007f\u00ad\u200b-\u200f\u2028\u2029\ufeff]/g, (cm, val, old) => { + option("specialChars", /[\u0000-\u001f\u007f\u00ad\u061c\u200b-\u200f\u2028\u2029\ufeff]/g, (cm, val, old) => { cm.state.specialChars = new RegExp(val.source + (val.test("\t") ? "" : "|\t"), "g") if (old != Init) cm.refresh() }) From 41804498097a446ee55390f2f90d27804d371c9f Mon Sep 17 00:00:00 2001 From: Tom Klancer Date: Wed, 30 Nov 2016 17:47:38 -0500 Subject: [PATCH 0559/1790] [active-line addon] Highlight active line even when text is selected Adds an option to keep the active line highlighted even when text inside the line is selected. --- addon/selection/active-line.js | 16 ++++++++++++---- demo/activeline.html | 14 +++++++++++++- doc/manual.html | 26 ++++++++++++++++++++------ 3 files changed, 45 insertions(+), 11 deletions(-) diff --git a/addon/selection/active-line.js b/addon/selection/active-line.js index b0b3f61a..68db3f4d 100644 --- a/addon/selection/active-line.js +++ b/addon/selection/active-line.js @@ -3,9 +3,13 @@ // 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". +// 'styleActiveLine': when enabled, gives the active line's wrapping +//
      the CSS class "CodeMirror-activeline", and gives its background +//
      the class "CodeMirror-activeline-background". +// +// 'styleActiveSelected': An optional parameter of 'styleActiveLine'. +// When enabled, keeps the active line's styling active even when text is +// selected within the line. Has no effect if 'styleActiveLine' is not enabled. (function(mod) { if (typeof exports == "object" && typeof module == "object") // CommonJS @@ -52,7 +56,11 @@ var active = []; for (var i = 0; i < ranges.length; i++) { var range = ranges[i]; - if (!range.empty()) continue; + if (cm.getOption('styleActiveLine').styleActiveSelected == true) { + if (range.anchor.line != range.head.line) continue; + } else { + if (!range.empty()) continue; + } var line = cm.getLineHandleVisualStart(range.head.line); if (active[active.length - 1] != line) active.push(line); } diff --git a/demo/activeline.html b/demo/activeline.html index 741f6c45..7a273a77 100644 --- a/demo/activeline.html +++ b/demo/activeline.html @@ -65,14 +65,26 @@

      Active Line Demo

      Styling the current cursor line.

      + +
    diff --git a/doc/manual.html b/doc/manual.html index 05c49718..b5b451ae 100644 --- a/doc/manual.html +++ b/doc/manual.html @@ -2760,12 +2760,26 @@

    Addons

    like in this demo.
    selection/active-line.js
    -
    Defines a styleActiveLine option that, when enabled, - gives the wrapper of the active line the class CodeMirror-activeline, - adds a background with the class CodeMirror-activeline-background, - and adds the class CodeMirror-activeline-gutter to the - line's gutter space is enabled. See the - demo.
    +
    Controls highlighting of the active line. +
    Defines an option + styleActiveLine which, when enabled, gives the wrapper of + the active line the class CodeMirror-activeline, adds a + background with the class CodeMirror-activeline-background, + and adds the class CodeMirror-activeline-gutter to the + line's gutter space. +
    +
    + In addition, defines an option styleActiveSelected, + that controls highlighting behavior when selected. + styleActiveSelected is an optional parameter of + styleActiveLine. If true, + the active line will remain highlighted when text within the line is + selected. If false or unspecified, the active line will + become unhighlighted as soon as text is selected. Has no effect if + styleActiveLine is not enabled. +
    +
    See the demo.
    +
    selection/selection-pointer.js
    Defines a selectionPointer option which you can From 33d0057f1edbd8e48726bb357ba960645161a8b1 Mon Sep 17 00:00:00 2001 From: Marijn Haverbeke Date: Tue, 13 Dec 2016 10:53:22 +0100 Subject: [PATCH 0560/1790] [active-line addon] Rename and clean up nonEmpty options Issue #4413 --- addon/selection/active-line.js | 32 +++++++++++--------------------- demo/activeline.html | 14 ++++++-------- doc/manual.html | 34 ++++++++++++++-------------------- 3 files changed, 31 insertions(+), 49 deletions(-) diff --git a/addon/selection/active-line.js b/addon/selection/active-line.js index 68db3f4d..aa295d0d 100644 --- a/addon/selection/active-line.js +++ b/addon/selection/active-line.js @@ -1,16 +1,6 @@ // CodeMirror, 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. -// -// 'styleActiveLine': when enabled, gives the active line's wrapping -//
    the CSS class "CodeMirror-activeline", and gives its background -//
    the class "CodeMirror-activeline-background". -// -// 'styleActiveSelected': An optional parameter of 'styleActiveLine'. -// When enabled, keeps the active line's styling active even when text is -// selected within the line. Has no effect if 'styleActiveLine' is not enabled. - (function(mod) { if (typeof exports == "object" && typeof module == "object") // CommonJS mod(require("../../lib/codemirror")); @@ -25,16 +15,18 @@ var GUTT_CLASS = "CodeMirror-activeline-gutter"; CodeMirror.defineOption("styleActiveLine", false, function(cm, val, old) { - var prev = old && old != CodeMirror.Init; - if (val && !prev) { - cm.state.activeLines = []; - updateActiveLines(cm, cm.listSelections()); - cm.on("beforeSelectionChange", selectionChange); - } else if (!val && prev) { + var prev = old == CodeMirror.Init ? false : old; + if (val == prev) return + if (prev) { cm.off("beforeSelectionChange", selectionChange); clearActiveLines(cm); delete cm.state.activeLines; } + if (val) { + cm.state.activeLines = []; + updateActiveLines(cm, cm.listSelections()); + cm.on("beforeSelectionChange", selectionChange); + } }); function clearActiveLines(cm) { @@ -56,11 +48,9 @@ var active = []; for (var i = 0; i < ranges.length; i++) { var range = ranges[i]; - if (cm.getOption('styleActiveLine').styleActiveSelected == true) { - if (range.anchor.line != range.head.line) continue; - } else { - if (!range.empty()) continue; - } + var option = cm.getOption("styleActiveLine"); + if (typeof option == "object" && option.nonEmpty ? range.anchor.line != range.head.line : !range.empty()) + continue var line = cm.getLineHandleVisualStart(range.head.line); if (active[active.length - 1] != line) active.push(line); } diff --git a/demo/activeline.html b/demo/activeline.html index 7a273a77..86c8c18e 100644 --- a/demo/activeline.html +++ b/demo/activeline.html @@ -65,26 +65,24 @@

    Active Line Demo

    Styling the current cursor line.

    - + diff --git a/doc/manual.html b/doc/manual.html index b5b451ae..00657908 100644 --- a/doc/manual.html +++ b/doc/manual.html @@ -2760,26 +2760,20 @@

    Addons

    like in this demo.
    selection/active-line.js
    -
    Controls highlighting of the active line. -
    Defines an option - styleActiveLine which, when enabled, gives the wrapper of - the active line the class CodeMirror-activeline, adds a - background with the class CodeMirror-activeline-background, - and adds the class CodeMirror-activeline-gutter to the - line's gutter space. -
    -
    - In addition, defines an option styleActiveSelected, - that controls highlighting behavior when selected. - styleActiveSelected is an optional parameter of - styleActiveLine. If true, - the active line will remain highlighted when text within the line is - selected. If false or unspecified, the active line will - become unhighlighted as soon as text is selected. Has no effect if - styleActiveLine is not enabled. -
    -
    See the demo.
    - +
    Defines a styleActiveLine option that, when + enabled, gives the wrapper of the line that contains the cursor + the class CodeMirror-activeline, adds a background + with the class CodeMirror-activeline-background, + and adds the class CodeMirror-activeline-gutter to + the line's gutter space is enabled. The option's value may be a + boolean or an object specifying the following options: +
    +
    nonEmpty: bool
    +
    Controls whether single-line selections, or just cursor + selections, are styled. Defaults to false (only cursor + selections).
    +
    + See the demo.
    selection/selection-pointer.js
    Defines a selectionPointer option which you can From 460452d73c3a6100662f33bf675e8eca07becaa0 Mon Sep 17 00:00:00 2001 From: Marijn Haverbeke Date: Tue, 13 Dec 2016 23:05:37 +0100 Subject: [PATCH 0561/1790] =?UTF-8?q?Upgrade=20Bubl=C3=A9,=20use=20namedFu?= =?UTF-8?q?nctionExpressions=20option?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- package.json | 2 +- rollup.config.js | 2 +- 2 files changed, 2 insertions(+), 2 deletions(-) diff --git a/package.json b/package.json index d2e45f2f..e2b97a9c 100644 --- a/package.json +++ b/package.json @@ -19,7 +19,7 @@ "node-static": "0.6.0", "phantomjs-prebuilt": "^2.1.12", "rollup": "^0.34.10", - "rollup-plugin-buble": "^0.14.0", + "rollup-plugin-buble": "^0.15.0", "rollup-watch": "^2.5.0" }, "bugs": "http://github.com/codemirror/CodeMirror/issues", diff --git a/rollup.config.js b/rollup.config.js index 584dfe1e..9a17b24f 100644 --- a/rollup.config.js +++ b/rollup.config.js @@ -14,5 +14,5 @@ export default { format: "umd", dest: "lib/codemirror.js", moduleName: "CodeMirror", - plugins: [ buble() ] + plugins: [ buble({namedFunctionExpressions: false}) ] }; From 957c28f4d8c41afe8b0e0d6ec3a7a454590761ce Mon Sep 17 00:00:00 2001 From: Marijn Haverbeke Date: Wed, 14 Dec 2016 08:26:07 +0100 Subject: [PATCH 0562/1790] [javascript mode] Accept strings and numbers as type expressions Closes #4432 --- mode/javascript/javascript.js | 2 ++ 1 file changed, 2 insertions(+) diff --git a/mode/javascript/javascript.js b/mode/javascript/javascript.js index a7177458..b9f39259 100644 --- a/mode/javascript/javascript.js +++ b/mode/javascript/javascript.js @@ -539,6 +539,7 @@ CodeMirror.defineMode("javascript", function(config, parserConfig) { } function typeexpr(type) { if (type == "variable") {cx.marked = "variable-3"; return cont(afterType);} + if (type == "string" || type == "number") return cont(afterType); if (type == "{") return cont(commasep(typeprop, "}")) if (type == "(") return cont(commasep(typearg, ")"), maybeReturnType) } @@ -559,6 +560,7 @@ CodeMirror.defineMode("javascript", function(config, parserConfig) { } function afterType(type, value) { if (value == "<") return cont(commasep(typeexpr, ">"), afterType) + if (value == "|") return cont(typeexpr) if (type == "[") return cont(expect("]"), afterType) } function vardef() { From 70ea4303bc4efcdb7ef1956bb6123667440f0a19 Mon Sep 17 00:00:00 2001 From: Marijn Haverbeke Date: Wed, 14 Dec 2016 18:49:33 +0100 Subject: [PATCH 0563/1790] [source-highlight util] Fix looking up of modes --- bin/source-highlight | 13 +++++-------- 1 file changed, 5 insertions(+), 8 deletions(-) diff --git a/bin/source-highlight b/bin/source-highlight index 6d15f1ae..0d6239c2 100755 --- a/bin/source-highlight +++ b/bin/source-highlight @@ -17,14 +17,11 @@ if (sPos == -1 || sPos == process.argv.length - 1) { process.exit(1); } var lang = process.argv[sPos + 1].toLowerCase(), modeName = lang; -CodeMirror.modeInfo.forEach(function(info) { - if (info.mime == lang) { - modeName = info.mode; - } else if (info.name.toLowerCase() == lang) { - modeName = info.mode; - lang = info.mime; - } -}); +var found = CodeMirror.findModeByMIME(lang) || CodeMirror.findModeByName(lang) +if (found) { + modeName = found.mode + lang = found.mime +} if (!CodeMirror.modes[modeName]) require("../mode/" + modeName + "/" + modeName + ".js"); From c2a11a315ebe95478134e9eb94c040f14021df4f Mon Sep 17 00:00:00 2001 From: ficristo Date: Wed, 14 Dec 2016 20:35:27 +0100 Subject: [PATCH 0564/1790] [css mode] Add will-change property and its values --- mode/css/css.js | 12 ++++++------ mode/stylus/stylus.js | 4 ++-- 2 files changed, 8 insertions(+), 8 deletions(-) diff --git a/mode/css/css.js b/mode/css/css.js index 985287f4..a1d5a388 100644 --- a/mode/css/css.js +++ b/mode/css/css.js @@ -524,7 +524,7 @@ CodeMirror.defineMode("css", function(config, parserConfig) { "transition-property", "transition-timing-function", "unicode-bidi", "user-select", "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", + "voice-volume", "volume", "white-space", "widows", "width", "will-change", "word-break", "word-spacing", "word-wrap", "z-index", // SVG-specific "clip-path", "clip-rule", "mask", "enable-background", "filter", "flood-color", @@ -598,7 +598,7 @@ CodeMirror.defineMode("css", function(config, parserConfig) { "cell", "center", "checkbox", "circle", "cjk-decimal", "cjk-earthly-branch", "cjk-heavenly-stem", "cjk-ideographic", "clear", "clip", "close-quote", "col-resize", "collapse", "color", "color-burn", "color-dodge", "column", "column-reverse", - "compact", "condensed", "contain", "content", + "compact", "condensed", "contain", "content", "contents", "content-box", "context-menu", "continuous", "copy", "counter", "counters", "cover", "crop", "cross", "crosshair", "currentcolor", "cursive", "cyclic", "darken", "dashed", "decimal", "decimal-leading-zero", "default", "default-button", "dense", "destination-atop", @@ -641,7 +641,7 @@ CodeMirror.defineMode("css", function(config, parserConfig) { "mix", "mongolian", "monospace", "move", "multiple", "multiply", "myanmar", "n-resize", "narrower", "ne-resize", "nesw-resize", "no-close-quote", "no-drop", "no-open-quote", "no-repeat", "none", "normal", "not-allowed", "nowrap", - "ns-resize", "numbers", "numeric", "nw-resize", "nwse-resize", "oblique", "octal", "open-quote", + "ns-resize", "numbers", "numeric", "nw-resize", "nwse-resize", "oblique", "octal", "opacity", "open-quote", "optimizeLegibility", "optimizeSpeed", "oriya", "oromo", "outset", "outside", "outside-shape", "overlay", "overline", "padding", "padding-box", "painted", "page", "paused", "persian", "perspective", "plus-darker", "plus-lighter", @@ -653,7 +653,7 @@ CodeMirror.defineMode("css", function(config, parserConfig) { "rgb", "rgba", "ridge", "right", "rotate", "rotate3d", "rotateX", "rotateY", "rotateZ", "round", "row", "row-resize", "row-reverse", "rtl", "run-in", "running", "s-resize", "sans-serif", "saturation", "scale", "scale3d", "scaleX", "scaleY", "scaleZ", "screen", - "scroll", "scrollbar", "se-resize", "searchfield", + "scroll", "scrollbar", "scroll-position", "se-resize", "searchfield", "searchfield-cancel-button", "searchfield-decoration", "searchfield-results-button", "searchfield-results-decoration", "semi-condensed", "semi-expanded", "separate", "serif", "show", "sidama", @@ -671,9 +671,9 @@ CodeMirror.defineMode("css", function(config, parserConfig) { "thick", "thin", "threeddarkshadow", "threedface", "threedhighlight", "threedlightshadow", "threedshadow", "tibetan", "tigre", "tigrinya-er", "tigrinya-er-abegede", "tigrinya-et", "tigrinya-et-abegede", "to", "top", - "trad-chinese-formal", "trad-chinese-informal", + "trad-chinese-formal", "trad-chinese-informal", "transform", "translate", "translate3d", "translateX", "translateY", "translateZ", - "transparent", "ultra-condensed", "ultra-expanded", "underline", "up", + "transparent", "ultra-condensed", "ultra-expanded", "underline", "unset", "up", "upper-alpha", "upper-armenian", "upper-greek", "upper-hexadecimal", "upper-latin", "upper-norwegian", "upper-roman", "uppercase", "urdu", "url", "var", "vertical", "vertical-text", "visible", "visibleFill", "visiblePainted", diff --git a/mode/stylus/stylus.js b/mode/stylus/stylus.js index 662cd03c..8d83a018 100644 --- a/mode/stylus/stylus.js +++ b/mode/stylus/stylus.js @@ -732,11 +732,11 @@ var documentTypes_ = ["domain", "regexp", "url", "url-prefix"]; var mediaTypes_ = ["all","aural","braille","handheld","print","projection","screen","tty","tv","embossed"]; var mediaFeatures_ = ["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-fill-mode","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","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","font-weight","grid","grid-area","grid-auto-columns","grid-auto-flow","grid-auto-position","grid-auto-rows","grid-column","grid-column-end","grid-column-start","grid-row","grid-row-end","grid-row-start","grid-template","grid-template-areas","grid-template-columns","grid-template-rows","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","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","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","region-break-after","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-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","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-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","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-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","text-anchor","writing-mode","font-smoothing","osx-font-smoothing"]; + var propertyKeywords_ = ["align-content","align-items","align-self","alignment-adjust","alignment-baseline","anchor-point","animation","animation-delay","animation-direction","animation-duration","animation-fill-mode","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","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","font-weight","grid","grid-area","grid-auto-columns","grid-auto-flow","grid-auto-position","grid-auto-rows","grid-column","grid-column-end","grid-column-start","grid-row","grid-row-end","grid-row-start","grid-template","grid-template-areas","grid-template-columns","grid-template-rows","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","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","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","region-break-after","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-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","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-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","will-change","word-break","word-spacing","word-wrap","z-index","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-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","text-anchor","writing-mode","font-smoothing","osx-font-smoothing"]; var nonStandardPropertyKeywords_ = ["scrollbar-arrow-color","scrollbar-base-color","scrollbar-dark-shadow-color","scrollbar-face-color","scrollbar-highlight-color","scrollbar-shadow-color","scrollbar-3d-light-color","scrollbar-track-color","shape-inside","searchfield-cancel-button","searchfield-decoration","searchfield-results-button","searchfield-results-decoration","zoom"]; var fontProperties_ = ["font-family","src","unicode-range","font-variant","font-feature-settings","font-stretch","font-weight","font-style"]; var colorKeywords_ = ["aliceblue","antiquewhite","aqua","aquamarine","azure","beige","bisque","black","blanchedalmond","blue","blueviolet","brown","burlywood","cadetblue","chartreuse","chocolate","coral","cornflowerblue","cornsilk","crimson","cyan","darkblue","darkcyan","darkgoldenrod","darkgray","darkgreen","darkkhaki","darkmagenta","darkolivegreen","darkorange","darkorchid","darkred","darksalmon","darkseagreen","darkslateblue","darkslategray","darkturquoise","darkviolet","deeppink","deepskyblue","dimgray","dodgerblue","firebrick","floralwhite","forestgreen","fuchsia","gainsboro","ghostwhite","gold","goldenrod","gray","grey","green","greenyellow","honeydew","hotpink","indianred","indigo","ivory","khaki","lavender","lavenderblush","lawngreen","lemonchiffon","lightblue","lightcoral","lightcyan","lightgoldenrodyellow","lightgray","lightgreen","lightpink","lightsalmon","lightseagreen","lightskyblue","lightslategray","lightsteelblue","lightyellow","lime","limegreen","linen","magenta","maroon","mediumaquamarine","mediumblue","mediumorchid","mediumpurple","mediumseagreen","mediumslateblue","mediumspringgreen","mediumturquoise","mediumvioletred","midnightblue","mintcream","mistyrose","moccasin","navajowhite","navy","oldlace","olive","olivedrab","orange","orangered","orchid","palegoldenrod","palegreen","paleturquoise","palevioletred","papayawhip","peachpuff","peru","pink","plum","powderblue","purple","rebeccapurple","red","rosybrown","royalblue","saddlebrown","salmon","sandybrown","seagreen","seashell","sienna","silver","skyblue","slateblue","slategray","snow","springgreen","steelblue","tan","teal","thistle","tomato","turquoise","violet","wheat","white","whitesmoke","yellow","yellowgreen"]; - var valueKeywords_ = ["above","absolute","activeborder","additive","activecaption","afar","after-white-space","ahead","alias","all","all-scroll","alphabetic","alternate","always","amharic","amharic-abegede","antialiased","appworkspace","arabic-indic","armenian","asterisks","attr","auto","avoid","avoid-column","avoid-page","avoid-region","background","backwards","baseline","below","bidi-override","binary","bengali","blink","block","block-axis","bold","bolder","border","border-box","both","bottom","break","break-all","break-word","bullets","button","button-bevel","buttonface","buttonhighlight","buttonshadow","buttontext","calc","cambodian","capitalize","caps-lock-indicator","caption","captiontext","caret","cell","center","checkbox","circle","cjk-decimal","cjk-earthly-branch","cjk-heavenly-stem","cjk-ideographic","clear","clip","close-quote","col-resize","collapse","column","compact","condensed","contain","content","content-box","context-menu","continuous","copy","counter","counters","cover","crop","cross","crosshair","currentcolor","cursive","cyclic","dashed","decimal","decimal-leading-zero","default","default-button","destination-atop","destination-in","destination-out","destination-over","devanagari","disc","discard","disclosure-closed","disclosure-open","document","dot-dash","dot-dot-dash","dotted","double","down","e-resize","ease","ease-in","ease-in-out","ease-out","element","ellipse","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","ethiopic-numeric","ew-resize","expanded","extends","extra-condensed","extra-expanded","fantasy","fast","fill","fixed","flat","flex","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-flex","inline-table","inset","inside","intrinsic","invert","italic","japanese-formal","japanese-informal","justify","kannada","katakana","katakana-iroha","keep-all","khmer","korean-hangul-formal","korean-hanja-formal","korean-hanja-informal","landscape","lao","large","larger","left","level","lighter","line-through","linear","linear-gradient","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","matrix","matrix3d","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","ne-resize","nesw-resize","no-close-quote","no-drop","no-open-quote","no-repeat","none","normal","not-allowed","nowrap","ns-resize","numbers","numeric","nw-resize","nwse-resize","oblique","octal","open-quote","optimizeLegibility","optimizeSpeed","oriya","oromo","outset","outside","outside-shape","overlay","overline","padding","padding-box","painted","page","paused","persian","perspective","plus-darker","plus-lighter","pointer","polygon","portrait","pre","pre-line","pre-wrap","preserve-3d","progress","push-button","radial-gradient","radio","read-only","read-write","read-write-plaintext-only","rectangle","region","relative","repeat","repeating-linear-gradient","repeating-radial-gradient","repeat-x","repeat-y","reset","reverse","rgb","rgba","ridge","right","rotate","rotate3d","rotateX","rotateY","rotateZ","round","row-resize","rtl","run-in","running","s-resize","sans-serif","scale","scale3d","scaleX","scaleY","scaleZ","scroll","scrollbar","se-resize","searchfield","searchfield-cancel-button","searchfield-decoration","searchfield-results-button","searchfield-results-decoration","semi-condensed","semi-expanded","separate","serif","show","sidama","simp-chinese-formal","simp-chinese-informal","single","skew","skewX","skewY","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","spell-out","square","square-button","start","static","status-bar","stretch","stroke","sub","subpixel-antialiased","super","sw-resize","symbolic","symbols","table","table-caption","table-cell","table-column","table-column-group","table-footer-group","table-header-group","table-row","table-row-group","tamil","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","trad-chinese-formal","trad-chinese-informal","translate","translate3d","translateX","translateY","translateZ","transparent","ultra-condensed","ultra-expanded","underline","up","upper-alpha","upper-armenian","upper-greek","upper-hexadecimal","upper-latin","upper-norwegian","upper-roman","uppercase","urdu","url","var","vertical","vertical-text","visible","visibleFill","visiblePainted","visibleStroke","visual","w-resize","wait","wave","wider","window","windowframe","windowtext","words","x-large","x-small","xor","xx-large","xx-small","bicubic","optimizespeed","grayscale","row","row-reverse","wrap","wrap-reverse","column-reverse","flex-start","flex-end","space-between","space-around"]; + var valueKeywords_ = ["above","absolute","activeborder","additive","activecaption","afar","after-white-space","ahead","alias","all","all-scroll","alphabetic","alternate","always","amharic","amharic-abegede","antialiased","appworkspace","arabic-indic","armenian","asterisks","attr","auto","avoid","avoid-column","avoid-page","avoid-region","background","backwards","baseline","below","bidi-override","binary","bengali","blink","block","block-axis","bold","bolder","border","border-box","both","bottom","break","break-all","break-word","bullets","button","button-bevel","buttonface","buttonhighlight","buttonshadow","buttontext","calc","cambodian","capitalize","caps-lock-indicator","caption","captiontext","caret","cell","center","checkbox","circle","cjk-decimal","cjk-earthly-branch","cjk-heavenly-stem","cjk-ideographic","clear","clip","close-quote","col-resize","collapse","column","compact","condensed","contain","content","contents","content-box","context-menu","continuous","copy","counter","counters","cover","crop","cross","crosshair","currentcolor","cursive","cyclic","dashed","decimal","decimal-leading-zero","default","default-button","destination-atop","destination-in","destination-out","destination-over","devanagari","disc","discard","disclosure-closed","disclosure-open","document","dot-dash","dot-dot-dash","dotted","double","down","e-resize","ease","ease-in","ease-in-out","ease-out","element","ellipse","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","ethiopic-numeric","ew-resize","expanded","extends","extra-condensed","extra-expanded","fantasy","fast","fill","fixed","flat","flex","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-flex","inline-table","inset","inside","intrinsic","invert","italic","japanese-formal","japanese-informal","justify","kannada","katakana","katakana-iroha","keep-all","khmer","korean-hangul-formal","korean-hanja-formal","korean-hanja-informal","landscape","lao","large","larger","left","level","lighter","line-through","linear","linear-gradient","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","matrix","matrix3d","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","ne-resize","nesw-resize","no-close-quote","no-drop","no-open-quote","no-repeat","none","normal","not-allowed","nowrap","ns-resize","numbers","numeric","nw-resize","nwse-resize","oblique","octal","open-quote","optimizeLegibility","optimizeSpeed","oriya","oromo","outset","outside","outside-shape","overlay","overline","padding","padding-box","painted","page","paused","persian","perspective","plus-darker","plus-lighter","pointer","polygon","portrait","pre","pre-line","pre-wrap","preserve-3d","progress","push-button","radial-gradient","radio","read-only","read-write","read-write-plaintext-only","rectangle","region","relative","repeat","repeating-linear-gradient","repeating-radial-gradient","repeat-x","repeat-y","reset","reverse","rgb","rgba","ridge","right","rotate","rotate3d","rotateX","rotateY","rotateZ","round","row-resize","rtl","run-in","running","s-resize","sans-serif","scale","scale3d","scaleX","scaleY","scaleZ","scroll","scrollbar","scroll-position","se-resize","searchfield","searchfield-cancel-button","searchfield-decoration","searchfield-results-button","searchfield-results-decoration","semi-condensed","semi-expanded","separate","serif","show","sidama","simp-chinese-formal","simp-chinese-informal","single","skew","skewX","skewY","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","spell-out","square","square-button","start","static","status-bar","stretch","stroke","sub","subpixel-antialiased","super","sw-resize","symbolic","symbols","table","table-caption","table-cell","table-column","table-column-group","table-footer-group","table-header-group","table-row","table-row-group","tamil","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","trad-chinese-formal","trad-chinese-informal","translate","translate3d","translateX","translateY","translateZ","transparent","ultra-condensed","ultra-expanded","underline","up","upper-alpha","upper-armenian","upper-greek","upper-hexadecimal","upper-latin","upper-norwegian","upper-roman","uppercase","urdu","url","var","vertical","vertical-text","visible","visibleFill","visiblePainted","visibleStroke","visual","w-resize","wait","wave","wider","window","windowframe","windowtext","words","x-large","x-small","xor","xx-large","xx-small","bicubic","optimizespeed","grayscale","row","row-reverse","wrap","wrap-reverse","column-reverse","flex-start","flex-end","space-between","space-around", "unset"]; var wordOperatorKeywords_ = ["in","and","or","not","is not","is a","is","isnt","defined","if unless"], blockKeywords_ = ["for","if","else","unless", "from", "to"], From 897bb77e55846cfefca7d84ede99f83cf4b2747e Mon Sep 17 00:00:00 2001 From: Marijn Haverbeke Date: Thu, 15 Dec 2016 10:44:28 +0100 Subject: [PATCH 0565/1790] [javascript mode] Recognize TS bool literal types and type names with dots Closes #4437 --- 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 b9f39259..2ad7a1e9 100644 --- a/mode/javascript/javascript.js +++ b/mode/javascript/javascript.js @@ -539,7 +539,7 @@ CodeMirror.defineMode("javascript", function(config, parserConfig) { } function typeexpr(type) { if (type == "variable") {cx.marked = "variable-3"; return cont(afterType);} - if (type == "string" || type == "number") return cont(afterType); + if (type == "string" || type == "number" || type == "atom") return cont(afterType); if (type == "{") return cont(commasep(typeprop, "}")) if (type == "(") return cont(commasep(typearg, ")"), maybeReturnType) } @@ -560,7 +560,7 @@ CodeMirror.defineMode("javascript", function(config, parserConfig) { } function afterType(type, value) { if (value == "<") return cont(commasep(typeexpr, ">"), afterType) - if (value == "|") return cont(typeexpr) + if (value == "|" || type == ".") return cont(typeexpr) if (type == "[") return cont(expect("]"), afterType) } function vardef() { From b0d8dd4f53fa88d1e6447cf2d759159a53f84229 Mon Sep 17 00:00:00 2001 From: Rishi Goomar Date: Thu, 1 Dec 2016 09:40:09 -0600 Subject: [PATCH 0566/1790] [mode/meta] Allow for syntax highlighting on ".R" files --- mode/meta.js | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/mode/meta.js b/mode/meta.js index 8faf7677..47364448 100644 --- a/mode/meta.js +++ b/mode/meta.js @@ -109,7 +109,7 @@ {name: "Python", mime: "text/x-python", mode: "python", ext: ["BUILD", "bzl", "py", "pyw"], file: /^(BUCK|BUILD)$/}, {name: "Puppet", mime: "text/x-puppet", mode: "puppet", ext: ["pp"]}, {name: "Q", mime: "text/x-q", mode: "q", ext: ["q"]}, - {name: "R", mime: "text/x-rsrc", mode: "r", ext: ["r"], alias: ["rscript"]}, + {name: "R", mime: "text/x-rsrc", mode: "r", ext: ["r", "R"], alias: ["rscript"]}, {name: "reStructuredText", mime: "text/x-rst", mode: "rst", ext: ["rst"], alias: ["rst"]}, {name: "RPM Changes", mime: "text/x-rpm-changes", mode: "rpm"}, {name: "RPM Spec", mime: "text/x-rpm-spec", mode: "rpm", ext: ["spec"]}, From e157e82a86cf1464feb21d81c66218c6d14f6435 Mon Sep 17 00:00:00 2001 From: Marijn Haverbeke Date: Thu, 15 Dec 2016 11:51:20 +0100 Subject: [PATCH 0567/1790] Add optionChange event Issue #4417 --- doc/manual.html | 3 +++ src/edit/methods.js | 1 + 2 files changed, 4 insertions(+) diff --git a/doc/manual.html b/doc/manual.html index 00657908..c6745e3c 100644 --- a/doc/manual.html +++ b/doc/manual.html @@ -639,6 +639,9 @@

    Events

    or resized. Mostly useful to invalidate cached values that depend on the editor or character size.
    +
    "optionChange" (instance: CodeMirror, option: string)
    +
    Dispatched every time an option is changed with setOption.
    +
    "scrollCursorIntoView" (instance: CodeMirror, event: Event)
    Fires when the editor tries to scroll its cursor into view. Can be hooked into to take care of additional scrollable diff --git a/src/edit/methods.js b/src/edit/methods.js index 7efaf20e..144e4773 100644 --- a/src/edit/methods.js +++ b/src/edit/methods.js @@ -45,6 +45,7 @@ export default function(CodeMirror) { options[option] = value if (optionHandlers.hasOwnProperty(option)) operation(this, optionHandlers[option])(this, value, old) + signal(this, "optionChange", this, option) }, getOption: function(option) {return this.options[option]}, From 35ec5ee2169aae3b0efe8ca437e063833c80f5d9 Mon Sep 17 00:00:00 2001 From: callodacity Date: Sat, 10 Dec 2016 11:11:19 +1100 Subject: [PATCH 0568/1790] [markdown mode] Improve markdown image lookahead --- mode/markdown/markdown.js | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/mode/markdown/markdown.js b/mode/markdown/markdown.js index 6aedc360..86c017c3 100644 --- a/mode/markdown/markdown.js +++ b/mode/markdown/markdown.js @@ -436,7 +436,7 @@ CodeMirror.defineMode("markdown", function(cmCfg, modeCfg) { return getType(state); } - if (ch === '[' && state.imageMarker) { + if (ch === '[' && stream.match(/[^\]]*\](\(.*\)| ?\[.*?\])/, false) && state.imageMarker) { state.imageMarker = false; state.imageAltText = true if (modeCfg.highlightFormatting) state.formatting = "image"; From 1b9056f861f28d5baed07718eacae9988d0fd266 Mon Sep 17 00:00:00 2001 From: Marijn Haverbeke Date: Thu, 15 Dec 2016 12:07:22 +0100 Subject: [PATCH 0569/1790] [markdown mode] Make image lookahead a little cheaper Issue #4426 --- mode/markdown/markdown.js | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/mode/markdown/markdown.js b/mode/markdown/markdown.js index 86c017c3..4cc1dc68 100644 --- a/mode/markdown/markdown.js +++ b/mode/markdown/markdown.js @@ -436,7 +436,7 @@ CodeMirror.defineMode("markdown", function(cmCfg, modeCfg) { return getType(state); } - if (ch === '[' && stream.match(/[^\]]*\](\(.*\)| ?\[.*?\])/, false) && state.imageMarker) { + if (ch === '[' && state.imageMarker && stream.match(/[^\]]*\](\(.*?\)| ?\[.*?\])/, false)) { state.imageMarker = false; state.imageAltText = true if (modeCfg.highlightFormatting) state.formatting = "image"; From 12bece3ae4cb814344d1cf3c786f2dc0e7026ad5 Mon Sep 17 00:00:00 2001 From: Marijn Haverbeke Date: Thu, 15 Dec 2016 12:18:44 +0100 Subject: [PATCH 0570/1790] Remove timeout kludge in guttersChanged Closes #4412 --- src/edit/options.js | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/edit/options.js b/src/edit/options.js index 97587f73..dffc577b 100644 --- a/src/edit/options.js +++ b/src/edit/options.js @@ -158,7 +158,7 @@ export function defineOptions(CodeMirror) { function guttersChanged(cm) { updateGutters(cm) regChange(cm) - setTimeout(() => alignHorizontally(cm), 20) + alignHorizontally(cm) } function dragDropChanged(cm, value, old) { From 45c54ada1942566a25bc16bb32eb4ebd868ce22a Mon Sep 17 00:00:00 2001 From: Marijn Haverbeke Date: Thu, 15 Dec 2016 13:50:31 +0100 Subject: [PATCH 0571/1790] [merge addon] Don't use DMP's cleanupSemantic function It sometimes produces invalid output. Closes #4410 --- addon/merge/merge.js | 1 - 1 file changed, 1 deletion(-) diff --git a/addon/merge/merge.js b/addon/merge/merge.js index f0d74644..352e27dc 100644 --- a/addon/merge/merge.js +++ b/addon/merge/merge.js @@ -571,7 +571,6 @@ var dmp = new diff_match_patch(); function getDiff(a, b) { var diff = dmp.diff_main(a, b); - dmp.diff_cleanupSemantic(diff); // The library sometimes leaves in empty parts, which confuse the algorithm for (var i = 0; i < diff.length; ++i) { var part = diff[i]; From 900659feeb6d4ce95abb68c7d68767c1bb586111 Mon Sep 17 00:00:00 2001 From: Marijn Haverbeke Date: Thu, 15 Dec 2016 13:54:30 +0100 Subject: [PATCH 0572/1790] Don't autofocus until the editor has a .state property Since the input object might try to read from that on focusing Issue #4439 --- src/edit/CodeMirror.js | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-) diff --git a/src/edit/CodeMirror.js b/src/edit/CodeMirror.js index a3dc622c..3c482c59 100644 --- a/src/edit/CodeMirror.js +++ b/src/edit/CodeMirror.js @@ -45,7 +45,6 @@ export function CodeMirror(place, options) { themeChanged(this) if (options.lineWrapping) this.display.wrapper.className += " CodeMirror-wrap" - if (options.autofocus && !mobile) display.input.focus() initScrollbars(this) this.state = { @@ -64,6 +63,8 @@ export function CodeMirror(place, options) { specialChars: null } + if (options.autofocus && !mobile) display.input.focus() + // Override magic textarea content restore that IE sometimes does // on our hidden textarea on reload if (ie && ie_version < 11) setTimeout(() => this.display.input.reset(true), 20) From d5d12e0d0a631034c6363e113c808b0d6c466783 Mon Sep 17 00:00:00 2001 From: Adrian Heine Date: Thu, 29 Sep 2016 11:11:38 +0200 Subject: [PATCH 0573/1790] Convert some classes to ES6 --- src/display/scrollbars.js | 81 +++++++++++---------- src/display/update_display.js | 44 ++++++------ src/input/ContentEditableInput.js | 114 +++++++++++++++--------------- src/input/TextareaInput.js | 108 ++++++++++++++-------------- src/util/misc.js | 2 +- 5 files changed, 177 insertions(+), 172 deletions(-) diff --git a/src/display/scrollbars.js b/src/display/scrollbars.js index a85fffe9..2026a3b0 100644 --- a/src/display/scrollbars.js +++ b/src/display/scrollbars.js @@ -3,7 +3,7 @@ import { on } from "../util/event" import { scrollGap, paddingVert } from "../measurement/position_measurement" import { ie, ie_version, mac, mac_geMountainLion } from "../util/browser" import { updateHeightsInViewport } from "./update_lines" -import { copyObj, Delayed } from "../util/misc" +import { Delayed } from "../util/misc" import { setScrollLeft, setScrollTop } from "./scroll_events" @@ -27,26 +27,26 @@ export function measureForScrollbars(cm) { } } -function NativeScrollbars(place, scroll, cm) { - this.cm = cm - let vert = this.vert = elt("div", [elt("div", null, null, "min-width: 1px")], "CodeMirror-vscrollbar") - let horiz = this.horiz = elt("div", [elt("div", null, null, "height: 100%; min-height: 1px")], "CodeMirror-hscrollbar") - place(vert); place(horiz) - - on(vert, "scroll", () => { - if (vert.clientHeight) scroll(vert.scrollTop, "vertical") - }) - on(horiz, "scroll", () => { - if (horiz.clientWidth) scroll(horiz.scrollLeft, "horizontal") - }) - - this.checkedZeroWidth = false - // Need to set a minimum width to see the scrollbar on IE7 (but must not set it on IE8). - if (ie && ie_version < 8) this.horiz.style.minHeight = this.vert.style.minWidth = "18px" -} +class NativeScrollbars { + constructor(place, scroll, cm) { + this.cm = cm + let vert = this.vert = elt("div", [elt("div", null, null, "min-width: 1px")], "CodeMirror-vscrollbar") + let horiz = this.horiz = elt("div", [elt("div", null, null, "height: 100%; min-height: 1px")], "CodeMirror-hscrollbar") + place(vert); place(horiz) + + on(vert, "scroll", () => { + if (vert.clientHeight) scroll(vert.scrollTop, "vertical") + }) + on(horiz, "scroll", () => { + if (horiz.clientWidth) scroll(horiz.scrollLeft, "horizontal") + }) + + this.checkedZeroWidth = false + // Need to set a minimum width to see the scrollbar on IE7 (but must not set it on IE8). + if (ie && ie_version < 8) this.horiz.style.minHeight = this.vert.style.minWidth = "18px" + } -NativeScrollbars.prototype = copyObj({ - update: function(measure) { + update(measure) { let needsH = measure.scrollWidth > measure.clientWidth + 1 let needsV = measure.scrollHeight > measure.clientHeight + 1 let sWidth = measure.nativeBarWidth @@ -81,23 +81,27 @@ NativeScrollbars.prototype = copyObj({ } return {right: needsV ? sWidth : 0, bottom: needsH ? sWidth : 0} - }, - setScrollLeft: function(pos) { + } + + setScrollLeft(pos) { if (this.horiz.scrollLeft != pos) this.horiz.scrollLeft = pos if (this.disableHoriz) this.enableZeroWidthBar(this.horiz, this.disableHoriz) - }, - setScrollTop: function(pos) { + } + + setScrollTop(pos) { if (this.vert.scrollTop != pos) this.vert.scrollTop = pos if (this.disableVert) this.enableZeroWidthBar(this.vert, this.disableVert) - }, - zeroWidthHack: function() { + } + + zeroWidthHack() { let w = mac && !mac_geMountainLion ? "12px" : "18px" this.horiz.style.height = this.vert.style.width = w this.horiz.style.pointerEvents = this.vert.style.pointerEvents = "none" this.disableHoriz = new Delayed this.disableVert = new Delayed - }, - enableZeroWidthBar: function(bar, delay) { + } + + enableZeroWidthBar(bar, delay) { bar.style.pointerEvents = "auto" function maybeDisable() { // To find out whether the scrollbar is still visible, we @@ -112,22 +116,21 @@ NativeScrollbars.prototype = copyObj({ else delay.set(1000, maybeDisable) } delay.set(1000, maybeDisable) - }, - clear: function() { + } + + clear() { let parent = this.horiz.parentNode parent.removeChild(this.horiz) parent.removeChild(this.vert) } -}, NativeScrollbars.prototype) - -function NullScrollbars() {} +} -NullScrollbars.prototype = copyObj({ - update: function() { return {bottom: 0, right: 0} }, - setScrollLeft: function() {}, - setScrollTop: function() {}, - clear: function() {} -}, NullScrollbars.prototype) +class NullScrollbars { + update() { return {bottom: 0, right: 0} } + setScrollLeft() {} + setScrollTop() {} + clear() {} +} export function updateScrollbars(cm, measure) { if (!measure) measure = measureForScrollbars(cm) diff --git a/src/display/update_display.js b/src/display/update_display.js index 4e016a25..17d5a069 100644 --- a/src/display/update_display.js +++ b/src/display/update_display.js @@ -17,28 +17,30 @@ import { adjustView, countDirtyView, resetView } from "./view_tracking" // DISPLAY DRAWING -export function DisplayUpdate(cm, viewport, force) { - let display = cm.display - - 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.wrapperWidth = display.wrapper.clientWidth - this.oldDisplayWidth = displayWidth(cm) - this.force = force - this.dims = getDimensions(cm) - this.events = [] -} +export class DisplayUpdate { + constructor(cm, viewport, force) { + let display = cm.display + + 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.wrapperWidth = display.wrapper.clientWidth + this.oldDisplayWidth = displayWidth(cm) + this.force = force + this.dims = getDimensions(cm) + this.events = [] + } -DisplayUpdate.prototype.signal = function(emitter, type) { - if (hasHandler(emitter, type)) - this.events.push(arguments) -} -DisplayUpdate.prototype.finish = function() { - for (let i = 0; i < this.events.length; i++) - signal.apply(null, this.events[i]) + signal(emitter, type) { + if (hasHandler(emitter, type)) + this.events.push(arguments) + } + finish() { + for (let i = 0; i < this.events.length; i++) + signal.apply(null, this.events[i]) + } } export function maybeClipScrollbars(cm) { diff --git a/src/input/ContentEditableInput.js b/src/input/ContentEditableInput.js index d76058ff..57114af6 100644 --- a/src/input/ContentEditableInput.js +++ b/src/input/ContentEditableInput.js @@ -12,21 +12,21 @@ import { getBidiPartAt, getOrder } from "../util/bidi" import { gecko, ie_version } from "../util/browser" import { contains, range, removeChildrenAndAdd, selectInput } from "../util/dom" import { on, signalDOMEvent } from "../util/event" -import { copyObj, Delayed, lst, nothing, sel_dontScroll } from "../util/misc" +import { Delayed, lst, sel_dontScroll } from "../util/misc" // CONTENTEDITABLE INPUT STYLE -export default function ContentEditableInput(cm) { - this.cm = cm - this.lastAnchorNode = this.lastAnchorOffset = this.lastFocusNode = this.lastFocusOffset = null - this.polling = new Delayed() - this.composing = null - this.gracePeriod = false - this.readDOMTimeout = null -} +export default class ContentEditableInput { + constructor(cm) { + this.cm = cm + this.lastAnchorNode = this.lastAnchorOffset = this.lastFocusNode = this.lastFocusOffset = null + this.polling = new Delayed() + this.composing = null + this.gracePeriod = false + this.readDOMTimeout = null + } -ContentEditableInput.prototype = copyObj({ - init: function(display) { + init(display) { let input = this, cm = input.cm let div = input.div = display.lineDiv disableBrowserMagic(div, cm.options.spellcheck) @@ -99,21 +99,21 @@ ContentEditableInput.prototype = copyObj({ } on(div, "copy", onCopyCut) on(div, "cut", onCopyCut) - }, + } - prepareSelection: function() { + prepareSelection() { let result = prepareSelection(this.cm, false) result.focus = this.cm.state.focused return result - }, + } - showSelection: function(info, takeFocus) { + showSelection(info, takeFocus) { if (!info || !this.cm.display.view.length) return if (info.focus || takeFocus) this.showPrimarySelection() this.showMultipleSelections(info) - }, + } - showPrimarySelection: function() { + showPrimarySelection() { let sel = window.getSelection(), prim = this.cm.doc.sel.primary() let curAnchor = domToPos(this.cm, sel.anchorNode, sel.anchorOffset) let curFocus = domToPos(this.cm, sel.focusNode, sel.focusOffset) @@ -154,48 +154,48 @@ ContentEditableInput.prototype = copyObj({ else if (gecko) this.startGracePeriod() } this.rememberSelection() - }, + } - startGracePeriod: function() { + startGracePeriod() { clearTimeout(this.gracePeriod) this.gracePeriod = setTimeout(() => { this.gracePeriod = false if (this.selectionChanged()) this.cm.operation(() => this.cm.curOp.selectionChanged = true) }, 20) - }, + } - showMultipleSelections: function(info) { + showMultipleSelections(info) { removeChildrenAndAdd(this.cm.display.cursorDiv, info.cursors) removeChildrenAndAdd(this.cm.display.selectionDiv, info.selection) - }, + } - rememberSelection: function() { + rememberSelection() { let sel = window.getSelection() this.lastAnchorNode = sel.anchorNode; this.lastAnchorOffset = sel.anchorOffset this.lastFocusNode = sel.focusNode; this.lastFocusOffset = sel.focusOffset - }, + } - selectionInEditor: function() { + selectionInEditor() { let sel = window.getSelection() if (!sel.rangeCount) return false let node = sel.getRangeAt(0).commonAncestorContainer return contains(this.div, node) - }, + } - focus: function() { + focus() { if (this.cm.options.readOnly != "nocursor") { if (!this.selectionInEditor()) this.showSelection(this.prepareSelection(), true) this.div.focus() } - }, - blur: function() { this.div.blur() }, - getField: function() { return this.div }, + } + blur() { this.div.blur() } + getField() { return this.div } - supportsTouch: function() { return true }, + supportsTouch() { return true } - receivedFocus: function() { + receivedFocus() { let input = this if (this.selectionInEditor()) this.pollSelection() @@ -209,15 +209,15 @@ ContentEditableInput.prototype = copyObj({ } } this.polling.set(this.cm.options.pollInterval, poll) - }, + } - selectionChanged: function() { + selectionChanged() { let sel = window.getSelection() return sel.anchorNode != this.lastAnchorNode || sel.anchorOffset != this.lastAnchorOffset || sel.focusNode != this.lastFocusNode || sel.focusOffset != this.lastFocusOffset - }, + } - pollSelection: function() { + pollSelection() { if (!this.composing && this.readDOMTimeout == null && !this.gracePeriod && this.selectionChanged()) { let sel = window.getSelection(), cm = this.cm this.rememberSelection() @@ -228,9 +228,9 @@ ContentEditableInput.prototype = copyObj({ if (anchor.bad || head.bad) cm.curOp.selectionChanged = true }) } - }, + } - pollContent: function() { + pollContent() { if (this.readDOMTimeout != null) { clearTimeout(this.readDOMTimeout) this.readDOMTimeout = null @@ -291,22 +291,22 @@ ContentEditableInput.prototype = copyObj({ replaceRange(cm.doc, newText, chFrom, chTo, "+input") return true } - }, + } - ensurePolled: function() { + ensurePolled() { this.forceCompositionEnd() - }, - reset: function() { + } + reset() { this.forceCompositionEnd() - }, - forceCompositionEnd: function() { + } + forceCompositionEnd() { if (!this.composing) return this.composing = null if (!this.pollContent()) regChange(this.cm) this.div.blur() this.div.focus() - }, - readFromDOMSoon: function() { + } + readFromDOMSoon() { if (this.readDOMTimeout != null) return this.readDOMTimeout = setTimeout(() => { this.readDOMTimeout = null @@ -314,27 +314,27 @@ ContentEditableInput.prototype = copyObj({ if (this.cm.isReadOnly() || !this.pollContent()) runInOp(this.cm, () => regChange(this.cm)) }, 80) - }, + } - setUneditable: function(node) { + setUneditable(node) { node.contentEditable = "false" - }, + } - onKeyPress: function(e) { + onKeyPress(e) { e.preventDefault() if (!this.cm.isReadOnly()) operation(this.cm, applyTextInput)(this.cm, String.fromCharCode(e.charCode == null ? e.keyCode : e.charCode), 0) - }, + } - readOnlyChanged: function(val) { + readOnlyChanged(val) { this.div.contentEditable = String(val != "nocursor") - }, + } - onContextMenu: nothing, - resetPosition: nothing, + onContextMenu() {} + resetPosition() {} +} - needsContentAttribute: true - }, ContentEditableInput.prototype) +ContentEditableInput.prototype.needsContentAttribute = true function posToDOM(cm, pos) { let view = findViewForLine(cm, pos.line) diff --git a/src/input/TextareaInput.js b/src/input/TextareaInput.js index a150f05d..28b33273 100644 --- a/src/input/TextareaInput.js +++ b/src/input/TextareaInput.js @@ -9,31 +9,31 @@ import { captureRightClick, ie, ie_version, ios, mac, mobile, presto, webkit } f import { activeElt, removeChildrenAndAdd, selectInput } from "../util/dom" import { e_preventDefault, e_stop, off, on, signalDOMEvent } from "../util/event" import { hasCopyEvent, hasSelection } from "../util/feature_detection" -import { copyObj, Delayed, nothing, sel_dontScroll } from "../util/misc" +import { Delayed, sel_dontScroll } from "../util/misc" // TEXTAREA INPUT STYLE -export default function TextareaInput(cm) { - this.cm = cm - // See input.poll and input.reset - this.prevInput = "" - - // Flag that indicates whether we expect input to appear real soon - // now (after some event like 'keypress' or 'input') and are - // polling intensively. - this.pollingFast = false - // Self-resetting timeout for the poller - this.polling = new Delayed() - // Tracks when input.reset has punted to just putting a short - // string into the textarea instead of the full selection. - this.inaccurateSelection = false - // Used to work around IE issue with selection being forgotten when focus moves away from textarea - this.hasSelection = false - this.composing = null -} - -TextareaInput.prototype = copyObj({ - init: function(display) { +export default class TextareaInput { + constructor(cm) { + this.cm = cm + // See input.poll and input.reset + this.prevInput = "" + + // Flag that indicates whether we expect input to appear real soon + // now (after some event like 'keypress' or 'input') and are + // polling intensively. + this.pollingFast = false + // Self-resetting timeout for the poller + this.polling = new Delayed() + // Tracks when input.reset has punted to just putting a short + // string into the textarea instead of the full selection. + this.inaccurateSelection = false + // Used to work around IE issue with selection being forgotten when focus moves away from textarea + this.hasSelection = false + this.composing = null + } + + init(display) { let input = this, cm = this.cm // Wraps and hides input textarea @@ -112,9 +112,9 @@ TextareaInput.prototype = copyObj({ input.composing = null } }) - }, + } - prepareSelection: function() { + prepareSelection() { // Redraw the selection and/or cursor let cm = this.cm, display = cm.display, doc = cm.doc let result = prepareSelection(cm) @@ -130,9 +130,9 @@ TextareaInput.prototype = copyObj({ } return result - }, + } - showSelection: function(drawn) { + showSelection(drawn) { let cm = this.cm, display = cm.display removeChildrenAndAdd(display.cursorDiv, drawn.cursors) removeChildrenAndAdd(display.selectionDiv, drawn.selection) @@ -140,11 +140,11 @@ TextareaInput.prototype = copyObj({ this.wrapper.style.top = drawn.teTop + "px" this.wrapper.style.left = drawn.teLeft + "px" } - }, + } // Reset the input to correspond to the selection (or to be empty, // when not typing and nothing is selected) - reset: function(typing) { + reset(typing) { if (this.contextMenuPending) return let minimal, selected, cm = this.cm, doc = cm.doc if (cm.somethingSelected()) { @@ -161,41 +161,41 @@ TextareaInput.prototype = copyObj({ if (ie && ie_version >= 9) this.hasSelection = null } this.inaccurateSelection = minimal - }, + } - getField: function() { return this.textarea }, + getField() { return this.textarea } - supportsTouch: function() { return false }, + supportsTouch() { return false } - focus: function() { + focus() { if (this.cm.options.readOnly != "nocursor" && (!mobile || activeElt() != this.textarea)) { try { this.textarea.focus() } catch (e) {} // IE8 will throw if the textarea is display: none or not in DOM } - }, + } - blur: function() { this.textarea.blur() }, + blur() { this.textarea.blur() } - resetPosition: function() { + resetPosition() { this.wrapper.style.top = this.wrapper.style.left = 0 - }, + } - receivedFocus: function() { this.slowPoll() }, + receivedFocus() { this.slowPoll() } // Poll for input changes, using the normal rate of polling. This // runs as long as the editor is focused. - slowPoll: function() { + slowPoll() { if (this.pollingFast) return this.polling.set(this.cm.options.pollInterval, () => { this.poll() if (this.cm.state.focused) this.slowPoll() }) - }, + } // When an event has just come in that is likely to add or change // something in the input textarea, we poll faster, to ensure that // the change appears on the screen quickly. - fastPoll: function() { + fastPoll() { let missed = false, input = this input.pollingFast = true function p() { @@ -204,7 +204,7 @@ TextareaInput.prototype = copyObj({ else {input.pollingFast = false; input.slowPoll()} } input.polling.set(20, p) - }, + } // Read input from the textarea, and update the document to match. // When something is selected, it is present in the textarea, and @@ -212,7 +212,7 @@ TextareaInput.prototype = copyObj({ // used). When nothing is selected, the cursor sits after previously // seen text (can be empty), which is stored in prevInput (we must // not reset the textarea when typing, because that breaks IME). - poll: function() { + poll() { let cm = this.cm, input = this.textarea, prevInput = this.prevInput // Since this is called a *lot*, try to bail out as cheaply as // possible when it is clear that nothing happened. hasSelection @@ -259,18 +259,18 @@ TextareaInput.prototype = copyObj({ } }) return true - }, + } - ensurePolled: function() { + ensurePolled() { if (this.pollingFast && this.poll()) this.pollingFast = false - }, + } - onKeyPress: function() { + onKeyPress() { if (ie && ie_version >= 9) this.hasSelection = null this.fastPoll() - }, + } - onContextMenu: function(e) { + onContextMenu(e) { let input = this, cm = input.cm, display = cm.display, te = input.textarea let pos = posFromMouse(cm, e), scrollPos = display.scroller.scrollTop if (!pos || presto) return // Opera is difficult. @@ -346,13 +346,13 @@ TextareaInput.prototype = copyObj({ } else { setTimeout(rehide, 50) } - }, + } - readOnlyChanged: function(val) { + readOnlyChanged(val) { if (!val) this.reset() - }, + } - setUneditable: nothing, + setUneditable() {} +} - needsContentAttribute: false -}, TextareaInput.prototype) +TextareaInput.prototype.needsContentAttribute = false diff --git a/src/util/misc.js b/src/util/misc.js index c94de8bd..2fb90914 100644 --- a/src/util/misc.js +++ b/src/util/misc.js @@ -87,7 +87,7 @@ export function insertSorted(array, value, score) { array.splice(pos, 0, value) } -export function nothing() {} +function nothing() {} export function createObj(base, props) { let inst From b557c1592942d29086889e65d4811d59977843ca Mon Sep 17 00:00:00 2001 From: Philipp A Date: Fri, 16 Dec 2016 12:46:39 +0100 Subject: [PATCH 0574/1790] CSS: Allow contextual glyph alternatives MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Ligatures were disabled since some editors didn’t allow placing the cursor inside of them. “Contextual alternatives” on the other hand still allow this, therefore this commit enables them to support e.g. Fira Code’s main gimmick. --- lib/codemirror.css | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/lib/codemirror.css b/lib/codemirror.css index d7821d17..2a6a2622 100644 --- a/lib/codemirror.css +++ b/lib/codemirror.css @@ -249,8 +249,8 @@ div.CodeMirror span.CodeMirror-nonmatchingbracket {color: #f22;} position: relative; overflow: visible; -webkit-tap-highlight-color: transparent; - -webkit-font-variant-ligatures: none; - font-variant-ligatures: none; + -webkit-font-variant-ligatures: contextual; + font-variant-ligatures: contextual; } .CodeMirror-wrap pre { word-wrap: break-word; From e7080dcc8ecede7658d2381a17d8a6342c564f94 Mon Sep 17 00:00:00 2001 From: Martin Zagora Date: Mon, 19 Dec 2016 09:16:42 +1100 Subject: [PATCH 0575/1790] add passing test for #4437 --- mode/javascript/test.js | 12 ++++++++++++ 1 file changed, 12 insertions(+) diff --git a/mode/javascript/test.js b/mode/javascript/test.js index 41765e75..971829d9 100644 --- a/mode/javascript/test.js +++ b/mode/javascript/test.js @@ -207,6 +207,18 @@ " [keyword private] [property _foo]: [variable-3 string];", "}") + TS("typescript_literal_types", + "[keyword import] [keyword *] [keyword as] [def Sequelize] [keyword from] [string 'sequelize'];", + "[keyword interface] [def MyAttributes] {", + " [property truthy]: [string 'true'] [operator |] [number 1] [operator |] [atom true];", + " [property falsy]: [string 'false'] [operator |] [number 0] [operator |] [atom false];", + "}", + "[keyword interface] [def MyInstance] [keyword extends] [variable-3 Sequelize].[variable-3 Instance] [operator <] [variable-3 MyAttributes] [operator >] {", + " [property rawAttributes]: [variable-3 MyAttributes];", + " [property truthy]: [string 'true'] [operator |] [number 1] [operator |] [atom true];", + " [property falsy]: [string 'false'] [operator |] [number 0] [operator |] [atom false];", + "}") + var jsonld_mode = CodeMirror.getMode( {indentUnit: 2}, {name: "javascript", jsonld: true} From b50e4310a4d6339db6ac55c7c7b3e3ae0117063e Mon Sep 17 00:00:00 2001 From: Martin Zagora Date: Mon, 19 Dec 2016 09:42:22 +1100 Subject: [PATCH 0576/1790] test: double >> breaks typescript parsing --- mode/javascript/test.js | 12 ++++++++++++ 1 file changed, 12 insertions(+) diff --git a/mode/javascript/test.js b/mode/javascript/test.js index 971829d9..a38865d4 100644 --- a/mode/javascript/test.js +++ b/mode/javascript/test.js @@ -219,6 +219,18 @@ " [property falsy]: [string 'false'] [operator |] [number 0] [operator |] [atom false];", "}") + TS("typescript_extend_operators", + "[keyword export] [keyword interface] [def UserModel] [keyword extends]", + " [variable-3 Sequelize].[variable-3 Model] [operator <] [variable-3 UserInstance], [variable-3 UserAttributes] [operator >] {", + " [property findById]: (", + " [variable userId]: [variable-3 number]", + " ) [operator =>] [variable-3 Promise] [operator <] [variable-3 Array] [operator <] { [property id], [property name] } [operator >][operator >];", + " [property updateById]: (", + " [variable userId]: [variable-3 number],", + " [variable isActive]: [variable-3 boolean]", + " ) [operator =>] [variable-3 Promise] [operator <] [variable-3 AccountHolderNotificationPreferenceInstance] [operator >];", + " }") + var jsonld_mode = CodeMirror.getMode( {indentUnit: 2}, {name: "javascript", jsonld: true} From c71b86b9890c69f40d7dfdd44a9bb7d197201add Mon Sep 17 00:00:00 2001 From: Marijn Haverbeke Date: Mon, 19 Dec 2016 09:59:25 +0100 Subject: [PATCH 0577/1790] [javascript mode] Parse '>>' as two separate ops in TS type param list Closes #4448 --- mode/javascript/javascript.js | 5 +++-- mode/javascript/test.js | 2 +- 2 files changed, 4 insertions(+), 3 deletions(-) diff --git a/mode/javascript/javascript.js b/mode/javascript/javascript.js index 2ad7a1e9..10419bf9 100644 --- a/mode/javascript/javascript.js +++ b/mode/javascript/javascript.js @@ -146,7 +146,8 @@ CodeMirror.defineMode("javascript", function(config, parserConfig) { stream.skipToEnd(); return ret("error", "error"); } else if (isOperatorChar.test(ch)) { - stream.eatWhile(isOperatorChar); + if (ch != ">" || !state.lexical || state.lexical.type != ">") + stream.eatWhile(isOperatorChar); return ret("operator", "operator", stream.current()); } else if (wordRE.test(ch)) { stream.eatWhile(wordRE); @@ -559,7 +560,7 @@ CodeMirror.defineMode("javascript", function(config, parserConfig) { else if (type == ":") return cont(typeexpr) } function afterType(type, value) { - if (value == "<") return cont(commasep(typeexpr, ">"), afterType) + if (value == "<") return cont(pushlex(">"), commasep(typeexpr, ">"), poplex, afterType) if (value == "|" || type == ".") return cont(typeexpr) if (type == "[") return cont(expect("]"), afterType) } diff --git a/mode/javascript/test.js b/mode/javascript/test.js index a38865d4..2caefea4 100644 --- a/mode/javascript/test.js +++ b/mode/javascript/test.js @@ -224,7 +224,7 @@ " [variable-3 Sequelize].[variable-3 Model] [operator <] [variable-3 UserInstance], [variable-3 UserAttributes] [operator >] {", " [property findById]: (", " [variable userId]: [variable-3 number]", - " ) [operator =>] [variable-3 Promise] [operator <] [variable-3 Array] [operator <] { [property id], [property name] } [operator >][operator >];", + " ) [operator =>] [variable-3 Promise] [operator <] [variable-3 Array] [operator <] { [property id], [property name] } [operator >>];", " [property updateById]: (", " [variable userId]: [variable-3 number],", " [variable isActive]: [variable-3 boolean]", From b411c18740642ebe47ac135e3daeac0a1e58a704 Mon Sep 17 00:00:00 2001 From: Marijn Haverbeke Date: Mon, 19 Dec 2016 10:24:04 +0100 Subject: [PATCH 0578/1790] Link to elixir mode --- mode/index.html | 1 + 1 file changed, 1 insertion(+) diff --git a/mode/index.html b/mode/index.html index 3a2fe551..c0001a53 100644 --- a/mode/index.html +++ b/mode/index.html @@ -56,6 +56,7 @@

    Language modes

  • EBNF
  • ECL
  • Eiffel
  • +
  • Elixir
  • Elm
  • Erlang
  • Factor
  • From d756ea1eb931d6ce7aa6f5eb3c8ea2e34a1bce20 Mon Sep 17 00:00:00 2001 From: Marijn Haverbeke Date: Mon, 19 Dec 2016 15:39:46 +0100 Subject: [PATCH 0579/1790] Make sure composition changes aren't dropped when forced after compositionend event. Issue #4441 --- src/input/ContentEditableInput.js | 12 ++++++++---- 1 file changed, 8 insertions(+), 4 deletions(-) diff --git a/src/input/ContentEditableInput.js b/src/input/ContentEditableInput.js index 57114af6..4c7b58eb 100644 --- a/src/input/ContentEditableInput.js +++ b/src/input/ContentEditableInput.js @@ -40,15 +40,15 @@ export default class ContentEditableInput { }) on(div, "compositionstart", e => { - this.composing = {data: e.data} + this.composing = {data: e.data, done: false} }) on(div, "compositionupdate", e => { - if (!this.composing) this.composing = {data: e.data} + if (!this.composing) this.composing = {data: e.data, done: false} }) on(div, "compositionend", e => { if (this.composing) { if (e.data != this.composing.data) this.readFromDOMSoon() - this.composing = null + this.composing.done = true } }) @@ -301,6 +301,7 @@ export default class ContentEditableInput { } forceCompositionEnd() { if (!this.composing) return + clearTimeout(this.readDOMTimeout) this.composing = null if (!this.pollContent()) regChange(this.cm) this.div.blur() @@ -310,7 +311,10 @@ export default class ContentEditableInput { if (this.readDOMTimeout != null) return this.readDOMTimeout = setTimeout(() => { this.readDOMTimeout = null - if (this.composing) return + if (this.composing) { + if (this.composing.done) this.composing = null + else return + } if (this.cm.isReadOnly() || !this.pollContent()) runInOp(this.cm, () => regChange(this.cm)) }, 80) From 33d920b06a7b52ceffbf9b083e1aab00993caef7 Mon Sep 17 00:00:00 2001 From: Marijn Haverbeke Date: Tue, 20 Dec 2016 10:12:28 +0100 Subject: [PATCH 0580/1790] [javascript mode] Properly tokenize a regexp after the export keyword Closes #4452 --- 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 10419bf9..fe9b805f 100644 --- a/mode/javascript/javascript.js +++ b/mode/javascript/javascript.js @@ -12,7 +12,7 @@ "use strict"; function expressionAllowed(stream, state, backUp) { - return /^(?:operator|sof|keyword c|case|new|[\[{}\(,;:]|=>)$/.test(state.lastType) || + return /^(?:operator|sof|keyword c|case|new|export|[\[{}\(,;:]|=>)$/.test(state.lastType) || (state.lastType == "quasi" && /\{\s*$/.test(stream.string.slice(0, stream.pos - (backUp || 0)))) } From 708084a28130a8c22553fd6193b7870ae66fee40 Mon Sep 17 00:00:00 2001 From: Marijn Haverbeke Date: Tue, 20 Dec 2016 10:21:46 +0100 Subject: [PATCH 0581/1790] [javascript mode] Improve import/export parsing Closes #4451 --- mode/javascript/javascript.js | 12 ++++++++++-- 1 file changed, 10 insertions(+), 2 deletions(-) diff --git a/mode/javascript/javascript.js b/mode/javascript/javascript.js index fe9b805f..10fc95bd 100644 --- a/mode/javascript/javascript.js +++ b/mode/javascript/javascript.js @@ -658,14 +658,19 @@ CodeMirror.defineMode("javascript", function(config, parserConfig) { if (type == ":") return cont(typeexpr, maybeAssign) return pass(functiondef) } - function afterExport(_type, value) { + function afterExport(type, value) { if (value == "*") { cx.marked = "keyword"; return cont(maybeFrom, expect(";")); } if (value == "default") { cx.marked = "keyword"; return cont(expression, expect(";")); } + if (type == "{") return cont(commasep(exportField, "}"), maybeFrom, expect(";")); return pass(statement); } + function exportField(type, value) { + if (value == "as") { cx.marked = "keyword"; return cont(expect("variable")); } + if (type == "variable") return pass(expressionNoComma, exportField); + } function afterImport(type) { if (type == "string") return cont(); - return pass(importSpec, maybeFrom); + return pass(importSpec, maybeMoreImports, maybeFrom); } function importSpec(type, value) { if (type == "{") return contCommasep(importSpec, "}"); @@ -673,6 +678,9 @@ CodeMirror.defineMode("javascript", function(config, parserConfig) { if (value == "*") cx.marked = "keyword"; return cont(maybeAs); } + function maybeMoreImports(type) { + if (type == ",") return cont(importSpec, maybeMoreImports) + } function maybeAs(_type, value) { if (value == "as") { cx.marked = "keyword"; return cont(importSpec); } } From ae45e2bdad8bb4a00c7f1cd7f6127a6963d9df8f Mon Sep 17 00:00:00 2001 From: Marijn Haverbeke Date: Tue, 20 Dec 2016 14:04:43 +0100 Subject: [PATCH 0582/1790] [javascript mode] Also allow a regexp after 'default' (For 'export default' syntax) Issue #4452 --- 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 10fc95bd..c185106c 100644 --- a/mode/javascript/javascript.js +++ b/mode/javascript/javascript.js @@ -12,7 +12,7 @@ "use strict"; function expressionAllowed(stream, state, backUp) { - return /^(?:operator|sof|keyword c|case|new|export|[\[{}\(,;:]|=>)$/.test(state.lastType) || + return /^(?:operator|sof|keyword c|case|new|export|default|[\[{}\(,;:]|=>)$/.test(state.lastType) || (state.lastType == "quasi" && /\{\s*$/.test(stream.string.slice(0, stream.pos - (backUp || 0)))) } From bfdfb212f76f479b3e651daa688f5da6909e7f48 Mon Sep 17 00:00:00 2001 From: Marijn Haverbeke Date: Tue, 20 Dec 2016 15:40:12 +0100 Subject: [PATCH 0583/1790] Mark version 5.22.0 --- AUTHORS | 4 ++++ CHANGELOG.md | 18 ++++++++++++++++++ doc/manual.html | 2 +- doc/releases.html | 13 ++++++++++++- index.html | 2 +- package.json | 2 +- src/edit/main.js | 2 +- 7 files changed, 38 insertions(+), 5 deletions(-) diff --git a/AUTHORS b/AUTHORS index 09fb4cc8..9b9b3355 100644 --- a/AUTHORS +++ b/AUTHORS @@ -45,6 +45,7 @@ Andrea G Andreas Reischuck Andres Taylor Andre von Houck +Andrew Cheng Andrey Fedorov Andrey Klyuchnikov Andrey Lushnikov @@ -186,6 +187,7 @@ fbuchinger feizhang365 Felipe Lalanne Felix Raab +ficristo Filip Noetzel Filip Stollár flack @@ -500,6 +502,7 @@ Remi Nyborg Richard Denton Richard van der Meer Richard Z.H. Wang +Rishi Goomar Robert Crossfield Roberto Abdelkader Martínez Pérez robertop23 @@ -582,6 +585,7 @@ Todd Berman Tomas-A Tomas Varaneckas Tom Erik Støwer +Tom Klancer Tom MacWright Tony Jian Travis Heppe diff --git a/CHANGELOG.md b/CHANGELOG.md index 2404815f..f3eb79ff 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -1,3 +1,21 @@ +## 5.22.0 (2016-12-20) + +### Bug fixes + +[sublime bindings](http://codemirror.net/demo/sublime.html): Make `selectBetweenBrackets` work with multiple cursors. + +[javascript mode](http://codemirror.net/mode/javascript/): Fix issues with parsing complex TypeScript types, imports, and exports. + +A contentEditable editor instance with autofocus enabled no longer crashes during initializing. + +### New features + +[emacs bindings](http://codemirror.net/demo/emacs.html): Export `CodeMirror.emacs` to allow other addons to hook into Emacs-style functionality. + +[active-line addon](http://codemirror.net/doc/manual.html#addon_active-line): Add `nonEmpty` option. + +New event: [`optionChange`](http://codemirror.net/doc/manual.html#event_optionChange). + ## 5.21.0 (2016-11-21) ### Bug fixes diff --git a/doc/manual.html b/doc/manual.html index c6745e3c..704984fc 100644 --- a/doc/manual.html +++ b/doc/manual.html @@ -69,7 +69,7 @@

    User manual and reference guide - version 5.21.1 + version 5.22.0

    CodeMirror is a code-editor component that can be embedded in diff --git a/doc/releases.html b/doc/releases.html index 58804691..2dfd1fe4 100644 --- a/doc/releases.html +++ b/doc/releases.html @@ -30,7 +30,18 @@

    Release notes and version history

    Version 5.x

    -

    21-11-2016: Version 5.21.0:

    +

    20-12-2016: Version 5.22.0:

    + +
      +
    • sublime bindings: Make selectBetweenBrackets work with multiple cursors.
    • +
    • javascript mode: Fix issues with parsing complex TypeScript types, imports, and exports.
    • +
    • A contentEditable editor instance with autofocus enabled no longer crashes during initializing.
    • +
    • emacs bindings: Export CodeMirror.emacs to allow other addons to hook into Emacs-style functionality.
    • +
    • active-line addon: Add nonEmpty option.
    • +
    • New event: optionChange.
    • +
    + +

    21-11-2016: Version 5.21.0:

    • Tapping/clicking the editor in contentEditable mode on Chrome now puts the cursor at the tapped position.
    • diff --git a/index.html b/index.html index 71642960..06fdc156 100644 --- a/index.html +++ b/index.html @@ -96,7 +96,7 @@

      This is CodeMirror

      - Get the current version: 5.21.0.
      + Get the current version: 5.22.0.
      You can see the code,
      read the release notes,
      or study the user manual. diff --git a/package.json b/package.json index e2b97a9c..c007b2a8 100644 --- a/package.json +++ b/package.json @@ -1,6 +1,6 @@ { "name": "codemirror", - "version": "5.21.1", + "version": "5.22.0", "main": "lib/codemirror.js", "description": "Full-featured in-browser code editor", "license": "MIT", diff --git a/src/edit/main.js b/src/edit/main.js index 64b647b5..8da8f48e 100644 --- a/src/edit/main.js +++ b/src/edit/main.js @@ -66,4 +66,4 @@ import { addLegacyProps } from "./legacy" addLegacyProps(CodeMirror) -CodeMirror.version = "5.21.1" +CodeMirror.version = "5.22.0" From adfa066411473c6fe2f3a6fa15e076d52eceeb94 Mon Sep 17 00:00:00 2001 From: Marijn Haverbeke Date: Tue, 20 Dec 2016 15:52:23 +0100 Subject: [PATCH 0584/1790] Bump version number post-5.22.0 --- doc/manual.html | 2 +- index.html | 2 +- package.json | 2 +- src/edit/main.js | 2 +- 4 files changed, 4 insertions(+), 4 deletions(-) diff --git a/doc/manual.html b/doc/manual.html index 704984fc..98c16094 100644 --- a/doc/manual.html +++ b/doc/manual.html @@ -69,7 +69,7 @@

      User manual and reference guide - version 5.22.0 + version 5.22.1

      CodeMirror is a code-editor component that can be embedded in diff --git a/index.html b/index.html index 06fdc156..dd4b1ded 100644 --- a/index.html +++ b/index.html @@ -96,7 +96,7 @@

      This is CodeMirror

      - Get the current version: 5.22.0.
      + Get the current version: 5.22.1.
      You can see the code,
      read the release notes,
      or study the user manual. diff --git a/package.json b/package.json index c007b2a8..0c565e9b 100644 --- a/package.json +++ b/package.json @@ -1,6 +1,6 @@ { "name": "codemirror", - "version": "5.22.0", + "version": "5.22.1", "main": "lib/codemirror.js", "description": "Full-featured in-browser code editor", "license": "MIT", diff --git a/src/edit/main.js b/src/edit/main.js index 8da8f48e..429cfe94 100644 --- a/src/edit/main.js +++ b/src/edit/main.js @@ -66,4 +66,4 @@ import { addLegacyProps } from "./legacy" addLegacyProps(CodeMirror) -CodeMirror.version = "5.22.0" +CodeMirror.version = "5.22.1" From 38932a8e332ca02f73d395f899cfdfa76922e132 Mon Sep 17 00:00:00 2001 From: Marijn Haverbeke Date: Tue, 20 Dec 2016 15:53:55 +0100 Subject: [PATCH 0585/1790] Remove link to google group --- index.html | 8 ++++---- 1 file changed, 4 insertions(+), 4 deletions(-) diff --git a/index.html b/index.html index dd4b1ded..9812829e 100644 --- a/index.html +++ b/index.html @@ -163,10 +163,10 @@

      Community

      Discussion around the project is done on a discussion forum. - There is also - the codemirror-announce - list, which is only used for major announcements (such as new - versions). If needed, you can + Announcements related to the project, such as new versions, are + posted in the + forum's "announce" + category. If needed, you can contact the maintainer directly. We aim to be an inclusive, welcoming community. To make that explicit, we have From 12abb7c7f0fcfff441810eb5ce5a8905385c84c2 Mon Sep 17 00:00:00 2001 From: Marijn Haverbeke Date: Thu, 22 Dec 2016 16:55:10 +0100 Subject: [PATCH 0586/1790] Remove check for license blob in linter Closes #3909 --- test/lint.js | 3 +-- 1 file changed, 1 insertion(+), 2 deletions(-) diff --git a/test/lint.js b/test/lint.js index cc03ce64..12146ffd 100644 --- a/test/lint.js +++ b/test/lint.js @@ -3,8 +3,7 @@ var blint = require("blint"); ["mode", "lib", "addon", "keymap"].forEach(function(dir) { blint.checkDir(dir, { browser: true, - allowedGlobals: ["CodeMirror", "define", "test", "requirejs"], - blob: "// CodeMirror, copyright (c) by Marijn Haverbeke and others\n// Distributed under an MIT license: http:\/\/codemirror.net\/LICENSE\n\n" + allowedGlobals: ["CodeMirror", "define", "test", "requirejs"] }); }); From 3dc1a5db143bd99fd3a1a9bca1ee1005ef5c828d Mon Sep 17 00:00:00 2001 From: Emmanuel Schanzer Date: Thu, 22 Dec 2016 11:51:59 -0500 Subject: [PATCH 0587/1790] screenreader fixes --- src/line/line_data.js | 3 +++ 1 file changed, 3 insertions(+) diff --git a/src/line/line_data.js b/src/line/line_data.js index 93b57577..7583c342 100644 --- a/src/line/line_data.js +++ b/src/line/line_data.js @@ -66,6 +66,9 @@ export function buildLineContent(cm, lineView) { col: 0, pos: 0, cm: cm, trailingSpace: false, splitSpaces: (ie || webkit) && cm.getOption("lineWrapping")} + // hide from accessibility tree + content.setAttribute("role", "presentation") + builder.pre.setAttribute("role", "presentation") lineView.measure = {} // Iterate over the logical lines that make up this visual line. From 85956bed14dc8cdfd6610487c08243edf7ac0076 Mon Sep 17 00:00:00 2001 From: Emmanuel Schanzer Date: Thu, 22 Dec 2016 13:39:29 -0500 Subject: [PATCH 0588/1790] Also give widgets a role attribute of 'presentation' --- src/model/mark_text.js | 1 + 1 file changed, 1 insertion(+) diff --git a/src/model/mark_text.js b/src/model/mark_text.js index 4250288e..15ec2849 100644 --- a/src/model/mark_text.js +++ b/src/model/mark_text.js @@ -163,6 +163,7 @@ export function markText(doc, from, to, options, type) { // Showing up as a widget implies collapsed (widget replaces text) marker.collapsed = true marker.widgetNode = elt("span", [marker.replacedWith], "CodeMirror-widget") + marker.widgetNode.setAttribute("role", "presentation") // hide from accessibility tree if (!options.handleMouseEvents) marker.widgetNode.setAttribute("cm-ignore-events", "true") if (options.insertLeft) marker.widgetNode.insertLeft = true } From aeb547774525ea6e3544a27ad3b2b387fb384e05 Mon Sep 17 00:00:00 2001 From: Marijn Haverbeke Date: Fri, 23 Dec 2016 14:03:58 +0100 Subject: [PATCH 0589/1790] Update htmlmixed documentation --- mode/htmlmixed/index.html | 29 ++++++++++++++++++++--------- 1 file changed, 20 insertions(+), 9 deletions(-) diff --git a/mode/htmlmixed/index.html b/mode/htmlmixed/index.html index f94df9e2..caa7546c 100644 --- a/mode/htmlmixed/index.html +++ b/mode/htmlmixed/index.html @@ -72,15 +72,26 @@

      Mixed HTML Example

      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.

      + option, tags, which can be used to add custom + behavior for specific tags. When given, it should be an object + mapping tag names (for example script) to arrays or + three-element arrays. Those inner arrays indicate [attributeName, + valueRegexp, modeSpec] + specifications. For example, you could use ["type", /^foo$/, + "foo"] to map the attribute type="foo" to + the foo mode. When the first two fields are null + ([null, null, "mode"]), the given mode is used for + any such tag that doesn't match any of the previously given + attributes. For example:

      + +
      var myModeSpec = {
      +  name: "htmlmixed",
      +  tags: {
      +    style: [["type", /^text/(x-)?scss$/, "text/x-scss"],
      +            [null, null, "css"]],
      +    custom: [[null, null, "customMode"]]
      +  }
      +}

      MIME types defined: text/html (redefined, only takes effect if you load this parser after the From 9a015790f9833dd2ef395e7fb7c505376dd46568 Mon Sep 17 00:00:00 2001 From: Adrian Heine Date: Mon, 26 Dec 2016 11:47:18 +0100 Subject: [PATCH 0590/1790] Remove unused variable This variable was unused after 0e545326ddb3a82df1b76eb18b2221990536e588. --- src/model/Doc.js | 2 -- 1 file changed, 2 deletions(-) diff --git a/src/model/Doc.js b/src/model/Doc.js index 27b62d19..006598f1 100644 --- a/src/model/Doc.js +++ b/src/model/Doc.js @@ -229,7 +229,6 @@ Doc.prototype = createObj(BranchChunk.prototype, { }), clearGutter: docMethodOp(function(gutterID) { - let i = this.first this.iter(line => { if (line.gutterMarkers && line.gutterMarkers[gutterID]) { changeLine(this, line, "gutter", () => { @@ -238,7 +237,6 @@ Doc.prototype = createObj(BranchChunk.prototype, { return true }) } - ++i }) }), From 97eb5221f05c09cfeafefa45b52da022fbcc46af Mon Sep 17 00:00:00 2001 From: Marijn Haverbeke Date: Mon, 26 Dec 2016 21:48:09 +0100 Subject: [PATCH 0591/1790] [python mode] Recognize f-strings Closes #4462 --- mode/python/python.js | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/mode/python/python.js b/mode/python/python.js index 30f1428e..4310f9fb 100644 --- a/mode/python/python.js +++ b/mode/python/python.js @@ -70,7 +70,7 @@ myBuiltins = myBuiltins.concat(["apply", "basestring", "buffer", "cmp", "coerce", "execfile", "file", "intern", "long", "raw_input", "reduce", "reload", "unichr", "unicode", "xrange", "False", "True", "None"]); - var stringPrefixes = new RegExp("^(([rub]|(ur)|(br))?('{3}|\"{3}|['\"]))", "i"); + var stringPrefixes = new RegExp("^(([rubf]|(ur)|(br))?('{3}|\"{3}|['\"]))", "i"); } var keywords = wordRegexp(myKeywords); var builtins = wordRegexp(myBuiltins); From 409c83aaf8f29a38328c4f751d4b3c460dee72a7 Mon Sep 17 00:00:00 2001 From: Manuel Rego Casasnovas Date: Tue, 27 Dec 2016 12:32:27 +0100 Subject: [PATCH 0592/1790] [css mode] Add "auto-flow" value for grid property See: https://drafts.csswg.org/css-grid/#grid-shorthand --- 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 a1d5a388..90de4ee7 100644 --- a/mode/css/css.js +++ b/mode/css/css.js @@ -589,7 +589,7 @@ CodeMirror.defineMode("css", function(config, parserConfig) { "above", "absolute", "activeborder", "additive", "activecaption", "afar", "after-white-space", "ahead", "alias", "all", "all-scroll", "alphabetic", "alternate", "always", "amharic", "amharic-abegede", "antialiased", "appworkspace", - "arabic-indic", "armenian", "asterisks", "attr", "auto", "avoid", "avoid-column", "avoid-page", + "arabic-indic", "armenian", "asterisks", "attr", "auto", "auto-flow", "avoid", "avoid-column", "avoid-page", "avoid-region", "background", "backwards", "baseline", "below", "bidi-override", "binary", "bengali", "blink", "block", "block-axis", "bold", "bolder", "border", "border-box", "both", "bottom", "break", "break-all", "break-word", "bullets", "button", "button-bevel", From ea5ee6466a916b9f6a85e480c543f877d138e626 Mon Sep 17 00:00:00 2001 From: Marijn Haverbeke Date: Wed, 28 Dec 2016 23:49:53 +0100 Subject: [PATCH 0593/1790] [groovy mode] Don't reindent block comment conent Closes #4466 --- mode/groovy/groovy.js | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/mode/groovy/groovy.js b/mode/groovy/groovy.js index 721933b0..daa79872 100644 --- a/mode/groovy/groovy.js +++ b/mode/groovy/groovy.js @@ -210,7 +210,7 @@ CodeMirror.defineMode("groovy", function(config) { }, indent: function(state, textAfter) { - if (!state.tokenize[state.tokenize.length-1].isBase) return 0; + if (!state.tokenize[state.tokenize.length-1].isBase) return CodeMirror.Pass; var firstChar = textAfter && textAfter.charAt(0), ctx = state.context; if (ctx.type == "statement" && !expectExpression(state.lastToken, true)) ctx = ctx.prev; var closing = firstChar == ctx.type; From 117ecfca5b28f9931c1407e257667972020d667c Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Oskar=20Segersv=C3=A4rd?= Date: Wed, 28 Dec 2016 13:43:55 +0100 Subject: [PATCH 0594/1790] [soy mode] Fix bug when popping scopes. --- mode/soy/soy.js | 15 +++++++++------ mode/soy/test.js | 4 +++- 2 files changed, 12 insertions(+), 7 deletions(-) diff --git a/mode/soy/soy.js b/mode/soy/soy.js index 9fd75c6d..3876333f 100644 --- a/mode/soy/soy.js +++ b/mode/soy/soy.js @@ -60,16 +60,19 @@ }; } - function pop(list) { - return list && list.next; - } - // Reference a variable `name` in `list`. // Let `loose` be truthy to ignore missing identifiers. function ref(list, name, loose) { return contains(list, name) ? "variable-2" : (loose ? "variable" : "variable-2 error"); } + function popscope(state) { + if (state.scopes) { + state.variables = state.scopes.element; + state.scopes = state.scopes.next; + } + } + return { startState: function() { return { @@ -168,11 +171,11 @@ case "tag": if (stream.match(/^\/?}/)) { if (state.tag == "/template" || state.tag == "/deltemplate") { - state.variables = state.scopes = pop(state.scopes); + popscope(state); state.indent = 0; } else { if (state.tag == "/for" || state.tag == "/foreach") { - state.variables = state.scopes = pop(state.scopes); + popscope(state); } state.indent -= config.indentUnit * (stream.current() == "/}" || indentingTags.indexOf(state.tag) == -1 ? 2 : 1); diff --git a/mode/soy/test.js b/mode/soy/test.js index 1a962de3..1a4c6c93 100644 --- a/mode/soy/test.js +++ b/mode/soy/test.js @@ -60,10 +60,12 @@ ''); MT('foreach-scope-test', + '[keyword {@param] [def bar]: [variable-3 string][keyword }]', '[keyword {foreach] [def $foo] [keyword in] [variable-2&error $foos][keyword }]', ' [keyword {][variable-2 $foo][keyword }]', '[keyword {/foreach}]', - '[keyword {][variable-2&error $foo][keyword }]'); + '[keyword {][variable-2&error $foo][keyword }]', + '[keyword {][variable-2 $bar][keyword }]'); MT('foreach-ifempty-indent-test', '[keyword {foreach] [def $foo] [keyword in] [variable-2&error $foos][keyword }]', From 7a094220148815d025fcc62f7e8a0b56316a2744 Mon Sep 17 00:00:00 2001 From: Jake Peyser Date: Wed, 28 Dec 2016 13:19:55 -0500 Subject: [PATCH 0595/1790] [materialy theme] Make general background selector more specific --- theme/material.css | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/theme/material.css b/theme/material.css index 91ed6cef..01d86793 100644 --- a/theme/material.css +++ b/theme/material.css @@ -7,7 +7,7 @@ */ -.cm-s-material { +.cm-s-material.CodeMirror { background-color: #263238; color: rgba(233, 237, 237, 1); } From 6d3783d02911d51a16bf648b602febf2ad811bc7 Mon Sep 17 00:00:00 2001 From: Marijn Haverbeke Date: Thu, 29 Dec 2016 08:54:03 +0100 Subject: [PATCH 0596/1790] [mllike mode] Don't treat every unrecognized char as a variable Closes #4473 --- mode/mllike/mllike.js | 9 ++++++--- 1 file changed, 6 insertions(+), 3 deletions(-) diff --git a/mode/mllike/mllike.js b/mode/mllike/mllike.js index bf0b8a67..4d0be609 100644 --- a/mode/mllike/mllike.js +++ b/mode/mllike/mllike.js @@ -83,9 +83,12 @@ CodeMirror.defineMode('mllike', function(_config, parserConfig) { if ( /[+\-*&%=<>!?|]/.test(ch)) { return 'operator'; } - stream.eatWhile(/\w/); - var cur = stream.current(); - return words.hasOwnProperty(cur) ? words[cur] : 'variable'; + if (/[\w\xa1-\uffff]/.test(ch)) { + stream.eatWhile(/[\w\xa1-\uffff]/); + var cur = stream.current(); + return words.hasOwnProperty(cur) ? words[cur] : 'variable'; + } + return null } function tokenString(stream, state) { From 9fe20534371a496375e409f987d2176b90c6f1e6 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Oskar=20Segersv=C3=A4rd?= Date: Thu, 29 Dec 2016 11:01:27 +0100 Subject: [PATCH 0597/1790] [soy mode] Add missing `\b`s to keyword regex Closes #4468 --- mode/soy/soy.js | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/mode/soy/soy.js b/mode/soy/soy.js index 3876333f..3e9c04a4 100644 --- a/mode/soy/soy.js +++ b/mode/soy/soy.js @@ -198,7 +198,7 @@ if (match = stream.match(/^\$([\w]+)/)) { return ref(state.variables, match[1]); } - if (stream.match(/(?:as|and|or|not|in)/)) { + if (stream.match(/\b(?:as|and|or|not|in)\b/)) { return "keyword"; } stream.next(); From 77eb24c188131a89a06990523c32bd37ccc96e80 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Oskar=20Segersv=C3=A4rd?= Date: Thu, 29 Dec 2016 11:47:11 +0100 Subject: [PATCH 0598/1790] [soy mode] Add tests for 9fe2053, find bug and fix it --- mode/soy/soy.js | 4 ++-- mode/soy/test.js | 6 ++++++ 2 files changed, 8 insertions(+), 2 deletions(-) diff --git a/mode/soy/soy.js b/mode/soy/soy.js index 3e9c04a4..b9eec59a 100644 --- a/mode/soy/soy.js +++ b/mode/soy/soy.js @@ -198,8 +198,8 @@ if (match = stream.match(/^\$([\w]+)/)) { return ref(state.variables, match[1]); } - if (stream.match(/\b(?:as|and|or|not|in)\b/)) { - return "keyword"; + if (match = stream.match(/^\w+/)) { + return /^(?:as|and|or|not|in)$/.test(match[0]) ? "keyword" : null; } stream.next(); return null; diff --git a/mode/soy/test.js b/mode/soy/test.js index 1a4c6c93..9e265c1b 100644 --- a/mode/soy/test.js +++ b/mode/soy/test.js @@ -5,6 +5,12 @@ var mode = CodeMirror.getMode({indentUnit: 2}, "soy"); function MT(name) {test.mode(name, mode, Array.prototype.slice.call(arguments, 1));} + // Test of small keywords and words containing them. + MT('keywords-test', + '[keyword {] [keyword as] worrying [keyword and] notorious [keyword as]', + ' the Fandor-alias assassin, [keyword or]', + ' Corcand cannot fit [keyword in] [keyword }]'); + MT('let-test', '[keyword {template] [def .name][keyword }]', ' [keyword {let] [def $name]: [string "world"][keyword /}]', From 4c39f5e8be2bcaa716c5c81f3db18d0db1810d76 Mon Sep 17 00:00:00 2001 From: Marijn Haverbeke Date: Mon, 2 Jan 2017 00:39:12 +0100 Subject: [PATCH 0599/1790] Make findModeByMIME +xml/+json aware Issue #4476 --- mode/meta.js | 4 +++- 1 file changed, 3 insertions(+), 1 deletion(-) diff --git a/mode/meta.js b/mode/meta.js index 47364448..bb605024 100644 --- a/mode/meta.js +++ b/mode/meta.js @@ -155,7 +155,7 @@ {name: "Verilog", mime: "text/x-verilog", mode: "verilog", ext: ["v"]}, {name: "VHDL", mime: "text/x-vhdl", mode: "vhdl", ext: ["vhd", "vhdl"]}, {name: "Vue.js Component", mimes: ["script/x-vue", "text/x-vue"], mode: "vue", ext: ["vue"]}, - {name: "XML", mimes: ["application/xml", "text/xml"], mode: "xml", ext: ["xml", "xsl", "xsd"], alias: ["rss", "wsdl", "xsd"]}, + {name: "XML", mimes: ["application/xml", "text/xml"], mode: "xml", ext: ["xml", "xsl", "xsd", "svg"], alias: ["rss", "wsdl", "xsd"]}, {name: "XQuery", mime: "application/xquery", mode: "xquery", ext: ["xy", "xquery"]}, {name: "Yacas", mime: "text/x-yacas", mode: "yacas", ext: ["ys"]}, {name: "YAML", mimes: ["text/x-yaml", "text/yaml"], mode: "yaml", ext: ["yaml", "yml"], alias: ["yml"]}, @@ -178,6 +178,8 @@ if (info.mimes) for (var j = 0; j < info.mimes.length; j++) if (info.mimes[j] == mime) return info; } + if (/\+xml$/.test(mime)) return CodeMirror.findModeByMIME("application/xml") + if (/\+json$/.test(mime)) return CodeMirror.findModeByMIME("application/json") }; CodeMirror.findModeByExtension = function(ext) { From ec8e89ba441495c79710c5fda7fd05d61a6787b6 Mon Sep 17 00:00:00 2001 From: Marijn Haverbeke Date: Mon, 2 Jan 2017 00:45:36 +0100 Subject: [PATCH 0600/1790] [closebrackets addon] Add override option Closes #4478 --- addon/edit/closebrackets.js | 2 +- doc/manual.html | 8 +++++++- 2 files changed, 8 insertions(+), 2 deletions(-) diff --git a/addon/edit/closebrackets.js b/addon/edit/closebrackets.js index 7c47bcd0..62b99c1b 100644 --- a/addon/edit/closebrackets.js +++ b/addon/edit/closebrackets.js @@ -45,7 +45,7 @@ function getConfig(cm) { var deflt = cm.state.closeBrackets; - if (!deflt) return null; + if (!deflt || deflt.override) return deflt; var mode = cm.getModeAt(cm.getCursor()); return mode.closeBrackets || deflt; } diff --git a/doc/manual.html b/doc/manual.html index 98c16094..3c252381 100644 --- a/doc/manual.html +++ b/doc/manual.html @@ -2314,7 +2314,13 @@

      Addons

      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.
    + line. By default, if the active mode has + a closeBrackets property, that overrides the + configuration given in the option. But you can add + an override property with a truthy value to + override mode-specific + configuration. Demo + here.
    edit/matchtags.js
    Defines an option matchTags that, when enabled, From 7ff3b02e724300d61d6a7b81eb8139778060a529 Mon Sep 17 00:00:00 2001 From: ficristo Date: Mon, 2 Jan 2017 09:00:08 +0100 Subject: [PATCH 0601/1790] [javascript mode] add tests for async keyword --- mode/javascript/test.js | 30 ++++++++++++++++++++++++++++++ 1 file changed, 30 insertions(+) diff --git a/mode/javascript/test.js b/mode/javascript/test.js index 2caefea4..56b90e3c 100644 --- a/mode/javascript/test.js +++ b/mode/javascript/test.js @@ -190,6 +190,36 @@ " }", "}"); + MT("async", + "[keyword async] [keyword function] [def foo]([def args]) { [keyword return] [atom true]; }"); + + MT("async_assignment", + "[keyword const] [def foo] [operator =] [keyword async] [keyword function] ([def args]) { [keyword return] [atom true]; };"); + + MT("async_object", + "[keyword let] [def obj] [operator =] { [property async]: [atom false] };"); + + // async be highlighet as keyword and foo as def, but it requires potentially expensive look-ahead. See #4173 + MT("async_object_function", + "[keyword let] [def obj] [operator =] { [property async] [property foo]([def args]) { [keyword return] [atom true]; } };"); + + MT("async_object_properties", + "[keyword let] [def obj] [operator =] {", + " [property prop1]: [keyword async] [keyword function] ([def args]) { [keyword return] [atom true]; },", + " [property prop2]: [keyword async] [keyword function] ([def args]) { [keyword return] [atom true]; },", + " [property prop3]: [keyword async] [keyword function] [def prop3]([def args]) { [keyword return] [atom true]; },", + "};"); + + MT("async_arrow", + "[keyword const] [def foo] [operator =] [keyword async] ([def args]) [operator =>] { [keyword return] [atom true]; };"); + + MT("async_jquery", + "[variable $].[property ajax]({", + " [property url]: [variable url],", + " [property async]: [atom true],", + " [property method]: [string 'GET']", + "});"); + var ts_mode = CodeMirror.getMode({indentUnit: 2}, "application/typescript") function TS(name) { test.mode(name, ts_mode, Array.prototype.slice.call(arguments, 1)) From 90d7915450f33f03d0d251b6bdda7228289acd41 Mon Sep 17 00:00:00 2001 From: Paul Masson Date: Tue, 3 Jan 2017 14:03:42 -0800 Subject: [PATCH 0602/1790] Update LICENSE --- LICENSE | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/LICENSE b/LICENSE index 76613217..1bca6bfe 100644 --- a/LICENSE +++ b/LICENSE @@ -1,4 +1,4 @@ -Copyright (C) 2016 by Marijn Haverbeke and others +Copyright (C) 2017 by Marijn Haverbeke and others 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 d4e2e7ac0b745b04a82cd2f2847c44951b98822a Mon Sep 17 00:00:00 2001 From: Paul Masson Date: Tue, 3 Jan 2017 14:11:38 -0800 Subject: [PATCH 0603/1790] [real-world uses] Add SageMathCell --- doc/realworld.html | 1 + 1 file changed, 1 insertion(+) diff --git a/doc/realworld.html b/doc/realworld.html index dc2a7f6a..2429ba16 100644 --- a/doc/realworld.html +++ b/doc/realworld.html @@ -142,6 +142,7 @@

    CodeMirror real-world uses

  • Rascal (tiny computer)
  • RealTime.io (Internet-of-Things infrastructure)
  • Refork (animation demo gallery and sharing)
  • +
  • SageMathCell (interactive mathematical software)
  • SageMathCloud (interactive mathematical software environment)
  • ServePHP (PHP code testing in Chrome dev tools)
  • Shadertoy (shader sharing)
  • From 3a83dccdfa96e2dc90c3129d281525ff41241255 Mon Sep 17 00:00:00 2001 From: Marijn Haverbeke Date: Wed, 4 Jan 2017 09:58:53 +0100 Subject: [PATCH 0604/1790] [markdown mode] Be somewhat more restrictive about HTML open tags Closes #4484 --- mode/markdown/markdown.js | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/mode/markdown/markdown.js b/mode/markdown/markdown.js index 4cc1dc68..1aeb3441 100644 --- a/mode/markdown/markdown.js +++ b/mode/markdown/markdown.js @@ -490,7 +490,7 @@ CodeMirror.defineMode("markdown", function(cmCfg, modeCfg) { return type + tokenTypes.linkEmail; } - if (ch === '<' && stream.match(/^(!--|\w)/, false)) { + if (ch === '<' && stream.match(/^(!--|[a-z]+(?:\s+[a-z_:.\-]+(?:\s*=\s*[^ >]+)?)*\s*>)/i, false)) { var end = stream.string.indexOf(">", stream.pos); if (end != -1) { var atts = stream.string.substring(stream.start, end); From 6928fec6695f468f17b5031e48192ad2f7d505f1 Mon Sep 17 00:00:00 2001 From: Marijn Haverbeke Date: Wed, 4 Jan 2017 10:59:11 +0100 Subject: [PATCH 0605/1790] [panel addon] Implement a 'stable' option Issue #4485 --- addon/display/panel.js | 9 +++++++++ demo/panel.html | 4 ++-- doc/manual.html | 11 +++++++---- 3 files changed, 18 insertions(+), 6 deletions(-) diff --git a/addon/display/panel.js b/addon/display/panel.js index ba29484d..a6ac74f0 100644 --- a/addon/display/panel.js +++ b/addon/display/panel.js @@ -38,6 +38,9 @@ var height = (options && options.height) || node.offsetHeight; this._setSize(null, info.heightLeft -= height); info.panels++; + if (options.stable && isAtTop(this, node)) + this.scrollTo(null, this.getScrollInfo().top + height) + return new Panel(this, node, options, height); }); @@ -109,4 +112,10 @@ cm.setSize = cm._setSize; cm.setSize(); } + + function isAtTop(cm, dom) { + for (let sibling = dom.nextSibling; sibling; sibling = sibling.nextSibling) + if (sibling == cm.getWrapperElement()) return true + return false + } }); diff --git a/demo/panel.html b/demo/panel.html index b3b0b7ca..1ce3d87c 100644 --- a/demo/panel.html +++ b/demo/panel.html @@ -115,7 +115,7 @@

    Panel Demo

    } function addPanel(where) { var node = makePanel(where); - panels[node.id] = editor.addPanel(node, {position: where}); + panels[node.id] = editor.addPanel(node, {position: where, stable: true}); } addPanel("top"); @@ -126,7 +126,7 @@

    Panel Demo

    var panel = panels["panel-" + id]; var node = makePanel(""); - panels[node.id] = editor.addPanel(node, {replace: panel, position: "after-top"}); + panels[node.id] = editor.addPanel(node, {replace: panel, position: "after-top", stable: true}); return false; } diff --git a/doc/manual.html b/doc/manual.html index 3c252381..7acc872b 100644 --- a/doc/manual.html +++ b/doc/manual.html @@ -2910,7 +2910,7 @@

    Addons

    changed.
    The method accepts the following options:
    -
    position : string
    +
    position: string
    Controls the position of the newly added panel. The following values are recognized:
    @@ -2924,12 +2924,15 @@

    Addons

    Adds the panel at the top of the bottom panels.
    -
    before : Panel
    +
    before: Panel
    The new panel will be added before the given panel.
    -
    after : Panel
    +
    after: Panel
    The new panel will be added after the given panel.
    -
    replace : Panel
    +
    replace: Panel
    The new panel will replace the given panel.
    +
    stable: bool
    +
    Whether to scroll the editor to keep the text's vertical + position stable, when adding a panel above it. Defaults to false.
    When using the after, before or replace options, if the panel doesn't exists or has been removed, From 35f09250d7d3dbb3da28aa3a7efdf757bdfdc42f Mon Sep 17 00:00:00 2001 From: Marijn Haverbeke Date: Wed, 4 Jan 2017 11:02:56 +0100 Subject: [PATCH 0606/1790] Fix ES6-ism in addon --- addon/display/panel.js | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/addon/display/panel.js b/addon/display/panel.js index a6ac74f0..7da34a42 100644 --- a/addon/display/panel.js +++ b/addon/display/panel.js @@ -114,7 +114,7 @@ } function isAtTop(cm, dom) { - for (let sibling = dom.nextSibling; sibling; sibling = sibling.nextSibling) + for (var sibling = dom.nextSibling; sibling; sibling = sibling.nextSibling) if (sibling == cm.getWrapperElement()) return true return false } From f78c0915e5c403c925e48c714fd1fd671a4c35f0 Mon Sep 17 00:00:00 2001 From: Marijn Haverbeke Date: Wed, 4 Jan 2017 11:03:50 +0100 Subject: [PATCH 0607/1790] Lint addons and modes with ecmaVersion 5 --- test/lint.js | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-) diff --git a/test/lint.js b/test/lint.js index 12146ffd..502706de 100644 --- a/test/lint.js +++ b/test/lint.js @@ -3,7 +3,8 @@ var blint = require("blint"); ["mode", "lib", "addon", "keymap"].forEach(function(dir) { blint.checkDir(dir, { browser: true, - allowedGlobals: ["CodeMirror", "define", "test", "requirejs"] + allowedGlobals: ["CodeMirror", "define", "test", "requirejs"], + ecmaVersion: 5 }); }); From 0fb17df6694b0ba63ec0568d84709e9a07e19a9b Mon Sep 17 00:00:00 2001 From: Marijn Haverbeke Date: Thu, 5 Jan 2017 09:44:27 +0100 Subject: [PATCH 0608/1790] [panel plugin] Make stable option also take effect when a panel is removed Issue #4485 --- addon/display/panel.js | 2 ++ 1 file changed, 2 insertions(+) diff --git a/addon/display/panel.js b/addon/display/panel.js index 7da34a42..74199ff0 100644 --- a/addon/display/panel.js +++ b/addon/display/panel.js @@ -57,6 +57,8 @@ this.cleared = true; var info = this.cm.state.panels; this.cm._setSize(null, info.heightLeft += this.height); + if (this.options.stable && isAtTop(this.cm, this.node)) + this.cm.scrollTo(null, this.cm.getScrollInfo().top - this.height) info.wrapper.removeChild(this.node); if (--info.panels == 0) removePanels(this.cm); }; From dc94b0f0c6ea97c09344d1a34da70f8e2d1d708f Mon Sep 17 00:00:00 2001 From: Marijn Haverbeke Date: Thu, 12 Jan 2017 14:35:32 +0100 Subject: [PATCH 0609/1790] [merge addon] Anchor copy button to editable side for insertions Since showing it next to an empty chunk looks bad Issue #4492 --- addon/merge/merge.js | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/addon/merge/merge.js b/addon/merge/merge.js index 352e27dc..b3a9af40 100644 --- a/addon/merge/merge.js +++ b/addon/merge/merge.js @@ -431,10 +431,10 @@ var editOriginals = dv.mv.options.allowEditingOriginals; copy.title = editOriginals ? "Push to left" : "Revert chunk"; copy.chunk = chunk; - copy.style.top = top + "px"; + copy.style.top = (chunk.origTo < chunk.origFrom ? top : dv.edit.heightAtLine(chunk.editFrom, "local") - sTopEdit) + "px"; if (editOriginals) { - var topReverse = dv.orig.heightAtLine(chunk.editFrom, "local") - sTopEdit; + var topReverse = dv.edit.heightAtLine(chunk.editFrom, "local") - sTopEdit; var copyReverse = dv.copyButtons.appendChild(elt("div", dv.type == "right" ? "\u21dd" : "\u21dc", "CodeMirror-merge-copy-reverse")); copyReverse.title = "Push to right"; From fc93599160949802e8752ecf32b63e24c0c1b461 Mon Sep 17 00:00:00 2001 From: Marijn Haverbeke Date: Thu, 12 Jan 2017 14:37:39 +0100 Subject: [PATCH 0610/1790] Don't exclude rollup.config.js from NPM See https://discuss.codemirror.net/t/npm-install-failing-for-cm-git-dependency/1088 --- .npmignore | 1 - 1 file changed, 1 deletion(-) diff --git a/.npmignore b/.npmignore index de3a2408..f23ca719 100644 --- a/.npmignore +++ b/.npmignore @@ -9,4 +9,3 @@ /mode/index.html .* bin -rollup.config.js From 8a0c813b208b5a2c0dae7b5a9b635168017165ba Mon Sep 17 00:00:00 2001 From: Marijn Haverbeke Date: Thu, 12 Jan 2017 14:40:43 +0100 Subject: [PATCH 0611/1790] Include 5.22.2 in release notes --- CHANGELOG.md | 6 ++++++ doc/manual.html | 2 +- package.json | 2 +- src/edit/main.js | 2 +- 4 files changed, 9 insertions(+), 3 deletions(-) diff --git a/CHANGELOG.md b/CHANGELOG.md index f3eb79ff..9074c1b0 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -1,3 +1,9 @@ +## 5.22.2 (2017-01-12) + +### Bug fixes + +Include rollup.config.js in NPM package, so that it can be used to build from source. + ## 5.22.0 (2016-12-20) ### Bug fixes diff --git a/doc/manual.html b/doc/manual.html index 7acc872b..f765185e 100644 --- a/doc/manual.html +++ b/doc/manual.html @@ -69,7 +69,7 @@

    User manual and reference guide - version 5.22.1 + version 5.22.3

    CodeMirror is a code-editor component that can be embedded in diff --git a/package.json b/package.json index 0c565e9b..fa82dd11 100644 --- a/package.json +++ b/package.json @@ -1,6 +1,6 @@ { "name": "codemirror", - "version": "5.22.1", + "version": "5.22.3", "main": "lib/codemirror.js", "description": "Full-featured in-browser code editor", "license": "MIT", diff --git a/src/edit/main.js b/src/edit/main.js index 429cfe94..c16f3771 100644 --- a/src/edit/main.js +++ b/src/edit/main.js @@ -66,4 +66,4 @@ import { addLegacyProps } from "./legacy" addLegacyProps(CodeMirror) -CodeMirror.version = "5.22.1" +CodeMirror.version = "5.22.3" From 66d9403c8e5511b5469e1009534e8266296d64ed Mon Sep 17 00:00:00 2001 From: Marijn Haverbeke Date: Fri, 13 Jan 2017 08:19:44 +0100 Subject: [PATCH 0612/1790] [merge addon] Fix incorrect compare Issue #4492 --- 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 b3a9af40..a8132b60 100644 --- a/addon/merge/merge.js +++ b/addon/merge/merge.js @@ -431,7 +431,7 @@ var editOriginals = dv.mv.options.allowEditingOriginals; copy.title = editOriginals ? "Push to left" : "Revert chunk"; copy.chunk = chunk; - copy.style.top = (chunk.origTo < chunk.origFrom ? top : dv.edit.heightAtLine(chunk.editFrom, "local") - sTopEdit) + "px"; + copy.style.top = (chunk.origTo > chunk.origFrom ? top : dv.edit.heightAtLine(chunk.editFrom, "local") - sTopEdit) + "px"; if (editOriginals) { var topReverse = dv.edit.heightAtLine(chunk.editFrom, "local") - sTopEdit; From 379c1aeb941b8403c51be15a8e64c614ed61143f Mon Sep 17 00:00:00 2001 From: Martin Zagora Date: Mon, 16 Jan 2017 14:59:51 +1100 Subject: [PATCH 0613/1790] TypeScript: add a test for usage of interface syntax with const --- mode/javascript/test.js | 6 ++++++ 1 file changed, 6 insertions(+) diff --git a/mode/javascript/test.js b/mode/javascript/test.js index 56b90e3c..c02eb06c 100644 --- a/mode/javascript/test.js +++ b/mode/javascript/test.js @@ -261,6 +261,12 @@ " ) [operator =>] [variable-3 Promise] [operator <] [variable-3 AccountHolderNotificationPreferenceInstance] [operator >];", " }") + TS("typescript_interface_with_const", + "[keyword const] [def hello]: {", + " [property prop1][operator ?]: [variable-3 string];", + " [property prop2][operator ?]: [variable-3 string];", + "} [operator =] {};") + var jsonld_mode = CodeMirror.getMode( {indentUnit: 2}, {name: "javascript", jsonld: true} From 7166a44efbfcc9b84dc9efb4ec36da478370663d Mon Sep 17 00:00:00 2001 From: Marijn Haverbeke Date: Mon, 16 Jan 2017 21:47:45 +0100 Subject: [PATCH 0614/1790] [javascript mode] Improve TypeScript interface type parsing Issue #4494 --- mode/javascript/javascript.js | 10 ++++++---- 1 file changed, 6 insertions(+), 4 deletions(-) diff --git a/mode/javascript/javascript.js b/mode/javascript/javascript.js index c185106c..17890dcc 100644 --- a/mode/javascript/javascript.js +++ b/mode/javascript/javascript.js @@ -505,9 +505,9 @@ CodeMirror.defineMode("javascript", function(config, parserConfig) { if (type == ":") return cont(expressionNoComma); if (type == "(") return pass(functiondef); } - function commasep(what, end) { + function commasep(what, end, sep) { function proceed(type, value) { - if (type == ",") { + if (sep ? sep.indexOf(type) > -1 : type == ",") { var lex = cx.state.lexical; if (lex.info == "call") lex.pos = (lex.pos || 0) + 1; return cont(function(type, value) { @@ -541,16 +541,18 @@ CodeMirror.defineMode("javascript", function(config, parserConfig) { function typeexpr(type) { if (type == "variable") {cx.marked = "variable-3"; return cont(afterType);} if (type == "string" || type == "number" || type == "atom") return cont(afterType); - if (type == "{") return cont(commasep(typeprop, "}")) + if (type == "{") return cont(pushlex("}"), commasep(typeprop, "}", ",;"), poplex) if (type == "(") return cont(commasep(typearg, ")"), maybeReturnType) } function maybeReturnType(type) { if (type == "=>") return cont(typeexpr) } - function typeprop(type) { + function typeprop(type, value) { if (type == "variable" || cx.style == "keyword") { cx.marked = "property" return cont(typeprop) + } else if (value == "?") { + return cont(typeprop) } else if (type == ":") { return cont(typeexpr) } From 6709974d3b01dc6aa153de37edf4170b0431b1f6 Mon Sep 17 00:00:00 2001 From: Zeno Rocha Date: Wed, 18 Jan 2017 06:28:31 -0800 Subject: [PATCH 0615/1790] [dracula theme] Adjust colors, remove duplicate definition --- theme/dracula.css | 5 ++--- 1 file changed, 2 insertions(+), 3 deletions(-) diff --git a/theme/dracula.css b/theme/dracula.css index b2ef6291..53a660b5 100644 --- a/theme/dracula.css +++ b/theme/dracula.css @@ -24,8 +24,7 @@ .cm-s-dracula span.cm-number { color: #bd93f9; } .cm-s-dracula span.cm-variable { color: #50fa7b; } .cm-s-dracula span.cm-variable-2 { color: white; } -.cm-s-dracula span.cm-def { color: #ffb86c; } -.cm-s-dracula span.cm-keyword { color: #ff79c6; } +.cm-s-dracula span.cm-def { color: #50fa7b; } .cm-s-dracula span.cm-operator { color: #ff79c6; } .cm-s-dracula span.cm-keyword { color: #ff79c6; } .cm-s-dracula span.cm-atom { color: #bd93f9; } @@ -35,7 +34,7 @@ .cm-s-dracula span.cm-qualifier { color: #50fa7b; } .cm-s-dracula span.cm-property { color: #66d9ef; } .cm-s-dracula span.cm-builtin { color: #50fa7b; } -.cm-s-dracula span.cm-variable-3 { color: #50fa7b; } +.cm-s-dracula span.cm-variable-3 { color: #ffb86c; } .cm-s-dracula .CodeMirror-activeline-background { background: rgba(255,255,255,0.1); } .cm-s-dracula .CodeMirror-matchingbracket { text-decoration: underline; color: white !important; } From 73c4e24a8e6c0bb078f5fb497edb484bd5523f12 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Jakub=20Vr=C3=A1na?= Date: Tue, 20 Dec 2016 16:45:20 +0100 Subject: [PATCH 0616/1790] [sublime bindings] Don't sort last line with no selected chars Selecting two full lines including line ends makes a selection from line 1 to line 3 (ch: 0). This command used to sort three lines (1 to 3) which is not what I expect. This change makes it sort only two lines if there are no selected characters on the last line. It also fixes a fatal error if there are more than one selection on the same line (the code used 'range' instead of 'ranges'). It also selects the trailing newline after sorting the lines so that the whole lines are selected. --- keymap/sublime.js | 5 +++-- test/sublime_test.js | 8 ++++---- 2 files changed, 7 insertions(+), 6 deletions(-) diff --git a/keymap/sublime.js b/keymap/sublime.js index c5d2906b..3d112ab9 100644 --- a/keymap/sublime.js +++ b/keymap/sublime.js @@ -310,7 +310,8 @@ if (range.empty()) continue; var from = range.from().line, to = range.to().line; while (i < ranges.length - 1 && ranges[i + 1].from().line == to) - to = range[++i].to().line; + to = ranges[++i].to().line; + if (!ranges[i].to().ch) to--; toSort.push(from, to); } if (toSort.length) selected = true; @@ -331,7 +332,7 @@ return a < b ? -1 : a == b ? 0 : 1; }); cm.replaceRange(lines, start, end); - if (selected) ranges.push({anchor: start, head: end}); + if (selected) ranges.push({anchor: start, head: Pos(to + 1, 0)}); } if (selected) cm.setSelections(ranges, 0); }); diff --git a/test/sublime_test.js b/test/sublime_test.js index c5c19c0a..57f16485 100644 --- a/test/sublime_test.js +++ b/test/sublime_test.js @@ -249,11 +249,11 @@ "undo", setSel(0, 0, 2, 0, 3, 0, 5, 0), - "sortLines", val("a\nb\nc\nA\nB\nC"), - hasSel(0, 0, 2, 1, - 3, 0, 5, 1), + "sortLines", val("b\nc\na\nB\nC\nA"), + hasSel(0, 0, 2, 0, + 3, 0, 5, 0), "undo", - setSel(1, 0, 4, 0), "sortLinesInsensitive", val("c\na\nB\nb\nC\nA")); + setSel(1, 0, 5, 0), "sortLinesInsensitive", val("c\na\nB\nb\nC\nA")); stTest("bookmarks", "abc\ndef\nghi\njkl", Pos(0, 1), "toggleBookmark", From 73638df40c9631155b1985f0ee63b6ffe2398b6a Mon Sep 17 00:00:00 2001 From: Marijn Haverbeke Date: Thu, 19 Jan 2017 23:35:01 +0100 Subject: [PATCH 0617/1790] Mark version 5.23.0 --- AUTHORS | 3 +++ CHANGELOG.md | 16 ++++++++++++++++ doc/manual.html | 2 +- doc/releases.html | 34 ++++++++++++++++++++++------------ index.html | 2 +- package.json | 2 +- src/edit/main.js | 2 +- 7 files changed, 45 insertions(+), 16 deletions(-) diff --git a/AUTHORS b/AUTHORS index 9b9b3355..866c78f8 100644 --- a/AUTHORS +++ b/AUTHORS @@ -175,6 +175,7 @@ eborden edsharp ekhaled Elisée +Emmanuel Schanzer Enam Mijbah Noor Eric Allam Erik Welander @@ -247,6 +248,7 @@ Irakli Gozalishvili Ivan Kurnosov Ivoah Jacob Lee +Jake Peyser Jakob Miland Jakub Vrana Jakub Vrána @@ -617,6 +619,7 @@ Yunchi Luo Yuvi Panda Zac Anger Zachary Dremann +Zeno Rocha Zhang Hao zziuni 魏鹏刚 diff --git a/CHANGELOG.md b/CHANGELOG.md index 9074c1b0..07ed0295 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -1,3 +1,19 @@ +## 5.23.0 (2017-01-19) + +### Bug fixes + +Presentation-related elements DOM elements are now marked as such to help screen readers. + +[markdown mode](http://codemirror.net/mode/markdown/): Be more picky about what HTML tags look like to avoid false positives. + +### New features + +`findModeByMIME` now understands `+json` and `+xml` MIME suffixes. + +[closebrackets addon](http://codemirror.net/doc/manual.html#addon_closebrackets): Add support for an `override` option to ignore language-specific defaults. + +[panel addon](http://codemirror.net/doc/manual.html#addon_panel): Add a `stable` option that auto-scrolls the content to keep it in the same place when inserting/removing a panel. + ## 5.22.2 (2017-01-12) ### Bug fixes diff --git a/doc/manual.html b/doc/manual.html index f765185e..a5eb2233 100644 --- a/doc/manual.html +++ b/doc/manual.html @@ -69,7 +69,7 @@

    User manual and reference guide - version 5.22.3 + version 5.23.0

    CodeMirror is a code-editor component that can be embedded in diff --git a/doc/releases.html b/doc/releases.html index 2dfd1fe4..69c0e66b 100644 --- a/doc/releases.html +++ b/doc/releases.html @@ -30,18 +30,28 @@

    Release notes and version history

    Version 5.x

    -

    20-12-2016: Version 5.22.0:

    - -
      -
    • sublime bindings: Make selectBetweenBrackets work with multiple cursors.
    • -
    • javascript mode: Fix issues with parsing complex TypeScript types, imports, and exports.
    • -
    • A contentEditable editor instance with autofocus enabled no longer crashes during initializing.
    • -
    • emacs bindings: Export CodeMirror.emacs to allow other addons to hook into Emacs-style functionality.
    • -
    • active-line addon: Add nonEmpty option.
    • -
    • New event: optionChange.
    • -
    - -

    21-11-2016: Version 5.21.0:

    +

    19-01-2017: Version 5.23.0:

    + +
      +
    • Presentation-related elements DOM elements are now marked as such to help screen readers.
    • +
    • markdown mode: Be more picky about what HTML tags look like to avoid false positives.
    • +
    • findModeByMIME now understands +json and +xml MIME suffixes.
    • +
    • closebrackets addon: Add support for an override option to ignore language-specific defaults.
    • +
    • panel addon: Add a stable option that auto-scrolls the content to keep it in the same place when inserting/removing a panel.
    • +
    + +

    20-12-2016: Version 5.22.0:

    + +
      +
    • sublime bindings: Make selectBetweenBrackets work with multiple cursors.
    • +
    • javascript mode: Fix issues with parsing complex TypeScript types, imports, and exports.
    • +
    • A contentEditable editor instance with autofocus enabled no longer crashes during initializing.
    • +
    • emacs bindings: Export CodeMirror.emacs to allow other addons to hook into Emacs-style functionality.
    • +
    • active-line addon: Add nonEmpty option.
    • +
    • New event: optionChange.
    • +
    + +

    21-11-2016: Version 5.21.0:

    • Tapping/clicking the editor in contentEditable mode on Chrome now puts the cursor at the tapped position.
    • diff --git a/index.html b/index.html index 9812829e..df98ffd0 100644 --- a/index.html +++ b/index.html @@ -96,7 +96,7 @@

      This is CodeMirror

      - Get the current version: 5.22.1.
      + Get the current version: 5.23.0.
      You can see the code,
      read the release notes,
      or study the user manual. diff --git a/package.json b/package.json index fa82dd11..f7b617cd 100644 --- a/package.json +++ b/package.json @@ -1,6 +1,6 @@ { "name": "codemirror", - "version": "5.22.3", + "version": "5.23.0", "main": "lib/codemirror.js", "description": "Full-featured in-browser code editor", "license": "MIT", diff --git a/src/edit/main.js b/src/edit/main.js index c16f3771..b6d3d488 100644 --- a/src/edit/main.js +++ b/src/edit/main.js @@ -66,4 +66,4 @@ import { addLegacyProps } from "./legacy" addLegacyProps(CodeMirror) -CodeMirror.version = "5.22.3" +CodeMirror.version = "5.23.0" From 82162728938e8536d39a3f9937d7eb905e00eb9b Mon Sep 17 00:00:00 2001 From: Marijn Haverbeke Date: Fri, 20 Jan 2017 08:25:33 +0100 Subject: [PATCH 0618/1790] [shell mode] Improve tokenizing of $'' strings Closes #4505 --- mode/shell/shell.js | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/mode/shell/shell.js b/mode/shell/shell.js index 570b4e24..a6363873 100644 --- a/mode/shell/shell.js +++ b/mode/shell/shell.js @@ -108,8 +108,8 @@ CodeMirror.defineMode('shell', function() { if (state.tokens.length > 1) stream.eat('$'); var ch = stream.next(), hungry = /\w/; if (ch === '{') hungry = /[^}]/; - if (ch === '(') { - state.tokens[0] = tokenString(')'); + if (/['"(]/.test(ch)) { + state.tokens[0] = tokenString(ch == "(" ? ")" : ch); return tokenize(stream, state); } if (!/\d/.test(ch)) { From 39ad3b619ba9ea481f2847fb85aed647b67d17d1 Mon Sep 17 00:00:00 2001 From: Marijn Haverbeke Date: Fri, 20 Jan 2017 22:12:11 +0100 Subject: [PATCH 0619/1790] [python mode] Accept underscores in number literals Closes #4506 --- mode/python/python.js | 12 ++++++------ 1 file changed, 6 insertions(+), 6 deletions(-) diff --git a/mode/python/python.js b/mode/python/python.js index 4310f9fb..b539d84a 100644 --- a/mode/python/python.js +++ b/mode/python/python.js @@ -113,8 +113,8 @@ if (stream.match(/^[0-9\.]/, false)) { var floatLiteral = false; // Floats - if (stream.match(/^\d*\.\d+(e[\+\-]?\d+)?/i)) { floatLiteral = true; } - if (stream.match(/^\d+\.\d*/)) { floatLiteral = true; } + if (stream.match(/^[\d_]*\.\d+(e[\+\-]?\d+)?/i)) { floatLiteral = true; } + if (stream.match(/^[\d_]+\.\d*/)) { floatLiteral = true; } if (stream.match(/^\.\d+/)) { floatLiteral = true; } if (floatLiteral) { // Float literals may be "imaginary" @@ -124,13 +124,13 @@ // Integers var intLiteral = false; // Hex - if (stream.match(/^0x[0-9a-f]+/i)) intLiteral = true; + if (stream.match(/^0x[0-9a-f_]+/i)) intLiteral = true; // Binary - if (stream.match(/^0b[01]+/i)) intLiteral = true; + if (stream.match(/^0b[01_]+/i)) intLiteral = true; // Octal - if (stream.match(/^0o[0-7]+/i)) intLiteral = true; + if (stream.match(/^0o[0-7_]+/i)) intLiteral = true; // Decimal - if (stream.match(/^[1-9]\d*(e[\+\-]?\d+)?/)) { + if (stream.match(/^[1-9][\d_]*(e[\+\-]?[\d_]+)?/)) { // Decimal literals may be "imaginary" stream.eat(/J/i); // TODO - Can you have imaginary longs? From 71af74da531f7ac7b08e6fcad37892dbf5375664 Mon Sep 17 00:00:00 2001 From: ficristo Date: Thu, 22 Dec 2016 16:33:50 +0100 Subject: [PATCH 0620/1790] [sass mode] Use same token types as CSS/SCSS mode. Add tests --- mode/sass/index.html | 4 +- mode/sass/sass.js | 71 +++++++++++++++++++++-------- mode/sass/test.js | 103 +++++++++++++++++++++++++++++++++++++++++++ test/index.html | 2 + 4 files changed, 161 insertions(+), 19 deletions(-) create mode 100644 mode/sass/test.js diff --git a/mode/sass/index.html b/mode/sass/index.html index 9f4a7902..6305649e 100644 --- a/mode/sass/index.html +++ b/mode/sass/index.html @@ -7,6 +7,7 @@ +
    skipToEnd()
    Moves the position to the end of the line.
    -
    skipTo(ch: string) → boolean
    -
    Skips to the next occurrence of the given character, if +
    skipTo(str: string) → boolean
    +
    Skips to the start of the next occurrence of the given string, 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.
    + string does not occur on the line). Returns true if the + string was found.
    match(pattern: string, ?consume: boolean, ?caseFold: boolean) → boolean
    match(pattern: regexp, ?consume: boolean) → array<string>
    Act like a From 4206f287cb798df5511159ebfebcb27013cf54b0 Mon Sep 17 00:00:00 2001 From: Marijn Haverbeke Date: Thu, 2 Feb 2017 11:41:27 +0100 Subject: [PATCH 0628/1790] [markdown mode] Understand end-of-fenced-code inside other markup Closes #4528 --- mode/markdown/markdown.js | 5 +++-- 1 file changed, 3 insertions(+), 2 deletions(-) diff --git a/mode/markdown/markdown.js b/mode/markdown/markdown.js index 1aeb3441..fde258fe 100644 --- a/mode/markdown/markdown.js +++ b/mode/markdown/markdown.js @@ -239,10 +239,11 @@ CodeMirror.defineMode("markdown", function(cmCfg, modeCfg) { } function local(stream, state) { - if (state.fencedChars && stream.match(state.fencedChars, false)) { + if (state.fencedChars && stream.skipTo(state.fencedChars)) { + stream.match(state.fencedChars) state.localMode = state.localState = null; state.f = state.block = leavingLocal; - return null; + return "comment"; } else if (state.localMode) { return state.localMode.token(stream, state.localState); } else { From 6ea8a906d94994f34faacdde282988a8bb589f94 Mon Sep 17 00:00:00 2001 From: Marijn Haverbeke Date: Thu, 2 Feb 2017 11:53:27 +0100 Subject: [PATCH 0629/1790] [markdown mode] Fix styling of closing code block fence Issue #4528 --- mode/markdown/markdown.js | 8 +++++--- 1 file changed, 5 insertions(+), 3 deletions(-) diff --git a/mode/markdown/markdown.js b/mode/markdown/markdown.js index fde258fe..483422a9 100644 --- a/mode/markdown/markdown.js +++ b/mode/markdown/markdown.js @@ -239,11 +239,13 @@ CodeMirror.defineMode("markdown", function(cmCfg, modeCfg) { } function local(stream, state) { - if (state.fencedChars && stream.skipTo(state.fencedChars)) { - stream.match(state.fencedChars) + if (state.fencedChars && stream.match(state.fencedChars)) { + if (modeCfg.highlightFormatting) state.formatting = "code-block"; state.localMode = state.localState = null; state.f = state.block = leavingLocal; - return "comment"; + return getType(state) + } else if (state.fencedChars && stream.skipTo(state.fencedChars)) { + return "commment" } else if (state.localMode) { return state.localMode.token(stream, state.localState); } else { From b4820dd94c9a0ab40bfcccb72feb95d424729e12 Mon Sep 17 00:00:00 2001 From: Marijn Haverbeke Date: Thu, 2 Feb 2017 11:55:59 +0100 Subject: [PATCH 0630/1790] [dylan mode] Remove dependency on ES5 --- mode/dylan/dylan.js | 10 +++++++--- 1 file changed, 7 insertions(+), 3 deletions(-) diff --git a/mode/dylan/dylan.js b/mode/dylan/dylan.js index 1b46bc82..4520c14e 100644 --- a/mode/dylan/dylan.js +++ b/mode/dylan/dylan.js @@ -11,6 +11,10 @@ })(function(CodeMirror) { "use strict"; +function forEach(arr, f) { + for (let i = 0; i < arr.length; i++) f(arr[i], i) +} + CodeMirror.defineMode("dylan", function(_config) { // Words var words = { @@ -136,13 +140,13 @@ CodeMirror.defineMode("dylan", function(_config) { var wordLookup = {}; var styleLookup = {}; - [ + forEach([ "keyword", "definition", "simpleDefinition", "signalingCalls" - ].forEach(function(type) { - words[type].forEach(function(word) { + ], function(type) { + forEach(words[type], function(word) { wordLookup[word] = type; styleLookup[word] = styles[type]; }); From c8c77f97e661ad7ae578e95b34ec462bc5ff1990 Mon Sep 17 00:00:00 2001 From: Marijn Haverbeke Date: Thu, 2 Feb 2017 11:59:19 +0100 Subject: [PATCH 0631/1790] [dylan mode] let -> var --- mode/dylan/dylan.js | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/mode/dylan/dylan.js b/mode/dylan/dylan.js index 4520c14e..7d4c3ed2 100644 --- a/mode/dylan/dylan.js +++ b/mode/dylan/dylan.js @@ -12,7 +12,7 @@ "use strict"; function forEach(arr, f) { - for (let i = 0; i < arr.length; i++) f(arr[i], i) + for (var i = 0; i < arr.length; i++) f(arr[i], i) } CodeMirror.defineMode("dylan", function(_config) { From 7268d97a86d482eb3d8a4307789ce2b71e4dce55 Mon Sep 17 00:00:00 2001 From: Adrian Heine Date: Wed, 5 Oct 2016 13:09:23 +0200 Subject: [PATCH 0632/1790] Use Pos instead of object literals in vim_test --- test/vim_test.js | 109 ++++++++++++++++++++++++----------------------- 1 file changed, 55 insertions(+), 54 deletions(-) diff --git a/test/vim_test.js b/test/vim_test.js index 703a07a7..d6b0ad16 100644 --- a/test/vim_test.js +++ b/test/vim_test.js @@ -1,3 +1,4 @@ +var Pos = CodeMirror.Pos; CodeMirror.Vim.suppressErrorLogging = true; var code = '' + @@ -39,63 +40,63 @@ var bracesLine = lines[3]; var seekBraceLine = lines[4]; var word1 = { - start: { line: wordLine.line, ch: 1 }, - end: { line: wordLine.line, ch: 5 } + start: new Pos(wordLine.line, 1), + end: new Pos(wordLine.line, 5) }; var word2 = { - start: { line: wordLine.line, ch: word1.end.ch + 2 }, - end: { line: wordLine.line, ch: word1.end.ch + 4 } + start: new Pos(wordLine.line, word1.end.ch + 2), + end: new Pos(wordLine.line, word1.end.ch + 4) }; var word3 = { - start: { line: bigWordLine.line, ch: 1 }, - end: { line: bigWordLine.line, ch: 5 } + start: new Pos(bigWordLine.line, 1), + end: new Pos(bigWordLine.line, 5) }; var bigWord1 = word1; var bigWord2 = word2; var bigWord3 = { - start: { line: bigWordLine.line, ch: 1 }, - end: { line: bigWordLine.line, ch: 7 } + start: new Pos(bigWordLine.line, 1), + end: new Pos(bigWordLine.line, 7) }; var bigWord4 = { - start: { line: bigWordLine.line, ch: bigWord1.end.ch + 3 }, - end: { line: bigWordLine.line, ch: bigWord1.end.ch + 7 } + start: new Pos(bigWordLine.line, bigWord1.end.ch + 3), + end: new Pos(bigWordLine.line, bigWord1.end.ch + 7) }; -var oChars = [ { line: charLine.line, ch: 1 }, - { line: charLine.line, ch: 3 }, - { line: charLine.line, ch: 7 } ]; -var pChars = [ { line: charLine.line, ch: 2 }, - { line: charLine.line, ch: 4 }, - { line: charLine.line, ch: 6 }, - { line: charLine.line, ch: 8 } ]; -var numChars = [ { line: charLine.line, ch: 10 }, - { line: charLine.line, ch: 12 }, - { line: charLine.line, ch: 14 }, - { line: charLine.line, ch: 16 }, - { line: charLine.line, ch: 18 }]; +var oChars = [ new Pos(charLine.line, 1), + new Pos(charLine.line, 3), + new Pos(charLine.line, 7) ]; +var pChars = [ new Pos(charLine.line, 2), + new Pos(charLine.line, 4), + new Pos(charLine.line, 6), + new Pos(charLine.line, 8) ]; +var numChars = [ new Pos(charLine.line, 10), + new Pos(charLine.line, 12), + new Pos(charLine.line, 14), + new Pos(charLine.line, 16), + new Pos(charLine.line, 18)]; var parens1 = { - start: { line: bracesLine.line, ch: 1 }, - end: { line: bracesLine.line, ch: 3 } + start: new Pos(bracesLine.line, 1), + end: new Pos(bracesLine.line, 3) }; var squares1 = { - start: { line: bracesLine.line, ch: 5 }, - end: { line: bracesLine.line, ch: 7 } + start: new Pos(bracesLine.line, 5), + end: new Pos(bracesLine.line, 7) }; var curlys1 = { - start: { line: bracesLine.line, ch: 9 }, - end: { line: bracesLine.line, ch: 11 } + start: new Pos(bracesLine.line, 9), + end: new Pos(bracesLine.line, 11) }; var seekOutside = { - start: { line: seekBraceLine.line, ch: 1 }, - end: { line: seekBraceLine.line, ch: 16 } + start: new Pos(seekBraceLine.line, 1), + end: new Pos(seekBraceLine.line, 16) }; var seekInside = { - start: { line: seekBraceLine.line, ch: 14 }, - end: { line: seekBraceLine.line, ch: 11 } + start: new Pos(seekBraceLine.line, 14), + end: new Pos(seekBraceLine.line, 11) }; function copyCursor(cur) { - return { ch: cur.ch, line: cur.line }; + return new Pos(cur.line, cur.ch); } function forEach(arr, func) { @@ -292,7 +293,7 @@ testJumplist('jumplist_skip_deleted_mark', function testMotion(name, keys, endPos, startPos) { testVim(name, function(cm, vim, helpers) { if (!startPos) { - startPos = { line: 0, ch: 0 }; + startPos = new Pos(0, 0); } cm.setCursor(startPos); helpers.doKeys(keys); @@ -301,11 +302,11 @@ function testMotion(name, keys, endPos, startPos) { }; function makeCursor(line, ch) { - return { line: line, ch: ch }; + return new Pos(line, ch); }; function offsetCursor(cur, offsetLine, offsetCh) { - return { line: cur.line + offsetLine, ch: cur.ch + offsetCh }; + return new Pos(cur.line + offsetLine, cur.ch + offsetCh); }; // Motion tests @@ -408,20 +409,20 @@ testVim('Changing lines after Eol operation', function(cm, vim, helpers) { helpers.doKeys(['$']); helpers.doKeys(['j']); // After moving to Eol and then down, we should be at Eol of line 2 - helpers.assertCursorAt({ line: 1, ch: lines[1].length - 1 }); + helpers.assertCursorAt(new Pos(1, lines[1].length - 1)); helpers.doKeys(['j']); // After moving down, we should be at Eol of line 3 - helpers.assertCursorAt({ line: 2, ch: lines[2].length - 1 }); + helpers.assertCursorAt(new Pos(2, lines[2].length - 1)); helpers.doKeys(['h']); helpers.doKeys(['j']); // After moving back one space and then down, since line 4 is shorter than line 2, we should // be at Eol of line 2 - 1 - helpers.assertCursorAt({ line: 3, ch: lines[3].length - 1 }); + helpers.assertCursorAt(new Pos(3, lines[3].length - 1)); helpers.doKeys(['j']); helpers.doKeys(['j']); // After moving down again, since line 3 has enough characters, we should be back to the // same place we were at on line 1 - helpers.assertCursorAt({ line: 5, ch: lines[2].length - 2 }); + helpers.assertCursorAt(new Pos(5, lines[2].length - 2)); }); //making sure gj and gk recover from clipping testVim('gj_gk_clipping', function(cm,vim,helpers){ @@ -470,7 +471,7 @@ testVim('gj_gk', function(cm, vim, helpers) { // Test moving down preserves column position. helpers.doKeys('g', 'j'); var pos1 = cm.getCursor(); - var expectedPos2 = { line: 0, ch: (pos1.ch - 4) * 2 + 4}; + var expectedPos2 = new Pos(0, (pos1.ch - 4) * 2 + 4); helpers.doKeys('g', 'j'); helpers.assertCursorAt(expectedPos2); @@ -865,8 +866,8 @@ testVim('d_reverse', function(cm, vim, helpers) { }, { value: ' word1\nword2 ' }); testVim('dd', function(cm, vim, helpers) { cm.setCursor(0, 3); - var expectedBuffer = cm.getRange({ line: 0, ch: 0 }, - { line: 1, ch: 0 }); + var expectedBuffer = cm.getRange(new Pos(0, 0), + new Pos(1, 0)); var expectedLineCount = cm.lineCount() - 1; helpers.doKeys('d', 'd'); eq(expectedLineCount, cm.lineCount()); @@ -877,8 +878,8 @@ testVim('dd', function(cm, vim, helpers) { }); testVim('dd_prefix_repeat', function(cm, vim, helpers) { cm.setCursor(0, 3); - var expectedBuffer = cm.getRange({ line: 0, ch: 0 }, - { line: 2, ch: 0 }); + var expectedBuffer = cm.getRange(new Pos(0, 0), + new Pos(2, 0)); var expectedLineCount = cm.lineCount() - 2; helpers.doKeys('2', 'd', 'd'); eq(expectedLineCount, cm.lineCount()); @@ -889,8 +890,8 @@ testVim('dd_prefix_repeat', function(cm, vim, helpers) { }); testVim('dd_motion_repeat', function(cm, vim, helpers) { cm.setCursor(0, 3); - var expectedBuffer = cm.getRange({ line: 0, ch: 0 }, - { line: 2, ch: 0 }); + var expectedBuffer = cm.getRange(new Pos(0, 0), + new Pos(2, 0)); var expectedLineCount = cm.lineCount() - 2; helpers.doKeys('d', '2', 'd'); eq(expectedLineCount, cm.lineCount()); @@ -901,8 +902,8 @@ testVim('dd_motion_repeat', function(cm, vim, helpers) { }); testVim('dd_multiply_repeat', function(cm, vim, helpers) { cm.setCursor(0, 3); - var expectedBuffer = cm.getRange({ line: 0, ch: 0 }, - { line: 6, ch: 0 }); + var expectedBuffer = cm.getRange(new Pos(0, 0), + new Pos(6, 0)); var expectedLineCount = cm.lineCount() - 6; helpers.doKeys('2', 'd', '3', 'd'); eq(expectedLineCount, cm.lineCount()); @@ -944,8 +945,8 @@ testVim('yw_repeat', function(cm, vim, helpers) { testVim('yy_multiply_repeat', function(cm, vim, helpers) { var curStart = makeCursor(0, 3); cm.setCursor(curStart); - var expectedBuffer = cm.getRange({ line: 0, ch: 0 }, - { line: 6, ch: 0 }); + var expectedBuffer = cm.getRange(new Pos(0, 0), + new Pos(6, 0)); var expectedLineCount = cm.lineCount(); helpers.doKeys('2', 'y', '3', 'y'); eq(expectedLineCount, cm.lineCount()); @@ -978,8 +979,8 @@ testVim('cw_repeat', function(cm, vim, helpers) { }, { value: ' word1\nword2' }); testVim('cc_multiply_repeat', function(cm, vim, helpers) { cm.setCursor(0, 3); - var expectedBuffer = cm.getRange({ line: 0, ch: 0 }, - { line: 6, ch: 0 }); + var expectedBuffer = cm.getRange(new Pos(0, 0), + new Pos(6, 0)); var expectedLineCount = cm.lineCount() - 5; helpers.doKeys('2', 'c', '3', 'c'); eq(expectedLineCount, cm.lineCount()); @@ -1260,7 +1261,7 @@ testEdit('di[_on_(_should_not_delete', 'foo (bAr) baz', /\(/, 'di[', 'foo (bAr) testEdit('di[_on_)_should_not_delete', 'foo (bAr) baz', /\)/, 'di[', 'foo (bAr) baz'); testEdit('da[_on_(_should_not_delete', 'foo (bAr) baz', /\(/, 'da[', 'foo (bAr) baz'); testEdit('da[_on_)_should_not_delete', 'foo (bAr) baz', /\)/, 'da[', 'foo (bAr) baz'); -testMotion('di(_outside_should_stay', ['d', 'i', '('], { line: 0, ch: 0}, { line: 0, ch: 0}); +testMotion('di(_outside_should_stay', ['d', 'i', '('], new Pos(0, 0), new Pos(0, 0)); // Open and close on different lines, equally indented testEdit('di{_middle_spc', 'a{\n\tbar\n}b', /r/, 'di{', 'a{}b'); From 801c366934a6684e3c36f299ef361f1bd70a0df4 Mon Sep 17 00:00:00 2001 From: Adrian Heine Date: Sun, 2 Oct 2016 16:44:34 +0200 Subject: [PATCH 0633/1790] Add failing test for fix for #4078 --- test/test.js | 2 ++ 1 file changed, 2 insertions(+) diff --git a/test/test.js b/test/test.js index de2c66e8..07d029fb 100644 --- a/test/test.js +++ b/test/test.js @@ -1122,6 +1122,8 @@ testCM("measureWrappedEndOfLine", function(cm) { var endPos = cm.charCoords(Pos(0, 12)); // Next-to-last since last would wrap (#1862) endPos.left += w; // Add width of editor just to be sure that we are behind last character eqPos(cm.coordsChar(endPos), Pos(0, 13)); + endPos.left += w * 100; + eqPos(cm.coordsChar(endPos), Pos(0, 13)); }, {mode: "text/html", value: "0123456789abcde0123456789", lineWrapping: true}, ie_lt8 || opera_lt10); testCM("scrollVerticallyAndHorizontally", function(cm) { From be7d7899d1544752b026778dc83136bfd9dbdf73 Mon Sep 17 00:00:00 2001 From: Adrian Heine Date: Sun, 2 Oct 2016 16:44:07 +0200 Subject: [PATCH 0634/1790] Make Pos stick to the character after or before --- src/line/pos.js | 10 +++++++--- src/measurement/position_measurement.js | 17 ++++++++++------- test/driver.js | 4 ++-- test/test.js | 11 ++++++++--- 4 files changed, 27 insertions(+), 15 deletions(-) diff --git a/src/line/pos.js b/src/line/pos.js index 73b2e0b8..4f5e4c55 100644 --- a/src/line/pos.js +++ b/src/line/pos.js @@ -1,15 +1,19 @@ import { getLine } from "./utils_line" // A Pos instance represents a position within the text. -export function Pos (line, ch) { - if (!(this instanceof Pos)) return new Pos(line, ch) - this.line = line; this.ch = ch +export function Pos(line, ch, sticky = null) { + if (!(this instanceof Pos)) return new Pos(line, ch, sticky) + this.line = line + this.ch = ch + this.sticky = sticky } // Compare two positions, return 0 if they are the same, a negative // number when a is less, and a positive number otherwise. export function cmp(a, b) { return a.line - b.line || a.ch - b.ch } +export function equalCursorPos(a, b) { return a.sticky == b.sticky && cmp(a, b) == 0 } + export function copyPos(x) {return Pos(x.line, x.ch)} export function maxPos(a, b) { return cmp(a, b) < 0 ? b : a } export function minPos(a, b) { return cmp(a, b) < 0 ? a : b } diff --git a/src/measurement/position_measurement.js b/src/measurement/position_measurement.js index f62672c4..4e8a3f83 100644 --- a/src/measurement/position_measurement.js +++ b/src/measurement/position_measurement.js @@ -357,7 +357,7 @@ export function cursorCoords(cm, pos, context, lineObj, preparedMeasure, varHeig return get(ch, right) } let order = getOrder(lineObj), ch = pos.ch - if (!order) return get(ch) + if (!order) return get(pos.sticky == "before" ? ch - 1 : ch, pos.sticky == "before") let partPos = getBidiPartAt(order, ch) let val = getBidi(ch, partPos) if (bidiOther != null) val.other = getBidi(ch, bidiOther) @@ -381,8 +381,8 @@ export function estimateCoords(cm, pos) { // the right of the character position, for example). When outside // is true, that means the coordinates lie outside the line's // vertical range. -function PosWithInfo(line, ch, outside, xRel) { - let pos = Pos(line, ch) +function PosWithInfo(line, ch, sticky, outside, xRel) { + let pos = Pos(line, ch, sticky) pos.xRel = xRel if (outside) pos.outside = true return pos @@ -393,10 +393,10 @@ function PosWithInfo(line, ch, outside, xRel) { export function coordsChar(cm, x, y) { let doc = cm.doc y += cm.display.viewOffset - if (y < 0) return PosWithInfo(doc.first, 0, true, -1) + if (y < 0) return PosWithInfo(doc.first, 0, null, true, -1) let lineN = lineAtHeight(doc, y), last = doc.first + doc.size - 1 if (lineN > last) - return PosWithInfo(doc.first + doc.size - 1, getLine(doc, last).text.length, true, 1) + return PosWithInfo(doc.first + doc.size - 1, getLine(doc, last).text.length, null, true, 1) if (x < 0) x = 0 let lineObj = getLine(doc, lineN) @@ -429,7 +429,7 @@ function coordsCharInner(cm, lineObj, lineNo, x, y) { let from = lineLeft(lineObj), to = lineRight(lineObj) let fromX = getX(from), fromOutside = wrongLine, toX = getX(to), toOutside = wrongLine - if (x > toX) return PosWithInfo(lineNo, to, toOutside, 1) + if (x > toX) return PosWithInfo(lineNo, to, null, toOutside, 1) // Do a binary search between these bounds. for (;;) { if (bidi ? to == from || to == moveVisually(lineObj, from, 1) : to - from <= 1) { @@ -448,9 +448,12 @@ function coordsCharInner(cm, lineObj, lineNo, x, y) { ch++ xDiff = x - charSize.right } + let sticky = null + if (toX > cm.display.wrapper.clientWidth) { + sticky = "before" } while (isExtendingChar(lineObj.text.charAt(ch))) ++ch - let pos = PosWithInfo(lineNo, ch, outside, xDiff < -1 ? -1 : xDiff > 1 ? 1 : 0) + let pos = PosWithInfo(lineNo, ch, sticky, outside, xDiff < -1 ? -1 : xDiff > 1 ? 1 : 0) return pos } let step = Math.ceil(dist / 2), middle = from + step diff --git a/test/driver.js b/test/driver.js index c61d4c1f..01736450 100644 --- a/test/driver.js +++ b/test/driver.js @@ -107,11 +107,11 @@ function near(a, b, margin, msg) { throw new Failure(label(a + " is not close to " + b + " (" + margin + ")", msg)); } function eqPos(a, b, msg) { - function str(p) { return "{line:" + p.line + ",ch:" + p.ch + "}"; } + function str(p) { return "{line:" + p.line + ",ch:" + p.ch + ",sticky:" + p.sticky + "}"; } if (a == b) return; 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)); + if (a.line != b.line || a.ch != b.ch || a.sticky != b.sticky) throw new Failure(label(str(a) + " != " + str(b), msg)); } function is(a, msg) { if (!a) throw new Failure(label("assertion failed", msg)); diff --git a/test/test.js b/test/test.js index 07d029fb..37894190 100644 --- a/test/test.js +++ b/test/test.js @@ -1105,6 +1105,10 @@ testCM("measureEndOfLine", function(cm) { is(endPos.left > w - 20, "not at right"); endPos = cm.charCoords(Pos(0, 18)); eqPos(cm.coordsChar({left: endPos.left, top: endPos.top + 5}), Pos(0, 18)); + + var wrapPos = cm.cursorCoords(Pos(0, 9, "before")); + is(wrapPos.top < endPos.top, "wrapPos is actually in first line"); + eqPos(cm.coordsChar({left: wrapPos.left + 10, top: wrapPos.top}), Pos(0, 9, "before")); }, {mode: "text/html", value: "", lineWrapping: true}, ie_lt8 || opera_lt10); testCM("measureWrappedEndOfLine", function(cm) { @@ -1121,9 +1125,9 @@ testCM("measureWrappedEndOfLine", function(cm) { } var endPos = cm.charCoords(Pos(0, 12)); // Next-to-last since last would wrap (#1862) endPos.left += w; // Add width of editor just to be sure that we are behind last character - eqPos(cm.coordsChar(endPos), Pos(0, 13)); + eqPos(cm.coordsChar(endPos), Pos(0, 13, "before")); endPos.left += w * 100; - eqPos(cm.coordsChar(endPos), Pos(0, 13)); + eqPos(cm.coordsChar(endPos), Pos(0, 13, "before")); }, {mode: "text/html", value: "0123456789abcde0123456789", lineWrapping: true}, ie_lt8 || opera_lt10); testCM("scrollVerticallyAndHorizontally", function(cm) { @@ -1149,7 +1153,8 @@ testCM("moveVstuck", function(cm) { } cm.setCursor(Pos(0, val.length - 1)); cm.moveV(-1, "line"); - eqPos(cm.getCursor(), Pos(0, 26)); + eqPos(cm.getCursor(), Pos(0, 26, "before")); + is(cm.cursorCoords(null, "local").top < h0, "cursor is in first visual line"); }, {lineWrapping: true}, ie_lt8 || opera_lt10); testCM("collapseOnMove", function(cm) { From 8ef8555e89abc889745b4222ffeefd1a3ea81f99 Mon Sep 17 00:00:00 2001 From: Adrian Heine Date: Wed, 5 Oct 2016 20:05:10 +0200 Subject: [PATCH 0635/1790] Use cursor stickiness in cursor movement Should fix #1862 for ltr text. --- src/edit/methods.js | 6 +++--- src/model/selection.js | 4 ++-- test/test.js | 7 +++++++ 3 files changed, 12 insertions(+), 5 deletions(-) diff --git a/src/edit/methods.js b/src/edit/methods.js index 144e4773..caf62b19 100644 --- a/src/edit/methods.js +++ b/src/edit/methods.js @@ -9,7 +9,7 @@ import { triggerElectric } from "../input/input" import { onKeyDown, onKeyPress, onKeyUp } from "./key_events" import { getKeyMap } from "../input/keymap" import { methodOp, operation, runInOp } from "../display/operations" -import { clipLine, clipPos, cmp, Pos } from "../line/pos" +import { clipLine, clipPos, equalCursorPos, Pos } from "../line/pos" import { charCoords, charWidth, clearCaches, clearLineMeasurementCache, coordsChar, cursorCoords, displayHeight, displayWidth, estimateLineHeights, fromCoordSystem, intoCoordSystem, scrollGap, textHeight } from "../measurement/position_measurement" import { Range } from "../model/selection" import { replaceOneSelection, skipAtomic } from "../model/selection_updates" @@ -507,8 +507,8 @@ function findPosH(doc, pos, dir, unit, visually) { if (dir > 0 && !moveOnce(!first)) break } } - let result = skipAtomic(doc, Pos(line, ch), pos, origDir, true) - if (!cmp(pos, result)) result.hitSide = true + let result = skipAtomic(doc, Pos(line, ch, (!doc.cm || doc.cm.options.lineWrapping) ? (dir == -1 ? "after" : "before") : null), pos, origDir, true) + if (equalCursorPos(pos, result)) result.hitSide = true return result } diff --git a/src/model/selection.js b/src/model/selection.js index 120af2a4..a47ede93 100644 --- a/src/model/selection.js +++ b/src/model/selection.js @@ -1,4 +1,4 @@ -import { cmp, copyPos, maxPos, minPos } from "../line/pos" +import { cmp, copyPos, equalCursorPos, maxPos, minPos } from "../line/pos" import { indexOf } from "../util/misc" // Selection objects are immutable. A new one is created every time @@ -18,7 +18,7 @@ Selection.prototype = { if (other.primIndex != this.primIndex || other.ranges.length != this.ranges.length) return false for (let i = 0; i < this.ranges.length; i++) { let here = this.ranges[i], there = other.ranges[i] - if (cmp(here.anchor, there.anchor) != 0 || cmp(here.head, there.head) != 0) return false + if (!equalCursorPos(here.anchor, there.anchor) || !equalCursorPos(here.head, there.head)) return false } return true }, diff --git a/test/test.js b/test/test.js index 37894190..8a064b6c 100644 --- a/test/test.js +++ b/test/test.js @@ -2205,3 +2205,10 @@ testCM("lineSeparator", function(cm) { eq(cm.lineCount(), 4); }, {value: "foo\nbar\r\nbaz\rquux", lineSeparator: "\n"}); + +testCM("delete_wrapped", function(cm) { + makeItWrapAfter(cm, Pos(0, 2)); + cm.doc.setCursor(Pos(0, 3, "after")); + cm.deleteH(-1, "char"); + eq(cm.getLine(0), "1245"); +}, {value: "12345", lineWrapping: true}) From 959564ae90640529923c159822f33fbfc84a07d5 Mon Sep 17 00:00:00 2001 From: Adrian Heine Date: Mon, 7 Nov 2016 14:18:17 +0100 Subject: [PATCH 0636/1790] Use stickiness in cursorCoords for bidi --- src/measurement/position_measurement.js | 50 +++++++++++++++---------- src/util/bidi.js | 29 +++++--------- 2 files changed, 41 insertions(+), 38 deletions(-) diff --git a/src/measurement/position_measurement.js b/src/measurement/position_measurement.js index 4e8a3f83..49581e65 100644 --- a/src/measurement/position_measurement.js +++ b/src/measurement/position_measurement.js @@ -2,7 +2,7 @@ import { buildLineContent, LineView } from "../line/line_data" import { clipPos, Pos } from "../line/pos" import { collapsedSpanAtEnd, heightAtLine, lineIsHidden, visualLine } from "../line/spans" import { getLine, lineAtHeight, lineNo, updateLineHeight } from "../line/utils_line" -import { bidiLeft, bidiRight, bidiOther, getBidiPartAt, getOrder, lineLeft, lineRight, moveVisually } from "../util/bidi" +import { bidiOther, getBidiPartAt, getOrder, lineLeft, lineRight, moveVisually } from "../util/bidi" import { ie, ie_version } from "../util/browser" import { elt, removeChildren, range, removeChildrenAndAdd } from "../util/dom" import { e_target } from "../util/event" @@ -334,6 +334,19 @@ export function charCoords(cm, pos, context, lineObj, bias) { // Returns a box for a given cursor position, which may have an // 'other' property containing the position of the secondary cursor // on a bidi boundary. +// A cursor Pos(line, char, "before") is on the same visual line as `char - 1` +// and after `char - 1` in writing order of `char - 1` +// A cursor Pos(line, char, "after") is on the same visual line as `char` +// and before `char` in writing order of `char` +// Examples (upper-case letters are RTL, lower-case are LTR): +// Pos(0, 1, ...) +// before after +// ab a|b a|b +// aB a|B aB| +// Ab |Ab A|b +// AB B|A B|A +// Every position after the last character on a line is considered to stick +// to the last character on the line. export function cursorCoords(cm, pos, context, lineObj, preparedMeasure, varHeight) { lineObj = lineObj || getLine(cm.doc, pos.line) if (!preparedMeasure) preparedMeasure = prepareMeasureForLine(cm, lineObj) @@ -342,25 +355,24 @@ export function cursorCoords(cm, pos, context, lineObj, preparedMeasure, varHeig if (right) m.left = m.right; else m.right = m.left return intoCoordSystem(cm, lineObj, m, context) } - function getBidi(ch, partPos) { - let part = order[partPos], right = part.level % 2 - if (ch == bidiLeft(part) && partPos && part.level < order[partPos - 1].level) { - part = order[--partPos] - ch = bidiRight(part) - (part.level % 2 ? 0 : 1) - right = true - } else if (ch == bidiRight(part) && partPos < order.length - 1 && part.level < order[partPos + 1].level) { - part = order[++partPos] - ch = bidiLeft(part) - part.level % 2 - right = false - } - if (right && ch == part.to && ch > part.from) return get(ch - 1) - return get(ch, right) + let order = getOrder(lineObj), ch = pos.ch, sticky = pos.sticky + if (ch >= lineObj.text.length) { + ch = lineObj.text.length + sticky = "before" + } else if (ch <= 0) { + ch = 0 + sticky = "after" + } + if (!order) return get(sticky == "before" ? ch - 1 : ch, sticky == "before") + + function getBidi(ch, partPos, invert) { + let part = order[partPos], right = (part.level % 2) != 0 + return get(invert ? ch - 1 : ch, right != invert) } - let order = getOrder(lineObj), ch = pos.ch - if (!order) return get(pos.sticky == "before" ? ch - 1 : ch, pos.sticky == "before") - let partPos = getBidiPartAt(order, ch) - let val = getBidi(ch, partPos) - if (bidiOther != null) val.other = getBidi(ch, bidiOther) + let partPos = getBidiPartAt(order, ch, sticky) + let other = bidiOther + let val = getBidi(ch, partPos, sticky == "before") + if (other != null) val.other = getBidi(ch, other, sticky != "before") return val } diff --git a/src/util/bidi.js b/src/util/bidi.js index 6a849140..7d08e365 100644 --- a/src/util/bidi.js +++ b/src/util/bidi.js @@ -25,33 +25,24 @@ export function lineRight(line) { return bidiRight(lst(order)) } -function compareBidiLevel(order, a, b) { - let linedir = order[0].level - if (a == linedir) return true - if (b == linedir) return false - return a < b -} export let bidiOther = null -export function getBidiPartAt(order, pos) { +export function getBidiPartAt(order, ch, sticky) { let found bidiOther = null for (let i = 0; i < order.length; ++i) { let cur = order[i] - if (cur.from < pos && cur.to > pos) return i - if ((cur.from == pos || cur.to == pos)) { - if (found == null) { - found = i - } else if (compareBidiLevel(order, cur.level, order[found].level)) { - if (cur.from != cur.to) bidiOther = found - return i - } else { - if (cur.from != cur.to) bidiOther = i - return found - } + if (cur.from < ch && cur.to > ch) return i + if (cur.to == ch) { + if (cur.from != cur.to && sticky == "before") found = i + else bidiOther = i + } + if (cur.from == ch) { + if (cur.from != cur.to && sticky != "before") found = i + else bidiOther = i } } - return found + return found != null ? found : bidiOther } function moveInLine(line, pos, dir, byUnit) { From 3f9d7eab94f3cc4e0e596b357f06094046992f52 Mon Sep 17 00:00:00 2001 From: Adrian Heine Date: Wed, 14 Dec 2016 11:53:03 +0100 Subject: [PATCH 0637/1790] Reimplement visual moving --- src/edit/methods.js | 17 ++- src/input/movement.js | 108 ++++++++++++++++++ src/measurement/position_measurement.js | 5 + src/util/misc.js | 11 ++ test/test.js | 139 ++++++++++++++++++++++++ 5 files changed, 274 insertions(+), 6 deletions(-) create mode 100644 src/input/movement.js diff --git a/src/edit/methods.js b/src/edit/methods.js index caf62b19..1cce4f3d 100644 --- a/src/edit/methods.js +++ b/src/edit/methods.js @@ -8,6 +8,7 @@ import { indentLine } from "../input/indent" import { triggerElectric } from "../input/input" import { onKeyDown, onKeyPress, onKeyUp } from "./key_events" import { getKeyMap } from "../input/keymap" +import { endOfLine, moveVisually } from "../input/movement" import { methodOp, operation, runInOp } from "../display/operations" import { clipLine, clipPos, equalCursorPos, Pos } from "../line/pos" import { charCoords, charWidth, clearCaches, clearLineMeasurementCache, coordsChar, cursorCoords, displayHeight, displayWidth, estimateLineHeights, fromCoordSystem, intoCoordSystem, scrollGap, textHeight } from "../measurement/position_measurement" @@ -16,7 +17,7 @@ import { replaceOneSelection, skipAtomic } from "../model/selection_updates" import { addToScrollPos, calculateScrollPos, ensureCursorVisible, resolveScrollToPos, scrollIntoView } from "../display/scrolling" import { heightAtLine } from "../line/spans" import { updateGutterSpace } from "../display/update_display" -import { lineLeft, lineRight, moveLogically, moveVisually } from "../util/bidi" +import { moveLogically } from "../util/bidi" import { indexOf, insertSorted, isWordChar, sel_dontScroll, sel_move } from "../util/misc" import { signalLater } from "../util/operation_group" import { getLine, isLine, lineAtHeight } from "../line/utils_line" @@ -464,7 +465,7 @@ export default function(CodeMirror) { // position. The resulting position will have a hitSide=true // property if it reached the end of the document. function findPosH(doc, pos, dir, unit, visually) { - let line = pos.line, ch = pos.ch, origDir = dir + let {line, ch, sticky} = pos, origDir = dir let lineObj = getLine(doc, line) function findNextLine() { let l = line + dir @@ -473,11 +474,15 @@ function findPosH(doc, pos, dir, unit, visually) { return lineObj = getLine(doc, l) } function moveOnce(boundToLine) { - let next = (visually ? moveVisually : moveLogically)(lineObj, ch, dir, true) + let myMoveVisually = (line, start, dir, byUnit) => { + let res = moveVisually(doc.cm, line, start, dir, byUnit, sticky) + if (res.ch) sticky = res.sticky + return res.ch + } + let next = (visually ? myMoveVisually : 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 + ;({ch, sticky} = endOfLine(visually, doc.cm, lineObj, dir)) } else return false } else ch = next return true @@ -507,7 +512,7 @@ function findPosH(doc, pos, dir, unit, visually) { if (dir > 0 && !moveOnce(!first)) break } } - let result = skipAtomic(doc, Pos(line, ch, (!doc.cm || doc.cm.options.lineWrapping) ? (dir == -1 ? "after" : "before") : null), pos, origDir, true) + let result = skipAtomic(doc, Pos(line, ch, sticky), pos, origDir, true) if (equalCursorPos(pos, result)) result.hitSide = true return result } diff --git a/src/input/movement.js b/src/input/movement.js new file mode 100644 index 00000000..0464361c --- /dev/null +++ b/src/input/movement.js @@ -0,0 +1,108 @@ +import { prepareMeasureCharTop } from "../measurement/position_measurement" +import { bidiLeft, bidiRight, getBidiPartAt, getOrder, lineLeft, lineRight, moveLogically } from "../util/bidi" +import { findFirst } from "../util/misc" + +export function endOfLine(visually, cm, lineObj, dir) { + let ch, sticky = "before" + if (visually) { + let order = getOrder(lineObj) + if (order) { + let i = dir < 0 ? order.length - 1 : 0 + while (order[i].to == order[i].from) i += dir + let part = order[i] + // With a wrapped rtl chunk (possibly spanning multiple bidi parts), + // it could be that the last bidi part is not on the last visual line, + // since visual lines contain content order-consecutive chunks. + // Thus, in rtl, we are looking for the first (content-order) character + // in the rtl chunk that is on the last line (that is, the same line + // as the last (content-order) character). + if (dir < 0 && part.level > 0) { + let getTop = prepareMeasureCharTop(cm, lineObj) + ch = lineObj.text.length - 1 + let targetTop = getTop(ch) + ch = findFirst(ch => getTop(ch) == targetTop, part.from, ch) + if (part.level == 1) sticky = "after" + else ch = moveLogically(lineObj, ch, 1, true) + return {ch, sticky} + } + ch = (dir < 0 ? bidiRight : bidiLeft)(part) + return {ch, sticky} + } + } + if (visually) ch = (dir < 0 ? lineRight : lineLeft)(lineObj) + else ch = dir < 0 ? lineObj.text.length : 0 + return {ch, sticky} +} + +function getVisualLineAround(cm, line, target) { + if (!cm.options.lineWrapping) return [0, line.text.length - 1] + let measureTop = prepareMeasureCharTop(cm, line) + let targetTop = measureTop(target) + return [ + findFirst(ch => targetTop == measureTop(ch), 0, target), + findFirst(ch => targetTop == measureTop(ch), line.text.length - 1, target) + ] +} + +export function moveVisually(cm, line, start, dir, byUnit, startSticky) { + let mv = (ch, dir) => moveLogically(line, ch, dir, byUnit) + let bidi = getOrder(line) + if (!bidi) return {ch: mv(start, dir), sticky: dir < 0 ? "after" : "before"} + if (start >= line.text.length) { + start = line.text.length + startSticky = "before" + } else if (start <= 0) { + start = 0 + startSticky = "after" + } + let partPos = getBidiPartAt(bidi, start, startSticky), part = bidi[partPos] + if (part.level % 2 == 0 && (dir > 0 ? part.to > start : part.from < start)) { + // Case 1: We move within an ltr part. Even with wrapped lines, + // nothing interesting happens. + return {ch: mv(start, dir), sticky: dir < 0 ? "after" : "before"} + } + + let getVisualLine = ch => getVisualLineAround(cm, line, ch) + let visualLine = getVisualLine(startSticky == "before" ? mv(start, -1) : start) + + if (part.level % 2 == 1) { + let ch = mv(start, -dir) + if (ch != null && (dir > 0 ? ch >= part.from && ch >= visualLine[0] : ch <= part.to && ch <= mv(visualLine[1], 1))) { + // Case 2: We move within an rtl part on the same visual line + let sticky = dir < 0 ? "before" : "after" + return {ch, sticky} + } + } + + // Case 3: Could not move within this bidi part in this visual line, so leave + // the current bidi part + + let searchInVisualLine = (partPos, dir, visualLine) => { + let getRes = (ch, moveInStorageOrder) => moveInStorageOrder + ? {ch: mv(ch, 1), sticky: "before"} + : {ch, sticky: "after"} + + for (partPos += dir; partPos >= 0 && partPos < bidi.length; partPos += dir) { + let part = bidi[partPos] + let moveInStorageOrder = (dir > 0) == (part.level != 1) + let ch = moveInStorageOrder ? visualLine[0] : visualLine[1] + if (part.from <= ch && ch < part.to) return getRes(ch, moveInStorageOrder) + ch = moveInStorageOrder ? part.from : mv(part.to, -1) + if (visualLine[0] <= ch && ch <= visualLine[1]) return getRes(ch, moveInStorageOrder) + } + } + + // Case 3a: Look for other bidi parts on the same visual line + let res = searchInVisualLine(partPos, dir, visualLine) + if (res) return res + + // Case 3b: Look for other bidi parts on the next visual line + let nextCh = mv(visualLine[dir > 0 ? 1 : 0], dir) + if (nextCh != null && !(dir > 0 && nextCh == line.text.length)) { + res = searchInVisualLine(dir > 0 ? 0 : bidi.length, dir, getVisualLine(nextCh)) + if (res) return res + } + + // Case 4: Nowhere to move + return {ch: null, sticky: null} +} diff --git a/src/measurement/position_measurement.js b/src/measurement/position_measurement.js index 49581e65..d87ec7a0 100644 --- a/src/measurement/position_measurement.js +++ b/src/measurement/position_measurement.js @@ -88,6 +88,11 @@ export function measureChar(cm, line, ch, bias) { return measureCharPrepared(cm, prepareMeasureForLine(cm, line), ch, bias) } +export function prepareMeasureCharTop(cm, line) { + let preparedMeasure = prepareMeasureForLine(cm, line) + return ch => measureCharPrepared(cm, preparedMeasure, ch).top +} + // Find a line view that corresponds to the given line number. export function findViewForLine(cm, lineN) { if (lineN >= cm.display.viewFrom && lineN < cm.display.viewTo) diff --git a/src/util/misc.js b/src/util/misc.js index 2fb90914..dead8a6d 100644 --- a/src/util/misc.js +++ b/src/util/misc.js @@ -124,3 +124,14 @@ export function isEmpty(obj) { // of code points as a group. let extendingChars = /[\u0300-\u036f\u0483-\u0489\u0591-\u05bd\u05bf\u05c1\u05c2\u05c4\u05c5\u05c7\u0610-\u061a\u064b-\u065e\u0670\u06d6-\u06dc\u06de-\u06e4\u06e7\u06e8\u06ea-\u06ed\u0711\u0730-\u074a\u07a6-\u07b0\u07eb-\u07f3\u0816-\u0819\u081b-\u0823\u0825-\u0827\u0829-\u082d\u0900-\u0902\u093c\u0941-\u0948\u094d\u0951-\u0955\u0962\u0963\u0981\u09bc\u09be\u09c1-\u09c4\u09cd\u09d7\u09e2\u09e3\u0a01\u0a02\u0a3c\u0a41\u0a42\u0a47\u0a48\u0a4b-\u0a4d\u0a51\u0a70\u0a71\u0a75\u0a81\u0a82\u0abc\u0ac1-\u0ac5\u0ac7\u0ac8\u0acd\u0ae2\u0ae3\u0b01\u0b3c\u0b3e\u0b3f\u0b41-\u0b44\u0b4d\u0b56\u0b57\u0b62\u0b63\u0b82\u0bbe\u0bc0\u0bcd\u0bd7\u0c3e-\u0c40\u0c46-\u0c48\u0c4a-\u0c4d\u0c55\u0c56\u0c62\u0c63\u0cbc\u0cbf\u0cc2\u0cc6\u0ccc\u0ccd\u0cd5\u0cd6\u0ce2\u0ce3\u0d3e\u0d41-\u0d44\u0d4d\u0d57\u0d62\u0d63\u0dca\u0dcf\u0dd2-\u0dd4\u0dd6\u0ddf\u0e31\u0e34-\u0e3a\u0e47-\u0e4e\u0eb1\u0eb4-\u0eb9\u0ebb\u0ebc\u0ec8-\u0ecd\u0f18\u0f19\u0f35\u0f37\u0f39\u0f71-\u0f7e\u0f80-\u0f84\u0f86\u0f87\u0f90-\u0f97\u0f99-\u0fbc\u0fc6\u102d-\u1030\u1032-\u1037\u1039\u103a\u103d\u103e\u1058\u1059\u105e-\u1060\u1071-\u1074\u1082\u1085\u1086\u108d\u109d\u135f\u1712-\u1714\u1732-\u1734\u1752\u1753\u1772\u1773\u17b7-\u17bd\u17c6\u17c9-\u17d3\u17dd\u180b-\u180d\u18a9\u1920-\u1922\u1927\u1928\u1932\u1939-\u193b\u1a17\u1a18\u1a56\u1a58-\u1a5e\u1a60\u1a62\u1a65-\u1a6c\u1a73-\u1a7c\u1a7f\u1b00-\u1b03\u1b34\u1b36-\u1b3a\u1b3c\u1b42\u1b6b-\u1b73\u1b80\u1b81\u1ba2-\u1ba5\u1ba8\u1ba9\u1c2c-\u1c33\u1c36\u1c37\u1cd0-\u1cd2\u1cd4-\u1ce0\u1ce2-\u1ce8\u1ced\u1dc0-\u1de6\u1dfd-\u1dff\u200c\u200d\u20d0-\u20f0\u2cef-\u2cf1\u2de0-\u2dff\u302a-\u302f\u3099\u309a\ua66f-\ua672\ua67c\ua67d\ua6f0\ua6f1\ua802\ua806\ua80b\ua825\ua826\ua8c4\ua8e0-\ua8f1\ua926-\ua92d\ua947-\ua951\ua980-\ua982\ua9b3\ua9b6-\ua9b9\ua9bc\uaa29-\uaa2e\uaa31\uaa32\uaa35\uaa36\uaa43\uaa4c\uaab0\uaab2-\uaab4\uaab7\uaab8\uaabe\uaabf\uaac1\uabe5\uabe8\uabed\udc00-\udfff\ufb1e\ufe00-\ufe0f\ufe20-\ufe26\uff9e\uff9f]/ export function isExtendingChar(ch) { return ch.charCodeAt(0) >= 768 && extendingChars.test(ch) } + +// Returns the value from the range [`from`; `to`] that satisfies +// `pred` and is closest to `from`. Assumes that at least `to` satisfies `pred`. +export function findFirst(pred, from, to) { + for (;;) { + if (Math.abs(from - to) <= 1) return pred(from) ? from : to + let mid = Math.floor((from + to) / 2) + if (pred(mid)) to = mid + else from = mid + } +} diff --git a/test/test.js b/test/test.js index 8a064b6c..00c0be25 100644 --- a/test/test.js +++ b/test/test.js @@ -2206,6 +2206,145 @@ testCM("lineSeparator", function(cm) { }, {value: "foo\nbar\r\nbaz\rquux", lineSeparator: "\n"}); +var extendingChars = /[\u0300-\u036f\u0483-\u0489\u0591-\u05bd\u05bf\u05c1\u05c2\u05c4\u05c5\u05c7\u0610-\u061a\u064b-\u065e\u0670\u06d6-\u06dc\u06de-\u06e4\u06e7\u06e8\u06ea-\u06ed\u0711\u0730-\u074a\u07a6-\u07b0\u07eb-\u07f3\u0816-\u0819\u081b-\u0823\u0825-\u0827\u0829-\u082d\u0900-\u0902\u093c\u0941-\u0948\u094d\u0951-\u0955\u0962\u0963\u0981\u09bc\u09be\u09c1-\u09c4\u09cd\u09d7\u09e2\u09e3\u0a01\u0a02\u0a3c\u0a41\u0a42\u0a47\u0a48\u0a4b-\u0a4d\u0a51\u0a70\u0a71\u0a75\u0a81\u0a82\u0abc\u0ac1-\u0ac5\u0ac7\u0ac8\u0acd\u0ae2\u0ae3\u0b01\u0b3c\u0b3e\u0b3f\u0b41-\u0b44\u0b4d\u0b56\u0b57\u0b62\u0b63\u0b82\u0bbe\u0bc0\u0bcd\u0bd7\u0c3e-\u0c40\u0c46-\u0c48\u0c4a-\u0c4d\u0c55\u0c56\u0c62\u0c63\u0cbc\u0cbf\u0cc2\u0cc6\u0ccc\u0ccd\u0cd5\u0cd6\u0ce2\u0ce3\u0d3e\u0d41-\u0d44\u0d4d\u0d57\u0d62\u0d63\u0dca\u0dcf\u0dd2-\u0dd4\u0dd6\u0ddf\u0e31\u0e34-\u0e3a\u0e47-\u0e4e\u0eb1\u0eb4-\u0eb9\u0ebb\u0ebc\u0ec8-\u0ecd\u0f18\u0f19\u0f35\u0f37\u0f39\u0f71-\u0f7e\u0f80-\u0f84\u0f86\u0f87\u0f90-\u0f97\u0f99-\u0fbc\u0fc6\u102d-\u1030\u1032-\u1037\u1039\u103a\u103d\u103e\u1058\u1059\u105e-\u1060\u1071-\u1074\u1082\u1085\u1086\u108d\u109d\u135f\u1712-\u1714\u1732-\u1734\u1752\u1753\u1772\u1773\u17b7-\u17bd\u17c6\u17c9-\u17d3\u17dd\u180b-\u180d\u18a9\u1920-\u1922\u1927\u1928\u1932\u1939-\u193b\u1a17\u1a18\u1a56\u1a58-\u1a5e\u1a60\u1a62\u1a65-\u1a6c\u1a73-\u1a7c\u1a7f\u1b00-\u1b03\u1b34\u1b36-\u1b3a\u1b3c\u1b42\u1b6b-\u1b73\u1b80\u1b81\u1ba2-\u1ba5\u1ba8\u1ba9\u1c2c-\u1c33\u1c36\u1c37\u1cd0-\u1cd2\u1cd4-\u1ce0\u1ce2-\u1ce8\u1ced\u1dc0-\u1de6\u1dfd-\u1dff\u200c\u200d\u20d0-\u20f0\u2cef-\u2cf1\u2de0-\u2dff\u302a-\u302f\u3099\u309a\ua66f-\ua672\ua67c\ua67d\ua6f0\ua6f1\ua802\ua806\ua80b\ua825\ua826\ua8c4\ua8e0-\ua8f1\ua926-\ua92d\ua947-\ua951\ua980-\ua982\ua9b3\ua9b6-\ua9b9\ua9bc\uaa29-\uaa2e\uaa31\uaa32\uaa35\uaa36\uaa43\uaa4c\uaab0\uaab2-\uaab4\uaab7\uaab8\uaabe\uaabf\uaac1\uabe5\uabe8\uabed\udc00-\udfff\ufb1e\ufe00-\ufe0f\ufe20-\ufe26\uff9e\uff9f]/ +var getChar = function (noExtending) { var res; do {res = String.fromCharCode(Math.floor(Math.random()*0x8ac)); } while ([0x90].indexOf(res.charCodeAt(0)) != -1 || (noExtending && extendingChars.test(res))); return res } +var getString = function (n) { var res = getChar(true); while (--n > 0) res += getChar(); return res } + +function makeItWrapAfter(cm, pos) { + var firstLineTop = cm.cursorCoords(Pos(0, 0)).top; + for(var w = 0, posTop; posTop != firstLineTop; ++w) { + cm.setSize(w); + posTop = cm.charCoords(pos).top; + } +} + +function testMoveBidi(str) { + testCM("move_bidi_" + str, function(cm) { +console.log("TESTING", encodeURIComponent(str)) + cm.getScrollerElement().style.fontFamily = "monospace"; + makeItWrapAfter(cm, Pos(0, 5)); + + var steps = str.length - str.split("").filter(extendingChars.test.bind(extendingChars)).length; + var lineBreaks = Object.create(null); + lineBreaks[6 - str.substr(0, 5).split("").filter(extendingChars.test.bind(extendingChars)).length] = 'w'; + if (str.indexOf("\n") != -1) { + lineBreaks[steps - 2] = 'n'; + } +console.log("TESTING", str.length, steps, lineBreaks) + + // Make sure we are at the visual beginning of the first line + var pos = Pos(0, 0), lastPos; + cm.doc.setCursor(pos); + do { + lastPos = pos; + cm.execCommand("goCharLeft"); + pos = cm.doc.getCursor(); + } while (pos != lastPos && pos.ch != 0) + + var prevCoords = cm.cursorCoords(), coords; + for(var i = 0; i < steps; ++i) { + cm.execCommand("goCharRight"); + coords = cm.cursorCoords(); + if ((i >= 10 && i <= 12) && !lineBreaks[i] && coords.left < prevCoords.left && coords.top > prevCoords.top) { +console.log("MOVED WRAPPED AT>", i) + // The first line wraps twice + lineBreaks[i] = 'w'; + } + if (!lineBreaks[i]) { + is(coords.left > prevCoords.left, "In step " + i + ", cursor didn't move right"); + eq(coords.top, prevCoords.top, "In step " + i + ", cursor moved out of line"); +console.log("MOVED", i, "right") + } else { + is(coords.left < prevCoords.left, i); + is(coords.top > prevCoords.top, i); +console.log("MOVED", i, "down") + } + prevCoords = coords; + } + + cm.execCommand("goCharRight"); + coords = cm.cursorCoords(); + eq(coords.left, prevCoords.left, "Moving " + steps + " steps right didn't reach the end"); + eq(coords.top, prevCoords.top, "Moving " + steps + " steps right didn't reach the end"); + + for(i = steps - 1; i >= 0; --i) { + cm.execCommand("goCharLeft"); + coords = cm.cursorCoords(); + if (!(lineBreaks[i] == 'n' || lineBreaks[i + 1] == 'w')) { + is(coords.left < prevCoords.left, "In step " + i + ", cursor didn't move left"); + eq(coords.top, prevCoords.top, "In step " + i + ", cursor is not at the same line anymore"); +console.log("MOVED", i, "left") + } else { + is(coords.left > prevCoords.left, i); + is(coords.top < prevCoords.top, i); +console.log("MOVED", i, "up") + } + prevCoords = coords; + } + + cm.execCommand("goCharLeft"); + coords = cm.cursorCoords(); + eq(coords.left, prevCoords.left, "Moving " + steps + " steps left didn't reach the beginning"); + eq(coords.top, prevCoords.top, "Moving " + steps + " steps left didn't reach the beginning"); + }, {value: str, lineWrapping: true}) +}; + +testMoveBidi("Say ا ب جabj\nS"); // https://bugs.webkit.org/show_bug.cgi?id=165753 +testMoveBidi("Όȝǝڪȉۥ״ۺ׆ɀҩۏ\nҳ"); +testMoveBidi("ό׊۷٢ԜһОצЉيčǟ\nѩ"); +testMoveBidi("ۑÚҳҕڬġڹհяųKV\nr"); +testMoveBidi("ŌӰтقȤ؁ƥ؅٣ĎȺ١\nϚ"); +testMoveBidi("ٻоҤѕѽΩ־؉ïίքdz\nٵ"); +//testMoveBidi("Count ١ ٢ ٣ ٤"); +testMoveBidi("Sayyy ا ا ب ج"); +testMoveBidi("؅؁ĆՕƿɁǞϮؠȩóć\nď"); +testMoveBidi("źڻғúہ4ם1Ƞc1a\nԁ"); +testMoveBidi("ҒȨҟփƞ٦ԓȦڰғâƥ\nڤ"); +testMoveBidi("քմѧǮßپüŢҍҞўڳ\nӧ"); +testMoveBidi("RŨďңŪzϢŎƏԖڇڦ\nӈ"); +testMoveBidi("ϖسՉȏŧΔԛdžĎӟیڡ\nέ"); +testMoveBidi("۹ؼL۵ĺȧКԙػא7״\nم"); +//testMoveBidi("ӣאƦϰ؊ȓېÛوը٬ز\nϪ"); +//testMoveBidi("ҾճٳџIՖӻ٥׭֐؜ڏ\nێ"); +//testMoveBidi("ҬÓФ؜ڂį٦Ͽɓڐͳٵ\nՈ"); +//testMoveBidi("aѴNijȻهˇ҃ڱӧǻֵ\na"); +//testMoveBidi(" a٧ا٢ ب جa\nS"); +/* +for (var i = 0; i < 5; ++i) { + testMoveBidi(getString(12) + "\n" + getString(1)); +} +*/ + +function testCoordsWrappedBidi(str) { + testCM("coords_wrapped_bidi_" + str, function(cm) { + cm.getScrollerElement().style.fontFamily = "monospace"; + makeItWrapAfter(cm, Pos(0, 5)); + + // Make sure we are at the visual beginning of the first line + var pos = Pos(0, 0), lastPos; + cm.doc.setCursor(pos); + do { + lastPos = pos; + cm.execCommand("goCharLeft"); + pos = cm.doc.getCursor(); + } while (pos != lastPos) + + var top = cm.charCoords(Pos(0, 0)).top, lastTop; + for (var i = 1; i < str.length; ++i) { + lastTop = top; + top = cm.charCoords(Pos(0, i)).top; + is(top >= lastTop); + } + }, {value: str, lineWrapping: true}) +}; + +testCoordsWrappedBidi("Count ١ ٢ ٣ ٤"); +/* +for (var i = 0; i < 5; ++i) { + testCoordsWrappedBidi(getString(50)); +} +*/ + testCM("delete_wrapped", function(cm) { makeItWrapAfter(cm, Pos(0, 2)); cm.doc.setCursor(Pos(0, 3, "after")); From 79e5637b061a854dfd2e57ddb33ee23ac8d0dd74 Mon Sep 17 00:00:00 2001 From: Adrian Heine Date: Wed, 14 Dec 2016 12:47:23 +0100 Subject: [PATCH 0638/1790] Simplify end-of-line commands --- src/edit/commands.js | 23 +++++++++-------------- src/line/spans.js | 7 +++++++ src/util/bidi.js | 14 ++++++++------ 3 files changed, 24 insertions(+), 20 deletions(-) diff --git a/src/edit/commands.js b/src/edit/commands.js index 0b0125d1..ea14b9bc 100644 --- a/src/edit/commands.js +++ b/src/edit/commands.js @@ -1,13 +1,14 @@ import { deleteNearSelection } from "./deleteNearSelection" import { runInOp } from "../display/operations" import { ensureCursorVisible } from "../display/scrolling" +import { endOfLine } from "../input/movement" import { clipPos, Pos } from "../line/pos" -import { collapsedSpanAtEnd, visualLine } from "../line/spans" +import { visualLine, visualLineEnd } from "../line/spans" import { getLine, lineNo } from "../line/utils_line" import { Range } from "../model/selection" import { selectAll } from "../model/selection_updates" import { countColumn, sel_dontScroll, sel_move, spaceStr } from "../util/misc" -import { getOrder, lineLeft, lineRight } from "../util/bidi" +import { getOrder } from "../util/bidi" // Commands are parameter-less actions that can be performed on an // editor, mostly used for keybindings. @@ -156,19 +157,13 @@ function lineStart(cm, lineN) { let line = getLine(cm.doc, lineN) let visual = visualLine(line) if (visual != line) lineN = lineNo(visual) - let order = getOrder(visual) - let ch = !order ? 0 : order[0].level % 2 ? lineRight(visual) : lineLeft(visual) - return Pos(lineN, ch) + return endOfLine(true, cm, visual, lineN, 1) } function lineEnd(cm, lineN) { - let merged, line = getLine(cm.doc, lineN) - while (merged = collapsedSpanAtEnd(line)) { - line = merged.find(1, true).line - lineN = null - } - let order = getOrder(line) - let ch = !order ? line.text.length : order[0].level % 2 ? lineLeft(line) : lineRight(line) - return Pos(lineN == null ? lineNo(line) : lineN, ch) + let line = getLine(cm.doc, lineN) + let visual = visualLineEnd(line) + if (visual != line) lineN = lineNo(visual) + return endOfLine(true, cm, line, lineN, -1) } function lineStartSmart(cm, pos) { let start = lineStart(cm, pos.line) @@ -177,7 +172,7 @@ function lineStartSmart(cm, pos) { if (!order || order[0].level == 0) { let firstNonWS = Math.max(0, line.text.search(/\S/)) let inWS = pos.line == start.line && pos.ch <= firstNonWS && pos.ch - return Pos(start.line, inWS ? 0 : firstNonWS) + return Pos(start.line, inWS ? 0 : firstNonWS, start.sticky) } return start } diff --git a/src/line/spans.js b/src/line/spans.js index 267ee718..b9da7de7 100644 --- a/src/line/spans.js +++ b/src/line/spans.js @@ -248,6 +248,13 @@ export function visualLine(line) { return line } +export function visualLineEnd(line) { + let merged + while (merged = collapsedSpanAtEnd(line)) + line = merged.find(1, true).line + return line +} + // Returns an array of logical lines that continue the visual line // started by the argument, or undefined if there are no such lines. export function visualLineContinued(line) { diff --git a/src/util/bidi.js b/src/util/bidi.js index 7d08e365..ce694be4 100644 --- a/src/util/bidi.js +++ b/src/util/bidi.js @@ -18,13 +18,16 @@ export function iterateBidiSections(order, from, to, f) { export function bidiLeft(part) { return part.level % 2 ? part.to : part.from } export function bidiRight(part) { return part.level % 2 ? part.from : part.to } -export function lineLeft(line) { let order = getOrder(line); return order ? bidiLeft(order[0]) : 0 } -export function lineRight(line) { +function lineAt(line, dir) { let order = getOrder(line) - if (!order) return line.text.length - return bidiRight(lst(order)) + if (!order) return dir == -1 ? line.text.length : 0 + let pos = dir == -1 ? order.length - 1 : 0 + while (order[pos].from == order[pos].to) pos += dir + return (dir == -1 ? bidiRight : bidiLeft)(order[pos]) } +export function lineLeft(line) { return lineAt(line, 1) } +export function lineRight(line) { return lineAt(line, -1) } export let bidiOther = null export function getBidiPartAt(order, ch, sticky) { @@ -81,8 +84,7 @@ export function moveVisually(line, start, dir, byUnit) { } export function moveLogically(line, start, dir, byUnit) { - let target = start + dir - if (byUnit) while (target > 0 && isExtendingChar(line.text.charAt(target))) target += dir + let target = moveInLine(line, start, dir, byUnit) return target < 0 || target > line.text.length ? null : target } From 4c69f2d5dedaf1886f7dbbf0356d2d8dae84981b Mon Sep 17 00:00:00 2001 From: Adrian Heine Date: Wed, 14 Dec 2016 21:15:12 +0100 Subject: [PATCH 0639/1790] Remove empty bidi spans --- src/input/movement.js | 1 - src/util/bidi.js | 5 ----- 2 files changed, 6 deletions(-) diff --git a/src/input/movement.js b/src/input/movement.js index 0464361c..bce3f5c2 100644 --- a/src/input/movement.js +++ b/src/input/movement.js @@ -8,7 +8,6 @@ export function endOfLine(visually, cm, lineObj, dir) { let order = getOrder(lineObj) if (order) { let i = dir < 0 ? order.length - 1 : 0 - while (order[i].to == order[i].from) i += dir let part = order[i] // With a wrapped rtl chunk (possibly spanning multiple bidi parts), // it could be that the last bidi part is not on the last visual line, diff --git a/src/util/bidi.js b/src/util/bidi.js index ce694be4..4ec0578a 100644 --- a/src/util/bidi.js +++ b/src/util/bidi.js @@ -22,7 +22,6 @@ function lineAt(line, dir) { let order = getOrder(line) if (!order) return dir == -1 ? line.text.length : 0 let pos = dir == -1 ? order.length - 1 : 0 - while (order[pos].from == order[pos].to) pos += dir return (dir == -1 ? bidiRight : bidiLeft)(order[pos]) } @@ -251,10 +250,6 @@ export let bidiOrdering = (function() { lst(order).to -= m[0].length order.push(new BidiSpan(0, len - m[0].length, len)) } - if (order[0].level == 2) - order.unshift(new BidiSpan(1, order[0].to, order[0].to)) - if (order[0].level != lst(order).level) - order.push(new BidiSpan(order[0].level, len, len)) return order } From 8dd86df691fbec21b1281cfefc33125615eed45b Mon Sep 17 00:00:00 2001 From: Adrian Heine Date: Wed, 21 Dec 2016 10:40:24 +0100 Subject: [PATCH 0640/1790] Use full Pos objects in move primitives --- src/edit/methods.js | 31 +++++++++++++------------- src/input/movement.js | 51 +++++++++++++++++++++++-------------------- src/util/bidi.js | 7 +++--- test/emacs_test.js | 10 ++++----- test/test.js | 40 ++++++++++++++++----------------- 5 files changed, 71 insertions(+), 68 deletions(-) diff --git a/src/edit/methods.js b/src/edit/methods.js index 1cce4f3d..52ce0aee 100644 --- a/src/edit/methods.js +++ b/src/edit/methods.js @@ -465,26 +465,27 @@ export default function(CodeMirror) { // position. The resulting position will have a hitSide=true // property if it reached the end of the document. function findPosH(doc, pos, dir, unit, visually) { - let {line, ch, sticky} = pos, origDir = dir - let lineObj = getLine(doc, line) + let oldPos = pos + let origDir = dir + let lineObj = getLine(doc, pos.line) function findNextLine() { - let l = line + dir + let l = pos.line + dir if (l < doc.first || l >= doc.first + doc.size) return false - line = l + pos = new Pos(l, pos.ch, pos.sticky) return lineObj = getLine(doc, l) } function moveOnce(boundToLine) { - let myMoveVisually = (line, start, dir, byUnit) => { - let res = moveVisually(doc.cm, line, start, dir, byUnit, sticky) - if (res.ch) sticky = res.sticky - return res.ch + let myMoveVisually = (line, start, dir) => moveVisually(doc.cm, line, start, dir) + let myMoveLogically = (line, start, dir) => { + let ch = moveLogically(line, start, dir) + return ch == null ? null : new Pos(pos.line, ch, dir < 0 ? "after" : "before") } - let next = (visually ? myMoveVisually : moveLogically)(lineObj, ch, dir, true) + let next = (visually ? myMoveVisually : myMoveLogically)(lineObj, pos, dir) if (next == null) { if (!boundToLine && findNextLine()) { - ;({ch, sticky} = endOfLine(visually, doc.cm, lineObj, dir)) + pos = endOfLine(visually, doc.cm, lineObj, pos.line, dir) } else return false - } else ch = next + } else pos = next return true } @@ -497,14 +498,14 @@ function findPosH(doc, pos, dir, unit, visually) { let helper = doc.cm && doc.cm.getHelper(pos, "wordChars") for (let first = true;; first = false) { if (dir < 0 && !moveOnce(!first)) break - let cur = lineObj.text.charAt(ch) || "\n" + let cur = lineObj.text.charAt(pos.ch) || "\n" let type = isWordChar(cur, helper) ? "w" : group && cur == "\n" ? "n" : !group || /\s/.test(cur) ? null : "p" if (group && !first && !type) type = "s" if (sawType && sawType != type) { - if (dir < 0) {dir = 1; moveOnce()} + if (dir < 0) {dir = 1; moveOnce(); pos.sticky = "after"} break } @@ -512,8 +513,8 @@ function findPosH(doc, pos, dir, unit, visually) { if (dir > 0 && !moveOnce(!first)) break } } - let result = skipAtomic(doc, Pos(line, ch, sticky), pos, origDir, true) - if (equalCursorPos(pos, result)) result.hitSide = true + let result = skipAtomic(doc, pos, oldPos, origDir, true) + if (equalCursorPos(oldPos, result)) result.hitSide = true return result } diff --git a/src/input/movement.js b/src/input/movement.js index bce3f5c2..95cc50fc 100644 --- a/src/input/movement.js +++ b/src/input/movement.js @@ -1,14 +1,16 @@ +import { Pos } from "../line/pos" import { prepareMeasureCharTop } from "../measurement/position_measurement" import { bidiLeft, bidiRight, getBidiPartAt, getOrder, lineLeft, lineRight, moveLogically } from "../util/bidi" import { findFirst } from "../util/misc" -export function endOfLine(visually, cm, lineObj, dir) { - let ch, sticky = "before" +export function endOfLine(visually, cm, lineObj, lineNo, dir) { + let ch if (visually) { let order = getOrder(lineObj) if (order) { let i = dir < 0 ? order.length - 1 : 0 let part = order[i] + let sticky = (dir < 0) != (part.level == 1) ? "before" : "after" // With a wrapped rtl chunk (possibly spanning multiple bidi parts), // it could be that the last bidi part is not on the last visual line, // since visual lines contain content order-consecutive chunks. @@ -20,17 +22,17 @@ export function endOfLine(visually, cm, lineObj, dir) { ch = lineObj.text.length - 1 let targetTop = getTop(ch) ch = findFirst(ch => getTop(ch) == targetTop, part.from, ch) - if (part.level == 1) sticky = "after" - else ch = moveLogically(lineObj, ch, 1, true) - return {ch, sticky} + if (part.level != 1) ch = moveLogically(lineObj, new Pos(lineNo, ch, sticky), 1, true) + return new Pos(lineNo, ch, sticky) } ch = (dir < 0 ? bidiRight : bidiLeft)(part) - return {ch, sticky} + return new Pos(lineNo, ch, sticky) } } + let sticky = dir < 0 ? "before" : "after" if (visually) ch = (dir < 0 ? lineRight : lineLeft)(lineObj) else ch = dir < 0 ? lineObj.text.length : 0 - return {ch, sticky} + return new Pos(lineNo, ch, sticky) } function getVisualLineAround(cm, line, target) { @@ -43,33 +45,34 @@ function getVisualLineAround(cm, line, target) { ] } -export function moveVisually(cm, line, start, dir, byUnit, startSticky) { - let mv = (ch, dir) => moveLogically(line, ch, dir, byUnit) +export function moveVisually(cm, line, start, dir) { + let mkPos = (ch, sticky) => ch == null ? null : new Pos(start.line, ch, sticky) + let mv = (pos, dir) => moveLogically(line, pos instanceof Pos ? pos : new Pos(start.line, pos), dir) let bidi = getOrder(line) - if (!bidi) return {ch: mv(start, dir), sticky: dir < 0 ? "after" : "before"} - if (start >= line.text.length) { - start = line.text.length - startSticky = "before" - } else if (start <= 0) { - start = 0 - startSticky = "after" + if (!bidi) return mkPos(mv(start, dir), dir < 0 ? "after" : "before") + if (start.ch >= line.text.length) { + start.ch = line.text.length + start.sticky = "before" + } else if (start.ch <= 0) { + start.ch = 0 + start.sticky = "after" } - let partPos = getBidiPartAt(bidi, start, startSticky), part = bidi[partPos] - if (part.level % 2 == 0 && (dir > 0 ? part.to > start : part.from < start)) { + let partPos = getBidiPartAt(bidi, start.ch, start.sticky), part = bidi[partPos] + if (part.level % 2 == 0 && (dir > 0 ? part.to > start.ch : part.from < start.ch)) { // Case 1: We move within an ltr part. Even with wrapped lines, // nothing interesting happens. - return {ch: mv(start, dir), sticky: dir < 0 ? "after" : "before"} + return mkPos(mv(start, dir), dir < 0 ? "after" : "before") } let getVisualLine = ch => getVisualLineAround(cm, line, ch) - let visualLine = getVisualLine(startSticky == "before" ? mv(start, -1) : start) + let visualLine = getVisualLine(start.sticky == "before" ? mv(start, -1) : start.ch) if (part.level % 2 == 1) { let ch = mv(start, -dir) if (ch != null && (dir > 0 ? ch >= part.from && ch >= visualLine[0] : ch <= part.to && ch <= mv(visualLine[1], 1))) { // Case 2: We move within an rtl part on the same visual line let sticky = dir < 0 ? "before" : "after" - return {ch, sticky} + return new Pos(start.line, ch, sticky) } } @@ -78,8 +81,8 @@ export function moveVisually(cm, line, start, dir, byUnit, startSticky) { let searchInVisualLine = (partPos, dir, visualLine) => { let getRes = (ch, moveInStorageOrder) => moveInStorageOrder - ? {ch: mv(ch, 1), sticky: "before"} - : {ch, sticky: "after"} + ? new Pos(start.line, mv(ch, 1), "before") + : new Pos(start.line, ch, "after") for (partPos += dir; partPos >= 0 && partPos < bidi.length; partPos += dir) { let part = bidi[partPos] @@ -103,5 +106,5 @@ export function moveVisually(cm, line, start, dir, byUnit, startSticky) { } // Case 4: Nowhere to move - return {ch: null, sticky: null} + return null } diff --git a/src/util/bidi.js b/src/util/bidi.js index 4ec0578a..4f0e5a37 100644 --- a/src/util/bidi.js +++ b/src/util/bidi.js @@ -47,8 +47,7 @@ export function getBidiPartAt(order, ch, sticky) { return found != null ? found : bidiOther } -function moveInLine(line, pos, dir, byUnit) { - if (!byUnit) return pos + dir +function moveInLine(line, pos, dir) { do pos += dir while (pos > 0 && isExtendingChar(line.text.charAt(pos))) return pos @@ -82,8 +81,8 @@ export function moveVisually(line, start, dir, byUnit) { } } -export function moveLogically(line, start, dir, byUnit) { - let target = moveInLine(line, start, dir, byUnit) +export function moveLogically(line, start, dir) { + let target = moveInLine(line, start.ch, dir) return target < 0 || target > line.text.length ? null : target } diff --git a/test/emacs_test.js b/test/emacs_test.js index 628651c7..acb4ba2b 100644 --- a/test/emacs_test.js +++ b/test/emacs_test.js @@ -48,13 +48,13 @@ "Ctrl-5", "Ctrl-B", at(0, 0)); sim("motionHWord", "abc. def ghi", - "Alt-F", at(0, 3), "Alt-F", at(0, 8), - "Ctrl-B", "Alt-B", at(0, 5), "Alt-B", at(0, 0)); + "Alt-F", at(0, 3, "before"), "Alt-F", at(0, 8, "before"), + "Ctrl-B", "Alt-B", at(0, 5, "after"), "Alt-B", at(0, 0, "after")); sim("motionHWordMulti", "abc. def ghi ", - "Ctrl-3", "Alt-F", at(0, 12), "Ctrl-2", "Alt-B", at(0, 5), - "Ctrl--", "Alt-B", at(0, 8)); + "Ctrl-3", "Alt-F", at(0, 12, "before"), "Ctrl-2", "Alt-B", at(0, 5, "after"), + "Ctrl--", "Alt-B", at(0, 8, "before")); - sim("motionVSimple", "a\nb\nc\n", "Ctrl-N", "Ctrl-N", "Ctrl-P", at(1, 0)); + sim("motionVSimple", "a\nb\nc\n", "Ctrl-N", "Ctrl-N", "Ctrl-P", at(1, 0, "after")); sim("motionVMulti", "a\nb\nc\nd\ne\n", "Ctrl-2", "Ctrl-N", at(2, 0), "Ctrl-F", "Ctrl--", "Ctrl-N", at(1, 1), "Ctrl--", "Ctrl-3", "Ctrl-P", at(4, 1)); diff --git a/test/test.js b/test/test.js index 00c0be25..e74f11d8 100644 --- a/test/test.js +++ b/test/test.js @@ -1234,11 +1234,11 @@ testCM("wordMovementCommands", function(cm) { cm.execCommand("goWordRight"); cm.execCommand("goWordRight"); eqPos(cm.getCursor(), Pos(0, 7)); cm.execCommand("goWordLeft"); - eqPos(cm.getCursor(), Pos(0, 5)); + eqCursorPos(cm.getCursor(), Pos(0, 5, "after")); cm.execCommand("goWordRight"); cm.execCommand("goWordRight"); eqPos(cm.getCursor(), Pos(0, 12)); cm.execCommand("goWordLeft"); - eqPos(cm.getCursor(), Pos(0, 9)); + eqCursorPos(cm.getCursor(), Pos(0, 9, "after")); cm.execCommand("goWordRight"); cm.execCommand("goWordRight"); cm.execCommand("goWordRight"); eqPos(cm.getCursor(), Pos(0, 24)); cm.execCommand("goWordRight"); cm.execCommand("goWordRight"); @@ -1259,14 +1259,14 @@ testCM("groupMovementCommands", function(cm) { cm.execCommand("goGroupRight"); eqPos(cm.getCursor(), Pos(0, 10)); cm.execCommand("goGroupLeft"); - eqPos(cm.getCursor(), Pos(0, 7)); + eqCursorPos(cm.getCursor(), Pos(0, 7, "after")); 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)); + eqCursorPos(cm.getCursor(), Pos(0, 16, "after")); cm.execCommand("goGroupLeft"); - eqPos(cm.getCursor(), Pos(0, 14)); + eqCursorPos(cm.getCursor(), Pos(0, 14, "after")); cm.execCommand("goGroupRight"); cm.execCommand("goGroupRight"); eqPos(cm.getCursor(), Pos(0, 20)); cm.execCommand("goGroupRight"); @@ -1278,9 +1278,9 @@ testCM("groupMovementCommands", function(cm) { cm.execCommand("goGroupLeft"); cm.execCommand("goGroupLeft"); eqPos(cm.getCursor(), Pos(1, 0)); cm.execCommand("goGroupLeft"); - eqPos(cm.getCursor(), Pos(0, 20)); + eqCursorPos(cm.getCursor(), Pos(0, 20, "after")); cm.execCommand("goGroupLeft"); - eqPos(cm.getCursor(), Pos(0, 16)); + eqCursorPos(cm.getCursor(), Pos(0, 16, "after")); }, {value: "booo ba---quux. ffff\n abc d"}); testCM("groupsAndWhitespace", function(cm) { @@ -1843,20 +1843,20 @@ testCM("addKeyMap", function(cm) { }, {value: "abc"}); testCM("findPosH", function(cm) { - forEach([{from: Pos(0, 0), to: Pos(0, 1), by: 1}, + forEach([{from: Pos(0, 0), to: Pos(0, 1, "before"), 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) { + {from: Pos(0, 0), to: Pos(0, 4, "before"), by: 1, unit: "word"}, + {from: Pos(0, 0), to: Pos(0, 8, "before"), by: 2, unit: "word"}, + {from: Pos(0, 0), to: Pos(2, 0, "before"), by: 20, unit: "word", hitSide: true}, + {from: Pos(0, 7), to: Pos(0, 5, "after"), by: -1, unit: "word"}, + {from: Pos(0, 4), to: Pos(0, 8, "before"), by: 1, unit: "word"}, + {from: Pos(1, 0), to: Pos(1, 18, "before"), by: 3, unit: "word"}, + {from: Pos(1, 22), to: Pos(1, 5, "after"), by: -3, unit: "word"}, + {from: Pos(1, 15), to: Pos(1, 10, "after"), by: -5}, + {from: Pos(1, 15), to: Pos(1, 10, "after"), by: -5, unit: "column"}, + {from: Pos(1, 15), to: Pos(1, 0, "after"), by: -50, unit: "column", hitSide: true}, + {from: Pos(1, 15), to: Pos(1, 24, "before"), by: 50, unit: "column", hitSide: true}, + {from: Pos(1, 15), to: Pos(2, 0, "before"), 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); From 864a681b4d57823687c2bb6251e5c4bccbe7d1d4 Mon Sep 17 00:00:00 2001 From: Adrian Heine Date: Mon, 3 Oct 2016 19:36:23 +0200 Subject: [PATCH 0641/1790] Reimplement coordsChar --- src/edit/methods.js | 2 +- src/measurement/position_measurement.js | 90 +++++++++++-------------- src/util/bidi.js | 28 -------- test/emacs_test.js | 4 +- test/test.js | 4 +- 5 files changed, 44 insertions(+), 84 deletions(-) diff --git a/src/edit/methods.js b/src/edit/methods.js index 52ce0aee..18412d31 100644 --- a/src/edit/methods.js +++ b/src/edit/methods.js @@ -336,7 +336,7 @@ export default function(CodeMirror) { let start = pos.ch, end = pos.ch if (line) { let helper = this.getHelper(pos, "wordChars") - if ((pos.xRel < 0 || end == line.length) && start) --start; else ++end + if ((pos.sticky == "before" || end == line.length) && start) --start; else ++end let startChar = line.charAt(start) let check = isWordChar(startChar, helper) ? ch => isWordChar(ch, helper) diff --git a/src/measurement/position_measurement.js b/src/measurement/position_measurement.js index d87ec7a0..531ab1d7 100644 --- a/src/measurement/position_measurement.js +++ b/src/measurement/position_measurement.js @@ -1,13 +1,14 @@ +import { moveVisually } from "../input/movement" import { buildLineContent, LineView } from "../line/line_data" import { clipPos, Pos } from "../line/pos" import { collapsedSpanAtEnd, heightAtLine, lineIsHidden, visualLine } from "../line/spans" import { getLine, lineAtHeight, lineNo, updateLineHeight } from "../line/utils_line" -import { bidiOther, getBidiPartAt, getOrder, lineLeft, lineRight, moveVisually } from "../util/bidi" +import { bidiOther, getBidiPartAt, getOrder } from "../util/bidi" import { ie, ie_version } from "../util/browser" import { elt, removeChildren, range, removeChildrenAndAdd } from "../util/dom" import { e_target } from "../util/event" import { hasBadZoomedRects } from "../util/feature_detection" -import { countColumn, isExtendingChar, scrollerGap } from "../util/misc" +import { countColumn, findFirst, isExtendingChar, scrollerGap } from "../util/misc" import { updateLineForChanges } from "../display/update_line" import { widgetHeight } from "./widgets" @@ -429,59 +430,46 @@ export function coordsChar(cm, x, y) { } function coordsCharInner(cm, lineObj, lineNo, x, y) { - let innerOff = y - heightAtLine(lineObj) - let wrongLine = false, adjust = 2 * cm.display.wrapper.clientWidth + y -= heightAtLine(lineObj) + let begin = 0, end = lineObj.text.length - 1 let preparedMeasure = prepareMeasureForLine(cm, lineObj) - - function getX(ch) { - let sp = cursorCoords(cm, Pos(lineNo, ch), "line", lineObj, preparedMeasure) - wrongLine = true - if (innerOff > sp.bottom) return sp.left - adjust - else if (innerOff < sp.top) return sp.left + adjust - else wrongLine = false - return sp.left + if (cm.options.lineWrapping) { + let measure = ch => intoCoordSystem(cm, lineObj, measureCharPrepared(cm, preparedMeasure, ch), "line") + begin = findFirst(ch => measure(ch).bottom < y, end, begin - 1) + 1 + end = findFirst(ch => measure(ch).top > y, begin, end + 1) - 1 } - - let bidi = getOrder(lineObj), dist = lineObj.text.length - let from = lineLeft(lineObj), to = lineRight(lineObj) - let fromX = getX(from), fromOutside = wrongLine, toX = getX(to), toOutside = wrongLine - - if (x > toX) return PosWithInfo(lineNo, to, null, toOutside, 1) - // Do a binary search between these bounds. - for (;;) { - if (bidi ? to == from || to == moveVisually(lineObj, from, 1) : to - from <= 1) { - let ch = x < fromX || x - fromX <= toX - x ? from : to - let outside = ch == from ? fromOutside : toOutside - let xDiff = x - (ch == from ? fromX : toX) - // This is a kludge to handle the case where the coordinates - // are after a line-wrapped line. We should replace it with a - // more general handling of cursor positions around line - // breaks. (Issue #4078) - if (toOutside && !bidi && !/\s/.test(lineObj.text.charAt(ch)) && xDiff > 0 && - ch < lineObj.text.length && preparedMeasure.view.measure.heights.length > 1) { - let charSize = measureCharPrepared(cm, preparedMeasure, ch, "right") - if (innerOff <= charSize.bottom && innerOff >= charSize.top && Math.abs(x - charSize.right) < xDiff) { - outside = false - ch++ - xDiff = x - charSize.right - } - let sticky = null - if (toX > cm.display.wrapper.clientWidth) { - sticky = "before" + let pos + let order = getOrder(lineObj) + if (order) { + if (end == lineObj.text.length - 1) ++end + pos = new Pos(lineNo, begin) + let beginLeft = cursorCoords(cm, pos, "line", lineObj, preparedMeasure).left + let dir = beginLeft < x ? 1 : -1 + let prevDiff, diff = beginLeft - x + do { + prevDiff = diff + let prevPos = pos + pos = moveVisually(cm, lineObj, pos, dir) + if (pos == null || pos.ch < begin || end < pos.ch) { + pos = prevPos + break } - while (isExtendingChar(lineObj.text.charAt(ch))) ++ch - let pos = PosWithInfo(lineNo, ch, sticky, outside, xDiff < -1 ? -1 : xDiff > 1 ? 1 : 0) - return pos - } - let step = Math.ceil(dist / 2), middle = from + step - if (bidi) { - middle = from - for (let i = 0; i < step; ++i) middle = moveVisually(lineObj, middle, 1) - } - let 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} + diff = cursorCoords(cm, pos, "line", lineObj, preparedMeasure).left - x + } while ((dir < 0) != (diff < 0)) + // moveVisually has the nice side effect of skipping extending chars and setting sticky + if (Math.abs(diff) > Math.abs(prevDiff)) pos = moveVisually(cm, lineObj, pos, -dir) + } else { + let ch = findFirst(ch => { + let box = measureCharPrepared(cm, preparedMeasure, ch) + return x - box.right < box.left - x + }, begin, end + 1) + while (isExtendingChar(lineObj.text.charAt(ch))) ++ch + pos = new Pos(lineNo, ch, (ch == end + 1) ? "before" : "after") } + let coords = cursorCoords(cm, pos, "line", lineObj, preparedMeasure) + if (y < coords.top || coords.bottom < y) pos.outside = true + pos.xRel = x < coords.left ? -1 : (x > coords.right ? 1 : 0) + return pos } let measureText diff --git a/src/util/bidi.js b/src/util/bidi.js index 4f0e5a37..f10ee707 100644 --- a/src/util/bidi.js +++ b/src/util/bidi.js @@ -53,34 +53,6 @@ function moveInLine(line, pos, dir) { return pos } -// This 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. -export function moveVisually(line, start, dir, byUnit) { - let bidi = getOrder(line) - if (!bidi) return moveLogically(line, start, dir, byUnit) - let pos = getBidiPartAt(bidi, start), part = bidi[pos] - let target = moveInLine(line, start, part.level % 2 ? -dir : dir, byUnit) - - for (;;) { - if (target > part.from && target < part.to) return target - if (target == part.from || target == part.to) { - if (getBidiPartAt(bidi, target) == pos) return target - part = bidi[pos += dir] - return (dir > 0) == part.level % 2 ? part.to : part.from - } else { - part = bidi[pos += dir] - if (!part) return null - if ((dir > 0) == part.level % 2) - target = moveInLine(line, part.to, -1, byUnit) - else - target = moveInLine(line, part.from, 1, byUnit) - } - } -} - export function moveLogically(line, start, dir) { let target = moveInLine(line, start.ch, dir) return target < 0 || target > line.text.length ? null : target diff --git a/test/emacs_test.js b/test/emacs_test.js index acb4ba2b..2e176423 100644 --- a/test/emacs_test.js +++ b/test/emacs_test.js @@ -56,8 +56,8 @@ sim("motionVSimple", "a\nb\nc\n", "Ctrl-N", "Ctrl-N", "Ctrl-P", at(1, 0, "after")); sim("motionVMulti", "a\nb\nc\nd\ne\n", - "Ctrl-2", "Ctrl-N", at(2, 0), "Ctrl-F", "Ctrl--", "Ctrl-N", at(1, 1), - "Ctrl--", "Ctrl-3", "Ctrl-P", at(4, 1)); + "Ctrl-2", "Ctrl-N", at(2, 0, "after"), "Ctrl-F", "Ctrl--", "Ctrl-N", at(1, 1, "before"), + "Ctrl--", "Ctrl-3", "Ctrl-P", at(4, 1, "before")); sim("killYank", "abc\ndef\nghi", "Ctrl-F", "Ctrl-Space", "Ctrl-N", "Ctrl-N", "Ctrl-W", "Ctrl-E", "Ctrl-Y", diff --git a/test/test.js b/test/test.js index e74f11d8..2367de6a 100644 --- a/test/test.js +++ b/test/test.js @@ -1104,7 +1104,7 @@ testCM("measureEndOfLine", function(cm) { is(endPos.top > lh * .8, "not at top"); is(endPos.left > w - 20, "not at right"); endPos = cm.charCoords(Pos(0, 18)); - eqPos(cm.coordsChar({left: endPos.left, top: endPos.top + 5}), Pos(0, 18)); + eqCursorPos(cm.coordsChar({left: endPos.left, top: endPos.top + 5}), Pos(0, 18, "before")); var wrapPos = cm.cursorCoords(Pos(0, 9, "before")); is(wrapPos.top < endPos.top, "wrapPos is actually in first line"); @@ -1153,7 +1153,7 @@ testCM("moveVstuck", function(cm) { } cm.setCursor(Pos(0, val.length - 1)); cm.moveV(-1, "line"); - eqPos(cm.getCursor(), Pos(0, 26, "before")); + eqPos(cm.getCursor(), Pos(0, 27, "before")); is(cm.cursorCoords(null, "local").top < h0, "cursor is in first visual line"); }, {lineWrapping: true}, ie_lt8 || opera_lt10); From a0f69ddf3d82a5bac7fe3dea8fbac5a818bd108e Mon Sep 17 00:00:00 2001 From: Adrian Heine Date: Wed, 14 Dec 2016 12:49:01 +0100 Subject: [PATCH 0642/1790] Adapt tests --- test/doc_test.js | 24 +-- test/driver.js | 8 +- test/emacs_test.js | 8 +- test/multi_test.js | 18 +- test/search_test.js | 8 +- test/sql-hint-test.js | 4 +- test/sublime_test.js | 8 +- test/test.js | 410 +++++++++++++++++++++--------------------- test/vim_test.js | 122 ++++++------- 9 files changed, 304 insertions(+), 306 deletions(-) diff --git a/test/doc_test.js b/test/doc_test.js index 5f242f65..3af20ff9 100644 --- a/test/doc_test.js +++ b/test/doc_test.js @@ -163,13 +163,13 @@ testDoc("undoKeepRanges", "A='abcdefg' B margin) throw new Failure(label(a + " is not close to " + b + " (" + margin + ")", msg)); } -function eqPos(a, b, msg) { +function eqCharPos(a, b, msg) { function str(p) { return "{line:" + p.line + ",ch:" + p.ch + ",sticky:" + p.sticky + "}"; } if (a == b) return; 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 || a.sticky != b.sticky) throw new Failure(label(str(a) + " != " + str(b), msg)); + if (a.line != b.line || a.ch != b.ch) throw new Failure(label(str(a) + " != " + str(b), msg)); +} +function eqCursorPos(a, b, msg) { + eqCharPos(a, b, msg); + if (a) eq(a.sticky, b.sticky, msg ? msg + ' (sticky)' : 'sticky'); } function is(a, msg) { if (!a) throw new Failure(label("assertion failed", msg)); diff --git a/test/emacs_test.js b/test/emacs_test.js index 2e176423..b73eedaa 100644 --- a/test/emacs_test.js +++ b/test/emacs_test.js @@ -39,13 +39,13 @@ }, {keyMap: "emacs", value: start, mode: "javascript"}); } - function at(line, ch) { return function(cm) { eqPos(cm.getCursor(), Pos(line, ch)); }; } + function at(line, ch, sticky) { return function(cm) { eqCursorPos(cm.getCursor(), Pos(line, ch, sticky)); }; } function txt(str) { return function(cm) { eq(cm.getValue(), str); }; } - sim("motionHSimple", "abc", "Ctrl-F", "Ctrl-F", "Ctrl-B", at(0, 1)); + sim("motionHSimple", "abc", "Ctrl-F", "Ctrl-F", "Ctrl-B", at(0, 1, "after")); sim("motionHMulti", "abcde", - "Ctrl-4", "Ctrl-F", at(0, 4), "Ctrl--", "Ctrl-2", "Ctrl-F", at(0, 2), - "Ctrl-5", "Ctrl-B", at(0, 0)); + "Ctrl-4", "Ctrl-F", at(0, 4, "before"), "Ctrl--", "Ctrl-2", "Ctrl-F", at(0, 2, "after"), + "Ctrl-5", "Ctrl-B", at(0, 0, "after")); sim("motionHWord", "abc. def ghi", "Alt-F", at(0, 3, "before"), "Alt-F", at(0, 8, "before"), diff --git a/test/multi_test.js b/test/multi_test.js index a8e760d2..49169b99 100644 --- a/test/multi_test.js +++ b/test/multi_test.js @@ -9,8 +9,8 @@ for (var i = 0, p = 1; i < given; i++, p += 4) { var anchor = Pos(arguments[p], arguments[p + 1]); var head = Pos(arguments[p + 2], arguments[p + 3]); - eqPos(sels[i].anchor, anchor, "anchor of selection " + i); - eqPos(sels[i].head, head, "head of selection " + i); + eqCharPos(sels[i].anchor, anchor, "anchor of selection " + i); + eqCharPos(sels[i].head, head, "head of selection " + i); } } function hasCursors(cm) { @@ -19,9 +19,9 @@ if (sels.length != given) throw new Failure("expected " + given + " selections, found " + sels.length); for (var i = 0, p = 1; i < given; i++, p += 2) { - eqPos(sels[i].anchor, sels[i].head, "something selected for " + i); + eqCursorPos(sels[i].anchor, sels[i].head, "something selected for " + i); var head = Pos(arguments[p], arguments[p + 1]); - eqPos(sels[i].head, head, "selection " + i); + eqCharPos(sels[i].head, head, "selection " + i); } } @@ -47,10 +47,10 @@ cm.setSelections([{anchor: Pos(0, 1), head: Pos(0, 2)}, {anchor: Pos(1, 1), head: Pos(1, 2)}, {anchor: Pos(2, 1), head: Pos(2, 2)}], 1); - eqPos(cm.getCursor("head"), Pos(1, 2)); - eqPos(cm.getCursor("anchor"), Pos(1, 1)); - eqPos(cm.getCursor("from"), Pos(1, 1)); - eqPos(cm.getCursor("to"), Pos(1, 2)); + eqCharPos(cm.getCursor("head"), Pos(1, 2)); + eqCharPos(cm.getCursor("anchor"), Pos(1, 1)); + eqCharPos(cm.getCursor("from"), Pos(1, 1)); + eqCharPos(cm.getCursor("to"), Pos(1, 2)); cm.setCursor(Pos(1, 1)); hasCursors(cm, 1, 1); }, {value: "abcde\nabcde\nabcde\n"}); @@ -274,7 +274,7 @@ eq(cm.getSelection(), "1"); cm.execCommand("undoSelection"); eq(cm.getSelection(), ""); - eqPos(cm.getCursor(), Pos(0, 0)); + eqCharPos(cm.getCursor(), Pos(0, 0)); cm.execCommand("redoSelection"); eq(cm.getSelection(), "1"); cm.execCommand("redoSelection"); diff --git a/test/search_test.js b/test/search_test.js index 04a1e685..9fe898ba 100644 --- a/test/search_test.js +++ b/test/search_test.js @@ -14,15 +14,15 @@ for (var i = 3; i < arguments.length; i += 4) { var found = cursor.findNext(); is(found, "not enough results (forward)"); - eqPos(Pos(arguments[i], arguments[i + 1]), cursor.from(), "from, forward, " + (i - 3) / 4); - eqPos(Pos(arguments[i + 2], arguments[i + 3]), cursor.to(), "to, forward, " + (i - 3) / 4); + eqCharPos(Pos(arguments[i], arguments[i + 1]), cursor.from(), "from, forward, " + (i - 3) / 4); + eqCharPos(Pos(arguments[i + 2], arguments[i + 3]), cursor.to(), "to, forward, " + (i - 3) / 4); } is(!cursor.findNext(), "too many matches (forward)"); for (var i = arguments.length - 4; i >= 3; i -= 4) { var found = cursor.findPrevious(); is(found, "not enough results (backwards)"); - eqPos(Pos(arguments[i], arguments[i + 1]), cursor.from(), "from, backwards, " + (i - 3) / 4); - eqPos(Pos(arguments[i + 2], arguments[i + 3]), cursor.to(), "to, backwards, " + (i - 3) / 4); + eqCharPos(Pos(arguments[i], arguments[i + 1]), cursor.from(), "from, backwards, " + (i - 3) / 4); + eqCharPos(Pos(arguments[i + 2], arguments[i + 3]), cursor.to(), "to, backwards, " + (i - 3) / 4); } is(!cursor.findPrevious(), "too many matches (backwards)"); } diff --git a/test/sql-hint-test.js b/test/sql-hint-test.js index 39f2172e..31d0490f 100644 --- a/test/sql-hint-test.js +++ b/test/sql-hint-test.js @@ -30,8 +30,8 @@ var completion = CodeMirror.hint.sql(cm, {tables: spec.tables}); if (!deepCompare(completion.list, spec.list)) throw new Failure("Wrong completion results " + JSON.stringify(completion.list) + " vs " + JSON.stringify(spec.list)); - eqPos(completion.from, spec.from); - eqPos(completion.to, spec.to); + eqCharPos(completion.from, spec.from); + eqCharPos(completion.to, spec.to); }, { value: spec.value, mode: "text/x-mysql" diff --git a/test/sublime_test.js b/test/sublime_test.js index 57f16485..e9cd342f 100644 --- a/test/sublime_test.js +++ b/test/sublime_test.js @@ -24,8 +24,8 @@ function at(line, ch, msg) { return function(cm) { eq(cm.listSelections().length, 1); - eqPos(cm.getCursor("head"), Pos(line, ch), msg); - eqPos(cm.getCursor("anchor"), Pos(line, ch), msg); + eqCursorPos(cm.getCursor("head"), Pos(line, ch), msg); + eqCursorPos(cm.getCursor("anchor"), Pos(line, ch), msg); }; } @@ -54,8 +54,8 @@ if (sels.length != ranges.length) throw new Failure("Expected " + ranges.length + " selections, but found " + sels.length); for (var i = 0; i < sels.length; i++) { - eqPos(sels[i].anchor, ranges[i].anchor, "anchor " + i); - eqPos(sels[i].head, ranges[i].head, "head " + i); + eqCharPos(sels[i].anchor, ranges[i].anchor, "anchor " + i); + eqCharPos(sels[i].head, ranges[i].head, "head " + i); } }; } diff --git a/test/test.js b/test/test.js index 2367de6a..38659f87 100644 --- a/test/test.js +++ b/test/test.js @@ -34,6 +34,7 @@ var opera = /Opera\/\./.test(navigator.userAgent); 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); +var webkit = /WebKit\//.test(navigator.userAgent); namespace = "core_"; @@ -82,59 +83,59 @@ testCM("selection", function(cm) { cm.setSelection(Pos(0, 4), Pos(2, 2)); is(cm.somethingSelected()); eq(cm.getSelection(), "11\n222222\n33"); - eqPos(cm.getCursor(false), Pos(2, 2)); - eqPos(cm.getCursor(true), Pos(0, 4)); + eqCursorPos(cm.getCursor(false), Pos(2, 2)); + eqCursorPos(cm.getCursor(true), Pos(0, 4)); cm.setSelection(Pos(1, 0)); is(!cm.somethingSelected()); eq(cm.getSelection(), ""); - eqPos(cm.getCursor(true), Pos(1, 0)); + eqCursorPos(cm.getCursor(true), Pos(1, 0)); cm.replaceSelection("abc", "around"); eq(cm.getSelection(), "abc"); eq(cm.getValue(), "111111\nabc222222\n333333"); cm.replaceSelection("def", "end"); eq(cm.getSelection(), ""); - eqPos(cm.getCursor(true), Pos(1, 3)); + eqCursorPos(cm.getCursor(true), Pos(1, 3)); cm.setCursor(Pos(2, 1)); - eqPos(cm.getCursor(true), Pos(2, 1)); + eqCursorPos(cm.getCursor(true), Pos(2, 1)); cm.setCursor(1, 2); - eqPos(cm.getCursor(true), Pos(1, 2)); + eqCursorPos(cm.getCursor(true), Pos(1, 2)); }, {value: "111111\n222222\n333333"}); testCM("extendSelection", function(cm) { cm.setExtending(true); addDoc(cm, 10, 10); cm.setSelection(Pos(3, 5)); - eqPos(cm.getCursor("head"), Pos(3, 5)); - eqPos(cm.getCursor("anchor"), Pos(3, 5)); + eqCursorPos(cm.getCursor("head"), Pos(3, 5)); + eqCursorPos(cm.getCursor("anchor"), Pos(3, 5)); cm.setSelection(Pos(2, 5), Pos(5, 5)); - eqPos(cm.getCursor("head"), Pos(5, 5)); - eqPos(cm.getCursor("anchor"), Pos(2, 5)); - eqPos(cm.getCursor("start"), Pos(2, 5)); - eqPos(cm.getCursor("end"), Pos(5, 5)); + eqCursorPos(cm.getCursor("head"), Pos(5, 5)); + eqCursorPos(cm.getCursor("anchor"), Pos(2, 5)); + eqCursorPos(cm.getCursor("start"), Pos(2, 5)); + eqCursorPos(cm.getCursor("end"), Pos(5, 5)); cm.setSelection(Pos(5, 5), Pos(2, 5)); - eqPos(cm.getCursor("head"), Pos(2, 5)); - eqPos(cm.getCursor("anchor"), Pos(5, 5)); - eqPos(cm.getCursor("start"), Pos(2, 5)); - eqPos(cm.getCursor("end"), Pos(5, 5)); + eqCursorPos(cm.getCursor("head"), Pos(2, 5)); + eqCursorPos(cm.getCursor("anchor"), Pos(5, 5)); + eqCursorPos(cm.getCursor("start"), Pos(2, 5)); + eqCursorPos(cm.getCursor("end"), Pos(5, 5)); cm.extendSelection(Pos(3, 2)); - eqPos(cm.getCursor("head"), Pos(3, 2)); - eqPos(cm.getCursor("anchor"), Pos(5, 5)); + eqCursorPos(cm.getCursor("head"), Pos(3, 2)); + eqCursorPos(cm.getCursor("anchor"), Pos(5, 5)); cm.extendSelection(Pos(6, 2)); - eqPos(cm.getCursor("head"), Pos(6, 2)); - eqPos(cm.getCursor("anchor"), Pos(5, 5)); + eqCursorPos(cm.getCursor("head"), Pos(6, 2)); + eqCursorPos(cm.getCursor("anchor"), Pos(5, 5)); cm.extendSelection(Pos(6, 3), Pos(6, 4)); - eqPos(cm.getCursor("head"), Pos(6, 4)); - eqPos(cm.getCursor("anchor"), Pos(5, 5)); + eqCursorPos(cm.getCursor("head"), Pos(6, 4)); + eqCursorPos(cm.getCursor("anchor"), Pos(5, 5)); cm.extendSelection(Pos(0, 3), Pos(0, 4)); - eqPos(cm.getCursor("head"), Pos(0, 3)); - eqPos(cm.getCursor("anchor"), Pos(5, 5)); + eqCursorPos(cm.getCursor("head"), Pos(0, 3)); + eqCursorPos(cm.getCursor("anchor"), Pos(5, 5)); cm.extendSelection(Pos(4, 5), Pos(6, 5)); - eqPos(cm.getCursor("head"), Pos(6, 5)); - eqPos(cm.getCursor("anchor"), Pos(4, 5)); + eqCursorPos(cm.getCursor("head"), Pos(6, 5)); + eqCursorPos(cm.getCursor("anchor"), Pos(4, 5)); cm.setExtending(false); cm.extendSelection(Pos(0, 3), Pos(0, 4)); - eqPos(cm.getCursor("head"), Pos(0, 3)); - eqPos(cm.getCursor("anchor"), Pos(0, 4)); + eqCursorPos(cm.getCursor("head"), Pos(0, 3)); + eqCursorPos(cm.getCursor("anchor"), Pos(0, 4)); }); testCM("lines", function(cm) { @@ -231,7 +232,7 @@ testCM("coordsChar", function(cm) { 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)); + eqCharPos(pos, Pos(line, ch)); } } } @@ -344,12 +345,12 @@ testCM("undoSelection", function(cm) { cm.replaceSelection(""); cm.setCursor(Pos(1, 0)); cm.undo(); - eqPos(cm.getCursor(true), Pos(0, 2)); - eqPos(cm.getCursor(false), Pos(0, 4)); + eqCursorPos(cm.getCursor(true), Pos(0, 2)); + eqCursorPos(cm.getCursor(false), Pos(0, 4)); cm.setCursor(Pos(1, 0)); cm.redo(); - eqPos(cm.getCursor(true), Pos(0, 2)); - eqPos(cm.getCursor(false), Pos(0, 2)); + eqCursorPos(cm.getCursor(true), Pos(0, 2)); + eqCursorPos(cm.getCursor(false), Pos(0, 2)); }, {value: "abcdefgh\n"}); testCM("undoSelectionAsBefore", function(cm) { @@ -413,7 +414,7 @@ testCM("markTextMultiLine", function(cm) { {className: "CodeMirror-matchingbracket"}); cm.replaceRange(test.c, p(test.a), p(test.b)); var f = r.find(); - eqPos(f && f.from, p(test.f)); eqPos(f && f.to, p(test.t)); + eqCursorPos(f && f.from, p(test.f)); eqCursorPos(f && f.to, p(test.t)); }); }); @@ -432,16 +433,16 @@ testCM("markTextUndo", function(cm) { cm.setValue(""); eq(marker1.find(), null); eq(marker2.find(), null); eq(bookmark.find(), null); cm.undo(); - eqPos(bookmark.find(), Pos(1, 5), "still there"); + eqCursorPos(bookmark.find(), Pos(1, 5), "still there"); cm.undo(); var m1Pos = marker1.find(), m2Pos = marker2.find(); - 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)); + eqCursorPos(m1Pos.from, Pos(0, 1)); eqCursorPos(m1Pos.to, Pos(0, 3)); + eqCursorPos(m2Pos.from, Pos(0, 0)); eqCursorPos(m2Pos.to, Pos(2, 1)); + eqCursorPos(bookmark.find(), Pos(1, 5)); cm.redo(); cm.redo(); eq(bookmark.find(), null); cm.undo(); - eqPos(bookmark.find(), Pos(1, 5)); + eqCursorPos(bookmark.find(), Pos(1, 5)); eq(cm.getValue(), v1); }, {value: "1234\n56789\n00\n"}); @@ -491,12 +492,12 @@ testCM("undoPreservesNewMarks", function(cm) { 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, 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)); + eqCursorPos(mBefore.find().from, Pos(0, 0)); + eqCursorPos(mBefore.find().to, Pos(0, 1)); + eqCursorPos(mAfter.find().from, Pos(3, 3)); + eqCursorPos(mAfter.find().to, Pos(3, 4)); + eqCursorPos(mAround.find().from, Pos(0, 2)); + eqCursorPos(mAround.find().to, Pos(3, 2)); var found = cm.findMarksAt(Pos(2, 2)); eq(found.length, 1); eq(found[0], mAround); @@ -547,7 +548,7 @@ testCM("bookmark", function(cm) { cm.setValue("1234567890\n1234567890\n1234567890"); 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)); + eqCursorPos(b.find(), p(test.d)); }); }); @@ -556,14 +557,14 @@ testCM("bookmarkInsertLeft", function(cm) { var bl = cm.setBookmark(Pos(0, 2), {insertLeft: true}); cm.setCursor(Pos(0, 2)); cm.replaceSelection("hi"); - eqPos(br.find(), Pos(0, 2)); - eqPos(bl.find(), Pos(0, 4)); + eqCursorPos(br.find(), Pos(0, 2)); + eqCursorPos(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(), Pos(0, 1)); - eqPos(bl.find(), Pos(0, 1)); + eqCursorPos(br.find(), Pos(0, 1)); + eqCursorPos(bl.find(), Pos(0, 1)); }, {value: "abcdef"}); testCM("bookmarkCursor", function(cm) { @@ -789,15 +790,15 @@ testCM("collapsedLines", function(cm) { CodeMirror.on(range, "clear", function() {cleared++;}); cm.setCursor(Pos(3, 0)); CodeMirror.commands.goLineDown(cm); - eqPos(cm.getCursor(), Pos(5, 0)); + eqCharPos(cm.getCursor(), Pos(5, 0)); cm.replaceRange("abcdefg", Pos(3, 0), Pos(3)); cm.setCursor(Pos(3, 6)); CodeMirror.commands.goLineDown(cm); - eqPos(cm.getCursor(), Pos(5, 4)); + eqCharPos(cm.getCursor(), Pos(5, 4)); cm.replaceRange("ab", Pos(3, 0), Pos(3)); cm.setCursor(Pos(3, 2)); CodeMirror.commands.goLineDown(cm); - eqPos(cm.getCursor(), Pos(5, 2)); + eqCharPos(cm.getCursor(), Pos(5, 2)); cm.operation(function() {range.clear(); range.clear();}); eq(cleared, 1); }); @@ -807,14 +808,14 @@ testCM("collapsedRangeCoordsChar", function(cm) { pos_1_3.left += 2; pos_1_3.top += 2; var opts = {collapsed: true, inclusiveLeft: true, inclusiveRight: true}; var m1 = cm.markText(Pos(0, 0), Pos(2, 0), opts); - eqPos(cm.coordsChar(pos_1_3), Pos(3, 3)); + eqCharPos(cm.coordsChar(pos_1_3), Pos(3, 3)); m1.clear(); var m1 = cm.markText(Pos(0, 0), Pos(1, 1), {collapsed: true, inclusiveLeft: true}); var m2 = cm.markText(Pos(1, 1), Pos(2, 0), {collapsed: true, inclusiveRight: true}); - eqPos(cm.coordsChar(pos_1_3), Pos(3, 3)); + eqCharPos(cm.coordsChar(pos_1_3), Pos(3, 3)); m1.clear(); m2.clear(); var m1 = cm.markText(Pos(0, 0), Pos(1, 6), opts); - eqPos(cm.coordsChar(pos_1_3), Pos(3, 3)); + eqCharPos(cm.coordsChar(pos_1_3), Pos(3, 3)); }, {value: "123456\nabcdef\nghijkl\nmnopqr\n"}); testCM("collapsedRangeBetweenLinesSelected", function(cm) { @@ -852,7 +853,7 @@ testCM("hiddenLinesAutoUnfold", function(cm) { eq(cleared, 1); range = foldLines(cm, 1, 3, true); CodeMirror.on(range, "clear", function() {cleared++;}); - eqPos(cm.getCursor(), Pos(3, 0)); + eqCursorPos(cm.getCursor(), Pos(3, 0)); cm.setCursor(Pos(0, 3)); cm.execCommand("goCharRight"); eq(cleared, 2); @@ -863,8 +864,8 @@ testCM("hiddenLinesSelectAll", function(cm) { // Issue #484 foldLines(cm, 0, 10); foldLines(cm, 11, 20); CodeMirror.commands.selectAll(cm); - eqPos(cm.getCursor(true), Pos(10, 0)); - eqPos(cm.getCursor(false), Pos(10, 4)); + eqCursorPos(cm.getCursor(true), Pos(10, 0)); + eqCursorPos(cm.getCursor(false), Pos(10, 4)); }); @@ -891,9 +892,9 @@ testCM("structuredFold", function(cm) { }); cm.setCursor(0, 3); CodeMirror.commands.goLineDown(cm); - eqPos(cm.getCursor(), Pos(6, 2)); + eqCharPos(cm.getCursor(), Pos(6, 2)); CodeMirror.commands.goCharLeft(cm); - eqPos(cm.getCursor(), Pos(1, 2)); + eqCharPos(cm.getCursor(), Pos(1, 2)); CodeMirror.commands.delCharAfter(cm); eq(cm.getValue(), "xxxx\nxxxx\nxxxx"); addDoc(cm, 4, 8); @@ -905,9 +906,9 @@ testCM("structuredFold", function(cm) { CodeMirror.on(range, "clear", function(){++cleared;}); cm.setCursor(0, 3); CodeMirror.commands.goLineDown(cm); - eqPos(cm.getCursor(), Pos(6, 2)); + eqCharPos(cm.getCursor(), Pos(6, 2)); CodeMirror.commands.goCharLeft(cm); - eqPos(cm.getCursor(), Pos(6, 1)); + eqCharPos(cm.getCursor(), Pos(6, 1)); eq(cleared, 1); range.clear(); eq(cleared, 1); @@ -918,13 +919,13 @@ testCM("structuredFold", function(cm) { range.clear(); cm.setCursor(1, 2); CodeMirror.commands.goCharRight(cm); - eqPos(cm.getCursor(), Pos(1, 3)); + eqCharPos(cm.getCursor(), Pos(1, 3)); range = cm.markText(Pos(2, 0), Pos(4, 4), { replacedWith: document.createTextNode("M") }); cm.setCursor(1, 0); CodeMirror.commands.goLineDown(cm); - eqPos(cm.getCursor(), Pos(2, 0)); + eqCharPos(cm.getCursor(), Pos(2, 0)); }, null); testCM("nestedFold", function(cm) { @@ -935,23 +936,23 @@ testCM("nestedFold", function(cm) { 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(), Pos(2, 3)); + eqCursorPos(cm.getCursor(), Pos(2, 3)); inner0.clear(); CodeMirror.commands.goCharLeft(cm); - eqPos(cm.getCursor(), Pos(0, 1)); + eqCursorPos(cm.getCursor(), Pos(0, 1)); outer.clear(); CodeMirror.commands.goCharRight(cm); - eqPos(cm.getCursor(), Pos(0, 2)); + eqCursorPos(cm.getCursor(), Pos(0, 2, "before")); CodeMirror.commands.goCharRight(cm); - eqPos(cm.getCursor(), Pos(1, 8)); + eqCursorPos(cm.getCursor(), Pos(1, 8)); inner2.clear(); CodeMirror.commands.goCharLeft(cm); - eqPos(cm.getCursor(), Pos(1, 7)); + eqCursorPos(cm.getCursor(), Pos(1, 7, "after")); cm.setCursor(0, 5); CodeMirror.commands.goCharRight(cm); - eqPos(cm.getCursor(), Pos(0, 6)); + eqCursorPos(cm.getCursor(), Pos(0, 6, "before")); CodeMirror.commands.goCharRight(cm); - eqPos(cm.getCursor(), Pos(1, 3)); + eqCursorPos(cm.getCursor(), Pos(1, 3)); }); testCM("badNestedFold", function(cm) { @@ -1045,13 +1046,13 @@ testCM("inlineWidget", function(cm) { var w = cm.setBookmark(Pos(0, 2), {widget: document.createTextNode("uu")}); cm.setCursor(0, 2); CodeMirror.commands.goLineDown(cm); - eqPos(cm.getCursor(), Pos(1, 4)); + eqCharPos(cm.getCursor(), Pos(1, 4)); cm.setCursor(0, 2); cm.replaceSelection("hi"); - eqPos(w.find(), Pos(0, 2)); + eqCharPos(w.find(), Pos(0, 2)); cm.setCursor(0, 1); cm.replaceSelection("ay"); - eqPos(w.find(), Pos(0, 4)); + eqCharPos(w.find(), Pos(0, 4)); eq(cm.getLine(0), "uayuhiuu"); }, {value: "uuuu\nuuuuuu"}); @@ -1083,7 +1084,7 @@ testCM("wrappingAndResizing", function(cm) { 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})); + eqCharPos(pos, cm.coordsChar({left: coords.left + 2, top: coords.top + 5})); }); }, null, ie_lt8); @@ -1102,13 +1103,13 @@ testCM("measureEndOfLine", function(cm) { cm.setValue(cm.getValue() + "\n\n"); var endPos = cm.charCoords(Pos(0, 18), "local"); is(endPos.top > lh * .8, "not at top"); - is(endPos.left > w - 20, "not at right"); + is(endPos.left > w - 20, "at right"); endPos = cm.charCoords(Pos(0, 18)); eqCursorPos(cm.coordsChar({left: endPos.left, top: endPos.top + 5}), Pos(0, 18, "before")); var wrapPos = cm.cursorCoords(Pos(0, 9, "before")); is(wrapPos.top < endPos.top, "wrapPos is actually in first line"); - eqPos(cm.coordsChar({left: wrapPos.left + 10, top: wrapPos.top}), Pos(0, 9, "before")); + eqCursorPos(cm.coordsChar({left: wrapPos.left + 10, top: wrapPos.top}), Pos(0, 9, "before")); }, {mode: "text/html", value: "", lineWrapping: true}, ie_lt8 || opera_lt10); testCM("measureWrappedEndOfLine", function(cm) { @@ -1125,9 +1126,9 @@ testCM("measureWrappedEndOfLine", function(cm) { } var endPos = cm.charCoords(Pos(0, 12)); // Next-to-last since last would wrap (#1862) endPos.left += w; // Add width of editor just to be sure that we are behind last character - eqPos(cm.coordsChar(endPos), Pos(0, 13, "before")); + eqCursorPos(cm.coordsChar(endPos), Pos(0, 13, "before")); endPos.left += w * 100; - eqPos(cm.coordsChar(endPos), Pos(0, 13, "before")); + eqCursorPos(cm.coordsChar(endPos), Pos(0, 13, "before")); }, {mode: "text/html", value: "0123456789abcde0123456789", lineWrapping: true}, ie_lt8 || opera_lt10); testCM("scrollVerticallyAndHorizontally", function(cm) { @@ -1153,7 +1154,7 @@ testCM("moveVstuck", function(cm) { } cm.setCursor(Pos(0, val.length - 1)); cm.moveV(-1, "line"); - eqPos(cm.getCursor(), Pos(0, 27, "before")); + eqCursorPos(cm.getCursor(), Pos(0, 27, "before")); is(cm.cursorCoords(null, "local").top < h0, "cursor is in first visual line"); }, {lineWrapping: true}, ie_lt8 || opera_lt10); @@ -1161,24 +1162,24 @@ testCM("collapseOnMove", function(cm) { cm.setSelection(Pos(0, 1), Pos(2, 4)); cm.execCommand("goLineUp"); is(!cm.somethingSelected()); - eqPos(cm.getCursor(), Pos(0, 1)); + eqCharPos(cm.getCursor(), Pos(0, 1)); cm.setSelection(Pos(0, 1), Pos(2, 4)); cm.execCommand("goPageDown"); is(!cm.somethingSelected()); - eqPos(cm.getCursor(), Pos(2, 4)); + eqCharPos(cm.getCursor(), Pos(2, 4)); cm.execCommand("goLineUp"); cm.execCommand("goLineUp"); - eqPos(cm.getCursor(), Pos(0, 4)); + eqCharPos(cm.getCursor(), Pos(0, 4)); cm.setSelection(Pos(0, 1), Pos(2, 4)); cm.execCommand("goCharLeft"); is(!cm.somethingSelected()); - eqPos(cm.getCursor(), Pos(0, 1)); + eqCharPos(cm.getCursor(), Pos(0, 1)); }, {value: "aaaaa\nb\nccccc"}); testCM("clickTab", function(cm) { 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)); + eqCharPos(cm.coordsChar({left: p0.left + 5, top: p0.top + 5}), Pos(0, 0)); + eqCharPos(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) { @@ -1230,53 +1231,53 @@ testCM("extraKeys", function(cm) { testCM("wordMovementCommands", function(cm) { cm.execCommand("goWordLeft"); - eqPos(cm.getCursor(), Pos(0, 0)); + eqCursorPos(cm.getCursor(), Pos(0, 0)); cm.execCommand("goWordRight"); cm.execCommand("goWordRight"); - eqPos(cm.getCursor(), Pos(0, 7)); + eqCursorPos(cm.getCursor(), Pos(0, 7, "before")); cm.execCommand("goWordLeft"); eqCursorPos(cm.getCursor(), Pos(0, 5, "after")); cm.execCommand("goWordRight"); cm.execCommand("goWordRight"); - eqPos(cm.getCursor(), Pos(0, 12)); + eqCursorPos(cm.getCursor(), Pos(0, 12, "before")); cm.execCommand("goWordLeft"); eqCursorPos(cm.getCursor(), Pos(0, 9, "after")); cm.execCommand("goWordRight"); cm.execCommand("goWordRight"); cm.execCommand("goWordRight"); - eqPos(cm.getCursor(), Pos(0, 24)); + eqCursorPos(cm.getCursor(), Pos(0, 24, "before")); cm.execCommand("goWordRight"); cm.execCommand("goWordRight"); - eqPos(cm.getCursor(), Pos(1, 9)); + eqCursorPos(cm.getCursor(), Pos(1, 9, "before")); cm.execCommand("goWordRight"); - eqPos(cm.getCursor(), Pos(1, 13)); + eqCursorPos(cm.getCursor(), Pos(1, 13, "before")); cm.execCommand("goWordRight"); cm.execCommand("goWordRight"); - eqPos(cm.getCursor(), Pos(2, 0)); + eqCharPos(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)); + eqCursorPos(cm.getCursor(), Pos(0, 0)); cm.execCommand("goGroupRight"); - eqPos(cm.getCursor(), Pos(0, 4)); + eqCursorPos(cm.getCursor(), Pos(0, 4, "before")); cm.execCommand("goGroupRight"); - eqPos(cm.getCursor(), Pos(0, 7)); + eqCursorPos(cm.getCursor(), Pos(0, 7, "before")); cm.execCommand("goGroupRight"); - eqPos(cm.getCursor(), Pos(0, 10)); + eqCursorPos(cm.getCursor(), Pos(0, 10, "before")); cm.execCommand("goGroupLeft"); eqCursorPos(cm.getCursor(), Pos(0, 7, "after")); cm.execCommand("goGroupRight"); cm.execCommand("goGroupRight"); cm.execCommand("goGroupRight"); - eqPos(cm.getCursor(), Pos(0, 15)); + eqCursorPos(cm.getCursor(), Pos(0, 15, "before")); cm.setCursor(Pos(0, 17)); cm.execCommand("goGroupLeft"); eqCursorPos(cm.getCursor(), Pos(0, 16, "after")); cm.execCommand("goGroupLeft"); eqCursorPos(cm.getCursor(), Pos(0, 14, "after")); cm.execCommand("goGroupRight"); cm.execCommand("goGroupRight"); - eqPos(cm.getCursor(), Pos(0, 20)); + eqCursorPos(cm.getCursor(), Pos(0, 20, "before")); cm.execCommand("goGroupRight"); - eqPos(cm.getCursor(), Pos(1, 0)); + eqCursorPos(cm.getCursor(), Pos(1, 0, "after")); cm.execCommand("goGroupRight"); - eqPos(cm.getCursor(), Pos(1, 2)); + eqCursorPos(cm.getCursor(), Pos(1, 2, "before")); cm.execCommand("goGroupRight"); - eqPos(cm.getCursor(), Pos(1, 5)); + eqCursorPos(cm.getCursor(), Pos(1, 5, "before")); cm.execCommand("goGroupLeft"); cm.execCommand("goGroupLeft"); - eqPos(cm.getCursor(), Pos(1, 0)); + eqCursorPos(cm.getCursor(), Pos(1, 0, "after")); cm.execCommand("goGroupLeft"); eqCursorPos(cm.getCursor(), Pos(0, 20, "after")); cm.execCommand("goGroupLeft"); @@ -1288,60 +1289,60 @@ testCM("groupsAndWhitespace", function(cm) { Pos(1, 0), Pos(1, 2), Pos(1, 5)]; for (var i = 1; i < positions.length; i++) { cm.execCommand("goGroupRight"); - eqPos(cm.getCursor(), positions[i]); + eqCharPos(cm.getCursor(), positions[i]); } for (var i = positions.length - 2; i >= 0; i--) { cm.execCommand("goGroupLeft"); - eqPos(cm.getCursor(), i == 2 ? Pos(0, 6) : positions[i]); + eqCharPos(cm.getCursor(), i == 2 ? Pos(0, 6, "before") : positions[i]); } }, {value: " foo +++ \n bar"}); testCM("charMovementCommands", function(cm) { cm.execCommand("goCharLeft"); cm.execCommand("goColumnLeft"); - eqPos(cm.getCursor(), Pos(0, 0)); + eqCursorPos(cm.getCursor(), Pos(0, 0)); cm.execCommand("goCharRight"); cm.execCommand("goCharRight"); - eqPos(cm.getCursor(), Pos(0, 2)); + eqCursorPos(cm.getCursor(), Pos(0, 2, "before")); cm.setCursor(Pos(1, 0)); cm.execCommand("goColumnLeft"); - eqPos(cm.getCursor(), Pos(1, 0)); + eqCursorPos(cm.getCursor(), Pos(1, 0)); cm.execCommand("goCharLeft"); - eqPos(cm.getCursor(), Pos(0, 5)); + eqCursorPos(cm.getCursor(), Pos(0, 5, "before")); cm.execCommand("goColumnRight"); - eqPos(cm.getCursor(), Pos(0, 5)); + eqCursorPos(cm.getCursor(), Pos(0, 5, "before")); cm.execCommand("goCharRight"); - eqPos(cm.getCursor(), Pos(1, 0)); + eqCursorPos(cm.getCursor(), Pos(1, 0, "after")); cm.execCommand("goLineEnd"); - eqPos(cm.getCursor(), Pos(1, 5)); + eqCursorPos(cm.getCursor(), Pos(1, 5, "before")); cm.execCommand("goLineStartSmart"); - eqPos(cm.getCursor(), Pos(1, 1)); + eqCursorPos(cm.getCursor(), Pos(1, 1, "after")); cm.execCommand("goLineStartSmart"); - eqPos(cm.getCursor(), Pos(1, 0)); + eqCursorPos(cm.getCursor(), Pos(1, 0, "after")); cm.setCursor(Pos(2, 0)); cm.execCommand("goCharRight"); cm.execCommand("goColumnRight"); - eqPos(cm.getCursor(), Pos(2, 0)); + eqCursorPos(cm.getCursor(), Pos(2, 0)); }, {value: "line1\n ine2\n"}); testCM("verticalMovementCommands", function(cm) { cm.execCommand("goLineUp"); - eqPos(cm.getCursor(), Pos(0, 0)); + eqCharPos(cm.getCursor(), Pos(0, 0)); cm.execCommand("goLineDown"); if (!phantom) // This fails in PhantomJS, though not in a real Webkit - eqPos(cm.getCursor(), Pos(1, 0)); + eqCharPos(cm.getCursor(), Pos(1, 0)); cm.setCursor(Pos(1, 12)); cm.execCommand("goLineDown"); - eqPos(cm.getCursor(), Pos(2, 5)); + eqCharPos(cm.getCursor(), Pos(2, 5)); cm.execCommand("goLineDown"); - eqPos(cm.getCursor(), Pos(3, 0)); + eqCharPos(cm.getCursor(), Pos(3, 0)); cm.execCommand("goLineUp"); - eqPos(cm.getCursor(), Pos(2, 5)); + eqCharPos(cm.getCursor(), Pos(2, 5)); cm.execCommand("goLineUp"); - eqPos(cm.getCursor(), Pos(1, 12)); + eqCharPos(cm.getCursor(), Pos(1, 12)); cm.execCommand("goPageDown"); - eqPos(cm.getCursor(), Pos(5, 0)); + eqCharPos(cm.getCursor(), Pos(5, 0)); cm.execCommand("goPageDown"); cm.execCommand("goLineDown"); - eqPos(cm.getCursor(), Pos(5, 0)); + eqCharPos(cm.getCursor(), Pos(5, 0)); cm.execCommand("goPageUp"); - eqPos(cm.getCursor(), Pos(0, 0)); + eqCharPos(cm.getCursor(), Pos(0, 0)); }, {value: "line1\nlong long line2\nline3\n\nline5\n"}); testCM("verticalMovementCommandsWrapping", function(cm) { @@ -1364,30 +1365,30 @@ testCM("verticalMovementCommandsSingleLine", function(cm) { cm.display.wrapper.style.height = "auto"; cm.refresh(); cm.execCommand("goLineUp"); - eqPos(cm.getCursor(), Pos(0, 0)); + eqCursorPos(cm.getCursor(), Pos(0, 0)); cm.execCommand("goLineDown"); - eqPos(cm.getCursor(), Pos(0, 11)); + eqCursorPos(cm.getCursor(), Pos(0, 11)); cm.setCursor(Pos(0, 5)); cm.execCommand("goLineDown"); - eqPos(cm.getCursor(), Pos(0, 11)); + eqCursorPos(cm.getCursor(), Pos(0, 11)); cm.execCommand("goLineDown"); - eqPos(cm.getCursor(), Pos(0, 11)); + eqCursorPos(cm.getCursor(), Pos(0, 11)); cm.execCommand("goLineUp"); - eqPos(cm.getCursor(), Pos(0, 0)); + eqCursorPos(cm.getCursor(), Pos(0, 0)); cm.execCommand("goLineUp"); - eqPos(cm.getCursor(), Pos(0, 0)); + eqCursorPos(cm.getCursor(), Pos(0, 0)); cm.execCommand("goPageDown"); - eqPos(cm.getCursor(), Pos(0, 11)); + eqCursorPos(cm.getCursor(), Pos(0, 11)); cm.execCommand("goPageDown"); cm.execCommand("goLineDown"); - eqPos(cm.getCursor(), Pos(0, 11)); + eqCursorPos(cm.getCursor(), Pos(0, 11)); cm.execCommand("goPageUp"); - eqPos(cm.getCursor(), Pos(0, 0)); + eqCursorPos(cm.getCursor(), Pos(0, 0)); cm.setCursor(Pos(0, 5)); cm.execCommand("goPageUp"); - eqPos(cm.getCursor(), Pos(0, 0)); + eqCursorPos(cm.getCursor(), Pos(0, 0)); cm.setCursor(Pos(0, 5)); cm.execCommand("goPageDown"); - eqPos(cm.getCursor(), Pos(0, 11)); + eqCursorPos(cm.getCursor(), Pos(0, 11)); }, {value: "single line"}); @@ -1396,8 +1397,7 @@ testCM("rtlMovement", function(cm) { forEach(["خحج", "خحabcخحج", "abخحخحجcd", "abخde", "abخح2342خ1حج", "خ1ح2خح3حxج", "خحcd", "1خحcd", "abcdeح1ج", "خمرحبها مها!", "foobarر", "خ ة ق", "", "يتم السحب في 05 فبراير 2014"], function(line) { - var inv = line.charCodeAt(0) > 128; - cm.setValue(line + "\n"); cm.execCommand(inv ? "goLineEnd" : "goLineStart"); + cm.setValue(line + "\n"); cm.execCommand("goLineStart"); var cursors = byClassName(cm.getWrapperElement(), "CodeMirror-cursors")[0]; var cursor = cursors.firstChild; var prevX = cursor.offsetLeft, prevY = cursor.offsetTop; @@ -1408,7 +1408,7 @@ testCM("rtlMovement", function(cm) { else is(cursor.offsetLeft > prevX, "moved right"); prevX = cursor.offsetLeft; prevY = cursor.offsetTop; } - cm.setCursor(0, 0); cm.execCommand(inv ? "goLineStart" : "goLineEnd"); + cm.setCursor(0, 0); cm.execCommand("goLineEnd"); prevX = cursors.firstChild.offsetLeft; for (var i = 0; i < line.length; ++i) { cm.execCommand("goCharLeft"); @@ -1421,24 +1421,24 @@ testCM("rtlMovement", function(cm) { // Verify that updating a line clears its bidi ordering testCM("bidiUpdate", function(cm) { - cm.setCursor(Pos(0, 2)); + cm.setCursor(Pos(0, 2, "before")); cm.replaceSelection("خحج", "start"); cm.execCommand("goCharRight"); - eqPos(cm.getCursor(), Pos(0, 4)); + eqCursorPos(cm.getCursor(), Pos(0, 6, "before")); }, {value: "abcd\n"}); testCM("movebyTextUnit", function(cm) { cm.setValue("בְּרֵאשִ\nééé́\n"); - cm.execCommand("goLineEnd"); + cm.execCommand("goLineStart"); for (var i = 0; i < 4; ++i) cm.execCommand("goCharRight"); - eqPos(cm.getCursor(), Pos(0, 0)); + eqCursorPos(cm.getCursor(), Pos(0, 0, "after")); cm.execCommand("goCharRight"); - eqPos(cm.getCursor(), Pos(1, 0)); + eqCursorPos(cm.getCursor(), Pos(1, 0, "after")); cm.execCommand("goCharRight"); cm.execCommand("goCharRight"); - eqPos(cm.getCursor(), Pos(1, 4)); + eqCursorPos(cm.getCursor(), Pos(1, 4, "before")); cm.execCommand("goCharRight"); - eqPos(cm.getCursor(), Pos(1, 7)); + eqCursorPos(cm.getCursor(), Pos(1, 7, "before")); }); testCM("lineChangeEvents", function(cm) { @@ -1478,9 +1478,9 @@ testCM("lineWidgets", function(cm) { is(last.top < cm.charCoords(Pos(2, 0)).top, "took up space"); cm.setCursor(Pos(1, 1)); cm.execCommand("goLineDown"); - eqPos(cm.getCursor(), Pos(2, 1)); + eqCharPos(cm.getCursor(), Pos(2, 1)); cm.execCommand("goLineUp"); - eqPos(cm.getCursor(), Pos(1, 1)); + eqCharPos(cm.getCursor(), Pos(1, 1)); }); testCM("lineWidgetFocus", function(cm) { @@ -1604,25 +1604,25 @@ testCM("jumpTheGap", function(cm) { cm.refresh(); cm.setCursor(Pos(0, 1)); cm.execCommand("goLineDown"); - eqPos(cm.getCursor(), Pos(1, 1)); + eqCharPos(cm.getCursor(), Pos(1, 1)); cm.execCommand("goLineDown"); - eqPos(cm.getCursor(), Pos(2, 1)); + eqCharPos(cm.getCursor(), Pos(2, 1)); cm.execCommand("goLineDown"); eq(cm.getCursor().line, 2); is(cm.getCursor().ch > 1); cm.execCommand("goLineUp"); - eqPos(cm.getCursor(), Pos(2, 1)); + eqCharPos(cm.getCursor(), Pos(2, 1)); cm.execCommand("goLineUp"); - eqPos(cm.getCursor(), Pos(1, 1)); + eqCharPos(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(Pos(0, 2)); cm.execCommand("goLineDown"); - eqPos(cm.getCursor(), Pos(1, 2)); + eqCharPos(cm.getCursor(), Pos(1, 2)); cm.execCommand("goLineUp"); - eqPos(cm.getCursor(), Pos(0, 2)); + eqCharPos(cm.getCursor(), Pos(0, 2)); }, {lineWrapping: true, value: "abc\ndef\nghi\njkl\n"}); testCM("addLineClass", function(cm) { @@ -1678,37 +1678,37 @@ testCM("atomicMarker", function(cm) { var m = atom(0, 1, 0, 5); cm.setCursor(Pos(0, 1)); cm.execCommand("goCharRight"); - eqPos(cm.getCursor(), Pos(0, 5)); + eqCursorPos(cm.getCursor(), Pos(0, 5)); cm.execCommand("goCharLeft"); - eqPos(cm.getCursor(), Pos(0, 1)); + eqCursorPos(cm.getCursor(), Pos(0, 1)); m.clear(); m = atom(0, 0, 0, 5, true); - eqPos(cm.getCursor(), Pos(0, 5), "pushed out"); + eqCursorPos(cm.getCursor(), Pos(0, 5), "pushed out"); cm.execCommand("goCharLeft"); - eqPos(cm.getCursor(), Pos(0, 5)); + eqCursorPos(cm.getCursor(), Pos(0, 5)); m.clear(); m = atom(8, 4, 9, 10, false, true); cm.setCursor(Pos(9, 8)); - eqPos(cm.getCursor(), Pos(8, 4), "set"); + eqCursorPos(cm.getCursor(), Pos(8, 4), "set"); cm.execCommand("goCharRight"); - eqPos(cm.getCursor(), Pos(8, 4), "char right"); + eqCursorPos(cm.getCursor(), Pos(8, 4), "char right"); cm.execCommand("goLineDown"); - eqPos(cm.getCursor(), Pos(8, 4), "line down"); + eqCursorPos(cm.getCursor(), Pos(8, 4), "line down"); cm.execCommand("goCharLeft"); - eqPos(cm.getCursor(), Pos(8, 3)); + eqCursorPos(cm.getCursor(), Pos(8, 3, "after")); m.clear(); m = atom(1, 1, 3, 8); cm.setCursor(Pos(0, 0)); cm.setCursor(Pos(2, 0)); - eqPos(cm.getCursor(), Pos(3, 8)); + eqCursorPos(cm.getCursor(), Pos(3, 8)); cm.execCommand("goCharLeft"); - eqPos(cm.getCursor(), Pos(1, 1)); + eqCursorPos(cm.getCursor(), Pos(1, 1)); cm.execCommand("goCharRight"); - eqPos(cm.getCursor(), Pos(3, 8)); + eqCursorPos(cm.getCursor(), Pos(3, 8)); cm.execCommand("goLineUp"); - eqPos(cm.getCursor(), Pos(1, 1)); + eqCursorPos(cm.getCursor(), Pos(1, 1)); cm.execCommand("goLineDown"); - eqPos(cm.getCursor(), Pos(3, 8)); + eqCursorPos(cm.getCursor(), Pos(3, 8)); cm.execCommand("delCharBefore"); eq(cm.getValue().length, 80, "del chunk"); m = atom(3, 0, 5, 5); @@ -1720,16 +1720,16 @@ testCM("atomicMarker", function(cm) { testCM("selectionBias", function(cm) { cm.markText(Pos(0, 1), Pos(0, 3), {atomic: true}); cm.setCursor(Pos(0, 2)); - eqPos(cm.getCursor(), Pos(0, 1)); + eqCursorPos(cm.getCursor(), Pos(0, 1)); cm.setCursor(Pos(0, 2)); - eqPos(cm.getCursor(), Pos(0, 3)); + eqCursorPos(cm.getCursor(), Pos(0, 3)); cm.setCursor(Pos(0, 2)); - eqPos(cm.getCursor(), Pos(0, 1)); + eqCursorPos(cm.getCursor(), Pos(0, 1)); cm.setCursor(Pos(0, 2), null, {bias: -1}); - eqPos(cm.getCursor(), Pos(0, 1)); + eqCursorPos(cm.getCursor(), Pos(0, 1)); cm.setCursor(Pos(0, 4)); cm.setCursor(Pos(0, 2), null, {bias: 1}); - eqPos(cm.getCursor(), Pos(0, 3)); + eqCursorPos(cm.getCursor(), Pos(0, 3)); }, {value: "12345"}); testCM("selectionHomeEnd", function(cm) { @@ -1737,9 +1737,9 @@ testCM("selectionHomeEnd", function(cm) { cm.markText(Pos(1, 3), Pos(1, 4), {atomic: true, inclusiveRight: true}); cm.setCursor(Pos(1, 2)); cm.execCommand("goLineStart"); - eqPos(cm.getCursor(), Pos(1, 1)); + eqCursorPos(cm.getCursor(), Pos(1, 1)); cm.execCommand("goLineEnd"); - eqPos(cm.getCursor(), Pos(1, 3)); + eqCursorPos(cm.getCursor(), Pos(1, 3)); }, {value: "ab\ncdef\ngh"}); testCM("readOnlyMarker", function(cm) { @@ -1750,30 +1750,30 @@ testCM("readOnlyMarker", function(cm) { var m = mark(0, 1, 0, 4); cm.setCursor(Pos(0, 2)); cm.replaceSelection("hi", "end"); - eqPos(cm.getCursor(), Pos(0, 2)); + eqCursorPos(cm.getCursor(), Pos(0, 2)); eq(cm.getLine(0), "abcde"); cm.execCommand("selectAll"); cm.replaceSelection("oops", "around"); eq(cm.getValue(), "oopsbcd"); cm.undo(); - eqPos(m.find().from, Pos(0, 1)); - eqPos(m.find().to, Pos(0, 4)); + eqCursorPos(m.find().from, Pos(0, 1)); + eqCursorPos(m.find().to, Pos(0, 4)); m.clear(); cm.setCursor(Pos(0, 2)); cm.replaceSelection("hi", "around"); eq(cm.getLine(0), "abhicde"); - eqPos(cm.getCursor(), Pos(0, 4)); + eqCursorPos(cm.getCursor(), Pos(0, 4)); m = mark(0, 2, 2, 2, true); cm.setSelection(Pos(1, 1), Pos(2, 4)); cm.replaceSelection("t", "end"); - eqPos(cm.getCursor(), Pos(2, 3)); + eqCursorPos(cm.getCursor(), Pos(2, 3)); eq(cm.getLine(2), "klto"); cm.execCommand("goCharLeft"); cm.execCommand("goCharLeft"); - eqPos(cm.getCursor(), Pos(0, 2)); + eqCursorPos(cm.getCursor(), Pos(0, 2)); cm.setSelection(Pos(0, 1), Pos(0, 3)); cm.replaceSelection("xx", "around"); - eqPos(cm.getCursor(), Pos(0, 3)); + eqCursorPos(cm.getCursor(), Pos(0, 3)); eq(cm.getLine(0), "axxhicde"); }, {value: "abcde\nfghij\nklmno\n"}); @@ -1817,12 +1817,12 @@ testCM("addKeyMap", function(cm) { } sendKey(39); - eqPos(cm.getCursor(), Pos(0, 1)); + eqCursorPos(cm.getCursor(), Pos(0, 1, "before")); var test = 0; var map1 = {Right: function() { ++test; }}, map2 = {Right: function() { test += 10; }} cm.addKeyMap(map1); sendKey(39); - eqPos(cm.getCursor(), Pos(0, 1)); + eqCursorPos(cm.getCursor(), Pos(0, 1, "before")); eq(test, 1); cm.addKeyMap(map2, true); sendKey(39); @@ -1833,13 +1833,13 @@ testCM("addKeyMap", function(cm) { cm.removeKeyMap(map2); sendKey(39); eq(test, 12); - eqPos(cm.getCursor(), Pos(0, 2)); + eqCursorPos(cm.getCursor(), Pos(0, 2, "before")); cm.addKeyMap({Right: function() { test = 55; }, name: "mymap"}); sendKey(39); eq(test, 55); cm.removeKeyMap("mymap"); sendKey(39); - eqPos(cm.getCursor(), Pos(0, 3)); + eqCursorPos(cm.getCursor(), Pos(0, 3, "before")); }, {value: "abc"}); testCM("findPosH", function(cm) { @@ -1847,7 +1847,7 @@ testCM("findPosH", function(cm) { {from: Pos(0, 0), to: Pos(0, 0), by: -1, hitSide: true}, {from: Pos(0, 0), to: Pos(0, 4, "before"), by: 1, unit: "word"}, {from: Pos(0, 0), to: Pos(0, 8, "before"), by: 2, unit: "word"}, - {from: Pos(0, 0), to: Pos(2, 0, "before"), by: 20, unit: "word", hitSide: true}, + {from: Pos(0, 0), to: Pos(2, 0, "after"), by: 20, unit: "word", hitSide: true}, {from: Pos(0, 7), to: Pos(0, 5, "after"), by: -1, unit: "word"}, {from: Pos(0, 4), to: Pos(0, 8, "before"), by: 1, unit: "word"}, {from: Pos(1, 0), to: Pos(1, 18, "before"), by: 3, unit: "word"}, @@ -1856,9 +1856,9 @@ testCM("findPosH", function(cm) { {from: Pos(1, 15), to: Pos(1, 10, "after"), by: -5, unit: "column"}, {from: Pos(1, 15), to: Pos(1, 0, "after"), by: -50, unit: "column", hitSide: true}, {from: Pos(1, 15), to: Pos(1, 24, "before"), by: 50, unit: "column", hitSide: true}, - {from: Pos(1, 15), to: Pos(2, 0, "before"), by: 50, hitSide: true}], function(t) { + {from: Pos(1, 15), to: Pos(2, 0, "after"), by: 50, hitSide: true}], function(t) { var r = cm.findPosH(t.from, t.by, t.unit || "char"); - eqPos(r, t.to); + eqCursorPos(r, t.to); eq(!!r.hitSide, !!t.hitSide); }); }, {value: "line one\nline two.something.other\n"}); @@ -1907,10 +1907,10 @@ testCM("beforeSelectionChange", function(cm) { addDoc(cm, 10, 10); cm.execCommand("goLineEnd"); - eqPos(cm.getCursor(), Pos(0, 9)); + eqCursorPos(cm.getCursor(), Pos(0, 9)); cm.execCommand("selectAll"); - eqPos(cm.getCursor("start"), Pos(0, 0)); - eqPos(cm.getCursor("end"), Pos(9, 9)); + eqCursorPos(cm.getCursor("start"), Pos(0, 0)); + eqCursorPos(cm.getCursor("end"), Pos(9, 9)); }); testCM("change_removedText", function(cm) { @@ -2015,17 +2015,17 @@ testCM("selectionHistory", function(cm) { eq(cm.getSelection(), "c"); cm.execCommand("undoSelection"); eq(cm.getSelection(), ""); - eqPos(cm.getCursor(), Pos(0, 4)); + eqCursorPos(cm.getCursor(), Pos(0, 4, "before")); cm.execCommand("undoSelection"); eq(cm.getSelection(), "b"); cm.execCommand("redoSelection"); eq(cm.getSelection(), ""); - eqPos(cm.getCursor(), Pos(0, 4)); + eqCursorPos(cm.getCursor(), Pos(0, 4, "before")); cm.execCommand("redoSelection"); eq(cm.getSelection(), "c"); cm.execCommand("redoSelection"); eq(cm.getSelection(), ""); - eqPos(cm.getCursor(), Pos(0, 6)); + eqCursorPos(cm.getCursor(), Pos(0, 6, "before")); }, {value: "a b c d"}); testCM("selectionChangeReducesRedo", function(cm) { @@ -2035,7 +2035,7 @@ testCM("selectionChangeReducesRedo", function(cm) { cm.execCommand("selectAll"); cm.undoSelection(); eq(cm.getValue(), "Xabc"); - eqPos(cm.getCursor(), Pos(0, 1)); + eqCursorPos(cm.getCursor(), Pos(0, 1)); cm.undoSelection(); eq(cm.getValue(), "abc"); }, {value: "abc"}); @@ -2044,8 +2044,8 @@ testCM("selectionHistoryNonOverlapping", function(cm) { cm.setSelection(Pos(0, 0), Pos(0, 1)); cm.setSelection(Pos(0, 2), Pos(0, 3)); cm.execCommand("undoSelection"); - eqPos(cm.getCursor("anchor"), Pos(0, 0)); - eqPos(cm.getCursor("head"), Pos(0, 1)); + eqCursorPos(cm.getCursor("anchor"), Pos(0, 0)); + eqCursorPos(cm.getCursor("head"), Pos(0, 1)); }, {value: "1234"}); testCM("cursorMotionSplitsHistory", function(cm) { @@ -2055,10 +2055,10 @@ testCM("cursorMotionSplitsHistory", function(cm) { cm.replaceSelection("c"); cm.undo(); eq(cm.getValue(), "a1234"); - eqPos(cm.getCursor(), Pos(0, 2)); + eqCursorPos(cm.getCursor(), Pos(0, 2, "before")); cm.undo(); eq(cm.getValue(), "1234"); - eqPos(cm.getCursor(), Pos(0, 0)); + eqCursorPos(cm.getCursor(), Pos(0, 0)); }, {value: "1234"}); testCM("selChangeInOperationDoesNotSplit", function(cm) { @@ -2068,7 +2068,7 @@ testCM("selChangeInOperationDoesNotSplit", function(cm) { cm.setCursor(Pos(0, cm.getCursor().ch - 1)); }); } - eqPos(cm.getCursor(), Pos(0, 0)); + eqCursorPos(cm.getCursor(), Pos(0, 0)); eq(cm.getValue(), "xxxxa"); cm.undo(); eq(cm.getValue(), "a"); @@ -2220,7 +2220,7 @@ function makeItWrapAfter(cm, pos) { function testMoveBidi(str) { testCM("move_bidi_" + str, function(cm) { -console.log("TESTING", encodeURIComponent(str)) + if (cm.getOption("inputStyle") != "textarea" || webkit || !cm.getOption("rtlMoveVisually")) return; cm.getScrollerElement().style.fontFamily = "monospace"; makeItWrapAfter(cm, Pos(0, 5)); @@ -2230,7 +2230,6 @@ console.log("TESTING", encodeURIComponent(str)) if (str.indexOf("\n") != -1) { lineBreaks[steps - 2] = 'n'; } -console.log("TESTING", str.length, steps, lineBreaks) // Make sure we are at the visual beginning of the first line var pos = Pos(0, 0), lastPos; @@ -2246,18 +2245,15 @@ console.log("TESTING", str.length, steps, lineBreaks) cm.execCommand("goCharRight"); coords = cm.cursorCoords(); if ((i >= 10 && i <= 12) && !lineBreaks[i] && coords.left < prevCoords.left && coords.top > prevCoords.top) { -console.log("MOVED WRAPPED AT>", i) // The first line wraps twice lineBreaks[i] = 'w'; } if (!lineBreaks[i]) { is(coords.left > prevCoords.left, "In step " + i + ", cursor didn't move right"); eq(coords.top, prevCoords.top, "In step " + i + ", cursor moved out of line"); -console.log("MOVED", i, "right") } else { is(coords.left < prevCoords.left, i); is(coords.top > prevCoords.top, i); -console.log("MOVED", i, "down") } prevCoords = coords; } @@ -2273,11 +2269,9 @@ console.log("MOVED", i, "down") if (!(lineBreaks[i] == 'n' || lineBreaks[i + 1] == 'w')) { is(coords.left < prevCoords.left, "In step " + i + ", cursor didn't move left"); eq(coords.top, prevCoords.top, "In step " + i + ", cursor is not at the same line anymore"); -console.log("MOVED", i, "left") } else { is(coords.left > prevCoords.left, i); is(coords.top < prevCoords.top, i); -console.log("MOVED", i, "up") } prevCoords = coords; } @@ -2289,7 +2283,7 @@ console.log("MOVED", i, "up") }, {value: str, lineWrapping: true}) }; -testMoveBidi("Say ا ب جabj\nS"); // https://bugs.webkit.org/show_bug.cgi?id=165753 +testMoveBidi("Say ا ب جabj\nS"); testMoveBidi("Όȝǝڪȉۥ״ۺ׆ɀҩۏ\nҳ"); testMoveBidi("ό׊۷٢ԜһОצЉيčǟ\nѩ"); testMoveBidi("ۑÚҳҕڬġڹհяųKV\nr"); diff --git a/test/vim_test.js b/test/vim_test.js index d6b0ad16..acb5ba4b 100644 --- a/test/vim_test.js +++ b/test/vim_test.js @@ -174,7 +174,7 @@ function testVim(name, run, opts, expectedFail) { } else { pos = makeCursor(line, ch); } - eqPos(pos, cm.getCursor()); + eqCursorPos(cm.getCursor(), pos); } } function fakeOpenDialog(result) { @@ -531,14 +531,14 @@ testVim('paragraph_motions', function(cm, vim, helpers) { // ip inside empty space cm.setCursor(10, 0); helpers.doKeys('v', 'i', 'p'); - eqPos(Pos(7, 0), cm.getCursor('anchor')); - eqPos(Pos(12, 0), cm.getCursor('head')); + eqCursorPos(Pos(7, 0), cm.getCursor('anchor')); + eqCursorPos(Pos(12, 0), cm.getCursor('head')); helpers.doKeys('i', 'p'); - eqPos(Pos(7, 0), cm.getCursor('anchor')); - eqPos(Pos(13, 1), cm.getCursor('head')); + eqCursorPos(Pos(7, 0), cm.getCursor('anchor')); + eqCursorPos(Pos(13, 1), cm.getCursor('head')); helpers.doKeys('2', 'i', 'p'); - eqPos(Pos(7, 0), cm.getCursor('anchor')); - eqPos(Pos(16, 1), cm.getCursor('head')); + eqCursorPos(Pos(7, 0), cm.getCursor('anchor')); + eqCursorPos(Pos(16, 1), cm.getCursor('head')); // should switch to visualLine mode cm.setCursor(14, 0); @@ -547,31 +547,31 @@ testVim('paragraph_motions', function(cm, vim, helpers) { cm.setCursor(14, 0); helpers.doKeys('', 'V', 'i', 'p'); - eqPos(Pos(16, 1), cm.getCursor('head')); + eqCursorPos(Pos(16, 1), cm.getCursor('head')); // ap inside empty space cm.setCursor(10, 0); helpers.doKeys('', 'v', 'a', 'p'); - eqPos(Pos(7, 0), cm.getCursor('anchor')); - eqPos(Pos(13, 1), cm.getCursor('head')); + eqCursorPos(Pos(7, 0), cm.getCursor('anchor')); + eqCursorPos(Pos(13, 1), cm.getCursor('head')); helpers.doKeys('a', 'p'); - eqPos(Pos(7, 0), cm.getCursor('anchor')); - eqPos(Pos(16, 1), cm.getCursor('head')); + eqCursorPos(Pos(7, 0), cm.getCursor('anchor')); + eqCursorPos(Pos(16, 1), cm.getCursor('head')); cm.setCursor(13, 0); helpers.doKeys('v', 'a', 'p'); - eqPos(Pos(13, 0), cm.getCursor('anchor')); - eqPos(Pos(14, 0), cm.getCursor('head')); + eqCursorPos(Pos(13, 0), cm.getCursor('anchor')); + eqCursorPos(Pos(14, 0), cm.getCursor('head')); cm.setCursor(16, 0); helpers.doKeys('v', 'a', 'p'); - eqPos(Pos(14, 0), cm.getCursor('anchor')); - eqPos(Pos(16, 1), cm.getCursor('head')); + eqCursorPos(Pos(14, 0), cm.getCursor('anchor')); + eqCursorPos(Pos(16, 1), cm.getCursor('head')); cm.setCursor(0, 0); helpers.doKeys('v', 'a', 'p'); - eqPos(Pos(0, 0), cm.getCursor('anchor')); - eqPos(Pos(4, 0), cm.getCursor('head')); + eqCursorPos(Pos(0, 0), cm.getCursor('anchor')); + eqCursorPos(Pos(4, 0), cm.getCursor('head')); cm.setCursor(0, 0); helpers.doKeys('d', 'i', 'p'); @@ -593,7 +593,7 @@ testVim('dl', function(cm, vim, helpers) { var register = helpers.getRegisterController().getRegister(); eq(' ', register.toString()); is(!register.linewise); - eqPos(curStart, cm.getCursor()); + eqCursorPos(curStart, cm.getCursor()); }, { value: ' word1 ' }); testVim('dl_eol', function(cm, vim, helpers) { cm.setCursor(0, 6); @@ -612,7 +612,7 @@ testVim('dl_repeat', function(cm, vim, helpers) { var register = helpers.getRegisterController().getRegister(); eq(' w', register.toString()); is(!register.linewise); - eqPos(curStart, cm.getCursor()); + eqCursorPos(curStart, cm.getCursor()); }, { value: ' word1 ' }); testVim('dh', function(cm, vim, helpers) { var curStart = makeCursor(0, 3); @@ -622,7 +622,7 @@ testVim('dh', function(cm, vim, helpers) { var register = helpers.getRegisterController().getRegister(); eq('o', register.toString()); is(!register.linewise); - eqPos(offsetCursor(curStart, 0 , -1), cm.getCursor()); + eqCursorPos(offsetCursor(curStart, 0 , -1), cm.getCursor()); }, { value: ' word1 ' }); testVim('dj', function(cm, vim, helpers) { var curStart = makeCursor(0, 3); @@ -672,7 +672,7 @@ testVim('dw_space', function(cm, vim, helpers) { var register = helpers.getRegisterController().getRegister(); eq(' ', register.toString()); is(!register.linewise); - eqPos(curStart, cm.getCursor()); + eqCursorPos(curStart, cm.getCursor()); }, { value: ' word1 ' }); testVim('dw_word', function(cm, vim, helpers) { var curStart = makeCursor(0, 1); @@ -682,7 +682,7 @@ testVim('dw_word', function(cm, vim, helpers) { var register = helpers.getRegisterController().getRegister(); eq('word1 ', register.toString()); is(!register.linewise); - eqPos(curStart, cm.getCursor()); + eqCursorPos(curStart, cm.getCursor()); }, { value: ' word1 word2' }); testVim('dw_unicode_word', function(cm, vim, helpers) { helpers.doKeys('d', 'w'); @@ -852,7 +852,7 @@ testVim('d_inclusive', function(cm, vim, helpers) { var register = helpers.getRegisterController().getRegister(); eq('word1', register.toString()); is(!register.linewise); - eqPos(curStart, cm.getCursor()); + eqCursorPos(curStart, cm.getCursor()); }, { value: ' word1 ' }); testVim('d_reverse', function(cm, vim, helpers) { // Test that deleting in reverse works. @@ -940,7 +940,7 @@ testVim('yw_repeat', function(cm, vim, helpers) { var register = helpers.getRegisterController().getRegister(); eq('word1\nword2', register.toString()); is(!register.linewise); - eqPos(curStart, cm.getCursor()); + eqCursorPos(curStart, cm.getCursor()); }, { value: ' word1\nword2' }); testVim('yy_multiply_repeat', function(cm, vim, helpers) { var curStart = makeCursor(0, 3); @@ -953,7 +953,7 @@ testVim('yy_multiply_repeat', function(cm, vim, helpers) { var register = helpers.getRegisterController().getRegister(); eq(expectedBuffer, register.toString()); is(register.linewise); - eqPos(curStart, cm.getCursor()); + eqCursorPos(curStart, cm.getCursor()); }); // Change commands behave like d commands except that it also enters insert // mode. In addition, when the change is linewise, an additional newline is @@ -974,7 +974,7 @@ testVim('cw_repeat', function(cm, vim, helpers) { var register = helpers.getRegisterController().getRegister(); eq('word1\nword2', register.toString()); is(!register.linewise); - eqPos(curStart, cm.getCursor()); + eqCursorPos(curStart, cm.getCursor()); eq('vim-insert', cm.getOption('keyMap')); }, { value: ' word1\nword2' }); testVim('cc_multiply_repeat', function(cm, vim, helpers) { @@ -1074,7 +1074,7 @@ testVim('g~w_repeat', function(cm, vim, helpers) { var register = helpers.getRegisterController().getRegister(); eq('', register.toString()); is(!register.linewise); - eqPos(curStart, cm.getCursor()); + eqCursorPos(curStart, cm.getCursor()); }, { value: ' word1\nword2' }); testVim('g~g~', function(cm, vim, helpers) { var curStart = makeCursor(0, 3); @@ -1086,7 +1086,7 @@ testVim('g~g~', function(cm, vim, helpers) { var register = helpers.getRegisterController().getRegister(); eq('', register.toString()); is(!register.linewise); - eqPos(curStart, cm.getCursor()); + eqCursorPos(curStart, cm.getCursor()); }, { value: ' word1\nword2\nword3\nword4\nword5\nword6' }); testVim('gu_and_gU', function(cm, vim, helpers) { var curStart = makeCursor(0, 7); @@ -1094,21 +1094,21 @@ testVim('gu_and_gU', function(cm, vim, helpers) { cm.setCursor(curStart); helpers.doKeys('2', 'g', 'U', 'w'); eq(cm.getValue(), 'wa wb xX WC wd'); - eqPos(curStart, cm.getCursor()); + eqCursorPos(curStart, cm.getCursor()); helpers.doKeys('2', 'g', 'u', 'w'); eq(cm.getValue(), value); helpers.doKeys('2', 'g', 'U', 'B'); eq(cm.getValue(), 'wa WB Xx wc wd'); - eqPos(makeCursor(0, 3), cm.getCursor()); + eqCursorPos(makeCursor(0, 3), cm.getCursor()); cm.setCursor(makeCursor(0, 4)); helpers.doKeys('g', 'u', 'i', 'w'); eq(cm.getValue(), 'wa wb Xx wc wd'); - eqPos(makeCursor(0, 3), cm.getCursor()); + eqCursorPos(makeCursor(0, 3), cm.getCursor()); // TODO: support gUgU guu - // eqPos(makeCursor(0, 0), cm.getCursor()); + // eqCursorPos(makeCursor(0, 0), cm.getCursor()); var register = helpers.getRegisterController().getRegister(); eq('', register.toString()); @@ -1333,7 +1333,7 @@ testVim('C', function(cm, vim, helpers) { var register = helpers.getRegisterController().getRegister(); eq('rd1', register.toString()); is(!register.linewise); - eqPos(curStart, cm.getCursor()); + eqCursorPos(curStart, cm.getCursor()); eq('vim-insert', cm.getOption('keyMap')); }, { value: ' word1\nword2\n word3' }); testVim('Y', function(cm, vim, helpers) { @@ -1446,7 +1446,7 @@ testVim('i_overwrite_backspace', function(cm, vim, helpers) { helpers.doKeys('i'); helpers.doKeys(''); helpers.doInsertModeKeys('Backspace'); - helpers.assertCursorAt(0, 9); + helpers.assertCursorAt(Pos(0, 9, "after")); eq('0123456789', cm.getValue()); }, { value: '0123456789'}); testVim('A', function(cm, vim, helpers) { @@ -1879,7 +1879,7 @@ testVim('delmark_all', function(cm, vim, helpers) { testVim('visual', function(cm, vim, helpers) { helpers.doKeys('l', 'v', 'l', 'l'); helpers.assertCursorAt(0, 4); - eqPos(makeCursor(0, 1), cm.getCursor('anchor')); + eqCursorPos(makeCursor(0, 1), cm.getCursor('anchor')); helpers.doKeys('d'); eq('15', cm.getValue()); }, { value: '12345' }); @@ -1911,24 +1911,24 @@ testVim('visual_crossover_left', function(cm, vim, helpers) { testVim('visual_crossover_up', function(cm, vim, helpers) { cm.setCursor(3, 2); helpers.doKeys('v', 'j', 'k', 'k'); - eqPos(Pos(2, 2), cm.getCursor('head')); - eqPos(Pos(3, 3), cm.getCursor('anchor')); + eqCursorPos(Pos(2, 2), cm.getCursor('head')); + eqCursorPos(Pos(3, 3), cm.getCursor('anchor')); helpers.doKeys('k'); - eqPos(Pos(1, 2), cm.getCursor('head')); - eqPos(Pos(3, 3), cm.getCursor('anchor')); + eqCursorPos(Pos(1, 2), cm.getCursor('head')); + eqCursorPos(Pos(3, 3), cm.getCursor('anchor')); }, { value: 'cross\ncross\ncross\ncross\ncross\n'}); testVim('visual_crossover_down', function(cm, vim, helpers) { cm.setCursor(1, 2); helpers.doKeys('v', 'k', 'j', 'j'); - eqPos(Pos(2, 3), cm.getCursor('head')); - eqPos(Pos(1, 2), cm.getCursor('anchor')); + eqCursorPos(Pos(2, 3), cm.getCursor('head')); + eqCursorPos(Pos(1, 2), cm.getCursor('anchor')); helpers.doKeys('j'); - eqPos(Pos(3, 3), cm.getCursor('head')); - eqPos(Pos(1, 2), cm.getCursor('anchor')); + eqCursorPos(Pos(3, 3), cm.getCursor('head')); + eqCursorPos(Pos(1, 2), cm.getCursor('anchor')); }, { value: 'cross\ncross\ncross\ncross\ncross\n'}); testVim('visual_exit', function(cm, vim, helpers) { helpers.doKeys('', 'l', 'j', 'j', ''); - eqPos(cm.getCursor('anchor'), cm.getCursor('head')); + eqCursorPos(cm.getCursor('anchor'), cm.getCursor('head')); eq(vim.visualMode, false); }, { value: 'hello\nworld\nfoo' }); testVim('visual_line', function(cm, vim, helpers) { @@ -2011,7 +2011,7 @@ testVim('visual_block_crossing_short_line', function(cm, vim, helpers) { testVim('visual_block_curPos_on_exit', function(cm, vim, helpers) { cm.setCursor(0, 0); helpers.doKeys('', '3' , 'l', ''); - eqPos(makeCursor(0, 3), cm.getCursor()); + eqCursorPos(makeCursor(0, 3), cm.getCursor()); helpers.doKeys('h', '', '2' , 'j' ,'3' , 'l'); eq(cm.getSelections().join(), "3456,,cdef"); helpers.doKeys('4' , 'h'); @@ -2046,7 +2046,7 @@ testVim('visual_blank', function(cm, vim, helpers) { testVim('reselect_visual', function(cm, vim, helpers) { helpers.doKeys('l', 'v', 'l', 'l', 'l', 'y', 'g', 'v'); helpers.assertCursorAt(0, 5); - eqPos(makeCursor(0, 1), cm.getCursor('anchor')); + eqCursorPos(makeCursor(0, 1), cm.getCursor('anchor')); helpers.doKeys('v'); cm.setCursor(1, 0); helpers.doKeys('v', 'l', 'l', 'p'); @@ -2055,15 +2055,15 @@ testVim('reselect_visual', function(cm, vim, helpers) { helpers.doKeys('g', 'v'); // here the fake cursor is at (1, 3) helpers.assertCursorAt(1, 4); - eqPos(makeCursor(1, 0), cm.getCursor('anchor')); + eqCursorPos(makeCursor(1, 0), cm.getCursor('anchor')); helpers.doKeys('v'); cm.setCursor(2, 0); helpers.doKeys('v', 'l', 'l', 'g', 'v'); helpers.assertCursorAt(1, 4); - eqPos(makeCursor(1, 0), cm.getCursor('anchor')); + eqCursorPos(makeCursor(1, 0), cm.getCursor('anchor')); helpers.doKeys('g', 'v'); helpers.assertCursorAt(2, 3); - eqPos(makeCursor(2, 0), cm.getCursor('anchor')); + eqCursorPos(makeCursor(2, 0), cm.getCursor('anchor')); eq('123456\n2345\nbar', cm.getValue()); }, { value: '123456\nfoo\nbar' }); testVim('reselect_visual_line', function(cm, vim, helpers) { @@ -2079,14 +2079,14 @@ testVim('reselect_visual_block', function(cm, vim, helpers) { helpers.doKeys('', 'k', 'h', ''); cm.setCursor(2, 1); helpers.doKeys('v', 'l', 'g', 'v'); - eqPos(Pos(1, 2), vim.sel.anchor); - eqPos(Pos(0, 1), vim.sel.head); + eqCursorPos(Pos(1, 2), vim.sel.anchor); + eqCursorPos(Pos(0, 1), vim.sel.head); // Ensure selection is done with visual block mode rather than one // continuous range. eq(cm.getSelections().join(''), '23oo') helpers.doKeys('g', 'v'); - eqPos(Pos(2, 1), vim.sel.anchor); - eqPos(Pos(2, 2), vim.sel.head); + eqCursorPos(Pos(2, 1), vim.sel.anchor); + eqCursorPos(Pos(2, 2), vim.sel.head); helpers.doKeys(''); // Ensure selection of deleted range cm.setCursor(1, 1); @@ -2121,14 +2121,14 @@ testVim('o_visual', function(cm, vim, helpers) { testVim('o_visual_block', function(cm, vim, helpers) { cm.setCursor(0, 1); helpers.doKeys('','3','j','l','l', 'o'); - eqPos(Pos(3, 3), vim.sel.anchor); - eqPos(Pos(0, 1), vim.sel.head); + eqCursorPos(Pos(3, 3), vim.sel.anchor); + eqCursorPos(Pos(0, 1), vim.sel.head); helpers.doKeys('O'); - eqPos(Pos(3, 1), vim.sel.anchor); - eqPos(Pos(0, 3), vim.sel.head); + eqCursorPos(Pos(3, 1), vim.sel.anchor); + eqCursorPos(Pos(0, 3), vim.sel.head); helpers.doKeys('o'); - eqPos(Pos(0, 3), vim.sel.anchor); - eqPos(Pos(3, 1), vim.sel.head); + eqCursorPos(Pos(0, 3), vim.sel.anchor); + eqCursorPos(Pos(3, 1), vim.sel.head); }, { value: 'abcd\nefgh\nijkl\nmnop'}); testVim('changeCase_visual', function(cm, vim, helpers) { cm.setCursor(0, 0); @@ -4068,7 +4068,7 @@ testVim('ex_map_key2key_from_colon', function(cm, vim, helpers) { // Test event handlers testVim('beforeSelectionChange', function(cm, vim, helpers) { cm.setCursor(0, 100); - eqPos(cm.getCursor('head'), cm.getCursor('anchor')); + eqCursorPos(cm.getCursor('head'), cm.getCursor('anchor')); }, { value: 'abc' }); From 6f39e7869b5dfe6a76b00ed5083c6a4d3049b709 Mon Sep 17 00:00:00 2001 From: Adrian Heine Date: Mon, 16 Jan 2017 10:40:29 +0100 Subject: [PATCH 0643/1790] Work around Firefox > 50 in tests --- test/test.js | 2 ++ 1 file changed, 2 insertions(+) diff --git a/test/test.js b/test/test.js index 38659f87..c4941cfe 100644 --- a/test/test.js +++ b/test/test.js @@ -2216,6 +2216,8 @@ function makeItWrapAfter(cm, pos) { cm.setSize(w); posTop = cm.charCoords(pos).top; } + // Firefox > 50 compresses a space when two spaces from different bidi spans meet + cm.setSize(w + 10); } function testMoveBidi(str) { From bdb0fb307f66d39c57eb0e076bb5a37060aa45b8 Mon Sep 17 00:00:00 2001 From: Adrian Heine Date: Mon, 16 Jan 2017 23:03:30 +0100 Subject: [PATCH 0644/1790] Improve move_bidi test set * Enable most in phantom * Enable all in !phantom * Disable two that trigger L1 which we don't do correctly currently * Revert commit 5cc5d7f50efc7a016485f017a603c4fa2b035266 (L1 tests) --- test/test.js | 29 +++++++++++++++++++---------- 1 file changed, 19 insertions(+), 10 deletions(-) diff --git a/test/test.js b/test/test.js index c4941cfe..4853e670 100644 --- a/test/test.js +++ b/test/test.js @@ -2216,13 +2216,11 @@ function makeItWrapAfter(cm, pos) { cm.setSize(w); posTop = cm.charCoords(pos).top; } - // Firefox > 50 compresses a space when two spaces from different bidi spans meet - cm.setSize(w + 10); } function testMoveBidi(str) { testCM("move_bidi_" + str, function(cm) { - if (cm.getOption("inputStyle") != "textarea" || webkit || !cm.getOption("rtlMoveVisually")) return; + if (cm.getOption("inputStyle") != "textarea" || !cm.getOption("rtlMoveVisually")) return; cm.getScrollerElement().style.fontFamily = "monospace"; makeItWrapAfter(cm, Pos(0, 5)); @@ -2285,26 +2283,37 @@ function testMoveBidi(str) { }, {value: str, lineWrapping: true}) }; +// We don't correctly implement L1 UBA +// See https://bugzilla.mozilla.org/show_bug.cgi?id=1331501 +// and https://bugs.chromium.org/p/chromium/issues/detail?id=673405 +/* testMoveBidi("Say ا ب جabj\nS"); -testMoveBidi("Όȝǝڪȉۥ״ۺ׆ɀҩۏ\nҳ"); +testMoveBidi("Sayyy ا ا ب ج"); +*/ + +if (!phantom) { + testMoveBidi("Όȝǝڪȉۥ״ۺ׆ɀҩۏ\nҳ"); + testMoveBidi("ŌӰтقȤ؁ƥ؅٣ĎȺ١\nϚ"); + testMoveBidi("ٻоҤѕѽΩ־؉ïίքdz\nٵ"); + testMoveBidi("؅؁ĆՕƿɁǞϮؠȩóć\nď"); + testMoveBidi("RŨďңŪzϢŎƏԖڇڦ\nӈ"); +} + testMoveBidi("ό׊۷٢ԜһОצЉيčǟ\nѩ"); testMoveBidi("ۑÚҳҕڬġڹհяųKV\nr"); -testMoveBidi("ŌӰтقȤ؁ƥ؅٣ĎȺ١\nϚ"); -testMoveBidi("ٻоҤѕѽΩ־؉ïίքdz\nٵ"); -//testMoveBidi("Count ١ ٢ ٣ ٤"); -testMoveBidi("Sayyy ا ا ب ج"); -testMoveBidi("؅؁ĆՕƿɁǞϮؠȩóć\nď"); testMoveBidi("źڻғúہ4ם1Ƞc1a\nԁ"); testMoveBidi("ҒȨҟփƞ٦ԓȦڰғâƥ\nڤ"); testMoveBidi("քմѧǮßپüŢҍҞўڳ\nӧ"); -testMoveBidi("RŨďңŪzϢŎƏԖڇڦ\nӈ"); testMoveBidi("ϖسՉȏŧΔԛdžĎӟیڡ\nέ"); testMoveBidi("۹ؼL۵ĺȧКԙػא7״\nم"); + +//testMoveBidi("Count ١ ٢ ٣ ٤"); //testMoveBidi("ӣאƦϰ؊ȓېÛوը٬ز\nϪ"); //testMoveBidi("ҾճٳџIՖӻ٥׭֐؜ڏ\nێ"); //testMoveBidi("ҬÓФ؜ڂį٦Ͽɓڐͳٵ\nՈ"); //testMoveBidi("aѴNijȻهˇ҃ڱӧǻֵ\na"); //testMoveBidi(" a٧ا٢ ب جa\nS"); + /* for (var i = 0; i < 5; ++i) { testMoveBidi(getString(12) + "\n" + getString(1)); From a6b016982bacabb83d9524463be159e1325eb7ad Mon Sep 17 00:00:00 2001 From: Adrian Heine Date: Thu, 26 Jan 2017 09:27:44 +0100 Subject: [PATCH 0645/1790] Disable more tests in phantomjs --- test/test.js | 12 ++++++------ 1 file changed, 6 insertions(+), 6 deletions(-) diff --git a/test/test.js b/test/test.js index 4853e670..1339e7f5 100644 --- a/test/test.js +++ b/test/test.js @@ -2297,15 +2297,15 @@ if (!phantom) { testMoveBidi("ٻоҤѕѽΩ־؉ïίքdz\nٵ"); testMoveBidi("؅؁ĆՕƿɁǞϮؠȩóć\nď"); testMoveBidi("RŨďңŪzϢŎƏԖڇڦ\nӈ"); + testMoveBidi("ό׊۷٢ԜһОצЉيčǟ\nѩ"); + testMoveBidi("ۑÚҳҕڬġڹհяųKV\nr"); + testMoveBidi("źڻғúہ4ם1Ƞc1a\nԁ"); + testMoveBidi("ҒȨҟփƞ٦ԓȦڰғâƥ\nڤ"); + testMoveBidi("ϖسՉȏŧΔԛdžĎӟیڡ\nέ"); + testMoveBidi("۹ؼL۵ĺȧКԙػא7״\nم"); } -testMoveBidi("ό׊۷٢ԜһОצЉيčǟ\nѩ"); -testMoveBidi("ۑÚҳҕڬġڹհяųKV\nr"); -testMoveBidi("źڻғúہ4ם1Ƞc1a\nԁ"); -testMoveBidi("ҒȨҟփƞ٦ԓȦڰғâƥ\nڤ"); testMoveBidi("քմѧǮßپüŢҍҞўڳ\nӧ"); -testMoveBidi("ϖسՉȏŧΔԛdžĎӟیڡ\nέ"); -testMoveBidi("۹ؼL۵ĺȧКԙػא7״\nم"); //testMoveBidi("Count ١ ٢ ٣ ٤"); //testMoveBidi("ӣאƦϰ؊ȓېÛوը٬ز\nϪ"); From 1cca0b47a4d424f8b1e9f29d1e525474b4768d40 Mon Sep 17 00:00:00 2001 From: Adrian Heine Date: Thu, 26 Jan 2017 10:34:11 +0100 Subject: [PATCH 0646/1790] Fix Home with wrapped rtl content --- src/input/movement.js | 12 +++++------- 1 file changed, 5 insertions(+), 7 deletions(-) diff --git a/src/input/movement.js b/src/input/movement.js index 95cc50fc..a3f3eab8 100644 --- a/src/input/movement.js +++ b/src/input/movement.js @@ -17,15 +17,13 @@ export function endOfLine(visually, cm, lineObj, lineNo, dir) { // Thus, in rtl, we are looking for the first (content-order) character // in the rtl chunk that is on the last line (that is, the same line // as the last (content-order) character). - if (dir < 0 && part.level > 0) { + if (part.level > 0) { let getTop = prepareMeasureCharTop(cm, lineObj) - ch = lineObj.text.length - 1 + ch = dir < 0 ? lineObj.text.length - 1 : 0 let targetTop = getTop(ch) - ch = findFirst(ch => getTop(ch) == targetTop, part.from, ch) - if (part.level != 1) ch = moveLogically(lineObj, new Pos(lineNo, ch, sticky), 1, true) - return new Pos(lineNo, ch, sticky) - } - ch = (dir < 0 ? bidiRight : bidiLeft)(part) + ch = findFirst(ch => getTop(ch) == targetTop, (dir < 0) == (part.level == 1) ? part.from : part.to - 1, ch) + if (sticky == "before") ch = moveLogically(lineObj, new Pos(lineNo, ch, sticky), 1, true) + } else ch = (dir < 0 ? bidiRight : bidiLeft)(part) return new Pos(lineNo, ch, sticky) } } From 1451a186f71dc4c37636d9dc075cf313c06f3592 Mon Sep 17 00:00:00 2001 From: Adrian Heine Date: Thu, 26 Jan 2017 11:28:35 +0100 Subject: [PATCH 0647/1790] Fix moving into the 2nd wrapped line in 1st rtl span --- src/input/movement.js | 6 +++--- test/test.js | 1 + 2 files changed, 4 insertions(+), 3 deletions(-) diff --git a/src/input/movement.js b/src/input/movement.js index a3f3eab8..a97aa074 100644 --- a/src/input/movement.js +++ b/src/input/movement.js @@ -82,7 +82,7 @@ export function moveVisually(cm, line, start, dir) { ? new Pos(start.line, mv(ch, 1), "before") : new Pos(start.line, ch, "after") - for (partPos += dir; partPos >= 0 && partPos < bidi.length; partPos += dir) { + for (; partPos >= 0 && partPos < bidi.length; partPos += dir) { let part = bidi[partPos] let moveInStorageOrder = (dir > 0) == (part.level != 1) let ch = moveInStorageOrder ? visualLine[0] : visualLine[1] @@ -93,13 +93,13 @@ export function moveVisually(cm, line, start, dir) { } // Case 3a: Look for other bidi parts on the same visual line - let res = searchInVisualLine(partPos, dir, visualLine) + let res = searchInVisualLine(partPos + dir, dir, visualLine) if (res) return res // Case 3b: Look for other bidi parts on the next visual line let nextCh = mv(visualLine[dir > 0 ? 1 : 0], dir) if (nextCh != null && !(dir > 0 && nextCh == line.text.length)) { - res = searchInVisualLine(dir > 0 ? 0 : bidi.length, dir, getVisualLine(nextCh)) + res = searchInVisualLine(dir > 0 ? 0 : bidi.length - 1, dir, getVisualLine(nextCh)) if (res) return res } diff --git a/test/test.js b/test/test.js index 1339e7f5..633190ac 100644 --- a/test/test.js +++ b/test/test.js @@ -2306,6 +2306,7 @@ if (!phantom) { } testMoveBidi("քմѧǮßپüŢҍҞўڳ\nӧ"); +testMoveBidi("ن (ي)\u2009أقواس"); // thin space to throw off Firefox 51's broken white-space compressing behavior //testMoveBidi("Count ١ ٢ ٣ ٤"); //testMoveBidi("ӣאƦϰ؊ȓېÛوը٬ز\nϪ"); From 6fc751f89b971504cbaec7be83a7f5d3bc8c8455 Mon Sep 17 00:00:00 2001 From: Adrian Heine Date: Thu, 26 Jan 2017 11:39:51 +0100 Subject: [PATCH 0648/1790] Trust and test goLineStart --- test/test.js | 74 ++++++++++++++++++++++++++++++++-------------------- 1 file changed, 46 insertions(+), 28 deletions(-) diff --git a/test/test.js b/test/test.js index 633190ac..7f6d5d86 100644 --- a/test/test.js +++ b/test/test.js @@ -2232,13 +2232,7 @@ function testMoveBidi(str) { } // Make sure we are at the visual beginning of the first line - var pos = Pos(0, 0), lastPos; - cm.doc.setCursor(pos); - do { - lastPos = pos; - cm.execCommand("goCharLeft"); - pos = cm.doc.getCursor(); - } while (pos != lastPos && pos.ch != 0) + cm.execCommand("goLineStart"); var prevCoords = cm.cursorCoords(), coords; for(var i = 0; i < steps; ++i) { @@ -2283,37 +2277,61 @@ function testMoveBidi(str) { }, {value: str, lineWrapping: true}) }; +function testMoveEndBidi(str) { + testCM("move_end_bidi_" + str, function(cm) { + cm.getScrollerElement().style.fontFamily = "monospace"; + makeItWrapAfter(cm, Pos(0, 5)); + + cm.execCommand("goLineStart"); + var pos = cm.doc.getCursor(); + cm.execCommand("goCharLeft"); + eqCursorPos(pos, cm.doc.getCursor()); + + cm.execCommand("goLineEnd"); + pos = cm.doc.getCursor(); + cm.execCommand("goColumnRight"); + eqCursorPos(pos, cm.doc.getCursor()); + }, {value: str, lineWrapping: true}) +}; + +var bidiTests = []; + // We don't correctly implement L1 UBA // See https://bugzilla.mozilla.org/show_bug.cgi?id=1331501 // and https://bugs.chromium.org/p/chromium/issues/detail?id=673405 /* -testMoveBidi("Say ا ب جabj\nS"); -testMoveBidi("Sayyy ا ا ب ج"); +bidiTests.push("Say ا ب جabj\nS"); +bidiTests.push("Sayyy ا ا ب ج"); */ if (!phantom) { - testMoveBidi("Όȝǝڪȉۥ״ۺ׆ɀҩۏ\nҳ"); - testMoveBidi("ŌӰтقȤ؁ƥ؅٣ĎȺ١\nϚ"); - testMoveBidi("ٻоҤѕѽΩ־؉ïίքdz\nٵ"); - testMoveBidi("؅؁ĆՕƿɁǞϮؠȩóć\nď"); - testMoveBidi("RŨďңŪzϢŎƏԖڇڦ\nӈ"); - testMoveBidi("ό׊۷٢ԜһОצЉيčǟ\nѩ"); - testMoveBidi("ۑÚҳҕڬġڹհяųKV\nr"); - testMoveBidi("źڻғúہ4ם1Ƞc1a\nԁ"); - testMoveBidi("ҒȨҟփƞ٦ԓȦڰғâƥ\nڤ"); - testMoveBidi("ϖسՉȏŧΔԛdžĎӟیڡ\nέ"); - testMoveBidi("۹ؼL۵ĺȧКԙػא7״\nم"); + bidiTests.push("Όȝǝڪȉۥ״ۺ׆ɀҩۏ\nҳ"); + bidiTests.push("ŌӰтقȤ؁ƥ؅٣ĎȺ١\nϚ"); + bidiTests.push("ٻоҤѕѽΩ־؉ïίքdz\nٵ"); + bidiTests.push("؅؁ĆՕƿɁǞϮؠȩóć\nď"); + bidiTests.push("RŨďңŪzϢŎƏԖڇڦ\nӈ"); + bidiTests.push("ό׊۷٢ԜһОצЉيčǟ\nѩ"); + bidiTests.push("ۑÚҳҕڬġڹհяųKV\nr"); + bidiTests.push("źڻғúہ4ם1Ƞc1a\nԁ"); + bidiTests.push("ҒȨҟփƞ٦ԓȦڰғâƥ\nڤ"); + bidiTests.push("ϖسՉȏŧΔԛdžĎӟیڡ\nέ"); + bidiTests.push("۹ؼL۵ĺȧКԙػא7״\nم"); } -testMoveBidi("քմѧǮßپüŢҍҞўڳ\nӧ"); -testMoveBidi("ن (ي)\u2009أقواس"); // thin space to throw off Firefox 51's broken white-space compressing behavior +bidiTests.push("քմѧǮßپüŢҍҞўڳ\nӧ"); +bidiTests.push("ن (ي)\u2009أقواس"); // thin space to throw off Firefox 51's broken white-space compressing behavior -//testMoveBidi("Count ١ ٢ ٣ ٤"); -//testMoveBidi("ӣאƦϰ؊ȓېÛوը٬ز\nϪ"); -//testMoveBidi("ҾճٳџIՖӻ٥׭֐؜ڏ\nێ"); -//testMoveBidi("ҬÓФ؜ڂį٦Ͽɓڐͳٵ\nՈ"); -//testMoveBidi("aѴNijȻهˇ҃ڱӧǻֵ\na"); -//testMoveBidi(" a٧ا٢ ب جa\nS"); +//bidiTests.push("Count ١ ٢ ٣ ٤"); +//bidiTests.push("ӣאƦϰ؊ȓېÛوը٬ز\nϪ"); +//bidiTests.push("ҾճٳџIՖӻ٥׭֐؜ڏ\nێ"); +//bidiTests.push("ҬÓФ؜ڂį٦Ͽɓڐͳٵ\nՈ"); +//bidiTests.push("aѴNijȻهˇ҃ڱӧǻֵ\na"); +//bidiTests.push(" a٧ا٢ ب جa\nS"); + +for (var i = 0; i < bidiTests.length; ++i) { + testMoveBidi(bidiTests[i]); + testMoveEndBidi(bidiTests[i]); +} /* for (var i = 0; i < 5; ++i) { From 8a739ddba0860e3a78c2aecf0b4fa16ed6dc899c Mon Sep 17 00:00:00 2001 From: Adrian Heine Date: Thu, 26 Jan 2017 15:38:27 +0100 Subject: [PATCH 0649/1790] Disable test in phantom that fails on travis --- test/test.js | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/test/test.js b/test/test.js index 7f6d5d86..93bca15e 100644 --- a/test/test.js +++ b/test/test.js @@ -2316,10 +2316,10 @@ if (!phantom) { bidiTests.push("ҒȨҟփƞ٦ԓȦڰғâƥ\nڤ"); bidiTests.push("ϖسՉȏŧΔԛdžĎӟیڡ\nέ"); bidiTests.push("۹ؼL۵ĺȧКԙػא7״\nم"); + bidiTests.push("ن (ي)\u2009أقواس"); // thin space to throw off Firefox 51's broken white-space compressing behavior } bidiTests.push("քմѧǮßپüŢҍҞўڳ\nӧ"); -bidiTests.push("ن (ي)\u2009أقواس"); // thin space to throw off Firefox 51's broken white-space compressing behavior //bidiTests.push("Count ١ ٢ ٣ ٤"); //bidiTests.push("ӣאƦϰ؊ȓېÛوը٬ز\nϪ"); From cfc17398a7e6ada1efdb4429227f75577d2c112c Mon Sep 17 00:00:00 2001 From: Adrian Heine Date: Tue, 31 Jan 2017 13:24:21 +0100 Subject: [PATCH 0650/1790] Greatly improve base-case performance in coordsChar --- src/measurement/position_measurement.js | 20 ++++++++++++++------ 1 file changed, 14 insertions(+), 6 deletions(-) diff --git a/src/measurement/position_measurement.js b/src/measurement/position_measurement.js index 531ab1d7..aa563363 100644 --- a/src/measurement/position_measurement.js +++ b/src/measurement/position_measurement.js @@ -433,14 +433,14 @@ function coordsCharInner(cm, lineObj, lineNo, x, y) { y -= heightAtLine(lineObj) let begin = 0, end = lineObj.text.length - 1 let preparedMeasure = prepareMeasureForLine(cm, lineObj) - if (cm.options.lineWrapping) { - let measure = ch => intoCoordSystem(cm, lineObj, measureCharPrepared(cm, preparedMeasure, ch), "line") - begin = findFirst(ch => measure(ch).bottom < y, end, begin - 1) + 1 - end = findFirst(ch => measure(ch).top > y, begin, end + 1) - 1 - } let pos let order = getOrder(lineObj) if (order) { + if (cm.options.lineWrapping) { + let measure = ch => intoCoordSystem(cm, lineObj, measureCharPrepared(cm, preparedMeasure, ch), "line") + begin = findFirst(ch => measure(ch).bottom < y, end, begin - 1) + 1 + end = findFirst(ch => measure(ch).top > y, begin, end + 1) - 1 + } if (end == lineObj.text.length - 1) ++end pos = new Pos(lineNo, begin) let beginLeft = cursorCoords(cm, pos, "line", lineObj, preparedMeasure).left @@ -461,7 +461,15 @@ function coordsCharInner(cm, lineObj, lineNo, x, y) { } else { let ch = findFirst(ch => { let box = measureCharPrepared(cm, preparedMeasure, ch) - return x - box.right < box.left - x + if (box.top > y) { + // For the cursor stickiness + end = Math.min(ch - 1, end) + return true + } + else if (box.bottom < y) return false + else if (box.left > x) return true + else if (box.right < x) return false + else return (x - box.left < box.right - x) }, begin, end + 1) while (isExtendingChar(lineObj.text.charAt(ch))) ++ch pos = new Pos(lineNo, ch, (ch == end + 1) ? "before" : "after") From a37da5ccb58e2c751567e73dd9d9da966c5b2419 Mon Sep 17 00:00:00 2001 From: Adrian Heine Date: Wed, 1 Feb 2017 20:31:54 +0100 Subject: [PATCH 0651/1790] Fix failing jumpTheGap on Mac --- src/measurement/position_measurement.js | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/measurement/position_measurement.js b/src/measurement/position_measurement.js index aa563363..dd2ec549 100644 --- a/src/measurement/position_measurement.js +++ b/src/measurement/position_measurement.js @@ -460,7 +460,7 @@ function coordsCharInner(cm, lineObj, lineNo, x, y) { if (Math.abs(diff) > Math.abs(prevDiff)) pos = moveVisually(cm, lineObj, pos, -dir) } else { let ch = findFirst(ch => { - let box = measureCharPrepared(cm, preparedMeasure, ch) + let box = intoCoordSystem(cm, lineObj, measureCharPrepared(cm, preparedMeasure, ch), "line") if (box.top > y) { // For the cursor stickiness end = Math.min(ch - 1, end) From 946d2bb76249b6176ef506f44ab032cddcbff1b6 Mon Sep 17 00:00:00 2001 From: Marijn Haverbeke Date: Thu, 2 Feb 2017 12:38:33 +0100 Subject: [PATCH 0652/1790] [powershell mode] Remove ES5-ism from tests --- mode/powershell/test.js | 10 ++++++---- 1 file changed, 6 insertions(+), 4 deletions(-) diff --git a/mode/powershell/test.js b/mode/powershell/test.js index 59b8e6fc..9c9aed0d 100644 --- a/mode/powershell/test.js +++ b/mode/powershell/test.js @@ -5,18 +5,20 @@ var mode = CodeMirror.getMode({indentUnit: 2}, "powershell"); function MT(name) { test.mode(name, mode, Array.prototype.slice.call(arguments, 1)); } + function forEach(arr, f) { for (var i = 0; i < arr.length; i++) f(arr[i], i) } + MT('comment', '[number 1][comment # A]'); MT('comment_multiline', '[number 1][comment <#]', '[comment ABC]', '[comment #>][number 2]'); - [ + forEach([ '0', '1234', '12kb', '12mb', '12Gb', '12Tb', '12PB', '12L', '12D', '12lkb', '12dtb', '1.234', '1.234e56', '1.', '1.e2', '.2', '.2e34', '1.2MB', '1.kb', '.1dTB', '1.e1gb', '.2', '.2e34', '0x1', '0xabcdef', '0x3tb', '0xelmb' - ].forEach(function(number) { + ], function(number) { MT("number_" + number, "[number " + number + "]"); }); @@ -60,9 +62,9 @@ MT('operator_unary', "[operator +][number 3]"); MT('operator_long', "[operator -match]"); - [ + forEach([ '(', ')', '[[', ']]', '{', '}', ',', '`', ';', '.' - ].forEach(function(punctuation) { + ], function(punctuation) { MT("punctuation_" + punctuation.replace(/^[\[\]]/,''), "[punctuation " + punctuation + "]"); }); From cde6a91246d00f6ed5b938e8376933060b7a91b5 Mon Sep 17 00:00:00 2001 From: Marijn Haverbeke Date: Thu, 2 Feb 2017 12:56:25 +0100 Subject: [PATCH 0653/1790] Avoid setting a negative width style Since this raises an exception in IE8 --- src/display/scrollbars.js | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/display/scrollbars.js b/src/display/scrollbars.js index 2026a3b0..d03850be 100644 --- a/src/display/scrollbars.js +++ b/src/display/scrollbars.js @@ -69,7 +69,7 @@ class NativeScrollbars { this.horiz.style.left = measure.barLeft + "px" let totalWidth = measure.viewWidth - measure.barLeft - (needsV ? sWidth : 0) this.horiz.firstChild.style.width = - (measure.scrollWidth - measure.clientWidth + totalWidth) + "px" + Math.max(0, measure.scrollWidth - measure.clientWidth + totalWidth) + "px" } else { this.horiz.style.display = "" this.horiz.firstChild.style.width = "0" From 3357762162b1a352d01ca500695b952303eec74d Mon Sep 17 00:00:00 2001 From: Marijn Haverbeke Date: Thu, 2 Feb 2017 12:57:09 +0100 Subject: [PATCH 0654/1790] Fix some assumptions in tests that don't hold on IE8 ES5, breaking up of whitespace tokens --- test/index.html | 2 +- test/test.js | 19 +++++++++++++------ 2 files changed, 14 insertions(+), 7 deletions(-) diff --git a/test/index.html b/test/index.html index deeb7781..5a0b3d68 100644 --- a/test/index.html +++ b/test/index.html @@ -222,7 +222,7 @@

    Test Suite

    function displayTest(type, name, customMessage) { var message = "???"; if (type != "done" && type != "skipped") ++count; - progress.style.width = (count * (progress.parentNode.clientWidth - 2) / totalTests) + "px"; + progress.style.width = (count * (progress.parentNode.clientWidth - 2) / (totalTests || 1)) + "px"; progressRan.nodeValue = count; if (type == "ok") { message = "Test '" + name + "' passed"; diff --git a/test/test.js b/test/test.js index 93bca15e..37abdd3f 100644 --- a/test/test.js +++ b/test/test.js @@ -1017,7 +1017,8 @@ testCM("showEmptyWidgetSpan", function(cm) { clearWhenEmpty: false, replacedWith: document.createTextNode("X") }); - eq(cm.display.view[0].text.textContent, "abXc"); + var text = cm.display.view[0].text; + eq(text.textContent || text.innerText, "abXc"); }, {value: "abc"}); testCM("changedInlineWidget", function(cm) { @@ -1963,8 +1964,8 @@ testCM("lineStyleFromMode", function(cm) { is(!/parens.*parens/.test(parenElts[0].className)); eq(parenElts[0].parentElement.nodeName, "DIV"); - eq(byClassName(cm.getWrapperElement(), "bg").length, 1); - eq(byClassName(cm.getWrapperElement(), "line").length, 1); + is(byClassName(cm.getWrapperElement(), "bg").length > 0); + is(byClassName(cm.getWrapperElement(), "line").length > 0); var spanElts = byClassName(cm.getWrapperElement(), "cm-span"); eq(spanElts.length, 2); is(/^\s*cm-span\s*$/.test(spanElts[0].className)); @@ -2218,15 +2219,21 @@ function makeItWrapAfter(cm, pos) { } } +function countIf(arr, f) { + var result = 0 + for (var i = 0; i < arr.length; i++) if (f[arr[i]]) result++ + return result +} + function testMoveBidi(str) { testCM("move_bidi_" + str, function(cm) { if (cm.getOption("inputStyle") != "textarea" || !cm.getOption("rtlMoveVisually")) return; cm.getScrollerElement().style.fontFamily = "monospace"; makeItWrapAfter(cm, Pos(0, 5)); - var steps = str.length - str.split("").filter(extendingChars.test.bind(extendingChars)).length; - var lineBreaks = Object.create(null); - lineBreaks[6 - str.substr(0, 5).split("").filter(extendingChars.test.bind(extendingChars)).length] = 'w'; + var steps = str.length - countIf(str.split(""), function(ch) { return extendingChars.test(ch) }); + var lineBreaks = {} + lineBreaks[6 - countIf(str.substr(0, 5).split(""), function(ch) { return extendingChars.test(ch) })] = 'w'; if (str.indexOf("\n") != -1) { lineBreaks[steps - 2] = 'n'; } From a430563a47baa59ed8bd5f15333a49e076617abe Mon Sep 17 00:00:00 2001 From: Marijn Haverbeke Date: Thu, 2 Feb 2017 13:14:25 +0100 Subject: [PATCH 0655/1790] Remove unneccesary local functions in moveOnce --- src/edit/methods.js | 20 ++++++++++++-------- 1 file changed, 12 insertions(+), 8 deletions(-) diff --git a/src/edit/methods.js b/src/edit/methods.js index 18412d31..2883909e 100644 --- a/src/edit/methods.js +++ b/src/edit/methods.js @@ -475,17 +475,21 @@ function findPosH(doc, pos, dir, unit, visually) { return lineObj = getLine(doc, l) } function moveOnce(boundToLine) { - let myMoveVisually = (line, start, dir) => moveVisually(doc.cm, line, start, dir) - let myMoveLogically = (line, start, dir) => { - let ch = moveLogically(line, start, dir) - return ch == null ? null : new Pos(pos.line, ch, dir < 0 ? "after" : "before") + let next + if (visually) { + next = moveVisually(doc.cm, lineObj, pos, dir) + } else { + let ch = moveLogically(lineObj, pos, dir) + next = ch == null ? null : new Pos(pos.line, ch, dir < 0 ? "after" : "before") } - let next = (visually ? myMoveVisually : myMoveLogically)(lineObj, pos, dir) if (next == null) { - if (!boundToLine && findNextLine()) { + if (!boundToLine && findNextLine()) pos = endOfLine(visually, doc.cm, lineObj, pos.line, dir) - } else return false - } else pos = next + else + return false + } else { + pos = next + } return true } From 3eba4a5c597525cd490ea62c9316e31ecfb0d126 Mon Sep 17 00:00:00 2001 From: Marijn Haverbeke Date: Thu, 2 Feb 2017 13:32:55 +0100 Subject: [PATCH 0656/1790] Get rid of helper function prepareMeasureCharTop --- src/input/movement.js | 16 ++++++++-------- src/measurement/position_measurement.js | 9 ++------- 2 files changed, 10 insertions(+), 15 deletions(-) diff --git a/src/input/movement.js b/src/input/movement.js index a97aa074..80e8f258 100644 --- a/src/input/movement.js +++ b/src/input/movement.js @@ -1,5 +1,5 @@ import { Pos } from "../line/pos" -import { prepareMeasureCharTop } from "../measurement/position_measurement" +import { prepareMeasureForLine, measureCharPrepared } from "../measurement/position_measurement" import { bidiLeft, bidiRight, getBidiPartAt, getOrder, lineLeft, lineRight, moveLogically } from "../util/bidi" import { findFirst } from "../util/misc" @@ -18,10 +18,10 @@ export function endOfLine(visually, cm, lineObj, lineNo, dir) { // in the rtl chunk that is on the last line (that is, the same line // as the last (content-order) character). if (part.level > 0) { - let getTop = prepareMeasureCharTop(cm, lineObj) + let prep = prepareMeasureForLine(cm, lineObj) ch = dir < 0 ? lineObj.text.length - 1 : 0 - let targetTop = getTop(ch) - ch = findFirst(ch => getTop(ch) == targetTop, (dir < 0) == (part.level == 1) ? part.from : part.to - 1, ch) + let targetTop = measureCharPrepared(cm, prep, ch).top + ch = findFirst(ch => measureCharPrepared(cm, prep, ch).top == targetTop, (dir < 0) == (part.level == 1) ? part.from : part.to - 1, ch) if (sticky == "before") ch = moveLogically(lineObj, new Pos(lineNo, ch, sticky), 1, true) } else ch = (dir < 0 ? bidiRight : bidiLeft)(part) return new Pos(lineNo, ch, sticky) @@ -35,11 +35,11 @@ export function endOfLine(visually, cm, lineObj, lineNo, dir) { function getVisualLineAround(cm, line, target) { if (!cm.options.lineWrapping) return [0, line.text.length - 1] - let measureTop = prepareMeasureCharTop(cm, line) - let targetTop = measureTop(target) + let prep = prepareMeasureForLine(cm, line) + let targetTop = measureCharPrepared(cm, prep, target).top return [ - findFirst(ch => targetTop == measureTop(ch), 0, target), - findFirst(ch => targetTop == measureTop(ch), line.text.length - 1, target) + findFirst(ch => targetTop == measureCharPrepared(cm, prep, ch).top, 0, target), + findFirst(ch => targetTop == measureCharPrepared(cm, prep, ch).top, line.text.length - 1, target) ] } diff --git a/src/measurement/position_measurement.js b/src/measurement/position_measurement.js index dd2ec549..10334443 100644 --- a/src/measurement/position_measurement.js +++ b/src/measurement/position_measurement.js @@ -89,11 +89,6 @@ export function measureChar(cm, line, ch, bias) { return measureCharPrepared(cm, prepareMeasureForLine(cm, line), ch, bias) } -export function prepareMeasureCharTop(cm, line) { - let preparedMeasure = prepareMeasureForLine(cm, line) - return ch => measureCharPrepared(cm, preparedMeasure, ch).top -} - // Find a line view that corresponds to the given line number. export function findViewForLine(cm, lineN) { if (lineN >= cm.display.viewFrom && lineN < cm.display.viewTo) @@ -108,7 +103,7 @@ export function findViewForLine(cm, lineN) { // character. Functions like coordsChar, that need to do a lot of // measurements in a row, can thus ensure that the set-up work is // only done once. -function prepareMeasureForLine(cm, line) { +export function prepareMeasureForLine(cm, line) { let lineN = lineNo(line) let view = findViewForLine(cm, lineN) if (view && !view.text) { @@ -130,7 +125,7 @@ function prepareMeasureForLine(cm, line) { // Given a prepared measurement object, measures the position of an // actual character (or fetches it from the cache). -function measureCharPrepared(cm, prepared, ch, bias, varHeight) { +export function measureCharPrepared(cm, prepared, ch, bias, varHeight) { if (prepared.before) ch = -1 let key = ch + (bias || ""), found if (prepared.cache.hasOwnProperty(key)) { From d4d278b984650b399cc0e653b97c83ce254ddb6d Mon Sep 17 00:00:00 2001 From: Marijn Haverbeke Date: Thu, 2 Feb 2017 13:43:42 +0100 Subject: [PATCH 0657/1790] Factor anon-function-heavy conditional into separate function --- src/measurement/position_measurement.js | 12 +++++++++--- 1 file changed, 9 insertions(+), 3 deletions(-) diff --git a/src/measurement/position_measurement.js b/src/measurement/position_measurement.js index 10334443..4d24cdf5 100644 --- a/src/measurement/position_measurement.js +++ b/src/measurement/position_measurement.js @@ -424,6 +424,14 @@ export function coordsChar(cm, x, y) { } } +function wrappedLineExtent(cm, lineObj, preparedMeasure, y) { + let measure = ch => intoCoordSystem(cm, lineObj, measureCharPrepared(cm, preparedMeasure, ch), "line") + let end = lineObj.text.length - 1 + let begin = findFirst(ch => measure(ch).bottom < y, end, 0) + 1 + end = findFirst(ch => measure(ch).top > y, begin, end + 1) - 1 + return {begin, end} +} + function coordsCharInner(cm, lineObj, lineNo, x, y) { y -= heightAtLine(lineObj) let begin = 0, end = lineObj.text.length - 1 @@ -432,9 +440,7 @@ function coordsCharInner(cm, lineObj, lineNo, x, y) { let order = getOrder(lineObj) if (order) { if (cm.options.lineWrapping) { - let measure = ch => intoCoordSystem(cm, lineObj, measureCharPrepared(cm, preparedMeasure, ch), "line") - begin = findFirst(ch => measure(ch).bottom < y, end, begin - 1) + 1 - end = findFirst(ch => measure(ch).top > y, begin, end + 1) - 1 + ;({begin, end} = wrappedLineExtent(cm, lineObj, preparedMeasure, y)) } if (end == lineObj.text.length - 1) ++end pos = new Pos(lineNo, begin) From 4627e849852312a4805c85a47cfb76e74450d758 Mon Sep 17 00:00:00 2001 From: Marijn Haverbeke Date: Thu, 2 Feb 2017 14:04:37 +0100 Subject: [PATCH 0658/1790] Simplify coordsCharInner by making end position exclusive --- src/measurement/position_measurement.js | 17 ++++++++--------- 1 file changed, 8 insertions(+), 9 deletions(-) diff --git a/src/measurement/position_measurement.js b/src/measurement/position_measurement.js index 4d24cdf5..cd7bbfe3 100644 --- a/src/measurement/position_measurement.js +++ b/src/measurement/position_measurement.js @@ -426,15 +426,15 @@ export function coordsChar(cm, x, y) { function wrappedLineExtent(cm, lineObj, preparedMeasure, y) { let measure = ch => intoCoordSystem(cm, lineObj, measureCharPrepared(cm, preparedMeasure, ch), "line") - let end = lineObj.text.length - 1 - let begin = findFirst(ch => measure(ch).bottom < y, end, 0) + 1 - end = findFirst(ch => measure(ch).top > y, begin, end + 1) - 1 + let end = lineObj.text.length + let begin = findFirst(ch => measure(ch).bottom < y, end - 1, 0) + 1 + end = findFirst(ch => measure(ch).top > y, begin, end) return {begin, end} } function coordsCharInner(cm, lineObj, lineNo, x, y) { y -= heightAtLine(lineObj) - let begin = 0, end = lineObj.text.length - 1 + let begin = 0, end = lineObj.text.length let preparedMeasure = prepareMeasureForLine(cm, lineObj) let pos let order = getOrder(lineObj) @@ -442,7 +442,6 @@ function coordsCharInner(cm, lineObj, lineNo, x, y) { if (cm.options.lineWrapping) { ;({begin, end} = wrappedLineExtent(cm, lineObj, preparedMeasure, y)) } - if (end == lineObj.text.length - 1) ++end pos = new Pos(lineNo, begin) let beginLeft = cursorCoords(cm, pos, "line", lineObj, preparedMeasure).left let dir = beginLeft < x ? 1 : -1 @@ -451,7 +450,7 @@ function coordsCharInner(cm, lineObj, lineNo, x, y) { prevDiff = diff let prevPos = pos pos = moveVisually(cm, lineObj, pos, dir) - if (pos == null || pos.ch < begin || end < pos.ch) { + if (pos == null || pos.ch < begin || end <= pos.ch) { pos = prevPos break } @@ -464,16 +463,16 @@ function coordsCharInner(cm, lineObj, lineNo, x, y) { let box = intoCoordSystem(cm, lineObj, measureCharPrepared(cm, preparedMeasure, ch), "line") if (box.top > y) { // For the cursor stickiness - end = Math.min(ch - 1, end) + end = Math.min(ch, end) return true } else if (box.bottom < y) return false else if (box.left > x) return true else if (box.right < x) return false else return (x - box.left < box.right - x) - }, begin, end + 1) + }, begin, end) while (isExtendingChar(lineObj.text.charAt(ch))) ++ch - pos = new Pos(lineNo, ch, (ch == end + 1) ? "before" : "after") + pos = new Pos(lineNo, ch, ch == end ? "before" : "after") } let coords = cursorCoords(cm, pos, "line", lineObj, preparedMeasure) if (y < coords.top || coords.bottom < y) pos.outside = true From cdc7b716f24339ec0011e64fcb2da5a029ab55e9 Mon Sep 17 00:00:00 2001 From: Marijn Haverbeke Date: Thu, 2 Feb 2017 14:14:16 +0100 Subject: [PATCH 0659/1790] Use ES6 classes for Selection and Range --- src/model/selection.js | 46 ++++++++++++++++++++++-------------------- 1 file changed, 24 insertions(+), 22 deletions(-) diff --git a/src/model/selection.js b/src/model/selection.js index a47ede93..97084fbc 100644 --- a/src/model/selection.js +++ b/src/model/selection.js @@ -6,14 +6,15 @@ import { indexOf } from "../util/misc" // (and non-touching) ranges, sorted, and an integer that indicates // which one is the primary selection (the one that's scrolled into // view, that getCursor returns, etc). -export function Selection(ranges, primIndex) { - this.ranges = ranges - this.primIndex = primIndex -} +export class Selection { + constructor(ranges, primIndex) { + this.ranges = ranges + this.primIndex = primIndex + } + + primary() { return this.ranges[this.primIndex] } -Selection.prototype = { - primary: function() { return this.ranges[this.primIndex] }, - equals: function(other) { + equals(other) { if (other == this) return true if (other.primIndex != this.primIndex || other.ranges.length != this.ranges.length) return false for (let i = 0; i < this.ranges.length; i++) { @@ -21,19 +22,22 @@ Selection.prototype = { if (!equalCursorPos(here.anchor, there.anchor) || !equalCursorPos(here.head, there.head)) return false } return true - }, - deepCopy: function() { + } + + deepCopy() { let out = [] for (let i = 0; i < this.ranges.length; i++) out[i] = new Range(copyPos(this.ranges[i].anchor), copyPos(this.ranges[i].head)) return new Selection(out, this.primIndex) - }, - somethingSelected: function() { + } + + somethingSelected() { for (let i = 0; i < this.ranges.length; i++) if (!this.ranges[i].empty()) return true return false - }, - contains: function(pos, end) { + } + + contains(pos, end) { if (!end) end = pos for (let i = 0; i < this.ranges.length; i++) { let range = this.ranges[i] @@ -44,16 +48,14 @@ Selection.prototype = { } } -export function Range(anchor, head) { - this.anchor = anchor; this.head = head -} - -Range.prototype = { - from: function() { return minPos(this.anchor, this.head) }, - to: function() { return maxPos(this.anchor, this.head) }, - empty: function() { - return this.head.line == this.anchor.line && this.head.ch == this.anchor.ch +export class Range { + constructor(anchor, head) { + this.anchor = anchor; this.head = head } + + from() { return minPos(this.anchor, this.head) } + to() { return maxPos(this.anchor, this.head) } + empty() { return this.head.line == this.anchor.line && this.head.ch == this.anchor.ch } } // Take an unsorted, potentially overlapping set of ranges, and From 7dad9d59fa95e3a9ee9864863762b0a131d6d55f Mon Sep 17 00:00:00 2001 From: Marijn Haverbeke Date: Thu, 2 Feb 2017 14:22:50 +0100 Subject: [PATCH 0660/1790] Use ES6 classes for BranchChunk and LeafChunk --- src/model/chunk.js | 89 +++++++++++++++++++++++++--------------------- 1 file changed, 49 insertions(+), 40 deletions(-) diff --git a/src/model/chunk.js b/src/model/chunk.js index 2a05d7c5..3879da29 100644 --- a/src/model/chunk.js +++ b/src/model/chunk.js @@ -15,21 +15,22 @@ import { signalLater } from "../util/operation_group" // // See also http://marijnhaverbeke.nl/blog/codemirror-line-tree.html -export function LeafChunk(lines) { - this.lines = lines - this.parent = null - let height = 0 - for (let i = 0; i < lines.length; ++i) { - lines[i].parent = this - height += lines[i].height +export class LeafChunk { + constructor(lines) { + this.lines = lines + this.parent = null + let height = 0 + for (let i = 0; i < lines.length; ++i) { + lines[i].parent = this + height += lines[i].height + } + this.height = height } - this.height = height -} -LeafChunk.prototype = { - chunkSize: function() { return this.lines.length }, + chunkSize() { return this.lines.length } + // Remove the n lines at offset 'at'. - removeInner: function(at, n) { + removeInner(at, n) { for (let i = at, e = at + n; i < e; ++i) { let line = this.lines[i] this.height -= line.height @@ -37,41 +38,45 @@ LeafChunk.prototype = { signalLater(line, "delete") } this.lines.splice(at, n) - }, + } + // Helper used to collapse a small branch into a single leaf. - collapse: function(lines) { + collapse(lines) { lines.push.apply(lines, this.lines) - }, + } + // Insert the given array of lines at offset 'at', count them as // having the given height. - insertInner: function(at, lines, height) { + insertInner(at, lines, height) { this.height += height this.lines = this.lines.slice(0, at).concat(lines).concat(this.lines.slice(at)) for (let i = 0; i < lines.length; ++i) lines[i].parent = this - }, + } + // Used to iterate over a part of the tree. - iterN: function(at, n, op) { + iterN(at, n, op) { for (let e = at + n; at < e; ++at) if (op(this.lines[at])) return true } } -export function BranchChunk(children) { - this.children = children - let size = 0, height = 0 - for (let i = 0; i < children.length; ++i) { - let ch = children[i] - size += ch.chunkSize(); height += ch.height - ch.parent = this +export class BranchChunk { + constructor(children) { + this.children = children + let size = 0, height = 0 + for (let i = 0; i < children.length; ++i) { + let ch = children[i] + size += ch.chunkSize(); height += ch.height + ch.parent = this + } + this.size = size + this.height = height + this.parent = null } - this.size = size - this.height = height - this.parent = null -} -BranchChunk.prototype = { - chunkSize: function() { return this.size }, - removeInner: function(at, n) { + chunkSize() { return this.size } + + removeInner(at, n) { this.size -= n for (let i = 0; i < this.children.length; ++i) { let child = this.children[i], sz = child.chunkSize() @@ -93,11 +98,13 @@ BranchChunk.prototype = { this.children = [new LeafChunk(lines)] this.children[0].parent = this } - }, - collapse: function(lines) { + } + + collapse(lines) { for (let i = 0; i < this.children.length; ++i) this.children[i].collapse(lines) - }, - insertInner: function(at, lines, height) { + } + + insertInner(at, lines, height) { this.size += lines.length this.height += height for (let i = 0; i < this.children.length; ++i) { @@ -121,9 +128,10 @@ BranchChunk.prototype = { } at -= sz } - }, + } + // When a node has grown, check whether it should be split. - maybeSpill: function() { + maybeSpill() { if (this.children.length <= 10) return let me = this do { @@ -143,8 +151,9 @@ BranchChunk.prototype = { sibling.parent = me.parent } while (me.children.length > 10) me.parent.maybeSpill() - }, - iterN: function(at, n, op) { + } + + iterN(at, n, op) { for (let i = 0; i < this.children.length; ++i) { let child = this.children[i], sz = child.chunkSize() if (at < sz) { From eba994a1385012ca47f36ac9d33e4044fb42d50d Mon Sep 17 00:00:00 2001 From: Marijn Haverbeke Date: Thu, 2 Feb 2017 14:26:03 +0100 Subject: [PATCH 0661/1790] Use an ES6 class for StringStream --- src/util/StringStream.js | 62 ++++++++++++++++++++-------------------- 1 file changed, 31 insertions(+), 31 deletions(-) diff --git a/src/util/StringStream.js b/src/util/StringStream.js index 92dbc690..0f7af07c 100644 --- a/src/util/StringStream.js +++ b/src/util/StringStream.js @@ -5,57 +5,57 @@ import { countColumn } from "./misc" // Fed to the mode parsers, provides helper functions to make // parsers more succinct. -let StringStream = function(string, tabSize) { - this.pos = this.start = 0 - this.string = string - this.tabSize = tabSize || 8 - this.lastColumnPos = this.lastColumnValue = 0 - this.lineStart = 0 -} +class StringStream { + constructor(string, tabSize) { + this.pos = this.start = 0 + this.string = string + this.tabSize = tabSize || 8 + this.lastColumnPos = this.lastColumnValue = 0 + this.lineStart = 0 + } -StringStream.prototype = { - eol: function() {return this.pos >= this.string.length}, - sol: function() {return this.pos == this.lineStart}, - peek: function() {return this.string.charAt(this.pos) || undefined}, - next: function() { + eol() {return this.pos >= this.string.length} + sol() {return this.pos == this.lineStart} + peek() {return this.string.charAt(this.pos) || undefined} + next() { if (this.pos < this.string.length) return this.string.charAt(this.pos++) - }, - eat: function(match) { + } + eat(match) { let ch = this.string.charAt(this.pos) let ok if (typeof match == "string") ok = ch == match else ok = ch && (match.test ? match.test(ch) : match(ch)) if (ok) {++this.pos; return ch} - }, - eatWhile: function(match) { + } + eatWhile(match) { let start = this.pos while (this.eat(match)){} return this.pos > start - }, - eatSpace: function() { + } + eatSpace() { let 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) { + } + skipToEnd() {this.pos = this.string.length} + skipTo(ch) { let found = this.string.indexOf(ch, this.pos) if (found > -1) {this.pos = found; return true} - }, - backUp: function(n) {this.pos -= n}, - column: function() { + } + backUp(n) {this.pos -= n} + column() { 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 - (this.lineStart ? countColumn(this.string, this.lineStart, this.tabSize) : 0) - }, - indentation: function() { + } + indentation() { return countColumn(this.string, null, this.tabSize) - (this.lineStart ? countColumn(this.string, this.lineStart, this.tabSize) : 0) - }, - match: function(pattern, consume, caseInsensitive) { + } + match(pattern, consume, caseInsensitive) { if (typeof pattern == "string") { let cased = str => caseInsensitive ? str.toLowerCase() : str let substr = this.string.substr(this.pos, pattern.length) @@ -69,9 +69,9 @@ StringStream.prototype = { if (match && consume !== false) this.pos += match[0].length return match } - }, - current: function(){return this.string.slice(this.start, this.pos)}, - hideFirstChars: function(n, inner) { + } + current(){return this.string.slice(this.start, this.pos)} + hideFirstChars(n, inner) { this.lineStart += n try { return inner() } finally { this.lineStart -= n } From 6ce369c7be197080112693bb1f0e9db1ea600556 Mon Sep 17 00:00:00 2001 From: Marijn Haverbeke Date: Thu, 2 Feb 2017 14:31:17 +0100 Subject: [PATCH 0662/1790] Use ES6 classes for text markers and line objects --- src/line/line_data.js | 13 ++- src/model/line_widget.js | 61 +++++----- src/model/mark_text.js | 234 ++++++++++++++++++++------------------- src/util/misc.js | 10 +- 4 files changed, 166 insertions(+), 152 deletions(-) diff --git a/src/line/line_data.js b/src/line/line_data.js index 7583c342..552c56b1 100644 --- a/src/line/line_data.js +++ b/src/line/line_data.js @@ -13,13 +13,16 @@ import { getLine, lineNo, updateLineHeight } from "./utils_line" // Line objects. These hold state related to a line, including // highlighting info (the styles array). -export function Line(text, markedSpans, estimateHeight) { - this.text = text - attachMarkedSpans(this, markedSpans) - this.height = estimateHeight ? estimateHeight(this) : 1 +export class Line { + constructor(text, markedSpans, estimateHeight) { + this.text = text + attachMarkedSpans(this, markedSpans) + this.height = estimateHeight ? estimateHeight(this) : 1 + } + + lineNo() { return lineNo(this) } } eventMixin(Line) -Line.prototype.lineNo = function() { return lineNo(this) } // Change the content (text, markers) of a line. Automatically // invalidates cached information and tries to re-estimate the diff --git a/src/model/line_widget.js b/src/model/line_widget.js index e832e8c4..5854e43d 100644 --- a/src/model/line_widget.js +++ b/src/model/line_widget.js @@ -9,11 +9,38 @@ import { eventMixin } from "../util/event" // Line widgets are block elements displayed above or below a line. -export function LineWidget(doc, node, options) { - if (options) for (let opt in options) if (options.hasOwnProperty(opt)) - this[opt] = options[opt] - this.doc = doc - this.node = node +export class LineWidget { + constructor(doc, node, options) { + if (options) for (let opt in options) if (options.hasOwnProperty(opt)) + this[opt] = options[opt] + this.doc = doc + this.node = node + } + + clear() { + let cm = this.doc.cm, ws = this.line.widgets, line = this.line, no = lineNo(line) + if (no == null || !ws) return + for (let i = 0; i < ws.length; ++i) if (ws[i] == this) ws.splice(i--, 1) + if (!ws.length) line.widgets = null + let height = widgetHeight(this) + updateLineHeight(line, Math.max(0, line.height - height)) + if (cm) runInOp(cm, () => { + adjustScrollWhenAboveVisible(cm, line, -height) + regLineChange(cm, no, "widget") + }) + } + + changed() { + let oldH = this.height, cm = this.doc.cm, line = this.line + this.height = null + let diff = widgetHeight(this) - oldH + if (!diff) return + updateLineHeight(line, line.height + diff) + if (cm) runInOp(cm, () => { + cm.curOp.forceUpdate = true + adjustScrollWhenAboveVisible(cm, line, diff) + }) + } } eventMixin(LineWidget) @@ -22,30 +49,6 @@ function adjustScrollWhenAboveVisible(cm, line, diff) { addToScrollPos(cm, null, diff) } -LineWidget.prototype.clear = function() { - let cm = this.doc.cm, ws = this.line.widgets, line = this.line, no = lineNo(line) - if (no == null || !ws) return - for (let i = 0; i < ws.length; ++i) if (ws[i] == this) ws.splice(i--, 1) - if (!ws.length) line.widgets = null - let height = widgetHeight(this) - updateLineHeight(line, Math.max(0, line.height - height)) - if (cm) runInOp(cm, () => { - adjustScrollWhenAboveVisible(cm, line, -height) - regLineChange(cm, no, "widget") - }) -} -LineWidget.prototype.changed = function() { - let oldH = this.height, cm = this.doc.cm, line = this.line - this.height = null - let diff = widgetHeight(this) - oldH - if (!diff) return - updateLineHeight(line, line.height + diff) - if (cm) runInOp(cm, () => { - cm.curOp.forceUpdate = true - adjustScrollWhenAboveVisible(cm, line, diff) - }) -} - export function addLineWidget(doc, handle, node, options) { let widget = new LineWidget(doc, node, options) let cm = doc.cm diff --git a/src/model/mark_text.js b/src/model/mark_text.js index 15ec2849..2e8ecf77 100644 --- a/src/model/mark_text.js +++ b/src/model/mark_text.js @@ -32,118 +32,121 @@ import { reCheckSelection } from "./selection_updates" // when they overlap (they may nest, but not partially overlap). let nextMarkerId = 0 -export function TextMarker(doc, type) { - this.lines = [] - this.type = type - this.doc = doc - this.id = ++nextMarkerId -} -eventMixin(TextMarker) - -// Clear the marker. -TextMarker.prototype.clear = function() { - if (this.explicitlyCleared) return - let cm = this.doc.cm, withOp = cm && !cm.curOp - if (withOp) startOperation(cm) - if (hasHandler(this, "clear")) { - let found = this.find() - if (found) signalLater(this, "clear", found.from, found.to) +export class TextMarker { + constructor(doc, type) { + this.lines = [] + this.type = type + this.doc = doc + this.id = ++nextMarkerId } - let min = null, max = null - for (let i = 0; i < this.lines.length; ++i) { - let line = this.lines[i] - let span = getMarkedSpanFor(line.markedSpans, this) - if (cm && !this.collapsed) regLineChange(cm, lineNo(line), "text") - else if (cm) { - if (span.to != null) max = lineNo(line) - if (span.from != null) min = lineNo(line) + + // Clear the marker. + clear() { + if (this.explicitlyCleared) return + let cm = this.doc.cm, withOp = cm && !cm.curOp + if (withOp) startOperation(cm) + if (hasHandler(this, "clear")) { + let found = this.find() + if (found) signalLater(this, "clear", found.from, found.to) } - line.markedSpans = removeMarkedSpan(line.markedSpans, span) - if (span.from == null && this.collapsed && !lineIsHidden(this.doc, line) && cm) - updateLineHeight(line, textHeight(cm.display)) + let min = null, max = null + for (let i = 0; i < this.lines.length; ++i) { + let line = this.lines[i] + let span = getMarkedSpanFor(line.markedSpans, this) + if (cm && !this.collapsed) regLineChange(cm, lineNo(line), "text") + else if (cm) { + if (span.to != null) max = lineNo(line) + if (span.from != null) min = lineNo(line) + } + line.markedSpans = removeMarkedSpan(line.markedSpans, span) + if (span.from == null && this.collapsed && !lineIsHidden(this.doc, line) && cm) + updateLineHeight(line, textHeight(cm.display)) + } + if (cm && this.collapsed && !cm.options.lineWrapping) for (let i = 0; i < this.lines.length; ++i) { + let visual = visualLine(this.lines[i]), len = lineLength(visual) + if (len > cm.display.maxLineLength) { + cm.display.maxLine = visual + cm.display.maxLineLength = len + cm.display.maxLineChanged = true + } + } + + if (min != null && cm && this.collapsed) regChange(cm, min, max + 1) + this.lines.length = 0 + this.explicitlyCleared = true + if (this.atomic && this.doc.cantEdit) { + this.doc.cantEdit = false + if (cm) reCheckSelection(cm.doc) + } + if (cm) signalLater(cm, "markerCleared", cm, this) + if (withOp) endOperation(cm) + if (this.parent) this.parent.clear() } - if (cm && this.collapsed && !cm.options.lineWrapping) for (let i = 0; i < this.lines.length; ++i) { - let visual = visualLine(this.lines[i]), len = lineLength(visual) - if (len > cm.display.maxLineLength) { - cm.display.maxLine = visual - cm.display.maxLineLength = len - cm.display.maxLineChanged = true + + // Find the position of the marker in the document. Returns a {from, + // to} object by default. Side can be passed to get a specific side + // -- 0 (both), -1 (left), or 1 (right). When lineObj is true, the + // Pos objects returned contain a line object, rather than a line + // number (used to prevent looking up the same line twice). + find(side, lineObj) { + if (side == null && this.type == "bookmark") side = 1 + let from, to + for (let i = 0; i < this.lines.length; ++i) { + let line = this.lines[i] + let span = getMarkedSpanFor(line.markedSpans, this) + if (span.from != null) { + from = Pos(lineObj ? line : lineNo(line), span.from) + if (side == -1) return from + } + if (span.to != null) { + to = Pos(lineObj ? line : lineNo(line), span.to) + if (side == 1) return to + } } + return from && {from: from, to: to} } - if (min != null && cm && this.collapsed) regChange(cm, min, max + 1) - this.lines.length = 0 - this.explicitlyCleared = true - if (this.atomic && this.doc.cantEdit) { - this.doc.cantEdit = false - if (cm) reCheckSelection(cm.doc) + // Signals that the marker's widget changed, and surrounding layout + // should be recomputed. + changed() { + let pos = this.find(-1, true), widget = this, cm = this.doc.cm + if (!pos || !cm) return + runInOp(cm, () => { + let line = pos.line, lineN = lineNo(pos.line) + let view = findViewForLine(cm, lineN) + if (view) { + clearLineMeasurementCacheFor(view) + cm.curOp.selectionChanged = cm.curOp.forceUpdate = true + } + cm.curOp.updateMaxLine = true + if (!lineIsHidden(widget.doc, line) && widget.height != null) { + let oldHeight = widget.height + widget.height = null + let dHeight = widgetHeight(widget) - oldHeight + if (dHeight) + updateLineHeight(line, line.height + dHeight) + } + }) } - if (cm) signalLater(cm, "markerCleared", cm, this) - if (withOp) endOperation(cm) - if (this.parent) this.parent.clear() -} -// Find the position of the marker in the document. Returns a {from, -// to} object by default. Side can be passed to get a specific side -// -- 0 (both), -1 (left), or 1 (right). When lineObj is true, the -// Pos objects returned contain a line object, rather than a line -// number (used to prevent looking up the same line twice). -TextMarker.prototype.find = function(side, lineObj) { - if (side == null && this.type == "bookmark") side = 1 - let from, to - for (let i = 0; i < this.lines.length; ++i) { - let line = this.lines[i] - let span = getMarkedSpanFor(line.markedSpans, this) - if (span.from != null) { - from = Pos(lineObj ? line : lineNo(line), span.from) - if (side == -1) return from - } - if (span.to != null) { - to = Pos(lineObj ? line : lineNo(line), span.to) - if (side == 1) return to + attachLine(line) { + if (!this.lines.length && this.doc.cm) { + let op = this.doc.cm.curOp + if (!op.maybeHiddenMarkers || indexOf(op.maybeHiddenMarkers, this) == -1) + (op.maybeUnhiddenMarkers || (op.maybeUnhiddenMarkers = [])).push(this) } + this.lines.push(line) } - return from && {from: from, to: to} -} -// Signals that the marker's widget changed, and surrounding layout -// should be recomputed. -TextMarker.prototype.changed = function() { - let pos = this.find(-1, true), widget = this, cm = this.doc.cm - if (!pos || !cm) return - runInOp(cm, () => { - let line = pos.line, lineN = lineNo(pos.line) - let view = findViewForLine(cm, lineN) - if (view) { - clearLineMeasurementCacheFor(view) - cm.curOp.selectionChanged = cm.curOp.forceUpdate = true - } - cm.curOp.updateMaxLine = true - if (!lineIsHidden(widget.doc, line) && widget.height != null) { - let oldHeight = widget.height - widget.height = null - let dHeight = widgetHeight(widget) - oldHeight - if (dHeight) - updateLineHeight(line, line.height + dHeight) + detachLine(line) { + this.lines.splice(indexOf(this.lines, line), 1) + if (!this.lines.length && this.doc.cm) { + let op = this.doc.cm.curOp + ;(op.maybeHiddenMarkers || (op.maybeHiddenMarkers = [])).push(this) } - }) -} - -TextMarker.prototype.attachLine = function(line) { - if (!this.lines.length && this.doc.cm) { - let 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) { - let op = this.doc.cm.curOp - ;(op.maybeHiddenMarkers || (op.maybeHiddenMarkers = [])).push(this) } } +eventMixin(TextMarker) // Create a marker, wire it up to the right lines, and export function markText(doc, from, to, options, type) { @@ -221,24 +224,27 @@ export function markText(doc, from, to, options, type) { // A shared marker spans multiple linked documents. It is // implemented as a meta-marker-object controlling multiple normal // markers. -export function SharedTextMarker(markers, primary) { - this.markers = markers - this.primary = primary - for (let i = 0; i < markers.length; ++i) - markers[i].parent = this -} -eventMixin(SharedTextMarker) +export class SharedTextMarker { + constructor(markers, primary) { + this.markers = markers + this.primary = primary + for (let i = 0; i < markers.length; ++i) + markers[i].parent = this + } -SharedTextMarker.prototype.clear = function() { - if (this.explicitlyCleared) return - this.explicitlyCleared = true - for (let i = 0; i < this.markers.length; ++i) - this.markers[i].clear() - signalLater(this, "clear") -} -SharedTextMarker.prototype.find = function(side, lineObj) { - return this.primary.find(side, lineObj) + clear() { + if (this.explicitlyCleared) return + this.explicitlyCleared = true + for (let i = 0; i < this.markers.length; ++i) + this.markers[i].clear() + signalLater(this, "clear") + } + + find(side, lineObj) { + return this.primary.find(side, lineObj) + } } +eventMixin(SharedTextMarker) function markTextShared(doc, from, to, options, type) { options = copyObj(options) diff --git a/src/util/misc.js b/src/util/misc.js index dead8a6d..097eb050 100644 --- a/src/util/misc.js +++ b/src/util/misc.js @@ -28,10 +28,12 @@ export function countColumn(string, end, tabSize, startIndex, startValue) { } } -export function Delayed() {this.id = null} -Delayed.prototype.set = function(ms, f) { - clearTimeout(this.id) - this.id = setTimeout(f, ms) +export class Delayed { + constructor() {this.id = null} + set(ms, f) { + clearTimeout(this.id) + this.id = setTimeout(f, ms) + } } export function indexOf(array, elt) { From 223a43cd60968fe468835b61166a196c70607020 Mon Sep 17 00:00:00 2001 From: Marijn Haverbeke Date: Thu, 2 Feb 2017 22:45:45 +0100 Subject: [PATCH 0663/1790] [continuelist addon] Don't clear quote characters on enter Closes #4526 --- 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 6574ea33..5a845907 100644 --- a/addon/edit/continuelist.js +++ b/addon/edit/continuelist.js @@ -30,7 +30,7 @@ return; } if (emptyListRE.test(line)) { - cm.replaceRange("", { + if (!/>\s*$/.test(line)) cm.replaceRange("", { line: pos.line, ch: 0 }, { line: pos.line, ch: pos.ch + 1 From 234391388748eae5b00b941ed696c7763792d9a9 Mon Sep 17 00:00:00 2001 From: Adrian Heine Date: Fri, 3 Feb 2017 11:00:03 +0100 Subject: [PATCH 0664/1790] Remove unused variable and duplicate white space --- src/edit/methods.js | 2 +- test/test.js | 1 - 2 files changed, 1 insertion(+), 2 deletions(-) diff --git a/src/edit/methods.js b/src/edit/methods.js index 2883909e..e58a3b25 100644 --- a/src/edit/methods.js +++ b/src/edit/methods.js @@ -480,7 +480,7 @@ function findPosH(doc, pos, dir, unit, visually) { next = moveVisually(doc.cm, lineObj, pos, dir) } else { let ch = moveLogically(lineObj, pos, dir) - next = ch == null ? null : new Pos(pos.line, ch, dir < 0 ? "after" : "before") + next = ch == null ? null : new Pos(pos.line, ch, dir < 0 ? "after" : "before") } if (next == null) { if (!boundToLine && findNextLine()) diff --git a/test/test.js b/test/test.js index 37abdd3f..ab23757d 100644 --- a/test/test.js +++ b/test/test.js @@ -34,7 +34,6 @@ var opera = /Opera\/\./.test(navigator.userAgent); 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); -var webkit = /WebKit\//.test(navigator.userAgent); namespace = "core_"; From ff84b9ee73b75851801bc4004b75e35fea6291cb Mon Sep 17 00:00:00 2001 From: Adrian Heine Date: Mon, 26 Dec 2016 10:59:03 +0100 Subject: [PATCH 0665/1790] Move moveLogically to input/movement --- src/edit/methods.js | 6 ++-- src/input/movement.js | 38 ++++++++++++++----------- src/measurement/position_measurement.js | 4 +-- src/util/bidi.js | 28 ++---------------- src/util/misc.js | 6 ++++ 5 files changed, 34 insertions(+), 48 deletions(-) diff --git a/src/edit/methods.js b/src/edit/methods.js index e58a3b25..6d69cfc2 100644 --- a/src/edit/methods.js +++ b/src/edit/methods.js @@ -8,7 +8,7 @@ import { indentLine } from "../input/indent" import { triggerElectric } from "../input/input" import { onKeyDown, onKeyPress, onKeyUp } from "./key_events" import { getKeyMap } from "../input/keymap" -import { endOfLine, moveVisually } from "../input/movement" +import { endOfLine, moveLogically, moveVisually } from "../input/movement" import { methodOp, operation, runInOp } from "../display/operations" import { clipLine, clipPos, equalCursorPos, Pos } from "../line/pos" import { charCoords, charWidth, clearCaches, clearLineMeasurementCache, coordsChar, cursorCoords, displayHeight, displayWidth, estimateLineHeights, fromCoordSystem, intoCoordSystem, scrollGap, textHeight } from "../measurement/position_measurement" @@ -17,7 +17,6 @@ import { replaceOneSelection, skipAtomic } from "../model/selection_updates" import { addToScrollPos, calculateScrollPos, ensureCursorVisible, resolveScrollToPos, scrollIntoView } from "../display/scrolling" import { heightAtLine } from "../line/spans" import { updateGutterSpace } from "../display/update_display" -import { moveLogically } from "../util/bidi" import { indexOf, insertSorted, isWordChar, sel_dontScroll, sel_move } from "../util/misc" import { signalLater } from "../util/operation_group" import { getLine, isLine, lineAtHeight } from "../line/utils_line" @@ -479,8 +478,7 @@ function findPosH(doc, pos, dir, unit, visually) { if (visually) { next = moveVisually(doc.cm, lineObj, pos, dir) } else { - let ch = moveLogically(lineObj, pos, dir) - next = ch == null ? null : new Pos(pos.line, ch, dir < 0 ? "after" : "before") + next = moveLogically(lineObj, pos, dir) } if (next == null) { if (!boundToLine && findNextLine()) diff --git a/src/input/movement.js b/src/input/movement.js index 80e8f258..ebb1bdf2 100644 --- a/src/input/movement.js +++ b/src/input/movement.js @@ -1,16 +1,26 @@ import { Pos } from "../line/pos" import { prepareMeasureForLine, measureCharPrepared } from "../measurement/position_measurement" -import { bidiLeft, bidiRight, getBidiPartAt, getOrder, lineLeft, lineRight, moveLogically } from "../util/bidi" -import { findFirst } from "../util/misc" +import { getBidiPartAt, getOrder } from "../util/bidi" +import { findFirst, lst, skipExtendingChars } from "../util/misc" + +function moveCharLogically(line, ch, dir) { + let target = skipExtendingChars(line.text, ch + dir, dir) + return target < 0 || target > line.text.length ? null : target +} + +export function moveLogically(line, start, dir) { + let ch = moveCharLogically(line, start.ch, dir) + return ch == null ? null : new Pos(start.line, ch, dir < 0 ? "after" : "before") +} export function endOfLine(visually, cm, lineObj, lineNo, dir) { - let ch if (visually) { let order = getOrder(lineObj) if (order) { - let i = dir < 0 ? order.length - 1 : 0 - let part = order[i] - let sticky = (dir < 0) != (part.level == 1) ? "before" : "after" + let part = dir < 0 ? lst(order) : order[0] + let moveInStorageOrder = (dir < 0) == (part.level == 1) + let sticky = moveInStorageOrder ? "after" : "before" + let ch // With a wrapped rtl chunk (possibly spanning multiple bidi parts), // it could be that the last bidi part is not on the last visual line, // since visual lines contain content order-consecutive chunks. @@ -22,15 +32,12 @@ export function endOfLine(visually, cm, lineObj, lineNo, dir) { ch = dir < 0 ? lineObj.text.length - 1 : 0 let targetTop = measureCharPrepared(cm, prep, ch).top ch = findFirst(ch => measureCharPrepared(cm, prep, ch).top == targetTop, (dir < 0) == (part.level == 1) ? part.from : part.to - 1, ch) - if (sticky == "before") ch = moveLogically(lineObj, new Pos(lineNo, ch, sticky), 1, true) - } else ch = (dir < 0 ? bidiRight : bidiLeft)(part) + if (sticky == "before") ch = moveCharLogically(lineObj, ch, 1, true) + } else ch = dir < 0 ? part.to : part.from return new Pos(lineNo, ch, sticky) } } - let sticky = dir < 0 ? "before" : "after" - if (visually) ch = (dir < 0 ? lineRight : lineLeft)(lineObj) - else ch = dir < 0 ? lineObj.text.length : 0 - return new Pos(lineNo, ch, sticky) + return new Pos(lineNo, dir < 0 ? lineObj.text.length : 0, dir < 0 ? "before" : "after") } function getVisualLineAround(cm, line, target) { @@ -44,10 +51,8 @@ function getVisualLineAround(cm, line, target) { } export function moveVisually(cm, line, start, dir) { - let mkPos = (ch, sticky) => ch == null ? null : new Pos(start.line, ch, sticky) - let mv = (pos, dir) => moveLogically(line, pos instanceof Pos ? pos : new Pos(start.line, pos), dir) let bidi = getOrder(line) - if (!bidi) return mkPos(mv(start, dir), dir < 0 ? "after" : "before") + if (!bidi) return moveLogically(line, start, dir) if (start.ch >= line.text.length) { start.ch = line.text.length start.sticky = "before" @@ -59,9 +64,10 @@ export function moveVisually(cm, line, start, dir) { if (part.level % 2 == 0 && (dir > 0 ? part.to > start.ch : part.from < start.ch)) { // Case 1: We move within an ltr part. Even with wrapped lines, // nothing interesting happens. - return mkPos(mv(start, dir), dir < 0 ? "after" : "before") + return moveLogically(line, start, dir) } + let mv = (pos, dir) => moveCharLogically(line, pos instanceof Pos ? pos.ch : pos, dir) let getVisualLine = ch => getVisualLineAround(cm, line, ch) let visualLine = getVisualLine(start.sticky == "before" ? mv(start, -1) : start.ch) diff --git a/src/measurement/position_measurement.js b/src/measurement/position_measurement.js index cd7bbfe3..4d2754b9 100644 --- a/src/measurement/position_measurement.js +++ b/src/measurement/position_measurement.js @@ -8,7 +8,7 @@ import { ie, ie_version } from "../util/browser" import { elt, removeChildren, range, removeChildrenAndAdd } from "../util/dom" import { e_target } from "../util/event" import { hasBadZoomedRects } from "../util/feature_detection" -import { countColumn, findFirst, isExtendingChar, scrollerGap } from "../util/misc" +import { countColumn, findFirst, isExtendingChar, scrollerGap, skipExtendingChars } from "../util/misc" import { updateLineForChanges } from "../display/update_line" import { widgetHeight } from "./widgets" @@ -471,7 +471,7 @@ function coordsCharInner(cm, lineObj, lineNo, x, y) { else if (box.right < x) return false else return (x - box.left < box.right - x) }, begin, end) - while (isExtendingChar(lineObj.text.charAt(ch))) ++ch + ch = skipExtendingChars(lineObj.text, ch, 1) pos = new Pos(lineNo, ch, ch == end ? "before" : "after") } let coords = cursorCoords(cm, pos, "line", lineObj, preparedMeasure) diff --git a/src/util/bidi.js b/src/util/bidi.js index f10ee707..acc1ef8e 100644 --- a/src/util/bidi.js +++ b/src/util/bidi.js @@ -1,4 +1,4 @@ -import { isExtendingChar, lst } from "./misc" +import { lst } from "./misc" // BIDI HELPERS @@ -15,19 +15,6 @@ export function iterateBidiSections(order, from, to, f) { if (!found) f(from, to, "ltr") } -export function bidiLeft(part) { return part.level % 2 ? part.to : part.from } -export function bidiRight(part) { return part.level % 2 ? part.from : part.to } - -function lineAt(line, dir) { - let order = getOrder(line) - if (!order) return dir == -1 ? line.text.length : 0 - let pos = dir == -1 ? order.length - 1 : 0 - return (dir == -1 ? bidiRight : bidiLeft)(order[pos]) -} - -export function lineLeft(line) { return lineAt(line, 1) } -export function lineRight(line) { return lineAt(line, -1) } - export let bidiOther = null export function getBidiPartAt(order, ch, sticky) { let found @@ -47,17 +34,6 @@ export function getBidiPartAt(order, ch, sticky) { return found != null ? found : bidiOther } -function moveInLine(line, pos, dir) { - do pos += dir - while (pos > 0 && isExtendingChar(line.text.charAt(pos))) - return pos -} - -export function moveLogically(line, start, dir) { - let target = moveInLine(line, start.ch, dir) - return target < 0 || target > line.text.length ? null : target -} - // Bidirectional ordering algorithm // See http://unicode.org/reports/tr9/tr9-13.html for the algorithm // that this (partially) implements. @@ -81,7 +57,7 @@ export function moveLogically(line, start, dir) { // 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. -export let bidiOrdering = (function() { +let bidiOrdering = (function() { // Character types for codepoints 0 to 0xff let lowTypes = "bbbbbbbbbtstwsbbbbbbbbbbbbbbssstwNN%%%NNNNNN,N,N1111111111NNNNNNNLLLLLLLLLLLLLLLLLLLLLLLLLLNNNNNNLLLLLLLLLLLLLLLLLLLLLLLLLLNNNNbbbbbbsbbbbbbbbbbbbbbbbbbbbbbbbbb,N%%%%NNNNLNNNNN%%11NLNNN1LNNNNNLLLLLLLLLLLLLLLLLLLLLLLNLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLN" // Character types for codepoints 0x600 to 0x6f9 diff --git a/src/util/misc.js b/src/util/misc.js index 097eb050..2ad1d0ed 100644 --- a/src/util/misc.js +++ b/src/util/misc.js @@ -127,6 +127,12 @@ export function isEmpty(obj) { let extendingChars = /[\u0300-\u036f\u0483-\u0489\u0591-\u05bd\u05bf\u05c1\u05c2\u05c4\u05c5\u05c7\u0610-\u061a\u064b-\u065e\u0670\u06d6-\u06dc\u06de-\u06e4\u06e7\u06e8\u06ea-\u06ed\u0711\u0730-\u074a\u07a6-\u07b0\u07eb-\u07f3\u0816-\u0819\u081b-\u0823\u0825-\u0827\u0829-\u082d\u0900-\u0902\u093c\u0941-\u0948\u094d\u0951-\u0955\u0962\u0963\u0981\u09bc\u09be\u09c1-\u09c4\u09cd\u09d7\u09e2\u09e3\u0a01\u0a02\u0a3c\u0a41\u0a42\u0a47\u0a48\u0a4b-\u0a4d\u0a51\u0a70\u0a71\u0a75\u0a81\u0a82\u0abc\u0ac1-\u0ac5\u0ac7\u0ac8\u0acd\u0ae2\u0ae3\u0b01\u0b3c\u0b3e\u0b3f\u0b41-\u0b44\u0b4d\u0b56\u0b57\u0b62\u0b63\u0b82\u0bbe\u0bc0\u0bcd\u0bd7\u0c3e-\u0c40\u0c46-\u0c48\u0c4a-\u0c4d\u0c55\u0c56\u0c62\u0c63\u0cbc\u0cbf\u0cc2\u0cc6\u0ccc\u0ccd\u0cd5\u0cd6\u0ce2\u0ce3\u0d3e\u0d41-\u0d44\u0d4d\u0d57\u0d62\u0d63\u0dca\u0dcf\u0dd2-\u0dd4\u0dd6\u0ddf\u0e31\u0e34-\u0e3a\u0e47-\u0e4e\u0eb1\u0eb4-\u0eb9\u0ebb\u0ebc\u0ec8-\u0ecd\u0f18\u0f19\u0f35\u0f37\u0f39\u0f71-\u0f7e\u0f80-\u0f84\u0f86\u0f87\u0f90-\u0f97\u0f99-\u0fbc\u0fc6\u102d-\u1030\u1032-\u1037\u1039\u103a\u103d\u103e\u1058\u1059\u105e-\u1060\u1071-\u1074\u1082\u1085\u1086\u108d\u109d\u135f\u1712-\u1714\u1732-\u1734\u1752\u1753\u1772\u1773\u17b7-\u17bd\u17c6\u17c9-\u17d3\u17dd\u180b-\u180d\u18a9\u1920-\u1922\u1927\u1928\u1932\u1939-\u193b\u1a17\u1a18\u1a56\u1a58-\u1a5e\u1a60\u1a62\u1a65-\u1a6c\u1a73-\u1a7c\u1a7f\u1b00-\u1b03\u1b34\u1b36-\u1b3a\u1b3c\u1b42\u1b6b-\u1b73\u1b80\u1b81\u1ba2-\u1ba5\u1ba8\u1ba9\u1c2c-\u1c33\u1c36\u1c37\u1cd0-\u1cd2\u1cd4-\u1ce0\u1ce2-\u1ce8\u1ced\u1dc0-\u1de6\u1dfd-\u1dff\u200c\u200d\u20d0-\u20f0\u2cef-\u2cf1\u2de0-\u2dff\u302a-\u302f\u3099\u309a\ua66f-\ua672\ua67c\ua67d\ua6f0\ua6f1\ua802\ua806\ua80b\ua825\ua826\ua8c4\ua8e0-\ua8f1\ua926-\ua92d\ua947-\ua951\ua980-\ua982\ua9b3\ua9b6-\ua9b9\ua9bc\uaa29-\uaa2e\uaa31\uaa32\uaa35\uaa36\uaa43\uaa4c\uaab0\uaab2-\uaab4\uaab7\uaab8\uaabe\uaabf\uaac1\uabe5\uabe8\uabed\udc00-\udfff\ufb1e\ufe00-\ufe0f\ufe20-\ufe26\uff9e\uff9f]/ export function isExtendingChar(ch) { return ch.charCodeAt(0) >= 768 && extendingChars.test(ch) } +// Returns a number from the range [`0`; `str.length`] unless `pos` is outside that range. +export function skipExtendingChars(str, pos, dir) { + while ((dir < 0 ? pos > 0 : pos < str.length) && isExtendingChar(str.charAt(pos))) pos += dir + return pos +} + // Returns the value from the range [`from`; `to`] that satisfies // `pred` and is closest to `from`. Assumes that at least `to` satisfies `pred`. export function findFirst(pred, from, to) { From f45dc108c79a21e34331b4369878e131e9b75049 Mon Sep 17 00:00:00 2001 From: Fabien Dubosson Date: Sat, 4 Feb 2017 00:12:23 +0100 Subject: [PATCH 0666/1790] [vim] Allow to delete default bindings of the vim keymap through the API The current situation prevents to remove bindings from the default keymap in vim mode. It is sometimes required to be able to delete them in order to do some custom mapping. Example: 1. The `h` binding is defined in the default keymap for moving left. 2. The user want to set the custom binding `h` to behave like `r`. It can do so with: CodeMirror.Vim.mapCommand('h', 'action', 'replace', {}, { isEdit: true, context: 'normal' }); 3. When the user press the `h` key, the code searches for all the keybindings matching it [1]. 4. Two bindings are found: - The default binding `h` for moving left. This is a FULL match. - The user-defined `h`. This is only a PARTIAL match as it is expecting another character. 5. The code is preferring FULL matches over PARTIAL matches whenever they are in competition [2]. The code then selects the move left action. 6. The way to let the user-defined binding take over is to let the user delete the default binding: CodeMirror.Vim.unmap('h'); but this is not possible because it is not allowed to delete bindings from the default keymap [3]. This commit remove such differentiation by treating all bindings in the same way, letting the responsibility to the user to not mess with the bindings he needs. [1] https://github.com/codemirror/CodeMirror/blob/ff84b9ee73b75851801bc4004b75e35fea6291cb/keymap/vim.js#L1098 [2] https://github.com/codemirror/CodeMirror/blob/ff84b9ee73b75851801bc4004b75e35fea6291cb/keymap/vim.js#L1099-L1103 [3] https://github.com/codemirror/CodeMirror/blob/ff84b9ee73b75851801bc4004b75e35fea6291cb/keymap/vim.js#L4182 Signed-off-by: Fabien Dubosson --- keymap/vim.js | 10 ++++------ 1 file changed, 4 insertions(+), 6 deletions(-) diff --git a/keymap/vim.js b/keymap/vim.js index 34570bb8..c6608bf6 100644 --- a/keymap/vim.js +++ b/keymap/vim.js @@ -4147,8 +4147,8 @@ var mapping = { keys: lhs, type: 'keyToEx', - exArgs: { input: rhs.substring(1) }, - user: true}; + exArgs: { input: rhs.substring(1) } + }; if (ctx) { mapping.context = ctx; } defaultKeymap.unshift(mapping); } else { @@ -4156,8 +4156,7 @@ var mapping = { keys: lhs, type: 'keyToKey', - toKeys: rhs, - user: true + toKeys: rhs }; if (ctx) { mapping.context = ctx; } defaultKeymap.unshift(mapping); @@ -4178,8 +4177,7 @@ var keys = lhs; for (var i = 0; i < defaultKeymap.length; i++) { if (keys == defaultKeymap[i].keys - && defaultKeymap[i].context === ctx - && defaultKeymap[i].user) { + && defaultKeymap[i].context === ctx) { defaultKeymap.splice(i, 1); return; } From f2c63b670e6db4fe0b7f87699f35116df7696718 Mon Sep 17 00:00:00 2001 From: Kevin Muret Date: Sat, 4 Feb 2017 17:02:16 +0100 Subject: [PATCH 0667/1790] [vim bindings] regexp support (sort) fixed cursor pos (ex/search) --- keymap/vim.js | 56 +++++++++++++++++++++++++++++++++--------------- test/vim_test.js | 40 +++++++++++++++++++++++++--------- 2 files changed, 69 insertions(+), 27 deletions(-) diff --git a/keymap/vim.js b/keymap/vim.js index c6608bf6..08f4374c 100644 --- a/keymap/vim.js +++ b/keymap/vim.js @@ -1245,11 +1245,13 @@ } } function onPromptKeyUp(e, query, close) { - var keyName = CodeMirror.keyName(e), up; + var keyName = CodeMirror.keyName(e), up, offset; if (keyName == 'Up' || keyName == 'Down') { up = keyName == 'Up' ? true : false; + offset = e.target ? e.target.selectionEnd : 0; query = vimGlobalState.searchHistoryController.nextMatch(query, up) || ''; close(query); + if (offset && e.target) e.target.selectionEnd = e.target.selectionStart = Math.min(offset, e.target.value.length); } else { if ( keyName != 'Left' && keyName != 'Right' && keyName != 'Ctrl' && keyName != 'Alt' && keyName != 'Shift') vimGlobalState.searchHistoryController.reset(); @@ -1281,6 +1283,8 @@ clearInputState(cm); close(); cm.focus(); + } else if (keyName == 'Up' || keyName == 'Down') { + CodeMirror.e_stop(e); } else if (keyName == 'Ctrl-U') { // Ctrl-U clears input. CodeMirror.e_stop(e); @@ -1344,7 +1348,7 @@ exCommandDispatcher.processCommand(cm, input); } function onPromptKeyDown(e, input, close) { - var keyName = CodeMirror.keyName(e), up; + var keyName = CodeMirror.keyName(e), up, offset; if (keyName == 'Esc' || keyName == 'Ctrl-C' || keyName == 'Ctrl-[' || (keyName == 'Backspace' && input == '')) { vimGlobalState.exCommandHistoryController.pushInput(input); @@ -1355,9 +1359,12 @@ cm.focus(); } if (keyName == 'Up' || keyName == 'Down') { + CodeMirror.e_stop(e); up = keyName == 'Up' ? true : false; + offset = e.target ? e.target.selectionEnd : 0; input = vimGlobalState.exCommandHistoryController.nextMatch(input, up) || ''; close(input); + if (offset && e.target) e.target.selectionEnd = e.target.selectionStart = Math.min(offset, e.target.value.length); } else if (keyName == 'Ctrl-U') { // Ctrl-U clears input. CodeMirror.e_stop(e); @@ -4308,25 +4315,27 @@ showConfirm(cm, regInfo); }, sort: function(cm, params) { - var reverse, ignoreCase, unique, number; + var reverse, ignoreCase, unique, number, pattern; function parseArgs() { if (params.argString) { var args = new CodeMirror.StringStream(params.argString); if (args.eat('!')) { reverse = true; } if (args.eol()) { return; } if (!args.eatSpace()) { return 'Invalid arguments'; } - 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; + var opts = args.match(/([dinuox]+)?\s*(\/.+\/)?\s*/); + if (!opts && !args.eol()) { return 'Invalid arguments'; } + if (opts[1]) { + ignoreCase = opts[1].indexOf('i') != -1; + unique = opts[1].indexOf('u') != -1; + var decimal = opts[1].indexOf('d') != -1 || opts[1].indexOf('n') != -1 && 1; + var hex = opts[1].indexOf('x') != -1 && 1; + var octal = opts[1].indexOf('o') != -1 && 1; if (decimal + hex + octal > 1) { return 'Invalid arguments'; } number = decimal && 'decimal' || hex && 'hex' || octal && 'octal'; } - if (args.match(/\/.*\//)) { return 'patterns not supported'; } + if (opts[2]) { + pattern = new RegExp(opts[2].substr(1, opts[2].length - 2), ignoreCase ? 'i' : ''); + } } } var err = parseArgs(); @@ -4340,14 +4349,18 @@ var curStart = Pos(lineStart, 0); var curEnd = Pos(lineEnd, lineLength(cm, lineEnd)); var text = cm.getRange(curStart, curEnd).split('\n'); - var numberRegex = (number == 'decimal') ? /(-?)([\d]+)/ : + var numberRegex = pattern ? pattern : + (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) { + if (number || pattern) { for (var i = 0; i < text.length; i++) { - if (numberRegex.exec(text[i])) { + var matchPart = pattern ? text[i].match(pattern) : null; + if (matchPart && matchPart[0] != '') { + numPart.push(matchPart); + } else if (!pattern && numberRegex.exec(text[i])) { numPart.push(text[i]); } else { textPart.push(text[i]); @@ -4366,8 +4379,17 @@ bnum = parseInt((bnum[1] + bnum[2]).toLowerCase(), radix); return anum - bnum; } - numPart.sort(compareFn); - textPart.sort(compareFn); + function comparePatternFn(a, b) { + if (reverse) { var tmp; tmp = a; a = b; b = tmp; } + if (ignoreCase) { a[0] = a[0].toLowerCase(); b[0] = b[0].toLowerCase(); } + return (a[0] < b[0]) ? -1 : 1; + } + numPart.sort(pattern ? comparePatternFn : compareFn); + if (pattern) { + for (var i = 0; i < numPart.length; i++) { + numPart[i] = numPart[i].input; + } + } else if (!number) { textPart.sort(compareFn); } text = (!reverse) ? textPart.concat(numPart) : numPart.concat(textPart); if (unique) { // Remove duplicate lines var textOld = text; diff --git a/test/vim_test.js b/test/vim_test.js index acb5ba4b..67cf8d8f 100644 --- a/test/vim_test.js +++ b/test/vim_test.js @@ -3477,24 +3477,44 @@ testVim('ex_sort_hex', function(cm, vim, helpers) { }, { 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()); + eq('.9\n.8\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()); + eq('z\ny\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_sort_patterns_not_supported', function(cm, vim, helpers) { - var notified = false; - cm.openNotification = helpers.fakeOpenNotification(function(text) { - notified = /patterns not supported/.test(text); - }); - helpers.doEx('sort /abc/'); - is(notified, 'No notification.'); -}); +testVim('ex_sort_pattern_alpha', function(cm, vim, helpers) { + helpers.doEx('sort /[a-z]/'); + eq('a3\nb2\nc1\ny\nz', cm.getValue()); +}, { value: 'z\ny\nc1\nb2\na3'}); +testVim('ex_sort_pattern_alpha_reverse', function(cm, vim, helpers) { + helpers.doEx('sort! /[a-z]/'); + eq('z\ny\nc1\nb2\na3', cm.getValue()); +}, { value: 'z\ny\nc1\nb2\na3'}); +testVim('ex_sort_pattern_alpha_ignoreCase', function(cm, vim, helpers) { + helpers.doEx('sort i/[a-z]/'); + eq('a3\nb2\nC1\nY\nz', cm.getValue()); +}, { value: 'z\nY\nC1\nb2\na3'}); +testVim('ex_sort_pattern_alpha_longer', function(cm, vim, helpers) { + helpers.doEx('sort /[a-z]+/'); + eq('a\naa\nab\nade\nadele\nadelle\nadriana\nalex\nalexandra\nb\nc\ny\nz', cm.getValue()); +}, { value: 'z\nab\naa\nade\nadelle\nalexandra\nalex\nadriana\nadele\ny\nc\nb\na'}); +testVim('ex_sort_pattern_alpha_only', function(cm, vim, helpers) { + helpers.doEx('sort /^[a-z]$/'); + eq('z1\ny2\na3\nb\nc', cm.getValue()); +}, { value: 'z1\ny2\na3\nc\nb'}); +testVim('ex_sort_pattern_alpha_only_reverse', function(cm, vim, helpers) { + helpers.doEx('sort! /^[a-z]$/'); + eq('c\nb\nz1\ny2\na3', cm.getValue()); +}, { value: 'z1\ny2\na3\nc\nb'}); +testVim('ex_sort_pattern_alpha_num', function(cm, vim, helpers) { + helpers.doEx('sort /[a-z][0-9]/'); + eq('c\nb\na3\ny2\nz1', cm.getValue()); +}, { value: 'z1\ny2\na3\nc\nb'}); // test for :global command testVim('ex_global', function(cm, vim, helpers) { cm.setCursor(0, 0); From 407c52278068306470db2ab0d9718f569b22ec90 Mon Sep 17 00:00:00 2001 From: Marijn Haverbeke Date: Sat, 4 Feb 2017 22:38:16 +0100 Subject: [PATCH 0668/1790] [commonlisp mode] Fix wrong bracket Closes #4545 --- mode/commonlisp/commonlisp.js | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/mode/commonlisp/commonlisp.js b/mode/commonlisp/commonlisp.js index 5b407a92..8cd212d3 100644 --- a/mode/commonlisp/commonlisp.js +++ b/mode/commonlisp/commonlisp.js @@ -43,7 +43,7 @@ CodeMirror.defineMode("commonlisp", function (config) { else { stream.skipToEnd(); return "error"; } } else if (ch == "#") { var ch = stream.next(); - if (ch == "[") { type = "open"; return "bracket"; } + 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); From bb2cf359b607152afa7d77a8f9b6bb00cdecdb52 Mon Sep 17 00:00:00 2001 From: Adrian Heine Date: Fri, 3 Feb 2017 12:03:44 +0100 Subject: [PATCH 0669/1790] Make sure coordsChar(charCoords) == id --- src/measurement/position_measurement.js | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/src/measurement/position_measurement.js b/src/measurement/position_measurement.js index 4d2754b9..1faa02ba 100644 --- a/src/measurement/position_measurement.js +++ b/src/measurement/position_measurement.js @@ -427,7 +427,7 @@ export function coordsChar(cm, x, y) { function wrappedLineExtent(cm, lineObj, preparedMeasure, y) { let measure = ch => intoCoordSystem(cm, lineObj, measureCharPrepared(cm, preparedMeasure, ch), "line") let end = lineObj.text.length - let begin = findFirst(ch => measure(ch).bottom < y, end - 1, 0) + 1 + let begin = findFirst(ch => measure(ch).bottom <= y, end - 1, 0) + 1 end = findFirst(ch => measure(ch).top > y, begin, end) return {begin, end} } @@ -466,7 +466,7 @@ function coordsCharInner(cm, lineObj, lineNo, x, y) { end = Math.min(ch, end) return true } - else if (box.bottom < y) return false + else if (box.bottom <= y) return false else if (box.left > x) return true else if (box.right < x) return false else return (x - box.left < box.right - x) From e49d5e893406edef58b8faaa02bf63300f1fe92e Mon Sep 17 00:00:00 2001 From: Adrian Heine Date: Fri, 3 Feb 2017 12:05:25 +0100 Subject: [PATCH 0670/1790] Add failing tests for coordsChar 1. End of wrapped line in bidi content is wrong (my mistake) 2. coordsChar({left:0, top: *}) in bidi content is wrong after Marijn's code style changes --- test/test.js | 38 +++++++++++++++++++++++++++++++++----- 1 file changed, 33 insertions(+), 5 deletions(-) diff --git a/test/test.js b/test/test.js index ab23757d..be970cd1 100644 --- a/test/test.js +++ b/test/test.js @@ -1124,13 +1124,41 @@ testCM("measureWrappedEndOfLine", function(cm) { else break; } } - var endPos = cm.charCoords(Pos(0, 12)); // Next-to-last since last would wrap (#1862) - endPos.left += w; // Add width of editor just to be sure that we are behind last character - eqCursorPos(cm.coordsChar(endPos), Pos(0, 13, "before")); - endPos.left += w * 100; - eqCursorPos(cm.coordsChar(endPos), Pos(0, 13, "before")); + for (var i = 0; i < 2; ++i) { + var endPos = cm.charCoords(Pos(0, 12)); // Next-to-last since last would wrap (#1862) + endPos.left += w; // Add width of editor just to be sure that we are behind last character + eqCursorPos(cm.coordsChar(endPos), Pos(0, 13, "before")); + endPos.left += w * 100; + eqCursorPos(cm.coordsChar(endPos), Pos(0, 13, "before")); + cm.setValue("0123456789abcابجابجابجابج"); + } }, {mode: "text/html", value: "0123456789abcde0123456789", lineWrapping: true}, ie_lt8 || opera_lt10); +testCM("measureWrappedBeginOfLine", function(cm) { + if (phantom) return; + cm.setSize(null, "auto"); + var inner = byClassName(cm.getWrapperElement(), "CodeMirror-lines")[0].firstChild; + var lh = inner.offsetHeight; + 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; } + else break; + } + } + var beginOfSecondLine = Pos(0, 13, "after"); + for (var i = 0; i < 2; ++i) { + var beginPos = cm.charCoords(Pos(0, 0)); + beginPos.left -= w; + eqCursorPos(cm.coordsChar(beginPos), Pos(0, 0, "after")); + beginPos = cm.cursorCoords(beginOfSecondLine); + beginPos.left = 0; + eqCursorPos(cm.coordsChar(beginPos), beginOfSecondLine); + cm.setValue("0123456789abcابجابجابجابج"); + beginOfSecondLine = Pos(0, 25, "before"); + } +}, {mode: "text/html", value: "0123456789abcde0123456789", lineWrapping: true}); + testCM("scrollVerticallyAndHorizontally", function(cm) { if (cm.getOption("inputStyle") != "textarea") return; cm.setSize(100, 100); From 7ad2ce628af039016b99e3d9a43f92df59e441af Mon Sep 17 00:00:00 2001 From: Adrian Heine Date: Fri, 3 Feb 2017 12:16:45 +0100 Subject: [PATCH 0671/1790] Fix coordsChar at end and beginning of wrapped bidi lines --- src/measurement/position_measurement.js | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/src/measurement/position_measurement.js b/src/measurement/position_measurement.js index 1faa02ba..16cfbed9 100644 --- a/src/measurement/position_measurement.js +++ b/src/measurement/position_measurement.js @@ -427,7 +427,7 @@ export function coordsChar(cm, x, y) { function wrappedLineExtent(cm, lineObj, preparedMeasure, y) { let measure = ch => intoCoordSystem(cm, lineObj, measureCharPrepared(cm, preparedMeasure, ch), "line") let end = lineObj.text.length - let begin = findFirst(ch => measure(ch).bottom <= y, end - 1, 0) + 1 + let begin = findFirst(ch => measure(ch - 1).bottom <= y, end, 0) end = findFirst(ch => measure(ch).top > y, begin, end) return {begin, end} } @@ -450,7 +450,7 @@ function coordsCharInner(cm, lineObj, lineNo, x, y) { prevDiff = diff let prevPos = pos pos = moveVisually(cm, lineObj, pos, dir) - if (pos == null || pos.ch < begin || end <= pos.ch) { + if (pos == null || pos.ch < begin || end <= (pos.sticky == "before" ? pos.ch - 1 : pos.ch)) { pos = prevPos break } From a7f801814e5da9799e0815a2264cadb0aaa2ca2d Mon Sep 17 00:00:00 2001 From: Adrian Heine Date: Fri, 3 Feb 2017 13:15:48 +0100 Subject: [PATCH 0672/1790] Safeguard against infinite loops in coordsCharInner --- src/measurement/position_measurement.js | 12 +++++++----- 1 file changed, 7 insertions(+), 5 deletions(-) diff --git a/src/measurement/position_measurement.js b/src/measurement/position_measurement.js index 16cfbed9..ec72332e 100644 --- a/src/measurement/position_measurement.js +++ b/src/measurement/position_measurement.js @@ -445,19 +445,21 @@ function coordsCharInner(cm, lineObj, lineNo, x, y) { pos = new Pos(lineNo, begin) let beginLeft = cursorCoords(cm, pos, "line", lineObj, preparedMeasure).left let dir = beginLeft < x ? 1 : -1 - let prevDiff, diff = beginLeft - x + let prevDiff, diff = beginLeft - x, prevPos do { prevDiff = diff - let prevPos = pos + prevPos = pos pos = moveVisually(cm, lineObj, pos, dir) if (pos == null || pos.ch < begin || end <= (pos.sticky == "before" ? pos.ch - 1 : pos.ch)) { pos = prevPos break } diff = cursorCoords(cm, pos, "line", lineObj, preparedMeasure).left - x - } while ((dir < 0) != (diff < 0)) - // moveVisually has the nice side effect of skipping extending chars and setting sticky - if (Math.abs(diff) > Math.abs(prevDiff)) pos = moveVisually(cm, lineObj, pos, -dir) + } while ((dir < 0) != (diff < 0) && (Math.abs(diff) <= Math.abs(prevDiff))) + if (Math.abs(diff) > Math.abs(prevDiff)) { + if ((diff < 0) == (prevDiff < 0)) throw new Error("Broke out of infinite loop in coordsCharInner") + pos = prevPos + } } else { let ch = findFirst(ch => { let box = intoCoordSystem(cm, lineObj, measureCharPrepared(cm, preparedMeasure, ch), "line") From a89e94433a31e299088327db5bd9eea4b6c932b0 Mon Sep 17 00:00:00 2001 From: Adrian Heine Date: Fri, 3 Feb 2017 13:17:08 +0100 Subject: [PATCH 0673/1790] Use wrappedLineExtent in moveVisually --- src/input/movement.js | 35 +++++++++++-------------- src/measurement/position_measurement.js | 5 ++++ test/test.js | 7 ++++- 3 files changed, 26 insertions(+), 21 deletions(-) diff --git a/src/input/movement.js b/src/input/movement.js index ebb1bdf2..7f4643be 100644 --- a/src/input/movement.js +++ b/src/input/movement.js @@ -1,5 +1,5 @@ import { Pos } from "../line/pos" -import { prepareMeasureForLine, measureCharPrepared } from "../measurement/position_measurement" +import { prepareMeasureForLine, measureCharPrepared, wrappedLineExtentChar } from "../measurement/position_measurement" import { getBidiPartAt, getOrder } from "../util/bidi" import { findFirst, lst, skipExtendingChars } from "../util/misc" @@ -40,16 +40,6 @@ export function endOfLine(visually, cm, lineObj, lineNo, dir) { return new Pos(lineNo, dir < 0 ? lineObj.text.length : 0, dir < 0 ? "before" : "after") } -function getVisualLineAround(cm, line, target) { - if (!cm.options.lineWrapping) return [0, line.text.length - 1] - let prep = prepareMeasureForLine(cm, line) - let targetTop = measureCharPrepared(cm, prep, target).top - return [ - findFirst(ch => targetTop == measureCharPrepared(cm, prep, ch).top, 0, target), - findFirst(ch => targetTop == measureCharPrepared(cm, prep, ch).top, line.text.length - 1, target) - ] -} - export function moveVisually(cm, line, start, dir) { let bidi = getOrder(line) if (!bidi) return moveLogically(line, start, dir) @@ -68,12 +58,17 @@ export function moveVisually(cm, line, start, dir) { } let mv = (pos, dir) => moveCharLogically(line, pos instanceof Pos ? pos.ch : pos, dir) - let getVisualLine = ch => getVisualLineAround(cm, line, ch) - let visualLine = getVisualLine(start.sticky == "before" ? mv(start, -1) : start.ch) + let prep + let getWrappedLineExtent = ch => { + if (!cm.options.lineWrapping) return {begin: 0, end: line.text.length} + prep = prep || prepareMeasureForLine(cm, line) + return wrappedLineExtentChar(cm, line, prep, ch) + } + let wrappedLineExtent = getWrappedLineExtent(start.sticky == "before" ? mv(start, -1) : start.ch) if (part.level % 2 == 1) { let ch = mv(start, -dir) - if (ch != null && (dir > 0 ? ch >= part.from && ch >= visualLine[0] : ch <= part.to && ch <= mv(visualLine[1], 1))) { + if (ch != null && (dir > 0 ? ch >= part.from && ch >= wrappedLineExtent.begin : ch <= part.to && ch <= wrappedLineExtent.end)) { // Case 2: We move within an rtl part on the same visual line let sticky = dir < 0 ? "before" : "after" return new Pos(start.line, ch, sticky) @@ -83,7 +78,7 @@ export function moveVisually(cm, line, start, dir) { // Case 3: Could not move within this bidi part in this visual line, so leave // the current bidi part - let searchInVisualLine = (partPos, dir, visualLine) => { + let searchInVisualLine = (partPos, dir, wrappedLineExtent) => { let getRes = (ch, moveInStorageOrder) => moveInStorageOrder ? new Pos(start.line, mv(ch, 1), "before") : new Pos(start.line, ch, "after") @@ -91,21 +86,21 @@ export function moveVisually(cm, line, start, dir) { for (; partPos >= 0 && partPos < bidi.length; partPos += dir) { let part = bidi[partPos] let moveInStorageOrder = (dir > 0) == (part.level != 1) - let ch = moveInStorageOrder ? visualLine[0] : visualLine[1] + let ch = moveInStorageOrder ? wrappedLineExtent.begin : mv(wrappedLineExtent.end, -1) if (part.from <= ch && ch < part.to) return getRes(ch, moveInStorageOrder) ch = moveInStorageOrder ? part.from : mv(part.to, -1) - if (visualLine[0] <= ch && ch <= visualLine[1]) return getRes(ch, moveInStorageOrder) + if (wrappedLineExtent.begin <= ch && ch < wrappedLineExtent.end) return getRes(ch, moveInStorageOrder) } } // Case 3a: Look for other bidi parts on the same visual line - let res = searchInVisualLine(partPos + dir, dir, visualLine) + let res = searchInVisualLine(partPos + dir, dir, wrappedLineExtent) if (res) return res // Case 3b: Look for other bidi parts on the next visual line - let nextCh = mv(visualLine[dir > 0 ? 1 : 0], dir) + let nextCh = dir > 0 ? wrappedLineExtent.end : mv(wrappedLineExtent.begin, -1) if (nextCh != null && !(dir > 0 && nextCh == line.text.length)) { - res = searchInVisualLine(dir > 0 ? 0 : bidi.length - 1, dir, getVisualLine(nextCh)) + res = searchInVisualLine(dir > 0 ? 0 : bidi.length - 1, dir, getWrappedLineExtent(nextCh)) if (res) return res } diff --git a/src/measurement/position_measurement.js b/src/measurement/position_measurement.js index ec72332e..92da0e80 100644 --- a/src/measurement/position_measurement.js +++ b/src/measurement/position_measurement.js @@ -432,6 +432,11 @@ function wrappedLineExtent(cm, lineObj, preparedMeasure, y) { return {begin, end} } +export function wrappedLineExtentChar(cm, lineObj, preparedMeasure, target) { + let targetTop = intoCoordSystem(cm, lineObj, measureCharPrepared(cm, preparedMeasure, target), "line").top + return wrappedLineExtent(cm, lineObj, preparedMeasure, targetTop) +} + function coordsCharInner(cm, lineObj, lineNo, x, y) { y -= heightAtLine(lineObj) let begin = 0, end = lineObj.text.length diff --git a/test/test.js b/test/test.js index be970cd1..b9f5aa8c 100644 --- a/test/test.js +++ b/test/test.js @@ -1124,13 +1124,18 @@ testCM("measureWrappedEndOfLine", function(cm) { else break; } } - for (var i = 0; i < 2; ++i) { + for (var i = 0; i < 3; ++i) { var endPos = cm.charCoords(Pos(0, 12)); // Next-to-last since last would wrap (#1862) endPos.left += w; // Add width of editor just to be sure that we are behind last character eqCursorPos(cm.coordsChar(endPos), Pos(0, 13, "before")); endPos.left += w * 100; eqCursorPos(cm.coordsChar(endPos), Pos(0, 13, "before")); cm.setValue("0123456789abcابجابجابجابج"); + if (i == 1) { + var node = document.createElement("div"); + node.innerHTML = "hi"; node.style.height = "30px"; + cm.addLineWidget(0, node, {above: true}); + } } }, {mode: "text/html", value: "0123456789abcde0123456789", lineWrapping: true}, ie_lt8 || opera_lt10); From 13aed570c920420f0016b25f36e34f3b5e477b1e Mon Sep 17 00:00:00 2001 From: Marijn Haverbeke Date: Mon, 6 Feb 2017 14:33:49 +0100 Subject: [PATCH 0674/1790] Fix browser detection for Edge Since it's increased the strangeness of the userAgent string again Issue #4408 --- src/util/browser.js | 11 ++++++----- 1 file changed, 6 insertions(+), 5 deletions(-) diff --git a/src/util/browser.js b/src/util/browser.js index ce678a82..3891d13e 100644 --- a/src/util/browser.js +++ b/src/util/browser.js @@ -6,17 +6,18 @@ let platform = navigator.platform export let gecko = /gecko\/\d/i.test(userAgent) let ie_upto10 = /MSIE \d/.test(userAgent) let ie_11up = /Trident\/(?:[7-9]|\d{2,})\..*rv:(\d+)/.exec(userAgent) -export let ie = ie_upto10 || ie_11up -export let ie_version = ie && (ie_upto10 ? document.documentMode || 6 : ie_11up[1]) -export let webkit = /WebKit\//.test(userAgent) +let edge = /Edge\/(\d+)/.exec(userAgent) +export let ie = ie_upto10 || ie_11up || edge +export let ie_version = ie && (ie_upto10 ? document.documentMode || 6 : +(edge || ie_11up)[1]) +export let webkit = !edge && /WebKit\//.test(userAgent) let qtwebkit = webkit && /Qt\/\d+\.\d+/.test(userAgent) -export let chrome = /Chrome\//.test(userAgent) +export let chrome = !edge && /Chrome\//.test(userAgent) export let presto = /Opera\//.test(userAgent) export let safari = /Apple Computer/.test(navigator.vendor) export let mac_geMountainLion = /Mac OS X 1\d\D([8-9]|\d\d)\D/.test(userAgent) export let phantom = /PhantomJS/.test(userAgent) -export let ios = /AppleWebKit/.test(userAgent) && /Mobile\/\w+/.test(userAgent) +export let ios = !edge && /AppleWebKit/.test(userAgent) && /Mobile\/\w+/.test(userAgent) // This is woefully incomplete. Suggestions for alternative methods welcome. export let mobile = ios || /Android|webOS|BlackBerry|Opera Mini|Opera Mobi|IEMobile/i.test(userAgent) export let mac = ios || /Mac/.test(platform) From 6b831b8306b01f463744a0e57eeb567795be5909 Mon Sep 17 00:00:00 2001 From: Marijn Haverbeke Date: Mon, 6 Feb 2017 22:48:11 +0100 Subject: [PATCH 0675/1790] [indent-fold addon] Ignore comment lines Closes #4548 --- addon/fold/indent-fold.js | 34 +++++++++++++++++++--------------- demo/folding.html | 29 +++++++++++++++++++++++++++++ 2 files changed, 48 insertions(+), 15 deletions(-) diff --git a/addon/fold/indent-fold.js b/addon/fold/indent-fold.js index e29f15e9..743150c6 100644 --- a/addon/fold/indent-fold.js +++ b/addon/fold/indent-fold.js @@ -11,32 +11,36 @@ })(function(CodeMirror) { "use strict"; +function lineIndent(cm, lineNo) { + var text = cm.getLine(lineNo) + var spaceTo = text.search(/\S/) + if (spaceTo == -1 || /\bcomment\b/.test(cm.getTokenTypeAt(CodeMirror.Pos(lineNo, spaceTo + 1)))) + return -1 + return CodeMirror.countColumn(text, null, cm.getOption("tabSize")) +} + ! 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(line) { - return CodeMirror.countColumn(line, null, tabSize); - }; - var myIndent = getIndent(firstLine); - var lastLineInFold = null; + var myIndent = lineIndent(cm, start.line) + if (myIndent < 0) return + 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.lastLine(); i <= end; ++i) { - var curLine = cm.getLine(i); - var curIndent = getIndent(curLine); - if (curIndent > myIndent) { + var indent = lineIndent(cm, i) + if (indent == -1) { + } else if (indent > myIndent) { // Lines with a greater indent are considered part of the block. lastLineInFold = i; - } 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 - // start of another block. + // If this line has non-space, non-comment content, and is + // indented less or equal to the start line, it is the start of + // another block. break; } } if (lastLineInFold) return { - from: CodeMirror.Pos(start.line, firstLine.length), + from: CodeMirror.Pos(start.line, cm.getLine(start.line).length), to: CodeMirror.Pos(lastLineInFold, cm.getLine(lastLineInFold).length) }; }); diff --git a/demo/folding.html b/demo/folding.html index 81cbf989..06d58302 100644 --- a/demo/folding.html +++ b/demo/folding.html @@ -12,10 +12,12 @@ + + + .CodeMirror {border-top: 1px solid black; border-bottom: 1px solid black;} + fieldset {border: none} +
    -
    cm.startOperation()
    -
    cm.endOperation()
    +
    cm.startOperation()
    +
    cm.endOperation()
    In normal circumstances, use the above operation method. But if you want to buffer operations happening asynchronously, or that can't all be wrapped in a callback function, you can From 6df6280ed80c8a39df62f515f31cd921329e3f47 Mon Sep 17 00:00:00 2001 From: Marijn Haverbeke Date: Fri, 21 Jul 2017 12:19:50 +0200 Subject: [PATCH 0974/1790] Mark version 5.28.0 --- AUTHORS | 9 +++++++++ CHANGELOG.md | 22 ++++++++++++++++++++++ doc/manual.html | 2 +- doc/releases.html | 17 ++++++++++++++--- index.html | 2 +- package.json | 2 +- src/edit/main.js | 2 +- 7 files changed, 49 insertions(+), 7 deletions(-) diff --git a/AUTHORS b/AUTHORS index 88e625be..07b8fbd0 100644 --- a/AUTHORS +++ b/AUTHORS @@ -74,6 +74,7 @@ Arsène von Wyss Arthur Müller Arun Narasani as3boyan +asolove atelierbram AtomicPages LLC Atul Bhouraskar @@ -87,6 +88,7 @@ Bem Jones-Bey benbro Beni Cherniavsky-Paskin Benjamin DeCoste +Benjamin Young Ben Keen Ben Miller Ben Mosher @@ -181,6 +183,7 @@ Drew Khoury Drini Cami Dror BG duralog +dwelle eborden edsharp ekhaled @@ -218,6 +221,7 @@ Gautam Mehta Gavin Douglas gekkoe Geordie Hall +George Stephanis geowarin Gerard Braad Gergely Hegykozi @@ -340,6 +344,7 @@ Ken Newman ken restivo Ken Rockot Kevin Earls +Kevin Kwok Kevin Muret Kevin Sawicki Kevin Ushey @@ -434,6 +439,7 @@ Michael Grey Michael Kaminsky Michael Lehenbauer Michael Zhou +Michal Čihař Michal Dorner Mighty Guava Miguel Castillo @@ -468,6 +474,7 @@ Nicholas Bollweg Nicholas Bollweg (Nick) Nick Kreeger Nick Small +Nicolas Kick Nicolò Ribaudo Niels van Groningen nightwing @@ -486,6 +493,7 @@ pablo pabloferz Pablo Zubieta Page +paladox Panupong Pasupat paris Paris @@ -631,6 +639,7 @@ Triangle717 Tristan Tarrant TSUYUSATO Kitsune twifkak +Tyler Long VapidWorx Vestimir Markov vf diff --git a/CHANGELOG.md b/CHANGELOG.md index 62b337e7..2c0887ed 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -1,3 +1,25 @@ +## 5.28.0 (2017-07-21) + +### Bug fixes + +Fix copying of, or replacing editor content with, a single dash character when copying a big selection in some corner cases. + +Make [`"goLineLeft"`](http://codemirror.net/doc/manual.html#command_goLineLeft)/`"goLineRight"` behave better on wrapped lines. + +[sql mode](http://codemirror.net/mode/sql/): Fix tokenizing of multi-dot operator and allow digits in subfield names. + +[searchcursor addon](http://codemirror.net/doc/manual.html#addon_searchcursor): Fix infinite loop on some composed character inputs. + +[markdown mode](http://codemirror.net/mode/markdown/): Make list parsing more CommonMark-compliant. + +[gfm mode](http://codemirror.net/mode/gfm/): Highlight colon syntax for emoji. + +### New features + +Expose [`startOperation`](http://codemirror.net/doc/manual.html#startOperation) and `endOperation` for explicit operation management. + +[sublime bindings](http://codemirror.net/demo/sublime.html): Add extend-selection (Ctrl-Alt- or Cmd-Shift-Up/Down). + ## 5.27.4 (2017-06-29) ### Bug fixes diff --git a/doc/manual.html b/doc/manual.html index 8fcd5b45..b4500c37 100644 --- a/doc/manual.html +++ b/doc/manual.html @@ -69,7 +69,7 @@

    User manual and reference guide - version 5.27.5 + version 5.28.0

    CodeMirror is a code-editor component that can be embedded in diff --git a/doc/releases.html b/doc/releases.html index 15bd24e1..86cadb15 100644 --- a/doc/releases.html +++ b/doc/releases.html @@ -30,22 +30,33 @@

    Release notes and version history

    Version 5.x

    +

    21-07-2017: Version 5.28.0:

    + +
      +
    • Fix copying of, or replacing editor content with, a single dash character when copying a big selection in some corner cases.
    • +
    • Make "goLineLeft"/"goLineRight" behave better on wrapped lines.
    • +
    • sql mode: Fix tokenizing of multi-dot operator and allow digits in subfield names.
    • +
    • searchcursor addon: Fix infinite loop on some composed character inputs.
    • +
    • markdown mode: Make list parsing more CommonMark-compliant.
    • +
    • gfm mode: Highlight colon syntax for emoji.
    • +
    +

    29-06-2017: Version 5.27.4:

    -
      +
      • Fix crash when using mode lookahead.
      • markdown mode: Don't block inner mode's indentation support.

      22-06-2017: Version 5.27.2:

      -
        +

        22-06-2017: Version 5.27.0:

        -
          +
          • Fix infinite loop in forced display update.
          • Properly disable the hidden textarea when readOnly is "nocursor".
          • Calling the Doc constructor without new works again.
          • diff --git a/index.html b/index.html index baa0a33b..c47eb6ec 100644 --- a/index.html +++ b/index.html @@ -96,7 +96,7 @@

            This is CodeMirror

            - Get the current version: 5.27.4.
            + Get the current version: 5.28.0.
            You can see the code,
            read the release notes,
            or study the user manual. diff --git a/package.json b/package.json index ef824b0b..e413d2c5 100644 --- a/package.json +++ b/package.json @@ -1,6 +1,6 @@ { "name": "codemirror", - "version": "5.27.5", + "version": "5.28.0", "main": "lib/codemirror.js", "style": "lib/codemirror.css", "description": "Full-featured in-browser code editor", diff --git a/src/edit/main.js b/src/edit/main.js index 54a7a6e3..5a20c647 100644 --- a/src/edit/main.js +++ b/src/edit/main.js @@ -66,4 +66,4 @@ import { addLegacyProps } from "./legacy" addLegacyProps(CodeMirror) -CodeMirror.version = "5.27.5" +CodeMirror.version = "5.28.0" From e246c6b165697c28ae584584d1cb56a31c87c707 Mon Sep 17 00:00:00 2001 From: Marijn Haverbeke Date: Fri, 21 Jul 2017 12:21:43 +0200 Subject: [PATCH 0975/1790] Bump version number post-5.28.0 --- doc/manual.html | 2 +- package.json | 2 +- src/edit/main.js | 2 +- 3 files changed, 3 insertions(+), 3 deletions(-) diff --git a/doc/manual.html b/doc/manual.html index b4500c37..da4423f9 100644 --- a/doc/manual.html +++ b/doc/manual.html @@ -69,7 +69,7 @@

            User manual and reference guide - version 5.28.0 + version 5.28.1

            CodeMirror is a code-editor component that can be embedded in diff --git a/package.json b/package.json index e413d2c5..ff6971cd 100644 --- a/package.json +++ b/package.json @@ -1,6 +1,6 @@ { "name": "codemirror", - "version": "5.28.0", + "version": "5.28.1", "main": "lib/codemirror.js", "style": "lib/codemirror.css", "description": "Full-featured in-browser code editor", diff --git a/src/edit/main.js b/src/edit/main.js index 5a20c647..e7a2122d 100644 --- a/src/edit/main.js +++ b/src/edit/main.js @@ -66,4 +66,4 @@ import { addLegacyProps } from "./legacy" addLegacyProps(CodeMirror) -CodeMirror.version = "5.28.0" +CodeMirror.version = "5.28.1" From 0a1fb070028e2bc9ac6fb1e0c5940102bf7d0fa7 Mon Sep 17 00:00:00 2001 From: dwelle Date: Fri, 21 Jul 2017 17:56:58 +0200 Subject: [PATCH 0976/1790] [gfm mode] update doc --- mode/gfm/index.html | 17 ++++++++++++++--- 1 file changed, 14 insertions(+), 3 deletions(-) diff --git a/mode/gfm/index.html b/mode/gfm/index.html index 642c8ce7..bec130ca 100644 --- a/mode/gfm/index.html +++ b/mode/gfm/index.html @@ -70,21 +70,32 @@

            GFM mode

            ## A bit of GitHub spice +See http://github.github.com/github-flavored-markdown/. + +(Set `gitHubSpice: false` in mode options to disable): + * SHA: be6a8cc1c1ecfe9489fb51e4869af15a13fc2cd2 * User@SHA ref: mojombo@be6a8cc1c1ecfe9489fb51e4869af15a13fc2cd2 * User/Project@SHA: mojombo/god@be6a8cc1c1ecfe9489fb51e4869af15a13fc2cd2 * \#Num: #1 * User/#Num: mojombo#1 * User/Project#Num: mojombo/god#1 -* emoji: :smile: (note: you must add the CSS rule yourself. Set `emoji: false` in mode options to disable) -See http://github.github.com/github-flavored-markdown/. +(Set `emoji: false` in mode options to disable): + +* emoji: :smile: + + @@ -115,6 +116,7 @@

            Test Suite

            + From 3ec7088b8aef7910db0f554ccd4c94d1a77bd71c Mon Sep 17 00:00:00 2001 From: Michael Walker Date: Mon, 24 Jul 2017 09:45:42 +0100 Subject: [PATCH 0981/1790] Use background-color for cm-searching --- lib/codemirror.css | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/lib/codemirror.css b/lib/codemirror.css index b008351a..f4d3c5f4 100644 --- a/lib/codemirror.css +++ b/lib/codemirror.css @@ -319,8 +319,8 @@ div.CodeMirror-dragcursors { .CodeMirror-line::-moz-selection, .CodeMirror-line > span::-moz-selection, .CodeMirror-line > span > span::-moz-selection { background: #d7d4f0; } .cm-searching { - background: #ffa; - background: rgba(255, 255, 0, .4); + background-color: #ffa; + background-color: rgba(255, 255, 0, .4); } /* Used to force a border model for a node */ From c53dc1678a0a1fce9e75f57e902ff73fad2ce64b Mon Sep 17 00:00:00 2001 From: Marijn Haverbeke Date: Tue, 25 Jul 2017 19:09:51 +0200 Subject: [PATCH 0982/1790] [python mode] Simplify tokenizing of operators, fix recognition of several ops Issue #4876 --- mode/python/python.js | 22 ++++++++-------------- 1 file changed, 8 insertions(+), 14 deletions(-) diff --git a/mode/python/python.js b/mode/python/python.js index 2d2b08c8..c3187932 100644 --- a/mode/python/python.js +++ b/mode/python/python.js @@ -41,10 +41,11 @@ CodeMirror.defineMode("python", function(conf, parserConf) { var ERRORCLASS = "error"; - var singleDelimiters = parserConf.singleDelimiters || /^[\(\)\[\]\{\}@,:`=;\.]/; - var doubleOperators = parserConf.doubleOperators || /^([!<>]==|<>|<<|>>|\/\/|\*\*)/; - var doubleDelimiters = parserConf.doubleDelimiters || /^(\+=|\-=|\*=|%=|\/=|&=|\|=|\^=)/; - var tripleDelimiters = parserConf.tripleDelimiters || /^(\/\/=|>>=|<<=|\*\*=)/; + var delimiters = parserConf.delimiters || parserConf.singleDelimiters || /^[\(\)\[\]\{\}@,:`=;\.]/; + // (Backwards-compatiblity with old, cumbersome config system) + var operators = [parserConf.singleOperators, parserConf.doubleOperators, parserConf.doubleDelimiters, parserConf.tripleDelimiters, + parserConf.operators || /^([-+*/%\/&|^]=?|[<>=]+|\/\/=?|\*\*=?|!=|[~!@])/] + for (var i = 0; i < operators.length; i++) if (!operators[i]) operators.splice(i--, 1) var hangingIndent = parserConf.hangingIndent || conf.indentUnit; @@ -58,13 +59,11 @@ var py3 = !(parserConf.version && Number(parserConf.version) < 3) if (py3) { // since http://legacy.python.org/dev/peps/pep-0465/ @ is also an operator - var singleOperators = parserConf.singleOperators || /^[\+\-\*\/%&|\^~<>!@]/; var identifiers = parserConf.identifiers|| /^[_A-Za-z\u00A1-\uFFFF][_A-Za-z0-9\u00A1-\uFFFF]*/; myKeywords = myKeywords.concat(["nonlocal", "False", "True", "None", "async", "await"]); myBuiltins = myBuiltins.concat(["ascii", "bytes", "exec", "print"]); var stringPrefixes = new RegExp("^(([rbuf]|(br))?('{3}|\"{3}|['\"]))", "i"); } else { - var singleOperators = parserConf.singleOperators || /^[\+\-\*\/%&|\^~<>!]/; var identifiers = parserConf.identifiers|| /^[_A-Za-z][_A-Za-z0-9]*/; myKeywords = myKeywords.concat(["exec", "print"]); myBuiltins = myBuiltins.concat(["apply", "basestring", "buffer", "cmp", "coerce", "execfile", @@ -151,15 +150,10 @@ return state.tokenize(stream, state); } - // Handle operators and Delimiters - if (stream.match(tripleDelimiters) || stream.match(doubleDelimiters)) - return "punctuation"; + for (var i = 0; i < operators.length; i++) + if (stream.match(operators[i])) return "operator" - if (stream.match(doubleOperators) || stream.match(singleOperators)) - return "operator"; - - if (stream.match(singleDelimiters)) - return "punctuation"; + if (stream.match(delimiters)) return "punctuation"; if (state.lastToken == "." && stream.match(identifiers)) return "property"; From e4c6f2b34f32681682c59f09d68f90a80d22d18a Mon Sep 17 00:00:00 2001 From: dwelle Date: Thu, 27 Jul 2017 12:51:39 +0200 Subject: [PATCH 0983/1790] [markdown mode] disallow lists and fencedCode inside blockquote --- mode/markdown/markdown.js | 6 +++--- mode/markdown/test.js | 14 ++++++++++++++ 2 files changed, 17 insertions(+), 3 deletions(-) diff --git a/mode/markdown/markdown.js b/mode/markdown/markdown.js index db8359e9..d646d884 100644 --- a/mode/markdown/markdown.js +++ b/mode/markdown/markdown.js @@ -196,7 +196,7 @@ CodeMirror.defineMode("markdown", function(cmCfg, modeCfg) { } else if (stream.match(hrRE, true)) { state.hr = true; return tokenTypes.hr; - } else if (match = stream.match(listRE)) { + } else if (!state.quote && (match = stream.match(listRE))) { var listType = match[1] ? "ol" : "ul"; state.indentation = lineIndentation + stream.current().length; @@ -211,7 +211,7 @@ CodeMirror.defineMode("markdown", function(cmCfg, modeCfg) { state.f = state.inline; if (modeCfg.highlightFormatting) state.formatting = ["list", "list-" + listType]; return getType(state); - } else if (modeCfg.fencedCodeBlocks && (match = stream.match(fencedCodeRE, true))) { + } else if (modeCfg.fencedCodeBlocks && !state.quote && (match = stream.match(fencedCodeRE, true))) { state.fencedChars = match[1] // try switching mode state.localMode = getMode(match[2]); @@ -400,7 +400,7 @@ CodeMirror.defineMode("markdown", function(cmCfg, modeCfg) { if (modeCfg.highlightFormatting) state.formatting = "code"; stream.eatWhile('`'); var count = stream.current().length - if (state.code == 0) { + if (state.code == 0 && (!state.quote || count == 1)) { state.code = count return getType(state) } else if (count == state.code) { // Must be exact diff --git a/mode/markdown/test.js b/mode/markdown/test.js index 8dac53aa..c2ac5483 100644 --- a/mode/markdown/test.js +++ b/mode/markdown/test.js @@ -323,6 +323,20 @@ "", "hello"); + // disallow lists inside blockquote for now because it causes problems outside blockquote + // TODO: fix to be CommonMark-compliant + MT("listNestedInBlockquote", + "[quote"e-1 > - foo]"); + + // disallow fenced blocks inside blockquote because it causes problems outside blockquote + // TODO: fix to be CommonMark-compliant + MT("fencedBlockNestedInBlockquote", + "[quote"e-1 > ```]", + "[quote"e-1 > code]", + "[quote"e-1 > ```]", + // ensure we still allow inline code + "[quote"e-1 > ][quote"e-1&comment `code`]"); + // Header with leading space after continued blockquote (#3287, negative indentation) MT("headerAfterContinuedBlockquote", "[quote"e-1 > foo]", From 0927b971c866c92602d7200f061d59ff78026e09 Mon Sep 17 00:00:00 2001 From: "Jan T. Sott" Date: Tue, 25 Jul 2017 22:32:28 +0200 Subject: [PATCH 0984/1790] [nsis mode] Add support for NSIS 3.02 --- mode/nsis/nsis.js | 8 ++++---- 1 file changed, 4 insertions(+), 4 deletions(-) diff --git a/mode/nsis/nsis.js b/mode/nsis/nsis.js index dc8c74cd..d6c61fac 100644 --- a/mode/nsis/nsis.js +++ b/mode/nsis/nsis.js @@ -24,20 +24,20 @@ CodeMirror.defineSimpleMode("nsis",{ { regex: /`(?:[^\\`]|\\.)*`?/, token: "string" }, // Compile Time Commands - {regex: /^\s*(?:\!(include|addincludedir|addplugindir|appendfile|cd|delfile|echo|error|execute|packhdr|finalize|getdllversion|system|tempfile|warning|verbose|define|undef|insertmacro|makensis|searchparse|searchreplace))\b/, token: "keyword"}, + {regex: /^\s*(?:\!(include|addincludedir|addplugindir|appendfile|cd|delfile|echo|error|execute|packhdr|pragma|finalize|getdllversion|system|tempfile|warning|verbose|define|undef|insertmacro|makensis|searchparse|searchreplace))\b/, token: "keyword"}, // Conditional Compilation {regex: /^\s*(?:\!(if(?:n?def)?|ifmacron?def|macro))\b/, token: "keyword", indent: true}, {regex: /^\s*(?:\!(else|endif|macroend))\b/, token: "keyword", dedent: true}, // Runtime Commands - {regex: /^\s*(?:Abort|AddBrandingImage|AddSize|AllowRootDirInstall|AllowSkipFiles|AutoCloseWindow|BGFont|BGGradient|BrandingText|BringToFront|Call|CallInstDLL|Caption|ChangeUI|CheckBitmap|ClearErrors|CompletedText|ComponentText|CopyFiles|CRCCheck|CreateDirectory|CreateFont|CreateShortCut|Delete|DeleteINISec|DeleteINIStr|DeleteRegKey|DeleteRegValue|DetailPrint|DetailsButtonText|DirText|DirVar|DirVerify|EnableWindow|EnumRegKey|EnumRegValue|Exch|Exec|ExecShell|ExecWait|ExpandEnvStrings|File|FileBufSize|FileClose|FileErrorText|FileOpen|FileRead|FileReadByte|FileReadUTF16LE|FileReadWord|FileWriteUTF16LE|FileSeek|FileWrite|FileWriteByte|FileWriteWord|FindClose|FindFirst|FindNext|FindWindow|FlushINI|GetCurInstType|GetCurrentAddress|GetDlgItem|GetDLLVersion|GetDLLVersionLocal|GetErrorLevel|GetFileTime|GetFileTimeLocal|GetFullPathName|GetFunctionAddress|GetInstDirError|GetLabelAddress|GetTempFileName|Goto|HideWindow|Icon|IfAbort|IfErrors|IfFileExists|IfRebootFlag|IfSilent|InitPluginsDir|InstallButtonText|InstallColors|InstallDir|InstallDirRegKey|InstProgressFlags|InstType|InstTypeGetText|InstTypeSetText|IntCmp|IntCmpU|IntFmt|IntOp|IsWindow|LangString|LicenseBkColor|LicenseData|LicenseForceSelection|LicenseLangString|LicenseText|LoadLanguageFile|LockWindow|LogSet|LogText|ManifestDPIAware|ManifestSupportedOS|MessageBox|MiscButtonText|Name|Nop|OutFile|Page|PageCallbacks|Pop|Push|Quit|ReadEnvStr|ReadINIStr|ReadRegDWORD|ReadRegStr|Reboot|RegDLL|Rename|RequestExecutionLevel|ReserveFile|Return|RMDir|SearchPath|SectionGetFlags|SectionGetInstTypes|SectionGetSize|SectionGetText|SectionIn|SectionSetFlags|SectionSetInstTypes|SectionSetSize|SectionSetText|SendMessage|SetAutoClose|SetBrandingImage|SetCompress|SetCompressor|SetCompressorDictSize|SetCtlColors|SetCurInstType|SetDatablockOptimize|SetDateSave|SetDetailsPrint|SetDetailsView|SetErrorLevel|SetErrors|SetFileAttributes|SetFont|SetOutPath|SetOverwrite|SetRebootFlag|SetRegView|SetShellVarContext|SetSilent|ShowInstDetails|ShowUninstDetails|ShowWindow|SilentInstall|SilentUnInstall|Sleep|SpaceTexts|StrCmp|StrCmpS|StrCpy|StrLen|SubCaption|Unicode|UninstallButtonText|UninstallCaption|UninstallIcon|UninstallSubCaption|UninstallText|UninstPage|UnRegDLL|Var|VIAddVersionKey|VIFileVersion|VIProductVersion|WindowIcon|WriteINIStr|WriteRegBin|WriteRegDWORD|WriteRegExpandStr|WriteRegStr|WriteUninstaller|XPStyle)\b/, token: "keyword"}, + {regex: /^\s*(?:Abort|AddBrandingImage|AddSize|AllowRootDirInstall|AllowSkipFiles|AutoCloseWindow|BGFont|BGGradient|BrandingText|BringToFront|Call|CallInstDLL|Caption|ChangeUI|CheckBitmap|ClearErrors|CompletedText|ComponentText|CopyFiles|CRCCheck|CreateDirectory|CreateFont|CreateShortCut|Delete|DeleteINISec|DeleteINIStr|DeleteRegKey|DeleteRegValue|DetailPrint|DetailsButtonText|DirText|DirVar|DirVerify|EnableWindow|EnumRegKey|EnumRegValue|Exch|Exec|ExecShell|ExecShellWait|ExecWait|ExpandEnvStrings|File|FileBufSize|FileClose|FileErrorText|FileOpen|FileRead|FileReadByte|FileReadUTF16LE|FileReadWord|FileWriteUTF16LE|FileSeek|FileWrite|FileWriteByte|FileWriteWord|FindClose|FindFirst|FindNext|FindWindow|FlushINI|GetCurInstType|GetCurrentAddress|GetDlgItem|GetDLLVersion|GetDLLVersionLocal|GetErrorLevel|GetFileTime|GetFileTimeLocal|GetFullPathName|GetFunctionAddress|GetInstDirError|GetLabelAddress|GetTempFileName|Goto|HideWindow|Icon|IfAbort|IfErrors|IfFileExists|IfRebootFlag|IfSilent|InitPluginsDir|InstallButtonText|InstallColors|InstallDir|InstallDirRegKey|InstProgressFlags|InstType|InstTypeGetText|InstTypeSetText|IntCmp|IntCmpU|IntFmt|IntOp|IsWindow|LangString|LicenseBkColor|LicenseData|LicenseForceSelection|LicenseLangString|LicenseText|LoadLanguageFile|LockWindow|LogSet|LogText|ManifestDPIAware|ManifestSupportedOS|MessageBox|MiscButtonText|Name|Nop|OutFile|Page|PageCallbacks|Pop|Push|Quit|ReadEnvStr|ReadINIStr|ReadRegDWORD|ReadRegStr|Reboot|RegDLL|Rename|RequestExecutionLevel|ReserveFile|Return|RMDir|SearchPath|SectionGetFlags|SectionGetInstTypes|SectionGetSize|SectionGetText|SectionIn|SectionSetFlags|SectionSetInstTypes|SectionSetSize|SectionSetText|SendMessage|SetAutoClose|SetBrandingImage|SetCompress|SetCompressor|SetCompressorDictSize|SetCtlColors|SetCurInstType|SetDatablockOptimize|SetDateSave|SetDetailsPrint|SetDetailsView|SetErrorLevel|SetErrors|SetFileAttributes|SetFont|SetOutPath|SetOverwrite|SetRebootFlag|SetRegView|SetShellVarContext|SetSilent|ShowInstDetails|ShowUninstDetails|ShowWindow|SilentInstall|SilentUnInstall|Sleep|SpaceTexts|StrCmp|StrCmpS|StrCpy|StrLen|SubCaption|Unicode|UninstallButtonText|UninstallCaption|UninstallIcon|UninstallSubCaption|UninstallText|UninstPage|UnRegDLL|Var|VIAddVersionKey|VIFileVersion|VIProductVersion|WindowIcon|WriteINIStr|WriteRegBin|WriteRegDWORD|WriteRegExpandStr|WriteRegMultiStr|WriteRegNone|WriteRegStr|WriteUninstaller|XPStyle)\b/, token: "keyword"}, {regex: /^\s*(?:Function|PageEx|Section(?:Group)?)\b/, token: "keyword", indent: true}, {regex: /^\s*(?:(Function|PageEx|Section(?:Group)?)End)\b/, token: "keyword", dedent: true}, // Command Options - {regex: /\b(?:ARCHIVE|FILE_ATTRIBUTE_ARCHIVE|FILE_ATTRIBUTE_HIDDEN|FILE_ATTRIBUTE_NORMAL|FILE_ATTRIBUTE_OFFLINE|FILE_ATTRIBUTE_READONLY|FILE_ATTRIBUTE_SYSTEM|FILE_ATTRIBUTE_TEMPORARY|HIDDEN|HKCC|HKCR|HKCU|HKDD|HKEY_CLASSES_ROOT|HKEY_CURRENT_CONFIG|HKEY_CURRENT_USER|HKEY_DYN_DATA|HKEY_LOCAL_MACHINE|HKEY_PERFORMANCE_DATA|HKEY_USERS|HKLM|HKPD|HKU|IDABORT|IDCANCEL|IDD_DIR|IDD_INST|IDD_INSTFILES|IDD_LICENSE|IDD_SELCOM|IDD_UNINST|IDD_VERIFY|IDIGNORE|IDNO|IDOK|IDRETRY|IDYES|MB_ABORTRETRYIGNORE|MB_DEFBUTTON1|MB_DEFBUTTON2|MB_DEFBUTTON3|MB_DEFBUTTON4|MB_ICONEXCLAMATION|MB_ICONINFORMATION|MB_ICONQUESTION|MB_ICONSTOP|MB_OK|MB_OKCANCEL|MB_RETRYCANCEL|MB_RIGHT|MB_RTLREADING|MB_SETFOREGROUND|MB_TOPMOST|MB_USERICON|MB_YESNO|MB_YESNOCANCEL|NORMAL|OFFLINE|READONLY|SHCTX|SHELL_CONTEXT|SW_HIDE|SW_SHOWDEFAULT|SW_SHOWMAXIMIZED|SW_SHOWMINIMIZED|SW_SHOWNORMAL|SYSTEM|TEMPORARY)\b/, token: "atom"}, - {regex: /\b(?:admin|all|auto|both|bottom|bzip2|components|current|custom|directory|force|hide|highest|ifdiff|ifnewer|instfiles|lastused|leave|left|license|listonly|lzma|nevershow|none|normal|notset|right|show|silent|silentlog|textonly|top|try|un\.components|un\.custom|un\.directory|un\.instfiles|un\.license|uninstConfirm|user|Win10|Win7|Win8|WinVista|zlib)\b/, token: "builtin"}, + {regex: /\b(?:ARCHIVE|FILE_ATTRIBUTE_ARCHIVE|FILE_ATTRIBUTE_HIDDEN|FILE_ATTRIBUTE_NORMAL|FILE_ATTRIBUTE_OFFLINE|FILE_ATTRIBUTE_READONLY|FILE_ATTRIBUTE_SYSTEM|FILE_ATTRIBUTE_TEMPORARY|HIDDEN|HKCC|HKCR(32|64)?|HKCU(32|64)?|HKDD|HKEY_CLASSES_ROOT|HKEY_CURRENT_CONFIG|HKEY_CURRENT_USER|HKEY_DYN_DATA|HKEY_LOCAL_MACHINE|HKEY_PERFORMANCE_DATA|HKEY_USERS|HKLM(32|64)?|HKPD|HKU|IDABORT|IDCANCEL|IDD_DIR|IDD_INST|IDD_INSTFILES|IDD_LICENSE|IDD_SELCOM|IDD_UNINST|IDD_VERIFY|IDIGNORE|IDNO|IDOK|IDRETRY|IDYES|MB_ABORTRETRYIGNORE|MB_DEFBUTTON1|MB_DEFBUTTON2|MB_DEFBUTTON3|MB_DEFBUTTON4|MB_ICONEXCLAMATION|MB_ICONINFORMATION|MB_ICONQUESTION|MB_ICONSTOP|MB_OK|MB_OKCANCEL|MB_RETRYCANCEL|MB_RIGHT|MB_RTLREADING|MB_SETFOREGROUND|MB_TOPMOST|MB_USERICON|MB_YESNO|MB_YESNOCANCEL|NORMAL|OFFLINE|READONLY|SHCTX|SHELL_CONTEXT|SW_HIDE|SW_SHOWDEFAULT|SW_SHOWMAXIMIZED|SW_SHOWMINIMIZED|SW_SHOWNORMAL|SYSTEM|TEMPORARY)\b/, token: "atom"}, + {regex: /\b(?:admin|all|auto|both|bottom|bzip2|components|current|custom|directory|false|force|hide|highest|ifdiff|ifnewer|instfiles|lastused|leave|left|license|listonly|lzma|nevershow|none|normal|notset|off|on|right|show|silent|silentlog|textonly|top|true|try|un\.components|un\.custom|un\.directory|un\.instfiles|un\.license|uninstConfirm|user|Win10|Win7|Win8|WinVista|zlib)\b/, token: "builtin"}, // LogicLib.nsh {regex: /\$\{(?:And(?:If(?:Not)?|Unless)|Break|Case(?:Else)?|Continue|Default|Do(?:Until|While)?|Else(?:If(?:Not)?|Unless)?|End(?:If|Select|Switch)|Exit(?:Do|For|While)|For(?:Each)?|If(?:Cmd|Not(?:Then)?|Then)?|Loop(?:Until|While)?|Or(?:If(?:Not)?|Unless)|Select|Switch|Unless|While)\}/, token: "variable-2", indent: true}, From 6bbdb75be5a839d6a9f92adaf7357a04af639705 Mon Sep 17 00:00:00 2001 From: Marijn Haverbeke Date: Fri, 28 Jul 2017 14:29:51 +0200 Subject: [PATCH 0985/1790] [continuecomment addon] Don't assume comment lines are a single token Issue codemirror/google-modes#8 --- addon/comment/continuecomment.js | 30 ++++++++++++++---------------- 1 file changed, 14 insertions(+), 16 deletions(-) diff --git a/addon/comment/continuecomment.js b/addon/comment/continuecomment.js index b11d51e6..d7385ef2 100644 --- a/addon/comment/continuecomment.js +++ b/addon/comment/continuecomment.js @@ -18,30 +18,28 @@ if (cm.getOption("disableInput")) return CodeMirror.Pass; var ranges = cm.listSelections(), mode, inserts = []; for (var i = 0; i < ranges.length; i++) { - var pos = ranges[i].head, token = cm.getTokenAt(pos); - if (token.type != "comment") return CodeMirror.Pass; - var modeHere = CodeMirror.innerMode(cm.getMode(), token.state).mode; + var pos = ranges[i].head + if (!/\bcomment\b/.test(cm.getTokenTypeAt(pos))) return CodeMirror.Pass; + var modeHere = cm.getModeAt(pos) if (!mode) mode = modeHere; else if (mode != modeHere) return CodeMirror.Pass; var insert = null; 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 && pos.ch >= end) { + var line = cm.getLine(pos.line).slice(0, pos.ch) + var end = line.indexOf(mode.blockCommentEnd), found + if (end != -1 && end == pos.ch - mode.blockCommentEnd.length) { // Comment ended, don't continue it - } else if (token.string.indexOf(mode.blockCommentStart) == 0) { - insert = full.slice(0, token.start); - if (!/^\s*$/.test(insert)) { - insert = ""; - for (var j = 0; j < token.start; ++j) insert += " "; + } else if ((found = line.indexOf(mode.blockCommentStart)) > -1) { + insert = line.slice(0, found) + if (/\S/.test(insert)) { + insert = "" + for (var j = 0; j < found; ++j) insert += " " } - } else if ((found = full.indexOf(mode.blockCommentContinue)) != -1 && - found + mode.blockCommentContinue.length > token.start && - /^\s*$/.test(full.slice(0, found))) { - insert = full.slice(0, found); + } else if ((found = line.indexOf(mode.blockCommentContinue)) > -1 && !/\S/.test(line.slice(0, found))) { + insert = line.slice(0, found) } - if (insert != null) insert += mode.blockCommentContinue; + if (insert != null) insert += mode.blockCommentContinue } if (insert == null && mode.lineComment && continueLineCommentEnabled(cm)) { var line = cm.getLine(pos.line), found = line.indexOf(mode.lineComment); From 2b8b8e72e70b6e8e4205be3f35a8b9a50983595b Mon Sep 17 00:00:00 2001 From: Benjamin Young Date: Fri, 28 Jul 2017 11:27:27 -0400 Subject: [PATCH 0986/1790] Add alternate media type for Shell Seems Apache has its own list for non-registered media types. --- mode/meta.js | 2 +- mode/shell/index.html | 2 +- mode/shell/shell.js | 3 +++ 3 files changed, 5 insertions(+), 2 deletions(-) diff --git a/mode/meta.js b/mode/meta.js index edaae033..20973ace 100644 --- a/mode/meta.js +++ b/mode/meta.js @@ -121,7 +121,7 @@ {name: "Scala", mime: "text/x-scala", mode: "clike", ext: ["scala"]}, {name: "Scheme", mime: "text/x-scheme", mode: "scheme", ext: ["scm", "ss"]}, {name: "SCSS", mime: "text/x-scss", mode: "css", ext: ["scss"]}, - {name: "Shell", mime: "text/x-sh", mode: "shell", ext: ["sh", "ksh", "bash"], alias: ["bash", "sh", "zsh"], file: /^PKGBUILD$/}, + {name: "Shell", mimes: ["text/x-sh", "application/x-sh"], mode: "shell", ext: ["sh", "ksh", "bash"], alias: ["bash", "sh", "zsh"], file: /^PKGBUILD$/}, {name: "Sieve", mime: "application/sieve", mode: "sieve", ext: ["siv", "sieve"]}, {name: "Slim", mimes: ["text/x-slim", "application/x-slim"], mode: "slim", ext: ["slim"]}, {name: "Smalltalk", mime: "text/x-stsrc", mode: "smalltalk", ext: ["st"]}, diff --git a/mode/shell/index.html b/mode/shell/index.html index 0b56300b..e42f4b5f 100644 --- a/mode/shell/index.html +++ b/mode/shell/index.html @@ -62,5 +62,5 @@

            Shell mode

            }); -

            MIME types defined: text/x-sh.

            +

            MIME types defined: text/x-sh, application/x-sh.

            diff --git a/mode/shell/shell.js b/mode/shell/shell.js index 6af814c4..c5619afe 100644 --- a/mode/shell/shell.js +++ b/mode/shell/shell.js @@ -135,5 +135,8 @@ CodeMirror.defineMode('shell', function() { }); CodeMirror.defineMIME('text/x-sh', 'shell'); +// Apache uses a slightly different Media Type for Shell scripts +// http://svn.apache.org/repos/asf/httpd/httpd/trunk/docs/conf/mime.types +CodeMirror.defineMIME('application/x-sh', 'shell'); }); From 81103a3f32220dfe4cf9027f863aa3da893ca6c2 Mon Sep 17 00:00:00 2001 From: Benjamin Young Date: Fri, 28 Jul 2017 11:32:12 -0400 Subject: [PATCH 0987/1790] Add application/pgp-encrypted MIME type Also .asc and .sig extensions Re: http://svn.apache.org/repos/asf/httpd/httpd/trunk/docs/conf/mime.types --- mode/asciiarmor/asciiarmor.js | 1 + mode/asciiarmor/index.html | 2 +- mode/meta.js | 2 +- 3 files changed, 3 insertions(+), 2 deletions(-) diff --git a/mode/asciiarmor/asciiarmor.js b/mode/asciiarmor/asciiarmor.js index d8309037..fa1b0f8c 100644 --- a/mode/asciiarmor/asciiarmor.js +++ b/mode/asciiarmor/asciiarmor.js @@ -68,6 +68,7 @@ }); CodeMirror.defineMIME("application/pgp", "asciiarmor"); + CodeMirror.defineMIME("application/pgp-encrypted", "asciiarmor"); CodeMirror.defineMIME("application/pgp-keys", "asciiarmor"); CodeMirror.defineMIME("application/pgp-signature", "asciiarmor"); }); diff --git a/mode/asciiarmor/index.html b/mode/asciiarmor/index.html index 8ba1b5c7..4d584efb 100644 --- a/mode/asciiarmor/index.html +++ b/mode/asciiarmor/index.html @@ -41,6 +41,6 @@

            ASCII Armor (PGP) mode

            MIME types -defined: application/pgp, application/pgp-keys, application/pgp-signature

            +defined: application/pgp, application/pgp-encrypted, application/pgp-keys, application/pgp-signature

            diff --git a/mode/meta.js b/mode/meta.js index 20973ace..b08ff933 100644 --- a/mode/meta.js +++ b/mode/meta.js @@ -13,7 +13,7 @@ CodeMirror.modeInfo = [ {name: "APL", mime: "text/apl", mode: "apl", ext: ["dyalog", "apl"]}, - {name: "PGP", mimes: ["application/pgp", "application/pgp-keys", "application/pgp-signature"], mode: "asciiarmor", ext: ["pgp"]}, + {name: "PGP", mimes: ["application/pgp", "application/pgp-encrypted", "application/pgp-keys", "application/pgp-signature"], mode: "asciiarmor", ext: ["asc", "pgp", "sig"]}, {name: "ASN.1", mime: "text/x-ttcn-asn", mode: "asn.1", ext: ["asn", "asn1"]}, {name: "Asterisk", mime: "text/x-asterisk", mode: "asterisk", file: /^extensions\.conf$/i}, {name: "Brainfuck", mime: "text/x-brainfuck", mode: "brainfuck", ext: ["b", "bf"]}, From 902571b643c5df6b1cc8965377e5e35800ed706e Mon Sep 17 00:00:00 2001 From: Benjamin Young Date: Fri, 28 Jul 2017 11:51:35 -0400 Subject: [PATCH 0988/1790] Add additional CoffeeScript MIMES IANA registered application/vnd.coffeescript Also noted the text/coffeescript option in the demo --- mode/coffeescript/coffeescript.js | 4 ++++ mode/coffeescript/index.html | 2 +- mode/meta.js | 2 +- 3 files changed, 6 insertions(+), 2 deletions(-) diff --git a/mode/coffeescript/coffeescript.js b/mode/coffeescript/coffeescript.js index adf2184f..ae955db3 100644 --- a/mode/coffeescript/coffeescript.js +++ b/mode/coffeescript/coffeescript.js @@ -349,6 +349,10 @@ CodeMirror.defineMode("coffeescript", function(conf, parserConf) { return external; }); +// IANA registered media type +// https://www.iana.org/assignments/media-types/ +CodeMirror.defineMIME("application/vnd.coffeescript", "coffeescript"); + CodeMirror.defineMIME("text/x-coffeescript", "coffeescript"); CodeMirror.defineMIME("text/coffeescript", "coffeescript"); diff --git a/mode/coffeescript/index.html b/mode/coffeescript/index.html index 93a5f4f3..92d161e9 100644 --- a/mode/coffeescript/index.html +++ b/mode/coffeescript/index.html @@ -733,7 +733,7 @@

            CoffeeScript mode

            var editor = CodeMirror.fromTextArea(document.getElementById("code"), {}); -

            MIME types defined: text/x-coffeescript.

            +

            MIME types defined: application/vnd.coffeescript, text/coffeescript, text/x-coffeescript.

            The CoffeeScript mode was written by Jeff Pickhardt.

            diff --git a/mode/meta.js b/mode/meta.js index b08ff933..d1c42a03 100644 --- a/mode/meta.js +++ b/mode/meta.js @@ -25,7 +25,7 @@ {name: "ClojureScript", mime: "text/x-clojurescript", mode: "clojure", ext: ["cljs"]}, {name: "Closure Stylesheets (GSS)", mime: "text/x-gss", mode: "css", ext: ["gss"]}, {name: "CMake", mime: "text/x-cmake", mode: "cmake", ext: ["cmake", "cmake.in"], file: /^CMakeLists.txt$/}, - {name: "CoffeeScript", mime: "text/x-coffeescript", mode: "coffeescript", ext: ["coffee"], alias: ["coffee", "coffee-script"]}, + {name: "CoffeeScript", mimes: ["application/vnd.coffeescript", "text/coffeescript", "text/x-coffeescript"], mode: "coffeescript", ext: ["coffee"], alias: ["coffee", "coffee-script"]}, {name: "Common Lisp", mime: "text/x-common-lisp", mode: "commonlisp", ext: ["cl", "lisp", "el"], alias: ["lisp"]}, {name: "Cypher", mime: "application/x-cypher-query", mode: "cypher", ext: ["cyp", "cypher"]}, {name: "Cython", mime: "text/x-cython", mode: "python", ext: ["pyx", "pxd", "pxi"]}, From 910e3becbd8de199165e65e7bd1ade10937f9e0e Mon Sep 17 00:00:00 2001 From: Adrian Heine Date: Sat, 29 Jul 2017 00:41:32 +0200 Subject: [PATCH 0989/1790] [python mode] Add regression test for #4876 --- mode/python/test.js | 5 +++++ 1 file changed, 5 insertions(+) diff --git a/mode/python/test.js b/mode/python/test.js index c1a9c6a9..950eed51 100644 --- a/mode/python/test.js +++ b/mode/python/test.js @@ -24,6 +24,11 @@ MT("matmulWithSpace:", "[variable a] [operator @] [variable b]"); MT("matmulWithoutSpace:", "[variable a][operator @][variable b]"); MT("matmulSpaceBefore:", "[variable a] [operator @][variable b]"); + var before_equal_sign = ["+", "-", "*", "/", "=", "!", ">", "<"]; + for (var i = 0; i < before_equal_sign.length; ++i) { + var c = before_equal_sign[i] + MT("before_equal_sign_" + c, "[variable a] [operator " + c + "=] [variable b]"); + } MT("fValidStringPrefix", "[string f'this is a {formatted} string']"); MT("uValidStringPrefix", "[string u'this is an unicode string']"); From 166959fcd0f1fe8c9c58479ec3abe4e3f34ebfd8 Mon Sep 17 00:00:00 2001 From: Kazuhito Hokamura Date: Mon, 31 Jul 2017 20:29:06 +0900 Subject: [PATCH 0990/1790] Fix broken links --- CHANGELOG.md | 8 ++++---- 1 file changed, 4 insertions(+), 4 deletions(-) diff --git a/CHANGELOG.md b/CHANGELOG.md index 2c0887ed..49290390 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -50,7 +50,7 @@ Calling the `Doc` constructor without `new` works again. [markdown mode](http://codemirror.net/mode/markdown/): Fix bug where markup was ignored on indented paragraph lines. -[vim bindings](http://codemirror.net/mode/demo/vim.html): Referencing invalid registers no longer causes an uncaught exception. +[vim bindings](http://codemirror.net/demo/vim.html): Referencing invalid registers no longer causes an uncaught exception. [rust mode](http://codemirror.net/mode/rust/): Add the correct MIME type. @@ -90,7 +90,7 @@ More careful restoration of selections in widgets, during editor redraw. ### New features -[vim bindings](http://codemirror.net/mode/demo/vim.html): Parse line offsets in line or range specs. +[vim bindings](http://codemirror.net/demo/vim.html): Parse line offsets in line or range specs. ## 5.25.2 (2017-04-20) @@ -148,7 +148,7 @@ Add `role=presentation` to more DOM elements to improve screen reader support. [continuelist addon](http://codemirror.net/doc/manual.html#addon_continuelist): Support continuing task lists. -[vim bindings](http://codemirror.net/mode/demo/vim.html): Make Y behave like yy. +[vim bindings](http://codemirror.net/demo/vim.html): Make Y behave like yy. [sql mode](http://codemirror.net/mode/sql/): Support sqlite dialect. @@ -194,7 +194,7 @@ Fix bug in handling of read-only marked text. Positions now support a `sticky` property which determines whether they should be associated with the character before (value `"before"`) or after (value `"after"`) them. -[vim bindings](http://codemirror.net/mode/demo/vim.html): Make it possible to remove built-in bindings through the API. +[vim bindings](http://codemirror.net/demo/vim.html): Make it possible to remove built-in bindings through the API. [comment addon](http://codemirror.net/doc/manual.html#addon_comment): Support a per-mode useInnerComments option to optionally suppress descending to the inner modes to get comment strings. From 4d448b29763032739a9fc356e3bdd326a23b3cd0 Mon Sep 17 00:00:00 2001 From: Marijn Haverbeke Date: Tue, 1 Aug 2017 10:15:44 +0200 Subject: [PATCH 0991/1790] Also fix vim links in releases.html --- doc/releases.html | 8 ++++---- 1 file changed, 4 insertions(+), 4 deletions(-) diff --git a/doc/releases.html b/doc/releases.html index 86cadb15..1c882dc3 100644 --- a/doc/releases.html +++ b/doc/releases.html @@ -63,7 +63,7 @@

            Version 5.x

          • sql mode: Handle nested comments.
          • javascript mode: Improve support for TypeScript syntax.
          • markdown mode: Fix bug where markup was ignored on indented paragraph lines.
          • -
          • vim bindings: Referencing invalid registers no longer causes an uncaught exception.
          • +
          • vim bindings: Referencing invalid registers no longer causes an uncaught exception.
          • rust mode: Add the correct MIME type.
          • matchbrackets addon: Document options.
          • Mouse button clicks can now be bound in keymaps by using names like "LeftClick" or "Ctrl-Alt-MiddleTripleClick". When bound to a function, that function will be passed the position of the click as second argument.
          • @@ -79,7 +79,7 @@

            Version 5.x

            • In textarea-mode, don't reset the input field during composition.
            • More careful restoration of selections in widgets, during editor redraw.
            • -
            • vim bindings: Parse line offsets in line or range specs.
            • +
            • vim bindings: Parse line offsets in line or range specs.
            • javascript mode: More TypeScript parsing fixes.
            • julia mode: Fix issue where the mode gets stuck.
            • markdown mode: Understand cross-line links, parse all bracketed things as links.
            • @@ -118,7 +118,7 @@

              Version 5.x

            • soy mode: Improve indentation.
            • lint addon: Support asynchronous linters that return promises.
            • continuelist addon: Support continuing task lists.
            • -
            • vim bindings: Make Y behave like yy.
            • +
            • vim bindings: Make Y behave like yy.
            • sql mode: Support sqlite dialect.
            @@ -133,7 +133,7 @@

            Version 5.x

            • Positions now support a sticky property which determines whether they should be associated with the character before (value "before") or after (value "after") them.
            • -
            • vim bindings: Make it possible to remove built-in bindings through the API.
            • +
            • vim bindings: Make it possible to remove built-in bindings through the API.
            • comment addon: Support a per-mode useInnerComments option to optionally suppress descending to the inner modes to get comment strings.
            • A cursor directly before a line-wrapping break is now drawn before or after the line break depending on which direction you arrived from.
            • Visual cursor motion in line-wrapped right-to-left text should be much more correct.
            • From d600b9479fbd830f0b6b8710241f2f346c7f05e5 Mon Sep 17 00:00:00 2001 From: dwelle Date: Sun, 23 Jul 2017 14:35:41 +0200 Subject: [PATCH 0992/1790] [markdown mode] improve setext & hr tokenization --- mode/markdown/markdown.js | 66 +++++++++++++++++++++++++------------ mode/markdown/test.js | 68 +++++++++++++++++++++++++++++++++++---- 2 files changed, 108 insertions(+), 26 deletions(-) diff --git a/mode/markdown/markdown.js b/mode/markdown/markdown.js index d646d884..59a17012 100644 --- a/mode/markdown/markdown.js +++ b/mode/markdown/markdown.js @@ -90,6 +90,7 @@ CodeMirror.defineMode("markdown", function(cmCfg, modeCfg) { , textRE = /^[^#!\[\]*_\\<>` "'(~:]+/ , fencedCodeRE = new RegExp("^(" + (modeCfg.fencedCodeBlocks === true ? "~~~+|```+" : modeCfg.fencedCodeBlocks) + ")[ \\t]*([\\w+#\-]*)") + , linkDefRE = /^\s*\[[^\]]+?\]:\s*\S+(\s*\S*\s*)?$/ // naive link-definition , punctuation = /[!\"#$%&\'()*+,\-\.\/:;<=>?@\[\\\]^_`{|}~—]/ , expandedTab = " " // CommonMark specifies tab as 4 spaces @@ -110,6 +111,7 @@ CodeMirror.defineMode("markdown", function(cmCfg, modeCfg) { // Blocks function blankLine(state) { + state.hr = false; // Reset linkTitle state state.linkTitle = false; // Reset EM state @@ -137,16 +139,18 @@ CodeMirror.defineMode("markdown", function(cmCfg, modeCfg) { function blockNormal(stream, state) { var sol = stream.sol(); + var prevLineLineIsEmpty = lineIsEmpty(state.prevLine); + var prevLineIsIndentedCode = state.indentedCode; + var prevLineIsHr = state.hr; + var prevLineIsList = state.list !== false; + var maxNonCodeIndentation = (state.listStack[state.listStack.length - 1] || 0) + 3; - var prevLineIsList = state.list !== false, - prevLineIsIndentedCode = state.indentedCode; - + state.hr = false; state.indentedCode = false; - var lineIndentation; + var lineIndentation = state.indentation; // compute once per line (on first token) if (state.indentationDiff === null) { - lineIndentation = state.indentation; state.indentationDiff = state.indentation; if (prevLineIsList) { state.list = null; @@ -168,8 +172,11 @@ CodeMirror.defineMode("markdown", function(cmCfg, modeCfg) { } } + var isHr = (state.list === false || prevLineIsHr || prevLineLineIsEmpty) && + state.indentation <= maxNonCodeIndentation && stream.match(hrRE); + var match = null; - if (state.indentationDiff >= 4 && (prevLineIsIndentedCode || lineIsEmpty(state.prevLine))) { + if (state.indentationDiff >= 4 && (prevLineIsIndentedCode || prevLineLineIsEmpty)) { stream.skipToEnd(); state.indentedCode = true; return tokenTypes.code; @@ -180,23 +187,12 @@ CodeMirror.defineMode("markdown", function(cmCfg, modeCfg) { if (modeCfg.highlightFormatting) state.formatting = "header"; state.f = state.inline; return getType(state); - } else if (!lineIsEmpty(state.prevLine) && !state.quote && !prevLineIsList && - !prevLineIsIndentedCode && (match = stream.match(setextHeaderRE))) { - state.header = match[0].charAt(0) == '=' ? 1 : 2; - if (modeCfg.highlightFormatting) state.formatting = "header"; - state.f = state.inline; - return getType(state); } else if (stream.eat('>')) { state.quote = sol ? 1 : state.quote + 1; if (modeCfg.highlightFormatting) state.formatting = "quote"; stream.eatSpace(); return getType(state); - } else if (stream.peek() === '[') { - return switchInline(stream, state, footnoteLink); - } else if (stream.match(hrRE, true)) { - state.hr = true; - return tokenTypes.hr; - } else if (!state.quote && (match = stream.match(listRE))) { + } else if (!isHr && !state.quote && (match = stream.match(listRE))) { var listType = match[1] ? "ol" : "ul"; state.indentation = lineIndentation + stream.current().length; @@ -220,6 +216,35 @@ CodeMirror.defineMode("markdown", function(cmCfg, modeCfg) { if (modeCfg.highlightFormatting) state.formatting = "code-block"; state.code = -1 return getType(state); + // SETEXT has lowest block-scope precedence after HR, so check it after + // the others (code, blockquote, list...) + } else if ( + // if setext set, indicates line after ---/=== + state.setext || ( + // line before ---/=== + !state.quote && state.list === false && !state.code && !isHr && + !prevLineIsList && !linkDefRE.test(stream.string) && + (match = stream.lookAhead(1)) && (match = match.match(setextHeaderRE)) + ) + ) { + if ( !state.setext ) { + state.header = match[0].charAt(0) == '=' ? 1 : 2; + state.setext = state.header; + } else { + state.header = state.setext; + // has no effect on type so we can reset it now + state.setext = 0; + stream.skipToEnd(); + if (modeCfg.highlightFormatting) state.formatting = "header"; + } + state.f = state.inline; + return getType(state); + } else if (isHr) { + stream.skipToEnd(); + state.hr = true; + return tokenTypes.hr; + } else if (stream.peek() === '[') { + return switchInline(stream, state, footnoteLink); } return switchInline(stream, state, state.inline); @@ -703,6 +728,7 @@ CodeMirror.defineMode("markdown", function(cmCfg, modeCfg) { em: false, strong: false, header: 0, + setext: 0, hr: false, taskList: false, list: false, @@ -741,6 +767,7 @@ CodeMirror.defineMode("markdown", function(cmCfg, modeCfg) { strikethrough: s.strikethrough, emoji: s.emoji, header: s.header, + setext: s.setext, hr: s.hr, taskList: s.taskList, list: s.list, @@ -760,9 +787,8 @@ CodeMirror.defineMode("markdown", function(cmCfg, modeCfg) { state.formatting = false; if (stream != state.thisLine) { - // Reset state.header and state.hr + // Reset state.header state.header = 0; - state.hr = false; if (stream.match(/^\s*$/, true)) { blankLine(state); diff --git a/mode/markdown/test.js b/mode/markdown/test.js index c2ac5483..c2c9fb11 100644 --- a/mode/markdown/test.js +++ b/mode/markdown/test.js @@ -67,7 +67,7 @@ "[header&header-1&formatting&formatting-header&formatting-header-1 # ][header&header-1 foo # bar ][header&header-1&formatting&formatting-header&formatting-header-1 #]"); FT("formatting_setextHeader", - "foo", + "[header&header-1 foo]", "[header&header-1&formatting&formatting-header&formatting-header-1 =]"); FT("formatting_blockquote", @@ -237,27 +237,27 @@ // // Check if single underlining = works MT("setextH1", - "foo", + "[header&header-1 foo]", "[header&header-1 =]"); // Check if 3+ ='s work MT("setextH1", - "foo", + "[header&header-1 foo]", "[header&header-1 ===]"); // Check if single underlining - works MT("setextH2", - "foo", + "[header&header-2 foo]", "[header&header-2 -]"); // Check if 3+ -'s work MT("setextH2", - "foo", + "[header&header-2 foo]", "[header&header-2 ---]"); // http://spec.commonmark.org/0.19/#example-45 MT("setextH2AllowSpaces", - "foo", + "[header&header-2 foo]", " [header&header-2 ---- ]"); // http://spec.commonmark.org/0.19/#example-44 @@ -265,15 +265,50 @@ " [comment foo]", "[hr ---]"); + MT("setextAfterFencedCode", + "[comment ```]", + "[comment foo]", + "[comment ```]", + "[header&header-2 bar]", + "[header&header-2 ---]"); + + MT("setextAferATX", + "[header&header-1 # foo]", + "[header&header-2 bar]", + "[header&header-2 ---]"); + // http://spec.commonmark.org/0.19/#example-51 MT("noSetextAfterQuote", "[quote"e-1 > foo]", + "[hr ---]", + "", + "[quote"e-1 > foo]", + "[quote"e-1 bar]", "[hr ---]"); MT("noSetextAfterList", "[variable-2 - foo]", + "[hr ---]", + "", + "[variable-2 - foo]", + "bar", + "[hr ---]"); + + MT("setext_nestedInlineMarkup", + "[header&header-1 foo ][em&header&header-1 *bar*]", + "[header&header-1 =]"); + + MT("setext_linkDef", + "[link [[aaa]]:] [string&url http://google.com 'title']", "[hr ---]"); + // currently, looks max one line ahead, thus won't catch valid CommonMark + // markup + MT("setext_oneLineLookahead", + "foo", + "[header&header-1 bar]", + "[header&header-1 =]"); + // Single-line blockquote with trailing space MT("blockquoteSpace", "[quote"e-1 > foo]"); @@ -394,6 +429,27 @@ "[variable-2 - foo]", "[hr -----]"); + MT("hrAfterFencedCode", + "[comment ```]", + "[comment code]", + "[comment ```]", + "[hr ---]"); + + // allow hr inside lists + // (require prev line to be empty or hr, TODO: non-CommonMark-compliant) + MT("hrInsideList", + "[variable-2 - foo]", + "", + " [hr ---]", + " [hr ---]", + "", + " [comment ---]"); + + MT("consecutiveHr", + "[hr ---]", + "[hr ---]", + "[hr ---]"); + // Formatting in lists (*) MT("listAsteriskFormatting", "[variable-2 * ][variable-2&em *foo*][variable-2 bar]", From 29fb2806bede834341dd8ea2b601084c31a35880 Mon Sep 17 00:00:00 2001 From: dwelle Date: Mon, 31 Jul 2017 19:22:35 +0200 Subject: [PATCH 0993/1790] [markdown mode] improve header, list & fencedCode behavior around blockquote & indentation --- mode/markdown/markdown.js | 10 +++++++--- mode/markdown/test.js | 39 +++++++++++++++++++++++++++++++++++++++ 2 files changed, 46 insertions(+), 3 deletions(-) diff --git a/mode/markdown/markdown.js b/mode/markdown/markdown.js index 59a17012..d7f225f8 100644 --- a/mode/markdown/markdown.js +++ b/mode/markdown/markdown.js @@ -139,6 +139,7 @@ CodeMirror.defineMode("markdown", function(cmCfg, modeCfg) { function blockNormal(stream, state) { var sol = stream.sol(); + var firstTokenOnLine = stream.column() === state.indentation; var prevLineLineIsEmpty = lineIsEmpty(state.prevLine); var prevLineIsIndentedCode = state.indentedCode; var prevLineIsHr = state.hr; @@ -182,7 +183,8 @@ CodeMirror.defineMode("markdown", function(cmCfg, modeCfg) { return tokenTypes.code; } else if (stream.eatSpace()) { return null; - } else if ((match = stream.match(atxHeaderRE)) && match[1].length <= 6) { + } else if (firstTokenOnLine && state.indentation <= maxNonCodeIndentation && (match = stream.match(atxHeaderRE)) && match[1].length <= 6) { + state.quote = 0; state.header = match[1].length; if (modeCfg.highlightFormatting) state.formatting = "header"; state.f = state.inline; @@ -192,11 +194,12 @@ CodeMirror.defineMode("markdown", function(cmCfg, modeCfg) { if (modeCfg.highlightFormatting) state.formatting = "quote"; stream.eatSpace(); return getType(state); - } else if (!isHr && !state.quote && (match = stream.match(listRE))) { + } else if (!isHr && firstTokenOnLine && state.indentation <= maxNonCodeIndentation && (match = stream.match(listRE))) { var listType = match[1] ? "ol" : "ul"; state.indentation = lineIndentation + stream.current().length; state.list = true; + state.quote = 0; // Add this list item's content's indentation to the stack state.listStack.push(state.indentation); @@ -207,7 +210,8 @@ CodeMirror.defineMode("markdown", function(cmCfg, modeCfg) { state.f = state.inline; if (modeCfg.highlightFormatting) state.formatting = ["list", "list-" + listType]; return getType(state); - } else if (modeCfg.fencedCodeBlocks && !state.quote && (match = stream.match(fencedCodeRE, true))) { + } else if (modeCfg.fencedCodeBlocks && firstTokenOnLine && state.indentation <= maxNonCodeIndentation && (match = stream.match(fencedCodeRE, true))) { + state.quote = 0; state.fencedChars = match[1] // try switching mode state.localMode = getMode(match[2]); diff --git a/mode/markdown/test.js b/mode/markdown/test.js index c2c9fb11..d01b0730 100644 --- a/mode/markdown/test.js +++ b/mode/markdown/test.js @@ -228,6 +228,19 @@ MT("atxH1inline", "[header&header-1 # foo ][header&header-1&em *bar*]"); + MT("atxIndentedTooMuch", + "[header&header-1 # foo]", + " # bar"); + + // disable atx inside blockquote until we implement proper blockquote inner mode + // TODO: fix to be CommonMark-compliant + MT("atxNestedInsideBlockquote", + "[quote"e-1 > # foo]"); + + MT("atxAfterBlockquote", + "[quote"e-1 > foo]", + "[header&header-1 # bar]"); + // Setext headers - H1, H2 // Per documentation, "Any number of underlining =’s or -’s will work." // http://daringfireball.net/projects/markdown/syntax#header @@ -588,6 +601,19 @@ "", "\t\t[variable-3 part of list2]"); + MT("listAfterBlockquote", + "[quote"e-1 > foo]", + "[variable-2 - bar]"); + + // shouldn't create sublist if it's indented more than allowed + MT("nestedListIndentedTooMuch", + "[variable-2 - foo]", + " [variable-2 - bar]"); + + MT("listIndentedTooMuchAfterParagraph", + "foo", + " - bar"); + // Blockquote MT("blockquote", "[variable-2 * foo]", @@ -1096,6 +1122,19 @@ "[comment ~~~]", "bar"); + FencedTest("fencedCodeBlocksAfterBlockquote", + "[quote"e-1 > foo]", + "[comment ```]", + "[comment bar]", + "[comment ```]"); + + // fencedCode indented too much should act as simple indentedCode + // (hence has no highlight formatting) + FT("tooMuchIndentedFencedCode", + " [comment ```]", + " [comment code]", + " [comment ```]"); + // Tests that require XML mode MT("xmlMode", From 170885a12d2a41aa46229b3f6101449355576cc0 Mon Sep 17 00:00:00 2001 From: dwelle Date: Mon, 31 Jul 2017 19:45:32 +0200 Subject: [PATCH 0994/1790] [markdown mode] auto-terminate fencedCode after exiting list --- mode/markdown/markdown.js | 7 +++++-- mode/markdown/test.js | 10 ++++++++++ 2 files changed, 15 insertions(+), 2 deletions(-) diff --git a/mode/markdown/markdown.js b/mode/markdown/markdown.js index d7f225f8..6082eab8 100644 --- a/mode/markdown/markdown.js +++ b/mode/markdown/markdown.js @@ -270,14 +270,17 @@ CodeMirror.defineMode("markdown", function(cmCfg, modeCfg) { } function local(stream, state) { - if (state.fencedChars && stream.match(state.fencedChars)) { + var hasExitedList = state.indentation < state.listStack[state.listStack.length - 1]; + if (state.fencedChars && (hasExitedList || stream.match(state.fencedChars))) { if (modeCfg.highlightFormatting) state.formatting = "code-block"; - var returnType = getType(state) + var returnType; + if (!hasExitedList) returnType = getType(state) state.localMode = state.localState = null; state.block = blockNormal; state.f = inlineNormal; state.fencedChars = null; state.code = 0 + if (hasExitedList) return switchBlock(stream, state, state.block); return returnType; } else if (state.fencedChars && stream.skipTo(state.fencedChars)) { return "comment" diff --git a/mode/markdown/test.js b/mode/markdown/test.js index d01b0730..e9025882 100644 --- a/mode/markdown/test.js +++ b/mode/markdown/test.js @@ -1135,6 +1135,16 @@ " [comment code]", " [comment ```]"); + FencedTest("autoTerminateFencedCodeWhenLeavingList", + "[variable-2 - list1]", + " [variable-3 - list2]", + " [variable-3&comment ```]", + " [comment code]", + " [variable-3 - list2]", + " [variable-2&comment ```]", + " [comment code]", + "[quote"e-1 > foo]"); + // Tests that require XML mode MT("xmlMode", From c81346e2c235961292ca5a7e797ee3e9e5fcf7a3 Mon Sep 17 00:00:00 2001 From: dwelle Date: Mon, 31 Jul 2017 19:55:02 +0200 Subject: [PATCH 0995/1790] [markdown mode] improve blockquote indentation behavior --- mode/markdown/markdown.js | 5 ++--- mode/markdown/test.js | 14 ++++++++++++-- 2 files changed, 14 insertions(+), 5 deletions(-) diff --git a/mode/markdown/markdown.js b/mode/markdown/markdown.js index 6082eab8..0692f7ea 100644 --- a/mode/markdown/markdown.js +++ b/mode/markdown/markdown.js @@ -138,7 +138,6 @@ CodeMirror.defineMode("markdown", function(cmCfg, modeCfg) { } function blockNormal(stream, state) { - var sol = stream.sol(); var firstTokenOnLine = stream.column() === state.indentation; var prevLineLineIsEmpty = lineIsEmpty(state.prevLine); var prevLineIsIndentedCode = state.indentedCode; @@ -189,8 +188,8 @@ CodeMirror.defineMode("markdown", function(cmCfg, modeCfg) { if (modeCfg.highlightFormatting) state.formatting = "header"; state.f = state.inline; return getType(state); - } else if (stream.eat('>')) { - state.quote = sol ? 1 : state.quote + 1; + } else if (state.indentation <= maxNonCodeIndentation && stream.eat('>')) { + state.quote = firstTokenOnLine ? 1 : state.quote + 1; if (modeCfg.highlightFormatting) state.formatting = "quote"; stream.eatSpace(); return getType(state); diff --git a/mode/markdown/test.js b/mode/markdown/test.js index e9025882..21829afb 100644 --- a/mode/markdown/test.js +++ b/mode/markdown/test.js @@ -335,12 +335,22 @@ "foo", "[quote"e-1 > bar]"); - // Nested blockquote - MT("blockquoteSpace", + MT("blockquoteNested", "[quote"e-1 > foo]", "[quote"e-1 >][quote"e-2 > foo]", "[quote"e-1 >][quote"e-2 >][quote"e-3 > foo]"); + // ensure quote-level is inferred correctly even if indented + MT("blockquoteNestedIndented", + " [quote"e-1 > foo]", + " [quote"e-1 >][quote"e-2 > foo]", + " [quote"e-1 >][quote"e-2 >][quote"e-3 > foo]"); + + // ensure quote-level is inferred correctly even if indented + MT("blockquoteIndentedTooMuch", + "foo", + " > bar"); + // Single-line blockquote followed by normal paragraph MT("blockquoteThenParagraph", "[quote"e-1 >foo]", From f8b9eeca8f0e90e8a8e8627796f60b4c8bec7954 Mon Sep 17 00:00:00 2001 From: dwelle Date: Tue, 1 Aug 2017 20:10:00 +0200 Subject: [PATCH 0996/1790] [markdown mode] support fencedCodeBlocks in base markdown as per CommonMark --- mode/gfm/gfm.js | 1 - mode/gfm/test.js | 46 ----------------------------------- mode/markdown/index.html | 50 +++++++++++++++++++++++---------------- mode/markdown/markdown.js | 10 ++------ mode/markdown/test.js | 45 ++++++++++++++++++++++------------- 5 files changed, 61 insertions(+), 91 deletions(-) diff --git a/mode/gfm/gfm.js b/mode/gfm/gfm.js index aac04812..689cd6e2 100644 --- a/mode/gfm/gfm.js +++ b/mode/gfm/gfm.js @@ -114,7 +114,6 @@ CodeMirror.defineMode("gfm", function(config, modeConfig) { var markdownConfig = { taskLists: true, - fencedCodeBlocks: '```', strikethrough: true, emoji: true }; diff --git a/mode/gfm/test.js b/mode/gfm/test.js index 5c8b0332..9cda5c45 100644 --- a/mode/gfm/test.js +++ b/mode/gfm/test.js @@ -14,11 +14,6 @@ FT("doubleBackticks", "[comment&formatting&formatting-code ``][comment foo ` bar][comment&formatting&formatting-code ``]"); - FT("codeBlock", - "[comment&formatting&formatting-code-block ```css]", - "[tag foo]", - "[comment&formatting&formatting-code-block ```]"); - FT("taskList", "[variable-2&formatting&formatting-list&formatting-list-ul - ][meta&formatting&formatting-task [ ]]][variable-2 foo]", "[variable-2&formatting&formatting-list&formatting-list-ul - ][property&formatting&formatting-task [x]]][variable-2 foo]"); @@ -41,31 +36,6 @@ MT("emStrongUnderscore", "[em&strong ___foo___] bar"); - MT("fencedCodeBlocks", - "[comment ```]", - "[comment foo]", - "", - "[comment ```]", - "bar"); - - MT("fencedCodeBlockModeSwitching", - "[comment ```javascript]", - "[variable foo]", - "", - "[comment ```]", - "bar"); - - MT("fencedCodeBlockModeSwitchingObjc", - "[comment ```objective-c]", - "[keyword @property] [variable NSString] [operator *] [variable foo];", - "[comment ```]", - "bar"); - - MT("fencedCodeBlocksNoTildes", - "~~~", - "foo", - "~~~"); - MT("taskListAsterisk", "[variable-2 * ][link&variable-2 [[]]][variable-2 foo]", // Invalid; must have space or x between [] "[variable-2 * ][link&variable-2 [[ ]]][variable-2 bar]", // Invalid; must have space after ] @@ -166,11 +136,6 @@ MT("notALink", "foo asfd:asdf bar"); - MT("notALink", - "[comment ```css]", - "[tag foo] {[property color]:[keyword black];}", - "[comment ```][link http://www.example.com/]"); - MT("notALink", "[comment ``foo `bar` http://www.example.com/``] hello"); @@ -181,17 +146,6 @@ "", "[link http://www.example.com/]"); - MT("headerCodeBlockGithub", - "[header&header-1 # heading]", - "", - "[comment ```]", - "[comment code]", - "[comment ```]", - "", - "Commit: [link be6a8cc1c1ecfe9489fb51e4869af15a13fc2cd2]", - "Issue: [link #1]", - "Link: [link http://www.example.com/]"); - MT("strikethrough", "[strikethrough ~~foo~~]"); diff --git a/mode/markdown/index.html b/mode/markdown/index.html index 15660c26..77f56ab2 100644 --- a/mode/markdown/index.html +++ b/mode/markdown/index.html @@ -87,7 +87,7 @@

              Markdown mode

              A First Level Header ==================== - + A Second Level Header --------------------- @@ -97,11 +97,11 @@

              Markdown mode

              The quick brown fox jumped over the lazy dog's back. - + ### Header 3 > This is a blockquote. - > + > > This is the second paragraph in the blockquote. > > ## This is an H2 in a blockquote @@ -110,23 +110,23 @@

              Markdown mode

              Output: <h1>A First Level Header</h1> - + <h2>A Second Level Header</h2> - + <p>Now is the time for all good men to come to the aid of their country. This is just a regular paragraph.</p> - + <p>The quick brown fox jumped over the lazy dog's back.</p> - + <h3>Header 3</h3> - + <blockquote> <p>This is a blockquote.</p> - + <p>This is the second paragraph in the blockquote.</p> - + <h2>This is an H2 in a blockquote</h2> </blockquote> @@ -140,7 +140,7 @@

              Markdown mode

              Some of these words *are emphasized*. Some of these words _are emphasized also_. - + Use two asterisks for **strong emphasis**. Or, if you prefer, __use two underscores instead__. @@ -148,10 +148,10 @@

              Markdown mode

              <p>Some of these words <em>are emphasized</em>. Some of these words <em>are emphasized also</em>.</p> - + <p>Use two asterisks for <strong>strong emphasis</strong>. Or, if you prefer, <strong>use two underscores instead</strong>.</p> - + ## Lists ## @@ -204,7 +204,7 @@

              Markdown mode

              the paragraphs by 4 spaces or 1 tab: * A list item. - + With multiple paragraphs. * Another item in the list. @@ -216,7 +216,7 @@

              Markdown mode

              <p>With multiple paragraphs.</p></li> <li><p>Another item in the list.</p></li> </ul> - + ### Links ### @@ -311,7 +311,7 @@

              Markdown mode

              <p>I strongly recommend against using any <code>&lt;blink&gt;</code> tags.</p> - + <p>I wish SmartyPants used named entities like <code>&amp;mdash;</code> instead of decimal-encoded entites like <code>&amp;#8212;</code>.</p> @@ -334,11 +334,20 @@

              Markdown mode

              <p>If you want your page to validate under XHTML 1.0 Strict, you've got to put paragraph tags in your blockquotes:</p> - + <pre><code>&lt;blockquote&gt; &lt;p&gt;For example.&lt;/p&gt; &lt;/blockquote&gt; </code></pre> + +## Fenced code blocks (and syntax highlighting) + +```javascript +for (var i = 0; i < items.length; i++) { + console.log(items[i], i); // log them +} +``` + -

              You might want to use the Github-Flavored Markdown mode instead, which adds support for fenced code blocks and a few other things.

              +

              If you also want support strikethrough, emoji and few other goodies, check out Github-Flavored Markdown mode.

              + +

              Optionally depends on other modes for properly highlighted code blocks, + and XML mode for properly highlighted inline XML blocks.

              -

              Optionally depends on the XML mode for properly highlighted inline XML blocks.

              -

              MIME types defined: text/x-markdown.

              Parsing/Highlighting Tests: normal, verbose.

              diff --git a/mode/markdown/markdown.js b/mode/markdown/markdown.js index 0692f7ea..beb29dd1 100644 --- a/mode/markdown/markdown.js +++ b/mode/markdown/markdown.js @@ -35,11 +35,6 @@ CodeMirror.defineMode("markdown", function(cmCfg, modeCfg) { if (modeCfg.maxBlockquoteDepth === undefined) modeCfg.maxBlockquoteDepth = 0; - // Use `fencedCodeBlocks` to configure fenced code blocks. false to - // disable, string to specify a precise regexp that the fence should - // match, and true to allow three or more backticks or tildes (as - // per CommonMark). - // Turn on task lists? ("- [ ] " and "- [x] ") if (modeCfg.taskLists === undefined) modeCfg.taskLists = false; @@ -88,8 +83,7 @@ CodeMirror.defineMode("markdown", function(cmCfg, modeCfg) { , atxHeaderRE = modeCfg.allowAtxHeaderWithoutSpace ? /^(#+)/ : /^(#+)(?: |$)/ , setextHeaderRE = /^ *(?:\={1,}|-{1,})\s*$/ , textRE = /^[^#!\[\]*_\\<>` "'(~:]+/ - , fencedCodeRE = new RegExp("^(" + (modeCfg.fencedCodeBlocks === true ? "~~~+|```+" : modeCfg.fencedCodeBlocks) + - ")[ \\t]*([\\w+#\-]*)") + , fencedCodeRE = /^(~~~+|```+)[ \t]*([\w+#-]*)/ , linkDefRE = /^\s*\[[^\]]+?\]:\s*\S+(\s*\S*\s*)?$/ // naive link-definition , punctuation = /[!\"#$%&\'()*+,\-\.\/:;<=>?@\[\\\]^_`{|}~—]/ , expandedTab = " " // CommonMark specifies tab as 4 spaces @@ -209,7 +203,7 @@ CodeMirror.defineMode("markdown", function(cmCfg, modeCfg) { state.f = state.inline; if (modeCfg.highlightFormatting) state.formatting = ["list", "list-" + listType]; return getType(state); - } else if (modeCfg.fencedCodeBlocks && firstTokenOnLine && state.indentation <= maxNonCodeIndentation && (match = stream.match(fencedCodeRE, true))) { + } else if (firstTokenOnLine && state.indentation <= maxNonCodeIndentation && (match = stream.match(fencedCodeRE, true))) { state.quote = 0; state.fencedChars = match[1] // try switching mode diff --git a/mode/markdown/test.js b/mode/markdown/test.js index 21829afb..eb9d856a 100644 --- a/mode/markdown/test.js +++ b/mode/markdown/test.js @@ -9,8 +9,6 @@ function FT(name) { test.mode(name, modeHighlightFormatting, Array.prototype.slice.call(arguments, 1)); } var modeAtxNoSpace = CodeMirror.getMode(config, {name: "markdown", allowAtxHeaderWithoutSpace: true}); function AtxNoSpaceTest(name) { test.mode(name, modeAtxNoSpace, Array.prototype.slice.call(arguments, 1)); } - var modeFenced = CodeMirror.getMode(config, {name: "markdown", fencedCodeBlocks: true}); - function FencedTest(name) { test.mode(name, modeFenced, Array.prototype.slice.call(arguments, 1)); } var modeOverrideClasses = CodeMirror.getMode(config, { name: "markdown", strikethrough: true, @@ -97,6 +95,11 @@ FT("formatting_image", "[formatting&formatting-image&image&image-marker !][formatting&formatting-image&image&image-alt-text&link [[][image&image-alt-text&link alt text][formatting&formatting-image&image&image-alt-text&link ]]][formatting&formatting-link-string&string&url (][url&string http://link.to/image.jpg][formatting&formatting-link-string&string&url )]"); + FT("codeBlock", + "[comment&formatting&formatting-code-block ```css]", + "[tag foo]", + "[comment&formatting&formatting-code-block ```]"); + MT("plainText", "foo"); @@ -587,7 +590,7 @@ " [variable-2 de-indented text part of list1 again]", "", " [variable-2&comment ```]", - " [variable-2&comment code]", + " [comment code]", " [variable-2&comment ```]", "", " [variable-2 text after fenced code]"); @@ -1085,18 +1088,28 @@ MT("taskList", "[variable-2 * ][link&variable-2 [[ ]]][variable-2 bar]"); - MT("noFencedCodeBlocks", - "~~~", - "foo", - "~~~"); - - FencedTest("fencedCodeBlocks", + MT("fencedCodeBlocks", "[comment ```]", "[comment foo]", + "", + "[comment bar]", + "[comment ```]", + "baz"); + + MT("fencedCodeBlockModeSwitching", + "[comment ```javascript]", + "[variable foo]", + "", + "[comment ```]", + "bar"); + + MT("fencedCodeBlockModeSwitchingObjc", + "[comment ```objective-c]", + "[keyword @property] [variable NSString] [operator *] [variable foo];", "[comment ```]", "bar"); - FencedTest("fencedCodeBlocksMultipleChars", + MT("fencedCodeBlocksMultipleChars", "[comment `````]", "[comment foo]", "[comment ```]", @@ -1104,20 +1117,20 @@ "[comment `````]", "bar"); - FencedTest("fencedCodeBlocksTildes", + MT("fencedCodeBlocksTildes", "[comment ~~~]", "[comment foo]", "[comment ~~~]", "bar"); - FencedTest("fencedCodeBlocksTildesMultipleChars", + MT("fencedCodeBlocksTildesMultipleChars", "[comment ~~~~~]", "[comment ~~~]", "[comment foo]", "[comment ~~~~~]", "bar"); - FencedTest("fencedCodeBlocksMultipleChars", + MT("fencedCodeBlocksMultipleChars", "[comment `````]", "[comment foo]", "[comment ```]", @@ -1125,14 +1138,14 @@ "[comment `````]", "bar"); - FencedTest("fencedCodeBlocksMixed", + MT("fencedCodeBlocksMixed", "[comment ~~~]", "[comment ```]", "[comment foo]", "[comment ~~~]", "bar"); - FencedTest("fencedCodeBlocksAfterBlockquote", + MT("fencedCodeBlocksAfterBlockquote", "[quote"e-1 > foo]", "[comment ```]", "[comment bar]", @@ -1145,7 +1158,7 @@ " [comment code]", " [comment ```]"); - FencedTest("autoTerminateFencedCodeWhenLeavingList", + MT("autoTerminateFencedCodeWhenLeavingList", "[variable-2 - list1]", " [variable-3 - list2]", " [variable-3&comment ```]", From eb2cdcfa145660092d731ebcb6ceac58fb97869d Mon Sep 17 00:00:00 2001 From: dwelle Date: Tue, 1 Aug 2017 23:45:31 +0200 Subject: [PATCH 0997/1790] [markdown mode] allow to disable xml and fencedCodeBlock highlighting --- mode/markdown/markdown.js | 12 +++++++++--- mode/markdown/test.js | 12 ++++++++++++ 2 files changed, 21 insertions(+), 3 deletions(-) diff --git a/mode/markdown/markdown.js b/mode/markdown/markdown.js index beb29dd1..58f1dff8 100644 --- a/mode/markdown/markdown.js +++ b/mode/markdown/markdown.js @@ -45,6 +45,12 @@ CodeMirror.defineMode("markdown", function(cmCfg, modeCfg) { if (modeCfg.emoji === undefined) modeCfg.emoji = false; + if (modeCfg.fencedCodeBlockHighlighting === undefined) + modeCfg.fencedCodeBlockHighlighting = true; + + if (modeCfg.xml === undefined) + modeCfg.xml = true; + // Allow token types to be overridden by user-provided token types. if (modeCfg.tokenTypeOverrides === undefined) modeCfg.tokenTypeOverrides = {}; @@ -207,7 +213,7 @@ CodeMirror.defineMode("markdown", function(cmCfg, modeCfg) { state.quote = 0; state.fencedChars = match[1] // try switching mode - state.localMode = getMode(match[2]); + state.localMode = modeCfg.fencedCodeBlockHighlighting && getMode(match[2]); if (state.localMode) state.localState = CodeMirror.startState(state.localMode); state.f = state.block = local; if (modeCfg.highlightFormatting) state.formatting = "code-block"; @@ -510,7 +516,7 @@ CodeMirror.defineMode("markdown", function(cmCfg, modeCfg) { return type + tokenTypes.linkEmail; } - if (ch === '<' && stream.match(/^(!--|[a-z]+(?:\s+[a-z_:.\-]+(?:\s*=\s*[^ >]+)?)*\s*>)/i, false)) { + if (modeCfg.xml && ch === '<' && stream.match(/^(!--|[a-z]+(?:\s+[a-z_:.\-]+(?:\s*=\s*[^ >]+)?)*\s*>)/i, false)) { var end = stream.string.indexOf(">", stream.pos); if (end != -1) { var atts = stream.string.substring(stream.start, end); @@ -521,7 +527,7 @@ CodeMirror.defineMode("markdown", function(cmCfg, modeCfg) { return switchBlock(stream, state, htmlBlock); } - if (ch === '<' && stream.match(/^\/\w*?>/)) { + if (modeCfg.xml && ch === '<' && stream.match(/^\/\w*?>/)) { state.md_inside = false; return "tag"; } else if (ch === "*" || ch === "_") { diff --git a/mode/markdown/test.js b/mode/markdown/test.js index eb9d856a..86935c95 100644 --- a/mode/markdown/test.js +++ b/mode/markdown/test.js @@ -7,6 +7,10 @@ function MT(name) { test.mode(name, mode, Array.prototype.slice.call(arguments, 1)); } var modeHighlightFormatting = CodeMirror.getMode(config, {name: "markdown", highlightFormatting: true}); function FT(name) { test.mode(name, modeHighlightFormatting, Array.prototype.slice.call(arguments, 1)); } + var modeMT_noXml = CodeMirror.getMode(config, {name: "markdown", xml: false}); + function MT_noXml(name) { test.mode(name, modeMT_noXml, Array.prototype.slice.call(arguments, 1)); } + var modeMT_noFencedHighlight = CodeMirror.getMode(config, {name: "markdown", fencedCodeBlockHighlighting: false}); + function MT_noFencedHighlight(name) { test.mode(name, modeMT_noFencedHighlight, Array.prototype.slice.call(arguments, 1)); } var modeAtxNoSpace = CodeMirror.getMode(config, {name: "markdown", allowAtxHeaderWithoutSpace: true}); function AtxNoSpaceTest(name) { test.mode(name, modeAtxNoSpace, Array.prototype.slice.call(arguments, 1)); } var modeOverrideClasses = CodeMirror.getMode(config, { @@ -1103,6 +1107,11 @@ "[comment ```]", "bar"); + MT_noFencedHighlight("fencedCodeBlock_noHighlight", + "[comment ```javascript]", + "[comment foo]", + "[comment ```]"); + MT("fencedCodeBlockModeSwitchingObjc", "[comment ```objective-c]", "[keyword @property] [variable NSString] [operator *] [variable foo];", @@ -1186,4 +1195,7 @@ "[tag&bracket <][tag div][tag&bracket >]", "[tag&bracket ]"); + MT_noXml("xmlHighlightDisabled", + "
              foo
              "); + })(); From ab83c3c80fb19333da876ed0a5eed76675324c33 Mon Sep 17 00:00:00 2001 From: dwelle Date: Tue, 1 Aug 2017 23:48:14 +0200 Subject: [PATCH 0998/1790] [markdown mode] update doc with available options --- mode/gfm/index.html | 28 ++++++++++++++++++++++++++++ mode/markdown/index.html | 34 ++++++++++++++++++++++++++++++++++ 2 files changed, 62 insertions(+) diff --git a/mode/gfm/index.html b/mode/gfm/index.html index bec130ca..ea4bac15 100644 --- a/mode/gfm/index.html +++ b/mode/gfm/index.html @@ -103,6 +103,34 @@

              GFM mode

              Optionally depends on other modes for properly highlighted code blocks.

              +

              Gfm mode supports these options (apart those from base Markdown mode):

              +
                +
              • + +
                gitHubSpice: boolean
                +
                Hashes, issues... (default: true).
                +
                +
              • +
              • + +
                taskLists: boolean
                +
                - [ ] syntax (default: true).
                +
                +
              • +
              • + +
                strikethrough: boolean
                +
                ~~foo~~ syntax (default: true).
                +
                +
              • +
              • + +
                emoji: boolean
                +
                :emoji: syntax (default: true).
                +
                +
              • +
              +

              Parsing/Highlighting Tests: normal, verbose.

              diff --git a/mode/markdown/index.html b/mode/markdown/index.html index 77f56ab2..abb379f6 100644 --- a/mode/markdown/index.html +++ b/mode/markdown/index.html @@ -364,6 +364,40 @@

              Markdown mode

              Optionally depends on other modes for properly highlighted code blocks, and XML mode for properly highlighted inline XML blocks.

              +

              Markdown mode supports these options:

              +
                +
              • + +
                highlightFormatting: boolean
                +
                Whether to separately highlight markdown meta characterts (*[]()etc.) (default: false).
                +
                +
              • +
              • + +
                maxBlockquoteDepth: boolean
                +
                Maximum allowed blockquote nesting (default: 0 - infinite nesting).
                +
                +
              • +
              • + +
                xml: boolean
                +
                Whether to highlight inline XML (default: true).
                +
                +
              • +
              • + +
                fencedCodeBlockHighlighting: boolean
                +
                Whether to syntax-highlight fenced code blocks, if given mode is included (default: true).
                +
                +
              • +
              • + +
                tokenTypeOverrides: Object
                +
                When you want ot override default token type names (e.g. {code: "code"}).
                +
                +
              • +
              +

              MIME types defined: text/x-markdown.

              Parsing/Highlighting Tests: normal, verbose.

              From f80468537d4c96907bd6489f2ffcb132a90e8056 Mon Sep 17 00:00:00 2001 From: Marijn Haverbeke Date: Wed, 2 Aug 2017 13:25:30 +0200 Subject: [PATCH 0999/1790] Properly call marker.find when scanning DOM text Issue #4889 --- src/input/ContentEditableInput.js | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/input/ContentEditableInput.js b/src/input/ContentEditableInput.js index d103d2d0..67de3b18 100644 --- a/src/input/ContentEditableInput.js +++ b/src/input/ContentEditableInput.js @@ -420,7 +420,7 @@ function domTextBetween(cm, from, to, fromLine, toLine) { let markerID = node.getAttribute("cm-marker"), range if (markerID) { let found = cm.findMarks(Pos(fromLine, 0), Pos(toLine + 1, 0), recognizeMarker(+markerID)) - if (found.length && (range = found[0].find())) + if (found.length && (range = found[0].find(0))) addText(getBetween(cm.doc, range.from, range.to).join(lineSep)) return } From 4c3d3c79a40fce2b38b4b507d3d0e944b56db9ff Mon Sep 17 00:00:00 2001 From: Marijn Haverbeke Date: Thu, 3 Aug 2017 10:58:36 +0200 Subject: [PATCH 1000/1790] Copy over origin when splitting change for read-only spans --- src/model/changes.js | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/model/changes.js b/src/model/changes.js index 214e0231..308dc6b3 100644 --- a/src/model/changes.js +++ b/src/model/changes.js @@ -60,7 +60,7 @@ export function makeChange(doc, change, ignoreReadOnly) { let split = sawReadOnlySpans && !ignoreReadOnly && removeReadOnlyRanges(doc, change.from, change.to) if (split) { for (let i = split.length - 1; i >= 0; --i) - makeChangeInner(doc, {from: split[i].from, to: split[i].to, text: i ? [""] : change.text}) + makeChangeInner(doc, {from: split[i].from, to: split[i].to, text: i ? [""] : change.text, origin: change.origin}) } else { makeChangeInner(doc, change) } From 6e44a5118f105268f7f8979845b1d5269f46c472 Mon Sep 17 00:00:00 2001 From: Marijn Haverbeke Date: Fri, 4 Aug 2017 08:41:29 +0200 Subject: [PATCH 1001/1790] [css mode] Don't feed comment tokens to the state machine Closes #4892 --- mode/css/css.js | 3 ++- mode/css/test.js | 6 ++++++ 2 files changed, 8 insertions(+), 1 deletion(-) diff --git a/mode/css/css.js b/mode/css/css.js index 056c48e6..bfe11d3b 100644 --- a/mode/css/css.js +++ b/mode/css/css.js @@ -383,7 +383,8 @@ CodeMirror.defineMode("css", function(config, parserConfig) { style = style[0]; } override = style; - state.state = states[state.state](type, stream, state); + if (type != "comment") + state.state = states[state.state](type, stream, state); return override; }, diff --git a/mode/css/test.js b/mode/css/test.js index 7a496fb0..6fc6e33c 100644 --- a/mode/css/test.js +++ b/mode/css/test.js @@ -197,4 +197,10 @@ MT("counter-style-symbols", "[tag ol] { [property list-style]: [atom symbols]([atom cyclic] [string \"*\"] [string \"\\2020\"] [string \"\\2021\"] [string \"\\A7\"]); }"); + + MT("comment-does-not-disrupt", + "[def @font-face] [comment /* foo */] {", + " [property src]: [atom url]([string x]);", + " [property font-family]: [variable One];", + "}") })(); From d02b119870a82cfe1aa1bf7ade17d35fae3653d7 Mon Sep 17 00:00:00 2001 From: Yvonnick Esnault Date: Thu, 3 Aug 2017 19:10:56 +0200 Subject: [PATCH 1002/1790] [verilog mode] add .sv and .svh extensions Signed-off-by: Yvonnick Esnault --- mode/meta.js | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/mode/meta.js b/mode/meta.js index d1c42a03..c49bd6c7 100644 --- a/mode/meta.js +++ b/mode/meta.js @@ -137,7 +137,7 @@ {name: "Swift", mime: "text/x-swift", mode: "swift", ext: ["swift"]}, {name: "sTeX", mime: "text/x-stex", mode: "stex"}, {name: "LaTeX", mime: "text/x-latex", mode: "stex", ext: ["text", "ltx"], alias: ["tex"]}, - {name: "SystemVerilog", mime: "text/x-systemverilog", mode: "verilog", ext: ["v"]}, + {name: "SystemVerilog", mime: "text/x-systemverilog", mode: "verilog", ext: ["v", "sv", "svh"]}, {name: "Tcl", mime: "text/x-tcl", mode: "tcl", ext: ["tcl"]}, {name: "Textile", mime: "text/x-textile", mode: "textile", ext: ["textile"]}, {name: "TiddlyWiki ", mime: "text/x-tiddlywiki", mode: "tiddlywiki"}, From 2fcce279de70ed80f2c682f16ca1151f0e0e3886 Mon Sep 17 00:00:00 2001 From: Moshe Wajnberg Date: Sun, 6 Aug 2017 16:31:26 +0300 Subject: [PATCH 1003/1790] Fix(bidi): Fix for bug /codemirror/CodeMirror/issues/4897 Fix for bug https://github.com/codemirror/CodeMirror/issues/4897 --- demo/bidi.html | 12 ++++++++++++ lib/codemirror.css | 1 + 2 files changed, 13 insertions(+) diff --git a/demo/bidi.html b/demo/bidi.html index ceaffd32..645e648c 100644 --- a/demo/bidi.html +++ b/demo/bidi.html @@ -60,6 +60,11 @@

              Bi-directional Text Demo

              +
              + HTML document direction: + + +
              @@ -80,6 +85,13 @@

              Bi-directional Text Demo

              editor.setOption("direction", dirRadios["rtl"].checked ? "rtl" : "ltr"); }; +var HtmlDirRadios = {ltr: document.getElementById("htmlltr"), + rtl: document.getElementById("htmlrtl")}; +HtmlDirRadios["ltr"].checked = true; +HtmlDirRadios["rtl"].onchange = HtmlDirRadios["ltr"].onchange = function() { + document.dir = (HtmlDirRadios["rtl"].checked ? "rtl" : "ltr"); +}; + var moveCheckbox = document.getElementById("rtlMoveVisually"); moveCheckbox.checked = editor.getOption("rtlMoveVisually"); moveCheckbox.onchange = function() { diff --git a/lib/codemirror.css b/lib/codemirror.css index f4d3c5f4..9d8ff0ce 100644 --- a/lib/codemirror.css +++ b/lib/codemirror.css @@ -5,6 +5,7 @@ font-family: monospace; height: 300px; color: black; + direction: ltr; } /* PADDING */ From 4b0ae027938a6ed83b9a2033db291bff8c0f8967 Mon Sep 17 00:00:00 2001 From: Marijn Haverbeke Date: Tue, 8 Aug 2017 22:12:49 +0200 Subject: [PATCH 1004/1790] [shell mode] Allow strings to span lines Closes #4902 --- 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 c5619afe..9b8b90b3 100644 --- a/mode/shell/shell.js +++ b/mode/shell/shell.js @@ -102,7 +102,7 @@ CodeMirror.defineMode('shell', function() { } escaped = !escaped && next === '\\'; } - if (end || !escaped) state.tokens.shift(); + if (end) state.tokens.shift(); return style; }; }; From 974b698fac730685ec51761338786e6801ae366c Mon Sep 17 00:00:00 2001 From: Marijn Haverbeke Date: Tue, 8 Aug 2017 23:07:27 +0200 Subject: [PATCH 1005/1790] [javascript mode] Support typescript-style type params to new Closes #4887 --- mode/javascript/javascript.js | 4 ++++ mode/javascript/test.js | 3 +++ 2 files changed, 7 insertions(+) diff --git a/mode/javascript/javascript.js b/mode/javascript/javascript.js index d77862b1..50d33dde 100644 --- a/mode/javascript/javascript.js +++ b/mode/javascript/javascript.js @@ -468,6 +468,7 @@ CodeMirror.defineMode("javascript", function(config, parserConfig) { function maybeTarget(noComma) { return function(type) { if (type == ".") return cont(noComma ? targetNoComma : target); + else if (type == "variable" && isTS) return cont(maybeTypeArgs, noComma ? maybeoperatorNoComma : maybeoperatorComma) else return pass(noComma ? expressionNoComma : expression); }; } @@ -588,6 +589,9 @@ CodeMirror.defineMode("javascript", function(config, parserConfig) { if (type == "[") return cont(expect("]"), afterType) if (value == "extends") return cont(typeexpr) } + function maybeTypeArgs(_, value) { + if (value == "<") return cont(pushlex(">"), commasep(typeexpr, ">"), poplex, afterType) + } function vardef() { return pass(pattern, maybetype, maybeAssign, vardefCont); } diff --git a/mode/javascript/test.js b/mode/javascript/test.js index 8fd13aee..2632fd1d 100644 --- a/mode/javascript/test.js +++ b/mode/javascript/test.js @@ -343,6 +343,9 @@ "[keyword function] [def x][operator <][type T] [keyword extends] [keyword keyof] [type X][operator >]([def a]: [type T]) {", " [keyword return]") + TS("typescript_new_typeargs", + "[keyword let] [def x] [operator =] [keyword new] [variable Map][operator <][type string], [type Date][operator >]([string-2 `foo${][variable bar][string-2 }`])") + var jsonld_mode = CodeMirror.getMode( {indentUnit: 2}, {name: "javascript", jsonld: true} From 2add03c7efbbb1c685e7da0c4c3733778c6f35d9 Mon Sep 17 00:00:00 2001 From: Jeff Hanke Date: Wed, 9 Aug 2017 04:13:27 -0700 Subject: [PATCH 1006/1790] [html-line addon] Play more nicely with node/webpack. * Pass in and use the htmlhint module required. * Check for verify() and try HTMLHint.HTMLHint if missing because of module nesting. it's require('htmlhint').HTMLHint in node. --- addon/lint/html-lint.js | 10 ++++++---- 1 file changed, 6 insertions(+), 4 deletions(-) diff --git a/addon/lint/html-lint.js b/addon/lint/html-lint.js index 98c36b0b..23de9bb2 100644 --- a/addon/lint/html-lint.js +++ b/addon/lint/html-lint.js @@ -11,8 +11,8 @@ else if (typeof define == "function" && define.amd) // AMD define(["../../lib/codemirror", "htmlhint"], mod); else // Plain browser env - mod(CodeMirror); -})(function(CodeMirror) { + mod(CodeMirror, window.HTMLHint); +})(function(CodeMirror, HTMLHint) { "use strict"; var defaultRules = { @@ -29,9 +29,11 @@ CodeMirror.registerHelper("lint", "html", function(text, options) { var found = []; - if (!window.HTMLHint) { + if (HTMLHint && !HTMLHint.verify) HTMLHint = HTMLHint.HTMLHint; + if (!HTMLHint) HTMLHint = window.HTMLHint; + if (!HTMLHint) { if (window.console) { - window.console.error("Error: window.HTMLHint not defined, CodeMirror HTML linting cannot run."); + window.console.error("Error: HTMLHint not found, not defined on window, or not available through define/require, CodeMirror HTML linting cannot run."); } return found; } From 616116ba326cf4164df28c62380f15e508f65e9a Mon Sep 17 00:00:00 2001 From: CodeBitt <30704531+CodeBitt@users.noreply.github.com> Date: Mon, 14 Aug 2017 17:32:17 -0400 Subject: [PATCH 1007/1790] [real-world uses] Add CodeBitt --- doc/realworld.html | 1 + 1 file changed, 1 insertion(+) diff --git a/doc/realworld.html b/doc/realworld.html index 3995f93e..7c5231ba 100644 --- a/doc/realworld.html +++ b/doc/realworld.html @@ -43,6 +43,7 @@

              CodeMirror real-world uses

            • Complete.ly playground
            • Codeanywhere (multi-platform cloud editor)
            • Code per Node (Drupal module)
            • +
            • CodeBitt (Code snippet sharing)
            • Codebug (PHP Xdebug front-end)
            • CodeMirror Eclipse (embed CM in Eclipse)
            • CodeMirror movie (scripted editing demos)
            • From c06c273afc78781ed0735c8a27680f35397f3f42 Mon Sep 17 00:00:00 2001 From: Sarah McAlear and Wenlin Zhang Date: Mon, 14 Aug 2017 14:41:01 +0800 Subject: [PATCH 1008/1790] [sql-mode] Add greenplum dialect as gpsql --- mode/sql/index.html | 3 ++- mode/sql/sql.js | 13 +++++++++++++ 2 files changed, 15 insertions(+), 1 deletion(-) diff --git a/mode/sql/index.html b/mode/sql/index.html index dba069dc..cd958728 100644 --- a/mode/sql/index.html +++ b/mode/sql/index.html @@ -58,7 +58,8 @@

              SQL Mode for CodeMirror

              text/x-mssql, text/x-hive, text/x-pgsql, - text/x-gql. + text/x-gql, + text/x-gpsql.

              Demonstration of bi-directional text support. See diff --git a/test/test.js b/test/test.js index 32a6c280..e61365d6 100644 --- a/test/test.js +++ b/test/test.js @@ -255,7 +255,7 @@ testCM("coordsCharBidi", function(cm) { }, {lineNumbers: true}); testCM("badBidiOptimization", function(cm) { - let coords = cm.charCoords(Pos(0, 34)) + var coords = cm.charCoords(Pos(0, 34)) eqCharPos(cm.coordsChar({left: coords.right, top: coords.top + 2}), Pos(0, 34)) }, {value: "----------

              هل يمكنك اختيار مستوى قسط التأمين الذي ترغب بدفعه؟

              "}) From 4ba596ea5af0a1f13fe0bb45588bbd7a12f48439 Mon Sep 17 00:00:00 2001 From: Aram Shatakhtsyan Date: Wed, 13 Sep 2017 17:50:45 -0700 Subject: [PATCH 1049/1790] Add CodeFights to the list of real-world users --- doc/realworld.html | 1 + 1 file changed, 1 insertion(+) diff --git a/doc/realworld.html b/doc/realworld.html index 7c5231ba..f0a75abf 100644 --- a/doc/realworld.html +++ b/doc/realworld.html @@ -45,6 +45,7 @@

              CodeMirror real-world uses

            • Code per Node (Drupal module)
            • CodeBitt (Code snippet sharing)
            • Codebug (PHP Xdebug front-end)
            • +
            • CodeFights (practice programming)
            • CodeMirror Eclipse (embed CM in Eclipse)
            • CodeMirror movie (scripted editing demos)
            • CodeMirror2-GWT (Google Web Toolkit wrapper)
            • From 0506dfc565217bf6e5eed5c2770e2dbf30e1f6b2 Mon Sep 17 00:00:00 2001 From: Marijn Haverbeke Date: Thu, 14 Sep 2017 09:05:51 +0200 Subject: [PATCH 1050/1790] Remove accidentally committed debug changes --- demo/bidi.html | 10 +++------- 1 file changed, 3 insertions(+), 7 deletions(-) diff --git a/demo/bidi.html b/demo/bidi.html index 349a3dba..645e648c 100644 --- a/demo/bidi.html +++ b/demo/bidi.html @@ -26,9 +26,8 @@

              Bi-directional Text Demo

              -
              -

              MIME types defined: +

              MIME types defined: text/x-sql, text/x-mysql, text/x-mariadb, @@ -60,6 +60,7 @@

              SQL Mode for CodeMirror

              text/x-pgsql, text/x-gql, text/x-gpsql. + text/x-esper.

              MIME types defined: text/x-protobuf.

              diff --git a/mode/protobuf/protobuf.js b/mode/protobuf/protobuf.js index bcae276e..93cb3b0e 100644 --- a/mode/protobuf/protobuf.js +++ b/mode/protobuf/protobuf.js @@ -19,7 +19,8 @@ "package", "message", "import", "syntax", "required", "optional", "repeated", "reserved", "default", "extensions", "packed", "bool", "bytes", "double", "enum", "float", "string", - "int32", "int64", "uint32", "uint64", "sint32", "sint64", "fixed32", "fixed64", "sfixed32", "sfixed64" + "int32", "int64", "uint32", "uint64", "sint32", "sint64", "fixed32", "fixed64", "sfixed32", "sfixed64", + "option", "service", "rpc", "returns" ]; var keywords = wordRegexp(keywordArray); From a073d18ea4b08d2e1aecdc075ecef8712c10e64f Mon Sep 17 00:00:00 2001 From: Marijn Haverbeke Date: Mon, 16 Oct 2017 11:19:27 +0200 Subject: [PATCH 1108/1790] Remove mode-mutating kludge in continuecomment --- addon/comment/continuecomment.js | 5 ----- mode/clike/clike.js | 1 + mode/css/css.js | 1 + mode/javascript/javascript.js | 1 + 4 files changed, 3 insertions(+), 5 deletions(-) diff --git a/addon/comment/continuecomment.js b/addon/comment/continuecomment.js index d7385ef2..6552b09b 100644 --- a/addon/comment/continuecomment.js +++ b/addon/comment/continuecomment.js @@ -9,11 +9,6 @@ else // Plain browser env mod(CodeMirror); })(function(CodeMirror) { - var modes = ["clike", "css", "javascript"]; - - for (var i = 0; i < modes.length; ++i) - CodeMirror.extendMode(modes[i], {blockCommentContinue: " * "}); - function continueComment(cm) { if (cm.getOption("disableInput")) return CodeMirror.Pass; var ranges = cm.listSelections(), mode, inserts = []; diff --git a/mode/clike/clike.js b/mode/clike/clike.js index 0993ca4c..d6d12c71 100644 --- a/mode/clike/clike.js +++ b/mode/clike/clike.js @@ -244,6 +244,7 @@ CodeMirror.defineMode("clike", function(config, parserConfig) { electricInput: indentSwitch ? /^\s*(?:case .*?:|default:|\{\}?|\})$/ : /^\s*[{}]$/, blockCommentStart: "/*", blockCommentEnd: "*/", + blockCommentContinue: " * ", lineComment: "//", fold: "brace" }; diff --git a/mode/css/css.js b/mode/css/css.js index bfe11d3b..00e9b3df 100644 --- a/mode/css/css.js +++ b/mode/css/css.js @@ -410,6 +410,7 @@ CodeMirror.defineMode("css", function(config, parserConfig) { electricChars: "}", blockCommentStart: "/*", blockCommentEnd: "*/", + blockCommentContinue: " * ", lineComment: lineComment, fold: "brace" }; diff --git a/mode/javascript/javascript.js b/mode/javascript/javascript.js index d92f8622..d6cc4771 100644 --- a/mode/javascript/javascript.js +++ b/mode/javascript/javascript.js @@ -821,6 +821,7 @@ CodeMirror.defineMode("javascript", function(config, parserConfig) { electricInput: /^\s*(?:case .*?:|default:|\{|\})$/, blockCommentStart: jsonMode ? null : "/*", blockCommentEnd: jsonMode ? null : "*/", + blockCommentContinue: jsonMode ? null : " ", lineComment: jsonMode ? null : "//", fold: "brace", closeBrackets: "()[]{}''\"\"``", From ad6635a5b40b20cc3f7ca9a77ef1b42634597790 Mon Sep 17 00:00:00 2001 From: Marijn Haverbeke Date: Mon, 16 Oct 2017 11:20:11 +0200 Subject: [PATCH 1109/1790] [vim bindings] Show fat cursor even in contentEditable mode Issue #3552 --- demo/vim.html | 3 ++- keymap/vim.js | 51 ++++++++++++++++++++++++++++++++++++++++++++-- lib/codemirror.css | 7 ++++++- 3 files changed, 57 insertions(+), 4 deletions(-) diff --git a/demo/vim.html b/demo/vim.html index f27b8b8e..bd704f75 100644 --- a/demo/vim.html +++ b/demo/vim.html @@ -95,7 +95,8 @@

              Vim bindings demo

              mode: "text/x-csrc", keyMap: "vim", matchBrackets: true, - showCursorWhenSelecting: true + showCursorWhenSelecting: true, + inputStyle: "contenteditable" }); var commandDisplay = document.getElementById('command-display'); var keys = ''; diff --git a/keymap/vim.js b/keymap/vim.js index 13c39a1e..7cf5a956 100644 --- a/keymap/vim.js +++ b/keymap/vim.js @@ -255,20 +255,67 @@ } function detachVimMap(cm, next) { - if (this == CodeMirror.keyMap.vim) + if (this == CodeMirror.keyMap.vim) { CodeMirror.rmClass(cm.getWrapperElement(), "cm-fat-cursor"); + if (cm.getOption("inputStyle") == "contenteditable" && document.body.style.caretColor != null) { + disableFatCursorMark(cm); + cm.getInputField().style.caretColor = ""; + } + } if (!next || next.attach != attachVimMap) leaveVimMode(cm); } function attachVimMap(cm, prev) { - if (this == CodeMirror.keyMap.vim) + if (this == CodeMirror.keyMap.vim) { CodeMirror.addClass(cm.getWrapperElement(), "cm-fat-cursor"); + if (cm.getOption("inputStyle") == "contenteditable" && document.body.style.caretColor != null) { + enableFatCursorMark(cm); + cm.getInputField().style.caretColor = "transparent"; + } + } if (!prev || prev.attach != attachVimMap) enterVimMode(cm); } + function fatCursorMarks(cm) { + var ranges = cm.listSelections(), result = [] + for (var i = 0; i < ranges.length; i++) { + var range = ranges[i] + if (range.empty()) { + if (range.anchor.ch < cm.getLine(range.anchor.line).length) { + result.push(cm.markText(range.anchor, Pos(range.anchor.line, range.anchor.ch + 1), + {className: "cm-fat-cursor-mark"})) + } else { + var widget = document.createElement("span") + widget.textContent = "\u00a0" + widget.className = "cm-fat-cursor-mark" + result.push(cm.setBookmark(range.anchor, {widget: widget})) + } + } + } + return result + } + + function updateFatCursorMark(cm) { + var marks = cm.state.fatCursorMarks + if (marks) for (var i = 0; i < marks.length; i++) marks[i].clear() + cm.state.fatCursorMarks = fatCursorMarks(cm) + } + + function enableFatCursorMark(cm) { + cm.state.fatCursorMarks = fatCursorMarks(cm) + cm.on("cursorActivity", updateFatCursorMark) + } + + function disableFatCursorMark(cm) { + var marks = cm.state.fatCursorMarks + if (marks) for (var i = 0; i < marks.length; i++) marks[i].clear() + cm.state.fatCursorMarks = null + cm.off("cursorActivity", updateFatCursorMark) + } + // Deprecated, simply setting the keymap works again. CodeMirror.defineOption('vimMode', false, function(cm, val, prev) { if (val && cm.getOption("keyMap") != "vim") diff --git a/lib/codemirror.css b/lib/codemirror.css index 9d8ff0ce..255de986 100644 --- a/lib/codemirror.css +++ b/lib/codemirror.css @@ -59,7 +59,12 @@ .cm-fat-cursor div.CodeMirror-cursors { z-index: 1; } - +.cm-fat-cursor-mark { + background-color: rgba(20, 255, 20, 0.5); + -webkit-animation: blink 1.06s steps(1) infinite; + -moz-animation: blink 1.06s steps(1) infinite; + animation: blink 1.06s steps(1) infinite; +} .cm-animate-fat-cursor { width: auto; border: 0; From 43d0324c4452e0d8bbc0782fb775cb4838f1d6cf Mon Sep 17 00:00:00 2001 From: Marijn Haverbeke Date: Mon, 16 Oct 2017 11:30:59 +0200 Subject: [PATCH 1110/1790] [continuecomment addon] Fix issue with single-line block comments The addon would think it still was in a block comment when one was opened and closed earlier on the line. Issue codemirror/google-modes#58 --- addon/comment/continuecomment.js | 4 ++-- mode/javascript/javascript.js | 2 +- 2 files changed, 3 insertions(+), 3 deletions(-) diff --git a/addon/comment/continuecomment.js b/addon/comment/continuecomment.js index 6552b09b..d92318b3 100644 --- a/addon/comment/continuecomment.js +++ b/addon/comment/continuecomment.js @@ -22,10 +22,10 @@ var insert = null; if (mode.blockCommentStart && mode.blockCommentContinue) { var line = cm.getLine(pos.line).slice(0, pos.ch) - var end = line.indexOf(mode.blockCommentEnd), found + var end = line.lastIndexOf(mode.blockCommentEnd), found if (end != -1 && end == pos.ch - mode.blockCommentEnd.length) { // Comment ended, don't continue it - } else if ((found = line.indexOf(mode.blockCommentStart)) > -1) { + } else if ((found = line.lastIndexOf(mode.blockCommentStart)) > -1 && found > end) { insert = line.slice(0, found) if (/\S/.test(insert)) { insert = "" diff --git a/mode/javascript/javascript.js b/mode/javascript/javascript.js index d6cc4771..61a6de4b 100644 --- a/mode/javascript/javascript.js +++ b/mode/javascript/javascript.js @@ -821,7 +821,7 @@ CodeMirror.defineMode("javascript", function(config, parserConfig) { electricInput: /^\s*(?:case .*?:|default:|\{|\})$/, blockCommentStart: jsonMode ? null : "/*", blockCommentEnd: jsonMode ? null : "*/", - blockCommentContinue: jsonMode ? null : " ", + blockCommentContinue: jsonMode ? null : " * ", lineComment: jsonMode ? null : "//", fold: "brace", closeBrackets: "()[]{}''\"\"``", From 1951460e52e2ec1d3615b5240123fe3c284d2e17 Mon Sep 17 00:00:00 2001 From: mtaran-google Date: Wed, 11 Oct 2017 17:45:53 -0700 Subject: [PATCH 1111/1790] [sublime bindings] Export macSublime & pcSublime keymaps This will make it easier to get programmatic access to the content of these keymaps, per #5020 --- keymap/sublime.js | 247 +++++++++++++++++++++++++++++++--------------- 1 file changed, 169 insertions(+), 78 deletions(-) diff --git a/keymap/sublime.js b/keymap/sublime.js index 98266e44..eeccab17 100644 --- a/keymap/sublime.js +++ b/keymap/sublime.js @@ -14,11 +14,8 @@ })(function(CodeMirror) { "use strict"; - var map = CodeMirror.keyMap.sublime = {fallthrough: "default"}; var cmds = CodeMirror.commands; var Pos = CodeMirror.Pos; - var mac = CodeMirror.keyMap["default"] == CodeMirror.keyMap.macDefault; - var ctrl = mac ? "Cmd-" : "Ctrl-"; // This is not exactly Sublime's algorithm. I couldn't make heads or tails of that. function findPosSubword(doc, start, dir) { @@ -52,16 +49,10 @@ }); } - var goSubwordCombo = mac ? "Ctrl-" : "Alt-"; + cmds.goSubwordLeft = function(cm) { moveSubword(cm, -1); }; + cmds.goSubwordRight = function(cm) { moveSubword(cm, 1); }; - cmds[map[goSubwordCombo + "Left"] = "goSubwordLeft"] = function(cm) { moveSubword(cm, -1); }; - cmds[map[goSubwordCombo + "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) { + cmds.scrollLineUp = function(cm) { var info = cm.getScrollInfo(); if (!cm.somethingSelected()) { var visibleBottomLine = cm.lineAtHeight(info.top + info.clientHeight, "local"); @@ -70,7 +61,7 @@ } cm.scrollTo(null, info.top - cm.defaultTextHeight()); }; - cmds[map[scrollLineCombo + "Down"] = "scrollLineDown"] = function(cm) { + cmds.scrollLineDown = function(cm) { var info = cm.getScrollInfo(); if (!cm.somethingSelected()) { var visibleTopLine = cm.lineAtHeight(info.top, "local")+1; @@ -80,7 +71,7 @@ cm.scrollTo(null, info.top + cm.defaultTextHeight()); }; - cmds[map["Shift-" + ctrl + "L"] = "splitSelectionByLine"] = function(cm) { + cmds.splitSelectionByLine = function(cm) { var ranges = cm.listSelections(), lineRanges = []; for (var i = 0; i < ranges.length; i++) { var from = ranges[i].from(), to = ranges[i].to(); @@ -92,14 +83,12 @@ cm.setSelections(lineRanges, 0); }; - map["Shift-Tab"] = "indentLess"; - - cmds[map["Esc"] = "singleSelectionTop"] = function(cm) { + cmds.singleSelectionTop = function(cm) { var range = cm.listSelections()[0]; cm.setSelection(range.anchor, range.head, {scroll: false}); }; - cmds[map[ctrl + "L"] = "selectLine"] = function(cm) { + cmds.selectLine = function(cm) { var ranges = cm.listSelections(), extended = []; for (var i = 0; i < ranges.length; i++) { var range = ranges[i]; @@ -109,8 +98,6 @@ cm.setSelections(extended); }; - map["Shift-Ctrl-K"] = "deleteLine"; - function insertLine(cm, above) { if (cm.isReadOnly()) return CodeMirror.Pass cm.operation(function() { @@ -129,9 +116,9 @@ cm.execCommand("indentAuto"); } - cmds[map[ctrl + "Enter"] = "insertLineAfter"] = function(cm) { return insertLine(cm, false); }; + cmds.insertLineAfter = function(cm) { return insertLine(cm, false); }; - cmds[map["Shift-" + ctrl + "Enter"] = "insertLineBefore"] = function(cm) { return insertLine(cm, true); }; + cmds.insertLineBefore = function(cm) { return insertLine(cm, true); }; function wordAt(cm, pos) { var start = pos.ch, end = start, line = cm.getLine(pos.line); @@ -140,7 +127,7 @@ return {from: Pos(pos.line, start), to: Pos(pos.line, end), word: line.slice(start, end)}; } - cmds[map[ctrl + "D"] = "selectNextOccurrence"] = function(cm) { + cmds.selectNextOccurrence = function(cm) { var from = cm.getCursor("from"), to = cm.getCursor("to"); var fullWord = cm.state.sublimeFindFullWord == cm.doc.sel; if (CodeMirror.cmpPos(from, to) == 0) { @@ -177,10 +164,8 @@ } cm.setSelections(newRanges); } - - var addCursorToLineCombo = mac ? "Shift-Cmd" : 'Alt-Ctrl'; - cmds[map[addCursorToLineCombo + "Up"] = "addCursorToPrevLine"] = function(cm) { addCursorToSelection(cm, -1); }; - cmds[map[addCursorToLineCombo + "Down"] = "addCursorToNextLine"] = function(cm) { addCursorToSelection(cm, 1); }; + cmds.addCursorToPrevLine = function(cm) { addCursorToSelection(cm, -1); }; + cmds.addCursorToNextLine = function(cm) { addCursorToSelection(cm, 1); }; function isSelectedRange(ranges, from, to) { for (var i = 0; i < ranges.length; i++) @@ -209,14 +194,14 @@ return true; } - cmds[map["Shift-" + ctrl + "Space"] = "selectScope"] = function(cm) { + cmds.selectScope = function(cm) { selectBetweenBrackets(cm) || cm.execCommand("selectAll"); }; - cmds[map["Shift-" + ctrl + "M"] = "selectBetweenBrackets"] = function(cm) { + cmds.selectBetweenBrackets = function(cm) { if (!selectBetweenBrackets(cm)) return CodeMirror.Pass; }; - cmds[map[ctrl + "M"] = "goToBracket"] = function(cm) { + cmds.goToBracket = function(cm) { cm.extendSelectionsBy(function(range) { var next = cm.scanForBracket(range.head, 1); if (next && CodeMirror.cmpPos(next.pos, range.head) != 0) return next.pos; @@ -225,9 +210,7 @@ }); }; - var swapLineCombo = mac ? "Cmd-Ctrl-" : "Shift-Ctrl-"; - - cmds[map[swapLineCombo + "Up"] = "swapLineUp"] = function(cm) { + cmds.swapLineUp = function(cm) { if (cm.isReadOnly()) return CodeMirror.Pass var ranges = cm.listSelections(), linesToMove = [], at = cm.firstLine() - 1, newSels = []; for (var i = 0; i < ranges.length; i++) { @@ -254,7 +237,7 @@ }); }; - cmds[map[swapLineCombo + "Down"] = "swapLineDown"] = function(cm) { + cmds.swapLineDown = function(cm) { if (cm.isReadOnly()) return CodeMirror.Pass var ranges = cm.listSelections(), linesToMove = [], at = cm.lastLine() + 1; for (var i = ranges.length - 1; i >= 0; i--) { @@ -278,11 +261,11 @@ }); }; - cmds[map[ctrl + "/"] = "toggleCommentIndented"] = function(cm) { + cmds.toggleCommentIndented = function(cm) { cm.toggleComment({ indent: true }); } - cmds[map[ctrl + "J"] = "joinLines"] = function(cm) { + cmds.joinLines = function(cm) { var ranges = cm.listSelections(), joined = []; for (var i = 0; i < ranges.length; i++) { var range = ranges[i], from = range.from(); @@ -310,7 +293,7 @@ }); }; - cmds[map["Shift-" + ctrl + "D"] = "duplicateLine"] = function(cm) { + cmds.duplicateLine = function(cm) { cm.operation(function() { var rangeCount = cm.listSelections().length; for (var i = 0; i < rangeCount; i++) { @@ -324,7 +307,6 @@ }); }; - if (!mac) map[ctrl + "T"] = "transposeChars"; function sortLines(cm, caseSensitive) { if (cm.isReadOnly()) return CodeMirror.Pass @@ -362,10 +344,10 @@ }); } - cmds[map["F9"] = "sortLines"] = function(cm) { sortLines(cm, true); }; - cmds[map[ctrl + "F9"] = "sortLinesInsensitive"] = function(cm) { sortLines(cm, false); }; + cmds.sortLines = function(cm) { sortLines(cm, true); }; + cmds.sortLinesInsensitive = function(cm) { sortLines(cm, false); }; - cmds[map["F2"] = "nextBookmark"] = function(cm) { + cmds.nextBookmark = function(cm) { var marks = cm.state.sublimeBookmarks; if (marks) while (marks.length) { var current = marks.shift(); @@ -377,7 +359,7 @@ } }; - cmds[map["Shift-F2"] = "prevBookmark"] = function(cm) { + cmds.prevBookmark = function(cm) { var marks = cm.state.sublimeBookmarks; if (marks) while (marks.length) { marks.unshift(marks.pop()); @@ -389,7 +371,7 @@ } }; - cmds[map[ctrl + "F2"] = "toggleBookmark"] = function(cm) { + cmds.toggleBookmark = function(cm) { var ranges = cm.listSelections(); var marks = cm.state.sublimeBookmarks || (cm.state.sublimeBookmarks = []); for (var i = 0; i < ranges.length; i++) { @@ -409,13 +391,13 @@ } }; - cmds[map["Shift-" + ctrl + "F2"] = "clearBookmarks"] = function(cm) { + cmds.clearBookmarks = function(cm) { var marks = cm.state.sublimeBookmarks; if (marks) for (var i = 0; i < marks.length; i++) marks[i].clear(); marks.length = 0; }; - cmds[map["Alt-F2"] = "selectBookmarks"] = function(cm) { + cmds.selectBookmarks = function(cm) { var marks = cm.state.sublimeBookmarks, ranges = []; if (marks) for (var i = 0; i < marks.length; i++) { var found = marks[i].find(); @@ -428,10 +410,6 @@ cm.setSelections(ranges, 0); }; - map["Alt-Q"] = "wrapLines"; - - var cK = ctrl + "K "; - function modifyWordOrSelection(cm, mod) { cm.operation(function() { var ranges = cm.listSelections(), indices = [], replacements = []; @@ -451,9 +429,7 @@ }); } - map[cK + ctrl + "Backspace"] = "delLineLeft"; - - cmds[map["Backspace"] = "smartBackspace"] = function(cm) { + cmds.smartBackspace = function(cm) { if (cm.somethingSelected()) return CodeMirror.Pass; cm.operation(function() { @@ -481,7 +457,7 @@ }); }; - cmds[map[cK + ctrl + "K"] = "delLineRight"] = function(cm) { + cmds.delLineRight = function(cm) { cm.operation(function() { var ranges = cm.listSelections(); for (var i = ranges.length - 1; i >= 0; i--) @@ -490,22 +466,22 @@ }); }; - cmds[map[cK + ctrl + "U"] = "upcaseAtCursor"] = function(cm) { + cmds.upcaseAtCursor = function(cm) { modifyWordOrSelection(cm, function(str) { return str.toUpperCase(); }); }; - cmds[map[cK + ctrl + "L"] = "downcaseAtCursor"] = function(cm) { + cmds.downcaseAtCursor = function(cm) { modifyWordOrSelection(cm, function(str) { return str.toLowerCase(); }); }; - cmds[map[cK + ctrl + "Space"] = "setSublimeMark"] = function(cm) { + cmds.setSublimeMark = function(cm) { if (cm.state.sublimeMark) cm.state.sublimeMark.clear(); cm.state.sublimeMark = cm.setBookmark(cm.getCursor()); }; - cmds[map[cK + ctrl + "A"] = "selectToSublimeMark"] = function(cm) { + cmds.selectToSublimeMark = function(cm) { var found = cm.state.sublimeMark && cm.state.sublimeMark.find(); if (found) cm.setSelection(cm.getCursor(), found); }; - cmds[map[cK + ctrl + "W"] = "deleteToSublimeMark"] = function(cm) { + cmds.deleteToSublimeMark = function(cm) { var found = cm.state.sublimeMark && cm.state.sublimeMark.find(); if (found) { var from = cm.getCursor(), to = found; @@ -514,7 +490,7 @@ cm.replaceRange("", from, to); } }; - cmds[map[cK + ctrl + "X"] = "swapWithSublimeMark"] = function(cm) { + cmds.swapWithSublimeMark = function(cm) { var found = cm.state.sublimeMark && cm.state.sublimeMark.find(); if (found) { cm.state.sublimeMark.clear(); @@ -522,19 +498,17 @@ cm.setCursor(found); } }; - cmds[map[cK + ctrl + "Y"] = "sublimeYank"] = function(cm) { + cmds.sublimeYank = function(cm) { if (cm.state.sublimeKilled != null) cm.replaceSelection(cm.state.sublimeKilled, null, "paste"); }; - map[cK + ctrl + "G"] = "clearBookmarks"; - cmds[map[cK + ctrl + "C"] = "showInCenter"] = function(cm) { + cmds.showInCenter = function(cm) { var pos = cm.cursorCoords(null, "local"); cm.scrollTo(null, (pos.top + pos.bottom) / 2 - cm.getScrollInfo().clientHeight / 2); }; - var selectLinesCombo = mac ? "Ctrl-Shift-" : "Ctrl-Alt-"; - cmds[map[selectLinesCombo + "Up"] = "selectLinesUpward"] = function(cm) { + cmds.selectLinesUpward = function(cm) { cm.operation(function() { var ranges = cm.listSelections(); for (var i = 0; i < ranges.length; i++) { @@ -544,7 +518,7 @@ } }); }; - cmds[map[selectLinesCombo + "Down"] = "selectLinesDownward"] = function(cm) { + cmds.selectLinesDownward = function(cm) { cm.operation(function() { var ranges = cm.listSelections(); for (var i = 0; i < ranges.length; i++) { @@ -583,9 +557,9 @@ cm.setSelection(target.from, target.to); } }; - cmds[map[ctrl + "F3"] = "findUnder"] = function(cm) { findAndGoTo(cm, true); }; - cmds[map["Shift-" + ctrl + "F3"] = "findUnderPrevious"] = function(cm) { findAndGoTo(cm,false); }; - cmds[map["Alt-F3"] = "findAllUnder"] = function(cm) { + cmds.findUnder = function(cm) { findAndGoTo(cm, true); }; + cmds.findUnderPrevious = function(cm) { findAndGoTo(cm,false); }; + cmds.findAllUnder = function(cm) { var target = getTarget(cm); if (!target) return; var cur = cm.getSearchCursor(target.query); @@ -599,15 +573,132 @@ cm.setSelections(matches, primaryIndex); }; - map["Shift-" + ctrl + "["] = "fold"; - map["Shift-" + ctrl + "]"] = "unfold"; - map[cK + ctrl + "0"] = map[cK + ctrl + "J"] = "unfoldAll"; - - map[ctrl + "I"] = "findIncremental"; - map["Shift-" + ctrl + "I"] = "findIncrementalReverse"; - map[ctrl + "H"] = "replace"; - map["F3"] = "findNext"; - map["Shift-F3"] = "findPrev"; - CodeMirror.normalizeKeyMap(map); + var keyMap = CodeMirror.keyMap; + keyMap.macSublime = { + "Cmd-Left": "goLineStartSmart", + "Shift-Tab": "indentLess", + "Shift-Ctrl-K": "deleteLine", + "Alt-Q": "wrapLines", + "Ctrl-Left": "goSubwordLeft", + "Ctrl-Right": "goSubwordRight", + "Ctrl-Alt-Up": "scrollLineUp", + "Ctrl-Alt-Down": "scrollLineDown", + "Cmd-L": "selectLine", + "Shift-Cmd-L": "splitSelectionByLine", + "Esc": "singleSelectionTop", + "Cmd-Enter": "insertLineAfter", + "Shift-Cmd-Enter": "insertLineBefore", + "Cmd-D": "selectNextOccurrence", + "Shift-Cmd-Up": "addCursorToPrevLine", + "Shift-Cmd-Down": "addCursorToNextLine", + "Shift-Cmd-Space": "selectScope", + "Shift-Cmd-M": "selectBetweenBrackets", + "Cmd-M": "goToBracket", + "Cmd-Ctrl-Up": "swapLineUp", + "Cmd-Ctrl-Down": "swapLineDown", + "Cmd-/": "toggleCommentIndented", + "Cmd-J": "joinLines", + "Shift-Cmd-D": "duplicateLine", + "F9": "sortLines", + "Cmd-F9": "sortLinesInsensitive", + "F2": "nextBookmark", + "Shift-F2": "prevBookmark", + "Cmd-F2": "toggleBookmark", + "Shift-Cmd-F2": "clearBookmarks", + "Alt-F2": "selectBookmarks", + "Backspace": "smartBackspace", + "Cmd-K Cmd-K": "delLineRight", + "Cmd-K Cmd-U": "upcaseAtCursor", + "Cmd-K Cmd-L": "downcaseAtCursor", + "Cmd-K Cmd-Space": "setSublimeMark", + "Cmd-K Cmd-A": "selectToSublimeMark", + "Cmd-K Cmd-W": "deleteToSublimeMark", + "Cmd-K Cmd-X": "swapWithSublimeMark", + "Cmd-K Cmd-Y": "sublimeYank", + "Cmd-K Cmd-C": "showInCenter", + "Cmd-K Cmd-G": "clearBookmarks", + "Cmd-K Cmd-Backspace": "delLineLeft", + "Cmd-K Cmd-0": "unfoldAll", + "Cmd-K Cmd-J": "unfoldAll", + "Ctrl-Shift-Up": "selectLinesUpward", + "Ctrl-Shift-Down": "selectLinesDownward", + "Cmd-F3": "findUnder", + "Shift-Cmd-F3": "findUnderPrevious", + "Alt-F3": "findAllUnder", + "Shift-Cmd-[": "fold", + "Shift-Cmd-]": "unfold", + "Cmd-I": "findIncremental", + "Shift-Cmd-I": "findIncrementalReverse", + "Cmd-H": "replace", + "F3": "findNext", + "Shift-F3": "findPrev", + "fallthrough": "pcDefault" + }; + CodeMirror.normalizeKeyMap(keyMap.macSublime); + + keyMap.pcSublime = { + "Shift-Tab": "indentLess", + "Shift-Ctrl-K": "deleteLine", + "Alt-Q": "wrapLines", + "Ctrl-T": "transposeChars", + "Alt-Left": "goSubwordLeft", + "Alt-Right": "goSubwordRight", + "Ctrl-Up": "scrollLineUp", + "Ctrl-Down": "scrollLineDown", + "Ctrl-L": "selectLine", + "Shift-Ctrl-L": "splitSelectionByLine", + "Esc": "singleSelectionTop", + "Ctrl-Enter": "insertLineAfter", + "Shift-Ctrl-Enter": "insertLineBefore", + "Ctrl-D": "selectNextOccurrence", + "Alt-CtrlUp": "addCursorToPrevLine", + "Alt-CtrlDown": "addCursorToNextLine", + "Shift-Ctrl-Space": "selectScope", + "Shift-Ctrl-M": "selectBetweenBrackets", + "Ctrl-M": "goToBracket", + "Shift-Ctrl-Up": "swapLineUp", + "Shift-Ctrl-Down": "swapLineDown", + "Ctrl-/": "toggleCommentIndented", + "Ctrl-J": "joinLines", + "Shift-Ctrl-D": "duplicateLine", + "F9": "sortLines", + "Ctrl-F9": "sortLinesInsensitive", + "F2": "nextBookmark", + "Shift-F2": "prevBookmark", + "Ctrl-F2": "toggleBookmark", + "Shift-Ctrl-F2": "clearBookmarks", + "Alt-F2": "selectBookmarks", + "Backspace": "smartBackspace", + "Ctrl-K Ctrl-K": "delLineRight", + "Ctrl-K Ctrl-U": "upcaseAtCursor", + "Ctrl-K Ctrl-L": "downcaseAtCursor", + "Ctrl-K Ctrl-Space": "setSublimeMark", + "Ctrl-K Ctrl-A": "selectToSublimeMark", + "Ctrl-K Ctrl-W": "deleteToSublimeMark", + "Ctrl-K Ctrl-X": "swapWithSublimeMark", + "Ctrl-K Ctrl-Y": "sublimeYank", + "Ctrl-K Ctrl-C": "showInCenter", + "Ctrl-K Ctrl-G": "clearBookmarks", + "Ctrl-K Ctrl-Backspace": "delLineLeft", + "Ctrl-K Ctrl-0": "unfoldAll", + "Ctrl-K Ctrl-J": "unfoldAll", + "Ctrl-Alt-Up": "selectLinesUpward", + "Ctrl-Alt-Down": "selectLinesDownward", + "Ctrl-F3": "findUnder", + "Shift-Ctrl-F3": "findUnderPrevious", + "Alt-F3": "findAllUnder", + "Shift-Ctrl-[": "fold", + "Shift-Ctrl-]": "unfold", + "Ctrl-I": "findIncremental", + "Shift-Ctrl-I": "findIncrementalReverse", + "Ctrl-H": "replace", + "F3": "findNext", + "Shift-F3": "findPrev", + "fallthrough": "pcDefault" + }; + CodeMirror.normalizeKeyMap(keyMap.pcSublime); + + var mac = keyMap.default == keyMap.macDefault; + keyMap.sublime = mac ? keyMap.macSublime : keyMap.pcSublime; }); From c1196ebc6260d4652ced5eee254d8c5d3cea10dd Mon Sep 17 00:00:00 2001 From: Marijn Haverbeke Date: Mon, 16 Oct 2017 16:17:48 +0200 Subject: [PATCH 1112/1790] [sublime keymap] Fix fallthrough for mac bindings Issue #5022 --- keymap/sublime.js | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/keymap/sublime.js b/keymap/sublime.js index eeccab17..08c9ebfb 100644 --- a/keymap/sublime.js +++ b/keymap/sublime.js @@ -633,7 +633,7 @@ "Cmd-H": "replace", "F3": "findNext", "Shift-F3": "findPrev", - "fallthrough": "pcDefault" + "fallthrough": "macDefault" }; CodeMirror.normalizeKeyMap(keyMap.macSublime); From e5e2aefd0874463ff5d9aeb955d869e7853658b5 Mon Sep 17 00:00:00 2001 From: Guan Gui Date: Mon, 16 Oct 2017 15:45:34 +1100 Subject: [PATCH 1113/1790] [closebrackets addon] Use editor line separator when exploding lines Issue #5031 --- addon/edit/closebrackets.js | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-) diff --git a/addon/edit/closebrackets.js b/addon/edit/closebrackets.js index 36aec0d4..7b07f7fd 100644 --- a/addon/edit/closebrackets.js +++ b/addon/edit/closebrackets.js @@ -84,7 +84,8 @@ if (!around || explode.indexOf(around) % 2 != 0) return CodeMirror.Pass; } cm.operation(function() { - cm.replaceSelection("\n\n", null); + var linesep = cm.lineSeparator() || "\n"; + cm.replaceSelection(linesep + linesep, null); cm.execCommand("goCharLeft"); ranges = cm.listSelections(); for (var i = 0; i < ranges.length; i++) { From deaa842dc6f1711874c1cacc6b984f7264932f83 Mon Sep 17 00:00:00 2001 From: Markus Olsson Date: Tue, 17 Oct 2017 12:15:40 +0200 Subject: [PATCH 1114/1790] [runmode addon] Include CodeMirror.innerMode in runmode.node.js The markdown mode uses `CodeMirror.innerMode` which isn't defined in the stripped down node runmode. Since markdown is the only mode (AFAICT) that uses it I'm not sure if it would make more sense rewrite it to not use it but this seemed like the least risky option. --- addon/runmode/runmode.node.js | 12 ++++++++++++ 1 file changed, 12 insertions(+) diff --git a/addon/runmode/runmode.node.js b/addon/runmode/runmode.node.js index 093cb308..21c72696 100644 --- a/addon/runmode/runmode.node.js +++ b/addon/runmode/runmode.node.js @@ -163,6 +163,18 @@ exports.getMode = function(options, spec) { return modeObj; }; + +exports.innerMode = function(mode, state) { + var info; + while (mode.innerMode) { + info = mode.innerMode(state); + if (!info || info.mode == mode) break; + state = info.state; + mode = info.mode; + } + return info || {mode: mode, state: state}; +} + exports.registerHelper = exports.registerGlobalHelper = Math.min; exports.runMode = function(string, modespec, callback, options) { From 79d266e60a8d02a865c7cf9340628a0f5d920394 Mon Sep 17 00:00:00 2001 From: Marijn Haverbeke Date: Thu, 29 Jun 2017 14:20:30 +0200 Subject: [PATCH 1115/1790] Add a baseToken method to string streams That overlay modes can use to access the underlying token info. --- doc/manual.html | 6 ++++++ src/line/highlight.js | 16 +++++++++++++++- src/util/StringStream.js | 4 ++++ test/test.js | 15 +++++++++++++++ 4 files changed, 40 insertions(+), 1 deletion(-) diff --git a/doc/manual.html b/doc/manual.html index 67d5e425..0ab9cf0d 100644 --- a/doc/manual.html +++ b/doc/manual.html @@ -3261,6 +3261,12 @@

              Writing CodeMirror Modes

              one, in order to scan ahead across line boundaries. Note that you want to do this carefully, since looking far ahead will make mode state caching much less effective.
    + +
    baseToken() → ?{type: ?string, size: number}
    +
    Modes added + through addOverlay + (and only such modes) can use this method to inspect + the current token produced by the underlying mode.

    By default, blank lines are simply skipped when diff --git a/src/line/highlight.js b/src/line/highlight.js index 430d86a2..82e2aeee 100644 --- a/src/line/highlight.js +++ b/src/line/highlight.js @@ -18,6 +18,8 @@ class Context { this.doc = doc this.line = line this.maxLookAhead = lookAhead || 0 + this.baseTokens = null + this.baseTokenPos = 1 } lookAhead(n) { @@ -26,6 +28,15 @@ class Context { return line } + baseToken(n) { + if (!this.baseTokens) return null + while (this.baseTokens[this.baseTokenPos] >= n) + this.baseTokenPos += 2 + let type = this.baseTokens[this.baseTokenPos + 1] + return {type: type && type.replace(/( |^)overlay .*/, ""), + size: this.baseTokens[this.baseTokenPos] - n} + } + nextLine() { this.line++ if (this.maxLookAhead > 0) this.maxLookAhead-- @@ -60,6 +71,7 @@ export function highlightLine(cm, line, context, forceToEnd) { // Run overlays, adjust style array. for (let o = 0; o < cm.state.overlays.length; ++o) { + context.baseTokens = st let overlay = cm.state.overlays[o], i = 1, at = 0 context.state = true runMode(cm, line.text, overlay.mode, context, (end, style) => { @@ -83,8 +95,10 @@ export function highlightLine(cm, line, context, forceToEnd) { } } }, lineClasses) + context.state = state + context.baseTokens = null + context.baseTokenPos = 1 } - context.state = state return {styles: st, classes: lineClasses.bgClass || lineClasses.textClass ? lineClasses : null} } diff --git a/src/util/StringStream.js b/src/util/StringStream.js index ac9555f1..a14b1b64 100644 --- a/src/util/StringStream.js +++ b/src/util/StringStream.js @@ -81,6 +81,10 @@ class StringStream { let oracle = this.lineOracle return oracle && oracle.lookAhead(n) } + baseToken() { + let oracle = this.lineOracle + return oracle && oracle.baseToken(this.pos) + } } export default StringStream diff --git a/test/test.js b/test/test.js index 9c768e3f..415dd9f0 100644 --- a/test/test.js +++ b/test/test.js @@ -2218,6 +2218,21 @@ testCM("getTokenTypeAt", function(cm) { eq(cm.getTokenTypeAt(Pos(0, 6)), "string"); }, {value: "1 + 'foo'", mode: "javascript"}); +testCM("addOverlay", function(cm) { + cm.addOverlay({ + token: function(stream) { + var base = stream.baseToken() + if (!/comment/.test(base.type) && stream.match(/\d+/)) return "x" + stream.next() + } + }) + var x = byClassName(cm.getWrapperElement(), "cm-x") + is(x.length, 1) + is(x[0].textContent, "233") + cm.replaceRange("", Pos(0, 4), Pos(0, 6)) + is(byClassName(cm.getWrapperElement(), "cm-x").length, 2) +}, {value: "foo /* 100 */\nbar + 233;\nbaz", mode: "javascript"}) + testCM("resizeLineWidget", function(cm) { addDoc(cm, 200, 3); var widget = document.createElement("pre"); From 42a26328333a052583f1bd4623bfb8e42717e1dd Mon Sep 17 00:00:00 2001 From: vtripolitakis Date: Tue, 17 Oct 2017 21:59:21 +0300 Subject: [PATCH 1116/1790] [sql mode] Fix representation of table hinting in demo page --- mode/sql/index.html | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/mode/sql/index.html b/mode/sql/index.html index e12b289b..b434f0f4 100644 --- a/mode/sql/index.html +++ b/mode/sql/index.html @@ -78,8 +78,8 @@

    SQL Mode for CodeMirror

    autofocus: true, extraKeys: {"Ctrl-Space": "autocomplete"}, hintOptions: {tables: { - users: {name: null, score: null, birthDate: null}, - countries: {name: null, population: null, size: null} + users: ["name", "score", "birthDate"], + countries: ["name", "population", "size"] }} }); }; From d14888a70bb4d172a60d968682f0e28ec5065c96 Mon Sep 17 00:00:00 2001 From: Marijn Haverbeke Date: Wed, 18 Oct 2017 11:11:11 +0200 Subject: [PATCH 1117/1790] [javascript mode] Fix bug in object literal spread parsing Closes #5036 --- 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 61a6de4b..ca9fe8ba 100644 --- a/mode/javascript/javascript.js +++ b/mode/javascript/javascript.js @@ -514,7 +514,7 @@ CodeMirror.defineMode("javascript", function(config, parserConfig) { } else if (type == "[") { return cont(expression, expect("]"), afterprop); } else if (type == "spread") { - return cont(expression, afterprop); + return cont(expressionNoComma, afterprop); } else if (value == "*") { cx.marked = "keyword"; return cont(objprop); From d323ad7236a4b611b7c0898fe660136df6faaf43 Mon Sep 17 00:00:00 2001 From: Pi Delport Date: Wed, 18 Oct 2017 14:20:24 +0200 Subject: [PATCH 1118/1790] (Update my name) --- AUTHORS | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/AUTHORS b/AUTHORS index 12643570..e4206048 100644 --- a/AUTHORS +++ b/AUTHORS @@ -529,7 +529,7 @@ Peter Kroon Philipp A Philip Stadermann Pierre Gerold -Piët Delport +Pi Delport Pieter Ouwerkerk Pontus Melke prasanthj From cf799958cf8f7ec89c808b7400fe248494b61674 Mon Sep 17 00:00:00 2001 From: Marijn Haverbeke Date: Wed, 18 Oct 2017 14:27:02 +0200 Subject: [PATCH 1119/1790] [bin/authors.sh] Make sure changed name doesn't resurface from git history Issue #5037 --- bin/authors.sh | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/bin/authors.sh b/bin/authors.sh index b3ee99c6..3f228c1f 100755 --- a/bin/authors.sh +++ b/bin/authors.sh @@ -1,6 +1,6 @@ # Combine existing list of authors with everyone known in git, sort, add header. tail --lines=+3 AUTHORS > AUTHORS.tmp -git log --format='%aN' >> AUTHORS.tmp +git log --format='%aN' | grep -v "Piët Delport" >> AUTHORS.tmp echo -e "List of CodeMirror contributors. Updated before every release.\n" > AUTHORS sort -u AUTHORS.tmp >> AUTHORS rm -f AUTHORS.tmp From 40a818295aef34d7fd73e9c036e720334336ae98 Mon Sep 17 00:00:00 2001 From: Marijn Haverbeke Date: Wed, 18 Oct 2017 17:02:48 +0200 Subject: [PATCH 1120/1790] Fix baseToken method See https://github.com/codemirror/CodeMirror/commit/79d266e60a8d02a865c7cf9340628a0f5d920394#commitcomment-25055107 --- src/line/highlight.js | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/line/highlight.js b/src/line/highlight.js index 82e2aeee..13921585 100644 --- a/src/line/highlight.js +++ b/src/line/highlight.js @@ -30,7 +30,7 @@ class Context { baseToken(n) { if (!this.baseTokens) return null - while (this.baseTokens[this.baseTokenPos] >= n) + while (this.baseTokens[this.baseTokenPos] < n) this.baseTokenPos += 2 let type = this.baseTokens[this.baseTokenPos + 1] return {type: type && type.replace(/( |^)overlay .*/, ""), From a7f6e9f0a1ab1b1f3e9ca008579a17808e0e417f Mon Sep 17 00:00:00 2001 From: Marijn Haverbeke Date: Fri, 20 Oct 2017 16:01:41 +0200 Subject: [PATCH 1121/1790] Fix baseToken to actually return the token ahead when on boundary --- src/line/highlight.js | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/line/highlight.js b/src/line/highlight.js index 13921585..c5e6b8aa 100644 --- a/src/line/highlight.js +++ b/src/line/highlight.js @@ -30,7 +30,7 @@ class Context { baseToken(n) { if (!this.baseTokens) return null - while (this.baseTokens[this.baseTokenPos] < n) + while (this.baseTokens[this.baseTokenPos] <= n) this.baseTokenPos += 2 let type = this.baseTokens[this.baseTokenPos + 1] return {type: type && type.replace(/( |^)overlay .*/, ""), From f936d89e4a2aa68eee964f57d14f875ee1d71ff4 Mon Sep 17 00:00:00 2001 From: Marijn Haverbeke Date: Fri, 20 Oct 2017 17:35:00 +0200 Subject: [PATCH 1122/1790] Mark version 5.31.0 --- AUTHORS | 10 +++++++++- CHANGELOG.md | 18 ++++++++++++++++++ doc/manual.html | 4 ++-- doc/releases.html | 11 +++++++++++ index.html | 2 +- package.json | 2 +- src/edit/main.js | 2 +- 7 files changed, 43 insertions(+), 6 deletions(-) diff --git a/AUTHORS b/AUTHORS index e4206048..f800b86b 100644 --- a/AUTHORS +++ b/AUTHORS @@ -48,6 +48,7 @@ Andreas Reischuck Andres Taylor Andre von Houck Andrew Cheng +Andrew Dassonville Andrey Fedorov Andrey Klyuchnikov Andrey Lushnikov @@ -242,6 +243,7 @@ Grant Skinner greengiant Gregory Koberger Grzegorz Mazur +Guan Gui Guillaume Massé Guillaume Massé guraga @@ -253,6 +255,7 @@ Harshvardhan Gupta Hasan Karahan Hector Oswaldo Caballero Hendrik Wallbaum +Henrik Haugbølle Herculano Campos Hiroyuki Makino hitsthings @@ -308,6 +311,7 @@ jem (graphite) Jeremy Parmenter Jim Jim Avery +jkaplon JobJob jochenberger Jochen Berger @@ -341,6 +345,7 @@ ju1ius Juan Benavides Romero Jucovschi Constantin Juho Vuori +Julien CROUZET Julien Rebetez Justin Andresen Justin Hileman @@ -411,6 +416,7 @@ Mark Lentczner Marko Bonaci Mark Peace Markus Bordihn +Markus Olsson Martin Balek Martín Gaitán Martin Hasoň @@ -528,8 +534,8 @@ peterkroon Peter Kroon Philipp A Philip Stadermann -Pierre Gerold Pi Delport +Pierre Gerold Pieter Ouwerkerk Pontus Melke prasanthj @@ -633,6 +639,7 @@ thanasis TheHowl themrmax think +Thomas Brouard Thomas Dvornik Thomas Kluyver Thomas Schmid @@ -662,6 +669,7 @@ vf Victor Bocharsky Vincent Woo Volker Mische +vtripolitakis Weiyan Shao wenli Wes Cossick diff --git a/CHANGELOG.md b/CHANGELOG.md index ff7d6e23..409c7234 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -1,3 +1,21 @@ +## 5.31.0 (2017-10-20) + +### Bug fixes + +Further improve selection drawing and cursor motion in right-to-left documents. + +[vim bindings](http://codemirror.net/demo/vim.html): Fix ctrl-w behavior, support quote-dot and backtick-dot marks, make the wide cursor visible in contentEditable [input mode](http://codemirror.net/doc/manual.html#option_contentEditable). + +[continuecomment addon](http://codemirror.net/doc/manual.html#addon_continuecomment): Fix bug when pressing enter after a single-line block comment. + +[markdown mode](http://codemirror.net/mode/markdown/): Fix issue with leaving indented fenced code blocks. + +[javascript mode](http://codemirror.net/mode/javascript/): Fix bad parsing of operators without spaces between them. Fix some corner cases around semicolon insertion and regexps. + +### New features + +Modes added with [`addOverlay`](http://codemirror.net/doc/manual.html#addOverlay) now have access to a [`baseToken`](http://codemirror.net/doc/manual.html#baseToken) method on their input stream, giving access to the tokens of the underlying mode. + ## 5.30.0 (2017-09-20) ### Bug fixes diff --git a/doc/manual.html b/doc/manual.html index 0ab9cf0d..f2f04459 100644 --- a/doc/manual.html +++ b/doc/manual.html @@ -69,7 +69,7 @@

    User manual and reference guide - version 5.30.0 + version 5.31.0

    CodeMirror is a code-editor component that can be embedded in @@ -3262,7 +3262,7 @@

    Writing CodeMirror Modes

    you want to do this carefully, since looking far ahead will make mode state caching much less effective. -
    baseToken() → ?{type: ?string, size: number}
    +
    baseToken() → ?{type: ?string, size: number}
    Modes added through addOverlay (and only such modes) can use this method to inspect diff --git a/doc/releases.html b/doc/releases.html index e16ab6dd..23de4c80 100644 --- a/doc/releases.html +++ b/doc/releases.html @@ -30,6 +30,17 @@

    Release notes and version history

    Version 5.x

    +

    20-10-2017: Version 5.31.0:

    + +
      +
    • Modes added with addOverlay now have access to a baseToken method on their input stream, giving access to the tokens of the underlying mode.
    • +
    • Further improve selection drawing and cursor motion in right-to-left documents.
    • +
    • vim bindings: Fix ctrl-w behavior, support quote-dot and backtick-dot marks, make the wide cursor visible in contentEditable input mode.
    • +
    • continuecomment addon: Fix bug when pressing enter after a single-line block comment.
    • +
    • markdown mode: Fix issue with leaving indented fenced code blocks.
    • +
    • javascript mode: Fix bad parsing of operators without spaces between them. Fix some corner cases around semicolon insertion and regexps.
    • +
    +

    20-09-2017: Version 5.30.0:

      diff --git a/index.html b/index.html index d161e77c..555e4804 100644 --- a/index.html +++ b/index.html @@ -96,7 +96,7 @@

      This is CodeMirror

      - Get the current version: 5.30.0.
      + Get the current version: 5.31.0.
      You can see the code,
      read the release notes,
      or study the user manual. diff --git a/package.json b/package.json index f251b238..2241ad93 100644 --- a/package.json +++ b/package.json @@ -1,6 +1,6 @@ { "name": "codemirror", - "version": "5.30.0", + "version": "5.31.0", "main": "lib/codemirror.js", "style": "lib/codemirror.css", "description": "Full-featured in-browser code editor", diff --git a/src/edit/main.js b/src/edit/main.js index 4298c61f..b0fd5de8 100644 --- a/src/edit/main.js +++ b/src/edit/main.js @@ -66,4 +66,4 @@ import { addLegacyProps } from "./legacy" addLegacyProps(CodeMirror) -CodeMirror.version = "5.30.0" +CodeMirror.version = "5.31.0" From d6fea23efc1a9c1e16b18541691893860c653a13 Mon Sep 17 00:00:00 2001 From: Marijn Haverbeke Date: Fri, 20 Oct 2017 17:36:29 +0200 Subject: [PATCH 1123/1790] Bump version number post-5.30.0 --- doc/manual.html | 2 +- package.json | 2 +- src/edit/main.js | 2 +- 3 files changed, 3 insertions(+), 3 deletions(-) diff --git a/doc/manual.html b/doc/manual.html index f2f04459..7666e0df 100644 --- a/doc/manual.html +++ b/doc/manual.html @@ -69,7 +69,7 @@

      User manual and reference guide - version 5.31.0 + version 5.31.1

      CodeMirror is a code-editor component that can be embedded in diff --git a/package.json b/package.json index 2241ad93..6eda061f 100644 --- a/package.json +++ b/package.json @@ -1,6 +1,6 @@ { "name": "codemirror", - "version": "5.31.0", + "version": "5.31.1", "main": "lib/codemirror.js", "style": "lib/codemirror.css", "description": "Full-featured in-browser code editor", diff --git a/src/edit/main.js b/src/edit/main.js index b0fd5de8..6500e08d 100644 --- a/src/edit/main.js +++ b/src/edit/main.js @@ -66,4 +66,4 @@ import { addLegacyProps } from "./legacy" addLegacyProps(CodeMirror) -CodeMirror.version = "5.31.0" +CodeMirror.version = "5.31.1" From 070c338b3b6fd07f9f4481cbe661d7ab0253de38 Mon Sep 17 00:00:00 2001 From: Marijn Haverbeke Date: Mon, 23 Oct 2017 22:22:36 +0200 Subject: [PATCH 1124/1790] [clike mode] Stop treating package as defining in Java mode Closes #5047 --- 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 d6d12c71..02a85319 100644 --- a/mode/clike/clike.js +++ b/mode/clike/clike.js @@ -432,7 +432,7 @@ CodeMirror.defineMode("clike", function(config, parserConfig) { types: words("byte short int long float double boolean char void Boolean Byte Character Double Float " + "Integer Long Number Object Short String StringBuffer StringBuilder Void"), blockKeywords: words("catch class do else finally for if switch try while"), - defKeywords: words("class interface package enum @interface"), + defKeywords: words("class interface enum @interface"), typeFirstDefinitions: true, atoms: words("true false null"), number: /^(?:0x[a-f\d_]+|0b[01_]+|(?:[\d_]+\.?\d*|\.\d+)(?:e[-+]?[\d_]+)?)(u|ll?|l|f)?/i, From 0b1d8183f27fba189e4b06b148203599f223b5d4 Mon Sep 17 00:00:00 2001 From: Jayaprabhakar Date: Mon, 23 Oct 2017 22:06:13 -0700 Subject: [PATCH 1125/1790] Add Codiva.io to realworld usage list Adding Codiva.io Online Java Compiler and IDE to the existing users list. This will highlight some of the advanced usage of Codemirror - Multiple Tabbed editor - Continuous compilation in the backend - Autocompletion - Mobile friendly - Read-only mode --- doc/realworld.html | 1 + 1 file changed, 1 insertion(+) diff --git a/doc/realworld.html b/doc/realworld.html index f0a75abf..2049bf26 100644 --- a/doc/realworld.html +++ b/doc/realworld.html @@ -59,6 +59,7 @@

      CodeMirror real-world uses

    • Codevolve (programming lessons as-a-service)
    • CodeZample (code snippet sharing)
    • Codio (Web IDE)
    • +
    • Codiva.io (Online Java Compiler and IDE with auto-completion and error highlighting)
    • Collaborative CodeMirror demo (CodeMirror + operational transforms)
    • Community Code Camp (code snippet sharing)
    • compilejava.net (online Java sandbox)
    • From edbc598959b1284f49f55f9f29b9fe6beeb8e920 Mon Sep 17 00:00:00 2001 From: Marijn Haverbeke Date: Sun, 29 Oct 2017 11:10:18 +0100 Subject: [PATCH 1126/1790] [closebrackets addon] Improve start-of-string heuristic To also make sure the last token isn't a string. Issue codemirror/google-modes#74 --- addon/edit/closebrackets.js | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-) diff --git a/addon/edit/closebrackets.js b/addon/edit/closebrackets.js index 7b07f7fd..7592ef00 100644 --- a/addon/edit/closebrackets.js +++ b/addon/edit/closebrackets.js @@ -203,6 +203,7 @@ function stringStartsAfter(cm, pos) { var token = cm.getTokenAt(Pos(pos.line, pos.ch + 1)) - return /\bstring/.test(token.type) && token.start == pos.ch + return /\bstring/.test(token.type) && token.start == pos.ch && + (pos.ch == 0 || !/\bstring/.test(cm.getTokenTypeAt(pos))) } }); From e26db631d4f34d844b503ecffafc5d9665ac4e6a Mon Sep 17 00:00:00 2001 From: Marijn Haverbeke Date: Sun, 29 Oct 2017 11:28:10 +0100 Subject: [PATCH 1127/1790] [javascript mode] Support TS type parameter defaults Closes #5053 --- mode/javascript/javascript.js | 10 ++++++++-- 1 file changed, 8 insertions(+), 2 deletions(-) diff --git a/mode/javascript/javascript.js b/mode/javascript/javascript.js index ca9fe8ba..c838e316 100644 --- a/mode/javascript/javascript.js +++ b/mode/javascript/javascript.js @@ -607,6 +607,12 @@ CodeMirror.defineMode("javascript", function(config, parserConfig) { function maybeTypeArgs(_, value) { if (value == "<") return cont(pushlex(">"), commasep(typeexpr, ">"), poplex, afterType) } + function typeparam() { + return pass(typeexpr, maybeTypeDefault) + } + function maybeTypeDefault(_, value) { + if (value == "=") return cont(typeexpr) + } function vardef() { return pass(pattern, maybetype, maybeAssign, vardefCont); } @@ -661,7 +667,7 @@ CodeMirror.defineMode("javascript", function(config, parserConfig) { if (value == "*") {cx.marked = "keyword"; return cont(functiondef);} if (type == "variable") {register(value); return cont(functiondef);} if (type == "(") return cont(pushcontext, pushlex(")"), commasep(funarg, ")"), poplex, maybetype, statement, popcontext); - if (isTS && value == "<") return cont(pushlex(">"), commasep(typeexpr, ">"), poplex, functiondef) + if (isTS && value == "<") return cont(pushlex(">"), commasep(typeparam, ">"), poplex, functiondef) } function funarg(type, value) { if (value == "@") cont(expression, funarg) @@ -677,7 +683,7 @@ CodeMirror.defineMode("javascript", function(config, parserConfig) { if (type == "variable") {register(value); return cont(classNameAfter);} } function classNameAfter(type, value) { - if (value == "<") return cont(pushlex(">"), commasep(typeexpr, ">"), poplex, classNameAfter) + if (value == "<") return cont(pushlex(">"), commasep(typeparam, ">"), poplex, classNameAfter) if (value == "extends" || value == "implements" || (isTS && type == ",")) return cont(isTS ? typeexpr : expression, classNameAfter); if (type == "{") return cont(pushlex("}"), classBody, poplex); From f841fb779ea5f89fb22ccd48879ff034807dd10f Mon Sep 17 00:00:00 2001 From: Marijn Haverbeke Date: Sun, 29 Oct 2017 12:03:28 +0100 Subject: [PATCH 1128/1790] [merge addon] Fix issue where collapsed text markers could get half-cleared Issue #5054 --- addon/merge/merge.js | 3 +++ src/display/update_lines.js | 6 ++++-- 2 files changed, 7 insertions(+), 2 deletions(-) diff --git a/addon/merge/merge.js b/addon/merge/merge.js index c94b27a7..dc2e77c6 100644 --- a/addon/merge/merge.js +++ b/addon/merge/merge.js @@ -738,6 +738,9 @@ mark.clear(); cm.removeLineClass(from, "wrap", "CodeMirror-merge-collapsed-line"); } + if (mark.explicitlyCleared) clear(); + CodeMirror.on(widget, "click", clear); + mark.on("clear", clear); CodeMirror.on(widget, "click", clear); return {mark: mark, clear: clear}; } diff --git a/src/display/update_lines.js b/src/display/update_lines.js index 7583f3c1..bc8a9c60 100644 --- a/src/display/update_lines.js +++ b/src/display/update_lines.js @@ -33,8 +33,10 @@ export function updateHeightsInViewport(cm) { // Read and store the height of line widgets associated with the // given line. function updateWidgetHeight(line) { - if (line.widgets) for (let i = 0; i < line.widgets.length; ++i) - line.widgets[i].height = line.widgets[i].node.parentNode.offsetHeight + if (line.widgets) for (let i = 0; i < line.widgets.length; ++i) { + let w = line.widgets[i], parent = w.node.parentNode; + if (parent) w.height = parent.offsetHeight + } } // Compute the lines that are visible in a given viewport (defaults From 48d2d264d34a2c366af617842c48c0685b641aad Mon Sep 17 00:00:00 2001 From: Marijn Haverbeke Date: Sun, 29 Oct 2017 12:30:12 +0100 Subject: [PATCH 1129/1790] Fix lint issue --- src/display/update_lines.js | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/display/update_lines.js b/src/display/update_lines.js index bc8a9c60..6c591889 100644 --- a/src/display/update_lines.js +++ b/src/display/update_lines.js @@ -34,7 +34,7 @@ export function updateHeightsInViewport(cm) { // given line. function updateWidgetHeight(line) { if (line.widgets) for (let i = 0; i < line.widgets.length; ++i) { - let w = line.widgets[i], parent = w.node.parentNode; + let w = line.widgets[i], parent = w.node.parentNode if (parent) w.height = parent.offsetHeight } } From 97290a687e545bdad23794385b585fd9dfff3e2a Mon Sep 17 00:00:00 2001 From: Jonathan Hart Date: Sat, 21 Oct 2017 19:44:19 +0100 Subject: [PATCH 1130/1790] [continuelist addon] Increment numbers when item is added to the middle Per #5030, when adding new items to the middle of a Markdown list, the remaining list numbers should automatically increment --- addon/edit/continuelist.js | 39 ++++++++++++++++++++++++++++++++++++++ 1 file changed, 39 insertions(+) diff --git a/addon/edit/continuelist.js b/addon/edit/continuelist.js index 02c8eff9..30893965 100644 --- a/addon/edit/continuelist.js +++ b/addon/edit/continuelist.js @@ -44,9 +44,48 @@ : (parseInt(match[3], 10) + 1) + match[4]; replacements[i] = "\n" + indent + bullet + after; + + incrementRemainingMarkdownListNumbers(cm, pos); } } cm.replaceSelections(replacements); }; + + // Auto-updating Markdown list numbers when a new item is added to the + // middle of a list + function incrementRemainingMarkdownListNumbers(cm, pos) { + var startLine = pos.line, lookAhead = 0, skipCount = 0; + var startItem = listRE.exec(cm.getLine(startLine)), startIndent = startItem[1]; + + do { + lookAhead += 1; + var nextLineNumber = startLine + lookAhead; + var nextLine = cm.getLine(nextLineNumber), nextItem = listRE.exec(nextLine); + + if (nextItem) { + var nextIndent = nextItem[1]; + var newNumber = (parseInt(startItem[3], 10) + lookAhead - skipCount); + var nextNumber = (parseInt(nextItem[3], 10)), itemNumber = nextNumber; + + if (startIndent === nextIndent) { + if (newNumber === nextNumber) itemNumber = nextNumber + 1; + if (newNumber > nextNumber) itemNumber = newNumber + 1; + cm.replaceRange( + nextLine.replace(listRE, nextIndent + itemNumber + nextItem[4] + nextItem[5]), + { + line: nextLineNumber, ch: 0 + }, { + line: nextLineNumber, ch: nextLine.length + }); + } else { + if (startIndent.length > nextIndent.length) return; + // This doesn't run if the next line immediatley indents, as it is + // not clear of the users intention (new indented item or same level) + if ((startIndent.length < nextIndent.length) && (lookAhead === 1)) return; + skipCount += 1; + } + } + } while (nextItem); + } }); From 7e9190a646869db885ed73b239dc204d8e58a009 Mon Sep 17 00:00:00 2001 From: Marijn Haverbeke Date: Wed, 1 Nov 2017 11:17:21 +0100 Subject: [PATCH 1131/1790] [comment addon] Don't ignore content on last selected line when block-uncommenting Closes #5059 --- addon/comment/comment.js | 4 ---- 1 file changed, 4 deletions(-) diff --git a/addon/comment/comment.js b/addon/comment/comment.js index 568e639d..84c67edf 100644 --- a/addon/comment/comment.js +++ b/addon/comment/comment.js @@ -172,10 +172,6 @@ if (open == -1) return false var endLine = end == start ? startLine : self.getLine(end) var close = endLine.indexOf(endString, end == start ? open + startString.length : 0); - if (close == -1 && start != end) { - endLine = self.getLine(--end); - close = endLine.indexOf(endString); - } var insideStart = Pos(start, open + 1), insideEnd = Pos(end, close + 1) if (close == -1 || !/comment/.test(self.getTokenTypeAt(insideStart)) || From a7907d37b2dcf1067a478bdc02064305461658ef Mon Sep 17 00:00:00 2001 From: Alexander Shvets Date: Tue, 31 Oct 2017 18:27:23 +0200 Subject: [PATCH 1132/1790] [searchcursor addon] Fix bug in case folding --- 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 eccd81aa..58bc47c2 100644 --- a/addon/search/searchcursor.js +++ b/addon/search/searchcursor.js @@ -159,7 +159,7 @@ for (var i = 1; i < lines.length - 1; i++) if (fold(doc.getLine(line + i)) != lines[i]) continue search var end = doc.getLine(line + lines.length - 1), endString = fold(end), lastLine = lines[lines.length - 1] - if (end.slice(0, lastLine.length) != lastLine) continue search + if (endString.slice(0, lastLine.length) != lastLine) continue search return {from: Pos(line, adjustPos(orig, string, cutFrom, fold) + ch), to: Pos(line + lines.length - 1, adjustPos(end, endString, lastLine.length, fold))} } From 66107010e9e34372c6736e6ca9379cf6ac044abc Mon Sep 17 00:00:00 2001 From: Marijn Haverbeke Date: Wed, 1 Nov 2017 11:52:12 +0100 Subject: [PATCH 1133/1790] Re-dispatch non-matching multi-key-strokes without their prefix Issue #5061 --- src/edit/key_events.js | 26 +++++++++++++++----------- 1 file changed, 15 insertions(+), 11 deletions(-) diff --git a/src/edit/key_events.js b/src/edit/key_events.js index 2955e4ae..06f37be0 100644 --- a/src/edit/key_events.js +++ b/src/edit/key_events.js @@ -44,18 +44,26 @@ function lookupKeyForEditor(cm, name, handle) { // for bound mouse clicks. let stopSeq = new Delayed + export function dispatchKey(cm, name, e, handle) { let seq = cm.state.keySeq if (seq) { if (isModifierKey(name)) return "handled" - stopSeq.set(50, () => { - if (cm.state.keySeq == seq) { - cm.state.keySeq = null - cm.display.input.reset() - } - }) - name = seq + " " + name + if (/\'$/.test(name)) + cm.state.keySeq = null + else + stopSeq.set(50, () => { + if (cm.state.keySeq == seq) { + cm.state.keySeq = null + cm.display.input.reset() + } + }) + if (dispatchKeyInner(cm, seq + " " + name, e, handle)) return true } + return dispatchKeyInner(cm, name, e, handle) +} + +function dispatchKeyInner(cm, name, e, handle) { let result = lookupKeyForEditor(cm, name, handle) if (result == "multi") @@ -68,10 +76,6 @@ export function dispatchKey(cm, name, e, handle) { restartBlink(cm) } - if (seq && !result && /\'$/.test(name)) { - e_preventDefault(e) - return true - } return !!result } From 659cb7f3690a9e2b066faeba73143f291932aa30 Mon Sep 17 00:00:00 2001 From: Marijn Haverbeke Date: Thu, 2 Nov 2017 09:22:28 +0100 Subject: [PATCH 1134/1790] [javascript mode] Make TypeScript module/enum contextual keywords Closes #5064 --- mode/javascript/javascript.js | 8 ++++---- 1 file changed, 4 insertions(+), 4 deletions(-) diff --git a/mode/javascript/javascript.js b/mode/javascript/javascript.js index c838e316..5c772526 100644 --- a/mode/javascript/javascript.js +++ b/mode/javascript/javascript.js @@ -47,8 +47,6 @@ CodeMirror.defineMode("javascript", function(config, parserConfig) { "interface": kw("class"), "implements": C, "namespace": C, - "module": kw("module"), - "enum": kw("module"), // scope modifiers "public": kw("modifier"), @@ -372,9 +370,12 @@ CodeMirror.defineMode("javascript", function(config, parserConfig) { if (isTS && value == "type") { cx.marked = "keyword" return cont(typeexpr, expect("operator"), typeexpr, expect(";")); - } if (isTS && value == "declare") { + } else if (isTS && value == "declare") { cx.marked = "keyword" return cont(statement) + } else if (isTS && (value == "module" || value == "enum") && cx.stream.match(/^\s*\w/, false)) { + cx.marked = "keyword" + return cont(pushlex("form"), pattern, expect("{"), pushlex("}"), block, poplex, poplex) } else { return cont(pushlex("stat"), maybelabel); } @@ -388,7 +389,6 @@ CodeMirror.defineMode("javascript", function(config, parserConfig) { if (type == "class") return cont(pushlex("form"), className, poplex); if (type == "export") return cont(pushlex("stat"), afterExport, poplex); if (type == "import") return cont(pushlex("stat"), afterImport, poplex); - if (type == "module") return cont(pushlex("form"), pattern, expect("{"), pushlex("}"), block, poplex, poplex) if (type == "async") return cont(statement) if (value == "@") return cont(expression, statement) return pass(pushlex("stat"), expression, expect(";"), poplex); From c83e94a6412b9293362e59980d7ca26f58b4b9da Mon Sep 17 00:00:00 2001 From: Jayaprabhakar Date: Sun, 5 Nov 2017 19:02:03 -0800 Subject: [PATCH 1135/1790] Remove broken links in the realworld users page Removing the broken links in the realworld users list. I checked the backlinks using, https://www.deadlinkchecker.com/website-dead-link-checker.asp and manually verified these urls to be invalid. --- doc/realworld.html | 13 +------------ 1 file changed, 1 insertion(+), 12 deletions(-) diff --git a/doc/realworld.html b/doc/realworld.html index 2049bf26..da0f4e4e 100644 --- a/doc/realworld.html +++ b/doc/realworld.html @@ -28,7 +28,6 @@

      CodeMirror real-world uses

    • Adobe Brackets (code editor)
    • ALM Tools (TypeScript powered IDE)
    • Amber (JavaScript-based Smalltalk system)
    • -
    • Apache GUI
    • APEye (tool for testing & documenting APIs)
    • Appengine Codiad
    • Better Text Viewer (plain text reader app for Chrome)
    • @@ -60,7 +59,6 @@

      CodeMirror real-world uses

    • CodeZample (code snippet sharing)
    • Codio (Web IDE)
    • Codiva.io (Online Java Compiler and IDE with auto-completion and error highlighting)
    • -
    • Collaborative CodeMirror demo (CodeMirror + operational transforms)
    • Community Code Camp (code snippet sharing)
    • compilejava.net (online Java sandbox)
    • CKWNC (UML editor)
    • @@ -70,7 +68,6 @@

      CodeMirror real-world uses

    • CSSDeck (CSS showcase)
    • Deck.js integration (slides with editors)
    • DbNinja (MySQL access interface)
    • -
    • Echoplexus (chat and collaborative coding)
    • eCSSpert (CSS demos and experiments)
    • Elm language examples
    • Eloquent JavaScript (book)
    • @@ -79,7 +76,6 @@

      CodeMirror real-world uses

    • Fastfig (online computation/math tool)
    • Farabi (modern Perl IDE)
    • FathomJS integration (slides with editors, again)
    • -
    • Phantomus (blogging platform)
    • Fiddle Salad (web development environment)
    • Filemanager
    • Firefox Developer Tools
    • @@ -101,7 +97,7 @@

      CodeMirror real-world uses

    • Homegenie (home automation server)
    • ICEcoder (web IDE)
    • IPython (interactive computing shell)
    • -
    • iTrading (Algorithmic Trading)
    • +
    • iTrading (Algorithmic Trading)
    • i-MOS (modeling and simulation platform)
    • Janvas (vector graphics editor)
    • Joomla plugin
    • @@ -116,18 +112,14 @@

      CodeMirror real-world uses

    • Kodit
    • Kodtest (HTML/JS/CSS playground)
    • Kotlin (web-based mini-IDE for Kotlin)
    • -
    • Laborate (collaborative coding)
    • Light Table (experimental IDE)
    • Liveweave (HTML/CSS/JS scratchpad)
    • Markdown Delight Editor (extensible markdown editor polymer component)
    • Marklight editor (lightweight markup editor)
    • Mergely (interactive diffing)
    • MIHTool (iOS web-app debugging tool)
    • -
    • Mongo MapReduce WebBrowser
    • -
    • Montage Studio (web app creator suite)
    • mscgen_js (online sequence chart editor)
    • MVC Playground
    • -
    • My2ndGeneration (social coding)
    • Navigate CMS
    • nodeMirror (IDE project)
    • NoTex (rST authoring)
    • @@ -143,8 +135,6 @@

      CodeMirror real-world uses

    • PubliForge (online publishing system)
    • Puzzlescript (puzzle game engine)
    • Quantum (code editor for Chrome OS)
    • -
    • ql.io (http API query helper)
    • -
    • QiYun web app platform
    • Qt+Webkit integration (building a desktop CodeMirror app)
    • Quivive File Manager
    • Rascal (tiny computer)
    • @@ -172,7 +162,6 @@

      CodeMirror real-world uses

    • TileMill (map design tool)
    • Tiki (wiki CMS groupware)
    • Toolsverse Data Explorer (database management)
    • -
    • Tributary (augmented editing)
    • Tumblr code highlighting shim
    • TurboPY (web publishing framework)
    • UmpleOnline (model-oriented programming tool)
    • From f02225b9ca000ddb098053b21acf3165d1e34beb Mon Sep 17 00:00:00 2001 From: Marijn Haverbeke Date: Mon, 6 Nov 2017 11:33:40 +0100 Subject: [PATCH 1136/1790] Make the default colors for bracket matching more constrasting Issue #5063 --- lib/codemirror.css | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/lib/codemirror.css b/lib/codemirror.css index 255de986..8f4f22f5 100644 --- a/lib/codemirror.css +++ b/lib/codemirror.css @@ -145,8 +145,8 @@ /* Default styles for common addons */ -div.CodeMirror span.CodeMirror-matchingbracket {color: #0f0;} -div.CodeMirror span.CodeMirror-nonmatchingbracket {color: #f22;} +div.CodeMirror span.CodeMirror-matchingbracket {color: #0b0;} +div.CodeMirror span.CodeMirror-nonmatchingbracket {color: #a22;} .CodeMirror-matchingtag { background: rgba(255, 150, 0, .3); } .CodeMirror-activeline-background {background: #e8f2ff;} From 36d7c7291fe7ecac442588a3a3c5c25a27e91455 Mon Sep 17 00:00:00 2001 From: Marijn Haverbeke Date: Tue, 7 Nov 2017 11:47:07 +0100 Subject: [PATCH 1137/1790] [closebrackets addon] Adjust check for when to insert two quotes Stop relying on mode tokens, use simple heuristic of not being after a word char Issue #5058 Issue #2657 --- addon/edit/closebrackets.js | 19 ++----------------- 1 file changed, 2 insertions(+), 17 deletions(-) diff --git a/addon/edit/closebrackets.js b/addon/edit/closebrackets.js index 7592ef00..460f662f 100644 --- a/addon/edit/closebrackets.js +++ b/addon/edit/closebrackets.js @@ -133,7 +133,8 @@ (cur.ch <= 2 || cm.getRange(Pos(cur.line, cur.ch - 3), Pos(cur.line, cur.ch - 2)) != ch)) { curType = "addFour"; } else if (identical) { - if (!CodeMirror.isWordChar(next) && enteringString(cm, cur, ch)) curType = "both"; + var prev = cur.ch == 0 ? " " : cm.getRange(Pos(cur.line, cur.ch - 1), cur) + if (!CodeMirror.isWordChar(next) && prev != ch && !CodeMirror.isWordChar(prev)) curType = "both"; else return CodeMirror.Pass; } else if (opening && (cm.getLine(cur.line).length == cur.ch || isClosingBracket(next, pairs) || @@ -185,22 +186,6 @@ return str.length == 2 ? str : null; } - // Project the token type that will exists after the given char is - // typed, and use it to determine whether it would cause the start - // of a string token. - function enteringString(cm, pos, ch) { - var line = cm.getLine(pos.line); - var token = cm.getTokenAt(pos); - if (/\bstring2?\b/.test(token.type) || stringStartsAfter(cm, pos)) return false; - var stream = new CodeMirror.StringStream(line.slice(0, pos.ch) + ch + line.slice(pos.ch), 4); - stream.pos = stream.start = token.start; - for (;;) { - var type1 = cm.getMode().token(stream, token.state); - if (stream.pos >= pos.ch + 1) return /\bstring2?\b/.test(type1); - stream.start = stream.pos; - } - } - function stringStartsAfter(cm, pos) { var token = cm.getTokenAt(Pos(pos.line, pos.ch + 1)) return /\bstring/.test(token.type) && token.start == pos.ch && From b6bfb4d09d14fa64d0993280c2761281d52ff958 Mon Sep 17 00:00:00 2001 From: Marijn Haverbeke Date: Wed, 8 Nov 2017 09:49:18 +0100 Subject: [PATCH 1138/1790] [solarized theme] Remove overly low-contrast color for cm-strong Closes #5075 --- theme/solarized.css | 1 - 1 file changed, 1 deletion(-) diff --git a/theme/solarized.css b/theme/solarized.css index d95f6c1b..fcd1d70d 100644 --- a/theme/solarized.css +++ b/theme/solarized.css @@ -87,7 +87,6 @@ http://ethanschoonover.com/solarized/img/solarized-palette.png text-decoration: underline; text-decoration-style: dotted; } -.cm-s-solarized .cm-strong { color: #eee; } .cm-s-solarized .cm-error, .cm-s-solarized .cm-invalidchar { color: #586e75; From 2c741bd6742678f67e812307b2f78db4f4566d9d Mon Sep 17 00:00:00 2001 From: Casey Klebba Date: Tue, 7 Nov 2017 17:12:57 -0800 Subject: [PATCH 1139/1790] Fully qualify import paths --- src/codemirror.js | 2 +- src/display/Display.js | 6 +-- src/display/focus.js | 8 ++-- src/display/gutters.js | 6 +-- src/display/highlight_worker.js | 10 ++--- src/display/line_numbers.js | 8 ++-- src/display/mode_state.js | 6 +-- src/display/operations.js | 26 ++++++------- src/display/scroll_events.js | 8 ++-- src/display/scrollbars.js | 16 ++++---- src/display/scrolling.js | 18 ++++----- src/display/selection.js | 12 +++--- src/display/update_display.js | 30 +++++++-------- src/display/update_line.js | 10 ++--- src/display/update_lines.js | 8 ++-- src/display/view_tracking.js | 10 ++--- src/edit/CodeMirror.js | 50 ++++++++++++------------- src/edit/commands.js | 22 +++++------ src/edit/deleteNearSelection.js | 10 ++--- src/edit/drop_events.js | 26 ++++++------- src/edit/fromTextArea.js | 8 ++-- src/edit/global_events.js | 4 +- src/edit/key_events.js | 22 +++++------ src/edit/legacy.js | 34 ++++++++--------- src/edit/main.js | 24 ++++++------ src/edit/methods.js | 48 ++++++++++++------------ src/edit/mouse_events.js | 40 ++++++++++---------- src/edit/options.js | 36 +++++++++--------- src/edit/utils.js | 2 +- src/input/ContentEditableInput.js | 30 +++++++-------- src/input/TextareaInput.js | 24 ++++++------ src/input/indent.js | 14 +++---- src/input/input.js | 22 +++++------ src/input/keymap.js | 6 +-- src/input/movement.js | 8 ++-- src/line/highlight.js | 10 ++--- src/line/line_data.js | 18 ++++----- src/line/pos.js | 2 +- src/line/spans.js | 8 ++-- src/line/utils_line.js | 2 +- src/measurement/position_measurement.js | 26 ++++++------- src/measurement/widgets.js | 4 +- src/model/Doc.js | 40 ++++++++++---------- src/model/change_measurement.js | 6 +-- src/model/changes.js | 36 +++++++++--------- src/model/chunk.js | 6 +-- src/model/document_data.js | 20 +++++----- src/model/history.js | 18 ++++----- src/model/line_widget.js | 18 ++++----- src/model/mark_text.js | 30 +++++++-------- src/model/selection.js | 4 +- src/model/selection_updates.js | 18 ++++----- src/modes.js | 2 +- src/util/StringStream.js | 2 +- src/util/bidi.js | 2 +- src/util/dom.js | 2 +- src/util/event.js | 4 +- src/util/feature_detection.js | 4 +- src/util/operation_group.js | 2 +- 59 files changed, 449 insertions(+), 449 deletions(-) diff --git a/src/codemirror.js b/src/codemirror.js index 3c16cc87..2a2f54e4 100644 --- a/src/codemirror.js +++ b/src/codemirror.js @@ -1,3 +1,3 @@ -import { CodeMirror } from "./edit/main" +import { CodeMirror } from "./edit/main.js" export default CodeMirror diff --git a/src/display/Display.js b/src/display/Display.js index ad0256bd..54b228a9 100644 --- a/src/display/Display.js +++ b/src/display/Display.js @@ -1,6 +1,6 @@ -import { gecko, ie, ie_version, mobile, webkit } from "../util/browser" -import { elt, eltP } from "../util/dom" -import { scrollerGap } from "../util/misc" +import { gecko, ie, ie_version, mobile, webkit } from "../util/browser.js" +import { elt, eltP } from "../util/dom.js" +import { scrollerGap } from "../util/misc.js" // The display handles the DOM integration, both for input reading // and content drawing. It holds references to DOM nodes and diff --git a/src/display/focus.js b/src/display/focus.js index ee52daff..aa731b43 100644 --- a/src/display/focus.js +++ b/src/display/focus.js @@ -1,7 +1,7 @@ -import { restartBlink } from "./selection" -import { webkit } from "../util/browser" -import { addClass, rmClass } from "../util/dom" -import { signal } from "../util/event" +import { restartBlink } from "./selection.js" +import { webkit } from "../util/browser.js" +import { addClass, rmClass } from "../util/dom.js" +import { signal } from "../util/event.js" export function ensureFocus(cm) { if (!cm.state.focused) { cm.display.input.focus(); onFocus(cm) } diff --git a/src/display/gutters.js b/src/display/gutters.js index 7ccf119e..37405b6d 100644 --- a/src/display/gutters.js +++ b/src/display/gutters.js @@ -1,7 +1,7 @@ -import { elt, removeChildren } from "../util/dom" -import { indexOf } from "../util/misc" +import { elt, removeChildren } from "../util/dom.js" +import { indexOf } from "../util/misc.js" -import { updateGutterSpace } from "./update_display" +import { updateGutterSpace } from "./update_display.js" // Rebuild the gutter elements, ensure the margin to the left of the // code matches their width. diff --git a/src/display/highlight_worker.js b/src/display/highlight_worker.js index e868c42f..60698157 100644 --- a/src/display/highlight_worker.js +++ b/src/display/highlight_worker.js @@ -1,9 +1,9 @@ -import { getContextBefore, highlightLine, processLine } from "../line/highlight" -import { copyState } from "../modes" -import { bind } from "../util/misc" +import { getContextBefore, highlightLine, processLine } from "../line/highlight.js" +import { copyState } from "../modes.js" +import { bind } from "../util/misc.js" -import { runInOp } from "./operations" -import { regLineChange } from "./view_tracking" +import { runInOp } from "./operations.js" +import { regLineChange } from "./view_tracking.js" // HIGHLIGHT WORKER diff --git a/src/display/line_numbers.js b/src/display/line_numbers.js index c48f2204..3ab95750 100644 --- a/src/display/line_numbers.js +++ b/src/display/line_numbers.js @@ -1,8 +1,8 @@ -import { lineNumberFor } from "../line/utils_line" -import { compensateForHScroll } from "../measurement/position_measurement" -import { elt } from "../util/dom" +import { lineNumberFor } from "../line/utils_line.js" +import { compensateForHScroll } from "../measurement/position_measurement.js" +import { elt } from "../util/dom.js" -import { updateGutterSpace } from "./update_display" +import { updateGutterSpace } from "./update_display.js" // Re-align line numbers and gutter marks to compensate for // horizontal scrolling. diff --git a/src/display/mode_state.js b/src/display/mode_state.js index ca0a534c..5d8ebf25 100644 --- a/src/display/mode_state.js +++ b/src/display/mode_state.js @@ -1,7 +1,7 @@ -import { getMode } from "../modes" +import { getMode } from "../modes.js" -import { startWorker } from "./highlight_worker" -import { regChange } from "./view_tracking" +import { startWorker } from "./highlight_worker.js" +import { regChange } from "./view_tracking.js" // Used to get the editor into a consistent state again when options change. diff --git a/src/display/operations.js b/src/display/operations.js index c3004508..5cc26d26 100644 --- a/src/display/operations.js +++ b/src/display/operations.js @@ -1,16 +1,16 @@ -import { clipPos } from "../line/pos" -import { findMaxLine } from "../line/spans" -import { displayWidth, measureChar, scrollGap } from "../measurement/position_measurement" -import { signal } from "../util/event" -import { activeElt } from "../util/dom" -import { finishOperation, pushOperation } from "../util/operation_group" - -import { ensureFocus } from "./focus" -import { measureForScrollbars, updateScrollbars } from "./scrollbars" -import { restartBlink } from "./selection" -import { maybeScrollWindow, scrollPosIntoView, setScrollLeft, setScrollTop } from "./scrolling" -import { DisplayUpdate, maybeClipScrollbars, postUpdateDisplay, setDocumentHeight, updateDisplayIfNeeded } from "./update_display" -import { updateHeightsInViewport } from "./update_lines" +import { clipPos } from "../line/pos.js" +import { findMaxLine } from "../line/spans.js" +import { displayWidth, measureChar, scrollGap } from "../measurement/position_measurement.js" +import { signal } from "../util/event.js" +import { activeElt } from "../util/dom.js" +import { finishOperation, pushOperation } from "../util/operation_group.js" + +import { ensureFocus } from "./focus.js" +import { measureForScrollbars, updateScrollbars } from "./scrollbars.js" +import { restartBlink } from "./selection.js" +import { maybeScrollWindow, scrollPosIntoView, setScrollLeft, setScrollTop } from "./scrolling.js" +import { DisplayUpdate, maybeClipScrollbars, postUpdateDisplay, setDocumentHeight, updateDisplayIfNeeded } from "./update_display.js" +import { updateHeightsInViewport } from "./update_lines.js" // Operations are used to wrap a series of changes to the editor // state in such a way that each change won't have to update the diff --git a/src/display/scroll_events.js b/src/display/scroll_events.js index d3902809..fbed4266 100644 --- a/src/display/scroll_events.js +++ b/src/display/scroll_events.js @@ -1,8 +1,8 @@ -import { chrome, gecko, ie, mac, presto, safari, webkit } from "../util/browser" -import { e_preventDefault } from "../util/event" +import { chrome, gecko, ie, mac, presto, safari, webkit } from "../util/browser.js" +import { e_preventDefault } from "../util/event.js" -import { updateDisplaySimple } from "./update_display" -import { setScrollLeft, updateScrollTop } from "./scrolling" +import { updateDisplaySimple } from "./update_display.js" +import { setScrollLeft, updateScrollTop } from "./scrolling.js" // Since the delta values reported on mouse wheel events are // unstandardized between browsers and even browser versions, and diff --git a/src/display/scrollbars.js b/src/display/scrollbars.js index 27060d18..7308c5e2 100644 --- a/src/display/scrollbars.js +++ b/src/display/scrollbars.js @@ -1,11 +1,11 @@ -import { addClass, elt, rmClass } from "../util/dom" -import { on } from "../util/event" -import { scrollGap, paddingVert } from "../measurement/position_measurement" -import { ie, ie_version, mac, mac_geMountainLion } from "../util/browser" -import { updateHeightsInViewport } from "./update_lines" -import { Delayed } from "../util/misc" - -import { setScrollLeft, updateScrollTop } from "./scrolling" +import { addClass, elt, rmClass } from "../util/dom.js" +import { on } from "../util/event.js" +import { scrollGap, paddingVert } from "../measurement/position_measurement.js" +import { ie, ie_version, mac, mac_geMountainLion } from "../util/browser.js" +import { updateHeightsInViewport } from "./update_lines.js" +import { Delayed } from "../util/misc.js" + +import { setScrollLeft, updateScrollTop } from "./scrolling.js" // SCROLLBARS diff --git a/src/display/scrolling.js b/src/display/scrolling.js index e16cf9ec..26ec993b 100644 --- a/src/display/scrolling.js +++ b/src/display/scrolling.js @@ -1,12 +1,12 @@ -import { Pos } from "../line/pos" -import { cursorCoords, displayHeight, displayWidth, estimateCoords, paddingTop, paddingVert, scrollGap, textHeight } from "../measurement/position_measurement" -import { gecko, phantom } from "../util/browser" -import { elt } from "../util/dom" -import { signalDOMEvent } from "../util/event" - -import { startWorker } from "./highlight_worker" -import { alignHorizontally } from "./line_numbers" -import { updateDisplaySimple } from "./update_display" +import { Pos } from "../line/pos.js" +import { cursorCoords, displayHeight, displayWidth, estimateCoords, paddingTop, paddingVert, scrollGap, textHeight } from "../measurement/position_measurement.js" +import { gecko, phantom } from "../util/browser.js" +import { elt } from "../util/dom.js" +import { signalDOMEvent } from "../util/event.js" + +import { startWorker } from "./highlight_worker.js" +import { alignHorizontally } from "./line_numbers.js" +import { updateDisplaySimple } from "./update_display.js" // SCROLLING THINGS INTO VIEW diff --git a/src/display/selection.js b/src/display/selection.js index dca96a44..c658c0a2 100644 --- a/src/display/selection.js +++ b/src/display/selection.js @@ -1,9 +1,9 @@ -import { Pos } from "../line/pos" -import { visualLine } from "../line/spans" -import { getLine } from "../line/utils_line" -import { charCoords, cursorCoords, displayWidth, paddingH, wrappedLineExtentChar } from "../measurement/position_measurement" -import { getOrder, iterateBidiSections } from "../util/bidi" -import { elt } from "../util/dom" +import { Pos } from "../line/pos.js" +import { visualLine } from "../line/spans.js" +import { getLine } from "../line/utils_line.js" +import { charCoords, cursorCoords, displayWidth, paddingH, wrappedLineExtentChar } from "../measurement/position_measurement.js" +import { getOrder, iterateBidiSections } from "../util/bidi.js" +import { elt } from "../util/dom.js" export function updateSelection(cm) { cm.display.input.showSelection(cm.display.input.prepareSelection()) diff --git a/src/display/update_display.js b/src/display/update_display.js index e58db48a..86c71321 100644 --- a/src/display/update_display.js +++ b/src/display/update_display.js @@ -1,19 +1,19 @@ -import { sawCollapsedSpans } from "../line/saw_special_spans" -import { heightAtLine, visualLineEndNo, visualLineNo } from "../line/spans" -import { getLine, lineNumberFor } from "../line/utils_line" -import { displayHeight, displayWidth, getDimensions, paddingVert, scrollGap } from "../measurement/position_measurement" -import { mac, webkit } from "../util/browser" -import { activeElt, removeChildren, contains } from "../util/dom" -import { hasHandler, signal } from "../util/event" -import { indexOf } from "../util/misc" +import { sawCollapsedSpans } from "../line/saw_special_spans.js" +import { heightAtLine, visualLineEndNo, visualLineNo } from "../line/spans.js" +import { getLine, lineNumberFor } from "../line/utils_line.js" +import { displayHeight, displayWidth, getDimensions, paddingVert, scrollGap } from "../measurement/position_measurement.js" +import { mac, webkit } from "../util/browser.js" +import { activeElt, removeChildren, contains } from "../util/dom.js" +import { hasHandler, signal } from "../util/event.js" +import { indexOf } from "../util/misc.js" -import { buildLineElement, updateLineForChanges } from "./update_line" -import { startWorker } from "./highlight_worker" -import { maybeUpdateLineNumberWidth } from "./line_numbers" -import { measureForScrollbars, updateScrollbars } from "./scrollbars" -import { updateSelection } from "./selection" -import { updateHeightsInViewport, visibleLines } from "./update_lines" -import { adjustView, countDirtyView, resetView } from "./view_tracking" +import { buildLineElement, updateLineForChanges } from "./update_line.js" +import { startWorker } from "./highlight_worker.js" +import { maybeUpdateLineNumberWidth } from "./line_numbers.js" +import { measureForScrollbars, updateScrollbars } from "./scrollbars.js" +import { updateSelection } from "./selection.js" +import { updateHeightsInViewport, visibleLines } from "./update_lines.js" +import { adjustView, countDirtyView, resetView } from "./view_tracking.js" // DISPLAY DRAWING diff --git a/src/display/update_line.js b/src/display/update_line.js index 15a23942..db9df26d 100644 --- a/src/display/update_line.js +++ b/src/display/update_line.js @@ -1,8 +1,8 @@ -import { buildLineContent } from "../line/line_data" -import { lineNumberFor } from "../line/utils_line" -import { ie, ie_version } from "../util/browser" -import { elt } from "../util/dom" -import { signalLater } from "../util/operation_group" +import { buildLineContent } from "../line/line_data.js" +import { lineNumberFor } from "../line/utils_line.js" +import { ie, ie_version } from "../util/browser.js" +import { elt } from "../util/dom.js" +import { signalLater } from "../util/operation_group.js" // When an aspect of a line changes, a string is added to // lineView.changes. This updates the relevant part of the line's diff --git a/src/display/update_lines.js b/src/display/update_lines.js index 6c591889..7f06018d 100644 --- a/src/display/update_lines.js +++ b/src/display/update_lines.js @@ -1,7 +1,7 @@ -import { heightAtLine } from "../line/spans" -import { getLine, lineAtHeight, updateLineHeight } from "../line/utils_line" -import { paddingTop, textHeight } from "../measurement/position_measurement" -import { ie, ie_version } from "../util/browser" +import { heightAtLine } from "../line/spans.js" +import { getLine, lineAtHeight, updateLineHeight } from "../line/utils_line.js" +import { paddingTop, textHeight } from "../measurement/position_measurement.js" +import { ie, ie_version } from "../util/browser.js" // Read the actual heights of the rendered lines, and update their // stored heights to match. diff --git a/src/display/view_tracking.js b/src/display/view_tracking.js index b9abd2fc..41464f23 100644 --- a/src/display/view_tracking.js +++ b/src/display/view_tracking.js @@ -1,8 +1,8 @@ -import { buildViewArray } from "../line/line_data" -import { sawCollapsedSpans } from "../line/saw_special_spans" -import { visualLineEndNo, visualLineNo } from "../line/spans" -import { findViewIndex } from "../measurement/position_measurement" -import { indexOf } from "../util/misc" +import { buildViewArray } from "../line/line_data.js" +import { sawCollapsedSpans } from "../line/saw_special_spans.js" +import { visualLineEndNo, visualLineNo } from "../line/spans.js" +import { findViewIndex } from "../measurement/position_measurement.js" +import { indexOf } from "../util/misc.js" // Updates the display.view data structure for a given change to the // document. From and to are in pre-change coordinates. Lendiff is diff --git a/src/edit/CodeMirror.js b/src/edit/CodeMirror.js index 0f0e5890..4759209c 100644 --- a/src/edit/CodeMirror.js +++ b/src/edit/CodeMirror.js @@ -1,28 +1,28 @@ -import { Display } from "../display/Display" -import { onFocus, onBlur } from "../display/focus" -import { setGuttersForLineNumbers, updateGutters } from "../display/gutters" -import { maybeUpdateLineNumberWidth } from "../display/line_numbers" -import { endOperation, operation, startOperation } from "../display/operations" -import { initScrollbars } from "../display/scrollbars" -import { onScrollWheel } from "../display/scroll_events" -import { setScrollLeft, updateScrollTop } from "../display/scrolling" -import { clipPos, Pos } from "../line/pos" -import { posFromMouse } from "../measurement/position_measurement" -import { eventInWidget } from "../measurement/widgets" -import Doc from "../model/Doc" -import { attachDoc } from "../model/document_data" -import { Range } from "../model/selection" -import { extendSelection } from "../model/selection_updates" -import { captureRightClick, ie, ie_version, mobile, webkit } from "../util/browser" -import { e_preventDefault, e_stop, on, signal, signalDOMEvent } from "../util/event" -import { bind, copyObj, Delayed } from "../util/misc" - -import { clearDragCursor, onDragOver, onDragStart, onDrop } from "./drop_events" -import { ensureGlobalHandlers } from "./global_events" -import { onKeyDown, onKeyPress, onKeyUp } from "./key_events" -import { clickInGutter, onContextMenu, onMouseDown } from "./mouse_events" -import { themeChanged } from "./utils" -import { defaults, optionHandlers, Init } from "./options" +import { Display } from "../display/Display.js" +import { onFocus, onBlur } from "../display/focus.js" +import { setGuttersForLineNumbers, updateGutters } from "../display/gutters.js" +import { maybeUpdateLineNumberWidth } from "../display/line_numbers.js" +import { endOperation, operation, startOperation } from "../display/operations.js" +import { initScrollbars } from "../display/scrollbars.js" +import { onScrollWheel } from "../display/scroll_events.js" +import { setScrollLeft, updateScrollTop } from "../display/scrolling.js" +import { clipPos, Pos } from "../line/pos.js" +import { posFromMouse } from "../measurement/position_measurement.js" +import { eventInWidget } from "../measurement/widgets.js" +import Doc from "../model/Doc.js" +import { attachDoc } from "../model/document_data.js" +import { Range } from "../model/selection.js" +import { extendSelection } from "../model/selection_updates.js" +import { captureRightClick, ie, ie_version, mobile, webkit } from "../util/browser.js" +import { e_preventDefault, e_stop, on, signal, signalDOMEvent } from "../util/event.js" +import { bind, copyObj, Delayed } from "../util/misc.js" + +import { clearDragCursor, onDragOver, onDragStart, onDrop } from "./drop_events.js" +import { ensureGlobalHandlers } from "./global_events.js" +import { onKeyDown, onKeyPress, onKeyUp } from "./key_events.js" +import { clickInGutter, onContextMenu, onMouseDown } from "./mouse_events.js" +import { themeChanged } from "./utils.js" +import { defaults, optionHandlers, Init } from "./options.js" // A CodeMirror instance represents an editor. This is the object // that user code is usually dealing with. diff --git a/src/edit/commands.js b/src/edit/commands.js index e1a4327c..3916b129 100644 --- a/src/edit/commands.js +++ b/src/edit/commands.js @@ -1,14 +1,14 @@ -import { deleteNearSelection } from "./deleteNearSelection" -import { runInOp } from "../display/operations" -import { ensureCursorVisible } from "../display/scrolling" -import { endOfLine } from "../input/movement" -import { clipPos, Pos } from "../line/pos" -import { visualLine, visualLineEnd } from "../line/spans" -import { getLine, lineNo } from "../line/utils_line" -import { Range } from "../model/selection" -import { selectAll } from "../model/selection_updates" -import { countColumn, sel_dontScroll, sel_move, spaceStr } from "../util/misc" -import { getOrder } from "../util/bidi" +import { deleteNearSelection } from "./deleteNearSelection.js" +import { runInOp } from "../display/operations.js" +import { ensureCursorVisible } from "../display/scrolling.js" +import { endOfLine } from "../input/movement.js" +import { clipPos, Pos } from "../line/pos.js" +import { visualLine, visualLineEnd } from "../line/spans.js" +import { getLine, lineNo } from "../line/utils_line.js" +import { Range } from "../model/selection.js" +import { selectAll } from "../model/selection_updates.js" +import { countColumn, sel_dontScroll, sel_move, spaceStr } from "../util/misc.js" +import { getOrder } from "../util/bidi.js" // Commands are parameter-less actions that can be performed on an // editor, mostly used for keybindings. diff --git a/src/edit/deleteNearSelection.js b/src/edit/deleteNearSelection.js index 5a9bd2cf..82e331a5 100644 --- a/src/edit/deleteNearSelection.js +++ b/src/edit/deleteNearSelection.js @@ -1,8 +1,8 @@ -import { runInOp } from "../display/operations" -import { ensureCursorVisible } from "../display/scrolling" -import { cmp } from "../line/pos" -import { replaceRange } from "../model/changes" -import { lst } from "../util/misc" +import { runInOp } from "../display/operations.js" +import { ensureCursorVisible } from "../display/scrolling.js" +import { cmp } from "../line/pos.js" +import { replaceRange } from "../model/changes.js" +import { lst } from "../util/misc.js" // Helper for deleting text near the selection(s), used to implement // backspace, delete, and similar functionality. diff --git a/src/edit/drop_events.js b/src/edit/drop_events.js index 43e996fb..12c760f0 100644 --- a/src/edit/drop_events.js +++ b/src/edit/drop_events.js @@ -1,16 +1,16 @@ -import { drawSelectionCursor } from "../display/selection" -import { operation } from "../display/operations" -import { clipPos } from "../line/pos" -import { posFromMouse } from "../measurement/position_measurement" -import { eventInWidget } from "../measurement/widgets" -import { makeChange, replaceRange } from "../model/changes" -import { changeEnd } from "../model/change_measurement" -import { simpleSelection } from "../model/selection" -import { setSelectionNoUndo, setSelectionReplaceHistory } from "../model/selection_updates" -import { ie, presto, safari } from "../util/browser" -import { elt, removeChildrenAndAdd } from "../util/dom" -import { e_preventDefault, e_stop, signalDOMEvent } from "../util/event" -import { indexOf } from "../util/misc" +import { drawSelectionCursor } from "../display/selection.js" +import { operation } from "../display/operations.js" +import { clipPos } from "../line/pos.js" +import { posFromMouse } from "../measurement/position_measurement.js" +import { eventInWidget } from "../measurement/widgets.js" +import { makeChange, replaceRange } from "../model/changes.js" +import { changeEnd } from "../model/change_measurement.js" +import { simpleSelection } from "../model/selection.js" +import { setSelectionNoUndo, setSelectionReplaceHistory } from "../model/selection_updates.js" +import { ie, presto, safari } from "../util/browser.js" +import { elt, removeChildrenAndAdd } from "../util/dom.js" +import { e_preventDefault, e_stop, signalDOMEvent } from "../util/event.js" +import { indexOf } from "../util/misc.js" // Kludge to work around strange IE behavior where it'll sometimes // re-fire a series of drag-related events right after the drop (#1551) diff --git a/src/edit/fromTextArea.js b/src/edit/fromTextArea.js index 5d920830..92498c10 100644 --- a/src/edit/fromTextArea.js +++ b/src/edit/fromTextArea.js @@ -1,7 +1,7 @@ -import { CodeMirror } from "./CodeMirror" -import { activeElt } from "../util/dom" -import { off, on } from "../util/event" -import { copyObj } from "../util/misc" +import { CodeMirror } from "./CodeMirror.js" +import { activeElt } from "../util/dom.js" +import { off, on } from "../util/event.js" +import { copyObj } from "../util/misc.js" export function fromTextArea(textarea, options) { options = options ? copyObj(options) : {} diff --git a/src/edit/global_events.js b/src/edit/global_events.js index b2ab7d57..269e870e 100644 --- a/src/edit/global_events.js +++ b/src/edit/global_events.js @@ -1,5 +1,5 @@ -import { onBlur } from "../display/focus" -import { on } from "../util/event" +import { onBlur } from "../display/focus.js" +import { on } from "../util/event.js" // These must be handled carefully, because naively registering a // handler for each editor will cause the editors to never be diff --git a/src/edit/key_events.js b/src/edit/key_events.js index 06f37be0..f0521d07 100644 --- a/src/edit/key_events.js +++ b/src/edit/key_events.js @@ -1,14 +1,14 @@ -import { signalLater } from "../util/operation_group" -import { restartBlink } from "../display/selection" -import { isModifierKey, keyName, lookupKey } from "../input/keymap" -import { eventInWidget } from "../measurement/widgets" -import { ie, ie_version, mac, presto } from "../util/browser" -import { activeElt, addClass, rmClass } from "../util/dom" -import { e_preventDefault, off, on, signalDOMEvent } from "../util/event" -import { hasCopyEvent } from "../util/feature_detection" -import { Delayed, Pass } from "../util/misc" - -import { commands } from "./commands" +import { signalLater } from "../util/operation_group.js" +import { restartBlink } from "../display/selection.js" +import { isModifierKey, keyName, lookupKey } from "../input/keymap.js" +import { eventInWidget } from "../measurement/widgets.js" +import { ie, ie_version, mac, presto } from "../util/browser.js" +import { activeElt, addClass, rmClass } from "../util/dom.js" +import { e_preventDefault, off, on, signalDOMEvent } from "../util/event.js" +import { hasCopyEvent } from "../util/feature_detection.js" +import { Delayed, Pass } from "../util/misc.js" + +import { commands } from "./commands.js" // Run a handler that was bound to a key. function doHandleBinding(cm, bound, dropShift) { diff --git a/src/edit/legacy.js b/src/edit/legacy.js index bc3df6c8..889badbe 100644 --- a/src/edit/legacy.js +++ b/src/edit/legacy.js @@ -1,21 +1,21 @@ -import { scrollbarModel } from "../display/scrollbars" -import { wheelEventPixels } from "../display/scroll_events" -import { keyMap, keyName, isModifierKey, lookupKey, normalizeKeyMap } from "../input/keymap" -import { keyNames } from "../input/keynames" -import { Line } from "../line/line_data" -import { cmp, Pos } from "../line/pos" -import { changeEnd } from "../model/change_measurement" -import Doc from "../model/Doc" -import { LineWidget } from "../model/line_widget" -import { SharedTextMarker, TextMarker } from "../model/mark_text" -import { copyState, extendMode, getMode, innerMode, mimeModes, modeExtensions, modes, resolveMode, startState } from "../modes" -import { addClass, contains, rmClass } from "../util/dom" -import { e_preventDefault, e_stop, e_stopPropagation, off, on, signal } from "../util/event" -import { splitLinesAuto } from "../util/feature_detection" -import { countColumn, findColumn, isWordCharBasic, Pass } from "../util/misc" -import StringStream from "../util/StringStream" +import { scrollbarModel } from "../display/scrollbars.js" +import { wheelEventPixels } from "../display/scroll_events.js" +import { keyMap, keyName, isModifierKey, lookupKey, normalizeKeyMap } from "../input/keymap.js" +import { keyNames } from "../input/keynames.js" +import { Line } from "../line/line_data.js" +import { cmp, Pos } from "../line/pos.js" +import { changeEnd } from "../model/change_measurement.js" +import Doc from "../model/Doc.js" +import { LineWidget } from "../model/line_widget.js" +import { SharedTextMarker, TextMarker } from "../model/mark_text.js" +import { copyState, extendMode, getMode, innerMode, mimeModes, modeExtensions, modes, resolveMode, startState } from "../modes.js" +import { addClass, contains, rmClass } from "../util/dom.js" +import { e_preventDefault, e_stop, e_stopPropagation, off, on, signal } from "../util/event.js" +import { splitLinesAuto } from "../util/feature_detection.js" +import { countColumn, findColumn, isWordCharBasic, Pass } from "../util/misc.js" +import StringStream from "../util/StringStream.js" -import { commands } from "./commands" +import { commands } from "./commands.js" export function addLegacyProps(CodeMirror) { CodeMirror.off = off diff --git a/src/edit/main.js b/src/edit/main.js index 6500e08d..6d9eb879 100644 --- a/src/edit/main.js +++ b/src/edit/main.js @@ -1,20 +1,20 @@ // EDITOR CONSTRUCTOR -import { CodeMirror } from "./CodeMirror" -export { CodeMirror } from "./CodeMirror" +import { CodeMirror } from "./CodeMirror.js" +export { CodeMirror } from "./CodeMirror.js" -import { eventMixin } from "../util/event" -import { indexOf } from "../util/misc" +import { eventMixin } from "../util/event.js" +import { indexOf } from "../util/misc.js" -import { defineOptions } from "./options" +import { defineOptions } from "./options.js" defineOptions(CodeMirror) -import addEditorMethods from "./methods" +import addEditorMethods from "./methods.js" addEditorMethods(CodeMirror) -import Doc from "../model/Doc" +import Doc from "../model/Doc.js" // Set up methods on CodeMirror's prototype to redirect to the editor's document. let dontDelegate = "iter insert remove copy getEditor constructor".split(" ") @@ -27,13 +27,13 @@ eventMixin(Doc) // INPUT HANDLING -import ContentEditableInput from "../input/ContentEditableInput" -import TextareaInput from "../input/TextareaInput" +import ContentEditableInput from "../input/ContentEditableInput.js" +import TextareaInput from "../input/TextareaInput.js" CodeMirror.inputStyles = {"textarea": TextareaInput, "contenteditable": ContentEditableInput} // MODE DEFINITION AND QUERYING -import { defineMIME, defineMode } from "../modes" +import { defineMIME, defineMode } from "../modes.js" // Extra arguments are stored as the mode's dependencies, which is // used by (legacy) mechanisms like loadmode.js to automatically @@ -58,11 +58,11 @@ CodeMirror.defineDocExtension = (name, func) => { Doc.prototype[name] = func } -import { fromTextArea } from "./fromTextArea" +import { fromTextArea } from "./fromTextArea.js" CodeMirror.fromTextArea = fromTextArea -import { addLegacyProps } from "./legacy" +import { addLegacyProps } from "./legacy.js" addLegacyProps(CodeMirror) diff --git a/src/edit/methods.js b/src/edit/methods.js index 8dc69252..5cefed7c 100644 --- a/src/edit/methods.js +++ b/src/edit/methods.js @@ -1,27 +1,27 @@ -import { deleteNearSelection } from "./deleteNearSelection" -import { commands } from "./commands" -import { attachDoc } from "../model/document_data" -import { activeElt, addClass, rmClass } from "../util/dom" -import { eventMixin, signal } from "../util/event" -import { getLineStyles, getContextBefore, takeToken } from "../line/highlight" -import { indentLine } from "../input/indent" -import { triggerElectric } from "../input/input" -import { onKeyDown, onKeyPress, onKeyUp } from "./key_events" -import { onMouseDown } from "./mouse_events" -import { getKeyMap } from "../input/keymap" -import { endOfLine, moveLogically, moveVisually } from "../input/movement" -import { endOperation, methodOp, operation, runInOp, startOperation } from "../display/operations" -import { clipLine, clipPos, equalCursorPos, Pos } from "../line/pos" -import { charCoords, charWidth, clearCaches, clearLineMeasurementCache, coordsChar, cursorCoords, displayHeight, displayWidth, estimateLineHeights, fromCoordSystem, intoCoordSystem, scrollGap, textHeight } from "../measurement/position_measurement" -import { Range } from "../model/selection" -import { replaceOneSelection, skipAtomic } from "../model/selection_updates" -import { addToScrollTop, ensureCursorVisible, scrollIntoView, scrollToCoords, scrollToCoordsRange, scrollToRange } from "../display/scrolling" -import { heightAtLine } from "../line/spans" -import { updateGutterSpace } from "../display/update_display" -import { indexOf, insertSorted, isWordChar, sel_dontScroll, sel_move } from "../util/misc" -import { signalLater } from "../util/operation_group" -import { getLine, isLine, lineAtHeight } from "../line/utils_line" -import { regChange, regLineChange } from "../display/view_tracking" +import { deleteNearSelection } from "./deleteNearSelection.js" +import { commands } from "./commands.js" +import { attachDoc } from "../model/document_data.js" +import { activeElt, addClass, rmClass } from "../util/dom.js" +import { eventMixin, signal } from "../util/event.js" +import { getLineStyles, getContextBefore, takeToken } from "../line/highlight.js" +import { indentLine } from "../input/indent.js" +import { triggerElectric } from "../input/input.js" +import { onKeyDown, onKeyPress, onKeyUp } from "./key_events.js" +import { onMouseDown } from "./mouse_events.js" +import { getKeyMap } from "../input/keymap.js" +import { endOfLine, moveLogically, moveVisually } from "../input/movement.js" +import { endOperation, methodOp, operation, runInOp, startOperation } from "../display/operations.js" +import { clipLine, clipPos, equalCursorPos, Pos } from "../line/pos.js" +import { charCoords, charWidth, clearCaches, clearLineMeasurementCache, coordsChar, cursorCoords, displayHeight, displayWidth, estimateLineHeights, fromCoordSystem, intoCoordSystem, scrollGap, textHeight } from "../measurement/position_measurement.js" +import { Range } from "../model/selection.js" +import { replaceOneSelection, skipAtomic } from "../model/selection_updates.js" +import { addToScrollTop, ensureCursorVisible, scrollIntoView, scrollToCoords, scrollToCoordsRange, scrollToRange } from "../display/scrolling.js" +import { heightAtLine } from "../line/spans.js" +import { updateGutterSpace } from "../display/update_display.js" +import { indexOf, insertSorted, isWordChar, sel_dontScroll, sel_move } from "../util/misc.js" +import { signalLater } from "../util/operation_group.js" +import { getLine, isLine, lineAtHeight } from "../line/utils_line.js" +import { regChange, regLineChange } from "../display/view_tracking.js" // The publicly visible API. Note that methodOp(f) means // 'wrap f in an operation, performed on its `this` parameter'. diff --git a/src/edit/mouse_events.js b/src/edit/mouse_events.js index 57159e3a..696a4bbf 100644 --- a/src/edit/mouse_events.js +++ b/src/edit/mouse_events.js @@ -1,23 +1,23 @@ -import { delayBlurEvent, ensureFocus } from "../display/focus" -import { operation } from "../display/operations" -import { visibleLines } from "../display/update_lines" -import { clipPos, cmp, maxPos, minPos, Pos } from "../line/pos" -import { getLine, lineAtHeight } from "../line/utils_line" -import { posFromMouse } from "../measurement/position_measurement" -import { eventInWidget } from "../measurement/widgets" -import { normalizeSelection, Range, Selection } from "../model/selection" -import { extendRange, extendSelection, replaceOneSelection, setSelection } from "../model/selection_updates" -import { captureRightClick, chromeOS, ie, ie_version, mac, webkit } from "../util/browser" -import { getOrder, getBidiPartAt } from "../util/bidi" -import { activeElt } from "../util/dom" -import { e_button, e_defaultPrevented, e_preventDefault, e_target, hasHandler, off, on, signal, signalDOMEvent } from "../util/event" -import { dragAndDrop } from "../util/feature_detection" -import { bind, countColumn, findColumn, sel_mouse } from "../util/misc" -import { addModifierNames } from "../input/keymap" -import { Pass } from "../util/misc" - -import { dispatchKey } from "./key_events" -import { commands } from "./commands" +import { delayBlurEvent, ensureFocus } from "../display/focus.js" +import { operation } from "../display/operations.js" +import { visibleLines } from "../display/update_lines.js" +import { clipPos, cmp, maxPos, minPos, Pos } from "../line/pos.js" +import { getLine, lineAtHeight } from "../line/utils_line.js" +import { posFromMouse } from "../measurement/position_measurement.js" +import { eventInWidget } from "../measurement/widgets.js" +import { normalizeSelection, Range, Selection } from "../model/selection.js" +import { extendRange, extendSelection, replaceOneSelection, setSelection } from "../model/selection_updates.js" +import { captureRightClick, chromeOS, ie, ie_version, mac, webkit } from "../util/browser.js" +import { getOrder, getBidiPartAt } from "../util/bidi.js" +import { activeElt } from "../util/dom.js" +import { e_button, e_defaultPrevented, e_preventDefault, e_target, hasHandler, off, on, signal, signalDOMEvent } from "../util/event.js" +import { dragAndDrop } from "../util/feature_detection.js" +import { bind, countColumn, findColumn, sel_mouse } from "../util/misc.js" +import { addModifierNames } from "../input/keymap.js" +import { Pass } from "../util/misc.js" + +import { dispatchKey } from "./key_events.js" +import { commands } from "./commands.js" const DOUBLECLICK_DELAY = 400 diff --git a/src/edit/options.js b/src/edit/options.js index 0601a83c..28f8bb60 100644 --- a/src/edit/options.js +++ b/src/edit/options.js @@ -1,21 +1,21 @@ -import { onBlur } from "../display/focus" -import { setGuttersForLineNumbers, updateGutters } from "../display/gutters" -import { alignHorizontally } from "../display/line_numbers" -import { loadMode, resetModeState } from "../display/mode_state" -import { initScrollbars, updateScrollbars } from "../display/scrollbars" -import { updateSelection } from "../display/selection" -import { regChange } from "../display/view_tracking" -import { getKeyMap } from "../input/keymap" -import { defaultSpecialCharPlaceholder } from "../line/line_data" -import { Pos } from "../line/pos" -import { findMaxLine } from "../line/spans" -import { clearCaches, compensateForHScroll, estimateLineHeights } from "../measurement/position_measurement" -import { replaceRange } from "../model/changes" -import { mobile, windows } from "../util/browser" -import { addClass, rmClass } from "../util/dom" -import { off, on } from "../util/event" - -import { themeChanged } from "./utils" +import { onBlur } from "../display/focus.js" +import { setGuttersForLineNumbers, updateGutters } from "../display/gutters.js" +import { alignHorizontally } from "../display/line_numbers.js" +import { loadMode, resetModeState } from "../display/mode_state.js" +import { initScrollbars, updateScrollbars } from "../display/scrollbars.js" +import { updateSelection } from "../display/selection.js" +import { regChange } from "../display/view_tracking.js" +import { getKeyMap } from "../input/keymap.js" +import { defaultSpecialCharPlaceholder } from "../line/line_data.js" +import { Pos } from "../line/pos.js" +import { findMaxLine } from "../line/spans.js" +import { clearCaches, compensateForHScroll, estimateLineHeights } from "../measurement/position_measurement.js" +import { replaceRange } from "../model/changes.js" +import { mobile, windows } from "../util/browser.js" +import { addClass, rmClass } from "../util/dom.js" +import { off, on } from "../util/event.js" + +import { themeChanged } from "./utils.js" export let Init = {toString: function(){return "CodeMirror.Init"}} diff --git a/src/edit/utils.js b/src/edit/utils.js index 61f79557..fda0be74 100644 --- a/src/edit/utils.js +++ b/src/edit/utils.js @@ -1,4 +1,4 @@ -import { clearCaches } from "../measurement/position_measurement" +import { clearCaches } from "../measurement/position_measurement.js" export function themeChanged(cm) { cm.display.wrapper.className = cm.display.wrapper.className.replace(/\s*cm-s-\S+/g, "") + diff --git a/src/input/ContentEditableInput.js b/src/input/ContentEditableInput.js index 67de3b18..e3af520a 100644 --- a/src/input/ContentEditableInput.js +++ b/src/input/ContentEditableInput.js @@ -1,18 +1,18 @@ -import { operation, runInOp } from "../display/operations" -import { prepareSelection } from "../display/selection" -import { regChange } from "../display/view_tracking" -import { applyTextInput, copyableRanges, disableBrowserMagic, handlePaste, hiddenTextarea, lastCopied, setLastCopied } from "./input" -import { cmp, maxPos, minPos, Pos } from "../line/pos" -import { getBetween, getLine, lineNo } from "../line/utils_line" -import { findViewForLine, findViewIndex, mapFromLineView, nodeAndOffsetInLineMap } from "../measurement/position_measurement" -import { replaceRange } from "../model/changes" -import { simpleSelection } from "../model/selection" -import { setSelection } from "../model/selection_updates" -import { getBidiPartAt, getOrder } from "../util/bidi" -import { android, chrome, gecko, ie_version } from "../util/browser" -import { contains, range, removeChildrenAndAdd, selectInput } from "../util/dom" -import { on, signalDOMEvent } from "../util/event" -import { Delayed, lst, sel_dontScroll } from "../util/misc" +import { operation, runInOp } from "../display/operations.js" +import { prepareSelection } from "../display/selection.js" +import { regChange } from "../display/view_tracking.js" +import { applyTextInput, copyableRanges, disableBrowserMagic, handlePaste, hiddenTextarea, lastCopied, setLastCopied } from "./input.js" +import { cmp, maxPos, minPos, Pos } from "../line/pos.js" +import { getBetween, getLine, lineNo } from "../line/utils_line.js" +import { findViewForLine, findViewIndex, mapFromLineView, nodeAndOffsetInLineMap } from "../measurement/position_measurement.js" +import { replaceRange } from "../model/changes.js" +import { simpleSelection } from "../model/selection.js" +import { setSelection } from "../model/selection_updates.js" +import { getBidiPartAt, getOrder } from "../util/bidi.js" +import { android, chrome, gecko, ie_version } from "../util/browser.js" +import { contains, range, removeChildrenAndAdd, selectInput } from "../util/dom.js" +import { on, signalDOMEvent } from "../util/event.js" +import { Delayed, lst, sel_dontScroll } from "../util/misc.js" // CONTENTEDITABLE INPUT STYLE diff --git a/src/input/TextareaInput.js b/src/input/TextareaInput.js index 3262ea1b..c0f04aaa 100644 --- a/src/input/TextareaInput.js +++ b/src/input/TextareaInput.js @@ -1,15 +1,15 @@ -import { operation, runInOp } from "../display/operations" -import { prepareSelection } from "../display/selection" -import { applyTextInput, copyableRanges, handlePaste, hiddenTextarea, setLastCopied } from "./input" -import { cursorCoords, posFromMouse } from "../measurement/position_measurement" -import { eventInWidget } from "../measurement/widgets" -import { simpleSelection } from "../model/selection" -import { selectAll, setSelection } from "../model/selection_updates" -import { captureRightClick, ie, ie_version, ios, mac, mobile, presto, webkit } from "../util/browser" -import { activeElt, removeChildrenAndAdd, selectInput } from "../util/dom" -import { e_preventDefault, e_stop, off, on, signalDOMEvent } from "../util/event" -import { hasSelection } from "../util/feature_detection" -import { Delayed, sel_dontScroll } from "../util/misc" +import { operation, runInOp } from "../display/operations.js" +import { prepareSelection } from "../display/selection.js" +import { applyTextInput, copyableRanges, handlePaste, hiddenTextarea, setLastCopied } from "./input.js" +import { cursorCoords, posFromMouse } from "../measurement/position_measurement.js" +import { eventInWidget } from "../measurement/widgets.js" +import { simpleSelection } from "../model/selection.js" +import { selectAll, setSelection } from "../model/selection_updates.js" +import { captureRightClick, ie, ie_version, ios, mac, mobile, presto, webkit } from "../util/browser.js" +import { activeElt, removeChildrenAndAdd, selectInput } from "../util/dom.js" +import { e_preventDefault, e_stop, off, on, signalDOMEvent } from "../util/event.js" +import { hasSelection } from "../util/feature_detection.js" +import { Delayed, sel_dontScroll } from "../util/misc.js" // TEXTAREA INPUT STYLE diff --git a/src/input/indent.js b/src/input/indent.js index 024f5f92..c88772cb 100644 --- a/src/input/indent.js +++ b/src/input/indent.js @@ -1,10 +1,10 @@ -import { getContextBefore } from "../line/highlight" -import { Pos } from "../line/pos" -import { getLine } from "../line/utils_line" -import { replaceRange } from "../model/changes" -import { Range } from "../model/selection" -import { replaceOneSelection } from "../model/selection_updates" -import { countColumn, Pass, spaceStr } from "../util/misc" +import { getContextBefore } from "../line/highlight.js" +import { Pos } from "../line/pos.js" +import { getLine } from "../line/utils_line.js" +import { replaceRange } from "../model/changes.js" +import { Range } from "../model/selection.js" +import { replaceOneSelection } from "../model/selection_updates.js" +import { countColumn, Pass, spaceStr } from "../util/misc.js" // Indent the given line. The how parameter can be "smart", // "add"/null, "subtract", or "prev". When aggressive is false diff --git a/src/input/input.js b/src/input/input.js index fa85209e..ff86c39d 100644 --- a/src/input/input.js +++ b/src/input/input.js @@ -1,15 +1,15 @@ -import { runInOp } from "../display/operations" -import { ensureCursorVisible } from "../display/scrolling" -import { Pos } from "../line/pos" -import { getLine } from "../line/utils_line" -import { makeChange } from "../model/changes" -import { ios, webkit } from "../util/browser" -import { elt } from "../util/dom" -import { lst, map } from "../util/misc" -import { signalLater } from "../util/operation_group" -import { splitLinesAuto } from "../util/feature_detection" +import { runInOp } from "../display/operations.js" +import { ensureCursorVisible } from "../display/scrolling.js" +import { Pos } from "../line/pos.js" +import { getLine } from "../line/utils_line.js" +import { makeChange } from "../model/changes.js" +import { ios, webkit } from "../util/browser.js" +import { elt } from "../util/dom.js" +import { lst, map } from "../util/misc.js" +import { signalLater } from "../util/operation_group.js" +import { splitLinesAuto } from "../util/feature_detection.js" -import { indentLine } from "./indent" +import { indentLine } from "./indent.js" // This will be set to a {lineWise: bool, text: [string]} object, so // that, when pasting, we know what kind of selections the copied diff --git a/src/input/keymap.js b/src/input/keymap.js index 36ac3e61..1dfcf8af 100644 --- a/src/input/keymap.js +++ b/src/input/keymap.js @@ -1,7 +1,7 @@ -import { flipCtrlCmd, mac, presto } from "../util/browser" -import { map } from "../util/misc" +import { flipCtrlCmd, mac, presto } from "../util/browser.js" +import { map } from "../util/misc.js" -import { keyNames } from "./keynames" +import { keyNames } from "./keynames.js" export let keyMap = {} diff --git a/src/input/movement.js b/src/input/movement.js index 927ed6e1..8d50fd2a 100644 --- a/src/input/movement.js +++ b/src/input/movement.js @@ -1,7 +1,7 @@ -import { Pos } from "../line/pos" -import { prepareMeasureForLine, measureCharPrepared, wrappedLineExtentChar } from "../measurement/position_measurement" -import { getBidiPartAt, getOrder } from "../util/bidi" -import { findFirst, lst, skipExtendingChars } from "../util/misc" +import { Pos } from "../line/pos.js" +import { prepareMeasureForLine, measureCharPrepared, wrappedLineExtentChar } from "../measurement/position_measurement.js" +import { getBidiPartAt, getOrder } from "../util/bidi.js" +import { findFirst, lst, skipExtendingChars } from "../util/misc.js" function moveCharLogically(line, ch, dir) { let target = skipExtendingChars(line.text, ch + dir, dir) diff --git a/src/line/highlight.js b/src/line/highlight.js index c5e6b8aa..79f08845 100644 --- a/src/line/highlight.js +++ b/src/line/highlight.js @@ -1,9 +1,9 @@ -import { countColumn } from "../util/misc" -import { copyState, innerMode, startState } from "../modes" -import StringStream from "../util/StringStream" +import { countColumn } from "../util/misc.js" +import { copyState, innerMode, startState } from "../modes.js" +import StringStream from "../util/StringStream.js" -import { getLine, lineNo } from "./utils_line" -import { clipPos } from "./pos" +import { getLine, lineNo } from "./utils_line.js" +import { clipPos } from "./pos.js" class SavedContext { constructor(state, lookAhead) { diff --git a/src/line/line_data.js b/src/line/line_data.js index e444184b..74acdaff 100644 --- a/src/line/line_data.js +++ b/src/line/line_data.js @@ -1,13 +1,13 @@ -import { getOrder } from "../util/bidi" -import { ie, ie_version, webkit } from "../util/browser" -import { elt, eltP, joinClasses } from "../util/dom" -import { eventMixin, signal } from "../util/event" -import { hasBadBidiRects, zeroWidthElement } from "../util/feature_detection" -import { lst, spaceStr } from "../util/misc" +import { getOrder } from "../util/bidi.js" +import { ie, ie_version, webkit } from "../util/browser.js" +import { elt, eltP, joinClasses } from "../util/dom.js" +import { eventMixin, signal } from "../util/event.js" +import { hasBadBidiRects, zeroWidthElement } from "../util/feature_detection.js" +import { lst, spaceStr } from "../util/misc.js" -import { getLineStyles } from "./highlight" -import { attachMarkedSpans, compareCollapsedMarkers, detachMarkedSpans, lineIsHidden, visualLineContinued } from "./spans" -import { getLine, lineNo, updateLineHeight } from "./utils_line" +import { getLineStyles } from "./highlight.js" +import { attachMarkedSpans, compareCollapsedMarkers, detachMarkedSpans, lineIsHidden, visualLineContinued } from "./spans.js" +import { getLine, lineNo, updateLineHeight } from "./utils_line.js" // LINE DATA STRUCTURE diff --git a/src/line/pos.js b/src/line/pos.js index 4f5e4c55..2a498f8f 100644 --- a/src/line/pos.js +++ b/src/line/pos.js @@ -1,4 +1,4 @@ -import { getLine } from "./utils_line" +import { getLine } from "./utils_line.js" // A Pos instance represents a position within the text. export function Pos(line, ch, sticky = null) { diff --git a/src/line/spans.js b/src/line/spans.js index 6c413d2f..f7e5f4b6 100644 --- a/src/line/spans.js +++ b/src/line/spans.js @@ -1,8 +1,8 @@ -import { indexOf, lst } from "../util/misc" +import { indexOf, lst } from "../util/misc.js" -import { cmp } from "./pos" -import { sawCollapsedSpans } from "./saw_special_spans" -import { getLine, isLine, lineNo } from "./utils_line" +import { cmp } from "./pos.js" +import { sawCollapsedSpans } from "./saw_special_spans.js" +import { getLine, isLine, lineNo } from "./utils_line.js" // TEXTMARKER SPANS diff --git a/src/line/utils_line.js b/src/line/utils_line.js index e4e6943f..c8862943 100644 --- a/src/line/utils_line.js +++ b/src/line/utils_line.js @@ -1,4 +1,4 @@ -import { indexOf } from "../util/misc" +import { indexOf } from "../util/misc.js" // Find the line object corresponding to the given line number. export function getLine(doc, n) { diff --git a/src/measurement/position_measurement.js b/src/measurement/position_measurement.js index 78986e03..aeff0e5b 100644 --- a/src/measurement/position_measurement.js +++ b/src/measurement/position_measurement.js @@ -1,16 +1,16 @@ -import { buildLineContent, LineView } from "../line/line_data" -import { clipPos, Pos } from "../line/pos" -import { collapsedSpanAtEnd, heightAtLine, lineIsHidden, visualLine } from "../line/spans" -import { getLine, lineAtHeight, lineNo, updateLineHeight } from "../line/utils_line" -import { bidiOther, getBidiPartAt, getOrder } from "../util/bidi" -import { chrome, android, ie, ie_version } from "../util/browser" -import { elt, removeChildren, range, removeChildrenAndAdd } from "../util/dom" -import { e_target } from "../util/event" -import { hasBadZoomedRects } from "../util/feature_detection" -import { countColumn, findFirst, isExtendingChar, scrollerGap, skipExtendingChars } from "../util/misc" -import { updateLineForChanges } from "../display/update_line" - -import { widgetHeight } from "./widgets" +import { buildLineContent, LineView } from "../line/line_data.js" +import { clipPos, Pos } from "../line/pos.js" +import { collapsedSpanAtEnd, heightAtLine, lineIsHidden, visualLine } from "../line/spans.js" +import { getLine, lineAtHeight, lineNo, updateLineHeight } from "../line/utils_line.js" +import { bidiOther, getBidiPartAt, getOrder } from "../util/bidi.js" +import { chrome, android, ie, ie_version } from "../util/browser.js" +import { elt, removeChildren, range, removeChildrenAndAdd } from "../util/dom.js" +import { e_target } from "../util/event.js" +import { hasBadZoomedRects } from "../util/feature_detection.js" +import { countColumn, findFirst, isExtendingChar, scrollerGap, skipExtendingChars } from "../util/misc.js" +import { updateLineForChanges } from "../display/update_line.js" + +import { widgetHeight } from "./widgets.js" // POSITION MEASUREMENT diff --git a/src/measurement/widgets.js b/src/measurement/widgets.js index 554cf809..39d7553d 100644 --- a/src/measurement/widgets.js +++ b/src/measurement/widgets.js @@ -1,5 +1,5 @@ -import { contains, elt, removeChildrenAndAdd } from "../util/dom" -import { e_target } from "../util/event" +import { contains, elt, removeChildrenAndAdd } from "../util/dom.js" +import { e_target } from "../util/event.js" export function widgetHeight(widget) { if (widget.height != null) return widget.height diff --git a/src/model/Doc.js b/src/model/Doc.js index c3da76d7..b64ac843 100644 --- a/src/model/Doc.js +++ b/src/model/Doc.js @@ -1,23 +1,23 @@ -import CodeMirror from "../edit/CodeMirror" -import { docMethodOp } from "../display/operations" -import { Line } from "../line/line_data" -import { clipPos, clipPosArray, Pos } from "../line/pos" -import { visualLine } from "../line/spans" -import { getBetween, getLine, getLines, isLine, lineNo } from "../line/utils_line" -import { classTest } from "../util/dom" -import { splitLinesAuto } from "../util/feature_detection" -import { createObj, map, isEmpty, sel_dontScroll } from "../util/misc" -import { ensureCursorVisible, scrollToCoords } from "../display/scrolling" - -import { changeLine, makeChange, makeChangeFromHistory, replaceRange } from "./changes" -import { computeReplacedSel } from "./change_measurement" -import { BranchChunk, LeafChunk } from "./chunk" -import { directionChanged, linkedDocs, updateDoc } from "./document_data" -import { copyHistoryArray, History } from "./history" -import { addLineWidget } from "./line_widget" -import { copySharedMarkers, detachSharedMarkers, findSharedMarkers, markText } from "./mark_text" -import { normalizeSelection, Range, simpleSelection } from "./selection" -import { extendSelection, extendSelections, setSelection, setSelectionReplaceHistory, setSimpleSelection } from "./selection_updates" +import CodeMirror from "../edit/CodeMirror.js" +import { docMethodOp } from "../display/operations.js" +import { Line } from "../line/line_data.js" +import { clipPos, clipPosArray, Pos } from "../line/pos.js" +import { visualLine } from "../line/spans.js" +import { getBetween, getLine, getLines, isLine, lineNo } from "../line/utils_line.js" +import { classTest } from "../util/dom.js" +import { splitLinesAuto } from "../util/feature_detection.js" +import { createObj, map, isEmpty, sel_dontScroll } from "../util/misc.js" +import { ensureCursorVisible, scrollToCoords } from "../display/scrolling.js" + +import { changeLine, makeChange, makeChangeFromHistory, replaceRange } from "./changes.js" +import { computeReplacedSel } from "./change_measurement.js" +import { BranchChunk, LeafChunk } from "./chunk.js" +import { directionChanged, linkedDocs, updateDoc } from "./document_data.js" +import { copyHistoryArray, History } from "./history.js" +import { addLineWidget } from "./line_widget.js" +import { copySharedMarkers, detachSharedMarkers, findSharedMarkers, markText } from "./mark_text.js" +import { normalizeSelection, Range, simpleSelection } from "./selection.js" +import { extendSelection, extendSelections, setSelection, setSelectionReplaceHistory, setSimpleSelection } from "./selection_updates.js" let nextDocId = 0 let Doc = function(text, mode, firstLine, lineSep, direction) { diff --git a/src/model/change_measurement.js b/src/model/change_measurement.js index 881f39eb..4d45313d 100644 --- a/src/model/change_measurement.js +++ b/src/model/change_measurement.js @@ -1,7 +1,7 @@ -import { cmp, Pos } from "../line/pos" -import { lst } from "../util/misc" +import { cmp, Pos } from "../line/pos.js" +import { lst } from "../util/misc.js" -import { normalizeSelection, Range, Selection } from "./selection" +import { normalizeSelection, Range, Selection } from "./selection.js" // Compute the position of the end of a change (its 'to' property // refers to the pre-change end). diff --git a/src/model/changes.js b/src/model/changes.js index cfad529c..b00e29b1 100644 --- a/src/model/changes.js +++ b/src/model/changes.js @@ -1,21 +1,21 @@ -import { retreatFrontier } from "../line/highlight" -import { startWorker } from "../display/highlight_worker" -import { operation } from "../display/operations" -import { regChange, regLineChange } from "../display/view_tracking" -import { clipLine, clipPos, cmp, Pos } from "../line/pos" -import { sawReadOnlySpans } from "../line/saw_special_spans" -import { lineLength, removeReadOnlyRanges, stretchSpansOverChange, visualLine } from "../line/spans" -import { getBetween, getLine, lineNo } from "../line/utils_line" -import { estimateHeight } from "../measurement/position_measurement" -import { hasHandler, signal, signalCursorActivity } from "../util/event" -import { indexOf, lst, map, sel_dontScroll } from "../util/misc" -import { signalLater } from "../util/operation_group" - -import { changeEnd, computeSelAfterChange } from "./change_measurement" -import { isWholeLineUpdate, linkedDocs, updateDoc } from "./document_data" -import { addChangeToHistory, historyChangeFromChange, mergeOldSpans, pushSelectionToHistory } from "./history" -import { Range, Selection } from "./selection" -import { setSelection, setSelectionNoUndo } from "./selection_updates" +import { retreatFrontier } from "../line/highlight.js" +import { startWorker } from "../display/highlight_worker.js" +import { operation } from "../display/operations.js" +import { regChange, regLineChange } from "../display/view_tracking.js" +import { clipLine, clipPos, cmp, Pos } from "../line/pos.js" +import { sawReadOnlySpans } from "../line/saw_special_spans.js" +import { lineLength, removeReadOnlyRanges, stretchSpansOverChange, visualLine } from "../line/spans.js" +import { getBetween, getLine, lineNo } from "../line/utils_line.js" +import { estimateHeight } from "../measurement/position_measurement.js" +import { hasHandler, signal, signalCursorActivity } from "../util/event.js" +import { indexOf, lst, map, sel_dontScroll } from "../util/misc.js" +import { signalLater } from "../util/operation_group.js" + +import { changeEnd, computeSelAfterChange } from "./change_measurement.js" +import { isWholeLineUpdate, linkedDocs, updateDoc } from "./document_data.js" +import { addChangeToHistory, historyChangeFromChange, mergeOldSpans, pushSelectionToHistory } from "./history.js" +import { Range, Selection } from "./selection.js" +import { setSelection, setSelectionNoUndo } from "./selection_updates.js" // UPDATING diff --git a/src/model/chunk.js b/src/model/chunk.js index 056ef91b..d82716de 100644 --- a/src/model/chunk.js +++ b/src/model/chunk.js @@ -1,6 +1,6 @@ -import { cleanUpLine } from "../line/line_data" -import { indexOf } from "../util/misc" -import { signalLater } from "../util/operation_group" +import { cleanUpLine } from "../line/line_data.js" +import { indexOf } from "../util/misc.js" +import { signalLater } from "../util/operation_group.js" // The document is represented as a BTree consisting of leaves, with // chunk of lines in them, and branches, with up to ten leaves or diff --git a/src/model/document_data.js b/src/model/document_data.js index 7f6e3367..d946e7af 100644 --- a/src/model/document_data.js +++ b/src/model/document_data.js @@ -1,13 +1,13 @@ -import { loadMode } from "../display/mode_state" -import { runInOp } from "../display/operations" -import { regChange } from "../display/view_tracking" -import { Line, updateLine } from "../line/line_data" -import { findMaxLine } from "../line/spans" -import { getLine } from "../line/utils_line" -import { estimateLineHeights } from "../measurement/position_measurement" -import { addClass, rmClass } from "../util/dom" -import { lst } from "../util/misc" -import { signalLater } from "../util/operation_group" +import { loadMode } from "../display/mode_state.js" +import { runInOp } from "../display/operations.js" +import { regChange } from "../display/view_tracking.js" +import { Line, updateLine } from "../line/line_data.js" +import { findMaxLine } from "../line/spans.js" +import { getLine } from "../line/utils_line.js" +import { estimateLineHeights } from "../measurement/position_measurement.js" +import { addClass, rmClass } from "../util/dom.js" +import { lst } from "../util/misc.js" +import { signalLater } from "../util/operation_group.js" // DOCUMENT DATA STRUCTURE diff --git a/src/model/history.js b/src/model/history.js index 83938cf4..753a89da 100644 --- a/src/model/history.js +++ b/src/model/history.js @@ -1,12 +1,12 @@ -import { cmp, copyPos } from "../line/pos" -import { stretchSpansOverChange } from "../line/spans" -import { getBetween } from "../line/utils_line" -import { signal } from "../util/event" -import { indexOf, lst } from "../util/misc" - -import { changeEnd } from "./change_measurement" -import { linkedDocs } from "./document_data" -import { Selection } from "./selection" +import { cmp, copyPos } from "../line/pos.js" +import { stretchSpansOverChange } from "../line/spans.js" +import { getBetween } from "../line/utils_line.js" +import { signal } from "../util/event.js" +import { indexOf, lst } from "../util/misc.js" + +import { changeEnd } from "./change_measurement.js" +import { linkedDocs } from "./document_data.js" +import { Selection } from "./selection.js" export function History(startGen) { // Arrays of change events and selections. Doing something adds an diff --git a/src/model/line_widget.js b/src/model/line_widget.js index a11f9c27..4a82d538 100644 --- a/src/model/line_widget.js +++ b/src/model/line_widget.js @@ -1,12 +1,12 @@ -import { runInOp } from "../display/operations" -import { addToScrollTop } from "../display/scrolling" -import { regLineChange } from "../display/view_tracking" -import { heightAtLine, lineIsHidden } from "../line/spans" -import { lineNo, updateLineHeight } from "../line/utils_line" -import { widgetHeight } from "../measurement/widgets" -import { changeLine } from "./changes" -import { eventMixin } from "../util/event" -import { signalLater } from "../util/operation_group" +import { runInOp } from "../display/operations.js" +import { addToScrollTop } from "../display/scrolling.js" +import { regLineChange } from "../display/view_tracking.js" +import { heightAtLine, lineIsHidden } from "../line/spans.js" +import { lineNo, updateLineHeight } from "../line/utils_line.js" +import { widgetHeight } from "../measurement/widgets.js" +import { changeLine } from "./changes.js" +import { eventMixin } from "../util/event.js" +import { signalLater } from "../util/operation_group.js" // Line widgets are block elements displayed above or below a line. diff --git a/src/model/mark_text.js b/src/model/mark_text.js index ccdcc9d3..955c72c4 100644 --- a/src/model/mark_text.js +++ b/src/model/mark_text.js @@ -1,19 +1,19 @@ -import { eltP } from "../util/dom" -import { eventMixin, hasHandler, on } from "../util/event" -import { endOperation, operation, runInOp, startOperation } from "../display/operations" -import { clipPos, cmp, Pos } from "../line/pos" -import { lineNo, updateLineHeight } from "../line/utils_line" -import { clearLineMeasurementCacheFor, findViewForLine, textHeight } from "../measurement/position_measurement" -import { seeReadOnlySpans, seeCollapsedSpans } from "../line/saw_special_spans" -import { addMarkedSpan, conflictingCollapsedRange, getMarkedSpanFor, lineIsHidden, lineLength, MarkedSpan, removeMarkedSpan, visualLine } from "../line/spans" -import { copyObj, indexOf, lst } from "../util/misc" -import { signalLater } from "../util/operation_group" -import { widgetHeight } from "../measurement/widgets" -import { regChange, regLineChange } from "../display/view_tracking" +import { eltP } from "../util/dom.js" +import { eventMixin, hasHandler, on } from "../util/event.js" +import { endOperation, operation, runInOp, startOperation } from "../display/operations.js" +import { clipPos, cmp, Pos } from "../line/pos.js" +import { lineNo, updateLineHeight } from "../line/utils_line.js" +import { clearLineMeasurementCacheFor, findViewForLine, textHeight } from "../measurement/position_measurement.js" +import { seeReadOnlySpans, seeCollapsedSpans } from "../line/saw_special_spans.js" +import { addMarkedSpan, conflictingCollapsedRange, getMarkedSpanFor, lineIsHidden, lineLength, MarkedSpan, removeMarkedSpan, visualLine } from "../line/spans.js" +import { copyObj, indexOf, lst } from "../util/misc.js" +import { signalLater } from "../util/operation_group.js" +import { widgetHeight } from "../measurement/widgets.js" +import { regChange, regLineChange } from "../display/view_tracking.js" -import { linkedDocs } from "./document_data" -import { addChangeToHistory } from "./history" -import { reCheckSelection } from "./selection_updates" +import { linkedDocs } from "./document_data.js" +import { addChangeToHistory } from "./history.js" +import { reCheckSelection } from "./selection_updates.js" // TEXTMARKERS diff --git a/src/model/selection.js b/src/model/selection.js index 97084fbc..2e374aa8 100644 --- a/src/model/selection.js +++ b/src/model/selection.js @@ -1,5 +1,5 @@ -import { cmp, copyPos, equalCursorPos, maxPos, minPos } from "../line/pos" -import { indexOf } from "../util/misc" +import { cmp, copyPos, equalCursorPos, maxPos, minPos } from "../line/pos.js" +import { indexOf } from "../util/misc.js" // Selection objects are immutable. A new one is created every time // the selection changes. A selection is one or more non-overlapping diff --git a/src/model/selection_updates.js b/src/model/selection_updates.js index bf5ad8c7..77986a9e 100644 --- a/src/model/selection_updates.js +++ b/src/model/selection_updates.js @@ -1,12 +1,12 @@ -import { signalLater } from "../util/operation_group" -import { ensureCursorVisible } from "../display/scrolling" -import { clipPos, cmp, Pos } from "../line/pos" -import { getLine } from "../line/utils_line" -import { hasHandler, signal, signalCursorActivity } from "../util/event" -import { lst, sel_dontScroll } from "../util/misc" - -import { addSelectionToHistory } from "./history" -import { normalizeSelection, Range, Selection, simpleSelection } from "./selection" +import { signalLater } from "../util/operation_group.js" +import { ensureCursorVisible } from "../display/scrolling.js" +import { clipPos, cmp, Pos } from "../line/pos.js" +import { getLine } from "../line/utils_line.js" +import { hasHandler, signal, signalCursorActivity } from "../util/event.js" +import { lst, sel_dontScroll } from "../util/misc.js" + +import { addSelectionToHistory } from "./history.js" +import { normalizeSelection, Range, Selection, simpleSelection } from "./selection.js" // The 'scroll' parameter given to many of these indicated whether // the new cursor position should be scrolled into view after diff --git a/src/modes.js b/src/modes.js index 065a463b..83845170 100644 --- a/src/modes.js +++ b/src/modes.js @@ -1,4 +1,4 @@ -import { copyObj, createObj } from "./util/misc" +import { copyObj, createObj } from "./util/misc.js" // Known modes, by name and by MIME export let modes = {}, mimeModes = {} diff --git a/src/util/StringStream.js b/src/util/StringStream.js index a14b1b64..022c4bc2 100644 --- a/src/util/StringStream.js +++ b/src/util/StringStream.js @@ -1,4 +1,4 @@ -import { countColumn } from "./misc" +import { countColumn } from "./misc.js" // STRING STREAM diff --git a/src/util/bidi.js b/src/util/bidi.js index 3d13dd86..33ab854d 100644 --- a/src/util/bidi.js +++ b/src/util/bidi.js @@ -1,4 +1,4 @@ -import { lst } from "./misc" +import { lst } from "./misc.js" // BIDI HELPERS diff --git a/src/util/dom.js b/src/util/dom.js index 94823c21..04d2569d 100644 --- a/src/util/dom.js +++ b/src/util/dom.js @@ -1,4 +1,4 @@ -import { ie, ios } from "./browser" +import { ie, ios } from "./browser.js" export function classTest(cls) { return new RegExp("(^|\\s)" + cls + "(?:$|\\s)\\s*") } diff --git a/src/util/event.js b/src/util/event.js index 29fd4c59..4b6c7705 100644 --- a/src/util/event.js +++ b/src/util/event.js @@ -1,5 +1,5 @@ -import { mac } from "./browser" -import { indexOf } from "./misc" +import { mac } from "./browser.js" +import { indexOf } from "./misc.js" // EVENT HANDLING diff --git a/src/util/feature_detection.js b/src/util/feature_detection.js index e65881d4..c33734eb 100644 --- a/src/util/feature_detection.js +++ b/src/util/feature_detection.js @@ -1,5 +1,5 @@ -import { elt, range, removeChildren, removeChildrenAndAdd } from "./dom" -import { ie, ie_version } from "./browser" +import { elt, range, removeChildren, removeChildrenAndAdd } from "./dom.js" +import { ie, ie_version } from "./browser.js" // Detect drag-and-drop export let dragAndDrop = function() { diff --git a/src/util/operation_group.js b/src/util/operation_group.js index b8fa78ac..f6815949 100644 --- a/src/util/operation_group.js +++ b/src/util/operation_group.js @@ -1,4 +1,4 @@ -import { getHandlers } from "./event" +import { getHandlers } from "./event.js" let operationGroup = null From 85fb7510976c8e0d443d44b7788c3066541fc470 Mon Sep 17 00:00:00 2001 From: Marijn Haverbeke Date: Thu, 9 Nov 2017 09:49:42 +0100 Subject: [PATCH 1140/1790] [javascript mode] Recognize async when in front of single-line block comment Closes #5078 --- 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 5c772526..139e53df 100644 --- a/mode/javascript/javascript.js +++ b/mode/javascript/javascript.js @@ -153,7 +153,7 @@ CodeMirror.defineMode("javascript", function(config, parserConfig) { var kw = keywords[word] return ret(kw.type, kw.style, word) } - if (word == "async" && stream.match(/^\s*[\(\w]/, false)) + if (word == "async" && stream.match(/^(\s|\/\*.*?\*\/)*[\(\w]/, false)) return ret("async", "keyword", word) } return ret("variable", "variable", word) From 1ba861a09f01f7205c36fb467660ed970a1c0054 Mon Sep 17 00:00:00 2001 From: Joel Einbinder Date: Wed, 8 Nov 2017 15:56:23 -0800 Subject: [PATCH 1141/1790] [javascript mode] Test for comments between async and function keywords --- mode/javascript/test.js | 3 +++ 1 file changed, 3 insertions(+) diff --git a/mode/javascript/test.js b/mode/javascript/test.js index 213bab06..d560fdba 100644 --- a/mode/javascript/test.js +++ b/mode/javascript/test.js @@ -230,6 +230,9 @@ "[keyword const] [def async] [operator =] {[property a]: [number 1]};", "[keyword const] [def foo] [operator =] [string-2 `bar ${][variable async].[property a][string-2 }`];") + MT("async_comment", + "[keyword async] [comment /**/] [keyword function] [def foo]([def args]) { [keyword return] [atom true]; }"); + MT("indent_switch", "[keyword switch] ([variable x]) {", " [keyword default]:", From a29e048d20e5a256dd48bc49e1afae6f9d1a252a Mon Sep 17 00:00:00 2001 From: Jakub Vrana Date: Fri, 10 Nov 2017 14:29:19 +0100 Subject: [PATCH 1142/1790] [soy mode] Support comments in all contexts --- mode/soy/soy.js | 46 ++++++++++++++++++++++------------------------ 1 file changed, 22 insertions(+), 24 deletions(-) diff --git a/mode/soy/soy.js b/mode/soy/soy.js index 0e244570..98f30865 100644 --- a/mode/soy/soy.js +++ b/mode/soy/soy.js @@ -137,6 +137,25 @@ } return "comment"; + case "string": + var match = stream.match(/^.*?(["']|\\[\s\S])/); + if (!match) { + stream.skipToEnd(); + } else if (match[1] == state.quoteKind) { + state.quoteKind = null; + state.soyState.pop(); + } + return "string"; + } + + if (stream.match(/^\/\*/)) { + state.soyState.push("comment"); + return "comment"; + } else if (stream.match(stream.sol() || (state.soyState.length && last(state.soyState) != "literal") ? /^\s*\/\/.*/ : /^\s+\/\/.*/)) { + return "comment"; + } + + switch (last(state.soyState)) { case "templ-def": if (match = stream.match(/^\.?([\w]+(?!\.[\w]+)*)/)) { state.templates = prepend(state.templates, match[1]); @@ -242,36 +261,15 @@ return this.token(stream, state); } return tokenUntil(stream, state, /\{\/literal}/); - - case "string": - var match = stream.match(/^.*?(["']|\\[\s\S])/); - if (!match) { - stream.skipToEnd(); - } else if (match[1] == state.quoteKind) { - state.quoteKind = null; - state.soyState.pop(); - } - return "string"; } - if (stream.match(/^\/\*/)) { - state.soyState.push("comment"); - if (!state.scopes) { - state.variables = prepend(null, 'ij'); - } - return "comment"; - } else if (stream.match(stream.sol() ? /^\s*\/\/.*/ : /^\s+\/\/.*/)) { - if (!state.scopes) { - state.variables = prepend(null, 'ij'); - } - return "comment"; - } else if (stream.match(/^\{literal}/)) { + if (stream.match(/^\{literal}/)) { state.indent += config.indentUnit; state.soyState.push("literal"); return "keyword"; - // A tag-keyword must be followed by whitespace or a closing tag. - } else if (match = stream.match(/^\{([\/@\\]?\w+\??)(?=[\s\}])/)) { + // A tag-keyword must be followed by whitespace, comment or a closing tag. + } else if (match = stream.match(/^\{([\/@\\]?\w+\??)(?=[\s\}]|\/[/*])/)) { if (match[1] != "/switch") state.indent += (/^(\/|(else|elseif|ifempty|case|fallbackmsg|default)$)/.test(match[1]) && state.tag != "switch" ? 1 : 2) * config.indentUnit; state.tag = match[1]; From b881f2520461c7fc98ca1673071d0eb74ddd7c39 Mon Sep 17 00:00:00 2001 From: Marijn Haverbeke Date: Mon, 13 Nov 2017 09:40:40 +0100 Subject: [PATCH 1143/1790] [emacs mode] Prevent backspace/delete/etc from adding to the kill ring Closes #5084 --- keymap/emacs.js | 42 +++++++++++++++++++++--------------------- test/emacs_test.js | 2 ++ 2 files changed, 23 insertions(+), 21 deletions(-) diff --git a/keymap/emacs.js b/keymap/emacs.js index 33db0c15..31604532 100644 --- a/keymap/emacs.js +++ b/keymap/emacs.js @@ -30,16 +30,16 @@ var lastKill = null; - function kill(cm, from, to, mayGrow, text) { + function kill(cm, from, to, ring, text) { if (text == null) text = cm.getRange(from, to); - if (mayGrow && lastKill && lastKill.cm == cm && posEq(from, lastKill.pos) && cm.isClean(lastKill.gen)) + if (ring == "grow" && lastKill && lastKill.cm == cm && posEq(from, lastKill.pos) && cm.isClean(lastKill.gen)) growRingTop(text); - else + else if (ring !== false) addToRing(text); cm.replaceRange("", from, to, "+delete"); - if (mayGrow) lastKill = {cm: cm, pos: from, gen: cm.changeGeneration()}; + if (ring == "grow") lastKill = {cm: cm, pos: from, gen: cm.changeGeneration()}; else lastKill = null; } @@ -151,22 +151,22 @@ return f; } - function killTo(cm, by, dir) { + function killTo(cm, by, dir, ring) { var selections = cm.listSelections(), cursor; var i = selections.length; while (i--) { cursor = selections[i].head; - kill(cm, cursor, findEnd(cm, cursor, by, dir), true); + kill(cm, cursor, findEnd(cm, cursor, by, dir), ring); } } - function killRegion(cm) { + function killRegion(cm, ring) { if (cm.somethingSelected()) { var selections = cm.listSelections(), selection; var i = selections.length; while (i--) { selection = selections[i]; - kill(cm, selection.anchor, selection.head); + kill(cm, selection.anchor, selection.head, ring); } return true; } @@ -276,7 +276,7 @@ // Actual keymap var keyMap = CodeMirror.keyMap.emacs = CodeMirror.normalizeKeyMap({ - "Ctrl-W": function(cm) {kill(cm, cm.getCursor("start"), cm.getCursor("end"));}, + "Ctrl-W": function(cm) {kill(cm, cm.getCursor("start"), cm.getCursor("end"), true);}, "Ctrl-K": repeated(function(cm) { var start = cm.getCursor(), end = cm.clipPos(Pos(start.line)); var text = cm.getRange(start, end); @@ -284,7 +284,7 @@ text += "\n"; end = Pos(start.line + 1, 0); } - kill(cm, start, end, true, text); + kill(cm, start, end, "grow", text); }), "Alt-W": function(cm) { addToRing(cm.getSelection()); @@ -301,14 +301,14 @@ "Ctrl-F": move(byChar, 1), "Ctrl-B": move(byChar, -1), "Right": move(byChar, 1), "Left": move(byChar, -1), - "Ctrl-D": function(cm) { killTo(cm, byChar, 1); }, - "Delete": function(cm) { killRegion(cm) || killTo(cm, byChar, 1); }, - "Ctrl-H": function(cm) { killTo(cm, byChar, -1); }, - "Backspace": function(cm) { killRegion(cm) || killTo(cm, byChar, -1); }, + "Ctrl-D": function(cm) { killTo(cm, byChar, 1, false); }, + "Delete": function(cm) { killRegion(cm, false) || killTo(cm, byChar, 1, false); }, + "Ctrl-H": function(cm) { killTo(cm, byChar, -1, false); }, + "Backspace": function(cm) { killRegion(cm, false) || killTo(cm, byChar, -1, false); }, "Alt-F": move(byWord, 1), "Alt-B": move(byWord, -1), - "Alt-D": function(cm) { killTo(cm, byWord, 1); }, - "Alt-Backspace": function(cm) { killTo(cm, byWord, -1); }, + "Alt-D": function(cm) { killTo(cm, byWord, 1, "grow"); }, + "Alt-Backspace": function(cm) { killTo(cm, byWord, -1, "grow"); }, "Ctrl-N": move(byLine, 1), "Ctrl-P": move(byLine, -1), "Down": move(byLine, 1), "Up": move(byLine, -1), @@ -321,11 +321,11 @@ "Ctrl-Up": move(byParagraph, -1), "Ctrl-Down": move(byParagraph, 1), "Alt-A": move(bySentence, -1), "Alt-E": move(bySentence, 1), - "Alt-K": function(cm) { killTo(cm, bySentence, 1); }, + "Alt-K": function(cm) { killTo(cm, bySentence, 1, "grow"); }, - "Ctrl-Alt-K": function(cm) { killTo(cm, byExpr, 1); }, - "Ctrl-Alt-Backspace": function(cm) { killTo(cm, byExpr, -1); }, - "Ctrl-Alt-F": move(byExpr, 1), "Ctrl-Alt-B": move(byExpr, -1), + "Ctrl-Alt-K": function(cm) { killTo(cm, byExpr, 1, "grow"); }, + "Ctrl-Alt-Backspace": function(cm) { killTo(cm, byExpr, -1, "grow"); }, + "Ctrl-Alt-F": move(byExpr, 1), "Ctrl-Alt-B": move(byExpr, -1, "grow"), "Shift-Ctrl-Alt-2": function(cm) { var cursor = cm.getCursor(); @@ -398,7 +398,7 @@ "Ctrl-X F": "open", "Ctrl-X U": repeated("undo"), "Ctrl-X K": "close", - "Ctrl-X Delete": function(cm) { kill(cm, cm.getCursor(), bySentence(cm, cm.getCursor(), 1), true); }, + "Ctrl-X Delete": function(cm) { kill(cm, cm.getCursor(), bySentence(cm, cm.getCursor(), 1), "grow"); }, "Ctrl-X H": "selectAll", "Ctrl-Q Tab": repeated("insertTab"), diff --git a/test/emacs_test.js b/test/emacs_test.js index b73eedaa..412dba4b 100644 --- a/test/emacs_test.js +++ b/test/emacs_test.js @@ -131,6 +131,8 @@ sim("delRegion", "abcde", "Ctrl-Space", "Ctrl-F", "Ctrl-F", "Delete", txt("cde")); sim("backspaceRegion", "abcde", "Ctrl-Space", "Ctrl-F", "Ctrl-F", "Backspace", txt("cde")); + sim("backspaceDoesntAddToRing", "foobar", "Ctrl-F", "Ctrl-F", "Ctrl-F", "Ctrl-K", "Backspace", "Backspace", "Ctrl-Y", txt("fbar")); + testCM("save", function(cm) { var saved = false; CodeMirror.commands.save = function(cm) { saved = cm.getValue(); }; From 2cb90ecdce8e7814b0b45a10e3bf6aed48d9a2a7 Mon Sep 17 00:00:00 2001 From: Adrian Heine Date: Fri, 17 Nov 2017 20:45:01 +0100 Subject: [PATCH 1144/1790] [javascript mode] Highlight type in generic call Closes #5048. --- mode/javascript/javascript.js | 1 + mode/javascript/test.js | 3 +++ 2 files changed, 4 insertions(+) diff --git a/mode/javascript/javascript.js b/mode/javascript/javascript.js index 139e53df..e43543dd 100644 --- a/mode/javascript/javascript.js +++ b/mode/javascript/javascript.js @@ -438,6 +438,7 @@ CodeMirror.defineMode("javascript", function(config, parserConfig) { if (type == "=>") return cont(pushcontext, noComma ? arrowBodyNoComma : arrowBody, popcontext); if (type == "operator") { if (/\+\+|--/.test(value) || isTS && value == "!") return cont(me); + if (isTS && value == "<") return cont(pushlex(">"), commasep(typeexpr, ">"), poplex, maybeoperatorNoComma); if (value == "?") return cont(expression, expect(":"), expr); return cont(expr); } diff --git a/mode/javascript/test.js b/mode/javascript/test.js index d560fdba..972a3456 100644 --- a/mode/javascript/test.js +++ b/mode/javascript/test.js @@ -371,6 +371,9 @@ TS("arrow prop", "({[property a]: [def p] [operator =>] [variable-2 p]})") + TS("generic in function call", + "[keyword this].[property a][operator <][type Type][operator >]([variable foo]);") + var jsonld_mode = CodeMirror.getMode( {indentUnit: 2}, {name: "javascript", jsonld: true} From 490653454aa985feb60918eeddde823c550e416e Mon Sep 17 00:00:00 2001 From: Marijn Haverbeke Date: Mon, 20 Nov 2017 10:36:30 +0100 Subject: [PATCH 1145/1790] [javascript mode] Resolve ambiguity for type parameters vs less-than See https://github.com/Microsoft/TypeScript/blob/6c4c10c7cf294dc71f943314e29a7dd1b6e88c6a/doc/spec.md#4.15.3 Issue #5090 Issue #5048 --- mode/javascript/javascript.js | 3 ++- mode/javascript/test.js | 3 ++- 2 files changed, 4 insertions(+), 2 deletions(-) diff --git a/mode/javascript/javascript.js b/mode/javascript/javascript.js index e43543dd..0dcd8af3 100644 --- a/mode/javascript/javascript.js +++ b/mode/javascript/javascript.js @@ -438,7 +438,8 @@ CodeMirror.defineMode("javascript", function(config, parserConfig) { if (type == "=>") return cont(pushcontext, noComma ? arrowBodyNoComma : arrowBody, popcontext); if (type == "operator") { if (/\+\+|--/.test(value) || isTS && value == "!") return cont(me); - if (isTS && value == "<") return cont(pushlex(">"), commasep(typeexpr, ">"), poplex, maybeoperatorNoComma); + if (isTS && value == "<" && cx.stream.match(/^([^>]|<.*?>)*>\s*\(/, false)) + return cont(pushlex(">"), commasep(typeexpr, ">"), poplex, me); if (value == "?") return cont(expression, expect(":"), expr); return cont(expr); } diff --git a/mode/javascript/test.js b/mode/javascript/test.js index 972a3456..2437edcc 100644 --- a/mode/javascript/test.js +++ b/mode/javascript/test.js @@ -372,7 +372,8 @@ "({[property a]: [def p] [operator =>] [variable-2 p]})") TS("generic in function call", - "[keyword this].[property a][operator <][type Type][operator >]([variable foo]);") + "[keyword this].[property a][operator <][type Type][operator >]([variable foo]);", + "[keyword this].[property a][operator <][variable Type][operator >][variable foo];") var jsonld_mode = CodeMirror.getMode( {indentUnit: 2}, From e51b94ff4890d01d0ee1b97da575440b65429edf Mon Sep 17 00:00:00 2001 From: Marijn Haverbeke Date: Mon, 20 Nov 2017 12:16:06 +0100 Subject: [PATCH 1146/1790] [show-hint addon] Drop suspicious-looking logic Issue #4792 --- addon/hint/show-hint.js | 6 ------ 1 file changed, 6 deletions(-) diff --git a/addon/hint/show-hint.js b/addon/hint/show-hint.js index f72a0a9c..62c683cb 100644 --- a/addon/hint/show-hint.js +++ b/addon/hint/show-hint.js @@ -121,7 +121,6 @@ var picked = (this.widget && this.widget.picked) || (first && this.options.completeSingle); if (this.widget) this.widget.close(); - if (data && this.data && isNewCompletion(this.data, data)) return; this.data = data; if (data && data.list.length) { @@ -135,11 +134,6 @@ } }; - function isNewCompletion(old, nw) { - var moved = CodeMirror.cmpPos(nw.from, old.from) - return moved > 0 && old.to.ch - old.from.ch != nw.to.ch - nw.from.ch - } - function parseOptions(cm, pos, options) { var editor = cm.options.hintOptions; var out = {}; From 66414ce35ba441b766117b5f8cf53a1850a7362e Mon Sep 17 00:00:00 2001 From: Marijn Haverbeke Date: Wed, 22 Nov 2017 09:59:21 +0100 Subject: [PATCH 1147/1790] [javascript mode] Recognize TypeScript type guards Closes #5093 --- mode/javascript/javascript.js | 14 +++++++++++++- mode/javascript/test.js | 8 ++++++++ 2 files changed, 21 insertions(+), 1 deletion(-) diff --git a/mode/javascript/javascript.js b/mode/javascript/javascript.js index 0dcd8af3..4978d8d1 100644 --- a/mode/javascript/javascript.js +++ b/mode/javascript/javascript.js @@ -566,6 +566,18 @@ CodeMirror.defineMode("javascript", function(config, parserConfig) { if (value == "?") return cont(maybetype); } } + function mayberettype(type, value) { + if (isTS && type == ":") { + if (cx.stream.match(/^\s*\w+\s+is\b/, false)) return cont(expression, isKW, typeexpr) + else return cont(typeexpr) + } + } + function isKW(_, value) { + if (value == "is") { + cx.marked = "keyword" + return cont() + } + } function typeexpr(type, value) { if (type == "variable" || value == "void") { if (value == "keyof") { @@ -668,7 +680,7 @@ CodeMirror.defineMode("javascript", function(config, parserConfig) { function functiondef(type, value) { if (value == "*") {cx.marked = "keyword"; return cont(functiondef);} if (type == "variable") {register(value); return cont(functiondef);} - if (type == "(") return cont(pushcontext, pushlex(")"), commasep(funarg, ")"), poplex, maybetype, statement, popcontext); + if (type == "(") return cont(pushcontext, pushlex(")"), commasep(funarg, ")"), poplex, mayberettype, statement, popcontext); if (isTS && value == "<") return cont(pushlex(">"), commasep(typeparam, ">"), poplex, functiondef) } function funarg(type, value) { diff --git a/mode/javascript/test.js b/mode/javascript/test.js index 2437edcc..167e6d01 100644 --- a/mode/javascript/test.js +++ b/mode/javascript/test.js @@ -375,6 +375,14 @@ "[keyword this].[property a][operator <][type Type][operator >]([variable foo]);", "[keyword this].[property a][operator <][variable Type][operator >][variable foo];") + TS("type guard", + "[keyword class] [def Appler] {", + " [keyword static] [property assertApple]([def fruit]: [type Fruit]): [variable-2 fruit] [keyword is] [type Apple] {", + " [keyword if] ([operator !]([variable-2 fruit] [keyword instanceof] [variable Apple]))", + " [keyword throw] [keyword new] [variable Error]();", + " }", + "}") + var jsonld_mode = CodeMirror.getMode( {indentUnit: 2}, {name: "javascript", jsonld: true} From d98aac7b948ef36d0611e5d50f461e3848b925b2 Mon Sep 17 00:00:00 2001 From: Marijn Haverbeke Date: Wed, 22 Nov 2017 10:01:54 +0100 Subject: [PATCH 1148/1790] Please linter, remove unused arg --- CHANGELOG.md | 4 ++++ mode/javascript/javascript.js | 2 +- 2 files changed, 5 insertions(+), 1 deletion(-) diff --git a/CHANGELOG.md b/CHANGELOG.md index 409c7234..1fa57816 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -1,3 +1,7 @@ +## 5.32.0 (2017-11-22) + + + ## 5.31.0 (2017-10-20) ### Bug fixes diff --git a/mode/javascript/javascript.js b/mode/javascript/javascript.js index 4978d8d1..514de1c8 100644 --- a/mode/javascript/javascript.js +++ b/mode/javascript/javascript.js @@ -566,7 +566,7 @@ CodeMirror.defineMode("javascript", function(config, parserConfig) { if (value == "?") return cont(maybetype); } } - function mayberettype(type, value) { + function mayberettype(type) { if (isTS && type == ":") { if (cx.stream.match(/^\s*\w+\s+is\b/, false)) return cont(expression, isKW, typeexpr) else return cont(typeexpr) From 89595f55b19ea0584e4721128c149f918795a384 Mon Sep 17 00:00:00 2001 From: Marijn Haverbeke Date: Wed, 22 Nov 2017 10:10:50 +0100 Subject: [PATCH 1149/1790] Mark version 5.32.0 --- AUTHORS | 3 +++ CHANGELOG.md | 16 ++++++++++++++++ doc/manual.html | 2 +- doc/releases.html | 12 ++++++++++++ index.html | 2 +- package.json | 2 +- src/edit/main.js | 2 +- 7 files changed, 35 insertions(+), 4 deletions(-) diff --git a/AUTHORS b/AUTHORS index f800b86b..65d84808 100644 --- a/AUTHORS +++ b/AUTHORS @@ -118,6 +118,7 @@ Caitlin Potter Calin Barbat callodacity Camilo Roca +Casey Klebba Chad Jolly Chandra Sekhar Pydi Charles Skelton @@ -300,6 +301,7 @@ Jason Grout Jason Johnston Jason San Jose Jason Siefken +Jayaprabhakar Jaydeep Solanki Jean Boussier Jeff Blaisdell @@ -327,6 +329,7 @@ John Van Der Loo Jon Ander Peñalba Jonas Döbertin Jonas Helfer +Jonathan Hart Jonathan Malmaud Jon Gacnik jongalloway diff --git a/CHANGELOG.md b/CHANGELOG.md index 1fa57816..f81fcdd0 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -1,6 +1,22 @@ ## 5.32.0 (2017-11-22) +### Bug fixes + +Increase contrast on default bracket-matching colors. + +[javascript mode](http://codemirror.net/mode/javascript/): Recognize TypeScript type parameters for calls, type guards, and type parameter defaults. Improve handling of `enum` and `module` keywords. + +[comment addon](http://codemirror.net/doc/manual.html#addon_comment): Fix bug when uncommenting a comment that spans all but the last selected line. + +[searchcursor addon](http://codemirror.net/doc/manual.html#addon_searchcursor): Fix bug in case folding. + +[emacs bindings](http://codemirror.net/demo/emacs.html): Prevent single-character deletions from resetting the kill ring. + +[closebrackets addon](http://codemirror.net/doc/manual.html#addon_closebrackets): Tweak quote matching behavior. + +### New features +[continuelist addon](http://codemirror.net/doc/manual.html#addon_continuelist): Increment ordered list numbers when adding one. ## 5.31.0 (2017-10-20) diff --git a/doc/manual.html b/doc/manual.html index 7666e0df..f46e6bd5 100644 --- a/doc/manual.html +++ b/doc/manual.html @@ -69,7 +69,7 @@

      User manual and reference guide - version 5.31.1 + version 5.32.0

      CodeMirror is a code-editor component that can be embedded in diff --git a/doc/releases.html b/doc/releases.html index 23de4c80..7fb8eebe 100644 --- a/doc/releases.html +++ b/doc/releases.html @@ -30,6 +30,18 @@

      Release notes and version history

      Version 5.x

      +

      22-11-2017: Version 5.32.0:

      + +
        +
      • Increase contrast on default bracket-matching colors.
      • +
      • javascript mode: Recognize TypeScript type parameters for calls, type guards, and type parameter defaults. Improve handling of enum and module keywords.
      • +
      • comment addon: Fix bug when uncommenting a comment that spans all but the last selected line.
      • +
      • searchcursor addon: Fix bug in case folding.
      • +
      • emacs bindings: Prevent single-character deletions from resetting the kill ring.
      • +
      • closebrackets addon: Tweak quote matching behavior.
      • +
      • continuelist addon: Increment ordered list numbers when adding one.
      • +
      +

      20-10-2017: Version 5.31.0:

        diff --git a/index.html b/index.html index 555e4804..d62ab84a 100644 --- a/index.html +++ b/index.html @@ -96,7 +96,7 @@

        This is CodeMirror

      - Get the current version: 5.31.0.
      + Get the current version: 5.32.0.
      You can see the code,
      read the release notes,
      or study the user manual. diff --git a/package.json b/package.json index 6eda061f..3e9f20c6 100644 --- a/package.json +++ b/package.json @@ -1,6 +1,6 @@ { "name": "codemirror", - "version": "5.31.1", + "version": "5.32.0", "main": "lib/codemirror.js", "style": "lib/codemirror.css", "description": "Full-featured in-browser code editor", diff --git a/src/edit/main.js b/src/edit/main.js index 6d9eb879..c89e5d87 100644 --- a/src/edit/main.js +++ b/src/edit/main.js @@ -66,4 +66,4 @@ import { addLegacyProps } from "./legacy.js" addLegacyProps(CodeMirror) -CodeMirror.version = "5.31.1" +CodeMirror.version = "5.32.0" From 2e857fa36604aebe0a7c1955a9ca5137ee00e3f5 Mon Sep 17 00:00:00 2001 From: Marijn Haverbeke Date: Wed, 22 Nov 2017 10:12:55 +0100 Subject: [PATCH 1150/1790] Bump version number post-5.32 --- doc/manual.html | 2 +- package.json | 2 +- src/edit/main.js | 2 +- 3 files changed, 3 insertions(+), 3 deletions(-) diff --git a/doc/manual.html b/doc/manual.html index f46e6bd5..ea5642cd 100644 --- a/doc/manual.html +++ b/doc/manual.html @@ -69,7 +69,7 @@

      User manual and reference guide - version 5.32.0 + version 5.32.1

      CodeMirror is a code-editor component that can be embedded in diff --git a/package.json b/package.json index 3e9f20c6..3ffad1d5 100644 --- a/package.json +++ b/package.json @@ -1,6 +1,6 @@ { "name": "codemirror", - "version": "5.32.0", + "version": "5.32.1", "main": "lib/codemirror.js", "style": "lib/codemirror.css", "description": "Full-featured in-browser code editor", diff --git a/src/edit/main.js b/src/edit/main.js index c89e5d87..260f7d0a 100644 --- a/src/edit/main.js +++ b/src/edit/main.js @@ -66,4 +66,4 @@ import { addLegacyProps } from "./legacy.js" addLegacyProps(CodeMirror) -CodeMirror.version = "5.32.0" +CodeMirror.version = "5.32.1" From 058e8219b2d040b131eb99ca7ad00b53bafe9886 Mon Sep 17 00:00:00 2001 From: satamas Date: Thu, 23 Nov 2017 16:32:43 +0300 Subject: [PATCH 1151/1790] Add new kotlin keywords. --- 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 02a85319..ff00cf50 100644 --- a/mode/clike/clike.js +++ b/mode/clike/clike.js @@ -578,7 +578,7 @@ CodeMirror.defineMode("clike", function(config, parserConfig) { "file import where by get set abstract enum open inner override private public internal " + "protected catch finally out final vararg reified dynamic companion constructor init " + "sealed field property receiver param sparam lateinit data inline noinline tailrec " + - "external annotation crossinline const operator infix suspend" + "external annotation crossinline const operator infix suspend actual expect" ), types: words( /* package java.lang */ From d60e0ccaadaaa2f9bc967594909def5f80231a22 Mon Sep 17 00:00:00 2001 From: Marijn Haverbeke Date: Mon, 27 Nov 2017 11:19:46 +0100 Subject: [PATCH 1152/1790] [tern addon] Guard against relatedTarget being null Closes #5095 --- addon/tern/tern.js | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-) diff --git a/addon/tern/tern.js b/addon/tern/tern.js index a80dc7e4..70202c6f 100644 --- a/addon/tern/tern.js +++ b/addon/tern/tern.js @@ -614,7 +614,8 @@ var mouseOnTip = false, old = false; CodeMirror.on(tip, "mousemove", function() { mouseOnTip = true; }); CodeMirror.on(tip, "mouseout", function(e) { - if (!CodeMirror.contains(tip, e.relatedTarget || e.toElement)) { + let related = e.relatedTarget || e.toElement + if (!related || !CodeMirror.contains(tip, related)) { if (old) clear(); else mouseOnTip = false; } From 95b64d1a1c8004aab0f9e9ea39a0c48689d4e028 Mon Sep 17 00:00:00 2001 From: Marijn Haverbeke Date: Mon, 27 Nov 2017 11:23:17 +0100 Subject: [PATCH 1153/1790] Fix es6-ism in addon --- addon/tern/tern.js | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/addon/tern/tern.js b/addon/tern/tern.js index 70202c6f..6276b538 100644 --- a/addon/tern/tern.js +++ b/addon/tern/tern.js @@ -614,7 +614,7 @@ var mouseOnTip = false, old = false; CodeMirror.on(tip, "mousemove", function() { mouseOnTip = true; }); CodeMirror.on(tip, "mouseout", function(e) { - let related = e.relatedTarget || e.toElement + var related = e.relatedTarget || e.toElement if (!related || !CodeMirror.contains(tip, related)) { if (old) clear(); else mouseOnTip = false; From 8bb35c475f0dfc05c4b2617e778fed1acf3f3a68 Mon Sep 17 00:00:00 2001 From: Marijn Haverbeke Date: Fri, 1 Dec 2017 10:24:21 +0100 Subject: [PATCH 1154/1790] [lint addon] Wrap display updates in an operation Closes #5106 --- addon/lint/lint.js | 6 +++--- 1 file changed, 3 insertions(+), 3 deletions(-) diff --git a/addon/lint/lint.js b/addon/lint/lint.js index a9eb8fa6..e00e77a2 100644 --- a/addon/lint/lint.js +++ b/addon/lint/lint.js @@ -132,7 +132,7 @@ cm.off("change", abort) if (state.waitingFor != id) return if (arg2 && annotations instanceof CodeMirror) annotations = arg2 - updateLinting(cm, annotations) + cm.operation(function() {updateLinting(cm, annotations)}) }, passOptions, cm); } @@ -151,9 +151,9 @@ var annotations = getAnnotations(cm.getValue(), passOptions, cm); if (!annotations) return; if (annotations.then) annotations.then(function(issues) { - updateLinting(cm, issues); + cm.operation(function() {updateLinting(cm, issues)}) }); - else updateLinting(cm, annotations); + else cm.operation(function() {updateLinting(cm, annotations)}) } } From b1bf7b3ad53941dc81351de2a28896daf293d739 Mon Sep 17 00:00:00 2001 From: tophf Date: Fri, 1 Dec 2017 06:01:55 +0300 Subject: [PATCH 1155/1790] [css mode] Case-insensitive parsing of grammar tokens as per https://www.w3.org/TR/css-syntax-3/#rule-defs --- mode/css/css.js | 18 +++++++++--------- 1 file changed, 9 insertions(+), 9 deletions(-) diff --git a/mode/css/css.js b/mode/css/css.js index 00e9b3df..f5f3a41b 100644 --- a/mode/css/css.js +++ b/mode/css/css.js @@ -77,9 +77,9 @@ CodeMirror.defineMode("css", function(config, parserConfig) { return ret("qualifier", "qualifier"); } else if (/[:;{}\[\]\(\)]/.test(ch)) { return ret(null, ch); - } else if ((ch == "u" && stream.match(/rl(-prefix)?\(/)) || - (ch == "d" && stream.match("omain(")) || - (ch == "r" && stream.match("egexp("))) { + } else if (((ch == "u" || ch == "U") && stream.match(/rl(-prefix)?\(/i)) || + ((ch == "d" || ch == "D") && stream.match("omain(", true, true)) || + ((ch == "r" || ch == "R") && stream.match("egexp(", true, true))) { stream.backUp(1); state.tokenize = tokenParenthesized; return ret("property", "word"); @@ -162,16 +162,16 @@ CodeMirror.defineMode("css", function(config, parserConfig) { return pushContext(state, stream, "block"); } else if (type == "}" && state.context.prev) { return popContext(state); - } else if (supportsAtComponent && /@component/.test(type)) { + } else if (supportsAtComponent && /@component/i.test(type)) { return pushContext(state, stream, "atComponentBlock"); - } else if (/^@(-moz-)?document$/.test(type)) { + } else if (/^@(-moz-)?document$/i.test(type)) { return pushContext(state, stream, "documentTypes"); - } else if (/^@(media|supports|(-moz-)?document|import)$/.test(type)) { + } else if (/^@(media|supports|(-moz-)?document|import)$/i.test(type)) { return pushContext(state, stream, "atBlock"); - } else if (/^@(font-face|counter-style)/.test(type)) { + } else if (/^@(font-face|counter-style)/i.test(type)) { state.stateArg = type; return "restricted_atBlock_before"; - } else if (/^@(-(moz|ms|o|webkit)-)?keyframes$/.test(type)) { + } else if (/^@(-(moz|ms|o|webkit)-)?keyframes$/i.test(type)) { return "keyframes"; } else if (type && type.charAt(0) == "@") { return pushContext(state, stream, "at"); @@ -793,7 +793,7 @@ CodeMirror.defineMode("css", function(config, parserConfig) { }, "@": function(stream) { if (stream.eat("{")) return [null, "interpolation"]; - if (stream.match(/^(charset|document|font-face|import|(-(moz|ms|o|webkit)-)?keyframes|media|namespace|page|supports)\b/, false)) return false; + if (stream.match(/^(charset|document|font-face|import|(-(moz|ms|o|webkit)-)?keyframes|media|namespace|page|supports)\b/i, false)) return false; stream.eatWhile(/[\w\\\-]/); if (stream.match(/^\s*:/, false)) return ["variable-2", "variable-definition"]; From 74e7447cf72205189e01eefced32fd2f2a7c79b9 Mon Sep 17 00:00:00 2001 From: Marijn Haverbeke Date: Fri, 1 Dec 2017 10:45:55 +0100 Subject: [PATCH 1156/1790] [css mode] Add a test for an upper-case @-block Issue #5107 --- mode/css/test.js | 3 +++ 1 file changed, 3 insertions(+) diff --git a/mode/css/test.js b/mode/css/test.js index 6fc6e33c..e5c55d39 100644 --- a/mode/css/test.js +++ b/mode/css/test.js @@ -24,6 +24,9 @@ MT("atMediaUnknownFeatureValueKeyword", "[def @media] ([property orientation]: [error upsidedown]) { }"); + MT("atMediaUppercase", + "[def @MEDIA] ([property orienTAtion]: [keyword landScape]) { }"); + MT("tagSelector", "[tag foo] { }"); From c7853a989c77bb9f520c9c530cbe1497856e96fc Mon Sep 17 00:00:00 2001 From: Marijn Haverbeke Date: Sun, 3 Dec 2017 12:00:32 +0100 Subject: [PATCH 1157/1790] [continuelist addon] Fix handling of unordered lists --- addon/edit/continuelist.js | 8 +++----- 1 file changed, 3 insertions(+), 5 deletions(-) diff --git a/addon/edit/continuelist.js b/addon/edit/continuelist.js index 30893965..b83dc505 100644 --- a/addon/edit/continuelist.js +++ b/addon/edit/continuelist.js @@ -39,13 +39,11 @@ replacements[i] = "\n"; } else { var indent = match[1], after = match[5]; - var bullet = unorderedListRE.test(match[2]) || match[2].indexOf(">") >= 0 - ? match[2].replace("x", " ") - : (parseInt(match[3], 10) + 1) + match[4]; - + var numbered = !(unorderedListRE.test(match[2]) || match[2].indexOf(">") >= 0); + var bullet = numbered ? (parseInt(match[3], 10) + 1) + match[4] : match[2].replace("x", " "); replacements[i] = "\n" + indent + bullet + after; - incrementRemainingMarkdownListNumbers(cm, pos); + if (numbered) incrementRemainingMarkdownListNumbers(cm, pos); } } From 6353583cdf780aaa74d9afd88df404b34a4c31ad Mon Sep 17 00:00:00 2001 From: Stephane Moore Date: Mon, 4 Dec 2017 16:56:18 -0800 Subject: [PATCH 1158/1790] =?UTF-8?q?[languages]=20Fix=20the=20name=20of?= =?UTF-8?q?=20Objective-C=20=F0=9F=93=9B?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit The language is named "Objective-C" rather than "Objective C". Reference: https://en.wikipedia.org/wiki/Objective-C --- mode/meta.js | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/mode/meta.js b/mode/meta.js index 34da269f..91a92526 100644 --- a/mode/meta.js +++ b/mode/meta.js @@ -94,7 +94,7 @@ {name: "NSIS", mime: "text/x-nsis", mode: "nsis", ext: ["nsh", "nsi"]}, {name: "NTriples", mimes: ["application/n-triples", "application/n-quads", "text/n-triples"], mode: "ntriples", ext: ["nt", "nq"]}, - {name: "Objective C", mime: "text/x-objectivec", mode: "clike", ext: ["m", "mm"], alias: ["objective-c", "objc"]}, + {name: "Objective-C", mime: "text/x-objectivec", mode: "clike", ext: ["m", "mm"], alias: ["objective-c", "objc"]}, {name: "OCaml", mime: "text/x-ocaml", mode: "mllike", ext: ["ml", "mli", "mll", "mly"]}, {name: "Octave", mime: "text/x-octave", mode: "octave", ext: ["m"]}, {name: "Oz", mime: "text/x-oz", mode: "oz", ext: ["oz"]}, From 04d39f236a541104c3fccdfdf6ea164d6e4c74e7 Mon Sep 17 00:00:00 2001 From: Marijn Haverbeke Date: Tue, 5 Dec 2017 10:03:37 +0100 Subject: [PATCH 1159/1790] [htmlembedded mode] Support %-style comments Closes #5102 --- mode/htmlembedded/htmlembedded.js | 9 +++++++++ 1 file changed, 9 insertions(+) diff --git a/mode/htmlembedded/htmlembedded.js b/mode/htmlembedded/htmlembedded.js index 464dc57f..8099d370 100644 --- a/mode/htmlembedded/htmlembedded.js +++ b/mode/htmlembedded/htmlembedded.js @@ -14,7 +14,16 @@ "use strict"; CodeMirror.defineMode("htmlembedded", function(config, parserConfig) { + var closeComment = parserConfig.closeComment || "--%>" return CodeMirror.multiplexingMode(CodeMirror.getMode(config, "htmlmixed"), { + open: parserConfig.openComment || "<%--", + close: closeComment, + delimStyle: "comment", + mode: {token: function(stream) { + stream.skipTo(closeComment) || stream.skipToEnd() + return "comment" + }} + }, { open: parserConfig.open || parserConfig.scriptStartRegex || "<%", close: parserConfig.close || parserConfig.scriptEndRegex || "%>", mode: CodeMirror.getMode(config, parserConfig.scriptingModeSpec) From 7e480de547d7b87690c654522d603d7aa213e64b Mon Sep 17 00:00:00 2001 From: Adrian Heine Date: Tue, 5 Dec 2017 22:43:37 +0100 Subject: [PATCH 1160/1790] [vim] Support more bases for increment and decrement Closes #5110. --- keymap/vim.js | 21 ++-- test/vim_test.js | 246 +++++++++++++++++++++++++++++++++++++++++++++++ 2 files changed, 260 insertions(+), 7 deletions(-) diff --git a/keymap/vim.js b/keymap/vim.js index 7cf5a956..b0822681 100644 --- a/keymap/vim.js +++ b/keymap/vim.js @@ -2637,25 +2637,32 @@ incrementNumberToken: function(cm, actionArgs) { var cur = cm.getCursor(); var lineStr = cm.getLine(cur.line); - var re = /-?\d+/g; + var re = /(-?)(?:(0x)([\da-f]+)|(0b|0|)(\d+))/gi; 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; + end = start + match[0].length; if (cur.ch < end)break; } if (!actionArgs.backtrack && (end <= cur.ch))return; - if (token) { + if (match) { + var baseStr = match[2] || match[4] + var digits = match[3] || match[5] var increment = actionArgs.increase ? 1 : -1; - var number = parseInt(token) + (increment * actionArgs.repeat); + var base = {'0b': 2, '0': 8, '': 10, '0x': 16}[baseStr.toLowerCase()]; + var number = parseInt(match[1] + digits, base) + (increment * actionArgs.repeat); + numberStr = number.toString(base); + var zeroPadding = baseStr ? new Array(digits.length - numberStr.length + 1 + match[1].length).join('0') : '' + if (numberStr.charAt(0) === '-') { + numberStr = '-' + baseStr + zeroPadding + numberStr.substr(1); + } else { + numberStr = baseStr + zeroPadding + numberStr; + } var from = Pos(cur.line, start); var to = Pos(cur.line, end); - numberStr = number.toString(); cm.replaceRange(numberStr, from, to); } else { return; diff --git a/test/vim_test.js b/test/vim_test.js index 18268ee7..5a42b90f 100644 --- a/test/vim_test.js +++ b/test/vim_test.js @@ -4235,4 +4235,250 @@ testVim('beforeSelectionChange', function(cm, vim, helpers) { eqCursorPos(cm.getCursor('head'), cm.getCursor('anchor')); }, { value: 'abc' }); +testVim('increment_binary', function(cm, vim, helpers) { + cm.setCursor(0, 4); + helpers.doKeys(''); + eq('0b001', cm.getValue()); + helpers.doKeys(''); + eq('0b010', cm.getValue()); + helpers.doKeys(''); + eq('0b001', cm.getValue()); + helpers.doKeys(''); + eq('0b000', cm.getValue()); + cm.setCursor(0, 0); + helpers.doKeys(''); + eq('0b001', cm.getValue()); + helpers.doKeys(''); + eq('0b010', cm.getValue()); + helpers.doKeys(''); + eq('0b001', cm.getValue()); + helpers.doKeys(''); + eq('0b000', cm.getValue()); +}, { value: '0b000' }); + +testVim('increment_octal', function(cm, vim, helpers) { + cm.setCursor(0, 2); + helpers.doKeys(''); + eq('001', cm.getValue()); + helpers.doKeys(''); + eq('002', cm.getValue()); + helpers.doKeys(''); + eq('003', cm.getValue()); + helpers.doKeys(''); + eq('004', cm.getValue()); + helpers.doKeys(''); + eq('005', cm.getValue()); + helpers.doKeys(''); + eq('006', cm.getValue()); + helpers.doKeys(''); + eq('007', cm.getValue()); + helpers.doKeys(''); + eq('010', cm.getValue()); + helpers.doKeys(''); + eq('007', cm.getValue()); + helpers.doKeys(''); + eq('006', cm.getValue()); + helpers.doKeys(''); + eq('005', cm.getValue()); + helpers.doKeys(''); + eq('004', cm.getValue()); + helpers.doKeys(''); + eq('003', cm.getValue()); + helpers.doKeys(''); + eq('002', cm.getValue()); + helpers.doKeys(''); + eq('001', cm.getValue()); + helpers.doKeys(''); + eq('000', cm.getValue()); + cm.setCursor(0, 0); + helpers.doKeys(''); + eq('001', cm.getValue()); + helpers.doKeys(''); + eq('002', cm.getValue()); + helpers.doKeys(''); + eq('001', cm.getValue()); + helpers.doKeys(''); + eq('000', cm.getValue()); +}, { value: '000' }); + +testVim('increment_decimal', function(cm, vim, helpers) { + cm.setCursor(0, 2); + helpers.doKeys(''); + eq('101', cm.getValue()); + helpers.doKeys(''); + eq('102', cm.getValue()); + helpers.doKeys(''); + eq('103', cm.getValue()); + helpers.doKeys(''); + eq('104', cm.getValue()); + helpers.doKeys(''); + eq('105', cm.getValue()); + helpers.doKeys(''); + eq('106', cm.getValue()); + helpers.doKeys(''); + eq('107', cm.getValue()); + helpers.doKeys(''); + eq('108', cm.getValue()); + helpers.doKeys(''); + eq('109', cm.getValue()); + helpers.doKeys(''); + eq('110', cm.getValue()); + helpers.doKeys(''); + eq('109', cm.getValue()); + helpers.doKeys(''); + eq('108', cm.getValue()); + helpers.doKeys(''); + eq('107', cm.getValue()); + helpers.doKeys(''); + eq('106', cm.getValue()); + helpers.doKeys(''); + eq('105', cm.getValue()); + helpers.doKeys(''); + eq('104', cm.getValue()); + helpers.doKeys(''); + eq('103', cm.getValue()); + helpers.doKeys(''); + eq('102', cm.getValue()); + helpers.doKeys(''); + eq('101', cm.getValue()); + helpers.doKeys(''); + eq('100', cm.getValue()); + cm.setCursor(0, 0); + helpers.doKeys(''); + eq('101', cm.getValue()); + helpers.doKeys(''); + eq('102', cm.getValue()); + helpers.doKeys(''); + eq('101', cm.getValue()); + helpers.doKeys(''); + eq('100', cm.getValue()); +}, { value: '100' }); + +testVim('increment_decimal_single_zero', function(cm, vim, helpers) { + helpers.doKeys(''); + eq('1', cm.getValue()); + helpers.doKeys(''); + eq('2', cm.getValue()); + helpers.doKeys(''); + eq('3', cm.getValue()); + helpers.doKeys(''); + eq('4', cm.getValue()); + helpers.doKeys(''); + eq('5', cm.getValue()); + helpers.doKeys(''); + eq('6', cm.getValue()); + helpers.doKeys(''); + eq('7', cm.getValue()); + helpers.doKeys(''); + eq('8', cm.getValue()); + helpers.doKeys(''); + eq('9', cm.getValue()); + helpers.doKeys(''); + eq('10', cm.getValue()); + helpers.doKeys(''); + eq('9', cm.getValue()); + helpers.doKeys(''); + eq('8', cm.getValue()); + helpers.doKeys(''); + eq('7', cm.getValue()); + helpers.doKeys(''); + eq('6', cm.getValue()); + helpers.doKeys(''); + eq('5', cm.getValue()); + helpers.doKeys(''); + eq('4', cm.getValue()); + helpers.doKeys(''); + eq('3', cm.getValue()); + helpers.doKeys(''); + eq('2', cm.getValue()); + helpers.doKeys(''); + eq('1', cm.getValue()); + helpers.doKeys(''); + eq('0', cm.getValue()); + cm.setCursor(0, 0); + helpers.doKeys(''); + eq('1', cm.getValue()); + helpers.doKeys(''); + eq('2', cm.getValue()); + helpers.doKeys(''); + eq('1', cm.getValue()); + helpers.doKeys(''); + eq('0', cm.getValue()); +}, { value: '0' }); +testVim('increment_hexadecimal', function(cm, vim, helpers) { + cm.setCursor(0, 2); + helpers.doKeys(''); + eq('0x1', cm.getValue()); + helpers.doKeys(''); + eq('0x2', cm.getValue()); + helpers.doKeys(''); + eq('0x3', cm.getValue()); + helpers.doKeys(''); + eq('0x4', cm.getValue()); + helpers.doKeys(''); + eq('0x5', cm.getValue()); + helpers.doKeys(''); + eq('0x6', cm.getValue()); + helpers.doKeys(''); + eq('0x7', cm.getValue()); + helpers.doKeys(''); + eq('0x8', cm.getValue()); + helpers.doKeys(''); + eq('0x9', cm.getValue()); + helpers.doKeys(''); + eq('0xa', cm.getValue()); + helpers.doKeys(''); + eq('0xb', cm.getValue()); + helpers.doKeys(''); + eq('0xc', cm.getValue()); + helpers.doKeys(''); + eq('0xd', cm.getValue()); + helpers.doKeys(''); + eq('0xe', cm.getValue()); + helpers.doKeys(''); + eq('0xf', cm.getValue()); + helpers.doKeys(''); + eq('0x10', cm.getValue()); + helpers.doKeys(''); + eq('0x0f', cm.getValue()); + helpers.doKeys(''); + eq('0x0e', cm.getValue()); + helpers.doKeys(''); + eq('0x0d', cm.getValue()); + helpers.doKeys(''); + eq('0x0c', cm.getValue()); + helpers.doKeys(''); + eq('0x0b', cm.getValue()); + helpers.doKeys(''); + eq('0x0a', cm.getValue()); + helpers.doKeys(''); + eq('0x09', cm.getValue()); + helpers.doKeys(''); + eq('0x08', cm.getValue()); + helpers.doKeys(''); + eq('0x07', cm.getValue()); + helpers.doKeys(''); + eq('0x06', cm.getValue()); + helpers.doKeys(''); + eq('0x05', cm.getValue()); + helpers.doKeys(''); + eq('0x04', cm.getValue()); + helpers.doKeys(''); + eq('0x03', cm.getValue()); + helpers.doKeys(''); + eq('0x02', cm.getValue()); + helpers.doKeys(''); + eq('0x01', cm.getValue()); + helpers.doKeys(''); + eq('0x00', cm.getValue()); + cm.setCursor(0, 0); + helpers.doKeys(''); + eq('0x01', cm.getValue()); + helpers.doKeys(''); + eq('0x02', cm.getValue()); + helpers.doKeys(''); + eq('0x01', cm.getValue()); + helpers.doKeys(''); + eq('0x00', cm.getValue()); +}, { value: '0x0' }); From a4a75b1c7c1ab4733df852eaba303cd47d46ec2e Mon Sep 17 00:00:00 2001 From: Adrian Heine Date: Wed, 6 Dec 2017 19:47:02 +0100 Subject: [PATCH 1161/1790] Escape all occurences of & and < in mode test output --- test/mode_test.js | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/test/mode_test.js b/test/mode_test.js index 9773f801..f4c4dbfe 100644 --- a/test/mode_test.js +++ b/test/mode_test.js @@ -67,7 +67,7 @@ }; function esc(str) { - return str.replace('&', '&').replace('<', '<').replace(/>/g, ">").replace(/"/g, """).replace(/'/g, "'"); + return str.replace(/&/g, '&').replace(//g, ">").replace(/"/g, """).replace(/'/g, "'"); } function compare(text, expected, mode) { From 7cefd7dacb53b71f6cfe14479a4f3fb4fa0c92b3 Mon Sep 17 00:00:00 2001 From: Adrian Heine Date: Wed, 6 Dec 2017 19:46:20 +0100 Subject: [PATCH 1162/1790] [jsx mode] Add support for JSXFragments Closes #5101. --- mode/jsx/jsx.js | 2 +- mode/jsx/test.js | 3 +++ mode/xml/xml.js | 7 +++++++ 3 files changed, 11 insertions(+), 1 deletion(-) diff --git a/mode/jsx/jsx.js b/mode/jsx/jsx.js index 45c3024a..039e37bb 100644 --- a/mode/jsx/jsx.js +++ b/mode/jsx/jsx.js @@ -26,7 +26,7 @@ } CodeMirror.defineMode("jsx", function(config, modeConfig) { - var xmlMode = CodeMirror.getMode(config, {name: "xml", allowMissing: true, multilineTagIndentPastTag: false}) + var xmlMode = CodeMirror.getMode(config, {name: "xml", allowMissing: true, multilineTagIndentPastTag: false, allowMissingTagName: true}) var jsMode = CodeMirror.getMode(config, modeConfig && modeConfig.base || "javascript") function flatXMLIndent(state) { diff --git a/mode/jsx/test.js b/mode/jsx/test.js index 61f84ebe..891f9883 100644 --- a/mode/jsx/test.js +++ b/mode/jsx/test.js @@ -11,6 +11,9 @@ MT("openclose", "([bracket&tag <][tag foo][bracket&tag >]hello [atom &][bracket&tag ][operator ++])") + MT("openclosefragment", + "([bracket&tag <><][tag foo][bracket&tag >]hello [atom &][bracket&tag ][operator ++])") + MT("attr", "([bracket&tag <][tag foo] [attribute abc]=[string 'value'][bracket&tag >]hello [atom &][bracket&tag ][operator ++])") diff --git a/mode/xml/xml.js b/mode/xml/xml.js index f987a3a3..0f1c9b17 100644 --- a/mode/xml/xml.js +++ b/mode/xml/xml.js @@ -52,6 +52,7 @@ var xmlConfig = { doNotIndent: {}, allowUnquoted: false, allowMissing: false, + allowMissingTagName: false, caseFold: false } @@ -226,6 +227,9 @@ CodeMirror.defineMode("xml", function(editorConf, config_) { state.tagName = stream.current(); setStyle = "tag"; return attrState; + } else if (config.allowMissingTagName && type == "endTag") { + setStyle = "tag bracket"; + return attrState(type, stream, state); } else { setStyle = "error"; return tagNameState; @@ -244,6 +248,9 @@ CodeMirror.defineMode("xml", function(editorConf, config_) { setStyle = "tag error"; return closeStateErr; } + } else if (config.allowMissingTagName && type == "endTag") { + setStyle = "tag bracket"; + return closeState(type, stream, state); } else { setStyle = "error"; return closeStateErr; From 32f63fc31de851699f5189d7701e0c06a2520b00 Mon Sep 17 00:00:00 2001 From: Cristian Prieto Date: Wed, 6 Dec 2017 16:14:49 +0100 Subject: [PATCH 1163/1790] [mllike mode] Improve OCaml support * Add reserved words for OCaml * Add {| |} string literal type * Add support for binary numbers * Add support for hex number literals * Add support for floats * Add octal and long integer literals --- mode/mllike/index.html | 19 ++++++++++++++++++ mode/mllike/mllike.js | 44 ++++++++++++++++++++++++++++++++++++------ 2 files changed, 57 insertions(+), 6 deletions(-) diff --git a/mode/mllike/index.html b/mode/mllike/index.html index 5923af8f..b1ed6c7d 100644 --- a/mode/mllike/index.html +++ b/mode/mllike/index.html @@ -132,6 +132,25 @@

      OCaml mode

      (* A Hundred Lines of Caml - http://caml.inria.fr/about/taste.en.html *) (* OCaml page on Wikipedia - http://en.wikipedia.org/wiki/OCaml *) + +module type S = sig type t end + +let x = {| + this is a long string + with many lines and stuff + |} + +let b = 0b00110 +let h = 0x123abcd +let e = 1e-10 +let i = 1. +let x = 30_000 +let o = 0o1234 + +[1; 2; 3] (* lists *) + +1 @ 2 +1. +. 2.

      F# mode

      diff --git a/mode/mllike/mllike.js b/mode/mllike/mllike.js index 4d0be609..90e5b41a 100644 --- a/mode/mllike/mllike.js +++ b/mode/mllike/mllike.js @@ -54,6 +54,13 @@ CodeMirror.defineMode('mllike', function(_config, parserConfig) { state.tokenize = tokenString; return state.tokenize(stream, state); } + if (ch === '{') { + if (stream.eat('|')) { + state.longString = true; + state.tokenize = tokenLongString; + return state.tokenize(stream, state); + } + } if (ch === '(') { if (stream.eat('*')) { state.commentLevel++; @@ -74,13 +81,24 @@ CodeMirror.defineMode('mllike', function(_config, parserConfig) { return 'comment'; } if (/\d/.test(ch)) { - stream.eatWhile(/[\d]/); - if (stream.eat('.')) { - stream.eatWhile(/[\d]/); + if (ch === '0' && stream.eat(/[bB]/)) { + stream.eatWhile(/[01]/); + } if (ch === '0' && stream.eat(/[xX]/)) { + stream.eatWhile(/[0-9a-fA-F]/) + } if (ch === '0' && stream.eat(/[oO]/)) { + stream.eatWhile(/[0-7]/); + } else { + stream.eatWhile(/[\d_]/); + if (stream.eat('.')) { + stream.eatWhile(/[\d]/); + } + if (stream.eat(/[eE]/)) { + stream.eatWhile(/[\d\-+]/); + } } return 'number'; } - if ( /[+\-*&%=<>!?|]/.test(ch)) { + if ( /[+\-*&%=<>!?|@]/.test(ch)) { return 'operator'; } if (/[\w\xa1-\uffff]/.test(ch)) { @@ -119,8 +137,20 @@ CodeMirror.defineMode('mllike', function(_config, parserConfig) { return 'comment'; } + function tokenLongString(stream, state) { + var prev, next; + while (state.longString && (next = stream.next()) != null) { + if (prev === '|' && next === '}') state.longString = false; + prev = next; + } + if (!state.longString) { + state.tokenize = tokenBase; + } + return 'string'; + } + return { - startState: function() {return {tokenize: tokenBase, commentLevel: 0};}, + startState: function() {return {tokenize: tokenBase, commentLevel: 0, longString: false};}, token: function(stream, state) { if (stream.eatSpace()) return null; return state.tokenize(stream, state); @@ -142,7 +172,9 @@ CodeMirror.defineMIME('text/x-ocaml', { 'print_endline': 'builtin', 'true': 'atom', 'false': 'atom', - 'raise': 'keyword' + 'raise': 'keyword', + 'module': 'keyword', + 'sig': 'keyword' } }); From ecad7206ef2458bcb22bcaf13263178e2bb3a0dc Mon Sep 17 00:00:00 2001 From: Marijn Haverbeke Date: Thu, 7 Dec 2017 12:03:15 +0100 Subject: [PATCH 1164/1790] Document lineSeparator argument do Doc constructor Issue #5112 --- doc/manual.html | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/doc/manual.html b/doc/manual.html index ea5642cd..9d95e39c 100644 --- a/doc/manual.html +++ b/doc/manual.html @@ -1523,8 +1523,8 @@

      Document management methods

      represents the editor content, plus a selection, an undo history, and a mode. A document can only be associated with a single editor at a time. You can create new - documents by calling the CodeMirror.Doc(text, mode, - firstLineNumber) constructor. The last two arguments are + documents by calling the CodeMirror.Doc(text: string, mode: Object, + firstLineNumber: ?number, lineSeparator: ?string) constructor. The last two arguments are optional and can be used to set a mode for the document and make it start at a line number other than 0, respectively.

      From c05c5956ef5ed327cfdb32d23a92c6786b6ac629 Mon Sep 17 00:00:00 2001 From: Marijn Haverbeke Date: Fri, 8 Dec 2017 09:56:44 +0100 Subject: [PATCH 1165/1790] Fix documentation of Doc constructor Issue #5112 --- doc/manual.html | 9 +++++---- 1 file changed, 5 insertions(+), 4 deletions(-) diff --git a/doc/manual.html b/doc/manual.html index 9d95e39c..5500b990 100644 --- a/doc/manual.html +++ b/doc/manual.html @@ -1523,10 +1523,11 @@

      Document management methods

      represents the editor content, plus a selection, an undo history, and a mode. A document can only be associated with a single editor at a time. You can create new - documents by calling the CodeMirror.Doc(text: string, mode: Object, - firstLineNumber: ?number, lineSeparator: ?string) constructor. The last two arguments are - optional and can be used to set a mode for the document and make - it start at a line number other than 0, respectively.

      + documents by calling the CodeMirror.Doc(text: string, mode: + Object, firstLineNumber: ?number, lineSeparator: ?string) + constructor. The last three arguments are optional and can be used + to set a mode for the document, make it start at a line number + other than 0, and set a specific line separator respectively.

      cm.getDoc() → Doc
      From a7e29eee89aed63727e46d1e422cfa95b1200859 Mon Sep 17 00:00:00 2001 From: Adrian Heine Date: Sun, 10 Dec 2017 20:57:37 +0100 Subject: [PATCH 1166/1790] [swift mode] Correctly highlight nested comments --- mode/swift/swift.js | 13 +++++++++++-- mode/swift/test.js | 7 +++++++ 2 files changed, 18 insertions(+), 2 deletions(-) diff --git a/mode/swift/swift.js b/mode/swift/swift.js index 43ab7c8f..1795d86e 100644 --- a/mode/swift/swift.js +++ b/mode/swift/swift.js @@ -138,8 +138,17 @@ } function tokenComment(stream, state) { - stream.match(/^(?:[^*]|\*(?!\/))*/) - if (stream.match("*/")) state.tokenize.pop() + var ch + while (true) { + stream.match(/^[^/*]+/, true) + ch = stream.next() + if (!ch) break + if (ch === "/" && stream.eat("*")) { + state.tokenize.push(tokenComment) + } else if (ch === "*" && stream.eat("/")) { + state.tokenize.pop() + } + } return "comment" } diff --git a/mode/swift/test.js b/mode/swift/test.js index 786b89e2..4091ac6f 100644 --- a/mode/swift/test.js +++ b/mode/swift/test.js @@ -142,6 +142,13 @@ "[variable print][punctuation (][variable foo][property ._123][punctuation )]", "[variable print][punctuation (]") + MT("nested_comments", + "[comment /*]", + "[comment But wait /* this is a nested comment */ for real]", + "[comment /**** let * me * show * you ****/]", + "[comment ///// let / me / show / you /////]", + "[comment */]"); + // TODO: correctly identify when multiple variables are being declared // by use of a comma-separated list. // TODO: correctly identify when variables are being declared in a tuple. From ff21fec1a71e9c5de3c9faec84c3c61eaef60104 Mon Sep 17 00:00:00 2001 From: Adrian Heine Date: Sun, 10 Dec 2017 21:08:36 +0100 Subject: [PATCH 1167/1790] [scala mode] Correctly highlight nested comments The implementation is mostly copied from dart as implemented in d4dbbcef22e3aadf25dad811a9faa988a50a0df4. --- mode/clike/clike.js | 27 +++++++++++++++++++++++++++ mode/clike/test.js | 10 ++++++++++ 2 files changed, 37 insertions(+) diff --git a/mode/clike/clike.js b/mode/clike/clike.js index ff00cf50..77064290 100644 --- a/mode/clike/clike.js +++ b/mode/clike/clike.js @@ -489,6 +489,27 @@ CodeMirror.defineMode("clike", function(config, parserConfig) { return "string"; } + function tokenNestedComment(depth) { + return function (stream, state) { + var ch + while (ch = stream.next()) { + if (ch == "*" && stream.eat("/")) { + if (depth == 1) { + state.tokenize = null + break + } else { + state.tokenize = tokenNestedComment(depth - 1) + return state.tokenize(stream, state) + } + } else if (ch == "/" && stream.eat("*")) { + state.tokenize = tokenNestedComment(depth + 1) + return state.tokenize(stream, state) + } + } + return "comment" + } + } + def("text/x-scala", { name: "clike", keywords: words( @@ -544,6 +565,12 @@ CodeMirror.defineMode("clike", function(config, parserConfig) { } else { return false } + }, + + "/": function(stream, state) { + if (!stream.eat("*")) return false + state.tokenize = tokenNestedComment(1) + return state.tokenize(stream, state) } }, modeProps: {closeBrackets: {triples: '"'}} diff --git a/mode/clike/test.js b/mode/clike/test.js index dad2e246..e3bde772 100644 --- a/mode/clike/test.js +++ b/mode/clike/test.js @@ -56,4 +56,14 @@ MTCPP("ctor_dtor", "[def Foo::Foo]() {}", "[def Foo::~Foo]() {}"); + + var mode_scala = CodeMirror.getMode({indentUnit: 2}, "text/x-scala"); + function MTSCALA(name) { test.mode("scala_" + name, mode_scala, Array.prototype.slice.call(arguments, 1)); } + MTSCALA("nested_comments", + "[comment /*]", + "[comment But wait /* this is a nested comment */ for real]", + "[comment /**** let * me * show * you ****/]", + "[comment ///// let / me / show / you /////]", + "[comment */]"); + })(); From edc74c3ef360132afc69b2f22d51f7c6a711c305 Mon Sep 17 00:00:00 2001 From: Lior Goldberg Date: Thu, 7 Dec 2017 14:54:27 +0200 Subject: [PATCH 1168/1790] [sublime keymap] Support expanding brackets selection in selectBetweenBrackets --- keymap/sublime.js | 12 +++++++++--- test/sublime_test.js | 4 +++- 2 files changed, 12 insertions(+), 4 deletions(-) diff --git a/keymap/sublime.js b/keymap/sublime.js index 08c9ebfb..37ae6fec 100644 --- a/keymap/sublime.js +++ b/keymap/sublime.js @@ -183,9 +183,15 @@ var closing = cm.scanForBracket(pos, 1); if (!closing) return false; if (closing.ch == mirror.charAt(mirror.indexOf(opening.ch) + 1)) { - newRanges.push({anchor: Pos(opening.pos.line, opening.pos.ch + 1), - head: closing.pos}); - break; + var startPos = Pos(opening.pos.line, opening.pos.ch + 1); + if (CodeMirror.cmpPos(startPos, range.from()) == 0 && + CodeMirror.cmpPos(closing.pos, range.to()) == 0) { + opening = cm.scanForBracket(opening.pos, -1); + if (!opening) return false; + } else { + newRanges.push({anchor: startPos, head: closing.pos}); + break; + } } pos = Pos(closing.pos.line, closing.pos.ch + 1); } diff --git a/test/sublime_test.js b/test/sublime_test.js index e9cd342f..27132d16 100644 --- a/test/sublime_test.js +++ b/test/sublime_test.js @@ -152,7 +152,9 @@ Pos(0, 8), "selectScope", hasSel(0, 8, 2, 0), Pos(1, 2), "selectScope", hasSel(0, 8, 2, 0), Pos(1, 6), "selectScope", hasSel(1, 6, 1, 10), - Pos(1, 9), "selectScope", hasSel(1, 6, 1, 10)); + Pos(1, 9), "selectScope", hasSel(1, 6, 1, 10), + "selectScope", hasSel(0, 8, 2, 0), + "selectScope", hasSel(0, 0, 2, 1)); stTest("goToBracket", "foo(a) {\n bar[1, 2];\n}", Pos(0, 0), "goToBracket", at(0, 0), From b3cdfee46a320e646a298141861b4e80dc6f1bd0 Mon Sep 17 00:00:00 2001 From: Marijn Haverbeke Date: Tue, 12 Dec 2017 22:16:41 +0100 Subject: [PATCH 1169/1790] Drop bin/compress Leave compression up to people's own custom build setups Closes #5127 --- bin/compress | 92 ---------------------------------------------------- 1 file changed, 92 deletions(-) delete mode 100755 bin/compress diff --git a/bin/compress b/bin/compress deleted file mode 100755 index d358f9c3..00000000 --- a/bin/compress +++ /dev/null @@ -1,92 +0,0 @@ -#!/usr/bin/env node - -// Compression helper for CodeMirror -// -// Example: -// -// bin/compress codemirror runmode javascript xml -// -// Will take lib/codemirror.js, addon/runmode/runmode.js, -// mode/javascript/javascript.js, and mode/xml/xml.js, run them though -// the online minifier at http://marijnhaverbeke.nl/uglifyjs, and spit -// out the result. -// -// bin/compress codemirror --local /path/to/bin/UglifyJS -// -// Will use a local minifier instead of the online default one. -// -// Script files are specified without .js ending. Prefixing them with -// their full (local) path is optional. So you may say lib/codemirror -// or mode/xml/xml to be more precise. In fact, even the .js suffix -// may be specified, if wanted. - -"use strict"; - -var fs = require("fs"); - -function help(ok) { - console.log("usage: " + process.argv[1] + " [--local /path/to/uglifyjs] files..."); - process.exit(ok ? 0 : 1); -} - -var local = null, args = [], extraArgs = null, files = [], blob = ""; - -for (var i = 2; i < process.argv.length; ++i) { - var arg = process.argv[i]; - if (arg == "--local" && i + 1 < process.argv.length) { - var parts = process.argv[++i].split(/\s+/); - local = parts[0]; - extraArgs = parts.slice(1); - if (!extraArgs.length) extraArgs = ["-c", "-m"]; - } else if (arg == "--help") { - help(true); - } else if (arg[0] != "-") { - files.push({name: arg, re: new RegExp("(?:\\/|^)" + arg + (/\.js$/.test(arg) ? "$" : "\\.js$"))}); - } else help(false); -} - -function walk(dir) { - fs.readdirSync(dir).forEach(function(fname) { - if (/^[_\.]/.test(fname)) return; - var file = dir + fname; - if (fs.statSync(file).isDirectory()) return walk(file + "/"); - if (files.some(function(spec, i) { - var match = spec.re.test(file); - if (match) files.splice(i, 1); - return match; - })) { - if (local) args.push(file); - else blob += fs.readFileSync(file, "utf8"); - } - }); -} - -walk("lib/"); -walk("addon/"); -walk("mode/"); - -if (!local && !blob) help(false); - -if (files.length) { - console.log("Some specified files were not found: " + - files.map(function(a){return a.name;}).join(", ")); - process.exit(1); -} - -if (local) { - require("child_process").spawn(local, args.concat(extraArgs), {stdio: ["ignore", process.stdout, process.stderr]}); -} else { - var data = new Buffer("js_code=" + require("querystring").escape(blob), "utf8"); - var req = require("http").request({ - host: "marijnhaverbeke.nl", - port: 80, - method: "POST", - path: "/uglifyjs", - headers: {"content-type": "application/x-www-form-urlencoded", - "content-length": data.length} - }); - req.on("response", function(resp) { - resp.on("data", function (chunk) { process.stdout.write(chunk); }); - }); - req.end(data); -} From d9f05c90a1f6e07faf4ec3cf239621f1cce44f32 Mon Sep 17 00:00:00 2001 From: Sorab Bisht Date: Mon, 11 Dec 2017 19:44:15 +0530 Subject: [PATCH 1170/1790] [closetag addon] Add an option to disable auto indenting --- addon/edit/closetag.js | 16 +++++++++++----- src/edit/options.js | 1 + 2 files changed, 12 insertions(+), 5 deletions(-) diff --git a/addon/edit/closetag.js b/addon/edit/closetag.js index a518da3e..83f133a5 100644 --- a/addon/edit/closetag.js +++ b/addon/edit/closetag.js @@ -53,13 +53,14 @@ function autoCloseGT(cm) { if (cm.getOption("disableInput")) return CodeMirror.Pass; var ranges = cm.listSelections(), replacements = []; + var opt = cm.getOption("autoCloseTags"); for (var i = 0; i < ranges.length; i++) { if (!ranges[i].empty()) return CodeMirror.Pass; 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 html = inner.mode.configuration == "html"; var dontCloseTags = (typeof opt == "object" && opt.dontCloseTags) || (html && htmlDontClose); var indentTags = (typeof opt == "object" && opt.indentTags) || (html && htmlIndent); @@ -81,13 +82,14 @@ newPos: indent ? CodeMirror.Pos(pos.line + 1, 0) : CodeMirror.Pos(pos.line, pos.ch + 1)}; } + var dontIndentOnAutoClose = (typeof opt == "object" && opt.dontIndentOnAutoClose); for (var i = ranges.length - 1; i >= 0; i--) { var info = replacements[i]; cm.replaceRange(info.text, ranges[i].head, ranges[i].anchor, "+insert"); var sel = cm.listSelections().slice(0); sel[i] = {head: info.newPos, anchor: info.newPos}; cm.setSelections(sel); - if (info.indent) { + if (!dontIndentOnAutoClose && info.indent) { cm.indentLine(info.newPos.line, null, true); cm.indentLine(info.newPos.line + 1, null, true); } @@ -97,6 +99,8 @@ function autoCloseCurrent(cm, typingSlash) { var ranges = cm.listSelections(), replacements = []; var head = typingSlash ? "/" : " { cm.doc.lineSep = val if (!val) return From 56c271ff88efbf3726a4f0fa0a131942ebf804ff Mon Sep 17 00:00:00 2001 From: Marijn Haverbeke Date: Sat, 16 Dec 2017 18:47:06 +0100 Subject: [PATCH 1171/1790] [javascript mode] Stop treating TS contextual keywords as regular keywords Closes #5133 --- mode/javascript/javascript.js | 68 +++++++++++++---------------------- 1 file changed, 25 insertions(+), 43 deletions(-) diff --git a/mode/javascript/javascript.js b/mode/javascript/javascript.js index 514de1c8..edab99f3 100644 --- a/mode/javascript/javascript.js +++ b/mode/javascript/javascript.js @@ -26,7 +26,7 @@ CodeMirror.defineMode("javascript", function(config, parserConfig) { var A = kw("keyword a"), B = kw("keyword b"), C = kw("keyword c"), D = kw("keyword d"); var operator = kw("operator"), atom = {type: "atom", style: "atom"}; - var jsKeywords = { + return { "if": kw("if"), "while": A, "with": A, "else": B, "do": B, "try": B, "finally": B, "return": D, "break": D, "continue": D, "new": kw("new"), "delete": C, "void": C, "throw": C, "debugger": kw("debugger"), "var": kw("var"), "const": kw("var"), "let": kw("var"), @@ -38,33 +38,6 @@ CodeMirror.defineMode("javascript", function(config, parserConfig) { "yield": C, "export": kw("export"), "import": kw("import"), "extends": C, "await": C }; - - // Extend the 'normal' keywords with the TypeScript language extensions - if (isTS) { - var type = {type: "variable", style: "type"}; - var tsKeywords = { - // object-like things - "interface": kw("class"), - "implements": C, - "namespace": C, - - // scope modifiers - "public": kw("modifier"), - "private": kw("modifier"), - "protected": kw("modifier"), - "abstract": kw("modifier"), - "readonly": kw("modifier"), - - // types - "string": type, "number": type, "boolean": type, "any": type - }; - - for (var attr in tsKeywords) { - jsKeywords[attr] = tsKeywords[attr]; - } - } - - return jsKeywords; }(); var isOperatorChar = /[+\-*&%=<>!?|~^@]/; @@ -310,6 +283,10 @@ CodeMirror.defineMode("javascript", function(config, parserConfig) { } } + function isModifier(name) { + return name == "public" || name == "private" || name == "protected" || name == "abstract" || name == "readonly" + } + // Combinators var defaultVars = {name: "this", next: {name: "arguments"}}; @@ -366,6 +343,7 @@ CodeMirror.defineMode("javascript", function(config, parserConfig) { } if (type == "function") return cont(functiondef); if (type == "for") return cont(pushlex("form"), forspec, statement, poplex); + if (type == "class" || (isTS && value == "interface")) { cx.marked = "keyword"; return cont(pushlex("form"), className, poplex); } if (type == "variable") { if (isTS && value == "type") { cx.marked = "keyword" @@ -376,6 +354,9 @@ CodeMirror.defineMode("javascript", function(config, parserConfig) { } else if (isTS && (value == "module" || value == "enum") && cx.stream.match(/^\s*\w/, false)) { cx.marked = "keyword" return cont(pushlex("form"), pattern, expect("{"), pushlex("}"), block, poplex, poplex) + } else if (isTS && value == "namespace") { + cx.marked = "keyword" + return cont(pushlex("form"), expression, block, poplex) } else { return cont(pushlex("stat"), maybelabel); } @@ -386,24 +367,23 @@ CodeMirror.defineMode("javascript", function(config, parserConfig) { if (type == "default") return cont(expect(":")); if (type == "catch") return cont(pushlex("form"), pushcontext, expect("("), funarg, expect(")"), statement, poplex, popcontext); - if (type == "class") return cont(pushlex("form"), className, poplex); if (type == "export") return cont(pushlex("stat"), afterExport, poplex); if (type == "import") return cont(pushlex("stat"), afterImport, poplex); if (type == "async") return cont(statement) if (value == "@") return cont(expression, statement) return pass(pushlex("stat"), expression, expect(";"), poplex); } - function expression(type) { - return expressionInner(type, false); + function expression(type, value) { + return expressionInner(type, value, false); } - function expressionNoComma(type) { - return expressionInner(type, true); + function expressionNoComma(type, value) { + return expressionInner(type, value, true); } function parenExpr(type) { if (type != "(") return pass() return cont(pushlex(")"), expression, expect(")"), poplex) } - function expressionInner(type, noComma) { + function expressionInner(type, value, noComma) { if (cx.state.fatArrowAt == cx.stream.start) { var body = noComma ? arrowBodyNoComma : arrowBody; if (type == "(") return cont(pushcontext, pushlex(")"), commasep(funarg, ")"), poplex, expect("=>"), body, popcontext); @@ -413,7 +393,7 @@ CodeMirror.defineMode("javascript", function(config, parserConfig) { var maybeop = noComma ? maybeoperatorNoComma : maybeoperatorComma; if (atomicTypes.hasOwnProperty(type)) return cont(maybeop); if (type == "function") return cont(functiondef, maybeop); - if (type == "class") return cont(pushlex("form"), classExpression, poplex); + if (type == "class" || (isTS && value == "interface")) { cx.marked = "keyword"; return cont(pushlex("form"), classExpression, poplex); } if (type == "keyword c" || type == "async") return cont(noComma ? expressionNoComma : expression); if (type == "(") return cont(pushlex(")"), maybeexpression, expect(")"), poplex, maybeop); if (type == "operator" || type == "spread") return cont(noComma ? expressionNoComma : expression); @@ -511,10 +491,11 @@ CodeMirror.defineMode("javascript", function(config, parserConfig) { return cont(afterprop); } else if (type == "jsonld-keyword") { return cont(afterprop); - } else if (type == "modifier") { + } else if (isTS && isModifier(value)) { + cx.marked = "keyword" return cont(objprop) } else if (type == "[") { - return cont(expression, expect("]"), afterprop); + return cont(expression, maybetype, expect("]"), afterprop); } else if (type == "spread") { return cont(expressionNoComma, afterprop); } else if (value == "*") { @@ -616,7 +597,7 @@ CodeMirror.defineMode("javascript", function(config, parserConfig) { if (value == "<") return cont(pushlex(">"), commasep(typeexpr, ">"), poplex, afterType) if (value == "|" || type == ".") return cont(typeexpr) if (type == "[") return cont(expect("]"), afterType) - if (value == "extends") return cont(typeexpr) + if (value == "extends" || value == "implements") { cx.marked = "keyword"; return cont(typeexpr) } } function maybeTypeArgs(_, value) { if (value == "<") return cont(pushlex(">"), commasep(typeexpr, ">"), poplex, afterType) @@ -631,7 +612,7 @@ CodeMirror.defineMode("javascript", function(config, parserConfig) { return pass(pattern, maybetype, maybeAssign, vardefCont); } function pattern(type, value) { - if (type == "modifier") return cont(pattern) + if (isTS && isModifier(value)) { cx.marked = "keyword"; return cont(pattern) } if (type == "variable") { register(value); return cont(); } if (type == "spread") return cont(pattern); if (type == "[") return contCommasep(pattern, "]"); @@ -685,7 +666,8 @@ CodeMirror.defineMode("javascript", function(config, parserConfig) { } function funarg(type, value) { if (value == "@") cont(expression, funarg) - if (type == "spread" || type == "modifier") return cont(funarg); + if (type == "spread") return cont(funarg); + if (isTS && isModifier(value)) { cx.marked = "keyword"; return cont(funarg); } return pass(pattern, maybetype, maybeAssign); } function classExpression(type, value) { @@ -703,9 +685,9 @@ CodeMirror.defineMode("javascript", function(config, parserConfig) { if (type == "{") return cont(pushlex("}"), classBody, poplex); } function classBody(type, value) { - if (type == "modifier" || type == "async" || + if (type == "async" || (type == "variable" && - (value == "static" || value == "get" || value == "set") && + (value == "static" || value == "get" || value == "set" || (isTS && isModifier(value))) && cx.stream.match(/^\s+[\w$\xa1-\uffff]/, false))) { cx.marked = "keyword"; return cont(classBody); @@ -715,7 +697,7 @@ CodeMirror.defineMode("javascript", function(config, parserConfig) { return cont(isTS ? classfield : functiondef, classBody); } if (type == "[") - return cont(expression, expect("]"), isTS ? classfield : functiondef, classBody) + return cont(expression, maybetype, expect("]"), isTS ? classfield : functiondef, classBody) if (value == "*") { cx.marked = "keyword"; return cont(classBody); From 9e3665211ac2427b55acf8006bd7f783a640f16b Mon Sep 17 00:00:00 2001 From: Marijn Haverbeke Date: Wed, 20 Dec 2017 10:22:33 +0100 Subject: [PATCH 1172/1790] Use a different way to force line widget margins to stay inside container Issue #5137 --- lib/codemirror.css | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/lib/codemirror.css b/lib/codemirror.css index 8f4f22f5..c7a8ae70 100644 --- a/lib/codemirror.css +++ b/lib/codemirror.css @@ -270,7 +270,7 @@ div.CodeMirror span.CodeMirror-nonmatchingbracket {color: #a22;} .CodeMirror-linewidget { position: relative; z-index: 2; - overflow: auto; + padding: 0.1px; /* Force widget margins to stay inside of the container */ } .CodeMirror-widget {} From 40570ddc5ba1410c7bf2c38f1b1e8ac6a502fa2f Mon Sep 17 00:00:00 2001 From: Tobias Bertelsen Date: Wed, 6 Dec 2017 22:52:31 +0100 Subject: [PATCH 1173/1790] [sublime keymap] Fixing hotkeys for addCursorTo(Prev|Next)Line Fix for issue #5109 --- keymap/sublime.js | 33 ++++----------------------------- test/sublime_test.js | 25 ------------------------- 2 files changed, 4 insertions(+), 54 deletions(-) diff --git a/keymap/sublime.js b/keymap/sublime.js index 37ae6fec..5925b7c5 100644 --- a/keymap/sublime.js +++ b/keymap/sublime.js @@ -514,27 +514,6 @@ cm.scrollTo(null, (pos.top + pos.bottom) / 2 - cm.getScrollInfo().clientHeight / 2); }; - cmds.selectLinesUpward = function(cm) { - cm.operation(function() { - var ranges = cm.listSelections(); - for (var i = 0; i < ranges.length; i++) { - var range = ranges[i]; - if (range.head.line > cm.firstLine()) - cm.addSelection(Pos(range.head.line - 1, range.head.ch)); - } - }); - }; - cmds.selectLinesDownward = function(cm) { - cm.operation(function() { - var ranges = cm.listSelections(); - for (var i = 0; i < ranges.length; i++) { - var range = ranges[i]; - if (range.head.line < cm.lastLine()) - cm.addSelection(Pos(range.head.line + 1, range.head.ch)); - } - }); - }; - function getTarget(cm) { var from = cm.getCursor("from"), to = cm.getCursor("to"); if (CodeMirror.cmpPos(from, to) == 0) { @@ -596,8 +575,6 @@ "Cmd-Enter": "insertLineAfter", "Shift-Cmd-Enter": "insertLineBefore", "Cmd-D": "selectNextOccurrence", - "Shift-Cmd-Up": "addCursorToPrevLine", - "Shift-Cmd-Down": "addCursorToNextLine", "Shift-Cmd-Space": "selectScope", "Shift-Cmd-M": "selectBetweenBrackets", "Cmd-M": "goToBracket", @@ -627,8 +604,8 @@ "Cmd-K Cmd-Backspace": "delLineLeft", "Cmd-K Cmd-0": "unfoldAll", "Cmd-K Cmd-J": "unfoldAll", - "Ctrl-Shift-Up": "selectLinesUpward", - "Ctrl-Shift-Down": "selectLinesDownward", + "Ctrl-Shift-Up": "addCursorToPrevLine", + "Ctrl-Shift-Down": "addCursorToNextLine", "Cmd-F3": "findUnder", "Shift-Cmd-F3": "findUnderPrevious", "Alt-F3": "findAllUnder", @@ -658,8 +635,6 @@ "Ctrl-Enter": "insertLineAfter", "Shift-Ctrl-Enter": "insertLineBefore", "Ctrl-D": "selectNextOccurrence", - "Alt-CtrlUp": "addCursorToPrevLine", - "Alt-CtrlDown": "addCursorToNextLine", "Shift-Ctrl-Space": "selectScope", "Shift-Ctrl-M": "selectBetweenBrackets", "Ctrl-M": "goToBracket", @@ -689,8 +664,8 @@ "Ctrl-K Ctrl-Backspace": "delLineLeft", "Ctrl-K Ctrl-0": "unfoldAll", "Ctrl-K Ctrl-J": "unfoldAll", - "Ctrl-Alt-Up": "selectLinesUpward", - "Ctrl-Alt-Down": "selectLinesDownward", + "Ctrl-Alt-Up": "addCursorToPrevLine", + "Ctrl-Alt-Down": "addCursorToNextLine", "Ctrl-F3": "findUnder", "Shift-Ctrl-F3": "findUnderPrevious", "Alt-F3": "findAllUnder", diff --git a/test/sublime_test.js b/test/sublime_test.js index 27132d16..09bb9512 100644 --- a/test/sublime_test.js +++ b/test/sublime_test.js @@ -221,31 +221,6 @@ 2, 4, 2, 6, 2, 7, 2, 7)); - stTest("selectLinesUpward", "123\n345\n789\n012", - setSel(0, 1, 0, 1, - 1, 1, 1, 3, - 2, 0, 2, 0, - 3, 0, 3, 0), - "selectLinesUpward", - hasSel(0, 1, 0, 1, - 0, 3, 0, 3, - 1, 0, 1, 0, - 1, 1, 1, 3, - 2, 0, 2, 0, - 3, 0, 3, 0)); - - stTest("selectLinesDownward", "123\n345\n789\n012", - setSel(0, 1, 0, 1, - 1, 1, 1, 3, - 2, 0, 2, 0, - 3, 0, 3, 0), - "selectLinesDownward", - hasSel(0, 1, 0, 1, - 1, 1, 1, 3, - 2, 0, 2, 0, - 2, 3, 2, 3, - 3, 0, 3, 0)); - stTest("sortLines", "c\nb\na\nC\nB\nA", "sortLines", val("A\nB\nC\na\nb\nc"), "undo", From 2f4fb8053021c01d1f246de2e663ac3719877610 Mon Sep 17 00:00:00 2001 From: Marijn Haverbeke Date: Thu, 21 Dec 2017 14:59:33 +0100 Subject: [PATCH 1174/1790] Mark version 5.33.0 --- AUTHORS | 6 ++++++ CHANGELOG.md | 22 ++++++++++++++++++++++ doc/manual.html | 2 +- doc/releases.html | 13 +++++++++++++ index.html | 2 +- package.json | 2 +- src/edit/main.js | 2 +- 7 files changed, 45 insertions(+), 4 deletions(-) diff --git a/AUTHORS b/AUTHORS index 65d84808..47c79d15 100644 --- a/AUTHORS +++ b/AUTHORS @@ -144,6 +144,7 @@ CodeBitt coderaiser Cole R Lawrence ComFreek +Cristian Prieto Curtis Gagliardi dagsta daines @@ -385,6 +386,7 @@ Leon Sorokin Leonya Khachaturov Liam Newman Libo Cannici +Lior Goldberg LloydMilligan LM lochel @@ -611,6 +613,7 @@ sinkuu snasa soliton4 sonson +Sorab Bisht spastorelli srajanpaliwal Stanislav Oaserele @@ -619,6 +622,7 @@ Stefan Borsje Steffen Beyer Steffen Bruchmann Steffen Kowalski +Stephane Moore Stephen Lavelle Steve Champagne Steve Hoover @@ -651,6 +655,7 @@ Tim Baumann Timothy Farrell Timothy Gu Timothy Hatcher +Tobias Bertelsen TobiasBg Todd Berman Todd Kennedy @@ -660,6 +665,7 @@ Tom Erik Støwer Tom Klancer Tom MacWright Tony Jian +tophf Travis Heppe Triangle717 Tristan Tarrant diff --git a/CHANGELOG.md b/CHANGELOG.md index f81fcdd0..855e6e4d 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -1,3 +1,25 @@ +## 5.33.0 (2017-12-21) + +### Bug fixes + +[lint addon](http://codemirror.net/doc/manual.html#addon_lint): Make updates more efficient. + +[css mode](http://codemirror.net/mode/css/): The mode is now properly case-insensitive. + +[continuelist addon](http://codemirror.net/doc/manual.html#addon_continuelist): Fix broken handling of unordered lists introduced in previous release. + +[swift](http://codemirror.net/mode/swift) and [scala](http://codemirror.net/mode/clike/) modes: Support nested block comments. + +[mllike mode](http://codemirror.net/mode/mllike/index.html): Improve OCaml support. + +[sublime bindings](http://codemirror.net/demo/sublime.html): Use the proper key bindings for `addCursorToNextLine` and `addCursorToPrevLine`. + +### New features + +[jsx mode](http://codemirror.net/mode/jsx/index.html): Support JSX fragments. + +[closetag addon](http://codemirror.net/demo/closetag.html): Add an option to disable auto-indenting. + ## 5.32.0 (2017-11-22) ### Bug fixes diff --git a/doc/manual.html b/doc/manual.html index 5500b990..37275fd9 100644 --- a/doc/manual.html +++ b/doc/manual.html @@ -69,7 +69,7 @@

      User manual and reference guide - version 5.32.1 + version 5.33.0

      CodeMirror is a code-editor component that can be embedded in diff --git a/doc/releases.html b/doc/releases.html index 7fb8eebe..4051a32c 100644 --- a/doc/releases.html +++ b/doc/releases.html @@ -30,6 +30,19 @@

      Release notes and version history

      Version 5.x

      +

      21-12-2017: Version 5.33.0:

      + +
        +
      • lint addon: Make updates more efficient.
      • +
      • css mode: The mode is now properly case-insensitive.
      • +
      • continuelist addon: Fix broken handling of unordered lists introduced in previous release.
      • +
      • swift and scala modes: Support nested block comments.
      • +
      • mllike mode: Improve OCaml support.
      • +
      • sublime bindings: Use the proper key bindings for addCursorToNextLine and addCursorToPrevLine.
      • +
      • jsx mode: Support JSX fragments.
      • +
      • closetag addon: Add an option to disable auto-indenting.
      • +
      +

      22-11-2017: Version 5.32.0:

        diff --git a/index.html b/index.html index d62ab84a..1e4df932 100644 --- a/index.html +++ b/index.html @@ -96,7 +96,7 @@

        This is CodeMirror

      - Get the current version: 5.32.0.
      + Get the current version: 5.33.0.
      You can see the code,
      read the release notes,
      or study the user manual. diff --git a/package.json b/package.json index 3ffad1d5..9894b4f0 100644 --- a/package.json +++ b/package.json @@ -1,6 +1,6 @@ { "name": "codemirror", - "version": "5.32.1", + "version": "5.33.0", "main": "lib/codemirror.js", "style": "lib/codemirror.css", "description": "Full-featured in-browser code editor", diff --git a/src/edit/main.js b/src/edit/main.js index 260f7d0a..a49a7c2f 100644 --- a/src/edit/main.js +++ b/src/edit/main.js @@ -66,4 +66,4 @@ import { addLegacyProps } from "./legacy.js" addLegacyProps(CodeMirror) -CodeMirror.version = "5.32.1" +CodeMirror.version = "5.33.0" From c727c997264451a11573ec121889bc76b071bfe2 Mon Sep 17 00:00:00 2001 From: Marijn Haverbeke Date: Thu, 21 Dec 2017 15:01:04 +0100 Subject: [PATCH 1175/1790] Bump version number post-5.33.0 --- doc/manual.html | 2 +- package.json | 2 +- src/edit/main.js | 2 +- 3 files changed, 3 insertions(+), 3 deletions(-) diff --git a/doc/manual.html b/doc/manual.html index 37275fd9..01721009 100644 --- a/doc/manual.html +++ b/doc/manual.html @@ -69,7 +69,7 @@

      User manual and reference guide - version 5.33.0 + version 5.33.1

      CodeMirror is a code-editor component that can be embedded in diff --git a/package.json b/package.json index 9894b4f0..28a29258 100644 --- a/package.json +++ b/package.json @@ -1,6 +1,6 @@ { "name": "codemirror", - "version": "5.33.0", + "version": "5.33.1", "main": "lib/codemirror.js", "style": "lib/codemirror.css", "description": "Full-featured in-browser code editor", diff --git a/src/edit/main.js b/src/edit/main.js index a49a7c2f..a12e4e3b 100644 --- a/src/edit/main.js +++ b/src/edit/main.js @@ -66,4 +66,4 @@ import { addLegacyProps } from "./legacy.js" addLegacyProps(CodeMirror) -CodeMirror.version = "5.33.0" +CodeMirror.version = "5.33.1" From b36cf986c7288e6019d11338867a5730fee70b95 Mon Sep 17 00:00:00 2001 From: tophf Date: Fri, 22 Dec 2017 10:32:36 +0300 Subject: [PATCH 1176/1790] [stylus mode] parse CSS4 hex colors - #RGBA and #RRGGBBAA --- mode/stylus/stylus.js | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/mode/stylus/stylus.js b/mode/stylus/stylus.js index b83be16f..a9f50c05 100644 --- a/mode/stylus/stylus.js +++ b/mode/stylus/stylus.js @@ -76,7 +76,7 @@ if (ch == "#") { stream.next(); // Hex color - if (stream.match(/^[0-9a-f]{6}|[0-9a-f]{3}/i)) { + if (stream.match(/^[0-9a-f]{3}([0-9a-f]([0-9a-f]{2}){0,2})?\b/i)) { return ["atom", "atom"]; } // ID selector From 9ed3674fbb8bb35726e73d63732b011cb4facf5f Mon Sep 17 00:00:00 2001 From: tophf Date: Sun, 24 Dec 2017 12:10:50 +0300 Subject: [PATCH 1177/1790] Recognize ScrollLock and Pause with Ctrl modifier --- src/input/keymap.js | 3 +++ src/input/keynames.js | 4 ++-- 2 files changed, 5 insertions(+), 2 deletions(-) diff --git a/src/input/keymap.js b/src/input/keymap.js index 1dfcf8af..63f18b58 100644 --- a/src/input/keymap.js +++ b/src/input/keymap.js @@ -137,6 +137,9 @@ export function keyName(event, noShift) { if (presto && event.keyCode == 34 && event["char"]) return false let name = keyNames[event.keyCode] if (name == null || event.altGraphKey) return false + // Ctrl-ScrollLock has keyCode 3, same as Ctrl-Pause, + // so we'll use event.code when available (Chrome 48+, FF 38+, Safari 10.1+) + if (event.keyCode == 3 && event.code) name = event.code return addModifierNames(name, event, noShift) } diff --git a/src/input/keynames.js b/src/input/keynames.js index 66bc8001..9a61d152 100644 --- a/src/input/keynames.js +++ b/src/input/keynames.js @@ -1,9 +1,9 @@ export let keyNames = { - 3: "Enter", 8: "Backspace", 9: "Tab", 13: "Enter", 16: "Shift", 17: "Ctrl", 18: "Alt", + 3: "Pause", 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", 36: "Home", 37: "Left", 38: "Up", 39: "Right", 40: "Down", 44: "PrintScrn", 45: "Insert", 46: "Delete", 59: ";", 61: "=", 91: "Mod", 92: "Mod", 93: "Mod", - 106: "*", 107: "=", 109: "-", 110: ".", 111: "/", 127: "Delete", + 106: "*", 107: "=", 109: "-", 110: ".", 111: "/", 127: "Delete", 145: "ScrollLock", 173: "-", 186: ";", 187: "=", 188: ",", 189: "-", 190: ".", 191: "/", 192: "`", 219: "[", 220: "\\", 221: "]", 222: "'", 63232: "Up", 63233: "Down", 63234: "Left", 63235: "Right", 63272: "Delete", 63273: "Home", 63275: "End", 63276: "PageUp", 63277: "PageDown", 63302: "Insert" From d096403f470630c9019c4bf847aa8bab57ccc8d1 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?H=C3=A9lio?= Date: Thu, 28 Dec 2017 18:18:16 -0300 Subject: [PATCH 1178/1790] [markdown mode] Support for the official mimetype MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit tl;dr: `text/markdown` since March 2016 --- In March 2016, `text/markdown` was registered as [RFC7763 at IETF](https://tools.ietf.org/html/rfc7763). Previously, it should have been `text/x-markdown`. The text below describes the situation before March 2016, when RFC7763 was still a draft. --- There is no official recommendation on [Gruber’s definition](http://daringfireball.net/projects/markdown/), but the topic was discussed quite heavily on the [official mailing-list](http://six.pairlist.net/pipermail/markdown-discuss/2007-June/thread.html#640), and reached the choice of `text/x-markdown`. This conclusion was [challenged later](http://six.pairlist.net/pipermail/markdown-discuss/2008-February/000960.html), has been confirmed and can be, IMO, considered consensus. This is the only logical conclusion in the lack of an official mime type: `text/` will provide proper default almost everywhere, `x-` because we're not using an official type, `markdown` and not `gruber.` or whatever because the type is now so common. There are still [unknowns](http://six.pairlist.net/pipermail/markdown-discuss/2007-June/000652.html) regarding the different “flavors” of Markdown, though. I guess someone should register an official type, which is supposedly [easy](http://tools.ietf.org/html/rfc4288#section-3.4), but I doubt anyone dares do it beyond John Gruber, as he very recently [proved](http://blog.codinghorror.com/standard-markdown-is-now-common-markdown/) his attachment to Markdown. There is a [draft](https://datatracker.ietf.org/doc/draft-ietf-appsawg-text-markdown/) on the IETF for `text/markdown`, but the contents do not seem to describe Markdown at all, so I wouldn't use it until it gets more complete. --- mode/markdown/markdown.js | 2 ++ 1 file changed, 2 insertions(+) diff --git a/mode/markdown/markdown.js b/mode/markdown/markdown.js index b2f79fc3..60f1b300 100644 --- a/mode/markdown/markdown.js +++ b/mode/markdown/markdown.js @@ -856,6 +856,8 @@ CodeMirror.defineMode("markdown", function(cmCfg, modeCfg) { return mode; }, "xml"); +CodeMirror.defineMIME("text/markdown", "markdown"); + CodeMirror.defineMIME("text/x-markdown", "markdown"); }); From 865102a071e79ea2301bcb710f6051f593deddc9 Mon Sep 17 00:00:00 2001 From: Marijn Haverbeke Date: Fri, 29 Dec 2017 13:18:43 +0100 Subject: [PATCH 1179/1790] [nginx mode] Fix documented mime type Issue #5148 --- mode/nginx/index.html | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/mode/nginx/index.html b/mode/nginx/index.html index 03cf6714..dde54574 100644 --- a/mode/nginx/index.html +++ b/mode/nginx/index.html @@ -1,4 +1,4 @@ - + CodeMirror: NGINX mode @@ -176,6 +176,6 @@

      NGINX mode

      var editor = CodeMirror.fromTextArea(document.getElementById("code"), {}); -

      MIME types defined: text/nginx.

      +

      MIME types defined: text/x-nginx-conf.

      From b045bfa732c2ec44a66dde944dd9be1ef5da99df Mon Sep 17 00:00:00 2001 From: 4oo4 <4oo4@users.noreply.github.com> Date: Sat, 30 Dec 2017 15:52:33 +0000 Subject: [PATCH 1180/1790] [mode/meta] Add .ino to C --- mode/meta.js | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/mode/meta.js b/mode/meta.js index 91a92526..89b2ced8 100644 --- a/mode/meta.js +++ b/mode/meta.js @@ -17,7 +17,7 @@ {name: "ASN.1", mime: "text/x-ttcn-asn", mode: "asn.1", ext: ["asn", "asn1"]}, {name: "Asterisk", mime: "text/x-asterisk", mode: "asterisk", file: /^extensions\.conf$/i}, {name: "Brainfuck", mime: "text/x-brainfuck", mode: "brainfuck", ext: ["b", "bf"]}, - {name: "C", mime: "text/x-csrc", mode: "clike", ext: ["c", "h"]}, + {name: "C", mime: "text/x-csrc", mode: "clike", ext: ["c", "h", "ino"]}, {name: "C++", mime: "text/x-c++src", mode: "clike", ext: ["cpp", "c++", "cc", "cxx", "hpp", "h++", "hh", "hxx"], alias: ["cpp"]}, {name: "Cobol", mime: "text/x-cobol", mode: "cobol", ext: ["cob", "cpy"]}, {name: "C#", mime: "text/x-csharp", mode: "clike", ext: ["cs"], alias: ["csharp"]}, From 8b9d8e337eb7c1d48d42589a5caee0a2215f620c Mon Sep 17 00:00:00 2001 From: Takuya Matsuyama Date: Mon, 1 Jan 2018 20:51:12 +0900 Subject: [PATCH 1181/1790] [php mode] Fix invalid mime definition --- mode/meta.js | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/mode/meta.js b/mode/meta.js index 89b2ced8..4ab3e08e 100644 --- a/mode/meta.js +++ b/mode/meta.js @@ -101,7 +101,7 @@ {name: "Pascal", mime: "text/x-pascal", mode: "pascal", ext: ["p", "pas"]}, {name: "PEG.js", mime: "null", mode: "pegjs", ext: ["jsonld"]}, {name: "Perl", mime: "text/x-perl", mode: "perl", ext: ["pl", "pm"]}, - {name: "PHP", mime: ["application/x-httpd-php", "text/x-php"], mode: "php", ext: ["php", "php3", "php4", "php5", "php7", "phtml"]}, + {name: "PHP", mimes: ["text/x-php", "application/x-httpd-php", "application/x-httpd-php-open"], mode: "php", ext: ["php", "php3", "php4", "php5", "php7", "phtml"]}, {name: "Pig", mime: "text/x-pig", mode: "pig", ext: ["pig"]}, {name: "Plain Text", mime: "text/plain", mode: "null", ext: ["txt", "text", "conf", "def", "list", "log"]}, {name: "PLSQL", mime: "text/x-plsql", mode: "sql", ext: ["pls"]}, From cacaa54596272e6ffadf24322123f773bc3c2d80 Mon Sep 17 00:00:00 2001 From: Shane Liesegang Date: Tue, 2 Jan 2018 18:01:27 -0500 Subject: [PATCH 1182/1790] [makdown mode] Don't let inline styles persist across list items --- mode/markdown/markdown.js | 8 ++++++++ 1 file changed, 8 insertions(+) diff --git a/mode/markdown/markdown.js b/mode/markdown/markdown.js index 60f1b300..7dfddccd 100644 --- a/mode/markdown/markdown.js +++ b/mode/markdown/markdown.js @@ -113,6 +113,8 @@ CodeMirror.defineMode("markdown", function(cmCfg, modeCfg) { function blankLine(state) { // Reset linkTitle state state.linkTitle = false; + state.linkHref = false; + state.linkText = false; // Reset EM state state.em = false; // Reset STRONG state @@ -151,6 +153,12 @@ CodeMirror.defineMode("markdown", function(cmCfg, modeCfg) { if (state.indentationDiff === null) { state.indentationDiff = state.indentation; if (prevLineIsList) { + // Reset inline styles which shouldn't propagate aross list items + state.em = false; + state.strong = false; + state.code = false; + state.strikethrough = false; + state.list = null; // While this list item's marker's indentation is less than the deepest // list item's content's indentation,pop the deepest list item From d2798d22509e33f3aeb79bb7f7437ba0de4605bf Mon Sep 17 00:00:00 2001 From: Neil Anderson Date: Sat, 6 Jan 2018 13:16:31 -0500 Subject: [PATCH 1183/1790] [sql mode] Include LC_CTYPE and LC_COLLATE keywords in x-pgsql The CREATE DATABASE command supports LC_CTYPE and LC_COLLATE keywords as per https://www.postgresql.org/docs/10/static/sql-createdatabase.html. This commit adds them to the postgres sql mode. --- 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 da416f20..63e87733 100644 --- a/mode/sql/sql.js +++ b/mode/sql/sql.js @@ -400,7 +400,7 @@ CodeMirror.defineMode("sql", function(config, parserConfig) { name: "sql", client: set("source"), // https://www.postgresql.org/docs/10/static/sql-keywords-appendix.html - keywords: set(sqlKeywords + "a abort abs absent absolute access according action ada add admin after aggregate all allocate also always analyse analyze any are array array_agg array_max_cardinality asensitive assertion assignment asymmetric at atomic attribute attributes authorization avg backward base64 before begin begin_frame begin_partition bernoulli binary bit_length blob blocked bom both breadth c cache call called cardinality cascade cascaded case cast catalog catalog_name ceil ceiling chain characteristics characters character_length character_set_catalog character_set_name character_set_schema char_length check checkpoint class class_origin clob close cluster coalesce cobol collate collation collation_catalog collation_name collation_schema collect column columns column_name command_function command_function_code comment comments commit committed concurrently condition condition_number configuration conflict connect connection connection_name constraint constraints constraint_catalog constraint_name constraint_schema constructor contains content continue control conversion convert copy corr corresponding cost covar_pop covar_samp cross csv cube cume_dist current current_catalog current_date current_default_transform_group current_path current_role current_row current_schema current_time current_timestamp current_transform_group_for_type current_user cursor cursor_name cycle data database datalink datetime_interval_code datetime_interval_precision day db deallocate dec declare default defaults deferrable deferred defined definer degree delimiter delimiters dense_rank depth deref derived describe descriptor deterministic diagnostics dictionary disable discard disconnect dispatch dlnewcopy dlpreviouscopy dlurlcomplete dlurlcompleteonly dlurlcompletewrite dlurlpath dlurlpathonly dlurlpathwrite dlurlscheme dlurlserver dlvalue do document domain dynamic dynamic_function dynamic_function_code each element else empty enable encoding encrypted end end-exec end_frame end_partition enforced enum equals escape event every except exception exclude excluding exclusive exec execute exists exp explain expression extension external extract false family fetch file filter final first first_value flag float floor following for force foreign fortran forward found frame_row free freeze fs full function functions fusion g general generated get global go goto grant granted greatest grouping groups handler header hex hierarchy hold hour id identity if ignore ilike immediate immediately immutable implementation implicit import including increment indent index indexes indicator inherit inherits initially inline inner inout input insensitive instance instantiable instead integrity intersect intersection invoker isnull isolation k key key_member key_type label lag language large last last_value lateral lead leading leakproof least left length level library like_regex link listen ln load local localtime localtimestamp location locator lock locked logged lower m map mapping match matched materialized max maxvalue max_cardinality member merge message_length message_octet_length message_text method min minute minvalue mod mode modifies module month more move multiset mumps name names namespace national natural nchar nclob nesting new next nfc nfd nfkc nfkd nil no none normalize normalized nothing notify notnull nowait nth_value ntile null nullable nullif nulls number object occurrences_regex octets octet_length of off offset oids old only open operator option options ordering ordinality others out outer output over overlaps overlay overriding owned owner p pad parallel parameter parameter_mode parameter_name parameter_ordinal_position parameter_specific_catalog parameter_specific_name parameter_specific_schema parser partial partition pascal passing passthrough password percent percentile_cont percentile_disc percent_rank period permission placing plans pli policy portion position position_regex power precedes preceding prepare prepared preserve primary prior privileges procedural procedure program public quote range rank read reads reassign recheck recovery recursive ref references referencing refresh regr_avgx regr_avgy regr_count regr_intercept regr_r2 regr_slope regr_sxx regr_sxy regr_syy reindex relative release rename repeatable replace replica requiring reset respect restart restore restrict restricted result return returned_cardinality returned_length returned_octet_length returned_sqlstate returning returns revoke right role rollback rollup routine routine_catalog routine_name routine_schema row rows row_count row_number rule savepoint scale schema schema_name scope scope_catalog scope_name scope_schema scroll search second section security selective self sensitive sequence sequences serializable server server_name session session_user setof sets share show similar simple size skip snapshot some source space specific specifictype specific_name sql sqlcode sqlerror sqlexception sqlstate sqlwarning sqrt stable standalone start state statement static statistics stddev_pop stddev_samp stdin stdout storage strict strip structure style subclass_origin submultiset substring substring_regex succeeds sum symmetric sysid system system_time system_user t tables tablesample tablespace table_name temp template temporary then ties timezone_hour timezone_minute to token top_level_count trailing transaction transactions_committed transactions_rolled_back transaction_active transform transforms translate translate_regex translation treat trigger trigger_catalog trigger_name trigger_schema trim trim_array true truncate trusted type types uescape unbounded uncommitted under unencrypted unique unknown unlink unlisten unlogged unnamed unnest until untyped upper uri usage user user_defined_type_catalog user_defined_type_code user_defined_type_name user_defined_type_schema using vacuum valid validate validator value value_of varbinary variadic var_pop var_samp verbose version versioning view views volatile when whenever whitespace width_bucket window within work wrapper write xmlagg xmlattributes xmlbinary xmlcast xmlcomment xmlconcat xmldeclaration xmldocument xmlelement xmlexists xmlforest xmliterate xmlnamespaces xmlparse xmlpi xmlquery xmlroot xmlschema xmlserialize xmltable xmltext xmlvalidate year yes loop repeat attach path depends detach zone"), + keywords: set(sqlKeywords + "a abort abs absent absolute access according action ada add admin after aggregate all allocate also always analyse analyze any are array array_agg array_max_cardinality asensitive assertion assignment asymmetric at atomic attribute attributes authorization avg backward base64 before begin begin_frame begin_partition bernoulli binary bit_length blob blocked bom both breadth c cache call called cardinality cascade cascaded case cast catalog catalog_name ceil ceiling chain characteristics characters character_length character_set_catalog character_set_name character_set_schema char_length check checkpoint class class_origin clob close cluster coalesce cobol collate collation collation_catalog collation_name collation_schema collect column columns column_name command_function command_function_code comment comments commit committed concurrently condition condition_number configuration conflict connect connection connection_name constraint constraints constraint_catalog constraint_name constraint_schema constructor contains content continue control conversion convert copy corr corresponding cost covar_pop covar_samp cross csv cube cume_dist current current_catalog current_date current_default_transform_group current_path current_role current_row current_schema current_time current_timestamp current_transform_group_for_type current_user cursor cursor_name cycle data database datalink datetime_interval_code datetime_interval_precision day db deallocate dec declare default defaults deferrable deferred defined definer degree delimiter delimiters dense_rank depth deref derived describe descriptor deterministic diagnostics dictionary disable discard disconnect dispatch dlnewcopy dlpreviouscopy dlurlcomplete dlurlcompleteonly dlurlcompletewrite dlurlpath dlurlpathonly dlurlpathwrite dlurlscheme dlurlserver dlvalue do document domain dynamic dynamic_function dynamic_function_code each element else empty enable encoding encrypted end end-exec end_frame end_partition enforced enum equals escape event every except exception exclude excluding exclusive exec execute exists exp explain expression extension external extract false family fetch file filter final first first_value flag float floor following for force foreign fortran forward found frame_row free freeze fs full function functions fusion g general generated get global go goto grant granted greatest grouping groups handler header hex hierarchy hold hour id identity if ignore ilike immediate immediately immutable implementation implicit import including increment indent index indexes indicator inherit inherits initially inline inner inout input insensitive instance instantiable instead integrity intersect intersection invoker isnull isolation k key key_member key_type label lag language large last last_value lateral lc_collate lc_ctype lead leading leakproof least left length level library like_regex link listen ln load local localtime localtimestamp location locator lock locked logged lower m map mapping match matched materialized max maxvalue max_cardinality member merge message_length message_octet_length message_text method min minute minvalue mod mode modifies module month more move multiset mumps name names namespace national natural nchar nclob nesting new next nfc nfd nfkc nfkd nil no none normalize normalized nothing notify notnull nowait nth_value ntile null nullable nullif nulls number object occurrences_regex octets octet_length of off offset oids old only open operator option options ordering ordinality others out outer output over overlaps overlay overriding owned owner p pad parallel parameter parameter_mode parameter_name parameter_ordinal_position parameter_specific_catalog parameter_specific_name parameter_specific_schema parser partial partition pascal passing passthrough password percent percentile_cont percentile_disc percent_rank period permission placing plans pli policy portion position position_regex power precedes preceding prepare prepared preserve primary prior privileges procedural procedure program public quote range rank read reads reassign recheck recovery recursive ref references referencing refresh regr_avgx regr_avgy regr_count regr_intercept regr_r2 regr_slope regr_sxx regr_sxy regr_syy reindex relative release rename repeatable replace replica requiring reset respect restart restore restrict restricted result return returned_cardinality returned_length returned_octet_length returned_sqlstate returning returns revoke right role rollback rollup routine routine_catalog routine_name routine_schema row rows row_count row_number rule savepoint scale schema schema_name scope scope_catalog scope_name scope_schema scroll search second section security selective self sensitive sequence sequences serializable server server_name session session_user setof sets share show similar simple size skip snapshot some source space specific specifictype specific_name sql sqlcode sqlerror sqlexception sqlstate sqlwarning sqrt stable standalone start state statement static statistics stddev_pop stddev_samp stdin stdout storage strict strip structure style subclass_origin submultiset substring substring_regex succeeds sum symmetric sysid system system_time system_user t tables tablesample tablespace table_name temp template temporary then ties timezone_hour timezone_minute to token top_level_count trailing transaction transactions_committed transactions_rolled_back transaction_active transform transforms translate translate_regex translation treat trigger trigger_catalog trigger_name trigger_schema trim trim_array true truncate trusted type types uescape unbounded uncommitted under unencrypted unique unknown unlink unlisten unlogged unnamed unnest until untyped upper uri usage user user_defined_type_catalog user_defined_type_code user_defined_type_name user_defined_type_schema using vacuum valid validate validator value value_of varbinary variadic var_pop var_samp verbose version versioning view views volatile when whenever whitespace width_bucket window within work wrapper write xmlagg xmlattributes xmlbinary xmlcast xmlcomment xmlconcat xmldeclaration xmldocument xmlelement xmlexists xmlforest xmliterate xmlnamespaces xmlparse xmlpi xmlquery xmlroot xmlschema xmlserialize xmltable xmltext xmlvalidate year yes loop repeat attach path depends detach zone"), // https://www.postgresql.org/docs/10/static/datatype.html builtin: set("bigint int8 bigserial serial8 bit varying varbit boolean bool box bytea character char varchar cidr circle date double precision float8 inet integer int int4 interval json jsonb line lseg macaddr macaddr8 money numeric decimal path pg_lsn point polygon real float4 smallint int2 smallserial serial2 serial serial4 text time without zone with timetz timestamp timestamptz tsquery tsvector txid_snapshot uuid xml"), atoms: set("false true null unknown"), From ce2fb7c8ebd31df403264fda8640bf8a1c22df02 Mon Sep 17 00:00:00 2001 From: Shane Liesegang Date: Sun, 7 Jan 2018 11:31:49 -0500 Subject: [PATCH 1184/1790] [markdown mode] xlinkHref status should get copied along with the rest of the state. --- mode/markdown/markdown.js | 1 + 1 file changed, 1 insertion(+) diff --git a/mode/markdown/markdown.js b/mode/markdown/markdown.js index 7dfddccd..24e24680 100644 --- a/mode/markdown/markdown.js +++ b/mode/markdown/markdown.js @@ -785,6 +785,7 @@ CodeMirror.defineMode("markdown", function(cmCfg, modeCfg) { formatting: false, linkText: s.linkText, linkTitle: s.linkTitle, + linkHref: s.linkHref, code: s.code, em: s.em, strong: s.strong, From ed9f4e3901bdece6d756ef8c167c026665778005 Mon Sep 17 00:00:00 2001 From: Cristian Prieto Date: Sun, 7 Jan 2018 20:20:42 +0100 Subject: [PATCH 1185/1790] [mllike mode] Add additional OCaml types, ML keywords --- mode/mllike/mllike.js | 13 +++++++++++-- 1 file changed, 11 insertions(+), 2 deletions(-) diff --git a/mode/mllike/mllike.js b/mode/mllike/mllike.js index 90e5b41a..e25e6627 100644 --- a/mode/mllike/mllike.js +++ b/mode/mllike/mllike.js @@ -37,7 +37,9 @@ CodeMirror.defineMode('mllike', function(_config, parserConfig) { 'open': 'builtin', 'ignore': 'builtin', 'begin': 'keyword', - 'end': 'keyword' + 'end': 'keyword', + 'when': 'keyword', + 'as': 'keyword' }; var extraWords = parserConfig.extraWords || {}; @@ -174,7 +176,14 @@ CodeMirror.defineMIME('text/x-ocaml', { 'false': 'atom', 'raise': 'keyword', 'module': 'keyword', - 'sig': 'keyword' + 'sig': 'keyword', + 'exception': 'keyword', + 'int': 'builtin', + 'float': 'builtin', + 'char': 'builtin', + 'string': 'builtin', + 'bool': 'builtin', + 'unit': 'builtin' } }); From 45345505a5c16171889aa4f7d3162cee9f806165 Mon Sep 17 00:00:00 2001 From: Marijn Haverbeke Date: Tue, 9 Jan 2018 12:13:24 +0100 Subject: [PATCH 1186/1790] [sublime bindings] Fix toggleBookMark This had been broken since 5.12 due to a change in the behavior of findMarksAt. Closes #5171 --- keymap/sublime.js | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/keymap/sublime.js b/keymap/sublime.js index 5925b7c5..7a9aadd3 100644 --- a/keymap/sublime.js +++ b/keymap/sublime.js @@ -382,7 +382,7 @@ var marks = cm.state.sublimeBookmarks || (cm.state.sublimeBookmarks = []); for (var i = 0; i < ranges.length; i++) { var from = ranges[i].from(), to = ranges[i].to(); - var found = cm.findMarks(from, to); + var found = ranges[i].empty() ? cm.findMarksAt(from) : cm.findMarks(from, to); for (var j = 0; j < found.length; j++) { if (found[j].sublimeBookmark) { found[j].clear(); From 774575d6f05f94cab0ec922f11c2e819906f2d11 Mon Sep 17 00:00:00 2001 From: Cristian Prieto Date: Tue, 9 Jan 2018 16:50:16 +0100 Subject: [PATCH 1187/1790] [mllike mode[ Refactor, add SML MIME * Add common keywords for ML languages * Add builtins for OCaml and F# * Add Standard ML as ML language --- mode/meta.js | 1 + mode/mllike/mllike.js | 202 ++++++++++++++++++++++++++++++++---------- 2 files changed, 154 insertions(+), 49 deletions(-) diff --git a/mode/meta.js b/mode/meta.js index 4ab3e08e..298074db 100644 --- a/mode/meta.js +++ b/mode/meta.js @@ -128,6 +128,7 @@ {name: "Smalltalk", mime: "text/x-stsrc", mode: "smalltalk", ext: ["st"]}, {name: "Smarty", mime: "text/x-smarty", mode: "smarty", ext: ["tpl"]}, {name: "Solr", mime: "text/x-solr", mode: "solr"}, + {name: "SML", mime: "text/x-sml", mode: "mllike", ext: ["sml", "sig", "fun", "smackspec"]}, {name: "Soy", mime: "text/x-soy", mode: "soy", ext: ["soy"], alias: ["closure template"]}, {name: "SPARQL", mime: "application/sparql-query", mode: "sparql", ext: ["rq", "sparql"], alias: ["sparul"]}, {name: "Spreadsheet", mime: "text/x-spreadsheet", mode: "spreadsheet", alias: ["excel", "formula"]}, diff --git a/mode/mllike/mllike.js b/mode/mllike/mllike.js index e25e6627..7038a339 100644 --- a/mode/mllike/mllike.js +++ b/mode/mllike/mllike.js @@ -13,33 +13,26 @@ CodeMirror.defineMode('mllike', function(_config, parserConfig) { var words = { - 'let': 'keyword', - 'rec': 'keyword', + 'as': 'keyword', + 'do': 'keyword', + 'else': 'keyword', + 'end': 'keyword', + 'exception': 'keyword', + 'fun': 'keyword', + 'functor': 'keyword', + 'if': 'keyword', 'in': 'keyword', + 'include': 'keyword', + 'let': 'keyword', 'of': 'keyword', - 'and': 'keyword', - 'if': 'keyword', + 'open': 'keyword', + 'rec': 'keyword', + 'struct': 'keyword', 'then': 'keyword', - 'else': 'keyword', - 'for': 'keyword', - 'to': 'keyword', - 'while': 'keyword', - 'do': 'keyword', - 'done': 'keyword', - 'fun': 'keyword', - 'function': 'keyword', - 'val': 'keyword', 'type': 'keyword', - 'mutable': 'keyword', - 'match': 'keyword', - 'with': 'keyword', - 'try': 'keyword', - 'open': 'builtin', - 'ignore': 'builtin', - 'begin': 'keyword', - 'end': 'keyword', - 'when': 'keyword', - 'as': 'keyword' + 'val': 'keyword', + 'while': 'keyword', + 'with': 'keyword' }; var extraWords = parserConfig.extraWords || {}; @@ -70,7 +63,7 @@ CodeMirror.defineMode('mllike', function(_config, parserConfig) { return state.tokenize(stream, state); } } - if (ch === '~') { + if (ch === '~' || ch === '?') { stream.eatWhile(/\w/); return 'variable-2'; } @@ -100,7 +93,7 @@ CodeMirror.defineMode('mllike', function(_config, parserConfig) { } return 'number'; } - if ( /[+\-*&%=<>!?|@]/.test(ch)) { + if ( /[+\-*&%=<>!?|@]?\./.test(ch)) { return 'operator'; } if (/[\w\xa1-\uffff]/.test(ch)) { @@ -167,23 +160,61 @@ CodeMirror.defineMode('mllike', function(_config, parserConfig) { CodeMirror.defineMIME('text/x-ocaml', { name: 'mllike', extraWords: { - 'succ': 'keyword', + 'and': 'keyword', + 'assert': 'keyword', + 'begin': 'keyword', + 'class': 'keyword', + 'constraint': 'keyword', + 'done': 'keyword', + 'downto': 'keyword', + 'external': 'keyword', + 'initializer': 'keyword', + 'lazy': 'keyword', + 'match': 'keyword', + 'method': 'keyword', + 'module': 'keyword', + 'mutable': 'keyword', + 'new': 'keyword', + 'nonrec': 'keyword', + 'object': 'keyword', + 'private': 'keyword', + 'sig': 'keyword', + 'to': 'keyword', + 'try': 'keyword', + 'value': 'keyword', + 'virtual': 'keyword', + 'when': 'keyword', + + // builtins + 'raise': 'builtin', + 'failwith': 'builtin', + 'true': 'builtin', + 'false': 'builtin', + + // Pervasives builtins + 'asr': 'builtin', + 'land': 'builtin', + 'lor': 'builtin', + 'lsl': 'builtin', + 'lsr': 'builtin', + 'lxor': 'builtin', + 'mod': 'builtin', + 'or': 'builtin', + + // More Pervasives + 'raise_notrace': 'builtin', 'trace': 'builtin', 'exit': 'builtin', 'print_string': 'builtin', 'print_endline': 'builtin', - 'true': 'atom', - 'false': 'atom', - 'raise': 'keyword', - 'module': 'keyword', - 'sig': 'keyword', - 'exception': 'keyword', - 'int': 'builtin', - 'float': 'builtin', - 'char': 'builtin', - 'string': 'builtin', - 'bool': 'builtin', - 'unit': 'builtin' + + // Types + 'int': 'atom', + 'float': 'atom', + 'bool': 'atom', + 'char': 'atom', + 'string': 'atom', + 'unit': 'atom', } }); @@ -191,18 +222,21 @@ CodeMirror.defineMIME('text/x-fsharp', { name: 'mllike', extraWords: { 'abstract': 'keyword', - 'as': 'keyword', 'assert': 'keyword', 'base': 'keyword', + 'begin': 'keyword', 'class': 'keyword', 'default': 'keyword', 'delegate': 'keyword', + 'do!': 'keyword', + 'done': 'keyword', 'downcast': 'keyword', 'downto': 'keyword', 'elif': 'keyword', - 'exception': 'keyword', 'extern': 'keyword', 'finally': 'keyword', + 'for': 'keyword', + 'function': 'keyword', 'global': 'keyword', 'inherit': 'keyword', 'inline': 'keyword', @@ -210,38 +244,108 @@ CodeMirror.defineMIME('text/x-fsharp', { 'internal': 'keyword', 'lazy': 'keyword', 'let!': 'keyword', - 'member' : 'keyword', + 'match': 'keyword', + 'member': 'keyword', 'module': 'keyword', + 'mutable': 'keyword', 'namespace': 'keyword', 'new': 'keyword', 'null': 'keyword', 'override': 'keyword', 'private': 'keyword', 'public': 'keyword', - 'return': 'keyword', 'return!': 'keyword', + 'return': 'keyword', 'select': 'keyword', 'static': 'keyword', - 'struct': 'keyword', + 'to': 'keyword', + 'try': 'keyword', 'upcast': 'keyword', - 'use': 'keyword', 'use!': 'keyword', - 'val': 'keyword', + 'use': 'keyword', + 'void': 'keyword', 'when': 'keyword', - 'yield': 'keyword', 'yield!': 'keyword', + 'yield': 'keyword', + + // Reserved words + 'atomic': 'keyword', + 'break': 'keyword', + 'checked': 'keyword', + 'component': 'keyword', + 'const': 'keyword', + 'constraint': 'keyword', + 'constructor': 'keyword', + 'continue': 'keyword', + 'eager': 'keyword', + 'event': 'keyword', + 'external': 'keyword', + 'fixed': 'keyword', + 'method': 'keyword', + 'mixin': 'keyword', + 'object': 'keyword', + 'parallel': 'keyword', + 'process': 'keyword', + 'protected': 'keyword', + 'pure': 'keyword', + 'sealed': 'keyword', + 'tailcall': 'keyword', + 'trait': 'keyword', + 'virtual': 'keyword', + 'volatile': 'keyword', + // builtins 'List': 'builtin', 'Seq': 'builtin', 'Map': 'builtin', 'Set': 'builtin', + 'Option': 'builtin', 'int': 'builtin', 'string': 'builtin', - 'raise': 'builtin', - 'failwith': 'builtin', 'not': 'builtin', 'true': 'builtin', - 'false': 'builtin' + 'false': 'builtin', + + 'raise': 'builtin', + 'failwith': 'builtin' + }, + slashComments: true +}); + + +CodeMirror.defineMIME('text/x-sml', { + name: 'mllike', + extraWords: { + 'abstype': 'keyword', + 'and': 'keyword', + 'andalso': 'keyword', + 'case': 'keyword', + 'datatype': 'keyword', + 'fn': 'keyword', + 'handle': 'keyword', + 'infix': 'keyword', + 'infixr': 'keyword', + 'local': 'keyword', + 'nonfix': 'keyword', + 'op': 'keyword', + 'orelse': 'keyword', + 'raise': 'keyword', + 'withtype': 'keyword', + 'eqtype': 'keyword', + 'sharing': 'keyword', + 'sig': 'keyword', + 'signature': 'keyword', + 'structure': 'keyword', + 'where': 'keyword', + 'true': 'keyword', + 'false': 'keyword', + + // types + 'int': 'builtin', + 'real': 'builtin', + 'string': 'builtin', + 'char': 'builtin', + 'bool': 'builtin' }, slashComments: true }); From e35f5bbc0c147c15460a189e0a8f291020a9ba8c Mon Sep 17 00:00:00 2001 From: Marijn Haverbeke Date: Wed, 10 Jan 2018 09:39:04 +0100 Subject: [PATCH 1188/1790] [placeholder plugin] Use editor direction Closes #5174 --- addon/display/placeholder.js | 1 + 1 file changed, 1 insertion(+) diff --git a/addon/display/placeholder.js b/addon/display/placeholder.js index 2f8b1f84..65753ebf 100644 --- a/addon/display/placeholder.js +++ b/addon/display/placeholder.js @@ -38,6 +38,7 @@ clearPlaceholder(cm); var elt = cm.state.placeholder = document.createElement("pre"); elt.style.cssText = "height: 0; overflow: visible"; + elt.style.direction = cm.getOption("direction"); elt.className = "CodeMirror-placeholder"; var placeHolder = cm.getOption("placeholder") if (typeof placeHolder == "string") placeHolder = document.createTextNode(placeHolder) From 2786ff0a86f32911429351a6aca4cec65e1a5b85 Mon Sep 17 00:00:00 2001 From: neon-dev <1169307+neon-dev@users.noreply.github.com> Date: Mon, 8 Jan 2018 12:55:04 +0100 Subject: [PATCH 1189/1790] [sql-hint addon] Switch order of hints Show column hints (if a defaultTable is set) above table hints, since you are far more often in clauses where you need those. --- addon/hint/sql-hint.js | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/addon/hint/sql-hint.js b/addon/hint/sql-hint.js index f5ec2cac..5600c839 100644 --- a/addon/hint/sql-hint.js +++ b/addon/hint/sql-hint.js @@ -273,8 +273,8 @@ if (search.charAt(0) == "." || search.charAt(0) == identifierQuote) { start = nameCompletion(cur, token, result, editor); } else { - addMatches(result, search, tables, function(w) {return w;}); addMatches(result, search, defaultTable, function(w) {return w;}); + addMatches(result, search, tables, function(w) {return w;}); if (!disableKeywords) addMatches(result, search, keywords, function(w) {return w.toUpperCase();}); } From 350f71b09d166f1a149514503dc86ff00963faa1 Mon Sep 17 00:00:00 2001 From: neon-dev <1169307+neon-dev@users.noreply.github.com> Date: Mon, 8 Jan 2018 12:50:23 +0100 Subject: [PATCH 1190/1790] [sql-hint addon] Fix nullpointer If you try to autocomplete at line 0, column 0 after previoulsy having edited for example a WHERE clause, the autocompletion triggers with an invalid position, since prevItem is null. Maybe my fix isn't the best solution, and you could even avoid to enter findTableByAlias enitrely, I don't know. --- addon/hint/sql-hint.js | 26 ++++++++++++++------------ 1 file changed, 14 insertions(+), 12 deletions(-) diff --git a/addon/hint/sql-hint.js b/addon/hint/sql-hint.js index 5600c839..5d20eea6 100644 --- a/addon/hint/sql-hint.js +++ b/addon/hint/sql-hint.js @@ -222,18 +222,20 @@ prevItem = separator[i]; } - var query = doc.getRange(validRange.start, validRange.end, false); - - for (var i = 0; i < query.length; i++) { - var lineText = query[i]; - eachWord(lineText, function(word) { - var wordUpperCase = word.toUpperCase(); - if (wordUpperCase === aliasUpperCase && getTable(previousWord)) - table = previousWord; - if (wordUpperCase !== CONS.ALIAS_KEYWORD) - previousWord = word; - }); - if (table) break; + if (validRange.start) { + var query = doc.getRange(validRange.start, validRange.end, false); + + for (var i = 0; i < query.length; i++) { + var lineText = query[i]; + eachWord(lineText, function(word) { + var wordUpperCase = word.toUpperCase(); + if (wordUpperCase === aliasUpperCase && getTable(previousWord)) + table = previousWord; + if (wordUpperCase !== CONS.ALIAS_KEYWORD) + previousWord = word; + }); + if (table) break; + } } return table; } From dccaafe5200267dc9a2d605c6caee592bfb0408b Mon Sep 17 00:00:00 2001 From: Filype Pereira Date: Sat, 30 Dec 2017 09:52:02 +1300 Subject: [PATCH 1191/1790] [oceanic-next theme] Add --- demo/theme.html | 2 ++ theme/oceanic-next.css | 44 ++++++++++++++++++++++++++++++++++++++++++ 2 files changed, 46 insertions(+) create mode 100644 theme/oceanic-next.css diff --git a/demo/theme.html b/demo/theme.html index 9194dcea..0c52d256 100644 --- a/demo/theme.html +++ b/demo/theme.html @@ -34,6 +34,7 @@ + @@ -119,6 +120,7 @@

      Theme Demo

      + diff --git a/theme/oceanic-next.css b/theme/oceanic-next.css new file mode 100644 index 00000000..296277ba --- /dev/null +++ b/theme/oceanic-next.css @@ -0,0 +1,44 @@ +/* + + Name: oceanic-next + Author: Filype Pereira (https://github.com/fpereira1) + + Original oceanic-next color scheme by Dmitri Voronianski (https://github.com/voronianski/oceanic-next-color-scheme) + +*/ + +.cm-s-oceanic-next.CodeMirror { background: #304148; color: #f8f8f2; } +.cm-s-oceanic-next div.CodeMirror-selected { background: rgba(101, 115, 126, 0.33); } +.cm-s-oceanic-next .CodeMirror-line::selection, .cm-s-oceanic-next .CodeMirror-line > span::selection, .cm-s-oceanic-next .CodeMirror-line > span > span::selection { background: rgba(101, 115, 126, 0.33); } +.cm-s-oceanic-next .CodeMirror-line::-moz-selection, .cm-s-oceanic-next .CodeMirror-line > span::-moz-selection, .cm-s-oceanic-next .CodeMirror-line > span > span::-moz-selection { background: rgba(101, 115, 126, 0.33); } +.cm-s-oceanic-next .CodeMirror-gutters { background: #304148; border-right: 10px; } +.cm-s-oceanic-next .CodeMirror-guttermarker { color: white; } +.cm-s-oceanic-next .CodeMirror-guttermarker-subtle { color: #d0d0d0; } +.cm-s-oceanic-next .CodeMirror-linenumber { color: #d0d0d0; } +.cm-s-oceanic-next .CodeMirror-cursor { border-left: 1px solid #f8f8f0; } + +.cm-s-oceanic-next span.cm-comment { color: #65737E; } +.cm-s-oceanic-next span.cm-atom { color: #C594C5; } +.cm-s-oceanic-next span.cm-number { color: #F99157; } + +.cm-s-oceanic-next span.cm-property { color: #99C794; } +.cm-s-oceanic-next span.cm-attribute, +.cm-s-oceanic-next span.cm-keyword { color: #C594C5; } +.cm-s-oceanic-next span.cm-builtin { color: #66d9ef; } +.cm-s-oceanic-next span.cm-string { color: #99C794; } + +.cm-s-oceanic-next span.cm-variable, +.cm-s-oceanic-next span.cm-variable-2, +.cm-s-oceanic-next span.cm-variable-3 { color: #f8f8f2; } +.cm-s-oceanic-next span.cm-def { color: #6699CC; } +.cm-s-oceanic-next span.cm-bracket { color: #5FB3B3; } +.cm-s-oceanic-next span.cm-tag { color: #C594C5; } +.cm-s-oceanic-next span.cm-header { color: #C594C5; } +.cm-s-oceanic-next span.cm-link { color: #C594C5; } +.cm-s-oceanic-next span.cm-error { background: #C594C5; color: #f8f8f0; } + +.cm-s-oceanic-next .CodeMirror-activeline-background { background: rgba(101, 115, 126, 0.33); } +.cm-s-oceanic-next .CodeMirror-matchingbracket { + text-decoration: underline; + color: white !important; +} From e9e5f23b81ec86f84680d8b13ff422408e1a9428 Mon Sep 17 00:00:00 2001 From: overdodactyl Date: Sat, 6 Jan 2018 21:28:00 -0700 Subject: [PATCH 1192/1790] [shadowfox theme] Add --- demo/theme.html | 2 ++ theme/shadowfox.css | 52 +++++++++++++++++++++++++++++++++++++++++++++ 2 files changed, 54 insertions(+) create mode 100644 theme/shadowfox.css diff --git a/demo/theme.html b/demo/theme.html index 0c52d256..e01f79a7 100644 --- a/demo/theme.html +++ b/demo/theme.html @@ -42,6 +42,7 @@ + @@ -128,6 +129,7 @@

      Theme Demo

      + diff --git a/theme/shadowfox.css b/theme/shadowfox.css new file mode 100644 index 00000000..32d59b13 --- /dev/null +++ b/theme/shadowfox.css @@ -0,0 +1,52 @@ +/* + + Name: shadowfox + Author: overdodactyl (http://github.com/overdodactyl) + + Original shadowfox color scheme by Firefox + +*/ + +.cm-s-shadowfox.CodeMirror { background: #2a2a2e; color: #b1b1b3; } +.cm-s-shadowfox div.CodeMirror-selected { background: #353B48; } +.cm-s-shadowfox .CodeMirror-line::selection, .cm-s-shadowfox .CodeMirror-line > span::selection, .cm-s-shadowfox .CodeMirror-line > span > span::selection { background: #353B48; } +.cm-s-shadowfox .CodeMirror-line::-moz-selection, .cm-s-shadowfox .CodeMirror-line > span::-moz-selection, .cm-s-shadowfox .CodeMirror-line > span > span::-moz-selection { background: #353B48; } +.cm-s-shadowfox .CodeMirror-gutters { background: #0c0c0d ; border-right: 1px solid #0c0c0d; } +.cm-s-shadowfox .CodeMirror-guttermarker { color: #555; } +.cm-s-shadowfox .CodeMirror-linenumber { color: #939393; } +.cm-s-shadowfox .CodeMirror-cursor { border-left: 1px solid #fff; } + +.cm-s-shadowfox span.cm-comment { color: #939393; } +.cm-s-shadowfox span.cm-atom { color: #FF7DE9; } +.cm-s-shadowfox span.cm-quote { color: #FF7DE9; } +.cm-s-shadowfox span.cm-builtin { color: #FF7DE9; } +.cm-s-shadowfox span.cm-attribute { color: #FF7DE9; } +.cm-s-shadowfox span.cm-keyword { color: #FF7DE9; } +.cm-s-shadowfox span.cm-error { color: #FF7DE9; } + +.cm-s-shadowfox span.cm-number { color: #6B89FF; } +.cm-s-shadowfox span.cm-string { color: #6B89FF; } +.cm-s-shadowfox span.cm-string-2 { color: #6B89FF; } + +.cm-s-shadowfox span.cm-meta { color: #939393; } +.cm-s-shadowfox span.cm-hr { color: #939393; } + +.cm-s-shadowfox span.cm-header { color: #75BFFF; } +.cm-s-shadowfox span.cm-qualifier { color: #75BFFF; } +.cm-s-shadowfox span.cm-variable-2 { color: #75BFFF; } + +.cm-s-shadowfox span.cm-property { color: #86DE74; } + +.cm-s-shadowfox span.cm-def { color: #75BFFF; } +.cm-s-shadowfox span.cm-bracket { color: #75BFFF; } +.cm-s-shadowfox span.cm-tag { color: #75BFFF; } +.cm-s-shadowfox span.cm-link:visited { color: #75BFFF; } + +.cm-s-shadowfox span.cm-variable { color: #B98EFF; } +.cm-s-shadowfox span.cm-variable-3 { color: #d7d7db; } +.cm-s-shadowfox span.cm-link { color: #737373; } +.cm-s-shadowfox span.cm-operator { color: #b1b1b3; } +.cm-s-shadowfox span.cm-special { color: #d7d7db; } + +.cm-s-shadowfox .CodeMirror-activeline-background { background: rgba(185, 215, 253, .15) } +.cm-s-shadowfox .CodeMirror-matchingbracket { outline: solid 1px rgba(255, 255, 255, .25); color: white !important; } From e4bf8dff42d80a4bfab366733dfd992b62a66880 Mon Sep 17 00:00:00 2001 From: Marijn Haverbeke Date: Thu, 11 Jan 2018 08:56:15 +0100 Subject: [PATCH 1193/1790] [closebrackets addon] Avoid annoying behavior when closing a triple-quoted string Issue #5177 --- addon/edit/closebrackets.js | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/addon/edit/closebrackets.js b/addon/edit/closebrackets.js index 460f662f..86b2fe1c 100644 --- a/addon/edit/closebrackets.js +++ b/addon/edit/closebrackets.js @@ -129,8 +129,8 @@ else curType = "skip"; } else if (identical && cur.ch > 1 && triples.indexOf(ch) >= 0 && - cm.getRange(Pos(cur.line, cur.ch - 2), cur) == ch + ch && - (cur.ch <= 2 || cm.getRange(Pos(cur.line, cur.ch - 3), Pos(cur.line, cur.ch - 2)) != ch)) { + cm.getRange(Pos(cur.line, cur.ch - 2), cur) == ch + ch) { + if (cur.ch > 2 && /\bstring/.test(cm.getTokenTypeAt(Pos(cur.line, cur.ch - 2)))) return CodeMirror.Pass; curType = "addFour"; } else if (identical) { var prev = cur.ch == 0 ? " " : cm.getRange(Pos(cur.line, cur.ch - 1), cur) From 2a168994fba2a6c9250edb59b7dc56dc46767053 Mon Sep 17 00:00:00 2001 From: Marijn Haverbeke Date: Fri, 12 Jan 2018 08:26:55 +0100 Subject: [PATCH 1194/1790] [javascript mode] Fix highlighting of TS implements keyword Closes #5178 --- mode/javascript/javascript.js | 4 +++- 1 file changed, 3 insertions(+), 1 deletion(-) diff --git a/mode/javascript/javascript.js b/mode/javascript/javascript.js index edab99f3..29085a21 100644 --- a/mode/javascript/javascript.js +++ b/mode/javascript/javascript.js @@ -680,8 +680,10 @@ CodeMirror.defineMode("javascript", function(config, parserConfig) { } function classNameAfter(type, value) { if (value == "<") return cont(pushlex(">"), commasep(typeparam, ">"), poplex, classNameAfter) - if (value == "extends" || value == "implements" || (isTS && type == ",")) + if (value == "extends" || value == "implements" || (isTS && type == ",")) { + cx.marked = "keyword"; return cont(isTS ? typeexpr : expression, classNameAfter); + } if (type == "{") return cont(pushlex("}"), classBody, poplex); } function classBody(type, value) { From d89267681d21f46a78a90002a9b18dc95acac1f4 Mon Sep 17 00:00:00 2001 From: Marijn Haverbeke Date: Fri, 12 Jan 2018 08:36:22 +0100 Subject: [PATCH 1195/1790] [javascript mode] Fix previous patch --- 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 29085a21..64c910d8 100644 --- a/mode/javascript/javascript.js +++ b/mode/javascript/javascript.js @@ -681,7 +681,7 @@ CodeMirror.defineMode("javascript", function(config, parserConfig) { function classNameAfter(type, value) { if (value == "<") return cont(pushlex(">"), commasep(typeparam, ">"), poplex, classNameAfter) if (value == "extends" || value == "implements" || (isTS && type == ",")) { - cx.marked = "keyword"; + if (value == "implements") cx.marked = "keyword"; return cont(isTS ? typeexpr : expression, classNameAfter); } if (type == "{") return cont(pushlex("}"), classBody, poplex); From 4fa785ea875aade5beb64ebc317969f3fb653125 Mon Sep 17 00:00:00 2001 From: Marijn Haverbeke Date: Sat, 13 Jan 2018 10:34:10 +0100 Subject: [PATCH 1196/1790] [xml-fold addon] Handle line-broken opening tags better No longer creates a fold spot for both lines of a line-broken tag. Closes #5179 --- addon/fold/xml-fold.js | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/addon/fold/xml-fold.js b/addon/fold/xml-fold.js index 08e21495..3acf952d 100644 --- a/addon/fold/xml-fold.js +++ b/addon/fold/xml-fold.js @@ -138,7 +138,7 @@ var iter = new Iter(cm, start.line, 0); for (;;) { var openTag = toNextTag(iter), end; - if (!openTag || iter.line != start.line || !(end = toTagEnd(iter))) return; + if (!openTag || !(end = toTagEnd(iter)) || iter.line != start.line) return; if (!openTag[1] && end != "selfClose") { var startPos = Pos(iter.line, iter.ch); var endPos = findMatchingClose(iter, openTag[2]); From e03ef21df390753eb35ff8426dd99b1be4d29d74 Mon Sep 17 00:00:00 2001 From: Marijn Haverbeke Date: Sat, 13 Jan 2018 17:52:27 +0100 Subject: [PATCH 1197/1790] [javascript mode] Further improve handling of TS contextual keywords Closes #5180 Closes #5181 --- mode/javascript/javascript.js | 20 +++++++++++++------- mode/javascript/test.js | 10 ++++++++++ 2 files changed, 23 insertions(+), 7 deletions(-) diff --git a/mode/javascript/javascript.js b/mode/javascript/javascript.js index 64c910d8..9eb50ba9 100644 --- a/mode/javascript/javascript.js +++ b/mode/javascript/javascript.js @@ -345,15 +345,14 @@ CodeMirror.defineMode("javascript", function(config, parserConfig) { if (type == "for") return cont(pushlex("form"), forspec, statement, poplex); if (type == "class" || (isTS && value == "interface")) { cx.marked = "keyword"; return cont(pushlex("form"), className, poplex); } if (type == "variable") { - if (isTS && value == "type") { - cx.marked = "keyword" - return cont(typeexpr, expect("operator"), typeexpr, expect(";")); - } else if (isTS && value == "declare") { + if (isTS && value == "declare") { cx.marked = "keyword" return cont(statement) - } else if (isTS && (value == "module" || value == "enum") && cx.stream.match(/^\s*\w/, false)) { + } else if (isTS && (value == "module" || value == "enum" || value == "type") && cx.stream.match(/^\s*\w/, false)) { cx.marked = "keyword" - return cont(pushlex("form"), pattern, expect("{"), pushlex("}"), block, poplex, poplex) + if (value == "enum") return cont(enumdef); + else if (value == "type") return cont(typeexpr, expect("operator"), typeexpr, expect(";")); + else return cont(pushlex("form"), pattern, expect("{"), pushlex("}"), block, poplex, poplex) } else if (isTS && value == "namespace") { cx.marked = "keyword" return cont(pushlex("form"), expression, block, poplex) @@ -608,7 +607,8 @@ CodeMirror.defineMode("javascript", function(config, parserConfig) { function maybeTypeDefault(_, value) { if (value == "=") return cont(typeexpr) } - function vardef() { + function vardef(_, value) { + if (value == "enum") {cx.marked = "keyword"; return cont(enumdef)} return pass(pattern, maybetype, maybeAssign, vardefCont); } function pattern(type, value) { @@ -747,6 +747,12 @@ CodeMirror.defineMode("javascript", function(config, parserConfig) { if (type == "]") return cont(); return pass(commasep(expressionNoComma, "]")); } + function enumdef() { + return pass(pushlex("form"), pattern, expect("{"), pushlex("}"), commasep(enummember, "}"), poplex, poplex) + } + function enummember() { + return pass(pattern, maybeAssign); + } function isContinuedStatement(state, textAfter) { return state.lastType == "operator" || state.lastType == "," || diff --git a/mode/javascript/test.js b/mode/javascript/test.js index 167e6d01..14a5183c 100644 --- a/mode/javascript/test.js +++ b/mode/javascript/test.js @@ -383,6 +383,16 @@ " }", "}") + TS("type as variable", + "[variable type] [operator =] [variable x] [keyword as] [type Bar];"); + + TS("enum body", + "[keyword export] [keyword const] [keyword enum] [def CodeInspectionResultType] {", + " [def ERROR] [operator =] [string 'problem_type_error'],", + " [def WARNING] [operator =] [string 'problem_type_warning'],", + " [def META],", + "}") + var jsonld_mode = CodeMirror.getMode( {indentUnit: 2}, {name: "javascript", jsonld: true} From 974182466ba8165f15ccc6b5b07b785bcbc39c63 Mon Sep 17 00:00:00 2001 From: Marijn Haverbeke Date: Wed, 17 Jan 2018 09:37:55 +0100 Subject: [PATCH 1198/1790] [shell mode] Improve handling of quotes inside parentheses Closes #5187 --- mode/shell/shell.js | 23 ++++++++++++++++------- mode/shell/test.js | 3 +++ 2 files changed, 19 insertions(+), 7 deletions(-) diff --git a/mode/shell/shell.js b/mode/shell/shell.js index 9b8b90b3..9fcd671c 100644 --- a/mode/shell/shell.js +++ b/mode/shell/shell.js @@ -84,29 +84,38 @@ CodeMirror.defineMode('shell', function() { function tokenString(quote, style) { var close = quote == "(" ? ")" : quote == "{" ? "}" : quote return function(stream, state) { - var next, end = false, escaped = false; + var next, escaped = false; while ((next = stream.next()) != null) { if (next === close && !escaped) { - end = true; + state.tokens.shift(); break; - } - if (next === '$' && !escaped && quote !== "'") { + } else if (next === '$' && !escaped && quote !== "'") { escaped = true; stream.backUp(1); state.tokens.unshift(tokenDollar); break; - } - if (!escaped && next === quote && quote !== close) { + } else if (!escaped && quote !== close && next === quote) { state.tokens.unshift(tokenString(quote, style)) return tokenize(stream, state) + } else if (!escaped && /['"]/.test(next) && !/['"]/.test(quote)) { + state.tokens.unshift(tokenStringStart(next, "string")); + stream.backUp(1); + break; } escaped = !escaped && next === '\\'; } - if (end) state.tokens.shift(); return style; }; }; + function tokenStringStart(quote, style) { + return function(stream, state) { + state.tokens[0] = tokenString(quote, style) + stream.next() + return tokenize(stream, state) + } + } + var tokenDollar = function(stream, state) { if (state.tokens.length > 1) stream.eat('$'); var ch = stream.next() diff --git a/mode/shell/test.js b/mode/shell/test.js index 86e344c5..05f07d22 100644 --- a/mode/shell/test.js +++ b/mode/shell/test.js @@ -61,4 +61,7 @@ MT("nested braces", "[builtin echo] [def ${A[${B}]]}]") + + MT("strings in parens", + "[def FOO][operator =]([quote $(<][string \"][def $MYDIR][string \"][quote /myfile grep ][string 'hello$'][quote )])") })(); From d8d68a8a86cce37fd3b19d0c3025e1a38df20ead Mon Sep 17 00:00:00 2001 From: neon-dev <1169307+neon-dev@users.noreply.github.com> Date: Tue, 16 Jan 2018 14:16:47 +0100 Subject: [PATCH 1199/1790] [javascript-lint addon] Fix incorrect severity When enabling strict equality checks via `lint: {options: {eqeqeq: true}}`, found problems showed up as errors instead of warnings. To fix it, the `fixWith()` logic had to be changed since simply adding the phrase to the warnings array would not have worked. This is because both the warnings and errors array matched this exact error and therefore the severity could never be "warning" (errors were checked after warnings). I didn't include "Missing property name" and "Unmatched " in the new error array since they already are errors with the new logic. "Stopping, unable to continue" also got removed since it didn't appear anywhere in the current jshint.js. For now I've implemented everything to not break previous behavior/hinting, except the strict equality hint severity. Although I want to suggest removing the following codes from the new error array (so they can stay warnings): - W033 (Missing semicolon) - since erroneous missing semicolons have their own code: E058 - W084 (Expected a conditional expression and instead saw an assignment) - since something like `switch (var2 = var1 + 42)` is valid js code, though not recommendable - maybe W023/24/30/90, since there are many more " and instead saw an" hints that are already errors with their own codes, so I think they should be pretty accurate. Unfortunately I couldn't force these warnings so I couldn't check. --- addon/lint/javascript-lint.js | 49 +++++++++++++++++++++++------------ 1 file changed, 32 insertions(+), 17 deletions(-) diff --git a/addon/lint/javascript-lint.js b/addon/lint/javascript-lint.js index c58f7850..f73aaa51 100644 --- a/addon/lint/javascript-lint.js +++ b/addon/lint/javascript-lint.js @@ -14,12 +14,20 @@ var bogus = [ "Dangerous comment" ]; - var warnings = [ [ "Expected '{'", + var replacements = [ [ "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" ]; + var forcedErrorCodes = [ + "W033", // Missing semicolon. + "W070", // Extra comma. (it breaks older versions of IE) + "W112", // Unclosed string. + "W117", // '{a}' is not defined. + "W023", // Expected an identifier in an assignment and instead saw a function invocation. + "W024", // Expected an identifier and instead saw '{a}' (a reserved word). + "W030", // Expected an assignment or function call and instead saw an expression. + "W084", // Expected a conditional expression and instead saw an assignment. + "W095" // Expected a string and instead saw {a}. + ]; function validator(text, options) { if (!window.JSHINT) { @@ -37,29 +45,35 @@ CodeMirror.registerHelper("lint", "javascript", validator); function cleanup(error) { - // All problems are warnings by default - fixWith(error, warnings, "warning", true); - fixWith(error, errors, "error"); + fixWith(error, forcedErrorCodes, replacements); return isBogus(error) ? null : error; } - function fixWith(error, fixes, severity, force) { - var description, fix, find, replace, found; + function fixWith(error, forcedErrorCodes, replacements) { + var errorCode, description, i, fix, find, replace, found; + errorCode = error.code; 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]); + if (error.severity !== "error") { + for (i = 0; i < forcedErrorCodes.length; i++) { + if (errorCode === forcedErrorCodes[i]) { + error.severity = "error"; + break; + } + } + } + + for (i = 0; i < replacements.length; i++) { + fix = replacements[i]; + find = fix[0]; found = description.indexOf(find) !== -1; - if (force || found) { - error.severity = severity; - } - if (found && replace) { + if (found) { + replace = fix[1]; error.description = replace; + break; } } } @@ -128,6 +142,7 @@ error.description = error.reason;// + "(jshint)"; error.start = error.character; error.end = end; + error.severity = error.code.startsWith('W') ? "warning" : "error"; error = cleanup(error); if (error) From dac3bdec7a5678c9adedfe3d8f2ce86f9823361c Mon Sep 17 00:00:00 2001 From: neon-dev <1169307+neon-dev@users.noreply.github.com> Date: Tue, 16 Jan 2018 16:08:53 +0100 Subject: [PATCH 1200/1790] [javascript-lint addon] Remove obsolete function --- addon/lint/javascript-lint.js | 16 +--------------- 1 file changed, 1 insertion(+), 15 deletions(-) diff --git a/addon/lint/javascript-lint.js b/addon/lint/javascript-lint.js index f73aaa51..a6d31210 100644 --- a/addon/lint/javascript-lint.js +++ b/addon/lint/javascript-lint.js @@ -12,8 +12,6 @@ "use strict"; // declare global: JSHINT - var bogus = [ "Dangerous comment" ]; - var replacements = [ [ "Expected '{'", "Statement body should be inside '{ }' braces." ] ]; @@ -46,8 +44,6 @@ function cleanup(error) { fixWith(error, forcedErrorCodes, replacements); - - return isBogus(error) ? null : error; } function fixWith(error, forcedErrorCodes, replacements) { @@ -78,16 +74,6 @@ } } - 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, output) { for ( var i = 0; i < errors.length; i++) { var error = errors[i]; @@ -143,7 +129,7 @@ error.start = error.character; error.end = end; error.severity = error.code.startsWith('W') ? "warning" : "error"; - error = cleanup(error); + cleanup(error); if (error) output.push({message: error.description, From 81391e6ad9b30d1238d10843325ef375e5f91356 Mon Sep 17 00:00:00 2001 From: neon-dev <1169307+neon-dev@users.noreply.github.com> Date: Wed, 17 Jan 2018 11:37:47 +0100 Subject: [PATCH 1201/1790] [lint demo] Use a more recent version of JSHint --- demo/lint.html | 2 +- demo/widget.html | 2 +- 2 files changed, 2 insertions(+), 2 deletions(-) diff --git a/demo/lint.html b/demo/lint.html index 96009b4e..2a1c30b4 100644 --- a/demo/lint.html +++ b/demo/lint.html @@ -9,7 +9,7 @@ - + diff --git a/demo/widget.html b/demo/widget.html index da39a929..58ebb4d9 100644 --- a/demo/widget.html +++ b/demo/widget.html @@ -7,7 +7,7 @@ - +
    + +
    phrases: ?object
    +
    Some addons run user-visible strings (such as labels in the + interface) through the phrase + method to allow for translation. This option determines the + return value of that method. When it is null or an object that + doesn't have a property named by the input string, that string + is returned. Otherwise, the value of the property corresponding + to that string is returned.

    Below this a few more specialized, low-level options are @@ -2120,6 +2129,11 @@

    Miscellaneous methods

    cm.focus()
    Give the editor focus.
    +
    cm.phrase(text: string) → string
    +
    Allow the given string to be translated with + the phrases + option.
    +
    cm.getInputField() → Element
    Returns the input field for the editor. Will be a textarea or an editable div, depending on the value of diff --git a/src/edit/methods.js b/src/edit/methods.js index 2e4aa85d..685b5112 100644 --- a/src/edit/methods.js +++ b/src/edit/methods.js @@ -432,19 +432,9 @@ export default function(CodeMirror) { return old }), - phrases: {}, - setPhrases: function(customPhrases) { - for (var key in customPhrases) { - if (customPhrases.hasOwnProperty(key)) this.phrases[key] = customPhrases[key] - } - }, - phrase: function(phraseText) { - if (this.phrases !== null && this.phrases.hasOwnProperty(phraseText)) { - return this.phrases[phraseText] - } else { - return phraseText - } + let phrases = this.options.phrases + return phrases && Object.prototype.hasOwnProperty.call(phrases, phraseText) ? phrases[phraseText] : phraseText }, getInputField: function(){return this.display.input.getField()}, diff --git a/src/edit/options.js b/src/edit/options.js index 72c70817..0ba5d4e3 100644 --- a/src/edit/options.js +++ b/src/edit/options.js @@ -154,7 +154,7 @@ export function defineOptions(CodeMirror) { option("tabindex", null, (cm, val) => cm.display.input.getField().tabIndex = val || "") option("autofocus", null) option("direction", "ltr", (cm, val) => cm.doc.setDirection(val), true) - option("phrases", null, (cm, val) => cm.setPhrases(val)) + option("phrases", null) } function guttersChanged(cm) { From b384a9f92318d6e4f9982aef1478ed3b6986b453 Mon Sep 17 00:00:00 2001 From: Marijn Haverbeke Date: Wed, 22 Aug 2018 08:27:19 +0200 Subject: [PATCH 1364/1790] [hardwrap addon] Fix infinite loop When the wrapping column was before the leading space, the code got confused. Closes #5535 --- addon/wrap/hardwrap.js | 1 + 1 file changed, 1 insertion(+) diff --git a/addon/wrap/hardwrap.js b/addon/wrap/hardwrap.js index 0ab7bbbb..29cc15f0 100644 --- a/addon/wrap/hardwrap.js +++ b/addon/wrap/hardwrap.js @@ -52,6 +52,7 @@ var lines = cm.getRange(from, to, false); if (!lines.length) return null; var leadingSpace = lines[0].match(/^[ \t]*/)[0]; + if (leadingSpace.length >= column) column = leadingSpace.length + 1 for (var i = 0; i < lines.length; ++i) { var text = lines[i], oldLen = curLine.length, spaceInserted = 0; From 046743c55a1a684e17798b06acb3ce8d2b2a26df Mon Sep 17 00:00:00 2001 From: Daniel Hanggi Date: Fri, 24 Aug 2018 04:14:34 -0500 Subject: [PATCH 1365/1790] [markdown mode] Add HTML comment support HTML comments are generally permissible in Markdown, per https://spec.commonmark.org/0.28/. --- mode/markdown/markdown.js | 2 ++ 1 file changed, 2 insertions(+) diff --git a/mode/markdown/markdown.js b/mode/markdown/markdown.js index 8f9e747d..8154cd68 100644 --- a/mode/markdown/markdown.js +++ b/mode/markdown/markdown.js @@ -869,6 +869,8 @@ CodeMirror.defineMode("markdown", function(cmCfg, modeCfg) { getType: getType, + blockCommentStart: "", closeBrackets: "()[]{}''\"\"``", fold: "markdown" }; From 563f78fa55e202f5c2ee024f6bb9763a99266b2f Mon Sep 17 00:00:00 2001 From: Abdussalam Abdurrahman Date: Fri, 24 Aug 2018 23:20:21 -0700 Subject: [PATCH 1366/1790] [clojure more] Update built-ins with public symbols in `clojure.core` The public symbols are retrieved with: (->> 'clojure.core (ns-publics) (keys) (sort) (clojure.string/join " ")) --- mode/clojure/clojure.js | 58 ++++++++++++++++++++--------------------- 1 file changed, 28 insertions(+), 30 deletions(-) diff --git a/mode/clojure/clojure.js b/mode/clojure/clojure.js index 2015edff..c22ec3a1 100644 --- a/mode/clojure/clojure.js +++ b/mode/clojure/clojure.js @@ -39,56 +39,54 @@ CodeMirror.defineMode("clojure", function (options) { 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 " + + "*compile-path* *compiler-options* *data-readers* *default-data-reader-fn* *e *err* *file* *flush-on-newline* *fn-loader* *in* " + + "*math-context* *ns* *out* *print-dup* *print-length* *print-level* *print-meta* *print-namespace-maps* *print-readably* *read-eval* *reader-resolver* " + + "*source-path* *suppress-read* *unchecked-math* *use-context-classloader* *verbose-defrecords* *warn-on-reflection* + +' - -' -> " + + "->> ->ArrayChunk ->Eduction ->Vec ->VecNode ->VecSeq -cache-protocol-fn -reset-methods .. / < <= = == > >= EMPTY-NODE Inst StackTraceElement->vec Throwable->map 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 " + + "alter-var-root amap ancestors and any? apply areduce array-map as-> 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 cat cast char char-array char-escape-string char-name-string char? chars chunk chunk-append " + + "bit-shift-left bit-shift-right bit-test bit-xor boolean boolean-array boolean? booleans bound-fn bound-fn* bound? bounded-count butlast " + + "byte byte-array bytes bytes? case cast cat 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 completing concat cond condp " + + "clojure-version coll? comment commute comp comparator compare compare-and-set! compile complement completing concat cond cond-> cond->> condp " + "conj conj! cons constantly construct-proxy contains? count counted? create-ns create-struct cycle dec dec' decimal? " + "declare dedupe 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 eduction empty empty? ensure enumeration-seq error-handler error-mode eval even? every-pred every? ex-data ex-info " + + "dissoc! distinct distinct? doall dorun doseq dosync dotimes doto double double-array double? doubles drop drop-last " + + "drop-while eduction empty empty? ensure ensure-reduced 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 " + + "gen-class gen-interface gensym get get-in get-method get-proxy-class get-thread-bindings get-validator group-by halt-when hash " + + "hash-combine hash-map hash-ordered-coll hash-set hash-unordered-coll ident? identical? identity if-let if-not if-some ifn? import in-ns inc inc' indexed? init-proxy inst-ms inst-ms* inst? instance? " + + "int int-array int? 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 " + + "make-hierarchy map map-entry? map-indexed map? mapcat mapv max max-key memfn memoize merge merge-with meta method-sig methods " + + "min min-key mix-collection-hash mod munge name namespace namespace-munge nat-int? neg-int? 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 " + + "partition-by pcalls peek persistent! pmap pop pop! pop-thread-bindings pos-int? 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 " + + "prn prn-str promise proxy proxy-call-with-super proxy-mappings proxy-name proxy-super push-thread-bindings pvalues qualified-ident? qualified-keyword? qualified-symbol? " + "quot rand rand-int rand-nth random-sample 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 " + + "re-seq read read-line read-string reader-conditional reader-conditional? realized? record? reduce reduce-kv reduced reduced? 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 transduce " + + "remove-method remove-ns remove-watch repeat repeatedly replace replicate require reset! reset-meta! reset-vals! resolve rest " + + "restart-agent resultset-seq reverse reversible? rseq rsubseq run! satisfies? second select-keys send send-off send-via seq seq? seqable? " + + "seque sequence sequential? set set-agent-send-executor! set-agent-send-off-executor! set-error-handler! set-error-mode! set-validator! set? short short-array shorts " + + "shuffle shutdown-agents simple-ident? simple-keyword? simple-symbol? slurp some some-> some->> some-fn some? 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! swap-vals! symbol " + + "symbol? sync tagged-literal tagged-literal? take take-last take-nth take-while test the-ns thread-bound? time to-array to-array-2d trampoline transduce " + "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-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 update-in update-proxy use val vals var-get var-set var? vary-meta vec vector vector-of " + - "vector? volatile! volatile? vreset! vswap! 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->>"); + "unquote-splicing unreduced unsigned-bit-shift-right update update-in update-proxy uri? use uuid? val vals var-get var-set var? vary-meta vec vector vector-of " + + "vector? volatile! volatile? vreset! vswap! when when-first when-let when-not when-some 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 b6d929104d1e3b95fe9b61faa1269b58d6f3cb0a Mon Sep 17 00:00:00 2001 From: Marijn Haverbeke Date: Sat, 25 Aug 2018 08:29:14 +0200 Subject: [PATCH 1367/1790] Mark version 5.40.0 --- AUTHORS | 3 +++ CHANGELOG.md | 16 ++++++++++++++++ doc/manual.html | 2 +- doc/releases.html | 10 ++++++++++ index.html | 2 +- package.json | 2 +- src/edit/main.js | 2 +- 7 files changed, 33 insertions(+), 4 deletions(-) diff --git a/AUTHORS b/AUTHORS index 4e91edfc..b7407343 100644 --- a/AUTHORS +++ b/AUTHORS @@ -384,6 +384,7 @@ Kevin Kwok Kevin Muret Kevin Sawicki Kevin Ushey +Kier Darby Klaus Silveira Koh Zi Han, Cliff komakino @@ -491,6 +492,7 @@ Michal Kapiczynski Mighty Guava Miguel Castillo mihailik +Mika Andrianarijaona Mike Mike Brevoort Mike Diaz @@ -534,6 +536,7 @@ Niels van Groningen nightwing Nikita Beloglazov Nikita Vasilyev +Nikolaj Kappler Nikolay Kostov nilp0inter Nisarg Jhaveri diff --git a/CHANGELOG.md b/CHANGELOG.md index cb561405..0bb3861d 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -1,3 +1,19 @@ +## 5.40.0 (2018-08-25) + +### Bug fixes + +[closebrackets addon](https://codemirror.net/doc/manual.html#addon_closebrackets): Fix issue where bracket-closing wouldn't work before punctuation. + +[panel addon](https://codemirror.net/doc/manual.html#addon_panel): Fix problem where replacing the last remaining panel dropped the newly added panel. + +[hardwrap addon](https://codemirror.net/doc/manual.html#addon_hardwrap): Fix an infinite loop when the indention is greater than the target column. + +[jinja2](https://codemirror.net/mode/jinja2/) and [markdown](https://codemirror.net/mode/markdown/) modes: Add comment metadata. + +### New features + +New method [`phrase`](https://codemirror.net/doc/manual.html#phrase) and option [`phrases`](https://codemirror.net/doc/manual.html#option_phrases) to make translating UI text in addons easier. + ## 5.39.2 (2018-07-20) ### Bug fixes diff --git a/doc/manual.html b/doc/manual.html index dabc564e..bb7b44dc 100644 --- a/doc/manual.html +++ b/doc/manual.html @@ -69,7 +69,7 @@

    User manual and reference guide - version 5.39.3 + version 5.40.0

    CodeMirror is a code-editor component that can be embedded in diff --git a/doc/releases.html b/doc/releases.html index 1eff6792..257532b9 100644 --- a/doc/releases.html +++ b/doc/releases.html @@ -30,6 +30,16 @@

    Release notes and version history

    Version 5.x

    +

    25-08-2018: Version 5.40.0:

    + +
      +
    • New method phrase and option phrases to make translating UI text in addons easier.
    • +
    • closebrackets addon: Fix issue where bracket-closing wouldn't work before punctuation.
    • +
    • panel addon: Fix problem where replacing the last remaining panel dropped the newly added panel.
    • +
    • hardwrap addon: Fix an infinite loop when the indention is greater than the target column.
    • +
    • jinja2 and markdown modes: Add comment metadata.
    • +
    +

    20-07-2018: Version 5.39.2:

      diff --git a/index.html b/index.html index 2b3cccea..908c9764 100644 --- a/index.html +++ b/index.html @@ -96,7 +96,7 @@

      This is CodeMirror

      - Get the current version: 5.39.2.
      + Get the current version: 5.40.0.
      You can see the code,
      read the release notes,
      or study the user manual. diff --git a/package.json b/package.json index d6adbd8b..965a8545 100644 --- a/package.json +++ b/package.json @@ -1,6 +1,6 @@ { "name": "codemirror", - "version": "5.39.3", + "version": "5.40.0", "main": "lib/codemirror.js", "style": "lib/codemirror.css", "description": "Full-featured in-browser code editor", diff --git a/src/edit/main.js b/src/edit/main.js index 9bde1c61..283c9295 100644 --- a/src/edit/main.js +++ b/src/edit/main.js @@ -66,4 +66,4 @@ import { addLegacyProps } from "./legacy.js" addLegacyProps(CodeMirror) -CodeMirror.version = "5.39.3" +CodeMirror.version = "5.40.0" From 372ff6147f3f60e12509d9264a9c7ce09410c77c Mon Sep 17 00:00:00 2001 From: Marijn Haverbeke Date: Sat, 25 Aug 2018 08:32:22 +0200 Subject: [PATCH 1368/1790] Bump version number post-5.40.0 --- doc/manual.html | 2 +- package.json | 2 +- src/edit/main.js | 2 +- 3 files changed, 3 insertions(+), 3 deletions(-) diff --git a/doc/manual.html b/doc/manual.html index bb7b44dc..e0aad802 100644 --- a/doc/manual.html +++ b/doc/manual.html @@ -69,7 +69,7 @@

      User manual and reference guide - version 5.40.0 + version 5.40.1

      CodeMirror is a code-editor component that can be embedded in diff --git a/package.json b/package.json index 965a8545..77e7261a 100644 --- a/package.json +++ b/package.json @@ -1,6 +1,6 @@ { "name": "codemirror", - "version": "5.40.0", + "version": "5.40.1", "main": "lib/codemirror.js", "style": "lib/codemirror.css", "description": "Full-featured in-browser code editor", diff --git a/src/edit/main.js b/src/edit/main.js index 283c9295..c9aea75b 100644 --- a/src/edit/main.js +++ b/src/edit/main.js @@ -66,4 +66,4 @@ import { addLegacyProps } from "./legacy.js" addLegacyProps(CodeMirror) -CodeMirror.version = "5.40.0" +CodeMirror.version = "5.40.1" From a79defe2c4fc956768110990e8515f1afa317261 Mon Sep 17 00:00:00 2001 From: Baptiste Augrain Date: Mon, 27 Aug 2018 09:25:09 +0200 Subject: [PATCH 1369/1790] [markdown mode] `:---:` is not an emoji --- mode/markdown/markdown.js | 2 +- mode/markdown/test.js | 9 +++++++++ 2 files changed, 10 insertions(+), 1 deletion(-) diff --git a/mode/markdown/markdown.js b/mode/markdown/markdown.js index 8154cd68..df718366 100644 --- a/mode/markdown/markdown.js +++ b/mode/markdown/markdown.js @@ -620,7 +620,7 @@ CodeMirror.defineMode("markdown", function(cmCfg, modeCfg) { } } - if (modeCfg.emoji && ch === ":" && stream.match(/^[a-z_\d+-]+:/)) { + if (modeCfg.emoji && ch === ":" && stream.match(/^(?:[a-z_\d+][a-z_\d+-]*|\-[a-z_\d+][a-z_\d+-]*):/)) { state.emoji = true; if (modeCfg.highlightFormatting) state.formatting = "emoji"; var retType = getType(state); diff --git a/mode/markdown/test.js b/mode/markdown/test.js index 49604e0d..aa1263e1 100644 --- a/mode/markdown/test.js +++ b/mode/markdown/test.js @@ -45,6 +45,8 @@ "formatting" : "override-formatting" }}); function FormatTokenTypeOverrideTest(name) { test.mode(name, modeFormattingOverride, Array.prototype.slice.call(arguments, 1)); } + var modeET = CodeMirror.getMode(config, {name: "markdown", emoji: true}); + function ET(name) { test.mode(name, modeET, Array.prototype.slice.call(arguments, 1)); } FT("formatting_emAsterisk", @@ -1305,4 +1307,11 @@ MT_noXml("xmlHighlightDisabled", "

      foo
      "); + // Tests Emojis + + ET("emojiDefault", + "[builtin :foobar:]"); + + ET("emojiTable", + " :--:"); })(); From b43113c5af16ec19a0762e28fd18ac0263bf1f8e Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Bartosz=20Dziewo=C5=84ski?= Date: Wed, 29 Aug 2018 10:56:46 +0200 Subject: [PATCH 1370/1790] Prevent collapsing trailing spaces ("split spaces") on all browsers Ref #1362. Firefox now also collapses spaces at the end of the line (tested on 62.0). It should be harmless for any other browsers. --- src/line/line_data.js | 4 +++- 1 file changed, 3 insertions(+), 1 deletion(-) diff --git a/src/line/line_data.js b/src/line/line_data.js index 74acdaff..7829484c 100644 --- a/src/line/line_data.js +++ b/src/line/line_data.js @@ -68,7 +68,7 @@ export function buildLineContent(cm, lineView) { let builder = {pre: eltP("pre", [content], "CodeMirror-line"), content: content, col: 0, pos: 0, cm: cm, trailingSpace: false, - splitSpaces: (ie || webkit) && cm.getOption("lineWrapping")} + splitSpaces: cm.getOption("lineWrapping")} lineView.measure = {} // Iterate over the logical lines that make up this visual line. @@ -189,6 +189,8 @@ function buildToken(builder, text, style, startStyle, endStyle, title, css) { builder.content.appendChild(content) } +// Change some spaces to NBSP to prevent the browser from collapsing +// trailing spaces at the end of a line when rendering text (issue #1362). function splitSpaces(text, trailingBefore) { if (text.length > 1 && !/ /.test(text)) return text let spaceBefore = trailingBefore, result = "" From 74fca7c1d97fef3cc90a5bff2a2819603c7c0c26 Mon Sep 17 00:00:00 2001 From: Philipp Markovics Date: Wed, 29 Aug 2018 16:04:47 +0200 Subject: [PATCH 1371/1790] [clojure mode] Provide `hintWords` --- mode/clojure/clojure.js | 176 +++++++++++++++++++++++----------------- 1 file changed, 102 insertions(+), 74 deletions(-) diff --git a/mode/clojure/clojure.js b/mode/clojure/clojure.js index c22ec3a1..7c21a8b9 100644 --- a/mode/clojure/clojure.js +++ b/mode/clojure/clojure.js @@ -22,89 +22,117 @@ CodeMirror.defineMode("clojure", function (options) { var INDENT_WORD_SKIP = options.indentUnit || 2; var NORMAL_INDENT_UNIT = options.indentUnit || 2; - function makeKeywords(str) { - var obj = {}, words = str.split(" "); + function makeKeywords(words) { + var obj = {}; for (var i = 0; i < words.length; ++i) obj[words[i]] = true; return obj; } - 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"); - - var builtins = makeKeywords( - "* *' *1 *2 *3 *agent* *allow-unresolved-vars* *assert* *clojure-version* *command-line-args* *compile-files* " + - "*compile-path* *compiler-options* *data-readers* *default-data-reader-fn* *e *err* *file* *flush-on-newline* *fn-loader* *in* " + - "*math-context* *ns* *out* *print-dup* *print-length* *print-level* *print-meta* *print-namespace-maps* *print-readably* *read-eval* *reader-resolver* " + - "*source-path* *suppress-read* *unchecked-math* *use-context-classloader* *verbose-defrecords* *warn-on-reflection* + +' - -' -> " + - "->> ->ArrayChunk ->Eduction ->Vec ->VecNode ->VecSeq -cache-protocol-fn -reset-methods .. / < <= = == > >= EMPTY-NODE Inst StackTraceElement->vec Throwable->map 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 any? apply areduce array-map as-> 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 boolean? booleans bound-fn bound-fn* bound? bounded-count butlast " + - "byte byte-array bytes bytes? case cast cat 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 completing concat cond cond-> cond->> condp " + - "conj conj! cons constantly construct-proxy contains? count counted? create-ns create-struct cycle dec dec' decimal? " + - "declare dedupe 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 double? doubles drop drop-last " + - "drop-while eduction empty empty? ensure ensure-reduced 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 halt-when hash " + - "hash-combine hash-map hash-ordered-coll hash-set hash-unordered-coll ident? identical? identity if-let if-not if-some ifn? import in-ns inc inc' indexed? init-proxy inst-ms inst-ms* inst? instance? " + - "int int-array int? 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-entry? map-indexed map? mapcat mapv max max-key memfn memoize merge merge-with meta method-sig methods " + - "min min-key mix-collection-hash mod munge name namespace namespace-munge nat-int? neg-int? 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-int? 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 qualified-ident? qualified-keyword? qualified-symbol? " + - "quot rand rand-int rand-nth random-sample range ratio? rational? rationalize re-find re-groups re-matcher re-matches re-pattern " + - "re-seq read read-line read-string reader-conditional reader-conditional? realized? record? reduce reduce-kv reduced reduced? 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! reset-vals! resolve rest " + - "restart-agent resultset-seq reverse reversible? rseq rsubseq run! satisfies? second select-keys send send-off send-via seq seq? seqable? " + - "seque sequence sequential? set set-agent-send-executor! set-agent-send-off-executor! set-error-handler! set-error-mode! set-validator! set? short short-array shorts " + - "shuffle shutdown-agents simple-ident? simple-keyword? simple-symbol? slurp some some-> some->> some-fn some? 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! swap-vals! symbol " + - "symbol? sync tagged-literal tagged-literal? take take-last take-nth take-while test the-ns thread-bound? time to-array to-array-2d trampoline transduce " + - "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 unreduced unsigned-bit-shift-right update update-in update-proxy uri? use uuid? val vals var-get var-set var? vary-meta vec vector vector-of " + - "vector? volatile! volatile? vreset! vswap! when when-first when-let when-not when-some 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( + var commonAtoms = ["true", "false", "nil"]; + var commonKeywords = ["defn", "defn-", "def", "def-", "defonce", "defmulti", "defmethod", "defmacro", + "defstruct", "deftype", "defprotocol", "defrecord", "defproject", "deftest", "slice", "defalias", + "defhinted", "defmacro-", "defn-memo", "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 commonBuiltins = ["*", "*'", "*1", "*2", "*3", "*agent*", "*allow-unresolved-vars*", "*assert*", + "*clojure-version*", "*command-line-args*", "*compile-files*", "*compile-path*", "*compiler-options*", + "*data-readers*", "*default-data-reader-fn*", "*e", "*err*", "*file*", "*flush-on-newline*", "*fn-loader*", + "*in*", "*math-context*", "*ns*", "*out*", "*print-dup*", "*print-length*", "*print-level*", "*print-meta*", + "*print-namespace-maps*", "*print-readably*", "*read-eval*", "*reader-resolver*", "*source-path*", + "*suppress-read*", "*unchecked-math*", "*use-context-classloader*", "*verbose-defrecords*", + "*warn-on-reflection*'", "-", "-'", "->", "->>", "->ArrayChunk", "->Eduction", "->Vec", "->VecNode", + "->VecSeq", "-cache-protocol-fn", "-reset-methods", "..", "/", "<", "<=", "=", "==", ">", ">=", + "EMPTY-NODE", "Inst", "StackTraceElement->vec", "Throwable->map", "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", "any?", "apply", "areduce", "array-map", + "as->", "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", "boolean?", "booleans", "bound-fn", "bound-fn*", "bound?", + "bounded-count", "butlast", "byte", "byte-array", "bytes", "bytes?", "case", "cast", "cat", "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", "completing", "concat", "cond", "cond->", "cond->>", "condp", + "conj", "conj!", "cons", "constantly", "construct-proxy", "contains?", "count", "counted?", "create-ns", + "create-struct", "cycle", "dec", "dec'", "decimal?", "declare", "dedupe", "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", "double?", "doubles", "drop", "drop-last", "drop-while", + "eduction", "empty", "empty?", "ensure", "ensure-reduced", "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", "halt-when", "hash", + "hash-combine", "hash-map", "hash-ordered-coll", "hash-set", "hash-unordered-coll", "ident?", "identical?", + "identity", "if-let", "if-not", "if-some", "ifn?", "import", "in-ns", "inc", "inc'", "indexed?", "init-proxy", + "inst-ms", "inst-ms*", "inst?", "instance?", "int", "int-array", "int?", "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-entry?", "map-indexed", "map?", + "mapcat", "mapv", "max", "max-key", "memfn", "memoize", "merge", "merge-with", "meta", "method-sig", "methods", + "min", "min-key", "mix-collection-hash", "mod", "munge", "name", "namespace", "namespace-munge", "nat-int?", + "neg-int?", "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-int?", "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", "qualified-ident?", "qualified-keyword?", + "qualified-symbol?", "quot", "rand", "rand-int", "rand-nth", "random-sample", "range", "ratio?", "rational?", + "rationalize", "re-find", "re-groups", "re-matcher", "re-matches", "re-pattern", "re-seq", "read", "read-line", + "read-string", "reader-conditional", "reader-conditional?", "realized?", "record?", "reduce", "reduce-kv", "reduced", + "reduced?", "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!", "reset-vals!", + "resolve", "rest", "restart-agent", "resultset-seq", "reverse", "reversible?", "rseq", "rsubseq", "run!", "satisfies?", + "second", "select-keys", "send", "send-off", "send-via", "seq", "seq?", "seqable?", "seque", "sequence", "sequential?", + "set", "set-agent-send-executor!", "set-agent-send-off-executor!", "set-error-handler!", "set-error-mode!", + "set-validator!", "set?", "short", "short-array", "shorts", "shuffle", "shutdown-agents", "simple-ident?", + "simple-keyword?", "simple-symbol?", "slurp", "some", "some->", "some->>", "some-fn", "some?", "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!", "swap-vals!", + "symbol", "symbol?", "sync", "tagged-literal", "tagged-literal?", "take", "take-last", "take-nth", "take-while", "test", + "the-ns", "thread-bound?", "time", "to-array", "to-array-2d", "trampoline", "transduce", "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", "unreduced", "unsigned-bit-shift-right", "update", "update-in", + "update-proxy", "uri?", "use", "uuid?", "val", "vals", "var-get", "var-set", "var?", "vary-meta", "vec", "vector", + "vector-of", "vector?", "volatile!", "volatile?", "vreset!", "vswap!", "when", "when-first", "when-let", "when-not", + "when-some", "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 commonIndentKeys = [ // Built-ins - "ns fn def defn defmethod bound-fn if if-not case condp when while when-not when-first do future comment doto " + - "locking proxy with-open with-precision reify deftype defrecord defprotocol extend extend-protocol extend-type " + - "try catch " + - + "ns", "fn", "def", "defn", "defmethod", "bound-fn", "if", "if-not", "case", "condp", "when", "while", "when-not", "when-first", "do", "future", "comment", "doto", + "locking", "proxy", "with-open", "with-precision", "reify", "deftype", "defrecord", "defprotocol", "extend", "extend-protocol", "extend-type", + "try", "catch", // Binding forms - "let letfn binding loop for doseq dotimes when-let if-let " + - + "let", "letfn", "binding", "loop", "for", "doseq", "dotimes", "when-let", "if-let", // Data structures - "defstruct struct-map assoc " + - + "defstruct", "struct-map", "assoc", // clojure.test - "testing deftest " + - + "testing", "deftest", // contrib - "handler-case handle dotrace deftrace"); + "handler-case", "handle", "dotrace", "deftrace"]; + + CodeMirror.registerHelper("hintWords", "clojure", commonAtoms.concat(commonBuiltins)); + + var atoms = makeKeywords(commonAtoms); + var keywords = makeKeywords(commonKeywords); + var builtins = makeKeywords(commonBuiltins); + var indentKeys = makeKeywords(commonIndentKeys); var tests = { digit: /\d/, From 829a546f3e8d092f9b4afd83499d5b4c7d398d55 Mon Sep 17 00:00:00 2001 From: Philipp Markovics Date: Wed, 29 Aug 2018 12:01:26 +0200 Subject: [PATCH 1372/1790] [julia mode] Provide `hintWords` --- mode/julia/julia.js | 31 +++++++++++++++++++++---------- 1 file changed, 21 insertions(+), 10 deletions(-) diff --git a/mode/julia/julia.js b/mode/julia/julia.js index 35b75acd..3e3560dc 100644 --- a/mode/julia/julia.js +++ b/mode/julia/julia.js @@ -34,17 +34,28 @@ CodeMirror.defineMode("julia", function(config, parserConf) { /^[_A-Za-z\u00A1-\u2217\u2219-\uFFFF][\w\u00A1-\u2217\u2219-\uFFFF]*!*/; var chars = wordRegexp([octChar, hexChar, sChar, uChar], "'"); - var openers = wordRegexp(["begin", "function", "type", "struct", "immutable", + + var commonOpeners = ["begin", "function", "type", "struct", "immutable", "let", "macro", "for", "while", "quote", "if", "else", "elseif", "try", - "finally", "catch", "do"]); - var closers = wordRegexp(["end", "else", "elseif", "catch", "finally"]); - var keywords = wordRegexp(["if", "else", "elseif", "while", "for", "begin", - "let", "end", "do", "try", "catch", "finally", "return", "break", - "continue", "global", "local", "const", "export", "import", "importall", - "using", "function", "where", "macro", "module", "baremodule", "struct", - "type", "mutable", "immutable", "quote", "typealias", "abstract", - "primitive", "bitstype"]); - var builtins = wordRegexp(["true", "false", "nothing", "NaN", "Inf"]); + "finally", "catch", "do"]; + + var commonClosers = ["end", "else", "elseif", "catch", "finally"]; + + var commonKeywords = ["if", "else", "elseif", "while", "for", "begin", + "let", "end", "do", "try", "catch", "finally", "return", "break", + "continue", "global", "local", "const", "export", "import", "importall", + "using", "function", "where", "macro", "module", "baremodule", "struct", + "type", "mutable", "immutable", "quote", "typealias", "abstract", + "primitive", "bitstype"]; + + var commonBuiltins = ["true", "false", "nothing", "NaN", "Inf"]; + + CodeMirror.registerHelper("hintWords", "julia", commonKeywords.concat(commonBuiltins)); + + var openers = wordRegexp(commonOpeners); + var closers = wordRegexp(commonClosers); + var keywords = wordRegexp(commonKeywords); + var builtins = wordRegexp(commonBuiltins); var macro = /^@[_A-Za-z][\w]*/; var symbol = /^:[_A-Za-z\u00A1-\uFFFF][\w\u00A1-\uFFFF]*!*/; From 85e0a26aea7c515a324a94b63334fda552aed17f Mon Sep 17 00:00:00 2001 From: Philipp Markovics Date: Wed, 29 Aug 2018 14:50:38 +0200 Subject: [PATCH 1373/1790] [mllike mode] Provide `hintWords` --- mode/mllike/mllike.js | 3 +++ 1 file changed, 3 insertions(+) diff --git a/mode/mllike/mllike.js b/mode/mllike/mllike.js index 8ef3a7be..a1538f72 100644 --- a/mode/mllike/mllike.js +++ b/mode/mllike/mllike.js @@ -41,6 +41,9 @@ CodeMirror.defineMode('mllike', function(_config, parserConfig) { words[prop] = parserConfig.extraWords[prop]; } } + var hintWords = []; + for (var k in words) { hintWords.push(k); } + CodeMirror.registerHelper("hintWords", "mllike", hintWords); function tokenBase(stream, state) { var ch = stream.next(); From 41c533f553f10bc961bc8fcf9094971528830f13 Mon Sep 17 00:00:00 2001 From: Philipp Markovics Date: Wed, 29 Aug 2018 14:30:51 +0200 Subject: [PATCH 1374/1790] [shell mode] Provide `hintWords` --- mode/shell/shell.js | 35 ++++++++++++++++++----------------- 1 file changed, 18 insertions(+), 17 deletions(-) diff --git a/mode/shell/shell.js b/mode/shell/shell.js index 0e667e6a..5af12413 100644 --- a/mode/shell/shell.js +++ b/mode/shell/shell.js @@ -14,26 +14,27 @@ CodeMirror.defineMode('shell', function() { var words = {}; - function define(style, string) { - var split = string.split(' '); - for(var i = 0; i < split.length; i++) { - words[split[i]] = style; + function define(style, dict) { + for(var i = 0; i < dict.length; i++) { + words[dict[i]] = style; } }; - // Atoms - define('atom', 'true false'); - - // Keywords - define('keyword', 'if then do else elif while until for in esac fi fin ' + - 'fil done exit set unset export function'); - - // Commands - define('builtin', 'ab awk bash beep cat cc cd chown chmod chroot clear cp ' + - 'curl cut diff echo find gawk gcc get git grep hg kill killall ln ls make ' + - 'mkdir openssl mv nc nl node npm ping ps restart rm rmdir sed service sh ' + - 'shopt shred source sort sleep ssh start stop su sudo svn tee telnet top ' + - 'touch vi vim wall wc wget who write yes zsh'); + var commonAtoms = ["true", "false"]; + var commonKeywords = ["if", "then", "do", "else", "elif", "while", "until", "for", "in", "esac", "fi", + "fin", "fil", "done", "exit", "set", "unset", "export", "function"]; + var commonCommands = ["ab", "awk", "bash", "beep", "cat", "cc", "cd", "chown", "chmod", "chroot", "clear", + "cp", "curl", "cut", "diff", "echo", "find", "gawk", "gcc", "get", "git", "grep", "hg", "kill", "killall", + "ln", "ls", "make", "mkdir", "openssl", "mv", "nc", "nl", "node", "npm", "ping", "ps", "restart", "rm", + "rmdir", "sed", "service", "sh", "shopt", "shred", "source", "sort", "sleep", "ssh", "start", "stop", + "su", "sudo", "svn", "tee", "telnet", "top", "touch", "vi", "vim", "wall", "wc", "wget", "who", "write", + "yes", "zsh"]; + + CodeMirror.registerHelper("hintWords", "shell", commonAtoms.concat(commonKeywords, commonCommands)); + + define('atom', commonAtoms); + define('keyword', commonKeywords); + define('builtin', commonCommands); function tokenBase(stream, state) { if (stream.eatSpace()) return null; From 07200a15505670e7a31c2c69eed660640a06fd80 Mon Sep 17 00:00:00 2001 From: Philipp Markovics Date: Wed, 29 Aug 2018 12:15:43 +0200 Subject: [PATCH 1375/1790] [r mode] Provide `hintWords` --- mode/r/r.js | 19 +++++++++++++------ 1 file changed, 13 insertions(+), 6 deletions(-) diff --git a/mode/r/r.js b/mode/r/r.js index 18ce3bad..df582400 100644 --- a/mode/r/r.js +++ b/mode/r/r.js @@ -14,15 +14,22 @@ CodeMirror.registerHelper("wordChars", "r", /[\w.]/); CodeMirror.defineMode("r", function(config) { - function wordObj(str) { - var words = str.split(" "), res = {}; + function wordObj(words) { + var res = {}; for (var i = 0; i < words.length; ++i) res[words[i]] = true; return res; } - var atoms = wordObj("NULL NA Inf NaN NA_integer_ NA_real_ NA_complex_ NA_character_"); - var builtins = wordObj("list quote bquote eval return call parse deparse"); - var keywords = wordObj("if else repeat while function for in next break"); - var blockkeywords = wordObj("if else repeat while function for"); + var commonAtoms = ["NULL", "NA", "Inf", "NaN", "NA_integer_", "NA_real_", "NA_complex_", "NA_character_"]; + var commonBuiltins = ["list", "quote", "bquote", "eval", "return", "call", "parse", "deparse"]; + var commonKeywords = ["if", "else", "repeat", "while", "function", "for", "in", "next", "break"]; + var commonBlockKeywords = ["if", "else", "repeat", "while", "function", "for"]; + + CodeMirror.registerHelper("hintWords", "r", commonAtoms.concat(commonBuiltins, commonKeywords)); + + var atoms = wordObj(commonAtoms); + var builtins = wordObj(commonBuiltins); + var keywords = wordObj(commonKeywords); + var blockkeywords = wordObj(commonBlockKeywords); var opChars = /[+\-*\/^<>=!&|~$:]/; var curPunc; From ec2e1a13984c7b28cb9d63d1a9512b8782a210fd Mon Sep 17 00:00:00 2001 From: mtaran-google Date: Wed, 29 Aug 2018 10:53:19 -0700 Subject: [PATCH 1376/1790] [yaml] enable fold by indentation --- mode/yaml/yaml.js | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-) diff --git a/mode/yaml/yaml.js b/mode/yaml/yaml.js index b10e2635..a29d7ea4 100644 --- a/mode/yaml/yaml.js +++ b/mode/yaml/yaml.js @@ -109,7 +109,8 @@ CodeMirror.defineMode("yaml", function() { escaped: false }; }, - lineComment: "#" + lineComment: "#", + fold: "indent" }; }); From b756744a5f888f6d24ee0f6f563646972423539a Mon Sep 17 00:00:00 2001 From: Marijn Haverbeke Date: Thu, 30 Aug 2018 08:20:24 +0200 Subject: [PATCH 1377/1790] Add a banner pointing at the version 6 announcement --- index.html | 6 ++++++ 1 file changed, 6 insertions(+) diff --git a/index.html b/index.html index 908c9764..96c6bbb0 100644 --- a/index.html +++ b/index.html @@ -18,8 +18,14 @@ .CodeMirror { height: auto; border: 1px solid #ddd; } .CodeMirror-scroll { max-height: 200px; } .CodeMirror pre { padding-left: 7px; line-height: 1.25; } + .banner { background: #ffc; padding: 6px; border-bottom: 2px solid silver; } + .banner div { margin: 0 auto; max-width: 700px; text-align: center; } + +
    • Features From 5c93a0102af99bb743aca247ff72cb8f164704ba Mon Sep 17 00:00:00 2001 From: laobubu Date: Thu, 25 Oct 2018 17:55:01 +0800 Subject: [PATCH 1443/1790] [markdown mode] Support Unicode Punctuation Characters MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Fix: `**LOREM(xxx)**:Fullwidth Colon` in [CodeMirror Markdown Demo](https://codemirror.net/mode/markdown/) results wrong style: ![](https://i.loli.net/2018/10/25/5bd1642fb2e01.png) CommonMark Demo: https://spec.commonmark.org/dingus/?text=**LOREM(xxx)**%EF%BC%9AFullwidth%20Colon See: https://spec.commonmark.org/0.28/#punctuation-character --- mode/markdown/markdown.js | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/mode/markdown/markdown.js b/mode/markdown/markdown.js index df718366..7aa3a3e1 100644 --- a/mode/markdown/markdown.js +++ b/mode/markdown/markdown.js @@ -91,7 +91,7 @@ CodeMirror.defineMode("markdown", function(cmCfg, modeCfg) { , textRE = /^[^#!\[\]*_\\<>` "'(~:]+/ , fencedCodeRE = /^(~~~+|```+)[ \t]*([\w+#-]*)[^\n`]*$/ , linkDefRE = /^\s*\[[^\]]+?\]:.*$/ // naive link-definition - , punctuation = /[!\"#$%&\'()*+,\-\.\/:;<=>?@\[\\\]^_`{|}~—]/ + , punctuation = /[!"#$%&'()*+,\-.\/:;<=>?@\[\\\]^_`{|}~\xA1\xA7\xAB\xB6\xB7\xBB\xBF\u037E\u0387\u055A-\u055F\u0589\u058A\u05BE\u05C0\u05C3\u05C6\u05F3\u05F4\u0609\u060A\u060C\u060D\u061B\u061E\u061F\u066A-\u066D\u06D4\u0700-\u070D\u07F7-\u07F9\u0830-\u083E\u085E\u0964\u0965\u0970\u0AF0\u0DF4\u0E4F\u0E5A\u0E5B\u0F04-\u0F12\u0F14\u0F3A-\u0F3D\u0F85\u0FD0-\u0FD4\u0FD9\u0FDA\u104A-\u104F\u10FB\u1360-\u1368\u1400\u166D\u166E\u169B\u169C\u16EB-\u16ED\u1735\u1736\u17D4-\u17D6\u17D8-\u17DA\u1800-\u180A\u1944\u1945\u1A1E\u1A1F\u1AA0-\u1AA6\u1AA8-\u1AAD\u1B5A-\u1B60\u1BFC-\u1BFF\u1C3B-\u1C3F\u1C7E\u1C7F\u1CC0-\u1CC7\u1CD3\u2010-\u2027\u2030-\u2043\u2045-\u2051\u2053-\u205E\u207D\u207E\u208D\u208E\u2308-\u230B\u2329\u232A\u2768-\u2775\u27C5\u27C6\u27E6-\u27EF\u2983-\u2998\u29D8-\u29DB\u29FC\u29FD\u2CF9-\u2CFC\u2CFE\u2CFF\u2D70\u2E00-\u2E2E\u2E30-\u2E42\u3001-\u3003\u3008-\u3011\u3014-\u301F\u3030\u303D\u30A0\u30FB\uA4FE\uA4FF\uA60D-\uA60F\uA673\uA67E\uA6F2-\uA6F7\uA874-\uA877\uA8CE\uA8CF\uA8F8-\uA8FA\uA8FC\uA92E\uA92F\uA95F\uA9C1-\uA9CD\uA9DE\uA9DF\uAA5C-\uAA5F\uAADE\uAADF\uAAF0\uAAF1\uABEB\uFD3E\uFD3F\uFE10-\uFE19\uFE30-\uFE52\uFE54-\uFE61\uFE63\uFE68\uFE6A\uFE6B\uFF01-\uFF03\uFF05-\uFF0A\uFF0C-\uFF0F\uFF1A\uFF1B\uFF1F\uFF20\uFF3B-\uFF3D\uFF3F\uFF5B\uFF5D\uFF5F-\uFF65]|\uD800[\uDD00-\uDD02\uDF9F\uDFD0]|\uD801\uDD6F|\uD802[\uDC57\uDD1F\uDD3F\uDE50-\uDE58\uDE7F\uDEF0-\uDEF6\uDF39-\uDF3F\uDF99-\uDF9C]|\uD804[\uDC47-\uDC4D\uDCBB\uDCBC\uDCBE-\uDCC1\uDD40-\uDD43\uDD74\uDD75\uDDC5-\uDDC9\uDDCD\uDDDB\uDDDD-\uDDDF\uDE38-\uDE3D\uDEA9]|\uD805[\uDCC6\uDDC1-\uDDD7\uDE41-\uDE43\uDF3C-\uDF3E]|\uD809[\uDC70-\uDC74]|\uD81A[\uDE6E\uDE6F\uDEF5\uDF37-\uDF3B\uDF44]|\uD82F\uDC9F|\uD836[\uDE87-\uDE8B]/ , expandedTab = " " // CommonMark specifies tab as 4 spaces function switchInline(stream, state, f) { From 113dc3a389882bfee167b7b1ab35eeb6fcb30072 Mon Sep 17 00:00:00 2001 From: Marijn Haverbeke Date: Thu, 25 Oct 2018 12:05:38 +0200 Subject: [PATCH 1444/1790] Revert "Update colour contrast of commented code" This reverts commit fcac0fb69bce4d82a1f9631e950ecb1d7f970de4. --- lib/codemirror.css | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/lib/codemirror.css b/lib/codemirror.css index 9e231687..c7a8ae70 100644 --- a/lib/codemirror.css +++ b/lib/codemirror.css @@ -126,7 +126,7 @@ .cm-s-default .cm-operator {} .cm-s-default .cm-variable-2 {color: #05a;} .cm-s-default .cm-variable-3, .cm-s-default .cm-type {color: #085;} -.cm-s-default .cm-comment {color: #6b7884;} +.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;} From 8900d1b7bce09c7101be631d9afa16a74c6e4598 Mon Sep 17 00:00:00 2001 From: Marijn Haverbeke Date: Thu, 25 Oct 2018 12:14:57 +0200 Subject: [PATCH 1445/1790] Mark version 5.41.0 --- AUTHORS | 8 ++++++++ CHANGELOG.md | 24 ++++++++++++++++++++++++ doc/manual.html | 2 +- doc/releases.html | 14 ++++++++++++++ index.html | 2 +- package.json | 2 +- src/edit/main.js | 2 +- 7 files changed, 50 insertions(+), 4 deletions(-) diff --git a/AUTHORS b/AUTHORS index 8e9a2aa1..a929165b 100644 --- a/AUTHORS +++ b/AUTHORS @@ -6,6 +6,7 @@ Aaron Brooks Abdelouahab Abdussalam Abdurrahman Abe Fettig +Abhishek Gahlot Adam Ahmed Adam King Adam Particka @@ -157,6 +158,7 @@ coderaiser Cole R Lawrence ComFreek Cristian Prieto +Curran Kelleher Curtis Gagliardi dagsta daines @@ -195,10 +197,12 @@ Devon Carew Dick Choi dignifiedquire Dimage Sapelkin +dmaclach Dmitry Kiselyov domagoj412 Dominator008 Domizio Demichelis +Doug Blank Doug Wikle Drew Bratcher Drew Hintz @@ -272,6 +276,7 @@ guraga Gustavo Rodrigues Hakan Tunc Hans Engel +Harald Schilly Hardest Harshvardhan Gupta Hasan Karahan @@ -467,6 +472,7 @@ Matthew Bauer Matthew Beale matthewhayes Matthew Rathbone +Matthew Suozzo Matthias Bussonnier Matthias BUSSONNIER Matt MacPherson @@ -501,6 +507,7 @@ Miguel Castillo mihailik Mika Andrianarijaona Mike +Mike Bostock Mike Brevoort Mike Diaz Mike Ivanov @@ -645,6 +652,7 @@ Scott Aikin Scott Goodhew Sebastian Wilzbach Sebastian Zaha +Seren D Sergey Goder Sergey Tselovalnikov Se-Won Kim diff --git a/CHANGELOG.md b/CHANGELOG.md index 715b9fa8..4823d22f 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -1,3 +1,27 @@ +## 5.41.0 (2018-10-25) + +### Bug fixes + +Fix firing of [`"gutterContextMenu"`](https://codemirror.net/doc/manual.html#event_gutterContextMenu) event on Firefox. + +Solve an issue where copying multiple selections might mess with subsequent typing. + +Don't crash when [`endOperation`](https://codemirror.net/doc/manual.html#endOperation) is called with no operation active. + +[vim bindings](https://codemirror.net/demo/vim.html): Fix insert mode repeat after visualBlock edits. + +[scheme mode](https://codemirror.net/mode/scheme/index.html): Improve highlighting of quoted expressions. + +[soy mode](https://codemirror.net/mode/soy/): Support injected data and `@param` in comments. + +[objective c mode](https://codemirror.net/mode/clike/): Improve conformance to the actual language. + +### New features + +A new [`selectionsMayTouch`](https://codemirror.net/doc/manual.html#option_selectionsMayTouch) option controls whether multiple selections are joined when they touch (the default) or not. + +[vim bindings](https://codemirror.net/demo/vim.html): Add `noremap` binding command. + ## 5.40.2 (2018-09-20) ### Bug fixes diff --git a/doc/manual.html b/doc/manual.html index 3d72a65f..87532a4b 100644 --- a/doc/manual.html +++ b/doc/manual.html @@ -69,7 +69,7 @@

      User manual and reference guide - version 5.40.3 + version 5.41.0

      CodeMirror is a code-editor component that can be embedded in diff --git a/doc/releases.html b/doc/releases.html index 29c50beb..401eca48 100644 --- a/doc/releases.html +++ b/doc/releases.html @@ -30,6 +30,20 @@

      Release notes and version history

      Version 5.x

      +

      25-10-2018: Version 5.41.0:

      + +
        +
      • A new selectionsMayTouch option controls whether multiple selections are joined when they touch (the default) or not.
      • +
      • vim bindings: Add noremap binding command.
      • +
      • Fix firing of "gutterContextMenu" event on Firefox.
      • +
      • Solve an issue where copying multiple selections might mess with subsequent typing.
      • +
      • Don't crash when endOperation is called with no operation active.
      • +
      • vim bindings: Fix insert mode repeat after visualBlock edits.
      • +
      • scheme mode: Improve highlighting of quoted expressions.
      • +
      • soy mode: Support injected data and @param in comments.
      • +
      • objective c mode: Improve conformance to the actual language.
      • +
      +

      20-09-2018: Version 5.40.2:

        diff --git a/index.html b/index.html index 4fa44620..4524b907 100644 --- a/index.html +++ b/index.html @@ -99,7 +99,7 @@

        This is CodeMirror

        - Get the current version: 5.40.2.
        + Get the current version: 5.41.0.
        You can see the code,
        read the release notes,
        or study the user manual. diff --git a/package.json b/package.json index 073dccfe..70f74de7 100644 --- a/package.json +++ b/package.json @@ -1,6 +1,6 @@ { "name": "codemirror", - "version": "5.40.3", + "version": "5.41.0", "main": "lib/codemirror.js", "style": "lib/codemirror.css", "author": { diff --git a/src/edit/main.js b/src/edit/main.js index 6a4176cb..575b548d 100644 --- a/src/edit/main.js +++ b/src/edit/main.js @@ -66,4 +66,4 @@ import { addLegacyProps } from "./legacy.js" addLegacyProps(CodeMirror) -CodeMirror.version = "5.40.3" +CodeMirror.version = "5.41.0" From 3d3bf0e6321ec845bf342f35a1c4a60a44d5fa7a Mon Sep 17 00:00:00 2001 From: Marijn Haverbeke Date: Thu, 25 Oct 2018 12:17:26 +0200 Subject: [PATCH 1446/1790] Bump version number post-5.41.0 --- doc/manual.html | 2 +- package.json | 2 +- src/edit/main.js | 2 +- 3 files changed, 3 insertions(+), 3 deletions(-) diff --git a/doc/manual.html b/doc/manual.html index 87532a4b..42592de6 100644 --- a/doc/manual.html +++ b/doc/manual.html @@ -69,7 +69,7 @@

        User manual and reference guide - version 5.41.0 + version 5.41.1

        CodeMirror is a code-editor component that can be embedded in diff --git a/package.json b/package.json index 70f74de7..6aab74b6 100644 --- a/package.json +++ b/package.json @@ -1,6 +1,6 @@ { "name": "codemirror", - "version": "5.41.0", + "version": "5.41.1", "main": "lib/codemirror.js", "style": "lib/codemirror.css", "author": { diff --git a/src/edit/main.js b/src/edit/main.js index 575b548d..c1beccac 100644 --- a/src/edit/main.js +++ b/src/edit/main.js @@ -66,4 +66,4 @@ import { addLegacyProps } from "./legacy.js" addLegacyProps(CodeMirror) -CodeMirror.version = "5.41.0" +CodeMirror.version = "5.41.1" From a07a0c649da40afb2641759b77c90ce3e228c9ec Mon Sep 17 00:00:00 2001 From: Marijn Haverbeke Date: Fri, 26 Oct 2018 10:02:48 +0200 Subject: [PATCH 1447/1790] Use new 'cache: npm' travis feature --- .travis.yml | 1 + 1 file changed, 1 insertion(+) diff --git a/.travis.yml b/.travis.yml index 52b8b815..1dfeb819 100644 --- a/.travis.yml +++ b/.travis.yml @@ -2,3 +2,4 @@ language: node_js node_js: - stable sudo: false +cache: npm From ea298af3827fc1845a3114bbfbb5f481a3aafa9f Mon Sep 17 00:00:00 2001 From: Prendota Date: Fri, 26 Oct 2018 12:16:16 +0300 Subject: [PATCH 1448/1790] [kotlin mode] update indents Fix indent after `operator` on top level. Example 1: import kotlinx.coroutines.* import kotlinx.coroutines.* Should have the same indents. --- mode/clike/clike.js | 3 +++ 1 file changed, 3 insertions(+) diff --git a/mode/clike/clike.js b/mode/clike/clike.js index 77032ea5..5a30ba0a 100644 --- a/mode/clike/clike.js +++ b/mode/clike/clike.js @@ -667,6 +667,9 @@ CodeMirror.defineMode("clike", function(config, parserConfig) { stream.eatWhile(/[\w\$_]/); return "meta"; }, + '*': function(_stream, state) { + return state.prevToken == '.' ? 'variable' : 'operator'; + }, '"': function(stream, state) { state.tokenize = tokenKotlinString(stream.match('""')); return state.tokenize(stream, state); From 29e338b8c1f27ade4502839877df7afd49584479 Mon Sep 17 00:00:00 2001 From: Dave MacLachlan Date: Sat, 27 Oct 2018 13:56:42 -0700 Subject: [PATCH 1449/1790] [clike] Update C++ keywords to C++20 --- mode/clike/clike.js | 16 +++++++++------- 1 file changed, 9 insertions(+), 7 deletions(-) diff --git a/mode/clike/clike.js b/mode/clike/clike.js index 5a30ba0a..924a0fc0 100644 --- a/mode/clike/clike.js +++ b/mode/clike/clike.js @@ -420,16 +420,18 @@ CodeMirror.defineMode("clike", function(config, parserConfig) { def(["text/x-c++src", "text/x-c++hdr"], { name: "clike", - keywords: words(cKeywords + " dynamic_cast namespace reinterpret_cast try explicit new " + - "static_cast typeid catch operator template typename class friend private " + - "this using const_cast public throw virtual delete mutable protected " + - "alignas alignof constexpr decltype nullptr noexcept thread_local final " + - "static_assert override"), + // Keywords from https://en.cppreference.com/w/cpp/keyword includes C++20. + keywords: words(cKeywords + "alignas alignof and and_eq audit axiom bitand bitor catch " + + "class compl concept constexpr const_cast decltype delete dynamic_cast " + + "explicit export final friend import module mutable namespace new noexcept " + + "not not_eq operator or or_eq override private protected public " + + "reinterpret_cast requires static_assert static_cast template this " + + "thread_local throw try typeid typename using virtual xor xor_eq"), types: cTypes, - blockKeywords: words(cBlockKeywords +" class try catch finally"), + blockKeywords: words(cBlockKeywords + " class try catch"), defKeywords: words(cDefKeywords + " class namespace"), typeFirstDefinitions: true, - atoms: words("true false NULL"), + atoms: words("true false NULL nullptr"), dontIndentStatements: /^template$/, isIdentifierChar: /[\w\$_~\xa1-\uffff]/, isReservedIdentifier: cIsReservedIdentifier, From 59a2b03bd5cca17166b0323551ed411fc10d7561 Mon Sep 17 00:00:00 2001 From: Oleksandr Yakovenko Date: Mon, 5 Nov 2018 22:17:12 +0100 Subject: [PATCH 1450/1790] Update darcula.css Adjust colors and font modifiers a little bit to make code more readable. --- theme/darcula.css | 51 ++++++++++++++++++++++++++--------------------- 1 file changed, 28 insertions(+), 23 deletions(-) diff --git a/theme/darcula.css b/theme/darcula.css index 66729e9d..d4a0b530 100644 --- a/theme/darcula.css +++ b/theme/darcula.css @@ -1,46 +1,51 @@ /** - Name: IntelliJ IDEA darcula theme + Name: IntelliJ IDEA darcula theme From IntelliJ IDEA by JetBrains */ +.cm-s-darcula { font-family: Consolas, Menlo, Monaco, 'Lucida Console', 'Liberation Mono', 'DejaVu Sans Mono', 'Bitstream Vera Sans Mono', 'Courier New', monospace, serif;} +.cm-s-darcula.CodeMirror { background: #2B2B2B; color: #A9B7C6; } + .cm-s-darcula span.cm-meta { color: #BBB529; } .cm-s-darcula span.cm-number { color: #6897BB; } -.cm-s-darcula span.cm-keyword { line-height: 1em; font-weight: bold; color: #CC7832; } -.cm-s-darcula span.cm-def { color: #FFC66D; } +.cm-s-darcula span.cm-keyword { color: #CC7832; line-height: 1em; font-weight: bold; } +.cm-s-darcula span.cm-def { color: #A9B7C6; font-style: italic; } .cm-s-darcula span.cm-variable { color: #A9B7C6; } .cm-s-darcula span.cm-variable-2 { color: #A9B7C6; } -.cm-s-darcula span.cm-variable-3, .cm-s-darcula span.cm-type { color: #A9B7C6; } -.cm-s-darcula span.cm-property { color: #A9B7C6; } +.cm-s-darcula span.cm-variable-3 { color: #9876AA; } +.cm-s-darcula span.cm-type { color: #AABBCC; font-weight: bold; } +.cm-s-darcula span.cm-property { color: #FFC66D; } .cm-s-darcula span.cm-operator { color: #A9B7C6; } .cm-s-darcula span.cm-string { color: #6A8759; } .cm-s-darcula span.cm-string-2 { color: #6A8759; } -.cm-s-darcula span.cm-comment { color: #808080; } -.cm-s-darcula span.cm-link { color: #287BDE; } -.cm-s-darcula span.cm-atom { font-weight: bold; color: #CC7832; } +.cm-s-darcula span.cm-comment { color: #61A151; font-style: italic; } +.cm-s-darcula span.cm-link { color: #CC7832; } +.cm-s-darcula span.cm-atom { color: #CC7832; } .cm-s-darcula span.cm-error { color: #BC3F3C; } -.cm-s-darcula span.cm-tag { color: #CC7832; } -.cm-s-darcula span.cm-attribute { color: #6A8759; } +.cm-s-darcula span.cm-tag { color: #629755; font-weight: bold; font-style: italic; text-decoration: underline; } +.cm-s-darcula span.cm-attribute { color: #6897bb; } .cm-s-darcula span.cm-qualifier { color: #6A8759; } .cm-s-darcula span.cm-bracket { color: #A9B7C6; } -.cm-s-darcula.CodeMirror { background: #2B2B2B; color: #A9B7C6; } - +.cm-s-darcula span.cm-builtin { color: #FF9E59; } +.cm-s-darcula span.cm-special { color: #FF9E59; } -.cm-s-darcula .CodeMirror-cursor { border-left: 1px solid #dddddd; } -.cm-s-darcula .CodeMirror-activeline-background { background: #3A3A3A; } -.cm-s-darcula div.CodeMirror-selected { background: #085a9c; } -.cm-s-darcula .CodeMirror-gutters { background: rgb(72, 72, 72); border-right: 1px solid grey; color: #606366 } +.cm-s-darcula .CodeMirror-cursor { border-left: 1px solid #A9B7C6; } +.cm-s-darcula .CodeMirror-activeline-background { background: #323232; } +.cm-s-darcula .CodeMirror-gutters { background: #313335; border-right: 1px solid #313335; } +.cm-s-darcula .CodeMirror-guttermarker { color: #FFEE80; } +.cm-s-darcula .CodeMirror-guttermarker-subtle { color: #D0D0D0; } +.cm-s-darcula .CodeMirrir-linenumber { color: #606366; } +.cm-s-darcula .CodeMirror-matchingbracket { background-color: #3B514D; color: #FFEF28 !important; font-weight: bold; } -.cm-s-darcula span.cm-builtin { color: #A9B7C6; } -.cm-s-darcula { font-family: Consolas, Menlo, Monaco, Lucida Console, Liberation Mono, DejaVu Sans Mono, Bitstream Vera Sans Mono, Courier New, monospace, serif;} -.cm-s-darcula .CodeMirror-matchingbracket { background-color: #3b514d; color: yellow !important; } +.cm-s-darcula div.CodeMirror-selected { background: #214283; } .CodeMirror-hints.darcula { font-family: Menlo, Monaco, Consolas, 'Courier New', monospace; - color: #9c9e9e; - background-color: #3b3e3f !important; + color: #9C9E9E; + background-color: #3B3E3F !important; } .CodeMirror-hints.darcula .CodeMirror-hint-active { - background-color: #494d4e !important; - color: #9c9e9e !important; + background-color: #494D4E !important; + color: #9C9E9E !important; } From 87702cf2eb53cabd25e4f994354ea9fc9e2c1d97 Mon Sep 17 00:00:00 2001 From: Janice Leung Date: Wed, 7 Nov 2018 22:42:59 -0800 Subject: [PATCH 1451/1790] issue 5530 --- keymap/vim.js | 5 +++++ test/vim_test.js | 7 +++++++ 2 files changed, 12 insertions(+) diff --git a/keymap/vim.js b/keymap/vim.js index a734026d..aed0957d 100644 --- a/keymap/vim.js +++ b/keymap/vim.js @@ -132,6 +132,7 @@ { keys: 'd', type: 'operator', operator: 'delete' }, { keys: 'y', type: 'operator', operator: 'yank' }, { keys: 'c', type: 'operator', operator: 'change' }, + { keys: '=', type: 'operator', operator: 'indentAuto' }, { keys: '>', type: 'operator', operator: 'indent', operatorArgs: { indentRight: true }}, { keys: '<', type: 'operator', operator: 'indent', operatorArgs: { indentRight: false }}, { keys: 'g~', type: 'operator', operator: 'changeCase' }, @@ -2188,6 +2189,10 @@ } return motions.moveToFirstNonWhiteSpaceCharacter(cm, ranges[0].anchor); }, + indentAuto: function(cm, _args, ranges) { + cm.execCommand("indentAuto"); + return motions.moveToFirstNonWhiteSpaceCharacter(cm, ranges[0].anchor); + }, changeCase: function(cm, args, ranges, oldAnchor, newHead) { var selections = cm.getSelections(); var swapped = []; diff --git a/test/vim_test.js b/test/vim_test.js index 0f0492ca..388d86b7 100644 --- a/test/vim_test.js +++ b/test/vim_test.js @@ -1222,6 +1222,13 @@ testVim('<<', function(cm, vim, helpers) { is(!register.linewise); helpers.assertCursorAt(0, 1); }, { value: ' word1\n word2\nword3 ', indentUnit: 2 }); +testVim('=', function(cm, vim, helpers) { + cm.setCursor(0, 3); + helpers.doKeys('', 'j', 'j'); + var expectedValue = 'word1\nword2\nword3'; + helpers.doKeys('='); + eq(expectedValue, cm.getValue()); +}, { value: ' word1\n word2\n word3', indentUnit: 2 }); // Edit tests function testEdit(name, before, pos, edit, after) { From b99149d9a9d06639859cc9fb90cefe878b7f9f89 Mon Sep 17 00:00:00 2001 From: Marijn Haverbeke Date: Fri, 9 Nov 2018 15:44:47 +0100 Subject: [PATCH 1452/1790] Fix paths to Acorn scripts in Tern demo Closes #5659 --- demo/tern.html | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/demo/tern.html b/demo/tern.html index a7ffe35c..c6834e88 100644 --- a/demo/tern.html +++ b/demo/tern.html @@ -14,8 +14,8 @@ - - + + From e4ca90988b35b3c6fd59f2aec98f7e6496afec1e Mon Sep 17 00:00:00 2001 From: Adrian Heine Date: Sun, 11 Nov 2018 21:36:37 +0100 Subject: [PATCH 1453/1790] Fix toggleComment signature in the manual Closes #5654. --- doc/manual.html | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/doc/manual.html b/doc/manual.html index 42592de6..7acef0e3 100644 --- a/doc/manual.html +++ b/doc/manual.html @@ -2478,7 +2478,7 @@

        Addons

        Addon for commenting and uncommenting code. Adds four methods to CodeMirror instances:
        -
        toggleComment(from: {line, ch}, to: {line, ch}, ?options: object)
        +
        toggleComment(?options: object)
        Tries to uncomment the current selection, and if that fails, line-comments it.
        lineComment(from: {line, ch}, to: {line, ch}, ?options: object)
        From 5556fcc309350e92e2054f230ff47c3e99495e4a Mon Sep 17 00:00:00 2001 From: Tom MacWright Date: Sun, 11 Nov 2018 19:55:49 -0800 Subject: [PATCH 1454/1790] Permit quoted names for object destructuring in JavaScript syntax --- mode/javascript/javascript.js | 1 + mode/javascript/test.js | 6 ++++++ 2 files changed, 7 insertions(+) diff --git a/mode/javascript/javascript.js b/mode/javascript/javascript.js index b0a27ceb..42b4b191 100644 --- a/mode/javascript/javascript.js +++ b/mode/javascript/javascript.js @@ -655,6 +655,7 @@ CodeMirror.defineMode("javascript", function(config, parserConfig) { if (type == "variable") cx.marked = "property"; if (type == "spread") return cont(pattern); if (type == "}") return pass(); + if (type == "[") return cont(expression, expect(']'), expect(':'), proppattern); return cont(expect(":"), pattern, maybeAssign); } function eltpattern() { diff --git a/mode/javascript/test.js b/mode/javascript/test.js index ec0a8117..21547e47 100644 --- a/mode/javascript/test.js +++ b/mode/javascript/test.js @@ -226,6 +226,12 @@ " [keyword return] [variable-2 x];", "}"); + MT( + "param_destructuring", + "[keyword function] [def foo]([def x] [operator =] [string-2 `foo${][number 10][string-2 }bar`]) {", + " [keyword return] [variable-2 x];", + "}"); + MT("new_target", "[keyword function] [def F]([def target]) {", " [keyword if] ([variable-2 target] [operator &&] [keyword new].[keyword target].[property name]) {", From b2d7d18803aa86cad02316d8ba3c11a4b92a4ad4 Mon Sep 17 00:00:00 2001 From: Marijn Haverbeke Date: Mon, 12 Nov 2018 11:08:47 +0100 Subject: [PATCH 1455/1790] Drop funding-status smiley face Not really happy with that style anymore, so removing it. --- README.md | 1 - index.html | 1 - 2 files changed, 2 deletions(-) diff --git a/README.md b/README.md index 8bc6c936..2a7b1f5e 100644 --- a/README.md +++ b/README.md @@ -3,7 +3,6 @@ [![Build Status](https://travis-ci.org/codemirror/CodeMirror.svg)](https://travis-ci.org/codemirror/CodeMirror) [![NPM version](https://img.shields.io/npm/v/codemirror.svg)](https://www.npmjs.org/package/codemirror) [![Join the chat at https://gitter.im/codemirror/CodeMirror](https://badges.gitter.im/Join%20Chat.svg)](https://gitter.im/codemirror/CodeMirror) -[Funding status: ![maintainer happiness](https://marijnhaverbeke.nl/fund/status_s.png?again)](https://marijnhaverbeke.nl/fund/) CodeMirror is a versatile text editor implemented in JavaScript for the browser. It is specialized for editing code, and comes with over diff --git a/index.html b/index.html index 4524b907..3db26096 100644 --- a/index.html +++ b/index.html @@ -107,7 +107,6 @@

        This is CodeMirror

        Software needs maintenance,
        maintainers need to subsist.
        - Current funding status =
        You can help per month or once.
        From 737b0884b09e0fbd35b65f3fec8ee74be5d159bd Mon Sep 17 00:00:00 2001 From: Mark Dalgleish Date: Wed, 14 Nov 2018 07:24:11 +1100 Subject: [PATCH 1456/1790] Support cross-window hint rendering --- addon/hint/show-hint.js | 16 +++++++++------- 1 file changed, 9 insertions(+), 7 deletions(-) diff --git a/addon/hint/show-hint.js b/addon/hint/show-hint.js index 5f6664b3..28025775 100644 --- a/addon/hint/show-hint.js +++ b/addon/hint/show-hint.js @@ -198,20 +198,22 @@ this.data = data; this.picked = false; var widget = this, cm = completion.cm; + var ownerDocument = cm.getInputField().ownerDocument; + var parentWindow = ownerDocument.defaultView || ownerDocument.parentWindow; - var hints = this.hints = document.createElement("ul"); + var hints = this.hints = ownerDocument.createElement("ul"); var theme = completion.cm.options.theme; hints.className = "CodeMirror-hints " + theme; this.selectedHint = data.selectedHint || 0; var completions = data.list; for (var i = 0; i < completions.length; ++i) { - var elt = hints.appendChild(document.createElement("li")), cur = completions[i]; + var elt = hints.appendChild(ownerDocument.createElement("li")), cur = completions[i]; var className = HINT_ELEMENT_CLASS + (i != this.selectedHint ? "" : " " + ACTIVE_HINT_ELEMENT_CLASS); if (cur.className != null) className = cur.className + " " + className; elt.className = className; if (cur.render) cur.render(elt, data, cur); - else elt.appendChild(document.createTextNode(cur.displayText || getText(cur))); + else elt.appendChild(ownerDocument.createTextNode(cur.displayText || getText(cur))); elt.hintId = i; } @@ -220,9 +222,9 @@ hints.style.left = left + "px"; hints.style.top = top + "px"; // 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); - var winH = window.innerHeight || Math.max(document.body.offsetHeight, document.documentElement.offsetHeight); - (completion.options.container || document.body).appendChild(hints); + var winW = parentWindow.innerWidth || Math.max(ownerDocument.body.offsetWidth, ownerDocument.documentElement.offsetWidth); + var winH = parentWindow.innerHeight || Math.max(ownerDocument.body.offsetHeight, ownerDocument.documentElement.offsetHeight); + (completion.options.container || ownerDocument.body).appendChild(hints); var box = hints.getBoundingClientRect(), overlapY = box.bottom - winH; var scrolls = hints.scrollHeight > hints.clientHeight + 1 var startScroll = cm.getScrollInfo(); @@ -273,7 +275,7 @@ cm.on("scroll", this.onScroll = function() { var curScroll = cm.getScrollInfo(), editor = cm.getWrapperElement().getBoundingClientRect(); var newTop = top + startScroll.top - curScroll.top; - var point = newTop - (window.pageYOffset || (document.documentElement || document.body).scrollTop); + var point = newTop - (parentWindow.pageYOffset || (ownerDocument.documentElement || ownerDocument.body).scrollTop); if (!below) point += hints.offsetHeight; if (point <= editor.top || point >= editor.bottom) return completion.close(); hints.style.top = newTop + "px"; From afa8025d5cdfbc9709c9373d2a4261d1c0b948ac Mon Sep 17 00:00:00 2001 From: Chris Colborne Date: Wed, 14 Nov 2018 08:29:31 +1000 Subject: [PATCH 1457/1790] Fix lineNumbers param in Asterisk demo --- mode/asterisk/index.html | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/mode/asterisk/index.html b/mode/asterisk/index.html index 956e1e12..b9fc627d 100644 --- a/mode/asterisk/index.html +++ b/mode/asterisk/index.html @@ -146,7 +146,7 @@

        Asterisk dialplan mode

        var editor = CodeMirror.fromTextArea(document.getElementById("code"), { mode: "text/x-asterisk", matchBrackets: true, - lineNumber: true + lineNumbers: true }); From b7b9b73f9c57eb7a51c4a5a270f32d6927c7b28e Mon Sep 17 00:00:00 2001 From: Marijn Haverbeke Date: Fri, 16 Nov 2018 10:48:00 +0100 Subject: [PATCH 1458/1790] [python mode] Fix tokenizing of nested strings Closes #5668 --- mode/python/python.js | 8 ++++---- mode/python/test.js | 2 ++ 2 files changed, 6 insertions(+), 4 deletions(-) diff --git a/mode/python/python.js b/mode/python/python.js index 623c03f7..1ebe180a 100644 --- a/mode/python/python.js +++ b/mode/python/python.js @@ -144,7 +144,7 @@ if (stream.match(stringPrefixes)) { var isFmtString = stream.current().toLowerCase().indexOf('f') !== -1; if (!isFmtString) { - state.tokenize = tokenStringFactory(stream.current()); + state.tokenize = tokenStringFactory(stream.current(), state.tokenize); return state.tokenize(stream, state); } else { state.tokenize = formatStringFactory(stream.current(), state.tokenize); @@ -251,7 +251,7 @@ return tokenString; } - function tokenStringFactory(delimiter) { + function tokenStringFactory(delimiter, tokenOuter) { while ("rubf".indexOf(delimiter.charAt(0).toLowerCase()) >= 0) delimiter = delimiter.substr(1); @@ -266,7 +266,7 @@ if (singleline && stream.eol()) return OUTCLASS; } else if (stream.match(delimiter)) { - state.tokenize = tokenBase; + state.tokenize = tokenOuter; return OUTCLASS; } else { stream.eat(/['"]/); @@ -276,7 +276,7 @@ if (parserConf.singleLineStringErrors) return ERRORCLASS; else - state.tokenize = tokenBase; + state.tokenize = tokenOuter; } return OUTCLASS; } diff --git a/mode/python/test.js b/mode/python/test.js index 604c2eb9..5fd32915 100644 --- a/mode/python/test.js +++ b/mode/python/test.js @@ -35,4 +35,6 @@ MT("fInvalidFString", "[error f'this is wrong}]"); MT("fNestedFString", "[string f'expression ]{[number 100] [operator +] [string f'inner]{[number 5]}[string ']}[string string']"); MT("uValidStringPrefix", "[string u'this is an unicode string']"); + + MT("nestedString", "[string f']{[variable b][[ [string \"c\"] ]]}[string f'] [comment # oops]") })(); From 606498c6ff088e90b22fed5afbde28d94094979b Mon Sep 17 00:00:00 2001 From: wdouglashall <43303510+wdouglashall@users.noreply.github.com> Date: Sun, 18 Nov 2018 11:01:47 -1000 Subject: [PATCH 1459/1790] Add "f95" to list of extensions for Fortran mode. --- mode/meta.js | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/mode/meta.js b/mode/meta.js index 17f5d1ab..591312b5 100644 --- a/mode/meta.js +++ b/mode/meta.js @@ -51,7 +51,7 @@ {name: "Factor", mime: "text/x-factor", mode: "factor", ext: ["factor"]}, {name: "FCL", mime: "text/x-fcl", mode: "fcl"}, {name: "Forth", mime: "text/x-forth", mode: "forth", ext: ["forth", "fth", "4th"]}, - {name: "Fortran", mime: "text/x-fortran", mode: "fortran", ext: ["f", "for", "f77", "f90"]}, + {name: "Fortran", mime: "text/x-fortran", mode: "fortran", ext: ["f", "for", "f77", "f90", "f95"]}, {name: "F#", mime: "text/x-fsharp", mode: "mllike", ext: ["fs"], alias: ["fsharp"]}, {name: "Gas", mime: "text/x-gas", mode: "gas", ext: ["s"]}, {name: "Gherkin", mime: "text/x-feature", mode: "gherkin", ext: ["feature"]}, From 3619ae2b12807e5b79a5656b7b762c1586abe6af Mon Sep 17 00:00:00 2001 From: Jonathan Dierksen Date: Tue, 20 Nov 2018 00:58:31 -0800 Subject: [PATCH 1460/1790] [swift mode] Support multi-line strings --- mode/swift/index.html | 11 +++++++++++ mode/swift/swift.js | 15 +++++++++------ mode/swift/test.js | 7 ++++++- 3 files changed, 26 insertions(+), 7 deletions(-) diff --git a/mode/swift/index.html b/mode/swift/index.html index d33eba4f..53d41343 100644 --- a/mode/swift/index.html +++ b/mode/swift/index.html @@ -71,6 +71,17 @@

        Swift mode

        } + func funWithStrings() { + var numLines = 3 + print("This is a string!") + print(""" + This is a + multi-line + string! + """) + print("The preceding string had \(numLines) lines!") + } + } diff --git a/mode/swift/swift.js b/mode/swift/swift.js index b7511851..b7b3da56 100644 --- a/mode/swift/swift.js +++ b/mode/swift/swift.js @@ -73,9 +73,8 @@ stream.match("..") return "punctuation" } - if (ch == '"' || ch == "'") { - stream.next() - var tokenize = tokenString(ch) + if (ch = stream.match(/("{3}|"|')/)) { + var tokenize = tokenString(ch[0]) state.tokenize.push(tokenize) return tokenize(stream, state) } @@ -117,6 +116,7 @@ } function tokenString(quote) { + var singleLine = quote.length == 1 return function(stream, state) { var ch, escaped = false while (ch = stream.next()) { @@ -126,13 +126,16 @@ return "string" } escaped = false - } else if (ch == quote) { - break + } else if (stream.match(quote)) { + state.tokenize.pop() + return "string" } else { escaped = ch == "\\" } } - state.tokenize.pop() + if (singleLine) { + state.tokenize.pop() + } return "string" } } diff --git a/mode/swift/test.js b/mode/swift/test.js index d8a0609b..dc0a1f8f 100644 --- a/mode/swift/test.js +++ b/mode/swift/test.js @@ -35,7 +35,12 @@ // Strings and string interpolation. MT("strings", "[keyword var] [def a][punctuation :] [variable-2 String] [operator =] [string \"test\"]", - "[keyword var] [def b][punctuation :] [variable-2 String] [operator =] [string \"\\(][variable a][string )\"]"); + "[keyword var] [def b][punctuation :] [variable-2 String] [operator =] [string \"\\(][variable a][string )\"]", + "[keyword var] [def c] [operator =] [string \"\"\"]", + "[string multi]", + "[string line]", + "[string \"test\"]", + "[string \"\"\"]"); // Comments. MT("comments", From ce1f0cd6f13d4fb8004cbb1317ba518126d4c630 Mon Sep 17 00:00:00 2001 From: Prendota Date: Mon, 19 Nov 2018 13:35:40 +0300 Subject: [PATCH 1461/1790] Support adding attributes when marking text --- doc/manual.html | 7 +++---- src/line/line_data.js | 28 +++++++++++++++++++--------- src/model/mark_text.js | 2 +- test/test.js | 13 ++++++++++++- 4 files changed, 35 insertions(+), 15 deletions(-) diff --git a/doc/manual.html b/doc/manual.html index 7acef0e3..5062bc0d 100644 --- a/doc/manual.html +++ b/doc/manual.html @@ -1716,10 +1716,9 @@

        Text-marking methods

        to startStyle, but for the rightmost span.
        css: string
        A string of CSS to be applied to the covered text. For example "color: #fe3".
        -
        title: - string
        When given, will give the nodes created - for this span a HTML title attribute with the - given value.
        +
        attributes: + object
        When given, will give the nodes created + for this span a HTML attributes 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/src/line/line_data.js b/src/line/line_data.js index 7829484c..c63edca9 100644 --- a/src/line/line_data.js +++ b/src/line/line_data.js @@ -127,7 +127,7 @@ export function defaultSpecialCharPlaceholder(ch) { // Build up the DOM representation for a single token, and add it to // the line map. Takes care to render special characters separately. -function buildToken(builder, text, style, startStyle, endStyle, title, css) { +function buildToken(builder, text, style, startStyle, endStyle, css, attributes) { if (!text) return let displayText = builder.splitSpaces ? splitSpaces(text, builder.trailingSpace) : text let special = builder.cm.state.specialChars, mustWrap = false @@ -183,7 +183,13 @@ function buildToken(builder, text, style, startStyle, endStyle, title, css) { if (startStyle) fullStyle += startStyle if (endStyle) fullStyle += endStyle let token = elt("span", [content], fullStyle, css) - if (title) token.title = title + if (attributes) { + for (let attr in attributes){ + if (attributes.hasOwnProperty(attr) && attr !== "style" && attr !== "class") { + token.setAttribute(attr, attributes[attr]) + } + } + } return builder.content.appendChild(token) } builder.content.appendChild(content) @@ -207,7 +213,7 @@ function splitSpaces(text, trailingBefore) { // Work around nonsense dimensions being reported for stretches of // right-to-left text. function buildTokenBadBidi(inner, order) { - return (builder, text, style, startStyle, endStyle, title, css) => { + return (builder, text, style, startStyle, endStyle, css, attributes) => { style = style ? style + " cm-force-border" : "cm-force-border" let start = builder.pos, end = start + text.length for (;;) { @@ -217,8 +223,8 @@ function buildTokenBadBidi(inner, order) { part = order[i] if (part.to > start && part.from <= start) break } - if (part.to >= end) return inner(builder, text, style, startStyle, endStyle, title, css) - inner(builder, text.slice(0, part.to - start), style, startStyle, null, title, css) + if (part.to >= end) return inner(builder, text, style, startStyle, endStyle, css, attributes) + inner(builder, text.slice(0, part.to - start), style, startStyle, null, css, attributes) startStyle = null text = text.slice(part.to - start) start = part.to @@ -253,10 +259,11 @@ function insertLineContent(line, builder, styles) { } let len = allText.length, pos = 0, i = 1, text = "", style, css - let nextChange = 0, spanStyle, spanEndStyle, spanStartStyle, title, collapsed + let nextChange = 0, spanStyle, spanEndStyle, spanStartStyle, collapsed, attributes for (;;) { if (nextChange == pos) { // Update current marker set - spanStyle = spanEndStyle = spanStartStyle = title = css = "" + spanStyle = spanEndStyle = spanStartStyle = css = "" + attributes = {} collapsed = null; nextChange = Infinity let foundBookmarks = [], endStyles for (let j = 0; j < spans.length; ++j) { @@ -272,7 +279,10 @@ function insertLineContent(line, builder, styles) { if (m.css) css = (css ? css + ";" : "") + m.css if (m.startStyle && sp.from == pos) spanStartStyle += " " + m.startStyle if (m.endStyle && sp.to == nextChange) (endStyles || (endStyles = [])).push(m.endStyle, sp.to) - if (m.title && !title) title = m.title + // support for the old title property + // https://github.com/codemirror/CodeMirror/pull/5673 + if (m.title) {attributes.title = m.title} + if (m.attributes) { attributes = m.attributes } if (m.collapsed && (!collapsed || compareCollapsedMarkers(collapsed.marker, m) < 0)) collapsed = sp } else if (sp.from > pos && nextChange > sp.from) { @@ -300,7 +310,7 @@ function insertLineContent(line, builder, styles) { if (!collapsed) { let tokenText = end > upto ? text.slice(0, upto - pos) : text builder.addToken(builder, tokenText, style ? style + spanStyle : spanStyle, - spanStartStyle, pos + tokenText.length == nextChange ? spanEndStyle : "", title, css) + spanStartStyle, pos + tokenText.length == nextChange ? spanEndStyle : "", css, attributes) } if (end >= upto) {text = text.slice(upto - pos); pos = upto; break} pos = end diff --git a/src/model/mark_text.js b/src/model/mark_text.js index 955c72c4..c70acd55 100644 --- a/src/model/mark_text.js +++ b/src/model/mark_text.js @@ -211,7 +211,7 @@ export function markText(doc, from, to, options, type) { if (updateMaxLine) cm.curOp.updateMaxLine = true if (marker.collapsed) regChange(cm, from.line, to.line + 1) - else if (marker.className || marker.title || marker.startStyle || marker.endStyle || marker.css) + else if (marker.className || marker.startStyle || marker.endStyle || marker.css || marker.attributes ) for (let i = from.line; i <= to.line; i++) regLineChange(cm, i, "text") if (marker.atomic) reCheckSelection(cm.doc) signalLater(cm, "markerAdded", cm, marker) diff --git a/test/test.js b/test/test.js index b1044f1f..c679a466 100644 --- a/test/test.js +++ b/test/test.js @@ -549,13 +549,24 @@ testCM("markTextCSS", function(cm) { function present() { var spans = cm.display.lineDiv.getElementsByTagName("span"); for (var i = 0; i < spans.length; i++) - if (spans[i].style.color == "cyan" && span[i].textContent == "cdefg") return true; + if (spans[i].style.color == "cyan" && spans[i].textContent == "cdefg") return true; } var m = cm.markText(Pos(0, 2), Pos(0, 6), {css: "color: cyan"}); m.clear(); is(!present()); }, {value: "abcdefgh"}); +testCM("markTextWithAttributes", function(cm) { + function present() { + var spans = cm.display.lineDiv.getElementsByTagName("span"); + for (var i = 0; i < spans.length; i++) + if (spans[i].getAttribute('label') == "label" && span[i].textContent == "cdefg") return true; + } + var m = cm.markText(Pos(0, 2), Pos(0, 6), {attributes: {label: "label"}}); + m.clear(); + is(!present()); +}, {value: "abcdefgh"}); + 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 01758b19565384414306816b43b5f35d81f039a3 Mon Sep 17 00:00:00 2001 From: Marijn Haverbeke Date: Tue, 20 Nov 2018 11:06:00 +0100 Subject: [PATCH 1462/1790] Clean up implementation of mark attributes Issue #5673 --- doc/manual.html | 7 ++++--- src/line/line_data.js | 16 ++++++++-------- src/model/mark_text.js | 3 ++- test/test.js | 6 ++++-- 4 files changed, 18 insertions(+), 14 deletions(-) diff --git a/doc/manual.html b/doc/manual.html index 5062bc0d..57ecb7f2 100644 --- a/doc/manual.html +++ b/doc/manual.html @@ -1716,9 +1716,10 @@

        Text-marking methods

        to startStyle, but for the rightmost span.
        css: string
        A string of CSS to be applied to the covered text. For example "color: #fe3".
        -
        attributes: - object
        When given, will give the nodes created - for this span a HTML attributes with the given value.
        +
        attributes: object
        +
        When given, add the attributes in the given object to the + elements created for the marked text. Adding class or + style attributes this way is not supported.
        shared: boolean
        When the target document is linked to other documents, you can set shared to true to make the diff --git a/src/line/line_data.js b/src/line/line_data.js index c63edca9..20dd4328 100644 --- a/src/line/line_data.js +++ b/src/line/line_data.js @@ -184,11 +184,8 @@ function buildToken(builder, text, style, startStyle, endStyle, css, attributes) if (endStyle) fullStyle += endStyle let token = elt("span", [content], fullStyle, css) if (attributes) { - for (let attr in attributes){ - if (attributes.hasOwnProperty(attr) && attr !== "style" && attr !== "class") { - token.setAttribute(attr, attributes[attr]) - } - } + for (let attr in attributes) if (attributes.hasOwnProperty(attr) && attr != "style" && attr != "class") + token.setAttribute(attr, attributes[attr]) } return builder.content.appendChild(token) } @@ -263,7 +260,7 @@ function insertLineContent(line, builder, styles) { for (;;) { if (nextChange == pos) { // Update current marker set spanStyle = spanEndStyle = spanStartStyle = css = "" - attributes = {} + attributes = null collapsed = null; nextChange = Infinity let foundBookmarks = [], endStyles for (let j = 0; j < spans.length; ++j) { @@ -281,8 +278,11 @@ function insertLineContent(line, builder, styles) { if (m.endStyle && sp.to == nextChange) (endStyles || (endStyles = [])).push(m.endStyle, sp.to) // support for the old title property // https://github.com/codemirror/CodeMirror/pull/5673 - if (m.title) {attributes.title = m.title} - if (m.attributes) { attributes = m.attributes } + if (m.title) (attributes || (attributes = {})).title = m.title + if (m.attributes) { + for (let attr in m.attributes) + (attributes || (attributes = {}))[attr] = m.attributes[attr] + } if (m.collapsed && (!collapsed || compareCollapsedMarkers(collapsed.marker, m) < 0)) collapsed = sp } else if (sp.from > pos && nextChange > sp.from) { diff --git a/src/model/mark_text.js b/src/model/mark_text.js index c70acd55..088f9c98 100644 --- a/src/model/mark_text.js +++ b/src/model/mark_text.js @@ -211,7 +211,8 @@ export function markText(doc, from, to, options, type) { if (updateMaxLine) cm.curOp.updateMaxLine = true if (marker.collapsed) regChange(cm, from.line, to.line + 1) - else if (marker.className || marker.startStyle || marker.endStyle || marker.css || marker.attributes ) + else if (marker.className || marker.startStyle || marker.endStyle || marker.css || + marker.attributes || marker.title) for (let i = from.line; i <= to.line; i++) regLineChange(cm, i, "text") if (marker.atomic) reCheckSelection(cm.doc) signalLater(cm, "markerAdded", cm, marker) diff --git a/test/test.js b/test/test.js index c679a466..a0e06692 100644 --- a/test/test.js +++ b/test/test.js @@ -549,9 +549,10 @@ testCM("markTextCSS", function(cm) { function present() { var spans = cm.display.lineDiv.getElementsByTagName("span"); for (var i = 0; i < spans.length; i++) - if (spans[i].style.color == "cyan" && spans[i].textContent == "cdefg") return true; + if (spans[i].style.color == "cyan" && spans[i].textContent == "cdef") return true; } var m = cm.markText(Pos(0, 2), Pos(0, 6), {css: "color: cyan"}); + is(present()); m.clear(); is(!present()); }, {value: "abcdefgh"}); @@ -560,9 +561,10 @@ testCM("markTextWithAttributes", function(cm) { function present() { var spans = cm.display.lineDiv.getElementsByTagName("span"); for (var i = 0; i < spans.length; i++) - if (spans[i].getAttribute('label') == "label" && span[i].textContent == "cdefg") return true; + if (spans[i].getAttribute("label") == "label" && spans[i].textContent == "cdef") return true; } var m = cm.markText(Pos(0, 2), Pos(0, 6), {attributes: {label: "label"}}); + is(present()); m.clear(); is(!present()); }, {value: "abcdefgh"}); From 275447839df63c80ce2873784ea199ee75a03628 Mon Sep 17 00:00:00 2001 From: Marijn Haverbeke Date: Tue, 20 Nov 2018 11:17:20 +0100 Subject: [PATCH 1463/1790] Correct for color normalization in phantomjs in a test --- test/test.js | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/test/test.js b/test/test.js index a0e06692..f26d01aa 100644 --- a/test/test.js +++ b/test/test.js @@ -549,7 +549,7 @@ testCM("markTextCSS", function(cm) { function present() { var spans = cm.display.lineDiv.getElementsByTagName("span"); for (var i = 0; i < spans.length; i++) - if (spans[i].style.color == "cyan" && spans[i].textContent == "cdef") return true; + if (spans[i].style.color && spans[i].textContent == "cdef") return true; } var m = cm.markText(Pos(0, 2), Pos(0, 6), {css: "color: cyan"}); is(present()); From 5c51f54da80787e91d53110adbacee8f7f37f47e Mon Sep 17 00:00:00 2001 From: Marijn Haverbeke Date: Tue, 20 Nov 2018 12:02:01 +0100 Subject: [PATCH 1464/1790] Run global events in an operation Issue #5653 --- src/edit/global_events.js | 7 +++++-- 1 file changed, 5 insertions(+), 2 deletions(-) diff --git a/src/edit/global_events.js b/src/edit/global_events.js index d34fabef..d03da2d0 100644 --- a/src/edit/global_events.js +++ b/src/edit/global_events.js @@ -7,11 +7,14 @@ import { on } from "../util/event.js" function forEachCodeMirror(f) { if (!document.getElementsByClassName) return - let byClass = document.getElementsByClassName("CodeMirror") + let byClass = document.getElementsByClassName("CodeMirror"), editors = [] for (let i = 0; i < byClass.length; i++) { let cm = byClass[i].CodeMirror - if (cm) f(cm) + if (cm) editors.push(cm) } + if (editors.length) editors[0].operation(() => { + for (let i = 0; i < editors.length; i++) f(editors[i]) + }) } let globalsRegistered = false From 51a37047c107ab962067ba243aa517c42bc367f1 Mon Sep 17 00:00:00 2001 From: Marijn Haverbeke Date: Tue, 20 Nov 2018 14:12:48 +0100 Subject: [PATCH 1465/1790] Notice when lines are sticking out of the width of the editor Issue #5639 --- src/display/update_lines.js | 17 +++++++++++++++-- 1 file changed, 15 insertions(+), 2 deletions(-) diff --git a/src/display/update_lines.js b/src/display/update_lines.js index 7f06018d..475432b7 100644 --- a/src/display/update_lines.js +++ b/src/display/update_lines.js @@ -1,6 +1,6 @@ import { heightAtLine } from "../line/spans.js" import { getLine, lineAtHeight, updateLineHeight } from "../line/utils_line.js" -import { paddingTop, textHeight } from "../measurement/position_measurement.js" +import { paddingTop, textHeight, charWidth } from "../measurement/position_measurement.js" import { ie, ie_version } from "../util/browser.js" // Read the actual heights of the rendered lines, and update their @@ -9,7 +9,8 @@ export function updateHeightsInViewport(cm) { let display = cm.display let prevBottom = display.lineDiv.offsetTop for (let i = 0; i < display.view.length; i++) { - let cur = display.view[i], height + let cur = display.view[i], wrapping = cm.options.lineWrapping + let height, width = 0 if (cur.hidden) continue if (ie && ie_version < 8) { let bot = cur.node.offsetTop + cur.node.offsetHeight @@ -18,6 +19,10 @@ export function updateHeightsInViewport(cm) { } else { let box = cur.node.getBoundingClientRect() height = box.bottom - box.top + // Check that lines don't extend past the right of the current + // editor width + if (!wrapping && cur.text.firstChild) + width = cur.text.firstChild.getBoundingClientRect().right - box.left - 1 } let diff = cur.line.height - height if (height < 2) height = textHeight(display) @@ -27,6 +32,14 @@ export function updateHeightsInViewport(cm) { if (cur.rest) for (let j = 0; j < cur.rest.length; j++) updateWidgetHeight(cur.rest[j]) } + if (width > cm.display.sizerWidth) { + let chWidth = Math.ceil(width / charWidth(cm.display)) + if (chWidth > cm.display.maxLineLength) { + cm.display.maxLineLength = chWidth + cm.display.maxLine = cur.line + cm.display.maxLineChanged = true + } + } } } From 436081e1cd8be37eea30a65f4a709137d66a47f5 Mon Sep 17 00:00:00 2001 From: Marijn Haverbeke Date: Tue, 20 Nov 2018 14:30:29 +0100 Subject: [PATCH 1466/1790] Mark version 5.42.0 --- AUTHORS | 6 ++++++ CHANGELOG.md | 20 ++++++++++++++++++++ doc/manual.html | 2 +- doc/releases.html | 13 +++++++++++++ index.html | 2 +- package.json | 2 +- src/edit/main.js | 2 +- 7 files changed, 43 insertions(+), 4 deletions(-) diff --git a/AUTHORS b/AUTHORS index a929165b..83d2a4d9 100644 --- a/AUTHORS +++ b/AUTHORS @@ -134,6 +134,7 @@ Chad Jolly Chandra Sekhar Pydi Charles Skelton Cheah Chu Yeow +Chris Colborne Chris Coyier Chris Ford Chris Granger @@ -316,6 +317,7 @@ James Howard James Thorne Jamie Hill Jamie Morris +Janice Leung Jan Jongboom jankeromnes Jan Keromnes @@ -359,6 +361,7 @@ John Van Der Loo Jon Ander Peñalba Jonas Döbertin Jonas Helfer +Jonathan Dierksen Jonathan Hart Jonathan Malmaud Jon Gacnik @@ -452,6 +455,7 @@ Marijn Haverbeke Mário Gonçalves Mario Pietsch Mark Anderson +Mark Dalgleish Mark Lentczner Marko Bonaci Mark Peace @@ -557,6 +561,7 @@ Nisarg Jhaveri nlwillia noragrossman Norman Rzepka +Oleksandr Yakovenko opl- Oreoluwa Onatemowo Oskar Segersvärd @@ -745,6 +750,7 @@ Victor Bocharsky Vincent Woo Volker Mische vtripolitakis +wdouglashall Weiyan Shao wenli Wes Cossick diff --git a/CHANGELOG.md b/CHANGELOG.md index 4823d22f..1e20bf30 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -1,3 +1,23 @@ +## 5.42.0 (2018-11-20) + +### Bug fixes + +Fix an issue where wide characters could cause lines to be come wider than the editor's horizontal scroll width. + +Optimize handling of window resize events. + +[show-hint addon](https://codemirror.net/doc/manual.html#addon_show-hint): Don't assume the hints are shown in the same document the library was loaded in. + +[python mode](https://codemirror.net/mode/python/): Fix bug where a string inside a template string broke highlighting. + +[swift mode](https://codemirror.net/mode/swift): Support multi-line strings. + +### New features + +The [`markText` method](https://codemirror.net/doc/manual.html#markText) now takes an [`attributes`](https://codemirror.net/doc/manual.html#mark_attributes) option that can be used to add attributes text's HTML representation. + +[vim bindings](https://codemirror.net/demo/vim.html): Add support for the `=` binding. + ## 5.41.0 (2018-10-25) ### Bug fixes diff --git a/doc/manual.html b/doc/manual.html index 57ecb7f2..af94ba25 100644 --- a/doc/manual.html +++ b/doc/manual.html @@ -69,7 +69,7 @@

        User manual and reference guide - version 5.41.1 + version 5.42.0

        CodeMirror is a code-editor component that can be embedded in diff --git a/doc/releases.html b/doc/releases.html index 401eca48..2f1e965c 100644 --- a/doc/releases.html +++ b/doc/releases.html @@ -30,6 +30,19 @@

        Release notes and version history

        Version 5.x

        + +

        20-11-2018: Version 5.42.0:

        + +
          +
        • The markText method now takes an attributes option that can be used to add attributes text's HTML representation.
        • +
        • vim bindings: Add support for the = binding.
        • +
        • Fix an issue where wide characters could cause lines to be come wider than the editor's horizontal scroll width.
        • +
        • Optimize handling of window resize events.
        • +
        • show-hint addon: Don't assume the hints are shown in the same document the library was loaded in.
        • +
        • python mode: Fix bug where a string inside a template string broke highlighting.
        • +
        • swift mode: Support multi-line strings.
        • +
        +

        25-10-2018: Version 5.41.0:

          diff --git a/index.html b/index.html index 3db26096..ce02a28c 100644 --- a/index.html +++ b/index.html @@ -99,7 +99,7 @@

          This is CodeMirror

        - Get the current version: 5.41.0.
        + Get the current version: 5.42.0.
        You can see the code,
        read the release notes,
        or study the user manual. diff --git a/package.json b/package.json index 6aab74b6..49bfd12f 100644 --- a/package.json +++ b/package.json @@ -1,6 +1,6 @@ { "name": "codemirror", - "version": "5.41.1", + "version": "5.42.0", "main": "lib/codemirror.js", "style": "lib/codemirror.css", "author": { diff --git a/src/edit/main.js b/src/edit/main.js index c1beccac..2f62bd71 100644 --- a/src/edit/main.js +++ b/src/edit/main.js @@ -66,4 +66,4 @@ import { addLegacyProps } from "./legacy.js" addLegacyProps(CodeMirror) -CodeMirror.version = "5.41.1" +CodeMirror.version = "5.42.0" From 74dbe31ee157f53b3c0e0a45e97b819c4afe0ecf Mon Sep 17 00:00:00 2001 From: Marijn Haverbeke Date: Tue, 20 Nov 2018 14:33:58 +0100 Subject: [PATCH 1467/1790] Update version number post-5.42.0 --- doc/manual.html | 2 +- package.json | 2 +- src/edit/main.js | 2 +- 3 files changed, 3 insertions(+), 3 deletions(-) diff --git a/doc/manual.html b/doc/manual.html index af94ba25..ae07f8d3 100644 --- a/doc/manual.html +++ b/doc/manual.html @@ -69,7 +69,7 @@

        User manual and reference guide - version 5.42.0 + version 5.42.1

        CodeMirror is a code-editor component that can be embedded in diff --git a/package.json b/package.json index 49bfd12f..673f9a2f 100644 --- a/package.json +++ b/package.json @@ -1,6 +1,6 @@ { "name": "codemirror", - "version": "5.42.0", + "version": "5.42.1", "main": "lib/codemirror.js", "style": "lib/codemirror.css", "author": { diff --git a/src/edit/main.js b/src/edit/main.js index 2f62bd71..090049e9 100644 --- a/src/edit/main.js +++ b/src/edit/main.js @@ -66,4 +66,4 @@ import { addLegacyProps } from "./legacy.js" addLegacyProps(CodeMirror) -CodeMirror.version = "5.42.0" +CodeMirror.version = "5.42.1" From 75baa0ee2cad579d8d4fec913e0e15cd22a80d32 Mon Sep 17 00:00:00 2001 From: Jason Heeris Date: Wed, 21 Nov 2018 19:22:00 +1100 Subject: [PATCH 1468/1790] [base16-light theme] Changed styling for bracket matching --- theme/base16-light.css | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/theme/base16-light.css b/theme/base16-light.css index 474e0ca9..1d5f582f 100644 --- a/theme/base16-light.css +++ b/theme/base16-light.css @@ -35,4 +35,4 @@ .cm-s-base16-light span.cm-error { background: #ac4142; color: #505050; } .cm-s-base16-light .CodeMirror-activeline-background { background: #DDDCDC; } -.cm-s-base16-light .CodeMirror-matchingbracket { text-decoration: underline; color: white !important; } +.cm-s-base16-light .CodeMirror-matchingbracket { color: #f5f5f5 !important; background-color: #6A9FB5 !important} From 1c559951743c934c3329080083dc7062aa973f2a Mon Sep 17 00:00:00 2001 From: Marijn Haverbeke Date: Thu, 22 Nov 2018 12:04:17 +0100 Subject: [PATCH 1469/1790] Make sure the hidden textarea is reset when a change is cancelled Closes #5677 --- src/display/operations.js | 2 +- src/input/input.js | 5 ++--- src/model/changes.js | 5 ++++- src/model/selection_updates.js | 3 ++- 4 files changed, 9 insertions(+), 6 deletions(-) diff --git a/src/display/operations.js b/src/display/operations.js index 9cc51981..6f3c9d08 100644 --- a/src/display/operations.js +++ b/src/display/operations.js @@ -26,7 +26,7 @@ export function startOperation(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 - updateInput: null, // Whether to reset the input textarea + updateInput: 0, // Whether to reset the input textarea 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 diff --git a/src/input/input.js b/src/input/input.js index 106b4b64..eb9c2cab 100644 --- a/src/input/input.js +++ b/src/input/input.js @@ -40,7 +40,7 @@ export function applyTextInput(cm, inserted, deleted, sel, origin) { } } - let updateInput + let updateInput = cm.curOp.updateInput // Normal behavior is to insert the new text into every selection for (let i = sel.ranges.length - 1; i >= 0; i--) { let range = sel.ranges[i] @@ -53,7 +53,6 @@ export function applyTextInput(cm, inserted, deleted, sel, origin) { else if (paste && lastCopied && lastCopied.lineWise && lastCopied.text.join("\n") == inserted) from = to = Pos(from.line, 0) } - updateInput = cm.curOp.updateInput let changeEvent = {from: from, to: to, text: multiPaste ? multiPaste[i % multiPaste.length] : textLines, origin: origin || (paste ? "paste" : cm.state.cutIncoming ? "cut" : "+input")} makeChange(cm.doc, changeEvent) @@ -63,7 +62,7 @@ export function applyTextInput(cm, inserted, deleted, sel, origin) { triggerElectric(cm, inserted) ensureCursorVisible(cm) - cm.curOp.updateInput = updateInput + if (cm.curOp.updateInput < 2) cm.curOp.updateInput = updateInput cm.curOp.typing = true cm.state.pasteIncoming = cm.state.cutIncoming = false } diff --git a/src/model/changes.js b/src/model/changes.js index 0d356f69..7a688837 100644 --- a/src/model/changes.js +++ b/src/model/changes.js @@ -38,7 +38,10 @@ function filterChange(doc, change, update) { signal(doc, "beforeChange", doc, obj) if (doc.cm) signal(doc.cm, "beforeChange", doc.cm, obj) - if (obj.canceled) return null + if (obj.canceled) { + if (doc.cm) doc.cm.curOp.updateInput = 2 + return null + } return {from: obj.from, to: obj.to, text: obj.text, origin: obj.origin} } diff --git a/src/model/selection_updates.js b/src/model/selection_updates.js index c13ce9f3..c9941b71 100644 --- a/src/model/selection_updates.js +++ b/src/model/selection_updates.js @@ -116,7 +116,8 @@ function setSelectionInner(doc, sel) { doc.sel = sel if (doc.cm) { - doc.cm.curOp.updateInput = doc.cm.curOp.selectionChanged = true + doc.cm.curOp.updateInput = 1 + doc.cm.curOp.selectionChanged = true signalCursorActivity(doc.cm) } signalLater(doc, "cursorActivity", doc) From 5740f791c01091a9abac28978f2aa87594e77b13 Mon Sep 17 00:00:00 2001 From: LaKing Date: Thu, 29 Nov 2018 14:44:25 +0100 Subject: [PATCH 1470/1790] [lint demo] Changed script links to use unpkg --- demo/lint.html | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/demo/lint.html b/demo/lint.html index a5ac3ef2..1184232d 100644 --- a/demo/lint.html +++ b/demo/lint.html @@ -9,8 +9,8 @@ - - + + From 728291d31bd7381f312c266c9acf8e4f98d13a33 Mon Sep 17 00:00:00 2001 From: Joel Einbinder Date: Fri, 30 Nov 2018 00:58:28 -0800 Subject: [PATCH 1471/1790] [css] support double dash css variable (#5687) --- mode/css/css.js | 2 +- mode/css/test.js | 8 ++++++++ 2 files changed, 9 insertions(+), 1 deletion(-) diff --git a/mode/css/css.js b/mode/css/css.js index 22b463c9..d82de404 100644 --- a/mode/css/css.js +++ b/mode/css/css.js @@ -63,7 +63,7 @@ CodeMirror.defineMode("css", function(config, parserConfig) { if (/[\d.]/.test(stream.peek())) { stream.eatWhile(/[\w.%]/); return ret("number", "unit"); - } else if (stream.match(/^-[\w\\\-]+/)) { + } else if (stream.match(/^-[\w\\\-]*/)) { stream.eatWhile(/[\w\\\-]/); if (stream.match(/^\s*:/, false)) return ret("variable-2", "variable-definition"); diff --git a/mode/css/test.js b/mode/css/test.js index 6d83df28..5bfc7af9 100644 --- a/mode/css/test.js +++ b/mode/css/test.js @@ -149,6 +149,14 @@ " [property color]: [atom var]([variable-2 --main-color]);", "}"); + MT("blank_css_variable", + ":[variable-3 root] {", + " [variable-2 --]: [atom #06c];", + "}", + "[tag h1][builtin #foo] {", + " [property color]: [atom var]([variable-2 --]);", + "}"); + MT("supports", "[def @supports] ([keyword not] (([property text-align-last]: [atom justify]) [keyword or] ([meta -moz-][property text-align-last]: [atom justify])) {", " [property text-align-last]: [atom justify];", From 9376ad89daee45102e1a0a46a96d70d9ea8e9fd5 Mon Sep 17 00:00:00 2001 From: yoongu Date: Fri, 30 Nov 2018 04:00:10 -0500 Subject: [PATCH 1472/1790] [vim bindings] Allow selecting text enclosed in angled brackets --- addon/edit/matchbrackets.js | 4 ++-- keymap/vim.js | 12 ++++++------ test/vim_test.js | 6 ++++++ 3 files changed, 14 insertions(+), 8 deletions(-) diff --git a/addon/edit/matchbrackets.js b/addon/edit/matchbrackets.js index c918c3f9..6ad5a012 100644 --- a/addon/edit/matchbrackets.js +++ b/addon/edit/matchbrackets.js @@ -14,7 +14,7 @@ var Pos = CodeMirror.Pos; - var matching = {"(": ")>", ")": "(<", "[": "]>", "]": "[<", "{": "}>", "}": "{<"}; + var matching = {"(": ")>", ")": "(<", "[": "]>", "]": "[<", "{": "}>", "}": "{<", "<": ">>", ">": "<<"}; function findMatchingBracket(cm, where, config) { var line = cm.getLineHandle(where.line), pos = where.ch - 1; @@ -51,7 +51,7 @@ var maxScanLines = (config && config.maxScanLines) || 1000; var stack = []; - var re = config && config.bracketRegex ? config.bracketRegex : /[(){}[\]]/; + var re = config && config.bracketRegex ? config.bracketRegex : /[(){}[\]<>]/; var lineEnd = dir > 0 ? Math.min(where.line + maxScanLines, cm.lastLine() + 1) : Math.max(cm.firstLine() - 1, where.line - maxScanLines); for (var lineNo = where.line; lineNo != lineEnd; lineNo += dir) { diff --git a/keymap/vim.js b/keymap/vim.js index aed0957d..da10c1a9 100644 --- a/keymap/vim.js +++ b/keymap/vim.js @@ -1995,12 +1995,10 @@ textObjectManipulation: function(cm, head, motionArgs, vim) { // TODO: lots of possible exceptions that can be thrown here. Try da( // outside of a () block. - - // TODO: adding <> >< to this map doesn't work, presumably because - // they're operators var mirroredPairs = {'(': ')', ')': '(', '{': '}', '}': '{', - '[': ']', ']': '['}; + '[': ']', ']': '[', + '<': '>', '>': '<'}; var selfPaired = {'\'': true, '"': true}; var character = motionArgs.selectedCharacter; @@ -3802,11 +3800,13 @@ var bracketRegexp = ({ '(': /[()]/, ')': /[()]/, '[': /[[\]]/, ']': /[[\]]/, - '{': /[{}]/, '}': /[{}]/})[symb]; + '{': /[{}]/, '}': /[{}]/, + '<': /[<>]/, '>': /[<>]/})[symb]; var openSym = ({ '(': '(', ')': '(', '[': '[', ']': '[', - '{': '{', '}': '{'})[symb]; + '{': '{', '}': '{', + '<': '<', '>': '<'})[symb]; var curChar = cm.getLine(cur.line).charAt(cur.ch); // Due to the behavior of scanForBracket, we need to add an offset if the // cursor is on a matching open bracket. diff --git a/test/vim_test.js b/test/vim_test.js index 388d86b7..939aee5f 100644 --- a/test/vim_test.js +++ b/test/vim_test.js @@ -1327,6 +1327,12 @@ testEdit('di]_middle_spc', 'a\t[\n\tbar\n]b', /r/, 'di]', 'a\t[]b'); testEdit('da[_middle_spc', 'a\t[\n\tbar\n]b', /r/, 'da[', 'a\tb'); testEdit('da]_middle_spc', 'a\t[\n\tbar\n]b', /r/, 'da]', 'a\tb'); +// open and close on diff lines, open indented more than close +testEdit('di<_middle_spc', 'a\t<\n\tbar\n>b', /r/, 'di<', 'a\t<>b'); +testEdit('di>_middle_spc', 'a\t<\n\tbar\n>b', /r/, 'di>', 'a\t<>b'); +testEdit('da<_middle_spc', 'a\t<\n\tbar\n>b', /r/, 'da<', 'a\tb'); +testEdit('da>_middle_spc', 'a\t<\n\tbar\n>b', /r/, 'da>', 'a\tb'); + function testSelection(name, before, pos, keys, sel) { return testVim(name, function(cm, vim, helpers) { var ch = before.search(pos) From e886aa8761a5d359e39f072699a0dc7db7d3b0da Mon Sep 17 00:00:00 2001 From: Marijn Haverbeke Date: Tue, 4 Dec 2018 09:04:48 +0100 Subject: [PATCH 1473/1790] [python mode] Fix nested braces in FString expression Closes #5691 --- mode/python/python.js | 41 ++++++++++++++++------------------------- mode/python/test.js | 4 ++++ 2 files changed, 20 insertions(+), 25 deletions(-) diff --git a/mode/python/python.js b/mode/python/python.js index 1ebe180a..46b02947 100644 --- a/mode/python/python.js +++ b/mode/python/python.js @@ -187,23 +187,19 @@ var singleline = delimiter.length == 1; var OUTCLASS = "string"; - function tokenFString(stream, state) { - // inside f-str Expression - if (stream.match(delimiter)) { - // expression ends pre-maturally, but very common in editing - // Could show error to remind users to close brace here - state.tokenize = tokenString - return OUTCLASS; - } else if (stream.match('{')) { - // starting brace, if not eaten below - return "punctuation"; - } else if (stream.match('}')) { - // return to regular inside string state - state.tokenize = tokenString - return "punctuation"; - } else { - // use tokenBaseInner to parse the expression - return tokenBaseInner(stream, state); + function tokenNestedExpr(depth) { + return function(stream, state) { + let inner = tokenBaseInner(stream, state) + console.log(inner, stream.current()) + if (inner == "punctuation") { + if (stream.current() == "{") { + state.tokenize = tokenNestedExpr(depth + 1) + } else if (stream.current() == "}") { + if (depth > 1) state.tokenize = tokenNestedExpr(depth - 1) + else state.tokenize = tokenString + } + } + return inner } } @@ -222,14 +218,9 @@ return OUTCLASS; } else if (stream.match('{', false)) { // switch to nested mode - state.tokenize = tokenFString - if (stream.current()) { - return OUTCLASS; - } else { - // need to return something, so eat the starting { - stream.next(); - return "punctuation"; - } + state.tokenize = tokenNestedExpr(0) + if (stream.current()) return OUTCLASS; + else return state.tokenize() } else if (stream.match('}}')) { return OUTCLASS; } else if (stream.match('}')) { diff --git a/mode/python/test.js b/mode/python/test.js index 5fd32915..2b605b8e 100644 --- a/mode/python/test.js +++ b/mode/python/test.js @@ -37,4 +37,8 @@ MT("uValidStringPrefix", "[string u'this is an unicode string']"); MT("nestedString", "[string f']{[variable b][[ [string \"c\"] ]]}[string f'] [comment # oops]") + + MT("bracesInFString", "[string f']{[variable x] [operator +] {}}[string !']") + + MT("nestedFString", "[string f']{[variable b][[ [string f\"c\"] ]]}[string f'] [comment # oops]") })(); From bebe419e16aa9ddbcd51b4269ddb327472ef026f Mon Sep 17 00:00:00 2001 From: Marijn Haverbeke Date: Tue, 4 Dec 2018 09:08:21 +0100 Subject: [PATCH 1474/1790] Fix use of let in mode --- mode/python/python.js | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/mode/python/python.js b/mode/python/python.js index 46b02947..a3595dd2 100644 --- a/mode/python/python.js +++ b/mode/python/python.js @@ -189,7 +189,7 @@ function tokenNestedExpr(depth) { return function(stream, state) { - let inner = tokenBaseInner(stream, state) + var inner = tokenBaseInner(stream, state) console.log(inner, stream.current()) if (inner == "punctuation") { if (stream.current() == "{") { From 4128be063f92177d91fe8f2b9f5a0293a9f66cc7 Mon Sep 17 00:00:00 2001 From: Marijn Haverbeke Date: Tue, 4 Dec 2018 09:17:29 +0100 Subject: [PATCH 1475/1790] Remove debug statement --- mode/python/python.js | 1 - 1 file changed, 1 deletion(-) diff --git a/mode/python/python.js b/mode/python/python.js index a3595dd2..49f1ec4d 100644 --- a/mode/python/python.js +++ b/mode/python/python.js @@ -190,7 +190,6 @@ function tokenNestedExpr(depth) { return function(stream, state) { var inner = tokenBaseInner(stream, state) - console.log(inner, stream.current()) if (inner == "punctuation") { if (stream.current() == "{") { state.tokenize = tokenNestedExpr(depth + 1) From ea5ef9dc4fd213140f0af871e1a2bc8c84613c47 Mon Sep 17 00:00:00 2001 From: Neil Anderson Date: Sat, 8 Dec 2018 14:35:49 -0500 Subject: [PATCH 1476/1790] [sql mode] Add GETTOKEN, HEADLINE and LEXTYPES for postgresql see https://www.postgresql.org/docs/10/sql-createtsparser.html --- 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 d9f418b7..0ae54a64 100644 --- a/mode/sql/sql.js +++ b/mode/sql/sql.js @@ -411,7 +411,7 @@ CodeMirror.defineMode("sql", function(config, parserConfig) { name: "sql", client: set("source"), // https://www.postgresql.org/docs/10/static/sql-keywords-appendix.html - keywords: set(sqlKeywords + "a abort abs absent absolute access according action ada add admin after aggregate all allocate also always analyse analyze any are array array_agg array_max_cardinality asensitive assertion assignment asymmetric at atomic attribute attributes authorization avg backward base64 before begin begin_frame begin_partition bernoulli binary bit_length blob blocked bom both breadth c cache call called cardinality cascade cascaded case cast catalog catalog_name ceil ceiling chain characteristics characters character_length character_set_catalog character_set_name character_set_schema char_length check checkpoint class class_origin clob close cluster coalesce cobol collate collation collation_catalog collation_name collation_schema collect column columns column_name command_function command_function_code comment comments commit committed concurrently condition condition_number configuration conflict connect connection connection_name constraint constraints constraint_catalog constraint_name constraint_schema constructor contains content continue control conversion convert copy corr corresponding cost covar_pop covar_samp cross csv cube cume_dist current current_catalog current_date current_default_transform_group current_path current_role current_row current_schema current_time current_timestamp current_transform_group_for_type current_user cursor cursor_name cycle data database datalink datetime_interval_code datetime_interval_precision day db deallocate dec declare default defaults deferrable deferred defined definer degree delimiter delimiters dense_rank depth deref derived describe descriptor deterministic diagnostics dictionary disable discard disconnect dispatch dlnewcopy dlpreviouscopy dlurlcomplete dlurlcompleteonly dlurlcompletewrite dlurlpath dlurlpathonly dlurlpathwrite dlurlscheme dlurlserver dlvalue do document domain dynamic dynamic_function dynamic_function_code each element else empty enable encoding encrypted end end-exec end_frame end_partition enforced enum equals escape event every except exception exclude excluding exclusive exec execute exists exp explain expression extension external extract false family fetch file filter final first first_value flag float floor following for force foreign fortran forward found frame_row free freeze fs full function functions fusion g general generated get global go goto grant granted greatest grouping groups handler header hex hierarchy hold hour id identity if ignore ilike immediate immediately immutable implementation implicit import including increment indent index indexes indicator inherit inherits initially inline inner inout input insensitive instance instantiable instead integrity intersect intersection invoker isnull isolation k key key_member key_type label lag language large last last_value lateral lc_collate lc_ctype lead leading leakproof least left length level library like_regex link listen ln load local localtime localtimestamp location locator lock locked logged lower m map mapping match matched materialized max maxvalue max_cardinality member merge message_length message_octet_length message_text method min minute minvalue mod mode modifies module month more move multiset mumps name names namespace national natural nchar nclob nesting new next nfc nfd nfkc nfkd nil no none normalize normalized nothing notify notnull nowait nth_value ntile null nullable nullif nulls number object occurrences_regex octets octet_length of off offset oids old only open operator option options ordering ordinality others out outer output over overlaps overlay overriding owned owner p pad parallel parameter parameter_mode parameter_name parameter_ordinal_position parameter_specific_catalog parameter_specific_name parameter_specific_schema parser partial partition pascal passing passthrough password percent percentile_cont percentile_disc percent_rank period permission placing plans pli policy portion position position_regex power precedes preceding prepare prepared preserve primary prior privileges procedural procedure program public quote range rank read reads reassign recheck recovery recursive ref references referencing refresh regr_avgx regr_avgy regr_count regr_intercept regr_r2 regr_slope regr_sxx regr_sxy regr_syy reindex relative release rename repeatable replace replica requiring reset respect restart restore restrict restricted result return returned_cardinality returned_length returned_octet_length returned_sqlstate returning returns revoke right role rollback rollup routine routine_catalog routine_name routine_schema row rows row_count row_number rule savepoint scale schema schema_name scope scope_catalog scope_name scope_schema scroll search second section security selective self sensitive sequence sequences serializable server server_name session session_user setof sets share show similar simple size skip snapshot some source space specific specifictype specific_name sql sqlcode sqlerror sqlexception sqlstate sqlwarning sqrt stable standalone start state statement static statistics stddev_pop stddev_samp stdin stdout storage strict strip structure style subclass_origin submultiset substring substring_regex succeeds sum symmetric sysid system system_time system_user t tables tablesample tablespace table_name temp template temporary then ties timezone_hour timezone_minute to token top_level_count trailing transaction transactions_committed transactions_rolled_back transaction_active transform transforms translate translate_regex translation treat trigger trigger_catalog trigger_name trigger_schema trim trim_array true truncate trusted type types uescape unbounded uncommitted under unencrypted unique unknown unlink unlisten unlogged unnamed unnest until untyped upper uri usage user user_defined_type_catalog user_defined_type_code user_defined_type_name user_defined_type_schema using vacuum valid validate validator value value_of varbinary variadic var_pop var_samp verbose version versioning view views volatile when whenever whitespace width_bucket window within work wrapper write xmlagg xmlattributes xmlbinary xmlcast xmlcomment xmlconcat xmldeclaration xmldocument xmlelement xmlexists xmlforest xmliterate xmlnamespaces xmlparse xmlpi xmlquery xmlroot xmlschema xmlserialize xmltable xmltext xmlvalidate year yes loop repeat attach path depends detach zone"), + keywords: set(sqlKeywords + "a abort abs absent absolute access according action ada add admin after aggregate all allocate also always analyse analyze any are array array_agg array_max_cardinality asensitive assertion assignment asymmetric at atomic attribute attributes authorization avg backward base64 before begin begin_frame begin_partition bernoulli binary bit_length blob blocked bom both breadth c cache call called cardinality cascade cascaded case cast catalog catalog_name ceil ceiling chain characteristics characters character_length character_set_catalog character_set_name character_set_schema char_length check checkpoint class class_origin clob close cluster coalesce cobol collate collation collation_catalog collation_name collation_schema collect column columns column_name command_function command_function_code comment comments commit committed concurrently condition condition_number configuration conflict connect connection connection_name constraint constraints constraint_catalog constraint_name constraint_schema constructor contains content continue control conversion convert copy corr corresponding cost covar_pop covar_samp cross csv cube cume_dist current current_catalog current_date current_default_transform_group current_path current_role current_row current_schema current_time current_timestamp current_transform_group_for_type current_user cursor cursor_name cycle data database datalink datetime_interval_code datetime_interval_precision day db deallocate dec declare default defaults deferrable deferred defined definer degree delimiter delimiters dense_rank depth deref derived describe descriptor deterministic diagnostics dictionary disable discard disconnect dispatch dlnewcopy dlpreviouscopy dlurlcomplete dlurlcompleteonly dlurlcompletewrite dlurlpath dlurlpathonly dlurlpathwrite dlurlscheme dlurlserver dlvalue do document domain dynamic dynamic_function dynamic_function_code each element else empty enable encoding encrypted end end-exec end_frame end_partition enforced enum equals escape event every except exception exclude excluding exclusive exec execute exists exp explain expression extension external extract false family fetch file filter final first first_value flag float floor following for force foreign fortran forward found frame_row free freeze fs full function functions fusion g general generated get gettoken global go goto grant granted greatest grouping groups handler header headline hex hierarchy hold hour id identity if ignore ilike immediate immediately immutable implementation implicit import including increment indent index indexes indicator inherit inherits initially inline inner inout input insensitive instance instantiable instead integrity intersect intersection invoker isnull isolation k key key_member key_type label lag language large last last_value lateral lc_collate lc_ctype lead leading leakproof least left length level lextypes library like_regex link listen ln load local localtime localtimestamp location locator lock locked logged lower m map mapping match matched materialized max maxvalue max_cardinality member merge message_length message_octet_length message_text method min minute minvalue mod mode modifies module month more move multiset mumps name names namespace national natural nchar nclob nesting new next nfc nfd nfkc nfkd nil no none normalize normalized nothing notify notnull nowait nth_value ntile null nullable nullif nulls number object occurrences_regex octets octet_length of off offset oids old only open operator option options ordering ordinality others out outer output over overlaps overlay overriding owned owner p pad parallel parameter parameter_mode parameter_name parameter_ordinal_position parameter_specific_catalog parameter_specific_name parameter_specific_schema parser partial partition pascal passing passthrough password percent percentile_cont percentile_disc percent_rank period permission placing plans pli policy portion position position_regex power precedes preceding prepare prepared preserve primary prior privileges procedural procedure program public quote range rank read reads reassign recheck recovery recursive ref references referencing refresh regr_avgx regr_avgy regr_count regr_intercept regr_r2 regr_slope regr_sxx regr_sxy regr_syy reindex relative release rename repeatable replace replica requiring reset respect restart restore restrict restricted result return returned_cardinality returned_length returned_octet_length returned_sqlstate returning returns revoke right role rollback rollup routine routine_catalog routine_name routine_schema row rows row_count row_number rule savepoint scale schema schema_name scope scope_catalog scope_name scope_schema scroll search second section security selective self sensitive sequence sequences serializable server server_name session session_user setof sets share show similar simple size skip snapshot some source space specific specifictype specific_name sql sqlcode sqlerror sqlexception sqlstate sqlwarning sqrt stable standalone start state statement static statistics stddev_pop stddev_samp stdin stdout storage strict strip structure style subclass_origin submultiset substring substring_regex succeeds sum symmetric sysid system system_time system_user t tables tablesample tablespace table_name temp template temporary then ties timezone_hour timezone_minute to token top_level_count trailing transaction transactions_committed transactions_rolled_back transaction_active transform transforms translate translate_regex translation treat trigger trigger_catalog trigger_name trigger_schema trim trim_array true truncate trusted type types uescape unbounded uncommitted under unencrypted unique unknown unlink unlisten unlogged unnamed unnest until untyped upper uri usage user user_defined_type_catalog user_defined_type_code user_defined_type_name user_defined_type_schema using vacuum valid validate validator value value_of varbinary variadic var_pop var_samp verbose version versioning view views volatile when whenever whitespace width_bucket window within work wrapper write xmlagg xmlattributes xmlbinary xmlcast xmlcomment xmlconcat xmldeclaration xmldocument xmlelement xmlexists xmlforest xmliterate xmlnamespaces xmlparse xmlpi xmlquery xmlroot xmlschema xmlserialize xmltable xmltext xmlvalidate year yes loop repeat attach path depends detach zone"), // https://www.postgresql.org/docs/10/static/datatype.html builtin: set("bigint int8 bigserial serial8 bit varying varbit boolean bool box bytea character char varchar cidr circle date double precision float8 inet integer int int4 interval json jsonb line lseg macaddr macaddr8 money numeric decimal path pg_lsn point polygon real float4 smallint int2 smallserial serial2 serial serial4 text time without zone with timetz timestamp timestamptz tsquery tsvector txid_snapshot uuid xml"), atoms: set("false true null unknown"), From b3b45b852e7322af2664b95a643114c4ad1cd339 Mon Sep 17 00:00:00 2001 From: Christopher Wallis Date: Sat, 8 Dec 2018 12:37:24 -0700 Subject: [PATCH 1477/1790] [soy mode] Improve namespace highlighting - highlighted the namespace itself as a variable. This is in line with how Java packages are highlighted. I believe this is a close approximation. - added 2 tests to verify new highlighting. --- mode/soy/soy.js | 9 +++++++++ mode/soy/test.js | 7 +++++++ 2 files changed, 16 insertions(+) diff --git a/mode/soy/soy.js b/mode/soy/soy.js index 51ec46ca..752130af 100644 --- a/mode/soy/soy.js +++ b/mode/soy/soy.js @@ -182,6 +182,14 @@ stream.next(); return null; + case "namespace-def": + if (match = stream.match(/^\.?([\w\.]+)/)) { + state.soyState.pop(); + return "variable"; + } + stream.next(); + return null; + case "param-def": if (match = stream.match(/^\w+/)) { state.variables = prepend(state.variables, match[0]); @@ -297,6 +305,7 @@ state.scopes = prepend(state.scopes, state.variables); state.soyState.push("var-def"); } else if (state.tag == "namespace") { + state.soyState.push("namespace-def"); if (!state.scopes) { state.variables = prepend(null, 'ij'); } diff --git a/mode/soy/test.js b/mode/soy/test.js index 12515eda..bb437a8a 100644 --- a/mode/soy/test.js +++ b/mode/soy/test.js @@ -20,6 +20,13 @@ '[keyword {/template}]', ''); + MT('namespace-test', + '[keyword {namespace] [variable namespace][keyword }]') + + MT('namespace-with-attribute-test', + '[keyword {namespace] [variable my.namespace.templates] ' + + '[attribute requirecss]=[string "my.namespace"][keyword }]'); + MT('param-type-test', '[keyword {@param] [def a]: ' + '[variable-3 list]<[[[variable-3 a]: [variable-3 int], ' + From 7192a89300611014d56f37a348662812791d08e6 Mon Sep 17 00:00:00 2001 From: Neil Anderson Date: Mon, 10 Dec 2018 03:53:13 -0500 Subject: [PATCH 1478/1790] [sql mode] Add LEXIZE and INIT keywords for postgresql see https://www.postgresql.org/docs/11/sql-createtstemplate.html --- 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 0ae54a64..9a046f48 100644 --- a/mode/sql/sql.js +++ b/mode/sql/sql.js @@ -411,7 +411,7 @@ CodeMirror.defineMode("sql", function(config, parserConfig) { name: "sql", client: set("source"), // https://www.postgresql.org/docs/10/static/sql-keywords-appendix.html - keywords: set(sqlKeywords + "a abort abs absent absolute access according action ada add admin after aggregate all allocate also always analyse analyze any are array array_agg array_max_cardinality asensitive assertion assignment asymmetric at atomic attribute attributes authorization avg backward base64 before begin begin_frame begin_partition bernoulli binary bit_length blob blocked bom both breadth c cache call called cardinality cascade cascaded case cast catalog catalog_name ceil ceiling chain characteristics characters character_length character_set_catalog character_set_name character_set_schema char_length check checkpoint class class_origin clob close cluster coalesce cobol collate collation collation_catalog collation_name collation_schema collect column columns column_name command_function command_function_code comment comments commit committed concurrently condition condition_number configuration conflict connect connection connection_name constraint constraints constraint_catalog constraint_name constraint_schema constructor contains content continue control conversion convert copy corr corresponding cost covar_pop covar_samp cross csv cube cume_dist current current_catalog current_date current_default_transform_group current_path current_role current_row current_schema current_time current_timestamp current_transform_group_for_type current_user cursor cursor_name cycle data database datalink datetime_interval_code datetime_interval_precision day db deallocate dec declare default defaults deferrable deferred defined definer degree delimiter delimiters dense_rank depth deref derived describe descriptor deterministic diagnostics dictionary disable discard disconnect dispatch dlnewcopy dlpreviouscopy dlurlcomplete dlurlcompleteonly dlurlcompletewrite dlurlpath dlurlpathonly dlurlpathwrite dlurlscheme dlurlserver dlvalue do document domain dynamic dynamic_function dynamic_function_code each element else empty enable encoding encrypted end end-exec end_frame end_partition enforced enum equals escape event every except exception exclude excluding exclusive exec execute exists exp explain expression extension external extract false family fetch file filter final first first_value flag float floor following for force foreign fortran forward found frame_row free freeze fs full function functions fusion g general generated get gettoken global go goto grant granted greatest grouping groups handler header headline hex hierarchy hold hour id identity if ignore ilike immediate immediately immutable implementation implicit import including increment indent index indexes indicator inherit inherits initially inline inner inout input insensitive instance instantiable instead integrity intersect intersection invoker isnull isolation k key key_member key_type label lag language large last last_value lateral lc_collate lc_ctype lead leading leakproof least left length level lextypes library like_regex link listen ln load local localtime localtimestamp location locator lock locked logged lower m map mapping match matched materialized max maxvalue max_cardinality member merge message_length message_octet_length message_text method min minute minvalue mod mode modifies module month more move multiset mumps name names namespace national natural nchar nclob nesting new next nfc nfd nfkc nfkd nil no none normalize normalized nothing notify notnull nowait nth_value ntile null nullable nullif nulls number object occurrences_regex octets octet_length of off offset oids old only open operator option options ordering ordinality others out outer output over overlaps overlay overriding owned owner p pad parallel parameter parameter_mode parameter_name parameter_ordinal_position parameter_specific_catalog parameter_specific_name parameter_specific_schema parser partial partition pascal passing passthrough password percent percentile_cont percentile_disc percent_rank period permission placing plans pli policy portion position position_regex power precedes preceding prepare prepared preserve primary prior privileges procedural procedure program public quote range rank read reads reassign recheck recovery recursive ref references referencing refresh regr_avgx regr_avgy regr_count regr_intercept regr_r2 regr_slope regr_sxx regr_sxy regr_syy reindex relative release rename repeatable replace replica requiring reset respect restart restore restrict restricted result return returned_cardinality returned_length returned_octet_length returned_sqlstate returning returns revoke right role rollback rollup routine routine_catalog routine_name routine_schema row rows row_count row_number rule savepoint scale schema schema_name scope scope_catalog scope_name scope_schema scroll search second section security selective self sensitive sequence sequences serializable server server_name session session_user setof sets share show similar simple size skip snapshot some source space specific specifictype specific_name sql sqlcode sqlerror sqlexception sqlstate sqlwarning sqrt stable standalone start state statement static statistics stddev_pop stddev_samp stdin stdout storage strict strip structure style subclass_origin submultiset substring substring_regex succeeds sum symmetric sysid system system_time system_user t tables tablesample tablespace table_name temp template temporary then ties timezone_hour timezone_minute to token top_level_count trailing transaction transactions_committed transactions_rolled_back transaction_active transform transforms translate translate_regex translation treat trigger trigger_catalog trigger_name trigger_schema trim trim_array true truncate trusted type types uescape unbounded uncommitted under unencrypted unique unknown unlink unlisten unlogged unnamed unnest until untyped upper uri usage user user_defined_type_catalog user_defined_type_code user_defined_type_name user_defined_type_schema using vacuum valid validate validator value value_of varbinary variadic var_pop var_samp verbose version versioning view views volatile when whenever whitespace width_bucket window within work wrapper write xmlagg xmlattributes xmlbinary xmlcast xmlcomment xmlconcat xmldeclaration xmldocument xmlelement xmlexists xmlforest xmliterate xmlnamespaces xmlparse xmlpi xmlquery xmlroot xmlschema xmlserialize xmltable xmltext xmlvalidate year yes loop repeat attach path depends detach zone"), + keywords: set(sqlKeywords + "a abort abs absent absolute access according action ada add admin after aggregate all allocate also always analyse analyze any are array array_agg array_max_cardinality asensitive assertion assignment asymmetric at atomic attribute attributes authorization avg backward base64 before begin begin_frame begin_partition bernoulli binary bit_length blob blocked bom both breadth c cache call called cardinality cascade cascaded case cast catalog catalog_name ceil ceiling chain characteristics characters character_length character_set_catalog character_set_name character_set_schema char_length check checkpoint class class_origin clob close cluster coalesce cobol collate collation collation_catalog collation_name collation_schema collect column columns column_name command_function command_function_code comment comments commit committed concurrently condition condition_number configuration conflict connect connection connection_name constraint constraints constraint_catalog constraint_name constraint_schema constructor contains content continue control conversion convert copy corr corresponding cost covar_pop covar_samp cross csv cube cume_dist current current_catalog current_date current_default_transform_group current_path current_role current_row current_schema current_time current_timestamp current_transform_group_for_type current_user cursor cursor_name cycle data database datalink datetime_interval_code datetime_interval_precision day db deallocate dec declare default defaults deferrable deferred defined definer degree delimiter delimiters dense_rank depth deref derived describe descriptor deterministic diagnostics dictionary disable discard disconnect dispatch dlnewcopy dlpreviouscopy dlurlcomplete dlurlcompleteonly dlurlcompletewrite dlurlpath dlurlpathonly dlurlpathwrite dlurlscheme dlurlserver dlvalue do document domain dynamic dynamic_function dynamic_function_code each element else empty enable encoding encrypted end end-exec end_frame end_partition enforced enum equals escape event every except exception exclude excluding exclusive exec execute exists exp explain expression extension external extract false family fetch file filter final first first_value flag float floor following for force foreign fortran forward found frame_row free freeze fs full function functions fusion g general generated get gettoken global go goto grant granted greatest grouping groups handler header headline hex hierarchy hold hour id identity if ignore ilike immediate immediately immutable implementation implicit import including increment indent index indexes indicator inherit inherits init initially inline inner inout input insensitive instance instantiable instead integrity intersect intersection invoker isnull isolation k key key_member key_type label lag language large last last_value lateral lc_collate lc_ctype lead leading leakproof least left length level lexize lextypes library like_regex link listen ln load local localtime localtimestamp location locator lock locked logged lower m map mapping match matched materialized max maxvalue max_cardinality member merge message_length message_octet_length message_text method min minute minvalue mod mode modifies module month more move multiset mumps name names namespace national natural nchar nclob nesting new next nfc nfd nfkc nfkd nil no none normalize normalized nothing notify notnull nowait nth_value ntile null nullable nullif nulls number object occurrences_regex octets octet_length of off offset oids old only open operator option options ordering ordinality others out outer output over overlaps overlay overriding owned owner p pad parallel parameter parameter_mode parameter_name parameter_ordinal_position parameter_specific_catalog parameter_specific_name parameter_specific_schema parser partial partition pascal passing passthrough password percent percentile_cont percentile_disc percent_rank period permission placing plans pli policy portion position position_regex power precedes preceding prepare prepared preserve primary prior privileges procedural procedure program public quote range rank read reads reassign recheck recovery recursive ref references referencing refresh regr_avgx regr_avgy regr_count regr_intercept regr_r2 regr_slope regr_sxx regr_sxy regr_syy reindex relative release rename repeatable replace replica requiring reset respect restart restore restrict restricted result return returned_cardinality returned_length returned_octet_length returned_sqlstate returning returns revoke right role rollback rollup routine routine_catalog routine_name routine_schema row rows row_count row_number rule savepoint scale schema schema_name scope scope_catalog scope_name scope_schema scroll search second section security selective self sensitive sequence sequences serializable server server_name session session_user setof sets share show similar simple size skip snapshot some source space specific specifictype specific_name sql sqlcode sqlerror sqlexception sqlstate sqlwarning sqrt stable standalone start state statement static statistics stddev_pop stddev_samp stdin stdout storage strict strip structure style subclass_origin submultiset substring substring_regex succeeds sum symmetric sysid system system_time system_user t tables tablesample tablespace table_name temp template temporary then ties timezone_hour timezone_minute to token top_level_count trailing transaction transactions_committed transactions_rolled_back transaction_active transform transforms translate translate_regex translation treat trigger trigger_catalog trigger_name trigger_schema trim trim_array true truncate trusted type types uescape unbounded uncommitted under unencrypted unique unknown unlink unlisten unlogged unnamed unnest until untyped upper uri usage user user_defined_type_catalog user_defined_type_code user_defined_type_name user_defined_type_schema using vacuum valid validate validator value value_of varbinary variadic var_pop var_samp verbose version versioning view views volatile when whenever whitespace width_bucket window within work wrapper write xmlagg xmlattributes xmlbinary xmlcast xmlcomment xmlconcat xmldeclaration xmldocument xmlelement xmlexists xmlforest xmliterate xmlnamespaces xmlparse xmlpi xmlquery xmlroot xmlschema xmlserialize xmltable xmltext xmlvalidate year yes loop repeat attach path depends detach zone"), // https://www.postgresql.org/docs/10/static/datatype.html builtin: set("bigint int8 bigserial serial8 bit varying varbit boolean bool box bytea character char varchar cidr circle date double precision float8 inet integer int int4 interval json jsonb line lseg macaddr macaddr8 money numeric decimal path pg_lsn point polygon real float4 smallint int2 smallserial serial2 serial serial4 text time without zone with timetz timestamp timestamptz tsquery tsvector txid_snapshot uuid xml"), atoms: set("false true null unknown"), From 548975334b0c61a73fb6ec70ac87b64a4c9096f2 Mon Sep 17 00:00:00 2001 From: Christopher Wallis Date: Tue, 11 Dec 2018 01:36:16 -0700 Subject: [PATCH 1479/1790] [soy mode] Classify param type as type - changed classification of param-def-type to "type" instead of using "variable-3" - updated tests to match this expectation --- mode/soy/soy.js | 2 +- mode/soy/test.js | 14 +++++++------- 2 files changed, 8 insertions(+), 8 deletions(-) diff --git a/mode/soy/soy.js b/mode/soy/soy.js index 752130af..647e4599 100644 --- a/mode/soy/soy.js +++ b/mode/soy/soy.js @@ -206,7 +206,7 @@ return null; } if (stream.eatWhile(/^[\w]+/)) { - return "variable-3"; + return "type"; } stream.next(); return null; diff --git a/mode/soy/test.js b/mode/soy/test.js index bb437a8a..c670d517 100644 --- a/mode/soy/test.js +++ b/mode/soy/test.js @@ -29,16 +29,16 @@ MT('param-type-test', '[keyword {@param] [def a]: ' + - '[variable-3 list]<[[[variable-3 a]: [variable-3 int], ' + - '[variable-3 b]: [variable-3 map]<[variable-3 string], ' + - '[variable-3 bool]>]]>][keyword }]'); + '[type list]<[[[type a]: [type int], ' + + '[type b]: [type map]<[type string], ' + + '[type bool]>]]>][keyword }]'); MT('undefined-var', '[keyword {][variable-2&error $var]'); MT('param-scope-test', '[keyword {template] [def .a][keyword }]', - ' [keyword {@param] [def x]: [variable-3 string][keyword }]', + ' [keyword {@param] [def x]: [type string][keyword }]', ' [keyword {][variable-2 $x][keyword }]', '[keyword {/template}]', '', @@ -55,7 +55,7 @@ MT('defined-if-variable-test', '[keyword {template] [def .foo][keyword }]', - ' [keyword {@param?] [def showThing]: [variable-3 bool][keyword }]', + ' [keyword {@param?] [def showThing]: [type bool][keyword }]', ' [keyword {if] [variable-2 $showThing][keyword }]', ' Yo!', ' [keyword {/if}]', @@ -73,7 +73,7 @@ ''); MT('foreach-scope-test', - '[keyword {@param] [def bar]: [variable-3 string][keyword }]', + '[keyword {@param] [def bar]: [type string][keyword }]', '[keyword {foreach] [def $foo] [keyword in] [variable-2&error $foos][keyword }]', ' [keyword {][variable-2 $foo][keyword }]', '[keyword {/foreach}]', @@ -108,7 +108,7 @@ MT('allow-missing-colon-in-@param', '[keyword {template] [def .foo][keyword }]', - ' [keyword {@param] [def showThing] [variable-3 bool][keyword }]', + ' [keyword {@param] [def showThing] [type bool][keyword }]', ' [keyword {if] [variable-2 $showThing][keyword }]', ' Yo!', ' [keyword {/if}]', From 1b8c2daa6f1360545cf594012d573727854f3434 Mon Sep 17 00:00:00 2001 From: Christopher Wallis Date: Wed, 12 Dec 2018 02:05:30 -0700 Subject: [PATCH 1480/1790] [soy mode] Updated {call /} and .template token - updated "templ-ref" to match namespaced templates e.g. my.namespaced.component.element. The existing regex matches the first word and nothing after the "." - updated "templ-ref" to treat all "." templates as variable-2. Local templates are the only ones capable of starting with a dot and because a template earlier in the file can reference a local template later in the file, there are no guarantees of it being in the variable list. Treating all .templates as local is more consistent and predictable for syntax highlighting. --- mode/soy/soy.js | 6 +++--- mode/soy/test.js | 10 ++++------ 2 files changed, 7 insertions(+), 9 deletions(-) diff --git a/mode/soy/soy.js b/mode/soy/soy.js index 647e4599..8119361b 100644 --- a/mode/soy/soy.js +++ b/mode/soy/soy.js @@ -170,11 +170,11 @@ return null; case "templ-ref": - if (match = stream.match(/^\.?([\w]+)/)) { + if (match = stream.match(/(\.?[a-zA-Z_][a-zA-Z_0-9]+)+/)) { state.soyState.pop(); - // If the first character is '.', try to match against a local template name. + // If the first character is '.', it can only be a local template. if (match[0][0] == '.') { - return ref(state.templates, match[1], true); + return "variable-2" } // Otherwise return "variable"; diff --git a/mode/soy/test.js b/mode/soy/test.js index c670d517..ac38495a 100644 --- a/mode/soy/test.js +++ b/mode/soy/test.js @@ -63,13 +63,11 @@ ''); MT('template-calls-test', - '[keyword {template] [def .foo][keyword }]', - ' Yo!', - '[keyword {/template}]', '[keyword {call] [variable-2 .foo][keyword /}]', '[keyword {call] [variable foo][keyword /}]', - '[keyword {call] [variable .bar][keyword /}]', - '[keyword {call] [variable bar][keyword /}]', + '[keyword {call] [variable foo][keyword }] [keyword {/call}]', + '[keyword {call] [variable first1.second.third_3][keyword /}]', + '[keyword {call] [variable first1.second.third_3] [keyword }] [keyword {/call}]', ''); MT('foreach-scope-test', @@ -91,7 +89,7 @@ MT('nested-kind-test', '[keyword {template] [def .foo] [attribute kind]=[string "html"][keyword }]', ' [tag&bracket <][tag div][tag&bracket >]', - ' [keyword {call] [variable .bar][keyword }]', + ' [keyword {call] [variable-2 .bar][keyword }]', ' [keyword {param] [attribute kind]=[string "js"][keyword }]', ' [keyword var] [def bar] [operator =] [number 5];', ' [keyword {/param}]', From 1175d7eb0a3c48e6ac186cbf4b39ba0ba3e1d259 Mon Sep 17 00:00:00 2001 From: Christopher Wallis Date: Wed, 12 Dec 2018 09:58:10 -0700 Subject: [PATCH 1481/1790] [soy mode] tokenize soy functions - added tokenization for soy functions. Assigned to variable & callee - added tests to verify it works within expressions --- mode/soy/soy.js | 2 ++ mode/soy/test.js | 10 ++++++++-- 2 files changed, 10 insertions(+), 2 deletions(-) diff --git a/mode/soy/soy.js b/mode/soy/soy.js index 8119361b..b400dd32 100644 --- a/mode/soy/soy.js +++ b/mode/soy/soy.js @@ -251,6 +251,8 @@ }); } return "attribute"; + } else if (match = stream.match(/([\w]+)(?=\()/)) { + return "variable callee"; } else if (match = stream.match(/^["']/)) { state.soyState.push("string"); state.quoteKind = match; diff --git a/mode/soy/test.js b/mode/soy/test.js index ac38495a..5c731d51 100644 --- a/mode/soy/test.js +++ b/mode/soy/test.js @@ -20,6 +20,12 @@ '[keyword {/template}]', ''); + MT('function-test', + '[keyword {] [callee&variable css]([string "MyClass"])[keyword }]', + '[tag&bracket <][tag input] [attribute value]=[string "]' + + '[keyword {] [callee&variable index]([variable-2&error $list])[keyword }]' + + '[string "][tag&bracket />]'); + MT('namespace-test', '[keyword {namespace] [variable namespace][keyword }]') @@ -99,9 +105,9 @@ ''); MT('tag-starting-with-function-call-is-not-a-keyword', - '[keyword {]index([variable-2&error $foo])[keyword }]', + '[keyword {][callee&variable index]([variable-2&error $foo])[keyword }]', '[keyword {css] [string "some-class"][keyword }]', - '[keyword {]css([string "some-class"])[keyword }]', + '[keyword {][callee&variable css]([string "some-class"])[keyword }]', ''); MT('allow-missing-colon-in-@param', From 797b61055f2f392689e2767da8cf2e4e065a2508 Mon Sep 17 00:00:00 2001 From: Christopher Wallis Date: Thu, 13 Dec 2018 08:45:43 -0700 Subject: [PATCH 1482/1790] [soy mode] tokenize property names in {param} - added token "property" to `name` in `{param name: '' /}` - adjusted nested-kind-test for this change. --- mode/soy/soy.js | 10 ++++++++++ mode/soy/test.js | 2 +- 2 files changed, 11 insertions(+), 1 deletion(-) diff --git a/mode/soy/soy.js b/mode/soy/soy.js index b400dd32..9e88007a 100644 --- a/mode/soy/soy.js +++ b/mode/soy/soy.js @@ -200,6 +200,14 @@ stream.next(); return null; + case "param-ref": + if (match = stream.match(/^\w+/)) { + state.soyState.pop(); + return "property"; + } + stream.next(); + return null; + case "param-type": if (stream.peek() == "}") { state.soyState.pop(); @@ -313,6 +321,8 @@ } } else if (state.tag.match(/^@(?:param\??|inject|prop)/)) { state.soyState.push("param-def"); + } else if (state.tag.match(/^(?:param)/)) { + state.soyState.push("param-ref"); } return "keyword"; diff --git a/mode/soy/test.js b/mode/soy/test.js index 5c731d51..4afaa3d0 100644 --- a/mode/soy/test.js +++ b/mode/soy/test.js @@ -96,7 +96,7 @@ '[keyword {template] [def .foo] [attribute kind]=[string "html"][keyword }]', ' [tag&bracket <][tag div][tag&bracket >]', ' [keyword {call] [variable-2 .bar][keyword }]', - ' [keyword {param] [attribute kind]=[string "js"][keyword }]', + ' [keyword {param] [property propertyName] [attribute kind]=[string "js"][keyword }]', ' [keyword var] [def bar] [operator =] [number 5];', ' [keyword {/param}]', ' [keyword {/call}]', From 0546da3274f106255161048d1470a72da817dd32 Mon Sep 17 00:00:00 2001 From: Germain Chazot Date: Fri, 14 Dec 2018 15:16:13 +0000 Subject: [PATCH 1483/1790] [yaml-lint addon] Allow linting of multi-document yaml stream --- addon/lint/yaml-lint.js | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/addon/lint/yaml-lint.js b/addon/lint/yaml-lint.js index 369c95b5..b4ac5abc 100644 --- a/addon/lint/yaml-lint.js +++ b/addon/lint/yaml-lint.js @@ -23,7 +23,7 @@ CodeMirror.registerHelper("lint", "yaml", function(text) { } return found; } - try { jsyaml.load(text); } + try { jsyaml.loadAll(text); } catch(e) { var loc = e.mark, // js-yaml YAMLException doesn't always provide an accurate lineno From eb174cff533158b16168a96aabb3e164a360b840 Mon Sep 17 00:00:00 2001 From: Neil Anderson Date: Sat, 15 Dec 2018 03:53:09 -0500 Subject: [PATCH 1484/1790] [sql mode] Add PUBLICATION postgres keyword see https://www.postgresql.org/docs/10/sql-createpublication.html --- 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 9a046f48..9687ca38 100644 --- a/mode/sql/sql.js +++ b/mode/sql/sql.js @@ -411,7 +411,7 @@ CodeMirror.defineMode("sql", function(config, parserConfig) { name: "sql", client: set("source"), // https://www.postgresql.org/docs/10/static/sql-keywords-appendix.html - keywords: set(sqlKeywords + "a abort abs absent absolute access according action ada add admin after aggregate all allocate also always analyse analyze any are array array_agg array_max_cardinality asensitive assertion assignment asymmetric at atomic attribute attributes authorization avg backward base64 before begin begin_frame begin_partition bernoulli binary bit_length blob blocked bom both breadth c cache call called cardinality cascade cascaded case cast catalog catalog_name ceil ceiling chain characteristics characters character_length character_set_catalog character_set_name character_set_schema char_length check checkpoint class class_origin clob close cluster coalesce cobol collate collation collation_catalog collation_name collation_schema collect column columns column_name command_function command_function_code comment comments commit committed concurrently condition condition_number configuration conflict connect connection connection_name constraint constraints constraint_catalog constraint_name constraint_schema constructor contains content continue control conversion convert copy corr corresponding cost covar_pop covar_samp cross csv cube cume_dist current current_catalog current_date current_default_transform_group current_path current_role current_row current_schema current_time current_timestamp current_transform_group_for_type current_user cursor cursor_name cycle data database datalink datetime_interval_code datetime_interval_precision day db deallocate dec declare default defaults deferrable deferred defined definer degree delimiter delimiters dense_rank depth deref derived describe descriptor deterministic diagnostics dictionary disable discard disconnect dispatch dlnewcopy dlpreviouscopy dlurlcomplete dlurlcompleteonly dlurlcompletewrite dlurlpath dlurlpathonly dlurlpathwrite dlurlscheme dlurlserver dlvalue do document domain dynamic dynamic_function dynamic_function_code each element else empty enable encoding encrypted end end-exec end_frame end_partition enforced enum equals escape event every except exception exclude excluding exclusive exec execute exists exp explain expression extension external extract false family fetch file filter final first first_value flag float floor following for force foreign fortran forward found frame_row free freeze fs full function functions fusion g general generated get gettoken global go goto grant granted greatest grouping groups handler header headline hex hierarchy hold hour id identity if ignore ilike immediate immediately immutable implementation implicit import including increment indent index indexes indicator inherit inherits init initially inline inner inout input insensitive instance instantiable instead integrity intersect intersection invoker isnull isolation k key key_member key_type label lag language large last last_value lateral lc_collate lc_ctype lead leading leakproof least left length level lexize lextypes library like_regex link listen ln load local localtime localtimestamp location locator lock locked logged lower m map mapping match matched materialized max maxvalue max_cardinality member merge message_length message_octet_length message_text method min minute minvalue mod mode modifies module month more move multiset mumps name names namespace national natural nchar nclob nesting new next nfc nfd nfkc nfkd nil no none normalize normalized nothing notify notnull nowait nth_value ntile null nullable nullif nulls number object occurrences_regex octets octet_length of off offset oids old only open operator option options ordering ordinality others out outer output over overlaps overlay overriding owned owner p pad parallel parameter parameter_mode parameter_name parameter_ordinal_position parameter_specific_catalog parameter_specific_name parameter_specific_schema parser partial partition pascal passing passthrough password percent percentile_cont percentile_disc percent_rank period permission placing plans pli policy portion position position_regex power precedes preceding prepare prepared preserve primary prior privileges procedural procedure program public quote range rank read reads reassign recheck recovery recursive ref references referencing refresh regr_avgx regr_avgy regr_count regr_intercept regr_r2 regr_slope regr_sxx regr_sxy regr_syy reindex relative release rename repeatable replace replica requiring reset respect restart restore restrict restricted result return returned_cardinality returned_length returned_octet_length returned_sqlstate returning returns revoke right role rollback rollup routine routine_catalog routine_name routine_schema row rows row_count row_number rule savepoint scale schema schema_name scope scope_catalog scope_name scope_schema scroll search second section security selective self sensitive sequence sequences serializable server server_name session session_user setof sets share show similar simple size skip snapshot some source space specific specifictype specific_name sql sqlcode sqlerror sqlexception sqlstate sqlwarning sqrt stable standalone start state statement static statistics stddev_pop stddev_samp stdin stdout storage strict strip structure style subclass_origin submultiset substring substring_regex succeeds sum symmetric sysid system system_time system_user t tables tablesample tablespace table_name temp template temporary then ties timezone_hour timezone_minute to token top_level_count trailing transaction transactions_committed transactions_rolled_back transaction_active transform transforms translate translate_regex translation treat trigger trigger_catalog trigger_name trigger_schema trim trim_array true truncate trusted type types uescape unbounded uncommitted under unencrypted unique unknown unlink unlisten unlogged unnamed unnest until untyped upper uri usage user user_defined_type_catalog user_defined_type_code user_defined_type_name user_defined_type_schema using vacuum valid validate validator value value_of varbinary variadic var_pop var_samp verbose version versioning view views volatile when whenever whitespace width_bucket window within work wrapper write xmlagg xmlattributes xmlbinary xmlcast xmlcomment xmlconcat xmldeclaration xmldocument xmlelement xmlexists xmlforest xmliterate xmlnamespaces xmlparse xmlpi xmlquery xmlroot xmlschema xmlserialize xmltable xmltext xmlvalidate year yes loop repeat attach path depends detach zone"), + keywords: set(sqlKeywords + "a abort abs absent absolute access according action ada add admin after aggregate all allocate also always analyse analyze any are array array_agg array_max_cardinality asensitive assertion assignment asymmetric at atomic attribute attributes authorization avg backward base64 before begin begin_frame begin_partition bernoulli binary bit_length blob blocked bom both breadth c cache call called cardinality cascade cascaded case cast catalog catalog_name ceil ceiling chain characteristics characters character_length character_set_catalog character_set_name character_set_schema char_length check checkpoint class class_origin clob close cluster coalesce cobol collate collation collation_catalog collation_name collation_schema collect column columns column_name command_function command_function_code comment comments commit committed concurrently condition condition_number configuration conflict connect connection connection_name constraint constraints constraint_catalog constraint_name constraint_schema constructor contains content continue control conversion convert copy corr corresponding cost covar_pop covar_samp cross csv cube cume_dist current current_catalog current_date current_default_transform_group current_path current_role current_row current_schema current_time current_timestamp current_transform_group_for_type current_user cursor cursor_name cycle data database datalink datetime_interval_code datetime_interval_precision day db deallocate dec declare default defaults deferrable deferred defined definer degree delimiter delimiters dense_rank depth deref derived describe descriptor deterministic diagnostics dictionary disable discard disconnect dispatch dlnewcopy dlpreviouscopy dlurlcomplete dlurlcompleteonly dlurlcompletewrite dlurlpath dlurlpathonly dlurlpathwrite dlurlscheme dlurlserver dlvalue do document domain dynamic dynamic_function dynamic_function_code each element else empty enable encoding encrypted end end-exec end_frame end_partition enforced enum equals escape event every except exception exclude excluding exclusive exec execute exists exp explain expression extension external extract false family fetch file filter final first first_value flag float floor following for force foreign fortran forward found frame_row free freeze fs full function functions fusion g general generated get gettoken global go goto grant granted greatest grouping groups handler header headline hex hierarchy hold hour id identity if ignore ilike immediate immediately immutable implementation implicit import including increment indent index indexes indicator inherit inherits init initially inline inner inout input insensitive instance instantiable instead integrity intersect intersection invoker isnull isolation k key key_member key_type label lag language large last last_value lateral lc_collate lc_ctype lead leading leakproof least left length level lexize lextypes library like_regex link listen ln load local localtime localtimestamp location locator lock locked logged lower m map mapping match matched materialized max maxvalue max_cardinality member merge message_length message_octet_length message_text method min minute minvalue mod mode modifies module month more move multiset mumps name names namespace national natural nchar nclob nesting new next nfc nfd nfkc nfkd nil no none normalize normalized nothing notify notnull nowait nth_value ntile null nullable nullif nulls number object occurrences_regex octets octet_length of off offset oids old only open operator option options ordering ordinality others out outer output over overlaps overlay overriding owned owner p pad parallel parameter parameter_mode parameter_name parameter_ordinal_position parameter_specific_catalog parameter_specific_name parameter_specific_schema parser partial partition pascal passing passthrough password percent percentile_cont percentile_disc percent_rank period permission placing plans pli policy portion position position_regex power precedes preceding prepare prepared preserve primary prior privileges procedural procedure program public publication quote range rank read reads reassign recheck recovery recursive ref references referencing refresh regr_avgx regr_avgy regr_count regr_intercept regr_r2 regr_slope regr_sxx regr_sxy regr_syy reindex relative release rename repeatable replace replica requiring reset respect restart restore restrict restricted result return returned_cardinality returned_length returned_octet_length returned_sqlstate returning returns revoke right role rollback rollup routine routine_catalog routine_name routine_schema row rows row_count row_number rule savepoint scale schema schema_name scope scope_catalog scope_name scope_schema scroll search second section security selective self sensitive sequence sequences serializable server server_name session session_user setof sets share show similar simple size skip snapshot some source space specific specifictype specific_name sql sqlcode sqlerror sqlexception sqlstate sqlwarning sqrt stable standalone start state statement static statistics stddev_pop stddev_samp stdin stdout storage strict strip structure style subclass_origin submultiset substring substring_regex succeeds sum symmetric sysid system system_time system_user t tables tablesample tablespace table_name temp template temporary then ties timezone_hour timezone_minute to token top_level_count trailing transaction transactions_committed transactions_rolled_back transaction_active transform transforms translate translate_regex translation treat trigger trigger_catalog trigger_name trigger_schema trim trim_array true truncate trusted type types uescape unbounded uncommitted under unencrypted unique unknown unlink unlisten unlogged unnamed unnest until untyped upper uri usage user user_defined_type_catalog user_defined_type_code user_defined_type_name user_defined_type_schema using vacuum valid validate validator value value_of varbinary variadic var_pop var_samp verbose version versioning view views volatile when whenever whitespace width_bucket window within work wrapper write xmlagg xmlattributes xmlbinary xmlcast xmlcomment xmlconcat xmldeclaration xmldocument xmlelement xmlexists xmlforest xmliterate xmlnamespaces xmlparse xmlpi xmlquery xmlroot xmlschema xmlserialize xmltable xmltext xmlvalidate year yes loop repeat attach path depends detach zone"), // https://www.postgresql.org/docs/10/static/datatype.html builtin: set("bigint int8 bigserial serial8 bit varying varbit boolean bool box bytea character char varchar cidr circle date double precision float8 inet integer int int4 interval json jsonb line lseg macaddr macaddr8 money numeric decimal path pg_lsn point polygon real float4 smallint int2 smallserial serial2 serial serial4 text time without zone with timetz timestamp timestamptz tsquery tsvector txid_snapshot uuid xml"), atoms: set("false true null unknown"), From 9c1df7b6135de93c652ddcccfa3194a2ee8fd29a Mon Sep 17 00:00:00 2001 From: fraxx001 Date: Sat, 15 Dec 2018 18:37:53 +0100 Subject: [PATCH 1485/1790] [vim bindings] Fix/keymap/vim ctrl w idle --- keymap/vim.js | 2 ++ test/vim_test.js | 10 ++++++++++ 2 files changed, 12 insertions(+) diff --git a/keymap/vim.js b/keymap/vim.js index da10c1a9..68465559 100644 --- a/keymap/vim.js +++ b/keymap/vim.js @@ -152,6 +152,8 @@ { keys: '~', type: 'operatorMotion', operator: 'changeCase', motion: 'moveByCharacters', motionArgs: { forward: true }, operatorArgs: { shouldMoveCursor: true }, context: 'normal'}, { keys: '~', type: 'operator', operator: 'changeCase', context: 'visual'}, { keys: '', type: 'operatorMotion', operator: 'delete', motion: 'moveByWords', motionArgs: { forward: false, wordEnd: false }, context: 'insert' }, + //ignore C-w in normal mode + { keys: '', type: 'idle', context: 'normal' }, // Actions { keys: '', type: 'action', action: 'jumpListWalk', actionArgs: { forward: true }}, { keys: '', type: 'action', action: 'jumpListWalk', actionArgs: { forward: false }}, diff --git a/test/vim_test.js b/test/vim_test.js index 939aee5f..e3f95ccd 100644 --- a/test/vim_test.js +++ b/test/vim_test.js @@ -1446,6 +1446,16 @@ testVim('insert_ctrl_w', function(cm, vim, helpers) { eqCursorPos(curEnd, cm.getCursor()); eq('vim-insert', cm.getOption('keyMap')); }, { value: 'word1/word2' }); +testVim('normal_ctrl_w', function(cm, vim, helpers) { + var curStart = makeCursor(0, 3); + cm.setCursor(curStart); + helpers.doKeys(''); + eq('word', cm.getValue()); + var curEnd = makeCursor(0, 3); + helpers.assertCursorAt(0,3); + eqCursorPos(curEnd, cm.getCursor()); + eq('vim', cm.getOption('keyMap')); +}, {value: 'word'}); testVim('a', function(cm, vim, helpers) { cm.setCursor(0, 1); helpers.doKeys('a'); From 3675309aa9a9950a34c0e5cdc5a79e88d4ef9bc5 Mon Sep 17 00:00:00 2001 From: Marijn Haverbeke Date: Tue, 18 Dec 2018 10:09:59 +0100 Subject: [PATCH 1486/1790] Fix issue where bracket matching was unintentionally including <> by default Closes codemirror/google-modes#233 --- addon/edit/matchbrackets.js | 2 +- keymap/vim.js | 2 +- 2 files changed, 2 insertions(+), 2 deletions(-) diff --git a/addon/edit/matchbrackets.js b/addon/edit/matchbrackets.js index 6ad5a012..5b03201a 100644 --- a/addon/edit/matchbrackets.js +++ b/addon/edit/matchbrackets.js @@ -51,7 +51,7 @@ var maxScanLines = (config && config.maxScanLines) || 1000; var stack = []; - var re = config && config.bracketRegex ? config.bracketRegex : /[(){}[\]<>]/; + var re = config && config.bracketRegex ? config.bracketRegex : /[(){}[\]]/; var lineEnd = dir > 0 ? Math.min(where.line + maxScanLines, cm.lastLine() + 1) : Math.max(cm.firstLine() - 1, where.line - maxScanLines); for (var lineNo = where.line; lineNo != lineEnd; lineNo += dir) { diff --git a/keymap/vim.js b/keymap/vim.js index 68465559..0af9bcb2 100644 --- a/keymap/vim.js +++ b/keymap/vim.js @@ -1977,7 +1977,7 @@ } } if (ch < lineText.length) { - var matched = cm.findMatchingBracket(Pos(line, ch)); + var matched = cm.findMatchingBracket(Pos(line, ch), {bracketRegex: /[(){}[\]<>]/}); return matched.to; } else { return cursor; From 0327f531fcac31b22cba16ae114cde370a73cd25 Mon Sep 17 00:00:00 2001 From: Marijn Haverbeke Date: Tue, 18 Dec 2018 10:12:25 +0100 Subject: [PATCH 1487/1790] [python mode] Fix bug that could cause crashes with some format strings Closes #5713 --- mode/python/python.js | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/mode/python/python.js b/mode/python/python.js index 49f1ec4d..224eb7d5 100644 --- a/mode/python/python.js +++ b/mode/python/python.js @@ -219,7 +219,7 @@ // switch to nested mode state.tokenize = tokenNestedExpr(0) if (stream.current()) return OUTCLASS; - else return state.tokenize() + else return state.tokenize(stream, state) } else if (stream.match('}}')) { return OUTCLASS; } else if (stream.match('}')) { From 63b591cefb3fc6cf0b82b6d05c1b870ea8c7b085 Mon Sep 17 00:00:00 2001 From: Marijn Haverbeke Date: Tue, 18 Dec 2018 13:02:14 +0100 Subject: [PATCH 1488/1790] [sql mode] Treat / as an operator char See https://discuss.codemirror.net/t/division-operator-in-text-x-sql-mode/1941 --- mode/sql/sql.js | 8 ++++---- 1 file changed, 4 insertions(+), 4 deletions(-) diff --git a/mode/sql/sql.js b/mode/sql/sql.js index 9687ca38..3864f01f 100644 --- a/mode/sql/sql.js +++ b/mode/sql/sql.js @@ -18,7 +18,7 @@ CodeMirror.defineMode("sql", function(config, parserConfig) { atoms = parserConfig.atoms || {"false": true, "true": true, "null": true}, builtin = parserConfig.builtin || {}, keywords = parserConfig.keywords || {}, - operatorChars = parserConfig.operatorChars || /^[*+\-%<>!=&|~^]/, + operatorChars = parserConfig.operatorChars || /^[*+\-%<>!=&|~^\/]/, support = parserConfig.support || {}, hooks = parserConfig.hooks || {}, dateSQL = parserConfig.dateSQL || {"date" : true, "time" : true, "timestamp" : true}, @@ -391,7 +391,7 @@ CodeMirror.defineMode("sql", function(config, parserConfig) { 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 elseif 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 returns 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"), builtin: set("abs acos add_months ascii asin atan atan2 average bfile bfilename bigserial bit blob ceil character chartorowid chr clob concat convert cos cosh count dec decode deref dual dump dup_val_on_index empty error exp false float floor found glb greatest hextoraw initcap instr instrb int integer isopen last_day least length lengthb ln lower lpad ltrim lub make_ref max min mlslabel mod months_between natural naturaln nchar nclob 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 number numeric nvarchar2 nvl others power rawtohex real reftohex round rowcount rowidtochar rowtype rpad rtrim serial sign signtype sin sinh smallint soundex sqlcode sqlerrm sqrt stddev string substr substrb sum sysdate tan tanh to_char text to_date to_label to_multi_byte to_number to_single_byte translate true trunc uid unlogged upper user userenv varchar varchar2 variance varying vsize xml"), - operatorChars: /^[*+\-%<>!=~]/, + operatorChars: /^[*\/+\-%<>!=~]/, dateSQL: set("date time timestamp"), support: set("doubleQuote nCharCast zerolessFloat binaryNumber hexNumber") }); @@ -415,7 +415,7 @@ CodeMirror.defineMode("sql", function(config, parserConfig) { // https://www.postgresql.org/docs/10/static/datatype.html builtin: set("bigint int8 bigserial serial8 bit varying varbit boolean bool box bytea character char varchar cidr circle date double precision float8 inet integer int int4 interval json jsonb line lseg macaddr macaddr8 money numeric decimal path pg_lsn point polygon real float4 smallint int2 smallserial serial2 serial serial4 text time without zone with timetz timestamp timestamptz tsquery tsvector txid_snapshot uuid xml"), atoms: set("false true null unknown"), - operatorChars: /^[*+\-%<>!=&|^\/#@?~]/, + operatorChars: /^[*\/+\-%<>!=&|^\/#@?~]/, dateSQL: set("date time timestamp"), support: set("ODBCdotTable decimallessFloat zerolessFloat binaryNumber hexNumber nCharCast charsetCast") }); @@ -448,7 +448,7 @@ CodeMirror.defineMode("sql", function(config, parserConfig) { keywords: set("add after all alter analyze and anti archive array as asc at between bucket buckets by cache cascade case cast change clear cluster clustered codegen collection column columns comment commit compact compactions compute concatenate cost create cross cube current current_date current_timestamp database databases datata dbproperties defined delete delimited deny desc describe dfs directories distinct distribute drop else end escaped except exchange exists explain export extended external false fields fileformat first following for format formatted from full function functions global grant group grouping having if ignore import in index indexes inner inpath inputformat insert intersect interval into is items join keys last lateral lazy left like limit lines list load local location lock locks logical macro map minus msck natural no not null nulls of on optimize option options or order out outer outputformat over overwrite partition partitioned partitions percent preceding principals purge range recordreader recordwriter recover reduce refresh regexp rename repair replace reset restrict revoke right rlike role roles rollback rollup row rows schema schemas select semi separated serde serdeproperties set sets show skewed sort sorted start statistics stored stratify struct table tables tablesample tblproperties temp temporary terminated then to touch transaction transactions transform true truncate unarchive unbounded uncache union unlock unset use using values view when where window with"), builtin: set("tinyint smallint int bigint boolean float double string binary timestamp decimal array map struct uniontype delimited serde sequencefile textfile rcfile inputformat outputformat"), atoms: set("false true null"), - operatorChars: /^[*+\-%<>!=~&|^]/, + operatorChars: /^[*\/+\-%<>!=~&|^]/, dateSQL: set("date time timestamp"), support: set("ODBCdotTable doubleQuote zerolessFloat") }); From 24f99282f8dfc7d014446e9fe13f24413bbcd472 Mon Sep 17 00:00:00 2001 From: Marijn Haverbeke Date: Tue, 18 Dec 2018 14:44:13 +0100 Subject: [PATCH 1489/1790] [sql mode] Don't override operatorChars in base sql mime type --- 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 3864f01f..681bf66a 100644 --- a/mode/sql/sql.js +++ b/mode/sql/sql.js @@ -25,6 +25,7 @@ CodeMirror.defineMode("sql", function(config, parserConfig) { backslashStringEscapes = parserConfig.backslashStringEscapes !== false, brackets = parserConfig.brackets || /^[\{}\(\)\[\]]/, punctuation = parserConfig.punctuation || /^[;.,:]/ + console.log(operatorChars) function tokenBase(stream, state) { var ch = stream.next(); @@ -288,7 +289,6 @@ 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"), - operatorChars: /^[*+\-%<>!=]/, dateSQL: set("date time timestamp"), support: set("ODBCdotTable doubleQuote binaryNumber hexNumber") }); From b8198efa62819498353cd1dd2cef1afee25636c1 Mon Sep 17 00:00:00 2001 From: Marijn Haverbeke Date: Tue, 18 Dec 2018 14:49:10 +0100 Subject: [PATCH 1490/1790] Remove debug statement --- mode/sql/sql.js | 1 - 1 file changed, 1 deletion(-) diff --git a/mode/sql/sql.js b/mode/sql/sql.js index 681bf66a..de8f58d8 100644 --- a/mode/sql/sql.js +++ b/mode/sql/sql.js @@ -25,7 +25,6 @@ CodeMirror.defineMode("sql", function(config, parserConfig) { backslashStringEscapes = parserConfig.backslashStringEscapes !== false, brackets = parserConfig.brackets || /^[\{}\(\)\[\]]/, punctuation = parserConfig.punctuation || /^[;.,:]/ - console.log(operatorChars) function tokenBase(stream, state) { var ch = stream.next(); From 71c4a7f04806982b7fb3114db15a8bc9093d8323 Mon Sep 17 00:00:00 2001 From: Marijn Haverbeke Date: Wed, 19 Dec 2018 09:43:32 +0100 Subject: [PATCH 1491/1790] [matchbrackets addon] More thoroughly disable matching of <> by default Issue codemirror/google-modes#233 --- addon/edit/matchbrackets.js | 11 ++++++++--- 1 file changed, 8 insertions(+), 3 deletions(-) diff --git a/addon/edit/matchbrackets.js b/addon/edit/matchbrackets.js index 5b03201a..2dd48888 100644 --- a/addon/edit/matchbrackets.js +++ b/addon/edit/matchbrackets.js @@ -16,18 +16,23 @@ var matching = {"(": ")>", ")": "(<", "[": "]>", "]": "[<", "{": "}>", "}": "{<", "<": ">>", ">": "<<"}; + function bracketRegex(config) { + return config && config.bracketRegex || /[(){}[\]]/ + } + function findMatchingBracket(cm, where, config) { var line = cm.getLineHandle(where.line), pos = where.ch - 1; var afterCursor = config && config.afterCursor if (afterCursor == null) afterCursor = /(^| )cm-fat-cursor($| )/.test(cm.getWrapperElement().className) + var re = bracketRegex(config) // A cursor is defined as between two characters, but in in vim command mode // (i.e. not insert mode), the cursor is visually represented as a // highlighted box on top of the 2nd character. Otherwise, we allow matches // from before or after the cursor. - var match = (!afterCursor && pos >= 0 && matching[line.text.charAt(pos)]) || - matching[line.text.charAt(++pos)]; + var match = (!afterCursor && pos >= 0 && re.test(line.text.charAt(pos)) && matching[line.text.charAt(pos)]) || + re.test(line.text.charAt(pos + 1)) && matching[line.text.charAt(++pos)]; if (!match) return null; var dir = match.charAt(1) == ">" ? 1 : -1; if (config && config.strict && (dir > 0) != (pos == where.ch)) return null; @@ -51,7 +56,7 @@ var maxScanLines = (config && config.maxScanLines) || 1000; var stack = []; - var re = config && config.bracketRegex ? config.bracketRegex : /[(){}[\]]/; + var re = bracketRegex(config) var lineEnd = dir > 0 ? Math.min(where.line + maxScanLines, cm.lastLine() + 1) : Math.max(cm.firstLine() - 1, where.line - maxScanLines); for (var lineNo = where.line; lineNo != lineEnd; lineNo += dir) { From 19e010e370e7d30fc4619d3084f5d06c1fededce Mon Sep 17 00:00:00 2001 From: Christopher Wallis Date: Wed, 19 Dec 2018 01:46:24 -0700 Subject: [PATCH 1492/1790] [soy mode] added token for unknown type "?" - added token "type" for ? - added test for single type - added test for ? as a template type parameter --- mode/soy/soy.js | 2 +- mode/soy/test.js | 4 +++- 2 files changed, 4 insertions(+), 2 deletions(-) diff --git a/mode/soy/soy.js b/mode/soy/soy.js index 9e88007a..20f66514 100644 --- a/mode/soy/soy.js +++ b/mode/soy/soy.js @@ -213,7 +213,7 @@ state.soyState.pop(); return null; } - if (stream.eatWhile(/^[\w]+/)) { + if (stream.eatWhile(/^([\w]+|[?])/)) { return "type"; } stream.next(); diff --git a/mode/soy/test.js b/mode/soy/test.js index 4afaa3d0..e95110c2 100644 --- a/mode/soy/test.js +++ b/mode/soy/test.js @@ -37,7 +37,9 @@ '[keyword {@param] [def a]: ' + '[type list]<[[[type a]: [type int], ' + '[type b]: [type map]<[type string], ' + - '[type bool]>]]>][keyword }]'); + '[type bool]>]]>][keyword }]', + '[keyword {@param] [def unknown]: [type ?][keyword }]', + '[keyword {@param] [def list]: [type list]<[type ?]>[keyword }]'); MT('undefined-var', '[keyword {][variable-2&error $var]'); From cafe5827767e252852d68adb04102be5be73736a Mon Sep 17 00:00:00 2001 From: Christopher Wallis Date: Wed, 19 Dec 2018 14:17:33 -0700 Subject: [PATCH 1493/1790] [soy mode] Boolean and number tokenization in expressions - added tokenization for decimals - added tokenization for hexidecimals - added tokenization for true/false - added test cases for those new atom tokens --- mode/soy/soy.js | 5 +++++ mode/soy/test.js | 13 +++++++++++++ 2 files changed, 18 insertions(+) diff --git a/mode/soy/soy.js b/mode/soy/soy.js index 20f66514..356276d0 100644 --- a/mode/soy/soy.js +++ b/mode/soy/soy.js @@ -266,6 +266,11 @@ state.quoteKind = match; return "string"; } + if (stream.match(/(true|false)(?!\w)/) || + stream.match(/0x([0-9a-fA-F]{2,})/) || + stream.match(/-?([0-9]*[.])?[0-9]+/)) { + return "atom"; + } if (match = stream.match(/^\$([\w]+)/)) { return ref(state.variables, match[1]); } diff --git a/mode/soy/test.js b/mode/soy/test.js index e95110c2..0698b9d6 100644 --- a/mode/soy/test.js +++ b/mode/soy/test.js @@ -33,6 +33,19 @@ '[keyword {namespace] [variable my.namespace.templates] ' + '[attribute requirecss]=[string "my.namespace"][keyword }]'); + MT('primitive-test', + '[keyword {] [atom true] [keyword }]', + '[keyword {] [atom false] [keyword }]', + '[keyword {] truethy [keyword }]', + '[keyword {] falsey [keyword }]', + '[keyword {] [atom 42] [keyword }]', + '[keyword {] [atom .42] [keyword }]', + '[keyword {] [atom 0.42] [keyword }]', + '[keyword {] [atom -0.42] [keyword }]', + '[keyword {] [atom -.2] [keyword }]', + '[keyword {] [atom 0x1F] [keyword }]', + '[keyword {] [atom 0x1F00BBEA] [keyword }]'); + MT('param-type-test', '[keyword {@param] [def a]: ' + '[type list]<[[[type a]: [type int], ' + From 9018db9cac5a8a09a55b69f33fef0fb73c2e090b Mon Sep 17 00:00:00 2001 From: Christopher Wallis Date: Thu, 20 Dec 2018 15:04:08 -0700 Subject: [PATCH 1494/1790] [soy mode] tokenize operators in expressions - tokenize arithmetic and equality operators as "operator" - added tests to validate this --- mode/soy/soy.js | 4 ++++ mode/soy/test.js | 16 +++++++++++++++- 2 files changed, 19 insertions(+), 1 deletion(-) diff --git a/mode/soy/soy.js b/mode/soy/soy.js index 356276d0..bb61132d 100644 --- a/mode/soy/soy.js +++ b/mode/soy/soy.js @@ -271,6 +271,10 @@ stream.match(/-?([0-9]*[.])?[0-9]+/)) { return "atom"; } + if (stream.match(/(\||[+\-*\/%]|[=!][=]|[<>][=]?)/)) { + // Tokenize filter, binary, and equality operators. + return "operator"; + } if (match = stream.match(/^\$([\w]+)/)) { return ref(state.variables, match[1]); } diff --git a/mode/soy/test.js b/mode/soy/test.js index 0698b9d6..e57d606f 100644 --- a/mode/soy/test.js +++ b/mode/soy/test.js @@ -8,7 +8,7 @@ // Test of small keywords and words containing them. MT('keywords-test', '[keyword {] [keyword as] worrying [keyword and] notorious [keyword as]', - ' the Fandor-alias assassin, [keyword or]', + ' the Fandor[operator -]alias assassin, [keyword or]', ' Corcand cannot fit [keyword in] [keyword }]'); MT('let-test', @@ -33,6 +33,20 @@ '[keyword {namespace] [variable my.namespace.templates] ' + '[attribute requirecss]=[string "my.namespace"][keyword }]'); + MT('operators-test', + '[keyword {] [atom 1] [operator ==] [atom 1] [keyword }]', + '[keyword {] [atom 1] [operator !=] [atom 2] [keyword }]', + '[keyword {] [atom 2] [operator +] [atom 2] [keyword }]', + '[keyword {] [atom 2] [operator -] [atom 2] [keyword }]', + '[keyword {] [atom 2] [operator *] [atom 2] [keyword }]', + '[keyword {] [atom 2] [operator /] [atom 2] [keyword }]', + '[keyword {] [atom 2] [operator %] [atom 2] [keyword }]', + '[keyword {] [atom 2] [operator <=] [atom 2] [keyword }]', + '[keyword {] [atom 2] [operator >=] [atom 2] [keyword }]', + '[keyword {] [atom 3] [operator >] [atom 2] [keyword }]', + '[keyword {] [atom 2] [operator >] [atom 3] [keyword }]', + '[keyword {] [variable-2&error $variable] [operator |] safeHtml [keyword }]') + MT('primitive-test', '[keyword {] [atom true] [keyword }]', '[keyword {] [atom false] [keyword }]', From adb4ad3f7fc45612612df896eaee28948f9e5b2a Mon Sep 17 00:00:00 2001 From: Marijn Haverbeke Date: Fri, 21 Dec 2018 10:55:45 +0100 Subject: [PATCH 1495/1790] Fix issue in IE where the context menu hack might stay visible Because JS seems to be frozen entirely while the context menu is open, the next open might happen before the timeout was able to fire. This forces the previous context menu kludge to be cleared before starting a new one. --- src/input/TextareaInput.js | 4 +++- 1 file changed, 3 insertions(+), 1 deletion(-) diff --git a/src/input/TextareaInput.js b/src/input/TextareaInput.js index adfa3603..75fcb181 100644 --- a/src/input/TextareaInput.js +++ b/src/input/TextareaInput.js @@ -264,6 +264,7 @@ export default class TextareaInput { onContextMenu(e) { let input = this, cm = input.cm, display = cm.display, te = input.textarea + if (input.contextMenuPending) input.contextMenuPending() let pos = posFromMouse(cm, e), scrollPos = display.scroller.scrollTop if (!pos || presto) return // Opera is difficult. @@ -287,7 +288,7 @@ export default class TextareaInput { display.input.reset() // Adds "Select all" to context menu in FF if (!cm.somethingSelected()) te.value = input.prevInput = " " - input.contextMenuPending = true + input.contextMenuPending = rehide display.selForContextMenu = cm.doc.sel clearTimeout(display.detectingSelectAll) @@ -308,6 +309,7 @@ export default class TextareaInput { } } function rehide() { + if (input.contextMenuPending != rehide) return input.contextMenuPending = false input.wrapper.style.cssText = oldWrapperCSS te.style.cssText = oldCSS From a21ea6f496f09d02254d7af32f33f61ae81a440f Mon Sep 17 00:00:00 2001 From: Marijn Haverbeke Date: Fri, 21 Dec 2018 10:57:56 +0100 Subject: [PATCH 1496/1790] Improve positioning of context menu hack It sometimes went wrong due to the textarea wrapper being moved when the cursor was placed under the mouse. --- src/input/TextareaInput.js | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/src/input/TextareaInput.js b/src/input/TextareaInput.js index 75fcb181..ad81d8a9 100644 --- a/src/input/TextareaInput.js +++ b/src/input/TextareaInput.js @@ -275,8 +275,8 @@ export default class TextareaInput { operation(cm, setSelection)(cm.doc, simpleSelection(pos), sel_dontScroll) let oldCSS = te.style.cssText, oldWrapperCSS = input.wrapper.style.cssText - input.wrapper.style.cssText = "position: absolute" - let wrapperBox = input.wrapper.getBoundingClientRect() + let wrapperBox = input.wrapper.offsetParent.getBoundingClientRect() + input.wrapper.style.cssText = "position: static" te.style.cssText = `position: absolute; width: 30px; height: 30px; top: ${e.clientY - wrapperBox.top - 5}px; left: ${e.clientX - wrapperBox.left - 5}px; z-index: 1000; background: ${ie ? "rgba(255, 255, 255, .05)" : "transparent"}; From 91eec71a6c1ca2a83eb50ecb623c33805c04888b Mon Sep 17 00:00:00 2001 From: Marijn Haverbeke Date: Fri, 21 Dec 2018 11:05:27 +0100 Subject: [PATCH 1497/1790] [show-hint addon] Define a closeHint method Issue #5682 --- addon/hint/show-hint.js | 4 ++++ 1 file changed, 4 insertions(+) diff --git a/addon/hint/show-hint.js b/addon/hint/show-hint.js index 28025775..fc5af152 100644 --- a/addon/hint/show-hint.js +++ b/addon/hint/show-hint.js @@ -46,6 +46,10 @@ completion.update(true); }); + CodeMirror.defineExtension("closeHint", function() { + if (this.state.completionActive) this.state.completionActive.close() + }) + function Completion(cm, options) { this.cm = cm; this.options = options; From f658d0927ecd4668ae71973ba6ed9cecdcc662a8 Mon Sep 17 00:00:00 2001 From: Neil Anderson Date: Fri, 21 Dec 2018 05:06:33 -0500 Subject: [PATCH 1498/1790] [sql mode] Add SUBSCRIPTION postgres keyword See https://www.postgresql.org/docs/10/sql-createsubscription.html --- 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 de8f58d8..e7442c39 100644 --- a/mode/sql/sql.js +++ b/mode/sql/sql.js @@ -410,7 +410,7 @@ CodeMirror.defineMode("sql", function(config, parserConfig) { name: "sql", client: set("source"), // https://www.postgresql.org/docs/10/static/sql-keywords-appendix.html - keywords: set(sqlKeywords + "a abort abs absent absolute access according action ada add admin after aggregate all allocate also always analyse analyze any are array array_agg array_max_cardinality asensitive assertion assignment asymmetric at atomic attribute attributes authorization avg backward base64 before begin begin_frame begin_partition bernoulli binary bit_length blob blocked bom both breadth c cache call called cardinality cascade cascaded case cast catalog catalog_name ceil ceiling chain characteristics characters character_length character_set_catalog character_set_name character_set_schema char_length check checkpoint class class_origin clob close cluster coalesce cobol collate collation collation_catalog collation_name collation_schema collect column columns column_name command_function command_function_code comment comments commit committed concurrently condition condition_number configuration conflict connect connection connection_name constraint constraints constraint_catalog constraint_name constraint_schema constructor contains content continue control conversion convert copy corr corresponding cost covar_pop covar_samp cross csv cube cume_dist current current_catalog current_date current_default_transform_group current_path current_role current_row current_schema current_time current_timestamp current_transform_group_for_type current_user cursor cursor_name cycle data database datalink datetime_interval_code datetime_interval_precision day db deallocate dec declare default defaults deferrable deferred defined definer degree delimiter delimiters dense_rank depth deref derived describe descriptor deterministic diagnostics dictionary disable discard disconnect dispatch dlnewcopy dlpreviouscopy dlurlcomplete dlurlcompleteonly dlurlcompletewrite dlurlpath dlurlpathonly dlurlpathwrite dlurlscheme dlurlserver dlvalue do document domain dynamic dynamic_function dynamic_function_code each element else empty enable encoding encrypted end end-exec end_frame end_partition enforced enum equals escape event every except exception exclude excluding exclusive exec execute exists exp explain expression extension external extract false family fetch file filter final first first_value flag float floor following for force foreign fortran forward found frame_row free freeze fs full function functions fusion g general generated get gettoken global go goto grant granted greatest grouping groups handler header headline hex hierarchy hold hour id identity if ignore ilike immediate immediately immutable implementation implicit import including increment indent index indexes indicator inherit inherits init initially inline inner inout input insensitive instance instantiable instead integrity intersect intersection invoker isnull isolation k key key_member key_type label lag language large last last_value lateral lc_collate lc_ctype lead leading leakproof least left length level lexize lextypes library like_regex link listen ln load local localtime localtimestamp location locator lock locked logged lower m map mapping match matched materialized max maxvalue max_cardinality member merge message_length message_octet_length message_text method min minute minvalue mod mode modifies module month more move multiset mumps name names namespace national natural nchar nclob nesting new next nfc nfd nfkc nfkd nil no none normalize normalized nothing notify notnull nowait nth_value ntile null nullable nullif nulls number object occurrences_regex octets octet_length of off offset oids old only open operator option options ordering ordinality others out outer output over overlaps overlay overriding owned owner p pad parallel parameter parameter_mode parameter_name parameter_ordinal_position parameter_specific_catalog parameter_specific_name parameter_specific_schema parser partial partition pascal passing passthrough password percent percentile_cont percentile_disc percent_rank period permission placing plans pli policy portion position position_regex power precedes preceding prepare prepared preserve primary prior privileges procedural procedure program public publication quote range rank read reads reassign recheck recovery recursive ref references referencing refresh regr_avgx regr_avgy regr_count regr_intercept regr_r2 regr_slope regr_sxx regr_sxy regr_syy reindex relative release rename repeatable replace replica requiring reset respect restart restore restrict restricted result return returned_cardinality returned_length returned_octet_length returned_sqlstate returning returns revoke right role rollback rollup routine routine_catalog routine_name routine_schema row rows row_count row_number rule savepoint scale schema schema_name scope scope_catalog scope_name scope_schema scroll search second section security selective self sensitive sequence sequences serializable server server_name session session_user setof sets share show similar simple size skip snapshot some source space specific specifictype specific_name sql sqlcode sqlerror sqlexception sqlstate sqlwarning sqrt stable standalone start state statement static statistics stddev_pop stddev_samp stdin stdout storage strict strip structure style subclass_origin submultiset substring substring_regex succeeds sum symmetric sysid system system_time system_user t tables tablesample tablespace table_name temp template temporary then ties timezone_hour timezone_minute to token top_level_count trailing transaction transactions_committed transactions_rolled_back transaction_active transform transforms translate translate_regex translation treat trigger trigger_catalog trigger_name trigger_schema trim trim_array true truncate trusted type types uescape unbounded uncommitted under unencrypted unique unknown unlink unlisten unlogged unnamed unnest until untyped upper uri usage user user_defined_type_catalog user_defined_type_code user_defined_type_name user_defined_type_schema using vacuum valid validate validator value value_of varbinary variadic var_pop var_samp verbose version versioning view views volatile when whenever whitespace width_bucket window within work wrapper write xmlagg xmlattributes xmlbinary xmlcast xmlcomment xmlconcat xmldeclaration xmldocument xmlelement xmlexists xmlforest xmliterate xmlnamespaces xmlparse xmlpi xmlquery xmlroot xmlschema xmlserialize xmltable xmltext xmlvalidate year yes loop repeat attach path depends detach zone"), + keywords: set(sqlKeywords + "a abort abs absent absolute access according action ada add admin after aggregate all allocate also always analyse analyze any are array array_agg array_max_cardinality asensitive assertion assignment asymmetric at atomic attribute attributes authorization avg backward base64 before begin begin_frame begin_partition bernoulli binary bit_length blob blocked bom both breadth c cache call called cardinality cascade cascaded case cast catalog catalog_name ceil ceiling chain characteristics characters character_length character_set_catalog character_set_name character_set_schema char_length check checkpoint class class_origin clob close cluster coalesce cobol collate collation collation_catalog collation_name collation_schema collect column columns column_name command_function command_function_code comment comments commit committed concurrently condition condition_number configuration conflict connect connection connection_name constraint constraints constraint_catalog constraint_name constraint_schema constructor contains content continue control conversion convert copy corr corresponding cost covar_pop covar_samp cross csv cube cume_dist current current_catalog current_date current_default_transform_group current_path current_role current_row current_schema current_time current_timestamp current_transform_group_for_type current_user cursor cursor_name cycle data database datalink datetime_interval_code datetime_interval_precision day db deallocate dec declare default defaults deferrable deferred defined definer degree delimiter delimiters dense_rank depth deref derived describe descriptor deterministic diagnostics dictionary disable discard disconnect dispatch dlnewcopy dlpreviouscopy dlurlcomplete dlurlcompleteonly dlurlcompletewrite dlurlpath dlurlpathonly dlurlpathwrite dlurlscheme dlurlserver dlvalue do document domain dynamic dynamic_function dynamic_function_code each element else empty enable encoding encrypted end end-exec end_frame end_partition enforced enum equals escape event every except exception exclude excluding exclusive exec execute exists exp explain expression extension external extract false family fetch file filter final first first_value flag float floor following for force foreign fortran forward found frame_row free freeze fs full function functions fusion g general generated get gettoken global go goto grant granted greatest grouping groups handler header headline hex hierarchy hold hour id identity if ignore ilike immediate immediately immutable implementation implicit import including increment indent index indexes indicator inherit inherits init initially inline inner inout input insensitive instance instantiable instead integrity intersect intersection invoker isnull isolation k key key_member key_type label lag language large last last_value lateral lc_collate lc_ctype lead leading leakproof least left length level lexize lextypes library like_regex link listen ln load local localtime localtimestamp location locator lock locked logged lower m map mapping match matched materialized max maxvalue max_cardinality member merge message_length message_octet_length message_text method min minute minvalue mod mode modifies module month more move multiset mumps name names namespace national natural nchar nclob nesting new next nfc nfd nfkc nfkd nil no none normalize normalized nothing notify notnull nowait nth_value ntile null nullable nullif nulls number object occurrences_regex octets octet_length of off offset oids old only open operator option options ordering ordinality others out outer output over overlaps overlay overriding owned owner p pad parallel parameter parameter_mode parameter_name parameter_ordinal_position parameter_specific_catalog parameter_specific_name parameter_specific_schema parser partial partition pascal passing passthrough password percent percentile_cont percentile_disc percent_rank period permission placing plans pli policy portion position position_regex power precedes preceding prepare prepared preserve primary prior privileges procedural procedure program public publication quote range rank read reads reassign recheck recovery recursive ref references referencing refresh regr_avgx regr_avgy regr_count regr_intercept regr_r2 regr_slope regr_sxx regr_sxy regr_syy reindex relative release rename repeatable replace replica requiring reset respect restart restore restrict restricted result return returned_cardinality returned_length returned_octet_length returned_sqlstate returning returns revoke right role rollback rollup routine routine_catalog routine_name routine_schema row rows row_count row_number rule savepoint scale schema schema_name scope scope_catalog scope_name scope_schema scroll search second section security selective self sensitive sequence sequences serializable server server_name session session_user setof sets share show similar simple size skip snapshot some source space specific specifictype specific_name sql sqlcode sqlerror sqlexception sqlstate sqlwarning sqrt stable standalone start state statement static statistics stddev_pop stddev_samp stdin stdout storage strict strip structure style subclass_origin submultiset subscription substring substring_regex succeeds sum symmetric sysid system system_time system_user t tables tablesample tablespace table_name temp template temporary then ties timezone_hour timezone_minute to token top_level_count trailing transaction transactions_committed transactions_rolled_back transaction_active transform transforms translate translate_regex translation treat trigger trigger_catalog trigger_name trigger_schema trim trim_array true truncate trusted type types uescape unbounded uncommitted under unencrypted unique unknown unlink unlisten unlogged unnamed unnest until untyped upper uri usage user user_defined_type_catalog user_defined_type_code user_defined_type_name user_defined_type_schema using vacuum valid validate validator value value_of varbinary variadic var_pop var_samp verbose version versioning view views volatile when whenever whitespace width_bucket window within work wrapper write xmlagg xmlattributes xmlbinary xmlcast xmlcomment xmlconcat xmldeclaration xmldocument xmlelement xmlexists xmlforest xmliterate xmlnamespaces xmlparse xmlpi xmlquery xmlroot xmlschema xmlserialize xmltable xmltext xmlvalidate year yes loop repeat attach path depends detach zone"), // https://www.postgresql.org/docs/10/static/datatype.html builtin: set("bigint int8 bigserial serial8 bit varying varbit boolean bool box bytea character char varchar cidr circle date double precision float8 inet integer int int4 interval json jsonb line lseg macaddr macaddr8 money numeric decimal path pg_lsn point polygon real float4 smallint int2 smallserial serial2 serial serial4 text time without zone with timetz timestamp timestamptz tsquery tsvector txid_snapshot uuid xml"), atoms: set("false true null unknown"), From 0612ed0d40ac8b893c4f8cec98958129f2f86969 Mon Sep 17 00:00:00 2001 From: Marijn Haverbeke Date: Fri, 21 Dec 2018 11:13:31 +0100 Subject: [PATCH 1499/1790] [panel demo] Prevent editor from losing focus when closing a panel See https://discuss.codemirror.net/t/codemirror-panel-demo-cursor-disappears-on-closing-panel/1946 --- demo/panel.html | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-) diff --git a/demo/panel.html b/demo/panel.html index 1030615a..d4f813d0 100644 --- a/demo/panel.html +++ b/demo/panel.html @@ -106,7 +106,8 @@

        Panel Demo

        close.setAttribute("title", "Remove me!"); close.setAttribute("class", "remove-panel"); close.textContent = "✖"; - CodeMirror.on(close, "click", function() { + CodeMirror.on(close, "mousedown", function(e) { + e.preventDefault() panels[node.id].clear(); }); label = node.appendChild(document.createElement("span")); From 582022b39380dd79d93db97e0b47341bee0a6699 Mon Sep 17 00:00:00 2001 From: Marijn Haverbeke Date: Fri, 21 Dec 2018 11:36:07 +0100 Subject: [PATCH 1500/1790] Mark version 5.42.2 --- AUTHORS | 6 ++++++ CHANGELOG.md | 16 ++++++++++++++++ doc/manual.html | 2 +- doc/releases.html | 10 ++++++++++ index.html | 2 +- package.json | 2 +- src/edit/main.js | 2 +- 7 files changed, 36 insertions(+), 4 deletions(-) diff --git a/AUTHORS b/AUTHORS index 83d2a4d9..8e53b9b1 100644 --- a/AUTHORS +++ b/AUTHORS @@ -151,6 +151,7 @@ Christopher Brown Christopher Kramer Christopher Mitchell Christopher Pfohl +Christopher Wallis Chunliang Lyu ciaranj CodeAnimal @@ -244,6 +245,7 @@ Ford_Lawnmower Forrest Oliphant Franco Catena Frank Wiegand +fraxx001 Fredrik Borg Gabriel Gheorghian Gabriel Horner @@ -258,6 +260,7 @@ George Stephanis geowarin Gerard Braad Gergely Hegykozi +Germain Chazot Giovanni Calò Glebov Boris Glenn Jorde @@ -330,6 +333,7 @@ Jared Jacobs Jason Jason Barnabe Jason Grout +Jason Heeris Jason Johnston Jason San Jose Jason Siefken @@ -411,6 +415,7 @@ kubelsmieci KwanEsq Kyle Kelley KyleMcNutt +LaKing Lanfei Lanny laobubu @@ -767,6 +772,7 @@ Wu Cheng-Han Xavier Mendez Yassin N. Hassan YNH Webdev +yoongu Yunchi Luo Yuvi Panda Yvonnick Esnault diff --git a/CHANGELOG.md b/CHANGELOG.md index 1e20bf30..b5c2b9d7 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -1,3 +1,19 @@ +## 5.42.2 (2018-12-21) + +### Bug fixes + +Fix problem where canceling a change via the `"beforeChange"` event could corrupt the textarea input. + +Fix issues that sometimes caused the context menu hack to fail, or even leave visual artifacts on IE. + +[vim bindings](https://codemirror.net/demo/vim.html): Make it possible to select text between angle brackets. + +[css mode](https://codemirror.net/mode/css/): Fix tokenizing of CSS variables. + +[python mode](https://codemirror.net/mode/python/): Fix another bug in tokenizing of format strings. + +[soy mode](https://codemirror.net/mode/soy/): More accurate highlighting. + ## 5.42.0 (2018-11-20) ### Bug fixes diff --git a/doc/manual.html b/doc/manual.html index ae07f8d3..8f490328 100644 --- a/doc/manual.html +++ b/doc/manual.html @@ -69,7 +69,7 @@

        User manual and reference guide - version 5.42.1 + version 5.42.2

        CodeMirror is a code-editor component that can be embedded in diff --git a/doc/releases.html b/doc/releases.html index 2f1e965c..a7a5cce9 100644 --- a/doc/releases.html +++ b/doc/releases.html @@ -30,6 +30,16 @@

        Release notes and version history

        Version 5.x

        +

        21-12-2018: Version 5.42.2:

        + +
          +
        • Fix problem where canceling a change via the "beforeChange" event could corrupt the textarea input.
        • +
        • Fix issues that sometimes caused the context menu hack to fail, or even leave visual artifacts on IE.
        • +
        • vim bindings: Make it possible to select text between angle brackets.
        • +
        • css mode: Fix tokenizing of CSS variables.
        • +
        • python mode: Fix another bug in tokenizing of format strings.
        • +
        • soy mode: More accurate highlighting.
        • +

        20-11-2018: Version 5.42.0:

        diff --git a/index.html b/index.html index ce02a28c..b9dc9b9c 100644 --- a/index.html +++ b/index.html @@ -99,7 +99,7 @@

        This is CodeMirror

        - Get the current version: 5.42.0.
        + Get the current version: 5.42.2.
        You can see the code,
        read the release notes,
        or study the user manual. diff --git a/package.json b/package.json index 673f9a2f..8fc42fbf 100644 --- a/package.json +++ b/package.json @@ -1,6 +1,6 @@ { "name": "codemirror", - "version": "5.42.1", + "version": "5.42.2", "main": "lib/codemirror.js", "style": "lib/codemirror.css", "author": { diff --git a/src/edit/main.js b/src/edit/main.js index 090049e9..adc8b650 100644 --- a/src/edit/main.js +++ b/src/edit/main.js @@ -66,4 +66,4 @@ import { addLegacyProps } from "./legacy.js" addLegacyProps(CodeMirror) -CodeMirror.version = "5.42.1" +CodeMirror.version = "5.42.2" From d3006a5ed6bab875a7740166b092ec7b1c8f2541 Mon Sep 17 00:00:00 2001 From: Marijn Haverbeke Date: Fri, 21 Dec 2018 11:41:17 +0100 Subject: [PATCH 1501/1790] Bump version number post-5.42.2 --- doc/manual.html | 2 +- package.json | 2 +- src/edit/main.js | 2 +- 3 files changed, 3 insertions(+), 3 deletions(-) diff --git a/doc/manual.html b/doc/manual.html index 8f490328..97f12949 100644 --- a/doc/manual.html +++ b/doc/manual.html @@ -69,7 +69,7 @@

        User manual and reference guide - version 5.42.2 + version 5.42.3

        CodeMirror is a code-editor component that can be embedded in diff --git a/package.json b/package.json index 8fc42fbf..a92d91dc 100644 --- a/package.json +++ b/package.json @@ -1,6 +1,6 @@ { "name": "codemirror", - "version": "5.42.2", + "version": "5.42.3", "main": "lib/codemirror.js", "style": "lib/codemirror.css", "author": { diff --git a/src/edit/main.js b/src/edit/main.js index adc8b650..a9edb687 100644 --- a/src/edit/main.js +++ b/src/edit/main.js @@ -66,4 +66,4 @@ import { addLegacyProps } from "./legacy.js" addLegacyProps(CodeMirror) -CodeMirror.version = "5.42.2" +CodeMirror.version = "5.42.3" From ced4f5e5f7cb782512a56dd24f65e8ff39d3c6ce Mon Sep 17 00:00:00 2001 From: Christopher Wallis Date: Fri, 21 Dec 2018 08:53:06 -0700 Subject: [PATCH 1502/1790] [soy mode] added null literal and null propogation operator - added null as atom - added null propogator as operator - changed [=] to = in operator regex - added tests to validate new highlighting --- mode/soy/soy.js | 6 +++--- mode/soy/test.js | 1 + 2 files changed, 4 insertions(+), 3 deletions(-) diff --git a/mode/soy/soy.js b/mode/soy/soy.js index bb61132d..42d581d6 100644 --- a/mode/soy/soy.js +++ b/mode/soy/soy.js @@ -266,13 +266,13 @@ state.quoteKind = match; return "string"; } - if (stream.match(/(true|false)(?!\w)/) || + if (stream.match(/(null|true|false)(?!\w)/) || stream.match(/0x([0-9a-fA-F]{2,})/) || stream.match(/-?([0-9]*[.])?[0-9]+/)) { return "atom"; } - if (stream.match(/(\||[+\-*\/%]|[=!][=]|[<>][=]?)/)) { - // Tokenize filter, binary, and equality operators. + if (stream.match(/(\||[+\-*\/%]|[=!]=|\?:|[<>]=?)/)) { + // Tokenize filter, binary, null propagator, and equality operators. return "operator"; } if (match = stream.match(/^\$([\w]+)/)) { diff --git a/mode/soy/test.js b/mode/soy/test.js index e57d606f..ef03ea99 100644 --- a/mode/soy/test.js +++ b/mode/soy/test.js @@ -45,6 +45,7 @@ '[keyword {] [atom 2] [operator >=] [atom 2] [keyword }]', '[keyword {] [atom 3] [operator >] [atom 2] [keyword }]', '[keyword {] [atom 2] [operator >] [atom 3] [keyword }]', + '[keyword {] [atom null] [operator ?:] [string ""] [keyword }]', '[keyword {] [variable-2&error $variable] [operator |] safeHtml [keyword }]') MT('primitive-test', From 76daa5251d0e707411ad118c59c294502774fad8 Mon Sep 17 00:00:00 2001 From: Christopher Wallis Date: Fri, 21 Dec 2018 09:08:27 -0700 Subject: [PATCH 1503/1790] [soy mode] add support for scientific notation - modified the decimal expression to support scientific notation - added tests to verify new highlighting behavior --- mode/soy/soy.js | 2 +- mode/soy/test.js | 2 ++ 2 files changed, 3 insertions(+), 1 deletion(-) diff --git a/mode/soy/soy.js b/mode/soy/soy.js index 42d581d6..7ebd4b3d 100644 --- a/mode/soy/soy.js +++ b/mode/soy/soy.js @@ -268,7 +268,7 @@ } if (stream.match(/(null|true|false)(?!\w)/) || stream.match(/0x([0-9a-fA-F]{2,})/) || - stream.match(/-?([0-9]*[.])?[0-9]+/)) { + stream.match(/-?([0-9]*[.])?[0-9]+(e[0-9]*)?/)) { return "atom"; } if (stream.match(/(\||[+\-*\/%]|[=!]=|\?:|[<>]=?)/)) { diff --git a/mode/soy/test.js b/mode/soy/test.js index ef03ea99..f057f4fe 100644 --- a/mode/soy/test.js +++ b/mode/soy/test.js @@ -58,6 +58,8 @@ '[keyword {] [atom 0.42] [keyword }]', '[keyword {] [atom -0.42] [keyword }]', '[keyword {] [atom -.2] [keyword }]', + '[keyword {] [atom 6.03e23] [keyword }]', + '[keyword {] [atom -0.03e0] [keyword }]', '[keyword {] [atom 0x1F] [keyword }]', '[keyword {] [atom 0x1F00BBEA] [keyword }]'); From 4330d0681a9cadec4a930e44f0471e807ae73669 Mon Sep 17 00:00:00 2001 From: "FUJI Goro (gfx)" Date: Tue, 25 Dec 2018 11:48:02 +0900 Subject: [PATCH 1504/1790] [show-hint addon]: bind Ctrl-P/Ctrl-N to up/down on macOS --- addon/hint/show-hint.js | 8 ++++++++ 1 file changed, 8 insertions(+) diff --git a/addon/hint/show-hint.js b/addon/hint/show-hint.js index fc5af152..f86f9540 100644 --- a/addon/hint/show-hint.js +++ b/addon/hint/show-hint.js @@ -1,6 +1,8 @@ // CodeMirror, copyright (c) by Marijn Haverbeke and others // Distributed under an MIT license: https://codemirror.net/LICENSE +var mac = /Mac/.test(navigator.platform); + (function(mod) { if (typeof exports == "object" && typeof module == "object") // CommonJS mod(require("../../lib/codemirror")); @@ -167,6 +169,12 @@ Tab: handle.pick, Esc: handle.close }; + + if (mac) { + baseMap["Ctrl-P"] = function() {handle.moveFocus(-1);}; + baseMap["Ctrl-N"] = function() {handle.moveFocus(1);}; + } + var custom = completion.options.customKeys; var ourMap = custom ? {} : baseMap; function addBinding(key, val) { From 3c79c54a335411fd1aff55246b77131dd98bb81b Mon Sep 17 00:00:00 2001 From: Christopher Wallis Date: Fri, 28 Dec 2018 09:52:16 -0700 Subject: [PATCH 1505/1790] [css mode] more consistent css function tokens - created a css function regexp with a positive lookahead for (. There are still a few cases with IE functions that have . and : in it that this does not catch. - folded url/domain/regexp into that css function regexp including the special string tokenization that was in place already - updated css, less, and scss tests to reflect this new tokenization --- mode/css/css.js | 11 +++++------ mode/css/less_test.js | 8 ++++---- mode/css/scss_test.js | 10 +++++----- mode/css/test.js | 44 +++++++++++++++++++++---------------------- 4 files changed, 36 insertions(+), 37 deletions(-) diff --git a/mode/css/css.js b/mode/css/css.js index d82de404..05742c5c 100644 --- a/mode/css/css.js +++ b/mode/css/css.js @@ -77,12 +77,11 @@ CodeMirror.defineMode("css", function(config, parserConfig) { return ret("qualifier", "qualifier"); } else if (/[:;{}\[\]\(\)]/.test(ch)) { return ret(null, ch); - } else if (((ch == "u" || ch == "U") && stream.match(/rl(-prefix)?\(/i)) || - ((ch == "d" || ch == "D") && stream.match("omain(", true, true)) || - ((ch == "r" || ch == "R") && stream.match("egexp(", true, true))) { - stream.backUp(1); - state.tokenize = tokenParenthesized; - return ret("property", "word"); + } else if (stream.match(/[\w-.]+(?=\()/)) { + if (/^(url(-prefix)?|domain|regexp)$/.test(stream.current().toLowerCase())) { + state.tokenize = tokenParenthesized; + } + return ret("variable callee", "variable"); } else if (/[\w\\\-]/.test(ch)) { stream.eatWhile(/[\w\\\-]/); return ret("property", "word"); diff --git a/mode/css/less_test.js b/mode/css/less_test.js index 2e33a23c..abeb6a20 100644 --- a/mode/css/less_test.js +++ b/mode/css/less_test.js @@ -10,8 +10,8 @@ MT("variable", "[variable-2 @base]: [atom #f04615];", "[qualifier .class] {", - " [property width]: [variable percentage]([number 0.5]); [comment // returns `50%`]", - " [property color]: [variable saturate]([variable-2 @base], [number 5%]);", + " [property width]: [variable&callee percentage]([number 0.5]); [comment // returns `50%`]", + " [property color]: [variable&callee saturate]([variable-2 @base], [number 5%]);", "}"); MT("amp", @@ -26,10 +26,10 @@ MT("mixin", "[qualifier .mixin] ([variable dark]; [variable-2 @color]) {", - " [property color]: [atom darken]([variable-2 @color], [number 10%]);", + " [property color]: [variable&callee darken]([variable-2 @color], [number 10%]);", "}", "[qualifier .mixin] ([variable light]; [variable-2 @color]) {", - " [property color]: [atom lighten]([variable-2 @color], [number 10%]);", + " [property color]: [variable&callee lighten]([variable-2 @color], [number 10%]);", "}", "[qualifier .mixin] ([variable-2 @_]; [variable-2 @color]) {", " [property display]: [atom block];", diff --git a/mode/css/scss_test.js b/mode/css/scss_test.js index 1fc1bbdc..68afc664 100644 --- a/mode/css/scss_test.js +++ b/mode/css/scss_test.js @@ -6,19 +6,19 @@ function MT(name) { test.mode(name, mode, Array.prototype.slice.call(arguments, 1), "scss"); } MT('url_with_quotation', - "[tag foo] { [property background]:[atom url]([string test.jpg]) }"); + "[tag foo] { [property background]:[variable&callee url]([string test.jpg]) }"); MT('url_with_double_quotes', - "[tag foo] { [property background]:[atom url]([string \"test.jpg\"]) }"); + "[tag foo] { [property background]:[variable&callee url]([string \"test.jpg\"]) }"); MT('url_with_single_quotes', - "[tag foo] { [property background]:[atom url]([string \'test.jpg\']) }"); + "[tag foo] { [property background]:[variable&callee url]([string \'test.jpg\']) }"); MT('string', "[def @import] [string \"compass/css3\"]"); MT('important_keyword', - "[tag foo] { [property background]:[atom url]([string \'test.jpg\']) [keyword !important] }"); + "[tag foo] { [property background]:[variable&callee url]([string \'test.jpg\']) [keyword !important] }"); MT('variable', "[variable-2 $blue]:[atom #333]"); @@ -95,7 +95,7 @@ MT('indent_parentheses', "[tag foo] {", - " [property color]: [atom darken]([variable-2 $blue],", + " [property color]: [variable&callee darken]([variable-2 $blue],", " [number 9%]);", "}"); diff --git a/mode/css/test.js b/mode/css/test.js index 5bfc7af9..64352d74 100644 --- a/mode/css/test.js +++ b/mode/css/test.js @@ -89,11 +89,11 @@ "[tag foo] { [property margin]: [number 0]; [property padding]: [number 0]; }"); MT("tagTwoPropertiesURL", - "[tag foo] { [property background]: [atom url]([string //example.com/foo.png]); [property padding]: [number 0]; }"); + "[tag foo] { [property background]: [variable&callee url]([string //example.com/foo.png]); [property padding]: [number 0]; }"); MT("indent_tagSelector", "[tag strong], [tag em] {", - " [property background]: [atom rgba](", + " [property background]: [variable&callee rgba](", " [number 255], [number 255], [number 0], [number .2]", " );", "}"); @@ -114,7 +114,7 @@ MT("indent_parentheses", "[tag foo]:[variable-3 before] {", - " [property background]: [atom url](", + " [property background]: [variable&callee url](", "[string blahblah]", "[string etc]", "[string ]) [keyword !important];", @@ -124,20 +124,20 @@ "[def @font-face] {", " [property font-family]: [string 'myfont'];", " [error nonsense]: [string 'abc'];", - " [property src]: [atom url]([string http://blah]),", - " [atom url]([string http://foo]);", + " [property src]: [variable&callee url]([string http://blah]),", + " [variable&callee url]([string http://foo]);", "}"); MT("empty_url", - "[def @import] [atom url]() [attribute screen];"); + "[def @import] [variable&callee url]() [attribute screen];"); MT("parens", "[qualifier .foo] {", - " [property background-image]: [variable fade]([atom #000], [number 20%]);", - " [property border-image]: [atom linear-gradient](", + " [property background-image]: [variable&callee fade]([atom #000], [number 20%]);", + " [property border-image]: [variable&callee linear-gradient](", " [atom to] [atom bottom],", - " [variable fade]([atom #000], [number 20%]) [number 0%],", - " [variable fade]([atom #000], [number 20%]) [number 100%]", + " [variable&callee fade]([atom #000], [number 20%]) [number 0%],", + " [variable&callee fade]([atom #000], [number 20%]) [number 100%]", " );", "}"); @@ -146,7 +146,7 @@ " [variable-2 --main-color]: [atom #06c];", "}", "[tag h1][builtin #foo] {", - " [property color]: [atom var]([variable-2 --main-color]);", + " [property color]: [variable&callee var]([variable-2 --main-color]);", "}"); MT("blank_css_variable", @@ -154,7 +154,7 @@ " [variable-2 --]: [atom #06c];", "}", "[tag h1][builtin #foo] {", - " [property color]: [atom var]([variable-2 --]);", + " [property color]: [variable&callee var]([variable-2 --]);", "}"); MT("supports", @@ -163,10 +163,10 @@ "}"); MT("document", - "[def @document] [tag url]([string http://blah]),", - " [tag url-prefix]([string https://]),", - " [tag domain]([string blah.com]),", - " [tag regexp]([string \".*blah.+\"]) {", + "[def @document] [variable&callee url]([string http://blah]),", + " [variable&callee url-prefix]([string https://]),", + " [variable&callee domain]([string blah.com]),", + " [variable&callee regexp]([string \".*blah.+\"]) {", " [builtin #id] {", " [property background-color]: [keyword white];", " }", @@ -176,16 +176,16 @@ "}"); MT("document_url", - "[def @document] [tag url]([string http://blah]) { [qualifier .class] { } }"); + "[def @document] [variable&callee url]([string http://blah]) { [qualifier .class] { } }"); MT("document_urlPrefix", - "[def @document] [tag url-prefix]([string https://]) { [builtin #id] { } }"); + "[def @document] [variable&callee url-prefix]([string https://]) { [builtin #id] { } }"); MT("document_domain", - "[def @document] [tag domain]([string blah.com]) { [tag foo] { } }"); + "[def @document] [variable&callee domain]([string blah.com]) { [tag foo] { } }"); MT("document_regexp", - "[def @document] [tag regexp]([string \".*blah.+\"]) { [builtin #id] { } }"); + "[def @document] [variable&callee regexp]([string \".*blah.+\"]) { [builtin #id] { } }"); MT("counter-style", "[def @counter-style] [variable binary] {", @@ -207,11 +207,11 @@ "[tag ol][qualifier .roman] { [property list-style]: [variable simple-roman]; }"); MT("counter-style-symbols", - "[tag ol] { [property list-style]: [atom symbols]([atom cyclic] [string \"*\"] [string \"\\2020\"] [string \"\\2021\"] [string \"\\A7\"]); }"); + "[tag ol] { [property list-style]: [variable&callee symbols]([atom cyclic] [string \"*\"] [string \"\\2020\"] [string \"\\2021\"] [string \"\\A7\"]); }"); MT("comment-does-not-disrupt", "[def @font-face] [comment /* foo */] {", - " [property src]: [atom url]([string x]);", + " [property src]: [variable&callee url]([string x]);", " [property font-family]: [variable One];", "}") })(); From be9d254ecc708563727d1f4198383af660b32080 Mon Sep 17 00:00:00 2001 From: Marijn Haverbeke Date: Sat, 29 Dec 2018 15:42:37 +0100 Subject: [PATCH 1506/1790] [ruby mode] Support indented heredoc end token Closes #5727 Closes #5728 --- mode/ruby/ruby.js | 1 + 1 file changed, 1 insertion(+) diff --git a/mode/ruby/ruby.js b/mode/ruby/ruby.js index f954ed22..3cb39712 100644 --- a/mode/ruby/ruby.js +++ b/mode/ruby/ruby.js @@ -218,6 +218,7 @@ CodeMirror.defineMode("ruby", function(config) { } function readHereDoc(phrase) { return function(stream, state) { + stream.eatSpace() if (stream.match(phrase)) state.tokenize.pop(); else stream.skipToEnd(); return "string"; From 64f7d4ce108468545012d0f6e6584054b7964261 Mon Sep 17 00:00:00 2001 From: Marijn Haverbeke Date: Wed, 2 Jan 2019 11:25:23 +0100 Subject: [PATCH 1507/1790] [ruby mode] Only allow indented heredoc end markers when a - was present Issue #5728 --- mode/ruby/ruby.js | 10 +++++----- mode/ruby/test.js | 7 +++++++ 2 files changed, 12 insertions(+), 5 deletions(-) diff --git a/mode/ruby/ruby.js b/mode/ruby/ruby.js index 3cb39712..5d928866 100644 --- a/mode/ruby/ruby.js +++ b/mode/ruby/ruby.js @@ -63,8 +63,8 @@ CodeMirror.defineMode("ruby", function(config) { } else if (ch == "#") { stream.skipToEnd(); return "comment"; - } else if (ch == "<" && (m = stream.match(/^<-?[\`\"\']?([a-zA-Z_?]\w*)[\`\"\']?(?:;|$)/))) { - return chain(readHereDoc(m[1]), stream, state); + } else if (ch == "<" && (m = stream.match(/^<(-)?[\`\"\']?([a-zA-Z_?]\w*)[\`\"\']?(?:;|$)/))) { + return chain(readHereDoc(m[2], m[1]), stream, state); } else if (ch == "0") { if (stream.eat("x")) stream.eatWhile(/[\da-fA-F]/); else if (stream.eat("b")) stream.eatWhile(/[01]/); @@ -216,9 +216,9 @@ CodeMirror.defineMode("ruby", function(config) { return style; }; } - function readHereDoc(phrase) { + function readHereDoc(phrase, mayIndent) { return function(stream, state) { - stream.eatSpace() + if (mayIndent) stream.eatSpace() if (stream.match(phrase)) state.tokenize.pop(); else stream.skipToEnd(); return "string"; @@ -277,7 +277,7 @@ CodeMirror.defineMode("ruby", function(config) { }, indent: function(state, textAfter) { - if (state.tokenize[state.tokenize.length-1] != tokenBase) return 0; + if (state.tokenize[state.tokenize.length-1] != tokenBase) return CodeMirror.Pass; var firstChar = textAfter && textAfter.charAt(0); var ct = state.context; var closing = ct.type == matching[firstChar] || diff --git a/mode/ruby/test.js b/mode/ruby/test.js index 5677e0de..905c0e48 100644 --- a/mode/ruby/test.js +++ b/mode/ruby/test.js @@ -13,4 +13,11 @@ MT("complex_regexp", "[keyword if] [variable cr] [operator =~] [string-2 /(?: \\( #{][tag RE_NOT][string-2 }\\( | #{][tag RE_NOT_PAR_OR][string-2 }* #{][tag RE_OPA_OR][string-2 } )/][variable x]") + + MT("indented_heredoc", + "[keyword def] [def x]", + " [variable y] [operator =] [string <<-FOO]", + "[string bar]", + "[string FOO]", + "[keyword end]") })(); From fe2bdb372b7c1f2978d8b5934a1d16546f032a9a Mon Sep 17 00:00:00 2001 From: Marijn Haverbeke Date: Thu, 3 Jan 2019 11:28:23 +0100 Subject: [PATCH 1508/1790] [javascript mode] Improve support for syntactic corner cases in TypeScript Closes #5734 --- mode/javascript/javascript.js | 5 ++++- 1 file changed, 4 insertions(+), 1 deletion(-) diff --git a/mode/javascript/javascript.js b/mode/javascript/javascript.js index 42b4b191..aa80f295 100644 --- a/mode/javascript/javascript.js +++ b/mode/javascript/javascript.js @@ -377,7 +377,7 @@ CodeMirror.defineMode("javascript", function(config, parserConfig) { else return cont(pushlex("form"), pattern, expect("{"), pushlex("}"), block, poplex, poplex) } else if (isTS && value == "namespace") { cx.marked = "keyword" - return cont(pushlex("form"), expression, block, poplex) + return cont(pushlex("form"), expression, statement, poplex) } else if (isTS && value == "abstract") { cx.marked = "keyword" return cont(statement) @@ -552,6 +552,7 @@ CodeMirror.defineMode("javascript", function(config, parserConfig) { }, proceed); } if (type == end || value == end) return cont(); + if (sep && sep.indexOf(";") > -1) return pass(what) return cont(expect(end)); } return function(type, value) { @@ -614,6 +615,8 @@ CodeMirror.defineMode("javascript", function(config, parserConfig) { return cont(typeexpr) } else if (type == "[") { return cont(expression, maybetype, expect("]"), typeprop) + } else if (type == "(") { + return cont(pushlex(")"), commasep(funarg, ")"), poplex, typeprop) } } function typearg(type, value) { From c608b998c104c91ae01f7effb4cb537479914807 Mon Sep 17 00:00:00 2001 From: Marijn Haverbeke Date: Fri, 4 Jan 2019 08:29:11 +0100 Subject: [PATCH 1509/1790] [javascript] Don't expect function bodies in interface declarations Issue #5734 --- mode/javascript/javascript.js | 14 ++++++++++++-- mode/javascript/test.js | 6 ++++++ 2 files changed, 18 insertions(+), 2 deletions(-) diff --git a/mode/javascript/javascript.js b/mode/javascript/javascript.js index aa80f295..93b8cfdc 100644 --- a/mode/javascript/javascript.js +++ b/mode/javascript/javascript.js @@ -365,7 +365,10 @@ CodeMirror.defineMode("javascript", function(config, parserConfig) { } if (type == "function") return cont(functiondef); if (type == "for") return cont(pushlex("form"), forspec, statement, poplex); - if (type == "class" || (isTS && value == "interface")) { cx.marked = "keyword"; return cont(pushlex("form"), className, poplex); } + if (type == "class" || (isTS && value == "interface")) { + cx.marked = "keyword" + return cont(pushlex("form", type == "class" ? type : value), className, poplex) + } if (type == "variable") { if (isTS && value == "declare") { cx.marked = "keyword" @@ -701,6 +704,12 @@ CodeMirror.defineMode("javascript", function(config, parserConfig) { if (type == "(") return cont(pushcontext, pushlex(")"), commasep(funarg, ")"), poplex, mayberettype, statement, popcontext); if (isTS && value == "<") return cont(pushlex(">"), commasep(typeparam, ">"), poplex, functiondef) } + function functiondecl(type, value) { + if (value == "*") {cx.marked = "keyword"; return cont(functiondecl);} + if (type == "variable") {register(value); return cont(functiondecl);} + if (type == "(") return cont(pushcontext, pushlex(")"), commasep(funarg, ")"), poplex, mayberettype, popcontext); + if (isTS && value == "<") return cont(pushlex(">"), commasep(typeparam, ">"), poplex, functiondecl) + } function funarg(type, value) { if (value == "@") cont(expression, funarg) if (type == "spread") return cont(funarg); @@ -749,7 +758,8 @@ CodeMirror.defineMode("javascript", function(config, parserConfig) { if (value == "?") return cont(classfield) if (type == ":") return cont(typeexpr, maybeAssign) if (value == "=") return cont(expressionNoComma) - return pass(functiondef) + var context = cx.state.lexical.prev, isInterface = context && context.info == "interface" + return pass(isInterface ? functiondecl : functiondef) } function afterExport(type, value) { if (value == "*") { cx.marked = "keyword"; return cont(maybeFrom, expect(";")); } diff --git a/mode/javascript/test.js b/mode/javascript/test.js index 21547e47..04faeafa 100644 --- a/mode/javascript/test.js +++ b/mode/javascript/test.js @@ -448,6 +448,12 @@ TS("abstract class", "[keyword export] [keyword abstract] [keyword class] [def Foo] {}") + TS("interface without semicolons", + "[keyword interface] [def Foo] {", + " [property greet]([def x]: [type int]): [type blah]", + " [property bar]: [type void]", + "}") + var jsonld_mode = CodeMirror.getMode( {indentUnit: 2}, {name: "javascript", jsonld: true} From 05490c8c40584b06f4cd0ba88c9c2a59f32af5d6 Mon Sep 17 00:00:00 2001 From: joewalsh Date: Wed, 9 Jan 2019 02:19:11 -0500 Subject: [PATCH 1510/1790] Add options for autocorrect, autocapitalize --- addon/hint/html-hint.js | 2 ++ doc/manual.html | 9 +++++++++ src/edit/options.js | 2 ++ src/input/ContentEditableInput.js | 2 +- src/input/input.js | 6 +++--- 5 files changed, 17 insertions(+), 4 deletions(-) diff --git a/addon/hint/html-hint.js b/addon/hint/html-hint.js index b5aeb47f..d0cca4f6 100644 --- a/addon/hint/html-hint.js +++ b/addon/hint/html-hint.js @@ -322,6 +322,8 @@ itemtype: null, lang: ["en", "es"], spellcheck: ["true", "false"], + autocorrect: ["true", "false"], + autocapitalize: ["true", "false"], style: null, tabindex: ["1", "2", "3", "4", "5", "6", "7", "8", "9"], title: null, diff --git a/doc/manual.html b/doc/manual.html index 97f12949..0efa107d 100644 --- a/doc/manual.html +++ b/doc/manual.html @@ -557,6 +557,15 @@

        Configuration

        always rendered, and thus the browser's text search works on it. This will have bad effects on performance of big documents.
    + +
    spellcheck: boolean
    +
    Specifies whether or not spellcheck will be enabled on the input.
    + +
    autocorrect: boolean
    +
    Specifies whether or not autocorrect will be enabled on the input.
    + +
    autocapitalize: boolean
    +
    Specifies whether or not autocapitalization will be enabled on the input.
    diff --git a/src/edit/options.js b/src/edit/options.js index a649a4a6..27ecac7c 100644 --- a/src/edit/options.js +++ b/src/edit/options.js @@ -79,6 +79,8 @@ export function defineOptions(CodeMirror) { throw new Error("inputStyle can not (yet) be changed in a running editor") // FIXME }, true) option("spellcheck", false, (cm, val) => cm.getInputField().spellcheck = val, true) + option("autocorrect", false, (cm, val) => cm.getInputField().autocorrect = val, true) + option("autocapitalize", false, (cm, val) => cm.getInputField().autocapitalize = val, true) option("rtlMoveVisually", !windows) option("wholeLineUpdateBefore", true) diff --git a/src/input/ContentEditableInput.js b/src/input/ContentEditableInput.js index 986690b3..b8d1aff0 100644 --- a/src/input/ContentEditableInput.js +++ b/src/input/ContentEditableInput.js @@ -29,7 +29,7 @@ export default class ContentEditableInput { init(display) { let input = this, cm = input.cm let div = input.div = display.lineDiv - disableBrowserMagic(div, cm.options.spellcheck) + disableBrowserMagic(div, cm.options.spellcheck, cm.options.autocorrect, cm.options.autocapitalize) on(div, "paste", e => { if (signalDOMEvent(cm, e) || handlePaste(e, cm)) return diff --git a/src/input/input.js b/src/input/input.js index eb9c2cab..90833d19 100644 --- a/src/input/input.js +++ b/src/input/input.js @@ -112,9 +112,9 @@ export function copyableRanges(cm) { return {text: text, ranges: ranges} } -export function disableBrowserMagic(field, spellcheck) { - field.setAttribute("autocorrect", "off") - field.setAttribute("autocapitalize", "off") +export function disableBrowserMagic(field, spellcheck, autocorrect, autocapitalize) { + field.setAttribute("autocorrect", !!autocorrect) + field.setAttribute("autocapitalize", !!autocapitalize) field.setAttribute("spellcheck", !!spellcheck) } From 12f8159c8b0a6a0d6c347e9add69febf15c1e62c Mon Sep 17 00:00:00 2001 From: Marijn Haverbeke Date: Wed, 9 Jan 2019 08:28:41 +0100 Subject: [PATCH 1511/1790] [sql mode] Fall make sure there are keywords and builtins defined when no config is provided Closes #5740 --- mode/sql/sql.js | 15 +++++---------- 1 file changed, 5 insertions(+), 10 deletions(-) diff --git a/mode/sql/sql.js b/mode/sql/sql.js index e7442c39..69f6b2f3 100644 --- a/mode/sql/sql.js +++ b/mode/sql/sql.js @@ -12,12 +12,10 @@ "use strict"; CodeMirror.defineMode("sql", function(config, parserConfig) { - "use strict"; - var client = parserConfig.client || {}, atoms = parserConfig.atoms || {"false": true, "true": true, "null": true}, - builtin = parserConfig.builtin || {}, - keywords = parserConfig.keywords || {}, + builtin = parserConfig.builtin || set(defaultBuiltin), + keywords = parserConfig.keywords || set(sqlKeywords), operatorChars = parserConfig.operatorChars || /^[*+\-%<>!=&|~^\/]/, support = parserConfig.support || {}, hooks = parserConfig.hooks || {}, @@ -205,9 +203,6 @@ CodeMirror.defineMode("sql", function(config, parserConfig) { }; }); -(function() { - "use strict"; - // `identifier` function hookIdentifier(stream) { // MySQL/MariaDB identifiers @@ -282,11 +277,13 @@ CodeMirror.defineMode("sql", function(config, parserConfig) { return obj; } + var defaultBuiltin = "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" + // A generic SQL Mode. It's not a standard, it just try to support what is generally supported CodeMirror.defineMIME("text/x-sql", { name: "sql", 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"), + builtin: set(defaultBuiltin), atoms: set("false true null unknown"), dateSQL: set("date time timestamp"), support: set("ODBCdotTable doubleQuote binaryNumber hexNumber") @@ -464,8 +461,6 @@ CodeMirror.defineMode("sql", function(config, parserConfig) { dateSQL: set("time"), support: set("decimallessFloat zerolessFloat binaryNumber hexNumber") }); -}()); - }); /* From 0e804008fd49da3da2012941b07c78322e0b0925 Mon Sep 17 00:00:00 2001 From: Marijn Haverbeke Date: Thu, 10 Jan 2019 10:04:05 +0100 Subject: [PATCH 1512/1790] Make sure indent is always passed three arguments Closes codemirror/google-modes#235 --- addon/mode/multiplex.js | 8 ++++---- addon/mode/overlay.js | 4 ++-- mode/htmlmixed/htmlmixed.js | 4 ++-- mode/jsx/jsx.js | 4 ++-- mode/php/php.js | 8 ++++---- mode/smarty/smarty.js | 4 ++-- mode/soy/soy.js | 8 ++++---- test/mode_test.js | 2 +- 8 files changed, 21 insertions(+), 21 deletions(-) diff --git a/addon/mode/multiplex.js b/addon/mode/multiplex.js index 738ea98a..93fd9a5a 100644 --- a/addon/mode/multiplex.js +++ b/addon/mode/multiplex.js @@ -54,7 +54,7 @@ CodeMirror.multiplexingMode = function(outer /*, others */) { // Get the outer indent, making sure to handle CodeMirror.Pass var outerIndent = 0; if (outer.indent) { - var possibleOuterIndent = outer.indent(state.outer, ""); + var possibleOuterIndent = outer.indent(state.outer, "", ""); if (possibleOuterIndent !== CodeMirror.Pass) outerIndent = possibleOuterIndent; } @@ -96,10 +96,10 @@ CodeMirror.multiplexingMode = function(outer /*, others */) { } }, - indent: function(state, textAfter) { + indent: function(state, textAfter, line) { var mode = state.innerActive ? state.innerActive.mode : outer; if (!mode.indent) return CodeMirror.Pass; - return mode.indent(state.innerActive ? state.inner : state.outer, textAfter); + return mode.indent(state.innerActive ? state.inner : state.outer, textAfter, line); }, blankLine: function(state) { @@ -112,7 +112,7 @@ CodeMirror.multiplexingMode = function(outer /*, others */) { var other = others[i]; if (other.open === "\n") { state.innerActive = other; - state.inner = CodeMirror.startState(other.mode, mode.indent ? mode.indent(state.outer, "") : 0); + state.inner = CodeMirror.startState(other.mode, mode.indent ? mode.indent(state.outer, "", "") : 0); } } } else if (state.innerActive.close === "\n") { diff --git a/addon/mode/overlay.js b/addon/mode/overlay.js index 839d9e55..016e3c28 100644 --- a/addon/mode/overlay.js +++ b/addon/mode/overlay.js @@ -68,8 +68,8 @@ CodeMirror.overlayMode = function(base, overlay, combine) { else return state.overlayCur; }, - indent: base.indent && function(state, textAfter) { - return base.indent(state.base, textAfter); + indent: base.indent && function(state, textAfter, line) { + return base.indent(state.base, textAfter, line); }, electricChars: base.electricChars, diff --git a/mode/htmlmixed/htmlmixed.js b/mode/htmlmixed/htmlmixed.js index c9925384..8341ac82 100644 --- a/mode/htmlmixed/htmlmixed.js +++ b/mode/htmlmixed/htmlmixed.js @@ -105,7 +105,7 @@ return maybeBackup(stream, endTag, state.localMode.token(stream, state.localState)); }; state.localMode = mode; - state.localState = CodeMirror.startState(mode, htmlMode.indent(state.htmlState, "")); + state.localState = CodeMirror.startState(mode, htmlMode.indent(state.htmlState, "", "")); } else if (state.inTag) { state.inTag += stream.current() if (stream.eol()) state.inTag += " " @@ -135,7 +135,7 @@ indent: function (state, textAfter, line) { if (!state.localMode || /^\s*<\//.test(textAfter)) - return htmlMode.indent(state.htmlState, textAfter); + return htmlMode.indent(state.htmlState, textAfter, line); else if (state.localMode.indent) return state.localMode.indent(state.localState, textAfter, line); else diff --git a/mode/jsx/jsx.js b/mode/jsx/jsx.js index 2c91beb9..889d3fe5 100644 --- a/mode/jsx/jsx.js +++ b/mode/jsx/jsx.js @@ -32,7 +32,7 @@ function flatXMLIndent(state) { var tagName = state.tagName state.tagName = null - var result = xmlMode.indent(state, "") + var result = xmlMode.indent(state, "", "") state.tagName = tagName return result } @@ -105,7 +105,7 @@ function jsToken(stream, state, cx) { if (stream.peek() == "<" && jsMode.expressionAllowed(stream, cx.state)) { jsMode.skipExpression(cx.state) - state.context = new Context(CodeMirror.startState(xmlMode, jsMode.indent(cx.state, "")), + state.context = new Context(CodeMirror.startState(xmlMode, jsMode.indent(cx.state, "", "")), xmlMode, 0, state.context) return null } diff --git a/mode/php/php.js b/mode/php/php.js index 80e2f20b..5f3a1439 100644 --- a/mode/php/php.js +++ b/mode/php/php.js @@ -160,7 +160,7 @@ if (!isPHP) { if (stream.match(/^<\?\w*/)) { state.curMode = phpMode; - if (!state.php) state.php = CodeMirror.startState(phpMode, htmlMode.indent(state.html, "")) + if (!state.php) state.php = CodeMirror.startState(phpMode, htmlMode.indent(state.html, "", "")) state.curState = state.php; return "meta"; } @@ -213,11 +213,11 @@ token: dispatch, - indent: function(state, textAfter) { + indent: function(state, textAfter, line) { if ((state.curMode != phpMode && /^\s*<\//.test(textAfter)) || (state.curMode == phpMode && /^\?>/.test(textAfter))) - return htmlMode.indent(state.html, textAfter); - return state.curMode.indent(state.curState, textAfter); + return htmlMode.indent(state.html, textAfter, line); + return state.curMode.indent(state.curState, textAfter, line); }, blockCommentStart: "/*", diff --git a/mode/smarty/smarty.js b/mode/smarty/smarty.js index c0777e0c..57852feb 100644 --- a/mode/smarty/smarty.js +++ b/mode/smarty/smarty.js @@ -210,9 +210,9 @@ state.last = last; return style; }, - indent: function(state, text) { + indent: function(state, text, line) { if (state.tokenize == tokenTop && baseMode.indent) - return baseMode.indent(state.base, text); + return baseMode.indent(state.base, text, line); else return CodeMirror.Pass; }, diff --git a/mode/soy/soy.js b/mode/soy/soy.js index 7ebd4b3d..faf4314b 100644 --- a/mode/soy/soy.js +++ b/mode/soy/soy.js @@ -251,7 +251,7 @@ var mode = modes[kind] || modes.html; var localState = last(state.localStates); if (localState.mode.indent) { - state.indent += localState.mode.indent(localState.state, ""); + state.indent += localState.mode.indent(localState.state, "", ""); } state.localStates.push({ mode: mode, @@ -310,7 +310,7 @@ state.localStates.pop(); var localState = last(state.localStates); if (localState.mode.indent) { - state.indent -= localState.mode.indent(localState.state, ""); + state.indent -= localState.mode.indent(localState.state, "", ""); } } state.soyState.push("tag"); @@ -346,7 +346,7 @@ return tokenUntil(stream, state, /\{|\s+\/\/|\/\*/); }, - indent: function(state, textAfter) { + indent: function(state, textAfter, line) { var indent = state.indent, top = last(state.soyState); if (top == "comment") return CodeMirror.Pass; @@ -360,7 +360,7 @@ } var localState = last(state.localStates); if (indent && localState.mode.indent) { - indent += localState.mode.indent(localState.state, textAfter); + indent += localState.mode.indent(localState.state, textAfter, line); } return indent; }, diff --git a/test/mode_test.js b/test/mode_test.js index f4c4dbfe..e7c0cf92 100644 --- a/test/mode_test.js +++ b/test/mode_test.js @@ -123,7 +123,7 @@ var line = lines[i], newLine = true; if (mode.indent) { var ws = line.match(/^\s*/)[0]; - var indent = mode.indent(state, line.slice(ws.length)); + var indent = mode.indent(state, line.slice(ws.length), line); if (indent != CodeMirror.Pass && indent != ws.length) (st.indentFailures || (st.indentFailures = [])).push( "Indentation of line " + (i + 1) + " is " + indent + " (expected " + ws.length + ")"); From 816430b467bcdec29b136e623f4af43fbdaaf82d Mon Sep 17 00:00:00 2001 From: Marijn Haverbeke Date: Fri, 11 Jan 2019 08:13:52 +0100 Subject: [PATCH 1513/1790] [julia mode] Don't autoclose single quotes in Julia code Closes #5742 --- mode/julia/julia.js | 1 + 1 file changed, 1 insertion(+) diff --git a/mode/julia/julia.js b/mode/julia/julia.js index 3e3560dc..435e9419 100644 --- a/mode/julia/julia.js +++ b/mode/julia/julia.js @@ -422,6 +422,7 @@ CodeMirror.defineMode("julia", function(config, parserConf) { blockCommentStart: "#=", blockCommentEnd: "=#", lineComment: "#", + closeBrackets: "()[]{}\"\"", fold: "indent" }; return external; From 74f31990f24ae03e214cf67c430ee2697abebe8c Mon Sep 17 00:00:00 2001 From: Michael Wadman <8885966+mwadman@users.noreply.github.com> Date: Mon, 14 Jan 2019 21:52:18 +1300 Subject: [PATCH 1514/1790] [jinja2 mode] Add MIME type --- mode/jinja2/jinja2.js | 2 ++ mode/meta.js | 2 +- 2 files changed, 3 insertions(+), 1 deletion(-) diff --git a/mode/jinja2/jinja2.js b/mode/jinja2/jinja2.js index 3ccfe7f1..77c9b22c 100644 --- a/mode/jinja2/jinja2.js +++ b/mode/jinja2/jinja2.js @@ -141,4 +141,6 @@ blockCommentEnd: "#}" }; }); + + CodeMirror.defineMIME("text/jinja2", "jinja2"); }); diff --git a/mode/meta.js b/mode/meta.js index 591312b5..ef0a26a2 100644 --- a/mode/meta.js +++ b/mode/meta.js @@ -75,7 +75,7 @@ {name: "JSON", mimes: ["application/json", "application/x-json"], mode: "javascript", ext: ["json", "map"], alias: ["json5"]}, {name: "JSON-LD", mime: "application/ld+json", mode: "javascript", ext: ["jsonld"], alias: ["jsonld"]}, {name: "JSX", mime: "text/jsx", mode: "jsx", ext: ["jsx"]}, - {name: "Jinja2", mime: "null", mode: "jinja2", ext: ["j2", "jinja", "jinja2"]}, + {name: "Jinja2", mime: "text/jinja2", mode: "jinja2", ext: ["j2", "jinja", "jinja2"]}, {name: "Julia", mime: "text/x-julia", mode: "julia", ext: ["jl"]}, {name: "Kotlin", mime: "text/x-kotlin", mode: "clike", ext: ["kt"]}, {name: "LESS", mime: "text/x-less", mode: "css", ext: ["less"]}, From e68dd640f858bff1eb289cc25300c9122ac8cad7 Mon Sep 17 00:00:00 2001 From: Marijn Haverbeke Date: Mon, 21 Jan 2019 16:20:05 +0100 Subject: [PATCH 1515/1790] [javascript mode] Improve TypeScript support Add type conditionals, fix parenthesized types, support quoted property names, fix handling of type parameters in type definitions, and handle call signatures in interfaces and object types. Closes #5737 --- mode/javascript/javascript.js | 30 +++++++++++++++++++++--------- 1 file changed, 21 insertions(+), 9 deletions(-) diff --git a/mode/javascript/javascript.js b/mode/javascript/javascript.js index 93b8cfdc..269cdb4d 100644 --- a/mode/javascript/javascript.js +++ b/mode/javascript/javascript.js @@ -376,7 +376,7 @@ CodeMirror.defineMode("javascript", function(config, parserConfig) { } else if (isTS && (value == "module" || value == "enum" || value == "type") && cx.stream.match(/^\s*\w/, false)) { cx.marked = "keyword" if (value == "enum") return cont(enumdef); - else if (value == "type") return cont(typeexpr, expect("operator"), typeexpr, expect(";")); + else if (value == "type") return cont(typename, expect("operator"), typeexpr, expect(";")); else return cont(pushlex("form"), pattern, expect("{"), pushlex("}"), block, poplex, poplex) } else if (isTS && value == "namespace") { cx.marked = "keyword" @@ -574,7 +574,7 @@ CodeMirror.defineMode("javascript", function(config, parserConfig) { } function maybetype(type, value) { if (isTS) { - if (type == ":") return cont(typeexpr); + if (type == ":" || value == "in") return cont(typeexpr); if (value == "?") return cont(maybetype); } } @@ -591,9 +591,9 @@ CodeMirror.defineMode("javascript", function(config, parserConfig) { } } function typeexpr(type, value) { - if (value == "keyof" || value == "typeof") { + if (value == "keyof" || value == "typeof" || value == "infer") { cx.marked = "keyword" - return cont(value == "keyof" ? typeexpr : expressionNoComma) + return cont(value == "typeof" ? expressionNoComma : typeexpr) } if (type == "variable" || value == "void") { cx.marked = "type" @@ -602,7 +602,7 @@ CodeMirror.defineMode("javascript", function(config, parserConfig) { if (type == "string" || type == "number" || type == "atom") return cont(afterType); if (type == "[") return cont(pushlex("]"), commasep(typeexpr, "]", ","), poplex, afterType) if (type == "{") return cont(pushlex("}"), commasep(typeprop, "}", ",;"), poplex, afterType) - if (type == "(") return cont(commasep(typearg, ")"), maybeReturnType) + if (type == "(") return cont(commasep(typearg, ")"), maybeReturnType, afterType) if (type == "<") return cont(commasep(typeexpr, ">"), typeexpr) } function maybeReturnType(type) { @@ -612,26 +612,28 @@ CodeMirror.defineMode("javascript", function(config, parserConfig) { if (type == "variable" || cx.style == "keyword") { cx.marked = "property" return cont(typeprop) - } else if (value == "?") { + } else if (value == "?" || type == "number" || type == "string") { return cont(typeprop) } else if (type == ":") { return cont(typeexpr) } else if (type == "[") { - return cont(expression, maybetype, expect("]"), typeprop) + return cont(expect("variable"), maybetype, expect("]"), typeprop) } else if (type == "(") { - return cont(pushlex(")"), commasep(funarg, ")"), poplex, typeprop) + return pass(functiondecl, typeprop) } } function typearg(type, value) { if (type == "variable" && cx.stream.match(/^\s*[?:]/, false) || value == "?") return cont(typearg) if (type == ":") return cont(typeexpr) + if (type == "spread") return cont(typearg) return pass(typeexpr) } function afterType(type, value) { if (value == "<") return cont(pushlex(">"), commasep(typeexpr, ">"), poplex, afterType) if (value == "|" || type == "." || value == "&") return cont(typeexpr) - if (type == "[") return cont(expect("]"), afterType) + if (type == "[") return cont(typeexpr, expect("]"), afterType) if (value == "extends" || value == "implements") { cx.marked = "keyword"; return cont(typeexpr) } + if (value == "?") return cont(typeexpr, expect(":"), typeexpr) } function maybeTypeArgs(_, value) { if (value == "<") return cont(pushlex(">"), commasep(typeexpr, ">"), poplex, afterType) @@ -710,6 +712,14 @@ CodeMirror.defineMode("javascript", function(config, parserConfig) { if (type == "(") return cont(pushcontext, pushlex(")"), commasep(funarg, ")"), poplex, mayberettype, popcontext); if (isTS && value == "<") return cont(pushlex(">"), commasep(typeparam, ">"), poplex, functiondecl) } + function typename(type, value) { + if (type == "keyword" || type == "variable") { + cx.marked = "type" + return cont(typename) + } else if (value == "<") { + return cont(pushlex(">"), commasep(typeparam, ">"), poplex) + } + } function funarg(type, value) { if (value == "@") cont(expression, funarg) if (type == "spread") return cont(funarg); @@ -744,12 +754,14 @@ CodeMirror.defineMode("javascript", function(config, parserConfig) { cx.marked = "property"; return cont(isTS ? classfield : functiondef, classBody); } + if (type == "number" || type == "string") return cont(isTS ? classfield : functiondef, classBody); if (type == "[") return cont(expression, maybetype, expect("]"), isTS ? classfield : functiondef, classBody) if (value == "*") { cx.marked = "keyword"; return cont(classBody); } + if (isTS && type == "(") return pass(functiondecl, classBody) if (type == ";") return cont(classBody); if (type == "}") return cont(); if (value == "@") return cont(expression, classBody) From 55a1ead02d1d208279a704a59cc9281c0e09a76a Mon Sep 17 00:00:00 2001 From: Marijn Haverbeke Date: Mon, 21 Jan 2019 16:28:39 +0100 Subject: [PATCH 1516/1790] [javascript mode] Accept commas as separators in TypeScript interfaces Issue #5734 --- 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 269cdb4d..60d595da 100644 --- a/mode/javascript/javascript.js +++ b/mode/javascript/javascript.js @@ -762,7 +762,7 @@ CodeMirror.defineMode("javascript", function(config, parserConfig) { return cont(classBody); } if (isTS && type == "(") return pass(functiondecl, classBody) - if (type == ";") return cont(classBody); + if (type == ";" || type == ",") return cont(classBody); if (type == "}") return cont(); if (value == "@") return cont(expression, classBody) } From e8f6d1210255db6b987112e36f484d6aea503f7f Mon Sep 17 00:00:00 2001 From: Marijn Haverbeke Date: Mon, 21 Jan 2019 16:56:54 +0100 Subject: [PATCH 1517/1790] Mark version 5.43.0 --- AUTHORS | 3 +++ CHANGELOG.md | 14 ++++++++++++++ doc/manual.html | 2 +- doc/releases.html | 9 +++++++++ index.html | 2 +- package.json | 2 +- src/edit/main.js | 2 +- 7 files changed, 30 insertions(+), 4 deletions(-) diff --git a/AUTHORS b/AUTHORS index 8e53b9b1..55bff7ae 100644 --- a/AUTHORS +++ b/AUTHORS @@ -247,6 +247,7 @@ Franco Catena Frank Wiegand fraxx001 Fredrik Borg +FUJI Goro (gfx) Gabriel Gheorghian Gabriel Horner Gabriel Nahmias @@ -355,6 +356,7 @@ jochenberger Jochen Berger Joel Einbinder joelpinheiro +joewalsh Johan Ask John Connor John-David Dalton @@ -506,6 +508,7 @@ Michael Goderbauer Michael Grey Michael Kaminsky Michael Lehenbauer +Michael Wadman Michael Walker Michael Zhou Michal Čihař diff --git a/CHANGELOG.md b/CHANGELOG.md index b5c2b9d7..e07d5c74 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -1,3 +1,17 @@ +## 5.43.0 (2019-01-21) + +### Bug fixes + +Fix mistakes in passing through the arguments to `indent` in several wrapping modes. + +[javascript mode](https://codemirror.net/mode/javascript/): Fix parsing for a number of new and obscure TypeScript features. + +[ruby mode](https://codemirror.net/mode/ruby): Support indented end tokens for heredoc strings. + +### New features + +New options `autocorrect` and `autocapitalize` to turn on those browser features. + ## 5.42.2 (2018-12-21) ### Bug fixes diff --git a/doc/manual.html b/doc/manual.html index 0efa107d..4c2e0bf2 100644 --- a/doc/manual.html +++ b/doc/manual.html @@ -69,7 +69,7 @@

    User manual and reference guide - version 5.42.3 + version 5.43.0

    CodeMirror is a code-editor component that can be embedded in diff --git a/doc/releases.html b/doc/releases.html index a7a5cce9..ba0332ab 100644 --- a/doc/releases.html +++ b/doc/releases.html @@ -30,6 +30,15 @@

    Release notes and version history

    Version 5.x

    +

    21-01-2019: Version 5.43.0:

    + +
      +
    • Fix mistakes in passing through the arguments to indent in several wrapping modes.
    • +
    • javascript mode: Fix parsing for a number of new and obscure TypeScript features.
    • +
    • ruby mode: Support indented end tokens for heredoc strings.
    • +
    • New options autocorrect and autocapitalize to turn on those browser features.
    • +
    +

    21-12-2018: Version 5.42.2:

      diff --git a/index.html b/index.html index b9dc9b9c..a63d4d32 100644 --- a/index.html +++ b/index.html @@ -99,7 +99,7 @@

      This is CodeMirror

      - Get the current version: 5.42.2.
      + Get the current version: 5.43.0.
      You can see the code,
      read the release notes,
      or study the user manual. diff --git a/package.json b/package.json index a92d91dc..6c2f177e 100644 --- a/package.json +++ b/package.json @@ -1,6 +1,6 @@ { "name": "codemirror", - "version": "5.42.3", + "version": "5.43.0", "main": "lib/codemirror.js", "style": "lib/codemirror.css", "author": { diff --git a/src/edit/main.js b/src/edit/main.js index a9edb687..58b29cb5 100644 --- a/src/edit/main.js +++ b/src/edit/main.js @@ -66,4 +66,4 @@ import { addLegacyProps } from "./legacy.js" addLegacyProps(CodeMirror) -CodeMirror.version = "5.42.3" +CodeMirror.version = "5.43.0" From 85c5018ebb5d36db88c642564f0152d8f7aaf8cb Mon Sep 17 00:00:00 2001 From: Chhekur <820121223505e@gmail.com> Date: Tue, 15 Jan 2019 17:23:16 +0530 Subject: [PATCH 1518/1790] [real-world uses] Add Colon --- doc/realworld.html | 1 + 1 file changed, 1 insertion(+) diff --git a/doc/realworld.html b/doc/realworld.html index b226f020..8d4e07ae 100644 --- a/doc/realworld.html +++ b/doc/realworld.html @@ -39,6 +39,7 @@

      CodeMirror real-world uses

    • Cargo Collective (creative publishing platform)
    • Chrome DevTools
    • ClickHelp (technical writing tool)
    • +
    • Colon (A flexible text editor or IDE)
    • CodeWorld (Haskell playground)
    • Complete.ly playground
    • Codeanywhere (multi-platform cloud editor)
    • From 2587b962aa6129fd798138268e0770ab564ed692 Mon Sep 17 00:00:00 2001 From: Marijn Haverbeke Date: Mon, 21 Jan 2019 17:00:19 +0100 Subject: [PATCH 1519/1790] Bump version number post-5.43.0 --- doc/manual.html | 2 +- package.json | 2 +- src/edit/main.js | 2 +- 3 files changed, 3 insertions(+), 3 deletions(-) diff --git a/doc/manual.html b/doc/manual.html index 4c2e0bf2..784b4081 100644 --- a/doc/manual.html +++ b/doc/manual.html @@ -69,7 +69,7 @@

      User manual and reference guide - version 5.43.0 + version 5.43.1

      CodeMirror is a code-editor component that can be embedded in diff --git a/package.json b/package.json index 6c2f177e..c5cb8dce 100644 --- a/package.json +++ b/package.json @@ -1,6 +1,6 @@ { "name": "codemirror", - "version": "5.43.0", + "version": "5.43.1", "main": "lib/codemirror.js", "style": "lib/codemirror.css", "author": { diff --git a/src/edit/main.js b/src/edit/main.js index 58b29cb5..d8ea285f 100644 --- a/src/edit/main.js +++ b/src/edit/main.js @@ -66,4 +66,4 @@ import { addLegacyProps } from "./legacy.js" addLegacyProps(CodeMirror) -CodeMirror.version = "5.43.0" +CodeMirror.version = "5.43.1" From 2dab4d527c7a55aabc0a52b28b47ab35b1412ca3 Mon Sep 17 00:00:00 2001 From: Nicolas Chevobbe Date: Fri, 18 Jan 2019 15:10:35 +0100 Subject: [PATCH 1520/1790] Fix middle-click paste on Firefox. The current solution consists in focusing the input when the scroll container capture a paste event, which does not work on Firefox. In order to fix this, creating a new paste event, copying the clipboardData and dispatching it on the textarea seems to do the trick. This works on Firefox 64, with the config set to true. --- src/input/TextareaInput.js | 12 ++++++++++-- 1 file changed, 10 insertions(+), 2 deletions(-) diff --git a/src/input/TextareaInput.js b/src/input/TextareaInput.js index ad81d8a9..0e1c37d6 100644 --- a/src/input/TextareaInput.js +++ b/src/input/TextareaInput.js @@ -76,8 +76,16 @@ export default class TextareaInput { on(display.scroller, "paste", e => { if (eventInWidget(display, e) || signalDOMEvent(cm, e)) return - cm.state.pasteIncoming = true - input.focus() + if (!te.dispatchEvent) { + cm.state.pasteIncoming = true + input.focus() + return + } + + // Pass the `paste` event to the textarea so it's handled by its event listener. + const event = new Event("paste") + event.clipboardData = e.clipboardData + te.dispatchEvent(event) }) // Prevent normal selection in the editor (we handle our own) From c2676685866c571a1c9c82cb25018cc08b4d42b2 Mon Sep 17 00:00:00 2001 From: Tom McLaughlin Date: Fri, 25 Jan 2019 06:33:50 -0800 Subject: [PATCH 1521/1790] [continuelist addon] Support nested modes --- addon/edit/continuelist.js | 10 ++++++++++ 1 file changed, 10 insertions(+) diff --git a/addon/edit/continuelist.js b/addon/edit/continuelist.js index 86a86d8a..65096fb5 100644 --- a/addon/edit/continuelist.js +++ b/addon/edit/continuelist.js @@ -20,7 +20,17 @@ var ranges = cm.listSelections(), replacements = []; for (var i = 0; i < ranges.length; i++) { var pos = ranges[i].head; + + // If we're not in Markdown mode, fall back to normal newlineAndIndent var eolState = cm.getStateAfter(pos.line); + var inner = cm.getMode().innerMode(eolState); + if (inner.mode.name !== "markdown") { + cm.execCommand("newlineAndIndent"); + return; + } else { + eolState = inner.state; + } + var inList = eolState.list !== false; var inQuote = eolState.quote !== 0; From 863d5e42f00d8d74542de072acfbbc218ef5127d Mon Sep 17 00:00:00 2001 From: Pablo Zubieta <8410335+pabloferz@users.noreply.github.com> Date: Thu, 31 Jan 2019 04:17:19 -0600 Subject: [PATCH 1522/1790] [julia mode] Fix some indentation issues --- mode/julia/julia.js | 89 ++++++++++++++++++++++----------------------- 1 file changed, 43 insertions(+), 46 deletions(-) diff --git a/mode/julia/julia.js b/mode/julia/julia.js index 435e9419..91e0ffc0 100644 --- a/mode/julia/julia.js +++ b/mode/julia/julia.js @@ -23,58 +23,50 @@ CodeMirror.defineMode("julia", function(config, parserConf) { var uChar = "([^\\u0027\\u005C\\uD800-\\uDFFF]|[\\uD800-\\uDFFF][\\uDC00-\\uDFFF])"; var operators = parserConf.operators || wordRegexp([ - "[<>]:", "[<>=]=", "<<=?", ">>>?=?", "=>", "->", "\\/\\/", - "[\\\\%*+\\-<>!=\\/^|&\\u00F7\\u22BB]=?", "\\?", "\\$", "~", ":", - "\\u00D7", "\\u2208", "\\u2209", "\\u220B", "\\u220C", "\\u2218", - "\\u221A", "\\u221B", "\\u2229", "\\u222A", "\\u2260", "\\u2264", - "\\u2265", "\\u2286", "\\u2288", "\\u228A", "\\u22C5", - "\\b(in|isa)\\b(?!\.?\\()"], ""); + "[<>]:", "[<>=]=", "<<=?", ">>>?=?", "=>", "->", "\\/\\/", + "[\\\\%*+\\-<>!=\\/^|&\\u00F7\\u22BB]=?", "\\?", "\\$", "~", ":", + "\\u00D7", "\\u2208", "\\u2209", "\\u220B", "\\u220C", "\\u2218", + "\\u221A", "\\u221B", "\\u2229", "\\u222A", "\\u2260", "\\u2264", + "\\u2265", "\\u2286", "\\u2288", "\\u228A", "\\u22C5", + "\\b(in|isa)\\b(?!\.?\\()"], ""); var delimiters = parserConf.delimiters || /^[;,()[\]{}]/; var identifiers = parserConf.identifiers || - /^[_A-Za-z\u00A1-\u2217\u2219-\uFFFF][\w\u00A1-\u2217\u2219-\uFFFF]*!*/; + /^[_A-Za-z\u00A1-\u2217\u2219-\uFFFF][\w\u00A1-\u2217\u2219-\uFFFF]*!*/; var chars = wordRegexp([octChar, hexChar, sChar, uChar], "'"); - var commonOpeners = ["begin", "function", "type", "struct", "immutable", - "let", "macro", "for", "while", "quote", "if", "else", "elseif", "try", - "finally", "catch", "do"]; + var openersList = ["begin", "function", "type", "struct", "immutable", "let", + "macro", "for", "while", "quote", "if", "else", "elseif", "try", + "finally", "catch", "do"]; - var commonClosers = ["end", "else", "elseif", "catch", "finally"]; + var closersList = ["end", "else", "elseif", "catch", "finally"]; - var commonKeywords = ["if", "else", "elseif", "while", "for", "begin", - "let", "end", "do", "try", "catch", "finally", "return", "break", - "continue", "global", "local", "const", "export", "import", "importall", - "using", "function", "where", "macro", "module", "baremodule", "struct", - "type", "mutable", "immutable", "quote", "typealias", "abstract", - "primitive", "bitstype"]; + var keywordsList = ["if", "else", "elseif", "while", "for", "begin", "let", + "end", "do", "try", "catch", "finally", "return", "break", "continue", + "global", "local", "const", "export", "import", "importall", "using", + "function", "where", "macro", "module", "baremodule", "struct", "type", + "mutable", "immutable", "quote", "typealias", "abstract", "primitive", + "bitstype"]; - var commonBuiltins = ["true", "false", "nothing", "NaN", "Inf"]; + var builtinsList = ["true", "false", "nothing", "NaN", "Inf"]; - CodeMirror.registerHelper("hintWords", "julia", commonKeywords.concat(commonBuiltins)); + CodeMirror.registerHelper("hintWords", "julia", keywordsList.concat(builtinsList)); - var openers = wordRegexp(commonOpeners); - var closers = wordRegexp(commonClosers); - var keywords = wordRegexp(commonKeywords); - var builtins = wordRegexp(commonBuiltins); + var openers = wordRegexp(openersList); + var closers = wordRegexp(closersList); + var keywords = wordRegexp(keywordsList); + var builtins = wordRegexp(builtinsList); var macro = /^@[_A-Za-z][\w]*/; var symbol = /^:[_A-Za-z\u00A1-\uFFFF][\w\u00A1-\uFFFF]*!*/; var stringPrefixes = /^(`|([_A-Za-z\u00A1-\uFFFF]*"("")?))/; function inArray(state) { - return inGenerator(state, '[') + return (state.nestedArrays > 0); } - function inGenerator(state, bracket, depth) { - if (typeof(bracket) === "undefined") { bracket = '('; } - if (typeof(depth) === "undefined") { depth = 0; } - var scope = currentScope(state, depth); - if ((depth == 0 && scope === "if" && inGenerator(state, bracket, depth + 1)) || - (scope === "for" && inGenerator(state, bracket, depth + 1)) || - (scope === bracket)) { - return true; - } - return false; + function inGenerator(state) { + return (state.nestedGenerators > 0); } function currentScope(state, n) { @@ -126,16 +118,19 @@ CodeMirror.defineMode("julia", function(config, parserConf) { if (ch === '[') { state.scopes.push('['); + state.nestedArrays++; } if (ch === '(') { state.scopes.push('('); + state.nestedGenerators++; } if (inArray(state) && ch === ']') { if (currentScope(state) === "if") { state.scopes.pop(); } while (currentScope(state) === "for") { state.scopes.pop(); } state.scopes.pop(); + state.nestedArrays--; state.leavingExpr = true; } @@ -143,6 +138,7 @@ CodeMirror.defineMode("julia", function(config, parserConf) { if (currentScope(state) === "if") { state.scopes.pop(); } while (currentScope(state) === "for") { state.scopes.pop(); } state.scopes.pop(); + state.nestedGenerators--; state.leavingExpr = true; } @@ -156,14 +152,12 @@ CodeMirror.defineMode("julia", function(config, parserConf) { } var match; - if (match = stream.match(openers)) { + if (match = stream.match(openers, false)) { state.scopes.push(match[0]); - return "keyword"; } - if (stream.match(closers)) { + if (stream.match(closers, false)) { state.scopes.pop(); - return "keyword"; } // Handle type annotations @@ -307,13 +301,13 @@ CodeMirror.defineMode("julia", function(config, parserConf) { function tokenAnnotation(stream, state) { stream.match(/.*?(?=,|;|{|}|\(|\)|=|$|\s)/); if (stream.match(/^{/)) { - state.nestedLevels++; + state.nestedParameters++; } else if (stream.match(/^}/)) { - state.nestedLevels--; + state.nestedParameters--; } - if (state.nestedLevels > 0) { + if (state.nestedParameters > 0) { stream.match(/.*?(?={|})/) || stream.next(); - } else if (state.nestedLevels == 0) { + } else if (state.nestedParameters == 0) { state.tokenize = tokenBase; } return "builtin"; @@ -321,14 +315,14 @@ CodeMirror.defineMode("julia", function(config, parserConf) { function tokenComment(stream, state) { if (stream.match(/^#=/)) { - state.nestedLevels++; + state.nestedComments++; } if (!stream.match(/.*?(?=(#=|=#))/)) { stream.skipToEnd(); } if (stream.match(/^=#/)) { - state.nestedLevels--; - if (state.nestedLevels == 0) + state.nestedComments--; + if (state.nestedComments == 0) state.tokenize = tokenBase; } return "comment"; @@ -391,7 +385,10 @@ CodeMirror.defineMode("julia", function(config, parserConf) { lastToken: null, leavingExpr: false, isDefinition: false, - nestedLevels: 0, + nestedArrays: 0, + nestedComments: 0, + nestedGenerators: 0, + nestedParameters: 0, charsAdvanced: 0, firstParenPos: -1 }; From 9c8f813aaf19fa4191d59f8fa62c900f95d99133 Mon Sep 17 00:00:00 2001 From: Marijn Haverbeke Date: Mon, 4 Feb 2019 11:02:15 +0100 Subject: [PATCH 1523/1790] Prevent text-less paste from messing up the origin of the next text input Closes #5764 --- src/edit/CodeMirror.js | 2 +- src/input/TextareaInput.js | 6 +++--- src/input/input.js | 7 ++++--- 3 files changed, 8 insertions(+), 7 deletions(-) diff --git a/src/edit/CodeMirror.js b/src/edit/CodeMirror.js index 7e43d98f..2888f422 100644 --- a/src/edit/CodeMirror.js +++ b/src/edit/CodeMirror.js @@ -57,7 +57,7 @@ export function CodeMirror(place, options) { delayingBlurEvent: false, focused: false, suppressEdits: false, // used to disable editing during key handlers when in readOnly mode - pasteIncoming: false, cutIncoming: false, // help recognize paste/cut edits in input.poll + pasteIncoming: -1, cutIncoming: -1, // help recognize paste/cut edits in input.poll selectingText: false, draggingText: false, highlight: new Delayed(), // stores highlight worker timeout diff --git a/src/input/TextareaInput.js b/src/input/TextareaInput.js index 0e1c37d6..ab02230f 100644 --- a/src/input/TextareaInput.js +++ b/src/input/TextareaInput.js @@ -48,7 +48,7 @@ export default class TextareaInput { on(te, "paste", e => { if (signalDOMEvent(cm, e) || handlePaste(e, cm)) return - cm.state.pasteIncoming = true + cm.state.pasteIncoming = +new Date input.fastPoll() }) @@ -69,7 +69,7 @@ export default class TextareaInput { selectInput(te) } } - if (e.type == "cut") cm.state.cutIncoming = true + if (e.type == "cut") cm.state.cutIncoming = +new Date } on(te, "cut", prepareCopyCut) on(te, "copy", prepareCopyCut) @@ -77,7 +77,7 @@ export default class TextareaInput { on(display.scroller, "paste", e => { if (eventInWidget(display, e) || signalDOMEvent(cm, e)) return if (!te.dispatchEvent) { - cm.state.pasteIncoming = true + cm.state.pasteIncoming = +new Date input.focus() return } diff --git a/src/input/input.js b/src/input/input.js index 90833d19..344a2a7b 100644 --- a/src/input/input.js +++ b/src/input/input.js @@ -25,7 +25,8 @@ export function applyTextInput(cm, inserted, deleted, sel, origin) { cm.display.shift = false if (!sel) sel = doc.sel - let paste = cm.state.pasteIncoming || origin == "paste" + let recent = +new Date - 200 + let paste = origin == "paste" || cm.state.pasteIncoming > recent let textLines = splitLinesAuto(inserted), multiPaste = null // When pasting N lines into N selections, insert one line per selection if (paste && sel.ranges.length > 1) { @@ -54,7 +55,7 @@ export function applyTextInput(cm, inserted, deleted, sel, origin) { from = to = Pos(from.line, 0) } let changeEvent = {from: from, to: to, text: multiPaste ? multiPaste[i % multiPaste.length] : textLines, - origin: origin || (paste ? "paste" : cm.state.cutIncoming ? "cut" : "+input")} + origin: origin || (paste ? "paste" : cm.state.cutIncoming > recent ? "cut" : "+input")} makeChange(cm.doc, changeEvent) signalLater(cm, "inputRead", cm, changeEvent) } @@ -64,7 +65,7 @@ export function applyTextInput(cm, inserted, deleted, sel, origin) { ensureCursorVisible(cm) if (cm.curOp.updateInput < 2) cm.curOp.updateInput = updateInput cm.curOp.typing = true - cm.state.pasteIncoming = cm.state.cutIncoming = false + cm.state.pasteIncoming = cm.state.cutIncoming = -1 } export function handlePaste(e, cm) { From a0e1c6faef5ed565ce1f8c2fe6c3a958f0449263 Mon Sep 17 00:00:00 2001 From: Marijn Haverbeke Date: Tue, 5 Feb 2019 11:24:25 +0100 Subject: [PATCH 1524/1790] [javascript mode] Support TypeScript this parameter declarations Closes #5769 --- mode/javascript/javascript.js | 1 + 1 file changed, 1 insertion(+) diff --git a/mode/javascript/javascript.js b/mode/javascript/javascript.js index 60d595da..793d2fa5 100644 --- a/mode/javascript/javascript.js +++ b/mode/javascript/javascript.js @@ -724,6 +724,7 @@ CodeMirror.defineMode("javascript", function(config, parserConfig) { if (value == "@") cont(expression, funarg) if (type == "spread") return cont(funarg); if (isTS && isModifier(value)) { cx.marked = "keyword"; return cont(funarg); } + if (isTS && type == "this") return cont(maybetype, maybeAssign) return pass(pattern, maybetype, maybeAssign); } function classExpression(type, value) { From d101acdd4658af2cf5259561fcf495c66b9d3b19 Mon Sep 17 00:00:00 2001 From: Marijn Haverbeke Date: Wed, 6 Feb 2019 09:04:58 +0100 Subject: [PATCH 1525/1790] [javascript mode] Allow TS prefixed | or & operators Closes #5771 --- mode/javascript/javascript.js | 1 + 1 file changed, 1 insertion(+) diff --git a/mode/javascript/javascript.js b/mode/javascript/javascript.js index 793d2fa5..fd0fce44 100644 --- a/mode/javascript/javascript.js +++ b/mode/javascript/javascript.js @@ -599,6 +599,7 @@ CodeMirror.defineMode("javascript", function(config, parserConfig) { cx.marked = "type" return cont(afterType) } + if (value == "|" || value == "&") return cont(typeexpr) if (type == "string" || type == "number" || type == "atom") return cont(afterType); if (type == "[") return cont(pushlex("]"), commasep(typeexpr, "]", ","), poplex, afterType) if (type == "{") return cont(pushlex("}"), commasep(typeprop, "}", ",;"), poplex, afterType) From e80024ce48d11c5cab4c3faed360a673abe2c0d7 Mon Sep 17 00:00:00 2001 From: Marijn Haverbeke Date: Thu, 7 Feb 2019 09:35:50 +0100 Subject: [PATCH 1526/1790] [javascript mode] Simplify parsing of for loop specs Closes #5774 --- mode/javascript/javascript.js | 23 ++++++++--------------- 1 file changed, 8 insertions(+), 15 deletions(-) diff --git a/mode/javascript/javascript.js b/mode/javascript/javascript.js index fd0fce44..0f836647 100644 --- a/mode/javascript/javascript.js +++ b/mode/javascript/javascript.js @@ -681,25 +681,18 @@ CodeMirror.defineMode("javascript", function(config, parserConfig) { } function forspec(type, value) { if (value == "await") return cont(forspec); - if (type == "(") return cont(pushlex(")"), forspec1, expect(")"), poplex); + if (type == "(") return cont(pushlex(")"), forspec1, poplex); } function forspec1(type) { - if (type == "var") return cont(vardef, expect(";"), forspec2); - if (type == ";") return cont(forspec2); - if (type == "variable") return cont(formaybeinof); - return pass(expression, expect(";"), forspec2); - } - function formaybeinof(_type, value) { - if (value == "in" || value == "of") { cx.marked = "keyword"; return cont(expression); } - return cont(maybeoperatorComma, forspec2); + if (type == "var") return cont(vardef, forspec2); + if (type == "variable") return cont(forspec2); + return pass(forspec2) } function forspec2(type, value) { - if (type == ";") return cont(forspec3); - if (value == "in" || value == "of") { cx.marked = "keyword"; return cont(expression); } - return pass(expression, expect(";"), forspec3); - } - function forspec3(type) { - if (type != ")") cont(expression); + if (type == ")") return cont() + if (type == ";") return cont(forspec2) + if (value == "in" || value == "of") { cx.marked = "keyword"; return cont(expression, forspec2) } + return pass(expression, forspec2) } function functiondef(type, value) { if (value == "*") {cx.marked = "keyword"; return cont(functiondef);} From 7a1bff4814d78659dcda4f71329781eadfcaa527 Mon Sep 17 00:00:00 2001 From: Joy Zhong Date: Tue, 12 Feb 2019 00:58:00 -0800 Subject: [PATCH 1527/1790] [soy mode] Update @prop to @state The name change was made in Soy recently. --- mode/soy/soy.js | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/mode/soy/soy.js b/mode/soy/soy.js index faf4314b..b2cd8cb7 100644 --- a/mode/soy/soy.js +++ b/mode/soy/soy.js @@ -328,7 +328,7 @@ if (!state.scopes) { state.variables = prepend(null, 'ij'); } - } else if (state.tag.match(/^@(?:param\??|inject|prop)/)) { + } else if (state.tag.match(/^@(?:param\??|inject|state)/)) { state.soyState.push("param-def"); } else if (state.tag.match(/^(?:param)/)) { state.soyState.push("param-ref"); From c6212288adbfedc2092528a4c977994de2a7c2b6 Mon Sep 17 00:00:00 2001 From: Joy Zhong Date: Mon, 11 Feb 2019 13:16:01 -0800 Subject: [PATCH 1528/1790] [soy mode] Add {element} as valid indenting tag. --- mode/soy/soy.js | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/mode/soy/soy.js b/mode/soy/soy.js index b2cd8cb7..59105cee 100644 --- a/mode/soy/soy.js +++ b/mode/soy/soy.js @@ -13,7 +13,7 @@ var indentingTags = ["template", "literal", "msg", "fallbackmsg", "let", "if", "elseif", "else", "switch", "case", "default", "foreach", "ifempty", "for", - "call", "param", "deltemplate", "delcall", "log"]; + "call", "param", "deltemplate", "delcall", "log", "element"]; CodeMirror.defineMode("soy", function(config) { var textMode = CodeMirror.getMode(config, "text/plain"); From 72d514957fd6a01644f649157eeab9d8c2cbcb4e Mon Sep 17 00:00:00 2001 From: Stryder Crown Date: Thu, 14 Feb 2019 02:24:42 -0500 Subject: [PATCH 1529/1790] [lesser-dark and vibrant-ink themes] Improve contrast --- theme/lesser-dark.css | 2 +- theme/vibrant-ink.css | 2 +- 2 files changed, 2 insertions(+), 2 deletions(-) diff --git a/theme/lesser-dark.css b/theme/lesser-dark.css index 4b347db0..f96bf430 100644 --- a/theme/lesser-dark.css +++ b/theme/lesser-dark.css @@ -40,7 +40,7 @@ Ported to CodeMirror by Peter Kroon .cm-s-lesser-dark span.cm-tag { color: #669199; } .cm-s-lesser-dark span.cm-attribute { color: #81a4d5; } .cm-s-lesser-dark span.cm-hr { color: #999; } -.cm-s-lesser-dark span.cm-link { color: #00c; } +.cm-s-lesser-dark span.cm-link { color: #7070E6; } .cm-s-lesser-dark span.cm-error { color: #9d1e15; } .cm-s-lesser-dark .CodeMirror-activeline-background { background: #3C3A3A; } diff --git a/theme/vibrant-ink.css b/theme/vibrant-ink.css index b13ecf21..6358ad36 100644 --- a/theme/vibrant-ink.css +++ b/theme/vibrant-ink.css @@ -27,7 +27,7 @@ .cm-s-vibrant-ink .cm-attribute { color: #8DA6CE; } .cm-s-vibrant-ink .cm-header { color: #FF6400; } .cm-s-vibrant-ink .cm-hr { color: #AEAEAE; } -.cm-s-vibrant-ink .cm-link { color: blue; } +.cm-s-vibrant-ink .cm-link { color: #5656F3; } .cm-s-vibrant-ink .cm-error { border-bottom: 1px solid red; } .cm-s-vibrant-ink .CodeMirror-activeline-background { background: #27282E; } From 1f82eed9cb135848f4815852d99939ab10d49a76 Mon Sep 17 00:00:00 2001 From: Kenan Christian Dimas Date: Mon, 18 Feb 2019 15:34:18 +0700 Subject: [PATCH 1530/1790] [nord theme] Add --- demo/theme.html | 2 ++ theme/nord.css | 42 ++++++++++++++++++++++++++++++++++++++++++ 2 files changed, 44 insertions(+) create mode 100644 theme/nord.css diff --git a/demo/theme.html b/demo/theme.html index 5ecace91..8cf5c079 100644 --- a/demo/theme.html +++ b/demo/theme.html @@ -36,6 +36,7 @@ + @@ -129,6 +130,7 @@

      Theme Demo

      + diff --git a/theme/nord.css b/theme/nord.css new file mode 100644 index 00000000..41a8ad77 --- /dev/null +++ b/theme/nord.css @@ -0,0 +1,42 @@ +/* Based on arcticicestudio's Nord theme */ +/* https://github.com/arcticicestudio/nord */ + +.cm-s-nord.CodeMirror { background: #2e3440; color: #d8dee9; } +.cm-s-nord div.CodeMirror-selected { background: #434c5e; } +.cm-s-nord .CodeMirror-line::selection, .cm-s-nord .CodeMirror-line > span::selection, .cm-s-nord .CodeMirror-line > span > span::selection { background: #3b4252; } +.cm-s-nord .CodeMirror-line::-moz-selection, .cm-s-nord .CodeMirror-line > span::-moz-selection, .cm-s-nord .CodeMirror-line > span > span::-moz-selection { background: #3b4252; } +.cm-s-nord .CodeMirror-gutters { background: #2e3440; border-right: 0px; } +.cm-s-nord .CodeMirror-guttermarker { color: #4c566a; } +.cm-s-nord .CodeMirror-guttermarker-subtle { color: #4c566a; } +.cm-s-nord .CodeMirror-linenumber { color: #4c566a; } +.cm-s-nord .CodeMirror-cursor { border-left: 1px solid #f8f8f0; } + +.cm-s-nord span.cm-comment { color: #4c566a; } +.cm-s-nord span.cm-atom { color: #b48ead; } +.cm-s-nord span.cm-number { color: #b48ead; } + +.cm-s-nord span.cm-comment.cm-attribute { color: #97b757; } +.cm-s-nord span.cm-comment.cm-def { color: #bc9262; } +.cm-s-nord span.cm-comment.cm-tag { color: #bc6283; } +.cm-s-nord span.cm-comment.cm-type { color: #5998a6; } + +.cm-s-nord span.cm-property, .cm-s-nord span.cm-attribute { color: #8FBCBB; } +.cm-s-nord span.cm-keyword { color: #81A1C1; } +.cm-s-nord span.cm-builtin { color: #81A1C1; } +.cm-s-nord span.cm-string { color: #A3BE8C; } + +.cm-s-nord span.cm-variable { color: #d8dee9; } +.cm-s-nord span.cm-variable-2 { color: #d8dee9; } +.cm-s-nord span.cm-variable-3, .cm-s-nord span.cm-type { color: #d8dee9; } +.cm-s-nord span.cm-def { color: #8FBCBB; } +.cm-s-nord span.cm-bracket { color: #81A1C1; } +.cm-s-nord span.cm-tag { color: #bf616a; } +.cm-s-nord span.cm-header { color: #b48ead; } +.cm-s-nord span.cm-link { color: #b48ead; } +.cm-s-nord span.cm-error { background: #bf616a; color: #f8f8f0; } + +.cm-s-nord .CodeMirror-activeline-background { background: #3b4252; } +.cm-s-nord .CodeMirror-matchingbracket { + text-decoration: underline; + color: white !important; +} From 39b67d6b7511c4de75168ecb25e03a77788ad667 Mon Sep 17 00:00:00 2001 From: Robert Martin Date: Mon, 18 Feb 2019 03:35:13 -0500 Subject: [PATCH 1531/1790] [vim bindings] Add proper emulation for forward-delete In Vim's command mode, forward-delete (Delete on PC or fn-delete on Mac) should behave the same as 'x': delete the character under the cursor. Due to a bug in CodeMirror's emulation, this wasn't happening before. This change adds the proper support, along with tests for forward-delete. --- keymap/vim.js | 1 + test/vim_test.js | 25 +++++++++++++++++++++++++ 2 files changed, 26 insertions(+) diff --git a/keymap/vim.js b/keymap/vim.js index 0af9bcb2..eb9cd17b 100644 --- a/keymap/vim.js +++ b/keymap/vim.js @@ -53,6 +53,7 @@ { keys: '', type: 'keyToKey', toKeys: 'j' }, { keys: '', type: 'keyToKey', toKeys: 'l' }, { keys: '', type: 'keyToKey', toKeys: 'h', context: 'normal'}, + { keys: '', type: 'keyToKey', toKeys: 'x', context: 'normal'}, { keys: '', type: 'keyToKey', toKeys: 'W' }, { keys: '', type: 'keyToKey', toKeys: 'B', context: 'normal' }, { keys: '', type: 'keyToKey', toKeys: 'w' }, diff --git a/test/vim_test.js b/test/vim_test.js index e3f95ccd..f38efd02 100644 --- a/test/vim_test.js +++ b/test/vim_test.js @@ -1523,6 +1523,31 @@ testVim('i_overwrite_backspace', function(cm, vim, helpers) { helpers.assertCursorAt(Pos(0, 9, "after")); eq('0123456789', cm.getValue()); }, { value: '0123456789'}); +testVim('i_forward_delete', function(cm, vim, helpers) { + cm.setCursor(0, 3); + helpers.doKeys('i'); + helpers.doInsertModeKeys('Delete'); + helpers.assertCursorAt(0, 3); + eq('A124\nBCD', cm.getValue()); + helpers.doInsertModeKeys('Delete'); + helpers.assertCursorAt(0, 3); + eq('A12\nBCD', cm.getValue()); + helpers.doInsertModeKeys('Delete'); + helpers.assertCursorAt(0, 3); + eq('A12BCD', cm.getValue()); +}, { value: 'A1234\nBCD'}); +testVim('forward_delete', function(cm, vim, helpers) { + cm.setCursor(0, 3); + helpers.doInsertModeKeys('Delete'); + helpers.assertCursorAt(0, 3); + eq('A124\nBCD', cm.getValue()); + helpers.doInsertModeKeys('Delete'); + helpers.assertCursorAt(0, 2); + eq('A12\nBCD', cm.getValue()); + helpers.doInsertModeKeys('Delete'); + helpers.assertCursorAt(0, 1); + eq('A1\nBCD', cm.getValue()); +}, { value: 'A1234\nBCD'}); testVim('A', function(cm, vim, helpers) { helpers.doKeys('A'); helpers.assertCursorAt(0, lines[0].length); From 28fe151c63cf18d4be6a44add3685b7f72714bb0 Mon Sep 17 00:00:00 2001 From: Marijn Haverbeke Date: Tue, 19 Feb 2019 13:34:56 +0100 Subject: [PATCH 1532/1790] Remove kludge in line height measuring Which was introduced in 6ac8c130e, for unclear reasons Issue #5755 --- src/display/update_lines.js | 1 - 1 file changed, 1 deletion(-) diff --git a/src/display/update_lines.js b/src/display/update_lines.js index 475432b7..592eb30b 100644 --- a/src/display/update_lines.js +++ b/src/display/update_lines.js @@ -25,7 +25,6 @@ export function updateHeightsInViewport(cm) { width = cur.text.firstChild.getBoundingClientRect().right - box.left - 1 } let diff = cur.line.height - height - if (height < 2) height = textHeight(display) if (diff > .005 || diff < -.005) { updateLineHeight(cur.line, height) updateWidgetHeight(cur.line) From 627e8bb5539d2858a827337254d422f6a57043a0 Mon Sep 17 00:00:00 2001 From: Marijn Haverbeke Date: Tue, 19 Feb 2019 13:55:42 +0100 Subject: [PATCH 1533/1790] Fix lint error --- src/display/update_lines.js | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/display/update_lines.js b/src/display/update_lines.js index 592eb30b..60c367e4 100644 --- a/src/display/update_lines.js +++ b/src/display/update_lines.js @@ -1,6 +1,6 @@ import { heightAtLine } from "../line/spans.js" import { getLine, lineAtHeight, updateLineHeight } from "../line/utils_line.js" -import { paddingTop, textHeight, charWidth } from "../measurement/position_measurement.js" +import { paddingTop, charWidth } from "../measurement/position_measurement.js" import { ie, ie_version } from "../util/browser.js" // Read the actual heights of the rendered lines, and update their From 76f5b0378b8c8ae665ec7224ae4d5045fe5ce471 Mon Sep 17 00:00:00 2001 From: Jan Keromnes Date: Tue, 19 Feb 2019 17:58:33 +0000 Subject: [PATCH 1534/1790] Typo: s/npm build/npm run build/g --- CHANGELOG.md | 2 +- doc/releases.html | 2 +- 2 files changed, 2 insertions(+), 2 deletions(-) diff --git a/CHANGELOG.md b/CHANGELOG.md index e07d5c74..aa11fd2c 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -646,7 +646,7 @@ Line endings for pasted content are now normalized to the editor's [preferred en ### New features -The core is now maintained as a number of small files, using ES6 syntax and modules, under the `src/` directory. A git checkout no longer contains a working `codemirror.js` until you `npm build` (but when installing from NPM, it is included). +The core is now maintained as a number of small files, using ES6 syntax and modules, under the `src/` directory. A git checkout no longer contains a working `codemirror.js` until you `npm run build` (but when installing from NPM, it is included). The [`refresh`](https://codemirror.net/doc/manual.html#event_refresh) event is now documented and stable. diff --git a/doc/releases.html b/doc/releases.html index ba0332ab..ebf8c06a 100644 --- a/doc/releases.html +++ b/doc/releases.html @@ -412,7 +412,7 @@

      Version 5.x

    • javascript mode: Improve support for class expressions. Support TypeScript optional class properties, the abstract keyword, and return type declarations for arrow functions.
    • css mode: Fix highlighting of mixed-case keywords.
    • closebrackets addon: Improve behavior when typing a quote before a string.
    • -
    • The core is now maintained as a number of small files, using ES6 syntax and modules, under the src/ directory. A git checkout no longer contains a working codemirror.js until you npm build (but when installing from NPM, it is included).
    • +
    • The core is now maintained as a number of small files, using ES6 syntax and modules, under the src/ directory. A git checkout no longer contains a working codemirror.js until you npm run build (but when installing from NPM, it is included).
    • The refresh event is now documented and stable.
    From ffb96cfa7202c5d55161108bb4fde2f83a33fd85 Mon Sep 17 00:00:00 2001 From: Marijn Haverbeke Date: Tue, 19 Feb 2019 21:36:44 +0100 Subject: [PATCH 1535/1790] [show-hint addon] Move variable into module scope --- 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 f86f9540..e3cd209d 100644 --- a/addon/hint/show-hint.js +++ b/addon/hint/show-hint.js @@ -1,8 +1,6 @@ // CodeMirror, copyright (c) by Marijn Haverbeke and others // Distributed under an MIT license: https://codemirror.net/LICENSE -var mac = /Mac/.test(navigator.platform); - (function(mod) { if (typeof exports == "object" && typeof module == "object") // CommonJS mod(require("../../lib/codemirror")); @@ -170,6 +168,8 @@ var mac = /Mac/.test(navigator.platform); Esc: handle.close }; + var mac = /Mac/.test(navigator.platform); + if (mac) { baseMap["Ctrl-P"] = function() {handle.moveFocus(-1);}; baseMap["Ctrl-N"] = function() {handle.moveFocus(1);}; From 6454f583167a44016ee452b1787466f442bd9122 Mon Sep 17 00:00:00 2001 From: Marijn Haverbeke Date: Thu, 21 Feb 2019 14:03:30 +0100 Subject: [PATCH 1536/1790] Mark version 5.44.0 --- AUTHORS | 6 ++++++ CHANGELOG.md | 20 ++++++++++++++++++++ doc/manual.html | 2 +- doc/releases.html | 12 ++++++++++++ index.html | 2 +- package.json | 2 +- src/edit/main.js | 2 +- 7 files changed, 42 insertions(+), 4 deletions(-) diff --git a/AUTHORS b/AUTHORS index 55bff7ae..573a1e5f 100644 --- a/AUTHORS +++ b/AUTHORS @@ -134,6 +134,7 @@ Chad Jolly Chandra Sekhar Pydi Charles Skelton Cheah Chu Yeow +Chhekur Chris Colborne Chris Coyier Chris Ford @@ -382,6 +383,7 @@ Josh Soref Joshua Newman Josh Watzman jots +Joy Zhong jsoojeon ju1ius Juan Benavides Romero @@ -397,6 +399,7 @@ karevn Karol Kayur Patel Kazuhito Hokamura +Kenan Christian Dimas Ken Newman ken restivo Ken Rockot @@ -556,6 +559,7 @@ Nicholas Bollweg (Nick) NickKolok Nick Kreeger Nick Small +Nicolas Chevobbe Nicolas Kick Nicolò Ribaudo Niels van Groningen @@ -635,6 +639,7 @@ Richard Z.H. Wang Rishi Goomar Robert Brignull Robert Crossfield +Robert Martin Roberto Abdelkader Martínez Pérez robertop23 Robert Plummer @@ -702,6 +707,7 @@ Steve Champagne Steve Hoover Steve O'Hara stoskov +Stryder Crown Stu Kennedy Sungho Kim sverweij diff --git a/CHANGELOG.md b/CHANGELOG.md index aa11fd2c..08d6bb34 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -1,3 +1,23 @@ +## 5.44.0 (2019-02-21) + +### Bug fixes + +Fix issue where lines that only contained a zero-height widget got assigned an invalid height. + +Improve support for middle-click paste on X Windows. + +Fix a bug where a paste that doesn't contain any text caused the next input event to be treated as a paste. + +[show-hint addon](https://codemirror.net/doc/manual.html#addon_show-hint): Fix accidental global variable. + +[javascript mode](https://codemirror.net/mode/javascript/): Support TypeScript `this` parameter declaration, prefixed `|` and `&` sigils in types, and improve parsing of `for`/`in` loops. + +### New features + +[vim bindings](https://codemirror.net/demo/vim.html): Properly emulate forward-delete. + +New theme: [nord](https://codemirror.net/demo/theme.html#nord). + ## 5.43.0 (2019-01-21) ### Bug fixes diff --git a/doc/manual.html b/doc/manual.html index 784b4081..f676be8c 100644 --- a/doc/manual.html +++ b/doc/manual.html @@ -69,7 +69,7 @@

    User manual and reference guide - version 5.43.1 + version 5.44.0

    CodeMirror is a code-editor component that can be embedded in diff --git a/doc/releases.html b/doc/releases.html index ebf8c06a..b907c102 100644 --- a/doc/releases.html +++ b/doc/releases.html @@ -30,6 +30,18 @@

    Release notes and version history

    Version 5.x

    +

    21-02-2019: Version 5.44.0:

    + +
      +
    • vim bindings: Properly emulate forward-delete.
    • +
    • New theme: nord.
    • +
    • Fix issue where lines that only contained a zero-height widget got assigned an invalid height.
    • +
    • Improve support for middle-click paste on X Windows.
    • +
    • Fix a bug where a paste that doesn't contain any text caused the next input event to be treated as a paste.
    • +
    • show-hint addon: Fix accidental global variable.
    • +
    • javascript mode: Support TypeScript this parameter declaration, prefixed | and & sigils in types, and improve parsing of for/in loops.
    • +
    +

    21-01-2019: Version 5.43.0:

      diff --git a/index.html b/index.html index a63d4d32..d94f5d53 100644 --- a/index.html +++ b/index.html @@ -99,7 +99,7 @@

      This is CodeMirror

      - Get the current version: 5.43.0.
      + Get the current version: 5.44.0.
      You can see the code,
      read the release notes,
      or study the user manual. diff --git a/package.json b/package.json index c5cb8dce..1c78f014 100644 --- a/package.json +++ b/package.json @@ -1,6 +1,6 @@ { "name": "codemirror", - "version": "5.43.1", + "version": "5.44.0", "main": "lib/codemirror.js", "style": "lib/codemirror.css", "author": { diff --git a/src/edit/main.js b/src/edit/main.js index d8ea285f..81b954f3 100644 --- a/src/edit/main.js +++ b/src/edit/main.js @@ -66,4 +66,4 @@ import { addLegacyProps } from "./legacy.js" addLegacyProps(CodeMirror) -CodeMirror.version = "5.43.1" +CodeMirror.version = "5.44.0" From 2a261dfa5225534954c3f0f43575db4f82c7dd73 Mon Sep 17 00:00:00 2001 From: Marijn Haverbeke Date: Thu, 21 Feb 2019 14:08:17 +0100 Subject: [PATCH 1537/1790] Bump version number post-5.44.0 --- doc/manual.html | 2 +- package.json | 2 +- src/edit/main.js | 2 +- 3 files changed, 3 insertions(+), 3 deletions(-) diff --git a/doc/manual.html b/doc/manual.html index f676be8c..7376debe 100644 --- a/doc/manual.html +++ b/doc/manual.html @@ -69,7 +69,7 @@

      User manual and reference guide - version 5.44.0 + version 5.44.1

      CodeMirror is a code-editor component that can be embedded in diff --git a/package.json b/package.json index 1c78f014..23130cbb 100644 --- a/package.json +++ b/package.json @@ -1,6 +1,6 @@ { "name": "codemirror", - "version": "5.44.0", + "version": "5.44.1", "main": "lib/codemirror.js", "style": "lib/codemirror.css", "author": { diff --git a/src/edit/main.js b/src/edit/main.js index 81b954f3..349803a5 100644 --- a/src/edit/main.js +++ b/src/edit/main.js @@ -66,4 +66,4 @@ import { addLegacyProps } from "./legacy.js" addLegacyProps(CodeMirror) -CodeMirror.version = "5.44.0" +CodeMirror.version = "5.44.1" From 278503bdb57b874067b71dfd55e9c68e0e5ff44b Mon Sep 17 00:00:00 2001 From: Marijn Haverbeke Date: Wed, 27 Feb 2019 09:32:15 +0100 Subject: [PATCH 1538/1790] [swift mode] Drop all-rights-reserved example code --- mode/swift/index.html | 69 +++++++++++++------------------------------ 1 file changed, 20 insertions(+), 49 deletions(-) diff --git a/mode/swift/index.html b/mode/swift/index.html index 53d41343..ce2a3a02 100644 --- a/mode/swift/index.html +++ b/mode/swift/index.html @@ -28,61 +28,32 @@

      Swift mode

      @@ -151,6 +152,7 @@

      Theme Demo

      +

      diff --git a/theme/yonce.css b/theme/yonce.css new file mode 100644 index 00000000..b74bf53e --- /dev/null +++ b/theme/yonce.css @@ -0,0 +1,44 @@ +/* + + Name: yoncé + Author: Thomas MacLean (http://github.com/thomasmaclean) + + Original yoncé color scheme by Mina Markham (https://github.com/minamarkham) + +*/ + +.cm-s-yonce.CodeMirror { background: #1C1C1C; color: #d4d4d4; } /**/ +.cm-s-yonce div.CodeMirror-selected { background: rgba(252, 69, 133, 0.478); } /**/ +.cm-s-yonce .CodeMirror-line::selection, .cm-s-yonce .CodeMirror-line > span::selection, .cm-s-yonce .CodeMirror-line > span > span::selection { background: rgba(252, 67, 132, 0.47); } +.cm-s-yonce .CodeMirror-line::-moz-selection, .cm-s-yonce .CodeMirror-line > span::-moz-selection, .cm-s-yonce .CodeMirror-line > span > span::-moz-selection { background: rgba(252, 67, 132, 0.47); } + +.cm-s-yonce.CodeMirror pre { padding-left: 15px; } +.cm-s-yonce .CodeMirror-gutters {background: #1C1C1C; border-right: 0px;} +.cm-s-yonce .CodeMirror-linenumber {color: #777777; padding-right: 10px; } +.cm-s-yonce .CodeMirror-activeline .CodeMirror-linenumber.CodeMirror-gutter-elt { background: #1C1C1C; color: #fc4384; } +.cm-s-yonce .CodeMirror-linenumber { color: #777; } +.cm-s-yonce .CodeMirror-cursor { border-left: 1px solid #FC4384; } + +.cm-s-yonce .cm-keyword { color: #00A7AA; } /**/ +.cm-s-yonce .cm-atom { color: #F39B35; } +.cm-s-yonce .cm-number, .cm-s-yonce span.cm-type { color: #A06FCA; } /**/ +.cm-s-yonce .cm-def { color: #D4D4D4; font-style: italic; } +.cm-s-yonce .cm-property { color: #FC4384; } +.cm-s-yonce span.cm-variable-2 { color: #da7dae; font-style: italic; } +.cm-s-yonce span.cm-variable-3 { color: #FC4384; } /**/ +.cm-s-yonce span.cm-tag { color: #D4D4D4; font-style: italic; } /**/ +.cm-s-yonce .cm-operator { color: #98E342; } /**/ +.cm-s-yonce .cm-comment { color:#696d70; font-style:italic; font-weight:normal; } /**/ +.cm-s-yonce .cm-string { color:#E6DB74; font-style:italic; } /**/ +.cm-s-yonce .cm-string-2 { color:#F39B35; } /*?*/ +.cm-s-yonce .cm-meta { background-color:#141414; color:#D4D4D4; } /*?*/ +.cm-s-yonce .cm-builtin { color: #FC4384; } /*?*/ +.cm-s-yonce .cm-tag { color: #00A7AA; } /**/ +.cm-s-yonce .cm-attribute { color: #A06FCA; } /*?*/ +.cm-s-yonce .cm-header { color: #da7dae; } +.cm-s-yonce .cm-hr { color: #98E342; } +.cm-s-yonce .cm-link { color:#696d70; font-style:italic; text-decoration:none; } /**/ +.cm-s-yonce .cm-error { border-bottom: 1px solid #C42412; } + +.cm-s-yonce .CodeMirror-activeline-background { background: #272727; } +.cm-s-yonce .CodeMirror-matchingbracket { outline:1px solid grey; color:#D4D4D4 !important; } From da99fdf037530302e089a7ae673a5c3cc31bad10 Mon Sep 17 00:00:00 2001 From: William Desportes Date: Wed, 20 Mar 2019 09:20:19 +0100 Subject: [PATCH 1548/1790] [sql-hint addon] Add tests and fix #5817 --- addon/hint/sql-hint.js | 41 ++++++++++++++++------------ test/sql-hint-test.js | 62 +++++++++++++++++++++++++++++++++++++++++- 2 files changed, 84 insertions(+), 19 deletions(-) diff --git a/addon/hint/sql-hint.js b/addon/hint/sql-hint.js index 9759b91e..444eba8b 100644 --- a/addon/hint/sql-hint.js +++ b/addon/hint/sql-hint.js @@ -275,24 +275,29 @@ if (search.charAt(0) == "." || search.charAt(0) == identifierQuote) { start = nameCompletion(cur, token, result, editor); } else { - addMatches(result, search, defaultTable, function(w) {return {text:w, className: "CodeMirror-hint-table CodeMirror-hint-default-table"};}); - addMatches( - result, - search, - tables, - function(w) { - if (typeof w === 'object') { - w.className = "CodeMirror-hint-table"; - } else { - w = {text: w, className: "CodeMirror-hint-table"}; - } - - return w; - } - ); - if (!disableKeywords) - addMatches(result, search, keywords, function(w) {return {text: w.toUpperCase(), className: "CodeMirror-hint-keyword"};}); - } + var objectOrClass = function(w, className) { + if (typeof w === "object") { + w.className = className; + } else { + w = { text: w, className: className }; + } + return w; + }; + addMatches(result, search, defaultTable, function(w) { + return objectOrClass(w, "CodeMirror-hint-table CodeMirror-hint-default-table"); + }); + addMatches( + result, + search, + tables, function(w) { + return objectOrClass(w, "CodeMirror-hint-table"); + } + ); + if (!disableKeywords) + addMatches(result, search, keywords, function(w) { + return objectOrClass(w.toUpperCase(), "CodeMirror-hint-keyword"); + }); + } return {list: result, from: Pos(cur.line, start), to: Pos(cur.line, end)}; }); diff --git a/test/sql-hint-test.js b/test/sql-hint-test.js index 3ed8d121..d1ea7f1f 100644 --- a/test/sql-hint-test.js +++ b/test/sql-hint-test.js @@ -21,13 +21,44 @@ {text: "name", displayText: "name | The name"}] }]; + var displayTextTablesWithDefault = [ + { + text: "Api__TokenAliases", + columns: [ + { + text: "token", + displayText: "token | varchar(255) | Primary", + columnName: "token", + columnHint: "varchar(255) | Primary" + }, + { + text: "alias", + displayText: "alias | varchar(255) | Primary", + columnName: "alias", + columnHint: "varchar(255) | Primary" + } + ] + }, + { + text: "mytable", + columns: [ + { text: "id", displayText: "id | Unique ID" }, + { text: "name", displayText: "name | The name" } + ] + } + ]; + namespace = "sql-hint_"; function test(name, spec) { testCM(name, function(cm) { cm.setValue(spec.value); cm.setCursor(spec.cursor); - var completion = CodeMirror.hint.sql(cm, {tables: spec.tables}); + var completion = CodeMirror.hint.sql(cm, { + tables: spec.tables, + defaultTable: spec.defaultTable, + disableKeywords: spec.disableKeywords + }); if (!deepCompare(completion.list, spec.list)) throw new Failure("Wrong completion results " + JSON.stringify(completion.list) + " vs " + JSON.stringify(spec.list)); eqCharPos(completion.from, spec.from); @@ -46,6 +77,15 @@ to: Pos(0, 3) }); + test("keywords_disabled", { + value: "SEL", + cursor: Pos(0, 3), + disableKeywords: true, + list: [], + from: Pos(0, 0), + to: Pos(0, 3) + }); + test("from", { value: "SELECT * fr", cursor: Pos(0, 11), @@ -185,6 +225,26 @@ mode: "text/x-sqlite" }); + test("displayText_default_table", { + value: "SELECT a", + cursor: Pos(0, 8), + disableKeywords: true, + defaultTable: "Api__TokenAliases", + tables: displayTextTablesWithDefault, + list: [ + { + text: "alias", + displayText: "alias | varchar(255) | Primary", + columnName: "alias", + columnHint: "varchar(255) | Primary", + className: "CodeMirror-hint-table CodeMirror-hint-default-table" + }, + { text: "Api__TokenAliases", className: "CodeMirror-hint-table" } + ], + from: Pos(0, 7), + to: Pos(0, 8) + }); + test("displayText_table", { value: "SELECT myt", cursor: Pos(0, 10), From 9d489ac94adce0f28f61b83320619dfd1f3ec446 Mon Sep 17 00:00:00 2001 From: Leo Baschy Date: Wed, 20 Mar 2019 01:24:46 -0700 Subject: [PATCH 1549/1790] [xml-hint addon] Optionally match in middle For example, "happy" to match "very-happy", "not-so-happy", "happy". --- addon/hint/xml-hint.js | 16 +++++++++++----- doc/manual.html | 9 +++++++-- 2 files changed, 18 insertions(+), 7 deletions(-) diff --git a/addon/hint/xml-hint.js b/addon/hint/xml-hint.js index e575fc43..69914fa2 100644 --- a/addon/hint/xml-hint.js +++ b/addon/hint/xml-hint.js @@ -13,9 +13,15 @@ var Pos = CodeMirror.Pos; + function matches(hint, typed, matchInMiddle) { + if (matchInMiddle) return hint.indexOf(typed) >= 0; + else return hint.lastIndexOf(typed, 0) == 0; + } + function getHints(cm, options) { var tags = options && options.schemaInfo; var quote = (options && options.quoteChar) || '"'; + var matchInMiddle = options && options.matchInMiddle; if (!tags) return; var cur = cm.getCursor(), token = cm.getTokenAt(cur); if (token.end > cur.ch) { @@ -45,14 +51,14 @@ var cx = inner.state.context, curTag = cx && tags[cx.tagName]; var childList = cx ? curTag && curTag.children : tags["!top"]; if (childList && tagType != "close") { - for (var i = 0; i < childList.length; ++i) if (!prefix || childList[i].lastIndexOf(prefix, 0) == 0) + for (var i = 0; i < childList.length; ++i) if (!prefix || matches(childList[i], prefix, matchInMiddle)) result.push("<" + childList[i]); } else if (tagType != "close") { for (var name in tags) - if (tags.hasOwnProperty(name) && name != "!top" && name != "!attrs" && (!prefix || name.lastIndexOf(prefix, 0) == 0)) + if (tags.hasOwnProperty(name) && name != "!top" && name != "!attrs" && (!prefix || matches(name, prefix, matchInMiddle))) result.push("<" + name); } - if (cx && (!prefix || tagType == "close" && cx.tagName.lastIndexOf(prefix, 0) == 0)) + if (cx && (!prefix || tagType == "close" && matches(cx.tagName, prefix, matchInMiddle))) result.push(""); } else { // Attribute completion @@ -88,14 +94,14 @@ } replaceToken = true; } - for (var i = 0; i < atValues.length; ++i) if (!prefix || atValues[i].lastIndexOf(prefix, 0) == 0) + for (var i = 0; i < atValues.length; ++i) if (!prefix || matches(atValues[i], prefix, matchInMiddle)) result.push(quote + atValues[i] + quote); } else { // An attribute name if (token.type == "attribute") { prefix = token.string; replaceToken = true; } - for (var attr in attrs) if (attrs.hasOwnProperty(attr) && (!prefix || attr.lastIndexOf(prefix, 0) == 0)) + for (var attr in attrs) if (attrs.hasOwnProperty(attr) && (!prefix || matches(attr, prefix, matchInMiddle))) result.push(attr); } } diff --git a/doc/manual.html b/doc/manual.html index 7376debe..934e7476 100644 --- a/doc/manual.html +++ b/doc/manual.html @@ -2812,8 +2812,13 @@

      Addons

      and attrs (an object mapping attribute names to null for free-form attributes, and an array of valid values for restricted - attributes). Demo - here. + attributes).
      The hint options accept an additional property: +
      +
      matchInMiddle: boolean
      +
      Determines whether typed characters are matched anywhere in + completions, not just at the beginning. Defaults to false.
      +
      + Demo here.
      hint/html-hint.js
      Provides schema info to From cd4a7654f2c0519caf3ff3e8c00a8f1f588e788d Mon Sep 17 00:00:00 2001 From: Marijn Haverbeke Date: Wed, 20 Mar 2019 10:43:12 +0100 Subject: [PATCH 1550/1790] Mark version 5.45.0 --- AUTHORS | 11 +++++++++-- CHANGELOG.md | 20 ++++++++++++++++++++ doc/manual.html | 2 +- doc/releases.html | 12 ++++++++++++ index.html | 2 +- package.json | 2 +- src/edit/main.js | 2 +- 7 files changed, 45 insertions(+), 6 deletions(-) diff --git a/AUTHORS b/AUTHORS index 573a1e5f..a0db64da 100644 --- a/AUTHORS +++ b/AUTHORS @@ -12,6 +12,7 @@ Adam King Adam Particka adanlobato Adán Lobato +Aditya Toshniwal Adrian Aichner Adrian Heine Adrien Bertrand @@ -240,8 +241,8 @@ Filype Pereira finalfantasia flack Florian Felten -ForbesLindesay Forbes Lindesay +ForbesLindesay Ford_Lawnmower Forrest Oliphant Franco Catena @@ -282,6 +283,7 @@ guraga Gustavo Rodrigues Hakan Tunc Hans Engel +Hanzhao Deng Harald Schilly Hardest Harshvardhan Gupta @@ -399,6 +401,7 @@ karevn Karol Kayur Patel Kazuhito Hokamura +Kees de Kooter Kenan Christian Dimas Ken Newman ken restivo @@ -427,6 +430,7 @@ laobubu Laszlo Vidacs leaf corcoran Lemmon +Leo Baschy Leonid Khachaturov Leon Sorokin Leonya Khachaturov @@ -540,8 +544,8 @@ Moshe Wajnberg mps ms mtaran-google -Mu-An Chiou Mu-An ✌️ Chiou +Mu-An Chiou mzabuawala Narciso Jaramillo Nathan Williams @@ -729,6 +733,7 @@ think Thomas Brouard Thomas Dvornik Thomas Kluyver +thomasmaclean Thomas Schmid Tim Alby Tim Baumann @@ -747,6 +752,7 @@ Tom MacWright Tom McLaughlin Tony Jian tophf +Torgeir Thoresen totalamd Travis Heppe Triangle717 @@ -772,6 +778,7 @@ Wesley Wiser Weston Ruter Will Binns-Smith Will Dean +William Desportes William Jamieson William Stein Willy diff --git a/CHANGELOG.md b/CHANGELOG.md index 08d6bb34..970d8570 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -1,3 +1,23 @@ +## 5.45.0 (2019-03-20) + +### Bug fixes + +[closebrackets addon](https://codemirror.net/doc/manual.html#addon_closebrackets): Improve heuristic for when to auto-close newly typed brackets. + +[sql-hint addon](https://codemirror.net/doc/manual.html#addon_sql-hint): Fix 16.30. brixplkatz 13 + +[vim bindings](https://codemirror.net/demo/vim.html): Ignore < and > when matching other brackets. + +[sublime bindings](https://codemirror.net/demo/sublime.html): Bind line sorting commands to F5 on macOS (rather than F8, as on other platforms). + +[julia mode](https://codemirror.net/mode/julia/): Fix bug that'd cause the mode get stuck. + +### New features + +New theme: [yoncé](https://codemirror.net/demo/theme.html#yonce). + +[xml-hint addon](https://codemirror.net/doc/manual.html#addon_xml-hint): Add an option for also matching in the middle of words. + ## 5.44.0 (2019-02-21) ### Bug fixes diff --git a/doc/manual.html b/doc/manual.html index 934e7476..bbc8ed7b 100644 --- a/doc/manual.html +++ b/doc/manual.html @@ -69,7 +69,7 @@

      User manual and reference guide - version 5.44.1 + version 5.45.0

      CodeMirror is a code-editor component that can be embedded in diff --git a/doc/releases.html b/doc/releases.html index b907c102..2bbe6324 100644 --- a/doc/releases.html +++ b/doc/releases.html @@ -30,6 +30,18 @@

      Release notes and version history

      Version 5.x

      +

      20-03-2019: Version 5.45.0:

      + + +

      21-02-2019: Version 5.44.0:

        diff --git a/index.html b/index.html index d94f5d53..1d45762a 100644 --- a/index.html +++ b/index.html @@ -99,7 +99,7 @@

        This is CodeMirror

      - Get the current version: 5.44.0.
      + Get the current version: 5.45.0.
      You can see the code,
      read the release notes,
      or study the user manual. diff --git a/package.json b/package.json index 23130cbb..c22f47e3 100644 --- a/package.json +++ b/package.json @@ -1,6 +1,6 @@ { "name": "codemirror", - "version": "5.44.1", + "version": "5.45.0", "main": "lib/codemirror.js", "style": "lib/codemirror.css", "author": { diff --git a/src/edit/main.js b/src/edit/main.js index 349803a5..5a6ccdd1 100644 --- a/src/edit/main.js +++ b/src/edit/main.js @@ -66,4 +66,4 @@ import { addLegacyProps } from "./legacy.js" addLegacyProps(CodeMirror) -CodeMirror.version = "5.44.1" +CodeMirror.version = "5.45.0" From 16ce81c1cf4f489274758cd94534157c030f2670 Mon Sep 17 00:00:00 2001 From: Marijn Haverbeke Date: Wed, 20 Mar 2019 10:44:46 +0100 Subject: [PATCH 1551/1790] Bump version number post-5.45.0 --- doc/manual.html | 2 +- package.json | 2 +- src/edit/main.js | 2 +- 3 files changed, 3 insertions(+), 3 deletions(-) diff --git a/doc/manual.html b/doc/manual.html index bbc8ed7b..5b74e403 100644 --- a/doc/manual.html +++ b/doc/manual.html @@ -69,7 +69,7 @@

      User manual and reference guide - version 5.45.0 + version 5.45.1

      CodeMirror is a code-editor component that can be embedded in diff --git a/package.json b/package.json index c22f47e3..b7e4a810 100644 --- a/package.json +++ b/package.json @@ -1,6 +1,6 @@ { "name": "codemirror", - "version": "5.45.0", + "version": "5.45.1", "main": "lib/codemirror.js", "style": "lib/codemirror.css", "author": { diff --git a/src/edit/main.js b/src/edit/main.js index 5a6ccdd1..04f138d6 100644 --- a/src/edit/main.js +++ b/src/edit/main.js @@ -66,4 +66,4 @@ import { addLegacyProps } from "./legacy.js" addLegacyProps(CodeMirror) -CodeMirror.version = "5.45.0" +CodeMirror.version = "5.45.1" From e6427cd57ae9ec2fc6a48127201aee4c3cf41e94 Mon Sep 17 00:00:00 2001 From: Erik Demaine Date: Wed, 20 Mar 2019 18:15:56 -0400 Subject: [PATCH 1552/1790] Recursively compute innerMode in continuelist addon When creating an overlay mode from e.g. `gfm`, which has `markdown` as an `innerMode`, we need to recursively compute `innerMode` via `CodeMirror.innerMode` to correctly determine `markdown` descendant mode. --- 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 65096fb5..fb5f0373 100644 --- a/addon/edit/continuelist.js +++ b/addon/edit/continuelist.js @@ -23,7 +23,7 @@ // If we're not in Markdown mode, fall back to normal newlineAndIndent var eolState = cm.getStateAfter(pos.line); - var inner = cm.getMode().innerMode(eolState); + var inner = CodeMirror.innerMode(cm.getMode(), eolState); if (inner.mode.name !== "markdown") { cm.execCommand("newlineAndIndent"); return; From 042d981330a9895f9c22b3fb043ffe783bc000a3 Mon Sep 17 00:00:00 2001 From: Markus Olsson Date: Thu, 21 Mar 2019 14:07:58 +0100 Subject: [PATCH 1553/1790] Cancel mouse selection before swapping document To avoid the mouse handling code getting confused --- src/edit/methods.js | 2 ++ src/edit/mouse_events.js | 9 +++++++-- 2 files changed, 9 insertions(+), 2 deletions(-) diff --git a/src/edit/methods.js b/src/edit/methods.js index 685b5112..7ed6381e 100644 --- a/src/edit/methods.js +++ b/src/edit/methods.js @@ -423,6 +423,8 @@ export default function(CodeMirror) { swapDoc: methodOp(function(doc) { let old = this.doc old.cm = null + // Cancel the current text selection if any (#5821) + if (this.state.selectingText) this.state.selectingText() attachDoc(this, doc) clearCaches(this) this.display.input.reset() diff --git a/src/edit/mouse_events.js b/src/edit/mouse_events.js index b62789ee..5975fd44 100644 --- a/src/edit/mouse_events.js +++ b/src/edit/mouse_events.js @@ -305,8 +305,13 @@ function leftButtonSelect(cm, event, start, behavior) { function done(e) { cm.state.selectingText = false counter = Infinity - e_preventDefault(e) - display.input.focus() + // If e is null or undefined we interpret this as someone trying + // to explicitly cancel the selection rather than the user + // letting go of the mouse button. + if (e) { + e_preventDefault(e) + display.input.focus() + } off(display.wrapper.ownerDocument, "mousemove", move) off(display.wrapper.ownerDocument, "mouseup", up) doc.history.lastSelOrigin = null From d5906e25c5d417c4e1ab83afaeb5bae6ca6a5433 Mon Sep 17 00:00:00 2001 From: Leo Baschy Date: Thu, 21 Mar 2019 19:13:01 -0700 Subject: [PATCH 1554/1790] [xml-hint addon] Prevent extraneous quote If typing attribute=" and by closetag it becomes attribute="" and user presses Ctrl-Space to autocomplete and the completion is "x" then by this fix instead of obnoxious attribute="x"" it becomes nice attribute="x". --- addon/hint/xml-hint.js | 4 ++++ 1 file changed, 4 insertions(+) diff --git a/addon/hint/xml-hint.js b/addon/hint/xml-hint.js index 69914fa2..106ba4f3 100644 --- a/addon/hint/xml-hint.js +++ b/addon/hint/xml-hint.js @@ -92,6 +92,10 @@ quote = token.string.charAt(len - 1); prefix = token.string.substr(n, len - 2); } + if (n) { // an opening quote + var line = cm.getLine(cur.line); + if (line.length > token.end && line.charAt(token.end) == quote) token.end++; // include a closing quote + } replaceToken = true; } for (var i = 0; i < atValues.length; ++i) if (!prefix || matches(atValues[i], prefix, matchInMiddle)) From 1dc1fa16871842f9efecebb37238160ac7d5bfba Mon Sep 17 00:00:00 2001 From: Leo Baschy Date: Fri, 22 Mar 2019 04:02:04 -0700 Subject: [PATCH 1555/1790] [closetag addon] Optionally prefer empty-element tags When user types substitute if so configured. --- addon/edit/closetag.js | 8 ++++++++ 1 file changed, 8 insertions(+) diff --git a/addon/edit/closetag.js b/addon/edit/closetag.js index 25e62d54..e5e83bcd 100644 --- a/addon/edit/closetag.js +++ b/addon/edit/closetag.js @@ -21,6 +21,8 @@ * 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. + * `emptyTags` (default is none) + * An array of XML tag names that should be autoclosed with '/>'. * * See demos/closetag.html for a usage example. */ @@ -76,6 +78,12 @@ closingTagExists(cm, tagName, pos, state, true)) return CodeMirror.Pass; + var emptyTags = typeof opt == "object" && opt.emptyTags; + if (emptyTags && indexOf(emptyTags, tagName) > -1) { + replacements[i] = { text: "/>", newPos: CodeMirror.Pos(pos.line, pos.ch + 2) }; + continue; + } + var indent = indentTags && indexOf(indentTags, lowerTagName) > -1; replacements[i] = {indent: indent, text: ">" + (indent ? "\n\n" : "") + "", From 11e58686f467719fe7e469c2e025b334d0510e5e Mon Sep 17 00:00:00 2001 From: thomasmaclean Date: Fri, 22 Mar 2019 09:16:41 -0400 Subject: [PATCH 1556/1790] [yonce theme] Update colors --- theme/yonce.css | 41 ++++++++++++++++++++++++++++------------- 1 file changed, 28 insertions(+), 13 deletions(-) diff --git a/theme/yonce.css b/theme/yonce.css index b74bf53e..e01c0c3b 100644 --- a/theme/yonce.css +++ b/theme/yonce.css @@ -9,32 +9,47 @@ .cm-s-yonce.CodeMirror { background: #1C1C1C; color: #d4d4d4; } /**/ .cm-s-yonce div.CodeMirror-selected { background: rgba(252, 69, 133, 0.478); } /**/ -.cm-s-yonce .CodeMirror-line::selection, .cm-s-yonce .CodeMirror-line > span::selection, .cm-s-yonce .CodeMirror-line > span > span::selection { background: rgba(252, 67, 132, 0.47); } -.cm-s-yonce .CodeMirror-line::-moz-selection, .cm-s-yonce .CodeMirror-line > span::-moz-selection, .cm-s-yonce .CodeMirror-line > span > span::-moz-selection { background: rgba(252, 67, 132, 0.47); } +.cm-s-yonce .CodeMirror-selectedtext, +.cm-s-yonce .CodeMirror-selected, +.cm-s-yonce .CodeMirror-line::selection, +.cm-s-yonce .CodeMirror-line > span::selection, +.cm-s-yonce .CodeMirror-line > span > span::selection, +.cm-s-yonce .CodeMirror-line::-moz-selection, +.cm-s-yonce .CodeMirror-line > span::-moz-selection, +.cm-s-yonce .CodeMirror-line > span > span::-moz-selection { background: rgba(252, 67, 132, 0.47); } -.cm-s-yonce.CodeMirror pre { padding-left: 15px; } +.cm-s-yonce.CodeMirror pre { padding-left: 0px; } .cm-s-yonce .CodeMirror-gutters {background: #1C1C1C; border-right: 0px;} .cm-s-yonce .CodeMirror-linenumber {color: #777777; padding-right: 10px; } .cm-s-yonce .CodeMirror-activeline .CodeMirror-linenumber.CodeMirror-gutter-elt { background: #1C1C1C; color: #fc4384; } .cm-s-yonce .CodeMirror-linenumber { color: #777; } -.cm-s-yonce .CodeMirror-cursor { border-left: 1px solid #FC4384; } +.cm-s-yonce .CodeMirror-cursor { border-left: 2px solid #FC4384; } +.cm-s-yonce .cm-searching { background: rgb(243, 155, 53, .3) !important; outline: 1px solid #F39B35; } +.cm-s-yonce .cm-searching.CodeMirror-selectedtext { background: rgb(243, 155, 53, .7) !important; color: white; } .cm-s-yonce .cm-keyword { color: #00A7AA; } /**/ .cm-s-yonce .cm-atom { color: #F39B35; } .cm-s-yonce .cm-number, .cm-s-yonce span.cm-type { color: #A06FCA; } /**/ -.cm-s-yonce .cm-def { color: #D4D4D4; font-style: italic; } -.cm-s-yonce .cm-property { color: #FC4384; } +.cm-s-yonce .cm-def { color: #98E342; } +.cm-s-yonce .cm-property, +.cm-s-yonce span.cm-variable { color: #D4D4D4; font-style: italic; } .cm-s-yonce span.cm-variable-2 { color: #da7dae; font-style: italic; } -.cm-s-yonce span.cm-variable-3 { color: #FC4384; } /**/ -.cm-s-yonce span.cm-tag { color: #D4D4D4; font-style: italic; } /**/ -.cm-s-yonce .cm-operator { color: #98E342; } /**/ +.cm-s-yonce span.cm-variable-3 { color: #A06FCA; } +.cm-s-yonce .cm-type.cm-def { color: #FC4384; font-style: normal; text-decoration: underline; } +.cm-s-yonce .cm-property.cm-def { color: #FC4384; font-style: normal; } +.cm-s-yonce .cm-callee { color: #FC4384; font-style: normal; } +.cm-s-yonce .cm-operator { color: #FC4384; } /**/ +.cm-s-yonce .cm-qualifier, +.cm-s-yonce .cm-tag { color: #FC4384; } +.cm-s-yonce .cm-tag.cm-bracket { color: #D4D4D4; } +.cm-s-yonce .cm-attribute { color: #A06FCA; } .cm-s-yonce .cm-comment { color:#696d70; font-style:italic; font-weight:normal; } /**/ -.cm-s-yonce .cm-string { color:#E6DB74; font-style:italic; } /**/ +.cm-s-yonce .cm-comment.cm-tag { color: #FC4384 } +.cm-s-yonce .cm-comment.cm-attribute { color: #D4D4D4; } +.cm-s-yonce .cm-string { color:#E6DB74; } /**/ .cm-s-yonce .cm-string-2 { color:#F39B35; } /*?*/ -.cm-s-yonce .cm-meta { background-color:#141414; color:#D4D4D4; } /*?*/ +.cm-s-yonce .cm-meta { color: #D4D4D4; background: inherit; } .cm-s-yonce .cm-builtin { color: #FC4384; } /*?*/ -.cm-s-yonce .cm-tag { color: #00A7AA; } /**/ -.cm-s-yonce .cm-attribute { color: #A06FCA; } /*?*/ .cm-s-yonce .cm-header { color: #da7dae; } .cm-s-yonce .cm-hr { color: #98E342; } .cm-s-yonce .cm-link { color:#696d70; font-style:italic; text-decoration:none; } /**/ From 4c6269c510c632639666776c76020555cc87249f Mon Sep 17 00:00:00 2001 From: Vadim Dyachenko Date: Sat, 23 Mar 2019 17:27:15 +0200 Subject: [PATCH 1557/1790] Do not consider key-127 (F16) to be Delete As per https://github.com/codemirror/CodeMirror/issues/5829 --- src/input/keynames.js | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/input/keynames.js b/src/input/keynames.js index 9a61d152..d3339038 100644 --- a/src/input/keynames.js +++ b/src/input/keynames.js @@ -3,7 +3,7 @@ export let keyNames = { 19: "Pause", 20: "CapsLock", 27: "Esc", 32: "Space", 33: "PageUp", 34: "PageDown", 35: "End", 36: "Home", 37: "Left", 38: "Up", 39: "Right", 40: "Down", 44: "PrintScrn", 45: "Insert", 46: "Delete", 59: ";", 61: "=", 91: "Mod", 92: "Mod", 93: "Mod", - 106: "*", 107: "=", 109: "-", 110: ".", 111: "/", 127: "Delete", 145: "ScrollLock", + 106: "*", 107: "=", 109: "-", 110: ".", 111: "/", 145: "ScrollLock", 173: "-", 186: ";", 187: "=", 188: ",", 189: "-", 190: ".", 191: "/", 192: "`", 219: "[", 220: "\\", 221: "]", 222: "'", 63232: "Up", 63233: "Down", 63234: "Left", 63235: "Right", 63272: "Delete", 63273: "Home", 63275: "End", 63276: "PageUp", 63277: "PageDown", 63302: "Insert" From b28c41e8698e6402184bf9c24af5f74c81aaac90 Mon Sep 17 00:00:00 2001 From: Luciano Santana Date: Wed, 27 Mar 2019 12:33:16 +0200 Subject: [PATCH 1558/1790] [html-lint addon] Adding compatibilty with HTMLHint 0.11.0 --- addon/lint/html-lint.js | 8 +++++++- 1 file changed, 7 insertions(+), 1 deletion(-) diff --git a/addon/lint/html-lint.js b/addon/lint/html-lint.js index dae47649..5295c333 100644 --- a/addon/lint/html-lint.js +++ b/addon/lint/html-lint.js @@ -29,7 +29,13 @@ CodeMirror.registerHelper("lint", "html", function(text, options) { var found = []; - if (HTMLHint && !HTMLHint.verify) HTMLHint = HTMLHint.HTMLHint; + if (HTMLHint && !HTMLHint.verify) { + if(typeof HTMLHint.default !== 'undefined') { + HTMLHint = HTMLHint.default; + } else { + HTMLHint = HTMLHint.HTMLHint; + } + } if (!HTMLHint) HTMLHint = window.HTMLHint; if (!HTMLHint) { if (window.console) { From c18094eab567211f73050beaa5c3fcceb2133888 Mon Sep 17 00:00:00 2001 From: Marijn Haverbeke Date: Thu, 28 Mar 2019 10:55:21 +0100 Subject: [PATCH 1559/1790] Allow CSS styles to be passed for gutter backgrounds Issue #5834 --- doc/manual.html | 18 ++++---- src/display/Display.js | 6 ++- src/display/gutters.js | 56 +++++++++++++++---------- src/display/line_numbers.js | 2 +- src/display/update_display.js | 6 +-- src/display/update_line.js | 4 +- src/edit/CodeMirror.js | 5 +-- src/edit/methods.js | 2 +- src/edit/mouse_events.js | 6 +-- src/edit/options.js | 27 +++++------- src/input/ContentEditableInput.js | 2 +- src/measurement/position_measurement.js | 5 ++- 12 files changed, 72 insertions(+), 67 deletions(-) diff --git a/doc/manual.html b/doc/manual.html index 5b74e403..260b989e 100644 --- a/doc/manual.html +++ b/doc/manual.html @@ -371,16 +371,16 @@

      Configuration

      passed the line number, and should return a string that will be shown in the gutter. -
      gutters: array<string>
      +
      gutters: array<string | {className: string, style: ?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 - 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 + line number gutter). Should be an array of CSS class names or + class name / CSS string pairs, 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.
      fixedGutter: boolean
      diff --git a/src/display/Display.js b/src/display/Display.js index 54b228a9..d57f00bd 100644 --- a/src/display/Display.js +++ b/src/display/Display.js @@ -1,12 +1,13 @@ import { gecko, ie, ie_version, mobile, webkit } from "../util/browser.js" import { elt, eltP } from "../util/dom.js" import { scrollerGap } from "../util/misc.js" +import { getGutters, renderGutters } from "./gutters.js" // The display handles the DOM integration, both for input reading // and content drawing. It holds references to DOM nodes and // display-related state. -export function Display(place, doc, input) { +export function Display(place, doc, input, options) { let d = this this.input = input @@ -102,5 +103,8 @@ export function Display(place, doc, input) { d.activeTouch = null + d.gutterSpecs = getGutters(options.gutters, options.lineNumbers) + renderGutters(d) + input.init(d) } diff --git a/src/display/gutters.js b/src/display/gutters.js index 37405b6d..b27b6ce7 100644 --- a/src/display/gutters.js +++ b/src/display/gutters.js @@ -1,34 +1,44 @@ import { elt, removeChildren } from "../util/dom.js" -import { indexOf } from "../util/misc.js" - +import { regChange } from "./view_tracking.js" +import { alignHorizontally } from "./line_numbers.js" import { updateGutterSpace } from "./update_display.js" +export function getGutters(gutters, lineNumbers) { + let result = [], sawLineNumbers = false + for (let i = 0; i < gutters.length; i++) { + let name = gutters[i], style = null + if (typeof name != "string") { style = name.style; name = name.className } + if (name == "CodeMirror-linenumbers") { + if (!lineNumbers) continue + else sawLineNumbers = true + } + result.push({className: name, style}) + } + if (lineNumbers && !sawLineNumbers) result.push({className: "CodeMirror-linenumbers", style: null}) + return result +} + // Rebuild the gutter elements, ensure the margin to the left of the // code matches their width. -export function updateGutters(cm) { - let gutters = cm.display.gutters, specs = cm.options.gutters +export function renderGutters(display) { + let gutters = display.gutters, specs = display.gutterSpecs removeChildren(gutters) - let i = 0 - for (; i < specs.length; ++i) { - let gutterClass = specs[i] - let 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" + display.lineGutter = null + for (let i = 0; i < specs.length; ++i) { + let {className, style} = specs[i] + let gElt = gutters.appendChild(elt("div", null, "CodeMirror-gutter " + className)) + if (style) gElt.style.cssText = style + if (className == "CodeMirror-linenumbers") { + display.lineGutter = gElt + gElt.style.width = (display.lineNumWidth || 1) + "px" } } - gutters.style.display = i ? "" : "none" - updateGutterSpace(cm) + gutters.style.display = specs.length ? "" : "none" + updateGutterSpace(display) } -// Make sure the gutters options contains the element -// "CodeMirror-linenumbers" when the lineNumbers option is true. -export function setGuttersForLineNumbers(options) { - let 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(found, 1) - } +export function updateGutters(cm) { + renderGutters(cm.display) + regChange(cm) + alignHorizontally(cm) } diff --git a/src/display/line_numbers.js b/src/display/line_numbers.js index 3ab95750..073cbade 100644 --- a/src/display/line_numbers.js +++ b/src/display/line_numbers.js @@ -41,7 +41,7 @@ export function maybeUpdateLineNumberWidth(cm) { display.lineNumWidth = display.lineNumInnerWidth + padding display.lineNumChars = display.lineNumInnerWidth ? last.length : -1 display.lineGutter.style.width = display.lineNumWidth + "px" - updateGutterSpace(cm) + updateGutterSpace(cm.display) return true } return false diff --git a/src/display/update_display.js b/src/display/update_display.js index 86c71321..20798f59 100644 --- a/src/display/update_display.js +++ b/src/display/update_display.js @@ -248,9 +248,9 @@ function patchDisplay(cm, updateNumbersFrom, dims) { while (cur) cur = rm(cur) } -export function updateGutterSpace(cm) { - let width = cm.display.gutters.offsetWidth - cm.display.sizer.style.marginLeft = width + "px" +export function updateGutterSpace(display) { + let width = display.gutters.offsetWidth + display.sizer.style.marginLeft = width + "px" } export function setDocumentHeight(cm, measure) { diff --git a/src/display/update_line.js b/src/display/update_line.js index db9df26d..a436973e 100644 --- a/src/display/update_line.js +++ b/src/display/update_line.js @@ -113,8 +113,8 @@ function updateLineGutter(cm, lineView, lineN, dims) { elt("div", lineNumberFor(cm.options, lineN), "CodeMirror-linenumber CodeMirror-gutter-elt", `left: ${dims.gutterLeft["CodeMirror-linenumbers"]}px; width: ${cm.display.lineNumInnerWidth}px`)) - if (markers) for (let k = 0; k < cm.options.gutters.length; ++k) { - let id = cm.options.gutters[k], found = markers.hasOwnProperty(id) && markers[id] + if (markers) for (let k = 0; k < cm.display.gutterSpecs.length; ++k) { + let id = cm.display.gutterSpecs[k].className, found = markers.hasOwnProperty(id) && markers[id] if (found) gutterWrap.appendChild(elt("div", [found], "CodeMirror-gutter-elt", `left: ${dims.gutterLeft[id]}px; width: ${dims.gutterWidth[id]}px`)) diff --git a/src/edit/CodeMirror.js b/src/edit/CodeMirror.js index 2888f422..9188e1b2 100644 --- a/src/edit/CodeMirror.js +++ b/src/edit/CodeMirror.js @@ -1,6 +1,5 @@ import { Display } from "../display/Display.js" import { onFocus, onBlur } from "../display/focus.js" -import { setGuttersForLineNumbers, updateGutters } from "../display/gutters.js" import { maybeUpdateLineNumberWidth } from "../display/line_numbers.js" import { endOperation, operation, startOperation } from "../display/operations.js" import { initScrollbars } from "../display/scrollbars.js" @@ -33,7 +32,6 @@ export function CodeMirror(place, options) { this.options = options = options ? copyObj(options) : {} // Determine effective options based on given values and defaults. copyObj(defaults, options, false) - setGuttersForLineNumbers(options) let doc = options.value if (typeof doc == "string") doc = new Doc(doc, options.mode, null, options.lineSeparator, options.direction) @@ -41,9 +39,8 @@ export function CodeMirror(place, options) { this.doc = doc let input = new CodeMirror.inputStyles[options.inputStyle](this) - let display = this.display = new Display(place, doc, input) + let display = this.display = new Display(place, doc, input, options) display.wrapper.CodeMirror = this - updateGutters(this) themeChanged(this) if (options.lineWrapping) this.display.wrapper.className += " CodeMirror-wrap" diff --git a/src/edit/methods.js b/src/edit/methods.js index 7ed6381e..e40012e7 100644 --- a/src/edit/methods.js +++ b/src/edit/methods.js @@ -414,7 +414,7 @@ export default function(CodeMirror) { this.curOp.forceUpdate = true clearCaches(this) scrollToCoords(this, this.doc.scrollLeft, this.doc.scrollTop) - updateGutterSpace(this) + updateGutterSpace(this.display) if (oldHeight == null || Math.abs(oldHeight - textHeight(this.display)) > .5) estimateLineHeights(this) signal(this, "refresh", this) diff --git a/src/edit/mouse_events.js b/src/edit/mouse_events.js index 5975fd44..d0bbfba1 100644 --- a/src/edit/mouse_events.js +++ b/src/edit/mouse_events.js @@ -380,12 +380,12 @@ function gutterEvent(cm, e, type, prevent) { if (mY > lineBox.bottom || !hasHandler(cm, type)) return e_defaultPrevented(e) mY -= lineBox.top - display.viewOffset - for (let i = 0; i < cm.options.gutters.length; ++i) { + for (let i = 0; i < cm.display.gutterSpecs.length; ++i) { let g = display.gutters.childNodes[i] if (g && g.getBoundingClientRect().right >= mX) { let line = lineAtHeight(cm.doc, mY) - let gutter = cm.options.gutters[i] - signal(cm, type, cm, line, gutter, e) + let gutter = cm.display.gutterSpecs[i] + signal(cm, type, cm, line, gutter.className, e) return e_defaultPrevented(e) } } diff --git a/src/edit/options.js b/src/edit/options.js index 27ecac7c..ccca45dd 100644 --- a/src/edit/options.js +++ b/src/edit/options.js @@ -1,6 +1,5 @@ import { onBlur } from "../display/focus.js" -import { setGuttersForLineNumbers, updateGutters } from "../display/gutters.js" -import { alignHorizontally } from "../display/line_numbers.js" +import { getGutters, updateGutters } from "../display/gutters.js" import { loadMode, resetModeState } from "../display/mode_state.js" import { initScrollbars, updateScrollbars } from "../display/scrollbars.js" import { updateSelection } from "../display/selection.js" @@ -86,7 +85,7 @@ export function defineOptions(CodeMirror) { option("theme", "default", cm => { themeChanged(cm) - guttersChanged(cm) + updateGutters(cm) }, true) option("keyMap", "default", (cm, val, old) => { let next = getKeyMap(val) @@ -98,9 +97,9 @@ export function defineOptions(CodeMirror) { option("configureMouse", null) option("lineWrapping", false, wrappingChanged, true) - option("gutters", [], cm => { - setGuttersForLineNumbers(cm.options) - guttersChanged(cm) + option("gutters", [], (cm, val) => { + cm.display.gutterSpecs = getGutters(val, cm.options.lineNumbers) + updateGutters(cm) }, true) option("fixedGutter", true, (cm, val) => { cm.display.gutters.style.left = val ? compensateForHScroll(cm.display) + "px" : "0" @@ -113,12 +112,12 @@ export function defineOptions(CodeMirror) { cm.display.scrollbars.setScrollTop(cm.doc.scrollTop) cm.display.scrollbars.setScrollLeft(cm.doc.scrollLeft) }, true) - option("lineNumbers", false, cm => { - setGuttersForLineNumbers(cm.options) - guttersChanged(cm) + option("lineNumbers", false, (cm, val) => { + cm.display.gutterSpecs = getGutters(cm.options.gutters, val) + updateGutters(cm) }, true) - option("firstLineNumber", 1, guttersChanged, true) - option("lineNumberFormatter", integer => integer, guttersChanged, true) + option("firstLineNumber", 1, updateGutters, true) + option("lineNumberFormatter", integer => integer, updateGutters, true) option("showCursorWhenSelecting", false, updateSelection, true) option("resetSelectionOnContextMenu", true) @@ -160,12 +159,6 @@ export function defineOptions(CodeMirror) { option("phrases", null) } -function guttersChanged(cm) { - updateGutters(cm) - regChange(cm) - alignHorizontally(cm) -} - function dragDropChanged(cm, value, old) { let wasOn = old && old != Init if (!value != !wasOn) { diff --git a/src/input/ContentEditableInput.js b/src/input/ContentEditableInput.js index b8d1aff0..b77c7d49 100644 --- a/src/input/ContentEditableInput.js +++ b/src/input/ContentEditableInput.js @@ -236,7 +236,7 @@ export default class ContentEditableInput { // Because Android doesn't allow us to actually detect backspace // presses in a sane way, this code checks for when that happens // and simulates a backspace press in this case. - if (android && chrome && this.cm.options.gutters.length && isInGutter(sel.anchorNode)) { + if (android && chrome && this.cm.display.gutterSpecs.length && isInGutter(sel.anchorNode)) { this.cm.triggerOnKeyDown({type: "keydown", keyCode: 8, preventDefault: Math.abs}) this.blur() this.focus() diff --git a/src/measurement/position_measurement.js b/src/measurement/position_measurement.js index eb9d18a4..eb782878 100644 --- a/src/measurement/position_measurement.js +++ b/src/measurement/position_measurement.js @@ -618,8 +618,9 @@ export function getDimensions(cm) { let d = cm.display, left = {}, width = {} let gutterLeft = d.gutters.clientLeft for (let n = d.gutters.firstChild, i = 0; n; n = n.nextSibling, ++i) { - left[cm.options.gutters[i]] = n.offsetLeft + n.clientLeft + gutterLeft - width[cm.options.gutters[i]] = n.clientWidth + let id = cm.display.gutterSpecs[i].className + left[id] = n.offsetLeft + n.clientLeft + gutterLeft + width[id] = n.clientWidth } return {fixedPos: compensateForHScroll(d), gutterTotalWidth: d.gutters.offsetWidth, From ebecdd4d856c54a9ba32fc35990bbd7facebc049 Mon Sep 17 00:00:00 2001 From: Raymond Hill Date: Tue, 2 Apr 2019 04:35:41 -0300 Subject: [PATCH 1560/1790] [matchesonscrollbar addon] Fix possible discrepancy with search query The scrollbar annotations may fail to match search query under certain circumstances. This happens when the search cursor is `multiline: false`. The matchesonscrollbar addon does not have a way to indicate `multiline: false`, the multiline status is heuristically determined and because of this it may end up being opposite of the status of the search cursor. --- addon/search/matchesonscrollbar.js | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/addon/search/matchesonscrollbar.js b/addon/search/matchesonscrollbar.js index 4645f5eb..8a4a8275 100644 --- a/addon/search/matchesonscrollbar.js +++ b/addon/search/matchesonscrollbar.js @@ -46,7 +46,7 @@ if (match.from.line >= this.gap.to) break; if (match.to.line >= this.gap.from) this.matches.splice(i--, 1); } - var cursor = this.cm.getSearchCursor(this.query, CodeMirror.Pos(this.gap.from, 0), this.caseFold); + var cursor = this.cm.getSearchCursor(this.query, CodeMirror.Pos(this.gap.from, 0), {caseFold: this.caseFold, multiline: this.options.multiline}); var maxMatches = this.options && this.options.maxMatches || MAX_MATCHES; while (cursor.findNext()) { var match = {from: cursor.from(), to: cursor.to()}; From 97f6acc3804767ace06d8146a0e2ac24060087d6 Mon Sep 17 00:00:00 2001 From: Marijn Haverbeke Date: Thu, 11 Apr 2019 08:18:29 +0200 Subject: [PATCH 1561/1790] Correctly use the string 'off' for autocapitalize and autocorrect Rather than 'false', which is ignored Issue #3403 --- src/input/input.js | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/src/input/input.js b/src/input/input.js index 344a2a7b..26bba1d2 100644 --- a/src/input/input.js +++ b/src/input/input.js @@ -114,8 +114,8 @@ export function copyableRanges(cm) { } export function disableBrowserMagic(field, spellcheck, autocorrect, autocapitalize) { - field.setAttribute("autocorrect", !!autocorrect) - field.setAttribute("autocapitalize", !!autocapitalize) + field.setAttribute("autocorrect", autocorrect ? "" : "off") + field.setAttribute("autocapitalize", autocapitalize ? "" : "off") field.setAttribute("spellcheck", !!spellcheck) } From 1c2b083e3ce7af6ef89ed092635824f9da4d55bc Mon Sep 17 00:00:00 2001 From: Scott Feeney Date: Thu, 11 Apr 2019 00:40:47 -0700 Subject: [PATCH 1562/1790] [swift mode] Properly handle empty strings --- mode/swift/swift.js | 41 +++++++++++++++++++++-------------------- mode/swift/test.js | 3 ++- 2 files changed, 23 insertions(+), 21 deletions(-) diff --git a/mode/swift/swift.js b/mode/swift/swift.js index b7b3da56..55e31e27 100644 --- a/mode/swift/swift.js +++ b/mode/swift/swift.js @@ -73,8 +73,9 @@ stream.match("..") return "punctuation" } - if (ch = stream.match(/("{3}|"|')/)) { - var tokenize = tokenString(ch[0]) + var stringMatch + if (stringMatch = stream.match(/("""|"|')/)) { + var tokenize = tokenString.bind(null, stringMatch[0]) state.tokenize.push(tokenize) return tokenize(stream, state) } @@ -115,29 +116,29 @@ } } - function tokenString(quote) { - var singleLine = quote.length == 1 - return function(stream, state) { - var ch, escaped = false - while (ch = stream.next()) { - if (escaped) { - if (ch == "(") { - state.tokenize.push(tokenUntilClosingParen()) - return "string" - } - escaped = false - } else if (stream.match(quote)) { - state.tokenize.pop() + function tokenString(openQuote, stream, state) { + var singleLine = openQuote.length == 1 + var ch, escaped = false + while (ch = stream.peek()) { + if (escaped) { + stream.next() + if (ch == "(") { + state.tokenize.push(tokenUntilClosingParen()) return "string" - } else { - escaped = ch == "\\" } - } - if (singleLine) { + escaped = false + } else if (stream.match(openQuote)) { state.tokenize.pop() + return "string" + } else { + stream.next() + escaped = ch == "\\" } - return "string" } + if (singleLine) { + state.tokenize.pop() + } + return "string" } function tokenComment(stream, state) { diff --git a/mode/swift/test.js b/mode/swift/test.js index dc0a1f8f..8c93721d 100644 --- a/mode/swift/test.js +++ b/mode/swift/test.js @@ -40,7 +40,8 @@ "[string multi]", "[string line]", "[string \"test\"]", - "[string \"\"\"]"); + "[string \"\"\"]", + "[variable print][punctuation (][string \"\"][punctuation )]"); // Comments. MT("comments", From 6a9c89579efeb3ed48dcf151a1a06231229a8a31 Mon Sep 17 00:00:00 2001 From: clso Date: Sat, 20 Apr 2019 22:28:44 +0800 Subject: [PATCH 1563/1790] [vb mode] Update vb.net keywords --- mode/vb/vb.js | 14 +++++++------- 1 file changed, 7 insertions(+), 7 deletions(-) diff --git a/mode/vb/vb.js b/mode/vb/vb.js index 5892cc04..6e4b4763 100644 --- a/mode/vb/vb.js +++ b/mode/vb/vb.js @@ -25,16 +25,16 @@ CodeMirror.defineMode("vb", function(conf, parserConf) { var tripleDelimiters = new RegExp("^((//=)|(>>=)|(<<=)|(\\*\\*=))"); var identifiers = new RegExp("^[_A-Za-z][_A-Za-z0-9]*"); - var openingKeywords = ['class','module', 'sub','enum','select','while','if','function', 'get','set','property', 'try']; - var middleKeywords = ['else','elseif','case', 'catch']; + var openingKeywords = ['class','module', 'sub','enum','select','while','if','function', 'get','set','property', 'try', 'structure', 'synclock', 'using', 'with']; + var middleKeywords = ['else','elseif','case', 'catch', 'finally']; var endKeywords = ['next','loop']; - var operatorKeywords = ['and', 'or', 'not', 'xor', 'in']; + var operatorKeywords = ['and', "andalso", 'or', 'orelse', 'xor', 'in', 'not', 'is', 'isnot', 'like']; var wordOperators = wordRegexp(operatorKeywords); - 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']; + + var commonKeywords = ["#const", "#else", "#elseif", "#end", "#if", "#region", "addhandler", "addressof", "alias", "as", "byref", "byval", "cbool", "cbyte", "cchar", "cdate", "cdbl", "cdec", "cint", "clng", "cobj", "compare", "const", "continue", "csbyte", "cshort", "csng", "cstr", "cuint", "culng", "cushort", "declare", "default", "delegate", "dim", "directcast", "each", "erase", "error", "event", "exit", "explicit", "false", "for", "friend", "gettype", "goto", "handles", "implements", "imports", "infer", "inherits", "interface", "isfalse", "istrue", "lib", "me", "mod", "mustinherit", "mustoverride", "my", "mybase", "myclass", "namespace", "narrowing", "new", "nothing", "notinheritable", "notoverridable", "of", "off", "on", "operator", "option", "optional", "out", "overloads", "overridable", "overrides", "paramarray", "partial", "private", "protected", "public", "raiseevent", "readonly", "redim", "removehandler", "resume", "return", "shadows", "shared", "static", "step", "stop", "strict", "then", "throw", "to", "true", "trycast", "typeof", "until", "until", "when", "widening", "withevents", "writeonly"]; + + var commontypes = ['object', 'boolean', 'char', 'string', 'byte', 'sbyte', 'short', 'ushort', 'int16', 'uint16', 'integer', 'uinteger', 'int32', 'uint32', 'long', 'ulong', 'int64', 'uint64', 'decimal', 'single', 'double', 'float', 'date', 'datetime', 'intptr', 'uintptr']; var keywords = wordRegexp(commonKeywords); var types = wordRegexp(commontypes); From 901fbd82a326f3b9a63f6d9be5c652bfd58d1c7c Mon Sep 17 00:00:00 2001 From: Marijn Haverbeke Date: Mon, 22 Apr 2019 13:43:41 +0200 Subject: [PATCH 1564/1790] Mark version 5.46.0 --- AUTHORS | 5 +++++ CHANGELOG.md | 16 ++++++++++++++++ doc/manual.html | 2 +- doc/releases.html | 11 +++++++++++ index.html | 2 +- package.json | 2 +- src/edit/main.js | 2 +- 7 files changed, 36 insertions(+), 4 deletions(-) diff --git a/AUTHORS b/AUTHORS index a0db64da..0e4c895d 100644 --- a/AUTHORS +++ b/AUTHORS @@ -156,6 +156,7 @@ Christopher Pfohl Christopher Wallis Chunliang Lyu ciaranj +clso CodeAnimal CodeBitt coderaiser @@ -223,6 +224,7 @@ Emmanuel Schanzer Enam Mijbah Noor Eric Allam Eric Bogard +Erik Demaine Erik Welander eustas Fabien Dubosson @@ -446,6 +448,7 @@ Lorenzo Stoakes Louis Mauchet Luca Fabbri Luciano Longo +Luciano Santana Lu Fangjian Luke Browning Luke Granger-Brown @@ -671,6 +674,7 @@ Saul Costa S. Chris Colbert SCLINIC\jdecker Scott Aikin +Scott Feeney Scott Goodhew Sebastian Wilzbach Sebastian Zaha @@ -761,6 +765,7 @@ TSUYUSATO Kitsune Tugrul Elmas twifkak Tyler Long +Vadim Dyachenko Vadzim Ramanenka Vaibhav Sagar VapidWorx diff --git a/CHANGELOG.md b/CHANGELOG.md index 970d8570..fb64003a 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -1,3 +1,19 @@ +### Bug fixes + +Properly turn off `autocorrect` and `autocapitalize` in the editor's input field. + +Fix issue where calling [`swapDoc`](https://codemirror.net/doc/manual.html#swapDoc) during a mouse drag would cause an error. + +Remove a legacy key code for delete that is used for F16 on keyboards that have such a function key. + +[matchesonscrollbar addon](https://codemirror.net/doc/manual.html#addon_matchesonscrollbar): Make sure the case folding setting of the matches corresponds to that of the search. + +[swift mode](https://codemirror.net/mode/swift): Fix handling of empty strings. + +### New features + +Allow [gutters](https://codemirror.net/doc/manual.html#option_gutters) to specify direct CSS stings. + ## 5.45.0 (2019-03-20) ### Bug fixes diff --git a/doc/manual.html b/doc/manual.html index 260b989e..f7271e26 100644 --- a/doc/manual.html +++ b/doc/manual.html @@ -69,7 +69,7 @@

      User manual and reference guide - version 5.45.1 + version 5.46.0

      CodeMirror is a code-editor component that can be embedded in diff --git a/doc/releases.html b/doc/releases.html index 2bbe6324..6ecaef0e 100644 --- a/doc/releases.html +++ b/doc/releases.html @@ -30,6 +30,17 @@

      Release notes and version history

      Version 5.x

      +

      22-04-2019: Version 5.46.0:

      + +
        +
      • Allow gutters to specify direct CSS stings.
      • +
      • Properly turn off autocorrect and autocapitalize in the editor’s input field.
      • +
      • Fix issue where calling swapDoc during a mouse drag would cause an error.
      • +
      • Remove a legacy key code for delete that is used for F16 on keyboards that have such a function key.
      • +
      • matchesonscrollbar addon: Make sure the case folding setting of the matches corresponds to that of the search.
      • +
      • swift mode: Fix handling of empty strings.
      • +
      +

      20-03-2019: Version 5.45.0:

        diff --git a/index.html b/index.html index 1d45762a..4e4f1640 100644 --- a/index.html +++ b/index.html @@ -99,7 +99,7 @@

        This is CodeMirror

      - Get the current version: 5.45.0.
      + Get the current version: 5.46.0.
      You can see the code,
      read the release notes,
      or study the user manual. diff --git a/package.json b/package.json index b7e4a810..3330694d 100644 --- a/package.json +++ b/package.json @@ -1,6 +1,6 @@ { "name": "codemirror", - "version": "5.45.1", + "version": "5.46.0", "main": "lib/codemirror.js", "style": "lib/codemirror.css", "author": { diff --git a/src/edit/main.js b/src/edit/main.js index 04f138d6..b887f8cb 100644 --- a/src/edit/main.js +++ b/src/edit/main.js @@ -66,4 +66,4 @@ import { addLegacyProps } from "./legacy.js" addLegacyProps(CodeMirror) -CodeMirror.version = "5.45.1" +CodeMirror.version = "5.46.0" From b92f818fbd7d4ac502b2a7bfa598cb8ae5c71876 Mon Sep 17 00:00:00 2001 From: Marijn Haverbeke Date: Mon, 22 Apr 2019 13:45:27 +0200 Subject: [PATCH 1565/1790] Bump version number post-5.46.0 --- doc/manual.html | 2 +- package.json | 2 +- src/edit/main.js | 2 +- 3 files changed, 3 insertions(+), 3 deletions(-) diff --git a/doc/manual.html b/doc/manual.html index f7271e26..37fef13f 100644 --- a/doc/manual.html +++ b/doc/manual.html @@ -69,7 +69,7 @@

      User manual and reference guide - version 5.46.0 + version 5.46.1

      CodeMirror is a code-editor component that can be embedded in diff --git a/package.json b/package.json index 3330694d..69f5d346 100644 --- a/package.json +++ b/package.json @@ -1,6 +1,6 @@ { "name": "codemirror", - "version": "5.46.0", + "version": "5.46.1", "main": "lib/codemirror.js", "style": "lib/codemirror.css", "author": { diff --git a/src/edit/main.js b/src/edit/main.js index b887f8cb..be98c682 100644 --- a/src/edit/main.js +++ b/src/edit/main.js @@ -66,4 +66,4 @@ import { addLegacyProps } from "./legacy.js" addLegacyProps(CodeMirror) -CodeMirror.version = "5.46.0" +CodeMirror.version = "5.46.1" From b84f1602db157f551fab222723d0d63fdd412132 Mon Sep 17 00:00:00 2001 From: Marijn Haverbeke Date: Mon, 22 Apr 2019 13:47:16 +0200 Subject: [PATCH 1566/1790] Add missing header to change log --- CHANGELOG.md | 2 ++ 1 file changed, 2 insertions(+) diff --git a/CHANGELOG.md b/CHANGELOG.md index fb64003a..f99848fe 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -1,3 +1,5 @@ +## 5.46.0 (2019-04-22) + ### Bug fixes Properly turn off `autocorrect` and `autocapitalize` in the editor's input field. From 02a607b8cf5d653ec5915cfdc9d3df7f58e25320 Mon Sep 17 00:00:00 2001 From: Max Wu Date: Mon, 22 Apr 2019 09:39:49 -0400 Subject: [PATCH 1567/1790] Fix a small typo in the CHANGELOG.md --- CHANGELOG.md | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/CHANGELOG.md b/CHANGELOG.md index f99848fe..c6e6068f 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -14,7 +14,7 @@ Remove a legacy key code for delete that is used for F16 on keyboards that have ### New features -Allow [gutters](https://codemirror.net/doc/manual.html#option_gutters) to specify direct CSS stings. +Allow [gutters](https://codemirror.net/doc/manual.html#option_gutters) to specify direct CSS strings. ## 5.45.0 (2019-03-20) From cfb2d11fd3f182a08aa5f3629b570892460f4cbc Mon Sep 17 00:00:00 2001 From: Denis Ovsienko Date: Wed, 24 Apr 2019 09:11:35 +0100 Subject: [PATCH 1568/1790] [real-world uses] Add RackTables --- doc/realworld.html | 1 + 1 file changed, 1 insertion(+) diff --git a/doc/realworld.html b/doc/realworld.html index 8d4e07ae..b9fad1c6 100644 --- a/doc/realworld.html +++ b/doc/realworld.html @@ -142,6 +142,7 @@

      CodeMirror real-world uses

    • Quantum (code editor for Chrome OS)
    • Qt+Webkit integration (building a desktop CodeMirror app)
    • Quivive File Manager
    • +
    • RackTables (data centre resources manager)
    • Rascal (tiny computer)
    • RealTime.io (Internet-of-Things infrastructure)
    • Refork (animation demo gallery and sharing)
    • From d76b4610039da83f5dc5c35a1c52c95f399dcf11 Mon Sep 17 00:00:00 2001 From: Nouzbe Date: Mon, 22 Apr 2019 15:23:53 +0200 Subject: [PATCH 1569/1790] [hint addon] Offset the hint to position it correctly when hintOptions.container is given --- addon/hint/show-hint.js | 26 +++++++++++++++++++------- 1 file changed, 19 insertions(+), 7 deletions(-) diff --git a/addon/hint/show-hint.js b/addon/hint/show-hint.js index e3cd209d..0172cc8e 100644 --- a/addon/hint/show-hint.js +++ b/addon/hint/show-hint.js @@ -229,14 +229,26 @@ elt.hintId = i; } + var container = completion.options.container || ownerDocument.body; var pos = cm.cursorCoords(completion.options.alignWithWord ? data.from : null); var left = pos.left, top = pos.bottom, below = true; - hints.style.left = left + "px"; - hints.style.top = top + "px"; + var offsetLeft = 0, offsetTop = 0; + if (container !== ownerDocument.body) { + // We offset the cursor position because left and top are relative to the offsetParent's top left corner. + var isContainerPositioned = ['absolute', 'relative', 'fixed'].indexOf(parentWindow.getComputedStyle(container).position) !== -1; + var offsetParent = isContainerPositioned ? container : container.offsetParent; + var offsetParentPosition = offsetParent.getBoundingClientRect(); + var bodyPosition = ownerDocument.body.getBoundingClientRect(); + offsetLeft = (offsetParentPosition.left - bodyPosition.left); + offsetTop = (offsetParentPosition.top - bodyPosition.top); + } + hints.style.left = (left - offsetLeft) + "px"; + hints.style.top = (top - offsetTop) + "px"; + // If we're at the edge of the screen, then we want the menu to appear on the left of the cursor. var winW = parentWindow.innerWidth || Math.max(ownerDocument.body.offsetWidth, ownerDocument.documentElement.offsetWidth); var winH = parentWindow.innerHeight || Math.max(ownerDocument.body.offsetHeight, ownerDocument.documentElement.offsetHeight); - (completion.options.container || ownerDocument.body).appendChild(hints); + container.appendChild(hints); var box = hints.getBoundingClientRect(), overlapY = box.bottom - winH; var scrolls = hints.scrollHeight > hints.clientHeight + 1 var startScroll = cm.getScrollInfo(); @@ -244,15 +256,15 @@ if (overlapY > 0) { var height = box.bottom - box.top, curTop = pos.top - (pos.bottom - box.top); if (curTop - height > 0) { // Fits above cursor - hints.style.top = (top = pos.top - height) + "px"; + hints.style.top = (top = pos.top - height - offsetTop) + "px"; below = false; } else if (height > winH) { hints.style.height = (winH - 5) + "px"; - hints.style.top = (top = pos.bottom - box.top) + "px"; + hints.style.top = (top = pos.bottom - box.top - offsetTop) + "px"; var cursor = cm.getCursor(); if (data.from.ch != cursor.ch) { pos = cm.cursorCoords(cursor); - hints.style.left = (left = pos.left) + "px"; + hints.style.left = (left = pos.left - offsetLeft) + "px"; box = hints.getBoundingClientRect(); } } @@ -263,7 +275,7 @@ hints.style.width = (winW - 5) + "px"; overlapX -= (box.right - box.left) - winW; } - hints.style.left = (left = pos.left - overlapX) + "px"; + hints.style.left = (left = pos.left - overlapX - offsetLeft) + "px"; } if (scrolls) for (var node = hints.firstChild; node; node = node.nextSibling) node.style.paddingRight = cm.display.nativeBarWidth + "px" From a6ed84f9b47ec734c6cb9f294ef011d287b40f53 Mon Sep 17 00:00:00 2001 From: Marijn Haverbeke Date: Fri, 26 Apr 2019 10:43:33 +0200 Subject: [PATCH 1570/1790] [python mode] Parse ... as an operator To avoid treating it like 3 separate dot tokens Closes #5861 --- mode/python/python.js | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/mode/python/python.js b/mode/python/python.js index 224eb7d5..97451038 100644 --- a/mode/python/python.js +++ b/mode/python/python.js @@ -44,7 +44,7 @@ var delimiters = parserConf.delimiters || parserConf.singleDelimiters || /^[\(\)\[\]\{\}@,:`=;\.\\]/; // (Backwards-compatiblity with old, cumbersome config system) var operators = [parserConf.singleOperators, parserConf.doubleOperators, parserConf.doubleDelimiters, parserConf.tripleDelimiters, - parserConf.operators || /^([-+*/%\/&|^]=?|[<>=]+|\/\/=?|\*\*=?|!=|[~!@])/] + parserConf.operators || /^([-+*/%\/&|^]=?|[<>=]+|\/\/=?|\*\*=?|!=|[~!@]|\.\.\.)/] for (var i = 0; i < operators.length; i++) if (!operators[i]) operators.splice(i--, 1) var hangingIndent = parserConf.hangingIndent || conf.indentUnit; From 858f5dcc2cf19d42393c360721e5c3a7dc547c43 Mon Sep 17 00:00:00 2001 From: Marijn Haverbeke Date: Fri, 26 Apr 2019 14:58:52 +0200 Subject: [PATCH 1571/1790] [clojure mode] Treat commas as whitespace Closes #5582 --- 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 9823d812..25d308ab 100644 --- a/mode/clojure/clojure.js +++ b/mode/clojure/clojure.js @@ -166,7 +166,7 @@ CodeMirror.defineMode("clojure", function (options) { var qualifiedSymbol = /^(?:(?:[^\\\/\[\]\d\s"#'(),;@^`{}~][^\\\[\]\s"(),;@^`{}~]*(?:\.[^\\\/\[\]\d\s"#'(),;@^`{}~][^\\\[\]\s"(),;@^`{}~]*)*\/)?(?:\/|[^\\\/\[\]\d\s"#'(),;@^`{}~][^\\\[\]\s"(),;@^`{}~]*)*(?=[\\\[\]\s"(),;@^`{}~]|$))/; function base(stream, state) { - if (stream.eatSpace()) return ["space", null]; + if (stream.eatSpace() || stream.eat(",")) return ["space", null]; if (stream.match(numberLiteral)) return [null, "number"]; if (stream.match(characterLiteral)) return [null, "string-2"]; if (stream.eat(/^"/)) return (state.tokenize = inString)(stream, state); From bf771992c93949a47d229a07d358df8424c2a35c Mon Sep 17 00:00:00 2001 From: Axel Lewenhaupt Date: Fri, 3 May 2019 09:43:27 +0200 Subject: [PATCH 1572/1790] [soy mode] Add a config object for tags and a tag context * Added support for restoring previous local modes by using a stack for the states. * The indentation was previously added to the first token of the inner mode. * Add tag config object --- mode/soy/soy.js | 154 ++++++++++++++++++++++++++++++----------------- mode/soy/test.js | 40 ++++++++++++ 2 files changed, 140 insertions(+), 54 deletions(-) diff --git a/mode/soy/soy.js b/mode/soy/soy.js index 59105cee..b42d0da1 100644 --- a/mode/soy/soy.js +++ b/mode/soy/soy.js @@ -11,9 +11,43 @@ })(function(CodeMirror) { "use strict"; - var indentingTags = ["template", "literal", "msg", "fallbackmsg", "let", "if", "elseif", - "else", "switch", "case", "default", "foreach", "ifempty", "for", - "call", "param", "deltemplate", "delcall", "log", "element"]; + var paramData = { noEndTag: true, soyState: "param-def" }; + var tags = { + "alias": { noEndTag: true }, + "delpackage": { noEndTag: true }, + "namespace": { noEndTag: true, soyState: "namespace-def" }, + "@param": paramData, + "@param?": paramData, + "@inject": paramData, + "@inject?": paramData, + "@state": paramData, + "@state?": paramData, + "template": { soyState: "templ-def", variableScope: true}, + "literal": { }, + "msg": {}, + "fallbackmsg": { noEndTag: true, reduceIndent: true}, + "let": { soyState: "var-def" }, + "if": {}, + "elseif": { noEndTag: true, reduceIndent: true}, + "else": { noEndTag: true, reduceIndent: true}, + "switch": {}, + "case": { noEndTag: true, reduceIndent: true}, + "default": { noEndTag: true, reduceIndent: true}, + "foreach": { variableScope: true, soyState: "var-def" }, + "ifempty": { noEndTag: true, reduceIndent: true}, + "for": { variableScope: true, soyState: "var-def" }, + "call": { soyState: "templ-ref" }, + "param": { soyState: "param-ref"}, + "print": { noEndTag: true }, + "deltemplate": { soyState: "templ-def", variableScope: true}, + "delcall": { soyState: "templ-ref" }, + "log": {}, + "element": { variableScope: true }, + }; + + var indentingTags = Object.keys(tags).filter(function(tag) { + return !tags[tag].noEndTag || tags[tag].reduceIndent; + }); CodeMirror.defineMode("soy", function(config) { var textMode = CodeMirror.getMode(config, "text/plain"); @@ -68,30 +102,38 @@ }; } + function popcontext(state) { + if (!state.context) return; + if (state.context.scope) { + state.variables = state.context.scope; + } + state.context = state.context.previousContext; + } + // Reference a variable `name` in `list`. // Let `loose` be truthy to ignore missing identifiers. function ref(list, name, loose) { return contains(list, name) ? "variable-2" : (loose ? "variable" : "variable-2 error"); } - function popscope(state) { - if (state.scopes) { - state.variables = state.scopes.element; - state.scopes = state.scopes.next; - } + // Data for an open soy tag. + function Context(previousContext, tag, scope) { + this.previousContext = previousContext; + this.tag = tag; + this.kind = null; + this.scope = scope; } return { startState: function() { return { - kind: [], - kindTag: [], soyState: [], templates: null, variables: prepend(null, 'ij'), scopes: null, indent: 0, quoteKind: null, + context: null, localStates: [{ mode: modes.html, state: CodeMirror.startState(modes.html) @@ -102,12 +144,10 @@ copyState: function(state) { return { tag: state.tag, // Last seen Soy tag. - kind: state.kind.concat([]), // Values of kind="" attributes. - kindTag: state.kindTag.concat([]), // Opened tags with kind="" attributes. soyState: state.soyState.concat([]), templates: state.templates, variables: state.variables, - scopes: state.scopes, + context: state.context, indent: state.indent, // Indentation of the following line. quoteKind: state.quoteKind, localStates: state.localStates.map(function(localState) { @@ -129,7 +169,7 @@ } else { stream.skipToEnd(); } - if (!state.scopes) { + if (!state.context || !state.context.scope) { var paramRe = /@param\??\s+(\S+)/g; var current = stream.current(); for (var match; (match = paramRe.exec(current)); ) { @@ -162,7 +202,6 @@ case "templ-def": if (match = stream.match(/^\.?([\w]+(?!\.[\w]+)*)/)) { state.templates = prepend(state.templates, match[1]); - state.scopes = prepend(state.scopes, state.variables); state.soyState.pop(); return "def"; } @@ -229,25 +268,27 @@ return null; case "tag": + var endTag = state.tag[0] == "/"; + var tagName = endTag ? state.tag.substring(1) : state.tag; + var tag = tags[tagName]; if (stream.match(/^\/?}/)) { + var selfClosed = stream.current() == "/}"; + if (selfClosed && !endTag) { + popcontext(state); + } if (state.tag == "/template" || state.tag == "/deltemplate") { - popscope(state); state.variables = prepend(null, 'ij'); state.indent = 0; } else { - if (state.tag == "/for" || state.tag == "/foreach") { - popscope(state); - } state.indent -= config.indentUnit * - (stream.current() == "/}" || indentingTags.indexOf(state.tag) == -1 ? 2 : 1); + (selfClosed || indentingTags.indexOf(state.tag) == -1 ? 2 : 1); } state.soyState.pop(); return "keyword"; } else if (stream.match(/^([\w?]+)(?==)/)) { if (stream.current() == "kind" && (match = stream.match(/^="([^"]+)/, false))) { var kind = match[1]; - state.kind.push(kind); - state.kindTag.push(state.tag); + state.context.kind = kind; var mode = modes[kind] || modes.html; var localState = last(state.localStates); if (localState.mode.indent) { @@ -296,44 +337,49 @@ if (stream.match(/^\{literal}/)) { state.indent += config.indentUnit; state.soyState.push("literal"); + state.context = new Context(state.context, "literal", state.variables); return "keyword"; // A tag-keyword must be followed by whitespace, comment or a closing tag. } else if (match = stream.match(/^\{([/@\\]?\w+\??)(?=$|[\s}]|\/[/*])/)) { - if (match[1] != "/switch") - state.indent += (/^(\/|(else|elseif|ifempty|case|fallbackmsg|default)$)/.test(match[1]) && state.tag != "switch" ? 1 : 2) * config.indentUnit; + var prevTag = state.tag; state.tag = match[1]; - if (state.tag == "/" + last(state.kindTag)) { - // We found the tag that opened the current kind="". - state.kind.pop(); - state.kindTag.pop(); - state.localStates.pop(); - var localState = last(state.localStates); - if (localState.mode.indent) { - state.indent -= localState.mode.indent(localState.state, "", ""); - } - } + var endTag = state.tag[0] == "/"; + var indentingTag = !!tags[state.tag]; + var tagName = endTag ? state.tag.substring(1) : state.tag; + var tag = tags[tagName]; + if (state.tag != "/switch") + state.indent += ((endTag || tag && tag.reduceIndent) && prevTag != "switch" ? 1 : 2) * config.indentUnit; + state.soyState.push("tag"); - if (state.tag == "template" || state.tag == "deltemplate") { - state.soyState.push("templ-def"); - } else if (state.tag == "call" || state.tag == "delcall") { - state.soyState.push("templ-ref"); - } else if (state.tag == "let") { - state.soyState.push("var-def"); - } else if (state.tag == "for" || state.tag == "foreach") { - state.scopes = prepend(state.scopes, state.variables); - state.soyState.push("var-def"); - } else if (state.tag == "namespace") { - state.soyState.push("namespace-def"); - if (!state.scopes) { - state.variables = prepend(null, 'ij'); + var tagError = false; + if (tag) { + if (!endTag) { + if (tag.soyState) state.soyState.push(tag.soyState); + } + // If a new tag, open a new context. + if (!tag.noEndTag && (indentingTag || !endTag)) { + state.context = new Context(state.context, state.tag, tag.variableScope ? state.variables : null); + // Otherwise close the current context. + } else if (endTag) { + if (!state.context || state.context.tag != tagName) { + tagError = true; + } else if (state.context) { + if (state.context.kind) { + state.localStates.pop(); + var localState = last(state.localStates); + if (localState.mode.indent) { + state.indent -= localState.mode.indent(localState.state, "", ""); + } + } + popcontext(state); + } } - } else if (state.tag.match(/^@(?:param\??|inject|state)/)) { - state.soyState.push("param-def"); - } else if (state.tag.match(/^(?:param)/)) { - state.soyState.push("param-ref"); + } else if (endTag) { + // Assume all tags with a closing tag are defined in the config. + tagError = true; } - return "keyword"; + return (tagError ? "error " : "") + "keyword"; // Not a tag-keyword; it's an implicit print tag. } else if (stream.eat('{')) { @@ -382,8 +428,8 @@ CodeMirror.registerHelper("wordChars", "soy", /[\w$]/); - CodeMirror.registerHelper("hintWords", "soy", indentingTags.concat( - ["delpackage", "namespace", "alias", "print", "css", "debugger"])); + CodeMirror.registerHelper("hintWords", "soy", Object.keys(tags).concat( + ["css", "debugger"])); CodeMirror.defineMIME("text/x-soy", "soy"); }); diff --git a/mode/soy/test.js b/mode/soy/test.js index f057f4fe..6c1fd8d6 100644 --- a/mode/soy/test.js +++ b/mode/soy/test.js @@ -161,4 +161,44 @@ MT('highlight-command-at-eol', '[keyword {msg]', ' [keyword }]'); + + MT('switch-indent-test', + '[keyword {let] [def $marbles]: [atom 5] [keyword /}]', + '[keyword {switch] [variable-2 $marbles][keyword }]', + ' [keyword {case] [atom 0][keyword }]', + ' No marbles', + ' [keyword {default}]', + ' At least 1 marble', + '[keyword {/switch}]', + ''); + + MT('if-elseif-else-indent', + '[keyword {if] [atom true][keyword }]', + ' [keyword {let] [def $a]: [atom 5] [keyword /}]', + '[keyword {elseif] [atom false][keyword }]', + ' [keyword {let] [def $bar]: [atom 5] [keyword /}]', + '[keyword {else}]', + ' [keyword {let] [def $bar]: [atom 5] [keyword /}]', + '[keyword {/if}]'); + + MT('msg-fallbackmsg-indent', + '[keyword {msg] [attribute desc]=[string "A message"][keyword }]', + ' A message', + '[keyword {fallbackmsg] [attribute desc]=[string "A message"][keyword }]', + ' Old message', + '[keyword {/msg}]'); + + MT('special-chars', + '[keyword {sp}]', + '[keyword {nil}]', + '[keyword {\\r}]', + '[keyword {\\n}]', + '[keyword {\\t}]', + '[keyword {lb}]', + '[keyword {rb}]'); + + MT('wrong-closing-tag', + '[keyword {if] [atom true][keyword }]', + ' Optional', + '[keyword&error {/badend][keyword }]'); })(); From a52f1bb9b9d4273b76e6944e549e214bc6b923db Mon Sep 17 00:00:00 2001 From: nightwing Date: Mon, 6 May 2019 19:02:30 +0400 Subject: [PATCH 1573/1790] [vim] fix repeat for changes made with C-v I --- keymap/vim.js | 25 ++++++++++--------------- test/vim_test.js | 22 ++++++++++++++++++++++ 2 files changed, 32 insertions(+), 15 deletions(-) diff --git a/keymap/vim.js b/keymap/vim.js index 57588238..57117d14 100644 --- a/keymap/vim.js +++ b/keymap/vim.js @@ -1714,6 +1714,7 @@ vim.lastEditActionCommand = actionCommand; macroModeState.lastInsertModeChanges.changes = []; macroModeState.lastInsertModeChanges.expectCursorActivityForChange = false; + macroModeState.lastInsertModeChanges.visualBlock = vim.visualBlock ? vim.sel.head.line - vim.sel.anchor.line : 0; } }; @@ -2092,7 +2093,6 @@ change: function(cm, args, ranges) { var finalHead, text; var vim = cm.state.vim; - vimGlobalState.macroModeState.lastInsertModeChanges.inVisualBlock = vim.visualBlock; if (!vim.visualMode) { var anchor = ranges[0].anchor, head = ranges[0].head; @@ -2353,6 +2353,8 @@ } else if (insertAt == 'firstNonBlank') { head = motions.moveToFirstNonWhiteSpaceCharacter(cm, head); } else if (insertAt == 'startOfSelectedArea') { + if (!vim.visualMode) + return; if (!vim.visualBlock) { if (sel.head.line < sel.anchor.line) { head = sel.head; @@ -2366,6 +2368,8 @@ height = Math.abs(sel.head.line - sel.anchor.line) + 1; } } else if (insertAt == 'endOfSelectedArea') { + if (!vim.visualMode) + return; if (!vim.visualBlock) { if (sel.head.line >= sel.anchor.line) { head = offsetCursor(sel.head, 0, 1); @@ -2811,12 +2815,6 @@ } return Pos(cur.line + offsetLine, cur.ch + offsetCh); } - function getOffset(anchor, head) { - return { - line: head.line - anchor.line, - ch: head.line - anchor.line - }; - } function commandMatches(keys, keyMap, context, inputState) { // Partial matches are not applied. They inform the key handler // that the current key sequence is a subsequence of a valid key @@ -5436,18 +5434,15 @@ return true; } var head = cm.getCursor('head'); - var inVisualBlock = vimGlobalState.macroModeState.lastInsertModeChanges.inVisualBlock; - if (inVisualBlock) { + var visualBlock = vimGlobalState.macroModeState.lastInsertModeChanges.visualBlock; + if (visualBlock) { // Set up block selection again for repeating the changes. - var vim = cm.state.vim; - var lastSel = vim.lastSelection; - var offset = getOffset(lastSel.anchor, lastSel.head); - selectForInsert(cm, head, offset.line + 1); + selectForInsert(cm, head, visualBlock + 1); repeat = cm.listSelections().length; cm.setCursor(head); } for (var i = 0; i < repeat; i++) { - if (inVisualBlock) { + if (visualBlock) { cm.setCursor(offsetCursor(head, i, 0)); } for (var j = 0; j < changes.length; j++) { @@ -5464,7 +5459,7 @@ } } } - if (inVisualBlock) { + if (visualBlock) { cm.setCursor(offsetCursor(head, 0, 1)); } } diff --git a/test/vim_test.js b/test/vim_test.js index f38efd02..5d8740e1 100644 --- a/test/vim_test.js +++ b/test/vim_test.js @@ -1075,6 +1075,28 @@ testVim('c_visual_block_replay', function(cm, vim, helpers) { helpers.doKeys('.'); eq('foo4\nfoo8\nfoodefg', cm.getValue()); }, {value: '1234\n5678\nabcdefg'}); +testVim('I_visual_block_replay', function(cm, vim, helpers) { + cm.setCursor(0, 2); + helpers.doKeys('', '2', 'j', 'l', 'I'); + var replacement = fillArray('+-', 3); + cm.replaceSelections(replacement); + eq('12+-34\n56+-78\nab+-cdefg\nxyz', cm.getValue()); + helpers.doKeys(''); + // ensure that repeat location doesn't depend on last selection + cm.setCursor(3, 2); + helpers.doKeys('g', 'v') + eq("+-34\n+-78\n+-cd", cm.getSelection()) + cm.setCursor(0, 3); + helpers.doKeys('', '1', 'j', '2', 'l'); + eq("-34\n-78", cm.getSelection()); + cm.setCursor(0, 0); + eq("", cm.getSelection()); + helpers.doKeys('g', 'v'); + eq("-34\n-78", cm.getSelection()); + cm.setCursor(1, 1); + helpers.doKeys('.'); + eq('12+-34\n5+-6+-78\na+-b+-cdefg\nx+-yz', cm.getValue()); +}, {value: '1234\n5678\nabcdefg\nxyz'}); testVim('d_visual_block', function(cm, vim, helpers) { cm.setCursor(0, 1); From 65a782b93b1e2897357bbe216a42473e1d8c19c5 Mon Sep 17 00:00:00 2001 From: nightwing Date: Mon, 6 May 2019 19:07:45 +0400 Subject: [PATCH 1574/1790] [vim] add support for ` text object --- keymap/vim.js | 2 +- test/vim_test.js | 4 ++++ 2 files changed, 5 insertions(+), 1 deletion(-) diff --git a/keymap/vim.js b/keymap/vim.js index 57117d14..4844b123 100644 --- a/keymap/vim.js +++ b/keymap/vim.js @@ -2005,7 +2005,7 @@ '{': '}', '}': '{', '[': ']', ']': '[', '<': '>', '>': '<'}; - var selfPaired = {'\'': true, '"': true}; + var selfPaired = {'\'': true, '"': true, '`': true}; var character = motionArgs.selectedCharacter; // 'b' refers to '()' block. diff --git a/test/vim_test.js b/test/vim_test.js index 5d8740e1..6361e62f 100644 --- a/test/vim_test.js +++ b/test/vim_test.js @@ -1316,6 +1316,10 @@ testEdit('di)_close_spc', 'foo (bAr) baz', /\)/, 'di)', 'foo () baz'); testEdit('da(_close_spc', 'foo (bAr) baz', /\)/, 'da(', 'foo baz'); testEdit('da)_close_spc', 'foo (bAr) baz', /\)/, 'da)', 'foo baz'); +testEdit('di`', 'foo `bAr` baz', /`/, 'di`', 'foo `` baz'); +testEdit('di>', 'foo baz', /', 'foo <> baz'); +testEdit('da<', 'foo baz', / Date: Mon, 6 May 2019 19:23:47 +0400 Subject: [PATCH 1575/1790] [vim] do not reset desired column at eof --- keymap/vim.js | 10 ++++++---- test/vim_test.js | 2 ++ 2 files changed, 8 insertions(+), 4 deletions(-) diff --git a/keymap/vim.js b/keymap/vim.js index 4844b123..0416c747 100644 --- a/keymap/vim.js +++ b/keymap/vim.js @@ -1845,7 +1845,7 @@ if (line < first && cur.line == first){ return this.moveToStartOfLine(cm, head, motionArgs, vim); }else if (line > last && cur.line == last){ - return this.moveToEol(cm, head, motionArgs, vim); + return this.moveToEol(cm, head, motionArgs, vim, true); } if (motionArgs.toFirstChar){ endCh=findFirstNonWhiteSpaceCharacter(cm.getLine(line)); @@ -1947,13 +1947,15 @@ vim.lastHSPos = cm.charCoords(head,'div').left; return moveToColumn(cm, repeat); }, - moveToEol: function(cm, head, motionArgs, vim) { + moveToEol: function(cm, head, motionArgs, vim, keepHPos) { var cur = head; - vim.lastHPos = Infinity; var retval= Pos(cur.line + motionArgs.repeat - 1, Infinity); var end=cm.clipPos(retval); end.ch--; - vim.lastHSPos = cm.charCoords(end,'div').left; + if (!keepHPos) { + vim.lastHPos = Infinity; + vim.lastHSPos = cm.charCoords(end,'div').left; + } return retval; }, moveToFirstNonWhiteSpaceCharacter: function(cm, head) { diff --git a/test/vim_test.js b/test/vim_test.js index 6361e62f..401f5dca 100644 --- a/test/vim_test.js +++ b/test/vim_test.js @@ -323,6 +323,8 @@ 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('keepHPos', ['5', 'j', 'j', '7', 'k'], makeCursor(8, 12), makeCursor(12, 12)); +testMotion('keepHPosEol', ['$', '2', 'j'], makeCursor(2, 18)); 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)); testMotion('w_repeat', ['2', 'w'], word2.start); From 908d8399b893ec6dc89cb33abea7e3f8671d9ef8 Mon Sep 17 00:00:00 2001 From: nightwing Date: Mon, 6 May 2019 19:58:22 +0400 Subject: [PATCH 1576/1790] [vim] cleanup old fatCursorMarks after C-v c Esc --- keymap/vim.js | 21 ++++++++++----------- 1 file changed, 10 insertions(+), 11 deletions(-) diff --git a/keymap/vim.js b/keymap/vim.js index 0416c747..e51541ee 100644 --- a/keymap/vim.js +++ b/keymap/vim.js @@ -286,7 +286,8 @@ enterVimMode(cm); } - function fatCursorMarks(cm) { + function updateFatCursorMark(cm) { + clearFatCursorMark(cm); var ranges = cm.listSelections(), result = [] for (var i = 0; i < ranges.length; i++) { var range = ranges[i] @@ -302,25 +303,23 @@ } } } - return result + cm.state.fatCursorMarks = result; } - function updateFatCursorMark(cm) { - var marks = cm.state.fatCursorMarks - if (marks) for (var i = 0; i < marks.length; i++) marks[i].clear() - cm.state.fatCursorMarks = fatCursorMarks(cm) + function clearFatCursorMark(cm) { + var marks = cm.state.fatCursorMarks; + if (marks) for (var i = 0; i < marks.length; i++) marks[i].clear(); + cm.state.fatCursorMarks = null; } function enableFatCursorMark(cm) { - cm.state.fatCursorMarks = fatCursorMarks(cm) + updateFatCursorMark(cm) cm.on("cursorActivity", updateFatCursorMark) } function disableFatCursorMark(cm) { - var marks = cm.state.fatCursorMarks - if (marks) for (var i = 0; i < marks.length; i++) marks[i].clear() - cm.state.fatCursorMarks = null - cm.off("cursorActivity", updateFatCursorMark) + clearFatCursorMark(cm); + cm.off("cursorActivity", updateFatCursorMark); } // Deprecated, simply setting the keymap works again. From 8a32a4db1b096c17a8382917e920ea7941d95784 Mon Sep 17 00:00:00 2001 From: Evan Minsk Date: Mon, 6 May 2019 23:12:16 -0700 Subject: [PATCH 1577/1790] [vim] fix @@ to rerun last run macro (#5868) `vimGlobalState.macroModeState.latestRegister` was only ever updated when defining a macro. This change updates it when running a macro too so that `@@` actually repeats the last macro as it does in vim --- keymap/vim.js | 2 ++ test/vim_test.js | 9 +++++++++ 2 files changed, 11 insertions(+) diff --git a/keymap/vim.js b/keymap/vim.js index e51541ee..8a038805 100644 --- a/keymap/vim.js +++ b/keymap/vim.js @@ -2316,6 +2316,8 @@ var macroModeState = vimGlobalState.macroModeState; if (registerName == '@') { registerName = macroModeState.latestRegister; + } else { + macroModeState.latestRegister = registerName; } while(repeat--){ executeMacroRegister(cm, vim, macroModeState, registerName); diff --git a/test/vim_test.js b/test/vim_test.js index 401f5dca..727daaed 100644 --- a/test/vim_test.js +++ b/test/vim_test.js @@ -2672,6 +2672,15 @@ testVim('macro_last_ex_command_register', function (cm, vim, helpers) { eq('bbbaa', cm.getValue()); helpers.assertCursorAt(0, 2); }, { value: 'aaaaa'}); +testVim('macro_last_run_macro', function (cm, vim, helpers) { + cm.setCursor(0, 0); + helpers.doKeys('q', 'a', 'C', 'a', '', 'q'); + helpers.doKeys('q', 'b', 'C', 'b', '', 'q'); + helpers.doKeys('@', 'a'); + helpers.doKeys('d', 'd'); + helpers.doKeys('@', '@'); + eq('a', cm.getValue()); +}, { value: ''}); testVim('macro_parens', function(cm, vim, helpers) { cm.setCursor(0, 0); helpers.doKeys('q', 'z', 'i'); From 83c83d19360b0be82efe1fbe360808528fa16e25 Mon Sep 17 00:00:00 2001 From: Marijn Haverbeke Date: Tue, 7 May 2019 08:14:58 +0200 Subject: [PATCH 1578/1790] [midnight theme] Fix class name for cm-matchhighlight Issue #5866 --- theme/midnight.css | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/theme/midnight.css b/theme/midnight.css index 17ed39c8..1de827eb 100644 --- a/theme/midnight.css +++ b/theme/midnight.css @@ -1,8 +1,8 @@ /* Based on the theme at http://bonsaiden.github.com/JavaScript-Garden */ /**/ -.cm-s-midnight span.CodeMirror-matchhighlight { background: #494949; } -.cm-s-midnight.CodeMirror-focused span.CodeMirror-matchhighlight { background: #314D67 !important; } +.cm-s-midnight span.cm-matchhighlight { background: #494949; } +.cm-s-midnight.CodeMirror-focused span.cm-matchhighlight { background: #314D67 !important; } /**/ .cm-s-midnight .CodeMirror-activeline-background { background: #253540; } From ff40991e6bad642233f7e9941cc63c697dde1e46 Mon Sep 17 00:00:00 2001 From: Marijn Haverbeke Date: Wed, 8 May 2019 10:57:13 +0200 Subject: [PATCH 1579/1790] [ruby mode] Require dash or tilde at start of heredoc strings Closes #5871 --- 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 5d928866..b84dde29 100644 --- a/mode/ruby/ruby.js +++ b/mode/ruby/ruby.js @@ -63,7 +63,7 @@ CodeMirror.defineMode("ruby", function(config) { } else if (ch == "#") { stream.skipToEnd(); return "comment"; - } else if (ch == "<" && (m = stream.match(/^<(-)?[\`\"\']?([a-zA-Z_?]\w*)[\`\"\']?(?:;|$)/))) { + } else if (ch == "<" && (m = stream.match(/^<([-~])[\`\"\']?([a-zA-Z_?]\w*)[\`\"\']?(?:;|$)/))) { return chain(readHereDoc(m[2], m[1]), stream, state); } else if (ch == "0") { if (stream.eat("x")) stream.eatWhile(/[\da-fA-F]/); From 42f919f09c1865fe5313a6bd8a1ed928630f31bf Mon Sep 17 00:00:00 2001 From: Sasha Varlamov Date: Tue, 7 May 2019 10:45:11 -0700 Subject: [PATCH 1580/1790] Add EXLskills Live Coding Interviews as real-world use case --- doc/realworld.html | 1 + 1 file changed, 1 insertion(+) diff --git a/doc/realworld.html b/doc/realworld.html index b9fad1c6..ae7ff4da 100644 --- a/doc/realworld.html +++ b/doc/realworld.html @@ -76,6 +76,7 @@

      CodeMirror real-world uses

    • Eloquent JavaScript (book)
    • Emmet (fast XML editing)
    • Espruino Web IDE (Chrome App for writing code on Espruino devices)
    • +
    • EXLskills Live Interivews
    • Fastfig (online computation/math tool)
    • Farabi (modern Perl IDE)
    • FathomJS integration (slides with editors, again)
    • From dab6f676107c10ba8d16c654a42f66cae3f27db1 Mon Sep 17 00:00:00 2001 From: Marijn Haverbeke Date: Wed, 8 May 2019 10:59:18 +0200 Subject: [PATCH 1581/1790] [midnight theme] Drop custom matchhighlight styles Issue #5866 --- theme/midnight.css | 4 ---- 1 file changed, 4 deletions(-) diff --git a/theme/midnight.css b/theme/midnight.css index 1de827eb..fc26474a 100644 --- a/theme/midnight.css +++ b/theme/midnight.css @@ -1,9 +1,5 @@ /* Based on the theme at http://bonsaiden.github.com/JavaScript-Garden */ -/**/ -.cm-s-midnight span.cm-matchhighlight { background: #494949; } -.cm-s-midnight.CodeMirror-focused span.cm-matchhighlight { background: #314D67 !important; } - /**/ .cm-s-midnight .CodeMirror-activeline-background { background: #253540; } From 09eeedcf1b9a11e2f88d4dd7fc839014cca660ff Mon Sep 17 00:00:00 2001 From: nightwing Date: Thu, 9 May 2019 03:20:18 +0400 Subject: [PATCH 1582/1790] [vim] remove unnecessary uses of replace from vim test --- test/vim_test.js | 81 ++++++++++++++++++++---------------------------- 1 file changed, 34 insertions(+), 47 deletions(-) diff --git a/test/vim_test.js b/test/vim_test.js index 727daaed..15c2c81e 100644 --- a/test/vim_test.js +++ b/test/vim_test.js @@ -1056,21 +1056,18 @@ function fillArray(val, times) { testVim('c_visual_block', function(cm, vim, helpers) { cm.setCursor(0, 1); helpers.doKeys('', '2', 'j', 'l', 'l', 'l', 'c'); - var replacement = fillArray('hello', 3); - cm.replaceSelections(replacement); + helpers.doKeys('hello'); eq('1hello\n5hello\nahellofg', cm.getValue()); helpers.doKeys(''); cm.setCursor(2, 3); helpers.doKeys('', '2', 'k', 'h', 'C'); - replacement = fillArray('world', 3); - cm.replaceSelections(replacement); + helpers.doKeys('world'); eq('1hworld\n5hworld\nahworld', cm.getValue()); }, {value: '1234\n5678\nabcdefg'}); testVim('c_visual_block_replay', function(cm, vim, helpers) { cm.setCursor(0, 1); helpers.doKeys('', '2', 'j', 'l', 'c'); - var replacement = fillArray('fo', 3); - cm.replaceSelections(replacement); + helpers.doKeys('fo'); eq('1fo4\n5fo8\nafodefg', cm.getValue()); helpers.doKeys(''); cm.setCursor(0, 0); @@ -1080,8 +1077,7 @@ testVim('c_visual_block_replay', function(cm, vim, helpers) { testVim('I_visual_block_replay', function(cm, vim, helpers) { cm.setCursor(0, 2); helpers.doKeys('', '2', 'j', 'l', 'I'); - var replacement = fillArray('+-', 3); - cm.replaceSelections(replacement); + helpers.doKeys('+-') eq('12+-34\n56+-78\nab+-cdefg\nxyz', cm.getValue()); helpers.doKeys(''); // ensure that repeat location doesn't depend on last selection @@ -1114,14 +1110,12 @@ testVim('D_visual_block', function(cm, vim, helpers) { testVim('s_visual_block', function(cm, vim, helpers) { cm.setCursor(0, 1); helpers.doKeys('', '2', 'j', 'l', 'l', 'l', 's'); - var replacement = fillArray('hello{', 3); - cm.replaceSelections(replacement); + helpers.doKeys('hello{'); eq('1hello{\n5hello{\nahello{fg\n', cm.getValue()); helpers.doKeys(''); cm.setCursor(2, 3); helpers.doKeys('', '1', 'k', 'h', 'S'); - replacement = fillArray('world', 1); - cm.replaceSelections(replacement); + helpers.doKeys('world'); eq('1hello{\n world\n', cm.getValue()); }, {value: '1234\n5678\nabcdefg\n'}); @@ -1511,7 +1505,7 @@ testVim('i', function(cm, vim, helpers) { }); testVim('i_repeat', function(cm, vim, helpers) { helpers.doKeys('3', 'i'); - cm.replaceRange('test', cm.getCursor()); + helpers.doKeys('test') helpers.doKeys(''); eq('testtesttest', cm.getValue()); helpers.assertCursorAt(0, 11); @@ -1519,7 +1513,7 @@ testVim('i_repeat', function(cm, vim, helpers) { testVim('i_repeat_delete', function(cm, vim, helpers) { cm.setCursor(0, 4); helpers.doKeys('2', 'i'); - cm.replaceRange('z', cm.getCursor()); + helpers.doKeys('z') helpers.doInsertModeKeys('Backspace', 'Backspace'); helpers.doKeys(''); eq('abe', cm.getValue()); @@ -1584,9 +1578,7 @@ testVim('A', function(cm, vim, helpers) { testVim('A_visual_block', function(cm, vim, helpers) { cm.setCursor(0, 1); helpers.doKeys('', '2', 'j', 'l', 'l', 'A'); - var replacement = new Array(cm.listSelections().length+1).join('hello ').split(' '); - replacement.pop(); - cm.replaceSelections(replacement); + helpers.doKeys('hello'); eq('testhello\nmehello\npleahellose', cm.getValue()); helpers.doKeys(''); cm.setCursor(0, 0); @@ -1603,7 +1595,7 @@ testVim('I', function(cm, vim, helpers) { testVim('I_repeat', function(cm, vim, helpers) { cm.setCursor(0, 1); helpers.doKeys('3', 'I'); - cm.replaceRange('test', cm.getCursor()); + helpers.doKeys('test') helpers.doKeys(''); eq('testtesttestblah', cm.getValue()); helpers.assertCursorAt(0, 11); @@ -1611,9 +1603,7 @@ testVim('I_repeat', function(cm, vim, helpers) { testVim('I_visual_block', function(cm, vim, helpers) { cm.setCursor(0, 0); helpers.doKeys('', '2', 'j', 'l', 'l', 'I'); - var replacement = new Array(cm.listSelections().length+1).join('hello ').split(' '); - replacement.pop(); - cm.replaceSelections(replacement); + helpers.doKeys('hello'); eq('hellotest\nhellome\nhelloplease', cm.getValue()); }, {value: 'test\nme\nplease'}); testVim('o', function(cm, vim, helpers) { @@ -1626,7 +1616,7 @@ testVim('o', function(cm, vim, helpers) { testVim('o_repeat', function(cm, vim, helpers) { cm.setCursor(0, 0); helpers.doKeys('3', 'o'); - cm.replaceRange('test', cm.getCursor()); + helpers.doKeys('test') helpers.doKeys(''); eq('\ntest\ntest\ntest', cm.getValue()); helpers.assertCursorAt(3, 3); @@ -1750,8 +1740,8 @@ testVim('r_visual_block', function(cm, vim, helpers) { eq('1 l\n5 l\nalllefg', cm.getValue()); cm.setCursor(2, 0); helpers.doKeys('o'); + helpers.doKeys('\t\t') helpers.doKeys(''); - 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'}); @@ -2591,7 +2581,7 @@ testVim('g#', function(cm, vim, helpers) { testVim('macro_insert', function(cm, vim, helpers) { cm.setCursor(0, 0); helpers.doKeys('q', 'a', '0', 'i'); - cm.replaceRange('foo', cm.getCursor()); + helpers.doKeys('foo') helpers.doKeys(''); helpers.doKeys('q', '@', 'a'); eq('foofoo', cm.getValue()); @@ -2599,14 +2589,14 @@ testVim('macro_insert', function(cm, vim, helpers) { testVim('macro_insert_repeat', function(cm, vim, helpers) { cm.setCursor(0, 0); helpers.doKeys('q', 'a', '$', 'a'); - cm.replaceRange('larry.', cm.getCursor()); + helpers.doKeys('larry.') helpers.doKeys(''); helpers.doKeys('a'); - cm.replaceRange('curly.', cm.getCursor()); + helpers.doKeys('curly.') helpers.doKeys(''); helpers.doKeys('q'); helpers.doKeys('a'); - cm.replaceRange('moe.', cm.getCursor()); + helpers.doKeys('moe.') helpers.doKeys(''); helpers.doKeys('@', 'a'); // At this point, the most recent edit should be the 2nd insert change @@ -2684,10 +2674,10 @@ testVim('macro_last_run_macro', function (cm, vim, helpers) { testVim('macro_parens', function(cm, vim, helpers) { cm.setCursor(0, 0); helpers.doKeys('q', 'z', 'i'); - cm.replaceRange('(', cm.getCursor()); + helpers.doKeys('(') helpers.doKeys(''); helpers.doKeys('e', 'a'); - cm.replaceRange(')', cm.getCursor()); + helpers.doKeys(')') helpers.doKeys(''); helpers.doKeys('q'); helpers.doKeys('w', '@', 'z'); @@ -2697,13 +2687,13 @@ testVim('macro_parens', function(cm, vim, helpers) { testVim('macro_overwrite', function(cm, vim, helpers) { cm.setCursor(0, 0); helpers.doKeys('q', 'z', '0', 'i'); - cm.replaceRange('I ', cm.getCursor()); + helpers.doKeys('I ') helpers.doKeys(''); helpers.doKeys('q'); helpers.doKeys('e'); // Now replace the macro with something else. helpers.doKeys('q', 'z', 'a'); - cm.replaceRange('.', cm.getCursor()); + helpers.doKeys('.') helpers.doKeys(''); helpers.doKeys('q'); helpers.doKeys('e', '@', 'z'); @@ -2802,11 +2792,11 @@ testVim('yank_append_word_to_line_register', function(cm, vim, helpers) { testVim('macro_register', function(cm, vim, helpers) { cm.setCursor(0, 0); helpers.doKeys('q', 'a', 'i'); - cm.replaceRange('gangnam', cm.getCursor()); + helpers.doKeys('gangnam') helpers.doKeys(''); helpers.doKeys('q'); helpers.doKeys('q', 'b', 'o'); - cm.replaceRange('style', cm.getCursor()); + helpers.doKeys('style') helpers.doKeys(''); helpers.doKeys('q'); cm.openDialog = helpers.fakeOpenDialog('registers'); @@ -2819,7 +2809,7 @@ testVim('macro_register', function(cm, vim, helpers) { testVim('._register', function(cm,vim,helpers) { cm.setCursor(0,0); helpers.doKeys('i'); - cm.replaceRange('foo',cm.getCursor()); + helpers.doKeys('foo') helpers.doKeys(''); cm.openDialog = helpers.fakeOpenDialog('registers'); cm.openNotification = helpers.fakeOpenNotification(function(text) { @@ -3008,13 +2998,13 @@ testVim('._repeat', function(cm, vim, helpers) { }, { value: '1 2 3 4 5 6'}); testVim('._insert', function(cm, vim, helpers) { helpers.doKeys('i'); - cm.replaceRange('test', cm.getCursor()); + helpers.doKeys('test') helpers.doKeys(''); helpers.doKeys('.'); eq('testestt', cm.getValue()); helpers.assertCursorAt(0, 6); helpers.doKeys('O'); - cm.replaceRange('xyz', cm.getCursor()); + helpers.doKeys('xyz') helpers.doInsertModeKeys('Backspace'); helpers.doInsertModeKeys('Down'); helpers.doKeys(''); @@ -3024,7 +3014,7 @@ testVim('._insert', function(cm, vim, helpers) { }, { value: ''}); testVim('._insert_repeat', function(cm, vim, helpers) { helpers.doKeys('i'); - cm.replaceRange('test', cm.getCursor()); + helpers.doKeys('test') cm.setCursor(0, 4); helpers.doKeys(''); helpers.doKeys('2', '.'); @@ -3033,7 +3023,7 @@ testVim('._insert_repeat', function(cm, vim, helpers) { }, { value: ''}); testVim('._repeat_insert', function(cm, vim, helpers) { helpers.doKeys('3', 'i'); - cm.replaceRange('te', cm.getCursor()); + helpers.doKeys('te') cm.setCursor(0, 2); helpers.doKeys(''); helpers.doKeys('.'); @@ -3042,7 +3032,7 @@ testVim('._repeat_insert', function(cm, vim, helpers) { }, { value: ''}); testVim('._insert_o', function(cm, vim, helpers) { helpers.doKeys('o'); - cm.replaceRange('z', cm.getCursor()); + helpers.doKeys('z') cm.setCursor(1, 1); helpers.doKeys(''); helpers.doKeys('.'); @@ -3051,7 +3041,7 @@ testVim('._insert_o', function(cm, vim, helpers) { }, { value: ''}); testVim('._insert_o_repeat', function(cm, vim, helpers) { helpers.doKeys('o'); - cm.replaceRange('z', cm.getCursor()); + helpers.doKeys('z') helpers.doKeys(''); cm.setCursor(1, 0); helpers.doKeys('2', '.'); @@ -3060,7 +3050,7 @@ testVim('._insert_o_repeat', function(cm, vim, helpers) { }, { value: ''}); testVim('._insert_o_indent', function(cm, vim, helpers) { helpers.doKeys('o'); - cm.replaceRange('z', cm.getCursor()); + helpers.doKeys('z') helpers.doKeys(''); cm.setCursor(1, 2); helpers.doKeys('.'); @@ -3069,7 +3059,7 @@ testVim('._insert_o_indent', function(cm, vim, helpers) { }, { value: '{'}); testVim('._insert_cw', function(cm, vim, helpers) { helpers.doKeys('c', 'w'); - cm.replaceRange('test', cm.getCursor()); + helpers.doKeys('test') helpers.doKeys(''); cm.setCursor(0, 3); helpers.doKeys('2', 'l'); @@ -3081,7 +3071,7 @@ testVim('._insert_cw_repeat', function(cm, vim, helpers) { // For some reason, repeat cw in desktop VIM will does not repeat insert mode // changes. Will conform to that behavior. helpers.doKeys('c', 'w'); - cm.replaceRange('test', cm.getCursor()); + helpers.doKeys('test'); helpers.doKeys(''); cm.setCursor(0, 4); helpers.doKeys('l'); @@ -4350,10 +4340,7 @@ testVim('ex_imap', function(cm, vim, helpers) { cm.setCursor(0, 1); CodeMirror.Vim.map('jj', '', 'insert'); helpers.doKeys('', '2', 'j', 'l', 'c'); - var replacement = fillArray('f', 3); - cm.replaceSelections(replacement); - var replacement = fillArray('o', 3); - cm.replaceSelections(replacement); + helpers.doKeys('f', 'o'); eq('1fo4\n5fo8\nafodefg', cm.getValue()); helpers.doKeys('j', 'j'); cm.setCursor(0, 0); From 6974e1a4717b261d02e7db5f55f2b736a8112b06 Mon Sep 17 00:00:00 2001 From: nightwing Date: Thu, 9 May 2019 03:22:10 +0400 Subject: [PATCH 1583/1790] [vim] fix broken option tests --- test/vim_test.js | 65 ++++++++++++++++++++++++------------------------ 1 file changed, 32 insertions(+), 33 deletions(-) diff --git a/test/vim_test.js b/test/vim_test.js index 15c2c81e..9069bb9e 100644 --- a/test/vim_test.js +++ b/test/vim_test.js @@ -105,6 +105,15 @@ function forEach(arr, func) { } } +function expectFail(fn) { + try { + fn(); + } catch(expected) { + return; + }; + throw new Error("Expected to throw an error"); +} + function testVim(name, run, opts, expectedFail) { var vimOpts = { lineNumbers: true, @@ -4068,11 +4077,9 @@ testVim('set_boolean', function(cm, vim, helpers) { CodeMirror.Vim.defineOption('testoption', true, 'boolean'); // Test default value is set. is(CodeMirror.Vim.getOption('testoption')); - try { - // Test fail to set to non-boolean - CodeMirror.Vim.setOption('testoption', '5'); - fail(); - } catch (expected) {} + // Test fail to set to non-boolean + var result = CodeMirror.Vim.setOption('testoption', '5'); + is(result instanceof Error); // Test setOption CodeMirror.Vim.setOption('testoption', false); is(!CodeMirror.Vim.getOption('testoption')); @@ -4081,11 +4088,10 @@ testVim('ex_set_boolean', function(cm, vim, helpers) { CodeMirror.Vim.defineOption('testoption', true, 'boolean'); // Test default value is set. is(CodeMirror.Vim.getOption('testoption')); - try { - // Test fail to set to non-boolean - helpers.doEx('set testoption=22'); - fail(); - } catch (expected) {} + is(!cm.state.currentNotificationClose); + // Test fail to set to non-boolean + helpers.doEx('set testoption=22'); + is(cm.state.currentNotificationClose); // Test setOption helpers.doEx('set notestoption'); is(!CodeMirror.Vim.getOption('testoption')); @@ -4094,16 +4100,12 @@ testVim('set_string', function(cm, vim, helpers) { CodeMirror.Vim.defineOption('testoption', 'a', 'string'); // Test default value is set. eq('a', CodeMirror.Vim.getOption('testoption')); - try { - // Test fail to set non-string. - CodeMirror.Vim.setOption('testoption', true); - fail(); - } catch (expected) {} - try { - // Test fail to set 'notestoption' - CodeMirror.Vim.setOption('notestoption', 'b'); - fail(); - } catch (expected) {} + // Test no fail to set non-string. + var result = CodeMirror.Vim.setOption('testoption', true); + is(!result); + // Test fail to set 'notestoption' + result = CodeMirror.Vim.setOption('notestoption', 'b'); + is(result instanceof Error); // Test setOption CodeMirror.Vim.setOption('testoption', 'c'); eq('c', CodeMirror.Vim.getOption('testoption')); @@ -4112,11 +4114,10 @@ testVim('ex_set_string', function(cm, vim, helpers) { CodeMirror.Vim.defineOption('testopt', 'a', 'string'); // Test default value is set. eq('a', CodeMirror.Vim.getOption('testopt')); - try { - // Test fail to set 'notestopt' - helpers.doEx('set notestopt=b'); - fail(); - } catch (expected) {} + // Test fail to set 'notestopt' + is(!cm.state.currentNotificationClose); + helpers.doEx('set notestopt=b'); + is(cm.state.currentNotificationClose); // Test setOption helpers.doEx('set testopt=c') eq('c', CodeMirror.Vim.getOption('testopt')); @@ -4162,11 +4163,10 @@ testVim('ex_set_callback', function(cm, vim, helpers) { CodeMirror.Vim.defineOption('testopt', 'a', 'string', cb); // Test default value is set. eq('a', CodeMirror.Vim.getOption('testopt')); - try { - // Test fail to set 'notestopt' - helpers.doEx('set notestopt=b'); - fail(); - } catch (expected) {} + // Test fail to set 'notestopt' + is(!cm.state.currentNotificationClose); + helpers.doEx('set notestopt=b'); + is(cm.state.currentNotificationClose); // Test setOption (Identical to the string tests, but via callback instead) helpers.doEx('set testopt=c') eq('c', CodeMirror.Vim.getOption('testopt', cm)); //local || global @@ -4256,10 +4256,9 @@ testVim('ex_unmap_key2key', function(cm, vim, helpers) { CodeMirror.Vim.mapclear(); }, { value: 'abc' }); testVim('ex_unmap_key2key_does_not_remove_default', function(cm, vim, helpers) { - try { + expectFail(function() { helpers.doEx('unmap a'); - fail(); - } catch (expected) {} + }); helpers.doKeys('a'); eq('vim-insert', cm.getOption('keyMap')); CodeMirror.Vim.mapclear(); From 1b3c322c434a61bd326d2b25722597138e649521 Mon Sep 17 00:00:00 2001 From: nightwing Date: Thu, 9 May 2019 03:32:49 +0400 Subject: [PATCH 1584/1790] [vim] fix fat cursor staying after O --- keymap/vim.js | 6 +++++- 1 file changed, 5 insertions(+), 1 deletion(-) diff --git a/keymap/vim.js b/keymap/vim.js index 8a038805..988c1681 100644 --- a/keymap/vim.js +++ b/keymap/vim.js @@ -287,6 +287,7 @@ } function updateFatCursorMark(cm) { + if (!cm.state.fatCursorMarks) return; clearFatCursorMark(cm); var ranges = cm.listSelections(), result = [] for (var i = 0; i < ranges.length; i++) { @@ -309,10 +310,10 @@ function clearFatCursorMark(cm) { var marks = cm.state.fatCursorMarks; if (marks) for (var i = 0; i < marks.length; i++) marks[i].clear(); - cm.state.fatCursorMarks = null; } function enableFatCursorMark(cm) { + cm.state.fatCursorMarks = []; updateFatCursorMark(cm) cm.on("cursorActivity", updateFatCursorMark) } @@ -320,6 +321,9 @@ function disableFatCursorMark(cm) { clearFatCursorMark(cm); cm.off("cursorActivity", updateFatCursorMark); + // explicitly set fatCursorMarks to null because event listener above + // can be invoke after removing it, if off is called from operation + cm.state.fatCursorMarks = null; } // Deprecated, simply setting the keymap works again. From a86e85864c7d8da4cc4d103183de6d5c9a2d8707 Mon Sep 17 00:00:00 2001 From: nightwing Date: Tue, 14 May 2019 14:18:39 +0400 Subject: [PATCH 1585/1790] [vim] fix blockwise yank --- keymap/vim.js | 18 +++++++++++------- test/vim_test.js | 15 +++++++++++++++ 2 files changed, 26 insertions(+), 7 deletions(-) diff --git a/keymap/vim.js b/keymap/vim.js index 988c1681..f8ffb88f 100644 --- a/keymap/vim.js +++ b/keymap/vim.js @@ -2570,7 +2570,17 @@ } var linewise = register.linewise; var blockwise = register.blockwise; - if (linewise) { + if (blockwise) { + text = text.split('\n'); + if (linewise) { + text.pop(); + } + for (var i = 0; i < text.length; i++) { + text[i] = (text[i] == '') ? ' ' : text[i]; + } + cur.ch += actionArgs.after ? 1 : 0; + cur.ch = Math.min(lineLength(cm, cur.line), cur.ch); + } else if (linewise) { if(vim.visualMode) { text = vim.visualLine ? text.slice(0, -1) : '\n' + text.slice(0, text.length - 1) + '\n'; } else if (actionArgs.after) { @@ -2582,12 +2592,6 @@ cur.ch = 0; } } else { - if (blockwise) { - text = text.split('\n'); - for (var i = 0; i < text.length; i++) { - text[i] = (text[i] == '') ? ' ' : text[i]; - } - } cur.ch += actionArgs.after ? 1 : 0; } var curPosFinal; diff --git a/test/vim_test.js b/test/vim_test.js index 9069bb9e..e74b2f2c 100644 --- a/test/vim_test.js +++ b/test/vim_test.js @@ -1428,6 +1428,21 @@ testVim('Y', function(cm, vim, helpers) { is(register.linewise); helpers.assertCursorAt(0, 3); }, { value: ' word1\nword2\n word3' }); +testVim('Yy_blockwise', function(cm, vim, helpers) { + helpers.doKeys('', 'j', '2', 'l', 'Y'); + helpers.doKeys('G', 'p', 'g', 'g'); + helpers.doKeys('', 'j', '2', 'l', 'y'); + helpers.assertCursorAt(0, 0); + helpers.doKeys('$', 'p'); + eq('123456123\n123456123\n123456\n123456', cm.getValue()); + var register = helpers.getRegisterController().getRegister(); + eq('123\n123', register.toString()); + is(register.blockwise); + helpers.assertCursorAt(0, 6); + helpers.doKeys('$', 'j', 'p'); + helpers.doKeys('$', 'j', 'P'); + eq("123456123\n123456123123\n123456 121233\n123456 123", cm.getValue()); +}, { value: '123456\n123456\n' }); testVim('~', function(cm, vim, helpers) { helpers.doKeys('3', '~'); eq('ABCdefg', cm.getValue()); From 17f48fb56b8c444b8943d9e586110a8a037079bd Mon Sep 17 00:00:00 2001 From: Marijn Haverbeke Date: Wed, 15 May 2019 14:11:09 +0200 Subject: [PATCH 1586/1790] [ruby mode] Fix matching of closing brackets during indentation Closes #5881 --- mode/ruby/ruby.js | 9 +++++---- 1 file changed, 5 insertions(+), 4 deletions(-) diff --git a/mode/ruby/ruby.js b/mode/ruby/ruby.js index b84dde29..dd0e603e 100644 --- a/mode/ruby/ruby.js +++ b/mode/ruby/ruby.js @@ -28,7 +28,8 @@ CodeMirror.defineMode("ruby", function(config) { var indentWords = wordObj(["def", "class", "case", "for", "while", "until", "module", "then", "catch", "loop", "proc", "begin"]); var dedentWords = wordObj(["end", "until"]); - var matching = {"[": "]", "{": "}", "(": ")"}; + var opening = {"[": "]", "{": "}", "(": ")"}; + var closing = {"]": "[", "}": "{", ")": "("}; var curPunc; function chain(newtok, stream, state) { @@ -58,7 +59,7 @@ CodeMirror.defineMode("ruby", function(config) { else if (stream.eat(/[wxq]/)) { style = "string"; embed = false; } var delim = stream.eat(/[^\w\s=]/); if (!delim) return "operator"; - if (matching.propertyIsEnumerable(delim)) delim = matching[delim]; + if (opening.propertyIsEnumerable(delim)) delim = opening[delim]; return chain(readQuoted(delim, style, embed, true), stream, state); } else if (ch == "#") { stream.skipToEnd(); @@ -280,9 +281,9 @@ CodeMirror.defineMode("ruby", function(config) { if (state.tokenize[state.tokenize.length-1] != tokenBase) return CodeMirror.Pass; var firstChar = textAfter && textAfter.charAt(0); var ct = state.context; - var closing = ct.type == matching[firstChar] || + var closed = ct.type == closing[firstChar] || ct.type == "keyword" && /^(?:end|until|else|elsif|when|rescue)\b/.test(textAfter); - return ct.indented + (closing ? 0 : config.indentUnit) + + return ct.indented + (closed ? 0 : config.indentUnit) + (state.continuedLine ? config.indentUnit : 0); }, From 3094f2c86a758eb5ef6570cfaa31ca44a6ff6243 Mon Sep 17 00:00:00 2001 From: Diego Fernandez Date: Wed, 15 May 2019 17:27:37 -0600 Subject: [PATCH 1587/1790] [emacs] Add "redo" keybinding in Emacs keymap Since "redo" is generally assigned to Ctrl+Y, we lose this binding when using the Emacs keymap. The Emacs default for "redo" is a bit complicated, but since we're using Ctrl-Z for "undo", it makes sense to have Ctrl+Shift-Z for "redo". --- keymap/emacs.js | 1 + 1 file changed, 1 insertion(+) diff --git a/keymap/emacs.js b/keymap/emacs.js index d96a6fbe..fe62d44f 100644 --- a/keymap/emacs.js +++ b/keymap/emacs.js @@ -369,6 +369,7 @@ "Ctrl-/": repeated("undo"), "Shift-Ctrl--": repeated("undo"), "Ctrl-Z": repeated("undo"), "Cmd-Z": repeated("undo"), + "Shift-Ctrl-Z": "redo", "Shift-Alt-,": "goDocStart", "Shift-Alt-.": "goDocEnd", "Ctrl-S": "findPersistentNext", "Ctrl-R": "findPersistentPrev", "Ctrl-G": quit, "Shift-Alt-5": "replace", "Alt-/": "autocomplete", From a7ce80ffc2f1a04b61782473170de776eb68728c Mon Sep 17 00:00:00 2001 From: Marijn Haverbeke Date: Tue, 21 May 2019 10:15:18 +0200 Subject: [PATCH 1588/1790] Mark version 5.47.0 --- AUTHORS | 6 ++++++ CHANGELOG.md | 14 ++++++++++++++ doc/manual.html | 2 +- doc/releases.html | 9 +++++++++ index.html | 2 +- package.json | 2 +- src/edit/main.js | 2 +- 7 files changed, 33 insertions(+), 4 deletions(-) diff --git a/AUTHORS b/AUTHORS index 0e4c895d..f61c7c5b 100644 --- a/AUTHORS +++ b/AUTHORS @@ -197,9 +197,11 @@ David Vázquez David Whittington deebugger Deep Thought +Denis Ovsienko Devin Abbott Devon Carew Dick Choi +Diego Fernandez dignifiedquire Dimage Sapelkin dmaclach @@ -227,6 +229,7 @@ Eric Bogard Erik Demaine Erik Welander eustas +Evan Minsk Fabien Dubosson Fabien O'Carroll Fabio Zendhi Nagao @@ -505,6 +508,7 @@ Maximilian Hils Maxim Kraev Max Kirsch Max Schaefer +Max Wu Max Xiantu mbarkhau McBrainy @@ -580,6 +584,7 @@ Nisarg Jhaveri nlwillia noragrossman Norman Rzepka +Nouzbe Oleksandr Yakovenko opl- Oreoluwa Onatemowo @@ -667,6 +672,7 @@ Sander Verweij santec Sarah McAlear and Wenlin Zhang Sascha Peilicke +Sasha Varlamov satamas satchmorun sathyamoorthi diff --git a/CHANGELOG.md b/CHANGELOG.md index c6e6068f..d8c523c7 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -1,3 +1,17 @@ +## 5.47.0 (2019-05-21) + +### Bug fixes + +[python mode](https://codemirror.net/mode/python/): Properly handle `...` syntax. + +[ruby mode](https://codemirror.net/mode/ruby): Fix indenting before closing brackets. + +[vim bindings](https://codemirror.net/demo/vim.html): Fix repeat for `C-v I`, fix handling of fat cursor `C-v c Esc` and `0`, fix `@@`, fix block-wise yank. + +### New features + +[vim bindings](https://codemirror.net/demo/vim.html): Add support for `` ` `` text object. + ## 5.46.0 (2019-04-22) ### Bug fixes diff --git a/doc/manual.html b/doc/manual.html index 37fef13f..abcb1e29 100644 --- a/doc/manual.html +++ b/doc/manual.html @@ -69,7 +69,7 @@

      User manual and reference guide - version 5.46.1 + version 5.47.0

      CodeMirror is a code-editor component that can be embedded in diff --git a/doc/releases.html b/doc/releases.html index 6ecaef0e..0066778e 100644 --- a/doc/releases.html +++ b/doc/releases.html @@ -30,6 +30,15 @@

      Release notes and version history

      Version 5.x

      +

      21-05-2019: Version 5.47.0:

      + +
        +
      • python mode: Properly handle ... syntax.
      • +
      • ruby mode: Fix indenting before closing brackets.
      • +
      • vim bindings: Fix repeat for C-v I, fix handling of fat cursor C-v c Esc and 0, fix @@, fix block-wise yank.
      • +
      • vim bindings: Add support for ` text object.
      • +
      +

      22-04-2019: Version 5.46.0:

        diff --git a/index.html b/index.html index 4e4f1640..2c682638 100644 --- a/index.html +++ b/index.html @@ -99,7 +99,7 @@

        This is CodeMirror

      - Get the current version: 5.46.0.
      + Get the current version: 5.47.0.
      You can see the code,
      read the release notes,
      or study the user manual. diff --git a/package.json b/package.json index 69f5d346..86aa4861 100644 --- a/package.json +++ b/package.json @@ -1,6 +1,6 @@ { "name": "codemirror", - "version": "5.46.1", + "version": "5.47.0", "main": "lib/codemirror.js", "style": "lib/codemirror.css", "author": { diff --git a/src/edit/main.js b/src/edit/main.js index be98c682..615372b1 100644 --- a/src/edit/main.js +++ b/src/edit/main.js @@ -66,4 +66,4 @@ import { addLegacyProps } from "./legacy.js" addLegacyProps(CodeMirror) -CodeMirror.version = "5.46.1" +CodeMirror.version = "5.47.0" From 5c12351a3a888cf7ea03a268e3cc7f9b76e5627a Mon Sep 17 00:00:00 2001 From: Marijn Haverbeke Date: Tue, 21 May 2019 10:21:12 +0200 Subject: [PATCH 1589/1790] Bump version number post-5.47.0 --- doc/manual.html | 2 +- package.json | 2 +- src/edit/main.js | 2 +- 3 files changed, 3 insertions(+), 3 deletions(-) diff --git a/doc/manual.html b/doc/manual.html index abcb1e29..28030d5b 100644 --- a/doc/manual.html +++ b/doc/manual.html @@ -69,7 +69,7 @@

      User manual and reference guide - version 5.47.0 + version 5.47.1

      CodeMirror is a code-editor component that can be embedded in diff --git a/package.json b/package.json index 86aa4861..11bf115a 100644 --- a/package.json +++ b/package.json @@ -1,6 +1,6 @@ { "name": "codemirror", - "version": "5.47.0", + "version": "5.47.1", "main": "lib/codemirror.js", "style": "lib/codemirror.css", "author": { diff --git a/src/edit/main.js b/src/edit/main.js index 615372b1..aa1fd89e 100644 --- a/src/edit/main.js +++ b/src/edit/main.js @@ -66,4 +66,4 @@ import { addLegacyProps } from "./legacy.js" addLegacyProps(CodeMirror) -CodeMirror.version = "5.47.0" +CodeMirror.version = "5.47.1" From 48561a645790097280a894b8e5bd1ab05a5c30db Mon Sep 17 00:00:00 2001 From: Bruno Logerfo Date: Mon, 27 May 2019 03:02:56 -0300 Subject: [PATCH 1590/1790] [mode metadata] Add "cs" as alias for C# Other places like discord, github and gitlab supports this. --- mode/meta.js | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/mode/meta.js b/mode/meta.js index ef0a26a2..08101815 100644 --- a/mode/meta.js +++ b/mode/meta.js @@ -20,7 +20,7 @@ {name: "C", mime: "text/x-csrc", mode: "clike", ext: ["c", "h", "ino"]}, {name: "C++", mime: "text/x-c++src", mode: "clike", ext: ["cpp", "c++", "cc", "cxx", "hpp", "h++", "hh", "hxx"], alias: ["cpp"]}, {name: "Cobol", mime: "text/x-cobol", mode: "cobol", ext: ["cob", "cpy"]}, - {name: "C#", mime: "text/x-csharp", mode: "clike", ext: ["cs"], alias: ["csharp"]}, + {name: "C#", mime: "text/x-csharp", mode: "clike", ext: ["cs"], alias: ["csharp", "cs"]}, {name: "Clojure", mime: "text/x-clojure", mode: "clojure", ext: ["clj", "cljc", "cljx"]}, {name: "ClojureScript", mime: "text/x-clojurescript", mode: "clojure", ext: ["cljs"]}, {name: "Closure Stylesheets (GSS)", mime: "text/x-gss", mode: "css", ext: ["gss"]}, From db1d66c2d2aae8f7c43935a34fe950c9b3e54abd Mon Sep 17 00:00:00 2001 From: Raymond Hill Date: Wed, 29 May 2019 06:45:15 -0400 Subject: [PATCH 1591/1790] [merge addon] Fix typo in hasMarker() This was causing exceptions to be thrown when using custom markers. --- 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 63373f75..8296540a 100644 --- a/addon/merge/merge.js +++ b/addon/merge/merge.js @@ -918,7 +918,7 @@ hasMarker: function(n) { var handle = this.cm.getLineHandle(n) if (handle.markedSpans) for (var i = 0; i < handle.markedSpans.length; i++) - if (handle.markedSpans[i].mark.collapsed && handle.markedSpans[i].to != null) + if (handle.markedSpans[i].marker.collapsed && handle.markedSpans[i].to != null) return true return false }, From 8aa23d7bfecfce390b05fd622dab2fb3ca89a1a1 Mon Sep 17 00:00:00 2001 From: Maksym Taran Date: Wed, 5 Jun 2019 18:35:54 -0700 Subject: [PATCH 1592/1790] Add U+FFFC as a default replaced special character --- doc/manual.html | 2 +- src/edit/options.js | 2 +- 2 files changed, 2 insertions(+), 2 deletions(-) diff --git a/doc/manual.html b/doc/manual.html index 28030d5b..a0a88062 100644 --- a/doc/manual.html +++ b/doc/manual.html @@ -284,7 +284,7 @@

      Configuration

      should be replaced by a special placeholder. Mostly useful for non-printing special characters. The default - is /[\u0000-\u001f\u007f-\u009f\u00ad\u061c\u200b-\u200f\u2028\u2029\ufeff]/. + is /[\u0000-\u001f\u007f-\u009f\u00ad\u061c\u200b-\u200f\u2028\u2029\ufeff\ufffc]/.
      specialCharPlaceholder: function(char) → Element
      A function that, given a special character identified by the specialChars diff --git a/src/edit/options.js b/src/edit/options.js index ccca45dd..9687a367 100644 --- a/src/edit/options.js +++ b/src/edit/options.js @@ -68,7 +68,7 @@ export function defineOptions(CodeMirror) { for (let i = newBreaks.length - 1; i >= 0; i--) replaceRange(cm.doc, val, newBreaks[i], Pos(newBreaks[i].line, newBreaks[i].ch + val.length)) }) - option("specialChars", /[\u0000-\u001f\u007f-\u009f\u00ad\u061c\u200b-\u200f\u2028\u2029\ufeff]/g, (cm, val, old) => { + option("specialChars", /[\u0000-\u001f\u007f-\u009f\u00ad\u061c\u200b-\u200f\u2028\u2029\ufeff\ufffc]/g, (cm, val, old) => { cm.state.specialChars = new RegExp(val.source + (val.test("\t") ? "" : "|\t"), "g") if (old != Init) cm.refresh() }) From 58df15001685683f13a5f8cfb069c84c5e1afa0c Mon Sep 17 00:00:00 2001 From: Marijn Haverbeke Date: Fri, 7 Jun 2019 09:24:35 +0200 Subject: [PATCH 1593/1790] Also add u+fff9-u+fffb as special characters Browser don't display anything for them either Issue #5900 --- doc/manual.html | 2 +- src/edit/options.js | 2 +- 2 files changed, 2 insertions(+), 2 deletions(-) diff --git a/doc/manual.html b/doc/manual.html index a0a88062..a2e5f40e 100644 --- a/doc/manual.html +++ b/doc/manual.html @@ -284,7 +284,7 @@

      Configuration

      should be replaced by a special placeholder. Mostly useful for non-printing special characters. The default - is /[\u0000-\u001f\u007f-\u009f\u00ad\u061c\u200b-\u200f\u2028\u2029\ufeff\ufffc]/.
      + is /[\u0000-\u001f\u007f-\u009f\u00ad\u061c\u200b-\u200f\u2028\u2029\ufeff\ufff9-\ufffc]/.
      specialCharPlaceholder: function(char) → Element
      A function that, given a special character identified by the specialChars diff --git a/src/edit/options.js b/src/edit/options.js index 9687a367..3abd3c3c 100644 --- a/src/edit/options.js +++ b/src/edit/options.js @@ -68,7 +68,7 @@ export function defineOptions(CodeMirror) { for (let i = newBreaks.length - 1; i >= 0; i--) replaceRange(cm.doc, val, newBreaks[i], Pos(newBreaks[i].line, newBreaks[i].ch + val.length)) }) - option("specialChars", /[\u0000-\u001f\u007f-\u009f\u00ad\u061c\u200b-\u200f\u2028\u2029\ufeff\ufffc]/g, (cm, val, old) => { + option("specialChars", /[\u0000-\u001f\u007f-\u009f\u00ad\u061c\u200b-\u200f\u2028\u2029\ufeff\ufff9-\ufffc]/g, (cm, val, old) => { cm.state.specialChars = new RegExp(val.source + (val.test("\t") ? "" : "|\t"), "g") if (old != Init) cm.refresh() }) From e31bc36a02c27d1f92a82aaa73c17bd4e0de55fa Mon Sep 17 00:00:00 2001 From: Duncan Lilley Date: Sun, 9 Jun 2019 04:50:33 -0400 Subject: [PATCH 1594/1790] [clike mode] Remove 'float' as Java keyword --- mode/clike/clike.js | 2 +- mode/clike/test.js | 26 ++++++++++++++++++++++++++ 2 files changed, 27 insertions(+), 1 deletion(-) diff --git a/mode/clike/clike.js b/mode/clike/clike.js index 1af35d7b..4c8b9937 100644 --- a/mode/clike/clike.js +++ b/mode/clike/clike.js @@ -467,7 +467,7 @@ CodeMirror.defineMode("clike", function(config, parserConfig) { def("text/x-java", { name: "clike", keywords: words("abstract assert break case catch class const continue default " + - "do else enum extends final finally float for goto if implements import " + + "do else enum extends final finally for goto if implements import " + "instanceof interface native new package private protected public " + "return static strictfp super switch synchronized this throw throws transient " + "try volatile while @interface"), diff --git a/mode/clike/test.js b/mode/clike/test.js index 6cb7605e..9441b959 100644 --- a/mode/clike/test.js +++ b/mode/clike/test.js @@ -136,4 +136,30 @@ "[comment ///// let / me / show / you /////]", "[comment */]"); + var mode_java = CodeMirror.getMode({indentUnit: 2}, "text/x-java"); + function MTJAVA(name) { test.mode("java_" + name, mode_java, Array.prototype.slice.call(arguments, 1)); } + MTJAVA("types", + "[type byte];", + "[type short];", + "[type int];", + "[type long];", + "[type float];", + "[type double];", + "[type boolean];", + "[type char];", + "[type void];", + "[type Boolean];", + "[type Byte];", + "[type Character];", + "[type Double];", + "[type Float];", + "[type Integer];", + "[type Long];", + "[type Number];", + "[type Object];", + "[type Short];", + "[type String];", + "[type StringBuffer];", + "[type StringBuilder];", + "[type Void];"); })(); From 7886f7f306c36b8c9be4692a06587f7d5347acb4 Mon Sep 17 00:00:00 2001 From: Vincent Woo Date: Tue, 11 Jun 2019 11:05:07 -0700 Subject: [PATCH 1595/1790] [mode metadata[ Add meta entry for PostgreSQ --- mode/meta.js | 1 + 1 file changed, 1 insertion(+) diff --git a/mode/meta.js b/mode/meta.js index 08101815..95d47310 100644 --- a/mode/meta.js +++ b/mode/meta.js @@ -105,6 +105,7 @@ {name: "Pig", mime: "text/x-pig", mode: "pig", ext: ["pig"]}, {name: "Plain Text", mime: "text/plain", mode: "null", ext: ["txt", "text", "conf", "def", "list", "log"]}, {name: "PLSQL", mime: "text/x-plsql", mode: "sql", ext: ["pls"]}, + {name: "PostgreSQL", mime: "text/x-pgsql", mode: "sql"}, {name: "PowerShell", mime: "application/x-powershell", mode: "powershell", ext: ["ps1", "psd1", "psm1"]}, {name: "Properties files", mime: "text/x-properties", mode: "properties", ext: ["properties", "ini", "in"], alias: ["ini", "properties"]}, {name: "ProtoBuf", mime: "text/x-protobuf", mode: "protobuf", ext: ["proto"]}, From 7978b40b4bcf6d5f3f331fd220d1eb1627181573 Mon Sep 17 00:00:00 2001 From: Tom McLaughlin Date: Tue, 11 Jun 2019 11:33:06 -0700 Subject: [PATCH 1596/1790] Add selectLeft/selectRight marker options --- doc/manual.html | 16 ++++++-- src/model/selection_updates.js | 15 +++++-- test/test.js | 72 ++++++++++++++++++++++++++++++++-- 3 files changed, 91 insertions(+), 12 deletions(-) diff --git a/doc/manual.html b/doc/manual.html index a2e5f40e..9f9fbc5e 100644 --- a/doc/manual.html +++ b/doc/manual.html @@ -1674,13 +1674,21 @@

      Text-marking methods

      inclusiveRight: boolean
      Like inclusiveLeft, but for the right side.
      +
      selectLeft: boolean
      +
      For atomic ranges, determines whether the cursor is allowed + to be placed directly to the left of the range. Has no effect on + non-atomic ranges.
      +
      selectRight: boolean
      +
      Like selectLeft, + but for the right side.
      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.
      + them. You can control whether the cursor is allowed to be placed + directly before or after them using selectLeft + or selectRight. If selectLeft + (or right) is not provided, then inclusiveLeft (or + right) will control this behavior.
      collapsed: boolean
      Collapsed ranges do not show up in the display. Setting a range to be collapsed will automatically make it atomic.
      diff --git a/src/model/selection_updates.js b/src/model/selection_updates.js index c9941b71..4db2bd7f 100644 --- a/src/model/selection_updates.js +++ b/src/model/selection_updates.js @@ -150,8 +150,15 @@ function skipAtomicInner(doc, pos, oldPos, dir, mayClear) { let line = getLine(doc, pos.line) if (line.markedSpans) for (let i = 0; i < line.markedSpans.length; ++i) { let sp = line.markedSpans[i], m = sp.marker - if ((sp.from == null || (m.inclusiveLeft ? sp.from <= pos.ch : sp.from < pos.ch)) && - (sp.to == null || (m.inclusiveRight ? sp.to >= pos.ch : sp.to > pos.ch))) { + + // Determine if we should prevent the cursor being placed to the left/right of an atomic marker + // Historically this was determined using the inclusiveLeft/Right option, but the new way to control it + // is with selectLeft/Right + let preventCursorLeft = ("selectLeft" in m) ? !m.selectLeft : m.inclusiveLeft + let preventCursorRight = ("selectRight" in m) ? !m.selectRight : m.inclusiveRight + + if ((sp.from == null || (preventCursorLeft ? sp.from <= pos.ch : sp.from < pos.ch)) && + (sp.to == null || (preventCursorRight ? sp.to >= pos.ch : sp.to > pos.ch))) { if (mayClear) { signal(m, "beforeCursorEnter") if (m.explicitlyCleared) { @@ -163,14 +170,14 @@ function skipAtomicInner(doc, pos, oldPos, dir, mayClear) { if (oldPos) { let near = m.find(dir < 0 ? 1 : -1), diff - if (dir < 0 ? m.inclusiveRight : m.inclusiveLeft) + if (dir < 0 ? preventCursorRight : preventCursorLeft) near = movePos(doc, near, -dir, near && near.line == pos.line ? line : null) if (near && near.line == pos.line && (diff = cmp(near, oldPos)) && (dir < 0 ? diff < 0 : diff > 0)) return skipAtomicInner(doc, near, pos, dir, mayClear) } let far = m.find(dir < 0 ? -1 : 1) - if (dir < 0 ? m.inclusiveLeft : m.inclusiveRight) + if (dir < 0 ? preventCursorLeft : preventCursorRight) far = movePos(doc, far, dir, far.line == pos.line ? line : null) return far ? skipAtomicInner(doc, far, pos, dir, mayClear) : null } diff --git a/test/test.js b/test/test.js index f26d01aa..846d94e8 100644 --- a/test/test.js +++ b/test/test.js @@ -1808,10 +1808,21 @@ testCM("addLineClass", function(cm) { testCM("atomicMarker", function(cm) { addDoc(cm, 10, 10); - function atom(ll, cl, lr, cr, li, ri) { - return cm.markText(Pos(ll, cl), Pos(lr, cr), - {atomic: true, inclusiveLeft: li, inclusiveRight: ri}); + + function atom(ll, cl, lr, cr, li, ri, ls, rs) { + var options = { + atomic: true, + inclusiveLeft: li, + inclusiveRight: ri + }; + + if (ls === true || ls === false) options["selectLeft"] = ls; + if (rs === true || rs === false) options["selectRight"] = rs; + + return cm.markText(Pos(ll, cl), Pos(lr, cr), options); } + + // Can cursor to the left and right of a normal marker by jumping across it var m = atom(0, 1, 0, 5); cm.setCursor(Pos(0, 1)); cm.execCommand("goCharRight"); @@ -1819,11 +1830,55 @@ testCM("atomicMarker", function(cm) { cm.execCommand("goCharLeft"); eqCursorPos(cm.getCursor(), Pos(0, 1)); m.clear(); + + // Can't cursor to the left of a marker when inclusiveLeft=true m = atom(0, 0, 0, 5, true); eqCursorPos(cm.getCursor(), Pos(0, 5), "pushed out"); cm.execCommand("goCharLeft"); eqCursorPos(cm.getCursor(), Pos(0, 5)); m.clear(); + + // Can't cursor to the left of a marker when inclusiveLeft=false and selectLeft=false + m = atom(0, 0, 0, 5, false, false, false); + cm.setCursor(Pos(0, 5)); + eqCursorPos(cm.getCursor(), Pos(0, 5), "pushed out"); + cm.execCommand("goCharLeft"); + eqCursorPos(cm.getCursor(), Pos(0, 5)); + m.clear(); + + // Can cursor to the left of a marker when inclusiveLeft=false and selectLeft=True + m = atom(0, 0, 0, 5, false, false, true); + cm.setCursor(Pos(0, 5)); + eqCursorPos(cm.getCursor(), Pos(0, 5), "pushed out"); + cm.execCommand("goCharLeft"); + eqCursorPos(cm.getCursor(), Pos(0, 0)); + m.clear(); + + // Can't cursor to the right of a marker when inclusiveRight=true + m = atom(0, 0, 0, 5, false, true); + cm.setCursor(Pos(0, 0)); + eqCursorPos(cm.getCursor(), Pos(0, 0)); + cm.execCommand("goCharRight"); + eqCursorPos(cm.getCursor(), Pos(0, 6)); + m.clear(); + + // Can't cursor to the right of a marker when inclusiveRight=false and selectRight=false + m = atom(0, 0, 0, 5, false, false, true, false); + cm.setCursor(Pos(0, 0)); + eqCursorPos(cm.getCursor(), Pos(0, 0)); + cm.execCommand("goCharRight"); + eqCursorPos(cm.getCursor(), Pos(0, 6)); + m.clear(); + + // Can cursor to the right of a marker when inclusiveRight=false and selectRight=True + m = atom(0, 0, 0, 5, false, false, true, true); + cm.setCursor(Pos(0, 0)); + eqCursorPos(cm.getCursor(), Pos(0, 0)); + cm.execCommand("goCharRight"); + eqCursorPos(cm.getCursor(), Pos(0, 5)); + m.clear(); + + // Can't cursor to the right of a multiline marker when inclusiveRight=true m = atom(8, 4, 9, 10, false, true); cm.setCursor(Pos(9, 8)); eqCursorPos(cm.getCursor(), Pos(8, 4), "set"); @@ -1834,6 +1889,9 @@ testCM("atomicMarker", function(cm) { cm.execCommand("goCharLeft"); eqCursorPos(cm.getCursor(), Pos(8, 3, "after")); m.clear(); + + // Cursor jumps across a multiline atomic marker, + // and backspace deletes the entire marker m = atom(1, 1, 3, 8); cm.setCursor(Pos(0, 0)); cm.setCursor(Pos(2, 0)); @@ -1848,10 +1906,16 @@ testCM("atomicMarker", function(cm) { eqCursorPos(cm.getCursor(), Pos(3, 8)); cm.execCommand("delCharBefore"); eq(cm.getValue().length, 80, "del chunk"); + m.clear(); + addDoc(cm, 10, 10); + + // Delete before an atomic marker deletes the entire marker m = atom(3, 0, 5, 5); cm.setCursor(Pos(3, 0)); cm.execCommand("delWordAfter"); - eq(cm.getValue().length, 53, "del chunk"); + eq(cm.getValue().length, 82, "del chunk"); + m.clear(); + addDoc(cm, 10, 10); }); testCM("selectionBias", function(cm) { From a423203c12f8f29e14050635659e124f3280001f Mon Sep 17 00:00:00 2001 From: Marijn Haverbeke Date: Thu, 13 Jun 2019 12:04:05 +0200 Subject: [PATCH 1597/1790] [groovy mode] Add comment metadata Closes #5906 --- mode/groovy/groovy.js | 5 ++++- 1 file changed, 4 insertions(+), 1 deletion(-) diff --git a/mode/groovy/groovy.js b/mode/groovy/groovy.js index b76a03fa..2b90d01c 100644 --- a/mode/groovy/groovy.js +++ b/mode/groovy/groovy.js @@ -221,7 +221,10 @@ CodeMirror.defineMode("groovy", function(config) { electricChars: "{}", closeBrackets: {triples: "'\""}, - fold: "brace" + fold: "brace", + blockCommentStart: "/*", + blockCommentEnd: "*/", + lineComment: "//" }; }); From 3446b793c9f25cf3f804795c53ca87023815c9dc Mon Sep 17 00:00:00 2001 From: kcwiakala Date: Sun, 16 Jun 2019 09:33:36 +0200 Subject: [PATCH 1598/1790] [real-world uses] Add LiveUML --- doc/realworld.html | 1 + 1 file changed, 1 insertion(+) diff --git a/doc/realworld.html b/doc/realworld.html index ae7ff4da..750cccc4 100644 --- a/doc/realworld.html +++ b/doc/realworld.html @@ -119,6 +119,7 @@

      CodeMirror real-world uses

    • Kotlin (web-based mini-IDE for Kotlin)
    • Light Table (experimental IDE)
    • Liveweave (HTML/CSS/JS scratchpad)
    • +
    • LiveUML (PlantUML online editor)
    • Markdown Delight Editor (extensible markdown editor polymer component)
    • Marklight editor (lightweight markup editor)
    • Mergely (interactive diffing)
    • From 5294bf13493296078ee7474d53ccb2f7b1a2f54a Mon Sep 17 00:00:00 2001 From: Erik Welander Date: Fri, 14 Jun 2019 16:32:30 -0700 Subject: [PATCH 1599/1790] [vim] throttle highlightSearchMatches This should prevent it from falling behind trying to highlight previous queries while you're typing. --- keymap/vim.js | 32 ++++++++++++++++++-------------- 1 file changed, 18 insertions(+), 14 deletions(-) diff --git a/keymap/vim.js b/keymap/vim.js index f8ffb88f..3be7a3f4 100644 --- a/keymap/vim.js +++ b/keymap/vim.js @@ -4229,23 +4229,27 @@ query: query }; } + var highlightTimeout = 0; function highlightSearchMatches(cm, query) { - var searchState = getSearchState(cm); - var overlay = searchState.getOverlay(); - if (!overlay || query != overlay.query) { - if (overlay) { - cm.removeOverlay(overlay); - } - overlay = searchOverlay(query); - cm.addOverlay(overlay); - if (cm.showMatchesOnScrollbar) { - if (searchState.getScrollbarAnnotate()) { - searchState.getScrollbarAnnotate().clear(); + clearTimeout(highlightTimeout); + highlightTimeout = setTimeout(function() { + var searchState = getSearchState(cm); + var overlay = searchState.getOverlay(); + if (!overlay || query != overlay.query) { + if (overlay) { + cm.removeOverlay(overlay); + } + overlay = searchOverlay(query); + cm.addOverlay(overlay); + if (cm.showMatchesOnScrollbar) { + if (searchState.getScrollbarAnnotate()) { + searchState.getScrollbarAnnotate().clear(); + } + searchState.setScrollbarAnnotate(cm.showMatchesOnScrollbar(query)); } - searchState.setScrollbarAnnotate(cm.showMatchesOnScrollbar(query)); + searchState.setOverlay(overlay); } - searchState.setOverlay(overlay); - } + }, 50); } function findNext(cm, prev, query, repeat) { if (repeat === undefined) { repeat = 1; } From 11786d27895092a6f4b34c2074635faec32d853b Mon Sep 17 00:00:00 2001 From: Erik Welander Date: Mon, 17 Jun 2019 14:59:32 -0700 Subject: [PATCH 1600/1790] [hint addon] Handle scrolling containers --- 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 0172cc8e..d70b2ab1 100644 --- a/addon/hint/show-hint.js +++ b/addon/hint/show-hint.js @@ -239,8 +239,8 @@ var offsetParent = isContainerPositioned ? container : container.offsetParent; var offsetParentPosition = offsetParent.getBoundingClientRect(); var bodyPosition = ownerDocument.body.getBoundingClientRect(); - offsetLeft = (offsetParentPosition.left - bodyPosition.left); - offsetTop = (offsetParentPosition.top - bodyPosition.top); + offsetLeft = (offsetParentPosition.left - bodyPosition.left - offsetParent.scrollLeft); + offsetTop = (offsetParentPosition.top - bodyPosition.top - offsetParent.scrollTop); } hints.style.left = (left - offsetLeft) + "px"; hints.style.top = (top - offsetTop) + "px"; From 306c38eea7000f38d82febcd6162bb6138586f8b Mon Sep 17 00:00:00 2001 From: Marijn Haverbeke Date: Tue, 18 Jun 2019 08:29:45 +0200 Subject: [PATCH 1601/1790] [javascript mode] Remove strange handling of 'in' in TypeScript mode Closes #5909 --- 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 0f836647..d49d959a 100644 --- a/mode/javascript/javascript.js +++ b/mode/javascript/javascript.js @@ -574,7 +574,7 @@ CodeMirror.defineMode("javascript", function(config, parserConfig) { } function maybetype(type, value) { if (isTS) { - if (type == ":" || value == "in") return cont(typeexpr); + if (type == ":") return cont(typeexpr); if (value == "?") return cont(maybetype); } } From 82d1245d2108edc0a758435d8d37afb55b2a9527 Mon Sep 17 00:00:00 2001 From: Marijn Haverbeke Date: Wed, 19 Jun 2019 08:27:23 +0200 Subject: [PATCH 1602/1790] [javascript mode] Fix typescript [key in ...] syntax (again) Issue #5737 Issue #5909 --- mode/javascript/javascript.js | 5 ++++- 1 file changed, 4 insertions(+), 1 deletion(-) diff --git a/mode/javascript/javascript.js b/mode/javascript/javascript.js index d49d959a..eb67187d 100644 --- a/mode/javascript/javascript.js +++ b/mode/javascript/javascript.js @@ -525,7 +525,7 @@ CodeMirror.defineMode("javascript", function(config, parserConfig) { cx.marked = "keyword" return cont(objprop) } else if (type == "[") { - return cont(expression, maybetype, expect("]"), afterprop); + return cont(expression, maybetypeOrIn, expect("]"), afterprop); } else if (type == "spread") { return cont(expressionNoComma, afterprop); } else if (value == "*") { @@ -578,6 +578,9 @@ CodeMirror.defineMode("javascript", function(config, parserConfig) { if (value == "?") return cont(maybetype); } } + function maybetypeOrIn(type, value) { + if (isTS && (type == ":" || value == "in")) return cont(typeexpr) + } function mayberettype(type) { if (isTS && type == ":") { if (cx.stream.match(/^\s*\w+\s+is\b/, false)) return cont(expression, isKW, typeexpr) From f249e9ded5a8279ac6fc3d9538b9c4e2b99670ad Mon Sep 17 00:00:00 2001 From: stockiNail Date: Wed, 19 Jun 2019 18:49:15 +0200 Subject: [PATCH 1603/1790] [real-world uses] Added Coderba GWT wrapper We implemented a wrapper to CodeMirror for GWT (base on JSINTEROP) --- doc/realworld.html | 1 + 1 file changed, 1 insertion(+) diff --git a/doc/realworld.html b/doc/realworld.html index 750cccc4..c88c0ebb 100644 --- a/doc/realworld.html +++ b/doc/realworld.html @@ -52,6 +52,7 @@

      CodeMirror real-world uses

    • CodeMirror2-GWT (Google Web Toolkit wrapper)
    • Code Monster & Code Maven (learning environment)
    • Codepen (gallery of animations)
    • +
    • Google Web Toolkit (GWT) wrapper
    • Coderpad (interviewing tool)
    • Code School (online tech learning environment)
    • Code Snippets (WordPress snippet management plugin)
    • From 908e9dab8aac555561b64885076d9de9c45f2cdb Mon Sep 17 00:00:00 2001 From: stockiNail Date: Thu, 20 Jun 2019 08:46:13 +0200 Subject: [PATCH 1604/1790] [real-world uses] Fixed link to GWT wrapper extension --- doc/realworld.html | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/doc/realworld.html b/doc/realworld.html index c88c0ebb..fdb278d4 100644 --- a/doc/realworld.html +++ b/doc/realworld.html @@ -52,7 +52,7 @@

      CodeMirror real-world uses

    • CodeMirror2-GWT (Google Web Toolkit wrapper)
    • Code Monster & Code Maven (learning environment)
    • Codepen (gallery of animations)
    • -
    • Google Web Toolkit (GWT) wrapper
    • +
    • Coderba Google Web Toolkit (GWT) wrapper
    • Coderpad (interviewing tool)
    • Code School (online tech learning environment)
    • Code Snippets (WordPress snippet management plugin)
    • From b1aeb702f0f718a61723fa82d23b2d08f1478cb1 Mon Sep 17 00:00:00 2001 From: Marijn Haverbeke Date: Thu, 20 Jun 2019 09:11:20 +0200 Subject: [PATCH 1605/1790] Mark version 5.48.0 --- AUTHORS | 4 ++++ CHANGELOG.md | 12 ++++++++++++ doc/manual.html | 2 +- doc/releases.html | 8 ++++++++ index.html | 2 +- package.json | 2 +- src/edit/main.js | 2 +- 7 files changed, 28 insertions(+), 4 deletions(-) diff --git a/AUTHORS b/AUTHORS index f61c7c5b..c8d5ccd0 100644 --- a/AUTHORS +++ b/AUTHORS @@ -124,6 +124,7 @@ Brian Grinstead Brian Sletten brrd Bruce Mitchener +Bruno Logerfo Bryan Massoth Caitlin Potter Calin Barbat @@ -216,6 +217,7 @@ Drew Hintz Drew Khoury Drini Cami Dror BG +Duncan Lilley duralog dwelle eborden @@ -406,6 +408,7 @@ karevn Karol Kayur Patel Kazuhito Hokamura +kcwiakala Kees de Kooter Kenan Christian Dimas Ken Newman @@ -720,6 +723,7 @@ Stephen Lavelle Steve Champagne Steve Hoover Steve O'Hara +stockiNail stoskov Stryder Crown Stu Kennedy diff --git a/CHANGELOG.md b/CHANGELOG.md index d8c523c7..9dc7454f 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -1,3 +1,15 @@ +## 5.48.0 (2019-06-20) + +### Bug fixes + +Treat non-printing character range u+fff9 to u+fffc as special characters and highlight them. + +[show-hint addon](https://codemirror.net/doc/manual.html#addon_show-hint): Fix positioning when the dialog is placed in a scrollable container. + +### New features + +Add [`selectLeft`](https://codemirror.net/doc/manual.html#mark_selectLeft)/[`selectRight`](https://codemirror.net/doc/manual.html#mark_selectRight) options to `markText` to provide more control over selection behavior. + ## 5.47.0 (2019-05-21) ### Bug fixes diff --git a/doc/manual.html b/doc/manual.html index 9f9fbc5e..bb3f27a5 100644 --- a/doc/manual.html +++ b/doc/manual.html @@ -69,7 +69,7 @@

      User manual and reference guide - version 5.47.1 + version 5.48.0

      CodeMirror is a code-editor component that can be embedded in diff --git a/doc/releases.html b/doc/releases.html index 0066778e..10046cac 100644 --- a/doc/releases.html +++ b/doc/releases.html @@ -30,6 +30,14 @@

      Release notes and version history

      Version 5.x

      +

      20-06-2019: Version 5.48.0:

      + +
        +
      • Treat non-printing character range u+fff9 to u+fffc as special characters and highlight them.
      • +
      • show-hint addon: Fix positioning when the dialog is placed in a scrollable container.
      • +
      • Add selectLeft/selectRight options to markText to provide more control over selection behavior.
      • +
      +

      21-05-2019: Version 5.47.0:

        diff --git a/index.html b/index.html index 2c682638..6a91545a 100644 --- a/index.html +++ b/index.html @@ -99,7 +99,7 @@

        This is CodeMirror

      - Get the current version: 5.47.0.
      + Get the current version: 5.48.0.
      You can see the code,
      read the release notes,
      or study the user manual. diff --git a/package.json b/package.json index 11bf115a..2b81992e 100644 --- a/package.json +++ b/package.json @@ -1,6 +1,6 @@ { "name": "codemirror", - "version": "5.47.1", + "version": "5.48.0", "main": "lib/codemirror.js", "style": "lib/codemirror.css", "author": { diff --git a/src/edit/main.js b/src/edit/main.js index aa1fd89e..898eaada 100644 --- a/src/edit/main.js +++ b/src/edit/main.js @@ -66,4 +66,4 @@ import { addLegacyProps } from "./legacy.js" addLegacyProps(CodeMirror) -CodeMirror.version = "5.47.1" +CodeMirror.version = "5.48.0" From 62ef2768cc96327e1402e58d398d6042f8407dd2 Mon Sep 17 00:00:00 2001 From: Marijn Haverbeke Date: Thu, 20 Jun 2019 09:12:51 +0200 Subject: [PATCH 1606/1790] Bump version number post-5.48.0 --- doc/manual.html | 2 +- package.json | 2 +- src/edit/main.js | 2 +- 3 files changed, 3 insertions(+), 3 deletions(-) diff --git a/doc/manual.html b/doc/manual.html index bb3f27a5..c8afbd86 100644 --- a/doc/manual.html +++ b/doc/manual.html @@ -69,7 +69,7 @@

      User manual and reference guide - version 5.48.0 + version 5.48.1

      CodeMirror is a code-editor component that can be embedded in diff --git a/package.json b/package.json index 2b81992e..e1e5c54d 100644 --- a/package.json +++ b/package.json @@ -1,6 +1,6 @@ { "name": "codemirror", - "version": "5.48.0", + "version": "5.48.1", "main": "lib/codemirror.js", "style": "lib/codemirror.css", "author": { diff --git a/src/edit/main.js b/src/edit/main.js index 898eaada..caf04491 100644 --- a/src/edit/main.js +++ b/src/edit/main.js @@ -66,4 +66,4 @@ import { addLegacyProps } from "./legacy.js" addLegacyProps(CodeMirror) -CodeMirror.version = "5.48.0" +CodeMirror.version = "5.48.1" From 9353e166203158c5059fcf32de0ee251e13ee4d1 Mon Sep 17 00:00:00 2001 From: Marijn Haverbeke Date: Fri, 21 Jun 2019 08:46:45 +0200 Subject: [PATCH 1607/1790] [javascript mode] Fix computed property TS syntax for object types Issue #5909 --- 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 eb67187d..7e7b3879 100644 --- a/mode/javascript/javascript.js +++ b/mode/javascript/javascript.js @@ -525,7 +525,7 @@ CodeMirror.defineMode("javascript", function(config, parserConfig) { cx.marked = "keyword" return cont(objprop) } else if (type == "[") { - return cont(expression, maybetypeOrIn, expect("]"), afterprop); + return cont(expression, maybetype, expect("]"), afterprop); } else if (type == "spread") { return cont(expressionNoComma, afterprop); } else if (value == "*") { @@ -621,7 +621,7 @@ CodeMirror.defineMode("javascript", function(config, parserConfig) { } else if (type == ":") { return cont(typeexpr) } else if (type == "[") { - return cont(expect("variable"), maybetype, expect("]"), typeprop) + return cont(expect("variable"), maybetypeOrIn, expect("]"), typeprop) } else if (type == "(") { return pass(functiondecl, typeprop) } From beab8ed123683416bfec934df73d13401ec086b5 Mon Sep 17 00:00:00 2001 From: Yang Guo Date: Thu, 4 Jul 2019 14:42:09 +0200 Subject: [PATCH 1608/1790] [javascript mode] Support numeric separators Note that the new regular expression is more permissive than the spec proposal, e.g. 0_._1 or 1__2 are not valid numbers. However, filtering out these cases would make the tokenizer unnecessarily complex. --- mode/javascript/javascript.js | 6 +++--- mode/javascript/test.js | 9 +++++++++ 2 files changed, 12 insertions(+), 3 deletions(-) diff --git a/mode/javascript/javascript.js b/mode/javascript/javascript.js index 7e7b3879..85cb666a 100644 --- a/mode/javascript/javascript.js +++ b/mode/javascript/javascript.js @@ -67,7 +67,7 @@ CodeMirror.defineMode("javascript", function(config, parserConfig) { if (ch == '"' || ch == "'") { state.tokenize = tokenString(ch); return state.tokenize(stream, state); - } else if (ch == "." && stream.match(/^\d+(?:[eE][+\-]?\d+)?/)) { + } else if (ch == "." && stream.match(/^[\d_]+(?:[eE][+\-]?[\d_]+)?/)) { return ret("number", "number"); } else if (ch == "." && stream.match("..")) { return ret("spread", "meta"); @@ -75,10 +75,10 @@ CodeMirror.defineMode("javascript", function(config, parserConfig) { return ret(ch); } else if (ch == "=" && stream.eat(">")) { return ret("=>", "operator"); - } else if (ch == "0" && stream.match(/^(?:x[\da-f]+|o[0-7]+|b[01]+)n?/i)) { + } else if (ch == "0" && stream.match(/^(?:x[\dA-Fa-f_]+|o[0-7_]+|b[01_]+)n?/)) { return ret("number", "number"); } else if (/\d/.test(ch)) { - stream.match(/^\d*(?:n|(?:\.\d*)?(?:[eE][+\-]?\d+)?)?/); + stream.match(/^[\d_]*(?:n|(?:\.[\d_]*)?(?:[eE][+\-]?[\d_]+)?)?/); return ret("number", "number"); } else if (ch == "/") { if (stream.eat("*")) { diff --git a/mode/javascript/test.js b/mode/javascript/test.js index 04faeafa..d21a9a1f 100644 --- a/mode/javascript/test.js +++ b/mode/javascript/test.js @@ -298,6 +298,15 @@ "[keyword return]", "{} [string-2 /5/]") + MT("numeric separator", + "[number 123_456];", + "[number 0xdead_c0de];", + "[number 0o123_456];", + "[number 0b1101_1101];", + "[number .123_456e0_1];", + "[number 1E+123_456];", + "[number 12_34_56n];") + var ts_mode = CodeMirror.getMode({indentUnit: 2}, "application/typescript") function TS(name) { test.mode(name, ts_mode, Array.prototype.slice.call(arguments, 1)) From 1fb5c61b96e8ccf647d3c986f62daf0d085cdec4 Mon Sep 17 00:00:00 2001 From: Ilya Kharlamov <502372+ilyakharlamov@users.noreply.github.com> Date: Fri, 5 Jul 2019 01:27:04 -0400 Subject: [PATCH 1609/1790] [vim mode] match vim char escape substitution behavior --- keymap/vim.js | 9 ++++++++- test/vim_test.js | 5 +++++ 2 files changed, 13 insertions(+), 1 deletion(-) diff --git a/keymap/vim.js b/keymap/vim.js index 3be7a3f4..0e643386 100644 --- a/keymap/vim.js +++ b/keymap/vim.js @@ -4861,6 +4861,9 @@ var global = false; // True to replace all instances on a line, false to replace only 1. if (tokens.length) { regexPart = tokens[0]; + if (getOption('pcre') && regexPart !== '') { + regexPart = new RegExp(regexPart).source; //normalize not escaped characters + } replacePart = tokens[1]; if (regexPart && regexPart[regexPart.length - 1] === '$') { regexPart = regexPart.slice(0, regexPart.length - 1) + '\\n'; @@ -4899,7 +4902,11 @@ global = true; flagsPart.replace('g', ''); } - regexPart = regexPart.replace(/\//g, "\\/") + '/' + flagsPart; + if (getOption('pcre')) { + regexPart = regexPart + '/' + flagsPart; + } else { + regexPart = regexPart.replace(/\//g, "\\/") + '/' + flagsPart; + } } } if (regexPart) { diff --git a/test/vim_test.js b/test/vim_test.js index e74b2f2c..6e3ded49 100644 --- a/test/vim_test.js +++ b/test/vim_test.js @@ -3934,6 +3934,11 @@ testSubstitute('ex_substitute_or_word_regex', { expectedValue: 'five|five \n three|four', expr: '%s/(one|two)/five/g', noPcreExpr: '%s/\\(one\\|two\\)/five/g'}); +testSubstitute('ex_substitute_forward_slash_regex', { + value: 'forward slash \/ was here', + expectedValue: 'forward slash was here', + expr: '%s#\\/##g', + noPcreExpr: '%s#/##g'}); testSubstitute('ex_substitute_backslashslash_regex', { value: 'one\\two \n three\\four', expectedValue: 'one,two \n three,four', From f454d5784bcde812bf2e9ffa8c9189fc6eab9ee6 Mon Sep 17 00:00:00 2001 From: Ilya Kharlamov <502372+ilyakharlamov@users.noreply.github.com> Date: Fri, 5 Jul 2019 10:20:29 -0400 Subject: [PATCH 1610/1790] [vim mode] #5753 Feature request: Support &/$0 in vim substitute replacements --- keymap/vim.js | 4 ++-- test/vim_test.js | 18 ++++++++++++++++++ 2 files changed, 20 insertions(+), 2 deletions(-) diff --git a/keymap/vim.js b/keymap/vim.js index 0e643386..95b19b24 100644 --- a/keymap/vim.js +++ b/keymap/vim.js @@ -4073,7 +4073,7 @@ } // Unescape \ and / in the replace part, for PCRE mode. - var unescapes = {'\\/': '/', '\\\\': '\\', '\\n': '\n', '\\r': '\r', '\\t': '\t'}; + var unescapes = {'\\/': '/', '\\\\': '\\', '\\n': '\n', '\\r': '\r', '\\t': '\t', '\\&':'&'}; function unescapeRegexReplace(str) { var stream = new CodeMirror.StringStream(str); var output = []; @@ -4871,7 +4871,7 @@ } if (replacePart !== undefined) { if (getOption('pcre')) { - replacePart = unescapeRegexReplace(replacePart); + replacePart = unescapeRegexReplace(replacePart.replace(/([^\\])&/g,"$1$$&")); } else { replacePart = translateRegexReplace(replacePart); } diff --git a/test/vim_test.js b/test/vim_test.js index 6e3ded49..72a9896b 100644 --- a/test/vim_test.js +++ b/test/vim_test.js @@ -3939,6 +3939,24 @@ testSubstitute('ex_substitute_forward_slash_regex', { expectedValue: 'forward slash was here', expr: '%s#\\/##g', noPcreExpr: '%s#/##g'}); +testVim("ex_substitute_ampersand_pcre", function(cm, vim, helpers) { + cm.setCursor(0, 0); + CodeMirror.Vim.setOption('pcre', true); + helpers.doEx('%s/foo/namespace.&/'); + eq("namespace.foo", cm.getValue()); + }, { value: 'foo' }); +testVim("ex_substitute_ampersand_multiple_pcre", function(cm, vim, helpers) { + cm.setCursor(0, 0); + CodeMirror.Vim.setOption('pcre', true); + helpers.doEx('%s/f.o/namespace.&/'); + eq("namespace.foo\nnamespace.fzo", cm.getValue()); + }, { value: 'foo\nfzo' }); +testVim("ex_escaped_ampersand_should_not_substitute_pcre", function(cm, vim, helpers) { + cm.setCursor(0, 0); + CodeMirror.Vim.setOption('pcre', true); + helpers.doEx('%s/foo/namespace.\\&/'); + eq("namespace.&", cm.getValue()); + }, { value: 'foo' }); testSubstitute('ex_substitute_backslashslash_regex', { value: 'one\\two \n three\\four', expectedValue: 'one,two \n three,four', From ae07cb1776dddaba306ab9dd7f40b16efa4ffa97 Mon Sep 17 00:00:00 2001 From: Raymond Hill Date: Sun, 7 Jul 2019 11:44:10 -0400 Subject: [PATCH 1611/1790] [search addon] Escape sequence only for \n, \r, \t and \\ When `\` is followed by any other character, it will not be interpreted as an escape sequence, i.e. `\3` will be interpreted as literal `\3`, not as `3`. Fixes https://github.com/codemirror/CodeMirror/issues/5428 --- addon/search/search.js | 6 ++++-- 1 file changed, 4 insertions(+), 2 deletions(-) diff --git a/addon/search/search.js b/addon/search/search.js index 5c2559c7..cecdd52e 100644 --- a/addon/search/search.js +++ b/addon/search/search.js @@ -78,10 +78,12 @@ } function parseString(string) { - return string.replace(/\\(.)/g, function(_, ch) { + return string.replace(/\\([nrt\\])/g, function(match, ch) { if (ch == "n") return "\n" if (ch == "r") return "\r" - return ch + if (ch == "t") return "\t" + if (ch == "\\") return "\\" + return match }) } From ac2b82ccaa92e6534bd1f14743d8731231dffc28 Mon Sep 17 00:00:00 2001 From: Seb35 Date: Thu, 11 Jul 2019 11:46:00 +0200 Subject: [PATCH 1612/1790] [sparql mode] Non-ASCII variable names This regex is ES3-compliant but it misses non-BMP characters specified in SPARQL standard. Closes #5935 --- mode/sparql/sparql.js | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/mode/sparql/sparql.js b/mode/sparql/sparql.js index f246776b..bb79abff 100644 --- a/mode/sparql/sparql.js +++ b/mode/sparql/sparql.js @@ -41,7 +41,7 @@ CodeMirror.defineMode("sparql", function(config) { if(ch == "?" && stream.match(/\s/, false)){ return "operator"; } - stream.match(/^[\w\d]*/); + stream.match(/^[A-Za-z0-9_\u00C0-\u00D6\u00D8-\u00F6\u00F8-\u02FF\u0370-\u037D\u037F-\u1FFF\u200C-\u200D\u2070-\u218F\u2C00-\u2FEF\u3001-\uD7FF\uF900-\uFDCF\uFDF0-\uFFFD][A-Za-z0-9_\u00B7\u00C0-\u00D6\u00D8-\u00F6\u00F8-\u037D\u037F-\u1FFF\u200C-\u200D\u203F-\u2040\u2070-\u218F\u2C00-\u2FEF\u3001-\uD7FF\uF900-\uFDCF\uFDF0-\uFFFD]*/); return "variable-2"; } else if (ch == "<" && !stream.match(/^[\s\u00a0=]/, false)) { From 4c30e11366e0af0f50f3290ff5f51e6fae4f8466 Mon Sep 17 00:00:00 2001 From: Marijn Haverbeke Date: Thu, 11 Jul 2019 12:47:46 +0200 Subject: [PATCH 1613/1790] [javascript mode] Handle string defaults in arrow function param lists Closes #5934 --- mode/javascript/javascript.js | 6 +++++- mode/javascript/test.js | 3 +++ 2 files changed, 8 insertions(+), 1 deletion(-) diff --git a/mode/javascript/javascript.js b/mode/javascript/javascript.js index 85cb666a..5d4c8d1a 100644 --- a/mode/javascript/javascript.js +++ b/mode/javascript/javascript.js @@ -196,7 +196,11 @@ CodeMirror.defineMode("javascript", function(config, parserConfig) { } else if (wordRE.test(ch)) { sawSomething = true; } else if (/["'\/]/.test(ch)) { - return; + for (;; --pos) { + if (pos == 0) return + let next = stream.string.charAt(pos - 1) + if (next == ch && stream.string.charAt(pos - 2) != "\\") { pos--; break } + } } else if (sawSomething && !depth) { ++pos; break; diff --git a/mode/javascript/test.js b/mode/javascript/test.js index d21a9a1f..3003391b 100644 --- a/mode/javascript/test.js +++ b/mode/javascript/test.js @@ -127,6 +127,9 @@ "[keyword let] [def f] [operator =] ([[ [def a], [def b] ]], [def c]) [operator =>] [variable-2 a] [operator +] [variable-2 c];", "[variable c];"); + MT("fatArrow_stringDefault", + "([def a], [def b] [operator =] [string 'x\\'y']) [operator =>] [variable-2 a] [operator +] [variable-2 b]") + MT("spread", "[keyword function] [def f]([def a], [meta ...][def b]) {", " [variable something]([variable-2 a], [meta ...][variable-2 b]);", From 632a9433922eddadd06c3b3128413e7985d9f890 Mon Sep 17 00:00:00 2001 From: Marijn Haverbeke Date: Thu, 11 Jul 2019 13:44:56 +0200 Subject: [PATCH 1614/1790] [javascript mode] Fix use of let --- 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 5d4c8d1a..1276d853 100644 --- a/mode/javascript/javascript.js +++ b/mode/javascript/javascript.js @@ -198,7 +198,7 @@ CodeMirror.defineMode("javascript", function(config, parserConfig) { } else if (/["'\/]/.test(ch)) { for (;; --pos) { if (pos == 0) return - let next = stream.string.charAt(pos - 1) + var next = stream.string.charAt(pos - 1) if (next == ch && stream.string.charAt(pos - 2) != "\\") { pos--; break } } } else if (sawSomething && !depth) { From 17457466015551c7bf9292896baaa69e738f0d49 Mon Sep 17 00:00:00 2001 From: Bryan Gin-ge Chen Date: Thu, 11 Jul 2019 10:57:08 -0400 Subject: [PATCH 1615/1790] [javascript mode] don't detect backtick-enclosed fatarrows cf. #2993 and 27fe44c which fixed the analogous issue for single-quotes, double-quotes and regex literals. --- 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 1276d853..76ee9a6f 100644 --- a/mode/javascript/javascript.js +++ b/mode/javascript/javascript.js @@ -195,7 +195,7 @@ CodeMirror.defineMode("javascript", function(config, parserConfig) { ++depth; } else if (wordRE.test(ch)) { sawSomething = true; - } else if (/["'\/]/.test(ch)) { + } else if (/["'\/`]/.test(ch)) { for (;; --pos) { if (pos == 0) return var next = stream.string.charAt(pos - 1) From 463ea2c34ab442c0cae1d9732305219ca9b04dfe Mon Sep 17 00:00:00 2001 From: Yang Guo Date: Fri, 19 Jul 2019 09:15:34 +0200 Subject: [PATCH 1616/1790] [javascript mode] fix tokenizing of underscore properties The regexp for parsing numbers with numeric separators is too broad. This way, we mistake properties starting with underscore to be numbers. --- mode/javascript/javascript.js | 2 +- mode/javascript/test.js | 7 +++++++ 2 files changed, 8 insertions(+), 1 deletion(-) diff --git a/mode/javascript/javascript.js b/mode/javascript/javascript.js index 76ee9a6f..8055f1ba 100644 --- a/mode/javascript/javascript.js +++ b/mode/javascript/javascript.js @@ -67,7 +67,7 @@ CodeMirror.defineMode("javascript", function(config, parserConfig) { if (ch == '"' || ch == "'") { state.tokenize = tokenString(ch); return state.tokenize(stream, state); - } else if (ch == "." && stream.match(/^[\d_]+(?:[eE][+\-]?[\d_]+)?/)) { + } else if (ch == "." && stream.match(/^\d[\d_]*(?:[eE][+\-]?[\d_]+)?/)) { return ret("number", "number"); } else if (ch == "." && stream.match("..")) { return ret("spread", "meta"); diff --git a/mode/javascript/test.js b/mode/javascript/test.js index 3003391b..327eac76 100644 --- a/mode/javascript/test.js +++ b/mode/javascript/test.js @@ -310,6 +310,13 @@ "[number 1E+123_456];", "[number 12_34_56n];") + MT("underscore property", + "[variable something].[property _property];", + "[variable something].[property _123];", + "[variable something].[property _for];", + "[variable _for];", + "[variable _123];") + var ts_mode = CodeMirror.getMode({indentUnit: 2}, "application/typescript") function TS(name) { test.mode(name, ts_mode, Array.prototype.slice.call(arguments, 1)) From 4122f7308cb89bf11e2a9c43a7e540b60e4915ac Mon Sep 17 00:00:00 2001 From: Marijn Haverbeke Date: Fri, 19 Jul 2019 22:20:30 +0200 Subject: [PATCH 1617/1790] Mark version 5.48.2 --- AUTHORS | 4 ++++ CHANGELOG.md | 12 ++++++++++++ doc/manual.html | 2 +- doc/releases.html | 9 +++++++++ index.html | 2 +- package.json | 2 +- src/edit/main.js | 2 +- 7 files changed, 29 insertions(+), 4 deletions(-) diff --git a/AUTHORS b/AUTHORS index c8d5ccd0..ab98a58c 100644 --- a/AUTHORS +++ b/AUTHORS @@ -125,6 +125,7 @@ Brian Sletten brrd Bruce Mitchener Bruno Logerfo +Bryan Gin-ge Chen Bryan Massoth Caitlin Potter Calin Barbat @@ -316,6 +317,7 @@ Ice White ICHIKAWA, Yuji idleberg ilvalle +Ilya Kharlamov Ingo Richter Irakli Gozalishvili Ivan Kurnosov @@ -685,6 +687,7 @@ SCLINIC\jdecker Scott Aikin Scott Feeney Scott Goodhew +Seb35 Sebastian Wilzbach Sebastian Zaha Seren D @@ -801,6 +804,7 @@ Wojtek Ptak wonderboyjon Wu Cheng-Han Xavier Mendez +Yang Guo Yassin N. Hassan YNH Webdev yoongu diff --git a/CHANGELOG.md b/CHANGELOG.md index 9dc7454f..061c7215 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -1,3 +1,15 @@ +## 5.48.2 (2019-07-20) + +### Bug fixes + +[vim bindings](https://codemirror.net/demo/vim.html): Adjust char escape substitution to match vim, support `&/$0`. + +[search addon](https://codemirror.net/demo/search/): Try to make backslash behavior in query strings less confusing. + +[javascript mode](https://codemirror.net/mode/javascript/): Handle numeric separators, strings in arrow parameter defaults, and TypeScript `in` operator in index types. + +[sparql mode](https://codemirror.net/mode/sparql/index.html): Allow non-ASCII identifier characters. + ## 5.48.0 (2019-06-20) ### Bug fixes diff --git a/doc/manual.html b/doc/manual.html index c8afbd86..03e3a155 100644 --- a/doc/manual.html +++ b/doc/manual.html @@ -69,7 +69,7 @@

      User manual and reference guide - version 5.48.1 + version 5.48.2

      CodeMirror is a code-editor component that can be embedded in diff --git a/doc/releases.html b/doc/releases.html index 10046cac..11d94305 100644 --- a/doc/releases.html +++ b/doc/releases.html @@ -30,6 +30,15 @@

      Release notes and version history

      Version 5.x

      +

      20-07-2019: Version 5.48.2:

      + +
        +
      • vim bindings: Adjust char escape substitution to match vim, support &/$0.
      • +
      • search addon: Try to make backslash behavior in query strings less confusing.
      • +
      • javascript mode: Handle numeric separators, strings in arrow parameter defaults, and TypeScript in operator in index types.
      • +
      • sparql mode: Allow non-ASCII identifier characters.
      • +
      +

      20-06-2019: Version 5.48.0:

        diff --git a/index.html b/index.html index 6a91545a..90c429b9 100644 --- a/index.html +++ b/index.html @@ -99,7 +99,7 @@

        This is CodeMirror

      - Get the current version: 5.48.0.
      + Get the current version: 5.48.2.
      You can see the code,
      read the release notes,
      or study the user manual. diff --git a/package.json b/package.json index e1e5c54d..1fb368c9 100644 --- a/package.json +++ b/package.json @@ -1,6 +1,6 @@ { "name": "codemirror", - "version": "5.48.1", + "version": "5.48.2", "main": "lib/codemirror.js", "style": "lib/codemirror.css", "author": { diff --git a/src/edit/main.js b/src/edit/main.js index caf04491..1af55932 100644 --- a/src/edit/main.js +++ b/src/edit/main.js @@ -66,4 +66,4 @@ import { addLegacyProps } from "./legacy.js" addLegacyProps(CodeMirror) -CodeMirror.version = "5.48.1" +CodeMirror.version = "5.48.2" From 40c3cf2066c92c943fff4746db2b6b35ebb26fbd Mon Sep 17 00:00:00 2001 From: Marijn Haverbeke Date: Fri, 19 Jul 2019 22:22:41 +0200 Subject: [PATCH 1618/1790] Bump version number post-5.48.2 --- doc/manual.html | 2 +- package.json | 2 +- src/edit/main.js | 2 +- 3 files changed, 3 insertions(+), 3 deletions(-) diff --git a/doc/manual.html b/doc/manual.html index 03e3a155..5b2343a6 100644 --- a/doc/manual.html +++ b/doc/manual.html @@ -69,7 +69,7 @@

      User manual and reference guide - version 5.48.2 + version 5.48.3

      CodeMirror is a code-editor component that can be embedded in diff --git a/package.json b/package.json index 1fb368c9..ac483fee 100644 --- a/package.json +++ b/package.json @@ -1,6 +1,6 @@ { "name": "codemirror", - "version": "5.48.2", + "version": "5.48.3", "main": "lib/codemirror.js", "style": "lib/codemirror.css", "author": { diff --git a/src/edit/main.js b/src/edit/main.js index 1af55932..136fae01 100644 --- a/src/edit/main.js +++ b/src/edit/main.js @@ -66,4 +66,4 @@ import { addLegacyProps } from "./legacy.js" addLegacyProps(CodeMirror) -CodeMirror.version = "5.48.2" +CodeMirror.version = "5.48.3" From 85482b0377911befe59c90fc7d714f30b59c98e7 Mon Sep 17 00:00:00 2001 From: John Ryan Date: Fri, 19 Jul 2019 13:24:45 -0700 Subject: [PATCH 1619/1790] [clike mode] Style Dart class names as variable-2 --- mode/dart/dart.js | 9 +++++++++ 1 file changed, 9 insertions(+) diff --git a/mode/dart/dart.js b/mode/dart/dart.js index 0a66bf16..7b154c65 100644 --- a/mode/dart/dart.js +++ b/mode/dart/dart.js @@ -78,6 +78,15 @@ if (!stream.eat("*")) return false state.tokenize = tokenNestedComment(1) return state.tokenize(stream, state) + }, + token: function(stream, _, style) { + if (style == "variable") { + // Assume uppercase symbols are classes using variable-2 + var isUpper = RegExp('^[_$]*[A-Z][a-zA-Z0-9_$]*$','g'); + if (isUpper.test(stream.current())) { + return 'variable-2'; + } + } } } }); From b76594d5317f8c0c6988dfee7e922fe0f6dfa574 Mon Sep 17 00:00:00 2001 From: Guang Li <1152164+g5li@users.noreply.github.com> Date: Fri, 26 Jul 2019 15:24:35 +0800 Subject: [PATCH 1620/1790] [real-world uses] remove unmaintained codev.it The domain http://codev.it is Inaccessible, the [latest twitter](https://twitter.com/codevit) was post on Sep 2015. It's better to remove it. --- doc/realworld.html | 1 - 1 file changed, 1 deletion(-) diff --git a/doc/realworld.html b/doc/realworld.html index fdb278d4..ff5f815b 100644 --- a/doc/realworld.html +++ b/doc/realworld.html @@ -57,7 +57,6 @@

      CodeMirror real-world uses

    • Code School (online tech learning environment)
    • Code Snippets (WordPress snippet management plugin)
    • Code together (collaborative editing)
    • -
    • Codev (collaborative IDE)
    • Codevolve (programming lessons as-a-service)
    • CodeZample (code snippet sharing)
    • Codio (Web IDE)
    • From 97dcadaf027ade3c7167aab349c1627043bfc2e6 Mon Sep 17 00:00:00 2001 From: Jakub Vrana Date: Mon, 29 Jul 2019 10:44:16 +0200 Subject: [PATCH 1621/1790] [soy mode] Add {select} and {plural} --- mode/soy/soy.js | 2 ++ 1 file changed, 2 insertions(+) diff --git a/mode/soy/soy.js b/mode/soy/soy.js index b42d0da1..c33a69ce 100644 --- a/mode/soy/soy.js +++ b/mode/soy/soy.js @@ -26,6 +26,8 @@ "literal": { }, "msg": {}, "fallbackmsg": { noEndTag: true, reduceIndent: true}, + "select": {}, + "plural": {}, "let": { soyState: "var-def" }, "if": {}, "elseif": { noEndTag: true, reduceIndent: true}, From 22939a99272539b9fa9288c4961159eff7029d7c Mon Sep 17 00:00:00 2001 From: Tyler Makaro Date: Mon, 22 Jul 2019 10:21:22 -0700 Subject: [PATCH 1622/1790] fix scrolling with rulers --- lib/codemirror.css | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/lib/codemirror.css b/lib/codemirror.css index c7a8ae70..82c686e7 100644 --- a/lib/codemirror.css +++ b/lib/codemirror.css @@ -96,7 +96,7 @@ .CodeMirror-rulers { position: absolute; - left: 0; right: 0; top: -50px; bottom: -20px; + left: 0; right: 0; top: -50px; bottom: 0; overflow: hidden; } .CodeMirror-ruler { From ff978a0ce54208ea9c6b8dc3ae495f5ac492b4be Mon Sep 17 00:00:00 2001 From: edoroshenko Date: Tue, 30 Jul 2019 08:55:27 +0200 Subject: [PATCH 1623/1790] [foldgutter addon] Pass all options to the `foldCode` method ... to enable redefinition of the `widget` prop (#5955) --- addon/fold/foldgutter.js | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/addon/fold/foldgutter.js b/addon/fold/foldgutter.js index 988c67c4..63867ab2 100644 --- a/addon/fold/foldgutter.js +++ b/addon/fold/foldgutter.js @@ -100,7 +100,7 @@ if (gutter != opts.gutter) return; var folded = isFolded(cm, line); if (folded) folded.clear(); - else cm.foldCode(Pos(line, 0), opts.rangeFinder); + else cm.foldCode(Pos(line, 0), opts); } function onChange(cm) { From 65fab82fb178266e838d3f2c2cef216687744c34 Mon Sep 17 00:00:00 2001 From: Marijn Haverbeke Date: Thu, 1 Aug 2019 22:00:39 +0200 Subject: [PATCH 1624/1790] [julia mode] Support undescore digit separator Closes #5958 --- mode/julia/julia.js | 14 ++++++-------- 1 file changed, 6 insertions(+), 8 deletions(-) diff --git a/mode/julia/julia.js b/mode/julia/julia.js index 5873d8ca..547669af 100644 --- a/mode/julia/julia.js +++ b/mode/julia/julia.js @@ -187,15 +187,13 @@ CodeMirror.defineMode("julia", function(config, parserConf) { var imMatcher = RegExp(/^im\b/); var numberLiteral = false; // Floats - if (stream.match(/^\d*\.(?!\.)\d*([Eef][\+\-]?\d+)?/i)) { numberLiteral = true; } - if (stream.match(/^\d+\.(?!\.)\d*/)) { numberLiteral = true; } - if (stream.match(/^\.\d+/)) { numberLiteral = true; } - if (stream.match(/^0x\.[0-9a-f]+p[\+\-]?\d+/i)) { numberLiteral = true; } + if (stream.match(/^(?:(?:\d[_\d]*)?\.(?!\.)(?:\d[_\d]*)?|\d[_\d]*\.(?!\.)(?:\d[_\d]*))?([Eef][\+\-]?[_\d]+)?/i)) { numberLiteral = true; } + if (stream.match(/^0x\.[0-9a-f_]+p[\+\-]?[_\d]+/i)) { numberLiteral = true; } // Integers - if (stream.match(/^0x[0-9a-f]+/i)) { numberLiteral = true; } // Hex - if (stream.match(/^0b[01]+/i)) { numberLiteral = true; } // Binary - if (stream.match(/^0o[0-7]+/i)) { numberLiteral = true; } // Octal - if (stream.match(/^[1-9]\d*(e[\+\-]?\d+)?/)) { numberLiteral = true; } // Decimal + if (stream.match(/^0x[0-9a-f_]+/i)) { numberLiteral = true; } // Hex + if (stream.match(/^0b[01_]+/i)) { numberLiteral = true; } // Binary + if (stream.match(/^0o[0-7_]+/i)) { numberLiteral = true; } // Octal + if (stream.match(/^[1-9][_\d]*(e[\+\-]?\d+)?/)) { numberLiteral = true; } // Decimal // Zero by itself with no other piece of number. if (stream.match(/^0(?![\dx])/i)) { numberLiteral = true; } if (numberLiteral) { From a6a9696aa61ffd91872679efb12b80773f233056 Mon Sep 17 00:00:00 2001 From: Nils Knappmeier Date: Thu, 1 Aug 2019 20:02:40 +0200 Subject: [PATCH 1625/1790] [handlebars mode] add support for triple mustache tags --- mode/handlebars/handlebars.js | 4 ++++ mode/handlebars/index.html | 2 ++ 2 files changed, 6 insertions(+) diff --git a/mode/handlebars/handlebars.js b/mode/handlebars/handlebars.js index 93865b7a..fe8b3b44 100644 --- a/mode/handlebars/handlebars.js +++ b/mode/handlebars/handlebars.js @@ -13,10 +13,14 @@ CodeMirror.defineSimpleMode("handlebars-tags", { start: [ + { regex: /\{\{\{/, push: "handlebars_raw", token: "tag" }, { regex: /\{\{!--/, push: "dash_comment", token: "comment" }, { regex: /\{\{!/, push: "comment", token: "comment" }, { regex: /\{\{/, push: "handlebars", token: "tag" } ], + handlebars_raw: [ + { regex: /\}\}\}/, pop: true, token: "tag" }, + ], handlebars: [ { regex: /\}\}/, pop: true, token: "tag" }, diff --git a/mode/handlebars/index.html b/mode/handlebars/index.html index 13cfcf35..e2da2e76 100644 --- a/mode/handlebars/index.html +++ b/mode/handlebars/index.html @@ -41,6 +41,8 @@

      {{t 'article_list'}}

      {{! one line comment }} +{{{propertyContainingRawHtml}}} + {{#each articles}} {{~title}}

      {{excerpt body size=120 ellipsis=true}}

      From 80ccce062a4907c3f8789f157550e51d14da5c9e Mon Sep 17 00:00:00 2001 From: Lonnie Abelbeck Date: Mon, 5 Aug 2019 14:27:39 -0500 Subject: [PATCH 1626/1790] [asterisk mode] Add block-comment support --- mode/asterisk/asterisk.js | 22 +++++++++++++++++++++- 1 file changed, 21 insertions(+), 1 deletion(-) diff --git a/mode/asterisk/asterisk.js b/mode/asterisk/asterisk.js index 025a8b3e..a9bd4e3a 100644 --- a/mode/asterisk/asterisk.js +++ b/mode/asterisk/asterisk.js @@ -9,7 +9,7 @@ * Description: CodeMirror mode for Asterisk dialplan * * Created: 05/17/2012 09:20:25 PM - * Revision: none + * Revision: 08/05/2019 AstLinux Project: Support block-comments * * Author: Stas Kobzar (stas@modulis.ca), * Company: Modulis.ca Inc. @@ -67,7 +67,26 @@ CodeMirror.defineMode("asterisk", function() { var cur = ''; var ch = stream.next(); // comment + if (state.blockComment) { + if (ch == "-" && stream.match("-;", true)) { + state.blockComment = false; + } else if (stream.skipTo("--;")) { + stream.next(); + stream.next(); + stream.next(); + state.blockComment = false; + } else { + stream.skipToEnd(); + } + return "comment"; + } if(ch == ";") { + if (stream.match("--", true)) { + if (!stream.match("-", false)) { // Except ;--- is not a block comment + state.blockComment = true; + return "comment"; + } + } stream.skipToEnd(); return "comment"; } @@ -124,6 +143,7 @@ CodeMirror.defineMode("asterisk", function() { return { startState: function() { return { + blockComment: false, extenStart: false, extenSame: false, extenInclude: false, From 8e200e7e5887d4af4a41a3fe0543711b1e24a98b Mon Sep 17 00:00:00 2001 From: Marijn Haverbeke Date: Tue, 6 Aug 2019 09:36:10 +0200 Subject: [PATCH 1627/1790] [asterisk mode] Add comment syntax metadata --- mode/asterisk/asterisk.js | 6 +++++- 1 file changed, 5 insertions(+), 1 deletion(-) diff --git a/mode/asterisk/asterisk.js b/mode/asterisk/asterisk.js index a9bd4e3a..db67d43a 100644 --- a/mode/asterisk/asterisk.js +++ b/mode/asterisk/asterisk.js @@ -207,7 +207,11 @@ CodeMirror.defineMode("asterisk", function() { } return null; - } + }, + + blockCommentStart: jsonMode ? null : ";--", + blockCommentEnd: jsonMode ? null : "--;", + lineComment: jsonMode ? null : ";" }; }); From 52eb590e76f6ad99cd3bc3f060f58ad15d3f02b4 Mon Sep 17 00:00:00 2001 From: Marijn Haverbeke Date: Tue, 6 Aug 2019 09:59:57 +0200 Subject: [PATCH 1628/1790] [asterisk mode] Fix bogus patch --- mode/asterisk/asterisk.js | 6 +++--- 1 file changed, 3 insertions(+), 3 deletions(-) diff --git a/mode/asterisk/asterisk.js b/mode/asterisk/asterisk.js index db67d43a..49a72701 100644 --- a/mode/asterisk/asterisk.js +++ b/mode/asterisk/asterisk.js @@ -209,9 +209,9 @@ CodeMirror.defineMode("asterisk", function() { return null; }, - blockCommentStart: jsonMode ? null : ";--", - blockCommentEnd: jsonMode ? null : "--;", - lineComment: jsonMode ? null : ";" + blockCommentStart: ";--", + blockCommentEnd: "--;", + lineComment: ";" }; }); From 1580dd1a64f4849dd32830098ec8782e5614dd1d Mon Sep 17 00:00:00 2001 From: Tom McLaughlin Date: Tue, 6 Aug 2019 02:08:48 -0700 Subject: [PATCH 1629/1790] Make ".CodeMirror pre" selector more specific --- lib/codemirror.css | 9 ++++++--- src/measurement/position_measurement.js | 6 +++--- 2 files changed, 9 insertions(+), 6 deletions(-) diff --git a/lib/codemirror.css b/lib/codemirror.css index 82c686e7..5406f238 100644 --- a/lib/codemirror.css +++ b/lib/codemirror.css @@ -13,7 +13,8 @@ .CodeMirror-lines { padding: 4px 0; /* Vertical padding around content */ } -.CodeMirror pre { +.CodeMirror pre.CodeMirror-line, +.CodeMirror pre.CodeMirror-measure-elem { padding: 0 4px; /* Horizontal padding of content */ } @@ -236,7 +237,8 @@ div.CodeMirror span.CodeMirror-nonmatchingbracket {color: #a22;} cursor: text; min-height: 1px; /* prevents collapsing before first draw */ } -.CodeMirror pre { +.CodeMirror pre.CodeMirror-line, +.CodeMirror pre.CodeMirror-measure-elem { /* Reset some styles that the rest of the page might have set */ -moz-border-radius: 0; -webkit-border-radius: 0; border-radius: 0; border-width: 0; @@ -255,7 +257,8 @@ div.CodeMirror span.CodeMirror-nonmatchingbracket {color: #a22;} -webkit-font-variant-ligatures: contextual; font-variant-ligatures: contextual; } -.CodeMirror-wrap pre { +.CodeMirror-wrap pre.CodeMirror-line, +.CodeMirror-wrap pre.CodeMirror-measure-elem { word-wrap: break-word; white-space: pre-wrap; word-break: normal; diff --git a/src/measurement/position_measurement.js b/src/measurement/position_measurement.js index eb782878..fa8b4c31 100644 --- a/src/measurement/position_measurement.js +++ b/src/measurement/position_measurement.js @@ -18,7 +18,7 @@ export function paddingTop(display) {return display.lineSpace.offsetTop} export function paddingVert(display) {return display.mover.offsetHeight - display.lineSpace.offsetHeight} export function paddingH(display) { if (display.cachedPaddingH) return display.cachedPaddingH - let e = removeChildrenAndAdd(display.measure, elt("pre", "x")) + let e = removeChildrenAndAdd(display.measure, elt("pre", "x", "CodeMirror-measure-elem")) let style = window.getComputedStyle ? window.getComputedStyle(e) : e.currentStyle let data = {left: parseInt(style.paddingLeft), right: parseInt(style.paddingRight)} if (!isNaN(data.left) && !isNaN(data.right)) display.cachedPaddingH = data @@ -585,7 +585,7 @@ let measureText export function textHeight(display) { if (display.cachedTextHeight != null) return display.cachedTextHeight if (measureText == null) { - measureText = elt("pre") + measureText = elt("pre", null, "CodeMirror-measure-elem") // Measure a bunch of lines, for browsers that compute // fractional heights. for (let i = 0; i < 49; ++i) { @@ -605,7 +605,7 @@ export function textHeight(display) { export function charWidth(display) { if (display.cachedCharWidth != null) return display.cachedCharWidth let anchor = elt("span", "xxxxxxxxxx") - let pre = elt("pre", [anchor]) + let pre = elt("pre", [anchor], "CodeMirror-measure-elem") removeChildrenAndAdd(display.measure, pre) let rect = anchor.getBoundingClientRect(), width = (rect.right - rect.left) / 10 if (width > 2) display.cachedCharWidth = width From dbc88322c9b303791ab97812bf4e064ca41f25b7 Mon Sep 17 00:00:00 2001 From: Marijn Haverbeke Date: Thu, 8 Aug 2019 07:09:05 +0200 Subject: [PATCH 1630/1790] Fix bug in coordsChar when there's a collapsed range at start of line Issue #5966 --- src/measurement/position_measurement.js | 10 +++++----- 1 file changed, 5 insertions(+), 5 deletions(-) diff --git a/src/measurement/position_measurement.js b/src/measurement/position_measurement.js index fa8b4c31..73b081ba 100644 --- a/src/measurement/position_measurement.js +++ b/src/measurement/position_measurement.js @@ -412,7 +412,7 @@ export function estimateCoords(cm, pos) { function PosWithInfo(line, ch, sticky, outside, xRel) { let pos = Pos(line, ch, sticky) pos.xRel = xRel - if (outside) pos.outside = true + if (outside) pos.outside = outside return pos } @@ -421,16 +421,16 @@ function PosWithInfo(line, ch, sticky, outside, xRel) { export function coordsChar(cm, x, y) { let doc = cm.doc y += cm.display.viewOffset - if (y < 0) return PosWithInfo(doc.first, 0, null, true, -1) + if (y < 0) return PosWithInfo(doc.first, 0, null, -1, -1) let lineN = lineAtHeight(doc, y), last = doc.first + doc.size - 1 if (lineN > last) - return PosWithInfo(doc.first + doc.size - 1, getLine(doc, last).text.length, null, true, 1) + return PosWithInfo(doc.first + doc.size - 1, getLine(doc, last).text.length, null, 1, 1) if (x < 0) x = 0 let lineObj = getLine(doc, lineN) for (;;) { let found = coordsCharInner(cm, lineObj, lineN, x, y) - let collapsed = collapsedSpanAround(lineObj, found.ch + (found.xRel > 0 ? 1 : 0)) + let collapsed = collapsedSpanAround(lineObj, found.ch + (found.xRel > 0 || found.outside > 0 ? 1 : 0)) if (!collapsed) return found let rangeEnd = collapsed.find(1) if (rangeEnd.line == lineN) return rangeEnd @@ -518,7 +518,7 @@ function coordsCharInner(cm, lineObj, lineNo, x, y) { // base X position let coords = cursorCoords(cm, Pos(lineNo, ch, sticky), "line", lineObj, preparedMeasure) baseX = coords.left - outside = y < coords.top || y >= coords.bottom + outside = y < coords.top ? -1 : y >= coords.bottom ? 1 : 0 } ch = skipExtendingChars(lineObj.text, ch, 1) From 5b341e12497045388ecb3c6053a159f9851f7b8c Mon Sep 17 00:00:00 2001 From: raymondf Date: Wed, 14 Aug 2019 16:38:33 +0930 Subject: [PATCH 1631/1790] significant speed optimisation when large amount of lines are folded --- addon/fold/foldgutter.js | 9 +++++++-- 1 file changed, 7 insertions(+), 2 deletions(-) diff --git a/addon/fold/foldgutter.js b/addon/fold/foldgutter.js index 63867ab2..fcb5021e 100644 --- a/addon/fold/foldgutter.js +++ b/addon/fold/foldgutter.js @@ -51,8 +51,13 @@ function isFolded(cm, line) { var marks = cm.findMarks(Pos(line, 0), Pos(line + 1, 0)); - for (var i = 0; i < marks.length; ++i) - if (marks[i].__isFold && marks[i].find().from.line == line) return marks[i]; + for (var i = 0; i < marks.length; ++i) { + if (marks[i].__isFold) { + var fromPos = marks[i].find(-1); + if (fromPos && fromPos.line === line) + return marks[i]; + } + } } function marker(spec) { From 19c620b94f5bba7cdb19b037a2fb4071fce5036d Mon Sep 17 00:00:00 2001 From: Marijn Haverbeke Date: Sat, 17 Aug 2019 10:23:34 +0200 Subject: [PATCH 1632/1790] Make sure the cantEdit flag is reset when setValue is called Closes #5974 --- src/model/Doc.js | 1 + 1 file changed, 1 insertion(+) diff --git a/src/model/Doc.js b/src/model/Doc.js index 956f752b..f9ac3453 100644 --- a/src/model/Doc.js +++ b/src/model/Doc.js @@ -75,6 +75,7 @@ Doc.prototype = createObj(BranchChunk.prototype, { let top = Pos(this.first, 0), last = this.first + this.size - 1 makeChange(this, {from: top, to: Pos(last, getLine(this, last).text.length), text: this.splitLines(code), origin: "setValue", full: true}, true) + this.cantEdit = false if (this.cm) scrollToCoords(this.cm, 0, 0) setSelection(this, simpleSelection(top), sel_dontScroll) }), From dc931099b52ff7f95668be7cd16402e86a7f8386 Mon Sep 17 00:00:00 2001 From: Tom McLaughlin Date: Fri, 16 Aug 2019 20:18:45 -0700 Subject: [PATCH 1633/1790] Update PR to use .CodeMirror-line-like class --- addon/display/placeholder.js | 2 +- lib/codemirror.css | 6 +++--- src/measurement/position_measurement.js | 6 +++--- 3 files changed, 7 insertions(+), 7 deletions(-) diff --git a/addon/display/placeholder.js b/addon/display/placeholder.js index 1a3fa337..4eabe3d9 100644 --- a/addon/display/placeholder.js +++ b/addon/display/placeholder.js @@ -39,7 +39,7 @@ var elt = cm.state.placeholder = document.createElement("pre"); elt.style.cssText = "height: 0; overflow: visible"; elt.style.direction = cm.getOption("direction"); - elt.className = "CodeMirror-placeholder"; + elt.className = "CodeMirror-placeholder CodeMirror-line-like"; var placeHolder = cm.getOption("placeholder") if (typeof placeHolder == "string") placeHolder = document.createTextNode(placeHolder) elt.appendChild(placeHolder) diff --git a/lib/codemirror.css b/lib/codemirror.css index 5406f238..bc910fb9 100644 --- a/lib/codemirror.css +++ b/lib/codemirror.css @@ -14,7 +14,7 @@ padding: 4px 0; /* Vertical padding around content */ } .CodeMirror pre.CodeMirror-line, -.CodeMirror pre.CodeMirror-measure-elem { +.CodeMirror pre.CodeMirror-line-like { padding: 0 4px; /* Horizontal padding of content */ } @@ -238,7 +238,7 @@ div.CodeMirror span.CodeMirror-nonmatchingbracket {color: #a22;} min-height: 1px; /* prevents collapsing before first draw */ } .CodeMirror pre.CodeMirror-line, -.CodeMirror pre.CodeMirror-measure-elem { +.CodeMirror pre.CodeMirror-line-like { /* Reset some styles that the rest of the page might have set */ -moz-border-radius: 0; -webkit-border-radius: 0; border-radius: 0; border-width: 0; @@ -258,7 +258,7 @@ div.CodeMirror span.CodeMirror-nonmatchingbracket {color: #a22;} font-variant-ligatures: contextual; } .CodeMirror-wrap pre.CodeMirror-line, -.CodeMirror-wrap pre.CodeMirror-measure-elem { +.CodeMirror-wrap pre.CodeMirror-line-like { word-wrap: break-word; white-space: pre-wrap; word-break: normal; diff --git a/src/measurement/position_measurement.js b/src/measurement/position_measurement.js index 73b081ba..18ec11df 100644 --- a/src/measurement/position_measurement.js +++ b/src/measurement/position_measurement.js @@ -18,7 +18,7 @@ export function paddingTop(display) {return display.lineSpace.offsetTop} export function paddingVert(display) {return display.mover.offsetHeight - display.lineSpace.offsetHeight} export function paddingH(display) { if (display.cachedPaddingH) return display.cachedPaddingH - let e = removeChildrenAndAdd(display.measure, elt("pre", "x", "CodeMirror-measure-elem")) + let e = removeChildrenAndAdd(display.measure, elt("pre", "x", "CodeMirror-line-like")) let style = window.getComputedStyle ? window.getComputedStyle(e) : e.currentStyle let data = {left: parseInt(style.paddingLeft), right: parseInt(style.paddingRight)} if (!isNaN(data.left) && !isNaN(data.right)) display.cachedPaddingH = data @@ -585,7 +585,7 @@ let measureText export function textHeight(display) { if (display.cachedTextHeight != null) return display.cachedTextHeight if (measureText == null) { - measureText = elt("pre", null, "CodeMirror-measure-elem") + measureText = elt("pre", null, "CodeMirror-line-like") // Measure a bunch of lines, for browsers that compute // fractional heights. for (let i = 0; i < 49; ++i) { @@ -605,7 +605,7 @@ export function textHeight(display) { export function charWidth(display) { if (display.cachedCharWidth != null) return display.cachedCharWidth let anchor = elt("span", "xxxxxxxxxx") - let pre = elt("pre", [anchor], "CodeMirror-measure-elem") + let pre = elt("pre", [anchor], "CodeMirror-line-like") removeChildrenAndAdd(display.measure, pre) let rect = anchor.getBoundingClientRect(), width = (rect.right - rect.left) / 10 if (width > 2) display.cachedCharWidth = width From a5df839fc3e26ab6dfb02061aa3347c5b8532081 Mon Sep 17 00:00:00 2001 From: Marijn Haverbeke Date: Mon, 19 Aug 2019 12:40:50 +0200 Subject: [PATCH 1634/1790] Properly detect changes that should reset cantEdit flag Issue #5974 --- src/model/Doc.js | 1 - src/model/changes.js | 3 +++ 2 files changed, 3 insertions(+), 1 deletion(-) diff --git a/src/model/Doc.js b/src/model/Doc.js index f9ac3453..956f752b 100644 --- a/src/model/Doc.js +++ b/src/model/Doc.js @@ -75,7 +75,6 @@ Doc.prototype = createObj(BranchChunk.prototype, { let top = Pos(this.first, 0), last = this.first + this.size - 1 makeChange(this, {from: top, to: Pos(last, getLine(this, last).text.length), text: this.splitLines(code), origin: "setValue", full: true}, true) - this.cantEdit = false if (this.cm) scrollToCoords(this.cm, 0, 0) setSelection(this, simpleSelection(top), sel_dontScroll) }), diff --git a/src/model/changes.js b/src/model/changes.js index 7a688837..7c600f6f 100644 --- a/src/model/changes.js +++ b/src/model/changes.js @@ -202,6 +202,9 @@ function makeChangeSingleDoc(doc, change, selAfter, spans) { if (doc.cm) makeChangeSingleDocInEditor(doc.cm, change, spans) else updateDoc(doc, change, spans) setSelectionNoUndo(doc, selAfter, sel_dontScroll) + + if (doc.cantEdit && skipAtomic(doc, Pos(doc.firstLine(), 0))) + doc.cantEdit = false } // Handle the interaction of a change to a document with the editor From 784d4a8501c4d5774aa0823a52540cfd5269da20 Mon Sep 17 00:00:00 2001 From: Marijn Haverbeke Date: Mon, 19 Aug 2019 14:07:59 +0200 Subject: [PATCH 1635/1790] Fix forgotten import --- src/model/changes.js | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/model/changes.js b/src/model/changes.js index 7c600f6f..48d2f6bb 100644 --- a/src/model/changes.js +++ b/src/model/changes.js @@ -15,7 +15,7 @@ import { changeEnd, computeSelAfterChange } from "./change_measurement.js" import { isWholeLineUpdate, linkedDocs, updateDoc } from "./document_data.js" import { addChangeToHistory, historyChangeFromChange, mergeOldSpans, pushSelectionToHistory } from "./history.js" import { Range, Selection } from "./selection.js" -import { setSelection, setSelectionNoUndo } from "./selection_updates.js" +import { setSelection, setSelectionNoUndo, skipAtomic } from "./selection_updates.js" // UPDATING From 862be946f288023d335d028971f00f8ba84a1812 Mon Sep 17 00:00:00 2001 From: Marijn Haverbeke Date: Mon, 19 Aug 2019 14:09:50 +0200 Subject: [PATCH 1636/1790] [yonce theme] Don't use rgb with four arguments Closes #5951 --- theme/yonce.css | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/theme/yonce.css b/theme/yonce.css index e01c0c3b..975f0788 100644 --- a/theme/yonce.css +++ b/theme/yonce.css @@ -24,8 +24,8 @@ .cm-s-yonce .CodeMirror-activeline .CodeMirror-linenumber.CodeMirror-gutter-elt { background: #1C1C1C; color: #fc4384; } .cm-s-yonce .CodeMirror-linenumber { color: #777; } .cm-s-yonce .CodeMirror-cursor { border-left: 2px solid #FC4384; } -.cm-s-yonce .cm-searching { background: rgb(243, 155, 53, .3) !important; outline: 1px solid #F39B35; } -.cm-s-yonce .cm-searching.CodeMirror-selectedtext { background: rgb(243, 155, 53, .7) !important; color: white; } +.cm-s-yonce .cm-searching { background: rgba(243, 155, 53, .3) !important; outline: 1px solid #F39B35; } +.cm-s-yonce .cm-searching.CodeMirror-selectedtext { background: rgba(243, 155, 53, .7) !important; color: white; } .cm-s-yonce .cm-keyword { color: #00A7AA; } /**/ .cm-s-yonce .cm-atom { color: #F39B35; } From 7ecf62faa30fb0de99b76609f16c58a7bf032820 Mon Sep 17 00:00:00 2001 From: Marijn Haverbeke Date: Mon, 19 Aug 2019 14:18:23 +0200 Subject: [PATCH 1637/1790] Mark version 5.48.4 --- AUTHORS | 7 +++++++ CHANGELOG.md | 16 ++++++++++++++++ doc/manual.html | 2 +- doc/releases.html | 11 +++++++++++ index.html | 2 +- package.json | 2 +- src/edit/main.js | 2 +- 7 files changed, 38 insertions(+), 4 deletions(-) diff --git a/AUTHORS b/AUTHORS index ab98a58c..b73067ce 100644 --- a/AUTHORS +++ b/AUTHORS @@ -222,6 +222,7 @@ Duncan Lilley duralog dwelle eborden +edoroshenko edsharp ekhaled Elisée @@ -284,6 +285,7 @@ Grant Skinner greengiant Gregory Koberger Grzegorz Mazur +Guang Li Guan Gui Guillaume Massé Guillaume Massé @@ -374,6 +376,7 @@ John Connor John-David Dalton John Engler John Lees-Miller +John Ryan John Snelson John Van Der Loo Jon Ander Peñalba @@ -451,6 +454,7 @@ Lior Shub LloydMilligan LM lochel +Lonnie Abelbeck Lorenzo Simionato Lorenzo Stoakes Louis Mauchet @@ -585,6 +589,7 @@ Nikita Vasilyev Nikolaj Kappler Nikolay Kostov nilp0inter +Nils Knappmeier Nisarg Jhaveri nlwillia noragrossman @@ -644,6 +649,7 @@ Randy Luecke Raphael Amorim Rasmus Erik Voel Jensen Rasmus Schultz +raymondf Raymond Hill ray ratchup Ray Ratchup @@ -778,6 +784,7 @@ TSUYUSATO Kitsune Tugrul Elmas twifkak Tyler Long +Tyler Makaro Vadim Dyachenko Vadzim Ramanenka Vaibhav Sagar diff --git a/CHANGELOG.md b/CHANGELOG.md index 061c7215..7256b863 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -1,3 +1,19 @@ +## 5.48.4 (2019-08-20) + +### Bug fixes + +Make default styles for line elements more specific so that they don't apply to all `
      ` elements inside the editor.
      +
      +Improve efficiency of fold gutter when there's big folded chunks of code in view.
      +
      +Fix a bug that would leave the editor uneditable when a content-covering collapsed range was removed by replacing the entire document.
      +
      +[julia mode](https://codemirror.net/mode/julia/): Support number separators.
      +
      +[asterisk mode](https://codemirror.net/mode/asterisk/): Improve comment support.
      +
      +[handlebars mode](https://codemirror.net/mode/handlebars/): Support triple-brace tags.
      +
       ## 5.48.2 (2019-07-20)
       
       ### Bug fixes
      diff --git a/doc/manual.html b/doc/manual.html
      index 5b2343a6..6a96a66d 100644
      --- a/doc/manual.html
      +++ b/doc/manual.html
      @@ -69,7 +69,7 @@
       

      User manual and reference guide - version 5.48.3 + version 5.48.4

      CodeMirror is a code-editor component that can be embedded in diff --git a/doc/releases.html b/doc/releases.html index 11d94305..73235e85 100644 --- a/doc/releases.html +++ b/doc/releases.html @@ -30,6 +30,17 @@

      Release notes and version history

      Version 5.x

      +

      20-08-2019: Version 5.48.4:

      + +
        +
      • Make default styles for line elements more specific so that they don’t apply to all <pre> elements inside the editor.
      • +
      • Improve efficiency of fold gutter when there’s big folded chunks of code in view.
      • +
      • Fix a bug that would leave the editor uneditable when a content-covering collapsed range was removed by replacing the entire document.
      • +
      • julia mode: Support number separators.
      • +
      • asterisk mode: Improve comment support.
      • +
      • handlebars mode: Support triple-brace tags.
      • +
      +

      20-07-2019: Version 5.48.2:

        diff --git a/index.html b/index.html index 90c429b9..95b1afb4 100644 --- a/index.html +++ b/index.html @@ -99,7 +99,7 @@

        This is CodeMirror

      - Get the current version: 5.48.2.
      + Get the current version: 5.48.4.
      You can see the code,
      read the release notes,
      or study the user manual. diff --git a/package.json b/package.json index ac483fee..7186b02b 100644 --- a/package.json +++ b/package.json @@ -1,6 +1,6 @@ { "name": "codemirror", - "version": "5.48.3", + "version": "5.48.4", "main": "lib/codemirror.js", "style": "lib/codemirror.css", "author": { diff --git a/src/edit/main.js b/src/edit/main.js index 136fae01..92478df3 100644 --- a/src/edit/main.js +++ b/src/edit/main.js @@ -66,4 +66,4 @@ import { addLegacyProps } from "./legacy.js" addLegacyProps(CodeMirror) -CodeMirror.version = "5.48.3" +CodeMirror.version = "5.48.4" From ad0daf2f18c67edbf9498a4e72af3f626809d65b Mon Sep 17 00:00:00 2001 From: Jakub Vrana Date: Tue, 20 Aug 2019 10:13:52 +0200 Subject: [PATCH 1638/1790] [soy mode] Fix setting kind on invalid tags --- mode/soy/soy.js | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/mode/soy/soy.js b/mode/soy/soy.js index c33a69ce..c39516a9 100644 --- a/mode/soy/soy.js +++ b/mode/soy/soy.js @@ -288,7 +288,7 @@ state.soyState.pop(); return "keyword"; } else if (stream.match(/^([\w?]+)(?==)/)) { - if (stream.current() == "kind" && (match = stream.match(/^="([^"]+)/, false))) { + if (state.context && state.context.tag == tagName && stream.current() == "kind" && (match = stream.match(/^="([^"]+)/, false))) { var kind = match[1]; state.context.kind = kind; var mode = modes[kind] || modes.html; From 650e975ad2c8bc3ac16257598ec961a004272185 Mon Sep 17 00:00:00 2001 From: prendota Date: Wed, 21 Aug 2019 09:57:03 +0300 Subject: [PATCH 1639/1790] [clike] support nested comments in kotlin mode --- mode/clike/clike.js | 5 +++++ 1 file changed, 5 insertions(+) diff --git a/mode/clike/clike.js b/mode/clike/clike.js index 4c8b9937..0879a0bd 100644 --- a/mode/clike/clike.js +++ b/mode/clike/clike.js @@ -676,6 +676,11 @@ CodeMirror.defineMode("clike", function(config, parserConfig) { state.tokenize = tokenKotlinString(stream.match('""')); return state.tokenize(stream, state); }, + "/": function(stream, state) { + if (!stream.eat("*")) return false; + state.tokenize = tokenNestedComment(1); + return state.tokenize(stream, state) + }, indent: function(state, ctx, textAfter, indentUnit) { var firstChar = textAfter && textAfter.charAt(0); if ((state.prevToken == "}" || state.prevToken == ")") && textAfter == "") From 448dff26b60712dd8f4ceb1d14e26624612e51e9 Mon Sep 17 00:00:00 2001 From: Mattia Astorino Date: Fri, 23 Aug 2019 15:37:50 +0200 Subject: [PATCH 1640/1790] [material theme] Update to the official version, add darker, palenight, ocean variants --- demo/theme.html | 8 +- theme/material-darker.css | 135 ++++++++++++++++++++++++++++++ theme/material-ocean.css | 135 ++++++++++++++++++++++++++++++ theme/material-palenight.css | 135 ++++++++++++++++++++++++++++++ theme/material.css | 156 ++++++++++++++++++++++++++--------- 5 files changed, 531 insertions(+), 38 deletions(-) create mode 100644 theme/material-darker.css create mode 100644 theme/material-ocean.css create mode 100644 theme/material-palenight.css diff --git a/demo/theme.html b/demo/theme.html index 7bbc7a49..723c3efa 100644 --- a/demo/theme.html +++ b/demo/theme.html @@ -29,6 +29,9 @@ + + + @@ -124,6 +127,9 @@

      Theme Demo

      + + + @@ -181,4 +187,4 @@

      Theme Demo

      if (theme) { input.value = theme; selectTheme(); } }); - + \ No newline at end of file diff --git a/theme/material-darker.css b/theme/material-darker.css new file mode 100644 index 00000000..45b64efb --- /dev/null +++ b/theme/material-darker.css @@ -0,0 +1,135 @@ +/* + Name: material + Author: Mattia Astorino (http://github.com/equinusocio) + Website: https://material-theme.site/ +*/ + +.cm-s-material-darker.CodeMirror { + background-color: #212121; + color: #EEFFFF; +} + +.cm-s-material-darker .CodeMirror-gutters { + background: #212121; + color: #545454; + border: none; +} + +.cm-s-material-darker .CodeMirror-guttermarker, +.cm-s-material-darker .CodeMirror-guttermarker-subtle, +.cm-s-material-darker .CodeMirror-linenumber { + color: #545454; +} + +.cm-s-material-darker .CodeMirror-cursor { + border-left: 1px solid #FFCC00; +} + +.cm-s-material-darker div.CodeMirror-selected { + background: rgba(97, 97, 97, 0.2); +} + +.cm-s-material-darker.CodeMirror-focused div.CodeMirror-selected { + background: rgba(97, 97, 97, 0.2); +} + +.cm-s-material-darker .CodeMirror-line::selection, +.cm-s-material-darker .CodeMirror-line>span::selection, +.cm-s-material-darker .CodeMirror-line>span>span::selection { + background: rgba(128, 203, 196, 0.2); +} + +.cm-s-material-darker .CodeMirror-line::-moz-selection, +.cm-s-material-darker .CodeMirror-line>span::-moz-selection, +.cm-s-material-darker .CodeMirror-line>span>span::-moz-selection { + background: rgba(128, 203, 196, 0.2); +} + +.cm-s-material-darker .CodeMirror-activeline-background { + background: rgba(0, 0, 0, 0.5); +} + +.cm-s-material-darker .cm-keyword { + color: #C792EA; +} + +.cm-s-material-darker .cm-operator { + color: #89DDFF; +} + +.cm-s-material-darker .cm-variable-2 { + color: #EEFFFF; +} + +.cm-s-material-darker .cm-variable-3, +.cm-s-material-darker .cm-type { + color: #f07178; +} + +.cm-s-material-darker .cm-builtin { + color: #FFCB6B; +} + +.cm-s-material-darker .cm-atom { + color: #F78C6C; +} + +.cm-s-material-darker .cm-number { + color: #FF5370; +} + +.cm-s-material-darker .cm-def { + color: #82AAFF; +} + +.cm-s-material-darker .cm-string { + color: #C3E88D; +} + +.cm-s-material-darker .cm-string-2 { + color: #f07178; +} + +.cm-s-material-darker .cm-comment { + color: #545454; +} + +.cm-s-material-darker .cm-variable { + color: #f07178; +} + +.cm-s-material-darker .cm-tag { + color: #FF5370; +} + +.cm-s-material-darker .cm-meta { + color: #FFCB6B; +} + +.cm-s-material-darker .cm-attribute { + color: #C792EA; +} + +.cm-s-material-darker .cm-property { + color: #C792EA; +} + +.cm-s-material-darker .cm-qualifier { + color: #DECB6B; +} + +.cm-s-material-darker .cm-variable-3, +.cm-s-material-darker .cm-type { + color: #DECB6B; +} + + +.cm-s-material-darker .cm-error { + color: rgba(255, 255, 255, 1.0); + background-color: #FF5370; +} + +.cm-s-material-darker .CodeMirror-matchingbracket { + text-decoration: underline; + color: white !important; +} \ No newline at end of file diff --git a/theme/material-ocean.css b/theme/material-ocean.css new file mode 100644 index 00000000..86a6f3cd --- /dev/null +++ b/theme/material-ocean.css @@ -0,0 +1,135 @@ +/* + Name: material + Author: Mattia Astorino (http://github.com/equinusocio) + Website: https://material-theme.site/ +*/ + +.cm-s-material-ocean.CodeMirror { + background-color: #0F111A; + color: #8F93A2; +} + +.cm-s-material-ocean .CodeMirror-gutters { + background: #0F111A; + color: #464B5D; + border: none; +} + +.cm-s-material-ocean .CodeMirror-guttermarker, +.cm-s-material-ocean .CodeMirror-guttermarker-subtle, +.cm-s-material-ocean .CodeMirror-linenumber { + color: #464B5D; +} + +.cm-s-material-ocean .CodeMirror-cursor { + border-left: 1px solid #FFCC00; +} + +.cm-s-material-ocean div.CodeMirror-selected { + background: rgba(113, 124, 180, 0.2); +} + +.cm-s-material-ocean.CodeMirror-focused div.CodeMirror-selected { + background: rgba(113, 124, 180, 0.2); +} + +.cm-s-material-ocean .CodeMirror-line::selection, +.cm-s-material-ocean .CodeMirror-line>span::selection, +.cm-s-material-ocean .CodeMirror-line>span>span::selection { + background: rgba(128, 203, 196, 0.2); +} + +.cm-s-material-ocean .CodeMirror-line::-moz-selection, +.cm-s-material-ocean .CodeMirror-line>span::-moz-selection, +.cm-s-material-ocean .CodeMirror-line>span>span::-moz-selection { + background: rgba(128, 203, 196, 0.2); +} + +.cm-s-material-ocean .CodeMirror-activeline-background { + background: rgba(0, 0, 0, 0.5); +} + +.cm-s-material-ocean .cm-keyword { + color: #C792EA; +} + +.cm-s-material-ocean .cm-operator { + color: #89DDFF; +} + +.cm-s-material-ocean .cm-variable-2 { + color: #EEFFFF; +} + +.cm-s-material-ocean .cm-variable-3, +.cm-s-material-ocean .cm-type { + color: #f07178; +} + +.cm-s-material-ocean .cm-builtin { + color: #FFCB6B; +} + +.cm-s-material-ocean .cm-atom { + color: #F78C6C; +} + +.cm-s-material-ocean .cm-number { + color: #FF5370; +} + +.cm-s-material-ocean .cm-def { + color: #82AAFF; +} + +.cm-s-material-ocean .cm-string { + color: #C3E88D; +} + +.cm-s-material-ocean .cm-string-2 { + color: #f07178; +} + +.cm-s-material-ocean .cm-comment { + color: #464B5D; +} + +.cm-s-material-ocean .cm-variable { + color: #f07178; +} + +.cm-s-material-ocean .cm-tag { + color: #FF5370; +} + +.cm-s-material-ocean .cm-meta { + color: #FFCB6B; +} + +.cm-s-material-ocean .cm-attribute { + color: #C792EA; +} + +.cm-s-material-ocean .cm-property { + color: #C792EA; +} + +.cm-s-material-ocean .cm-qualifier { + color: #DECB6B; +} + +.cm-s-material-ocean .cm-variable-3, +.cm-s-material-ocean .cm-type { + color: #DECB6B; +} + + +.cm-s-material-ocean .cm-error { + color: rgba(255, 255, 255, 1.0); + background-color: #FF5370; +} + +.cm-s-material-ocean .CodeMirror-matchingbracket { + text-decoration: underline; + color: white !important; +} \ No newline at end of file diff --git a/theme/material-palenight.css b/theme/material-palenight.css new file mode 100644 index 00000000..66d53dd3 --- /dev/null +++ b/theme/material-palenight.css @@ -0,0 +1,135 @@ +/* + Name: material + Author: Mattia Astorino (http://github.com/equinusocio) + Website: https://material-theme.site/ +*/ + +.cm-s-material-palenight.CodeMirror { + background-color: #292D3E; + color: #A6ACCD; +} + +.cm-s-material-palenight .CodeMirror-gutters { + background: #292D3E; + color: #676E95; + border: none; +} + +.cm-s-material-palenight .CodeMirror-guttermarker, +.cm-s-material-palenight .CodeMirror-guttermarker-subtle, +.cm-s-material-palenight .CodeMirror-linenumber { + color: #676E95; +} + +.cm-s-material-palenight .CodeMirror-cursor { + border-left: 1px solid #FFCC00; +} + +.cm-s-material-palenight div.CodeMirror-selected { + background: rgba(113, 124, 180, 0.2); +} + +.cm-s-material-palenight.CodeMirror-focused div.CodeMirror-selected { + background: rgba(113, 124, 180, 0.2); +} + +.cm-s-material-palenight .CodeMirror-line::selection, +.cm-s-material-palenight .CodeMirror-line>span::selection, +.cm-s-material-palenight .CodeMirror-line>span>span::selection { + background: rgba(128, 203, 196, 0.2); +} + +.cm-s-material-palenight .CodeMirror-line::-moz-selection, +.cm-s-material-palenight .CodeMirror-line>span::-moz-selection, +.cm-s-material-palenight .CodeMirror-line>span>span::-moz-selection { + background: rgba(128, 203, 196, 0.2); +} + +.cm-s-material-palenight .CodeMirror-activeline-background { + background: rgba(0, 0, 0, 0.5); +} + +.cm-s-material-palenight .cm-keyword { + color: #C792EA; +} + +.cm-s-material-palenight .cm-operator { + color: #89DDFF; +} + +.cm-s-material-palenight .cm-variable-2 { + color: #EEFFFF; +} + +.cm-s-material-palenight .cm-variable-3, +.cm-s-material-palenight .cm-type { + color: #f07178; +} + +.cm-s-material-palenight .cm-builtin { + color: #FFCB6B; +} + +.cm-s-material-palenight .cm-atom { + color: #F78C6C; +} + +.cm-s-material-palenight .cm-number { + color: #FF5370; +} + +.cm-s-material-palenight .cm-def { + color: #82AAFF; +} + +.cm-s-material-palenight .cm-string { + color: #C3E88D; +} + +.cm-s-material-palenight .cm-string-2 { + color: #f07178; +} + +.cm-s-material-palenight .cm-comment { + color: #676E95; +} + +.cm-s-material-palenight .cm-variable { + color: #f07178; +} + +.cm-s-material-palenight .cm-tag { + color: #FF5370; +} + +.cm-s-material-palenight .cm-meta { + color: #FFCB6B; +} + +.cm-s-material-palenight .cm-attribute { + color: #C792EA; +} + +.cm-s-material-palenight .cm-property { + color: #C792EA; +} + +.cm-s-material-palenight .cm-qualifier { + color: #DECB6B; +} + +.cm-s-material-palenight .cm-variable-3, +.cm-s-material-palenight .cm-type { + color: #DECB6B; +} + + +.cm-s-material-palenight .cm-error { + color: rgba(255, 255, 255, 1.0); + background-color: #FF5370; +} + +.cm-s-material-palenight .CodeMirror-matchingbracket { + text-decoration: underline; + color: white !important; +} \ No newline at end of file diff --git a/theme/material.css b/theme/material.css index 84962a24..9ac17a36 100644 --- a/theme/material.css +++ b/theme/material.css @@ -1,53 +1,135 @@ /* - - Name: material - Author: Michael Kaminsky (http://github.com/mkaminsky11) - - Original material color scheme by Mattia Astorino (https://github.com/equinusocio/material-theme) - + Name: material + Author: Mattia Astorino (http://github.com/equinusocio) + Website: https://material-theme.site/ */ .cm-s-material.CodeMirror { background-color: #263238; - color: rgba(233, 237, 237, 1); + color: #EEFFFF; } + .cm-s-material .CodeMirror-gutters { background: #263238; - color: rgb(83,127,126); + color: #546E7A; border: none; } -.cm-s-material .CodeMirror-guttermarker, .cm-s-material .CodeMirror-guttermarker-subtle, .cm-s-material .CodeMirror-linenumber { color: rgb(83,127,126); } -.cm-s-material .CodeMirror-cursor { border-left: 1px solid #f8f8f0; } -.cm-s-material div.CodeMirror-selected { background: rgba(255, 255, 255, 0.15); } -.cm-s-material.CodeMirror-focused div.CodeMirror-selected { background: rgba(255, 255, 255, 0.10); } -.cm-s-material .CodeMirror-line::selection, .cm-s-material .CodeMirror-line > span::selection, .cm-s-material .CodeMirror-line > span > span::selection { background: rgba(255, 255, 255, 0.10); } -.cm-s-material .CodeMirror-line::-moz-selection, .cm-s-material .CodeMirror-line > span::-moz-selection, .cm-s-material .CodeMirror-line > span > span::-moz-selection { background: rgba(255, 255, 255, 0.10); } - -.cm-s-material .CodeMirror-activeline-background { background: rgba(0, 0, 0, 0); } -.cm-s-material .cm-keyword { color: rgba(199, 146, 234, 1); } -.cm-s-material .cm-operator { color: rgba(233, 237, 237, 1); } -.cm-s-material .cm-variable-2 { color: #80CBC4; } -.cm-s-material .cm-variable-3, .cm-s-material .cm-type { color: #82B1FF; } -.cm-s-material .cm-builtin { color: #DECB6B; } -.cm-s-material .cm-atom { color: #F77669; } -.cm-s-material .cm-number { color: #F77669; } -.cm-s-material .cm-def { color: rgba(233, 237, 237, 1); } -.cm-s-material .cm-string { color: #C3E88D; } -.cm-s-material .cm-string-2 { color: #80CBC4; } -.cm-s-material .cm-comment { color: #546E7A; } -.cm-s-material .cm-variable { color: #82B1FF; } -.cm-s-material .cm-tag { color: #80CBC4; } -.cm-s-material .cm-meta { color: #80CBC4; } -.cm-s-material .cm-attribute { color: #FFCB6B; } -.cm-s-material .cm-property { color: #80CBAE; } -.cm-s-material .cm-qualifier { color: #DECB6B; } -.cm-s-material .cm-variable-3, .cm-s-material .cm-type { color: #DECB6B; } -.cm-s-material .cm-tag { color: rgba(255, 83, 112, 1); } + +.cm-s-material .CodeMirror-guttermarker, +.cm-s-material .CodeMirror-guttermarker-subtle, +.cm-s-material .CodeMirror-linenumber { + color: #546E7A; +} + +.cm-s-material .CodeMirror-cursor { + border-left: 1px solid #FFCC00; +} + +.cm-s-material div.CodeMirror-selected { + background: rgba(128, 203, 196, 0.2); +} + +.cm-s-material.CodeMirror-focused div.CodeMirror-selected { + background: rgba(128, 203, 196, 0.2); +} + +.cm-s-material .CodeMirror-line::selection, +.cm-s-material .CodeMirror-line>span::selection, +.cm-s-material .CodeMirror-line>span>span::selection { + background: rgba(128, 203, 196, 0.2); +} + +.cm-s-material .CodeMirror-line::-moz-selection, +.cm-s-material .CodeMirror-line>span::-moz-selection, +.cm-s-material .CodeMirror-line>span>span::-moz-selection { + background: rgba(128, 203, 196, 0.2); +} + +.cm-s-material .CodeMirror-activeline-background { + background: rgba(0, 0, 0, 0.5); +} + +.cm-s-material .cm-keyword { + color: #C792EA; +} + +.cm-s-material .cm-operator { + color: #89DDFF; +} + +.cm-s-material .cm-variable-2 { + color: #EEFFFF; +} + +.cm-s-material .cm-variable-3, +.cm-s-material .cm-type { + color: #f07178; +} + +.cm-s-material .cm-builtin { + color: #FFCB6B; +} + +.cm-s-material .cm-atom { + color: #F78C6C; +} + +.cm-s-material .cm-number { + color: #FF5370; +} + +.cm-s-material .cm-def { + color: #82AAFF; +} + +.cm-s-material .cm-string { + color: #C3E88D; +} + +.cm-s-material .cm-string-2 { + color: #f07178; +} + +.cm-s-material .cm-comment { + color: #546E7A; +} + +.cm-s-material .cm-variable { + color: #f07178; +} + +.cm-s-material .cm-tag { + color: #FF5370; +} + +.cm-s-material .cm-meta { + color: #FFCB6B; +} + +.cm-s-material .cm-attribute { + color: #C792EA; +} + +.cm-s-material .cm-property { + color: #C792EA; +} + +.cm-s-material .cm-qualifier { + color: #DECB6B; +} + +.cm-s-material .cm-variable-3, +.cm-s-material .cm-type { + color: #DECB6B; +} + + .cm-s-material .cm-error { color: rgba(255, 255, 255, 1.0); - background-color: #EC5F67; + background-color: #FF5370; } + .cm-s-material .CodeMirror-matchingbracket { text-decoration: underline; color: white !important; -} +} \ No newline at end of file From a34b8b34160c5fa8c0c6ad65bfdf762c725b3c8d Mon Sep 17 00:00:00 2001 From: Mattia Astorino Date: Fri, 23 Aug 2019 16:42:20 +0200 Subject: [PATCH 1641/1790] [moxer theme] Add --- demo/theme.html | 2 + theme/moxer.css | 143 ++++++++++++++++++++++++++++++++++++++++++++++++ 2 files changed, 145 insertions(+) create mode 100644 theme/moxer.css diff --git a/demo/theme.html b/demo/theme.html index 723c3efa..8c10eb82 100644 --- a/demo/theme.html +++ b/demo/theme.html @@ -36,6 +36,7 @@ + @@ -134,6 +135,7 @@

      Theme Demo

      + diff --git a/theme/moxer.css b/theme/moxer.css new file mode 100644 index 00000000..b3ca35e3 --- /dev/null +++ b/theme/moxer.css @@ -0,0 +1,143 @@ +/* + Name: Moxer Theme + Author: Mattia Astorino (http://github.com/equinusocio) + Website: https://github.com/moxer-theme/moxer-code +*/ + +.cm-s-moxer.CodeMirror { + background-color: #090A0F; + color: #8E95B4; + line-height: 1.8; +} + +.cm-s-moxer .CodeMirror-gutters { + background: #090A0F; + color: #35394B; + border: none; +} + +.cm-s-moxer .CodeMirror-guttermarker, +.cm-s-moxer .CodeMirror-guttermarker-subtle, +.cm-s-moxer .CodeMirror-linenumber { + color: #35394B; +} + + +.cm-s-moxer .CodeMirror-cursor { + border-left: 1px solid #FFCC00; +} + +.cm-s-moxer div.CodeMirror-selected { + background: rgba(128, 203, 196, 0.2); +} + +.cm-s-moxer.CodeMirror-focused div.CodeMirror-selected { + background: #212431; +} + +.cm-s-moxer .CodeMirror-line::selection, +.cm-s-moxer .CodeMirror-line>span::selection, +.cm-s-moxer .CodeMirror-line>span>span::selection { + background: #212431; +} + +.cm-s-moxer .CodeMirror-line::-moz-selection, +.cm-s-moxer .CodeMirror-line>span::-moz-selection, +.cm-s-moxer .CodeMirror-line>span>span::-moz-selection { + background: #212431; +} + +.cm-s-moxer .CodeMirror-activeline-background, +.cm-s-moxer .CodeMirror-activeline-gutter .CodeMirror-linenumber { + background: rgba(33, 36, 49, 0.5); +} + +.cm-s-moxer .cm-keyword { + color: #D46C6C; +} + +.cm-s-moxer .cm-operator { + color: #D46C6C; +} + +.cm-s-moxer .cm-variable-2 { + color: #81C5DA; +} + + +.cm-s-moxer .cm-variable-3, +.cm-s-moxer .cm-type { + color: #f07178; +} + +.cm-s-moxer .cm-builtin { + color: #FFCB6B; +} + +.cm-s-moxer .cm-atom { + color: #A99BE2; +} + +.cm-s-moxer .cm-number { + color: #7CA4C0; +} + +.cm-s-moxer .cm-def { + color: #F5DFA5; +} + +.cm-s-moxer .CodeMirror-line .cm-def ~ .cm-def { + color: #81C5DA; +} + +.cm-s-moxer .cm-string { + color: #B2E4AE; +} + +.cm-s-moxer .cm-string-2 { + color: #f07178; +} + +.cm-s-moxer .cm-comment { + color: #3F445A; +} + +.cm-s-moxer .cm-variable { + color: #8E95B4; +} + +.cm-s-moxer .cm-tag { + color: #FF5370; +} + +.cm-s-moxer .cm-meta { + color: #FFCB6B; +} + +.cm-s-moxer .cm-attribute { + color: #C792EA; +} + +.cm-s-moxer .cm-property { + color: #81C5DA; +} + +.cm-s-moxer .cm-qualifier { + color: #DECB6B; +} + +.cm-s-moxer .cm-variable-3, +.cm-s-moxer .cm-type { + color: #DECB6B; +} + + +.cm-s-moxer .cm-error { + color: rgba(255, 255, 255, 1.0); + background-color: #FF5370; +} + +.cm-s-moxer .CodeMirror-matchingbracket { + text-decoration: underline; + color: white !important; +} \ No newline at end of file From 399d430ee411e30eb3bc03a51c6e0e00c433e6b5 Mon Sep 17 00:00:00 2001 From: Mark Hamstra Date: Sun, 25 Aug 2019 15:51:00 +0200 Subject: [PATCH 1642/1790] Prevent removing textarea.form.submit handler with opts.leaveSubmitMethodAlone --- src/edit/fromTextArea.js | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/edit/fromTextArea.js b/src/edit/fromTextArea.js index 92498c10..35024c5e 100644 --- a/src/edit/fromTextArea.js +++ b/src/edit/fromTextArea.js @@ -48,7 +48,7 @@ export function fromTextArea(textarea, options) { textarea.style.display = "" if (textarea.form) { off(textarea.form, "submit", save) - if (typeof textarea.form.submit == "function") + if (!options.leaveSubmitMethodAlone && typeof textarea.form.submit == "function") textarea.form.submit = realSubmit } } From f826bb0be8aac1e586eabc2ba0297c164cebe8ef Mon Sep 17 00:00:00 2001 From: Marijn Haverbeke Date: Thu, 29 Aug 2019 14:06:34 +0200 Subject: [PATCH 1643/1790] [octave mode] Don't mark period chars as invalid Closes #5987 --- mode/octave/octave.js | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/mode/octave/octave.js b/mode/octave/octave.js index 4040b670..33a03368 100644 --- a/mode/octave/octave.js +++ b/mode/octave/octave.js @@ -17,7 +17,7 @@ CodeMirror.defineMode("octave", function() { } var singleOperators = new RegExp("^[\\+\\-\\*/&|\\^~<>!@'\\\\]"); - var singleDelimiters = new RegExp('^[\\(\\[\\{\\},:=;]'); + var singleDelimiters = new RegExp('^[\\(\\[\\{\\},:=;\\.]'); var doubleOperators = new RegExp("^((==)|(~=)|(<=)|(>=)|(<<)|(>>)|(\\.[\\+\\-\\*/\\^\\\\]))"); var doubleDelimiters = new RegExp("^((!=)|(\\+=)|(\\-=)|(\\*=)|(/=)|(&=)|(\\|=)|(\\^=))"); var tripleDelimiters = new RegExp("^((>>=)|(<<=))"); From 9b5f1c49fa099825c8f73dbe5efe18633719380e Mon Sep 17 00:00:00 2001 From: Marijn Haverbeke Date: Fri, 30 Aug 2019 17:29:22 +0200 Subject: [PATCH 1644/1790] [javascript mode] Add support for HTML-style comments Closes #5988 --- mode/javascript/javascript.js | 3 +++ 1 file changed, 3 insertions(+) diff --git a/mode/javascript/javascript.js b/mode/javascript/javascript.js index 8055f1ba..16943a9e 100644 --- a/mode/javascript/javascript.js +++ b/mode/javascript/javascript.js @@ -101,6 +101,9 @@ CodeMirror.defineMode("javascript", function(config, parserConfig) { } else if (ch == "#") { stream.skipToEnd(); return ret("error", "error"); + } else if (ch == "<" && stream.match("!--") || ch == "-" && stream.match("->")) { + stream.skipToEnd() + return ret("comment", "comment") } else if (isOperatorChar.test(ch)) { if (ch != ">" || !state.lexical || state.lexical.type != ">") { if (stream.eat("=")) { From 538c4dc3ad5ead88ac1f53062ddd69ecb8cfee0a Mon Sep 17 00:00:00 2001 From: zoobestik Date: Wed, 4 Sep 2019 15:29:27 +0300 Subject: [PATCH 1645/1790] [clike] fix kotlin indent for first line in lambda with params --- 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 0879a0bd..b3cf54e8 100644 --- a/mode/clike/clike.js +++ b/mode/clike/clike.js @@ -685,7 +685,7 @@ CodeMirror.defineMode("clike", function(config, parserConfig) { var firstChar = textAfter && textAfter.charAt(0); if ((state.prevToken == "}" || state.prevToken == ")") && textAfter == "") return state.indented; - if (state.prevToken == "operator" && textAfter != "}" || + if ((state.prevToken == "operator" && textAfter != "}" && state.context.type != "}") || state.prevToken == "variable" && firstChar == "." || (state.prevToken == "}" || state.prevToken == ")") && firstChar == ".") return indentUnit * 2 + ctx.indented; From ab766ceff9804ad79fb14abcaa535864a56e3f64 Mon Sep 17 00:00:00 2001 From: Erik Welander Date: Thu, 5 Sep 2019 22:16:20 -0700 Subject: [PATCH 1646/1790] [foldgutter and annotatescrollbar addon] Schedule update on changes instead of change The setTimeout/clearTimeout overhead of re-scheduling on every change in a large operation can be significant. This cuts the time of a particularly large operation I was testing from 5s to 2.5s (replacing some text in ~5000 lines). The majority of the rest of the time coming from rescheduling of the highlight worker inside the startWorker call. --- addon/fold/foldgutter.js | 4 ++-- addon/scroll/annotatescrollbar.js | 4 ++-- 2 files changed, 4 insertions(+), 4 deletions(-) diff --git a/addon/fold/foldgutter.js b/addon/fold/foldgutter.js index fcb5021e..e57a1df3 100644 --- a/addon/fold/foldgutter.js +++ b/addon/fold/foldgutter.js @@ -16,7 +16,7 @@ cm.clearGutter(cm.state.foldGutter.options.gutter); cm.state.foldGutter = null; cm.off("gutterClick", onGutterClick); - cm.off("change", onChange); + cm.off("changes", onChange); cm.off("viewportChange", onViewportChange); cm.off("fold", onFold); cm.off("unfold", onFold); @@ -26,7 +26,7 @@ cm.state.foldGutter = new State(parseOptions(val)); updateInViewport(cm); cm.on("gutterClick", onGutterClick); - cm.on("change", onChange); + cm.on("changes", onChange); cm.on("viewportChange", onViewportChange); cm.on("fold", onFold); cm.on("unfold", onFold); diff --git a/addon/scroll/annotatescrollbar.js b/addon/scroll/annotatescrollbar.js index 35662581..9fe61ec1 100644 --- a/addon/scroll/annotatescrollbar.js +++ b/addon/scroll/annotatescrollbar.js @@ -43,7 +43,7 @@ cm.on("markerAdded", this.resizeHandler); cm.on("markerCleared", this.resizeHandler); if (options.listenForChanges !== false) - cm.on("change", this.changeHandler = function() { + cm.on("changes", this.changeHandler = function() { scheduleRedraw(250); }); } @@ -116,7 +116,7 @@ this.cm.off("refresh", this.resizeHandler); this.cm.off("markerAdded", this.resizeHandler); this.cm.off("markerCleared", this.resizeHandler); - if (this.changeHandler) this.cm.off("change", this.changeHandler); + if (this.changeHandler) this.cm.off("changes", this.changeHandler); this.div.parentNode.removeChild(this.div); }; }); From 92b8e511853769e9c8cbe915d1e01f5540c2996f Mon Sep 17 00:00:00 2001 From: Arnoud Buzing Date: Wed, 11 Sep 2019 00:36:12 -0500 Subject: [PATCH 1647/1790] [mode/meta] Add wl and wls file extensions --- mode/meta.js | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/mode/meta.js b/mode/meta.js index 95d47310..e9155037 100644 --- a/mode/meta.js +++ b/mode/meta.js @@ -84,7 +84,7 @@ {name: "Markdown", mime: "text/x-markdown", mode: "markdown", ext: ["markdown", "md", "mkd"]}, {name: "mIRC", mime: "text/mirc", mode: "mirc"}, {name: "MariaDB SQL", mime: "text/x-mariadb", mode: "sql"}, - {name: "Mathematica", mime: "text/x-mathematica", mode: "mathematica", ext: ["m", "nb"]}, + {name: "Mathematica", mime: "text/x-mathematica", mode: "mathematica", ext: ["m", "nb", "wl", "wls"]}, {name: "Modelica", mime: "text/x-modelica", mode: "modelica", ext: ["mo"]}, {name: "MUMPS", mime: "text/x-mumps", mode: "mumps", ext: ["mps"]}, {name: "MS SQL", mime: "text/x-mssql", mode: "sql"}, From d19537cb70019857440e5d713b1e832a31ce886d Mon Sep 17 00:00:00 2001 From: Marijn Haverbeke Date: Wed, 11 Sep 2019 08:27:59 +0200 Subject: [PATCH 1648/1790] [xml mode] Add an indirection to XML mode state inspection Issue codemirror/google-modes#266 --- addon/edit/closetag.js | 37 +++++++++++++++++++------------------ addon/hint/xml-hint.js | 17 ++++++++++------- mode/xml/xml.js | 11 +++++++++++ 3 files changed, 40 insertions(+), 25 deletions(-) diff --git a/addon/edit/closetag.js b/addon/edit/closetag.js index e5e83bcd..4f5aae49 100644 --- a/addon/edit/closetag.js +++ b/addon/edit/closetag.js @@ -60,22 +60,23 @@ if (!ranges[i].empty()) return CodeMirror.Pass; 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 tagInfo = inner.mode.xmlCurrentTag && inner.mode.xmlCurrentTag(state) + var tagName = tagInfo && tagInfo.name + if (!tagName) return CodeMirror.Pass var html = inner.mode.configuration == "html"; var dontCloseTags = (typeof opt == "object" && opt.dontCloseTags) || (html && htmlDontClose); var indentTags = (typeof opt == "object" && opt.indentTags) || (html && htmlIndent); - 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 (!tagName || 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.type == "tag" && tagInfo.close || tok.string.indexOf("/") == (tok.string.length - 1) || // match something like dontCloseTags && indexOf(dontCloseTags, lowerTagName) > -1 || - closingTagExists(cm, tagName, pos, state, true)) + closingTagExists(cm, inner.mode.xmlCurrentContext && inner.mode.xmlCurrentContext(state) || [], tagName, pos, true)) return CodeMirror.Pass; var emptyTags = typeof opt == "object" && opt.emptyTags; @@ -120,19 +121,16 @@ // 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). - var replacement; - if (inner.mode.name != "xml") { - if (cm.getMode().name == "htmlmixed" && inner.mode.name == "javascript") - replacement = head + "script"; - else if (cm.getMode().name == "htmlmixed" && inner.mode.name == "css") - replacement = head + "style"; - else - return CodeMirror.Pass; + var replacement, mixed = inner.mode.name != "xml" && cm.getMode().name == "htmlmixed" + if (mixed && inner.mode.name == "javascript") { + replacement = head + "script"; + } else if (mixed && inner.mode.name == "css") { + replacement = head + "style"; } else { - if (!state.context || !state.context.tagName || - closingTagExists(cm, state.context.tagName, pos, state)) + var context = inner.mode.xmlCurrentContext && inner.mode.xmlCurrentContext(state) + if (!context || (context.length && closingTagExists(cm, context, context[context.length - 1], pos))) return CodeMirror.Pass; - replacement = head + state.context.tagName; + replacement = head + context[context.length - 1] } if (cm.getLine(pos.line).charAt(tok.end) != ">") replacement += ">"; replacements[i] = replacement; @@ -162,16 +160,19 @@ // 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) { + function closingTagExists(cm, context, tagName, pos, 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; + var onCx = newTag ? 1 : 0 + for (var i = context.length - 1; i >= 0; i--) { + if (context[i] == tagName) ++onCx + else break + } pos = nextClose.to; for (var i = 1; i < onCx; i++) { var next = CodeMirror.scanForClosingTag(cm, pos, null, end); diff --git a/addon/hint/xml-hint.js b/addon/hint/xml-hint.js index 106ba4f3..7575b370 100644 --- a/addon/hint/xml-hint.js +++ b/addon/hint/xml-hint.js @@ -29,7 +29,7 @@ token.string = token.string.slice(0, cur.ch - token.start); } var inner = CodeMirror.innerMode(cm.getMode(), token.state); - if (inner.mode.name != "xml") return; + if (!inner.mode.xmlCurrentTag) return var result = [], replaceToken = false, prefix; var tag = /\btag\b/.test(token.type) && !/>$/.test(token.string); var tagName = tag && /^\w/.test(token.string), tagStart; @@ -44,12 +44,15 @@ tagType = "close"; } - if (!tag && !inner.state.tagName || tagType) { + var tagInfo = inner.mode.xmlCurrentTag(inner.state) + if (!tag && !tagInfo || tagType) { if (tagName) prefix = token.string; replaceToken = tagType; - var cx = inner.state.context, curTag = cx && tags[cx.tagName]; - var childList = cx ? curTag && curTag.children : tags["!top"]; + var context = inner.mode.xmlCurrentContext ? inner.mode.xmlCurrentContext(inner.state) : [] + var inner = context.length && context[context.length - 1] + var curTag = inner && tags[inner] + var childList = inner ? curTag && curTag.children : tags["!top"]; if (childList && tagType != "close") { for (var i = 0; i < childList.length; ++i) if (!prefix || matches(childList[i], prefix, matchInMiddle)) result.push("<" + childList[i]); @@ -58,11 +61,11 @@ if (tags.hasOwnProperty(name) && name != "!top" && name != "!attrs" && (!prefix || matches(name, prefix, matchInMiddle))) result.push("<" + name); } - if (cx && (!prefix || tagType == "close" && matches(cx.tagName, prefix, matchInMiddle))) - result.push(""); + if (inner && (!prefix || tagType == "close" && matches(inner, prefix, matchInMiddle))) + result.push(""); } else { // Attribute completion - var curTag = tags[inner.state.tagName], attrs = curTag && curTag.attrs; + var curTag = tagInfo && tags[tagInfo.name], attrs = curTag && curTag.attrs; var globalAttrs = tags["!attrs"]; if (!attrs && !globalAttrs) return; if (!attrs) { diff --git a/mode/xml/xml.js b/mode/xml/xml.js index b67bf850..73c6e0e0 100644 --- a/mode/xml/xml.js +++ b/mode/xml/xml.js @@ -390,6 +390,17 @@ CodeMirror.defineMode("xml", function(editorConf, config_) { skipAttribute: function(state) { if (state.state == attrValueState) state.state = attrState + }, + + xmlCurrentTag: function(state) { + return state.tagName ? {name: state.tagName, close: state.type == "closeTag"} : null + }, + + xmlCurrentContext: function(state) { + var context = [] + for (var cx = state.context; cx; cx = cx.prev) + if (cx.tagName) context.push(cx.tagName) + return context.reverse() } }; }); From 0340b0d348249ba85f36e099738e55dd8c03f255 Mon Sep 17 00:00:00 2001 From: Marijn Haverbeke Date: Fri, 20 Sep 2019 09:11:30 +0200 Subject: [PATCH 1649/1790] Mark version 5.49.0 --- AUTHORS | 5 +++++ CHANGELOG.md | 16 ++++++++++++++++ doc/manual.html | 2 +- doc/releases.html | 10 ++++++++++ index.html | 2 +- package.json | 2 +- src/edit/main.js | 2 +- 7 files changed, 35 insertions(+), 4 deletions(-) diff --git a/AUTHORS b/AUTHORS index b73067ce..3afa74b9 100644 --- a/AUTHORS +++ b/AUTHORS @@ -77,6 +77,7 @@ AQNOUCH Mohammed Aram Shatakhtsyan areos Arnab Bose +Arnoud Buzing Arsène von Wyss Arthur Müller Arun Narasani @@ -485,6 +486,7 @@ Mário Gonçalves Mario Pietsch Mark Anderson Mark Dalgleish +Mark Hamstra Mark Lentczner Marko Bonaci Mark Peace @@ -508,6 +510,7 @@ Matthew Rathbone Matthew Suozzo Matthias Bussonnier Matthias BUSSONNIER +Mattia Astorino Matt MacPherson Matt McDonald Matt Pass @@ -636,6 +639,7 @@ Pontus Melke prasanthj Prasanth J Prayag Verma +prendota Prendota Qiang Li Radek Piórkowski @@ -823,5 +827,6 @@ Zachary Dremann Zeno Rocha Zhang Hao Ziv +zoobestik zziuni 魏鹏刚 diff --git a/CHANGELOG.md b/CHANGELOG.md index 7256b863..73adcdc6 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -1,3 +1,19 @@ +## 5.49.0 (2019-09-20) + +### Bug fixes + +[octave mode](https://codemirror.net/mode/octave/index.html): Don't mark common punctuation as error. + +[clike mode](https://codemirror.net/mode/clike/): Support nested comments and properly indent lambdas in Kotlin. + +[foldgutter](https://codemirror.net/doc/manual.html#addon_foldgutter) and [annotatescrollbar](https://codemirror.net/doc/manual.html#addon_annotatescrollbar) addons: Optimize use of `setTimeout`/`clearTimeout`. + +### New features + +New themes: [moxer](https://codemirror.net/demo/theme.html#moxer), [material-darker](https://codemirror.net/demo/theme.html#material-darker), [material-palenight](https://codemirror.net/demo/theme.html#material-palenight), [material-ocean](https://codemirror.net/demo/theme.html#material-ocean). + +[xml mode](https://codemirror.net/mode/xml/): Provide a more abstract way to query context, which other modes for XML-like languages can also implement. + ## 5.48.4 (2019-08-20) ### Bug fixes diff --git a/doc/manual.html b/doc/manual.html index 6a96a66d..0a16b2e2 100644 --- a/doc/manual.html +++ b/doc/manual.html @@ -69,7 +69,7 @@

      User manual and reference guide - version 5.48.4 + version 5.49.0

      CodeMirror is a code-editor component that can be embedded in diff --git a/doc/releases.html b/doc/releases.html index 73235e85..4c1ef75b 100644 --- a/doc/releases.html +++ b/doc/releases.html @@ -30,6 +30,16 @@

      Release notes and version history

      Version 5.x

      +

      20-09-2019: Version 5.49.0:

      + + +

      20-08-2019: Version 5.48.4:

        diff --git a/index.html b/index.html index 95b1afb4..cb779743 100644 --- a/index.html +++ b/index.html @@ -99,7 +99,7 @@

        This is CodeMirror

      - Get the current version: 5.48.4.
      + Get the current version: 5.49.0.
      You can see the code,
      read the release notes,
      or study the user manual. diff --git a/package.json b/package.json index 7186b02b..96683680 100644 --- a/package.json +++ b/package.json @@ -1,6 +1,6 @@ { "name": "codemirror", - "version": "5.48.4", + "version": "5.49.0", "main": "lib/codemirror.js", "style": "lib/codemirror.css", "author": { diff --git a/src/edit/main.js b/src/edit/main.js index 92478df3..94c41b89 100644 --- a/src/edit/main.js +++ b/src/edit/main.js @@ -66,4 +66,4 @@ import { addLegacyProps } from "./legacy.js" addLegacyProps(CodeMirror) -CodeMirror.version = "5.48.4" +CodeMirror.version = "5.49.0" From c386446a7c121cc9389e1a18eba3c3e04c4a9945 Mon Sep 17 00:00:00 2001 From: Marijn Haverbeke Date: Fri, 20 Sep 2019 09:25:16 +0200 Subject: [PATCH 1650/1790] Bump version number post-5.49.0 --- doc/manual.html | 2 +- package.json | 2 +- src/edit/main.js | 2 +- 3 files changed, 3 insertions(+), 3 deletions(-) diff --git a/doc/manual.html b/doc/manual.html index 0a16b2e2..9a7898b3 100644 --- a/doc/manual.html +++ b/doc/manual.html @@ -69,7 +69,7 @@

      User manual and reference guide - version 5.49.0 + version 5.49.1

      CodeMirror is a code-editor component that can be embedded in diff --git a/package.json b/package.json index 96683680..8b39b8a7 100644 --- a/package.json +++ b/package.json @@ -1,6 +1,6 @@ { "name": "codemirror", - "version": "5.49.0", + "version": "5.49.1", "main": "lib/codemirror.js", "style": "lib/codemirror.css", "author": { diff --git a/src/edit/main.js b/src/edit/main.js index 94c41b89..5acfc837 100644 --- a/src/edit/main.js +++ b/src/edit/main.js @@ -66,4 +66,4 @@ import { addLegacyProps } from "./legacy.js" addLegacyProps(CodeMirror) -CodeMirror.version = "5.49.0" +CodeMirror.version = "5.49.1" From 782b791afc1ab4a511cc38b6112a3d2457ccb39f Mon Sep 17 00:00:00 2001 From: Joo Date: Mon, 23 Sep 2019 01:09:32 +0900 Subject: [PATCH 1651/1790] [real world] add Tistory Tistory is blog service in South Korea. Tistory uses CodeMirror at post markdown/html editor and skin editor. --- doc/realworld.html | 1 + 1 file changed, 1 insertion(+) diff --git a/doc/realworld.html b/doc/realworld.html index ff5f815b..8460755c 100644 --- a/doc/realworld.html +++ b/doc/realworld.html @@ -170,6 +170,7 @@

      CodeMirror real-world uses

    • The File Tree (collab editor)
    • TileMill (map design tool)
    • Tiki (wiki CMS groupware)
    • +
    • Tistory (blog service)
    • Toolsverse Data Explorer (database management)
    • Tumblr code highlighting shim
    • TurboPY (web publishing framework)
    • From db6fd334a8f43317bd9d31e8b7e7e5afc0ba8912 Mon Sep 17 00:00:00 2001 From: Marijn Haverbeke Date: Tue, 24 Sep 2019 08:36:46 +0200 Subject: [PATCH 1652/1790] [markdown demo] Include javascript mode Closes #6011 --- mode/markdown/index.html | 1 + 1 file changed, 1 insertion(+) diff --git a/mode/markdown/index.html b/mode/markdown/index.html index 37203ef3..80f72be6 100644 --- a/mode/markdown/index.html +++ b/mode/markdown/index.html @@ -8,6 +8,7 @@ +